Split keyboard related functionality from InputRedirection
Similar to the change regarding pointer and touch a KeyboardInputRedirection is created. The Xkb class is also moved to the new files keyboard_input.h and keyboard_input.cpp. Just like in the case of PointerInputRedirection no signals are added, but the existing signals in InputRedirection are directly invoked.
This commit is contained in:
parent
71a8879e95
commit
849d17519c
5 changed files with 517 additions and 349 deletions
|
@ -332,6 +332,7 @@ set(kwin_KDEINIT_SRCS
|
||||||
focuschain.cpp
|
focuschain.cpp
|
||||||
globalshortcuts.cpp
|
globalshortcuts.cpp
|
||||||
input.cpp
|
input.cpp
|
||||||
|
keyboard_input.cpp
|
||||||
pointer_input.cpp
|
pointer_input.cpp
|
||||||
touch_input.cpp
|
touch_input.cpp
|
||||||
netinfo.cpp
|
netinfo.cpp
|
||||||
|
|
313
input.cpp
313
input.cpp
|
@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*********************************************************************/
|
*********************************************************************/
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
|
#include "keyboard_input.h"
|
||||||
#include "pointer_input.h"
|
#include "pointer_input.h"
|
||||||
#include "touch_input.h"
|
#include "touch_input.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
@ -65,222 +66,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
Xkb::Xkb(InputRedirection *input)
|
|
||||||
: m_input(input)
|
|
||||||
, m_context(xkb_context_new(static_cast<xkb_context_flags>(0)))
|
|
||||||
, m_keymap(NULL)
|
|
||||||
, m_state(NULL)
|
|
||||||
, m_shiftModifier(0)
|
|
||||||
, m_controlModifier(0)
|
|
||||||
, m_altModifier(0)
|
|
||||||
, m_metaModifier(0)
|
|
||||||
, m_modifiers(Qt::NoModifier)
|
|
||||||
{
|
|
||||||
if (!m_context) {
|
|
||||||
qCDebug(KWIN_CORE) << "Could not create xkb context";
|
|
||||||
} else {
|
|
||||||
// load default keymap
|
|
||||||
xkb_keymap *keymap = xkb_keymap_new_from_names(m_context, nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
|
||||||
if (keymap) {
|
|
||||||
updateKeymap(keymap);
|
|
||||||
} else {
|
|
||||||
qCDebug(KWIN_CORE) << "Could not create default xkb keymap";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Xkb::~Xkb()
|
|
||||||
{
|
|
||||||
xkb_state_unref(m_state);
|
|
||||||
xkb_keymap_unref(m_keymap);
|
|
||||||
xkb_context_unref(m_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Xkb::installKeymap(int fd, uint32_t size)
|
|
||||||
{
|
|
||||||
if (!m_context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
char *map = reinterpret_cast<char*>(mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0));
|
|
||||||
if (map == MAP_FAILED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
xkb_keymap *keymap = xkb_keymap_new_from_string(m_context, map, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_MAP_COMPILE_PLACEHOLDER);
|
|
||||||
munmap(map, size);
|
|
||||||
if (!keymap) {
|
|
||||||
qCDebug(KWIN_CORE) << "Could not map keymap from file";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
updateKeymap(keymap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Xkb::updateKeymap(xkb_keymap *keymap)
|
|
||||||
{
|
|
||||||
Q_ASSERT(keymap);
|
|
||||||
xkb_state *state = xkb_state_new(keymap);
|
|
||||||
if (!state) {
|
|
||||||
qCDebug(KWIN_CORE) << "Could not create XKB state";
|
|
||||||
xkb_keymap_unref(keymap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// now release the old ones
|
|
||||||
xkb_state_unref(m_state);
|
|
||||||
xkb_keymap_unref(m_keymap);
|
|
||||||
|
|
||||||
m_keymap = keymap;
|
|
||||||
m_state = state;
|
|
||||||
|
|
||||||
m_shiftModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_SHIFT);
|
|
||||||
m_controlModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CTRL);
|
|
||||||
m_altModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT);
|
|
||||||
m_metaModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO);
|
|
||||||
|
|
||||||
createKeymapFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Xkb::createKeymapFile()
|
|
||||||
{
|
|
||||||
if (!waylandServer()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO: uninstall keymap on server?
|
|
||||||
if (!m_keymap) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedCPointer<char> keymapString(xkb_keymap_get_as_string(m_keymap, XKB_KEYMAP_FORMAT_TEXT_V1));
|
|
||||||
if (keymapString.isNull()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const uint size = qstrlen(keymapString.data()) + 1;
|
|
||||||
|
|
||||||
QTemporaryFile *tmp = new QTemporaryFile(m_input);
|
|
||||||
if (!tmp->open()) {
|
|
||||||
delete tmp;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unlink(tmp->fileName().toUtf8().constData());
|
|
||||||
if (!tmp->resize(size)) {
|
|
||||||
delete tmp;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uchar *address = tmp->map(0, size);
|
|
||||||
if (!address) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (qstrncpy(reinterpret_cast<char*>(address), keymapString.data(), size) == nullptr) {
|
|
||||||
delete tmp;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
waylandServer()->seat()->setKeymap(tmp->handle(), size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Xkb::updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
|
|
||||||
{
|
|
||||||
if (!m_keymap || !m_state) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
xkb_state_update_mask(m_state, modsDepressed, modsLatched, modsLocked, 0, 0, group);
|
|
||||||
updateModifiers();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Xkb::updateKey(uint32_t key, InputRedirection::KeyboardKeyState state)
|
|
||||||
{
|
|
||||||
if (!m_keymap || !m_state) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
xkb_state_update_key(m_state, key + 8, static_cast<xkb_key_direction>(state));
|
|
||||||
updateModifiers();
|
|
||||||
if (state == InputRedirection::KeyboardKeyPressed) {
|
|
||||||
m_modOnlyShortcut.pressCount++;
|
|
||||||
if (m_modOnlyShortcut.pressCount == 1) {
|
|
||||||
m_modOnlyShortcut.modifier = Qt::KeyboardModifier(int(m_modifiers));
|
|
||||||
} else {
|
|
||||||
m_modOnlyShortcut.modifier = Qt::NoModifier;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_modOnlyShortcut.pressCount--;
|
|
||||||
// TODO: ignore on lock screen
|
|
||||||
if (m_modOnlyShortcut.pressCount == 0) {
|
|
||||||
if (m_modOnlyShortcut.modifier != Qt::NoModifier) {
|
|
||||||
const auto list = options->modifierOnlyDBusShortcut(m_modOnlyShortcut.modifier);
|
|
||||||
if (list.size() >= 4) {
|
|
||||||
auto call = QDBusMessage::createMethodCall(list.at(0), list.at(1), list.at(2), list.at(3));
|
|
||||||
QVariantList args;
|
|
||||||
for (int i = 4; i < list.size(); ++i) {
|
|
||||||
args << list.at(i);
|
|
||||||
}
|
|
||||||
call.setArguments(args);
|
|
||||||
QDBusConnection::sessionBus().asyncCall(call);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_modOnlyShortcut.modifier = Qt::NoModifier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Xkb::updateModifiers()
|
|
||||||
{
|
|
||||||
Qt::KeyboardModifiers mods = Qt::NoModifier;
|
|
||||||
if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
|
|
||||||
mods |= Qt::ShiftModifier;
|
|
||||||
}
|
|
||||||
if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
|
|
||||||
mods |= Qt::AltModifier;
|
|
||||||
}
|
|
||||||
if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
|
|
||||||
mods |= Qt::ControlModifier;
|
|
||||||
}
|
|
||||||
if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
|
|
||||||
mods |= Qt::MetaModifier;
|
|
||||||
}
|
|
||||||
m_modifiers = mods;
|
|
||||||
}
|
|
||||||
|
|
||||||
xkb_keysym_t Xkb::toKeysym(uint32_t key)
|
|
||||||
{
|
|
||||||
if (!m_state) {
|
|
||||||
return XKB_KEY_NoSymbol;
|
|
||||||
}
|
|
||||||
return xkb_state_key_get_one_sym(m_state, key + 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Xkb::toString(xkb_keysym_t keysym)
|
|
||||||
{
|
|
||||||
if (!m_state || keysym == XKB_KEY_NoSymbol) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
QByteArray byteArray(7, 0);
|
|
||||||
int ok = xkb_keysym_to_utf8(keysym, byteArray.data(), byteArray.size());
|
|
||||||
if (ok == -1 || ok == 0) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return QString::fromUtf8(byteArray.constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::Key Xkb::toQtKey(xkb_keysym_t keysym)
|
|
||||||
{
|
|
||||||
int key = Qt::Key_unknown;
|
|
||||||
KKeyServer::symXToKeyQt(keysym, &key);
|
|
||||||
return static_cast<Qt::Key>(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
quint32 Xkb::getMods(quint32 components)
|
|
||||||
{
|
|
||||||
if (!m_state) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return xkb_state_serialize_mods(m_state, xkb_state_component(components));
|
|
||||||
}
|
|
||||||
|
|
||||||
quint32 Xkb::getGroup()
|
|
||||||
{
|
|
||||||
if (!m_state) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
InputEventFilter::InputEventFilter() = default;
|
InputEventFilter::InputEventFilter() = default;
|
||||||
|
|
||||||
InputEventFilter::~InputEventFilter()
|
InputEventFilter::~InputEventFilter()
|
||||||
|
@ -388,7 +173,7 @@ public:
|
||||||
if (!waylandServer()->isScreenLocked()) {
|
if (!waylandServer()->isScreenLocked()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
input()->updateKeyboardWindow();
|
input()->keyboard()->update();
|
||||||
if (!keyboardSurfaceAllowed()) {
|
if (!keyboardSurfaceAllowed()) {
|
||||||
// don't pass event to seat
|
// don't pass event to seat
|
||||||
return true;
|
return true;
|
||||||
|
@ -754,7 +539,7 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto seat = waylandServer()->seat();
|
auto seat = waylandServer()->seat();
|
||||||
input()->updateKeyboardWindow();
|
input()->keyboard()->update();
|
||||||
seat->setTimestamp(event->timestamp());
|
seat->setTimestamp(event->timestamp());
|
||||||
switch (event->type()) {
|
switch (event->type()) {
|
||||||
case QEvent::KeyPress:
|
case QEvent::KeyPress:
|
||||||
|
@ -822,9 +607,9 @@ KWIN_SINGLETON_FACTORY(InputRedirection)
|
||||||
|
|
||||||
InputRedirection::InputRedirection(QObject *parent)
|
InputRedirection::InputRedirection(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
, m_keyboard(new KeyboardInputRedirection(this))
|
||||||
, m_pointer(new PointerInputRedirection(this))
|
, m_pointer(new PointerInputRedirection(this))
|
||||||
, m_touch(new TouchInputRedirection(this))
|
, m_touch(new TouchInputRedirection(this))
|
||||||
, m_xkb(new Xkb(this))
|
|
||||||
, m_shortcuts(new GlobalShortcutsManager(this))
|
, m_shortcuts(new GlobalShortcutsManager(this))
|
||||||
{
|
{
|
||||||
qRegisterMetaType<KWin::InputRedirection::KeyboardKeyState>();
|
qRegisterMetaType<KWin::InputRedirection::KeyboardKeyState>();
|
||||||
|
@ -867,7 +652,6 @@ void InputRedirection::init()
|
||||||
void InputRedirection::setupWorkspace()
|
void InputRedirection::setupWorkspace()
|
||||||
{
|
{
|
||||||
if (waylandServer()) {
|
if (waylandServer()) {
|
||||||
connect(workspace(), &Workspace::clientActivated, this, &InputRedirection::updateKeyboardWindow);
|
|
||||||
using namespace KWayland::Server;
|
using namespace KWayland::Server;
|
||||||
FakeInputInterface *fakeInput = waylandServer()->display()->createFakeInput(this);
|
FakeInputInterface *fakeInput = waylandServer()->display()->createFakeInput(this);
|
||||||
fakeInput->create();
|
fakeInput->create();
|
||||||
|
@ -923,18 +707,17 @@ void InputRedirection::setupWorkspace()
|
||||||
if (!waylandServer()->seat()) {
|
if (!waylandServer()->seat()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
waylandServer()->seat()->updateKeyboardModifiers(m_xkb->getMods(XKB_STATE_MODS_DEPRESSED),
|
waylandServer()->seat()->updateKeyboardModifiers(m_keyboard->xkb()->getMods(XKB_STATE_MODS_DEPRESSED),
|
||||||
m_xkb->getMods(XKB_STATE_MODS_LATCHED),
|
m_keyboard->xkb()->getMods(XKB_STATE_MODS_LATCHED),
|
||||||
m_xkb->getMods(XKB_STATE_MODS_LOCKED),
|
m_keyboard->xkb()->getMods(XKB_STATE_MODS_LOCKED),
|
||||||
m_xkb->getGroup());
|
m_keyboard->xkb()->getGroup());
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
connect(workspace(), &Workspace::configChanged, this, &InputRedirection::reconfigure);
|
connect(workspace(), &Workspace::configChanged, this, &InputRedirection::reconfigure);
|
||||||
|
|
||||||
|
m_keyboard->init();
|
||||||
m_pointer->init();
|
m_pointer->init();
|
||||||
m_touch->init();
|
m_touch->init();
|
||||||
|
|
||||||
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &InputRedirection::updateKeyboardWindow);
|
|
||||||
}
|
}
|
||||||
setupInputFilters();
|
setupInputFilters();
|
||||||
}
|
}
|
||||||
|
@ -1006,7 +789,7 @@ void InputRedirection::setupLibInput()
|
||||||
);
|
);
|
||||||
connect(conn, &LibInput::Connection::pointerButtonChanged, m_pointer, &PointerInputRedirection::processButton);
|
connect(conn, &LibInput::Connection::pointerButtonChanged, m_pointer, &PointerInputRedirection::processButton);
|
||||||
connect(conn, &LibInput::Connection::pointerAxisChanged, m_pointer, &PointerInputRedirection::processAxis);
|
connect(conn, &LibInput::Connection::pointerAxisChanged, m_pointer, &PointerInputRedirection::processAxis);
|
||||||
connect(conn, &LibInput::Connection::keyChanged, this, &InputRedirection::processKeyboardKey);
|
connect(conn, &LibInput::Connection::keyChanged, m_keyboard, &KeyboardInputRedirection::processKey);
|
||||||
connect(conn, &LibInput::Connection::pointerMotion, this,
|
connect(conn, &LibInput::Connection::pointerMotion, this,
|
||||||
[this] (QPointF delta, uint32_t time) {
|
[this] (QPointF delta, uint32_t time) {
|
||||||
m_pointer->processMotion(m_pointer->pos() + delta, time);
|
m_pointer->processMotion(m_pointer->pos() + delta, time);
|
||||||
|
@ -1098,87 +881,19 @@ void InputRedirection::processPointerAxis(InputRedirection::PointerAxis axis, qr
|
||||||
m_pointer->processAxis(axis, delta, time);
|
m_pointer->processAxis(axis, delta, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputRedirection::updateKeyboardWindow()
|
|
||||||
{
|
|
||||||
if (!workspace()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (auto seat = findSeat()) {
|
|
||||||
// TODO: this needs better integration
|
|
||||||
Toplevel *found = nullptr;
|
|
||||||
if (waylandServer()->isScreenLocked()) {
|
|
||||||
const ToplevelList &stacking = Workspace::self()->stackingOrder();
|
|
||||||
if (!stacking.isEmpty()) {
|
|
||||||
auto it = stacking.end();
|
|
||||||
do {
|
|
||||||
--it;
|
|
||||||
Toplevel *t = (*it);
|
|
||||||
if (t->isDeleted()) {
|
|
||||||
// a deleted window doesn't get mouse events
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!t->isLockScreen()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!t->readyForPainting()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
found = t;
|
|
||||||
break;
|
|
||||||
} while (it != stacking.begin());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
found = workspace()->activeClient();
|
|
||||||
}
|
|
||||||
if (found && found->surface()) {
|
|
||||||
if (found->surface() != seat->focusedKeyboardSurface()) {
|
|
||||||
seat->setFocusedKeyboardSurface(found->surface());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
seat->setFocusedKeyboardSurface(nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InputRedirection::processKeyboardKey(uint32_t key, InputRedirection::KeyboardKeyState state, uint32_t time)
|
void InputRedirection::processKeyboardKey(uint32_t key, InputRedirection::KeyboardKeyState state, uint32_t time)
|
||||||
{
|
{
|
||||||
emit keyStateChanged(key, state);
|
m_keyboard->processKey(key, state, time);
|
||||||
const Qt::KeyboardModifiers oldMods = keyboardModifiers();
|
|
||||||
m_xkb->updateKey(key, state);
|
|
||||||
if (oldMods != keyboardModifiers()) {
|
|
||||||
emit keyboardModifiersChanged(keyboardModifiers(), oldMods);
|
|
||||||
}
|
|
||||||
const xkb_keysym_t keySym = m_xkb->toKeysym(key);
|
|
||||||
QKeyEvent event((state == KeyboardKeyPressed) ? QEvent::KeyPress : QEvent::KeyRelease,
|
|
||||||
m_xkb->toQtKey(keySym),
|
|
||||||
m_xkb->modifiers(),
|
|
||||||
key,
|
|
||||||
keySym,
|
|
||||||
0,
|
|
||||||
m_xkb->toString(m_xkb->toKeysym(key)));
|
|
||||||
event.setTimestamp(time);
|
|
||||||
|
|
||||||
for (auto it = m_filters.constBegin(), end = m_filters.constEnd(); it != end; it++) {
|
|
||||||
if ((*it)->keyEvent(&event)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputRedirection::processKeyboardModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
|
void InputRedirection::processKeyboardModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
|
||||||
{
|
{
|
||||||
// TODO: send to proper Client and also send when active Client changes
|
m_keyboard->processModifiers(modsDepressed, modsLatched, modsLocked, group);
|
||||||
Qt::KeyboardModifiers oldMods = keyboardModifiers();
|
|
||||||
m_xkb->updateModifiers(modsDepressed, modsLatched, modsLocked, group);
|
|
||||||
if (oldMods != keyboardModifiers()) {
|
|
||||||
emit keyboardModifiersChanged(keyboardModifiers(), oldMods);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputRedirection::processKeymapChange(int fd, uint32_t size)
|
void InputRedirection::processKeymapChange(int fd, uint32_t size)
|
||||||
{
|
{
|
||||||
// TODO: should we pass the keymap to our Clients? Or only to the currently active one and update
|
m_keyboard->processKeymapChange(fd, size);
|
||||||
m_xkb->installKeymap(fd, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputRedirection::processTouchDown(qint32 id, const QPointF &pos, quint32 time)
|
void InputRedirection::processTouchDown(qint32 id, const QPointF &pos, quint32 time)
|
||||||
|
@ -1269,7 +984,7 @@ Toplevel *InputRedirection::findToplevel(const QPoint &pos)
|
||||||
|
|
||||||
Qt::KeyboardModifiers InputRedirection::keyboardModifiers() const
|
Qt::KeyboardModifiers InputRedirection::keyboardModifiers() const
|
||||||
{
|
{
|
||||||
return m_xkb->modifiers();
|
return m_keyboard->modifiers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputRedirection::registerShortcut(const QKeySequence &shortcut, QAction *action)
|
void InputRedirection::registerShortcut(const QKeySequence &shortcut, QAction *action)
|
||||||
|
|
55
input.h
55
input.h
|
@ -32,18 +32,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
class KGlobalAccelInterface;
|
class KGlobalAccelInterface;
|
||||||
class QKeySequence;
|
class QKeySequence;
|
||||||
|
|
||||||
struct xkb_context;
|
|
||||||
struct xkb_keymap;
|
|
||||||
struct xkb_state;
|
|
||||||
typedef uint32_t xkb_mod_index_t;
|
|
||||||
typedef uint32_t xkb_keysym_t;
|
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
class GlobalShortcutsManager;
|
class GlobalShortcutsManager;
|
||||||
class Toplevel;
|
class Toplevel;
|
||||||
class Xkb;
|
|
||||||
class InputEventFilter;
|
class InputEventFilter;
|
||||||
|
class KeyboardInputRedirection;
|
||||||
class PointerInputRedirection;
|
class PointerInputRedirection;
|
||||||
class TouchInputRedirection;
|
class TouchInputRedirection;
|
||||||
|
|
||||||
|
@ -146,11 +140,12 @@ public:
|
||||||
return m_shortcuts;
|
return m_shortcuts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateKeyboardWindow();
|
|
||||||
|
|
||||||
QVector<InputEventFilter*> filters() const {
|
QVector<InputEventFilter*> filters() const {
|
||||||
return m_filters;
|
return m_filters;
|
||||||
}
|
}
|
||||||
|
KeyboardInputRedirection *keyboard() const {
|
||||||
|
return m_keyboard;
|
||||||
|
}
|
||||||
PointerInputRedirection *pointer() const {
|
PointerInputRedirection *pointer() const {
|
||||||
return m_pointer;
|
return m_pointer;
|
||||||
}
|
}
|
||||||
|
@ -205,9 +200,9 @@ private:
|
||||||
void reconfigure();
|
void reconfigure();
|
||||||
void setupInputFilters();
|
void setupInputFilters();
|
||||||
void installInputEventFilter(InputEventFilter *filter);
|
void installInputEventFilter(InputEventFilter *filter);
|
||||||
|
KeyboardInputRedirection *m_keyboard;
|
||||||
PointerInputRedirection *m_pointer;
|
PointerInputRedirection *m_pointer;
|
||||||
TouchInputRedirection *m_touch;
|
TouchInputRedirection *m_touch;
|
||||||
QScopedPointer<Xkb> m_xkb;
|
|
||||||
|
|
||||||
GlobalShortcutsManager *m_shortcuts;
|
GlobalShortcutsManager *m_shortcuts;
|
||||||
|
|
||||||
|
@ -279,40 +274,6 @@ public:
|
||||||
virtual bool touchUp(quint32 id, quint32 time);
|
virtual bool touchUp(quint32 id, quint32 time);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Xkb
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Xkb(InputRedirection *input);
|
|
||||||
~Xkb();
|
|
||||||
void installKeymap(int fd, uint32_t size);
|
|
||||||
void updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group);
|
|
||||||
void updateKey(uint32_t key, InputRedirection::KeyboardKeyState state);
|
|
||||||
xkb_keysym_t toKeysym(uint32_t key);
|
|
||||||
QString toString(xkb_keysym_t keysym);
|
|
||||||
Qt::Key toQtKey(xkb_keysym_t keysym);
|
|
||||||
Qt::KeyboardModifiers modifiers() const;
|
|
||||||
|
|
||||||
quint32 getMods(quint32 components);
|
|
||||||
quint32 getGroup();
|
|
||||||
private:
|
|
||||||
void updateKeymap(xkb_keymap *keymap);
|
|
||||||
void createKeymapFile();
|
|
||||||
void updateModifiers();
|
|
||||||
InputRedirection *m_input;
|
|
||||||
xkb_context *m_context;
|
|
||||||
xkb_keymap *m_keymap;
|
|
||||||
xkb_state *m_state;
|
|
||||||
xkb_mod_index_t m_shiftModifier;
|
|
||||||
xkb_mod_index_t m_controlModifier;
|
|
||||||
xkb_mod_index_t m_altModifier;
|
|
||||||
xkb_mod_index_t m_metaModifier;
|
|
||||||
Qt::KeyboardModifiers m_modifiers;
|
|
||||||
struct {
|
|
||||||
uint pressCount = 0;
|
|
||||||
Qt::KeyboardModifier modifier = Qt::NoModifier;
|
|
||||||
} m_modOnlyShortcut;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline
|
inline
|
||||||
InputRedirection *input()
|
InputRedirection *input()
|
||||||
{
|
{
|
||||||
|
@ -326,12 +287,6 @@ void InputRedirection::registerShortcut(const QKeySequence &shortcut, QAction *a
|
||||||
connect(action, &QAction::triggered, receiver, slot);
|
connect(action, &QAction::triggered, receiver, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
|
||||||
Qt::KeyboardModifiers Xkb::modifiers() const
|
|
||||||
{
|
|
||||||
return m_modifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace KWin
|
} // namespace KWin
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(KWin::InputRedirection::KeyboardKeyState)
|
Q_DECLARE_METATYPE(KWin::InputRedirection::KeyboardKeyState)
|
||||||
|
|
376
keyboard_input.cpp
Normal file
376
keyboard_input.cpp
Normal file
|
@ -0,0 +1,376 @@
|
||||||
|
/********************************************************************
|
||||||
|
KWin - the KDE window manager
|
||||||
|
This file is part of the KDE project.
|
||||||
|
|
||||||
|
Copyright (C) 2013, 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*********************************************************************/
|
||||||
|
#include "keyboard_input.h"
|
||||||
|
#include "abstract_client.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "toplevel.h"
|
||||||
|
#include "wayland_server.h"
|
||||||
|
#include "workspace.h"
|
||||||
|
// KWayland
|
||||||
|
#include <KWayland/Server/seat_interface.h>
|
||||||
|
//screenlocker
|
||||||
|
#include <KScreenLocker/KsldApp>
|
||||||
|
// Frameworks
|
||||||
|
#include <KKeyServer>
|
||||||
|
// Qt
|
||||||
|
#include <QDBusConnection>
|
||||||
|
#include <QDBusMessage>
|
||||||
|
#include <QDBusPendingCall>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
// xkbcommon
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||||
|
// system
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace KWin
|
||||||
|
{
|
||||||
|
Xkb::Xkb(InputRedirection *input)
|
||||||
|
: m_input(input)
|
||||||
|
, m_context(xkb_context_new(static_cast<xkb_context_flags>(0)))
|
||||||
|
, m_keymap(NULL)
|
||||||
|
, m_state(NULL)
|
||||||
|
, m_shiftModifier(0)
|
||||||
|
, m_controlModifier(0)
|
||||||
|
, m_altModifier(0)
|
||||||
|
, m_metaModifier(0)
|
||||||
|
, m_modifiers(Qt::NoModifier)
|
||||||
|
{
|
||||||
|
if (!m_context) {
|
||||||
|
qCDebug(KWIN_CORE) << "Could not create xkb context";
|
||||||
|
} else {
|
||||||
|
// load default keymap
|
||||||
|
xkb_keymap *keymap = xkb_keymap_new_from_names(m_context, nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
|
if (keymap) {
|
||||||
|
updateKeymap(keymap);
|
||||||
|
} else {
|
||||||
|
qCDebug(KWIN_CORE) << "Could not create default xkb keymap";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Xkb::~Xkb()
|
||||||
|
{
|
||||||
|
xkb_state_unref(m_state);
|
||||||
|
xkb_keymap_unref(m_keymap);
|
||||||
|
xkb_context_unref(m_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Xkb::installKeymap(int fd, uint32_t size)
|
||||||
|
{
|
||||||
|
if (!m_context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char *map = reinterpret_cast<char*>(mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0));
|
||||||
|
if (map == MAP_FAILED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
xkb_keymap *keymap = xkb_keymap_new_from_string(m_context, map, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_MAP_COMPILE_PLACEHOLDER);
|
||||||
|
munmap(map, size);
|
||||||
|
if (!keymap) {
|
||||||
|
qCDebug(KWIN_CORE) << "Could not map keymap from file";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateKeymap(keymap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Xkb::updateKeymap(xkb_keymap *keymap)
|
||||||
|
{
|
||||||
|
Q_ASSERT(keymap);
|
||||||
|
xkb_state *state = xkb_state_new(keymap);
|
||||||
|
if (!state) {
|
||||||
|
qCDebug(KWIN_CORE) << "Could not create XKB state";
|
||||||
|
xkb_keymap_unref(keymap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// now release the old ones
|
||||||
|
xkb_state_unref(m_state);
|
||||||
|
xkb_keymap_unref(m_keymap);
|
||||||
|
|
||||||
|
m_keymap = keymap;
|
||||||
|
m_state = state;
|
||||||
|
|
||||||
|
m_shiftModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_SHIFT);
|
||||||
|
m_controlModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CTRL);
|
||||||
|
m_altModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT);
|
||||||
|
m_metaModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO);
|
||||||
|
|
||||||
|
createKeymapFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Xkb::createKeymapFile()
|
||||||
|
{
|
||||||
|
if (!waylandServer()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: uninstall keymap on server?
|
||||||
|
if (!m_keymap) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedCPointer<char> keymapString(xkb_keymap_get_as_string(m_keymap, XKB_KEYMAP_FORMAT_TEXT_V1));
|
||||||
|
if (keymapString.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uint size = qstrlen(keymapString.data()) + 1;
|
||||||
|
|
||||||
|
QTemporaryFile *tmp = new QTemporaryFile(m_input);
|
||||||
|
if (!tmp->open()) {
|
||||||
|
delete tmp;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unlink(tmp->fileName().toUtf8().constData());
|
||||||
|
if (!tmp->resize(size)) {
|
||||||
|
delete tmp;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uchar *address = tmp->map(0, size);
|
||||||
|
if (!address) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (qstrncpy(reinterpret_cast<char*>(address), keymapString.data(), size) == nullptr) {
|
||||||
|
delete tmp;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
waylandServer()->seat()->setKeymap(tmp->handle(), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Xkb::updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
|
||||||
|
{
|
||||||
|
if (!m_keymap || !m_state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
xkb_state_update_mask(m_state, modsDepressed, modsLatched, modsLocked, 0, 0, group);
|
||||||
|
updateModifiers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Xkb::updateKey(uint32_t key, InputRedirection::KeyboardKeyState state)
|
||||||
|
{
|
||||||
|
if (!m_keymap || !m_state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
xkb_state_update_key(m_state, key + 8, static_cast<xkb_key_direction>(state));
|
||||||
|
updateModifiers();
|
||||||
|
if (state == InputRedirection::KeyboardKeyPressed) {
|
||||||
|
m_modOnlyShortcut.pressCount++;
|
||||||
|
if (m_modOnlyShortcut.pressCount == 1) {
|
||||||
|
m_modOnlyShortcut.modifier = Qt::KeyboardModifier(int(m_modifiers));
|
||||||
|
} else {
|
||||||
|
m_modOnlyShortcut.modifier = Qt::NoModifier;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_modOnlyShortcut.pressCount--;
|
||||||
|
// TODO: ignore on lock screen
|
||||||
|
if (m_modOnlyShortcut.pressCount == 0) {
|
||||||
|
if (m_modOnlyShortcut.modifier != Qt::NoModifier) {
|
||||||
|
const auto list = options->modifierOnlyDBusShortcut(m_modOnlyShortcut.modifier);
|
||||||
|
if (list.size() >= 4) {
|
||||||
|
auto call = QDBusMessage::createMethodCall(list.at(0), list.at(1), list.at(2), list.at(3));
|
||||||
|
QVariantList args;
|
||||||
|
for (int i = 4; i < list.size(); ++i) {
|
||||||
|
args << list.at(i);
|
||||||
|
}
|
||||||
|
call.setArguments(args);
|
||||||
|
QDBusConnection::sessionBus().asyncCall(call);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_modOnlyShortcut.modifier = Qt::NoModifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Xkb::updateModifiers()
|
||||||
|
{
|
||||||
|
Qt::KeyboardModifiers mods = Qt::NoModifier;
|
||||||
|
if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
|
||||||
|
mods |= Qt::ShiftModifier;
|
||||||
|
}
|
||||||
|
if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
|
||||||
|
mods |= Qt::AltModifier;
|
||||||
|
}
|
||||||
|
if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
|
||||||
|
mods |= Qt::ControlModifier;
|
||||||
|
}
|
||||||
|
if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
|
||||||
|
mods |= Qt::MetaModifier;
|
||||||
|
}
|
||||||
|
m_modifiers = mods;
|
||||||
|
}
|
||||||
|
|
||||||
|
xkb_keysym_t Xkb::toKeysym(uint32_t key)
|
||||||
|
{
|
||||||
|
if (!m_state) {
|
||||||
|
return XKB_KEY_NoSymbol;
|
||||||
|
}
|
||||||
|
return xkb_state_key_get_one_sym(m_state, key + 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Xkb::toString(xkb_keysym_t keysym)
|
||||||
|
{
|
||||||
|
if (!m_state || keysym == XKB_KEY_NoSymbol) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
QByteArray byteArray(7, 0);
|
||||||
|
int ok = xkb_keysym_to_utf8(keysym, byteArray.data(), byteArray.size());
|
||||||
|
if (ok == -1 || ok == 0) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
return QString::fromUtf8(byteArray.constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::Key Xkb::toQtKey(xkb_keysym_t keysym)
|
||||||
|
{
|
||||||
|
int key = Qt::Key_unknown;
|
||||||
|
KKeyServer::symXToKeyQt(keysym, &key);
|
||||||
|
return static_cast<Qt::Key>(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 Xkb::getMods(quint32 components)
|
||||||
|
{
|
||||||
|
if (!m_state) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return xkb_state_serialize_mods(m_state, xkb_state_component(components));
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 Xkb::getGroup()
|
||||||
|
{
|
||||||
|
if (!m_state) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyboardInputRedirection::KeyboardInputRedirection(InputRedirection *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_input(parent)
|
||||||
|
, m_xkb(new Xkb(parent))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyboardInputRedirection::~KeyboardInputRedirection() = default;
|
||||||
|
|
||||||
|
void KeyboardInputRedirection::init()
|
||||||
|
{
|
||||||
|
Q_ASSERT(!m_inited);
|
||||||
|
m_inited = true;
|
||||||
|
|
||||||
|
connect(workspace(), &QObject::destroyed, this, [this] { m_inited = false; });
|
||||||
|
connect(waylandServer(), &QObject::destroyed, this, [this] { m_inited = false; });
|
||||||
|
connect(workspace(), &Workspace::clientActivated, this, &KeyboardInputRedirection::update);
|
||||||
|
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &KeyboardInputRedirection::update);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardInputRedirection::update()
|
||||||
|
{
|
||||||
|
if (!m_inited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto seat = waylandServer()->seat();
|
||||||
|
// TODO: this needs better integration
|
||||||
|
Toplevel *found = nullptr;
|
||||||
|
if (waylandServer()->isScreenLocked()) {
|
||||||
|
const ToplevelList &stacking = Workspace::self()->stackingOrder();
|
||||||
|
if (!stacking.isEmpty()) {
|
||||||
|
auto it = stacking.end();
|
||||||
|
do {
|
||||||
|
--it;
|
||||||
|
Toplevel *t = (*it);
|
||||||
|
if (t->isDeleted()) {
|
||||||
|
// a deleted window doesn't get mouse events
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!t->isLockScreen()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!t->readyForPainting()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
found = t;
|
||||||
|
break;
|
||||||
|
} while (it != stacking.begin());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
found = workspace()->activeClient();
|
||||||
|
}
|
||||||
|
if (found && found->surface()) {
|
||||||
|
if (found->surface() != seat->focusedKeyboardSurface()) {
|
||||||
|
seat->setFocusedKeyboardSurface(found->surface());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
seat->setFocusedKeyboardSurface(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardInputRedirection::processKey(uint32_t key, InputRedirection::KeyboardKeyState state, uint32_t time)
|
||||||
|
{
|
||||||
|
if (!m_inited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit m_input->keyStateChanged(key, state);
|
||||||
|
const Qt::KeyboardModifiers oldMods = modifiers();
|
||||||
|
m_xkb->updateKey(key, state);
|
||||||
|
if (oldMods != modifiers()) {
|
||||||
|
emit m_input->keyboardModifiersChanged(modifiers(), oldMods);
|
||||||
|
}
|
||||||
|
const xkb_keysym_t keySym = m_xkb->toKeysym(key);
|
||||||
|
QKeyEvent event((state == InputRedirection::KeyboardKeyPressed) ? QEvent::KeyPress : QEvent::KeyRelease,
|
||||||
|
m_xkb->toQtKey(keySym),
|
||||||
|
m_xkb->modifiers(),
|
||||||
|
key,
|
||||||
|
keySym,
|
||||||
|
0,
|
||||||
|
m_xkb->toString(m_xkb->toKeysym(key)));
|
||||||
|
event.setTimestamp(time);
|
||||||
|
|
||||||
|
const auto &filters = m_input->filters();
|
||||||
|
for (auto it = filters.begin(), end = filters.end(); it != end; it++) {
|
||||||
|
if ((*it)->keyEvent(&event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardInputRedirection::processModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
|
||||||
|
{
|
||||||
|
if (!m_inited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: send to proper Client and also send when active Client changes
|
||||||
|
Qt::KeyboardModifiers oldMods = modifiers();
|
||||||
|
m_xkb->updateModifiers(modsDepressed, modsLatched, modsLocked, group);
|
||||||
|
if (oldMods != modifiers()) {
|
||||||
|
emit m_input->keyboardModifiersChanged(modifiers(), oldMods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardInputRedirection::processKeymapChange(int fd, uint32_t size)
|
||||||
|
{
|
||||||
|
if (!m_inited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: should we pass the keymap to our Clients? Or only to the currently active one and update
|
||||||
|
m_xkb->installKeymap(fd, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
121
keyboard_input.h
Normal file
121
keyboard_input.h
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
/********************************************************************
|
||||||
|
KWin - the KDE window manager
|
||||||
|
This file is part of the KDE project.
|
||||||
|
|
||||||
|
Copyright (C) 2013, 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*********************************************************************/
|
||||||
|
#ifndef KWIN_KEYBOARD_INPUT_H
|
||||||
|
#define KWIN_KEYBOARD_INPUT_H
|
||||||
|
|
||||||
|
#include "input.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QPointF>
|
||||||
|
|
||||||
|
class QWindow;
|
||||||
|
struct xkb_context;
|
||||||
|
struct xkb_keymap;
|
||||||
|
struct xkb_state;
|
||||||
|
typedef uint32_t xkb_mod_index_t;
|
||||||
|
typedef uint32_t xkb_keysym_t;
|
||||||
|
|
||||||
|
namespace KWin
|
||||||
|
{
|
||||||
|
|
||||||
|
class InputRedirection;
|
||||||
|
class Toplevel;
|
||||||
|
|
||||||
|
class Xkb
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Xkb(InputRedirection *input);
|
||||||
|
~Xkb();
|
||||||
|
void installKeymap(int fd, uint32_t size);
|
||||||
|
void updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group);
|
||||||
|
void updateKey(uint32_t key, InputRedirection::KeyboardKeyState state);
|
||||||
|
xkb_keysym_t toKeysym(uint32_t key);
|
||||||
|
QString toString(xkb_keysym_t keysym);
|
||||||
|
Qt::Key toQtKey(xkb_keysym_t keysym);
|
||||||
|
Qt::KeyboardModifiers modifiers() const;
|
||||||
|
|
||||||
|
quint32 getMods(quint32 components);
|
||||||
|
quint32 getGroup();
|
||||||
|
private:
|
||||||
|
void updateKeymap(xkb_keymap *keymap);
|
||||||
|
void createKeymapFile();
|
||||||
|
void updateModifiers();
|
||||||
|
InputRedirection *m_input;
|
||||||
|
xkb_context *m_context;
|
||||||
|
xkb_keymap *m_keymap;
|
||||||
|
xkb_state *m_state;
|
||||||
|
xkb_mod_index_t m_shiftModifier;
|
||||||
|
xkb_mod_index_t m_controlModifier;
|
||||||
|
xkb_mod_index_t m_altModifier;
|
||||||
|
xkb_mod_index_t m_metaModifier;
|
||||||
|
Qt::KeyboardModifiers m_modifiers;
|
||||||
|
struct {
|
||||||
|
uint pressCount = 0;
|
||||||
|
Qt::KeyboardModifier modifier = Qt::NoModifier;
|
||||||
|
} m_modOnlyShortcut;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KeyboardInputRedirection : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit KeyboardInputRedirection(InputRedirection *parent);
|
||||||
|
virtual ~KeyboardInputRedirection();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
void processKey(uint32_t key, InputRedirection::KeyboardKeyState state, uint32_t time);
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
void processModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group);
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
**/
|
||||||
|
void processKeymapChange(int fd, uint32_t size);
|
||||||
|
|
||||||
|
Xkb *xkb() const {
|
||||||
|
return m_xkb.data();
|
||||||
|
}
|
||||||
|
Qt::KeyboardModifiers modifiers() const {
|
||||||
|
return m_xkb->modifiers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
InputRedirection *m_input;
|
||||||
|
bool m_inited = false;
|
||||||
|
QScopedPointer<Xkb> m_xkb;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
Qt::KeyboardModifiers Xkb::modifiers() const
|
||||||
|
{
|
||||||
|
return m_modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue