summaryrefslogtreecommitdiff
path: root/src/platform/x11/window.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-13 17:08:48 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-13 17:08:48 +0300
commitac6f9f5e7711521455b83ceb843a25e091646d43 (patch)
tree0dfd5fc46a605cd4980ef3c9131ddbaa2270c78a /src/platform/x11/window.c
parentd4d13e616cb15ef22479d9cb97922af93317a01a (diff)
Report the system double-click interval
wsap_term hardcodes 400 ms in three places to count clicks. yasl_display_double_click_ms() reads the interval from the system. X11 parses Net/DoubleClickTime from the XSETTINGS owner's property. Wayland has no protocol for it, so it asks the Settings portal for KDE's DoubleClickInterval, which KDE sends as a string and which exists only when the user set it, then GNOME's double-click. X11 without XSETTINGS asks the portal too. Cocoa reads NSEvent.doubleClickInterval. Missing or implausible values give YASL_DOUBLE_CLICK_MS_DEFAULT, 400 ms. The X11 state test covers the XSETTINGS parser in both byte orders and with truncation. The Xwayland test owns the settings selection. The desktop test serves both portal keys. The Cocoa path is not built or run here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/platform/x11/window.c')
-rw-r--r--src/platform/x11/window.c80
1 files changed, 80 insertions, 0 deletions
diff --git a/src/platform/x11/window.c b/src/platform/x11/window.c
index be1ff32..724987f 100644
--- a/src/platform/x11/window.c
+++ b/src/platform/x11/window.c
@@ -6,6 +6,7 @@
#include "keymap.h"
#include "common/utf8.h"
#include "common/timing.h"
+#include "platform/freedesktop/dbus.h"
#include <X11/Xatom.h>
#include <X11/XKBlib.h>
@@ -27,6 +28,7 @@
#include <limits.h>
#include <math.h>
#include <poll.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
@@ -1804,6 +1806,83 @@ static bool place(struct yasl_surface *base, struct yasl_vector2i at)
return true;
}
+static uint32_t xsettings_card(const unsigned char *p, int size, bool msb)
+{
+ uint32_t v = 0;
+ for (int i = 0; i < size; i++)
+ v |= (uint32_t)p[i] << (8 * (msb ? size - 1 - i : i));
+ return v;
+}
+
+/* Find an integer in an _XSETTINGS_SETTINGS blob: a byte-order byte, three
+ * pad bytes, a serial, a count, then entries of type, pad, name length, name
+ * padded to four bytes, serial, and a typed value. */
+static bool xsettings_int(const unsigned char *p, size_t len, const char *name, int32_t *out)
+{
+ if (len < 12) return false;
+ bool msb = p[0] == MSBFirst;
+ uint32_t n = xsettings_card(p + 8, 4, msb);
+ size_t want = strlen(name);
+ size_t off = 12;
+ for (uint32_t i = 0; i < n; i++) {
+ if (len - off < 4) return false;
+ unsigned type = p[off];
+ size_t name_len = xsettings_card(p + off + 2, 2, msb);
+ size_t padded = (name_len + 3) & ~(size_t)3;
+ const unsigned char *entry_name = p + off + 4;
+ off += 4;
+ if (len - off < padded + 4) return false;
+ off += padded + 4;
+ size_t value_len;
+ if (type == 0) {
+ value_len = 4;
+ } else if (type == 1) {
+ if (len - off < 4) return false;
+ size_t str_len = xsettings_card(p + off, 4, msb);
+ if (str_len > len) return false;
+ value_len = 4 + ((str_len + 3) & ~(size_t)3);
+ } else if (type == 2) {
+ value_len = 8;
+ } else {
+ return false;
+ }
+ if (len - off < value_len) return false;
+ if (type == 0 && name_len == want && memcmp(entry_name, name, want) == 0) {
+ *out = (int32_t)xsettings_card(p + off, 4, msb);
+ return true;
+ }
+ off += value_len;
+ }
+ return false;
+}
+
+static int double_click_ms(const struct yasl_display *base)
+{
+ const struct x11_display *d = (const struct x11_display *)base;
+ if (d->native) {
+ char name[32];
+ snprintf(name, sizeof(name), "_XSETTINGS_S%d", d->screen);
+ Atom selection = XInternAtom(d->native, name, True);
+ Atom property = XInternAtom(d->native, "_XSETTINGS_SETTINGS", True);
+ Window owner = selection ? XGetSelectionOwner(d->native, selection) : None;
+ Atom actual;
+ int format;
+ unsigned long count, left;
+ unsigned char *data = NULL;
+ int32_t ms;
+ if (owner && property &&
+ XGetWindowProperty(d->native, owner, property, 0, 0x10000, False, property,
+ &actual, &format, &count, &left, &data) == Success &&
+ data && actual == property && format == 8 &&
+ xsettings_int(data, count, "Net/DoubleClickTime", &ms)) {
+ XFree(data);
+ return ms;
+ }
+ if (data) XFree(data);
+ }
+ return portal_double_click_ms();
+}
+
static bool bell(struct yasl_surface *base)
{
struct x11_surface *s = (struct x11_surface *)base;
@@ -2059,6 +2138,7 @@ const struct yasl_backend_ops yasl_x11_backend = {
.surface_drag_carry = drag_carry,
.surface_place_under_pointer = place,
.surface_bell = bell,
+ .display_double_click_ms = double_click_ms,
.surface_start_interactive_move = interactive_move,
.surface_start_interactive_resize = interactive_resize,
.surface_show_window_menu = show_window_menu,