summaryrefslogtreecommitdiff
path: root/src/x11
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-12 22:12:02 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-12 22:12:02 +0300
commit94c5e361fbb229b0cd6853e2102a7d95f1f358b3 (patch)
tree3c0f11af9dcb84dac8067a31a6979acd6dd56fbf /src/x11
parent08dc9047098d6d96791024ac7d8f5837633673ef (diff)
better public interface and doxygen
Diffstat (limited to 'src/x11')
-rw-r--r--src/x11/window.c102
1 files changed, 78 insertions, 24 deletions
diff --git a/src/x11/window.c b/src/x11/window.c
index 1a0db7d..f7d036f 100644
--- a/src/x11/window.c
+++ b/src/x11/window.c
@@ -2,6 +2,8 @@
#define _DEFAULT_SOURCE
#include "common/backend.h"
#include "common/input.h"
+#include "common/keyboard.h"
+#include "common/utf8.h"
#include "common/timing.h"
#include <X11/Xatom.h>
@@ -103,6 +105,8 @@ struct x11_display {
bool xi2;
bool has_shm;
bool has_xkb;
+ int xkb_event;
+ enum yasl_key keys[256];
Cursor cursors[16];
Atom abs_pressure, abs_tilt_x, abs_tilt_y, tool_type, eraser;
struct x11_pointer *pointers;
@@ -379,7 +383,8 @@ static void xi_pointer_event(struct x11_surface *s, const XIDeviceEvent *ev)
else if (ev->detail >= 4 && ev->detail <= 7) {
if (ev->evtype == XI_ButtonRelease) return;
mouse.kind = YASL_PTR_SCROLL;
- mouse.scroll_lines = (ev->detail == 4 || ev->detail == 6) ? -1 : 1;
+ if (ev->detail <= 5) mouse.scroll.y = ev->detail == 4 ? -1 : 1;
+ else mouse.scroll.x = ev->detail == 6 ? -1 : 1;
} else {
mouse.kind = ev->evtype == XI_ButtonPress ? YASL_PTR_PRESS : YASL_PTR_RELEASE;
mouse.button = pointer_button(ev->detail);
@@ -915,20 +920,24 @@ static void selection_request(struct x11_display *d, XSelectionRequestEvent *req
Atom property = request->property ? request->property : request->target;
int slot = request->selection == XA_PRIMARY ? YASL_PRIMARY : YASL_CLIPBOARD;
const char *text = d->selection[slot];
+ size_t latin1_len = 0;
+ char *latin1 = text ? yasl_utf8_to_latin1(text, &latin1_len) : NULL;
if (text && request->target == d->targets) {
Atom targets[] = {d->targets, d->utf8, XA_STRING};
XChangeProperty(d->native, request->requestor, property, XA_ATOM, 32,
- PropModeReplace, (unsigned char *)targets, 3);
+ PropModeReplace, (unsigned char *)targets, latin1 ? 3 : 2);
reply.xselection.property = property;
} else if (text && (request->target == d->utf8 || request->target == XA_STRING)) {
- size_t len = strlen(text);
+ const char *payload = request->target == XA_STRING ? latin1 : text;
+ size_t len = request->target == XA_STRING ? latin1_len : strlen(text);
long max = XMaxRequestSize(d->native) * 4 - 128;
- if (len <= (size_t)max) {
+ if (payload && len <= (size_t)max) {
XChangeProperty(d->native, request->requestor, property, request->target,
- 8, PropModeReplace, (const unsigned char *)text, (int)len);
+ 8, PropModeReplace, (const unsigned char *)payload, (int)len);
reply.xselection.property = property;
}
}
+ free(latin1);
XSendEvent(d->native, request->requestor, False, 0, &reply);
}
@@ -1038,6 +1047,7 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event)
char *buf = NULL;
size_t total = 0;
unsigned long offset = 0;
+ bool complete = false;
/* Read up to TEXT_LIMIT bytes in 64KB chunks to avoid huge single-request limits */
while (total < TEXT_LIMIT) {
@@ -1053,7 +1063,7 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event)
if (data) { XFree(data); data = NULL; }
break;
}
- if ((actual != d->utf8 && actual != XA_STRING) || format != 8 || count == 0) {
+ if ((actual != d->utf8 && actual != XA_STRING) || format != 8) {
if (data) { XFree(data); data = NULL; }
break;
}
@@ -1063,22 +1073,41 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event)
break;
}
buf = nb;
- memcpy(buf + total, data, count);
+ if (count) memcpy(buf + total, data, count);
total += count;
buf[total] = '\0';
offset += count / 4;
XFree(data);
data = NULL;
- if (left == 0) break;
+ if (left == 0) { complete = true; break; }
}
- if (buf && total > 0) {
- push(s, (struct yasl_input_event){.kind = YASL_INPUT_PASTE}, buf, total);
+ if (complete && actual == XA_STRING) {
+ size_t utf8_len;
+ char *utf8 = yasl_utf8_from_latin1(buf, total, &utf8_len);
free(buf);
- } else free(buf);
+ buf = utf8;
+ if (buf) total = utf8_len;
+ }
+ if (complete && buf && yasl_utf8_validate(buf, total))
+ push(s, (struct yasl_input_event){.kind = YASL_INPUT_PASTE}, buf, total);
+ free(buf);
XDestroyWindow(d->native, r->window);
free(r);
}
+static void refresh_keyboard(struct x11_display *d)
+{
+ memset(d->keys, 0, sizeof(d->keys));
+ if (!d->has_xkb) return;
+ XkbDescPtr keyboard = XkbGetKeyboard(d->native, XkbAllComponentsMask, XkbUseCoreKbd);
+ if (!keyboard) return;
+ if (keyboard->names && keyboard->names->keys) {
+ for (unsigned code = keyboard->min_key_code; code <= keyboard->max_key_code; code++)
+ d->keys[code] = yasl_key_from_xkb_name(keyboard->names->keys[code].name);
+ }
+ XkbFreeKeyboard(keyboard, XkbAllComponentsMask, True);
+}
+
static void key_event(struct x11_surface *s, XKeyEvent *key, bool repeat)
{
struct x11_display *d = display_of(&s->base);
@@ -1086,10 +1115,11 @@ static void key_event(struct x11_surface *s, XKeyEvent *key, bool repeat)
unsigned clean_state = key->state & ~(Mod2Mask | LockMask);
KeySym sym = NoSymbol, base = NoSymbol, shifted = NoSymbol;
+ int group = XkbGroupForCoreState(key->state);
if (d->has_xkb) {
- sym = XkbKeycodeToKeysym(d->native, key->keycode, 0, (clean_state & ShiftMask) ? 1 : 0);
- base = XkbKeycodeToKeysym(d->native, key->keycode, 0, 0);
- shifted = XkbKeycodeToKeysym(d->native, key->keycode, 0, 1);
+ sym = XkbKeycodeToKeysym(d->native, key->keycode, group, (clean_state & ShiftMask) ? 1 : 0);
+ base = XkbKeycodeToKeysym(d->native, key->keycode, group, 0);
+ shifted = XkbKeycodeToKeysym(d->native, key->keycode, group, 1);
}
if (sym == NoSymbol) {
XKeyEvent clean_key = *key;
@@ -1111,21 +1141,26 @@ static void key_event(struct x11_surface *s, XKeyEvent *key, bool repeat)
else len = 0;
}
if (status != XLookupChars && status != XLookupBoth) len = 0;
- } else len = XLookupString(key, local, sizeof(local), &sym, NULL);
+ } else {
+ /* XLookupString's bytes are Latin-1. Use its modifier-aware keysym
+ * and encode UTF-8 even when no input method is available. */
+ XLookupString(key, local, sizeof(local), &sym, NULL);
+ len = xkb_keysym_to_utf8((xkb_keysym_t)sym, local, sizeof(local));
+ len = len > 0 ? len - 1 : 0; /* xkb includes the terminating NUL. */
+ }
}
uint32_t cp = xkb_keysym_to_utf32((xkb_keysym_t)base);
- if (!cp) cp = xkb_keysym_to_utf32((xkb_keysym_t)sym);
push(s, (struct yasl_input_event){
.kind = YASL_INPUT_KEY,
- .key = {.key = (uint32_t)sym, .codepoint = cp,
- .base = key->keycode >= 8 ? key->keycode - 8 : 0,
- .keypad = sym >= XK_KP_Space && sym <= XK_KP_Equal,
- .shifted = xkb_keysym_to_utf32((xkb_keysym_t)shifted),
- .text = xkb_keysym_to_utf32((xkb_keysym_t)sym), .mods = d->mods,
+ .key = {.key = key->keycode < 256 ? d->keys[key->keycode] : YASL_KEY_UNKNOWN,
+ .codepoint = yasl_key_label(cp), .native_code = key->keycode,
+ .shifted = yasl_key_label(xkb_keysym_to_utf32((xkb_keysym_t)shifted)),
+ .mods = d->mods,
.action = key->type == KeyRelease ? YASL_KEY_RELEASE :
repeat ? YASL_KEY_REPEAT : YASL_KEY_PRESS},
}, NULL, 0);
- if (len > 0 && text)
+ if (len > 0 && text && (unsigned char)text[0] >= 0x20 && text[0] != 0x7f &&
+ !(d->mods & (YASL_MOD_CTRL | YASL_MOD_SUPER | YASL_MOD_ALT)))
push(s, (struct yasl_input_event){.kind = YASL_INPUT_TEXT}, text, (size_t)len);
if (text != local) free(text);
}
@@ -1256,6 +1291,17 @@ static void dispatch(struct yasl_display *base)
XFreeEventData(d->native, &event.xcookie);
continue;
}
+ if (event.type == MappingNotify) {
+ XRefreshKeyboardMapping(&event.xmapping);
+ refresh_keyboard(d);
+ continue;
+ }
+ if (d->has_xkb && event.type == d->xkb_event) {
+ XkbEvent *xkb = (XkbEvent *)&event;
+ if (xkb->any.xkb_type == XkbMapNotify) XkbRefreshKeyboardMapping(&xkb->map);
+ refresh_keyboard(d);
+ continue;
+ }
if (XFilterEvent(&event, None)) continue;
if (event.type == SelectionRequest) {
selection_request(d, &event.xselectionrequest);
@@ -1353,7 +1399,8 @@ static void dispatch(struct yasl_display *base)
if (button >= 4 && button <= 7) {
if (event.type == ButtonRelease) break;
mouse.kind = YASL_PTR_SCROLL;
- mouse.scroll_lines = (button == 4 || button == 6) ? -1 : 1;
+ if (button <= 5) mouse.scroll.y = button == 4 ? -1 : 1;
+ else mouse.scroll.x = button == 6 ? -1 : 1;
} else {
mouse.kind = event.type == ButtonPress ? YASL_PTR_PRESS : YASL_PTR_RELEASE;
mouse.button = pointer_button(button);
@@ -1519,8 +1566,15 @@ static struct yasl_display *display_open(struct yasl_loop *loop,
d->base.native_handle = d->native;
d->screen = DefaultScreen(d->native);
d->has_shm = XShmQueryExtension(d->native);
- int xkb_opcode, xkb_event, xkb_error, xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion;
+ int xkb_opcode = 0, xkb_event = 0, xkb_error = 0;
+ int xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion;
d->has_xkb = XkbQueryExtension(d->native, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor);
+ d->xkb_event = xkb_event;
+ if (d->has_xkb) {
+ unsigned events = XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbNamesNotifyMask;
+ XkbSelectEvents(d->native, XkbUseCoreKbd, events, events);
+ refresh_keyboard(d);
+ }
int xi_event_base, xi_error_base;
int major = 2, minor = 0;
d->xi2 = XQueryExtension(d->native, "XInputExtension", &d->xi_opcode,