merge the C branch into HEAD
authorDana Jansens <danakj@orodu.net>
Sun, 16 Mar 2003 21:11:39 +0000 (21:11 +0000)
committerDana Jansens <danakj@orodu.net>
Sun, 16 Mar 2003 21:11:39 +0000 (21:11 +0000)
170 files changed:
Makefile.am
README.CVS
bootstrap
c/.cvsignore [new file with mode: 0644]
c/Makefile.am [new file with mode: 0644]
c/client.c [new file with mode: 0644]
c/client.h [new file with mode: 0644]
c/clientwrap.c [new file with mode: 0644]
c/clientwrap.h [new file with mode: 0644]
c/event.c [new file with mode: 0644]
c/event.h [new file with mode: 0644]
c/eventdata.c [new file with mode: 0644]
c/eventdata.h [new file with mode: 0644]
c/extensions.c [new file with mode: 0644]
c/extensions.h [new file with mode: 0644]
c/focus.c [new file with mode: 0644]
c/focus.h [new file with mode: 0644]
c/frame.c [new file with mode: 0644]
c/frame.h [new file with mode: 0644]
c/geom.h [new file with mode: 0644]
c/gettext.h [new file with mode: 0644]
c/hooks.c [new file with mode: 0644]
c/hooks.h [new file with mode: 0644]
c/kbind.c [new file with mode: 0644]
c/kbind.h [new file with mode: 0644]
c/mbind.c [new file with mode: 0644]
c/mbind.h [new file with mode: 0644]
c/obexport.c [new file with mode: 0644]
c/obexport.h [new file with mode: 0644]
c/openbox.c [new file with mode: 0644]
c/openbox.h [new file with mode: 0644]
c/prop.c [new file with mode: 0644]
c/prop.h [new file with mode: 0644]
c/python.c [new file with mode: 0644]
c/python.h [new file with mode: 0644]
c/screen.c [new file with mode: 0644]
c/screen.h [new file with mode: 0644]
c/screenwrap.c [new file with mode: 0644]
c/screenwrap.h [new file with mode: 0644]
c/stacking.c [new file with mode: 0644]
c/stacking.h [new file with mode: 0644]
c/xerror.c [new file with mode: 0644]
c/xerror.h [new file with mode: 0644]
configure.ac
data/styles/Makefile.am [deleted file]
doc/Makefile.am
doc/bsetbg.1 [deleted file]
doc/bsetroot.1 [deleted file]
doc/openbox.1.in [deleted file]
doc/python/client.txt [new file with mode: 0644]
doc/python/config.txt [new file with mode: 0644]
doc/python/helpers.txt [new file with mode: 0644]
doc/python/hooks.txt [new file with mode: 0644]
doc/python/keyboard.txt [new file with mode: 0644]
doc/python/keyboarddata.txt [new file with mode: 0644]
doc/python/openbox.txt [new file with mode: 0644]
doc/python/pointer.txt [new file with mode: 0644]
doc/python/pointerdata.txt [new file with mode: 0644]
doc/themerc.txt [new file with mode: 0644]
engines/.cvsignore [moved from data/styles/.cvsignore with 100% similarity]
engines/Makefile.am [new file with mode: 0644]
engines/engineinterface.h [new file with mode: 0644]
engines/openbox/.cvsignore [new file with mode: 0644]
engines/openbox/Makefile.am [new file with mode: 0644]
engines/openbox/openbox.c [new file with mode: 0644]
engines/openbox/openbox.h [new file with mode: 0644]
engines/openbox/theme.c [new file with mode: 0644]
engines/openbox/theme.h [new file with mode: 0644]
m4/openbox.m4
m4/swig.m4 [deleted file]
openbox/.cvsignore [new file with mode: 0644]
openbox/Makefile.am [new file with mode: 0644]
openbox/client.c [new file with mode: 0644]
openbox/client.h [new file with mode: 0644]
openbox/clientwrap.c [new file with mode: 0644]
openbox/clientwrap.h [new file with mode: 0644]
openbox/engine.c [new file with mode: 0644]
openbox/engine.h [new file with mode: 0644]
openbox/event.c [new file with mode: 0644]
openbox/event.h [new file with mode: 0644]
openbox/extensions.c [new file with mode: 0644]
openbox/extensions.h [new file with mode: 0644]
openbox/focus.c [new file with mode: 0644]
openbox/focus.h [new file with mode: 0644]
openbox/frame.c [new file with mode: 0644]
openbox/frame.h [new file with mode: 0644]
openbox/geom.h [new file with mode: 0644]
openbox/gettext.h [new file with mode: 0644]
openbox/hooks.c [new file with mode: 0644]
openbox/hooks.h [new file with mode: 0644]
openbox/keyboard.c [new file with mode: 0644]
openbox/keyboard.h [new file with mode: 0644]
openbox/openbox.c [new file with mode: 0644]
openbox/openbox.h [new file with mode: 0644]
openbox/openboxwrap.c [new file with mode: 0644]
openbox/openboxwrap.h [new file with mode: 0644]
openbox/pointer.c [new file with mode: 0644]
openbox/pointer.h [new file with mode: 0644]
openbox/prop.c [new file with mode: 0644]
openbox/prop.h [new file with mode: 0644]
openbox/python.c [new file with mode: 0644]
openbox/python.h [new file with mode: 0644]
openbox/screen.c [new file with mode: 0644]
openbox/screen.h [new file with mode: 0644]
openbox/stacking.c [new file with mode: 0644]
openbox/stacking.h [new file with mode: 0644]
openbox/themerc.c [new file with mode: 0644]
openbox/themerc.h [new file with mode: 0644]
openbox/timer.c [new file with mode: 0644]
openbox/timer.h [new file with mode: 0644]
openbox/xerror.c [new file with mode: 0644]
openbox/xerror.h [new file with mode: 0644]
otk/Makefile.am
otk/display.cc
otk/display.hh
otk/screeninfo.cc
otk/screeninfo.hh
python/.cvsignore [new file with mode: 0644]
python/Makefile.am [new file with mode: 0644]
python/buttonmap.py [new file with mode: 0644]
python/config.py [new file with mode: 0644]
python/focus.py [new file with mode: 0644]
python/historyplacement.py [new file with mode: 0644]
python/keymap.py [new file with mode: 0644]
python/motion.py [new file with mode: 0644]
python/rc.py [new file with mode: 0644]
python/stackedcycle.py [new file with mode: 0644]
python/windowplacement.py [new file with mode: 0644]
render/.cvsignore [new file with mode: 0644]
render/Makefile.am [new file with mode: 0644]
render/color.c [new file with mode: 0644]
render/color.h [new file with mode: 0644]
render/font.c [new file with mode: 0644]
render/gradient.c [new file with mode: 0644]
render/gradient.h [new file with mode: 0644]
render/render.c [new file with mode: 0644]
render/render.h [new file with mode: 0644]
render/test.c [new file with mode: 0644]
scripts/historyplacement.py
src/actions.cc
themes/.cvsignore [new file with mode: 0644]
themes/Makefile.am [new file with mode: 0644]
themes/openbox/.cvsignore [new file with mode: 0644]
themes/openbox/Makefile.am [new file with mode: 0644]
themes/openbox/artwiz [moved from data/styles/artwiz with 100% similarity]
themes/openbox/bbs [moved from data/styles/bbs with 100% similarity]
themes/openbox/bluebox [moved from data/styles/bluebox with 100% similarity]
themes/openbox/cthulhain [moved from data/styles/cthulhain with 100% similarity]
themes/openbox/deep [moved from data/styles/deep with 100% similarity]
themes/openbox/fieron [moved from data/styles/fieron with 100% similarity]
themes/openbox/fieron2 [moved from data/styles/fieron2 with 100% similarity]
themes/openbox/flux [moved from data/styles/flux with 100% similarity]
themes/openbox/frobozz [moved from data/styles/frobozz with 100% similarity]
themes/openbox/frobust [moved from data/styles/frobust with 100% similarity]
themes/openbox/mbdtex [moved from data/styles/mbdtex with 100% similarity]
themes/openbox/miklos [moved from data/styles/miklos with 100% similarity]
themes/openbox/nyz [moved from data/styles/nyz with 100% similarity]
themes/openbox/nyzclone [moved from data/styles/nyzclone with 100% similarity]
themes/openbox/ob20 [moved from data/styles/ob20 with 100% similarity]
themes/openbox/operation [moved from data/styles/operation with 100% similarity]
themes/openbox/outcomes [moved from data/styles/outcomes with 100% similarity]
themes/openbox/paper [moved from data/styles/paper with 100% similarity]
themes/openbox/purplehaaze [moved from data/styles/purplehaaze with 100% similarity]
themes/openbox/shade [moved from data/styles/shade with 100% similarity]
themes/openbox/steelblue [moved from data/styles/steelblue with 100% similarity]
themes/openbox/steelblue2 [moved from data/styles/steelblue2 with 100% similarity]
themes/openbox/the_orange [moved from data/styles/the_orange with 100% similarity]
themes/openbox/trisb [moved from data/styles/trisb with 100% similarity]
themes/openbox/twice [moved from data/styles/twice with 100% similarity]
themes/openbox/warp-xp [moved from data/styles/warp-xp with 100% similarity]

index 2491616..aff7de3 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS = po data doc otk wrap src scripts tools
+SUBDIRS = po data themes doc render kernel engines python
 MAINTAINERCLEANFILES = aclocal.m4 config.h.in configure Makefile.in stamp-h.in
 
 .PHONY: doc
index 2f8dcbe..893da87 100644 (file)
@@ -1,7 +1,6 @@
 To build Openbox from CVS you need:
 
-A C++ Compiler (GNU GCC/G++ 3.2+ suggested, 2.9x is not supported as it is not
-                very compliant with the ISO C++ standards)
+A C Compiler (GNU GCC 3.2+ suggested)
 GNU Gettext 0.11.5
 GNU Autoconf 2.50+
 GNU Automake 1.7.1+ (1.7.1 has a bug fix for py-compile)
@@ -9,7 +8,7 @@ GNU Libtool
 Xft2 library/headers (devel package) (http://www.fontconfig.org)
 Xlib library/headers (devel package)
 Python library/headers (devel package) (http://www.python.org)
-SWIG 1.3.14+ (http://www.swig.org)
+Pkg-Config
 
 I recommend the latest version of all these packages.
 
@@ -19,10 +18,6 @@ Do the following to build and install Openbox in CVS:
 % ./configure
 % make all install
 
-When building the SWIG generated wrappers (*_wrap.cc), a huge number of
-compiler warnings will be generated. Don't be phased by this, it doesn't mean
-anything is broken unless you get an actual error.
-
 Don't try running it from the src/ directory without installing, it won't work.
 It needs to be installed before it is run.
 
@@ -32,15 +27,14 @@ The installed binary is 'openbox3'.
 
 ----
 Here's the packages from Debian (sid):
-  g++
+  gcc
   gettext
   automake1.7
   autoconf
   libtool
+  pkg-config
   libxft2-dev
   python-dev
-  swig1.3
-
 
 ----
 Check out the Wiki for how to configure Openbox 3:
index f0e2b2b..288bd69 100755 (executable)
--- a/bootstrap
+++ b/bootstrap
@@ -7,7 +7,7 @@ sh() {
 sh autopoint --force # for GNU gettext
 sh libtoolize --copy --force --automake
 sh aclocal -I m4
-sh autoheader
+#sh autoheader
 sh autoconf
 sh automake --foreign --include-deps --add-missing --copy
 
diff --git a/c/.cvsignore b/c/.cvsignore
new file mode 100644 (file)
index 0000000..250363e
--- /dev/null
@@ -0,0 +1,5 @@
+ob3
+Makefile.in
+.libs
+.deps
+Makefile
diff --git a/c/Makefile.am b/c/Makefile.am
new file mode 100644 (file)
index 0000000..cfde7b6
--- /dev/null
@@ -0,0 +1,34 @@
+localedir=$(datadir)/locale
+scriptdir = $(libdir)/openbox/python
+
+CPPFLAGS=$(PYTHON_CFLAGS) $(GLIB_CFLAGS) @CPPFLAGS@ \
+-DLOCALEDIR=\"$(localedir)\" \
+-DSCRIPTDIR=\"$(scriptdir)\" \
+-DG_LOG_DOMAIN=\"Openbox\"
+
+LIBS=$(PYTHON_LIBS) $(GLIB_LIBS) @LIBS@
+
+bin_PROGRAMS= ob3
+
+ob3_LDADD=@LIBINTL@
+ob3_LDFLAGS=-export-dynamic
+ob3_SOURCES=client.c event.c extensions.c focus.c frame.c openbox.c prop.c \
+       python.c screen.c stacking.c xerror.c hooks.c eventdata.c obexport.c \
+       clientwrap.c screenwrap.c kbind.c mbind.c
+
+noinst_HEADERS=client.h event.h extensions.h focus.h frame.h geom.h gettext.h \
+       openbox.h prop.h python.h screen.h stacking.h xerror.h hooks.h \
+       eventdata.h clientwrap.h obexport.h screenwrap.h kbind.h mbind.h
+
+MAINTAINERCLEANFILES= Makefile.in
+
+#if CVS
+#ob3.i: $(wildcard *.h)
+#      @touch $@
+
+#ob3_wrap.c: ob3.i
+#      $(SWIG) $(SWIG_PYTHON_OPT) $(filter -I%,$(CPPFLAGS)) -o $@ $<
+#endif
+
+distclean-local:
+       $(RM) *\~ *.orig *.rej .\#*
diff --git a/c/client.c b/c/client.c
new file mode 100644 (file)
index 0000000..995cba9
--- /dev/null
@@ -0,0 +1,1869 @@
+#include "client.h"
+#include "screen.h"
+#include "prop.h"
+#include "extensions.h"
+#include "frame.h"
+#include "event.h"
+#include "focus.h"
+#include "clientwrap.h"
+#include "stacking.h"
+#include "hooks.h"
+#include "mbind.h"
+#include <X11/Xutil.h>
+
+/*! The event mask to grab on client windows */
+#define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
+                         StructureNotifyMask)
+
+#define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
+                               ButtonMotionMask)
+
+GSList     *client_list      = NULL;
+GHashTable *client_map       = NULL;
+
+static void client_get_all(Client *self);
+static void client_toggle_border(Client *self, gboolean show);
+static void client_get_area(Client *self);
+static void client_get_desktop(Client *self);
+static void client_get_state(Client *self);
+static void client_get_shaped(Client *self);
+static void client_get_mwm_hints(Client *self);
+static void client_get_gravity(Client *self);
+static void client_change_allowed_actions(Client *self);
+static void client_change_state(Client *self);
+static Client *search_focus_tree(Client *node, Client *skip);
+static void client_apply_startup_state(Client *self);
+static Client *search_modal_tree(Client *node, Client *skip);
+
+static guint map_hash(Window w) { return w; }
+static gboolean map_key_comp(Window w1, Window w2) { return w1 == w2; }
+
+void client_startup()
+{
+    client_map = g_hash_table_new((GHashFunc)map_hash,
+                                 (GEqualFunc)map_key_comp);
+    client_set_list();
+}
+
+void client_shutdown()
+{
+    g_hash_table_destroy(client_map);
+}
+
+void client_set_list()
+{
+    Window *windows, *win_it;
+    GSList *it;
+    guint size = g_slist_length(client_list);
+
+    /* create an array of the window ids */
+    if (size > 0) {
+       windows = g_new(Window, size);
+       win_it = windows;
+       for (it = client_list; it != NULL; it = it->next, ++win_it)
+           *win_it = ((Client*)it->data)->window;
+    } else
+       windows = NULL;
+
+    PROP_SET32A(ob_root, net_client_list, window, windows, size);
+
+    if (windows)
+       g_free(windows);
+
+    stacking_set_list();
+}
+
+void client_manage_all()
+{
+    unsigned int i, j, nchild;
+    Window w, *children;
+    XWMHints *wmhints;
+    XWindowAttributes attrib;
+
+    XQueryTree(ob_display, ob_root, &w, &w, &children, &nchild);
+
+    /* remove all icon windows from the list */
+    for (i = 0; i < nchild; i++) {
+       if (children[i] == None) continue;
+       wmhints = XGetWMHints(ob_display, children[i]);
+       if (wmhints) {
+           if ((wmhints->flags & IconWindowHint) &&
+               (wmhints->icon_window != children[i]))
+               for (j = 0; j < nchild; j++)
+                   if (children[j] == wmhints->icon_window) {
+                       children[j] = None;
+                       break;
+                   }
+           XFree(wmhints);
+       }
+    }
+
+    for (i = 0; i < nchild; ++i) {
+       if (children[i] == None)
+           continue;
+       if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
+           if (attrib.override_redirect) continue;
+
+           if (attrib.map_state != IsUnmapped)
+               client_manage(children[i]);
+       }
+    }
+    XFree(children);
+}
+
+void client_manage(Window window)
+{
+    Client *client;
+    XEvent e;
+    XWindowAttributes attrib;
+    XSetWindowAttributes attrib_set;
+    XWMHints *wmhint;
+     
+    XGrabServer(ob_display);
+    XSync(ob_display, FALSE);
+
+    /* check if it has already been unmapped by the time we started mapping
+       the grab does a sync so we don't have to here */
+    if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
+       XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e)) {
+       XPutBackEvent(ob_display, &e);
+    
+       XUngrabServer(ob_display);
+       XFlush(ob_display);
+       return; /* don't manage it */
+    }
+
+    /* make sure it isn't an override-redirect window */
+    if (!XGetWindowAttributes(ob_display, window, &attrib) ||
+       attrib.override_redirect) {
+       XUngrabServer(ob_display);
+       XFlush(ob_display);
+       return; /* don't manage it */
+    }
+  
+    /* is the window a docking app */
+    if ((wmhint = XGetWMHints(ob_display, window))) {
+       if ((wmhint->flags & StateHint) &&
+           wmhint->initial_state == WithdrawnState) {
+           /* XXX: make dock apps work! */
+           XUngrabServer(ob_display);
+           XFlush(ob_display);
+           XFree(wmhint);
+           return;
+       }
+       XFree(wmhint);
+    }
+
+    /* choose the events we want to receive on the CLIENT window */
+    attrib_set.event_mask = CLIENT_EVENTMASK;
+    attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
+    XChangeWindowAttributes(ob_display, window,
+                           CWEventMask|CWDontPropagate, &attrib_set);
+
+
+    /* create the Client struct, and populate it from the hints on the
+       window */
+    client = g_new(Client, 1);
+    client->window = window;
+    client_get_all(client);
+
+    /* remove the client's border (and adjust re gravity) */
+    client_toggle_border(client, FALSE);
+     
+    /* specify that if we exit, the window should not be destroyed and should
+       be reparented back to root automatically */
+    XChangeSaveSet(ob_display, window, SetModeInsert);
+
+    /* create the decoration frame for the client window */
+    client->frame = frame_new(client);
+
+    frame_grab_client(client->frame);
+
+    client_apply_startup_state(client);
+
+    XUngrabServer(ob_display);
+    XFlush(ob_display);
+     
+    client_list = g_slist_append(client_list, client);
+    stacking_list = g_list_append(stacking_list, client);
+    g_hash_table_insert(client_map, (gpointer)window, client);
+
+    stacking_raise(client);
+
+    screen_update_struts();
+
+    LOGICALHOOK(NewWindow, g_quark_try_string("client"), client);
+
+    client_showhide(client);
+
+    /* grab all mouse bindings */
+    mbind_grab_all(client, TRUE);
+
+    /* update the list hints */
+    client_set_list();
+
+    g_message("Managed window 0x%lx frame 0x%lx", window,
+             client->frame->window);
+}
+
+void client_unmanage_all()
+{
+    while (client_list != NULL) {
+       client_unmanage(client_list->data);
+    }
+}
+
+void client_unmanage(Client *client)
+{
+    int j;
+    GSList *it;
+
+    g_message("Unmanaging window: %lx", client->window);
+
+    LOGICALHOOK(CloseWindow, g_quark_try_string("client"), client);
+
+    /* remove the window from our save set */
+    XChangeSaveSet(ob_display, client->window, SetModeDelete);
+
+    /* we dont want events no more */
+    XSelectInput(ob_display, client->window, NoEventMask);
+
+    /* ungrab any mouse bindings */
+    mbind_grab_all(client, FALSE);
+     
+    frame_hide(client->frame);
+
+    /* give the client its border back */
+    client_toggle_border(client, TRUE);
+
+    /* reparent the window out of the frame */
+    frame_release_client(client->frame);
+     
+    client_list = g_slist_remove(client_list, client);
+    stacking_list = g_list_remove(stacking_list, client);
+    g_hash_table_remove(client_map, (gpointer)client->window);
+
+    /* once the client is out of the list, update the struts to remove it's
+       influence */
+    screen_update_struts();
+
+    frame_free(client->frame);
+
+    /* notify the wrapper that its useless now */
+    if (client->wrap != NULL)
+       client->wrap->client = NULL;
+
+    /* tell our parent that we're gone */
+    if (client->transient_for != NULL)
+       client->transient_for->transients =
+           g_slist_remove(client->transient_for->transients, client);
+
+    /* tell our transients that we're gone */
+    for (it = client->transients; it != NULL; it = it->next) {
+       Client *t = ((Client*)it->data)->transient_for;
+       ((Client*)it->data)->transient_for = NULL;
+       if (t != NULL)
+           client_calc_layer(it->data);
+    }
+
+    /* unfocus the client (calls the focus callbacks) (we're out of the
+     transient lists already, so being modal doesn't matter) */
+    if (client->focused)
+       client_unfocus(client);
+    
+    if (ob_state != State_Exiting) {
+       /* these values should not be persisted across a window
+          unmapping/mapping */
+       prop_erase(client->window, prop_atoms.net_wm_desktop);
+       prop_erase(client->window, prop_atoms.net_wm_state);
+    } else {
+       /* if we're left in an iconic state, the client wont be mapped. this is
+          bad, since we will no longer be managing the window on restart */
+       if (client->iconic)
+           XMapWindow(ob_display, client->window);
+    }
+
+    /* free all data allocated in the client struct */
+    g_slist_free(client->transients);
+    for (j = 0; j < client->nicons; ++j)
+       g_free(client->icons[j].data);
+    if (client->nicons > 0)
+       g_free(client->icons);
+    g_free(client->title);
+    g_free(client->icon_title);
+    g_free(client->res_name);
+    g_free(client->res_class);
+    g_free(client->role);
+    g_free(client);
+     
+    /* update the list hints */
+    client_set_list();
+}
+
+static void client_toggle_border(Client *self, gboolean show)
+{
+    /* adjust our idea of where the client is, based on its border. When the
+       border is removed, the client should now be considered to be in a
+       different position.
+       when re-adding the border to the client, the same operation needs to be
+       reversed. */
+    int oldx = self->area.x, oldy = self->area.y;
+    int x = oldx, y = oldy;
+    switch(self->gravity) {
+    default:
+    case NorthWestGravity:
+    case WestGravity:
+    case SouthWestGravity:
+       break;
+    case NorthEastGravity:
+    case EastGravity:
+    case SouthEastGravity:
+       if (show) x -= self->border_width * 2;
+       else      x += self->border_width * 2;
+       break;
+    case NorthGravity:
+    case SouthGravity:
+    case CenterGravity:
+    case ForgetGravity:
+    case StaticGravity:
+       if (show) x -= self->border_width;
+       else      x += self->border_width;
+       break;
+    }
+    switch(self->gravity) {
+    default:
+    case NorthWestGravity:
+    case NorthGravity:
+    case NorthEastGravity:
+       break;
+    case SouthWestGravity:
+    case SouthGravity:
+    case SouthEastGravity:
+       if (show) y -= self->border_width * 2;
+       else      y += self->border_width * 2;
+       break;
+    case WestGravity:
+    case EastGravity:
+    case CenterGravity:
+    case ForgetGravity:
+    case StaticGravity:
+       if (show) y -= self->border_width;
+       else      y += self->border_width;
+       break;
+    }
+    self->area.x = x;
+    self->area.y = y;
+
+    if (show) {
+       XSetWindowBorderWidth(ob_display, self->window, self->border_width);
+
+       /* move the client so it is back it the right spot _with_ its
+          border! */
+       if (x != oldx || y != oldy)
+           XMoveWindow(ob_display, self->window, x, y);
+    } else
+       XSetWindowBorderWidth(ob_display, self->window, 0);
+}
+
+
+static void client_get_all(Client *self)
+{
+    /* update EVERYTHING!! */
+
+    self->ignore_unmaps = 0;
+  
+    /* defaults */
+    self->frame = NULL;
+    self->title = self->icon_title = NULL;
+    self->res_name = self->res_class = self->role = NULL;
+    self->wmstate = NormalState;
+    self->focused = FALSE;
+    self->transient = FALSE;
+    self->transients = NULL;
+    self->transient_for = NULL;
+    self->layer = -1;
+    self->urgent = FALSE;
+    self->positioned = FALSE;
+    self->disabled_decorations = 0;
+    self->group = None;
+    self->nicons = 0;
+    self->wrap = NULL;
+
+    client_get_area(self);
+    client_get_desktop(self);
+    client_get_state(self);
+    client_get_shaped(self);
+
+    client_update_transient_for(self);
+    client_get_mwm_hints(self);
+    client_get_type(self);/* this can change the mwmhints for special cases */
+
+    client_update_protocols(self);
+
+    client_get_gravity(self); /* get the attribute gravity */
+    client_update_normal_hints(self); /* this may override the attribute
+                                        gravity */
+
+    /* got the type, the mwmhints, the protocols, and the normal hints
+       (min/max sizes), so we're ready to set up the decorations/functions */
+    client_setup_decor_and_functions(self);
+  
+    client_update_wmhints(self);
+    client_update_title(self);
+    client_update_icon_title(self);
+    client_update_class(self);
+    client_update_strut(self);
+    client_update_icons(self);
+    client_update_kwm_icon(self);
+
+    /* this makes sure that these windows appear on all desktops */
+    if (self->type == Type_Desktop)
+       self->desktop = DESKTOP_ALL;
+
+    /* set the desktop hint, to make sure that it always exists, and to
+       reflect any changes we've made here */
+    PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
+
+    client_change_state(self);
+}
+
+static void client_get_area(Client *self)
+{
+    XWindowAttributes wattrib;
+    Status ret;
+  
+    ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
+    g_assert(ret != BadWindow);
+
+    RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
+    self->border_width = wattrib.border_width;
+}
+
+static void client_get_desktop(Client *self)
+{
+    unsigned int d;
+
+    if (PROP_GET32(self->window, net_wm_desktop, cardinal, d)) {
+       if (d >= screen_desktop && d != DESKTOP_ALL)
+           d = screen_desktop - 1;
+       self->desktop = d;
+    } else {
+       /* defaults to the current desktop */
+       self->desktop = screen_desktop;
+    }
+}
+
+static void client_get_state(Client *self)
+{
+    gulong *state;
+    gulong num;
+  
+    self->modal = self->shaded = self->max_horz = self->max_vert =
+       self->fullscreen = self->above = self->below = self->iconic =
+       self->skip_taskbar = self->skip_pager = FALSE;
+
+    if (PROP_GET32U(self->window, net_wm_state, atom, state, num)) {
+       gulong i;
+       for (i = 0; i < num; ++i) {
+           if (state[i] == prop_atoms.net_wm_state_modal)
+               self->modal = TRUE;
+           else if (state[i] == prop_atoms.net_wm_state_shaded)
+               self->shaded = TRUE;
+           else if (state[i] == prop_atoms.net_wm_state_hidden)
+               self->iconic = TRUE;
+           else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
+               self->skip_taskbar = TRUE;
+           else if (state[i] == prop_atoms.net_wm_state_skip_pager)
+               self->skip_pager = TRUE;
+           else if (state[i] == prop_atoms.net_wm_state_fullscreen)
+               self->fullscreen = TRUE;
+           else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
+               self->max_vert = TRUE;
+           else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
+               self->max_horz = TRUE;
+           else if (state[i] == prop_atoms.net_wm_state_above)
+               self->above = TRUE;
+           else if (state[i] == prop_atoms.net_wm_state_below)
+               self->below = TRUE;
+       }
+
+       g_free(state);
+    }
+}
+
+static void client_get_shaped(Client *self)
+{
+    self->shaped = FALSE;
+#ifdef   SHAPE
+    if (extensions_shape) {
+       int foo;
+       guint ufoo;
+       int s;
+
+       XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
+
+       XShapeQueryExtents(ob_display, self->window, &s, &foo,
+                          &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
+                          &ufoo);
+       self->shaped = (s != 0);
+    }
+#endif
+}
+
+void client_update_transient_for(Client *self)
+{
+    Window t = None;
+    Client *c = NULL;
+
+    if (XGetTransientForHint(ob_display, self->window, &t) &&
+       t != self->window) { /* cant be transient to itself! */
+       self->transient = TRUE;
+       c = g_hash_table_lookup(client_map, (gpointer)t);
+       g_assert(c != self);/* if this happens then we need to check for it*/
+
+       if (!c /*XXX: && _group*/) {
+           /* not transient to a client, see if it is transient for a
+              group */
+           if (/*t == _group->leader() || */
+               t == None ||
+               t == ob_root) {
+               /* window is a transient for its group! */
+               /* XXX: for now this is treated as non-transient.
+                  this needs to be fixed! */
+           }
+       }
+    } else
+       self->transient = FALSE;
+
+    /* if anything has changed... */
+    if (c != self->transient_for) {
+       if (self->transient_for)
+           /* remove from old parent */
+           g_slist_remove(self->transient_for->transients, self);
+       self->transient_for = c;
+       if (self->transient_for)
+           /* add to new parent */
+           g_slist_append(self->transient_for->transients, self);
+    }
+}
+
+static void client_get_mwm_hints(Client *self)
+{
+    unsigned long num;
+    unsigned long *hints;
+
+    self->mwmhints.flags = 0; /* default to none */
+
+    if (PROP_GET32U(self->window, motif_wm_hints, motif_wm_hints, hints, num)) {
+       if (num >= MWM_ELEMENTS) {
+           self->mwmhints.flags = hints[0];
+           self->mwmhints.functions = hints[1];
+           self->mwmhints.decorations = hints[2];
+       }
+       g_free(hints);
+    }
+}
+
+void client_get_type(Client *self)
+{
+    gulong *val, num, i;
+
+    self->type = -1;
+  
+    if (PROP_GET32U(self->window, net_wm_window_type, atom, val, num)) {
+       /* use the first value that we know about in the array */
+       for (i = 0; i < num; ++i) {
+           if (val[i] == prop_atoms.net_wm_window_type_desktop)
+               self->type = Type_Desktop;
+           else if (val[i] == prop_atoms.net_wm_window_type_dock)
+               self->type = Type_Dock;
+           else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
+               self->type = Type_Toolbar;
+           else if (val[i] == prop_atoms.net_wm_window_type_menu)
+               self->type = Type_Menu;
+           else if (val[i] == prop_atoms.net_wm_window_type_utility)
+               self->type = Type_Utility;
+           else if (val[i] == prop_atoms.net_wm_window_type_splash)
+               self->type = Type_Splash;
+           else if (val[i] == prop_atoms.net_wm_window_type_dialog)
+               self->type = Type_Dialog;
+           else if (val[i] == prop_atoms.net_wm_window_type_normal)
+               self->type = Type_Normal;
+           else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
+               /* prevent this window from getting any decor or
+                  functionality */
+               self->mwmhints.flags &= (MwmFlag_Functions |
+                                        MwmFlag_Decorations);
+               self->mwmhints.decorations = 0;
+               self->mwmhints.functions = 0;
+           }
+           if (self->type != (WindowType) -1)
+               break; /* grab the first legit type */
+       }
+       g_free(val);
+    }
+    
+    if (self->type == (WindowType) -1) {
+       /*the window type hint was not set, which means we either classify
+         ourself as a normal window or a dialog, depending on if we are a
+         transient. */
+       if (self->transient)
+           self->type = Type_Dialog;
+       else
+           self->type = Type_Normal;
+    }
+}
+
+void client_update_protocols(Client *self)
+{
+    Atom *proto;
+    int num_return = 0, i;
+
+    self->focus_notify = FALSE;
+    self->delete_window = FALSE;
+
+    if (XGetWMProtocols(ob_display, self->window, &proto, &num_return)) {
+       for (i = 0; i < num_return; ++i) {
+           if (proto[i] == prop_atoms.wm_delete_window) {
+               /* this means we can request the window to close */
+               self->delete_window = TRUE;
+           } else if (proto[i] == prop_atoms.wm_take_focus)
+               /* if this protocol is requested, then the window will be
+                  notified whenever we want it to receive focus */
+               self->focus_notify = TRUE;
+       }
+       XFree(proto);
+    }
+}
+
+static void client_get_gravity(Client *self)
+{
+    XWindowAttributes wattrib;
+    Status ret;
+
+    ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
+    g_assert(ret != BadWindow);
+    self->gravity = wattrib.win_gravity;
+}
+
+void client_update_normal_hints(Client *self)
+{
+    XSizeHints size;
+    long ret;
+    int oldgravity = self->gravity;
+
+    /* defaults */
+    self->min_ratio = 0.0f;
+    self->max_ratio = 0.0f;
+    SIZE_SET(self->size_inc, 1, 1);
+    SIZE_SET(self->base_size, 0, 0);
+    SIZE_SET(self->min_size, 0, 0);
+    SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
+
+    /* get the hints from the window */
+    if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
+       self->positioned = (size.flags & (PPosition|USPosition));
+
+       if (size.flags & PWinGravity) {
+           self->gravity = size.win_gravity;
+      
+           /* if the client has a frame, i.e. has already been mapped and
+              is changing its gravity */
+           if (self->frame && self->gravity != oldgravity) {
+               /* move our idea of the client's position based on its new
+                  gravity */
+               self->area.x = self->frame->area.x;
+               self->area.y = self->frame->area.y;
+               frame_frame_gravity(self->frame,
+                                   &self->area.x, &self->area.y);
+           }
+       }
+
+       if (size.flags & PAspect) {
+           if (size.min_aspect.y)
+               self->min_ratio = size.min_aspect.x / size.min_aspect.y;
+           if (size.max_aspect.y)
+               self->max_ratio = size.max_aspect.x / size.max_aspect.y;
+       }
+
+       if (size.flags & PMinSize)
+           SIZE_SET(self->min_size, size.min_width, size.min_height);
+    
+       if (size.flags & PMaxSize)
+           SIZE_SET(self->max_size, size.max_width, size.max_height);
+    
+       if (size.flags & PBaseSize)
+           SIZE_SET(self->base_size, size.base_width, size.base_height);
+    
+       if (size.flags & PResizeInc)
+           SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
+    }
+}
+
+void client_setup_decor_and_functions(Client *self)
+{
+    /* start with everything (cept fullscreen) */
+    self->decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
+       Decor_Icon | Decor_AllDesktops | Decor_Iconify | Decor_Maximize;
+    self->functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
+       Func_Shade;
+    if (self->delete_window) {
+       self->decorations |= Decor_Close;
+       self->functions |= Func_Close;
+    }
+
+    if (!(self->min_size.width < self->max_size.width ||
+         self->min_size.height < self->max_size.height)) {
+       self->decorations &= ~(Decor_Maximize | Decor_Handle);
+       self->functions &= ~(Func_Resize | Func_Maximize);
+    }
+
+    switch (self->type) {
+    case Type_Normal:
+       /* normal windows retain all of the possible decorations and
+          functionality, and are the only windows that you can fullscreen */
+       self->functions |= Func_Fullscreen;
+       break;
+
+    case Type_Dialog:
+       /* dialogs cannot be maximized */
+       self->decorations &= ~Decor_Maximize;
+       self->functions &= ~Func_Maximize;
+       break;
+
+    case Type_Menu:
+    case Type_Toolbar:
+    case Type_Utility:
+       /* these windows get less functionality */
+       self->decorations &= ~(Decor_Iconify | Decor_Handle);
+       self->functions &= ~(Func_Iconify | Func_Resize);
+       break;
+
+    case Type_Desktop:
+    case Type_Dock:
+    case Type_Splash:
+       /* none of these windows are manipulated by the window manager */
+       self->decorations = 0;
+       self->functions = 0;
+       break;
+    }
+
+    /* Mwm Hints are applied subtractively to what has already been chosen for
+       decor and functionality */
+    if (self->mwmhints.flags & MwmFlag_Decorations) {
+       if (! (self->mwmhints.decorations & MwmDecor_All)) {
+           if (! (self->mwmhints.decorations & MwmDecor_Border))
+               self->decorations &= ~Decor_Border;
+           if (! (self->mwmhints.decorations & MwmDecor_Handle))
+               self->decorations &= ~Decor_Handle;
+           if (! (self->mwmhints.decorations & MwmDecor_Title))
+               self->decorations &= ~Decor_Titlebar;
+           if (! (self->mwmhints.decorations & MwmDecor_Iconify))
+               self->decorations &= ~Decor_Iconify;
+           if (! (self->mwmhints.decorations & MwmDecor_Maximize))
+               self->decorations &= ~Decor_Maximize;
+       }
+    }
+
+    if (self->mwmhints.flags & MwmFlag_Functions) {
+       if (! (self->mwmhints.functions & MwmFunc_All)) {
+           if (! (self->mwmhints.functions & MwmFunc_Resize))
+               self->functions &= ~Func_Resize;
+           if (! (self->mwmhints.functions & MwmFunc_Move))
+               self->functions &= ~Func_Move;
+           if (! (self->mwmhints.functions & MwmFunc_Iconify))
+               self->functions &= ~Func_Iconify;
+           if (! (self->mwmhints.functions & MwmFunc_Maximize))
+               self->functions &= ~Func_Maximize;
+           /* dont let mwm hints kill the close button
+              if (! (self->mwmhints.functions & MwmFunc_Close))
+              self->functions &= ~Func_Close; */
+       }
+    }
+
+    /* can't maximize without moving/resizing */
+    if (!((self->functions & Func_Move) && (self->functions & Func_Resize)))
+       self->functions &= ~Func_Maximize;
+
+    /* finally, user specified disabled decorations are applied to subtract
+       decorations */
+    if (self->disabled_decorations & Decor_Titlebar)
+       self->decorations &= ~Decor_Titlebar;
+    if (self->disabled_decorations & Decor_Handle)
+       self->decorations &= ~Decor_Handle;
+    if (self->disabled_decorations & Decor_Border)
+       self->decorations &= ~Decor_Border;
+    if (self->disabled_decorations & Decor_Iconify)
+       self->decorations &= ~Decor_Iconify;
+    if (self->disabled_decorations & Decor_Maximize)
+       self->decorations &= ~Decor_Maximize;
+    if (self->disabled_decorations & Decor_AllDesktops)
+       self->decorations &= ~Decor_AllDesktops;
+    if (self->disabled_decorations & Decor_Close)
+       self->decorations &= ~Decor_Close;
+
+    /* if we don't have a titlebar, then we cannot shade! */
+    if (!(self->decorations & Decor_Titlebar))
+       self->functions &= ~Func_Shade;
+
+    client_change_allowed_actions(self);
+
+    if (self->frame) {
+       /* change the decors on the frame */
+       frame_adjust_size(self->frame);
+       /* with more/less decorations, we may need to be repositioned */
+       frame_adjust_position(self->frame);
+       /* with new decor, the window's maximized size may change */
+       client_remaximize(self);
+    }
+}
+
+static void client_change_allowed_actions(Client *self)
+{
+    Atom actions[9];
+    int num = 0;
+
+    actions[num++] = prop_atoms.net_wm_action_change_desktop;
+
+    if (self->functions & Func_Shade)
+       actions[num++] = prop_atoms.net_wm_action_shade;
+    if (self->functions & Func_Close)
+       actions[num++] = prop_atoms.net_wm_action_close;
+    if (self->functions & Func_Move)
+       actions[num++] = prop_atoms.net_wm_action_move;
+    if (self->functions & Func_Iconify)
+       actions[num++] = prop_atoms.net_wm_action_minimize;
+    if (self->functions & Func_Resize)
+       actions[num++] = prop_atoms.net_wm_action_resize;
+    if (self->functions & Func_Fullscreen)
+       actions[num++] = prop_atoms.net_wm_action_fullscreen;
+    if (self->functions & Func_Maximize) {
+       actions[num++] = prop_atoms.net_wm_action_maximize_horz;
+       actions[num++] = prop_atoms.net_wm_action_maximize_vert;
+    }
+
+    PROP_SET32A(self->window, net_wm_allowed_actions, atom, actions, num);
+
+    /* make sure the window isn't breaking any rules now */
+
+    if (!(self->functions & Func_Shade) && self->shaded) {
+       if (self->frame) client_shade(self, FALSE);
+       else self->shaded = FALSE;
+    }
+    if (!(self->functions & Func_Iconify) && self->iconic) {
+       if (self->frame) client_iconify(self, FALSE, TRUE);
+       else self->iconic = FALSE;
+    }
+    if (!(self->functions & Func_Fullscreen) && self->fullscreen) {
+       if (self->frame) client_fullscreen(self, FALSE, TRUE);
+       else self->fullscreen = FALSE;
+    }
+    if (!(self->functions & Func_Maximize) && (self->max_horz ||
+                                              self->max_vert)) {
+       if (self->frame) client_maximize(self, FALSE, 0, TRUE);
+       else self->max_vert = self->max_horz = FALSE;
+    }
+}
+
+void client_remaximize(Client *self)
+{
+    int dir;
+    if (self->max_horz && self->max_vert)
+       dir = 0;
+    else if (self->max_horz)
+       dir = 1;
+    else if (self->max_vert)
+       dir = 2;
+    else
+       return; /* not maximized */
+    self->max_horz = self->max_vert = FALSE;
+    client_maximize(self, TRUE, dir, FALSE);
+}
+
+void client_update_wmhints(Client *self)
+{
+    XWMHints *hints;
+    gboolean ur = FALSE;
+
+    /* assume a window takes input if it doesnt specify */
+    self->can_focus = TRUE;
+  
+    if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
+       if (hints->flags & InputHint)
+           self->can_focus = hints->input;
+
+       /* only do this when starting! */
+       if (ob_state == State_Starting && (hints->flags & StateHint))
+           self->iconic = hints->initial_state == IconicState;
+
+       if (hints->flags & XUrgencyHint)
+           ur = TRUE;
+
+       if (hints->flags & WindowGroupHint) {
+           if (hints->window_group != self->group) {
+               /* XXX: remove from the old group if there was one */
+               self->group = hints->window_group;
+               /* XXX: do stuff with the group */
+           }
+       } else /* no group! */
+           self->group = None;
+
+       if (hints->flags & IconPixmapHint) {
+           client_update_kwm_icon(self);
+           /* try get the kwm icon first, this is a fallback only */
+           if (self->pixmap_icon == None) {
+               self->pixmap_icon = hints->icon_pixmap;
+               if (hints->flags & IconMaskHint)
+                   self->pixmap_icon_mask = hints->icon_mask;
+               else
+                   self->pixmap_icon_mask = None;
+
+               if (self->frame)
+                   frame_adjust_icon(self->frame);
+           }
+       }
+
+       XFree(hints);
+    }
+
+    if (ur != self->urgent) {
+       self->urgent = ur;
+       g_message("Urgent Hint for 0x%lx: %s\n", self->window,
+                 ur ? "ON" : "OFF");
+       /* fire the urgent callback if we're mapped, otherwise, wait until
+          after we're mapped */
+       if (self->frame)
+           LOGICALHOOK(UrgentWindow, g_quark_try_string("client"), self);
+    }
+}
+
+void client_update_title(Client *self)
+{
+    gchar *data = NULL;
+
+    if (self->title != NULL)
+       g_free(self->title);
+     
+    /* try netwm */
+    if (!PROP_GETS(self->window, net_wm_name, utf8, data)) {
+       /* try old x stuff */
+       if (PROP_GETS(self->window, wm_name, string, data)) {
+           /* convert it to UTF-8 */
+           gsize r, w;
+           gchar *u;
+
+           u = g_locale_to_utf8(data, -1, &r, &w, NULL);
+           if (u == NULL) {
+               g_warning("Unable to convert string to UTF-8");
+           } else {
+               g_free(data);
+               data = u;
+           }
+       }
+    }
+
+    if (data == NULL)
+       data = g_strdup("Unnamed Window");
+
+    self->title = data;
+
+    if (self->frame)
+       frame_adjust_title(self->frame);
+}
+
+void client_update_icon_title(Client *self)
+{
+    gchar *data = NULL;
+
+    if (self->icon_title != NULL)
+       g_free(self->icon_title);
+     
+    /* try netwm */
+    if (!PROP_GETS(self->window, net_wm_icon_name, utf8, data)) {
+       /* try old x stuff */
+       if (PROP_GETS(self->window, wm_icon_name, string, data)) {
+           /* convert it to UTF-8 */
+           gsize r, w;
+           gchar *u;
+
+           u = g_locale_to_utf8(data, -1, &r, &w, NULL);
+           if (u == NULL) {
+               g_warning("Unable to convert string to UTF-8");
+           } else {
+               g_free(data);
+               data = u;
+           }
+       }
+    }
+
+    if (data == NULL)
+       data = g_strdup(self->title);
+
+    self->icon_title = data;
+}
+
+void client_update_class(Client *self)
+{
+    GPtrArray *data;
+    gchar *s;
+    guint i;
+
+    if (self->res_name) g_free(self->res_name);
+    if (self->res_class) g_free(self->res_class);
+    if (self->role) g_free(self->role);
+
+    self->res_name = self->res_class = self->role = NULL;
+
+    data = g_ptr_array_new();
+     
+    if (PROP_GETSA(self->window, wm_class, string, data)) {
+       if (data->len > 0)
+           self->res_name = g_strdup(g_ptr_array_index(data, 0));
+       if (data->len > 1)
+           self->res_class = g_strdup(g_ptr_array_index(data, 1));
+    }
+     
+    for (i = 0; i < data->len; ++i)
+       g_free(g_ptr_array_index(data, i));
+    g_ptr_array_free(data, TRUE);
+
+    if (PROP_GETS(self->window, wm_window_role, string, s))
+       self->role = g_strdup(s);
+
+    if (self->res_name == NULL) self->res_name = g_strdup("");
+    if (self->res_class == NULL) self->res_class = g_strdup("");
+    if (self->role == NULL) self->role = g_strdup("");
+}
+
+void client_update_strut(Client *self)
+{
+    gulong num = 4;
+    gulong *data;
+
+    if (PROP_GET32A(self->window, net_wm_strut, cardinal, data, num)) {
+       STRUT_SET(self->strut, data[0], data[1], data[2], data[3]);
+       g_free(data);
+    } else
+       STRUT_SET(self->strut, 0, 0, 0, 0);
+
+    /* updating here is pointless while we're being mapped cuz we're not in
+       the client list yet */
+    if (self->frame)
+       screen_update_struts();
+}
+
+void client_update_icons(Client *self)
+{
+    unsigned long num;
+    unsigned long *data;
+    unsigned long w, h, i;
+    int j;
+
+    for (j = 0; j < self->nicons; ++j)
+       g_free(self->icons[j].data);
+    if (self->nicons > 0)
+       g_free(self->icons);
+    self->nicons = 0;
+
+    if (PROP_GET32U(self->window, net_wm_icon, cardinal, data, num)) {
+       /* figure out how many valid icons are in here */
+       i = 0;
+       while (num - i > 2) {
+           w = data[i++];
+           h = data[i++];
+           i += w * h;
+           if (i > num) break;
+           ++self->nicons;
+       }
+
+       self->icons = g_new(Icon, self->nicons);
+    
+       /* store the icons */
+       i = 0;
+       for (j = 0; j < self->nicons; ++j) {
+           w = self->icons[j].w = data[i++];
+           h = self->icons[j].h = data[i++];
+           self->icons[j].data =
+               g_memdup(&data[i], w * h * sizeof(gulong));
+           i += w * h;
+           g_assert(i <= num);
+       }
+
+       g_free(data);
+    }
+
+    if (self->nicons <= 0) {
+       self->nicons = 1;
+       self->icons = g_new0(Icon, 1);
+    }
+
+    if (self->frame)
+       frame_adjust_icon(self->frame);
+}
+
+void client_update_kwm_icon(Client *self)
+{
+    Pixmap *data;
+
+    if (PROP_GET32A(self->window, kwm_win_icon, kwm_win_icon, data, 2)) {
+       self->pixmap_icon = data[0];
+       self->pixmap_icon_mask = data[1];
+       g_free(data);
+    } else {
+       self->pixmap_icon = self->pixmap_icon_mask = None;
+    }
+    if (self->frame)
+       frame_adjust_icon(self->frame);
+}
+
+static void client_change_state(Client *self)
+{
+    unsigned long state[2];
+    Atom netstate[10];
+    int num;
+
+    state[0] = self->wmstate;
+    state[1] = None;
+    PROP_SET32A(self->window, wm_state, wm_state, state, 2);
+
+    num = 0;
+    if (self->modal)
+       netstate[num++] = prop_atoms.net_wm_state_modal;
+    if (self->shaded)
+       netstate[num++] = prop_atoms.net_wm_state_shaded;
+    if (self->iconic)
+       netstate[num++] = prop_atoms.net_wm_state_hidden;
+    if (self->skip_taskbar)
+       netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
+    if (self->skip_pager)
+       netstate[num++] = prop_atoms.net_wm_state_skip_pager;
+    if (self->fullscreen)
+       netstate[num++] = prop_atoms.net_wm_state_fullscreen;
+    if (self->max_vert)
+       netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
+    if (self->max_horz)
+       netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
+    if (self->above)
+       netstate[num++] = prop_atoms.net_wm_state_above;
+    if (self->below)
+       netstate[num++] = prop_atoms.net_wm_state_below;
+    PROP_SET32A(self->window, net_wm_state, atom, netstate, num);
+
+    client_calc_layer(self);
+
+    if (self->frame)
+       frame_adjust_state(self->frame);
+}
+
+static Client *search_focus_tree(Client *node, Client *skip)
+{
+    GSList *it;
+    Client *ret;
+
+    for (it = node->transients; it != NULL; it = g_slist_next(it)) {
+       Client *c = it->data;
+       if (c == skip) continue; /* circular? */
+       if ((ret = search_focus_tree(c, skip))) return ret;
+       if (c->focused) return c;
+    }
+    return NULL;
+}
+
+void client_calc_layer(Client *self)
+{
+    StackLayer l;
+    gboolean fs;
+    Client *c;
+
+    /* are we fullscreen, or do we have a fullscreen transient parent? */
+    c = self;
+    fs = FALSE;
+    while (c) {
+       if (c->fullscreen) {
+           fs = TRUE;
+           break;
+       }
+       c = c->transient_for;
+    }
+    if (!fs && self->fullscreen) {
+       /* is one of our transients focused? */
+       c = search_focus_tree(self, self);
+       if (c != NULL) fs = TRUE;
+    }
+  
+    if (self->iconic) l = Layer_Icon;
+    else if (fs) l = Layer_Fullscreen;
+    else if (self->type == Type_Desktop) l = Layer_Desktop;
+    else if (self->type == Type_Dock) {
+       if (!self->below) l = Layer_Top;
+       else l = Layer_Normal;
+    }
+    else if (self->above) l = Layer_Above;
+    else if (self->below) l = Layer_Below;
+    else l = Layer_Normal;
+     
+    if (l != self->layer) {
+       self->layer = l;
+       if (self->frame)
+           stacking_raise(self);
+    }
+}
+
+void client_showhide(Client *self)
+{
+    gboolean show;
+
+    if (self->iconic) show = FALSE;
+    else if (!(self->desktop == screen_desktop ||
+              self->desktop == DESKTOP_ALL)) show = FALSE;
+    else if (client_normal(self) && screen_showing_desktop) show = FALSE;
+    else show = TRUE;
+
+    if (show) frame_show(self->frame);
+    else      frame_hide(self->frame);
+}
+
+gboolean client_normal(Client *self) {
+    return ! (self->type == Type_Desktop || self->type == Type_Dock ||
+             self->type == Type_Splash);
+}
+
+static void client_apply_startup_state(Client *self)
+{
+    /* these are in a carefully crafted order.. */
+
+    if (self->iconic) {
+       self->iconic = FALSE;
+       client_iconify(self, TRUE, FALSE);
+    }
+    if (self->fullscreen) {
+       self->fullscreen = FALSE;
+       client_fullscreen(self, TRUE, FALSE);
+    }
+    if (self->shaded) {
+       self->shaded = FALSE;
+       client_shade(self, TRUE);
+    }
+    if (self->urgent)
+       LOGICALHOOK(UrgentWindow, g_quark_try_string("client"), self);
+  
+    if (self->max_vert && self->max_horz) {
+       self->max_vert = self->max_horz = FALSE;
+       client_maximize(self, TRUE, 0, FALSE);
+    } else if (self->max_vert) {
+       self->max_vert = FALSE;
+       client_maximize(self, TRUE, 2, FALSE);
+    } else if (self->max_horz) {
+       self->max_horz = FALSE;
+       client_maximize(self, TRUE, 1, FALSE);
+    }
+
+    /* nothing to do for the other states:
+       skip_taskbar
+       skip_pager
+       modal
+       above
+       below
+    */
+}
+
+void client_configure(Client *self, Corner anchor, int x, int y, int w, int h,
+                     gboolean user, gboolean final)
+{
+    w -= self->base_size.width;
+    h -= self->base_size.height;
+
+    if (user) {
+       /* for interactive resizing. have to move half an increment in each
+          direction. */
+
+       /* how far we are towards the next size inc */
+       int mw = w % self->size_inc.width; 
+       int mh = h % self->size_inc.height;
+       /* amount to add */
+       int aw = self->size_inc.width / 2;
+       int ah = self->size_inc.height / 2;
+       /* don't let us move into a new size increment */
+       if (mw + aw >= self->size_inc.width)
+           aw = self->size_inc.width - mw - 1;
+       if (mh + ah >= self->size_inc.height)
+           ah = self->size_inc.height - mh - 1;
+       w += aw;
+       h += ah;
+    
+       /* if this is a user-requested resize, then check against min/max
+          sizes and aspect ratios */
+
+       /* smaller than min size or bigger than max size? */
+       if (w > self->max_size.width) w = self->max_size.width;
+       if (w < self->min_size.width) w = self->min_size.width;
+       if (h > self->max_size.height) h = self->max_size.height;
+       if (h < self->min_size.height) h = self->min_size.height;
+
+       /* adjust the height ot match the width for the aspect ratios */
+       if (self->min_ratio)
+           if (h * self->min_ratio > w) h = (int)(w / self->min_ratio);
+       if (self->max_ratio)
+           if (h * self->max_ratio < w) h = (int)(w / self->max_ratio);
+    }
+
+    /* keep to the increments */
+    w /= self->size_inc.width;
+    h /= self->size_inc.height;
+
+    /* you cannot resize to nothing */
+    if (w < 1) w = 1;
+    if (h < 1) h = 1;
+  
+    /* store the logical size */
+    SIZE_SET(self->logical_size, w, h);
+
+    w *= self->size_inc.width;
+    h *= self->size_inc.height;
+
+    w += self->base_size.width;
+    h += self->base_size.height;
+
+    switch (anchor) {
+    case Corner_TopLeft:
+       break;
+    case Corner_TopRight:
+       x -= w - self->area.width;
+       break;
+    case Corner_BottomLeft:
+       y -= h - self->area.height;
+       break;
+    case Corner_BottomRight:
+       x -= w - self->area.width;
+       y -= h - self->area.height;
+       break;
+    }
+
+    RECT_SET(self->area, x, y, w, h);
+
+    XResizeWindow(ob_display, self->window, w, h);
+
+    /* move/resize the frame to match the request */
+    if (self->frame) {
+       /* Adjust the size and then the position, as required by the EWMH */
+       frame_adjust_size(self->frame);
+       frame_adjust_position(self->frame);
+
+       if (!user || final) {
+           XEvent event;
+           event.type = ConfigureNotify;
+           event.xconfigure.display = ob_display;
+           event.xconfigure.event = self->window;
+           event.xconfigure.window = self->window;
+    
+           /* root window coords with border in mind */
+           event.xconfigure.x = x - self->border_width +
+               self->frame->size.left;
+           event.xconfigure.y = y - self->border_width +
+               self->frame->size.top;
+    
+           event.xconfigure.width = self->area.width;
+           event.xconfigure.height = self->area.height;
+           event.xconfigure.border_width = self->border_width;
+           event.xconfigure.above = self->frame->plate;
+           event.xconfigure.override_redirect = FALSE;
+           XSendEvent(event.xconfigure.display, event.xconfigure.window,
+                      FALSE, StructureNotifyMask, &event);
+       }
+    }
+}
+
+void client_fullscreen(Client *self, gboolean fs, gboolean savearea)
+{
+    static int saved_func, saved_decor;
+    int x, y, w, h;
+
+    if (!(self->functions & Func_Fullscreen) || /* can't */
+       self->fullscreen == fs) return;         /* already done */
+
+    self->fullscreen = fs;
+    client_change_state(self); /* change the state hints on the client */
+
+    if (fs) {
+       /* save the functions and remove them */
+       saved_func = self->functions;
+       self->functions &= (Func_Close | Func_Fullscreen |
+                           Func_Iconify);
+       /* save the decorations and remove them */
+       saved_decor = self->decorations;
+       self->decorations = 0;
+       if (savearea) {
+           long dimensions[4];
+           dimensions[0] = self->area.x;
+           dimensions[1] = self->area.y;
+           dimensions[2] = self->area.width;
+           dimensions[3] = self->area.height;
+  
+           PROP_SET32A(self->window, openbox_premax, cardinal,
+                       dimensions, 4);
+       }
+       x = 0;
+       y = 0;
+       w = screen_physical_size.width;
+       h = screen_physical_size.height;
+    } else {
+       long *dimensions;
+
+       self->functions = saved_func;
+       self->decorations = saved_decor;
+         
+       if (PROP_GET32A(self->window, openbox_premax, cardinal,
+                       dimensions, 4)) {
+           x = dimensions[0];
+           y = dimensions[1];
+           w = dimensions[2];
+           h = dimensions[3];
+           g_free(dimensions);
+       } else {
+           /* pick some fallbacks... */
+           x = screen_area(self->desktop)->x +
+               screen_area(self->desktop)->width / 4;
+           y = screen_area(self->desktop)->y +
+               screen_area(self->desktop)->height / 4;
+           w = screen_area(self->desktop)->width / 2;
+           h = screen_area(self->desktop)->height / 2;
+       }
+    }
+
+    client_change_allowed_actions(self); /* based on the new _functions */
+
+    /* when fullscreening, don't obey things like increments, fill the
+       screen */
+    client_configure(self, Corner_TopLeft, x, y, w, h, !fs, TRUE);
+
+    /* raise (back) into our stacking layer */
+    stacking_raise(self);
+
+    /* try focus us when we go into fullscreen mode */
+    client_focus(self);
+}
+
+void client_iconify(Client *self, gboolean iconic, gboolean curdesk)
+{
+    if (self->iconic == iconic) return; /* nothing to do */
+
+    g_message("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
+             self->window);
+
+    self->iconic = iconic;
+
+    if (iconic) {
+       self->wmstate = IconicState;
+       self->ignore_unmaps++;
+       /* we unmap the client itself so that we can get MapRequest events,
+          and because the ICCCM tells us to! */
+       XUnmapWindow(ob_display, self->window);
+    } else {
+       if (curdesk)
+           client_set_desktop(self, screen_desktop);
+       self->wmstate = self->shaded ? IconicState : NormalState;
+       XMapWindow(ob_display, self->window);
+    }
+    client_change_state(self);
+    client_showhide(self);
+    screen_update_struts();
+}
+
+void client_maximize(Client *self, gboolean max, int dir, gboolean savearea)
+{
+    int x, y, w, h;
+     
+    g_assert(dir == 0 || dir == 1 || dir == 2);
+    if (!(self->functions & Func_Maximize)) return; /* can't */
+
+    /* check if already done */
+    if (max) {
+       if (dir == 0 && self->max_horz && self->max_vert) return;
+       if (dir == 1 && self->max_horz) return;
+       if (dir == 2 && self->max_vert) return;
+    } else {
+       if (dir == 0 && !self->max_horz && !self->max_vert) return;
+       if (dir == 1 && !self->max_horz) return;
+       if (dir == 2 && !self->max_vert) return;
+    }
+
+    x = self->frame->area.x;
+    y = self->frame->area.y;
+    w = self->frame->area.width;
+    h = self->frame->area.height;
+  
+    if (max) {
+       if (savearea) {
+           long dimensions[4];
+           long *readdim;
+
+           dimensions[0] = x;
+           dimensions[1] = y;
+           dimensions[2] = w;
+           dimensions[3] = h;
+
+           /* get the property off the window and use it for the dimensions
+              we are already maxed on */
+           if (PROP_GET32A(self->window, openbox_premax, cardinal,
+                           readdim, 4)) {
+               if (self->max_horz) {
+                   dimensions[0] = readdim[0];
+                   dimensions[2] = readdim[2];
+               }
+               if (self->max_vert) {
+                   dimensions[1] = readdim[1];
+                   dimensions[3] = readdim[3];
+               }
+               g_free(readdim);
+           }
+
+           PROP_SET32A(self->window, openbox_premax, cardinal,
+                       dimensions, 4);
+       }
+       if (dir == 0 || dir == 1) { /* horz */
+           x = screen_area(self->desktop)->x;
+           w = screen_area(self->desktop)->x +
+               screen_area(self->desktop)->width;
+       }
+       if (dir == 0 || dir == 2) { /* vert */
+           y = screen_area(self->desktop)->y;
+           h = screen_area(self->desktop)->y +
+               screen_area(self->desktop)->height -
+               self->frame->size.top - self->frame->size.bottom;
+       }
+    } else {
+       long *dimensions;
+
+       if (PROP_GET32A(self->window, openbox_premax, cardinal,
+                       dimensions, 4)) {
+           if (dir == 0 || dir == 1) { /* horz */
+               x = dimensions[0];
+               w = dimensions[2];
+           }
+           if (dir == 0 || dir == 2) { /* vert */
+               y = dimensions[1];
+               h = dimensions[3];
+           }
+           g_free(dimensions);
+       } else {
+           /* pick some fallbacks... */
+           if (dir == 0 || dir == 1) { /* horz */
+               x = screen_area(self->desktop)->x +
+                   screen_area(self->desktop)->width / 4;
+               w = screen_area(self->desktop)->width / 2;
+           }
+           if (dir == 0 || dir == 2) { /* vert */
+               y = screen_area(self->desktop)->y +
+                   screen_area(self->desktop)->height / 4;
+               h = screen_area(self->desktop)->height / 2;
+           }
+       }
+    }
+
+    if (dir == 0 || dir == 1) /* horz */
+       self->max_horz = max;
+    if (dir == 0 || dir == 2) /* vert */
+       self->max_vert = max;
+
+    if (!self->max_horz && !self->max_vert)
+       PROP_ERASE(self->window, openbox_premax);
+
+    client_change_state(self); /* change the state hints on the client */
+
+    /* figure out where the client should be going */
+    frame_frame_gravity(self->frame, &x, &y);
+    client_configure(self, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
+}
+
+void client_shade(Client *self, gboolean shade)
+{
+    if (!(self->functions & Func_Shade) || /* can't */
+       self->shaded == shade) return;     /* already done */
+
+    /* when we're iconic, don't change the wmstate */
+    if (!self->iconic)
+       self->wmstate = shade ? IconicState : NormalState;
+    self->shaded = shade;
+    client_change_state(self);
+    frame_adjust_size(self->frame);
+}
+
+void client_close(Client *self)
+{
+    XEvent ce;
+
+    if (!(self->functions & Func_Close)) return;
+
+    /*
+      XXX: itd be cool to do timeouts and shit here for killing the client's
+      process off
+      like... if the window is around after 5 seconds, then the close button
+      turns a nice red, and if this function is called again, the client is
+      explicitly killed.
+    */
+
+    ce.xclient.type = ClientMessage;
+    ce.xclient.message_type =  prop_atoms.wm_protocols;
+    ce.xclient.display = ob_display;
+    ce.xclient.window = self->window;
+    ce.xclient.format = 32;
+    ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
+    ce.xclient.data.l[1] = CurrentTime;
+    ce.xclient.data.l[2] = 0l;
+    ce.xclient.data.l[3] = 0l;
+    ce.xclient.data.l[4] = 0l;
+    XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
+}
+
+void client_set_desktop(Client *self, unsigned int target)
+{
+    if (target == self->desktop) return;
+  
+    g_message("Setting desktop %u\n", target);
+
+    if (!(target < screen_num_desktops ||
+         target == DESKTOP_ALL))
+       return;
+
+    self->desktop = target;
+    PROP_SET32(self->window, net_wm_desktop, cardinal, target);
+    /* the frame can display the current desktop state */
+    frame_adjust_state(self->frame);
+    /* 'move' the window to the new desktop */
+    client_showhide(self);
+    screen_update_struts();
+}
+
+static Client *search_modal_tree(Client *node, Client *skip)
+{
+    GSList *it;
+    Client *ret;
+  
+    for (it = node->transients; it != NULL; it = it->next) {
+       Client *c = it->data;
+       if (c == skip) continue; /* circular? */
+       if ((ret = search_modal_tree(c, skip))) return ret;
+       if (c->modal) return c;
+    }
+    return NULL;
+}
+
+Client *client_find_modal_child(Client *self)
+{
+    return search_modal_tree(self, self);
+}
+
+gboolean client_validate(Client *self)
+{
+    XEvent e; 
+
+    XSync(ob_display, FALSE); /* get all events on the server */
+
+    if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
+       XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
+       XPutBackEvent(ob_display, &e);
+       return FALSE;
+    }
+
+    return FALSE;
+}
+
+void client_set_wm_state(Client *self, long state)
+{
+    if (state == self->wmstate) return; /* no change */
+  
+    switch (state) {
+    case IconicState:
+       client_iconify(self, TRUE, TRUE);
+       break;
+    case NormalState:
+       client_iconify(self, FALSE, TRUE);
+       break;
+    }
+}
+
+void client_set_state(Client *self, Atom action, long data1, long data2)
+{
+    gboolean shaded = self->shaded;
+    gboolean fullscreen = self->fullscreen;
+    gboolean max_horz = self->max_horz;
+    gboolean max_vert = self->max_vert;
+    int i;
+
+    if (!(action == prop_atoms.net_wm_state_add ||
+         action == prop_atoms.net_wm_state_remove ||
+         action == prop_atoms.net_wm_state_toggle))
+       /* an invalid action was passed to the client message, ignore it */
+       return; 
+
+    for (i = 0; i < 2; ++i) {
+       Atom state = i == 0 ? data1 : data2;
+    
+       if (!state) continue;
+
+       /* if toggling, then pick whether we're adding or removing */
+       if (action == prop_atoms.net_wm_state_toggle) {
+           if (state == prop_atoms.net_wm_state_modal)
+               action = self->modal ? prop_atoms.net_wm_state_remove :
+                   prop_atoms.net_wm_state_add;
+           else if (state == prop_atoms.net_wm_state_maximized_vert)
+               action = self->max_vert ? prop_atoms.net_wm_state_remove :
+                   prop_atoms.net_wm_state_add;
+           else if (state == prop_atoms.net_wm_state_maximized_horz)
+               action = self->max_horz ? prop_atoms.net_wm_state_remove :
+                   prop_atoms.net_wm_state_add;
+           else if (state == prop_atoms.net_wm_state_shaded)
+               action = self->shaded ? prop_atoms.net_wm_state_remove :
+                   prop_atoms.net_wm_state_add;
+           else if (state == prop_atoms.net_wm_state_skip_taskbar)
+               action = self->skip_taskbar ?
+                   prop_atoms.net_wm_state_remove :
+                   prop_atoms.net_wm_state_add;
+           else if (state == prop_atoms.net_wm_state_skip_pager)
+               action = self->skip_pager ?
+                   prop_atoms.net_wm_state_remove :
+                   prop_atoms.net_wm_state_add;
+           else if (state == prop_atoms.net_wm_state_fullscreen)
+               action = self->fullscreen ?
+                   prop_atoms.net_wm_state_remove :
+                   prop_atoms.net_wm_state_add;
+           else if (state == prop_atoms.net_wm_state_above)
+               action = self->above ? prop_atoms.net_wm_state_remove :
+                   prop_atoms.net_wm_state_add;
+           else if (state == prop_atoms.net_wm_state_below)
+               action = self->below ? prop_atoms.net_wm_state_remove :
+                   prop_atoms.net_wm_state_add;
+       }
+    
+       if (action == prop_atoms.net_wm_state_add) {
+           if (state == prop_atoms.net_wm_state_modal) {
+               /* XXX raise here or something? */
+               self->modal = TRUE;
+           } else if (state == prop_atoms.net_wm_state_maximized_vert) {
+               max_vert = TRUE;
+           } else if (state == prop_atoms.net_wm_state_maximized_horz) {
+               max_horz = TRUE;
+           } else if (state == prop_atoms.net_wm_state_shaded) {
+               shaded = TRUE;
+           } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
+               self->skip_taskbar = TRUE;
+           } else if (state == prop_atoms.net_wm_state_skip_pager) {
+               self->skip_pager = TRUE;
+           } else if (state == prop_atoms.net_wm_state_fullscreen) {
+               fullscreen = TRUE;
+           } else if (state == prop_atoms.net_wm_state_above) {
+               self->above = TRUE;
+           } else if (state == prop_atoms.net_wm_state_below) {
+               self->below = TRUE;
+           }
+
+       } else { /* action == prop_atoms.net_wm_state_remove */
+           if (state == prop_atoms.net_wm_state_modal) {
+               self->modal = FALSE;
+           } else if (state == prop_atoms.net_wm_state_maximized_vert) {
+               max_vert = FALSE;
+           } else if (state == prop_atoms.net_wm_state_maximized_horz) {
+               max_horz = FALSE;
+           } else if (state == prop_atoms.net_wm_state_shaded) {
+               shaded = FALSE;
+           } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
+               self->skip_taskbar = FALSE;
+           } else if (state == prop_atoms.net_wm_state_skip_pager) {
+               self->skip_pager = FALSE;
+           } else if (state == prop_atoms.net_wm_state_fullscreen) {
+               fullscreen = FALSE;
+           } else if (state == prop_atoms.net_wm_state_above) {
+               self->above = FALSE;
+           } else if (state == prop_atoms.net_wm_state_below) {
+               self->below = FALSE;
+           }
+       }
+    }
+    if (max_horz != self->max_horz || max_vert != self->max_vert) {
+       if (max_horz != self->max_horz && max_vert != self->max_vert) {
+           /* toggling both */
+           if (max_horz == max_vert) { /* both going the same way */
+               client_maximize(self, max_horz, 0, TRUE);
+           } else {
+               client_maximize(self, max_horz, 1, TRUE);
+               client_maximize(self, max_vert, 2, TRUE);
+           }
+       } else {
+           /* toggling one */
+           if (max_horz != self->max_horz)
+               client_maximize(self, max_horz, 1, TRUE);
+           else
+               client_maximize(self, max_vert, 2, TRUE);
+       }
+    }
+    /* change fullscreen state before shading, as it will affect if the window
+       can shade or not */
+    if (fullscreen != self->fullscreen)
+       client_fullscreen(self, fullscreen, TRUE);
+    if (shaded != self->shaded)
+       client_shade(self, shaded);
+    client_calc_layer(self);
+    client_change_state(self); /* change the hint to relect these changes */
+}
+
+gboolean client_focus(Client *self)
+{
+    XEvent ev;
+    Client *child;
+     
+    /* if we have a modal child, then focus it, not us */
+    child = client_find_modal_child(self);
+    if (child)
+       return client_focus(child);
+
+    /* won't try focus if the client doesn't want it, or if the window isn't
+       visible on the screen */
+    if (!(self->frame->visible &&
+         (self->can_focus || self->focus_notify)))
+       return FALSE;
+
+    /* do a check to see if the window has already been unmapped or destroyed
+       do this intelligently while watching out for unmaps we've generated
+       (ignore_unmaps > 0) */
+    if (XCheckTypedWindowEvent(ob_display, self->window,
+                              DestroyNotify, &ev)) {
+       XPutBackEvent(ob_display, &ev);
+       return FALSE;
+    }
+    while (XCheckTypedWindowEvent(ob_display, self->window,
+                                 UnmapNotify, &ev)) {
+       if (self->ignore_unmaps) {
+           self->ignore_unmaps--;
+       } else {
+           XPutBackEvent(ob_display, &ev);
+           return FALSE;
+       }
+    }
+     
+    if (self->can_focus)
+       XSetInputFocus(ob_display, self->window, RevertToNone, CurrentTime);
+
+    if (self->focus_notify) {
+       XEvent ce;
+       ce.xclient.type = ClientMessage;
+       ce.xclient.message_type = prop_atoms.wm_protocols;
+       ce.xclient.display = ob_display;
+       ce.xclient.window = self->window;
+       ce.xclient.format = 32;
+       ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
+       ce.xclient.data.l[1] = event_lasttime;
+       ce.xclient.data.l[2] = 0l;
+       ce.xclient.data.l[3] = 0l;
+       ce.xclient.data.l[4] = 0l;
+       XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
+    }
+
+    /*XSync(ob_display, FALSE); XXX Why sync? */
+    return TRUE;
+}
+
+void client_unfocus(Client *self)
+{
+    g_assert(focus_client == self);
+    focus_set_client(NULL);
+}
diff --git a/c/client.h b/c/client.h
new file mode 100644 (file)
index 0000000..d0f5266
--- /dev/null
@@ -0,0 +1,401 @@
+#ifndef __client_h
+#define __client_h
+
+#include "geom.h"
+#include "obexport.h"
+#include <glib.h>
+#include <X11/Xlib.h>
+
+struct ClientWrap;
+struct Frame;
+
+
+/*! Holds an icon in ARGB format */
+typedef struct Icon {
+    unsigned long w, h;
+    unsigned long *data;
+} Icon;
+     
+/*! The MWM Hints as retrieved from the window property
+  This structure only contains 3 elements, even though the Motif 2.0
+  structure contains 5. We only use the first 3, so that is all gets
+  defined.
+*/
+typedef struct MwmHints {
+    /*! A bitmask of Client::MwmFlags values */
+    unsigned long flags;
+    /*! A bitmask of Client::MwmFunctions values */
+    unsigned long functions;
+    /*! A bitmask of Client::MwmDecorations values */
+    unsigned long decorations;
+} MwmHints;
+/*! The number of elements in the Client::MwmHints struct */
+#define MWM_ELEMENTS 3
+     
+/*! Possible flags for MWM Hints (defined by Motif 2.0) */
+typedef enum {
+    MwmFlag_Functions   = 1 << 0, /*!< The MMW Hints define funcs */
+    MwmFlag_Decorations = 1 << 1  /*!< The MWM Hints define decor */
+} MwmFlags;
+
+/*! Possible functions for MWM Hints (defined by Motif 2.0) */
+typedef enum {
+    MwmFunc_All      = 1 << 0, /*!< All functions */
+    MwmFunc_Resize   = 1 << 1, /*!< Allow resizing */
+    MwmFunc_Move     = 1 << 2, /*!< Allow moving */
+    MwmFunc_Iconify  = 1 << 3, /*!< Allow to be iconfied */
+    MwmFunc_Maximize = 1 << 4  /*!< Allow to be maximized */
+    /*MwmFunc_Close    = 1 << 5 /!< Allow to be closed */
+} MwmFunctions;
+
+/*! Possible decorations for MWM Hints (defined by Motif 2.0) */
+typedef enum {
+    MwmDecor_All      = 1 << 0, /*!< All decorations */
+    MwmDecor_Border   = 1 << 1, /*!< Show a border */
+    MwmDecor_Handle   = 1 << 2, /*!< Show a handle (bottom) */
+    MwmDecor_Title    = 1 << 3, /*!< Show a titlebar */
+    /*MwmDecor_Menu     = 1 << 4, /!< Show a menu */
+    MwmDecor_Iconify  = 1 << 5, /*!< Show an iconify button */
+    MwmDecor_Maximize = 1 << 6  /*!< Show a maximize button */
+} MemDecorations;
+
+
+typedef struct Client {
+    Window  window;
+
+    struct Frame *frame;
+
+    /*! The number of unmap events to ignore on the window */
+    int ignore_unmaps;
+
+    /*! The id of the group the window belongs to */
+    Window  group;
+    /*! Whether or not the client is a transient window. This is guaranteed to 
+      be TRUE if transient_for != NULL, but not guaranteed to be FALSE if
+      transient_for == NULL. */
+    gboolean transient;
+    /*! The client which this client is a transient (child) for */
+    struct Client *transient_for;
+    /*! The clients which are transients (children) of this client */
+    GSList *transients;
+    /*! The desktop on which the window resides (0xffffffff for all
+      desktops) */
+    unsigned int desktop;
+
+    /*! Normal window title */
+    gchar *title;
+    /*! Window title when iconified */
+    gchar *icon_title;
+
+    /*! The application that created the window */
+    gchar *res_name;
+    /*! The class of the window, can used for grouping */
+    gchar *res_class;
+    /*! The specified role of the window, used for identification */
+    gchar *role;
+
+    /*! The type of window (what its function is) */
+    WindowType type;
+
+    /*! Position and size of the window
+      This will not always be the actual position of the window on screen, it
+      is, rather, the position requested by the client, to which the window's
+      gravity is applied.
+    */
+    Rect    area;
+
+    /*! The window's strut
+      The strut defines areas of the screen that are marked off-bounds for
+      window placement. In theory, where this window exists.
+    */
+    Strut   strut;
+     
+    /*! The logical size of the window
+      The "logical" size of the window is refers to the user's perception of
+      the size of the window, and is the value that should be displayed to the
+      user. For example, with xterms, this value it the number of characters
+      being displayed in the terminal, instead of the number of pixels.
+    */
+    Size   logical_size;
+
+    /*! Width of the border on the window.
+      The window manager will set this to 0 while the window is being managed,
+      but needs to restore it afterwards, so it is saved here.
+    */
+    guint border_width;
+
+    /*! The minimum aspect ratio the client window can be sized to.
+      A value of 0 means this is ignored.
+    */
+    float min_ratio;
+    /*! The maximum aspect ratio the client window can be sized to.
+      A value of 0 means this is ignored.
+    */
+    float max_ratio;
+  
+    /*! The minimum size of the client window
+      If the min is > the max, then the window is not resizable
+    */
+    Size min_size;
+    /*! The maximum size of the client window
+      If the min is > the max, then the window is not resizable
+    */
+    Size max_size;
+    /*! The size of increments to resize the client window by */
+    Size size_inc;
+    /*! The base size of the client window
+      This value should be subtracted from the window's actual size when
+      displaying its size to the user, or working with its min/max size
+    */
+    Size base_size;
+
+    /*! Window decoration and functionality hints */
+    MwmHints mwmhints;
+  
+    /*! Where to place the decorated window in relation to the undecorated
+      window */
+    int gravity;
+
+    /*! The state of the window, one of WithdrawnState, IconicState, or
+      NormalState */
+    long wmstate;
+
+    /*! True if the client supports the delete_window protocol */
+    gboolean delete_window;
+  
+    /*! Was the window's position requested by the application? if not, we
+      should place the window ourselves when it first appears */
+    gboolean positioned;
+  
+    /*! Can the window receive input focus? */
+    gboolean can_focus;
+    /*! Urgency flag */
+    gboolean urgent;
+    /*! Notify the window when it receives focus? */
+    gboolean focus_notify;
+    /*! Does the client window have the input focus? */
+    gboolean focused;
+
+    /*! The window uses shape extension to be non-rectangular? */
+    gboolean shaped;
+
+    /*! The window is modal, so it must be processed before any windows it is
+      related to can be focused */
+    gboolean modal;
+    /*! Only the window's titlebar is displayed */
+    gboolean shaded;
+    /*! The window is iconified */
+    gboolean iconic;
+    /*! The window is maximized to fill the screen vertically */
+    gboolean max_vert;
+    /*! The window is maximized to fill the screen horizontally */
+    gboolean max_horz;
+    /*! The window should not be displayed by pagers */
+    gboolean skip_pager;
+    /*! The window should not be displayed by taskbars */
+    gboolean skip_taskbar;
+    /*! The window is a 'fullscreen' window, and should be on top of all
+      others */
+    gboolean fullscreen;
+    /*! The window should be on top of other windows of the same type */
+    gboolean above;
+    /*! The window should be underneath other windows of the same type */
+    gboolean below;
+
+    /*! The layer in which the window will be stacked, windows in lower layers
+      are always below windows in higher layers. */
+    StackLayer layer;
+
+    /*! A bitmask of values in the Decoration enum
+      The values in the variable are the decorations that the client wants to
+      be displayed around it.
+    */
+    int decorations;
+
+    /*! A bitmask of values in the Decoration enum.
+      Specifies the decorations that should NOT be displayed on the client.
+    */
+    int disabled_decorations;
+
+    /*! A bitmask of values in the Function enum
+      The values in the variable specify the ways in which the user is allowed
+      to modify this window.
+    */
+    int functions;
+
+    /*! Icons for the client as specified on the client window */
+    Icon *icons;
+    /*! The number of icons in icons */
+    int nicons;
+
+    /*! The icon for the client specified in the WMHints or the KWM hints */
+    Pixmap pixmap_icon;
+    /*! The mask for the pixmap_icon, or None if its not masked */
+    Pixmap pixmap_icon_mask;
+
+    /* The instance of the wrapper class if one exists */
+    struct ClientWrap *wrap;
+} Client;
+
+extern GSList *client_list;
+extern GHashTable *client_map;
+
+void client_startup();
+void client_shutdown();
+
+/*! Manages all existing windows */
+void client_manage_all();
+/*! Manages a given window */
+void client_manage(Window win);
+/*! Unmanages all managed windows */
+void client_unmanage_all();
+/*! Unmanages a given client */
+void client_unmanage(Client *client);
+
+/*! Sets the client list on the root window from the client_list */
+void client_set_list();
+
+/*! Reapplies the maximized state to the window
+  Use this to make the window readjust its maximized size to new
+  surroundings (struts, etc). */
+void client_remaximize(Client *self);
+
+/*! Shows the window if it should be shown, or hides it
+  Used when changing desktops, the window's state, etc. */
+void client_showhide(Client *self);
+
+/*! Returns if the window should be treated as a normal window.
+  Some windows (desktops, docks, splash screens) have special rules applied
+  to them in a number of places regarding focus or user interaction. */
+gboolean client_normal(Client *self);
+
+/*! Internal version of the Client::resize function
+  This also maintains things like the client's minsize, and size increments.
+  @param anchor The corner to keep in the same position when resizing.
+  @param x The x coordiante of the new position for the client.
+  @param y The y coordiante of the new position for the client.
+  @param w The width component of the new size for the client.
+  @param h The height component of the new size for the client.
+  @param user Specifies whether this is a user-requested change or a
+              program requested change. For program requested changes, the
+             constraints are not checked.
+  @param final If user is true, then this should specify if this is a final
+               configuration. e.g. Final should be FALSE if doing an
+              interactive move/resize, and then be TRUE for the last call
+              only.
+*/
+void client_configure(Client *self, Corner anchor, int x, int y, int w, int h,
+                     gboolean user, gboolean final);
+
+/*! Fullscreen's or unfullscreen's the client window
+  @param fs true if the window should be made fullscreen; false if it should
+            be returned to normal state.
+  @param savearea true to have the client's current size and position saved;
+                  otherwise, they are not. You should not save when mapping a
+                 new window that is set to fullscreen. This has no effect
+                 when restoring a window from fullscreen.
+*/
+void client_fullscreen(Client *self, gboolean fs, gboolean savearea);
+
+/*! Iconifies or uniconifies the client window
+  @param iconic true if the window should be iconified; false if it should be
+                restored.
+  @param curdesk If iconic is FALSE, then this determines if the window will
+                 be uniconified to the current viewable desktop (true) or to
+                its previous desktop (false)
+*/
+void client_iconify(Client *self, gboolean iconic, gboolean curdesk);
+
+/*! Maximize or unmaximize the client window
+  @param max true if the window should be maximized; false if it should be
+             returned to normal size.
+  @param dir 0 to set both horz and vert, 1 to set horz, 2 to set vert.
+  @param savearea true to have the client's current size and position saved;
+                  otherwise, they are not. You should not save when mapping a
+                 new window that is set to fullscreen. This has no effect
+                 when unmaximizing a window.
+*/
+void client_maximize(Client *self, gboolean max, int dir,
+                    gboolean savearea);
+
+/*! Shades or unshades the client window
+  @param shade true if the window should be shaded; false if it should be
+               unshaded.
+*/
+void client_shade(Client *self, gboolean shade);
+
+/*! Request the client to close its window. */
+void client_close(Client *self);
+
+/*! Sends the window to the specified desktop */
+void client_set_desktop(Client *self, unsigned int target);
+
+/*! Return a modal child of the client window
+    @return A modal child of the client window, or 0 if none was found.
+*/
+Client *client_find_modal_child(Client *self);
+
+/*! Validate client, by making sure no Destroy or Unmap events exist in
+  the event queue for the window.
+  @return true if the client is valid; false if the client has already
+          been unmapped/destroyed, and so is invalid.
+*/
+gboolean client_validate(Client *self);
+
+/*! Sets the wm_state to the specified value */
+void client_set_wm_state(Client *self, long state);
+
+/*! Adjusts the window's net_state
+  This should not be called as part of the window mapping process! It is for
+  use when updating the state post-mapping.<br>
+  client_apply_startup_state is used to do the same things during the mapping
+  process.
+*/
+void client_set_state(Client *self, Atom action, long data1, long data2);
+
+/*! Attempt to focus the client window */
+gboolean client_focus(Client *self);
+
+/*! Remove focus from the client window */
+void client_unfocus(Client *self);
+
+/*! Calculates the stacking layer for the client window */
+void client_calc_layer(Client *self);
+
+/*! Updates the window's transient status, and any parents of it */
+void client_update_transient_for(Client *self);
+/*! Update the protocols that the window supports and adjusts things if they
+  change */
+void client_update_protocols(Client *self);
+/*! Updates the WMNormalHints and adjusts things if they change */
+void client_update_normal_hints(Client *self);
+
+/*! Updates the WMHints and adjusts things if they change
+  @param initstate Whether to read the initial_state property from the
+                   WMHints. This should only be used during the mapping
+                  process.
+*/
+void client_update_wmhints(Client *self);
+/*! Updates the window's title */
+void client_update_title(Client *self);
+/*! Updates the window's icon title */
+void client_update_icon_title(Client *self);
+/*! Updates the window's application name and class */
+void client_update_class(Client *self);
+/*! Updates the strut for the client */
+void client_update_strut(Client *self);
+/*! Updates the window's icons */
+void client_update_icons(Client *self);
+/*! Updates the window's kwm icon */
+void client_update_kwm_icon(Client *self);
+
+/*! Set up what decor should be shown on the window and what functions should
+  be allowed (Client::_decorations and Client::_functions).
+  This also updates the NET_WM_ALLOWED_ACTIONS hint.
+*/
+void client_setup_decor_and_functions(Client *self);
+
+/*! Retrieves the window's type and sets Client->type */
+void client_get_type(Client *self);
+
+#endif
diff --git a/c/clientwrap.c b/c/clientwrap.c
new file mode 100644 (file)
index 0000000..1ea5c9f
--- /dev/null
@@ -0,0 +1,792 @@
+#include "clientwrap.h"
+#include "client.h"
+#include "frame.h"
+#include "stacking.h"
+#include "focus.h"
+#include <glib.h>
+
+/***************************************************************************
+   Define the type 'ClientWrap'
+
+ ***************************************************************************/
+
+#define IS_CWRAP(v)  ((v)->ob_type == &ClientWrapType)
+#define IS_VALID_CWRAP(v) ((v)->client != NULL)
+#define CHECK_CWRAP(self, funcname) { \
+    if (!IS_CWRAP(self)) { \
+        PyErr_SetString(PyExc_TypeError, \
+                       "descriptor '" funcname "' requires a 'Client' " \
+                       "object"); \
+       return NULL; \
+    } \
+    if (!IS_VALID_CWRAP(self)) { \
+        PyErr_SetString(PyExc_ValueError, \
+                       "This 'Client' is wrapping a client which no longer "\
+                       "exists."); \
+        return NULL; \
+    } \
+}
+
+
+staticforward PyTypeObject ClientWrapType;
+
+/***************************************************************************
+   Attribute methods
+ ***************************************************************************/
+
+static PyObject *cwrap_window(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "window");
+    if (!PyArg_ParseTuple(args, ":window"))
+       return NULL;
+    return PyInt_FromLong(self->client->window);
+}
+
+static PyObject *cwrap_group(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "group");
+    if (!PyArg_ParseTuple(args, ":group"))
+       return NULL;
+    return PyInt_FromLong(self->client->group);
+}
+
+static PyObject *cwrap_parent(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "parent");
+    if (!PyArg_ParseTuple(args, ":parent"))
+       return NULL;
+    if (self->client->transient_for != NULL)
+       return clientwrap_new(self->client->transient_for);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *cwrap_children(ClientWrap *self, PyObject *args)
+{
+    PyObject *list;
+    GSList *it;
+    guint i, s;
+
+    CHECK_CWRAP(self, "children");
+    if (!PyArg_ParseTuple(args, ":children"))
+       return NULL;
+    s = g_slist_length(self->client->transients);
+    list = PyList_New(s);
+    for (i = 0, it = self->client->transients; i < s; ++i, it = it->next)
+       PyList_SET_ITEM(list, i, clientwrap_new(it->data));
+    return list;
+}
+
+static PyObject *cwrap_desktop(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "desktop");
+    if (!PyArg_ParseTuple(args, ":desktop"))
+       return NULL;
+    return PyInt_FromLong(self->client->desktop);
+}
+
+static PyObject *cwrap_title(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "title");
+    if (!PyArg_ParseTuple(args, ":title"))
+       return NULL;
+    return PyString_FromString(self->client->title);
+}
+
+static PyObject *cwrap_iconTitle(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "iconTitle");
+    if (!PyArg_ParseTuple(args, ":iconTitle"))
+       return NULL;
+    return PyString_FromString(self->client->icon_title);
+}
+
+static PyObject *cwrap_resName(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "resName");
+    if (!PyArg_ParseTuple(args, ":resName"))
+       return NULL;
+    return PyString_FromString(self->client->res_name);
+}
+
+static PyObject *cwrap_resClass(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "resClass");
+    if (!PyArg_ParseTuple(args, ":resClass"))
+       return NULL;
+    return PyString_FromString(self->client->res_class);
+}
+
+static PyObject *cwrap_role(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "role");
+    if (!PyArg_ParseTuple(args, ":role"))
+       return NULL;
+    return PyString_FromString(self->client->role);
+}
+
+static PyObject *cwrap_type(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "type");
+    if (!PyArg_ParseTuple(args, ":type"))
+       return NULL;
+    return PyInt_FromLong(self->client->type);
+}
+
+static PyObject *cwrap_area(ClientWrap *self, PyObject *args)
+{
+    PyObject *tuple;
+
+    CHECK_CWRAP(self, "area");
+    if (!PyArg_ParseTuple(args, ":area"))
+       return NULL;
+    tuple = PyTuple_New(4);
+    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(self->client->area.x));
+    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(self->client->area.y));
+    PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(self->client->area.width));
+    PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(self->client->area.height));
+    return tuple;
+}
+
+static PyObject *cwrap_screenArea(ClientWrap *self, PyObject *args)
+{
+    PyObject *tuple;
+
+    CHECK_CWRAP(self, "screenArea");
+    if (!PyArg_ParseTuple(args, ":screenArea"))
+       return NULL;
+    tuple = PyTuple_New(4);
+    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(self->client->frame->area.x));
+    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(self->client->frame->area.y));
+    PyTuple_SET_ITEM(tuple, 2,
+                    PyInt_FromLong(self->client->frame->area.width));
+    PyTuple_SET_ITEM(tuple, 3,
+                    PyInt_FromLong(self->client->frame->area.height));
+    return tuple;
+}
+
+static PyObject *cwrap_strut(ClientWrap *self, PyObject *args)
+{
+    PyObject *tuple;
+
+    CHECK_CWRAP(self, "strut");
+    if (!PyArg_ParseTuple(args, ":strut"))
+       return NULL;
+    tuple = PyTuple_New(4);
+    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(self->client->strut.left));
+    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(self->client->strut.top));
+    PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(self->client->strut.right));
+    PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(self->client->strut.bottom));
+    return tuple;
+}
+
+static PyObject *cwrap_logicalSize(ClientWrap *self, PyObject *args)
+{
+    PyObject *tuple;
+
+    CHECK_CWRAP(self, "logicalSize");
+    if (!PyArg_ParseTuple(args, ":logicalSize"))
+       return NULL;
+    tuple = PyTuple_New(2);
+    PyTuple_SET_ITEM(tuple, 0,
+                    PyInt_FromLong(self->client->logical_size.width));
+    PyTuple_SET_ITEM(tuple, 1,
+                    PyInt_FromLong(self->client->logical_size.height));
+    return tuple;
+}
+
+static PyObject *cwrap_minRatio(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "minRatio");
+    if (!PyArg_ParseTuple(args, ":minRatio"))
+       return NULL;
+    return PyFloat_FromDouble(self->client->min_ratio);
+}
+
+static PyObject *cwrap_maxRatio(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "maxRatio");
+    if (!PyArg_ParseTuple(args, ":maxRatio"))
+       return NULL;
+    return PyFloat_FromDouble(self->client->max_ratio);
+}
+
+static PyObject *cwrap_minSize(ClientWrap *self, PyObject *args)
+{
+    PyObject *tuple;
+
+    CHECK_CWRAP(self, "minSize");
+    if (!PyArg_ParseTuple(args, ":minSize"))
+       return NULL;
+    tuple = PyTuple_New(2);
+    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(self->client->min_size.width));
+    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(self->client->min_size.height));
+    return tuple;
+}
+
+static PyObject *cwrap_maxSize(ClientWrap *self, PyObject *args)
+{
+    PyObject *tuple;
+
+    CHECK_CWRAP(self, "maxSize");
+    if (!PyArg_ParseTuple(args, ":maxSize"))
+       return NULL;
+    tuple = PyTuple_New(2);
+    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(self->client->max_size.width));
+    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(self->client->max_size.height));
+    return tuple;
+}
+
+static PyObject *cwrap_sizeIncrement(ClientWrap *self, PyObject *args)
+{
+    PyObject *tuple;
+
+    CHECK_CWRAP(self, "sizeIncrement");
+    if (!PyArg_ParseTuple(args, ":sizeIncrement"))
+       return NULL;
+    tuple = PyTuple_New(2);
+    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(self->client->size_inc.width));
+    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(self->client->size_inc.height));
+    return tuple;
+}
+
+static PyObject *cwrap_baseSize(ClientWrap *self, PyObject *args)
+{
+    PyObject *tuple;
+
+    CHECK_CWRAP(self, "baseSize");
+    if (!PyArg_ParseTuple(args, ":baseSize"))
+       return NULL;
+    tuple = PyTuple_New(2);
+    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(self->client->base_size.width));
+    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(self->client->base_size.height));
+    return tuple;
+}
+
+static PyObject *cwrap_gravity(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "gravity");
+    if (!PyArg_ParseTuple(args, ":gravity"))
+       return NULL;
+    return PyInt_FromLong(self->client->gravity);
+}
+
+static PyObject *cwrap_canClose(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "canClose");
+    if (!PyArg_ParseTuple(args, ":canClose"))
+       return NULL;
+    return PyInt_FromLong(self->client->delete_window ? 1 : 0);
+}
+
+static PyObject *cwrap_positionRequested(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "positionRequested");
+    if (!PyArg_ParseTuple(args, ":positionRequested"))
+       return NULL;
+    return PyInt_FromLong(self->client->positioned ? 1 : 0);
+}
+
+static PyObject *cwrap_canFocus(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "canFocus");
+    if (!PyArg_ParseTuple(args, ":canFocus"))
+       return NULL;
+    return PyInt_FromLong(self->client->can_focus ||
+                         self->client->focus_notify);
+}
+
+static PyObject *cwrap_urgent(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "urgent");
+    if (!PyArg_ParseTuple(args, ":urgent"))
+       return NULL;
+    return PyInt_FromLong(self->client->urgent ? 1 : 0);
+}
+
+static PyObject *cwrap_focused(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "focused");
+    if (!PyArg_ParseTuple(args, ":focused"))
+       return NULL;
+    return PyInt_FromLong(self->client->focused ? 1 : 0);
+}
+
+static PyObject *cwrap_modal(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "modal");
+    if (!PyArg_ParseTuple(args, ":modal"))
+       return NULL;
+    return PyInt_FromLong(self->client->modal ? 1 : 0);
+}
+
+static PyObject *cwrap_shaded(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "shaded");
+    if (!PyArg_ParseTuple(args, ":shaded"))
+       return NULL;
+    return PyInt_FromLong(self->client->shaded ? 1 : 0);
+}
+
+static PyObject *cwrap_iconic(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "iconic");
+    if (!PyArg_ParseTuple(args, ":iconc"))
+       return NULL;
+    return PyInt_FromLong(self->client->iconic ? 1 : 0);
+}
+
+static PyObject *cwrap_maximizedVertical(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "maximizedVertical");
+    if (!PyArg_ParseTuple(args, ":maximizedVertical"))
+       return NULL;
+    return PyInt_FromLong(self->client->max_vert ? 1 : 0);
+}
+
+static PyObject *cwrap_maximizedHorizontal(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "maximizedHorizontal");
+    if (!PyArg_ParseTuple(args, ":maximizedHorizontal"))
+       return NULL;
+    return PyInt_FromLong(self->client->max_horz ? 1 : 0);
+}
+
+static PyObject *cwrap_skipPager(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "skipPager");
+    if (!PyArg_ParseTuple(args, ":skipPager"))
+       return NULL;
+    return PyInt_FromLong(self->client->skip_pager ? 1 : 0);
+}
+
+static PyObject *cwrap_skipTaskbar(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "skipTaskbar");
+    if (!PyArg_ParseTuple(args, ":skipTaskbar"))
+       return NULL;
+    return PyInt_FromLong(self->client->skip_taskbar ? 1 : 0);
+}
+
+static PyObject *cwrap_fullscreen(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "fullscreen");
+    if (!PyArg_ParseTuple(args, ":fullscreen"))
+       return NULL;
+    return PyInt_FromLong(self->client->fullscreen ? 1 : 0);
+}
+
+static PyObject *cwrap_above(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "above");
+    if (!PyArg_ParseTuple(args, ":above"))
+       return NULL;
+    return PyInt_FromLong(self->client->above ? 1 : 0);
+}
+
+static PyObject *cwrap_below(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "below");
+    if (!PyArg_ParseTuple(args, ":below"))
+       return NULL;
+    return PyInt_FromLong(self->client->below ? 1 : 0);
+}
+
+static PyObject *cwrap_layer(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "layer");
+    if (!PyArg_ParseTuple(args, ":layer"))
+       return NULL;
+    return PyInt_FromLong(self->client->layer);
+}
+
+static PyObject *cwrap_decorations(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "decorations");
+    if (!PyArg_ParseTuple(args, ":decorations"))
+       return NULL;
+    return PyInt_FromLong(self->client->decorations);
+}
+
+static PyObject *cwrap_disabledDecorations(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "disabledDecorations");
+    if (!PyArg_ParseTuple(args, ":disabledDecorations"))
+       return NULL;
+    return PyInt_FromLong(self->client->disabled_decorations);
+}
+
+static PyObject *cwrap_functions(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "functions");
+    if (!PyArg_ParseTuple(args, ":functions"))
+       return NULL;
+    return PyInt_FromLong(self->client->functions);
+}
+
+static PyObject *cwrap_visible(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "visible");
+    if (!PyArg_ParseTuple(args, ":visible"))
+       return NULL;
+    return PyInt_FromLong(self->client->frame->visible ? 1 : 0);
+}
+
+static PyObject *cwrap_decorationSize(ClientWrap *self, PyObject *args)
+{
+    PyObject *tuple;
+
+    CHECK_CWRAP(self, "decorationSize");
+    if (!PyArg_ParseTuple(args, ":decorationSize"))
+       return NULL;
+    tuple = PyTuple_New(4);
+    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(self->client->frame->size.left));
+    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(self->client->frame->size.top));
+    PyTuple_SET_ITEM(tuple, 2,
+                    PyInt_FromLong(self->client->frame->size.right));
+    PyTuple_SET_ITEM(tuple, 3,
+                    PyInt_FromLong(self->client->frame->size.bottom));
+    return tuple;
+}
+
+static PyObject *cwrap_normal(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "normal");
+    if (!PyArg_ParseTuple(args, ":normal"))
+       return NULL;
+    return PyInt_FromLong(client_normal(self->client) ? 1 : 0);
+}
+
+static PyObject *cwrap_setVisible(ClientWrap *self, PyObject *args)
+{
+    int i;
+
+    CHECK_CWRAP(self, "setVisible");
+    if (!PyArg_ParseTuple(args, "i:setVisible", &i))
+       return NULL;
+    if (i)
+       frame_show(self->client->frame);
+    else
+       frame_hide(self->client->frame);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *cwrap_raiseWindow(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "raiseWindow");
+    if (!PyArg_ParseTuple(args, ":raiseWindow"))
+       return NULL;
+    stacking_raise(self->client);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *cwrap_lowerWindow(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "lowerWindow");
+    if (!PyArg_ParseTuple(args, ":lowerWindow"))
+       return NULL;
+    stacking_lower(self->client);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *cwrap_focus(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "focus");
+    if (!PyArg_ParseTuple(args, ":focus"))
+       return NULL;
+    return PyInt_FromLong(client_focus(self->client) ? 1 : 0);
+}
+
+static PyObject *cwrap_unfocus(ClientWrap *self, PyObject *args)
+{
+    CHECK_CWRAP(self, "unfocus");
+    if (!PyArg_ParseTuple(args, ":unfocus"))
+       return NULL;
+    client_unfocus(self->client);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *cwrap_move(ClientWrap *self, PyObject *args)
+{
+    int x, y;
+    int final = TRUE;
+    CHECK_CWRAP(self, "move");
+    if (!PyArg_ParseTuple(args, "ii|i:unfocus", &x, &y, &final))
+       return NULL;
+    /* get the client's position based on x,y for the frame */
+    frame_frame_gravity(self->client->frame, &x, &y); 
+
+    client_configure(self->client, Corner_TopLeft, x, y,
+                    self->client->area.width, self->client->area.height,
+                    TRUE, final);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+#define ATTRMETH(n, d) {#n, (PyCFunction)cwrap_##n, METH_VARARGS, #d}
+
+static PyMethodDef ClientWrapMethods[] = {
+    ATTRMETH(window,
+            "c.window() -- Returns the window id for the Client."),
+    ATTRMETH(group,
+            "c.group() -- Returns the group id for the Client."),
+    ATTRMETH(parent,
+            "c.parent() -- Returns the parent Client for the Client, or the "
+            "Client for which this Client is a transient. Returns None if it "
+            "is not a transient."),
+    ATTRMETH(children,
+            "c.parent() -- Returns a list of child Clients for the Client, "
+            "or the Clients transients."),
+    ATTRMETH(desktop,
+            "c.desktop() -- Returns the desktop on which the Client resides, "
+            "or 0xffffffff for 'all desktops'."),
+    ATTRMETH(title,
+            "c.title() -- Returns the Client's title string. This is in "
+            "UTF-8 encoding."),
+    ATTRMETH(iconTitle,
+            "c.iconTitle() -- Returns the Client's icon title string. This "
+            "is in UTF-8 encoding."),
+    ATTRMETH(resName,
+            "c.resName() -- Returns the application's specified resource "
+            "name."),
+    ATTRMETH(resClass,
+            "c.resClass() -- Returns the application's specified resource "
+            "class."),
+    ATTRMETH(role,
+            "c.role() -- Returns the window's role, which should be unique "
+            "for every window of an application, if it is not empty."),
+    ATTRMETH(type,
+            "c.type() -- Returns the window's type, one of the ob.Type_ "
+            "constants. This is the logical type of the window."),
+    ATTRMETH(area,
+            "c.area() -- Returns the area rectangle for the Client in a "
+            "tuple. The tuple's format is (x, y, width, height). This is "
+            "not the area on-screen that the Client and frame occupies, but "
+            "rather, the position and size that the Client has requested, "
+            "before its gravity() and decorations have been applied. You "
+            "should use the c.screenArea() to get the actual on-screen area "
+            "for the Client."),
+    ATTRMETH(screenArea,
+            "c.screenArea() -- Returns the on-screen area rectangle for the "
+            "Client in a tuple. The tuple's format is (x, y, width, height). "
+            "This is the actual position and size of the Client plus its "
+            "decorations."),
+    ATTRMETH(strut,
+            "c.strut() -- Returns the strut requested by the Client in a "
+            "tuple. The format of the tuple is (left, top, right, bottom)."),
+    ATTRMETH(logicalSize,
+            "c.logicalSize() -- Returns the logical size of the Client. This "
+            "is the 'user friendly' size for the Client, such as for an "
+            "xterm, it will be the number of characters in the xterm "
+            "instead of the number of pixels it takes up."),
+    ATTRMETH(minRatio,
+            "c.minRatio() -- Returns the minimum width:height ratio for "
+            "the Client. A value of 0 implies no ratio enforcement."),
+    ATTRMETH(maxRatio,
+            "c.maxRatio() -- Returns the maximum width:height ratio for "
+            "the Client. A value of 0 implies no ratio enforcement."),
+    ATTRMETH(minSize,
+            "c.minSize() -- Returns the minimum size of the Client."),
+    ATTRMETH(maxSize,
+            "c.maxSize() -- Returns the maximum size of the Client."),
+    ATTRMETH(sizeIncrement,
+            "c.sizeIncrement() -- Returns the size increments in which the "
+            "Client must be resized."),
+    ATTRMETH(baseSize,
+            "c.baseSize() -- Returns the base size of the Client, which is "
+            "subtracted from the Client's size before comparing to its "
+            "various sizing constraints."),
+    ATTRMETH(gravity,
+            "c.gravity() -- Returns the gravity for the Client. One of the "
+            "ob.Gravity_ constants."),
+    ATTRMETH(canClose,
+            "c.canClose() -- Returns whether or not the Client provides a "
+            "means for Openbox to request that it close."),
+    ATTRMETH(positionRequested,
+            "c.positionRequested() -- Returns whether or not the Client has "
+            "requested a specified position on screen. When it has it should "
+            "probably not be placed using an algorithm when it is managed."),
+    ATTRMETH(canFocus,
+            "c.canFocus() -- Returns whether or not the Client can be "
+            "given input focus."),
+    ATTRMETH(urgent,
+            "c.urgent() -- Returns the urgent state of the window (on/off)."),
+    ATTRMETH(focused,
+            "c.focused() -- Returns whether or not the Client has the input "
+            "focus."),
+    ATTRMETH(modal,
+            "c.modal() -- Returns whether or not the Client is modal. A "
+            "modal Client implies that it needs to be closed before its "
+            "parent() can be used (focsed) again."),
+    ATTRMETH(shaded,
+            "c.shaded() -- Returns whether or not the Client is shaded. "
+            "Shaded clients are hidden except for their titlebar."),
+    ATTRMETH(iconic,
+            "c.iconic() -- Returns whether or not the Client is iconic. "
+            "Iconic windows are represented only by icons, or possibly "
+            "hidden entirely."),
+    ATTRMETH(maximizedVertical,
+            "c.maximizedVertical() -- Returns whether or not the Client is "
+            "maxized vertically. When a Client is maximized it will expand "
+            "to fill as much of the screen as it can in that direction."),
+    ATTRMETH(maximizedHorizontal,
+            "c.maximizedHorizontal() -- Returns whether or not the Client is "
+            "maxized horizontally. When a Client is maximized it will expand "
+            "to fill as much of the screen as it can in that direction."),
+    ATTRMETH(skipPager,
+            "c.skipPager() -- Returns whether the Client as requested to be "
+            "skipped by pagers."),
+    ATTRMETH(skipTaskbar,
+            "c.skipTaskbar() -- Returns whether the Client as requested to "
+            "be skipped by taskbars."),
+    ATTRMETH(fullscreen,
+            "c.fullscreen() -- Returns whether the Client is in fullscreen "
+            "mode."),
+    ATTRMETH(above,
+            "c.above() -- Returns whether the Client should be stacked above "
+            "other windows of the same type."),
+    ATTRMETH(below,
+            "c.below() -- Returns whether the Client should be stacked below "
+            "other windows of the same type."),
+    ATTRMETH(layer,
+            "c.layer() -- Returns the layer in which the window should be "
+            "stacked. This is one of the ob.Layer_ constants. Windows in "
+            "layers with higher values should be kept above windows in lower "
+            "valued layers."),
+    ATTRMETH(decorations,
+            "c.decorations() -- Returns a mask of decorations which the "
+            "Client will be given. It is made up of the ob.Decor_ constants. "
+            "These can be turned off with the "
+            " disabledDecorations()."),
+    ATTRMETH(disabledDecorations,
+            "c.disabledDecorations() -- returns a mask of decorations which "
+            "are disabled on the Client. This is made up of the ob.Decor_ "
+            "constants."),
+    ATTRMETH(functions,
+            "ob.functions() -- Returns the list of functionality for the "
+            "Client, in a mask made up of the ob.Func_ constants."),
+    ATTRMETH(visible,
+            "ob.visible() -- Returns if the client is currently visible "
+            "or hidden."),
+    ATTRMETH(decorationSize,
+            "c.decorationSize() -- Returns the size of the Client's "
+            "decorations around the Client window, in a tuple. The format of "
+            "the tuple is (left, top, right, bottom)."),
+    ATTRMETH(normal,
+            "c.normal() -- Returns if the window should be treated as a "
+            "normal window. Some windows (desktops, docks, splash screens) "
+            "should have special rules applied to them in a number of "
+            "places regarding focus or user interaction."),
+    ATTRMETH(setVisible,
+            "c.setVisible(show) -- Shows or hides the Client."),
+    ATTRMETH(raiseWindow,
+            "c.raiseWindow() -- Raises the Client to the top of its layer."),
+    ATTRMETH(lowerWindow,
+            "c.lowerWindow() -- Lowers the Client to the bottom of its "
+            "layer."),
+    ATTRMETH(focus,
+            "c.focus() -- Focuses the Client. Returns 1 if the Client will "
+            "be focused, or 0 if it will not."),
+    ATTRMETH(unfocus,
+            "c.unfocus() -- Unfocuses the Client, leaving nothing focused."),
+    ATTRMETH(move,
+            "c.move(x, y) -- Moves the Client to the specified position. The "
+            "top left corner of the Client's decorations is positioned at "
+            "the given x, y."),
+    { NULL, NULL, 0, NULL }
+};
+
+/***************************************************************************
+   Type methods/struct
+ ***************************************************************************/
+
+/*static PyObject *cwrap_getattr(ClientWrap *self, char *name)
+{
+    CHECK_CWRAP(self, "getattr");
+    return Py_FindMethod(ClientWrapAttributeMethods, (PyObject*)self, name);
+}*/
+
+static void cwrap_dealloc(ClientWrap *self)
+{
+    if (self->client != NULL)
+       self->client->wrap = NULL;
+    PyObject_Del((PyObject*) self);
+}
+
+static PyObject *cwrap_repr(ClientWrap *self)
+{
+     CHECK_CWRAP(self, "repr");
+     return PyString_FromFormat("0x%x", (guint)self->client->window);
+}
+
+static int cwrap_compare(ClientWrap *self, ClientWrap *other)
+{
+    Window w1, w2;
+    if (!IS_VALID_CWRAP(self)) {
+       PyErr_SetString(PyExc_ValueError,
+                       "This 'Client' is wrapping a client which no longer "
+                       "exists.");
+    }
+
+    w1 = self->client->window;
+    w2 = self->client->window;
+    return w1 > w2 ? 1 : w1 < w2 ? -1 : 0;
+}
+
+static PyTypeObject ClientWrapType = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "Client",
+    sizeof(ClientWrap),
+    0,
+    (destructor) cwrap_dealloc,     /*tp_dealloc*/
+    0,                              /*tp_print*/
+    0,                              /*tp_getattr*/
+    0,                              /*tp_setattr*/
+    (cmpfunc) cwrap_compare,        /*tp_compare*/
+    (reprfunc) cwrap_repr,          /*tp_repr*/
+    0,                              /*tp_as_number*/
+    0,                              /*tp_as_sequence*/
+    0,                              /*tp_as_mapping*/
+    0,                              /*tp_hash */
+};
+
+/***************************************************************************
+   External methods
+ ***************************************************************************/
+
+void clientwrap_startup()
+{
+    ClientWrapType.ob_type = &PyType_Type;
+    ClientWrapType.tp_methods = ClientWrapMethods;
+    PyType_Ready(&ClientWrapType);
+}
+
+void clientwrap_shutdown()
+{
+}
+
+PyObject *clientwrap_new(Client *client)
+{
+    g_assert(client != NULL);
+
+    if (client->wrap != NULL) {
+       /* already has a wrapper! */
+       Py_INCREF((PyObject*) client->wrap);
+    } else {
+       client->wrap = PyObject_New(ClientWrap, &ClientWrapType);
+       client->wrap->client = client;
+    }
+    return (PyObject*) client->wrap;
+}
diff --git a/c/clientwrap.h b/c/clientwrap.h
new file mode 100644 (file)
index 0000000..b20ca91
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __clientwrap_h
+#define __clientwrap_h
+
+#include <Python.h>
+
+struct Client;
+
+/* ClientWrap is a PyObject */
+typedef struct ClientWrap {
+    PyObject_HEAD
+    struct Client *client;
+} ClientWrap;
+
+void clientwrap_startup();
+void clientwrap_shutdown();
+
+PyObject *clientwrap_new(struct Client *client);
+
+#endif
diff --git a/c/event.c b/c/event.c
new file mode 100644 (file)
index 0000000..32d8734
--- /dev/null
+++ b/c/event.c
@@ -0,0 +1,593 @@
+#include "openbox.h"
+#include "client.h"
+#include "xerror.h"
+#include "prop.h"
+#include "screen.h"
+#include "frame.h"
+#include "focus.h"
+#include "hooks.h"
+#include "stacking.h"
+#include "kbind.h"
+#include "mbind.h"
+
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+
+static void event_process(XEvent *e);
+static void event_handle_root(XEvent *e);
+static void event_handle_client(Client *c, XEvent *e);
+
+Time event_lasttime = 0;
+
+/*! A list of all possible combinations of keyboard lock masks */
+static unsigned int mask_list[8];
+/*! The value of the mask for the NumLock modifier */
+static unsigned int NumLockMask;
+/*! The value of the mask for the ScrollLock modifier */
+static unsigned int ScrollLockMask;
+/*! The key codes for the modifier keys */
+static XModifierKeymap *modmap;
+/*! Table of the constant modifier masks */
+static const int mask_table[] = {
+    ShiftMask, LockMask, ControlMask, Mod1Mask,
+    Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
+};
+static int mask_table_size;
+
+void event_startup()
+{
+    mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
+     
+    /* get lock masks that are defined by the display (not constant) */
+    modmap = XGetModifierMapping(ob_display);
+    g_assert(modmap);
+    if (modmap && modmap->max_keypermod > 0) {
+       size_t cnt;
+       const size_t size = mask_table_size * modmap->max_keypermod;
+       /* get the values of the keyboard lock modifiers
+          Note: Caps lock is not retrieved the same way as Scroll and Num
+          lock since it doesn't need to be. */
+       const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
+       const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
+                                                    XK_Scroll_Lock);
+         
+       for (cnt = 0; cnt < size; ++cnt) {
+           if (! modmap->modifiermap[cnt]) continue;
+              
+           if (num_lock == modmap->modifiermap[cnt])
+               NumLockMask = mask_table[cnt / modmap->max_keypermod];
+           if (scroll_lock == modmap->modifiermap[cnt])
+               ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
+       }
+    }
+
+    mask_list[0] = 0;
+    mask_list[1] = LockMask;
+    mask_list[2] = NumLockMask;
+    mask_list[3] = LockMask | NumLockMask;
+    mask_list[4] = ScrollLockMask;
+    mask_list[5] = ScrollLockMask | LockMask;
+    mask_list[6] = ScrollLockMask | NumLockMask;
+    mask_list[7] = ScrollLockMask | LockMask | NumLockMask;
+}
+
+void event_shutdown()
+{
+    XFreeModifiermap(modmap);
+}
+
+void event_loop()
+{
+    fd_set selset;
+    XEvent e;
+    int x_fd;
+
+    while (TRUE) {
+       /*
+         There are slightly different event retrieval semantics here for
+         local (or high bandwidth) versus remote (or low bandwidth)
+         connections to the display/Xserver.
+       */
+       if (ob_remote) {
+           if (!XPending(ob_display))
+               break;
+       } else {
+           /*
+             This XSync allows for far more compression of events, which
+             makes things like Motion events perform far far better. Since
+             it also means network traffic for every event instead of every
+             X events (where X is the number retrieved at a time), it
+             probably should not be used for setups where Openbox is
+             running on a remote/low bandwidth display/Xserver.
+           */
+           XSync(ob_display, FALSE);
+           if (!XEventsQueued(ob_display, QueuedAlready))
+               break;
+       }
+       XNextEvent(ob_display, &e);
+
+       event_process(&e);
+    }
+     
+    x_fd = ConnectionNumber(ob_display);
+    FD_ZERO(&selset);
+    FD_SET(x_fd, &selset);
+    select(x_fd + 1, &selset, NULL, NULL, NULL);
+}
+
+void event_process(XEvent *e)
+{
+    XEvent ce;
+    KeyCode *kp;
+    Window window;
+    int i, k;
+    Client *client;
+    GQuark context;
+    static guint motion_button = 0;
+
+    /* pick a window */
+    switch (e->type) {
+    case UnmapNotify:
+       window = e->xunmap.window;
+       break;
+    case DestroyNotify:
+       window = e->xdestroywindow.window;
+       break;
+    case ConfigureRequest:
+       window = e->xconfigurerequest.window;
+       break;
+    default:
+       window = e->xany.window;
+    }
+     
+    /* grab the lasttime and hack up the state */
+    switch (e->type) {
+    case ButtonPress:
+    case ButtonRelease:
+       event_lasttime = e->xbutton.time;
+       e->xbutton.state &= ~(LockMask | NumLockMask | ScrollLockMask);
+       /* kill off the Button1Mask etc, only want the modifiers */
+       e->xbutton.state &= (ControlMask | ShiftMask | Mod1Mask |
+                            Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
+       break;
+    case KeyPress:
+       event_lasttime = e->xkey.time;
+       e->xkey.state &= ~(LockMask | NumLockMask | ScrollLockMask);
+       /* kill off the Button1Mask etc, only want the modifiers */
+       e->xkey.state &= (ControlMask | ShiftMask | Mod1Mask |
+                         Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
+       /* add to the state the mask of the modifier being pressed, if it is
+          a modifier key being pressed (this is a little ugly..) */
+/* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
+/*     kp = modmap->modifiermap;*/
+/*     for (i = 0; i < mask_table_size; ++i) {*/
+/*         for (k = 0; k < modmap->max_keypermod; ++k) {*/
+/*             if (*kp == e->xkey.keycode) {*/ /* found the keycode */
+                   /* add the mask for it */
+/*                 e->xkey.state |= mask_table[i];*/
+                   /* cause the first loop to break; */
+/*                 i = mask_table_size;*/
+/*                 break;*/ /* get outta here! */
+/*             }*/
+/*             ++kp;*/
+/*         }*/
+/*     }*/
+
+       break;
+    case KeyRelease:
+       event_lasttime = e->xkey.time;
+       e->xkey.state &= ~(LockMask | NumLockMask | ScrollLockMask);
+       /* kill off the Button1Mask etc, only want the modifiers */
+       e->xkey.state &= (ControlMask | ShiftMask | Mod1Mask |
+                         Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
+       /* remove from the state the mask of the modifier being released, if
+          it is a modifier key being released (this is a little ugly..) */
+       kp = modmap->modifiermap;
+       for (i = 0; i < mask_table_size; ++i) {
+           for (k = 0; k < modmap->max_keypermod; ++k) {
+               if (*kp == e->xkey.keycode) { /* found the keycode */
+                   /* remove the mask for it */
+                   e->xkey.state &= ~mask_table[i];
+                   /* cause the first loop to break; */
+                   i = mask_table_size;
+                   break; /* get outta here! */
+               }
+               ++kp;
+           }
+       }
+       break;
+    case MotionNotify:
+       event_lasttime = e->xmotion.time;
+       e->xmotion.state &= ~(LockMask | NumLockMask | ScrollLockMask);
+       /* kill off the Button1Mask etc, only want the modifiers */
+       e->xmotion.state &= (ControlMask | ShiftMask | Mod1Mask |
+                            Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
+       /* compress events */
+       while (XCheckTypedWindowEvent(ob_display, window, e->type, &ce)) {
+           e->xmotion.x_root = ce.xmotion.x_root;
+           e->xmotion.y_root = ce.xmotion.y_root;
+       }
+       break;
+    case PropertyNotify:
+       event_lasttime = e->xproperty.time;
+       break;
+    case FocusIn:
+    case FocusOut:
+       if (e->xfocus.mode == NotifyGrab)
+           /*|| e.xfocus.mode == NotifyUngrab ||*/
+              
+           /* From Metacity, from WindowMaker, ignore all funky pointer
+              root events. Its commented out cuz I don't think we need this
+              at all. If problems arise we can look into it */
+           /*e.xfocus.detail > NotifyNonlinearVirtual) */
+           return; /* skip me! */
+       if (e->type == FocusOut) {
+           /* FocusOut events just make us look for FocusIn events. They
+              are mostly ignored otherwise. */
+           XEvent fi;
+           if (XCheckTypedEvent(ob_display, FocusIn, &fi)) {
+               event_process(&fi);
+               /* dont unfocus the window we just focused! */
+               if (fi.xfocus.window == e->xfocus.window)
+                   return;
+           }
+       }
+       break;
+    case EnterNotify:
+    case LeaveNotify:
+       event_lasttime = e->xcrossing.time;
+       if (e->xcrossing.mode != NotifyNormal)
+           return; /* skip me! */
+       break;
+    }
+
+    client = g_hash_table_lookup(client_map, (gpointer)window);
+    if (client) {
+       event_handle_client(client, e);
+    } else if (window == ob_root)
+       event_handle_root(e);
+    else if (e->type == ConfigureRequest) {
+       /* unhandled configure requests must be used to configure the
+          window directly */
+       XWindowChanges xwc;
+              
+       xwc.x = e->xconfigurerequest.x;
+       xwc.y = e->xconfigurerequest.y;
+       xwc.width = e->xconfigurerequest.width;
+       xwc.height = e->xconfigurerequest.height;
+       xwc.border_width = e->xconfigurerequest.border_width;
+       xwc.sibling = e->xconfigurerequest.above;
+       xwc.stack_mode = e->xconfigurerequest.detail;
+       
+       g_message("Proxying configure event for 0x%lx\n", window);
+       
+       /* we are not to be held responsible if someone sends us an
+          invalid request! */
+       xerror_set_ignore(TRUE);
+       XConfigureWindow(ob_display, window,
+                        e->xconfigurerequest.value_mask, &xwc);
+       xerror_set_ignore(FALSE);
+    }
+
+    /* dispatch Crossing, Pointer and Key events to the hooks */
+    switch(e->type) {
+    case EnterNotify:
+       context = frame_get_context(client, window);
+       LOGICALHOOK(EnterWindow, context, client);
+       break;
+    case LeaveNotify:
+       context = frame_get_context(client, window);
+       LOGICALHOOK(LeaveWindow, context, client);
+       break;
+    case ButtonPress:
+       if (!motion_button) motion_button = e->xbutton.button;
+       context = frame_get_context(client, window);
+       mbind_fire(e->xbutton.state, e->xbutton.button, context,
+                  Pointer_Press, client, e->xbutton.x_root,
+                  e->xbutton.y_root);
+       break;
+    case ButtonRelease:
+       if (motion_button == e->xbutton.button) motion_button = 0;
+       context = frame_get_context(client, window);
+       mbind_fire(e->xbutton.state, e->xbutton.button, context,
+                  Pointer_Release, client, e->xbutton.x_root,
+                  e->xbutton.y_root);
+       break;
+    case MotionNotify:
+       context = frame_get_context(client, window);
+       mbind_fire(e->xkey.state, motion_button, context, Pointer_Motion,
+                  client, e->xmotion.x_root, e->xmotion.y_root);
+       break;
+    case KeyPress:
+       kbind_fire(e->xkey.state, e->xkey.keycode, TRUE);
+       break;
+    case KeyRelease:
+       kbind_fire(e->xkey.state, e->xkey.keycode, FALSE);
+       break;
+    }
+}
+
+static void event_handle_root(XEvent *e)
+{
+    Atom msgtype;
+     
+    switch(e->type) {
+    case MapRequest:
+       g_message("MapRequest on root");
+       client_manage(e->xmap.window);
+       break;
+    case ClientMessage:
+       if (e->xclient.format != 32) break;
+
+       msgtype = e->xclient.message_type;
+       if (msgtype == prop_atoms.net_current_desktop) {
+           unsigned int d = e->xclient.data.l[0];
+           if (d <= screen_num_desktops)
+               screen_set_desktop(d);
+       } else if (msgtype == prop_atoms.net_number_of_desktops) {
+           unsigned int d = e->xclient.data.l[0];
+           if (d > 0)
+               screen_set_num_desktops(d);
+       } else if (msgtype == prop_atoms.net_showing_desktop) {
+           screen_show_desktop(e->xclient.data.l[0] != 0);
+       }
+       break;
+    case PropertyNotify:
+       if (e->xproperty.atom == prop_atoms.net_desktop_names)
+           screen_update_desktop_names();
+       else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
+           screen_update_layout();
+       break;
+    }
+}
+
+static void event_handle_client(Client *client, XEvent *e)
+{
+    XEvent ce;
+    Atom msgtype;
+     
+    switch (e->type) {
+    case FocusIn:
+       client->focused = TRUE;
+       frame_adjust_focus(client->frame);
+
+       /* focus state can affect the stacking layer */
+       client_calc_layer(client);
+
+       focus_set_client(client);
+       break;
+    case FocusOut:
+       client->focused = FALSE;
+       frame_adjust_focus(client->frame);
+
+       /* focus state can affect the stacking layer */
+       client_calc_layer(client);
+
+       if (focus_client == client)
+           focus_set_client(NULL);
+       break;
+    case ConfigureRequest:
+       g_message("ConfigureRequest for window %lx", client->window);
+       /* compress these */
+       while (XCheckTypedWindowEvent(ob_display, client->window,
+                                     ConfigureRequest, &ce)) {
+           /* XXX if this causes bad things.. we can compress config req's
+              with the same mask. */
+           e->xconfigurerequest.value_mask |=
+               ce.xconfigurerequest.value_mask;
+           if (ce.xconfigurerequest.value_mask & CWX)
+               e->xconfigurerequest.x = ce.xconfigurerequest.x;
+           if (ce.xconfigurerequest.value_mask & CWY)
+               e->xconfigurerequest.y = ce.xconfigurerequest.y;
+           if (ce.xconfigurerequest.value_mask & CWWidth)
+               e->xconfigurerequest.width = ce.xconfigurerequest.width;
+           if (ce.xconfigurerequest.value_mask & CWHeight)
+               e->xconfigurerequest.height = ce.xconfigurerequest.height;
+           if (ce.xconfigurerequest.value_mask & CWBorderWidth)
+               e->xconfigurerequest.border_width =
+                   ce.xconfigurerequest.border_width;
+           if (ce.xconfigurerequest.value_mask & CWStackMode)
+               e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
+       }
+
+       /* if we are iconic (or shaded (fvwm does this)) ignore the event */
+       if (client->iconic || client->shaded) return;
+
+       if (e->xconfigurerequest.value_mask & CWBorderWidth)
+           client->border_width = e->xconfigurerequest.border_width;
+
+       /* resize, then move, as specified in the EWMH section 7.7 */
+       if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
+                                              CWX | CWY)) {
+           int x, y, w, h;
+           Corner corner;
+              
+           x = (e->xconfigurerequest.value_mask & CWX) ?
+               e->xconfigurerequest.x : client->area.x;
+           y = (e->xconfigurerequest.value_mask & CWY) ?
+               e->xconfigurerequest.y : client->area.y;
+           w = (e->xconfigurerequest.value_mask & CWWidth) ?
+               e->xconfigurerequest.width : client->area.width;
+           h = (e->xconfigurerequest.value_mask & CWHeight) ?
+               e->xconfigurerequest.height : client->area.height;
+              
+           switch (client->gravity) {
+           case NorthEastGravity:
+           case EastGravity:
+               corner = Corner_TopRight;
+               break;
+           case SouthWestGravity:
+           case SouthGravity:
+               corner = Corner_BottomLeft;
+               break;
+           case SouthEastGravity:
+               corner = Corner_BottomRight;
+               break;
+           default:     /* NorthWest, Static, etc */
+               corner = Corner_TopLeft;
+           }
+
+           client_configure(client, corner, x, y, w, h, FALSE, FALSE);
+       }
+
+       if (e->xconfigurerequest.value_mask & CWStackMode) {
+           switch (e->xconfigurerequest.detail) {
+           case Below:
+           case BottomIf:
+               stacking_lower(client);
+               break;
+
+           case Above:
+           case TopIf:
+           default:
+               stacking_raise(client);
+               break;
+           }
+       }
+       break;
+    case UnmapNotify:
+       if (client->ignore_unmaps) {
+           client->ignore_unmaps--;
+           break;
+       }
+       g_message("UnmapNotify for %lx", client->window);
+       client_unmanage(client);
+       break;
+    case DestroyNotify:
+       g_message("DestroyNotify for %lx", client->window);
+       client_unmanage(client);
+       break;
+    case ReparentNotify:
+       /* this is when the client is first taken captive in the frame */
+       if (e->xreparent.parent == client->frame->plate) break;
+
+       /*
+         This event is quite rare and is usually handled in unmapHandler.
+         However, if the window is unmapped when the reparent event occurs,
+         the window manager never sees it because an unmap event is not sent
+         to an already unmapped window.
+       */
+
+       /* we don't want the reparent event, put it back on the stack for the
+          X server to deal with after we unmanage the window */
+       XPutBackEvent(ob_display, e);
+     
+       client_unmanage(client);
+       break;
+    case MapRequest:
+       /* we shouldn't be able to get this unless we're iconic */
+       g_assert(client->iconic);
+
+       LOGICALHOOK(RequestActivate, g_quark_try_string("client"), client);
+       break;
+    case ClientMessage:
+       /* validate cuz we query stuff off the client here */
+       if (!client_validate(client)) break;
+  
+       if (e->xclient.format != 32) return;
+
+       msgtype = e->xclient.message_type;
+       if (msgtype == prop_atoms.wm_change_state) {
+           /* compress changes into a single change */
+           while (XCheckTypedWindowEvent(ob_display, e->type,
+                                         client->window, &ce)) {
+               /* XXX: it would be nice to compress ALL messages of a
+                  type, not just messages in a row without other
+                  message types between. */
+               if (ce.xclient.message_type != msgtype) {
+                   XPutBackEvent(ob_display, &ce);
+                   break;
+               }
+               e->xclient = ce.xclient;
+           }
+           client_set_wm_state(client, e->xclient.data.l[0]);
+       } else if (msgtype == prop_atoms.net_wm_desktop) {
+           /* compress changes into a single change */
+           while (XCheckTypedWindowEvent(ob_display, e->type,
+                                         client->window, &ce)) {
+               /* XXX: it would be nice to compress ALL messages of a
+                  type, not just messages in a row without other
+                  message types between. */
+               if (ce.xclient.message_type != msgtype) {
+                   XPutBackEvent(ob_display, &ce);
+                   break;
+               }
+               e->xclient = ce.xclient;
+           }
+           client_set_desktop(client, e->xclient.data.l[0]);
+       } else if (msgtype == prop_atoms.net_wm_state) {
+           /* can't compress these */
+           g_message("net_wm_state %s %ld %ld for 0x%lx\n",
+                     (e->xclient.data.l[0] == 0 ? "Remove" :
+                      e->xclient.data.l[0] == 1 ? "Add" :
+                      e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
+                     e->xclient.data.l[1], e->xclient.data.l[2],
+                     client->window);
+           client_set_state(client, e->xclient.data.l[0],
+                            e->xclient.data.l[1], e->xclient.data.l[2]);
+       } else if (msgtype == prop_atoms.net_close_window) {
+           g_message("net_close_window for 0x%lx\n", client->window);
+           client_close(client);
+       } else if (msgtype == prop_atoms.net_active_window) {
+           g_message("net_active_window for 0x%lx\n", client->window);
+           if (screen_showing_desktop)
+               screen_show_desktop(FALSE);
+           if (client->iconic)
+               client_iconify(client, FALSE, TRUE);
+           else if (!client->frame->visible)
+               /* if its not visible for other reasons, then don't mess
+                  with it */
+               return;
+           LOGICALHOOK(RequestActivate, g_quark_try_string("client"), client);
+       }
+       break;
+    case PropertyNotify:
+       /* validate cuz we query stuff off the client here */
+       if (!client_validate(client)) break;
+  
+       /* compress changes to a single property into a single change */
+       while (XCheckTypedWindowEvent(ob_display, e->type,
+                                     client->window, &ce)) {
+           /* XXX: it would be nice to compress ALL changes to a property,
+              not just changes in a row without other props between. */
+           if (ce.xproperty.atom != e->xproperty.atom) {
+               XPutBackEvent(ob_display, &ce);
+               break;
+           }
+       }
+
+       msgtype = e->xproperty.atom;
+       if (msgtype == XA_WM_NORMAL_HINTS) {
+           client_update_normal_hints(client);
+           /* normal hints can make a window non-resizable */
+           client_setup_decor_and_functions(client);
+       } else if (msgtype == XA_WM_HINTS)
+           client_update_wmhints(client);
+       else if (msgtype == XA_WM_TRANSIENT_FOR) {
+           client_update_transient_for(client);
+           client_get_type(client);
+           /* type may have changed, so update the layer */
+           client_calc_layer(client);
+           client_setup_decor_and_functions(client);
+       }
+       else if (msgtype == prop_atoms.net_wm_name ||
+                msgtype == prop_atoms.wm_name)
+           client_update_title(client);
+       else if (msgtype == prop_atoms.net_wm_icon_name ||
+                msgtype == prop_atoms.wm_icon_name)
+           client_update_icon_title(client);
+       else if (msgtype == prop_atoms.wm_class)
+           client_update_class(client);
+       else if (msgtype == prop_atoms.wm_protocols) {
+           client_update_protocols(client);
+           client_setup_decor_and_functions(client);
+       }
+       else if (msgtype == prop_atoms.net_wm_strut)
+           client_update_strut(client);
+       else if (msgtype == prop_atoms.net_wm_icon)
+           client_update_icons(client);
+       else if (msgtype == prop_atoms.kwm_win_icon)
+           client_update_kwm_icon(client);
+    }
+}
diff --git a/c/event.h b/c/event.h
new file mode 100644 (file)
index 0000000..9153116
--- /dev/null
+++ b/c/event.h
@@ -0,0 +1,12 @@
+#ifndef __events_h
+#define __events_h
+
+/*! Time at which the last event with a timestamp occured. */
+extern Time event_lasttime;
+
+void event_startup();
+void event_shutdown();
+
+void event_loop();
+
+#endif
diff --git a/c/eventdata.c b/c/eventdata.c
new file mode 100644 (file)
index 0000000..e3bf15e
--- /dev/null
@@ -0,0 +1,433 @@
+#include "eventdata.h"
+#include "openbox.h"
+#include "event.h"
+#include "clientwrap.h"
+#include <X11/Xlib.h>
+
+/*
+ *
+ * Define the type 'EventData'
+ *
+ */
+
+#define IS_EVENTDATA(v)  ((v)->ob_type == &EventDataType)
+#define CHECK_EVENTDATA(self, funcname) { \
+    if (!IS_EVENTDATA(self)) { \
+        PyErr_SetString(PyExc_TypeError, \
+                       "descriptor '" funcname "' requires an 'EventData' " \
+                       "object"); \
+       return NULL; \
+    } \
+}
+
+staticforward PyTypeObject EventDataType;
+
+static PyObject *eventdata_type(EventData *self, PyObject *args)
+{
+    CHECK_EVENTDATA(self, "type");
+    if (!PyArg_ParseTuple(args, ":type"))
+       return NULL;
+    return PyInt_FromLong(self->type);
+}
+
+static PyObject *eventdata_time(EventData *self, PyObject *args)
+{
+    CHECK_EVENTDATA(self, "time");
+    if (!PyArg_ParseTuple(args, ":time"))
+       return NULL;
+    return PyInt_FromLong(event_lasttime);
+}
+
+static PyObject *eventdata_context(EventData *self, PyObject *args)
+{
+    CHECK_EVENTDATA(self, "context");
+    if (!PyArg_ParseTuple(args, ":context"))
+       return NULL;
+    return PyString_FromString(self->context);
+}
+
+static PyObject *eventdata_client(EventData *self, PyObject *args)
+{
+    CHECK_EVENTDATA(self, "client");
+    if (!PyArg_ParseTuple(args, ":client"))
+       return NULL;
+    if (self->client == NULL) {
+       Py_INCREF(Py_None);
+       return Py_None;
+    } else {
+       return clientwrap_new(self->client);
+    }
+}
+
+static PyObject *eventdata_keycode(EventData *self, PyObject *args)
+{
+    CHECK_EVENTDATA(self, "keycode");
+    if (!PyArg_ParseTuple(args, ":keycode"))
+       return NULL;
+    switch (self->type) {
+    case Key_Press:
+    case Key_Release:
+       break;
+    default:
+       PyErr_SetString(PyExc_TypeError,
+                       "The EventData object is not a Key event");
+       return NULL;
+    }
+    return PyInt_FromLong(self->details.key->keycode);
+}
+
+static PyObject *eventdata_modifiers(EventData *self, PyObject *args)
+{
+    CHECK_EVENTDATA(self, "key");
+    if (!PyArg_ParseTuple(args, ":key"))
+       return NULL;
+    switch (self->type) {
+    case Key_Press:
+    case Key_Release:
+    case Pointer_Press:
+    case Pointer_Release:
+    case Pointer_Motion:
+       break;
+    default:
+       PyErr_SetString(PyExc_TypeError,
+                       "The EventData object is not a Key or Pointer event");
+       return NULL;
+    }
+    return PyInt_FromLong(self->details.key->modifiers);
+}
+
+static PyObject *eventdata_keyName(EventData *self, PyObject *args)
+{
+    GList *it;
+    PyObject *tuple;
+    int i;
+
+    CHECK_EVENTDATA(self, "keyName");
+    if (!PyArg_ParseTuple(args, ":keyName"))
+       return NULL;
+    switch (self->type) {
+    case Key_Press:
+    case Key_Release:
+       break;
+    default:
+       PyErr_SetString(PyExc_TypeError,
+                       "The EventData object is not a Key event");
+       return NULL;
+    }
+
+    if (self->details.key->keylist != NULL) {
+       tuple = PyTuple_New(g_list_length(self->details.key->keylist));
+       for (i = 0, it = self->details.key->keylist; it != NULL;
+            it = it->next, ++i)
+           PyTuple_SET_ITEM(tuple, i, PyString_FromString(it->data));
+       return tuple;
+    } else {
+       GString *str = g_string_sized_new(0);
+       KeySym sym;
+
+       if (self->details.key->modifiers & ControlMask)
+           g_string_append(str, "C-");
+       if (self->details.key->modifiers & ShiftMask)
+           g_string_append(str, "S-");
+       if (self->details.key->modifiers & Mod1Mask)
+           g_string_append(str, "Mod1-");
+       if (self->details.key->modifiers & Mod2Mask)
+           g_string_append(str, "Mod2-");
+       if (self->details.key->modifiers & Mod3Mask)
+           g_string_append(str, "Mod3-");
+       if (self->details.key->modifiers & Mod4Mask)
+           g_string_append(str, "Mod4-");
+       if (self->details.key->modifiers & Mod5Mask)
+           g_string_append(str, "Mod5-");
+
+       sym = XKeycodeToKeysym(ob_display, self->details.key->keycode, 0);
+       if (sym == NoSymbol)
+           g_string_append(str, "NoSymbol");
+       else {
+           char *name = XKeysymToString(sym);
+           if (name == NULL)
+               name = "Undefined";
+           g_string_append(str, name);
+       }
+
+       tuple = PyTuple_New(1);
+       PyTuple_SET_ITEM(tuple, 0, PyString_FromString(str->str));
+       g_string_free(str, TRUE);
+
+       return tuple;
+    }
+}
+
+static PyObject *eventdata_button(EventData *self, PyObject *args)
+{
+    CHECK_EVENTDATA(self, "button");
+    if (!PyArg_ParseTuple(args, ":button"))
+       return NULL;
+    switch (self->type) {
+    case Pointer_Press:
+    case Pointer_Release:
+    case Pointer_Motion:
+       break;
+    default:
+       PyErr_SetString(PyExc_TypeError,
+                       "The EventData object is not a Pointer event");
+       return NULL;
+    }
+    return PyInt_FromLong(self->details.pointer->button);
+}
+
+static PyObject *eventdata_buttonName(EventData *self, PyObject *args)
+{
+    CHECK_EVENTDATA(self, "buttonName");
+    if (!PyArg_ParseTuple(args, ":buttonName"))
+       return NULL;
+    switch (self->type) {
+    case Pointer_Press:
+    case Pointer_Release:
+    case Pointer_Motion:
+       break;
+    default:
+       PyErr_SetString(PyExc_TypeError,
+                       "The EventData object is not a Pointer event");
+       return NULL;
+    }
+
+    if (self->details.pointer->name != NULL) {
+       return PyString_FromString(self->details.pointer->name);
+    } else {
+       PyObject *pystr;
+       GString *str = g_string_sized_new(0);
+
+       if (self->details.pointer->modifiers & ControlMask)
+           g_string_append(str, "C-");
+       if (self->details.pointer->modifiers & ShiftMask)
+           g_string_append(str, "S-");
+       if (self->details.pointer->modifiers & Mod1Mask)
+           g_string_append(str, "Mod1-");
+       if (self->details.pointer->modifiers & Mod2Mask)
+           g_string_append(str, "Mod2-");
+       if (self->details.pointer->modifiers & Mod3Mask)
+           g_string_append(str, "Mod3-");
+       if (self->details.pointer->modifiers & Mod4Mask)
+           g_string_append(str, "Mod4-");
+       if (self->details.pointer->modifiers & Mod5Mask)
+           g_string_append(str, "Mod5-");
+       
+       g_string_append_printf(str, "%d", self->details.pointer->button);
+
+       pystr = PyString_FromString(str->str);
+
+       g_string_free(str, TRUE);
+
+       return pystr;
+    }
+}
+
+static PyObject *eventdata_position(EventData *self, PyObject *args)
+{
+    PyObject *tuple;
+
+    CHECK_EVENTDATA(self, "position");
+    if (!PyArg_ParseTuple(args, ":position"))
+       return NULL;
+    switch (self->type) {
+    case Pointer_Press:
+    case Pointer_Release:
+    case Pointer_Motion:
+       break;
+    default:
+       PyErr_SetString(PyExc_TypeError,
+                       "The EventData object is not a Pointer event");
+       return NULL;
+    }
+    tuple = PyTuple_New(2);
+    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(self->details.pointer->xroot));
+    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(self->details.pointer->yroot));
+    return tuple;
+}
+
+static PyMethodDef EventDataAttributeMethods[] = {
+    {"type", (PyCFunction)eventdata_type, METH_VARARGS,
+     "data.type() -- Return the event type"},
+    {"context", (PyCFunction)eventdata_context, METH_VARARGS,
+     "data.context() -- Return the context for the event. If it is "
+     "\"client\", then data.client() can be used to find out the "
+     "client."},
+    {"client", (PyCFunction)eventdata_client, METH_VARARGS,
+     "data.client() -- Return the client for the event. This may be None if "
+     "there is no client, even if data.context() gives Context_Client."},
+    {"time", (PyCFunction)eventdata_time, METH_VARARGS,
+     "data.time() -- Return the time at which the last X event occured with "
+     "a timestamp. Should be the time at which this event, or the event that "
+     "caused this event to occur happened."},
+    {"modifiers", (PyCFunction)eventdata_modifiers, METH_VARARGS,
+     "data.modifiers() -- Return the modifier keymask that was pressed "
+     "when the event occured. A bitmask of ShiftMask, LockMask, ControlMask, "
+     "Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, and Mod5Mask. Cannot be used "
+     "when the data.type() is not a Key_* or Pointer_* event type."},
+    {"keycode", (PyCFunction)eventdata_keycode, METH_VARARGS,
+     "data.keycode() -- Return the keycode for the key which generated the "
+     "event. Cannot be used when the data.type() is not a Key_* event type."},
+    {"keyName", (PyCFunction)eventdata_keyName, METH_VARARGS,
+     "data.keyName() -- Return a tuple of the string names of the key which "
+     "generated the event. Cannot be used when the data.type() is not a Key_* "
+     "event "
+     "type."},
+    {"button", (PyCFunction)eventdata_button, METH_VARARGS,
+     "data.button() -- Return the pointer button which generated the event. "
+     "Cannot be used when the data.type() is not a Pointer_* event type."},
+    {"buttonName", (PyCFunction)eventdata_keyName, METH_VARARGS,
+     "data.buttonName() -- Return the name of the button which generated the "
+     "event. Cannot be used when the data.type() is not a Pointer_* event "
+     "type."},
+    {"position", (PyCFunction)eventdata_position, METH_VARARGS,
+     "data.position() -- Returns the current position of the pointer on the "
+     "root window when the event was generated. Gives the position in a tuple "
+     "with a format of (x, y). Cannot be used when the data.type() is not a "
+     "Pointer_* event type."},
+    { NULL, NULL, 0, NULL }
+};
+
+static void data_dealloc(EventData *self)
+{
+    GList *it;
+
+    switch(self->type) {
+    case Logical_EnterWindow:
+    case Logical_LeaveWindow:
+    case Logical_NewWindow:
+    case Logical_CloseWindow:
+    case Logical_Startup:
+    case Logical_Shutdown:
+    case Logical_RequestActivate:
+    case Logical_WindowShow:
+    case Logical_WindowHide:
+    case Logical_Focus:
+    case Logical_Bell:
+    case Logical_UrgentWindow:
+       g_free(self->details.logical);
+       break;
+    case Pointer_Press:
+    case Pointer_Release:
+    case Pointer_Motion:
+       if (self->details.pointer->name != NULL)
+           g_free(self->details.pointer->name);
+       g_free(self->details.pointer);
+       break;
+    case Key_Press:
+    case Key_Release:
+       for (it = self->details.key->keylist; it != NULL; it = it->next)
+           g_free(it->data);
+       g_list_free(self->details.key->keylist);
+       g_free(self->details.key);
+       break;
+    default:
+       g_assert_not_reached();
+    }
+    PyObject_Del((PyObject*) self);
+}
+
+static PyObject *eventdata_getattr(EventData *self, char *name)
+{
+    return Py_FindMethod(EventDataAttributeMethods, (PyObject*)self, name);
+}
+
+static PyTypeObject EventDataType = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "EventData",
+    sizeof(EventData),
+    0,
+    (destructor) data_dealloc,       /*tp_dealloc*/
+    0,                               /*tp_print*/
+    (getattrfunc) eventdata_getattr, /*tp_getattr*/
+    0,                               /*tp_setattr*/
+    0,                               /*tp_compare*/
+    0,                               /*tp_repr*/
+    0,                               /*tp_as_number*/
+    0,                               /*tp_as_sequence*/
+    0,                               /*tp_as_mapping*/
+    0,                               /*tp_hash */
+};
+
+
+
+void eventdata_startup()
+{
+    EventDataType.ob_type = &PyType_Type;
+    PyType_Ready(&EventDataType);
+}
+
+void eventdata_shutdown()
+{
+}
+
+void eventdata_free(EventData *data)
+{
+    Py_DECREF(data);
+}
+
+EventData *eventdata_new_logical(EventType type, GQuark context,
+                                struct Client *client)
+{
+    EventData *data;
+
+    g_assert(type < Pointer_Press);
+
+    data = PyObject_New(EventData, &EventDataType);
+    data->type = type;
+    data->context = g_quark_to_string(context);
+    data->client = client;
+    data->details.logical = g_new(LogicalEvent, 1);
+    return data;
+}
+
+EventData *eventdata_new_pointer(EventType type, GQuark context,
+                                struct Client *client, guint modifiers,
+                                guint button, char *name,
+                                int xroot, int yroot)
+{
+    EventData *data;
+
+    g_assert(type >= Pointer_Press && type < Key_Press);
+
+    data = PyObject_New(EventData, &EventDataType);
+    data->type = type;
+    data->context = g_quark_to_string(context);
+    data->client = client;
+    data->details.pointer = g_new(PointerEvent, 1);
+    data->details.pointer->modifiers = modifiers;
+    data->details.pointer->button = button;
+    data->details.pointer->name = name == NULL ? name : g_strdup(name);
+    data->details.pointer->xroot = xroot;
+    data->details.pointer->yroot = yroot;
+    return data;
+}
+
+EventData *eventdata_new_key(EventType type, GQuark context,
+                            struct Client *client, guint modifiers,
+                            guint keycode, GList *keylist)
+{
+    EventData *data;
+    GList *mykeylist, *it;
+
+    g_assert(type >= Key_Press);
+
+    data = PyObject_New(EventData, &EventDataType);
+    data->type = type;
+    data->context = g_quark_to_string(context);
+    data->client = client;
+    data->details.key = g_new(KeyEvent, 1);
+
+    /* make a copy of the keylist.
+     If the user were to clear the key bindings, then the keylist given here
+     would no longer point at valid memory.*/
+    mykeylist = g_list_copy(keylist); /* shallow copy */
+    for (it = mykeylist; it != NULL; it = it->next) /* deep copy */
+       it->data = g_strdup(it->data);
+
+    data->details.key->keylist = mykeylist;
+    data->details.key->keycode = keycode;
+    data->details.key->modifiers = modifiers;
+    return data;
+}
diff --git a/c/eventdata.h b/c/eventdata.h
new file mode 100644 (file)
index 0000000..ef6beab
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef __eventdata_h
+#define __eventdata_h
+
+#include "obexport.h"
+#include <Python.h>
+#include <glib.h>
+
+struct Client;
+
+typedef struct {
+    int temp:1; /* just a placeholder to kill warnings for now.. */
+} LogicalEvent;
+
+typedef struct {
+    /*! The button which generated the event */
+    guint button;
+    /*! The pointer's x position on the root window when the event occured */
+    int xroot;
+    /*! The pointer's y position on the root window when the event occured */
+    int yroot;
+    /*! The modifiers that were pressed when the event occured. A bitmask of:
+      ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, 
+      Mod4Mask, Mod5Mask */
+    guint modifiers;
+    /*! The name of the button/modifier combination being pressed,
+      eg "Mod1-1" */
+    char *name;
+} PointerEvent;
+
+typedef struct {
+    /*! The keycode of the key which generated the event */
+    guint keycode;
+    /*! The modifiers that were pressed when the event occured. A bitmask of:
+      ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, 
+      Mod4Mask, Mod5Mask */
+    guint modifiers;
+    /* The list of strings which make up the chain that fired,
+       eg ("Mod1-a", "a") */
+    GList *keylist;
+} KeyEvent;
+
+/* EventData is a PyObject */
+typedef struct EventData {
+    PyObject_HEAD
+    /* The type of event which occured */
+    EventType type;
+    /*! The context in which the event occured, the type of window it occured
+      for. */
+    const char *context;
+    /* The Client on which the event occured, or NULL */
+    struct Client *client;
+
+    union EventDetails {
+       LogicalEvent *logical;
+       PointerEvent *pointer;
+       KeyEvent *key;
+    } details;
+} EventData;
+
+void eventdata_startup();
+void eventdata_shutdown();
+
+EventData *eventdata_new_logical(EventType type, GQuark context,
+                                struct Client *client);
+EventData *eventdata_new_pointer(EventType type, GQuark context,
+                                struct Client *client, guint modifiers,
+                                guint button, char *name,
+                                int xroot, int yroot);
+EventData *eventdata_new_key(EventType type, GQuark context,
+                            struct Client *client, guint modifiers,
+                            guint keycode, GList *keylist);
+void eventdata_free(EventData *data);
+
+#endif
diff --git a/c/extensions.c b/c/extensions.c
new file mode 100644 (file)
index 0000000..3fe4319
--- /dev/null
@@ -0,0 +1,34 @@
+#include "openbox.h"
+#include "extensions.h"
+
+gboolean extensions_xkb       = FALSE;
+int      extensions_xkb_event_basep;
+gboolean extensions_shape     = FALSE;
+int      extensions_shape_event_basep;
+gboolean extensions_xinerama  = FALSE;
+int      extensions_xinerama_event_basep;
+
+
+void extensions_query_all()
+{
+    int junk;
+    (void)junk;
+     
+#ifdef XKB
+    extensions_xkb =
+       XkbQueryExtension(ob_display, &junk, &extensions_xkb_event_basep,
+                         &junk, NULL, NULL);
+#endif
+
+#ifdef SHAPE
+    extensions_shape =
+       XShapeQueryExtension(ob_display, &extensions_shape_event_basep,
+                            &junk);
+#endif
+
+#ifdef XINERAMA
+    extensions_xinerama =
+       XineramaQueryExtension(ob_display, &extensions_xinerama_event_basep,
+                              &junk);
+#endif
+}
diff --git a/c/extensions.h b/c/extensions.h
new file mode 100644 (file)
index 0000000..3c11076
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef __extensions_h
+#define __extensions_h
+
+#include <X11/Xlib.h>
+#ifdef    XKB
+#include <X11/XKBlib.h>
+#endif
+#ifdef    SHAPE
+#include <X11/extensions/shape.h>
+#endif
+#ifdef    XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif
+#include <glib.h>
+
+/*! Does the display have the XKB extension? */
+extern gboolean extensions_xkb;
+/*! Base for events for the XKB extension */
+extern int extensions_xkb_event_basep;
+
+/*! Does the display have the Shape extension? */
+extern gboolean extensions_shape;
+/*! Base for events for the Shape extension */
+extern int extensions_shape_event_basep;
+
+/*! Does the display have the Xinerama extension? */
+extern gboolean extensions_xinerama;
+/*! Base for events for the Xinerama extension */
+extern int extensions_xinerama_event_basep;
+
+void extensions_query_all();
+  
+#endif
diff --git a/c/focus.c b/c/focus.c
new file mode 100644 (file)
index 0000000..329a4a9
--- /dev/null
+++ b/c/focus.c
@@ -0,0 +1,56 @@
+#include "openbox.h"
+#include "client.h"
+#include "screen.h"
+#include "prop.h"
+#include "hooks.h"
+#include <X11/Xlib.h>
+
+Client *focus_client = NULL;
+
+Window focus_backup = None;
+
+void focus_set_client(Client *client);
+
+void focus_startup()
+{
+    /* create the window which gets focus when no clients get it. Have to
+       make it override-redirect so we don't try manage it, since it is
+       mapped. */
+    XSetWindowAttributes attrib;
+
+    attrib.override_redirect = TRUE;
+    focus_backup = XCreateWindow(ob_display, ob_root,
+                                -100, -100, 1, 1, 0, 0, InputOnly,
+                                CopyFromParent, CWOverrideRedirect, &attrib);
+    XMapRaised(ob_display, focus_backup);
+
+    /* start with nothing focused */
+    focus_set_client(NULL);
+}
+
+void focus_set_client(Client *client)
+{
+    Window active;
+     
+    /* sometimes this is called with the already-focused window, this is
+       important for the python scripts to work (eg, c = 0 twice). don't just
+       return if _focused_client == c */
+
+    /* uninstall the old colormap, and install the new one */
+    screen_install_colormap(focus_client, FALSE);
+    screen_install_colormap(client, TRUE);
+
+
+    if (client == NULL) {
+       /* when nothing will be focused, send focus to the backup target */
+       XSetInputFocus(ob_display, focus_backup, RevertToNone, CurrentTime);
+    }
+
+    focus_client = client;
+
+    /* set the NET_ACTIVE_WINDOW hint */
+    active = client ? client->window : None;
+    PROP_SET32(ob_root, net_active_window, window, active);
+
+    LOGICALHOOK(Focus, g_quark_try_string("client"), client);
+}
diff --git a/c/focus.h b/c/focus.h
new file mode 100644 (file)
index 0000000..9db5202
--- /dev/null
+++ b/c/focus.h
@@ -0,0 +1,20 @@
+#ifndef __focus_h
+#define __focus_h
+
+#include <X11/Xlib.h>
+
+struct Client;
+
+/*! The window which gets focus when nothing else will be focused */
+extern Window focus_backup;
+
+/*! The client which is currently focused */
+extern struct Client *focus_client;
+
+void focus_startup();
+
+/*! Specify which client is currently focused, this doesn't actually
+  send focus anywhere, its called by the Focus event handlers */
+void focus_set_client(struct Client *client);
+
+#endif
diff --git a/c/frame.c b/c/frame.c
new file mode 100644 (file)
index 0000000..536b4cd
--- /dev/null
+++ b/c/frame.c
@@ -0,0 +1,533 @@
+#include "openbox.h"
+#include "frame.h"
+#include "extensions.h"
+#include "hooks.h"
+
+#define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
+#define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask)
+
+static Window createWindow(Window parent, unsigned long mask,
+                          XSetWindowAttributes *attrib)
+{
+    /* XXX DONT USE THE DEFAULT SHIT */
+    return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
+                        DefaultDepth(ob_display, ob_screen), InputOutput,
+                        DefaultVisual(ob_display, ob_screen),
+                        mask, attrib);
+                       
+}
+
+Frame *frame_new(Client *client)
+{
+    XSetWindowAttributes attrib;
+    unsigned long mask;
+    Frame *self;
+
+    self = g_new(Frame, 1);
+
+    self->client = client;
+    self->visible = FALSE;
+
+    /* create all of the decor windows */
+    mask = CWOverrideRedirect | CWEventMask;
+    attrib.event_mask = FRAME_EVENTMASK;
+    attrib.override_redirect = TRUE;
+    self->window = createWindow(ob_root, mask, &attrib);
+
+    mask = 0;
+    self->plate = createWindow(self->window, mask, &attrib);
+    mask = CWEventMask;
+    attrib.event_mask = (ButtonPressMask | ButtonReleaseMask |
+                        ButtonMotionMask | ExposureMask);
+    self->title = createWindow(self->window, mask, &attrib);
+    self->label = createWindow(self->title, mask, &attrib);
+    self->max = createWindow(self->title, mask, &attrib);
+    self->close = createWindow(self->title, mask, &attrib);
+    self->desk = createWindow(self->title, mask, &attrib);
+    self->icon = createWindow(self->title, mask, &attrib);
+    self->iconify = createWindow(self->title, mask, &attrib);
+    self->handle = createWindow(self->window, mask, &attrib);
+    mask |= CWCursor;
+    attrib.cursor = ob_cursors.ll_angle;
+    self->lgrip = createWindow(self->handle, mask, &attrib);
+    attrib.cursor = ob_cursors.lr_angle;
+    self->rgrip = createWindow(self->handle, mask, &attrib);
+
+    /* the other stuff is shown based on decor settings */
+    XMapWindow(ob_display, self->plate);
+    XMapWindow(ob_display, self->lgrip);
+    XMapWindow(ob_display, self->rgrip);
+    XMapWindow(ob_display, self->label);
+
+
+    /* XXX TEMPORARY OF COURSE!@&*(@! */
+
+    XSetWindowBackground(ob_display, self->title, 0x3333aa);
+    XSetWindowBackground(ob_display, self->handle, 0x3333aa);
+    XSetWindowBackground(ob_display, self->lgrip, 0x2233aa);
+    XSetWindowBackground(ob_display, self->rgrip, 0x2233aa);
+
+    XSetWindowBorder(ob_display, self->window, 0);
+    XSetWindowBorder(ob_display, self->label, 0);
+    XSetWindowBorder(ob_display, self->rgrip, 0);
+    XSetWindowBorder(ob_display, self->lgrip, 0);
+    XSetWindowBorder(ob_display, self->plate, 0x771122);
+
+    /* XXX /TEMPORARY OF COURSE!@&*(@! */
+
+    /* set all the windows for the frame in the client_map */
+    g_hash_table_insert(client_map, (gpointer)self->window, self->client);
+    g_hash_table_insert(client_map, (gpointer)self->plate, self->client);
+    g_hash_table_insert(client_map, (gpointer)self->title, self->client);
+    g_hash_table_insert(client_map, (gpointer)self->label, self->client);
+    g_hash_table_insert(client_map, (gpointer)self->max, self->client);
+    g_hash_table_insert(client_map, (gpointer)self->close, self->client);
+    g_hash_table_insert(client_map, (gpointer)self->desk, self->client);
+    g_hash_table_insert(client_map, (gpointer)self->icon, self->client);
+    g_hash_table_insert(client_map, (gpointer)self->iconify, self->client);
+    g_hash_table_insert(client_map, (gpointer)self->handle, self->client);
+    g_hash_table_insert(client_map, (gpointer)self->lgrip, self->client);
+    g_hash_table_insert(client_map, (gpointer)self->rgrip, self->client);
+
+    return self;
+}
+
+void frame_free(Frame *self)
+{
+    /* remove all the windows for the frame from the client_map */
+    g_hash_table_remove(client_map, (gpointer)self->window);
+    g_hash_table_remove(client_map, (gpointer)self->plate);
+    g_hash_table_remove(client_map, (gpointer)self->title);
+    g_hash_table_remove(client_map, (gpointer)self->label);
+    g_hash_table_remove(client_map, (gpointer)self->max);
+    g_hash_table_remove(client_map, (gpointer)self->close);
+    g_hash_table_remove(client_map, (gpointer)self->desk);
+    g_hash_table_remove(client_map, (gpointer)self->icon);
+    g_hash_table_remove(client_map, (gpointer)self->iconify);
+    g_hash_table_remove(client_map, (gpointer)self->handle);
+    g_hash_table_remove(client_map, (gpointer)self->lgrip);
+    g_hash_table_remove(client_map, (gpointer)self->rgrip);
+
+    XDestroyWindow(ob_display, self->window);
+
+    g_free(self);
+}
+
+void frame_grab_client(Frame *self)
+{
+    /* reparent the client to the frame */
+    XReparentWindow(ob_display, self->client->window, self->plate, 0, 0);
+    /*
+      When reparenting the client window, it is usually not mapped yet, since
+      this occurs from a MapRequest. However, in the case where Openbox is
+      starting up, the window is already mapped, so we'll see unmap events for
+      it. There are 2 unmap events generated that we see, one with the 'event'
+      member set the root window, and one set to the client, but both get
+      handled and need to be ignored.
+    */
+    if (ob_state == State_Starting)
+       self->client->ignore_unmaps += 2;
+
+    /* select the event mask on the client's parent (to receive config/map
+       req's) the ButtonPress is to catch clicks on the client border */
+    XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
+
+    /* map the client so it maps when the frame does */
+    XMapWindow(ob_display, self->client->window);
+
+    frame_adjust_size(self);
+    frame_adjust_position(self);
+}
+
+void frame_release_client(Frame *self)
+{
+    XEvent ev;
+
+    /* check if the app has already reparented its window away */
+    if (XCheckTypedWindowEvent(ob_display, self->client->window,
+                              ReparentNotify, &ev)) {
+       XPutBackEvent(ob_display, &ev);
+       /* re-map the window since the unmanaging process unmaps it */
+       XMapWindow(ob_display, self->client->window);
+    } else {
+       /* according to the ICCCM - if the client doesn't reparent itself,
+          then we will reparent the window to root for them */
+       XReparentWindow(ob_display, self->client->window, ob_root,
+                       self->client->area.x, self->client->area.y);
+    }
+}
+
+void frame_show(Frame *self)
+{
+    if (!self->visible) {
+       self->visible = TRUE;
+       XMapWindow(ob_display, self->window);
+       LOGICALHOOK(WindowShow, g_quark_try_string("client"), self->client);
+    }
+}
+
+void frame_hide(Frame *self)
+{
+    if (self->visible) {
+       self->visible = FALSE;
+       self->client->ignore_unmaps++;
+       XUnmapWindow(ob_display, self->window);
+       LOGICALHOOK(WindowHide, g_quark_try_string("client"), self->client);
+    }
+}
+
+void frame_adjust_size(Frame *self)
+{
+    self->decorations = self->client->decorations;
+
+    /* XXX set shit from the style */
+    self->geom.font_height = 10;
+    self->geom.bevel = 1;
+    self->geom.button_size = self->geom.font_height - 2;
+    self->geom.handle_height = 2;
+    self->geom.grip_width = self->geom.button_size * 2;
+    XResizeWindow(ob_display, self->lgrip, self->geom.grip_width,
+                 self->geom.handle_height);
+    XResizeWindow(ob_display, self->rgrip, self->geom.grip_width,
+                 self->geom.handle_height);
+         
+     
+     
+         
+    if (self->decorations & Decor_Border) {
+       self->geom.bwidth = 1;/*XXX style->frameBorderWidth(); */
+       self->geom.cbwidth = 1; /*XXX style->clientBorderWidth(); */
+    } else {
+       self->geom.bwidth = self->geom.cbwidth = 0;
+    }
+    STRUT_SET(self->innersize, self->geom.cbwidth, self->geom.cbwidth,
+             self->geom.cbwidth, self->geom.cbwidth);
+    self->geom.width = self->client->area.width + self->geom.cbwidth * 2;
+    g_assert(self->geom.width > 0);
+
+    /* set border widths */
+    XSetWindowBorderWidth(ob_display, self->plate,  self->geom.cbwidth);
+    XSetWindowBorderWidth(ob_display, self->window, self->geom.bwidth);
+    XSetWindowBorderWidth(ob_display, self->title,  self->geom.bwidth);
+    XSetWindowBorderWidth(ob_display, self->handle, self->geom.bwidth);
+    XSetWindowBorderWidth(ob_display, self->lgrip,  self->geom.bwidth);
+    XSetWindowBorderWidth(ob_display, self->rgrip,  self->geom.bwidth);
+  
+    /* position/size and map/unmap all the windows */
+
+    if (self->decorations & Decor_Titlebar) {
+       self->geom.title_height = self->geom.font_height +
+           self->geom.bevel * 2;
+       XMoveResizeWindow(ob_display, self->title,
+                         -self->geom.bwidth, -self->geom.bwidth,
+                         self->geom.width, self->geom.title_height);
+       self->innersize.top += self->geom.title_height + self->geom.bwidth;
+       XMapWindow(ob_display, self->title);
+
+       /* layout the title bar elements */
+       /*XXX layoutTitle(); */
+    } else {
+       XUnmapWindow(ob_display, self->title);
+       /* make all the titlebar stuff not render */
+       self->decorations &= ~(Decor_Icon | Decor_Iconify |
+                              Decor_Maximize | Decor_Close |
+                              Decor_AllDesktops);
+    }
+
+    if (self->decorations & Decor_Handle) {
+       self->geom.handle_y = self->innersize.top +
+           self->client->area.height + self->geom.cbwidth;
+       XMoveResizeWindow(ob_display, self->handle,
+                         -self->geom.bwidth, self->geom.handle_y,
+                         self->geom.width, self->geom.handle_height);
+       XMoveWindow(ob_display, self->lgrip,
+                   -self->geom.bwidth, -self->geom.bwidth);
+       XMoveWindow(ob_display, self->rgrip,
+                   -self->geom.bwidth + self->geom.width -
+                   self->geom.grip_width, -self->geom.bwidth);
+       self->innersize.bottom += self->geom.handle_height +
+           self->geom.bwidth;
+       XMapWindow(ob_display, self->handle);
+    } else
+       XUnmapWindow(ob_display, self->handle);
+  
+    XResizeWindow(ob_display, self->window, self->geom.width,
+                 (self->client->shaded ? self->geom.title_height :
+                  self->innersize.top + self->innersize.bottom +
+                  self->client->area.height));
+
+    /* do this in two steps because clients whose gravity is set to
+       'Static' don't end up getting moved at all with an XMoveResizeWindow */
+    XMoveWindow(ob_display, self->plate,
+               self->innersize.left - self->geom.cbwidth,
+               self->innersize.top - self->geom.cbwidth);
+    XResizeWindow(ob_display, self->plate, self->client->area.width,
+                 self->client->area.height);
+
+    STRUT_SET(self->size,
+             self->innersize.left + self->geom.bwidth,
+             self->innersize.right + self->geom.bwidth,
+             self->innersize.top + self->geom.bwidth,
+             self->innersize.bottom + self->geom.bwidth);
+
+    RECT_SET_SIZE(self->area,
+                 self->client->area.width +
+                 self->size.left + self->size.right,
+                 self->client->area.height +
+                 self->size.top + self->size.bottom);
+
+    /*
+    // render all the elements
+    int screen = _client->screen();
+    bool focus = _client->focused();
+    if (_decorations & Client::Decor_Titlebar) {
+    render(screen, otk::Size(geom.width, geom.title_height()), _title,
+    &_title_sur, *(focus ? style->titlebarFocusBackground() :
+    style->titlebarUnfocusBackground()), false);
+    
+    renderLabel();
+    renderMax();
+    renderDesk();
+    renderIconify();
+    renderIcon();
+    renderClose();
+    }
+
+    if (_decorations & Client::Decor_Handle) {
+    render(screen, otk::Size(geom.width, geom.handle_height), _handle,
+    &_handle_sur, *(focus ? style->handleFocusBackground() :
+    style->handleUnfocusBackground()));
+    render(screen, otk::Size(geom.grip_width(), geom.handle_height), _lgrip,
+    &_grip_sur, *(focus ? style->gripFocusBackground() :
+    style->gripUnfocusBackground()));
+    if ((focus ? style->gripFocusBackground() :
+    style->gripUnfocusBackground())->parentRelative())
+    XSetWindowBackgroundPixmap(**otk::display, _rgrip, ParentRelative);
+    else {
+    XSetWindowBackgroundPixmap(**otk::display, _rgrip, _grip_sur->pixmap());
+    }
+    XClearWindow(**otk::display, _rgrip);
+    }
+
+    XSetWindowBorder(**otk::display, _plate,
+    focus ? style->clientBorderFocusColor()->pixel() :
+    style->clientBorderUnfocusColor()->pixel());
+
+    */
+     
+    frame_adjust_shape(self);
+}
+
+void frame_adjust_position(Frame *self)
+{
+    self->area.x = self->client->area.x;
+    self->area.y = self->client->area.y;
+    frame_client_gravity(self, &self->area.x, &self->area.y);
+    XMoveWindow(ob_display, self->window, self->area.x, self->area.y);
+}
+
+void frame_adjust_shape(Frame *self)
+{
+#ifdef SHAPE
+    int num;
+    XRectangle xrect[2];
+
+    if (!self->client->shaped) {
+       /* clear the shape on the frame window */
+       XShapeCombineMask(ob_display, self->window, ShapeBounding,
+                         self->innersize.left,
+                         self->innersize.top,
+                         None, ShapeSet);
+    } else {
+       /* make the frame's shape match the clients */
+       XShapeCombineShape(ob_display, self->window, ShapeBounding,
+                          self->innersize.left,
+                          self->innersize.top,
+                          self->client->window, ShapeBounding, ShapeSet);
+
+       num = 0;
+       if (self->decorations & Decor_Titlebar) {
+           xrect[0].x = -self->geom.bevel;
+           xrect[0].y = -self->geom.bevel;
+           xrect[0].width = self->geom.width + self->geom.bwidth * 2;
+           xrect[0].height = self->geom.title_height +
+               self->geom.bwidth * 2;
+           ++num;
+       }
+
+       if (self->decorations & Decor_Handle) {
+           xrect[1].x = -self->geom.bevel;
+           xrect[1].y = self->geom.handle_y;
+           xrect[1].width = self->geom.width + self->geom.bwidth * 2;
+           xrect[1].height = self->geom.handle_height +
+               self->geom.bwidth * 2;
+           ++num;
+       }
+
+       XShapeCombineRectangles(ob_display, self->window,
+                               ShapeBounding, 0, 0, xrect, num,
+                               ShapeUnion, Unsorted);
+    }
+#endif
+}
+
+void frame_client_gravity(Frame *self, int *x, int *y)
+{
+    /* horizontal */
+    switch (self->client->gravity) {
+    default:
+    case NorthWestGravity:
+    case SouthWestGravity:
+    case WestGravity:
+       break;
+
+    case NorthGravity:
+    case SouthGravity:
+    case CenterGravity:
+       *x -= (self->size.left + self->size.right) / 2;
+       break;
+
+    case NorthEastGravity:
+    case SouthEastGravity:
+    case EastGravity:
+       *x -= self->size.left + self->size.right;
+       break;
+
+    case ForgetGravity:
+    case StaticGravity:
+       *x -= self->size.left;
+       break;
+    }
+
+    /* vertical */
+    switch (self->client->gravity) {
+    default:
+    case NorthWestGravity:
+    case NorthEastGravity:
+    case NorthGravity:
+       break;
+
+    case CenterGravity:
+    case EastGravity:
+    case WestGravity:
+       *y -= (self->size.top + self->size.bottom) / 2;
+       break;
+
+    case SouthWestGravity:
+    case SouthEastGravity:
+    case SouthGravity:
+       *y -= self->size.top + self->size.bottom;
+       break;
+
+    case ForgetGravity:
+    case StaticGravity:
+       *y -= self->size.top;
+       break;
+    }
+}
+
+void frame_frame_gravity(Frame *self, int *x, int *y)
+{
+    /* horizontal */
+    switch (self->client->gravity) {
+    default:
+    case NorthWestGravity:
+    case WestGravity:
+    case SouthWestGravity:
+       break;
+    case NorthGravity:
+    case CenterGravity:
+    case SouthGravity:
+       *x += (self->size.left + self->size.right) / 2;
+       break;
+    case NorthEastGravity:
+    case EastGravity:
+    case SouthEastGravity:
+       *x += self->size.left + self->size.right;
+       break;
+    case StaticGravity:
+    case ForgetGravity:
+       x += self->size.left;
+       break;
+    }
+
+    /* vertical */
+    switch (self->client->gravity) {
+    default:
+    case NorthWestGravity:
+    case WestGravity:
+    case SouthWestGravity:
+       break;
+    case NorthGravity:
+    case CenterGravity:
+    case SouthGravity:
+       *y += (self->size.top + self->size.bottom) / 2;
+       break;
+    case NorthEastGravity:
+    case EastGravity:
+    case SouthEastGravity:
+       *y += self->size.top + self->size.bottom;
+       break;
+    case StaticGravity:
+    case ForgetGravity:
+       *y += self->size.top;
+       break;
+    }
+}
+
+void frame_adjust_state(Frame *self)
+{
+    /* XXX do shit.. buttons? */
+}
+
+void frame_adjust_focus(Frame *self)
+{
+    /* XXX optimizations later... */
+    frame_adjust_size(self);
+}
+
+void frame_adjust_title(Frame *self)
+{
+    /* XXX optimizations later... */
+    frame_adjust_size(self);
+}
+
+void frame_adjust_icon(Frame *self)
+{
+    /* XXX render icon */
+}
+
+GQuark frame_get_context(Client *client, Window win)
+{
+    Frame *self;
+
+    if (win == ob_root) return g_quark_try_string("root");
+    if (client == NULL) return g_quark_try_string("none");
+    if (win == client->window) return g_quark_try_string("client");
+
+    self = client->frame;
+    if (win == self->window) return g_quark_try_string("frame");
+    if (win == self->plate)  return g_quark_try_string("frame");
+    if (win == self->title)  return g_quark_try_string("titlebar");
+    if (win == self->label)  return g_quark_try_string("titlebar");
+    if (win == self->handle) return g_quark_try_string("handle");
+    if (win == self->lgrip)  return g_quark_try_string("blcorner");
+    if (win == self->rgrip)  return g_quark_try_string("brcorner");
+
+    return g_quark_try_string("none");
+}
+
+void frame_startup(void)
+{
+    g_quark_from_string("none");
+    g_quark_from_string("root");
+    g_quark_from_string("client");
+    g_quark_from_string("titlebar");
+    g_quark_from_string("handle");
+    g_quark_from_string("frame");
+    g_quark_from_string("blcorner");
+    g_quark_from_string("brcorner");
+    g_quark_from_string("tlcorner");
+    g_quark_from_string("trcorner");
+    g_quark_from_string("foo");
+}
diff --git a/c/frame.h b/c/frame.h
new file mode 100644 (file)
index 0000000..7d84167
--- /dev/null
+++ b/c/frame.h
@@ -0,0 +1,101 @@
+#ifndef __frame_h
+#define __frame_h
+
+#include <X11/Xlib.h>
+#include "geom.h"
+#include "client.h"
+
+/*! Varius geometry settings in the frame decorations */
+typedef struct {
+    int width; /* title and handle */
+    int font_height;
+/*  int title_height() { return font_height + bevel*2; } */
+    int title_height;
+    int label_width;
+/*  int label_height() { return font_height; } */
+    int handle_height; /* static, from the style */
+    int icon_x;        /* x-position of the window icon button */
+    int title_x;       /* x-position of the window title */
+    int iconify_x;     /* x-position of the window iconify button */
+    int desktop_x;     /* x-position of the window all-desktops button */
+    int max_x;         /* x-position of the window maximize button */
+    int close_x;       /* x-position of the window close button */
+    int handle_y;
+    int button_size;   /* static, from the style */
+/*  int grip_width() { return button_size * 2; } */
+    int grip_width;
+    int bevel;         /* static, from the style */
+    int bwidth;  /* frame elements' border width */
+    int cbwidth; /* client border width */
+} FrameGeometry;
+
+typedef struct Frame {
+    Window window;
+    Window plate;
+    Window title;
+    Window label;
+    Window max;
+    Window close;
+    Window desk;
+    Window icon;
+    Window iconify;
+    Window handle;
+    Window lgrip;
+    Window rgrip;
+
+    Strut  size;
+    Strut  innersize;
+    Rect   area;
+    FrameGeometry geom;
+
+    Client *client;
+    int decorations;
+
+    gboolean visible;
+} Frame;
+
+Frame *frame_new(struct Client *client);
+void frame_free(Frame *self);
+
+void frame_grab_client(Frame *self);
+void frame_release_client(Frame *self);
+
+/*! Update the frame's size to match the client */
+void frame_adjust_size(Frame *self);
+/*! Update the frame's position to match the client */
+void frame_adjust_position(Frame *self);
+/*! Shape the frame window to the client window */
+void frame_adjust_shape(Frame *self);
+/*! Update the frame to match the client's new state (for things like toggle
+  buttons, focus, and the title) XXX break this up */
+void frame_adjust_state(Frame *self);
+/*! Update the frame to match the client's focused state */
+void frame_adjust_focus(Frame *self);
+/*! Update the frame to display the client's current title */
+void frame_adjust_title(Frame *self);
+/*! Update the frame to display the client's current icon */
+void frame_adjust_icon(Frame *self);
+
+/*! Applies gravity to the client's position to find where the frame should
+  be positioned.
+  @return The proper coordinates for the frame, based on the client.
+*/
+void frame_client_gravity(Frame *self, int *x, int *y);
+
+/*! Reversly applies gravity to the frame's position to find where the client
+  should be positioned.
+    @return The proper coordinates for the client, based on the frame.
+*/
+void frame_frame_gravity(Frame *self, int *x, int *y);
+
+/*! Shows the frame */
+void frame_show(Frame *self);
+/*! Hides the frame */
+void frame_hide(Frame *self);
+
+/*! inits quarks - this will go in engines later */
+void frame_startup(void);
+
+GQuark frame_get_context(Client *client, Window win);
+
+#endif
diff --git a/c/geom.h b/c/geom.h
new file mode 100644 (file)
index 0000000..aa763db
--- /dev/null
+++ b/c/geom.h
@@ -0,0 +1,53 @@
+#ifndef __geom_h
+#define __geom_h
+
+#ifdef HAVE_ASSERT_H
+#  include <assert.h>
+#endif
+
+typedef struct Point {
+    int x;
+    int y;
+} Point;
+
+#define POINT_SET(pt, nx, ny) {pt.x = nx; pt.y = ny;}
+
+typedef struct Size {
+    int width;
+    int height;
+} Size;
+
+#define SIZE_SET(sz, w, h) {sz.width = w; sz.height = h;}
+
+typedef struct Rect {
+    int x;
+    int y;
+    int width;
+    int height;
+} Rect;
+
+#define RECT_SET_POINT(r, nx, ny) \
+  {r.x = ny; r.y = ny;}
+#define RECT_SET_SIZE(r, w, h) \
+  {r.width = w; r.height = h;}
+#define RECT_SET(r, nx, ny, w, h) \
+  {r.x = nx; r.y = ny; r.width = w; r.height = h;}
+
+#define RECT_EQUAL(r1, r2) (r1.x == r2.x && r1.y == r2.y && \
+                           r1.width == r2.width && r1.height == r2.height)
+
+typedef struct Strut {
+    int left;
+    int top;
+    int right;
+    int bottom;
+} Strut;
+
+#define STRUT_SET(s, l, t, r, b) \
+  {s.left = l; s.top = t; s.right = r; s.bottom = b; }
+
+#define STRUT_ADD(s1, s2) \
+  {s1.left = MAX(s1.left, s2.left); s1.right = MAX(s1.right, s2.right); \
+   s1.top = MAX(s1.top, s2.top); s1.bottom = MAX(s1.bottom, s2.bottom); }
+
+#endif
diff --git a/c/gettext.h b/c/gettext.h
new file mode 100644 (file)
index 0000000..7bbc6a9
--- /dev/null
@@ -0,0 +1,73 @@
+/* Convenience header for conditional use of GNU <libintl.h>.
+   Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Library General Public License as published
+   by the Free Software Foundation; either version 2, 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA.  */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+
+/* NLS can be disabled through the configure --disable-nls option.  */
+#if ENABLE_NLS
+
+/* Get declarations of GNU message catalog functions.  */
+# include <libintl.h>
+
+#else
+
+/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
+   chokes if dcgettext is defined as a macro.  So include it now, to make
+   later inclusions of <locale.h> a NOP.  We don't include <libintl.h>
+   as well because people using "gettext.h" will not include <libintl.h>,
+   and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
+   is OK.  */
+#if defined(__sun)
+# include <locale.h>
+#endif
+
+/* Disabled NLS.
+   The casts to 'const char *' serve the purpose of producing warnings
+   for invalid uses of the value returned from these functions.
+   On pre-ANSI systems without 'const', the config.h file is supposed to
+   contain "#define const".  */
+# define gettext(Msgid) ((const char *) (Msgid))
+# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
+# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
+# define ngettext(Msgid1, Msgid2, N) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
+
+#endif
+
+/* A pseudo function call that serves as a marker for the automated
+   extraction of messages, but does not call gettext().  The run-time
+   translation is done at a different place in the code.
+   The argument, String, should be a literal string.  Concatenated strings
+   and other string expressions won't work.
+   The macro's expansion is not parenthesized, so that it is suitable as
+   initializer for static 'char[]' or 'const char[]' variables.  */
+#define gettext_noop(String) String
+
+/* Custom macro to make life easier */
+#define _(str) gettext(str)
+
+#endif /* _LIBGETTEXT_H */
diff --git a/c/hooks.c b/c/hooks.c
new file mode 100644 (file)
index 0000000..6bf15d2
--- /dev/null
+++ b/c/hooks.c
@@ -0,0 +1,285 @@
+#include "hooks.h"
+#include <Python.h>
+#include <glib.h>
+
+/* the 'hooks' module and its dictionary */
+static PyObject *hooks = NULL, *hooksdict = NULL;
+
+/*
+ *
+ * Define the type 'Hook'
+ *
+ */
+#define IS_HOOK(v)  ((v)->ob_type == &HookType)
+
+staticforward PyTypeObject HookType;
+
+typedef struct {
+    PyObject_HEAD
+    GSList *funcs;
+} HookObject;
+
+static PyObject *create_Hook(PyObject *self, PyObject *args)
+{
+    HookObject *hook;
+    char *name;
+    int ret;
+
+    (void) self;
+
+    if (!PyArg_ParseTuple(args, "s:Hook", &name))
+       return NULL;
+
+    hook = PyObject_New(HookObject, &HookType);
+    hook->funcs = NULL;
+
+    /* add it to the hooks module */
+    ret = PyDict_SetItemString(hooksdict, name, (PyObject*) hook);
+    Py_DECREF(hook);
+
+    if (ret == -1) {
+       char *s = g_strdup_printf(
+           "Failed to add the hook '%s' to the 'hooks' module", name);
+       PyErr_SetString(PyExc_RuntimeError, s);
+       g_free(s);
+       return NULL;
+    }
+     
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static void hook_dealloc(HookObject *self)
+{
+    GSList *it;
+
+    for (it = self->funcs; it != NULL; it = it->next)
+       Py_DECREF((PyObject*) it->data);
+     
+    PyObject_Del((PyObject*) self);
+}
+
+static PyObject *hook_fire(HookObject *self, PyObject *args)
+{
+    GSList *it;
+
+    if (!IS_HOOK(self)) {
+       PyErr_SetString(PyExc_TypeError,
+                       "descriptor 'fire' requires a 'Hook' object");
+       return NULL;
+    }
+
+    for (it = self->funcs; it != NULL; it = it->next) {
+       PyObject *ret = PyObject_CallObject(it->data, args);
+       if (ret == NULL)
+           return NULL;
+       Py_DECREF(ret);
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *hook_add(HookObject *self, PyObject *args)
+{
+    PyObject *func;
+     
+    if (!IS_HOOK(self)) {
+       PyErr_SetString(PyExc_TypeError,
+                       "descriptor 'add' requires a 'Hook' object");
+       return NULL;
+    }
+    if (!PyArg_ParseTuple(args, "O:add", &func))
+       return NULL;
+    if (!PyCallable_Check(func)) {
+       PyErr_SetString(PyExc_TypeError,
+                       "descriptor 'add' requires a callable argument");
+       return NULL;
+    }
+    self->funcs = g_slist_append(self->funcs, func);
+    Py_INCREF(func);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *hook_remove(HookObject *self, PyObject *args)
+{
+    PyObject *func;
+    GSList *it;
+     
+    if (!IS_HOOK(self)) {
+       PyErr_SetString(PyExc_TypeError,
+                       "descriptor 'remove' requires a 'Hook' object");
+       return NULL;
+    }
+    if (!PyArg_ParseTuple(args, "O:remove", &func))
+       return NULL;
+    if (!PyCallable_Check(func)) {
+       PyErr_SetString(PyExc_TypeError,
+                       "descriptor 'remove' requires a callable argument");
+       return NULL;
+    }
+    it = g_slist_find(self->funcs, func);
+    if (it != NULL) {
+       self->funcs = g_slist_delete_link(self->funcs, it);
+       Py_DECREF(func);
+
+       Py_INCREF(Py_None);
+       return Py_None;
+    }
+    PyErr_SetString(PyExc_TypeError,
+                   "given callable object was not found in Hook");
+    return NULL;
+}
+
+static PyObject *hook_count(HookObject *self, PyObject *args)
+{
+    if (!IS_HOOK(self)) {
+       PyErr_SetString(PyExc_TypeError,
+                       "descriptor 'fire' requires a 'Hook' object");
+       return NULL;
+    }
+    if (!PyArg_ParseTuple(args, ":count"))
+       return NULL;
+
+    return PyInt_FromLong(g_slist_length(self->funcs));
+}
+
+static PyTypeObject HookType = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "Hook",
+    sizeof(HookObject),
+    0,
+    (destructor) hook_dealloc, /*tp_dealloc*/
+    0,                         /*tp_print*/
+    0,                         /*tp_getattr*/
+    0,                         /*tp_setattr*/
+    0,                         /*tp_compare*/
+    0,                         /*tp_repr*/
+    0,                         /*tp_as_number*/
+    0,                         /*tp_as_sequence*/
+    0,                         /*tp_as_mapping*/
+    0,                         /*tp_hash */
+};
+
+static PyMethodDef HookMethods[] = {
+    {"fire", (PyCFunction)hook_fire, METH_VARARGS,
+     "hook.fire() -- Fire the added hook functions for the Hook."},
+    {"add", (PyCFunction)hook_add, METH_VARARGS,
+     "hook.add(func) -- Add a function to the hook." },
+    {"remove", (PyCFunction)hook_remove, METH_VARARGS,
+     "hook.remove(func) -- Remove a function from the hook." },
+    {"count", (PyCFunction)hook_count, METH_VARARGS,
+     "hook.count() -- Return the number of functions in the hook." },
+     
+    { NULL, NULL, 0, NULL }
+};
+
+
+/*
+ *
+ * Module initialization/finalization
+ *
+ */
+
+/* the "events" hook */
+static HookObject *events_hook = NULL, *keyboard_hook = NULL,
+    *pointer_hook = NULL;
+
+static PyMethodDef HooksMethods[] = {
+    {"create", create_Hook, METH_VARARGS,
+     "hooks.create('name') -- Add a hook called 'name' to the hooks module."},
+     
+    { NULL, NULL, 0, NULL }
+};
+
+void hooks_startup()
+{
+    int ret;
+
+    HookType.ob_type = &PyType_Type;
+    HookType.tp_methods = HookMethods;
+    PyType_Ready(&HookType);
+
+    Py_InitModule("hooks", HooksMethods);
+
+    /* get the hooks module/dict */
+    hooks = PyImport_ImportModule("hooks"); /* new */
+    g_assert(hooks != NULL);
+    hooksdict = PyModule_GetDict(hooks); /* borrowed */
+    g_assert(hooksdict != NULL);
+
+    /* create the "events" hook */
+    events_hook = PyObject_New(HookObject, &HookType);
+    events_hook->funcs = NULL;
+
+    /* add it to the hooks module */
+    ret = PyDict_SetItemString(hooksdict, "events", (PyObject*) events_hook);
+    g_assert(ret == 0);
+
+    /* create the "keyboard" hook */
+    keyboard_hook = PyObject_New(HookObject, &HookType);
+    keyboard_hook->funcs = NULL;
+
+    /* add it to the hooks module */
+    ret = PyDict_SetItemString(hooksdict, "keyboard",
+                              (PyObject*) keyboard_hook);
+    g_assert(ret == 0);
+
+    /* create the "pointer" hook */
+    pointer_hook = PyObject_New(HookObject, &HookType);
+    pointer_hook->funcs = NULL;
+
+    /* add it to the hooks module */
+    ret = PyDict_SetItemString(hooksdict, "pointer", (PyObject*) pointer_hook);
+    g_assert(ret == 0);
+}
+
+void hooks_shutdown()
+{
+    Py_DECREF(pointer_hook);
+    Py_DECREF(keyboard_hook);
+    Py_DECREF(events_hook);
+    Py_DECREF(hooks);
+}
+
+void hooks_fire(EventData *data)
+{
+    PyObject *ret, *args;
+
+    g_assert(events_hook != NULL);
+
+    args = Py_BuildValue("(O)", data);
+    ret = hook_fire(events_hook, args);
+    Py_DECREF(args);
+    if (ret == NULL)
+       PyErr_Print();
+}
+
+void hooks_fire_keyboard(EventData *data)
+{
+    PyObject *ret, *args;
+
+    g_assert(events_hook != NULL);
+
+    args = Py_BuildValue("(O)", data);
+    ret = hook_fire(keyboard_hook, args);
+    Py_DECREF(args);
+    if (ret == NULL)
+       PyErr_Print();
+}
+
+void hooks_fire_pointer(EventData *data)
+{
+    PyObject *ret, *args;
+
+    g_assert(events_hook != NULL);
+
+    args = Py_BuildValue("(O)", data);
+    ret = hook_fire(pointer_hook, args);
+    Py_DECREF(args);
+    if (ret == NULL)
+       PyErr_Print();
+}
diff --git a/c/hooks.h b/c/hooks.h
new file mode 100644 (file)
index 0000000..477c812
--- /dev/null
+++ b/c/hooks.h
@@ -0,0 +1,23 @@
+#ifndef __hooks_h
+#define __hooks_h
+
+#include "eventdata.h"
+
+void hooks_startup();
+void hooks_shutdown();
+
+void hooks_fire(EventData *data);
+
+void hooks_fire_keyboard(EventData *data);
+
+void hooks_fire_pointer(EventData *data);
+
+#define LOGICALHOOK(type, context, client) \
+{ EventData *data = eventdata_new_logical(Logical_##type, \
+                                         context, client); \
+  g_assert(data != NULL); \
+  hooks_fire(data); \
+  eventdata_free(data); \
+}
+
+#endif
diff --git a/c/kbind.c b/c/kbind.c
new file mode 100644 (file)
index 0000000..399ec83
--- /dev/null
+++ b/c/kbind.c
@@ -0,0 +1,354 @@
+#include "focus.h"
+#include "openbox.h"
+#include "hooks.h"
+#include "kbind.h"
+
+#include <glib.h>
+#ifdef HAVE_STRING_H
+#  include <string.h>
+#endif
+
+typedef struct KeyBindingTree {
+    guint state;
+    guint key;
+    GList *keylist;
+
+    /* the next binding in the tree at the same level */
+    struct KeyBindingTree *next_sibling; 
+    /* the first child of this binding (next binding in a chained sequence).*/
+    struct KeyBindingTree *first_child;
+} KeyBindingTree;
+
+
+static KeyBindingTree *firstnode, *curpos;
+static guint reset_key, reset_state;
+static gboolean grabbed, user_grabbed;
+
+guint kbind_translate_modifier(char *str)
+{
+    if (!strcmp("Mod1", str)) return Mod1Mask;
+    else if (!strcmp("Mod2", str)) return Mod2Mask;
+    else if (!strcmp("Mod3", str)) return Mod3Mask;
+    else if (!strcmp("Mod4", str)) return Mod4Mask;
+    else if (!strcmp("Mod5", str)) return Mod5Mask;
+    else if (!strcmp("C", str)) return ControlMask;
+    else if (!strcmp("S", str)) return ShiftMask;
+    g_warning("Invalid modifier '%s' in binding.", str);
+    return 0;
+}
+
+static gboolean translate(char *str, guint *state, guint *keycode)
+{
+    char **parsed;
+    char *l;
+    int i;
+    gboolean ret = FALSE;
+    KeySym sym;
+
+    parsed = g_strsplit(str, "-", -1);
+    
+    /* first, find the key (last token) */
+    l = NULL;
+    for (i = 0; parsed[i] != NULL; ++i)
+       l = parsed[i];
+    if (l == NULL)
+       goto translation_fail;
+
+    /* figure out the mod mask */
+    *state = 0;
+    for (i = 0; parsed[i] != l; ++i) {
+       guint m = kbind_translate_modifier(parsed[i]);
+       if (!m) goto translation_fail;
+       *state |= m;
+    }
+
+    /* figure out the keycode */
+    sym = XStringToKeysym(l);
+    if (sym == NoSymbol) {
+       g_warning("Invalid key name '%s' in key binding.", l);
+       goto translation_fail;
+    }
+    *keycode = XKeysymToKeycode(ob_display, sym);
+    if (!keycode) {
+       g_warning("Key '%s' does not exist on the display.", l); 
+       goto translation_fail;
+    }
+
+    ret = TRUE;
+
+translation_fail:
+    g_strfreev(parsed);
+    return ret;
+}
+
+static void destroytree(KeyBindingTree *tree)
+{
+    KeyBindingTree *c;
+
+    while (tree) {
+       destroytree(tree->next_sibling);
+       c = tree->first_child;
+       if (c == NULL) {
+           GList *it;
+           for (it = tree->keylist; it != NULL; it = it->next)
+               g_free(it->data);
+           g_list_free(tree->keylist);
+       }
+       g_free(tree);
+       tree = c;
+    }
+}
+
+static KeyBindingTree *buildtree(GList *keylist)
+{
+    GList *it;
+    KeyBindingTree *ret = NULL, *p;
+
+    if (g_list_length(keylist) <= 0)
+       return NULL; /* nothing in the list.. */
+
+    for (it = g_list_last(keylist); it != NULL; it = it->prev) {
+       p = ret;
+       ret = g_new(KeyBindingTree, 1);
+       ret->next_sibling = NULL;
+       if (p == NULL) {
+           GList *it;
+
+           /* this is the first built node, the bottom node of the tree */
+           ret->keylist = g_list_copy(keylist); /* shallow copy */
+           for (it = ret->keylist; it != NULL; it = it->next) /* deep copy */
+               it->data = g_strdup(it->data);
+       }
+       ret->first_child = p;
+       if (!translate(it->data, &ret->state, &ret->key)) {
+           destroytree(ret);
+           return NULL;
+       }
+    }
+    return ret;
+}
+
+static void assimilate(KeyBindingTree *node)
+{
+    KeyBindingTree *a, *b, *tmp, *last;
+
+    if (firstnode == NULL) {
+       /* there are no nodes at this level yet */
+       firstnode = node;
+    } else {
+       a = firstnode;
+       last = a;
+       b = node;
+       while (a) {
+           last = a;
+           if (!(a->state == b->state && a->key == b->key)) {
+               a = a->next_sibling;
+           } else {
+               tmp = b;
+               b = b->first_child;
+               g_free(tmp);
+               a = a->first_child;
+           }
+       }
+       if (!(last->state == b->state && last->key == a->key))
+           last->next_sibling = b;
+       else {
+           last->first_child = b->first_child;
+           g_free(b);
+       }
+    }
+}
+
+KeyBindingTree *find(KeyBindingTree *search, gboolean *conflict)
+{
+    KeyBindingTree *a, *b;
+
+    *conflict = FALSE;
+
+    a = firstnode;
+    b = search;
+    while (a && b) {
+       if (!(a->state == b->state && a->key == b->key)) {
+           a = a->next_sibling;
+       } else {
+           if ((a->first_child == NULL) == (b->first_child == NULL)) {
+               if (a->first_child == NULL) {
+                   /* found it! (return the actual node, not the search's) */
+                   return a;
+               }
+           } else {
+               *conflict = TRUE;
+               return NULL; /* the chain status' don't match (conflict!) */
+           }
+           b = b->first_child;
+           a = a->first_child;
+       }
+    }
+    return NULL; // it just isn't in here
+}
+
+static void grab_keys(gboolean grab)
+{
+    if (!grab) {
+       XUngrabKey(ob_display, AnyKey, AnyModifier, ob_root);
+    } else {
+       KeyBindingTree *p = firstnode;
+       while (p) {
+           XGrabKey(ob_display, p->key, p->state, ob_root, FALSE,
+                    GrabModeAsync, GrabModeSync);
+           p = p->next_sibling;
+       }
+    }
+}
+
+void reset_chains()
+{
+    /* XXX kill timer */
+    curpos = NULL;
+    if (grabbed) {
+       grabbed = FALSE;
+       g_message("reset chains. user: %d", user_grabbed);
+       if (!user_grabbed)
+           XUngrabKeyboard(ob_display, CurrentTime);
+    }
+}
+
+void kbind_fire(guint state, guint key, gboolean press)
+{
+    EventData *data;
+    struct Client *c = focus_client;
+    GQuark context = c != NULL ? g_quark_try_string("client")
+                              : g_quark_try_string("root");
+
+    if (user_grabbed) {
+       data = eventdata_new_key(press ? Key_Press : Key_Release,
+                                context, c, state, key, NULL);
+       g_assert(data != NULL);
+       hooks_fire_keyboard(data);
+       eventdata_free(data);
+    }
+
+    if (key == reset_key && state == reset_state) {
+       reset_chains();
+       XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
+    } else {
+       KeyBindingTree *p;
+       if (curpos == NULL)
+           p = firstnode;
+       else
+           p = curpos->first_child;
+       while (p) {
+           if (p->key == key && p->state == state) {
+               if (p->first_child != NULL) { /* part of a chain */
+                   /* XXX TIMER */
+                   if (!grabbed && !user_grabbed) {
+                       /*grab should never fail because we should have a sync
+                         grab at this point */
+                       XGrabKeyboard(ob_display, ob_root, 0, GrabModeAsync, 
+                                     GrabModeSync, CurrentTime);
+                   }
+                   grabbed = TRUE;
+                   curpos = p;
+                   XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
+               } else {
+                   data = eventdata_new_key(press ? Key_Press : Key_Release,
+                                            context, c, state, key,
+                                            p->keylist);
+                   g_assert(data != NULL);
+                   hooks_fire(data);
+                   eventdata_free(data);
+
+                   XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
+                   reset_chains();
+               }
+               break;
+           }
+           p = p->next_sibling;
+       }
+    }
+}
+
+gboolean kbind_add(GList *keylist)
+{
+    KeyBindingTree *tree, *t;
+    gboolean conflict;
+
+    if (!(tree = buildtree(keylist)))
+       return FALSE; /* invalid binding requested */
+
+    t = find(tree, &conflict);
+    if (conflict) {
+       /* conflicts with another binding */
+       destroytree(tree);
+       return FALSE;
+    }
+
+    if (t != NULL) {
+       /* already bound to something */
+       destroytree(tree);
+    } else {
+       /* grab the server here to make sure no key pressed go missed */
+       XGrabServer(ob_display);
+       XSync(ob_display, FALSE);
+
+       grab_keys(FALSE);
+
+       /* assimilate this built tree into the main tree */
+       assimilate(tree); // assimilation destroys/uses the tree
+
+       grab_keys(TRUE); 
+
+       XUngrabServer(ob_display);
+       XFlush(ob_display);
+    }
+    return TRUE;
+}
+
+void kbind_clearall()
+{
+    grab_keys(FALSE);
+    destroytree(firstnode);
+    firstnode = NULL;
+    grab_keys(TRUE);
+}
+
+void kbind_startup()
+{
+    gboolean b;
+
+    curpos = firstnode = NULL;
+    grabbed = user_grabbed = FALSE;
+
+    b = translate("C-G", &reset_state, &reset_key);
+    g_assert(b);
+}
+
+void kbind_shutdown()
+{
+    if (grabbed || user_grabbed) {
+       grabbed = FALSE;
+       kbind_grab_keyboard(FALSE);
+    }
+    grab_keys(FALSE);
+    destroytree(firstnode);
+    firstnode = NULL;
+}
+
+gboolean kbind_grab_keyboard(gboolean grab)
+{
+    gboolean ret = TRUE;
+
+    if (!grab)
+       g_message("grab_keyboard(false). grabbed: %d", grabbed);
+
+    user_grabbed = grab;
+    if (!grabbed) {
+       if (grab)
+           ret = XGrabKeyboard(ob_display, ob_root, 0, GrabModeAsync, 
+                               GrabModeAsync, CurrentTime) == GrabSuccess;
+       else
+           XUngrabKeyboard(ob_display, CurrentTime);
+    }
+    return ret;
+}
diff --git a/c/kbind.h b/c/kbind.h
new file mode 100644 (file)
index 0000000..3a584b7
--- /dev/null
+++ b/c/kbind.h
@@ -0,0 +1,23 @@
+#ifndef __kbind_h
+#define __kbind_h
+
+#include <glib.h>
+
+void kbind_startup();
+void kbind_shutdown();
+
+/*! Adds a new key binding
+  A binding will fail to be added if the binding already exists (as part of
+  a chain or not), or if any of the strings in the keylist are invalid.    
+  @return TRUE if the binding could be added; FALSE if it could not.
+*/
+gboolean kbind_add(GList *keylist);
+void kbind_clearall();
+
+guint kbind_translate_modifier(char *str);
+
+void kbind_fire(guint state, guint key, gboolean press);
+
+gboolean kbind_grab_keyboard(gboolean grab);
+
+#endif
diff --git a/c/mbind.c b/c/mbind.c
new file mode 100644 (file)
index 0000000..f363be6
--- /dev/null
+++ b/c/mbind.c
@@ -0,0 +1,220 @@
+#include "mbind.h"
+#include "kbind.h"
+#include "frame.h"
+#include "openbox.h"
+#include "eventdata.h"
+#include "hooks.h"
+
+#include <glib.h>
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif
+
+/* GData of GSList*'s of PointerBinding*'s. */
+static GData *bound_contexts;
+
+static gboolean grabbed;
+
+struct mbind_foreach_grab_temp {
+    Client *client;
+    gboolean grab;
+};
+
+typedef struct {
+    guint state;
+    guint button;
+    char *name;
+} PointerBinding;
+
+static gboolean translate(char *str, guint *state, guint *button)
+{
+    char **parsed;
+    char *l;
+    int i;
+    gboolean ret = FALSE;
+
+    parsed = g_strsplit(str, "-", -1);
+    
+    /* first, find the button (last token) */
+    l = NULL;
+    for (i = 0; parsed[i] != NULL; ++i)
+       l = parsed[i];
+    if (l == NULL)
+       goto translation_fail;
+
+    /* figure out the mod mask */
+    *state = 0;
+    for (i = 0; parsed[i] != l; ++i) {
+       guint m = kbind_translate_modifier(parsed[i]);
+       if (!m) goto translation_fail;
+       *state |= m;
+    }
+
+    /* figure out the button */
+    *button = atoi(l);
+    if (!*button) {
+       g_warning("Invalid button '%s' in pointer binding.", l);
+       goto translation_fail;
+    }
+
+    ret = TRUE;
+
+translation_fail:
+    g_strfreev(parsed);
+    return ret;
+}
+
+void grab_button(Client *client, guint state, guint button, GQuark context,
+                gboolean grab)
+{
+    Window win;
+    int mode = GrabModeAsync;
+    unsigned int mask;
+
+    if (context == g_quark_try_string("frame")) {
+       win = client->frame->window;
+       mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask;
+    } else if (context == g_quark_try_string("client")) {
+       win = client->frame->plate;
+       mode = GrabModeSync; /* this is handled in mbind_fire */
+       mask = ButtonPressMask; /* can't catch more than this with Sync mode
+                                  the release event is manufactured in
+                                  mbind_fire */
+    } else return;
+
+    if (grab)
+       XGrabButton(ob_display, button, state, win, FALSE, mask, mode,
+                   GrabModeAsync, None, None);
+    else
+       XUngrabButton(ob_display, button, state, win);
+}
+
+static void mbind_foreach_grab(GQuark key, gpointer data, gpointer user_data)
+{
+    struct mbind_foreach_grab_temp *d = user_data;
+    PointerBinding *b = ((GSList *)data)->data;
+    if (b != NULL)
+       grab_button(d->client, b->state, b->button, key, d->grab);
+}
+  
+void mbind_grab_all(Client *client, gboolean grab)
+{
+    struct mbind_foreach_grab_temp bt;
+    bt.client = client;
+    bt.grab = grab;
+    g_datalist_foreach(&bound_contexts, mbind_foreach_grab, &bt);
+}
+
+void grab_all_clients(gboolean grab)
+{
+    GSList *it;
+
+    for (it = client_list; it != NULL; it = it->next)
+       mbind_grab_all(it->data, grab);
+}
+
+void mbind_startup()
+{
+    grabbed = FALSE;
+    g_datalist_init(&bound_contexts);
+}
+
+void mbind_shutdown()
+{
+    if (grabbed)
+       mbind_grab_pointer(FALSE);
+    mbind_clearall();
+    g_datalist_clear(&bound_contexts);
+}
+
+gboolean mbind_add(char *name, GQuark context)
+{
+    guint state, button;
+    PointerBinding *b;
+    GSList *it;
+
+    if (!translate(name, &state, &button))
+       return FALSE;
+
+    for (it = g_datalist_id_get_data(&bound_contexts, context);
+        it != NULL; it = it->next){
+       b = it->data;
+       if (b->state == state && b->button == button)
+           return TRUE; /* already bound */
+    }
+
+    grab_all_clients(FALSE);
+
+    /* add the binding */
+    b = g_new(PointerBinding, 1);
+    b->state = state;
+    b->button = button;
+    b->name = g_strdup(name);
+    g_datalist_id_set_data(&bound_contexts, context, 
+        g_slist_append(g_datalist_id_get_data(&bound_contexts, context), b));
+    grab_all_clients(TRUE);
+
+    return TRUE;
+}
+
+static void mbind_foreach_clear(GQuark key, gpointer data, gpointer user_data)
+{
+    GSList *it;
+    user_data = user_data;
+    for (it = data; it != NULL; it = it->next) {
+        PointerBinding *b = it->data;
+        g_free(b->name);
+        g_free(b);
+    }
+    g_slist_free(data);
+}
+void mbind_clearall()
+{
+    grab_all_clients(FALSE);
+    g_datalist_foreach(&bound_contexts, mbind_foreach_clear, NULL);
+}
+
+void mbind_fire(guint state, guint button, GQuark context, EventType type,
+               Client *client, int xroot, int yroot)
+{
+    GSList *it;
+
+    if (grabbed) {
+           EventData *data;
+           data = eventdata_new_pointer(type, context, client, state, button,
+                                        NULL, xroot, yroot);
+           g_assert(data != NULL);
+           hooks_fire_pointer(data);
+           eventdata_free(data);
+           return;
+    }
+
+    for (it = g_datalist_id_get_data(&bound_contexts, context);
+        it != NULL; it = it->next){
+       PointerBinding *b = it->data;
+       if (b->state == state && b->button == button) {
+           EventData *data;
+           data = eventdata_new_pointer(type, context, client, state, button,
+                                        b->name, xroot, yroot);
+           g_assert(data != NULL);
+           hooks_fire(data);
+           eventdata_free(data);
+           break;
+       }
+    }
+}
+
+gboolean mbind_grab_pointer(gboolean grab)
+{
+    gboolean ret = TRUE;
+    if (grab)
+       ret = XGrabPointer(ob_display, ob_root, FALSE, (ButtonPressMask |
+                                                       ButtonReleaseMask |
+                                                       ButtonMotionMask |
+                                                       PointerMotionMask),
+                          GrabModeAsync, GrabModeAsync, None, None,
+                          CurrentTime) == GrabSuccess;
+    else
+       XUngrabPointer(ob_display, CurrentTime);
+    return ret;
+}
diff --git a/c/mbind.h b/c/mbind.h
new file mode 100644 (file)
index 0000000..dfd8c91
--- /dev/null
+++ b/c/mbind.h
@@ -0,0 +1,21 @@
+#ifndef __mbind_h
+#define __mbind_h
+
+#include "obexport.h"
+#include "client.h"
+#include <glib.h>
+
+void mbind_startup();
+void mbind_shutdown();
+
+/*! Adds a new pointer binding */
+gboolean mbind_add(char *name, GQuark context);
+void mbind_clearall();
+
+void mbind_fire(guint state, guint button, GQuark context, EventType type,
+               Client *client, int xroot, int yroot);
+
+void mbind_grab_all(Client *client, gboolean grab);
+gboolean mbind_grab_pointer(gboolean grab);
+
+#endif
diff --git a/c/obexport.c b/c/obexport.c
new file mode 100644 (file)
index 0000000..d92e319
--- /dev/null
@@ -0,0 +1,116 @@
+#include "obexport.h"
+#include <Python.h>
+#include <glib.h>
+
+static PyMethodDef obMethods[] = {
+    { NULL, NULL, 0, NULL }
+};
+
+#define ADD_INT_CONST(n) (PyModule_AddIntConstant(ob, #n, n))
+
+void obexport_startup()
+{
+    PyObject *ob, *obdict;
+
+    Py_InitModule("ob", obMethods);
+
+    /* get the ob module/dict */
+    ob = PyImport_ImportModule("ob"); /* new */
+    g_assert(ob != NULL);
+    obdict = PyModule_GetDict(ob); /* borrowed */
+    g_assert(obdict != NULL);
+
+    /* define all the constants! */
+
+    /* State */
+    ADD_INT_CONST(State_Starting);
+    ADD_INT_CONST(State_Exiting);
+    ADD_INT_CONST(State_Running);
+
+    /* Corner */
+    ADD_INT_CONST(Corner_TopLeft);
+    ADD_INT_CONST(Corner_TopRight);
+    ADD_INT_CONST(Corner_BottomLeft);
+    ADD_INT_CONST(Corner_BottomRight);
+
+    /* Orientation */
+    ADD_INT_CONST(Orientation_Horz);
+    ADD_INT_CONST(Orientation_Vert);
+
+    /* Gravity */
+    ADD_INT_CONST(Gravity_Forget);
+    ADD_INT_CONST(Gravity_NE);
+    ADD_INT_CONST(Gravity_N);
+    ADD_INT_CONST(Gravity_NW);
+    ADD_INT_CONST(Gravity_W);
+    ADD_INT_CONST(Gravity_SW);
+    ADD_INT_CONST(Gravity_S);
+    ADD_INT_CONST(Gravity_SE);
+    ADD_INT_CONST(Gravity_E);
+    ADD_INT_CONST(Gravity_Center);
+    ADD_INT_CONST(Gravity_Static);
+
+    /* WindowType */
+    ADD_INT_CONST(Type_Desktop);
+    ADD_INT_CONST(Type_Dock);
+    ADD_INT_CONST(Type_Toolbar);
+    ADD_INT_CONST(Type_Menu);
+    ADD_INT_CONST(Type_Utility);
+    ADD_INT_CONST(Type_Splash);
+    ADD_INT_CONST(Type_Dialog);
+    ADD_INT_CONST(Type_Normal);
+
+    /* Function */
+    ADD_INT_CONST(Func_Resize);
+    ADD_INT_CONST(Func_Move);
+    ADD_INT_CONST(Func_Iconify);
+    ADD_INT_CONST(Func_Maximize);
+    ADD_INT_CONST(Func_Shade);
+    ADD_INT_CONST(Func_Fullscreen);
+    ADD_INT_CONST(Func_Close);
+
+    /* Decoration */
+    ADD_INT_CONST(Decor_Titlebar);
+    ADD_INT_CONST(Decor_Handle);
+    ADD_INT_CONST(Decor_Border);
+    ADD_INT_CONST(Decor_Icon);
+    ADD_INT_CONST(Decor_Iconify);
+    ADD_INT_CONST(Decor_Maximize);
+    ADD_INT_CONST(Decor_AllDesktops);
+    ADD_INT_CONST(Decor_Close);
+
+    /* StackLayer */
+    ADD_INT_CONST(Layer_Icon);
+    ADD_INT_CONST(Layer_Desktop);
+    ADD_INT_CONST(Layer_Below);
+    ADD_INT_CONST(Layer_Normal);
+    ADD_INT_CONST(Layer_Above);
+    ADD_INT_CONST(Layer_Top);
+    ADD_INT_CONST(Layer_Fullscreen);
+    ADD_INT_CONST(Layer_Internal);
+
+    /* EventType */
+    ADD_INT_CONST(Logical_EnterWindow);
+    ADD_INT_CONST(Logical_LeaveWindow);
+    ADD_INT_CONST(Logical_NewWindow);
+    ADD_INT_CONST(Logical_CloseWindow);
+    ADD_INT_CONST(Logical_Startup);
+    ADD_INT_CONST(Logical_Shutdown);
+    ADD_INT_CONST(Logical_RequestActivate);
+    ADD_INT_CONST(Logical_Focus);
+    ADD_INT_CONST(Logical_Bell);
+    ADD_INT_CONST(Logical_UrgentWindow);
+    ADD_INT_CONST(Logical_WindowShow);
+    ADD_INT_CONST(Logical_WindowHide);
+    ADD_INT_CONST(Pointer_Press);
+    ADD_INT_CONST(Pointer_Release);
+    ADD_INT_CONST(Pointer_Motion);
+    ADD_INT_CONST(Key_Press);
+    ADD_INT_CONST(Key_Release);
+
+    Py_DECREF(ob);
+}
+
+void obexport_shutdown()
+{
+}
diff --git a/c/obexport.h b/c/obexport.h
new file mode 100644 (file)
index 0000000..17f68f4
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef __obexport_h
+#define __obexport_h
+
+#include <X11/Xlib.h>
+
+/* Define values which will be exported in the 'ob' module. */
+
+typedef enum {
+    /*! Occurs when the mouse enters a window */
+    Logical_EnterWindow,
+    /*! Occurs when the mouse enters a window */
+    Logical_LeaveWindow,
+    /*! Occurs when a window is finished being managed, just before it is
+      (possibly) displayed.
+      The python scripts are reponsible for showing the window when this is
+      called if they want it to be shown.
+    */
+    Logical_NewWindow,
+    /*! Occurs when a window is being unmanaged */
+    Logical_CloseWindow,
+    /*! Occurs when the window manager starts up */
+    Logical_Startup,
+    /*! Occurs when the window manager is shutting down */
+    Logical_Shutdown,
+    /*! Occurs when a client is requesting/requested to be activated (i.e.
+      focused, raised, unshaded) */
+    Logical_RequestActivate,
+    /*! Occurs when the input focus target changes
+      The data.client will be NULL of no client is focused. */
+    Logical_Focus,
+    /*! Occurs when the system is fired through X.
+      The data.client will hold the client associated with the bell if
+      one has been specified, or NULL. */
+    Logical_Bell,
+    /*! Occurs when a client toggles its urgent status.
+      The client.urgent member can be used to get the status. */
+    Logical_UrgentWindow,
+    /*! Occurs when a client becomes visible */
+    Logical_WindowShow,
+    /*! Occurs when a client becomes non-visible */
+    Logical_WindowHide,
+    /*! Occurs when a pointer button is pressed on a client or its
+      decorations.
+      Note: to get the event for the client's window or for the entire
+      window+decorations, you need to do an mgrab for the window. */
+    Pointer_Press,
+    /*! Occurs when a pointer button is released on a client or its
+      decorations.
+      Note: to get the event for the client's window or for the entire
+      window+decorations, you need to do an mgrab for the window. */
+    Pointer_Release,
+    /*! Occurs when a pointer button is held and the pointer is dragged on a
+      client or its decorations.
+      Note: to get the event for the client's window or for the entire
+      window+decorations, you need to do an mgrab for the window, or an
+      mgrab_pointer (in which case it may not be a drag). */
+    Pointer_Motion,
+    /*! Occurs when a key is pressed.
+      Note: in order to recieve a key event, a kgrab must be done for the
+      key combination, or a kgrab_keyboard.
+    */
+    Key_Press,
+    /*! Occurs when a key is released.
+      Note: in order to recieve a key event, a kgrab must be done for the
+      key combination, or a kgrab_keyboard.
+    */
+    Key_Release
+} EventType;
+
+/* create the 'ob' module */
+void obexport_startup();
+void obexport_shutdown();
+
+#endif
diff --git a/c/openbox.c b/c/openbox.c
new file mode 100644 (file)
index 0000000..77e4123
--- /dev/null
@@ -0,0 +1,203 @@
+#include "openbox.h"
+#include "event.h"
+#include "client.h"
+#include "xerror.h"
+#include "prop.h"
+#include "screen.h"
+#include "focus.h"
+#include "extensions.h"
+#include "python.h"
+#include "hooks.h"
+#include "eventdata.h"
+#include "gettext.h"
+#include "clientwrap.h"
+#include "screenwrap.h"
+#include "kbind.h"
+#include "mbind.h"
+#include "frame.h"
+
+#ifdef HAVE_FCNTL_H
+#  include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#  include <sys/select.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#  include <signal.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#  include <sys/types.h>
+#  include <sys/wait.h>
+#endif
+#ifdef HAVE_LOCALE_H
+#  include <locale.h>
+#endif
+
+#include <X11/cursorfont.h>
+
+Display *ob_display  = NULL;
+int      ob_screen;
+Window   ob_root;
+State    ob_state;
+gboolean ob_shutdown = FALSE;
+gboolean ob_restart  = FALSE;
+gboolean ob_remote   = FALSE;
+gboolean ob_sync     = TRUE;
+Cursors  ob_cursors;
+
+void signal_handler(int signal);
+
+int main(int argc, char **argv)
+{
+    struct sigaction action;
+    sigset_t sigset;
+
+    ob_state = State_Starting;
+
+    /* initialize the locale */
+    if (!setlocale(LC_ALL, ""))
+       g_warning("Couldn't set locale from environment.\n");
+    bindtextdomain(PACKAGE, LOCALEDIR);
+    bind_textdomain_codeset(PACKAGE, "UTF-8");
+    textdomain(PACKAGE);
+
+    /* set up signal handler */
+    sigemptyset(&sigset);
+    action.sa_handler = signal_handler;
+    action.sa_mask = sigset;
+    action.sa_flags = SA_NOCLDSTOP | SA_NODEFER;
+    sigaction(SIGUSR1, &action, (struct sigaction *) NULL);
+    sigaction(SIGPIPE, &action, (struct sigaction *) NULL);
+    sigaction(SIGSEGV, &action, (struct sigaction *) NULL);
+    sigaction(SIGFPE, &action, (struct sigaction *) NULL);
+    sigaction(SIGTERM, &action, (struct sigaction *) NULL);
+    sigaction(SIGINT, &action, (struct sigaction *) NULL);
+    sigaction(SIGHUP, &action, (struct sigaction *) NULL);
+    sigaction(SIGCHLD, &action, (struct sigaction *) NULL);
+
+    /* anything that died while we were restarting won't give us a SIGCHLD */
+    while (waitpid(-1, NULL, WNOHANG) > 0);
+     
+    /* XXX parse out command line args */
+    (void)argc;(void)argv;
+
+    /* critical warnings will exit the program */
+    g_log_set_always_fatal(G_LOG_LEVEL_CRITICAL);
+
+    ob_display = XOpenDisplay(NULL);
+    if (ob_display == NULL)
+       /* print a message and exit */
+       g_critical("Failed to open the display.");
+    if (fcntl(ConnectionNumber(ob_display), F_SETFD, 1) == -1)
+       /* print a message and exit */
+       g_critical("Failed to set display as close-on-exec.");
+         
+    ob_screen = DefaultScreen(ob_display);
+    ob_root = RootWindow(ob_display, ob_screen);
+
+    /* XXX fork self onto other screens */
+     
+    XSynchronize(ob_display, ob_sync);
+
+    /* check for locale support */
+    if (!XSupportsLocale())
+       g_warning("X server does not support locale.");
+    if (!XSetLocaleModifiers(""))
+       g_warning("Cannot set locale modifiers for the X server.");
+
+    /* set our error handler */
+    XSetErrorHandler(xerror_handler);
+
+    /* set the DISPLAY environment variable for any lauched children, to the
+       display we're using, so they open in the right place. */
+    putenv(g_strdup_printf("DISPLAY=%s", DisplayString(ob_display)));
+
+    ob_cursors.left_ptr = XCreateFontCursor(ob_display, XC_left_ptr);
+    ob_cursors.ll_angle = XCreateFontCursor(ob_display, XC_ll_angle);
+    ob_cursors.lr_angle = XCreateFontCursor(ob_display, XC_lr_angle);
+
+    prop_init(); /* get atoms values for the display */
+    extensions_query_all(); /* find which extensions are present */
+     
+    if (screen_annex()) { /* it will be ours! */
+        frame_startup();
+       python_startup();
+       obexport_startup();
+       hooks_startup();
+       screenwrap_startup();
+       clientwrap_startup();
+       eventdata_startup();
+       event_startup();
+       screen_startup();
+       focus_startup();
+       client_startup();
+       kbind_startup();
+       mbind_startup();
+         
+       /* load the user's settings */
+       if (!python_import("rc"))
+           g_warning("ERROR LOADING RC FILE");
+
+       LOGICALHOOK(Startup, g_quark_try_string("none"), NULL);
+
+       /* get all the existing windows */
+       client_manage_all();
+
+       ob_state = State_Running;
+       while (!ob_shutdown) {
+           event_loop();
+       }
+       ob_state = State_Exiting;
+
+       client_unmanage_all();
+
+       LOGICALHOOK(Shutdown, g_quark_try_string("none"), NULL);
+
+       mbind_shutdown();
+       kbind_shutdown();
+       client_shutdown();
+       screen_shutdown();
+       event_shutdown();
+       eventdata_shutdown();
+       clientwrap_shutdown();
+       screenwrap_shutdown();
+       hooks_shutdown();
+       obexport_shutdown();
+       python_shutdown();
+    }
+         
+    XCloseDisplay(ob_display);
+
+    /* XXX if (ob_restart) */
+     
+    return 0;
+}
+
+void signal_handler(int signal)
+{
+    switch (signal) {
+    case SIGUSR1:
+       g_message("Caught SIGUSR1 signal. Restarting.");
+       ob_shutdown = ob_restart = TRUE;
+       break;
+
+    case SIGCHLD:
+       wait(NULL);
+       break;
+
+    case SIGHUP:
+    case SIGINT:
+    case SIGTERM:
+    case SIGPIPE:
+       g_message("Caught signal %d. Exiting.", signal);
+       ob_shutdown = TRUE;
+       break;
+
+    case SIGFPE:
+    case SIGSEGV:
+       g_error("Caught signal %d. Aborting and dumping core.", signal);
+    }
+}
diff --git a/c/openbox.h b/c/openbox.h
new file mode 100644 (file)
index 0000000..34160c8
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef __openbox_h
+#define __openbox_h
+
+#include "obexport.h"
+#include <glib.h>
+#include <X11/Xlib.h>
+
+/*! The X display */
+extern Display *ob_display; 
+/*! The number of the screen on which we're running */
+extern int      ob_screen;
+/*! The root window */
+extern Window   ob_root;
+
+/* The state of execution of the window manager */
+State ob_state;
+
+/*! When set to true, Openbox will exit */
+extern gboolean ob_shutdown;
+/*! When set to true, Openbox will restart instead of shutting down */
+extern gboolean ob_restart;
+
+/*! Runtime option to specify running on a remote display */
+extern gboolean ob_remote;
+/*! Runtime option to run in synchronous mode */
+extern gboolean ob_sync;
+
+typedef struct Cursors {
+    Cursor left_ptr;
+    Cursor ll_angle;
+    Cursor lr_angle;
+} Cursors;
+Cursors ob_cursors;
+
+#endif
diff --git a/c/prop.c b/c/prop.c
new file mode 100644 (file)
index 0000000..d239276
--- /dev/null
+++ b/c/prop.c
@@ -0,0 +1,271 @@
+#include "prop.h"
+#include "openbox.h"
+#include <X11/Xatom.h>
+
+Atoms prop_atoms;
+
+#define CREATE(var, name) (prop_atoms.var = \
+                           XInternAtom(ob_display, name, FALSE))
+
+void prop_init()
+{
+    g_assert(ob_display != NULL);
+
+    CREATE(cardinal, "CARDINAL");
+    CREATE(window, "WINDOW");
+    CREATE(pixmap, "PIXMAP");
+    CREATE(atom, "ATOM");
+    CREATE(string, "STRING");
+    CREATE(utf8, "UTF8_STRING");
+     
+    CREATE(wm_colormap_windows, "WM_COLORMAP_WINDOWS");
+    CREATE(wm_protocols, "WM_PROTOCOLS");
+    CREATE(wm_state, "WM_STATE");
+    CREATE(wm_change_state, "WM_CHANGE_STATE");
+    CREATE(wm_delete_window, "WM_DELETE_WINDOW");
+    CREATE(wm_take_focus, "WM_TAKE_FOCUS");
+    CREATE(wm_name, "WM_NAME");
+    CREATE(wm_icon_name, "WM_ICON_NAME");
+    CREATE(wm_class, "WM_CLASS");
+    CREATE(wm_window_role, "WM_WINDOW_ROLE");
+    CREATE(motif_wm_hints, "_MOTIF_WM_HINTS");
+
+    CREATE(net_supported, "_NET_SUPPORTED");
+    CREATE(net_client_list, "_NET_CLIENT_LIST");
+    CREATE(net_client_list_stacking, "_NET_CLIENT_LIST_STACKING");
+    CREATE(net_number_of_desktops, "_NET_NUMBER_OF_DESKTOPS");
+    CREATE(net_desktop_geometry, "_NET_DESKTOP_GEOMETRY");
+    CREATE(net_desktop_viewport, "_NET_DESKTOP_VIEWPORT");
+    CREATE(net_current_desktop, "_NET_CURRENT_DESKTOP");
+    CREATE(net_desktop_names, "_NET_DESKTOP_NAMES");
+    CREATE(net_active_window, "_NET_ACTIVE_WINDOW");
+    CREATE(net_workarea, "_NET_WORKAREA");
+    CREATE(net_supporting_wm_check, "_NET_SUPPORTING_WM_CHECK");
+/*   CREATE(net_virtual_roots, "_NET_VIRTUAL_ROOTS"); */
+    CREATE(net_desktop_layout, "_NET_DESKTOP_LAYOUT");
+    CREATE(net_showing_desktop, "_NET_SHOWING_DESKTOP");
+
+    CREATE(net_close_window, "_NET_CLOSE_WINDOW");
+    CREATE(net_wm_moveresize, "_NET_WM_MOVERESIZE");
+
+/*   CREATE(net_properties, "_NET_PROPERTIES"); */
+    CREATE(net_wm_name, "_NET_WM_NAME");
+    CREATE(net_wm_visible_name, "_NET_WM_VISIBLE_NAME");
+    CREATE(net_wm_icon_name, "_NET_WM_ICON_NAME");
+    CREATE(net_wm_visible_icon_name, "_NET_WM_VISIBLE_ICON_NAME");
+    CREATE(net_wm_desktop, "_NET_WM_DESKTOP");
+    CREATE(net_wm_window_type, "_NET_WM_WINDOW_TYPE");
+    CREATE(net_wm_state, "_NET_WM_STATE");
+    CREATE(net_wm_strut, "_NET_WM_STRUT");
+/*   CREATE(net_wm_icon_geometry, "_NET_WM_ICON_GEOMETRY"); */
+    CREATE(net_wm_icon, "_NET_WM_ICON");
+/*   CREATE(net_wm_pid, "_NET_WM_PID"); */
+/*   CREATE(net_wm_handled_icons, "_NET_WM_HANDLED_ICONS"); */
+    CREATE(net_wm_allowed_actions, "_NET_WM_ALLOWED_ACTIONS");
+
+/*   CREATE(net_wm_ping, "_NET_WM_PING"); */
+  
+    CREATE(net_wm_window_type_desktop, "_NET_WM_WINDOW_TYPE_DESKTOP");
+    CREATE(net_wm_window_type_dock, "_NET_WM_WINDOW_TYPE_DOCK");
+    CREATE(net_wm_window_type_toolbar, "_NET_WM_WINDOW_TYPE_TOOLBAR");
+    CREATE(net_wm_window_type_menu, "_NET_WM_WINDOW_TYPE_MENU");
+    CREATE(net_wm_window_type_utility, "_NET_WM_WINDOW_TYPE_UTILITY");
+    CREATE(net_wm_window_type_splash, "_NET_WM_WINDOW_TYPE_SPLASH");
+    CREATE(net_wm_window_type_dialog, "_NET_WM_WINDOW_TYPE_DIALOG");
+    CREATE(net_wm_window_type_normal, "_NET_WM_WINDOW_TYPE_NORMAL");
+
+    CREATE(net_wm_moveresize_size_topleft, "_NET_WM_MOVERESIZE_SIZE_TOPLEFT");
+    CREATE(net_wm_moveresize_size_topright,
+          "_NET_WM_MOVERESIZE_SIZE_TOPRIGHT");
+    CREATE(net_wm_moveresize_size_bottomleft,
+          "_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT");
+    CREATE(net_wm_moveresize_size_bottomright,
+          "_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT");
+    CREATE(net_wm_moveresize_move, "_NET_WM_MOVERESIZE_MOVE");
+    CREATE(net_wm_action_move, "_NET_WM_ACTION_MOVE");
+    CREATE(net_wm_action_resize, "_NET_WM_ACTION_RESIZE");
+    CREATE(net_wm_action_minimize, "_NET_WM_ACTION_MINIMIZE");
+    CREATE(net_wm_action_shade, "_NET_WM_ACTION_SHADE");
+    CREATE(net_wm_action_stick, "_NET_WM_ACTION_STICK");
+    CREATE(net_wm_action_maximize_horz, "_NET_WM_ACTION_MAXIMIZE_HORZ");
+    CREATE(net_wm_action_maximize_vert, "_NET_WM_ACTION_MAXIMIZE_VERT");
+    CREATE(net_wm_action_fullscreen, "_NET_WM_ACTION_FULLSCREEN");
+    CREATE(net_wm_action_change_desktop, "_NET_WM_ACTION_CHANGE_DESKTOP");
+    CREATE(net_wm_action_close, "_NET_WM_ACTION_CLOSE");
+    CREATE(net_wm_state_modal, "_NET_WM_STATE_MODAL");
+    CREATE(net_wm_state_sticky, "_NET_WM_STATE_STICKY");
+    CREATE(net_wm_state_maximized_vert, "_NET_WM_STATE_MAXIMIZED_VERT");
+    CREATE(net_wm_state_maximized_horz, "_NET_WM_STATE_MAXIMIZED_HORZ");
+    CREATE(net_wm_state_shaded, "_NET_WM_STATE_SHADED");
+    CREATE(net_wm_state_skip_taskbar, "_NET_WM_STATE_SKIP_TASKBAR");
+    CREATE(net_wm_state_skip_pager, "_NET_WM_STATE_SKIP_PAGER");
+    CREATE(net_wm_state_hidden, "_NET_WM_STATE_HIDDEN");
+    CREATE(net_wm_state_fullscreen, "_NET_WM_STATE_FULLSCREEN");
+    CREATE(net_wm_state_above, "_NET_WM_STATE_ABOVE");
+    CREATE(net_wm_state_below, "_NET_WM_STATE_BELOW");
+  
+    prop_atoms.net_wm_state_add = 1;
+    prop_atoms.net_wm_state_remove = 0;
+    prop_atoms.net_wm_state_toggle = 2;
+
+    prop_atoms.net_wm_orientation_horz = 0;
+    prop_atoms.net_wm_orientation_vert = 1;
+    prop_atoms.net_wm_topleft = 0;
+    prop_atoms.net_wm_topright = 1;
+    prop_atoms.net_wm_bottomright = 2;
+    prop_atoms.net_wm_bottomleft = 3;
+
+    CREATE(kde_net_system_tray_windows, "_KDE_NET_SYSTEM_TRAY_WINDOWS");
+    CREATE(kde_net_wm_system_tray_window_for,
+          "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR");
+    CREATE(kde_net_wm_window_type_override,
+          "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE");
+
+    CREATE(kwm_win_icon, "KWM_WIN_ICON");
+  
+    CREATE(rootpmapid, "_XROOTPMAP_ID");
+    CREATE(esetrootid, "ESETROOT_PMAP_ID");
+
+    CREATE(openbox_pid, "_OPENBOX_PID");
+    CREATE(openbox_premax, "_OPENBOX_PREMAX");
+}
+
+gboolean prop_get(Window win, Atom prop, Atom type, int size,
+                 guchar **data, gulong num)
+{
+    gboolean ret = FALSE;
+    int res;
+    guchar *xdata = NULL;
+    Atom ret_type;
+    int ret_size;
+    gulong ret_items, bytes_left;
+    long num32 = 32 / size * num; /* num in 32-bit elements */
+
+    res = XGetWindowProperty(ob_display, win, prop, 0l, num32,
+                            FALSE, type, &ret_type, &ret_size,
+                            &ret_items, &bytes_left, &xdata);
+    if (res == Success && ret_items && xdata) {
+       if (ret_size == size && ret_items >= num) {
+           *data = g_memdup(xdata, num * (size / 8));
+           ret = TRUE;
+       }
+       XFree(xdata);
+    }
+    return ret;
+}
+
+gboolean prop_get_prealloc(Window win, Atom prop, Atom type, int size,
+                          guchar *data, gulong num)
+{
+    gboolean ret = FALSE;
+    int res;
+    guchar *xdata = NULL;
+    Atom ret_type;
+    int ret_size;
+    gulong ret_items, bytes_left;
+    long num32 = 32 / size * num; /* num in 32-bit elements */
+
+    res = XGetWindowProperty(ob_display, win, prop, 0l, num32,
+                            FALSE, type, &ret_type, &ret_size,
+                            &ret_items, &bytes_left, &xdata);
+    if (res == Success && ret_items && xdata) {
+       if (ret_size == size && ret_items >= num) {
+           gulong i;
+           for (i = 0; i < num; ++i)
+               switch (size) {
+               case 8:
+                   data[i] = xdata[i];
+                   break;
+               case 16:
+                   ((guint16*)data)[i] = ((guint16*)xdata)[i];
+                   break;
+               case 32:
+                   ((guint32*)data)[i] = ((guint32*)xdata)[i];
+                   break;
+               default:
+                   g_assert_not_reached(); /* unhandled size */
+               }
+           ret = TRUE;
+       }
+       XFree(xdata);
+    }
+    return ret;
+}
+
+gboolean prop_get_all(Window win, Atom prop, Atom type, int size,
+                     guchar **data, gulong *num)
+{
+    gboolean ret = FALSE;
+    int res;
+    guchar *xdata = NULL;
+    Atom ret_type;
+    int ret_size;
+    gulong ret_items, bytes_left;
+
+    res = XGetWindowProperty(ob_display, win, prop, 0l, G_MAXLONG,
+                            FALSE, type, &ret_type, &ret_size,
+                            &ret_items, &bytes_left, &xdata);
+    if (res == Success) {
+       if (ret_size == size && ret_items > 0) {
+           *data = g_memdup(xdata, ret_items * (size / 8));
+           *num = ret_items;
+           ret = TRUE;
+       }
+       XFree(xdata);
+    }
+    return ret;
+}
+
+gboolean prop_get_string(Window win, Atom prop, Atom type, guchar **data)
+{
+    guchar *raw;
+    gulong num;
+    GString *str;
+     
+    if (prop_get_all(win, prop, type, 8, &raw, &num)) {
+       str = g_string_new_len((char*)raw, num);
+       g_assert(str->str[num] == '\0');
+
+       g_free(raw);
+
+       *data = (guchar*)g_string_free(str, FALSE);
+       return TRUE;
+    }
+    return FALSE;
+}
+
+gboolean prop_get_strings(Window win, Atom prop, Atom type,
+                         GPtrArray *data)
+{
+    guchar *raw;
+    gulong num;
+    GString *str, *str2;
+    guint i, start;
+     
+    if (prop_get_all(win, prop, type, 8, &raw, &num)) {
+       str = g_string_new_len((gchar*)raw, num);
+       g_assert(str->str[num] == '\0'); /* assuming this is always true.. */
+
+       g_free(raw);
+
+       /* split it into the list */
+       for (start = 0, i = 0; i < str->len; ++i) {
+           if (str->str[i] == '\0') {
+               str2 = g_string_new_len(&str->str[start], i - start);
+               g_ptr_array_add(data, g_string_free(str2, FALSE));
+               start = i + 1;
+           }
+       }
+       g_string_free(str, TRUE);
+
+       if (data->len > 0)
+           return TRUE;
+    }
+    return FALSE;
+}
+
+void prop_erase(Window win, Atom prop)
+{
+    XDeleteProperty(ob_display, win, prop);
+}
diff --git a/c/prop.h b/c/prop.h
new file mode 100644 (file)
index 0000000..2c08130
--- /dev/null
+++ b/c/prop.h
@@ -0,0 +1,202 @@
+#ifndef __atoms_h
+#define __atoms_h
+
+#include <X11/Xlib.h>
+#include <glib.h>
+#ifdef HAVE_STRING_H
+#  include <string.h>
+#endif
+
+#include "openbox.h"
+
+/*! The atoms on the X server which this class will cache */
+typedef struct Atoms {
+    /* types */
+    Atom cardinal; /*!< The atom which represents the Cardinal data type */
+    Atom window;   /*!< The atom which represents window ids */
+    Atom pixmap;   /*!< The atom which represents pixmap ids */
+    Atom atom;     /*!< The atom which represents atom values */
+    Atom string;   /*!< The atom which represents ascii strings */
+    Atom utf8;     /*!< The atom which represents utf8-encoded strings */
+
+    /* window hints */
+    Atom wm_colormap_windows;
+    Atom wm_protocols;
+    Atom wm_state;
+    Atom wm_delete_window;
+    Atom wm_take_focus;
+    Atom wm_change_state;
+    Atom wm_name;
+    Atom wm_icon_name;
+    Atom wm_class;
+    Atom wm_window_role;
+    Atom motif_wm_hints;
+
+    /* NETWM atoms */
+     
+    /* root window properties */
+    Atom net_supported;
+    Atom net_client_list;
+    Atom net_client_list_stacking;
+    Atom net_number_of_desktops;
+    Atom net_desktop_geometry;
+    Atom net_desktop_viewport;
+    Atom net_current_desktop;
+    Atom net_desktop_names;
+    Atom net_active_window;
+    Atom net_workarea;
+    Atom net_supporting_wm_check;
+/*  Atom net_virtual_roots; */
+    Atom net_desktop_layout;
+    Atom net_showing_desktop;
+    /* root window messages */
+    Atom net_close_window;
+    Atom net_wm_moveresize;
+    /* application window properties */
+/*  Atom net_properties; */
+    Atom net_wm_name;
+    Atom net_wm_visible_name;
+    Atom net_wm_icon_name;
+    Atom net_wm_visible_icon_name;
+    Atom net_wm_desktop;
+    Atom net_wm_window_type;
+    Atom net_wm_state;
+    Atom net_wm_strut;
+/*  Atom net_wm_icon_geometry; */
+    Atom net_wm_icon;
+/*  Atom net_wm_pid; */
+/*  Atom net_wm_handled_icons; */
+    Atom net_wm_allowed_actions;
+    /* application protocols */
+/*  Atom   Atom net_wm_ping; */
+
+    Atom net_wm_window_type_desktop;
+    Atom net_wm_window_type_dock;
+    Atom net_wm_window_type_toolbar;
+    Atom net_wm_window_type_menu;
+    Atom net_wm_window_type_utility;
+    Atom net_wm_window_type_splash;
+    Atom net_wm_window_type_dialog;
+    Atom net_wm_window_type_normal;
+
+    Atom net_wm_moveresize_size_topleft;
+    Atom net_wm_moveresize_size_topright;
+    Atom net_wm_moveresize_size_bottomleft;
+    Atom net_wm_moveresize_size_bottomright;
+    Atom net_wm_moveresize_move;
+
+    Atom net_wm_action_move;
+    Atom net_wm_action_resize;
+    Atom net_wm_action_minimize;
+    Atom net_wm_action_shade;
+    Atom net_wm_action_stick;
+    Atom net_wm_action_maximize_horz;
+    Atom net_wm_action_maximize_vert;
+    Atom net_wm_action_fullscreen;
+    Atom net_wm_action_change_desktop;
+    Atom net_wm_action_close;
+
+    Atom net_wm_state_modal;
+    Atom net_wm_state_sticky;
+    Atom net_wm_state_maximized_vert;
+    Atom net_wm_state_maximized_horz;
+    Atom net_wm_state_shaded;
+    Atom net_wm_state_skip_taskbar;
+    Atom net_wm_state_skip_pager;
+    Atom net_wm_state_hidden;
+    Atom net_wm_state_fullscreen;
+    Atom net_wm_state_above;
+    Atom net_wm_state_below;
+
+    Atom net_wm_state_add;
+    Atom net_wm_state_remove;
+    Atom net_wm_state_toggle;
+
+    Atom net_wm_orientation_horz;
+    Atom net_wm_orientation_vert;
+    Atom net_wm_topleft;
+    Atom net_wm_topright;
+    Atom net_wm_bottomright;
+    Atom net_wm_bottomleft;
+
+    /* Extra atoms */
+
+    Atom kde_net_system_tray_windows;
+    Atom kde_net_wm_system_tray_window_for;
+    Atom kde_net_wm_window_type_override;
+
+    Atom kwm_win_icon;
+
+    Atom rootpmapid;
+    Atom esetrootid;
+
+    /* Openbox specific atoms */
+     
+    Atom openbox_pid;
+    Atom openbox_premax;
+} Atoms;
+Atoms prop_atoms;
+
+void prop_init();
+
+gboolean prop_get(Window win, Atom prop, Atom type, int size,
+                       guchar **data, gulong num);
+
+gboolean prop_get_prealloc(Window win, Atom prop, Atom type, int size,
+                          guchar *data, gulong num);
+
+gboolean prop_get_all(Window win, Atom prop, Atom type, int size,
+                     guchar **data, gulong *num);
+
+gboolean prop_get_string(Window win, Atom prop, Atom type, guchar **data);
+gboolean prop_get_strings(Window win, Atom prop, Atom type,
+                         GPtrArray *data);
+
+void prop_erase(Window win, Atom prop);
+
+/* Set an 8-bit property from a string */
+#define PROP_SETS(win, prop, type, value) \
+  (XChangeProperty(ob_display, win, prop_atoms.prop, prop_atoms.type, 8, \
+                  PropModeReplace, (guchar*)value, strlen(value)))
+/* Set a 32-bit property from a single value */
+#define PROP_SET32(win, prop, type, value) \
+  (XChangeProperty(ob_display, win, prop_atoms.prop, prop_atoms.type, 32, \
+                  PropModeReplace, (guchar*)&value, 1))
+/* Set a 32-bit property from an array */
+#define PROP_SET32A(win, prop, type, value, num) \
+  (XChangeProperty(ob_display, win, prop_atoms.prop, prop_atoms.type, 32, \
+                  PropModeReplace, (guchar*)value, num))
+
+/* Get an 8-bit property into a string */
+#define PROP_GETS(win, prop, type, value) \
+  (prop_get_string(win, prop_atoms.prop, prop_atoms.type, \
+                          (guchar**)&value))
+/* Get an 8-bit property into a GPtrArray of strings
+   (The strings must be freed, the GPtrArray must already be created.) */
+#define PROP_GETSA(win, prop, type, value) \
+  (prop_get_strings(win, prop_atoms.prop, prop_atoms.type, \
+                           value))
+
+/* Get an entire 8-bit property into an array (which must be freed) */
+#define PROP_GET8U(win, prop, type, value, num) \
+  (prop_get_all(win, prop_atoms.prop, prop_atoms.type, 8, \
+                (guchar**)&value, &num))
+
+/* Get 1 element of a 32-bit property into a given variable */
+#define PROP_GET32(win, prop, type, value) \
+  (prop_get_prealloc(win, prop_atoms.prop, prop_atoms.type, 32, \
+                    (guchar*)&value, 1))
+
+/* Get an amount of a 32-bit property into an array (which must be freed) */
+#define PROP_GET32A(win, prop, type, value, num) \
+  (prop_get(win, prop_atoms.prop, prop_atoms.type, 32, \
+           (guchar**)&value, num))
+
+/* Get an entire 32-bit property into an array (which must be freed) */
+#define PROP_GET32U(win, prop, type, value, num) \
+  (prop_get_all(win, prop_atoms.prop, prop_atoms.type, 32, \
+               (guchar**)&value, &num))
+
+#define PROP_ERASE(win, prop) (prop_erase(win, prop_atoms.prop))
+
+#endif
diff --git a/c/python.c b/c/python.c
new file mode 100644 (file)
index 0000000..6622ed8
--- /dev/null
@@ -0,0 +1,55 @@
+#include <Python.h>
+#include <glib.h>
+
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif
+
+void python_startup()
+{
+    PyObject *sys, *sysdict, *syspath, *path1, *path2;
+    char *home, *homescriptdir;
+
+    Py_Initialize();
+
+    /* fix up the system path */
+
+    sys = PyImport_ImportModule((char*)"sys"); /* new */
+    sysdict = PyModule_GetDict(sys); /* borrowed */
+    syspath = PyDict_GetItemString(sysdict, (char*)"path"); /* borrowed */
+
+    path1 = PyString_FromString(SCRIPTDIR); /* new */
+    PyList_Insert(syspath, 0, path1);
+    Py_DECREF(path1);
+
+    home = getenv("HOME");
+    if (home != NULL) {
+       homescriptdir = g_strdup_printf("%s/.openbox", home);
+       path2 = PyString_FromString(homescriptdir); /* new */
+       g_free(homescriptdir);
+
+       PyList_Insert(syspath, 0, path2);
+       Py_DECREF(path2);
+    } else
+       g_warning("Failed to read the $HOME environment variable");
+
+    Py_DECREF(sys);
+}
+
+void python_shutdown()
+{
+    Py_Finalize();
+}
+
+gboolean python_import(char *module)
+{
+    PyObject *mod;
+
+    mod = PyImport_ImportModule(module); /* new */
+    if (mod == NULL) {
+       PyErr_Print();
+       return FALSE;
+    }
+    Py_DECREF(mod);
+    return TRUE;
+}
diff --git a/c/python.h b/c/python.h
new file mode 100644 (file)
index 0000000..9036631
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __python_h
+#define __python_h
+
+void python_startup();
+void python_shutdown();
+
+/*! Import a python module */
+gboolean python_import(char *module);
+
+#endif
diff --git a/c/screen.c b/c/screen.c
new file mode 100644 (file)
index 0000000..ab6765f
--- /dev/null
@@ -0,0 +1,516 @@
+#include "openbox.h"
+#include "prop.h"
+#include "screen.h"
+#include "client.h"
+#include "focus.h"
+
+#include <X11/Xlib.h>
+#ifdef HAVE_UNISTD_H
+#  include <sys/types.h>
+#  include <unistd.h>
+#endif
+
+/*! The event mask to grab on the root window */
+#define ROOT_EVENTMASK (/*ColormapChangeMask |*/ PropertyChangeMask | \
+                       EnterWindowMask | LeaveWindowMask | \
+                       SubstructureNotifyMask | SubstructureRedirectMask | \
+                       ButtonPressMask | ButtonReleaseMask | ButtonMotionMask)
+
+guint    screen_num_desktops    = 1;
+guint    screen_desktop         = 0;
+Size     screen_physical_size;
+gboolean screen_showing_desktop;
+DesktopLayout screen_desktop_layout;
+GPtrArray *screen_desktop_names;
+
+static Rect  *area = NULL;
+static Strut *strut = NULL;
+
+static void screen_update_area();
+
+static gboolean running;
+static int another_running(Display *d, XErrorEvent *e)
+{
+    (void)d;(void)e;
+    g_message("A window manager is already running on screen %d",
+             ob_screen);
+    running = TRUE;
+    return -1;
+}
+
+gboolean screen_annex()
+{
+    XErrorHandler old;
+    Window support;
+    pid_t pid;
+    int i, num_support;
+    Atom *supported;
+
+    running = FALSE;
+    old = XSetErrorHandler(another_running);
+    XSelectInput(ob_display, ob_root, ROOT_EVENTMASK);
+    XSync(ob_display, FALSE);
+    XSetErrorHandler(old);
+    if (running)
+       return FALSE;
+
+    g_message("Managing screen %d", ob_screen);
+
+    /* set the mouse cursor for the root window (the default cursor) */
+    XDefineCursor(ob_display, ob_root, ob_cursors.left_ptr);
+
+    /* set the OPENBOX_PID hint */
+    pid = getpid();
+    PROP_SET32(ob_root, openbox_pid, cardinal, pid);
+
+    /* create the netwm support window */
+    support = XCreateSimpleWindow(ob_display, ob_root, 0, 0, 1, 1, 0, 0, 0);
+
+    /* set supporting window */
+    PROP_SET32(ob_root, net_supporting_wm_check, window, support);
+
+    /* set properties on the supporting window */
+    PROP_SETS(support, net_wm_name, utf8, "Openbox");
+    PROP_SET32(support, net_supporting_wm_check, window, support);
+
+    /* set the _NET_SUPPORTED_ATOMS hint */
+    num_support = 48;
+    i = 0;
+    supported = g_new(Atom, num_support);
+    supported[i++] = prop_atoms.net_current_desktop;
+    supported[i++] = prop_atoms.net_number_of_desktops;
+    supported[i++] = prop_atoms.net_desktop_geometry;
+    supported[i++] = prop_atoms.net_desktop_viewport;
+    supported[i++] = prop_atoms.net_active_window;
+    supported[i++] = prop_atoms.net_workarea;
+    supported[i++] = prop_atoms.net_client_list;
+    supported[i++] = prop_atoms.net_client_list_stacking;
+    supported[i++] = prop_atoms.net_desktop_names;
+    supported[i++] = prop_atoms.net_close_window;
+    supported[i++] = prop_atoms.net_desktop_layout;
+    supported[i++] = prop_atoms.net_showing_desktop;
+    supported[i++] = prop_atoms.net_wm_name;
+    supported[i++] = prop_atoms.net_wm_visible_name;
+    supported[i++] = prop_atoms.net_wm_icon_name;
+    supported[i++] = prop_atoms.net_wm_visible_icon_name;
+    supported[i++] = prop_atoms.net_wm_desktop;
+    supported[i++] = prop_atoms.net_wm_strut;
+    supported[i++] = prop_atoms.net_wm_window_type;
+    supported[i++] = prop_atoms.net_wm_window_type_desktop;
+    supported[i++] = prop_atoms.net_wm_window_type_dock;
+    supported[i++] = prop_atoms.net_wm_window_type_toolbar;
+    supported[i++] = prop_atoms.net_wm_window_type_menu;
+    supported[i++] = prop_atoms.net_wm_window_type_utility;
+    supported[i++] = prop_atoms.net_wm_window_type_splash;
+    supported[i++] = prop_atoms.net_wm_window_type_dialog;
+    supported[i++] = prop_atoms.net_wm_window_type_normal;
+    supported[i++] = prop_atoms.net_wm_allowed_actions;
+    supported[i++] = prop_atoms.net_wm_action_move;
+    supported[i++] = prop_atoms.net_wm_action_resize;
+    supported[i++] = prop_atoms.net_wm_action_minimize;
+    supported[i++] = prop_atoms.net_wm_action_shade;
+    supported[i++] = prop_atoms.net_wm_action_maximize_horz;
+    supported[i++] = prop_atoms.net_wm_action_maximize_vert;
+    supported[i++] = prop_atoms.net_wm_action_fullscreen;
+    supported[i++] = prop_atoms.net_wm_action_change_desktop;
+    supported[i++] = prop_atoms.net_wm_action_close;
+    supported[i++] = prop_atoms.net_wm_state;
+    supported[i++] = prop_atoms.net_wm_state_modal;
+    supported[i++] = prop_atoms.net_wm_state_maximized_vert;
+    supported[i++] = prop_atoms.net_wm_state_maximized_horz;
+    supported[i++] = prop_atoms.net_wm_state_shaded;
+    supported[i++] = prop_atoms.net_wm_state_skip_taskbar;
+    supported[i++] = prop_atoms.net_wm_state_skip_pager;
+    supported[i++] = prop_atoms.net_wm_state_hidden;
+    supported[i++] = prop_atoms.net_wm_state_fullscreen;
+    supported[i++] = prop_atoms.net_wm_state_above;
+    supported[i++] = prop_atoms.net_wm_state_below;
+    g_assert(i == num_support);
+/*
+  supported[] = prop_atoms.net_wm_moveresize;
+  supported[] = prop_atoms.net_wm_moveresize_size_topleft;
+  supported[] = prop_atoms.net_wm_moveresize_size_topright;
+  supported[] = prop_atoms.net_wm_moveresize_size_bottomleft;
+  supported[] = prop_atoms.net_wm_moveresize_size_bottomright;
+  supported[] = prop_atoms.net_wm_moveresize_move;
+  supported[] = prop_atoms.net_wm_action_stick;
+*/
+
+    PROP_SET32A(ob_root, net_supported, atom, supported, num_support);
+    g_free(supported);
+
+    return TRUE;
+}
+
+void screen_startup()
+{
+    screen_desktop_names = g_ptr_array_new();
+     
+    /* get the initial size */
+    screen_resize();
+
+    screen_set_num_desktops(4);
+    screen_set_desktop(0);
+
+    /* don't start in showing-desktop mode */
+    screen_showing_desktop = FALSE;
+    PROP_SET32(ob_root, net_showing_desktop, cardinal, screen_showing_desktop);
+
+    screen_update_layout();
+}
+
+void screen_shutdown()
+{
+    guint i;
+    for (i = 0; i < screen_desktop_names->len; ++i)
+       g_free(g_ptr_array_index(screen_desktop_names, i));
+    g_ptr_array_free(screen_desktop_names, TRUE);
+    g_free(strut);
+    g_free(area);
+}
+
+void screen_resize()
+{
+    /* Set the _NET_DESKTOP_GEOMETRY hint */
+    /* XXX RandR support here? */
+    int geometry[2];
+
+    geometry[0] = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
+    geometry[1] = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen));
+    PROP_SET32A(ob_root, net_desktop_geometry, cardinal, geometry, 2);
+    screen_physical_size.width = geometry[0];
+    screen_physical_size.height = geometry[1];
+
+    if (ob_state == State_Starting)
+       return;
+
+    screen_update_struts();
+
+    /* XXX adjust more stuff ? */
+}
+
+void screen_set_num_desktops(guint num)
+{
+    unsigned long *viewport;
+     
+    g_assert(num > 0);
+  
+    /* move windows on desktops that will no longer exist! */
+    /* XXX
+       std::list<Client*>::iterator it, end = clients.end();
+       for (it = clients.begin(); it != end; ++it) {
+       unsigned int d = (*it)->desktop();
+       if (d >= num && d != 0xffffffff) {
+       XEvent ce;
+       ce.xclient.type = ClientMessage;
+       ce.xclient.message_type = otk::Property::atoms.net_wm_desktop;
+       ce.xclient.display = **otk::display;
+       ce.xclient.window = (*it)->window();
+       ce.xclient.format = 32;
+       ce.xclient.data.l[0] = num - 1;
+       XSendEvent(**otk::display, _info->rootWindow(), false,
+       SubstructureNotifyMask | SubstructureRedirectMask, &ce);
+       }
+       }
+    */
+
+    screen_num_desktops = num;
+    PROP_SET32(ob_root, net_number_of_desktops, cardinal, num);
+
+    /* set the viewport hint */
+    viewport = g_new0(unsigned long, num * 2);
+    PROP_SET32A(ob_root, net_desktop_viewport, cardinal, viewport, num * 2);
+    g_free(viewport);
+
+    /* change our struts/area to match */
+    screen_update_struts();
+
+    /* the number of rows/columns will differ */
+    screen_update_layout();
+
+    /* may be some unnamed desktops that we need to fill in with names */
+    screen_update_desktop_names();
+
+    /* change our desktop if we're on one that no longer exists! */
+    if (screen_desktop >= screen_num_desktops)
+       screen_set_desktop(num - 1);
+}
+
+void screen_set_desktop(guint num)
+{
+    guint old = screen_desktop;
+     
+    g_assert(num < screen_num_desktops);
+
+    g_message("Moving to desktop %u", num);
+  
+    screen_desktop = num;
+    PROP_SET32(ob_root, net_current_desktop, cardinal, num);
+
+    if (old == num) return;
+
+    /* XXX show/hide all the clients
+       std::list<Client*>::iterator it, end = clients.end();
+       for (it = clients.begin(); it != end; ++it)
+       (*it)->showhide(); */
+
+    /* XXX force the callbacks to fire
+       if (!openbox->focusedClient())
+       openbox->setFocusedClient(0); */
+}
+
+void screen_update_layout()
+{
+    unsigned long *data = NULL;
+
+    /* defaults */
+    screen_desktop_layout.orientation = prop_atoms.net_wm_orientation_horz;
+    screen_desktop_layout.start_corner = prop_atoms.net_wm_topleft;
+    screen_desktop_layout.rows = 1;
+    screen_desktop_layout.columns = screen_num_desktops;
+
+    if (PROP_GET32A(ob_root, net_desktop_layout, cardinal, data, 4)) {
+       if (data[0] == prop_atoms.net_wm_orientation_vert)
+           screen_desktop_layout.orientation = data[0];
+       if (data[3] == prop_atoms.net_wm_topright)
+           screen_desktop_layout.start_corner = data[3];
+       else if (data[3] == prop_atoms.net_wm_bottomright)
+           screen_desktop_layout.start_corner = data[3];
+       else if (data[3] == prop_atoms.net_wm_bottomleft)
+           screen_desktop_layout.start_corner = data[3];
+
+       /* fill in a zero rows/columns */
+       if (!(data[1] == 0 && data[2] == 0)) { /* both 0's is bad data.. */
+           if (data[1] == 0) {
+               data[1] = (screen_num_desktops +
+                          screen_num_desktops % data[2]) / data[2];
+           } else if (data[2] == 0) {
+               data[2] = (screen_num_desktops +
+                          screen_num_desktops % data[1]) / data[1];
+           }
+           screen_desktop_layout.columns = data[1];
+           screen_desktop_layout.rows = data[2];
+       }
+
+       /* bounds checking */
+       if (screen_desktop_layout.orientation ==
+           prop_atoms.net_wm_orientation_horz) {
+           if (screen_desktop_layout.rows > screen_num_desktops)
+               screen_desktop_layout.rows = screen_num_desktops;
+           if (screen_desktop_layout.columns > ((screen_num_desktops +
+                                                 screen_num_desktops %
+                                                 screen_desktop_layout.rows) /
+                                                screen_desktop_layout.rows))
+               screen_desktop_layout.columns =
+                   (screen_num_desktops + screen_num_desktops %
+                    screen_desktop_layout.rows) /
+                   screen_desktop_layout.rows;
+       } else {
+           if (screen_desktop_layout.columns > screen_num_desktops)
+               screen_desktop_layout.columns = screen_num_desktops;
+           if (screen_desktop_layout.rows > ((screen_num_desktops +
+                                              screen_num_desktops %
+                                              screen_desktop_layout.columns) /
+                                             screen_desktop_layout.columns))
+               screen_desktop_layout.rows =
+                   (screen_num_desktops + screen_num_desktops %
+                    screen_desktop_layout.columns) /
+                   screen_desktop_layout.columns;
+       }
+       g_free(data);
+    }
+}
+
+void screen_update_desktop_names()
+{
+    guint i;
+
+    /* empty the array */
+    for (i = 0; i < screen_desktop_names->len; ++i)
+       g_free(g_ptr_array_index(screen_desktop_names, i));
+    g_ptr_array_set_size(screen_desktop_names, 0);
+
+    PROP_GETSA(ob_root, net_desktop_names, utf8, screen_desktop_names);
+
+    while (screen_desktop_names->len < screen_num_desktops)
+       g_ptr_array_add(screen_desktop_names, g_strdup("Unnamed Desktop"));
+}
+
+void screen_show_desktop(gboolean show)
+{
+    GSList *it;
+    static Window saved_focus = 0;
+     
+    if (show == screen_showing_desktop) return; /* no change */
+
+    /* save the window focus, and restore it when leaving the show-desktop
+       mode */
+    if (show && focus_client)
+       saved_focus = focus_client->window;
+  
+    screen_showing_desktop = show;
+
+    for (it = client_list; it; it = g_slist_next(it)) {
+       Client *client = it->data;
+       if (client->type == Type_Desktop) {
+           if (show) client_focus(client);
+       } else
+           client_showhide(client);
+    }
+
+    if (!show) {
+       Client *f = focus_client;
+       if (!f || f->type == Type_Desktop) {
+           Client *c = g_hash_table_lookup(client_map,
+                                           (gpointer)saved_focus);
+           if (c) client_focus(c);
+       }
+    }
+
+    show = show ? 1 : 0; /* make it boolean */
+    PROP_SET32(ob_root, net_showing_desktop, cardinal, show);
+}
+
+void screen_install_colormap(Client *client, gboolean install)
+{
+    if (client == NULL) {
+       /* XXX DONT USE THE DEFAULT SHIT HERE */
+       if (install)
+           XInstallColormap(ob_display,
+                            DefaultColormap(ob_display, ob_screen));
+       else
+           XUninstallColormap(ob_display,
+                              DefaultColormap(ob_display, ob_screen));
+    } else {
+       XWindowAttributes wa;
+       if (XGetWindowAttributes(ob_display, client->window, &wa)) {
+           if (install)
+               XInstallColormap(ob_display, wa.colormap);
+           else
+               XUninstallColormap(ob_display, wa.colormap);
+       }
+    }
+}
+
+void screen_update_struts()
+{
+    GSList *it;
+    guint i;
+     
+    if (strut != NULL)
+       g_free(strut);
+    strut = g_new0(Strut, screen_num_desktops + 1);
+
+    for (it = client_list; it; it = it->next) {
+       Client *c = it->data;
+       if (c->iconic) continue; /* these dont count in the strut */
+    
+       if (c->desktop == 0xffffffff) {
+           for (i = 0; i < screen_num_desktops; ++i)
+               STRUT_ADD(strut[i], c->strut);
+       } else {
+           g_assert(c->desktop < screen_num_desktops);
+           STRUT_ADD(strut[c->desktop], c->strut);
+       }
+       /* apply to the 'all desktops' strut */
+       STRUT_ADD(strut[screen_num_desktops], c->strut);
+    }
+    screen_update_area();
+}
+
+static void screen_update_area()
+{
+    guint i;
+    gulong *dims;
+     
+    if (area != NULL)
+       g_free(area);
+    area = g_new0(Rect, screen_num_desktops + 1);
+     
+    dims = g_new(unsigned long, 4 * screen_num_desktops);
+    for (i = 0; i < screen_num_desktops + 1; ++i) {
+       Rect old_area = area[i];
+/*
+  #ifdef    XINERAMA
+  // reset to the full areas
+  if (isXineramaActive())
+  xineramaUsableArea = getXineramaAreas();
+  #endif // XINERAMA
+*/
+
+       RECT_SET(area[i], strut[i].left, strut[i].top,
+                screen_physical_size.width - (strut[i].left +
+                                              strut[i].right),
+                screen_physical_size.height - (strut[i].top +
+                                               strut[i].bottom));
+    
+/*
+  #ifdef    XINERAMA
+  if (isXineramaActive()) {
+  // keep each of the ximerama-defined areas inside the strut
+  RectList::iterator xit, xend = xineramaUsableArea.end();
+  for (xit = xineramaUsableArea.begin(); xit != xend; ++xit) {
+  if (xit->x() < usableArea.x()) {
+  xit->setX(usableArea.x());
+  xit->setWidth(xit->width() - usableArea.x());
+  }
+  if (xit->y() < usableArea.y()) {
+  xit->setY(usableArea.y());
+  xit->setHeight(xit->height() - usableArea.y());
+  }
+  if (xit->x() + xit->width() > usableArea.width())
+  xit->setWidth(usableArea.width() - xit->x());
+  if (xit->y() + xit->height() > usableArea.height())
+  xit->setHeight(usableArea.height() - xit->y());
+  }
+  }
+  #endif // XINERAMA
+*/
+       if (!RECT_EQUAL(old_area, area[i])) {
+           /* the area has changed, adjust all the maximized windows */
+           GSList *it;
+           for (it = client_list; it; it = it->next) {
+               Client *c = it->data;
+               if (i < screen_num_desktops) {
+                   if (c->desktop == i)
+                       client_remaximize(c);
+               } else {
+                   /* the 'all desktops' size */
+                   if (c->desktop == DESKTOP_ALL)
+                       client_remaximize(c);
+               }
+           }
+       }
+
+       /* don't set these for the 'all desktops' area */
+       if (i < screen_num_desktops) {
+           dims[(i * 4) + 0] = area[i].x;
+           dims[(i * 4) + 1] = area[i].y;
+           dims[(i * 4) + 2] = area[i].width;
+           dims[(i * 4) + 3] = area[i].height;
+       }
+    }
+    PROP_SET32A(ob_root, net_workarea, cardinal,
+               dims, 4 * screen_num_desktops);
+    g_free(dims);
+}
+
+Rect *screen_area(guint desktop)
+{
+    if (desktop >= screen_num_desktops) {
+       if (desktop == DESKTOP_ALL)
+           return &area[screen_num_desktops];
+       return NULL;
+    }
+    return &area[desktop];
+}
+
+Strut *screen_strut(guint desktop)
+{
+    if (desktop >= screen_num_desktops) {
+       if (desktop == DESKTOP_ALL)
+           return &strut[screen_num_desktops];
+       return NULL;
+    }
+    return &strut[desktop];
+}
diff --git a/c/screen.h b/c/screen.h
new file mode 100644 (file)
index 0000000..a591a20
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef __screen_h
+#define __screen_h
+
+#include "geom.h"
+
+struct Client;
+
+#define DESKTOP_ALL (0xffffffff)
+
+/*! The number of available desktops */
+extern guint screen_num_desktops;
+/*! The current desktop */
+extern guint screen_desktop;
+/*! The size of the screen */
+extern Size  screen_physical_size;
+/*! Are we in showing-desktop mode? */
+extern gboolean screen_showing_desktop;
+
+typedef struct DesktopLayout {
+    guint orientation;
+    guint start_corner;
+    guint rows;
+    guint columns;
+} DesktopLayout;
+extern DesktopLayout screen_desktop_layout;
+
+/*! An array of gchar*'s which are desktop names in UTF-8 format */
+extern GPtrArray *screen_desktop_names;
+
+/*! Take over the screen, set the basic hints on it claming it as ours */
+gboolean screen_annex();
+
+/*! Once the screen is ours, set up its initial state */
+void screen_startup();
+/*! Free resources */
+void screen_shutdown();
+
+/*! Figure out the new size of the screen and adjust stuff for it */
+void screen_resize();
+
+/*! Change the number of available desktops */
+void screen_set_num_desktops(guint num);
+/*! Change the current desktop */
+void screen_set_desktop(guint num);
+
+/*! Shows and focuses the desktop and hides all the client windows, or
+  returns to the normal state, showing client windows. */
+void screen_show_desktop(gboolean show);
+
+/*! Updates the desktop layout from the root property if available */
+void screen_update_layout();
+
+/*! Get desktop names from the root window property */
+void screen_update_desktop_names();
+
+/*! Installs or uninstalls a colormap for a client. If client is NULL, then
+  it handles the root colormap. */
+void screen_install_colormap(struct Client *client, gboolean install);
+
+void screen_update_struts();
+
+Rect *screen_area(guint desktop);
+
+Strut *screen_strut(guint desktop);
+
+#endif
diff --git a/c/screenwrap.c b/c/screenwrap.c
new file mode 100644 (file)
index 0000000..9ef14d8
--- /dev/null
@@ -0,0 +1,433 @@
+#include "screenwrap.h"
+#include "openbox.h"
+#include "screen.h"
+#include "kbind.h"
+#include "mbind.h"
+
+ScreenWrap *screenwrap_instance;
+
+/***************************************************************************
+   Define the type 'ScreenWrap'
+
+ ***************************************************************************/
+
+#define IS_SWRAP(v)  ((v)->ob_type == &ScreenWrapType)
+#define CHECK_SWRAP(self, funcname) { \
+    if (!IS_SWRAP(self)) { \
+        PyErr_SetString(PyExc_TypeError, \
+                       "descriptor '" funcname "'  a 'Screen' object"); \
+       return NULL; \
+    } \
+}
+
+staticforward PyTypeObject ScreenWrapType;
+
+/***************************************************************************
+   Attribute methods
+ ***************************************************************************/
+
+static PyObject *swrap_number(ScreenWrap *self, PyObject *args)
+{
+    CHECK_SWRAP(self, "number");
+    if (!PyArg_ParseTuple(args, ":number"))
+       return NULL;
+    return PyInt_FromLong(ob_screen);
+}
+
+static PyObject *swrap_rootWindow(ScreenWrap *self, PyObject *args)
+{
+    CHECK_SWRAP(self, "rootWindow");
+    if (!PyArg_ParseTuple(args, ":rootWindow"))
+       return NULL;
+    return PyInt_FromLong(ob_root);
+}
+
+static PyObject *swrap_state(ScreenWrap *self, PyObject *args)
+{
+    CHECK_SWRAP(self, "state");
+    if (!PyArg_ParseTuple(args, ":state"))
+       return NULL;
+    return PyInt_FromLong(ob_state);
+}
+
+static PyObject *swrap_numDesktops(ScreenWrap *self, PyObject *args)
+{
+    CHECK_SWRAP(self, "numDesktops");
+    if (!PyArg_ParseTuple(args, ":numDesktops"))
+       return NULL;
+    return PyInt_FromLong(screen_num_desktops);
+}
+
+static PyObject *swrap_desktop(ScreenWrap *self, PyObject *args)
+{
+    CHECK_SWRAP(self, "desktop");
+    if (!PyArg_ParseTuple(args, ":desktop"))
+       return NULL;
+    return PyInt_FromLong(screen_desktop);
+}
+
+static PyObject *swrap_physicalSize(ScreenWrap *self, PyObject *args)
+{
+    PyObject *tuple;
+
+    CHECK_SWRAP(self, "physicalSize");
+    if (!PyArg_ParseTuple(args, ":physicalSize"))
+       return NULL;
+    tuple = PyTuple_New(2);
+    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(screen_physical_size.width));
+    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(screen_physical_size.height));
+    return tuple;
+}
+
+static PyObject *swrap_showingDesktop(ScreenWrap *self, PyObject *args)
+{
+    CHECK_SWRAP(self, "showingDesktop");
+    if (!PyArg_ParseTuple(args, ":showingDesktop"))
+       return NULL;
+    return PyInt_FromLong(screen_showing_desktop ? 1 : 0);
+}
+
+static PyObject *swrap_desktopLayout(ScreenWrap *self, PyObject *args)
+{
+    PyObject *tuple;
+
+    CHECK_SWRAP(self, "desktopLayout");
+    if (!PyArg_ParseTuple(args, ":desktopLayout"))
+       return NULL;
+    tuple = PyTuple_New(4);
+    PyTuple_SET_ITEM(tuple, 0,
+                    PyInt_FromLong(screen_desktop_layout.orientation));
+    PyTuple_SET_ITEM(tuple, 1,
+                    PyInt_FromLong(screen_desktop_layout.start_corner));
+    PyTuple_SET_ITEM(tuple, 2,
+                    PyInt_FromLong(screen_desktop_layout.rows));
+    PyTuple_SET_ITEM(tuple, 3,
+                    PyInt_FromLong(screen_desktop_layout.columns));
+    return tuple;
+}
+
+static PyObject *swrap_desktopNames(ScreenWrap *self, PyObject *args)
+{
+    PyObject *list;
+    guint s, i;
+
+    CHECK_SWRAP(self, "desktopNames");
+    if (!PyArg_ParseTuple(args, ":desktopNames"))
+       return NULL;
+    s = screen_desktop_names->len;
+    list = PyList_New(s);
+    for (i = 0; i < s; ++i)
+       PyList_SET_ITEM(list, i, PyString_FromString
+                       (g_ptr_array_index(screen_desktop_names, i)));
+    return list;
+}
+
+static PyObject *swrap_area(ScreenWrap *self, PyObject *args)
+{
+    PyObject * tuple;
+    Rect *r;
+    guint i;
+
+    CHECK_SWRAP(self, "area");
+    if (!PyArg_ParseTuple(args, "i:area", &i))
+       return NULL;
+    r = screen_area(i);
+    if (r == NULL) {
+       PyErr_SetString(PyExc_IndexError,
+                       "the requested desktop was not valid");
+       return NULL;
+    }
+    tuple = PyTuple_New(4);
+    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(r->x));
+    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(r->y));
+    PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(r->width));
+    PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(r->height));
+    return tuple;
+}
+
+static PyObject *swrap_strut(ScreenWrap *self, PyObject *args)
+{
+    PyObject *tuple;
+    Strut *s;
+    guint i;
+
+    CHECK_SWRAP(self, "strut");
+    if (!PyArg_ParseTuple(args, "i:strut", &i))
+       return NULL;
+    s = screen_strut(i);
+    if (s == NULL) {
+       PyErr_SetString(PyExc_IndexError,
+                       "the requested desktop was not valid");
+       return NULL;
+    }
+    tuple = PyTuple_New(4);
+    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(s->left));
+    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(s->top));
+    PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(s->right));
+    PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(s->bottom));
+    return tuple;
+}
+
+static PyObject *swrap_grabKey(ScreenWrap *self, PyObject *args)
+{
+    PyObject *item, *tuple;
+    GList *keylist = NULL, *it;
+    int i, s;
+    gboolean grab = FALSE;
+
+    CHECK_SWRAP(self, "grabKey");
+    if (!PyArg_ParseTuple(args, "O:grabKey", &tuple))
+       return NULL;
+
+    if (PyTuple_Check(tuple)) {
+       s = PyTuple_GET_SIZE(tuple);
+       if (s > 0) {
+           for (i = 0; i < s; ++i) {
+               item = PyTuple_GET_ITEM(tuple, i);
+               if (!PyString_Check(item))
+                   break;
+               keylist = g_list_append(keylist,
+                                       g_strdup(PyString_AsString(item)));
+           }
+           if (i == s)
+               grab = kbind_add(keylist);
+
+           for (it = keylist; it != NULL; it = it->next)
+               g_free(it->data);
+           g_list_free(it);
+
+           if (grab) {
+               Py_INCREF(Py_None);
+               return Py_None;
+           } else {
+               PyErr_SetString(PyExc_ValueError,
+                               "the key could not be grabbed");
+               return NULL;
+           }
+       }
+    }
+
+    PyErr_SetString(PyExc_TypeError, "expected a tuple of strings");
+    return NULL;
+}
+
+static PyObject *swrap_clearKeyGrabs(ScreenWrap *self, PyObject *args)
+{
+    CHECK_SWRAP(self, "clearKeyGrabs");
+    if (!PyArg_ParseTuple(args, ":clearKeyGrabs"))
+       return NULL;
+    kbind_clearall();
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *swrap_grabKeyboard(ScreenWrap *self, PyObject *args)
+{
+    int grab;
+
+    CHECK_SWRAP(self, "grabKeyboard");
+    if (!PyArg_ParseTuple(args, "i:grabKeyboard", &grab))
+       return NULL;
+    if (!kbind_grab_keyboard(grab)) {
+       PyErr_SetString(PyExc_RuntimeError, "failed to grab keyboard");
+       return NULL;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *swrap_grabButton(ScreenWrap *self, PyObject *args)
+{
+    char *name;
+    char *context_str;
+    GQuark context;
+
+    CHECK_SWRAP(self, "grabButton");
+    if (!PyArg_ParseTuple(args, "ss:grabKey", &name, &context_str))
+       return NULL;
+
+    context = g_quark_try_string(context_str);
+
+    if (!context) {
+       PyErr_SetString(PyExc_ValueError, "invalid context");
+       return NULL;
+    }
+
+    if (mbind_add(name, context)) {
+       Py_INCREF(Py_None);
+       return Py_None;
+    } else {
+       PyErr_SetString(PyExc_ValueError,
+                       "the button could not be grabbed");
+       return NULL;
+    }
+}
+
+static PyObject *swrap_clearButtonGrabs(ScreenWrap *self, PyObject *args)
+{
+    CHECK_SWRAP(self, "clearButtonGrabs");
+    if (!PyArg_ParseTuple(args, ":clearButtonGrabs"))
+       return NULL;
+    mbind_clearall();
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *swrap_grabPointer(ScreenWrap *self, PyObject *args)
+{
+    int grab;
+
+    CHECK_SWRAP(self, "grabPointer");
+    if (!PyArg_ParseTuple(args, "i:grabPointer", &grab))
+       return NULL;
+    if (!mbind_grab_pointer(grab)) {
+       PyErr_SetString(PyExc_RuntimeError, "failed to grab pointer");
+       return NULL;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyMethodDef ScreenWrapAttributeMethods[] = {
+    {"number", (PyCFunction)swrap_number, METH_VARARGS,
+     "Screen.number() -- Returns the number of the screen on the X server on "
+     "which this Openbox instance is running."},
+    {"rootWindow", (PyCFunction)swrap_rootWindow, METH_VARARGS,
+     "Screen.rootWindow() -- Returns the window id of the root window."},
+    {"state", (PyCFunction)swrap_state, METH_VARARGS,
+     "Screen.state() -- Returns the running state of Openbox. One of the "
+     "ob.State_ constants."},
+    {"numDesktops", (PyCFunction)swrap_numDesktops, METH_VARARGS,
+     "Screen.numDesktops() -- Returns the number of desktops available."},
+    {"desktop", (PyCFunction)swrap_desktop, METH_VARARGS,
+     "Screen.desktop() -- Returns the currently visible desktop."},
+    {"physicalSize", (PyCFunction)swrap_physicalSize, METH_VARARGS,
+     "Screen.physicalSize() -- Returns the physical size (in pixels) of the "
+     "display in a tuple. The tuple is formatted as (width, height)."},
+    {"showingDesktop", (PyCFunction)swrap_showingDesktop, METH_VARARGS,
+     "Screen.showingDesktop() -- Returns if in showing-the-desktop mode or "
+     "not."},
+    {"desktopNames", (PyCFunction)swrap_desktopNames, METH_VARARGS,
+     "Screen.desktopNames() -- Returns a list of the names of all the "
+     "desktops, and possibly for desktops beyond those. The names are encoded "
+     "as UTF-8."},
+    {"desktopLayout", (PyCFunction)swrap_desktopLayout, METH_VARARGS,
+     "Screen.desktopLayout() -- Returns the layout of the desktops, as "
+     "specified by a compliant pager, in a tuple. The format of the tuple is "
+     "(orientation, corner, rows, columns). Where, orientation is one of the "
+     "ob.Orientation_ constants, corner is one of the ob.Corner_ constants, "
+     "and rows and columns specify the size of the layout. The rows and "
+     "columns will always include all the desktops."},
+    {"area", (PyCFunction)swrap_area, METH_VARARGS,
+     "Screen.area(d) -- Returns the usuable area on the Screen for a desktop, "
+     "in the form of a tuple. The tuples format is (x, y, width, height). The "
+     "desktop must be in the range of desktops on the screen, or 0xffffffff "
+     "to get a combined area for 'all desktops'."},
+    {"strut", (PyCFunction)swrap_area, METH_VARARGS,
+     "Screen.strut(d) -- Returns the combined strut of all clients on a "
+     "desktop, in the form of a tuple. The tuples format is "
+     "(left, top, right, bottom). The desktop must be in the range of "
+     "desktops on the screen, or 0xffffffff to get a combined strut for "
+     "'all desktops'."},
+    {"grabKey", (PyCFunction)swrap_grabKey, METH_VARARGS,
+     "Screen.grabKey(('Mod1-C-a', 'd')) -- Grabs a key chain so that key "
+     "events for it will occur. The argument must be a tuple of one or "
+     "more elements. Each key element is made up of "
+     "Modifier-Modifier-...-Key, where Modifier is one of Mod1, Mod2, "
+     "Mod3, Mod4, Mod5, S (for Shift), or C (for Control)."},
+    {"clearKeyGrabs", (PyCFunction)swrap_clearKeyGrabs, METH_VARARGS,
+     "Screen.clearKeyGrabs() -- Removes all key grabs that have been done "
+     "with grabKey()."},
+    {"grabKeyboard", (PyCFunction)swrap_grabKeyboard, METH_VARARGS,
+     "Screen.grabKeyboard(grab) -- Grabs or ungrabs the entire keyboard. When "
+     "the keyboard is grabbed, all key presses will be sent to the "
+     "hooks.keyboard hook. (grabbed keys will go to the hooks.events hook "
+     "too. "},
+    {"grabButton", (PyCFunction)swrap_grabButton, METH_VARARGS,
+     "Screen.grabButton('C-1', \"frame\") -- Grabs a pointer button "
+     "for the given context. The context must be one of the ob.Context_* "
+     "constants. The button definition is made up of "
+     "Modifier-Modifier-...-Button, where Modifier is one of Mod1, Mod2, "
+     "Mod3, Mod4, Mod5, S (for Shift), or C (for Control)."},
+    {"clearButtonGrabs", (PyCFunction)swrap_clearButtonGrabs, METH_VARARGS,
+     "Screen.clearButtonGrabs() -- Removes all button grabs that have been "
+     "done with grabButton()."},
+    {"grabPointer", (PyCFunction)swrap_grabPointer, METH_VARARGS,
+     "grabPointer(grab) -- Grabs or ungrabs the pointer device. When the "
+     "pointer is grabbed, all pointer events will be sent to the "
+     "hooks.pointer  hook. (grabbed buttons will NOT go to the hooks.events "
+     "hook while the pointer is grabbed)."},
+    {NULL, NULL, 0, NULL}
+};
+
+/***************************************************************************
+   Type methods/struct
+ ***************************************************************************/
+
+static PyObject *swrap_getattr(ScreenWrap *self, char *name)
+{
+    CHECK_SWRAP(self, "getattr");
+    return Py_FindMethod(ScreenWrapAttributeMethods, (PyObject*)self, name);
+}
+
+static void swrap_dealloc(ScreenWrap *self)
+{
+    PyObject_Del((PyObject*) self);
+}
+
+static PyTypeObject ScreenWrapType = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "Screen",
+    sizeof(ScreenWrap),
+    0,
+    (destructor) swrap_dealloc,     /*tp_dealloc*/
+    0,                              /*tp_print*/
+    (getattrfunc) swrap_getattr,    /*tp_getattr*/
+    0,                              /*tp_setattr*/
+    0,                              /*tp_compare*/
+    0,                              /*tp_repr*/
+    0,                              /*tp_as_number*/
+    0,                              /*tp_as_sequence*/
+    0,                              /*tp_as_mapping*/
+    0,                              /*tp_hash */
+};
+
+/***************************************************************************
+   External methods
+ ***************************************************************************/
+
+void screenwrap_startup()
+{
+    PyObject *ob, *obdict;
+    ScreenWrap *swrap;
+
+    ScreenWrapType.ob_type = &PyType_Type;
+    ScreenWrapType.tp_doc = "Wraps information and functionality global to an "
+       "instance of Openbox.";
+    PyType_Ready(&ScreenWrapType);
+    swrap = PyObject_New(ScreenWrap, &ScreenWrapType);
+
+    /* get the ob module/dict */
+    ob = PyImport_ImportModule("ob"); /* new */
+    g_assert(ob != NULL);
+    obdict = PyModule_GetDict(ob); /* borrowed */
+    g_assert(obdict != NULL);
+
+    PyDict_SetItemString(obdict, "Screen", (PyObject*)swrap);
+    Py_DECREF(swrap);
+    Py_DECREF(ob);
+}
+
+void screenwrap_shutdown()
+{
+}
+
+
diff --git a/c/screenwrap.h b/c/screenwrap.h
new file mode 100644 (file)
index 0000000..2ca278c
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __screenwrap_h
+#define __screenwrap_h
+
+#include <Python.h>
+
+/* ScreenWrap is a PyObject */
+typedef struct ScreenWrap {
+    PyObject_HEAD
+} ScreenWrap;
+
+void screenwrap_startup();
+void screenwrap_shutdown();
+
+#endif
diff --git a/c/stacking.c b/c/stacking.c
new file mode 100644 (file)
index 0000000..081bde9
--- /dev/null
@@ -0,0 +1,116 @@
+#include "openbox.h"
+#include "prop.h"
+#include "focus.h"
+#include "client.h"
+#include "frame.h"
+#include <glib.h>
+
+GList  *stacking_list = NULL;
+
+void stacking_set_list()
+{
+    Window *windows, *win_it;
+    GList *it;
+    guint size = g_list_length(stacking_list);
+
+    /* create an array of the window ids (from bottom to top,
+       reverse order!) */
+    if (size > 0) {
+       windows = g_new(Window, size);
+       win_it = windows;
+       for (it = g_list_last(stacking_list); it; it = it->prev, ++win_it)
+           *win_it = ((Client*)it->data)->window;
+    } else
+       windows = NULL;
+
+    PROP_SET32A(ob_root, net_client_list_stacking, window, windows, size);
+
+    if (windows)
+       g_free(windows);
+}
+
+void stacking_raise(Client *client)
+{
+    Window wins[2];  /* only ever restack 2 windows. */
+    GList *it;
+    Client *m;
+
+    g_assert(stacking_list != NULL); /* this would be bad */
+
+    m = client_find_modal_child(client);
+    /* if we have a modal child, raise it instead, we'll go along tho later */
+    if (m) stacking_raise(m);
+  
+    /* remove the client before looking so we can't run into ourselves */
+    stacking_list = g_list_remove(stacking_list, client);
+  
+    /* the stacking list is from highest to lowest */
+    it = stacking_list;
+    while (it) {
+       Client *c = it->data;
+       if (client->layer >= c->layer && m != c)
+           break;
+       it = it->next;
+    }
+
+    /*
+      if our new position is the top, we want to stack under the focus_backup.
+      otherwise, we want to stack under the previous window in the stack.
+    */
+    if (it == stacking_list)
+       wins[0] = focus_backup;
+    else if (it != NULL)
+       wins[0] = ((Client*)it->prev->data)->frame->window;
+    else
+       wins[0] = ((Client*)g_list_last(stacking_list)->data)->frame->window;
+    wins[1] = client->frame->window;
+
+    stacking_list = g_list_insert_before(stacking_list, it, client);
+
+    XRestackWindows(ob_display, wins, 2);
+
+    stacking_set_list();
+}
+
+void stacking_lower(Client *client)
+{
+    Window wins[2];  /* only ever restack 2 windows. */
+    GList *it;
+
+    g_assert(stacking_list != NULL); /* this would be bad */
+
+    it = g_list_last(stacking_list);
+
+    if (client->modal && client->transient_for) {
+       /* don't let a modal window lower below its transient_for */
+       it = g_list_find(stacking_list, client->transient_for);
+       g_assert(it != NULL);
+
+       wins[0] = (it == stacking_list ? focus_backup :
+                  ((Client*)it->prev->data)->frame->window);
+       wins[1] = client->frame->window;
+       if (wins[0] == wins[1]) return; /* already right above the window */
+
+       stacking_list = g_list_remove(stacking_list, client);
+       stacking_list = g_list_insert_before(stacking_list, it, client);
+    } else {
+       while (it != stacking_list) {
+           Client *c = it->data;
+           if (client->layer >= c->layer)
+               break;
+           it = it->prev;
+       }
+       if (it->data == client) return; /* already the bottom, return */
+
+       wins[0] = ((Client*)it->data)->frame->window;
+       wins[1] = client->frame->window;
+
+       stacking_list = g_list_remove(stacking_list, client);
+       stacking_list = g_list_insert_before(stacking_list,
+                                            it->next, client);
+    }
+
+    XRestackWindows(ob_display, wins, 2);
+    stacking_set_list();
+}
+
diff --git a/c/stacking.h b/c/stacking.h
new file mode 100644 (file)
index 0000000..fa6bcf8
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __stacking_h
+#define __stacking_h
+
+#include <glib.h>
+
+struct Client;
+
+extern GList  *stacking_list;
+
+/*! Sets the client stacking list on the root window from the
+  stacking_clientlist */
+void stacking_set_list();
+
+/*! Raises a client window above all others in its stacking layer
+  raiseWindow has a couple of constraints that lowerWindow does not.<br>
+  1) raiseWindow can be called after changing a Client's stack layer, and
+     the list will be reorganized properly.<br>
+  2) raiseWindow guarantees that XRestackWindows() will <i>always</i> be
+     called for the specified client.
+*/
+void stacking_raise(struct Client *client);
+
+/*! Lowers a client window below all others in its stacking layer */
+void stacking_lower(struct Client *client);
+
+#endif
diff --git a/c/xerror.c b/c/xerror.c
new file mode 100644 (file)
index 0000000..49a795f
--- /dev/null
@@ -0,0 +1,32 @@
+#include "openbox.h"
+#include <glib.h>
+#include <X11/Xlib.h>
+
+static gboolean xerror_ignore = FALSE;
+
+int xerror_handler(Display *d, XErrorEvent *e)
+{
+#ifdef DEBUG
+    if (!xerror_ignore) {
+       char errtxt[128];
+         
+       /*if (e->error_code != BadWindow) */
+       {
+           XGetErrorText(d, e->error_code, errtxt, 127);
+           if (e->error_code == BadWindow)
+               g_warning("X Error: %s", errtxt);
+           else
+               g_error("X Error: %s", errtxt);
+       }
+    }
+#else
+    (void)d; (void)e;
+#endif
+    return 0;
+}
+
+void xerror_set_ignore(gboolean ignore)
+{
+    XSync(ob_display, FALSE);
+    xerror_ignore = ignore;
+}
diff --git a/c/xerror.h b/c/xerror.h
new file mode 100644 (file)
index 0000000..74b236f
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __xerror_h
+#define __xerror_h
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+int xerror_handler(Display *, XErrorEvent *);
+
+void xerror_set_ignore(gboolean ignore);
+
+#endif
index b83776f..dc1404c 100644 (file)
@@ -1,7 +1,7 @@
 AC_PREREQ([2.50])
 AC_INIT([src/main.cc])
-AC_CONFIG_HEADERS(config.h)
-AC_LANG([C++])
+#AC_CONFIG_HEADERS(config.h)
+AC_LANG([C])
 AC_ENABLE_STATIC([no])
 AC_ENABLE_SHARED([yes])
 
@@ -13,23 +13,27 @@ fi
 
 AM_INIT_AUTOMAKE([openbox], [2.90.0cvs])
 
-AC_PATH_PROG([awk_cmd], [awk]) # used by swig.m4
-test "$awk_cmd" || AC_MSG_ERROR([awk not found])
+##AC_PATH_PROG([awk_cmd], [awk]) # used by swig.m4
+##test "$awk_cmd" || AC_MSG_ERROR([awk not found])
 AC_PATH_PROG([regex_cmd], [sed])
 test "$regex_cmd" || AC_MSG_ERROR([sed not found])
 
 AM_MAINTAINER_MODE
 # Determine build target
 OB_DEBUG
-# Pick compiler specific/build target flags
+# Pick compiler specific/build target flags, and set $CVS
 OB_COMPILER_FLAGS
-AC_C_BIGENDIAN
 
-SWIG_PROG(1.3.14)
-SWIG_ENABLE_CXX
-SWIG_MULTI_MODULE_SUPPORT
-SWIG_PYTHON
+PKG_CHECK_MODULES(GLIB, glib-2.0)
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
        
+PKG_CHECK_MODULES(GMODULE, gmodule-2.0)
+AC_SUBST(GMODULE_CFLAGS)
+AC_SUBST(GMODULE_LIBS)
+       
+PYTHON_DEVEL
+
 AC_PROG_LN_S
 AC_PROG_LIBTOOL
 LIBTOOL="$LIBTOOL --silent"
@@ -39,12 +43,14 @@ ALL_LINGUAS=""
 AM_GNU_GETTEXT_VERSION(0.11.5)
 AM_GNU_GETTEXT([external])
 
-AC_CHECK_HEADERS(fcntl.h signal.h stdarg.h stdint.h unistd.h sys/select.h sys/signal.h sys/stat.h sys/time.h sys/types.h sys/wait.h)
+AC_CHECK_HEADERS(ctype.h fcntl.h locale.h signal.h string.h stdlib.h unistd.h)
+AC_CHECK_HEADERS(sys/select.h sys/time.h sys/wait.h)
 # AC_HEADER_TIME
 # AC_TYPE_SIGNAL
 
-# Check for Xft2
-XFT_DEVEL(2.0.0)
+PKG_CHECK_MODULES(XFT, xft)
+AC_SUBST(XFT_CFLAGS)
+AC_SUBST(XFT_LIBS)
 
 # Check for X11 extensions
 X11_EXT_XKB
@@ -52,16 +58,18 @@ X11_EXT_SHAPE
 X11_EXT_XINERAMA
 
 AC_CONFIG_FILES([Makefile po/Makefile.in
-               otk/Makefile
-               src/Makefile
-               tools/Makefile
-               wrap/Makefile
-               scripts/Makefile
+               c/Makefile
+               kernel/Makefile
+               render/Makefile
+               engines/Makefile
+               engines/openbox/Makefile
+               python/Makefile
                doc/Makefile
                doc/doxygen/Makefile
                data/Makefile
                data/buttons/Makefile
-               data/styles/Makefile
+               themes/Makefile
+               themes/openbox/Makefile
                ])
 AC_OUTPUT
 
@@ -74,10 +82,5 @@ if test "$DEBUG" = "yes"; then
 else
   AC_MSG_RESULT([Creating a RELEASE build.])
 fi
-AC_MSG_RESULT([Using '$prefix' for installation.])
-AC_MSG_RESULT([Using '$CXX' for C++ compiler.])
-AC_MSG_RESULT([Building with '$CPPFLAGS' for C++ preprocessor flags.])
-AC_MSG_RESULT([Building with '$CXXFLAGS' for C++ compiler flags.])
-AC_MSG_RESULT([Building with '$LIBS' for linker flags.])
 AC_MSG_RESULT
 AC_MSG_RESULT([configure complete, now type \"make\"])
diff --git a/data/styles/Makefile.am b/data/styles/Makefile.am
deleted file mode 100644 (file)
index eef4b14..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#styledir = $(pkgdatadir)/styles
-MAINTAINERCLEANFILES = Makefile.in
-#style_DATA =
-#artwiz bbs bluebox cthulhain deep fieron fieron2 flux frobozz \
-#frobust mbdtex miklos nyz nyzclone ob20 operation outcomes paper \
-#purplehaaze shade steelblue steelblue2 the_orange trisb twice \
-#warp-xp
-#EXTRA_DIST = $(style_DATA)
-
-distclean-local:
-       $(RM) *\~ .\#*
-#uninstall-am:
-#      -rmdir -p $(DESTDIR)$(styledir)
index a79bc85..3409d8c 100644 (file)
@@ -1,9 +1,9 @@
-CLEANFILES = openbox.1
+#CLEANFILES = openbox.1
 MAINTAINERCLEANFILES = Makefile.in
-man_MANS = openbox.1
+#man_MANS = openbox.1
 SUBDIRS = doxygen
 
-EXTRA_DIST = openbox.1.in
+#EXTRA_DIST = openbox.1.in
 
 DEFAULT_MENU=$(pkgdatadir)/menu
 
diff --git a/doc/bsetbg.1 b/doc/bsetbg.1
deleted file mode 100644 (file)
index a233325..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-.TH bsetbg 1 "February 2002" "bsetbg" "v2.0"
-.SH NAME
-bsetbg \- utility to manipulate the appearance of the X11 desktop's root window.
-.SH SYNOPSIS
-\fBbsetbg\fR [options] \fIimage\fR
-.SH DESCRIPTION
-\fBbsetbg\fR is intended to provide a standard method for the \fIBlackbox\fR
-window manager to alter the background of the root window
-(although it will work with any other window manager as well). \fBbsetbg\fR
-acts as a wrapper both to \fIbsetroot\fR
-and to a flexible variety of third-party applications that it uses when handling images files.
-
-.SH OPTIONS
-.TP
-\fB\-f\fR, \fB\-full\fR \fIimage\fR
-\fIimage\fR is stretched to fill the entire desktop.
-.TP
-\fB\-t\fR, \fB\-tile\fR \fIimage\fR
-\fIimage\fR is tiled on the desktop.
-.TP
-\fB\-c\fR, \fB\-center\fR \fIimage\fR
-\fIimage\fR is centered on the desktop.
-.TP
-\fB\-e\fR, \fB\-exec\fR \fIprogram\fR \fIoptions\fR \fIfallback\-\fIarg\fR \fIimage\fR
-This option tells \fBbsetbg\fR to run a separate command by executing \fIprogram\fR with 
-\fIoptions\fR, where \fIoptions\fR are arguments to \fIprogram\fR.
-
-If a \fIfallback\-arg\fR is supplied (\fB\-full\fR, \fB\-tile\fR, or \fB\-center\fR
-as described above),
-\fBbsetbg\fR will assume that the last argument is a filename. In the case that
-\fIprogram\fR exits non-zero or isn't available on the target system, \fBbsetbg\fR 
-will try to handle the file with the fallback argument.
-
-See the \fBEXAMPLES\fR section for more information on \fB\-exec\fR.
-.TP
-\fB\-p\fR, \fB\-post\fR \fIlist\fR
-Specifies a list of arguments to pass to the $POST_COMMAND.
-.TP
-\fB\-d\fR, \fB\-debug\fR
-Debugging mode. \fBbsetbg\fR will print commands without executing them.
-.TP
-\fB\-g\fR, \fB\-generate\fR \fIlist\fR
-Output a list of default configuration values, suitable for redirecting into 
-\fI~/.bsetbgrc\fR. Any arguments that are supplied will be considered applications 
-to search for in the system path, overriding \fBbsetbg\fR's internal defaults.
-.TP
-\fB\-app\fR \fIimageApp\fR
-Use this flag to specify which image application to use. This
-application may be one of the pre-defined list or any application
-capable of displaying an image on the root window. This flag may be
-used in conjunction with passing application specific  parameters to
-the application, in which
-case they should be enclosed in double quotes.
-.TP
-\fB\-v\fR, \fB\-version\fR
-Output version number.
-.TP
-\fB\-h\fR, \fB\-help\fR
-Output a brief usage message.
-
-.SH OTHER OPTIONS
-\fBbsetbg\fR will also accept all of the arguments for \fIbsetroot\fR.
-Consult the \fIbsetroot\fR(1) man page for further information.
-
-.SH CONFIGURATION
-\fBbsetbg\fR will read its configuration values from the file \fI~/.bsetbgrc\fR
-if it exists. Otherwise, it will scan the 
-system path for a pre-defined list of image applications to use 
-(currently this list consists of qiv, xli, xv, wmsetbg, Esetroot, 
-display, and xsetbg).
-\fP
-\fI~/.bsetbgrc\fR should contain the following variables:
-.TP
-\fB    CENTER=\fR\fI"string"\fR
-Application and arguments to be used to center an image on the root window
-when the \fB-center\fR argument is specified.
-
-.TP
-\fB    FULL=\fR\fI"string"\fR
-Application and arguments to be used to stretch an image to fill the root window
-when the \fB-full\fR argument is specified.
-
-.TP
-\fB    TILE=\fR\fI"string"\fR
-Application and arguments to be used to tile an image on the root window
-when the \fB-tile\fR argument is specified.
-
-.TP
-\fB    DEFAULT=\fR\fI"string"\fR
-Action to take place by default if none of the above have been specified.
-
-.TP
-The following variables are optional:
-
-.TP
-\fB    NO_EXEC=\fR\fI"boolean"\fR
-If this variable is set, bsetbg will never modify the root window.
-
-.TP
-\fB    POST_COMMAND=\fR\fI"string"\fR
-This variable specifies a command that \fBbsetbg\fR will run after every
-successful modification of the root window.
-
-.TP
-\fB    LOG_LAST_CMD=\fR\fI"boolean"\fR
-If this variable is set, \fBbsetbg\fR will keep a logfile of the last two
-successful commands.
-
-.TP
-\fB    LOGFILE=\fR\fI"string"\fR
-This variable can specify the logfile to be used when $LOG_LAST_CMD is defined.
-The default is ~/.bsetbg_last_cmd .
-
-.TP
-As mentioned above, \fBbsetbg\fR will function perfectly for the majority of users without having a configuration file. Power users who want more control over \fBbsetbg\fR's behavior should run \fBbsetbg -g\fR and use the output to create a \fI~/.bsetbgrc\fR which may then be tweaked by hand.
-
-.SH EXAMPLES
-In this example, bsetbg will set the image in centered mode:
-
-    bsetbg -center foo.png
-
-An example of the \fB-exec\fR argument:
-
-    bsetbg -exec xv -root -quit -rmode 5 -rbg rgb:2/2/2 \\
-       -center foo.png
-
-An example in which bsetbg creates a configuration file using xv and qiv:
-
-    bsetbg -g xv qiv > ~/.bsetbgrc
-
-An example of the use of the \fB-app\fR argument:
-
-    bsetbg  -app qiv "-o rgb:d6/c5/a2 -x" -c foo.png
-
-.SH AUTHOR
-The author of
-.B bsetbg
-may be reached at \fItmk@lordzork.com\fR.
-
-.SH SEE ALSO
-\fIblackbox\fR(1), \fIbsetroot\fR(1), \fIqiv\fR(1), \fIxli\fR(1), \fIxv\fR(1), \fIdisplay\fR(1), 
-\fIwmsetbg\fR(1)
diff --git a/doc/bsetroot.1 b/doc/bsetroot.1
deleted file mode 100644 (file)
index 9445c0d..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-.\"
-.\" Man page for Bsetroot
-.\"
-.\" Copyright (c) 2000 by Wilbert Berendsen <wbsoft@xs4all.nl>
-.\"
-.TH bsetroot 1 "June 16th, 2000" "0.60.3"
-.SH NAME
-bsetroot \- Openbox utility to change root window appearance
-.SH SYNOPSIS
-.BR bsetroot " \-help"
-.br
-.B bsetroot
-.RI "[ \-display " display " ] \-solid " color
-.br
-.B bsetroot
-.RI "[ \-display " display " ] \-mod " "x y" " \-fg " color " \-bg " color
-.br
-.B bsetroot
-.RI "[ \-display " display " ] \-gradient " texture " \-from " color " \-to " color
-.SH DESCRIPTION
-Bsetroot is a utility that can control the appearance of the root window in
-three ways: Either give it a solid color, or write a two color modula pattern
-to it, or render a gradient texture, based on two different colors.
-.PP
-Bsetroot resembles
-.IR xsetroot (1)
-in this functionality but it supports multiple screen displays, and gradient
-textures the same way as Openbox does.
-It doesn't handle cursors etc.
-Bsetroot is part of the Openbox package.
-.SH OPTIONS
-Bsetroot operates in three ways, you must choose one of the first 3 options:
-.TP
-.BI \-solid " color"
-Sets the root window to specified color.
-.TP
-.BI \-mod " x y"
-Creates a modula pattern. You must specify
-.BR \-bg " and " \-fg
-colors.
-.TP
-.BI \-gradient " texturestring"
-Renders the specified texture string to the root window.
-For possible texture strings, please refer to
-.IR openbox (1).
-You must also specify both a
-.BR \-from " and a " \-to
-color.
-.TP
-.BI \-display " display"
-Tells Bsetroot to connect to the specified display.
-.TP
-.BI "\-bg, \-background " color
-Background color.
-Needed for
-.B \-mod
-patterns.
-.TP
-.BI "\-fg, \-foreground " color
-Foreground color.
-Needed for
-.B \-mod
-patterns.
-.TP
-.BI \-from " color"
-Start color for rendering textures.
-Needed for
-.B \-gradient
-operation mode.
-.TP
-.BI \-to " color"
-Ending color for rendering textures.
-Needed for
-.B \-gradient
-operation mode.
-.TP
-.B \-help
-Prints version info and short help text.
-.SH AUTHOR
-Bsetroot was written and maintained by Brad Hughes
-.nh \" hyphenation off
-(blackbox@alug.org)
-.hy \" on again
-and Jeff Raven
-.nh
-(jraven@psu.edu).
-.hy
-.SH SEE ALSO
-.IR openbox (1)
diff --git a/doc/openbox.1.in b/doc/openbox.1.in
deleted file mode 100644 (file)
index 674d836..0000000
+++ /dev/null
@@ -1,859 +0,0 @@
-.\"
-.\" Man page for Openbox
-.\"
-.\" Copyright (c) 2000 by Wilbert Berendsen <wbsoft@xs4all.nl>
-.\"
-.\" This manual page may be freely distributed and modified.
-.\" Parts of the text are taken from website and several README's
-.\" by His Great Hughesness himself. Why reinvent wheels?
-.\"
-.\" Created with NEdit, tested with ``man'' and ``tkman.''
-.\" This manpage uses only standard groff and tmac.an macros.
-.\" To all translators who didn't do manpages earlier (like me ;-):
-.\" Read the Man-Page-Mini-HOWTO and the LDP manpage ``man 7 man''
-.\" There's all I needed to know about these macros.
-.\"
-.\" Updated for bb 0.61 at Sat Sep  9 06:56:04 CEST 2000
-.\"
-.\" ..define sort of <blockquote><pre> macro
-.de EX
-.ne 5
-.if n .sp 1
-.if t .sp .5
-.nf
-.in +.5i
-..
-.de EE
-.fi
-.in -.5i
-.if n .sp 1
-.if t .sp .5
-..
-.TH openbox 1 "April 8th, 2002" "@VERSION@"
-.SH NAME
-openbox \- a window manager for X11
-.SH SYNOPSIS
-.BR openbox " \-help | \-version"
-.br
-.B openbox 
-.RI "[ \-rc" " rcfile " "] [ \-display" " display " ]
-.SH DESCRIPTION
-.\"
-.\" First few Paragraphs taken from FILLMEINNOW
-.\"
-.\"Openbox is yet another addition to the list of window managers for the Open
-.\"Group's X Window System, Version 11 Release 6 and above.
-.\"Openbox is built with C++, sharing no common code with any other window
-.\"manager (even though the graphics implementation is similar to that of Window
-.\"Maker).
-.\".PP
-.\"From the time the first line of code was written, Openbox has evolved around
-.\"one premise, minimalism.
-.\"It's not meant to be Eye Candy, nor the most Featureful, nor the most Adorned
-.\"for modelling the Widely acclaimed NeXT interface.
-.\"It is just meant to be
-.\".BR fast .
-.\".PP
-.\"Openbox provides configurable window decorations, a root menu to launch
-.\"applications, and a toolbar that shows the current workspace name, the focused
-.\"application name, and the current time.
-.\"There is also a workspace menu to add or remove workspaces. The `slit' can be
-.\"used to dock small applications, e.g. most of the bbtools can use the slit.
-.\".PP
-.\"Openbox features a special kind of icon handling: When you minimize a window,
-.\"no icon appears; instead, you can view all minimized applications in the `Icons'
-.\"submenu of the workspace menu.
-.\"Your desktop will never get cluttered with icons. As an alternative to icons,
-.\"shaded windows are provided: A double click on the titlebar of a window will
-.\"shade it (i.e. the window will disappear; only the titlebar stays visible).
-.\".PP
-.\"Openbox uses its own graphics class to render its images on the fly.
-.\"By using style files, you can determine at a great level how your desktop looks.
-.\"Openbox currently uses the Blackbox protocol to communicate with other clients
-.\"such as the pager.  Work is underway to support the new window manager
-.\"specification that both GNOME and KDE use.
-.SH OPTIONS
-Openbox supports the following command line options:
-.TP
-.B \-help
-Display command line options and compiled-in features, then exit.
-.TP
-.B \-version
-Display version info and exit.
-.TP
-.BI \-rc \ rcfile
-Use another rcfile than the default
-.IR  "~/.openbox/rc" .
-.TP
-.BI \-display \ display
-Start Openbox on the specified display.
-Programs started by Openbox will have the
-.B DISPLAY
-environment variable set to this value, too.
-.SH RUNNING OPENBOX
-This program is usually started by the user's startup script, most times called
-.IR ~/.xinitrc .
-To run openbox, modify the script by adding
-.EX 0
-exec openbox
-.EE
-as the last executed command of the script.
-When Openbox terminates, the X session will terminate too.
-.PP
-When started, Openbox will try to find a default menu file in
-.IR @defaultmenu@ .
-You can provide a system-wide menu for your users here.
-.PP
-On exit or restart, Openbox will save user defaults in the file
-.I ~/.openbox/rc
-in the user's home directory.
-Some resources in this file can be edited by hand.
-.SH USING OPENBOX
-Openbox does no keyboard handling by itself; instead, it relies on an external
-program
-.IR epist (1)
-or
-.IR bbkeys (1)
-for this.
-So, in this section, we will discuss all mouse commands.
-.SS Root window (background):
-A right click (button 3) will pop up the root menu.
-With this, you can launch your applications.
-You can also customize this menu for your needs.  See above for its location.
-A middle click (button 2) will pop up the workspace menu.
-You can add or remove a workspace, view
-applications running on all workspace, inspect your iconified applications,
-and jump directly to any workspace or application.
-.PP
-Left clicking (button 1) on an application in the Workspaces menu will bring
-you to that workspace and raise/focus that application;
-middle clicking (button 2) will warp the application to the current workspace.
-.SS Toolbar:
-The toolbar consists of three fields: a workspace name, the name of the
-window that currently has focus, and a clock.
-A left click on the toolbar will bring it to the foreground, a
-middle click will hide it behind other windows (if AlwaysOnTop is not set), and
-the right button will bring up a little menu.
-.PP
-Using this menu, you can enter a name for the current workspace (when finished,
-press Enter).
-Also, you can choose the toolbar's position, whether or not it
-should be always on top (i.e. it cannot be obscured by other windows),
-and whether it should hide itself when the mouse moves away.
-.PP
-Note: In Openbox versions below 0.60.0, a right click on the toolbar
-immediately entered workspace name edit mode.
-.SS Window Titlebar and Borders:
-A left click on any part of the window's border will raise it.
-Dragging then moves the window.
-Dragging the resize grips at the bottom left and bottom right
-corners resizes the window.
-Middle clicking will immediately lower the window.
-Right clicking on the border or titlebar pops up the window menu,
-containing these commands:
-.TP
-.B Send To...
-Send window to another workspace.
-When you select the workspace with the middle button, Openbox will
-send you, along with the application, to the selected workspace.
-.TP
-.B Shade
-Shade window (display titlebar only).
-.TP
-.B Iconify
-Iconify window.
-The `icon' can be found in the `Icons' submenu of the workspace menu.  It will
-*NOT* appear on screen otherwise.
-.TP
-.B Maximize
-(Un)Maximize window.
-When you click the middle button on this item, the
-window will maximize only vertically.
-.TP
-.B Raise
-Raise window.
-.TP
-.B Lower
-Lower window.
-.TP
-.B Stick
-(Un)Stick window.
-A stuck window will always be displayed in the current workspace.
-.TP
-.B Kill Client
-Kill (-SIGKILL) owner of window.  Only use this if the client refuses to close.
-.TP
-.B Close
-Close the application cleanly.
-.PP
-When you double click on the titlebar of a window, it will `shade', so
-that only the titlebar stays visible.
-Another double click will redisplay the window contents.
-.SS Window Buttons:
-The button at the left upper corner of a window is the Minimize button.
-Clicking with any button causes the window to be iconified.
-The rightmost button (with the X) closes the application.
-The other button on the right (if present) maximizes the window in three ways:
-Button 1 causes full screen maximization, button 2 maximizes the window only
-vertically, and button 3 only horizontally.
-.SS Any menu:
-Clicking button 3 in a menu will popdown the menu.
-Clicking button 1 on the titlebar of any (sub)menu and then dragging it somewhere
-else will cause the menu to stay visible, and not disappear when you click on
-a menu item.
-.SS Miscellaneous:
-When you want to drag a window, but cannot see either the bottom handle or its
-titlebar, you can press Alt + button 1 anywhere in the window and then
-drag it around.
-You can also use Alt + button 1 to raise a partially visible window.
-Finally, Alt + button 2 lowers a window, and Alt + button 3 resizes the window.
-.SH MENU FILE
-A default menu file is installed in
-.IR @defaultmenu@ .
-Of course, this system-wide menu can be customized for all users at once.
-But it is also possible to create a personal menu.
-It is a convention to use the directory
-.IR "~/.openbox/"
-in your home directory, and to create a menu file, e.g.
-.I menu
-in this directory, or copy the system-wide menu file to this location.
-Next, we have to tell Openbox to load our menu file instead of the default.
-This is accomplished by adding (or changing) a resource value in the
-.I ~/.openbox/rc
-file, e.g.:
-.EX
-session.menuFile:       ~/.openbox/menu
-.EE
-For this change to take effect, Openbox has to be restarted.
-Be sure that your menu is usable, then choose `Restart' from the default
-Openbox root menu.
-.SS Menu syntax
-The menu syntax is very simple and very effective.
-There are up to three fields in a menu line.
-They are of the form:
-.EX
-[tag] (label or filename) {command or filename}
-.EE
-The supported tags are as follows:
-.TP
-.B [begin] (label for root menu)
-This tells Openbox to start parsing the menu file.
-This tag is required for Openbox to parse your menu file.
-If it cannot find it, the system default menu is used instead.
-.TP
-.B [end]
-This tells Openbox that it is at the end of a menu.
-This can either be a submenu or the main root menu.
-There must be at least one of these tags in your menu to correspond to the
-required [begin] tag.
-.TP
-.B [exec] (label for command) {shell command}
-Inserts a command item into the menu.
-When you select the menu item from the menu, Openbox runs `shell command.'
-.TP
-.B [exit] (label for exit)
-Inserts an item that shuts down and exits Openbox.
-Any running programs are not closed.
-.TP
-.B [include] (filename)
-Parses the file specified by
-.I filename
-and includes it with the current menu. The filename can be the full path to a
- file, or it can begin with
-.IR ~/ ,
-which will be expanded into your home directory (e.g.
-.EX
-[include] (~/.openbox/stylesmenu)
-.EE
-will include
-.I /home/bhughes/.openbox/stylesmenu
-in my menu).
-.TP
-.B [nop] (label - optional)
-Insert a non-operational item into the current menu.
-This can be used to help format the menu into blocks or sections
-if so desired.
-.B [nop]
-does accept a label, but it is not required, and a blank item will be used
-if none is supplied.
-.TP 
-.B [style] (label) {filename}
-This tells Openbox to insert an item that, when selected, reads the style file
-named
-.I filename
-and applies the new textures, colors, and fonts to the current
-running session.
-.TP
-.B [stylesdir] (directory name)
-Reads all filenames (directories are ignored) from the specified directory and
- creates menu items in the current menu for every filename.  Openbox assumes
- that each file is a valid style file.  When selected by the user, Openbox 
-applies the selected style file to the current session.
-The labels that are created in the menu are the filenames of the style files.
-.TP
-.B [stylesmenu] (label) {directory name}
-Creates a submenu entry with
-.I label 
-(that is also the title of the new submenu), and inserts in that submenu all
-filenames in the specified directory, in the same way as the
-.B [stylesdir]
-command does.
-.IP
-.RB Both\  [stylesdir] \ and\  [stylesmenu]
-commands make it possible to install style files without editing your menu file.
-.TP
-.B [submenu] (label) {title for menu - optional}
-This tells Openbox to create and parse a new menu.
-This menu is inserted as a submenu into the parent menu.
-These menus are parsed recursively, so there is no limit to the number of levels
-or nested submenus you can have.
-The title for the new menu is optional; if none is supplied,
-the new menu's title is the same as the item label.
-An
-.B [end]
-tag is required to end the submenu.
-.TP
-.B [reconfig] (label)
-When selected, this item rereads the current style, menu files and rc file,
-and applies any changes.
-This is useful for creating a new style or theme, as you don't have to
-constantly restart Openbox every