aboutsummaryrefslogtreecommitdiffstats
path: root/matchbox2/mb-window-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'matchbox2/mb-window-manager.c')
-rw-r--r--matchbox2/mb-window-manager.c2252
1 files changed, 2252 insertions, 0 deletions
diff --git a/matchbox2/mb-window-manager.c b/matchbox2/mb-window-manager.c
new file mode 100644
index 0000000..6468873
--- /dev/null
+++ b/matchbox2/mb-window-manager.c
@@ -0,0 +1,2252 @@
+/*
+ * Matchbox Window Manager II - A lightweight window manager not for the
+ * desktop.
+ *
+ * Authored By Matthew Allum <mallum@o-hand.com>
+ * Tomas Frydrych <tf@o-hand.com>
+ *
+ * Copyright (c) 2005, 2007, 2008 OpenedHand Ltd - http://o-hand.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ */
+#include "mb-wm.h"
+#include "mb-wm-client-app.h"
+#include "mb-wm-client-panel.h"
+#include "mb-wm-client-dialog.h"
+#include "mb-wm-client-desktop.h"
+#include "mb-wm-client-input.h"
+#include "mb-wm-client-note.h"
+#include "mb-wm-client-menu.h"
+#include "mb-wm-theme.h"
+
+#if ENABLE_COMPOSITE
+# include "mb-wm-comp-mgr.h"
+# if ENABLE_CLUTTER_COMPOSITE_MANAGER
+# include <clutter/x11/clutter-x11.h>
+# include "mb-wm-comp-mgr-clutter.h"
+# else
+# include "mb-wm-comp-mgr-xrender.h"
+# endif
+# include "mb-wm-client-override.h"
+# include <X11/extensions/Xdamage.h>
+# include <X11/extensions/Xrender.h>
+# include <X11/extensions/Xcomposite.h>
+#endif
+
+#if USE_GTK
+# include <gdk/gdk.h>
+#endif
+
+#include <stdarg.h>
+
+#include <X11/Xmd.h>
+
+#ifdef HAVE_XFIXES
+#include <X11/extensions/Xfixes.h> /* Used to *really* hide cursor */
+#endif
+
+#ifdef HAVE_XCURSOR
+#include <X11/Xcursor/Xcursor.h>
+#endif
+
+#include <X11/cursorfont.h>
+
+static void
+mb_wm_process_cmdline (MBWindowManager *wm);
+
+static void
+mb_wm_focus_client (MBWindowManager *wm, MBWindowManagerClient *client);
+
+static Bool
+mb_wm_activate_client_real (MBWindowManager * wm, MBWindowManagerClient *c);
+
+static void
+mb_wm_update_root_win_rectangles (MBWindowManager *wm);
+
+static Bool
+mb_wm_is_my_window (MBWindowManager *wm, Window xwin,
+ MBWindowManagerClient **client);
+
+static void
+mb_wm_real_get_desktop_geometry (MBWindowManager *wm, MBGeometry *geom);
+
+static MBWindowManagerClient*
+mb_wm_client_new_func (MBWindowManager *wm, MBWMClientWindow *win)
+{
+ if (win->override_redirect)
+ {
+ MBWM_DBG ("### override-redirect window ###\n");
+#if ENABLE_COMPOSITE
+ return mb_wm_client_override_new (wm, win);
+#else
+ return NULL;
+#endif
+ }
+
+ if (win->net_type == wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_DOCK])
+ {
+ MBWM_DBG ("### is panel ###\n");
+ return mb_wm_client_panel_new(wm, win);
+ }
+ else if (win->net_type == wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_DIALOG])
+ {
+ MBWM_DBG ("### is dialog ###\n");
+ return mb_wm_client_dialog_new(wm, win);
+ }
+ else if (win->net_type == wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_NOTIFICATION])
+ {
+ MBWM_DBG ("### is notification ###\n");
+ return mb_wm_client_note_new (wm, win);
+ }
+ else if (win->net_type ==wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_MENU] ||
+ win->net_type ==wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_POPUP_MENU]||
+ win->net_type ==wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_DROPDOWN_MENU])
+ {
+ MBWM_DBG ("### is menu ###\n");
+ return mb_wm_client_menu_new (wm, win);
+ }
+ else if (win->net_type == wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_DESKTOP])
+ {
+ MBWM_DBG ("### is desktop ###\n");
+ /* Only one desktop allowed */
+ if (wm->desktop)
+ return NULL;
+
+ return mb_wm_client_desktop_new (wm, win);
+ }
+ else if (win->net_type == wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_TOOLBAR] ||
+ win->net_type == wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_INPUT])
+ {
+ MBWM_DBG ("### is input ###\n");
+ return mb_wm_client_input_new (wm, win);
+ }
+ else if (win->net_type == wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_NORMAL])
+ {
+ MBWM_DBG ("### is application ###\n");
+ return mb_wm_client_app_new (wm, win);
+ }
+ else
+ {
+#if MBWM_WANT_DEBUG
+ char * name = XGetAtomName (wm->xdpy, win->net_type);
+ printf("### unhandled window type %s (%x) ###\n", name, win->xwindow);
+ XFree (name);
+#endif
+ return mb_wm_client_app_new (wm, win);
+ }
+
+ return NULL;
+}
+
+static MBWMTheme *
+mb_wm_real_theme_new (MBWindowManager * wm, const char * path)
+{
+ /*
+ * FIXME -- load the selected theme from some configuration
+ */
+ return mb_wm_theme_new (wm, path);
+}
+
+#if ENABLE_COMPOSITE && COMP_MGR_BACKEND
+static MBWMCompMgr *
+mb_wm_real_comp_mgr_new (MBWindowManager *wm)
+{
+#if ENABLE_CLUTTER_COMPOSITE_MANAGER
+ return mb_wm_comp_mgr_clutter_new (wm);
+#else
+ return mb_wm_comp_mgr_xrender_new (wm);
+#endif
+}
+#endif
+
+static MBWMLayout *
+mb_wm_layout_new_real (MBWindowManager *wm)
+{
+ MBWMLayout * layout = mb_wm_layout_new (wm);
+
+ if (!layout)
+ mb_wm_util_fatal_error("OOM?");
+
+ return layout;
+}
+
+#if USE_GTK
+static GdkFilterReturn
+mb_wm_gdk_xevent_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data)
+{
+ MBWindowManager * wm = data;
+ XEvent * xev = (XEvent*) xevent;
+
+ mb_wm_main_context_handle_x_event (xev, wm->main_ctx);
+
+ if (wm->sync_type)
+ mb_wm_sync (wm);
+
+ return GDK_FILTER_CONTINUE;
+}
+#endif
+
+#if ENABLE_CLUTTER_COMPOSITE_MANAGER
+#if USE_GTK
+static GdkFilterReturn
+mb_wm_clutter_gdk_xevent_filter (GdkXEvent *xevent, GdkEvent *event,
+ gpointer data)
+{
+ switch (clutter_x11_handle_event ((XEvent*)xevent))
+ {
+ default:
+ case CLUTTER_X11_FILTER_CONTINUE:
+ return GDK_FILTER_CONTINUE;
+ case CLUTTER_X11_FILTER_TRANSLATE:
+ return GDK_FILTER_TRANSLATE;
+ case CLUTTER_X11_FILTER_REMOVE:
+ return GDK_FILTER_REMOVE;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+#else
+static ClutterX11FilterReturn
+mb_wm_clutter_xevent_filter (XEvent *xev, ClutterEvent *cev, gpointer data)
+{
+ MBWindowManager * wm = data;
+
+ mb_wm_main_context_handle_x_event (xev, wm->main_ctx);
+
+ if (wm->sync_type)
+ mb_wm_sync (wm);
+
+ return CLUTTER_X11_FILTER_CONTINUE;
+}
+#endif
+#endif
+
+#if ENABLE_CLUTTER_COMPOSITE_MANAGER || USE_GTK
+static void
+mb_wm_main_real (MBWindowManager *wm)
+{
+
+#if USE_GTK
+ gdk_window_add_filter (NULL, mb_wm_gdk_xevent_filter, wm);
+#if ENABLE_CLUTTER_COMPOSITE_MANAGER
+ gdk_window_add_filter (NULL, mb_wm_clutter_gdk_xevent_filter, NULL);
+#endif
+ gtk_main ();
+#else
+ clutter_x11_add_filter (mb_wm_clutter_xevent_filter, wm);
+ clutter_main ();
+#endif
+}
+#endif
+
+static void
+mb_wm_class_init (MBWMObjectClass *klass)
+{
+ MBWindowManagerClass *wm_class;
+
+ MBWM_MARK();
+
+ wm_class = (MBWindowManagerClass *)klass;
+
+ wm_class->process_cmdline = mb_wm_process_cmdline;
+ wm_class->client_new = mb_wm_client_new_func;
+ wm_class->theme_new = mb_wm_real_theme_new;
+ wm_class->client_activate = mb_wm_activate_client_real;
+ wm_class->layout_new = mb_wm_layout_new_real;
+ wm_class->get_desktop_geometry = mb_wm_real_get_desktop_geometry;
+
+#if ENABLE_CLUTTER_COMPOSITE_MANAGER
+ wm_class->main = mb_wm_main_real;
+#endif
+
+#if ENABLE_COMPOSITE && COMP_MGR_BACKEND
+ wm_class->comp_mgr_new = mb_wm_real_comp_mgr_new;
+#endif
+
+#if MBWM_WANT_DEBUG
+ klass->klass_name = "MBWindowManager";
+#endif
+}
+
+static void
+mb_wm_destroy (MBWMObject *this)
+{
+ MBWindowManager * wm = MB_WINDOW_MANAGER (this);
+ MBWMList *l = wm->clients;
+
+ while (l)
+ {
+ MBWMList * old = l;
+ mb_wm_object_unref (l->data);
+
+ l = l->next;
+
+ free (old);
+ }
+
+ mb_wm_object_unref (MB_WM_OBJECT (wm->root_win));
+ mb_wm_object_unref (MB_WM_OBJECT (wm->theme));
+ mb_wm_object_unref (MB_WM_OBJECT (wm->layout));
+ mb_wm_object_unref (MB_WM_OBJECT (wm->main_ctx));
+}
+
+static int
+mb_window_manager_init (MBWMObject *this, va_list vap);
+
+int
+mb_wm_class_type ()
+{
+ static int type = 0;
+
+ if (UNLIKELY(type == 0))
+ {
+ static MBWMObjectClassInfo info = {
+ sizeof (MBWindowManagerClass),
+ sizeof (MBWindowManager),
+ mb_window_manager_init,
+ mb_wm_destroy,
+ mb_wm_class_init
+ };
+
+ type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0);
+ }
+
+ return type;
+}
+
+MBWindowManager*
+mb_wm_new (int argc, char **argv)
+{
+ MBWindowManager *wm = NULL;
+
+ wm = MB_WINDOW_MANAGER (mb_wm_object_new (MB_TYPE_WINDOW_MANAGER,
+ MBWMObjectPropArgc, argc,
+ MBWMObjectPropArgv, argv,
+ NULL));
+
+ if (!wm)
+ return wm;
+
+ return wm;
+}
+
+MBWindowManager*
+mb_wm_new_with_dpy (int argc, char **argv, Display * dpy)
+{
+ MBWindowManager *wm = NULL;
+
+ wm = MB_WINDOW_MANAGER (mb_wm_object_new (MB_TYPE_WINDOW_MANAGER,
+ MBWMObjectPropArgc, argc,
+ MBWMObjectPropArgv, argv,
+ MBWMObjectPropDpy, dpy,
+ NULL));
+
+ if (!wm)
+ return wm;
+
+ return wm;
+}
+
+static Bool
+mb_wm_handle_key_press (XKeyEvent *xev,
+ void *userdata)
+{
+ MBWindowManager *wm = (MBWindowManager*)userdata;
+
+ mb_wm_keys_press (wm,
+ XKeycodeToKeysym(wm->xdpy, xev->keycode, 0),
+ xev->state);
+
+ return True;
+}
+
+static Bool
+mb_wm_handle_button_press (XButtonEvent *xev, void *userdata)
+{
+ MBWindowManager *wm = (MBWindowManager*)userdata;
+ MBWindowManagerClient *client = NULL;
+
+ if (xev->button != 1)
+ return True;
+
+ mb_wm_is_my_window (wm, xev->window, &client);
+
+ if (!client)
+ return True;
+
+ /*
+ * If the client is not application, we make sure it has focus.
+ * If the client is an application, we top it if it is currently not the top
+ * application; otherwise, we ensure it has focus.
+ */
+ if (MB_WM_CLIENT_CLIENT_TYPE (client) == MBWMClientTypeApp)
+ {
+ MBWindowManagerClient * top = mb_wm_get_visible_main_client(wm);
+
+ if (top == client)
+ mb_wm_focus_client (wm, client);
+ else
+ mb_wm_activate_client (wm, client);
+ }
+ else
+ {
+ mb_wm_focus_client (wm, client);
+ }
+
+ XAllowEvents (wm->xdpy, ReplayPointer, CurrentTime);
+
+ return True;
+}
+
+static Bool
+mb_wm_handle_destroy_notify (XDestroyWindowEvent *xev,
+ void *userdata)
+{
+ MBWindowManager *wm = (MBWindowManager*)userdata;
+ MBWindowManagerClient *client = NULL;
+
+ MBWM_MARK();
+
+ client = mb_wm_managed_client_from_xwindow(wm, xev->window);
+
+ if (client)
+ {
+ if (mb_wm_client_window_is_state_set (client->window,
+ MBWMClientWindowEWMHStateHidden))
+ {
+ if (mb_wm_client_is_hiding_from_desktop (client))
+ {
+ /*
+ * If the destroyed hidden window is hidden because it was
+ * on a different desktop than current, we have to unmanage
+ * it, as it is still considered managed.
+ */
+ mb_wm_unmanage_client (wm, client, True);
+ }
+ else
+ {
+ /*
+ * Otherwise, this is regular minimized window, in which case
+ * the window is no longer managed, only the resources are
+ * kept in the clients list; so we only remove it and free.
+ */
+ wm->clients = mb_wm_util_list_remove (wm->clients, client);
+ mb_wm_object_unref (MB_WM_OBJECT (client));
+ }
+ }
+ else
+ mb_wm_unmanage_client (wm, client, True);
+ }
+
+ return True;
+}
+
+static Bool
+mb_wm_handle_unmap_notify (XUnmapEvent *xev,
+ void *userdata)
+{
+ MBWindowManager *wm = (MBWindowManager*)userdata;
+ MBWindowManagerClient *client = NULL;
+
+ MBWM_MARK();
+
+ client = mb_wm_managed_client_from_xwindow(wm, xev->window);
+
+ if (client)
+ {
+ if (client->skip_unmaps)
+ {
+ MBWM_DBG ("skipping unmap for %p (skip count %d)\n",
+ client, client->skip_unmaps);
+
+ client->skip_unmaps--;
+ }
+ else
+ {
+ /*
+ * If the iconizing flag is set, we unmanage the client, but keep
+ * the resources around; we reset the iconizing flag to indicate
+ * that the iconizing has completed (and the client window is now in
+ * hidden state).
+ *
+ * If the client is not iconizing and is not alreadly in a hidden
+ * state, we unmange it and destroy all the associated resources.
+ */
+ if (mb_wm_client_is_iconizing (client))
+ {
+ MBWM_DBG ("iconizing client %p\n", client);
+
+ mb_wm_unmanage_client (wm, client, False);
+ mb_wm_client_reset_iconizing (client);
+ }
+ else if (!mb_wm_client_window_is_state_set (client->window,
+ MBWMClientWindowEWMHStateHidden))
+ {
+#if ENABLE_COMPOSITE
+ if (mb_wm_compositing_enabled (wm))
+ mb_wm_comp_mgr_unmap_notify (wm->comp_mgr, client);
+#endif
+ MBWM_DBG ("removing client %p\n", client);
+ mb_wm_unmanage_client (wm, client, True);
+ }
+ }
+ }
+
+ return True;
+}
+
+static Bool
+mb_wm_handle_property_notify (XPropertyEvent *xev,
+ void *userdata)
+{
+ MBWindowManager *wm = (MBWindowManager*)userdata;
+ MBWindowManagerClient *client;
+ int flag = 0;
+
+ if (xev->window == wm->root_win->xwindow)
+ {
+ if (xev->atom == wm->atoms[MBWM_ATOM_MB_THEME])
+ {
+ Atom type;
+ int format;
+ unsigned long items;
+ unsigned long left;
+ char *theme_path;
+
+ XGetWindowProperty (wm->xdpy, wm->root_win->xwindow,
+ xev->atom, 0, 8192, False,
+ XA_STRING, &type, &format,
+ &items, &left,
+ (unsigned char **)&theme_path);
+
+ if (!type || !items)
+ return True;
+
+ mb_wm_set_theme_from_path (wm, theme_path);
+
+ XFree (theme_path);
+ }
+
+ return True;
+ }
+
+ client = mb_wm_managed_client_from_xwindow(wm, xev->window);
+
+ if (!client)
+ return True;
+
+ if (xev->atom == wm->atoms[MBWM_ATOM_NET_WM_USER_TIME])
+ flag = MBWM_WINDOW_PROP_NET_USER_TIME;
+ else if (xev->atom == wm->atoms[MBWM_ATOM_WM_NAME] ||
+ xev->atom == wm->atoms[MBWM_ATOM_NET_WM_NAME])
+ flag = MBWM_WINDOW_PROP_NAME;
+ else if (xev->atom == wm->atoms[MBWM_ATOM_WM_HINTS])
+ flag = MBWM_WINDOW_PROP_WM_HINTS;
+ else if (xev->atom == wm->atoms[MBWM_ATOM_WM_NORMAL_HINTS])
+ flag = MBWM_WINDOW_PROP_WM_NORMAL_HINTS;
+ else if (xev->atom == wm->atoms[MBWM_ATOM_NET_WM_ICON])
+ flag = MBWM_WINDOW_PROP_NET_ICON;
+ else if (xev->atom == wm->atoms[MBWM_ATOM_WM_PROTOCOLS])
+ flag = MBWM_WINDOW_PROP_PROTOS;
+ else if (xev->atom == wm->atoms[MBWM_ATOM_WM_TRANSIENT_FOR])
+ flag = MBWM_WINDOW_PROP_TRANSIENCY;
+ else if (xev->atom == wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE])
+ flag = MBWM_WINDOW_PROP_WIN_TYPE;
+ else if (xev->atom == wm->atoms[MBWM_ATOM_WM_CLIENT_MACHINE])
+ flag = MBWM_WINDOW_PROP_CLIENT_MACHINE;
+ else if (xev->atom == wm->atoms[MBWM_ATOM_NET_WM_PID])
+ flag = MBWM_WINDOW_PROP_NET_PID;
+
+ if (flag)
+ mb_wm_client_window_sync_properties (client->window, flag);
+
+ return True;
+}
+
+#if ENABLE_COMPOSITE
+static Bool
+mb_wm_handle_composite_config_notify (XConfigureEvent *xev,
+ void *userdata)
+{
+ MBWindowManager * wm = (MBWindowManager*)userdata;
+
+ if (mb_wm_comp_mgr_enabled (wm->comp_mgr))
+ {
+ MBWindowManagerClient *client;
+
+ client = mb_wm_managed_client_from_frame (wm, xev->window);
+
+ if (client)
+ mb_wm_comp_mgr_client_configure (client->cm_client);
+ }
+ return True;
+}
+#endif
+
+/*
+ * This is called if the root window resizes itself, which happens when RANDR is
+ * used to resize or rotate the display.
+ */
+static Bool
+mb_wm_handle_root_config_notify (XConfigureEvent *xev,
+ void *userdata)
+{
+ MBWindowManager * wm = (MBWindowManager*)userdata;
+
+ wm->xdpy_width = xev->width;
+ wm->xdpy_height = xev->height;
+
+ mb_wm_update_root_win_rectangles (wm);
+
+ mb_wm_display_sync_queue (wm, MBWMSyncGeometry);
+}
+
+static Bool
+mb_wm_handle_config_request (XConfigureRequestEvent *xev,
+ void *userdata)
+{
+ MBWindowManager *wm = (MBWindowManager*)userdata;
+ MBWindowManagerClient *client;
+ unsigned long value_mask;
+ int req_x = xev->x;
+ int req_y = xev->y;
+ int req_w = xev->width;
+ int req_h = xev->height;
+ MBGeometry req_geom, *win_geom;
+ Bool no_size_change;
+
+ client = mb_wm_managed_client_from_xwindow(wm, xev->window);
+
+ if (!client)
+ {
+ XWindowChanges xwc;
+ MBWM_DBG ("### No client found for configure event ###\n");
+ /*
+ * We have to allow this window to configure; things like gtk menus
+ * and hildon banners all request configuration before mapping, so
+ * if we did not allow this, things break down.
+ */
+ xwc.x = req_x;
+ xwc.y = req_y;
+ xwc.width = req_w;
+ xwc.height = req_h;
+ xwc.sibling = xev->above;
+ xwc.stack_mode = xev->detail;
+
+ XConfigureWindow (wm->xdpy, xev->window, xev->value_mask, &xwc);
+
+ return True;
+ }
+
+ value_mask = xev->value_mask;
+ win_geom = &client->window->geometry;
+
+ req_geom.x = (value_mask & CWX) ? req_x : win_geom->x;
+ req_geom.y = (value_mask & CWY) ? req_y : win_geom->y;
+ req_geom.width = (value_mask & CWWidth) ? req_w : win_geom->width;
+ req_geom.height = (value_mask & CWHeight) ? req_h : win_geom->height;
+
+ /* We can't determine at this point what the right response
+ * to this configure request is since layout management might
+ * also want to tweak the window geometry.
+ *
+ * We make a note that the configure request needs a response
+ * and when we reach mb_wm_sync - but after all layout decisions
+ * have been made - then we can determine if the request
+ * has been accepted or not and send any synthetic events as
+ * needed.
+ */
+ mb_wm_client_configure_request_ack_queue (client);
+
+ mb_wm_client_request_geometry (client,
+ &req_geom,
+ MBWMClientReqGeomIsViaConfigureReq);
+
+ return True;
+}
+
+/*
+ * Check if this window belongs to the WM, and if it does, and is a client
+ * window, optionaly return the client.
+ */
+static Bool
+mb_wm_is_my_window (MBWindowManager *wm,
+ Window xwin,
+ MBWindowManagerClient **client)
+{
+ MBWindowManagerClient *c;
+
+#if ENABLE_COMPOSITE
+ if (wm->comp_mgr && mb_wm_comp_mgr_is_my_window (wm->comp_mgr, xwin))
+ {
+ /* Make sure to set the returned client to NULL, as this is a
+ * window that belongs to the composite manager, and so it has no
+ * client associated with it.
+ */
+ if (client)
+ *client = NULL;
+
+ return True;
+ }
+#endif
+
+ mb_wm_stack_enumerate_reverse(wm, c)
+ if (mb_wm_client_owns_xwindow (c, xwin))
+ {
+ if (client)
+ *client = c;
+
+ return True;
+ }
+
+ return False;
+}
+
+#if ENABLE_COMPOSITE
+
+/* For the compositing engine we need to track overide redirect
+ * windows so the compositor can paint them.
+ */
+static Bool
+mb_wm_handle_map_notify (XMapEvent *xev,
+ void *userdata)
+{
+ MBWindowManager *wm = (MBWindowManager*)userdata;
+ MBWindowManagerClient *client = NULL;
+ MBWindowManagerClass *wm_class =
+ MB_WINDOW_MANAGER_CLASS (MB_WM_OBJECT_GET_CLASS (wm));
+ MBWMClientWindow *win = NULL;
+
+ MBWM_NOTE (COMPOSITOR, "@@@@ Map Notify for %x @@@@\n", xev->window);
+
+ if (!wm_class->client_new)
+ {
+ MBWM_DBG("### No new client hook exists ###");
+ return True;
+ }
+
+ if (mb_wm_is_my_window (wm, xev->window, &client))
+ {
+ if (client)
+ {
+ Window xwin_top =
+ client->xwin_frame ? client->xwin_frame : client->window->xwindow;
+
+ /*
+ * Only notify the CM when the top-level window maps, not for the
+ * decors, etc.
+ */
+ if (xev->window == xwin_top && wm->comp_mgr)
+ {
+ MBWM_NOTE (COMPOSITOR, "@@@@ client %p @@@@\n", client);
+ mb_wm_comp_mgr_map_notify (wm->comp_mgr, client);
+
+ /*
+ * If the hiding_from_deskotp flag is set, reset it
+ */
+ mb_wm_client_reset_hiding_from_desktop (client);
+ }
+ }
+
+ return True;
+ }
+
+ win = mb_wm_client_window_new (wm, xev->window);
+
+ if (!win || win->window_class == InputOnly)
+ {
+ mb_wm_object_unref (MB_WM_OBJECT (win));
+ return True;
+ }
+
+ client = wm_class->client_new (wm, win);
+
+ if (!client)
+ {
+ mb_wm_object_unref (MB_WM_OBJECT (win));
+ return True;
+ }
+
+ mb_wm_manage_client (wm, client, True);
+ mb_wm_comp_mgr_map_notify (wm->comp_mgr, client);
+
+ return True;
+}
+#endif
+
+static Bool
+mb_wm_handle_map_request (XMapRequestEvent *xev,
+ void *userdata)
+{
+ MBWindowManager *wm = (MBWindowManager*)userdata;
+ MBWindowManagerClient *client = NULL;
+ MBWindowManagerClass *wm_class =
+ MB_WINDOW_MANAGER_CLASS (MB_WM_OBJECT_GET_CLASS (wm));
+ MBWMClientWindow *win = NULL;
+
+ MBWM_MARK();
+
+ MBWM_NOTE (COMPOSITOR, "@@@@ Map Request for %x @@@@\n", xev->window);
+
+ if (mb_wm_is_my_window (wm, xev->window, &client))
+ {
+ if (client)
+ mb_wm_activate_client (wm, client);
+
+ return True;
+ }
+
+ if (!wm_class->client_new)
+ {
+ MBWM_DBG("### No new client hook exists ###");
+ return True;
+ }
+
+ win = mb_wm_client_window_new (wm, xev->window);
+
+ if (!win)
+ return True;
+
+ client = wm_class->client_new (wm, win);
+
+ if (!client)
+ {
+ mb_wm_object_unref (MB_WM_OBJECT (win));
+ return True;
+ }
+
+ mb_wm_manage_client (wm, client, True);
+
+ return True;
+}
+
+
+static void
+stack_get_window_list (MBWindowManager *wm, Window * win_list, int * count)
+{
+ MBWindowManagerClient *client;
+ int i = 0;
+
+ if (!wm->stack_n_clients)
+ return;
+
+ mb_wm_stack_enumerate_reverse(wm, client)
+ {
+ if (client->xwin_frame &&
+ !(client->window->ewmh_state & MBWMClientWindowEWMHStateFullscreen))
+ win_list[i++] = client->xwin_frame;
+ else
+ win_list[i++] = MB_WM_CLIENT_XWIN(client);
+
+ if (client->xwin_modal_blocker)
+ win_list[i++] = client->xwin_modal_blocker;
+ }
+
+ *count = i;
+}
+
+static void
+stack_sync_to_display (MBWindowManager *wm)
+{
+ Window *win_list = NULL;
+ int count;
+
+ if (!wm->stack_n_clients)
+ return;
+
+ /*
+ * Allocate two slots for each client; this guarantees us enough space for
+ * both client windows and any modal blockers without having to keep track
+ * of how many of the blocker windows we have (the memory overhead for this
+ * is negligeable and very short lived)
+ */
+ win_list = alloca (sizeof(Window) * (wm->stack_n_clients * 2));
+
+ stack_get_window_list(wm, win_list, &count);
+
+ mb_wm_util_trap_x_errors();
+ XRestackWindows(wm->xdpy, win_list, count);
+ mb_wm_util_untrap_x_errors();
+}
+
+void
+mb_wm_sync (MBWindowManager *wm)
+{
+ /* Sync all changes to display */
+ MBWindowManagerClient *client = NULL;
+
+ MBWM_MARK();
+ MBWM_TRACE ();
+
+ XGrabServer(wm->xdpy);
+
+ /* First of all, make sure stack is correct */
+ if (wm->sync_type & MBWMSyncStacking)
+ {
+ mb_wm_stack_ensure (wm);
+
+#if ENABLE_COMPOSITE
+ if (wm->comp_mgr && mb_wm_comp_mgr_enabled (wm->comp_mgr))
+ mb_wm_comp_mgr_restack (wm->comp_mgr);
+#endif
+ }
+
+ /* Size stuff first assume newly managed windows unmapped ?
+ *
+ */
+ if (wm->layout && (wm->sync_type & MBWMSyncGeometry))
+ mb_wm_layout_update (wm->layout);
+
+ /* Create the actual windows */
+ mb_wm_stack_enumerate(wm, client)
+ if (!mb_wm_client_is_realized (client))
+ mb_wm_client_realize (client);
+
+ /*
+ * Now do updates per individual client - maps, paints etc, main work here
+ *
+ * If an item in the stack needs visibilty sync, then we have to force it
+ * for all items that are above it on the stack.
+ */
+ mb_wm_stack_enumerate(wm, client)
+ if (mb_wm_client_needs_sync (client))
+ mb_wm_client_display_sync (client);
+
+#if ENABLE_COMPOSITE
+ if (mb_wm_comp_mgr_enabled (wm->comp_mgr))
+ mb_wm_comp_mgr_render (wm->comp_mgr);
+#endif
+
+ /* FIXME: optimise wm sync flags so know if this needs calling */
+ /* FIXME: Can we restack an unmapped window ? - problem of new
+ * clients mapping below existing ones.
+ */
+ if (wm->sync_type & MBWMSyncStacking)
+ stack_sync_to_display (wm);
+
+ /* FIXME: New clients now managed will likely need some propertys
+ * synced up here.
+ */
+
+ XUngrabServer(wm->xdpy);
+
+ wm->sync_type = 0;
+}
+
+static void
+mb_wm_update_root_win_lists (MBWindowManager *wm)
+{
+ Window root_win = wm->root_win->xwindow;
+
+ if (!mb_wm_stack_empty(wm))
+ {
+ Window *wins = NULL;
+ Window *app_wins = NULL;
+ int app_win_cnt = 0;
+ int cnt = 0;
+ int list_size;
+ MBWindowManagerClient *c;
+ MBWMList *l;
+
+ list_size = mb_wm_util_list_length (wm->clients);
+
+ wins = alloca (sizeof(Window) * list_size);
+ app_wins = alloca (sizeof(Window) * list_size);
+
+ if ((wm->flags & MBWindowManagerFlagDesktop) && wm->desktop)
+ {
+ wins[cnt++] = MB_WM_CLIENT_XWIN(wm->desktop);
+ }
+
+ mb_wm_stack_enumerate (wm,c)
+ {
+ if (!(wm->flags & MBWindowManagerFlagDesktop) || c != wm->desktop)
+ wins[cnt++] = c->window->xwindow;
+ }
+
+ /* The MB_APP_WINDOW_LIST_STACKING list is used to construct
+ * application switching menus -- we append anything we have
+ * in client list (some of which might be hidden).
+ * apps)
+ */
+ l = wm->clients;
+ while (l)
+ {
+ c = l->data;
+
+ if (MB_WM_IS_CLIENT_APP (c))
+ app_wins[app_win_cnt++] = c->window->xwindow;
+
+ l = l->next;
+ }
+
+ XChangeProperty(wm->xdpy, root_win,
+ wm->atoms[MBWM_ATOM_NET_CLIENT_LIST_STACKING],
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)wins, cnt);
+
+ XChangeProperty(wm->xdpy, root_win,
+ wm->atoms[MBWM_ATOM_MB_APP_WINDOW_LIST_STACKING],
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)app_wins, app_win_cnt);
+
+ /* Update _NET_CLIENT_LIST but with 'age' order rather than stacking */
+ cnt = 0;
+ l = wm->clients;
+ while (l)
+ {
+ c = l->data;
+ wins[cnt++] = c->window->xwindow;
+
+ l = l->next;
+ }
+
+ XChangeProperty(wm->xdpy, root_win,
+ wm->atoms[MBWM_ATOM_NET_CLIENT_LIST] ,
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)wins, cnt);
+ }
+ else
+ {
+ /* No managed windows */
+ XChangeProperty(wm->xdpy, root_win,
+ wm->atoms[MBWM_ATOM_NET_CLIENT_LIST_STACKING] ,
+ XA_WINDOW, 32, PropModeReplace,
+ NULL, 0);
+
+ XChangeProperty(wm->xdpy, root_win,
+ wm->atoms[MBWM_ATOM_MB_APP_WINDOW_LIST_STACKING],
+ XA_WINDOW, 32, PropModeReplace,
+ NULL, 0);
+
+ XChangeProperty(wm->xdpy, root_win,
+ wm->atoms[MBWM_ATOM_NET_CLIENT_LIST] ,
+ XA_WINDOW, 32, PropModeReplace,
+ NULL, 0);
+ }
+}
+
+void
+mb_wm_manage_client (MBWindowManager *wm,
+ MBWindowManagerClient *client,
+ Bool activate)
+{
+ /* Add to our list of managed clients */
+ MBWMSyncType sync_flags = MBWMSyncVisibility | MBWMSyncGeometry;
+
+ if (client == NULL)
+ return;
+
+ wm->clients = mb_wm_util_list_append(wm->clients, (void*)client);
+
+ /* add to stack and move to position in stack */
+ mb_wm_stack_append_top (client);
+ mb_wm_client_stack(client, 0);
+ mb_wm_update_root_win_lists (wm);
+
+ if (MB_WM_CLIENT_CLIENT_TYPE (client) == MBWMClientTypePanel)
+ {
+ mb_wm_update_root_win_rectangles (wm);
+ mb_wm_client_set_desktop (client, -1);
+ }
+ else if (MB_WM_CLIENT_CLIENT_TYPE (client) == MBWMClientTypeDesktop)
+ {
+ wm->desktop = client;
+ mb_wm_client_set_desktop (client, -1);
+ }
+ else if (client->transient_for)
+ {
+ /*
+ * For transient clients, set the desktop to that of the top level
+ * parent; if this does not match the active desktop, hide the client.
+ */
+ MBWindowManagerClient * parent = client->transient_for;
+ int desktop;
+
+ while (parent->transient_for)
+ parent = parent->transient_for;
+
+ desktop = mb_wm_client_get_desktop (parent);
+
+ mb_wm_client_set_desktop (client, desktop);
+
+ if (desktop != wm->active_desktop)
+ mb_wm_client_desktop_change (client, wm->active_desktop);
+ }
+ else
+ mb_wm_client_set_desktop (client, wm->active_desktop);
+
+ /*
+ * Must not mess with stacking if the client if is of the override type
+ */
+ if (MB_WM_CLIENT_CLIENT_TYPE (client) != MBWMClientTypeOverride)
+ sync_flags |= MBWMSyncStacking;
+
+#if ENABLE_COMPOSITE
+ if (mb_wm_comp_mgr_enabled (wm->comp_mgr))
+ mb_wm_comp_mgr_register_client (wm->comp_mgr, client);
+#endif
+
+ if (activate && MB_WM_CLIENT_CLIENT_TYPE (client) != MBWMClientTypeDesktop)
+ mb_wm_activate_client (wm, client);
+ else
+ mb_wm_client_show (client);
+
+ mb_wm_display_sync_queue (client->wmref, sync_flags);
+}
+
+/*
+ * destroy indicates whether the client, if it is an application,
+ * should be destroyed or moved into the iconized category.
+ */
+void
+mb_wm_unmanage_client (MBWindowManager *wm,
+ MBWindowManagerClient *client,
+ Bool destroy)
+{
+ /* FIXME: set a managed flag in client object ? */
+ MBWindowManagerClient *c;
+ MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client);
+ MBWMSyncType sync_flags = 0;
+ MBWindowManagerClient * next_focused;
+
+ /*
+ * Must not mess with stacking if the client if is of the override type
+ */
+ if (c_type != MBWMClientTypeOverride)
+ sync_flags |= MBWMSyncStacking;
+
+ if (c_type & (MBWMClientTypePanel | MBWMClientTypeInput))
+ sync_flags |= MBWMSyncGeometry;
+
+ if (destroy)
+ wm->clients = mb_wm_util_list_remove (wm->clients, (void*)client);
+
+ mb_wm_stack_remove (client);
+ mb_wm_update_root_win_lists (wm);
+
+ if (MB_WM_CLIENT_CLIENT_TYPE (client) == MBWMClientTypePanel)
+ mb_wm_update_root_win_rectangles (wm);
+
+ /*
+ * Must remove client from any transient list, otherwise when we call
+ * _stack_enumerate() everything will go pearshape
+ */
+ mb_wm_client_detransitise (client);
+
+ next_focused = client->next_focused_client;
+ mb_wm_stack_enumerate (wm, c)
+ {
+ /*
+ * Must avoid circular dependcy here
+ */
+ if (c->next_focused_client == client)
+ {
+ if (c != next_focused)
+ c->next_focused_client = next_focused;
+ else
+ c->next_focused_client = NULL;
+ }
+ }
+
+#if ENABLE_COMPOSITE
+ if (mb_wm_comp_mgr_enabled (wm->comp_mgr))
+ {
+ /*
+ * If destroy == False, this unmap was triggered by iconizing the
+ * client; in that case, we do not destory the CM client data, only
+ * make sure the client is hidden (note that any 'minimize' effect
+ * has already completed by the time we get here).
+ */
+ if (destroy)
+ mb_wm_comp_mgr_unregister_client (wm->comp_mgr, client);
+ else
+ mb_wm_comp_mgr_client_hide (client->cm_client);
+ }
+#endif
+
+ if (wm->focused_client == client)
+ mb_wm_unfocus_client (wm, client);
+
+ if (client == wm->desktop)
+ wm->desktop = NULL;
+
+ if (destroy)
+ mb_wm_object_unref (MB_WM_OBJECT(client));
+
+ mb_wm_display_sync_queue (wm, sync_flags);
+}
+
+MBWindowManagerClient*
+mb_wm_managed_client_from_xwindow(MBWindowManager *wm, Window win)
+{
+ MBWindowManagerClient *client = NULL;
+ MBWMList *l;
+
+ if (win == wm->root_win->xwindow)
+ return NULL;
+
+ l = wm->clients;
+ while (l)
+ {
+ client = l->data;
+
+ if (client->window && client->window->xwindow == win)
+ return client;
+
+ l = l->next;
+ }
+
+ return NULL;
+}
+
+MBWindowManagerClient*
+mb_wm_managed_client_from_frame (MBWindowManager *wm, Window frame)
+{
+ MBWindowManagerClient *client = NULL;
+ MBWMList *l;
+
+ if (frame == wm->root_win->xwindow)
+ return NULL;
+
+ l = wm->clients;
+ while (l)
+ {
+ client = l->data;
+
+ if (mb_wm_client_owns_xwindow (client, frame))
+ return client;
+
+ l = l->next;
+ }
+
+ return NULL;
+}
+
+/*
+ * Run the main loop; there are three options dependent on how we were
+ * configured at build time:
+ *
+ * * If configured without glib main loop integration, we defer to our own
+ * main loop implementation provided by MBWMMainContext.
+ *
+ * * If configured with glib main loop integration:
+ *
+ * * If there is an implemetation for the MBWindowManager main() virtual
+ * function, we call it.
+ *
+ * * Otherwise, start a normal glib main loop.
+ */
+void
+mb_wm_main_loop(MBWindowManager *wm)
+{
+#if !USE_GLIB_MAINLOOP
+ mb_wm_main_context_loop (wm->main_ctx);
+#else
+ MBWindowManagerClass * wm_class =
+ MB_WINDOW_MANAGER_CLASS (MB_WM_OBJECT_GET_CLASS (wm));
+
+#if !ENABLE_CLUTTER_COMPOSITE_MANAGER
+ if (!wm_class->main)
+ {
+ GMainLoop * loop = g_main_loop_new (NULL, FALSE);
+
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+ mb_wm_main_context_gloop_xevent, wm->main_ctx, NULL);
+
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+ }
+ else
+#endif
+ {
+ wm_class->main (wm);
+ }
+#endif
+}
+
+void
+mb_wm_get_display_geometry (MBWindowManager *wm,
+ MBGeometry *geometry)
+{
+ geometry->x = 0;
+ geometry->y = 0;
+ geometry->width = wm->xdpy_width;
+ geometry->height = wm->xdpy_height;
+}
+
+void
+mb_wm_display_sync_queue (MBWindowManager* wm, MBWMSyncType sync)
+{
+ wm->sync_type |= sync;
+}
+
+static void
+mb_wm_manage_preexistsing_wins (MBWindowManager* wm)
+{
+ unsigned int nwins, i;
+ Window foowin1, foowin2, *wins;
+ XWindowAttributes attr;
+ MBWindowManagerClass * wm_class =
+ MB_WINDOW_MANAGER_CLASS (MB_WM_OBJECT_GET_CLASS (wm));
+
+ if (!wm_class->client_new)
+ return;
+
+ XQueryTree(wm->xdpy, wm->root_win->xwindow,
+ &foowin1, &foowin2, &wins, &nwins);
+
+ for (i = 0; i < nwins; i++)
+ {
+ XGetWindowAttributes(wm->xdpy, wins[i], &attr);
+
+ if (
+#if ! ENABLE_COMPOSITE
+ !attr.override_redirect &&
+#endif
+ attr.map_state == IsViewable)
+ {
+ MBWMClientWindow *win = NULL;
+ MBWindowManagerClient *client = NULL;
+
+ win = mb_wm_client_window_new (wm, wins[i]);
+
+ if (!win)
+ continue;
+
+ client = wm_class->client_new (wm, win);
+
+ if (client)
+ {
+ /*
+ * When we realize the client, we reparent the application
+ * window to the new frame, which generates an unmap event.
+ * We need to skip it.
+ */
+ client->skip_unmaps++;
+
+#if ENABLE_COMPOSITE
+ /*
+ * Register the new client with the composite manager before
+ * we call mb_wm_manage_client() -- this is necessary so that
+ * we can process map notification on the frame.
+ */
+ if (wm->comp_mgr && mb_wm_comp_mgr_enabled (wm->comp_mgr))
+ mb_wm_comp_mgr_register_client (wm->comp_mgr, client);
+#endif
+ mb_wm_manage_client(wm, client, False);
+ }
+ else
+ mb_wm_object_unref (MB_WM_OBJECT (win));
+ }
+ }
+
+ XFree(wins);
+}
+
+static void
+mb_wm_real_get_desktop_geometry (MBWindowManager *wm, MBGeometry * geom)
+{
+ MBWindowManagerClient *c;
+ MBGeometry p_geom;
+ MBWMClientLayoutHints hints;
+
+ geom->x = 0;
+ geom->y = 0;
+ geom->width = wm->xdpy_width;
+ geom->height = wm->xdpy_height;
+
+ if (mb_wm_stack_empty(wm))
+ return;
+
+ mb_wm_stack_enumerate(wm, c)
+ {
+ if (MB_WM_CLIENT_CLIENT_TYPE (c) != MBWMClientTypePanel ||
+ ((hints = mb_wm_client_get_layout_hints (c)) & LayoutPrefOverlaps))
+ continue;
+
+ mb_wm_client_get_coverage (c, & p_geom);
+
+ if (LayoutPrefReserveEdgeNorth & hints)
+ geom->y += p_geom.height;
+
+ if (LayoutPrefReserveEdgeSouth & hints)
+ geom->height -= p_geom.height;
+
+ if (LayoutPrefReserveEdgeWest & hints)
+ geom->x += p_geom.width;
+
+ if (LayoutPrefReserveEdgeEast & hints)
+ geom->width -= p_geom.width;
+ }
+}
+
+static void
+mb_wm_get_desktop_geometry (MBWindowManager *wm, MBGeometry * geom)
+{
+ MBWindowManagerClass *wm_class;
+
+ wm_class = (MBWindowManagerClass *) MB_WM_OBJECT_GET_CLASS (wm);
+
+ MBWM_ASSERT (wm_class->get_desktop_geometry);
+
+ wm_class->get_desktop_geometry (wm, geom);
+}
+
+static void
+mb_wm_update_root_win_rectangles (MBWindowManager *wm)
+{
+ Display * dpy = wm->xdpy;
+ Window root = wm->root_win->xwindow;
+ MBGeometry d_geom;
+ CARD32 val[4];
+
+ mb_wm_get_desktop_geometry (wm, &d_geom);
+
+ val[0] = d_geom.x;
+ val[1] = d_geom.y;
+ val[2] = d_geom.width;
+ val[3] = d_geom.height;
+
+ /* FIXME -- handle decorated desktops */
+
+ XChangeProperty(dpy, root, wm->atoms[MBWM_ATOM_NET_WORKAREA],
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *)val, 4);
+
+ val[2] = wm->xdpy_width;
+ val[3] = wm->xdpy_height;
+
+ XChangeProperty(dpy, root, wm->atoms[MBWM_ATOM_NET_DESKTOP_GEOMETRY],
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *)&val[2], 2);
+
+}
+
+int
+mb_wm_register_client_type (void)
+{
+ static int type_cnt = 0;
+ return ++type_cnt;
+}
+
+static int
+mb_wm_init_xdpy (MBWindowManager * wm, const char * display)
+{
+ if (!wm->xdpy)
+ {
+ wm->xdpy = XOpenDisplay(display ? display : getenv("DISPLAY"));
+
+ if (!wm->xdpy)
+ {
+ /* FIXME: Error codes */
+ mb_wm_util_fatal_error("Display connection failed");
+ return 0;
+ }
+ }
+
+ wm->xscreen = DefaultScreen(wm->xdpy);
+ wm->xdpy_width = DisplayWidth(wm->xdpy, wm->xscreen);
+ wm->xdpy_height = DisplayHeight(wm->xdpy, wm->xscreen);
+
+ return 1;
+}
+
+static void
+mb_wm_init_cursors (MBWindowManager * wm)
+{
+ XColor col;
+ Pixmap pix = XCreatePixmap (wm->xdpy, wm->root_win->xwindow, 1, 1, 1);
+
+ memset (&col, 0, sizeof (col));
+
+ wm->cursors[MBWindowManagerCursorNone] =
+ XCreatePixmapCursor (wm->xdpy, pix, pix, &col, &col, 1, 1);
+
+ XFreePixmap (wm->xdpy, pix);
+
+ wm->cursors[MBWindowManagerCursorLeftPtr] =
+ XCreateFontCursor(wm->xdpy, XC_left_ptr);
+
+ MBWM_ASSERT (wm->cursors[_MBWindowManagerCursorLast - 1] != 0);
+
+ mb_wm_set_cursor (wm, MBWindowManagerCursorLeftPtr);
+}
+
+#if ENABLE_COMPOSITE
+static Bool
+mb_wm_init_comp_extensions (MBWindowManager *wm)
+{
+ int event_base, error_base;
+ int damage_error;
+ int xfixes_event, xfixes_error;
+
+ if (!XCompositeQueryExtension (wm->xdpy, &event_base, &error_base))
+ {
+ fprintf (stderr, "matchbox: No composite extension\n");
+ return False;
+ }
+
+ if (!XDamageQueryExtension (wm->xdpy,
+ &wm->damage_event_base, &damage_error))
+ {
+ fprintf (stderr, "matchbox: No damage extension\n");
+ return False;
+ }
+
+ if (!XFixesQueryExtension (wm->xdpy, &xfixes_event, &xfixes_error))
+ {
+ fprintf (stderr, "matchbox: No XFixes extension\n");
+ return False;
+ }
+
+ return True;
+}
+#endif
+
+/*
+ * This function must be called before the MBWindowManager object can be
+ * used.
+ */
+void
+mb_wm_init (MBWindowManager * wm)
+{
+ MBWindowManagerClass *wm_class;
+
+ wm_class = (MBWindowManagerClass *) MB_WM_OBJECT_GET_CLASS (wm);
+
+ mb_wm_set_theme_from_path (wm, wm->theme_path);
+
+ MBWM_ASSERT (wm_class->layout_new);
+
+ mb_wm_set_layout (wm, wm_class->layout_new (wm));
+
+#if ENABLE_COMPOSITE
+ if (wm_class->comp_mgr_new && mb_wm_theme_use_compositing_mgr (wm->theme))
+ mb_wm_compositing_on (wm);
+#endif
+
+ mb_wm_manage_preexistsing_wins (wm);
+
+ /*
+ * Force an initial stack sync even when there are no managed windows (when
+ * using compositor, this triggers call to MBWMCompMgr::restack(), allowing
+ * the CM to set its house into order (i.e., a clutter-based compositor
+ * might want to reorganize any auxiliar actors that it might have, depending
+ * on whether the initial stack is empty or not.
+ */
+ wm->sync_type |= MBWMSyncStacking;
+}
+
+
+static int
+mb_window_manager_init (MBWMObject *this, va_list vap)
+{
+ MBWindowManager *wm = MB_WINDOW_MANAGER (this);
+ MBWindowManagerClass *wm_class;
+ MBWMObjectProp prop;
+ int argc = 0;
+ char **argv = NULL;
+
+ prop = va_arg(vap, MBWMObjectProp);
+ while (prop)
+ {
+ switch (prop)
+ {
+ case MBWMObjectPropArgc:
+ argc = va_arg(vap, int);
+ break;
+ case MBWMObjectPropArgv:
+ argv = va_arg(vap, char **);
+ break;
+ case MBWMObjectPropDpy:
+ wm->xdpy = va_arg(vap, Display *);
+ break;
+ default:
+ MBWMO_PROP_EAT (vap, prop);
+ }
+
+ prop = va_arg(vap, MBWMObjectProp);
+ }
+
+ wm_class = (MBWindowManagerClass *) MB_WM_OBJECT_GET_CLASS (wm);
+
+ wm->argv = argv;
+ wm->argc = argc;
+
+ if (argc && argv && wm_class->process_cmdline)
+ wm_class->process_cmdline (wm);
+
+ if (!mb_wm_init_xdpy (wm, NULL))
+ return 0;
+
+ if (getenv("MB_SYNC"))
+ XSynchronize (wm->xdpy, True);
+
+ mb_wm_debug_init (getenv("MB_DEBUG"));
+
+ /* FIXME: Multiple screen handling */
+
+ wm->xas_context = xas_context_new(wm->xdpy);
+
+ mb_wm_atoms_init(wm);
+
+#if ENABLE_COMPOSITE
+ if (!mb_wm_init_comp_extensions (wm))
+ return 0;
+#endif
+
+ wm->root_win = mb_wm_root_window_get (wm);
+
+ mb_wm_update_root_win_rectangles (wm);
+
+ wm->main_ctx = mb_wm_main_context_new (wm);
+
+ mb_wm_main_context_x_event_handler_add (wm->main_ctx,
+ None,
+ MapRequest,
+ (MBWMXEventFunc)mb_wm_handle_map_request,
+ wm);
+
+#if ENABLE_COMPOSITE
+ mb_wm_main_context_x_event_handler_add (wm->main_ctx,
+ None,
+ MapNotify,
+ (MBWMXEventFunc)mb_wm_handle_map_notify,
+ wm);
+
+ mb_wm_main_context_x_event_handler_add (wm->main_ctx,
+ None,
+ ConfigureNotify,
+ (MBWMXEventFunc)mb_wm_handle_composite_config_notify,
+ wm);
+#endif
+
+ mb_wm_main_context_x_event_handler_add (wm->main_ctx,
+ wm->root_win->xwindow,
+ ConfigureNotify,
+ (MBWMXEventFunc)mb_wm_handle_root_config_notify,
+ wm);
+
+ mb_wm_main_context_x_event_handler_add (wm->main_ctx,
+ None,
+ ConfigureRequest,
+ (MBWMXEventFunc)mb_wm_handle_config_request,
+ wm);
+
+ mb_wm_main_context_x_event_handler_add (wm->main_ctx,
+ None,
+ PropertyNotify,
+ (MBWMXEventFunc)mb_wm_handle_property_notify,
+ wm);
+
+ mb_wm_main_context_x_event_handler_add (wm->main_ctx,
+ None,
+ DestroyNotify,
+ (MBWMXEventFunc)mb_wm_handle_destroy_notify,
+ wm);
+
+ mb_wm_main_context_x_event_handler_add (wm->main_ctx,
+ None,
+ UnmapNotify,
+ (MBWMXEventFunc)mb_wm_handle_unmap_notify,
+ wm);
+
+ mb_wm_main_context_x_event_handler_add (wm->main_ctx,
+ None,
+ KeyPress,
+ (MBWMXEventFunc)mb_wm_handle_key_press,
+ wm);
+
+ mb_wm_main_context_x_event_handler_add (wm->main_ctx,
+ None,
+ ButtonPress,
+ (MBWMXEventFunc)mb_wm_handle_button_press,
+ wm);
+
+ mb_wm_keys_init(wm);
+
+ mb_wm_init_cursors (wm);
+
+ base_foo ();
+
+ return 1;
+}
+
+static void
+mb_wm_cmdline_help (const char *arg0, Bool quit)
+{
+ FILE * f = stdout;
+ const char * name;
+ char * p = strrchr (arg0, '/');
+
+ if (p)
+ name = p+1;
+ else
+ name = arg0;
+
+ fprintf (f, "\nThis is Matchbox Window Manager 2.\n");
+
+ fprintf (f, "\nUsage: %s [options]\n\n", name);
+ fprintf (f, "Options:\n");
+ fprintf (f, " -display display : X display to connect to (alternatively, display\n"
+ " can be specified using the DISPLAY environment\n"
+ " variable).\n");
+ fprintf (f, " -sm-client-id id : Session id.\n");
+ fprintf (f, " -theme-always-reload : Reload theme even if it matches the currently\n"
+ " loaded theme.\n");
+ fprintf (f, " -theme theme : Load the specified theme\n");
+
+ if (quit)
+ exit (0);
+}
+
+static void
+mb_wm_process_cmdline (MBWindowManager *wm)
+{
+ int i;
+ char ** argv = wm->argv;
+ int argc = wm->argc;
+
+ for (i = 0; i < argc; ++i)
+ {
+ if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "--help"))
+ {
+ mb_wm_cmdline_help (argv[0], True);
+ }
+ else if (!strcmp(argv[i], "-theme-always-reload"))
+ {
+ wm->flags |= MBWindowManagerFlagAlwaysReloadTheme;
+ }
+ else if (i < argc - 1)
+ {
+ /* These need to have a value after the name parameter */
+ if (!strcmp(argv[i], "-display"))
+ {
+ mb_wm_init_xdpy (wm, argv[++i]);
+ }
+ else if (!strcmp ("-sm-client-id", argv[i]))
+ {
+ wm->sm_client_id = argv[++i];
+ }
+ else if (!strcmp ("-theme", argv[i]))
+ {
+ wm->theme_path = argv[++i];
+ }
+ }
+ }
+
+ /*
+ * Anything below here needs a display conection
+ */
+ if (!wm->xdpy && !mb_wm_init_xdpy (wm, NULL))
+ return;
+}
+
+void
+mb_wm_activate_client (MBWindowManager * wm, MBWindowManagerClient *c)
+{
+ MBWindowManagerClass *wm_klass;
+ wm_klass = MB_WINDOW_MANAGER_CLASS (MB_WM_OBJECT_GET_CLASS (wm));
+
+ MBWM_ASSERT (wm_klass->client_activate);
+
+ wm_klass->client_activate (wm, c);
+}
+
+
+static Bool
+mb_wm_activate_client_real (MBWindowManager * wm, MBWindowManagerClient *c)
+{
+ MBWMClientType c_type;
+ Bool was_desktop;
+ Bool is_desktop;
+ MBWindowManagerClient * c_focus = c;
+ MBWindowManagerClient * trans;
+
+ if (c == NULL)
+ return False;
+
+ c_type = MB_WM_CLIENT_CLIENT_TYPE (c);
+
+ /*
+ * Under no circumtances attempt to activate override windows; only call
+ * show on them.
+ */
+ if (c_type == MBWMClientTypeOverride)
+ {
+ mb_wm_client_show (c);
+ return True;
+ }
+
+ was_desktop = (wm->flags & MBWindowManagerFlagDesktop);
+
+ /*
+ * We are showing desktop if either the client is desktop, it is transient
+ * for the desktop, or the last client was desktop, and the current is a
+ * dialog or menu transiet for root.
+ */
+ is_desktop = ((MB_WM_CLIENT_CLIENT_TYPE (c) == MBWMClientTypeDesktop) ||
+ ((trans = mb_wm_client_get_transient_for (c)) == c) ||
+ (was_desktop &&
+ !trans &&
+ (c_type & (MBWMClientTypeDialog|
+ MBWMClientTypeMenu|
+ MBWMClientTypeNote|
+ MBWMClientTypeOverride))));
+
+ if (is_desktop)
+ wm->flags |= MBWindowManagerFlagDesktop;
+ else
+ wm->flags &= ~MBWindowManagerFlagDesktop;
+
+ mb_wm_client_show (c);
+
+ /* If the next focused client after this one is transient for it,
+ * activate it instead
+ */
+ if (c->last_focused_transient &&
+ c->last_focused_transient->transient_for == c)
+ {
+ c_focus = c->last_focused_transient;
+ }
+
+ mb_wm_focus_client (wm, c_focus);
+ mb_wm_client_stack (c, 0);
+
+ if (is_desktop != was_desktop)
+ {
+ CARD32 card = is_desktop ? 1 : 0;
+
+ XChangeProperty(wm->xdpy, wm->root_win->xwindow,
+ wm->atoms[MBWM_ATOM_NET_SHOWING_DESKTOP],
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *)&card, 1);
+ }
+
+ if (is_desktop || c_type == MBWMClientTypeApp)
+ {
+ XChangeProperty(wm->xdpy, wm->root_win->xwindow,
+ wm->atoms[MBWM_ATOM_MB_CURRENT_APP_WINDOW],
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)&c->window->xwindow, 1);
+ }
+
+ mb_wm_display_sync_queue (wm, MBWMSyncStacking | MBWMSyncVisibility);
+
+ return True;
+}
+
+MBWindowManagerClient*
+mb_wm_get_visible_main_client(MBWindowManager *wm)
+{
+ if ((wm->flags & MBWindowManagerFlagDesktop) && wm->desktop)
+ return wm->desktop;
+
+ return mb_wm_stack_get_highest_by_type (wm, MBWMClientTypeApp);
+}
+
+void
+mb_wm_handle_ping_reply (MBWindowManager * wm, MBWindowManagerClient *c)
+{
+ if (c == NULL)
+ return;
+
+ if (mb_wm_client_ping_in_progress (c))
+ {
+ MBWindowManagerClass *wm_klass;
+
+ mb_wm_client_ping_stop (c);
+
+ wm_klass = MB_WINDOW_MANAGER_CLASS (MB_WM_OBJECT_GET_CLASS (wm));
+
+ if (wm_klass->client_responding)
+ wm_klass->client_responding (wm, c);
+ }
+}
+
+void
+mb_wm_handle_hang_client (MBWindowManager * wm, MBWindowManagerClient *c)
+{
+ MBWindowManagerClass *wm_klass;
+
+ if (c == NULL)
+ return;
+
+ wm_klass = MB_WINDOW_MANAGER_CLASS (MB_WM_OBJECT_GET_CLASS (wm));
+
+ if (!wm_klass->client_hang || !wm_klass->client_hang (wm, c))
+ {
+ mb_wm_client_shutdown (c);
+ }
+}
+
+void
+mb_wm_toggle_desktop (MBWindowManager * wm)
+{
+ Bool show = !(wm->flags & MBWindowManagerFlagDesktop);
+ mb_wm_handle_show_desktop (wm, show);
+}
+
+void
+mb_wm_handle_show_desktop (MBWindowManager * wm, Bool show)
+{
+ if (!wm->desktop)
+ return;
+
+ if (show)
+ mb_wm_activate_client (wm, wm->desktop);
+ else
+ {
+ MBWindowManagerClient * c =
+ mb_wm_stack_get_highest_by_type (wm, MBWMClientTypeApp);
+
+ if (c)
+ mb_wm_activate_client (wm, c);
+ }
+}
+
+void
+mb_wm_set_layout (MBWindowManager *wm, MBWMLayout *layout)
+{
+ wm->layout = layout;
+ wm->sync_type |= (MBWMSyncGeometry | MBWMSyncVisibility);
+}
+
+static void
+mb_wm_focus_client (MBWindowManager *wm, MBWindowManagerClient *c)
+{
+ MBWindowManagerClient * client = c;
+
+ /*
+ * The last focused transient for this client is modal, we try to focus
+ * the transient rather than the client itself
+ */
+ if (c->last_focused_transient &&
+ mb_wm_client_is_modal (c->last_focused_transient))
+ {
+ client = c->last_focused_transient;
+ }
+
+ /*
+ * If the client is currently focused, it does not want focus, it is a
+ * parent of a currently focused modal client, or is system-modal,
+ * do nothing.
+ */
+ if (wm->focused_client == client ||
+ !mb_wm_client_want_focus (client) ||
+ ((wm->focused_client && mb_wm_client_is_modal (wm->focused_client) &&
+ (client == mb_wm_client_get_transient_for (wm->focused_client) ||
+ (wm->modality_type == MBWMModalitySystem &&
+ !mb_wm_client_get_transient_for (wm->focused_client))))))
+ return;
+
+ if (!mb_wm_client_is_realized (client))
+ {
+ /* We need the window mapped before we can focus it, but do not
+ * want to do a full-scale mb_wm_sync ():
+ *
+ * First We need to update layout, othewise the window will get frame
+ * size of 0x0; then we can realize it, and do a display sync (i.e.,
+ * map).
+ */
+ if (wm->layout)
+ mb_wm_layout_update (wm->layout);
+
+ mb_wm_client_realize (client);
+ mb_wm_client_display_sync (client);
+ }
+
+ if (mb_wm_client_focus (client))
+ {
+ if (wm->focused_client)
+ {
+ MBWindowManagerClient *trans_old = wm->focused_client;
+ MBWindowManagerClient *trans_new = client;
+
+ while (trans_old->transient_for)
+ trans_old = trans_old->transient_for;
+
+ while (trans_new->transient_for)
+ trans_new = trans_new->transient_for;
+
+ client->next_focused_client = NULL;
+
+ /*
+ * Are we both transient for the same thing ?
+ */
+ if (trans_new && trans_new == trans_old)
+ client->next_focused_client = wm->focused_client;
+
+ /* From regular dialog to transient for root dialogs */
+ if (MB_WM_IS_CLIENT_DIALOG (client) &&
+ !client->transient_for &&
+ MB_WM_IS_CLIENT_DIALOG (wm->focused_client))
+ client->next_focused_client = wm->focused_client;
+ }
+
+ wm->focused_client = client;
+ }
+}
+
+void
+mb_wm_unfocus_client (MBWindowManager *wm, MBWindowManagerClient *client)
+{
+ MBWindowManagerClient *next = NULL;
+
+ if (client != wm->focused_client)
+ return;
+
+ /*
+ * Remove this client from any other's next_focused_client
+ */
+ next = client->next_focused_client;
+
+ if (!next && wm->stack_top)
+ {
+ MBWindowManagerClient *c;
+
+ mb_wm_stack_enumerate_reverse (wm, c)
+ {
+ if (c != client && mb_wm_client_want_focus (c))
+ {
+ next = c;
+ break;
+ }
+ }
+ }
+
+ wm->focused_client = NULL;
+
+ if (next)
+ mb_wm_activate_client (wm, next);
+}
+
+void
+mb_wm_cycle_apps (MBWindowManager *wm, Bool reverse)
+{
+ MBWindowManagerClient *old_top, *new_top;
+
+ if (wm->flags & MBWindowManagerFlagDesktop)
+ {
+ mb_wm_handle_show_desktop (wm, False);
+ return;
+ }
+
+ old_top = mb_wm_stack_get_highest_by_type (wm, MBWMClientTypeApp);
+
+ new_top = mb_wm_stack_cycle_by_type(wm, MBWMClientTypeApp, reverse);
+
+ if (new_top && old_top != new_top)
+ {
+#if ENABLE_COMPOSITE
+ if (wm->comp_mgr && mb_wm_comp_mgr_enabled (wm->comp_mgr))
+ mb_wm_comp_mgr_do_transition (wm->comp_mgr, old_top, new_top, reverse);
+#endif
+ mb_wm_activate_client (wm, new_top);
+ }
+}
+
+void
+mb_wm_set_theme (MBWindowManager *wm, MBWMTheme * theme)
+{
+ if (!theme)
+ return;
+
+ XGrabServer(wm->xdpy);
+
+ if (wm->theme)
+ mb_wm_object_unref (MB_WM_OBJECT (wm->theme));
+
+ wm->theme = theme;
+ wm->sync_type |= (MBWMSyncGeometry | MBWMSyncVisibility | MBWMSyncDecor);
+
+ /* When initializing the MBWindowManager object, the theme gets created
+ * before the root window, so that the root window can interogate it,
+ * so we can get here before the window is in place
+ */
+ if (wm->root_win)
+ mb_wm_root_window_update_supported_props (wm->root_win);
+
+ mb_wm_object_signal_emit (MB_WM_OBJECT (wm),
+ MBWindowManagerSignalThemeChange);
+
+
+ XUngrabServer(wm->xdpy);
+}
+
+void
+mb_wm_set_theme_from_path (MBWindowManager *wm, const char *theme_path)
+{
+ MBWMTheme *theme;
+ MBWindowManagerClass *wm_class;
+
+ wm_class = MB_WINDOW_MANAGER_CLASS (MB_WM_OBJECT_GET_CLASS (wm));
+
+ if (wm->theme)
+ {
+ if (!(wm->flags & MBWindowManagerFlagAlwaysReloadTheme) &&
+ (wm->theme->path && theme_path &&
+ !strcmp (theme_path, wm->theme->path)))
+ return;
+
+ if (!wm->theme->path && !theme_path)
+ return;
+ }
+
+ theme = wm_class->theme_new (wm, theme_path);
+
+ mb_wm_set_theme (wm, theme);
+}
+
+void
+mb_wm_set_cursor (MBWindowManager * wm, MBWindowManagerCursor cursor)
+{
+ static int major = 0, minor = 0, ev_base, err_base;
+ Display * dpy;
+ Window rwin;
+
+ if (wm->cursor == cursor)
+ return;
+
+ dpy = wm->xdpy;
+ rwin = wm->root_win->xwindow;
+
+ mb_wm_util_trap_x_errors();
+
+#ifdef HAVE_XFIXES
+ if (!major)
+ {
+ if (XFixesQueryExtension (dpy, &ev_base, &err_base))
+ XFixesQueryVersion (dpy, &major, &minor);
+ else
+ major = -1;
+ }
+
+ if (major >= 4)
+ {
+ if (cursor == MBWindowManagerCursorNone)
+ {
+ XFixesHideCursor (dpy, rwin);
+ }
+ else
+ {
+ XDefineCursor(dpy, rwin, wm->cursors[cursor]);
+ XFixesShowCursor (dpy, rwin);
+ mb_wm_util_trap_x_errors();
+ }
+ }
+ else
+#endif
+ {
+ XDefineCursor(dpy, rwin, wm->cursors[cursor]);
+ }
+
+ XSync (dpy, False);
+
+ if (!mb_wm_util_untrap_x_errors())
+ wm->cursor = cursor;
+}
+
+void
+mb_wm_compositing_on (MBWindowManager * wm)
+{
+#if ENABLE_COMPOSITE
+ MBWindowManagerClass *wm_class =
+ MB_WINDOW_MANAGER_CLASS (MB_WM_OBJECT_GET_CLASS (wm));
+
+ if (!wm->comp_mgr && wm_class->comp_mgr_new)
+ wm->comp_mgr = wm_class->comp_mgr_new (wm);
+
+ if (wm->comp_mgr && !mb_wm_comp_mgr_enabled (wm->comp_mgr))
+ {
+ mb_wm_comp_mgr_turn_on (wm->comp_mgr);
+ XSync (wm->xdpy, False);
+ }
+#endif
+}
+
+
+void
+mb_wm_compositing_off (MBWindowManager * wm)
+{
+#if ENABLE_COMPOSITE
+ if (wm->comp_mgr && mb_wm_comp_mgr_enabled (wm->comp_mgr))
+ mb_wm_comp_mgr_turn_off (wm->comp_mgr);
+#endif
+}
+
+Bool
+mb_wm_compositing_enabled (MBWindowManager * wm)
+{
+#if ENABLE_COMPOSITE
+ return mb_wm_comp_mgr_enabled (wm->comp_mgr);
+#else
+ return False;
+#endif
+}
+
+MBWMModality
+mb_wm_get_modality_type (MBWindowManager * wm)
+{
+ return wm->modality_type;
+}
+
+void
+mb_wm_set_n_desktops (MBWindowManager *wm, int n_desktops)
+{
+ CARD32 card32 = n_desktops;
+
+ wm->n_desktops = n_desktops;
+
+ XChangeProperty(wm->xdpy, wm->root_win->xwindow,
+ wm->atoms[MBWM_ATOM_NET_NUMBER_OF_DESKTOPS],
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *)&card32, 1);
+
+ /* FIXME -- deal with the case where the number is shrinking */
+}
+
+
+void
+mb_wm_select_desktop (MBWindowManager *wm, int desktop)
+{
+ CARD32 card32 = desktop;
+ MBWindowManagerClient *c;
+ int old_desktop;
+
+ if (desktop == wm->active_desktop)
+ return;
+
+ old_desktop = wm->active_desktop;
+
+ wm->active_desktop = desktop;
+
+ if (desktop >= wm->n_desktops)
+ {
+ mb_wm_set_n_desktops (wm, desktop + 1);
+ }
+
+ mb_wm_stack_enumerate (wm, c)
+ mb_wm_client_desktop_change (c, desktop);
+
+ XChangeProperty(wm->xdpy, wm->root_win->xwindow,
+ wm->atoms[MBWM_ATOM_NET_CURRENT_DESKTOP],
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *)&card32, 1);
+
+#if ENABLE_COMPOSITE
+ if (mb_wm_compositing_enabled (wm))
+ mb_wm_comp_mgr_select_desktop (wm->comp_mgr, desktop, old_desktop);
+#endif
+}
+