#include "commons/commons_base.h" #include "commons/commons_platform_linux.h" #include #include #include "commons/commons_array.h" #include auto API X11GetConn() -> xcb_connection_t* { return platform->x11.conn; } auto API X11GetScreenIndex() -> int { return platform->x11.screenIndex; } auto API X11GetScreen() -> xcb_screen_t* { return platform->x11.screen; } auto API X11GetRoot() -> xcb_window_t { return X11GetScreen()->root; } void API X11Grab() { xcb_grab_server(X11GetConn()); } void API X11Ungrab() { xcb_ungrab_server(X11GetConn()); } void API X11Flush() { xcb_flush(X11GetConn()); } auto API X11GetXInputExtensionMajorOpCode() -> uint32_t { return platform->x11.extensionXInput2MajorOpCode; } auto API X11GetAtoms() -> const x11_atoms& { return platform->x11.atoms; } auto API X11CreateWin(uint32_t width, uint32_t height, bool overrideRedirect, xcb_event_mask_t eventMask) -> xcb_window_t { const xcb_window_t window = xcb_generate_id(platform->x11.conn); const array values { overrideRedirect, eventMask }; xcb_create_window(platform->x11.conn, XCB_COPY_FROM_PARENT, window, X11GetRoot(), 0, 0, width, height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, X11GetScreen()->root_visual, XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK, values.data); xcb_map_window(X11GetConn(), window); uint32_t pid = (uint32_t)getpid(); xcb_change_property(X11GetConn(), XCB_PROP_MODE_REPLACE, window, X11GetAtoms().net_wm_pid, XCB_ATOM_CARDINAL, 32, 1, &pid); char hostname[256]; if (gethostname(hostname, sizeof(hostname)) == 0) { xcb_change_property(X11GetConn(), XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_CLIENT_MACHINE, XCB_ATOM_STRING, 8, (uint32_t)strlen(hostname), hostname); } xcb_flush(X11GetConn()); return window; } auto API X11WinGetHandle() -> xcb_window_t { return platform->x11.win; } void API X11SetWindow(xcb_window_t window) { platform->x11.win = window; } void API X11SendClientMessage32(xcb_window_t window, xcb_atom_t type, xcb_atom_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4) { const xcb_client_message_event_t event = { .response_type = XCB_CLIENT_MESSAGE, .format = 32, .window = window, .type = type, .data = { .data32 = { arg1, arg2, arg3, arg4 } }, }; xcb_send_event(X11GetConn(), false, window, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast(&event)); } void API X11SendWmTakeFocus(xcb_window_t window, uint32_t time) { X11SendClientMessage32(window, X11GetAtoms().wm_protocols, X11GetAtoms().wm_take_focus, time, 0, 0); } void API X11SendWmDeleteWindow(xcb_window_t window) { X11SendClientMessage32(window, X11GetAtoms().wm_protocols, X11GetAtoms().wm_delete_window, XCB_CURRENT_TIME, 0, 0); } void API X11SendConfigureNotify(xcb_window_t window, xcb_window_t parent, int16_t x, int16_t y, uint16_t width, uint16_t height, uint16_t borderWidth) { const xcb_configure_notify_event_t event = { .response_type = XCB_CONFIGURE_NOTIFY, .event = window, .window = window, .x = x, .y = y, .width = width, .height = height, .border_width = borderWidth, }; xcb_send_event(X11GetConn(), false, window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, reinterpret_cast(&event)); } auto API X11KeyPhysicalToKeyCode(KeyPhysical key) -> uint32_t { return platform->x11.keyCodes[static_cast(key)]; } auto API X11KeyCodeToKeyPhysical(uint32_t keyCode, KeyPhysical* outKeyPhysical) -> bool { for (uint32_t i = 1; i < Array_Size(platform->x11.keyCodes); ++i) { if (platform->x11.keyCodes[i] == keyCode) { *outKeyPhysical = static_cast(i); return true; } } return false; } void API X11RandRInit() { const xcb_query_extension_reply_t* ext = xcb_get_extension_data(X11GetConn(), &xcb_randr_id); if (!ext || !ext->present) { platform->x11.monitorX = 0; platform->x11.monitorY = 0; platform->x11.monitorWidth = X11GetScreen()->width_in_pixels; platform->x11.monitorHeight = X11GetScreen()->height_in_pixels; platform->x11.extensionXRandRFirstEvent = 0; return; } platform->x11.extensionXRandRFirstEvent = ext->first_event; xcb_randr_select_input(X11GetConn(), X11GetRoot(), XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE | XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE); } auto API X11RandRGetEventOffset() -> uint32_t { return platform->x11.extensionXRandRFirstEvent; } void API X11RefreshKeyboardMapping() { // When a keyboard is plugged/unplugged or xmodmap changes, X sends // MappingNotify. Rebuild the xkb keymap and state from the new device // topology, then remap our keycode table. const int32_t deviceId = xkb_x11_get_core_keyboard_device_id(X11GetConn()); if (deviceId == -1) return; xkb_keymap_unref(platform->x11.xkbKeymap); platform->x11.xkbKeymap = xkb_x11_keymap_new_from_device(platform->x11.xkbCtx, X11GetConn(), deviceId, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!platform->x11.xkbKeymap) return; xkb_state_unref(platform->x11.xkbState); platform->x11.xkbState = xkb_x11_state_new_from_device(platform->x11.xkbKeymap, X11GetConn(), deviceId); for (uint32_t i = 1; i < static_cast(KeyPhysical::Count); ++i) { const auto key = static_cast(i); byteview xkbName = X11ConvertKeyPhysicalIntoXkbName(key); const xkb_keycode_t keycode = xkb_keymap_key_by_name(platform->x11.xkbKeymap, Span_CStr(xkbName)); if (xkb_keycode_is_legal_x11(keycode)) platform->x11.keyCodes[i] = keycode; } }