From dd7326d62df858ad2d193cadf8f9b3f4adf76c00 Mon Sep 17 00:00:00 2001 From: "David H. Bronke" Date: Mon, 21 May 2012 05:46:59 -0500 Subject: [PATCH 1/9] Corrected a bug which prevented set_wm_pid or set_wm_pid_checked from being usable. --- xpybutil/ewmh.py | 105 ++++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/xpybutil/ewmh.py b/xpybutil/ewmh.py index d3f51e0..ad1b18e 100644 --- a/xpybutil/ewmh.py +++ b/xpybutil/ewmh.py @@ -2,8 +2,8 @@ Implements the entire EWMH spec. The spec can be found here: http://standards.freedesktop.org/wm-spec/wm-spec-latest.html -xpybutil's primary purpose was to make accessing ICCCM and EWMH related -information extremely easy. This can be done by using the ewmh or icccm +xpybutil's primary purpose was to make accessing ICCCM and EWMH related +information extremely easy. This can be done by using the ewmh or icccm modules. Here's a quick example (assuming xpybutil is installed): :: @@ -15,19 +15,19 @@ print names.get(current_desktop, current_desktop) -This imports the ewmh module (and by extension, connects to the X server) and -fetches a list of the current desktop names and the current desktop index -(starting from 0). Since not every desktop must be named, the desktop index is +This imports the ewmh module (and by extension, connects to the X server) and +fetches a list of the current desktop names and the current desktop index +(starting from 0). Since not every desktop must be named, the desktop index is printed if it has no name. -Note that the functions in the ewmh and icccm module return *cookies*. In order -to pull a response from the X server, call the 'reply()' method on a cookie -object. To force a response sent to the X server, call the 'check()' method on +Note that the functions in the ewmh and icccm module return *cookies*. In order +to pull a response from the X server, call the 'reply()' method on a cookie +object. To force a response sent to the X server, call the 'check()' method on the corresponding cookie. -Much of the EWMH and ICCCM modules encapsulate packing data from convenient -Python data types into C structs (using the 'struct' Python module). For -example, if one wants to fetch the partial struts set by some window with +Much of the EWMH and ICCCM modules encapsulate packing data from convenient +Python data types into C structs (using the 'struct' Python module). For +example, if one wants to fetch the partial struts set by some window with identifier ID, you could do: :: @@ -36,37 +36,37 @@ print ewmh.get_wm_strut_partial(ID).reply() -Which outputs a dictionary with 12 entries, where each corresponds to a value +Which outputs a dictionary with 12 entries, where each corresponds to a value in the partial strut. (i.e., 'left', 'top_end_x', 'right_start_y', etc...). -In general, particularly with the EWMH module, the ewmh module is very nearly -representative of the spec itself. Functions that get property values start -with ``get_``, functions that set property values start with ``set_``, and -functions that send an event to a client (which typically requests the window +In general, particularly with the EWMH module, the ewmh module is very nearly +representative of the spec itself. Functions that get property values start +with ``get_``, functions that set property values start with ``set_``, and +functions that send an event to a client (which typically requests the window manager to DO something) start with ``request_``. -If a request has no reply (typically the ``set_`` functions), then the -default is to call it 'unchecked'. If you want to check the result (and force +If a request has no reply (typically the ``set_`` functions), then the +default is to call it 'unchecked'. If you want to check the result (and force retrieval), then use the '_checked' variant. -The reverse goes for the ``get_`` functions. By default, they are checked, but +The reverse goes for the ``get_`` functions. By default, they are checked, but you can use the '_unchecked' variant too. -Basically, you should probably almost always use the 'checked' variant of -everything. The cases where you don't are when you want to send a whole bunch -of requests to the X server at once. You could use the unchecked invariant, and -after you've initialized all the cookies, calling 'flush' will force +Basically, you should probably almost always use the 'checked' variant of +everything. The cases where you don't are when you want to send a whole bunch +of requests to the X server at once. You could use the unchecked invariant, and +after you've initialized all the cookies, calling 'flush' will force communication with the X server. -Finally, unless you're writing a window manager or creating a client window -from scratch, you'll almost always want to use the ``get_`` and ``request_`` +Finally, unless you're writing a window manager or creating a client window +from scratch, you'll almost always want to use the ``get_`` and ``request_`` functions. For exampe, if you want to change the current desktop... DON'T DO: ``ewmh.get_current_desktop_checked(2).check()`` DO: ``ewmh.request_current_desktop_checked(2).check()`` -The former will probably not work, but the latter will. Just follow the EWMH +The former will probably not work, but the latter will. Just follow the EWMH spec :-) """ import struct @@ -191,7 +191,7 @@ def get_supported(): return util.PropertyCookie(util.get_property(root, '_NET_SUPPORTED')) def get_supported_unchecked(): - return util.PropertyCookie(util.get_property_unchecked(root, + return util.PropertyCookie(util.get_property_unchecked(root, '_NET_SUPPORTED')) def set_supported(atoms): @@ -225,7 +225,7 @@ def get_client_list(): return util.PropertyCookie(util.get_property(root, '_NET_CLIENT_LIST')) def get_client_list_unchecked(): - return util.PropertyCookie(util.get_property_unchecked(root, + return util.PropertyCookie(util.get_property_unchecked(root, '_NET_CLIENT_LIST')) def set_client_list(windows): @@ -256,7 +256,7 @@ def get_client_list_stacking(): :return: A list of window identifiers. :rtype: util.PropertyCookie (ATOM[]/32) """ - return util.PropertyCookie(util.get_property(root, + return util.PropertyCookie(util.get_property(root, '_NET_CLIENT_LIST_STACKING')) def get_client_list_stacking_unchecked(): @@ -308,7 +308,7 @@ def set_number_of_desktops(number_of_desktops): """ packed = struct.pack('I', number_of_desktops) return c.core.ChangeProperty(xcb.xproto.PropMode.Replace, root, - atom('_NET_NUMBER_OF_DESKTOPS'), CARDINAL, 32, + atom('_NET_NUMBER_OF_DESKTOPS'), CARDINAL, 32, 1, packed) def set_number_of_desktops_checked(number_of_desktops): @@ -351,7 +351,7 @@ def get_desktop_geometry(): Keys: width, height :rtype: DesktopGeometryCookie (CARDINAL[2]/32) """ - return DesktopGeometryCookie(util.get_property(root, + return DesktopGeometryCookie(util.get_property(root, '_NET_DESKTOP_GEOMETRY')) def get_desktop_geometry_unchecked(): @@ -421,7 +421,7 @@ def get_desktop_viewport(): Keys: x, y :rtype: DesktopViewportCookie (CARDINAL[][2]/32) """ - return DesktopViewportCookie(util.get_property(root, + return DesktopViewportCookie(util.get_property(root, '_NET_DESKTOP_VIEWPORT')) def get_desktop_viewport_unchecked(): @@ -516,7 +516,7 @@ def set_current_desktop(current_desktop): """ packed = struct.pack('I', current_desktop) return c.core.ChangeProperty(xcb.xproto.PropMode.Replace, root, - atom('_NET_CURRENT_DESKTOP'), CARDINAL, 32, 1, + atom('_NET_CURRENT_DESKTOP'), CARDINAL, 32, 1, packed) def set_current_desktop_checked(current_desktop): @@ -634,7 +634,7 @@ def get_active_window(): :return: The window ID of the active window. :rtype: util.PropertyCookieSingle (WINDOW/32) """ - return util.PropertyCookieSingle(util.get_property(root, + return util.PropertyCookieSingle(util.get_property(root, '_NET_ACTIVE_WINDOW')) def get_active_window_unchecked(): @@ -678,7 +678,7 @@ def request_active_window(active, source=1, def request_active_window_checked(active, source=1, timestamp=xcb.xproto.Time.CurrentTime, current=0): - return revent_checked(active, '_NET_ACTIVE_WINDOW', source, timestamp, + return revent_checked(active, '_NET_ACTIVE_WINDOW', source, timestamp, current) # _NET_WORKAREA @@ -1107,7 +1107,7 @@ def get_wm_name(window): return util.PropertyCookie(util.get_property(window, '_NET_WM_NAME')) def get_wm_name_unchecked(window): - return util.PropertyCookie(util.get_property_unchecked(window, + return util.PropertyCookie(util.get_property_unchecked(window, '_NET_WM_NAME')) def set_wm_name(window, wm_name): @@ -1138,7 +1138,7 @@ def get_wm_visible_name(window): :return: The window's visible title. :rtype: util.PropertyCookie (UTF8_STRING) """ - return util.PropertyCookie(util.get_property(window, + return util.PropertyCookie(util.get_property(window, '_NET_WM_VISIBLE_NAME')) def get_wm_visible_name_unchecked(window): @@ -1188,7 +1188,7 @@ def set_wm_icon_name(window, icon_name): :rtype: xcb.VoidCookie """ return c.core.ChangeProperty(xcb.xproto.PropMode.Replace, window, - atom('_NET_WM_ICON_NAME'), atom('UTF8_STRING'), + atom('_NET_WM_ICON_NAME'), atom('UTF8_STRING'), 8, len(icon_name), icon_name) def set_wm_icon_name_checked(window, icon_name): @@ -1207,7 +1207,7 @@ def get_wm_visible_icon_name(window): :return: The window's visible icon name. :rtype: util.PropertyCookie (UTF8_STRING) """ - return util.PropertyCookie(util.get_property(window, + return util.PropertyCookie(util.get_property(window, '_NET_WM_VISIBLE_ICON_NAME')) def get_wm_visible_icon_name_unchecked(window): @@ -1255,7 +1255,7 @@ def get_wm_window_opacity(window): :return: An opacity percentage between 0 and 1 (inclusive) :rtype: util.PropertyCookieSingle (CARDINAL/32) """ - return OpacityCookieSingle(util.get_property(window, + return OpacityCookieSingle(util.get_property(window, '_NET_WM_WINDOW_OPACITY')) def get_wm_window_opacity_unchecked(window): @@ -1299,7 +1299,7 @@ def get_wm_desktop(window): :return: The window's virtual desktop index. :rtype: util.PropertyCookieSingle (CARDINAL/32) """ - return util.PropertyCookieSingle(util.get_property(window, + return util.PropertyCookieSingle(util.get_property(window, '_NET_WM_DESKTOP')) def get_wm_desktop_unchecked(window): @@ -1388,7 +1388,7 @@ def get_wm_state(window): return util.PropertyCookie(util.get_property(window, '_NET_WM_STATE')) def get_wm_state_unchecked(window): - return util.PropertyCookie(util.get_property_unchecked(window, + return util.PropertyCookie(util.get_property_unchecked(window, '_NET_WM_STATE')) def set_wm_state(window, states): @@ -1431,7 +1431,7 @@ def request_wm_state(window, action, first, second=0, source=1): return revent(window, '_NET_WM_STATE', action, first, second, source) def request_wm_state_checked(window, action, first, second=0, source=1): - return revent_checked(window, '_NET_WM_STATE', + return revent_checked(window, '_NET_WM_STATE', action, first, second, source) # _NET_WM_ALLOWED_ACTIONS @@ -1445,7 +1445,7 @@ def get_wm_allowed_actions(window): actions through the window manager. :rtype: util.PropertyCookie (ATOM[]/32) """ - return util.PropertyCookie(util.get_property(window, + return util.PropertyCookie(util.get_property(window, '_NET_WM_ALLOWED_ACTIONS')) def get_wm_allowed_actions_unchecked(window): @@ -1564,7 +1564,7 @@ def get_wm_strut_partial(window): bottom_start_x, bottom_end_x :rtype: StrutPartialCookie (CARDINAL[12]/32) """ - return StrutPartialCookie(util.get_property(window, + return StrutPartialCookie(util.get_property(window, '_NET_WM_STRUT_PARTIAL')) def get_wm_strut_partial_unchecked(window): @@ -1640,7 +1640,7 @@ def get_wm_icon_geometry(window): Keys: x, y, width, height :rtype: IconGeometryCookie (CARDINAL[4]/32) """ - return IconGeometryCookie(util.get_property(window, + return IconGeometryCookie(util.get_property(window, '_NET_WM_ICON_GEOMETRY')) def get_wm_icon_geometry_unchecked(window): @@ -1773,13 +1773,16 @@ def set_wm_pid(window, pid): :type pid: CARDINAL/32 :rtype: xcb.VoidCookie """ + packed = struct.pack('I', pid) return c.core.ChangeProperty(xcb.xproto.PropMode.Replace, window, - atom('_NET_WM_PID'), CARDINAL, 32, 1, [pid]) + atom('_NET_WM_PID'), CARDINAL, 32, 1, + packed) def set_wm_pid_checked(window, pid): + packed = struct.pack('I', pid) return c.core.ChangePropertyChecked(xcb.xproto.PropMode.Replace, window, atom('_NET_WM_PID'), CARDINAL, 32, 1, - [pid]) + packed) # _NET_WM_HANDLED_ICONS @@ -1791,7 +1794,7 @@ def get_wm_handled_icons(window): :return: Whether this property is set or not. :rtype: util.PropertyCookieSingle (CARDINAL/32) """ - return util.PropertyCookieSingle(util.get_property(window, + return util.PropertyCookieSingle(util.get_property(window, '_NET_WM_HANDLED_ICONS')) def get_wm_handled_icons_unchecked(window): @@ -1825,7 +1828,7 @@ def get_wm_user_time(window): :return: The XServer time when user activity last occurred. :rtype: util.PropertyCookieSingle (CARDINAL/32) """ - return util.PropertyCookieSingle(util.get_property(window, + return util.PropertyCookieSingle(util.get_property(window, '_NET_WM_USER_TIME')) def get_wm_user_time_unchecked(window): @@ -1960,7 +1963,7 @@ def request_wm_ping(window, response=False, def request_wm_ping_checked(window, response=False, timestamp=xcb.xproto.Time.CurrentTime): - return revent_checked(window if not response else root(c), 'WM_PROTOCOLS', + return revent_checked(window if not response else root(c), 'WM_PROTOCOLS', atom('_NET_WM_PING'), timestamp, window) # _NET_WM_SYNC_REQUEST From b9394a2d80d248c3d0023db9b617266a2c8f38ad Mon Sep 17 00:00:00 2001 From: "David H. Bronke" Date: Mon, 21 May 2012 05:51:07 -0500 Subject: [PATCH 2/9] A dirty, dirty hack to allow WMs using xpybutil to actually get the MapRequestEvents they subscribe to. --- xpybutil/event.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xpybutil/event.py b/xpybutil/event.py index a0d79d9..638bf92 100644 --- a/xpybutil/event.py +++ b/xpybutil/event.py @@ -130,6 +130,8 @@ def main(): w = None if isinstance(e, xproto.MappingNotifyEvent): w = None + elif isinstance(e, xproto.MapRequestEvent): + w = root elif hasattr(e, 'window'): w = e.window elif hasattr(e, 'event'): From 99bceedd040041c082b4497308a21e4faf072591 Mon Sep 17 00:00:00 2001 From: "David H. Bronke" Date: Mon, 21 May 2012 20:14:43 -0500 Subject: [PATCH 3/9] Corrected atoms used for request_desktop_geometry{,_checked}. --- xpybutil/ewmh.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xpybutil/ewmh.py b/xpybutil/ewmh.py index ad1b18e..a8655ae 100644 --- a/xpybutil/ewmh.py +++ b/xpybutil/ewmh.py @@ -389,10 +389,10 @@ def request_desktop_geometry(width, height): :type height: CARDINAL/32 :rtype: xcb.VoidCookie """ - return revent(root, '_NET_NUMBER_OF_DESKTOPS', width, height) + return revent(root, '_NET_DESKTOP_GEOMETRY', width, height) def request_desktop_geometry_checked(width, height): - return revent_checked(root, '_NET_NUMBER_OF_DESKTOPS', width, height) + return revent_checked(root, '_NET_DESKTOP_GEOMETRY', width, height) # _NET_DESKTOP_VIEWPORT From f9edea172a451d55eae5bccbeb61a6228e733674 Mon Sep 17 00:00:00 2001 From: "David H. Bronke" Date: Sat, 26 May 2012 21:04:23 -0500 Subject: [PATCH 4/9] Added a special case for e.owner to event.main, in order to ensure that selection owners get SelectionRequest events when appropriate. --- xpybutil/event.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xpybutil/event.py b/xpybutil/event.py index 638bf92..348743a 100644 --- a/xpybutil/event.py +++ b/xpybutil/event.py @@ -136,6 +136,8 @@ def main(): w = e.window elif hasattr(e, 'event'): w = e.event + elif hasattr(e, 'owner'): + w = e.owner elif hasattr(e, 'requestor'): w = e.requestor From 96adc52868d7d5160b53beab2a59d4bd30d47ee5 Mon Sep 17 00:00:00 2001 From: "David H. Bronke" Date: Sun, 27 May 2012 00:17:58 -0500 Subject: [PATCH 5/9] Corrected docstring for the 'ewmh' module to contrast 'set_' (instead of 'get_') with 'request_'. --- xpybutil/ewmh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xpybutil/ewmh.py b/xpybutil/ewmh.py index a8655ae..59e56bd 100644 --- a/xpybutil/ewmh.py +++ b/xpybutil/ewmh.py @@ -62,7 +62,7 @@ from scratch, you'll almost always want to use the ``get_`` and ``request_`` functions. For exampe, if you want to change the current desktop... - DON'T DO: ``ewmh.get_current_desktop_checked(2).check()`` + DON'T DO: ``ewmh.set_current_desktop_checked(2).check()`` DO: ``ewmh.request_current_desktop_checked(2).check()`` From 0340d5ae60b1346e1decb3f5e3bce2d9e2650c96 Mon Sep 17 00:00:00 2001 From: "David H. Bronke" Date: Wed, 30 May 2012 23:29:27 -0500 Subject: [PATCH 6/9] Added the ability for callbacks in key bindings to act the same as callbacks for mouse bindings, actually getting the event object. Uses a try/except to fall back to the old behavior. --- xpybutil/keybind.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/xpybutil/keybind.py b/xpybutil/keybind.py index a508b35..4a35981 100644 --- a/xpybutil/keybind.py +++ b/xpybutil/keybind.py @@ -165,7 +165,7 @@ def get_keyboard_mapping(): def get_keyboard_mapping_unchecked(): """ - Return an unchecked keyboard mapping cookie that can be used to fetch the + Return an unchecked keyboard mapping cookie that can be used to fetch the table of keysyms in the current X environment. :rtype: xcb.xproto.GetKeyboardMappingCookie @@ -177,7 +177,7 @@ def get_keyboard_mapping_unchecked(): def get_keysym(keycode, col=0, kbmap=None): """ Get the keysym associated with a particular keycode in the current X - environment. Although we get a list of keysyms from X in + environment. Although we get a list of keysyms from X in 'get_keyboard_mapping', this list is really a table with 'keysys_per_keycode' columns and ``mx - mn`` rows (where ``mx`` is the maximum keycode and ``mn`` is the minimum keycode). @@ -187,7 +187,7 @@ def get_keysym(keycode, col=0, kbmap=None): In most cases, setting ``col`` to 0 will work. - Witness the utter complexity: + Witness the utter complexity: http://tronche.com/gui/x/xlib/input/keyboard-encoding.html You may also pass in your own keyboard mapping using the ``kbmap`` @@ -449,7 +449,7 @@ def __run_keybind_callbacks(e): their corresponding callback functions. Nothing much to see here, except that we must mask out the trivial modifiers from the state in order to find the right callback. - + Callbacks are called in the order that they have been added. (FIFO.) :param e: A Key{Press,Release} event. @@ -462,7 +462,10 @@ def __run_keybind_callbacks(e): key = (e.event, mods, kc) for cb in __keybinds.get(key, []): - cb() + try: + cb(e) + except TypeError: + cb() def __regrab(changes): """ From 3e1b10b8300aca165de25018c97fe698d8eb5425 Mon Sep 17 00:00:00 2001 From: "David H. Bronke" Date: Mon, 31 Dec 2012 03:40:51 -0600 Subject: [PATCH 7/9] Corrected get_keysym to use 'kbmap' everywhere, so it doesn't crash if 'kbmap' is specified. --- xpybutil/keybind.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xpybutil/keybind.py b/xpybutil/keybind.py index 4a35981..99dae66 100644 --- a/xpybutil/keybind.py +++ b/xpybutil/keybind.py @@ -209,7 +209,7 @@ def get_keysym(keycode, col=0, kbmap=None): per = kbmap.keysyms_per_keycode ind = (keycode - mn) * per + col - return __kbmap.keysyms[ind] + return kbmap.keysyms[ind] def get_keysym_string(keysym): """ From 8ac687452f3a62196d6dc960a9c25cf669a56ba0 Mon Sep 17 00:00:00 2001 From: "David H. Bronke" Date: Mon, 14 Jan 2013 21:24:07 -0600 Subject: [PATCH 8/9] Updated README to match the ewmh.py docstring change from 96adc52868d7d5160b53beab2a59d4bd30d47ee5, corrected spelling in both, and clarified both. --- README | 113 +++++++++++++++++++++++------------------------ xpybutil/ewmh.py | 3 +- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/README b/README index 6b4c279..ed059be 100644 --- a/README +++ b/README @@ -1,14 +1,14 @@ Introduction ============ -xpybutil is an abstraction over the X Python Binding (xpyb - the Python version -of XCB). It exists because xpyb is a very low level library that communicates +xpybutil is an abstraction over the X Python Binding (xpyb - the Python version +of XCB). It exists because xpyb is a very low level library that communicates with X. -The most mature portions of xpybutil are the ICCCM and EWMH modules. Each -implement their respective specifications of the same name. The EWMH module -also implements the '_NET_WM_WINDOW_OPACITY' and '_NET_VISIBLE_DESKTOPS' -non-standard features. The former is widely used by compositing managers and -other utilities (i.e., xcompmgr and transset-df) while the latter is used by my +The most mature portions of xpybutil are the ICCCM and EWMH modules. Each +implement their respective specifications of the same name. The EWMH module +also implements the '_NET_WM_WINDOW_OPACITY' and '_NET_VISIBLE_DESKTOPS' +non-standard features. The former is widely used by compositing managers and +other utilities (i.e., xcompmgr and transset-df) while the latter is used by my fork of Openbox called Openbox Multihead. @@ -19,15 +19,15 @@ The API docs can be found here: http://burntsushi.net/X11/xpybutil/docs/ Examples ======== -There are a few examples included in the 'examples' directory of the source of -xpybutil. You could also look at my 'window-marker' or 'pytyle3' utilities. The -former is a small script but the latter is on the order of ~1000 lines and -encompasses a wide variety of use cases of xpybutil. (And still yet, other use +There are a few examples included in the 'examples' directory of the source of +xpybutil. You could also look at my 'window-marker' or 'pytyle3' utilities. The +former is a small script but the latter is on the order of ~1000 lines and +encompasses a wide variety of use cases of xpybutil. (And still yet, other use cases are probably more appropriate only for window manager development.) -Another program that uses xpybutil is 'pager-multihead'. I would advise not -looking to it for inspiration, as it mixes xpybutil and GTK. (Namely, it uses -GTK's event dispatcher, but interacts with the window manager mostly using +Another program that uses xpybutil is 'pager-multihead'. I would advise not +looking to it for inspiration, as it mixes xpybutil and GTK. (Namely, it uses +GTK's event dispatcher, but interacts with the window manager mostly using xpybutil.) @@ -46,7 +46,7 @@ event.py - A module that can send client events to windows. It also allows icccm.py - A module that implements most of the ICCCM spec. -image.py - An incomplete and haphazard collection of functions that can +image.py - An incomplete and haphazard collection of functions that can bridge a gap between PIL and drawing images with X. keybind.py - A set of functions devoted to binding key presses and registering @@ -58,35 +58,35 @@ keysymdef.py - The sole purpose of this module is to provide a mapping from by the user). This should probably never be used directly. It is used by the keysym module. -motif.py - Implements a subset of the Motif spec. This module exists +motif.py - Implements a subset of the Motif spec. This module exists because some window managers still use this as the only way of toggling window decorations via events. mousebind.py - Similar to keybind.py, but for mice. -rect.py - A few utility functions that do math associated with X style +rect.py - A few utility functions that do math associated with X style rectangles. (i.e., a 4-tuple of (top_left_x, top_left_y, width, height).) It can also calculate monitor rectangles after accounting for struts. -render.py - A nearly exact port of the module by the same name from +render.py - A nearly exact port of the module by the same name from xcb-util. I used it once to help with a compositing manager that I wrote with xpyb. -util.py - A vast assortment of utility functions. The ones of interest to +util.py - A vast assortment of utility functions. The ones of interest to you are probably 'get_atom' and 'get_atom_name'. The rest are heavily used throughout the rest of xpybutil. -window.py - A few utility functions related to client windows. In +window.py - A few utility functions related to client windows. In particular, getting an accurate geometry of a client window including the decorations (this can vary with the window manager). Also, a functon to move and/or resize a window accurately by the top-left corner. (Also can change based on - the currently running window manager.) + the currently running window manager.) - This module also contains a function 'listen' that must be used - in order to receive certain events from a window. For example, - if you wanted to run 'func' whenever a property on the root + This module also contains a function 'listen' that must be used + in order to receive certain events from a window. For example, + if you wanted to run 'func' whenever a property on the root window changed, you could do something like: import xpybutil @@ -101,10 +101,10 @@ window.py - A few utility functions related to client windows. In active_window_id = ewmh.get_active_window().reply() window.listen(xpybutil.root, 'PropertyChange') - event.connect('PropertyNotify', xpybutil.root, func) + event.connect('PropertyNotify', xpybutil.root, func) The idea here is to tell X that you want events that fall under - the 'PropertyChange' category. Then you bind 'func' to the + the 'PropertyChange' category. Then you bind 'func' to the particular event 'PropertyNotify'. xinerama.py - A couple of functions that support retrieving information about @@ -120,14 +120,14 @@ xinerama.py - A couple of functions that support retrieving information about Unusable modules ================ font.py is pretty inadequate. Mostly because if you find -yourself wanting to use font.py, you're probably doing something wrong. +yourself wanting to use font.py, you're probably doing something wrong. (i.e., use pycairo or PIL.) EWMH and ICCCM ============== -xpybutil's primary purpose was to make accessing ICCCM and EWMH related -information extremely easy. This can be done by using the ewmh or icccm +xpybutil's primary purpose was to make accessing ICCCM and EWMH related +information extremely easy. This can be done by using the ewmh or icccm modules. Here's a quick example (assuming xpybutil is installed): import xpybutil.ewmh as ewmh @@ -137,56 +137,55 @@ modules. Here's a quick example (assuming xpybutil is installed): print names.get(current_desktop, current_desktop) -This imports the ewmh module (and by extension, connects to the X server) and -fetches a list of the current desktop names and the current desktop index -(starting from 0). Since not every desktop must be named, the desktop index is +This imports the ewmh module (and by extension, connects to the X server) and +fetches a list of the current desktop names and the current desktop index +(starting from 0). Since not every desktop must be named, the desktop index is printed if it has no name. -Note that the functions in the ewmh and icccm module return *cookies*. In order -to pull a response from the X server, call the 'reply()' method on a cookie -object. To force a response sent to the X server, call the 'check()' method on +Note that the functions in the ewmh and icccm module return *cookies*. In order +to pull a response from the X server, call the 'reply()' method on a cookie +object. To force a response sent to the X server, call the 'check()' method on the corresponding cookie. -Much of the EWMH and ICCCM modules encapsulate packing data from convenient -Python data types into C structs (using the 'struct' Python module). For -example, if one wants to fetch the partial struts set by some window with +Much of the EWMH and ICCCM modules encapsulate packing data from convenient +Python data types into C structs (using the 'struct' Python module). For +example, if one wants to fetch the partial struts set by some window with identifier ID, you could do: import xpybutil.ewmh as ewmh print ewmh.get_wm_strut_partial(ID).reply() -Which outputs a dictionary with 12 entries, where each corresponds to a value +Which outputs a dictionary with 12 entries, where each corresponds to a value in the partial strut. (i.e., 'left', 'top_end_x', 'right_start_y', etc...). -In general, particularly with the EWMH module, the ewmh module is very nearly -representative of the spec itself. Functions that get property values start -with 'get_', functions that set property values start with 'set_', and -functions that send an event to a client (which typically requests the window +In general, particularly with the EWMH module, the ewmh module is very nearly +representative of the spec itself. Functions that get property values start +with 'get_', functions that set property values start with 'set_', and +functions that send an event to a client (which typically requests the window manager to DO something) start with 'request_'. -If a request has no reply (typically the 'set_' functions), then the -default is to call it 'unchecked'. If you want to check the result (and force +If a request has no reply (typically the 'set_' functions), then the +default is to call it 'unchecked'. If you want to check the result (and force retrieval), then use the '_checked' variant. -The reverse goes for the 'get_' functions. By default, they are checked, but +The reverse goes for the 'get_' functions. By default, they are checked, but you can use the '_unchecked' variant too. -Basically, you should probably almost always use the 'checked' variant of -everything. The cases where you don't are when you want to send a whole bunch -of requests to the X server at once. You could use the unchecked invariant, and -after you've initialized all the cookies, calling 'flush' will force +Basically, you should probably almost always use the 'checked' variant of +everything. The cases where you don't are when you want to send a whole bunch +of requests to the X server at once. You could use the unchecked invariant, and +after you've initialized all the cookies, calling 'flush' will force communication with the X server. -Finally, unless you're writing a window manager or creating a client window -from scratch, you'll almost always want to use the 'get_' and 'request_' -functions. For exampe, if you want to change the current desktop... +Finally, unless you're writing a window manager or creating a client window +from scratch, you'll almost always want to use the 'get_' and 'request_' +functions, not 'set_'. For example, if you want to change the current +desktop... - DON'T DO: ewmh.get_current_desktop_checked(2).check() + DON'T DO: ewmh.set_current_desktop_checked(2).check() DO: ewmh.request_current_desktop_checked(2).check() -The former will probably not work, but the latter will. Just follow the EWMH +The former will probably not work, but the latter will. Just follow the EWMH spec :-) - - diff --git a/xpybutil/ewmh.py b/xpybutil/ewmh.py index 59e56bd..0165eb3 100644 --- a/xpybutil/ewmh.py +++ b/xpybutil/ewmh.py @@ -60,7 +60,8 @@ Finally, unless you're writing a window manager or creating a client window from scratch, you'll almost always want to use the ``get_`` and ``request_`` -functions. For exampe, if you want to change the current desktop... +functions, not ``set_``. For example, if you want to change the current +desktop... DON'T DO: ``ewmh.set_current_desktop_checked(2).check()`` From c97fd144f51287e46b3ca48e7533d766b3dd3628 Mon Sep 17 00:00:00 2001 From: "David H. Bronke" Date: Mon, 14 Jan 2013 21:50:32 -0600 Subject: [PATCH 9/9] Added a comment explaining the MapRequestEvents change from b9394a2d80d248c3d0023db9b617266a2c8f38ad. --- xpybutil/event.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xpybutil/event.py b/xpybutil/event.py index 348743a..9b7a0be 100644 --- a/xpybutil/event.py +++ b/xpybutil/event.py @@ -131,6 +131,8 @@ def main(): if isinstance(e, xproto.MappingNotifyEvent): w = None elif isinstance(e, xproto.MapRequestEvent): + # Force all MapRequestEvents to go to the root window so + # a window manager using xpybutil can get them. w = root elif hasattr(e, 'window'): w = e.window