------------------------------------------------------------------------------- X Window Event Handling There are many programs which allow you to launch applications or do certain actions based on X events they receive (hotkeys). This is especially the case with Window Managers. Basically they grab events to perform the action requested, then either allow the event to pass on to other clients... Or not! This includes, Twm, CTwm, Openbox, Gnome 2 Session, etc, etc.... If you do have a window manager with good event handler configuration (Not Gnome) then that is typically the best place to add your hotkeys. But sometimes you don't have that option, or you are running clients without any window manager (such as specially setup Kiosk Terminals). Other programs to generate 'hotkey' events also include: xbindkeys xhkeys bbkeys xhotkeys xrebind For a execute command when mouse is in a specific spot try: xautolock For Sending or injecting events into the X window system you programs such as "xdotool" One of the best tools. With many user requested features, and active development. "xte" (from the "xautomation" package) "xmacroplay" (from the "xmacro" package) "xvkbd" (virtual onscreen keyboard - no package known) "cnee" (from "Xnee" source - not ferdora package) Very low level (keys not strings) see "xnee.sh" for usage ------------------------------------------------------------------------------- X Events... The "xmodmap" program without arguments will list what keys are used as event (key state) modifiers for example... shift Shift_L (0x32), Shift_R (0x3e) lock Caps_Lock (0x42) control Control_L (0x25), Control_R (0x6d) mod1 Alt_L (0x40), Mode_switch (0x71), Meta_L (0x9c) mod2 Num_Lock (0x4d) mod3 mod4 Super_L (0x7f), Hyper_L (0x80) mod5 Mode_switch (0x5d), ISO_Level3_Shift (0x7c) Shows 8 modifiers are possible (and any combination of modifier can be used) Be warned however that events that have 'modifier' attached are the most trouble. Many programs only 'bind' or use an event that has no modifiers. That means if 'NumLock" is accidentally on some specific key may not actually be reconised by an application! Typically programs either ignore all modifiers, or ignore/handle specific ones globally. For example, ignore "mod2" (numLock), or merge "lock" and "shift" modifiers to mean the same thing. But each program does this in its own way. For example "FireFox" web browser will ignore key stroke events that happen while "mod4" (typically the Super or Windows key) is pressed. On the other hand a normal "XTerm" will just ignore the "mod4" modifier and use the keys pressed as is. Also you should note that keyboard presses are actually two separate events, specifically KeyDown and KeyUp events. Whcih one an application uses is again up to the application, but typicaly they perform actions on KeyDown. This is much more important for modify keys where you get sequences like Alt_L down A down A up Alt_L up Note it is posible for an application to treat ANY key as a modifier EG: do something only if all the keys A, S and D are down. Though not all physical keyboards can generate these situation, and I know of no application that does this. ------------ Watching Events... You can see what xevents are being generated by looking at the output of the "xev" program. However I find you get swamped in Mouse movement events so I tend to use this command instead... xterm -g 80x30+60+10 -e \ sh -c 'xev -g +730+10 | sed "/^MotionNotify/,/^$/d"' The 'sed' removes the movements from xev output. More importantally this command will list the actual key names, and the appropriate capitalizations used. For example these are specific 'keysym' used for specific keys: "quotedbl", "Delete", "BackSpace" WARNING: xev may not see (and thus not show) some event that some other program has globally captured for some reason. Programs that can do this include the window manager, Gnome keyboard shortcuts, or "xbindkeys". As an alturnative you can also use "xbindkeys -k" to see what event various key pressed will generate. ------------------------------------------------------------------------------- Generating X Keyboard Events (Global Keyboard Macros). You can for example create new X events (such as key strokes) by using a program such as "xmacroplay" (xmacro package), "xte" (xautomation package) or the "xdotool" program. This works well from the command line. For example xte 'str your.email.address@this.machine.com' Will act as if the user typed that string (an email address). Of course it doesn't work as a typed command, but what if this was run while the mouse pointer was in a different window, such as a text editor, or some webpage form. For example try this sleep 5; xte 'str your.email.address@this.machine.com' and quickly move your mouse pointer to some other window (click to make it active if need be). When the sleep finished suddenly the string would have appeared in the other window as if you typed it yourself. It does not matter what input box it is, or whether it was designed to allow "CUT 'n' PASTE" or anything. The application will just accept the input as if the string was typed at the keyboard. Now if you can arrange for it to execute this command when you press a certain key, the longer string will be immediatally typed in. And you can Atype it into any input box. It does not matter if this inptu is a terminal, a mail program or a web form! To the program the user typed it (very quickly). In other words a Global Keyboard Macro. --- This is good in theory, but some tricky and confusing problems pop up when you try to put the above into practice, causing it to either not work, work intermittently, or with the letters becoming shifted, or as control characters. To demonstrate the problems, lets call our keyboard marco from a general event handler such as "xbindkeys". First create a default ".xbindkeysrc" file in your home... xbindkeys --defaults Now edit the file ".xbindkeysrc", and add the following test bindings... =======8<--------CUT HERE---------- # TESTING # "zenity --info --text 'email.address@machine.com'" F1 # "xte 'str email.address@machine.com'" F2 # "xte 'usleep 50000' 'str email.address@machine.com'" F3 # "xte 'usleep 50000' 'str email.address@machine.com'" Shift + F4 # "xte 'usleep 500000' 'str email.address@machine.com'" Mod4 + F5 # =======8<--------CUT HERE---------- Now run "xbindkeys", but lets turn off its background daemon mode so we can easilly stop it using Ctrl-C. xbindkeys -n Now if you press 'F1' anywhere on your display, it should run the 'zenity' program to pop up a window containing the email address.. That shows "xbindkeys" is working, and it has bound an action to 'F1'. (xbindkeys will ignore CapsLock and NumLock modifiers by default) But when you press 'F2' with the input directed to a XTerm window, nothing happens. -- WHY Well that is because it is being 'actioned' while the "F2" key was will being held down!!!! That means in reality you are sending the events... F2-down e-down e-release m-down m-release.... That is 'F2' and 'e' keys are BOTH pressed together, and very few X window programs, will handle multiple key combinations such as this. ASIDE: programs can do this, and some like games do, but generally they don't. Simple Solution: Delay the Macro Output Now try giving a quick tap of 'F3' in an xterm window, and suddenly the string will appear, or at least the last part of the string will appear if you were a little slow in your key tap. The delay (usleep) option causes the "xte" program to wait a moment before it outputs the macro string. That gives the user time to release the 'F3' key, so the later key events will be clear of multiple key presses and come out correctly. The sleep makes the difference. Solution: Bind to the Release of Keys Some event binding software, can be set do the requested action when the key is released, rather than pressed. that means the events will not be generated until after the 'F3' (or whatever) was released by the user. This will avoid any 'multi-key' event generation problems, and allow a a binding to work without delay. The "openbox" window manager, for example, does this by default (no choice). "Twm", "CTwm" and other old-style window managers can also specify 'release' key binding events separateally to 'press' events. Do you know any other key binding programs that lets you do this? The Bigger Problem: Modifiers... Unfortunately that does NOT help when event 'modifiers' become involved. For example try playing with the Shift-F4 combination in an XTerm window. If you try this you will probably find the email address will appear, but with all the letters capitalized. At least up until the '@' sign. That is because even if you wait for the primary key to be released, users are much slower in releasing modifiers like Shift, Control, Alt, Super, etc.. As such the modifier will still be pressed when the macro is generated, producing shifted or capitalized result. If this was a Ctrl modifier you would generate control events, and that can be VERY bad, not to mention DANGEROUS! Ctrl key macros are NOT recommended. For a modifier like Mod4 (Super/Meta), some applications may ignore the modifier, while other applications may not. A terminal for example will typically ignore the 'Super' or 'mod4' modifier and you will get the email address printed, but other applications like "FireFox" completely ignore keyboard events that have a 'mod4' modifier, so nothing gets 'typed' into web forms, where you could use it the most. Try it again with the next keyboard binding that was setup above. The 'Windows-F5' or 'Super-F5' key-binding (the "Mod4" modifier in the binding) will work with "firefox", but only if you release the modifier key quickly. That is because by delaying a longer half second, now gives the user plenty of time release both the primary key, and the 'Window' modifier key as well. UPDATE: the "xdotool" has a --clearmodifiers option that will fix this problem! You no longer need to delay to give the user time to release the modifier! xdotool type --clearmodifiers -delay 0 'A.Thyssen@griffith.edu.au' The -delay in the above is to speed up 'typing' of the string. WARNING: before version 2.20110530 the above are fairly slow, but with that release typing is extremely quick. Problem: Recursive Macros... You should not try binding a keyboard macro to a normal alphabetic key, and especially not without some sort of modifier. For example try this key binding into an xterm window, but keep the 'Super' or 'Windows' key modifier pressed for a moment... =======8<--------CUT HERE---------- "xte 'usleep 500000' 'str email.address@machine.com'" Mod4 + a =======8<--------CUT HERE---------- What will happen is that it will continually repeat the macro over and over and over until you release the 'Super/Windows' modifier. What happens is that "xte" injects the X Window Events as if it is from the keyboard. As such "xbindkeys" (or any such event binding program) will see all the keyboard events that the macro itself produces! As such the "xbindkeys" will see the 'a' in the macro, and if the user is still pressing the 'mod4' modifier, then it will expand the macro again! Basically it will loop continuously, until you release the modifier. The Ideal Solution... Ideal solution would be to either wait for both the primary key and modifer to be released before sending the event, OR somehow turn off the modifier while the keyboard macro is being output. Ideally you would combine a program like "xbindkeys" with "xte" so that while a Keyboard Macro is being output, it can monitor the events (including the macro events it is itself generating) and keep track of modifiers, and other events, delaying or adjusting the modifier events, so while the macro was being output, any existing modifiers do not effect the macro output. That is if a keyboard macro was started using an event "Mod4 + a" with the action taken on key release), then it turns off whatever key caused the 'Mod4' and watches for other events from the user (such as other modifier changes). When the macro has finally streamed passed, the other events can be forward as if nothing had happened. ------------------------------------------------------------------------------- XDoTool -- Event handler. As mentioned above "xdotool" has added features that make it ideal for generating keyboard macros. As a bonus it also has * Window Manager controls (similar to those provided by "xwit") * It also has built-in "xwininfo" search support * Mouse Input Controls (movements, clicks, warps) * Each key press can be delayed as if you were typing (default) or not! * Build a single free standing 'static' version (do dynamic libraries) (from version 2010 10 ) However what makes the program specifically useful is * a special --clearmodifiers option This option ensures any and all user 'shift' keys are released before you feed your own events. That way you don't generate strings with all the characters 'Ctrl' or 'Alt' shifted! (from version 2010-08 ) You can Download the latest it from http://www.semicomplete.com/projects/xdotool/ or a beta release (zip) from GitHub https://github.com/jordansissel/xdotool You may like to browse the "xdotool-users" google group mailing list ------------------------------------------------------------------------------- Example... Keyboard Macro - Using OpenBox keyboard binding... xte 'usleep 500000' \ 'str A.Thyssen@griffith.edu.au' With this, any time I press the Windows and F1 keys together the marco will pause 1/2 second for me to release the Windows Key then type into the current input box my password. I use it all the time when writeing Emails, Gmails, Web Page Messages, and even for login on some web sites. -- Very quick and easy. xdotool example (no delay needed) xdotool type --clearmodifiers -delay 0 'A.Thyssen@griffith.edu.au' ------------------------------------------------------------------------------- Example... Generating Mouse Events This is a little script that causes the mouse to 'double click' where ever it is to select and then grab the selected word for further processing. =======8<-------- #!/bin/bash # getdclick: simulate double-click and get selected word # Time to wait between clicks (test what works for you) DCLICK_WAIT=0.1 # Before double-click, simulate a Release of all buttons # then simulate double click xte "mouseup 1" "mouseup 2" "mouseup 3" "mouseup 4" "mouseup 5" \ "mouseclick 1" "sleep $DCLICK_WAIT" "mouseclick 1" # Get selection first word from primary X clipbloard SELECTION=$(xsel -p | awk '{print $1}') echo $SELECTION exit 0 =======8<-------- You can use this in a event action, for example dictionary lookup gnome-dictionary $(getdclick) Example: Send Ctrl-Alt-N to the currently active client xte 'keydown Control_L' 'keydown Alt_L' \ 'key n' \ 'keyup Control_L' 'keyup Alt_L' ------------------------------------------------------------------------------- Typing Strings with Return Keys Often when typing a string to use as an event sequence you can't specify 'Return Key' in the string. After all how does the application knows if "\n" means '\' and 'n' or just a Return key. Here is a way for xdotool to do this. Essentually including the actual return character in the argument that is given to xdotool itself sleep 1; xdotool type "hello world " or better still.. sleep 1; xdotool type "$(printf "hello\nworld\n")" ------------------------------------------------------------------------------- XSendEvents vs XTest Events XTest events is a additional module to the X Server. It is usually present but not always. It is untargeted, and as such follows focus sending the fake event to the currently active window. It basically means that if the focus changes while events are being sent, the events may not reach its intended target. XSendEvents is older and has always been present in X windows. It will only target a specific window, and not the general 'current focus'. Of course that means XSendEvents can not target the window manager, or something like xbindkeys, only specific applications, though that will prevent posible 'event loops'. The BIG problem is that XSendEvents have a flag set in the event structure, that will cause many applications to ignore the event. For example XTerm's and the Firefox Browser will ignore XSendEvents. This is its biggest drawback and why it is not used much. --- Override the rejection of XSendEvent by application... This lets you run a application so it never sees that 'send_event' flag. See XSendEvent + LD_PRELOAD == win http://www.semicomplete.com/blog/geekery/xsendevent-xdotool-and-ld_preload.html Basically create a custom library that will overrides the XNextEvent() and XPeekEvent() functions used by an application, forcably setting the 'send_event' to always be false. (Using LD_PRELOAD) =======8<-------- #include #include void hack_send_event(XEvent *ev) { switch (ev->type) { case KeyPress: case KeyRelease: case ButtonPress: case ButtonRelease: ev->xany.send_event = False; break; } } override(`XNextEvent', ` { real_func(display, event_return); hack_send_event(event_return); return; } ') =======8<-------- Compile this into a shared library and set the LD_PRELOAD environment variable before running the application. WARNING: Firefox will ignore all events when it does not have focus. As such you still have to give focus to firefox before sending it events, just like you would do for a X Test Events. In Summery: It may not be worth the trouble! Use Xtest events instead. -------------------------------------------------------------------------------