File indexing completed on 2024-11-24 05:00:47
0001 /* 0002 SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "xrecordkeyboardmonitor.h" 0008 #include "c_ptr.h" 0009 0010 #include <cstdlib> 0011 #include <limits> 0012 #include <memory> 0013 0014 #include <X11/Xlib.h> 0015 #include <xcb/xcbext.h> 0016 0017 XRecordKeyboardMonitor::XRecordKeyboardMonitor(Display *display) 0018 : m_connection(xcb_connect(XDisplayString(display), nullptr)) 0019 , m_modifiersPressed(0) 0020 , m_keysPressed(0) 0021 { 0022 if (!m_connection) { 0023 return; 0024 } 0025 0026 xcb_get_modifier_mapping_cookie_t modmapCookie = xcb_get_modifier_mapping(m_connection); 0027 0028 m_context = xcb_generate_id(m_connection); 0029 xcb_record_range_t range; 0030 memset(&range, 0, sizeof(range)); 0031 range.device_events.first = XCB_KEY_PRESS; 0032 range.device_events.last = XCB_KEY_RELEASE; 0033 xcb_record_client_spec_t cs = XCB_RECORD_CS_ALL_CLIENTS; 0034 xcb_record_create_context(m_connection, m_context, 0, 1, 1, &cs, &range); 0035 xcb_flush(m_connection); 0036 0037 std::unique_ptr<xcb_get_modifier_mapping_reply_t, CDeleter> modmap(xcb_get_modifier_mapping_reply(m_connection, modmapCookie, nullptr)); 0038 if (!modmap) { 0039 return; 0040 } 0041 0042 int nModifiers = xcb_get_modifier_mapping_keycodes_length(modmap.get()); 0043 xcb_keycode_t *modifiers = xcb_get_modifier_mapping_keycodes(modmap.get()); 0044 m_modifier.fill(false, std::numeric_limits<xcb_keycode_t>::max() + 1); 0045 for (xcb_keycode_t *i = modifiers; i < modifiers + nModifiers; i++) { 0046 m_modifier[*i] = true; 0047 } 0048 m_ignore.fill(false, std::numeric_limits<xcb_keycode_t>::max() + 1); 0049 for (xcb_keycode_t *i = modifiers; i < modifiers + modmap->keycodes_per_modifier; i++) { 0050 m_ignore[*i] = true; 0051 } 0052 m_pressed.fill(false, std::numeric_limits<xcb_keycode_t>::max() + 1); 0053 0054 m_cookie = xcb_record_enable_context(m_connection, m_context); 0055 xcb_flush(m_connection); 0056 0057 m_notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this); 0058 connect(m_notifier, &QSocketNotifier::activated, this, &XRecordKeyboardMonitor::processNextReply); 0059 m_notifier->setEnabled(true); 0060 } 0061 0062 XRecordKeyboardMonitor::~XRecordKeyboardMonitor() 0063 { 0064 if (!m_connection) { 0065 return; 0066 } 0067 0068 xcb_record_disable_context(m_connection, m_context); 0069 xcb_record_free_context(m_connection, m_context); 0070 xcb_disconnect(m_connection); 0071 } 0072 0073 void XRecordKeyboardMonitor::processNextReply() 0074 { 0075 xcb_generic_event_t *event; 0076 while ((event = xcb_poll_for_event(m_connection))) { 0077 std::free(event); 0078 } 0079 0080 void *reply = nullptr; 0081 xcb_generic_error_t *error = nullptr; 0082 while (m_cookie.sequence && xcb_poll_for_reply(m_connection, m_cookie.sequence, &reply, &error)) { 0083 // xcb_poll_for_reply may set both reply and error to null if connection has error. 0084 // break if xcb_connection has error, no point to continue anyway. 0085 if (xcb_connection_has_error(m_connection)) { 0086 break; 0087 } 0088 0089 if (error) { 0090 std::free(error); 0091 break; 0092 } 0093 0094 if (!reply) { 0095 continue; 0096 } 0097 0098 std::unique_ptr<xcb_record_enable_context_reply_t, CDeleter> data(reinterpret_cast<xcb_record_enable_context_reply_t *>(reply)); 0099 process(data.get()); 0100 } 0101 } 0102 0103 void XRecordKeyboardMonitor::process(xcb_record_enable_context_reply_t *reply) 0104 { 0105 bool prevActivity = activity(); 0106 0107 xcb_key_press_event_t *events = reinterpret_cast<xcb_key_press_event_t *>(xcb_record_enable_context_data(reply)); 0108 int nEvents = xcb_record_enable_context_data_length(reply) / sizeof(xcb_key_press_event_t); 0109 bool wasActivity = prevActivity; 0110 for (xcb_key_press_event_t *e = events; e < events + nEvents; e++) { 0111 if (e->response_type != XCB_KEY_PRESS && e->response_type != XCB_KEY_RELEASE) { 0112 continue; 0113 } 0114 0115 if (m_ignore[e->detail]) { 0116 continue; 0117 } 0118 0119 bool pressed = (e->response_type == XCB_KEY_PRESS); 0120 if (m_pressed[e->detail] == pressed) { 0121 continue; 0122 } 0123 m_pressed[e->detail] = pressed; 0124 0125 int &counter = m_modifier[e->detail] ? m_modifiersPressed : m_keysPressed; 0126 if (pressed) { 0127 counter++; 0128 } else { 0129 counter--; 0130 } 0131 0132 wasActivity = wasActivity || activity(); 0133 } 0134 0135 if (!prevActivity && activity()) { 0136 Q_EMIT keyboardActivityStarted(); 0137 } else if (!activity() && wasActivity) { 0138 Q_EMIT keyboardActivityFinished(); 0139 } 0140 }