Reputation: 11302
I can determine the current mouse pointer position with:
from Xlib.display import Display
display = Display()
qp = display.screen().root.query_pointer()
print(qp.root_x, qp.root_y)
How do I get the current mouse button states like left/right button pressed/released via Xlib
, too? (Or if this is not possible - why not?)
Upvotes: 3
Views: 1289
Reputation: 51
If the X server has the RECORD extension enabled, it can be used to find out the current mouse button state. However, it won't provide the information directly: The individual button up and down events have to be tracked.
This also means that the current state will only be known, once the program has received at least one first event on mouse button up or down.
python-xlib
provides the following example script at https://github.com/python-xlib/python-xlib/blob/master/examples/record_demo.py
from __future__ import print_function import sys import os # Change path so we find Xlib sys.path.append(os.path.join(os.path.dirname(__file__), '..')) from Xlib import X, XK, display from Xlib.ext import record from Xlib.protocol import rq local_dpy = display.Display() record_dpy = display.Display() def lookup_keysym(keysym): for name in dir(XK): if name[:3] == "XK_" and getattr(XK, name) == keysym: return name[3:] return "[%d]" % keysym def record_callback(reply): if reply.category != record.FromServer: return if reply.client_swapped: print("* received swapped protocol data, cowardly ignored") return if not len(reply.data) or reply.data[0] < 2: # not an event return data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value( data, record_dpy.display, None, None) if event.type in [X.KeyPress, X.KeyRelease]: pr = event.type == X.KeyPress and "Press" or "Release" keysym = local_dpy.keycode_to_keysym(event.detail, 0) if not keysym: print("KeyCode%s" % pr, event.detail) else: print("KeyStr%s" % pr, lookup_keysym(keysym)) if event.type == X.KeyPress and keysym == XK.XK_Escape: local_dpy.record_disable_context(ctx) local_dpy.flush() return elif event.type == X.ButtonPress: print("ButtonPress", event.detail) elif event.type == X.ButtonRelease: print("ButtonRelease", event.detail) elif event.type == X.MotionNotify: print("MotionNotify", event.root_x, event.root_y) # Check if the extension is present if not record_dpy.has_extension("RECORD"): print("RECORD extension not found") sys.exit(1) r = record_dpy.record_get_version(0, 0) print("RECORD extension version %d.%d" % (r.major_version, r.minor_version)) # Create a recording context; we only want key and mouse events ctx = record_dpy.record_create_context( 0, [record.AllClients], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.KeyPress, X.MotionNotify), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) # Enable the context; this only returns after a call to record_disable_context, # while calling the callback function in the meantime record_dpy.record_enable_context(ctx, record_callback) # Finally free the context record_dpy.record_free_context(ctx)
Upvotes: 1
Reputation: 443
Your X window must support XInput extension. Real X works but getting to mouse button doesn't work if the X server doesn't support the extension like VNC server.
If the X server supports it, then you can get to the mouse state as follows:
from Xlib.display import Display
from Xlib.ext import xinput
display = Display()
import time
while True:
buttons = []
for device_info in display.xinput_query_device(xinput.AllDevices).devices:
if not device_info.enabled:
continue
if xinput.ButtonClass not in [ device_class.type for device_class in device_info.classes ]:
continue
buttons.append(device_info)
for button in buttons:
for device_class in button.classes:
if xinput.ButtonClass == device_class.type:
if device_class.state[0]:
print('Device {name} - Primary button down'.format(name=button.name))
time.sleep(1)
I'm not 100% sure as the docs are not found anywhere, but I'm pretty sure device_class.state[0] is primary (left button), 1 is middle, and 2 is right button.
You can probably find out the button number assignment spec here
EDIT:
Why there are two for loops - First I wrote the "buttons" part outside of forever loop. But, "hey, you can plug in mouse any time."
You'll find that, there are many devices including "virtual" ones. On laptop, touchpad does work as button too so in your app, if you want to know the real mouse's buttons, you may have to pick a device from name. Again, there is no good docs so you probably have to decipher the device class object. You can find the xinput as /usr/lib/python3/dist-packages/Xlib/ext/xinput.py. (Adjust it accordingly if you are using Python2.) Good luck.
Upvotes: 1