From d331d306f6bf67ff6309c91a987ea93a62112c82 Mon Sep 17 00:00:00 2001 From: Kalyax Date: Mon, 12 Sep 2022 17:51:24 +0200 Subject: [PATCH] pynput --- Xlib/ChangeLog | 123 + Xlib/X.py | 424 ++ Xlib/XK.py | 89 + Xlib/Xatom.py | 90 + Xlib/Xcursorfont.py | 99 + Xlib/Xutil.py | 81 + Xlib/__init__.py | 39 + Xlib/__pycache__/X.cpython-38.pyc | Bin 0 -> 7456 bytes Xlib/__pycache__/XK.cpython-38.pyc | Bin 0 -> 2067 bytes Xlib/__pycache__/Xatom.cpython-38.pyc | Bin 0 -> 1739 bytes Xlib/__pycache__/Xutil.cpython-38.pyc | Bin 0 -> 1403 bytes Xlib/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 358 bytes Xlib/__pycache__/display.cpython-38.pyc | Bin 0 -> 32379 bytes Xlib/__pycache__/error.cpython-38.pyc | Bin 0 -> 6616 bytes Xlib/__pycache__/threaded.cpython-38.pyc | Bin 0 -> 249 bytes Xlib/__pycache__/xauth.cpython-38.pyc | Bin 0 -> 2855 bytes Xlib/display.py | 951 +++ Xlib/error.py | 160 + Xlib/ext/__init__.py | 46 + Xlib/ext/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 667 bytes Xlib/ext/__pycache__/composite.cpython-38.pyc | Bin 0 -> 6388 bytes Xlib/ext/__pycache__/damage.cpython-38.pyc | Bin 0 -> 4208 bytes Xlib/ext/__pycache__/dpms.cpython-38.pyc | Bin 0 -> 4888 bytes Xlib/ext/__pycache__/ge.cpython-38.pyc | Bin 0 -> 2176 bytes Xlib/ext/__pycache__/randr.cpython-38.pyc | Bin 0 -> 26032 bytes Xlib/ext/__pycache__/record.cpython-38.pyc | Bin 0 -> 7141 bytes Xlib/ext/__pycache__/res.cpython-38.pyc | Bin 0 -> 7278 bytes .../__pycache__/screensaver.cpython-38.pyc | Bin 0 -> 4754 bytes Xlib/ext/__pycache__/security.cpython-38.pyc | Bin 0 -> 2928 bytes Xlib/ext/__pycache__/shape.cpython-38.pyc | Bin 0 -> 7699 bytes Xlib/ext/__pycache__/xfixes.cpython-38.pyc | Bin 0 -> 5522 bytes Xlib/ext/__pycache__/xinerama.cpython-38.pyc | Bin 0 -> 5447 bytes Xlib/ext/__pycache__/xinput.cpython-38.pyc | Bin 0 -> 17363 bytes Xlib/ext/__pycache__/xtest.cpython-38.pyc | Bin 0 -> 3046 bytes Xlib/ext/composite.py | 272 + Xlib/ext/damage.py | 182 + Xlib/ext/dpms.py | 233 + Xlib/ext/ge.py | 112 + Xlib/ext/nvcontrol.py | 5393 +++++++++++++++++ Xlib/ext/randr.py | 1197 ++++ Xlib/ext/record.py | 283 + Xlib/ext/res.py | 288 + Xlib/ext/screensaver.py | 198 + Xlib/ext/security.py | 139 + Xlib/ext/shape.py | 297 + Xlib/ext/xfixes.py | 201 + Xlib/ext/xinerama.py | 223 + Xlib/ext/xinput.py | 777 +++ Xlib/ext/xtest.py | 122 + Xlib/keysymdef/__init__.py | 42 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 395 bytes Xlib/keysymdef/__pycache__/apl.cpython-38.pyc | Bin 0 -> 587 bytes .../__pycache__/arabic.cpython-38.pyc | Bin 0 -> 1560 bytes .../__pycache__/cyrillic.cpython-38.pyc | Bin 0 -> 3045 bytes .../__pycache__/greek.cpython-38.pyc | Bin 0 -> 2269 bytes .../__pycache__/hebrew.cpython-38.pyc | Bin 0 -> 1206 bytes .../__pycache__/katakana.cpython-38.pyc | Bin 0 -> 1798 bytes .../__pycache__/korean.cpython-38.pyc | Bin 0 -> 3417 bytes .../__pycache__/latin1.cpython-38.pyc | Bin 0 -> 4331 bytes .../__pycache__/latin2.cpython-38.pyc | Bin 0 -> 1448 bytes .../__pycache__/latin3.cpython-38.pyc | Bin 0 -> 698 bytes .../__pycache__/latin4.cpython-38.pyc | Bin 0 -> 962 bytes .../__pycache__/miscellany.cpython-38.pyc | Bin 0 -> 3624 bytes .../__pycache__/publishing.cpython-38.pyc | Bin 0 -> 2458 bytes .../__pycache__/special.cpython-38.pyc | Bin 0 -> 732 bytes .../__pycache__/technical.cpython-38.pyc | Bin 0 -> 1547 bytes .../keysymdef/__pycache__/thai.cpython-38.pyc | Bin 0 -> 2483 bytes .../keysymdef/__pycache__/xf86.cpython-38.pyc | Bin 0 -> 5518 bytes .../__pycache__/xk3270.cpython-38.pyc | Bin 0 -> 973 bytes Xlib/keysymdef/__pycache__/xkb.cpython-38.pyc | Bin 0 -> 3364 bytes Xlib/keysymdef/apl.py | 19 + Xlib/keysymdef/arabic.py | 50 + Xlib/keysymdef/cyrillic.py | 107 + Xlib/keysymdef/greek.py | 74 + Xlib/keysymdef/hebrew.py | 40 + Xlib/keysymdef/katakana.py | 70 + Xlib/keysymdef/korean.py | 107 + Xlib/keysymdef/latin1.py | 195 + Xlib/keysymdef/latin2.py | 57 + Xlib/keysymdef/latin3.py | 22 + Xlib/keysymdef/latin4.py | 36 + Xlib/keysymdef/miscellany.py | 169 + Xlib/keysymdef/publishing.py | 83 + Xlib/keysymdef/special.py | 24 + Xlib/keysymdef/technical.py | 49 + Xlib/keysymdef/thai.py | 84 + Xlib/keysymdef/xf86.py | 202 + Xlib/keysymdef/xk3270.py | 30 + Xlib/keysymdef/xkb.py | 100 + Xlib/protocol/ChangeLog | 124 + Xlib/protocol/__init__.py | 28 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 219 bytes .../__pycache__/display.cpython-38.pyc | Bin 0 -> 16923 bytes .../protocol/__pycache__/event.cpython-38.pyc | Bin 0 -> 11730 bytes .../__pycache__/request.cpython-38.pyc | Bin 0 -> 44815 bytes Xlib/protocol/__pycache__/rq.cpython-38.pyc | Bin 0 -> 39187 bytes .../__pycache__/structs.cpython-38.pyc | Bin 0 -> 3151 bytes Xlib/protocol/display.py | 1075 ++++ Xlib/protocol/event.py | 434 ++ Xlib/protocol/request.py | 1900 ++++++ Xlib/protocol/rq.py | 1464 +++++ Xlib/protocol/structs.py | 161 + Xlib/rdb.py | 712 +++ Xlib/support/__init__.py | 26 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 183 bytes .../__pycache__/connect.cpython-38.pyc | Bin 0 -> 2203 bytes Xlib/support/__pycache__/lock.cpython-38.pyc | Bin 0 -> 747 bytes .../__pycache__/unix_connect.cpython-38.pyc | Bin 0 -> 3805 bytes Xlib/support/connect.py | 102 + Xlib/support/lock.py | 44 + Xlib/support/unix_connect.py | 206 + Xlib/support/vms_connect.py | 74 + Xlib/threaded.py | 28 + Xlib/xauth.py | 134 + Xlib/xobject/__init__.py | 29 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 239 bytes .../__pycache__/colormap.cpython-38.pyc | Bin 0 -> 4393 bytes .../xobject/__pycache__/cursor.cpython-38.pyc | Bin 0 -> 1036 bytes .../__pycache__/drawable.cpython-38.pyc | Bin 0 -> 25331 bytes .../__pycache__/fontable.cpython-38.pyc | Bin 0 -> 3116 bytes Xlib/xobject/__pycache__/icccm.cpython-38.pyc | Bin 0 -> 1184 bytes .../__pycache__/resource.cpython-38.pyc | Bin 0 -> 1514 bytes Xlib/xobject/colormap.py | 141 + Xlib/xobject/cursor.py | 47 + Xlib/xobject/drawable.py | 835 +++ Xlib/xobject/fontable.py | 110 + Xlib/xobject/icccm.py | 75 + Xlib/xobject/resource.py | 54 + .../__pycache__/personnage.cpython-38.pyc | Bin 0 -> 2921 bytes cli/__pycache__/colors.cpython-38.pyc | Bin 0 -> 1108 bytes cli/__pycache__/engine.cpython-38.pyc | Bin 0 -> 2775 bytes cli/__pycache__/writer.cpython-38.pyc | Bin 0 -> 2775 bytes cli/layer/__pycache__/menu.cpython-38.pyc | Bin 0 -> 896 bytes cli/layer/menu.py | 20 + cli/{cli.py => writer.py} | 36 +- listener.py | 16 + main.py | 25 +- pynput/__init__.py | 41 + pynput/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 866 bytes pynput/_info.py | 19 + pynput/_util/__init__.py | 465 ++ .../_util/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 15192 bytes pynput/_util/__pycache__/xorg.cpython-38.pyc | Bin 0 -> 12345 bytes .../__pycache__/xorg_keysyms.cpython-38.pyc | Bin 0 -> 54792 bytes pynput/_util/darwin.py | 277 + pynput/_util/darwin_vks.py | 79 + pynput/_util/uinput.py | 99 + pynput/_util/win32.py | 598 ++ pynput/_util/win32_vks.py | 179 + pynput/_util/xorg.py | 492 ++ pynput/_util/xorg_keysyms.py | 1715 ++++++ pynput/keyboard/__init__.py | 233 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 7011 bytes .../keyboard/__pycache__/_base.cpython-38.pyc | Bin 0 -> 19302 bytes .../keyboard/__pycache__/_xorg.cpython-38.pyc | Bin 0 -> 16997 bytes pynput/keyboard/_base.py | 739 +++ pynput/keyboard/_darwin.py | 345 ++ pynput/keyboard/_dummy.py | 23 + pynput/keyboard/_uinput.py | 445 ++ pynput/keyboard/_win32.py | 347 ++ pynput/keyboard/_xorg.py | 668 ++ pynput/mouse/__init__.py | 98 + .../mouse/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2132 bytes pynput/mouse/__pycache__/_base.cpython-38.pyc | Bin 0 -> 9076 bytes pynput/mouse/__pycache__/_xorg.cpython-38.pyc | Bin 0 -> 5279 bytes pynput/mouse/_base.py | 263 + pynput/mouse/_darwin.py | 212 + pynput/mouse/_dummy.py | 22 + pynput/mouse/_win32.py | 221 + pynput/mouse/_xorg.py | 181 + 170 files changed, 29220 insertions(+), 20 deletions(-) create mode 100644 Xlib/ChangeLog create mode 100644 Xlib/X.py create mode 100644 Xlib/XK.py create mode 100644 Xlib/Xatom.py create mode 100644 Xlib/Xcursorfont.py create mode 100644 Xlib/Xutil.py create mode 100644 Xlib/__init__.py create mode 100644 Xlib/__pycache__/X.cpython-38.pyc create mode 100644 Xlib/__pycache__/XK.cpython-38.pyc create mode 100644 Xlib/__pycache__/Xatom.cpython-38.pyc create mode 100644 Xlib/__pycache__/Xutil.cpython-38.pyc create mode 100644 Xlib/__pycache__/__init__.cpython-38.pyc create mode 100644 Xlib/__pycache__/display.cpython-38.pyc create mode 100644 Xlib/__pycache__/error.cpython-38.pyc create mode 100644 Xlib/__pycache__/threaded.cpython-38.pyc create mode 100644 Xlib/__pycache__/xauth.cpython-38.pyc create mode 100644 Xlib/display.py create mode 100644 Xlib/error.py create mode 100644 Xlib/ext/__init__.py create mode 100644 Xlib/ext/__pycache__/__init__.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/composite.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/damage.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/dpms.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/ge.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/randr.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/record.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/res.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/screensaver.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/security.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/shape.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/xfixes.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/xinerama.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/xinput.cpython-38.pyc create mode 100644 Xlib/ext/__pycache__/xtest.cpython-38.pyc create mode 100644 Xlib/ext/composite.py create mode 100644 Xlib/ext/damage.py create mode 100644 Xlib/ext/dpms.py create mode 100644 Xlib/ext/ge.py create mode 100644 Xlib/ext/nvcontrol.py create mode 100644 Xlib/ext/randr.py create mode 100644 Xlib/ext/record.py create mode 100644 Xlib/ext/res.py create mode 100644 Xlib/ext/screensaver.py create mode 100644 Xlib/ext/security.py create mode 100644 Xlib/ext/shape.py create mode 100644 Xlib/ext/xfixes.py create mode 100644 Xlib/ext/xinerama.py create mode 100644 Xlib/ext/xinput.py create mode 100644 Xlib/ext/xtest.py create mode 100644 Xlib/keysymdef/__init__.py create mode 100644 Xlib/keysymdef/__pycache__/__init__.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/apl.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/arabic.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/cyrillic.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/greek.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/hebrew.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/katakana.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/korean.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/latin1.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/latin2.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/latin3.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/latin4.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/miscellany.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/publishing.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/special.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/technical.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/thai.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/xf86.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/xk3270.cpython-38.pyc create mode 100644 Xlib/keysymdef/__pycache__/xkb.cpython-38.pyc create mode 100644 Xlib/keysymdef/apl.py create mode 100644 Xlib/keysymdef/arabic.py create mode 100644 Xlib/keysymdef/cyrillic.py create mode 100644 Xlib/keysymdef/greek.py create mode 100644 Xlib/keysymdef/hebrew.py create mode 100644 Xlib/keysymdef/katakana.py create mode 100644 Xlib/keysymdef/korean.py create mode 100644 Xlib/keysymdef/latin1.py create mode 100644 Xlib/keysymdef/latin2.py create mode 100644 Xlib/keysymdef/latin3.py create mode 100644 Xlib/keysymdef/latin4.py create mode 100644 Xlib/keysymdef/miscellany.py create mode 100644 Xlib/keysymdef/publishing.py create mode 100644 Xlib/keysymdef/special.py create mode 100644 Xlib/keysymdef/technical.py create mode 100644 Xlib/keysymdef/thai.py create mode 100644 Xlib/keysymdef/xf86.py create mode 100644 Xlib/keysymdef/xk3270.py create mode 100644 Xlib/keysymdef/xkb.py create mode 100644 Xlib/protocol/ChangeLog create mode 100644 Xlib/protocol/__init__.py create mode 100644 Xlib/protocol/__pycache__/__init__.cpython-38.pyc create mode 100644 Xlib/protocol/__pycache__/display.cpython-38.pyc create mode 100644 Xlib/protocol/__pycache__/event.cpython-38.pyc create mode 100644 Xlib/protocol/__pycache__/request.cpython-38.pyc create mode 100644 Xlib/protocol/__pycache__/rq.cpython-38.pyc create mode 100644 Xlib/protocol/__pycache__/structs.cpython-38.pyc create mode 100644 Xlib/protocol/display.py create mode 100644 Xlib/protocol/event.py create mode 100644 Xlib/protocol/request.py create mode 100644 Xlib/protocol/rq.py create mode 100644 Xlib/protocol/structs.py create mode 100644 Xlib/rdb.py create mode 100644 Xlib/support/__init__.py create mode 100644 Xlib/support/__pycache__/__init__.cpython-38.pyc create mode 100644 Xlib/support/__pycache__/connect.cpython-38.pyc create mode 100644 Xlib/support/__pycache__/lock.cpython-38.pyc create mode 100644 Xlib/support/__pycache__/unix_connect.cpython-38.pyc create mode 100644 Xlib/support/connect.py create mode 100644 Xlib/support/lock.py create mode 100644 Xlib/support/unix_connect.py create mode 100644 Xlib/support/vms_connect.py create mode 100644 Xlib/threaded.py create mode 100644 Xlib/xauth.py create mode 100644 Xlib/xobject/__init__.py create mode 100644 Xlib/xobject/__pycache__/__init__.cpython-38.pyc create mode 100644 Xlib/xobject/__pycache__/colormap.cpython-38.pyc create mode 100644 Xlib/xobject/__pycache__/cursor.cpython-38.pyc create mode 100644 Xlib/xobject/__pycache__/drawable.cpython-38.pyc create mode 100644 Xlib/xobject/__pycache__/fontable.cpython-38.pyc create mode 100644 Xlib/xobject/__pycache__/icccm.cpython-38.pyc create mode 100644 Xlib/xobject/__pycache__/resource.cpython-38.pyc create mode 100644 Xlib/xobject/colormap.py create mode 100644 Xlib/xobject/cursor.py create mode 100644 Xlib/xobject/drawable.py create mode 100644 Xlib/xobject/fontable.py create mode 100644 Xlib/xobject/icccm.py create mode 100644 Xlib/xobject/resource.py create mode 100644 charaters/__pycache__/personnage.cpython-38.pyc create mode 100644 cli/__pycache__/colors.cpython-38.pyc create mode 100644 cli/__pycache__/engine.cpython-38.pyc create mode 100644 cli/__pycache__/writer.cpython-38.pyc create mode 100644 cli/layer/__pycache__/menu.cpython-38.pyc create mode 100644 cli/layer/menu.py rename cli/{cli.py => writer.py} (77%) create mode 100644 listener.py create mode 100644 pynput/__init__.py create mode 100644 pynput/__pycache__/__init__.cpython-38.pyc create mode 100644 pynput/_info.py create mode 100644 pynput/_util/__init__.py create mode 100644 pynput/_util/__pycache__/__init__.cpython-38.pyc create mode 100644 pynput/_util/__pycache__/xorg.cpython-38.pyc create mode 100644 pynput/_util/__pycache__/xorg_keysyms.cpython-38.pyc create mode 100644 pynput/_util/darwin.py create mode 100644 pynput/_util/darwin_vks.py create mode 100644 pynput/_util/uinput.py create mode 100644 pynput/_util/win32.py create mode 100644 pynput/_util/win32_vks.py create mode 100644 pynput/_util/xorg.py create mode 100644 pynput/_util/xorg_keysyms.py create mode 100644 pynput/keyboard/__init__.py create mode 100644 pynput/keyboard/__pycache__/__init__.cpython-38.pyc create mode 100644 pynput/keyboard/__pycache__/_base.cpython-38.pyc create mode 100644 pynput/keyboard/__pycache__/_xorg.cpython-38.pyc create mode 100644 pynput/keyboard/_base.py create mode 100644 pynput/keyboard/_darwin.py create mode 100644 pynput/keyboard/_dummy.py create mode 100644 pynput/keyboard/_uinput.py create mode 100644 pynput/keyboard/_win32.py create mode 100644 pynput/keyboard/_xorg.py create mode 100644 pynput/mouse/__init__.py create mode 100644 pynput/mouse/__pycache__/__init__.cpython-38.pyc create mode 100644 pynput/mouse/__pycache__/_base.cpython-38.pyc create mode 100644 pynput/mouse/__pycache__/_xorg.cpython-38.pyc create mode 100644 pynput/mouse/_base.py create mode 100644 pynput/mouse/_darwin.py create mode 100644 pynput/mouse/_dummy.py create mode 100644 pynput/mouse/_win32.py create mode 100644 pynput/mouse/_xorg.py diff --git a/Xlib/ChangeLog b/Xlib/ChangeLog new file mode 100644 index 0000000..52bfb93 --- /dev/null +++ b/Xlib/ChangeLog @@ -0,0 +1,123 @@ +2007-06-10 Mike Grant + + * (many files): (mgg) Converted tabs to spaces throughout the + codebase, using reindent.py (SF id: 1559082) + +2007-03-18 Mike Grant + + * Xlib/display.py: (mgg) Added a get_atom alias that uses the + internal cache + * Xlib/xobject/drawable.py: (mgg) Added a raise_window() alias + to the Window class + +2007-02-15 Mike Grant + + * Xlib/xauth.py: (mgg) Python 2.5 didn't like the way the buffer + type was used, resulting in X authorisation failure, so + reverted to using slices + +2006-11-22 Mike Grant + + * Xlib/ext/record.py: Addition of RECORD extension by Alex Badea + , SF patch id #1538663 (demo + program in python-xlib/examples/record_demo.py) + +2006-09-20 Mike Meyer + + * Xlib/ext/xinerama.py: (mwm) Addition of Xinerama extension + +2006-07-22 Mike Grant + + Various typo fixes, general updates. + + Changelog hasn't been maintained since 2002, but some of the more + significant comments from cvs logs follow: + + * Xlib/display.py: (petli) Fix bug in refresh_keyboard_mapping: + ignore modifier and pointer remappings. Plays nice with pydoc. + Copied some text from the docs to __doc__ strings in + Xlib/display.py so that they appear when you use pydoc. + Completed documentation for Display objects. + * Xlib/XK.py: (calroc99) Minor doc string changes. Called + load_keysym_group() for miscellany and latin1 keysyms, rather + than importing the modules. + * Xlib/keysymdef/*: (calroc99) Small change to keysym loading. + Works the same way. + * Xlib/support/*, Xlib/xauth.py, Xlib/error.py: (petli) Added + ~/.Xauthority parsing by Python code instead of relying on + /usr/X11R6/bin/xauth. Not activated yet in all cases yet? + Activated in unix_support.py. + * Xlib/xobject/drawable.py: (petli) Fix bugs in definition and + method of GrabButton/Pointer + * Xlib/xobject/icccm.py: (petli) Add WithdrawnState to WMHints + * doc/*: (petli) documentation updates, typos and completing + documentation for Display objects + + +2002-03-30 Peter Liljenberg + + * support/unix_connect.py: Handle fcntl/FCNTL changes in Python + 2.2. + +2002-03-11 Peter Liljenberg + + * xobject/drawable.py (Drawable.fill_arc): This should be a + PolyFillArc. + +Fri Jan 19 17:49:45 2001 Peter Liljenberg + + * XK.py: Moved all keysyms into separate modules, based on their + category. By default only the miscellany and latin1 keysyms are + loaded, and other have to be loaded by importing the + Xlib.keysymdef. module, or calling + load_keysym_group('category'). + + * display.py (Display.lookup_string): + (Display.rebind_string): + + Functions to translate keysyms to strings, and binding keysyms to + new strings. + + +2001-01-16 + + * xobject/drawable.py (Window.send_event): + * display.py (Display.send_event): Changed the order of the + event_mask and propagate arguments. + +2001-01-10 + + * display.py (Display._update_keymap): The first half of the + update algorithm operated on an earlier type of code->sym map than + the second half. Stupid, stupid. It would have been nice with a + type-checker now. + +Tue Jan 9 13:03:19 2001 Peter Liljenberg + + * display.py (Display._update_keymap): Fixed call to append with + 1.5.2 semantics, broke in newer Pythons. + +2000-12-22 + + * display.py (Display.keycode_to_keysym): + (Display.keysym_to_keycode): + (Display.keysym_to_keycodes): + (Display.refresh_keyboard_mapping): + (Display._update_keymap): + Added keymap cache implementation. + +2000-12-21 + + * xobject/colormap.py (Colormap.alloc_named_color): Extended to + handle #000000 style color specifications. + + * xobject/drawable.py (Window.reparent): Renamed from + reparent_window. + + * display.py (Display.set_error_handler): Added. + +2000-12-20 + + * display.py (_BaseDisplay): + Implement a cache of atom names. + diff --git a/Xlib/X.py b/Xlib/X.py new file mode 100644 index 0000000..1a09e39 --- /dev/null +++ b/Xlib/X.py @@ -0,0 +1,424 @@ +# Xlib.X -- basic X constants +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +# Avoid overwriting None if doing "from Xlib.X import *" +NONE = 0 + +ParentRelative = 1 # background pixmap in CreateWindow + # and ChangeWindowAttributes + +CopyFromParent = 0 # border pixmap in CreateWindow + # and ChangeWindowAttributes + # special VisualID and special window + # class passed to CreateWindow + +PointerWindow = 0 # destination window in SendEvent +InputFocus = 1 # destination window in SendEvent +PointerRoot = 1 # focus window in SetInputFocus +AnyPropertyType = 0 # special Atom, passed to GetProperty +AnyKey = 0 # special Key Code, passed to GrabKey +AnyButton = 0 # special Button Code, passed to GrabButton +AllTemporary = 0 # special Resource ID passed to KillClient +CurrentTime = 0 # special Time +NoSymbol = 0 # special KeySym + + +#----------------------------------------------------------------------- +# Event masks: +# +NoEventMask = 0 +KeyPressMask = (1<<0) +KeyReleaseMask = (1<<1) +ButtonPressMask = (1<<2) +ButtonReleaseMask = (1<<3) +EnterWindowMask = (1<<4) +LeaveWindowMask = (1<<5) +PointerMotionMask = (1<<6) +PointerMotionHintMask = (1<<7) +Button1MotionMask = (1<<8) +Button2MotionMask = (1<<9) +Button3MotionMask = (1<<10) +Button4MotionMask = (1<<11) +Button5MotionMask = (1<<12) +ButtonMotionMask = (1<<13) +KeymapStateMask = (1<<14) +ExposureMask = (1<<15) +VisibilityChangeMask = (1<<16) +StructureNotifyMask = (1<<17) +ResizeRedirectMask = (1<<18) +SubstructureNotifyMask = (1<<19) +SubstructureRedirectMask = (1<<20) +FocusChangeMask = (1<<21) +PropertyChangeMask = (1<<22) +ColormapChangeMask = (1<<23) +OwnerGrabButtonMask = (1<<24) + +#----------------------------------------------------------------------- +# Event names: +# +# Used in "type" field in XEvent structures. Not to be confused with event +# masks above. They start from 2 because 0 and 1 are reserved in the +# protocol for errors and replies. +# +KeyPress = 2 +KeyRelease = 3 +ButtonPress = 4 +ButtonRelease = 5 +MotionNotify = 6 +EnterNotify = 7 +LeaveNotify = 8 +FocusIn = 9 +FocusOut = 10 +KeymapNotify = 11 +Expose = 12 +GraphicsExpose = 13 +NoExpose = 14 +VisibilityNotify = 15 +CreateNotify = 16 +DestroyNotify = 17 +UnmapNotify = 18 +MapNotify = 19 +MapRequest = 20 +ReparentNotify = 21 +ConfigureNotify = 22 +ConfigureRequest = 23 +GravityNotify = 24 +ResizeRequest = 25 +CirculateNotify = 26 +CirculateRequest = 27 +PropertyNotify = 28 +SelectionClear = 29 +SelectionRequest = 30 +SelectionNotify = 31 +ColormapNotify = 32 +ClientMessage = 33 +MappingNotify = 34 +LASTEvent = 35 # must be bigger than any event + + +#----------------------------------------------------------------------- +# Key masks: +# +# Used as modifiers to GrabButton and GrabKey, results of QueryPointer, +# state in various key-, mouse-, and button-related events. +# +ShiftMask = (1<<0) +LockMask = (1<<1) +ControlMask = (1<<2) +Mod1Mask = (1<<3) +Mod2Mask = (1<<4) +Mod3Mask = (1<<5) +Mod4Mask = (1<<6) +Mod5Mask = (1<<7) + + +#----------------------------------------------------------------------- +# Modifier names: +# +# Used to build a SetModifierMapping request or to read a +# GetModifierMapping request. These correspond to the masks defined above. +# +ShiftMapIndex = 0 +LockMapIndex = 1 +ControlMapIndex = 2 +Mod1MapIndex = 3 +Mod2MapIndex = 4 +Mod3MapIndex = 5 +Mod4MapIndex = 6 +Mod5MapIndex = 7 + +#----------------------------------------------------------------------- +# Button masks: +# +# Used in same manner as Key masks above. Not to be confused with button +# names below. Note that 0 is already defined above as "AnyButton". +# +Button1Mask = (1<<8) +Button2Mask = (1<<9) +Button3Mask = (1<<10) +Button4Mask = (1<<11) +Button5Mask = (1<<12) + +AnyModifier = (1<<15) # used in GrabButton, GrabKey + +#----------------------------------------------------------------------- +# Button names: +# +# Used as arguments to GrabButton and as detail in ButtonPress and +# ButtonRelease events. Not to be confused with button masks above. +# Note that 0 is already defined above as "AnyButton". +# +Button1 = 1 +Button2 = 2 +Button3 = 3 +Button4 = 4 +Button5 = 5 + + +#----------------------------------------------------------------------- +# XXX These still need documentation -- for now, read +# +NotifyNormal = 0 +NotifyGrab = 1 +NotifyUngrab = 2 +NotifyWhileGrabbed = 3 +NotifyHint = 1 +NotifyAncestor = 0 +NotifyVirtual = 1 +NotifyInferior = 2 +NotifyNonlinear = 3 +NotifyNonlinearVirtual = 4 +NotifyPointer = 5 +NotifyPointerRoot = 6 +NotifyDetailNone = 7 +VisibilityUnobscured = 0 +VisibilityPartiallyObscured = 1 +VisibilityFullyObscured = 2 +PlaceOnTop = 0 +PlaceOnBottom = 1 +FamilyInternet = 0 +FamilyDECnet = 1 +FamilyChaos = 2 +FamilyServerInterpreted = 5 +FamilyInternetV6 = 6 +PropertyNewValue = 0 +PropertyDelete = 1 +ColormapUninstalled = 0 +ColormapInstalled = 1 +GrabModeSync = 0 +GrabModeAsync = 1 +GrabSuccess = 0 +AlreadyGrabbed = 1 +GrabInvalidTime = 2 +GrabNotViewable = 3 +GrabFrozen = 4 +AsyncPointer = 0 +SyncPointer = 1 +ReplayPointer = 2 +AsyncKeyboard = 3 +SyncKeyboard = 4 +ReplayKeyboard = 5 +AsyncBoth = 6 +SyncBoth = 7 +RevertToNone = 0 +RevertToPointerRoot = PointerRoot +RevertToParent = 2 +Success = 0 +BadRequest = 1 +BadValue = 2 +BadWindow = 3 +BadPixmap = 4 +BadAtom = 5 +BadCursor = 6 +BadFont = 7 +BadMatch = 8 +BadDrawable = 9 +BadAccess = 10 +BadAlloc = 11 +BadColor = 12 +BadGC = 13 +BadIDChoice = 14 +BadName = 15 +BadLength = 16 +BadImplementation = 17 +FirstExtensionError = 128 +LastExtensionError = 255 +InputOutput = 1 +InputOnly = 2 +CWBackPixmap = (1<<0) +CWBackPixel = (1<<1) +CWBorderPixmap = (1<<2) +CWBorderPixel = (1<<3) +CWBitGravity = (1<<4) +CWWinGravity = (1<<5) +CWBackingStore = (1<<6) +CWBackingPlanes = (1<<7) +CWBackingPixel = (1<<8) +CWOverrideRedirect = (1<<9) +CWSaveUnder = (1<<10) +CWEventMask = (1<<11) +CWDontPropagate = (1<<12) +CWColormap = (1<<13) +CWCursor = (1<<14) +CWX = (1<<0) +CWY = (1<<1) +CWWidth = (1<<2) +CWHeight = (1<<3) +CWBorderWidth = (1<<4) +CWSibling = (1<<5) +CWStackMode = (1<<6) +ForgetGravity = 0 +NorthWestGravity = 1 +NorthGravity = 2 +NorthEastGravity = 3 +WestGravity = 4 +CenterGravity = 5 +EastGravity = 6 +SouthWestGravity = 7 +SouthGravity = 8 +SouthEastGravity = 9 +StaticGravity = 10 +UnmapGravity = 0 +NotUseful = 0 +WhenMapped = 1 +Always = 2 +IsUnmapped = 0 +IsUnviewable = 1 +IsViewable = 2 +SetModeInsert = 0 +SetModeDelete = 1 +DestroyAll = 0 +RetainPermanent = 1 +RetainTemporary = 2 +Above = 0 +Below = 1 +TopIf = 2 +BottomIf = 3 +Opposite = 4 +RaiseLowest = 0 +LowerHighest = 1 +PropModeReplace = 0 +PropModePrepend = 1 +PropModeAppend = 2 +GXclear = 0x0 +GXand = 0x1 +GXandReverse = 0x2 +GXcopy = 0x3 +GXandInverted = 0x4 +GXnoop = 0x5 +GXxor = 0x6 +GXor = 0x7 +GXnor = 0x8 +GXequiv = 0x9 +GXinvert = 0xa +GXorReverse = 0xb +GXcopyInverted = 0xc +GXorInverted = 0xd +GXnand = 0xe +GXset = 0xf +LineSolid = 0 +LineOnOffDash = 1 +LineDoubleDash = 2 +CapNotLast = 0 +CapButt = 1 +CapRound = 2 +CapProjecting = 3 +JoinMiter = 0 +JoinRound = 1 +JoinBevel = 2 +FillSolid = 0 +FillTiled = 1 +FillStippled = 2 +FillOpaqueStippled = 3 +EvenOddRule = 0 +WindingRule = 1 +ClipByChildren = 0 +IncludeInferiors = 1 +Unsorted = 0 +YSorted = 1 +YXSorted = 2 +YXBanded = 3 +CoordModeOrigin = 0 +CoordModePrevious = 1 +Complex = 0 +Nonconvex = 1 +Convex = 2 +ArcChord = 0 +ArcPieSlice = 1 +GCFunction = (1<<0) +GCPlaneMask = (1<<1) +GCForeground = (1<<2) +GCBackground = (1<<3) +GCLineWidth = (1<<4) +GCLineStyle = (1<<5) +GCCapStyle = (1<<6) +GCJoinStyle = (1<<7) +GCFillStyle = (1<<8) +GCFillRule = (1<<9) +GCTile = (1<<10) +GCStipple = (1<<11) +GCTileStipXOrigin = (1<<12) +GCTileStipYOrigin = (1<<13) +GCFont = (1<<14) +GCSubwindowMode = (1<<15) +GCGraphicsExposures = (1<<16) +GCClipXOrigin = (1<<17) +GCClipYOrigin = (1<<18) +GCClipMask = (1<<19) +GCDashOffset = (1<<20) +GCDashList = (1<<21) +GCArcMode = (1<<22) +GCLastBit = 22 +FontLeftToRight = 0 +FontRightToLeft = 1 +FontChange = 255 +XYBitmap = 0 +XYPixmap = 1 +ZPixmap = 2 +AllocNone = 0 +AllocAll = 1 +DoRed = (1<<0) +DoGreen = (1<<1) +DoBlue = (1<<2) +CursorShape = 0 +TileShape = 1 +StippleShape = 2 +AutoRepeatModeOff = 0 +AutoRepeatModeOn = 1 +AutoRepeatModeDefault = 2 +LedModeOff = 0 +LedModeOn = 1 +KBKeyClickPercent = (1<<0) +KBBellPercent = (1<<1) +KBBellPitch = (1<<2) +KBBellDuration = (1<<3) +KBLed = (1<<4) +KBLedMode = (1<<5) +KBKey = (1<<6) +KBAutoRepeatMode = (1<<7) +MappingSuccess = 0 +MappingBusy = 1 +MappingFailed = 2 +MappingModifier = 0 +MappingKeyboard = 1 +MappingPointer = 2 +DontPreferBlanking = 0 +PreferBlanking = 1 +DefaultBlanking = 2 +DisableScreenSaver = 0 +DisableScreenInterval = 0 +DontAllowExposures = 0 +AllowExposures = 1 +DefaultExposures = 2 +ScreenSaverReset = 0 +ScreenSaverActive = 1 +HostInsert = 0 +HostDelete = 1 +EnableAccess = 1 +DisableAccess = 0 +StaticGray = 0 +GrayScale = 1 +StaticColor = 2 +PseudoColor = 3 +TrueColor = 4 +DirectColor = 5 +LSBFirst = 0 +MSBFirst = 1 diff --git a/Xlib/XK.py b/Xlib/XK.py new file mode 100644 index 0000000..7603ccd --- /dev/null +++ b/Xlib/XK.py @@ -0,0 +1,89 @@ +# Xlib.XK -- X keysym defs +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA +# +# This module defines some functions for working with X keysyms as well +# as a modular keysym definition and loading mechanism. See the keysym +# definition modules in the Xlib/keysymdef directory. + +from Xlib.X import NoSymbol + +def string_to_keysym(keysym): + '''Return the (16 bit) numeric code of keysym. + + Given the name of a keysym as a string, return its numeric code. + Don't include the 'XK_' prefix, just use the base, i.e. 'Delete' + instead of 'XK_Delete'.''' + return globals().get('XK_' + keysym, NoSymbol) + +def load_keysym_group(group): + '''Load all the keysyms in group. + + Given a group name such as 'latin1' or 'katakana' load the keysyms + defined in module 'Xlib.keysymdef.group-name' into this XK module.''' + if '.' in group: + raise ValueError('invalid keysym group name: %s' % group) + + G = globals() #Get a reference to XK.__dict__ a.k.a. globals + + #Import just the keysyms module. + mod = __import__('Xlib.keysymdef.%s' % group, G, locals(), [group]) + + #Extract names of just the keysyms. + keysyms = [n for n in dir(mod) if n.startswith('XK_')] + + #Copy the named keysyms into XK.__dict__ + for keysym in keysyms: + ## k = mod.__dict__[keysym]; assert k == int(k) #probably too much. + G[keysym] = mod.__dict__[keysym] + + #And get rid of the keysym module. + del mod + +def _load_keysyms_into_XK(mod): + '''keysym definition modules need no longer call Xlib.XK._load_keysyms_into_XK(). + You should remove any calls to that function from your keysym modules.''' + pass + +# Always import miscellany and latin1 keysyms +load_keysym_group('miscellany') +load_keysym_group('latin1') + + +def keysym_to_string(keysym): + '''Translate a keysym (16 bit number) into a python string. + + This will pass 0 to 0xff as well as XK_BackSpace, XK_Tab, XK_Clear, + XK_Return, XK_Pause, XK_Scroll_Lock, XK_Escape, XK_Delete. For other + values it returns None.''' + + # ISO latin 1, LSB is the code + if keysym & 0xff00 == 0: + return chr(keysym & 0xff) + + if keysym in [XK_BackSpace, XK_Tab, XK_Clear, XK_Return, + XK_Pause, XK_Scroll_Lock, XK_Escape, XK_Delete]: + return chr(keysym & 0xff) + + # We should be able to do these things quite automatically + # for latin2, latin3, etc, in Python 2.0 using the Unicode, + # but that will have to wait. + + return None diff --git a/Xlib/Xatom.py b/Xlib/Xatom.py new file mode 100644 index 0000000..b013777 --- /dev/null +++ b/Xlib/Xatom.py @@ -0,0 +1,90 @@ +# Xlib.Xatom -- Standard X atoms +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +PRIMARY = 1 +SECONDARY = 2 +ARC = 3 +ATOM = 4 +BITMAP = 5 +CARDINAL = 6 +COLORMAP = 7 +CURSOR = 8 +CUT_BUFFER0 = 9 +CUT_BUFFER1 = 10 +CUT_BUFFER2 = 11 +CUT_BUFFER3 = 12 +CUT_BUFFER4 = 13 +CUT_BUFFER5 = 14 +CUT_BUFFER6 = 15 +CUT_BUFFER7 = 16 +DRAWABLE = 17 +FONT = 18 +INTEGER = 19 +PIXMAP = 20 +POINT = 21 +RECTANGLE = 22 +RESOURCE_MANAGER = 23 +RGB_COLOR_MAP = 24 +RGB_BEST_MAP = 25 +RGB_BLUE_MAP = 26 +RGB_DEFAULT_MAP = 27 +RGB_GRAY_MAP = 28 +RGB_GREEN_MAP = 29 +RGB_RED_MAP = 30 +STRING = 31 +VISUALID = 32 +WINDOW = 33 +WM_COMMAND = 34 +WM_HINTS = 35 +WM_CLIENT_MACHINE = 36 +WM_ICON_NAME = 37 +WM_ICON_SIZE = 38 +WM_NAME = 39 +WM_NORMAL_HINTS = 40 +WM_SIZE_HINTS = 41 +WM_ZOOM_HINTS = 42 +MIN_SPACE = 43 +NORM_SPACE = 44 +MAX_SPACE = 45 +END_SPACE = 46 +SUPERSCRIPT_X = 47 +SUPERSCRIPT_Y = 48 +SUBSCRIPT_X = 49 +SUBSCRIPT_Y = 50 +UNDERLINE_POSITION = 51 +UNDERLINE_THICKNESS = 52 +STRIKEOUT_ASCENT = 53 +STRIKEOUT_DESCENT = 54 +ITALIC_ANGLE = 55 +X_HEIGHT = 56 +QUAD_WIDTH = 57 +WEIGHT = 58 +POINT_SIZE = 59 +RESOLUTION = 60 +COPYRIGHT = 61 +NOTICE = 62 +FONT_NAME = 63 +FAMILY_NAME = 64 +FULL_NAME = 65 +CAP_HEIGHT = 66 +WM_CLASS = 67 +WM_TRANSIENT_FOR = 68 +LAST_PREDEFINED = 68 diff --git a/Xlib/Xcursorfont.py b/Xlib/Xcursorfont.py new file mode 100644 index 0000000..1991943 --- /dev/null +++ b/Xlib/Xcursorfont.py @@ -0,0 +1,99 @@ +# Xlib.Xcursorfont -- standard cursors +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +num_glyphs = 154 +X_cursor = 0 +arrow = 2 +based_arrow_down = 4 +based_arrow_up = 6 +boat = 8 +bogosity = 10 +bottom_left_corner = 12 +bottom_right_corner = 14 +bottom_side = 16 +bottom_tee = 18 +box_spiral = 20 +center_ptr = 22 +circle = 24 +clock = 26 +coffee_mug = 28 +cross = 30 +cross_reverse = 32 +crosshair = 34 +diamond_cross = 36 +dot = 38 +dotbox = 40 +double_arrow = 42 +draft_large = 44 +draft_small = 46 +draped_box = 48 +exchange = 50 +fleur = 52 +gobbler = 54 +gumby = 56 +hand1 = 58 +hand2 = 60 +heart = 62 +icon = 64 +iron_cross = 66 +left_ptr = 68 +left_side = 70 +left_tee = 72 +leftbutton = 74 +ll_angle = 76 +lr_angle = 78 +man = 80 +middlebutton = 82 +mouse = 84 +pencil = 86 +pirate = 88 +plus = 90 +question_arrow = 92 +right_ptr = 94 +right_side = 96 +right_tee = 98 +rightbutton = 100 +rtl_logo = 102 +sailboat = 104 +sb_down_arrow = 106 +sb_h_double_arrow = 108 +sb_left_arrow = 110 +sb_right_arrow = 112 +sb_up_arrow = 114 +sb_v_double_arrow = 116 +shuttle = 118 +sizing = 120 +spider = 122 +spraycan = 124 +star = 126 +target = 128 +tcross = 130 +top_left_arrow = 132 +top_left_corner = 134 +top_right_corner = 136 +top_side = 138 +top_tee = 140 +trek = 142 +ul_angle = 144 +umbrella = 146 +ur_angle = 148 +watch = 150 +xterm = 152 diff --git a/Xlib/Xutil.py b/Xlib/Xutil.py new file mode 100644 index 0000000..768c5e2 --- /dev/null +++ b/Xlib/Xutil.py @@ -0,0 +1,81 @@ +# Xlib.Xutil -- ICCCM definitions and similar stuff +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + + +NoValue = 0x0000 +XValue = 0x0001 +YValue = 0x0002 +WidthValue = 0x0004 +HeightValue = 0x0008 +AllValues = 0x000F +XNegative = 0x0010 +YNegative = 0x0020 +USPosition = (1 << 0) +USSize = (1 << 1) +PPosition = (1 << 2) +PSize = (1 << 3) +PMinSize = (1 << 4) +PMaxSize = (1 << 5) +PResizeInc = (1 << 6) +PAspect = (1 << 7) +PBaseSize = (1 << 8) +PWinGravity = (1 << 9) +PAllHints = (PPosition|PSize|PMinSize|PMaxSize|PResizeInc|PAspect) +InputHint = (1 << 0) +StateHint = (1 << 1) +IconPixmapHint = (1 << 2) +IconWindowHint = (1 << 3) +IconPositionHint = (1 << 4) +IconMaskHint = (1 << 5) +WindowGroupHint = (1 << 6) +MessageHint = (1 << 7) +UrgencyHint = (1 << 8) +AllHints = (InputHint|StateHint|IconPixmapHint|IconWindowHint| + IconPositionHint|IconMaskHint|WindowGroupHint|MessageHint| + UrgencyHint) +WithdrawnState = 0 +NormalState = 1 +IconicState = 3 +DontCareState = 0 +ZoomState = 2 +InactiveState = 4 +RectangleOut = 0 +RectangleIn = 1 +RectanglePart = 2 +VisualNoMask = 0x0 +VisualIDMask = 0x1 +VisualScreenMask = 0x2 +VisualDepthMask = 0x4 +VisualClassMask = 0x8 +VisualRedMaskMask = 0x10 +VisualGreenMaskMask = 0x20 +VisualBlueMaskMask = 0x40 +VisualColormapSizeMask = 0x80 +VisualBitsPerRGBMask = 0x100 +VisualAllMask = 0x1FF +ReleaseByFreeingColormap = 1 +BitmapSuccess = 0 +BitmapOpenFailed = 1 +BitmapFileInvalid = 2 +BitmapNoMemory = 3 +XCSUCCESS = 0 +XCNOMEM = 1 +XCNOENT = 2 diff --git a/Xlib/__init__.py b/Xlib/__init__.py new file mode 100644 index 0000000..8947373 --- /dev/null +++ b/Xlib/__init__.py @@ -0,0 +1,39 @@ +# Xlib.__init__ -- glue for Xlib package +# +# Copyright (C) 2000-2002 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +__version__ = (0, 31) + +__version_extra__ = '' + +__version_string__ = '.'.join(map(str, __version__)) + __version_extra__ + +__all__ = [ + 'X', + 'XK', + 'Xatom', + 'Xcursorfont', + 'Xutil', + 'display', + 'error', + 'rdb', + # Explicitly exclude threaded, so that it isn't imported by + # from Xlib import * + ] diff --git a/Xlib/__pycache__/X.cpython-38.pyc b/Xlib/__pycache__/X.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e06ad0127a3a3d05f9b01ee788db1f96841efae GIT binary patch literal 7456 zcmd^DXLuaPkzJ4on|A>)iIgacN>qfH6l_^0b{7C4A_)r+U^(ap+YMmE!OVJQ7QpI~ z94?p)|MI{44)~_J>UDKZb#-@j&Ccg? z=>+~h@W|oX{$q*64_N5@>w<6wPjIj|kx&Urs4nV4y9>{5@ZETN!1vG+R38nhB<)ct+N;vEPi1Jo%F+RqqsvsD4ypnjQboF4 z_0ttD_-BWf>QrS{QLwV$q52k07g8C?te*QtYaJ>GFl9iki5<#eOEf^Ne5 zZdQls7Ih`vs*cc*x{7X7N9nk_nog)|=p=kOrLLvZ>N>hzT~BwYV|1swf$mZ_(qq(3 z^jLK>-K}n+$EjQC9yLUdSGUm_b)1IP2`Z_RRK`3i>J*Kr(=@7Xr!ma+EPNS+^HUgOVayV2)qpGD8u&8!6=17qZD0=oy9iwex@)lS!oG+8I{Kf2ejj`QK1AC< zyAEsvx|`5#0(%hHPow=Av_FgX=g|H<+K13S6}nG@{innJGl2a9uwMlBOTc~^*fU}0 zS+MhLyz@Eef3EJK=b``k;9mgU7lMBgbYBeJmq7QW(0v(nUyhi)0_*iktk}CHneYty?4OI!_a#t;{Gng{oT-g z590b>#PxlM>-!Pc44-%q3eGwA;; z{QVqsJ`aC?1^)gj{QWiP|2q8r4fy+;(ETkuUx5A>q5s?9e+T^Ug8x16UjqMSv|mB{ zRbaml{t@th0RC&R{dL&>2C#1e`xe?3+Ha%pJK(-mW zlY*p#rUlQa-N3V|8#JeSK=Y~>w4nMxiz*4)uTr1`Dh)cQGN5}@7Id%5f$mdz(EX|a zdO#IHFH`-X2h{-RAvFkkx!MDIh1v^xSnUJ7QtbylqHYAeN_;yiUR|y30CJ5W*9vl- z_<6k`#{{`S=#7HkB>vwly|)N{tKdU|+$P9zK~4yAQjk+ZPYZs#ShzzXaHmAzE{VWn zBm$3>2;41WA19;kkx`Eq(Xw}OFc|* zS0yg>F-bAuEACQ;NtQ{DNuEi8Nk5YTCWB1&FxkswACvt|4lucl$w4NEm|V`}3MPk{ zT*>4rCP$fE&E!WQQ^zh~uC4B=>8TOQ&se^8!&&WEp}nptU-s(FQQuoLIOS$M+YPn9 zV7toOp!B#~Z-k>>tr1Wr>YDYukP5?YbH?}T+7Fv^&AO&u0Oxd*QfNz!F!Wr?4m-}A zUaNb)M6W&)I9bm7Mab&9x=Zq0E#wvJQCC3NBI>7G``M1R~`j!BK4mTI7PU zyQR=?fNeS>Ti%;N6WUtWW;z1&N7I<}Lfdl%+Pe)pivSo+WBz!DX7t7rkA_Y@8anl8 z=yV5EH0C>y0w%m>)vKWuni*$DHtSx{@Qt+RydBs}wqu9Q@~Y*o7<8~2`i)u$r76s4 zxhbv>%<8~?P|s>*`?_YPv#;7%3Vw|4flhqeh=MG9$GZmNCGW(_p5ys&y8|1X-f*=) z=37hA9I?2qWW2bo^wz>s#%Rc*QqBm`f>PGR#>|T{vb3=v%NhxN;^(+aNnxfNq3k61 z6e;vd9I@NsQhn8~1(A$x7;(j|*hXWrWnW_*A}LqVh@IDr#LT?AWj!_7eo-3itbR%Z z%9NkgbvbO&m_pfem+h7ILQ$~|#6xoMZyn1UzcgM&W3W)R{aOPDH+HJn24VyGc!44d z`6|v&jW>E3=g6mGTaHH*+DJSkUeDNxvSai4#HqAabSsA$)@^qs>P<}ySLfsaw^G$r zd)aKYuF{*7HFhJ318Qu? z-Nt^0gm-jEcu$9fcXdd3UnE64(l{Ck#@9$N?nZ*~I1-H08KgjXVlUg;r#`c#kH^f3 zm^m3Ur()(bWldD4_&hn3HWDABxM$v7VQIkhEUem&=I$k}Vr|Y|lsEmuZVemJ^C@TK z^R^#0U^wa?cbBzqqq|_br##oOUEU`9b^!6f$WD|lso$t|-U~$oRdi_C4h(AQ$XfHR zw-nTH02CeCI&ZjnLfdkj=5*Y-f2(t}(bEG2z07kBilv&Cc2`S18XulDv+F#ec45<4$gj2=sHqPHp#rAp98|N*jffN!W6`ZV4 z(_nmD=3UzjLd*k(`eSgs4P|+quq$-6>DF3&&_x;!AX0|KYNLkb3MfD9AoHkZys!mk z#@%(xu@zsQBmm-k-qst|lA|fj8o0zC)O_i(#dw7>{I*!gAt5aMOr6el~mz~v!U^wT=LJ!7L=YD`4qY~8@ebFpi>5`?Q9MnpJm*}=q z;LKeu?2Ns+X4PALK!>r&f}xBEJYTR;oPQZSX@#}bRt9Pn-;$V!>aYYxQ$RkQOXFe)1+*9p=8xAn#ob7Eoq!g2%fLBA9dzBWOVaLJH%@9 zCBYJ66-zu6R*{;kIP%yfZ5ao})!0{CfV~N01LcKjtc`E0cAdb^R8cO?Bis5cY?Wp( zS3$UW_ggEtf+$^Hh<7<9!OODH?()JSlMB?x-YMQM<%P4_URe#PJsZ=J!prO>q?{Fb zZwT-CfKYDK^H+3xnn)>rxVnH$7Ecvv5=bnW{t?7KMlzksxiVHxiV+D~rc<$4^%^@& z3lbYL{hcFnT%6gpSRpHQKrE$jDCYyc+(7PLSk)*l>vd#<-eG6MY6d8#15sxz%lvx# z0FMXl!KR`0P~`WNw1Z;4 z2WfMBnUW@BGB;gEd1+%YGqaW*=m~Fw3m4q78`?jMC1a_;OU@2SMyO$9#QA=pnIpSY|h1EWlNen!p`b^^i;{$LzE_y}Vqp zf>p}1Sn(Q2sUoM#QrGdx#%{Lie3N50qr&qVu$KdeP&~l(6}E8dUVJ%CBAC8mjgp`x zo;F^UFQnyy z$AIl9d|FX)+^snc-a6)D4=6eBB3W{L`z}-slf1CF#gz~kbfS_%*5M_b_U#qhw;#tz71ni0wsUYztA~ z-gx4?wXoxOE24*`2^$*>E+TaD4zRrEkm(6uU~V{}mvP6=N(vP~*g21JO0$Of^rGbA z1q{NC$y{`U^ijm5rtke-T%FFxw4cw@fC4(hFVC9ZP#V;vi(x@zImKE*LK zY&Hs`k@|-lA;#D7og{l_d6|kkAeZ)TM=E;RYB=zFLYobTL8+L>#yeL+&4GB~e$;-A zvs&(43Hi&3#Y`mHxbi7)gi6CVcXQ9V5^PaQ8VM#47^LF4(sn;6&)J!8%c$(iL~^MS zAUQ?CD5_XKACU-)Qq{L&pj{uu*5cx7z+9FZ2eE|fm#<~cliTD%6pOaPK*bKYpsUt+ zG58|(Y40{ns#R2iB=&h3H{zcNq;T9O7bE*y8pRIF__o9r?C(H^aZ+(xq|bUm7~MX? zM^{gF#AWA9QAN4PuSiU{??wCsgcH)N)+|m}29`nrKOhCVp*$m`=KO|^@QVCMVT9yF zwIl@=B_|{9;e=no?^r^9c=OPzx2A_+d=t6L*FzQjIK{cL9J+$c9OGAsd z)|;*5-D{pg-n@r@L<`=JpZmIZb?@%(>F(O`-@8NJ+5h9@)K4&Gr=6WPcFM{2J9c$% U_5CE??tJtAQ@OY6|JMKi0?Y~!D*ylh literal 0 HcmV?d00001 diff --git a/Xlib/__pycache__/XK.cpython-38.pyc b/Xlib/__pycache__/XK.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4466e1932ad8b7a0391cc787bba055e1aecd28a7 GIT binary patch literal 2067 zcmZuyOK%%D5GHq}^;(q+$4K)i`rx64BEVTYJ@ilrhS50A!GTd!Y2gScw$zf6)>hfWgT)>+Wjcl!&d>;VFh_c%S}PCut3yG<7ggepRb(lX8a zTSPj;sa99)Mb^l^$QzoZd6E@yg}JA*#Sn^(azC}(7rAeXOR+lQiMbd~zq_NL4 zu6e^6Npqz+WyV8uda)IiJ}jDkxzf)>mu9N;`&>(d{VQAs?k%s73_D-<<+^)ZtVQhXd|<#ercoeG?5{+i3Ifw? z1ipib!G0LXn;HT>L$}Zz|9~}~qYJEo07f#y+MlBt>f?FkVnGL8nR#=(584<0%-=`f zZd`!mq&lnI1^rp2g`OXL@38>lxibCri2y`smRV4plL~+)eJP6Zl@L-VBQ}hxNCqZ; zjg0Cvf6yR8lE#o~I;1&mkjz*$VOm(U_%n!A&Y7`360FEf5CN;m>A-GeFTHOrXn>X$ zU?NrI_;8^gl{lJynC7Q6OW7rf>>2CS=VVh&Z(K9Esjgsbus0aDp{e(w>jQd#5T4-| zkYX>ftjzGw-t!8egQX7&kT5l+snqH$)q|#2deKgabIS+Ut%6orYh|g@i5QJ{KQ*=k z=mO-S8k%kzG!_Ji9j>iRBtW&@=)Y4(&7iD(L$iWEl2S-%avcO!ffQ9RB-gwKGPmH(g(4lIG>t5jB@;Cg@TleK% zkX-@KlnB!-L5+V5+oJG`yOG!cr#cmv0ftoO5b#_8A@V+#1Rk9c8zD%Lc*z$Pn|Q~^ zhxeK;K4YSqAq*3@qK>Ld8(PqOu2*8Q`)9DLeG!9c@c-m004KD zW|6Q@P1>BLfc2OvMRp8gJLkQg$=EY^iW#BkeL<69drT9kmauwCyLNn-aVocL#)8z< zd%N)^g$iqD?F1@a7C#Zm(5@e;gpS>otEv$>fPx^P0!V8JGPdAQSM;LHD{>@q9;N6F zgvC(mHB8V^bG7u7f$Y?mo$Cb7xlRb9b*{7euVYqd9qYUc!X?M7l5@;XonzAE<#i&g zJN}w7?O;3rC3O=z6xJapYS@EJhTogOs|WI9keh4`9Fgr`g{|f`V=QT`&eApJ&qLZe Z5w;vRcMZ1`{OhB-7lz@TFbHwz{{u+}EJ^?X literal 0 HcmV?d00001 diff --git a/Xlib/__pycache__/Xatom.cpython-38.pyc b/Xlib/__pycache__/Xatom.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4367d0eba9e87838ae36e4766641b10d920d9b61 GIT binary patch literal 1739 zcmd7S$#UvO6b4{yV`dDV=XoCSJWq|(VAU0pu7&{rhH~zqDoNSbDf_xAd4s%27Fp(5 zvSXW-Um>gXkx4FjgNWj*{!cfwx?2!rv#E%>e*a_U)35PJisgNS`MK8q=qd4M}K!TD;QVJ8B@ z;*`#CM&~%E3tZ48F6jzabd77e!42KwmIMS9Q6vqTdh4Wnk#+gT`}Ee$^e|DTmx(cI zXCrS3Cdu%YzonTBlV$pt9Fu4InE_^y8DfT+5oVMbW5$^YW|Em=rkNRLmYHMbnFVH% zSz?x%6=s!LW7e4sW|P@swwWEK!0a-6%sz9#95P4DF>}J4GH1*=bHQ9PSIjkY!`w20 zDKZ+PE39>ixZ@fX;ntD#q-I+>H+zJuwW7keD~J^hzaktYG~w!oB}~<_P21&KObcAk zc9GTszflZICF$;VhP}?P-x&@%!(nGQ>I}!7;RFfY6;)9*C88zU@)0*IUzVkdm}AuV zhI$=am5_9$<_oK=2IgJq*@3IcMnzbH$7bDfu@Npol^kR^7NzHRBs1XQ4szV5%aRDp zj_$H6>S1^IO=VX~D~xF#?Mj_PE9UvGVU>~i#_$4R8hR^MH7wn(B2}%Z=_~4X^j1Q( z@6;W8t$wbWhO~GhP1R&8qe_N4k%lEI9mz8wTXEGFmUGD@*cqvw%wiv z$%4UV@(<3}htsVVeE4q9yWei{KpVoLk&qS?pCynx* zkJQIN=#8qO`*(;{+qLlg!}TM@CvOJf>`Bdb>TdY4m}UEhT7HtBpl}b7E{Td^*55<2 z6qx3FGo=ZqJzIEEnkhW5l~+6JyTbCq!z!uAKd0(`!%;^sOKQ>jZ;|JZ>cc$<0@v_zOA2c{4-NKDf$Yb)(cDr^U~3)_QT zgI$N+K%bj9=R4?ii`B`yh;O6kd+7N-;t#-o2z&?eUBn+D{usG^%=-lMKE=GxFz<8B zyN7vg7LfbUeF6QKxPJxx*Xa8V@VCI<0e{aLk+Jx*2b8u)#lWpv`Y`=fNiWqe_!mDN!ir$SFzN}o!55aJJ1 zI-|)FnEoPmAUIG`cWKHUtP-VW%S=zw*lg9o`VecNa+2_qToZqVdXfYy5YkkW`eTvh zG#M!K440^PU{TBEqB!N;J|k&5=77)j)Jm2_H=$W(iA|>%a%P~Vwj6bQ*~OAq9NEEl zv=Z((uB#HWfVp{pR=d5_3Y~>K9UgZq8#oqgv~X|86OOmuxj4cgAt#q@NYD}5w9Dfd zFN3rkJehNOL`A|G*>ubiKu@00L@?5D6xPCLD#e#G?na|-_b`e`dE6bG^bh+kI2`=^ z%u56OKdk%retW8ByiF5+P9;lu`#{C{jN_AQ|HkuQQkC%bI1#_L$GH~Cz4^uS%8xU} l@`V4iizeA$aHYcAe|vAxGz!I+#n-J^{I9h3_FivN{15DhjPd{g literal 0 HcmV?d00001 diff --git a/Xlib/__pycache__/__init__.cpython-38.pyc b/Xlib/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69b10b1870c2df6f98787bcc1ed44d3f4861c26d GIT binary patch literal 358 zcmY+8u};G<5Qc3hA)%#6AOsuCU77(#gc#V6SP)X=<>dBEapl;R?Vxt%Masy?v#@z( z>Kh;?T$aLrx=;E~|DEn(FzAt__m9)bDktP~WB#g2<~p^%NI=2~QcjVrn9O7@$qRd> zlAaJ=V1_vsxPv8DxQo4M#dolOPlkI#wobcbeU=bOq@zfi$eLSTG$PoR_nS%QymgP( zgf#1d(y>J4=UOadA)T|1XAZ}A!!q^(o~8538UTKWw zvdTcGueT&TGowL;~GF26#z!_ ZWvnh+i=CEN$LWsWAZIx{${77uUq2Wu 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 RsyLOyiTb(k70WyJ{y%x+I2Zr` literal 0 HcmV?d00001 diff --git a/Xlib/__pycache__/error.cpython-38.pyc b/Xlib/__pycache__/error.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1729114a1a4f8603a236945c664cffa0e251eb5 GIT binary patch literal 6616 zcmb_g&2!tv6~`ApMM;z_$@;QP%O5dQ$Fd#Aaoi-8O*w9+37d(ht%s8og|KUzDoMx- zXlyHXFG(+*>GYbJ&UEafj{RGD!?h=$de5o9-+~k&iDJTO!Py7=faUwWx9?&1VSh6| zUNmt0^It3e-=+=Y-vsGn6<{4Lyk!}NFofAM+GfKvd2O|^ly zjSO%`WP!6X1DplUi9B##;~a276oHGne;#;Dlz>Yb7l6mb1n`8$Mc_$M1}Cpp~|7to}mgj{cvUNanP1)UQ0gnIzq{{jlkb& z%T5%oZOI*11uePupw--5lO(>p{XEXQZnM*jTz7?p+7fejHf`SJUJsum&^vg2z)k@> zw~RU1S+(lB(_IY}O;lMuAFiq_{<3GPJWkr$M9{}L=el8}T=xt>&xn~rM(7Upy$nt6 zcx~yranW_#f!JyBy5zb~cDz>dM2(ST{2&uYNRduC5U0>WUgXWZt?wKk(HV3*8c}%a z#O1D)by$={bA(Zvz2k|BNa9(c<^6$DC2$@ZChgF9+TD%Q{9$s$DjBA#qb8}KOff}q z>|kKlG|r)ErFl~kA3qd7A-U(eR@H(|NBU7S=p+MULviom^?v%$$Ry2QSV(rfIQI>e z!OcBOXLoG3!$(y+$sDG;nx)Y3><5)0ucGOo8fMnqJw4cv;}e=z)u@6NB?*nBHXmBO zv_4bwI&Vc4Kk=**1^dxshTp234;6Fe*xVm8|kR;?9;QY0bMrgoL`noSST!I>!umKi%wG5u7nBvA9{n=)U& zc#P3iwEZy{NPB#M4)7nGaeBe-1qW$Y@wF?J&F2ivN!oMVTXj8U)T!f)`mwmoLCF;= z+MiZ<=SW;QL9DYIjp?mJ;x{)||JQp+e=P54YBV0_Tb$0p+qBoAx ze&wKLg!$UkXG@>6!q(@UaJo3teV*0lyvV&aU*{SHkrxFl*hNtkV>qL%LtMt=>;r9b z>_pGEW$gHYknvgL({J-*U~K$)1k=<;;;5soEWW_7N^FlU02MZs@cdX@2P0|QPsv0`miZmB5ahl zJrzoKv)S>~^El%QFY?lpXYBA!D#ZJ5<}b<4G&0QH$;9EouOiLmGOo?Ng^NeVOBlLg ze7*by{ffpLTh3*EOAE_NbiZeA&iJNhj zr>i#_`uy&-*#5#3ifi?clp-ucK1K_t=HO-LrC6((%+{@ zh({tRfaG`0jtHI(VYvaVZi3j84? zoO>UoNQg%!sRgZ}3HA#peVig89*HCaPwv%*xUmk6%@hOiNDPepryI4$LDL@=`P+Hm}r?;pd-9h5cWFwR8NH(&`PGln|#;|yo;$n?+gkHE0?AK5? z+db;%XGw2gDf!@Lb-Dl9jcRgCa)ZcawHwA64dR?i7SZkpUYymp!^2e-OOrp7h5J4H z4#$`HiN00+iqhuu?&09f#wdiJK(GK-*C*t!+wed9?< zpps1A=iPLv{017aTcfBsE;hqvCyYE)?ux^qSoh)~tHAz^Mn=;6L*8-o$@f|<*Zm86 z>!OV=7V)E_dCRc|IGp_BKs-3rvuTGpQgpSu?NK!oGX{LD#)U^Ra`nJ}PXicIRO>UT z@!jt*3Ll4H(8qB+Ms?l5|2@SxcZjju)%@WSl!>TVAFIzP>J-1G7*uoK#e?tA^qwm!@JxOpZ=?F={d)Cm zY$$pX=eJc51%A*{%-*F64@S|d$C>>vqw;WO literal 0 HcmV?d00001 diff --git a/Xlib/__pycache__/threaded.cpython-38.pyc b/Xlib/__pycache__/threaded.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d931fefa45032b6a57d7022d5f3972b349d778f GIT binary patch literal 249 zcmYjMv2MaJ5Ix7Hg(_JYJ9Wtt53C4v=)lyaLaMSvCO!#*9Y=Oj!6$X>w{*+eiC^f% z85W*&r}v)j-sv`3_5_5V-{*QQ0RE!5M?&J2D!fqBpqT;7O2#5jOk#P-86ax}@=9p_ z0l7HSB;;&-tooftrHKmT>ce|J8E0XCEUf#&;A3)`ewzKZ2t(I7-+NBhm?=Zks>doW zeS?1>_uBp!a_1~cW$>t4?NPqDda&5`K{hy4-WilJP~HxsF1ph=U0SCHgR42^1o{!u GNy;wtyFRV} literal 0 HcmV?d00001 diff --git a/Xlib/__pycache__/xauth.cpython-38.pyc b/Xlib/__pycache__/xauth.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..86d1f69bd8055b876840eddfd7923de7771db767 GIT binary patch literal 2855 zcmbVO&2Jn<7O$%QoSw1Qc)iYt5Gt{V&8#MNSg{d82r#xoMi85g4H>ssdfQVqnV#wC z9#_{Sc`ut+oiOft4Jfhpt$XXn<#g;bUCj6W#9gGH8vNsn-~}f|zkk3VaVGub4<$Lr>hr zuzQT8J#(0OjK9HqC?Op*eO0=EVI7fg=L?dPS?w4V#y#{`^fz=Dbr2xD2?!n_e1}m1 z`J67781}55$Z8cW+VSpC}UpbGas%2Ci@PGLR9)0?>^=W^o+57A>Rev_jtje*7uNr^aKOj8gtS*-l?cvdlv^61a9_x)_Cj9X3whNc3-u$M zJ3|BV@WW$#@KcTkjoCtO=Wbrhy}aJDjxk0D|AD-}4tbv+*hB9z5lbhx_Y(7T-jDGS zSXG$@Ou#t8%o!4}T#+v^h=Jigpo$w?zSmj%boci5-S+ODGH-9+*__r_o|WA6+G|=z zlT^}-%NFg1i5~Z&gwd~8&si!W_NOm*(@D%qYr#WCdmWq7z5^D_`pBew#iFMG__VTeKHOF=te!bR3F|Ybj~kqorNh1rR&t8ni{j{ZvSB zqhTD=-bBbg7o{KQrf)tMKS|F+F)krqM#Bg23HM{(TvQ~@l*5w;kw_C|+~-o6<52dM zIY^^KxgYVvP2KH6H}*+)mI`I2W1c8eA6z-@?Mi9)QZWjpMwBArnKEK9vNI_rU8(Fz zG7h^#Wrt(%Wy%_hD3Q&EGBY0cl&g=Lgd?s$UvK2!!!jlzJ?3rA6WsBAE}uXh(R zCX|s(M#?NbOcu)U2@+`!9t0l!A$%`OCmkU*?gpjku{)*w_Fk(3b?j(!`o z9t2P_k>sNw;NWl?c=eFWq1~qug zRLo!isQ|j1VIX5GLO_gYKsM0dKlt1#oWd zswcV8Gc?&V0>om3|A70urCB0OsP1tnwx}D*C{3ttg+mHm(e3Nzy|s3y{qbIlu5E1G z-Q3v$2jt&@}7zIjBaHOjfWoP0lQ|bw;D)8N}Wv zn>#C%wtGNn54Z^9a!1gdm2;~E?nMI7PLQL7?rN_yjE500&OWEzewt=HIk!PTg%ejt zj569wCkd+%3VWpy|#(9+HT-7QRmX)J9gJ&81)`+rkk}69cj(H|@ z%ll4wKRokI4palbkV=9vIWP=N^daD*Poc22?##Ae5 +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +# Python modules +import types + +# Python 2/3 compatibility. +from six import create_unbound_method + +# Xlib modules +from . import error +from . import ext +from . import X + +# Xlib.protocol modules +from .protocol import display as protocol_display +from .protocol import request, event, rq + +# Xlib.xobjects modules +from .xobject import resource +from .xobject import drawable +from .xobject import fontable +from .xobject import colormap +from .xobject import cursor + +_resource_baseclasses = { + 'resource': resource.Resource, + 'drawable': drawable.Drawable, + 'window': drawable.Window, + 'pixmap': drawable.Pixmap, + 'fontable': fontable.Fontable, + 'font': fontable.Font, + 'gc': fontable.GC, + 'colormap': colormap.Colormap, + 'cursor': cursor.Cursor, + } + +_resource_hierarchy = { + 'resource': ('drawable', 'window', 'pixmap', + 'fontable', 'font', 'gc', + 'colormap', 'cursor'), + 'drawable': ('window', 'pixmap'), + 'fontable': ('font', 'gc') + } + +class _BaseDisplay(protocol_display.Display): + + # Implement a cache of atom names, used by Window objects when + # dealing with some ICCCM properties not defined in Xlib.Xatom + + def __init__(self, *args, **keys): + self.resource_classes = _resource_baseclasses.copy() + protocol_display.Display.__init__(self, *args, **keys) + self._atom_cache = {} + + def get_atom(self, atomname, only_if_exists=0): + if atomname in self._atom_cache: + return self._atom_cache[atomname] + + r = request.InternAtom(display = self, name = atomname, only_if_exists = only_if_exists) + + # don't cache NONE responses in case someone creates this later + if r.atom != X.NONE: + self._atom_cache[atomname] = r.atom + + return r.atom + + +class Display(object): + def __init__(self, display = None): + self.display = _BaseDisplay(display) + + # Create the keymap cache + self._keymap_codes = [()] * 256 + self._keymap_syms = {} + self._update_keymap(self.display.info.min_keycode, + (self.display.info.max_keycode + - self.display.info.min_keycode + 1)) + + # Translations for keysyms to strings. + self.keysym_translations = {} + + # Find all supported extensions + self.extensions = [] + self.class_extension_dicts = {} + self.display_extension_methods = {} + + # a dict that maps the event name to the code + # or, when it's an event with a subcode, to a tuple of (event,subcode) + # note this wraps the dict so you address it as + # extension_event.EXTENSION_EVENT_NAME rather than + # extension_event["EXTENSION_EVENT_NAME"] + self.extension_event = rq.DictWrapper({}) + + exts = self.list_extensions() + + # Go through all extension modules + for extname, modname in ext.__extensions__: + if extname in exts: + + # Import the module and fetch it + __import__('Xlib.ext.' + modname) + mod = getattr(ext, modname) + + info = self.query_extension(extname) + self.display.set_extension_major(extname, info.major_opcode) + + # Call initialiasation function + mod.init(self, info) + + self.extensions.append(extname) + + + # Finalize extensions by creating new classes + for class_name, dictionary in self.class_extension_dicts.items(): + origcls = self.display.resource_classes[class_name] + self.display.resource_classes[class_name] = type(origcls.__name__, + (origcls,), + dictionary) + + # Problem: we have already created some objects without the + # extensions: the screen roots and default colormaps. + # Fix that by reinstantiating them. + for screen in self.display.info.roots: + screen.root = self.display.resource_classes['window'](self.display, screen.root.id) + screen.default_colormap = self.display.resource_classes['colormap'](self.display, screen.default_colormap.id) + + + def get_display_name(self): + """Returns the name used to connect to the server, either + provided when creating the Display object, or fetched from the + environmental variable $DISPLAY.""" + return self.display.get_display_name() + + def fileno(self): + """Returns the file descriptor number of the underlying socket. + This method is provided to allow Display objects to be passed + select.select().""" + return self.display.fileno() + + def close(self): + """Close the display, freeing the resources that it holds.""" + self.display.close() + + def set_error_handler(self, handler): + """Set the default error handler which will be called for all + unhandled errors. handler should take two arguments as a normal + request error handler, but the second argument (the request) will + be None. See section Error Handling.""" + self.display.set_error_handler(handler) + + def flush(self): + """Flush the request queue, building and sending the queued + requests. This can be necessary in applications that never wait + for events, and in threaded applications.""" + self.display.flush() + + def sync(self): + """Flush the queue and wait until the server has processed all + the queued requests. Use this e.g. when it is important that + errors caused by a certain request is trapped.""" + # Do a light-weight replyrequest to sync. There must + # be a better way to do it... + self.get_pointer_control() + + def next_event(self): + """Return the next event. If there are no events queued, it will + block until the next event is fetched from the server.""" + return self.display.next_event() + + def pending_events(self): + """Return the number of events queued, i.e. the number of times + that Display.next_event() can be called without blocking.""" + return self.display.pending_events() + + def has_extension(self, extension): + """Check if both the server and the client library support the X + extension named extension.""" + return extension in self.extensions + + def create_resource_object(self, type, id): + """Create a resource object of type for the integer id. type + should be one of the following strings: + + resource + drawable + window + pixmap + fontable + font + gc + colormap + cursor + + This function can be used when a resource ID has been fetched + e.g. from an resource or a command line argument. Resource + objects should never be created by instantiating the appropriate + class directly, since any X extensions dynamically added by the + library will not be available. + """ + return self.display.resource_classes[type](self.display, id) + + # We need this to handle display extension methods + def __getattr__(self, attr): + try: + function = self.display_extension_methods[attr] + return types.MethodType(function, self) + except KeyError: + raise AttributeError(attr) + + ### + ### display information retrieval + ### + + def screen(self, sno = None): + if sno is None: + return self.display.info.roots[self.display.default_screen] + else: + return self.display.info.roots[sno] + + def screen_count(self): + """Return the total number of screens on the display.""" + return len(self.display.info.roots) + + def get_default_screen(self): + """Return the number of the default screen, extracted from the + display name.""" + return self.display.get_default_screen() + + ### + ### Extension module interface + ### + + def extension_add_method(self, object, name, function): + """extension_add_method(object, name, function) + + Add an X extension module method. OBJECT is the type of + object to add the function to, a string from this list: + + display + resource + drawable + window + pixmap + fontable + font + gc + colormap + cursor + + NAME is the name of the method, a string. FUNCTION is a + normal function whose first argument is a 'self'. + """ + + if object == 'display': + if hasattr(self, name): + raise AssertionError('attempting to replace display method: %s' % name) + + self.display_extension_methods[name] = function + + else: + class_list = (object, ) + _resource_hierarchy.get(object, ()) + for class_name in class_list: + cls = _resource_baseclasses[class_name] + if hasattr(cls, name): + raise AssertionError('attempting to replace %s method: %s' % (class_name, name)) + + method = create_unbound_method(function, cls) + + # Maybe should check extension overrides too + try: + self.class_extension_dicts[class_name][name] = method + except KeyError: + self.class_extension_dicts[class_name] = { name: method } + + def extension_add_event(self, code, evt, name = None): + """extension_add_event(code, evt, [name]) + + Add an extension event. CODE is the numeric code, and EVT is + the event class. EVT will be cloned, and the attribute _code + of the new event class will be set to CODE. + + If NAME is omitted, it will be set to the name of EVT. This + name is used to insert an entry in the DictWrapper + extension_event. + """ + + newevt = type(evt.__name__, evt.__bases__, + evt.__dict__.copy()) + newevt._code = code + + self.display.add_extension_event(code, newevt) + + if name is None: + name = evt.__name__ + + setattr(self.extension_event, name, code) + + def extension_add_subevent(self, code, subcode, evt, name = None): + """extension_add_subevent(code, evt, [name]) + + Add an extension subevent. CODE is the numeric code, subcode + is the sub-ID of this event that shares the code ID with other + sub-events and EVT is the event class. EVT will be cloned, and + the attribute _code of the new event class will be set to CODE. + + If NAME is omitted, it will be set to the name of EVT. This + name is used to insert an entry in the DictWrapper + extension_event. + """ + + newevt = type(evt.__name__, evt.__bases__, + evt.__dict__.copy()) + newevt._code = code + + self.display.add_extension_event(code, newevt, subcode) + + if name is None: + name = evt.__name__ + + # store subcodes as a tuple of (event code, subcode) in the + # extension dict maintained in the display object + setattr(self.extension_event, name, (code,subcode)) + + def add_extension_error(self, code, err): + """add_extension_error(code, err) + + Add an extension error. CODE is the numeric code, and ERR is + the error class. + """ + + self.display.add_extension_error(code, err) + + ### + ### keymap cache implementation + ### + + # The keycode->keysym map is stored in a list with 256 elements. + # Each element represents a keycode, and the tuple elements are + # the keysyms bound to the key. + + # The keysym->keycode map is stored in a mapping, where the keys + # are keysyms. The values are a sorted list of tuples with two + # elements each: (index, keycode) + # keycode is the code for a key to which this keysym is bound, and + # index is the keysyms index in the map for that keycode. + + def keycode_to_keysym(self, keycode, index): + """Convert a keycode to a keysym, looking in entry index. + Normally index 0 is unshifted, 1 is shifted, 2 is alt grid, and 3 + is shift+alt grid. If that key entry is not bound, X.NoSymbol is + returned.""" + try: + return self._keymap_codes[keycode][index] + except IndexError: + return X.NoSymbol + + def keysym_to_keycode(self, keysym): + """Look up the primary keycode that is bound to keysym. If + several keycodes are found, the one with the lowest index and + lowest code is returned. If keysym is not bound to any key, 0 is + returned.""" + try: + return self._keymap_syms[keysym][0][1] + except (KeyError, IndexError): + return 0 + + def keysym_to_keycodes(self, keysym): + """Look up all the keycodes that is bound to keysym. A list of + tuples (keycode, index) is returned, sorted primarily on the + lowest index and secondarily on the lowest keycode.""" + try: + # Copy the map list, reversing the arguments + return map(lambda x: (x[1], x[0]), self._keymap_syms[keysym]) + except KeyError: + return [] + + def refresh_keyboard_mapping(self, evt): + """This method should be called once when a MappingNotify event + is received, to update the keymap cache. evt should be the event + object.""" + if isinstance(evt, event.MappingNotify): + if evt.request == X.MappingKeyboard: + self._update_keymap(evt.first_keycode, evt.count) + else: + raise TypeError('expected a MappingNotify event') + + def _update_keymap(self, first_keycode, count): + """Internal function, called to refresh the keymap cache. + """ + + # Delete all sym->code maps for the changed codes + + lastcode = first_keycode + count + for keysym, codes in self._keymap_syms.items(): + i = 0 + while i < len(codes): + code = codes[i][1] + if code >= first_keycode and code < lastcode: + del codes[i] + else: + i = i + 1 + + # Get the new keyboard mapping + keysyms = self.get_keyboard_mapping(first_keycode, count) + + # Replace code->sym map with the new map + self._keymap_codes[first_keycode:lastcode] = keysyms + + # Update sym->code map + code = first_keycode + for syms in keysyms: + index = 0 + for sym in syms: + if sym != X.NoSymbol: + if sym in self._keymap_syms: + symcodes = self._keymap_syms[sym] + symcodes.append((index, code)) + symcodes.sort() + else: + self._keymap_syms[sym] = [(index, code)] + + index = index + 1 + code = code + 1 + + ### + ### client-internal keysym to string translations + ### + + def lookup_string(self, keysym): + """Return a string corresponding to KEYSYM, or None if no + reasonable translation is found. + """ + s = self.keysym_translations.get(keysym) + if s is not None: + return s + + import Xlib.XK + return Xlib.XK.keysym_to_string(keysym) + + def rebind_string(self, keysym, newstring): + """Change the translation of KEYSYM to NEWSTRING. + If NEWSTRING is None, remove old translation if any. + """ + if newstring is None: + try: + del self.keysym_translations[keysym] + except KeyError: + pass + else: + self.keysym_translations[keysym] = newstring + + + ### + ### X requests + ### + + def intern_atom(self, name, only_if_exists = 0): + """Intern the string name, returning its atom number. If + only_if_exists is true and the atom does not already exist, it + will not be created and X.NONE is returned.""" + r = request.InternAtom(display = self.display, + name = name, + only_if_exists = only_if_exists) + return r.atom + + def get_atom(self, atom, only_if_exists = 0): + """Alias for intern_atom, using internal cache""" + return self.display.get_atom(atom, only_if_exists) + + + def get_atom_name(self, atom): + """Look up the name of atom, returning it as a string. Will raise + BadAtom if atom does not exist.""" + r = request.GetAtomName(display = self.display, + atom = atom) + return r.name + + def get_selection_owner(self, selection): + """Return the window that owns selection (an atom), or X.NONE if + there is no owner for the selection. Can raise BadAtom.""" + r = request.GetSelectionOwner(display = self.display, + selection = selection) + return r.owner + + def send_event(self, destination, event, event_mask = 0, propagate = 0, + onerror = None): + """Send a synthetic event to the window destination which can be + a window object, or X.PointerWindow or X.InputFocus. event is the + event object to send, instantiated from one of the classes in + protocol.events. See XSendEvent(3X11) for details. + + There is also a Window.send_event() method.""" + request.SendEvent(display = self.display, + onerror = onerror, + propagate = propagate, + destination = destination, + event_mask = event_mask, + event = event) + + def ungrab_pointer(self, time, onerror = None): + """Release a grabbed pointer and any queued events. See + XUngrabPointer(3X11).""" + request.UngrabPointer(display = self.display, + onerror = onerror, + time = time) + + def change_active_pointer_grab(self, event_mask, cursor, time, onerror = None): + """Change the dynamic parameters of a pointer grab. See + XChangeActivePointerGrab(3X11).""" + request.ChangeActivePointerGrab(display = self.display, + onerror = onerror, + cursor = cursor, + time = time, + event_mask = event_mask) + + def ungrab_keyboard(self, time, onerror = None): + """Ungrab a grabbed keyboard and any queued events. See + XUngrabKeyboard(3X11).""" + request.UngrabKeyboard(display = self.display, + onerror = onerror, + time = time) + + def allow_events(self, mode, time, onerror = None): + """Release some queued events. mode should be one of + X.AsyncPointer, X.SyncPointer, X.AsyncKeyboard, X.SyncKeyboard, + X.ReplayPointer, X.ReplayKeyboard, X.AsyncBoth, or X.SyncBoth. + time should be a timestamp or X.CurrentTime.""" + request.AllowEvents(display = self.display, + onerror = onerror, + mode = mode, + time = time) + + def grab_server(self, onerror = None): + """Disable processing of requests on all other client connections + until the server is ungrabbed. Server grabbing should be avoided + as much as possible.""" + request.GrabServer(display = self.display, + onerror = onerror) + + def ungrab_server(self, onerror = None): + """Release the server if it was previously grabbed by this client.""" + request.UngrabServer(display = self.display, + onerror = onerror) + + def warp_pointer(self, x, y, src_window = X.NONE, src_x = 0, src_y = 0, + src_width = 0, src_height = 0, onerror = None): + """Move the pointer relative its current position by the offsets + (x, y). However, if src_window is a window the pointer is only + moved if the specified rectangle in src_window contains it. If + src_width is 0 it will be replaced with the width of src_window - + src_x. src_height is treated in a similar way. + + To move the pointer to absolute coordinates, use Window.warp_pointer().""" + request.WarpPointer(display = self.display, + onerror = onerror, + src_window = src_window, + dst_window = X.NONE, + src_x = src_x, + src_y = src_y, + src_width = src_width, + src_height = src_height, + dst_x = x, + dst_y = y) + + def set_input_focus(self, focus, revert_to, time, onerror = None): + """Set input focus to focus, which should be a window, + X.PointerRoot or X.NONE. revert_to specifies where the focus + reverts to if the focused window becomes not visible, and should + be X.RevertToParent, RevertToPointerRoot, or RevertToNone. See + XSetInputFocus(3X11) for details. + + There is also a Window.set_input_focus().""" + request.SetInputFocus(display = self.display, + onerror = onerror, + revert_to = revert_to, + focus = focus, + time = time) + + def get_input_focus(self): + """Return an object with the following attributes: + + focus + The window which currently holds the input + focus, X.NONE or X.PointerRoot. + revert_to + Where the focus will revert, one of X.RevertToParent, + RevertToPointerRoot, or RevertToNone. """ + return request.GetInputFocus(display = self.display) + + def query_keymap(self): + """Return a bit vector for the logical state of the keyboard, + where each bit set to 1 indicates that the corresponding key is + currently pressed down. The vector is represented as a list of 32 + integers. List item N contains the bits for keys 8N to 8N + 7 + with the least significant bit in the byte representing key 8N.""" + r = request.QueryKeymap(display = self.display) + return r.map + + def open_font(self, name): + """Open the font identifed by the pattern name and return its + font object. If name does not match any font, None is returned.""" + fid = self.display.allocate_resource_id() + ec = error.CatchError(error.BadName) + + request.OpenFont(display = self.display, + onerror = ec, + fid = fid, + name = name) + self.sync() + + if ec.get_error(): + self.display.free_resource_id(fid) + return None + else: + cls = self.display.get_resource_class('font', fontable.Font) + return cls(self.display, fid, owner = 1) + + def list_fonts(self, pattern, max_names): + """Return a list of font names matching pattern. No more than + max_names will be returned.""" + r = request.ListFonts(display = self.display, + max_names = max_names, + pattern = pattern) + return r.fonts + + def list_fonts_with_info(self, pattern, max_names): + """Return a list of fonts matching pattern. No more than + max_names will be returned. Each list item represents one font + and has the following properties: + + name + The name of the font. + min_bounds + max_bounds + min_char_or_byte2 + max_char_or_byte2 + default_char + draw_direction + min_byte1 + max_byte1 + all_chars_exist + font_ascent + font_descent + replies_hint + See the description of XFontStruct in XGetFontProperty(3X11) + for details on these values. + properties + A list of properties. Each entry has two attributes: + + name + The atom identifying this property. + value + A 32-bit unsigned value. + """ + return request.ListFontsWithInfo(display = self.display, + max_names = max_names, + pattern = pattern) + + def set_font_path(self, path, onerror = None): + """Set the font path to path, which should be a list of strings. + If path is empty, the default font path of the server will be + restored.""" + request.SetFontPath(display = self.display, + onerror = onerror, + path = path) + + def get_font_path(self): + """Return the current font path as a list of strings.""" + r = request.GetFontPath(display = self.display) + return r.paths + + def query_extension(self, name): + """Ask the server if it supports the extension name. If it is + supported an object with the following attributes is returned: + + major_opcode + The major opcode that the requests of this extension uses. + first_event + The base event code if the extension have additional events, or 0. + first_error + The base error code if the extension have additional errors, or 0. + + If the extension is not supported, None is returned.""" + r = request.QueryExtension(display = self.display, + name = name) + if r.present: + return r + else: + return None + + def list_extensions(self): + """Return a list of all the extensions provided by the server.""" + r = request.ListExtensions(display = self.display) + return r.names + + def change_keyboard_mapping(self, first_keycode, keysyms, onerror = None): + """Modify the keyboard mapping, starting with first_keycode. + keysyms is a list of tuples of keysyms. keysyms[n][i] will be + assigned to keycode first_keycode+n at index i.""" + request.ChangeKeyboardMapping(display = self.display, + onerror = onerror, + first_keycode = first_keycode, + keysyms = keysyms) + + def get_keyboard_mapping(self, first_keycode, count): + """Return the current keyboard mapping as a list of tuples, + starting at first_keycount and no more than count.""" + r = request.GetKeyboardMapping(display = self.display, + first_keycode = first_keycode, + count = count) + return r.keysyms + + def change_keyboard_control(self, onerror = None, **keys): + """Change the parameters provided as keyword arguments: + + key_click_percent + The volume of key clicks between 0 (off) and 100 (load). + -1 will restore default setting. + bell_percent + The base volume of the bell, coded as above. + bell_pitch + The pitch of the bell in Hz, -1 restores the default. + bell_duration + The duration of the bell in milliseconds, -1 restores + the default. + led + + led_mode + led_mode should be X.LedModeOff or X.LedModeOn. If led is + provided, it should be a 32-bit mask listing the LEDs that + should change. If led is not provided, all LEDs are changed. + key + + auto_repeat_mode + auto_repeat_mode should be one of X.AutoRepeatModeOff, + X.AutoRepeatModeOn, or X.AutoRepeatModeDefault. If key is + provided, that key will be modified, otherwise the global + state for the entire keyboard will be modified.""" + request.ChangeKeyboardControl(display = self.display, + onerror = onerror, + attrs = keys) + + def get_keyboard_control(self): + """Return an object with the following attributes: + + global_auto_repeat + X.AutoRepeatModeOn or X.AutoRepeatModeOff. + + auto_repeats + A list of 32 integers. List item N contains the bits for keys + 8N to 8N + 7 with the least significant bit in the byte + representing key 8N. If a bit is on, autorepeat is enabled + for the corresponding key. + + led_mask + A 32-bit mask indicating which LEDs are on. + + key_click_percent + The volume of key click, from 0 to 100. + + bell_percent + + bell_pitch + + bell_duration + The volume, pitch and duration of the bell. """ + return request.GetKeyboardControl(display = self.display) + + def bell(self, percent = 0, onerror = None): + """Ring the bell at the volume percent which is relative the base + volume. See XBell(3X11).""" + request.Bell(display = self.display, + onerror = onerror, + percent = percent) + + def change_pointer_control(self, accel = None, threshold = None, onerror = None): + """To change the pointer acceleration, set accel to a tuple (num, + denum). The pointer will then move num/denum times the normal + speed if it moves beyond the threshold number of pixels at once. + To change the threshold, set it to the number of pixels. -1 + restores the default.""" + if accel is None: + do_accel = 0 + accel_num = 0 + accel_denum = 0 + else: + do_accel = 1 + accel_num, accel_denum = accel + + if threshold is None: + do_threshold = 0 + else: + do_threshold = 1 + + request.ChangePointerControl(display = self.display, + onerror = onerror, + do_accel = do_accel, + do_thresh = do_threshold, + accel_num = accel_num, + accel_denum = accel_denum, + threshold = threshold) + + def get_pointer_control(self): + """Return an object with the following attributes: + + accel_num + + accel_denom + The acceleration as numerator/denumerator. + + threshold + The number of pixels the pointer must move before the + acceleration kicks in.""" + return request.GetPointerControl(display = self.display) + + def set_screen_saver(self, timeout, interval, prefer_blank, allow_exposures, onerror = None): + """See XSetScreenSaver(3X11).""" + request.SetScreenSaver(display = self.display, + onerror = onerror, + timeout = timeout, + interval = interval, + prefer_blank = prefer_blank, + allow_exposures = allow_exposures) + + def get_screen_saver(self): + """Return an object with the attributes timeout, interval, + prefer_blanking, allow_exposures. See XGetScreenSaver(3X11) for + details.""" + return request.GetScreenSaver(display = self.display) + + def change_hosts(self, mode, host_family, host, onerror = None): + """mode is either X.HostInsert or X.HostDelete. host_family is + one of X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos, + X.FamilyServerInterpreted or X.FamilyInternetV6. + + host is a list of bytes. For the Internet family, it should be the + four bytes of an IPv4 address.""" + request.ChangeHosts(display = self.display, + onerror = onerror, + mode = mode, + host_family = host_family, + host = host) + + def list_hosts(self): + """Return an object with the following attributes: + +mode + X.EnableAccess if the access control list is used, X.DisableAccess otherwise. +hosts + The hosts on the access list. Each entry has the following attributes: + + family + X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos, X.FamilyServerInterpreted or X.FamilyInternetV6. + name + A list of byte values, the coding depends on family. For the Internet family, it is the 4 bytes of an IPv4 address. + +""" + return request.ListHosts(display = self.display) + + def set_access_control(self, mode, onerror = None): + """Enable use of access control lists at connection setup if mode + is X.EnableAccess, disable if it is X.DisableAccess.""" + request.SetAccessControl(display = self.display, + onerror = onerror, + mode = mode) + + def set_close_down_mode(self, mode, onerror = None): + """Control what will happen with the client's resources at + connection close. The default is X.DestroyAll, the other values + are X.RetainPermanent and X.RetainTemporary.""" + request.SetCloseDownMode(display = self.display, + onerror = onerror, + mode = mode) + + def force_screen_saver(self, mode, onerror = None): + """If mode is X.ScreenSaverActive the screen saver is activated. + If it is X.ScreenSaverReset, the screen saver is deactivated as + if device input had been received.""" + request.ForceScreenSaver(display = self.display, + onerror = onerror, + mode = mode) + + def set_pointer_mapping(self, map): + """Set the mapping of the pointer buttons. map is a list of + logical button numbers. map must be of the same length as the + list returned by Display.get_pointer_mapping(). + + map[n] sets the + logical number for the physical button n+1. Logical number 0 + disables the button. Two physical buttons cannot be mapped to the + same logical number. + + If one of the buttons to be altered are + logically in the down state, X.MappingBusy is returned and the + mapping is not changed. Otherwise the mapping is changed and + X.MappingSuccess is returned.""" + r = request.SetPointerMapping(display = self.display, + map = map) + return r.status + + def get_pointer_mapping(self): + """Return a list of the pointer button mappings. Entry N in the + list sets the logical button number for the physical button N+1.""" + r = request.GetPointerMapping(display = self.display) + return r.map + + def set_modifier_mapping(self, keycodes): + """Set the keycodes for the eight modifiers X.Shift, X.Lock, + X.Control, X.Mod1, X.Mod2, X.Mod3, X.Mod4 and X.Mod5. keycodes + should be a eight-element list where each entry is a list of the + keycodes that should be bound to that modifier. + + If any changed + key is logically in the down state, X.MappingBusy is returned and + the mapping is not changed. If the mapping violates some server + restriction, X.MappingFailed is returned. Otherwise the mapping + is changed and X.MappingSuccess is returned.""" + r = request.SetModifierMapping(display = self.display, + keycodes = keycodes) + return r.status + + def get_modifier_mapping(self): + """Return a list of eight lists, one for each modifier. The list + can be indexed using X.ShiftMapIndex, X.Mod1MapIndex, and so on. + The sublists list the keycodes bound to that modifier.""" + r = request.GetModifierMapping(display = self.display) + return r.keycodes + + def no_operation(self, onerror = None): + """Do nothing but send a request to the server.""" + request.NoOperation(display = self.display, + onerror = onerror) diff --git a/Xlib/error.py b/Xlib/error.py new file mode 100644 index 0000000..cb6d0d0 --- /dev/null +++ b/Xlib/error.py @@ -0,0 +1,160 @@ +# Xlib.error -- basic error classes +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +# Xlib modules +from . import X + +# Xlib.protocol modules +from .protocol import rq + + +class DisplayError(Exception): + def __init__(self, display): + self.display = display + + def __str__(self): + return 'Display error "%s"' % self.display + +class DisplayNameError(DisplayError): + def __str__(self): + return 'Bad display name "%s"' % self.display + +class DisplayConnectionError(DisplayError): + def __init__(self, display, msg): + self.display = display + self.msg = msg + + def __str__(self): + return 'Can\'t connect to display "%s": %s' % (self.display, self.msg) + +class ConnectionClosedError(Exception): + def __init__(self, whom): + self.whom = whom + + def __str__(self): + return 'Display connection closed by %s' % self.whom + + +class XauthError(Exception): pass +class XNoAuthError(Exception): pass + +class ResourceIDError(Exception): pass + + +class XError(rq.GetAttrData, Exception): + _fields = rq.Struct( rq.Card8('type'), # Always 0 + rq.Card8('code'), + rq.Card16('sequence_number'), + rq.Card32('resource_id'), + rq.Card16('minor_opcode'), + rq.Card8('major_opcode'), + rq.Pad(21) + ) + + def __init__(self, display, data): + self._data, data = self._fields.parse_binary(data, display, rawdict = 1) + + def __str__(self): + s = [] + for f in ('code', 'resource_id', 'sequence_number', + 'major_opcode', 'minor_opcode'): + s.append('{0} = {1}'.format(f, self._data[f])) + + return '{0}: {1}'.format(self.__class__, ', '.join(s)) + +class XResourceError(XError): + _fields = rq.Struct( rq.Card8('type'), # Always 0 + rq.Card8('code'), + rq.Card16('sequence_number'), + rq.Resource('resource_id'), + rq.Card16('minor_opcode'), + rq.Card8('major_opcode'), + rq.Pad(21) + ) + +class BadRequest(XError): pass +class BadValue(XError): pass +class BadWindow(XResourceError): pass +class BadPixmap(XResourceError): pass +class BadAtom(XError): pass +class BadCursor(XResourceError): pass +class BadFont(XResourceError): pass +class BadMatch(XError): pass +class BadDrawable(XResourceError): pass +class BadAccess(XError): pass +class BadAlloc(XError): pass +class BadColor(XResourceError): pass +class BadGC(XResourceError): pass +class BadIDChoice(XResourceError): pass +class BadName(XError): pass +class BadLength(XError): pass +class BadImplementation(XError): pass + +xerror_class = { + X.BadRequest: BadRequest, + X.BadValue: BadValue, + X.BadWindow: BadWindow, + X.BadPixmap: BadPixmap, + X.BadAtom: BadAtom, + X.BadCursor: BadCursor, + X.BadFont: BadFont, + X.BadMatch: BadMatch, + X.BadDrawable: BadDrawable, + X.BadAccess: BadAccess, + X.BadAlloc: BadAlloc, + X.BadColor: BadColor, + X.BadGC: BadGC, + X.BadIDChoice: BadIDChoice, + X.BadName: BadName, + X.BadLength: BadLength, + X.BadImplementation: BadImplementation, + } + + +class CatchError(object): + def __init__(self, *errors): + self.error_types = errors + self.error = None + self.request = None + + def __call__(self, error, request): + if self.error_types: + for etype in self.error_types: + if isinstance(error, etype): + self.error = error + self.request = request + return 1 + + return 0 + else: + self.error = error + self.request = request + return 1 + + def get_error(self): + return self.error + + def get_request(self): + return self.request + + def reset(self): + self.error = None + self.request = None diff --git a/Xlib/ext/__init__.py b/Xlib/ext/__init__.py new file mode 100644 index 0000000..37229ba --- /dev/null +++ b/Xlib/ext/__init__.py @@ -0,0 +1,46 @@ +# Xlib.ext.__init__ -- X extension modules +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +# __extensions__ is a list of tuples: (extname, extmod) +# extname is the name of the extension according to the X +# protocol. extmod is the name of the module in this package. + +__extensions__ = [ + # We load this first so other extensions can register generic event data + # structures. + ('Generic Event Extension', 'ge'), + ('XTEST', 'xtest'), + ('SHAPE', 'shape'), + ('XINERAMA', 'xinerama'), + ('RECORD', 'record'), + ('Composite', 'composite'), + ('RANDR', 'randr'), + ('XFIXES', 'xfixes'), + ('SECURITY', 'security'), + ('XInputExtension', 'xinput'), + ('NV-CONTROL', 'nvcontrol'), + ('DAMAGE', 'damage'), + ('DPMS', 'dpms'), + ('X-Resource', 'res'), + ('MIT-SCREEN-SAVER', 'screensaver'), + ] + +__all__ = map(lambda x: x[1], __extensions__) diff --git a/Xlib/ext/__pycache__/__init__.cpython-38.pyc b/Xlib/ext/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e1978d9db4f884abf2af3085a7d02d8782afd78 GIT binary patch literal 667 zcmaJHV%6t|tkbrRB)3JEcQvSvuVAhDo|$hnqC#HsAGGG0zszoRwsm+tJyjo9Fy zVdQUNyOoKFfsKiGtqM%M(|daF_wL^3osUKX1L@1xX9=?nkmYj}V1Nl0 zbf618uwf4z=)(Yp@Bl`ze>IK`Zo(K2J{jEjXvG#h42|jG)Y?5h$(t+*F$NVriwgk`Pm@b&zRsGhuLmG(Q)(kH=uHIJP(#Xu})h}r@1*1(zHlxN{^}=<-E*TehKXO2xI+3HpCExKkcY}O7ZsH4ke|kle0?gQOdH6(!aUy aH2uLaSYASt@#Cjx$q5R}{LlH(ee@gMHmy7W literal 0 HcmV?d00001 diff --git a/Xlib/ext/__pycache__/composite.cpython-38.pyc b/Xlib/ext/__pycache__/composite.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b10f05c254b42721b90a5159104c48609f6aa0a GIT binary patch literal 6388 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 literal 0 HcmV?d00001 diff --git a/Xlib/ext/__pycache__/damage.cpython-38.pyc b/Xlib/ext/__pycache__/damage.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..25eab340704c3c4e0283816da9ade2d6b9f70e0a GIT binary patch literal 4208 zcmb7HTXP#p6`uQyZni8taW38@n*OnI(~_rw*}I8xO7a z%@dMa+t&7@wxb>OM2C5yM!h1(Sfpg0jZ{yf9X#4mDpNYxzV+){cXxU;ZT^9vjmYb0 zNGrC56+6Pl7LH!JorvvZknBrdqtkYE)a?$Ff$Vm5wc8zJVw6(5-t9gaC8?qO!&0;s zSFb+Ze4GvBW|GS1$xtY{xt;Y!134`6%>y~=sw|b8kJA3`rhHy(3f}9Z!?B96z)h6g zK^58DKcSN!2~=mw zU1e%b_)M*ffWM8!IpD8}5OYJQRRoz;bp5v@sm8yTD(`1Q9b|{SOh`pX*Ns7Pkg4v| zDXlT+4^O*liCC6%`D7%AJ=q?cYh1_*@6~ zc`w~~Kx^d1JvrPj9_xUppZpleDKoAs59DDwzJqgV_cw_!93OwCYdl^~13S1*p){=q z?Y@HQbCi4oRbUrSIpU?%a;$e1C%=``6X0^bz~jbRF0hja0q&x zmmY-ac33EFPluwPAEwDzwJ<}iQniMv?ePZcI@)@5Ulv_xksOlbyDV!RVt}*~hT!Eg z-J81(-9$GaDK#!3`4xt8_E5M%)pl&>YxP}>EvOpl_ZBhVL-iJ_21WaV)UQH{hHq8) z!jZL!i#Dql6sRFW+&R*9V{vzsObTf(%&HL>>(G%<$+KiPmD*3`Q<*BGB1>;l5w(u< zsS8wHq>2QtLR321E?03M%0jQ0O`!mpg5uD!SBRGYo|75dHLJh}AlygEY4=hENC}_} zuM|;xWU1vt`bzsU>>r{5z^!9@V*S}E9BOmlEg+sF>ib7fgO|Q-9XaHQI&GgpHsbgm z=BqYU-$&IB4E}3Lnr1!742o;KU$-y#B?bdm`H~hi!f1tOJ6{EX`VlA#nn9P`qA5guTg{kwh+3razHS&Gwqd_2o9g>AmQr-- zPP_U}D1SSAA7eE-J8Sz4zONOJ&jUwoABBOVGn}+IBaDO&u-?4MeiQiW6O1hYLuS22%v8NCFmonbp=dA( zkXi(|T&R}}P!C7Dg-Uvb(M&Q1MoI_po=IP6|1eQS-f7p~4Y@AHW5=Vt~6P1Y>qW@=>!R zzl(|eZ;&WJ{)J9V7c4=dAgPGbb5WHKwP!(*{O+3|0zjPj8q@OTOlx*{pg?P%v9HqM z&DhSuQk}w*2=P};pEoCpfF$t+JSYfk5%V8Wm3Rm{cBOahqnJdCzLa8l$00{P4=EQV zRTU8+9(ss_NS^SmiAdHD8WV@x71N$ju1<+)wTy|KtmyBJJK8J8hmxJYI*08|EFjg> zm7+g@V@L*vv6sMq$17#D?Cwh>qJ4FC9;RCrqkM>97vVp$~|kb%EnL z1zwI0+Uwl!>=@l(k3>F1`#XdqqsBs;!(e7DI{JnAtNbOUuI%mWb`x1;-SJ$!PSYa; f!s`Mcdhyc{c&(KvXjP+XD~zI6r4_a!d>j7-@o;DL literal 0 HcmV?d00001 diff --git a/Xlib/ext/__pycache__/dpms.cpython-38.pyc b/Xlib/ext/__pycache__/dpms.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f1e27720352ae6160d0fd0d352ac7cdaa2b7fa36 GIT binary patch literal 4888 zcmbtY&2!tv6$e0&06|i+KK%U=IF6ez$%yr&jWeD!qegZ!?ZmWerKtvoU?A>NqK5?N z#nQ4mx+T|6&%K$q>eilm?XmaXaLeSxKOi%W8_o3h79a^xa^+g!?8EN9#eTfsd%xY~ z?`kzi!}GU)U28qKq-oz#A^(^ttRlr1bWLNLr!k!wUA<>`hEDlHx6m^^6M54sGTke& zqGz#^XEVzy^AfkX&C6}mb3W6U&C1_stjx{Fm9Gn4l{u{Pjpo&$sj?b0wM;X|=GZ(m z^U$1O3(zcNnlo&XEkUyY%`!U+%~@y`(c2O`$Ie5u49x|05t@sc<}AC!E<gtd3KGx0nG(yTy`Cr>zU>vdz0OO<`Oh-v76A`gob8)nf-*l4b2ty4y)sRmA$*J z)!+NW_s$pFo!I5iBoE_G6uNy8J?$_ayBqF95y_|(b=_7JN`ZIulnYmGbN9=8>%VmG zcH(|F*mWL8JE*=Nguxc?@ld+!ovkqFcET<9ox2b3uh-qyb|8Y5h$3qE_P>)`msNsKNTZcx7Amy3UAPrLHIX281D=BqmLC z1Xs~U28jYh3+d2PL1`5!ehR@gPiLBE@WOKw2|hH`@h&k#y{(~!lFbULWHWQ94~y`u zw6&wO6;{l&CH1Zz{RZtT%)+;pS7SC1D<^X_)z7&6k_!SXDMkHO1pCqClUgr$90?yd zDP67C38zI-q$cw*e=^`q58MV#&Tw?ov81%UBCywv;A~xt!zUuo=20=H~ z2(n93T9;zbl1Xt5=zWxw9;gWCeq{8gF%$>g&=AYH5jfL=_^jT50|0JW9>|c`-+-c@k$xkUxn9 z5{o33NRU5@b0p{^ae>5H5*Hy7^V2Bm3IZl)D&;a{FYy+{Z;=#q6Z8+v(jnwusCKW5 zySR!004`;29{1TpOMQKogR+{GEVWNcu=06f;t~dgyZI}FVj|_ltJ9*A)8cG zqIP>+7!k7Ims3Oka?tA-p@S4toV^ruqJV!3YI|DXsgG9xKB-dhW=4wlXo54*yF!ye zI`mAo8H)FlJpd>k+GbQa-B`+mHnt9jSwPoL(Ou{AFuE3o5$}?CkHq^V2=QjU^2(U0 z_*9Xq0(Tk!)5vU~%de4Q@`njXik$(>4}$b9I<}1;%9oMWkQA98$HaU3p0QT|AJYoQ zd~BuQ(8p6Qo>?N|mqwg{5w&kp?Tk65>rtd!jdQsEiKa(nrj1#dm{~Voh@YbF2)bn1 zRiv1PI2m2yW>&!3%*3JCycbSrc0>kh?w4N~q?bZ-3myHL=Hx(3pg9m!f7)1~K4y%3 z97!f6u2wc2Ph(=u1MxGO2GXJD_3fzkD;!>@hNS(fDL#PAQTNnye-6c8kY>$8^`|ut z_3`&r5&ygW39xb!fbZtBJKsQIf%wD{-z zDetPyU_^#bDt$Wi`(3L3KX@XsAMrREpu@Cpb#+MleyMYQgz^4P3!$BH;+(qkqaaPo zfEYiNCpJ@zL6LeqV}LD+IkqSs%NBR=IoN>4n8BxRq#Qvla{i*2q0%O6rFQuT^lH)* zsC45;@e9-*0c%3}ZD>yh>zD)E|Jw9x*`|+eKy3$e*-gSGLiu%wDU-8|)IKmdx(Qld zQp8p-`H1MfDc*+6EqEFrpFr_Xq?l|lfoS&7MI=uxPa87S#|$LLGK$&3(h`~$(b@!r z1Mw+JNBo8suKY$Kv!^{*e;7!8SkT9%f;4g^6L*2Bt|-l1sU(ZJk|j&Ik}a)VsVr?O zF%ACcG}WrSGjvb&bLW2iW?5N+KbD!wYY$^ TgjBViMH_EQW!qj{w5|UFpzhe5 literal 0 HcmV?d00001 diff --git a/Xlib/ext/__pycache__/ge.cpython-38.pyc b/Xlib/ext/__pycache__/ge.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1332d6cea6afe94af944c58cff11f776272c9ba GIT binary patch literal 2176 zcmZ`)OOG2x5bo~jdDv@vmn0Ac#9{%043AhLQa~btNFj;@$O(x+k%csxxO;cK*fUPg zc(snarvz?r<-)-}a^$yg>no@I2|y5h)njj*MD(a?y1Q!nQT2T_UpJd|hW6X<+x@u7 z*dH`lZxVy6km4?sVm+p~5<@-`J;9msdY+O!d4nlm1&^2twAl9_dc8n}Dtg3vAuKgj zhoufn1WQeAs0J)`)l@CKH`L}G7H>V+uJ7vh`|TS#(`L|bUq8@U*}i^Q>Z};#S^XHh zvzS|9?msyDKTgMQz^6}+>um#rtB~RVO0yovC4~0o65=bq;CKg0xOcc<7)8o+qe#gG zU--S+(!1`g8_IXqh6>!f`TRHV-%ugG4SOvW;d(XOx^exhi8j-3w4wXhARqU0r47fS zY;%50Nr#oi2-+mLQ<_P?wEnf!sE=%L(;dJzZ_~m>`K8Ww%RL*A_0k7e)>QMhc3Y2! z(`z`a^=_rA^0;`}Zn*i?XK;ttugsjoUQYdWv9p(tbSE9^!!%PycRtJelM&KdboTWm zG5Jt;?hXg{I{L8e?CJ~SsUfgY5{oue#3hgL1f>6#p{#mK9sTLN`xKp3qkaIn1}R>K zD%p(fbCS6zkyOI2@_v*^iR_vP9xef~w4oXl<6$~AK26dhCWMNm+r_+szCFFG%LIq5 zAep$N*$@*HW?)kmdbsl>0ESLrn~1KNu5x=BQ$+)cae?dAp~YkK8phAzwuZ^8km7Tw zC%ElN?au>9=&(y~!mhtWtv|03YTXz(1$yfsAd!)SY5^`@w~Zx#9LKCIr(bxJm>?mcl#b@SB(PaL%x6%KS0g6*`85!+4tfcJCf+5PPjhLq!LH6L=N`FjP39l zpCJdoh?)1Z_=$5i6Xvk=Feepz=pFep-yF{TxkT1{NU$RmTZp4z7GOS@hch`73-K-1 zJfc3^kCcA}l?hi1y2n(^EQ^J$-y3A9nX0r*tL!y*b)u=NBs6Cw>9|&L!>>XCE)B9l z3I2pgby}9jdgz$fv8Z{IR&I<_Q|P2x+xl*)I5OMh@HQz26#1Q=7AB_gdHN~@zZ~~? z@(eu8yRhGc6mLN>K%&80LW+Qke}w=z{*h8V4uJgzY-b=_;=zmS`Ma<>b1b^uxM8T= z8A6yrHD`8f-Pe)WPh8=lnqXR_Hc7oi>J*d>lbwMcs=^&@?YNHE1ZmTT`V>--Ke4pp zDRC7%Z8>|NsJv>_Cvm)_h*Z2({Glk$3>-%j;DLAwi~t`yVwq9VbrFm`8}6kAWyD3e znv_-CPqJe!V(GOLJN~s&rPlJk3zvfE%@EOun-9_JQ!G}E`h;zF*7wkvvAO%_m3-j= zt<}gY#d748vh)}9FC+w_st^KtTL9Zr#P7X;nWv8u2lmVf>zr+*%e`E=a^MbTPd{yU z@HLmPfs1i0dXmbPm4j?2fA->-9F!yYF0b3-Ep9%>7$`xK6P5P?^d?o{3uBX)c|RXI zHkemw1KPzlT#aA;A*xD9waUChs!fXG=T=?Y{sbvDx>Eg-J~<1wf__<2HiYz~cj|Oh JJ9Rz^{sX#U0R8{~ literal 0 HcmV?d00001 diff --git a/Xlib/ext/__pycache__/randr.cpython-38.pyc b/Xlib/ext/__pycache__/randr.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b2add69d58346c9de6fe912bbc524f1e9e267072 GIT binary patch literal 26032 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 literal 0 HcmV?d00001 diff --git a/Xlib/ext/__pycache__/screensaver.cpython-38.pyc b/Xlib/ext/__pycache__/screensaver.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76327eaba1764cf171b645069e93ba13c133217b GIT binary patch literal 4754 zcmb7HTW=f372YLx$>s8*yYCmKHPJogl~DAkT7v z$LyehzK?zp{Sx|R^eexzSe|(=Eaq`%f9{XjV4fA2|H29uz$vm4IHeJ1k(F5moF#DP z*gQD%;4EX!3R_@{;9LM_i7kV(JmOsBnf*)S##Y)6yTC5Ku!GBxyu>a;^72UX3ahd! z>?(Ly!Mnz;gLfUgYtVn4-C#GN{{~yxwrX#M!nUl>_99v3hl+P)lys|M947~|D)??B z6&F!=x2pDdwG~IatE!=_hTZCus%#041>tip-tiMreb(n

s6nM07%B^z0;kl`icI zL2IYhtor_atYTb+QOvPn4rbId3nk}x$Do?D&FXeS8=`Hnq}A_0kzDm3i9{u>B(BOH zZ$<5>W!`z$uj03-R8QWm*AEU3Ru5Mbv0E2B=Aq=W{^ZuJtvjoC>d;ij3)B7gNLSr_ z%Ico##Qu}@d)4#2ptT#R)i%xnyML;Zo_W_{Yp0%SRN&O5FmD>4)#@Si`zL^$RkOAI zq?XYc@l5BX68)Bvzn_2j;IntP*S9t{8t-m@^mt?I1O~EBG9WElH2o`Lu!bi80K%=n zW>%2l*&_$dW%jX+XO3meGk}mF zi>GTntjz=a1t7`SrB4X~kGUWa>0HulC5#iiEM4w|`-x}*Du!3?MBT|ys3tiqK#Zvu-cX!pE&OQnmbc%FR7q)mW9;{=#I!Ehn-PT1jH4@K35&~aX;tFk* z$TxTEdr61a!irm`lFeD8~GL;_KY^SJ!ye)A*qOoX7|}&SG|-%47IE2(DkCIU z7%&TNg%!-xWxjdNu_AQ(h}b0@Sy>khmLE_sGv{Q`=`j%=ggY_Uu2dl+Bl%xYGI67c z_{5TfproR>32e&^rB6+ejHL^`P79t90`w zOwkvJAA&S$#q*j^{FoN~gorWU~ZgtqjQcNB#fn{zGes z5MbON7I0=PVD^t$(mP{@T1Hecs~rOCtT{?>dmKqKqBBttn|1J2%$+d_p?Hmi{|xfx zCNZW-T^a?KaaMoi%>Kxki&SRu(ta7Of;QLA9%r%6C8x#{PL1cn5sEIDxEw683QRnw z%iCOiq?Cwu`ie_q?@c<7t%pD9;8U4>Q6h9+!o5&p^9}xLD2mnocn+0^y0Q%gT)}R7Gnh)eG`YRGik<6LG_-8zV!q^&`q%S+aM^MURl+j&1_jwn)nZ+)d++ep+^WO+>-;IoQ zsQe3UwFEMSj8}p<=L$+wRf$Op^aYckv<5X3gTscTch-h!P!dLV5upoHSNP)ERi4)*QbBcW51%8@sZPvvxXyp;TsS z?}!?>!a^{t6glxPA!MgK#T8bQH;kGkR^eL#cACXq)(J|(gRqD$stHoDHG zcNV%=q&4N+B#|F8O?_jI!z6|eNsIz=8e=FZXm{iE2+XHU1^rXRYVX7J#`#OCyCtO_ nr&zM*Gfo!Zr8cIS#bqzQxPn&jyv0Ssu`{pyl||3bFIN5ssYW~` literal 0 HcmV?d00001 diff --git a/Xlib/ext/__pycache__/security.cpython-38.pyc b/Xlib/ext/__pycache__/security.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a806418ee3b7d32e7f68e964f29076bf54f2753 GIT binary patch literal 2928 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 C?fXjr literal 0 HcmV?d00001 diff --git a/Xlib/ext/__pycache__/xfixes.cpython-38.pyc b/Xlib/ext/__pycache__/xfixes.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ed9ff75705374ecacd91e89b6db5117ce42f43d GIT binary patch literal 5522 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 literal 0 HcmV?d00001 diff --git a/Xlib/ext/__pycache__/xinerama.cpython-38.pyc b/Xlib/ext/__pycache__/xinerama.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d81454286089bf83753e65de1cf1885d8bdd0b1 GIT binary patch literal 5447 zcmcIoO>^7E83sTQ1i=rTiG?S>Ul{Ok2%7L&;5h@7C z3sAB$PA|=&J>=FOU`fC|EDI^YW@FpY;NLsuNNxP6V`5d2zqy@1eO>Fk;vE{Awh=@^dP9%hWIHn64Y**Q?~5d1Y0T1rU}XpKFcnb} z#1RXkek_MxiubPLygv{UgXl?FD7+-4aj5d3-^Xv1Vs0Q z8!U-gGKfbZ7LMg_^VNftc?sJSVc2DC)fubMkD*5>Ni84Jp4UeayVZT2N%3GLl!~xI zkVLPh44Oz;+=uwq4V*1?QaUjmI!e(M2i+9D2Mw@Q#$y^W@OGhXmI#T%Rj^qoe$ubY-iwno+`mCR_8lqGk&K{KCLeUy zQ#tan<%orW7}0)ryd(*tEo$0{1MF22L^#%#NE!E1Pw5h;5mV!RkZp(~p}*qSkN_4; zJoLp5?Ul|QBzLgyU2%i$4FZ2q>?6lNNxf8Hbjd)FYpfnx{8gYqdkD+pc~F{2x`(iA zt)pl9M#snu`5>zSTbwA#?2Xm6pWeUo-W{L1On(}vtl&w0heBws&NbH%mB%KYD%THm z{94>lzqJERwN_NE%1!la^Xh?qV7c{Tl%smt+)~m8uc_arYHjd*Y@-}&V{FZB@jA@a z$(p|$3Az7ks2O_7tayi5CUtBrYYn~aSoXjMC2I|XXi_CJ*A7*`xrFE!`Jxw%hMPhj zE<&`{X=iq?7kNX`>t#-_H;nlxr1xg8_W&lFOJs#svT`0|)w`bLw=?VKY8P4Se$MN6 zMYNR;vdS+!jyVgmXZ8DHC)~e_?PeB@yS1D(R8!I2LQiyfqDeUCymg(!;`wb$9lqg(+^}Nv@%=byK(TwNz<8HS1>a zUbK9s`un7v@rT}NlDl}4%P3Oqk+!YV^Vmp%5ds3j!zU(CsjNvNNyp4;a27kEw=Zj` zOPh+ilBfmbOL))D!ZLcLAMGj7vKksl!%y_ggpu|qI*>2YpcW95`(tcZ(3GfPhI2T& zp&MUFI_NR(QXkd1kC#(%*CzfQm@`}*v$07`28&w-gLAI7iOJyi9Irtec4prb={lKc zt_IRgX6*$LkN2{wvR&Ckn{1(2>&(kGRpzLer-CRZ&rq>I#aSwdqM7x9nnRwamJ8Hk zz7xlxyh!y+RGg!N(3;YpViPG*UPo~cPeQdRQvAoXju7Ih>UoR~Rg5~34j_if^-n76 z+7n}~Q&CXo$A1OGI#o4g-g*TM@?{j65pc< z;XL+OjRNP~Cn-)4>IKg20_Qg2>?86mSYH$t*lVj zKcG0hB?GlOMUsQ6>lD$ap=`IbVl=mff0X@n;sj!roN-TvIHjoko6i{krlNh5p%#rQ+-Wo#f{G8-Gn89YLKP|?Bo z@U{CYhbnRxsEHCM%80g*-$X3e5zB*d$DDB;@^vf#meDSr#j=heuR`S33u{BnpP}5` z6y*}E{zr7rqlZD!!=&i3jJ9K(E29V5(${nD02iQwV|B$bXC{})y-BO^EzXQLu~&p{}@}UAIwuGmwG;2oPd!d;Y*c;(CVLa1`|-aa~uQFUcV$RZSpvt=s|>%5E*E zbF$W1ICZr6pKxd9yL6EwT2U(Mr(U!bijzt3Hah+V&(Rn%O9H+5nWUaaXh6ARNdwx{ ztbZUHtZVX3h+ywDein8{Z8LUUi$|^2AN75&ish)_eN)S#oV$QnprM!!lBYQN^Q0}ph$|LQu`pYWS4=Q?It8gOg%Ubl>#a~2e?BnV}L{mN~zYsLl zg}Asz=Xb_y)Z;o8l= literal 0 HcmV?d00001 diff --git a/Xlib/ext/__pycache__/xinput.cpython-38.pyc b/Xlib/ext/__pycache__/xinput.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e559310d44d5c2a021426ca7eee0c42d761ba9cc GIT binary patch literal 17363 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 Xr@rKu>NU`^$GBx%_CG;b_kH(Ye`Skm literal 0 HcmV?d00001 diff --git a/Xlib/ext/composite.py b/Xlib/ext/composite.py new file mode 100644 index 0000000..0e10b63 --- /dev/null +++ b/Xlib/ext/composite.py @@ -0,0 +1,272 @@ +# $Id: xtest.py,v 1.1 2000/08/21 10:03:45 petli Exp $ +# +# Xlib.ext.composite -- Composite extension module +# +# Copyright (C) 2007 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +"""Composite extension, allowing windows to be rendered to off-screen +storage. + +For detailed description, see the protocol specification at +http://freedesktop.org/wiki/Software/CompositeExt + +By itself this extension is not very useful, it is intended to be used +together with the DAMAGE and XFIXES extensions. Typically you would +also need RENDER or glX or some similar method of creating fancy +graphics. +""" + +from Xlib import X +from Xlib.protocol import rq +from Xlib.xobject import drawable + +extname = 'Composite' + +RedirectAutomatic = 0 +RedirectManual = 1 + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card32('major_version'), + rq.Card32('minor_version') + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + rq.Pad(16), + ) + +def query_version(self): + return QueryVersion( + display = self.display, + opcode = self.display.get_extension_major(extname), + major_version=0, + minor_version=4 + ) + + +class RedirectWindow(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + rq.Window('window'), + rq.Set('update', 1, (RedirectAutomatic, RedirectManual)), + rq.Pad(3), + ) + +def redirect_window(self, update, onerror = None): + """Redirect the hierarchy starting at this window to off-screen + storage. + """ + RedirectWindow(display = self.display, + onerror = onerror, + opcode = self.display.get_extension_major(extname), + window = self, + update = update, + ) + + +class RedirectSubwindows(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + rq.Window('window'), + rq.Set('update', 1, (RedirectAutomatic, RedirectManual)), + rq.Pad(3), + ) + +def redirect_subwindows(self, update, onerror = None): + """Redirect the hierarchies starting at all current and future + children to this window to off-screen storage. + """ + RedirectSubwindows(display = self.display, + onerror = onerror, + opcode = self.display.get_extension_major(extname), + window = self, + update = update, + ) + + +class UnredirectWindow(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + rq.Window('window'), + rq.Set('update', 1, (RedirectAutomatic, RedirectManual)), + rq.Pad(3), + ) + +def unredirect_window(self, update, onerror = None): + """Stop redirecting this window hierarchy. + """ + UnredirectWindow(display = self.display, + onerror = onerror, + opcode = self.display.get_extension_major(extname), + window = self, + update = update, + ) + + +class UnredirectSubindows(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + rq.Window('window'), + rq.Set('update', 1, (RedirectAutomatic, RedirectManual)), + rq.Pad(3), + ) + +def unredirect_subwindows(self, update, onerror = None): + """Stop redirecting the hierarchies of children to this window. + """ + RedirectWindow(display = self.display, + onerror = onerror, + opcode = self.display.get_extension_major(extname), + window = self, + update = update, + ) + + +class CreateRegionFromBorderClip(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(5), + rq.RequestLength(), + rq.Card32('region'), # FIXME: this should be a Region from XFIXES extension + rq.Window('window'), + ) + +def create_region_from_border_clip(self, onerror = None): + """Create a region of the border clip of the window, i.e. the area + that is not clipped by the parent and any sibling windows. + """ + + rid = self.display.allocate_resource_id() + CreateRegionFromBorderClip( + display = self.display, + onerror = onerror, + opcode = self.display.get_extension_major(extname), + region = rid, + window = self, + ) + + # FIXME: create Region object and return it + return rid + + +class NameWindowPixmap(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(6), + rq.RequestLength(), + rq.Window('window'), + rq.Pixmap('pixmap'), + ) + +def name_window_pixmap(self, onerror = None): + """Create a new pixmap that refers to the off-screen storage of + the window, including its border. + + This pixmap will remain allocated until freed whatever happens + with the window. However, the window will get a new off-screen + pixmap every time it is mapped or resized, so to keep track of the + contents you must listen for these events and get a new pixmap + after them. + """ + + pid = self.display.allocate_resource_id() + NameWindowPixmap(display = self.display, + onerror = onerror, + opcode = self.display.get_extension_major(extname), + window = self, + pixmap = pid, + ) + + cls = self.display.get_resource_class('pixmap', drawable.Pixmap) + return cls(self.display, pid, owner = 1) + +class GetOverlayWindow(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(7), + rq.RequestLength(), + rq.Window('window') + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('overlay_window'), + rq.Pad(20), + ) + +def get_overlay_window(self): + """Return the overlay window of the root window. + """ + + return GetOverlayWindow(display = self.display, + opcode = self.display.get_extension_major(extname), + window = self) + +def init(disp, info): + disp.extension_add_method('display', + 'composite_query_version', + query_version) + + disp.extension_add_method('window', + 'composite_redirect_window', + redirect_window) + + disp.extension_add_method('window', + 'composite_redirect_subwindows', + redirect_subwindows) + + disp.extension_add_method('window', + 'composite_unredirect_window', + unredirect_window) + + disp.extension_add_method('window', + 'composite_unredirect_subwindows', + unredirect_subwindows) + + disp.extension_add_method('window', + 'composite_create_region_from_border_clip', + create_region_from_border_clip) + + disp.extension_add_method('window', + 'composite_name_window_pixmap', + name_window_pixmap) + + disp.extension_add_method('window', + 'composite_get_overlay_window', + get_overlay_window) diff --git a/Xlib/ext/damage.py b/Xlib/ext/damage.py new file mode 100644 index 0000000..0cde5ed --- /dev/null +++ b/Xlib/ext/damage.py @@ -0,0 +1,182 @@ +# Xlib.ext.damage -- DAMAGE extension module +# +# Copyright (C) 2018 Joseph Kogut +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + + +from Xlib import X +from Xlib.protocol import rq, structs +from Xlib.xobject import resource +from Xlib.error import XError + +extname = 'DAMAGE' + +# Event codes # +DamageNotifyCode = 0 + +# Error codes # +BadDamageCode = 0 + +class BadDamageError(XError): + pass + +# DamageReportLevel options +DamageReportRawRectangles = 0 +DamageReportDeltaRectangles = 1 +DamageReportBoundingBox = 2 +DamageReportNonEmpty = 3 + +DamageReportLevel = ( + DamageReportRawRectangles, + DamageReportDeltaRectangles, + DamageReportBoundingBox, + DamageReportNonEmpty, +) + +DAMAGE = rq.Card32 + +# Methods + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + ) + + _reply = rq.Struct(rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + rq.Pad(16), + ) + +def query_version(self): + return QueryVersion(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + +class DamageCreate(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + DAMAGE('damage'), + rq.Drawable('drawable'), + rq.Set('level', 1, DamageReportLevel), + rq.Pad(3), + ) + +def damage_create(self, level): + did = self.display.allocate_resource_id() + DamageCreate(display=self.display, + opcode=self.display.get_extension_major(extname), + damage=did, + drawable=self.id, + level=level, + ) + return did + +class DamageDestroy(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + DAMAGE('damage') + ) + +def damage_destroy(self, damage): + DamageDestroy(display=self.display, + opcode=self.display.get_extension_major(extname), + damage=damage, + ) + + self.display.free_resource_id(damage) + +class DamageSubtract(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + DAMAGE('damage'), + rq.Card32('repair'), + rq.Card32('parts') + ) + +def damage_subtract(self, damage, repair=X.NONE, parts=X.NONE): + DamageSubtract(display=self.display, + opcode=self.display.get_extension_major(extname), + damage=damage, + repair=repair, + parts=parts) + +class DamageAdd(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + rq.Card32('repair'), + rq.Card32('parts'), + ) + +def damage_add(self, repair, parts): + DamageAdd(display=self.display, + opcode=self.display.get_extension_major(extname), + repair=repair, + parts=parts) + +# Events # + +class DamageNotify(rq.Event): + _code = None + _fields = rq.Struct( + rq.Card8('type'), + rq.Card8('level'), + rq.Card16('sequence_number'), + rq.Drawable('drawable'), + DAMAGE('damage'), + rq.Card32('timestamp'), + rq.Object('area', structs.Rectangle), + rq.Object('drawable_geometry', structs.Rectangle) + ) + +def init(disp, info): + disp.extension_add_method('display', + 'damage_query_version', + query_version) + + disp.extension_add_method('drawable', + 'damage_create', + damage_create) + + disp.extension_add_method('display', + 'damage_destroy', + damage_destroy) + + disp.extension_add_method('display', + 'damage_subtract', + damage_subtract) + + disp.extension_add_method('drawable', + 'damage_add', + damage_add) + + disp.extension_add_event(info.first_event + DamageNotifyCode, DamageNotify) + + disp.add_extension_error(code=BadDamageCode, err=BadDamageError) diff --git a/Xlib/ext/dpms.py b/Xlib/ext/dpms.py new file mode 100644 index 0000000..3ff9a24 --- /dev/null +++ b/Xlib/ext/dpms.py @@ -0,0 +1,233 @@ +# Xlib.ext.dpms -- X Display Power Management Signaling +# +# Copyright (C) 2020 Thiago Kenji Okada +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +''' +This extension provides X Protocol control over the VESA Display +Power Management Signaling (DPMS) characteristics of video boards +under control of the X Window System. + +Documentation: https://www.x.org/releases/X11R7.7/doc/xextproto/dpms.html +''' + +from Xlib import X +from Xlib.protocol import rq + +extname = 'DPMS' + + +# DPMS Extension Power Levels +# 0 DPMSModeOn In use +# 1 DPMSModeStandby Blanked, low power +# 2 DPMSModeSuspend Blanked, lower power +# 3 DPMSModeOff Shut off, awaiting activity +DPMSModeOn = 0 +DPMSModeStandby = 1 +DPMSModeSuspend = 2 +DPMSModeOff = 3 + +DPMSPowerLevel = ( + DPMSModeOn, + DPMSModeStandby, + DPMSModeSuspend, + DPMSModeOff, +) + + +class DPMSGetVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + rq.Pad(20), + ) + + +def get_version(self): + return DPMSGetVersion(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +class DPMSCapable(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Bool('capable'), + rq.Pad(23), + ) + + +def capable(self): + return DPMSCapable(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +class DPMSGetTimeouts(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('standby_timeout'), + rq.Card16('suspend_timeout'), + rq.Card16('off_timeout'), + rq.Pad(18), + ) + + +def get_timeouts(self): + return DPMSGetTimeouts(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +class DPMSSetTimeouts(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + rq.Card16('standby_timeout'), + rq.Card16('suspend_timeout'), + rq.Card16('off_timeout'), + rq.Pad(2) + ) + + +def set_timeouts(self, standby_timeout, suspend_timeout, off_timeout): + return DPMSSetTimeouts(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1, + standby_timeout=standby_timeout, + suspend_timeout=suspend_timeout, + off_timeout=off_timeout) + + +class DPMSEnable(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + ) + + +def enable(self): + return DPMSEnable(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +class DPMSDisable(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(5), + rq.RequestLength(), + ) + + +def disable(self): + return DPMSDisable(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +class DPMSForceLevel(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(6), + rq.RequestLength(), + rq.Resource('power_level', DPMSPowerLevel), + ) + + +def force_level(self, power_level): + return DPMSForceLevel(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1, + power_level=power_level) + + +class DPMSInfo(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(7), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('power_level'), + rq.Bool('state'), + rq.Pad(21), + ) + + +def info(self): + return DPMSInfo(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +def init(disp, _info): + disp.extension_add_method('display', 'dpms_get_version', get_version) + disp.extension_add_method('display', 'dpms_capable', capable) + disp.extension_add_method('display', 'dpms_get_timeouts', get_timeouts) + disp.extension_add_method('display', 'dpms_set_timeouts', set_timeouts) + disp.extension_add_method('display', 'dpms_enable', enable) + disp.extension_add_method('display', 'dpms_disable', disable) + disp.extension_add_method('display', 'dpms_force_level', force_level) + disp.extension_add_method('display', 'dpms_info', info) diff --git a/Xlib/ext/ge.py b/Xlib/ext/ge.py new file mode 100644 index 0000000..291ffa9 --- /dev/null +++ b/Xlib/ext/ge.py @@ -0,0 +1,112 @@ +# Xlib.ext.ge -- Generic Event extension module +# +# Copyright (C) 2012 Outpost Embedded, LLC +# Forest Bond +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +''' +ge - Generic Event Extension +''' + +from Xlib.protocol import rq + +extname = 'Generic Event Extension' + + +GenericEventCode = 35 + + +class GEQueryVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + rq.Pad(16), + ) + + +def query_version(self): + return GEQueryVersion( + display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=0, + ) + + +class GenericEvent(rq.Event): + _code = GenericEventCode + _fields = rq.Struct( + rq.Card8('type'), + rq.Card8('extension'), + rq.Card16('sequence_number'), + rq.Card32('length'), + rq.Card16('evtype'), + # Some generic events make use of this space, but with + # others the data is simply discarded. In any case we + # don't need to explicitly pad this out as we are + # always given at least 32 bytes and we save + # everything after the first ten as the "data" field. + #rq.Pad(22), + ) + + def __init__(self, binarydata = None, display = None, **keys): + if binarydata: + data = binarydata[10:] + binarydata = binarydata[:10] + else: + data = '' + + rq.Event.__init__( + self, + binarydata=binarydata, + display=display, + **keys + ) + + if display: + ge_event_data = getattr(display, 'ge_event_data', None) + if ge_event_data: + estruct = ge_event_data.get((self.extension, self.evtype), None) + if estruct: + data, _ = estruct.parse_binary(data, display) + + self._data['data'] = data + + +def add_event_data(self, extension, evtype, estruct): + if not hasattr(self.display, 'ge_event_data'): + self.display.ge_event_data = {} + self.display.ge_event_data[(extension, evtype)] = estruct + + +def init(disp, info): + disp.extension_add_method('display', 'ge_query_version', query_version) + disp.extension_add_method('display', 'ge_add_event_data', add_event_data) + disp.extension_add_event(GenericEventCode, GenericEvent) diff --git a/Xlib/ext/nvcontrol.py b/Xlib/ext/nvcontrol.py new file mode 100644 index 0000000..7a21826 --- /dev/null +++ b/Xlib/ext/nvcontrol.py @@ -0,0 +1,5393 @@ +# Xlib.ext.nvcontrol -- NV-CONTROL extension module +# +# Copyright (C) 2019 Roberto Leinardi +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + + +"""NV-CONTROL - provide access to the NV-CONTROL extension information.""" + +from Xlib.protocol import rq + +extname = 'NV-CONTROL' + + +def query_target_count(self, target): + """Return the target count""" + reply = NVCtrlQueryTargetCountReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_type=target.type()) + return int(reply._data.get('count')) + + +def query_int_attribute(self, target, display_mask, attr): + """Return the value of an integer attribute""" + reply = NVCtrlQueryAttributeReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_id=target.id(), + target_type=target.type(), + display_mask=display_mask, + attr=attr) + if not reply._data.get('flags'): + return None + return int(reply._data.get('value')) + + +def set_int_attribute(self, target, display_mask, attr, value): + """Set the value of an integer attribute""" + reply = NVCtrlSetAttributeAndGetStatusReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_id=target.id(), + target_type=target.type(), + display_mask=display_mask, + attr=attr, + value=value) + return reply._data.get('flags') != 0 + + +def query_string_attribute(self, target, display_mask, attr): + """Return the value of a string attribute""" + reply = NVCtrlQueryStringAttributeReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_id=target.id(), + target_type=target.type(), + display_mask=display_mask, + attr=attr) + if not reply._data.get('flags'): + return None + return str(reply._data.get('string')).strip('\0') + + +def query_valid_attr_values(self, target, display_mask, attr): + """Return the value of an integer attribute""" + reply = NVCtrlQueryValidAttributeValuesReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_id=target.id(), + target_type=target.type(), + display_mask=display_mask, + attr=attr) + if not reply._data.get('flags'): + return None + return int(reply._data.get('min')), int(reply._data.get('max')) + + +def query_binary_data(self, target, display_mask, attr): + """Return binary data""" + reply = NVCtrlQueryBinaryDataReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_id=target.id(), + target_type=target.type(), + display_mask=display_mask, + attr=attr) + if not reply._data.get('flags'): + return None + return reply._data.get('data') + + +def get_coolers_used_by_gpu(self, target): + reply = NVCtrlQueryListCard32ReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_id=target.id(), + target_type=target.type(), + display_mask=0, + attr=NV_CTRL_BINARY_DATA_COOLERS_USED_BY_GPU) + if not reply._data.get('flags'): + return None + fans = reply._data.get('list') + if len(fans) > 1: + return fans[1:] + else: + return None + + +def get_gpu_count(self): + """Return the number of GPU's present in the system.""" + return int(query_target_count(self, Gpu())) + + +def get_name(self, target): + """Return the GPU product name on which the specified X screen is running""" + return query_string_attribute(self, target, 0, NV_CTRL_STRING_PRODUCT_NAME) + + +def get_driver_version(self, target): + """Return the NVIDIA (kernel level) driver version for the specified screen or GPU""" + return query_string_attribute(self, target, 0, NV_CTRL_STRING_NVIDIA_DRIVER_VERSION) + + +def get_vbios_version(self, target): + """Return the version of the VBIOS for the specified screen or GPU""" + return query_string_attribute(self, target, 0, NV_CTRL_STRING_VBIOS_VERSION) + + +def get_gpu_uuid(self, target): + return query_string_attribute(self, target, 0, NV_CTRL_STRING_GPU_UUID) + + +def get_utilization_rates(self, target): + string = query_string_attribute(self, target, 0, NV_CTRL_STRING_GPU_UTILIZATION) + result = {} + if string is not None and string != '': + for line in string.split(','): + [key, value] = line.split('=')[:2] + result[key.strip()] = int(value) if value.isdigit() else value + return result + + +def get_performance_modes(self, target): + string = query_string_attribute(self, target, 0, NV_CTRL_STRING_PERFORMANCE_MODES) + result = [] + if string is not None and string != '': + for perf in string.split(';'): + perf_dict = {} + for line in perf.split(','): + [key, value] = line.split('=')[:2] + perf_dict[key.strip()] = int(value) if value.isdigit() else value + result.append(perf_dict) + return result + + +def get_clock_info(self, target): + string = query_string_attribute(self, target, 0, NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS) + result = {} + if string is not None and string != '': + for line in string.split(','): + [key, value] = line.split('=')[:2] + result[key.strip()] = int(value) if value.isdigit() else value + return result + + +def get_vram(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_VIDEO_RAM) + + +def get_irq(self, target): + """Return the interrupt request line used by the GPU driving the screen""" + return query_int_attribute(self, target, 0, NV_CTRL_IRQ) + + +def supports_framelock(self, target): + """Return whether the underlying GPU supports Frame Lock. + + All of the other frame lock attributes are only applicable if this returns True. + """ + return query_int_attribute(self, target, 0, NV_CTRL_FRAMELOCK) == 1 + + +def gvo_supported(self, screen): + """Return whether this X screen supports GVO + + If this screen does not support GVO output, then all other GVO attributes are unavailable. + """ + return query_int_attribute(self, screen, [], NV_CTRL_GVO_SUPPORTED) + + +def get_core_temp(self, target): + """Return the current core temperature of the GPU driving the X screen.""" + return query_int_attribute(self, target, 0, NV_CTRL_GPU_CORE_TEMPERATURE) + + +def get_core_threshold(self, target): + """Return the current GPU core slowdown threshold temperature. + + It reflects the temperature at which the GPU is throttled to prevent overheating. + """ + return query_int_attribute(self, target, 0, NV_CTRL_GPU_CORE_THRESHOLD) + + +def get_default_core_threshold(self, target): + """Return the default core threshold temperature.""" + return query_int_attribute(self, target, 0, NV_CTRL_GPU_DEFAULT_CORE_THRESHOLD) + + +def get_max_core_threshold(self, target): + """Return the maximum core threshold temperature.""" + return query_int_attribute(self, target, 0, NV_CTRL_GPU_MAX_CORE_THRESHOLD) + + +def get_ambient_temp(self, target): + """Return the current temperature in the immediate neighbourhood of the GPU driving the X screen.""" + return query_int_attribute(self, target, 0, NV_CTRL_AMBIENT_TEMPERATURE) + + +def get_cuda_cores(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_CORES) + + +def get_memory_bus_width(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_MEMORY_BUS_WIDTH) + + +def get_total_dedicated_gpu_memory(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY) + + +def get_used_dedicated_gpu_memory(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_USED_DEDICATED_GPU_MEMORY) + + +def get_curr_pcie_link_width(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_PCIE_CURRENT_LINK_WIDTH) + + +def get_max_pcie_link_width(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_PCIE_MAX_LINK_WIDTH) + + +def get_curr_pcie_link_generation(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_PCIE_GENERATION) + + +def get_encoder_utilization(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_VIDEO_ENCODER_UTILIZATION) + + +def get_decoder_utilization(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_VIDEO_DECODER_UTILIZATION) + + +def get_current_performance_level(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL) + + +def get_gpu_nvclock_offset(self, target, perf_level): + return query_int_attribute(self, target, perf_level, NV_CTRL_GPU_NVCLOCK_OFFSET) + + +def set_gpu_nvclock_offset(self, target, perf_level, offset): + return set_int_attribute(self, target, perf_level, NV_CTRL_GPU_NVCLOCK_OFFSET, offset) + + +def set_gpu_nvclock_offset_all_levels(self, target, offset): + return set_int_attribute(self, target, 0, NV_CTRL_GPU_NVCLOCK_OFFSET_ALL_PERFORMANCE_LEVELS, offset) + + +def get_gpu_nvclock_offset_range(self, target, perf_level): + return query_valid_attr_values(self, target, perf_level, NV_CTRL_GPU_NVCLOCK_OFFSET) + + +def get_mem_transfer_rate_offset(self, target, perf_level): + return query_int_attribute(self, target, perf_level, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET) + + +def set_mem_transfer_rate_offset(self, target, perf_level, offset): + return set_int_attribute(self, target, perf_level, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET, offset) + + +def set_mem_transfer_rate_offset_all_levels(self, target, offset): + return set_int_attribute(self, target, 0, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET_ALL_PERFORMANCE_LEVELS, offset) + + +def get_mem_transfer_rate_offset_range(self, target, perf_level): + return query_valid_attr_values(self, target, perf_level, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET) + + +def get_cooler_manual_control_enabled(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_COOLER_MANUAL_CONTROL) + + +def set_cooler_manual_control_enabled(self, target, enabled): + return set_int_attribute(self, target, 0, NV_CTRL_GPU_COOLER_MANUAL_CONTROL, 1 if enabled else 0) == 1 + + +def get_fan_duty(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL) + + +def set_fan_duty(self, cooler, speed): + return set_int_attribute(self, cooler, 0, NV_CTRL_THERMAL_COOLER_LEVEL, speed) + + +def get_fan_rpm(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_THERMAL_COOLER_SPEED) + + +def get_max_displays(self, target): + """Return the maximum number of display devices that can be driven simultaneously on a GPU. + + Note that this does not indicate the maximum number of bits that can be set in + NV_CTRL_CONNECTED_DISPLAYS, because more display devices can be connected than are actively + in use. + """ + return query_int_attribute(self, target, 0, NV_CTRL_MAX_DISPLAYS) + + +def _displaystr2num(st): + """Return a display number from a string""" + num = None + for s, n in [('DFP-', 16), ('TV-', 8), ('CRT-', 0)]: + if st.startswith(s): + try: + curnum = int(st[len(s):]) + if 0 <= curnum <= 7: + num = n + curnum + break + except Exception: + pass + if num is not None: + return num + else: + raise ValueError('Unrecognised display name: ' + st) + + +def _displays2mask(displays): + """Return a display mask from an array of display numbers.""" + mask = 0 + for d in displays: + mask += (1 << _displaystr2num(d)) + return mask + + +def init(disp, info): + disp.extension_add_method('display', 'nvcontrol_query_target_count', query_target_count) + disp.extension_add_method('display', 'nvcontrol_query_int_attribute', query_int_attribute) + disp.extension_add_method('display', 'nvcontrol_query_string_attribute', query_string_attribute) + disp.extension_add_method('display', 'nvcontrol_query_valid_attr_values', query_valid_attr_values) + disp.extension_add_method('display', 'nvcontrol_query_binary_data', query_binary_data) + disp.extension_add_method('display', 'nvcontrol_get_gpu_count', get_gpu_count) + disp.extension_add_method('display', 'nvcontrol_get_vram', get_vram) + disp.extension_add_method('display', 'nvcontrol_get_irq', get_irq) + disp.extension_add_method('display', 'nvcontrol_supports_framelock', supports_framelock) + disp.extension_add_method('display', 'nvcontrol_get_core_temp', get_core_temp) + disp.extension_add_method('display', 'nvcontrol_get_core_threshold', get_core_threshold) + disp.extension_add_method('display', 'nvcontrol_get_default_core_threshold', get_default_core_threshold) + disp.extension_add_method('display', 'nvcontrol_get_max_core_threshold', get_max_core_threshold) + disp.extension_add_method('display', 'nvcontrol_get_ambient_temp', get_ambient_temp) + disp.extension_add_method('display', 'nvcontrol_get_cuda_cores', get_cuda_cores) + disp.extension_add_method('display', 'nvcontrol_get_memory_bus_width', get_memory_bus_width) + disp.extension_add_method('display', 'nvcontrol_get_total_dedicated_gpu_memory', get_total_dedicated_gpu_memory) + disp.extension_add_method('display', 'nvcontrol_get_used_dedicated_gpu_memory', get_used_dedicated_gpu_memory) + disp.extension_add_method('display', 'nvcontrol_get_curr_pcie_link_width', get_curr_pcie_link_width) + disp.extension_add_method('display', 'nvcontrol_get_max_pcie_link_width', get_max_pcie_link_width) + disp.extension_add_method('display', 'nvcontrol_get_curr_pcie_link_generation', get_curr_pcie_link_generation) + disp.extension_add_method('display', 'nvcontrol_get_encoder_utilization', get_encoder_utilization) + disp.extension_add_method('display', 'nvcontrol_get_decoder_utilization', get_decoder_utilization) + disp.extension_add_method('display', 'nvcontrol_get_current_performance_level', get_current_performance_level) + disp.extension_add_method('display', 'nvcontrol_get_gpu_nvclock_offset', get_gpu_nvclock_offset) + disp.extension_add_method('display', 'nvcontrol_set_gpu_nvclock_offset', set_gpu_nvclock_offset) + disp.extension_add_method('display', 'nvcontrol_set_gpu_nvclock_offset_all_levels', set_gpu_nvclock_offset_all_levels) + disp.extension_add_method('display', 'nvcontrol_get_mem_transfer_rate_offset', get_mem_transfer_rate_offset) + disp.extension_add_method('display', 'nvcontrol_set_mem_transfer_rate_offset', set_mem_transfer_rate_offset) + disp.extension_add_method('display', 'nvcontrol_set_mem_transfer_rate_offset_all_levels', set_mem_transfer_rate_offset_all_levels) + disp.extension_add_method('display', 'nvcontrol_get_cooler_manual_control_enabled', + get_cooler_manual_control_enabled) + disp.extension_add_method('display', 'nvcontrol_get_fan_duty', get_fan_duty) + disp.extension_add_method('display', 'nvcontrol_set_fan_duty', set_fan_duty) + disp.extension_add_method('display', 'nvcontrol_get_fan_rpm', get_fan_rpm) + disp.extension_add_method('display', 'nvcontrol_get_coolers_used_by_gpu', get_coolers_used_by_gpu) + disp.extension_add_method('display', 'nvcontrol_get_max_displays', get_max_displays) + disp.extension_add_method('display', 'nvcontrol_get_name', get_name) + disp.extension_add_method('display', 'nvcontrol_get_driver_version', get_driver_version) + disp.extension_add_method('display', 'nvcontrol_get_vbios_version', get_vbios_version) + disp.extension_add_method('display', 'nvcontrol_get_gpu_uuid', get_gpu_uuid) + disp.extension_add_method('display', 'nvcontrol_get_utilization_rates', get_utilization_rates) + disp.extension_add_method('display', 'nvcontrol_get_performance_modes', get_performance_modes) + disp.extension_add_method('display', 'nvcontrol_get_clock_info', get_clock_info) + disp.extension_add_method('display', 'nvcontrol_set_cooler_manual_control_enabled', + set_cooler_manual_control_enabled) + disp.extension_add_method('display', 'nvcontrol_get_gpu_nvclock_offset_range', + get_gpu_nvclock_offset_range) + disp.extension_add_method('display', 'nvcontrol_get_mem_transfer_rate_offset_range', + get_mem_transfer_rate_offset_range) + + +############################################################################ +# +# Attributes +# +# Some attributes may only be read; some may require a display_mask +# argument and others may be valid only for specific target types. +# This information is encoded in the "permission" comment after each +# attribute #define, and can be queried at run time with +# XNVCTRLQueryValidAttributeValues() and/or +# XNVCTRLQueryValidTargetAttributeValues() +# +# Key to Integer Attribute "Permissions": +# +# R: The attribute is readable (in general, all attributes will be +# readable) +# +# W: The attribute is writable (attributes may not be writable for +# various reasons: they represent static system information, they +# can only be changed by changing an XF86Config option, etc). +# +# D: The attribute requires the display mask argument. The +# attributes NV_CTRL_CONNECTED_DISPLAYS and NV_CTRL_ENABLED_DISPLAYS +# will be a bitmask of what display devices are connected and what +# display devices are enabled for use in X, respectively. Each bit +# in the bitmask represents a display device; it is these bits which +# should be used as the display_mask when dealing with attributes +# designated with "D" below. For attributes that do not require the +# display mask, the argument is ignored. +# +# Alternatively, NV-CONTROL versions 1.27 and greater allow these +# attributes to be accessed via display target types, in which case +# the display_mask is ignored. +# +# G: The attribute may be queried using an NV_CTRL_TARGET_TYPE_GPU +# target type via XNVCTRLQueryTargetAttribute(). +# +# F: The attribute may be queried using an NV_CTRL_TARGET_TYPE_FRAMELOCK +# target type via XNVCTRLQueryTargetAttribute(). +# +# X: When Xinerama is enabled, this attribute is kept consistent across +# all Physical X Screens; assignment of this attribute will be +# broadcast by the NVIDIA X Driver to all X Screens. +# +# V: The attribute may be queried using an NV_CTRL_TARGET_TYPE_VCSC +# target type via XNVCTRLQueryTargetAttribute(). +# +# I: The attribute may be queried using an NV_CTRL_TARGET_TYPE_GVI target type +# via XNVCTRLQueryTargetAttribute(). +# +# Q: The attribute is a 64-bit integer attribute; use the 64-bit versions +# of the appropriate query interfaces. +# +# C: The attribute may be queried using an NV_CTRL_TARGET_TYPE_COOLER target +# type via XNVCTRLQueryTargetAttribute(). +# +# S: The attribute may be queried using an NV_CTRL_TARGET_TYPE_THERMAL_SENSOR +# target type via XNVCTRLQueryTargetAttribute(). +# +# T: The attribute may be queried using an +# NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER target type +# via XNVCTRLQueryTargetAttribute(). +# +# NOTE: Unless mentioned otherwise, all attributes may be queried using +# an NV_CTRL_TARGET_TYPE_X_SCREEN target type via +# XNVCTRLQueryTargetAttribute(). +# + + +############################################################################ + +# +# Integer attributes: +# +# Integer attributes can be queried through the XNVCTRLQueryAttribute() and +# XNVCTRLQueryTargetAttribute() function calls. +# +# Integer attributes can be set through the XNVCTRLSetAttribute() and +# XNVCTRLSetTargetAttribute() function calls. +# +# Unless otherwise noted, all integer attributes can be queried/set +# using an NV_CTRL_TARGET_TYPE_X_SCREEN target. Attributes that cannot +# take an NV_CTRL_TARGET_TYPE_X_SCREEN also cannot be queried/set through +# XNVCTRLQueryAttribute()/XNVCTRLSetAttribute() (Since these assume +# an X Screen target). +# + + +# +# NV_CTRL_FLATPANEL_SCALING - not supported +# + +NV_CTRL_FLATPANEL_SCALING = 2 # not supported +NV_CTRL_FLATPANEL_SCALING_DEFAULT = 0 # not supported +NV_CTRL_FLATPANEL_SCALING_NATIVE = 1 # not supported +NV_CTRL_FLATPANEL_SCALING_SCALED = 2 # not supported +NV_CTRL_FLATPANEL_SCALING_CENTERED = 3 # not supported +NV_CTRL_FLATPANEL_SCALING_ASPECT_SCALED = 4 # not supported + +# +# NV_CTRL_FLATPANEL_DITHERING - not supported +# +# NV_CTRL_DITHERING should be used instead. +# + +NV_CTRL_FLATPANEL_DITHERING = 3 # not supported +NV_CTRL_FLATPANEL_DITHERING_DEFAULT = 0 # not supported +NV_CTRL_FLATPANEL_DITHERING_ENABLED = 1 # not supported +NV_CTRL_FLATPANEL_DITHERING_DISABLED = 2 # not supported + +# +# NV_CTRL_DITHERING - the requested dithering configuration; +# possible values are: +# +# 0: auto (the driver will decide when to dither) +# 1: enabled (the driver will always dither when possible) +# 2: disabled (the driver will never dither) +# + +NV_CTRL_DITHERING = 3 # RWDG +NV_CTRL_DITHERING_AUTO = 0 +NV_CTRL_DITHERING_ENABLED = 1 +NV_CTRL_DITHERING_DISABLED = 2 + +# +# NV_CTRL_DIGITAL_VIBRANCE - sets the digital vibrance level for the +# specified display device. +# + +NV_CTRL_DIGITAL_VIBRANCE = 4 # RWDG + +# +# NV_CTRL_BUS_TYPE - returns the bus type through which the specified device +# is connected to the computer. +# When this attribute is queried on an X screen target, the bus type of the +# GPU driving the X screen is returned. +# + +NV_CTRL_BUS_TYPE = 5 # R--GI +NV_CTRL_BUS_TYPE_AGP = 0 +NV_CTRL_BUS_TYPE_PCI = 1 +NV_CTRL_BUS_TYPE_PCI_EXPRESS = 2 +NV_CTRL_BUS_TYPE_INTEGRATED = 3 + +# +# NV_CTRL_TOTAL_GPU_MEMORY - returns the total amount of memory available +# to the specified GPU (or the GPU driving the specified X +# screen). Note: if the GPU supports TurboCache(TM), the value +# reported may exceed the amount of video memory installed on the +# GPU. The value reported for integrated GPUs may likewise exceed +# the amount of dedicated system memory set aside by the system +# BIOS for use by the integrated GPU. +# + +NV_CTRL_TOTAL_GPU_MEMORY = 6 # R--G +NV_CTRL_VIDEO_RAM = NV_CTRL_TOTAL_GPU_MEMORY + +# +# NV_CTRL_IRQ - returns the interrupt request line used by the specified +# device. +# When this attribute is queried on an X screen target, the IRQ of the GPU +# driving the X screen is returned. +# + +NV_CTRL_IRQ = 7 # R--GI + +# +# NV_CTRL_OPERATING_SYSTEM - returns the operating system on which +# the X server is running. +# + +NV_CTRL_OPERATING_SYSTEM = 8 # R--G +NV_CTRL_OPERATING_SYSTEM_LINUX = 0 +NV_CTRL_OPERATING_SYSTEM_FREEBSD = 1 +NV_CTRL_OPERATING_SYSTEM_SUNOS = 2 + +# +# NV_CTRL_SYNC_TO_VBLANK - enables sync to vblank for OpenGL clients. +# This setting is only applied to OpenGL clients that are started +# after this setting is applied. +# + +NV_CTRL_SYNC_TO_VBLANK = 9 # RW-X +NV_CTRL_SYNC_TO_VBLANK_OFF = 0 +NV_CTRL_SYNC_TO_VBLANK_ON = 1 + +# +# NV_CTRL_LOG_ANISO - enables anisotropic filtering for OpenGL +# clients; on some NVIDIA hardware, this can only be enabled or +# disabled; on other hardware different levels of anisotropic +# filtering can be specified. This setting is only applied to OpenGL +# clients that are started after this setting is applied. +# + +NV_CTRL_LOG_ANISO = 10 # RW-X + +# +# NV_CTRL_FSAA_MODE - the FSAA setting for OpenGL clients; possible +# FSAA modes: +# +# NV_CTRL_FSAA_MODE_2x "2x Bilinear Multisampling" +# NV_CTRL_FSAA_MODE_2x_5t "2x Quincunx Multisampling" +# NV_CTRL_FSAA_MODE_15x15 "1.5 x 1.5 Supersampling" +# NV_CTRL_FSAA_MODE_2x2 "2 x 2 Supersampling" +# NV_CTRL_FSAA_MODE_4x "4x Bilinear Multisampling" +# NV_CTRL_FSAA_MODE_4x_9t "4x Gaussian Multisampling" +# NV_CTRL_FSAA_MODE_8x "2x Bilinear Multisampling by 4x Supersampling" +# NV_CTRL_FSAA_MODE_16x "4x Bilinear Multisampling by 4x Supersampling" +# NV_CTRL_FSAA_MODE_8xS "4x Multisampling by 2x Supersampling" +# +# This setting is only applied to OpenGL clients that are started +# after this setting is applied. +# + +NV_CTRL_FSAA_MODE = 11 # RW-X +NV_CTRL_FSAA_MODE_NONE = 0 +NV_CTRL_FSAA_MODE_2x = 1 +NV_CTRL_FSAA_MODE_2x_5t = 2 +NV_CTRL_FSAA_MODE_15x15 = 3 +NV_CTRL_FSAA_MODE_2x2 = 4 +NV_CTRL_FSAA_MODE_4x = 5 +NV_CTRL_FSAA_MODE_4x_9t = 6 +NV_CTRL_FSAA_MODE_8x = 7 +NV_CTRL_FSAA_MODE_16x = 8 +NV_CTRL_FSAA_MODE_8xS = 9 +NV_CTRL_FSAA_MODE_8xQ = 10 +NV_CTRL_FSAA_MODE_16xS = 11 +NV_CTRL_FSAA_MODE_16xQ = 12 +NV_CTRL_FSAA_MODE_32xS = 13 +NV_CTRL_FSAA_MODE_32x = 14 +NV_CTRL_FSAA_MODE_64xS = 15 +NV_CTRL_FSAA_MODE_MAX = NV_CTRL_FSAA_MODE_64xS + +# +# NV_CTRL_UBB - returns whether UBB is enabled for the specified X +# screen. +# + +NV_CTRL_UBB = 13 # R-- +NV_CTRL_UBB_OFF = 0 +NV_CTRL_UBB_ON = 1 + +# +# NV_CTRL_OVERLAY - returns whether the RGB overlay is enabled for +# the specified X screen. +# + +NV_CTRL_OVERLAY = 14 # R-- +NV_CTRL_OVERLAY_OFF = 0 +NV_CTRL_OVERLAY_ON = 1 + +# +# NV_CTRL_STEREO - returns whether stereo (and what type) is enabled +# for the specified X screen. +# + +NV_CTRL_STEREO = 16 # R-- +NV_CTRL_STEREO_OFF = 0 +NV_CTRL_STEREO_DDC = 1 +NV_CTRL_STEREO_BLUELINE = 2 +NV_CTRL_STEREO_DIN = 3 +NV_CTRL_STEREO_PASSIVE_EYE_PER_DPY = 4 +NV_CTRL_STEREO_VERTICAL_INTERLACED = 5 +NV_CTRL_STEREO_COLOR_INTERLACED = 6 +NV_CTRL_STEREO_HORIZONTAL_INTERLACED = 7 +NV_CTRL_STEREO_CHECKERBOARD_PATTERN = 8 +NV_CTRL_STEREO_INVERSE_CHECKERBOARD_PATTERN = 9 +NV_CTRL_STEREO_3D_VISION = 10 +NV_CTRL_STEREO_3D_VISION_PRO = 11 +NV_CTRL_STEREO_HDMI_3D = 12 +NV_CTRL_STEREO_TRIDELITY_SL = 13 +NV_CTRL_STEREO_INBAND_STEREO_SIGNALING = 14 +NV_CTRL_STEREO_MAX = NV_CTRL_STEREO_INBAND_STEREO_SIGNALING + +# +# NV_CTRL_EMULATE - not supported +# + +NV_CTRL_EMULATE = 17 # not supported +NV_CTRL_EMULATE_NONE = 0 # not supported + +# +# NV_CTRL_TWINVIEW - returns whether TwinView is enabled for the +# specified X screen. +# + +NV_CTRL_TWINVIEW = 18 # R-- +NV_CTRL_TWINVIEW_NOT_ENABLED = 0 +NV_CTRL_TWINVIEW_ENABLED = 1 + +# +# NV_CTRL_CONNECTED_DISPLAYS - deprecated +# +# NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU and +# NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN should be used instead. +# + +NV_CTRL_CONNECTED_DISPLAYS = 19 # deprecated + +# +# NV_CTRL_ENABLED_DISPLAYS - Event that notifies when one or more display +# devices are enabled or disabled on a GPU and/or X screen. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# +# Note: Querying this value has been deprecated. +# NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU, +# NV_CTRL_DISPLAY_ENABLED, and +# NV_CTRL_BINARY_DATA_DISPLAYS_ENABLED_ON_XSCREEN should be used +# instead to obtain the list of enabled displays. +# + +NV_CTRL_ENABLED_DISPLAYS = 20 # ---G + +############################################################################ +# +# Integer attributes specific to configuring Frame Lock on boards that +# support it. +# + + +# +# NV_CTRL_FRAMELOCK - returns whether the underlying GPU supports +# Frame Lock. All of the other frame lock attributes are only +# applicable if NV_CTRL_FRAMELOCK is _SUPPORTED. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_FRAMELOCK = 21 # R--G +NV_CTRL_FRAMELOCK_NOT_SUPPORTED = 0 +NV_CTRL_FRAMELOCK_SUPPORTED = 1 + +# +# NV_CTRL_FRAMELOCK_MASTER - deprecated +# +# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG should be used instead. +# + +NV_CTRL_FRAMELOCK_MASTER = 22 # deprecated +NV_CTRL_FRAMELOCK_MASTER_FALSE = 0 # deprecated +NV_CTRL_FRAMELOCK_MASTER_TRUE = 1 # deprecated + +# +# NV_CTRL_FRAMELOCK_POLARITY - sync either to the rising edge of the +# frame lock pulse, the falling edge of the frame lock pulse or both. +# +# On Quadro Sync II, this attribute is ignored when +# NV_CTRL_USE_HOUSE_SYNC is OUTPUT. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_POLARITY = 23 # RW-F +NV_CTRL_FRAMELOCK_POLARITY_RISING_EDGE = 0x1 +NV_CTRL_FRAMELOCK_POLARITY_FALLING_EDGE = 0x2 +NV_CTRL_FRAMELOCK_POLARITY_BOTH_EDGES = 0x3 + +# +# NV_CTRL_FRAMELOCK_SYNC_DELAY - delay between the frame lock pulse +# and the GPU sync. This value must be multiplied by +# NV_CTRL_FRAMELOCK_SYNC_DELAY_RESOLUTION to determine the sync delay in +# nanoseconds. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# +# USAGE NOTE: NV_CTRL_FRAMELOCK_SYNC_DELAY_MAX and +# NV_CTRL_FRAMELOCK_SYNC_DELAY_FACTOR are deprecated. +# The Sync Delay _MAX and _FACTOR are different for different +# Quadro Sync products and so, to be correct, the valid values for +# NV_CTRL_FRAMELOCK_SYNC_DELAY must be queried to get the range +# of acceptable sync delay values, and +# NV_CTRL_FRAMELOCK_SYNC_DELAY_RESOLUTION must be queried to +# obtain the correct factor. +# + +NV_CTRL_FRAMELOCK_SYNC_DELAY = 24 # RW-F +NV_CTRL_FRAMELOCK_SYNC_DELAY_MAX = 2047 # deprecated +NV_CTRL_FRAMELOCK_SYNC_DELAY_FACTOR = 7.81 # deprecated + +# +# NV_CTRL_FRAMELOCK_SYNC_INTERVAL - how many house sync pulses +# between the frame lock sync generation (0 == sync every house sync); +# this only applies to the master when receiving house sync. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_SYNC_INTERVAL = 25 # RW-F + +# +# NV_CTRL_FRAMELOCK_PORT0_STATUS - status of the rj45 port0. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_PORT0_STATUS = 26 # R--F +NV_CTRL_FRAMELOCK_PORT0_STATUS_INPUT = 0 +NV_CTRL_FRAMELOCK_PORT0_STATUS_OUTPUT = 1 + +# +# NV_CTRL_FRAMELOCK_PORT1_STATUS - status of the rj45 port1. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_PORT1_STATUS = 27 # R--F +NV_CTRL_FRAMELOCK_PORT1_STATUS_INPUT = 0 +NV_CTRL_FRAMELOCK_PORT1_STATUS_OUTPUT = 1 + +# +# NV_CTRL_FRAMELOCK_HOUSE_STATUS - returns whether or not the house +# sync input signal was detected on the BNC connector of the frame lock +# board. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_HOUSE_STATUS = 28 # R--F +NV_CTRL_FRAMELOCK_HOUSE_STATUS_NOT_DETECTED = 0 +NV_CTRL_FRAMELOCK_HOUSE_STATUS_DETECTED = 1 + +# +# NV_CTRL_FRAMELOCK_SYNC - enable/disable the syncing of display +# devices to the frame lock pulse as specified by previous calls to +# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG. +# +# This attribute can only be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN. +# + +NV_CTRL_FRAMELOCK_SYNC = 29 # RW-G +NV_CTRL_FRAMELOCK_SYNC_DISABLE = 0 +NV_CTRL_FRAMELOCK_SYNC_ENABLE = 1 + +# +# NV_CTRL_FRAMELOCK_SYNC_READY - reports whether a frame lock +# board is receiving sync (regardless of whether or not any display +# devices are using the sync). +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_SYNC_READY = 30 # R--F +NV_CTRL_FRAMELOCK_SYNC_READY_FALSE = 0 +NV_CTRL_FRAMELOCK_SYNC_READY_TRUE = 1 + +# +# NV_CTRL_FRAMELOCK_STEREO_SYNC - this indicates that the GPU stereo +# signal is in sync with the frame lock stereo signal. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_STEREO_SYNC = 31 # R--G +NV_CTRL_FRAMELOCK_STEREO_SYNC_FALSE = 0 +NV_CTRL_FRAMELOCK_STEREO_SYNC_TRUE = 1 + +# +# NV_CTRL_FRAMELOCK_TEST_SIGNAL - to test the connections in the sync +# group, tell the master to enable a test signal, then query port[01] +# status and sync_ready on all slaves. When done, tell the master to +# disable the test signal. Test signal should only be manipulated +# while NV_CTRL_FRAMELOCK_SYNC is enabled. +# +# The TEST_SIGNAL is also used to reset the Universal Frame Count (as +# returned by the glXQueryFrameCountNV() function in the +# GLX_NV_swap_group extension). Note: for best accuracy of the +# Universal Frame Count, it is recommended to toggle the TEST_SIGNAL +# on and off after enabling frame lock. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_FRAMELOCK_TEST_SIGNAL = 32 # RW-G +NV_CTRL_FRAMELOCK_TEST_SIGNAL_DISABLE = 0 +NV_CTRL_FRAMELOCK_TEST_SIGNAL_ENABLE = 1 + +# +# NV_CTRL_FRAMELOCK_ETHERNET_DETECTED - The frame lock boards are +# cabled together using regular cat5 cable, connecting to rj45 ports +# on the backplane of the card. There is some concern that users may +# think these are ethernet ports and connect them to a +# router/hub/etc. The hardware can detect this and will shut off to +# prevent damage (either to itself or to the router). +# NV_CTRL_FRAMELOCK_ETHERNET_DETECTED may be called to find out if +# ethernet is connected to one of the rj45 ports. An appropriate +# error message should then be displayed. The _PORT0 and _PORT1 +# values may be or'ed together. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_ETHERNET_DETECTED = 33 # R--F +NV_CTRL_FRAMELOCK_ETHERNET_DETECTED_NONE = 0 +NV_CTRL_FRAMELOCK_ETHERNET_DETECTED_PORT0 = 0x1 +NV_CTRL_FRAMELOCK_ETHERNET_DETECTED_PORT1 = 0x2 + +# +# NV_CTRL_FRAMELOCK_VIDEO_MODE - get/set what video mode is used +# to interperate the house sync signal. This should only be set +# on the master. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_VIDEO_MODE = 34 # RW-F +NV_CTRL_FRAMELOCK_VIDEO_MODE_NONE = 0 +NV_CTRL_FRAMELOCK_VIDEO_MODE_TTL = 1 +NV_CTRL_FRAMELOCK_VIDEO_MODE_NTSCPALSECAM = 2 +NV_CTRL_FRAMELOCK_VIDEO_MODE_HDTV = 3 + +# +# During FRAMELOCK bring-up, the above values were redefined to +# these: +# + +NV_CTRL_FRAMELOCK_VIDEO_MODE_COMPOSITE_AUTO = 0 +NV_CTRL_FRAMELOCK_VIDEO_MODE_COMPOSITE_BI_LEVEL = 2 +NV_CTRL_FRAMELOCK_VIDEO_MODE_COMPOSITE_TRI_LEVEL = 3 + +# +# NV_CTRL_FRAMELOCK_SYNC_RATE - this is the refresh rate that the +# frame lock board is sending to the GPU, in milliHz. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_SYNC_RATE = 35 # R--F + +############################################################################ + +# +# NV_CTRL_FORCE_GENERIC_CPU - not supported +# + +NV_CTRL_FORCE_GENERIC_CPU = 37 # not supported +NV_CTRL_FORCE_GENERIC_CPU_DISABLE = 0 # not supported +NV_CTRL_FORCE_GENERIC_CPU_ENABLE = 1 # not supported + +# +# NV_CTRL_OPENGL_AA_LINE_GAMMA - for OpenGL clients, allow +# Gamma-corrected antialiased lines to consider variances in the +# color display capabilities of output devices when rendering smooth +# lines. Only available on recent Quadro GPUs. This setting is only +# applied to OpenGL clients that are started after this setting is +# applied. +# + +NV_CTRL_OPENGL_AA_LINE_GAMMA = 38 # RW-X +NV_CTRL_OPENGL_AA_LINE_GAMMA_DISABLE = 0 +NV_CTRL_OPENGL_AA_LINE_GAMMA_ENABLE = 1 + +# +# NV_CTRL_FRAMELOCK_TIMING - this is TRUE when the gpu is both receiving +# and locked to an input timing signal. Timing information may come from +# the following places: Another frame lock device that is set to master, +# the house sync signal, or the GPU's internal timing from a display +# device. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_FRAMELOCK_TIMING = 39 # R--G +NV_CTRL_FRAMELOCK_TIMING_FALSE = 0 +NV_CTRL_FRAMELOCK_TIMING_TRUE = 1 + +# +# NV_CTRL_FLIPPING_ALLOWED - when TRUE, OpenGL will swap by flipping +# when possible; when FALSE, OpenGL will always swap by blitting. +# + +NV_CTRL_FLIPPING_ALLOWED = 40 # RW-X +NV_CTRL_FLIPPING_ALLOWED_FALSE = 0 +NV_CTRL_FLIPPING_ALLOWED_TRUE = 1 + +# +# NV_CTRL_ARCHITECTURE - returns the architecture on which the X server is +# running. +# + +NV_CTRL_ARCHITECTURE = 41 # R-- +NV_CTRL_ARCHITECTURE_X86 = 0 +NV_CTRL_ARCHITECTURE_X86_64 = 1 +NV_CTRL_ARCHITECTURE_IA64 = 2 +NV_CTRL_ARCHITECTURE_ARM = 3 +NV_CTRL_ARCHITECTURE_AARCH64 = 4 +NV_CTRL_ARCHITECTURE_PPC64LE = 5 + +# +# NV_CTRL_TEXTURE_CLAMPING - texture clamping mode in OpenGL. By +# default, _SPEC is used, which forces OpenGL texture clamping to +# conform with the OpenGL specification. _EDGE forces NVIDIA's +# OpenGL implementation to remap GL_CLAMP to GL_CLAMP_TO_EDGE, +# which is not strictly conformant, but some applications rely on +# the non-conformant behavior. +# + +NV_CTRL_TEXTURE_CLAMPING = 42 # RW-X +NV_CTRL_TEXTURE_CLAMPING_EDGE = 0 +NV_CTRL_TEXTURE_CLAMPING_SPEC = 1 + +# +# The NV_CTRL_CURSOR_SHADOW - not supported +# +# use an ARGB cursor instead. +# + +NV_CTRL_CURSOR_SHADOW = 43 # not supported +NV_CTRL_CURSOR_SHADOW_DISABLE = 0 # not supported +NV_CTRL_CURSOR_SHADOW_ENABLE = 1 # not supported + +NV_CTRL_CURSOR_SHADOW_ALPHA = 44 # not supported +NV_CTRL_CURSOR_SHADOW_RED = 45 # not supported +NV_CTRL_CURSOR_SHADOW_GREEN = 46 # not supported +NV_CTRL_CURSOR_SHADOW_BLUE = 47 # not supported + +NV_CTRL_CURSOR_SHADOW_X_OFFSET = 48 # not supported +NV_CTRL_CURSOR_SHADOW_Y_OFFSET = 49 # not supported + +# +# When Application Control for FSAA is enabled, then what the +# application requests is used, and NV_CTRL_FSAA_MODE is ignored. If +# this is disabled, then any application setting is overridden with +# NV_CTRL_FSAA_MODE +# + +NV_CTRL_FSAA_APPLICATION_CONTROLLED = 50 # RW-X +NV_CTRL_FSAA_APPLICATION_CONTROLLED_ENABLED = 1 +NV_CTRL_FSAA_APPLICATION_CONTROLLED_DISABLED = 0 + +# +# When Application Control for LogAniso is enabled, then what the +# application requests is used, and NV_CTRL_LOG_ANISO is ignored. If +# this is disabled, then any application setting is overridden with +# NV_CTRL_LOG_ANISO +# + +NV_CTRL_LOG_ANISO_APPLICATION_CONTROLLED = 51 # RW-X +NV_CTRL_LOG_ANISO_APPLICATION_CONTROLLED_ENABLED = 1 +NV_CTRL_LOG_ANISO_APPLICATION_CONTROLLED_DISABLED = 0 + +# +# IMAGE_SHARPENING adjusts the sharpness of the display's image +# quality by amplifying high frequency content. Valid values will +# normally be in the range [0,32). Only available on GeForceFX or +# newer. +# + +NV_CTRL_IMAGE_SHARPENING = 52 # RWDG + +# +# NV_CTRL_TV_OVERSCAN - not supported +# + +NV_CTRL_TV_OVERSCAN = 53 # not supported + +# +# NV_CTRL_TV_FLICKER_FILTER - not supported +# + +NV_CTRL_TV_FLICKER_FILTER = 54 # not supported + +# +# NV_CTRL_TV_BRIGHTNESS - not supported +# + +NV_CTRL_TV_BRIGHTNESS = 55 # not supported + +# +# NV_CTRL_TV_HUE - not supported +# + +NV_CTRL_TV_HUE = 56 # not supported + +# +# NV_CTRL_TV_CONTRAST - not suppoerted +# + +NV_CTRL_TV_CONTRAST = 57 # not supported + +# +# NV_CTRL_TV_SATURATION - not supported +# + +NV_CTRL_TV_SATURATION = 58 # not supported + +# +# NV_CTRL_TV_RESET_SETTINGS - not supported +# + +NV_CTRL_TV_RESET_SETTINGS = 59 # not supported + +# +# NV_CTRL_GPU_CORE_TEMPERATURE reports the current core temperature +# of the GPU driving the X screen. +# + +NV_CTRL_GPU_CORE_TEMPERATURE = 60 # R--G + +# +# NV_CTRL_GPU_CORE_THRESHOLD reports the current GPU core slowdown +# threshold temperature, NV_CTRL_GPU_DEFAULT_CORE_THRESHOLD and +# NV_CTRL_GPU_MAX_CORE_THRESHOLD report the default and MAX core +# slowdown threshold temperatures. +# +# NV_CTRL_GPU_CORE_THRESHOLD reflects the temperature at which the +# GPU is throttled to prevent overheating. +# + +NV_CTRL_GPU_CORE_THRESHOLD = 61 # R--G +NV_CTRL_GPU_DEFAULT_CORE_THRESHOLD = 62 # R--G +NV_CTRL_GPU_MAX_CORE_THRESHOLD = 63 # R--G + +# +# NV_CTRL_AMBIENT_TEMPERATURE reports the current temperature in the +# immediate neighbourhood of the GPU driving the X screen. +# + +NV_CTRL_AMBIENT_TEMPERATURE = 64 # R--G + +# +# NV_CTRL_PBUFFER_SCANOUT_SUPPORTED - returns whether this X screen +# supports scanout of FP pbuffers; +# +# if this screen does not support PBUFFER_SCANOUT, then all other +# PBUFFER_SCANOUT attributes are unavailable. +# +# PBUFFER_SCANOUT is supported if and only if: +# - Twinview is configured with clone mode. The secondary screen is used to +# scanout the pbuffer. +# - The desktop is running in with 16 bits per pixel. +# +NV_CTRL_PBUFFER_SCANOUT_SUPPORTED = 65 # not supported +NV_CTRL_PBUFFER_SCANOUT_FALSE = 0 +NV_CTRL_PBUFFER_SCANOUT_TRUE = 1 + +# +# NV_CTRL_PBUFFER_SCANOUT_XID indicates the XID of the pbuffer used for +# scanout. +# +NV_CTRL_PBUFFER_SCANOUT_XID = 66 # not supported + +############################################################################ +# +# The NV_CTRL_GVO_* integer attributes are used to configure GVO +# (Graphics to Video Out). This functionality is available, for +# example, on the Quadro SDI Output card. +# +# The following is a typical usage pattern for the GVO attributes: +# +# - query NV_CTRL_GVO_SUPPORTED to determine if the X screen supports GV0. +# +# - specify NV_CTRL_GVO_SYNC_MODE (one of FREE_RUNNING, GENLOCK, or +# FRAMELOCK); if you specify GENLOCK or FRAMELOCK, you should also +# specify NV_CTRL_GVO_SYNC_SOURCE. +# +# - Use NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED and +# NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED to detect what input syncs are +# present. +# +# (If no analog sync is detected but it is known that a valid +# bi-level or tri-level sync is connected set +# NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE appropriately and +# retest with NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED). +# +# - if syncing to input sync, query the +# NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT attribute; note that Input video +# format can only be queried after SYNC_SOURCE is specified. +# +# - specify the NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT +# +# - specify the NV_CTRL_GVO_DATA_FORMAT +# +# - specify any custom Color Space Conversion (CSC) matrix, offset, +# and scale with XNVCTRLSetGvoColorConversion(). +# +# - if using the GLX_NV_video_out extension to display one or more +# pbuffers, call glXGetVideoDeviceNV() to lock the GVO output for use +# by the GLX client; then bind the pbuffer(s) to the GVO output with +# glXBindVideoImageNV() and send pbuffers to the GVO output with +# glXSendPbufferToVideoNV(); see the GLX_NV_video_out spec for more +# details. +# +# - if using the GLX_NV_present_video extension, call +# glXBindVideoDeviceNV() to bind the GVO video device to current +# OpenGL context. +# +# Note that setting most GVO attributes only causes the value to be +# cached in the X server. The values will be flushed to the hardware +# either when the next MetaMode is set that uses the GVO display +# device, or when a GLX pbuffer is bound to the GVO output (with +# glXBindVideoImageNV()). +# +# Note that GLX_NV_video_out/GLX_NV_present_video and X screen use +# are mutually exclusive. If a MetaMode is currently using the GVO +# device, then glXGetVideoDeviceNV and glXBindVideoImageNV() will +# fail. Similarly, if a GLX client has locked the GVO output (via +# glXGetVideoDeviceNV or glXBindVideoImageNV), then setting a +# MetaMode that uses the GVO device will fail. The +# NV_CTRL_GVO_GLX_LOCKED event will be sent when a GLX client locks +# the GVO output. +# +# + + +# +# NV_CTRL_GVO_SUPPORTED - returns whether this X screen supports GVO; +# if this screen does not support GVO output, then all other GVO +# attributes are unavailable. +# + +NV_CTRL_GVO_SUPPORTED = 67 # R-- +NV_CTRL_GVO_SUPPORTED_FALSE = 0 +NV_CTRL_GVO_SUPPORTED_TRUE = 1 + +# +# NV_CTRL_GVO_SYNC_MODE - selects the GVO sync mode; possible values +# are: +# +# FREE_RUNNING - GVO does not sync to any external signal +# +# GENLOCK - the GVO output is genlocked to an incoming sync signal; +# genlocking locks at hsync. This requires that the output video +# format exactly match the incoming sync video format. +# +# FRAMELOCK - the GVO output is frame locked to an incoming sync +# signal; frame locking locks at vsync. This requires that the output +# video format have the same refresh rate as the incoming sync video +# format. +# + +NV_CTRL_GVO_SYNC_MODE = 68 # RW- +NV_CTRL_GVO_SYNC_MODE_FREE_RUNNING = 0 +NV_CTRL_GVO_SYNC_MODE_GENLOCK = 1 +NV_CTRL_GVO_SYNC_MODE_FRAMELOCK = 2 + +# +# NV_CTRL_GVO_SYNC_SOURCE - if NV_CTRL_GVO_SYNC_MODE is set to either +# GENLOCK or FRAMELOCK, this controls which sync source is used as +# the incoming sync signal (either Composite or SDI). If +# NV_CTRL_GVO_SYNC_MODE is FREE_RUNNING, this attribute has no +# effect. +# + +NV_CTRL_GVO_SYNC_SOURCE = 69 # RW- +NV_CTRL_GVO_SYNC_SOURCE_COMPOSITE = 0 +NV_CTRL_GVO_SYNC_SOURCE_SDI = 1 + +# +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT - specifies the desired output video +# format for GVO devices or the desired input video format for GVI devices. +# +# Note that for GVO, the valid video formats may vary depending on +# the NV_CTRL_GVO_SYNC_MODE and the incoming sync video format. See +# the definition of NV_CTRL_GVO_SYNC_MODE. +# +# Note that when querying the ValidValues for this data type, the +# values are reported as bits within a bitmask +# (ATTRIBUTE_TYPE_INT_BITS); unfortunately, there are more valid +# value bits than will fit in a single 32-bit value. To solve this, +# query the ValidValues for NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT to +# check which of the first 31 VIDEO_FORMATS are valid, query the +# ValidValues for NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT2 to check which +# of the 32-63 VIDEO_FORMATS are valid, and query the ValidValues of +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT3 to check which of the 64-95 +# VIDEO_FORMATS are valid. +# +# Note: Setting this attribute on a GVI device may also result in the +# following NV-CONTROL attributes being reset on that device (to +# ensure the configuration remains valid): +# NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT +# NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING +# + +NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT = 70 # RW--I + +NV_CTRL_GVIO_VIDEO_FORMAT_NONE = 0 +NV_CTRL_GVIO_VIDEO_FORMAT_487I_59_94_SMPTE259_NTSC = 1 +NV_CTRL_GVIO_VIDEO_FORMAT_576I_50_00_SMPTE259_PAL = 2 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_59_94_SMPTE296 = 3 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_60_00_SMPTE296 = 4 +NV_CTRL_GVIO_VIDEO_FORMAT_1035I_59_94_SMPTE260 = 5 +NV_CTRL_GVIO_VIDEO_FORMAT_1035I_60_00_SMPTE260 = 6 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_50_00_SMPTE295 = 7 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_50_00_SMPTE274 = 8 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_59_94_SMPTE274 = 9 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_60_00_SMPTE274 = 10 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_23_976_SMPTE274 = 11 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_24_00_SMPTE274 = 12 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_25_00_SMPTE274 = 13 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_29_97_SMPTE274 = 14 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_30_00_SMPTE274 = 15 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_50_00_SMPTE296 = 16 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_48_00_SMPTE274 = 17 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_47_96_SMPTE274 = 18 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_30_00_SMPTE296 = 19 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_29_97_SMPTE296 = 20 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_25_00_SMPTE296 = 21 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_24_00_SMPTE296 = 22 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_23_98_SMPTE296 = 23 +NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_25_00_SMPTE274 = 24 +NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_29_97_SMPTE274 = 25 +NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_30_00_SMPTE274 = 26 +NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_24_00_SMPTE274 = 27 +NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_23_98_SMPTE274 = 28 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_30_00_SMPTE372 = 29 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_29_97_SMPTE372 = 30 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_60_00_SMPTE372 = 31 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_59_94_SMPTE372 = 32 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_25_00_SMPTE372 = 33 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_50_00_SMPTE372 = 34 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_24_00_SMPTE372 = 35 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_23_98_SMPTE372 = 36 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_48_00_SMPTE372 = 37 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_47_96_SMPTE372 = 38 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_50_00_3G_LEVEL_A_SMPTE274 = 39 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_59_94_3G_LEVEL_A_SMPTE274 = 40 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_60_00_3G_LEVEL_A_SMPTE274 = 41 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_60_00_3G_LEVEL_B_SMPTE274 = 42 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_60_00_3G_LEVEL_B_SMPTE274 = 43 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_60_00_3G_LEVEL_B_SMPTE372 = 44 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_50_00_3G_LEVEL_B_SMPTE274 = 45 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_50_00_3G_LEVEL_B_SMPTE274 = 46 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_50_00_3G_LEVEL_B_SMPTE372 = 47 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_30_00_3G_LEVEL_B_SMPTE274 = 48 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_30_00_3G_LEVEL_B_SMPTE372 = 49 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_25_00_3G_LEVEL_B_SMPTE274 = 50 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_25_00_3G_LEVEL_B_SMPTE372 = 51 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_24_00_3G_LEVEL_B_SMPTE274 = 52 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_24_00_3G_LEVEL_B_SMPTE372 = 53 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_48_00_3G_LEVEL_B_SMPTE274 = 54 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_48_00_3G_LEVEL_B_SMPTE372 = 55 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_59_94_3G_LEVEL_B_SMPTE274 = 56 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_59_94_3G_LEVEL_B_SMPTE274 = 57 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_59_94_3G_LEVEL_B_SMPTE372 = 58 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_29_97_3G_LEVEL_B_SMPTE274 = 59 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_29_97_3G_LEVEL_B_SMPTE372 = 60 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_23_98_3G_LEVEL_B_SMPTE274 = 61 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_23_98_3G_LEVEL_B_SMPTE372 = 62 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_47_96_3G_LEVEL_B_SMPTE274 = 63 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_47_96_3G_LEVEL_B_SMPTE372 = 64 + +# +# The following have been renamed; NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT and the +# corresponding NV_CTRL_GVIO_* formats should be used instead. +# +NV_CTRL_GVO_OUTPUT_VIDEO_FORMAT = 70 # renamed + +NV_CTRL_GVO_VIDEO_FORMAT_NONE = 0 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_487I_59_94_SMPTE259_NTSC = 1 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_576I_50_00_SMPTE259_PAL = 2 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_59_94_SMPTE296 = 3 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_60_00_SMPTE296 = 4 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1035I_59_94_SMPTE260 = 5 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1035I_60_00_SMPTE260 = 6 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080I_50_00_SMPTE295 = 7 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080I_50_00_SMPTE274 = 8 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080I_59_94_SMPTE274 = 9 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080I_60_00_SMPTE274 = 10 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080P_23_976_SMPTE274 = 11 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080P_24_00_SMPTE274 = 12 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080P_25_00_SMPTE274 = 13 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080P_29_97_SMPTE274 = 14 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080P_30_00_SMPTE274 = 15 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_50_00_SMPTE296 = 16 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080I_48_00_SMPTE274 = 17 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080I_47_96_SMPTE274 = 18 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_30_00_SMPTE296 = 19 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_29_97_SMPTE296 = 20 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_25_00_SMPTE296 = 21 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_24_00_SMPTE296 = 22 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_23_98_SMPTE296 = 23 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_25_00_SMPTE274 = 24 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_29_97_SMPTE274 = 25 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_30_00_SMPTE274 = 26 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_24_00_SMPTE274 = 27 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_23_98_SMPTE274 = 28 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048P_30_00_SMPTE372 = 29 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048P_29_97_SMPTE372 = 30 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048I_60_00_SMPTE372 = 31 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048I_59_94_SMPTE372 = 32 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048P_25_00_SMPTE372 = 33 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048I_50_00_SMPTE372 = 34 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048P_24_00_SMPTE372 = 35 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048P_23_98_SMPTE372 = 36 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048I_48_00_SMPTE372 = 37 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048I_47_96_SMPTE372 = 38 # renamed + +# +# NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT - indicates the input video format +# detected for GVO or GVI devices; the possible values are the +# NV_CTRL_GVIO_VIDEO_FORMAT constants. +# +# For GVI devices, the jack number should be specified in the lower +# 16 bits of the "display_mask" parameter, while the channel number should be +# specified in the upper 16 bits. +# + +NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT = 71 # R--I + +# +# NV_CTRL_GVO_INPUT_VIDEO_FORMAT - renamed +# +# NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT should be used instead. +# + +NV_CTRL_GVO_INPUT_VIDEO_FORMAT = 71 # renamed + +# +# NV_CTRL_GVO_DATA_FORMAT - This controls how the data in the source +# (either the X screen or the GLX pbuffer) is interpretted and +# displayed. +# +# Note: some of the below DATA_FORMATS have been renamed. For +# example, R8G8B8_TO_RGB444 has been renamed to X8X8X8_444_PASSTHRU. +# This is to more accurately reflect DATA_FORMATS where the +# per-channel data could be either RGB or YCrCb -- the point is that +# the driver and GVO hardware do not perform any implicit color space +# conversion on the data; it is passed through to the SDI out. +# + +NV_CTRL_GVO_DATA_FORMAT = 72 # RW- +NV_CTRL_GVO_DATA_FORMAT_R8G8B8_TO_YCRCB444 = 0 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8A8_TO_YCRCBA4444 = 1 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8Z10_TO_YCRCBZ4444 = 2 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8_TO_YCRCB422 = 3 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8A8_TO_YCRCBA4224 = 4 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8Z10_TO_YCRCBZ4224 = 5 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8_TO_RGB444 = 6 # renamed +NV_CTRL_GVO_DATA_FORMAT_X8X8X8_444_PASSTHRU = 6 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8A8_TO_RGBA4444 = 7 # renamed +NV_CTRL_GVO_DATA_FORMAT_X8X8X8A8_4444_PASSTHRU = 7 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8Z10_TO_RGBZ4444 = 8 # renamed +NV_CTRL_GVO_DATA_FORMAT_X8X8X8Z8_4444_PASSTHRU = 8 +NV_CTRL_GVO_DATA_FORMAT_Y10CR10CB10_TO_YCRCB444 = 9 # renamed +NV_CTRL_GVO_DATA_FORMAT_X10X10X10_444_PASSTHRU = 9 +NV_CTRL_GVO_DATA_FORMAT_Y10CR8CB8_TO_YCRCB444 = 10 # renamed +NV_CTRL_GVO_DATA_FORMAT_X10X8X8_444_PASSTHRU = 10 +NV_CTRL_GVO_DATA_FORMAT_Y10CR8CB8A10_TO_YCRCBA4444 = 11 # renamed +NV_CTRL_GVO_DATA_FORMAT_X10X8X8A10_4444_PASSTHRU = 11 +NV_CTRL_GVO_DATA_FORMAT_Y10CR8CB8Z10_TO_YCRCBZ4444 = 12 # renamed +NV_CTRL_GVO_DATA_FORMAT_X10X8X8Z10_4444_PASSTHRU = 12 +NV_CTRL_GVO_DATA_FORMAT_DUAL_R8G8B8_TO_DUAL_YCRCB422 = 13 +NV_CTRL_GVO_DATA_FORMAT_DUAL_Y8CR8CB8_TO_DUAL_YCRCB422 = 14 # renamed +NV_CTRL_GVO_DATA_FORMAT_DUAL_X8X8X8_TO_DUAL_422_PASSTHRU = 14 +NV_CTRL_GVO_DATA_FORMAT_R10G10B10_TO_YCRCB422 = 15 +NV_CTRL_GVO_DATA_FORMAT_R10G10B10_TO_YCRCB444 = 16 +NV_CTRL_GVO_DATA_FORMAT_Y12CR12CB12_TO_YCRCB444 = 17 # renamed +NV_CTRL_GVO_DATA_FORMAT_X12X12X12_444_PASSTHRU = 17 +NV_CTRL_GVO_DATA_FORMAT_R12G12B12_TO_YCRCB444 = 18 +NV_CTRL_GVO_DATA_FORMAT_X8X8X8_422_PASSTHRU = 19 +NV_CTRL_GVO_DATA_FORMAT_X8X8X8A8_4224_PASSTHRU = 20 +NV_CTRL_GVO_DATA_FORMAT_X8X8X8Z8_4224_PASSTHRU = 21 +NV_CTRL_GVO_DATA_FORMAT_X10X10X10_422_PASSTHRU = 22 +NV_CTRL_GVO_DATA_FORMAT_X10X8X8_422_PASSTHRU = 23 +NV_CTRL_GVO_DATA_FORMAT_X10X8X8A10_4224_PASSTHRU = 24 +NV_CTRL_GVO_DATA_FORMAT_X10X8X8Z10_4224_PASSTHRU = 25 +NV_CTRL_GVO_DATA_FORMAT_X12X12X12_422_PASSTHRU = 26 +NV_CTRL_GVO_DATA_FORMAT_R12G12B12_TO_YCRCB422 = 27 + +# +# NV_CTRL_GVO_DISPLAY_X_SCREEN - not supported +# + +NV_CTRL_GVO_DISPLAY_X_SCREEN = 73 # not supported +NV_CTRL_GVO_DISPLAY_X_SCREEN_ENABLE = 1 # not supported +NV_CTRL_GVO_DISPLAY_X_SCREEN_DISABLE = 0 # not supported + +# +# NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED - indicates whether +# Composite Sync input is detected. +# + +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED = 74 # R-- +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED_FALSE = 0 +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED_TRUE = 1 + +# +# NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE - get/set the +# Composite Sync input detect mode. +# + +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE = 75 # RW- +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE_AUTO = 0 +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE_BI_LEVEL = 1 +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE_TRI_LEVEL = 2 + +# +# NV_CTRL_GVO_SYNC_INPUT_DETECTED - indicates whether SDI Sync input +# is detected, and what type. +# + +NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED = 76 # R-- +NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED_NONE = 0 +NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED_HD = 1 +NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED_SD = 2 + +# +# NV_CTRL_GVO_VIDEO_OUTPUTS - indicates which GVO video output +# connectors are currently outputing data. +# + +NV_CTRL_GVO_VIDEO_OUTPUTS = 77 # R-- +NV_CTRL_GVO_VIDEO_OUTPUTS_NONE = 0 +NV_CTRL_GVO_VIDEO_OUTPUTS_VIDEO1 = 1 +NV_CTRL_GVO_VIDEO_OUTPUTS_VIDEO2 = 2 +NV_CTRL_GVO_VIDEO_OUTPUTS_VIDEO_BOTH = 3 + +# +# NV_CTRL_GVO_FIRMWARE_VERSION - deprecated +# +# NV_CTRL_STRING_GVIO_FIRMWARE_VERSION should be used instead. +# + +NV_CTRL_GVO_FIRMWARE_VERSION = 78 # deprecated + +# +# NV_CTRL_GVO_SYNC_DELAY_PIXELS - controls the delay between the +# input sync and the output sync in numbers of pixels from hsync; +# this is a 12 bit value. +# +# If the NV_CTRL_GVO_CAPABILITIES_ADVANCE_SYNC_SKEW bit is set, +# then setting this value will set an advance instead of a delay. +# + +NV_CTRL_GVO_SYNC_DELAY_PIXELS = 79 # RW- + +# +# NV_CTRL_GVO_SYNC_DELAY_LINES - controls the delay between the input +# sync and the output sync in numbers of lines from vsync; this is a +# 12 bit value. +# +# If the NV_CTRL_GVO_CAPABILITIES_ADVANCE_SYNC_SKEW bit is set, +# then setting this value will set an advance instead of a delay. +# + +NV_CTRL_GVO_SYNC_DELAY_LINES = 80 # RW- + +# +# NV_CTRL_GVO_INPUT_VIDEO_FORMAT_REACQUIRE - must be set for a period +# of about 2 seconds for the new InputVideoFormat to be properly +# locked to. In nvidia-settings, we do a reacquire whenever genlock +# or frame lock mode is entered into, when the user clicks the +# "detect" button. This value can be written, but always reads back +# _FALSE. +# + +NV_CTRL_GVO_INPUT_VIDEO_FORMAT_REACQUIRE = 81 # -W- +NV_CTRL_GVO_INPUT_VIDEO_FORMAT_REACQUIRE_FALSE = 0 +NV_CTRL_GVO_INPUT_VIDEO_FORMAT_REACQUIRE_TRUE = 1 + +# +# NV_CTRL_GVO_GLX_LOCKED - deprecated +# +# NV_CTRL_GVO_LOCK_OWNER should be used instead. +# + +NV_CTRL_GVO_GLX_LOCKED = 82 # deprecated +NV_CTRL_GVO_GLX_LOCKED_FALSE = 0 # deprecated +NV_CTRL_GVO_GLX_LOCKED_TRUE = 1 # deprecated + +# +# NV_CTRL_GVIO_VIDEO_FORMAT_{WIDTH,HEIGHT,REFRESH_RATE} - query the +# width, height, and refresh rate for the specified +# NV_CTRL_GVIO_VIDEO_FORMAT_*. So that this can be queried with +# existing interfaces, XNVCTRLQueryAttribute() should be used, and +# the video format specified in the display_mask field; eg: +# +# XNVCTRLQueryAttribute (dpy, +# screen, +# NV_CTRL_GVIO_VIDEO_FORMAT_487I_59_94_SMPTE259_NTSC, +# NV_CTRL_GVIO_VIDEO_FORMAT_WIDTH, +# &value); +# +# Note that Refresh Rate is in milliHertz values +# + +NV_CTRL_GVIO_VIDEO_FORMAT_WIDTH = 83 # R--I +NV_CTRL_GVIO_VIDEO_FORMAT_HEIGHT = 84 # R--I +NV_CTRL_GVIO_VIDEO_FORMAT_REFRESH_RATE = 85 # R--I + +# The following have been renamed; use the NV_CTRL_GVIO_* versions, instead +NV_CTRL_GVO_VIDEO_FORMAT_WIDTH = 83 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_HEIGHT = 84 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_REFRESH_RATE = 85 # renamed + +# +# NV_CTRL_GVO_X_SCREEN_PAN_[XY] - not supported +# + +NV_CTRL_GVO_X_SCREEN_PAN_X = 86 # not supported +NV_CTRL_GVO_X_SCREEN_PAN_Y = 87 # not supported + +# +# NV_CTRL_GPU_OVERCLOCKING_STATE - not supported +# + +NV_CTRL_GPU_OVERCLOCKING_STATE = 88 # not supported +NV_CTRL_GPU_OVERCLOCKING_STATE_NONE = 0 # not supported +NV_CTRL_GPU_OVERCLOCKING_STATE_MANUAL = 1 # not supported + +# +# NV_CTRL_GPU_{2,3}D_CLOCK_FREQS - not supported +# + +NV_CTRL_GPU_2D_CLOCK_FREQS = 89 # not supported +NV_CTRL_GPU_3D_CLOCK_FREQS = 90 # not supported + +# +# NV_CTRL_GPU_DEFAULT_{2,3}D_CLOCK_FREQS - not supported +# + +NV_CTRL_GPU_DEFAULT_2D_CLOCK_FREQS = 91 # not supported +NV_CTRL_GPU_DEFAULT_3D_CLOCK_FREQS = 92 # not supported + +# +# NV_CTRL_GPU_CURRENT_CLOCK_FREQS - query the current GPU and memory +# clocks of the graphics device driving the X screen. +# +# NV_CTRL_GPU_CURRENT_CLOCK_FREQS is a "packed" integer attribute; +# the GPU clock is stored in the upper 16 bits of the integer, and +# the memory clock is stored in the lower 16 bits of the integer. +# All clock values are in MHz. All clock values are in MHz. +# + +NV_CTRL_GPU_CURRENT_CLOCK_FREQS = 93 # R--G + +# +# NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS - not supported +# + +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS = 94 # not supported +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_INVALID = 0 # not supported + +# +# NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION - not supported +# + +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION = 95 # not supported +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_START = 0 # not supported +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_CANCEL = 1 # not supported + +# +# NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_STATE - not supported +# + +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_STATE = 96 # not supported +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_STATE_IDLE = 0 # not supported +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_STATE_BUSY = 1 # not supported + +# +# NV_CTRL_FLATPANEL_CHIP_LOCATION - for the specified display device, +# report whether the flat panel is driven by the on-chip controller, +# or a separate controller chip elsewhere on the graphics board. +# This attribute is only available for flat panels. +# + +NV_CTRL_FLATPANEL_CHIP_LOCATION = 215 # R-DG +NV_CTRL_FLATPANEL_CHIP_LOCATION_INTERNAL = 0 +NV_CTRL_FLATPANEL_CHIP_LOCATION_EXTERNAL = 1 + +# +# NV_CTRL_FLATPANEL_LINK - report the number of links for a DVI connection, or +# the main link's active lane count for DisplayPort. +# This attribute is only available for flat panels. +# + +NV_CTRL_FLATPANEL_LINK = 216 # R-DG +NV_CTRL_FLATPANEL_LINK_SINGLE = 0 +NV_CTRL_FLATPANEL_LINK_DUAL = 1 +NV_CTRL_FLATPANEL_LINK_QUAD = 3 + +# +# NV_CTRL_FLATPANEL_SIGNAL - for the specified display device, report +# whether the flat panel is driven by an LVDS, TMDS, or DisplayPort signal. +# This attribute is only available for flat panels. +# + +NV_CTRL_FLATPANEL_SIGNAL = 217 # R-DG +NV_CTRL_FLATPANEL_SIGNAL_LVDS = 0 +NV_CTRL_FLATPANEL_SIGNAL_TMDS = 1 +NV_CTRL_FLATPANEL_SIGNAL_DISPLAYPORT = 2 + +# +# NV_CTRL_USE_HOUSE_SYNC - when INPUT, the server (master) frame lock +# device will propagate the incoming house sync signal as the outgoing +# frame lock sync signal. If the frame lock device cannot detect a +# frame lock sync signal, it will default to using the internal timings +# from the GPU connected to the primary connector. +# +# When set to OUTPUT, the server (master) frame lock device will +# generate a house sync signal from its internal timing and output +# this signal over the BNC connector on the frame lock device. This +# is only allowed on a Quadro Sync II device. If an incoming house +# sync signal is present on the BNC connector, this setting will +# have no effect. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_USE_HOUSE_SYNC = 218 # RW-F +NV_CTRL_USE_HOUSE_SYNC_DISABLED = 0 # aliases with FALSE +NV_CTRL_USE_HOUSE_SYNC_INPUT = 1 # aliases with TRUE +NV_CTRL_USE_HOUSE_SYNC_OUTPUT = 2 +NV_CTRL_USE_HOUSE_SYNC_FALSE = 0 +NV_CTRL_USE_HOUSE_SYNC_TRUE = 1 + +# +# NV_CTRL_EDID_AVAILABLE - report if an EDID is available for the +# specified display device. +# +# This attribute may also be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_EDID_AVAILABLE = 219 # R-DG +NV_CTRL_EDID_AVAILABLE_FALSE = 0 +NV_CTRL_EDID_AVAILABLE_TRUE = 1 + +# +# NV_CTRL_FORCE_STEREO - when TRUE, OpenGL will force stereo flipping +# even when no stereo drawables are visible (if the device is configured +# to support it, see the "Stereo" X config option). +# When false, fall back to the default behavior of only flipping when a +# stereo drawable is visible. +# + +NV_CTRL_FORCE_STEREO = 220 # RW- +NV_CTRL_FORCE_STEREO_FALSE = 0 +NV_CTRL_FORCE_STEREO_TRUE = 1 + +# +# NV_CTRL_IMAGE_SETTINGS - the image quality setting for OpenGL clients. +# +# This setting is only applied to OpenGL clients that are started +# after this setting is applied. +# + +NV_CTRL_IMAGE_SETTINGS = 221 # RW-X +NV_CTRL_IMAGE_SETTINGS_HIGH_QUALITY = 0 +NV_CTRL_IMAGE_SETTINGS_QUALITY = 1 +NV_CTRL_IMAGE_SETTINGS_PERFORMANCE = 2 +NV_CTRL_IMAGE_SETTINGS_HIGH_PERFORMANCE = 3 + +# +# NV_CTRL_XINERAMA - return whether xinerama is enabled +# + +NV_CTRL_XINERAMA = 222 # R--G +NV_CTRL_XINERAMA_OFF = 0 +NV_CTRL_XINERAMA_ON = 1 + +# +# NV_CTRL_XINERAMA_STEREO - when TRUE, OpenGL will allow stereo flipping +# on multiple X screens configured with Xinerama. +# When FALSE, flipping is allowed only on one X screen at a time. +# + +NV_CTRL_XINERAMA_STEREO = 223 # RW- +NV_CTRL_XINERAMA_STEREO_FALSE = 0 +NV_CTRL_XINERAMA_STEREO_TRUE = 1 + +# +# NV_CTRL_BUS_RATE - if the bus type of the specified device is AGP, then +# NV_CTRL_BUS_RATE returns the configured AGP transfer rate. If the bus type +# is PCI Express, then this attribute returns the maximum link width. +# When this attribute is queried on an X screen target, the bus rate of the +# GPU driving the X screen is returned. +# + +NV_CTRL_BUS_RATE = 224 # R--GI + +# +# NV_CTRL_GPU_PCIE_MAX_LINK_WIDTH - returns the maximum +# PCIe link width, in number of lanes. +# +NV_CTRL_GPU_PCIE_MAX_LINK_WIDTH = NV_CTRL_BUS_RATE +# +# NV_CTRL_SHOW_SLI_VISUAL_INDICATOR - when TRUE, OpenGL will draw information +# about the current SLI mode. +# + +NV_CTRL_SHOW_SLI_VISUAL_INDICATOR = 225 # RW-X +NV_CTRL_SHOW_SLI_VISUAL_INDICATOR_FALSE = 0 +NV_CTRL_SHOW_SLI_VISUAL_INDICATOR_TRUE = 1 + +# +# NV_CTRL_SHOW_SLI_HUD - when TRUE, OpenGL will draw information about the +# current SLI mode. +# Renamed this attribute to NV_CTRL_SHOW_SLI_VISUAL_INDICATOR +# + +NV_CTRL_SHOW_SLI_HUD = NV_CTRL_SHOW_SLI_VISUAL_INDICATOR +NV_CTRL_SHOW_SLI_HUD_FALSE = NV_CTRL_SHOW_SLI_VISUAL_INDICATOR_FALSE +NV_CTRL_SHOW_SLI_HUD_TRUE = NV_CTRL_SHOW_SLI_VISUAL_INDICATOR_TRUE + +# +# NV_CTRL_XV_SYNC_TO_DISPLAY - deprecated +# +# NV_CTRL_XV_SYNC_TO_DISPLAY_ID should be used instead. +# + +NV_CTRL_XV_SYNC_TO_DISPLAY = 226 # deprecated + +# +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT2 - this attribute is only +# intended to be used to query the ValidValues for +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT for VIDEO_FORMAT values between +# 31 and 63. See NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT for details. +# + +NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT2 = 227 # ---GI + +# +# NV_CTRL_GVO_OUTPUT_VIDEO_FORMAT2 - renamed +# +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT2 should be used instead. +# +NV_CTRL_GVO_OUTPUT_VIDEO_FORMAT2 = 227 # renamed + +# +# NV_CTRL_GVO_OVERRIDE_HW_CSC - Override the SDI hardware's Color Space +# Conversion with the values controlled through +# XNVCTRLSetGvoColorConversion() and XNVCTRLGetGvoColorConversion(). If +# this attribute is FALSE, then the values specified through +# XNVCTRLSetGvoColorConversion() are ignored. +# + +NV_CTRL_GVO_OVERRIDE_HW_CSC = 228 # RW- +NV_CTRL_GVO_OVERRIDE_HW_CSC_FALSE = 0 +NV_CTRL_GVO_OVERRIDE_HW_CSC_TRUE = 1 + +# +# NV_CTRL_GVO_CAPABILITIES - this read-only attribute describes GVO +# capabilities that differ between NVIDIA SDI products. This value +# is a bitmask where each bit indicates whether that capability is +# available. +# +# APPLY_CSC_IMMEDIATELY - whether the CSC matrix, offset, and scale +# specified through XNVCTRLSetGvoColorConversion() will take affect +# immediately, or only after SDI output is disabled and enabled +# again. +# +# APPLY_CSC_TO_X_SCREEN - whether the CSC matrix, offset, and scale +# specified through XNVCTRLSetGvoColorConversion() will also apply +# to GVO output of an X screen, or only to OpenGL GVO output, as +# enabled through the GLX_NV_video_out extension. +# +# COMPOSITE_TERMINATION - whether the 75 ohm termination of the +# SDI composite input signal can be programmed through the +# NV_CTRL_GVO_COMPOSITE_TERMINATION attribute. +# +# SHARED_SYNC_BNC - whether the SDI device has a single BNC +# connector used for both (SDI & Composite) incoming signals. +# +# MULTIRATE_SYNC - whether the SDI device supports synchronization +# of input and output video modes that match in being odd or even +# modes (ie, AA.00 Hz modes can be synched to other BB.00 Hz modes and +# AA.XX Hz can match to BB.YY Hz where .XX and .YY are not .00) +# + +NV_CTRL_GVO_CAPABILITIES = 229 # R-- +NV_CTRL_GVO_CAPABILITIES_APPLY_CSC_IMMEDIATELY = 0x00000001 +NV_CTRL_GVO_CAPABILITIES_APPLY_CSC_TO_X_SCREEN = 0x00000002 +NV_CTRL_GVO_CAPABILITIES_COMPOSITE_TERMINATION = 0x00000004 +NV_CTRL_GVO_CAPABILITIES_SHARED_SYNC_BNC = 0x00000008 +NV_CTRL_GVO_CAPABILITIES_MULTIRATE_SYNC = 0x00000010 +NV_CTRL_GVO_CAPABILITIES_ADVANCE_SYNC_SKEW = 0x00000020 + +# +# NV_CTRL_GVO_COMPOSITE_TERMINATION - enable or disable 75 ohm +# termination of the SDI composite input signal. +# + +NV_CTRL_GVO_COMPOSITE_TERMINATION = 230 # RW- +NV_CTRL_GVO_COMPOSITE_TERMINATION_ENABLE = 1 +NV_CTRL_GVO_COMPOSITE_TERMINATION_DISABLE = 0 + +# +# NV_CTRL_ASSOCIATED_DISPLAY_DEVICES - deprecated +# +# NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN should be used instead. +# + +NV_CTRL_ASSOCIATED_DISPLAY_DEVICES = 231 # deprecated + +# +# NV_CTRL_FRAMELOCK_SLAVES - deprecated +# +# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG should be used instead. +# + +NV_CTRL_FRAMELOCK_SLAVES = 232 # deprecated + +# +# NV_CTRL_FRAMELOCK_MASTERABLE - deprecated +# +# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG should be used instead. +# + +NV_CTRL_FRAMELOCK_MASTERABLE = 233 # deprecated + +# +# NV_CTRL_PROBE_DISPLAYS - re-probes the hardware to detect what +# display devices are connected to the GPU or GPU driving the +# specified X screen. The return value is deprecated and should not be used. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_PROBE_DISPLAYS = 234 # R--G + +# +# NV_CTRL_REFRESH_RATE - Returns the refresh rate of the specified +# display device in 100# Hz (ie. to get the refresh rate in Hz, divide +# the returned value by 100.) +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_REFRESH_RATE = 235 # R-DG + +# +# NV_CTRL_GVO_FLIP_QUEUE_SIZE - The Graphics to Video Out interface +# exposed through NV-CONTROL and the GLX_NV_video_out extension uses +# an internal flip queue when pbuffers are sent to the video device +# (via glXSendPbufferToVideoNV()). The NV_CTRL_GVO_FLIP_QUEUE_SIZE +# can be used to query and assign the flip queue size. This +# attribute is applied to GLX when glXGetVideoDeviceNV() is called by +# the application. +# + +NV_CTRL_GVO_FLIP_QUEUE_SIZE = 236 # RW- + +# +# NV_CTRL_CURRENT_SCANLINE - query the current scanline for the +# specified display device. +# + +NV_CTRL_CURRENT_SCANLINE = 237 # R-DG + +# +# NV_CTRL_INITIAL_PIXMAP_PLACEMENT - Controls where X pixmaps are initially +# created. +# +# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_FORCE_SYSMEM causes pixmaps to stay in +# system memory. These pixmaps can't be accelerated by the NVIDIA driver; this +# will cause blank windows if used with an OpenGL compositing manager. +# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_SYSMEM creates pixmaps in system memory +# initially, but allows them to migrate to video memory. +# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_VIDMEM creates pixmaps in video memory +# when enough resources are available. +# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_RESERVED is currently reserved for future +# use. Behavior is undefined. +# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_GPU_SYSMEM creates pixmaps in GPU accessible +# system memory when enough resources are available. +# + +NV_CTRL_INITIAL_PIXMAP_PLACEMENT = 238 # RW- +NV_CTRL_INITIAL_PIXMAP_PLACEMENT_FORCE_SYSMEM = 0 +NV_CTRL_INITIAL_PIXMAP_PLACEMENT_SYSMEM = 1 +NV_CTRL_INITIAL_PIXMAP_PLACEMENT_VIDMEM = 2 +NV_CTRL_INITIAL_PIXMAP_PLACEMENT_RESERVED = 3 +NV_CTRL_INITIAL_PIXMAP_PLACEMENT_GPU_SYSMEM = 4 + +# +# NV_CTRL_PCI_BUS - Returns the PCI bus number the specified device is using. +# + +NV_CTRL_PCI_BUS = 239 # R--GI + +# +# NV_CTRL_PCI_DEVICE - Returns the PCI device number the specified device is +# using. +# + +NV_CTRL_PCI_DEVICE = 240 # R--GI + +# +# NV_CTRL_PCI_FUNCTION - Returns the PCI function number the specified device +# is using. +# + +NV_CTRL_PCI_FUNCTION = 241 # R--GI + +# +# NV_CTRL_FRAMELOCK_FPGA_REVISION - Queries the FPGA revision of the +# Frame Lock device. +# +# This attribute must be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK target. +# + +NV_CTRL_FRAMELOCK_FPGA_REVISION = 242 # R--F + +# +# NV_CTRL_MAX_SCREEN_{WIDTH,HEIGHT} - the maximum allowable size, in +# pixels, of either the specified X screen (if the target_type of the +# query is an X screen), or any X screen on the specified GPU (if the +# target_type of the query is a GPU). +# + +NV_CTRL_MAX_SCREEN_WIDTH = 243 # R--G +NV_CTRL_MAX_SCREEN_HEIGHT = 244 # R--G + +# +# NV_CTRL_MAX_DISPLAYS - The maximum number of display devices that +# can be driven simultaneously on a GPU (e.g., that can be used in a +# MetaMode at once). Note that this does not indicate the maximum +# number of displays that are listed in NV_CTRL_BINARY_DATA_DISPLAYS_ON_GPU +# and NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU because more display +# devices can be connected than are actively in use. +# + +NV_CTRL_MAX_DISPLAYS = 245 # R--G + +# +# NV_CTRL_DYNAMIC_TWINVIEW - Returns whether or not the screen +# supports dynamic twinview. +# + +NV_CTRL_DYNAMIC_TWINVIEW = 246 # R-- + +# +# NV_CTRL_MULTIGPU_DISPLAY_OWNER - Returns the (NV-CONTROL) GPU ID of +# the GPU that has the display device(s) used for showing the X Screen. +# + +NV_CTRL_MULTIGPU_DISPLAY_OWNER = 247 # R-- + +# +# NV_CTRL_GPU_SCALING - not supported +# + +NV_CTRL_GPU_SCALING = 248 # not supported + +NV_CTRL_GPU_SCALING_TARGET_INVALID = 0 # not supported +NV_CTRL_GPU_SCALING_TARGET_FLATPANEL_BEST_FIT = 1 # not supported +NV_CTRL_GPU_SCALING_TARGET_FLATPANEL_NATIVE = 2 # not supported + +NV_CTRL_GPU_SCALING_METHOD_INVALID = 0 # not supported +NV_CTRL_GPU_SCALING_METHOD_STRETCHED = 1 # not supported +NV_CTRL_GPU_SCALING_METHOD_CENTERED = 2 # not supported +NV_CTRL_GPU_SCALING_METHOD_ASPECT_SCALED = 3 # not supported + +# +# NV_CTRL_FRONTEND_RESOLUTION - not supported +# + +NV_CTRL_FRONTEND_RESOLUTION = 249 # not supported + +# +# NV_CTRL_BACKEND_RESOLUTION - not supported +# + +NV_CTRL_BACKEND_RESOLUTION = 250 # not supported + +# +# NV_CTRL_FLATPANEL_NATIVE_RESOLUTION - not supported +# + +NV_CTRL_FLATPANEL_NATIVE_RESOLUTION = 251 # not supported + +# +# NV_CTRL_FLATPANEL_BEST_FIT_RESOLUTION - not supported +# + +NV_CTRL_FLATPANEL_BEST_FIT_RESOLUTION = 252 # not supported + +# +# NV_CTRL_GPU_SCALING_ACTIVE - not supported +# + +NV_CTRL_GPU_SCALING_ACTIVE = 253 # not supported + +# +# NV_CTRL_DFP_SCALING_ACTIVE - not supported +# + +NV_CTRL_DFP_SCALING_ACTIVE = 254 # not supported + +# +# NV_CTRL_FSAA_APPLICATION_ENHANCED - Controls how the NV_CTRL_FSAA_MODE +# is applied when NV_CTRL_FSAA_APPLICATION_CONTROLLED is set to +# NV_CTRL_APPLICATION_CONTROLLED_DISABLED. When +# NV_CTRL_FSAA_APPLICATION_ENHANCED is _DISABLED, OpenGL applications will +# be forced to use the FSAA mode specified by NV_CTRL_FSAA_MODE. when set +# to _ENABLED, only those applications that have selected a multisample +# FBConfig will be made to use the NV_CTRL_FSAA_MODE specified. +# +# This attribute is ignored when NV_CTRL_FSAA_APPLICATION_CONTROLLED is +# set to NV_CTRL_FSAA_APPLICATION_CONTROLLED_ENABLED. +# + +NV_CTRL_FSAA_APPLICATION_ENHANCED = 255 # RW-X +NV_CTRL_FSAA_APPLICATION_ENHANCED_ENABLED = 1 +NV_CTRL_FSAA_APPLICATION_ENHANCED_DISABLED = 0 + +# +# NV_CTRL_FRAMELOCK_SYNC_RATE_4 - This is the refresh rate that the +# frame lock board is sending to the GPU with 4 digits of precision. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK. +# + +NV_CTRL_FRAMELOCK_SYNC_RATE_4 = 256 # R--F + +# +# NV_CTRL_GVO_LOCK_OWNER - indicates that the GVO device is available +# or in use (by GLX or an X screen). +# +# The GVO device is locked by GLX when either glXGetVideoDeviceNV +# (part of GLX_NV_video_out) or glXBindVideoDeviceNV (part of +# GLX_NV_present_video) is called. All GVO output resources are +# locked until released by the GLX_NV_video_out/GLX_NV_present_video +# client. +# +# The GVO device is locked/unlocked by an X screen, when the GVO device is +# used in a MetaMode on an X screen. +# +# When the GVO device is locked, setting of the following GVO NV-CONTROL +# attributes will not happen immediately and will instead be cached. The +# GVO resource will need to be disabled/released and re-enabled/claimed for +# the values to be flushed. These attributes are: +# +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT +# NV_CTRL_GVO_DATA_FORMAT +# NV_CTRL_GVO_FLIP_QUEUE_SIZE +# + +NV_CTRL_GVO_LOCK_OWNER = 257 # R-- +NV_CTRL_GVO_LOCK_OWNER_NONE = 0 +NV_CTRL_GVO_LOCK_OWNER_GLX = 1 +NV_CTRL_GVO_LOCK_OWNER_CLONE = 2 # not supported +NV_CTRL_GVO_LOCK_OWNER_X_SCREEN = 3 + +# +# NV_CTRL_HWOVERLAY - when a workstation overlay is in use, reports +# whether the hardware overlay is used, or if the overlay is emulated. +# + +NV_CTRL_HWOVERLAY = 258 # R-- +NV_CTRL_HWOVERLAY_FALSE = 0 +NV_CTRL_HWOVERLAY_TRUE = 1 + +# +# NV_CTRL_NUM_GPU_ERRORS_RECOVERED - Returns the number of GPU errors +# occured. This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_NUM_GPU_ERRORS_RECOVERED = 259 # R--- + +# +# NV_CTRL_REFRESH_RATE_3 - Returns the refresh rate of the specified +# display device in 1000# Hz (ie. to get the refresh rate in Hz, divide +# the returned value by 1000.) +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_REFRESH_RATE_3 = 260 # R-DG + +# +# NV_CTRL_ONDEMAND_VBLANK_INTERRUPTS - not supported +# + +NV_CTRL_ONDEMAND_VBLANK_INTERRUPTS = 261 # not supported +NV_CTRL_ONDEMAND_VBLANK_INTERRUPTS_OFF = 0 # not supported +NV_CTRL_ONDEMAND_VBLANK_INTERRUPTS_ON = 1 # not supported + +# +# NV_CTRL_GPU_POWER_SOURCE reports the type of power source +# of the GPU driving the X screen. +# + +NV_CTRL_GPU_POWER_SOURCE = 262 # R--G +NV_CTRL_GPU_POWER_SOURCE_AC = 0 +NV_CTRL_GPU_POWER_SOURCE_BATTERY = 1 + +# +# NV_CTRL_GPU_CURRENT_PERFORMANCE_MODE - not supported +# + +NV_CTRL_GPU_CURRENT_PERFORMANCE_MODE = 263 # not supported +NV_CTRL_GPU_CURRENT_PERFORMANCE_MODE_DESKTOP = 0 # not supported +NV_CTRL_GPU_CURRENT_PERFORMANCE_MODE_MAXPERF = 1 # not supported + +# NV_CTRL_GLYPH_CACHE - Enables RENDER Glyph Caching to VRAM + +NV_CTRL_GLYPH_CACHE = 264 # RW- +NV_CTRL_GLYPH_CACHE_DISABLED = 0 +NV_CTRL_GLYPH_CACHE_ENABLED = 1 + +# +# NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL reports the current +# Performance level of the GPU driving the X screen. Each +# Performance level has associated NVClock and Mem Clock values. +# + +NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL = 265 # R--G + +# +# NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE reports if Adaptive Clocking +# is Enabled on the GPU driving the X screen. +# + +NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE = 266 # R--G +NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE_DISABLED = 0 +NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE_ENABLED = 1 + +# +# NV_CTRL_GVO_OUTPUT_VIDEO_LOCKED - Returns whether or not the GVO output +# video is locked to the GPU. +# + +NV_CTRL_GVO_OUTPUT_VIDEO_LOCKED = 267 # R--- +NV_CTRL_GVO_OUTPUT_VIDEO_LOCKED_FALSE = 0 +NV_CTRL_GVO_OUTPUT_VIDEO_LOCKED_TRUE = 1 + +# +# NV_CTRL_GVO_SYNC_LOCK_STATUS - Returns whether or not the GVO device +# is locked to the input ref signal. If the sync mode is set to +# NV_CTRL_GVO_SYNC_MODE_GENLOCK, then this returns the genlock +# sync status, and if the sync mode is set to NV_CTRL_GVO_SYNC_MODE_FRAMELOCK, +# then this reports the frame lock status. +# + +NV_CTRL_GVO_SYNC_LOCK_STATUS = 268 # R--- +NV_CTRL_GVO_SYNC_LOCK_STATUS_UNLOCKED = 0 +NV_CTRL_GVO_SYNC_LOCK_STATUS_LOCKED = 1 + +# +# NV_CTRL_GVO_ANC_TIME_CODE_GENERATION - Allows SDI device to generate +# time codes in the ANC region of the SDI video output stream. +# + +NV_CTRL_GVO_ANC_TIME_CODE_GENERATION = 269 # RW-- +NV_CTRL_GVO_ANC_TIME_CODE_GENERATION_DISABLE = 0 +NV_CTRL_GVO_ANC_TIME_CODE_GENERATION_ENABLE = 1 + +# +# NV_CTRL_GVO_COMPOSITE - Enables/Disables SDI compositing. This attribute +# is only available when an SDI input source is detected and is in genlock +# mode. +# + +NV_CTRL_GVO_COMPOSITE = 270 # RW-- +NV_CTRL_GVO_COMPOSITE_DISABLE = 0 +NV_CTRL_GVO_COMPOSITE_ENABLE = 1 + +# +# NV_CTRL_GVO_COMPOSITE_ALPHA_KEY - When compositing is enabled, this +# enables/disables alpha blending. +# + +NV_CTRL_GVO_COMPOSITE_ALPHA_KEY = 271 # RW-- +NV_CTRL_GVO_COMPOSITE_ALPHA_KEY_DISABLE = 0 +NV_CTRL_GVO_COMPOSITE_ALPHA_KEY_ENABLE = 1 + +# +# NV_CTRL_GVO_COMPOSITE_LUMA_KEY_RANGE - Set the values of a luma +# channel range. This is a packed int that has the following format +# (in order of high-bits to low bits): +# +# Range # (11 bits), (Enabled 1 bit), min value (10 bits), max value (10 bits) +# +# To query the current values, pass the range # throught the display_mask +# variable. +# + +NV_CTRL_GVO_COMPOSITE_LUMA_KEY_RANGE = 272 # RW-- + +# +# NV_CTRL_GVO_COMPOSITE_CR_KEY_RANGE - Set the values of a CR +# channel range. This is a packed int that has the following format +# (in order of high-bits to low bits): +# +# Range # (11 bits), (Enabled 1 bit), min value (10 bits), max value (10 bits) +# +# To query the current values, pass the range # throught he display_mask +# variable. +# + +NV_CTRL_GVO_COMPOSITE_CR_KEY_RANGE = 273 # RW-- + +# +# NV_CTRL_GVO_COMPOSITE_CB_KEY_RANGE - Set the values of a CB +# channel range. This is a packed int that has the following format +# (in order of high-bits to low bits): +# +# Range # (11 bits), (Enabled 1 bit), min value (10 bits), max value (10 bits) +# +# To query the current values, pass the range # throught he display_mask +# variable. +# + +NV_CTRL_GVO_COMPOSITE_CB_KEY_RANGE = 274 # RW-- + +# +# NV_CTRL_GVO_COMPOSITE_NUM_KEY_RANGES - Returns the number of ranges +# available for each channel (Y/Luma, Cr, and Cb.) +# + +NV_CTRL_GVO_COMPOSITE_NUM_KEY_RANGES = 275 # R--- + +# +# NV_CTRL_SWITCH_TO_DISPLAYS - not supported +# + +NV_CTRL_SWITCH_TO_DISPLAYS = 276 # not supported + +# +# NV_CTRL_NOTEBOOK_DISPLAY_CHANGE_LID_EVENT - not supported +# + +NV_CTRL_NOTEBOOK_DISPLAY_CHANGE_LID_EVENT = 277 # not supported + +# +# NV_CTRL_NOTEBOOK_INTERNAL_LCD - deprecated +# + +NV_CTRL_NOTEBOOK_INTERNAL_LCD = 278 # deprecated + +# +# NV_CTRL_DEPTH_30_ALLOWED - returns whether the NVIDIA X driver supports +# depth 30 on the specified X screen or GPU. +# + +NV_CTRL_DEPTH_30_ALLOWED = 279 # R--G + +# +# NV_CTRL_MODE_SET_EVENT This attribute is sent as an event +# when hotkey, ctrl-alt-+/- or randr event occurs. Note that +# This attribute cannot be set or queried and is meant to +# be received by clients that wish to be notified of when +# mode set events occur. +# + +NV_CTRL_MODE_SET_EVENT = 280 # --- + +# +# NV_CTRL_OPENGL_AA_LINE_GAMMA_VALUE - the gamma value used by +# OpenGL when NV_CTRL_OPENGL_AA_LINE_GAMMA is enabled +# + +NV_CTRL_OPENGL_AA_LINE_GAMMA_VALUE = 281 # RW-X + +# +# NV_CTRL_VCSC_HIGH_PERF_MODE - deprecated +# +# Is used to both query High Performance Mode status on the Visual Computing +# System, and also to enable or disable High Performance Mode. +# + +NV_CTRL_VCSC_HIGH_PERF_MODE = 282 # RW-V +NV_CTRL_VCSC_HIGH_PERF_MODE_DISABLE = 0 +NV_CTRL_VCSC_HIGH_PERF_MODE_ENABLE = 1 + +# +# NV_CTRL_DISPLAYPORT_LINK_RATE - returns the negotiated lane bandwidth of the +# DisplayPort main link. The numerical value of this attribute is the link +# rate in bps divided by 27000000. +# This attribute is only available for DisplayPort flat panels. +# + +NV_CTRL_DISPLAYPORT_LINK_RATE = 291 # R-DG +NV_CTRL_DISPLAYPORT_LINK_RATE_DISABLED = 0x0 +NV_CTRL_DISPLAYPORT_LINK_RATE_1_62GBPS = 0x6 # deprecated +NV_CTRL_DISPLAYPORT_LINK_RATE_2_70GBPS = 0xA # deprecated + +# +# NV_CTRL_STEREO_EYES_EXCHANGE - Controls whether or not the left and right +# eyes of a stereo image are flipped. +# + +NV_CTRL_STEREO_EYES_EXCHANGE = 292 # RW-X +NV_CTRL_STEREO_EYES_EXCHANGE_OFF = 0 +NV_CTRL_STEREO_EYES_EXCHANGE_ON = 1 + +# +# NV_CTRL_NO_SCANOUT - returns whether the special "NoScanout" mode is +# enabled on the specified X screen or GPU; for details on this mode, +# see the description of the "none" value for the "UseDisplayDevice" +# X configuration option in the NVIDIA driver README. +# + +NV_CTRL_NO_SCANOUT = 293 # R--G +NV_CTRL_NO_SCANOUT_DISABLED = 0 +NV_CTRL_NO_SCANOUT_ENABLED = 1 + +# +# NV_CTRL_GVO_CSC_CHANGED_EVENT This attribute is sent as an event +# when the color space conversion matrix has been altered by another +# client. +# + +NV_CTRL_GVO_CSC_CHANGED_EVENT = 294 # --- + +# +# NV_CTRL_FRAMELOCK_SLAVEABLE - deprecated +# +# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG should be used instead. +# + +NV_CTRL_FRAMELOCK_SLAVEABLE = 295 # deprecated + +# +# NV_CTRL_GVO_SYNC_TO_DISPLAY This attribute controls whether or not +# the non-SDI display device will be sync'ed to the SDI display device +# (when configured in TwinView, Clone Mode or when using the SDI device +# with OpenGL). +# + +NV_CTRL_GVO_SYNC_TO_DISPLAY = 296 # --- +NV_CTRL_GVO_SYNC_TO_DISPLAY_DISABLE = 0 +NV_CTRL_GVO_SYNC_TO_DISPLAY_ENABLE = 1 + +# +# NV_CTRL_X_SERVER_UNIQUE_ID - returns a pseudo-unique identifier for this +# X server. Intended for use in cases where an NV-CONTROL client communicates +# with multiple X servers, and wants some level of confidence that two +# X Display connections correspond to the same or different X servers. +# + +NV_CTRL_X_SERVER_UNIQUE_ID = 297 # R--- + +# +# NV_CTRL_PIXMAP_CACHE - This attribute controls whether the driver attempts to +# store video memory pixmaps in a cache. The cache speeds up allocation and +# deallocation of pixmaps, but could use more memory than when the cache is +# disabled. +# + +NV_CTRL_PIXMAP_CACHE = 298 # RW-X +NV_CTRL_PIXMAP_CACHE_DISABLE = 0 +NV_CTRL_PIXMAP_CACHE_ENABLE = 1 + +# +# NV_CTRL_PIXMAP_CACHE_ROUNDING_SIZE_KB - When the pixmap cache is enabled and +# there is not enough free space in the cache to fit a new pixmap, the driver +# will round up to the next multiple of this number of kilobytes when +# allocating more memory for the cache. +# + +NV_CTRL_PIXMAP_CACHE_ROUNDING_SIZE_KB = 299 # RW-X + +# +# NV_CTRL_IS_GVO_DISPLAY - returns whether or not a given display is an +# SDI device. +# + +NV_CTRL_IS_GVO_DISPLAY = 300 # R-D +NV_CTRL_IS_GVO_DISPLAY_FALSE = 0 +NV_CTRL_IS_GVO_DISPLAY_TRUE = 1 + +# +# NV_CTRL_PCI_ID - Returns the PCI vendor and device ID of the specified +# device. +# +# NV_CTRL_PCI_ID is a "packed" integer attribute; the PCI vendor ID is stored +# in the upper 16 bits of the integer, and the PCI device ID is stored in the +# lower 16 bits of the integer. +# + +NV_CTRL_PCI_ID = 301 # R--GI + +# +# NV_CTRL_GVO_FULL_RANGE_COLOR - Allow full range color data [4-1019] +# without clamping to [64-940]. +# + +NV_CTRL_GVO_FULL_RANGE_COLOR = 302 # RW- +NV_CTRL_GVO_FULL_RANGE_COLOR_DISABLED = 0 +NV_CTRL_GVO_FULL_RANGE_COLOR_ENABLED = 1 + +# +# NV_CTRL_SLI_MOSAIC_MODE_AVAILABLE - Returns whether or not +# SLI Mosaic Mode supported. +# + +NV_CTRL_SLI_MOSAIC_MODE_AVAILABLE = 303 # R-- +NV_CTRL_SLI_MOSAIC_MODE_AVAILABLE_FALSE = 0 +NV_CTRL_SLI_MOSAIC_MODE_AVAILABLE_TRUE = 1 + +# +# NV_CTRL_GVO_ENABLE_RGB_DATA - Allows clients to specify when +# the GVO board should process colors as RGB when the output data +# format is one of the NV_CTRL_GVO_DATA_FORMAT_???_PASSTRHU modes. +# + +NV_CTRL_GVO_ENABLE_RGB_DATA = 304 # RW- +NV_CTRL_GVO_ENABLE_RGB_DATA_DISABLE = 0 +NV_CTRL_GVO_ENABLE_RGB_DATA_ENABLE = 1 + +# +# NV_CTRL_IMAGE_SHARPENING_DEFAULT - Returns default value of +# Image Sharpening. +# + +NV_CTRL_IMAGE_SHARPENING_DEFAULT = 305 # R-- + +# +# NV_CTRL_PCI_DOMAIN - Returns the PCI domain number the specified device is +# using. +# + +NV_CTRL_PCI_DOMAIN = 306 # R--GI + +# +# NV_CTRL_GVI_NUM_JACKS - Returns the number of input BNC jacks available +# on a GVI device. +# + +NV_CTRL_GVI_NUM_JACKS = 307 # R--I + +# +# NV_CTRL_GVI_MAX_LINKS_PER_STREAM - Returns the maximum supported number of +# links that can be tied to one stream. +# + +NV_CTRL_GVI_MAX_LINKS_PER_STREAM = 308 # R--I + +# +# NV_CTRL_GVI_DETECTED_CHANNEL_BITS_PER_COMPONENT - Returns the detected +# number of bits per component (BPC) of data on the given input jack+ +# channel. +# +# The jack number should be specified in the lower 16 bits of the +# "display_mask" parameter, while the channel number should be specified in +# the upper 16 bits. +# + +NV_CTRL_GVI_DETECTED_CHANNEL_BITS_PER_COMPONENT = 309 # R--I +NV_CTRL_GVI_BITS_PER_COMPONENT_UNKNOWN = 0 +NV_CTRL_GVI_BITS_PER_COMPONENT_8 = 1 +NV_CTRL_GVI_BITS_PER_COMPONENT_10 = 2 +NV_CTRL_GVI_BITS_PER_COMPONENT_12 = 3 + +# +# NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT - Specify the number of +# bits per component (BPC) of data for the captured stream. +# The stream number should be specified in the "display_mask" parameter. +# +# Note: Setting this attribute may also result in the following +# NV-CONTROL attributes being reset on the GVI device (to ensure +# the configuration remains valid): +# NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING +# + +NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT = 310 # RW-I + +# +# NV_CTRL_GVI_DETECTED_CHANNEL_COMPONENT_SAMPLING - Returns the detected +# sampling format for the input jack+channel. +# +# The jack number should be specified in the lower 16 bits of the +# "display_mask" parameter, while the channel number should be specified in +# the upper 16 bits. +# + +NV_CTRL_GVI_DETECTED_CHANNEL_COMPONENT_SAMPLING = 311 # R--I +NV_CTRL_GVI_COMPONENT_SAMPLING_UNKNOWN = 0 +NV_CTRL_GVI_COMPONENT_SAMPLING_4444 = 1 +NV_CTRL_GVI_COMPONENT_SAMPLING_4224 = 2 +NV_CTRL_GVI_COMPONENT_SAMPLING_444 = 3 +NV_CTRL_GVI_COMPONENT_SAMPLING_422 = 4 +NV_CTRL_GVI_COMPONENT_SAMPLING_420 = 5 + +# +# NV_CTRL_GVI_REQUESTED_COMPONENT_SAMPLING - Specify the sampling format for +# the captured stream. +# The possible values are the NV_CTRL_GVI_DETECTED_COMPONENT_SAMPLING +# constants. +# The stream number should be specified in the "display_mask" parameter. +# + +NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING = 312 # RW-I + +# +# NV_CTRL_GVI_CHROMA_EXPAND - Enable or disable 4:2:2 -> 4:4:4 chroma +# expansion for the captured stream. This value is ignored when a +# COMPONENT_SAMPLING format is selected that does not use chroma subsampling, +# or if a BITS_PER_COMPONENT value is selected that is not supported. +# The stream number should be specified in the "display_mask" parameter. +# + +NV_CTRL_GVI_REQUESTED_STREAM_CHROMA_EXPAND = 313 # RW-I +NV_CTRL_GVI_CHROMA_EXPAND_FALSE = 0 +NV_CTRL_GVI_CHROMA_EXPAND_TRUE = 1 + +# +# NV_CTRL_GVI_DETECTED_CHANNEL_COLOR_SPACE - Returns the detected color space +# of the input jack+channel. +# +# The jack number should be specified in the lower 16 bits of the +# "display_mask" parameter, while the channel number should be specified in +# the upper 16 bits. +# + +NV_CTRL_GVI_DETECTED_CHANNEL_COLOR_SPACE = 314 # R--I +NV_CTRL_GVI_COLOR_SPACE_UNKNOWN = 0 +NV_CTRL_GVI_COLOR_SPACE_GBR = 1 +NV_CTRL_GVI_COLOR_SPACE_GBRA = 2 +NV_CTRL_GVI_COLOR_SPACE_GBRD = 3 +NV_CTRL_GVI_COLOR_SPACE_YCBCR = 4 +NV_CTRL_GVI_COLOR_SPACE_YCBCRA = 5 +NV_CTRL_GVI_COLOR_SPACE_YCBCRD = 6 + +# +# NV_CTRL_GVI_DETECTED_CHANNEL_LINK_ID - Returns the detected link identifier +# for the given input jack+channel. +# +# The jack number should be specified in the lower 16 bits of the +# "display_mask" parameter, while the channel number should be specified in +# the upper 16 bits. +# + +NV_CTRL_GVI_DETECTED_CHANNEL_LINK_ID = 315 # R--I +NV_CTRL_GVI_LINK_ID_UNKNOWN = 0xFFFF + +# +# NV_CTRL_GVI_DETECTED_CHANNEL_SMPTE352_IDENTIFIER - Returns the 4-byte +# SMPTE 352 identifier from the given input jack+channel. +# +# The jack number should be specified in the lower 16 bits of the +# "display_mask" parameter, while the channel number should be specified in +# the upper 16 bits. +# + +NV_CTRL_GVI_DETECTED_CHANNEL_SMPTE352_IDENTIFIER = 316 # R--I + +# +# NV_CTRL_GVI_GLOBAL_IDENTIFIER - Returns a global identifier for the +# GVI device. This identifier can be used to relate GVI devices named +# in NV-CONTROL with those enumerated in OpenGL. +# + +NV_CTRL_GVI_GLOBAL_IDENTIFIER = 317 # R--I + +# +# NV_CTRL_FRAMELOCK_SYNC_DELAY_RESOLUTION - Returns the number of nanoseconds +# that one unit of NV_CTRL_FRAMELOCK_SYNC_DELAY corresponds to. +# +NV_CTRL_FRAMELOCK_SYNC_DELAY_RESOLUTION = 318 # R-- + +# +# NV_CTRL_GPU_COOLER_MANUAL_CONTROL - Query the current or set a new +# cooler control state; the value of this attribute controls the +# availability of additional cooler control attributes (see below). +# +# Note: this attribute is unavailable unless cooler control support +# has been enabled in the X server (by the user). +# + +NV_CTRL_GPU_COOLER_MANUAL_CONTROL = 319 # RW-G +NV_CTRL_GPU_COOLER_MANUAL_CONTROL_FALSE = 0 +NV_CTRL_GPU_COOLER_MANUAL_CONTROL_TRUE = 1 + +# +# NV_CTRL_THERMAL_COOLER_LEVEL - The cooler's target level. +# Normally, the driver dynamically adjusts the cooler based on +# the needs of the GPU. But when NV_CTRL_GPU_COOLER_MANUAL_CONTROL=TRUE, +# the driver will attempt to make the cooler achieve the setting in +# NV_CTRL_THERMAL_COOLER_LEVEL. The actual current level of the cooler +# is reported in NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL. +# + +NV_CTRL_THERMAL_COOLER_LEVEL = 320 # RW-C + +# NV_CTRL_THERMAL_COOLER_LEVEL_SET_DEFAULT - Sets default values of +# cooler. +# + +NV_CTRL_THERMAL_COOLER_LEVEL_SET_DEFAULT = 321 # -W-C + +# +# NV_CTRL_THERMAL_COOLER_CONTROL_TYPE - +# Returns a cooler's control signal characteristics. +# The possible types are restricted, Variable and Toggle. +# + +NV_CTRL_THERMAL_COOLER_CONTROL_TYPE = 322 # R--C +NV_CTRL_THERMAL_COOLER_CONTROL_TYPE_NONE = 0 +NV_CTRL_THERMAL_COOLER_CONTROL_TYPE_TOGGLE = 1 +NV_CTRL_THERMAL_COOLER_CONTROL_TYPE_VARIABLE = 2 + +# +# NV_CTRL_THERMAL_COOLER_TARGET - Returns objects that cooler cools. +# Targets may be GPU, Memory, Power Supply or All of these. +# GPU_RELATED = GPU | MEMORY | POWER_SUPPLY +# +# + +NV_CTRL_THERMAL_COOLER_TARGET = 323 # R--C +NV_CTRL_THERMAL_COOLER_TARGET_NONE = 0 +NV_CTRL_THERMAL_COOLER_TARGET_GPU = 1 +NV_CTRL_THERMAL_COOLER_TARGET_MEMORY = 2 +NV_CTRL_THERMAL_COOLER_TARGET_POWER_SUPPLY = 4 +NV_CTRL_THERMAL_COOLER_TARGET_GPU_RELATED = NV_CTRL_THERMAL_COOLER_TARGET_GPU | NV_CTRL_THERMAL_COOLER_TARGET_MEMORY | NV_CTRL_THERMAL_COOLER_TARGET_POWER_SUPPLY + +# +# NV_CTRL_GPU_ECC_SUPPORTED - Reports whether ECC is supported by the +# targeted GPU. +# +NV_CTRL_GPU_ECC_SUPPORTED = 324 # R--G +NV_CTRL_GPU_ECC_SUPPORTED_FALSE = 0 +NV_CTRL_GPU_ECC_SUPPORTED_TRUE = 1 + +# +# NV_CTRL_GPU_ECC_STATUS - Returns the current hardware ECC setting +# for the targeted GPU. +# +NV_CTRL_GPU_ECC_STATUS = 325 # R--G +NV_CTRL_GPU_ECC_STATUS_DISABLED = 0 +NV_CTRL_GPU_ECC_STATUS_ENABLED = 1 + +# +# NV_CTRL_GPU_ECC_CONFIGURATION - Reports whether ECC can be configured +# dynamically for the GPU in question. +# +NV_CTRL_GPU_ECC_CONFIGURATION_SUPPORTED = 326 # R--G +NV_CTRL_GPU_ECC_CONFIGURATION_SUPPORTED_FALSE = 0 +NV_CTRL_GPU_ECC_CONFIGURATION_SUPPORTED_TRUE = 1 + +# +# NV_CTRL_GPU_ECC_CONFIGURATION_SETTING - Returns the current ECC +# configuration setting or specifies new settings. New settings do not +# take effect until the next POST. +# +NV_CTRL_GPU_ECC_CONFIGURATION = 327 # RW-G +NV_CTRL_GPU_ECC_CONFIGURATION_DISABLED = 0 +NV_CTRL_GPU_ECC_CONFIGURATION_ENABLED = 1 + +# +# NV_CTRL_GPU_ECC_DEFAULT_CONFIGURATION_SETTING - Returns the default +# ECC configuration setting. +# +NV_CTRL_GPU_ECC_DEFAULT_CONFIGURATION = 328 # R--G +NV_CTRL_GPU_ECC_DEFAULT_CONFIGURATION_DISABLED = 0 +NV_CTRL_GPU_ECC_DEFAULT_CONFIGURATION_ENABLED = 1 + +# +# NV_CTRL_GPU_ECC_SINGLE_BIT_ERRORS - Returns the number of single-bit +# ECC errors detected by the targeted GPU since the last POST. +# Note: this attribute is a 64-bit integer attribute. +# +NV_CTRL_GPU_ECC_SINGLE_BIT_ERRORS = 329 # R--GQ + +# +# NV_CTRL_GPU_ECC_DOUBLE_BIT_ERRORS - Returns the number of double-bit +# ECC errors detected by the targeted GPU since the last POST. +# Note: this attribute is a 64-bit integer attribute. +# +NV_CTRL_GPU_ECC_DOUBLE_BIT_ERRORS = 330 # R--GQ + +# +# NV_CTRL_GPU_ECC_AGGREGATE_SINGLE_BIT_ERRORS - Returns the number of +# single-bit ECC errors detected by the targeted GPU since the +# last counter reset. +# Note: this attribute is a 64-bit integer attribute. +# +NV_CTRL_GPU_ECC_AGGREGATE_SINGLE_BIT_ERRORS = 331 # R--GQ + +# +# NV_CTRL_GPU_ECC_AGGREGATE_DOUBLE_BIT_ERRORS - Returns the number of +# double-bit ECC errors detected by the targeted GPU since the +# last counter reset. +# Note: this attribute is a 64-bit integer attribute. +# +NV_CTRL_GPU_ECC_AGGREGATE_DOUBLE_BIT_ERRORS = 332 # R--GQ + +# +# NV_CTRL_GPU_ECC_RESET_ERROR_STATUS - Resets the volatile/aggregate +# single-bit and double-bit error counters. This attribute is a +# bitmask attribute. +# +NV_CTRL_GPU_ECC_RESET_ERROR_STATUS = 333 # -W-G +NV_CTRL_GPU_ECC_RESET_ERROR_STATUS_VOLATILE = 0x00000001 +NV_CTRL_GPU_ECC_RESET_ERROR_STATUS_AGGREGATE = 0x00000002 + +# +# NV_CTRL_GPU_POWER_MIZER_MODE - Provides a hint to the driver +# as to how to manage the performance of the GPU. +# +# ADAPTIVE - adjust GPU clocks based on GPU +# utilization +# PREFER_MAXIMUM_PERFORMANCE - raise GPU clocks to favor +# maximum performance, to the extent +# that thermal and other constraints +# allow +# AUTO - let the driver choose the performance +# policy +# PREFER_CONSISTENT_PERFORMANCE - lock to GPU base clocks +# +NV_CTRL_GPU_POWER_MIZER_MODE = 334 # RW-G +NV_CTRL_GPU_POWER_MIZER_MODE_ADAPTIVE = 0 +NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_MAXIMUM_PERFORMANCE = 1 +NV_CTRL_GPU_POWER_MIZER_MODE_AUTO = 2 +NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_CONSISTENT_PERFORMANCE = 3 + +# +# NV_CTRL_GVI_SYNC_OUTPUT_FORMAT - Returns the output sync signal +# from the GVI device. +# + +NV_CTRL_GVI_SYNC_OUTPUT_FORMAT = 335 # R--I + +# +# NV_CTRL_GVI_MAX_CHANNELS_PER_JACK - Returns the maximum +# supported number of (logical) channels within a single physical jack of +# a GVI device. For most SDI video formats, there is only one channel +# (channel 0). But for 3G video formats (as specified in SMPTE 425), +# as an example, there are two channels (channel 0 and channel 1) per +# physical jack. +# + +NV_CTRL_GVI_MAX_CHANNELS_PER_JACK = 336 # R--I + +# +# NV_CTRL_GVI_MAX_STREAMS - Returns the maximum number of streams +# that can be configured on the GVI device. +# + +NV_CTRL_GVI_MAX_STREAMS = 337 # R--I + +# +# NV_CTRL_GVI_NUM_CAPTURE_SURFACES - The GVI interface exposed through +# NV-CONTROL and the GLX_NV_video_input extension uses internal capture +# surfaces when frames are read from the GVI device. The +# NV_CTRL_GVI_NUM_CAPTURE_SURFACES can be used to query and assign the +# number of capture surfaces. This attribute is applied when +# glXBindVideoCaptureDeviceNV() is called by the application. +# +# A lower number of capture surfaces will mean less video memory is used, +# but can result in frames being dropped if the application cannot keep up +# with the capture device. A higher number will prevent frames from being +# dropped, making capture more reliable but will consume move video memory. +# +NV_CTRL_GVI_NUM_CAPTURE_SURFACES = 338 # RW-I + +# +# NV_CTRL_OVERSCAN_COMPENSATION - not supported +# +NV_CTRL_OVERSCAN_COMPENSATION = 339 # not supported + +# +# NV_CTRL_GPU_PCIE_GENERATION - Reports the current PCIe generation. +# +NV_CTRL_GPU_PCIE_GENERATION = 341 # R--GI +NV_CTRL_GPU_PCIE_GENERATION1 = 0x00000001 +NV_CTRL_GPU_PCIE_GENERATION2 = 0x00000002 +NV_CTRL_GPU_PCIE_GENERATION3 = 0x00000003 + +# +# NV_CTRL_GVI_BOUND_GPU - Returns the NV_CTRL_TARGET_TYPE_GPU target_id of +# the GPU currently bound to the GVI device. Returns -1 if no GPU is +# currently bound to the GVI device. +# +NV_CTRL_GVI_BOUND_GPU = 342 # R--I + +# +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT3 - this attribute is only +# intended to be used to query the ValidValues for +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT for VIDEO_FORMAT values between +# 64 and 95. See NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT for details. +# + +NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT3 = 343 # ---GI + +# +# NV_CTRL_ACCELERATE_TRAPEZOIDS - Toggles RENDER Trapezoid acceleration +# + +NV_CTRL_ACCELERATE_TRAPEZOIDS = 344 # RW- +NV_CTRL_ACCELERATE_TRAPEZOIDS_DISABLE = 0 +NV_CTRL_ACCELERATE_TRAPEZOIDS_ENABLE = 1 + +# +# NV_CTRL_GPU_CORES - Returns number of GPU cores supported by the graphics +# pipeline. +# + +NV_CTRL_GPU_CORES = 345 # R--G + +# +# NV_CTRL_GPU_MEMORY_BUS_WIDTH - Returns memory bus bandwidth on the associated +# subdevice. +# + +NV_CTRL_GPU_MEMORY_BUS_WIDTH = 346 # R--G + +# +# NV_CTRL_GVI_TEST_MODE - This attribute controls the GVI test mode. When +# enabled, the GVI device will generate fake data as quickly as possible. All +# GVI settings are still valid when this is enabled (e.g., the requested video +# format is honored and sets the video size). +# This may be used to test the pipeline. +# + +NV_CTRL_GVI_TEST_MODE = 347 # R--I +NV_CTRL_GVI_TEST_MODE_DISABLE = 0 +NV_CTRL_GVI_TEST_MODE_ENABLE = 1 + +# +# NV_CTRL_COLOR_SPACE - This option controls the preferred color space of the +# video signal. This may not match the current color space depending on the +# current mode on this display. +# +# NV_CTRL_CURRENT_COLOR_SPACE will reflect the actual color space in use. +# +NV_CTRL_COLOR_SPACE = 348 # RWDG +NV_CTRL_COLOR_SPACE_RGB = 0 +NV_CTRL_COLOR_SPACE_YCbCr422 = 1 +NV_CTRL_COLOR_SPACE_YCbCr444 = 2 + +# +# NV_CTRL_COLOR_RANGE - This option controls the preferred color range of the +# video signal. +# +# If the current color space requires it, the actual color range will be +# limited. +# +# NV_CTRL_CURRENT_COLOR_RANGE will reflect the actual color range in use. +# +NV_CTRL_COLOR_RANGE = 349 # RWDG +NV_CTRL_COLOR_RANGE_FULL = 0 +NV_CTRL_COLOR_RANGE_LIMITED = 1 + +# +# NV_CTRL_GPU_SCALING_DEFAULT_TARGET - not supported +# + +NV_CTRL_GPU_SCALING_DEFAULT_TARGET = 350 # not supported + +# +# NV_CTRL_GPU_SCALING_DEFAULT_METHOD - not supported +# + +NV_CTRL_GPU_SCALING_DEFAULT_METHOD = 351 # not supported + +# +# NV_CTRL_DITHERING_MODE - Controls the dithering mode, when +# NV_CTRL_CURRENT_DITHERING is Enabled. +# +# AUTO: allow the driver to choose the dithering mode automatically. +# +# DYNAMIC_2X2: use a 2x2 matrix to dither from the GPU's pixel +# pipeline to the bit depth of the flat panel. The matrix values +# are changed from frame to frame. +# +# STATIC_2X2: use a 2x2 matrix to dither from the GPU's pixel +# pipeline to the bit depth of the flat panel. The matrix values +# do not change from frame to frame. +# +# TEMPORAL: use a pseudorandom value from a uniform distribution calculated at +# every pixel to achieve stochastic dithering. This method produces a better +# visual result than 2x2 matrix approaches. +# +NV_CTRL_DITHERING_MODE = 352 # RWDG +NV_CTRL_DITHERING_MODE_AUTO = 0 +NV_CTRL_DITHERING_MODE_DYNAMIC_2X2 = 1 +NV_CTRL_DITHERING_MODE_STATIC_2X2 = 2 +NV_CTRL_DITHERING_MODE_TEMPORAL = 3 + +# +# NV_CTRL_CURRENT_DITHERING - Returns the current dithering state. +# +NV_CTRL_CURRENT_DITHERING = 353 # R-DG +NV_CTRL_CURRENT_DITHERING_DISABLED = 0 +NV_CTRL_CURRENT_DITHERING_ENABLED = 1 + +# +# NV_CTRL_CURRENT_DITHERING_MODE - Returns the current dithering +# mode. +# +NV_CTRL_CURRENT_DITHERING_MODE = 354 # R-DG +NV_CTRL_CURRENT_DITHERING_MODE_NONE = 0 +NV_CTRL_CURRENT_DITHERING_MODE_DYNAMIC_2X2 = 1 +NV_CTRL_CURRENT_DITHERING_MODE_STATIC_2X2 = 2 +NV_CTRL_CURRENT_DITHERING_MODE_TEMPORAL = 3 + +# +# NV_CTRL_THERMAL_SENSOR_READING - Returns the thermal sensor's current +# reading. +# +NV_CTRL_THERMAL_SENSOR_READING = 355 # R--S + +# +# NV_CTRL_THERMAL_SENSOR_PROVIDER - Returns the hardware device that +# provides the thermal sensor. +# +NV_CTRL_THERMAL_SENSOR_PROVIDER = 356 # R--S +NV_CTRL_THERMAL_SENSOR_PROVIDER_NONE = 0 +NV_CTRL_THERMAL_SENSOR_PROVIDER_GPU_INTERNAL = 1 +NV_CTRL_THERMAL_SENSOR_PROVIDER_ADM1032 = 2 +NV_CTRL_THERMAL_SENSOR_PROVIDER_ADT7461 = 3 +NV_CTRL_THERMAL_SENSOR_PROVIDER_MAX6649 = 4 +NV_CTRL_THERMAL_SENSOR_PROVIDER_MAX1617 = 5 +NV_CTRL_THERMAL_SENSOR_PROVIDER_LM99 = 6 +NV_CTRL_THERMAL_SENSOR_PROVIDER_LM89 = 7 +NV_CTRL_THERMAL_SENSOR_PROVIDER_LM64 = 8 +NV_CTRL_THERMAL_SENSOR_PROVIDER_G781 = 9 +NV_CTRL_THERMAL_SENSOR_PROVIDER_ADT7473 = 10 +NV_CTRL_THERMAL_SENSOR_PROVIDER_SBMAX6649 = 11 +NV_CTRL_THERMAL_SENSOR_PROVIDER_VBIOSEVT = 12 +NV_CTRL_THERMAL_SENSOR_PROVIDER_OS = 13 +NV_CTRL_THERMAL_SENSOR_PROVIDER_UNKNOWN = 0xFFFFFFFF + +# +# NV_CTRL_THERMAL_SENSOR_TARGET - Returns what hardware component +# the thermal sensor is measuring. +# +NV_CTRL_THERMAL_SENSOR_TARGET = 357 # R--S +NV_CTRL_THERMAL_SENSOR_TARGET_NONE = 0 +NV_CTRL_THERMAL_SENSOR_TARGET_GPU = 1 +NV_CTRL_THERMAL_SENSOR_TARGET_MEMORY = 2 +NV_CTRL_THERMAL_SENSOR_TARGET_POWER_SUPPLY = 4 +NV_CTRL_THERMAL_SENSOR_TARGET_BOARD = 8 +NV_CTRL_THERMAL_SENSOR_TARGET_UNKNOWN = 0xFFFFFFFF + +# +# NV_CTRL_SHOW_MULTIGPU_VISUAL_INDICATOR - when TRUE, OpenGL will +# draw information about the current MULTIGPU mode. +# +NV_CTRL_SHOW_MULTIGPU_VISUAL_INDICATOR = 358 # RW-X +NV_CTRL_SHOW_MULTIGPU_VISUAL_INDICATOR_FALSE = 0 +NV_CTRL_SHOW_MULTIGPU_VISUAL_INDICATOR_TRUE = 1 + +# +# NV_CTRL_GPU_CURRENT_PROCESSOR_CLOCK_FREQS - Returns GPU's processor +# clock freqs. +# +NV_CTRL_GPU_CURRENT_PROCESSOR_CLOCK_FREQS = 359 # RW-G + +# +# NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS - query the flags (various information +# for the specified NV_CTRL_GVIO_VIDEO_FORMAT_*. So that this can be +# queried with existing interfaces, the video format should be specified +# in the display_mask field; eg: +# +# XNVCTRLQueryTargetAttribute(dpy, +# NV_CTRL_TARGET_TYPE_GVI, +# gvi, +# NV_CTRL_GVIO_VIDEO_FORMAT_720P_60_00_SMPTE296, +# NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS, +# &flags); +# +# Note: The NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_1080P_NO_12BPC flag is set +# for those 1080P 3G modes (level A and B) that do not support +# 12 bits per component (when configuring a GVI stream.) +# + +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS = 360 # R--I +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_NONE = 0x00000000 +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_INTERLACED = 0x00000001 +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_PROGRESSIVE = 0x00000002 +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_PSF = 0x00000004 +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_LEVEL_A = 0x00000008 +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_LEVEL_B = 0x00000010 +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G = NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_LEVEL_A | NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_LEVEL_B +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_1080P_NO_12BPC = 0x00000020 + +# +# NV_CTRL_GPU_PCIE_MAX_LINK_SPEED - returns maximum PCIe link speed, +# in gigatransfers per second (GT/s). +# + +NV_CTRL_GPU_PCIE_MAX_LINK_SPEED = 361 # R--GI + +# +# NV_CTRL_3D_VISION_PRO_RESET_TRANSCEIVER_TO_FACTORY_SETTINGS - Resets the +# 3D Vision Pro transceiver to its factory settings. +# +NV_CTRL_3D_VISION_PRO_RESET_TRANSCEIVER_TO_FACTORY_SETTINGS = 363 # -W-T + +# +# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL - Controls the channel that is +# currently used by the 3D Vision Pro transceiver. +# +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL = 364 # RW-T + +# +# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE - Controls the mode in which the +# 3D Vision Pro transceiver operates. +# NV_CTRL_3D_VISION_PRO_TM_LOW_RANGE is bidirectional +# NV_CTRL_3D_VISION_PRO_TM_MEDIUM_RANGE is bidirectional +# NV_CTRL_3D_VISION_PRO_TM_HIGH_RANGE may be bidirectional just up to a +# given range, and unidirectional beyond it +# NV_CTRL_3D_VISION_PRO_TM_COUNT is the total number of +# 3D Vision Pro transceiver modes +# +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE = 365 # RW-T +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_INVALID = 0 +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_LOW_RANGE = 1 +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_MEDIUM_RANGE = 2 +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_HIGH_RANGE = 3 +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_COUNT = 4 + +# +# NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES - controls whether updates to the color +# lookup table (LUT) are synchronous with respect to X rendering. For example, +# if an X client sends XStoreColors followed by XFillRectangle, the driver will +# guarantee that the FillRectangle request is not processed until after the +# updated LUT colors are actually visible on the screen if +# NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES is enabled. Otherwise, the rendering may +# occur first. +# +# This makes a difference for applications that use the LUT to animate, such as +# XPilot. If you experience flickering in applications that use LUT +# animations, try enabling this attribute. +# +# When synchronous updates are enabled, XStoreColors requests will be processed +# at your screen's refresh rate. +# + +NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES = 367 # RWDG +NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES_DISABLE = 0 +NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES_ENABLE = 1 + +# +# NV_CTRL_DITHERING_DEPTH - Controls the dithering depth when +# NV_CTRL_CURRENT_DITHERING is ENABLED. Some displays connected +# to the GPU via the DVI or LVDS interfaces cannot display the +# full color range of ten bits per channel, so the GPU will +# dither to either 6 or 8 bits per channel. +# +NV_CTRL_DITHERING_DEPTH = 368 # RWDG +NV_CTRL_DITHERING_DEPTH_AUTO = 0 +NV_CTRL_DITHERING_DEPTH_6_BITS = 1 +NV_CTRL_DITHERING_DEPTH_8_BITS = 2 + +# +# NV_CTRL_CURRENT_DITHERING_DEPTH - Returns the current dithering +# depth value. +# +NV_CTRL_CURRENT_DITHERING_DEPTH = 369 # R-DG +NV_CTRL_CURRENT_DITHERING_DEPTH_NONE = 0 +NV_CTRL_CURRENT_DITHERING_DEPTH_6_BITS = 1 +NV_CTRL_CURRENT_DITHERING_DEPTH_8_BITS = 2 + +# +# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_FREQUENCY - Returns the +# frequency of the channel(in kHz) of the 3D Vision Pro transceiver. +# Use the display_mask parameter to specify the channel number. +# +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_FREQUENCY = 370 # R--T + +# +# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_QUALITY - Returns the +# quality of the channel(in percentage) of the 3D Vision Pro transceiver. +# Use the display_mask parameter to specify the channel number. +# +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_QUALITY = 371 # R--T + +# +# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_COUNT - Returns the number of +# channels on the 3D Vision Pro transceiver. +# +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_COUNT = 372 # R--T + +# +# NV_CTRL_3D_VISION_PRO_PAIR_GLASSES - Puts the 3D Vision Pro +# transceiver into pairing mode to gather additional glasses. +# NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_STOP - stops any pairing +# NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_BEACON - starts continuous +# pairing via beacon mode +# Any other value, N - Puts the 3D Vision Pro transceiver into +# authenticated pairing mode for N seconds. +# +NV_CTRL_3D_VISION_PRO_PAIR_GLASSES = 373 # -W-T +NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_STOP = 0 +NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_BEACON = 0xFFFFFFFF + +# +# NV_CTRL_3D_VISION_PRO_UNPAIR_GLASSES - Tells a specific pair +# of glasses to unpair. The glasses will "forget" the address +# of the 3D Vision Pro transceiver to which they have been paired. +# To unpair all the currently paired glasses, specify +# the glasses id as 0. +# +NV_CTRL_3D_VISION_PRO_UNPAIR_GLASSES = 374 # -W-T + +# +# NV_CTRL_3D_VISION_PRO_DISCOVER_GLASSES - Tells the 3D Vision Pro +# transceiver about the glasses that have been paired using +# NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_BEACON. Unless this is done, +# the 3D Vision Pro transceiver will not know about glasses paired in +# beacon mode. +# +NV_CTRL_3D_VISION_PRO_DISCOVER_GLASSES = 375 # -W-T + +# +# NV_CTRL_3D_VISION_PRO_IDENTIFY_GLASSES - Causes glasses LEDs to +# flash for a short period of time. +# +NV_CTRL_3D_VISION_PRO_IDENTIFY_GLASSES = 376 # -W-T + +# +# NV_CTRL_3D_VISION_PRO_GLASSES_SYNC_CYCLE - Controls the +# sync cycle duration(in milliseconds) of the glasses. +# Use the display_mask parameter to specify the glasses id. +# +NV_CTRL_3D_VISION_PRO_GLASSES_SYNC_CYCLE = 378 # RW-T + +# +# NV_CTRL_3D_VISION_PRO_GLASSES_MISSED_SYNC_CYCLES - Returns the +# number of state sync cycles recently missed by the glasses. +# Use the display_mask parameter to specify the glasses id. +# +NV_CTRL_3D_VISION_PRO_GLASSES_MISSED_SYNC_CYCLES = 379 # R--T + +# +# NV_CTRL_3D_VISION_PRO_GLASSES_BATTERY_LEVEL - Returns the +# battery level(in percentage) of the glasses. +# Use the display_mask parameter to specify the glasses id. +# +NV_CTRL_3D_VISION_PRO_GLASSES_BATTERY_LEVEL = 380 # R--T + +# +# NV_CTRL_GVO_ANC_PARITY_COMPUTATION - Controls the SDI device's computation +# of the parity bit (bit 8) for ANC data words. +# + +NV_CTRL_GVO_ANC_PARITY_COMPUTATION = 381 # RW--- +NV_CTRL_GVO_ANC_PARITY_COMPUTATION_AUTO = 0 +NV_CTRL_GVO_ANC_PARITY_COMPUTATION_ON = 1 +NV_CTRL_GVO_ANC_PARITY_COMPUTATION_OFF = 2 + +# +# NV_CTRL_3D_VISION_PRO_GLASSES_PAIR_EVENT - This attribute is sent +# as an event when glasses get paired in response to pair command +# from any of the clients. +# +NV_CTRL_3D_VISION_PRO_GLASSES_PAIR_EVENT = 382 # ---T + +# +# NV_CTRL_3D_VISION_PRO_GLASSES_UNPAIR_EVENT - This attribute is sent +# as an event when glasses get unpaired in response to unpair command +# from any of the clients. +# +NV_CTRL_3D_VISION_PRO_GLASSES_UNPAIR_EVENT = 383 # ---T + +# +# NV_CTRL_GPU_PCIE_CURRENT_LINK_WIDTH - returns the current +# PCIe link width, in number of lanes. +# +NV_CTRL_GPU_PCIE_CURRENT_LINK_WIDTH = 384 # R--GI + +# +# NV_CTRL_GPU_PCIE_CURRENT_LINK_SPEED - returns the current +# PCIe link speed, in megatransfers per second (GT/s). +# +NV_CTRL_GPU_PCIE_CURRENT_LINK_SPEED = 385 # R--GI + +# +# NV_CTRL_GVO_AUDIO_BLANKING - specifies whether the GVO device should delete +# audio ancillary data packets when frames are repeated. +# +# When a new frame is not ready in time, the current frame, including all +# ancillary data packets, is repeated. When this data includes audio packets, +# this can result in stutters or clicks. When this option is enabled, the GVO +# device will detect when frames are repeated, identify audio ancillary data +# packets, and mark them for deletion. +# +# This option is applied when the GVO device is bound. +# +NV_CTRL_GVO_AUDIO_BLANKING = 386 # RW- +NV_CTRL_GVO_AUDIO_BLANKING_DISABLE = 0 +NV_CTRL_GVO_AUDIO_BLANKING_ENABLE = 1 + +# +# NV_CTRL_CURRENT_METAMODE_ID - switch modes to the MetaMode with +# the specified ID. +# +NV_CTRL_CURRENT_METAMODE_ID = 387 # RW- + +# +# NV_CTRL_DISPLAY_ENABLED - Returns whether or not the display device +# is currently enabled. +# +NV_CTRL_DISPLAY_ENABLED = 388 # R-D +NV_CTRL_DISPLAY_ENABLED_TRUE = 1 +NV_CTRL_DISPLAY_ENABLED_FALSE = 0 + +# +# NV_CTRL_FRAMELOCK_INCOMING_HOUSE_SYNC_RATE: this is the rate +# of an incomming house sync signal to the frame lock board, in milliHz. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# +NV_CTRL_FRAMELOCK_INCOMING_HOUSE_SYNC_RATE = 389 # R--F + +# +# NV_CTRL_FXAA - enables FXAA. A pixel shader based anti- +# aliasing method. +# +NV_CTRL_FXAA = 390 # RW-X +NV_CTRL_FXAA_DISABLE = 0 +NV_CTRL_FXAA_ENABLE = 1 + +# +# NV_CTRL_DISPLAY_RANDR_OUTPUT_ID - the RandR Output ID (type RROutput) +# that corresponds to the specified Display Device target. If a new +# enough version of RandR is not available in the X server, +# DISPLAY_RANDR_OUTPUT_ID will be 0. +# +NV_CTRL_DISPLAY_RANDR_OUTPUT_ID = 391 # R-D- + +# +# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG - Configures whether the display device +# should listen, ignore or drive the framelock sync signal. +# +# Note that whether or not a display device may be set as a client/server +# depends on the current configuration. For example, only one server may be +# set per Quadro Sync device, and displays can only be configured as a client +# if their refresh rate sufficiently matches the refresh rate of the server +# device. +# +# Note that when querying the ValidValues for this data type, the values are +# reported as bits within a bitmask (ATTRIBUTE_TYPE_INT_BITS); +# +NV_CTRL_FRAMELOCK_DISPLAY_CONFIG = 392 # RWD +NV_CTRL_FRAMELOCK_DISPLAY_CONFIG_DISABLED = 0 +NV_CTRL_FRAMELOCK_DISPLAY_CONFIG_CLIENT = 1 +NV_CTRL_FRAMELOCK_DISPLAY_CONFIG_SERVER = 2 + +# +# NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY - Returns the total amount of dedicated +# GPU video memory, in MB, on the specified GPU. This excludes any TurboCache +# padding included in the value returned by NV_CTRL_TOTAL_GPU_MEMORY. +# +NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY = 393 # R--G + +# +# NV_CTRL_USED_DEDICATED_GPU_MEMORY- Returns the amount of video memory +# currently used on the graphics card in MB. +# +NV_CTRL_USED_DEDICATED_GPU_MEMORY = 394 # R--G + +# +# NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_IMMEDIATE +# Some GPUs can make a tradeoff between double-precision floating-point +# performance and clock speed. Enabling double-precision floating point +# performance may benefit CUDA or OpenGL applications that require high +# bandwidth double-precision performance. Disabling this feature may benefit +# graphics applications that require higher clock speeds. +# +# This attribute is only available when toggling double precision boost +# can be done immediately (without need for a rebooot). +# +NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_IMMEDIATE = 395 # RW-G +NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_IMMEDIATE_DISABLED = 0 +NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_IMMEDIATE_ENABLED = 1 + +# +# NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_REBOOT +# Some GPUs can make a tradeoff between double-precision floating-point +# performance and clock speed. Enabling double-precision floating point +# performance may benefit CUDA or OpenGL applications that require high +# bandwidth double-precision performance. Disabling this feature may benefit +# graphics applications that require higher clock speeds. +# +# This attribute is only available when toggling double precision boost +# requires a reboot. +# + +NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_REBOOT = 396 # RW-G +NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_REBOOT_DISABLED = 0 +NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_REBOOT_ENALED = 1 + +# +# NV_CTRL_DPY_HDMI_3D - Returns whether the specified display device is +# currently using HDMI 3D Frame Packed Stereo mode. Clients may use this +# to help interpret the refresh rate returned by NV_CTRL_REFRESH_RATE or +# NV_CTRL_REFRESH_RATE_3, which will be doubled when using HDMI 3D mode. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU target. +# + +NV_CTRL_DPY_HDMI_3D = 397 # R-DG +NV_CTRL_DPY_HDMI_3D_DISABLED = 0 +NV_CTRL_DPY_HDMI_3D_ENABLED = 1 + +# +# NV_CTRL_BASE_MOSAIC - Returns whether Base Mosaic is currently enabled on the +# given GPU. Querying the valid values of this attribute returns capabilities. +# + +NV_CTRL_BASE_MOSAIC = 398 # R--G +NV_CTRL_BASE_MOSAIC_DISABLED = 0 +NV_CTRL_BASE_MOSAIC_FULL = 1 +NV_CTRL_BASE_MOSAIC_LIMITED = 2 + +# +# NV_CTRL_MULTIGPU_MASTER_POSSIBLE - Returns whether the GPU can be configured +# as the master GPU in a Multi GPU configuration (SLI, SLI Mosaic, +# Base Mosaic). +# + +NV_CTRL_MULTIGPU_MASTER_POSSIBLE = 399 # R--G +NV_CTRL_MULTIGPU_MASTER_POSSIBLE_FALSE = 0 +NV_CTRL_MULTIGPU_MASTER_POSSIBLE_TRUE = 1 + +# +# NV_CTRL_GPU_POWER_MIZER_DEFAULT_MODE - Returns the default PowerMizer mode +# for the given GPU. +# +NV_CTRL_GPU_POWER_MIZER_DEFAULT_MODE = 400 # R--G + +# +# NV_CTRL_XV_SYNC_TO_DISPLAY_ID - When XVideo Sync To VBlank is enabled, this +# controls which display device will be synched to if the display is enabled. +# Returns NV_CTRL_XV_SYNC_TO_DISPLAY_ID_AUTO if no display has been +# selected. +# +NV_CTRL_XV_SYNC_TO_DISPLAY_ID = 401 # RW- +NV_CTRL_XV_SYNC_TO_DISPLAY_ID_AUTO = 0xFFFFFFFF + +# +# NV_CTRL_BACKLIGHT_BRIGHTNESS - The backlight brightness of an internal panel. +# +NV_CTRL_BACKLIGHT_BRIGHTNESS = 402 # RWD- + +# +# NV_CTRL_GPU_LOGO_BRIGHTNESS - Controls brightness +# of the logo on the GPU, if any. The value is variable from 0% - 100%. +# +NV_CTRL_GPU_LOGO_BRIGHTNESS = 403 # RW-G + +# +# NV_CTRL_GPU_SLI_LOGO_BRIGHTNESS - Controls brightness of the logo +# on the SLI bridge, if any. The value is variable from 0% - 100%. +# +NV_CTRL_GPU_SLI_LOGO_BRIGHTNESS = 404 # RW-G + +# +# NV_CTRL_THERMAL_COOLER_SPEED - Returns cooler's current operating speed in +# rotations per minute (RPM). +# + +NV_CTRL_THERMAL_COOLER_SPEED = 405 # R--C + +# +# NV_CTRL_PALETTE_UPDATE_EVENT - The Color Palette has been changed and the +# color correction info needs to be updated. +# + +NV_CTRL_PALETTE_UPDATE_EVENT = 406 # --- + +# +# NV_CTRL_VIDEO_ENCODER_UTILIZATION - Returns the video encoder engine +# utilization as a percentage. +# +NV_CTRL_VIDEO_ENCODER_UTILIZATION = 407 # R--G + +# +# NV_CTRL_GSYNC_ALLOWED - when TRUE, OpenGL will enable G-SYNC when possible; +# when FALSE, OpenGL will always use a fixed monitor refresh rate. +# + +NV_CTRL_GSYNC_ALLOWED = 408 # RW-X +NV_CTRL_GSYNC_ALLOWED_FALSE = 0 +NV_CTRL_GSYNC_ALLOWED_TRUE = 1 + +# +# NV_CTRL_GPU_NVCLOCK_OFFSET - This attribute controls the GPU clock offsets +# (in MHz) used for overclocking per performance level. +# Use the display_mask parameter to specify the performance level. +# +# Note: To enable overclocking support, set the X configuration +# option "Coolbits" to value "8". +# +# This offset can have any integer value between +# NVCTRLAttributeValidValues.u.range.min and +# NVCTRLAttributeValidValues.u.range.max (inclusive). +# +# This attribute is available on GeForce GTX 400 series and later +# Geforce GPUs. +# +NV_CTRL_GPU_NVCLOCK_OFFSET = 409 # RW-G + +# +# NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET - This attribute controls +# the memory transfer rate offsets (in MHz) used for overclocking +# per performance level. +# Use the display_mask parameter to specify the performance level. +# +# Note: To enable overclocking support, set the X configuration +# option "Coolbits" to value "8". +# +# This offset can have any integer value between +# NVCTRLAttributeValidValues.u.range.min and +# NVCTRLAttributeValidValues.u.range.max (inclusive). +# +# This attribute is available on GeForce GTX 400 series and later +# Geforce GPUs. +# +NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET = 410 # RW-G + +# +# NV_CTRL_VIDEO_DECODER_UTILIZATION - Returns the video decoder engine +# utilization as a percentage. +# +NV_CTRL_VIDEO_DECODER_UTILIZATION = 411 # R--G + +# +# NV_CTRL_GPU_OVER_VOLTAGE_OFFSET - This attribute controls +# the overvoltage offset in microvolts (uV). +# +# Note: To enable overvoltage support, set the X configuration +# option "Coolbits" to value "16". +# +# This offset can have any integer value between +# NVCTRLAttributeValidValues.u.range.min and +# NVCTRLAttributeValidValues.u.range.max (inclusive). +# +# This attribute is available on GeForce GTX 400 series and later +# Geforce GPUs. +# + +NV_CTRL_GPU_OVER_VOLTAGE_OFFSET = 412 # RW-G + +# +# NV_CTRL_GPU_CURRENT_CORE_VOLTAGE - This attribute returns the +# GPU's current operating voltage in microvolts (uV). +# +# This attribute is available on GPUs that support +# NV_CTRL_GPU_OVER_VOLTAGE_OFFSET. +# +NV_CTRL_GPU_CURRENT_CORE_VOLTAGE = 413 # R--G + +# +# NV_CTRL_CURRENT_COLOR_SPACE - Returns the current color space of the video +# signal. +# +# This will match NV_CTRL_COLOR_SPACE unless the current mode on this display +# device is an HDMI 2.0 4K@60Hz mode and the display device or GPU does not +# support driving this mode in RGB, in which case YCbCr420 will be returned. +# +NV_CTRL_CURRENT_COLOR_SPACE = 414 # R-DG +NV_CTRL_CURRENT_COLOR_SPACE_RGB = 0 +NV_CTRL_CURRENT_COLOR_SPACE_YCbCr422 = 1 +NV_CTRL_CURRENT_COLOR_SPACE_YCbCr444 = 2 +NV_CTRL_CURRENT_COLOR_SPACE_YCbCr420 = 3 + +# +# NV_CTRL_CURRENT_COLOR_RANGE - Returns the current color range of the video +# signal. +# +NV_CTRL_CURRENT_COLOR_RANGE = 415 # R-DG +NV_CTRL_CURRENT_COLOR_RANGE_FULL = 0 +NV_CTRL_CURRENT_COLOR_RANGE_LIMITED = 1 + +# +# NV_CTRL_SHOW_GSYNC_VISUAL_INDICATOR - when TRUE, OpenGL will indicate when +# G-SYNC is in use for full-screen applications. +# + +NV_CTRL_SHOW_GSYNC_VISUAL_INDICATOR = 416 # RW-X +NV_CTRL_SHOW_GSYNC_VISUAL_INDICATOR_FALSE = 0 +NV_CTRL_SHOW_GSYNC_VISUAL_INDICATOR_TRUE = 1 + +# +# NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL - Returns cooler's current +# operating level. This may fluctuate dynamically. When +# NV_CTRL_GPU_COOLER_MANUAL_CONTROL=TRUE, the driver attempts +# to make this match NV_CTRL_THERMAL_COOLER_LEVEL. When +# NV_CTRL_GPU_COOLER_MANUAL_CONTROL=FALSE, the driver adjusts the +# current level based on the needs of the GPU. +# + +NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL = 417 # R--C + +# +# NV_CTRL_STEREO_SWAP_MODE - This attribute controls the swap mode when +# Quad-Buffered stereo is used. +# NV_CTRL_STEREO_SWAP_MODE_APPLICATION_CONTROL : Stereo swap mode is derived +# from the value of swap interval. +# If it's odd, the per eye swap mode is used. +# If it's even, the per eye pair swap mode is used. +# NV_CTRL_STEREO_SWAP_MODE_PER_EYE : The driver swaps each eye as it is ready. +# NV_CTRL_STEREO_SWAP_MODE_PER_EYE_PAIR : The driver waits for both eyes to +# complete rendering before swapping. +# + +NV_CTRL_STEREO_SWAP_MODE = 418 # RW-X +NV_CTRL_STEREO_SWAP_MODE_APPLICATION_CONTROL = 0 +NV_CTRL_STEREO_SWAP_MODE_PER_EYE = 1 +NV_CTRL_STEREO_SWAP_MODE_PER_EYE_PAIR = 2 + +# +# NV_CTRL_CURRENT_XV_SYNC_TO_DISPLAY_ID - When XVideo Sync To VBlank is +# enabled, this returns the display id of the device currently synched to. +# Returns NV_CTRL_XV_SYNC_TO_DISPLAY_ID_AUTO if no display is currently +# set. +# + +NV_CTRL_CURRENT_XV_SYNC_TO_DISPLAY_ID = 419 # R-- + +# +# NV_CTRL_GPU_FRAMELOCK_FIRMWARE_UNSUPPORTED - Returns true if the +# Quadro Sync card connected to this GPU has a firmware version incompatible +# with this GPU. +# + +NV_CTRL_GPU_FRAMELOCK_FIRMWARE_UNSUPPORTED = 420 # R--G +NV_CTRL_GPU_FRAMELOCK_FIRMWARE_UNSUPPORTED_FALSE = 0 +NV_CTRL_GPU_FRAMELOCK_FIRMWARE_UNSUPPORTED_TRUE = 1 + +# +# NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE - Returns the connector type used by +# a DisplayPort display. +# + +NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE = 421 # R-DG +NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_UNKNOWN = 0 +NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_DISPLAYPORT = 1 +NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_HDMI = 2 +NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_DVI = 3 +NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_VGA = 4 + +# +# NV_CTRL_DISPLAYPORT_IS_MULTISTREAM - Returns multi-stream support for +# DisplayPort displays. +# +NV_CTRL_DISPLAYPORT_IS_MULTISTREAM = 422 # R-DG + +# +# NV_CTRL_DISPLAYPORT_SINK_IS_AUDIO_CAPABLE - Returns whether a DisplayPort +# device supports audio. +# +NV_CTRL_DISPLAYPORT_SINK_IS_AUDIO_CAPABLE = 423 # R-DG + +# +# NV_CTRL_GPU_NVCLOCK_OFFSET_ALL_PERFORMANCE_LEVELS - This attribute +# controls the GPU clock offsets (in MHz) used for overclocking. +# The offset is applied to all performance levels. +# +# Note: To enable overclocking support, set the X configuration +# option "Coolbits" to value "8". +# +# This offset can have any integer value between +# NVCTRLAttributeValidValues.u.range.min and +# NVCTRLAttributeValidValues.u.range.max (inclusive). +# +# This attribute is available on GeForce GTX 1000 series and later +# Geforce GPUs. +# +NV_CTRL_GPU_NVCLOCK_OFFSET_ALL_PERFORMANCE_LEVELS = 424 # RW-G + +# +# NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET_ALL_PERFORMANCE_LEVELS - This +# attribute controls the memory transfer rate offsets (in MHz) used +# for overclocking. The offset is applied to all performance levels. +# +# Note: To enable overclocking support, set the X configuration +# option "Coolbits" to value "8". +# +# This offset can have any integer value between +# NVCTRLAttributeValidValues.u.range.min and +# NVCTRLAttributeValidValues.u.range.max (inclusive). +# +# This attribute is available on GeForce GTX 1000 series and later +# Geforce GPUs. +# +NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET_ALL_PERFORMANCE_LEVELS = 425 # RW-G + +# +# NV_CTRL_FRAMELOCK_FIRMWARE_VERSION - Queries the firmware major version of +# the Frame Lock device. +# +# This attribute must be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK target. +# + +NV_CTRL_FRAMELOCK_FIRMWARE_VERSION = 426 # R--F + +# +# NV_CTRL_FRAMELOCK_FIRMWARE_MINOR_VERSION - Queries the firmware minor +# version of the Frame Lock device. +# +# This attribute must be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK target. +# + +NV_CTRL_FRAMELOCK_FIRMWARE_MINOR_VERSION = 427 # R--F + +# +# NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR - when TRUE, graphics APIs will +# indicate various runtime information such as flip/blit, vsync status, API +# in use. +# + +NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR = 428 # RW-X +NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR_FALSE = 0 +NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR_TRUE = 1 + +NV_CTRL_LAST_ATTRIBUTE = NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR + +############################################################################ + +# +# String Attributes: +# +# String attributes can be queryied through the XNVCTRLQueryStringAttribute() +# and XNVCTRLQueryTargetStringAttribute() function calls. +# +# String attributes can be set through the XNVCTRLSetStringAttribute() +# function call. (There are currently no string attributes that can be +# set on non-X Screen targets.) +# +# Unless otherwise noted, all string attributes can be queried/set using an +# NV_CTRL_TARGET_TYPE_X_SCREEN target. Attributes that cannot take an +# NV_CTRL_TARGET_TYPE_X_SCREEN target also cannot be queried/set through +# XNVCTRLQueryStringAttribute()/XNVCTRLSetStringAttribute() (Since +# these assume an X Screen target). +# + + +# +# NV_CTRL_STRING_PRODUCT_NAME - the product name on which the +# specified X screen is running, or the product name of the specified +# Frame Lock device. +# +# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target to +# return the product name of the GPU, or a NV_CTRL_TARGET_TYPE_FRAMELOCK to +# return the product name of the Frame Lock device. +# + +NV_CTRL_STRING_PRODUCT_NAME = 0 # R--GF + +# +# NV_CTRL_STRING_VBIOS_VERSION - the video bios version on the GPU on +# which the specified X screen is running. +# + +NV_CTRL_STRING_VBIOS_VERSION = 1 # R--G + +# +# NV_CTRL_STRING_NVIDIA_DRIVER_VERSION - string representation of the +# NVIDIA driver version number for the NVIDIA X driver in use. +# + +NV_CTRL_STRING_NVIDIA_DRIVER_VERSION = 3 # R--G + +# +# NV_CTRL_STRING_DISPLAY_DEVICE_NAME - name of the display device +# specified in the display_mask argument. +# +# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_STRING_DISPLAY_DEVICE_NAME = 4 # R-DG + +# +# NV_CTRL_STRING_TV_ENCODER_NAME - not supported +# + +NV_CTRL_STRING_TV_ENCODER_NAME = 5 # not supported + +# +# NV_CTRL_STRING_GVIO_FIRMWARE_VERSION - indicates the version of the +# Firmware on the GVIO device. +# + +NV_CTRL_STRING_GVIO_FIRMWARE_VERSION = 8 # R--I + +# +# NV_CTRL_STRING_GVO_FIRMWARE_VERSION - renamed +# +# NV_CTRL_STRING_GVIO_FIRMWARE_VERSION should be used instead. +# +NV_CTRL_STRING_GVO_FIRMWARE_VERSION = 8 # renamed + +# +# NV_CTRL_STRING_CURRENT_MODELINE - Return the ModeLine currently +# being used by the specified display device. +# +# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() +# using an NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# +# The ModeLine string may be prepended with a comma-separated list of +# "token=value" pairs, separated from the ModeLine string by "::". +# This "token=value" syntax is the same as that used in +# NV_CTRL_BINARY_DATA_MODELINES +# + +NV_CTRL_STRING_CURRENT_MODELINE = 9 # R-DG + +# +# NV_CTRL_STRING_ADD_MODELINE - Adds a ModeLine to the specified +# display device. The ModeLine is not added if validation fails. +# +# The ModeLine string should have the same syntax as a ModeLine in +# the X configuration file; e.g., +# +# "1600x1200" 229.5 1600 1664 1856 2160 1200 1201 1204 1250 +HSync +VSync +# + +NV_CTRL_STRING_ADD_MODELINE = 10 # -WDG + +# +# NV_CTRL_STRING_DELETE_MODELINE - Deletes an existing ModeLine +# from the specified display device. The currently selected +# ModeLine cannot be deleted. (This also means you cannot delete +# the last ModeLine.) +# +# The ModeLine string should have the same syntax as a ModeLine in +# the X configuration file; e.g., +# +# "1600x1200" 229.5 1600 1664 1856 2160 1200 1201 1204 1250 +HSync +VSync +# + +NV_CTRL_STRING_DELETE_MODELINE = 11 # -WDG + +# +# NV_CTRL_STRING_CURRENT_METAMODE - Returns the metamode currently +# being used by the specified X screen. The MetaMode string has the +# same syntax as the MetaMode X configuration option, as documented +# in the NVIDIA driver README. +# +# The returned string may be prepended with a comma-separated list of +# "token=value" pairs, separated from the MetaMode string by "::". +# This "token=value" syntax is the same as that used in +# NV_CTRL_BINARY_DATA_METAMODES. +# + +NV_CTRL_STRING_CURRENT_METAMODE = 12 # RW-- +NV_CTRL_STRING_CURRENT_METAMODE_VERSION_1 = NV_CTRL_STRING_CURRENT_METAMODE + +# +# NV_CTRL_STRING_ADD_METAMODE - Adds a MetaMode to the specified +# X Screen. +# +# It is recommended to not use this attribute, but instead use +# NV_CTRL_STRING_OPERATION_ADD_METAMODE. +# + +NV_CTRL_STRING_ADD_METAMODE = 13 # -W-- + +# +# NV_CTRL_STRING_DELETE_METAMODE - Deletes an existing MetaMode from +# the specified X Screen. The currently selected MetaMode cannot be +# deleted. (This also means you cannot delete the last MetaMode). +# The MetaMode string should have the same syntax as the MetaMode X +# configuration option, as documented in the NVIDIA driver README. +# + +NV_CTRL_STRING_DELETE_METAMODE = 14 # -WD-- + +# +# NV_CTRL_STRING_VCSC_PRODUCT_NAME - deprecated +# +# Queries the product name of the VCSC device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_PRODUCT_NAME = 15 # R---V + +# +# NV_CTRL_STRING_VCSC_PRODUCT_ID - deprecated +# +# Queries the product ID of the VCSC device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_PRODUCT_ID = 16 # R---V + +# +# NV_CTRL_STRING_VCSC_SERIAL_NUMBER - deprecated +# +# Queries the unique serial number of the VCS device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_SERIAL_NUMBER = 17 # R---V + +# +# NV_CTRL_STRING_VCSC_BUILD_DATE - deprecated +# +# Queries the date of the VCS device. the returned string is in the following +# format: "Week.Year" +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_BUILD_DATE = 18 # R---V + +# +# NV_CTRL_STRING_VCSC_FIRMWARE_VERSION - deprecated +# +# Queries the firmware version of the VCS device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_FIRMWARE_VERSION = 19 # R---V + +# +# NV_CTRL_STRING_VCSC_FIRMWARE_REVISION - deprecated +# +# Queries the firmware revision of the VCS device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCS target. +# + +NV_CTRL_STRING_VCSC_FIRMWARE_REVISION = 20 # R---V + +# +# NV_CTRL_STRING_VCSC_HARDWARE_VERSION - deprecated +# +# Queries the hardware version of the VCS device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_HARDWARE_VERSION = 21 # R---V + +# +# NV_CTRL_STRING_VCSC_HARDWARE_REVISION - deprecated +# +# Queries the hardware revision of the VCS device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_HARDWARE_REVISION = 22 # R---V + +# +# NV_CTRL_STRING_MOVE_METAMODE - Moves a MetaMode to the specified +# index location. The MetaMode must already exist in the X Screen's +# list of MetaModes (as returned by the NV_CTRL_BINARY_DATA_METAMODES +# attribute). If the index is larger than the number of MetaModes in +# the list, the MetaMode is moved to the end of the list. The +# MetaMode string should have the same syntax as the MetaMode X +# configuration option, as documented in the NVIDIA driver README. + +# The MetaMode string must be prepended with a comma-separated list +# of "token=value" pairs, separated from the MetaMode string by "::". +# Currently, the only valid token is "index", which indicates where +# in the MetaMode list the MetaMode should be moved to. +# +# Other tokens may be added in the future. +# +# E.g., +# "index=5 :: CRT-0: 1024x768 @1024x768 +0+0" +# + +NV_CTRL_STRING_MOVE_METAMODE = 23 # -W-- + +# +# NV_CTRL_STRING_VALID_HORIZ_SYNC_RANGES - returns the valid +# horizontal sync ranges used to perform mode validation for the +# specified display device. The ranges are in the same format as the +# "HorizSync" X config option: +# +# "horizsync-range may be a comma separated list of either discrete +# values or ranges of values. A range of values is two values +# separated by a dash." +# +# The values are in kHz. +# +# Additionally, the string may be prepended with a comma-separated +# list of "token=value" pairs, separated from the HorizSync string by +# "::". Valid tokens: +# +# Token Value +# "source" "edid" - HorizSync is from the display device's EDID +# "xconfig" - HorizSync is from the "HorizSync" entry in +# the Monitor section of the X config file +# "option" - HorizSync is from the "HorizSync" NVIDIA X +# config option +# "builtin" - HorizSync is from NVIDIA X driver builtin +# default values +# +# Additional tokens and/or values may be added in the future. +# +# Example: "source=edid :: 30.000-62.000" +# + +NV_CTRL_STRING_VALID_HORIZ_SYNC_RANGES = 24 # R-DG + +# +# NV_CTRL_STRING_VALID_VERT_REFRESH_RANGES - returns the valid +# vertical refresh ranges used to perform mode validation for the +# specified display device. The ranges are in the same format as the +# "VertRefresh" X config option: +# +# "vertrefresh-range may be a comma separated list of either discrete +# values or ranges of values. A range of values is two values +# separated by a dash." +# +# The values are in Hz. +# +# Additionally, the string may be prepended with a comma-separated +# list of "token=value" pairs, separated from the VertRefresh string by +# "::". Valid tokens: +# +# Token Value +# "source" "edid" - VertRefresh is from the display device's EDID +# "xconfig" - VertRefresh is from the "VertRefresh" entry in +# the Monitor section of the X config file +# "option" - VertRefresh is from the "VertRefresh" NVIDIA X +# config option +# "builtin" - VertRefresh is from NVIDIA X driver builtin +# default values +# +# Additional tokens and/or values may be added in the future. +# +# Example: "source=edid :: 50.000-75.000" +# + +NV_CTRL_STRING_VALID_VERT_REFRESH_RANGES = 25 # R-DG + +# +# NV_CTRL_STRING_SCREEN_RECTANGLE - returns the physical X Screen's +# initial position and size (in absolute coordinates) within the +# desktop as the "token=value" string: "x=#, y=#, width=#, height=#" +# +# Querying this attribute returns success only when Xinerama is enabled +# or the X server ABI is greater than equal to 12. +# + +NV_CTRL_STRING_SCREEN_RECTANGLE = 26 # R--- + +# +# NV_CTRL_STRING_XINERAMA_SCREEN_INFO - renamed +# +# NV_CTRL_STRING_SCREEN_RECTANGLE should be used instead. +# + +NV_CTRL_STRING_XINERAMA_SCREEN_INFO = 26 # renamed + +# +# NV_CTRL_STRING_TWINVIEW_XINERAMA_INFO_ORDER - used to specify the +# order that display devices will be returned via Xinerama when +# nvidiaXineramaInfo is enabled. Follows the same syntax as the +# nvidiaXineramaInfoOrder X config option. +# + +NV_CTRL_STRING_NVIDIA_XINERAMA_INFO_ORDER = 27 # RW-- + +NV_CTRL_STRING_TWINVIEW_XINERAMA_INFO_ORDER = NV_CTRL_STRING_NVIDIA_XINERAMA_INFO_ORDER # for backwards compatibility: + +# +# NV_CTRL_STRING_SLI_MODE - returns a string describing the current +# SLI mode, if any, or FALSE if SLI is not currently enabled. +# +# This string should be used for informational purposes only, and +# should not be used to distinguish between SLI modes, other than to +# recognize when SLI is disabled (FALSE is returned) or +# enabled (the returned string is non-NULL and describes the current +# SLI configuration). +# + +NV_CTRL_STRING_SLI_MODE = 28 # R---*/ + +# +# NV_CTRL_STRING_PERFORMANCE_MODES - returns a string with all the +# performance modes defined for this GPU along with their associated +# NV Clock and Memory Clock values. +# Not all tokens will be reported on all GPUs, and additional tokens +# may be added in the future. +# For backwards compatibility we still provide nvclock, memclock, and +# processorclock those are the same as nvclockmin, memclockmin and +# processorclockmin. +# +# Note: These clock values take into account the offset +# set by clients through NV_CTRL_GPU_NVCLOCK_OFFSET and +# NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET. +# +# Each performance modes are returned as a comma-separated list of +# "token=value" pairs. Each set of performance mode tokens are separated +# by a ";". Valid tokens: +# +# Token Value +# "perf" integer - the Performance level +# "nvclock" integer - the GPU clocks (in MHz) for the perf level +# "nvclockmin" integer - the GPU clocks min (in MHz) for the perf level +# "nvclockmax" integer - the GPU clocks max (in MHz) for the perf level +# "nvclockeditable" integer - if the GPU clock domain is editable +# for the perf level +# "memclock" integer - the memory clocks (in MHz) for the perf level +# "memclockmin" integer - the memory clocks min (in MHz) for the perf level +# "memclockmax" integer - the memory clocks max (in MHz) for the perf level +# "memclockeditable" integer - if the memory clock domain is editable +# for the perf level +# "memtransferrate" integer - the memory transfer rate (in MHz) +# for the perf level +# "memtransferratemin" integer - the memory transfer rate min (in MHz) +# for the perf level +# "memtransferratemax" integer - the memory transfer rate max (in MHz) +# for the perf level +# "memtransferrateeditable" integer - if the memory transfer rate is editable +# for the perf level +# "processorclock" integer - the processor clocks (in MHz) +# for the perf level +# "processorclockmin" integer - the processor clocks min (in MHz) +# for the perf level +# "processorclockmax" integer - the processor clocks max (in MHz) +# for the perf level +# "processorclockeditable" integer - if the processor clock domain is editable +# for the perf level +# +# Example: +# +# perf=0, nvclock=324, nvclockmin=324, nvclockmax=324, nvclockeditable=0, +# memclock=324, memclockmin=324, memclockmax=324, memclockeditable=0, +# memtransferrate=648, memtransferratemin=648, memtransferratemax=648, +# memtransferrateeditable=0 ; +# perf=1, nvclock=324, nvclockmin=324, nvclockmax=640, nvclockeditable=0, +# memclock=810, memclockmin=810, memclockmax=810, memclockeditable=0, +# memtransferrate=1620, memtransferrate=1620, memtransferrate=1620, +# memtransferrateeditable=0 ; +# +# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_STRING_PERFORMANCE_MODES = 29 # R--G + +# +# NV_CTRL_STRING_VCSC_FAN_STATUS - deprecated +# +# Returns a string with status of all the fans in the Visual Computing System, +# if such a query is supported. Fan information is reported along with its +# tachometer reading (in RPM) and a flag indicating whether the fan has failed +# or not. +# +# Valid tokens: +# +# Token Value +# "fan" integer - the Fan index +# "speed" integer - the tachometer reading of the fan in rpm +# "fail" integer - flag to indicate whether the fan has failed +# +# Example: +# +# fan=0, speed=694, fail=0 ; fan=1, speed=693, fail=0 +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# +# + +NV_CTRL_STRING_VCSC_FAN_STATUS = 30 # R---V + +# +# NV_CTRL_STRING_VCSC_TEMPERATURES - Deprecated +# +# Returns a string with all Temperature readings in the Visual Computing +# System, if such a query is supported. Intake, Exhaust and Board Temperature +# values are reported in Celcius. +# +# Valid tokens: +# +# Token Value +# "intake" integer - the intake temperature for the VCS +# "exhaust" integer - the exhaust temperature for the VCS +# "board" integer - the board temperature of the VCS +# +# Example: +# +# intake=29, exhaust=46, board=41 +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# +# + +NV_CTRL_STRING_VCSC_TEMPERATURES = 31 # R---V + +# +# NV_CTRL_STRING_VCSC_PSU_INFO - Deprecated +# +# Returns a string with all Power Supply Unit related readings in the Visual +# Computing System, if such a query is supported. Current in amperes, Power +# in watts, Voltage in volts and PSU state may be reported. Not all PSU types +# support all of these values, and therefore some readings may be unknown. +# +# Valid tokens: +# +# Token Value +# "current" integer - the current drawn in amperes by the VCS +# "power" integer - the power drawn in watts by the VCS +# "voltage" integer - the voltage reading of the VCS +# "state" integer - flag to indicate whether PSU is operating normally +# +# Example: +# +# current=10, power=15, voltage=unknown, state=normal +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# +# + + +NV_CTRL_STRING_VCSC_PSU_INFO = 32 # R---V + +# +# NV_CTRL_STRING_GVIO_VIDEO_FORMAT_NAME - query the name for the specified +# NV_CTRL_GVIO_VIDEO_FORMAT_*. So that this can be queried with existing +# interfaces, XNVCTRLQueryStringAttribute() should be used, and the video +# format specified in the display_mask field; eg: +# +# XNVCTRLQueryStringAttribute(dpy, +# screen, +# NV_CTRL_GVIO_VIDEO_FORMAT_720P_60_00_SMPTE296, +# NV_CTRL_GVIO_VIDEO_FORMAT_NAME, +# &name); +# + +NV_CTRL_STRING_GVIO_VIDEO_FORMAT_NAME = 33 # R--GI + +# +# NV_CTRL_STRING_GVO_VIDEO_FORMAT_NAME - renamed +# +# NV_CTRL_STRING_GVIO_VIDEO_FORMAT_NAME should be used instead. +# +NV_CTRL_STRING_GVO_VIDEO_FORMAT_NAME = 33 # renamed + +# +# NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS - returns a string with the +# associated NV Clock, Memory Clock and Processor Clock values. +# +# Current valid tokens are "nvclock", "nvclockmin", "nvclockmax", +# "memclock", "memclockmin", "memclockmax", "processorclock", +# "processorclockmin" and "processorclockmax". +# Not all tokens will be reported on all GPUs, and additional tokens +# may be added in the future. +# +# Note: These clock values take into account the offset +# set by clients through NV_CTRL_GPU_NVCLOCK_OFFSET and +# NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET. +# +# Clock values are returned as a comma-separated list of +# "token=value" pairs. +# Valid tokens: +# +# Token Value +# "nvclock" integer - the GPU clocks (in MHz) for the perf level +# "nvclockmin" integer - the GPU clocks min (in MHz) for the perf level +# "nvclockmax" integer - the GPU clocks max (in MHz) for the perf level +# "nvclockeditable" integer - if the GPU clock domain is editable +# for the perf level +# "memclock" integer - the memory clocks (in MHz) for the perf level +# "memclockmin" integer - the memory clocks min (in MHz) for the perf level +# "memclockmax" integer - the memory clocks (max in MHz) for the perf level +# "memclockeditable" integer - if the memory clock domain is editable +# for the perf level +# "memtransferrate" integer - the memory transfer rate (in MHz) +# for the perf level +# "memtransferratemin" integer - the memory transfer rate min (in MHz) +# for the perf level +# "memtransferratemax" integer - the memory transfer rate max (in MHz) +# for the perf level +# "memtransferrateeditable" integer - if the memory transfer rate is editable +# for the perf level +# "processorclock" integer - the processor clocks (in MHz) +# for the perf level +# "processorclockmin" integer - the processor clocks min (in MHz) +# for the perf level +# "processorclockmax" integer - the processor clocks max (in MHz) +# for the perf level +# "processorclockeditable" integer - if the processor clock domain is editable +# for the perf level +# +# Example: +# +# nvclock=324, nvclockmin=324, nvclockmax=324, nvclockeditable=0 +# memclock=324, memclockmin=324, memclockmax=324, memclockeditable=0 +# memtrasferrate=628 +# +# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() +# using an NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS = 34 # RW-G + +# +# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_HARDWARE_REVISION - Returns the +# hardware revision of the 3D Vision Pro transceiver. +# +NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_HARDWARE_REVISION = 35 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_VERSION_A - Returns the +# firmware version of chip A of the 3D Vision Pro transceiver. +# +NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_VERSION_A = 36 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_DATE_A - Returns the +# date of the firmware of chip A of the 3D Vision Pro transceiver. +# +NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_DATE_A = 37 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_VERSION_B - Returns the +# firmware version of chip B of the 3D Vision Pro transceiver. +# +NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_VERSION_B = 38 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_DATE_B - Returns the +# date of the firmware of chip B of the 3D Vision Pro transceiver. +# +NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_DATE_B = 39 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_ADDRESS - Returns the RF address +# of the 3D Vision Pro transceiver. +# +NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_ADDRESS = 40 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_GLASSES_FIRMWARE_VERSION_A - Returns the +# firmware version of chip A of the glasses. +# Use the display_mask parameter to specify the glasses id. +# +NV_CTRL_STRING_3D_VISION_PRO_GLASSES_FIRMWARE_VERSION_A = 41 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_GLASSES_FIRMWARE_DATE_A - Returns the +# date of the firmware of chip A of the glasses. +# Use the display_mask parameter to specify the glasses id. +# +NV_CTRL_STRING_3D_VISION_PRO_GLASSES_FIRMWARE_DATE_A = 42 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_GLASSES_ADDRESS - Returns the RF address +# of the glasses. +# Use the display_mask parameter to specify the glasses id. +# +NV_CTRL_STRING_3D_VISION_PRO_GLASSES_ADDRESS = 43 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME - Controls the name the +# glasses should use. +# Use the display_mask parameter to specify the glasses id. +# Glasses' name should start and end with an alpha-numeric character. +# +NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME = 44 # RW-T + +# +# NV_CTRL_STRING_CURRENT_METAMODE_VERSION_2 - Returns the metamode currently +# being used by the specified X screen. The MetaMode string has the same +# syntax as the MetaMode X configuration option, as documented in the NVIDIA +# driver README. Also, see NV_CTRL_BINARY_DATA_METAMODES_VERSION_2 for more +# details on the base syntax. +# +# The returned string may also be prepended with a comma-separated list of +# "token=value" pairs, separated from the MetaMode string by "::". +# +NV_CTRL_STRING_CURRENT_METAMODE_VERSION_2 = 45 # RW-- + +# +# NV_CTRL_STRING_DISPLAY_NAME_TYPE_BASENAME - Returns a type name for the +# display device ("CRT", "DFP", or "TV"). However, note that the determination +# of the name is based on the protocol through which the X driver communicates +# to the display device. E.g., if the driver communicates using VGA ,then the +# basename is "CRT"; if the driver communicates using TMDS, LVDS, or DP, then +# the name is "DFP". +# +NV_CTRL_STRING_DISPLAY_NAME_TYPE_BASENAME = 46 # R-D- + +# +# NV_CTRL_STRING_DISPLAY_NAME_TYPE_ID - Returns the type-based name + ID for +# the display device, e.g. "CRT-0", "DFP-1", "TV-2". If this device is a +# DisplayPort multistream device, then this name will also be prepended with the +# device's port address like so: "DFP-1.0.1.2.3". See +# NV_CTRL_STRING_DISPLAY_NAME_TYPE_BASENAME for more information about the +# construction of type-based names. +# +NV_CTRL_STRING_DISPLAY_NAME_TYPE_ID = 47 # R-D- + +# +# NV_CTRL_STRING_DISPLAY_NAME_DP_GUID - Returns the GUID of the DisplayPort +# display device. e.g. "DP-GUID-f16a5bde-79f3-11e1-b2ae-8b5a8969ba9c" +# +# The display device must be a DisplayPort 1.2 device. +# +NV_CTRL_STRING_DISPLAY_NAME_DP_GUID = 48 # R-D- + +# +# NV_CTRL_STRING_DISPLAY_NAME_EDID_HASH - Returns the SHA-1 hash of the +# display device's EDID in 8-4-4-4-12 UID format. e.g. +# "DPY-EDID-f16a5bde-79f3-11e1-b2ae-8b5a8969ba9c" +# +# The display device must have a valid EDID. +# +NV_CTRL_STRING_DISPLAY_NAME_EDID_HASH = 49 # R-D- + +# +# NV_CTRL_STRING_DISPLAY_NAME_TARGET_INDEX - Returns the current NV-CONTROL +# target ID (name) of the display device. e.g. "DPY-1", "DPY-4" +# +# This name for the display device is not guarenteed to be the same between +# different runs of the X server. +# +NV_CTRL_STRING_DISPLAY_NAME_TARGET_INDEX = 50 # R-D- + +# +# NV_CTRL_STRING_DISPLAY_NAME_RANDR - Returns the RandR output name for the +# display device. e.g. "VGA-1", "DVI-I-0", "DVI-D-3", "LVDS-1", "DP-2", +# "HDMI-3", "eDP-6". This name should match If this device is a DisplayPort +# 1.2 device, then this name will also be prepended with the device's port +# address like so: "DVI-I-3.0.1.2.3" +# +NV_CTRL_STRING_DISPLAY_NAME_RANDR = 51 # R-D- + +# +# NV_CTRL_STRING_GPU_UUID - Returns the UUID of the given GPU. +# +NV_CTRL_STRING_GPU_UUID = 52 # R--G + +# +# NV_CTRL_STRING_GPU_UTILIZATION - Returns the current percentage usage +# of the various components of the GPU. +# +# Current valid tokens are "graphics", "memory", "video" and "PCIe". +# Not all tokens will be reported on all GPUs, and additional tokens +# may be added in the future. +# +# Utilization values are returned as a comma-separated list of +# "token=value" pairs. +# Valid tokens: +# +# Token Value +# "graphics" integer - the percentage usage of graphics engine. +# "memory" integer - the percentage usage of FB. +# "video" integer - the percentage usage of video engine. +# "PCIe" integer - the percentage usage of PCIe bandwidth. +# +# +# Example: +# +# graphics=45, memory=6, video=0, PCIe=0 +# +# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() +# using an NV_CTRL_TARGET_TYPE_GPU. +# +NV_CTRL_STRING_GPU_UTILIZATION = 53 # R--G + +# +# NV_CTRL_STRING_MULTIGPU_MODE - returns a string describing the current +# MULTIGPU mode, if any, or FALSE if MULTIGPU is not currently enabled. +# +NV_CTRL_STRING_MULTIGPU_MODE = 54 # R--- + +# +# NV_CTRL_STRING_PRIME_OUTPUTS_DATA - returns a semicolon delimited list of +# strings that describe all PRIME configured displays. +# +# ex. "xpos=1920, ypos=0, width=1280, height=1024, screen=0;xpos=3200, +# ypos=0, width=800, height=600, screen=0;" +# +NV_CTRL_STRING_PRIME_OUTPUTS_DATA = 55 # R--- + +NV_CTRL_STRING_LAST_ATTRIBUTE = NV_CTRL_STRING_PRIME_OUTPUTS_DATA + +############################################################################ + +# +# Binary Data Attributes: +# +# Binary data attributes can be queryied through the XNVCTRLQueryBinaryData() +# and XNVCTRLQueryTargetBinaryData() function calls. +# +# There are currently no binary data attributes that can be set. +# +# Unless otherwise noted, all Binary data attributes can be queried +# using an NV_CTRL_TARGET_TYPE_X_SCREEN target. Attributes that cannot take +# an NV_CTRL_TARGET_TYPE_X_SCREEN target also cannot be queried through +# XNVCTRLQueryBinaryData() (Since an X Screen target is assumed). +# + + +# +# NV_CTRL_BINARY_DATA_EDID - Returns a display device's EDID information +# data. +# +# This attribute may be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_BINARY_DATA_EDID = 0 # R-DG + +# +# NV_CTRL_BINARY_DATA_MODELINES - Returns a display device's supported +# ModeLines. ModeLines are returned in a buffer, separated by a single +# '\0' and terminated by two consecutive '\0' s like so: +# +# "ModeLine 1\0ModeLine 2\0ModeLine 3\0Last ModeLine\0\0" +# +# This attribute may be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# +# Each ModeLine string may be prepended with a comma-separated list +# of "token=value" pairs, separated from the ModeLine string with a +# "::". Valid tokens: +# +# Token Value +# "source" "xserver" - the ModeLine is from the core X server +# "xconfig" - the ModeLine was specified in the X config file +# "builtin" - the NVIDIA driver provided this builtin ModeLine +# "vesa" - this is a VESA standard ModeLine +# "edid" - the ModeLine was in the display device's EDID +# "nv-control" - the ModeLine was specified via NV-CONTROL +# +# "xconfig-name" - for ModeLines that were specified in the X config +# file, this is the name the X config file +# gave for the ModeLine. +# +# Note that a ModeLine can have several sources; the "source" token +# can appear multiple times in the "token=value" pairs list. +# Additional source values may be specified in the future. +# +# Additional tokens may be added in the future, so it is recommended +# that any token parser processing the returned string from +# NV_CTRL_BINARY_DATA_MODELINES be implemented to gracefully ignore +# unrecognized tokens. +# +# E.g., +# +# "source=xserver, source=vesa, source=edid :: "1024x768_70" 75.0 1024 1048 1184 1328 768 771 777 806 -HSync -VSync" +# "source=xconfig, xconfig-name=1600x1200_60.00 :: "1600x1200_60_0" 161.0 1600 1704 1880 2160 1200 1201 1204 1242 -HSync +VSync" +# + +NV_CTRL_BINARY_DATA_MODELINES = 1 # R-DG + +# +# NV_CTRL_BINARY_DATA_METAMODES - Returns an X Screen's supported +# MetaModes. MetaModes are returned in a buffer separated by a +# single '\0' and terminated by two consecutive '\0' s like so: +# +# "MetaMode 1\0MetaMode 2\0MetaMode 3\0Last MetaMode\0\0" +# +# The MetaMode string should have the same syntax as the MetaMode X +# configuration option, as documented in the NVIDIA driver README. + +# Each MetaMode string may be prepended with a comma-separated list +# of "token=value" pairs, separated from the MetaMode string with +# "::". Currently, valid tokens are: +# +# Token Value +# "id" - the id of this MetaMode; this is stored in +# the Vertical Refresh field, as viewed +# by the XRandR and XF86VidMode X# +# extensions. +# +# "switchable" "yes"/"no" - whether this MetaMode may be switched to via +# ctrl-alt-+/-; Implicit MetaModes (see +# the "IncludeImplicitMetaModes" X +# config option), for example, are not +# normally made available through +# ctrl-alt-+/-. +# +# "source" "xconfig" - the MetaMode was specified in the X +# config file. +# "implicit" - the MetaMode was implicitly added; see the +# "IncludeImplicitMetaModes" X config option +# for details. +# "nv-control" - the MetaMode was added via the NV-CONTROL X +# extension to the currently running X server. +# "RandR" - the MetaMode was modified in response to an +# RandR RRSetCrtcConfig request. +# +# Additional tokens may be added in the future, so it is recommended +# that any token parser processing the returned string from +# NV_CTRL_BINARY_DATA_METAMODES be implemented to gracefully ignore +# unrecognized tokens. +# +# E.g., +# +# "id=50, switchable=yes, source=xconfig :: CRT-0: 1024x768 @1024x768 +0+0" +# + +NV_CTRL_BINARY_DATA_METAMODES = 2 # R-D- +NV_CTRL_BINARY_DATA_METAMODES_VERSION_1 = NV_CTRL_BINARY_DATA_METAMODES + +# +# NV_CTRL_BINARY_DATA_XSCREENS_USING_GPU - Returns the list of X +# screens currently driven by the given GPU. +# +# The format of the returned data is: +# +# 4 CARD32 number of screens +# 4# n CARD32 screen indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN. +# + +NV_CTRL_BINARY_DATA_XSCREENS_USING_GPU = 3 # R-DG + +# +# NV_CTRL_BINARY_DATA_GPUS_USED_BY_XSCREEN - Returns the list of GPUs +# currently in use by the given X screen. +# +# The format of the returned data is: +# +# 4 CARD32 number of GPUs +# 4# n CARD32 GPU indices +# + +NV_CTRL_BINARY_DATA_GPUS_USED_BY_XSCREEN = 4 # R--- + +# +# NV_CTRL_BINARY_DATA_GPUS_USING_FRAMELOCK - Returns the list of +# GPUs currently connected to the given frame lock board. +# +# The format of the returned data is: +# +# 4 CARD32 number of GPUs +# 4# n CARD32 GPU indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN. +# + +NV_CTRL_BINARY_DATA_GPUS_USING_FRAMELOCK = 5 # R-DF + +# +# NV_CTRL_BINARY_DATA_DISPLAY_VIEWPORT - Returns the Display Device's +# viewport box into the given X Screen (in X Screen coordinates.) +# +# The format of the returned data is: +# +# 4 CARD32 Offset X +# 4 CARD32 Offset Y +# 4 CARD32 Width +# 4 CARD32 Height +# + +NV_CTRL_BINARY_DATA_DISPLAY_VIEWPORT = 6 # R-DG + +# +# NV_CTRL_BINARY_DATA_FRAMELOCKS_USED_BY_GPU - Returns the list of +# Framelock devices currently connected to the given GPU. +# +# The format of the returned data is: +# +# 4 CARD32 number of Framelocks +# 4# n CARD32 Framelock indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN. +# + +NV_CTRL_BINARY_DATA_FRAMELOCKS_USED_BY_GPU = 7 # R-DG + +# +# NV_CTRL_BINARY_DATA_GPUS_USING_VCSC - Deprecated +# +# Returns the list of GPU devices connected to the given VCS. +# +# The format of the returned data is: +# +# 4 CARD32 number of GPUs +# 4# n CARD32 GPU indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_VCSC target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN and cannot be queried using +# a NV_CTRL_TARGET_TYPE_X_GPU +# + +NV_CTRL_BINARY_DATA_GPUS_USING_VCSC = 8 # R-DV + +# +# NV_CTRL_BINARY_DATA_VCSCS_USED_BY_GPU - Deprecated +# +# Returns the VCSC device that is controlling the given GPU. +# +# The format of the returned data is: +# +# 4 CARD32 number of VCS (always 1) +# 4# n CARD32 VCS indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN +# + +NV_CTRL_BINARY_DATA_VCSCS_USED_BY_GPU = 9 # R-DG + +# +# NV_CTRL_BINARY_DATA_COOLERS_USED_BY_GPU - Returns the coolers that +# are cooling the given GPU. +# +# The format of the returned data is: +# +# 4 CARD32 number of COOLER +# 4# n CARD32 COOLER indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN +# + +NV_CTRL_BINARY_DATA_COOLERS_USED_BY_GPU = 10 # R-DG + +# +# NV_CTRL_BINARY_DATA_GPUS_USED_BY_LOGICAL_XSCREEN - Returns the list of +# GPUs currently driving the given X screen. If Xinerama is enabled, this +# will return all GPUs that are driving any X screen. +# +# The format of the returned data is: +# +# 4 CARD32 number of GPUs +# 4# n CARD32 GPU indices +# + +NV_CTRL_BINARY_DATA_GPUS_USED_BY_LOGICAL_XSCREEN = 11 # R--- + +# +# NV_CTRL_BINARY_DATA_THERMAL_SENSORS_USED_BY_GPU - Returns the sensors that +# are attached to the given GPU. +# +# The format of the returned data is: +# +# 4 CARD32 number of SENSOR +# 4# n CARD32 SENSOR indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN +# + +NV_CTRL_BINARY_DATA_THERMAL_SENSORS_USED_BY_GPU = 12 # R--G + +# +# NV_CTRL_BINARY_DATA_GLASSES_PAIRED_TO_3D_VISION_PRO_TRANSCEIVER - Returns +# the id of the glasses that are currently paired to the given +# 3D Vision Pro transceiver. +# +# The format of the returned data is: +# +# 4 CARD32 number of glasses +# 4# n CARD32 id of glasses +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER target. +# +NV_CTRL_BINARY_DATA_GLASSES_PAIRED_TO_3D_VISION_PRO_TRANSCEIVER = 13 # R--T + +# +# NV_CTRL_BINARY_DATA_DISPLAY_TARGETS - Returns all the display devices +# currently connected to any GPU on the X server. +# +# The format of the returned data is: +# +# 4 CARD32 number of display devices +# 4# n CARD32 display device indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData(). +# + +NV_CTRL_BINARY_DATA_DISPLAY_TARGETS = 14 # R--- + +# +# NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU - Returns the list of +# display devices that are connected to the GPU target. +# +# The format of the returned data is: +# +# 4 CARD32 number of display devices +# 4# n CARD32 display device indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. +# + +NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU = 15 # R--G + +# +# NV_CTRL_BINARY_DATA_METAMODES_VERSION_2 - Returns values similar to +# NV_CTRL_BINARY_DATA_METAMODES(_VERSION_1) but also returns extended syntax +# information to indicate a specific display device, as well as other per- +# display deviceflags as "token=value" pairs. For example: +# +# "DPY-1: 1280x1024 {Stereo=PassiveLeft}, +# DPY-2: 1280x1024 {Stereo=PassiveRight}," +# +# The display device names have the form "DPY-%d", where the integer +# part of the name is the NV-CONTROL target ID for that display device +# for this instance of the X server. Note that display device NV-CONTROL +# target IDs are not guaranteed to be the same from one run of the X +# server to the next. +# + +NV_CTRL_BINARY_DATA_METAMODES_VERSION_2 = 16 # R-D- + +# +# NV_CTRL_BINARY_DATA_DISPLAYS_ENABLED_ON_XSCREEN - Returns the list of +# display devices that are currently scanning out the X screen target. +# +# The format of the returned data is: +# +# 4 CARD32 number of display devices +# 4# n CARD32 display device indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_BINARY_DATA_DISPLAYS_ENABLED_ON_XSCREEN = 17 # R--- + +# +# NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN - Returns the list of +# display devices that are currently assigned the X screen target. +# +# The format of the returned data is: +# +# 4 CARD32 number of display devices +# 4# n CARD32 display device indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN = 18 # R--- + +# +# NV_CTRL_BINARY_DATA_GPU_FLAGS - Returns a list of flags for the +# given GPU. A flag can, for instance, be a capability which enables +# or disables some features according to the GPU state. +# +# The format of the returned data is: +# +# 4 CARD32 number of GPU flags +# 4# n CARD32 GPU flag +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. +# +NV_CTRL_BINARY_DATA_GPU_FLAGS = 19 # R--- + +# Stereo and display composition transformations are mutually exclusive. +NV_CTRL_BINARY_DATA_GPU_FLAGS_STEREO_DISPLAY_TRANSFORM_EXCLUSIVE = 0 +# Overlay and display composition transformations are mutually exclusive. +NV_CTRL_BINARY_DATA_GPU_FLAGS_OVERLAY_DISPLAY_TRANSFORM_EXCLUSIVE = 1 +# Depth 8 and display composition transformations are mutually exclusive. +NV_CTRL_BINARY_DATA_GPU_FLAGS_DEPTH_8_DISPLAY_TRANSFORM_EXCLUSIVE = 2 + +# +# NV_CTRL_BINARY_DATA_DISPLAYS_ON_GPU - Returns the list of valid +# display devices that can be connected to the GPU target. +# +# The format of the returned data is: +# +# 4 CARD32 number of display devices +# 4# n CARD32 display device indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. +# + +NV_CTRL_BINARY_DATA_DISPLAYS_ON_GPU = 20 # R--G + +NV_CTRL_BINARY_DATA_LAST_ATTRIBUTE = NV_CTRL_BINARY_DATA_DISPLAYS_ON_GPU + +############################################################################ + +# +# String Operation Attributes: +# +# These attributes are used with the XNVCTRLStringOperation() +# function; a string is specified as input, and a string is returned +# as output. +# +# Unless otherwise noted, all attributes can be operated upon using +# an NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + + +# +# NV_CTRL_STRING_OPERATION_ADD_METAMODE - provide a MetaMode string +# as input, and returns a string containing comma-separated list of +# "token=value" pairs as output. Currently, the only output token is +# "id", which indicates the id that was assigned to the MetaMode. +# +# All ModeLines referenced in the MetaMode must already exist for +# each display device (as returned by the +# NV_CTRL_BINARY_DATA_MODELINES attribute). +# +# The MetaMode string should have the same syntax as the MetaMode X +# configuration option, as documented in the NVIDIA driver README. +# +# The input string can optionally be prepended with a string of +# comma-separated "token=value" pairs, separated from the MetaMode +# string by "::". Currently, the only valid token is "index" which +# indicates the insertion index for the MetaMode. +# +# E.g., +# +# Input: "index=5 :: 1600x1200+0+0, 1600x1200+1600+0" +# Output: "id=58" +# +# which causes the MetaMode to be inserted at position 5 in the +# MetaMode list (all entries after 5 will be shifted down one slot in +# the list), and the X server's containing mode stores 58 as the +# VRefresh, so that the MetaMode can be uniquely identifed through +# XRandR and XF86VidMode. +# + +NV_CTRL_STRING_OPERATION_ADD_METAMODE = 0 # ---- + +# +# NV_CTRL_STRING_OPERATION_GTF_MODELINE - provide as input a string +# of comma-separated "token=value" pairs, and returns a ModeLine +# string, computed using the GTF formula using the parameters from +# the input string. Valid tokens for the input string are "width", +# "height", and "refreshrate". +# +# E.g., +# +# Input: "width=1600, height=1200, refreshrate=60" +# Output: "160.96 1600 1704 1880 2160 1200 1201 1204 1242 -HSync +VSync" +# +# This operation does not have any impact on any display device's +# modePool, and the ModeLine is not validated; it is simply intended +# for generating ModeLines. +# + +NV_CTRL_STRING_OPERATION_GTF_MODELINE = 1 # --- + +# +# NV_CTRL_STRING_OPERATION_CVT_MODELINE - provide as input a string +# of comma-separated "token=value" pairs, and returns a ModeLine +# string, computed using the CVT formula using the parameters from +# the input string. Valid tokens for the input string are "width", +# "height", "refreshrate", and "reduced-blanking". The +# "reduced-blanking" argument can be "0" or "1", to enable or disable +# use of reduced blanking for the CVT formula. +# +# E.g., +# +# Input: "width=1600, height=1200, refreshrate=60, reduced-blanking=1" +# Output: "130.25 1600 1648 1680 1760 1200 1203 1207 1235 +HSync -VSync" +# +# This operation does not have any impact on any display device's +# modePool, and the ModeLine is not validated; it is simply intended +# for generating ModeLines. +# + +NV_CTRL_STRING_OPERATION_CVT_MODELINE = 2 # --- + +# +# NV_CTRL_STRING_OPERATION_BUILD_MODEPOOL - build a ModePool for the +# specified display device on the specified target (either an X +# screen or a GPU). This is typically used to generate a ModePool +# for a display device on a GPU on which no X screens are present. +# +# Currently, a display device's ModePool is static for the life of +# the X server, so XNVCTRLStringOperation will return FALSE if +# requested to build a ModePool on a display device that already has +# a ModePool. +# +# The string input to BUILD_MODEPOOL may be NULL. If it is not NULL, +# then it is interpreted as a double-colon ("::") separated list +# of "option=value" pairs, where the options and the syntax of their +# values are the X configuration options that impact the behavior of +# modePool construction; namely: +# +# "ModeValidation" +# "HorizSync" +# "VertRefresh" +# "FlatPanelProperties" +# "ExactModeTimingsDVI" +# "UseEdidFreqs" +# +# An example input string might look like: +# +# "ModeValidation=NoVesaModes :: HorizSync=50-110 :: VertRefresh=50-150" +# +# This request currently does not return a string. +# + +NV_CTRL_STRING_OPERATION_BUILD_MODEPOOL = 3 # DG + +# +# NV_CTRL_STRING_OPERATION_GVI_CONFIGURE_STREAMS - Configure the streams- +# to-jack+channel topology for a GVI (Graphics capture board). +# +# The string input to GVI_CONFIGURE_STREAMS may be NULL. If this is the +# case, then the current topology is returned. +# +# If the input string to GVI_CONFIGURE_STREAMS is not NULL, the string +# is interpreted as a semicolon (";") separated list of comma-separated +# lists of "option=value" pairs that define a stream's composition. The +# available options and their values are: +# +# "stream": Defines which stream this comma-separated list describes. +# Valid values are the integers between 0 and +# NV_CTRL_GVI_NUM_STREAMS-1 (inclusive). +# +# "linkN": Defines a jack+channel pair to use for the given link N. +# Valid options are the string "linkN", where N is an integer +# between 0 and NV_CTRL_GVI_MAX_LINKS_PER_STREAM-1 (inclusive). +# Valid values for these options are strings of the form +# "jackX" and/or "jackX.Y", where X is an integer between 0 and +# NV_CTRL_GVI_NUM_JACKS-1 (inclusive), and Y (optional) is an +# integer between 0 and NV_CTRL_GVI_MAX_CHANNELS_PER_JACK-1 +# (inclusive). +# +# An example input string might look like: +# +# "stream=0, link0=jack0, link1=jack1; stream=1, link0=jack2.1" +# +# This example specifies two streams, stream 0 and stream 1. Stream 0 +# is defined to capture link0 data from the first channel (channel 0) of +# BNC jack 0 and link1 data from the first channel of BNC jack 1. The +# second stream (Stream 1) is defined to capture link0 data from channel 1 +# (second channel) of BNC jack 2. +# +# This example shows a possible configuration for capturing 3G input: +# +# "stream=0, link0=jack0.0, link1=jack0.1" +# +# Applications should query the following attributes to determine +# possible combinations: +# +# NV_CTRL_GVI_MAX_STREAMS +# NV_CTRL_GVI_MAX_LINKS_PER_STREAM +# NV_CTRL_GVI_NUM_JACKS +# NV_CTRL_GVI_MAX_CHANNELS_PER_JACK +# +# Note: A jack+channel pair can only be tied to one link/stream. +# +# Upon successful configuration or querying of this attribute, a string +# representing the current topology for all known streams on the device +# will be returned. On failure, NULL is returned. +# +# Note: Setting this attribute may also result in the following +# NV-CONTROL attributes being reset on the GVI device (to ensure +# the configuration remains valid): +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT +# NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT +# NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING +# + +NV_CTRL_STRING_OPERATION_GVI_CONFIGURE_STREAMS = 4 # RW-I + +# +# NV_CTRL_STRING_OPERATION_PARSE_METAMODE - Parses the given MetaMode string +# and returns the validated MetaMode string - possibly re-calculating various +# values such as ViewPortIn. If the MetaMode matches an existing MetaMode, +# the details of the existing MetaMode are returned. If the MetaMode fails to +# be parsed, NULL is returned. +# +NV_CTRL_STRING_OPERATION_PARSE_METAMODE = 5 # R--- + +NV_CTRL_STRING_OPERATION_LAST_ATTRIBUTE = NV_CTRL_STRING_OPERATION_PARSE_METAMODE + +############################################################################### +# NV-CONTROL major op numbers. these constants identify the request type +# +X_nvCtrlQueryExtension = 0 +X_nvCtrlQueryAttribute = 2 +X_nvCtrlQueryStringAttribute = 4 +X_nvCtrlQueryValidAttributeValues = 5 +X_nvCtrlSetStringAttribute = 9 +X_nvCtrlSetAttributeAndGetStatus = 19 +X_nvCtrlQueryBinaryData = 20 +X_nvCtrlQueryTargetCount = 24 +X_nvCtrlStringOperation = 25 + +############################################################################### +# various lists that go with attrs, but are handled more compactly +# this way. these lists are indexed by the possible values of their attrs +# and are explained in NVCtrl.h +# + +ATTRIBUTE_TYPE_UNKNOWN = 0 +ATTRIBUTE_TYPE_INTEGER = 1 +ATTRIBUTE_TYPE_BITMASK = 2 +ATTRIBUTE_TYPE_BOOL = 3 +ATTRIBUTE_TYPE_RANGE = 4 +ATTRIBUTE_TYPE_INT_BITS = 5 + +ATTRIBUTE_TYPE_READ = 0x01 +ATTRIBUTE_TYPE_WRITE = 0x02 +ATTRIBUTE_TYPE_DISPLAY = 0x04 +ATTRIBUTE_TYPE_GPU = 0x08 +ATTRIBUTE_TYPE_FRAMELOCK = 0x10 +ATTRIBUTE_TYPE_X_SCREEN = 0x20 +ATTRIBUTE_TYPE_XINERAMA = 0x40 +ATTRIBUTE_TYPE_VCSC = 0x80 + +############################################################################ + +# +# Attribute Targets +# +# Targets define attribute groups. For example, some attributes are only +# valid to set on a GPU, others are only valid when talking about an +# X Screen. Target types are then what is used to identify the target +# group of the attribute you wish to set/query. +# +# Here are the supported target types: +# + +NV_CTRL_TARGET_TYPE_X_SCREEN = 0 +NV_CTRL_TARGET_TYPE_GPU = 1 +NV_CTRL_TARGET_TYPE_FRAMELOCK = 2 +# Visual Computing System - deprecated. To be removed along with all +# VCS-specific attributes in a later release. +NV_CTRL_TARGET_TYPE_VCSC = 3 +NV_CTRL_TARGET_TYPE_GVI = 4 +NV_CTRL_TARGET_TYPE_COOLER = 5 # e.g., fan +NV_CTRL_TARGET_TYPE_THERMAL_SENSOR = 6 +NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER = 7 +NV_CTRL_TARGET_TYPE_DISPLAY = 8 + + +############################################################################### +# Targets, to indicate where a command should be executed. +# +class Target(object): + def __init__(self): + self._id = -1 + self._type = -1 + self._name = '' + + def id(self): + return self._id + + def type(self): + return self._type + + def __str__(self): + return ''.format(self._name, self.id()) + + +class Gpu(Target): + def __init__(self, ngpu=0): + """Target a GPU""" + super(self.__class__, self).__init__() + self._id = ngpu + self._type = NV_CTRL_TARGET_TYPE_GPU + self._name = 'GPU' + + +class Screen(Target): + def __init__(self, nscr=0): + """Target an X screen""" + super(self.__class__, self).__init__() + self._id = nscr + self._type = NV_CTRL_TARGET_TYPE_X_SCREEN + self._name = 'X screen' + + +class Cooler(Target): + def __init__(self, nfan=0): + """Target a fann""" + super(self.__class__, self).__init__() + self._id = nfan + self._type = NV_CTRL_TARGET_TYPE_COOLER + self._name = 'Cooler' + + +class NVCtrlQueryTargetCountReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlQueryTargetCount), + rq.RequestLength(), + rq.Card32('target_type'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('padb1'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('count'), + rq.Card32('pad4'), + rq.Card32('pad5'), + rq.Card32('pad6'), + rq.Card32('pad7'), + rq.Card32('pad8'), + ) + + +class NVCtrlQueryAttributeReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlQueryAttribute), + rq.RequestLength(), + rq.Card16('target_id'), + rq.Card16('target_type'), + rq.Card32('display_mask'), + rq.Card32('attr'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('pad0'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('flags'), + rq.Int32('value'), + rq.Card32('pad4'), + rq.Card32('pad5'), + rq.Card32('pad6'), + rq.Card32('pad7'), + ) + + +class NVCtrlSetAttributeAndGetStatusReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlSetAttributeAndGetStatus), + rq.RequestLength(), + rq.Card16('target_id'), + rq.Card16('target_type'), + rq.Card32('display_mask'), + rq.Card32('attr'), + rq.Int32('value') + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('pad0'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('flags'), + rq.Card32('pad3'), + rq.Card32('pad4'), + rq.Card32('pad5'), + rq.Card32('pad6'), + rq.Card32('pad7'), + ) + + +class NVCtrlQueryStringAttributeReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlQueryStringAttribute), + rq.RequestLength(), + rq.Card16('target_id'), + rq.Card16('target_type'), + rq.Card32('display_mask'), + rq.Card32('attr'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('pad0'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('flags'), + rq.Card32('string', 4), + rq.Card32('pad4'), + rq.Card32('pad5'), + rq.Card32('pad6'), + rq.Card32('pad7'), + rq.String8('string'), + ) + + +class NVCtrlQueryValidAttributeValuesReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlQueryValidAttributeValues), + rq.RequestLength(), + rq.Card16('target_id'), + rq.Card16('target_type'), + rq.Card32('display_mask'), + rq.Card32('attr'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('pad0'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('flags'), + rq.Int32('attr_type'), + rq.Int32('min'), + rq.Int32('max'), + rq.Card32('bits'), + rq.Card32('perms'), + ) + + +class NVCtrlQueryBinaryDataReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlQueryBinaryData), + rq.RequestLength(), + rq.Card16('target_id'), + rq.Card16('target_type'), + rq.Card32('display_mask'), + rq.Card32('attr'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('pad0'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('flags'), + rq.Card32('data', 4), + rq.Card32('pad4'), + rq.Card32('pad5'), + rq.Card32('pad6'), + rq.Card32('pad7'), + rq.Binary('data'), + ) + + +class NVCtrlQueryListCard32ReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlQueryBinaryData), + rq.RequestLength(), + rq.Card16('target_id'), + rq.Card16('target_type'), + rq.Card32('display_mask'), + rq.Card32('attr'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('pad0'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('flags'), + rq.Card32('list', 4), + rq.Card32('pad4'), + rq.Card32('pad5'), + rq.Card32('pad6'), + rq.Card32('pad7'), + rq.List('list', rq.Card32), + ) diff --git a/Xlib/ext/randr.py b/Xlib/ext/randr.py new file mode 100644 index 0000000..48f4b63 --- /dev/null +++ b/Xlib/ext/randr.py @@ -0,0 +1,1197 @@ +# Xlib.ext.randr -- RandR extension module +# +# Copyright (C) 2006 Mike Meyer +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + + +"""RandR - provide access to the RandR extension information. + +This implementation is based off version 1.3 of the XRandR protocol, and may +not be compatible with other versions. + +Version 1.2 of the protocol is documented at: +http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt + +Version 1.3.1 here: +http://www.x.org/releases/X11R7.5/doc/randrproto/randrproto.txt + +""" + + +from Xlib import X +from Xlib.protocol import rq, structs + +extname = 'RANDR' + + +# Event codes # +RRScreenChangeNotify = 0 + +# V1.2 additions +RRNotify = 1 + +# RRNotify Subcodes +RRNotify_CrtcChange = 0 +RRNotify_OutputChange = 1 +RRNotify_OutputProperty = 2 + + +# Event selection bits # +RRScreenChangeNotifyMask = (1 << 0) + +# V1.2 additions +RRCrtcChangeNotifyMask = (1 << 1) +RROutputChangeNotifyMask = (1 << 2) +RROutputPropertyNotifyMask = (1 << 3) + + +# Constants # +SetConfigSuccess = 0 +SetConfigInvalidConfigTime = 1 +SetConfigInvalidTime = 2 +SetConfigFailed = 3 + +# used in the rotation field; rotation and reflection in 0.1 proto. +Rotate_0 = 1 +Rotate_90 = 2 +Rotate_180 = 4 +Rotate_270 = 8 + +# new in 1.0 protocol, to allow reflection of screen +Reflect_X = 16 +Reflect_Y = 32 + +# new in 1.2 protocol +HSyncPositive = 0x00000001 +HSyncNegative = 0x00000002 +VSyncPositive = 0x00000004 +VSyncNegative = 0x00000008 +Interlace = 0x00000010 +DoubleScan = 0x00000020 +CSync = 0x00000040 +CSyncPositive = 0x00000080 +CSyncNegative = 0x00000100 +HSkewPresent = 0x00000200 +BCast = 0x00000400 +PixelMultiplex = 0x00000800 +DoubleClock = 0x00001000 +ClockDivideBy2 = 0x00002000 + +# event types? +Connected = 0 +Disconnected = 1 +UnknownConnection = 2 + +# Conventional RandR output properties +PROPERTY_RANDR_EDID = "EDID" +PROPERTY_SIGNAL_FORMAT = "SignalFormat" +PROPERTY_SIGNAL_PROPERTIES = "SignalProperties" +PROPERTY_CONNECTOR_TYPE = "ConnectorType" +PROPERTY_CONNECTOR_NUMBER = "ConnectorNumber" +PROPERTY_COMPATIBILITY_LIST = "CompatibilityList" +PROPERTY_CLONE_LIST = "CloneList" + +# subpixel order - TODO: These constants are part of the RENDER extension and +# should be moved there if/when that extension is added to python-xlib. +SubPixelUnknown = 0 +SubPixelHorizontalRGB = 1 +SubPixelHorizontalBGR = 2 +SubPixelVerticalRGB = 3 +SubPixelVerticalBGR = 4 +SubPixelNone = 5 + + +# Error Codes # +BadRROutput = 0 +BadRRCrtc = 1 +BadRRMode = 2 + + +# Data Structures # + +RandR_ScreenSizes = rq.Struct( + rq.Card16('width_in_pixels'), + rq.Card16('height_in_pixels'), + rq.Card16('width_in_millimeters'), + rq.Card16('height_in_millimeters'), + ) + + +RandR_ModeInfo = rq.Struct( + rq.Card32('id'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card32('dot_clock'), + rq.Card16('h_sync_start'), + rq.Card16('h_sync_end'), + rq.Card16('h_total'), + rq.Card16('h_skew'), + rq.Card16('v_sync_start'), + rq.Card16('v_sync_end'), + rq.Card16('v_total'), + rq.Card16('name_length'), + rq.Card32('flags'), + ) + +RandR_Rates = rq.Struct( + rq.LengthOf('rates', 2), + rq.List('rates', rq.Card16Obj) + ) + +# TODO: This struct is part of the RENDER extension and should be moved there +# if/when that extension is added to python-xlib. +Render_Transform = rq.Struct( + rq.Card32('matrix11'), #FIXME: All of these are listed as FIXED in the protocol header. + rq.Card32('matrix12'), + rq.Card32('matrix13'), + rq.Card32('matrix21'), + rq.Card32('matrix22'), + rq.Card32('matrix23'), + rq.Card32('matrix31'), + rq.Card32('matrix32'), + rq.Card32('matrix33'), + ) + + +# Requests # + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + rq.Pad(16), + ) + +def query_version(self): + """Get the current version of the RandR extension. + + """ + return QueryVersion( + display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=3, + ) + + +class _1_0SetScreenConfig(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.Card32('timestamp'), + rq.Card32('config_timestamp'), + rq.Card16('size_id'), + rq.Card16('rotation'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('new_timestamp'), + rq.Card32('new_config_timestamp'), + rq.Window('root'), + rq.Card16('subpixel_order'), + rq.Pad(10), + ) + +def _1_0set_screen_config(self, size_id, rotation, config_timestamp, timestamp=X.CurrentTime): + """Sets the screen to the specified size and rotation. + + """ + return _1_0SetScreenConfig( + display=self.display, + opcode=self.display.get_extension_major(extname), + drawable=self, + timestamp=timestamp, + config_timestamp=config_timestamp, + size_id=size_id, + rotation=rotation, + ) + + +class SetScreenConfig(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.Card32('timestamp'), + rq.Card32('config_timestamp'), + rq.Card16('size_id'), + rq.Card16('rotation'), + rq.Card16('rate'), # added in version 1.1 + rq.Pad(2), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('new_timestamp'), + rq.Card32('new_config_timestamp'), + rq.Window('root'), + rq.Card16('subpixel_order'), + rq.Pad(10), + ) + +def set_screen_config(self, size_id, rotation, config_timestamp, rate=0, timestamp=X.CurrentTime): + """Sets the screen to the specified size, rate, rotation and reflection. + + rate can be 0 to have the server select an appropriate rate. + + """ + return SetScreenConfig( + display=self.display, + opcode=self.display.get_extension_major(extname), + drawable=self, + timestamp=timestamp, + config_timestamp=config_timestamp, + size_id=size_id, + rotation=rotation, + rate=rate, + ) + + +class SelectInput(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + rq.Window('window'), + rq.Card16('mask'), + rq.Pad(2), + ) + +def select_input(self, mask): + return SelectInput( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + mask=mask, + ) + + +class GetScreenInfo(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(5), + rq.RequestLength(), + rq.Window('window'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('set_of_rotations'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('root'), + rq.Card32('timestamp'), + rq.Card32('config_timestamp'), + rq.LengthOf('sizes', 2), + rq.Card16('size_id'), + rq.Card16('rotation'), + rq.Card16('rate'), # added in version 1.1 + rq.Card16('n_rate_ents'), # XCB's protocol description disagrees with the X headers on this; ignoring. + rq.Pad(2), + rq.List('sizes', RandR_ScreenSizes), + #rq.List('rates', RandR_Rates) #FIXME: Why does uncommenting this cause an error? + ) + +def get_screen_info(self): + """Retrieve information about the current and available configurations for + the screen associated with this window. + + """ + return GetScreenInfo( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + ) + + +# version 1.2 + +class GetScreenSizeRange(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(6), + rq.RequestLength(), + rq.Window('window'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('min_width'), + rq.Card16('min_height'), + rq.Card16('max_width'), + rq.Card16('max_height'), + rq.Pad(16), + ) + +def get_screen_size_range(self): + """Retrieve the range of possible screen sizes. The screen may be set to + any size within this range. + + """ + return GetScreenSizeRange( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + ) + + +class SetScreenSize(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(7), + rq.RequestLength(), + rq.Window('window'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card32('width_in_millimeters'), + rq.Card32('height_in_millimeters'), + ) + +def set_screen_size(self, width, height, width_in_millimeters=None, height_in_millimeters=None): + return SetScreenSize( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + width=width, + height=height, + width_in_millimeters=width_in_millimeters, + height_in_millimeters=height_in_millimeters, + ) + + +class GetScreenResources(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(8), + rq.RequestLength(), + rq.Window('window'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('timestamp'), + rq.Card32('config_timestamp'), + rq.LengthOf('crtcs', 2), + rq.LengthOf('outputs', 2), + rq.LengthOf('modes', 2), + rq.LengthOf('mode_names', 2), + rq.Pad(8), + rq.List('crtcs', rq.Card32Obj), + rq.List('outputs', rq.Card32Obj), + rq.List('modes', RandR_ModeInfo), + rq.String8('mode_names'), + ) + +def get_screen_resources(self): + return GetScreenResources( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + ) + + +class GetOutputInfo(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(9), + rq.RequestLength(), + rq.Card32('output'), + rq.Card32('config_timestamp'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('timestamp'), + rq.Card32('crtc'), + rq.Card32('mm_width'), + rq.Card32('mm_height'), + rq.Card8('connection'), + rq.Card8('subpixel_order'), + rq.LengthOf('crtcs', 2), + rq.LengthOf('modes', 2), + rq.Card16('num_preferred'), + rq.LengthOf('clones', 2), + rq.LengthOf('name', 2), + rq.List('crtcs', rq.Card32Obj), + rq.List('modes', rq.Card32Obj), + rq.List('clones', rq.Card32Obj), + rq.String8('name'), + ) + +def get_output_info(self, output, config_timestamp): + return GetOutputInfo( + display=self.display, + opcode=self.display.get_extension_major(extname), + output=output, + config_timestamp=config_timestamp, + ) + + +class ListOutputProperties(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(10), + rq.RequestLength(), + rq.Card32('output'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('atoms', 2), + rq.Pad(22), + rq.List('atoms', rq.Card32Obj), + ) + +def list_output_properties(self, output): + return ListOutputProperties ( + display=self.display, + opcode=self.display.get_extension_major(extname), + output=output, + ) + + +class QueryOutputProperty(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(11), + rq.RequestLength(), + rq.Card32('output'), + rq.Card32('property'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Bool('pending'), + rq.Bool('range'), + rq.Bool('immutable'), + rq.Pad(21), + rq.List('valid_values', rq.Card32Obj), + ) + +def query_output_property(self, output, property): + return QueryOutputProperty ( + display=self.display, + opcode=self.display.get_extension_major(extname), + output=output, + property=property, + ) + + +class ConfigureOutputProperty (rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(12), + rq.RequestLength(), + rq.Card32('output'), + rq.Card32('property'), + rq.Bool('pending'), + rq.Bool('range'), + rq.Pad(2), + rq.List('valid_values', rq.Card32Obj), + ) + +def configure_output_property (self, output, property): + return ConfigureOutputProperty ( + display=self.display, + opcode=self.display.get_extension_major(extname), + output=output, + property=property, + ) + + +class ChangeOutputProperty(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(13), + rq.RequestLength(), + rq.Card32('output'), + rq.Card32('property'), + rq.Card32('type'), + rq.Format('value', 1), + rq.Card8('mode'), + rq.Pad(2), + rq.LengthOf('value', 4), + rq.PropertyData('value'), + ) + +def change_output_property(self, output, property, type, mode, value): + return ChangeOutputProperty( + display=self.display, + opcode=self.display.get_extension_major(extname), + output=output, + property=property, + type=type, + mode=mode, + value=value, + ) + + +class DeleteOutputProperty(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(14), + rq.RequestLength(), + rq.Card32('output'), + rq.Card32('property'), + ) + +def delete_output_property(self, output, property): + return DeleteOutputProperty( + display=self.display, + opcode=self.display.get_extension_major(extname), + output=output, + property=property, + ) + + +class GetOutputProperty(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(15), + rq.RequestLength(), + rq.Card32('output'), + rq.Card32('property'), + rq.Card32('type'), + rq.Card32('long_offset'), + rq.Card32('long_length'), + rq.Bool('delete'), + rq.Bool('pending'), + rq.Pad(2), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Format('value', 1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('property_type'), + rq.Card32('bytes_after'), + rq.LengthOf('value', 4), + rq.Pad(12), + rq.List('value', rq.Card8Obj), + ) + +def get_output_property(self, output, property, type, long_offset, long_length, delete=False, pending=False): + return GetOutputProperty( + display=self.display, + opcode=self.display.get_extension_major(extname), + output=output, + property=property, + type=type, + long_offset=long_offset, + long_length=long_length, + delete=delete, + pending=pending, + ) + + +class CreateMode(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(16), + rq.RequestLength(), + rq.Window('window'), + rq.Object('mode', RandR_ModeInfo), + rq.String8('name'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('mode'), + rq.Pad(20), + ) + +def create_mode(self, mode, name): + return CreateMode ( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + mode=mode, + name=name, + ) + + +class DestroyMode(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(17), + rq.RequestLength(), + rq.Card32('mode'), + ) + +def destroy_mode(self, mode): + return DestroyMode( + display=self.display, + opcode=self.display.get_extension_major(extname), + mode=mode, + ) + + +class AddOutputMode(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(18), + rq.RequestLength(), + rq.Card32('output'), + rq.Card32('mode'), + ) + +def add_output_mode(self, output, mode): + return AddOutputMode( + display=self.display, + opcode=self.display.get_extension_major(extname), + output=output, + mode=mode, + ) + + +class DeleteOutputMode(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(19), + rq.RequestLength(), + rq.Card32('output'), + rq.Card32('mode'), + ) + +def delete_output_mode(self, output, mode): + return DeleteOutputMode( + display=self.display, + opcode=self.display.get_extension_major(extname), + output=output, + mode=mode, + ) + + +class GetCrtcInfo(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(20), + rq.RequestLength(), + rq.Card32('crtc'), + rq.Card32('config_timestamp'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('timestamp'), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card32('mode'), + rq.Card16('rotation'), + rq.Card16('possible_rotations'), + rq.LengthOf('outputs', 2), + rq.LengthOf('possible_outputs', 2), + rq.List('outputs', rq.Card32Obj), + rq.List('possible_outputs', rq.Card32Obj), + ) + +def get_crtc_info(self, crtc, config_timestamp): + return GetCrtcInfo ( + display=self.display, + opcode=self.display.get_extension_major(extname), + crtc=crtc, + config_timestamp=config_timestamp, + ) + + +class SetCrtcConfig(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(21), + rq.RequestLength(), + rq.Card32('crtc'), + rq.Card32('timestamp'), + rq.Card32('config_timestamp'), + rq.Int16('x'), + rq.Int16('y'), + rq.Card32('mode'), + rq.Card16('rotation'), + rq.Pad(2), + rq.List('outputs', rq.Card32Obj), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('new_timestamp'), + rq.Pad(20), + ) + +def set_crtc_config(self, crtc, config_timestamp, x, y, mode, rotation, outputs, timestamp=X.CurrentTime): + return SetCrtcConfig ( + display=self.display, + opcode=self.display.get_extension_major(extname), + crtc=crtc, + config_timestamp=config_timestamp, + x=x, + y=y, + mode=mode, + rotation=rotation, + outputs=outputs, + timestamp=timestamp, + ) + + +class GetCrtcGammaSize(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(22), + rq.RequestLength(), + rq.Card32('crtc'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('size'), + rq.Pad(22), + ) + +def get_crtc_gamma_size(self, crtc): + return GetCrtcGammaSize ( + display=self.display, + opcode=self.display.get_extension_major(extname), + crtc=crtc, + ) + + +class GetCrtcGamma(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(23), + rq.RequestLength(), + rq.Card32('crtc'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf(('red', 'green', 'blue'), 2), + rq.Pad(22), + rq.List('red', rq.Card16Obj), + rq.List('green', rq.Card16Obj), + rq.List('blue', rq.Card16Obj), + ) + +def get_crtc_gamma(self, crtc): + return GetCrtcGamma ( + display=self.display, + opcode=self.display.get_extension_major(extname), + crtc=crtc, + ) + + +class SetCrtcGamma(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(24), + rq.RequestLength(), + rq.Card32('crtc'), + rq.Card16('size'), + rq.Pad(2), + rq.List('red', rq.Card16Obj), + rq.List('green', rq.Card16Obj), + rq.List('blue', rq.Card16Obj), + ) + +def set_crtc_gamma(self, crtc, size, red, green, blue): + return SetCrtcGamma( + display=self.display, + opcode=self.display.get_extension_major(extname), + crtc=crtc, + size=size, + red=red, + green=green, + blue=blue, + ) + + +# version 1.3 + +class GetScreenResourcesCurrent(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(25), + rq.RequestLength(), + rq.Window('window'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('timestamp'), + rq.Card32('config_timestamp'), + rq.LengthOf('crtcs', 2), + rq.LengthOf('outputs', 2), + rq.LengthOf('modes', 2), + rq.LengthOf('names', 2), + rq.Pad(8), + rq.List('crtcs', rq.Card32Obj), + rq.List('outputs', rq.Card32Obj), + rq.List('modes', RandR_ModeInfo), + rq.String8('names'), + ) + +def get_screen_resources_current(self): + return GetScreenResourcesCurrent( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + ) + + +class SetCrtcTransform(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(26), + rq.RequestLength(), + rq.Card32('crtc'), + rq.Object('transform', Render_Transform), + rq.LengthOf('filter_name', 2), + rq.Pad(2), + rq.String8('filter_name'), + rq.List('filter_params', rq.Card32Obj), #FIXME: The protocol says FIXED? http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt#n2161 + ) + +def set_crtc_transform(self, crtc, n_bytes_filter): + return SetCrtcTransform( + display=self.display, + opcode=self.display.get_extension_major(extname), + crtc=crtc, + n_bytes_filter=n_bytes_filter, + ) + + +class GetCrtcTransform(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(27), + rq.RequestLength(), + rq.Card32('crtc'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Object('pending_transform', Render_Transform), + rq.Bool('has_transforms'), + rq.Pad(3), + rq.Object('current_transform', Render_Transform), + rq.Pad(4), + rq.LengthOf('pending_filter_name', 2), + rq.LengthOf('pending_filter_params', 2), + rq.LengthOf('current_filter_name', 2), + rq.LengthOf('current_filter_params', 2), + rq.String8('pending_filter_name'), + rq.List('pending_filter_params', rq.Card32Obj), #FIXME: The protocol says FIXED? http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt#n2161 + rq.String8('current_filter_name'), + rq.List('current_filter_params', rq.Card32Obj), #FIXME: The protocol says FIXED? http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt#n2161 + ) + +def get_crtc_transform(self, crtc): + return GetCrtcTransform( + display=self.display, + opcode=self.display.get_extension_major(extname), + crtc=crtc, + ) + + +class GetPanning(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(28), + rq.RequestLength(), + rq.Card32('crtc'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('timestamp'), + rq.Card16('left'), + rq.Card16('top'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('track_left'), + rq.Card16('track_top'), + rq.Card16('track_width'), + rq.Card16('track_height'), + rq.Int16('border_left'), + rq.Int16('border_top'), + rq.Int16('border_right'), + rq.Int16('border_bottom'), + ) + +def get_panning(self, crtc): + return GetPanning ( + display=self.display, + opcode=self.display.get_extension_major(extname), + crtc=crtc, + ) + + +class SetPanning(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(29), + rq.RequestLength(), + rq.Card32('crtc'), + rq.Card32('timestamp'), + rq.Card16('left'), + rq.Card16('top'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('track_left'), + rq.Card16('track_top'), + rq.Card16('track_width'), + rq.Card16('track_height'), + rq.Int16('border_left'), + rq.Int16('border_top'), + rq.Int16('border_right'), + rq.Int16('border_bottom'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('new_timestamp'), + rq.Pad(20), + ) + +def set_panning(self, crtc, left, top, width, height, track_left, track_top, track_width, track_height, border_left, border_top, border_width, border_height, timestamp=X.CurrentTime): + return SetPanning ( + display=self.display, + opcode=self.display.get_extension_major(extname), + crtc=crtc, + left=left, + top=top, + width=width, + height=height, + track_left=track_left, + track_top=track_top, + track_width=track_width, + track_height=track_height, + border_left=border_left, + border_top=border_top, + border_width=border_width, + border_height=border_height, + timestamp=timestamp, + ) + + +class SetOutputPrimary(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(30), + rq.RequestLength(), + rq.Window('window'), + rq.Card32('output'), + ) + +def set_output_primary(self, output): + return SetOutputPrimary( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + output=output, + ) + + +class GetOutputPrimary(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(31), + rq.RequestLength(), + rq.Window('window'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('output'), + rq.Pad(20), + ) + +def get_output_primary(self): + return GetOutputPrimary( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + ) + + +# Events # + +class ScreenChangeNotify(rq.Event): + _code = None + _fields = rq.Struct( + rq.Card8('type'), + rq.Card8('rotation'), + rq.Card16('sequence_number'), + rq.Card32('timestamp'), + rq.Card32('config_timestamp'), + rq.Window('root'), + rq.Window('window'), + rq.Card16('size_id'), + rq.Card16('subpixel_order'), + rq.Card16('width_in_pixels'), + rq.Card16('height_in_pixels'), + rq.Card16('width_in_millimeters'), + rq.Card16('height_in_millimeters'), + ) + + +class CrtcChangeNotify(rq.Event): + _code = None + _fields = rq.Struct( + rq.Card8('type'), + rq.Card8('sub_code'), + rq.Card16('sequence_number'), + rq.Card32('timestamp'), + rq.Window('window'), + rq.Card32('crtc'), + rq.Card32('mode'), + rq.Card16('rotation'), + rq.Pad(2), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + ) + + +class OutputChangeNotify(rq.Event): + _code = None + _fields = rq.Struct( + rq.Card8('type'), + rq.Card8('sub_code'), + rq.Card16('sequence_number'), + rq.Card32('timestamp'), + rq.Card32('config_timestamp'), + rq.Window('window'), + rq.Card32('output'), + rq.Card32('crtc'), + rq.Card32('mode'), + rq.Card16('rotation'), + rq.Card8('connection'), + rq.Card8('subpixel_order'), + ) + + +class OutputPropertyNotify(rq.Event): + _code = None + _fields = rq.Struct( + rq.Card8('type'), + rq.Card8('sub_code'), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Card32('output'), + rq.Card32('atom'), + rq.Card32('timestamp'), + rq.Card8('state'), + rq.Pad(11), + ) + + +# Initialization # + +def init(disp, info): + disp.extension_add_method('display', 'xrandr_query_version', query_version) + disp.extension_add_method('window', 'xrandr_select_input', select_input) + disp.extension_add_method('window', 'xrandr_get_screen_info', get_screen_info) + disp.extension_add_method('drawable', 'xrandr_1_0set_screen_config', _1_0set_screen_config) + disp.extension_add_method('drawable', 'xrandr_set_screen_config', set_screen_config) + disp.extension_add_method('window', 'xrandr_get_screen_size_range', get_screen_size_range) + disp.extension_add_method('window', 'xrandr_set_screen_size', set_screen_size) + disp.extension_add_method('window', 'xrandr_get_screen_resources', get_screen_resources) + disp.extension_add_method('display', 'xrandr_get_output_info', get_output_info) + disp.extension_add_method('display', 'xrandr_list_output_properties', list_output_properties) + disp.extension_add_method('display', 'xrandr_query_output_property', query_output_property) + disp.extension_add_method('display', 'xrandr_configure_output_property ', configure_output_property ) + disp.extension_add_method('display', 'xrandr_change_output_property', change_output_property) + disp.extension_add_method('display', 'xrandr_delete_output_property', delete_output_property) + disp.extension_add_method('display', 'xrandr_get_output_property', get_output_property) + disp.extension_add_method('window', 'xrandr_create_mode', create_mode) + disp.extension_add_method('display', 'xrandr_destroy_mode', destroy_mode) + disp.extension_add_method('display', 'xrandr_add_output_mode', add_output_mode) + disp.extension_add_method('display', 'xrandr_delete_output_mode', delete_output_mode) + disp.extension_add_method('display', 'xrandr_get_crtc_info', get_crtc_info) + disp.extension_add_method('display', 'xrandr_set_crtc_config', set_crtc_config) + disp.extension_add_method('display', 'xrandr_get_crtc_gamma_size', get_crtc_gamma_size) + disp.extension_add_method('display', 'xrandr_get_crtc_gamma', get_crtc_gamma) + disp.extension_add_method('display', 'xrandr_set_crtc_gamma', set_crtc_gamma) + disp.extension_add_method('window', 'xrandr_get_screen_resources_current', get_screen_resources_current) + disp.extension_add_method('display', 'xrandr_set_crtc_transform', set_crtc_transform) + disp.extension_add_method('display', 'xrandr_get_crtc_transform', get_crtc_transform) + disp.extension_add_method('window', 'xrandr_set_output_primary', set_output_primary) + disp.extension_add_method('window', 'xrandr_get_output_primary', get_output_primary) + disp.extension_add_method('display', 'xrandr_get_panning', get_panning) + disp.extension_add_method('display', 'xrandr_set_panning', set_panning) + + disp.extension_add_event(info.first_event + RRScreenChangeNotify, ScreenChangeNotify) + # add RRNotify events (1 event code with 3 subcodes) + disp.extension_add_subevent(info.first_event + RRNotify, RRNotify_CrtcChange, CrtcChangeNotify) + disp.extension_add_subevent(info.first_event + RRNotify, RRNotify_OutputChange, OutputChangeNotify) + disp.extension_add_subevent(info.first_event + RRNotify, RRNotify_OutputProperty, OutputPropertyNotify) + + #disp.extension_add_error(BadRROutput, BadRROutputError) + #disp.extension_add_error(BadRRCrtc, BadRRCrtcError) + #disp.extension_add_error(BadRRMode, BadRRModeError) diff --git a/Xlib/ext/record.py b/Xlib/ext/record.py new file mode 100644 index 0000000..bb53ec1 --- /dev/null +++ b/Xlib/ext/record.py @@ -0,0 +1,283 @@ +# Xlib.ext.record -- RECORD extension module +# +# Copyright (C) 2006 Alex Badea +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +from Xlib import X +from Xlib.protocol import rq + +extname = 'RECORD' + +FromServerTime = 0x01 +FromClientTime = 0x02 +FromClientSequence = 0x04 + +CurrentClients = 1 +FutureClients = 2 +AllClients = 3 + +FromServer = 0 +FromClient = 1 +ClientStarted = 2 +ClientDied = 3 +StartOfData = 4 +EndOfData = 5 + +Record_Range8 = rq.Struct( + rq.Card8('first'), + rq.Card8('last')) +Record_Range16 = rq.Struct( + rq.Card16('first'), + rq.Card16('last')) +Record_ExtRange = rq.Struct( + rq.Card8('major_range_first'), + rq.Card8('major_range_last'), + rq.Card16('minor_range_first'), + rq.Card16('minor_range_last')) +Record_Range = rq.Struct( + rq.Object('core_requests', Record_Range8), + rq.Object('core_replies', Record_Range8), + rq.Object('ext_requests', Record_ExtRange), + rq.Object('ext_replies', Record_ExtRange), + rq.Object('delivered_events', Record_Range8), + rq.Object('device_events', Record_Range8), + rq.Object('errors', Record_Range8), + rq.Bool('client_started'), + rq.Bool('client_died')) + +Record_ClientInfo = rq.Struct( + rq.Card32('client_resource'), + rq.LengthOf('ranges', 4), + rq.List('ranges', Record_Range)) + + +class RawField(rq.ValueField): + """A field with raw data, stored as a string""" + + structcode = None + + def pack_value(self, val): + return val, len(val), None + + def parse_binary_value(self, data, display, length, format): + return data, '' + + +class GetVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card16('major_version'), + rq.Card16('minor_version')) + _reply = rq.Struct( + rq.Pad(2), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + rq.Pad(20)) + +def get_version(self, major, minor): + return GetVersion( + display = self.display, + opcode = self.display.get_extension_major(extname), + major_version = major, + minor_version = minor) + + +class CreateContext(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + rq.Card32('context'), # Record_RC + rq.Card8('element_header'), # Record_Element_Header + rq.Pad(3), + rq.LengthOf('clients', 4), + rq.LengthOf('ranges', 4), + rq.List('clients', rq.Card32Obj), + rq.List('ranges', Record_Range)) + +def create_context(self, datum_flags, clients, ranges): + context = self.display.allocate_resource_id() + CreateContext( + display = self.display, + opcode = self.display.get_extension_major(extname), + context = context, + element_header = datum_flags, + clients = clients, + ranges = ranges) + return context + + +class RegisterClients(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + rq.Card32('context'), # Record_RC + rq.Card8('element_header'), # Record_Element_Header + rq.Pad(3), + rq.LengthOf('clients', 4), + rq.LengthOf('ranges', 4), + rq.List('clients', rq.Card32Obj), + rq.List('ranges', Record_Range)) + +def register_clients(self, context, element_header, clients, ranges): + RegisterClients( + display = self.display, + opcode = self.display.get_extension_major(extname), + context = context, + element_header = element_header, + clients = clients, + ranges = ranges) + + +class UnregisterClients(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + rq.Card32('context'), # Record_RC + rq.LengthOf('clients', 4), + rq.List('clients', rq.Card32Obj)) + +def unregister_clients(self, context, clients): + UnregisterClients( + display = self.display, + opcode = self.display.get_extension_major(extname), + context = context, + clients = clients) + + +class GetContext(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + rq.Card32('context')) # Record_RC + _reply = rq.Struct( + rq.Pad(2), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card8('element_header'), # Record_Element_Header + rq.Pad(3), + rq.LengthOf('client_info', 4), + rq.Pad(16), + rq.List('client_info', Record_ClientInfo)) + +def get_context(self, context): + return GetContext( + display = self.display, + opcode = self.display.get_extension_major(extname), + context = context) + + +class EnableContext(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(5), + rq.RequestLength(), + rq.Card32('context')) # Record_RC + _reply = rq.Struct( + rq.Pad(1), + rq.Card8('category'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card8('element_header'), # Record_Element_Header + rq.Bool('client_swapped'), + rq.Pad(2), + rq.Card32('id_base'), # Record_XIDBase + rq.Card32('server_time'), + rq.Card32('recorded_sequence_number'), + rq.Pad(8), + RawField('data')) + + # This request receives multiple responses, so we need to keep + # ourselves in the 'sent_requests' list in order to receive them all. + + # See the discussion on ListFonstsWithInfo in request.py + + def __init__(self, callback, *args, **keys): + self._callback = callback + rq.ReplyRequest.__init__(self, *args, **keys) + + def _parse_response(self, data): + r, d = self._reply.parse_binary(data, self._display) + self._callback(r) + + if r.category == StartOfData: + # Hack ourselves a sequence number, used by the code in + # Xlib.protocol.display.Display.parse_request_response() + self.sequence_number = r.sequence_number + + if r.category == EndOfData: + self._response_lock.acquire() + self._data = r + self._response_lock.release() + else: + self._display.sent_requests.insert(0, self) + +def enable_context(self, context, callback): + EnableContext( + callback = callback, + display = self.display, + opcode = self.display.get_extension_major(extname), + context = context) + + +class DisableContext(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(6), + rq.RequestLength(), + rq.Card32('context')) # Record_RC + +def disable_context(self, context): + DisableContext( + display = self.display, + opcode = self.display.get_extension_major(extname), + context = context) + + +class FreeContext(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(7), + rq.RequestLength(), + rq.Card32('context')) # Record_RC + +def free_context(self, context): + FreeContext( + display = self.display, + opcode = self.display.get_extension_major(extname), + context = context) + self.display.free_resource_id(context) + + +def init(disp, info): + disp.extension_add_method('display', 'record_get_version', get_version) + disp.extension_add_method('display', 'record_create_context', create_context) + disp.extension_add_method('display', 'record_register_clients', register_clients) + disp.extension_add_method('display', 'record_unregister_clients', unregister_clients) + disp.extension_add_method('display', 'record_get_context', get_context) + disp.extension_add_method('display', 'record_enable_context', enable_context) + disp.extension_add_method('display', 'record_disable_context', disable_context) + disp.extension_add_method('display', 'record_free_context', free_context) diff --git a/Xlib/ext/res.py b/Xlib/ext/res.py new file mode 100644 index 0000000..f2c4e9f --- /dev/null +++ b/Xlib/ext/res.py @@ -0,0 +1,288 @@ +# Xlib.ext.res -- X-Resource extension module +# +# Copyright (C) 2021 Aleksei Bavshin +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, +# Fifth Floor, +# Boston, MA 02110-1301 USA + +"""X-Resource extension allows a client to query the X server about its usage +of various resources. + +For detailed description see any of the following documents. +Protocol specification: + https://www.x.org/releases/current/doc/resourceproto/resproto.txt +XCB Protocol specification: + https://cgit.freedesktop.org/xcb/proto/tree/src/res.xml +""" +from Xlib.protocol import rq + +RES_MAJOR_VERSION = 1 +RES_MINOR_VERSION = 2 + +extname = "X-Resource" + +# v1.0 +ResQueryVersion = 0 +ResQueryClients = 1 +ResQueryClientResources = 2 +ResQueryClientPixmapBytes = 3 +# v1.2 +ResQueryClientIds = 4 +ResQueryResourceBytes = 5 + + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8("opcode"), + rq.Opcode(ResQueryVersion), + rq.RequestLength(), + rq.Card8("client_major"), + rq.Card8("client_minor"), + rq.Pad(2)) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16("sequence_number"), + rq.ReplyLength(), + rq.Card16("server_major"), + rq.Card16("server_minor"), + rq.Pad(20)) + + +def query_version(self, client_major=RES_MAJOR_VERSION, + client_minor=RES_MINOR_VERSION): + """ Query the protocol version supported by the X server. + + The client sends the highest supported version to the server and the + server sends the highest version it supports, but no higher than the + requested version.""" + return QueryVersion( + display=self.display, + opcode=self.display.get_extension_major(extname), + client_major=client_major, + client_minor=client_minor) + + +Client = rq.Struct( + rq.Card32("resource_base"), + rq.Card32("resource_mask")) + + +class QueryClients(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8("opcode"), + rq.Opcode(ResQueryClients), + rq.RequestLength()) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16("sequence_number"), + rq.ReplyLength(), + rq.LengthOf("clients", 4), + rq.Pad(20), + rq.List("clients", Client)) + + +def query_clients(self): + """Request the list of all currently connected clients.""" + return QueryClients( + display=self.display, + opcode=self.display.get_extension_major(extname)) + + +Type = rq.Struct( + rq.Card32("resource_type"), + rq.Card32("count")) + + +class QueryClientResources(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8("opcode"), + rq.Opcode(ResQueryClientResources), + rq.RequestLength(), + rq.Card32("client")) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16("sequence_number"), + rq.ReplyLength(), + rq.LengthOf("types", 4), + rq.Pad(20), + rq.List("types", Type)) + + +def query_client_resources(self, client): + """Request the number of resources owned by a client. + + The server will return the counts of each type of resource. + """ + return QueryClientResources( + display=self.display, + opcode=self.display.get_extension_major(extname), + client=client) + + +class QueryClientPixmapBytes(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8("opcode"), + rq.Opcode(ResQueryClientPixmapBytes), + rq.RequestLength(), + rq.Card32("client")) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16("sequence_number"), + rq.ReplyLength(), + rq.Card32("bytes"), + rq.Card32("bytes_overflow"), + rq.Pad(16)) + + +def query_client_pixmap_bytes(self, client): + """Query the pixmap usage of some client. + + The returned number is a sum of memory usage of each pixmap that can be + attributed to the given client. + """ + return QueryClientPixmapBytes( + display=self.display, + opcode=self.display.get_extension_major(extname), + client=client) + + +class SizeOf(rq.LengthOf): + """A SizeOf stores the size in bytes of some other Field whose size + may vary, e.g. List + """ + def __init__(self, name, size, item_size): + rq.LengthOf.__init__(self, name, size) + self.item_size = item_size + + def parse_value(self, length, display): + return length // self.item_size + + +ClientXIDMask = 1 << 0 +LocalClientPIDMask = 1 << 1 + + +ClientIdSpec = rq.Struct( + rq.Card32("client"), + rq.Card32("mask")) + + +ClientIdValue = rq.Struct( + rq.Object("spec", ClientIdSpec), + SizeOf("value", 4, 4), + rq.List("value", rq.Card32Obj)) + + +class QueryClientIds(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8("opcode"), + rq.Opcode(ResQueryClientIds), + rq.RequestLength(), + rq.LengthOf("specs", 4), + rq.List("specs", ClientIdSpec)) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16("sequence_number"), + rq.ReplyLength(), + rq.LengthOf("ids", 4), + rq.Pad(20), + rq.List("ids", ClientIdValue)) + + +def query_client_ids(self, specs): + """Request to identify a given set of clients with some identification method. + + The request sends a list of specifiers that select clients and + identification methods to server. The server then tries to identify the + chosen clients using the identification methods specified for each client. + The server returns IDs for those clients that were successfully identified. + """ + return QueryClientIds( + display=self.display, + opcode=self.display.get_extension_major(extname), + specs=specs) + + +ResourceIdSpec = rq.Struct( + rq.Card32("resource"), + rq.Card32("type")) + + +ResourceSizeSpec = rq.Struct( + # inline struct ResourceIdSpec to work around + # a parser bug with nested objects + rq.Card32("resource"), + rq.Card32("type"), + rq.Card32("bytes"), + rq.Card32("ref_count"), + rq.Card32("use_count")) + + +ResourceSizeValue = rq.Struct( + rq.Object("size", ResourceSizeSpec), + rq.LengthOf("cross_references", 4), + rq.List("cross_references", ResourceSizeSpec)) + + +class QueryResourceBytes(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8("opcode"), + rq.Opcode(ResQueryResourceBytes), + rq.RequestLength(), + rq.Card32("client"), + rq.LengthOf("specs", 4), + rq.List("specs", ResourceIdSpec)) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16("sequence_number"), + rq.ReplyLength(), + rq.LengthOf("sizes", 4), + rq.Pad(20), + rq.List("sizes", ResourceSizeValue)) + + +def query_resource_bytes(self, client, specs): + """Query the sizes of resources from X server. + + The request sends a list of specifiers that selects resources for size + calculation. The server tries to calculate the sizes of chosen resources + and returns an estimate for a resource only if the size could be determined + """ + return QueryResourceBytes( + display=self.display, + opcode=self.display.get_extension_major(extname), + client=client, + specs=specs) + + +def init(disp, info): + disp.extension_add_method("display", "res_query_version", query_version) + disp.extension_add_method("display", "res_query_clients", query_clients) + disp.extension_add_method("display", "res_query_client_resources", + query_client_resources) + disp.extension_add_method("display", "res_query_client_pixmap_bytes", + query_client_pixmap_bytes) + disp.extension_add_method("display", "res_query_client_ids", + query_client_ids) + disp.extension_add_method("display", "res_query_resource_bytes", + query_resource_bytes) diff --git a/Xlib/ext/screensaver.py b/Xlib/ext/screensaver.py new file mode 100644 index 0000000..12f4325 --- /dev/null +++ b/Xlib/ext/screensaver.py @@ -0,0 +1,198 @@ +# Xlib.ext.screensaver -- X ScreenSaver extension module +# +# Copyright (C) 2022 Vladimir Panteleev +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, +# Fifth Floor, +# Boston, MA 02110-1301 USA + +"""This extension allows registering the client as an X screensaver, +or query information about the current screensaver. + +For detailed description see any of the following documents. +Protocol specification: + https://www.x.org/releases/X11R7.7/doc/scrnsaverproto/saver.html +XCB Protocol specification: + https://cgit.freedesktop.org/xcb/proto/tree/src/screensaver.xml + +""" + +from Xlib import X +from Xlib.protocol import rq, structs + +extname = 'MIT-SCREEN-SAVER' + +# Event members +NotifyMask = 1 +CycleMask = 2 + +# Notify state +StateOff = 0 +StateOn = 1 +StateCycle = 2 + +# Notify kind +KindBlanked = 0 +KindInternal = 1 +KindExternal = 2 + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card8('major_version'), + rq.Card8('minor_version'), + rq.Pad(2), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + rq.Pad(20), + ) + +def query_version(self): + return QueryVersion(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=0) + + +class QueryInfo(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + rq.Drawable('drawable'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('state'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('saver_window'), + rq.Card32('til_or_since'), + rq.Card32('idle'), + rq.Card32('event_mask'), # rq.Set('event_mask', 4, (NotifyMask, CycleMask)), + rq.Card8('kind'), + rq.Pad(7), + ) + +def query_info(self): + return QueryInfo(display=self.display, + opcode=self.display.get_extension_major(extname), + drawable=self, + ) + + +class SelectInput(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.Card32('event_mask'), # rq.Set('event_mask', 4, (NotifyMask, CycleMask)), + ) + +def select_input(self, mask): + return SelectInput(display=self.display, + opcode=self.display.get_extension_major(extname), + drawable=self, + event_mask=mask, + ) + + +class SetAttributes(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('border_width'), + rq.Set('window_class', 1, (X.CopyFromParent, X.InputOutput, X.InputOnly)), + rq.Card8('depth'), + rq.Card32('visual'), + structs.WindowValues('attrs'), + ) + +def set_attributes(self, x, y, width, height, border_width, + window_class = X.CopyFromParent, + depth = X.CopyFromParent, + visual = X.CopyFromParent, + onerror = None, + **keys): + return SetAttributes(display=self.display, + onerror = onerror, + opcode=self.display.get_extension_major(extname), + drawable=self, + x = x, + y = y, + width = width, + height = height, + border_width = border_width, + window_class = window_class, + depth = depth, + visual = visual, + attrs = keys) + + +class UnsetAttributes(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + rq.Drawable('drawable'), + ) + +def unset_attributes(self, onerror = None): + return UnsetAttributes(display=self.display, + onerror = onerror, + opcode=self.display.get_extension_major(extname), + drawable=self) + + +class Notify(rq.Event): + _code = None + _fields = rq.Struct( + rq.Card8('type'), + rq.Set('state', 1, (StateOff, StateOn, StateCycle)), + rq.Card16('sequence_number'), + rq.Card32('timestamp'), + rq.Window('root'), + rq.Window('window'), + rq.Set('kind', 1, (KindBlanked, KindInternal, KindExternal)), + rq.Bool('forced'), + rq.Pad(14), + ) + +def init(disp, info): + disp.extension_add_method('display', 'screensaver_query_version', query_version) + disp.extension_add_method('drawable', 'screensaver_query_info', query_info) + disp.extension_add_method('drawable', 'screensaver_select_input', select_input) + disp.extension_add_method('drawable', 'screensaver_set_attributes', set_attributes) + disp.extension_add_method('drawable', 'screensaver_unset_attributes', unset_attributes) + + disp.extension_add_event(info.first_event + 0, Notify) diff --git a/Xlib/ext/security.py b/Xlib/ext/security.py new file mode 100644 index 0000000..a821017 --- /dev/null +++ b/Xlib/ext/security.py @@ -0,0 +1,139 @@ +# Xlib.ext.security -- SECURITY extension module +# +# Copyright (C) 2010-2013 Outpost Embedded, LLC +# Forest Bond +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +''' +A partial implementation of the SECURITY extension. Support for the +SecurityAuthorizationRevoked event is not implemented. +''' + +from Xlib.protocol import rq + + +extname = 'SECURITY' + + +SecurityClientTrusted = 0 +SecurityClientUntrusted = 1 + +SecurityAuthorizationRevokedMask = 1 + + +AUTHID = rq.Card32 + + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card16('major_version'), + rq.Card16('minor_version') + ) + _reply = rq.Struct(rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + rq.Pad(20) + ) + + +def query_version(self): + return QueryVersion(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=0) + + +class SecurityGenerateAuthorization(rq.ReplyRequest): + # The order of fields here does not match the specifications I've seen + # online, but it *does* match with the X.org implementation. I guess the + # spec is out-of-date. + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + rq.LengthOf('auth_proto', 2), + rq.LengthOf('auth_data', 2), + rq.Card32('value_mask'), + rq.String8('auth_proto'), + rq.Binary('auth_data'), + rq.List('values', rq.Card32Obj) + ) + _reply = rq.Struct(rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + AUTHID('authid'), + rq.LengthOf('auth_data_return', 2), + rq.Pad(18), + rq.Binary('auth_data_return') + ) + + +def generate_authorization(self, auth_proto, auth_data=b'', timeout=None, + trust_level=None, group=None, event_mask=None): + value_mask = 0 + values = [] + if timeout is not None: + value_mask |= 1 + values.append(timeout) + if trust_level is not None: + value_mask |= 2 + values.append(trust_level) + if group is not None: + value_mask |= 4 + values.append(group) + if event_mask is not None: + value_mask |= 8 + values.append(event_mask) + return SecurityGenerateAuthorization(display=self.display, + opcode=self.display.get_extension_major(extname), + value_mask=value_mask, + auth_proto=auth_proto, + auth_data=auth_data, + values=values) + + +class SecurityRevokeAuthorization(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + AUTHID('authid') + ) + + +def revoke_authorization(self, authid): + return SecurityRevokeAuthorization(display=self.display, + opcode=self.display.get_extension_major(extname), + authid=authid) + + +def init(disp, info): + disp.extension_add_method('display', + 'security_query_version', + query_version) + disp.extension_add_method('display', + 'security_generate_authorization', + generate_authorization) + disp.extension_add_method('display', + 'security_revoke_authorization', + revoke_authorization) diff --git a/Xlib/ext/shape.py b/Xlib/ext/shape.py new file mode 100644 index 0000000..05a517a --- /dev/null +++ b/Xlib/ext/shape.py @@ -0,0 +1,297 @@ +# Automatically generated file; DO NOT EDIT. +# Generated from: /usr/share/xcb/shape.xml + +from Xlib.protocol import rq, structs + + +extname = 'SHAPE' + +OP = rq.Card8 + +class SO: + Set = 0 + Union = 1 + Intersect = 2 + Subtract = 3 + Invert = 4 + +class SK: + Bounding = 0 + Clip = 1 + Input = 2 + +class KIND(rq.Set): + + def __init__(self, name): + super(KIND, self).__init__(name, 1, + values=(SK.Bounding, + SK.Clip, + SK.Input)) + +class NotifyEventData(rq.Event): + _code = None + _fields = rq.Struct( + rq.Card8('type'), + KIND('shape_kind'), + rq.Card16('sequence_number'), + rq.Window('affected_window'), + rq.Int16('extents_x'), + rq.Int16('extents_y'), + rq.Card16('extents_width'), + rq.Card16('extents_height'), + rq.Card32('server_time'), + rq.Card8('shaped'), + rq.Pad(11), + ) + +class QueryVersion(rq.ReplyRequest): + + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + ) + +class Rectangles(rq.Request): + + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + OP('operation'), + KIND('destination_kind'), + rq.Card8('ordering'), + rq.Pad(1), + rq.Window('destination_window'), + rq.Int16('x_offset'), + rq.Int16('y_offset'), + rq.List('rectangles', structs.Rectangle, pad=0), + ) + +class Mask(rq.Request): + + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + OP('operation'), + KIND('destination_kind'), + rq.Pad(2), + rq.Window('destination_window'), + rq.Int16('x_offset'), + rq.Int16('y_offset'), + rq.Pixmap('source_bitmap'), + ) + +class Combine(rq.Request): + + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + OP('operation'), + KIND('destination_kind'), + KIND('source_kind'), + rq.Pad(1), + rq.Window('destination_window'), + rq.Int16('x_offset'), + rq.Int16('y_offset'), + rq.Window('source_window'), + ) + +class Offset(rq.Request): + + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + KIND('destination_kind'), + rq.Pad(3), + rq.Window('destination_window'), + rq.Int16('x_offset'), + rq.Int16('y_offset'), + ) + +class QueryExtents(rq.ReplyRequest): + + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(5), + rq.RequestLength(), + rq.Window('destination_window'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card8('bounding_shaped'), + rq.Card8('clip_shaped'), + rq.Pad(2), + rq.Int16('bounding_shape_extents_x'), + rq.Int16('bounding_shape_extents_y'), + rq.Card16('bounding_shape_extents_width'), + rq.Card16('bounding_shape_extents_height'), + rq.Int16('clip_shape_extents_x'), + rq.Int16('clip_shape_extents_y'), + rq.Card16('clip_shape_extents_width'), + rq.Card16('clip_shape_extents_height'), + ) + +class SelectInput(rq.Request): + + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(6), + rq.RequestLength(), + rq.Window('destination_window'), + rq.Card8('enable'), + rq.Pad(3), + ) + +class InputSelected(rq.ReplyRequest): + + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(7), + rq.RequestLength(), + rq.Window('destination_window'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('enabled'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + ) + +class GetRectangles(rq.ReplyRequest): + + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(8), + rq.RequestLength(), + rq.Window('window'), + KIND('source_kind'), + rq.Pad(3), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('ordering'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('rectangles', 4), + rq.Pad(20), + rq.List('rectangles', structs.Rectangle, pad=0), + ) + +class Event: + # Sub events. + Notify = 0 + +def combine(self, operation, destination_kind, source_kind, x_offset, y_offset): + Combine( + display=self.display, + opcode=self.display.get_extension_major(extname), + source_window=self, + operation=operation, + destination_kind=destination_kind, + source_kind=source_kind, + x_offset=x_offset, + y_offset=y_offset, + ) + +def get_rectangles(self, source_kind): + return GetRectangles( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + source_kind=source_kind, + ) + +def input_selected(self, ): + return InputSelected( + display=self.display, + opcode=self.display.get_extension_major(extname), + destination_window=self, + ) + +def mask(self, operation, destination_kind, x_offset, y_offset, source_bitmap): + Mask( + display=self.display, + opcode=self.display.get_extension_major(extname), + destination_window=self, + operation=operation, + destination_kind=destination_kind, + x_offset=x_offset, + y_offset=y_offset, + source_bitmap=source_bitmap, + ) + +def offset(self, destination_kind, x_offset, y_offset): + Offset( + display=self.display, + opcode=self.display.get_extension_major(extname), + destination_window=self, + destination_kind=destination_kind, + x_offset=x_offset, + y_offset=y_offset, + ) + +def query_extents(self, ): + return QueryExtents( + display=self.display, + opcode=self.display.get_extension_major(extname), + destination_window=self, + ) + +def query_version(self, ): + return QueryVersion( + display=self.display, + opcode=self.display.get_extension_major(extname), + ) + +def rectangles(self, operation, destination_kind, ordering, x_offset, y_offset, rectangles): + Rectangles( + display=self.display, + opcode=self.display.get_extension_major(extname), + destination_window=self, + operation=operation, + destination_kind=destination_kind, + ordering=ordering, + x_offset=x_offset, + y_offset=y_offset, + rectangles=rectangles, + ) + +def select_input(self, enable): + SelectInput( + display=self.display, + opcode=self.display.get_extension_major(extname), + destination_window=self, + enable=enable, + ) + +def init(disp, info): + disp.extension_add_method('window', 'shape_combine', combine) + disp.extension_add_method('window', 'shape_get_rectangles', get_rectangles) + disp.extension_add_method('window', 'shape_input_selected', input_selected) + disp.extension_add_method('window', 'shape_mask', mask) + disp.extension_add_method('window', 'shape_offset', offset) + disp.extension_add_method('window', 'shape_query_extents', query_extents) + disp.extension_add_method('display', 'shape_query_version', query_version) + disp.extension_add_method('window', 'shape_rectangles', rectangles) + disp.extension_add_method('window', 'shape_select_input', select_input) + disp.extension_add_event(info.first_event + Event.Notify, NotifyEventData, 'ShapeNotify') + diff --git a/Xlib/ext/xfixes.py b/Xlib/ext/xfixes.py new file mode 100644 index 0000000..59f9277 --- /dev/null +++ b/Xlib/ext/xfixes.py @@ -0,0 +1,201 @@ +# Xlib.ext.xfixes -- XFIXES extension module +# +# Copyright (C) 2010-2011 Outpost Embedded, LLC +# Forest Bond +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +''' +A partial implementation of the XFIXES extension. Only the HideCursor and +ShowCursor requests and SelectionNotify events are provided. +''' + +from Xlib import X +from Xlib.protocol import rq, structs + +extname = 'XFIXES' + +XFixesSelectionNotify = 0 +XFixesCursorNotify = 1 + +XFixesSetSelectionOwnerNotifyMask = (1 << 0) +XFixesSelectionWindowDestroyNotifyMask = (1 << 1) +XFixesSelectionClientCloseNotifyMask = (1 << 2) +XFixesDisplayCursorNotifyMask = (1 << 0) + +XFixesSetSelectionOwnerNotify = 0 +XFixesSelectionWindowDestroyNotify = 1 +XFixesSelectionClientCloseNotify = 2 +XFixesDisplayCursorNotify = 0 + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card32('major_version'), + rq.Card32('minor_version') + ) + _reply = rq.Struct(rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + rq.Pad(16) + ) + + +def query_version(self): + return QueryVersion(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=4, + minor_version=0) + + +class HideCursor(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(29), + rq.RequestLength(), + rq.Window('window') + ) + +def hide_cursor(self): + HideCursor(display=self.display, + opcode=self.display.get_extension_major(extname), + window=self) + + +class ShowCursor(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(30), + rq.RequestLength(), + rq.Window('window') + ) + + +def show_cursor(self): + ShowCursor(display=self.display, + opcode=self.display.get_extension_major(extname), + window=self) + +class SelectSelectionInput(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + rq.Window('window'), + rq.Card32('selection'), + rq.Card32('mask') + ) + +def select_selection_input(self, window, selection, mask): + return SelectSelectionInput(opcode=self.display.get_extension_major(extname), + display=self.display, + window=window, + selection=selection, + mask=mask) + + +class SelectionNotify(rq.Event): + _code = None + _fields = rq.Struct(rq.Card8('type'), + rq.Card8('sub_code'), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Window('owner'), + rq.Card32('selection'), + rq.Card32('timestamp'), + rq.Card32('selection_timestamp'), + rq.Pad(8)) + + +class SetSelectionOwnerNotify(SelectionNotify): + pass + + +class SelectionWindowDestroyNotify(SelectionNotify): + pass + + +class SelectionClientCloseNotify(SelectionNotify): + pass + + +class SelectCursorInput(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + rq.Window('window'), + rq.Card32('mask') + ) + +def select_cursor_input(self, window, mask): + return SelectCursorInput(opcode=self.display.get_extension_major(extname), + display=self.display, + window=window, + cursor_serial=0, + mask=mask) + + +class GetCursorImage(rq.ReplyRequest): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength() + ) + _reply = rq.Struct(rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('xhot'), + rq.Card16('yhot'), + rq.Card32('cursor_serial'), + rq.Pad(8), + rq.List('cursor_image', rq.Card32) + ) + +def get_cursor_image(self, window): + return GetCursorImage(opcode=self.display.get_extension_major(extname), + display=self.display, + ) + + +class DisplayCursorNotify(rq.Event): + _code = None + _fields = rq.Struct(rq.Card8('type'), + rq.Card8('sub_code'), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Card32('cursor_serial'), + rq.Card32('timestamp')) + + +def init(disp, info): + disp.extension_add_method('display', 'xfixes_select_selection_input', select_selection_input) + disp.extension_add_method('display', 'xfixes_query_version', query_version) + disp.extension_add_method('window', 'xfixes_hide_cursor', hide_cursor) + disp.extension_add_method('window', 'xfixes_show_cursor', show_cursor) + disp.extension_add_method('display', 'xfixes_select_cursor_input', select_cursor_input) + disp.extension_add_method('display', 'xfixes_get_cursor_image', get_cursor_image) + + disp.extension_add_subevent(info.first_event + XFixesSelectionNotify, XFixesSetSelectionOwnerNotify, SetSelectionOwnerNotify) + disp.extension_add_subevent(info.first_event + XFixesSelectionNotify, XFixesSelectionWindowDestroyNotify, SelectionWindowDestroyNotify) + disp.extension_add_subevent(info.first_event + XFixesSelectionNotify, XFixesSelectionClientCloseNotify, SelectionClientCloseNotify) + disp.extension_add_subevent(info.first_event + XFixesCursorNotify, XFixesDisplayCursorNotify, DisplayCursorNotify) diff --git a/Xlib/ext/xinerama.py b/Xlib/ext/xinerama.py new file mode 100644 index 0000000..f054670 --- /dev/null +++ b/Xlib/ext/xinerama.py @@ -0,0 +1,223 @@ +# Xlib.ext.xinerama -- Xinerama extension module +# +# Copyright (C) 2006 Mike Meyer +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + + +"""Xinerama - provide access to the Xinerama extension information. + +There are at least there different - and mutually incomparable - +Xinerama extensions available. This uses the one bundled with XFree86 +4.6 and/or Xorg 6.9 in the ati/radeon driver. It uses the include +files from that X distribution, so should work with it as well. I +provide code for the lone Sun 1.0 request that isn't part of 1.1, but +this is untested because I don't have a server that implements it. + +The functions loosely follow the libXineram functions. Mostly, they +return an rq.Struct in lieu of passing in pointers that get data from +the rq.Struct crammed into them. The exception is isActive, which +returns the state information - because that's what libXinerama does.""" + + +from Xlib import X +from Xlib.protocol import rq, structs + +extname = 'XINERAMA' + + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card8('major_version'), + rq.Card8('minor_version'), + rq.Pad(2), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + rq.Pad(20), + ) + +def query_version(self): + return QueryVersion(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +class GetState(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + rq.Window('window'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Bool('state'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('window'), + rq.Pad(20), + ) + +def get_state(self): + return GetState(display=self.display, + opcode=self.display.get_extension_major(extname), + window=self.id, + ) + + +class GetScreenCount(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + rq.Window('window'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('screen_count'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('window'), + rq.Pad(20), + ) + +def get_screen_count(self): + return GetScreenCount(display=self.display, + opcode=self.display.get_extension_major(extname), + window=self.id, + ) + + +class GetScreenSize(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + rq.Window('window'), + rq.Card32('screen'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Card32('length'), + rq.Card32('width'), + rq.Card32('height'), + rq.Window('window'), + rq.Card32('screen'), + rq.Pad(8), + ) + +def get_screen_size(self, screen_no): + """Returns the size of the given screen number""" + return GetScreenSize(display=self.display, + opcode=self.display.get_extension_major(extname), + window=self.id, + screen=screen_no, + ) + + +# IsActive is only available from Xinerama 1.1 and later. +# It should be used in preference to GetState. +class IsActive(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('state'), + rq.Pad(20), + ) + +def is_active(self): + r = IsActive(display=self.display, + opcode=self.display.get_extension_major(extname), + ) + return r.state + + +# QueryScreens is only available from Xinerama 1.1 and later +class QueryScreens(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(5), + rq.RequestLength(), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('number'), + rq.Pad(20), + rq.List('screens', structs.Rectangle), + ) + +def query_screens(self): + # Hmm. This one needs to read the screen data from the socket. Ooops... + return QueryScreens(display=self.display, + opcode=self.display.get_extension_major(extname), + ) + + +# GetInfo is only available from some Xinerama 1.0, and *NOT* later! Untested +class GetInfo(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + rq.Card32('visual'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('window'), + # An array of subwindow slots goes here. Bah. + ) + +def get_info(self, visual): + r = GetInfo(display=self.display, + opcode=self.display.get_extension_major(extname), + visual=visual) + +def init(disp, info): + disp.extension_add_method('display', 'xinerama_query_version', query_version) + disp.extension_add_method('window', 'xinerama_get_state', get_state) + disp.extension_add_method('window', 'xinerama_get_screen_count', get_screen_count) + disp.extension_add_method('window', 'xinerama_get_screen_size', get_screen_size) + disp.extension_add_method('display', 'xinerama_is_active', is_active) + disp.extension_add_method('display', 'xinerama_query_screens', query_screens) + disp.extension_add_method('display', 'xinerama_get_info', get_info) diff --git a/Xlib/ext/xinput.py b/Xlib/ext/xinput.py new file mode 100644 index 0000000..f921806 --- /dev/null +++ b/Xlib/ext/xinput.py @@ -0,0 +1,777 @@ +# Xlib.ext.xinput -- XInput extension module +# +# Copyright (C) 2012 Outpost Embedded, LLC +# Forest Bond +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +''' +A very incomplete implementation of the XInput extension. +''' + +import sys +import array +import struct + +# Python 2/3 compatibility. +from six import integer_types + +from Xlib.protocol import rq +from Xlib import X + + +extname = 'XInputExtension' + +PropertyDeleted = 0 +PropertyCreated = 1 +PropertyModified = 2 + +NotifyNormal = 0 +NotifyGrab = 1 +NotifyUngrab = 2 +NotifyWhileGrabbed = 3 +NotifyPassiveGrab = 4 +NotifyPassiveUngrab = 5 + +NotifyAncestor = 0 +NotifyVirtual = 1 +NotifyInferior = 2 +NotifyNonlinear = 3 +NotifyNonlinearVirtual = 4 +NotifyPointer = 5 +NotifyPointerRoot = 6 +NotifyDetailNone = 7 + +GrabtypeButton = 0 +GrabtypeKeycode = 1 +GrabtypeEnter = 2 +GrabtypeFocusIn = 3 +GrabtypeTouchBegin = 4 + +AnyModifier = (1 << 31) +AnyButton = 0 +AnyKeycode = 0 + +AsyncDevice = 0 +SyncDevice = 1 +ReplayDevice = 2 +AsyncPairedDevice = 3 +AsyncPair = 4 +SyncPair = 5 + +SlaveSwitch = 1 +DeviceChange = 2 + +MasterAdded = (1 << 0) +MasterRemoved = (1 << 1) +SlaveAdded = (1 << 2) +SlaveRemoved = (1 << 3) +SlaveAttached = (1 << 4) +SlaveDetached = (1 << 5) +DeviceEnabled = (1 << 6) +DeviceDisabled = (1 << 7) + +AddMaster = 1 +RemoveMaster = 2 +AttachSlave = 3 +DetachSlave = 4 + +AttachToMaster = 1 +Floating = 2 + +ModeRelative = 0 +ModeAbsolute = 1 + +MasterPointer = 1 +MasterKeyboard = 2 +SlavePointer = 3 +SlaveKeyboard = 4 +FloatingSlave = 5 + +KeyClass = 0 +ButtonClass = 1 +ValuatorClass = 2 +ScrollClass = 3 +TouchClass = 8 + +KeyRepeat = (1 << 16) + +AllDevices = 0 +AllMasterDevices = 1 + +DeviceChanged = 1 +KeyPress = 2 +KeyRelease = 3 +ButtonPress = 4 +ButtonRelease = 5 +Motion = 6 +Enter = 7 +Leave = 8 +FocusIn = 9 +FocusOut = 10 +HierarchyChanged = 11 +PropertyEvent = 12 +RawKeyPress = 13 +RawKeyRelease = 14 +RawButtonPress = 15 +RawButtonRelease = 16 +RawMotion = 17 + +DeviceChangedMask = (1 << DeviceChanged) +KeyPressMask = (1 << KeyPress) +KeyReleaseMask = (1 << KeyRelease) +ButtonPressMask = (1 << ButtonPress) +ButtonReleaseMask = (1 << ButtonRelease) +MotionMask = (1 << Motion) +EnterMask = (1 << Enter) +LeaveMask = (1 << Leave) +FocusInMask = (1 << FocusIn) +FocusOutMask = (1 << FocusOut) +HierarchyChangedMask = (1 << HierarchyChanged) +PropertyEventMask = (1 << PropertyEvent) +RawKeyPressMask = (1 << RawKeyPress) +RawKeyReleaseMask = (1 << RawKeyRelease) +RawButtonPressMask = (1 << RawButtonPress) +RawButtonReleaseMask = (1 << RawButtonRelease) +RawMotionMask = (1 << RawMotion) + +GrabModeSync = 0 +GrabModeAsync = 1 +GrabModeTouch = 2 + +DEVICEID = rq.Card16 +DEVICE = rq.Card16 +DEVICEUSE = rq.Card8 + +PROPERTY_TYPE_FLOAT = 'FLOAT' + +class FP1616(rq.Int32): + + def check_value(self, value): + return int(value * 65536.0) + + def parse_value(self, value, display): + return float(value) / float(1 << 16) + +class FP3232(rq.ValueField): + structcode = 'lL' + structvalues = 2 + + def check_value(self, value): + return value + + def parse_value(self, value, display): + integral, frac = value + ret = float(integral) + # optimised math.ldexp(float(frac), -32) + ret += float(frac) * (1.0 / (1 << 32)) + return ret + +class XIQueryVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(47), + rq.RequestLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + rq.Pad(20), + ) + + +def query_version(self): + return XIQueryVersion( + display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=2, + minor_version=0, + ) + +class Mask(rq.List): + + def __init__(self, name): + rq.List.__init__(self, name, rq.Card32, pad=0) + + def pack_value(self, val): + + mask_seq = array.array(rq.struct_to_array_codes['L']) + + if isinstance(val, integer_types): + # We need to build a "binary mask" that (as far as I can tell) is + # encoded in native byte order from end to end. The simple case is + # with a single unsigned 32-bit value, for which we construct an + # array with just one item. For values too big to fit inside 4 + # bytes we build a longer array, being careful to maintain native + # byte order across the entire set of values. + if sys.byteorder == 'little': + def fun(val): + mask_seq.insert(0, val) + elif sys.byteorder == 'big': + fun = mask_seq.append + else: + raise AssertionError(sys.byteorder) + while val: + fun(val & 0xFFFFFFFF) + val = val >> 32 + else: + mask_seq.extend(val) + + return rq.encode_array(mask_seq), len(mask_seq), None + +EventMask = rq.Struct( + DEVICE('deviceid'), + rq.LengthOf('mask', 2), + Mask('mask'), +) + + +class XISelectEvents(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(46), + rq.RequestLength(), + rq.Window('window'), + rq.LengthOf('masks', 2), + rq.Pad(2), + rq.List('masks', EventMask), + ) + +def select_events(self, event_masks): + ''' + select_events(event_masks) + + event_masks: + Sequence of (deviceid, mask) pairs, where deviceid is a numerical device + ID, or AllDevices or AllMasterDevices, and mask is either an unsigned + integer or sequence of 32 bits unsigned values + ''' + return XISelectEvents( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + masks=event_masks, + ) + +AnyInfo = rq.Struct( + rq.Card16('type'), + rq.Card16('length'), + rq.Card16('sourceid'), + rq.Pad(2), +) + +class ButtonMask(object): + + def __init__(self, value, length): + self._value = value + self._length = length + + def __len__(self): + return self._length + + def __getitem__(self, key): + return self._value & (1 << key) + + def __str__(self): + return repr(self) + + def __repr__(self): + return '0b{value:0{width}b}'.format(value=self._value, + width=self._length) + +class ButtonState(rq.ValueField): + + structcode = None + + def __init__(self, name): + rq.ValueField.__init__(self, name) + + def parse_binary_value(self, data, display, length, fmt): + # Mask: bitfield of button states. + mask_len = 4 * ((((length + 7) >> 3) + 3) >> 2) + mask_data = data[:mask_len] + mask_value = 0 + for byte in reversed(struct.unpack('={0:d}B'.format(mask_len), mask_data)): + mask_value <<= 8 + mask_value |= byte + data = data[mask_len:] + assert (mask_value & 1) == 0 + return ButtonMask(mask_value >> 1, length), data + +ButtonInfo = rq.Struct( + rq.Card16('type'), + rq.Card16('length'), + rq.Card16('sourceid'), + rq.LengthOf(('state', 'labels'), 2), + ButtonState('state'), + rq.List('labels', rq.Card32), +) + +KeyInfo = rq.Struct( + rq.Card16('type'), + rq.Card16('length'), + rq.Card16('sourceid'), + rq.LengthOf('keycodes', 2), + rq.List('keycodes', rq.Card32), +) + +ValuatorInfo = rq.Struct( + rq.Card16('type'), + rq.Card16('length'), + rq.Card16('sourceid'), + rq.Card16('number'), + rq.Card32('label'), + FP3232('min'), + FP3232('max'), + FP3232('value'), + rq.Card32('resolution'), + rq.Card8('mode'), + rq.Pad(3), +) + +ScrollInfo = rq.Struct( + rq.Card16('type'), + rq.Card16('length'), + rq.Card16('sourceid'), + rq.Card16('number'), + rq.Card16('scroll_type'), + rq.Pad(2), + rq.Card32('flags'), + FP3232('increment'), +) + +TouchInfo = rq.Struct( + rq.Card16('type'), + rq.Card16('length'), + rq.Card16('sourceid'), + rq.Card8('mode'), + rq.Card8('num_touches'), +) + +INFO_CLASSES = { + KeyClass: KeyInfo, + ButtonClass: ButtonInfo, + ValuatorClass: ValuatorInfo, + ScrollClass: ScrollInfo, + TouchClass: TouchInfo, +} + +class ClassInfoClass(object): + + structcode = None + + def parse_binary(self, data, display): + class_type, length = struct.unpack('=HH', data[:4]) + class_struct = INFO_CLASSES.get(class_type, AnyInfo) + class_data, _ = class_struct.parse_binary(data, display) + data = data[length * 4:] + return class_data, data + +ClassInfo = ClassInfoClass() + +DeviceInfo = rq.Struct( + DEVICEID('deviceid'), + rq.Card16('use'), + rq.Card16('attachment'), + rq.LengthOf('classes', 2), + rq.LengthOf('name', 2), + rq.Bool('enabled'), + rq.Pad(1), + rq.String8('name', 4), + rq.List('classes', ClassInfo), +) + +class XIQueryDevice(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(48), + rq.RequestLength(), + DEVICEID('deviceid'), + rq.Pad(2), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('devices', 2), + rq.Pad(22), + rq.List('devices', DeviceInfo), + ) + +def query_device(self, deviceid): + return XIQueryDevice( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + ) + +class XIListProperties(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(56), + rq.RequestLength(), + DEVICEID('deviceid'), + rq.Pad(2), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('atoms', 2), + rq.Pad(22), + rq.List('atoms', rq.Card32Obj), + ) + +def list_device_properties(self, deviceid): + return XIListProperties( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + ) + +class XIGetProperty(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(59), + rq.RequestLength(), + DEVICEID('deviceid'), + rq.Card8('delete'), + rq.Pad(1), + rq.Card32('property'), + rq.Card32('type'), + rq.Card32('offset'), + rq.Card32('length'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('type'), + rq.Card32('bytes_after'), + rq.LengthOf('value', 4), + rq.Format('value', 1), + rq.Pad(11), + rq.PropertyData('value') + ) + +def get_device_property(self, deviceid, property, type, offset, length, delete=False): + return XIGetProperty( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + property=property, + type=type, + offset=offset, + length=length, + delete=delete, + ) + +class XIChangeProperty(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(57), + rq.RequestLength(), + DEVICEID('deviceid'), + rq.Card8('mode'), + rq.Format('value', 1), + rq.Card32('property'), + rq.Card32('type'), + rq.LengthOf('value', 4), + rq.PropertyData('value'), + ) + +def change_device_property(self, deviceid, property, type, mode, value): + return XIChangeProperty( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + property=property, + type=type, + mode=mode, + value=value, + ) + +class XIDeleteProperty(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(58), + rq.RequestLength(), + DEVICEID('deviceid'), + rq.Pad(2), + rq.Card32('property'), + ) + +def delete_device_property(self, deviceid, property): + return XIDeleteProperty( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + property=property, + ) + +class XIGrabDevice(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(51), + rq.RequestLength(), + rq.Window('grab_window'), + rq.Card32('time'), + rq.Cursor('cursor', (X.NONE, )), + DEVICEID('deviceid'), + rq.Set('grab_mode', 1, (GrabModeSync, GrabModeAsync)), + rq.Set('paired_device_mode', 1, (GrabModeSync, GrabModeAsync)), + rq.Bool('owner_events'), + rq.Pad(1), + rq.LengthOf('mask', 2), + Mask('mask'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card8('status'), + rq.Pad(23), + ) + +def grab_device(self, deviceid, time, grab_mode, paired_device_mode, owner_events, event_mask): + return XIGrabDevice( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + grab_window=self, + time=time, + cursor=X.NONE, + grab_mode=grab_mode, + paired_device_mode=paired_device_mode, + owner_events=owner_events, + mask=event_mask, + ) + +class XIUngrabDevice(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(52), + rq.RequestLength(), + rq.Card32('time'), + DEVICEID('deviceid'), + rq.Pad(2), + ) + +def ungrab_device(self, deviceid, time): + return XIUngrabDevice( + display=self.display, + opcode=self.display.get_extension_major(extname), + time=time, + deviceid=deviceid, + ) + +class XIPassiveGrabDevice(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(54), + rq.RequestLength(), + rq.Card32('time'), + rq.Window('grab_window'), + rq.Cursor('cursor', (X.NONE, )), + rq.Card32('detail'), + DEVICEID('deviceid'), + rq.LengthOf('modifiers', 2), + rq.LengthOf('mask', 2), + rq.Set('grab_type', 1, (GrabtypeButton, GrabtypeKeycode, GrabtypeEnter, + GrabtypeFocusIn, GrabtypeTouchBegin)), + rq.Set('grab_mode', 1, (GrabModeSync, GrabModeAsync)), + rq.Set('paired_device_mode', 1, (GrabModeSync, GrabModeAsync)), + rq.Bool('owner_events'), + rq.Pad(2), + Mask('mask'), + rq.List('modifiers', rq.Card32), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('modifiers', 2), + rq.Pad(22), + rq.List('modifiers', rq.Card32), + ) + +def passive_grab_device(self, deviceid, time, detail, + grab_type, grab_mode, paired_device_mode, + owner_events, event_mask, modifiers): + return XIPassiveGrabDevice( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + grab_window=self, + time=time, + cursor=X.NONE, + detail=detail, + grab_type=grab_type, + grab_mode=grab_mode, + paired_device_mode=paired_device_mode, + owner_events=owner_events, + mask=event_mask, + modifiers=modifiers, + ) + +def grab_keycode(self, deviceid, time, keycode, + grab_mode, paired_device_mode, + owner_events, event_mask, modifiers): + return passive_grab_device(self, deviceid, time, keycode, + GrabtypeKeycode, + grab_mode, paired_device_mode, + owner_events, event_mask, modifiers) + +class XIPassiveUngrabDevice(rq.Request): + + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(55), + rq.RequestLength(), + rq.Window('grab_window'), + rq.Card32('detail'), + DEVICEID('deviceid'), + rq.LengthOf('modifiers', 2), + rq.Set('grab_type', 1, (GrabtypeButton, GrabtypeKeycode, + GrabtypeEnter, GrabtypeFocusIn, + GrabtypeTouchBegin)), + rq.Pad(3), + rq.List('modifiers', rq.Card32), + ) + +def passive_ungrab_device(self, deviceid, detail, grab_type, modifiers): + return XIPassiveUngrabDevice( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + grab_window=self, + detail=detail, + grab_type=grab_type, + modifiers=modifiers, + ) + +def ungrab_keycode(self, deviceid, keycode, modifiers): + return passive_ungrab_device(self, deviceid, keycode, + GrabtypeKeycode, modifiers) + +HierarchyInfo = rq.Struct( + DEVICEID('deviceid'), + DEVICEID('attachment'), + DEVICEUSE('type'), + rq.Bool('enabled'), + rq.Pad(2), + rq.Card32('flags'), +) + + +HierarchyEventData = rq.Struct( + DEVICEID('deviceid'), + rq.Card32('time'), + rq.Card32('flags'), + rq.LengthOf('info', 2), + rq.Pad(10), + rq.List('info', HierarchyInfo), +) + +ModifierInfo = rq.Struct( + rq.Card32('base_mods'), + rq.Card32('latched_mods'), + rq.Card32('locked_mods'), + rq.Card32('effective_mods'), +) + +GroupInfo = rq.Struct( + rq.Card8('base_group'), + rq.Card8('latched_group'), + rq.Card8('locked_group'), + rq.Card8('effective_group'), +) + +DeviceEventData = rq.Struct( + DEVICEID('deviceid'), + rq.Card32('time'), + rq.Card32('detail'), + rq.Window('root'), + rq.Window('event'), + rq.Window('child'), + FP1616('root_x'), + FP1616('root_y'), + FP1616('event_x'), + FP1616('event_y'), + rq.LengthOf('buttons', 2), + rq.Card16('valulators_len'), + DEVICEID('sourceid'), + rq.Pad(2), + rq.Card32('flags'), + rq.Object('mods', ModifierInfo), + rq.Object('groups', GroupInfo), + ButtonState('buttons'), +) + +DeviceChangedEventData = rq.Struct( + DEVICEID('deviceid'), + rq.Card32('time'), + rq.LengthOf('classes', 2), + DEVICEID('sourceid'), + rq.Card8('reason'), + rq.Pad(11), + rq.List('classes', ClassInfo), +) + +PropertyEventData = rq.Struct( + DEVICEID('deviceid'), + rq.Card32('time'), + rq.Card32('property'), + rq.Card8('what'), + rq.Pad(11), +) + +def init(disp, info): + disp.extension_add_method('display', 'xinput_query_version', query_version) + disp.extension_add_method('window', 'xinput_select_events', select_events) + disp.extension_add_method('display', 'xinput_query_device', query_device) + disp.extension_add_method('window', 'xinput_grab_device', grab_device) + disp.extension_add_method('display', 'xinput_ungrab_device', ungrab_device) + disp.extension_add_method('window', 'xinput_grab_keycode', grab_keycode) + disp.extension_add_method('window', 'xinput_ungrab_keycode', ungrab_keycode) + disp.extension_add_method('display', 'xinput_get_device_property', get_device_property) + disp.extension_add_method('display', 'xinput_list_device_properties', list_device_properties) + disp.extension_add_method('display', 'xinput_change_device_property', change_device_property) + disp.extension_add_method('display', 'xinput_delete_device_property', delete_device_property) + if hasattr(disp,"ge_add_event_data"): + for device_event in (ButtonPress, ButtonRelease, KeyPress, KeyRelease, Motion): + disp.ge_add_event_data(info.major_opcode, device_event, DeviceEventData) + disp.ge_add_event_data(info.major_opcode, DeviceChanged, DeviceEventData) + disp.ge_add_event_data(info.major_opcode, HierarchyChanged, HierarchyEventData) + disp.ge_add_event_data(info.major_opcode, PropertyEvent, PropertyEventData) diff --git a/Xlib/ext/xtest.py b/Xlib/ext/xtest.py new file mode 100644 index 0000000..602df2a --- /dev/null +++ b/Xlib/ext/xtest.py @@ -0,0 +1,122 @@ +# Xlib.ext.xtest -- XTEST extension module +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +from Xlib import X +from Xlib.protocol import rq + +extname = 'XTEST' + +CurrentCursor = 1 + +class GetVersion(rq.ReplyRequest): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card8('major_version'), + rq.Pad(1), + rq.Card16('minor_version') + ) + + _reply = rq.Struct(rq.Pad(1), + rq.Card8('major_version'), + rq.Card16('sequence_number'), + rq.Pad(4), + rq.Card16('minor_version'), + rq.Pad(22) + ) + +def get_version(self, major, minor): + return GetVersion(display = self.display, + opcode = self.display.get_extension_major(extname), + major_version = major, + minor_version = minor) + + +class CompareCursor(rq.ReplyRequest): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + rq.Window('window'), + rq.Cursor('cursor', (X.NONE, CurrentCursor)), + ) + + _reply = rq.Struct(rq.Pad(1), + rq.Card8('same'), + rq.Card16('sequence_number'), + rq.Pad(28), + ) + +def compare_cursor(self, cursor): + r = CompareCursor(display = self.display, + opcode = self.display.get_extension_major(extname), + window = self.id, + cursor = cursor) + return r.same + +class FakeInput(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + rq.Set('event_type', 1, (X.KeyPress, + X.KeyRelease, + X.ButtonPress, + X.ButtonRelease, + X.MotionNotify)), + rq.Card8('detail'), + rq.Pad(2), + rq.Card32('time'), + rq.Window('root', (X.NONE, )), + rq.Pad(8), + rq.Int16('x'), + rq.Int16('y'), + rq.Pad(8) + ) + +def fake_input(self, event_type, detail = 0, time = X.CurrentTime, + root = X.NONE, x = 0, y = 0): + + FakeInput(display = self.display, + opcode = self.display.get_extension_major(extname), + event_type = event_type, + detail = detail, + time = time, + root = root, + x = x, + y = y) + +class GrabControl(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + rq.Bool('impervious'), + rq.Pad(3) + ) + +def grab_control(self, impervious): + GrabControl(display = self.display, + opcode = self.display.get_extension_major(extname), + impervious = impervious) + +def init(disp, info): + disp.extension_add_method('display', 'xtest_get_version', get_version) + disp.extension_add_method('window', 'xtest_compare_cursor', compare_cursor) + disp.extension_add_method('display', 'xtest_fake_input', fake_input) + disp.extension_add_method('display', 'xtest_grab_control', grab_control) diff --git a/Xlib/keysymdef/__init__.py b/Xlib/keysymdef/__init__.py new file mode 100644 index 0000000..4ff1441 --- /dev/null +++ b/Xlib/keysymdef/__init__.py @@ -0,0 +1,42 @@ +# Xlib.keysymdef -- X keysym defs +# +# Copyright (C) 2001 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +__all__ = [ + 'apl', + 'arabic', + 'cyrillic', + 'greek', + 'hebrew', + 'katakana', + 'korean', + 'latin1', + 'latin2', + 'latin3', + 'latin4', + 'miscellany', + 'publishing', + 'special', + 'technical', + 'thai', + 'xf86', + 'xk3270', + 'xkb', + ] diff --git a/Xlib/keysymdef/__pycache__/__init__.cpython-38.pyc b/Xlib/keysymdef/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b57c94f646b0739d0a66f8faf3a0e8e006d57d71 GIT binary patch literal 395 zcma)&y-veG5QLq?aU4U!Pmt&+k>)p=(a-+f97F)j~))t#~}u| zgS$AwG4A0$9^m16!W^7DI@2T8gIrk;3`9~`Uo=K4)n5IzAxf+lQ)K9s`3aPefClR& znIX{DQ&38MzU6$&#g9OxI?b?BbbGvc+i7dv&Ohes2VOmI761SM literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/apl.cpython-38.pyc b/Xlib/keysymdef/__pycache__/apl.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71c2753ab277121e398bbdc4093ba6232c8922f6 GIT binary patch literal 587 zcmaixKTE?v7{;&mf70}?vy)2~vpR@4xCtVNP~N4ad9OBYl4~!C+Rr119sLHP;vk|! zL8Q3(EyTfluhr`0h5V8y_dL(tkt548m>O5pHFuO^?488#p}w&lbw>vZ$bexAQn&&u zn1(cFAcI-RVh(bchddUbfJG>x0S202VhKuUfrVu#V+AT$g(}vdhIOc8pW> z?}Bhim=LZBH-uZl9pRqvKzJlP5uOQC!VBS*FeAJXJ{9(x4Jftt+CJ~bt|NI2mQvC? z?kyxuqBsrblZH~saQQAyxa>F*%!Mm<+yNA|De4KXt0^7Dp3k)_32`3DDOX>_kXO%9 z;@~3o)8Km2RhjvU%e-#;uGah13(*GX41a{~q626jsR@(xzcfBJ?Vbp@?fCr62~l!; ySGY;Q!#J}0JZVeebGzkx9ecpX(KtZfwVk2AF&xhdTYWmzZvhzOr~Cw*M;q&!e(02c1fsLN{Yks_O~QZ z0}WG^1 z%!`Cegv*30gsX&WgiXSA!VSVr!Y#sW!X3gE;V$7G;XdI3;UVD>VVm%n@PzP`@Qm=B z@Pe>Icu9Cgcum+Pydk_Lyd%6Pd?0)zd?I`%d?9=#d?S1({2=@!{34wFqiM&pE6}`p zEO*3Xd9#o!xI!>!j}4y-v*6mU1KqK?9&$f0T_-1)XQ{&tCN>y!#!0~tnd>l{7gamT z?b#i1de1Jg67=kILkGDRmH9Vc<_0vy`6!i%bzVMJ1_j)Z%g%=Z10^I~wMJkh^&00w zjT1TF+FW0?&Ae{$vR@X+i#m%w Z(8bf`o!W!K#n9%5sGgsfNT>ey?=M8X&IkYi literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/cyrillic.cpython-38.pyc b/Xlib/keysymdef/__pycache__/cyrillic.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8276af3a226b3c04b3ba2a8f69ac016313ed5afa GIT binary patch literal 3045 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 literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/katakana.cpython-38.pyc b/Xlib/keysymdef/__pycache__/katakana.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10826669b03b04070d2dd3e4fbdd16cf0d92840f GIT binary patch literal 1798 zcmd7SXKxf&6b9fi*xySojcr)OMaVFN zcD7?N7o#H?o$NptJJHQ9EMYg6atV646wBCyB*_uwG+;t==YF!$pK58x;dVvvV0#KRco5gg-DjBpTH4&gY5ae~Ki zk|P*pwrKSqcds;Bmix5ujPR`RobbHxg7Bj7l5kacS$IWwRd`K!U3f#dCR`WZ6y6fv z7TytV2sedyh4+N_g%5-eg^z@fg-?V}h0lar!foMm;S1qQ;Va>5;Tz#w;XC1ua98+V z_(Awl_(}L#xF`G~{3`q=+!r1QzY7nAKZHMpzl6Vqe}pTKEGyrC3T@|3PX)84>bbr_ zM>3gqd}r!H?0I1n%%G>f63iIi^)H^UI^}5-rR!xc2-SL~zP}t)Dh|5Ssed{4%aJ>4 z&{)e6O!{-Dvnr3PJfZTa%Br@QO^;d}Q^iG9TvWwHRa{cVCvI^wmn^Uzp zRXeF_C)NH*wWpx=6jc9$+Fwxp3##_4x+ZzFCwF)@aLe_})RoLbQ*rg0?5SPG@Ci36 zU%F~l<^R9Ne?#`Apki#tGfz4`tHwSVl;eu=qtL!=;;H1O8vC5*p0}sXTsT*uxnNH_ ckt6K}X67C?4^{%ko*9;o;Zq5X4gZ(VV@~ literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/korean.cpython-38.pyc b/Xlib/keysymdef/__pycache__/korean.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..020cd16971a8bbd984a40d04ea314062915d447f GIT binary patch literal 3417 zcmds)=UW>`5XObOjlsS5ZrtSFz}Pa#24RYkvC!)xAH0V+kaR>P319NRq{pdtdhfj_ zcAC@U^xliVI5&4nZa1?3fxyG>Vc(tInVp?kK}SW!n1J(j^3>e!Rpo)eSsAEbWzJxi z^OHN_{KfzREJGQGUI>gE^dwxtxc2oR9fj zfCXHLgQ=c4TA`BE!J`!)^R=7 za|1STBQ|mqHgPjHa|^a`E4FeQwsAYQvl`W`K@E3c2WwHwo!H4;*u@Y++>PC=Lmlf; z&oIK=gFW1fy=*`O8_~!JB5Xnv_hBEK(aaXKuobOrLmT&FKikpHD55-o1MENt58@zW zh_Mr$j3Z6}JcL6$jKe&FBRqEJ5C)q-mT*9xu^ zTrapma9nVs;3mP%f?EW)3T_je5S$d;F1SN*r{FHZ-GX}r_X_S4+%I@Q@Sxx!!NY<_ z1dj?H6Fe?>Lhz*EDZ$f%X9Uj*o)bJTctP-@;3dJ!f>#8u3SJYuE;uDPEqFulrr<5X z+k$rlX9VvG-V?kp_(1TX;3L7uf=>iz1)mB&6MQcCLhz;FE5X--Zv@{8z7u>e_(AZa z;3vV)f?ou`3Vsv(F8D+6r{FKaIl*}cEmi#(-Px3AFfz$P3giC$H4)7l**q$IA)KK) z@2u!EQ0dP{jI@pM{!U#YWcTA6`DUNwuDwrr$$-tF!@(O=h=H9c?2UTtE->rp!7cLyJb<#4LZRxsb|R0~eWe99(MIg`B@~g1<64V0K$ZN{u;pnU(ME zRh1@d(6p17qAbffDJ{yAec4l@mYq|N++vx93=}h0h&XCriQ3?(n(un6-z>1icTrXn z+wQDPsdlbV8lx*WJH~0%xvRE0YL|MauIe5~iQW|tIHvg$Z*9Gf+*7izcPJ%R&%Gje zo)sriNpML#uHeR_l%_jYipO30-#ch@t{|yiN=f?NWuB@8R_7e3TdM^*;w9=%UMiH{ zC}u&~#Y;fBUnn7rRR&>o6c4F=84qcb;Z`WSgf&ZNPT4dbRxP!unC2m-duY}C#WjB& zs=t`-FQ%=FHEOYRsH3npiZ`lpHbQwBB8g)4PV;jWNx1h^oS3RkD9xudcqHMqpJu8w zV5#n1mKH~`+q7-nHh>nID_tlwcfu=r&7Z4#RjxcPk%X(eC0uImm0PLbV%3!{@Xn*f z?uDXB7l^{|Hl?_o3y|kc3)k;BZA-7`G|B5PP4YTSOLvpkW15+FY|X5AV$IAu>`cEd z+*HO*J8NaUIpt#`TFCS}FMB5GJceFME;8vQM{(3Ni15usQ%<{fF?U@8kt1Q&KwV9 z+SAB+RWs0!9KH)$l570*qO4rg8k-TZZJ)=C)n@q bv}R!N%&3~Q%|gnomHl$39Ske`|9$@rTF)wE literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/latin1.cpython-38.pyc b/Xlib/keysymdef/__pycache__/latin1.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b39dcbc0427536e13635975a7159f145a69bd367 GIT binary patch literal 4331 zcmeI!_nQ<|76#x!Q4s|(=Kw}f7;+Fq1{h!nGlUt!hlqa)Oo7|j(8{GSyLTaA<2lkhpdHQ{-y6!zUoO5fYtxuocb?#^LyC>vx zed_Ao(@poE9`0t6`y~DLuB&4m>evH4xEJ=~-q@Qx(UbdNAMT5NxgYj(pI+>R{kcDS zvo{Xl0XUEcq7VC^FZ<#k9)y1Ehl6=A4&fm3xf_m0tD2HMghhaE}V+2QFBu8QtM`1KaV+_Y&EXQIT$KhhAVhEuH+TCidW)l zUWIFTHLm3~xQ^H2dR~Vccs*|94Y-Ln;%452TX-{WA<_u)a_kB9gG9_E91gb(3SK8(lQ=W#xQwR{xo_!yqx<9L#5 z@f6qLX+D8x_$1czDLl)k@f@GQ2Cm0OK8sC!4$pG~Uf@P-<|e$z=kXF>z!q-CR=$Xr z`4YBq3$}ABcJO7q!fn{e?Rb?t@ETvi>)eSq_$uDyYj}&V<88jNpl;xs@1Nw(#{2!n z0CBQ7MVu;56Q_$a#6U4foGH!{gT)Y0FNTU?Vz?L~Mv75lv=}4CigDs>Fniu1(z z;sUWuTqrz|6L~@5i+0f=3L+58MW+ZwQItd^%A!leA`ul)6)VI_u}aj$YH^WRBQ6$~ zh{wd^Vy##wo)Axpr^M6Z8L?hGE1nY@#7416JTG1lo5hRbC9y?p6)%fzV!PNOUJ*OR ztKv2Bx_CppDc%xqi+9Aw#K*-a#3#k4#HYn)#An6l#OK8q#23Yv#Fxca#Jl3F;%nmT z;v3?d;#*>u__p|t_^$Y#_`djo_@Vfbcu)LT{6zdz{7n2@yf1zsekpz>el30@ek*<_ zelPwY{wV$={w)3?{wn?^{w_Wc{}BHa{}TTeA3B-`zJp$iXDv(0Uf#$4?#f@454|D| zaF<=xsNyphqK~^NRf{=4PJ)h-HDeTpUX0%EN!gF{eyM^3-L+SAcZpY`_3_G4Qi-E- z!M9tB7Y7}Mv~Ago{Zi<+SL~tPr4Kc>msI>XNIJ2fMhL5kO*0=Ai=JH-gOdAB6Ltdz z5z|$KUQ$3$_vKJ?m^s`WVU9FMnWN1y=2&yw?m@z+WUER1Vvvt?r&)x4lFq{Ks(PU< zy(9KLH_zR>idH5<)o=5%w0*<{W%XPL9jIcBrjV$L<^ zne)vBW~6`6lhnXfWuvt&kQ+3Yf7 zGchY>)m&k&G*_9mUD|=wHdF0@Z|mRJ)*su#?AvtRs(V{^B?#TW7TT9mlyf=nOwWs+ zr{{&y%Ct!AB&dK@zhO}7u-eF1V^{0>8Z=PNPpc~zN1c8txBC<&e%`7_sbJvwv7ZEq zO*0>rYgrzmnC-4COF_v;Ki9US8ib)=jM7BvJeH!$o?}|5xx-hob;W*1ph@|(V^j3< zu`5=cAo{M3)8y%-rAJFSudIxcs-3Uc#;O!tyFK6Nz2W7n72hhpShdNzW9+$FC>LvQAA|r>6hv)Rc8n0X4SHutI9A6l})L>5LVxC5zXR#cSyn z&st?mY{^2aYKzsd5|qQ5Ew3fYg@M~e8>B6Z+?GXd>lRtdYsk)mif*0 zs%R}laawpuQt;xkg;&wI7l=A}S$MBoczcvh+{;F?Lfn%Z&rgqB_uRMw|0uejMfbZ! zx1*ee)L=o@DabnQeg|5)s8f)2Qf~a+IVoj1iLy9R7ANX9xJHYzrA1jt<%GeCAkiNd zYnHyEby>~gR9^O1UL`0E8&aNTMlu^Rdld$@SX PRnDG0{{Q>G-huxB0P11wj*a-r2kDtffYqX zMfV_zh@cxm7l=R#iJ%JvU7$M8j&=_H0d4HV_q==ep6A^*Sx^x2*^jeN>mq#tpKp;D z-iP15*kQjCOE&O{4}S3@Lo$#lnFvS#S(1fp$wrRkASglPN-jbYLZ0LyU-D5P1t^q4 z6iE?^r5LMZ6-uN8rBaI3vKnPlhBdMVZA_!QjZ2{ zK%+FGNt)0s%~&gI(IPEql~%M#8`jA>v`ahI%X(~(4d{>#bV?_>qzjs8=$3BuNDq3Y z7k$!)e(A@63}B;d#3tE<&9WJTVPE@}=wW;D=n-<193#ic0y#lWl2ha~IYZ8pbL2d^ zKrWI?K?{qQQ{NH3uGw${bIbTfr z%W$hZJx6B3Hg!B|L%Vwq@pMhCXeyml78!9?1cvILq21h@)8FUWWdk^ z9`u42ec(es_%Q$hTz~}(LJ&g`!Z3s}0uhWt6k`xW1r&@!921bhBqT8fDNI8eGmyb7 zWHARh%$HeV(Kz-pW}FZs;*>Ze#>6>sL0l47#5Hk4+!GJP6Y)&E5^uyi@j-kM-;(`8 z9wODf+O}|PtHU8q_qt*0{cgkLM-Y?a9j$H+I9d*b<)~D*`QUfru4^Kdv|J@ER~D6- zA=jH+h)G9t+R=8+qs@wco6)Nj=hD(lT5_fIYL#hi*%eW(KC@7tOPe!k)0I+fe_AW1 z(TLgfS4{X^Q(IP-tD5{|S`TfmZdvjR@}48qHt*ML%j9a+H0o-H55=&HyrG(!WAs*6 X4u_AS^{$0|lW$O;SeE!b|2=;IKgGj` literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/latin4.cpython-38.pyc b/Xlib/keysymdef/__pycache__/latin4.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0df510cd0ac2b912a479cebcdd0cf36083235498 GIT binary patch literal 962 zcmb``Noy2A6bJC0C6igR@5^Mp0L}e~hg)20`12o8k zG{i$R%)>OoBQ(mRG{$2z&f_$}6Ew+_G{sXi&C@i)Gc?PyG{jt<9_P}j$2iyhs!2|FRJOYow6Yvx~1JA(=@DjWNufZFz z58i_J-~;#wK7%jdEBFS!gCF20_znI<)Yn!ix3#(5_5|he6`hWtQZzf?lx!&_s1Pk` zfu&f+@v}+17{%9Z(ezv=PHZFz9JuH!;qS(APmf!Ng;WwpA*mH=B?%M?dWCSu_4-D9 zSLw6}XHFfQ!Mx_STt~L4jD2Fqy&~BS;$1bZay_YxBj>clrk-dV)I$44@4ndI(Ao)Y zZfULMKdmjTgQItFwM1J_gj(zHuyxqmf6MUj2gLk3X70MSG=-8^g~OgSPrJ>~mQLWC wmu0x^xk{Q_%Gxp8vg>zkmX}N=0?Rpiq|<$uKW@7mDtQ9+{4){R)c<(@0%92jRsaA1 literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/miscellany.cpython-38.pyc b/Xlib/keysymdef/__pycache__/miscellany.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95cd3ad9066c003e507c04815087cffd5a4e02f5 GIT binary patch literal 3624 zcmeH~=X)E+5yl}|g``ADR&kRpt4mY^0kFwdWiw@hG;|;(dR(9*W=S3i93bFMB3vZ7 zTTYzBz1!)oDNfIa^xlh8nI5ONKOkS6d3X0jKlz-GA)oo}%--DYyR&T7HAdl$!IuOoQ#-y8`Jh*e*h8;fy_VmycD@LZb9=h1mQkLK}wn$HVp0WYM5Tt{_W zPxahD4ZMgJ@nTxcjnv3XXbCT+rM!%miJi~q({f%;D|iL1 zn`j$vrtQ3icJNl($xXD2w^5w8Q#0?N1n;CI@1hjPsfC-Vl@rv)Nop7C;1uoV7P^94 z=}K;+J={)vxr6rcZraaR&;h=Z4)PxA31uQx6}YUa_nAAoYnI;!e7nyQrVL zDa}1Jz`ZocSJ5@xM;Sguhxux9xSz6|rXe1nVIHI-d<`At42|$%x|SUx&eC-}MA!2$ z-M~laMm|b89-(7=ExAlI%GXhzuP5dkNb!v{#yJ}2V>H1od18}1N>gG5&Qnp$XQq-^ znH3!utMC|A#R49uQ0xRx&`Ggr_NXRy6Hn63Vz=-V-70p93v^oS3>WD(vD?|FJH+nf z65Sg%+Z>RV09rRwllitU7(fj#s zdY<6tm&**_eLz{)dLN{BA^ji0jva80jq&GfVIFLU@x!_SO=^JHUJxeO~7Vg z3$PVv0=5C$fgQk3U>6Vvnt=>(7;u0rFa!((M}VWi2yhH=fl(k2C}0d22POaym;|PP zQ^0B93~&~>A9w(G5O@f97vc_&D$h;FG|ofKLOT1wIFS9{2+AMc_-omw~SUUj@Dfd>!}(@J--b zz_)?#0N(|^2Yes+0q{fMHQ-0UkAa^6KLvgU{2Z78egXUv_!aPL;5Wc;f!_hY2VMvM z0Q?d76Yyu?FTh`czX5*-{sH_G_?KX?>BX7FBmKEfH$RmvyLmOUK%yaclom;w_Ix#_ z6wlO2&{a@wWu{&{M+ITU*PR(Rtg4wM(wWUyN`*o$UCK{U1L9g$5|8T}da8N1thC#$ z3Mx>9IS0Z*;N_-Njg}+d^T!LS%k^1aACj>$$|(PLZ>FM7c%`s9n+tjUu0QE%)dS&# z@~2#%AgfQixq*_rbD-VC+I31r*WS|SRorpcclG`KZs5x8u9gs8k@0OW7DpI$D%1<@ zbB&Kh*#2D9huoo3SSd*^W*=~?)mllfUi7-AT8(l+N7)H)EFgqEUNy{_5R1@ZP#Tv4 zX@xRPuD>)#j`2D^>IoGVZuS zB5oX1(}5PKJ9RRSM32w|x_<^cu*%I_7&UwjmUh16Jq8BB3x~|ww@pWnSk;|ZHb~;mF zo1)8RFPJjxP0r+^O4~eL?4@Sd*?OZIS|~?@o2uiXk$ziO8`%fSPS$fgbPKv*lBgfB za-kL#;h=nf?POV1b}Mcm`;YS_x!G_usJOD9R%o}%dqo*4n#DWe$rFSvl;q~fNkmRE za#E4g5;?7r(-t}Hk<$@5yY*3(&A^H6O`0ZUnikWvnx@S(?WXB4&2GXG?M*b>){K)* z*wBoZPTJ6nn@-u#jGu0?p_wP$YC|($y3K}W-gLVS&HU*O8=7ULt(>Hhla6F1jV#B? zN*Y;?m6bHI94jknWI0w=(#UeGtfZ0USXoIU%dxVOMwVk`C5n+4~c$fsz6?pv#z6`&Vz+OI`I_3bIf#MSyiH`wQu_SYP0>ucV3KD>g893#=rN+ zC*-vmcMED-K0g%|?=I!TqVj`kd{TuuQLW-51#dJi->Pb@$Z9NJ^s4f*D!6`aSGhJ* Zx2IU*u%Pzg*Ge@boHytH|NqMh{0BqmSF8X4 literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/publishing.cpython-38.pyc b/Xlib/keysymdef/__pycache__/publishing.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42edb1112403d1a05e3054033580f19afe271249 GIT binary patch literal 2458 zcmd6p_iq$O6vxjr7ck8R(|a@R=)D*)y+b0Q_Y2zFH+MVnHfDAK|2auWA&L@6kJ5ff zkp#Di%t51}F4NR8Y?P25b)+(Iopl!o#!8pf^E%EM_m zkDw7el1B0<8pWe&G`CS3w^KWhp)ov`#&QRB@HiUB<7qrkpb0#YCh{bj#FJ?6ogjka+&b@O)G&O2xa@1&i)i+1sD+Rb}t5AUVDypQ(re%j9m=l~z2 zgM5e%@nJg5N9YJ2JzDC#PTpwBFK+^F2F?Jt0Jj3S0k;EZfjfXZfxCdafpfrl;2z*! z;6C7f-~r%4;341<;8EZ);Bnvy;7Q;q;A!9);91}~;CbK$;6>mi;AP+y;8ox?-~#YE z@CNWE@D^|pcpG>Jco%pNcpvxx_z?IAxCDF*d;)w5d_A`~v(6{096ETn7FC{sjI4{s#UrIM8{NT8`~KE+U|yNvB}xY24^i)FPEdT|d~&tWK2 zEj5icMo#id#nU2`vEU@L8mzyHH@J{XqCVcBbLF;)P+7xsz}m_H7r6s_DF)0OL#O-l&p79@&IOsE6wiJ6-;@RPLHW@|ITmqBK0?WQ_RPW4SZTHuoD zr$)Mdu5_Z#mK1(>SX|y*4w6WeO>C#V*bHU4JMr_#yh&ZYLFC6(5{mM%P*%!4qSvIu kqFPSPgOS>N$JQ37y%(D{L<#4i*n~Uv8G}I$|Np;#0mB&jumAu6 literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/special.cpython-38.pyc b/Xlib/keysymdef/__pycache__/special.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e90278e449a2ddd847637ae5dc9684919ec03a1 GIT binary patch literal 732 zcmb8t&rcLF6bJAw?6N;#*;bA;gdnd(gu^^l<374e#QL!1+DiFd?%;)1v&J`f*?E8-LJnfO9{ zCB6~giEHA9_(A+6egz(FZb0ks;ZdeiJ%V1SoKX@{riIZM1D*>$5;ilbMQDT>xsSte z7zb|Sp#FAR#qPwzAk0+e)XL-Bb7Qrz&<{~nPA0TrRVp!@oD$ypC*jsOC-t$CTDZ7! zZmc|uEl>5G>1ca8+L?~-PDi`(bI|@C+TnEZv>uwF%uL9r@TT<83B`%9K1=OWX6cuS z{N77)Q-~y0;z_E}isXUeRUx!@$zxF+S))X9sAQIm#Mq4s6vM=og3DBGmE#w!y~1Fn P#64QjZ3XJJ|GEDFLG93C literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/technical.cpython-38.pyc b/Xlib/keysymdef/__pycache__/technical.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d0837f8489a5c68604f84f528a969fe1a8657a3 GIT binary patch literal 1547 zcmcJPNlz3(6vrDu7=d9|P!W|)aiP_-F&?~`crhmAU9jm_!<2Ma4b|P~_-)+xeY?0P zCU{T}CLVA*_yN3maEtZt*#P6wnanT!wtv4@^)g4Y*=EOnZoM7w?=(2h7Z{5_b=KH# zf6=@JJLGU3)$s~i!Sz(n4b;FZX(cyOBR5eKXDGv~Xcae8Gq+F+w^A!-Da&ot#_iP3 z9n`^{)X81c#og4+J=DXiX*I8*HQYTZ=el4Km)vyHu5Ig z#G7d|57HnH(GU;QFptm(kJ2b_p)I_Xw(=N_@iyAV+i5%RpdGxEcJerlvr8`TqFucE zursztp2|3mJPn)y&I0Fv^S}k*B5(<~3|s-O0@r}+zzyIga0|E%+yU+a_kjDr1K=U> z2zU%U0iFWSfakyqU>6itd_j zG&^NHe@4Uvr!B+FYR;ZgrjgV6WnP=Q%!~JwyDfM|sEFVxOO`$5iPFkNu8LUM)obg} zM<|&C;U^|2qDw)2Hjvi-6-;dyjTv;7=1i#HL(?VhH`r1B@>CPkE_(W+6?z^o6c;jkL(NGb_CYc_ LYSB>lf4_eKC3x8o literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/thai.cpython-38.pyc b/Xlib/keysymdef/__pycache__/thai.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d7bfd40aa7ea55efe592aec31adb40a8937dd115 GIT binary patch literal 2483 zcmdUx_j40R6vt&S*ci(d8%#01m}1evV2lA%4W`)4Fz*-7TJP+;NO!A|?#9udlSV>1 z>Am-!KpH=!kzQuf+fR94O?10*{(?xO&)j?a_SM}RolPd|67sotsWW}KCXu)b!TwZ< z&@aEjh&*wE5?n=9JdLLDbehi9RLwJJ2G69KTthWnOSL?UW^oFQa9=oR;$nTEQ!6C9k4YyqZ??8d}3$)WvIQEw7_>yq?zc2HL>g)XgbM z@kZLnn`jenrp>&Cw(wTk%G+ogZ>R0NgLd#v+Q~iC!@Fn~@21_{OTD~@_HZBd@m|`? z{nXFTZlfYBJ)4(&pv%qt}^S}$hi@;03%fKtZ ztH5i(>%bepo4{MZ+rT@(CEzmfF7O`kKJWqXA#erw2>2NI1o#yA47dt>4txQ8348^7 z4SWN93w#HB5Bvc92>b;64EzH83j7BA4*UW93H&7(>KdWsxxvv9S9zluolzb&#-gh; zqNb(_MWn(hn#uoc(gC$toAOHh1QXOy)&&zfn21cIw5!u%Vf>a9o@|L@|7$Wq^<^go zUC>ipXvf4T>LMJ()i6S^GKx^=RH!nR!qp+NXe&nuT^&f?z7FKcR3t*qjl@)&)&)9n z0wc0!Cu77=hhiMpA`U_~lH{7LNaC-e1tlYClPOqYM8?UO9#ft#t&hi-)48lN2Dwt2 zwu5m!Zq;8}mvZC%ShfZy>jmZKP-fh7U;D~zkQPh-;{AD@j}+CFYsyDv7+IagFyghW zwKl0ImGmw?95PoWby#vfDp+O8x`c_Ny4v+}W{6oCb0pj2lr>IX+{Xh3Q{>1oS;IVXoXY)Hk4o#-g~_>| zoXLnf%FQ-8j>;pBYutb_<%X4xty==eYD!#fZa`@nr_7J!XtbB38MRExqt>!Mt1_jV zUbIiU>@F%_TP;V1%9Amev_vHrCT=e*q=pJPZv~Qdxy@myCY6bf%PT+0zmDMW$ky~qJBWBjIo0WMw~9SVE6kgek)qH7%mh*GWXF6}yDg(9a!Vf)W0eIb#@ zpOO;(?n$}YcTy_rT&n`kJE;Mkj(jHw3#n_IXf&_0PU>9N8%t%JVxj1>GoF&tz3yD` WN^P&NIm$YH@K!i4sIK}yeE$K>FYq=1 literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/xf86.cpython-38.pyc b/Xlib/keysymdef/__pycache__/xf86.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2aede3056ebb5e757b02c60f61869e5cdcc66cb2 GIT binary patch literal 5518 zcmcJTS9cr78HTCeZS`hbR<$h`QKTe_vSnEWt4x5T0Fal}+a7!e4jf|FXTQ z>F*rGe-@a*N%OD#kA+Q5(u5{ifCaJ;3uO@&$zm*)C0HU$u~e2}nJmY0S%DR@5-VjD zR>^9tmNi%-m*5h)6qm|ctd(_GC+o3ZHeiEn#75bKO|luAWec{*R&14R*e2VtT`t3A zayc%SD{zJEzz(?*SISQ8lwH^*yRlpLV2|v@UfG9zauu$Ut8uklgKK0z_RF=nR<6T! zay_n>8*qahzyWDSvmC@hX+ev$qE!yzkQ~NgIf5h7hBi5hqtcFcIfi3$BW{$NaFZOz zak&{c%L$y2TX2h<#7XHuha`}YPIO8ax}+Q3(t{r9MX&UsPfp>K+=^SJAN`Uiy|`EI!+ml;?w1GffINr?B z1fR&K_*6c_XYx5dmoM;ze2FjRJkHBk_)5OU*YXX%k#F&>e24GkdwefH;0L*Y3-Tj= zl%MdE{EVOF7yKf>;#c_%zsc{Jru~0*7yNtqT6ZC_h*(T4A(j%$h_%EzVm+~e*hp+5 zHWOQjt;9BBJ8>CtIdKKCgSe8|N$eta6MKlg#6IFG;%ed=Vn1;$aUF3zaRYIHXeJI4 zEkrAEh&W6fA=-$eL_2YexRJPtI8NM5oFHx?P7)nNg6Jf=h;E{X=q37yQ^c)AKanI- z!~ii!3=yY^G?5{)#4s^Jcz}42c!+qIc$9dIc$|2Gc#?REc$#>I zc$RpMc%FEHc#(LCc$s*Gc$IjKc%68Ic$0XGc$;{Kc$avOc%S%y_>lOB_?Y;F_>}mJ z_?-BH_>wqJ{6JhFek6V(ekOh)ekFb*ekT_E%U~g~h*(T4A(j%$h~>lzVkNPPSWWCC zb`iUYJ;YvOpTWTX-?4VIe{8g;y=^R|MJ0893R|q=Cjxh}5c;ZI9xh?qycfFkw2w{o zOa0@Lp-x+E_OOzzT!Ed4q~&aqPB0%>Mosh%P~95 zGs+hyu*MG2Pc>(6jwaTA7&zsyL(W#>52sB=sK5k2GGSJ!JssIPt9ARKirJNLOfp3g z>(RlqW0n#*qvq1+a#~FUs$8(^sM*LIiX4=4rj#)x@?ZB^l@G1IGWkF$KjWM;Hms^I zE0@dJ2Qr>gu_*R9=XBtPs%+O*^ZAfdDXRu8rNnitma2B@V#(xGV6_qBWF0W**3q?6 zNL0M>*s=>IlcHt!nMmoNYK6g)@`uw&yQ~h8x}nxNeL76K{#4nfw5_g-5sY5ZPf3<*B4u}>*Y7`DP3P&1+ zwnpJ-qtM( ztgL5gXO~T*dWD^Grb``)FwfrLh96|s?GS$N@z{(a?2$)v#1`r1~WE0yL8@k zNysk>+L%CmXaJyY^@5HIRp$K-cpc(1nQ>1K7s^slH~lX`Ma`KFv;O2uqTDHXc@ zqJEr5id&(Sbls_4tA*aSWouV~?yLPk3sC8JdrsR?x1GYAYa|vv$CtPhOs=qbb zXD^-!O3Ddi?Wfkf&3Q-{YbTe;#!O{AbVGA&icWO3=W9nP6DX4{Q7YE74u8_z937l` z0#oa?O+2!yCe^KqJeqY)Ply7LQ6Bf@=<0}T$~201R+m`kcGYPWOVeI%;dZJsCTVr7 z#_2*kRi;ul6)o1_K6hLNzNtP$$C_GCDmjwXlU!T0-nu^M+c-y>IdnuTu6Zow=4%JY z$g};bIfw62FV06B%w*(%#8|~|wMy&`NYHXiAUxW+vXFG&L_6dCmyqYw05=}oL6m`S8cUE9rh;!XWT}+ zwkh^filWN%_t*&kkg*z%=@+%=j})^t%{J()$v11bkvZQfe?p&c3i$tYHT?5fbHSYI zn$0nK##C9Lnxl=g`K+ppnHH^@M?H7Ec}i8w)uO10=9!81wgaW=-eY1ZOMz zhKm<2UJ(7pzgv~x{&?k&%G@XZm(l-Y&i`&l{EhYR_0Ps9>w5i@Ug2;3)9Q1djOqUY DPvYI) literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/xk3270.cpython-38.pyc b/Xlib/keysymdef/__pycache__/xk3270.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..25f3f016ef08b44b3b24bdd429d2c0e8f71a2aa5 GIT binary patch literal 973 zcmbW$NpI6o5C?E)YgT99H_p9aNDoMW5NVQ1r7R&SqMA$OdZvxBV@LM0lt_Fbj(iqw z9N}9aA>k=?Y_2%UPx_DEo4hwkKFD%T@}IBYhT4y~B>m(NePVui>A&62J}7~NF^FLt z;+TL0CLxI_NZ}D2VH(nyfedCLi#f<)9`aa#0?Hs`5sFxX5|*Ki6{uhps#t>>)}f9K zXkZhX*n$?ep^Y8rU>CaBgC6#wj{_Lsa48)>&||+PNsluLCds6jBPPvcm@JcH@=Sq| znIcnS%1ni+GBu{oG?*sSV%kiH=`ua0&kUHM&)ef6RIV;oC!;5iR#R_l>Y6GjES>3O z;v3a5pct%L=(lSz0y)U9>Bf(xAfBq4aj9NIIVj8=)uq|HGpPPkIk&c+2)iuEB>{zC z$$L*1kPp&){5Tp$(Qp!Ox+FK!Vl0FOwV=hsb6ndwHFe7fZ#Oo@-+Fl33))ZoAsAuT zSKekDp7R{ZihRhzGbvm;vA6p$%}sS5Zd{V^wvj24W;+P=!}->#bxTXlA+o|rP01ud zA9-as&)w3mY9g}t?r!(h!;R;TZi)Mbhc9bp339$j4*xOk`1MR#+Z$3;lXj|w4k=Sx z^EUn^UF8#bE5|lTxia-@#h|^r-yq#6JA;2N54Zd8ndciD{Smz2e(qDBWbA)me*g!U B1T_Ev literal 0 HcmV?d00001 diff --git a/Xlib/keysymdef/__pycache__/xkb.cpython-38.pyc b/Xlib/keysymdef/__pycache__/xkb.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae314f31ba871d23c83530c896ae0069c35e8464 GIT binary patch literal 3364 zcmds(*IFA_5XXg$ZA^9V#=YQzED79W8zFF^8Ue=RvR>^RV9jb**&>54@)UWAT;?Tm zoqCh2Bu?+WIQ9I`YSpYK2z>lL?SE#@nR8}l#Y{&>YfS#U{!X{?xGffYhKK5(Ci!qp z{&}y;PaLBdH&GL>qE)<_R&z5o^BP*iYiTX7qjlUuExexAb1SuS8?|vewQ~n`a3^(g z7j^Ll+Q1uWBX6QjyqPxh7TUsFX)AA|ZM>bf^A6gJZkC?BI^e4LJR5B2Z~I>9IDB%h*Fe40*kFZFVq z;(UhAaDoz?q$H;(#eLMr{nXEA=`5e4b9|o8^98!V7wIBjqDy?4F7r$D5?`S!e3h>9 zHM+(FG{9*}^B@g!hB7=vLp)5we4VcI4Z6WM=_cQzTRcJ|JW8WHMq@lq<2*qVJV}#$ zn{M+IO>vg8JWbO)Lo+-}vwVl{@EpzYUAoIe#P{eP->3WhfFAHeddM1S{D>a0PCDl( z#|9b9*;tP-oBoN#%vHc@pcz;Lyz;NaT6|guv;gaYR-g@N2ReXGpbOX!8~_dihk(Pt z5#T6r3^)$-04IQxz$xG~&bDAy1QW7VN7%bReBf@EjA@Ap?jhxXm@F&I$Ic6Z%jM>Vkz3AXuDzD z>tNt|jvM9?9k|*Mt{2^0wMAZNqm|SJMf(prwr|>j6L{K;&BC>0C8)cUdB*gNM)nOQ z`%u2HsC(v9L0d}7ps;=6Y7@D~!tkj*@`Fen@`;7c+JdVWMdeX90$&iiURe#(HG=%S zC6+5LzG<M3LC?vaV#g=+y;9iJg)2PMt4PYpHSGn}iJ_Mh zy9b2J&!|Vi`mZev6aTXj~EUBX^2h;I&bsg%d=$M9J&2s{q3Dr}oC-P>E zrJQ3iWHfy?mAe%mGxI%=ppoX@AuI9dhu7n@Vq%~$kyd;sBwv;VIerD8%vQ5Ri6?Cq5C6Z#fwx_GAoZ!t-&)bFToJ?YSuT6 zrIl(GnT^SYtZ~IaS41RaRqr<;v$yn>L<21uX&8}#fK7R-w6MzIDD9+Us=ie(2oxg{ z!r?+QM2Z}$>IgMVxVDO2ErLs^NZ~R#Sfn9NIpSuj#n2V6(MU}PzVFytX5RAEl+@~p z23oR#mTI8&HPHH1n?`$Na@L?F3l#2NlWWP6P|aTNpKPe68mfH_)&5%5)fd7Xs;iTc zYC_F@d4H)SUr + * Xlib/protocol/display.py: (mggrant) Fix for 1219457 - flushing + was blocking and waiting for a read operation. Added missing + "import socket" per bug report #681511. Fix for bug:1098695 & + 1098738. The "recv" variable was being used for more than one + thing - renamed one. + + Changelog hasn't been maintained since 2002, but some of the more + significant comments from cvs logs follow: + * Xlib/protocol/request.py: (petli) Fix bugs in definition and + method of GrabButton/Pointer + +2002-02-22 Peter Liljenberg + + * event.py(CirculateNotify, CirculateRequest): These are + identical, so subclass the common Circulate. + +2002-02-13 Peter Liljenberg + + * rq.py (ValueList.parse_binary_value): Use = both for calcsize + and unpacking. Caused problems on Alpha. + +2002-02-11 Peter Liljenberg + + * request.py (GetWindowAttributes): Rename class to win_class. + (AllocColorPlanes): Fix Pad(4) to Pad(8) in reply. + + * rq.py (ReplyLength): Add a reply length field, for completeness + and easier unit test generation. + +2002-02-10 Peter Liljenberg + + * rq.py (DictWrapper.__cmp__): Let DictWrapper compare with plain + dictionaries. + (Event.__init__): Set send_event to 0 when creating new events + objects, and allow events to be compared. + + (Struct.parse_binary): Allow LengthFields to have a parse_value method. + (OddLength.parse_value): Decode field. + (String16.parse_binary_value): Handle OddLength fields. + + (TextElements8.parse_binary_value): Bugfix: return values instead + of v. + (String8.parse_binary_value): Parse String8 with no LengthOf + field. + +2002-02-09 Peter Liljenberg + + * rq.py (TextElements16): Bugfix: inherit TextElements8 instead of + TextElements16. Found while preparing unit tests, whee. + +2002-01-14 Peter Liljenberg + + * display.py (Display.parse_event_response): Fix bug reported by + Ilpo Nyyssönen, whereby ReplyRequests which generates events + (e.g. get_property with delete = 1) will get dropped when the + event is received. + +2001-12-14 Peter Liljenberg + + * display.py (Display.parse_event_response): + * rq.py (Event.__init__): Fixed bug in event type decoding: bit + 0-6 is the event type, and bit 7 is set if the event was sent by + SendEvent. + + +2001-01-16 + + * event.py: Changed some class names so that they correspond + exactly to the event type constants. + +Tue Jan 9 10:03:25 2001 Peter Liljenberg + + * display.py (Display.send_request): Fixed a call to append() with + multiple arguments, something that modern Pythons don't allow. + + +2001-01-04 + + * rq.py: The fix for 64-bit platforms didn't work, and close + scrutiny of structmodule.c shows why: it turns out that '=' + translates into '<' or '>', the one the platform would use. This + means B is one byte, H is two and L is four, and no extra + alignment, always. '@', which is the default, selects native + number of bytes, which on Alpha means that 'L' is eight bytes. + + Now the code goes to pains to ensure that '=' encoding is always + used, so _now_ it should work on all platforms. Ahem. + + +2000-12-29 + + * rq.py: Optimizations: + + replace calls to Field.get_name() with access to attribute + name. + (Struct.build_from_args): + +Fri Dec 29 17:05:02 2000 Peter Liljenberg + + * rq.py: Alpha forces us to probe how many bytes each struct code + in 'bhil' represents, instead of being able to assume that b is 1, + h is 2 and l is 4. + +2000-12-21 + + * request.py (SetClipRectangles): Fixed typo (attribute was + "rectangels"). + +2000-12-20 + + * rq.py (DictWrapper.__setitem__), + (DictWrapper.__delitem__), + (DictWrapper.__setattr__), + (DictWrapper.__delattr__): + Add a few methods to the DictWrapper, to make sure that even if + attributes are changed, all attributes can be found in the _data + mapping. + + (ValueField.__init__): + (Object.__init__): + (ValueField.pack_value): + (Set.__init__): + Added a default parameter, so that structure elements with a + default value can be omitted when calling build_from_args. diff --git a/Xlib/protocol/__init__.py b/Xlib/protocol/__init__.py new file mode 100644 index 0000000..4e2840a --- /dev/null +++ b/Xlib/protocol/__init__.py @@ -0,0 +1,28 @@ +# Xlib.protocol.__init__ -- glue for Xlib.protocol package +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +__all__ = [ + 'display', + 'event', + 'request', + 'rq', + 'structs', + ] diff --git a/Xlib/protocol/__pycache__/__init__.cpython-38.pyc b/Xlib/protocol/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0fbef6974be8c7c00c56f45edd07c984596d35b GIT binary patch literal 219 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 AX8-^I literal 0 HcmV?d00001 diff --git a/Xlib/protocol/__pycache__/event.cpython-38.pyc b/Xlib/protocol/__pycache__/event.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a815b2677d9794776aaac78cf74514bfbf6f36f9 GIT binary patch literal 11730 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 literal 0 HcmV?d00001 diff --git a/Xlib/protocol/__pycache__/request.cpython-38.pyc b/Xlib/protocol/__pycache__/request.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d59fe5ebca8d1d3b983f27e272ed8305d49016c0 GIT binary patch literal 44815 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 K|6l#T@_zuYkoxWb literal 0 HcmV?d00001 diff --git a/Xlib/protocol/__pycache__/rq.cpython-38.pyc b/Xlib/protocol/__pycache__/rq.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e4dcf9764f2bf4d229389fcd5007d0619c1a441 GIT binary patch literal 39187 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 eJ^rUl1-?*4zqSNVwm literal 0 HcmV?d00001 diff --git a/Xlib/protocol/__pycache__/structs.cpython-38.pyc b/Xlib/protocol/__pycache__/structs.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ed7a70102a369da8c178b6ceef5018cfc980874 GIT binary patch literal 3151 zcma)7&2!tv6(>Lt1i=qWl;jWD@ki3yQIbZrJCo^5JDo;!%&K)Hjg$`!E}SKHK|%qs z@M0m0Gd=XszoF;wm}CEw&h)m^TTj0A>~ZV-b^+0{+(XED`#$!)xBKuu_Umrfui^Li zf8GxNv0khFTQe7bdiVE$Tv@dmtx+qf&8)z(YSgB6&~{*hc4*_TwV+O2+5~o({e1)A zQg2}aH)*TF{=!<+1Fza|(KfZ2b>aisfKF6j)Uns4w$bm=4y`S0NUqQhvrkqQ_QDBP zE9)lhR$hA*zEa_p3a=X6r+u9225Xg_OFKsH(lyBKV0|fHuk<%6e6_;Y4BnvE4K}xf zzpIs9pZ5}PcM{znC<&p~~>iWFixhzXa%5t^T!QpVW%1@aPF=Zpc zXe?MLm7kMSHp2awP(C|lsT|En{+-&Oe3WuI$^_5Igh*D3y^tqdAP(h*g~++s>IxmM zYJEeJf<29MsoVn-sd5KI&|f`L&0%%@`Vo^tCt9`kxcoY2QIRPB+bK)GBw5C&az9U= zlX@gDO%irSQYzT?V;&YWM38Tvuwo>5 z!nTi-c)X1mB*(?Kb18~Y=AUMB)%i9~DSvKmo!^4H{63bNeqLQ|ZlC{>iiTg6EoSwGRx0JwlAzyq`ZKA;Wg0J?x4;0j;`unOn{)}j?tpzBp( zuhX)y*Q>(b(9;g5+9+&&&Q-uQ!1b!IZR#8O4ahbDHvzXI$N01{3AN38fZKpOOPh|7 z--YZR;C;XcOPj9Q`x#_Ek8I=TJ}sN%J}sN%0WF*4LDeK5>LxLFTr;*?u>GjA{g`6@ zVef&SX9i>b0pF*Xf52w`0e?axGzb@q@}eRQWz194 z8_G95Vaho0BeYD}mx2GLAp<{&Q#N`YQ#q~ZT+R~~v_g`h&ot?ua5P~>M{$x=bj#49 zfQlnytj%1;Sq5T5v(Pf!=xoGAJc-jw;&~-jPQ+6g^zveiW=DmNW}LF1j}eef<1inw zvyA73V0q9DlQ?_huVl~t;L3W}^m3b$d^+M$lrwavW?=0R5gOlH+oprXs{U{rdND29 z93F=WBSJNX$7nCrG3XE@LgY-j*yU)Wu95DhkV@Ua!{e0mOxv7sp`76{Rxl|L9I|Ic ze5$n){b}*Q`Obve!3q; zj}fNoY4$NM#tAdLKOh-K&r^ai)dam$NQpT^vO_(hRTtEO;3pW27_6%GN2L18SYk99 zvQ#qU4!%R8T2GMw@?b-Lg+${^8EKhrs`{GS&&aca9i7YCUtrMgQ+kMMQEfemaGYjD zU&qzhO(nu|o}dy`Z-6}R%m>pr!Hi4Q%5EAa1!cQw#6-+RE^ZhbJ#2riyw62AnBqNA zZBPd>J4)h^2|eJ<&@7ib+QJ(S%IWt@aQP{ga{6^Fr-pyIp5u~xWxuxFH`DNZHbVO~ zcIuw>uEf7IKQHs^{}OxpHt3ldQ`VWX=E{x8EKcUiNy&@_s|kzbDA%vn81;xR74*?T z(@U}|;gkpxB3V`cx+D+r7z9nqO2Q{nig;W|JdBM~{amR=`7YM+9ID1dFnnKUY&N2V zO!B}!dl=Yrz}X|syrsq`eiHRARXCg(Lc1r&77X!_;OKvbFJXiGtLY4QFr4u>bkCbB`T@*3$ zzdt@v&L245xzF?=9t_Yz5m%nBC6sn1{$x%!PwTE^GgUtv?x@BADjh9x#3uRyRjos` zfC*03QAZa_N58zHte@XvGNERFcMYQaFIYPDj_s{`A35a<`d`O&tm~kCtMkffR?>3s crPDI}|D47pxxeIJIo>-u7yVqMwbz$F0wr&wjsO4v literal 0 HcmV?d00001 diff --git a/Xlib/protocol/display.py b/Xlib/protocol/display.py new file mode 100644 index 0000000..56623c3 --- /dev/null +++ b/Xlib/protocol/display.py @@ -0,0 +1,1075 @@ +# -*- coding: utf-8 -*- +# +# Xlib.protocol.display -- core display communication +# +# Copyright (C) 2000-2002 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +# Standard modules +import errno +import math +import select +import socket +import struct +import sys + +# Python 2/3 compatibility. +from six import PY3, byte2int, indexbytes + +# Xlib modules +from .. import error +from ..ext import ge + +from ..support import lock, connect + +# Xlib.protocol modules +from . import rq +from . import event + +if PY3: + + class bytesview(object): + + def __init__(self, data, offset=0, size=None): + if size is None: + size = len(data)-offset + if isinstance(data, bytes): + view = memoryview(data) + elif isinstance(data, bytesview): + view = data.view + else: + raise TypeError('unsupported type: {}'.format(type(data))) + self.view = view[offset:offset+size] + + def __len__(self): + return len(self.view) + + def __getitem__(self, key): + if isinstance(key, slice): + return bytes(self.view[key]) + return self.view[key] + +else: + + def bytesview(data, offset=0, size=None): + if not isinstance(data, (bytes, buffer)): + raise TypeError('unsupported type: {}'.format(type(data))) + if size is None: + size = len(data)-offset + return buffer(data, offset, size) + + +class Display(object): + extension_major_opcodes = {} + error_classes = error.xerror_class.copy() + event_classes = event.event_class.copy() + + def __init__(self, display = None): + name, protocol, host, displayno, screenno = connect.get_display(display) + + self.display_name = name + self.default_screen = screenno + + self.socket = connect.get_socket(name, protocol, host, displayno) + + auth_name, auth_data = connect.get_auth(self.socket, name, + protocol, host, displayno) + + # Internal structures for communication, grouped + # by their function and locks + + # Socket error indicator, set when the socket is closed + # in one way or another + self.socket_error_lock = lock.allocate_lock() + self.socket_error = None + + # Event queue + self.event_queue_read_lock = lock.allocate_lock() + self.event_queue_write_lock = lock.allocate_lock() + self.event_queue = [] + + # Unsent request queue and sequence number counter + self.request_queue_lock = lock.allocate_lock() + self.request_serial = 1 + self.request_queue = [] + + # Send-and-receive loop, see function send_and_receive + # for a detailed explanation + self.send_recv_lock = lock.allocate_lock() + self.send_active = 0 + self.recv_active = 0 + + self.event_waiting = 0 + self.event_wait_lock = lock.allocate_lock() + self.request_waiting = 0 + self.request_wait_lock = lock.allocate_lock() + + # Calculate optimal default buffer size for recv. + buffer_size = self.socket.getsockopt(socket.SOL_SOCKET, + socket.SO_RCVBUF) + buffer_size = math.pow(2, math.floor(math.log(buffer_size, 2))) + self.recv_buffer_size = int(buffer_size) + + # Data used by the send-and-receive loop + self.sent_requests = [] + self.recv_packet_len = 0 + self.data_send = b'' + self.data_recv = b'' + self.data_sent_bytes = 0 + + # Resource ID structures + self.resource_id_lock = lock.allocate_lock() + self.resource_ids = {} + self.last_resource_id = 0 + + # Use an default error handler, one which just prints the error + self.error_handler = None + + + # Right, now we're all set up for the connection setup + # request with the server. + + # Figure out which endianness the hardware uses + self.big_endian = struct.unpack('BB', struct.pack('H', 0x0100))[0] + + if self.big_endian: + order = 0x42 + else: + order = 0x6c + + # Send connection setup + r = ConnectionSetupRequest(self, + byte_order = order, + protocol_major = 11, + protocol_minor = 0, + auth_prot_name = auth_name, + auth_prot_data = auth_data) + + # Did connection fail? + if r.status != 1: + raise error.DisplayConnectionError(self.display_name, r.reason) + + # Set up remaining info + self.info = r + self.default_screen = min(self.default_screen, len(self.info.roots) - 1) + + + # + # Public interface + # + + def get_display_name(self): + return self.display_name + + def get_default_screen(self): + return self.default_screen + + def fileno(self): + self.check_for_error() + return self.socket.fileno() + + def next_event(self): + self.check_for_error() + + # Main lock, so that only one thread at a time performs the + # event waiting code. This at least guarantees that the first + # thread calling next_event() will get the next event, although + # no order is guaranteed among other threads calling next_event() + # while the first is blocking. + + self.event_queue_read_lock.acquire() + + # Lock event queue, so we can check if it is empty + self.event_queue_write_lock.acquire() + + # We have too loop until we get an event, as + # we might be woken up when there is no event. + + while not self.event_queue: + + # Lock send_recv so no send_and_receive + # can start or stop while we're checking + # whether there are one active. + self.send_recv_lock.acquire() + + # Release event queue to allow an send_and_recv to + # insert any now. + self.event_queue_write_lock.release() + + # Call send_and_recv, which will return when + # something has occured + self.send_and_recv(event = 1) + + # Before looping around, lock the event queue against + # modifications. + self.event_queue_write_lock.acquire() + + # Whiew, we have an event! Remove it from + # the event queue and relaese its write lock. + + event = self.event_queue[0] + del self.event_queue[0] + self.event_queue_write_lock.release() + + # Finally, allow any other threads which have called next_event() + # while we were waiting to proceed. + + self.event_queue_read_lock.release() + + # And return the event! + return event + + def pending_events(self): + self.check_for_error() + + # Make a send_and_recv pass, receiving any events + self.send_recv_lock.acquire() + self.send_and_recv(recv = 1) + + # Lock the queue, get the event count, and unlock again. + self.event_queue_write_lock.acquire() + count = len(self.event_queue) + self.event_queue_write_lock.release() + + return count + + def flush(self): + self.check_for_error() + self.send_recv_lock.acquire() + self.send_and_recv(flush = 1) + + def close(self): + self.flush() + self.close_internal('client') + + def set_error_handler(self, handler): + self.error_handler = handler + + + def allocate_resource_id(self): + """id = d.allocate_resource_id() + + Allocate a new X resource id number ID. + + Raises ResourceIDError if there are no free resource ids. + """ + + self.resource_id_lock.acquire() + try: + i = self.last_resource_id + while i in self.resource_ids: + i = i + 1 + if i > self.info.resource_id_mask: + i = 0 + if i == self.last_resource_id: + raise error.ResourceIDError('out of resource ids') + + self.resource_ids[i] = None + self.last_resource_id = i + return self.info.resource_id_base | i + finally: + self.resource_id_lock.release() + + def free_resource_id(self, rid): + """d.free_resource_id(rid) + + Free resource id RID. Attempts to free a resource id which + isn't allocated by us are ignored. + """ + + self.resource_id_lock.acquire() + try: + i = rid & self.info.resource_id_mask + + # Attempting to free a resource id outside our range + if rid - i != self.info.resource_id_base: + return None + + try: + del self.resource_ids[i] + except KeyError: + pass + finally: + self.resource_id_lock.release() + + + + def get_resource_class(self, class_name, default = None): + """class = d.get_resource_class(class_name, default = None) + + Return the class to be used for X resource objects of type + CLASS_NAME, or DEFAULT if no such class is set. + """ + + return self.resource_classes.get(class_name, default) + + def set_extension_major(self, extname, major): + self.extension_major_opcodes[extname] = major + + def get_extension_major(self, extname): + return self.extension_major_opcodes[extname] + + def add_extension_event(self, code, evt, subcode=None): + if subcode == None: + self.event_classes[code] = evt + else: + if not code in self.event_classes: + self.event_classes[code] = {subcode: evt} + else: + self.event_classes[code][subcode] = evt + + def add_extension_error(self, code, err): + self.error_classes[code] = err + + + # + # Private functions + # + + def check_for_error(self): + self.socket_error_lock.acquire() + err = self.socket_error + self.socket_error_lock.release() + + if err: + raise err + + def send_request(self, request, wait_for_response): + if self.socket_error: + raise self.socket_error + + self.request_queue_lock.acquire() + + request._serial = self.request_serial + self.request_serial = (self.request_serial + 1) % 65536 + + self.request_queue.append((request, wait_for_response)) + qlen = len(self.request_queue) + + self.request_queue_lock.release() + +# if qlen > 10: +# self.flush() + + def close_internal(self, whom): + # Clear out data structures + self.request_queue = None + self.sent_requests = None + self.event_queue = None + self.data_send = None + self.data_recv = None + + # Close the connection + self.socket.close() + + # Set a connection closed indicator + self.socket_error_lock.acquire() + self.socket_error = error.ConnectionClosedError(whom) + self.socket_error_lock.release() + + + def send_and_recv(self, flush = None, event = None, request = None, recv = None): + """send_and_recv(flush = None, event = None, request = None, recv = None) + + Perform I/O, or wait for some other thread to do it for us. + + send_recv_lock MUST be LOCKED when send_and_recv is called. + It will be UNLOCKED at return. + + Exactly or one of the parameters flush, event, request and recv must + be set to control the return condition. + + To attempt to send all requests in the queue, flush should + be true. Will return immediately if another thread is + already doing send_and_recv. + + To wait for an event to be received, event should be true. + + To wait for a response to a certain request (either an error + or a response), request should be set the that request's + serial number. + + To just read any pending data from the server, recv should be true. + + It is not guaranteed that the return condition has been + fulfilled when the function returns, so the caller has to loop + until it is finished. + """ + + # We go to sleep if there is already a thread doing what we + # want to do: + + # If flushing, we want to send + # If waiting for a response to a request, we want to send + # (to ensure that the request was sent - we alway recv + # when we get to the main loop, but sending is the important + # thing here) + # If waiting for an event, we want to recv + # If just trying to receive anything we can, we want to recv + + # FIXME: It would be good if we could also sleep when we're waiting on + # a response to a request that has already been sent. + + if (((flush or request is not None) and self.send_active) + or ((event or recv) and self.recv_active)): + + # Signal that we are waiting for something. These locks + # together with the *_waiting variables are used as + # semaphores. When an event or a request response arrives, + # it will zero the *_waiting and unlock the lock. The + # locks will also be unlocked when an active send_and_recv + # finishes to signal the other waiting threads that one of + # them has to take over the send_and_recv function. + + # All this makes these locks and variables a part of the + # send_and_recv control logic, and hence must be modified + # only when we have the send_recv_lock locked. + if event: + wait_lock = self.event_wait_lock + if not self.event_waiting: + self.event_waiting = 1 + wait_lock.acquire() + + elif request is not None: + wait_lock = self.request_wait_lock + if not self.request_waiting: + self.request_waiting = 1 + wait_lock.acquire() + + # Release send_recv, allowing a send_and_recive + # to terminate or other threads to queue up + self.send_recv_lock.release() + + # Return immediately if flushing, even if that + # might mean that not necessarily all requests + # have been sent. + if flush or recv: + return + + # Wait for something to happen, as the wait locks are + # unlocked either when what we wait for has arrived (not + # necessarily the exact object we're waiting for, though), + # or when an active send_and_recv exits. + + # Release it immediately afterwards as we're only using + # the lock for synchonization. Since we're not modifying + # event_waiting or request_waiting here we don't have + # to lock send_and_recv_lock. In fact, we can't do that + # or we trigger a dead-lock. + + wait_lock.acquire() + wait_lock.release() + + # Return to caller to let it check whether it has + # got the data it was waiting for + return + + + # There's no thread doing what we need to do. Find out exactly + # what to do + + # There must always be some thread receiving data, but it must not + # necessarily be us + + if not self.recv_active: + receiving = 1 + self.recv_active = 1 + else: + receiving = 0 + + flush_bytes = None + sending = 0 + + # Loop, receiving and sending data. + while 1: + + # We might want to start sending data + if sending or not self.send_active: + + # Turn all requests on request queue into binary form + # and append them to self.data_send + + self.request_queue_lock.acquire() + for req, wait in self.request_queue: + self.data_send = self.data_send + req._binary + if wait: + self.sent_requests.append(req) + + del self.request_queue[:] + self.request_queue_lock.release() + + # If there now is data to send, mark us as senders + + if self.data_send: + self.send_active = 1 + sending = 1 + else: + self.send_active = 0 + sending = 0 + + # We've done all setup, so release the lock and start waiting + # for the network to fire up + self.send_recv_lock.release() + + # There's no longer anything useful we can do here. + if not (sending or receiving): + break + + # If we're flushing, figure out how many bytes we + # have to send so that we're not caught in an interminable + # loop if other threads continuously append requests. + if flush and flush_bytes is None: + flush_bytes = self.data_sent_bytes + len(self.data_send) + + + try: + # We're only checking for the socket to be writable + # if we're the sending thread. We always check for it + # to become readable: either we are the receiving thread + # and should take care of the data, or the receiving thread + # might finish receiving after having read the data + + if sending: + writeset = [self.socket] + else: + writeset = [] + + # Timeout immediately if we're only checking for + # something to read or if we're flushing, otherwise block + + if recv or flush: + timeout = 0 + else: + timeout = None + + rs, ws, es = select.select([self.socket], writeset, [], timeout) + + # Ignore errors caused by a signal received while blocking. + # All other errors are re-raised. + except select.error as err: + if isinstance(err, OSError): + code = err.errno + else: + code = err[0] + if code != errno.EINTR: + raise + + # We must lock send_and_recv before we can loop to + # the start of the loop + + self.send_recv_lock.acquire() + continue + + + # Socket is ready for sending data, send as much as possible. + if ws: + try: + i = self.socket.send(self.data_send) + except socket.error as err: + self.close_internal('server: %s' % err) + raise self.socket_error + + self.data_send = self.data_send[i:] + self.data_sent_bytes = self.data_sent_bytes + i + + + # There is data to read + gotreq = 0 + if rs: + + # We're the receiving thread, parse the data + if receiving: + try: + count = self.recv_packet_len - len(self.data_recv) + count = max(self.recv_buffer_size, count) + bytes_recv = self.socket.recv(count) + except socket.error as err: + self.close_internal('server: %s' % err) + raise self.socket_error + + if not bytes_recv: + # Clear up, set a connection closed indicator and raise it + self.close_internal('server') + raise self.socket_error + + self.data_recv = bytes(self.data_recv) + bytes_recv + gotreq = self.parse_response(request) + + # Otherwise return, allowing the calling thread to figure + # out if it has got the data it needs + else: + # We must be a sending thread if we're here, so reset + # that indicator. + self.send_recv_lock.acquire() + self.send_active = 0 + self.send_recv_lock.release() + + # And return to the caller + return + + + # There are three different end of send-recv-loop conditions. + # However, we don't leave the loop immediately, instead we + # try to send and receive any data that might be left. We + # do this by giving a timeout of 0 to select to poll + # the socket. + + # When flushing: all requests have been sent + if flush and flush_bytes >= self.data_sent_bytes: + break + + # When waiting for an event: an event has been read + if event and self.event_queue: + break + + # When processing a certain request: got its reply + if request is not None and gotreq: + break + + # Always break if we just want to receive as much as possible + if recv: + break + + # Else there's may still data which must be sent, or + # we haven't got the data we waited for. Lock and loop + + self.send_recv_lock.acquire() + + + # We have accomplished the callers request. + # Record that there are now no active send_and_recv, + # and wake up all waiting thread + + self.send_recv_lock.acquire() + + if sending: + self.send_active = 0 + if receiving: + self.recv_active = 0 + + if self.event_waiting: + self.event_waiting = 0 + self.event_wait_lock.release() + + if self.request_waiting: + self.request_waiting = 0 + self.request_wait_lock.release() + + self.send_recv_lock.release() + + + def parse_response(self, request): + """Internal method. + + Parse data received from server. If REQUEST is not None + true is returned if the request with that serial number + was received, otherwise false is returned. + + If REQUEST is -1, we're parsing the server connection setup + response. + """ + + if request == -1: + return self.parse_connection_setup() + + # Parse ordinary server response + gotreq = 0 + while 1: + if self.data_recv: + # Check the first byte to find out what kind of response it is + rtype = byte2int(self.data_recv) + + # Are we're waiting for additional data for the current packet? + if self.recv_packet_len: + if len(self.data_recv) < self.recv_packet_len: + return gotreq + + if rtype == 1: + gotreq = self.parse_request_response(request) or gotreq + continue + elif rtype & 0x7f == ge.GenericEventCode: + self.parse_event_response(rtype) + continue + else: + raise AssertionError(rtype) + + # Every response is at least 32 bytes long, so don't bother + # until we have received that much + if len(self.data_recv) < 32: + return gotreq + + # Error response + if rtype == 0: + gotreq = self.parse_error_response(request) or gotreq + + # Request response or generic event. + elif rtype == 1 or rtype & 0x7f == ge.GenericEventCode: + # Set reply length, and loop around to see if + # we have got the full response + rlen = int(struct.unpack('=L', self.data_recv[4:8])[0]) + self.recv_packet_len = 32 + rlen * 4 + + # Else non-generic event + else: + self.parse_event_response(rtype) + + + def parse_error_response(self, request): + # Code is second byte + code = indexbytes(self.data_recv, 1) + + # Fetch error class + estruct = self.error_classes.get(code, error.XError) + + e = estruct(self, self.data_recv[:32]) + self.data_recv = bytesview(self.data_recv, 32) + + # print 'recv Error:', e + + req = self.get_waiting_request(e.sequence_number) + + # Error for a request whose response we are waiting for, + # or which have an error handler. However, if the error + # handler indicates that it hasn't taken care of the + # error, pass it on to the default error handler + + if req and req._set_error(e): + + # If this was a ReplyRequest, unlock any threads waiting + # for a request to finish + + if isinstance(req, rq.ReplyRequest): + self.send_recv_lock.acquire() + + if self.request_waiting: + self.request_waiting = 0 + self.request_wait_lock.release() + + self.send_recv_lock.release() + + return request == e.sequence_number + + # Else call the error handler + else: + if self.error_handler: + rq.call_error_handler(self.error_handler, e, None) + else: + self.default_error_handler(e) + + return 0 + + + def default_error_handler(self, err): + sys.stderr.write('X protocol error:\n%s\n' % err) + + + def parse_request_response(self, request): + req = self.get_waiting_replyrequest() + + # Sequence number is always data[2:4] + # Do sanity check before trying to parse the data + sno = struct.unpack('=H', self.data_recv[2:4])[0] + if sno != req._serial: + raise RuntimeError("Expected reply for request %s, but got %s. Can't happen!" + % (req._serial, sno)) + + req._parse_response(self.data_recv[:self.recv_packet_len]) + # print 'recv Request:', req + + self.data_recv = bytesview(self.data_recv, self.recv_packet_len) + self.recv_packet_len = 0 + + + # Unlock any response waiting threads + + self.send_recv_lock.acquire() + + if self.request_waiting: + self.request_waiting = 0 + self.request_wait_lock.release() + + self.send_recv_lock.release() + + + return req.sequence_number == request + + + def parse_event_response(self, etype): + # Skip bit 8, that is set if this event came from an SendEvent + etype = etype & 0x7f + + if etype == ge.GenericEventCode: + length = self.recv_packet_len + else: + length = 32 + + estruct = self.event_classes.get(etype, event.AnyEvent) + if type(estruct) == dict: + subcode = self.data_recv[1] + + # Python2 compatibility + if type(subcode) == str: + subcode = ord(subcode) + + # this etype refers to a set of sub-events with individual subcodes + estruct = estruct[subcode] + + e = estruct(display = self, binarydata = self.data_recv[:length]) + + if etype == ge.GenericEventCode: + self.recv_packet_len = 0 + + self.data_recv = bytesview(self.data_recv, length) + + # Drop all requests having an error handler, + # but which obviously succeded. + + # Decrement it by one, so that we don't remove the request + # that generated these events, if there is such a one. + # Bug reported by Ilpo Nyyssönen + # Note: not all events have a sequence_number field! + # (e.g. KeymapNotify). + if hasattr(e, 'sequence_number'): + self.get_waiting_request((e.sequence_number - 1) % 65536) + + # print 'recv Event:', e + + # Insert the event into the queue + self.event_queue_write_lock.acquire() + self.event_queue.append(e) + self.event_queue_write_lock.release() + + # Unlock any event waiting threads + self.send_recv_lock.acquire() + + if self.event_waiting: + self.event_waiting = 0 + self.event_wait_lock.release() + + self.send_recv_lock.release() + + + def get_waiting_request(self, sno): + if not self.sent_requests: + return None + + # Normalize sequence numbers, even if they have wrapped. + # This ensures that + # sno <= last_serial + # and + # self.sent_requests[0]._serial <= last_serial + + if self.sent_requests[0]._serial > self.request_serial: + last_serial = self.request_serial + 65536 + if sno < self.request_serial: + sno = sno + 65536 + + else: + last_serial = self.request_serial + if sno > self.request_serial: + sno = sno - 65536 + + # No matching events at all + if sno < self.sent_requests[0]._serial: + return None + + # Find last req <= sno + req = None + reqpos = len(self.sent_requests) + adj = 0 + last = 0 + + for i in range(0, len(self.sent_requests)): + rno = self.sent_requests[i]._serial + adj + + # Did serial numbers just wrap around? + if rno < last: + adj = 65536 + rno = rno + adj + + last = rno + + if sno == rno: + req = self.sent_requests[i] + reqpos = i + 1 + break + elif sno < rno: + req = None + reqpos = i + break + + # Delete all request such as req <= sno + del self.sent_requests[:reqpos] + + return req + + def get_waiting_replyrequest(self): + for i in range(0, len(self.sent_requests)): + if hasattr(self.sent_requests[i], '_reply'): + req = self.sent_requests[i] + del self.sent_requests[:i + 1] + return req + + # Reply for an unknown request? No, that can't happen. + else: + raise RuntimeError("Request reply to unknown request. Can't happen!") + + def parse_connection_setup(self): + """Internal function used to parse connection setup response. + """ + + # Only the ConnectionSetupRequest has been sent so far + r = self.sent_requests[0] + + while 1: + # print 'data_send:', repr(self.data_send) + # print 'data_recv:', repr(self.data_recv) + + if r._data: + alen = r._data['additional_length'] * 4 + + # The full response haven't arrived yet + if len(self.data_recv) < alen: + return 0 + + # Connection failed or further authentication is needed. + # Set reason to the reason string + if r._data['status'] != 1: + r._data['reason'] = self.data_recv[:r._data['reason_length']] + + # Else connection succeeded, parse the reply + else: + x, d = r._success_reply.parse_binary(self.data_recv[:alen], + self, rawdict = 1) + r._data.update(x) + + del self.sent_requests[0] + + self.data_recv = self.data_recv[alen:] + + return 1 + + else: + # The base reply is 8 bytes long + if len(self.data_recv) < 8: + return 0 + + r._data, d = r._reply.parse_binary(self.data_recv[:8], + self, rawdict = 1) + self.data_recv = self.data_recv[8:] + + # Loop around to see if we have got the additional data + # already + + +PixmapFormat = rq.Struct( rq.Card8('depth'), + rq.Card8('bits_per_pixel'), + rq.Card8('scanline_pad'), + rq.Pad(5) + ) + +VisualType = rq.Struct ( rq.Card32('visual_id'), + rq.Card8('visual_class'), + rq.Card8('bits_per_rgb_value'), + rq.Card16('colormap_entries'), + rq.Card32('red_mask'), + rq.Card32('green_mask'), + rq.Card32('blue_mask'), + rq.Pad(4) + ) + +Depth = rq.Struct( rq.Card8('depth'), + rq.Pad(1), + rq.LengthOf('visuals', 2), + rq.Pad(4), + rq.List('visuals', VisualType) + ) + +Screen = rq.Struct( rq.Window('root'), + rq.Colormap('default_colormap'), + rq.Card32('white_pixel'), + rq.Card32('black_pixel'), + rq.Card32('current_input_mask'), + rq.Card16('width_in_pixels'), + rq.Card16('height_in_pixels'), + rq.Card16('width_in_mms'), + rq.Card16('height_in_mms'), + rq.Card16('min_installed_maps'), + rq.Card16('max_installed_maps'), + rq.Card32('root_visual'), + rq.Card8('backing_store'), + rq.Card8('save_unders'), + rq.Card8('root_depth'), + rq.LengthOf('allowed_depths', 1), + rq.List('allowed_depths', Depth) + ) + + +class ConnectionSetupRequest(rq.GetAttrData): + _request = rq.Struct( rq.Set('byte_order', 1, (0x42, 0x6c)), + rq.Pad(1), + rq.Card16('protocol_major'), + rq.Card16('protocol_minor'), + rq.LengthOf('auth_prot_name', 2), + rq.LengthOf('auth_prot_data', 2), + rq.Pad(2), + rq.String8('auth_prot_name'), + rq.String8('auth_prot_data') ) + + _reply = rq.Struct ( rq.Card8('status'), + rq.Card8('reason_length'), + rq.Card16('protocol_major'), + rq.Card16('protocol_minor'), + rq.Card16('additional_length') ) + + _success_reply = rq.Struct( rq.Card32('release_number'), + rq.Card32('resource_id_base'), + rq.Card32('resource_id_mask'), + rq.Card32('motion_buffer_size'), + rq.LengthOf('vendor', 2), + rq.Card16('max_request_length'), + rq.LengthOf('roots', 1), + rq.LengthOf('pixmap_formats', 1), + rq.Card8('image_byte_order'), + rq.Card8('bitmap_format_bit_order'), + rq.Card8('bitmap_format_scanline_unit'), + rq.Card8('bitmap_format_scanline_pad'), + rq.Card8('min_keycode'), + rq.Card8('max_keycode'), + rq.Pad(4), + rq.String8('vendor'), + rq.List('pixmap_formats', PixmapFormat), + rq.List('roots', Screen), + ) + + + def __init__(self, display, *args, **keys): + self._binary = self._request.to_binary(*args, **keys) + self._data = None + + # Don't bother about locking, since no other threads have + # access to the display yet + + display.request_queue.append((self, 1)) + + # However, we must lock send_and_recv, but we don't have + # to loop. + + display.send_recv_lock.acquire() + display.send_and_recv(request = -1) diff --git a/Xlib/protocol/event.py b/Xlib/protocol/event.py new file mode 100644 index 0000000..04743c6 --- /dev/null +++ b/Xlib/protocol/event.py @@ -0,0 +1,434 @@ +# Xlib.protocol.event -- definitions of core events +# +# Copyright (C) 2000-2002 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + + +# Xlib modules +from .. import X + +# Xlib.protocol modules +from . import rq + + +class AnyEvent(rq.Event): + _code = None + _fields = rq.Struct( rq.Card8('type'), + rq.Card8('detail'), + rq.Card16('sequence_number'), + rq.FixedBinary('data', 28), + ) + +class KeyButtonPointer(rq.Event): + _code = None + _fields = rq.Struct( rq.Card8('type'), + rq.Card8('detail'), + rq.Card16('sequence_number'), + rq.Card32('time'), + rq.Window('root'), + rq.Window('window'), + rq.Window('child', (X.NONE, )), + rq.Int16('root_x'), + rq.Int16('root_y'), + rq.Int16('event_x'), + rq.Int16('event_y'), + rq.Card16('state'), + rq.Card8('same_screen'), + rq.Pad(1), + ) + +class KeyPress(KeyButtonPointer): + _code = X.KeyPress + +class KeyRelease(KeyButtonPointer): + _code = X.KeyRelease + +class ButtonPress(KeyButtonPointer): + _code = X.ButtonPress + +class ButtonRelease(KeyButtonPointer): + _code = X.ButtonRelease + +class MotionNotify(KeyButtonPointer): + _code = X.MotionNotify + +class EnterLeave(rq.Event): + _code = None + _fields = rq.Struct( rq.Card8('type'), + rq.Card8('detail'), + rq.Card16('sequence_number'), + rq.Card32('time'), + rq.Window('root'), + rq.Window('window'), + rq.Window('child', (X.NONE, )), + rq.Int16('root_x'), + rq.Int16('root_y'), + rq.Int16('event_x'), + rq.Int16('event_y'), + rq.Card16('state'), + rq.Card8('mode'), + rq.Card8('flags'), + ) + +class EnterNotify(EnterLeave): + _code = X.EnterNotify + +class LeaveNotify(EnterLeave): + _code = X.LeaveNotify + + +class Focus(rq.Event): + _code = None + _fields = rq.Struct( rq.Card8('type'), + rq.Card8('detail'), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Card8('mode'), + rq.Pad(23), + ) + +class FocusIn(Focus): + _code = X.FocusIn + +class FocusOut(Focus): + _code = X.FocusOut + +class Expose(rq.Event): + _code = X.Expose + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Card16('x'), + rq.Card16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('count'), + rq.Pad(14), + ) + +class GraphicsExpose(rq.Event): + _code = X.GraphicsExpose + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Drawable('drawable'), + rq.Card16('x'), + rq.Card16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('minor_event'), + rq.Card16('count'), + rq.Card8('major_event'), + rq.Pad(11), + ) + +class NoExpose(rq.Event): + _code = X.NoExpose + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Drawable('window'), + rq.Card16('minor_event'), + rq.Card8('major_event'), + rq.Pad(21), + ) + +class VisibilityNotify(rq.Event): + _code = X.VisibilityNotify + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Card8('state'), + rq.Pad(23), + ) + +class CreateNotify(rq.Event): + _code = X.CreateNotify + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('parent'), + rq.Window('window'), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('border_width'), + rq.Card8('override'), + rq.Pad(9), + ) + +class DestroyNotify(rq.Event): + _code = X.DestroyNotify + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('event'), + rq.Window('window'), + rq.Pad(20), + ) + +class UnmapNotify(rq.Event): + _code = X.UnmapNotify + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('event'), + rq.Window('window'), + rq.Card8('from_configure'), + rq.Pad(19), + ) + +class MapNotify(rq.Event): + _code = X.MapNotify + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('event'), + rq.Window('window'), + rq.Card8('override'), + rq.Pad(19), + ) + +class MapRequest(rq.Event): + _code = X.MapRequest + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('parent'), + rq.Window('window'), + rq.Pad(20), + ) + +class ReparentNotify(rq.Event): + _code = X.ReparentNotify + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('event'), + rq.Window('window'), + rq.Window('parent'), + rq.Int16('x'), + rq.Int16('y'), + rq.Card8('override'), + rq.Pad(11), + ) + +class ConfigureNotify(rq.Event): + _code = X.ConfigureNotify + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('event'), + rq.Window('window'), + rq.Window('above_sibling', (X.NONE, )), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('border_width'), + rq.Card8('override'), + rq.Pad(5), + ) + +class ConfigureRequest(rq.Event): + _code = X.ConfigureRequest + _fields = rq.Struct( rq.Card8('type'), + rq.Card8('stack_mode'), + rq.Card16('sequence_number'), + rq.Window('parent'), + rq.Window('window'), + rq.Window('sibling', (X.NONE, )), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('border_width'), + rq.Card16('value_mask'), + rq.Pad(4), + ) + +class GravityNotify(rq.Event): + _code = X.GravityNotify + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('event'), + rq.Window('window'), + rq.Int16('x'), + rq.Int16('y'), + rq.Pad(16), + ) + +class ResizeRequest(rq.Event): + _code = X.ResizeRequest + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Card16('width'), + rq.Card16('height'), + rq.Pad(20), + ) + +class Circulate(rq.Event): + _code = None + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('event'), + rq.Window('window'), + rq.Pad(4), + rq.Card8('place'), + rq.Pad(15), + ) + +class CirculateNotify(Circulate): + _code = X.CirculateNotify + +class CirculateRequest(Circulate): + _code = X.CirculateRequest + +class PropertyNotify(rq.Event): + _code = X.PropertyNotify + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Card32('atom'), + rq.Card32('time'), + rq.Card8('state'), + rq.Pad(15), + ) + +class SelectionClear(rq.Event): + _code = X.SelectionClear + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Card32('time'), + rq.Window('window'), + rq.Card32('atom'), + rq.Pad(16), + ) + +class SelectionRequest(rq.Event): + _code = X.SelectionRequest + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Card32('time'), + rq.Window('owner'), + rq.Window('requestor'), + rq.Card32('selection'), + rq.Card32('target'), + rq.Card32('property'), + rq.Pad(4), + ) + +class SelectionNotify(rq.Event): + _code = X.SelectionNotify + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Card32('time'), + rq.Window('requestor'), + rq.Card32('selection'), + rq.Card32('target'), + rq.Card32('property'), + rq.Pad(8), + ) + +class ColormapNotify(rq.Event): + _code = X.ColormapNotify + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Colormap('colormap', (X.NONE, )), + rq.Card8('new'), + rq.Card8('state'), + rq.Pad(18), + ) + +class MappingNotify(rq.Event): + _code = X.MappingNotify + _fields = rq.Struct( rq.Card8('type'), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.Card8('request'), + rq.Card8('first_keycode'), + rq.Card8('count'), + rq.Pad(25), + ) + +class ClientMessage(rq.Event): + _code = X.ClientMessage + _fields = rq.Struct( rq.Card8('type'), + rq.Format('data', 1), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Card32('client_type'), + rq.FixedPropertyData('data', 20), + ) + +class KeymapNotify(rq.Event): + _code = X.KeymapNotify + _fields = rq.Struct( rq.Card8('type'), + rq.FixedList('data', 31, rq.Card8Obj, pad = 0) + ) + + +event_class = { + X.KeyPress: KeyPress, + X.KeyRelease: KeyRelease, + X.ButtonPress: ButtonPress, + X.ButtonRelease: ButtonRelease, + X.MotionNotify: MotionNotify, + X.EnterNotify: EnterNotify, + X.LeaveNotify: LeaveNotify, + X.FocusIn: FocusIn, + X.FocusOut: FocusOut, + X.KeymapNotify: KeymapNotify, + X.Expose: Expose, + X.GraphicsExpose: GraphicsExpose, + X.NoExpose: NoExpose, + X.VisibilityNotify: VisibilityNotify, + X.CreateNotify: CreateNotify, + X.DestroyNotify: DestroyNotify, + X.UnmapNotify: UnmapNotify, + X.MapNotify: MapNotify, + X.MapRequest: MapRequest, + X.ReparentNotify: ReparentNotify, + X.ConfigureNotify: ConfigureNotify, + X.ConfigureRequest: ConfigureRequest, + X.GravityNotify: GravityNotify, + X.ResizeRequest: ResizeRequest, + X.CirculateNotify: CirculateNotify, + X.CirculateRequest: CirculateRequest, + X.PropertyNotify: PropertyNotify, + X.SelectionClear: SelectionClear, + X.SelectionRequest: SelectionRequest, + X.SelectionNotify: SelectionNotify, + X.ColormapNotify: ColormapNotify, + X.ClientMessage: ClientMessage, + X.MappingNotify: MappingNotify, + } diff --git a/Xlib/protocol/request.py b/Xlib/protocol/request.py new file mode 100644 index 0000000..b431e13 --- /dev/null +++ b/Xlib/protocol/request.py @@ -0,0 +1,1900 @@ +# Xlib.protocol.request -- definitions of core requests +# +# Copyright (C) 2000-2002 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + + +# Xlib modules +from .. import X + +# Xlib.protocol modules +from . import rq +from . import structs + + +class CreateWindow(rq.Request): + _request = rq.Struct( + rq.Opcode(1), + rq.Card8('depth'), + rq.RequestLength(), + rq.Window('wid'), + rq.Window('parent'), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('border_width'), + rq.Set('window_class', 2, (X.CopyFromParent, X.InputOutput, X.InputOnly)), + rq.Card32('visual'), + structs.WindowValues('attrs'), + ) + +class ChangeWindowAttributes(rq.Request): + _request = rq.Struct( + rq.Opcode(2), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window'), + structs.WindowValues('attrs'), + ) + +class GetWindowAttributes(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(3), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window') + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('backing_store'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('visual'), + rq.Card16('win_class'), + rq.Card8('bit_gravity'), + rq.Card8('win_gravity'), + rq.Card32('backing_bit_planes'), + rq.Card32('backing_pixel'), + rq.Card8('save_under'), + rq.Card8('map_is_installed'), + rq.Card8('map_state'), + rq.Card8('override_redirect'), + rq.Colormap('colormap', (X.NONE, )), + rq.Card32('all_event_masks'), + rq.Card32('your_event_mask'), + rq.Card16('do_not_propagate_mask'), + rq.Pad(2), + ) + +class DestroyWindow(rq.Request): + _request = rq.Struct( + rq.Opcode(4), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window') + ) + +class DestroySubWindows(rq.Request): + _request = rq.Struct( + rq.Opcode(5), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window') + ) + +class ChangeSaveSet(rq.Request): + _request = rq.Struct( + rq.Opcode(6), + rq.Set('mode', 1, (X.SetModeInsert, X.SetModeDelete)), + rq.RequestLength(), + rq.Window('window'), + ) + +class ReparentWindow(rq.Request): + _request = rq.Struct( + rq.Opcode(7), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window'), + rq.Window('parent'), + rq.Int16('x'), + rq.Int16('y'), + ) + +class MapWindow(rq.Request): + _request = rq.Struct( + rq.Opcode(8), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window') + ) + +class MapSubwindows(rq.Request): + _request = rq.Struct( + rq.Opcode(9), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window') + ) + +class UnmapWindow(rq.Request): + _request = rq.Struct( + rq.Opcode(10), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window') + ) + +class UnmapSubwindows(rq.Request): + _request = rq.Struct( + rq.Opcode(11), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window') + ) + +class ConfigureWindow(rq.Request): + _request = rq.Struct( + rq.Opcode(12), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window'), + rq.ValueList( 'attrs', 2, 2, + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Int16('border_width'), + rq.Window('sibling'), + rq.Set('stack_mode', 1, + (X.Above, X.Below, X.TopIf, + X.BottomIf, X.Opposite)) + ) + ) + +class CirculateWindow(rq.Request): + _request = rq.Struct( + rq.Opcode(13), + rq.Set('direction', 1, (X.RaiseLowest, X.LowerHighest)), + rq.RequestLength(), + rq.Window('window'), + ) + +class GetGeometry(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(14), + rq.Pad(1), + rq.RequestLength(), + rq.Drawable('drawable') + ) + + _reply = rq.Struct ( + rq.ReplyCode(), + rq.Card8('depth'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('root'), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('border_width'), + rq.Pad(10) + ) + +class QueryTree(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(15), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window') + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('root'), + rq.Window('parent', (X.NONE, )), + rq.LengthOf('children', 2), + rq.Pad(14), + rq.List('children', rq.WindowObj), + ) + +class InternAtom(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(16), + rq.Bool('only_if_exists'), + rq.RequestLength(), + rq.LengthOf('name', 2), + rq.Pad(2), + rq.String8('name'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('atom'), + rq.Pad(20), + ) + + +class GetAtomName(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(17), + rq.Pad(1), + rq.RequestLength(), + rq.Card32('atom') + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('name', 2), + rq.Pad(22), + rq.String8('name'), + ) + +class ChangeProperty(rq.Request): + _request = rq.Struct( + rq.Opcode(18), + rq.Set('mode', 1, (X.PropModeReplace, X.PropModePrepend, X.PropModeAppend)), + rq.RequestLength(), + rq.Window('window'), + rq.Card32('property'), + rq.Card32('type'), + rq.Format('data', 1), + rq.Pad(3), + rq.LengthOf('data', 4), + rq.PropertyData('data'), + ) + +class DeleteProperty(rq.Request): + _request = rq.Struct( + rq.Opcode(19), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window'), + rq.Card32('property'), + ) + +class GetProperty(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(20), + rq.Bool('delete'), + rq.RequestLength(), + rq.Window('window'), + rq.Card32('property'), + rq.Card32('type'), + rq.Card32('long_offset'), + rq.Card32('long_length'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Format('value', 1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('property_type'), + rq.Card32('bytes_after'), + rq.LengthOf('value', 4), + rq.Pad(12), + rq.PropertyData('value'), + ) + +class ListProperties(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(21), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window') + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('atoms', 2), + rq.Pad(22), + rq.List('atoms', rq.Card32Obj), + ) + +class SetSelectionOwner(rq.Request): + _request = rq.Struct( + rq.Opcode(22), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window'), + rq.Card32('selection'), + rq.Card32('time'), + ) + +class GetSelectionOwner(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(23), + rq.Pad(1), + rq.RequestLength(), + rq.Card32('selection') + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('owner', (X.NONE, )), + rq.Pad(20), + ) + +class ConvertSelection(rq.Request): + _request = rq.Struct( + rq.Opcode(24), + rq.Pad(1), + rq.RequestLength(), + rq.Window('requestor'), + rq.Card32('selection'), + rq.Card32('target'), + rq.Card32('property'), + rq.Card32('time'), + ) + +class SendEvent(rq.Request): + _request = rq.Struct( + rq.Opcode(25), + rq.Bool('propagate'), + rq.RequestLength(), + rq.Window('destination'), + rq.Card32('event_mask'), + rq.EventField('event'), + ) + +class GrabPointer(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(26), + rq.Bool('owner_events'), + rq.RequestLength(), + rq.Window('grab_window'), + rq.Card16('event_mask'), + rq.Set('pointer_mode', 1, (X.GrabModeSync, X.GrabModeAsync)), + rq.Set('keyboard_mode', 1, (X.GrabModeSync, X.GrabModeAsync)), + rq.Window('confine_to', (X.NONE, )), + rq.Cursor('cursor', (X.NONE, )), + rq.Card32('time'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Pad(24), + ) + +class UngrabPointer(rq.Request): + _request = rq.Struct( + rq.Opcode(27), + rq.Pad(1), + rq.RequestLength(), + rq.Card32('time') + ) + +class GrabButton(rq.Request): + _request = rq.Struct( + rq.Opcode(28), + rq.Bool('owner_events'), + rq.RequestLength(), + rq.Window('grab_window'), + rq.Card16('event_mask'), + rq.Set('pointer_mode', 1, (X.GrabModeSync, X.GrabModeAsync)), + rq.Set('keyboard_mode', 1, (X.GrabModeSync, X.GrabModeAsync)), + rq.Window('confine_to', (X.NONE, )), + rq.Cursor('cursor', (X.NONE, )), + rq.Card8('button'), + rq.Pad(1), + rq.Card16('modifiers'), + ) + +class UngrabButton(rq.Request): + _request = rq.Struct( + rq.Opcode(29), + rq.Card8('button'), + rq.RequestLength(), + rq.Window('grab_window'), + rq.Card16('modifiers'), + rq.Pad(2), + ) + +class ChangeActivePointerGrab(rq.Request): + _request = rq.Struct( + rq.Opcode(30), + rq.Pad(1), + rq.RequestLength(), + rq.Cursor('cursor'), + rq.Card32('time'), + rq.Card16('event_mask'), + rq.Pad(2), + ) + +class GrabKeyboard(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(31), + rq.Bool('owner_events'), + rq.RequestLength(), + rq.Window('grab_window'), + rq.Card32('time'), + rq.Set('pointer_mode', 1, (X.GrabModeSync, X.GrabModeAsync)), + rq.Set('keyboard_mode', 1, (X.GrabModeSync, X.GrabModeAsync)), + rq.Pad(2), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Pad(24), + ) + +class UngrabKeyboard(rq.Request): + _request = rq.Struct( + rq.Opcode(32), + rq.Pad(1), + rq.RequestLength(), + rq.Card32('time') + ) + +class GrabKey(rq.Request): + _request = rq.Struct( + rq.Opcode(33), + rq.Bool('owner_events'), + rq.RequestLength(), + rq.Window('grab_window'), + rq.Card16('modifiers'), + rq.Card8('key'), + rq.Set('pointer_mode', 1, (X.GrabModeSync, X.GrabModeAsync)), + rq.Set('keyboard_mode', 1, (X.GrabModeSync, X.GrabModeAsync)), + rq.Pad(3), + ) + +class UngrabKey(rq.Request): + _request = rq.Struct( + rq.Opcode(34), + rq.Card8('key'), + rq.RequestLength(), + rq.Window('grab_window'), + rq.Card16('modifiers'), + rq.Pad(2), + ) + +class AllowEvents(rq.Request): + _request = rq.Struct( + rq.Opcode(35), + rq.Set('mode', 1, (X.AsyncPointer, + X.SyncPointer, + X.ReplayPointer, + X.AsyncKeyboard, + X.SyncKeyboard, + X.ReplayKeyboard, + X.AsyncBoth, + X.SyncBoth)), + rq.RequestLength(), + rq.Card32('time'), + ) + +class GrabServer(rq.Request): + _request = rq.Struct( + rq.Opcode(36), + rq.Pad(1), + rq.RequestLength(), + ) + +class UngrabServer(rq.Request): + _request = rq.Struct( + rq.Opcode(37), + rq.Pad(1), + rq.RequestLength(), + ) + +class QueryPointer(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(38), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window') + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('same_screen'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('root'), + rq.Window('child', (X.NONE, )), + rq.Int16('root_x'), + rq.Int16('root_y'), + rq.Int16('win_x'), + rq.Int16('win_y'), + rq.Card16('mask'), + rq.Pad(6), + ) + +class GetMotionEvents(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(39), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window'), + rq.Card32('start'), + rq.Card32('stop'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('events', 4), + rq.Pad(20), + rq.List('events', structs.TimeCoord), + ) + +class TranslateCoords(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(40), + rq.Pad(1), + rq.RequestLength(), + rq.Window('src_wid'), + rq.Window('dst_wid'), + rq.Int16('src_x'), + rq.Int16('src_y'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('same_screen'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('child', (X.NONE, )), + rq.Int16('x'), + rq.Int16('y'), + rq.Pad(16), + ) + +class WarpPointer(rq.Request): + _request = rq.Struct( + rq.Opcode(41), + rq.Pad(1), + rq.RequestLength(), + rq.Window('src_window'), + rq.Window('dst_window'), + rq.Int16('src_x'), + rq.Int16('src_y'), + rq.Card16('src_width'), + rq.Card16('src_height'), + rq.Int16('dst_x'), + rq.Int16('dst_y'), + ) + +class SetInputFocus(rq.Request): + _request = rq.Struct( + rq.Opcode(42), + rq.Set('revert_to', 1, (X.RevertToNone, X.RevertToPointerRoot, + X.RevertToParent)), + rq.RequestLength(), + rq.Window('focus'), + rq.Card32('time'), + ) + +class GetInputFocus(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(43), + rq.Pad(1), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('revert_to'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('focus', (X.NONE, X.PointerRoot)), + rq.Pad(20), + ) + +class QueryKeymap(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(44), + rq.Pad(1), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.FixedList('map', 32, rq.Card8Obj), + ) + + +class OpenFont(rq.Request): + _request = rq.Struct( + rq.Opcode(45), + rq.Pad(1), + rq.RequestLength(), + rq.Font('fid'), + rq.LengthOf('name', 2), + rq.Pad(2), + rq.String8('name'), + ) + +class CloseFont(rq.Request): + _request = rq.Struct( + rq.Opcode(46), + rq.Pad(1), + rq.RequestLength(), + rq.Font('font') + ) + +class QueryFont(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(47), + rq.Pad(1), + rq.RequestLength(), + rq.Fontable('font') + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Object('min_bounds', structs.CharInfo), + rq.Pad(4), + rq.Object('max_bounds', structs.CharInfo), + rq.Pad(4), + rq.Card16('min_char_or_byte2'), + rq.Card16('max_char_or_byte2'), + rq.Card16('default_char'), + rq.LengthOf('properties', 2), + rq.Card8('draw_direction'), + rq.Card8('min_byte1'), + rq.Card8('max_byte1'), + rq.Card8('all_chars_exist'), + rq.Int16('font_ascent'), + rq.Int16('font_descent'), + rq.LengthOf('char_infos', 4), + rq.List('properties', structs.FontProp), + rq.List('char_infos', structs.CharInfo), + ) + +class QueryTextExtents(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(48), + rq.OddLength('string'), + rq.RequestLength(), + rq.Fontable('font'), + rq.String16('string'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('draw_direction'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Int16('font_ascent'), + rq.Int16('font_descent'), + rq.Int16('overall_ascent'), + rq.Int16('overall_descent'), + rq.Int32('overall_width'), + rq.Int32('overall_left'), + rq.Int32('overall_right'), + rq.Pad(4), + ) + +class ListFonts(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(49), + rq.Pad(1), + rq.RequestLength(), + rq.Card16('max_names'), + rq.LengthOf('pattern', 2), + rq.String8('pattern'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('fonts', 2), + rq.Pad(22), + rq.List('fonts', rq.Str), + ) + + +class ListFontsWithInfo(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(50), + rq.Pad(1), + rq.RequestLength(), + rq.Card16('max_names'), + rq.LengthOf('pattern', 2), + rq.String8('pattern'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.LengthOf('name', 1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Object('min_bounds', structs.CharInfo), + rq.Pad(4), + rq.Object('max_bounds', structs.CharInfo), + rq.Pad(4), + rq.Card16('min_char_or_byte2'), + rq.Card16('max_char_or_byte2'), + rq.Card16('default_char'), + rq.LengthOf('properties', 2), + rq.Card8('draw_direction'), + rq.Card8('min_byte1'), + rq.Card8('max_byte1'), + rq.Card8('all_chars_exist'), + rq.Int16('font_ascent'), + rq.Int16('font_descent'), + rq.Card32('replies_hint'), + rq.List('properties', structs.FontProp), + rq.String8('name'), + ) + + + # Somebody must have smoked some really wicked weed when they + # defined the ListFontsWithInfo request: + # The server sends a reply for _each_ matching font... + # It then sends a special reply (name length == 0) to indicate + # that there are no more fonts in the reply. + + # This means that we have to do some special parsing to see if + # we have got the end-of-reply reply. If we haven't, we + # have to reinsert the request in the front of the + # display.sent_request queue to catch the next response. + + # Bastards. + + def __init__(self, *args, **keys): + self._fonts = [] + rq.ReplyRequest.__init__(self, *args, **keys) + + def _parse_response(self, data): + + if ord(data[1]) == 0: + self._response_lock.acquire() + self._data = self._fonts + del self._fonts + self._response_lock.release() + return + + r, d = self._reply.parse_binary(data) + self._fonts.append(r) + + self._display.sent_requests.insert(0, self) + + + # Override the default __getattr__, since it isn't usable for + # the list reply. Instead provide a __getitem__ and a __len__. + + def __getattr__(self, attr): + raise AttributeError(attr) + + def __getitem__(self, item): + return self._data[item] + + def __len__(self): + return len(self._data) + + +class SetFontPath(rq.Request): + _request = rq.Struct( + rq.Opcode(51), + rq.Pad(1), + rq.RequestLength(), + rq.LengthOf('path', 2), + rq.Pad(2), + rq.List('path', rq.Str), + ) + +class GetFontPath(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(52), + rq.Pad(1), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('paths', 2), + rq.Pad(22), + rq.List('paths', rq.Str), + ) + +class CreatePixmap(rq.Request): + _request = rq.Struct( + rq.Opcode(53), + rq.Card8('depth'), + rq.RequestLength(), + rq.Pixmap('pid'), + rq.Drawable('drawable'), + rq.Card16('width'), + rq.Card16('height'), + ) + +class FreePixmap(rq.Request): + _request = rq.Struct( + rq.Opcode(54), + rq.Pad(1), + rq.RequestLength(), + rq.Pixmap('pixmap') + ) + +class CreateGC(rq.Request): + _request = rq.Struct( + rq.Opcode(55), + rq.Pad(1), + rq.RequestLength(), + rq.GC('cid'), + rq.Drawable('drawable'), + structs.GCValues('attrs'), + ) + +class ChangeGC(rq.Request): + _request = rq.Struct( + rq.Opcode(56), + rq.Pad(1), + rq.RequestLength(), + rq.GC('gc'), + structs.GCValues('attrs'), + ) + +class CopyGC(rq.Request): + _request = rq.Struct( + rq.Opcode(57), + rq.Pad(1), + rq.RequestLength(), + rq.GC('src_gc'), + rq.GC('dst_gc'), + rq.Card32('mask'), + ) + +class SetDashes(rq.Request): + _request = rq.Struct( + rq.Opcode(58), + rq.Pad(1), + rq.RequestLength(), + rq.GC('gc'), + rq.Card16('dash_offset'), + rq.LengthOf('dashes', 2), + rq.List('dashes', rq.Card8Obj), + ) + +class SetClipRectangles(rq.Request): + _request = rq.Struct( + rq.Opcode(59), + rq.Set('ordering', 1, (X.Unsorted, X.YSorted, X.YXSorted, X.YXBanded)), + rq.RequestLength(), + rq.GC('gc'), + rq.Int16('x_origin'), + rq.Int16('y_origin'), + rq.List('rectangles', structs.Rectangle), + ) + +class FreeGC(rq.Request): + _request = rq.Struct( + rq.Opcode(60), + rq.Pad(1), + rq.RequestLength(), + rq.GC('gc') + ) + +class ClearArea(rq.Request): + _request = rq.Struct( + rq.Opcode(61), + rq.Bool('exposures'), + rq.RequestLength(), + rq.Window('window'), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + ) + +class CopyArea(rq.Request): + _request = rq.Struct( + rq.Opcode(62), + rq.Pad(1), + rq.RequestLength(), + rq.Drawable('src_drawable'), + rq.Drawable('dst_drawable'), + rq.GC('gc'), + rq.Int16('src_x'), + rq.Int16('src_y'), + rq.Int16('dst_x'), + rq.Int16('dst_y'), + rq.Card16('width'), + rq.Card16('height'), + ) + +class CopyPlane(rq.Request): + _request = rq.Struct( + rq.Opcode(63), + rq.Pad(1), + rq.RequestLength(), + rq.Drawable('src_drawable'), + rq.Drawable('dst_drawable'), + rq.GC('gc'), + rq.Int16('src_x'), + rq.Int16('src_y'), + rq.Int16('dst_x'), + rq.Int16('dst_y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card32('bit_plane'), + ) + +class PolyPoint(rq.Request): + _request = rq.Struct( + rq.Opcode(64), + rq.Set('coord_mode', 1, (X.CoordModeOrigin, X.CoordModePrevious)), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.List('points', structs.Point), + ) + +class PolyLine(rq.Request): + _request = rq.Struct( + rq.Opcode(65), + rq.Set('coord_mode', 1, (X.CoordModeOrigin, X.CoordModePrevious)), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.List('points', structs.Point), + ) + + +class PolySegment(rq.Request): + _request = rq.Struct( + rq.Opcode(66), + rq.Pad(1), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.List('segments', structs.Segment), + ) + + +class PolyRectangle(rq.Request): + _request = rq.Struct( + rq.Opcode(67), + rq.Pad(1), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.List('rectangles', structs.Rectangle), + ) + +class PolyArc(rq.Request): + _request = rq.Struct( + rq.Opcode(68), + rq.Pad(1), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.List('arcs', structs.Arc), + ) + +class FillPoly(rq.Request): + _request = rq.Struct( + rq.Opcode(69), + rq.Pad(1), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.Set('shape', 1, (X.Complex, X.Nonconvex, X.Convex)), + rq.Set('coord_mode', 1, (X.CoordModeOrigin, X.CoordModePrevious)), + rq.Pad(2), + rq.List('points', structs.Point), + ) + +class PolyFillRectangle(rq.Request): + _request = rq.Struct( + rq.Opcode(70), + rq.Pad(1), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.List('rectangles', structs.Rectangle), + ) + +class PolyFillArc(rq.Request): + _request = rq.Struct( + rq.Opcode(71), + rq.Pad(1), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.List('arcs', structs.Arc), + ) + +class PutImage(rq.Request): + _request = rq.Struct( + rq.Opcode(72), + rq.Set('format', 1, (X.XYBitmap, X.XYPixmap, X.ZPixmap)), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.Card16('width'), + rq.Card16('height'), + rq.Int16('dst_x'), + rq.Int16('dst_y'), + rq.Card8('left_pad'), + rq.Card8('depth'), + rq.Pad(2), + rq.Binary('data'), + ) + +class GetImage(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(73), + rq.Set('format', 1, (X.XYPixmap, X.ZPixmap)), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card32('plane_mask'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('depth'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('visual'), + rq.Pad(20), + rq.Binary('data'), + ) + +class PolyText8(rq.Request): + _request = rq.Struct( + rq.Opcode(74), + rq.Pad(1), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.Int16('x'), + rq.Int16('y'), + rq.TextElements8('items'), + ) + +class PolyText16(rq.Request): + _request = rq.Struct( + rq.Opcode(75), + rq.Pad(1), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.Int16('x'), + rq.Int16('y'), + rq.TextElements16('items'), + ) + +class ImageText8(rq.Request): + _request = rq.Struct( + rq.Opcode(76), + rq.LengthOf('string', 1), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.Int16('x'), + rq.Int16('y'), + rq.String8('string'), + ) + +class ImageText16(rq.Request): + _request = rq.Struct( + rq.Opcode(77), + rq.LengthOf('string', 1), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.GC('gc'), + rq.Int16('x'), + rq.Int16('y'), + rq.String16('string'), + ) + +class CreateColormap(rq.Request): + _request = rq.Struct( + rq.Opcode(78), + rq.Set('alloc', 1, (X.AllocNone, X.AllocAll)), + rq.RequestLength(), + rq.Colormap('mid'), + rq.Window('window'), + rq.Card32('visual'), + ) + +class FreeColormap(rq.Request): + _request = rq.Struct( + rq.Opcode(79), + rq.Pad(1), + rq.RequestLength(), + rq.Colormap('cmap') + ) + +class CopyColormapAndFree(rq.Request): + _request = rq.Struct( + rq.Opcode(80), + rq.Pad(1), + rq.RequestLength(), + rq.Colormap('mid'), + rq.Colormap('src_cmap'), + ) + +class InstallColormap(rq.Request): + _request = rq.Struct( + rq.Opcode(81), + rq.Pad(1), + rq.RequestLength(), + rq.Colormap('cmap') + ) + +class UninstallColormap(rq.Request): + _request = rq.Struct( + rq.Opcode(82), + rq.Pad(1), + rq.RequestLength(), + rq.Colormap('cmap') + ) + +class ListInstalledColormaps(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(83), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window') + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('cmaps', 2), + rq.Pad(22), + rq.List('cmaps', rq.ColormapObj), + ) + +class AllocColor(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(84), + rq.Pad(1), + rq.RequestLength(), + rq.Colormap('cmap'), + rq.Card16('red'), + rq.Card16('green'), + rq.Card16('blue'), + rq.Pad(2), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('red'), + rq.Card16('green'), + rq.Card16('blue'), + rq.Pad(2), + rq.Card32('pixel'), + rq.Pad(12), + ) + +class AllocNamedColor(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(85), + rq.Pad(1), + rq.RequestLength(), + rq.Colormap('cmap'), + rq.LengthOf('name', 2), + rq.Pad(2), + rq.String8('name'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('pixel'), + rq.Card16('exact_red'), + rq.Card16('exact_green'), + rq.Card16('exact_blue'), + rq.Card16('screen_red'), + rq.Card16('screen_green'), + rq.Card16('screen_blue'), + rq.Pad(8), + ) + +class AllocColorCells(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(86), + rq.Bool('contiguous'), + rq.RequestLength(), + rq.Colormap('cmap'), + rq.Card16('colors'), + rq.Card16('planes'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('pixels', 2), + rq.LengthOf('masks', 2), + rq.Pad(20), + rq.List('pixels', rq.Card32Obj), + rq.List('masks', rq.Card32Obj), + ) + +class AllocColorPlanes(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(87), + rq.Bool('contiguous'), + rq.RequestLength(), + rq.Colormap('cmap'), + rq.Card16('colors'), + rq.Card16('red'), + rq.Card16('green'), + rq.Card16('blue'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('pixels', 2), + rq.Pad(2), + rq.Card32('red_mask'), + rq.Card32('green_mask'), + rq.Card32('blue_mask'), + rq.Pad(8), + rq.List('pixels', rq.Card32Obj), + ) + +class FreeColors(rq.Request): + _request = rq.Struct( + rq.Opcode(88), + rq.Pad(1), + rq.RequestLength(), + rq.Colormap('cmap'), + rq.Card32('plane_mask'), + rq.List('pixels', rq.Card32Obj), + ) + +class StoreColors(rq.Request): + _request = rq.Struct( + rq.Opcode(89), + rq.Pad(1), + rq.RequestLength(), + rq.Colormap('cmap'), + rq.List('items', structs.ColorItem), + ) + +class StoreNamedColor(rq.Request): + _request = rq.Struct( + rq.Opcode(90), + rq.Card8('flags'), + rq.RequestLength(), + rq.Colormap('cmap'), + rq.Card32('pixel'), + rq.LengthOf('name', 2), + rq.Pad(2), + rq.String8('name'), + ) + +class QueryColors(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(91), + rq.Pad(1), + rq.RequestLength(), + rq.Colormap('cmap'), + rq.List('pixels', rq.Card32Obj), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('colors', 2), + rq.Pad(22), + rq.List('colors', structs.RGB), + ) + +class LookupColor(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(92), + rq.Pad(1), + rq.RequestLength(), + rq.Colormap('cmap'), + rq.LengthOf('name', 2), + rq.Pad(2), + rq.String8('name'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('exact_red'), + rq.Card16('exact_green'), + rq.Card16('exact_blue'), + rq.Card16('screen_red'), + rq.Card16('screen_green'), + rq.Card16('screen_blue'), + rq.Pad(12), + ) + + +class CreateCursor(rq.Request): + _request = rq.Struct( + rq.Opcode(93), + rq.Pad(1), + rq.RequestLength(), + rq.Cursor('cid'), + rq.Pixmap('source'), + rq.Pixmap('mask'), + rq.Card16('fore_red'), + rq.Card16('fore_green'), + rq.Card16('fore_blue'), + rq.Card16('back_red'), + rq.Card16('back_green'), + rq.Card16('back_blue'), + rq.Card16('x'), + rq.Card16('y'), + ) + +class CreateGlyphCursor(rq.Request): + _request = rq.Struct( + rq.Opcode(94), + rq.Pad(1), + rq.RequestLength(), + rq.Cursor('cid'), + rq.Font('source'), + rq.Font('mask'), + rq.Card16('source_char'), + rq.Card16('mask_char'), + rq.Card16('fore_red'), + rq.Card16('fore_green'), + rq.Card16('fore_blue'), + rq.Card16('back_red'), + rq.Card16('back_green'), + rq.Card16('back_blue'), + ) + +class FreeCursor(rq.Request): + _request = rq.Struct( + rq.Opcode(95), + rq.Pad(1), + rq.RequestLength(), + rq.Cursor('cursor') + ) + +class RecolorCursor(rq.Request): + _request = rq.Struct( + rq.Opcode(96), + rq.Pad(1), + rq.RequestLength(), + rq.Cursor('cursor'), + rq.Card16('fore_red'), + rq.Card16('fore_green'), + rq.Card16('fore_blue'), + rq.Card16('back_red'), + rq.Card16('back_green'), + rq.Card16('back_blue'), + ) + +class QueryBestSize(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(97), + rq.Set('item_class', 1, (X.CursorShape, X.TileShape, X.StippleShape)), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.Card16('width'), + rq.Card16('height'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('width'), + rq.Card16('height'), + rq.Pad(20), + ) + +class QueryExtension(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(98), + rq.Pad(1), + rq.RequestLength(), + rq.LengthOf('name', 2), + rq.Pad(2), + rq.String8('name'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card8('present'), + rq.Card8('major_opcode'), + rq.Card8('first_event'), + rq.Card8('first_error'), + rq.Pad(20), + ) + +class ListExtensions(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(99), + rq.Pad(1), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.LengthOf('names', 1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Pad(24), + rq.List('names', rq.Str), + ) + +class ChangeKeyboardMapping(rq.Request): + _request = rq.Struct( + rq.Opcode(100), + rq.LengthOf('keysyms', 1), + rq.RequestLength(), + rq.Card8('first_keycode'), + rq.Format('keysyms', 1), + rq.Pad(2), + rq.KeyboardMapping('keysyms'), + ) + +class GetKeyboardMapping(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(101), + rq.Pad(1), + rq.RequestLength(), + rq.Card8('first_keycode'), + rq.Card8('count'), + rq.Pad(2), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Format('keysyms', 1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Pad(24), + rq.KeyboardMapping('keysyms'), + ) + + +class ChangeKeyboardControl(rq.Request): + _request = rq.Struct( + rq.Opcode(102), + rq.Pad(1), + rq.RequestLength(), + rq.ValueList( 'attrs', 4, 0, + rq.Int8('key_click_percent'), + rq.Int8('bell_percent'), + rq.Int16('bell_pitch'), + rq.Int16('bell_duration'), + rq.Card8('led'), + rq.Set('led_mode', 1, (X.LedModeOff, X.LedModeOn)), + rq.Card8('key'), + rq.Set('auto_repeat_mode', 1, (X.AutoRepeatModeOff, + X.AutoRepeatModeOn, + X.AutoRepeatModeDefault)) + ) + ) + +class GetKeyboardControl(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(103), + rq.Pad(1), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('global_auto_repeat'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('led_mask'), + rq.Card8('key_click_percent'), + rq.Card8('bell_percent'), + rq.Card16('bell_pitch'), + rq.Card16('bell_duration'), + rq.Pad(2), + rq.FixedList('auto_repeats', 32, rq.Card8Obj), + ) + +class Bell(rq.Request): + _request = rq.Struct( + rq.Opcode(104), + rq.Int8('percent'), + rq.RequestLength(), + ) + +class ChangePointerControl(rq.Request): + _request = rq.Struct( + rq.Opcode(105), + rq.Pad(1), + rq.RequestLength(), + rq.Int16('accel_num'), + rq.Int16('accel_denum'), + rq.Int16('threshold'), + rq.Bool('do_accel'), + rq.Bool('do_thresh'), + ) + +class GetPointerControl(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(106), + rq.Pad(1), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('accel_num'), + rq.Card16('accel_denom'), + rq.Card16('threshold'), + rq.Pad(18), + ) + +class SetScreenSaver(rq.Request): + _request = rq.Struct( + rq.Opcode(107), + rq.Pad(1), + rq.RequestLength(), + rq.Int16('timeout'), + rq.Int16('interval'), + rq.Set('prefer_blank', 1, (X.DontPreferBlanking, + X.PreferBlanking, + X.DefaultBlanking)), + rq.Set('allow_exposures', 1, (X.DontAllowExposures, + X.AllowExposures, + X.DefaultExposures)), + rq.Pad(2), + ) + +class GetScreenSaver(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(108), + rq.Pad(1), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('timeout'), + rq.Card16('interval'), + rq.Card8('prefer_blanking'), + rq.Card8('allow_exposures'), + rq.Pad(18), + ) + +class ChangeHosts(rq.Request): + _request = rq.Struct( + rq.Opcode(109), + rq.Set('mode', 1, (X.HostInsert, X.HostDelete)), + rq.RequestLength(), + rq.Set('host_family', 1, (X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos, + X.FamilyServerInterpreted, X.FamilyInternetV6)), + rq.Pad(1), + rq.LengthOf('host', 2), + rq.List('host', rq.Card8Obj) + ) + +class ListHosts(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(110), + rq.Pad(1), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('mode'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('hosts', 2), + rq.Pad(22), + rq.List('hosts', structs.Host), + ) + +class SetAccessControl(rq.Request): + _request = rq.Struct( + rq.Opcode(111), + rq.Set('mode', 1, (X.DisableAccess, X.EnableAccess)), + rq.RequestLength(), + ) + +class SetCloseDownMode(rq.Request): + _request = rq.Struct( + rq.Opcode(112), + rq.Set('mode', 1, (X.DestroyAll, X.RetainPermanent, X.RetainTemporary)), + rq.RequestLength(), + ) + +class KillClient(rq.Request): + _request = rq.Struct( + rq.Opcode(113), + rq.Pad(1), + rq.RequestLength(), + rq.Resource('resource') + ) + +class RotateProperties(rq.Request): + _request = rq.Struct( + rq.Opcode(114), + rq.Pad(1), + rq.RequestLength(), + rq.Window('window'), + rq.LengthOf('properties', 2), + rq.Int16('delta'), + rq.List('properties', rq.Card32Obj), + ) + +class ForceScreenSaver(rq.Request): + _request = rq.Struct( + rq.Opcode(115), + rq.Set('mode', 1, (X.ScreenSaverReset, X.ScreenSaverActive)), + rq.RequestLength(), + ) + +class SetPointerMapping(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(116), + rq.LengthOf('map', 1), + rq.RequestLength(), + rq.List('map', rq.Card8Obj), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Pad(24), + ) + +class GetPointerMapping(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(117), + rq.Pad(1), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.LengthOf('map', 1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Pad(24), + rq.List('map', rq.Card8Obj), + ) + +class SetModifierMapping(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(118), + rq.Format('keycodes', 1), + rq.RequestLength(), + rq.ModifierMapping('keycodes') + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('status'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Pad(24), + ) + +class GetModifierMapping(rq.ReplyRequest): + _request = rq.Struct( + rq.Opcode(119), + rq.Pad(1), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Format('keycodes', 1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Pad(24), + rq.ModifierMapping('keycodes') + ) + +class NoOperation(rq.Request): + _request = rq.Struct( + rq.Opcode(127), + rq.Pad(1), + rq.RequestLength(), + ) + + +major_codes = { + 1: CreateWindow, + 2: ChangeWindowAttributes, + 3: GetWindowAttributes, + 4: DestroyWindow, + 5: DestroySubWindows, + 6: ChangeSaveSet, + 7: ReparentWindow, + 8: MapWindow, + 9: MapSubwindows, + 10: UnmapWindow, + 11: UnmapSubwindows, + 12: ConfigureWindow, + 13: CirculateWindow, + 14: GetGeometry, + 15: QueryTree, + 16: InternAtom, + 17: GetAtomName, + 18: ChangeProperty, + 19: DeleteProperty, + 20: GetProperty, + 21: ListProperties, + 22: SetSelectionOwner, + 23: GetSelectionOwner, + 24: ConvertSelection, + 25: SendEvent, + 26: GrabPointer, + 27: UngrabPointer, + 28: GrabButton, + 29: UngrabButton, + 30: ChangeActivePointerGrab, + 31: GrabKeyboard, + 32: UngrabKeyboard, + 33: GrabKey, + 34: UngrabKey, + 35: AllowEvents, + 36: GrabServer, + 37: UngrabServer, + 38: QueryPointer, + 39: GetMotionEvents, + 40: TranslateCoords, + 41: WarpPointer, + 42: SetInputFocus, + 43: GetInputFocus, + 44: QueryKeymap, + 45: OpenFont, + 46: CloseFont, + 47: QueryFont, + 48: QueryTextExtents, + 49: ListFonts, + 50: ListFontsWithInfo, + 51: SetFontPath, + 52: GetFontPath, + 53: CreatePixmap, + 54: FreePixmap, + 55: CreateGC, + 56: ChangeGC, + 57: CopyGC, + 58: SetDashes, + 59: SetClipRectangles, + 60: FreeGC, + 61: ClearArea, + 62: CopyArea, + 63: CopyPlane, + 64: PolyPoint, + 65: PolyLine, + 66: PolySegment, + 67: PolyRectangle, + 68: PolyArc, + 69: FillPoly, + 70: PolyFillRectangle, + 71: PolyFillArc, + 72: PutImage, + 73: GetImage, + 74: PolyText8, + 75: PolyText16, + 76: ImageText8, + 77: ImageText16, + 78: CreateColormap, + 79: FreeColormap, + 80: CopyColormapAndFree, + 81: InstallColormap, + 82: UninstallColormap, + 83: ListInstalledColormaps, + 84: AllocColor, + 85: AllocNamedColor, + 86: AllocColorCells, + 87: AllocColorPlanes, + 88: FreeColors, + 89: StoreColors, + 90: StoreNamedColor, + 91: QueryColors, + 92: LookupColor, + 93: CreateCursor, + 94: CreateGlyphCursor, + 95: FreeCursor, + 96: RecolorCursor, + 97: QueryBestSize, + 98: QueryExtension, + 99: ListExtensions, + 100: ChangeKeyboardMapping, + 101: GetKeyboardMapping, + 102: ChangeKeyboardControl, + 103: GetKeyboardControl, + 104: Bell, + 105: ChangePointerControl, + 106: GetPointerControl, + 107: SetScreenSaver, + 108: GetScreenSaver, + 109: ChangeHosts, + 110: ListHosts, + 111: SetAccessControl, + 112: SetCloseDownMode, + 113: KillClient, + 114: RotateProperties, + 115: ForceScreenSaver, + 116: SetPointerMapping, + 117: GetPointerMapping, + 118: SetModifierMapping, + 119: GetModifierMapping, + 127: NoOperation, + } diff --git a/Xlib/protocol/rq.py b/Xlib/protocol/rq.py new file mode 100644 index 0000000..86cb2de --- /dev/null +++ b/Xlib/protocol/rq.py @@ -0,0 +1,1464 @@ +# Xlib.protocol.rq -- structure primitives for request, events and errors +# +# Copyright (C) 2000-2002 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +# Standard modules +import sys +import traceback +import struct +from array import array +import types + +# Python 2/3 compatibility. +from six import PY3, binary_type, byte2int, indexbytes, iterbytes + +# Xlib modules +from .. import X +from ..support import lock + + +def decode_string(bs): + return bs.decode('latin1') + +if PY3: + def encode_array(a): + return a.tobytes() +else: + def encode_array(a): + return a.tostring() + + +class BadDataError(Exception): pass + +# These are struct codes, we know their byte sizes + +signed_codes = { 1: 'b', 2: 'h', 4: 'l' } +unsigned_codes = { 1: 'B', 2: 'H', 4: 'L' } + + +# Unfortunately, we don't know the array sizes of B, H and L, since +# these use the underlying architecture's size for a char, short and +# long. Therefore we probe for their sizes, and additionally create +# a mapping that translates from struct codes to array codes. +# +# Bleah. + +array_unsigned_codes = { } +struct_to_array_codes = { } + +for c in 'bhil': + size = array(c).itemsize + array_unsigned_codes[size] = c.upper() + try: + struct_to_array_codes[signed_codes[size]] = c + struct_to_array_codes[unsigned_codes[size]] = c.upper() + except KeyError: + pass + +# print array_unsigned_codes, struct_to_array_codes + + +class Field(object): + """Field objects represent the data fields of a Struct. + + Field objects must have the following attributes: + + name -- the field name, or None + structcode -- the struct codes representing this field + structvalues -- the number of values encodes by structcode + + Additionally, these attributes should either be None or real methods: + + check_value -- check a value before it is converted to binary + parse_value -- parse a value after it has been converted from binary + + If one of these attributes are None, no check or additional + parsings will be done one values when converting to or from binary + form. Otherwise, the methods should have the following behaviour: + + newval = check_value(val) + Check that VAL is legal when converting to binary form. The + value can also be converted to another Python value. In any + case, return the possibly new value. NEWVAL should be a + single Python value if structvalues is 1, a tuple of + structvalues elements otherwise. + + newval = parse_value(val, display) + VAL is an unpacked Python value, which now can be further + refined. DISPLAY is the current Display object. Return the + new value. VAL will be a single value if structvalues is 1, + a tuple of structvalues elements otherwise. + + If `structcode' is None the Field must have the method + f.parse_binary_value() instead. See its documentation string for + details. + """ + name = None + default = None + + structcode = None + structvalues = 0 + + check_value = None + parse_value = None + + keyword_args = 0 + + def __init__(self): + pass + + def parse_binary_value(self, data, display, length, format): + """value, remaindata = f.parse_binary_value(data, display, length, format) + + Decode a value for this field from the binary string DATA. + If there are a LengthField and/or a FormatField connected to this + field, their values will be LENGTH and FORMAT, respectively. If + there are no such fields the parameters will be None. + + DISPLAY is the display involved, which is really only used by + the Resource fields. + + The decoded value is returned as VALUE, and the remaining part + of DATA shold be returned as REMAINDATA. + """ + raise RuntimeError('Neither structcode or parse_binary_value ' \ + 'provided for {0}'.format(self)) + + +class Pad(Field): + def __init__(self, size): + self.size = size + self.value = b'\0' * size + self.structcode = '{0}x'.format(size) + self.structvalues = 0 + + +class ConstantField(Field): + def __init__(self, value): + self.value = value + + +class Opcode(ConstantField): + structcode = 'B' + structvalues = 1 + +class ReplyCode(ConstantField): + structcode = 'B' + structvalues = 1 + + def __init__(self): + self.value = 1 + +class LengthField(Field): + """A LengthField stores the length of some other Field whose size + may vary, e.g. List and String8. + + Its name should be the same as the name of the field whose size + it stores. The other_fields attribute can be used to specify the + names of other fields whose sizes are stored by this field, so + a single length field can set the length of multiple fields. + + The lf.get_binary_value() method of LengthFields is not used, instead + a lf.get_binary_length() should be provided. + + Unless LengthField.get_binary_length() is overridden in child classes, + there should also be a lf.calc_length(). + """ + structcode = 'L' + structvalues = 1 + other_fields = None + + def calc_length(self, length): + """newlen = lf.calc_length(length) + + Return a new length NEWLEN based on the provided LENGTH. + """ + + return length + + +class TotalLengthField(LengthField): + pass + +class RequestLength(TotalLengthField): + structcode = 'H' + structvalues = 1 + + def calc_length(self, length): + return length // 4 + +class ReplyLength(TotalLengthField): + structcode = 'L' + structvalues = 1 + + def calc_length(self, length): + return (length - 32) // 4 + + +class LengthOf(LengthField): + def __init__(self, name, size): + if isinstance(name, (list, tuple)): + self.name = name[0] + self.other_fields = name[1:] + else: + self.name = name + self.structcode = unsigned_codes[size] + + +class OddLength(LengthField): + structcode = 'B' + structvalues = 1 + + def __init__(self, name): + self.name = name + + def calc_length(self, length): + return length % 2 + + def parse_value(self, value, display): + if value == 0: + return 'even' + else: + return 'odd' + + +class FormatField(Field): + """A FormatField encodes the format of some other field, in a manner + similar to LengthFields. + + The ff.get_binary_value() method is not used, replaced by + ff.get_binary_format(). + """ + + structvalues = 1 + + def __init__(self, name, size): + self.name = name + self.structcode = unsigned_codes[size] + +Format = FormatField + + +class ValueField(Field): + def __init__(self, name, default = None): + self.name = name + self.default = default + + +class Int8(ValueField): + structcode = 'b' + structvalues = 1 + +class Int16(ValueField): + structcode = 'h' + structvalues = 1 + +class Int32(ValueField): + structcode = 'l' + structvalues = 1 + +class Card8(ValueField): + structcode = 'B' + structvalues = 1 + +class Card16(ValueField): + structcode = 'H' + structvalues = 1 + +class Card32(ValueField): + structcode = 'L' + structvalues = 1 + + +class Resource(Card32): + cast_function = '__resource__' + class_name = 'resource' + + def __init__(self, name, codes = (), default = None): + Card32.__init__(self, name, default) + self.codes = codes + + def check_value(self, value): + if hasattr(value, self.cast_function): + return getattr(value, self.cast_function)() + else: + return value + + def parse_value(self, value, display): + # if not display: + # return value + if value in self.codes: + return value + + c = display.get_resource_class(self.class_name) + if c: + return c(display, value) + else: + return value + +class Window(Resource): + cast_function = '__window__' + class_name = 'window' + +class Pixmap(Resource): + cast_function = '__pixmap__' + class_name = 'pixmap' + +class Drawable(Resource): + cast_function = '__drawable__' + class_name = 'drawable' + +class Fontable(Resource): + cast_function = '__fontable__' + class_name = 'fontable' + +class Font(Resource): + cast_function = '__font__' + class_name = 'font' + +class GC(Resource): + cast_function = '__gc__' + class_name = 'gc' + +class Colormap(Resource): + cast_function = '__colormap__' + class_name = 'colormap' + +class Cursor(Resource): + cast_function = '__cursor__' + class_name = 'cursor' + + +class Bool(ValueField): + structvalues = 1 + structcode = 'B' + + def check_value(self, value): + return not not value + +class Set(ValueField): + structvalues = 1 + + def __init__(self, name, size, values, default = None): + ValueField.__init__(self, name, default) + self.structcode = unsigned_codes[size] + self.values = values + + def check_value(self, val): + if val not in self.values: + raise ValueError('field %s: argument %s not in %s' + % (self.name, val, self.values)) + + return val + +class Gravity(Set): + def __init__(self, name): + Set.__init__(self, name, 1, (X.ForgetGravity, X.StaticGravity, + X.NorthWestGravity, X.NorthGravity, + X.NorthEastGravity, X.WestGravity, + X.CenterGravity, X.EastGravity, + X.SouthWestGravity, X.SouthGravity, + X.SouthEastGravity)) + + +class FixedBinary(ValueField): + structvalues = 1 + + def __init__(self, name, size): + ValueField.__init__(self, name) + self.structcode = '{0}s'.format(size) + + +class Binary(ValueField): + structcode = None + + def __init__(self, name, pad = 1): + ValueField.__init__(self, name) + self.pad = pad + + def pack_value(self, val): + val_bytes = val + slen = len(val_bytes) + + if self.pad: + return val_bytes + b'\0' * ((4 - slen % 4) % 4), slen, None + else: + return val_bytes, slen, None + + def parse_binary_value(self, data, display, length, format): + if length is None: + return data, b'' + + if self.pad: + slen = length + ((4 - length % 4) % 4) + else: + slen = length + + return data[:length], data[slen:] + + +class String8(ValueField): + structcode = None + + def __init__(self, name, pad = 1): + ValueField.__init__(self, name) + self.pad = pad + + def pack_value(self, val): + if isinstance(val, bytes): + val_bytes = val + else: + val_bytes = val.encode() + slen = len(val_bytes) + + if self.pad: + return val_bytes + b'\0' * ((4 - slen % 4) % 4), slen, None + else: + return val_bytes, slen, None + + def parse_binary_value(self, data, display, length, format): + if length is None: + return decode_string(data), b'' + + if self.pad: + slen = length + ((4 - length % 4) % 4) + else: + slen = length + + data_str = decode_string(data[:length]) + + return data_str, data[slen:] + + +class String16(ValueField): + structcode = None + + def __init__(self, name, pad = 1): + ValueField.__init__(self, name) + self.pad = pad + + def pack_value(self, val): + """Convert 8-byte string into 16-byte list""" + if isinstance(val, bytes): + val = list(iterbytes(val)) + + slen = len(val) + + if self.pad: + pad = b'\0\0' * (slen % 2) + else: + pad = b'' + + return struct.pack('>' + 'H' * slen, *val) + pad, slen, None + + def parse_binary_value(self, data, display, length, format): + if length == 'odd': + length = len(data) // 2 - 1 + elif length == 'even': + length = len(data) // 2 + + if self.pad: + slen = length + (length % 2) + else: + slen = length + + return struct.unpack('>' + 'H' * length, data[:length * 2]), data[slen * 2:] + + + +class List(ValueField): + """The List, FixedList and Object fields store compound data objects. + The type of data objects must be provided as an object with the + following attributes and methods: + + ... + + """ + + structcode = None + + def __init__(self, name, type, pad = 1): + ValueField.__init__(self, name) + self.type = type + self.pad = pad + + def parse_binary_value(self, data, display, length, format): + if length is None: + ret = [] + if self.type.structcode is None: + while data: + val, data = self.type.parse_binary(data, display) + ret.append(val) + else: + scode = '=' + self.type.structcode + slen = struct.calcsize(scode) + pos = 0 + while pos + slen <= len(data): + v = struct.unpack(scode, data[pos: pos + slen]) + + if self.type.structvalues == 1: + v = v[0] + + if self.type.parse_value is None: + ret.append(v) + else: + ret.append(self.type.parse_value(v, display)) + + pos = pos + slen + + data = data[pos:] + + else: + ret = [None] * int(length) + + if self.type.structcode is None: + for i in range(0, length): + ret[i], data = self.type.parse_binary(data, display) + else: + scode = '=' + self.type.structcode + slen = struct.calcsize(scode) + pos = 0 + for i in range(0, length): + v = struct.unpack(scode, data[pos: pos + slen]) + + if self.type.structvalues == 1: + v = v[0] + + if self.type.parse_value is None: + ret[i] = v + else: + ret[i] = self.type.parse_value(v, display) + + pos = pos + slen + + data = data[pos:] + + if self.pad: + data = data[len(data) % 4:] + + return ret, data + + def pack_value(self, val): + # Single-char values, we'll assume that means integer lists. + if self.type.structcode and len(self.type.structcode) == 1: + if self.type.check_value is not None: + val = [self.type.check_value(v) for v in val] + a = array(struct_to_array_codes[self.type.structcode], val) + data = encode_array(a) + else: + data = [] + for v in val: + data.append(self.type.pack_value(v)) + + data = b''.join(data) + + if self.pad: + dlen = len(data) + data = data + b'\0' * ((4 - dlen % 4) % 4) + + return data, len(val), None + + +class FixedList(List): + def __init__(self, name, size, type, pad = 1): + List.__init__(self, name, type, pad) + self.size = size + + def parse_binary_value(self, data, display, length, format): + return List.parse_binary_value(self, data, display, self.size, format) + + def pack_value(self, val): + if len(val) != self.size: + raise BadDataError('length mismatch for FixedList %s' % self.name) + return List.pack_value(self, val) + + +class Object(ValueField): + def __init__(self, name, type, default = None): + ValueField.__init__(self, name, default) + self.type = type + self.structcode = self.type.structcode + self.structvalues = self.type.structvalues + + def parse_binary_value(self, data, display, length, format): + return self.type.parse_binary(data, display) + + def parse_value(self, val, display): + return self.type.parse_value(val, display) + + def pack_value(self, val): + return self.type.pack_value(val) + + def check_value(self, val): + if isinstance(val, tuple): + vals = [] + i = 0 + for f in self.type.fields: + if f.name: + if f.check_value is None: + v = val[i] + else: + v = f.check_value(val[i]) + if f.structvalues == 1: + vals.append(v) + else: + vals.extend(v) + i = i + 1 + return vals + + if isinstance(val, dict): + data = val + elif isinstance(val, DictWrapper): + data = val._data + else: + raise TypeError('Object value must be tuple, dictionary or DictWrapper: %s' % val) + + vals = [] + for f in self.type.fields: + if f.name: + if f.check_value is None: + v = data[f.name] + else: + v = f.check_value(data[f.name]) + if f.structvalues == 1: + vals.append(v) + else: + vals.extend(v) + + return vals + + +class PropertyData(ValueField): + structcode = None + + def parse_binary_value(self, data, display, length, format): + if length is None: + length = len(data) // (format // 8) + else: + length = int(length) + + if format == 0: + ret = None + + elif format == 8: + ret = (8, data[:length]) + data = data[length + ((4 - length % 4) % 4):] + + elif format == 16: + ret = (16, array(array_unsigned_codes[2], data[:2 * length])) + data = data[2 * (length + length % 2):] + + elif format == 32: + ret = (32, array(array_unsigned_codes[4], data[:4 * length])) + data = data[4 * length:] + + return ret, data + + def pack_value(self, value): + fmt, val = value + + if fmt not in (8, 16, 32): + raise BadDataError('Invalid property data format {0}'.format(fmt)) + + if isinstance(val, binary_type): + size = fmt // 8 + vlen = len(val) + if vlen % size: + vlen = vlen - vlen % size + data = val[:vlen] + else: + data = val + + dlen = vlen // size + + else: + if isinstance(val, tuple): + val = list(val) + + size = fmt // 8 + a = array(array_unsigned_codes[size], val) + data = encode_array(a) + dlen = len(val) + + dl = len(data) + data = data + b'\0' * ((4 - dl % 4) % 4) + + return data, dlen, fmt + + +class FixedPropertyData(PropertyData): + def __init__(self, name, size): + PropertyData.__init__(self, name) + self.size = size + + def parse_binary_value(self, data, display, length, format): + return PropertyData.parse_binary_value(self, data, display, + self.size // (format // 8), format) + + def pack_value(self, value): + data, dlen, fmt = PropertyData.pack_value(self, value) + + if len(data) != self.size: + raise BadDataError('Wrong data length for FixedPropertyData: %s' + % (value, )) + + return data, dlen, fmt + + +class ValueList(Field): + structcode = None + keyword_args = 1 + default = 'usekeywords' + + def __init__(self, name, mask, pad, *fields): + self.name = name + self.maskcode = '={0}{1}x'.format(unsigned_codes[mask], pad).encode() + self.maskcodelen = struct.calcsize(self.maskcode) + self.fields = [] + + flag = 1 + for f in fields: + if f.name: + self.fields.append((f, flag)) + flag = flag << 1 + + def pack_value(self, arg, keys): + mask = 0 + data = b'' + + if arg == self.default: + arg = keys + + for field, flag in self.fields: + if field.name in arg: + mask = mask | flag + + val = arg[field.name] + if field.check_value is not None: + val = field.check_value(val) + + d = struct.pack('=' + field.structcode, val) + data = data + d + b'\0' * (4 - len(d)) + + return struct.pack(self.maskcode, mask) + data, None, None + + def parse_binary_value(self, data, display, length, format): + r = {} + + mask = int(struct.unpack(self.maskcode, data[:self.maskcodelen])[0]) + data = data[self.maskcodelen:] + + for field, flag in self.fields: + if mask & flag: + if field.structcode: + vals = struct.unpack('=' + field.structcode, + data[:struct.calcsize('=' + field.structcode)]) + if field.structvalues == 1: + vals = vals[0] + + if field.parse_value is not None: + vals = field.parse_value(vals, display) + + else: + vals, d = field.parse_binary_value(data[:4], display, None, None) + + r[field.name] = vals + data = data[4:] + + return DictWrapper(r), data + + +class KeyboardMapping(ValueField): + structcode = None + + def parse_binary_value(self, data, display, length, format): + if length is None: + dlen = len(data) + else: + dlen = 4 * length * format + + a = array(array_unsigned_codes[4], bytes(data[:dlen])) + + ret = [] + for i in range(0, len(a), format): + ret.append(a[i : i + format]) + + return ret, data[dlen:] + + def pack_value(self, value): + keycodes = 0 + for v in value: + keycodes = max(keycodes, len(v)) + + a = array(array_unsigned_codes[4]) + + for v in value: + for k in v: + a.append(k) + for i in range(len(v), keycodes): + a.append(X.NoSymbol) + + return encode_array(a), len(value), keycodes + + +class ModifierMapping(ValueField): + structcode = None + + def parse_binary_value(self, data, display, length, format): + a = array(array_unsigned_codes[1], data[:8 * format]) + + ret = [] + for i in range(0, 8): + ret.append(a[i * format : (i + 1) * format]) + + return ret, data[8 * format:] + + def pack_value(self, value): + if len(value) != 8: + raise BadDataError('ModifierMapping list should have eight elements') + + keycodes = 0 + for v in value: + keycodes = max(keycodes, len(v)) + + a = array(array_unsigned_codes[1]) + + for v in value: + for k in v: + a.append(k) + for i in range(len(v), keycodes): + a.append(0) + + return encode_array(a), len(value), keycodes + +class EventField(ValueField): + structcode = None + + def pack_value(self, value): + if not isinstance(value, Event): + raise BadDataError('%s is not an Event for field %s' % (value, self.name)) + + return value._binary, None, None + + def parse_binary_value(self, data, display, length, format): + from . import event + + estruct = display.event_classes.get(byte2int(data) & 0x7f, event.AnyEvent) + if type(estruct) == dict: + # this etype refers to a set of sub-events with individual subcodes + estruct = estruct[indexbytes(data, 1)] + + return estruct(display = display, binarydata = data[:32]), data[32:] + + +# +# Objects usable for List and FixedList fields. +# Struct is also usable. +# + +class ScalarObj(object): + def __init__(self, code): + self.structcode = code + self.structvalues = 1 + self.parse_value = None + self.check_value = None + +Card8Obj = ScalarObj('B') +Card16Obj = ScalarObj('H') +Card32Obj = ScalarObj('L') + +class ResourceObj(object): + structcode = 'L' + structvalues = 1 + + def __init__(self, class_name): + self.class_name = class_name + self.check_value = None + + def parse_value(self, value, display): + # if not display: + # return value + c = display.get_resource_class(self.class_name) + if c: + return c(display, value) + else: + return value + +WindowObj = ResourceObj('window') +ColormapObj = ResourceObj('colormap') + +class StrClass(object): + structcode = None + + def pack_value(self, val): + return (chr(len(val)) + val).encode() + + def parse_binary(self, data, display): + slen = byte2int(data) + 1 + return decode_string(data[1:slen]), data[slen:] + +Str = StrClass() + + +class Struct(object): + + """Struct objects represents a binary data structure. It can + contain both fields with static and dynamic sizes. However, all + static fields must appear before all dynamic fields. + + Fields are represented by various subclasses of the abstract base + class Field. The fields of a structure are given as arguments + when instantiating a Struct object. + + Struct objects have two public methods: + + to_binary() -- build a binary representation of the structure + with the values given as arguments + parse_binary() -- convert a binary (string) representation into + a Python dictionary or object. + + These functions will be generated dynamically for each Struct + object to make conversion as fast as possible. They are + generated the first time the methods are called. + """ + + def __init__(self, *fields): + self.fields = fields + + # Structures for to_binary, parse_value and parse_binary + self.static_codes = '=' + self.static_values = 0 + self.static_fields = [] + self.static_size = None + self.var_fields = [] + + for f in self.fields: + # Append structcode if there is one and we haven't + # got any varsize fields yet. + if f.structcode is not None: + assert not self.var_fields + + self.static_codes = self.static_codes + f.structcode + + # Only store fields with values + if f.structvalues > 0: + self.static_fields.append(f) + self.static_values = self.static_values + f.structvalues + + # If we have got one varsize field, all the rest must + # also be varsize fields. + else: + self.var_fields.append(f) + + self.static_size = struct.calcsize(self.static_codes) + if self.var_fields: + self.structcode = None + self.structvalues = 0 + else: + self.structcode = self.static_codes[1:] + self.structvalues = self.static_values + + + # These functions get called only once, as they will override + # themselves with dynamically created functions in the Struct + # object + + def to_binary(self, *varargs, **keys): + """data = s.to_binary(...) + + Convert Python values into the binary representation. The + arguments will be all value fields with names, in the order + given when the Struct object was instantiated. With one + exception: fields with default arguments will be last. + + Returns the binary representation as the string DATA. + """ + # Emulate Python function argument handling with our field names + names = [f.name for f in self.fields \ + if isinstance(f, ValueField) and f.name] + field_args = dict(zip(names, varargs)) + if set(field_args).intersection(keys): + dupes = ", ".join(set(field_args).intersection(keys)) + raise TypeError("{0} arguments were passed both positionally and by keyword".format(dupes)) + field_args.update(keys) + for f in self.fields: + if f.name and (f.name not in field_args): + if f.default is None: + raise TypeError("Missing required argument {0}".format(f.name)) + field_args[f.name] = f.default + # /argument handling + + # First pack all varfields so their lengths and formats are + # available when we pack their static LengthFields and + # FormatFields + + total_length = self.static_size + var_vals = {} + lengths = {} + formats = {} + + for f in self.var_fields: + if f.keyword_args: + v, l, fm = f.pack_value(field_args[f.name], keys) + else: + v, l, fm = f.pack_value(field_args[f.name]) + var_vals[f.name] = v + lengths[f.name] = l + formats[f.name] = fm + + total_length += len(v) + + + # Construct item list for struct.pack call, packing all static fields. + pack_items = [] + + for f in self.static_fields: + if isinstance(f, LengthField): + + # If this is a total length field, insert + # the calculated field value here + if isinstance(f, TotalLengthField): + pack_items.append(f.calc_length(total_length)) + else: + pack_items.append(f.calc_length(lengths[f.name])) + + # Format field, just insert the value we got previously + elif isinstance(f, FormatField): + pack_items.append(formats[f.name]) + + # A constant field, insert its value directly + elif isinstance(f, ConstantField): + pack_items.append(f.value) + + # Value fields + else: + if f.structvalues == 1: + # If there's a value check/convert function, call it + if f.check_value is not None: + pack_items.append(f.check_value(field_args[f.name])) + # Else just use the argument as provided + else: + pack_items.append(field_args[f.name]) + + # Multivalue field. Handled like single valuefield, + # but the value are tuple unpacked into separate arguments + # which are appended to pack_items + else: + if f.check_value is not None: + pack_items.extend(f.check_value(field_args[f.name])) + else: + pack_items.extend(field_args[f.name]) + + static_part = struct.pack(self.static_codes, *pack_items) + var_parts = [var_vals[f.name] for f in self.var_fields] + return static_part + b''.join(var_parts) + + + def pack_value(self, value): + + """ This function allows Struct objects to be used in List and + Object fields. Each item represents the arguments to pass to + to_binary, either a tuple, a dictionary or a DictWrapper. + + """ + + if type(value) is tuple: + return self.to_binary(*value) + elif isinstance(value, dict): + return self.to_binary(**value) + elif isinstance(value, DictWrapper): + return self.to_binary(**value._data) + else: + raise BadDataError('%s is not a tuple or a list' % (value)) + + + def parse_value(self, val, display, rawdict = 0): + + """This function is used by List and Object fields to convert + Struct objects with no var_fields into Python values. + + """ + ret = {} + vno = 0 + for f in self.static_fields: + # Fields without names should be ignored, and there should + # not be any length or format fields if this function + # ever gets called. (If there were such fields, there should + # be a matching field in var_fields and then parse_binary + # would have been called instead. + + if not f.name: + pass + + elif isinstance(f, LengthField): + pass + + elif isinstance(f, FormatField): + pass + + # Value fields + else: + # If this field has a parse_value method, call it, otherwise + # use the unpacked value as is. + if f.structvalues == 1: + field_val = val[vno] + else: + field_val = val[vno:vno+f.structvalues] + + if f.parse_value is not None: + field_val = f.parse_value(field_val, display, rawdict=rawdict) + ret[f.name] = field_val + + vno = vno + f.structvalues + + if not rawdict: + return DictWrapper(ret) + return ret + + def parse_binary(self, data, display, rawdict = 0): + + """values, remdata = s.parse_binary(data, display, rawdict = 0) + + Convert a binary representation of the structure into Python values. + + DATA is a string or a buffer containing the binary data. + DISPLAY should be a Xlib.protocol.display.Display object if + there are any Resource fields or Lists with ResourceObjs. + + The Python values are returned as VALUES. If RAWDICT is true, + a Python dictionary is returned, where the keys are field + names and the values are the corresponding Python value. If + RAWDICT is false, a DictWrapper will be returned where all + fields are available as attributes. + + REMDATA are the remaining binary data, unused by the Struct object. + + """ + ret = {} + val = struct.unpack(self.static_codes, data[:self.static_size]) + lengths = {} + formats = {} + + vno = 0 + for f in self.static_fields: + + # Fields without name should be ignored. This is typically + # pad and constant fields + + if not f.name: + pass + + # Store index in val for Length and Format fields, to be used + # when treating varfields. + + elif isinstance(f, LengthField): + f_names = [f.name] + if f.other_fields: + f_names.extend(f.other_fields) + field_val = val[vno] + if f.parse_value is not None: + field_val = f.parse_value(field_val, display) + for f_name in f_names: + lengths[f_name] = field_val + + elif isinstance(f, FormatField): + formats[f.name] = val[vno] + + # Treat value fields the same was as in parse_value. + else: + if f.structvalues == 1: + field_val = val[vno] + else: + field_val = val[vno:vno+f.structvalues] + + if f.parse_value is not None: + field_val = f.parse_value(field_val, display) + ret[f.name] = field_val + + vno = vno + f.structvalues + + data = data[self.static_size:] + + # Call parse_binary_value for each var_field, passing the + # length and format values from the unpacked val. + + for f in self.var_fields: + ret[f.name], data = f.parse_binary_value(data, display, + lengths.get(f.name), + formats.get(f.name), + ) + + if not rawdict: + ret = DictWrapper(ret) + return ret, data + + +class TextElements8(ValueField): + string_textitem = Struct( LengthOf('string', 1), + Int8('delta'), + String8('string', pad = 0) ) + + def pack_value(self, value): + data = b'' + args = {} + + for v in value: + # Let values be simple strings, meaning a delta of 0 + if type(v) in (str, bytes): + v = (0, v) + + # A tuple, it should be (delta, string) + # Encode it as one or more textitems + + if isinstance(v, (tuple, dict, DictWrapper)): + + if isinstance(v, tuple): + delta, m_str = v + else: + delta = v['delta'] + m_str = v['string'] + + while delta or m_str: + args['delta'] = delta + args['string'] = m_str[:254] + + data = data + self.string_textitem.to_binary(*(), **args) + + delta = 0 + m_str = m_str[254:] + + # Else an integer, i.e. a font change + else: + # Use fontable cast function if instance + if isinstance(v, Fontable): + v = v.__fontable__() + + data = data + struct.pack('>BL', 255, v) + + # Pad out to four byte length + dlen = len(data) + return data + b'\0' * ((4 - dlen % 4) % 4), None, None + + def parse_binary_value(self, data, display, length, format): + values = [] + while 1: + if len(data) < 2: + break + + # font change + if byte2int(data) == 255: + values.append(struct.unpack('>L', bytes(data[1:5]))[0]) + data = data[5:] + + # skip null strings + elif byte2int(data) == 0 and indexbytes(data, 1) == 0: + data = data[2:] + + # string with delta + else: + v, data = self.string_textitem.parse_binary(data, display) + values.append(v) + + return values, '' + + + +class TextElements16(TextElements8): + string_textitem = Struct( LengthOf('string', 1), + Int8('delta'), + String16('string', pad = 0) ) + + + +class GetAttrData(object): + def __getattr__(self, attr): + try: + if self._data: + return self._data[attr] + else: + raise AttributeError(attr) + except KeyError: + raise AttributeError(attr) + +class DictWrapper(GetAttrData): + def __init__(self, dict): + self.__dict__['_data'] = dict + + def __getitem__(self, key): + return self._data[key] + + def __setitem__(self, key, value): + self._data[key] = value + + def __delitem__(self, key): + del self._data[key] + + def __setattr__(self, key, value): + self._data[key] = value + + def __delattr__(self, key): + del self._data[key] + + def __str__(self): + return str(self._data) + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, repr(self._data)) + + def __lt__(self, other): + if isinstance(other, DictWrapper): + return self._data < other._data + else: + return self._data < other + + def __gt__(self, other): + if isinstance(other, DictWrapper): + return self._data > other._data + else: + return self._data > other + + def __eq__(self, other): + if isinstance(other, DictWrapper): + return self._data == other._data + else: + return self._data == other + + +class Request(object): + def __init__(self, display, onerror = None, *args, **keys): + self._errorhandler = onerror + self._binary = self._request.to_binary(*args, **keys) + self._serial = None + display.send_request(self, onerror is not None) + + def _set_error(self, error): + if self._errorhandler is not None: + return call_error_handler(self._errorhandler, error, self) + else: + return 0 + +class ReplyRequest(GetAttrData): + def __init__(self, display, defer = 0, *args, **keys): + self._display = display + self._binary = self._request.to_binary(*args, **keys) + self._serial = None + self._data = None + self._error = None + + self._response_lock = lock.allocate_lock() + + self._display.send_request(self, 1) + if not defer: + self.reply() + + def reply(self): + # Send request and wait for reply if we hasn't + # already got one. This means that reply() can safely + # be called more than one time. + + self._response_lock.acquire() + while self._data is None and self._error is None: + self._display.send_recv_lock.acquire() + self._response_lock.release() + + self._display.send_and_recv(request = self._serial) + self._response_lock.acquire() + + self._response_lock.release() + self._display = None + + # If error has been set, raise it + if self._error: + raise self._error + + def _parse_response(self, data): + self._response_lock.acquire() + self._data, d = self._reply.parse_binary(data, self._display, rawdict = 1) + self._response_lock.release() + + def _set_error(self, error): + self._response_lock.acquire() + self._error = error + self._response_lock.release() + return 1 + + def __repr__(self): + return '<%s serial = %s, data = %s, error = %s>' % (self.__class__.__name__, self._serial, self._data, self._error) + + +class Event(GetAttrData): + def __init__(self, binarydata = None, display = None, + **keys): + if binarydata: + self._binary = binarydata + self._data, data = self._fields.parse_binary(binarydata, display, + rawdict = 1) + # split event type into type and send_event bit + self._data['send_event'] = not not self._data['type'] & 0x80 + self._data['type'] = self._data['type'] & 0x7f + else: + if self._code: + keys['type'] = self._code + + keys['sequence_number'] = 0 + + self._binary = self._fields.to_binary(**keys) + + keys['send_event'] = 0 + self._data = keys + + def __repr__(self): + kwlist = [] + for kw, val in self._data.items(): + if kw == 'send_event': + continue + if kw == 'type' and self._data['send_event']: + val = val | 0x80 + kwlist.append('%s = %s' % (kw, repr(val))) + + kws = ', '.join(kwlist) + return '%s(%s)' % (self.__class__.__name__, kws) + + def __lt__(self, other): + if isinstance(other, Event): + return self._data < other._data + else: + return self._data < other + + def __gt__(self, other): + if isinstance(other, Event): + return self._data > other._data + else: + return self._data > other + + def __eq__(self, other): + if isinstance(other, Event): + return self._data == other._data + else: + return self._data == other + + +def call_error_handler(handler, error, request): + try: + return handler(error, request) + except: + sys.stderr.write('Exception raised by error handler.\n') + traceback.print_exc() + return 0 diff --git a/Xlib/protocol/structs.py b/Xlib/protocol/structs.py new file mode 100644 index 0000000..1661440 --- /dev/null +++ b/Xlib/protocol/structs.py @@ -0,0 +1,161 @@ +# Xlib.protocol.structs -- some common request structures +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +# Xlib modules +from .. import X + +# Xlib.protocol modules +from . import rq + +def WindowValues(arg): + return rq.ValueList( arg, 4, 0, + rq.Pixmap('background_pixmap'), + rq.Card32('background_pixel'), + rq.Pixmap('border_pixmap'), + rq.Card32('border_pixel'), + rq.Gravity('bit_gravity'), + rq.Gravity('win_gravity'), + rq.Set('backing_store', 1, + (X.NotUseful, X.WhenMapped, X.Always)), + rq.Card32('backing_planes'), + rq.Card32('backing_pixel'), + rq.Bool('override_redirect'), + rq.Bool('save_under'), + rq.Card32('event_mask'), + rq.Card32('do_not_propagate_mask'), + rq.Colormap('colormap'), + rq.Cursor('cursor'), + ) + +def GCValues(arg): + return rq.ValueList( arg, 4, 0, + rq.Set('function', 1, + (X.GXclear, X.GXand, X.GXandReverse, + X.GXcopy, X.GXandInverted, X.GXnoop, + X.GXxor, X.GXor, X.GXnor, X.GXequiv, + X.GXinvert, X.GXorReverse, X.GXcopyInverted, + X.GXorInverted, X.GXnand, X.GXset)), + rq.Card32('plane_mask'), + rq.Card32('foreground'), + rq.Card32('background'), + rq.Card16('line_width'), + rq.Set('line_style', 1, + (X.LineSolid, X.LineOnOffDash, X.LineDoubleDash)), + rq.Set('cap_style', 1, + (X.CapNotLast, X.CapButt, + X.CapRound, X.CapProjecting)), + rq.Set('join_style', 1, + (X.JoinMiter, X.JoinRound, X.JoinBevel)), + rq.Set('fill_style', 1, + (X.FillSolid, X.FillTiled, + X.FillStippled, X.FillOpaqueStippled)), + rq.Set('fill_rule', 1, + (X.EvenOddRule, X.WindingRule)), + rq.Pixmap('tile'), + rq.Pixmap('stipple'), + rq.Int16('tile_stipple_x_origin'), + rq.Int16('tile_stipple_y_origin'), + rq.Font('font'), + rq.Set('subwindow_mode', 1, + (X.ClipByChildren, X.IncludeInferiors)), + rq.Bool('graphics_exposures'), + rq.Int16('clip_x_origin'), + rq.Int16('clip_y_origin'), + rq.Pixmap('clip_mask'), + rq.Card16('dash_offset'), + rq.Card8('dashes'), + rq.Set('arc_mode', 1, (X.ArcChord, X.ArcPieSlice)) + ) + + + +TimeCoord = rq.Struct( + rq.Card32('time'), + rq.Int16('x'), + rq.Int16('y'), + ) + +Host = rq.Struct( + rq.Set('family', 1, (X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos)), + rq.Pad(1), + rq.LengthOf('name', 2), + rq.List('name', rq.Card8Obj) + ) + +CharInfo = rq.Struct( + rq.Int16('left_side_bearing'), + rq.Int16('right_side_bearing'), + rq.Int16('character_width'), + rq.Int16('ascent'), + rq.Int16('descent'), + rq.Card16('attributes'), + ) + +FontProp = rq.Struct( + rq.Card32('name'), + rq.Card32('value'), + ) + +ColorItem = rq.Struct( + rq.Card32('pixel'), + rq.Card16('red'), + rq.Card16('green'), + rq.Card16('blue'), + rq.Card8('flags'), + rq.Pad(1), + ) + + +RGB = rq.Struct( + rq.Card16('red'), + rq.Card16('green'), + rq.Card16('blue'), + rq.Pad(2), + ) + + +Point = rq.Struct( + rq.Int16('x'), + rq.Int16('y'), + ) + +Segment = rq.Struct( + rq.Int16('x1'), + rq.Int16('y1'), + rq.Int16('x2'), + rq.Int16('y2'), + ) + +Rectangle = rq.Struct( + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + ) + +Arc = rq.Struct( + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Int16('angle1'), + rq.Int16('angle2'), + ) diff --git a/Xlib/rdb.py b/Xlib/rdb.py new file mode 100644 index 0000000..03b06e2 --- /dev/null +++ b/Xlib/rdb.py @@ -0,0 +1,712 @@ +# Xlib.rdb -- X resource database implementation +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + + +# See end of file for an explanation of the algorithm and +# data structures used. + + +# Standard modules +import re +import sys + +# Xlib modules +from .support import lock + +# Set up a few regexpes for parsing string representation of resources + +comment_re = re.compile(r'^\s*!') +resource_spec_re = re.compile(r'^\s*([-_a-zA-Z0-9?.*]+)\s*:\s*(.*)$') +value_escape_re = re.compile('\\\\([ \tn\\\\]|[0-7]{3,3})') +resource_parts_re = re.compile(r'([.*]+)') + +# Constants used for determining which match is best + +NAME_MATCH = 0 +CLASS_MATCH = 2 +WILD_MATCH = 4 +MATCH_SKIP = 6 + +# Option error class +class OptionError(Exception): + pass + + +class ResourceDB(object): + def __init__(self, file = None, string = None, resources = None): + self.db = {} + self.lock = lock.allocate_lock() + + if file is not None: + self.insert_file(file) + if string is not None: + self.insert_string(string) + if resources is not None: + self.insert_resources(resources) + + def insert_file(self, file): + """insert_file(file) + + Load resources entries from FILE, and insert them into the + database. FILE can be a filename (a string)or a file object. + + """ + + if type(file) is bytes: + file = open(file, 'r') + + self.insert_string(file.read()) + + + def insert_string(self, data): + """insert_string(data) + + Insert the resources entries in the string DATA into the + database. + + """ + + # First split string into lines + lines = data.split('\n') + + while lines: + line = lines[0] + del lines[0] + + # Skip empty line + if not line: + continue + + # Skip comments + if comment_re.match(line): + continue + + # Handle continued lines + while line[-1] == '\\': + if lines: + line = line[:-1] + lines[0] + del lines[0] + else: + line = line[:-1] + break + + # Split line into resource and value + m = resource_spec_re.match(line) + + # Bad line, just ignore it silently + if not m: + continue + + res, value = m.group(1, 2) + + # Convert all escape sequences in value + splits = value_escape_re.split(value) + + for i in range(1, len(splits), 2): + s = splits[i] + if len(s) == 3: + splits[i] = chr(int(s, 8)) + elif s == 'n': + splits[i] = '\n' + + # strip the last value part to get rid of any + # unescaped blanks + splits[-1] = splits[-1].rstrip() + + value = ''.join(splits) + + self.insert(res, value) + + + def insert_resources(self, resources): + """insert_resources(resources) + + Insert all resources entries in the list RESOURCES into the + database. Each element in RESOURCES should be a tuple: + + (resource, value) + + Where RESOURCE is a string and VALUE can be any Python value. + + """ + + for res, value in resources: + self.insert(res, value) + + def insert(self, resource, value): + """insert(resource, value) + + Insert a resource entry into the database. RESOURCE is a + string and VALUE can be any Python value. + + """ + + # Split res into components and bindings + parts = resource_parts_re.split(resource) + + # If the last part is empty, this is an invalid resource + # which we simply ignore + if parts[-1] == '': + return + + self.lock.acquire() + + db = self.db + for i in range(1, len(parts), 2): + + # Create a new mapping/value group + if parts[i - 1] not in db: + db[parts[i - 1]] = ({}, {}) + + # Use second mapping if a loose binding, first otherwise + if '*' in parts[i]: + db = db[parts[i - 1]][1] + else: + db = db[parts[i - 1]][0] + + # Insert value into the derived db + if parts[-1] in db: + db[parts[-1]] = db[parts[-1]][:2] + (value, ) + else: + db[parts[-1]] = ({}, {}, value) + + self.lock.release() + + def __getitem__(self, keys_tuple): + """db[name, class] + + Return the value matching the resource identified by NAME and + CLASS. If no match is found, KeyError is raised. + """ + + # Split name and class into their parts + name, cls = keys_tuple + + namep = name.split('.') + clsp = cls.split('.') + + # It is an error for name and class to have different number + # of parts + + if len(namep) != len(clsp): + raise ValueError('Different number of parts in resource name/class: %s/%s' % (name, cls)) + + complen = len(namep) + matches = [] + + # Lock database and wrap the lookup code in a try-finally + # block to make sure that it is unlocked. + + self.lock.acquire() + try: + + # Precedence order: name -> class -> ? + + if namep[0] in self.db: + bin_insert(matches, _Match((NAME_MATCH, ), self.db[namep[0]])) + + if clsp[0] in self.db: + bin_insert(matches, _Match((CLASS_MATCH, ), self.db[clsp[0]])) + + if '?' in self.db: + bin_insert(matches, _Match((WILD_MATCH, ), self.db['?'])) + + + # Special case for the unlikely event that the resource + # only has one component + if complen == 1 and matches: + x = matches[0] + if x.final(complen): + return x.value() + else: + raise KeyError((name, cls)) + + + # Special case for resources which begins with a loose + # binding, e.g. '*foo.bar' + if '' in self.db: + bin_insert(matches, _Match((), self.db[''][1])) + + + # Now iterate over all components until we find the best match. + + # For each component, we choose the best partial match among + # the mappings by applying these rules in order: + + # Rule 1: If the current group contains a match for the + # name, class or '?', we drop all previously found loose + # binding mappings. + + # Rule 2: A matching name has precedence over a matching + # class, which in turn has precedence over '?'. + + # Rule 3: Tight bindings have precedence over loose + # bindings. + + while matches: + + # Work on the first element == the best current match + + x = matches[0] + del matches[0] + + # print 'path: ', x.path + # if x.skip: + # print 'skip: ', x.db + # else: + # print 'group: ', x.group + # print + + i = x.match_length() + + for part, score in ((namep[i], NAME_MATCH), + (clsp[i], CLASS_MATCH), + ('?', WILD_MATCH)): + + # Attempt to find a match in x + match = x.match(part, score) + if match: + # Hey, we actually found a value! + if match.final(complen): + return match.value() + + # Else just insert the new match + else: + bin_insert(matches, match) + + # Generate a new loose match + match = x.skip_match(complen) + if match: + bin_insert(matches, match) + + # Oh well, nothing matched + raise KeyError((name, cls)) + + finally: + self.lock.release() + + def get(self, res, cls, default = None): + """get(name, class [, default]) + + Return the value matching the resource identified by NAME and + CLASS. If no match is found, DEFAULT is returned, or None if + DEFAULT isn't specified. + + """ + + try: + return self[(res, cls)] + except KeyError: + return default + + def update(self, db): + """update(db) + + Update this database with all resources entries in the resource + database DB. + + """ + + self.lock.acquire() + update_db(self.db, db.db) + self.lock.release() + + def output(self): + """output() + + Return the resource database in text representation. + """ + + self.lock.acquire() + text = output_db('', self.db) + self.lock.release() + return text + + def getopt(self, name, argv, opts): + """getopt(name, argv, opts) + + Parse X command line options, inserting the recognised options + into the resource database. + + NAME is the application name, and will be prepended to all + specifiers. ARGV is the list of command line arguments, + typically sys.argv[1:]. + + OPTS is a mapping of options to resource specifiers. The key is + the option flag (with leading -), and the value is an instance of + some Option subclass: + + NoArg(specifier, value): set resource to value. + IsArg(specifier): set resource to option itself + SepArg(specifier): value is next argument + ResArg: resource and value in next argument + SkipArg: ignore this option and next argument + SkipLine: ignore rest of arguments + SkipNArgs(count): ignore this option and count arguments + + The remaining, non-option, oparguments is returned. + + rdb.OptionError is raised if there is an error in the argument list. + """ + + while argv and argv[0] and argv[0][0] == '-': + try: + argv = opts[argv[0]].parse(name, self, argv) + except KeyError: + raise OptionError('unknown option: %s' % argv[0]) + except IndexError: + raise OptionError('missing argument to option: %s' % argv[0]) + + return argv + + +class _Match(object): + def __init__(self, path, dbs): + self.path = path + + if type(dbs) is tuple: + self.skip = 0 + self.group = dbs + + else: + self.skip = 1 + self.db = dbs + + def __lt__(self, other): + return self.path < other.path + + def __gt__(self, other): + return self.path > other.path + + def __eq__(self, other): + return self.path == other.path + + def match_length(self): + return len(self.path) + + def match(self, part, score): + if self.skip: + if part in self.db: + return _Match(self.path + (score, ), self.db[part]) + else: + return None + else: + if part in self.group[0]: + return _Match(self.path + (score, ), self.group[0][part]) + elif part in self.group[1]: + return _Match(self.path + (score + 1, ), self.group[1][part]) + else: + return None + + def skip_match(self, complen): + # Can't make another skip if we have run out of components + if len(self.path) + 1 >= complen: + return None + + # If this already is a skip match, clone a new one + if self.skip: + if self.db: + return _Match(self.path + (MATCH_SKIP, ), self.db) + else: + return None + + # Only generate a skip match if the loose binding mapping + # is non-empty + elif self.group[1]: + return _Match(self.path + (MATCH_SKIP, ), self.group[1]) + + # This is a dead end match + else: + return None + + def final(self, complen): + if not self.skip and len(self.path) == complen and len(self.group) > 2: + return 1 + else: + return 0 + + def value(self): + return self.group[2] + + +# +# Helper function for ResourceDB.__getitem__() +# + +def bin_insert(list, element): + """bin_insert(list, element) + + Insert ELEMENT into LIST. LIST must be sorted, and ELEMENT will + be inserted to that LIST remains sorted. If LIST already contains + ELEMENT, it will not be duplicated. + + """ + + if not list: + list.append(element) + return + + lower = 0 + upper = len(list) - 1 + + while lower <= upper: + center = (lower + upper) // 2 + if element < list[center]: + upper = center - 1 + elif element > list[center]: + lower = center + 1 + elif element == list[center]: + return + + if element < list[upper]: + list.insert(upper, element) + elif element > list[upper]: + list.insert(upper + 1, element) + + +# +# Helper functions for ResourceDB.update() +# + +def update_db(dest, src): + for comp, group in src.items(): + + # DEST already contains this component, update it + if comp in dest: + + # Update tight and loose binding databases + update_db(dest[comp][0], group[0]) + update_db(dest[comp][1], group[1]) + + # If a value has been set in SRC, update + # value in DEST + + if len(group) > 2: + dest[comp] = dest[comp][:2] + group[2:] + + # COMP not in src, make a deep copy + else: + dest[comp] = copy_group(group) + +def copy_group(group): + return (copy_db(group[0]), copy_db(group[1])) + group[2:] + +def copy_db(db): + newdb = {} + for comp, group in db.items(): + newdb[comp] = copy_group(group) + + return newdb + + +# +# Helper functions for output +# + +def output_db(prefix, db): + res = '' + for comp, group in db.items(): + + # There's a value for this component + if len(group) > 2: + res = res + '%s%s: %s\n' % (prefix, comp, output_escape(group[2])) + + # Output tight and loose bindings + res = res + output_db(prefix + comp + '.', group[0]) + res = res + output_db(prefix + comp + '*', group[1]) + + return res + +def output_escape(value): + value = str(value) + if not value: + return value + + for char, esc in (('\\', '\\\\'), + ('\000', '\\000'), + ('\n', '\\n')): + + value = value.replace(char, esc) + + # If first or last character is space or tab, escape them. + if value[0] in ' \t': + value = '\\' + value + if value[-1] in ' \t' and value[-2:-1] != '\\': + value = value[:-1] + '\\' + value[-1] + + return value + + +# +# Option type definitions +# + +class Option(object): + def __init__(self): + pass + + def parse(self, name, db, args): + pass + +class NoArg(Option): + """Value is provided to constructor.""" + def __init__(self, specifier, value): + self.specifier = specifier + self.value = value + + def parse(self, name, db, args): + db.insert(name + self.specifier, self.value) + return args[1:] + +class IsArg(Option): + """Value is the option string itself.""" + def __init__(self, specifier): + self.specifier = specifier + + def parse(self, name, db, args): + db.insert(name + self.specifier, args[0]) + return args[1:] + +class SepArg(Option): + """Value is the next argument.""" + def __init__(self, specifier): + self.specifier = specifier + + def parse(self, name, db, args): + db.insert(name + self.specifier, args[1]) + return args[2:] + +class ResArgClass(Option): + """Resource and value in the next argument.""" + def parse(self, name, db, args): + db.insert_string(args[1]) + return args[2:] + +ResArg = ResArgClass() + +class SkipArgClass(Option): + """Ignore this option and next argument.""" + def parse(self, name, db, args): + return args[2:] + +SkipArg = SkipArgClass() + +class SkipLineClass(Option): + """Ignore rest of the arguments.""" + def parse(self, name, db, args): + return [] + +SkipLine = SkipLineClass() + +class SkipNArgs(Option): + """Ignore this option and the next COUNT arguments.""" + def __init__(self, count): + self.count = count + + def parse(self, name, db, args): + return args[1 + self.count:] + + + +def get_display_opts(options, argv = sys.argv): + """display, name, db, args = get_display_opts(options, [argv]) + + Parse X OPTIONS from ARGV (or sys.argv if not provided). + + Connect to the display specified by a *.display resource if one is + set, or to the default X display otherwise. Extract the + RESOURCE_MANAGER property and insert all resources from ARGV. + + The four return values are: + DISPLAY -- the display object + NAME -- the application name (the filname of ARGV[0]) + DB -- the created resource database + ARGS -- any remaining arguments + """ + + from Xlib import display, Xatom + import os + + name = os.path.splitext(os.path.basename(argv[0]))[0] + + optdb = ResourceDB() + leftargv = optdb.getopt(name, argv[1:], options) + + dname = optdb.get(name + '.display', name + '.Display', None) + d = display.Display(dname) + + rdbstring = d.screen(0).root.get_full_property(Xatom.RESOURCE_MANAGER, + Xatom.STRING) + if rdbstring: + data = rdbstring.value + else: + data = None + + db = ResourceDB(string = data) + db.update(optdb) + + return d, name, db, leftargv + + +# Common X options +stdopts = {'-bg': SepArg('*background'), + '-background': SepArg('*background'), + '-fg': SepArg('*foreground'), + '-foreground': SepArg('*foreground'), + '-fn': SepArg('*font'), + '-font': SepArg('*font'), + '-name': SepArg('.name'), + '-title': SepArg('.title'), + '-synchronous': NoArg('*synchronous', 'on'), + '-xrm': ResArg, + '-display': SepArg('.display'), + '-d': SepArg('.display'), + } + + +# Notes on the implementation: + +# Resource names are split into their components, and each component +# is stored in a mapping. The value for a component is a tuple of two +# or three elements: + +# (tightmapping, loosemapping [, value]) + +# tightmapping contains the next components which are connected with a +# tight binding (.). loosemapping contains the ones connected with +# loose binding (*). If value is present, then this component is the +# last component for some resource which that value. + +# The top level components are stored in the mapping r.db, where r is +# the resource object. + +# Example: Inserting "foo.bar*gazonk: yep" into an otherwise empty +# resource database would give the following structure: + +# { 'foo': ( { 'bar': ( { }, +# { 'gazonk': ( { }, +# { }, +# 'yep') +# } +# ) +# }, +# {}) +# } diff --git a/Xlib/support/__init__.py b/Xlib/support/__init__.py new file mode 100644 index 0000000..4c0d622 --- /dev/null +++ b/Xlib/support/__init__.py @@ -0,0 +1,26 @@ +# Xlib.support.__init__ -- support code package +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +__all__ = [ + 'lock', + 'connect' + # The platform specific modules should not be listed here + ] diff --git a/Xlib/support/__pycache__/__init__.cpython-38.pyc b/Xlib/support/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a9f26a23a4b6b1268a37773fa6398e92f9ec0a7 GIT binary patch literal 183 zcmWIL<>g`kf{mBuk`FL5FgylvkO32r;{e3PJU}9aA%!uWDT*P5DVRZ%`4&q~esXpc zdvbnWUTSiQpC;oi_W1b3oSgXhl?+8d!yv>jYyFJ;+*JL?(%jU% zl4AX=)YAB({G3$%h@8wM{o>Msg8ZTq{rLFIyv&mLc)fzkTO2mI`6;D2sdnNZN3Z}1 ICPt=z0IY5>5&!@I literal 0 HcmV?d00001 diff --git a/Xlib/support/__pycache__/connect.cpython-38.pyc b/Xlib/support/__pycache__/connect.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8889f902435ddc5cd6b9e5436e90837fa0cd102e GIT binary patch literal 2203 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 +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +import sys +import importlib + +# List the modules which contain the corresponding functions + +_display_mods = { + 'OpenVMS': 'vms_connect', + } + +_default_display_mod = 'unix_connect' + +_socket_mods = { + 'OpenVMS': 'vms_connect' + } + +_default_socket_mod = 'unix_connect' + +_auth_mods = { + 'OpenVMS': 'vms_connect' + } + +_default_auth_mod = 'unix_connect' + + +# Figure out which OS we're using. +# sys.platform is either "OS-ARCH" or just "OS". + +_parts = sys.platform.split('-') +platform = _parts[0] +del _parts + + +def _relative_import(modname): + return importlib.import_module('..' + modname, __name__) + + +def get_display(display): + """dname, protocol, host, dno, screen = get_display(display) + + Parse DISPLAY into its components. If DISPLAY is None, use + the default display. The return values are: + + DNAME -- the full display name (string) + PROTOCOL -- the protocol to use (None if automatic) + HOST -- the host name (string, possibly empty) + DNO -- display number (integer) + SCREEN -- default screen number (integer) + """ + + modname = _display_mods.get(platform, _default_display_mod) + mod = _relative_import(modname) + return mod.get_display(display) + + +def get_socket(dname, protocol, host, dno): + """socket = get_socket(dname, protocol, host, dno) + + Connect to the display specified by DNAME, PROTOCOL, HOST and DNO, which + are the corresponding values from a previous call to get_display(). + + Return SOCKET, a new socket object connected to the X server. + """ + + modname = _socket_mods.get(platform, _default_socket_mod) + mod = _relative_import(modname) + return mod.get_socket(dname, protocol, host, dno) + + +def get_auth(sock, dname, protocol, host, dno): + """auth_name, auth_data = get_auth(sock, dname, protocol, host, dno) + + Return authentication data for the display on the other side of + SOCK, which was opened with DNAME, HOST and DNO, using PROTOCOL. + + Return AUTH_NAME and AUTH_DATA, two strings to be used in the + connection setup request. + """ + + modname = _auth_mods.get(platform, _default_auth_mod) + mod = _relative_import(modname) + return mod.get_auth(sock, dname, protocol, host, dno) diff --git a/Xlib/support/lock.py b/Xlib/support/lock.py new file mode 100644 index 0000000..6eee31f --- /dev/null +++ b/Xlib/support/lock.py @@ -0,0 +1,44 @@ +# Xlib.support.lock -- allocate a lock +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +class _DummyLock(object): + def __init__(self): + + # This might be nerdy, but by assigning methods like this + # instead of defining them all, we create a single bound + # method object once instead of one each time one of the + # methods is called. + + # This gives some speed improvements which should reduce the + # impact of the threading infrastructure in the regular code, + # when not using threading. + + self.acquire = self.release = self.locked = self.__noop + + def __noop(self, *args): + return + + +# More optimisations: we use a single lock for all lock instances +_dummy_lock = _DummyLock() + +def allocate_lock(): + return _dummy_lock diff --git a/Xlib/support/unix_connect.py b/Xlib/support/unix_connect.py new file mode 100644 index 0000000..c2261da --- /dev/null +++ b/Xlib/support/unix_connect.py @@ -0,0 +1,206 @@ +# Xlib.support.unix_connect -- Unix-type display connection functions +# +# Copyright (C) 2000,2002 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +import re +import os +import platform +import socket + +# FCNTL is deprecated from Python 2.2, so only import it if we doesn't +# get the names we need. Furthermore, FD_CLOEXEC seems to be missing +# in Python 2.2. + +import fcntl + +if hasattr(fcntl, 'F_SETFD'): + F_SETFD = fcntl.F_SETFD + if hasattr(fcntl, 'FD_CLOEXEC'): + FD_CLOEXEC = fcntl.FD_CLOEXEC + else: + FD_CLOEXEC = 1 +else: + from FCNTL import F_SETFD, FD_CLOEXEC + + +from Xlib import error, xauth + + +SUPPORTED_PROTOCOLS = (None, 'tcp', 'unix') + +# Darwin funky socket. +uname = platform.uname() +if (uname[0] == 'Darwin') and ([int(x) for x in uname[2].split('.')] >= [9, 0]): + SUPPORTED_PROTOCOLS += ('darwin',) + DARWIN_DISPLAY_RE = re.compile(r'^/private/tmp/[-:a-zA-Z0-9._]*:(?P[0-9]+)(\.(?P[0-9]+))?$') + +DISPLAY_RE = re.compile(r'^((?Ptcp|unix)/)?(?P[-:a-zA-Z0-9._]*):(?P[0-9]+)(\.(?P[0-9]+))?$') + + +def get_display(display): + # Use $DISPLAY if display isn't provided + if display is None: + display = os.environ.get('DISPLAY', '') + + re_list = [(DISPLAY_RE, {})] + + if 'darwin' in SUPPORTED_PROTOCOLS: + re_list.insert(0, (DARWIN_DISPLAY_RE, {'protocol': 'darwin'})) + + for re, defaults in re_list: + m = re.match(display) + if m is not None: + protocol, host, dno, screen = [ + m.groupdict().get(field, defaults.get(field)) + for field in ('proto', 'host', 'dno', 'screen') + ] + break + else: + raise error.DisplayNameError(display) + + if protocol == 'tcp' and not host: + # Host is mandatory when protocol is TCP. + raise error.DisplayNameError(display) + + dno = int(dno) + if screen: + screen = int(screen) + else: + screen = 0 + + return display, protocol, host, dno, screen + + +def _get_tcp_socket(host, dno): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((host, 6000 + dno)) + return s + +def _get_unix_socket(address): + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.connect(address) + return s + +def get_socket(dname, protocol, host, dno): + assert protocol in SUPPORTED_PROTOCOLS + try: + # Darwin funky socket. + if protocol == 'darwin': + s = _get_unix_socket(dname) + + # TCP socket, note the special case: `unix:0.0` is equivalent to `:0.0`. + elif (protocol is None or protocol != 'unix') and host and host != 'unix': + s = _get_tcp_socket(host, dno) + + # Unix socket. + else: + address = '/tmp/.X11-unix/X%d' % dno + if not os.path.exists(address): + # Use abstract address. + address = '\0' + address + try: + s = _get_unix_socket(address) + except socket.error: + if not protocol and not host: + # If no protocol/host was specified, fallback to TCP. + s = _get_tcp_socket(host, dno) + else: + raise + except socket.error as val: + raise error.DisplayConnectionError(dname, str(val)) + + # Make sure that the connection isn't inherited in child processes. + fcntl.fcntl(s.fileno(), F_SETFD, FD_CLOEXEC) + + return s + + +def new_get_auth(sock, dname, protocol, host, dno): + assert protocol in SUPPORTED_PROTOCOLS + # Translate socket address into the xauth domain + if protocol == 'darwin': + family = xauth.FamilyLocal + addr = socket.gethostname() + + elif protocol == 'tcp': + family = xauth.FamilyInternet + + # Convert the prettyprinted IP number into 4-octet string. + # Sometimes these modules are too damn smart... + octets = sock.getpeername()[0].split('.') + addr = bytearray(int(x) for x in octets) + else: + family = xauth.FamilyLocal + addr = socket.gethostname().encode() + + try: + au = xauth.Xauthority() + except error.XauthError: + return b'', b'' + + while 1: + try: + return au.get_best_auth(family, addr, dno) + except error.XNoAuthError: + pass + + # We need to do this to handle ssh's X forwarding. It sets + # $DISPLAY to localhost:10, but stores the xauth cookie as if + # DISPLAY was :10. Hence, if localhost and not found, try + # again as a Unix socket. + if family == xauth.FamilyInternet and addr == b'\x7f\x00\x00\x01': + family = xauth.FamilyLocal + addr = socket.gethostname().encode() + else: + return b'', b'' + + +def old_get_auth(sock, dname, host, dno): + # Find authorization cookie + auth_name = auth_data = b'' + + try: + # We could parse .Xauthority, but xauth is simpler + # although more inefficient + data = os.popen('xauth list %s 2>/dev/null' % dname).read() + + # If there's a cookie, it is of the format + # DISPLAY SCHEME COOKIE + # We're interested in the two last parts for the + # connection establishment + lines = data.split('\n') + if len(lines) >= 1: + parts = lines[0].split(None, 2) + if len(parts) == 3: + auth_name = parts[1] + hexauth = parts[2] + auth = b'' + + # Translate hexcode into binary + for i in range(0, len(hexauth), 2): + auth = auth + chr(int(hexauth[i:i+2], 16)) + + auth_data = auth + except os.error: + pass + + return auth_name, auth_data + +get_auth = new_get_auth diff --git a/Xlib/support/vms_connect.py b/Xlib/support/vms_connect.py new file mode 100644 index 0000000..3c53695 --- /dev/null +++ b/Xlib/support/vms_connect.py @@ -0,0 +1,74 @@ +# Xlib.support.vms_connect -- VMS-type display connection functions +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +import re +import socket + +from Xlib import error + +display_re = re.compile(r'^([-a-zA-Z0-9._]*):([0-9]+)(\.([0-9]+))?$') + +def get_display(display): + + # Use dummy display if none is set. We really should + # check DECW$DISPLAY instead, but that has to wait + + if display is None: + return ':0.0', None, 'localhost', 0, 0 + + m = display_re.match(display) + if not m: + raise error.DisplayNameError(display) + + name = display + + # Always return a host, since we don't have AF_UNIX sockets + host = m.group(1) + if not host: + host = 'localhost' + + dno = int(m.group(2)) + screen = m.group(4) + if screen: + screen = int(screen) + else: + screen = 0 + + return name, None, host, dno, screen + + +def get_socket(dname, protocol, host, dno): + try: + # Always use TCP/IP sockets. Later it would be nice to + # be able to use DECNET och LOCAL connections. + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((host, 6000 + dno)) + + except socket.error as val: + raise error.DisplayConnectionError(dname, str(val)) + + return s + + +def get_auth(sock, dname, host, dno): + # VMS doesn't have xauth + return '', '' diff --git a/Xlib/threaded.py b/Xlib/threaded.py new file mode 100644 index 0000000..44fcafe --- /dev/null +++ b/Xlib/threaded.py @@ -0,0 +1,28 @@ +# Xlib.threaded -- Import this module to enable threading +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +from six.moves import _thread + +# We change the allocate_lock function in Xlib.support.lock to +# return a basic Python lock, instead of the default dummy lock + +from Xlib.support import lock +lock.allocate_lock = _thread.allocate_lock diff --git a/Xlib/xauth.py b/Xlib/xauth.py new file mode 100644 index 0000000..303bd49 --- /dev/null +++ b/Xlib/xauth.py @@ -0,0 +1,134 @@ +# Xlib.xauth -- ~/.Xauthority access +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +import os +import struct + +from Xlib import X, error + +FamilyInternet = X.FamilyInternet +FamilyDECnet = X.FamilyDECnet +FamilyChaos = X.FamilyChaos +FamilyServerInterpreted = X.FamilyServerInterpreted +FamilyInternetV6 = X.FamilyInternetV6 +FamilyLocal = 256 + +class Xauthority(object): + def __init__(self, filename = None): + if filename is None: + filename = os.environ.get('XAUTHORITY') + + if filename is None: + try: + filename = os.path.join(os.environ['HOME'], '.Xauthority') + except KeyError: + raise error.XauthError( + '$HOME not set, cannot find ~/.Xauthority') + + try: + with open(filename, 'rb') as fp: + raw = fp.read() + except IOError as err: + raise error.XauthError('could not read from {0}: {1}'.format(filename, err)) + + self.entries = [] + + # entry format (all shorts in big-endian) + # short family; + # short addrlen; + # char addr[addrlen]; + # short numlen; + # char num[numlen]; + # short namelen; + # char name[namelen]; + # short datalen; + # char data[datalen]; + + n = 0 + try: + while n < len(raw): + family, = struct.unpack('>H', raw[n:n+2]) + n = n + 2 + + length, = struct.unpack('>H', raw[n:n+2]) + n = n + length + 2 + addr = raw[n - length : n] + + length, = struct.unpack('>H', raw[n:n+2]) + n = n + length + 2 + num = raw[n - length : n] + + length, = struct.unpack('>H', raw[n:n+2]) + n = n + length + 2 + name = raw[n - length : n] + + length, = struct.unpack('>H', raw[n:n+2]) + n = n + length + 2 + data = raw[n - length : n] + + if len(data) != length: + break + + self.entries.append((family, addr, num, name, data)) + except struct.error: + print("Xlib.xauth: warning, failed to parse part of xauthority file {0}, aborting all further parsing".format(filename)) + + if len(self.entries) == 0: + print("Xlib.xauth: warning, no xauthority details available") + # raise an error? this should get partially caught by the XNoAuthError in get_best_auth.. + + def __len__(self): + return len(self.entries) + + def __getitem__(self, i): + return self.entries[i] + + def get_best_auth(self, family, address, dispno, + types = ( b"MIT-MAGIC-COOKIE-1", )): + + """Find an authentication entry matching FAMILY, ADDRESS and + DISPNO. + + The name of the auth scheme must match one of the names in + TYPES. If several entries match, the first scheme in TYPES + will be choosen. + + If an entry is found, the tuple (name, data) is returned, + otherwise XNoAuthError is raised. + """ + + num = str(dispno).encode() + + matches = {} + + for efam, eaddr, enum, ename, edata in self.entries: + if enum == b'' and ename not in matches: + enum = num + if efam == family and eaddr == address and num == enum: + matches[ename] = edata + + for t in types: + try: + return (t, matches[t]) + except KeyError: + pass + + raise error.XNoAuthError((family, address, dispno)) diff --git a/Xlib/xobject/__init__.py b/Xlib/xobject/__init__.py new file mode 100644 index 0000000..67d3254 --- /dev/null +++ b/Xlib/xobject/__init__.py @@ -0,0 +1,29 @@ +# Xlib.xobject.__init__ -- glue for Xlib.xobject package +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +__all__ = [ + 'colormap', + 'cursor', + 'drawable', + 'fontable', + 'icccm', + 'resource', + ] diff --git a/Xlib/xobject/__pycache__/__init__.cpython-38.pyc b/Xlib/xobject/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fba46b2c1fcf4652228e67b56dcc0bf7285735ad GIT binary patch literal 239 zcmYk0u}%Xq5JY_mxw8=Ikm%^s;138w6x1{b38h*2b{BGyeH-naMffC2{)3LrEq{>; z9|dAY`!v(6=5e_^HANp^m-sn1=6fptRhM#GZa6O>Llw^L&TV^@*qi(?^jiB0DK4l??#*Q{};!#+e&2T10Q@( y%I;1uMefJJ?Kpu-VD}Wd)*YnXF^&$P>$?%)YX6q)bred0Z!V_MX9ctB&&MCUYeJ#` literal 0 HcmV?d00001 diff --git a/Xlib/xobject/__pycache__/colormap.cpython-38.pyc b/Xlib/xobject/__pycache__/colormap.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ffb3726931ffbadd83f213600ff27c0228a42af GIT binary patch literal 4393 zcmb_gOLG%P5T2QRY9-?b#=M_-6iUD%l^nt&6b1sJ5}b#N3Q?f8B zU<*GAd&2V<`eYSCWW#RO-yFa{l>VvgvZAYK()cdFIEWA;at-iVg z(l^JX3;T{t+{t%XuKViccCf;0iokw9?`Xf*Tlc?9->G{Db@9=la|zz)M<70_Q%>s4 z*V=m9sGIx{XIy(i>lW9!@r2ZEewdrwf-fb0gxkCXoWmVnhA{{&J^^io@8XltPVgx{ z4ec&|l+W|&+)y$P4j(xKS<5+1NFLHPo8Sv zZUeld4Aweeu^kD|Mb9L5rlr8`utbF4gX^l6lAdh@LFhH&5%(x(?!FSPKFYLT=yLR} zn1w}>nWi5P-RT95C=w>Fu8T=%>UQJ_xx3hn;+hvk(#48Qqvy{gSEDcFiw_;z?uz8%h;K`(T!s8Mz)jmkEnoz zK!wH)m})8U+=dT9D8i5U*;4c4vj2m+)3>_ms@X38qVP;Fn~Sgega=2O1A9YbghIeR(ED?-*|wlGz-YTPpxk<@eGMZziJf-& zz9W94AW6wLaP3$kw$?729#g9}Zw39Fq z)mbx2Xft8U&@7K5JFVbCrMSZP2F#8y2oPTf5O(MRmc6r#Db!!u+7#^{7(>0V%npND z9!c8;bK-bmH*Ph1VJ}jiGd(;GqQr#rsq05sSWimtNJXyr0Tei zWGuE*56Hz`4_9zODvxkDWmJTZ+gh05VQtPHhJa6EZV)YG~{i(hCJ^O@;o@+R-J(?VeG{D>IZ!S76Tw-IG>WmE4p)?+?q? z)>*Gcdc=A>P&KfgV%fsW`AF$Pk13cPsf*ibME3#e)6{^;k49}??9?}AENB<{vPQdxS7lsB|W?cWe@UsDLw+7+z=?g1(?jC zG3L;i#8?R$%`_T{>-vTDWie*!qIhI{=49Bo7dk)@`V>zZ{+ormOmlGX-r>N6+0@?Xh z5yl~?sZMO7EMme>EHCV=wgO+A!i|=)-L_LBu`ek9T!jDrAQ6IFp%qqPwgx(>zZbgw E57su;NdN!< literal 0 HcmV?d00001 diff --git a/Xlib/xobject/__pycache__/cursor.cpython-38.pyc b/Xlib/xobject/__pycache__/cursor.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd607ac8876bd73d07c1bf73651a9d275634ccd9 GIT binary patch literal 1036 zcmZuvOK%e~5VpN9XPcDr6d=I`39*;#l}ZRzDuP=MMM!9cv`W@)N!Y~gY*uQk&52(5 z4{+qjZ{g-Er~U)P0Wo8zj|1M!WX2xPH}iSk4F)O0_2cK`YCB=<7dh8gh|Ug5Geo7B z;x(J|f^(*X3TiPAih#2>Oob{sV=B_YEc_f4k&554EIG$JEaNr-ebnk^dCt8rK{uv_ zN>>zceFf<3pft}=X;yH>3PDL{Q4y<{K+kxQsKf#CLMo0x(#E?jGzLbMzq>flbu3IjqMgHZcngV8F(e!^k3_#5R2m zT6~ic)U=c^H-*7=X$Yh z@|kW+FtyI#*V9RUY$h{ZEpv}NJUp=>0X)Jaje9pPx!~d7I5^$#r3~r3qF1|E6t{um zSa^;`$3FzCa22iMPN0>nQ-Ah8CRctfV0*C&ZdAY^mGpPbY*G-*BAvVJxifaKra0On0A3sblcm$ zlr4;ao1|`$x=rd1sk@|zbFVhhqG@zUPta-ZqhdVesc=jpo0s43)6F%f>kAnl(XVG^ zSuVzNU6wX2%ehf)O?JO5kJ@qVPk^4l%DwfV)8XY*xNH7NarXuW!UIxQbvlcR1)AT7 yC=HF67_fUcllYNPhiq?2`dUi33*_ma} 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%0AXU|wLhkjMS8!o8@O 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 literal 0 HcmV?d00001 diff --git a/Xlib/xobject/__pycache__/fontable.cpython-38.pyc b/Xlib/xobject/__pycache__/fontable.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..81478417838b8132e1b8934b011e26a095789179 GIT binary patch literal 3116 zcmb_eOOG2x5T5sp$M$9)AqoVAH^P9{fDi~0!b%QV5YkF{AR!BBG&|k)+8KK`J!7%9 z#wT)(_zTG~M}7;pzH-VB;1Y$vSKWSQSAqkx+vV!+>h7wps;~NodObAo{r3Ci1~XG*V75)uU>37eYhbr+#XI1gwqqLm26LHr-(ViM`|dAx+rx;@ z0*nOQL*GZg!b0>z)el(pw$Z3P!Tv_Wl)m8iMm#T`n7SVbo@XPGaLjqhNaUGFNObkH z(Ah)F-v;4E+hj)D!tqYuZF|gQ)_tSxGn+a14Va7LJQ>``hDCgs@`MhWf5el?Ewp?a zq%itsY4j~Lo0$(RthEjrmh@RKKThMZbUN6l;Rw<}R&I{C7}L(er@qx=P}|9Q+Ieh< z8it-;X&z+*-i%Xz5)YZ+&Ffh*8t`F}H~V}Pi7e&KAJg7p^CUa$^Q36f{-o#b@mRXJ z8Zo|%eR2=PFay*1d$KV%Wmionq{{pw-bGUeod>gOP-(&>TbpDnjYR+v>E(s!4ZCxz zHkDO(_(}22NkN)wQ3{-fC93!l=ZKJfn$*F`ZKX|AU|obp3*W?ePDd!sP3!sO(z@Ph8qUhczPW6l+hsog9EKX>09y3~WeuEg_fT-9avQ$=a zq_Lnoy@aMNH!uS$aMakTfU_aAM}jv%P6b?za^t>4he(PY(d2|fv^gSK*3dq8F6q9yo!F@=4;C?0nCerv^pw)Wu5*j_q!`8Z#>8s$eXQHwqxB@|d7>MYYSt-mIf{noCY zO~?uJk9dlI6?CtHS?Fz-OU>P-=C0O9>EPueiJ-a1@`8SyL7d;68{oCF)E6ZgO)Oh!U+h{M|=fM z`Q>#m3%|Ifqaq51m4tKt(l`E!Z-PuPPSBu@3{Ij<^t!#F490UV6aXy*S-P}$3--3s z-tn!)%!=Kf_TT%^Mo@@}4~aC0oaM`Qymsy3^ z_a9l)dRfk?BAutGCLo7Fs3IH-8c{{Hmpo95)A z4vf>=P?}c$B|GuN=^(CfgM7>*MsP1B@n_r>@coRE51rR~B2ArK(3!@5Zo>f{5J7rU!VK1$-yj{m@a zSdSd}2mA+azH-`)0|x{Mu#*r4V%hKWynf!-?`!9U=Q*1EK788s?|GW`S&{m;F3BS~ z(~m&Yh(WO*t)MtUzwz3aoV0DE&4ubAOXq%FZm(kkR%# zJ;%)&c51j)!)^_G6?RCQHprUHWXMbo>C~7-x|Pl%>ovSl;FX73|JFCN$!4W<$rjnB z`plEFE$7{2k>n8@3d_qkcs+sNUw0lf zW|5Da!c|}uiPJgtc~4K|FQw&65nA|>bB0q|rflW7NFZ7Z8GN4LH;i8hlST84BacOl zGaQUljyb)8s^}K;<7hl*%Mi~?A|pw>Kvur9EbRqq<~+pdE94dunLp0v!pzelsP+fF zZ2?aMc9!U>UPCUzQ+#ip#60%nAW_E(Ysiz#=fXUU_`yE19_MWe>j_TC!M-p~FcH@A z*-Z8>ywTAhPC^_!XAw_@F{E6yM~qKNf)~+H^&(n@q9lj166P1gI>~zz)+ldFG)6~7 z9ymB2JX5sLIin*LHe_$M9W0D2*;kL|^zLZM`xcZLn0vous(FW~;eT8evZ<`Rv@k=-0 aH`uHxzrYPw?Wn!qI&A-hw#rqGAAbO5woDxW literal 0 HcmV?d00001 diff --git a/Xlib/xobject/__pycache__/resource.cpython-38.pyc b/Xlib/xobject/__pycache__/resource.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90ac021c1ccb5d95bab868b1393c5a088599c956 GIT binary patch literal 1514 zcmaJ>OKTKC5bo~T$Lwa;yi8P(5QTsvm<@Ol3?UFvLCC=%$X=Swbcf7lb|&eb#bnuA za`lJg=+E%%ZLXgD3m(L;da|1_QG4j>>gh+-SM^nYXt$dT?aQ~7@N12+?=&g53MSj= zb`?S~#bY+$UC!AXriAh?nDVqZ_qv{{?6IKw1G`wj^SaR|Lv6E==H=#LvW;%vLTJ|I zigksWcXx5RMO80Yx1wsQjBhvfF8kuK18(CsqBn2(ucV*$y>9;8rgk6rTJz{~3@vd3}nEEkc=tLdjio1l_c ztd0*%6=!qLM9S2mT{1(V`}wkSm=1KO7wgkrqKxjmO2gqmCzR z_xn1`I_ApL_2aYLmoiGCOv(qi$|3=b@fyC-`~=VXWJgHACXICJ4ig<4vpz5w!WwWx ze=fQ4F*qj+F@tH|l5*mJlq>jSDK?CcmP!_{cZ6=0cA$|?33OIBp=5|<#?OU$0{ddj z`{GEL$Iw0K-jUb?VfI)umm$jM9Fm`N_ZPkfV)r#d+l*|K*sPZzb#%9jasfa%0J(>_ z-9%B7QlCiq2nVbqFmLh}Z;6^1&6aS`o?8f}T%dzvAUG1ON$d(cbL0oDmf{ABiI(yy zS-gr^*%GbY%FrK{5G&RP{z;ZZ?nO!Fkec??mb2;-1!^4j?4gv;N+!#+C{}M7ohE1j z=M<>khgnL`fmklWKAPEFv#T4YYa1_4x4`)ZT#tL!N=bZ7dIMnjuU|$V8!0#b;a9P` zdn@i#K8`u}p=4K1Q7!g8#Xox-&x>P4;1!itPf>g(HMu{bV^A>^(!BXDisPL)LXln5 zB`D}(sFH3bZ{`RG0Fq8T$GWA;VBF_|SALD|P8n0$hAN^03-Bs5%{^#&Q_4ZAhB2Z; zRdh1!#aFRvVcFavK`}RrB$h}JQuC0+HJhdxc66s5mX7+oS@k`?fsW6u?V>!|^mMEr fn>0(qG%n!Riie~qm%m(`` +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +from Xlib import error +from Xlib.protocol import request + +from . import resource + +import re + +rgb_res = [ + re.compile(r'\Argb:([0-9a-fA-F]{1,4})/([0-9a-fA-F]{1,4})/([0-9a-fA-F]{1,4})\Z'), + re.compile(r'\A#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])\Z'), + re.compile(r'\A#([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])\Z'), + re.compile(r'\A#([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])\Z'), + re.compile(r'\A#([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])\Z'), + ] + +class Colormap(resource.Resource): + __colormap__ = resource.Resource.__resource__ + + def free(self, onerror = None): + request.FreeColormap(display = self.display, + onerror = onerror, + cmap = self.id) + + self.display.free_resource_id(self.id) + + def copy_colormap_and_free(self, scr_cmap): + mid = self.display.allocate_resource_id() + request.CopyColormapAndFree(display = self.display, + mid = mid, + src_cmap = src_cmap) + + cls = self.display.get_resource_class('colormap', Colormap) + return cls(self.display, mid, owner = 1) + + def install_colormap(self, onerror = None): + request.InstallColormap(display = self.display, + onerror = onerror, + cmap = self.id) + + def uninstall_colormap(self, onerror = None): + request.UninstallColormap(display = self.display, + onerror = onerror, + cmap = self.id) + + def alloc_color(self, red, green, blue): + return request.AllocColor(display = self.display, + cmap = self.id, + red = red, + green = green, + blue = blue) + + def alloc_named_color(self, name): + for r in rgb_res: + m = r.match(name) + if m: + rs = m.group(1) + r = int(rs + '0' * (4 - len(rs)), 16) + + gs = m.group(2) + g = int(gs + '0' * (4 - len(gs)), 16) + + bs = m.group(3) + b = int(bs + '0' * (4 - len(bs)), 16) + + return self.alloc_color(r, g, b) + + try: + return request.AllocNamedColor(display = self.display, + cmap = self.id, + name = name) + except error.BadName: + return None + + def alloc_color_cells(self, contiguous, colors, planes): + return request.AllocColorCells(display = self.display, + contiguous = contiguous, + cmap = self.id, + colors = colors, + planes = planes) + + def alloc_color_planes(self, contiguous, colors, red, green, blue): + return request.AllocColorPlanes(display = self.display, + contiguous = contiguous, + cmap = self.id, + colors = colors, + red = red, + green = green, + blue = blue) + + def free_colors(self, pixels, plane_mask, onerror = None): + request.FreeColors(display = self.display, + onerror = onerror, + cmap = self.id, + plane_mask = plane_mask, + pixels = pixels) + + def store_colors(self, items, onerror = None): + request.StoreColors(display = self.display, + onerror = onerror, + cmap = self.id, + items = items) + + def store_named_color(self, name, pixel, flags, onerror = None): + request.StoreNamedColor(display = self.display, + onerror = onerror, + flags = flags, + cmap = self.id, + pixel = pixel, + name = name) + + def query_colors(self, pixels): + r = request.QueryColors(display = self.display, + cmap = self.id, + pixels = pixels) + return r.colors + + def lookup_color(self, name): + return request.LookupColor(display = self.display, + cmap = self.id, + name = name) diff --git a/Xlib/xobject/cursor.py b/Xlib/xobject/cursor.py new file mode 100644 index 0000000..432e4fd --- /dev/null +++ b/Xlib/xobject/cursor.py @@ -0,0 +1,47 @@ +# Xlib.xobject.cursor -- cursor object +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +from Xlib.protocol import request + +from . import resource + +class Cursor(resource.Resource): + __cursor__ = resource.Resource.__resource__ + + def free(self, onerror = None): + request.FreeCursor(display = self.display, + onerror = onerror, + cursor = self.id) + self.display.free_resource_id(self.id) + + def recolor(self, foreground, background, onerror=None): + fore_red, fore_green, fore_blue = foreground + back_red, back_green, back_blue = background + + request.RecolorCursor(display = self.display, + onerror = onerror, + cursor = self.id, + fore_red = fore_red, + fore_green = fore_green, + fore_blue = fore_blue, + back_red = back_red, + back_green = back_green, + back_blue = back_blue) diff --git a/Xlib/xobject/drawable.py b/Xlib/xobject/drawable.py new file mode 100644 index 0000000..c36a973 --- /dev/null +++ b/Xlib/xobject/drawable.py @@ -0,0 +1,835 @@ +# Xlib.xobject.drawable -- drawable objects (window and pixmap) +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +from Xlib import X, Xatom, Xutil +from Xlib.protocol import request, rq + +# Other X resource objects +from . import resource +from . import colormap +from . import cursor +from . import fontable + +# Inter-client communication conventions +from . import icccm + +class Drawable(resource.Resource): + __drawable__ = resource.Resource.__resource__ + + def get_geometry(self): + return request.GetGeometry(display = self.display, + drawable = self) + + def create_pixmap(self, width, height, depth): + pid = self.display.allocate_resource_id() + request.CreatePixmap(display = self.display, + depth = depth, + pid = pid, + drawable = self.id, + width = width, + height = height) + + cls = self.display.get_resource_class('pixmap', Pixmap) + return cls(self.display, pid, owner = 1) + + def create_gc(self, **keys): + cid = self.display.allocate_resource_id() + request.CreateGC(display = self.display, + cid = cid, + drawable = self.id, + attrs = keys) + + cls = self.display.get_resource_class('gc', fontable.GC) + return cls(self.display, cid, owner = 1) + + def copy_area(self, gc, src_drawable, src_x, src_y, width, height, dst_x, dst_y, onerror = None): + request.CopyArea(display = self.display, + onerror = onerror, + src_drawable = src_drawable, + dst_drawable = self.id, + gc = gc, + src_x = src_x, + src_y = src_y, + dst_x = dst_x, + dst_y = dst_y, + width = width, + height = height) + + def copy_plane(self, gc, src_drawable, src_x, src_y, width, height, + dst_x, dst_y, bit_plane, onerror = None): + request.CopyPlane(display = self.display, + onerror = onerror, + src_drawable = src_drawable, + dst_drawable = self.id, + gc = gc, + src_x = src_x, + src_y = src_y, + dst_x = dst_x, + dst_y = dst_y, + width = width, + height = height, + bit_plane = bit_plane) + + def poly_point(self, gc, coord_mode, points, onerror = None): + request.PolyPoint(display = self.display, + onerror = onerror, + coord_mode = coord_mode, + drawable = self.id, + gc = gc, + points = points) + + def point(self, gc, x, y, onerror = None): + request.PolyPoint(display = self.display, + onerror = onerror, + coord_mode = X.CoordModeOrigin, + drawable = self.id, + gc = gc, + points = [(x, y)]) + + def poly_line(self, gc, coord_mode, points, onerror = None): + request.PolyLine(display = self.display, + onerror = onerror, + coord_mode = coord_mode, + drawable = self.id, + gc = gc, + points = points) + + def line(self, gc, x1, y1, x2, y2, onerror = None): + request.PolySegment(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + segments = [(x1, y1, x2, y2)]) + + def poly_segment(self, gc, segments, onerror = None): + request.PolySegment(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + segments = segments) + + def poly_rectangle(self, gc, rectangles, onerror = None): + request.PolyRectangle(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + rectangles = rectangles) + + def rectangle(self, gc, x, y, width, height, onerror = None): + request.PolyRectangle(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + rectangles = [(x, y, width, height)]) + + + def poly_arc(self, gc, arcs, onerror = None): + request.PolyArc(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + arcs = arcs) + + def arc(self, gc, x, y, width, height, angle1, angle2, onerror = None): + request.PolyArc(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + arcs = [(x, y, width, height, angle1, angle2)]) + + def fill_poly(self, gc, shape, coord_mode, points, onerror = None): + request.FillPoly(display = self.display, + onerror = onerror, + shape = shape, + coord_mode = coord_mode, + drawable = self.id, + gc = gc, + points = points) + + def poly_fill_rectangle(self, gc, rectangles, onerror = None): + request.PolyFillRectangle(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + rectangles = rectangles) + + def fill_rectangle(self, gc, x, y, width, height, onerror = None): + request.PolyFillRectangle(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + rectangles = [(x, y, width, height)]) + + def poly_fill_arc(self, gc, arcs, onerror = None): + request.PolyFillArc(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + arcs = arcs) + + def fill_arc(self, gc, x, y, width, height, angle1, angle2, onerror = None): + request.PolyFillArc(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + arcs = [(x, y, width, height, angle1, angle2)]) + + + def put_image(self, gc, x, y, width, height, format, + depth, left_pad, data, onerror = None): + request.PutImage(display = self.display, + onerror = onerror, + format = format, + drawable = self.id, + gc = gc, + width = width, + height = height, + dst_x = x, + dst_y = y, + left_pad = left_pad, + depth = depth, + data = data) + + # Trivial little method for putting PIL images. Will break on anything + # but depth 1 or 24... + def put_pil_image(self, gc, x, y, image, onerror = None): + width, height = image.size + if image.mode == '1': + format = X.XYBitmap + depth = 1 + if self.display.info.bitmap_format_bit_order == 0: + rawmode = '1;R' + else: + rawmode = '1' + pad = self.display.info.bitmap_format_scanline_pad + stride = roundup(width, pad) >> 3 + elif image.mode == 'RGB': + format = X.ZPixmap + depth = 24 + if self.display.info.image_byte_order == 0: + rawmode = 'BGRX' + else: + rawmode = 'RGBX' + pad = self.display.info.bitmap_format_scanline_pad + unit = self.display.info.bitmap_format_scanline_unit + stride = roundup(width * unit, pad) >> 3 + else: + raise ValueError('Unknown data format') + + maxlen = (self.display.info.max_request_length << 2) \ + - request.PutImage._request.static_size + split = maxlen // stride + + x1 = 0 + x2 = width + y1 = 0 + + while y1 < height: + h = min(height, split) + if h < height: + subimage = image.crop((x1, y1, x2, y1 + h)) + else: + subimage = image + w, h = subimage.size + data = subimage.tobytes("raw", rawmode, stride, 0) + self.put_image(gc, x, y, w, h, format, depth, 0, data) + y1 = y1 + h + y = y + h + + + def get_image(self, x, y, width, height, format, plane_mask): + return request.GetImage(display = self.display, + format = format, + drawable = self.id, + x = x, + y = y, + width = width, + height = height, + plane_mask = plane_mask) + + def draw_text(self, gc, x, y, text, onerror = None): + request.PolyText8(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + x = x, + y = y, + items = [text]) + + def poly_text(self, gc, x, y, items, onerror = None): + request.PolyText8(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + x = x, + y = y, + items = items) + + def poly_text_16(self, gc, x, y, items, onerror = None): + request.PolyText16(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + x = x, + y = y, + items = items) + + def image_text(self, gc, x, y, string, onerror = None): + request.ImageText8(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + x = x, + y = y, + string = string) + + def image_text_16(self, gc, x, y, string, onerror = None): + request.ImageText16(display = self.display, + onerror = onerror, + drawable = self.id, + gc = gc, + x = x, + y = y, + string = string) + + def query_best_size(self, item_class, width, height): + return request.QueryBestSize(display = self.display, + item_class = item_class, + drawable = self.id, + width = width, + height = height) + +class Window(Drawable): + __window__ = resource.Resource.__resource__ + + _STRING_ENCODING = 'ISO-8859-1' + _UTF8_STRING_ENCODING = 'UTF-8' + + def create_window(self, x, y, width, height, border_width, depth, + window_class = X.CopyFromParent, + visual = X.CopyFromParent, + onerror = None, + **keys): + + wid = self.display.allocate_resource_id() + request.CreateWindow(display = self.display, + onerror = onerror, + depth = depth, + wid = wid, + parent = self.id, + x = x, + y = y, + width = width, + height = height, + border_width = border_width, + window_class = window_class, + visual = visual, + attrs = keys) + + cls = self.display.get_resource_class('window', Window) + return cls(self.display, wid, owner = 1) + + def change_attributes(self, onerror = None, **keys): + request.ChangeWindowAttributes(display = self.display, + onerror = onerror, + window = self.id, + attrs = keys) + + def get_attributes(self): + return request.GetWindowAttributes(display = self.display, + window = self.id) + + def destroy(self, onerror = None): + request.DestroyWindow(display = self.display, + onerror = onerror, + window = self.id) + + self.display.free_resource_id(self.id) + + def destroy_sub_windows(self, onerror = None): + request.DestroySubWindows(display = self.display, + onerror = onerror, + window = self.id) + + + def change_save_set(self, mode, onerror = None): + request.ChangeSaveSet(display = self.display, + onerror = onerror, + mode = mode, + window = self.id) + + def reparent(self, parent, x, y, onerror = None): + request.ReparentWindow(display = self.display, + onerror = onerror, + window = self.id, + parent = parent, + x = x, + y = y) + + def map(self, onerror = None): + request.MapWindow(display = self.display, + onerror = onerror, + window = self.id) + + def map_sub_windows(self, onerror = None): + request.MapSubwindows(display = self.display, + onerror = onerror, + window = self.id) + + def unmap(self, onerror = None): + request.UnmapWindow(display = self.display, + onerror = onerror, + window = self.id) + + def unmap_sub_windows(self, onerror = None): + request.UnmapSubwindows(display = self.display, + onerror = onerror, + window = self.id) + + def configure(self, onerror = None, **keys): + request.ConfigureWindow(display = self.display, + onerror = onerror, + window = self.id, + attrs = keys) + + def circulate(self, direction, onerror = None): + request.CirculateWindow(display = self.display, + onerror = onerror, + direction = direction, + window = self.id) + + def raise_window(self, onerror = None): + """alias for raising the window to the top - as in XRaiseWindow""" + self.configure(onerror, stack_mode = X.Above) + + def query_tree(self): + return request.QueryTree(display = self.display, + window = self.id) + + def change_property(self, property, property_type, format, data, + mode = X.PropModeReplace, onerror = None): + + request.ChangeProperty(display = self.display, + onerror = onerror, + mode = mode, + window = self.id, + property = property, + type = property_type, + data = (format, data)) + + def change_text_property(self, property, property_type, data, + mode = X.PropModeReplace, onerror = None): + if not isinstance(data, bytes): + if property_type == Xatom.STRING: + data = data.encode(self._STRING_ENCODING) + elif property_type == self.display.get_atom('UTF8_STRING'): + data = data.encode(self._UTF8_STRING_ENCODING) + self.change_property(property, property_type, 8, data, + mode=mode, onerror=onerror) + + def delete_property(self, property, onerror = None): + request.DeleteProperty(display = self.display, + onerror = onerror, + window = self.id, + property = property) + + def get_property(self, property, property_type, offset, length, delete = 0): + r = request.GetProperty(display = self.display, + delete = delete, + window = self.id, + property = property, + type = property_type, + long_offset = offset, + long_length = length) + + if r.property_type: + fmt, value = r.value + r.format = fmt + r.value = value + return r + else: + return None + + def get_full_property(self, property, property_type, sizehint = 10): + prop = self.get_property(property, property_type, 0, sizehint) + if prop: + val = prop.value + if prop.bytes_after: + prop = self.get_property(property, property_type, sizehint, + prop.bytes_after // 4 + 1) + val = val + prop.value + + prop.value = val + return prop + else: + return None + + def get_full_text_property(self, property, property_type=X.AnyPropertyType, sizehint = 10): + prop = self.get_full_property(property, property_type, + sizehint=sizehint) + if prop is None or prop.format != 8: + return None + if prop.property_type == Xatom.STRING: + prop.value = prop.value.decode(self._STRING_ENCODING) + elif prop.property_type == self.display.get_atom('UTF8_STRING'): + prop.value = prop.value.decode(self._UTF8_STRING_ENCODING) + # FIXME: at least basic support for compound text would be nice. + # elif prop.property_type == self.display.get_atom('COMPOUND_TEXT'): + return prop.value + + def list_properties(self): + r = request.ListProperties(display = self.display, + window = self.id) + return r.atoms + + def set_selection_owner(self, selection, time, onerror = None): + request.SetSelectionOwner(display = self.display, + onerror = onerror, + window = self.id, + selection = selection, + time = time) + + def convert_selection(self, selection, target, property, time, onerror = None): + request.ConvertSelection(display = self.display, + onerror = onerror, + requestor = self.id, + selection = selection, + target = target, + property = property, + time = time) + + def send_event(self, event, event_mask = 0, propagate = 0, onerror = None): + request.SendEvent(display = self.display, + onerror = onerror, + propagate = propagate, + destination = self.id, + event_mask = event_mask, + event = event) + + def grab_pointer(self, owner_events, event_mask, + pointer_mode, keyboard_mode, + confine_to, cursor, time): + + r = request.GrabPointer(display = self.display, + owner_events = owner_events, + grab_window = self.id, + event_mask = event_mask, + pointer_mode = pointer_mode, + keyboard_mode = keyboard_mode, + confine_to = confine_to, + cursor = cursor, + time = time) + return r.status + + def grab_button(self, button, modifiers, owner_events, event_mask, + pointer_mode, keyboard_mode, + confine_to, cursor, onerror = None): + + request.GrabButton(display = self.display, + onerror = onerror, + owner_events = owner_events, + grab_window = self.id, + event_mask = event_mask, + pointer_mode = pointer_mode, + keyboard_mode = keyboard_mode, + confine_to = confine_to, + cursor = cursor, + button = button, + modifiers = modifiers) + + def ungrab_button(self, button, modifiers, onerror = None): + request.UngrabButton(display = self.display, + onerror = onerror, + button = button, + grab_window = self.id, + modifiers = modifiers) + + + def grab_keyboard(self, owner_events, pointer_mode, keyboard_mode, time): + r = request.GrabKeyboard(display = self.display, + owner_events = owner_events, + grab_window = self.id, + time = time, + pointer_mode = pointer_mode, + keyboard_mode = keyboard_mode) + + return r.status + + def grab_key(self, key, modifiers, owner_events, pointer_mode, keyboard_mode, onerror = None): + request.GrabKey(display = self.display, + onerror = onerror, + owner_events = owner_events, + grab_window = self.id, + modifiers = modifiers, + key = key, + pointer_mode = pointer_mode, + keyboard_mode = keyboard_mode) + + def ungrab_key(self, key, modifiers, onerror = None): + request.UngrabKey(display = self.display, + onerror = onerror, + key = key, + grab_window = self.id, + modifiers = modifiers) + + def query_pointer(self): + return request.QueryPointer(display = self.display, + window = self.id) + + def get_motion_events(self, start, stop): + r = request.GetMotionEvents(display = self.display, + window = self.id, + start = start, + stop = stop) + return r.events + + def translate_coords(self, src_window, src_x, src_y): + return request.TranslateCoords(display = self.display, + src_wid = src_window, + dst_wid = self.id, + src_x = src_x, + src_y = src_y) + + def warp_pointer(self, x, y, src_window = 0, src_x = 0, src_y = 0, + src_width = 0, src_height = 0, onerror = None): + + request.WarpPointer(display = self.display, + onerror = onerror, + src_window = src_window, + dst_window = self.id, + src_x = src_x, + src_y = src_y, + src_width = src_width, + src_height = src_height, + dst_x = x, + dst_y = y) + + def set_input_focus(self, revert_to, time, onerror = None): + request.SetInputFocus(display = self.display, + onerror = onerror, + revert_to = revert_to, + focus = self.id, + time = time) + + def clear_area(self, x = 0, y = 0, width = 0, height = 0, exposures = 0, onerror = None): + request.ClearArea(display = self.display, + onerror = onerror, + exposures = exposures, + window = self.id, + x = x, + y = y, + width = width, + height = height) + + def create_colormap(self, visual, alloc): + mid = self.display.allocate_resource_id() + request.CreateColormap(display = self.display, + alloc = alloc, + mid = mid, + window = self.id, + visual = visual) + cls = self.display.get_resource_class('colormap', colormap.Colormap) + return cls(self.display, mid, owner = 1) + + def list_installed_colormaps(self): + r = request.ListInstalledColormaps(display = self.display, + window = self.id) + return r.cmaps + + def rotate_properties(self, properties, delta, onerror = None): + request.RotateProperties(display = self.display, + onerror = onerror, + window = self.id, + delta = delta, + properties = properties) + + def set_wm_name(self, name, onerror = None): + self.change_text_property(Xatom.WM_NAME, Xatom.STRING, name, + onerror = onerror) + + def get_wm_name(self): + return self.get_full_text_property(Xatom.WM_NAME, Xatom.STRING) + + def set_wm_icon_name(self, name, onerror = None): + self.change_text_property(Xatom.WM_ICON_NAME, Xatom.STRING, name, + onerror = onerror) + + def get_wm_icon_name(self): + return self.get_full_text_property(Xatom.WM_ICON_NAME, Xatom.STRING) + + def set_wm_class(self, inst, cls, onerror = None): + self.change_text_property(Xatom.WM_CLASS, Xatom.STRING, + '%s\0%s\0' % (inst, cls), + onerror = onerror) + + def get_wm_class(self): + value = self.get_full_text_property(Xatom.WM_CLASS, Xatom.STRING) + if value is None: + return None + parts = value.split('\0') + if len(parts) < 2: + return None + else: + return parts[0], parts[1] + + def set_wm_transient_for(self, window, onerror = None): + self.change_property(Xatom.WM_TRANSIENT_FOR, Xatom.WINDOW, + 32, [window.id], + onerror = onerror) + + def get_wm_transient_for(self): + d = self.get_property(Xatom.WM_TRANSIENT_FOR, Xatom.WINDOW, 0, 1) + if d is None or d.format != 32 or len(d.value) < 1: + return None + else: + cls = self.display.get_resource_class('window', Window) + return cls(self.display, d.value[0]) + + + def set_wm_protocols(self, protocols, onerror = None): + self.change_property(self.display.get_atom('WM_PROTOCOLS'), + Xatom.ATOM, 32, protocols, + onerror = onerror) + + def get_wm_protocols(self): + d = self.get_full_property(self.display.get_atom('WM_PROTOCOLS'), Xatom.ATOM) + if d is None or d.format != 32: + return [] + else: + return d.value + + def set_wm_colormap_windows(self, windows, onerror = None): + self.change_property(self.display.get_atom('WM_COLORMAP_WINDOWS'), + Xatom.WINDOW, 32, + map(lambda w: w.id, windows), + onerror = onerror) + + def get_wm_colormap_windows(self): + d = self.get_full_property(self.display.get_atom('WM_COLORMAP_WINDOWS'), + Xatom.WINDOW) + if d is None or d.format != 32: + return [] + else: + cls = self.display.get_resource_class('window', Window) + return map(lambda i, d = self.display, c = cls: c(d, i), + d.value) + + + def set_wm_client_machine(self, name, onerror = None): + self.change_text_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING, name, + onerror = onerror) + + def get_wm_client_machine(self): + return self.get_full_text_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING) + + def set_wm_normal_hints(self, hints = {}, onerror = None, **keys): + self._set_struct_prop(Xatom.WM_NORMAL_HINTS, Xatom.WM_SIZE_HINTS, + icccm.WMNormalHints, hints, keys, onerror) + + def get_wm_normal_hints(self): + return self._get_struct_prop(Xatom.WM_NORMAL_HINTS, Xatom.WM_SIZE_HINTS, + icccm.WMNormalHints) + + def set_wm_hints(self, hints = {}, onerror = None, **keys): + self._set_struct_prop(Xatom.WM_HINTS, Xatom.WM_HINTS, + icccm.WMHints, hints, keys, onerror) + + def get_wm_hints(self): + return self._get_struct_prop(Xatom.WM_HINTS, Xatom.WM_HINTS, + icccm.WMHints) + + def set_wm_state(self, hints = {}, onerror = None, **keys): + atom = self.display.get_atom('WM_STATE') + self._set_struct_prop(atom, atom, icccm.WMState, hints, keys, onerror) + + def get_wm_state(self): + atom = self.display.get_atom('WM_STATE') + return self._get_struct_prop(atom, atom, icccm.WMState) + + def set_wm_icon_size(self, hints = {}, onerror = None, **keys): + self._set_struct_prop(Xatom.WM_ICON_SIZE, Xatom.WM_ICON_SIZE, + icccm.WMIconSize, hints, keys, onerror) + + def get_wm_icon_size(self): + return self._get_struct_prop(Xatom.WM_ICON_SIZE, Xatom.WM_ICON_SIZE, + icccm.WMIconSize) + + # Helper function for getting structured properties. + # pname and ptype are atoms, and pstruct is a Struct object. + # Returns a DictWrapper, or None + + def _get_struct_prop(self, pname, ptype, pstruct): + r = self.get_property(pname, ptype, 0, pstruct.static_size // 4) + if r and r.format == 32: + value = rq.encode_array(r.value) + if len(value) == pstruct.static_size: + return pstruct.parse_binary(value, self.display)[0] + + return None + + # Helper function for setting structured properties. + # pname and ptype are atoms, and pstruct is a Struct object. + # hints is a mapping or a DictWrapper, keys is a mapping. keys + # will be modified. onerror is the error handler. + + def _set_struct_prop(self, pname, ptype, pstruct, hints, keys, onerror): + if isinstance(hints, rq.DictWrapper): + keys.update(hints._data) + else: + keys.update(hints) + + value = pstruct.to_binary(*(), **keys) + + self.change_property(pname, ptype, 32, value, onerror = onerror) + + +class Pixmap(Drawable): + __pixmap__ = resource.Resource.__resource__ + + def free(self, onerror = None): + request.FreePixmap(display = self.display, + onerror = onerror, + pixmap = self.id) + + self.display.free_resource_id(self.id) + + def create_cursor(self, mask, foreground, background, x, y): + fore_red, fore_green, fore_blue = foreground + back_red, back_green, back_blue = background + cid = self.display.allocate_resource_id() + request.CreateCursor(display = self.display, + cid = cid, + source = self.id, + mask = mask, + fore_red = fore_red, + fore_green = fore_green, + fore_blue = fore_blue, + back_red = back_red, + back_green = back_green, + back_blue = back_blue, + x = x, + y = y) + cls = self.display.get_resource_class('cursor', cursor.Cursor) + return cls(self.display, cid, owner = 1) + + +def roundup(value, unit): + return (value + (unit - 1)) & ~(unit - 1) diff --git a/Xlib/xobject/fontable.py b/Xlib/xobject/fontable.py new file mode 100644 index 0000000..892f266 --- /dev/null +++ b/Xlib/xobject/fontable.py @@ -0,0 +1,110 @@ +# Xlib.xobject.fontable -- fontable objects (GC, font) +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +from Xlib.protocol import request + +from . import resource +from . import cursor + +class Fontable(resource.Resource): + __fontable__ = resource.Resource.__resource__ + + def query(self): + return request.QueryFont(display = self.display, + font = self.id) + + def query_text_extents(self, string): + return request.QueryTextExtents(display = self.display, + font = self.id, + string = string) + + +class GC(Fontable): + __gc__ = resource.Resource.__resource__ + + def change(self, onerror = None, **keys): + request.ChangeGC(display = self.display, + onerror = onerror, + gc = self.id, + attrs = keys) + + + def copy(self, src_gc, mask, onerror = None): + request.CopyGC(display = self.display, + onerror = onerror, + src_gc = src_gc, + dst_gc = self.id, + mask = mask) + + def set_dashes(self, offset, dashes, onerror = None): + request.SetDashes(display = self.display, + onerror = onerror, + gc = self.id, + dash_offset = offset, + dashes = dashes) + + def set_clip_rectangles(self, x_origin, y_origin, rectangles, ordering, onerror = None): + request.SetClipRectangles(display = self.display, + onerror = onerror, + ordering = ordering, + gc = self.id, + x_origin = x_origin, + y_origin = y_origin, + rectangles = rectangles) + def free(self, onerror = None): + request.FreeGC(display = self.display, + onerror = onerror, + gc = self.id) + + self.display.free_resource_id(self.id) + + + +class Font(Fontable): + __font__ = resource.Resource.__resource__ + + def close(self, onerror = None): + request.CloseFont(display = self.display, + onerror = onerror, + font = self.id) + self.display.free_resource_id(self.id) + + def create_glyph_cursor(self, mask, source_char, mask_char, + foreground, background): + fore_red, fore_green, fore_blue = foreground + back_red, back_green, back_blue = background + + cid = self.display.allocate_resource_id() + request.CreateGlyphCursor(display = self.display, + cid = cid, + source = self.id, + mask = mask, + source_char = source_char, + mask_char = mask_char, + fore_red = fore_red, + fore_green = fore_green, + fore_blue = fore_blue, + back_red = back_red, + back_green = back_green, + back_blue = back_blue) + + cls = self.display.get_resource_class('cursor', cursor.Cursor) + return cls(self.display, cid, owner = 1) diff --git a/Xlib/xobject/icccm.py b/Xlib/xobject/icccm.py new file mode 100644 index 0000000..d445e6d --- /dev/null +++ b/Xlib/xobject/icccm.py @@ -0,0 +1,75 @@ +# Xlib.xobject.icccm -- ICCCM structures +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +from Xlib import X, Xutil +from Xlib.protocol import rq + +Aspect = rq.Struct( rq.Int32('num'), rq.Int32('denum') ) + +WMNormalHints = rq.Struct( rq.Card32('flags'), + rq.Pad(16), + rq.Int32('min_width', default = 0), + rq.Int32('min_height', default = 0), + rq.Int32('max_width', default = 0), + rq.Int32('max_height', default = 0), + rq.Int32('width_inc', default = 0), + rq.Int32('height_inc', default = 0), + rq.Object('min_aspect', Aspect, default = (0, 0)), + rq.Object('max_aspect', Aspect, default = (0, 0)), + rq.Int32('base_width', default = 0), + rq.Int32('base_height', default = 0), + rq.Int32('win_gravity', default = 0), + ) + +WMHints = rq.Struct( rq.Card32('flags'), + rq.Card32('input', default = 0), + rq.Set('initial_state', 4, + # withdrawn is totally bogus according to + # ICCCM, but some window managers seem to + # use this value to identify dockapps. + # Oh well. + ( Xutil.WithdrawnState, + Xutil.NormalState, + Xutil.IconicState ), + default = Xutil.NormalState), + rq.Pixmap('icon_pixmap', default = 0), + rq.Window('icon_window', default = 0), + rq.Int32('icon_x', default = 0), + rq.Int32('icon_y', default = 0), + rq.Pixmap('icon_mask', default = 0), + rq.Window('window_group', default = 0), + ) + +WMState = rq.Struct( rq.Set('state', 4, + ( Xutil.WithdrawnState, + Xutil.NormalState, + Xutil.IconicState )), + rq.Window('icon', ( X.NONE, )), + ) + + +WMIconSize = rq.Struct( rq.Card32('min_width'), + rq.Card32('min_height'), + rq.Card32('max_width'), + rq.Card32('max_height'), + rq.Card32('width_inc'), + rq.Card32('height_inc'), + ) diff --git a/Xlib/xobject/resource.py b/Xlib/xobject/resource.py new file mode 100644 index 0000000..ea256ca --- /dev/null +++ b/Xlib/xobject/resource.py @@ -0,0 +1,54 @@ +# Xlib.xobject.resource -- any X resource object +# +# Copyright (C) 2000 Peter Liljenberg +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +from Xlib.protocol import request + +class Resource(object): + def __init__(self, display, rid, owner = 0): + self.display = display + self.id = rid + self.owner = owner + + def __resource__(self): + return self.id + + def __eq__(self, obj): + if isinstance(obj, Resource): + if self.display == obj.display: + return self.id == obj.id + else: + return False + else: + return id(self) == id(obj) + + def __ne__(self, obj): + return not self == obj + + def __hash__(self): + return int(self.id) + + def __repr__(self): + return '<%s 0x%08x>' % (self.__class__.__name__, self.id) + + def kill_client(self, onerror = None): + request.KillClient(display = self.display, + onerror = onerror, + resource = self.id) diff --git a/charaters/__pycache__/personnage.cpython-38.pyc b/charaters/__pycache__/personnage.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..763ded81f49fca07070ef53b330dff12f971aefb GIT binary patch literal 2921 zcmcImO>Y}T7~a`0uh&VMw4v>x(1lXyQqmxVpo$PmmAWA+m?%jr%hG zrIxwS{)?&-96026@Gtht$$x+YC!TlKUb{|;0U0u)OSQ_z{|q#?QPM2{*Es8HJ=SDQ+ts+vjTag>p6g8=Et6YlS!fw(*}Q^Q1uc_1hnim< zW4`9IyduMxx8p3L_WY-#v5k_h1BBLOTx;r_as7qXG`N8^%-lK5!Yn-$SyaKG=|?op z-a$!k07h)21=>glvXKGQM<&o1SwM4S11+u{YNxE>+qvFKax=`H?B^yI$G}Lov%E?u z!%Xbw_DK@;#j#(>%~W)crG+h|O+Y(Ks7;eq0#fCvCYMnCy|vlu_rzw{6($Advm8*o;~s?I6?51kjJ;@Ugg+JjDY7H&<) zHApu>5QjYx1i2doy*?jwiCzkVr-QItaO4VBQpcNvW$Gg{Q%OmWj;`8gEJV_7)1c1) zQws@$Y{U)NUx)SWrimUK?FzTJ!>j1AxC`sm^6E}EOw*%hi5QcO$E2^`U;9PHH(n$GukcK3Gn>Ww+EXkDpfI}eJ8)C4%} zJd@OOMmmyw8zub?FqLlRf82y0!pFId+JbZrYMWQNi@L&VdoqhTdO(O1~q+bAD^IWaNI^@GfX5t*>|Ge;D;@ITHzR{2jbH@a5QX}s7 zayKGf2N{&D{53xp%+3%5twiZCw^9nI1?EZO8?uJQT9IkZbBL zTW7-yvrS6pjObj`kF-p7P!fL$B12B$`g8qf#O$vq9le8y?U?J@Z^$yxN70AK?bsdb z5_Y{rkqepM#cX*2;8!Fq3z=JC{8$9yBqyk zq?inQO$ss?6=fvS0F)1@{MS;TBIcd6BDB9}^N!^72&nuaxYu zlTpC1UvNRrf`8~iayY&R=M^zey+Q0D4?L2>OZsU$Yac^J6biZfFzgOQUCO?kYRxX@ zE{LEU?{hkT5d;of)raq#RTy?XqcKY8%A$XPpb&=#v6tE_oSRV_$%_Jgu15v^TpJg5 z$C_(flbJ|NK$X0j;?+tXsFJs& zcuOS@RKZi0S#AuiM!&b^4Oi*T^SJO-QWog39=1^p=vu0uB!8UBwumEQTv2zsccU2T z-Sm?3=J2*M7@Yxl z;-mnab8M9v)U)6U7<7B2^sI20<2n|8-La`i;x>G%Y14DH-%}Tix-!+{L9ySoYVLep SJiWf5{HX$GNndeS?0*0>#z7|l literal 0 HcmV?d00001 diff --git a/cli/__pycache__/colors.cpython-38.pyc b/cli/__pycache__/colors.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4282fbd7e7f45be244ced17110e48659ada5db6a GIT binary patch literal 1108 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 O1gl8jX~>4ty!bEAB@@d4 literal 0 HcmV?d00001 diff --git a/cli/layer/__pycache__/menu.cpython-38.pyc b/cli/layer/__pycache__/menu.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c788192f425c532ed40e8022bf2296a02cb7ebac GIT binary patch literal 896 zcmYjP!EVz)5S`hzozzWKR0#x9B+lUwUy!&U1WBqCQ6dCIAWLW!ye=d-cDuIKrgBd0 znL8Km{F1Mn@(Y}pSu2p0=6QBz{C4K;zVCK@p!MVQVf0Y|{N~N>lC*hFmOmoUpfQ0Z zhKTHoL@Z?}5hkFec5XpCHzIVj`wsfvhU%e@wKvL=%;e;`C$m*-B98AaI<+~WM@|eO zY6yi!Ep8!{4eyq-c3#J68F5eB#Xd8P$ntvx6;`025`?JP7aFJ^F*QipzN}@G%>!aPl}gGcHvbkKh-WJ^K849DL1|aWG5bt68c|9E`K5T+%Oju!u`# zvLp_OGDvv3fD+~4d{wuVny2$ZDdx+W56}}HUUzwf!8RLlQ`^O*Txn-9IV4a+Sg`wscbnnkslHO z9-@OjcJK~akG$&xE9U>&Y2Rh#)7dgss`iyyX1Yw+?nmU-i|#0ssI2 literal 0 HcmV?d00001 diff --git a/cli/layer/menu.py b/cli/layer/menu.py new file mode 100644 index 0000000..b9fd02d --- /dev/null +++ b/cli/layer/menu.py @@ -0,0 +1,20 @@ +from cli.colors import Colors +from cli.writer import Layer + +class Menu: + def __init__(self, cli, buttons): + self.cli = cli + #self.stats = stats + self.buttons = buttons + self.current = 0 + + def draw_layer(self): + layer = Layer(self.cli.x, self.cli.y) + x = 1 + for button in self.buttons: + color = (Colors.WHITEBG, Colors.BLACK) + if self.buttons[self.current] == button: + color = (Colors.REDBG, Colors.BLACK) + layer.put_string(button, x, self.cli.y-10, *color) + x += 11 + return layer \ No newline at end of file diff --git a/cli/cli.py b/cli/writer.py similarity index 77% rename from cli/cli.py rename to cli/writer.py index fad9763..8f81abf 100644 --- a/cli/cli.py +++ b/cli/writer.py @@ -1,12 +1,12 @@ import os -from time import sleep +#from time import sleep from typing import Dict -from colors import Colors +from cli.colors import Colors class Layer: def __init__(self, x, y): self.screen = {} - self.x = x #largeur + self.x = x #largeur self.y = y #hauteur def clear(self): @@ -54,7 +54,7 @@ class CLI: self.screen[(x, y)] = layers[layer_name].screen[(x, y)] def draw(self): - cli.combine_layers() + self.combine_layers() content = "" for y in range(self.y): #content += "\n" @@ -67,17 +67,17 @@ class CLI: print("\033[H\033[J", end="") print(content) -if __name__ == "__main__": - cli = CLI() - layer = Layer(cli.x, cli.y) - layer.put_char("t", 5, 6, Colors.BLUEBG, Colors.BLACK) - layer.put_string("""test""", 8, 9, Colors.BEIGEBG) - - cli.set_layer("test", layer) - ##cli.put_char("t", 5, 6, Colors.BLUEBG, Colors.BLACK) - #cli.put_string("tttaaaaaa ttt \n ttttttttttnnnnnn t", 8, 9, Colors.BEIGEBG) - #cli.draw() - - while True: - cli.draw() - sleep(1) \ No newline at end of file +#if __name__ == "__main__": +# cli = CLI() +# layer = Layer(cli.x, cli.y) +# layer.put_char("t", 5, 6, Colors.BLUEBG, Colors.BLACK) +# layer.put_string("""test""", 8, 9, Colors.BEIGEBG) +# +# cli.set_layer("test", layer) +# ##cli.put_char("t", 5, 6, Colors.BLUEBG, Colors.BLACK) +# #cli.put_string("tttaaaaaa ttt \n ttttttttttnnnnnn t", 8, 9, Colors.BEIGEBG) +# #cli.draw() +# +# while True: +# cli.draw() +# sleep(1) \ No newline at end of file diff --git a/listener.py b/listener.py new file mode 100644 index 0000000..3b5e880 --- /dev/null +++ b/listener.py @@ -0,0 +1,16 @@ +from pynput.keyboard import Key, Listener +import threading + +def start_listener(): + + + +def show(key): + + print('\nYou Entered {0}'.format( key)) + + if key == Key.delete: + return False + +with Listener(on_press = show) as listener: + listener.start() \ No newline at end of file diff --git a/main.py b/main.py index 4c9198a..d7a4f12 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,25 @@ -from personnage import * +from charaters.personnage import Personnage +from cli.colors import Colors +from cli.layer.menu import Menu +from cli.writer import * +from time import sleep +from pynput.keyboard import Key, Listener + +game = CLI() +menu = Menu(game, ["Inventaire", "Teset"]) + +def show(key): + if key == Key.esc: + menu.current = 1 + + +def draw(): + game.set_layer("menu", menu.draw_layer()) if __name__ == "__main__": - personnage = Personnage() \ No newline at end of file + #personnage = Personnage() + with Listener(on_press = show) as listener: + while True: + draw() + game.draw() + sleep(1) \ No newline at end of file diff --git a/pynput/__init__.py b/pynput/__init__.py new file mode 100644 index 0000000..cdc1f0e --- /dev/null +++ b/pynput/__init__.py @@ -0,0 +1,41 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +The main *pynput* module. + +This module imports ``keyboard`` and ``mouse``. +""" + +def _logger(cls): + """Creates a logger with a name suitable for a specific class. + + This function takes into account that implementations for classes reside in + platform dependent modules, and thus removes the final part of the module + name. + + :param type cls: The class for which to create a logger. + + :return: a logger + """ + import logging + return logging.getLogger('{}.{}'.format( + '.'.join(cls.__module__.split('.', 2)[:2]), + cls.__name__)) + + +from . import keyboard +from . import mouse diff --git a/pynput/__pycache__/__init__.cpython-38.pyc b/pynput/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a9ff0ef776c503d46d3cd6b4fa983a1d77e257b3 GIT binary patch literal 866 zcmYjPv2NQi5G7^FR-G7arlOrV) zkG`WZ<}Dr6w?CnN=k~JuITxg4QjybYsjAwYl2Y@!5Q8p!l6D^v zSytLOOD2wRCJ+ z2r|?d| zV^4GhC9EWFSqT`xj)1v$V3NS*CYM=GfOi%Yc@X5#ZG@|h8a?C#{1(JVSGURCYH+t| z@L=n&rr&8gUQDUx<0o_BE`zX5+fT(@6ECz>O_$OgO-h@@R)us;N~xzyX+LUOz|h11 z_6wOKc>R4l%ylV-tPtN|HH;X3&{^#TwZny|snLZP?zU`5sZ`QYI;fUShwiTRIbhhZ z=SL|{@k<>4Gd;*{{a*EPlO9$N{3LAMRX>4l%5_F*C~c5=3dQD`H=ffYa|~gVp6w<& PKY_@$!Nl$K. + +__author__ = u'Moses Palmér' +__version__ = (1, 7, 6) diff --git a/pynput/_util/__init__.py b/pynput/_util/__init__.py new file mode 100644 index 0000000..070a935 --- /dev/null +++ b/pynput/_util/__init__.py @@ -0,0 +1,465 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +General utility functions and classes. +""" + +# pylint: disable=R0903 +# We implement minimal mixins + +# pylint: disable=W0212 +# We implement an internal API + +import contextlib +import functools +import importlib +import os +import sys +import threading +import time + +import six + +from six.moves import queue + + +#: Possible resolutions for import related errors. +RESOLUTIONS = { + 'darwin': 'Please make sure that you have Python bindings for the ' + 'system frameworks installed', + 'uinput': 'Please make sure that you are running as root, and that ' + 'the utility dumpkeys is installed', + 'xorg': 'Please make sure that you have an X server running, and that ' + 'the DISPLAY environment variable is set correctly'} + + +def backend(package): + """Returns the backend module for a package. + + :param str package: The package for which to load a backend. + """ + backend_name = os.environ.get( + 'PYNPUT_BACKEND_{}'.format(package.rsplit('.')[-1].upper()), + os.environ.get('PYNPUT_BACKEND', None)) + if backend_name: + modules = [backend_name] + elif sys.platform == 'darwin': + modules = ['darwin'] + elif sys.platform == 'win32': + modules = ['win32'] + else: + modules = ['xorg'] + + errors = [] + resolutions = [] + for module in modules: + try: + return importlib.import_module('._' + module, package) + except ImportError as e: + errors.append(e) + if module in RESOLUTIONS: + resolutions.append(RESOLUTIONS[module]) + + raise ImportError('this platform is not supported: {}'.format( + '; '.join(str(e) for e in errors)) + ('\n\n' + 'Try one of the following resolutions:\n\n' + + '\n\n'.join( + ' * {}'.format(s) + for s in resolutions)) + if resolutions else '') + + +def prefix(base, cls): + """Calculates the prefix to use for platform specific options for a + specific class. + + The prefix if the name of the module containing the class that is an + immediate subclass of ``base`` among the super classes of ``cls``. + """ + for super_cls in filter( + lambda cls: issubclass(cls, base), + cls.__mro__[1:]): + if super_cls is base: + return cls.__module__.rsplit('.', 1)[-1][1:] + '_' + else: + result = prefix(base, super_cls) + if result is not None: + return result + + +class AbstractListener(threading.Thread): + """A class implementing the basic behaviour for event listeners. + + Instances of this class can be used as context managers. This is equivalent + to the following code:: + + listener.start() + listener.wait() + try: + with_statements() + finally: + listener.stop() + + Actual implementations of this class must set the attribute ``_log``, which + must be an instance of :class:`logging.Logger`. + + :param bool suppress: Whether to suppress events. Setting this to ``True`` + will prevent the input events from being passed to the rest of the + system. + + :param kwargs: A mapping from callback attribute to callback handler. All + handlers will be wrapped in a function reading the return value of the + callback, and if it ``is False``, raising :class:`StopException`. + + Any callback that is falsy will be ignored. + """ + class StopException(Exception): + """If an event listener callback raises this exception, the current + listener is stopped. + """ + pass + + #: Exceptions that are handled outside of the emitter and should thus not + #: be passed through the queue + _HANDLED_EXCEPTIONS = tuple() + + def __init__(self, suppress=False, **kwargs): + super(AbstractListener, self).__init__() + + def wrapper(f): + def inner(*args): + if f(*args) is False: + raise self.StopException() + return inner + + self._suppress = suppress + self._running = False + self._thread = threading.current_thread() + self._condition = threading.Condition() + self._ready = False + + # Allow multiple calls to stop + self._queue = queue.Queue(10) + + self.daemon = True + + for name, callback in kwargs.items(): + setattr(self, name, wrapper(callback or (lambda *a: None))) + + @property + def suppress(self): + """Whether to suppress events. + """ + return self._suppress + + @property + def running(self): + """Whether the listener is currently running. + """ + return self._running + + def stop(self): + """Stops listening for events. + + When this method returns, no more events will be delivered. Once this + method has been called, the listener instance cannot be used any more, + since a listener is a :class:`threading.Thread`, and once stopped it + cannot be restarted. + + To resume listening for event, a new listener must be created. + """ + if self._running: + self._running = False + self._queue.put(None) + self._stop_platform() + + def __enter__(self): + self.start() + self.wait() + return self + + def __exit__(self, exc_type, value, traceback): + self.stop() + + def wait(self): + """Waits for this listener to become ready. + """ + self._condition.acquire() + while not self._ready: + self._condition.wait() + self._condition.release() + + def run(self): + """The thread runner method. + """ + self._running = True + self._thread = threading.current_thread() + self._run() + + # Make sure that the queue contains something + self._queue.put(None) + + @classmethod + def _emitter(cls, f): + """A decorator to mark a method as the one emitting the callbacks. + + This decorator will wrap the method and catch exception. If a + :class:`StopException` is caught, the listener will be stopped + gracefully. If any other exception is caught, it will be propagated to + the thread calling :meth:`join` and reraised there. + """ + @functools.wraps(f) + def inner(self, *args, **kwargs): + # pylint: disable=W0702; we want to catch all exception + try: + return f(self, *args, **kwargs) + except Exception as e: + if not isinstance(e, self._HANDLED_EXCEPTIONS): + if not isinstance(e, AbstractListener.StopException): + self._log.exception( + 'Unhandled exception in listener callback') + self._queue.put( + None if isinstance(e, cls.StopException) + else sys.exc_info()) + self.stop() + raise + # pylint: enable=W0702 + + return inner + + def _mark_ready(self): + """Marks this listener as ready to receive events. + + This method must be called from :meth:`_run`. :meth:`wait` will block + until this method is called. + """ + self._condition.acquire() + self._ready = True + self._condition.notify() + self._condition.release() + + def _run(self): + """The implementation of the :meth:`run` method. + + This is a platform dependent implementation. + """ + raise NotImplementedError() + + def _stop_platform(self): + """The implementation of the :meth:`stop` method. + + This is a platform dependent implementation. + """ + raise NotImplementedError() + + def join(self, timeout=None, *args): + start = time.time() + super(AbstractListener, self).join(timeout, *args) + timeout = max(0.0, timeout - (time.time() - start)) \ + if timeout is not None \ + else None + + # Reraise any exceptions; make sure not to block if a timeout was + # provided + try: + exc_type, exc_value, exc_traceback = self._queue.get( + timeout=timeout) + six.reraise(exc_type, exc_value, exc_traceback) + except queue.Empty: + pass + except TypeError: + return + + +class Events(object): + """A base class to enable iterating over events. + """ + #: The listener class providing events. + _Listener = None + + class Event(object): + def __str__(self): + return '{}({})'.format( + self.__class__.__name__, + ', '.join( + '{}={}'.format(k, v) + for (k, v) in vars(self).items())) + + def __eq__(self, other): + return self.__class__ == other.__class__ \ + and dir(self) == dir(other) \ + and all( + getattr(self, k) == getattr(other, k) + for k in dir(self)) + + def __init__(self, *args, **kwargs): + super(Events, self).__init__() + self._event_queue = queue.Queue() + self._sentinel = object() + self._listener = self._Listener(*args, **{ + key: self._event_mapper(value) + for (key, value) in kwargs.items()}) + self.start = self._listener.start + + def __enter__(self): + self._listener.__enter__() + return self + + def __exit__(self, *args): + self._listener.__exit__(*args) + + # Drain the queue to ensure that the put does not block + while True: + try: + self._event_queue.get_nowait() + except queue.Empty: + break + + self._event_queue.put(self._sentinel) + + def __iter__(self): + return self + + def __next__(self): + event = self.get() + if event is not None: + return event + else: + raise StopIteration() + + def get(self, timeout=None): + """Attempts to read the next event. + + :param int timeout: An optional timeout. If this is not provided, this + method may block infinitely. + + :return: the next event, or ``None`` if the source has been stopped or + no events were received + """ + try: + event = self._event_queue.get(timeout=timeout) + return event if event is not self._sentinel else None + except queue.Empty: + return None + + def _event_mapper(self, event): + """Generates an event callback to transforms the callback arguments to + an event and then publishes it. + + :param callback event: A function generating an event object. + + :return: a callback + """ + @functools.wraps(event) + def inner(*args): + try: + self._event_queue.put(event(*args), block=False) + except queue.Full: + pass + + return inner + + +class NotifierMixin(object): + """A mixin for notifiers of fake events. + + This mixin can be used for controllers on platforms where sending fake + events does not cause a listener to receive a notification. + """ + def _emit(self, action, *args): + """Sends a notification to all registered listeners. + + This method will ensure that listeners that raise + :class:`StopException` are stopped. + + :param str action: The name of the notification. + + :param args: The arguments to pass. + """ + stopped = [] + for listener in self._listeners(): + try: + getattr(listener, action)(*args) + except listener.StopException: + stopped.append(listener) + for listener in stopped: + listener.stop() + + @classmethod + def _receiver(cls, listener_class): + """A decorator to make a class able to receive fake events from a + controller. + + This decorator will add the method ``_receive`` to the decorated class. + + This method is a context manager which ensures that all calls to + :meth:`_emit` will invoke the named method in the listener instance + while the block is active. + """ + @contextlib.contextmanager + def receive(self): + """Executes a code block with this listener instance registered as + a receiver of fake input events. + """ + self._controller_class._add_listener(self) + try: + yield + finally: + self._controller_class._remove_listener(self) + + listener_class._receive = receive + listener_class._controller_class = cls + + # Make sure this class has the necessary attributes + if not hasattr(cls, '_listener_cache'): + cls._listener_cache = set() + cls._listener_lock = threading.Lock() + + return listener_class + + @classmethod + def _listeners(cls): + """Iterates over the set of running listeners. + + This method will quit without acquiring the lock if the set is empty, + so there is potential for race conditions. This is an optimisation, + since :class:`Controller` will need to call this method for every + control event. + """ + if not cls._listener_cache: + return + with cls._listener_lock: + for listener in cls._listener_cache: + yield listener + + @classmethod + def _add_listener(cls, listener): + """Adds a listener to the set of running listeners. + + :param listener: The listener for fake events. + """ + with cls._listener_lock: + cls._listener_cache.add(listener) + + @classmethod + def _remove_listener(cls, listener): + """Removes this listener from the set of running listeners. + + :param listener: The listener for fake events. + """ + with cls._listener_lock: + cls._listener_cache.remove(listener) diff --git a/pynput/_util/__pycache__/__init__.cpython-38.pyc b/pynput/_util/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d090251ee6688f7ae632a0932cb2ad78ca341a64 GIT binary patch literal 15192 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 literal 0 HcmV?d00001 diff --git a/pynput/_util/__pycache__/xorg.cpython-38.pyc b/pynput/_util/__pycache__/xorg.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6d134b47a2f26e553dd477a0f24f4ec88029e01 GIT binary patch literal 12345 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%0D~UCzP&5to1nO1yXHZhj zNpzoCAj|cjob{2HNVAppv z`QzQY18Y#<0MrMyE86beU9o#*K*4Ua{~zN!o*W#!=NYL@V2?5k7nmV(hb7`|0S;Ig zhj~TAbh7fRNx{laUZdqMP1ga@+S2(qlRHcGYxvut}OUDxebCng)Ov- zX_&fIsOk&Qv4&w7}d|r;u6ve*nFeigou#cpdX8fjYK9I z@Pvg9=q(b=kTyuCs)MZlpN*8N-RlH=;x~*7(QaTANXnOX6tbs^N~Jx~G_O34gGr5? zL3;VTGRxnAea6lheqPJ#bfSbd%^O$^h3ZEX%nmRmoZsqcA(EnDq$Z8;l#vNKUO3I| zI(;3*_iqnXOql}grXd}4H^D#D7@aQDvIxCQSta+0hzb( zi#vFXpJ4muf#wu_-m@J-H6Q6tm99_VV#16liW1&6Q5F{7v!WuZc-Q?oF(YbxNn+kV zvArN>MV)ge#hjQ&jYV-nEZ}{L55P{Qv$uHP?#mB?`$4DwTUYHiCD`oqA@5x}(;)wg z!cw_P#c@Y|*C7k`;II%vqs~Ss#Iiy>Tv>Ooe{y~G_8QKBp|OXdDprLnZNzL*Lme){ z{iQ9y>I5`NtjPjiBlcVXQxz;2a3w$}Hu33y)69tE-@vPx@=E$IP^-fJkdtLj!)-mp6=j`a~Kd1VtAJbRi zaE|&?bZi$6=QB#o!xIzb3&V0w-O=dUN29d*u0pxNFO@b&=~1dtab_V`zDriskCWJ` zW!Y|s&8$=N?gGb*P(%?m#a;Gz1$g=dKC9zdgiH)#1;R-VP`IrSF$=_D>pghWKp znaT1Z@I_-u2_ao7kreDmXWjhRI8LH&IycNpgm$EGEc6qxD^Dka&M8&wwm94ftJO(B zVOpR^DQi-_8iv=H2Aoi4u*8*&ysi}^ga*$!PU~)-_Tnw) zWcGPO`onLsLX(&oyUl`3+mkzBp(pk*pRpmpkKqLT&cqN_eyUDtKla0o)DZ1y?PI#K z*V!kHL*Ao?bGjNGsk8O08e~D$KV?mg|1SM>e$K>qi)$_7X=>(U%P1lKMC}&!I&R$1 zufH&%94^p66Y@!>gVYzxCppVxdiiV?cT3c*5}=e$>me0m4VK--^`(R&cYjWkChwT92o?IzOp!ZL`3Wzdh-bn{ z(I?u0hHEbH6doG=AFdHdacv}n1ns00v@`oon(9kmBRO^>m3PfbkGiA3d<>O?GQ_#i z$<-*ik%#zwah)zOJv0<|SY9SsB-@zGUlLzPcWR~nv(r4u=A;Di%Q8jw(3!kTkNfnP z8gvU~e>*nl6ALjKH0nNqAll^fLeRC41h`4CpCQBu=w`x8f9W>-Y32?e=f@twI=vR z?!j=6wu%gBhxu0$@B>()ZDZdU81j7_8(|+N->119;%*AwFz#fDj!eF}f|5fV32j#f zaIA27Z!tR-dKN?x3+Pe;T_(idXOk>4rmEuYWU0yOwj;W=lH+IyE)GzUxs9kkE+)X4 zMLpW3s9*syWT2^kF3ksu$3`0VhEt~enjY>G;6UI>sZbOsr%t6YB(6sCO*#;J0T-+g z?4)A_y9qZ|?x_oR!!bw%h)7b0bo*|?op3+A`qA3;k205ms;)MwX&Kt715r(9*PtY> z^I;K)OHJWHoUdbDU{=^2$=W*$%m!Bx;Ii+c^bp@}Ea%GCLsOjGl0tGyYBut>skKY6+JYOSs=e8=Mk0DhMPQ{nJy($}U(#YP3aFa@QTjLz z{V&Oi@$h-ut2L6HY(ir*8D|!8ir5N)fIkp*C9!MNcwF#Q{5yz;pv~98zJ~)$ z<%qQWYv^PLGZJWqCLpB5T0?jEUDsBd#)+5wm6c$rj=OY?=n?4^Z~6LylMb zr;Y_=~%a9~gNFsEMc-GwH3&?dmc4-E51ra;jQmKIG z!Ln7h@Q0`YqP5kNR+Z~DUQ~ovGv5vODka?s2ZsL>FY# z`Qq!8qEkT*@^HLTlAp(M5Z=u5t}-n}&27Jx@N|#kS882Bj0{(f-#mfVF~v_!4hc|L mAo~X`RWq#;G*w}~4E8;1owDZmU)8EwFIiVO{i;>B=Kc?e?an3u literal 0 HcmV?d00001 diff --git a/pynput/_util/__pycache__/xorg_keysyms.cpython-38.pyc b/pynput/_util/__pycache__/xorg_keysyms.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f27a0aff6b5ff1eedef439b10cdc62e982884fb4 GIT binary patch literal 54792 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. +""" +Utility functions and classes for the *Darwin* backend. +""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# This module contains wrapper classes + +import contextlib +import ctypes +import ctypes.util +import six + +import objc +import CoreFoundation +import HIServices +import Quartz + +from . import AbstractListener + + +def _wrap_value(value): + """Converts a pointer to a *Python objc* value. + + :param value: The pointer to convert. + + :return: a wrapped value + """ + return objc.objc_object(c_void_p=value) if value is not None else None + + +@contextlib.contextmanager +def _wrapped(value): + """A context manager that converts a raw pointer to a *Python objc* value. + + When the block is exited, the value is released. + + :param value: The raw value to wrap. + """ + wrapped_value = _wrap_value(value) + + try: + yield value + finally: + CoreFoundation.CFRelease(wrapped_value) + + +class CarbonExtra(object): + """A class exposing some missing functionality from *Carbon* as class + attributes. + """ + _Carbon = ctypes.cdll.LoadLibrary(ctypes.util.find_library('Carbon')) + + _Carbon.TISCopyCurrentKeyboardInputSource.argtypes = [] + _Carbon.TISCopyCurrentKeyboardInputSource.restype = ctypes.c_void_p + + _Carbon.TISCopyCurrentASCIICapableKeyboardLayoutInputSource.argtypes = [] + _Carbon.TISCopyCurrentASCIICapableKeyboardLayoutInputSource.restype = \ + ctypes.c_void_p + + _Carbon.TISGetInputSourceProperty.argtypes = [ + ctypes.c_void_p, ctypes.c_void_p] + _Carbon.TISGetInputSourceProperty.restype = ctypes.c_void_p + + _Carbon.LMGetKbdType.argtypes = [] + _Carbon.LMGetKbdType.restype = ctypes.c_uint32 + + _Carbon.UCKeyTranslate.argtypes = [ + ctypes.c_void_p, + ctypes.c_uint16, + ctypes.c_uint16, + ctypes.c_uint32, + ctypes.c_uint32, + ctypes.c_uint32, + ctypes.POINTER(ctypes.c_uint32), + ctypes.c_uint8, + ctypes.POINTER(ctypes.c_uint8), + ctypes.c_uint16 * 4] + _Carbon.UCKeyTranslate.restype = ctypes.c_uint32 + + TISCopyCurrentKeyboardInputSource = \ + _Carbon.TISCopyCurrentKeyboardInputSource + + TISCopyCurrentASCIICapableKeyboardLayoutInputSource = \ + _Carbon.TISCopyCurrentASCIICapableKeyboardLayoutInputSource + + kTISPropertyUnicodeKeyLayoutData = ctypes.c_void_p.in_dll( + _Carbon, 'kTISPropertyUnicodeKeyLayoutData') + + TISGetInputSourceProperty = \ + _Carbon.TISGetInputSourceProperty + + LMGetKbdType = \ + _Carbon.LMGetKbdType + + kUCKeyActionDisplay = 3 + kUCKeyTranslateNoDeadKeysBit = 0 + + UCKeyTranslate = \ + _Carbon.UCKeyTranslate + + +@contextlib.contextmanager +def keycode_context(): + """Returns an opaque value representing a context for translating keycodes + to strings. + """ + keyboard_type, layout_data = None, None + for source in [ + CarbonExtra.TISCopyCurrentKeyboardInputSource, + CarbonExtra.TISCopyCurrentASCIICapableKeyboardLayoutInputSource]: + with _wrapped(source()) as keyboard: + keyboard_type = CarbonExtra.LMGetKbdType() + layout = _wrap_value(CarbonExtra.TISGetInputSourceProperty( + keyboard, + CarbonExtra.kTISPropertyUnicodeKeyLayoutData)) + layout_data = layout.bytes().tobytes() if layout else None + if keyboard is not None and layout_data is not None: + break + yield (keyboard_type, layout_data) + + +def keycode_to_string(context, keycode, modifier_state=0): + """Converts a keycode to a string. + """ + LENGTH = 4 + + keyboard_type, layout_data = context + + dead_key_state = ctypes.c_uint32() + length = ctypes.c_uint8() + unicode_string = (ctypes.c_uint16 * LENGTH)() + CarbonExtra.UCKeyTranslate( + layout_data, + keycode, + CarbonExtra.kUCKeyActionDisplay, + modifier_state, + keyboard_type, + CarbonExtra.kUCKeyTranslateNoDeadKeysBit, + ctypes.byref(dead_key_state), + LENGTH, + ctypes.byref(length), + unicode_string) + return u''.join( + six.unichr(unicode_string[i]) + for i in range(length.value)) + + +def get_unicode_to_keycode_map(): + """Returns a mapping from unicode strings to virtual key codes. + + :return: a dict mapping key codes to strings + """ + with keycode_context() as context: + return { + keycode_to_string(context, keycode): keycode + for keycode in range(128)} + + +class ListenerMixin(object): + """A mixin for *Quartz* event listeners. + + Subclasses should set a value for :attr:`_EVENTS` and implement + :meth:`_handle`. + """ + #: The events that we listen to + _EVENTS = tuple() + + #: Whether this process is trusted to monitor input events. + IS_TRUSTED = False + + def _run(self): + self.IS_TRUSTED = HIServices.AXIsProcessTrusted() + if not self.IS_TRUSTED: + self._log.warning( + 'This process is not trusted! Input event monitoring will not ' + 'be possible until it is added to accessibility clients.') + + self._loop = None + try: + tap = self._create_event_tap() + if tap is None: + self._mark_ready() + return + + loop_source = Quartz.CFMachPortCreateRunLoopSource( + None, tap, 0) + self._loop = Quartz.CFRunLoopGetCurrent() + + Quartz.CFRunLoopAddSource( + self._loop, loop_source, Quartz.kCFRunLoopDefaultMode) + Quartz.CGEventTapEnable(tap, True) + + self._mark_ready() + + # pylint: disable=W0702; we want to silence errors + try: + while self.running: + result = Quartz.CFRunLoopRunInMode( + Quartz.kCFRunLoopDefaultMode, 1, False) + try: + if result != Quartz.kCFRunLoopRunTimedOut: + break + except AttributeError: + # This happens during teardown of the virtual machine + break + + except: + # This exception will have been passed to the main thread + pass + # pylint: enable=W0702 + + finally: + self._loop = None + + def _stop_platform(self): + # The base class sets the running flag to False; this will cause the + # loop around run loop invocations to terminate and set this event + try: + if self._loop is not None: + Quartz.CFRunLoopStop(self._loop) + except AttributeError: + # The loop may not have been created + pass + + def _create_event_tap(self): + """Creates the event tap used by the listener. + + :return: an event tap + """ + return Quartz.CGEventTapCreate( + Quartz.kCGSessionEventTap, + Quartz.kCGHeadInsertEventTap, + Quartz.kCGEventTapOptionListenOnly if ( + True + and not self.suppress + and self._intercept is None) + else Quartz.kCGEventTapOptionDefault, + self._EVENTS, + self._handler, + None) + + @AbstractListener._emitter + def _handler(self, proxy, event_type, event, refcon): + """The callback registered with *macOS* for mouse events. + + This method will call the callbacks registered on initialisation. + """ + self._handle(proxy, event_type, event, refcon) + if self._intercept is not None: + return self._intercept(event_type, event) + elif self.suppress: + return None + + def _handle(self, proxy, event_type, event, refcon): + """The device specific callback handler. + + This method calls the appropriate callback registered when this + listener was created based on the event. + """ + raise NotImplementedError() diff --git a/pynput/_util/darwin_vks.py b/pynput/_util/darwin_vks.py new file mode 100644 index 0000000..7eeb41b --- /dev/null +++ b/pynput/_util/darwin_vks.py @@ -0,0 +1,79 @@ +# coding: utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +# pylint: disable=C0111,C0302 + +SYMBOLS = { + 0: 'a', + 1: 's', + 2: 'd', + 3: 'f', + 4: 'h', + 5: 'g', + 6: 'z', + 7: 'x', + 8: 'c', + 9: 'v', + 11: 'b', + 12: 'q', + 13: 'w', + 14: 'e', + 15: 'r', + 16: 'y', + 17: 't', + 18: '1', + 19: '2', + 20: '3', + 21: '4', + 22: '6', + 23: '5', + 24: '=', + 25: '9', + 26: '7', + 27: '-', + 28: '8', + 29: '0', + 30: ']', + 31: 'o', + 32: 'u', + 33: '[', + 34: 'i', + 35: 'p', + 37: 'l', + 38: 'j', + 39: '\'', + 40: 'k', + 41: ';', + 42: '\\', + 43: ',', + 44: '/', + 45: 'n', + 46: 'm', + 47: '.', + 49: ' ', + 50: '`', + 82: '0', + 83: '1', + 84: '2', + 85: '3', + 86: '4', + 87: '5', + 88: '6', + 89: '7', + 91: '8', + 92: '9', +} diff --git a/pynput/_util/uinput.py b/pynput/_util/uinput.py new file mode 100644 index 0000000..d73e07f --- /dev/null +++ b/pynput/_util/uinput.py @@ -0,0 +1,99 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +Utility functions and classes for the *uinput* backend. +""" + +# pylint: disable=R0903 +# We implement stubs + +import evdev + + +# Check that we have permissions to continue +def _check(): + # TODO: Implement! + pass +_check() +del _check + + +class ListenerMixin(object): + """A mixin for *uinput* event listeners. + + Subclasses should set a value for :attr:`_EVENTS` and implement + :meth:`_handle`. + """ + #: The events for which to listen + _EVENTS = tuple() + + def __init__(self, *args, **kwargs): + super(ListenerMixin, self).__init__(*args, **kwargs) + self._dev = self._device(self._options.get( + 'device_paths', + evdev.list_devices())) + if self.suppress: + self._dev.grab() + + def _run(self): + for event in self._dev.read_loop(): + if event.type in self._EVENTS: + self._handle(event) + + def _stop_platform(self): + self._dev.close() + + def _device(self, paths): + """Attempts to load a readable keyboard device. + + :param paths: A list of paths. + + :return: a compatible device + """ + dev, count = None, 0 + for path in paths: + # Open the device + try: + next_dev = evdev.InputDevice(path) + except OSError: + continue + + # Does this device provide more handled event codes? + capabilities = next_dev.capabilities() + next_count = sum( + len(codes) + for event, codes in capabilities.items() + if event in self._EVENTS) + if next_count > count: + dev = next_dev + count = next_count + else: + next_dev.close() + + if dev is None: + raise OSError('no keyboard device available') + else: + return dev + + def _handle(self, event): + """Handles a single event. + + This method should call one of the registered event callbacks. + + :param event: The event. + """ + raise NotImplementedError() diff --git a/pynput/_util/win32.py b/pynput/_util/win32.py new file mode 100644 index 0000000..756d876 --- /dev/null +++ b/pynput/_util/win32.py @@ -0,0 +1,598 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +Utility functions and classes for the *win32* backend. +""" + +# pylint: disable=C0103 +# We want to make it obvious how structs are related + +# pylint: disable=R0903 +# This module contains a number of structs + +import contextlib +import ctypes +import itertools +import threading + +from ctypes import ( + windll, + wintypes) + +from . import AbstractListener, win32_vks as VK + + +# LPDWORD is not in ctypes.wintypes on Python 2 +if not hasattr(wintypes, 'LPDWORD'): + wintypes.LPDWORD = ctypes.POINTER(wintypes.DWORD) + + +class MOUSEINPUT(ctypes.Structure): + """Contains information about a simulated mouse event. + """ + MOVE = 0x0001 + LEFTDOWN = 0x0002 + LEFTUP = 0x0004 + RIGHTDOWN = 0x0008 + RIGHTUP = 0x0010 + MIDDLEDOWN = 0x0020 + MIDDLEUP = 0x0040 + XDOWN = 0x0080 + XUP = 0x0100 + WHEEL = 0x0800 + HWHEEL = 0x1000 + ABSOLUTE = 0x8000 + + XBUTTON1 = 0x0001 + XBUTTON2 = 0x0002 + + _fields_ = [ + ('dx', wintypes.LONG), + ('dy', wintypes.LONG), + ('mouseData', wintypes.DWORD), + ('dwFlags', wintypes.DWORD), + ('time', wintypes.DWORD), + ('dwExtraInfo', ctypes.c_void_p)] + + +class KEYBDINPUT(ctypes.Structure): + """Contains information about a simulated keyboard event. + """ + EXTENDEDKEY = 0x0001 + KEYUP = 0x0002 + SCANCODE = 0x0008 + UNICODE = 0x0004 + + _fields_ = [ + ('wVk', wintypes.WORD), + ('wScan', wintypes.WORD), + ('dwFlags', wintypes.DWORD), + ('time', wintypes.DWORD), + ('dwExtraInfo', ctypes.c_void_p)] + + +class HARDWAREINPUT(ctypes.Structure): + """Contains information about a simulated message generated by an input + device other than a keyboard or mouse. + """ + _fields_ = [ + ('uMsg', wintypes.DWORD), + ('wParamL', wintypes.WORD), + ('wParamH', wintypes.WORD)] + + +class INPUT_union(ctypes.Union): + """Represents the union of input types in :class:`INPUT`. + """ + _fields_ = [ + ('mi', MOUSEINPUT), + ('ki', KEYBDINPUT), + ('hi', HARDWAREINPUT)] + + +class INPUT(ctypes.Structure): + """Used by :attr:`SendInput` to store information for synthesizing input + events such as keystrokes, mouse movement, and mouse clicks. + """ + MOUSE = 0 + KEYBOARD = 1 + HARDWARE = 2 + + _fields_ = [ + ('type', wintypes.DWORD), + ('value', INPUT_union)] + + +LPINPUT = ctypes.POINTER(INPUT) + +VkKeyScan = windll.user32.VkKeyScanW +VkKeyScan.argtypes = ( + wintypes.WCHAR,) + +MapVirtualKey = windll.user32.MapVirtualKeyW +MapVirtualKey.argtypes = ( + wintypes.UINT, + wintypes.UINT) +MapVirtualKey.MAPVK_VK_TO_VSC = 0 + +SendInput = windll.user32.SendInput +SendInput.argtypes = ( + wintypes.UINT, + ctypes.c_voidp, # Really LPINPUT + ctypes.c_int) + +GetCurrentThreadId = windll.kernel32.GetCurrentThreadId +GetCurrentThreadId.restype = wintypes.DWORD + + +class MessageLoop(object): + """A class representing a message loop. + """ + #: The message that signals this loop to terminate + WM_STOP = 0x0401 + + _LPMSG = ctypes.POINTER(wintypes.MSG) + + _GetMessage = windll.user32.GetMessageW + _GetMessage.argtypes = ( + ctypes.c_voidp, # Really _LPMSG + wintypes.HWND, + wintypes.UINT, + wintypes.UINT) + _PeekMessage = windll.user32.PeekMessageW + _PeekMessage.argtypes = ( + ctypes.c_voidp, # Really _LPMSG + wintypes.HWND, + wintypes.UINT, + wintypes.UINT, + wintypes.UINT) + _PostThreadMessage = windll.user32.PostThreadMessageW + _PostThreadMessage.argtypes = ( + wintypes.DWORD, + wintypes.UINT, + wintypes.WPARAM, + wintypes.LPARAM) + + PM_NOREMOVE = 0 + + def __init__(self): + self._threadid = None + self._event = threading.Event() + self.thread = None + + def __iter__(self): + """Initialises the message loop and yields all messages until + :meth:`stop` is called. + + :raises AssertionError: if :meth:`start` has not been called + """ + assert self._threadid is not None + + try: + # Pump messages until WM_STOP + while True: + msg = wintypes.MSG() + lpmsg = ctypes.byref(msg) + r = self._GetMessage(lpmsg, None, 0, 0) + if r <= 0 or msg.message == self.WM_STOP: + break + else: + yield msg + + finally: + self._threadid = None + self.thread = None + + def start(self): + """Starts the message loop. + + This method must be called before iterating over messages, and it must + be called from the same thread. + """ + self._threadid = GetCurrentThreadId() + self.thread = threading.current_thread() + + # Create the message loop + msg = wintypes.MSG() + lpmsg = ctypes.byref(msg) + self._PeekMessage(lpmsg, None, 0x0400, 0x0400, self.PM_NOREMOVE) + + # Set the event to signal to other threads that the loop is created + self._event.set() + + def stop(self): + """Stops the message loop. + """ + self._event.wait() + if self._threadid: + self.post(self.WM_STOP, 0, 0) + + def post(self, msg, wparam, lparam): + """Posts a message to this message loop. + + :param ctypes.wintypes.UINT msg: The message. + + :param ctypes.wintypes.WPARAM wparam: The value of ``wParam``. + + :param ctypes.wintypes.LPARAM lparam: The value of ``lParam``. + """ + self._PostThreadMessage(self._threadid, msg, wparam, lparam) + + +class SystemHook(object): + """A class to handle Windows hooks. + """ + #: The hook action value for actions we should check + HC_ACTION = 0 + + _HOOKPROC = ctypes.WINFUNCTYPE( + wintypes.LPARAM, + ctypes.c_int32, wintypes.WPARAM, wintypes.LPARAM) + + _SetWindowsHookEx = windll.user32.SetWindowsHookExW + _SetWindowsHookEx.argtypes = ( + ctypes.c_int, + _HOOKPROC, + wintypes.HINSTANCE, + wintypes.DWORD) + _UnhookWindowsHookEx = windll.user32.UnhookWindowsHookEx + _UnhookWindowsHookEx.argtypes = ( + wintypes.HHOOK,) + _CallNextHookEx = windll.user32.CallNextHookEx + _CallNextHookEx.argtypes = ( + wintypes.HHOOK, + ctypes.c_int, + wintypes.WPARAM, + wintypes.LPARAM) + + #: The registered hook procedures + _HOOKS = {} + + class SuppressException(Exception): + """An exception raised by a hook callback to suppress further + propagation of events. + """ + pass + + def __init__(self, hook_id, on_hook=lambda code, msg, lpdata: None): + self.hook_id = hook_id + self.on_hook = on_hook + self._hook = None + + def __enter__(self): + key = threading.current_thread().ident + assert key not in self._HOOKS + + # Add ourself to lookup table and install the hook + self._HOOKS[key] = self + self._hook = self._SetWindowsHookEx( + self.hook_id, + self._handler, + None, + 0) + + return self + + def __exit__(self, exc_type, value, traceback): + key = threading.current_thread().ident + assert key in self._HOOKS + + if self._hook is not None: + # Uninstall the hook and remove ourself from lookup table + self._UnhookWindowsHookEx(self._hook) + del self._HOOKS[key] + + @staticmethod + @_HOOKPROC + def _handler(code, msg, lpdata): + key = threading.current_thread().ident + self = SystemHook._HOOKS.get(key, None) + if self: + # pylint: disable=W0702; we want to silence errors + try: + self.on_hook(code, msg, lpdata) + except self.SuppressException: + # Return non-zero to stop event propagation + return 1 + except: + # Ignore any errors + pass + # pylint: enable=W0702 + return SystemHook._CallNextHookEx(0, code, msg, lpdata) + + +class ListenerMixin(object): + """A mixin for *win32* event listeners. + + Subclasses should set a value for :attr:`_EVENTS` and implement + :meth:`_handle`. + + Subclasses must also be decorated with a decorator compatible with + :meth:`pynput._util.NotifierMixin._receiver` or implement the method + ``_receive()``. + """ + #: The Windows hook ID for the events to capture. + _EVENTS = None + + #: The window message used to signal that an even should be handled. + _WM_PROCESS = 0x410 + + #: Additional window messages to propagate to the subclass handler. + _WM_NOTIFICATIONS = [] + + def suppress_event(self): + """Causes the currently filtered event to be suppressed. + + This has a system wide effect and will generally result in no + applications receiving the event. + + This method will raise an undefined exception. + """ + raise SystemHook.SuppressException() + + def _run(self): + self._message_loop = MessageLoop() + with self._receive(): + self._mark_ready() + self._message_loop.start() + + # pylint: disable=W0702; we want to silence errors + try: + with SystemHook(self._EVENTS, self._handler): + # Just pump messages + for msg in self._message_loop: + if not self.running: + break + if msg.message == self._WM_PROCESS: + self._process(msg.wParam, msg.lParam) + elif msg.message in self._WM_NOTIFICATIONS: + self._on_notification( + msg.message, msg.wParam, msg.lParam) + except: + # This exception will have been passed to the main thread + pass + # pylint: enable=W0702 + + def _stop_platform(self): + try: + self._message_loop.stop() + except AttributeError: + # The loop may not have been created + pass + + @AbstractListener._emitter + def _handler(self, code, msg, lpdata): + """The callback registered with *Windows* for events. + + This method will post the message :attr:`_WM_HANDLE` to the message + loop started with this listener using :meth:`MessageLoop.post`. The + parameters are retrieved with a call to :meth:`_handle`. + """ + try: + converted = self._convert(code, msg, lpdata) + if converted is not None: + self._message_loop.post(self._WM_PROCESS, *converted) + except NotImplementedError: + self._handle(code, msg, lpdata) + + if self.suppress: + self.suppress_event() + + def _convert(self, code, msg, lpdata): + """The device specific callback handler. + + This method converts a low-level message and data to a + ``WPARAM`` / ``LPARAM`` pair. + """ + raise NotImplementedError() + + def _process(self, wparam, lparam): + """The device specific callback handler. + + This method performs the actual dispatching of events. + """ + raise NotImplementedError() + + def _handle(self, code, msg, lpdata): + """The device specific callback handler. + + This method calls the appropriate callback registered when this + listener was created based on the event. + + This method is only called if :meth:`_convert` is not implemented. + """ + raise NotImplementedError() + + def _on_notification(self, code, wparam, lparam): + """An additional notification handler. + + This method will be called for every message in + :attr:`_WM_NOTIFICATIONS`. + """ + raise NotImplementedError() + + +class KeyTranslator(object): + """A class to translate virtual key codes to characters. + """ + _GetAsyncKeyState = ctypes.windll.user32.GetAsyncKeyState + _GetAsyncKeyState.argtypes = ( + ctypes.c_int,) + _GetKeyboardLayout = ctypes.windll.user32.GetKeyboardLayout + _GetKeyboardLayout.argtypes = ( + wintypes.DWORD,) + _GetKeyboardState = ctypes.windll.user32.GetKeyboardState + _GetKeyboardState.argtypes = ( + ctypes.c_voidp,) + _GetKeyState = ctypes.windll.user32.GetAsyncKeyState + _GetKeyState.argtypes = ( + ctypes.c_int,) + _MapVirtualKeyEx = ctypes.windll.user32.MapVirtualKeyExW + _MapVirtualKeyEx.argtypes = ( + wintypes.UINT, + wintypes.UINT, + wintypes.HKL) + _ToUnicodeEx = ctypes.windll.user32.ToUnicodeEx + _ToUnicodeEx.argtypes = ( + wintypes.UINT, + wintypes.UINT, + ctypes.c_voidp, + ctypes.c_voidp, + ctypes.c_int, + wintypes.UINT, + wintypes.HKL) + + _MAPVK_VK_TO_VSC = 0 + _MAPVK_VSC_TO_VK = 1 + _MAPVK_VK_TO_CHAR = 2 + + def __init__(self): + self.update_layout() + + def __call__(self, vk, is_press): + """Converts a virtual key code to a string. + + :param int vk: The virtual key code. + + :param bool is_press: Whether this is a press. + + :return: parameters suitable for the :class:`pynput.keyboard.KeyCode` + constructor + + :raises OSError: if a call to any *win32* function fails + """ + # Get a string representation of the key + layout_data = self._layout_data[self._modifier_state()] + scan = self._to_scan(vk, self._layout) + character, is_dead = layout_data[scan] + + return { + 'char': character, + 'is_dead': is_dead, + 'vk': vk, + '_scan': scan} + + def update_layout(self): + """Updates the cached layout data. + """ + self._layout, self._layout_data = self._generate_layout() + + def char_from_scan(self, scan): + """Translates a scan code to a character, if possible. + + :param int scan: The scan code to translate. + + :return: maybe a character + :rtype: str or None + """ + return self._layout_data[(False, False, False)][scan][0] + + def _generate_layout(self): + """Generates the keyboard layout. + + This method will call ``ToUnicodeEx``, which modifies kernel buffers, + so it must *not* be called from the keyboard hook. + + The return value is the tuple ``(layout_handle, layout_data)``, where + ``layout_data`` is a mapping from the tuple ``(shift, ctrl, alt)`` to + an array indexed by scan code containing the data + ``(character, is_dead)``, and ``layout_handle`` is the handle of the + layout. + + :return: a composite layout + """ + layout_data = {} + + state = (ctypes.c_ubyte * 255)() + with self._thread_input() as active_thread: + layout = self._GetKeyboardLayout(active_thread) + vks = [ + self._to_vk(scan, layout) + for scan in range(len(state))] + + for shift, ctrl, alt in itertools.product( + (False, True), (False, True), (False, True)): + current = [(None, False)] * len(state) + layout_data[(shift, ctrl, alt)] = current + + # Update the keyboard state based on the modifier state + state[VK.SHIFT] = 0x80 if shift else 0x00 + state[VK.CONTROL] = 0x80 if ctrl else 0x00 + state[VK.MENU] = 0x80 if alt else 0x00 + + # For each virtual key code... + out = (ctypes.wintypes.WCHAR * 5)() + for (scan, vk) in enumerate(vks): + # ...translate it to a unicode character + count = self._ToUnicodeEx( + vk, scan, ctypes.byref(state), ctypes.byref(out), + len(out), 0, layout) + + # Cache the result if a key is mapped + if count != 0: + character = out[0] + is_dead = count < 0 + current[scan] = (character, is_dead) + + # If the key is dead, flush the keyboard state + if is_dead: + self._ToUnicodeEx( + vk, scan, ctypes.byref(state), + ctypes.byref(out), len(out), 0, layout) + + return (layout, layout_data) + + def _to_scan(self, vk, layout): + """Retrieves the scan code for a virtual key code. + + :param int vk: The virtual key code. + + :param layout: The keyboard layout. + + :return: the scan code + """ + return self._MapVirtualKeyEx( + vk, self._MAPVK_VK_TO_VSC, layout) + + def _to_vk(self, scan, layout): + """Retrieves the virtual key code for a scan code. + + :param int vscan: The scan code. + + :param layout: The keyboard layout. + + :return: the virtual key code + """ + return self._MapVirtualKeyEx( + scan, self._MAPVK_VSC_TO_VK, layout) + + def _modifier_state(self): + """Returns a key into :attr:`_layout_data` for the current modifier + state. + + :return: the current modifier state + """ + shift = bool(self._GetAsyncKeyState(VK.SHIFT) & 0x8000) + ctrl = bool(self._GetAsyncKeyState(VK.CONTROL) & 0x8000) + alt = bool(self._GetAsyncKeyState(VK.MENU) & 0x8000) + return (shift, ctrl, alt) + + @contextlib.contextmanager + def _thread_input(self): + """Yields the current thread ID. + """ + yield GetCurrentThreadId() diff --git a/pynput/_util/win32_vks.py b/pynput/_util/win32_vks.py new file mode 100644 index 0000000..9469e76 --- /dev/null +++ b/pynput/_util/win32_vks.py @@ -0,0 +1,179 @@ +# coding: utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +# pylint: disable=C0111,C0302 + +LBUTTON = 1 +RBUTTON = 2 +CANCEL = 3 +MBUTTON = 4 +XBUTTON1 = 5 +XBUTTON2 = 6 +BACK = 8 +TAB = 9 +CLEAR = 12 +RETURN = 13 +SHIFT = 16 +CONTROL = 17 +MENU = 18 +PAUSE = 19 +CAPITAL = 20 +KANA = 21 +HANGEUL = 21 +HANGUL = 21 +JUNJA = 23 +FINAL = 24 +HANJA = 25 +KANJI = 25 +ESCAPE = 27 +CONVERT = 28 +NONCONVERT = 29 +ACCEPT = 30 +MODECHANGE = 31 +SPACE = 32 +PRIOR = 33 +NEXT = 34 +END = 35 +HOME = 36 +LEFT = 37 +UP = 38 +RIGHT = 39 +DOWN = 40 +SELECT = 41 +PRINT = 42 +EXECUTE = 43 +SNAPSHOT = 44 +INSERT = 45 +DELETE = 46 +HELP = 47 +LWIN = 91 +RWIN = 92 +APPS = 93 +SLEEP = 95 +NUMPAD0 = 96 +NUMPAD1 = 97 +NUMPAD2 = 98 +NUMPAD3 = 99 +NUMPAD4 = 100 +NUMPAD5 = 101 +NUMPAD6 = 102 +NUMPAD7 = 103 +NUMPAD8 = 104 +NUMPAD9 = 105 +MULTIPLY = 106 +ADD = 107 +SEPARATOR = 108 +SUBTRACT = 109 +DECIMAL = 110 +DIVIDE = 111 +F1 = 112 +F2 = 113 +F3 = 114 +F4 = 115 +F5 = 116 +F6 = 117 +F7 = 118 +F8 = 119 +F9 = 120 +F10 = 121 +F11 = 122 +F12 = 123 +F13 = 124 +F14 = 125 +F15 = 126 +F16 = 127 +F17 = 128 +F18 = 129 +F19 = 130 +F20 = 131 +F21 = 132 +F22 = 133 +F23 = 134 +F24 = 135 +NUMLOCK = 144 +SCROLL = 145 +OEM_NEC_EQUAL = 146 +OEM_FJ_JISHO = 146 +OEM_FJ_MASSHOU = 147 +OEM_FJ_TOUROKU = 148 +OEM_FJ_LOYA = 149 +OEM_FJ_ROYA = 150 +LSHIFT = 160 +RSHIFT = 161 +LCONTROL = 162 +RCONTROL = 163 +LMENU = 164 +RMENU = 165 +BROWSER_BACK = 166 +BROWSER_FORWARD = 167 +BROWSER_REFRESH = 168 +BROWSER_STOP = 169 +BROWSER_SEARCH = 170 +BROWSER_FAVORITES = 171 +BROWSER_HOME = 172 +VOLUME_MUTE = 173 +VOLUME_DOWN = 174 +VOLUME_UP = 175 +MEDIA_NEXT_TRACK = 176 +MEDIA_PREV_TRACK = 177 +MEDIA_STOP = 178 +MEDIA_PLAY_PAUSE = 179 +LAUNCH_MAIL = 180 +LAUNCH_MEDIA_SELECT = 181 +LAUNCH_APP1 = 182 +LAUNCH_APP2 = 183 +OEM_1 = 186 +OEM_PLUS = 187 +OEM_COMMA = 188 +OEM_MINUS = 189 +OEM_PERIOD = 190 +OEM_2 = 191 +OEM_3 = 192 +OEM_4 = 219 +OEM_5 = 220 +OEM_6 = 221 +OEM_7 = 222 +OEM_8 = 223 +OEM_AX = 225 +OEM_102 = 226 +ICO_HELP = 227 +ICO_00 = 228 +PROCESSKEY = 229 +ICO_CLEAR = 230 +PACKET = 231 +OEM_RESET = 233 +OEM_JUMP = 234 +OEM_PA1 = 235 +OEM_PA2 = 236 +OEM_PA3 = 237 +OEM_WSCTRL = 238 +OEM_CUSEL = 239 +OEM_ATTN = 240 +OEM_FINISH = 241 +OEM_COPY = 242 +OEM_AUTO = 243 +OEM_ENLW = 244 +OEM_BACKTAB = 245 +ATTN = 246 +CRSEL = 247 +EXSEL = 248 +EREOF = 249 +PLAY = 250 +ZOOM = 251 +NONAME = 252 +PA1 = 253 +OEM_CLEAR = 254 diff --git a/pynput/_util/xorg.py b/pynput/_util/xorg.py new file mode 100644 index 0000000..a777a49 --- /dev/null +++ b/pynput/_util/xorg.py @@ -0,0 +1,492 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +Utility functions and classes for the *Xorg* backend. +""" + +# pylint: disable=R0903 +# We implement stubs + +import contextlib +import functools +import itertools +import operator +import Xlib.display +import Xlib.keysymdef +import Xlib.threaded +import Xlib.XK + +from . import AbstractListener +from .xorg_keysyms import SYMBOLS + + +# Create a display to verify that we have an X connection +def _check_and_initialize(): + display = Xlib.display.Display() + display.close() + + for group in Xlib.keysymdef.__all__: + Xlib.XK.load_keysym_group(group) +_check_and_initialize() +del _check_and_initialize + + +class X11Error(Exception): + """An error that is thrown at the end of a code block managed by a + :func:`display_manager` if an *X11* error occurred. + """ + pass + + +@contextlib.contextmanager +def display_manager(display): + """Traps *X* errors and raises an :class:``X11Error`` at the end if any + error occurred. + + This handler also ensures that the :class:`Xlib.display.Display` being + managed is sync'd. + + :param Xlib.display.Display display: The *X* display. + + :return: the display + :rtype: Xlib.display.Display + """ + errors = [] + + def handler(*args): + """The *Xlib* error handler. + """ + errors.append(args) + + old_handler = display.set_error_handler(handler) + try: + yield display + display.sync() + finally: + display.set_error_handler(old_handler) + if errors: + raise X11Error(errors) + + +def _find_mask(display, symbol): + """Returns the mode flags to use for a modifier symbol. + + :param Xlib.display.Display display: The *X* display. + + :param str symbol: The name of the symbol. + + :return: the modifier mask + """ + # Get the key code for the symbol + modifier_keycode = display.keysym_to_keycode( + Xlib.XK.string_to_keysym(symbol)) + + for index, keycodes in enumerate(display.get_modifier_mapping()): + for keycode in keycodes: + if keycode == modifier_keycode: + return 1 << index + + return 0 + + +def alt_mask(display): + """Returns the *alt* mask flags. + + The first time this function is called for a display, the value is cached. + Subsequent calls will return the cached value. + + :param Xlib.display.Display display: The *X* display. + + :return: the modifier mask + """ + if not hasattr(display, '__alt_mask'): + display.__alt_mask = _find_mask(display, 'Alt_L') + return display.__alt_mask + + +def alt_gr_mask(display): + """Returns the *alt* mask flags. + + The first time this function is called for a display, the value is cached. + Subsequent calls will return the cached value. + + :param Xlib.display.Display display: The *X* display. + + :return: the modifier mask + """ + if not hasattr(display, '__altgr_mask'): + display.__altgr_mask = _find_mask(display, 'Mode_switch') + return display.__altgr_mask + + +def numlock_mask(display): + """Returns the *numlock* mask flags. + + The first time this function is called for a display, the value is cached. + Subsequent calls will return the cached value. + + :param Xlib.display.Display display: The *X* display. + + :return: the modifier mask + """ + if not hasattr(display, '__numlock_mask'): + display.__numlock_mask = _find_mask(display, 'Num_Lock') + return display.__numlock_mask + + +def keysym_is_latin_upper(keysym): + """Determines whether a *keysym* is an upper case *latin* character. + + This is true only if ``XK_A`` <= ``keysym`` <= ` XK_Z``. + + :param in keysym: The *keysym* to check. + """ + return Xlib.XK.XK_A <= keysym <= Xlib.XK.XK_Z + + +def keysym_is_latin_lower(keysym): + """Determines whether a *keysym* is a lower case *latin* character. + + This is true only if ``XK_a`` <= ``keysym`` <= ` XK_z``. + + :param in keysym: The *keysym* to check. + """ + return Xlib.XK.XK_a <= keysym <= Xlib.XK.XK_z + + +def keysym_group(ks1, ks2): + """Generates a group from two *keysyms*. + + The implementation of this function comes from: + + Within each group, if the second element of the group is ``NoSymbol``, + then the group should be treated as if the second element were the same + as the first element, except when the first element is an alphabetic + *KeySym* ``K`` for which both lowercase and uppercase forms are + defined. + + In that case, the group should be treated as if the first element were + the lowercase form of ``K`` and the second element were the uppercase + form of ``K``. + + This function assumes that *alphabetic* means *latin*; this assumption + appears to be consistent with observations of the return values from + ``XGetKeyboardMapping``. + + :param ks1: The first *keysym*. + + :param ks2: The second *keysym*. + + :return: a tuple conforming to the description above + """ + if ks2 == Xlib.XK.NoSymbol: + if keysym_is_latin_upper(ks1): + return (Xlib.XK.XK_a + ks1 - Xlib.XK.XK_A, ks1) + elif keysym_is_latin_lower(ks1): + return (ks1, Xlib.XK.XK_A + ks1 - Xlib.XK.XK_a) + else: + return (ks1, ks1) + else: + return (ks1, ks2) + + +def keysym_normalize(keysym): + """Normalises a list of *keysyms*. + + The implementation of this function comes from: + + If the list (ignoring trailing ``NoSymbol`` entries) is a single + *KeySym* ``K``, then the list is treated as if it were the list + ``K NoSymbol K NoSymbol``. + + If the list (ignoring trailing ``NoSymbol`` entries) is a pair of + *KeySyms* ``K1 K2``, then the list is treated as if it were the list + ``K1 K2 K1 K2``. + + If the list (ignoring trailing ``NoSymbol`` entries) is a triple of + *KeySyms* ``K1 K2 K3``, then the list is treated as if it were the list + ``K1 K2 K3 NoSymbol``. + + This function will also group the *keysyms* using :func:`keysym_group`. + + :param keysyms: A list of keysyms. + + :return: the tuple ``(group_1, group_2)`` or ``None`` + """ + # Remove trailing NoSymbol + stripped = list(reversed(list( + itertools.dropwhile( + lambda n: n == Xlib.XK.NoSymbol, + reversed(keysym))))) + + if not stripped: + return + + elif len(stripped) == 1: + return ( + keysym_group(stripped[0], Xlib.XK.NoSymbol), + keysym_group(stripped[0], Xlib.XK.NoSymbol)) + + elif len(stripped) == 2: + return ( + keysym_group(stripped[0], stripped[1]), + keysym_group(stripped[0], stripped[1])) + + elif len(stripped) == 3: + return ( + keysym_group(stripped[0], stripped[1]), + keysym_group(stripped[2], Xlib.XK.NoSymbol)) + + elif len(stripped) >= 6: + # TODO: Find out why this is necessary; using only the documented + # behaviour may lead to only a US layout being used? + return ( + keysym_group(stripped[0], stripped[1]), + keysym_group(stripped[4], stripped[5])) + + else: + return ( + keysym_group(stripped[0], stripped[1]), + keysym_group(stripped[2], stripped[3])) + + +def index_to_shift(display, index): + """Converts an index in a *key code* list to the corresponding shift state. + + :param Xlib.display.Display display: The display for which to retrieve the + shift mask. + + :param int index: The keyboard mapping *key code* index. + + :return: a shift mask + """ + return ( + (1 << 0 if index & 1 else 0) | + (alt_gr_mask(display) if index & 2 else 0)) + + +def shift_to_index(display, shift): + """Converts an index in a *key code* list to the corresponding shift state. + + :param Xlib.display.Display display: The display for which to retrieve the + shift mask. + + :param int index: The keyboard mapping *key code* index. + + :return: a shift mask + """ + return ( + (1 if shift & 1 else 0) + + (2 if shift & alt_gr_mask(display) else 0)) + + +def keyboard_mapping(display): + """Generates a mapping from *keysyms* to *key codes* and required + modifier shift states. + + :param Xlib.display.Display display: The display for which to retrieve the + keyboard mapping. + + :return: the keyboard mapping + """ + mapping = {} + + shift_mask = 1 << 0 + group_mask = alt_gr_mask(display) + + # Iterate over all keysym lists in the keyboard mapping + min_keycode = display.display.info.min_keycode + keycode_count = display.display.info.max_keycode - min_keycode + 1 + for index, keysyms in enumerate(display.get_keyboard_mapping( + min_keycode, keycode_count)): + key_code = index + min_keycode + + # Normalise the keysym list to yield a tuple containing the two groups + normalized = keysym_normalize(keysyms) + if not normalized: + continue + + # Iterate over the groups to extract the shift and modifier state + for groups, group in zip(normalized, (False, True)): + for keysym, shift in zip(groups, (False, True)): + if not keysym: + continue + shift_state = 0 \ + | (shift_mask if shift else 0) \ + | (group_mask if group else 0) + + # Prefer already known lesser shift states + if keysym in mapping and mapping[keysym][1] < shift_state: + continue + mapping[keysym] = (key_code, shift_state) + + return mapping + + +def char_to_keysym(char): + """Converts a unicode character to a *keysym*. + + :param str char: The unicode character. + + :return: the corresponding *keysym*, or ``0`` if it cannot be found + """ + ordinal = ord(char) + if ordinal < 0x100: + return ordinal + else: + return ordinal | 0x01000000 + + +def symbol_to_keysym(symbol): + """Converts a symbol name to a *keysym*. + + :param str symbol: The name of the symbol. + + :return: the corresponding *keysym*, or ``0`` if it cannot be found + """ + # First try simple translation, the try a module attribute of + # Xlib.keysymdef.xkb and fall back on our pre-generated table + return (0 + or Xlib.XK.string_to_keysym(symbol) + or getattr(Xlib.keysymdef.xkb, "XK_" + symbol, 0) + or SYMBOLS.get(symbol, (0,))[0]) + + +class ListenerMixin(object): + """A mixin for *X* event listeners. + + Subclasses should set a value for :attr:`_EVENTS` and implement + :meth:`_handle`. + """ + #: The events for which to listen + _EVENTS = tuple() + + #: We use this instance for parsing the binary data + _EVENT_PARSER = Xlib.protocol.rq.EventField(None) + + def _run(self): + self._display_stop = Xlib.display.Display() + self._display_record = Xlib.display.Display() + self._stopped = False + with display_manager(self._display_record) as dm: + self._context = dm.record_create_context( + 0, + [Xlib.ext.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': self._EVENTS, + 'errors': (0, 0), + 'client_started': False, + 'client_died': False}]) + + # pylint: disable=W0702; we want to silence errors + try: + self._initialize(self._display_stop) + self._mark_ready() + if self.suppress: + with display_manager(self._display_stop) as dm: + self._suppress_start(dm) + self._display_record.record_enable_context( + self._context, self._handler) + except: + # This exception will have been passed to the main thread + pass + finally: + if self.suppress: + with display_manager(self._display_stop) as dm: + self._suppress_stop(dm) + self._display_stop.record_disable_context(self._context) + self._display_stop.flush() + self._display_record.record_free_context(self._context) + self._display_stop.close() + self._display_record.close() + # pylint: enable=W0702 + + def _stop_platform(self): + if not hasattr(self, '_context'): + self.wait() + + # Do this asynchronously to avoid deadlocks + self._display_record.record_disable_context(self._context) + + def _suppress_start(self, display): + """Starts suppressing events. + + :param Xlib.display.Display display: The display for which to suppress + events. + """ + raise NotImplementedError() + + def _suppress_stop(self, display): + """Starts suppressing events. + + :param Xlib.display.Display display: The display for which to suppress + events. + """ + raise NotImplementedError() + + @property + def _event_mask(self): + """The event mask. + """ + return functools.reduce(operator.__or__, self._EVENTS, 0) + + @AbstractListener._emitter + def _handler(self, events): + """The callback registered with *X* for mouse events. + + This method will parse the response and call the callbacks registered + on initialisation. + + :param events: The events passed by *X*. This is a binary block + parsable by :attr:`_EVENT_PARSER`. + """ + if not self.running: + raise self.StopException() + + data = events.data + + while data and len(data): + event, data = self._EVENT_PARSER.parse_binary_value( + data, self._display_record.display, None, None) + self._handle(self._display_stop, event) + + def _initialize(self, display): + """Initialises this listener. + + This method is called immediately before the event loop, from the + handler thread. + + :param display: The display being used. + """ + pass + + def _handle(self, display, event): + """The device specific callback handler. + + This method calls the appropriate callback registered when this + listener was created based on the event. + + :param display: The display being used. + + :param event: The event. + """ + pass diff --git a/pynput/_util/xorg_keysyms.py b/pynput/_util/xorg_keysyms.py new file mode 100644 index 0000000..128b8c2 --- /dev/null +++ b/pynput/_util/xorg_keysyms.py @@ -0,0 +1,1715 @@ +# coding: utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +# pylint: disable=C0111,C0302 + +SYMBOLS = { + '0': (0x0030, u'\u0030'), + '1': (0x0031, u'\u0031'), + '2': (0x0032, u'\u0032'), + '3': (0x0033, u'\u0033'), + '4': (0x0034, u'\u0034'), + '5': (0x0035, u'\u0035'), + '6': (0x0036, u'\u0036'), + '7': (0x0037, u'\u0037'), + '8': (0x0038, u'\u0038'), + '9': (0x0039, u'\u0039'), + 'A': (0x0041, u'\u0041'), + 'AE': (0x00c6, u'\u00C6'), + 'Aacute': (0x00c1, u'\u00C1'), + 'Abelowdot': (0x1001ea0, u'\u1EA0'), + 'Abreve': (0x01c3, u'\u0102'), + 'Abreveacute': (0x1001eae, u'\u1EAE'), + 'Abrevebelowdot': (0x1001eb6, u'\u1EB6'), + 'Abrevegrave': (0x1001eb0, u'\u1EB0'), + 'Abrevehook': (0x1001eb2, u'\u1EB2'), + 'Abrevetilde': (0x1001eb4, u'\u1EB4'), + 'Acircumflex': (0x00c2, u'\u00C2'), + 'Acircumflexacute': (0x1001ea4, u'\u1EA4'), + 'Acircumflexbelowdot': (0x1001eac, u'\u1EAC'), + 'Acircumflexgrave': (0x1001ea6, u'\u1EA6'), + 'Acircumflexhook': (0x1001ea8, u'\u1EA8'), + 'Acircumflextilde': (0x1001eaa, u'\u1EAA'), + 'Adiaeresis': (0x00c4, u'\u00C4'), + 'Agrave': (0x00c0, u'\u00C0'), + 'Ahook': (0x1001ea2, u'\u1EA2'), + 'Amacron': (0x03c0, u'\u0100'), + 'Aogonek': (0x01a1, u'\u0104'), + 'Arabic_0': (0x1000660, u'\u0660'), + 'Arabic_1': (0x1000661, u'\u0661'), + 'Arabic_2': (0x1000662, u'\u0662'), + 'Arabic_3': (0x1000663, u'\u0663'), + 'Arabic_4': (0x1000664, u'\u0664'), + 'Arabic_5': (0x1000665, u'\u0665'), + 'Arabic_6': (0x1000666, u'\u0666'), + 'Arabic_7': (0x1000667, u'\u0667'), + 'Arabic_8': (0x1000668, u'\u0668'), + 'Arabic_9': (0x1000669, u'\u0669'), + 'Arabic_ain': (0x05d9, u'\u0639'), + 'Arabic_alef': (0x05c7, u'\u0627'), + 'Arabic_alefmaksura': (0x05e9, u'\u0649'), + 'Arabic_beh': (0x05c8, u'\u0628'), + 'Arabic_comma': (0x05ac, u'\u060C'), + 'Arabic_dad': (0x05d6, u'\u0636'), + 'Arabic_dal': (0x05cf, u'\u062F'), + 'Arabic_damma': (0x05ef, u'\u064F'), + 'Arabic_dammatan': (0x05ec, u'\u064C'), + 'Arabic_ddal': (0x1000688, u'\u0688'), + 'Arabic_farsi_yeh': (0x10006cc, u'\u06CC'), + 'Arabic_fatha': (0x05ee, u'\u064E'), + 'Arabic_fathatan': (0x05eb, u'\u064B'), + 'Arabic_feh': (0x05e1, u'\u0641'), + 'Arabic_fullstop': (0x10006d4, u'\u06D4'), + 'Arabic_gaf': (0x10006af, u'\u06AF'), + 'Arabic_ghain': (0x05da, u'\u063A'), + 'Arabic_ha': (0x05e7, u'\u0647'), + 'Arabic_hah': (0x05cd, u'\u062D'), + 'Arabic_hamza': (0x05c1, u'\u0621'), + 'Arabic_hamza_above': (0x1000654, u'\u0654'), + 'Arabic_hamza_below': (0x1000655, u'\u0655'), + 'Arabic_hamzaonalef': (0x05c3, u'\u0623'), + 'Arabic_hamzaonwaw': (0x05c4, u'\u0624'), + 'Arabic_hamzaonyeh': (0x05c6, u'\u0626'), + 'Arabic_hamzaunderalef': (0x05c5, u'\u0625'), + 'Arabic_heh_doachashmee': (0x10006be, u'\u06BE'), + 'Arabic_heh_goal': (0x10006c1, u'\u06C1'), + 'Arabic_jeem': (0x05cc, u'\u062C'), + 'Arabic_jeh': (0x1000698, u'\u0698'), + 'Arabic_kaf': (0x05e3, u'\u0643'), + 'Arabic_kasra': (0x05f0, u'\u0650'), + 'Arabic_kasratan': (0x05ed, u'\u064D'), + 'Arabic_keheh': (0x10006a9, u'\u06A9'), + 'Arabic_khah': (0x05ce, u'\u062E'), + 'Arabic_lam': (0x05e4, u'\u0644'), + 'Arabic_madda_above': (0x1000653, u'\u0653'), + 'Arabic_maddaonalef': (0x05c2, u'\u0622'), + 'Arabic_meem': (0x05e5, u'\u0645'), + 'Arabic_noon': (0x05e6, u'\u0646'), + 'Arabic_noon_ghunna': (0x10006ba, u'\u06BA'), + 'Arabic_peh': (0x100067e, u'\u067E'), + 'Arabic_percent': (0x100066a, u'\u066A'), + 'Arabic_qaf': (0x05e2, u'\u0642'), + 'Arabic_question_mark': (0x05bf, u'\u061F'), + 'Arabic_ra': (0x05d1, u'\u0631'), + 'Arabic_rreh': (0x1000691, u'\u0691'), + 'Arabic_sad': (0x05d5, u'\u0635'), + 'Arabic_seen': (0x05d3, u'\u0633'), + 'Arabic_semicolon': (0x05bb, u'\u061B'), + 'Arabic_shadda': (0x05f1, u'\u0651'), + 'Arabic_sheen': (0x05d4, u'\u0634'), + 'Arabic_sukun': (0x05f2, u'\u0652'), + 'Arabic_superscript_alef': (0x1000670, u'\u0670'), + 'Arabic_tah': (0x05d7, u'\u0637'), + 'Arabic_tatweel': (0x05e0, u'\u0640'), + 'Arabic_tcheh': (0x1000686, u'\u0686'), + 'Arabic_teh': (0x05ca, u'\u062A'), + 'Arabic_tehmarbuta': (0x05c9, u'\u0629'), + 'Arabic_thal': (0x05d0, u'\u0630'), + 'Arabic_theh': (0x05cb, u'\u062B'), + 'Arabic_tteh': (0x1000679, u'\u0679'), + 'Arabic_veh': (0x10006a4, u'\u06A4'), + 'Arabic_waw': (0x05e8, u'\u0648'), + 'Arabic_yeh': (0x05ea, u'\u064A'), + 'Arabic_yeh_baree': (0x10006d2, u'\u06D2'), + 'Arabic_zah': (0x05d8, u'\u0638'), + 'Arabic_zain': (0x05d2, u'\u0632'), + 'Aring': (0x00c5, u'\u00C5'), + 'Armenian_AT': (0x1000538, u'\u0538'), + 'Armenian_AYB': (0x1000531, u'\u0531'), + 'Armenian_BEN': (0x1000532, u'\u0532'), + 'Armenian_CHA': (0x1000549, u'\u0549'), + 'Armenian_DA': (0x1000534, u'\u0534'), + 'Armenian_DZA': (0x1000541, u'\u0541'), + 'Armenian_E': (0x1000537, u'\u0537'), + 'Armenian_FE': (0x1000556, u'\u0556'), + 'Armenian_GHAT': (0x1000542, u'\u0542'), + 'Armenian_GIM': (0x1000533, u'\u0533'), + 'Armenian_HI': (0x1000545, u'\u0545'), + 'Armenian_HO': (0x1000540, u'\u0540'), + 'Armenian_INI': (0x100053b, u'\u053B'), + 'Armenian_JE': (0x100054b, u'\u054B'), + 'Armenian_KE': (0x1000554, u'\u0554'), + 'Armenian_KEN': (0x100053f, u'\u053F'), + 'Armenian_KHE': (0x100053d, u'\u053D'), + 'Armenian_LYUN': (0x100053c, u'\u053C'), + 'Armenian_MEN': (0x1000544, u'\u0544'), + 'Armenian_NU': (0x1000546, u'\u0546'), + 'Armenian_O': (0x1000555, u'\u0555'), + 'Armenian_PE': (0x100054a, u'\u054A'), + 'Armenian_PYUR': (0x1000553, u'\u0553'), + 'Armenian_RA': (0x100054c, u'\u054C'), + 'Armenian_RE': (0x1000550, u'\u0550'), + 'Armenian_SE': (0x100054d, u'\u054D'), + 'Armenian_SHA': (0x1000547, u'\u0547'), + 'Armenian_TCHE': (0x1000543, u'\u0543'), + 'Armenian_TO': (0x1000539, u'\u0539'), + 'Armenian_TSA': (0x100053e, u'\u053E'), + 'Armenian_TSO': (0x1000551, u'\u0551'), + 'Armenian_TYUN': (0x100054f, u'\u054F'), + 'Armenian_VEV': (0x100054e, u'\u054E'), + 'Armenian_VO': (0x1000548, u'\u0548'), + 'Armenian_VYUN': (0x1000552, u'\u0552'), + 'Armenian_YECH': (0x1000535, u'\u0535'), + 'Armenian_ZA': (0x1000536, u'\u0536'), + 'Armenian_ZHE': (0x100053a, u'\u053A'), + 'Armenian_accent': (0x100055b, u'\u055B'), + 'Armenian_amanak': (0x100055c, u'\u055C'), + 'Armenian_apostrophe': (0x100055a, u'\u055A'), + 'Armenian_at': (0x1000568, u'\u0568'), + 'Armenian_ayb': (0x1000561, u'\u0561'), + 'Armenian_ben': (0x1000562, u'\u0562'), + 'Armenian_but': (0x100055d, u'\u055D'), + 'Armenian_cha': (0x1000579, u'\u0579'), + 'Armenian_da': (0x1000564, u'\u0564'), + 'Armenian_dza': (0x1000571, u'\u0571'), + 'Armenian_e': (0x1000567, u'\u0567'), + 'Armenian_exclam': (0x100055c, u'\u055C'), + 'Armenian_fe': (0x1000586, u'\u0586'), + 'Armenian_full_stop': (0x1000589, u'\u0589'), + 'Armenian_ghat': (0x1000572, u'\u0572'), + 'Armenian_gim': (0x1000563, u'\u0563'), + 'Armenian_hi': (0x1000575, u'\u0575'), + 'Armenian_ho': (0x1000570, u'\u0570'), + 'Armenian_hyphen': (0x100058a, u'\u058A'), + 'Armenian_ini': (0x100056b, u'\u056B'), + 'Armenian_je': (0x100057b, u'\u057B'), + 'Armenian_ke': (0x1000584, u'\u0584'), + 'Armenian_ken': (0x100056f, u'\u056F'), + 'Armenian_khe': (0x100056d, u'\u056D'), + 'Armenian_ligature_ew': (0x1000587, u'\u0587'), + 'Armenian_lyun': (0x100056c, u'\u056C'), + 'Armenian_men': (0x1000574, u'\u0574'), + 'Armenian_nu': (0x1000576, u'\u0576'), + 'Armenian_o': (0x1000585, u'\u0585'), + 'Armenian_paruyk': (0x100055e, u'\u055E'), + 'Armenian_pe': (0x100057a, u'\u057A'), + 'Armenian_pyur': (0x1000583, u'\u0583'), + 'Armenian_question': (0x100055e, u'\u055E'), + 'Armenian_ra': (0x100057c, u'\u057C'), + 'Armenian_re': (0x1000580, u'\u0580'), + 'Armenian_se': (0x100057d, u'\u057D'), + 'Armenian_separation_mark': (0x100055d, u'\u055D'), + 'Armenian_sha': (0x1000577, u'\u0577'), + 'Armenian_shesht': (0x100055b, u'\u055B'), + 'Armenian_tche': (0x1000573, u'\u0573'), + 'Armenian_to': (0x1000569, u'\u0569'), + 'Armenian_tsa': (0x100056e, u'\u056E'), + 'Armenian_tso': (0x1000581, u'\u0581'), + 'Armenian_tyun': (0x100057f, u'\u057F'), + 'Armenian_verjaket': (0x1000589, u'\u0589'), + 'Armenian_vev': (0x100057e, u'\u057E'), + 'Armenian_vo': (0x1000578, u'\u0578'), + 'Armenian_vyun': (0x1000582, u'\u0582'), + 'Armenian_yech': (0x1000565, u'\u0565'), + 'Armenian_yentamna': (0x100058a, u'\u058A'), + 'Armenian_za': (0x1000566, u'\u0566'), + 'Armenian_zhe': (0x100056a, u'\u056A'), + 'Atilde': (0x00c3, u'\u00C3'), + 'B': (0x0042, u'\u0042'), + 'Babovedot': (0x1001e02, u'\u1E02'), + 'Byelorussian_SHORTU': (0x06be, u'\u040E'), + 'Byelorussian_shortu': (0x06ae, u'\u045E'), + 'C': (0x0043, u'\u0043'), + 'Cabovedot': (0x02c5, u'\u010A'), + 'Cacute': (0x01c6, u'\u0106'), + 'Ccaron': (0x01c8, u'\u010C'), + 'Ccedilla': (0x00c7, u'\u00C7'), + 'Ccircumflex': (0x02c6, u'\u0108'), + 'ColonSign': (0x10020a1, u'\u20A1'), + 'CruzeiroSign': (0x10020a2, u'\u20A2'), + 'Cyrillic_A': (0x06e1, u'\u0410'), + 'Cyrillic_BE': (0x06e2, u'\u0411'), + 'Cyrillic_CHE': (0x06fe, u'\u0427'), + 'Cyrillic_CHE_descender': (0x10004b6, u'\u04B6'), + 'Cyrillic_CHE_vertstroke': (0x10004b8, u'\u04B8'), + 'Cyrillic_DE': (0x06e4, u'\u0414'), + 'Cyrillic_DZHE': (0x06bf, u'\u040F'), + 'Cyrillic_E': (0x06fc, u'\u042D'), + 'Cyrillic_EF': (0x06e6, u'\u0424'), + 'Cyrillic_EL': (0x06ec, u'\u041B'), + 'Cyrillic_EM': (0x06ed, u'\u041C'), + 'Cyrillic_EN': (0x06ee, u'\u041D'), + 'Cyrillic_EN_descender': (0x10004a2, u'\u04A2'), + 'Cyrillic_ER': (0x06f2, u'\u0420'), + 'Cyrillic_ES': (0x06f3, u'\u0421'), + 'Cyrillic_GHE': (0x06e7, u'\u0413'), + 'Cyrillic_GHE_bar': (0x1000492, u'\u0492'), + 'Cyrillic_HA': (0x06e8, u'\u0425'), + 'Cyrillic_HARDSIGN': (0x06ff, u'\u042A'), + 'Cyrillic_HA_descender': (0x10004b2, u'\u04B2'), + 'Cyrillic_I': (0x06e9, u'\u0418'), + 'Cyrillic_IE': (0x06e5, u'\u0415'), + 'Cyrillic_IO': (0x06b3, u'\u0401'), + 'Cyrillic_I_macron': (0x10004e2, u'\u04E2'), + 'Cyrillic_JE': (0x06b8, u'\u0408'), + 'Cyrillic_KA': (0x06eb, u'\u041A'), + 'Cyrillic_KA_descender': (0x100049a, u'\u049A'), + 'Cyrillic_KA_vertstroke': (0x100049c, u'\u049C'), + 'Cyrillic_LJE': (0x06b9, u'\u0409'), + 'Cyrillic_NJE': (0x06ba, u'\u040A'), + 'Cyrillic_O': (0x06ef, u'\u041E'), + 'Cyrillic_O_bar': (0x10004e8, u'\u04E8'), + 'Cyrillic_PE': (0x06f0, u'\u041F'), + 'Cyrillic_SCHWA': (0x10004d8, u'\u04D8'), + 'Cyrillic_SHA': (0x06fb, u'\u0428'), + 'Cyrillic_SHCHA': (0x06fd, u'\u0429'), + 'Cyrillic_SHHA': (0x10004ba, u'\u04BA'), + 'Cyrillic_SHORTI': (0x06ea, u'\u0419'), + 'Cyrillic_SOFTSIGN': (0x06f8, u'\u042C'), + 'Cyrillic_TE': (0x06f4, u'\u0422'), + 'Cyrillic_TSE': (0x06e3, u'\u0426'), + 'Cyrillic_U': (0x06f5, u'\u0423'), + 'Cyrillic_U_macron': (0x10004ee, u'\u04EE'), + 'Cyrillic_U_straight': (0x10004ae, u'\u04AE'), + 'Cyrillic_U_straight_bar': (0x10004b0, u'\u04B0'), + 'Cyrillic_VE': (0x06f7, u'\u0412'), + 'Cyrillic_YA': (0x06f1, u'\u042F'), + 'Cyrillic_YERU': (0x06f9, u'\u042B'), + 'Cyrillic_YU': (0x06e0, u'\u042E'), + 'Cyrillic_ZE': (0x06fa, u'\u0417'), + 'Cyrillic_ZHE': (0x06f6, u'\u0416'), + 'Cyrillic_ZHE_descender': (0x1000496, u'\u0496'), + 'Cyrillic_a': (0x06c1, u'\u0430'), + 'Cyrillic_be': (0x06c2, u'\u0431'), + 'Cyrillic_che': (0x06de, u'\u0447'), + 'Cyrillic_che_descender': (0x10004b7, u'\u04B7'), + 'Cyrillic_che_vertstroke': (0x10004b9, u'\u04B9'), + 'Cyrillic_de': (0x06c4, u'\u0434'), + 'Cyrillic_dzhe': (0x06af, u'\u045F'), + 'Cyrillic_e': (0x06dc, u'\u044D'), + 'Cyrillic_ef': (0x06c6, u'\u0444'), + 'Cyrillic_el': (0x06cc, u'\u043B'), + 'Cyrillic_em': (0x06cd, u'\u043C'), + 'Cyrillic_en': (0x06ce, u'\u043D'), + 'Cyrillic_en_descender': (0x10004a3, u'\u04A3'), + 'Cyrillic_er': (0x06d2, u'\u0440'), + 'Cyrillic_es': (0x06d3, u'\u0441'), + 'Cyrillic_ghe': (0x06c7, u'\u0433'), + 'Cyrillic_ghe_bar': (0x1000493, u'\u0493'), + 'Cyrillic_ha': (0x06c8, u'\u0445'), + 'Cyrillic_ha_descender': (0x10004b3, u'\u04B3'), + 'Cyrillic_hardsign': (0x06df, u'\u044A'), + 'Cyrillic_i': (0x06c9, u'\u0438'), + 'Cyrillic_i_macron': (0x10004e3, u'\u04E3'), + 'Cyrillic_ie': (0x06c5, u'\u0435'), + 'Cyrillic_io': (0x06a3, u'\u0451'), + 'Cyrillic_je': (0x06a8, u'\u0458'), + 'Cyrillic_ka': (0x06cb, u'\u043A'), + 'Cyrillic_ka_descender': (0x100049b, u'\u049B'), + 'Cyrillic_ka_vertstroke': (0x100049d, u'\u049D'), + 'Cyrillic_lje': (0x06a9, u'\u0459'), + 'Cyrillic_nje': (0x06aa, u'\u045A'), + 'Cyrillic_o': (0x06cf, u'\u043E'), + 'Cyrillic_o_bar': (0x10004e9, u'\u04E9'), + 'Cyrillic_pe': (0x06d0, u'\u043F'), + 'Cyrillic_schwa': (0x10004d9, u'\u04D9'), + 'Cyrillic_sha': (0x06db, u'\u0448'), + 'Cyrillic_shcha': (0x06dd, u'\u0449'), + 'Cyrillic_shha': (0x10004bb, u'\u04BB'), + 'Cyrillic_shorti': (0x06ca, u'\u0439'), + 'Cyrillic_softsign': (0x06d8, u'\u044C'), + 'Cyrillic_te': (0x06d4, u'\u0442'), + 'Cyrillic_tse': (0x06c3, u'\u0446'), + 'Cyrillic_u': (0x06d5, u'\u0443'), + 'Cyrillic_u_macron': (0x10004ef, u'\u04EF'), + 'Cyrillic_u_straight': (0x10004af, u'\u04AF'), + 'Cyrillic_u_straight_bar': (0x10004b1, u'\u04B1'), + 'Cyrillic_ve': (0x06d7, u'\u0432'), + 'Cyrillic_ya': (0x06d1, u'\u044F'), + 'Cyrillic_yeru': (0x06d9, u'\u044B'), + 'Cyrillic_yu': (0x06c0, u'\u044E'), + 'Cyrillic_ze': (0x06da, u'\u0437'), + 'Cyrillic_zhe': (0x06d6, u'\u0436'), + 'Cyrillic_zhe_descender': (0x1000497, u'\u0497'), + 'D': (0x0044, u'\u0044'), + 'Dabovedot': (0x1001e0a, u'\u1E0A'), + 'Dcaron': (0x01cf, u'\u010E'), + 'DongSign': (0x10020ab, u'\u20AB'), + 'Dstroke': (0x01d0, u'\u0110'), + 'E': (0x0045, u'\u0045'), + 'ENG': (0x03bd, u'\u014A'), + 'ETH': (0x00d0, u'\u00D0'), + 'EZH': (0x10001b7, u'\u01B7'), + 'Eabovedot': (0x03cc, u'\u0116'), + 'Eacute': (0x00c9, u'\u00C9'), + 'Ebelowdot': (0x1001eb8, u'\u1EB8'), + 'Ecaron': (0x01cc, u'\u011A'), + 'Ecircumflex': (0x00ca, u'\u00CA'), + 'Ecircumflexacute': (0x1001ebe, u'\u1EBE'), + 'Ecircumflexbelowdot': (0x1001ec6, u'\u1EC6'), + 'Ecircumflexgrave': (0x1001ec0, u'\u1EC0'), + 'Ecircumflexhook': (0x1001ec2, u'\u1EC2'), + 'Ecircumflextilde': (0x1001ec4, u'\u1EC4'), + 'EcuSign': (0x10020a0, u'\u20A0'), + 'Ediaeresis': (0x00cb, u'\u00CB'), + 'Egrave': (0x00c8, u'\u00C8'), + 'Ehook': (0x1001eba, u'\u1EBA'), + 'Emacron': (0x03aa, u'\u0112'), + 'Eogonek': (0x01ca, u'\u0118'), + 'Etilde': (0x1001ebc, u'\u1EBC'), + 'EuroSign': (0x20ac, u'\u20AC'), + 'F': (0x0046, u'\u0046'), + 'FFrancSign': (0x10020a3, u'\u20A3'), + 'Fabovedot': (0x1001e1e, u'\u1E1E'), + 'Farsi_0': (0x10006f0, u'\u06F0'), + 'Farsi_1': (0x10006f1, u'\u06F1'), + 'Farsi_2': (0x10006f2, u'\u06F2'), + 'Farsi_3': (0x10006f3, u'\u06F3'), + 'Farsi_4': (0x10006f4, u'\u06F4'), + 'Farsi_5': (0x10006f5, u'\u06F5'), + 'Farsi_6': (0x10006f6, u'\u06F6'), + 'Farsi_7': (0x10006f7, u'\u06F7'), + 'Farsi_8': (0x10006f8, u'\u06F8'), + 'Farsi_9': (0x10006f9, u'\u06F9'), + 'Farsi_yeh': (0x10006cc, u'\u06CC'), + 'G': (0x0047, u'\u0047'), + 'Gabovedot': (0x02d5, u'\u0120'), + 'Gbreve': (0x02ab, u'\u011E'), + 'Gcaron': (0x10001e6, u'\u01E6'), + 'Gcedilla': (0x03ab, u'\u0122'), + 'Gcircumflex': (0x02d8, u'\u011C'), + 'Georgian_an': (0x10010d0, u'\u10D0'), + 'Georgian_ban': (0x10010d1, u'\u10D1'), + 'Georgian_can': (0x10010ea, u'\u10EA'), + 'Georgian_char': (0x10010ed, u'\u10ED'), + 'Georgian_chin': (0x10010e9, u'\u10E9'), + 'Georgian_cil': (0x10010ec, u'\u10EC'), + 'Georgian_don': (0x10010d3, u'\u10D3'), + 'Georgian_en': (0x10010d4, u'\u10D4'), + 'Georgian_fi': (0x10010f6, u'\u10F6'), + 'Georgian_gan': (0x10010d2, u'\u10D2'), + 'Georgian_ghan': (0x10010e6, u'\u10E6'), + 'Georgian_hae': (0x10010f0, u'\u10F0'), + 'Georgian_har': (0x10010f4, u'\u10F4'), + 'Georgian_he': (0x10010f1, u'\u10F1'), + 'Georgian_hie': (0x10010f2, u'\u10F2'), + 'Georgian_hoe': (0x10010f5, u'\u10F5'), + 'Georgian_in': (0x10010d8, u'\u10D8'), + 'Georgian_jhan': (0x10010ef, u'\u10EF'), + 'Georgian_jil': (0x10010eb, u'\u10EB'), + 'Georgian_kan': (0x10010d9, u'\u10D9'), + 'Georgian_khar': (0x10010e5, u'\u10E5'), + 'Georgian_las': (0x10010da, u'\u10DA'), + 'Georgian_man': (0x10010db, u'\u10DB'), + 'Georgian_nar': (0x10010dc, u'\u10DC'), + 'Georgian_on': (0x10010dd, u'\u10DD'), + 'Georgian_par': (0x10010de, u'\u10DE'), + 'Georgian_phar': (0x10010e4, u'\u10E4'), + 'Georgian_qar': (0x10010e7, u'\u10E7'), + 'Georgian_rae': (0x10010e0, u'\u10E0'), + 'Georgian_san': (0x10010e1, u'\u10E1'), + 'Georgian_shin': (0x10010e8, u'\u10E8'), + 'Georgian_tan': (0x10010d7, u'\u10D7'), + 'Georgian_tar': (0x10010e2, u'\u10E2'), + 'Georgian_un': (0x10010e3, u'\u10E3'), + 'Georgian_vin': (0x10010d5, u'\u10D5'), + 'Georgian_we': (0x10010f3, u'\u10F3'), + 'Georgian_xan': (0x10010ee, u'\u10EE'), + 'Georgian_zen': (0x10010d6, u'\u10D6'), + 'Georgian_zhar': (0x10010df, u'\u10DF'), + 'Greek_ALPHA': (0x07c1, u'\u0391'), + 'Greek_ALPHAaccent': (0x07a1, u'\u0386'), + 'Greek_BETA': (0x07c2, u'\u0392'), + 'Greek_CHI': (0x07d7, u'\u03A7'), + 'Greek_DELTA': (0x07c4, u'\u0394'), + 'Greek_EPSILON': (0x07c5, u'\u0395'), + 'Greek_EPSILONaccent': (0x07a2, u'\u0388'), + 'Greek_ETA': (0x07c7, u'\u0397'), + 'Greek_ETAaccent': (0x07a3, u'\u0389'), + 'Greek_GAMMA': (0x07c3, u'\u0393'), + 'Greek_IOTA': (0x07c9, u'\u0399'), + 'Greek_IOTAaccent': (0x07a4, u'\u038A'), + 'Greek_IOTAdieresis': (0x07a5, u'\u03AA'), + 'Greek_KAPPA': (0x07ca, u'\u039A'), + 'Greek_LAMBDA': (0x07cb, u'\u039B'), + 'Greek_LAMDA': (0x07cb, u'\u039B'), + 'Greek_MU': (0x07cc, u'\u039C'), + 'Greek_NU': (0x07cd, u'\u039D'), + 'Greek_OMEGA': (0x07d9, u'\u03A9'), + 'Greek_OMEGAaccent': (0x07ab, u'\u038F'), + 'Greek_OMICRON': (0x07cf, u'\u039F'), + 'Greek_OMICRONaccent': (0x07a7, u'\u038C'), + 'Greek_PHI': (0x07d6, u'\u03A6'), + 'Greek_PI': (0x07d0, u'\u03A0'), + 'Greek_PSI': (0x07d8, u'\u03A8'), + 'Greek_RHO': (0x07d1, u'\u03A1'), + 'Greek_SIGMA': (0x07d2, u'\u03A3'), + 'Greek_TAU': (0x07d4, u'\u03A4'), + 'Greek_THETA': (0x07c8, u'\u0398'), + 'Greek_UPSILON': (0x07d5, u'\u03A5'), + 'Greek_UPSILONaccent': (0x07a8, u'\u038E'), + 'Greek_UPSILONdieresis': (0x07a9, u'\u03AB'), + 'Greek_XI': (0x07ce, u'\u039E'), + 'Greek_ZETA': (0x07c6, u'\u0396'), + 'Greek_accentdieresis': (0x07ae, u'\u0385'), + 'Greek_alpha': (0x07e1, u'\u03B1'), + 'Greek_alphaaccent': (0x07b1, u'\u03AC'), + 'Greek_beta': (0x07e2, u'\u03B2'), + 'Greek_chi': (0x07f7, u'\u03C7'), + 'Greek_delta': (0x07e4, u'\u03B4'), + 'Greek_epsilon': (0x07e5, u'\u03B5'), + 'Greek_epsilonaccent': (0x07b2, u'\u03AD'), + 'Greek_eta': (0x07e7, u'\u03B7'), + 'Greek_etaaccent': (0x07b3, u'\u03AE'), + 'Greek_finalsmallsigma': (0x07f3, u'\u03C2'), + 'Greek_gamma': (0x07e3, u'\u03B3'), + 'Greek_horizbar': (0x07af, u'\u2015'), + 'Greek_iota': (0x07e9, u'\u03B9'), + 'Greek_iotaaccent': (0x07b4, u'\u03AF'), + 'Greek_iotaaccentdieresis': (0x07b6, u'\u0390'), + 'Greek_iotadieresis': (0x07b5, u'\u03CA'), + 'Greek_kappa': (0x07ea, u'\u03BA'), + 'Greek_lambda': (0x07eb, u'\u03BB'), + 'Greek_lamda': (0x07eb, u'\u03BB'), + 'Greek_mu': (0x07ec, u'\u03BC'), + 'Greek_nu': (0x07ed, u'\u03BD'), + 'Greek_omega': (0x07f9, u'\u03C9'), + 'Greek_omegaaccent': (0x07bb, u'\u03CE'), + 'Greek_omicron': (0x07ef, u'\u03BF'), + 'Greek_omicronaccent': (0x07b7, u'\u03CC'), + 'Greek_phi': (0x07f6, u'\u03C6'), + 'Greek_pi': (0x07f0, u'\u03C0'), + 'Greek_psi': (0x07f8, u'\u03C8'), + 'Greek_rho': (0x07f1, u'\u03C1'), + 'Greek_sigma': (0x07f2, u'\u03C3'), + 'Greek_tau': (0x07f4, u'\u03C4'), + 'Greek_theta': (0x07e8, u'\u03B8'), + 'Greek_upsilon': (0x07f5, u'\u03C5'), + 'Greek_upsilonaccent': (0x07b8, u'\u03CD'), + 'Greek_upsilonaccentdieresis': (0x07ba, u'\u03B0'), + 'Greek_upsilondieresis': (0x07b9, u'\u03CB'), + 'Greek_xi': (0x07ee, u'\u03BE'), + 'Greek_zeta': (0x07e6, u'\u03B6'), + 'H': (0x0048, u'\u0048'), + 'Hcircumflex': (0x02a6, u'\u0124'), + 'Hstroke': (0x02a1, u'\u0126'), + 'I': (0x0049, u'\u0049'), + 'Iabovedot': (0x02a9, u'\u0130'), + 'Iacute': (0x00cd, u'\u00CD'), + 'Ibelowdot': (0x1001eca, u'\u1ECA'), + 'Ibreve': (0x100012c, u'\u012C'), + 'Icircumflex': (0x00ce, u'\u00CE'), + 'Idiaeresis': (0x00cf, u'\u00CF'), + 'Igrave': (0x00cc, u'\u00CC'), + 'Ihook': (0x1001ec8, u'\u1EC8'), + 'Imacron': (0x03cf, u'\u012A'), + 'Iogonek': (0x03c7, u'\u012E'), + 'Itilde': (0x03a5, u'\u0128'), + 'J': (0x004a, u'\u004A'), + 'Jcircumflex': (0x02ac, u'\u0134'), + 'K': (0x004b, u'\u004B'), + 'KP_0': (0xffb0, None), + 'KP_1': (0xffb1, None), + 'KP_2': (0xffb2, None), + 'KP_3': (0xffb3, None), + 'KP_4': (0xffb4, None), + 'KP_5': (0xffb5, None), + 'KP_6': (0xffb6, None), + 'KP_7': (0xffb7, None), + 'KP_8': (0xffb8, None), + 'KP_9': (0xffb9, None), + 'KP_Add': (0xffab, None), + 'KP_Begin': (0xff9d, None), + 'KP_Decimal': (0xffae, None), + 'KP_Delete': (0xff9f, None), + 'KP_Divide': (0xffaf, None), + 'KP_Down': (0xff99, None), + 'KP_End': (0xff9c, None), + 'KP_Enter': (0xff8d, None), + 'KP_Equal': (0xffbd, None), + 'KP_F1': (0xff91, None), + 'KP_F2': (0xff92, None), + 'KP_F3': (0xff93, None), + 'KP_F4': (0xff94, None), + 'KP_Home': (0xff95, None), + 'KP_Insert': (0xff9e, None), + 'KP_Left': (0xff96, None), + 'KP_Multiply': (0xffaa, None), + 'KP_Next': (0xff9b, None), + 'KP_Page_Down': (0xff9b, None), + 'KP_Page_Up': (0xff9a, None), + 'KP_Prior': (0xff9a, None), + 'KP_Right': (0xff98, None), + 'KP_Separator': (0xffac, None), + 'KP_Space': (0xff80, None), + 'KP_Subtract': (0xffad, None), + 'KP_Tab': (0xff89, None), + 'KP_Up': (0xff97, None), + 'Kcedilla': (0x03d3, u'\u0136'), + 'L': (0x004c, u'\u004C'), + 'Lacute': (0x01c5, u'\u0139'), + 'Lbelowdot': (0x1001e36, u'\u1E36'), + 'Lcaron': (0x01a5, u'\u013D'), + 'Lcedilla': (0x03a6, u'\u013B'), + 'LiraSign': (0x10020a4, u'\u20A4'), + 'Lstroke': (0x01a3, u'\u0141'), + 'M': (0x004d, u'\u004D'), + 'Mabovedot': (0x1001e40, u'\u1E40'), + 'Macedonia_DSE': (0x06b5, u'\u0405'), + 'Macedonia_GJE': (0x06b2, u'\u0403'), + 'Macedonia_KJE': (0x06bc, u'\u040C'), + 'Macedonia_dse': (0x06a5, u'\u0455'), + 'Macedonia_gje': (0x06a2, u'\u0453'), + 'Macedonia_kje': (0x06ac, u'\u045C'), + 'MillSign': (0x10020a5, u'\u20A5'), + 'N': (0x004e, u'\u004E'), + 'Nacute': (0x01d1, u'\u0143'), + 'NairaSign': (0x10020a6, u'\u20A6'), + 'Ncaron': (0x01d2, u'\u0147'), + 'Ncedilla': (0x03d1, u'\u0145'), + 'NewSheqelSign': (0x10020aa, u'\u20AA'), + 'Ntilde': (0x00d1, u'\u00D1'), + 'O': (0x004f, u'\u004F'), + 'OE': (0x13bc, u'\u0152'), + 'Oacute': (0x00d3, u'\u00D3'), + 'Obarred': (0x100019f, u'\u019F'), + 'Obelowdot': (0x1001ecc, u'\u1ECC'), + 'Ocaron': (0x10001d1, u'\u01D2'), + 'Ocircumflex': (0x00d4, u'\u00D4'), + 'Ocircumflexacute': (0x1001ed0, u'\u1ED0'), + 'Ocircumflexbelowdot': (0x1001ed8, u'\u1ED8'), + 'Ocircumflexgrave': (0x1001ed2, u'\u1ED2'), + 'Ocircumflexhook': (0x1001ed4, u'\u1ED4'), + 'Ocircumflextilde': (0x1001ed6, u'\u1ED6'), + 'Odiaeresis': (0x00d6, u'\u00D6'), + 'Odoubleacute': (0x01d5, u'\u0150'), + 'Ograve': (0x00d2, u'\u00D2'), + 'Ohook': (0x1001ece, u'\u1ECE'), + 'Ohorn': (0x10001a0, u'\u01A0'), + 'Ohornacute': (0x1001eda, u'\u1EDA'), + 'Ohornbelowdot': (0x1001ee2, u'\u1EE2'), + 'Ohorngrave': (0x1001edc, u'\u1EDC'), + 'Ohornhook': (0x1001ede, u'\u1EDE'), + 'Ohorntilde': (0x1001ee0, u'\u1EE0'), + 'Omacron': (0x03d2, u'\u014C'), + 'Ooblique': (0x00d8, u'\u00D8'), + 'Oslash': (0x00d8, u'\u00D8'), + 'Otilde': (0x00d5, u'\u00D5'), + 'P': (0x0050, u'\u0050'), + 'Pabovedot': (0x1001e56, u'\u1E56'), + 'PesetaSign': (0x10020a7, u'\u20A7'), + 'Q': (0x0051, u'\u0051'), + 'R': (0x0052, u'\u0052'), + 'Racute': (0x01c0, u'\u0154'), + 'Rcaron': (0x01d8, u'\u0158'), + 'Rcedilla': (0x03a3, u'\u0156'), + 'RupeeSign': (0x10020a8, u'\u20A8'), + 'S': (0x0053, u'\u0053'), + 'SCHWA': (0x100018f, u'\u018F'), + 'Sabovedot': (0x1001e60, u'\u1E60'), + 'Sacute': (0x01a6, u'\u015A'), + 'Scaron': (0x01a9, u'\u0160'), + 'Scedilla': (0x01aa, u'\u015E'), + 'Scircumflex': (0x02de, u'\u015C'), + 'Serbian_DJE': (0x06b1, u'\u0402'), + 'Serbian_TSHE': (0x06bb, u'\u040B'), + 'Serbian_dje': (0x06a1, u'\u0452'), + 'Serbian_tshe': (0x06ab, u'\u045B'), + 'Sinh_a': (0x1000d85, u'\u0D85'), + 'Sinh_aa': (0x1000d86, u'\u0D86'), + 'Sinh_aa2': (0x1000dcf, u'\u0DCF'), + 'Sinh_ae': (0x1000d87, u'\u0D87'), + 'Sinh_ae2': (0x1000dd0, u'\u0DD0'), + 'Sinh_aee': (0x1000d88, u'\u0D88'), + 'Sinh_aee2': (0x1000dd1, u'\u0DD1'), + 'Sinh_ai': (0x1000d93, u'\u0D93'), + 'Sinh_ai2': (0x1000ddb, u'\u0DDB'), + 'Sinh_al': (0x1000dca, u'\u0DCA'), + 'Sinh_au': (0x1000d96, u'\u0D96'), + 'Sinh_au2': (0x1000dde, u'\u0DDE'), + 'Sinh_ba': (0x1000db6, u'\u0DB6'), + 'Sinh_bha': (0x1000db7, u'\u0DB7'), + 'Sinh_ca': (0x1000da0, u'\u0DA0'), + 'Sinh_cha': (0x1000da1, u'\u0DA1'), + 'Sinh_dda': (0x1000da9, u'\u0DA9'), + 'Sinh_ddha': (0x1000daa, u'\u0DAA'), + 'Sinh_dha': (0x1000daf, u'\u0DAF'), + 'Sinh_dhha': (0x1000db0, u'\u0DB0'), + 'Sinh_e': (0x1000d91, u'\u0D91'), + 'Sinh_e2': (0x1000dd9, u'\u0DD9'), + 'Sinh_ee': (0x1000d92, u'\u0D92'), + 'Sinh_ee2': (0x1000dda, u'\u0DDA'), + 'Sinh_fa': (0x1000dc6, u'\u0DC6'), + 'Sinh_ga': (0x1000d9c, u'\u0D9C'), + 'Sinh_gha': (0x1000d9d, u'\u0D9D'), + 'Sinh_h2': (0x1000d83, u'\u0D83'), + 'Sinh_ha': (0x1000dc4, u'\u0DC4'), + 'Sinh_i': (0x1000d89, u'\u0D89'), + 'Sinh_i2': (0x1000dd2, u'\u0DD2'), + 'Sinh_ii': (0x1000d8a, u'\u0D8A'), + 'Sinh_ii2': (0x1000dd3, u'\u0DD3'), + 'Sinh_ja': (0x1000da2, u'\u0DA2'), + 'Sinh_jha': (0x1000da3, u'\u0DA3'), + 'Sinh_jnya': (0x1000da5, u'\u0DA5'), + 'Sinh_ka': (0x1000d9a, u'\u0D9A'), + 'Sinh_kha': (0x1000d9b, u'\u0D9B'), + 'Sinh_kunddaliya': (0x1000df4, u'\u0DF4'), + 'Sinh_la': (0x1000dbd, u'\u0DBD'), + 'Sinh_lla': (0x1000dc5, u'\u0DC5'), + 'Sinh_lu': (0x1000d8f, u'\u0D8F'), + 'Sinh_lu2': (0x1000ddf, u'\u0DDF'), + 'Sinh_luu': (0x1000d90, u'\u0D90'), + 'Sinh_luu2': (0x1000df3, u'\u0DF3'), + 'Sinh_ma': (0x1000db8, u'\u0DB8'), + 'Sinh_mba': (0x1000db9, u'\u0DB9'), + 'Sinh_na': (0x1000db1, u'\u0DB1'), + 'Sinh_ndda': (0x1000dac, u'\u0DAC'), + 'Sinh_ndha': (0x1000db3, u'\u0DB3'), + 'Sinh_ng': (0x1000d82, u'\u0D82'), + 'Sinh_ng2': (0x1000d9e, u'\u0D9E'), + 'Sinh_nga': (0x1000d9f, u'\u0D9F'), + 'Sinh_nja': (0x1000da6, u'\u0DA6'), + 'Sinh_nna': (0x1000dab, u'\u0DAB'), + 'Sinh_nya': (0x1000da4, u'\u0DA4'), + 'Sinh_o': (0x1000d94, u'\u0D94'), + 'Sinh_o2': (0x1000ddc, u'\u0DDC'), + 'Sinh_oo': (0x1000d95, u'\u0D95'), + 'Sinh_oo2': (0x1000ddd, u'\u0DDD'), + 'Sinh_pa': (0x1000db4, u'\u0DB4'), + 'Sinh_pha': (0x1000db5, u'\u0DB5'), + 'Sinh_ra': (0x1000dbb, u'\u0DBB'), + 'Sinh_ri': (0x1000d8d, u'\u0D8D'), + 'Sinh_rii': (0x1000d8e, u'\u0D8E'), + 'Sinh_ru2': (0x1000dd8, u'\u0DD8'), + 'Sinh_ruu2': (0x1000df2, u'\u0DF2'), + 'Sinh_sa': (0x1000dc3, u'\u0DC3'), + 'Sinh_sha': (0x1000dc1, u'\u0DC1'), + 'Sinh_ssha': (0x1000dc2, u'\u0DC2'), + 'Sinh_tha': (0x1000dad, u'\u0DAD'), + 'Sinh_thha': (0x1000dae, u'\u0DAE'), + 'Sinh_tta': (0x1000da7, u'\u0DA7'), + 'Sinh_ttha': (0x1000da8, u'\u0DA8'), + 'Sinh_u': (0x1000d8b, u'\u0D8B'), + 'Sinh_u2': (0x1000dd4, u'\u0DD4'), + 'Sinh_uu': (0x1000d8c, u'\u0D8C'), + 'Sinh_uu2': (0x1000dd6, u'\u0DD6'), + 'Sinh_va': (0x1000dc0, u'\u0DC0'), + 'Sinh_ya': (0x1000dba, u'\u0DBA'), + 'T': (0x0054, u'\u0054'), + 'THORN': (0x00de, u'\u00DE'), + 'Tabovedot': (0x1001e6a, u'\u1E6A'), + 'Tcaron': (0x01ab, u'\u0164'), + 'Tcedilla': (0x01de, u'\u0162'), + 'Thai_baht': (0x0ddf, u'\u0E3F'), + 'Thai_bobaimai': (0x0dba, u'\u0E1A'), + 'Thai_chochan': (0x0da8, u'\u0E08'), + 'Thai_chochang': (0x0daa, u'\u0E0A'), + 'Thai_choching': (0x0da9, u'\u0E09'), + 'Thai_chochoe': (0x0dac, u'\u0E0C'), + 'Thai_dochada': (0x0dae, u'\u0E0E'), + 'Thai_dodek': (0x0db4, u'\u0E14'), + 'Thai_fofa': (0x0dbd, u'\u0E1D'), + 'Thai_fofan': (0x0dbf, u'\u0E1F'), + 'Thai_hohip': (0x0dcb, u'\u0E2B'), + 'Thai_honokhuk': (0x0dce, u'\u0E2E'), + 'Thai_khokhai': (0x0da2, u'\u0E02'), + 'Thai_khokhon': (0x0da5, u'\u0E05'), + 'Thai_khokhuat': (0x0da3, u'\u0E03'), + 'Thai_khokhwai': (0x0da4, u'\u0E04'), + 'Thai_khorakhang': (0x0da6, u'\u0E06'), + 'Thai_kokai': (0x0da1, u'\u0E01'), + 'Thai_lakkhangyao': (0x0de5, u'\u0E45'), + 'Thai_lekchet': (0x0df7, u'\u0E57'), + 'Thai_lekha': (0x0df5, u'\u0E55'), + 'Thai_lekhok': (0x0df6, u'\u0E56'), + 'Thai_lekkao': (0x0df9, u'\u0E59'), + 'Thai_leknung': (0x0df1, u'\u0E51'), + 'Thai_lekpaet': (0x0df8, u'\u0E58'), + 'Thai_leksam': (0x0df3, u'\u0E53'), + 'Thai_leksi': (0x0df4, u'\u0E54'), + 'Thai_leksong': (0x0df2, u'\u0E52'), + 'Thai_leksun': (0x0df0, u'\u0E50'), + 'Thai_lochula': (0x0dcc, u'\u0E2C'), + 'Thai_loling': (0x0dc5, u'\u0E25'), + 'Thai_lu': (0x0dc6, u'\u0E26'), + 'Thai_maichattawa': (0x0deb, u'\u0E4B'), + 'Thai_maiek': (0x0de8, u'\u0E48'), + 'Thai_maihanakat': (0x0dd1, u'\u0E31'), + 'Thai_maitaikhu': (0x0de7, u'\u0E47'), + 'Thai_maitho': (0x0de9, u'\u0E49'), + 'Thai_maitri': (0x0dea, u'\u0E4A'), + 'Thai_maiyamok': (0x0de6, u'\u0E46'), + 'Thai_moma': (0x0dc1, u'\u0E21'), + 'Thai_ngongu': (0x0da7, u'\u0E07'), + 'Thai_nikhahit': (0x0ded, u'\u0E4D'), + 'Thai_nonen': (0x0db3, u'\u0E13'), + 'Thai_nonu': (0x0db9, u'\u0E19'), + 'Thai_oang': (0x0dcd, u'\u0E2D'), + 'Thai_paiyannoi': (0x0dcf, u'\u0E2F'), + 'Thai_phinthu': (0x0dda, u'\u0E3A'), + 'Thai_phophan': (0x0dbe, u'\u0E1E'), + 'Thai_phophung': (0x0dbc, u'\u0E1C'), + 'Thai_phosamphao': (0x0dc0, u'\u0E20'), + 'Thai_popla': (0x0dbb, u'\u0E1B'), + 'Thai_rorua': (0x0dc3, u'\u0E23'), + 'Thai_ru': (0x0dc4, u'\u0E24'), + 'Thai_saraa': (0x0dd0, u'\u0E30'), + 'Thai_saraaa': (0x0dd2, u'\u0E32'), + 'Thai_saraae': (0x0de1, u'\u0E41'), + 'Thai_saraaimaimalai': (0x0de4, u'\u0E44'), + 'Thai_saraaimaimuan': (0x0de3, u'\u0E43'), + 'Thai_saraam': (0x0dd3, u'\u0E33'), + 'Thai_sarae': (0x0de0, u'\u0E40'), + 'Thai_sarai': (0x0dd4, u'\u0E34'), + 'Thai_saraii': (0x0dd5, u'\u0E35'), + 'Thai_sarao': (0x0de2, u'\u0E42'), + 'Thai_sarau': (0x0dd8, u'\u0E38'), + 'Thai_saraue': (0x0dd6, u'\u0E36'), + 'Thai_sarauee': (0x0dd7, u'\u0E37'), + 'Thai_sarauu': (0x0dd9, u'\u0E39'), + 'Thai_sorusi': (0x0dc9, u'\u0E29'), + 'Thai_sosala': (0x0dc8, u'\u0E28'), + 'Thai_soso': (0x0dab, u'\u0E0B'), + 'Thai_sosua': (0x0dca, u'\u0E2A'), + 'Thai_thanthakhat': (0x0dec, u'\u0E4C'), + 'Thai_thonangmontho': (0x0db1, u'\u0E11'), + 'Thai_thophuthao': (0x0db2, u'\u0E12'), + 'Thai_thothahan': (0x0db7, u'\u0E17'), + 'Thai_thothan': (0x0db0, u'\u0E10'), + 'Thai_thothong': (0x0db8, u'\u0E18'), + 'Thai_thothung': (0x0db6, u'\u0E16'), + 'Thai_topatak': (0x0daf, u'\u0E0F'), + 'Thai_totao': (0x0db5, u'\u0E15'), + 'Thai_wowaen': (0x0dc7, u'\u0E27'), + 'Thai_yoyak': (0x0dc2, u'\u0E22'), + 'Thai_yoying': (0x0dad, u'\u0E0D'), + 'Tslash': (0x03ac, u'\u0166'), + 'U': (0x0055, u'\u0055'), + 'Uacute': (0x00da, u'\u00DA'), + 'Ubelowdot': (0x1001ee4, u'\u1EE4'), + 'Ubreve': (0x02dd, u'\u016C'), + 'Ucircumflex': (0x00db, u'\u00DB'), + 'Udiaeresis': (0x00dc, u'\u00DC'), + 'Udoubleacute': (0x01db, u'\u0170'), + 'Ugrave': (0x00d9, u'\u00D9'), + 'Uhook': (0x1001ee6, u'\u1EE6'), + 'Uhorn': (0x10001af, u'\u01AF'), + 'Uhornacute': (0x1001ee8, u'\u1EE8'), + 'Uhornbelowdot': (0x1001ef0, u'\u1EF0'), + 'Uhorngrave': (0x1001eea, u'\u1EEA'), + 'Uhornhook': (0x1001eec, u'\u1EEC'), + 'Uhorntilde': (0x1001eee, u'\u1EEE'), + 'Ukrainian_GHE_WITH_UPTURN': (0x06bd, u'\u0490'), + 'Ukrainian_I': (0x06b6, u'\u0406'), + 'Ukrainian_IE': (0x06b4, u'\u0404'), + 'Ukrainian_YI': (0x06b7, u'\u0407'), + 'Ukrainian_ghe_with_upturn': (0x06ad, u'\u0491'), + 'Ukrainian_i': (0x06a6, u'\u0456'), + 'Ukrainian_ie': (0x06a4, u'\u0454'), + 'Ukrainian_yi': (0x06a7, u'\u0457'), + 'Umacron': (0x03de, u'\u016A'), + 'Uogonek': (0x03d9, u'\u0172'), + 'Uring': (0x01d9, u'\u016E'), + 'Utilde': (0x03dd, u'\u0168'), + 'V': (0x0056, u'\u0056'), + 'W': (0x0057, u'\u0057'), + 'Wacute': (0x1001e82, u'\u1E82'), + 'Wcircumflex': (0x1000174, u'\u0174'), + 'Wdiaeresis': (0x1001e84, u'\u1E84'), + 'Wgrave': (0x1001e80, u'\u1E80'), + 'WonSign': (0x10020a9, u'\u20A9'), + 'X': (0x0058, u'\u0058'), + 'Xabovedot': (0x1001e8a, u'\u1E8A'), + 'Y': (0x0059, u'\u0059'), + 'Yacute': (0x00dd, u'\u00DD'), + 'Ybelowdot': (0x1001ef4, u'\u1EF4'), + 'Ycircumflex': (0x1000176, u'\u0176'), + 'Ydiaeresis': (0x13be, u'\u0178'), + 'Ygrave': (0x1001ef2, u'\u1EF2'), + 'Yhook': (0x1001ef6, u'\u1EF6'), + 'Ytilde': (0x1001ef8, u'\u1EF8'), + 'Z': (0x005a, u'\u005A'), + 'Zabovedot': (0x01af, u'\u017B'), + 'Zacute': (0x01ac, u'\u0179'), + 'Zcaron': (0x01ae, u'\u017D'), + 'Zstroke': (0x10001b5, u'\u01B5'), + 'a': (0x0061, u'\u0061'), + 'aacute': (0x00e1, u'\u00E1'), + 'abelowdot': (0x1001ea1, u'\u1EA1'), + 'abovedot': (0x01ff, u'\u02D9'), + 'abreve': (0x01e3, u'\u0103'), + 'abreveacute': (0x1001eaf, u'\u1EAF'), + 'abrevebelowdot': (0x1001eb7, u'\u1EB7'), + 'abrevegrave': (0x1001eb1, u'\u1EB1'), + 'abrevehook': (0x1001eb3, u'\u1EB3'), + 'abrevetilde': (0x1001eb5, u'\u1EB5'), + 'acircumflex': (0x00e2, u'\u00E2'), + 'acircumflexacute': (0x1001ea5, u'\u1EA5'), + 'acircumflexbelowdot': (0x1001ead, u'\u1EAD'), + 'acircumflexgrave': (0x1001ea7, u'\u1EA7'), + 'acircumflexhook': (0x1001ea9, u'\u1EA9'), + 'acircumflextilde': (0x1001eab, u'\u1EAB'), + 'acute': (0x00b4, u'\u00B4'), + 'adiaeresis': (0x00e4, u'\u00E4'), + 'ae': (0x00e6, u'\u00E6'), + 'agrave': (0x00e0, u'\u00E0'), + 'ahook': (0x1001ea3, u'\u1EA3'), + 'amacron': (0x03e0, u'\u0101'), + 'ampersand': (0x0026, u'\u0026'), + 'aogonek': (0x01b1, u'\u0105'), + 'apostrophe': (0x0027, u'\u0027'), + 'approxeq': (0x1002248, u'\u2245'), + 'approximate': (0x08c8, u'\u223C'), + 'aring': (0x00e5, u'\u00E5'), + 'asciicircum': (0x005e, u'\u005E'), + 'asciitilde': (0x007e, u'\u007E'), + 'asterisk': (0x002a, u'\u002A'), + 'at': (0x0040, u'\u0040'), + 'atilde': (0x00e3, u'\u00E3'), + 'b': (0x0062, u'\u0062'), + 'babovedot': (0x1001e03, u'\u1E03'), + 'backslash': (0x005c, u'\u005C'), + 'ballotcross': (0x0af4, u'\u2717'), + 'bar': (0x007c, u'\u007C'), + 'because': (0x1002235, u'\u2235'), + 'botintegral': (0x08a5, u'\u2321'), + 'botleftparens': (0x08ac, u'\u239D'), + 'botleftsqbracket': (0x08a8, u'\u23A3'), + 'botrightparens': (0x08ae, u'\u23A0'), + 'botrightsqbracket': (0x08aa, u'\u23A6'), + 'bott': (0x09f6, u'\u2534'), + 'braceleft': (0x007b, u'\u007B'), + 'braceright': (0x007d, u'\u007D'), + 'bracketleft': (0x005b, u'\u005B'), + 'bracketright': (0x005d, u'\u005D'), + 'braille_blank': (0x1002800, u'\u2800'), + 'braille_dots_1': (0x1002801, u'\u2801'), + 'braille_dots_12': (0x1002803, u'\u2803'), + 'braille_dots_123': (0x1002807, u'\u2807'), + 'braille_dots_1234': (0x100280f, u'\u280f'), + 'braille_dots_12345': (0x100281f, u'\u281f'), + 'braille_dots_123456': (0x100283f, u'\u283f'), + 'braille_dots_1234567': (0x100287f, u'\u287f'), + 'braille_dots_12345678': (0x10028ff, u'\u28ff'), + 'braille_dots_1234568': (0x10028bf, u'\u28bf'), + 'braille_dots_123457': (0x100285f, u'\u285f'), + 'braille_dots_1234578': (0x10028df, u'\u28df'), + 'braille_dots_123458': (0x100289f, u'\u289f'), + 'braille_dots_12346': (0x100282f, u'\u282f'), + 'braille_dots_123467': (0x100286f, u'\u286f'), + 'braille_dots_1234678': (0x10028ef, u'\u28ef'), + 'braille_dots_123468': (0x10028af, u'\u28af'), + 'braille_dots_12347': (0x100284f, u'\u284f'), + 'braille_dots_123478': (0x10028cf, u'\u28cf'), + 'braille_dots_12348': (0x100288f, u'\u288f'), + 'braille_dots_1235': (0x1002817, u'\u2817'), + 'braille_dots_12356': (0x1002837, u'\u2837'), + 'braille_dots_123567': (0x1002877, u'\u2877'), + 'braille_dots_1235678': (0x10028f7, u'\u28f7'), + 'braille_dots_123568': (0x10028b7, u'\u28b7'), + 'braille_dots_12357': (0x1002857, u'\u2857'), + 'braille_dots_123578': (0x10028d7, u'\u28d7'), + 'braille_dots_12358': (0x1002897, u'\u2897'), + 'braille_dots_1236': (0x1002827, u'\u2827'), + 'braille_dots_12367': (0x1002867, u'\u2867'), + 'braille_dots_123678': (0x10028e7, u'\u28e7'), + 'braille_dots_12368': (0x10028a7, u'\u28a7'), + 'braille_dots_1237': (0x1002847, u'\u2847'), + 'braille_dots_12378': (0x10028c7, u'\u28c7'), + 'braille_dots_1238': (0x1002887, u'\u2887'), + 'braille_dots_124': (0x100280b, u'\u280b'), + 'braille_dots_1245': (0x100281b, u'\u281b'), + 'braille_dots_12456': (0x100283b, u'\u283b'), + 'braille_dots_124567': (0x100287b, u'\u287b'), + 'braille_dots_1245678': (0x10028fb, u'\u28fb'), + 'braille_dots_124568': (0x10028bb, u'\u28bb'), + 'braille_dots_12457': (0x100285b, u'\u285b'), + 'braille_dots_124578': (0x10028db, u'\u28db'), + 'braille_dots_12458': (0x100289b, u'\u289b'), + 'braille_dots_1246': (0x100282b, u'\u282b'), + 'braille_dots_12467': (0x100286b, u'\u286b'), + 'braille_dots_124678': (0x10028eb, u'\u28eb'), + 'braille_dots_12468': (0x10028ab, u'\u28ab'), + 'braille_dots_1247': (0x100284b, u'\u284b'), + 'braille_dots_12478': (0x10028cb, u'\u28cb'), + 'braille_dots_1248': (0x100288b, u'\u288b'), + 'braille_dots_125': (0x1002813, u'\u2813'), + 'braille_dots_1256': (0x1002833, u'\u2833'), + 'braille_dots_12567': (0x1002873, u'\u2873'), + 'braille_dots_125678': (0x10028f3, u'\u28f3'), + 'braille_dots_12568': (0x10028b3, u'\u28b3'), + 'braille_dots_1257': (0x1002853, u'\u2853'), + 'braille_dots_12578': (0x10028d3, u'\u28d3'), + 'braille_dots_1258': (0x1002893, u'\u2893'), + 'braille_dots_126': (0x1002823, u'\u2823'), + 'braille_dots_1267': (0x1002863, u'\u2863'), + 'braille_dots_12678': (0x10028e3, u'\u28e3'), + 'braille_dots_1268': (0x10028a3, u'\u28a3'), + 'braille_dots_127': (0x1002843, u'\u2843'), + 'braille_dots_1278': (0x10028c3, u'\u28c3'), + 'braille_dots_128': (0x1002883, u'\u2883'), + 'braille_dots_13': (0x1002805, u'\u2805'), + 'braille_dots_134': (0x100280d, u'\u280d'), + 'braille_dots_1345': (0x100281d, u'\u281d'), + 'braille_dots_13456': (0x100283d, u'\u283d'), + 'braille_dots_134567': (0x100287d, u'\u287d'), + 'braille_dots_1345678': (0x10028fd, u'\u28fd'), + 'braille_dots_134568': (0x10028bd, u'\u28bd'), + 'braille_dots_13457': (0x100285d, u'\u285d'), + 'braille_dots_134578': (0x10028dd, u'\u28dd'), + 'braille_dots_13458': (0x100289d, u'\u289d'), + 'braille_dots_1346': (0x100282d, u'\u282d'), + 'braille_dots_13467': (0x100286d, u'\u286d'), + 'braille_dots_134678': (0x10028ed, u'\u28ed'), + 'braille_dots_13468': (0x10028ad, u'\u28ad'), + 'braille_dots_1347': (0x100284d, u'\u284d'), + 'braille_dots_13478': (0x10028cd, u'\u28cd'), + 'braille_dots_1348': (0x100288d, u'\u288d'), + 'braille_dots_135': (0x1002815, u'\u2815'), + 'braille_dots_1356': (0x1002835, u'\u2835'), + 'braille_dots_13567': (0x1002875, u'\u2875'), + 'braille_dots_135678': (0x10028f5, u'\u28f5'), + 'braille_dots_13568': (0x10028b5, u'\u28b5'), + 'braille_dots_1357': (0x1002855, u'\u2855'), + 'braille_dots_13578': (0x10028d5, u'\u28d5'), + 'braille_dots_1358': (0x1002895, u'\u2895'), + 'braille_dots_136': (0x1002825, u'\u2825'), + 'braille_dots_1367': (0x1002865, u'\u2865'), + 'braille_dots_13678': (0x10028e5, u'\u28e5'), + 'braille_dots_1368': (0x10028a5, u'\u28a5'), + 'braille_dots_137': (0x1002845, u'\u2845'), + 'braille_dots_1378': (0x10028c5, u'\u28c5'), + 'braille_dots_138': (0x1002885, u'\u2885'), + 'braille_dots_14': (0x1002809, u'\u2809'), + 'braille_dots_145': (0x1002819, u'\u2819'), + 'braille_dots_1456': (0x1002839, u'\u2839'), + 'braille_dots_14567': (0x1002879, u'\u2879'), + 'braille_dots_145678': (0x10028f9, u'\u28f9'), + 'braille_dots_14568': (0x10028b9, u'\u28b9'), + 'braille_dots_1457': (0x1002859, u'\u2859'), + 'braille_dots_14578': (0x10028d9, u'\u28d9'), + 'braille_dots_1458': (0x1002899, u'\u2899'), + 'braille_dots_146': (0x1002829, u'\u2829'), + 'braille_dots_1467': (0x1002869, u'\u2869'), + 'braille_dots_14678': (0x10028e9, u'\u28e9'), + 'braille_dots_1468': (0x10028a9, u'\u28a9'), + 'braille_dots_147': (0x1002849, u'\u2849'), + 'braille_dots_1478': (0x10028c9, u'\u28c9'), + 'braille_dots_148': (0x1002889, u'\u2889'), + 'braille_dots_15': (0x1002811, u'\u2811'), + 'braille_dots_156': (0x1002831, u'\u2831'), + 'braille_dots_1567': (0x1002871, u'\u2871'), + 'braille_dots_15678': (0x10028f1, u'\u28f1'), + 'braille_dots_1568': (0x10028b1, u'\u28b1'), + 'braille_dots_157': (0x1002851, u'\u2851'), + 'braille_dots_1578': (0x10028d1, u'\u28d1'), + 'braille_dots_158': (0x1002891, u'\u2891'), + 'braille_dots_16': (0x1002821, u'\u2821'), + 'braille_dots_167': (0x1002861, u'\u2861'), + 'braille_dots_1678': (0x10028e1, u'\u28e1'), + 'braille_dots_168': (0x10028a1, u'\u28a1'), + 'braille_dots_17': (0x1002841, u'\u2841'), + 'braille_dots_178': (0x10028c1, u'\u28c1'), + 'braille_dots_18': (0x1002881, u'\u2881'), + 'braille_dots_2': (0x1002802, u'\u2802'), + 'braille_dots_23': (0x1002806, u'\u2806'), + 'braille_dots_234': (0x100280e, u'\u280e'), + 'braille_dots_2345': (0x100281e, u'\u281e'), + 'braille_dots_23456': (0x100283e, u'\u283e'), + 'braille_dots_234567': (0x100287e, u'\u287e'), + 'braille_dots_2345678': (0x10028fe, u'\u28fe'), + 'braille_dots_234568': (0x10028be, u'\u28be'), + 'braille_dots_23457': (0x100285e, u'\u285e'), + 'braille_dots_234578': (0x10028de, u'\u28de'), + 'braille_dots_23458': (0x100289e, u'\u289e'), + 'braille_dots_2346': (0x100282e, u'\u282e'), + 'braille_dots_23467': (0x100286e, u'\u286e'), + 'braille_dots_234678': (0x10028ee, u'\u28ee'), + 'braille_dots_23468': (0x10028ae, u'\u28ae'), + 'braille_dots_2347': (0x100284e, u'\u284e'), + 'braille_dots_23478': (0x10028ce, u'\u28ce'), + 'braille_dots_2348': (0x100288e, u'\u288e'), + 'braille_dots_235': (0x1002816, u'\u2816'), + 'braille_dots_2356': (0x1002836, u'\u2836'), + 'braille_dots_23567': (0x1002876, u'\u2876'), + 'braille_dots_235678': (0x10028f6, u'\u28f6'), + 'braille_dots_23568': (0x10028b6, u'\u28b6'), + 'braille_dots_2357': (0x1002856, u'\u2856'), + 'braille_dots_23578': (0x10028d6, u'\u28d6'), + 'braille_dots_2358': (0x1002896, u'\u2896'), + 'braille_dots_236': (0x1002826, u'\u2826'), + 'braille_dots_2367': (0x1002866, u'\u2866'), + 'braille_dots_23678': (0x10028e6, u'\u28e6'), + 'braille_dots_2368': (0x10028a6, u'\u28a6'), + 'braille_dots_237': (0x1002846, u'\u2846'), + 'braille_dots_2378': (0x10028c6, u'\u28c6'), + 'braille_dots_238': (0x1002886, u'\u2886'), + 'braille_dots_24': (0x100280a, u'\u280a'), + 'braille_dots_245': (0x100281a, u'\u281a'), + 'braille_dots_2456': (0x100283a, u'\u283a'), + 'braille_dots_24567': (0x100287a, u'\u287a'), + 'braille_dots_245678': (0x10028fa, u'\u28fa'), + 'braille_dots_24568': (0x10028ba, u'\u28ba'), + 'braille_dots_2457': (0x100285a, u'\u285a'), + 'braille_dots_24578': (0x10028da, u'\u28da'), + 'braille_dots_2458': (0x100289a, u'\u289a'), + 'braille_dots_246': (0x100282a, u'\u282a'), + 'braille_dots_2467': (0x100286a, u'\u286a'), + 'braille_dots_24678': (0x10028ea, u'\u28ea'), + 'braille_dots_2468': (0x10028aa, u'\u28aa'), + 'braille_dots_247': (0x100284a, u'\u284a'), + 'braille_dots_2478': (0x10028ca, u'\u28ca'), + 'braille_dots_248': (0x100288a, u'\u288a'), + 'braille_dots_25': (0x1002812, u'\u2812'), + 'braille_dots_256': (0x1002832, u'\u2832'), + 'braille_dots_2567': (0x1002872, u'\u2872'), + 'braille_dots_25678': (0x10028f2, u'\u28f2'), + 'braille_dots_2568': (0x10028b2, u'\u28b2'), + 'braille_dots_257': (0x1002852, u'\u2852'), + 'braille_dots_2578': (0x10028d2, u'\u28d2'), + 'braille_dots_258': (0x1002892, u'\u2892'), + 'braille_dots_26': (0x1002822, u'\u2822'), + 'braille_dots_267': (0x1002862, u'\u2862'), + 'braille_dots_2678': (0x10028e2, u'\u28e2'), + 'braille_dots_268': (0x10028a2, u'\u28a2'), + 'braille_dots_27': (0x1002842, u'\u2842'), + 'braille_dots_278': (0x10028c2, u'\u28c2'), + 'braille_dots_28': (0x1002882, u'\u2882'), + 'braille_dots_3': (0x1002804, u'\u2804'), + 'braille_dots_34': (0x100280c, u'\u280c'), + 'braille_dots_345': (0x100281c, u'\u281c'), + 'braille_dots_3456': (0x100283c, u'\u283c'), + 'braille_dots_34567': (0x100287c, u'\u287c'), + 'braille_dots_345678': (0x10028fc, u'\u28fc'), + 'braille_dots_34568': (0x10028bc, u'\u28bc'), + 'braille_dots_3457': (0x100285c, u'\u285c'), + 'braille_dots_34578': (0x10028dc, u'\u28dc'), + 'braille_dots_3458': (0x100289c, u'\u289c'), + 'braille_dots_346': (0x100282c, u'\u282c'), + 'braille_dots_3467': (0x100286c, u'\u286c'), + 'braille_dots_34678': (0x10028ec, u'\u28ec'), + 'braille_dots_3468': (0x10028ac, u'\u28ac'), + 'braille_dots_347': (0x100284c, u'\u284c'), + 'braille_dots_3478': (0x10028cc, u'\u28cc'), + 'braille_dots_348': (0x100288c, u'\u288c'), + 'braille_dots_35': (0x1002814, u'\u2814'), + 'braille_dots_356': (0x1002834, u'\u2834'), + 'braille_dots_3567': (0x1002874, u'\u2874'), + 'braille_dots_35678': (0x10028f4, u'\u28f4'), + 'braille_dots_3568': (0x10028b4, u'\u28b4'), + 'braille_dots_357': (0x1002854, u'\u2854'), + 'braille_dots_3578': (0x10028d4, u'\u28d4'), + 'braille_dots_358': (0x1002894, u'\u2894'), + 'braille_dots_36': (0x1002824, u'\u2824'), + 'braille_dots_367': (0x1002864, u'\u2864'), + 'braille_dots_3678': (0x10028e4, u'\u28e4'), + 'braille_dots_368': (0x10028a4, u'\u28a4'), + 'braille_dots_37': (0x1002844, u'\u2844'), + 'braille_dots_378': (0x10028c4, u'\u28c4'), + 'braille_dots_38': (0x1002884, u'\u2884'), + 'braille_dots_4': (0x1002808, u'\u2808'), + 'braille_dots_45': (0x1002818, u'\u2818'), + 'braille_dots_456': (0x1002838, u'\u2838'), + 'braille_dots_4567': (0x1002878, u'\u2878'), + 'braille_dots_45678': (0x10028f8, u'\u28f8'), + 'braille_dots_4568': (0x10028b8, u'\u28b8'), + 'braille_dots_457': (0x1002858, u'\u2858'), + 'braille_dots_4578': (0x10028d8, u'\u28d8'), + 'braille_dots_458': (0x1002898, u'\u2898'), + 'braille_dots_46': (0x1002828, u'\u2828'), + 'braille_dots_467': (0x1002868, u'\u2868'), + 'braille_dots_4678': (0x10028e8, u'\u28e8'), + 'braille_dots_468': (0x10028a8, u'\u28a8'), + 'braille_dots_47': (0x1002848, u'\u2848'), + 'braille_dots_478': (0x10028c8, u'\u28c8'), + 'braille_dots_48': (0x1002888, u'\u2888'), + 'braille_dots_5': (0x1002810, u'\u2810'), + 'braille_dots_56': (0x1002830, u'\u2830'), + 'braille_dots_567': (0x1002870, u'\u2870'), + 'braille_dots_5678': (0x10028f0, u'\u28f0'), + 'braille_dots_568': (0x10028b0, u'\u28b0'), + 'braille_dots_57': (0x1002850, u'\u2850'), + 'braille_dots_578': (0x10028d0, u'\u28d0'), + 'braille_dots_58': (0x1002890, u'\u2890'), + 'braille_dots_6': (0x1002820, u'\u2820'), + 'braille_dots_67': (0x1002860, u'\u2860'), + 'braille_dots_678': (0x10028e0, u'\u28e0'), + 'braille_dots_68': (0x10028a0, u'\u28a0'), + 'braille_dots_7': (0x1002840, u'\u2840'), + 'braille_dots_78': (0x10028c0, u'\u28c0'), + 'braille_dots_8': (0x1002880, u'\u2880'), + 'breve': (0x01a2, u'\u02D8'), + 'brokenbar': (0x00a6, u'\u00A6'), + 'c': (0x0063, u'\u0063'), + 'cabovedot': (0x02e5, u'\u010B'), + 'cacute': (0x01e6, u'\u0107'), + 'careof': (0x0ab8, u'\u2105'), + 'caret': (0x0afc, u'\u2038'), + 'caron': (0x01b7, u'\u02C7'), + 'ccaron': (0x01e8, u'\u010D'), + 'ccedilla': (0x00e7, u'\u00E7'), + 'ccircumflex': (0x02e6, u'\u0109'), + 'cedilla': (0x00b8, u'\u00B8'), + 'cent': (0x00a2, u'\u00A2'), + 'checkerboard': (0x09e1, u'\u2592'), + 'checkmark': (0x0af3, u'\u2713'), + 'circle': (0x0bcf, u'\u25CB'), + 'club': (0x0aec, u'\u2663'), + 'colon': (0x003a, u'\u003A'), + 'comma': (0x002c, u'\u002C'), + 'containsas': (0x100220B, u'\u220B'), + 'copyright': (0x00a9, u'\u00A9'), + 'cr': (0x09e4, u'\u240D'), + 'crossinglines': (0x09ee, u'\u253C'), + 'cuberoot': (0x100221B, u'\u221B'), + 'currency': (0x00a4, u'\u00A4'), + 'd': (0x0064, u'\u0064'), + 'dabovedot': (0x1001e0b, u'\u1E0B'), + 'dagger': (0x0af1, u'\u2020'), + 'dcaron': (0x01ef, u'\u010F'), + 'dead_A': (0xfe81, None), + 'dead_E': (0xfe83, None), + 'dead_I': (0xfe85, None), + 'dead_O': (0xfe87, None), + 'dead_U': (0xfe89, None), + 'dead_a': (0xfe80, None), + 'dead_abovecomma': (0xfe64, u'\u0315'), + 'dead_abovedot': (0xfe56, u'\u0307'), + 'dead_abovereversedcomma': (0xfe65, u'\u0312'), + 'dead_abovering': (0xfe58, u'\u030A'), + 'dead_aboveverticalline': (0xfe91, u'\u030D'), + 'dead_acute': (0xfe51, u'\u0301'), + 'dead_belowbreve': (0xfe6b, u'\u032E'), + 'dead_belowcircumflex': (0xfe69, u'\u032D'), + 'dead_belowcomma': (0xfe6e, u'\u0326'), + 'dead_belowdiaeresis': (0xfe6c, u'\u0324'), + 'dead_belowdot': (0xfe60, u'\u0323'), + 'dead_belowmacron': (0xfe68, u'\u0331'), + 'dead_belowring': (0xfe67, u'\u0325'), + 'dead_belowtilde': (0xfe6a, u'\u0330'), + 'dead_belowverticalline': (0xfe92, u'\u0329'), + 'dead_breve': (0xfe55, u'\u0306'), + 'dead_capital_schwa': (0xfe8b, None), + 'dead_caron': (0xfe5a, u'\u030C'), + 'dead_cedilla': (0xfe5b, u'\u0327'), + 'dead_circumflex': (0xfe52, u'\u0302'), + 'dead_currency': (0xfe6f, None), + 'dead_diaeresis': (0xfe57, u'\u0308'), + 'dead_doubleacute': (0xfe59, u'\u030B'), + 'dead_doublegrave': (0xfe66, u'\u030F'), + 'dead_e': (0xfe82, None), + 'dead_grave': (0xfe50, u'\u0300'), + 'dead_greek': (0xfe8c, None), + 'dead_hook': (0xfe61, u'\u0309'), + 'dead_horn': (0xfe62, u'\u031B'), + 'dead_i': (0xfe84, None), + 'dead_invertedbreve': (0xfe6d, u'\u032F'), + 'dead_iota': (0xfe5d, u'\u0345'), + 'dead_longsolidusoverlay': (0xfe93, u'\u0338'), + 'dead_lowline': (0xfe90, u'\u0332'), + 'dead_macron': (0xfe54, u'\u0304'), + 'dead_o': (0xfe86, None), + 'dead_ogonek': (0xfe5c, u'\u0328'), + 'dead_semivoiced_sound': (0xfe5f, None), + 'dead_small_schwa': (0xfe8a, None), + 'dead_stroke': (0xfe63, u'\u0335'), + 'dead_tilde': (0xfe53, u'\u0303'), + 'dead_u': (0xfe88, None), + 'dead_voiced_sound': (0xfe5e, None), + 'degree': (0x00b0, u'\u00B0'), + 'diaeresis': (0x00a8, u'\u00A8'), + 'diamond': (0x0aed, u'\u2666'), + 'digitspace': (0x0aa5, u'\u2007'), + 'dintegral': (0x100222C, u'\u222C'), + 'division': (0x00f7, u'\u00F7'), + 'dollar': (0x0024, u'\u0024'), + 'doubbaselinedot': (0x0aaf, u'\u2025'), + 'doubleacute': (0x01bd, u'\u02DD'), + 'doubledagger': (0x0af2, u'\u2021'), + 'doublelowquotemark': (0x0afe, u'\u201E'), + 'downarrow': (0x08fe, u'\u2193'), + 'downstile': (0x0bc4, u'\u230A'), + 'downtack': (0x0bc2, u'\u22A4'), + 'dstroke': (0x01f0, u'\u0111'), + 'e': (0x0065, u'\u0065'), + 'eabovedot': (0x03ec, u'\u0117'), + 'eacute': (0x00e9, u'\u00E9'), + 'ebelowdot': (0x1001eb9, u'\u1EB9'), + 'ecaron': (0x01ec, u'\u011B'), + 'ecircumflex': (0x00ea, u'\u00EA'), + 'ecircumflexacute': (0x1001ebf, u'\u1EBF'), + 'ecircumflexbelowdot': (0x1001ec7, u'\u1EC7'), + 'ecircumflexgrave': (0x1001ec1, u'\u1EC1'), + 'ecircumflexhook': (0x1001ec3, u'\u1EC3'), + 'ecircumflextilde': (0x1001ec5, u'\u1EC5'), + 'ediaeresis': (0x00eb, u'\u00EB'), + 'egrave': (0x00e8, u'\u00E8'), + 'ehook': (0x1001ebb, u'\u1EBB'), + 'eightsubscript': (0x1002088, u'\u2088'), + 'eightsuperior': (0x1002078, u'\u2078'), + 'elementof': (0x1002208, u'\u2208'), + 'ellipsis': (0x0aae, u'\u2026'), + 'em3space': (0x0aa3, u'\u2004'), + 'em4space': (0x0aa4, u'\u2005'), + 'emacron': (0x03ba, u'\u0113'), + 'emdash': (0x0aa9, u'\u2014'), + 'emptyset': (0x1002205, u'\u2205'), + 'emspace': (0x0aa1, u'\u2003'), + 'endash': (0x0aaa, u'\u2013'), + 'eng': (0x03bf, u'\u014B'), + 'enspace': (0x0aa2, u'\u2002'), + 'eogonek': (0x01ea, u'\u0119'), + 'equal': (0x003d, u'\u003D'), + 'eth': (0x00f0, u'\u00F0'), + 'etilde': (0x1001ebd, u'\u1EBD'), + 'exclam': (0x0021, u'\u0021'), + 'exclamdown': (0x00a1, u'\u00A1'), + 'ezh': (0x1000292, u'\u0292'), + 'f': (0x0066, u'\u0066'), + 'fabovedot': (0x1001e1f, u'\u1E1F'), + 'femalesymbol': (0x0af8, u'\u2640'), + 'ff': (0x09e3, u'\u240C'), + 'figdash': (0x0abb, u'\u2012'), + 'fiveeighths': (0x0ac5, u'\u215D'), + 'fivesixths': (0x0ab7, u'\u215A'), + 'fivesubscript': (0x1002085, u'\u2085'), + 'fivesuperior': (0x1002075, u'\u2075'), + 'fourfifths': (0x0ab5, u'\u2158'), + 'foursubscript': (0x1002084, u'\u2084'), + 'foursuperior': (0x1002074, u'\u2074'), + 'fourthroot': (0x100221C, u'\u221C'), + 'function': (0x08f6, u'\u0192'), + 'g': (0x0067, u'\u0067'), + 'gabovedot': (0x02f5, u'\u0121'), + 'gbreve': (0x02bb, u'\u011F'), + 'gcaron': (0x10001e7, u'\u01E7'), + 'gcedilla': (0x03bb, u'\u0123'), + 'gcircumflex': (0x02f8, u'\u011D'), + 'grave': (0x0060, u'\u0060'), + 'greater': (0x003e, u'\u003E'), + 'greaterthanequal': (0x08be, u'\u2265'), + 'guillemotleft': (0x00ab, u'\u00AB'), + 'guillemotright': (0x00bb, u'\u00BB'), + 'h': (0x0068, u'\u0068'), + 'hairspace': (0x0aa8, u'\u200A'), + 'hcircumflex': (0x02b6, u'\u0125'), + 'heart': (0x0aee, u'\u2665'), + 'hebrew_aleph': (0x0ce0, u'\u05D0'), + 'hebrew_ayin': (0x0cf2, u'\u05E2'), + 'hebrew_bet': (0x0ce1, u'\u05D1'), + 'hebrew_chet': (0x0ce7, u'\u05D7'), + 'hebrew_dalet': (0x0ce3, u'\u05D3'), + 'hebrew_doublelowline': (0x0cdf, u'\u2017'), + 'hebrew_finalkaph': (0x0cea, u'\u05DA'), + 'hebrew_finalmem': (0x0ced, u'\u05DD'), + 'hebrew_finalnun': (0x0cef, u'\u05DF'), + 'hebrew_finalpe': (0x0cf3, u'\u05E3'), + 'hebrew_finalzade': (0x0cf5, u'\u05E5'), + 'hebrew_gimel': (0x0ce2, u'\u05D2'), + 'hebrew_he': (0x0ce4, u'\u05D4'), + 'hebrew_kaph': (0x0ceb, u'\u05DB'), + 'hebrew_lamed': (0x0cec, u'\u05DC'), + 'hebrew_mem': (0x0cee, u'\u05DE'), + 'hebrew_nun': (0x0cf0, u'\u05E0'), + 'hebrew_pe': (0x0cf4, u'\u05E4'), + 'hebrew_qoph': (0x0cf7, u'\u05E7'), + 'hebrew_resh': (0x0cf8, u'\u05E8'), + 'hebrew_samech': (0x0cf1, u'\u05E1'), + 'hebrew_shin': (0x0cf9, u'\u05E9'), + 'hebrew_taw': (0x0cfa, u'\u05EA'), + 'hebrew_tet': (0x0ce8, u'\u05D8'), + 'hebrew_waw': (0x0ce5, u'\u05D5'), + 'hebrew_yod': (0x0ce9, u'\u05D9'), + 'hebrew_zade': (0x0cf6, u'\u05E6'), + 'hebrew_zain': (0x0ce6, u'\u05D6'), + 'horizlinescan1': (0x09ef, u'\u23BA'), + 'horizlinescan3': (0x09f0, u'\u23BB'), + 'horizlinescan5': (0x09f1, u'\u2500'), + 'horizlinescan7': (0x09f2, u'\u23BC'), + 'horizlinescan9': (0x09f3, u'\u23BD'), + 'hstroke': (0x02b1, u'\u0127'), + 'ht': (0x09e2, u'\u2409'), + 'hyphen': (0x00ad, u'\u00AD'), + 'i': (0x0069, u'\u0069'), + 'iacute': (0x00ed, u'\u00ED'), + 'ibelowdot': (0x1001ecb, u'\u1ECB'), + 'ibreve': (0x100012d, u'\u012D'), + 'icircumflex': (0x00ee, u'\u00EE'), + 'identical': (0x08cf, u'\u2261'), + 'idiaeresis': (0x00ef, u'\u00EF'), + 'idotless': (0x02b9, u'\u0131'), + 'ifonlyif': (0x08cd, u'\u21D4'), + 'igrave': (0x00ec, u'\u00EC'), + 'ihook': (0x1001ec9, u'\u1EC9'), + 'imacron': (0x03ef, u'\u012B'), + 'implies': (0x08ce, u'\u21D2'), + 'includedin': (0x08da, u'\u2282'), + 'includes': (0x08db, u'\u2283'), + 'infinity': (0x08c2, u'\u221E'), + 'integral': (0x08bf, u'\u222B'), + 'intersection': (0x08dc, u'\u2229'), + 'iogonek': (0x03e7, u'\u012F'), + 'itilde': (0x03b5, u'\u0129'), + 'j': (0x006a, u'\u006A'), + 'jcircumflex': (0x02bc, u'\u0135'), + 'jot': (0x0bca, u'\u2218'), + 'k': (0x006b, u'\u006B'), + 'kana_A': (0x04b1, u'\u30A2'), + 'kana_CHI': (0x04c1, u'\u30C1'), + 'kana_E': (0x04b4, u'\u30A8'), + 'kana_FU': (0x04cc, u'\u30D5'), + 'kana_HA': (0x04ca, u'\u30CF'), + 'kana_HE': (0x04cd, u'\u30D8'), + 'kana_HI': (0x04cb, u'\u30D2'), + 'kana_HO': (0x04ce, u'\u30DB'), + 'kana_I': (0x04b2, u'\u30A4'), + 'kana_KA': (0x04b6, u'\u30AB'), + 'kana_KE': (0x04b9, u'\u30B1'), + 'kana_KI': (0x04b7, u'\u30AD'), + 'kana_KO': (0x04ba, u'\u30B3'), + 'kana_KU': (0x04b8, u'\u30AF'), + 'kana_MA': (0x04cf, u'\u30DE'), + 'kana_ME': (0x04d2, u'\u30E1'), + 'kana_MI': (0x04d0, u'\u30DF'), + 'kana_MO': (0x04d3, u'\u30E2'), + 'kana_MU': (0x04d1, u'\u30E0'), + 'kana_N': (0x04dd, u'\u30F3'), + 'kana_NA': (0x04c5, u'\u30CA'), + 'kana_NE': (0x04c8, u'\u30CD'), + 'kana_NI': (0x04c6, u'\u30CB'), + 'kana_NO': (0x04c9, u'\u30CE'), + 'kana_NU': (0x04c7, u'\u30CC'), + 'kana_O': (0x04b5, u'\u30AA'), + 'kana_RA': (0x04d7, u'\u30E9'), + 'kana_RE': (0x04da, u'\u30EC'), + 'kana_RI': (0x04d8, u'\u30EA'), + 'kana_RO': (0x04db, u'\u30ED'), + 'kana_RU': (0x04d9, u'\u30EB'), + 'kana_SA': (0x04bb, u'\u30B5'), + 'kana_SE': (0x04be, u'\u30BB'), + 'kana_SHI': (0x04bc, u'\u30B7'), + 'kana_SO': (0x04bf, u'\u30BD'), + 'kana_SU': (0x04bd, u'\u30B9'), + 'kana_TA': (0x04c0, u'\u30BF'), + 'kana_TE': (0x04c3, u'\u30C6'), + 'kana_TO': (0x04c4, u'\u30C8'), + 'kana_TSU': (0x04c2, u'\u30C4'), + 'kana_U': (0x04b3, u'\u30A6'), + 'kana_WA': (0x04dc, u'\u30EF'), + 'kana_WO': (0x04a6, u'\u30F2'), + 'kana_YA': (0x04d4, u'\u30E4'), + 'kana_YO': (0x04d6, u'\u30E8'), + 'kana_YU': (0x04d5, u'\u30E6'), + 'kana_a': (0x04a7, u'\u30A1'), + 'kana_closingbracket': (0x04a3, u'\u300D'), + 'kana_comma': (0x04a4, u'\u3001'), + 'kana_conjunctive': (0x04a5, u'\u30FB'), + 'kana_e': (0x04aa, u'\u30A7'), + 'kana_fullstop': (0x04a1, u'\u3002'), + 'kana_i': (0x04a8, u'\u30A3'), + 'kana_o': (0x04ab, u'\u30A9'), + 'kana_openingbracket': (0x04a2, u'\u300C'), + 'kana_tsu': (0x04af, u'\u30C3'), + 'kana_u': (0x04a9, u'\u30A5'), + 'kana_ya': (0x04ac, u'\u30E3'), + 'kana_yo': (0x04ae, u'\u30E7'), + 'kana_yu': (0x04ad, u'\u30E5'), + 'kcedilla': (0x03f3, u'\u0137'), + 'kra': (0x03a2, u'\u0138'), + 'l': (0x006c, u'\u006C'), + 'lacute': (0x01e5, u'\u013A'), + 'latincross': (0x0ad9, u'\u271D'), + 'lbelowdot': (0x1001e37, u'\u1E37'), + 'lcaron': (0x01b5, u'\u013E'), + 'lcedilla': (0x03b6, u'\u013C'), + 'leftarrow': (0x08fb, u'\u2190'), + 'leftdoublequotemark': (0x0ad2, u'\u201C'), + 'leftmiddlecurlybrace': (0x08af, u'\u23A8'), + 'leftradical': (0x08a1, u'\u23B7'), + 'leftsinglequotemark': (0x0ad0, u'\u2018'), + 'leftt': (0x09f4, u'\u251C'), + 'lefttack': (0x0bdc, u'\u22A3'), + 'less': (0x003c, u'\u003C'), + 'lessthanequal': (0x08bc, u'\u2264'), + 'lf': (0x09e5, u'\u240A'), + 'logicaland': (0x08de, u'\u2227'), + 'logicalor': (0x08df, u'\u2228'), + 'lowleftcorner': (0x09ed, u'\u2514'), + 'lowrightcorner': (0x09ea, u'\u2518'), + 'lstroke': (0x01b3, u'\u0142'), + 'm': (0x006d, u'\u006D'), + 'mabovedot': (0x1001e41, u'\u1E41'), + 'macron': (0x00af, u'\u00AF'), + 'malesymbol': (0x0af7, u'\u2642'), + 'maltesecross': (0x0af0, u'\u2720'), + 'masculine': (0x00ba, u'\u00BA'), + 'minus': (0x002d, u'\u002D'), + 'minutes': (0x0ad6, u'\u2032'), + 'mu': (0x00b5, u'\u00B5'), + 'multiply': (0x00d7, u'\u00D7'), + 'musicalflat': (0x0af6, u'\u266D'), + 'musicalsharp': (0x0af5, u'\u266F'), + 'n': (0x006e, u'\u006E'), + 'nabla': (0x08c5, u'\u2207'), + 'nacute': (0x01f1, u'\u0144'), + 'ncaron': (0x01f2, u'\u0148'), + 'ncedilla': (0x03f1, u'\u0146'), + 'ninesubscript': (0x1002089, u'\u2089'), + 'ninesuperior': (0x1002079, u'\u2079'), + 'nl': (0x09e8, u'\u2424'), + 'nobreakspace': (0x00a0, u'\u00A0'), + 'notapproxeq': (0x1002247, u'\u2247'), + 'notelementof': (0x1002209, u'\u2209'), + 'notequal': (0x08bd, u'\u2260'), + 'notidentical': (0x1002262, u'\u2262'), + 'notsign': (0x00ac, u'\u00AC'), + 'ntilde': (0x00f1, u'\u00F1'), + 'numbersign': (0x0023, u'\u0023'), + 'numerosign': (0x06b0, u'\u2116'), + 'o': (0x006f, u'\u006F'), + 'oacute': (0x00f3, u'\u00F3'), + 'obarred': (0x1000275, u'\u0275'), + 'obelowdot': (0x1001ecd, u'\u1ECD'), + 'ocaron': (0x10001d2, u'\u01D2'), + 'ocircumflex': (0x00f4, u'\u00F4'), + 'ocircumflexacute': (0x1001ed1, u'\u1ED1'), + 'ocircumflexbelowdot': (0x1001ed9, u'\u1ED9'), + 'ocircumflexgrave': (0x1001ed3, u'\u1ED3'), + 'ocircumflexhook': (0x1001ed5, u'\u1ED5'), + 'ocircumflextilde': (0x1001ed7, u'\u1ED7'), + 'odiaeresis': (0x00f6, u'\u00F6'), + 'odoubleacute': (0x01f5, u'\u0151'), + 'oe': (0x13bd, u'\u0153'), + 'ogonek': (0x01b2, u'\u02DB'), + 'ograve': (0x00f2, u'\u00F2'), + 'ohook': (0x1001ecf, u'\u1ECF'), + 'ohorn': (0x10001a1, u'\u01A1'), + 'ohornacute': (0x1001edb, u'\u1EDB'), + 'ohornbelowdot': (0x1001ee3, u'\u1EE3'), + 'ohorngrave': (0x1001edd, u'\u1EDD'), + 'ohornhook': (0x1001edf, u'\u1EDF'), + 'ohorntilde': (0x1001ee1, u'\u1EE1'), + 'omacron': (0x03f2, u'\u014D'), + 'oneeighth': (0x0ac3, u'\u215B'), + 'onefifth': (0x0ab2, u'\u2155'), + 'onehalf': (0x00bd, u'\u00BD'), + 'onequarter': (0x00bc, u'\u00BC'), + 'onesixth': (0x0ab6, u'\u2159'), + 'onesubscript': (0x1002081, u'\u2081'), + 'onesuperior': (0x00b9, u'\u00B9'), + 'onethird': (0x0ab0, u'\u2153'), + 'ooblique': (0x00f8, u'\u00F8'), + 'ordfeminine': (0x00aa, u'\u00AA'), + 'oslash': (0x00f8, u'\u00F8'), + 'otilde': (0x00f5, u'\u00F5'), + 'overline': (0x047e, u'\u203E'), + 'p': (0x0070, u'\u0070'), + 'pabovedot': (0x1001e57, u'\u1E57'), + 'paragraph': (0x00b6, u'\u00B6'), + 'parenleft': (0x0028, u'\u0028'), + 'parenright': (0x0029, u'\u0029'), + 'partdifferential': (0x1002202, u'\u2202'), + 'partialderivative': (0x08ef, u'\u2202'), + 'percent': (0x0025, u'\u0025'), + 'period': (0x002e, u'\u002E'), + 'periodcentered': (0x00b7, u'\u00B7'), + 'permille': (0x0ad5, u'\u2030'), + 'phonographcopyright': (0x0afb, u'\u2117'), + 'plus': (0x002b, u'\u002B'), + 'plusminus': (0x00b1, u'\u00B1'), + 'prescription': (0x0ad4, u'\u211E'), + 'prolongedsound': (0x04b0, u'\u30FC'), + 'punctspace': (0x0aa6, u'\u2008'), + 'q': (0x0071, u'\u0071'), + 'quad': (0x0bcc, u'\u2395'), + 'question': (0x003f, u'\u003F'), + 'questiondown': (0x00bf, u'\u00BF'), + 'quotedbl': (0x0022, u'\u0022'), + 'r': (0x0072, u'\u0072'), + 'racute': (0x01e0, u'\u0155'), + 'radical': (0x08d6, u'\u221A'), + 'rcaron': (0x01f8, u'\u0159'), + 'rcedilla': (0x03b3, u'\u0157'), + 'registered': (0x00ae, u'\u00AE'), + 'rightarrow': (0x08fd, u'\u2192'), + 'rightdoublequotemark': (0x0ad3, u'\u201D'), + 'rightmiddlecurlybrace': (0x08b0, u'\u23AC'), + 'rightsinglequotemark': (0x0ad1, u'\u2019'), + 'rightt': (0x09f5, u'\u2524'), + 'righttack': (0x0bfc, u'\u22A2'), + 's': (0x0073, u'\u0073'), + 'sabovedot': (0x1001e61, u'\u1E61'), + 'sacute': (0x01b6, u'\u015B'), + 'scaron': (0x01b9, u'\u0161'), + 'scedilla': (0x01ba, u'\u015F'), + 'schwa': (0x1000259, u'\u0259'), + 'scircumflex': (0x02fe, u'\u015D'), + 'seconds': (0x0ad7, u'\u2033'), + 'section': (0x00a7, u'\u00A7'), + 'semicolon': (0x003b, u'\u003B'), + 'semivoicedsound': (0x04df, u'\u309C'), + 'seveneighths': (0x0ac6, u'\u215E'), + 'sevensubscript': (0x1002087, u'\u2087'), + 'sevensuperior': (0x1002077, u'\u2077'), + 'similarequal': (0x08c9, u'\u2243'), + 'singlelowquotemark': (0x0afd, u'\u201A'), + 'sixsubscript': (0x1002086, u'\u2086'), + 'sixsuperior': (0x1002076, u'\u2076'), + 'slash': (0x002f, u'\u002F'), + 'soliddiamond': (0x09e0, u'\u25C6'), + 'space': (0x0020, u'\u0020'), + 'squareroot': (0x100221A, u'\u221A'), + 'ssharp': (0x00df, u'\u00DF'), + 'sterling': (0x00a3, u'\u00A3'), + 'stricteq': (0x1002263, u'\u2263'), + 't': (0x0074, u'\u0074'), + 'tabovedot': (0x1001e6b, u'\u1E6B'), + 'tcaron': (0x01bb, u'\u0165'), + 'tcedilla': (0x01fe, u'\u0163'), + 'telephone': (0x0af9, u'\u260E'), + 'telephonerecorder': (0x0afa, u'\u2315'), + 'therefore': (0x08c0, u'\u2234'), + 'thinspace': (0x0aa7, u'\u2009'), + 'thorn': (0x00fe, u'\u00FE'), + 'threeeighths': (0x0ac4, u'\u215C'), + 'threefifths': (0x0ab4, u'\u2157'), + 'threequarters': (0x00be, u'\u00BE'), + 'threesubscript': (0x1002083, u'\u2083'), + 'threesuperior': (0x00b3, u'\u00B3'), + 'tintegral': (0x100222D, u'\u222D'), + 'topintegral': (0x08a4, u'\u2320'), + 'topleftparens': (0x08ab, u'\u239B'), + 'topleftsqbracket': (0x08a7, u'\u23A1'), + 'toprightparens': (0x08ad, u'\u239E'), + 'toprightsqbracket': (0x08a9, u'\u23A4'), + 'topt': (0x09f7, u'\u252C'), + 'trademark': (0x0ac9, u'\u2122'), + 'tslash': (0x03bc, u'\u0167'), + 'twofifths': (0x0ab3, u'\u2156'), + 'twosubscript': (0x1002082, u'\u2082'), + 'twosuperior': (0x00b2, u'\u00B2'), + 'twothirds': (0x0ab1, u'\u2154'), + 'u': (0x0075, u'\u0075'), + 'uacute': (0x00fa, u'\u00FA'), + 'ubelowdot': (0x1001ee5, u'\u1EE5'), + 'ubreve': (0x02fd, u'\u016D'), + 'ucircumflex': (0x00fb, u'\u00FB'), + 'udiaeresis': (0x00fc, u'\u00FC'), + 'udoubleacute': (0x01fb, u'\u0171'), + 'ugrave': (0x00f9, u'\u00F9'), + 'uhook': (0x1001ee7, u'\u1EE7'), + 'uhorn': (0x10001b0, u'\u01B0'), + 'uhornacute': (0x1001ee9, u'\u1EE9'), + 'uhornbelowdot': (0x1001ef1, u'\u1EF1'), + 'uhorngrave': (0x1001eeb, u'\u1EEB'), + 'uhornhook': (0x1001eed, u'\u1EED'), + 'uhorntilde': (0x1001eef, u'\u1EEF'), + 'umacron': (0x03fe, u'\u016B'), + 'underscore': (0x005f, u'\u005F'), + 'union': (0x08dd, u'\u222A'), + 'uogonek': (0x03f9, u'\u0173'), + 'uparrow': (0x08fc, u'\u2191'), + 'upleftcorner': (0x09ec, u'\u250C'), + 'uprightcorner': (0x09eb, u'\u2510'), + 'upstile': (0x0bd3, u'\u2308'), + 'uptack': (0x0bce, u'\u22A5'), + 'uring': (0x01f9, u'\u016F'), + 'utilde': (0x03fd, u'\u0169'), + 'v': (0x0076, u'\u0076'), + 'variation': (0x08c1, u'\u221D'), + 'vertbar': (0x09f8, u'\u2502'), + 'voicedsound': (0x04de, u'\u309B'), + 'vt': (0x09e9, u'\u240B'), + 'w': (0x0077, u'\u0077'), + 'wacute': (0x1001e83, u'\u1E83'), + 'wcircumflex': (0x1000175, u'\u0175'), + 'wdiaeresis': (0x1001e85, u'\u1E85'), + 'wgrave': (0x1001e81, u'\u1E81'), + 'x': (0x0078, u'\u0078'), + 'xabovedot': (0x1001e8b, u'\u1E8B'), + 'y': (0x0079, u'\u0079'), + 'yacute': (0x00fd, u'\u00FD'), + 'ybelowdot': (0x1001ef5, u'\u1EF5'), + 'ycircumflex': (0x1000177, u'\u0177'), + 'ydiaeresis': (0x00ff, u'\u00FF'), + 'yen': (0x00a5, u'\u00A5'), + 'ygrave': (0x1001ef3, u'\u1EF3'), + 'yhook': (0x1001ef7, u'\u1EF7'), + 'ytilde': (0x1001ef9, u'\u1EF9'), + 'z': (0x007a, u'\u007A'), + 'zabovedot': (0x01bf, u'\u017C'), + 'zacute': (0x01bc, u'\u017A'), + 'zcaron': (0x01be, u'\u017E'), + 'zerosubscript': (0x1002080, u'\u2080'), + 'zerosuperior': (0x1002070, u'\u2070'), + 'zstroke': (0x10001b6, u'\u01B6')} + +DEAD_KEYS = { + u'\u0307': u'\u02D9', + u'\u030A': u'\u02DA', + u'\u0301': u'\u00B4', + u'\u0306': u'\u02D8', + u'\u030C': u'\u02C7', + u'\u0327': u'\u00B8', + u'\u0302': u'\u005E', + u'\u0308': u'\u00A8', + u'\u030B': u'\u02DD', + u'\u0300': u'\u0060', + u'\u0345': u'\u037A', + u'\u0332': u'\u005F', + u'\u0304': u'\u00AF', + u'\u0328': u'\u02DB', + u'\u0303': u'\u007E'} + +KEYPAD_KEYS = { + 'KP_0': 0xffb0, + 'KP_1': 0xffb1, + 'KP_2': 0xffb2, + 'KP_3': 0xffb3, + 'KP_4': 0xffb4, + 'KP_5': 0xffb5, + 'KP_6': 0xffb6, + 'KP_7': 0xffb7, + 'KP_8': 0xffb8, + 'KP_9': 0xffb9, + 'KP_Add': 0xffab, + 'KP_Begin': 0xff9d, + 'KP_Decimal': 0xffae, + 'KP_Delete': 0xff9f, + 'KP_Divide': 0xffaf, + 'KP_Down': 0xff99, + 'KP_End': 0xff9c, + 'KP_Enter': 0xff8d, + 'KP_Equal': 0xffbd, + 'KP_F1': 0xff91, + 'KP_F2': 0xff92, + 'KP_F3': 0xff93, + 'KP_F4': 0xff94, + 'KP_Home': 0xff95, + 'KP_Insert': 0xff9e, + 'KP_Left': 0xff96, + 'KP_Multiply': 0xffaa, + 'KP_Next': 0xff9b, + 'KP_Page_Down': 0xff9b, + 'KP_Page_Up': 0xff9a, + 'KP_Prior': 0xff9a, + 'KP_Right': 0xff98, + 'KP_Separator': 0xffac, + 'KP_Space': 0xff80, + 'KP_Subtract': 0xffad, + 'KP_Tab': 0xff89, + 'KP_Up': 0xff97} + +CHARS = { + codepoint: name + for name, (keysym, codepoint) in SYMBOLS.items() + if codepoint} + +KEYSYMS = { + keysym: name + for name, (keysym, codepoint) in SYMBOLS.items() + if codepoint} diff --git a/pynput/keyboard/__init__.py b/pynput/keyboard/__init__.py new file mode 100644 index 0000000..822602e --- /dev/null +++ b/pynput/keyboard/__init__.py @@ -0,0 +1,233 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +The module containing keyboard classes. + +See the documentation for more information. +""" + +# pylint: disable=C0103 +# KeyCode, Key, Controller and Listener are not constants + +import itertools + +from pynput._util import backend, Events + + +backend = backend(__name__) +KeyCode = backend.KeyCode +Key = backend.Key +Controller = backend.Controller +Listener = backend.Listener +del backend + + +# pylint: disable=C0326; it is easier to read column aligned keys +#: The keys used as modifiers; the first value in each tuple is the +#: base modifier to use for subsequent modifiers. +_MODIFIER_KEYS = ( + (Key.alt_gr, (Key.alt_gr.value,)), + (Key.alt, (Key.alt.value, Key.alt_l.value, Key.alt_r.value)), + (Key.cmd, (Key.cmd.value, Key.cmd_l.value, Key.cmd_r.value)), + (Key.ctrl, (Key.ctrl.value, Key.ctrl_l.value, Key.ctrl_r.value)), + (Key.shift, (Key.shift.value, Key.shift_l.value, Key.shift_r.value))) + +#: Normalised modifiers as a mapping from virtual key code to basic modifier. +_NORMAL_MODIFIERS = { + value: key + for combination in _MODIFIER_KEYS + for key, value in zip( + itertools.cycle((combination[0],)), + combination[1])} + +#: Control codes to transform into key codes when typing +_CONTROL_CODES = { + '\n': Key.enter, + '\r': Key.enter, + '\t': Key.tab} +# pylint: enable=C0326 + + +class Events(Events): + """A keyboard event listener supporting synchronous iteration over the + events. + + Possible events are: + + :class:`Events.Press` + A key was pressed. + + :class:`Events.Release` + A key was released. + """ + _Listener = Listener + + class Press(Events.Event): + """A key press event. + """ + def __init__(self, key): + #: The key. + self.key = key + + class Release(Events.Event): + """A key release event. + """ + def __init__(self, key): + #: The key. + self.key = key + + def __init__(self): + super(Events, self).__init__( + on_press=self.Press, + on_release=self.Release) + + +class HotKey(object): + """A combination of keys acting as a hotkey. + + This class acts as a container of hotkey state for a keyboard listener. + + :param set keys: The collection of keys that must be pressed for this + hotkey to activate. Please note that a common limitation of the + hardware is that at most three simultaneously pressed keys are + supported, so using more keys may not work. + + :param callable on_activate: The activation callback. + """ + def __init__(self, keys, on_activate): + self._state = set() + self._keys = set(keys) + self._on_activate = on_activate + + @staticmethod + def parse(keys): + """Parses a key combination string. + + Key combination strings are sequences of key identifiers separated by + ``'+'``. Key identifiers are either single characters representing a + keyboard key, such as ``'a'``, or special key names identified by names + enclosed by brackets, such as ``''``. + + Keyboard keys are case-insensitive. + + :raises ValueError: if a part of the keys string is invalid, or if it + contains multiple equal parts + """ + def parts(): + start = 0 + for i, c in enumerate(keys): + if c == '+' and i != start: + yield keys[start:i] + start = i + 1 + if start == len(keys): + raise ValueError(keys) + else: + yield keys[start:] + + def parse(s): + if len(s) == 1: + return KeyCode.from_char(s.lower()) + elif len(s) > 2 and (s[0], s[-1]) == ('<', '>'): + p = s[1:-1] + try: + return Key[p.lower()] + except KeyError: + try: + return KeyCode.from_vk(int(p)) + except ValueError: + raise ValueError(s) + else: + raise ValueError(s) + + # Split the string and parse the individual parts + raw_parts = list(parts()) + parsed_parts = [ + parse(s) + for s in raw_parts] + + # Ensure no duplicate parts + if len(parsed_parts) != len(set(parsed_parts)): + raise ValueError(keys) + else: + return parsed_parts + + def press(self, key): + """Updates the hotkey state for a pressed key. + + If the key is not currently pressed, but is the last key for the full + combination, the activation callback will be invoked. + + Please note that the callback will only be invoked once. + + :param key: The key being pressed. + :type key: Key or KeyCode + """ + if key in self._keys and key not in self._state: + self._state.add(key) + if self._state == self._keys: + self._on_activate() + + def release(self, key): + """Updates the hotkey state for a released key. + + :param key: The key being released. + :type key: Key or KeyCode + """ + if key in self._state: + self._state.remove(key) + + +class GlobalHotKeys(Listener): + """A keyboard listener supporting a number of global hotkeys. + + This is a convenience wrapper to simplify registering a number of global + hotkeys. + + :param dict hotkeys: A mapping from hotkey description to hotkey action. + Keys are strings passed to :meth:`HotKey.parse`. + + :raises ValueError: if any hotkey description is invalid + """ + def __init__(self, hotkeys, *args, **kwargs): + self._hotkeys = [ + HotKey(HotKey.parse(key), value) + for key, value in hotkeys.items()] + super(GlobalHotKeys, self).__init__( + on_press=self._on_press, + on_release=self._on_release, + *args, + **kwargs) + + def _on_press(self, key): + """The press callback. + + This is automatically registered upon creation. + + :param key: The key provided by the base class. + """ + for hotkey in self._hotkeys: + hotkey.press(self.canonical(key)) + + def _on_release(self, key): + """The release callback. + + This is automatically registered upon creation. + + :param key: The key provided by the base class. + """ + for hotkey in self._hotkeys: + hotkey.release(self.canonical(key)) diff --git a/pynput/keyboard/__pycache__/__init__.cpython-38.pyc b/pynput/keyboard/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5be686072a2e5ed168d83d1f5f69769206c30557 GIT binary patch literal 7011 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 N8TU=UWxQoP_kW@|MtA@K literal 0 HcmV?d00001 diff --git a/pynput/keyboard/__pycache__/_xorg.cpython-38.pyc b/pynput/keyboard/__pycache__/_xorg.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71296bb27262eeb677729634254476cc6936eb8e GIT binary patch literal 16997 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 literal 0 HcmV?d00001 diff --git a/pynput/keyboard/_base.py b/pynput/keyboard/_base.py new file mode 100644 index 0000000..151ee2e --- /dev/null +++ b/pynput/keyboard/_base.py @@ -0,0 +1,739 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +This module contains the base implementation. + +The actual interface to keyboard classes is defined here, but the +implementation is located in a platform dependent module. +""" + +# pylint: disable=R0903 +# We implement stubs + +import contextlib +import enum +import threading +import unicodedata + +import six + +from pynput._util import AbstractListener, prefix +from pynput import _logger + + +class KeyCode(object): + """ + A :class:`KeyCode` represents the description of a key code used by the + operating system. + """ + #: The names of attributes used as platform extensions. + _PLATFORM_EXTENSIONS = [] + + def __init__(self, vk=None, char=None, is_dead=False, **kwargs): + self.vk = vk + self.char = six.text_type(char) if char is not None else None + self.is_dead = is_dead + + if self.is_dead: + try: + self.combining = unicodedata.lookup( + 'COMBINING ' + unicodedata.name(self.char)) + except KeyError: + self.is_dead = False + self.combining = None + if self.is_dead and not self.combining: + raise KeyError(char) + else: + self.combining = None + + for key in self._PLATFORM_EXTENSIONS: + setattr(self, key, kwargs.pop(key, None)) + if kwargs: + raise ValueError(kwargs) + + + def __repr__(self): + if self.is_dead: + return '[%s]' % repr(self.char) + if self.char is not None: + return repr(self.char) + else: + return '<%d>' % self.vk + + def __str__(self): + return repr(self) + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + if self.char is not None and other.char is not None: + return self.char == other.char and self.is_dead == other.is_dead + else: + return self.vk == other.vk and all( + getattr(self, f) == getattr(other, f) + for f in self._PLATFORM_EXTENSIONS) + + def __hash__(self): + return hash(repr(self)) + + def join(self, key): + """Applies this dead key to another key and returns the result. + + Joining a dead key with space (``' '``) or itself yields the non-dead + version of this key, if one exists; for example, + ``KeyCode.from_dead('~').join(KeyCode.from_char(' '))`` equals + ``KeyCode.from_char('~')`` and + ``KeyCode.from_dead('~').join(KeyCode.from_dead('~'))``. + + :param KeyCode key: The key to join with this key. + + :return: a key code + + :raises ValueError: if the keys cannot be joined + """ + # A non-dead key cannot be joined + if not self.is_dead: + raise ValueError(self) + + # Joining two of the same keycodes, or joining with space, yields the + # non-dead version of the key + if key.char == ' ' or self == key: + return self.from_char(self.char) + + # Otherwise we combine the characters + if key.char is not None: + combined = unicodedata.normalize( + 'NFC', + key.char + self.combining) + if combined: + return self.from_char(combined) + + raise ValueError(key) + + @classmethod + def from_vk(cls, vk, **kwargs): + """Creates a key from a virtual key code. + + :param vk: The virtual key code. + + :param kwargs: Any other parameters to pass. + + :return: a key code + """ + return cls(vk=vk, **kwargs) + + @classmethod + def from_char(cls, char, **kwargs): + """Creates a key from a character. + + :param str char: The character. + + :return: a key code + """ + return cls(char=char, **kwargs) + + @classmethod + def from_dead(cls, char, **kwargs): + """Creates a dead key. + + :param char: The dead key. This should be the unicode character + representing the stand alone character, such as ``'~'`` for + *COMBINING TILDE*. + + :return: a key code + """ + return cls(char=char, is_dead=True, **kwargs) + + +class Key(enum.Enum): + """A class representing various buttons that may not correspond to + letters. This includes modifier keys and function keys. + + The actual values for these items differ between platforms. Some platforms + may have additional buttons, but these are guaranteed to be present + everywhere. + """ + #: A generic Alt key. This is a modifier. + alt = 0 + + #: The left Alt key. This is a modifier. + alt_l = 0 + + #: The right Alt key. This is a modifier. + alt_r = 0 + + #: The AltGr key. This is a modifier. + alt_gr = 0 + + #: The Backspace key. + backspace = 0 + + #: The CapsLock key. + caps_lock = 0 + + #: A generic command button. On *PC* platforms, this corresponds to the + #: Super key or Windows key, and on *Mac* it corresponds to the Command + #: key. This may be a modifier. + cmd = 0 + + #: The left command button. On *PC* platforms, this corresponds to the + #: Super key or Windows key, and on *Mac* it corresponds to the Command + #: key. This may be a modifier. + cmd_l = 0 + + #: The right command button. On *PC* platforms, this corresponds to the + #: Super key or Windows key, and on *Mac* it corresponds to the Command + #: key. This may be a modifier. + cmd_r = 0 + + #: A generic Ctrl key. This is a modifier. + ctrl = 0 + + #: The left Ctrl key. This is a modifier. + ctrl_l = 0 + + #: The right Ctrl key. This is a modifier. + ctrl_r = 0 + + #: The Delete key. + delete = 0 + + #: A down arrow key. + down = 0 + + #: The End key. + end = 0 + + #: The Enter or Return key. + enter = 0 + + #: The Esc key. + esc = 0 + + #: The function keys. F1 to F20 are defined. + f1 = 0 + f2 = 0 + f3 = 0 + f4 = 0 + f5 = 0 + f6 = 0 + f7 = 0 + f8 = 0 + f9 = 0 + f10 = 0 + f11 = 0 + f12 = 0 + f13 = 0 + f14 = 0 + f15 = 0 + f16 = 0 + f17 = 0 + f18 = 0 + f19 = 0 + f20 = 0 + + #: The Home key. + home = 0 + + #: A left arrow key. + left = 0 + + #: The PageDown key. + page_down = 0 + + #: The PageUp key. + page_up = 0 + + #: A right arrow key. + right = 0 + + #: A generic Shift key. This is a modifier. + shift = 0 + + #: The left Shift key. This is a modifier. + shift_l = 0 + + #: The right Shift key. This is a modifier. + shift_r = 0 + + #: The Space key. + space = 0 + + #: The Tab key. + tab = 0 + + #: An up arrow key. + up = 0 + + #: The play/pause toggle. + media_play_pause = 0 + + #: The volume mute button. + media_volume_mute = 0 + + #: The volume down button. + media_volume_down = 0 + + #: The volume up button. + media_volume_up = 0 + + #: The previous track button. + media_previous = 0 + + #: The next track button. + media_next = 0 + + #: The Insert key. This may be undefined for some platforms. + insert = 0 + + #: The Menu key. This may be undefined for some platforms. + menu = 0 + + #: The NumLock key. This may be undefined for some platforms. + num_lock = 0 + + #: The Pause/Break key. This may be undefined for some platforms. + pause = 0 + + #: The PrintScreen key. This may be undefined for some platforms. + print_screen = 0 + + #: The ScrollLock key. This may be undefined for some platforms. + scroll_lock = 0 + + +class Controller(object): + """A controller for sending virtual keyboard events to the system. + """ + #: The virtual key codes + _KeyCode = KeyCode + + #: The various keys. + _Key = Key + + class InvalidKeyException(Exception): + """The exception raised when an invalid ``key`` parameter is passed to + either :meth:`Controller.press` or :meth:`Controller.release`. + + Its first argument is the ``key`` parameter. + """ + pass + + class InvalidCharacterException(Exception): + """The exception raised when an invalid character is encountered in + the string passed to :meth:`Controller.type`. + + Its first argument is the index of the character in the string, and the + second the character. + """ + pass + + def __init__(self): + self._log = _logger(self.__class__) + self._modifiers_lock = threading.RLock() + self._modifiers = set() + self._caps_lock = False + self._dead_key = None + + def press(self, key): + """Presses a key. + + A key may be either a string of length 1, one of the :class:`Key` + members or a :class:`KeyCode`. + + Strings will be transformed to :class:`KeyCode` using + :meth:`KeyCode.char`. Members of :class:`Key` will be translated to + their :meth:`~Key.value`. + + :param key: The key to press. + + :raises InvalidKeyException: if the key is invalid + + :raises ValueError: if ``key`` is a string, but its length is not ``1`` + """ + resolved = self._resolve(key) + if resolved is None: + raise self.InvalidKeyException(key) + self._update_modifiers(resolved, True) + + # Update caps lock state + if resolved == self._Key.caps_lock.value: + self._caps_lock = not self._caps_lock + + # If we currently have a dead key pressed, join it with this key + original = resolved + if self._dead_key: + try: + resolved = self._dead_key.join(resolved) + except ValueError: + self._handle(self._dead_key, True) + self._handle(self._dead_key, False) + + # If the key is a dead key, keep it for later + if resolved.is_dead: + self._dead_key = resolved + return + + try: + self._handle(resolved, True) + except self.InvalidKeyException: + if resolved != original: + self._handle(self._dead_key, True) + self._handle(self._dead_key, False) + self._handle(original, True) + + self._dead_key = None + + def release(self, key): + """Releases a key. + + A key may be either a string of length 1, one of the :class:`Key` + members or a :class:`KeyCode`. + + Strings will be transformed to :class:`KeyCode` using + :meth:`KeyCode.char`. Members of :class:`Key` will be translated to + their :meth:`~Key.value`. + + :param key: The key to release. If this is a string, it is passed to + :meth:`touches` and the returned releases are used. + + :raises InvalidKeyException: if the key is invalid + + :raises ValueError: if ``key`` is a string, but its length is not ``1`` + """ + resolved = self._resolve(key) + if resolved is None: + raise self.InvalidKeyException(key) + self._update_modifiers(resolved, False) + + # Ignore released dead keys + if resolved.is_dead: + return + + self._handle(resolved, False) + + def tap(self, key): + """Presses and releases a key. + + This is equivalent to the following code:: + + controller.press(key) + controller.release(key) + + :param key: The key to press. + + :raises InvalidKeyException: if the key is invalid + + :raises ValueError: if ``key`` is a string, but its length is not ``1`` + """ + self.press(key) + self.release(key) + + def touch(self, key, is_press): + """Calls either :meth:`press` or :meth:`release` depending on the value + of ``is_press``. + + :param key: The key to press or release. + + :param bool is_press: Whether to press the key. + + :raises InvalidKeyException: if the key is invalid + """ + if is_press: + self.press(key) + else: + self.release(key) + + @contextlib.contextmanager + def pressed(self, *args): + """Executes a block with some keys pressed. + + :param keys: The keys to keep pressed. + """ + for key in args: + self.press(key) + + try: + yield + finally: + for key in reversed(args): + self.release(key) + + def type(self, string): + """Types a string. + + This method will send all key presses and releases necessary to type + all characters in the string. + + :param str string: The string to type. + + :raises InvalidCharacterException: if an untypable character is + encountered + """ + from . import _CONTROL_CODES + for i, character in enumerate(string): + key = _CONTROL_CODES.get(character, character) + try: + self.press(key) + self.release(key) + + except (ValueError, self.InvalidKeyException): + raise self.InvalidCharacterException(i, character) + + @property + @contextlib.contextmanager + def modifiers(self): + """The currently pressed modifier keys. + + Please note that this reflects only the internal state of this + controller, and not the state of the operating system keyboard buffer. + This property cannot be used to determine whether a key is physically + pressed. + + Only the generic modifiers will be set; when pressing either + :attr:`Key.shift_l`, :attr:`Key.shift_r` or :attr:`Key.shift`, only + :attr:`Key.shift` will be present. + + Use this property within a context block thus:: + + with controller.modifiers as modifiers: + with_block() + + This ensures that the modifiers cannot be modified by another thread. + """ + with self._modifiers_lock: + yield set( + self._as_modifier(modifier) + for modifier in self._modifiers) + + @property + def alt_pressed(self): + """Whether any *alt* key is pressed. + + Please note that this reflects only the internal state of this + controller. See :attr:`modifiers` for more information. + """ + with self.modifiers as modifiers: + return self._Key.alt in modifiers + + @property + def alt_gr_pressed(self): + """Whether *altgr* is pressed. + + Please note that this reflects only the internal state of this + controller. See :attr:`modifiers` for more information. + """ + with self.modifiers as modifiers: + return self._Key.alt_gr in modifiers + + @property + def ctrl_pressed(self): + """Whether any *ctrl* key is pressed. + + Please note that this reflects only the internal state of this + controller. See :attr:`modifiers` for more information. + """ + with self.modifiers as modifiers: + return self._Key.ctrl in modifiers + + @property + def shift_pressed(self): + """Whether any *shift* key is pressed, or *caps lock* is toggled. + + Please note that this reflects only the internal state of this + controller. See :attr:`modifiers` for more information. + """ + if self._caps_lock: + return True + + with self.modifiers as modifiers: + return self._Key.shift in modifiers + + def _resolve(self, key): + """Resolves a key to a :class:`KeyCode` instance. + + This method will convert any key representing a character to uppercase + if a shift modifier is active. + + :param key: The key to resolve. + + :return: a key code, or ``None`` if it cannot be resolved + """ + # Use the value for the key constants + if key in (k for k in self._Key): + return key.value + + # Convert strings to key codes + if isinstance(key, six.string_types): + if len(key) != 1: + raise ValueError(key) + return self._KeyCode.from_char(key) + + # Assume this is a proper key + if isinstance(key, self._KeyCode): + if key.char is not None and self.shift_pressed: + return self._KeyCode(vk=key.vk, char=key.char.upper()) + else: + return key + + def _update_modifiers(self, key, is_press): + """Updates the current modifier list. + + If ``key`` is not a modifier, no action is taken. + + :param key: The key being pressed or released. + """ + # Check whether the key is a modifier + if self._as_modifier(key): + with self._modifiers_lock: + if is_press: + self._modifiers.add(key) + else: + try: + self._modifiers.remove(key) + except KeyError: + pass + + def _as_modifier(self, key): + """Returns a key as the modifier used internally if defined. + + This method will convert values like :attr:`Key.alt_r.value` and + :attr:`Key.shift_l.value` to :attr:`Key.alt` and :attr:`Key.shift`. + + :param key: The possible modifier key. + + :return: the base modifier key, or ``None`` if ``key`` is not a + modifier + """ + from . import _NORMAL_MODIFIERS + return _NORMAL_MODIFIERS.get(key, None) + + def _handle(self, key, is_press): + """The platform implementation of the actual emitting of keyboard + events. + + This is a platform dependent implementation. + + :param Key key: The key to handle. + + :param bool is_press: Whether this is a key press event. + """ + raise NotImplementedError() + + +# pylint: disable=W0223; This is also an abstract class +class Listener(AbstractListener): + """A listener for keyboard events. + + Instances of this class can be used as context managers. This is equivalent + to the following code:: + + listener.start() + try: + listener.wait() + with_statements() + finally: + listener.stop() + + This class inherits from :class:`threading.Thread` and supports all its + methods. It will set :attr:`daemon` to ``True`` when created. + + :param callable on_press: The callback to call when a button is pressed. + + It will be called with the argument ``(key)``, where ``key`` is a + :class:`KeyCode`, a :class:`Key` or ``None`` if the key is unknown. + + :param callable on_release: The callback to call when a button is released. + + It will be called with the argument ``(key)``, where ``key`` is a + :class:`KeyCode`, a :class:`Key` or ``None`` if the key is unknown. + + :param bool suppress: Whether to suppress events. Setting this to ``True`` + will prevent the input events from being passed to the rest of the + system. + + :param kwargs: Any non-standard platform dependent options. These should be + prefixed with the platform name thus: ``darwin_``, ``uinput_``, + ``xorg_`` or ``win32_``. + + Supported values are: + + ``darwin_intercept`` + A callable taking the arguments ``(event_type, event)``, where + ``event_type`` is ``Quartz.kCGEventKeyDown`` or + ``Quartz.kCGEventKeyDown``, and ``event`` is a ``CGEventRef``. + + This callable can freely modify the event using functions like + ``Quartz.CGEventSetIntegerValueField``. If this callable does not + return the event, the event is suppressed system wide. + + ``uinput_device_paths`` + A list of device paths. + + If this is specified, *pynput* will limit the number of devices + checked for the capabilities needed to those passed, otherwise all + system devices will be used. Passing this might be required if an + incorrect device is chosen. + + ``win32_event_filter`` + A callable taking the arguments ``(msg, data)``, where ``msg`` is + the current message, and ``data`` associated data as a + `KBDLLHOOKSTRUCT `_. + + If this callback returns ``False``, the event will not be + propagated to the listener callback. + + If ``self.suppress_event()`` is called, the event is suppressed + system wide. + """ + def __init__(self, on_press=None, on_release=None, suppress=False, + **kwargs): + self._log = _logger(self.__class__) + option_prefix = prefix(Listener, self.__class__) + self._options = { + key[len(option_prefix):]: value + for key, value in kwargs.items() + if key.startswith(option_prefix)} + super(Listener, self).__init__( + on_press=on_press, on_release=on_release, suppress=suppress) +# pylint: enable=W0223 + + def canonical(self, key): + """Performs normalisation of a key. + + This method attempts to convert key events to their canonical form, so + that events will equal regardless of modifier state. + + This method will convert upper case keys to lower case keys, convert + any modifiers with a right and left version to the same value, and may + slow perform additional platform dependent normalisation. + + :param key: The key to normalise. + :type key: Key or KeyCode + + :return: a key + :rtype: Key or KeyCode + """ + from pynput.keyboard import Key, KeyCode, _NORMAL_MODIFIERS + if isinstance(key, KeyCode) and key.char is not None: + return KeyCode.from_char(key.char.lower()) + elif isinstance(key, Key) and key.value in _NORMAL_MODIFIERS: + return _NORMAL_MODIFIERS[key.value] + elif isinstance(key, Key) and key.value.vk is not None: + return KeyCode.from_vk(key.value.vk) + else: + return key diff --git a/pynput/keyboard/_darwin.py b/pynput/keyboard/_darwin.py new file mode 100644 index 0000000..a62c6ad --- /dev/null +++ b/pynput/keyboard/_darwin.py @@ -0,0 +1,345 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +The keyboard implementation for *macOS*. +""" + +# pylint: disable=C0111 +# The documentation is extracted from the base classes + +# pylint: disable=R0903 +# We implement stubs + +import enum + +import Quartz + +from pynput._util.darwin import ( + get_unicode_to_keycode_map, + keycode_context, + ListenerMixin) +from pynput._util.darwin_vks import SYMBOLS +from . import _base + + +# From hidsystem/ev_keymap.h +NX_KEYTYPE_PLAY = 16 +NX_KEYTYPE_MUTE = 7 +NX_KEYTYPE_SOUND_DOWN = 1 +NX_KEYTYPE_SOUND_UP = 0 +NX_KEYTYPE_NEXT = 17 +NX_KEYTYPE_PREVIOUS = 18 + +# pylint: disable=C0103; We want to use the names from the C API +# This is undocumented, but still widely known +kSystemDefinedEventMediaKeysSubtype = 8 + +# We extract this here since the name is very long +otherEventWithType = getattr( + Quartz.NSEvent, + 'otherEventWithType_' + 'location_' + 'modifierFlags_' + 'timestamp_' + 'windowNumber_' + 'context_' + 'subtype_' + 'data1_' + 'data2_') +# pylint: enable=C0103 + + +class KeyCode(_base.KeyCode): + _PLATFORM_EXTENSIONS = ( + # Whether this is a media key + '_is_media', + ) + + # Be explicit about fields + _is_media = None + + @classmethod + def _from_media(cls, vk, **kwargs): + """Creates a media key from a key code. + + :param int vk: The key code. + + :return: a key code + """ + return cls.from_vk(vk, _is_media=True, **kwargs) + + def _event(self, modifiers, mapping, is_pressed): + """This key as a *Quartz* event. + + :param set modifiers: The currently active modifiers. + + :param mapping: The current keyboard mapping. + + :param bool is_press: Whether to generate a press event. + + :return: a *Quartz* event + """ + vk = self.vk or mapping.get(self.char) + if self._is_media: + result = otherEventWithType( + Quartz.NSSystemDefined, + (0, 0), + 0xa00 if is_pressed else 0xb00, + 0, + 0, + 0, + 8, + (self.vk << 16) | ((0xa if is_pressed else 0xb) << 8), + -1).CGEvent() + else: + result = Quartz.CGEventCreateKeyboardEvent( + None, 0 if vk is None else vk, is_pressed) + + Quartz.CGEventSetFlags( + result, + 0 + | (Quartz.kCGEventFlagMaskAlternate + if Key.alt in modifiers else 0) + + | (Quartz.kCGEventFlagMaskCommand + if Key.cmd in modifiers else 0) + + | (Quartz.kCGEventFlagMaskControl + if Key.ctrl in modifiers else 0) + + | (Quartz.kCGEventFlagMaskShift + if Key.shift in modifiers else 0)) + + if vk is None and self.char is not None: + Quartz.CGEventKeyboardSetUnicodeString( + result, len(self.char), self.char) + + return result + + +# pylint: disable=W0212 +class Key(enum.Enum): + # Default keys + alt = KeyCode.from_vk(0x3A) + alt_l = KeyCode.from_vk(0x3A) + alt_r = KeyCode.from_vk(0x3D) + alt_gr = KeyCode.from_vk(0x3D) + backspace = KeyCode.from_vk(0x33) + caps_lock = KeyCode.from_vk(0x39) + cmd = KeyCode.from_vk(0x37) + cmd_l = KeyCode.from_vk(0x37) + cmd_r = KeyCode.from_vk(0x36) + ctrl = KeyCode.from_vk(0x3B) + ctrl_l = KeyCode.from_vk(0x3B) + ctrl_r = KeyCode.from_vk(0x3E) + delete = KeyCode.from_vk(0x75) + down = KeyCode.from_vk(0x7D) + end = KeyCode.from_vk(0x77) + enter = KeyCode.from_vk(0x24) + esc = KeyCode.from_vk(0x35) + f1 = KeyCode.from_vk(0x7A) + f2 = KeyCode.from_vk(0x78) + f3 = KeyCode.from_vk(0x63) + f4 = KeyCode.from_vk(0x76) + f5 = KeyCode.from_vk(0x60) + f6 = KeyCode.from_vk(0x61) + f7 = KeyCode.from_vk(0x62) + f8 = KeyCode.from_vk(0x64) + f9 = KeyCode.from_vk(0x65) + f10 = KeyCode.from_vk(0x6D) + f11 = KeyCode.from_vk(0x67) + f12 = KeyCode.from_vk(0x6F) + f13 = KeyCode.from_vk(0x69) + f14 = KeyCode.from_vk(0x6B) + f15 = KeyCode.from_vk(0x71) + f16 = KeyCode.from_vk(0x6A) + f17 = KeyCode.from_vk(0x40) + f18 = KeyCode.from_vk(0x4F) + f19 = KeyCode.from_vk(0x50) + f20 = KeyCode.from_vk(0x5A) + home = KeyCode.from_vk(0x73) + left = KeyCode.from_vk(0x7B) + page_down = KeyCode.from_vk(0x79) + page_up = KeyCode.from_vk(0x74) + right = KeyCode.from_vk(0x7C) + shift = KeyCode.from_vk(0x38) + shift_l = KeyCode.from_vk(0x38) + shift_r = KeyCode.from_vk(0x3C) + space = KeyCode.from_vk(0x31, char=' ') + tab = KeyCode.from_vk(0x30) + up = KeyCode.from_vk(0x7E) + + media_play_pause = KeyCode._from_media(NX_KEYTYPE_PLAY) + media_volume_mute = KeyCode._from_media(NX_KEYTYPE_MUTE) + media_volume_down = KeyCode._from_media(NX_KEYTYPE_SOUND_DOWN) + media_volume_up = KeyCode._from_media(NX_KEYTYPE_SOUND_UP) + media_previous = KeyCode._from_media(NX_KEYTYPE_PREVIOUS) + media_next = KeyCode._from_media(NX_KEYTYPE_NEXT) +# pylint: enable=W0212 + + +class Controller(_base.Controller): + _KeyCode = KeyCode + _Key = Key + + def __init__(self): + super(Controller, self).__init__() + self._mapping = get_unicode_to_keycode_map() + + def _handle(self, key, is_press): + with self.modifiers as modifiers: + Quartz.CGEventPost( + Quartz.kCGHIDEventTap, + (key if key not in (k for k in Key) else key.value)._event( + modifiers, self._mapping, is_press)) + + +class Listener(ListenerMixin, _base.Listener): + #: The events that we listen to + _EVENTS = ( + Quartz.CGEventMaskBit(Quartz.kCGEventKeyDown) | + Quartz.CGEventMaskBit(Quartz.kCGEventKeyUp) | + Quartz.CGEventMaskBit(Quartz.kCGEventFlagsChanged) | + Quartz.CGEventMaskBit(Quartz.NSSystemDefined) + ) + + # pylint: disable=W0212 + #: A mapping from keysym to special key + _SPECIAL_KEYS = { + (key.value.vk, key.value._is_media): key + for key in Key} + # pylint: enable=W0212 + + #: The event flags set for the various modifier keys + _MODIFIER_FLAGS = { + Key.alt: Quartz.kCGEventFlagMaskAlternate, + Key.alt_l: Quartz.kCGEventFlagMaskAlternate, + Key.alt_r: Quartz.kCGEventFlagMaskAlternate, + Key.cmd: Quartz.kCGEventFlagMaskCommand, + Key.cmd_l: Quartz.kCGEventFlagMaskCommand, + Key.cmd_r: Quartz.kCGEventFlagMaskCommand, + Key.ctrl: Quartz.kCGEventFlagMaskControl, + Key.ctrl_l: Quartz.kCGEventFlagMaskControl, + Key.ctrl_r: Quartz.kCGEventFlagMaskControl, + Key.shift: Quartz.kCGEventFlagMaskShift, + Key.shift_l: Quartz.kCGEventFlagMaskShift, + Key.shift_r: Quartz.kCGEventFlagMaskShift} + + def __init__(self, *args, **kwargs): + super(Listener, self).__init__(*args, **kwargs) + self._flags = 0 + self._context = None + self._intercept = self._options.get( + 'intercept', + None) + + def _run(self): + with keycode_context() as context: + self._context = context + try: + super(Listener, self)._run() + finally: + self._context = None + + def _handle(self, _proxy, event_type, event, _refcon): + # Convert the event to a KeyCode; this may fail, and in that case we + # pass None + try: + key = self._event_to_key(event) + except IndexError: + key = None + + try: + if event_type == Quartz.kCGEventKeyDown: + # This is a normal key press + self.on_press(key) + + elif event_type == Quartz.kCGEventKeyUp: + # This is a normal key release + self.on_release(key) + + elif key == Key.caps_lock: + # We only get an event when caps lock is toggled, so we fake + # press and release + self.on_press(key) + self.on_release(key) + + elif event_type == Quartz.NSSystemDefined: + sys_event = Quartz.NSEvent.eventWithCGEvent_(event) + if sys_event.subtype() == kSystemDefinedEventMediaKeysSubtype: + # The key in the special key dict; True since it is a media + # key + key = ((sys_event.data1() & 0xffff0000) >> 16, True) + if key in self._SPECIAL_KEYS: + flags = sys_event.data1() & 0x0000ffff + is_press = ((flags & 0xff00) >> 8) == 0x0a + if is_press: + self.on_press(self._SPECIAL_KEYS[key]) + else: + self.on_release(self._SPECIAL_KEYS[key]) + + else: + # This is a modifier event---excluding caps lock---for which we + # must check the current modifier state to determine whether + # the key was pressed or released + flags = Quartz.CGEventGetFlags(event) + is_press = flags & self._MODIFIER_FLAGS.get(key, 0) + if is_press: + self.on_press(key) + else: + self.on_release(key) + + finally: + # Store the current flag mask to be able to detect modifier state + # changes + self._flags = Quartz.CGEventGetFlags(event) + + def _event_to_key(self, event): + """Converts a *Quartz* event to a :class:`KeyCode`. + + :param event: The event to convert. + + :return: a :class:`pynput.keyboard.KeyCode` + + :raises IndexError: if the key code is invalid + """ + vk = Quartz.CGEventGetIntegerValueField( + event, Quartz.kCGKeyboardEventKeycode) + event_type = Quartz.CGEventGetType(event) + is_media = True if event_type == Quartz.NSSystemDefined else None + + # First try special keys... + key = (vk, is_media) + if key in self._SPECIAL_KEYS: + return self._SPECIAL_KEYS[key] + + # ...then try characters... + length, chars = Quartz.CGEventKeyboardGetUnicodeString( + event, 100, None, None) + try: + printable = chars.isprintable() + except AttributeError: + printable = chars.isalnum() + if not printable and vk in SYMBOLS \ + and Quartz.CGEventGetFlags(event) \ + & Quartz.kCGEventFlagMaskControl: + return KeyCode.from_char(SYMBOLS[vk], vk=vk) + elif length > 0: + return KeyCode.from_char(chars, vk=vk) + + # ...and fall back on a virtual key code + return KeyCode.from_vk(vk) diff --git a/pynput/keyboard/_dummy.py b/pynput/keyboard/_dummy.py new file mode 100644 index 0000000..ea70949 --- /dev/null +++ b/pynput/keyboard/_dummy.py @@ -0,0 +1,23 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +This module contains a dummy implementation. + +It cannot be used, but importing it will not raise any exceptions. +""" + +from ._base import Controller, Key, KeyCode, Listener diff --git a/pynput/keyboard/_uinput.py b/pynput/keyboard/_uinput.py new file mode 100644 index 0000000..e6dae4f --- /dev/null +++ b/pynput/keyboard/_uinput.py @@ -0,0 +1,445 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +The keyboard implementation for *uinput*. +""" + +# pylint: disable=C0111 +# The documentation is extracted from the base classes + +# pylint: disable=R0903 +# We implement stubs + +import enum +import errno +import functools +import os +import re +import subprocess + +import evdev + +from evdev.events import KeyEvent + +from pynput._util import xorg_keysyms +from pynput._util.uinput import ListenerMixin +from . import _base + + +class KeyCode(_base.KeyCode): + _PLATFORM_EXTENSIONS = ( + # The name for this key + '_x_name', + '_kernel_name', + ) + + # Be explicit about fields + _x_name = None + _kernel_name = None +# pylint: enable=W0212 + + @classmethod + def _from_name(cls, x_name, kernel_name, **kwargs): + """Creates a key from a name. + + :param str x_name: The X name. + + :param str kernel_name: The kernel name. + + :return: a key code + """ + try: + vk = getattr(evdev.ecodes, kernel_name) + except AttributeError: + vk = None + return cls.from_vk( + vk, _x_name=x_name, _kernel_name=kernel_name, **kwargs) + + +# pylint: disable=W0212 +class Key(enum.Enum): + alt = KeyCode._from_name('Alt_L', 'KEY_LEFTALT') + alt_l = KeyCode._from_name('Alt_L', 'KEY_LEFTALT') + alt_r = KeyCode._from_name('Alt_R', 'KEY_RIGHTALT') + alt_gr = KeyCode._from_name('Mode_switch', 'KEY_RIGHTALT') + backspace = KeyCode._from_name('BackSpace', 'KEY_BACKSPACE') + caps_lock = KeyCode._from_name('Caps_Lock', 'KEY_CAPSLOCK') + cmd = KeyCode._from_name('Super_L', 'KEY_LEFTMETA') + cmd_l = KeyCode._from_name('Super_L', 'KEY_LEFTMETA') + cmd_r = KeyCode._from_name('Super_R', 'KEY_RIGHTMETA') + ctrl = KeyCode._from_name('Control_L', 'KEY_LEFTCTRL') + ctrl_l = KeyCode._from_name('Control_L', 'KEY_LEFTCTRL') + ctrl_r = KeyCode._from_name('Control_R', 'KEY_RIGHTCTRL') + delete = KeyCode._from_name('Delete', 'KEY_DELETE') + down = KeyCode._from_name('Down', 'KEY_DOWN') + end = KeyCode._from_name('End', 'KEY_END') + enter = KeyCode._from_name('Return', 'KEY_ENTER') + esc = KeyCode._from_name('Escape', 'KEY_ESC') + f1 = KeyCode._from_name('F1', 'KEY_F1') + f2 = KeyCode._from_name('F2', 'KEY_F2') + f3 = KeyCode._from_name('F3', 'KEY_F3') + f4 = KeyCode._from_name('F4', 'KEY_F4') + f5 = KeyCode._from_name('F5', 'KEY_F5') + f6 = KeyCode._from_name('F6', 'KEY_F6') + f7 = KeyCode._from_name('F7', 'KEY_F7') + f8 = KeyCode._from_name('F8', 'KEY_F8') + f9 = KeyCode._from_name('F9', 'KEY_F9') + f10 = KeyCode._from_name('F10', 'KEY_F10') + f11 = KeyCode._from_name('F11', 'KEY_F11') + f12 = KeyCode._from_name('F12', 'KEY_F12') + f13 = KeyCode._from_name('F13', 'KEY_F13') + f14 = KeyCode._from_name('F14', 'KEY_F14') + f15 = KeyCode._from_name('F15', 'KEY_F15') + f16 = KeyCode._from_name('F16', 'KEY_F16') + f17 = KeyCode._from_name('F17', 'KEY_F17') + f18 = KeyCode._from_name('F18', 'KEY_F18') + f19 = KeyCode._from_name('F19', 'KEY_F19') + f20 = KeyCode._from_name('F20', 'KEY_F20') + home = KeyCode._from_name('Home', 'KEY_HOME') + left = KeyCode._from_name('Left', 'KEY_LEFT') + page_down = KeyCode._from_name('Page_Down', 'KEY_PAGEDOWN') + page_up = KeyCode._from_name('Page_Up', 'KEY_PAGEUP') + right = KeyCode._from_name('Right', 'KEY_RIGHT') + shift = KeyCode._from_name('Shift_L', 'KEY_LEFTSHIFT') + shift_l = KeyCode._from_name('Shift_L', 'KEY_LEFTSHIFT') + shift_r = KeyCode._from_name('Shift_R', 'KEY_RIGHTSHIFT') + space = KeyCode._from_name('space', 'KEY_SPACE', char=' ') + tab = KeyCode._from_name('Tab', 'KEY_TAB', char='\t') + up = KeyCode._from_name('Up', 'KEY_UP') + + media_play_pause = KeyCode._from_name('Play', 'KEY_PLAYPAUSE') + media_volume_mute = KeyCode._from_name('Mute', 'KEY_MUTE') + media_volume_down = KeyCode._from_name('LowerVolume', 'KEY_VOLUMEDOWN') + media_volume_up = KeyCode._from_name('RaiseVolume', 'KEY_VOLUMEUP') + media_previous = KeyCode._from_name('Prev', 'KEY_PREVIOUSSONG') + media_next = KeyCode._from_name('Next', 'KEY_NEXTSONG') + + insert = KeyCode._from_name('Insert', 'KEY_INSERT') + menu = KeyCode._from_name('Menu', 'KEY_MENU') + num_lock = KeyCode._from_name('Num_Lock', 'KEY_NUMLOCK') + pause = KeyCode._from_name('Pause', 'KEY_PAUSE') + print_screen = KeyCode._from_name('Print', 'KEY_SYSRQ') + scroll_lock = KeyCode._from_name('Scroll_Lock', 'KEY_SCROLLLOCK') +# pylint: enable=W0212 + + +class Layout(object): + """A description of the keyboard layout. + """ + #: A regular expression to parse keycodes in the dumpkeys output + #: + #: The groups are: keycode number, key names. + KEYCODE_RE = re.compile( + r'keycode\s+(\d+)\s+=(.*)') + + class Key(object): + """A key in a keyboard layout. + """ + def __init__(self, normal, shifted, alt, alt_shifted): + self._values = ( + normal, + shifted, + alt, + alt_shifted) + + def __str__(self): + return ('<' + 'normal: {}, ' + 'shifted: {}, ' + 'alternative: {}, ' + 'shifted alternative: {}>').format( + self.normal, self.shifted, self.alt, self.alt_shifted) + + __repr__ = __str__ + + def __iter__(self): + return iter(self._values) + + def __getitem__(self, i): + return self._values[i] + + @property + def normal(self): + """The normal key. + """ + return self._values[0] + + @property + def shifted(self): + """The shifted key. + """ + return self._values[1] + + @property + def alt(self): + """The alternative key. + """ + return self._values[2] + + @property + def alt_shifted(self): + """The shifted alternative key. + """ + return self._values[3] + + def __init__(self): + def as_char(k): + return k.value.char if isinstance(k, Key) else k.char + self._vk_table = self._load() + self._char_table = { + as_char(key): ( + vk, + set() + | {Key.shift} if i & 1 else set() + | {Key.alt_gr} if i & 2 else set()) + for vk, keys in self._vk_table.items() + for i, key in enumerate(keys) + if key is not None and as_char(key) is not None} + + def for_vk(self, vk, modifiers): + """Reads a key for a virtual key code and modifier state. + + :param int vk: The virtual key code. + + :param set modifiers: A set of modifiers. + + :return: a mapped key + + :raises KeyError: if ``vk`` is an unknown key + """ + return self._vk_table[vk][ + 0 + | (1 if Key.shift in modifiers else 0) + | (2 if Key.alt_gr in modifiers else 0)] + + def for_char(self, char): + """Reads a virtual key code and modifier state for a character. + + :param str char: The character. + + :return: the tuple ``(vk, modifiers)`` + + :raises KeyError: if ``vk`` is an unknown key + """ + return self._char_table[char] + + @functools.lru_cache() + def _load(self): + """Loads the keyboard layout. + + For simplicity, we call out to the ``dumpkeys`` binary. In the future, + we may want to implement this ourselves. + """ + result = {} + for keycode, names in self.KEYCODE_RE.findall( + subprocess.check_output( + ['dumpkeys', '--full-table', '--keys-only']).decode('utf-8')): + vk = int(keycode) + keys = tuple( + self._parse(vk, name) + for name in names.split()[:4]) + if any(key is not None for key in keys): + result[vk] = self.Key(*keys) + return result + + def _parse(self, vk, name): + """Parses a single key from the ``dumpkeys`` output. + + :param int vk: The key code. + + :param str name: The key name. + + :return: a key representation + """ + try: + # First try special keys... + return next( + key + for key in Key + if key.value._x_name == name) + except StopIteration: + # ...then characters... + try: + _, char = xorg_keysyms.SYMBOLS[name.lstrip('+')] + if char: + return KeyCode.from_char(char, vk=vk) + except KeyError: + pass + + # ...and finally special dumpkeys names + try: + return KeyCode.from_char({ + 'one': '1', + 'two': '2', + 'three': '3', + 'four': '4', + 'five': '5', + 'six': '6', + 'seven': '7', + 'eight': '8', + 'nine': '9', + 'zero': '0'}[name]) + except KeyError: + pass + + +class Controller(_base.Controller): + _KeyCode = KeyCode + _Key = Key + + def __init__(self, *args, **kwargs): + super(Controller, self).__init__(*args, **kwargs) + self._layout = LAYOUT + self._dev = evdev.UInput() + + def __del__(self): + if hasattr(self, '_dev'): + self._dev.close() + + def _handle(self, key, is_press): + # Resolve the key to a virtual key code and a possible set of required + # modifiers + try: + vk, required_modifiers = self._to_vk_and_modifiers(key) + except ValueError: + raise self.InvalidKeyException(key) + + # Determine how we need to modify the modifier state + if is_press and required_modifiers is not None: + with self.modifiers as modifiers: + vk, required_modifiers = self._layout.for_char(key.char) + to_press = { + getattr(evdev.ecodes, key.value._kernel_name) + for key in (required_modifiers - modifiers)} + to_release = { + getattr(evdev.ecodes, key.value._kernel_name) + for key in (modifiers - required_modifiers)} + else: + to_release = set() + to_press = set() + + # Update the modifier state, send the key, and finally release any + # modifiers + cleanup = [] + try: + for k in to_release: + self._send(k, False) + cleanup.append((k, True)) + for k in to_press: + self._send(k, True) + cleanup.append((k, False)) + + self._send(vk, is_press) + + finally: + for e in reversed(cleanup): + # pylint: disable E722; we want to suppress exceptions + try: + self._send(*e) + except: + pass + # pylint: enable E722 + + self._dev.syn() + + def _to_vk_and_modifiers(self, key): + """Resolves a key to a virtual key code and a modifier set. + + :param key: The key to resolve. + :type key: Key or KeyCode + + :return: a virtual key code and possible required modifiers + """ + if hasattr(key, 'vk') and key.vk is not None: + return (key.vk, None) + elif hasattr(key, 'char') and key.char is not None: + return self._layout.for_char(key.char) + else: + raise ValueError(key) + + def _send(self, vk, is_press): + """Sends a virtual key event. + + This method does not perform ``SYN``. + + :param int vk: The virtual key. + + :param bool is_press: Whether this is a press event. + """ + self._dev.write(evdev.ecodes.EV_KEY, vk, int(is_press)) + + +class Listener(ListenerMixin, _base.Listener): + _EVENTS = ( + evdev.ecodes.EV_KEY,) + + #: A + _MODIFIERS = { + Key.alt.value.vk: Key.alt, + Key.alt_l.value.vk: Key.alt, + Key.alt_r.value.vk: Key.alt, + Key.alt_gr.value.vk: Key.alt_gr, + Key.shift.value.vk: Key.shift, + Key.shift_l.value.vk: Key.shift, + Key.shift_r.value.vk: Key.shift} + + def __init__(self, *args, **kwargs): + super(Listener, self).__init__(*args, **kwargs) + self._layout = LAYOUT + self._modifiers = set() + + def _handle(self, event): + is_press = event.value in (KeyEvent.key_down, KeyEvent.key_hold) + vk = event.code + + # Update the modifier state + if vk in self._MODIFIERS: + modifier = self._MODIFIERS[vk] + if is_press: + self._modifiers.add(modifier) + elif modifier in self._modifiers: + self._modifiers.remove(modifier) + + # Attempt to map the virtual key code to a key + try: + key = self._layout.for_vk(vk, self._modifiers) + except KeyError: + try: + key = next( + key + for key in Key + if key.value.vk == vk) + except StopIteration: + key = KeyCode.from_vk(vk) + + if is_press: + self.on_press(key) + else: + self.on_release(key) + + +try: + #: The keyboard layout. + LAYOUT = Layout() +except subprocess.CalledProcessError as e: + raise ImportError('failed to load keyboard layout: "' + str(e) + ( + '"; please make sure you are root' if os.getuid() != 1 else '"')) +except OSError as e: + raise ImportError({ + errno.ENOENT: 'the binary dumpkeys is not installed'}.get( + e.args[0], + str(e))) diff --git a/pynput/keyboard/_win32.py b/pynput/keyboard/_win32.py new file mode 100644 index 0000000..5ff1d2d --- /dev/null +++ b/pynput/keyboard/_win32.py @@ -0,0 +1,347 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +The keyboard implementation for *Windows*. +""" + +# pylint: disable=C0111 +# The documentation is extracted from the base classes + +# pylint: disable=R0903 +# We implement stubs + +import contextlib +import ctypes +import enum +import six + +from ctypes import wintypes + +import pynput._util.win32_vks as VK + +from pynput._util import AbstractListener +from pynput._util.win32 import ( + INPUT, + INPUT_union, + KEYBDINPUT, + KeyTranslator, + ListenerMixin, + MapVirtualKey, + SendInput, + SystemHook, + VkKeyScan) +from . import _base + + +class KeyCode(_base.KeyCode): + _PLATFORM_EXTENSIONS = ( + # Any extra flags. + '_flags', + + #: The scan code. + '_scan', + ) + + # Be explicit about fields + _flags = None + _scan = None + + def _parameters(self, is_press): + """The parameters to pass to ``SendInput`` to generate this key. + + :param bool is_press: Whether to generate a press event. + + :return: all arguments to pass to ``SendInput`` for this key + + :rtype: dict + """ + if self.vk: + vk = self.vk + scan = self._scan \ + or MapVirtualKey(vk, MapVirtualKey.MAPVK_VK_TO_VSC) + flags = 0 + else: + res = VkKeyScan(self.char) + if (res >> 8) & 0xFF == 0: + vk = res & 0xFF + scan = self._scan \ + or MapVirtualKey(vk, MapVirtualKey.MAPVK_VK_TO_VSC) + flags = 0 + else: + vk = 0 + scan = ord(self.char) + flags = KEYBDINPUT.UNICODE + state_flags = (KEYBDINPUT.KEYUP if not is_press else 0) + return dict( + dwFlags=(self._flags or 0) | flags | state_flags, + wVk=vk, + wScan=scan) + + @classmethod + def _from_ext(cls, vk, **kwargs): + """Creates an extended key code. + + :param vk: The virtual key code. + + :param kwargs: Any other parameters to pass. + + :return: a key code + """ + return cls.from_vk(vk, _flags=KEYBDINPUT.EXTENDEDKEY, **kwargs) + + +# pylint: disable=W0212 +class Key(enum.Enum): + alt = KeyCode.from_vk(VK.MENU) + alt_l = KeyCode.from_vk(VK.LMENU) + alt_r = KeyCode._from_ext(VK.RMENU) + alt_gr = KeyCode.from_vk(VK.RMENU) + backspace = KeyCode.from_vk(VK.BACK) + caps_lock = KeyCode.from_vk(VK.CAPITAL) + cmd = KeyCode.from_vk(VK.LWIN) + cmd_l = KeyCode.from_vk(VK.LWIN) + cmd_r = KeyCode.from_vk(VK.RWIN) + ctrl = KeyCode.from_vk(VK.CONTROL) + ctrl_l = KeyCode.from_vk(VK.LCONTROL) + ctrl_r = KeyCode._from_ext(VK.RCONTROL) + delete = KeyCode._from_ext(VK.DELETE) + down = KeyCode._from_ext(VK.DOWN) + end = KeyCode._from_ext(VK.END) + enter = KeyCode.from_vk(VK.RETURN) + esc = KeyCode.from_vk(VK.ESCAPE) + f1 = KeyCode.from_vk(VK.F1) + f2 = KeyCode.from_vk(VK.F2) + f3 = KeyCode.from_vk(VK.F3) + f4 = KeyCode.from_vk(VK.F4) + f5 = KeyCode.from_vk(VK.F5) + f6 = KeyCode.from_vk(VK.F6) + f7 = KeyCode.from_vk(VK.F7) + f8 = KeyCode.from_vk(VK.F8) + f9 = KeyCode.from_vk(VK.F9) + f10 = KeyCode.from_vk(VK.F10) + f11 = KeyCode.from_vk(VK.F11) + f12 = KeyCode.from_vk(VK.F12) + f13 = KeyCode.from_vk(VK.F13) + f14 = KeyCode.from_vk(VK.F14) + f15 = KeyCode.from_vk(VK.F15) + f16 = KeyCode.from_vk(VK.F16) + f17 = KeyCode.from_vk(VK.F17) + f18 = KeyCode.from_vk(VK.F18) + f19 = KeyCode.from_vk(VK.F19) + f20 = KeyCode.from_vk(VK.F20) + f21 = KeyCode.from_vk(VK.F21) + f22 = KeyCode.from_vk(VK.F22) + f23 = KeyCode.from_vk(VK.F23) + f24 = KeyCode.from_vk(VK.F24) + home = KeyCode._from_ext(VK.HOME) + left = KeyCode._from_ext(VK.LEFT) + page_down = KeyCode._from_ext(VK.NEXT) + page_up = KeyCode._from_ext(VK.PRIOR) + right = KeyCode._from_ext(VK.RIGHT) + shift = KeyCode.from_vk(VK.LSHIFT) + shift_l = KeyCode.from_vk(VK.LSHIFT) + shift_r = KeyCode.from_vk(VK.RSHIFT) + space = KeyCode.from_vk(VK.SPACE, char=' ') + tab = KeyCode.from_vk(VK.TAB) + up = KeyCode._from_ext(VK.UP) + + media_play_pause = KeyCode._from_ext(VK.MEDIA_PLAY_PAUSE) + media_volume_mute = KeyCode._from_ext(VK.VOLUME_MUTE) + media_volume_down = KeyCode._from_ext(VK.VOLUME_DOWN) + media_volume_up = KeyCode._from_ext(VK.VOLUME_UP) + media_previous = KeyCode._from_ext(VK.MEDIA_PREV_TRACK) + media_next = KeyCode._from_ext(VK.MEDIA_NEXT_TRACK) + + insert = KeyCode._from_ext(VK.INSERT) + menu = KeyCode.from_vk(VK.APPS) + num_lock = KeyCode._from_ext(VK.NUMLOCK) + pause = KeyCode.from_vk(VK.PAUSE) + print_screen = KeyCode._from_ext(VK.SNAPSHOT) + scroll_lock = KeyCode.from_vk(VK.SCROLL) +# pylint: enable=W0212 + + +class Controller(_base.Controller): + _KeyCode = KeyCode + _Key = Key + + def __init__(self, *args, **kwargs): + super(Controller, self).__init__(*args, **kwargs) + + def _handle(self, key, is_press): + SendInput( + 1, + ctypes.byref(INPUT( + type=INPUT.KEYBOARD, + value=INPUT_union( + ki=KEYBDINPUT(**key._parameters(is_press))))), + ctypes.sizeof(INPUT)) + + +class Listener(ListenerMixin, _base.Listener): + #: The Windows hook ID for low level keyboard events, ``WH_KEYBOARD_LL`` + _EVENTS = 13 + + _WM_INPUTLANGCHANGE = 0x0051 + _WM_KEYDOWN = 0x0100 + _WM_KEYUP = 0x0101 + _WM_SYSKEYDOWN = 0x0104 + _WM_SYSKEYUP = 0x0105 + + # A bit flag attached to messages indicating that the payload is an actual + # UTF-16 character code + _UTF16_FLAG = 0x1000 + + # A special virtual key code designating unicode characters + _VK_PACKET = 0xE7 + + #: The messages that correspond to a key press + _PRESS_MESSAGES = (_WM_KEYDOWN, _WM_SYSKEYDOWN) + + #: The messages that correspond to a key release + _RELEASE_MESSAGES = (_WM_KEYUP, _WM_SYSKEYUP) + + #: Additional window messages to propagate to the subclass handler. + _WM_NOTIFICATIONS = ( + _WM_INPUTLANGCHANGE, + ) + + #: A mapping from keysym to special key + _SPECIAL_KEYS = { + key.value.vk: key + for key in Key} + + _HANDLED_EXCEPTIONS = ( + SystemHook.SuppressException,) + + class _KBDLLHOOKSTRUCT(ctypes.Structure): + """Contains information about a mouse event passed to a + ``WH_KEYBOARD_LL`` hook procedure, ``LowLevelKeyboardProc``. + """ + _fields_ = [ + ('vkCode', wintypes.DWORD), + ('scanCode', wintypes.DWORD), + ('flags', wintypes.DWORD), + ('time', wintypes.DWORD), + ('dwExtraInfo', ctypes.c_void_p)] + + #: A pointer to a :class:`KBDLLHOOKSTRUCT` + _LPKBDLLHOOKSTRUCT = ctypes.POINTER(_KBDLLHOOKSTRUCT) + + def __init__(self, *args, **kwargs): + super(Listener, self).__init__(*args, **kwargs) + self._translator = KeyTranslator() + self._event_filter = self._options.get( + 'event_filter', + lambda msg, data: True) + + def _convert(self, code, msg, lpdata): + if code != SystemHook.HC_ACTION: + return + + data = ctypes.cast(lpdata, self._LPKBDLLHOOKSTRUCT).contents + is_packet = data.vkCode == self._VK_PACKET + + # Suppress further propagation of the event if it is filtered + if self._event_filter(msg, data) is False: + return None + elif is_packet: + return (msg | self._UTF16_FLAG, data.scanCode) + else: + return (msg, data.vkCode) + + @AbstractListener._emitter + def _process(self, wparam, lparam): + msg = wparam + vk = lparam + + # If the key has the UTF-16 flag, we treat it as a unicode character, + # otherwise convert the event to a KeyCode; this may fail, and in that + # case we pass None + is_utf16 = msg & self._UTF16_FLAG + if is_utf16: + msg = msg ^ self._UTF16_FLAG + scan = vk + key = KeyCode.from_char(six.unichr(scan)) + else: + try: + key = self._event_to_key(msg, vk) + except OSError: + key = None + + if msg in self._PRESS_MESSAGES: + self.on_press(key) + + elif msg in self._RELEASE_MESSAGES: + self.on_release(key) + + # pylint: disable=R0201 + @contextlib.contextmanager + def _receive(self): + """An empty context manager; we do not need to fake keyboard events. + """ + yield + # pylint: enable=R0201 + + def _on_notification(self, code, wparam, lparam): + """Receives ``WM_INPUTLANGCHANGE`` and updates the cached layout. + """ + if code == self._WM_INPUTLANGCHANGE: + self._translator.update_layout() + + def _event_to_key(self, msg, vk): + """Converts an :class:`_KBDLLHOOKSTRUCT` to a :class:`KeyCode`. + + :param msg: The message received. + + :param vk: The virtual key code to convert. + + :return: a :class:`pynput.keyboard.KeyCode` + + :raises OSError: if the message and data could not be converted + """ + # If the virtual key code corresponds to a Key value, we prefer that + if vk in self._SPECIAL_KEYS: + return self._SPECIAL_KEYS[vk] + else: + return KeyCode(**self._translate( + vk, + msg in self._PRESS_MESSAGES)) + + def _translate(self, vk, is_press): + """Translates a virtual key code to a parameter list passable to + :class:`pynput.keyboard.KeyCode`. + + :param int vk: The virtual key code. + + :param bool is_press: Whether this is a press event. + + :return: a parameter list to the :class:`pynput.keyboard.KeyCode` + constructor + """ + return self._translator(vk, is_press) + + def canonical(self, key): + # If the key has a scan code, and we can find the character for it, + # return that, otherwise call the super class + scan = getattr(key, '_scan', None) + if scan is not None: + char = self._translator.char_from_scan(scan) + if char is not None: + return KeyCode.from_char(char) + + return super(Listener, self).canonical(key) diff --git a/pynput/keyboard/_xorg.py b/pynput/keyboard/_xorg.py new file mode 100644 index 0000000..3a5c459 --- /dev/null +++ b/pynput/keyboard/_xorg.py @@ -0,0 +1,668 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +The keyboard implementation for *Xorg*. +""" + +# pylint: disable=C0111 +# The documentation is extracted from the base classes + +# pylint: disable=R0903 +# We implement stubs + +# pylint: disable=W0611 +try: + import pynput._util.xorg +except Exception as e: + raise ImportError('failed to acquire X connection: {}'.format(str(e)), e) +# pylint: enable=W0611 + +import enum +import threading + +import Xlib.display +import Xlib.ext +import Xlib.ext.xtest +import Xlib.X +import Xlib.XK +import Xlib.protocol +import Xlib.keysymdef.xkb + +from pynput._util import NotifierMixin +from pynput._util.xorg import ( + alt_mask, + alt_gr_mask, + char_to_keysym, + display_manager, + index_to_shift, + keyboard_mapping, + ListenerMixin, + numlock_mask, + shift_to_index, + symbol_to_keysym) +from pynput._util.xorg_keysyms import ( + CHARS, + DEAD_KEYS, + KEYPAD_KEYS, + KEYSYMS, + SYMBOLS) +from . import _base + + +class KeyCode(_base.KeyCode): + _PLATFORM_EXTENSIONS = ( + # The symbol name for this key + '_symbol', + ) + + # Be explicit about fields + _symbol = None + + @classmethod + def _from_symbol(cls, symbol, **kwargs): + """Creates a key from a symbol. + + :param str symbol: The symbol name. + + :return: a key code + """ + # First try simple translation + keysym = Xlib.XK.string_to_keysym(symbol) + if keysym: + return cls.from_vk(keysym, _symbol=symbol, **kwargs) + + # If that fails, try checking a module attribute of Xlib.keysymdef.xkb + if not keysym: + # pylint: disable=W0702; we want to ignore errors + try: + symbol = 'XK_' + symbol + return cls.from_vk( + getattr(Xlib.keysymdef.xkb, symbol, 0), + _symbol=symbol, + **kwargs) + except: + return cls.from_vk( + SYMBOLS.get(symbol, (0,))[0], + _symbol=symbol, + **kwargs) + # pylint: enable=W0702 + + @classmethod + def _from_media(cls, name, **kwargs): + """Creates a media key from a partial name. + + :param str name: The name. The actual symbol name will be this string + with ``'XF86_Audio'`` prepended. + + :return: a key code + """ + return cls._from_symbol('XF86_Audio' + name, **kwargs) + + +# pylint: disable=W0212 +class Key(enum.Enum): + # Default keys + alt = KeyCode._from_symbol('Alt_L') + alt_l = KeyCode._from_symbol('Alt_L') + alt_r = KeyCode._from_symbol('Alt_R') + alt_gr = KeyCode._from_symbol('Mode_switch') + backspace = KeyCode._from_symbol('BackSpace') + caps_lock = KeyCode._from_symbol('Caps_Lock') + cmd = KeyCode._from_symbol('Super_L') + cmd_l = KeyCode._from_symbol('Super_L') + cmd_r = KeyCode._from_symbol('Super_R') + ctrl = KeyCode._from_symbol('Control_L') + ctrl_l = KeyCode._from_symbol('Control_L') + ctrl_r = KeyCode._from_symbol('Control_R') + delete = KeyCode._from_symbol('Delete') + down = KeyCode._from_symbol('Down') + end = KeyCode._from_symbol('End') + enter = KeyCode._from_symbol('Return') + esc = KeyCode._from_symbol('Escape') + f1 = KeyCode._from_symbol('F1') + f2 = KeyCode._from_symbol('F2') + f3 = KeyCode._from_symbol('F3') + f4 = KeyCode._from_symbol('F4') + f5 = KeyCode._from_symbol('F5') + f6 = KeyCode._from_symbol('F6') + f7 = KeyCode._from_symbol('F7') + f8 = KeyCode._from_symbol('F8') + f9 = KeyCode._from_symbol('F9') + f10 = KeyCode._from_symbol('F10') + f11 = KeyCode._from_symbol('F11') + f12 = KeyCode._from_symbol('F12') + f13 = KeyCode._from_symbol('F13') + f14 = KeyCode._from_symbol('F14') + f15 = KeyCode._from_symbol('F15') + f16 = KeyCode._from_symbol('F16') + f17 = KeyCode._from_symbol('F17') + f18 = KeyCode._from_symbol('F18') + f19 = KeyCode._from_symbol('F19') + f20 = KeyCode._from_symbol('F20') + home = KeyCode._from_symbol('Home') + left = KeyCode._from_symbol('Left') + page_down = KeyCode._from_symbol('Page_Down') + page_up = KeyCode._from_symbol('Page_Up') + right = KeyCode._from_symbol('Right') + shift = KeyCode._from_symbol('Shift_L') + shift_l = KeyCode._from_symbol('Shift_L') + shift_r = KeyCode._from_symbol('Shift_R') + space = KeyCode._from_symbol('space', char=' ') + tab = KeyCode._from_symbol('Tab') + up = KeyCode._from_symbol('Up') + + media_play_pause = KeyCode._from_media('Play') + media_volume_mute = KeyCode._from_media('Mute') + media_volume_down = KeyCode._from_media('LowerVolume') + media_volume_up = KeyCode._from_media('RaiseVolume') + media_previous = KeyCode._from_media('Prev') + media_next = KeyCode._from_media('Next') + + insert = KeyCode._from_symbol('Insert') + menu = KeyCode._from_symbol('Menu') + num_lock = KeyCode._from_symbol('Num_Lock') + pause = KeyCode._from_symbol('Pause') + print_screen = KeyCode._from_symbol('Print') + scroll_lock = KeyCode._from_symbol('Scroll_Lock') +# pylint: enable=W0212 + + +class Controller(NotifierMixin, _base.Controller): + _KeyCode = KeyCode + _Key = Key + + #: The shift mask for :attr:`Key.ctrl` + CTRL_MASK = Xlib.X.ControlMask + + #: The shift mask for :attr:`Key.shift` + SHIFT_MASK = Xlib.X.ShiftMask + + def __init__(self, *args, **kwargs): + super(Controller, self).__init__(*args, **kwargs) + self._display = Xlib.display.Display() + self._keyboard_mapping = None + self._borrows = {} + self._borrow_lock = threading.RLock() + + # pylint: disable=C0103; this is treated as a class scope constant, but + # we cannot set it in the class scope, as it requires a Display instance + self.ALT_MASK = alt_mask(self._display) + self.ALT_GR_MASK = alt_gr_mask(self._display) + # pylint: enable=C0103 + + def __del__(self): + if self._display: + self._display.close() + + @property + def keyboard_mapping(self): + """A mapping from *keysyms* to *key codes*. + + Each value is the tuple ``(key_code, shift_state)``. By sending an + event with the specified *key code* and shift state, the specified + *keysym* will be touched. + """ + if not self._keyboard_mapping: + self._update_keyboard_mapping() + return self._keyboard_mapping + + def _handle(self, key, is_press): + """Resolves a key identifier and sends a keyboard event. + + :param event: The *X* keyboard event. + + :param int keysym: The keysym to handle. + """ + event = Xlib.display.event.KeyPress if is_press \ + else Xlib.display.event.KeyRelease + keysym = self._keysym(key) + + # Make sure to verify that the key was resolved + if keysym is None: + raise self.InvalidKeyException(key) + + # If the key has a virtual key code, use that immediately with + # fake_input; fake input,being an X server extension, has access to + # more internal state that we do + if key.vk is not None: + with display_manager(self._display) as dm: + Xlib.ext.xtest.fake_input( + dm, + Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, + dm.keysym_to_keycode(key.vk)) + + # Otherwise use XSendEvent; we need to use this in the general case to + # work around problems with keyboard layouts + else: + try: + keycode, shift_state = self.keyboard_mapping[keysym] + self._send_key(event, keycode, shift_state) + + except KeyError: + with self._borrow_lock: + keycode, index, count = self._borrows[keysym] + self._send_key( + event, + keycode, + index_to_shift(self._display, index)) + count += 1 if is_press else -1 + self._borrows[keysym] = (keycode, index, count) + + # Notify any running listeners + self._emit('_on_fake_event', key, is_press) + + def _keysym(self, key): + """Converts a key to a *keysym*. + + :param KeyCode key: The key code to convert. + """ + return self._resolve_dead(key) if key.is_dead else None \ + or self._resolve_special(key) \ + or self._resolve_normal(key) \ + or self._resolve_borrowed(key) \ + or self._resolve_borrowing(key) + + def _send_key(self, event, keycode, shift_state): + """Sends a single keyboard event. + + :param event: The *X* keyboard event. + + :param int keycode: The calculated keycode. + + :param int shift_state: The shift state. The actual value used is + :attr:`shift_state` or'd with this value. + """ + with display_manager(self._display) as dm, self.modifiers as modifiers: + # Under certain cimcumstances, such as when running under Xephyr, + # the value returned by dm.get_input_focus is an int + window = dm.get_input_focus().focus + send_event = getattr( + window, + 'send_event', + lambda event: dm.send_event(window, event)) + send_event(event( + detail=keycode, + state=shift_state | self._shift_mask(modifiers), + time=0, + root=dm.screen().root, + window=window, + same_screen=0, + child=Xlib.X.NONE, + root_x=0, root_y=0, event_x=0, event_y=0)) + + def _resolve_dead(self, key): + """Tries to resolve a dead key. + + :param str identifier: The identifier to resolve. + """ + # pylint: disable=W0702; we want to ignore errors + try: + keysym, _ = SYMBOLS[CHARS[key.combining]] + except: + return None + # pylint: enable=W0702 + + if keysym not in self.keyboard_mapping: + return None + + return keysym + + def _resolve_special(self, key): + """Tries to resolve a special key. + + A special key has the :attr:`~KeyCode.vk` attribute set. + + :param KeyCode key: The key to resolve. + """ + if not key.vk: + return None + + return key.vk + + def _resolve_normal(self, key): + """Tries to resolve a normal key. + + A normal key exists on the keyboard, and is typed by pressing + and releasing a simple key, possibly in combination with a modifier. + + :param KeyCode key: The key to resolve. + """ + keysym = self._key_to_keysym(key) + if keysym is None: + return None + + if keysym not in self.keyboard_mapping: + return None + + return keysym + + def _resolve_borrowed(self, key): + """Tries to resolve a key by looking up the already borrowed *keysyms*. + + A borrowed *keysym* does not exist on the keyboard, but has been + temporarily added to the layout. + + :param KeyCode key: The key to resolve. + """ + keysym = self._key_to_keysym(key) + if keysym is None: + return None + + with self._borrow_lock: + if keysym not in self._borrows: + return None + + return keysym + + def _resolve_borrowing(self, key): + """Tries to resolve a key by modifying the layout temporarily. + + A borrowed *keysym* does not exist on the keyboard, but is temporarily + added to the layout. + + :param KeyCode key: The key to resolve. + """ + keysym = self._key_to_keysym(key) + if keysym is None: + return None + + mapping = self._display.get_keyboard_mapping(8, 255 - 8) + + def i2kc(index): + return index + 8 + + def kc2i(keycode): + return keycode - 8 + + #: Finds a keycode and index by looking at already used keycodes + def reuse(): + for _, (keycode, _, _) in self._borrows.items(): + keycodes = mapping[kc2i(keycode)] + + # Only the first four items are addressable by X + for index in range(4): + if not keycodes[index]: + return keycode, index + + #: Finds a keycode and index by using a new keycode + def borrow(): + for i, keycodes in enumerate(mapping): + if not any(keycodes): + return i2kc(i), 0 + + #: Finds a keycode and index by reusing an old, unused one + def overwrite(): + for keysym, (keycode, index, count) in self._borrows.items(): + if count < 1: + del self._borrows[keysym] + return keycode, index + + #: Registers a keycode for a specific key and modifier state + def register(dm, keycode, index): + i = kc2i(keycode) + + # Check for use of empty mapping with a character that has upper + # and lower forms + lower = key.char.lower() + upper = key.char.upper() + if lower != upper and len(lower) == 1 and len(upper) == 1 and all( + m == Xlib.XK.NoSymbol + for m in mapping[i]): + lower = self._key_to_keysym(KeyCode.from_char(lower)) + upper = self._key_to_keysym(KeyCode.from_char(upper)) + if lower: + mapping[i][0] = lower + self._borrows[lower] = (keycode, 0, 0) + if upper: + mapping[i][1] = upper + self._borrows[upper] = (keycode, 1, 0) + else: + mapping[i][index] = keysym + self._borrows[keysym] = (keycode, index, 0) + dm.change_keyboard_mapping(keycode, mapping[i:i + 1]) + + try: + with display_manager(self._display) as dm, self._borrow_lock as _: + # First try an already used keycode, then try a new one, and + # fall back on reusing one that is not currently pressed + register(dm, *( + reuse() or + borrow() or + overwrite())) + return keysym + + except TypeError: + return None + + def _key_to_keysym(self, key): + """Converts a character key code to a *keysym*. + + :param KeyCode key: The key code. + + :return: a keysym if found + :rtype: int or None + """ + # If the key code already has a VK, simply return it + if key.vk is not None: + return key.vk + + # If the character has no associated symbol, we try to map the + # character to a keysym + symbol = CHARS.get(key.char, None) + if symbol is None: + return char_to_keysym(key.char) + + # Otherwise we attempt to convert the symbol to a keysym + # pylint: disable=W0702; we want to ignore errors + try: + return symbol_to_keysym(symbol) + except: + try: + return SYMBOLS[symbol][0] + except: + return None + # pylint: enable=W0702 + + def _shift_mask(self, modifiers): + """The *X* modifier mask to apply for a set of modifiers. + + :param set modifiers: A set of active modifiers for which to get the + shift mask. + """ + return ( + 0 + | (self.ALT_MASK + if Key.alt in modifiers else 0) + + | (self.ALT_GR_MASK + if Key.alt_gr in modifiers else 0) + + | (self.CTRL_MASK + if Key.ctrl in modifiers else 0) + + | (self.SHIFT_MASK + if Key.shift in modifiers else 0)) + + def _update_keyboard_mapping(self): + """Updates the keyboard mapping. + """ + with display_manager(self._display) as dm: + self._keyboard_mapping = keyboard_mapping(dm) + + +@Controller._receiver +class Listener(ListenerMixin, _base.Listener): + _EVENTS = ( + Xlib.X.KeyPress, + Xlib.X.KeyRelease) + + #: A mapping from keysym to special key + _SPECIAL_KEYS = { + key.value.vk: key + for key in Key} + + #: A mapping from numeric keypad keys to keys + _KEYPAD_KEYS = { + KEYPAD_KEYS['KP_0']: KeyCode.from_char('0'), + KEYPAD_KEYS['KP_1']: KeyCode.from_char('1'), + KEYPAD_KEYS['KP_2']: KeyCode.from_char('2'), + KEYPAD_KEYS['KP_3']: KeyCode.from_char('3'), + KEYPAD_KEYS['KP_4']: KeyCode.from_char('4'), + KEYPAD_KEYS['KP_5']: KeyCode.from_char('5'), + KEYPAD_KEYS['KP_6']: KeyCode.from_char('6'), + KEYPAD_KEYS['KP_7']: KeyCode.from_char('7'), + KEYPAD_KEYS['KP_8']: KeyCode.from_char('8'), + KEYPAD_KEYS['KP_9']: KeyCode.from_char('9'), + KEYPAD_KEYS['KP_Add']: KeyCode.from_char('+'), + KEYPAD_KEYS['KP_Decimal']: KeyCode.from_char(','), + KEYPAD_KEYS['KP_Delete']: Key.delete, + KEYPAD_KEYS['KP_Divide']: KeyCode.from_char('/'), + KEYPAD_KEYS['KP_Down']: Key.down, + KEYPAD_KEYS['KP_End']: Key.end, + KEYPAD_KEYS['KP_Enter']: Key.enter, + KEYPAD_KEYS['KP_Equal']: KeyCode.from_char('='), + KEYPAD_KEYS['KP_F1']: Key.f1, + KEYPAD_KEYS['KP_F2']: Key.f2, + KEYPAD_KEYS['KP_F3']: Key.f3, + KEYPAD_KEYS['KP_F4']: Key.f4, + KEYPAD_KEYS['KP_Home']: Key.home, + KEYPAD_KEYS['KP_Insert']: Key.insert, + KEYPAD_KEYS['KP_Left']: Key.left, + KEYPAD_KEYS['KP_Multiply']: KeyCode.from_char('*'), + KEYPAD_KEYS['KP_Page_Down']: Key.page_down, + KEYPAD_KEYS['KP_Page_Up']: Key.page_up, + KEYPAD_KEYS['KP_Right']: Key.right, + KEYPAD_KEYS['KP_Space']: Key.space, + KEYPAD_KEYS['KP_Subtract']: KeyCode.from_char('-'), + KEYPAD_KEYS['KP_Tab']: Key.tab, + KEYPAD_KEYS['KP_Up']: Key.up} + + def __init__(self, *args, **kwargs): + super(Listener, self).__init__(*args, **kwargs) + self._keyboard_mapping = None + + def _run(self): + with self._receive(): + super(Listener, self)._run() + + def _initialize(self, display): + # Get the keyboard mapping to be able to translate events details to + # key codes + min_keycode = display.display.info.min_keycode + keycode_count = display.display.info.max_keycode - min_keycode + 1 + self._keyboard_mapping = display.get_keyboard_mapping( + min_keycode, keycode_count) + + def _handle(self, display, event): + # Convert the event to a KeyCode; this may fail, and in that case we + # pass None + try: + key = self._event_to_key(display, event) + except IndexError: + key = None + + if event.type == Xlib.X.KeyPress: + self.on_press(key) + + elif event.type == Xlib.X.KeyRelease: + self.on_release(key) + + def _suppress_start(self, display): + display.screen().root.grab_keyboard( + self._event_mask, Xlib.X.GrabModeAsync, Xlib.X.GrabModeAsync, + Xlib.X.CurrentTime) + + def _suppress_stop(self, display): + display.ungrab_keyboard(Xlib.X.CurrentTime) + + def _on_fake_event(self, key, is_press): + """The handler for fake press events sent by the controllers. + + :param KeyCode key: The key pressed. + + :param bool is_press: Whether this is a press event. + """ + (self.on_press if is_press else self.on_release)( + self._SPECIAL_KEYS.get(key.vk, key)) + + def _keycode_to_keysym(self, display, keycode, index): + """Converts a keycode and shift state index to a keysym. + + This method uses a simplified version of the *X* convention to locate + the correct keysym in the display table: since this method is only used + to locate special keys, alphanumeric keys are not treated specially. + + :param display: The current *X* display. + + :param keycode: The keycode. + + :param index: The shift state index. + + :return: a keysym + """ + keysym = display.keycode_to_keysym(keycode, index) + if keysym: + return keysym + elif index & 0x2: + return self._keycode_to_keysym(display, keycode, index & ~0x2) + elif index & 0x1: + return self._keycode_to_keysym(display, keycode, index & ~0x1) + else: + return 0 + + def _event_to_key(self, display, event): + """Converts an *X* event to a :class:`KeyCode`. + + :param display: The current *X* display. + + :param event: The event to convert. + + :return: a :class:`pynput.keyboard.KeyCode` + + :raises IndexError: if the key code is invalid + """ + keycode = event.detail + index = shift_to_index(display, event.state) + + # First try special keys... + keysym = self._keycode_to_keysym(display, keycode, index) + if keysym in self._SPECIAL_KEYS: + return self._SPECIAL_KEYS[keysym] + elif keysym in self._KEYPAD_KEYS: + # We must recalculate the index if numlock is active; index 1 is the + # one to use + try: + return self._KEYPAD_KEYS[ + self._keycode_to_keysym( + display, + keycode, + bool(event.state & numlock_mask(display)))] + except KeyError: + # Since we recalculated the key, this may happen + pass + + # ...then try characters... + name = KEYSYMS.get(keysym, None) + if name is not None and name in SYMBOLS: + char = SYMBOLS[name][1].upper() if index & 1 else SYMBOLS[name][1] + if char in DEAD_KEYS: + return KeyCode.from_dead(DEAD_KEYS[char], vk=keysym) + else: + return KeyCode.from_char(char, vk=keysym) + + # ...and fall back on a virtual key code + return KeyCode.from_vk(keysym) diff --git a/pynput/mouse/__init__.py b/pynput/mouse/__init__.py new file mode 100644 index 0000000..e26edcd --- /dev/null +++ b/pynput/mouse/__init__.py @@ -0,0 +1,98 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +The module containing mouse classes. + +See the documentation for more information. +""" + +# pylint: disable=C0103 +# Button, Controller and Listener are not constants + +from pynput._util import backend, Events + + +backend = backend(__name__) +Button = backend.Button +Controller = backend.Controller +Listener = backend.Listener +del backend + + +class Events(Events): + """A mouse event listener supporting synchronous iteration over the events. + + Possible events are: + + :class:`Events.Move` + The mouse was moved. + + :class:`Events.Click` + A mouse button was pressed or released. + + :class:`Events.Scroll` + The device was scrolled. + """ + _Listener = Listener + + class Move(Events.Event): + """A move event. + """ + def __init__(self, x, y): + #: The X screen coordinate. + self.x = x + + #: The Y screen coordinate. + self.y = y + + class Click(Events.Event): + """A click event. + """ + def __init__(self, x, y, button, pressed): + #: The X screen coordinate. + self.x = x + + #: The Y screen coordinate. + self.y = y + + #: The button. + self.button = button + + #: Whether the button was pressed. + self.pressed = pressed + + class Scroll(Events.Event): + """A scroll event. + """ + def __init__(self, x, y, dx, dy): + #: The X screen coordinate. + self.x = x + + #: The Y screen coordinate. + self.y = y + + #: The number of horisontal steps. + self.dx = dx + + #: The number of vertical steps. + self.dy = dy + + def __init__(self): + super(Events, self).__init__( + on_move=self.Move, + on_click=self.Click, + on_scroll=self.Scroll) diff --git a/pynput/mouse/__pycache__/__init__.cpython-38.pyc b/pynput/mouse/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..435b9a0d54083ba42d9d1e78ad70e1cadc9c579d GIT binary patch literal 2132 zcmcIl&5q+l5VqStaXb??47(>?r`^o#a-dt zLYsSIEB4pp@Vjdf%__bqL^P?&I?KzvdzNW50kg~aC_NII<~d}&?TJmB3IL2~rca;@YTX@=6l0ojMPWu3Q~ z35qBxa#f2`$Ve^b^Geo~xmuNzYgv^5Aj)eYO{UcnS_FVLBt#;tZz`qoOCV>YQ6|Ni zd2^WZyLr|ucLRL(tbc>pZgBIZy|pbz0+4> z|1a?QCe0DK+TnMK-2@lqZV^wNLX)Z@klLy-qql5|8J(g_rs$EW$L(M61)@%g*(i3B zPE}$!mLdtj=w3{^;0&Iz-*~ukh13_b84~J#N!61SLTu}1y0e>Z3UhphFigA3Yl|PD zrt}xn4sDlyf35GeAZ+I~?#W|v*(356k$WIb@Kt;9!~2%ip){U$phs~Zj6SaEwSi1T zQEY?J5z(C}b!7;n5AY~D6z$k^*-3cPKj~}dqc||fADg>qkS^-HkTik@2eL~1&-Cry ngctZ4)13{J-XoQL7wm9of=|r_^oT}HQG#x_?*z85f6VzCQP$51 literal 0 HcmV?d00001 diff --git a/pynput/mouse/__pycache__/_base.cpython-38.pyc b/pynput/mouse/__pycache__/_base.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f32a18cc7a510b8b6b6de8baa57dc5ba4c6a85f GIT binary patch literal 9076 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 P-LmG*ncA@v^Pl_&ptHXS literal 0 HcmV?d00001 diff --git a/pynput/mouse/__pycache__/_xorg.cpython-38.pyc b/pynput/mouse/__pycache__/_xorg.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cca865bd53449bdb1b71f27c9bd5daa1a1ca2ee8 GIT binary patch literal 5279 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 E1zzdDl>h($ literal 0 HcmV?d00001 diff --git a/pynput/mouse/_base.py b/pynput/mouse/_base.py new file mode 100644 index 0000000..1087ab9 --- /dev/null +++ b/pynput/mouse/_base.py @@ -0,0 +1,263 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +This module contains the base implementation. + +The actual interface to mouse classes is defined here, but the implementation +is located in a platform dependent module. +""" + +# pylint: disable=R0903 +# We implement stubs + +import enum + +from pynput._util import AbstractListener, prefix +from pynput import _logger + + +class Button(enum.Enum): + """The various buttons. + + The actual values for these items differ between platforms. Some + platforms may have additional buttons, but these are guaranteed to be + present everywhere. + """ + #: An unknown button was pressed + unknown = 0 + + #: The left button + left = 1 + + #: The middle button + middle = 2 + + #: The right button + right = 3 + + +class Controller(object): + """A controller for sending virtual mouse events to the system. + """ + def __init__(self): + self._log = _logger(self.__class__) + + @property + def position(self): + """The current position of the mouse pointer. + + This is the tuple ``(x, y)``, and setting it will move the pointer. + """ + return self._position_get() + + @position.setter + def position(self, pos): + self._position_set(pos) + + def scroll(self, dx, dy): + """Sends scroll events. + + :param int dx: The horizontal scroll. The units of scrolling is + undefined. + + :param int dy: The vertical scroll. The units of scrolling is + undefined. + + :raises ValueError: if the values are invalid, for example out of + bounds + """ + self._scroll(dx, dy) + + def press(self, button): + """Emits a button press event at the current position. + + :param Button button: The button to press. + """ + self._press(button) + + def release(self, button): + """Emits a button release event at the current position. + + :param Button button: The button to release. + """ + self._release(button) + + def move(self, dx, dy): + """Moves the mouse pointer a number of pixels from its current + position. + + :param int dx: The horizontal offset. + + :param int dy: The vertical offset. + + :raises ValueError: if the values are invalid, for example out of + bounds + """ + self.position = tuple(sum(i) for i in zip(self.position, (dx, dy))) + + def click(self, button, count=1): + """Emits a button click event at the current position. + + The default implementation sends a series of press and release events. + + :param Button button: The button to click. + + :param int count: The number of clicks to send. + """ + with self as controller: + for _ in range(count): + controller.press(button) + controller.release(button) + + def __enter__(self): + """Begins a series of clicks. + + In the default :meth:`click` implementation, the return value of this + method is used for the calls to :meth:`press` and :meth:`release` + instead of ``self``. + + The default implementation is a no-op. + """ + return self + + def __exit__(self, exc_type, value, traceback): + """Ends a series of clicks. + """ + pass + + def _position_get(self): + """The implementation of the getter for :attr:`position`. + + This is a platform dependent implementation. + """ + raise NotImplementedError() + + def _position_set(self, pos): + """The implementation of the setter for :attr:`position`. + + This is a platform dependent implementation. + """ + raise NotImplementedError() + + def _scroll(self, dx, dy): + """The implementation of the :meth:`scroll` method. + + This is a platform dependent implementation. + """ + raise NotImplementedError() + + def _press(self, button): + """The implementation of the :meth:`press` method. + + This is a platform dependent implementation. + """ + raise NotImplementedError() + + def _release(self, button): + """The implementation of the :meth:`release` method. + + This is a platform dependent implementation. + """ + raise NotImplementedError() + + +# pylint: disable=W0223; This is also an abstract class +class Listener(AbstractListener): + """A listener for mouse events. + + Instances of this class can be used as context managers. This is equivalent + to the following code:: + + listener.start() + try: + listener.wait() + with_statements() + finally: + listener.stop() + + This class inherits from :class:`threading.Thread` and supports all its + methods. It will set :attr:`daemon` to ``True`` when created. + + :param callable on_move: The callback to call when mouse move events occur. + + It will be called with the arguments ``(x, y)``, which is the new + pointer position. If this callback raises :class:`StopException` or + returns ``False``, the listener is stopped. + + :param callable on_click: The callback to call when a mouse button is + clicked. + + It will be called with the arguments ``(x, y, button, pressed)``, + where ``(x, y)`` is the new pointer position, ``button`` is one of the + :class:`Button` values and ``pressed`` is whether the button was + pressed. + + If this callback raises :class:`StopException` or returns ``False``, + the listener is stopped. + + :param callable on_scroll: The callback to call when mouse scroll + events occur. + + It will be called with the arguments ``(x, y, dx, dy)``, where + ``(x, y)`` is the new pointer position, and ``(dx, dy)`` is the scroll + vector. + + If this callback raises :class:`StopException` or returns ``False``, + the listener is stopped. + + :param bool suppress: Whether to suppress events. Setting this to ``True`` + will prevent the input events from being passed to the rest of the + system. + + :param kwargs: Any non-standard platform dependent options. These should be + prefixed with the platform name thus: ``darwin_``, ``xorg_`` or + ``win32_``. + + Supported values are: + + ``darwin_intercept`` + A callable taking the arguments ``(event_type, event)``, where + ``event_type`` is any mouse related event type constant, and + ``event`` is a ``CGEventRef``. + + This callable can freely modify the event using functions like + ``Quartz.CGEventSetIntegerValueField``. If this callable does not + return the event, the event is suppressed system wide. + + ``win32_event_filter`` + A callable taking the arguments ``(msg, data)``, where ``msg`` is + the current message, and ``data`` associated data as a + `MSLLHOOKSTRUCT `_. + + If this callback returns ``False``, the event will not + be propagated to the listener callback. + + If ``self.suppress_event()`` is called, the event is suppressed + system wide. + """ + def __init__(self, on_move=None, on_click=None, on_scroll=None, + suppress=False, **kwargs): + self._log = _logger(self.__class__) + option_prefix = prefix(Listener, self.__class__) + self._options = { + key[len(option_prefix):]: value + for key, value in kwargs.items() + if key.startswith(option_prefix)} + super(Listener, self).__init__( + on_move=on_move, on_click=on_click, on_scroll=on_scroll, + suppress=suppress) +# pylint: enable=W0223 diff --git a/pynput/mouse/_darwin.py b/pynput/mouse/_darwin.py new file mode 100644 index 0000000..78d7d7a --- /dev/null +++ b/pynput/mouse/_darwin.py @@ -0,0 +1,212 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +The mouse implementation for *macOS*. +""" + +# pylint: disable=C0111 +# The documentation is extracted from the base classes + +# pylint: disable=R0903 +# We implement stubs + +import enum +import Quartz + +from AppKit import NSEvent + +from pynput._util.darwin import ( + ListenerMixin) +from . import _base + + +def _button_value(base_name, mouse_button): + """Generates the value tuple for a :class:`Button` value. + + :param str base_name: The base name for the button. This should be a string + like ``'kCGEventLeftMouse'``. + + :param int mouse_button: The mouse button ID. + + :return: a value tuple + """ + return ( + tuple( + getattr(Quartz, '%sMouse%s' % (base_name, name)) + for name in ('Down', 'Up', 'Dragged')), + mouse_button) + + +class Button(enum.Enum): + """The various buttons. + """ + unknown = None + left = _button_value('kCGEventLeft', 0) + middle = _button_value('kCGEventOther', 2) + right = _button_value('kCGEventRight', 1) + + +class Controller(_base.Controller): + #: The scroll speed + _SCROLL_SPEED = 10 + + def __init__(self, *args, **kwargs): + super(Controller, self).__init__(*args, **kwargs) + self._click = None + self._drag_button = None + + def _position_get(self): + pos = NSEvent.mouseLocation() + + return pos.x, Quartz.CGDisplayPixelsHigh(0) - pos.y + + def _position_set(self, pos): + try: + (_, _, mouse_type), mouse_button = self._drag_button.value + except AttributeError: + mouse_type = Quartz.kCGEventMouseMoved + mouse_button = 0 + + Quartz.CGEventPost( + Quartz.kCGHIDEventTap, + Quartz.CGEventCreateMouseEvent( + None, + mouse_type, + pos, + mouse_button)) + + def _scroll(self, dx, dy): + dx = int(dx) + dy = int(dy) + + Quartz.CGEventPost( + Quartz.kCGHIDEventTap, + Quartz.CGEventCreateScrollWheelEvent( + None, + Quartz.kCGScrollEventUnitPixel, + 2, + dy * self._SCROLL_SPEED, + dx * self._SCROLL_SPEED)) + + def _press(self, button): + (press, _, _), mouse_button = button.value + event = Quartz.CGEventCreateMouseEvent( + None, + press, + self.position, + mouse_button) + + # If we are performing a click, we need to set this state flag + if self._click is not None: + self._click += 1 + Quartz.CGEventSetIntegerValueField( + event, + Quartz.kCGMouseEventClickState, + self._click) + + Quartz.CGEventPost(Quartz.kCGHIDEventTap, event) + + # Store the button to enable dragging + self._drag_button = button + + def _release(self, button): + (_, release, _), mouse_button = button.value + event = Quartz.CGEventCreateMouseEvent( + None, + release, + self.position, + mouse_button) + + # If we are performing a click, we need to set this state flag + if self._click is not None: + Quartz.CGEventSetIntegerValueField( + event, + Quartz.kCGMouseEventClickState, + self._click) + + Quartz.CGEventPost(Quartz.kCGHIDEventTap, event) + + if button == self._drag_button: + self._drag_button = None + + def __enter__(self): + self._click = 0 + return self + + def __exit__(self, exc_type, value, traceback): + self._click = None + + +class Listener(ListenerMixin, _base.Listener): + #: The events that we listen to + _EVENTS = ( + Quartz.CGEventMaskBit(Quartz.kCGEventMouseMoved) | + Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseDown) | + Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseUp) | + Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseDragged) | + Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseDown) | + Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseUp) | + Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseDragged) | + Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseDown) | + Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseUp) | + Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseDragged) | + Quartz.CGEventMaskBit(Quartz.kCGEventScrollWheel)) + + def __init__(self, *args, **kwargs): + super(Listener, self).__init__(*args, **kwargs) + self._intercept = self._options.get( + 'intercept', + None) + + def _handle(self, _proxy, event_type, event, _refcon): + """The callback registered with *macOS* for mouse events. + + This method will call the callbacks registered on initialisation. + """ + try: + (px, py) = Quartz.CGEventGetLocation(event) + except AttributeError: + # This happens during teardown of the virtual machine + return + + # Quickly detect the most common event type + if event_type == Quartz.kCGEventMouseMoved: + self.on_move(px, py) + + elif event_type == Quartz.kCGEventScrollWheel: + dx = Quartz.CGEventGetIntegerValueField( + event, + Quartz.kCGScrollWheelEventDeltaAxis2) + dy = Quartz.CGEventGetIntegerValueField( + event, + Quartz.kCGScrollWheelEventDeltaAxis1) + self.on_scroll(px, py, dx, dy) + + else: + for button in Button: + try: + (press, release, drag), _ = button.value + except TypeError: + # Button.unknown cannot be enumerated + continue + + # Press and release generate click events, and drag + # generates move events + if event_type in (press, release): + self.on_click(px, py, button, event_type == press) + elif event_type == drag: + self.on_move(px, py) diff --git a/pynput/mouse/_dummy.py b/pynput/mouse/_dummy.py new file mode 100644 index 0000000..56a9332 --- /dev/null +++ b/pynput/mouse/_dummy.py @@ -0,0 +1,22 @@ +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +This module contains a dummy implementation. + +It cannot be used, but importing it will not raise any exceptions. +""" + +from ._base import Button, Controller, Listener diff --git a/pynput/mouse/_win32.py b/pynput/mouse/_win32.py new file mode 100644 index 0000000..d0a37de --- /dev/null +++ b/pynput/mouse/_win32.py @@ -0,0 +1,221 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +The mouse implementation for *Windows*. +""" + +# pylint: disable=C0111 +# The documentation is extracted from the base classes + +# pylint: disable=R0903 +# We implement stubs + +import ctypes +import enum + +from ctypes import ( + windll, + wintypes) + +from pynput._util import NotifierMixin +from pynput._util.win32 import ( + INPUT, + INPUT_union, + ListenerMixin, + MOUSEINPUT, + SendInput, + SystemHook) +from . import _base + +#: A constant used as a factor when constructing mouse scroll data. +WHEEL_DELTA = 120 + + +class Button(enum.Enum): + """The various buttons. + """ + unknown = None + left = (MOUSEINPUT.LEFTUP, MOUSEINPUT.LEFTDOWN, 0) + middle = (MOUSEINPUT.MIDDLEUP, MOUSEINPUT.MIDDLEDOWN, 0) + right = (MOUSEINPUT.RIGHTUP, MOUSEINPUT.RIGHTDOWN, 0) + x1 = (MOUSEINPUT.XUP, MOUSEINPUT.XDOWN, MOUSEINPUT.XBUTTON1) + x2 = (MOUSEINPUT.XUP, MOUSEINPUT.XDOWN, MOUSEINPUT.XBUTTON2) + + +class Controller(NotifierMixin, _base.Controller): + __GetCursorPos = windll.user32.GetCursorPos + __SetCursorPos = windll.user32.SetCursorPos + + def __init__(self, *args, **kwargs): + super(Controller, self).__init__(*args, **kwargs) + + def _position_get(self): + point = wintypes.POINT() + if self.__GetCursorPos(ctypes.byref(point)): + return (point.x, point.y) + else: + return None + + def _position_set(self, pos): + pos = int(pos[0]), int(pos[1]) + self.__SetCursorPos(*pos) + self._emit('on_move', *pos) + + def _scroll(self, dx, dy): + if dy: + SendInput( + 1, + ctypes.byref(INPUT( + type=INPUT.MOUSE, + value=INPUT_union( + mi=MOUSEINPUT( + dwFlags=MOUSEINPUT.WHEEL, + mouseData=int(dy * WHEEL_DELTA))))), + ctypes.sizeof(INPUT)) + + if dx: + SendInput( + 1, + ctypes.byref(INPUT( + type=INPUT.MOUSE, + value=INPUT_union( + mi=MOUSEINPUT( + dwFlags=MOUSEINPUT.HWHEEL, + mouseData=int(dx * WHEEL_DELTA))))), + ctypes.sizeof(INPUT)) + + if dx or dy: + px, py = self._position_get() + self._emit('on_scroll', px, py, dx, dy) + + def _press(self, button): + SendInput( + 1, + ctypes.byref(INPUT( + type=INPUT.MOUSE, + value=INPUT_union( + mi=MOUSEINPUT( + dwFlags=button.value[1], + mouseData=button.value[2])))), + ctypes.sizeof(INPUT)) + + def _release(self, button): + SendInput( + 1, + ctypes.byref(INPUT( + type=INPUT.MOUSE, + value=INPUT_union( + mi=MOUSEINPUT( + dwFlags=button.value[0], + mouseData=button.value[2])))), + ctypes.sizeof(INPUT)) + + +@Controller._receiver +class Listener(ListenerMixin, _base.Listener): + #: The Windows hook ID for low level mouse events, ``WH_MOUSE_LL`` + _EVENTS = 14 + + WM_LBUTTONDOWN = 0x0201 + WM_LBUTTONUP = 0x0202 + WM_MBUTTONDOWN = 0x0207 + WM_MBUTTONUP = 0x0208 + WM_MOUSEMOVE = 0x0200 + WM_MOUSEWHEEL = 0x020A + WM_MOUSEHWHEEL = 0x020E + WM_RBUTTONDOWN = 0x0204 + WM_RBUTTONUP = 0x0205 + WM_XBUTTONDOWN = 0x20B + WM_XBUTTONUP = 0x20C + + MK_XBUTTON1 = 0x0020 + MK_XBUTTON2 = 0x0040 + + XBUTTON1 = 1 + XBUTTON2 = 2 + + #: A mapping from messages to button events + CLICK_BUTTONS = { + WM_LBUTTONDOWN: (Button.left, True), + WM_LBUTTONUP: (Button.left, False), + WM_MBUTTONDOWN: (Button.middle, True), + WM_MBUTTONUP: (Button.middle, False), + WM_RBUTTONDOWN: (Button.right, True), + WM_RBUTTONUP: (Button.right, False)} + + #: A mapping from message to X button events. + X_BUTTONS = { + WM_XBUTTONDOWN: { + XBUTTON1: (Button.x1, True), + XBUTTON2: (Button.x2, True)}, + WM_XBUTTONUP: { + XBUTTON1: (Button.x1, False), + XBUTTON2: (Button.x2, False)}} + + #: A mapping from messages to scroll vectors + SCROLL_BUTTONS = { + WM_MOUSEWHEEL: (0, 1), + WM_MOUSEHWHEEL: (1, 0)} + + _HANDLED_EXCEPTIONS = ( + SystemHook.SuppressException,) + + class _MSLLHOOKSTRUCT(ctypes.Structure): + """Contains information about a mouse event passed to a ``WH_MOUSE_LL`` + hook procedure, ``MouseProc``. + """ + _fields_ = [ + ('pt', wintypes.POINT), + ('mouseData', wintypes.DWORD), + ('flags', wintypes.DWORD), + ('time', wintypes.DWORD), + ('dwExtraInfo', ctypes.c_void_p)] + + #: A pointer to a :class:`_MSLLHOOKSTRUCT` + _LPMSLLHOOKSTRUCT = ctypes.POINTER(_MSLLHOOKSTRUCT) + + def __init__(self, *args, **kwargs): + super(Listener, self).__init__(*args, **kwargs) + self._event_filter = self._options.get( + 'event_filter', + lambda msg, data: True) + + def _handle(self, code, msg, lpdata): + if code != SystemHook.HC_ACTION: + return + + data = ctypes.cast(lpdata, self._LPMSLLHOOKSTRUCT).contents + + # Suppress further propagation of the event if it is filtered + if self._event_filter(msg, data) is False: + return + + if msg == self.WM_MOUSEMOVE: + self.on_move(data.pt.x, data.pt.y) + + elif msg in self.CLICK_BUTTONS: + button, pressed = self.CLICK_BUTTONS[msg] + self.on_click(data.pt.x, data.pt.y, button, pressed) + + elif msg in self.X_BUTTONS: + button, pressed = self.X_BUTTONS[msg][data.mouseData >> 16] + self.on_click(data.pt.x, data.pt.y, button, pressed) + + elif msg in self.SCROLL_BUTTONS: + mx, my = self.SCROLL_BUTTONS[msg] + dd = wintypes.SHORT(data.mouseData >> 16).value // WHEEL_DELTA + self.on_scroll(data.pt.x, data.pt.y, dd * mx, dd * my) diff --git a/pynput/mouse/_xorg.py b/pynput/mouse/_xorg.py new file mode 100644 index 0000000..18119c0 --- /dev/null +++ b/pynput/mouse/_xorg.py @@ -0,0 +1,181 @@ +# coding=utf-8 +# pynput +# Copyright (C) 2015-2022 Moses Palmér +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +""" +The keyboard implementation for *Xorg*. +""" + +# pylint: disable=C0111 +# The documentation is extracted from the base classes + + +# pylint: disable=E1101,E1102 +# We dynamically generate the Button class + +# pylint: disable=R0903 +# We implement stubs + +# pylint: disable=W0611 +try: + import pynput._util.xorg +except Exception as e: + raise ImportError('failed to acquire X connection: {}'.format(str(e)), e) +# pylint: enable=W0611 + +import enum +import Xlib.display +import Xlib.ext +import Xlib.ext.xtest +import Xlib.X +import Xlib.protocol + +from pynput._util.xorg import ( + display_manager, + ListenerMixin) +from . import _base + + +# pylint: disable=C0103 +Button = enum.Enum( + 'Button', + module=__name__, + names=[ + ('unknown', None), + ('left', 1), + ('middle', 2), + ('right', 3), + ('scroll_up', 4), + ('scroll_down', 5), + ('scroll_left', 6), + ('scroll_right', 7)] + [ + ('button%d' % i, i) + for i in range(8, 31)]) +# pylint: enable=C0103 + + +class Controller(_base.Controller): + def __init__(self, *args, **kwargs): + super(Controller, self).__init__(*args, **kwargs) + self._display = Xlib.display.Display() + + def __del__(self): + if hasattr(self, '_display'): + self._display.close() + + def _position_get(self): + with display_manager(self._display) as dm: + qp = dm.screen().root.query_pointer() + return (qp.root_x, qp.root_y) + + def _position_set(self, pos): + px, py = self._check_bounds(*pos) + with display_manager(self._display) as dm: + Xlib.ext.xtest.fake_input(dm, Xlib.X.MotionNotify, x=px, y=py) + + def _scroll(self, dx, dy): + dx, dy = self._check_bounds(dx, dy) + if dy: + self.click( + button=Button.scroll_up if dy > 0 else Button.scroll_down, + count=abs(dy)) + + if dx: + self.click( + button=Button.scroll_right if dx > 0 else Button.scroll_left, + count=abs(dx)) + + def _press(self, button): + with display_manager(self._display) as dm: + Xlib.ext.xtest.fake_input(dm, Xlib.X.ButtonPress, button.value) + + def _release(self, button): + with display_manager(self._display) as dm: + Xlib.ext.xtest.fake_input(dm, Xlib.X.ButtonRelease, button.value) + + def _check_bounds(self, *args): + """Checks the arguments and makes sure they are within the bounds of a + short integer. + + :param args: The values to verify. + """ + if not all( + (-0x7fff - 1) <= number <= 0x7fff + for number in args): + raise ValueError(args) + else: + return tuple(int(p) for p in args) + + +class Listener(ListenerMixin, _base.Listener): + #: A mapping from button values to scroll directions + _SCROLL_BUTTONS = { + Button.scroll_up.value: (0, 1), + Button.scroll_down.value: (0, -1), + Button.scroll_right.value: (1, 0), + Button.scroll_left.value: (-1, 0)} + + _EVENTS = ( + Xlib.X.ButtonPressMask, + Xlib.X.ButtonReleaseMask) + + def __init__(self, *args, **kwargs): + super(Listener, self).__init__(*args, **kwargs) + + def _handle(self, dummy_display, event): + px = event.root_x + py = event.root_y + + if event.type == Xlib.X.ButtonPress: + # Scroll events are sent as button presses with the scroll + # button codes + scroll = self._SCROLL_BUTTONS.get(event.detail, None) + if scroll: + self.on_scroll(px, py, *scroll) + else: + self.on_click(px, py, self._button(event.detail), True) + + elif event.type == Xlib.X.ButtonRelease: + # Send an event only if this was not a scroll event + if event.detail not in self._SCROLL_BUTTONS: + self.on_click(px, py, self._button(event.detail), False) + + else: + self.on_move(px, py) + + + def _suppress_start(self, display): + display.screen().root.grab_pointer( + True, self._event_mask, Xlib.X.GrabModeAsync, Xlib.X.GrabModeAsync, + 0, 0, Xlib.X.CurrentTime) + + def _suppress_stop(self, display): + display.ungrab_pointer(Xlib.X.CurrentTime) + + # pylint: disable=R0201 + def _button(self, detail): + """Creates a mouse button from an event detail. + + If the button is unknown, :attr:`Button.unknown` is returned. + + :param detail: The event detail. + + :return: a button + """ + try: + return Button(detail) + except ValueError: + return Button.unknown + # pylint: enable=R0201