Initial commit
[keepassx] / src / lib / AutoTypeX11.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2008 by Tarek Saidi, Felix Geyer                   *
3  *   tarek.saidi@arcor.de                                                  *
4  *                                                                         *
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.               *
8
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #include "AutoTypeX11.h"
22
23 #include "mainwindow.h"
24 #include "lib/HelperX11.h"
25 #include <QX11Info>
26
27 #ifndef GLOBAL_AUTOTYPE
28 AutoType* autoType = NULL;
29
30 void initAutoType(KeepassMainWindow* mainWin) {
31         autoType = new AutoTypeX11(mainWin);
32 }
33 #endif
34
35 AutoTypeAction::AutoTypeAction(AutoTypeActionType t, KeySym d) : type(t), data(d){
36 }
37
38 AutoTypeX11::AutoTypeX11(KeepassMainWindow* mainWin) {
39         this->mainWin = mainWin;
40         dpy = QX11Info::display();
41         inAutoType = false;
42         
43         keysym_table = NULL;
44         alt_mask = 0;
45         meta_mask = 0;
46         altgr_mask = 0;
47         altgr_keysym = NoSymbol;
48         
49         updateKeymap();
50         reReadKeymap = false;
51 }
52
53 void AutoTypeX11::updateKeymap() {
54         ReadKeymap();
55         if (!altgr_mask)
56                 AddModifier(XK_Mode_switch);
57         if (!meta_mask)
58                 meta_mask = Mod4Mask;
59 }
60
61 Window AutoTypeX11::getFocusWindow() {
62         Window w;
63         int revert_to_return;
64         XGetInputFocus(dpy, &w, &revert_to_return);
65         int tree;
66         do {
67                 XTextProperty textProp;
68                 if (XGetWMName(dpy, w, &textProp) != 0) {
69                         break;
70                 }
71                 Window root = 0;
72                 Window parent = 0;
73                 Window* children = NULL;
74                 unsigned int num_children;
75                 tree = XQueryTree(dpy, w, &root, &parent, &children, &num_children);
76                 w = parent;
77                 if (children) XFree(children);
78         } while (tree && w);
79         
80         return w;
81 }
82
83 void AutoTypeX11::perform(IEntryHandle* entry, bool hideWindow, int nr, bool wasLocked){
84         if (inAutoType)
85                 return;
86         inAutoType = true;
87         
88         QString indexStr;
89         if (nr==0)
90                 indexStr = "Auto-Type:";
91         else
92                 indexStr = QString("Auto-Type-%1:").arg(nr);
93         QString str;
94         QString comment=entry->comment();
95         int c=comment.count(indexStr, Qt::CaseInsensitive);
96         if(c>1) {
97                 qWarning("More than one 'Auto-Type:' key sequence found.\nAllowed is only one per entry.");
98                 return;
99         }
100         else if (c==1) {
101                 int start = comment.indexOf(indexStr,0,Qt::CaseInsensitive) + indexStr.length();
102                 int end = comment.indexOf("\n", start);
103                 if (end == -1)
104                         end = comment.length();
105                 
106                 str=comment.mid(start,end-start).trimmed();
107                 if (str.isEmpty())
108                         return;
109         }
110         else {
111                 bool usernameEmpty = entry->username().trimmed().isEmpty();
112                 SecString password=entry->password();
113                 password.unlock();
114                 bool passwordEmpty = password.string().trimmed().isEmpty();
115                 if (usernameEmpty && passwordEmpty)
116                         return;
117                 else if (usernameEmpty)
118                         str="{PASSWORD}{ENTER}";
119                 else if (passwordEmpty)
120                         str="{USERNAME}{ENTER}";
121                 else
122                         str="{USERNAME}{TAB}{PASSWORD}{ENTER}";
123         }
124         
125         QList<AutoTypeAction> Keys;
126         for(int i=0;i<str.size();i++){
127                 if(str[i]=='{'){
128                         QString tmpl;
129                         i++;
130                         while(str[i]!='}' && i<str.size()){
131                                 tmpl += str[i];
132                                 i++;
133                         }
134                         if(i>=str.size()){
135                                 qWarning("Syntax Error in Auto-Type sequence near character %d\nFound '{' without closing '}'", i+10);
136                                 return;
137                         }
138                         templateToKeysyms(tmpl.toLower(),Keys,entry);
139                         continue;
140                 }
141                 else{
142                         Keys << AutoTypeAction(TypeKey, str[i].unicode());
143                 }
144         }
145         
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 */
149         if (!reReadKeymap) {
150                 updateKeymap();
151                 reReadKeymap = true;
152         }
153         
154         if (hideWindow)
155                 mainWin->hide();
156         
157         QApplication::processEvents();
158         sleepTime(config->autoTypePreGap());
159         
160         if (!focusWindow)
161                 focusWindow = getFocusWindow();
162         
163         QString type;
164         for(int i=0;i<Keys.size();i++){
165                 if (focusWindow != getFocusWindow()) {
166                         qWarning("Focus window changed, interrupting auto-type");
167                         break;
168                 }
169                 
170                 if (Keys[i].type==TypeKey){
171                         SendKeyPressedEvent(Keys[i].data, 0);
172                         sleepKeyStrokeDelay();
173                 }
174                 else if (Keys[i].type==Delay){
175                         QApplication::processEvents();
176                         sleepTime(Keys[i].data);
177                 }
178         }
179         
180         if (config->lockOnMinimize()){
181                 if (hideWindow || wasLocked){
182                         if ( !(config->showSysTrayIcon() && config->minimizeTray()) )
183                                 mainWin->showMinimized();
184                         else
185                                 mainWin->OnUnLockWorkspace();
186                 }
187         }
188         else{
189                 if (hideWindow && !(config->showSysTrayIcon() && config->minimizeTray()) )
190                         mainWin->showMinimized();
191         }
192         
193         inAutoType = false;
194         focusWindow = NULL;
195 }
196
197 void AutoTypeX11::sleepTime(int msec){
198         if (msec==0) return;
199         timespec timeOut, remains;
200         timeOut.tv_sec = msec/1000;
201         timeOut.tv_nsec = (msec%1000)*1000000;
202         nanosleep(&timeOut, &remains);
203 }
204
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);
209                 return;
210         }
211         if(!tmpl.compare("username")){
212                 stringToKeysyms(entry->username(),keys);
213                 return;
214         }
215         if(!tmpl.compare("url")){
216                 stringToKeysyms(entry->url(),keys);
217                 return;
218         }
219         if(!tmpl.compare("password")){
220                 SecString password=entry->password();
221                 password.unlock();
222                 stringToKeysyms(password,keys);
223                 return;
224         }
225         if(!tmpl.compare("space")){
226                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym(' '));
227                 return;
228         }
229         
230         if(!tmpl.compare("backspace") || !tmpl.compare("bs") || !tmpl.compare("bksp")){
231                 keys << AutoTypeAction(TypeKey, XK_BackSpace);
232                 return;
233         }
234         
235         if(!tmpl.compare("break")){
236                 keys << AutoTypeAction(TypeKey, XK_Break);
237                 return;
238         }
239         
240         if(!tmpl.compare("capslock")){
241                 keys << AutoTypeAction(TypeKey, XK_Caps_Lock);
242                 return;
243         }
244         
245         if(!tmpl.compare("del") || !tmpl.compare("delete")){
246                 keys << AutoTypeAction(TypeKey, XK_Delete);
247                 return;
248         }
249         
250         if(!tmpl.compare("end")){
251                 keys << AutoTypeAction(TypeKey, XK_End);
252                 return;
253         }
254         
255         if(!tmpl.compare("enter")){
256                 keys << AutoTypeAction(TypeKey, XK_Return);
257                 return;
258         }
259         
260         if(!tmpl.compare("esc")){
261                 keys << AutoTypeAction(TypeKey, XK_Escape);
262                 return;
263         }
264         
265         if(!tmpl.compare("help")){
266                 keys << AutoTypeAction(TypeKey, XK_Help);
267                 return;
268         }
269         
270         if(!tmpl.compare("home")){
271                 keys << AutoTypeAction(TypeKey, XK_Home);
272                 return;
273         }
274         
275         if(!tmpl.compare("insert") || !tmpl.compare("ins")){
276                 keys << AutoTypeAction(TypeKey, XK_Insert);
277                 return;
278         }
279         
280         if(!tmpl.compare("numlock")){
281                 keys << AutoTypeAction(TypeKey, XK_Num_Lock);
282                 return;
283         }
284         
285         if(!tmpl.compare("scroll")){
286                 keys << AutoTypeAction(TypeKey, XK_Scroll_Lock);
287                 return;
288         }
289         
290         if(!tmpl.compare("pgdn")){
291                 keys << AutoTypeAction(TypeKey, XK_Page_Down);
292                 return;
293         }
294         
295         if(!tmpl.compare("pgup")){
296                 keys << AutoTypeAction(TypeKey, XK_Page_Up);
297                 return;
298         }
299         
300         if(!tmpl.compare("prtsc")){
301                 keys << AutoTypeAction(TypeKey, XK_3270_PrintScreen);
302                 return;
303         }
304         
305         if(!tmpl.compare("up")){
306                 keys << AutoTypeAction(TypeKey, XK_Up);
307                 return;
308         }
309         
310         if(!tmpl.compare("down")){
311                 keys << AutoTypeAction(TypeKey, XK_Down);
312                 return;
313         }
314         
315         if(!tmpl.compare("left")){
316                 keys << AutoTypeAction(TypeKey, XK_Left);
317                 return;
318         }
319         
320         if(!tmpl.compare("right")){
321                 keys << AutoTypeAction(TypeKey, XK_Right);
322                 return;
323         }
324         
325         if(!tmpl.compare("f1")){
326                 keys << AutoTypeAction(TypeKey, XK_F1);
327                 return;
328         }
329         
330         if(!tmpl.compare("f2")){
331                 keys << AutoTypeAction(TypeKey, XK_F2);
332                 return;
333         }
334         
335         if(!tmpl.compare("f3")){
336                 keys << AutoTypeAction(TypeKey, XK_F3);
337                 return;
338         }
339         
340         if(!tmpl.compare("f4")){
341                 keys << AutoTypeAction(TypeKey, XK_F4);
342                 return;
343         }
344         
345         if(!tmpl.compare("f5")){
346                 keys << AutoTypeAction(TypeKey, XK_F5);
347                 return;
348         }
349         
350         if(!tmpl.compare("f6")){
351                 keys << AutoTypeAction(TypeKey, XK_F6);
352                 return;
353         }
354         
355         if(!tmpl.compare("f7")){
356                 keys << AutoTypeAction(TypeKey, XK_F7);
357                 return;
358         }
359         
360         if(!tmpl.compare("f8")){
361                 keys << AutoTypeAction(TypeKey, XK_F8);
362                 return;
363         }
364         
365         if(!tmpl.compare("f9")){
366                 keys << AutoTypeAction(TypeKey, XK_F9);
367                 return;
368         }
369         
370         if(!tmpl.compare("f10")){
371                 keys << AutoTypeAction(TypeKey, XK_F10);
372                 return;
373         }
374         
375         if(!tmpl.compare("f11")){
376                 keys << AutoTypeAction(TypeKey, XK_F11);
377                 return;
378         }
379         
380         if(!tmpl.compare("f12")){
381                 keys << AutoTypeAction(TypeKey, XK_F12);
382                 return;
383         }
384         
385         if(!tmpl.compare("f13")){
386                 keys << AutoTypeAction(TypeKey, XK_F13);
387                 return;
388         }
389         
390         if(!tmpl.compare("f14")){
391                 keys << AutoTypeAction(TypeKey, XK_F14);
392                 return;
393         }
394         
395         if(!tmpl.compare("f15")){
396                 keys << AutoTypeAction(TypeKey, XK_F15);
397                 return;
398         }
399         
400         if(!tmpl.compare("f16")){
401                 keys << AutoTypeAction(TypeKey, XK_F16);
402                 return;
403         }
404         
405         if(!tmpl.compare("add") || !tmpl.compare("plus")){
406                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('+'));
407                 return;
408         }
409         
410         if(!tmpl.compare("subtract")){
411                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('-'));
412                 return;
413         }
414         
415         if(!tmpl.compare("multiply")){
416                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('+'));
417                 return;
418         }
419         
420         if(!tmpl.compare("divide")){
421                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('/'));
422                 return;
423         }
424         
425         if(!tmpl.compare("at")){
426                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('@'));
427                 return;
428         }
429         
430         if(!tmpl.compare("percent")){
431                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('%'));
432                 return;
433         }
434         
435         if(!tmpl.compare("caret")){
436                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('^'));
437                 return;
438         }
439         
440         if(!tmpl.compare("tilde")){
441                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('~'));
442                 return;
443         }
444         
445         if(!tmpl.compare("leftbrace")){
446                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('{'));
447                 return;
448         }
449         
450         if(!tmpl.compare("rightbrace")){
451                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('}'));
452                 return;
453         }
454         
455         if(!tmpl.compare("leftparen")){
456                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym('('));
457                 return;
458         }
459         
460         if(!tmpl.compare("rightparen")){
461                 keys << AutoTypeAction(TypeKey, HelperX11::getKeysym(')'));
462                 return;
463         }
464         
465         if(!tmpl.compare("winl")){
466                 keys << AutoTypeAction(TypeKey, XK_Super_L);
467                 return;
468         }
469         
470         if(!tmpl.compare("winr")){
471                 keys << AutoTypeAction(TypeKey, XK_Super_R);
472                 return;
473         }
474         
475         if(!tmpl.compare("win")){
476                 keys << AutoTypeAction(TypeKey, XK_Super_L);
477                 return;
478         }
479         
480         if(!tmpl.compare("tab")){
481                 keys << AutoTypeAction(TypeKey, XK_Tab);
482                 return;
483         }
484         
485         if(tmpl.startsWith("delay ") && tmpl.length()>6){
486                 bool ok;
487                 quint16 delay = tmpl.right(tmpl.length()-6).toInt(&ok);
488                 if (ok && delay>0 && delay<=10000)
489                         keys << AutoTypeAction(Delay, delay);
490         }
491 }
492
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]));
496 }
497
498
499 // ----------------------------------------------------------------------
500 // The following code is taken from xvkbd and has been slightly modified.
501 // ----------------------------------------------------------------------
502
503 /*
504  * xvkbd - Virtual Keyboard for X Window System
505  * (Version 3.0, 2008-05-05)
506  *
507  * Copyright (C) 2000-2008 by Tom Sato <VEF00200@nifty.ne.jp>
508  * http://homepage3.nifty.com/tsato/
509  *
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.
514  *
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.
519  */
520
521 /*
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).
527  */
528 int AutoTypeX11::AddKeysym(KeySym keysym, bool top)
529 {
530         int keycode, pos, max_pos, inx, phase;
531
532         if (top) {
533                 max_pos = 0;
534         } else {
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;
538         }
539
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);
552                                                 XFlush(dpy);
553                                                 return keycode;
554                                         }
555                                 }
556                         }
557                 }
558         }
559         qWarning("Couldn't add \"%s\" to keymap", XKeysymToString(keysym));
560         return NoSymbol;
561 }
562
563 /*
564  * Add the specified key as a new modifier.
565  * This is used to use Mode_switch (AltGr) as a modifier.
566  */
567 void AutoTypeX11::AddModifier(KeySym keysym)
568 {
569         XModifierKeymap *modifiers;
570         int keycode, i, pos;
571
572         keycode = XKeysymToKeycode(dpy, keysym);
573         if (keycode == NoSymbol) keycode = AddKeysym(keysym, TRUE);
574         
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))
581                 {
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);
586                                         return;
587                                 }
588                         }
589                 }
590         }
591         qWarning("Couldn't add \"%s\" as modifier", XKeysymToString(keysym));
592 }
593
594 /*
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.
599  */
600 void AutoTypeX11::ReadKeymap()
601 {
602         int i;
603         int keycode, inx, pos;
604         KeySym keysym;
605         XModifierKeymap *modifiers;
606         int last_altgr_mask;
607
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)))
620                 {
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;
624                 }
625         }
626
627         last_altgr_mask = altgr_mask;
628         alt_mask = 0;
629         meta_mask = 0;
630         altgr_mask = 0;
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;
637
638                         keysym = keysym_table[(keycode - min_keycode) * keysym_per_keycode];
639                         if (keysym == XK_Alt_L || keysym == XK_Alt_R) {
640                                 alt_mask = 1 << i;
641                         } else if (keysym == XK_Meta_L || keysym == XK_Meta_R) {
642                                 meta_mask = 1 << i;
643                         } else if (keysym == XK_Mode_switch) {
644                                 if (altgr_keysym == XK_ISO_Level3_Shift) {
645                                 } else {
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;
649                                 }
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 */
653                                 altgr_mask = 1 << i;
654                                 altgr_keysym = keysym;
655                         }
656                 }
657         }
658         XFreeModifiermap(modifiers);
659 }
660
661 /*
662  * Send event to the focused window.
663  * If input focus is specified explicitly, select the window
664  * before send event to the window.
665  */
666 void AutoTypeX11::SendEvent(XKeyEvent *event)
667 {
668         XSync(event->display, FALSE);
669         int (*oldHandler) (Display*, XErrorEvent*) = XSetErrorHandler(MyErrorHandler);
670
671         XTestFakeKeyEvent(event->display, event->keycode, event->type == KeyPress, 0);
672         XFlush(event->display);
673
674         XSetErrorHandler(oldHandler);
675 }
676
677 /*
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.
681  */
682 void AutoTypeX11::SendKeyPressedEvent(KeySym keysym, unsigned int shift)
683 {
684         Window cur_focus;
685         int revert_to;
686         XKeyEvent event;
687         int keycode;
688         int phase, inx;
689         bool found;
690
691         XGetInputFocus(dpy, &cur_focus, &revert_to);
692
693         found = FALSE;
694         keycode = 0;
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;
704                                         found = TRUE;
705                                         break;
706                                 } else if (keysym_table[inx + 1] == keysym) {
707                                         shift &= ~altgr_mask;
708                                         shift |= ShiftMask;
709                                         found = TRUE;
710                                         break;
711                                 }
712                         }
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) {
717                                                 shift &= ~ShiftMask;
718                                                 shift |= altgr_mask;
719                                                 found = TRUE;
720                                                 break;
721                                         } else if (4 <= keysym_per_keycode && keysym_table[inx + 3] == keysym) {
722                                                 shift |= ShiftMask | altgr_mask;
723                                                 found = TRUE;
724                                                 break;
725                                         }
726                                 }
727                         }
728                         if (found) break;
729
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);
734                         } else {
735                                 AddKeysym(keysym, FALSE);
736                         }
737                 }
738         }
739
740         event.display = dpy;
741         event.window = cur_focus;
742         event.root = RootWindow(event.display, DefaultScreen(event.display));
743         event.subwindow = None;
744         event.time = CurrentTime;
745         event.x = 1;
746         event.y = 1;
747         event.x_root = 1;
748         event.y_root = 1;
749         event.same_screen = TRUE;
750
751         Window root, child;
752         int root_x, root_y, x, y;
753         unsigned int mask;
754
755         XQueryPointer(dpy, event.root, &root, &child, &root_x, &root_y, &x, &y, &mask);
756
757         event.type = KeyRelease;
758         event.state = 0;
759         if (mask & ControlMask) {
760                 event.keycode = XKeysymToKeycode(dpy, XK_Control_L);
761                 SendEvent(&event);
762         }
763         if (mask & alt_mask) {
764                 event.keycode = XKeysymToKeycode(dpy, XK_Alt_L);
765                 SendEvent(&event);
766         }
767         if (mask & meta_mask) {
768                 event.keycode = XKeysymToKeycode(dpy, XK_Meta_L);
769                 SendEvent(&event);
770         }
771         if (mask & altgr_mask) {
772                 event.keycode = XKeysymToKeycode(dpy, altgr_keysym);
773                 SendEvent(&event);
774         }
775         if (mask & ShiftMask) {
776                 event.keycode = XKeysymToKeycode(dpy, XK_Shift_L);
777                 SendEvent(&event);
778         }
779         if (mask & LockMask) {
780                 event.keycode = XKeysymToKeycode(dpy, XK_Caps_Lock);
781                 SendEvent(&event);
782         }
783
784         event.type = KeyPress;
785         event.state = 0;
786         if (shift & ControlMask) {
787                 event.keycode = XKeysymToKeycode(dpy, XK_Control_L);
788                 SendEvent(&event);
789                 event.state |= ControlMask;
790         }
791         if (shift & alt_mask) {
792                 event.keycode = XKeysymToKeycode(dpy, XK_Alt_L);
793                 SendEvent(&event);
794                 event.state |= alt_mask;
795         }
796         if (shift & meta_mask) {
797                 event.keycode = XKeysymToKeycode(dpy, XK_Meta_L);
798                 SendEvent(&event);
799                 event.state |= meta_mask;
800         }
801         if (shift & altgr_mask) {
802                 event.keycode = XKeysymToKeycode(dpy, altgr_keysym);
803                 SendEvent(&event);
804                 event.state |= altgr_mask;
805         }
806         if (shift & ShiftMask) {
807                 event.keycode = XKeysymToKeycode(dpy, XK_Shift_L);
808                 SendEvent(&event);
809                 event.state |= ShiftMask;
810         }
811
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);
819                         else
820                                 qWarning("No such key: keysym=0x%lX", (long)keysym);
821                 } else {
822                         SendEvent(&event);
823                         event.type = KeyRelease;
824                         SendEvent(&event);
825                 }
826         }
827
828         event.type = KeyRelease;
829         if (shift & ShiftMask) {
830                 event.keycode = XKeysymToKeycode(dpy, XK_Shift_L);
831                 SendEvent(&event);
832                 event.state &= ~ShiftMask;
833         }
834         if (shift & altgr_mask) {
835                 event.keycode = XKeysymToKeycode(dpy, altgr_keysym);
836                 SendEvent(&event);
837                 event.state &= ~altgr_mask;
838         }
839         if (shift & meta_mask) {
840                 event.keycode = XKeysymToKeycode(dpy, XK_Meta_L);
841                 SendEvent(&event);
842                 event.state &= ~meta_mask;
843         }
844         if (shift & alt_mask) {
845                 event.keycode = XKeysymToKeycode(dpy, XK_Alt_L);
846                 SendEvent(&event);
847                 event.state &= ~alt_mask;
848         }
849         if (shift & ControlMask) {
850                 event.keycode = XKeysymToKeycode(dpy, XK_Control_L);
851                 SendEvent(&event);
852                 event.state &= ~ControlMask;
853         }
854 }
855
856 int AutoTypeX11::MyErrorHandler(Display *my_dpy, XErrorEvent *event)
857 {
858         char msg[200];
859
860         if (event->error_code == BadWindow) {
861                 return 0;
862         }
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);
865         return 0;
866 }