| 1 | # This file is part of Xpra. |
| 2 | # Copyright (C) 2017 Antoine Martin <antoine@devloop.org.uk> |
| 3 | # Xpra is released under the terms of the GNU GPL v2, or, at your option, any |
| 4 | # later version. See the file COPYING for details. |
| 5 | |
| 6 | import os |
| 7 | import time |
| 8 | import collections |
| 9 | |
| 10 | from xpra.log import Logger |
| 11 | log = Logger("x11", "bindings", "xinput") |
| 12 | |
| 13 | from xpra.x11.gtk2.common import X11Event |
| 14 | |
| 15 | from libc.stdint cimport uintptr_t |
| 16 | |
| 17 | |
| 18 | ################################### |
| 19 | # Headers, python magic |
| 20 | ################################### |
| 21 | cdef extern from "string.h": |
| 22 | void* memset(void * ptr, int value, size_t num) |
| 23 | |
| 24 | cdef extern from "X11/Xutil.h": |
| 25 | pass |
| 26 | |
| 27 | ###### |
| 28 | # Xlib primitives and constants |
| 29 | ###### |
| 30 | |
| 31 | include "constants.pxi" |
| 32 | ctypedef unsigned long CARD32 |
| 33 | |
| 34 | cdef extern from "X11/Xlib.h": |
| 35 | ctypedef struct Display: |
| 36 | pass |
| 37 | |
| 38 | ctypedef CARD32 XID |
| 39 | ctypedef int Bool |
| 40 | ctypedef int Status |
| 41 | ctypedef CARD32 Atom |
| 42 | ctypedef XID Window |
| 43 | ctypedef CARD32 Time |
| 44 | |
| 45 | ctypedef struct XGenericEventCookie: |
| 46 | int type # of event. Always GenericEvent |
| 47 | unsigned long serial |
| 48 | Bool send_event |
| 49 | Display *display |
| 50 | int extension #major opcode of extension that caused the event |
| 51 | int evtype #actual event type |
| 52 | unsigned int cookie |
| 53 | void *data |
| 54 | |
| 55 | Atom XInternAtom(Display * display, char * atom_name, Bool only_if_exists) |
| 56 | int XFree(void * data) |
| 57 | |
| 58 | Bool XQueryExtension(Display * display, char *name, |
| 59 | int *major_opcode_return, int *first_event_return, int *first_error_return) |
| 60 | |
| 61 | Bool XGetEventData(Display *display, XGenericEventCookie *cookie) |
| 62 | void XFreeEventData(Display *display, XGenericEventCookie *cookie) |
| 63 | |
| 64 | Window XDefaultRootWindow(Display * display) |
| 65 | |
| 66 | Bool XQueryPointer(Display *display, Window w, Window *root_return, Window *child_return, int *root_x_return, int *root_y_return, |
| 67 | int *win_x_return, int *win_y_return, unsigned int *mask_return) |
| 68 | int XFlush(Display *dpy) |
| 69 | |
| 70 | cdef extern from "X11/extensions/XInput2.h": |
| 71 | int XI_LASTEVENT |
| 72 | int XI_DeviceChanged |
| 73 | int XI_KeyPress |
| 74 | int XI_KeyRelease |
| 75 | int XI_ButtonPress |
| 76 | int XI_ButtonRelease |
| 77 | int XI_Motion |
| 78 | int XI_Enter |
| 79 | int XI_Leave |
| 80 | int XI_FocusIn |
| 81 | int XI_FocusOut |
| 82 | int XI_HierarchyChanged |
| 83 | int XI_PropertyEvent |
| 84 | int XI_RawKeyPress |
| 85 | int XI_RawKeyRelease |
| 86 | int XI_RawButtonPress |
| 87 | int XI_RawButtonRelease |
| 88 | int XI_RawMotion |
| 89 | int XI_TouchBegin |
| 90 | int XI_TouchUpdate |
| 91 | int XI_TouchEnd |
| 92 | int XI_TouchOwnership |
| 93 | int XI_RawTouchBegin |
| 94 | int XI_RawTouchUpdate |
| 95 | int XI_RawTouchEnd |
| 96 | |
| 97 | int XIMasterPointer |
| 98 | int XIMasterKeyboard |
| 99 | int XISlavePointer |
| 100 | int XISlaveKeyboard |
| 101 | int XIFloatingSlave |
| 102 | |
| 103 | int XIButtonClass |
| 104 | int XIKeyClass |
| 105 | int XIValuatorClass |
| 106 | int XIScrollClass |
| 107 | int XITouchClass |
| 108 | |
| 109 | int XIAllDevices |
| 110 | int XIAllMasterDevices |
| 111 | |
| 112 | ctypedef struct XIValuatorState: |
| 113 | int mask_len |
| 114 | unsigned char *mask |
| 115 | double *values |
| 116 | |
| 117 | ctypedef struct XIEvent: |
| 118 | int type |
| 119 | unsigned long serial |
| 120 | Bool send_event |
| 121 | Display *display |
| 122 | int extension |
| 123 | int evtype |
| 124 | Time time |
| 125 | |
| 126 | ctypedef struct XIRawEvent: |
| 127 | int type #GenericEvent |
| 128 | unsigned long serial |
| 129 | Bool send_event |
| 130 | Display *display |
| 131 | int extension #XI extension offset |
| 132 | int evtype #XI_RawKeyPress, XI_RawKeyRelease, etc |
| 133 | Time time |
| 134 | int deviceid |
| 135 | int sourceid |
| 136 | int detail |
| 137 | int flags |
| 138 | XIValuatorState valuators |
| 139 | double *raw_values |
| 140 | |
| 141 | ctypedef struct XIButtonState: |
| 142 | int mask_len |
| 143 | unsigned char *mask |
| 144 | |
| 145 | ctypedef struct XIModifierState: |
| 146 | int base |
| 147 | int latched |
| 148 | int locked |
| 149 | int effective |
| 150 | |
| 151 | ctypedef XIModifierState XIGroupState |
| 152 | |
| 153 | ctypedef struct XIDeviceEvent: |
| 154 | int type |
| 155 | unsigned long serial |
| 156 | Bool send_event |
| 157 | Display *display |
| 158 | int extension |
| 159 | int evtype |
| 160 | Time time |
| 161 | int deviceid |
| 162 | int sourceid |
| 163 | int detail |
| 164 | Window root |
| 165 | Window event |
| 166 | Window child |
| 167 | double root_x |
| 168 | double root_y |
| 169 | double event_x |
| 170 | double event_y |
| 171 | int flags |
| 172 | XIButtonState buttons |
| 173 | XIValuatorState valuators |
| 174 | XIModifierState mods |
| 175 | XIGroupState group |
| 176 | |
| 177 | ctypedef struct XIHierarchyInfo: |
| 178 | int deviceid |
| 179 | int attachment |
| 180 | int use |
| 181 | Bool enabled |
| 182 | int flags |
| 183 | |
| 184 | ctypedef struct XIHierarchyEvent: |
| 185 | int type |
| 186 | unsigned long serial |
| 187 | Bool send_event |
| 188 | Display *display |
| 189 | int extension |
| 190 | int evtype #XI_HierarchyChanged |
| 191 | Time time |
| 192 | int flags |
| 193 | int num_info |
| 194 | XIHierarchyInfo *info |
| 195 | |
| 196 | ctypedef struct XIEventMask: |
| 197 | int deviceid |
| 198 | int mask_len |
| 199 | unsigned char* mask |
| 200 | |
| 201 | ctypedef struct XIAnyClassInfo: |
| 202 | int type |
| 203 | int sourceid |
| 204 | |
| 205 | ctypedef struct XIDeviceInfo: |
| 206 | int deviceid |
| 207 | char *name |
| 208 | int use |
| 209 | int attachment |
| 210 | Bool enabled |
| 211 | int num_classes |
| 212 | XIAnyClassInfo **classes |
| 213 | |
| 214 | ctypedef struct XIButtonClassInfo: |
| 215 | int type |
| 216 | int sourceid |
| 217 | int num_buttons |
| 218 | Atom *labels |
| 219 | XIButtonState state |
| 220 | |
| 221 | ctypedef struct XIKeyClassInfo: |
| 222 | int type |
| 223 | int sourceid |
| 224 | int num_keycodes |
| 225 | int *keycodes |
| 226 | |
| 227 | ctypedef struct XIValuatorClassInfo: |
| 228 | int type |
| 229 | int sourceid |
| 230 | int number |
| 231 | Atom label |
| 232 | double min |
| 233 | double max |
| 234 | double value |
| 235 | int resolution |
| 236 | int mode |
| 237 | |
| 238 | ctypedef struct XIScrollClassInfo: |
| 239 | int type |
| 240 | int sourceid |
| 241 | int number |
| 242 | int scroll_type |
| 243 | double increment |
| 244 | int flags |
| 245 | |
| 246 | ctypedef struct XITouchClassInfo: |
| 247 | int type |
| 248 | int sourceid |
| 249 | int mode |
| 250 | int num_touches |
| 251 | |
| 252 | Status XIQueryVersion(Display *display, int *major_version_inout, int *minor_version_inout) |
| 253 | Status XISelectEvents(Display *display, Window win, XIEventMask *masks, int num_masks) |
| 254 | XIDeviceInfo* XIQueryDevice(Display *display, int deviceid, int *ndevices_return) |
| 255 | void XIFreeDeviceInfo(XIDeviceInfo *info) |
| 256 | Atom *XIListProperties(Display *display, int deviceid, int *num_props_return) |
| 257 | Status XIGetProperty(Display *display, int deviceid, Atom property, long offset, long length, |
| 258 | Bool delete_property, Atom type, Atom *type_return, |
| 259 | int *format_return, unsigned long *num_items_return, |
| 260 | unsigned long *bytes_after_return, unsigned char **data) |
| 261 | |
| 262 | |
| 263 | DEF MAX_XI_EVENTS = 64 |
| 264 | DEF XI_EVENT_MASK_SIZE = (MAX_XI_EVENTS+7)//8 |
| 265 | |
| 266 | XI_EVENT_NAMES = { |
| 267 | XI_DeviceChanged : "XI_DeviceChanged", |
| 268 | XI_KeyPress : "XI_KeyPress", |
| 269 | XI_KeyRelease : "XI_KeyRelease", |
| 270 | XI_ButtonPress : "XI_ButtonPress", |
| 271 | XI_ButtonRelease : "XI_ButtonRelease", |
| 272 | XI_Motion : "XI_Motion", |
| 273 | XI_Enter : "XI_Enter", |
| 274 | XI_Leave : "XI_Leave", |
| 275 | XI_FocusIn : "XI_FocusIn", |
| 276 | XI_FocusOut : "XI_FocusOut", |
| 277 | XI_HierarchyChanged : "XI_HierarchyChanged", |
| 278 | XI_PropertyEvent : "XI_PropertyEvent", |
| 279 | XI_RawKeyPress : "XI_RawKeyPress", |
| 280 | XI_RawKeyRelease : "XI_RawKeyRelease", |
| 281 | XI_RawButtonPress : "XI_RawButtonPress", |
| 282 | XI_RawButtonRelease : "XI_RawButtonRelease", |
| 283 | XI_RawMotion : "XI_RawMotion", |
| 284 | XI_TouchBegin : "XI_TouchBegin", |
| 285 | XI_TouchUpdate : "XI_TouchUpdate", |
| 286 | XI_TouchEnd : "XI_TouchEnd", |
| 287 | XI_TouchOwnership : "XI_TouchOwnership", |
| 288 | XI_RawTouchBegin : "XI_RawTouchBegin", |
| 289 | XI_RawTouchUpdate : "XI_RawTouchUpdate", |
| 290 | XI_RawTouchEnd : "XI_RawTouchEnd", |
| 291 | } |
| 292 | |
| 293 | XI_USE = { |
| 294 | XIMasterPointer : "master pointer", |
| 295 | XIMasterKeyboard : "master keyboard", |
| 296 | XISlavePointer : "slave pointer", |
| 297 | XISlaveKeyboard : "slave keyboard", |
| 298 | XIFloatingSlave : "floating slave", |
| 299 | } |
| 300 | |
| 301 | CLASS_INFO = { |
| 302 | XIButtonClass : "button", |
| 303 | XIKeyClass : "key", |
| 304 | XIValuatorClass : "valuator", |
| 305 | XIScrollClass : "scroll", |
| 306 | XITouchClass : "touch", |
| 307 | } |
| 308 | |
| 309 | |
| 310 | from core_bindings cimport _X11CoreBindings |
| 311 | |
| 312 | cdef _X11XI2Bindings singleton = None |
| 313 | def X11XI2Bindings(): |
| 314 | global singleton |
| 315 | if singleton is None: |
| 316 | singleton = _X11XI2Bindings() |
| 317 | return singleton |
| 318 | |
| 319 | cdef class _X11XI2Bindings(_X11CoreBindings): |
| 320 | |
| 321 | cdef int opcode |
| 322 | cdef object events |
| 323 | |
| 324 | def __init__(self): |
| 325 | self.opcode = -1 |
| 326 | self.reset_events() |
| 327 | |
| 328 | def __repr__(self): |
| 329 | return "X11XI2Bindings(%s)" % self.display_name |
| 330 | |
| 331 | def reset_events(self): |
| 332 | self.events = collections.deque(maxlen=100) |
| 333 | |
| 334 | def find_events(self, event_name): |
| 335 | found = 0 |
| 336 | matches = [] |
| 337 | for x in reversed(self.events): |
| 338 | if x.name==event_name and (found==0 or found==x.window): |
| 339 | matches.append(x) |
| 340 | found = x.window |
| 341 | elif found: |
| 342 | break |
| 343 | return matches |
| 344 | |
| 345 | cdef int get_xi_opcode(self, int major=2, int minor=2): |
| 346 | if self.opcode!=-1: |
| 347 | return self.opcode |
| 348 | cdef int opcode, event, error |
| 349 | if not XQueryExtension(self.display, "XInputExtension", &opcode, &event, &error): |
| 350 | log.warn("Warning: XI2 events are not supported") |
| 351 | self.opcode = 0 |
| 352 | return 0 |
| 353 | cdef int rmajor = major, rminor = minor |
| 354 | cdef int rc = XIQueryVersion(self.display, &rmajor, &rminor) |
| 355 | if rc == BadRequest: |
| 356 | log.warn("Warning: no XI2 %i.%i support,", major, minor) |
| 357 | log.warn(" server supports version %i.%i only", rmajor, rminor) |
| 358 | self.opcode = 0 |
| 359 | return 0 |
| 360 | elif rc: |
| 361 | log.warn("Warning: Xlib bug querying XI2, code %i", rc) |
| 362 | self.opcode = 0 |
| 363 | return 0 |
| 364 | self.opcode = opcode |
| 365 | log("get_xi_opcode%s=%i", (major, minor), opcode) |
| 366 | return opcode |
| 367 | |
| 368 | cdef register_parser(self): |
| 369 | log("register_parser()") |
| 370 | if self.opcode>0: |
| 371 | from xpra.x11.gtk2.gdk_bindings import add_x_event_parser |
| 372 | add_x_event_parser(self.opcode, self.parse_xi_event) |
| 373 | |
| 374 | cdef register_gdk_events(self): |
| 375 | log("register_gdk_events()") |
| 376 | if self.opcode<=0: |
| 377 | return |
| 378 | global XI_EVENT_NAMES |
| 379 | from xpra.x11.gtk2.gdk_bindings import add_x_event_signal, add_x_event_type_name |
| 380 | for e, xi_event_name in { |
| 381 | XI_DeviceChanged : "device-changed", |
| 382 | XI_KeyPress : "key-press", |
| 383 | XI_KeyRelease : "key-release", |
| 384 | XI_ButtonPress : "button-press", |
| 385 | XI_ButtonRelease : "button-release", |
| 386 | XI_Motion : "motion", |
| 387 | XI_Enter : "enter", |
| 388 | XI_Leave : "leave", |
| 389 | XI_FocusIn : "focus-in", |
| 390 | XI_FocusOut : "focus-out", |
| 391 | XI_HierarchyChanged : "focus-changed", |
| 392 | XI_PropertyEvent : "property-event", |
| 393 | XI_RawKeyPress : "raw-key-press", |
| 394 | XI_RawKeyRelease : "raw-key-release", |
| 395 | XI_RawButtonPress : "raw-button-press", |
| 396 | XI_RawButtonRelease : "raw-button-release", |
| 397 | XI_RawMotion : "raw-motion", |
| 398 | XI_TouchBegin : "touch-begin", |
| 399 | XI_TouchUpdate : "touch-update", |
| 400 | XI_TouchEnd : "touch-end", |
| 401 | XI_TouchOwnership : "touch-ownership", |
| 402 | XI_RawTouchBegin : "raw-touch-begin", |
| 403 | XI_RawTouchUpdate : "raw-touch-update", |
| 404 | XI_RawTouchEnd : "raw-touch-end", |
| 405 | }.items(): |
| 406 | event = self.opcode+e |
| 407 | add_x_event_signal(event, ("xi-%s" % xi_event_name, None)) |
| 408 | name = XI_EVENT_NAMES[e] |
| 409 | add_x_event_type_name(event, name) |
| 410 | |
| 411 | def select_xi2_events(self): |
| 412 | cdef Window win = XDefaultRootWindow(self.display) |
| 413 | log("select_xi2_events() root window=%#x", win) |
| 414 | assert XI_LASTEVENT<MAX_XI_EVENTS, "bug: source needs to be updated, XI_LASTEVENT=%i" % XI_LASTEVENT |
| 415 | cdef XIEventMask evmasks[1] |
| 416 | cdef unsigned char mask1[XI_EVENT_MASK_SIZE] |
| 417 | memset(mask1, 0, XI_EVENT_MASK_SIZE) |
| 418 | #define XISetMask(ptr, event) (((unsigned char*)(ptr))[(event)>>3] |= (1 << ((event) & 7))) |
| 419 | #XISetMask(mask1, XI_RawMotion) |
| 420 | for e in ( |
| 421 | XI_KeyPress, XI_KeyRelease, |
| 422 | XI_Motion, |
| 423 | XI_HierarchyChanged, |
| 424 | XI_ButtonPress, XI_ButtonRelease, |
| 425 | XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd, |
| 426 | ): |
| 427 | mask1[e>>3] |= (1<< (e & 0x7)) |
| 428 | evmasks[0].deviceid = XIAllDevices #XIAllMasterDevices #XIAllDevices |
| 429 | evmasks[0].mask_len = XI_EVENT_MASK_SIZE |
| 430 | evmasks[0].mask = mask1 |
| 431 | XISelectEvents(self.display, win, evmasks, 1) |
| 432 | XFlush(self.display) |
| 433 | |
| 434 | def parse_xi_event(self, display, uintptr_t _cookie): |
| 435 | log("parse_xi_event(%s)", _cookie) |
| 436 | cdef XGenericEventCookie *cookie = <XGenericEventCookie*> _cookie |
| 437 | cdef XIDeviceEvent *device_e |
| 438 | cdef XIHierarchyEvent * hierarchy_e |
| 439 | cdef XIEvent *xie |
| 440 | cdef XIRawEvent *raw |
| 441 | cdef int i = 0, j = 0 |
| 442 | if not XGetEventData(self.display, cookie): |
| 443 | return None |
| 444 | xie = <XIEvent*> cookie.data |
| 445 | device_e = <XIDeviceEvent*> cookie.data |
| 446 | cdef int xi_type = cookie.evtype |
| 447 | etype = self.opcode+xi_type |
| 448 | global XI_EVENT_NAMES |
| 449 | event_name = XI_EVENT_NAMES.get(xi_type) |
| 450 | if not event_name: |
| 451 | log("unknown XI2 event code: %i", xi_type) |
| 452 | return None |
| 453 | |
| 454 | pyev = X11Event(event_name) |
| 455 | pyev.type = etype |
| 456 | pyev.display = display |
| 457 | pyev.send_event = bool(xie.send_event) |
| 458 | pyev.serial = xie.serial |
| 459 | pyev.time = int(xie.time) |
| 460 | pyev.window = XDefaultRootWindow(self.display) |
| 461 | |
| 462 | if xi_type in (XI_KeyPress, XI_KeyRelease, |
| 463 | XI_ButtonPress, XI_ButtonRelease, |
| 464 | XI_Motion, |
| 465 | XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd): |
| 466 | device = <XIDeviceEvent*> cookie.data |
| 467 | #pyev.source = device.sourceid #always 0 |
| 468 | pyev.device = device.deviceid |
| 469 | pyev.detail = device.detail |
| 470 | pyev.flags = device.flags |
| 471 | pyev.window = int(device.child or device.event or device.root) |
| 472 | pyev.root_x = device.root_x |
| 473 | pyev.root_y = device.root_y |
| 474 | pyev.x = device.event_x |
| 475 | pyev.y = device.event_y |
| 476 | #mask = [] |
| 477 | #values = [] |
| 478 | #raw_values = [] |
| 479 | #if False: |
| 480 | # for i in range(device.valuators.mask_len): |
| 481 | # if device.valuators.mask[i//8] & (1 << (i & 0x7)): |
| 482 | # mask.append(i) |
| 483 | # values.append(j) |
| 484 | # raw_values.append(device.valuators.values[j]) |
| 485 | # j += 1 |
| 486 | #pyev.valuators = { |
| 487 | # "mask" : mask, |
| 488 | # "values" : values, |
| 489 | # } |
| 490 | elif xi_type == XI_HierarchyChanged: |
| 491 | pass |
| 492 | #hierarchy_e = <XIHierarchyEvent*> cookie.data |
| 493 | #pyev.flags = hierarchy_e.flags |
| 494 | #info = {} |
| 495 | #for i in range(hierarchy_e.num_info): |
| 496 | # |
| 497 | XFreeEventData(self.display, cookie) |
| 498 | self.events.append(pyev) |
| 499 | return pyev |
| 500 | |
| 501 | def get_devices(self, show_all=False, show_disabled=False): |
| 502 | global XI_USE |
| 503 | cdef int ndevices, i, j |
| 504 | cdef XIDeviceInfo *devices |
| 505 | cdef XIDeviceInfo *device |
| 506 | cdef XIAnyClassInfo *clazz |
| 507 | if show_all: |
| 508 | device_types = XIAllDevices |
| 509 | else: |
| 510 | device_types = XIAllMasterDevices |
| 511 | devices = XIQueryDevice(self.display, device_types, &ndevices) |
| 512 | dinfo = {} |
| 513 | for i in range(ndevices): |
| 514 | device = &devices[i] |
| 515 | if not device.enabled and not show_disabled: |
| 516 | continue |
| 517 | info = { |
| 518 | "name" : device.name, |
| 519 | "id" : device.deviceid, |
| 520 | "use" : XI_USE.get(device.use, "unknown use: %i" % device.use), |
| 521 | "attachment" : device.attachment, |
| 522 | "enabled" : device.enabled, |
| 523 | } |
| 524 | classes = {} |
| 525 | for j in range(device.num_classes): |
| 526 | clazz = device.classes[j] |
| 527 | classes[j] = self.get_class_info(clazz) |
| 528 | info["classes"] = classes |
| 529 | dinfo[i] = info |
| 530 | XIFreeDeviceInfo(devices) |
| 531 | return dinfo |
| 532 | |
| 533 | cdef get_class_info(self, XIAnyClassInfo *class_info): |
| 534 | cdef int i |
| 535 | cdef XIButtonClassInfo *button |
| 536 | cdef XIKeyClassInfo *key |
| 537 | cdef XIValuatorClassInfo *valuator |
| 538 | cdef XIScrollClassInfo *scroll |
| 539 | cdef XITouchClassInfo *touch |
| 540 | info = { |
| 541 | "type" : CLASS_INFO.get(class_info.type, "unknown type: %i" % class_info.type), |
| 542 | "sourceid" : class_info.sourceid, |
| 543 | } |
| 544 | if class_info.type==XIButtonClass: |
| 545 | button = <XIButtonClassInfo*> class_info |
| 546 | buttons = [] |
| 547 | for i in range(button.num_buttons): |
| 548 | if button.labels[i]>0: |
| 549 | buttons.append(self.XGetAtomName(button.labels[i])) |
| 550 | info["buttons"] = buttons |
| 551 | #XIButtonState state |
| 552 | elif class_info.type==XIKeyClass: |
| 553 | key = <XIKeyClassInfo*> class_info |
| 554 | keys = [] |
| 555 | for i in range(key.num_keycodes): |
| 556 | keys.append(key.keycodes[i]) |
| 557 | elif class_info.type==XIValuatorClass: |
| 558 | valuator = <XIValuatorClassInfo*> class_info |
| 559 | info.update({ |
| 560 | "number" : valuator.number, |
| 561 | "min" : valuator.min, |
| 562 | "max" : valuator.max, |
| 563 | "value" : valuator.value, |
| 564 | "resolution": valuator.resolution, |
| 565 | "mode" : valuator.mode, |
| 566 | }) |
| 567 | if valuator.label: |
| 568 | info["label"] = self.XGetAtomName(valuator.label) |
| 569 | elif class_info.type==XIScrollClass: |
| 570 | scroll = <XIScrollClassInfo*> class_info |
| 571 | info.update({ |
| 572 | "number" : scroll.number, |
| 573 | "scroll-type" : scroll.scroll_type, |
| 574 | "increment" : scroll.increment, |
| 575 | "flags" : scroll.flags, |
| 576 | }) |
| 577 | elif class_info.type==XITouchClass: |
| 578 | touch = <XITouchClassInfo*> class_info |
| 579 | info.update({ |
| 580 | "mode" : touch.mode, |
| 581 | "num-touches" : touch.num_touches, |
| 582 | }) |
| 583 | return info |
| 584 | |
| 585 | |
| 586 | def gdk_inject(self): |
| 587 | self.get_xi_opcode() |
| 588 | log.info("XInput Devices:") |
| 589 | from xpra.util import print_nested_dict |
| 590 | print_nested_dict(self.get_devices(), print_fn=log.info) |
| 591 | self.register_parser() |
| 592 | self.register_gdk_events() |
| 593 | #self.select_xi2_events() |