mirror of https://github.com/cutefishos/core
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
141 lines
4.4 KiB
C++
141 lines
4.4 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "xrecordkeyboardmonitor.h"
|
|
|
|
#include <cstdlib>
|
|
#include <limits>
|
|
|
|
#include <QScopedPointer>
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <xcb/xcbext.h>
|
|
|
|
XRecordKeyboardMonitor::XRecordKeyboardMonitor(Display *display)
|
|
: m_connection(xcb_connect(XDisplayString(display), nullptr))
|
|
, m_modifiersPressed(0)
|
|
, m_keysPressed(0)
|
|
{
|
|
if (!m_connection) {
|
|
return;
|
|
}
|
|
|
|
xcb_get_modifier_mapping_cookie_t modmapCookie = xcb_get_modifier_mapping(m_connection);
|
|
|
|
m_context = xcb_generate_id(m_connection);
|
|
xcb_record_range_t range;
|
|
memset(&range, 0, sizeof(range));
|
|
range.device_events.first = XCB_KEY_PRESS;
|
|
range.device_events.last = XCB_KEY_RELEASE;
|
|
xcb_record_client_spec_t cs = XCB_RECORD_CS_ALL_CLIENTS;
|
|
xcb_record_create_context(m_connection, m_context, 0, 1, 1, &cs, &range);
|
|
xcb_flush(m_connection);
|
|
|
|
QScopedPointer<xcb_get_modifier_mapping_reply_t, QScopedPointerPodDeleter> modmap(xcb_get_modifier_mapping_reply(m_connection, modmapCookie, nullptr));
|
|
if (!modmap) {
|
|
return;
|
|
}
|
|
|
|
int nModifiers = xcb_get_modifier_mapping_keycodes_length(modmap.data());
|
|
xcb_keycode_t *modifiers = xcb_get_modifier_mapping_keycodes(modmap.data());
|
|
m_modifier.fill(false, std::numeric_limits<xcb_keycode_t>::max() + 1);
|
|
for (xcb_keycode_t *i = modifiers; i < modifiers + nModifiers; i++) {
|
|
m_modifier[*i] = true;
|
|
}
|
|
m_ignore.fill(false, std::numeric_limits<xcb_keycode_t>::max() + 1);
|
|
for (xcb_keycode_t *i = modifiers; i < modifiers + modmap->keycodes_per_modifier; i++) {
|
|
m_ignore[*i] = true;
|
|
}
|
|
m_pressed.fill(false, std::numeric_limits<xcb_keycode_t>::max() + 1);
|
|
|
|
m_cookie = xcb_record_enable_context(m_connection, m_context);
|
|
xcb_flush(m_connection);
|
|
|
|
m_notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this);
|
|
connect(m_notifier, &QSocketNotifier::activated, this, &XRecordKeyboardMonitor::processNextReply);
|
|
m_notifier->setEnabled(true);
|
|
}
|
|
|
|
XRecordKeyboardMonitor::~XRecordKeyboardMonitor()
|
|
{
|
|
if (!m_connection) {
|
|
return;
|
|
}
|
|
|
|
xcb_record_disable_context(m_connection, m_context);
|
|
xcb_record_free_context(m_connection, m_context);
|
|
xcb_disconnect(m_connection);
|
|
}
|
|
|
|
void XRecordKeyboardMonitor::processNextReply()
|
|
{
|
|
xcb_generic_event_t *event;
|
|
while ((event = xcb_poll_for_event(m_connection))) {
|
|
std::free(event);
|
|
}
|
|
|
|
void *reply = nullptr;
|
|
xcb_generic_error_t *error = nullptr;
|
|
while (m_cookie.sequence && xcb_poll_for_reply(m_connection, m_cookie.sequence, &reply, &error)) {
|
|
// xcb_poll_for_reply may set both reply and error to null if connection has error.
|
|
// break if xcb_connection has error, no point to continue anyway.
|
|
if (xcb_connection_has_error(m_connection)) {
|
|
break;
|
|
}
|
|
|
|
if (error) {
|
|
std::free(error);
|
|
break;
|
|
}
|
|
|
|
if (!reply) {
|
|
continue;
|
|
}
|
|
|
|
QScopedPointer<xcb_record_enable_context_reply_t, QScopedPointerPodDeleter> data(reinterpret_cast<xcb_record_enable_context_reply_t *>(reply));
|
|
process(data.data());
|
|
}
|
|
}
|
|
|
|
void XRecordKeyboardMonitor::process(xcb_record_enable_context_reply_t *reply)
|
|
{
|
|
bool prevActivity = activity();
|
|
|
|
xcb_key_press_event_t *events = reinterpret_cast<xcb_key_press_event_t *>(xcb_record_enable_context_data(reply));
|
|
int nEvents = xcb_record_enable_context_data_length(reply) / sizeof(xcb_key_press_event_t);
|
|
bool wasActivity = prevActivity;
|
|
for (xcb_key_press_event_t *e = events; e < events + nEvents; e++) {
|
|
if (e->response_type != XCB_KEY_PRESS && e->response_type != XCB_KEY_RELEASE) {
|
|
continue;
|
|
}
|
|
|
|
if (m_ignore[e->detail]) {
|
|
continue;
|
|
}
|
|
|
|
bool pressed = (e->response_type == XCB_KEY_PRESS);
|
|
if (m_pressed[e->detail] == pressed) {
|
|
continue;
|
|
}
|
|
m_pressed[e->detail] = pressed;
|
|
|
|
int &counter = m_modifier[e->detail] ? m_modifiersPressed : m_keysPressed;
|
|
if (pressed) {
|
|
counter++;
|
|
} else {
|
|
counter--;
|
|
}
|
|
|
|
wasActivity = wasActivity || activity();
|
|
}
|
|
|
|
if (!prevActivity && activity()) {
|
|
Q_EMIT keyboardActivityStarted();
|
|
} else if (!activity() && wasActivity) {
|
|
Q_EMIT keyboardActivityFinished();
|
|
}
|
|
}
|