1 /***************************************************************************
2 * Copyright (C) 2005-2008 by Tarek Saidi, Felix Geyer *
3 * tarek.saidi@arcor.de *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; version 2 of the License. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include "AutoTypeX11.h"
23 #include "mainwindow.h"
24 #include "lib/HelperX11.h"
27 #ifndef GLOBAL_AUTOTYPE
28 AutoType* autoType = NULL;
30 void initAutoType(KeepassMainWindow* mainWin) {
31 autoType = new AutoTypeX11(mainWin);
35 AutoTypeAction::AutoTypeAction(AutoTypeActionType t, KeySym d) : type(t), data(d){
38 AutoTypeX11::AutoTypeX11(KeepassMainWindow* mainWin) {
39 this->mainWin = mainWin;
40 dpy = QX11Info::display();
47 altgr_keysym = NoSymbol;
53 void AutoTypeX11::updateKeymap() {
56 AddModifier(XK_Mode_switch);
61 Window AutoTypeX11::getFocusWindow() {
64 XGetInputFocus(dpy, &w, &revert_to_return);
67 XTextProperty textProp;
68 if (XGetWMName(dpy, w, &textProp) != 0) {
73 Window* children = NULL;
74 unsigned int num_children;
75 tree = XQueryTree(dpy, w, &root, &parent, &children, &num_children);
77 if (children) XFree(children);
83 void AutoTypeX11::perform(IEntryHandle* entry, bool hideWindow, int nr, bool wasLocked){
90 indexStr = "Auto-Type:";
92 indexStr = QString("Auto-Type-%1:").arg(nr);
94 QString comment=entry->comment();
95 int c=comment.count(indexStr, Qt::CaseInsensitive);
97 qWarning("More than one 'Auto-Type:' key sequence found.\nAllowed is only one per entry.");
101 int start = comment.indexOf(indexStr,0,Qt::CaseInsensitive) + indexStr.length();
102 int end = comment.indexOf("\n", start);
104 end = comment.length();
106 str=comment.mid(start,end-start).trimmed();
111 bool usernameEmpty = entry->username().trimmed().isEmpty();
112 SecString password=entry->password();
114 bool passwordEmpty = password.string().trimmed().isEmpty();
115 if (usernameEmpty && passwordEmpty)
117 else if (usernameEmpty)
118 str="{PASSWORD}{ENTER}";
119 else if (passwordEmpty)
120 str="{USERNAME}{ENTER}";
122 str="{USERNAME}{TAB}{PASSWORD}{ENTER}";
125 QList<AutoTypeAction> Keys;
126 for(int i=0;i<str.size();i++){
130 while(str[i]!='}' && i<str.size()){
135 qWarning("Syntax Error in Auto-Type sequence near character %d\nFound '{' without closing '}'", i+10);
138 templateToKeysyms(tmpl.toLower(),Keys,entry);
142 Keys << AutoTypeAction(TypeKey, str[i].unicode());
146 /* Re-read keymap before first auto-type,
147 seems to be necessary on X.Org Server 1.6,
148 when KeePassX is in autostart */
157 QApplication::processEvents();
158 sleepTime(config->autoTypePreGap());
161 focusWindow = getFocusWindow();
164 for(int i=0;i<Keys.size();i++){
165 if (focusWindow != getFocusWindow()) {
166 qWarning("Focus window changed, interrupting auto-type");
170 if (Keys[i].type==TypeKey){
171 SendKeyPressedEvent(Keys[i].data, 0);
172 sleepKeyStrokeDelay();
174 else if (Keys[i].type==Delay){
175 QApplication::processEvents();
176 sleepTime(Keys[i].data);
180 if (config->lockOnMinimize()){
181 if (hideWindow || wasLocked){
182 if ( !(config->showSysTrayIcon() && config->minimizeTray()) )
183 mainWin->showMinimized();
185 mainWin->OnUnLockWorkspace();
189 if (hideWindow && !(config->showSysTrayIcon() && config->minimizeTray()) )
190 mainWin->showMinimized();
197 void AutoTypeX11::sleepTime(int msec){
199 timespec timeOut, remains;
200 timeOut.tv_sec = msec/1000;
201 timeOut.tv_nsec = (msec%1000)*1000000;
202 nanosleep(&timeOut, &remains);
205 void AutoTypeX11::templateToKeysyms(const QString& tmpl, QList<AutoTypeAction>& keys,IEntryHandle* entry){
206 //tmpl must be lower case!!!
207 if(!tmpl.compare("title")){
208 stringToKeysyms(entry->title(),keys);
211 if(!tmpl.compare("username")){
212 stringToKeysyms(entry->username(),keys);
215 if(!tmpl.compare("url")){
216 stringToKeysyms(entry->url(),keys);
219 if(!tmpl.compare("password")){
220 SecString password=entry->password();
222 stringToKeysyms(password,keys);
225 if(!tmpl.compare("space")){
226 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym(' '));
230 if(!tmpl.compare("backspace") || !tmpl.compare("bs") || !tmpl.compare("bksp")){
231 keys << AutoTypeAction(TypeKey, XK_BackSpace);
235 if(!tmpl.compare("break")){
236 keys << AutoTypeAction(TypeKey, XK_Break);
240 if(!tmpl.compare("capslock")){
241 keys << AutoTypeAction(TypeKey, XK_Caps_Lock);
245 if(!tmpl.compare("del") || !tmpl.compare("delete")){
246 keys << AutoTypeAction(TypeKey, XK_Delete);
250 if(!tmpl.compare("end")){
251 keys << AutoTypeAction(TypeKey, XK_End);
255 if(!tmpl.compare("enter")){
256 keys << AutoTypeAction(TypeKey, XK_Return);
260 if(!tmpl.compare("esc")){
261 keys << AutoTypeAction(TypeKey, XK_Escape);
265 if(!tmpl.compare("help")){
266 keys << AutoTypeAction(TypeKey, XK_Help);
270 if(!tmpl.compare("home")){
271 keys << AutoTypeAction(TypeKey, XK_Home);
275 if(!tmpl.compare("insert") || !tmpl.compare("ins")){
276 keys << AutoTypeAction(TypeKey, XK_Insert);
280 if(!tmpl.compare("numlock")){
281 keys << AutoTypeAction(TypeKey, XK_Num_Lock);
285 if(!tmpl.compare("scroll")){
286 keys << AutoTypeAction(TypeKey, XK_Scroll_Lock);
290 if(!tmpl.compare("pgdn")){
291 keys << AutoTypeAction(TypeKey, XK_Page_Down);
295 if(!tmpl.compare("pgup")){
296 keys << AutoTypeAction(TypeKey, XK_Page_Up);
300 if(!tmpl.compare("prtsc")){
301 keys << AutoTypeAction(TypeKey, XK_3270_PrintScreen);
305 if(!tmpl.compare("up")){
306 keys << AutoTypeAction(TypeKey, XK_Up);
310 if(!tmpl.compare("down")){
311 keys << AutoTypeAction(TypeKey, XK_Down);
315 if(!tmpl.compare("left")){
316 keys << AutoTypeAction(TypeKey, XK_Left);
320 if(!tmpl.compare("right")){
321 keys << AutoTypeAction(TypeKey, XK_Right);
325 if(!tmpl.compare("f1")){
326 keys << AutoTypeAction(TypeKey, XK_F1);
330 if(!tmpl.compare("f2")){
331 keys << AutoTypeAction(TypeKey, XK_F2);
335 if(!tmpl.compare("f3")){
336 keys << AutoTypeAction(TypeKey, XK_F3);
340 if(!tmpl.compare("f4")){
341 keys << AutoTypeAction(TypeKey, XK_F4);
345 if(!tmpl.compare("f5")){
346 keys << AutoTypeAction(TypeKey, XK_F5);
350 if(!tmpl.compare("f6")){
351 keys << AutoTypeAction(TypeKey, XK_F6);
355 if(!tmpl.compare("f7")){
356 keys << AutoTypeAction(TypeKey, XK_F7);
360 if(!tmpl.compare("f8")){
361 keys << AutoTypeAction(TypeKey, XK_F8);
365 if(!tmpl.compare("f9")){
366 keys << AutoTypeAction(TypeKey, XK_F9);
370 if(!tmpl.compare("f10")){
371 keys << AutoTypeAction(TypeKey, XK_F10);
375 if(!tmpl.compare("f11")){
376 keys << AutoTypeAction(TypeKey, XK_F11);
380 if(!tmpl.compare("f12")){
381 keys << AutoTypeAction(TypeKey, XK_F12);
385 if(!tmpl.compare("f13")){
386 keys << AutoTypeAction(TypeKey, XK_F13);
390 if(!tmpl.compare("f14")){
391 keys << AutoTypeAction(TypeKey, XK_F14);
395 if(!tmpl.compare("f15")){
396 keys << AutoTypeAction(TypeKey, XK_F15);
400 if(!tmpl.compare("f16")){
401 keys << AutoTypeAction(TypeKey, XK_F16);
405 if(!tmpl.compare("add") || !tmpl.compare("plus")){
406 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('+'));
410 if(!tmpl.compare("subtract")){
411 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('-'));
415 if(!tmpl.compare("multiply")){
416 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('+'));
420 if(!tmpl.compare("divide")){
421 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('/'));
425 if(!tmpl.compare("at")){
426 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('@'));
430 if(!tmpl.compare("percent")){
431 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('%'));
435 if(!tmpl.compare("caret")){
436 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('^'));
440 if(!tmpl.compare("tilde")){
441 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('~'));
445 if(!tmpl.compare("leftbrace")){
446 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('{'));
450 if(!tmpl.compare("rightbrace")){
451 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('}'));
455 if(!tmpl.compare("leftparen")){
456 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('('));
460 if(!tmpl.compare("rightparen")){
461 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym(')'));
465 if(!tmpl.compare("winl")){
466 keys << AutoTypeAction(TypeKey, XK_Super_L);
470 if(!tmpl.compare("winr")){
471 keys << AutoTypeAction(TypeKey, XK_Super_R);
475 if(!tmpl.compare("win")){
476 keys << AutoTypeAction(TypeKey, XK_Super_L);
480 if(!tmpl.compare("tab")){
481 keys << AutoTypeAction(TypeKey, XK_Tab);
485 if(tmpl.startsWith("delay ") && tmpl.length()>6){
487 quint16 delay = tmpl.right(tmpl.length()-6).toInt(&ok);
488 if (ok && delay>0 && delay<=10000)
489 keys << AutoTypeAction(Delay, delay);
493 void AutoTypeX11::stringToKeysyms(const QString& string,QList<AutoTypeAction>& KeySymList){
494 for(int i=0; i<string.length();i++)
495 KeySymList << AutoTypeAction(TypeKey, HelperX11::getKeysym(string[i]));
499 // ----------------------------------------------------------------------
500 // The following code is taken from xvkbd and has been slightly modified.
501 // ----------------------------------------------------------------------
504 * xvkbd - Virtual Keyboard for X Window System
505 * (Version 3.0, 2008-05-05)
507 * Copyright (C) 2000-2008 by Tom Sato <VEF00200@nifty.ne.jp>
508 * http://homepage3.nifty.com/tsato/
510 * This program is free software; you can redistribute it and/or
511 * modify it under the terms of the GNU General Public License
512 * as published by the Free Software Foundation; either version 2
513 * of the License, or any later version.
515 * This program is distributed in the hope that it will be useful,
516 * but WITHOUT ANY WARRANTY; without even the implied warranty of
517 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
518 * See the GNU General Public License for more details.
522 * Insert a specified keysym to unused position in the keymap table.
523 * This will be called to add required keysyms on-the-fly.
524 * if the second parameter is TRUE, the keysym will be added to the
525 * non-shifted position - this may be required for modifier keys
526 * (e.g. Mode_switch) and some special keys (e.g. F20).
528 int AutoTypeX11::AddKeysym(KeySym keysym, bool top)
530 int keycode, pos, max_pos, inx, phase;
535 max_pos = keysym_per_keycode - 1;
536 if (4 <= max_pos) max_pos = 3;
537 if (2 <= max_pos && altgr_keysym != XK_Mode_switch) max_pos = 1;
540 for (phase = 0; phase < 2; phase++) {
541 for (keycode = max_keycode; min_keycode <= keycode; keycode--) {
542 for (pos = max_pos; 0 <= pos; pos--) {
543 inx = (keycode - min_keycode) * keysym_per_keycode;
544 if ((phase != 0 || keysym_table[inx] == NoSymbol) && keysym_table[inx] < 0xFF00) {
545 /* In the first phase, to avoid modifing existing keys, */
546 /* add the keysym only to the keys which has no keysym in the first position. */
547 /* If no place fuond in the first phase, add the keysym for any keys except */
548 /* for modifier keys and other special keys */
549 if (keysym_table[inx + pos] == NoSymbol) {
550 keysym_table[inx + pos] = keysym;
551 XChangeKeyboardMapping(dpy, keycode, keysym_per_keycode, &keysym_table[inx], 1);
559 qWarning("Couldn't add \"%s\" to keymap", XKeysymToString(keysym));
564 * Add the specified key as a new modifier.
565 * This is used to use Mode_switch (AltGr) as a modifier.
567 void AutoTypeX11::AddModifier(KeySym keysym)
569 XModifierKeymap *modifiers;
572 keycode = XKeysymToKeycode(dpy, keysym);
573 if (keycode == NoSymbol) keycode = AddKeysym(keysym, TRUE);
575 modifiers = XGetModifierMapping(dpy);
576 for (i = 7; 3 < i; i--) {
577 if (modifiers->modifiermap[i * modifiers->max_keypermod] == NoSymbol
578 || ((keysym_table[(modifiers->modifiermap[i * modifiers->max_keypermod]
579 - min_keycode) * keysym_per_keycode]) == XK_ISO_Level3_Shift
580 && keysym == XK_Mode_switch))
582 for (pos = 0; pos < modifiers->max_keypermod; pos++) {
583 if (modifiers->modifiermap[i * modifiers->max_keypermod + pos] == NoSymbol) {
584 modifiers->modifiermap[i * modifiers->max_keypermod + pos] = keycode;
585 XSetModifierMapping(dpy, modifiers);
591 qWarning("Couldn't add \"%s\" as modifier", XKeysymToString(keysym));
595 * Read keyboard mapping and modifier mapping.
596 * Keyboard mapping is used to know what keys are in shifted position.
597 * Modifier mapping is required because we should know Alt and Meta
598 * key are used as which modifier.
600 void AutoTypeX11::ReadKeymap()
603 int keycode, inx, pos;
605 XModifierKeymap *modifiers;
608 XDisplayKeycodes(dpy, &min_keycode, &max_keycode);
609 if (keysym_table != NULL) XFree(keysym_table);
610 keysym_table = XGetKeyboardMapping(dpy,
611 min_keycode, max_keycode - min_keycode + 1,
612 &keysym_per_keycode);
613 for (keycode = min_keycode; keycode <= max_keycode; keycode++) {
614 /* if the first keysym is alphabet and the second keysym is NoSymbol,
615 it is equivalent to pair of lowercase and uppercase alphabet */
616 inx = (keycode - min_keycode) * keysym_per_keycode;
617 if (keysym_table[inx + 1] == NoSymbol
618 && ((XK_A <= keysym_table[inx] && keysym_table[inx] <= XK_Z)
619 || (XK_a <= keysym_table[inx] && keysym_table[inx] <= XK_z)))
621 if (XK_A <= keysym_table[inx] && keysym_table[inx] <= XK_Z)
622 keysym_table[inx] = keysym_table[inx] - XK_A + XK_a;
623 keysym_table[inx + 1] = keysym_table[inx] - XK_a + XK_A;
627 last_altgr_mask = altgr_mask;
631 altgr_keysym = NoSymbol;
632 modifiers = XGetModifierMapping(dpy);
633 for (i = 0; i < 8; i++) {
634 for (pos = 0; pos < modifiers->max_keypermod; pos++) {
635 keycode = modifiers->modifiermap[i * modifiers->max_keypermod + pos];
636 if (keycode < min_keycode || max_keycode < keycode) continue;
638 keysym = keysym_table[(keycode - min_keycode) * keysym_per_keycode];
639 if (keysym == XK_Alt_L || keysym == XK_Alt_R) {
641 } else if (keysym == XK_Meta_L || keysym == XK_Meta_R) {
643 } else if (keysym == XK_Mode_switch) {
644 if (altgr_keysym == XK_ISO_Level3_Shift) {
646 altgr_mask = 0x0101 << i;
647 /* I don't know why, but 0x2000 was required for mod3 on my Linux box */
648 altgr_keysym = keysym;
650 } else if (keysym == XK_ISO_Level3_Shift) {
651 /* if no Mode_switch, try to use ISO_Level3_Shift instead */
652 /* however, it may not work as intended - I don't know why */
654 altgr_keysym = keysym;
658 XFreeModifiermap(modifiers);
662 * Send event to the focused window.
663 * If input focus is specified explicitly, select the window
664 * before send event to the window.
666 void AutoTypeX11::SendEvent(XKeyEvent *event)
668 XSync(event->display, FALSE);
669 int (*oldHandler) (Display*, XErrorEvent*) = XSetErrorHandler(MyErrorHandler);
671 XTestFakeKeyEvent(event->display, event->keycode, event->type == KeyPress, 0);
672 XFlush(event->display);
674 XSetErrorHandler(oldHandler);
678 * Send sequence of KeyPressed/KeyReleased events to the focused
679 * window to simulate keyboard. If modifiers (shift, control, etc)
680 * are set ON, many events will be sent.
682 void AutoTypeX11::SendKeyPressedEvent(KeySym keysym, unsigned int shift)
691 XGetInputFocus(dpy, &cur_focus, &revert_to);
695 if (keysym != NoSymbol) {
696 for (phase = 0; phase < 2; phase++) {
697 for (keycode = min_keycode; !found && (keycode <= max_keycode); keycode++) {
698 /* Determine keycode for the keysym: we use this instead
699 of XKeysymToKeycode() because we must know shift_state, too */
700 inx = (keycode - min_keycode) * keysym_per_keycode;
701 if (keysym_table[inx] == keysym) {
702 shift &= ~altgr_mask;
703 if (keysym_table[inx + 1] != NoSymbol) shift &= ~ShiftMask;
706 } else if (keysym_table[inx + 1] == keysym) {
707 shift &= ~altgr_mask;
713 if (!found && altgr_mask && 3 <= keysym_per_keycode) {
714 for (keycode = min_keycode; !found && (keycode <= max_keycode); keycode++) {
715 inx = (keycode - min_keycode) * keysym_per_keycode;
716 if (keysym_table[inx + 2] == keysym) {
721 } else if (4 <= keysym_per_keycode && keysym_table[inx + 3] == keysym) {
722 shift |= ShiftMask | altgr_mask;
730 if (0xF000 <= keysym) {
731 /* for special keys such as function keys,
732 first try to add it in the non-shifted position of the keymap */
733 if (AddKeysym(keysym, TRUE) == NoSymbol) AddKeysym(keysym, FALSE);
735 AddKeysym(keysym, FALSE);
741 event.window = cur_focus;
742 event.root = RootWindow(event.display, DefaultScreen(event.display));
743 event.subwindow = None;
744 event.time = CurrentTime;
749 event.same_screen = TRUE;
752 int root_x, root_y, x, y;
755 XQueryPointer(dpy, event.root, &root, &child, &root_x, &root_y, &x, &y, &mask);
757 event.type = KeyRelease;
759 if (mask & ControlMask) {
760 event.keycode = XKeysymToKeycode(dpy, XK_Control_L);
763 if (mask & alt_mask) {
764 event.keycode = XKeysymToKeycode(dpy, XK_Alt_L);
767 if (mask & meta_mask) {
768 event.keycode = XKeysymToKeycode(dpy, XK_Meta_L);
771 if (mask & altgr_mask) {
772 event.keycode = XKeysymToKeycode(dpy, altgr_keysym);
775 if (mask & ShiftMask) {
776 event.keycode = XKeysymToKeycode(dpy, XK_Shift_L);
779 if (mask & LockMask) {
780 event.keycode = XKeysymToKeycode(dpy, XK_Caps_Lock);
784 event.type = KeyPress;
786 if (shift & ControlMask) {
787 event.keycode = XKeysymToKeycode(dpy, XK_Control_L);
789 event.state |= ControlMask;
791 if (shift & alt_mask) {
792 event.keycode = XKeysymToKeycode(dpy, XK_Alt_L);
794 event.state |= alt_mask;
796 if (shift & meta_mask) {
797 event.keycode = XKeysymToKeycode(dpy, XK_Meta_L);
799 event.state |= meta_mask;
801 if (shift & altgr_mask) {
802 event.keycode = XKeysymToKeycode(dpy, altgr_keysym);
804 event.state |= altgr_mask;
806 if (shift & ShiftMask) {
807 event.keycode = XKeysymToKeycode(dpy, XK_Shift_L);
809 event.state |= ShiftMask;
812 if (keysym != NoSymbol) { /* send event for the key itself */
813 event.keycode = found ? keycode : XKeysymToKeycode(dpy, keysym);
814 if (event.keycode == NoSymbol) {
815 if ((keysym & ~0x7f) == 0 && isprint(keysym))
816 qWarning("No such key: %c", (char)keysym);
817 else if (XKeysymToString(keysym) != NULL)
818 qWarning("No such key: keysym=%s (0x%lX)", XKeysymToString(keysym), (long)keysym);
820 qWarning("No such key: keysym=0x%lX", (long)keysym);
823 event.type = KeyRelease;
828 event.type = KeyRelease;
829 if (shift & ShiftMask) {
830 event.keycode = XKeysymToKeycode(dpy, XK_Shift_L);
832 event.state &= ~ShiftMask;
834 if (shift & altgr_mask) {
835 event.keycode = XKeysymToKeycode(dpy, altgr_keysym);
837 event.state &= ~altgr_mask;
839 if (shift & meta_mask) {
840 event.keycode = XKeysymToKeycode(dpy, XK_Meta_L);
842 event.state &= ~meta_mask;
844 if (shift & alt_mask) {
845 event.keycode = XKeysymToKeycode(dpy, XK_Alt_L);
847 event.state &= ~alt_mask;
849 if (shift & ControlMask) {
850 event.keycode = XKeysymToKeycode(dpy, XK_Control_L);
852 event.state &= ~ControlMask;
856 int AutoTypeX11::MyErrorHandler(Display *my_dpy, XErrorEvent *event)
860 if (event->error_code == BadWindow) {
863 XGetErrorText(my_dpy, event->error_code, msg, sizeof(msg) - 1);
864 qWarning("X error trapped: %s, request-code=%d\n", msg, event->request_code);