Showing posts with label xlib. Show all posts
Showing posts with label xlib. Show all posts

Tuesday, 15 October 2013

Sending key presses to specific windows in X

For a project I wanted to send key presses to a specific X Window, so I dug out my code from "Moving mouse and pressing keys in X using python" and made some modifications.
I also included the code I wrote in "screenshots using Python under Linux" for finding a particular window id from its name.


As an example the following code takes a window name as an argument to the program when run, finds the window id and then sends the key press of F11 (the window I wanted to interact with was a browser window and therefore it will go fullscreen).

#!/usr/bin/python
"""
Issue a keypress event in X to a specific window
"""
from Xlib import XK, display, ext, X, protocol
import sys
import time

def get_window(display, name):
    root_win = display.screen().root
    window_list = [root_win]

    while len(window_list) != 0:
        win = window_list.pop(0)
        if win.get_wm_name() == name:
           return win.id
        children = win.query_tree().children
        if children != None:
            window_list += children

    print 'Unable to find window matching - %s\n' % name
    sys.exit(1)


d=display.Display()

win=get_window(d,sys.argv[1])

keysym=XK.string_to_keysym("F11")
keycode=d.keysym_to_keycode(keysym)

event = protocol.event.KeyPress(
   time = int(time.time()),
   root = d.screen().root,
   window = win,
   same_screen = 0, child = X.NONE,
   root_x = 0, root_y = 0, event_x = 0, event_y = 0,
   state = 0,
   detail = keycode
)
d.send_event(win, event, propagate=True)
d.sync()
event = protocol.event.KeyRelease(
   time = int(time.time()),
   root = d.screen().root,
   window = win,
   same_screen = 0, child = X.NONE,
   root_x = 0, root_y = 0, event_x = 0, event_y = 0,
   state = 0,
   detail = keycode
)
d.send_event(win, event, propagate=True)
d.sync()

While this seemed to work (my Firefox browser window went into fullscreen mode), I did start to see some strange behaviour, such as none of the menus would display either from the toolbar or right clicking in the window. I did notice that clicking on a menu did very briefly flash up what looked like an outline, so I wondered if this could have anything to do with focus and modified my code like so.

#!/usr/bin/python
"""
Issue a keypress event in X to a specific window
"""
from Xlib import XK, display, ext, X, protocol
import sys
import time

def get_window(display, name):
root_win = display.screen().root
window_list = [root_win]

while len(window_list) != 0:
win = window_list.pop(0)
#print win.get_wm_name()
if win.get_wm_name() == name:
return win.id
children = win.query_tree().children
if children != None:
window_list += children

print 'Unable to find window matching - %s\n' % name
sys.exit(1)

d=display.Display()

win=get_window(d,sys.argv[1])

keysym=XK.string_to_keysym("F11")
keycode=d.keysym_to_keycode(keysym)

#store current input focus
currentfocus=d.get_input_focus()

#set input focus to selected window
d.set_input_focus(win, X.RevertToParent,X.CurrentTime)

#send keypress and keyrelease
ext.xtest.fake_input(d, X.KeyPress, keycode)
ext.xtest.fake_input(d, X.KeyRelease, keycode)

#revert focus to original window
d.set_input_focus(currentfocus.focus,X.RevertToParent,X.CurrentTime)

d.sync()
d.close()

This seems to work much better and Firefox still works correctly.

Wednesday, 11 September 2013

Moving mouse and pressing keys in X using python

I have just seen a cool piece of code in python which simulates key presses and mouse clicks to automatically play a browser based game. This was on Windows but it got me thinking of how similar keyboard and mouse events could be simulated in Linux using pure python (I could wrap programs such as xvkbd or xdotool, or even write some extensions in C and hook into xlib).

Fortunately someone has already written a python X library (Xlib) which I can utilise. Here are a couple of simple programs to get an idea of how to simulate mouse and keypress events.

First up how to move the mouse. This snippet moves the moves to a set location and clicks the first mouse button (left click for my setup)


#!/usr/bin/python
from Xlib import X, display, ext

d = display.Display()
s = d.screen()
root = s.root
#move pointer to set location
root.warp_pointer(300,300)
d.sync()
#press button 1, for middle mouse button use 2, for opposite button use 3
ext.xtest.fake_input(d, X.ButtonPress,1)
d.sync()
#we want a click so we need to also relese the same button
ext.xtest.fake_input(d, X.ButtonRelease,1)
d.sync()


Now, how to simulate keypresses.

#!/usr/bin/python
from Xlib import XK, display, ext, X

d=display.Display()
#send F1 as in gnome this will bring up help screen, proves it works
keysym=XK.string_to_keysym("F1")
keycode=d.keysym_to_keycode(keysym)
#press key
ext.xtest.fake_input(d, X.KeyPress, keycode)
d.sync()
#remember to release it, otherwise help screen will continue to appear
ext.xtest.fake_input(d, X.KeyRelease, keycode)
d.sync()