Skip to content

Commit

Permalink
Auto-Type: Support multiple Xkb layouts
Browse files Browse the repository at this point in the history
Removed hardcoded KeySymMap and used query instead.
  • Loading branch information
hifi committed Mar 13, 2021
1 parent 8b8fb95 commit 320dccf
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 229 deletions.
1 change: 1 addition & 0 deletions src/autotype/AutoType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ AutoType::parseSequence(const QString& entrySequence, const Entry* entry, QStrin
const int maxRepetition = 100;

QList<QSharedPointer<AutoTypeAction>> actions;
actions << QSharedPointer<AutoTypeBegin>::create();
actions << QSharedPointer<AutoTypeDelay>::create(qMax(0, config()->get(Config::AutoTypeDelay).toInt()), true);

// Replace escaped braces with a template for easier regex
Expand Down
5 changes: 5 additions & 0 deletions src/autotype/AutoTypeAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,8 @@ void AutoTypeClearField::exec(AutoTypeExecutor* executor) const
{
executor->execClearField(this);
}

void AutoTypeBegin::exec(AutoTypeExecutor* executor) const
{
executor->execBegin(this);
}
7 changes: 7 additions & 0 deletions src/autotype/AutoTypeAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,17 @@ class KEEPASSXC_EXPORT AutoTypeClearField : public AutoTypeAction
void exec(AutoTypeExecutor* executor) const override;
};

class KEEPASSXC_EXPORT AutoTypeBegin : public AutoTypeAction
{
public:
void exec(AutoTypeExecutor* executor) const override;
};

class KEEPASSXC_EXPORT AutoTypeExecutor
{
public:
virtual ~AutoTypeExecutor() = default;
virtual void execBegin(const AutoTypeBegin* action) = 0;
virtual void execType(const AutoTypeKey* action) = 0;
virtual void execClearField(const AutoTypeClearField* action) = 0;

Expand Down
109 changes: 75 additions & 34 deletions src/autotype/xcb/AutoTypeXCB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@

AutoTypePlatformX11::AutoTypePlatformX11()
{
m_dpy = QX11Info::display();
// Qt handles XCB slightly differently so we open our own connection
m_dpy = XOpenDisplay(DisplayString(QX11Info::display()));
m_rootWindow = QX11Info::appRootWindow();

m_atomWmState = XInternAtom(m_dpy, "WM_STATE", True);
Expand Down Expand Up @@ -93,6 +94,8 @@ void AutoTypePlatformX11::unload()
XkbFreeKeyboard(m_xkb, XkbAllComponentsMask, True);
}

XCloseDisplay(m_dpy);

m_loaded = false;
}

Expand Down Expand Up @@ -341,6 +344,36 @@ void AutoTypePlatformX11::updateKeymap()
/* Xlib needs some time until the mapping is distributed to
all clients */
Tools::sleep(30);

/* Build updated keymap */
m_keymap.clear();

XkbDescPtr desc = XkbGetMap(m_dpy, XkbAllClientInfoMask, XkbUseCoreKbd);

for (int ckeycode = desc->min_key_code; ckeycode < desc->max_key_code; ckeycode++) {
int groups = XkbKeyNumGroups(desc, ckeycode);

for (int cgroup = 0; cgroup < groups; cgroup++) {
XkbKeyTypePtr type = XkbKeyKeyType(desc, ckeycode, cgroup);

for (int clevel = 0; clevel < type->num_levels; clevel++) {
KeySym sym = XkbKeycodeToKeysym(m_dpy, ckeycode, cgroup, clevel);

int mask = 0;
for (int nmap = 0; nmap < type->map_count; nmap++) {
XkbKTMapEntryRec map = type->map[nmap];
if (map.active && map.level == clevel) {
mask = map.mods.mask;
break;
}
}

m_keymap.append(AutoTypePlatformX11::KeyDesc{sym, ckeycode, cgroup, mask});
}
}
}

XkbFreeKeyboard(desc, 0, 1);
}

bool AutoTypePlatformX11::isRemapKeycodeValid()
Expand Down Expand Up @@ -435,42 +468,26 @@ void AutoTypePlatformX11::SendModifiers(unsigned int mask, bool press)
* Determines the keycode and modifier mask for the given
* keysym.
*/
int AutoTypePlatformX11::GetKeycode(KeySym keysym, unsigned int* mask)
bool AutoTypePlatformX11::GetKeycode(KeySym keysym, int* keycode, int* group, unsigned int* mask)
{
int keycode = XKeysymToKeycode(m_dpy, keysym);

if (keycode && keysymModifiers(keysym, keycode, mask)) {
return keycode;
}

/* no modifier matches => resort to remapping */
keycode = AddKeysym(keysym);
if (keycode && keysymModifiers(keysym, keycode, mask)) {
return keycode;
}
const KeyDesc* desc = nullptr;

*mask = 0;
return 0;
}

bool AutoTypePlatformX11::keysymModifiers(KeySym keysym, int keycode, unsigned int* mask)
{
int shift, mod;
unsigned int mods_rtrn;

/* determine whether there is a combination of the modifiers
(Mod1-Mod5) with or without shift which returns keysym */
for (shift = 0; shift < 2; shift++) {
for (mod = ControlMapIndex; mod <= Mod5MapIndex; mod++) {
KeySym keysym_rtrn;
*mask = (mod == ControlMapIndex) ? shift : shift | (1 << mod);
XkbTranslateKeyCode(m_xkb, keycode, *mask, &mods_rtrn, &keysym_rtrn);
if (keysym_rtrn == keysym) {
return true;
for (const auto& key : m_keymap) {
if (key.sym == keysym) {
// pick this description if we don't have any for this sym or this matches the current group
if (desc == nullptr || key.group == *group) {
desc = &key;
}
}
}

if (desc) {
*keycode = desc->code;
*group = desc->group;
*mask = desc->mask;
return true;
}

return false;
}

Expand All @@ -487,14 +504,20 @@ void AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers)
}

int keycode;
int group;
unsigned int wanted_mask;

/* determine keycode and mask for the given keysym */
keycode = GetKeycode(keysym, &wanted_mask);
if (keycode < 8 || keycode > 255) {
XkbStateRec state;
XkbGetState(m_dpy, XkbUseCoreKbd, &state);
int old_group = state.group;
group = old_group;

/* determine keycode, group and mask for the given keysym */
if (!GetKeycode(keysym, &keycode, &group, &wanted_mask)) {
qWarning("Unable to get valid keycode for key: keysym=0x%lX", keysym);
return;
}

wanted_mask |= modifiers;

Window root, child;
Expand Down Expand Up @@ -541,6 +564,12 @@ void AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers)
release_mask = release_check_mask;
}

/* change layout group if necessary */
if (old_group != group) {
XkbLockGroup(m_dpy, XkbUseCoreKbd, group);
XFlush(m_dpy);
}

/* set modifiers mask */
if ((release_mask | press_mask) & LockMask) {
SendModifiers(LockMask, true);
Expand All @@ -560,6 +589,12 @@ void AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers)
SendModifiers(LockMask, true);
SendModifiers(LockMask, false);
}

/* reset layout group if necessary */
if (old_group != group) {
XkbLockGroup(m_dpy, XkbUseCoreKbd, old_group);
XFlush(m_dpy);
}
}

int AutoTypePlatformX11::MyErrorHandler(Display* my_dpy, XErrorEvent* event)
Expand All @@ -579,6 +614,12 @@ AutoTypeExecutorX11::AutoTypeExecutorX11(AutoTypePlatformX11* platform)
{
}

void AutoTypeExecutorX11::execBegin(const AutoTypeBegin* action)
{
Q_UNUSED(action);
m_platform->updateKeymap();
}

void AutoTypeExecutorX11::execType(const AutoTypeKey* action)
{
if (action->key != Qt::Key_unknown) {
Expand Down
15 changes: 12 additions & 3 deletions src/autotype/xcb/AutoTypeXCB.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class AutoTypePlatformX11 : public QObject, public AutoTypePlatformInterface
QString activeWindowTitle() override;
bool raiseWindow(WId window) override;
AutoTypeExecutor* createExecutor() override;
void updateKeymap();

void sendKey(KeySym keysym, unsigned int modifiers = 0);

Expand All @@ -65,13 +66,11 @@ class AutoTypePlatformX11 : public QObject, public AutoTypePlatformInterface
bool isTopLevelWindow(Window window);

XkbDescPtr getKeyboard();
void updateKeymap();
bool isRemapKeycodeValid();
int AddKeysym(KeySym keysym);
void SendKeyEvent(unsigned keycode, bool press);
void SendModifiers(unsigned int mask, bool press);
int GetKeycode(KeySym keysym, unsigned int* mask);
bool keysymModifiers(KeySym keysym, int keycode, unsigned int* mask);
bool GetKeycode(KeySym keysym, int* keycode, int* group, unsigned int* mask);

static int MyErrorHandler(Display* my_dpy, XErrorEvent* event);

Expand All @@ -97,13 +96,23 @@ class AutoTypePlatformX11 : public QObject, public AutoTypePlatformInterface
KeySym m_currentRemapKeysym;
KeyCode m_modifier_keycode[N_MOD_INDICES];
bool m_loaded;

typedef struct {
KeySym sym;
int code;
int group;
int mask;
} KeyDesc;

QList<KeyDesc> m_keymap;
};

class AutoTypeExecutorX11 : public AutoTypeExecutor
{
public:
explicit AutoTypeExecutorX11(AutoTypePlatformX11* platform);

void execBegin(const AutoTypeBegin* action) override;
void execType(const AutoTypeKey* action) override;
void execClearField(const AutoTypeClearField* action) override;

Expand Down
Loading

0 comments on commit 320dccf

Please sign in to comment.