summaryrefslogtreecommitdiff
path: root/src/platform/x11
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/x11')
-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,