[Xmonad] patch: wmdock script - manage dockable windows using dmenu
Krzysztof Kościuszkiewicz
k.kosciuszkiewicz at gmail.com
Mon May 28 21:30:47 EDT 2007
Hello,
I'm really enyoing the Xmonad experience so far, but inability to use
applications that insist on docking was a drawback to me. So I did my
firsts steps in the Xlib programming and here are the results. The hack
is really crude; script identifies windows that look like they should be
docked and optionally sends mouse events to them (most of such apps will
raise the main window after such treatment).
I hope someone will find it useful.
Cheers,
--
Krzysztof Kościuszkiewicz,
Gadu: 111851, Jabber: kokr at jabberpl.org
"Simplicity is the ultimate sophistication"
-- Leonardo da Vinci
-------------- next part --------------
New patches:
[Added wmdock application
Krzysztof Kosciuszkiewicz <k.kosciuszkiewicz at gmail.com>**20070529011349] {
adddir ./examples/dmenu-wmdock
addfile ./examples/dmenu-wmdock/README
hunk ./examples/dmenu-wmdock/README 1
+Use dmenu to "manage" windows that belong to WM-style docks.
+
+wmdock
+ A Python script using Xlib bindings to query and send mouse button events
+ to windows that seem to be dockable. Probably should be rewritten in
+ haskell ;)
+
+Config.hs
+ Add something similar to setup keybindings for wmdock and dmenu:
+
+ , ((modMask, xK_t ), spawn "wmdock -n | dmenu | wmdock send")
+
+ mod-t pops up a menu with all dockable windows. Selecting one will send a
+ double-button1 click to it (hopefully raising/lowering main app window).
+ Modify wmdock to suit your taste.
+
+dmenu is available from
+ http://www.suckless.org/wiki/tools/xlib
+
+python Xlib bindings are available from
+ http://python-xlib.sf.net/
addfile ./examples/dmenu-wmdock/wmdock
hunk ./examples/dmenu-wmdock/wmdock 1
+#! /usr/bin/env python
+
+__version__ = "0.2"
+__author__ = "Krzysztof Kosciuszkiewicz <k.kosciuszkiewicz at gmail.com>"
+__program__ = "wmdock"
+__license__ = "BSD"
+
+import Xlib
+import Xlib.X
+import Xlib.Xatom
+import Xlib.Xutil
+import Xlib.display
+import Xlib.protocol
+import sys
+import time
+
+# globals
+disp = Xlib.display.Display()
+scrn = disp.screen()
+root = scrn.root
+
+# the atoms that we will need to query for
+atom_wm_type = disp.intern_atom("_NET_WM_WINDOW_TYPE")
+atom_wm_type_dock = disp.intern_atom("_NET_WM_WINDOW_TYPE_DOCK")
+atom_wm_kde_dock = disp.intern_atom("_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR")
+
+def get_property(win, prop, prop_type=Xlib.X.AnyPropertyType):
+ return win.get_full_property(prop, prop_type)
+
+def is_dock(win):
+ """Check if the X window is a dock app"""
+ # everything in this function is based on code from Ion3 WM
+
+ # test window manager hints
+ hints = win.get_wm_hints()
+ if hints:
+ init_state = Xlib.Xutil.NormalState
+ if hints.flags & Xlib.Xutil.StateHint:
+ init_state = hints.initial_state
+ if init_state == Xlib.Xutil.WithdrawnState and \
+ hints.flags & Xlib.Xutil.IconWindowHint and hints.icon_window:
+ return True
+
+ # check if window has WM_WINDOW_TYPE_DOCK as WM_WINDOW_TYPE property
+ prop = get_property(win, atom_wm_type)
+ if prop:
+ # type should be atom, and value should contain the atom type_dock
+ if prop.format == Xlib.Xatom.ATOM and \
+ atom_wm_type_dock in prop.value:
+ return True
+
+ # test window manager class
+ # also all Qt dockable windows I've seen satisfy cls[0].endswith('dock')
+ cls = win.get_wm_class()
+ if cls and cls[1] == "DockApp":
+ # or cls[0].endswith('dock'):
+ return True
+
+ # test if it's kde-style dockapp
+ if get_property(win, atom_wm_kde_dock):
+ return True
+
+ return False
+
+
+def send_mouse_click(window, button=Xlib.X.Button1, double=False, delay=500):
+ """Send button press event followed by button release event to client"""
+ # I hope that translation of coordinates is ok here.
+ coords = root.translate_coords(window, 4, 4)
+ ev1_dict = dict(
+ time=0,
+ root=root,
+ window=window,
+ same_screen=1,
+ child=Xlib.X.NONE,
+ root_x=coords.x,
+ root_y=coords.y,
+ event_x=4,
+ event_y=4,
+ state=Xlib.X.NONE,
+ detail=button)
+ ev2_dict = ev1_dict.copy()
+ ev2_dict['state'] = button << 8 # that is ButtonXMask
+ ev1 = Xlib.protocol.event.ButtonPress(**ev1_dict)
+ ev2 = Xlib.protocol.event.ButtonRelease(**ev2_dict)
+ window.send_event(ev1)
+ disp.sync()
+ time.sleep(delay/1000)
+ window.send_event(ev2)
+ disp.sync()
+ if double:
+ time.sleep(delay/1000)
+ window.send_event(ev1)
+ disp.sync()
+ time.sleep(delay/1000)
+ window.send_event(ev2)
+ disp.sync()
+
+
+def walk_tree(win=root, func=None):
+ """Walk the X window hierarchy and call func on each window"""
+ if callable(func) and win:
+ func(win)
+ if win:
+ res = win.query_tree()
+ if res:
+ for chld in res.children:
+ walk_tree(chld, func=func)
+ return func
+
+
+class WindowVisitor(object):
+ """Visits windows and collects ones that match predicate"""
+ def __init__(self, condition=None):
+ self.winlist = []
+ self.condition = condition
+
+ def __call__(self, window):
+ if callable(self.condition) and self.condition(window):
+ self.winlist.append(window)
+
+ def collected(self):
+ return self.winlist
+
+
+if __name__ == "__main__":
+ import getopt
+ import string
+ import os
+
+ usage = """Usage: %s [options...] [list|send {ids|names...}]
+ """ % __program__
+ helpmsg = """Options:
+ -n list only window names (default: False)
+ -i list only window IDs (default: False)
+ -b button mouse button number in event to be sent (default: 1)
+ -d delay delay between mouse press and release events (default: 500ms)
+ -s send single-click events (default: False)
+ """
+ behaviour = """Operation:
+ If you tell me to "list", I will find and print IDs and names of dockable
+ X11 windows on your display. If you tell me to "send", I will send button
+ click events to each of specified windows (use names or IDs). If you
+ don't specify any after "send" I will try to read them from standard
+ input.
+
+ You can combine "list" and "send". By default I just "list".
+ """
+ try:
+ opts, args = getopt.gnu_getopt(sys.argv[1:],
+ "b:d:sinh", ["help", "version"])
+ except getopt.GetoptError, e:
+ print >>sys.stderr, e
+ print >>sys.stderr, usage
+ sys.exit(0)
+
+ button = Xlib.X.Button1
+ delay = 500
+ double = True
+ window_repr = lambda win: "%-10s %s" % (win.get_wm_name(), hex(int(win.id)))
+ for o, val in opts:
+ if o == '-b':
+ button = Xlib.X.Button1 + int(val) - 1
+ elif o == '-d':
+ delay = int(val)
+ elif o == '-s':
+ double = False
+ elif o == '-i':
+ window_repr = lambda win: hex(int(win.id))
+ elif o == '-n':
+ window_repr = lambda win: win.get_wm_name()
+ elif o == '--help':
+ print usage
+ print helpmsg
+ print behaviour
+ sys.exit(0)
+ elif o == '--version':
+ print "%s %s" % (__program__, __version__)
+ sys.exit(0)
+
+ do_print = True
+ do_send = False
+ get_ids = lambda: []
+
+ if not args or args[0] == "list":
+ args = args[1:]
+ else:
+ do_print = False
+
+ if args and args[0] == "send":
+ args = args[1:]
+ do_send = True
+ if args:
+ get_ids, args = lambda: args, []
+ else:
+ get_ids = lambda: map(string.strip, sys.stdin.read().split())
+
+ if args:
+ print >>sys.stderr, usage
+ sys.exit(0)
+
+ windows = walk_tree(func=WindowVisitor(is_dock)).collected()
+
+ if do_print:
+ for win in windows:
+ print window_repr(win)
+ sys.stdout.flush()
+
+ if do_send:
+ for word in get_ids():
+ try:
+ winid = int(word, 16)
+ finder = lambda w: w.id == winid
+ except:
+ finder = lambda w: w.get_wm_name() == word
+
+ for win in filter(finder, windows):
+ send_mouse_click(win, button=button, delay=delay, double=double)
+
+ sys.exit(0)
}
Context:
[Rescreen is in main xmonad now
Spencer Janssen <sjanssen at cse.unl.edu>**20070528050656]
[replace "name" in NamedWindow with a Show instance.
David Roundy <droundy at darcs.net>**20070526185114]
[[Spiral] blend in the scale factor so it doesn't have any effect on the smallest windows
joe.thornber at gmail.com**20070525032732]
[[Spiral] last rect takes all available space
joe.thornber at gmail.com**20070524120239]
[[Spiral] Introduce a simpler Rect data type to remove a lot of the fromIntegrals
joe.thornber at gmail.com**20070524100423]
[[Spiral] divideRects now takes a list of directions to split in
joe.thornber at gmail.com**20070524090211]
[[Spiral] misc tidying
joe.thornber at gmail.com**20070524085537]
[[Spiral] remove old spiral code
joe.thornber at gmail.com**20070524084805]
[[Spiral] add fibonacci spiral
joe.thornber at gmail.com**20070524084423]
[Allow clients of NamedWindows to get at the name.
glasser at mit.edu**20070523184251]
[dzen module (with xinerama support, which requires glasser's Xinerama patch to dzen)
glasser at mit.edu**20070523184315]
[Extract NamedWindow support from Mosaic into its own module
glasser at mit.edu**20070523155855]
[remove SwapFocus (which is no longer possible)
David Roundy <droundy at darcs.net>**20070523153841
This module depended on the focus stack.
]
[Fix Spiral's module name
Spencer Janssen <sjanssen at cse.unl.edu>**20070522170909]
[[SPIRAL] add spiral tiling layout
joe.thornber at gmail.com**20070522062537]
[Make RotView compile.
Miikka Koskinen <arcatan at kapsi.fi>**20070522075338
As I'm not a Xinerama user, I'm not sure if rotView should consider only
hidden workspaces or also visible but not focused workspaces. I thought hidden
workspaces only would be more logical.
]
[bug fix in DwmPromote. whoops.
Miikka Koskinen <arcatan at kapsi.fi>**20070522062118]
[make FindEmptyWorkspace compile
Miikka Koskinen <arcatan at kapsi.fi>**20070521123239]
[make DwmPromote compile
Miikka Koskinen <arcatan at kapsi.fi>**20070521123140]
[updated Dmenu.hs to work with zipper StackSet
Jason Creighton <jcreigh at gmail.com>**20070521233947]
[Add GreedyView
Spencer Janssen <sjanssen at cse.unl.edu>**20070521220048]
[Rescreen: collects new screen information
Spencer Janssen <sjanssen at cse.unl.edu>**20070521164808]
[Fixes for windowset -> workspace rename
Spencer Janssen <sjanssen at cse.unl.edu>**20070521042118]
[TwoPane: hide windows that aren't in view
Spencer Janssen <sjanssen at cse.unl.edu>**20070518224240]
[make Mosaic even less picky by default.
David Roundy <droundy at darcs.net>**20070516175554]
[add clear window message in Mosaic.
David Roundy <droundy at darcs.net>**20070516175518]
[Comment only
Spencer Janssen <sjanssen at cse.unl.edu>**20070517211003]
[Add instructions for TwoPane
Spencer Janssen <sjanssen at cse.unl.edu>**20070517210206]
[Add TwoPane
Spencer Janssen <sjanssen at cse.unl.edu>**20070517195618]
[throttle the exponential expense when many windows are present.
David Roundy <droundy at darcs.net>**20070516022123]
[make mosaic configure windows by name rather than by Window.
David Roundy <droundy at darcs.net>**20070512215644
Note that this is still pretty flawed. Often window names change, and the
layout then stagnates a bit. Gimp, for example, opens most its windows
with the same name before renaming them, so you have to hit mod-return or
something to force a doLayout. Also, gimp still overrides xmonad regarding
the size of its main window. :(
]
[XMonadContrib.FindEmptyWorkspace
Miikka Koskinen <arcatan at kapsi.fi>**20070513184338
With this module you can find empty workspaces, view them and tag windows to
them.
]
[make DwmPromote compile
Miikka Koskinen <arcatan at kapsi.fi>**20070513184254]
[make DwmPromote compile again
Miikka Koskinen <arcatan at kapsi.fi>**20070510154158]
[make DwmPromote compile
Miikka Koskinen <arcatan at kapsi.fi>**20070503105236]
[add SwapFocus.
David Roundy <droundy at darcs.net>**20070512191315]
[make rotView only consider non-visible workspaces (Xinerama)
Jason Creighton <jcreigh at gmail.com>**20070510012059]
[fix commend in RotView.
David Roundy <droundy at darcs.net>**20070505185654]
[switch to Message type for layout messages
Don Stewart <dons at cse.unsw.edu.au>**20070505014332]
[Fix instructions in Mosaic.
Chris Mears <chris at cmears.id.au>**20070503222345]
[add Mosaic layout.
David Roundy <droundy at darcs.net>**20070503151024]
[-Wall police
Spencer Janssen <sjanssen at cse.unl.edu>**20070503211700]
[Make RotView build, and add a brief description.
Chris Mears <chris at cmears.id.au>**20070503104234]
[comment: Gave URL to xinerama-enabled dmenu patch
Jason Creighton <jcreigh at gmail.com>**20070503053133]
[Put dmenu in X too
Spencer Janssen <sjanssen at cse.unl.edu>**20070503053727]
[Add dmenu (thanks jcreigh)
Spencer Janssen <sjanssen at cse.unl.edu>**20070503052225]
[add RotView module.
David Roundy <droundy at darcs.net>**20070421233838]
[XMonadContrib.DwmPromote: dwm-like promote
Miikka Koskinen <arcatan at kapsi.fi>**20070501082031
I like the way dwm's equivalent to xmonad's promote works, so I
implemented dwmpromote.
]
[add simple date example
Don Stewart <dons at cse.unsw.edu.au>**20070429064013]
[more details
Don Stewart <dons at cse.unsw.edu.au>**20070429061426]
[add readme
Don Stewart <dons at cse.unsw.edu.au>**20070429061329]
[Initial import of xmonad contributions
Don Stewart <dons at cse.unsw.edu.au>**20070429061150]
Patch bundle hash:
8c693213500612db37f8411c2498d77d9268dcd9
More information about the Xmonad
mailing list