// Copyright (C) 2022 RozK // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "../events.hpp" #include "events_x11.hpp" #include "../display/display_x11.hpp" #include static void rk_x11_printf( char const * messsage) { printf("[X11] %s\n", messsage); } static void rk_backup(rk_events_x11 & events) { XKeyboardState backup; XGetKeyboardControl(events.display, &backup); events.backup_autorepeat = (AutoRepeatModeOn == backup.global_auto_repeat); XGetPointerControl( events.display, &events.backup_numerator, &events.backup_denominator, &events.backup_threshold); } static void rk_restore(rk_events_x11 & events) { if (events.backup_autorepeat) { XAutoRepeatOn(events.display); } else { XAutoRepeatOff(events.display); } XChangePointerControl( events.display, True, True, events.backup_numerator, events.backup_denominator, events.backup_threshold); } static void rk_apply(rk_events_x11 & events) { if (events.key_autorepeat) { XAutoRepeatOn(events.display); } else { XAutoRepeatOff(events.display); } XChangePointerControl( events.display, True, True, events.motion_numerator, events.motion_denominator, events.motion_threshold); } static void rk_focus_in(rk_events_x11 & events) { if (!events.focused) { rk_backup(events); rk_apply(events); XGrabPointer(events.display, events.window, True, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, events.window, None, CurrentTime); events.focused = true; } } static void rk_focus_out(rk_events_x11 & events) { if (events.focused) { XUngrabPointer(events.display, CurrentTime); rk_restore(events); events.focused = false; } } rk_events_t rk_create_events( rk_display_t _display) { rk_display_x11 const * const display = reinterpret_cast(_display); if (!display || !display->display || !display->window) { return nullptr; } rk_events_x11 * const events = new rk_events_x11; events->display = display->display; events->window = display->window; events->input_manager = XOpenIM(display->display, nullptr, nullptr, nullptr); if (!events->input_manager) { rk_x11_printf("Failed to open input manager."); delete events; return nullptr; } events->input_context = XCreateIC(events->input_manager, XNInputStyle, XIMPreeditNone | XIMStatusNone, XNClientWindow, display->window, NULL); if (!events->input_context) { rk_x11_printf("Failed to create input context."); XCloseIM(events->input_manager); delete events; return nullptr; } events->focused = false; rk_backup(*events); events->key_autorepeat = events->backup_autorepeat; events->motion_numerator = events->backup_numerator; events->motion_denominator = events->backup_denominator; events->motion_threshold = events->backup_threshold; XSetICFocus(events->input_context); return reinterpret_cast(events); } void rk_destroy_events( rk_display_t _display, rk_events_t _events) { rk_display_x11 const * const display = reinterpret_cast(_display); rk_events_x11 * const events = reinterpret_cast(_events); if (display && events && display->display && display->display == events->display) { rk_restore(*events); if (events->input_context) { XUnsetICFocus(events->input_context); XDestroyIC(events->input_context); } if (events->input_manager) { XCloseIM(events->input_manager); } } if (events) { delete events; } } void rk_set_key_autorepeat( rk_events_t _events, rk_bool autorepeat) { rk_events_x11 * const events = reinterpret_cast(_events); if (events) { events->key_autorepeat = autorepeat; if (events->focused) { rk_apply(*events); } } } void rk_set_motion_acceleration( rk_events_t _events, rk_uint numerator, rk_uint denominator, rk_uint threshold) { rk_events_x11 * const events = reinterpret_cast(_events); if (events) { events->motion_numerator = numerator; events->motion_denominator = denominator; events->motion_threshold = threshold; if (events->focused) { rk_apply(*events); } } } rk_uint rk_consume_events( rk_events_t _events, rk_event * buffer, rk_uint max_events) { static wchar_t string[256]; rk_events_x11 * const events = reinterpret_cast(_events); if (!events || !buffer || !max_events) { return 0; } XEvent x11_event; KeySym keysym; Status status; unsigned nevents = 0; while (nevents < max_events && XCheckWindowEvent(events->display, events->window, RK_EVENTS_MASK, &x11_event)) { if (XFilterEvent(&x11_event, 0)) { continue; } rk_event & event = buffer[nevents]; switch (x11_event.type) { case FocusIn: if (NotifyNormal == x11_event.xfocus.mode) { rk_focus_in(*events); event.type = RK_EVENT_FOCUS_IN; ++nevents; } break; case FocusOut: if (NotifyNormal == x11_event.xfocus.mode) { rk_focus_out(*events); event.type = RK_EVENT_FOCUS_OUT; ++nevents; } break; case KeyPress: event.type = RK_EVENT_KEY_PRESS; event.key.code = x11_event.xkey.keycode; XwcLookupString(events->input_context, &x11_event.xkey, string, 256, &keysym, &status); switch (status) { case XLookupChars: event.key.symbol = XLookupKeysym(&x11_event.xkey, 0); event.key.character = string[0]; break; case XLookupKeySym: event.key.symbol = keysym; event.key.character = 0; break; case XLookupBoth: event.key.symbol = keysym; event.key.character = string[0]; break; default: event.key.symbol = 0; event.key.character = 0; break; } ++nevents; break; case KeyRelease: event.type = RK_EVENT_KEY_RELEASE; event.key.code = x11_event.xkey.keycode; event.key.symbol = XLookupKeysym(&x11_event.xkey, 0); event.key.character = 0; ++nevents; break; case ButtonPress: event.type = RK_EVENT_BUTTON_PRESS; event.button.index = x11_event.xbutton.button; ++nevents; break; case ButtonRelease: event.type = RK_EVENT_BUTTON_RELEASE; event.button.index = x11_event.xbutton.button; ++nevents; break; case MotionNotify: event.type = RK_EVENT_MOTION; event.motion.x = x11_event.xbutton.x; event.motion.y = x11_event.xbutton.y; ++nevents; break; } } return nevents; }