diff options
Diffstat (limited to 'matchbox')
68 files changed, 25462 insertions, 0 deletions
diff --git a/matchbox/Makefile.am b/matchbox/Makefile.am new file mode 100644 index 0000000..f864097 --- /dev/null +++ b/matchbox/Makefile.am @@ -0,0 +1,100 @@ + +lib_LTLIBRARIES = libmatchbox-@MBWM_API_VERSION@.la + +source_public_h = \ + matchbox.h + +# Core... +source = \ + mb-wm-types.h \ + mb-wm-macros.h \ + mb-wm-object.h \ + mb-wm-object-props.h \ + mb-wm-object.c \ + mb-wm-debug.h \ + mb-wm-debug.c \ + mb-wm-client.h \ + mb-wm-client.c \ + mb-wm-client-base.h \ + mb-wm-client-base.c \ + mb-wm-client-window.h \ + mb-wm-client-window.c \ + mb-wm-root-window.h \ + mb-wm-root-window.c \ + mb-wm-stack.h \ + mb-wm-stack.c \ + mb-wm-props.h \ + mb-wm-props.c \ + mb-wm-util.h \ + mb-wm-util.c \ + mb-wm-atoms.h \ + mb-wm-atoms.c \ + mb-wm-layout.h \ + mb-wm-layout.c \ + mb-wm-keys.h \ + mb-wm-keys.c \ + mb-wm-decor.h \ + mb-wm-decor.c \ + mb-window-manager.h \ + mb-window-manager.c \ + mb-wm-main-context.h \ + mb-wm-main-context.c \ + xas.h \ + xas.c + +# Client window types... +source += \ + mb-wm-client-app.h \ + mb-wm-client-app.c \ + mb-wm-client-panel.h \ + mb-wm-client-panel.c \ + mb-wm-client-dialog.h \ + mb-wm-client-dialog.c \ + mb-wm-client-note.h \ + mb-wm-client-note.c \ + mb-wm-client-input.h \ + mb-wm-client-input.c \ + mb-wm-client-desktop.h \ + mb-wm-client-desktop.c \ + mb-wm-client-menu.h \ + mb-wm-client-menu.c \ + mb-wm-client-override.h \ + mb-wm-client-override.c + + +# Compositing... +if ENABLE_COMPOSITE +source += mb-wm-comp-mgr.h +source += mb-wm-comp-mgr.c + +if COMP_MGR_BACKEND +if ENABLE_CLUTTER_COMPOSITE_MANAGER +source += mb-wm-comp-mgr-clutter.h +source += mb-wm-comp-mgr-clutter.c +source += tidy/tidy-texture-frame.h tidy/tidy-texture-frame.c +else +source += mb-wm-comp-mgr-xrender.h +source += mb-wm-comp-mgr-xrender.c +endif +endif + +endif #ENABLE_COMPOSITE + +# Theming... +source += \ + mb-wm-theme.h \ + mb-wm-theme.c \ + mb-wm-theme-xml.h \ + mb-wm-theme-xml.c + +if THEME_PNG +source += mb-wm-theme-png.c mb-wm-theme-png.h +endif + +pkgincludedir = $(includedir)/matchbox-$(MBWM_API_VERSION)/matchbox +pkginclude_HEADERS = $(source_public_h) + +libmatchbox_@MBWM_API_VERSION@_la_SOURCES = $(source_public_h) $(source) +libmatchbox_@MBWM_API_VERSION@_la_CFLAGS = $(MBWM_INCS) $(MBWM_CFLAGS) -DDATADIR=\"$(datadir)\" +libmatchbox_@MBWM_API_VERSION@_la_LIBADD = $(MBWM_LIBS) + diff --git a/matchbox/matchbox.h b/matchbox/matchbox.h new file mode 100644 index 0000000..f59d0e7 --- /dev/null +++ b/matchbox/matchbox.h @@ -0,0 +1,70 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_WM_H +#define _HAVE_MB_WM_H + +#define _GNU_SOURCE /* For vasprintf */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> /* for XA_ATOM etc */ +#include <X11/keysym.h> /* key mask defines */ + +#include <matchbox/mb-wm-config.h> +#include <matchbox/xas.h> /* async stuff not needed for xlib on xcb */ + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#include <matchbox/mb-wm-macros.h> +#include <matchbox/mb-wm-debug.h> +#include <matchbox/mb-wm-types.h> +#include <matchbox/mb-wm-util.h> +#include <matchbox/mb-wm-object.h> +#include <matchbox/mb-wm-atoms.h> +#include <matchbox/mb-wm-props.h> +#include <matchbox/mb-wm-keys.h> +#include <matchbox/mb-wm-decor.h> +#include <matchbox/mb-wm-client-window.h> +#include <matchbox/mb-wm-root-window.h> +#include <matchbox/mb-wm-client.h> +#include <matchbox/mb-wm-client-base.h> +#include <matchbox/mb-wm-client-app.h> +#include <matchbox/mb-wm-client-desktop.h> +#include <matchbox/mb-wm-client-dialog.h> +#include <matchbox/mb-wm-client-input.h> +#include <matchbox/mb-wm-client-menu.h> +#include <matchbox/mb-wm-client-note.h> +#include <matchbox/mb-wm-client-override.h> +#include <matchbox/mb-wm-client-panel.h> +#include <matchbox/mb-wm-layout.h> +#include <matchbox/mb-wm-stack.h> +#include <matchbox/mb-window-manager.h> +#include <matchbox/mb-wm-main-context.h> +#endif diff --git a/matchbox/mb-window-manager.c b/matchbox/mb-window-manager.c new file mode 100644 index 0000000..7d1a201 --- /dev/null +++ b/matchbox/mb-window-manager.c @@ -0,0 +1,2271 @@ +/* + * 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 "matchbox.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 +} + +int +mb_wm_util_pixels_to_points (MBWindowManager *wm, int pixels) +{ + static double scale = 0.0; /* Points per pixel */ + int points; + + if (scale == 0.0) + { + scale = + ((double)DisplayHeightMM (wm->xdpy, wm->xscreen) * 2.8346456693) + / (double) DisplayHeight(wm->xdpy, wm->xscreen); + } + + /* Scale and round */ + points = (((int)((double)(pixels << 10) * scale) + 512) >> 10); + + return points; +} + diff --git a/matchbox/mb-window-manager.h b/matchbox/mb-window-manager.h new file mode 100644 index 0000000..5c72845 --- /dev/null +++ b/matchbox/mb-window-manager.h @@ -0,0 +1,258 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005, 2007 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. + * + */ + +#ifndef _HAVE_MB_WM_WINDOW_MANAGER_H +#define _HAVE_MB_WM_WINDOW_MANAGER_H + +#include <matchbox/mb-wm-config.h> +#include <matchbox/mb-wm-object.h> +#include <matchbox/mb-wm-keys.h> +#include <matchbox/mb-wm-root-window.h> +#include <matchbox/mb-wm-theme.h> +#include <matchbox/mb-wm-layout.h> +#include <matchbox/mb-wm-main-context.h> +#if ENABLE_COMPOSITE +#include <matchbox/mb-wm-comp-mgr.h> +#endif +#include <matchbox/xas.h> + +/* XXX: we have a circular dependency between mb-wm-main-context.h + * and mb-window-manager.h */ +#ifndef MB_WM_MAIN_CONTEXT_TYPEDEF_DEFINED +typedef struct MBWMMainContext MBWMMainContext; +#define MB_WM_MAIN_CONTEXT_TYPEDEF_DEFINED +#endif + +#define MB_WINDOW_MANAGER(c) ((MBWindowManager*)(c)) +#define MB_WINDOW_MANAGER_CLASS(c) ((MBWindowManagerClass*)(c)) +#define MB_TYPE_WINDOW_MANAGER (mb_wm_class_type ()) + +typedef struct MBWindowManagerClass MBWindowManagerClass; +typedef struct MBWindowManagerPriv MBWindowManagerPriv; + +typedef enum MBWindowManagerFlag +{ + MBWindowManagerFlagDesktop = (1<<0), + MBWindowManagerFlagAlwaysReloadTheme = (1<<1), +} MBWindowManagerFlag; + +typedef enum +{ + MBWindowManagerSignalThemeChange = 1, +} MBWindowManagerSingal; + +typedef enum +{ + MBWindowManagerCursorNone = 0, + MBWindowManagerCursorLeftPtr, + + _MBWindowManagerCursorLast +} MBWindowManagerCursor; + + +struct MBWindowManager +{ + MBWMObject parent; + + Display *xdpy; + unsigned int xdpy_width, xdpy_height; + int xscreen; + + MBWindowManagerClient *stack_top, *stack_bottom; + MBWMList *clients; + MBWindowManagerClient *desktop; + MBWindowManagerClient *focused_client; + + int n_desktops; + int active_desktop; + + Atom atoms[MBWM_ATOM_COUNT]; + + MBWMKeys *keys; /* Keybindings etc */ + + XasContext *xas_context; + + /* ### Private ### */ + MBWMSyncType sync_type; + int client_type_cnt; + int stack_n_clients; + MBWMRootWindow *root_win; + + const char *sm_client_id; + + MBWMTheme *theme; + MBWMLayout *layout; + MBWMMainContext *main_ctx; + MBWindowManagerFlag flags; +#if ENABLE_COMPOSITE + MBWMCompMgr *comp_mgr; + int damage_event_base; +#endif + + MBWindowManagerCursor cursor; + Cursor cursors[_MBWindowManagerCursorLast]; + + /* Temporary stuff, only valid during object initialization */ + const char *theme_path; + + MBWMModality modality_type; + + char **argv; + int argc; +}; + +struct MBWindowManagerClass +{ + MBWMObjectClass parent; + + void (*process_cmdline) (MBWindowManager * wm); + + MBWindowManagerClient* (*client_new) (MBWindowManager *wm, + MBWMClientWindow *w); + MBWMLayout * (*layout_new) (MBWindowManager *wm); + + /* These return True if now further action to be taken */ + Bool (*client_activate) (MBWindowManager *wm, MBWindowManagerClient *c); + Bool (*client_responding) (MBWindowManager *wm, MBWindowManagerClient *c); + Bool (*client_hang) (MBWindowManager *wm, MBWindowManagerClient *c); + + MBWMTheme * (*theme_new) (MBWindowManager *wm, const char * path); + +#if ENABLE_COMPOSITE + MBWMCompMgr * (*comp_mgr_new) (MBWindowManager *wm); +#endif + + void (*get_desktop_geometry) (MBWindowManager *wm, MBGeometry *geom); + + void (*main) (MBWindowManager *wm); +}; + +/** + * SECTION:matchbox2 + * @short_description: Core functions for creating and manipulating a + * #MBWindowManager + * + * The core object of Matchbox2 is the #MBWindowManager object, which + * is the first thing you would instantiate when implementing a new + * window manager. + */ + +MBWindowManager * +mb_wm_new (int argc, char **argv); + +MBWindowManager * +mb_wm_new_with_dpy (int argc, char **argv, Display * dpy); + +void +mb_wm_init (MBWindowManager * wm); + +void +mb_wm_set_layout (MBWindowManager *wm, MBWMLayout *layout); + +int +mb_wm_class_type (); + +void +mb_wm_main_loop(MBWindowManager *wm); + +MBWindowManagerClient* +mb_wm_managed_client_from_xwindow(MBWindowManager *wm, Window win); + +MBWindowManagerClient* +mb_wm_managed_client_from_frame (MBWindowManager *wm, Window frame); + +int +mb_wm_register_client_type (void); + +void +mb_wm_manage_client (MBWindowManager *wm, + MBWindowManagerClient *client, + Bool activate); + +void +mb_wm_unmanage_client (MBWindowManager *wm, + MBWindowManagerClient *client, + Bool destroy); + +void +mb_wm_display_sync_queue (MBWindowManager* wm, MBWMSyncType sync); + +void +mb_wm_get_display_geometry (MBWindowManager *wm, + MBGeometry *geometry); + +void +mb_wm_activate_client(MBWindowManager * wm, MBWindowManagerClient *c); + +void +mb_wm_handle_ping_reply (MBWindowManager * wm, MBWindowManagerClient *c); + +void +mb_wm_handle_hang_client (MBWindowManager * wm, MBWindowManagerClient *c); + +void +mb_wm_handle_show_desktop (MBWindowManager * wm, Bool show); + +void +mb_wm_toggle_desktop (MBWindowManager * wm); + +MBWindowManagerClient* +mb_wm_get_visible_main_client(MBWindowManager *wm); + +void +mb_wm_unfocus_client (MBWindowManager *wm, MBWindowManagerClient *client); + +void +mb_wm_cycle_apps (MBWindowManager *wm, Bool reverse); + +void +mb_wm_set_theme (MBWindowManager *wm, MBWMTheme * theme); + +void +mb_wm_set_theme_from_path (MBWindowManager *wm, const char *theme_path); + +void +mb_wm_set_cursor (MBWindowManager * wm, MBWindowManagerCursor cursor); + +void +mb_wm_compositing_on (MBWindowManager * wm); + +void +mb_wm_compositing_off (MBWindowManager * wm); + +Bool +mb_wm_compositing_enabled (MBWindowManager * wm); + +MBWMModality +mb_wm_get_modality_type (MBWindowManager * wm); + +void +mb_wm_sync (MBWindowManager *wm); + +void +mb_wm_set_n_desktops (MBWindowManager *wm, int n_desktops); + +void +mb_wm_select_desktop (MBWindowManager *wm, int desktop); + +int +mb_wm_util_pixels_to_points (MBWindowManager *wm, int pixels); + +#endif diff --git a/matchbox/mb-wm-atoms.c b/matchbox/mb-wm-atoms.c new file mode 100644 index 0000000..4b270a8 --- /dev/null +++ b/matchbox/mb-wm-atoms.c @@ -0,0 +1,116 @@ +#include "matchbox.h" + +void +mb_wm_atoms_init(MBWindowManager *wm) +{ + /* + * The list below *MUST* be kept in the same order as the corresponding + * emun in structs.h or *everything* will break. + * Doing it like this avoids a mass of round trips on startup. + */ + + char *atom_names[] = { + + "WM_NAME", + "WM_STATE", + "WM_HINTS", + "WM_NORMAL_HINTS", + "WM_CHANGE_STATE", + "WM_PROTOCOLS", + "WM_DELETE_WINDOW", + "WM_COLORMAP_WINDOWS", + "WM_CLIENT_MACHINE", + "WM_TRANSIENT_FOR", + "WM_TAKE_FOCUS", + + "_NET_WM_WINDOW_TYPE", + "_NET_WM_WINDOW_TYPE_NORMAL", + "_NET_WM_WINDOW_TYPE_TOOLBAR", + "_NET_WM_WINDOW_TYPE_INPUT", + "_NET_WM_WINDOW_TYPE_DOCK", + "_NET_WM_WINDOW_TYPE_MENU", + "_NET_WM_WINDOW_TYPE_POPUP_MENU", + "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", + "_NET_WM_WINDOW_TYPE_DIALOG", + "_NET_WM_WINDOW_TYPE_SPLASH", + "_NET_WM_WINDOW_TYPE_DESKTOP", + "_NET_WM_WINDOW_TYPE_NOTIFICATION", + + "_NET_WM_STATE", + "_NET_WM_STATE_FULLSCREEN", + "_NET_WM_STATE_MODAL", + "_NET_WM_STATE_ABOVE", + "_NET_WM_STATE_STICKY", + "_NET_WM_STATE_MAXIMIZED_VERT", + "_NET_WM_STATE_MAXIMIZED_HORZ", + "_NET_WM_STATE_SHADED", + "_NET_WM_STATE_SKIP_TASKBAR", + "_NET_WM_STATE_SKIP_PAGER", + "_NET_WM_STATE_HIDDEN", + "_NET_WM_STATE_BELOW", + "_NET_WM_STATE_DEMANDS_ATTENTION", + + "_NET_SUPPORTED", + "_NET_CLIENT_LIST", + "_NET_NUMBER_OF_DESKTOPS", + "_NET_ACTIVE_WINDOW", + "_NET_SUPPORTING_WM_CHECK", + "_NET_CLOSE_WINDOW", + "_NET_WM_NAME", + "_NET_WM_USER_TIME", + + "_NET_CLIENT_LIST_STACKING", + "_NET_CURRENT_DESKTOP", + "_NET_WM_DESKTOP", + "_NET_WM_ICON", + "_NET_DESKTOP_GEOMETRY", + "_NET_WORKAREA", + "_NET_SHOWING_DESKTOP", + "_NET_DESKTOP_VIEWPORT", + "_NET_FRAME_EXTENTS", + "_NET_WM_FULL_PLACEMENT", + + "_NET_WM_ALLOWED_ACTIONS", + "_NET_WM_ACTION_MOVE", + "_NET_WM_ACTION_RESIZE", + "_NET_WM_ACTION_MINIMIZE", + "_NET_WM_ACTION_SHADE", + "_NET_WM_ACTION_STICK", + "_NET_WM_ACTION_MAXIMIZE_HORZ", + "_NET_WM_ACTION_MAXIMIZE_VERT", + "_NET_WM_ACTION_FULLSCREEN", + "_NET_WM_ACTION_CHANGE_DESKTOP", + "_NET_WM_ACTION_CLOSE", + + "_NET_WM_PING", + "_NET_WM_PID", + + "_NET_STARTUP_ID", + + "UTF8_STRING", + "_MOTIF_WM_HINTS", + "WIN_SUPPORTING_WM_CHECK", + + "_NET_WM_CONTEXT_HELP", + "_NET_WM_CONTEXT_ACCEPT", + "_NET_WM_CONTEXT_CUSTOM", + "_NET_WM_SYNC_REQUEST", + "CM_TRANSLUCENCY", + "_MB_APP_WINDOW_LIST_STACKING", + "_MB_THEME", + "_MB_THEME_NAME", + "_MB_COMMAND", + "_MB_GRAB_TRANSFER", + "_MB_CURRENT_APP_WINDOW", + }; + + /* FIXME: Error Traps */ + + MBWM_ASSERT (MBWM_ATOM_COUNT == sizeof (atom_names) / sizeof (char*)); + + XInternAtoms (wm->xdpy, + atom_names, + MBWM_ATOM_COUNT, + False, + wm->atoms); +} diff --git a/matchbox/mb-wm-atoms.h b/matchbox/mb-wm-atoms.h new file mode 100644 index 0000000..2007acc --- /dev/null +++ b/matchbox/mb-wm-atoms.h @@ -0,0 +1,29 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_WM_ATOMS_H +#define _HAVE_MB_WM_ATOMS_H + +#include <matchbox/mb-window-manager.h> + +void +mb_wm_atoms_init(MBWindowManager *wm); + +#endif diff --git a/matchbox/mb-wm-client-app.c b/matchbox/mb-wm-client-app.c new file mode 100644 index 0000000..3aa9d2e --- /dev/null +++ b/matchbox/mb-wm-client-app.c @@ -0,0 +1,240 @@ +#include "mb-wm-client-app.h" + +#include "mb-wm-theme.h" + +static Bool +mb_wm_client_app_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags); + +static MBWMStackLayerType +mb_wm_client_app_stacking_layer (MBWindowManagerClient *client); + +static void +mb_wm_client_app_theme_change (MBWindowManagerClient *client); + +static void +mb_wm_client_app_class_init (MBWMObjectClass *klass) +{ + MBWindowManagerClientClass *client; + MBWMClientAppClass * client_app; + + MBWM_MARK(); + + client = (MBWindowManagerClientClass *)klass; + client_app = (MBWMClientAppClass *)klass; + + MBWM_DBG("client->stack is %p", client->stack); + + client->client_type = MBWMClientTypeApp; + client->geometry = mb_wm_client_app_request_geometry; + client->theme_change = mb_wm_client_app_theme_change; + client->stacking_layer = mb_wm_client_app_stacking_layer; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMClientApp"; +#endif +} + +static void +mb_wm_client_app_destroy (MBWMObject *this) +{ +} + + +static int +mb_wm_client_app_init (MBWMObject *this, va_list vap) +{ + MBWindowManagerClient *client = MB_WM_CLIENT (this); + MBWindowManager *wm = NULL; + MBWMClientAppClass *app_class; + + app_class = MB_WM_CLIENT_APP_CLASS (MB_WM_OBJECT_GET_CLASS (this)); + +#if 0 + /* + * Property parsing not needed for now, as there are no ClientApp specific + * properties + */ + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + if (prop == MBWMObjectPropWm) + { + wm = va_arg(vap, MBWindowManager *); + break; + } + else + MBWMO_PROP_EAT (vap, prop); + + prop = va_arg (vap, MBWMObjectProp); + } +#endif + + wm = client->wmref; + + if (!wm) + return 0; + + { + Atom actions[] = { + wm->atoms[MBWM_ATOM_NET_WM_ACTION_CLOSE], + wm->atoms[MBWM_ATOM_NET_WM_ACTION_FULLSCREEN] + }; + + XChangeProperty (wm->xdpy, client->window->xwindow, + wm->atoms[MBWM_ATOM_NET_WM_ALLOWED_ACTIONS], + XA_ATOM, 32, PropModeReplace, + (unsigned char *)actions, + sizeof (actions)/sizeof (actions[0])); + } + + client->stacking_layer = MBWMStackLayerMid; + + mb_wm_client_set_layout_hints (client, + LayoutPrefGrowToFreeSpace|LayoutPrefVisible); + + if (!client->window->undecorated) + { + mb_wm_theme_create_decor (wm->theme, client, MBWMDecorTypeNorth); + mb_wm_theme_create_decor (wm->theme, client, MBWMDecorTypeSouth); + mb_wm_theme_create_decor (wm->theme, client, MBWMDecorTypeWest); + mb_wm_theme_create_decor (wm->theme, client, MBWMDecorTypeEast); + } + + return 1; +} + +int +mb_wm_client_app_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMClientAppClass), + sizeof (MBWMClientApp), + mb_wm_client_app_init, + mb_wm_client_app_destroy, + mb_wm_client_app_class_init + }; + type = mb_wm_object_register_class (&info, MB_WM_TYPE_CLIENT_BASE, 0); + } + + return type; +} + +static Bool +mb_wm_client_app_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags) +{ + if (flags & (MBWMClientReqGeomIsViaLayoutManager | + MBWMClientReqGeomForced | + MBWMClientReqGeomIsViaUserAction)) + { + int north, south, west, east; + MBWindowManager *wm = client->wmref; + + if ((client->window->ewmh_state & MBWMClientWindowEWMHStateFullscreen)|| + !client->decor) + { + /* Undecorated window */ + client->window->geometry.x = new_geometry->x; + client->window->geometry.y = new_geometry->y; + client->window->geometry.width = new_geometry->width; + client->window->geometry.height = new_geometry->height; + + client->frame_geometry.x = new_geometry->x; + client->frame_geometry.y = new_geometry->y; + client->frame_geometry.width = new_geometry->width; + client->frame_geometry.height = new_geometry->height; + } + else + { + mb_wm_theme_get_decor_dimensions (wm->theme, client, + &north, &south, &west, &east); + + client->frame_geometry.x = new_geometry->x; + client->frame_geometry.y = new_geometry->y; + client->frame_geometry.width = new_geometry->width; + client->frame_geometry.height = new_geometry->height; + + client->window->geometry.x + = client->frame_geometry.x + west; + client->window->geometry.y + = client->frame_geometry.y + north; + client->window->geometry.width + = client->frame_geometry.width - (west + east); + client->window->geometry.height + = client->frame_geometry.height - (south + north); + } + + mb_wm_client_geometry_mark_dirty (client); + + return True; /* Geometry accepted */ + } + + return False; +} + +static void +mb_wm_client_app_theme_change (MBWindowManagerClient *client) +{ + MBWMList * l = client->decor; + + while (l) + { + MBWMDecor * d = l->data; + MBWMList * n = l->next; + + mb_wm_object_unref (MB_WM_OBJECT (d)); + free (l); + + l = n; + } + + client->decor = NULL; + + if (!client->window->undecorated) + { + mb_wm_theme_create_decor (client->wmref->theme, + client, MBWMDecorTypeNorth); + + mb_wm_theme_create_decor (client->wmref->theme, + client, MBWMDecorTypeSouth); + + mb_wm_theme_create_decor (client->wmref->theme, + client, MBWMDecorTypeWest); + + mb_wm_theme_create_decor (client->wmref->theme, + client, MBWMDecorTypeEast); + } + + mb_wm_client_geometry_mark_dirty (client); + mb_wm_client_visibility_mark_dirty (client); +} + +static MBWMStackLayerType +mb_wm_client_app_stacking_layer (MBWindowManagerClient *client) +{ + if (client->window->ewmh_state & MBWMClientWindowEWMHStateFullscreen) + return MBWMStackLayerTopMid; + + return client->stacking_layer; +} + +MBWindowManagerClient* +mb_wm_client_app_new (MBWindowManager *wm, MBWMClientWindow *win) +{ + MBWindowManagerClient *client; + + client = MB_WM_CLIENT(mb_wm_object_new (MB_WM_TYPE_CLIENT_APP, + MBWMObjectPropWm, wm, + MBWMObjectPropClientWindow, win, + NULL)); + + return client; +} + diff --git a/matchbox/mb-wm-client-app.h b/matchbox/mb-wm-client-app.h new file mode 100644 index 0000000..1f33a7e --- /dev/null +++ b/matchbox/mb-wm-client-app.h @@ -0,0 +1,51 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_CLIENT_APP_H +#define _HAVE_MB_CLIENT_APP_H + +#include <matchbox/matchbox.h> + +typedef struct MBWMClientApp MBWMClientApp; +typedef struct MBWMClientAppClass MBWMClientAppClass; + +#define MB_WM_CLIENT_APP(c) ((MBWMClientApp*)(c)) +#define MB_WM_CLIENT_APP_CLASS(c) ((MBWMClientAppClass*)(c)) +#define MB_WM_TYPE_CLIENT_APP (mb_wm_client_app_class_type ()) +#define MB_WM_IS_CLIENT_APP(c) (MB_WM_OBJECT_TYPE(c)==MB_WM_TYPE_CLIENT_APP) + +struct MBWMClientApp +{ + MBWMClientBase parent; +}; + +struct MBWMClientAppClass +{ + MBWMClientBaseClass parent; + +}; + +MBWindowManagerClient* +mb_wm_client_app_new(MBWindowManager *wm, MBWMClientWindow *win); + +int +mb_wm_client_app_class_type (); + +#endif diff --git a/matchbox/mb-wm-client-base.c b/matchbox/mb-wm-client-base.c new file mode 100644 index 0000000..d9b0446 --- /dev/null +++ b/matchbox/mb-wm-client-base.c @@ -0,0 +1,742 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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 "matchbox.h" +#include "mb-wm-theme.h" + +#include <X11/Xmd.h> + +#if ENABLE_COMPOSITE +#include <X11/extensions/Xrender.h> +#endif + +#ifdef HAVE_XEXT +#include <X11/extensions/shape.h> +#endif + +static void +mb_wm_client_base_realize (MBWindowManagerClient *client); + +static void +mb_wm_client_base_stack (MBWindowManagerClient *client, + int flags); +static void +mb_wm_client_base_show (MBWindowManagerClient *client); + +static void +mb_wm_client_base_hide (MBWindowManagerClient *client); + + +static void +mb_wm_client_base_display_sync (MBWindowManagerClient *client); + +static Bool +mb_wm_client_base_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags); + +static Bool +mb_wm_client_base_focus (MBWindowManagerClient *client); + +static void +mb_wm_client_base_class_init (MBWMObjectClass *klass) +{ + MBWindowManagerClientClass *client; + + MBWM_MARK(); + + client = (MBWindowManagerClientClass *)klass; + + client->realize = mb_wm_client_base_realize; + client->geometry = mb_wm_client_base_request_geometry; + client->stack = mb_wm_client_base_stack; + client->show = mb_wm_client_base_show; + client->hide = mb_wm_client_base_hide; + client->sync = mb_wm_client_base_display_sync; + client->focus = mb_wm_client_base_focus; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMClientBase"; +#endif +} + +static void +mb_wm_client_base_destroy (MBWMObject *this) +{ + MBWindowManagerClient *parent; + MBWindowManagerClient *client; + MBWindowManager *wm; + + MBWM_MARK(); + + client = MB_WM_CLIENT(this); + + wm = client->wmref; + + mb_wm_util_trap_x_errors(); + + if (client->xwin_frame) + { + XReparentWindow (wm->xdpy, MB_WM_CLIENT_XWIN(client), + wm->root_win->xwindow, 0, 0); + + XDestroyWindow (wm->xdpy, client->xwin_frame); + client->xwin_frame = None; + + + if (client->xwin_modal_blocker) + { + XDestroyWindow (wm->xdpy, client->xwin_modal_blocker); + client->xwin_modal_blocker = None; + } + } + + XSync(wm->xdpy, False); + mb_wm_util_untrap_x_errors(); + + parent = mb_wm_client_get_transient_for (MB_WM_CLIENT(this)); + + if (parent) + mb_wm_client_remove_transient (parent, MB_WM_CLIENT(this)); +} + +static int +mb_wm_client_base_init (MBWMObject *this, va_list vap) +{ + return 1; +} + +int +mb_wm_client_base_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMClientBaseClass), + sizeof (MBWMClientBase), + mb_wm_client_base_init, + mb_wm_client_base_destroy, + mb_wm_client_base_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_CLIENT, 0); + } + + return type; +} + +static void +mb_wm_client_base_realize (MBWindowManagerClient *client) +{ + MBWindowManager *wm = client->wmref; + + XSetWindowAttributes attr; + + MBWM_ASSERT(client->window != NULL); + + /* create the frame window */ + + attr.override_redirect = True; + attr.background_pixel = BlackPixel(wm->xdpy, wm->xscreen); + attr.event_mask = MBWMChildMask|MBWMButtonMask|ExposureMask; + + /* This should probably be called via rather than on new clien sync() ...? */ + /* + * We only create a frame window if the client is decorated (decors are + * constructed in the _init functions, so we can easily test if the frame + * is needed or not). + */ + if (client->decor) + { + if (client->xwin_frame == None) + { +#if ENABLE_COMPOSITE + if (mb_wm_client_is_argb32 (client)) + { + attr.colormap = client->window->colormap; + + client->xwin_frame + = XCreateWindow(wm->xdpy, wm->root_win->xwindow, + client->frame_geometry.x, + client->frame_geometry.y, + client->frame_geometry.width, + client->frame_geometry.height, + 0, + 32, + InputOutput, + client->window->visual, + CWOverrideRedirect|CWEventMask|CWBackPixel| + CWBorderPixel|CWColormap, + &attr); + } + else +#endif + { + client->xwin_frame + = XCreateWindow(wm->xdpy, wm->root_win->xwindow, + client->frame_geometry.x, + client->frame_geometry.y, + client->frame_geometry.width, + client->frame_geometry.height, + 0, + CopyFromParent, + CopyFromParent, + CopyFromParent, + CWOverrideRedirect|CWEventMask|CWBackPixel, + &attr); + } + + /* + * Assume geometry sync will fix this up correctly togeather with + * any decoration creation. Layout manager will call this + */ + XReparentWindow(wm->xdpy, + MB_WM_CLIENT_XWIN(client), + client->xwin_frame, + 0, 0); + } + else + { + /* + * This is an undecorated client; we Must reparent the window to our + * root, otherwise we restacking of pre-existing windows might fail. + */ + XReparentWindow(client->wmref->xdpy, MB_WM_CLIENT_XWIN(client), + client->wmref->root_win->xwindow, 0, 0); + } + + /* + * If this is a system-modal client and the global setting is to support + * system modal windows, we create a fullscreen, input-only window that + * gets stacked immediately bellow it, catching any input events that + * fall outside of the system-modal client. + */ + if (mb_wm_client_is_modal (client) && + !mb_wm_client_get_transient_for (client) && + mb_wm_get_modality_type (wm) == MBWMModalitySystem) + { + XSetWindowAttributes attr; + attr.override_redirect = True; + attr.event_mask = MBWMChildMask|ButtonPressMask|ExposureMask; + + client->xwin_modal_blocker = + XCreateWindow (wm->xdpy, + wm->root_win->xwindow, + 0, 0, + wm->xdpy_width, + wm->xdpy_height, + 0, + CopyFromParent, + InputOnly, + CopyFromParent, + CWOverrideRedirect|CWEventMask, + &attr); + } + } + + XSetWindowBorderWidth(wm->xdpy, MB_WM_CLIENT_XWIN(client), 0); + + XAddToSaveSet(wm->xdpy, MB_WM_CLIENT_XWIN(client)); + + XSelectInput(wm->xdpy, + MB_WM_CLIENT_XWIN(client), + PropertyChangeMask); +} + +static void +mb_wm_client_base_stack (MBWindowManagerClient *client, + int flags) +{ + /* Stack to highest/lowest possible possition in stack */ + MBWMList * t = mb_wm_client_get_transients (client); + + mb_wm_stack_move_top(client); + + mb_wm_util_list_foreach (t, (MBWMListForEachCB)mb_wm_client_stack, + (void*)flags); + + mb_wm_util_list_free (t); +} + +static void +mb_wm_client_base_show (MBWindowManagerClient *client) +{ + /* mark dirty somehow */ + +} + +static void +mb_wm_client_base_hide (MBWindowManagerClient *client) +{ + + /* mark dirty somehow */ + +} + +static void +mb_wm_client_base_set_state_props (MBWindowManagerClient *c) +{ + unsigned long flags = c->window->ewmh_state; + Window xwin = c->window->xwindow; + MBWindowManager *wm = c->wmref; + Display *xdpy = wm->xdpy; + CARD32 card32[2]; + Atom ewmh_state [MBWMClientWindowEWHMStatesCount]; + int ewmh_i = 0; + + card32[1] = None; + + if (mb_wm_client_is_mapped (c)) + card32[0] = NormalState; + else + card32[0] = IconicState; + + if (flags & MBWMClientWindowEWMHStateFullscreen) + ewmh_state[ewmh_i++] = wm->atoms[MBWM_ATOM_NET_WM_STATE_FULLSCREEN]; + + if (flags & MBWMClientWindowEWMHStateModal) + ewmh_state[ewmh_i++] = wm->atoms[MBWM_ATOM_NET_WM_STATE_MODAL]; + + if (flags & MBWMClientWindowEWMHStateSticky) + ewmh_state[ewmh_i++] = wm->atoms[MBWM_ATOM_NET_WM_STATE_STICKY]; + + if (flags & MBWMClientWindowEWMHStateMaximisedVert) + ewmh_state[ewmh_i++] = wm->atoms[MBWM_ATOM_NET_WM_STATE_MAXIMIZED_VERT]; + if (flags & MBWMClientWindowEWMHStateMaximisedHorz) + ewmh_state[ewmh_i++] = wm->atoms[MBWM_ATOM_NET_WM_STATE_MAXIMIZED_HORZ]; + if (flags & MBWMClientWindowEWMHStateShaded) + ewmh_state[ewmh_i++] = wm->atoms[MBWM_ATOM_NET_WM_STATE_SHADED]; + + if (flags & MBWMClientWindowEWMHStateSkipTaskbar) + ewmh_state[ewmh_i++] = wm->atoms[MBWM_ATOM_NET_WM_STATE_SKIP_TASKBAR]; + + if (flags & MBWMClientWindowEWMHStateSkipPager) + ewmh_state[ewmh_i++] = wm->atoms[MBWM_ATOM_NET_WM_STATE_SKIP_PAGER]; + + if (flags & MBWMClientWindowEWMHStateAbove) + ewmh_state[ewmh_i++] = wm->atoms[MBWM_ATOM_NET_WM_STATE_BELOW]; + + if (flags & MBWMClientWindowEWMHStateDemandsAttention) + ewmh_state[ewmh_i++] = wm->atoms[MBWM_ATOM_NET_WM_STATE_DEMANDS_ATTENTION]; + if (flags & MBWMClientWindowEWMHStateHidden) + ewmh_state[ewmh_i++] = wm->atoms[MBWM_ATOM_NET_WM_STATE_HIDDEN]; + + + XChangeProperty(xdpy, xwin, wm->atoms[MBWM_ATOM_WM_STATE], + wm->atoms[MBWM_ATOM_WM_STATE], 32, PropModeReplace, + (void *)&card32[0], 2); + + if (ewmh_i) + XChangeProperty (xdpy, xwin, wm->atoms[MBWM_ATOM_NET_WM_STATE], + XA_ATOM, 32, PropModeReplace, + (void*) &ewmh_state[0], ewmh_i); + else + XDeleteProperty (xdpy, xwin, wm->atoms[MBWM_ATOM_NET_WM_STATE]); +} + +static void +send_synthetic_configure_notify (MBWindowManagerClient *client) +{ + MBWindowManager *wm = client->wmref; + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.event = MB_WM_CLIENT_XWIN(client); + ce.window = MB_WM_CLIENT_XWIN(client); + ce.x = client->window->geometry.x; + ce.y = client->window->geometry.y; + ce.width = client->window->geometry.width; + ce.height = client->window->geometry.height; + ce.border_width = 0; + ce.above = None; + ce.override_redirect = 0; + + XSendEvent(wm->xdpy, MB_WM_CLIENT_XWIN(client), False, + StructureNotifyMask, (XEvent *)&ce); +} + +static void +move_resize_client_xwin (MBWindowManagerClient *client, int x, int y, int w, int h) +{ + MBWindowManager *wm = client->wmref; + + /* ICCCM says if you ignore a configure request or you respond + * by only moving/re-stacking the window - without a size change, + * then the WM must send a synthetic ConfigureNotify. + * + * NB: the above description assumes that a move/re-stack may be + * done by the WM by moving the frame (whereby a regular + * ConfigureNotify wouldn't be sent in direct response to the + * request which I think is the real point) + * + * NB: It's assumed that no cleverness is going elsewhere + * to optimise out calls to this function when the move/resize + * is obviously not needed (e.g. when just moving the frame + * of a client) + */ + if (mb_wm_client_needs_configure_request_ack (client) + && x == client->window->x_geometry.x + && y == client->window->x_geometry.y + && w == client->window->x_geometry.width + && h == client->window->x_geometry.height) + { + send_synthetic_configure_notify (client); + } + else + { + XMoveResizeWindow(wm->xdpy, MB_WM_CLIENT_XWIN(client), + x, y, w, h); + client->window->x_geometry.x = x; + client->window->x_geometry.y = y; + client->window->x_geometry.width = w; + client->window->x_geometry.height = h; + +#if ENABLE_COMPOSITE + if (mb_wm_comp_mgr_enabled (wm->comp_mgr)) + { + mb_wm_comp_mgr_client_configure (client->cm_client); + } +#endif + } +} + +static void +mb_wm_client_base_display_sync (MBWindowManagerClient *client) +{ + MBWindowManager *wm = client->wmref; + MBWMClientWindow * win = client->window; + Bool fullscreen = (win->ewmh_state & MBWMClientWindowEWMHStateFullscreen); + + MBWM_MARK(); + + /* + * If we need to sync due to change in fullscreen state, we have reparent + * the client window to the frame/root window + */ + if (mb_wm_client_needs_fullscreen_sync (client)) + { + if (mb_wm_client_is_mapped (client)) + { + if (client->xwin_frame) + { + if (!fullscreen) + { + client->skip_unmaps++; + XReparentWindow(wm->xdpy, MB_WM_CLIENT_XWIN(client), + client->xwin_frame, 0, 0); + XMapWindow(wm->xdpy, client->xwin_frame); + XMapSubwindows(wm->xdpy, client->xwin_frame); + + /* The frame is very likely the correct dimensions (since the + * app geometry is pretty static), but the client window is + * not (it was fullscreened) -- we need to force + * recalculation of the window frame dimensions, so that it + * is correct when we apply it below. + */ + mb_wm_client_request_geometry (client, + &client->frame_geometry, + MBWMClientReqGeomForced); + } + else + { + client->skip_unmaps++; + XReparentWindow(wm->xdpy, MB_WM_CLIENT_XWIN(client), + wm->root_win->xwindow, 0, 0); + XUnmapWindow(wm->xdpy, client->xwin_frame); + XMapWindow(wm->xdpy, MB_WM_CLIENT_XWIN(client)); + } + } + else + { + client->skip_unmaps++; + XReparentWindow(wm->xdpy, MB_WM_CLIENT_XWIN(client), + wm->root_win->xwindow, + client->window->geometry.x, + client->window->geometry.y); + } + } + + if (wm->focused_client == client) + { + /* + * If we are the currently focused client, + * we need to reset the focus to RevertToPointerRoot, since the + * focus was lost during the implicit unmap. + */ + XSetInputFocus (wm->xdpy, client->window->xwindow, + RevertToPointerRoot, CurrentTime); + + } + } + + /* Sync up any geometry */ + + if (mb_wm_client_needs_geometry_sync (client)) + { + int x, y, w, h; + CARD32 wgeom[4]; + + mb_wm_util_trap_x_errors(); + + if (fullscreen || !client->xwin_frame) + { + + x = client->window->geometry.x; + y = client->window->geometry.y; + w = client->window->geometry.width; + h = client->window->geometry.height; + + move_resize_client_xwin (client, x, y, w, h); + + wgeom[0] = 0; + wgeom[1] = 0; + wgeom[2] = 0; + wgeom[3] = 0; + } + else + { + XMoveResizeWindow(wm->xdpy, + client->xwin_frame, + client->frame_geometry.x, + client->frame_geometry.y, + client->frame_geometry.width, + client->frame_geometry.height); + + /* FIXME: Call XConfigureWindow(w->dpy, e->window, + * value_mask,&xwc); here instead as can set border + * width = 0. + */ + x = client->window->geometry.x - client->frame_geometry.x; + y = client->window->geometry.y - client->frame_geometry.y; + w = client->window->geometry.width; + h = client->window->geometry.height; + + move_resize_client_xwin (client, x, y, w, h); + + wgeom[0] = x; + wgeom[1] = client->frame_geometry.width - w - x; + wgeom[2] = y; + wgeom[3] = client->frame_geometry.height - h - y; + } + + XChangeProperty(wm->xdpy, + MB_WM_CLIENT_XWIN(client), + wm->atoms[MBWM_ATOM_NET_FRAME_EXTENTS], + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)&wgeom[0], 4); + + /* FIXME: need flags to handle other stuff like configure events etc */ + + /* Resize any decor */ + mb_wm_util_list_foreach(client->decor, + (MBWMListForEachCB)mb_wm_decor_handle_resize, + NULL); + + mb_wm_util_untrap_x_errors(); + } + else if (mb_wm_client_needs_configure_request_ack (client)) + send_synthetic_configure_notify (client); + + + /* Handle any mapping - should be visible state ? */ + + if (mb_wm_client_needs_visibility_sync (client)) + { + mb_wm_util_trap_x_errors(); + + if (mb_wm_client_is_mapped (client)) + { + if (client->xwin_frame) + { + if (!fullscreen) + { + XMapWindow (wm->xdpy, client->xwin_frame); + XMapSubwindows (wm->xdpy, client->xwin_frame); + } + else + { + XUnmapWindow (wm->xdpy, client->xwin_frame); + XMapWindow (wm->xdpy, MB_WM_CLIENT_XWIN(client)); + } + } + else + { + XMapWindow (wm->xdpy, MB_WM_CLIENT_XWIN(client)); + } + + if (client->xwin_modal_blocker) + XMapWindow (wm->xdpy, client->xwin_modal_blocker); + } + else + { + if (client->xwin_frame) + { + XUnmapWindow (wm->xdpy, client->xwin_frame); + XUnmapSubwindows (wm->xdpy, client->xwin_frame); + } + else + XUnmapWindow (wm->xdpy, MB_WM_CLIENT_XWIN(client)); + + + if (client->xwin_modal_blocker) + XUnmapWindow (wm->xdpy, client->xwin_modal_blocker); + + } + + mb_wm_client_base_set_state_props (client); + mb_wm_util_untrap_x_errors(); + } + + /* Paint any decor */ + + mb_wm_util_trap_x_errors(); + + if (mb_wm_client_needs_decor_sync (client)) + { + /* + * First, we set the base shape mask, if needed, so that individual + * decors can add themselves to it. + */ +#ifdef HAVE_XEXT + if (mb_wm_theme_is_client_shaped (wm->theme, client)) + { + XRectangle rects[1]; + + rects[0].x = client->window->geometry.x - client->frame_geometry.x; + rects[0].y = client->window->geometry.y - client->frame_geometry.y; + rects[0].width = client->window->geometry.width; + rects[0].height = client->window->geometry.height; + + XShapeCombineRectangles (wm->xdpy, client->xwin_frame, + ShapeBounding, + 0, 0, rects, 1, ShapeSet, 0 ); + } +#endif + + if (fullscreen) + { + if (client->xwin_frame) + XUnmapWindow(wm->xdpy, client->xwin_frame); + } + else + { + if (client->xwin_frame) + XMapWindow(wm->xdpy, client->xwin_frame); + } + + mb_wm_util_list_foreach(client->decor, + (MBWMListForEachCB)mb_wm_decor_handle_repaint, + NULL); + + + } + + mb_wm_util_untrap_x_errors(); + + mb_wm_util_trap_x_errors(); + XSync(wm->xdpy, False); + mb_wm_util_untrap_x_errors(); +} + +/* Note request geometry always called by layout manager */ +static Bool +mb_wm_client_base_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags) +{ + /* + * flags are + * + * MBReqGeomDontCommit + * MBReqGeomIsViaConfigureReq + * MBReqGeomIsViaUserAction + * MBReqGeomIsViaLayoutManager + * MBReqGeomForced + * + */ + + /* Dont actually change anything - mb_wm_core_sync() should do that + * but mark dirty and 'queue any extra info like configure req'. + */ + + if (flags & MBWMClientReqGeomIsViaLayoutManager) + { + MBWM_DBG("called with 'MBWMClientReqGeomIsViaLayoutManager' %ix%i+%i+%i", + new_geometry->width, + new_geometry->height, + new_geometry->x, + new_geometry->y); + + client->frame_geometry.x = new_geometry->x; + client->frame_geometry.y = new_geometry->y; + client->frame_geometry.width = new_geometry->width; + client->frame_geometry.height = new_geometry->height; + + client->window->geometry.x = client->frame_geometry.x + 4; + client->window->geometry.y = client->frame_geometry.y + 4; + client->window->geometry.width = client->frame_geometry.width - 8; + client->window->geometry.height = client->frame_geometry.height - 8; + + mb_wm_client_geometry_mark_dirty (client); + + return True; /* Geometry accepted */ + } + + return True; +} + +static Bool +mb_wm_client_base_focus (MBWindowManagerClient *client) +{ + static Window last_focused = None; + MBWindowManager *wm = client->wmref; + Window xwin = client->window->xwindow; + + if (!mb_wm_client_want_focus (client)) + return False; + + if (xwin == last_focused) + return False; + + mb_wm_util_trap_x_errors (); + + XSetInputFocus(wm->xdpy, xwin, RevertToPointerRoot, CurrentTime); + + XChangeProperty(wm->xdpy, wm->root_win->xwindow, + wm->atoms[MBWM_ATOM_NET_ACTIVE_WINDOW], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *)&xwin, 1); + + if (mb_wm_util_untrap_x_errors()) + return False; + + last_focused = xwin; + + return True; +} + +void base_foo(void) +{ + ; /* nasty hack to workaround linking issues WTF... + * use .la's rather than .a's ?? + */ +} diff --git a/matchbox/mb-wm-client-base.h b/matchbox/mb-wm-client-base.h new file mode 100644 index 0000000..26ebd63 --- /dev/null +++ b/matchbox/mb-wm-client-base.h @@ -0,0 +1,48 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_WM_CLIENT_BASE_H +#define _HAVE_MB_WM_CLIENT_BASE_H + +#include <matchbox/mb-wm-client.h> + +#define MB_WM_CLIENT_BASE(c) ((MBWMClientBase*)(c)) +#define MB_WM_CLIENT_BASE_CLASS(c) ((MBWMClientBaseClass*)(c)) +#define MB_WM_TYPE_CLIENT_BASE (mb_wm_client_base_class_type ()) + +typedef struct MBWMClientBase +{ + MBWindowManagerClient parent; +} +MBWMClientBase; + +typedef struct MBWMClientBaseClass +{ + MBWindowManagerClientClass parent; + +} +MBWMClientBaseClass; + +int +mb_wm_client_base_class_type (); + +void base_foo(void); + +#endif diff --git a/matchbox/mb-wm-client-desktop.c b/matchbox/mb-wm-client-desktop.c new file mode 100644 index 0000000..81ce628 --- /dev/null +++ b/matchbox/mb-wm-client-desktop.c @@ -0,0 +1,196 @@ +#include "mb-wm-client-desktop.h" +#include "mb-wm-theme.h" + +static Bool +mb_wm_client_desktop_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags); + +static MBWMStackLayerType +mb_wm_client_desktop_stacking_layer (MBWindowManagerClient *client); + +static void +mb_wm_client_desktop_theme_change (MBWindowManagerClient *client); + +static void +mb_wm_client_desktop_class_init (MBWMObjectClass *klass) +{ + MBWindowManagerClientClass *client; + MBWMClientDesktopClass * client_desktop; + + MBWM_MARK(); + + client = (MBWindowManagerClientClass *)klass; + client_desktop = (MBWMClientDesktopClass *)klass; + + client->client_type = MBWMClientTypeDesktop; + client->geometry = mb_wm_client_desktop_request_geometry; + client->stacking_layer = mb_wm_client_desktop_stacking_layer; + client->theme_change = mb_wm_client_desktop_theme_change; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMClientDesktop"; +#endif +} + +static void +mb_wm_client_desktop_destroy (MBWMObject *this) +{ +} + +static int +mb_wm_client_desktop_init (MBWMObject *this, va_list vap) +{ + MBWindowManagerClient *client = MB_WM_CLIENT (this); + MBWindowManager *wm = NULL; + MBWMClientDesktopClass *inp_class; + MBGeometry geom; + + inp_class = MB_WM_CLIENT_DESKTOP_CLASS (MB_WM_OBJECT_GET_CLASS (this)); + + wm = client->wmref; + + if (!wm) + return 0; + + client->stacking_layer = MBWMStackLayerBottom; + + mb_wm_client_set_layout_hints (client, + LayoutPrefFullscreen|LayoutPrefVisible); + + if (!client->window->undecorated) + { + mb_wm_theme_create_decor (wm->theme, client, MBWMDecorTypeNorth); + mb_wm_theme_create_decor (wm->theme, client, MBWMDecorTypeSouth); + mb_wm_theme_create_decor (wm->theme, client, MBWMDecorTypeWest); + mb_wm_theme_create_decor (wm->theme, client, MBWMDecorTypeEast); + } + + /* + * Initialize window geometry, so that the frame size is correct + */ + geom.x = 0; + geom.y = 0; + geom.width = wm->xdpy_width; + geom.height = wm->xdpy_height; + + mb_wm_client_desktop_request_geometry (client, &geom, + MBWMClientReqGeomForced); + + return 1; +} + +int +mb_wm_client_desktop_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMClientDesktopClass), + sizeof (MBWMClientDesktop), + mb_wm_client_desktop_init, + mb_wm_client_desktop_destroy, + mb_wm_client_desktop_class_init + }; + type = mb_wm_object_register_class (&info, MB_WM_TYPE_CLIENT_BASE, 0); + } + + return type; +} + +static Bool +mb_wm_client_desktop_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags) +{ + if (flags & (MBWMClientReqGeomIsViaLayoutManager|MBWMClientReqGeomForced)) + { + int north = 0, south = 0, west = 0, east = 0; + MBWindowManager *wm = client->wmref; + + if (client->decor) + mb_wm_theme_get_decor_dimensions (wm->theme, client, + &north, &south, &west, &east); + + client->frame_geometry.x = new_geometry->x; + client->frame_geometry.y = new_geometry->y; + client->frame_geometry.width = new_geometry->width; + client->frame_geometry.height = new_geometry->height; + + client->window->geometry.x + = client->frame_geometry.x + west; + client->window->geometry.y + = client->frame_geometry.y + north; + client->window->geometry.width + = client->frame_geometry.width - (west + east); + client->window->geometry.height + = client->frame_geometry.height - (south + north); + + mb_wm_client_geometry_mark_dirty (client); + + return True; /* Geometry accepted */ + } + return False; +} + +static MBWMStackLayerType +mb_wm_client_desktop_stacking_layer (MBWindowManagerClient *client) +{ + if (client->wmref->flags & MBWindowManagerFlagDesktop) + return MBWMStackLayerMid; + + return MBWMStackLayerBottom; +} + +static void +mb_wm_client_desktop_theme_change (MBWindowManagerClient *client) +{ + MBWMList * l = client->decor; + + while (l) + { + MBWMDecor * d = l->data; + MBWMList * n = l->next; + + mb_wm_object_unref (MB_WM_OBJECT (d)); + free (l); + + l = n; + } + + client->decor = NULL; + + if (!client->window->undecorated) + { + mb_wm_theme_create_decor (client->wmref->theme, + client, MBWMDecorTypeNorth); + + mb_wm_theme_create_decor (client->wmref->theme, + client, MBWMDecorTypeSouth); + + mb_wm_theme_create_decor (client->wmref->theme, + client, MBWMDecorTypeWest); + + mb_wm_theme_create_decor (client->wmref->theme, + client, MBWMDecorTypeEast); + } + + mb_wm_client_geometry_mark_dirty (client); + mb_wm_client_visibility_mark_dirty (client); +} + +MBWindowManagerClient* +mb_wm_client_desktop_new (MBWindowManager *wm, MBWMClientWindow *win) +{ + MBWindowManagerClient *client; + + client = MB_WM_CLIENT(mb_wm_object_new (MB_WM_TYPE_CLIENT_DESKTOP, + MBWMObjectPropWm, wm, + MBWMObjectPropClientWindow, win, + NULL)); + + return client; +} + diff --git a/matchbox/mb-wm-client-desktop.h b/matchbox/mb-wm-client-desktop.h new file mode 100644 index 0000000..3484fa6 --- /dev/null +++ b/matchbox/mb-wm-client-desktop.h @@ -0,0 +1,50 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_CLIENT_DESKTOP_H +#define _HAVE_MB_CLIENT_DESKTOP_H + +#include <matchbox/matchbox.h> + +typedef struct MBWMClientDesktop MBWMClientDesktop; +typedef struct MBWMClientDesktopClass MBWMClientDesktopClass; + +#define MB_WM_CLIENT_DESKTOP(c) ((MBWMClientDesktop*)(c)) +#define MB_WM_CLIENT_DESKTOP_CLASS(c) ((MBWMClientDesktopClass*)(c)) +#define MB_WM_TYPE_CLIENT_DESKTOP (mb_wm_client_desktop_class_type ()) +#define MB_WM_IS_CLIENT_DESKTOP(c) (MB_WM_OBJECT_TYPE(c)==MB_WM_TYPE_CLIENT_DESKTOP) + +struct MBWMClientDesktop +{ + MBWMClientBase parent; +}; + +struct MBWMClientDesktopClass +{ + MBWMClientBaseClass parent; +}; + +MBWindowManagerClient* +mb_wm_client_desktop_new(MBWindowManager *wm, MBWMClientWindow *win); + +int +mb_wm_client_desktop_class_type (); + +#endif diff --git a/matchbox/mb-wm-client-dialog.c b/matchbox/mb-wm-client-dialog.c new file mode 100644 index 0000000..b854c7c --- /dev/null +++ b/matchbox/mb-wm-client-dialog.c @@ -0,0 +1,360 @@ +#include "mb-wm-client-dialog.h" +#include "mb-window-manager.h" + +#include "mb-wm-theme.h" + +static Bool +mb_wm_client_dialog_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags); + +static void +mb_wm_client_dialog_theme_change (MBWindowManagerClient *client); + +static void +mb_wm_client_dialog_show (MBWindowManagerClient *client) +{ + MBWindowManagerClientClass *parent_klass = NULL; + + /* + * We need the parent of the MBWMClientDialogClass to chain up + */ + if (MB_WM_IS_CLIENT_DIALOG (client)) + parent_klass = MB_WM_CLIENT_CLASS (MB_WM_OBJECT_GET_PARENT_CLASS (client)); + else + { + /* + * A derived klass -- need to traverse the klass hierarchy to get to + * the dialog klass + */ + MBWMObjectClass * object_klass = MB_WM_OBJECT_GET_PARENT_CLASS (client); + + while (object_klass && object_klass->type != MB_WM_TYPE_CLIENT_DIALOG) + object_klass = object_klass->parent; + + if (object_klass && object_klass->parent) + parent_klass = MB_WM_CLIENT_CLASS (object_klass->parent); + } + + if (client->transient_for != NULL) + { + MBWindowManager * wm = client->wmref; + + /* + * If an attempt has been made to activate a hidden + * dialog, activate its parent app first. + * + * Note this is mainly to work with some task selectors + * ( eg the gnome one, which activates top dialog ). + */ + MBWindowManagerClient *parent = client->transient_for; + + while (parent->transient_for != NULL) + parent = parent->transient_for; + + if (parent != mb_wm_get_visible_main_client(wm)) + mb_wm_client_show (parent); + } + + if (parent_klass && parent_klass->show) + parent_klass->show(client); +} + +static void +mb_wm_client_dialog_class_init (MBWMObjectClass *klass) +{ + MBWindowManagerClientClass *client; + + MBWM_MARK(); + + client = (MBWindowManagerClientClass *)klass; + + client->client_type = MBWMClientTypeDialog; + client->geometry = mb_wm_client_dialog_request_geometry; + client->show = mb_wm_client_dialog_show; + client->theme_change = mb_wm_client_dialog_theme_change; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMClientDialog"; +#endif +} + +static void +mb_wm_client_dialog_destroy (MBWMObject *this) +{ +} + +static int +mb_wm_client_dialog_init (MBWMObject *this, va_list vap) +{ + MBWindowManagerClient *client = MB_WM_CLIENT (this); + MBWindowManager *wm = client->wmref; + MBWMClientWindow *win = client->window; + MBGeometry geom; + int n, s, w, e; + Atom actions[] = { + wm->atoms[MBWM_ATOM_NET_WM_ACTION_CLOSE], + wm->atoms[MBWM_ATOM_NET_WM_ACTION_MOVE], + }; + + XChangeProperty (wm->xdpy, win->xwindow, + wm->atoms[MBWM_ATOM_NET_WM_ALLOWED_ACTIONS], + XA_ATOM, 32, PropModeReplace, + (unsigned char *)actions, + sizeof (actions)/sizeof (actions[0])); + + + mb_wm_client_set_layout_hints (client, + LayoutPrefPositionFree | + LayoutPrefMovable | + LayoutPrefVisible); + + if (!client->window->undecorated) + { + mb_wm_theme_create_decor (wm->theme, client, MBWMDecorTypeNorth); + mb_wm_theme_create_decor (wm->theme, client, MBWMDecorTypeSouth); + mb_wm_theme_create_decor (wm->theme, client, MBWMDecorTypeWest); + mb_wm_theme_create_decor (wm->theme, client, MBWMDecorTypeEast); + } + + if (win->xwin_transient_for + && win->xwin_transient_for != win->xwindow + && win->xwin_transient_for != wm->root_win->xwindow) + { + MBWM_DBG ("Adding to '%lx' transient list", + win->xwin_transient_for); + mb_wm_client_add_transient + (mb_wm_managed_client_from_xwindow (wm, + win->xwin_transient_for), + client); + client->stacking_layer = 0; /* We stack with whatever transient too */ + } + else + { + MBWM_DBG ("Dialog is transient to root"); + /* Stack with 'always on top' */ + client->stacking_layer = MBWMStackLayerTopMid; + } + + /* center if window sets 0,0 + * Only do this for dialogs, not derived classes. + * FIXME needs to work on frame, not window. + */ + if (MB_WM_IS_CLIENT_DIALOG (client) && + client->window->geometry.x == 0 && client->window->geometry.y == 0) + { + MBGeometry avail_geom; + + mb_wm_get_display_geometry (wm, &avail_geom); + + client->window->geometry.x + = (avail_geom.width - client->window->geometry.width) / 2; + client->window->geometry.y + = (avail_geom.height - client->window->geometry.height) / 2; + } + + mb_wm_client_geometry_mark_dirty (client); + mb_wm_client_visibility_mark_dirty (client); + + if (!wm->theme) + return 1; + + /* + * Since dialogs are free-sized, they do not necessarily get a request for + * geometry from the layout manager -- we have to set the initial geometry + * here + */ + mb_wm_theme_get_decor_dimensions (wm->theme, client, &n, &s, &w, &e); + + geom.x = client->window->geometry.x; + geom.y = client->window->geometry.y; + geom.width = client->window->geometry.width + w + e; + geom.height = client->window->geometry.height + n + s; + + mb_wm_client_dialog_request_geometry (client, &geom, + MBWMClientReqGeomForced); + + return 1; +} + +int +mb_wm_client_dialog_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMClientDialogClass), + sizeof (MBWMClientDialog), + mb_wm_client_dialog_init, + mb_wm_client_dialog_destroy, + mb_wm_client_dialog_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_CLIENT_BASE, 0); + } + + return type; +} + +static Bool +mb_wm_client_dialog_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags) +{ + const MBGeometry * geom; + Bool change_pos; + Bool change_size; + + /* + * When we get an internal geometry request, like from the layout manager, + * the new geometry applies to the frame; however, if the request is + * external from ConfigureRequest, it is new geometry of the client window, + * so we need to take care to handle this right. + */ + geom = (flags & MBWMClientReqGeomIsViaConfigureReq) ? + &client->window->geometry : &client->frame_geometry; + + change_pos = (geom->x != new_geometry->x || geom->y != new_geometry->y); + + change_size = (geom->width != new_geometry->width || + geom->height != new_geometry->height); + + if (change_size) + { + int north = 0, south = 0, west = 0, east = 0; + MBWindowManager *wm = client->wmref; + + if (client->decor) + mb_wm_theme_get_decor_dimensions (wm->theme, client, + &north, &south, &west, &east); + + if (flags & MBWMClientReqGeomIsViaConfigureReq) + { + /* + * Calculate the frame size from the window size + */ + MBWM_DBG ("ConfigureRequest [%d,%d;%dx%d] -> [%d,%d;%dx%d]\n", + client->window->geometry.x, + client->window->geometry.y, + client->window->geometry.width, + client->window->geometry.height, + new_geometry->x, + new_geometry->y, + new_geometry->width, + new_geometry->height); + + client->window->geometry.x = new_geometry->x; + client->window->geometry.y = new_geometry->y; + client->window->geometry.width = new_geometry->width; + client->window->geometry.height = new_geometry->height; + + client->frame_geometry.x + = client->window->geometry.x - west; + client->frame_geometry.y + = client->window->geometry.y - north; + client->frame_geometry.width + = client->window->geometry.width + (west + east); + client->frame_geometry.height + = client->window->geometry.height + (south + north); + } + else + { + /* + * Internal request, e.g., from layout manager; work out client + * window size from the provided frame size. + */ + client->frame_geometry.x = new_geometry->x; + client->frame_geometry.y = new_geometry->y; + client->frame_geometry.width = new_geometry->width; + client->frame_geometry.height = new_geometry->height; + + client->window->geometry.x + = client->frame_geometry.x + west; + client->window->geometry.y + = client->frame_geometry.y + north; + client->window->geometry.width + = client->frame_geometry.width - (west + east); + client->window->geometry.height + = client->frame_geometry.height - (south + north); + } + + mb_wm_client_geometry_mark_dirty (client); + + return True; /* Geometry accepted */ + } + else if (change_pos) + { + /* + * Change of position only, just move both windows, no need to + * mess about with the decor. + */ + int x_diff = geom->x - new_geometry->x; + int y_diff = geom->y - new_geometry->y; + + client->frame_geometry.x -= x_diff; + client->frame_geometry.y -= y_diff; + client->window->geometry.x -= x_diff; + client->window->geometry.y -= y_diff; + + mb_wm_client_geometry_mark_dirty (client); + + return True; + } + + return True; /* Geometry accepted */ +} + +static void +mb_wm_client_dialog_theme_change (MBWindowManagerClient *client) +{ + MBWMList * l = client->decor; + + while (l) + { + MBWMDecor * d = l->data; + MBWMList * n = l->next; + + mb_wm_object_unref (MB_WM_OBJECT (d)); + free (l); + + l = n; + } + + client->decor = NULL; + + if (!client->window->undecorated) + { + mb_wm_theme_create_decor (client->wmref->theme, + client, MBWMDecorTypeNorth); + + mb_wm_theme_create_decor (client->wmref->theme, + client, MBWMDecorTypeSouth); + + mb_wm_theme_create_decor (client->wmref->theme, + client, MBWMDecorTypeWest); + + mb_wm_theme_create_decor (client->wmref->theme, + client, MBWMDecorTypeEast); + } + + mb_wm_client_geometry_mark_dirty (client); + mb_wm_client_visibility_mark_dirty (client); +} + + +MBWindowManagerClient* +mb_wm_client_dialog_new (MBWindowManager *wm, MBWMClientWindow *win) +{ + MBWindowManagerClient *client; + + client + = MB_WM_CLIENT(mb_wm_object_new (MB_WM_TYPE_CLIENT_DIALOG, + MBWMObjectPropWm, wm, + MBWMObjectPropClientWindow, win, + NULL)); + + return client; +} + diff --git a/matchbox/mb-wm-client-dialog.h b/matchbox/mb-wm-client-dialog.h new file mode 100644 index 0000000..c22ec2c --- /dev/null +++ b/matchbox/mb-wm-client-dialog.h @@ -0,0 +1,52 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_CLIENT_DIALOG_H +#define _HAVE_MB_CLIENT_DIALOG_H + +#include <matchbox/mb-wm-client-base.h> +#include <matchbox/mb-wm-decor.h> + +#define MB_WM_CLIENT_DIALOG(c) ((MBWMClientDialog*)(c)) +#define MB_WM_CLIENT_DIALOG_CLASS(c) ((MBWMClientDialogClass*)(c)) +#define MB_WM_TYPE_CLIENT_DIALOG (mb_wm_client_dialog_class_type ()) +#define MB_WM_IS_CLIENT_DIALOG(c) (MB_WM_OBJECT_TYPE(c)==MB_WM_TYPE_CLIENT_DIALOG) + +typedef struct MBWMClientDialog MBWMClientDialog; +typedef struct MBWMClientDialogClass MBWMClientDialogClass; + +struct MBWMClientDialog +{ + MBWMClientBase parent; + MBWMDecorButton *button_close; +}; + +struct MBWMClientDialogClass +{ + MBWMClientBaseClass parent; +}; + +MBWindowManagerClient* +mb_wm_client_dialog_new (MBWindowManager *wm, MBWMClientWindow *win); + +int +mb_wm_client_dialog_class_type (); + +#endif diff --git a/matchbox/mb-wm-client-input.c b/matchbox/mb-wm-client-input.c new file mode 100644 index 0000000..3a63d40 --- /dev/null +++ b/matchbox/mb-wm-client-input.c @@ -0,0 +1,184 @@ +#include "mb-wm-client-input.h" + +static void +mb_wm_client_input_detransitise (MBWindowManagerClient *client); + +static void +mb_wm_client_input_realize (MBWindowManagerClient *client); + +static Bool +mb_wm_client_input_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags); + +static void +mb_wm_client_input_stack (MBWindowManagerClient *client, int flags); + +static void +mb_wm_client_input_class_init (MBWMObjectClass *klass) +{ + MBWindowManagerClientClass *client; + MBWMClientInputClass * client_input; + + MBWM_MARK(); + + client = (MBWindowManagerClientClass *)klass; + client_input = (MBWMClientInputClass *)klass; + + MBWM_DBG("client->stack is %p", client->stack); + + client->client_type = MBWMClientTypeInput; + client->geometry = mb_wm_client_input_request_geometry; + client->stack = mb_wm_client_input_stack; + client->realize = mb_wm_client_input_realize; + client->detransitise = mb_wm_client_input_detransitise; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMClientInput"; +#endif +} + +static void +mb_wm_client_input_destroy (MBWMObject *this) +{ +} + +static int +mb_wm_client_input_init (MBWMObject *this, va_list vap) +{ + MBWMClientInput *client_input = MB_WM_CLIENT_INPUT (this); + MBWindowManagerClient *client = MB_WM_CLIENT (this); + MBWindowManager *wm = client->wmref; + MBWMClientWindow *win = client->window; + + if (win->xwin_transient_for + && win->xwin_transient_for != win->xwindow + && win->xwin_transient_for != wm->root_win->xwindow) + { + MBWindowManagerClient * t = + mb_wm_managed_client_from_xwindow (wm, + win->xwin_transient_for); + + mb_wm_client_get_coverage (t, & client_input->transient_geom); + + MBWM_DBG ("Adding to '%lx' transient list", win->xwin_transient_for); + mb_wm_client_add_transient (t, client); + client->stacking_layer = 0; /* We stack with whatever transient too */ + } + else + { + MBWM_DBG ("Input is transient to root"); + /* Stack with 'always on top' */ + client->stacking_layer = MBWMStackLayerTopMid; + } + + mb_wm_client_set_layout_hints (client, + LayoutPrefReserveSouth|LayoutPrefVisible); + + return 1; +} + +int +mb_wm_client_input_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMClientInputClass), + sizeof (MBWMClientInput), + mb_wm_client_input_init, + mb_wm_client_input_destroy, + mb_wm_client_input_class_init + }; + type = mb_wm_object_register_class (&info, MB_WM_TYPE_CLIENT_BASE, 0); + } + + return type; +} + +static void +mb_wm_client_input_realize (MBWindowManagerClient *client) +{ + /* + * Must reparent the window to our root, otherwise we restacking of + * pre-existing windows might fail. + */ + XReparentWindow(client->wmref->xdpy, MB_WM_CLIENT_XWIN(client), + client->wmref->root_win->xwindow, 0, 0); + + return; +} + +static Bool +mb_wm_client_input_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags) +{ + if (flags & + (MBWMClientReqGeomIsViaLayoutManager|MBWMClientReqGeomIsViaConfigureReq)) + { + client->frame_geometry.x = new_geometry->x; + client->frame_geometry.y = new_geometry->y; + client->frame_geometry.width = new_geometry->width; + client->frame_geometry.height = new_geometry->height; + + client->window->geometry.x = client->frame_geometry.x; + client->window->geometry.y = client->frame_geometry.y; + client->window->geometry.width = client->frame_geometry.width; + client->window->geometry.height = client->frame_geometry.height; + + mb_wm_client_geometry_mark_dirty (client); + + return True; /* Geometry accepted */ + } + return False; +} + +static void +mb_wm_client_input_stack (MBWindowManagerClient *client, int flags) +{ + MBWM_MARK(); + + mb_wm_stack_move_client_above_type (client, + MBWMClientTypeApp|MBWMClientTypeDesktop); +} + +MBWindowManagerClient* +mb_wm_client_input_new (MBWindowManager *wm, MBWMClientWindow *win) +{ + MBWindowManagerClient *client; + + client = MB_WM_CLIENT(mb_wm_object_new (MB_WM_TYPE_CLIENT_INPUT, + MBWMObjectPropWm, wm, + MBWMObjectPropClientWindow, win, + NULL)); + + return client; +} + +static void +mb_wm_client_input_detransitise (MBWindowManagerClient *client) +{ + MBWMClientInput * input = MB_WM_CLIENT_INPUT (client); + + /* + * If the IM is not transient, or it is transient for something other + * than an application or dialog, just return from here. + * + * If it is transient for an app or dialog, adjust the geometry accordingly. + */ + if (!client->transient_for || + MB_WM_CLIENT_CLIENT_TYPE (client->transient_for) != + (MBWMClientTypeApp | MBWMClientTypeDialog)) + return; + + if (input->transient_geom.width && input->transient_geom.height) + { + mb_wm_client_request_geometry (client->transient_for, + &input->transient_geom, + MBWMClientReqGeomForced); + } +} + diff --git a/matchbox/mb-wm-client-input.h b/matchbox/mb-wm-client-input.h new file mode 100644 index 0000000..ed15a56 --- /dev/null +++ b/matchbox/mb-wm-client-input.h @@ -0,0 +1,52 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_CLIENT_INPUT_H +#define _HAVE_MB_CLIENT_INPUT_H + +#include <matchbox/matchbox.h> + +typedef struct MBWMClientInput MBWMClientInput; +typedef struct MBWMClientInputClass MBWMClientInputClass; + +#define MB_WM_CLIENT_INPUT(c) ((MBWMClientInput*)(c)) +#define MB_WM_CLIENT_INPUT_CLASS(c) ((MBWMClientInputClass*)(c)) +#define MB_WM_TYPE_CLIENT_INPUT (mb_wm_client_input_class_type ()) +#define MB_WM_IS_CLIENT_INPUT(c) (MB_WM_OBJECT_TYPE(c)==MB_WM_TYPE_CLIENT_INPUT) + +struct MBWMClientInput +{ + MBWMClientBase parent; + + MBGeometry transient_geom; +}; + +struct MBWMClientInputClass +{ + MBWMClientBaseClass parent; +}; + +MBWindowManagerClient* +mb_wm_client_input_new (MBWindowManager *wm, MBWMClientWindow *win); + +int +mb_wm_client_input_class_type (); + +#endif diff --git a/matchbox/mb-wm-client-menu.c b/matchbox/mb-wm-client-menu.c new file mode 100644 index 0000000..b68f771 --- /dev/null +++ b/matchbox/mb-wm-client-menu.c @@ -0,0 +1,156 @@ +#include "mb-wm-client-menu.h" + +#include "mb-wm-theme.h" + +static Bool +mb_wm_client_menu_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags); + +static void +mb_wm_client_menu_realize (MBWindowManagerClient *client) +{ + /* + * Must reparent the window to our root, otherwise we restacking of + * pre-existing windows might fail. + */ + XReparentWindow(client->wmref->xdpy, MB_WM_CLIENT_XWIN(client), + client->wmref->root_win->xwindow, 0, 0); +} + +static void +mb_wm_client_menu_class_init (MBWMObjectClass *klass) +{ + MBWindowManagerClientClass *client; + + MBWM_MARK(); + + client = (MBWindowManagerClientClass *)klass; + + client->client_type = MBWMClientTypeMenu; + client->geometry = mb_wm_client_menu_request_geometry; + client->realize = mb_wm_client_menu_realize; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMClientMenu"; +#endif +} + +static void +mb_wm_client_menu_destroy (MBWMObject *this) +{ +} + +static int +mb_wm_client_menu_init (MBWMObject *this, va_list vap) +{ + MBWindowManagerClient *client = MB_WM_CLIENT (this); + MBWindowManager *wm = client->wmref; + MBWMClientWindow *win = client->window; + Atom actions[] = { + wm->atoms[MBWM_ATOM_NET_WM_ACTION_CLOSE], + wm->atoms[MBWM_ATOM_NET_WM_ACTION_MOVE], + wm->atoms[MBWM_ATOM_NET_WM_ACTION_RESIZE], + }; + + XChangeProperty (wm->xdpy, win->xwindow, + wm->atoms[MBWM_ATOM_NET_WM_ALLOWED_ACTIONS], + XA_ATOM, 32, PropModeReplace, + (unsigned char *)actions, + sizeof (actions)/sizeof (actions[0])); + + mb_wm_client_set_layout_hints (client, + LayoutPrefPositionFree|LayoutPrefVisible| + LayoutPrefFixedX|LayoutPrefFixedY); + + /* + * Stack menus on the top of the stacking order, regardless of whether they + * declare themselves transient or not. + * + * (Gtk menus do kbd and pointer grabs and do not take kindly to being + * restacked.) + */ +#if 0 + if (win->xwin_transient_for + && win->xwin_transient_for != win->xwindow + && win->xwin_transient_for != wm->root_win->xwindow) + { + MBWM_DBG ("Adding to '%lx' transient list", + win->xwin_transient_for); + mb_wm_client_add_transient + (mb_wm_managed_client_from_xwindow (wm, + win->xwin_transient_for), + client); + client->stacking_layer = 0; /* We stack with whatever transient too */ + } + else + { + MBWM_DBG ("Menu is transient to root"); + /* Stack with 'always on top' */ + client->stacking_layer = MBWMStackLayerTopMid; + } +#else + client->stacking_layer = MBWMStackLayerTop; +#endif + + return 1; +} + +int +mb_wm_client_menu_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMClientMenuClass), + sizeof (MBWMClientMenu), + mb_wm_client_menu_init, + mb_wm_client_menu_destroy, + mb_wm_client_menu_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_CLIENT_BASE, 0); + } + + return type; +} + +static Bool +mb_wm_client_menu_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags) +{ + if (client->window->geometry.x != new_geometry->x + || client->window->geometry.y != new_geometry->y + || client->window->geometry.width != new_geometry->width + || client->window->geometry.height != new_geometry->height) + { + client->window->geometry.x = new_geometry->x; + client->window->geometry.y = new_geometry->y; + client->window->geometry.width = new_geometry->width; + client->window->geometry.height = new_geometry->height; + + mb_wm_client_geometry_mark_dirty (client); + + return True; /* Geometry accepted */ + } + + return True; /* Geometry accepted */ +} + +MBWindowManagerClient* +mb_wm_client_menu_new (MBWindowManager *wm, MBWMClientWindow *win) +{ + MBWindowManagerClient *client; + + client + = MB_WM_CLIENT(mb_wm_object_new (MB_WM_TYPE_CLIENT_MENU, + MBWMObjectPropWm, wm, + MBWMObjectPropClientWindow, win, + NULL)); + + return client; +} + diff --git a/matchbox/mb-wm-client-menu.h b/matchbox/mb-wm-client-menu.h new file mode 100644 index 0000000..8d2f1c6 --- /dev/null +++ b/matchbox/mb-wm-client-menu.h @@ -0,0 +1,50 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_CLIENT_MENU_H +#define _HAVE_MB_CLIENT_MENU_H + +#include <matchbox/matchbox.h> + +typedef struct MBWMClientMenu MBWMClientMenu; +typedef struct MBWMClientMenuClass MBWMClientMenuClass; + +#define MB_WM_CLIENT_MENU(c) ((MBWMClientMenu*)(c)) +#define MB_WM_CLIENT_MENU_CLASS(c) ((MBWMClientMenuClass*)(c)) +#define MB_WM_TYPE_CLIENT_MENU (mb_wm_client_menu_class_type ()) +#define MB_WM_IS_CLIENT_MENU(c) (MB_WM_OBJECT_TYPE(c)==MB_WM_TYPE_CLIENT_MENU) + +struct MBWMClientMenu +{ + MBWMClientBase parent; +}; + +struct MBWMClientMenuClass +{ + MBWMClientBaseClass parent; +}; + +MBWindowManagerClient* +mb_wm_client_menu_new (MBWindowManager *wm, MBWMClientWindow *win); + +int +mb_wm_client_menu_class_type (); + +#endif diff --git a/matchbox/mb-wm-client-note.c b/matchbox/mb-wm-client-note.c new file mode 100644 index 0000000..d0bb770 --- /dev/null +++ b/matchbox/mb-wm-client-note.c @@ -0,0 +1,153 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2007 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-client-note.h" + +#include "mb-wm-theme.h" + +static void +mb_wm_client_note_class_init (MBWMObjectClass *klass) +{ + MBWindowManagerClientClass *client; + + MBWM_MARK(); + + client = (MBWindowManagerClientClass *)klass; + + client->client_type = MBWMClientTypeNote; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMClientNote"; +#endif +} + +static void +mb_wm_client_note_destroy (MBWMObject *this) +{ +} + +static int +mb_wm_client_note_init (MBWMObject *this, va_list vap) +{ + MBWindowManagerClient *client = MB_WM_CLIENT (this); + MBWindowManager *wm = client->wmref; + MBWMClientWindow *win = client->window; + MBGeometry geom; + Bool change = False; + Atom actions[] = { + wm->atoms[MBWM_ATOM_NET_WM_ACTION_CLOSE], + wm->atoms[MBWM_ATOM_NET_WM_ACTION_MOVE], + }; + + geom = client->frame_geometry; + + if (geom.x >= wm->xdpy_width) + { + geom.x = wm->xdpy_width - geom.width; + change = True; + } + + if (geom.y >= wm->xdpy_height) + { + geom.y = wm->xdpy_height - geom.height; + change = True; + } + + switch (win->gravity) + { + case SouthGravity: + geom.y = wm->xdpy_height - geom.height; + change = True; + break; + case EastGravity: + geom.x = wm->xdpy_width - geom.width; + change = True; + break; + case SouthEastGravity: + geom.y = wm->xdpy_height - geom.height; + geom.x = wm->xdpy_width - geom.width; + change = True; + break; + case NorthGravity: + geom.y = 0; + change = True; + break; + case WestGravity: + geom.x = 0; + change = True; + break; + case NorthWestGravity: + case CenterGravity: + case StaticGravity: + default: + ; + } + + if (change) + { + mb_wm_client_request_geometry (client, &geom, MBWMClientReqGeomForced); + mb_wm_client_geometry_mark_dirty (client); + } + + + XChangeProperty (wm->xdpy, win->xwindow, + wm->atoms[MBWM_ATOM_NET_WM_ALLOWED_ACTIONS], + XA_ATOM, 32, PropModeReplace, + (unsigned char *)actions, + sizeof (actions)/sizeof (actions[0])); + + return 1; +} + +int +mb_wm_client_note_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMClientNoteClass), + sizeof (MBWMClientNote), + mb_wm_client_note_init, + mb_wm_client_note_destroy, + mb_wm_client_note_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_CLIENT_DIALOG, 0); + } + + return type; +} + +MBWindowManagerClient* +mb_wm_client_note_new (MBWindowManager *wm, MBWMClientWindow *win) +{ + MBWindowManagerClient *client; + + client + = MB_WM_CLIENT (mb_wm_object_new (MB_WM_TYPE_CLIENT_NOTE, + MBWMObjectPropWm, wm, + MBWMObjectPropClientWindow, win, + NULL)); + + return client; +} + diff --git a/matchbox/mb-wm-client-note.h b/matchbox/mb-wm-client-note.h new file mode 100644 index 0000000..2b12cb9 --- /dev/null +++ b/matchbox/mb-wm-client-note.h @@ -0,0 +1,51 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2007 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. + * + */ + +#ifndef _HAVE_MB_CLIENT_NOTE_H +#define _HAVE_MB_CLIENT_NOTE_H + +#include <matchbox/matchbox.h> +#include <matchbox/mb-wm-client-dialog.h> + +typedef struct MBWMClientNote MBWMClientNote; +typedef struct MBWMClientNoteClass MBWMClientNoteClass; + +#define MB_WM_CLIENT_NOTE(c) ((MBWMClientNote*)(c)) +#define MB_WM_CLIENT_NOTE_CLASS(c) ((MBWMClientNoteClass*)(c)) +#define MB_WM_TYPE_CLIENT_NOTE (mb_wm_client_note_class_type ()) +#define MB_WM_IS_CLIENT_NOTE(c) (MB_WM_OBJECT_TYPE(c)==MB_WM_TYPE_CLIENT_NOTE) + +struct MBWMClientNote +{ + MBWMClientDialog parent; +}; + +struct MBWMClientNoteClass +{ + MBWMClientDialogClass parent; +}; + +MBWindowManagerClient* +mb_wm_client_note_new (MBWindowManager *wm, MBWMClientWindow *win); + +int +mb_wm_client_note_class_type (); + +#endif diff --git a/matchbox/mb-wm-client-override.c b/matchbox/mb-wm-client-override.c new file mode 100644 index 0000000..238ce73 --- /dev/null +++ b/matchbox/mb-wm-client-override.c @@ -0,0 +1,124 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2007 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-client-override.h" + +#include "mb-wm-theme.h" + +static void +mb_wm_client_override_stack (MBWindowManagerClient *client, + int flags) +{ + MBWMList * t = mb_wm_client_get_transients (client); + + mb_wm_stack_move_top(client); + + mb_wm_util_list_foreach (t, (MBWMListForEachCB)mb_wm_client_stack, + (void*)flags); + + mb_wm_util_list_free (t); +} + +static void +mb_wm_client_override_class_init (MBWMObjectClass *klass) +{ + MBWindowManagerClientClass *client; + + MBWM_MARK(); + + client = (MBWindowManagerClientClass *)klass; + + client->client_type = MBWMClientTypeOverride; + client->stack = mb_wm_client_override_stack; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMClientOverride"; +#endif +} + +static void +mb_wm_client_override_destroy (MBWMObject *this) +{ +} + +static int +mb_wm_client_override_init (MBWMObject *this, va_list vap) +{ + MBWindowManagerClient *client = MB_WM_CLIENT (this); + MBWindowManager *wm = client->wmref; + MBWMClientWindow *win = client->window; + + if (win->xwin_transient_for + && win->xwin_transient_for != win->xwindow + && win->xwin_transient_for != wm->root_win->xwindow) + { + MBWM_DBG ("Adding to '%lx' transient list", + win->xwin_transient_for); + mb_wm_client_add_transient + (mb_wm_managed_client_from_xwindow (wm, + win->xwin_transient_for), + client); + client->stacking_layer = 0; /* We stack with whatever transient too */ + } + else + { + MBWM_DBG ("Override is transient to root or intransient"); + /* Stack with 'always on top' */ + client->stacking_layer = MBWMStackLayerTop; + } + + return 1; +} + +int +mb_wm_client_override_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMClientOverrideClass), + sizeof (MBWMClientOverride), + mb_wm_client_override_init, + mb_wm_client_override_destroy, + mb_wm_client_override_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_CLIENT, 0); + } + + return type; +} + +MBWindowManagerClient* +mb_wm_client_override_new (MBWindowManager *wm, MBWMClientWindow *win) +{ + MBWindowManagerClient *client; + + client + = MB_WM_CLIENT(mb_wm_object_new (MB_WM_TYPE_CLIENT_OVERRIDE, + MBWMObjectPropWm, wm, + MBWMObjectPropClientWindow, win, + NULL)); + + return client; +} + diff --git a/matchbox/mb-wm-client-override.h b/matchbox/mb-wm-client-override.h new file mode 100644 index 0000000..2f155b4 --- /dev/null +++ b/matchbox/mb-wm-client-override.h @@ -0,0 +1,50 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2007 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. + * + */ + +#ifndef _HAVE_MB_CLIENT_OVERRIDE_H +#define _HAVE_MB_CLIENT_OVERRIDE_H + +#include <matchbox/matchbox.h> + +typedef struct MBWMClientOverride MBWMClientOverride; +typedef struct MBWMClientOverrideClass MBWMClientOverrideClass; + +#define MB_WM_CLIENT_OVERRIDE(c) ((MBWMClientOverride*)(c)) +#define MB_WM_CLIENT_OVERRIDE_CLASS(c) ((MBWMClientOverrideClass*)(c)) +#define MB_WM_TYPE_CLIENT_OVERRIDE (mb_wm_client_override_class_type ()) +#define MB_WM_IS_CLIENT_OVERRIDE(c) (MB_WM_OBJECT_TYPE(c)==MB_WM_TYPE_CLIENT_OVERRIDE) + +struct MBWMClientOverride +{ + MBWindowManagerClient parent; +}; + +struct MBWMClientOverrideClass +{ + MBWindowManagerClientClass parent; +}; + +MBWindowManagerClient* +mb_wm_client_override_new (MBWindowManager *wm, MBWMClientWindow *win); + +int +mb_wm_client_override_class_type (); + +#endif diff --git a/matchbox/mb-wm-client-panel.c b/matchbox/mb-wm-client-panel.c new file mode 100644 index 0000000..c915d6b --- /dev/null +++ b/matchbox/mb-wm-client-panel.c @@ -0,0 +1,229 @@ +#include "mb-wm-client-panel.h" +#include "mb-wm-theme.h" + +static void +mb_wm_client_panel_realize (MBWindowManagerClient *client); + +static Bool +mb_wm_client_panel_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags); + +static MBWMStackLayerType +mb_wm_client_panel_stacking_layer (MBWindowManagerClient *client); + +static void +mb_wm_client_panel_stack (MBWindowManagerClient *client, int flags); + +static void +mb_wm_client_panel_class_init (MBWMObjectClass *klass) +{ + MBWindowManagerClientClass *client; + + client = (MBWindowManagerClientClass *)klass; + + client->client_type = MBWMClientTypePanel; + + client->realize = mb_wm_client_panel_realize; + client->geometry = mb_wm_client_panel_request_geometry; + client->stacking_layer = mb_wm_client_panel_stacking_layer; + client->stack = mb_wm_client_panel_stack; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMClientPanel"; +#endif +} + +static int +mb_wm_client_panel_init (MBWMObject *this, va_list vap) +{ + MBWindowManagerClient * client = MB_WM_CLIENT (this); + MBGeometry * win_geo = &client->window->geometry; + MBGeometry geom; + MBWMTheme * theme = client->wmref->theme; + + if (theme && mb_wm_theme_get_client_geometry (theme, client, &geom)) + { + /* The theme prescribes geometry for the panel, resize the + * window accordingly + */ + MBWMClientWindow * win = client->window; + MBWindowManager * wm = client->wmref; + int g_x, g_y, x, y, w, h; + unsigned int g_w, g_h, g_bw, g_d; + + x = geom.x; + y = geom.y; + w = geom.width; + h = geom.height; + + /* If some of the theme values are unset, we have to get the current + * values for the window in their place -- this is a round trip, so + * we only do this when necessary + */ + if (x < 0 || y < 0 || w < 0 || h < 0) + { + Window root; + XGetGeometry (wm->xdpy, win->xwindow, + &root, &g_x, &g_y, &g_w, &g_h, &g_bw, &g_d); + } + + if (x < 0) + x = g_x; + + if (y < 0) + y = g_y; + + if (w < 0) + w = g_w; + + if (h < 0) + h = g_h; + + win->geometry.x = x; + win->geometry.y = y; + win->geometry.width = w; + win->geometry.height = h; + + mb_wm_client_geometry_mark_dirty (client); + } + + if (!client->layout_hints) + { + /* + * The theme did not prescribe layout hints, work something out + */ + MBWMClientLayoutHints hints = LayoutPrefVisible; + + if (win_geo->width > win_geo->height) + if (win_geo->y < (client->wmref->xdpy_height/2)) + hints |= LayoutPrefReserveEdgeNorth; + else + hints |= LayoutPrefReserveEdgeSouth; + else + if (win_geo->x < (client->wmref->xdpy_width/2)) + hints |= LayoutPrefReserveEdgeWest; + else + hints |= LayoutPrefReserveEdgeEast; + + mb_wm_client_set_layout_hints (client, hints); + } + else + { + /* Make sure we are marked as visible */ + mb_wm_client_set_layout_hint (client, LayoutPrefVisible, True); + } + + if (client->layout_hints & LayoutPrefOverlaps) + client->stacking_layer = MBWMStackLayerTopMid; + else + client->stacking_layer = MBWMStackLayerBottomMid; + + return 1; +} + +static void +mb_wm_client_panel_destroy (MBWMObject *this) +{ +} + +int +mb_wm_client_panel_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMClientPanelClass), + sizeof (MBWMClientPanel), + mb_wm_client_panel_init, + mb_wm_client_panel_destroy, + mb_wm_client_panel_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_CLIENT_BASE, 0); + } + + return type; +} + +static void +mb_wm_client_panel_realize (MBWindowManagerClient *client) +{ + /* + * Must reparent the window to our root, otherwise we restacking of + * pre-existing windows might fail. + */ + XReparentWindow(client->wmref->xdpy, MB_WM_CLIENT_XWIN(client), + client->wmref->root_win->xwindow, 0, 0); + + return; +} + +static Bool +mb_wm_client_panel_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags) +{ + if (client->window->geometry.x != new_geometry->x + || client->window->geometry.y != new_geometry->y + || client->window->geometry.width != new_geometry->width + || client->window->geometry.height != new_geometry->height) + { + client->window->geometry.x = new_geometry->x; + client->window->geometry.y = new_geometry->y; + client->window->geometry.width = new_geometry->width; + client->window->geometry.height = new_geometry->height; + + mb_wm_client_geometry_mark_dirty (client); + } + + return True; +} + +static void +mb_wm_client_panel_stack (MBWindowManagerClient *client, int flags) +{ + /* + * If this is 'normal' panel, we stack with the default value. + * + * If this is an overlaping panel, stack immediately above the top-level + * application. + */ + if (!(client->layout_hints & LayoutPrefOverlaps)) + { + mb_wm_stack_move_top (client); + return; + } + + mb_wm_stack_move_client_above_type (client, + MBWMClientTypeApp|MBWMClientTypeDesktop); +} + +static MBWMStackLayerType +mb_wm_client_panel_stacking_layer (MBWindowManagerClient *client) +{ + /* + * If we are showing desktop, ensure that we stack above it. + */ + if (client->wmref->flags & MBWindowManagerFlagDesktop) + return MBWMStackLayerTopMid; + + return client->stacking_layer; +} + +MBWindowManagerClient* +mb_wm_client_panel_new(MBWindowManager *wm, MBWMClientWindow *win) +{ + MBWindowManagerClient *client = NULL; + + client = MB_WM_CLIENT(mb_wm_object_new (MB_WM_TYPE_CLIENT_PANEL, + MBWMObjectPropWm, wm, + MBWMObjectPropClientWindow, win, + NULL)); + + return client; +} + + diff --git a/matchbox/mb-wm-client-panel.h b/matchbox/mb-wm-client-panel.h new file mode 100644 index 0000000..42096be --- /dev/null +++ b/matchbox/mb-wm-client-panel.h @@ -0,0 +1,51 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_CLIENT_PANEL_H +#define _HAVE_MB_CLIENT_PANEL_H + +#include <matchbox/matchbox.h> + +typedef struct MBWMClientPanel MBWMClientPanel; +typedef struct MBWMClientPanelClass MBWMClientPanelClass; + +#define MB_WM_CLIENT_PANEL(c) ((MBWMClientPanel*)(c)) +#define MB_WM_CLIENT_PANEL_CLASS(c) ((MBWMClientPanelClass*)(c)) +#define MB_WM_TYPE_CLIENT_PANEL (mb_wm_client_panel_class_type ()) +#define MB_WM_IS_CLIENT_PANEL(c) \ + (MB_WM_OBJECT_TYPE(c)==MB_WM_TYPE_CLIENT_PANEL) + +struct MBWMClientPanel +{ + MBWMClientBase parent; +}; + +struct MBWMClientPanelClass +{ + MBWMClientBaseClass parent; +}; + +MBWindowManagerClient* +mb_wm_client_panel_new(MBWindowManager *wm, MBWMClientWindow *win); + +int +mb_wm_client_panel_class_type (); + +#endif diff --git a/matchbox/mb-wm-client-window.c b/matchbox/mb-wm-client-window.c new file mode 100644 index 0000000..85c7c17 --- /dev/null +++ b/matchbox/mb-wm-client-window.c @@ -0,0 +1,978 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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 "matchbox.h" + +/* Via Xatomtype.h */ +#define NumPropWMHintsElements 9 /* number of elements in this structure */ +#define NumPropWMSizeHintsElements 18 + +enum { + COOKIE_WIN_TYPE = 0, + COOKIE_WIN_ATTR, + COOKIE_WIN_GEOM, + COOKIE_WIN_NAME, + COOKIE_WIN_NAME_UTF8, + COOKIE_WIN_WM_HINTS, + COOKIE_WIN_WM_NORMAL_HINTS, + COOKIE_WIN_TRANSIENCY, + COOKIE_WIN_PROTOS, + COOKIE_WIN_MACHINE, + COOKIE_WIN_PID, + COOKIE_WIN_ICON, + COOKIE_WIN_ACTIONS, + COOKIE_WIN_USER_TIME, + COOKIE_WIN_CM_TRANSLUCENCY, + COOKIE_WIN_NET_STATE, + COOKIE_WIN_MWM_HINTS, + + N_COOKIES +}; + +#define PROP_MOTIF_WM_HINTS_ELEMENTS 5 +#define MWM_HINTS_DECORATIONS (1L << 1) + +#define MWM_DECOR_BORDER (1L << 1) +#define MWM_DECOR_RESIZEH (1L << 2) +#define MWM_DECOR_TITLE (1L << 3) +#define MWM_DECOR_MENU (1L << 4) +#define MWM_DECOR_MINIMIZE (1L << 5) +#define MWM_DECOR_MAXIMIZE (1L << 6) + +static void +mb_wm_client_window_class_init (MBWMObjectClass *klass) +{ + MBWMClientWindowClass *rw_class; + + MBWM_MARK(); + + rw_class = (MBWMClientWindowClass *)klass; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMClientWindow"; +#endif +} + +static void +mb_wm_client_window_destroy (MBWMObject *this) +{ + MBWMClientWindow * win = MB_WM_CLIENT_WINDOW (this); + MBWMList * l = win->icons; + + if (win->name) + XFree (win->name); + + if (win->machine) + XFree (win->machine); + + while (l) + { + mb_wm_rgba_icon_free ((MBWMRgbaIcon *)l->data); + l = l->next; + } +} + +static int +mb_wm_client_window_init (MBWMObject *this, va_list vap) +{ + MBWMClientWindow *win = MB_WM_CLIENT_WINDOW (this); + MBWMObjectProp prop; + MBWindowManager *wm = NULL; + Window xwin = None; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + switch (prop) + { + case MBWMObjectPropWm: + wm = va_arg(vap, MBWindowManager *); + break; + case MBWMObjectPropXwindow: + xwin = va_arg(vap, Window); + break; + default: + MBWMO_PROP_EAT (vap, prop); + } + + prop = va_arg(vap, MBWMObjectProp); + } + + if (!wm || !xwin) + { + fprintf (stderr, "--- error, no wm or xwin ---\n"); + return 0; + } + + win->xwindow = xwin; + win->wm = wm; + + mb_wm_client_window_sync_properties (win, MBWM_WINDOW_PROP_ALL); + + return 1; +} + +int +mb_wm_client_window_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMClientWindowClass), + sizeof (MBWMClientWindow), + mb_wm_client_window_init, + mb_wm_client_window_destroy, + mb_wm_client_window_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); + } + + return type; +} + +MBWMClientWindow* +mb_wm_client_window_new (MBWindowManager *wm, Window xwin) +{ + MBWMClientWindow *win; + + win = MB_WM_CLIENT_WINDOW (mb_wm_object_new (MB_WM_TYPE_CLIENT_WINDOW, + MBWMObjectPropWm, wm, + MBWMObjectPropXwindow, xwin, + NULL)); + return win; +} + +/* + * Creates MBWMIcon from raw _NET_WM_ICON property data, returning + * pointer to where the next icon might be in the data + */ +static unsigned long * +icon_from_net_wm_icon (unsigned long * data, MBWMRgbaIcon ** mb_icon) +{ + MBWMRgbaIcon * icon = mb_wm_rgba_icon_new (); + size_t byte_len; + + *mb_icon = icon; + + if (!icon) + return data; + + icon->width = *data++; + icon->height = *data++; + + byte_len = sizeof (unsigned long) * icon->width * icon->height; + + icon->pixels = malloc (byte_len); + + if (!icon->pixels) + { + mb_wm_rgba_icon_free (icon); + *mb_icon = NULL; + return (data - 2); + } + + memcpy (icon->pixels, data, byte_len); + + MBWM_DBG("@@@ Icon %d x %d @@@", icon->width, icon->height); + + return (data + icon->width * icon->height); +} + +Bool +mb_wm_client_window_sync_properties ( MBWMClientWindow *win, + unsigned long props_req) +{ + MBWMCookie cookies[N_COOKIES]; + MBWindowManager *wm = win->wm; + Atom actual_type_return, *result_atom = NULL; + int actual_format_return; + unsigned long nitems_return; + unsigned long bytes_after_return; + unsigned int foo; + int x_error_code; + Window xwin; + int changes = 0; + + MBWMClientWindowAttributes *xwin_attr = NULL; + + xwin = win->xwindow; + + if (props_req & MBWM_WINDOW_PROP_WIN_TYPE) + cookies[COOKIE_WIN_TYPE] + = mb_wm_property_atom_req(wm, xwin, + wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE]); + + if (props_req & MBWM_WINDOW_PROP_NET_STATE) + cookies[COOKIE_WIN_NET_STATE] + = mb_wm_property_atom_req(wm, xwin, + wm->atoms[MBWM_ATOM_NET_WM_STATE]); + + if (props_req & MBWM_WINDOW_PROP_ATTR) + cookies[COOKIE_WIN_ATTR] + = mb_wm_xwin_get_attributes (wm, xwin); + + if (props_req & MBWM_WINDOW_PROP_GEOMETRY) + cookies[COOKIE_WIN_GEOM] + = mb_wm_xwin_get_geometry (wm, (Drawable)xwin); + + if (props_req & MBWM_WINDOW_PROP_NAME) + { + cookies[COOKIE_WIN_NAME] + = mb_wm_property_req (wm, + xwin, + wm->atoms[MBWM_ATOM_WM_NAME], + 0, + 2048L, + False, + XA_STRING); + + cookies[COOKIE_WIN_NAME_UTF8] = + mb_wm_property_utf8_req(wm, xwin, + wm->atoms[MBWM_ATOM_NET_WM_NAME]); + } + + if (props_req & MBWM_WINDOW_PROP_WM_HINTS) + { + cookies[COOKIE_WIN_WM_HINTS] + = mb_wm_property_req (wm, + xwin, + wm->atoms[MBWM_ATOM_WM_HINTS], + 0, + 1024L, + False, + XA_WM_HINTS); + } + + if (props_req & MBWM_WINDOW_PROP_WM_NORMAL_HINTS) + { + cookies[COOKIE_WIN_WM_NORMAL_HINTS] + = mb_wm_property_req (wm, + xwin, + wm->atoms[MBWM_ATOM_WM_NORMAL_HINTS], + 0, + 1024L, + False, + XA_WM_SIZE_HINTS); + } + + if (props_req & MBWM_WINDOW_PROP_MWM_HINTS) + { + cookies[COOKIE_WIN_MWM_HINTS] + = mb_wm_property_req (wm, + xwin, + wm->atoms[MBWM_ATOM_MOTIF_WM_HINTS], + 0, + PROP_MOTIF_WM_HINTS_ELEMENTS, + False, + wm->atoms[MBWM_ATOM_MOTIF_WM_HINTS]); + } + + if (props_req & MBWM_WINDOW_PROP_TRANSIENCY) + { + cookies[COOKIE_WIN_TRANSIENCY] + = mb_wm_property_req (wm, + xwin, + wm->atoms[MBWM_ATOM_WM_TRANSIENT_FOR], + 0, + 1L, + False, + XA_WINDOW); + } + + if (props_req & MBWM_WINDOW_PROP_PROTOS) + { + cookies[COOKIE_WIN_PROTOS] + = mb_wm_property_atom_req(wm, xwin, + wm->atoms[MBWM_ATOM_WM_PROTOCOLS]); + } + + if (props_req & MBWM_WINDOW_PROP_CLIENT_MACHINE) + { + cookies[COOKIE_WIN_MACHINE] + = mb_wm_property_req (wm, + xwin, + wm->atoms[MBWM_ATOM_WM_CLIENT_MACHINE], + 0, + 2048L, + False, + XA_STRING); + } + + if (props_req & MBWM_WINDOW_PROP_NET_PID) + { + cookies[COOKIE_WIN_PID] + = mb_wm_property_cardinal_req (wm, + xwin, + wm->atoms[MBWM_ATOM_NET_WM_PID]); + } + + if (props_req & MBWM_WINDOW_PROP_NET_ICON) + { + cookies[COOKIE_WIN_ICON] + = mb_wm_property_cardinal_req (wm, + xwin, + wm->atoms[MBWM_ATOM_NET_WM_ICON]); + } + + if (props_req & MBWM_WINDOW_PROP_NET_USER_TIME) + { + cookies[COOKIE_WIN_USER_TIME] + = mb_wm_property_cardinal_req (wm, + xwin, + wm->atoms[MBWM_ATOM_NET_WM_USER_TIME]); + } + + if (props_req & MBWM_WINDOW_PROP_CM_TRANSLUCENCY) + { + cookies[COOKIE_WIN_CM_TRANSLUCENCY] + = mb_wm_property_cardinal_req (wm, + xwin, + wm->atoms[MBWM_ATOM_CM_TRANSLUCENCY]); + } + + /* bundle all pending requests to server and wait for replys */ + XSync(wm->xdpy, False); + + if (props_req & MBWM_WINDOW_PROP_WIN_TYPE) + { + mb_wm_property_reply (wm, + cookies[COOKIE_WIN_TYPE], + &actual_type_return, + &actual_format_return, + &nitems_return, + &bytes_after_return, + (unsigned char **)&result_atom, + &x_error_code); + + if (x_error_code + || actual_type_return != XA_ATOM + || actual_format_return != 32 + || nitems_return != 1 + || result_atom == NULL + ) + { + MBWM_DBG("### Warning net type prop failed ###"); + } + else + { + if (win->net_type != result_atom[0]) + changes |= MBWM_WINDOW_PROP_WIN_TYPE; + + win->net_type = result_atom[0]; + } + + if (result_atom) + XFree(result_atom); + + result_atom = NULL; + } + + if (props_req & MBWM_WINDOW_PROP_NET_STATE) + { + mb_wm_property_reply (wm, + cookies[COOKIE_WIN_NET_STATE], + &actual_type_return, + &actual_format_return, + &nitems_return, + &bytes_after_return, + (unsigned char **)&result_atom, + &x_error_code); + + if (x_error_code + || actual_type_return != XA_ATOM + || actual_format_return != 32 + || nitems_return <= 0 + || result_atom == NULL + ) + { + MBWM_DBG("### Warning net type state failed ###"); + } + else + { + int i; + win->ewmh_state = 0; + + for (i = 0; i < nitems_return; ++i) + { + if (result_atom[i] == wm->atoms[MBWM_ATOM_NET_WM_STATE_MODAL]) + win->ewmh_state |= MBWMClientWindowEWMHStateModal; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_STATE_STICKY]) + win->ewmh_state |= MBWMClientWindowEWMHStateSticky; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_STATE_MAXIMIZED_VERT]) + win->ewmh_state |= MBWMClientWindowEWMHStateMaximisedVert; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_STATE_MAXIMIZED_HORZ]) + win->ewmh_state |= MBWMClientWindowEWMHStateMaximisedHorz; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_STATE_SHADED]) + win->ewmh_state |= MBWMClientWindowEWMHStateShaded; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_STATE_SKIP_TASKBAR]) + win->ewmh_state |= MBWMClientWindowEWMHStateSkipTaskbar; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_STATE_SKIP_PAGER]) + win->ewmh_state |= MBWMClientWindowEWMHStateSkipPager; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_STATE_HIDDEN]) + win->ewmh_state |= MBWMClientWindowEWMHStateHidden; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_STATE_FULLSCREEN]) + win->ewmh_state |= MBWMClientWindowEWMHStateFullscreen; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_STATE_ABOVE]) + win->ewmh_state |= MBWMClientWindowEWMHStateAbove; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_STATE_BELOW]) + win->ewmh_state |= MBWMClientWindowEWMHStateBelow; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_STATE_DEMANDS_ATTENTION]) + win->ewmh_state |= MBWMClientWindowEWMHStateDemandsAttention; + } + } + + changes |= MBWM_WINDOW_PROP_NET_STATE; + + if (result_atom) + XFree(result_atom); + + result_atom = NULL; + } + + if (props_req & MBWM_WINDOW_PROP_GEOMETRY) + { + if (!mb_wm_xwin_get_geometry_reply (wm, + cookies[COOKIE_WIN_GEOM], + &win->x_geometry, + &foo, + &win->depth, + &x_error_code)) + { + MBWM_DBG("### Warning Get Geometry Failed ( %i ) ###", + x_error_code); + MBWM_DBG("### Cookie ID was %li ###", + cookies[COOKIE_WIN_GEOM]); + goto abort; + } + + MBWM_DBG("@@@ New Window Obj @@@"); + MBWM_DBG("Win: %lx", win->xwindow); + MBWM_DBG("Type: %lx",win->net_type); + MBWM_DBG("Geom: +%i+%i,%ix%i", + win->x_geometry.x, + win->x_geometry.y, + win->x_geometry.width, + win->x_geometry.height); + + /* FIXME is it right that we directly/immeditaly update + * win->geometry here, perhaps that should be left as a + * responsability for a signal handler? */ + win->geometry = win->x_geometry; + } + + if (props_req & MBWM_WINDOW_PROP_ATTR) + { + xwin_attr = mb_wm_xwin_get_attributes_reply (wm, + cookies[COOKIE_WIN_ATTR], + &x_error_code); + + if (!xwin_attr || x_error_code) + { + MBWM_DBG("### Warning Get Attr Failed ( %i ) ###", x_error_code); + goto abort; + } + + win->visual = xwin_attr->visual; + win->colormap = xwin_attr->colormap; + + /* Window gravity should not be set from the window attribute + * but using the WM_NORMAL_HINTS atom + */ + + /* win->gravity = xwin_attr->win_gravity;*/ + win->override_redirect = xwin_attr->override_redirect; + win->window_class = xwin_attr->class; + } + + if (props_req & MBWM_WINDOW_PROP_NAME) + { + if (win->name) + XFree(win->name); + + /* Prefer UTF8 Naming... */ + win->name + = mb_wm_property_get_reply_and_validate (wm, + cookies[COOKIE_WIN_NAME_UTF8], + wm->atoms[MBWM_ATOM_UTF8_STRING], + 8, + 0, + NULL, + &x_error_code); + + /* FIXME: Validate the UTF8 */ + + if (!win->name) + { + /* FIXME: Should flag up name could be in some wacko encoding ? */ + win->name + = mb_wm_property_get_reply_and_validate (wm, + cookies[COOKIE_WIN_NAME], + XA_STRING, + 8, + 0, + NULL, + &x_error_code); + } + + if (win->name == NULL) + win->name = strdup("unknown"); + + MBWM_DBG("@@@ New Window Name: '%s' @@@", win->name); + + changes |= MBWM_WINDOW_PROP_NAME; + } + + if (props_req & MBWM_WINDOW_PROP_WM_HINTS) + { + XWMHints *wmhints = NULL; + + /* NOTE: pre-R3 X strips group element so will faill for that */ + wmhints = mb_wm_property_get_reply_and_validate (wm, + cookies[COOKIE_WIN_WM_HINTS], + XA_WM_HINTS, + 32, + NumPropWMHintsElements, + NULL, + &x_error_code); + + if (wmhints) + { + MBWM_DBG("@@@ New Window WM Hints @@@"); + + if (wmhints->flags & InputHint) + { + win->want_key_input = wmhints->input; + MBWM_DBG("Want key input: %s", wmhints->input ? "yes" : "no" ); + } + + if (wmhints->flags & StateHint) + { + win->initial_state = wmhints->initial_state; + MBWM_DBG("Initial State : %i", wmhints->initial_state ); + } + + if (wmhints->flags & IconPixmapHint) + { + win->icon_pixmap = wmhints->icon_pixmap; + MBWM_DBG("Icon Pixmap : %lx", wmhints->icon_pixmap ); + } + + if (wmhints->flags & IconMaskHint) + { + win->icon_pixmap_mask = wmhints->icon_mask; + MBWM_DBG("Icon Mask : %lx", wmhints->icon_mask ); + } + + if (wmhints->flags & WindowGroupHint) + { + win->xwin_group = wmhints->window_group; + MBWM_DBG("Window Group : %lx", wmhints->window_group ); + } + + /* NOTE: we ignore icon window stuff */ + + XFree(wmhints); + + /* FIXME: should track better if thus has changed or not */ + changes |= MBWM_WINDOW_PROP_WM_HINTS; + } + } + if (props_req & MBWM_WINDOW_PROP_WM_NORMAL_HINTS) + { + XSizeHints *sizehints = NULL; + + sizehints = mb_wm_property_get_reply_and_validate (wm, + cookies[COOKIE_WIN_WM_NORMAL_HINTS], + XA_WM_SIZE_HINTS, + 32, + NumPropWMSizeHintsElements, + NULL, + &x_error_code); + if (sizehints) + { + MBWM_DBG("@@@ New Window WM Normal Hints @@@"); + + if (sizehints->flags & PWinGravity) + { + win->gravity = sizehints->win_gravity; + MBWM_DBG("Window Gravity : %i", sizehints->win_gravity); + } + + win->user_pos = (sizehints->flags & USPosition) ? True : False; + MBWM_DBG ("User positioning : %s", win->user_pos ? "yes" : "no"); + + /* May be used to recover other information */ + + XFree (sizehints); + + changes |= MBWM_WINDOW_PROP_WM_NORMAL_HINTS; + } + + } + + if (props_req & MBWM_WINDOW_PROP_MWM_HINTS) + { + /* + * Handle the Motif decoration hint (Gtk uses it). + * + * We currently only differentiate between decorated and undecorated + * windows, but do not allow finer customisation of the decor. + */ + typedef struct + { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long inputMode; + unsigned long status; + } MotifWmHints; + + MotifWmHints *mwmhints = NULL; + + mwmhints = + mb_wm_property_get_reply_and_validate (wm, + cookies[COOKIE_WIN_MWM_HINTS], + wm->atoms[MBWM_ATOM_MOTIF_WM_HINTS], + 32, + PROP_MOTIF_WM_HINTS_ELEMENTS, + NULL, + &x_error_code); + + if (mwmhints) + { + MBWM_DBG("@@@ New Window Motif WM Hints @@@"); + + if (mwmhints->flags & MWM_HINTS_DECORATIONS) + { + if (mwmhints->decorations == 0) + { + win->undecorated = TRUE; + } + } + + XFree(mwmhints); + + /* FIXME: should track better if thus has changed or not */ + changes |= MBWM_WINDOW_PROP_MWM_HINTS; + } + } + + if (props_req & MBWM_WINDOW_PROP_TRANSIENCY) + { + Window *trans_win = NULL; + + trans_win + = mb_wm_property_get_reply_and_validate (wm, + cookies[COOKIE_WIN_TRANSIENCY], + MBWM_ATOM_WM_TRANSIENT_FOR, + 32, + 1, + NULL, + &x_error_code); + + if (trans_win) + { + MBWM_DBG("@@@ Window transient for %lx @@@", *trans_win); + + if (*trans_win != win->xwin_transient_for) + changes |= MBWM_WINDOW_PROP_TRANSIENCY; + + win->xwin_transient_for = *trans_win; + XFree(trans_win); + } + else MBWM_DBG("@@@ Window transient for nothing @@@"); + + changes |= MBWM_WINDOW_PROP_TRANSIENCY; + } + + if (props_req & MBWM_WINDOW_PROP_PROTOS) + { + mb_wm_property_reply (wm, + cookies[COOKIE_WIN_PROTOS], + &actual_type_return, + &actual_format_return, + &nitems_return, + &bytes_after_return, + (unsigned char **)&result_atom, + &x_error_code); + + if (x_error_code + || actual_type_return != XA_ATOM + || actual_format_return != 32 + || result_atom == NULL + ) + { + MBWM_DBG("### Warning wm protocols prop failed ###"); + } + else + { + int i; + + for (i = 0; i < nitems_return; ++i) + { + if (result_atom[i] == wm->atoms[MBWM_ATOM_WM_TAKE_FOCUS]) + win->protos |= MBWMClientWindowProtosFocus; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_WM_DELETE_WINDOW]) + win->protos |= MBWMClientWindowProtosDelete; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_CONTEXT_HELP]) + win->protos |= MBWMClientWindowProtosContextHelp; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_CONTEXT_ACCEPT]) + win->protos |= MBWMClientWindowProtosContextAccept; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_CONTEXT_CUSTOM]) + win->protos |= MBWMClientWindowProtosContextCustom; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_PING]) + win->protos |= MBWMClientWindowProtosPing; + else if (result_atom[i] == + wm->atoms[MBWM_ATOM_NET_WM_SYNC_REQUEST]) + win->protos |= MBWMClientWindowProtosSyncRequest; + else + MBWM_DBG("Unhandled WM Protocol %d", result_atom[i]); + } + } + + if (result_atom) + XFree(result_atom); + + changes |= MBWM_WINDOW_PROP_PROTOS; + + result_atom = NULL; + } + + if (props_req & MBWM_WINDOW_PROP_CLIENT_MACHINE) + { + if (win->machine) + XFree(win->machine); + + win->machine + = mb_wm_property_get_reply_and_validate (wm, + cookies[COOKIE_WIN_MACHINE], + XA_STRING, + 8, + 0, + NULL, + &x_error_code); + + if (!win->machine) + { + MBWM_DBG("### Warning wm client machine prop failed ###"); + } + else + { + MBWM_DBG("@@@ Client machine %s @@@", win->machine); + } + } + + if (props_req & MBWM_WINDOW_PROP_NET_PID) + { + unsigned int *pid = NULL; + + mb_wm_property_reply (wm, + cookies[COOKIE_WIN_PID], + &actual_type_return, + &actual_format_return, + &nitems_return, + &bytes_after_return, + (unsigned char **)&pid, + &x_error_code); + + if (x_error_code + || actual_type_return != XA_CARDINAL + || actual_format_return != 32 + || pid == NULL + ) + { + MBWM_DBG("### Warning net pid prop failed ###"); + } + else + { + win->pid = (pid_t) *pid; + MBWM_DBG("@@@ pid %d @@@", win->pid); + } + + if (pid) + XFree(pid); + } + + if (props_req & MBWM_WINDOW_PROP_CM_TRANSLUCENCY) + { + int *translucency = NULL; + + mb_wm_property_reply (wm, + cookies[COOKIE_WIN_CM_TRANSLUCENCY], + &actual_type_return, + &actual_format_return, + &nitems_return, + &bytes_after_return, + (unsigned char **)&translucency, + &x_error_code); + + if (x_error_code + || actual_type_return != XA_CARDINAL + || actual_format_return != 32 + || translucency == NULL + ) + { + MBWM_DBG("### no CM_TRANSLUCENCY prop ###"); + win->translucency = -1; + } + else + { + win->translucency = *translucency; + MBWM_DBG("@@@ translucency %d @@@", win->translucency); + } + + changes |= MBWM_WINDOW_PROP_CM_TRANSLUCENCY; + + if (translucency) + XFree (translucency); + } + + if (props_req & MBWM_WINDOW_PROP_NET_ICON) + { + unsigned long *icons = NULL; + + mb_wm_property_reply (wm, + cookies[COOKIE_WIN_ICON], + &actual_type_return, + &actual_format_return, + &nitems_return, + &bytes_after_return, + (unsigned char **)&icons, + &x_error_code); + + if (x_error_code + || actual_type_return != XA_CARDINAL + || actual_format_return != 32 + || icons == NULL + ) + { + MBWM_DBG("### Warning net icon prop failed ###"); + } + else + { + MBWMList *l = win->icons; + MBWMList *list_end = NULL; + unsigned long *p = icons; + unsigned long *p_end = icons + nitems_return; + + while (l) + { + MBWMRgbaIcon * ic = l->data; + + mb_wm_rgba_icon_free (ic); + + l = l->next; + } + + while (p < p_end) + { + l = mb_wm_util_malloc0 (sizeof (MBWMList)); + p = icon_from_net_wm_icon (p, (MBWMRgbaIcon **)&l->data); + + if (list_end) + { + l->prev = list_end; + list_end->next = l; + } + else + { + win->icons = l; + } + + list_end = l; + } + } + + if (icons) + XFree(icons); + + changes |= MBWM_WINDOW_PROP_NET_ICON; + + } + + if (props_req & MBWM_WINDOW_PROP_NET_USER_TIME) + { + unsigned long *user_time = NULL; + + mb_wm_property_reply (wm, + cookies[COOKIE_WIN_USER_TIME], + &actual_type_return, + &actual_format_return, + &nitems_return, + &bytes_after_return, + (unsigned char **)&user_time, + &x_error_code); + + if (x_error_code + || actual_type_return != XA_CARDINAL + || actual_format_return != 32 + || user_time == NULL + ) + { + MBWM_DBG("### Warning _NET_WM_USER_TIME failed ###"); + } + else + { + win->user_time = *user_time; + MBWM_DBG("@@@ user time %d @@@", win->user_time); + } + + if (user_time) + XFree(user_time); + } + + + if (changes) + mb_wm_object_signal_emit (MB_WM_OBJECT (win), changes); + + abort: + + if (xwin_attr) + XFree(xwin_attr); + + return True; +} + +Bool +mb_wm_client_window_is_state_set (MBWMClientWindow *win, + MBWMClientWindowEWMHState state) +{ + return (win->ewmh_state & state) ? True : False; +} + diff --git a/matchbox/mb-wm-client-window.h b/matchbox/mb-wm-client-window.h new file mode 100644 index 0000000..e277d14 --- /dev/null +++ b/matchbox/mb-wm-client-window.h @@ -0,0 +1,181 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_WM_CLIENT_WINDOW_H +#define _HAVE_MB_WM_CLIENT_WINDOW_H + +#include <matchbox/mb-wm-object.h> +#include <matchbox/mb-wm-types.h> + +/* FIXME: below limits to 32 props */ + +/* When a property changes + * - window updates its internal values + * - somehow signals client object to process with what changed. + */ + +#define MBWM_WINDOW_PROP_WIN_TYPE (1<<0) +#define MBWM_WINDOW_PROP_GEOMETRY (1<<1) +#define MBWM_WINDOW_PROP_ATTR (1<<2) +#define MBWM_WINDOW_PROP_NAME (1<<3) +#define MBWM_WINDOW_PROP_WM_HINTS (1<<4) +#define MBWM_WINDOW_PROP_WM_NORMAL_HINTS (1<<5) +#define MBWM_WINDOW_PROP_NET_ICON (1<<6) +#define MBWM_WINDOW_PROP_NET_PID (1<<7) +#define MBWM_WINDOW_PROP_PROTOS (1<<8) +#define MBWM_WINDOW_PROP_TRANSIENCY (1<<9) +#define MBWM_WINDOW_PROP_STATE (1<<10) +#define MBWM_WINDOW_PROP_NET_STATE (1<<11) +#define MBWM_WINDOW_PROP_STARTUP_ID (1<<12) +#define MBWM_WINDOW_PROP_CLIENT_MACHINE (1<<13) +#define MBWM_WINDOW_PROP_ALLOWED_ACTIONS (1<<14) +#define MBWM_WINDOW_PROP_NET_USER_TIME (1<<15) +#define MBWM_WINDOW_PROP_CM_TRANSLUCENCY (1<<17) +#define MBWM_WINDOW_PROP_MWM_HINTS (1<<18) + +#define MBWM_WINDOW_PROP_ALL (0xffffffff) + +typedef enum MBWMClientWindowEWMHState + { + MBWMClientWindowEWMHStateModal = (1<<0), + MBWMClientWindowEWMHStateSticky = (1<<1), + MBWMClientWindowEWMHStateMaximisedVert = (1<<2), + MBWMClientWindowEWMHStateMaximisedHorz = (1<<3), + MBWMClientWindowEWMHStateShaded = (1<<4), + MBWMClientWindowEWMHStateSkipTaskbar = (1<<5), + MBWMClientWindowEWMHStateSkipPager = (1<<6), + MBWMClientWindowEWMHStateHidden = (1<<7), + MBWMClientWindowEWMHStateFullscreen = (1<<8), + MBWMClientWindowEWMHStateAbove = (1<<9), + MBWMClientWindowEWMHStateBelow = (1<<10), + MBWMClientWindowEWMHStateDemandsAttention = (1<<11), + /* + * Keep in sync with the MBWMClientWindowEWHMStatesCount define below !!! + */ + } +MBWMClientWindowEWMHState; + +#define MBWMClientWindowEWHMStatesCount 12 + +typedef enum MBWMClientWindowStateChange + { + MBWMClientWindowStateChangeRemove = (1<<0), + MBWMClientWindowStateChangeAdd = (1<<1), + MBWMClientWindowStateChangeToggle = (1<<2), + } +MBWMClientWindowStateChange; + +typedef enum MBWMClientWindowProtos + { + MBWMClientWindowProtosFocus = (1<<0), + MBWMClientWindowProtosDelete = (1<<1), + MBWMClientWindowProtosContextHelp = (1<<2), + MBWMClientWindowProtosContextAccept = (1<<3), + MBWMClientWindowProtosContextCustom = (1<<4), + MBWMClientWindowProtosPing = (1<<5), + MBWMClientWindowProtosSyncRequest = (1<<6), + } +MBWMClientWindowProtos; + +typedef enum MBWMClientWindowAllowedActions + { + MBWMClientWindowActionMove = (1<<0), + MBWMClientWindowActionResize = (1<<1), + MBWMClientWindowActionMinimize = (1<<2), + MBWMClientWindowActionShade = (1<<3), + MBWMClientWindowActionStick = (1<<4), + MBWMClientWindowActionMaximizeHorz = (1<<5), + MBWMClientWindowActionMaximizeVert = (1<<6), + MBWMClientWindowActionFullscreen = (1<<7), + MBWMClientWindowActionChangeDesktop = (1<<8), + MBWMClientWindowActionClose = (1<<9), + } +MBWMClientWindowAllowedActions; + +#define MB_WM_CLIENT_WINDOW(c) ((MBWMClientWindow*)(c)) +#define MB_WM_CLIENT_WINDOW_CLASS(c) ((MBWMClientWindowClass*)(c)) +#define MB_WM_TYPE_CLIENT_WINDOW (mb_wm_client_window_class_type ()) + +typedef struct MBWMClientWindow MBWMClientWindow; +typedef struct MBWMClientWindowClass MBWMClientWindowClass; + +struct MBWMClientWindow +{ + MBWMObject parent; + + MBGeometry geometry; + MBGeometry x_geometry; + unsigned int depth; + char *name; + Window xwindow; + Visual *visual; + Colormap colormap; + MBWindowManager *wm; + + Atom net_type; + Bool want_key_input; + Window xwin_group; + Pixmap icon_pixmap, icon_pixmap_mask; + + /* WithdrawnState 0, NormalState 1, IconicState 3 */ + int initial_state ; + + MBWMClientWindowEWMHState ewmh_state; + Window xwin_transient_for; + + MBWMClientWindowProtos protos; + pid_t pid; + int translucency; + char *machine; + + MBWMList *icons; + + MBWMClientWindowAllowedActions allowed_actions; + + unsigned long user_time; + + int gravity; + int window_class; + Bool override_redirect; + Bool undecorated; + + Bool user_pos; +}; + +struct MBWMClientWindowClass +{ + MBWMObjectClass parent; +}; + +int +mb_wm_client_window_class_type (); + +MBWMClientWindow* +mb_wm_client_window_new (MBWindowManager *wm, Window xwin); + +Bool +mb_wm_client_window_sync_properties (MBWMClientWindow *win, + unsigned long props_req); + +Bool +mb_wm_client_window_is_state_set (MBWMClientWindow *win, + MBWMClientWindowEWMHState state); + +#endif diff --git a/matchbox/mb-wm-client.c b/matchbox/mb-wm-client.c new file mode 100644 index 0000000..6deeb13 --- /dev/null +++ b/matchbox/mb-wm-client.c @@ -0,0 +1,1122 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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-config.h" +#include "mb-wm-client.h" +#include "mb-window-manager.h" +#include "mb-wm-theme.h" + +#include <unistd.h> +#include <signal.h> + +#if ENABLE_COMPOSITE +#include <X11/extensions/Xrender.h> +#endif + +struct MBWindowManagerClientPriv +{ + Bool realized; + Bool mapped; + Bool iconizing; + Bool hiding_from_desktop; + MBWMSyncType sync_state; +}; + +static void +mb_wm_client_destroy (MBWMObject *obj) +{ + MBWindowManagerClient * client = MB_WM_CLIENT(obj); + MBWindowManager * wm = client->wmref; + MBWMList * l = client->decor; + + if (client->sig_theme_change_id) + mb_wm_object_signal_disconnect (MB_WM_OBJECT (wm), + client->sig_theme_change_id); + client->sig_theme_change_id = 0; + + if (client->sig_prop_change_id) + mb_wm_object_signal_disconnect (MB_WM_OBJECT (client->window), + client->sig_prop_change_id); + + client->sig_prop_change_id = 0; + + /* Must call mb_wm_client_ping_stop rather than + * mb_wm_main_context_timeout_handler_remove() to prevent a race condition + * segfault in the timeout list manipulation + */ + mb_wm_client_ping_stop (client); + +#if ENABLE_COMPOSITE + if (mb_wm_compositing_enabled (wm)) + { + mb_wm_comp_mgr_unregister_client (wm->comp_mgr, client); + } +#endif + + mb_wm_object_unref (MB_WM_OBJECT (client->window)); + + while (l) + { + mb_wm_object_unref (l->data); + l = l->next; + } + + if (client->transient_for) + mb_wm_client_remove_transient (client->transient_for, client); + + /* If we have transient windows, we need to make sure they are unmapped; for + * application dialogs this will happen automatically, but not for external + * transients, such as input windows. + * + * We also have to make sure that the transients no longer refer to this + * client, which is about the be destroyed. + */ + l = client->transients; + while (l) + { + MBWindowManagerClient * c = l->data; + MBWMList * l2 = l; + + c->transient_for = NULL; + XUnmapWindow (wm->xdpy, c->window->xwindow); + + l = l->next; + + free (l2); + } +} + +static Bool +mb_wm_client_on_property_change (MBWMClientWindow *window, + int property, + void *userdata); + +static Bool +mb_wm_client_on_theme_change (MBWindowManager * wm, int signal, + MBWindowManagerClient * client) +{ + mb_wm_client_theme_change (client); + return False; +} + + +static int +mb_wm_client_init (MBWMObject *obj, va_list vap) +{ + MBWindowManagerClient *client; + MBWindowManager *wm = NULL; + MBWMClientWindow *win = NULL; + MBWMObjectProp prop; + int status; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + switch (prop) + { + case MBWMObjectPropWm: + wm = va_arg(vap, MBWindowManager *); + break; + case MBWMObjectPropClientWindow: + win = va_arg(vap, MBWMClientWindow *); + break; + default: + MBWMO_PROP_EAT (vap, prop); + } + + prop = va_arg(vap, MBWMObjectProp); + } + + MBWM_MARK(); + + client = MB_WM_CLIENT(obj); + client->priv = mb_wm_util_malloc0(sizeof(MBWindowManagerClientPriv)); + + client->window = win; + client->wmref = wm; + client->ping_timeout = 1000; + + if (wm->theme) + { + client->layout_hints = + mb_wm_theme_get_client_layout_hints (wm->theme, client); + } + + /* sync with client window */ + mb_wm_client_on_property_change (win, MBWM_WINDOW_PROP_ALL, client); + + /* Handle underlying property changes */ + client->sig_prop_change_id = + mb_wm_object_signal_connect (MB_WM_OBJECT (win), + MBWM_WINDOW_PROP_ALL, + (MBWMObjectCallbackFunc)mb_wm_client_on_property_change, + client); + + client->sig_theme_change_id = + mb_wm_object_signal_connect (MB_WM_OBJECT (wm), + MBWindowManagerSignalThemeChange, + (MBWMObjectCallbackFunc)mb_wm_client_on_theme_change, + client); + + status = XGrabButton(wm->xdpy, Button1, 0, win->xwindow, True, + ButtonPressMask, + GrabModeSync, GrabModeSync, None, None); + + MBWM_NOTE (CLIENT, "XGrabButton() returned status %d for client window %x", + status, + win->xwindow); + +#if ENABLE_COMPOSITE + { + XRenderPictFormat *format; + + format = XRenderFindVisualFormat (wm->xdpy, win->visual); + + if (format && format->type == PictTypeDirect && + format->direct.alphaMask) + { + client->is_argb32 = True; + } + } +#endif + + return 1; +} + +int +mb_wm_client_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWindowManagerClientClass), + sizeof (MBWindowManagerClient), + mb_wm_client_init, + mb_wm_client_destroy, + NULL + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); + } + + return type; +} + +void +mb_wm_client_fullscreen_mark_dirty (MBWindowManagerClient *client) +{ + /* fullscreen implies geometry and visibility sync */ + mb_wm_display_sync_queue (client->wmref, + MBWMSyncFullscreen | + MBWMSyncVisibility | MBWMSyncGeometry); + + client->priv->sync_state |= (MBWMSyncFullscreen | + MBWMSyncGeometry | + MBWMSyncVisibility); +} + +void +mb_wm_client_stacking_mark_dirty (MBWindowManagerClient *client) +{ + mb_wm_display_sync_queue (client->wmref, MBWMSyncStacking); + client->priv->sync_state |= MBWMSyncStacking; +} + +void +mb_wm_client_geometry_mark_dirty (MBWindowManagerClient *client) +{ + mb_wm_display_sync_queue (client->wmref, MBWMSyncGeometry); + + client->priv->sync_state |= MBWMSyncGeometry; +} + +void +mb_wm_client_visibility_mark_dirty (MBWindowManagerClient *client) +{ + mb_wm_display_sync_queue (client->wmref, MBWMSyncVisibility); + + client->priv->sync_state |= MBWMSyncVisibility; + + MBWM_DBG(" sync state: %i", client->priv->sync_state); +} + +void +mb_wm_client_configure_request_ack_queue (MBWindowManagerClient *client) +{ + mb_wm_display_sync_queue (client->wmref, MBWMSyncConfigRequestAck); + + client->priv->sync_state |= MBWMSyncConfigRequestAck; + + MBWM_DBG(" sync state: %i", client->priv->sync_state); +} + +Bool +mb_wm_client_needs_configure_request_ack (MBWindowManagerClient *client) +{ + return (client->priv->sync_state & MBWMSyncConfigRequestAck); +} + +void +mb_wm_client_decor_mark_dirty (MBWindowManagerClient *client) +{ + mb_wm_display_sync_queue (client->wmref, MBWMSyncDecor); + + client->priv->sync_state |= MBWMSyncDecor; + + MBWM_DBG(" sync state: %i", client->priv->sync_state); +} + +Bool +mb_wm_client_needs_fullscreen_sync (MBWindowManagerClient *client) +{ + return (client->priv->sync_state & MBWMSyncFullscreen); +} + +static Bool +mb_wm_client_on_property_change (MBWMClientWindow *window, + int property, + void *userdata) +{ + MBWindowManagerClient * client = MB_WM_CLIENT (userdata); + + if (property & MBWM_WINDOW_PROP_NAME) + { + MBWMList * l = client->decor; + while (l) + { + MBWMDecor * decor = l->data; + + if (mb_wm_decor_get_type (decor) == MBWMDecorTypeNorth) + { + mb_wm_decor_mark_title_dirty (decor); + break; + } + + l = l->next; + } + } + + if (property & MBWM_WINDOW_PROP_GEOMETRY) + mb_wm_client_geometry_mark_dirty (client); + +#if ENABLE_COMPOSITE + if ((property & MBWM_WINDOW_PROP_CM_TRANSLUCENCY) && + client->cm_client && mb_wm_comp_mgr_enabled (client->wmref->comp_mgr)) + { + mb_wm_comp_mgr_client_repair (client->cm_client); + } +#endif + + return False; +} + +MBWindowManagerClient* /* FIXME: rename to mb_wm_client_base/class_new ? */ +mb_wm_client_new (MBWindowManager *wm, MBWMClientWindow *win) +{ + MBWindowManagerClient *client = NULL; + + client = MB_WM_CLIENT(mb_wm_object_new (MB_WM_TYPE_CLIENT, + MBWMObjectPropWm, wm, + MBWMObjectPropClientWindow, win, + NULL)); + + return client; +} + +void +mb_wm_client_realize (MBWindowManagerClient *client) +{ + MBWindowManagerClientClass *klass; + + if (client->priv->realized) + return; + + klass = MB_WM_CLIENT_CLASS(mb_wm_object_get_class (MB_WM_OBJECT(client))); + + if (klass->realize) + klass->realize(client); + + client->priv->realized = True; +} + +Bool +mb_wm_client_is_realized (MBWindowManagerClient *client) +{ + return client->priv->realized; +} + + +/* ## Stacking ## */ + + +void +mb_wm_client_stack (MBWindowManagerClient *client, + int flags) +{ + MBWindowManagerClientClass *klass; + + klass = MB_WM_CLIENT_CLASS(mb_wm_object_get_class (MB_WM_OBJECT(client))); + + if (klass->stack) + { + klass->stack(client, flags); + + /* Schedule stack sync, but not if the client is of override type */ + if (MB_WM_CLIENT_CLIENT_TYPE (client) != MBWMClientTypeOverride) + mb_wm_client_stacking_mark_dirty (client); + } +} + +Bool +mb_wm_client_needs_stack_sync (MBWindowManagerClient *client) +{ + return (client->priv->sync_state & MBWMSyncStacking); +} + +void +mb_wm_client_show (MBWindowManagerClient *client) +{ + MBWindowManagerClientClass *klass; + + klass = MB_WM_CLIENT_CLASS(mb_wm_object_get_class (MB_WM_OBJECT(client))); + + if (klass->show) + klass->show (client); + + client->priv->mapped = True; + + /* Make sure any Hidden state flag is cleared */ + mb_wm_client_set_state (client, + MBWM_ATOM_NET_WM_STATE_HIDDEN, + MBWMClientWindowStateChangeRemove); + + mb_wm_client_visibility_mark_dirty (client); +} + +void +mb_wm_client_hide (MBWindowManagerClient *client) +{ + MBWindowManagerClientClass *klass; + + klass = MB_WM_CLIENT_CLASS(mb_wm_object_get_class (MB_WM_OBJECT(client))); + + if (klass->hide) + { + klass->hide (client); + + client->priv->mapped = False; + + mb_wm_unfocus_client (client->wmref, client); + mb_wm_client_visibility_mark_dirty (client); + } +} + +/* + * The focus processing is split into two stages, client and WM + * + * The client stage is handled by this function, with the return value + * of True indicating that the focus has changed. + * + * NB: This function should be considered protected and only called by the + * MBWindowManager object code. + */ +Bool +mb_wm_client_focus (MBWindowManagerClient *client) +{ + MBWindowManagerClientClass *klass; + Bool ret = False; + + klass = MB_WM_CLIENT_CLASS(mb_wm_object_get_class (MB_WM_OBJECT(client))); + + if (klass->focus) + ret = klass->focus(client); + + if (ret) + { + /* + * If this client is transient, store it with the parent; if it is not + * transient, reset the last transient field + */ + if (client->transient_for) + client->transient_for->last_focused_transient = client; + else + client->last_focused_transient = NULL; + } + + return ret; +} + +Bool +mb_wm_client_want_focus (MBWindowManagerClient *client) +{ + return (client->window->want_key_input != False); +} + +Bool +mb_wm_client_needs_visibility_sync (MBWindowManagerClient *client) +{ + return (client->priv->sync_state & MBWMSyncVisibility); +} + +Bool +mb_wm_client_needs_geometry_sync (MBWindowManagerClient *client) +{ + return (client->priv->sync_state & MBWMSyncGeometry); +} + +Bool +mb_wm_client_needs_decor_sync (MBWindowManagerClient *client) +{ + return (client->priv->sync_state & MBWMSyncDecor); +} + +Bool +mb_wm_client_needs_sync (MBWindowManagerClient *client) +{ + MBWM_DBG("sync_state: %i", client->priv->sync_state); + + return client->priv->sync_state; +} + +Bool +mb_wm_client_is_mapped (MBWindowManagerClient *client) +{ + return client->priv->mapped; +} + +void +mb_wm_client_display_sync (MBWindowManagerClient *client) +{ + MBWindowManagerClientClass *klass; + + klass = MB_WM_CLIENT_CLASS(mb_wm_object_get_class (MB_WM_OBJECT(client))); + + if (klass->sync) + klass->sync (client); + + client->priv->sync_state = 0; +} + + +Bool +mb_wm_client_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags) +{ + MBWindowManagerClientClass *klass; + + klass = MB_WM_CLIENT_CLASS(mb_wm_object_get_class (MB_WM_OBJECT(client))); + + if (klass->geometry) + return klass->geometry(client, new_geometry, flags); + + return False; +} + +MBWMClientLayoutHints +mb_wm_client_get_layout_hints (MBWindowManagerClient *client) +{ + if ((client->window->ewmh_state & MBWMClientWindowEWMHStateFullscreen)) + { + if (client->layout_hints & LayoutPrefVisible) + return (LayoutPrefFullscreen | LayoutPrefVisible); + else + return LayoutPrefFullscreen; + } + + return client->layout_hints; +} + +void +mb_wm_client_set_layout_hints (MBWindowManagerClient *client, + MBWMClientLayoutHints hints) +{ + client->layout_hints = hints; +} + +void +mb_wm_client_set_layout_hint (MBWindowManagerClient *client, + MBWMClientLayoutHints hint, + Bool state) +{ + if (state) + client->layout_hints |= hint; + else + client->layout_hints &= ~hint; +} + +void /* needs to be boolean, client may not have any coverage */ +mb_wm_client_get_coverage (MBWindowManagerClient *client, + MBGeometry *coverage) +{ + MBGeometry *geometry; + + if (!client->xwin_frame) + geometry = &client->window->geometry; + else + geometry = &client->frame_geometry; + + coverage->x = geometry->x; + coverage->y = geometry->y; + coverage->width = geometry->width; + coverage->height = geometry->height; +} + +void +mb_wm_client_add_transient (MBWindowManagerClient *client, + MBWindowManagerClient *transient) +{ + MBWMList *l; + + if (transient == NULL || client == NULL) + return; + + transient->transient_for = client; + + /* + * Make sure that each transient is only added once (theoretically it should + * be, but it is very easy for a derived class to call this function without + * realizing the parent has dones so). + */ + l = client->transients; + while (l) + { + if (l->data == transient) + return; + + l = l->next; + } + + client->transients = mb_wm_util_list_append(client->transients, transient); +} + +void +mb_wm_client_remove_transient (MBWindowManagerClient *client, + MBWindowManagerClient *transient) +{ + if (!transient || !client || !client->transients) + return; + + transient->transient_for = NULL; + + client->transients = mb_wm_util_list_remove(client->transients, transient); + + if (client->last_focused_transient == transient) + client->last_focused_transient = transient->next_focused_client; +} + +MBWMList* +mb_wm_client_get_transients (MBWindowManagerClient *client) +{ + MBWMList *trans = NULL; + MBWMList *l = client->transients; + Window xgroup = client->window->xwin_group; + MBWindowManagerClient *c; + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + while (l) + { + trans = mb_wm_util_list_prepend (trans, l->data); + l = l->next; + } + + /* If this is an application or desktop that are part of an group, + * we add any other transients that are part of the group to the list. + */ + if (xgroup && + (c_type == MBWMClientTypeApp || c_type == MBWMClientTypeDesktop)) + { + mb_wm_stack_enumerate (client->wmref, c) + if (c != client && + c->transient_for && c->window->xwin_group == xgroup) + { + MBWindowManagerClient * t = c->transient_for; + + /* Only add it if it is not directly transiet for our client (in + * which case it is already in the list + * + * Find the bottom level transient + */ + while (t && t->transient_for) + t = t->transient_for; + + if (!t || (MB_WM_CLIENT_CLIENT_TYPE (t) == MBWMClientTypeApp || + MB_WM_CLIENT_CLIENT_TYPE (t) == MBWMClientTypeDesktop)) + { + trans = mb_wm_util_list_prepend (trans, c); + } + } + } + + return trans; +} + +MBWindowManagerClient* +mb_wm_client_get_transient_for (MBWindowManagerClient *client) +{ + return client->transient_for; +} + + +const char* +mb_wm_client_get_name (MBWindowManagerClient *client) +{ + if (!client->window) + return NULL; + + return client->window->name; +} + +void +mb_wm_client_deliver_message (MBWindowManagerClient *client, + Atom delivery_atom, + unsigned long data0, + unsigned long data1, + unsigned long data2, + unsigned long data3, + unsigned long data4) +{ + MBWindowManager *wm = client->wmref; + Window xwin = client->window->xwindow; + XEvent ev; + + memset(&ev, 0, sizeof(ev)); + + ev.xclient.type = ClientMessage; + ev.xclient.window = xwin; + ev.xclient.message_type = delivery_atom; + ev.xclient.format = 32; + ev.xclient.data.l[0] = data0; + ev.xclient.data.l[1] = data1; + ev.xclient.data.l[2] = data2; + ev.xclient.data.l[3] = data3; + ev.xclient.data.l[4] = data4; + + XSendEvent(wm->xdpy, xwin, False, NoEventMask, &ev); + + XSync(wm->xdpy, False); +} + +void +mb_wm_client_deliver_wm_protocol (MBWindowManagerClient *client, + Atom protocol) +{ + MBWindowManager *wm = client->wmref; + + mb_wm_client_deliver_message (client, wm->atoms[MBWM_ATOM_WM_PROTOCOLS], + protocol, CurrentTime, 0, 0, 0); +} + +static void +mb_wm_client_deliver_ping_protocol (MBWindowManagerClient *client) +{ + MBWindowManager *wm = client->wmref; + + mb_wm_client_deliver_message (client, + wm->atoms[MBWM_ATOM_WM_PROTOCOLS], + wm->atoms[MBWM_ATOM_NET_WM_PING], + CurrentTime, + client->window->xwindow, + 0, 0); +} + +static Bool +mb_wm_client_ping_timeout_cb (void * userdata) +{ + MBWindowManagerClient * client = userdata; + + mb_wm_handle_hang_client (client->wmref, client); + + mb_wm_client_ping_stop (client); + return False; +} + +void +mb_wm_client_ping_start (MBWindowManagerClient *client) +{ + MBWindowManager * wm = client->wmref; + MBWMMainContext * ctx = wm->main_ctx; + MBWMClientWindowProtos protos = client->window->protos; + + if (!(protos & MBWMClientWindowProtosPing)) + return; + + if (client->ping_cb_id) + return; + + client->ping_cb_id = + mb_wm_main_context_timeout_handler_add (ctx, client->ping_timeout, + mb_wm_client_ping_timeout_cb, + client); + + mb_wm_client_deliver_ping_protocol (client); +} + +void +mb_wm_client_ping_stop (MBWindowManagerClient *client) +{ + MBWMMainContext * ctx = client->wmref->main_ctx; + + if (!client->ping_cb_id) + return; + + mb_wm_main_context_timeout_handler_remove (ctx, client->ping_cb_id); + client->ping_cb_id = 0; +} + +void +mb_wm_client_shutdown (MBWindowManagerClient *client) +{ + char buf[257]; + int sig = 9; + MBWindowManager *wm = client->wmref; + MBWMClientWindow *win = client->window; + Window xwin = client->window->xwindow; + const char *machine = win->machine; + pid_t pid = win->pid; + + if (machine && pid && (gethostname (buf, sizeof(buf)-1) == 0)) + { + if (!strcmp (buf, machine)) + { + if (kill (pid, sig) >= 0) + return; + } + } + + XKillClient(wm->xdpy, xwin); +} + +void +mb_wm_client_deliver_delete (MBWindowManagerClient *client) +{ + MBWindowManager *wm = client->wmref; + MBWMClientWindowProtos protos = client->window->protos; + + if ((protos & MBWMClientWindowProtosDelete)) + { + mb_wm_client_deliver_wm_protocol (client, + wm->atoms[MBWM_ATOM_WM_DELETE_WINDOW]); + + /* NB: It is not appropriate (or even possible) to try and + * determine a failure to respond to a WM_DELETE, since it + * would be reasonable for a client to put up a confirmation + * dialog in response to a WM_DELETE and do nothing if the + * user cancels the operation (because it was an accident) + * + * The NET_WM_PING protocol is the right way to determine an + * unresponsive client and we always send a ping (if the + * client supports the protocol) when issuing a WM_DELETE. + */ + + mb_wm_client_ping_start (client); + } + else + mb_wm_client_shutdown (client); +} + +void +mb_wm_client_set_state (MBWindowManagerClient *client, + MBWMAtom state, + MBWMClientWindowStateChange state_op) +{ + MBWindowManager *wm = client->wmref; + MBWMClientWindow *win = client->window; + Bool old_state; + Bool new_state; + Bool activate = True; + MBWMClientWindowEWMHState state_flag; + + switch (state) + { + case MBWM_ATOM_NET_WM_STATE_FULLSCREEN: + state_flag = MBWMClientWindowEWMHStateFullscreen; + break; + case MBWM_ATOM_NET_WM_STATE_ABOVE: + state_flag = MBWMClientWindowEWMHStateAbove; + break; + case MBWM_ATOM_NET_WM_STATE_HIDDEN: + state_flag = MBWMClientWindowEWMHStateHidden; + break; + case MBWM_ATOM_NET_WM_STATE_MODAL: + case MBWM_ATOM_NET_WM_STATE_STICKY: + case MBWM_ATOM_NET_WM_STATE_MAXIMIZED_VERT: + case MBWM_ATOM_NET_WM_STATE_MAXIMIZED_HORZ: + case MBWM_ATOM_NET_WM_STATE_SHADED: + case MBWM_ATOM_NET_WM_STATE_SKIP_TASKBAR: + case MBWM_ATOM_NET_WM_STATE_SKIP_PAGER: + case MBWM_ATOM_NET_WM_STATE_BELOW: + case MBWM_ATOM_NET_WM_STATE_DEMANDS_ATTENTION: + default: + return; /* not handled yet */ + } + + old_state = (win->ewmh_state & state_flag); + + switch (state_op) + { + case MBWMClientWindowStateChangeRemove: + new_state = False; + break; + case MBWMClientWindowStateChangeAdd: + new_state = True; + break; + case MBWMClientWindowStateChangeToggle: + new_state = !old_state; + break; + } + + if (new_state == old_state) + return; + + if (new_state) + { + win->ewmh_state |= state_flag; + } + else + { + win->ewmh_state &= ~state_flag; + } + + if (state_flag & MBWMClientWindowEWMHStateHidden) + { + if (new_state) + mb_wm_client_hide (client); + else + mb_wm_client_show (client); + + return; + } + + if ((state_flag & MBWMClientWindowEWMHStateFullscreen)) + { + mb_wm_client_fullscreen_mark_dirty (client); + } + + /* + * FIXME -- resize && move, possibly redraw decors when returning from + * fullscreen + */ + if (activate) + mb_wm_activate_client(wm, client); +} + +Bool +mb_wm_client_ping_in_progress (MBWindowManagerClient * client) +{ + return (client->ping_cb_id != 0); +} + +void +mb_wm_client_theme_change (MBWindowManagerClient *client) +{ + MBWindowManagerClientClass *klass; + + klass = MB_WM_CLIENT_CLASS(mb_wm_object_get_class (MB_WM_OBJECT(client))); + + if (klass->theme_change) + klass->theme_change (client); +} + +/* + * Cleanup any transient relationships this client might have + * (we need to do this when the client window unmaps to ensure correct + * functioning of the stack). + */ +void +mb_wm_client_detransitise (MBWindowManagerClient *client) +{ + MBWindowManagerClientClass *klass; + + if (!client->transient_for) + return; + + klass = MB_WM_CLIENT_CLASS(mb_wm_object_get_class (MB_WM_OBJECT(client))); + + if (klass->detransitise) + klass->detransitise (client); + + mb_wm_client_remove_transient (client->transient_for, client); +} + +Bool +mb_wm_client_is_iconizing (MBWindowManagerClient *client) +{ + return client->priv->iconizing; +} + +void +mb_wm_client_reset_iconizing (MBWindowManagerClient *client) +{ + client->priv->iconizing = False; +} + +void +mb_wm_client_iconize (MBWindowManagerClient *client) +{ + /* + * Set the iconizing flag and put the window into hidden state + * This triggers an umap event, at which point the client gets unmanaged + * by the window manager. + */ +#if ENABLE_COMPOSITE + /* + * We cannot iconize the client until the effect finished, otherwise it + * will unmap before the effect takes place, so we do this in the callback. + */ + if (mb_wm_compositing_enabled (client->wmref)) + { + mb_wm_comp_mgr_do_effect (client->wmref->comp_mgr, client, + MBWMCompMgrClientEventMinimize); + } +#endif + + client->priv->iconizing = True; + + + mb_wm_client_set_state (client, + MBWM_ATOM_NET_WM_STATE_HIDDEN, + MBWMClientWindowStateChangeAdd); +} + +int +mb_wm_client_title_height (MBWindowManagerClient *client) +{ + MBWMClientWindow * win = client->window; + MBWindowManager * wm = client->wmref; + int north; + + if (!wm->theme || + mb_wm_client_window_is_state_set (win, + MBWMClientWindowEWMHStateFullscreen)) + { + return 0; + } + + + mb_wm_theme_get_decor_dimensions (wm->theme, client, + &north, NULL, NULL, NULL); + + return north; +} + +Bool +mb_wm_client_is_modal (MBWindowManagerClient *client) +{ + return mb_wm_client_window_is_state_set (client->window, + MBWMClientWindowEWMHStateModal); +} + +Bool +mb_wm_client_owns_xwindow (MBWindowManagerClient *client, Window xwin) +{ + MBWMList * l; + + if (client->xwin_frame == xwin || client->window->xwindow == xwin) + return True; + + l = client->decor; + while (l) + { + MBWMDecor * d = l->data; + + if (d->xwin == xwin) + return True; + + l = l->next; + } + + return False; +} + +MBWMStackLayerType +mb_wm_client_get_stacking_layer (MBWindowManagerClient *client) +{ + MBWindowManagerClientClass *klass; + + klass = MB_WM_CLIENT_CLASS(mb_wm_object_get_class (MB_WM_OBJECT(client))); + + if (klass->stacking_layer) + return klass->stacking_layer (client); + + return client->stacking_layer; +} + +Bool +mb_wm_client_is_argb32 (MBWindowManagerClient *client) +{ + return client->is_argb32; +} + +void +mb_wm_client_set_desktop (MBWindowManagerClient * client, int desktop) +{ + client->desktop = desktop; +} + +int +mb_wm_client_get_desktop (MBWindowManagerClient * client) +{ + return client->desktop; +} + +void +mb_wm_client_desktop_change (MBWindowManagerClient * client, int desktop) +{ + if (client->desktop < 0) + return; + + if (client->desktop == desktop) + { + mb_wm_client_set_state (client, + MBWM_ATOM_NET_WM_STATE_HIDDEN, + MBWMClientWindowStateChangeRemove); + + /* + * NB -- we do not reset the hiding_from_desktop flag here + * since it is there to indicate to the WM that the window is + * mapping because of the desktop change; the WM resets it when + * it get the map-notify for it. + */ + } + else + { + client->priv->hiding_from_desktop = True; + + mb_wm_client_set_state (client, + MBWM_ATOM_NET_WM_STATE_HIDDEN, + MBWMClientWindowStateChangeAdd); + } +} + +Bool +mb_wm_client_is_hiding_from_desktop (MBWindowManagerClient * client) +{ + return client->priv->hiding_from_desktop; +} + +void +mb_wm_client_reset_hiding_from_desktop (MBWindowManagerClient * client) +{ + client->priv->hiding_from_desktop = False; +} + diff --git a/matchbox/mb-wm-client.h b/matchbox/mb-wm-client.h new file mode 100644 index 0000000..57c2c49 --- /dev/null +++ b/matchbox/mb-wm-client.h @@ -0,0 +1,404 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_CLIENT_H +#define _HAVE_MB_CLIENT_H + +#include <matchbox/mb-wm-client-window.h> +#include <matchbox/mb-wm-config.h> +#if ENABLE_COMPOSITE +#include <matchbox/mb-wm-comp-mgr.h> +#endif + +/* XXX: We have a circular dependency between mb-wm-comp-mgr.h + * and mb-wm-client.h */ +#ifndef MB_WM_COMP_MGR_TYPEDEF_DEFINED +typedef struct MBWMCompMgr MBWMCompMgr; +#define MB_WM_COMP_MGR_TYPEDEF_DEFINED +#endif + +#define MB_WM_CLIENT(c) ((MBWindowManagerClient*)(c)) +#define MB_WM_CLIENT_CLASS(c) ((MBWindowManagerClientClass*)(c)) +#define MB_WM_TYPE_CLIENT (mb_wm_client_class_type ()) +#define MB_WM_CLIENT_XWIN(w) (w)->window->xwindow +#define MB_WM_CLIENT_CLIENT_TYPE(c) \ + (MB_WM_CLIENT_CLASS(MB_WM_OBJECT_GET_CLASS(c))->client_type) + +/* XXX: We have a circular dependency between mb-wm-comp-mgr.h + * and mb-wm-client.h */ +#ifndef MB_WM_CLIENT_TYPEDEF_DEFINED +typedef struct MBWindowManagerClient MBWindowManagerClient; +#define MB_WM_CLIENT_TYPEDEF_DEFINED +#endif +typedef struct MBWindowManagerClientClass MBWindowManagerClientClass; +typedef struct MBWindowManagerClientPriv MBWindowManagerClientPriv; + +typedef void (*MBWindowManagerClientInitMethod) (MBWindowManagerClient *client); + +/* Clients hint to what stacking layer they exist in. By default all + * transients to that client will also be stacked there. + */ +typedef enum MBWMStackLayerType +{ + MBWMStackLayerUnknown = 0, /* Transients */ + MBWMStackLayerBottom , /* Desktop window */ + MBWMStackLayerBottomMid , /* Panels */ + MBWMStackLayerMid , /* Apps */ + MBWMStackLayerTopMid , /* Trans for root dialogs */ + MBWMStackLayerTop , /* Something else ? */ + N_MBWMStackLayerTypes +} +MBWMStackLayerType; + +/* Clients can also hint to as how they would like to be managed by the + * layout manager. + */ +typedef enum MBWMClientLayoutHints + { + LayoutPrefReserveEdgeNorth = (1<< 0), /* panels */ + LayoutPrefReserveEdgeSouth = (1<< 1), + LayoutPrefReserveEdgeEast = (1<< 2), + LayoutPrefReserveEdgeWest = (1<< 3), + LayoutPrefReserveNorth = (1<< 4), /* Input wins */ + LayoutPrefReserveSouth = (1<< 5), + LayoutPrefReserveEast = (1<< 6), + LayoutPrefReserveWest = (1<< 7), + LayoutPrefGrowToFreeSpace = (1<< 8), /* Free space left by above */ + LayoutPrefFullscreen = (1<< 9), /* Fullscreen and desktop wins */ + LayoutPrefPositionFree = (1<<10), /* Dialog */ + LayoutPrefVisible = (1<<11), /* Flag is toggled by stacking */ + LayoutPrefFixedX = (1<<12), /* X and width are fixed*/ + LayoutPrefFixedY = (1<<13), + LayoutPrefOverlaps = (1<<14), /* stacked over other windows */ + LayoutPrefMovable = (1<<15), + LayoutPrefResizable = (1<<16), + } +MBWMClientLayoutHints; + +typedef enum MBWMClientReqGeomType + { + MBWMClientReqGeomDontCommit = (1 << 1), + MBWMClientReqGeomIsViaConfigureReq = (1 << 2), + MBWMClientReqGeomIsViaUserAction = (1 << 3), + MBWMClientReqGeomIsViaLayoutManager = (1 << 4), + MBWMClientReqGeomForced = (1 << 5) + } +MBWMClientReqGeomType; + +/* Methods */ + +typedef void (*MBWMClientNewMethod) (MBWindowManager *wm, + MBWMClientWindow *win); + +typedef void (*MBWMClientInitMethod) (MBWindowManager *wm, + MBWindowManagerClient *client, + MBWMClientWindow *win); + +typedef void (*MBWMClientRealizeMethod) (MBWindowManagerClient *client); + +typedef void (*MBWMClientDestroyMethod) (MBWindowManagerClient *client); + +typedef Bool (*MBWMClientGeometryMethod) (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags); + +typedef void (*MBWMClientStackMethod) (MBWindowManagerClient *client, + int flags); + +typedef void (*MBWMClientShowMethod) (MBWindowManagerClient *client); + +typedef void (*MBWMClientHideMethod) (MBWindowManagerClient *client); + +typedef void (*MBWMClientSyncMethod) (MBWindowManagerClient *client); + +typedef Bool (*MBWMClientFocusMethod)(MBWindowManagerClient *client); + +typedef void (*MBWMClientThemeChangeMethod) (MBWindowManagerClient *client); + +typedef void (*MBWMClientDetransitise) (MBWindowManagerClient *client); + +typedef MBWMStackLayerType (*MBWMClientStackingLayer)(MBWindowManagerClient*); + +struct MBWindowManagerClientClass +{ + MBWMObjectClass parent; + + MBWMClientType client_type; + + MBWMClientRealizeMethod realize; /* create dpy resources / reparent */ + MBWMClientGeometryMethod geometry; /* requests a gemetry change */ + MBWMClientStackMethod stack; /* positions win in stack */ + MBWMClientShowMethod show; + MBWMClientHideMethod hide; + MBWMClientSyncMethod sync; /* sync internal changes to display */ + MBWMClientFocusMethod focus; + MBWMClientThemeChangeMethod theme_change; + MBWMClientDetransitise detransitise; + MBWMClientStackingLayer stacking_layer; +}; + +struct MBWindowManagerClient +{ + MBWMObject parent; + /* ### public ### */ + + MBWindowManager *wmref; + char *name; + MBWMClientWindow *window; + Window xwin_frame; + Window xwin_modal_blocker; + MBWMStackLayerType stacking_layer; + unsigned long stacking_hints; + + MBWMClientLayoutHints layout_hints; + + MBWindowManagerClient *stacked_above, *stacked_below; + MBWindowManagerClient *next_focused_client; + + MBGeometry frame_geometry; /* FIXME: in ->priv ? */ + MBWMList *decor; + MBWMList *transients; + MBWindowManagerClient *transient_for; + MBWindowManagerClient *last_focused_transient; + + int skip_unmaps; + + /* ### Private ### */ + + MBWindowManagerClientPriv *priv; + unsigned long sig_prop_change_id; + unsigned long ping_cb_id; + unsigned long sig_theme_change_id; + int ping_timeout; + + Bool is_argb32; + + int desktop; + +#if ENABLE_COMPOSITE + MBWMCompMgrClient *cm_client; +#endif +}; + +#define mb_wm_client_frame_west_width(c) \ + ((c)->window->geometry.x - (c)->frame_geometry.x) +#define mb_wm_client_frame_east_width(c) \ + (((c)->frame_geometry.x + (c)->frame_geometry.width) \ + - ((c)->window->geometry.x + (c)->window->geometry.width)) +#define mb_wm_client_frame_east_x(c) \ + ((c)->window->geometry.x + (c)->window->geometry.width) +#define mb_wm_client_frame_north_height(c) \ + ((c)->window->geometry.y - (c)->frame_geometry.y) +#define mb_wm_client_frame_south_y(c) \ + ((c)->window->geometry.y + (c)->window->geometry.height) +#define mb_wm_client_frame_south_height(c) \ + ( ((c)->frame_geometry.y + (c)->frame_geometry.height) \ + - ((c)->window->geometry.y + (c)->window->geometry.height) ) + +int +mb_wm_client_class_type (); + +MBWMClientWindow* +mb_wm_client_window_new (MBWindowManager *wm, Window window); + +MBWindowManagerClient* +mb_wm_client_new (MBWindowManager *wm, MBWMClientWindow *win); + +void +mb_wm_client_realize (MBWindowManagerClient *client); + +void +mb_wm_client_stack (MBWindowManagerClient *client, + int flags); +void +mb_wm_client_show (MBWindowManagerClient *client); + +void +mb_wm_client_hide (MBWindowManagerClient *client); + +Bool +mb_wm_client_focus (MBWindowManagerClient *client); + +Bool +mb_wm_client_want_focus (MBWindowManagerClient *client); + +void +mb_wm_client_display_sync (MBWindowManagerClient *client); + + +Bool +mb_wm_client_is_realized (MBWindowManagerClient *client); + +Bool +mb_wm_client_request_geometry (MBWindowManagerClient *client, + MBGeometry *new_geometry, + MBWMClientReqGeomType flags); + +Bool +mb_wm_client_needs_geometry_sync (MBWindowManagerClient *client); + +Bool +mb_wm_client_needs_visibility_sync (MBWindowManagerClient *client); + +Bool +mb_wm_client_needs_fullscreen_sync (MBWindowManagerClient *client); + +Bool +mb_wm_client_needs_decor_sync (MBWindowManagerClient *client); + +Bool +mb_wm_client_needs_configure_request_ack (MBWindowManagerClient *client); + +void +mb_wm_client_configure_request_ack_queue (MBWindowManagerClient *client); + +Bool +mb_wm_client_needs_sync (MBWindowManagerClient *client); + +Bool +mb_wm_client_is_mapped (MBWindowManagerClient *client); + +void +mb_wm_client_get_coverage (MBWindowManagerClient *client, + MBGeometry *coverage); + +MBWMClientLayoutHints +mb_wm_client_get_layout_hints (MBWindowManagerClient *client); + +void +mb_wm_client_set_layout_hints (MBWindowManagerClient *client, + MBWMClientLayoutHints hints); + +void +mb_wm_client_set_layout_hint (MBWindowManagerClient *client, + MBWMClientLayoutHints hint, + Bool state); + +void +mb_wm_client_stacking_mark_dirty (MBWindowManagerClient *client); + +void +mb_wm_client_fullscreen_mark_dirty (MBWindowManagerClient *client); + +void +mb_wm_client_geometry_mark_dirty (MBWindowManagerClient *client); + +void +mb_wm_client_visibility_mark_dirty (MBWindowManagerClient *client); + +void +mb_wm_client_decor_mark_dirty (MBWindowManagerClient *client); + +void +mb_wm_client_add_transient (MBWindowManagerClient *client, + MBWindowManagerClient *transient); + +void +mb_wm_client_remove_transient (MBWindowManagerClient *client, + MBWindowManagerClient *transient); + +MBWMList* +mb_wm_client_get_transients (MBWindowManagerClient *client); + +MBWindowManagerClient* +mb_wm_client_get_transient_for (MBWindowManagerClient *client); + +const char* +mb_wm_client_get_name (MBWindowManagerClient *client); + +void +mb_wm_client_deliver_delete (MBWindowManagerClient *client); + +void +mb_wm_client_deliver_message (MBWindowManagerClient *client, + Atom delivery_atom, + unsigned long data0, + unsigned long data1, + unsigned long data2, + unsigned long data3, + unsigned long data4); + +void +mb_wm_client_deliver_wm_protocol (MBWindowManagerClient *client, + Atom protocol); + +void +mb_wm_client_shutdown (MBWindowManagerClient *client); + +void +mb_wm_client_set_state (MBWindowManagerClient *client, + MBWMAtom state, + MBWMClientWindowStateChange state_op); + +Bool +mb_wm_client_ping_in_progress (MBWindowManagerClient * client); + +void +mb_wm_client_ping_stop (MBWindowManagerClient *client); + +void +mb_wm_client_theme_change (MBWindowManagerClient *client); + +void +mb_wm_client_detransitise (MBWindowManagerClient *client); + +Bool +mb_wm_client_is_iconizing (MBWindowManagerClient *client); + +void +mb_wm_client_reset_iconizing (MBWindowManagerClient *client); + +void +mb_wm_client_iconize (MBWindowManagerClient *client); + +int +mb_wm_client_title_height (MBWindowManagerClient *client); + +Bool +mb_wm_client_is_modal (MBWindowManagerClient *client); + +Bool +mb_wm_client_owns_xwindow (MBWindowManagerClient *client, Window xwin); + +MBWMStackLayerType +mb_wm_client_get_stacking_layer (MBWindowManagerClient *client); + +void +mb_wm_client_ping_start (MBWindowManagerClient *client); + +Bool +mb_wm_client_is_argb32 (MBWindowManagerClient *client); + +void +mb_wm_client_set_desktop (MBWindowManagerClient * client, int desktop); + +int +mb_wm_client_get_desktop (MBWindowManagerClient * client); + +void +mb_wm_client_desktop_change (MBWindowManagerClient * client, int desktop); + +Bool +mb_wm_client_is_hiding_from_desktop (MBWindowManagerClient * client); + +void +mb_wm_client_reset_hiding_from_desktop (MBWindowManagerClient * client); + +#endif diff --git a/matchbox/mb-wm-comp-mgr-clutter.c b/matchbox/mb-wm-comp-mgr-clutter.c new file mode 100644 index 0000000..eea78ce --- /dev/null +++ b/matchbox/mb-wm-comp-mgr-clutter.c @@ -0,0 +1,1679 @@ +/* + * Matchbox Window Manager - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 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 "matchbox.h" +#include "mb-wm-client.h" +#include "mb-wm-comp-mgr.h" +#include "mb-wm-comp-mgr-clutter.h" +#include "mb-wm-theme.h" +#include "tidy/tidy-texture-frame.h" + +#include <clutter/clutter.h> +#include <clutter/x11/clutter-x11.h> +#if HAVE_CLUTTER_GLX +#include <clutter/glx/clutter-glx-texture-pixmap.h> +#endif +#include <X11/Xresource.h> +#include <X11/extensions/shape.h> +#include <X11/extensions/Xcomposite.h> + +#include <math.h> + +#define SHADOW_RADIUS 4 +#define SHADOW_OPACITY 0.9 +#define SHADOW_OFFSET_X (-SHADOW_RADIUS) +#define SHADOW_OFFSET_Y (-SHADOW_RADIUS) + +#define MAX_TILE_SZ 16 /* make sure size/2 < MAX_TILE_SZ */ +#define WIDTH (3*MAX_TILE_SZ) +#define HEIGHT (3*MAX_TILE_SZ) + +static unsigned char * +mb_wm_comp_mgr_clutter_shadow_gaussian_make_tile (); + +static void +mb_wm_comp_mgr_clutter_add_actor (MBWMCompMgrClutter *, + MBWMCompMgrClutterClient *); + +/* + * A helper object to store manager's per-client data + */ +struct _MBWMCompMgrClutterClientPrivate +{ + ClutterActor * actor; /* Overall actor */ + ClutterActor * texture; /* The texture part of our actor */ + Pixmap pixmap; + int pxm_width; + int pxm_height; + int pxm_depth; + unsigned int flags; + Damage damage; +}; + +static void +mb_wm_comp_mgr_clutter_client_show_real (MBWMCompMgrClient * client); + +static void +mb_wm_comp_mgr_clutter_client_hide_real (MBWMCompMgrClient * client); + +static void +mb_wm_comp_mgr_clutter_client_repair_real (MBWMCompMgrClient * client); + +static void +mb_wm_comp_mgr_clutter_client_configure_real (MBWMCompMgrClient * client); + +static void +mb_wm_comp_mgr_clutter_client_class_init (MBWMObjectClass *klass) +{ + MBWMCompMgrClientClass *c_klass = MB_WM_COMP_MGR_CLIENT_CLASS (klass); + + c_klass->show = mb_wm_comp_mgr_clutter_client_show_real; + c_klass->hide = mb_wm_comp_mgr_clutter_client_hide_real; + c_klass->repair = mb_wm_comp_mgr_clutter_client_repair_real; + c_klass->configure = mb_wm_comp_mgr_clutter_client_configure_real; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMCompMgrClutterClient"; +#endif +} + +/* + * Fetch the entire texture for our client + */ +static void +mb_wm_comp_mgr_clutter_fetch_texture (MBWMCompMgrClient *client) +{ + MBWMCompMgrClutterClient *cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT(client); + MBWindowManagerClient *wm_client = client->wm_client; + MBWindowManager *wm = client->wm; + MBGeometry geom; + Window xwin; + Window root; + int x, y, w, h, bw, depth; +#ifdef HAVE_XEXT + /* Stuff we need for shaped windows */ + XRectangle *shp_rect; + int shp_order; + int shp_count; + int i; + + /* + * This is square of 32-bit comletely transparent values we use to + * clear bits of the texture from shaped windows; the size is a compromise + * between how much memory we want to allocate and how much tiling we are + * happy with. + * + * Make this power of 2 for efficient operation + */ +#define SHP_CLEAR_SIZE 4 + static int clear_init = 0; + static guint32 clear_data[SHP_CLEAR_SIZE * SHP_CLEAR_SIZE]; + + if (!clear_init) + { + memset (&clear_data, 0, sizeof (clear_data)); + clear_init = 1; + } +#endif + + if (!(cclient->priv->flags & MBWMCompMgrClutterClientMapped)) + return; + + xwin = + wm_client->xwin_frame ? wm_client->xwin_frame : wm_client->window->xwindow; + + if (cclient->priv->pixmap) + XFreePixmap (wm->xdpy, cclient->priv->pixmap); + + cclient->priv->pixmap = XCompositeNameWindowPixmap (wm->xdpy, xwin); + + if (!cclient->priv->pixmap) + return; + + XGetGeometry (wm->xdpy, cclient->priv->pixmap, &root, + &x, &y, &w, &h, &bw, &depth); + + mb_wm_client_get_coverage (wm_client, &geom); + + cclient->priv->pxm_width = w; + cclient->priv->pxm_height = h; + cclient->priv->pxm_depth = depth; + + clutter_actor_set_position (cclient->priv->actor, geom.x, geom.y); + clutter_actor_set_size (cclient->priv->texture, geom.width, geom.height); + + clutter_x11_texture_pixmap_set_pixmap ( + CLUTTER_X11_TEXTURE_PIXMAP (cclient->priv->texture), + cclient->priv->pixmap); + +#ifdef HAVE_XEXT + /* + * If the client is shaped, we have to manually clear any pixels in our + * texture in the non-visible areas. + */ + if (mb_wm_theme_is_client_shaped (wm->theme, wm_client)) + { + shp_rect = XShapeGetRectangles (wm->xdpy, xwin, + ShapeBounding, &shp_count, &shp_order); + + if (shp_rect && shp_count) + { + XserverRegion clear_rgn; + XRectangle rect; + XRectangle * clear_rect; + int clear_count; + + XRectangle *r0, r1; + int c; + + rect.x = 0; + rect.y = 0; + rect.width = geom.width; + rect.height = geom.height; + + clear_rgn = XFixesCreateRegion (wm->xdpy, shp_rect, shp_count); + + XFixesInvertRegion (wm->xdpy, clear_rgn, &rect, clear_rgn); + + clear_rect = XFixesFetchRegion (wm->xdpy, clear_rgn, &clear_count); + + for (i = 0; i < clear_count; ++i) + { + int k, l; + + for (k = 0; k < clear_rect[i].width; k += SHP_CLEAR_SIZE) + for (l = 0; l < clear_rect[i].height; l += SHP_CLEAR_SIZE) + { + int w1 = clear_rect[i].width - k; + int h1 = clear_rect[i].height - l; + + if (w1 > SHP_CLEAR_SIZE) + w1 = SHP_CLEAR_SIZE; + + if (h1 > SHP_CLEAR_SIZE) + h1 = SHP_CLEAR_SIZE; + + clutter_texture_set_area_from_rgb_data ( + CLUTTER_TEXTURE (cclient->priv->texture), + (const guchar *)&clear_data, + TRUE, + clear_rect[i].x + k, + clear_rect[i].y + l, + w1, h1, + SHP_CLEAR_SIZE * 4, + 4, + CLUTTER_TEXTURE_RGB_FLAG_BGR, + NULL); + } + } + + XFixesDestroyRegion (wm->xdpy, clear_rgn); + + XFree (shp_rect); + + if (clear_rect) + XFree (clear_rect); + } + } + +#endif +} + +static int +mb_wm_comp_mgr_clutter_client_init (MBWMObject *obj, va_list vap) +{ + MBWMCompMgrClutterClient *cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (obj); + + cclient->priv = + mb_wm_util_malloc0 (sizeof (MBWMCompMgrClutterClientPrivate)); + + return 1; +} + +static void +mb_wm_comp_mgr_clutter_client_destroy (MBWMObject* obj) +{ + MBWMCompMgrClient * c = MB_WM_COMP_MGR_CLIENT (obj); + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (obj); + MBWindowManager * wm = c->wm; + MBWMCompMgrClutter * mgr = MB_WM_COMP_MGR_CLUTTER (wm->comp_mgr); + int i; + + if (cclient->priv->actor) + clutter_actor_destroy (cclient->priv->actor); + + if (cclient->priv->pixmap) + XFreePixmap (wm->xdpy, cclient->priv->pixmap); + + if (cclient->priv->damage) + XDamageDestroy (wm->xdpy, cclient->priv->damage); + + free (cclient->priv); +} + +int +mb_wm_comp_mgr_clutter_client_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMCompMgrClutterClientClass), + sizeof (MBWMCompMgrClutterClient), + mb_wm_comp_mgr_clutter_client_init, + mb_wm_comp_mgr_clutter_client_destroy, + mb_wm_comp_mgr_clutter_client_class_init + }; + + type = + mb_wm_object_register_class (&info, MB_WM_TYPE_COMP_MGR_CLIENT, 0); + } + + return type; +} + +/* + * This is a private method, hence static (all instances of this class are + * created automatically by the composite manager). + */ +static MBWMCompMgrClient * +mb_wm_comp_mgr_clutter_client_new (MBWindowManagerClient * client) +{ + MBWMObject *c; + + c = mb_wm_object_new (MB_WM_TYPE_COMP_MGR_CLUTTER_CLIENT, + MBWMObjectPropClient, client, + NULL); + + return MB_WM_COMP_MGR_CLIENT (c); +} + +static void +mb_wm_comp_mgr_clutter_client_hide_real (MBWMCompMgrClient * client) +{ + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + + /* + * Do not hide the actor if effect is in progress + */ + if (cclient->priv->flags & MBWMCompMgrClutterClientEffectRunning) + return; + + clutter_actor_hide (cclient->priv->actor); +} + +static void +mb_wm_comp_mgr_clutter_client_show_real (MBWMCompMgrClient * client) +{ + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + + if (!cclient->priv->actor) + { + /* + * This can happen if show() is called on our client before it is + * actually mapped (we only alocate the actor in response to map + * notification. + */ + return; + } + + /* + * Clear the don't update flag, if set + */ + cclient->priv->flags &= ~MBWMCompMgrClutterClientDontUpdate; + clutter_actor_show_all (cclient->priv->actor); +} + +void +mb_wm_comp_mgr_clutter_client_set_flags (MBWMCompMgrClutterClient *cclient, + MBWMCompMgrClutterClientFlags flags) +{ + cclient->priv->flags |= flags; +} + + +void +mb_wm_comp_mgr_clutter_client_unset_flags (MBWMCompMgrClutterClient *cclient, + MBWMCompMgrClutterClientFlags flags) +{ + cclient->priv->flags &= ~flags; +} + +MBWMCompMgrClutterClientFlags +mb_wm_comp_mgr_clutter_client_get_flags (MBWMCompMgrClutterClient *cclient) +{ + return (MBWMCompMgrClutterClientFlags) cclient->priv->flags; +} + + +/* + * MBWMCompMgrClutterClientEventEffect + */ +typedef struct MBWMCompMgrClutterClientEventEffect +{ + ClutterTimeline *timeline; + ClutterBehaviour *behaviour; /* can be either behaviour or effect */ +} MBWMCompMgrClutterClientEventEffect; + +static void +mb_wm_comp_mgr_clutter_client_event_free (MBWMCompMgrClutterClientEventEffect * effect) +{ + g_object_unref (effect->timeline); + g_object_unref (effect->behaviour); + + free (effect); +} + +struct completed_cb_data +{ + gulong my_id; + MBWMCompMgrClutterClient * cclient; + MBWMCompMgrClientEvent event; + MBWMCompMgrClutterClientEventEffect * effect; +}; + +ClutterActor * +mb_wm_comp_mgr_clutter_client_get_actor (MBWMCompMgrClutterClient *cclient) +{ + return g_object_ref(MB_WM_OBJECT (cclient->priv->actor)); +} + +static MBWMCompMgrClutterClientEventEffect * +mb_wm_comp_mgr_clutter_client_event_new (MBWMCompMgrClient *client, + MBWMCompMgrClientEvent event, + unsigned long duration) +{ + MBWMCompMgrClutterClientEventEffect * eff; + ClutterTimeline * timeline; + ClutterBehaviour * behaviour; + ClutterAlpha * alpha; + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + MBWindowManager * wm = client->wm; + ClutterPath * path; + MBGeometry geom; + + if (!cclient->priv->actor) + return NULL; + + timeline = clutter_timeline_new (duration); + + if (!timeline) + return NULL; + + alpha = clutter_alpha_new_full (timeline, CLUTTER_LINEAR); + + mb_wm_client_get_coverage (client->wm_client, &geom); + + switch (event) + { + case MBWMCompMgrClientEventMinimize: + behaviour = + clutter_behaviour_scale_new (alpha, 1.0, 1.0, 0, 0); + break; + case MBWMCompMgrClientEventUnmap: + behaviour = clutter_behaviour_opacity_new (alpha, 0xff, 0); + break; + case MBWMCompMgrClientEventMap: + path = clutter_path_new (); + clutter_path_add_move_to (path, -wm->xdpy_width, geom.y); + clutter_path_add_line_to (path, geom.x, geom.y); + behaviour = clutter_behaviour_path_new (alpha, path); + break; + } + + eff = mb_wm_util_malloc0 (sizeof (MBWMCompMgrClutterClientEventEffect)); + eff->timeline = timeline; + eff->behaviour = behaviour; + + clutter_behaviour_apply (behaviour, cclient->priv->actor); + + return eff; +} + +/* + * Implementation of MBWMCompMgrClutter + */ +struct _MBWMCompMgrClutterPrivate +{ + ClutterActor * arena; + MBWMList * desktops; + ClutterActor * shadow; + + Window overlay_window; +}; + +static void +mb_wm_comp_mgr_clutter_private_free (MBWMCompMgrClutter *mgr) +{ + MBWMCompMgrClutterPrivate * priv = mgr->priv; + + if (priv->shadow) + clutter_actor_destroy (priv->shadow); + + free (priv); +} + +static void +mb_wm_comp_mgr_clutter_register_client_real (MBWMCompMgr * mgr, + MBWindowManagerClient * c) +{ + MBWMCompMgrClient *cclient; + MBWMCompMgrClutter *cmgr = MB_WM_COMP_MGR_CLUTTER (mgr); + MBWMCompMgrClutterClass *klass + = MB_WM_COMP_MGR_CLUTTER_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + if (c->cm_client) + return; + + cclient = klass->client_new (c); + c->cm_client = cclient; +} + +static void +mb_wm_comp_mgr_clutter_turn_on_real (MBWMCompMgr *mgr); + +static void +mb_wm_comp_mgr_clutter_turn_off_real (MBWMCompMgr *mgr); + +static void +mb_wm_comp_mgr_clutter_map_notify_real (MBWMCompMgr *mgr, + MBWindowManagerClient *c); + +static void +mb_wm_comp_mgr_clutter_client_transition_real (MBWMCompMgr * mgr, + MBWindowManagerClient *c1, + MBWindowManagerClient *c2, + Bool reverse); + +static void +mb_wm_comp_mgr_clutter_client_event_real (MBWMCompMgr * mgr, + MBWindowManagerClient * client, + MBWMCompMgrClientEvent event); + +static void +mb_wm_comp_mgr_clutter_restack_real (MBWMCompMgr *mgr); + +static Bool +mb_wm_comp_mgr_is_my_window_real (MBWMCompMgr * mgr, Window xwin); + +static void +mb_wm_comp_mgr_clutter_select_desktop (MBWMCompMgr * mgr, + int desktop, int old_desktop); + +static Bool +mb_wm_comp_mgr_clutter_handle_damage (XDamageNotifyEvent * de, + MBWMCompMgr * mgr); + +static void +mb_wm_comp_mgr_clutter_class_init (MBWMObjectClass *klass) +{ + MBWMCompMgrClass *cm_klass = MB_WM_COMP_MGR_CLASS (klass); + MBWMCompMgrClutterClass *clutter_klass = + MB_WM_COMP_MGR_CLUTTER_CLASS (klass); + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMCompMgrClutter"; +#endif + + /* + * NB -- we do not need render() implementation, since that is taken care of + * automatically by clutter stage. + */ + cm_klass->register_client = mb_wm_comp_mgr_clutter_register_client_real; + cm_klass->turn_on = mb_wm_comp_mgr_clutter_turn_on_real; + cm_klass->turn_off = mb_wm_comp_mgr_clutter_turn_off_real; + cm_klass->map_notify = mb_wm_comp_mgr_clutter_map_notify_real; + cm_klass->my_window = mb_wm_comp_mgr_is_my_window_real; + cm_klass->client_transition = mb_wm_comp_mgr_clutter_client_transition_real; + cm_klass->client_event = mb_wm_comp_mgr_clutter_client_event_real; + cm_klass->restack = mb_wm_comp_mgr_clutter_restack_real; + cm_klass->select_desktop = mb_wm_comp_mgr_clutter_select_desktop; + cm_klass->handle_damage = mb_wm_comp_mgr_clutter_handle_damage; + + clutter_klass->client_new = mb_wm_comp_mgr_clutter_client_new; +} + +static int +mb_wm_comp_mgr_clutter_init (MBWMObject *obj, va_list vap) +{ + MBWMCompMgr * mgr = MB_WM_COMP_MGR (obj); + MBWMCompMgrClutter * cmgr = MB_WM_COMP_MGR_CLUTTER (obj); + MBWMCompMgrClutterPrivate * priv; + MBWindowManager * wm = mgr->wm; + ClutterActor * desktop, * arena; + + priv = mb_wm_util_malloc0 (sizeof (MBWMCompMgrClutterPrivate)); + cmgr->priv = priv; + + XCompositeRedirectSubwindows (wm->xdpy, wm->root_win->xwindow, + CompositeRedirectManual); + + priv->arena = arena = clutter_group_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (clutter_stage_get_default()), + arena); + clutter_actor_show (arena); + + desktop = clutter_group_new (); + clutter_actor_show (desktop); + clutter_container_add_actor (CLUTTER_CONTAINER (arena), desktop); + priv->desktops = mb_wm_util_list_append (priv->desktops, desktop); + + return 1; +} + +static void +mb_wm_comp_mgr_clutter_destroy (MBWMObject * obj) +{ + MBWMCompMgr * mgr = MB_WM_COMP_MGR (obj); + MBWMCompMgrClutter * cmgr = MB_WM_COMP_MGR_CLUTTER (obj); + + mb_wm_comp_mgr_turn_off (mgr); + mb_wm_comp_mgr_clutter_private_free (cmgr); +} + +int +mb_wm_comp_mgr_clutter_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMCompMgrClutterClass), + sizeof (MBWMCompMgrClutter), + mb_wm_comp_mgr_clutter_init, + mb_wm_comp_mgr_clutter_destroy, + mb_wm_comp_mgr_clutter_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_COMP_MGR, 0); + } + + return type; +} + +/* Shuts the compositing down */ +static void +mb_wm_comp_mgr_clutter_turn_off_real (MBWMCompMgr *mgr) +{ + MBWindowManager * wm = mgr->wm; + MBWMCompMgrClutterPrivate * priv; + + if (!mgr) + return; + + priv = MB_WM_COMP_MGR_CLUTTER (mgr)->priv; + + if (mgr->disabled) + return; + + if (!mb_wm_stack_empty (wm)) + { + MBWindowManagerClient * c; + + mb_wm_stack_enumerate (wm, c) + { + mb_wm_comp_mgr_unregister_client (mgr, c); + } + } + + XCompositeReleaseOverlayWindow (wm->xdpy, wm->root_win->xwindow); + priv->overlay_window = None; + + mgr->disabled = True; +} + +static void +mb_wm_comp_mgr_clutter_turn_on_real (MBWMCompMgr *mgr) +{ + MBWindowManager * wm; + MBWMCompMgrClutterPrivate * priv; + + if (!mgr || !mgr->disabled) + return; + + priv = MB_WM_COMP_MGR_CLUTTER (mgr)->priv; + wm = mgr->wm; + + mgr->disabled = False; + + if (priv->overlay_window == None) + { + ClutterActor * stage = clutter_stage_get_default (); + ClutterColor clr = {0, 0, 0, 0xff }; + Window xwin; + XserverRegion region; + + /* + * Fetch the overlay window + */ + xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (stage)); + + priv->overlay_window = + XCompositeGetOverlayWindow (wm->xdpy, wm->root_win->xwindow); + + /* + * Reparent the stage window to the overlay window, this makes it + * magically work :) + */ + XReparentWindow (wm->xdpy, xwin, priv->overlay_window, 0, 0); + + /* + * Use xfixes shape to make events pass through the overlay window + * + * TODO -- this has certain drawbacks, notably when our client is + * tranformed (rotated, scaled, etc), the events will not be landing in + * the right place. The answer to that is event forwarding with + * translation. + */ + region = XFixesCreateRegion (wm->xdpy, NULL, 0); + + XFixesSetWindowShapeRegion (wm->xdpy, priv->overlay_window, + ShapeBounding, 0, 0, 0); + XFixesSetWindowShapeRegion (wm->xdpy, priv->overlay_window, + ShapeInput, 0, 0, region); + + XFixesDestroyRegion (wm->xdpy, region); + + clutter_actor_set_size (stage, wm->xdpy_width, wm->xdpy_height); + clutter_stage_set_color (CLUTTER_STAGE (stage), &clr); + + clutter_actor_show (stage); + } +} + +static void +mb_wm_comp_mgr_clutter_client_repair_real (MBWMCompMgrClient * client) +{ + MBWindowManagerClient * wm_client = client->wm_client; + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + MBWindowManager * wm = client->wm; + XserverRegion parts; + int i, r_count; + XRectangle * r_damage; + XRectangle r_bounds; + + MBWM_NOTE (COMPOSITOR, "REPAIRING %x", wm_client->window->xwindow); + + if (!cclient->priv->actor) + return; + + if (!cclient->priv->pixmap) + { + /* + * First time we have been called since creation/configure, + * fetch the whole texture. + */ + MBWM_NOTE (DAMAGE, "Full screen repair."); + XDamageSubtract (wm->xdpy, cclient->priv->damage, None, None); + mb_wm_comp_mgr_clutter_fetch_texture (client); + return; + } + + /* + * Retrieve the damaged region and break it down into individual + * rectangles so we do not have to update the whole shebang. + */ + parts = XFixesCreateRegion (wm->xdpy, 0, 0); + XDamageSubtract (wm->xdpy, cclient->priv->damage, None, parts); + + r_damage = XFixesFetchRegionAndBounds (wm->xdpy, parts, + &r_count, + &r_bounds); + + if (r_damage) + { + for (i = 0; i < r_count; ++i) + { + MBWM_NOTE (DAMAGE, "Repairing %d,%d;%dx%d", + r_damage[i].x, + r_damage[i].y, + r_damage[i].width, + r_damage[i].height); + + clutter_x11_texture_pixmap_update_area ( + CLUTTER_X11_TEXTURE_PIXMAP (cclient->priv->texture), + r_damage[i].x, + r_damage[i].y, + r_damage[i].width, + r_damage[i].height); + } + + XFree (r_damage); + } + + XFixesDestroyRegion (wm->xdpy, parts); +} + +static void +mb_wm_comp_mgr_clutter_client_configure_real (MBWMCompMgrClient * client) +{ + MBWindowManagerClient * wm_client = client->wm_client; + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT (client); + + MBWM_NOTE (COMPOSITOR, "CONFIGURE request"); + + /* + * Release the backing pixmap; we will recreate it next time we get damage + * notification for this window. + */ + if (cclient->priv->pixmap) + { + XFreePixmap (client->wm->xdpy, cclient->priv->pixmap); + cclient->priv->pixmap = None; + } +} + +static Bool +mb_wm_comp_mgr_clutter_handle_damage (XDamageNotifyEvent * de, + MBWMCompMgr * mgr) +{ + MBWMCompMgrClutterPrivate * priv = MB_WM_COMP_MGR_CLUTTER (mgr)->priv; + MBWindowManager * wm = mgr->wm; + MBWindowManagerClient * c; + + c = mb_wm_managed_client_from_frame (wm, de->drawable); + + if (c && c->cm_client) + { + MBWMCompMgrClutterClient *cclient = + MB_WM_COMP_MGR_CLUTTER_CLIENT (c->cm_client); + + if (!cclient->priv->actor || + (cclient->priv->flags & MBWMCompMgrClutterClientDontUpdate)) + return False; + + MBWM_NOTE (COMPOSITOR, + "Reparing window %x, geometry %d,%d;%dx%d; more %d\n", + de->drawable, + de->geometry.x, + de->geometry.y, + de->geometry.width, + de->geometry.height, + de->more); + + mb_wm_comp_mgr_clutter_client_repair_real (c->cm_client); + } + else + { + MBWM_NOTE (COMPOSITOR, "Failed to find client for window %x\n", + de->drawable); + } + + return False; +} + +static void +mb_wm_comp_mgr_clutter_restack_real (MBWMCompMgr *mgr) +{ + MBWindowManager * wm = mgr->wm; + MBWMCompMgrClutter * cmgr = MB_WM_COMP_MGR_CLUTTER (mgr); + MBWMList * l; + int i = 0; + + l = cmgr->priv->desktops; + + if (!mb_wm_stack_empty (wm)) + { + MBWindowManagerClient * c; + + while (l) + { + ClutterActor *desktop = l->data; + ClutterActor * prev = NULL; + + mb_wm_stack_enumerate (wm, c) + { + MBWMCompMgrClutterClient * cc; + ClutterActor * a; + + if (mb_wm_client_get_desktop (c) != i) + continue; + + cc = MB_WM_COMP_MGR_CLUTTER_CLIENT (c->cm_client); + + a = cc->priv->actor; + + if (!a || clutter_actor_get_parent (a) != desktop) + continue; + + clutter_actor_raise (a, prev); + + prev = a; + } + + l = l->next; + ++i; + } + } +} + +MBWMList * +mb_wm_comp_mgr_clutter_get_desktops (MBWMCompMgrClutter *cmgr) +{ + return cmgr->priv->desktops; +} + +/* + * Gets the n-th desktop from our desktop list; if we do not have that many + * desktops, just append new ones. + */ +ClutterActor * +mb_wm_comp_mgr_clutter_get_nth_desktop (MBWMCompMgrClutter * cmgr, int desktop) +{ + MBWMCompMgrClutterPrivate * priv = cmgr->priv; + MBWMList * l = priv->desktops; + int i = 0; + + while (l && i != desktop) + { + ++i; + + if (l->next) + l = l->next; + else + { + /* End of the line -- append new desktop */ + ClutterActor * d = clutter_group_new (); + priv->desktops = mb_wm_util_list_append (priv->desktops, d); + clutter_container_add_actor (CLUTTER_CONTAINER (priv->arena), d); + + l = l->next; + } + } + + return CLUTTER_ACTOR (l->data); +} + +/* + * Returns the arena; this is an intermediate group which contains all the + * other actors the CM uses. The caller of this function holds a reference + * to the returned ClutterActor and must release it once no longer needed. + */ +ClutterActor * +mb_wm_comp_mgr_clutter_get_arena (MBWMCompMgrClutter *cmgr) +{ + MBWMCompMgrClutterPrivate * priv = cmgr->priv; + + return g_object_ref (priv->arena); +} + + +static void +mb_wm_comp_mgr_clutter_select_desktop (MBWMCompMgr * mgr, + int desktop, + int old_desktop) +{ + MBWMCompMgrClutter * cmgr = MB_WM_COMP_MGR_CLUTTER (mgr); + ClutterActor * d; + MBWMList * l; + + d = mb_wm_comp_mgr_clutter_get_nth_desktop (cmgr, desktop); + + l = cmgr->priv->desktops; + + while (l) + { + ClutterActor * a = l->data; + + if (a == d) + clutter_actor_show (a); + else + clutter_actor_hide (a); + + l = l->next; + } +} + +static void +mb_wm_comp_mgr_clutter_map_notify_real (MBWMCompMgr *mgr, + MBWindowManagerClient *c) +{ + MBWMCompMgrClutter * cmgr = MB_WM_COMP_MGR_CLUTTER (mgr); + MBWMCompMgrClient * client = c->cm_client; + MBWMCompMgrClutterClient * cclient = MB_WM_COMP_MGR_CLUTTER_CLIENT(client); + MBWindowManager * wm = client->wm; + ClutterActor * actor; + ClutterActor * texture; + ClutterActor * rect; + MBGeometry geom; + const MBWMList * l; + unsigned int shadow_clr[4]; + ClutterColor shadow_cclr; + MBWMCompMgrShadowType shadow_type; + MBWMClientType ctype = MB_WM_CLIENT_CLIENT_TYPE (c); + + if (mb_wm_client_is_hiding_from_desktop (c)) + { + /* + * We already have the resources, except we have to get a new + * backing pixmap + */ + Window xwin = c->xwin_frame ? c->xwin_frame : c->window->xwindow; + + /* + * FIXME -- Must rebind the pixmap to the texture -- this is not ideal + * since our texture already contains the correct data, but without + * this it will not update. Perhaps we some extension to the clutter + * API is needed here. + */ + mb_wm_comp_mgr_clutter_fetch_texture (client); + + clutter_actor_show (cclient->priv->actor); + return; + } + + /* + * We get called for windows as well as their children, so once we are + * mapped do nothing. + */ + if (cclient->priv->flags & MBWMCompMgrClutterClientMapped) + return; + + cclient->priv->flags |= MBWMCompMgrClutterClientMapped; + + cclient->priv->damage = XDamageCreate (wm->xdpy, + c->xwin_frame ? + c->xwin_frame : + c->window->xwindow, + XDamageReportNonEmpty); + + mb_wm_client_get_coverage (c, &geom); + + actor = g_object_ref (clutter_group_new ()); +#if HAVE_CLUTTER_GLX + texture = clutter_glx_texture_pixmap_new (); +#else + texture = clutter_x11_texture_pixmap_new (); +#endif + clutter_actor_show (texture); + + if (ctype == MBWMClientTypeDialog || + ctype == MBWMClientTypeMenu || + ctype == MBWMClientTypeNote || + ctype == MBWMClientTypeOverride) + { + shadow_type = mb_wm_theme_get_shadow_type (wm->theme); + + if (shadow_type == MBWM_COMP_MGR_SHADOW_NONE) + { + clutter_container_add (CLUTTER_CONTAINER (actor), texture, NULL); + } + else + { + if (shadow_type == MBWM_COMP_MGR_SHADOW_SIMPLE) + { + mb_wm_theme_get_shadow_color (wm->theme, + &shadow_clr[0], + &shadow_clr[1], + &shadow_clr[2], + &shadow_clr[3]); + + shadow_cclr.red = 0xff * shadow_clr[0] / 0xffff; + shadow_cclr.green = 0xff * shadow_clr[1] / 0xffff; + shadow_cclr.blue = 0xff * shadow_clr[2] / 0xffff; + shadow_cclr.alpha = 0xff * shadow_clr[3] / 0xffff; + + rect = clutter_rectangle_new_with_color (&shadow_cclr); + clutter_actor_set_position (rect, 4, 4); + } + else + { + ClutterActor * txt = cmgr->priv->shadow; + if (!txt) + { + unsigned char * data; + + data = mb_wm_comp_mgr_clutter_shadow_gaussian_make_tile (); + + txt = g_object_new (CLUTTER_TYPE_TEXTURE, NULL); + + clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (txt), + data, + TRUE, + WIDTH, + HEIGHT, + WIDTH*4, + 4, + 0, + NULL); + free (data); + + cmgr->priv->shadow = txt; + } + + rect = tidy_texture_frame_new (CLUTTER_TEXTURE (txt), + MAX_TILE_SZ, + MAX_TILE_SZ, + MAX_TILE_SZ, + MAX_TILE_SZ); + clutter_actor_set_position (rect, + 2*SHADOW_RADIUS, 2*SHADOW_RADIUS); + } + + clutter_actor_set_size (rect, geom.width, geom.height); + clutter_actor_show (rect); + + clutter_container_add (CLUTTER_CONTAINER (actor), + rect, texture, NULL); + } + } + else + { + clutter_container_add (CLUTTER_CONTAINER (actor), texture, NULL); + } + + + cclient->priv->actor = actor; + cclient->priv->texture = texture; + + g_object_set_data (G_OBJECT (actor), "MBWMCompMgrClutterClient", cclient); + + clutter_actor_set_position (actor, geom.x, geom.y); + clutter_actor_set_size (texture, geom.width, geom.height); + + mb_wm_comp_mgr_clutter_add_actor (cmgr, cclient); +} + +struct _fade_cb_data +{ + MBWMCompMgrClutterClient *cclient1; + MBWMCompMgrClutterClient *cclient2; + ClutterTimeline * timeline; + ClutterBehaviour * beh; +}; + +static void +mb_wm_comp_mgr_clutter_transtion_fade_cb (ClutterTimeline * t, void * data) +{ + struct _fade_cb_data * d = data; + ClutterActor * a1 = d->cclient1->priv->actor; + ClutterActor * a2 = d->cclient2->priv->actor; + + clutter_actor_set_opacity (a1, 0xff); + + d->cclient1->priv->flags &= ~MBWMCompMgrClutterClientEffectRunning; + d->cclient2->priv->flags &= ~MBWMCompMgrClutterClientEffectRunning; + + mb_wm_object_unref (MB_WM_OBJECT (d->cclient1)); + mb_wm_object_unref (MB_WM_OBJECT (d->cclient2)); + + g_object_unref (d->timeline); + g_object_unref (d->beh); +} + +static void +_fade_apply_behaviour_to_client (MBWindowManagerClient * wc, + ClutterBehaviour * b) +{ + MBWMList * l; + ClutterActor * a = MB_WM_COMP_MGR_CLUTTER_CLIENT (wc->cm_client)->priv->actor; + + clutter_actor_set_opacity (a, 0); + clutter_behaviour_apply (b, a); + + l = mb_wm_client_get_transients (wc); + while (l) + { + MBWindowManagerClient * c = l->data; + + _fade_apply_behaviour_to_client (c, b); + l = l->next; + } +} + +static void +mb_wm_comp_mgr_clutter_client_transition_fade (MBWMCompMgrClutterClient *cclient1, + MBWMCompMgrClutterClient *cclient2, + unsigned long duration) +{ + ClutterTimeline * timeline; + ClutterAlpha * alpha; + static struct _fade_cb_data cb_data; + ClutterBehaviour * b; + + /* + * Fade is simple -- we only need to animate the second actor and its + * children, as the stacking order automatically takes care of the + * actor appearing to fade out from the first one + */ + timeline = clutter_timeline_new (duration); + + alpha = clutter_alpha_new_full (timeline, CLUTTER_LINEAR); + + b = clutter_behaviour_opacity_new (alpha, 0xff, 0); + + cb_data.cclient1 = mb_wm_object_ref (MB_WM_OBJECT (cclient1)); + cb_data.cclient2 = mb_wm_object_ref (MB_WM_OBJECT (cclient2)); + cb_data.timeline = timeline; + cb_data.beh = b; + + _fade_apply_behaviour_to_client (MB_WM_COMP_MGR_CLIENT (cclient2)->wm_client, b); + + /* + * Must restore the opacity on the 'from' actor + */ + g_signal_connect (timeline, "completed", + G_CALLBACK (mb_wm_comp_mgr_clutter_transtion_fade_cb), + &cb_data); + + cclient1->priv->flags |= MBWMCompMgrClutterClientEffectRunning; + cclient2->priv->flags |= MBWMCompMgrClutterClientEffectRunning; + + clutter_timeline_start (timeline); +} + +static void +mb_wm_comp_mgr_clutter_client_transition_real (MBWMCompMgr * mgr, + MBWindowManagerClient *c1, + MBWindowManagerClient *c2, + Bool reverse) +{ + MBWMCompMgrClutterClient * cclient1 = + MB_WM_COMP_MGR_CLUTTER_CLIENT (c1->cm_client); + MBWMCompMgrClutterClient * cclient2 = + MB_WM_COMP_MGR_CLUTTER_CLIENT (c2->cm_client); + + mb_wm_comp_mgr_clutter_client_transition_fade (cclient1, + cclient2, + 100); +} + +/* + * Callback for ClutterTimeline::completed signal. + * + * One-off; get connected when the timeline is started, and disconnected + * again when it finishes. + */ +static void +mb_wm_comp_mgr_clutter_client_event_completed_cb (ClutterTimeline * t, void * data) +{ + struct completed_cb_data * d = data; + + d->cclient->priv->flags &= ~MBWMCompMgrClutterClientEffectRunning; + + g_signal_handler_disconnect (t, d->my_id); + + switch (d->event) + { + case MBWMCompMgrClientEventUnmap: + case MBWMCompMgrClientEventMinimize: + clutter_actor_hide (d->cclient->priv->actor); + break; + + default: + break; + } + + /* + * Release the extra reference on the CM client that was added for the sake + * of the effect + */ + mb_wm_object_unref (MB_WM_OBJECT (d->cclient)); + + mb_wm_comp_mgr_clutter_client_event_free (d->effect); + + free (d); +} + +static void +mb_wm_comp_mgr_clutter_client_event_real (MBWMCompMgr * mgr, + MBWindowManagerClient * client, + MBWMCompMgrClientEvent event) +{ + MBWMCompMgrClutterClientEventEffect * eff; + MBWMCompMgrClutterClient * cclient = + MB_WM_COMP_MGR_CLUTTER_CLIENT (client->cm_client); + + if (MB_WM_CLIENT_CLIENT_TYPE (client) != MBWMClientTypeApp) + return; + + eff = mb_wm_comp_mgr_clutter_client_event_new (client->cm_client, + event, 600); + + if (eff) + { + ClutterActor *a; + Bool dont_run = False; + + a = cclient->priv->actor; + + if (CLUTTER_IS_BEHAVIOUR_PATH (eff->behaviour)) + { + /* + * At this stage, if the actor is not yet visible, move it to + * the starting point of the path (this is mostly because of + * 'map' effects, where the clutter_actor_show () is delayed + * until this point, so that the actor can be positioned in the + * correct location without visible artefacts). + * + * FIXME -- this is very clumsy; we need clutter API to query + * the first knot of the path to avoid messing about with copies + * of the list. + */ + + ClutterPath *path = + clutter_behaviour_path_get_path ( + CLUTTER_BEHAVIOUR_PATH (eff->behaviour)); + if (path) + { + ClutterKnot k; + clutter_path_get_position (path, 0, &k); + clutter_actor_set_position (a, k.x, k.y); + } + } + + if (event == MBWMCompMgrClientEventUnmap) + { + cclient->priv->flags |= MBWMCompMgrClutterClientDontUpdate; + + if (cclient->priv->flags & MBWMCompMgrClutterClientDone) + dont_run = True; + else + cclient->priv->flags |= MBWMCompMgrClutterClientDone; + } + else if (event == MBWMCompMgrClientEventMinimize) + { + /* + * This is tied specifically to the unmap scale effect (the + * themable version of effects allowed to handle this is a nice + * generic fashion. :-( + */ + clutter_actor_move_anchor_point_from_gravity (a, + CLUTTER_GRAVITY_SOUTH_EAST); + } + + /* + * Make sure the actor is showing (for example with 'map' effects, + * the show() is delayed until the effect had chance to + * set up the actor postion). + */ + if (!dont_run) + { + struct completed_cb_data * d; + + d = mb_wm_util_malloc0 (sizeof (struct completed_cb_data)); + + d->cclient = mb_wm_object_ref (MB_WM_OBJECT (cclient)); + d->event = event; + d->effect = eff; + + d->my_id = g_signal_connect (eff->timeline, "completed", + G_CALLBACK (mb_wm_comp_mgr_clutter_client_event_completed_cb), + d); + + cclient->priv->flags |= MBWMCompMgrClutterClientEffectRunning; + clutter_actor_show (a); + clutter_timeline_start (eff->timeline); + } + else + mb_wm_comp_mgr_clutter_client_event_free (eff); + } +} + +/* + * Our windows which we need the WM to ingore are the overlay and the stage + * window. + */ +static Bool +mb_wm_comp_mgr_is_my_window_real (MBWMCompMgr * mgr, Window xwin) +{ + MBWMCompMgrClutterPrivate * priv = MB_WM_COMP_MGR_CLUTTER (mgr)->priv; + ClutterActor * stage; + + if (priv->overlay_window == xwin) + return True; + + stage = clutter_stage_get_default (); + + if (xwin == clutter_x11_get_stage_window (CLUTTER_STAGE (stage))) + return True; + + return False; +} + +static void +mb_wm_comp_mgr_clutter_add_actor (MBWMCompMgrClutter * cmgr, + MBWMCompMgrClutterClient * cclient) +{ + MBWindowManagerClient * c = MB_WM_COMP_MGR_CLIENT (cclient)->wm_client; + ClutterActor * d; + int desktop = mb_wm_client_get_desktop (c); + + /* + * Sanity check; if the desktop is unset, add to desktop 0. + */ + if (desktop < 0) + desktop = 0; + + d = mb_wm_comp_mgr_clutter_get_nth_desktop (cmgr, desktop); + + clutter_container_add_actor (CLUTTER_CONTAINER (d), cclient->priv->actor); +} + +MBWMCompMgr * +mb_wm_comp_mgr_clutter_new (MBWindowManager *wm) +{ + MBWMObject *mgr; + + mgr = mb_wm_object_new (MB_WM_TYPE_COMP_MGR_CLUTTER, + MBWMObjectPropWm, wm, + NULL); + + return MB_WM_COMP_MGR (mgr); +} + +/* ------------------------------- */ +/* Shadow Generation */ + +typedef struct MBGaussianMap +{ + int size; + double * data; +} MBGaussianMap; + +static double +gaussian (double r, double x, double y) +{ + return ((1 / (sqrt (2 * M_PI * r))) * + exp ((- (x * x + y * y)) / (2 * r * r))); +} + + +static MBGaussianMap * +mb_wm_comp_mgr_clutter_make_gaussian_map (double r) +{ + MBGaussianMap *c; + int size = ((int) ceil ((r * 3)) + 1) & ~1; + int center = size / 2; + int x, y; + double t = 0.0; + double g; + + c = malloc (sizeof (MBGaussianMap) + size * size * sizeof (double)); + c->size = size; + + c->data = (double *) (c + 1); + + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) + { + g = gaussian (r, (double) (x - center), (double) (y - center)); + t += g; + c->data[y * size + x] = g; + } + + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) + c->data[y*size + x] /= t; + + return c; +} + +static unsigned char +mb_wm_comp_mgr_clutter_sum_gaussian (MBGaussianMap * map, double opacity, + int x, int y, int width, int height) +{ + int fx, fy; + double * g_data; + double * g_line = map->data; + int g_size = map->size; + int center = g_size / 2; + int fx_start, fx_end; + int fy_start, fy_end; + double v; + unsigned int r; + + /* + * Compute set of filter values which are "in range", + * that's the set with: + * 0 <= x + (fx-center) && x + (fx-center) < width && + * 0 <= y + (fy-center) && y + (fy-center) < height + * + * 0 <= x + (fx - center) x + fx - center < width + * center - x <= fx fx < width + center - x + */ + + fx_start = center - x; + if (fx_start < 0) + fx_start = 0; + fx_end = width + center - x; + if (fx_end > g_size) + fx_end = g_size; + + fy_start = center - y; + if (fy_start < 0) + fy_start = 0; + fy_end = height + center - y; + if (fy_end > g_size) + fy_end = g_size; + + g_line = g_line + fy_start * g_size + fx_start; + + v = 0; + for (fy = fy_start; fy < fy_end; fy++) + { + g_data = g_line; + g_line += g_size; + + for (fx = fx_start; fx < fx_end; fx++) + v += *g_data++; + } + if (v > 1) + v = 1; + + v *= (opacity * 255.0); + + r = (unsigned int) v; + + return (unsigned char) r; +} + +static unsigned char * +mb_wm_comp_mgr_clutter_shadow_gaussian_make_tile () +{ + unsigned char * data; + int size; + int center; + int x, y; + unsigned char d; + int pwidth, pheight; + double opacity = SHADOW_OPACITY; + static MBGaussianMap * gaussian_map = NULL; + + struct _mypixel + { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + } * _d; + + + if (!gaussian_map) + gaussian_map = + mb_wm_comp_mgr_clutter_make_gaussian_map (SHADOW_RADIUS); + + size = gaussian_map->size; + center = size / 2; + + /* Top & bottom */ + + pwidth = MAX_TILE_SZ; + pheight = MAX_TILE_SZ; + + data = mb_wm_util_malloc0 (4 * WIDTH * HEIGHT); + + _d = (struct _mypixel*) data; + + /* N */ + for (y = 0; y < pheight; y++) + { + d = mb_wm_comp_mgr_clutter_sum_gaussian (gaussian_map, opacity, + center, y - center, + WIDTH, HEIGHT); + for (x = 0; x < pwidth; x++) + { + _d[y*3*pwidth + x + pwidth].r = 0; + _d[y*3*pwidth + x + pwidth].g = 0; + _d[y*3*pwidth + x + pwidth].b = 0; + _d[y*3*pwidth + x + pwidth].a = d; + } + + } + + /* S */ + pwidth = MAX_TILE_SZ; + pheight = MAX_TILE_SZ; + + for (y = 0; y < pheight; y++) + { + d = mb_wm_comp_mgr_clutter_sum_gaussian (gaussian_map, opacity, + center, y - center, + WIDTH, HEIGHT); + for (x = 0; x < pwidth; x++) + { + _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].r = 0; + _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].g = 0; + _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].b = 0; + _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].a = d; + } + + } + + + /* w */ + pwidth = MAX_TILE_SZ; + pheight = MAX_TILE_SZ; + + for (x = 0; x < pwidth; x++) + { + d = mb_wm_comp_mgr_clutter_sum_gaussian (gaussian_map, opacity, + x - center, center, + WIDTH, HEIGHT); + for (y = 0; y < pheight; y++) + { + _d[y*3*pwidth + 3*pwidth*pheight + x].r = 0; + _d[y*3*pwidth + 3*pwidth*pheight + x].g = 0; + _d[y*3*pwidth + 3*pwidth*pheight + x].b = 0; + _d[y*3*pwidth + 3*pwidth*pheight + x].a = d; + } + + } + + /* E */ + for (x = 0; x < pwidth; x++) + { + d = mb_wm_comp_mgr_clutter_sum_gaussian (gaussian_map, opacity, + x - center, center, + WIDTH, HEIGHT); + for (y = 0; y < pheight; y++) + { + _d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].r = 0; + _d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].g = 0; + _d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].b = 0; + _d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].a = d; + } + + } + + /* NW */ + pwidth = MAX_TILE_SZ; + pheight = MAX_TILE_SZ; + + for (x = 0; x < pwidth; x++) + for (y = 0; y < pheight; y++) + { + d = mb_wm_comp_mgr_clutter_sum_gaussian (gaussian_map, opacity, + x-center, y-center, + WIDTH, HEIGHT); + + _d[y*3*pwidth + x].r = 0; + _d[y*3*pwidth + x].g = 0; + _d[y*3*pwidth + x].b = 0; + _d[y*3*pwidth + x].a = d; + } + + /* SW */ + for (x = 0; x < pwidth; x++) + for (y = 0; y < pheight; y++) + { + d = mb_wm_comp_mgr_clutter_sum_gaussian (gaussian_map, opacity, + x-center, y-center, + WIDTH, HEIGHT); + + _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].r = 0; + _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].g = 0; + _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].b = 0; + _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].a = d; + } + + /* SE */ + for (x = 0; x < pwidth; x++) + for (y = 0; y < pheight; y++) + { + d = mb_wm_comp_mgr_clutter_sum_gaussian (gaussian_map, opacity, + x-center, y-center, + WIDTH, HEIGHT); + + _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) + + 2*pwidth].r = 0; + _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) + + 2*pwidth].g = 0; + _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) + + 2*pwidth].b = 0; + _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) + + 2*pwidth].a = d; + } + + /* NE */ + for (x = 0; x < pwidth; x++) + for (y = 0; y < pheight; y++) + { + d = mb_wm_comp_mgr_clutter_sum_gaussian (gaussian_map, opacity, + x-center, y-center, WIDTH, HEIGHT); + + _d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].r = 0; + _d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].g = 0; + _d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].b = 0; + _d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].a = d; + } + + /* center */ + pwidth = MAX_TILE_SZ; + pheight = MAX_TILE_SZ; + + d = mb_wm_comp_mgr_clutter_sum_gaussian (gaussian_map, opacity, + center, center, WIDTH, HEIGHT); + + for (x = 0; x < pwidth; x++) + for (y = 0; y < pheight; y++) + { + _d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].r = 0; + _d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].g = 0; + _d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].b = 0; + _d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].a = d; + } + + return data; +} + + diff --git a/matchbox/mb-wm-comp-mgr-clutter.h b/matchbox/mb-wm-comp-mgr-clutter.h new file mode 100644 index 0000000..f67f46c --- /dev/null +++ b/matchbox/mb-wm-comp-mgr-clutter.h @@ -0,0 +1,108 @@ +/* + * Matchbox Window Manager - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 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. + * + */ + +#ifndef _HAVE_MB_WM_COMP_MGR_CLUTTER_H +#define _HAVE_MB_WM_COMP_MGR_CLUTTER_H + +#include <matchbox/mb-wm-config.h> +#include <clutter/clutter.h> + +#define MB_WM_COMP_MGR_CLUTTER(c) ((MBWMCompMgrClutter*)(c)) +#define MB_WM_COMP_MGR_CLUTTER_CLASS(c) ((MBWMCompMgrClutterClass*)(c)) +#define MB_WM_TYPE_COMP_MGR_CLUTTER (mb_wm_comp_mgr_clutter_class_type ()) + +#define MB_WM_COMP_MGR_CLUTTER_CLIENT(c) ((MBWMCompMgrClutterClient*)(c)) +#define MB_WM_COMP_MGR_CLUTTER_CLIENT_CLASS(c) ((MBWMCompMgrClutterClientClass*)(c)) +#define MB_WM_TYPE_COMP_MGR_CLUTTER_CLIENT (mb_wm_comp_mgr_clutter_client_class_type ()) + +typedef struct _MBWMCompMgrClutter MBWMCompMgrClutter; +typedef struct _MBWMCompMgrClutterClass MBWMCompMgrClutterClass; +typedef struct _MBWMCompMgrClutterPrivate MBWMCompMgrClutterPrivate; + +typedef struct _MBWMCompMgrClutterClient MBWMCompMgrClutterClient; +typedef struct _MBWMCompMgrClutterClientClass MBWMCompMgrClutterClientClass; +typedef struct _MBWMCompMgrClutterClientPrivate MBWMCompMgrClutterClientPrivate; + +typedef enum +{ + MBWMCompMgrClutterClientMapped = (1<<0), + MBWMCompMgrClutterClientDontUpdate = (1<<1), + MBWMCompMgrClutterClientDone = (1<<2), + MBWMCompMgrClutterClientEffectRunning = (1<<3), +} MBWMCompMgrClutterClientFlags; + +struct _MBWMCompMgrClutter +{ + MBWMCompMgr parent; + MBWMCompMgrClutterPrivate *priv; +}; + +struct _MBWMCompMgrClutterClass +{ + MBWMCompMgrClass parent; + + MBWMCompMgrClient * (*client_new) (MBWindowManagerClient * client); +}; + +int +mb_wm_comp_mgr_clutter_class_type (); + +MBWMCompMgr* +mb_wm_comp_mgr_clutter_new (MBWindowManager *wm); + +struct _MBWMCompMgrClutterClient +{ + MBWMCompMgrClient parent; + + MBWMCompMgrClutterClientPrivate *priv; +}; + +struct _MBWMCompMgrClutterClientClass +{ + MBWMCompMgrClientClass parent; +}; + +int +mb_wm_comp_mgr_clutter_client_class_type (); + +ClutterActor * +mb_wm_comp_mgr_clutter_client_get_actor (MBWMCompMgrClutterClient *cclient); + +void +mb_wm_comp_mgr_clutter_client_set_flags (MBWMCompMgrClutterClient *cclient, + MBWMCompMgrClutterClientFlags flags); + +void +mb_wm_comp_mgr_clutter_client_unset_flags (MBWMCompMgrClutterClient *cclient, + MBWMCompMgrClutterClientFlags flags); + +MBWMCompMgrClutterClientFlags +mb_wm_comp_mgr_clutter_client_get_flags (MBWMCompMgrClutterClient *cclient); + +MBWMList * +mb_wm_comp_mgr_clutter_get_desktops (MBWMCompMgrClutter *cmgr); + +ClutterActor * +mb_wm_comp_mgr_clutter_get_nth_desktop (MBWMCompMgrClutter *cmgr, int desktop); + +ClutterActor * +mb_wm_comp_mgr_clutter_get_arena (MBWMCompMgrClutter *cmgr); + +#endif diff --git a/matchbox/mb-wm-comp-mgr-xrender.c b/matchbox/mb-wm-comp-mgr-xrender.c new file mode 100644 index 0000000..b3ae255 --- /dev/null +++ b/matchbox/mb-wm-comp-mgr-xrender.c @@ -0,0 +1,1837 @@ +/* + * Matchbox Window Manager - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2002, 2004, 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 "matchbox.h" +#include "mb-wm-client.h" +#include "mb-wm-comp-mgr.h" +#include "mb-wm-comp-mgr-xrender.h" + +#include <math.h> + +#include <X11/Xresource.h> +#include <X11/extensions/Xdamage.h> +#include <X11/extensions/Xrender.h> +#include <X11/extensions/Xcomposite.h> + +#define SHADOW_RADIUS 6 +#define SHADOW_OPACITY 0.75 +#define SHADOW_OFFSET_X (-SHADOW_RADIUS) +#define SHADOW_OFFSET_Y (-SHADOW_RADIUS) + +/* + * A helper object to store manager's per-client data + */ +struct MBWMCompMgrDefaultClient +{ + MBWMCompMgrClient parent; + + int damaged; + Damage damage; + Picture picture; + XserverRegion extents; + XserverRegion border_clip; +}; + +static void +mb_wm_comp_mgr_xrender_client_show_real (MBWMCompMgrClient * client); + +static void +mb_wm_comp_mgr_xrender_client_hide_real (MBWMCompMgrClient * client); + +static void +mb_wm_comp_mgr_xrender_client_repair_real (MBWMCompMgrClient * client); + +static void +mb_wm_comp_mgr_xrender_client_configure_real (MBWMCompMgrClient * client); + +static void +mb_wm_comp_mgr_xrender_client_class_init (MBWMObjectClass *klass) +{ + MBWMCompMgrClientClass *c_klass = MB_WM_COMP_MGR_CLIENT_CLASS (klass); + + c_klass->show = mb_wm_comp_mgr_xrender_client_show_real; + c_klass->hide = mb_wm_comp_mgr_xrender_client_hide_real; + c_klass->repair = mb_wm_comp_mgr_xrender_client_repair_real; + c_klass->configure = mb_wm_comp_mgr_xrender_client_configure_real; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMCompMgrDefaultClient"; +#endif +} + +static int +mb_wm_comp_mgr_xrender_client_init (MBWMObject *obj, va_list vap) +{ + MBWMCompMgrClient *client = MB_WM_COMP_MGR_CLIENT (obj); + MBWMCompMgrDefaultClient *dclient = MB_WM_COMP_MGR_DEFAULT_CLIENT (obj); + MBWindowManager *wm; + MBWindowManagerClient *wm_client = client->wm_client; + + if (!wm_client || !wm_client->wmref) + return 0; + + return 1; +} + +static void +mb_wm_comp_mgr_xrender_client_destroy (MBWMObject* obj) +{ + MBWMCompMgrClient * c = MB_WM_COMP_MGR_CLIENT (obj); + MBWMCompMgrDefaultClient * dc = MB_WM_COMP_MGR_DEFAULT_CLIENT (obj); + MBWindowManager * wm = c->wm; + + mb_wm_comp_mgr_client_hide (c); + + if (dc->damage) + XDamageDestroy (wm->xdpy, dc->damage); + + if (dc->picture) + XRenderFreePicture (wm->xdpy, dc->picture); + + if (dc->extents) + XFixesDestroyRegion (wm->xdpy, dc->extents); + + if (dc->border_clip) + XFixesDestroyRegion (wm->xdpy, dc->border_clip); +} + +int +mb_wm_comp_mgr_xrender_client_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMCompMgrDefaultClientClass), + sizeof (MBWMCompMgrDefaultClient), + mb_wm_comp_mgr_xrender_client_init, + mb_wm_comp_mgr_xrender_client_destroy, + mb_wm_comp_mgr_xrender_client_class_init + }; + + type = + mb_wm_object_register_class (&info, MB_WM_TYPE_COMP_MGR_CLIENT, 0); + } + + return type; +} + +/* + * This is a private method, hence static + */ +static MBWMCompMgrClient * +mb_wm_comp_mgr_xrender_client_new (MBWindowManagerClient * client) +{ + MBWMObject *c; + + c = mb_wm_object_new (MB_WM_TYPE_COMP_MGR_DEFAULT_CLIENT, + MBWMObjectPropClient, client, + NULL); + + return MB_WM_COMP_MGR_CLIENT (c); +} + +static void +mb_wm_comp_mgr_xrender_add_damage (MBWMCompMgr * mgr, XserverRegion damage); + +static XserverRegion +mb_wm_comp_mgr_xrender_client_extents (MBWMCompMgrClient *client); + +static void +mb_wm_comp_mgr_xrender_client_hide_real (MBWMCompMgrClient * client) +{ + MBWMCompMgrDefaultClient * dclient = MB_WM_COMP_MGR_DEFAULT_CLIENT (client); + MBWindowManagerClient * wm_client = client->wm_client; + MBWindowManager * wm = client->wm; + MBWMCompMgr * mgr = wm->comp_mgr; + MBWindowManagerClient * c; + Bool is_modal = mb_wm_client_is_modal (wm_client); + + if (is_modal && ((c = mb_wm_get_visible_main_client (wm)) != NULL)) + { + XserverRegion extents; + /* We need to make sure the any lowlighting on a 'parent' + * modal for app gets cleared. This is kind of a sledgehammer + * approach to it, but more suttle attempts oddly fail at times. + * + * FIXME: keep an eye on this for future revisions of composite + * - there may be a better way. + */ + mb_wm_comp_mgr_xrender_client_repair_real (c->cm_client); + extents = mb_wm_comp_mgr_xrender_client_extents (c->cm_client); + mb_wm_comp_mgr_xrender_add_damage (mgr, extents); + } + + if (dclient->damage) + { + XDamageDestroy (wm->xdpy, dclient->damage); + dclient->damage = None; + } + + if (dclient->extents) + { + mb_wm_comp_mgr_xrender_add_damage (mgr, dclient->extents); + dclient->extents = None; + } + + if (dclient->picture) + { + XRenderFreePicture (wm->xdpy, dclient->picture); + dclient->picture = None; + } +} + +static void +mb_wm_comp_mgr_xrender_client_show_real (MBWMCompMgrClient * client) +{ + MBWMCompMgrDefaultClient * dclient = MB_WM_COMP_MGR_DEFAULT_CLIENT (client); + MBWindowManagerClient * wm_client = client->wm_client; + MBWindowManager * wm = client->wm; + MBWMCompMgr * mgr = wm->comp_mgr; + XserverRegion region; + XRenderPictureAttributes pa; + MBWMClientType ctype = MB_WM_CLIENT_CLIENT_TYPE (wm_client); + Bool is_modal; + + /* + * Destroying / Recreating the client pictures should hopefully save + * some memory in the server. + */ + if (!dclient->picture) + { + pa.subwindow_mode = IncludeInferiors; + + dclient->picture = + XRenderCreatePicture (wm->xdpy, + wm_client->xwin_frame ? + wm_client->xwin_frame : + wm_client->window->xwindow, + client->is_argb32 ? + XRenderFindStandardFormat (wm->xdpy, + PictStandardARGB32) + + : XRenderFindVisualFormat (wm->xdpy, + wm_client->window->visual), + CPSubwindowMode, + &pa); + } + + if (dclient->damage != None) + XDamageDestroy (wm->xdpy, dclient->damage); + + dclient->damage = XDamageCreate (wm->xdpy, + wm_client->xwin_frame ? + wm_client->xwin_frame : + wm_client->window->xwindow, + XDamageReportNonEmpty); + + region = mb_wm_comp_mgr_xrender_client_extents (client); + + mb_wm_comp_mgr_xrender_add_damage (mgr, region); + + /* + * If the wm client is modal we have to add its parent to the damage + * in order for lowlighting to work + */ + if (mb_wm_client_is_modal (wm_client)) + { + MBWindowManagerClient * parent = + mb_wm_client_get_transient_for (wm_client); + + if (parent && parent->cm_client) + { + XserverRegion extents = + mb_wm_comp_mgr_xrender_client_extents (parent->cm_client); + + mb_wm_comp_mgr_xrender_add_damage (mgr, extents); + } + } + + if (!dclient->extents) + { + dclient->extents = mb_wm_comp_mgr_xrender_client_extents (client); + } +} + + +/* + * The Manager itself + */ + +typedef struct MBGaussianMap +{ + int size; + double * data; +} MBGaussianMap; + + +struct MBWMCompMgrDefaultPrivate +{ + MBGaussianMap * gaussian_map; + + Picture shadow_n_pic; + Picture shadow_e_pic; + Picture shadow_s_pic; + Picture shadow_w_pic; + + Picture shadow_ne_pic; + Picture shadow_nw_pic; + Picture shadow_se_pic; + Picture shadow_sw_pic; + + Picture shadow_pic; + + int shadow_dx; + int shadow_dy; + int shadow_padding_width; + int shadow_padding_height; + int shadow_style; + unsigned int shadow_color[4]; /* RGBA */ + + Picture trans_picture; + Picture black_picture; + Picture lowlight_picture; + unsigned int lowlight_params[4]; /* RGBA */ + + Picture root_picture; + Picture root_buffer; + + XserverRegion all_damage; + Bool dialog_shade; +}; + +static void +mb_wm_comp_mgr_xrender_private_free (MBWMCompMgrDefault *mgr) +{ + MBWMCompMgrDefaultPrivate * priv = mgr->priv; + Display * xdpy = MB_WM_COMP_MGR (mgr)->wm->xdpy; + + if (priv->gaussian_map) + free (priv->gaussian_map); + + XRenderFreePicture (xdpy, priv->shadow_n_pic); + XRenderFreePicture (xdpy, priv->shadow_e_pic); + XRenderFreePicture (xdpy, priv->shadow_s_pic); + XRenderFreePicture (xdpy, priv->shadow_w_pic); + + XRenderFreePicture (xdpy, priv->shadow_ne_pic); + XRenderFreePicture (xdpy, priv->shadow_nw_pic); + XRenderFreePicture (xdpy, priv->shadow_se_pic); + XRenderFreePicture (xdpy, priv->shadow_sw_pic); + + XRenderFreePicture (xdpy, priv->shadow_pic); + + XRenderFreePicture (xdpy, priv->black_picture); + XRenderFreePicture (xdpy, priv->lowlight_picture); + XRenderFreePicture (xdpy, priv->trans_picture); + + if (priv->root_picture) + XRenderFreePicture (xdpy, priv->root_picture); + + if (priv->root_buffer) + XRenderFreePicture (xdpy, priv->root_buffer); + + if (priv->all_damage) + XDamageDestroy (xdpy, priv->all_damage); + + free (priv); +} + +static void +mb_wm_comp_mgr_xrender_register_client_real (MBWMCompMgr * mgr, + MBWindowManagerClient * c) +{ + MBWMCompMgrDefaultPrivate * priv = MB_WM_COMP_MGR_DEFAULT (mgr)->priv; + + MBWM_NOTE (COMPOSITOR, "@@@@ registering client for %x @@@@\n", + c->window->xwindow); + + if (c->cm_client) + return; + + c->cm_client = mb_wm_comp_mgr_xrender_client_new (c); +} + +static void +mb_wm_comp_mgr_xrender_turn_on_real (MBWMCompMgr *mgr); + +static void +mb_wm_comp_mgr_xrender_turn_off_real (MBWMCompMgr *mgr); + +static void +mb_wm_comp_mgr_xrender_render_real (MBWMCompMgr *mgr); + +static Bool +mb_wm_comp_mgr_xrender_handle_damage (XDamageNotifyEvent * de, + MBWMCompMgr * mgr); + +static void +mb_wm_comp_mgr_xrender_class_init (MBWMObjectClass *klass) +{ + MBWMCompMgrClass *cm_klass = MB_WM_COMP_MGR_CLASS (klass); + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMCompMgrDefault"; +#endif + + cm_klass->register_client = mb_wm_comp_mgr_xrender_register_client_real; + cm_klass->turn_on = mb_wm_comp_mgr_xrender_turn_on_real; + cm_klass->turn_off = mb_wm_comp_mgr_xrender_turn_off_real; + cm_klass->render = mb_wm_comp_mgr_xrender_render_real; + cm_klass->handle_damage = mb_wm_comp_mgr_xrender_handle_damage; +} + +static void +mb_wm_comp_mgr_xrender_init_pictures (MBWMCompMgr *mgr); + +static int +mb_wm_comp_mgr_xrender_init (MBWMObject *obj, va_list vap) +{ + MBWMCompMgr * mgr = MB_WM_COMP_MGR (obj); + MBWMCompMgrDefault * dmgr = MB_WM_COMP_MGR_DEFAULT (obj); + MBWindowManager * wm = mgr->wm; + MBWMCompMgrDefaultPrivate * priv; + + if (!wm) + return 0; + + dmgr->priv = mb_wm_util_malloc0 (sizeof (MBWMCompMgrDefaultPrivate)); + priv = dmgr->priv; + + priv->shadow_dx = SHADOW_OFFSET_X; + priv->shadow_dy = SHADOW_OFFSET_Y; + + /* Not really used yet */ + priv->shadow_padding_width = 0; + priv->shadow_padding_height = 0; + + mgr->disabled = True; + + XCompositeRedirectSubwindows (wm->xdpy, wm->root_win->xwindow, + CompositeRedirectManual); + + mb_wm_theme_get_shadow_color (wm->theme, + &priv->shadow_color[0], + &priv->shadow_color[1], + &priv->shadow_color[2], + &priv->shadow_color[3]); + + mb_wm_theme_get_lowlight_color (wm->theme, + &priv->lowlight_params[0], + &priv->lowlight_params[1], + &priv->lowlight_params[2], + &priv->lowlight_params[3]); + + priv->shadow_style = mb_wm_theme_get_shadow_type (wm->theme); + + mb_wm_comp_mgr_xrender_init_pictures (mgr); + + return 1; +} + +static void +mb_wm_comp_mgr_xrender_destroy (MBWMObject * obj) +{ + MBWMCompMgr * mgr = MB_WM_COMP_MGR (obj); + MBWMCompMgrDefault * dmgr = MB_WM_COMP_MGR_DEFAULT (obj); + + mb_wm_comp_mgr_turn_off (mgr); + mb_wm_comp_mgr_xrender_private_free (dmgr); +} + +int +mb_wm_comp_mgr_xrender_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMCompMgrDefaultClass), + sizeof (MBWMCompMgrDefault), + mb_wm_comp_mgr_xrender_init, + mb_wm_comp_mgr_xrender_destroy, + mb_wm_comp_mgr_xrender_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_COMP_MGR, 0); + } + + return type; +} + +/* Shadow Generation */ +static double +gaussian (double r, double x, double y) +{ + return ((1 / (sqrt (2 * M_PI * r))) * + exp ((- (x * x + y * y)) / (2 * r * r))); +} + + +static MBGaussianMap * +mb_wm_comp_mgr_xrender_make_gaussian_map (double r) +{ + MBGaussianMap *c; + int size = ((int) ceil ((r * 3)) + 1) & ~1; + int center = size / 2; + int x, y; + double t = 0.0; + double g; + + c = malloc (sizeof (MBGaussianMap) + size * size * sizeof (double)); + c->size = size; + + c->data = (double *) (c + 1); + + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) + { + g = gaussian (r, (double) (x - center), (double) (y - center)); + t += g; + c->data[y * size + x] = g; + } + + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) + c->data[y*size + x] /= t; + + return c; +} + +static unsigned char +mb_wm_comp_mgr_xrender_sum_gaussian (MBWMCompMgr * mgr, double opacity, + int x, int y, int width, int height) +{ + MBGaussianMap * map = MB_WM_COMP_MGR_DEFAULT (mgr)->priv->gaussian_map; + int fx, fy; + double * g_data; + double * g_line = map->data; + int g_size = map->size; + int center = g_size / 2; + int fx_start, fx_end; + int fy_start, fy_end; + double v; + + /* + * Compute set of filter values which are "in range", + * that's the set with: + * 0 <= x + (fx-center) && x + (fx-center) < width && + * 0 <= y + (fy-center) && y + (fy-center) < height + * + * 0 <= x + (fx - center) x + fx - center < width + * center - x <= fx fx < width + center - x + */ + + fx_start = center - x; + if (fx_start < 0) + fx_start = 0; + fx_end = width + center - x; + if (fx_end > g_size) + fx_end = g_size; + + fy_start = center - y; + if (fy_start < 0) + fy_start = 0; + fy_end = height + center - y; + if (fy_end > g_size) + fy_end = g_size; + + g_line = g_line + fy_start * g_size + fx_start; + + v = 0; + for (fy = fy_start; fy < fy_end; fy++) + { + g_data = g_line; + g_line += g_size; + + for (fx = fx_start; fx < fx_end; fx++) + v += *g_data++; + } + if (v > 1) + v = 1; + + return ((unsigned int) (v * opacity * 255.0)); +} + +#define MAX_TILE_SZ 16 /* make sure size/2 < MAX_TILE_SZ */ +#define WIDTH 320 +#define HEIGHT 320 + +static void +mb_wm_comp_mgr_xrender_shadow_setup_part (MBWMCompMgr * mgr, + XImage ** ximage, + Picture * pic, + Pixmap * pxm, + int width, + int height) +{ + MBWindowManager * wm = mgr->wm; + + *ximage = XCreateImage (wm->xdpy, DefaultVisual(wm->xdpy, wm->xscreen), + 8, ZPixmap, 0, 0, + width, height, 8, width * sizeof (unsigned char)); + + (*ximage)->data = malloc (width * height * sizeof (unsigned char)); + + *pxm = XCreatePixmap (wm->xdpy, wm->root_win->xwindow, + width, height, 8); + + *pic = XRenderCreatePicture (wm->xdpy, *pxm, + XRenderFindStandardFormat (wm->xdpy, + PictStandardA8), + 0, 0); +} + +static void +mb_wm_comp_mgr_xrender_shadow_finalise_part (MBWMCompMgr * mgr, + XImage * ximage, + Picture pic, + Pixmap pxm, + int width, + int height) +{ + MBWindowManager * wm = mgr->wm; + + GC gc = XCreateGC (wm->xdpy, pxm, 0, 0); + XPutImage (wm->xdpy, pxm, gc, ximage, 0, 0, 0, 0, width, height); + XDestroyImage (ximage); + XFreeGC (wm->xdpy, gc); + XFreePixmap (wm->xdpy, pxm); +} + +static void +mb_wm_comp_mgr_xrender_shadow_setup (MBWMCompMgr * mgr) +{ + MBWindowManager * wm = mgr->wm; + MBWMCompMgrDefaultPrivate * priv = MB_WM_COMP_MGR_DEFAULT (mgr)->priv; + XImage * ximage; + Pixmap pxm; + unsigned char * data; + int size; + int center; + int x, y; + unsigned char d; + int pwidth, pheight; + double opacity = SHADOW_OPACITY; + + if (priv->shadow_style == MBWM_COMP_MGR_SHADOW_NONE) + return; + + if (priv->shadow_style == MBWM_COMP_MGR_SHADOW_SIMPLE) + { + priv->shadow_padding_width = 0; + priv->shadow_padding_height = 0; + return; + } + + /* SHADOW_STYLE_GAUSSIAN */ + priv->gaussian_map = + mb_wm_comp_mgr_xrender_make_gaussian_map (SHADOW_RADIUS); + + priv->shadow_padding_width = priv->gaussian_map->size; + priv->shadow_padding_height = priv->gaussian_map->size; + + size = priv->gaussian_map->size; + center = size / 2; + + /* Top & bottom */ + pwidth = MAX_TILE_SZ; + pheight = size/2; + mb_wm_comp_mgr_xrender_shadow_setup_part (mgr, + &ximage, &priv->shadow_n_pic, &pxm, + pwidth, pheight); + + data = (unsigned char*)ximage->data; + + for (y = 0; y < pheight; y++) + { + d = mb_wm_comp_mgr_xrender_sum_gaussian (mgr, opacity, + center, y - center, + WIDTH, HEIGHT); + for (x = 0; x < pwidth; x++) + data[y * pwidth + x] = d; + } + + mb_wm_comp_mgr_xrender_shadow_finalise_part (mgr, ximage, priv->shadow_n_pic, + pxm, pwidth, pheight); + + pwidth = MAX_TILE_SZ; + pheight = MAX_TILE_SZ; + + mb_wm_comp_mgr_xrender_shadow_setup_part (mgr, &ximage, &priv->shadow_s_pic, + &pxm, pwidth, pheight); + + data = (unsigned char*)ximage->data; + + for (y = 0; y < pheight; y++) + { + d = mb_wm_comp_mgr_xrender_sum_gaussian (mgr, opacity, + center, y - center, + WIDTH, HEIGHT); + for (x = 0; x < pwidth; x++) + data[(pheight - y - 1) * pwidth + x] = d; + } + + mb_wm_comp_mgr_xrender_shadow_finalise_part (mgr, ximage, priv->shadow_s_pic, + pxm, pwidth, pheight); + + /* Sides */ + pwidth = MAX_TILE_SZ; + pheight = MAX_TILE_SZ; + mb_wm_comp_mgr_xrender_shadow_setup_part (mgr, &ximage, &priv->shadow_w_pic, + &pxm, pwidth, pheight); + + data = (unsigned char*)ximage->data; + + for (x = 0; x < pwidth; x++) + { + d = mb_wm_comp_mgr_xrender_sum_gaussian (mgr, opacity, + x - center, center, + WIDTH, HEIGHT); + for (y = 0; y < pheight; y++) + data[y * pwidth + (pwidth - x - 1)] = d; + } + + mb_wm_comp_mgr_xrender_shadow_finalise_part (mgr, ximage, priv->shadow_w_pic, + pxm, pwidth, pheight); + + mb_wm_comp_mgr_xrender_shadow_setup_part (mgr, &ximage, &priv->shadow_e_pic, + &pxm, pwidth, pheight); + + data = (unsigned char*)ximage->data; + + for (x = 0; x < pwidth; x++) + { + d = mb_wm_comp_mgr_xrender_sum_gaussian (mgr, opacity, + x - center, center, + WIDTH, HEIGHT); + for (y = 0; y < pheight; y++) + data[y * pwidth + x] = d; + } + + mb_wm_comp_mgr_xrender_shadow_finalise_part (mgr, ximage, priv->shadow_e_pic, + pxm, pwidth, pheight); + + /* Corners */ + pwidth = MAX_TILE_SZ; + pheight = MAX_TILE_SZ; + mb_wm_comp_mgr_xrender_shadow_setup_part (mgr, &ximage, &priv->shadow_nw_pic, + &pxm, pwidth, pheight); + + data = (unsigned char*)ximage->data; + + for (x = 0; x < pwidth; x++) + for (y = 0; y < pheight; y++) + { + d = mb_wm_comp_mgr_xrender_sum_gaussian (mgr, opacity, + x-center, y-center, + WIDTH, HEIGHT); + + data[y * pwidth + x] = d; + } + + mb_wm_comp_mgr_xrender_shadow_finalise_part (mgr, ximage, + priv->shadow_nw_pic, + pxm, pwidth, pheight); + + mb_wm_comp_mgr_xrender_shadow_setup_part (mgr, &ximage, &priv->shadow_sw_pic, + &pxm, pwidth, pheight); + + data = (unsigned char*)ximage->data; + + for (x = 0; x < pwidth; x++) + for (y = 0; y < pheight; y++) + { + d = mb_wm_comp_mgr_xrender_sum_gaussian (mgr, opacity, + x-center, y-center, + WIDTH, HEIGHT); + + data[(pheight - y - 1) * pwidth + x] = d; + } + + mb_wm_comp_mgr_xrender_shadow_finalise_part (mgr, ximage, + priv->shadow_sw_pic, + pxm, pwidth, pheight); + + mb_wm_comp_mgr_xrender_shadow_setup_part (mgr, &ximage, &priv->shadow_se_pic, + &pxm, pwidth, pheight); + + data = (unsigned char*)ximage->data; + + for (x = 0; x < pwidth; x++) + for (y = 0; y < pheight; y++) + { + d = mb_wm_comp_mgr_xrender_sum_gaussian (mgr, opacity, + x-center, y-center, + WIDTH, HEIGHT); + + data[(pheight - y - 1) * pwidth + (pwidth - x -1)] = d; + } + + mb_wm_comp_mgr_xrender_shadow_finalise_part (mgr, ximage, + priv->shadow_se_pic, + pxm, pwidth, pheight); + + mb_wm_comp_mgr_xrender_shadow_setup_part(mgr, &ximage, &priv->shadow_ne_pic, + &pxm, pwidth, pheight); + + data = (unsigned char*)ximage->data; + + for (x = 0; x < pwidth; x++) + for (y = 0; y < pheight; y++) + { + d = mb_wm_comp_mgr_xrender_sum_gaussian (mgr, opacity, + x-center, y-center, WIDTH, HEIGHT); + + data[y * pwidth + (pwidth - x -1)] = d; + } + + mb_wm_comp_mgr_xrender_shadow_finalise_part (mgr, ximage, + priv->shadow_ne_pic, + pxm, pwidth, pheight); + + /* Finally center */ + pwidth = MAX_TILE_SZ; + pheight = MAX_TILE_SZ; + mb_wm_comp_mgr_xrender_shadow_setup_part (mgr, &ximage, &priv->shadow_pic, + &pxm, pwidth, pheight); + + data = (unsigned char*)ximage->data; + + d = mb_wm_comp_mgr_xrender_sum_gaussian (mgr, opacity, + center, center, WIDTH, HEIGHT); + + for (x = 0; x < pwidth; x++) + for (y = 0; y < pheight; y++) + data[y * pwidth + x] = d; + + mb_wm_comp_mgr_xrender_shadow_finalise_part (mgr, ximage, priv->shadow_pic, + pxm, pwidth, pheight); + +} + +static Picture +mb_wm_comp_mgr_xrender_shadow_gaussian_make_picture (MBWMCompMgr * mgr, + int width, int height) +{ + MBWindowManager * wm = mgr->wm; + MBWMCompMgrDefaultPrivate * priv = MB_WM_COMP_MGR_DEFAULT (mgr)->priv; + Picture pic; + Pixmap pxm; + int pwidth, pheight, x, y, dw, dh; + + pxm = XCreatePixmap (wm->xdpy, wm->root_win->xwindow, width, height, 8); + pic = XRenderCreatePicture (wm->xdpy, pxm, + XRenderFindStandardFormat (wm->xdpy, + PictStandardA8), + 0,0); + + pwidth = MAX_TILE_SZ; + pheight = MAX_TILE_SZ; + + for (x=0; x < width; x += pwidth) + for (y=0; y < height; y += pheight) + { + if ( (y + pheight) > height ) + dh = pheight - ((y + pheight)-height); + else + dh = pheight; + + if ( (x + pwidth) > width ) + dw = pwidth - ((x + pwidth)-width); + else + dw = pwidth; + + XRenderComposite (wm->xdpy, PictOpSrc, + priv->shadow_pic, None, pic, + 0, 0, 0, 0, x, y, dw, dh); + } + + /* Top & bottom */ + if ( width > (MAX_TILE_SZ*2) ) + { + pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; + + for (x=0; x < width; x += pwidth ) + { + if ( (x + pwidth) > width ) + dw = pwidth - ((x + pwidth)-width); + else + dw = pwidth; + + XRenderComposite (wm->xdpy, PictOpSrc, + priv->shadow_n_pic, None, pic, + 0, 0, 0, 0, x, 0, dw, pheight); + XRenderComposite (wm->xdpy, PictOpSrc, + priv->shadow_s_pic, None, pic, + 0, 0, 0, 0, x, height - pheight, dw, pheight); + } + } + + /* Sides */ + if ( height > (MAX_TILE_SZ*2) ) + { + pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; + + for (y=0; y < height; y += pheight) + { + if ( (y + pheight) > height ) + dh = pheight - ((y + pheight)-height); + else + dh = pheight; + + XRenderComposite (wm->xdpy, PictOpSrc /* PictOpIn */, + priv->shadow_e_pic, None, pic, + 0, 0, 0, 0, 0, y, pwidth, dh); + XRenderComposite (wm->xdpy, PictOpSrc /* PictOpIn */, + priv->shadow_w_pic, None, pic, + 0, 0, 0, 0, width - pwidth, y, pwidth, dh); + } + } + + /* Corners */ + pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; + + XRenderComposite (wm->xdpy, PictOpSrc, priv->shadow_nw_pic, None, pic, + 0, 0, 0, 0, 0, 0, pwidth, pheight); + + XRenderComposite (wm->xdpy, PictOpSrc, priv->shadow_ne_pic, None, pic, + 0, 0, 0, 0, width - pwidth, 0, pwidth, pheight); + + XRenderComposite (wm->xdpy, PictOpSrc, priv->shadow_sw_pic, None, pic, + 0, 0, 0, 0, 0, height - pheight, pwidth, pheight); + + XRenderComposite (wm->xdpy, PictOpSrc, priv->shadow_se_pic, None, pic, + 0, 0, 0, 0, width - pwidth, height - pheight, + pwidth, pheight); + + XFreePixmap (wm->xdpy, pxm); + return pic; +} + +static XserverRegion +mb_wm_comp_mgr_xrender_client_extents (MBWMCompMgrClient *client) +{ + MBWindowManagerClient *wm_client = client->wm_client; + MBWindowManager *wm = client->wm; + MBWMCompMgr *mgr = wm->comp_mgr; + MBWMCompMgrDefaultPrivate *priv = MB_WM_COMP_MGR_DEFAULT (mgr)->priv; + MBGeometry geom; + XRectangle r; + MBWMClientType ctype = MB_WM_CLIENT_CLIENT_TYPE (wm_client); + XserverRegion extents; + + mb_wm_client_get_coverage (wm_client, &geom); + + r.x = geom.x; + r.y = geom.y; + r.width = geom.width; + r.height = geom.height; + + if (priv->shadow_style) + { + if (ctype == MBWMClientTypeDialog || + ctype == MBWMClientTypeMenu || + ctype == MBWMClientTypeOverride) + { + if (priv->shadow_style == MBWM_COMP_MGR_SHADOW_SIMPLE) + { + r.width += priv->shadow_dx; + r.height += priv->shadow_dy; + } + else + { + r.x += priv->shadow_dx; + r.y += priv->shadow_dy; + r.width += priv->shadow_padding_width; + r.height += priv->shadow_padding_height; + } + } + } + + extents = XFixesCreateRegion (wm->xdpy, &r, 1); + + return extents; +} + +static XserverRegion +mb_wm_comp_mgr_xrender_client_border_size (MBWMCompMgrClient * client, + int x, int y) +{ + MBWindowManagerClient * wm_client = client->wm_client; + MBWindowManager * wm = client->wm; + XserverRegion border; + + border = XFixesCreateRegionFromWindow (wm->xdpy, + wm_client->xwin_frame ? + wm_client->xwin_frame : + wm_client->window->xwindow, + WindowRegionBounding); + /* translate this */ + XFixesTranslateRegion (wm->xdpy, border, x, y); + return border; +} + +static XserverRegion +mb_wm_comp_mgr_xrender_client_window_region (MBWMCompMgrClient *client, + Window xwin, int x, int y) +{ + MBWindowManagerClient * wm_client = client->wm_client; + MBWindowManager * wm = client->wm; + XserverRegion region; + + region = + XFixesCreateRegionFromWindow (wm->xdpy, xwin, WindowRegionBounding); + + /* translate this */ + XFixesTranslateRegion (wm->xdpy, region, x, y); + + return region; +} + +static Visual* +mb_wm_comp_mgr_xrender_get_argb32_visual (MBWMCompMgr * mgr) +{ + MBWindowManager * wm = mgr->wm; + XVisualInfo * xvi; + XVisualInfo template; + int nvi; + int i; + XRenderPictFormat * format; + Visual * visual = NULL; + + template.screen = wm->xscreen; + template.depth = 32; + template.class = TrueColor; + + if ((xvi = XGetVisualInfo (wm->xdpy, + VisualScreenMask|VisualDepthMask|VisualClassMask, + &template, + &nvi)) == NULL) + return NULL; + + for (i = 0; i < nvi; i++) + { + format = XRenderFindVisualFormat (wm->xdpy, xvi[i].visual); + if (format->type == PictTypeDirect && format->direct.alphaMask) + { + visual = xvi[i].visual; + break; + } + } + + XFree (xvi); + return visual; +} + +static void +mb_wm_comp_mgr_xrender_init_pictures (MBWMCompMgr *mgr) +{ + MBWindowManager * wm; + Window rwin; + MBWMCompMgrDefaultPrivate * priv; + Pixmap transPixmap, blackPixmap, lowlightPixmap, + redPixmap; + XRenderPictureAttributes pa; + XRenderColor c; + int i; + + if (!mgr) + return; + + wm = mgr->wm; + rwin = wm->root_win->xwindow; + priv = MB_WM_COMP_MGR_DEFAULT (mgr)->priv; + + { + Picture pics_to_free[] = { priv->trans_picture, + priv->black_picture, + priv->lowlight_picture, + priv->shadow_n_pic, + priv->shadow_e_pic, + priv->shadow_s_pic, + priv->shadow_w_pic, + priv->shadow_ne_pic, + priv->shadow_nw_pic, + priv->shadow_se_pic, + priv->shadow_sw_pic, + priv->shadow_pic }; + + for (i=0; i < (sizeof(pics_to_free)/sizeof(Picture)); i++) + if (pics_to_free[i] != None) + XRenderFreePicture (wm->xdpy, pics_to_free[i]); + } + + if (priv->shadow_style == MBWM_COMP_MGR_SHADOW_GAUSSIAN) + mb_wm_comp_mgr_xrender_shadow_setup (mgr); + + pa.subwindow_mode = IncludeInferiors; + pa.repeat = True; + + transPixmap = XCreatePixmap (wm->xdpy, rwin, 1, 1, 8); + + priv->trans_picture + = XRenderCreatePicture (wm->xdpy, transPixmap, + XRenderFindStandardFormat (wm->xdpy, + PictStandardA8), + CPRepeat, + &pa); + + c.red = c.green = c.blue = 0; + c.alpha = 0xddff; + + XRenderFillRectangle (wm->xdpy, PictOpSrc, priv->trans_picture, + &c, 0, 0, 1, 1); + + /* black pixmap used for shadows */ + + blackPixmap = XCreatePixmap (wm->xdpy, rwin, 1, 1, 32); + + priv->black_picture + = XRenderCreatePicture (wm->xdpy, blackPixmap, + XRenderFindStandardFormat (wm->xdpy, + PictStandardARGB32), + CPRepeat, + &pa); + + c.red = priv->shadow_color[0]; + c.green = priv->shadow_color[1]; + c.blue = priv->shadow_color[2]; + + if (priv->shadow_style == MBWM_COMP_MGR_SHADOW_GAUSSIAN) + c.alpha = 0xffff; + else + c.alpha = priv->shadow_color[3]; + + XRenderFillRectangle (wm->xdpy, PictOpSrc, priv->black_picture, + &c, 0, 0, 1, 1); + + /* Used for lowlights */ + lowlightPixmap = XCreatePixmap (wm->xdpy, rwin, 1, 1, 32); + priv->lowlight_picture + = XRenderCreatePicture (wm->xdpy, lowlightPixmap, + XRenderFindStandardFormat (wm->xdpy, + PictStandardARGB32), + CPRepeat, + &pa); + + c.red = priv->lowlight_params[0]; + c.green = priv->lowlight_params[1]; + c.blue = priv->lowlight_params[2]; + c.alpha = priv->lowlight_params[3]; + + XRenderFillRectangle (wm->xdpy, PictOpSrc, priv->lowlight_picture, + &c, 0, 0, 1, 1); + + + pa.repeat = False; + + priv->root_picture + = XRenderCreatePicture (wm->xdpy, rwin, + XRenderFindVisualFormat (wm->xdpy, + DefaultVisual (wm->xdpy, + wm->xscreen)), + CPSubwindowMode, + &pa); + + priv->all_damage = None; +} + +/* Shuts the compositing down */ +static void +mb_wm_comp_mgr_xrender_turn_off_real (MBWMCompMgr *mgr) +{ + MBWindowManager * wm; + Window rwin; + MBWMCompMgrDefaultPrivate * priv; + MBWMList * l; + + if (!mgr) + return; + + priv = MB_WM_COMP_MGR_DEFAULT (mgr)->priv; + + if (mgr->disabled) + return; + + wm = mgr->wm; + rwin = wm->root_win->xwindow; + + /* + * really shut down the composite manager. + */ + XCompositeUnredirectSubwindows (wm->xdpy, rwin, CompositeRedirectManual); + + if (priv->root_picture) + { + XRenderFreePicture (wm->xdpy, priv->root_picture); + priv->root_picture = None; + } + + if (priv->root_buffer) + { + XRenderFreePicture (wm->xdpy, priv->root_buffer); + priv->root_buffer = None; + } + + if (priv->all_damage) + { + XDamageDestroy (wm->xdpy, priv->all_damage); + priv->all_damage = None; + } + + /* Free up any client composite resources */ + l = wm->clients; + + while (l) + { + MBWindowManagerClient * wmc = l->data; + MBWMCompMgrClient * c = wmc->cm_client; + + if (c) + { + mb_wm_object_unref (MB_WM_OBJECT (c)); + wmc->cm_client = NULL; + } + + l = l->next; + } + + mgr->disabled = True; +} + +static void +mb_wm_comp_mgr_xrender_render_region (MBWMCompMgr *mgr, XserverRegion region); + +static void +mb_wm_comp_mgr_xrender_turn_on_real (MBWMCompMgr *mgr) +{ + MBWindowManager * wm; + Window rwin; + MBWMCompMgrDefaultPrivate * priv; + MBWMList * l; + + if (!mgr) + return; + + priv = MB_WM_COMP_MGR_DEFAULT (mgr)->priv; + + if (!mgr->disabled) + return; + + wm = mgr->wm; + rwin = wm->root_win->xwindow; + + XCompositeRedirectSubwindows (wm->xdpy, wm->root_win->xwindow, + CompositeRedirectManual); + + mb_wm_comp_mgr_xrender_init_pictures (mgr); + + XSync (wm->xdpy, False); + + mgr->disabled = False; + + if (!mb_wm_stack_empty (wm)) + { + MBWindowManagerClient * c; + + mb_wm_stack_enumerate (wm, c) + { + mb_wm_comp_mgr_xrender_register_client_real (mgr, c); + mb_wm_comp_mgr_xrender_client_show_real (c->cm_client); + } + + mb_wm_comp_mgr_xrender_render_region (mgr, None); + } +} + +static int +mb_wm_comp_mgr_xrender_client_get_translucency (MBWMCompMgrClient *client) +{ + MBWindowManagerClient * wm_client = client->wm_client; + + return wm_client->window->translucency; +} + +static void +mb_wm_comp_mgr_xrender_add_damage (MBWMCompMgr * mgr, XserverRegion damage) +{ + MBWMCompMgrDefaultPrivate * priv = MB_WM_COMP_MGR_DEFAULT (mgr)->priv; + MBWindowManager * wm = mgr->wm; + + if (priv->all_damage) + { + XFixesUnionRegion (wm->xdpy, priv->all_damage, priv->all_damage, + damage); + + XFixesDestroyRegion (wm->xdpy, damage); + } + else + priv->all_damage = damage; + + mb_wm_display_sync_queue (wm, MBWMSyncVisibility); +} + +static void +mb_wm_comp_mgr_xrender_client_repair_real (MBWMCompMgrClient * client) +{ + MBWindowManagerClient * wm_client = client->wm_client; + MBWindowManager * wm = client->wm; + MBWMCompMgr * mgr = wm->comp_mgr; + XserverRegion parts; + MBGeometry geom; + + parts = XFixesCreateRegion (wm->xdpy, 0, 0); + + /* translate region */ + XDamageSubtract (wm->xdpy, MB_WM_COMP_MGR_DEFAULT_CLIENT (client)->damage, + None, parts); + + mb_wm_client_get_coverage (wm_client, &geom); + + XFixesTranslateRegion (wm->xdpy, parts, geom.x, geom.y); + mb_wm_comp_mgr_xrender_add_damage (mgr, parts); +} + +static void +mb_wm_comp_mgr_xrender_client_configure_real (MBWMCompMgrClient * client) +{ + MBWMCompMgrDefaultClient * dclient = MB_WM_COMP_MGR_DEFAULT_CLIENT (client); + MBWindowManagerClient * wm_client = client->wm_client; + MBWindowManager * wm = client->wm; + MBWMCompMgr * mgr = wm->comp_mgr; + XserverRegion damage = None; + XserverRegion extents; + + extents = mb_wm_comp_mgr_xrender_client_extents (client); + + if (dclient->picture) + { + XRenderFreePicture (wm->xdpy, dclient->picture); + dclient->picture = None; + } + + damage = XFixesCreateRegion (wm->xdpy, 0, 0); + + if (dclient->extents) + { + XFixesCopyRegion (wm->xdpy, damage, dclient->extents); + XFixesDestroyRegion (wm->xdpy, dclient->extents); + } + + XFixesUnionRegion (wm->xdpy, damage, damage, extents); + + dclient->extents = extents; + + mb_wm_comp_mgr_xrender_add_damage (mgr, damage); +} + +static Bool +mb_wm_comp_mgr_xrender_handle_damage (XDamageNotifyEvent * de, + MBWMCompMgr * mgr) +{ + MBWMCompMgrDefaultPrivate * priv = MB_WM_COMP_MGR_DEFAULT (mgr)->priv; + MBWindowManager * wm = mgr->wm; + MBWindowManagerClient * c; + + c = mb_wm_managed_client_from_frame (wm, de->drawable); + + if (c && c->cm_client) + { + MBWM_NOTE (COMPOSITOR, + "Reparing window %x, a %d,%d;%dx%d, g %d,%d;%dx%d\n", + de->drawable, + de->area.x, + de->area.y, + de->area.width, + de->area.height, + de->geometry.x, + de->geometry.y, + de->geometry.width, + de->geometry.height); + + mb_wm_comp_mgr_xrender_client_repair_real (c->cm_client); + } + else + { + MBWM_NOTE (COMPOSITOR, "Failed to find client for window %x\n", + de->drawable); + } + + return False; +} + +static void +_render_a_client (MBWMCompMgrClient * client, + XserverRegion region, + int lowlight_type) /*0 none, 1 app, 2 full*/ +{ + MBWMCompMgrDefaultClient * dclient = MB_WM_COMP_MGR_DEFAULT_CLIENT (client); + MBWindowManagerClient * wm_client = client->wm_client; + MBWindowManager * wm = client->wm; + MBWMCompMgr * mgr = wm->comp_mgr; + MBWMCompMgrDefaultPrivate * priv = MB_WM_COMP_MGR_DEFAULT (mgr)->priv; + MBWMClientType ctype = MB_WM_CLIENT_CLIENT_TYPE (wm_client); + MBGeometry geom; + + if (!dclient->picture) + return; + + mb_wm_client_get_coverage (wm_client, &geom); + + /* Translucency only done for dialogs and overides */ + if ( !client->is_argb32 && + (ctype == MBWMClientTypeApp || + ctype == MBWMClientTypeDesktop || + ctype == MBWMClientTypeInput || + ctype == MBWMClientTypePanel || + mb_wm_comp_mgr_xrender_client_get_translucency (client) == -1)) + { + XserverRegion winborder; + + winborder = mb_wm_comp_mgr_xrender_client_border_size (client, + geom.x, geom.y); + + XFixesSetPictureClipRegion (wm->xdpy, priv->root_buffer, 0, 0, region); + + XFixesSubtractRegion (wm->xdpy, region, region, winborder); + + XRenderComposite (wm->xdpy, PictOpSrc, + dclient->picture, + None, priv->root_buffer, + 0, 0, 0, 0, + geom.x, geom.y, geom.width, geom.height); + + XFixesDestroyRegion (wm->xdpy, winborder); + } + else if (client->is_argb32 || + mb_wm_comp_mgr_xrender_client_get_translucency (client) != -1) + { + /* + * If the client is translucent, paint the decors only (solid). + */ + MBWMList * l = wm_client->decor; + + while (l) + { + MBWMDecor * d = l->data; + MBGeometry * g = & d->geom; + XserverRegion r; + + r = mb_wm_comp_mgr_xrender_client_window_region (client, d->xwin, + g->x, g->y); + + XFixesSetPictureClipRegion (wm->xdpy, priv->root_buffer, 0, 0, r); + XFixesSubtractRegion (wm->xdpy, region, region, r); + + XRenderComposite (wm->xdpy, PictOpSrc, + dclient->picture, + None, priv->root_buffer, + 0, 0, 0, 0, + geom.x + g->x, geom.y + g->y, + g->width, g->height); + + XFixesDestroyRegion (wm->xdpy, r); + + l = l->next; + } + } + + + /* Render lowlight dialog modal for app */ + if (lowlight_type == 1 && + (ctype & (MBWMClientTypeApp | MBWMClientTypeDesktop))) + { + int title_offset = 0; + + if (ctype == MBWMClientTypeApp) + title_offset = mb_wm_client_title_height (wm_client); + + XRenderComposite (wm->xdpy, PictOpOver, priv->lowlight_picture, None, + priv->root_buffer, + 0, 0, 0, 0, geom.x, geom.y + title_offset, + geom.width, geom.height - title_offset); + } + else if (lowlight_type == 2 /* && client->win_modal_blocker == None */) + { + /* Render lowlight dialog modal for root - e.g lowlight everything */ + XRenderComposite (wm->xdpy, PictOpOver, priv->lowlight_picture, None, + priv->root_buffer, + 0, 0, 0, 0, geom.x, geom.y, + geom.width, geom.height); + } + + if (dclient->border_clip) + { + XFixesDestroyRegion (wm->xdpy, dclient->border_clip); + dclient->border_clip = None; + } + + dclient->border_clip = XFixesCreateRegion (wm->xdpy, 0, 0); + XFixesCopyRegion (wm->xdpy, dclient->border_clip, region); +} + +static void +mb_wm_comp_mgr_xrender_render_real (MBWMCompMgr *mgr) +{ + MBWMCompMgrDefaultPrivate * priv = MB_WM_COMP_MGR_DEFAULT (mgr)->priv; + + mb_wm_comp_mgr_xrender_render_region (mgr, priv->all_damage); + + if (priv->all_damage) + { + XDamageDestroy (mgr->wm->xdpy, priv->all_damage); + priv->all_damage = None; + } +} + +static void +mb_wm_comp_mgr_xrender_render_region (MBWMCompMgr *mgr, XserverRegion region) +{ + MBWindowManager * wm = mgr->wm; + MBWMCompMgrDefaultPrivate * priv = MB_WM_COMP_MGR_DEFAULT (mgr)->priv; + MBWindowManagerClient * wmc_top, * wmc_temp, * wmc_solid = NULL; + int lowlight = 0; + int destroy_region = 0; + Bool done; + Bool top_translucent = False; + + if (mgr->disabled) + return; + + if (!region) + { + /* + * Fullscreen render + */ + XRectangle r; + + r.x = 0; + r.y = 0; + r.width = wm->xdpy_width; + r.height = wm->xdpy_height; + + region = XFixesCreateRegion (wm->xdpy, &r, 1); + destroy_region = 1; + } + + wmc_top = mb_wm_get_visible_main_client (wm); + + if (wmc_top) + top_translucent = + (mb_wm_comp_mgr_xrender_client_get_translucency(wmc_top->cm_client) == -1); + + if (!priv->root_buffer) + { + Pixmap rootPixmap = + XCreatePixmap (wm->xdpy, wm->root_win->xwindow, + wm->xdpy_width, wm->xdpy_height, + DefaultDepth (wm->xdpy, wm->xscreen)); + + priv->root_buffer = + XRenderCreatePicture (wm->xdpy, rootPixmap, + XRenderFindVisualFormat (wm->xdpy, + DefaultVisual (wm->xdpy, + wm->xscreen)), + 0, 0); + + XFreePixmap (wm->xdpy, rootPixmap); + } + + XFixesSetPictureClipRegion (wm->xdpy, priv->root_picture, 0, 0, region); + XFixesSetPictureClipRegion (wm->xdpy, priv->root_buffer, 0, 0, region); + + XRenderComposite (wm->xdpy, PictOpSrc, priv->black_picture, + None, priv->root_buffer, 0, 0, 0, 0, 0, 0, + wm->xdpy_width, wm->xdpy_height); + + /* + * Check initially to see what kind of lowlight todo ( if any ) + */ + mb_wm_stack_enumerate_reverse (wm, wmc_temp) + { + MBWMClientType type = MB_WM_CLIENT_CLIENT_TYPE (wmc_temp); + Bool is_modal = mb_wm_client_is_modal (wmc_temp); + + if (type == MBWMClientTypeDialog && is_modal) + { + MBWMModality modality = mb_wm_get_modality_type (wm); + switch (modality) + { + case MBWMModalityNormal: + default: + lowlight = 1; + break; + case MBWMModalitySystem: + lowlight = 2; + break; + case MBWMModalityNone: + lowlight = 0; + break; + } + } + + if (wmc_temp == wmc_top) + break; + } + + /* Render top -> bottom */ + done = False; + mb_wm_stack_enumerate_reverse (wm, wmc_temp) + { + _render_a_client(wmc_temp->cm_client, region, lowlight); + + /* + * Render clients until we reach first client on/below the top + * which is not translucent and is either and application or desktop + * (to have adequate coverage). + */ + if (wmc_temp == wmc_top) + { + done = True; + } + + if (done && + (MB_WM_CLIENT_CLIENT_TYPE (wmc_temp) & + (MBWMClientTypeApp | MBWMClientTypeDesktop)) && + !wmc_temp->cm_client->is_argb32 && + mb_wm_comp_mgr_xrender_client_get_translucency (wmc_temp->cm_client) + == -1) + { + wmc_solid = wmc_temp; + break; + } + } + + if (!wmc_top) + { + /* Render block of boring black in case of no top app or desktop */ + XFixesSetPictureClipRegion (wm->xdpy, priv->root_buffer, 0, 0, region); + + XRenderComposite (wm->xdpy, PictOpSrc, priv->black_picture, + None, priv->root_buffer, 0, 0, 0, 0, 0, 0, + wm->xdpy_width, wm->xdpy_height); + + XFixesSetPictureClipRegion (wm->xdpy, priv->root_buffer, 0, 0, None); + + wmc_top = wm->stack_bottom; + } + + + XFixesSetPictureClipRegion (wm->xdpy, priv->root_buffer, 0, 0, None); + + /* + * Now render shadows and any translucent clients but bottom -> top this + * time + * + * We start from the first solid client on the stack, so that any + * translucent windows on the top of the stack get correctly rendered. + */ + for (wmc_temp = wmc_solid ? wmc_solid : wmc_top; + wmc_temp; wmc_temp=wmc_temp->stacked_above) + { + MBWMClientType type = MB_WM_CLIENT_CLIENT_TYPE (wmc_temp); + MBWMCompMgrClient * c = wmc_temp->cm_client; + MBWMCompMgrDefaultClient * dc = MB_WM_COMP_MGR_DEFAULT_CLIENT (c); + Bool is_translucent; + + if (!dc || !dc->picture) + continue; + + /* + * We have to process all dialogs and, if the top client is translucent, + * any translucent windows as well. + */ + is_translucent = (c->is_argb32 || + mb_wm_comp_mgr_xrender_client_get_translucency (c) + != -1); + + if (mb_wm_client_is_mapped (wmc_temp) && + (type == MBWMClientTypeDialog || + type == MBWMClientTypeMenu || + type == MBWMClientTypeOverride || + (top_translucent && is_translucent))) + { + if (priv->shadow_style) + { + Picture shadow_pic; + MBGeometry geom; + + mb_wm_client_get_coverage (wmc_temp, &geom); + + if (priv->shadow_style == MBWM_COMP_MGR_SHADOW_SIMPLE) + { + XserverRegion shadow_region; + + /* Grab 'shape' region of window */ + shadow_region = + mb_wm_comp_mgr_xrender_client_border_size (c, + geom.x, geom.y); + + /* Offset it. */ + XFixesTranslateRegion (wm->xdpy, shadow_region, + priv->shadow_dx, + priv->shadow_dy); + + /* Intersect it, so only border remains */ + XFixesIntersectRegion (wm->xdpy, shadow_region, + dc->border_clip, + shadow_region ); + + XFixesSetPictureClipRegion (wm->xdpy, priv->root_buffer, + 0, 0, shadow_region); + + /* now paint them */ + if (wmc_temp->cm_client->is_argb32) + { + XRenderComposite (wm->xdpy, PictOpOver, + priv->black_picture, + dc->picture, + priv->root_buffer, + 0, 0, 0, 0, + geom.x + priv->shadow_dx, + geom.y + priv->shadow_dy, + geom.width + + priv->shadow_padding_width, + geom.height + + priv->shadow_padding_height); + } + else + { + XRenderComposite (wm->xdpy, PictOpOver, + priv->black_picture, + None, + priv->root_buffer, + 0, 0, 0, 0, + geom.x + priv->shadow_dx, + geom.y + priv->shadow_dy, + geom.width + + priv->shadow_padding_width, + geom.height + + priv->shadow_padding_height); + } + + /* Paint any translucent window contents, but no the + * decors. + */ + if (is_translucent) + { + MBGeometry * win_geom = & wmc_temp->window->geometry; + + XFixesDestroyRegion (wm->xdpy, shadow_region); + + shadow_region = + mb_wm_comp_mgr_xrender_client_border_size (c, + geom.x, geom.y); + + XFixesIntersectRegion (wm->xdpy, shadow_region, + dc->border_clip, shadow_region ); + + XFixesSetPictureClipRegion (wm->xdpy, priv->root_buffer, + 0, 0, shadow_region); + + if (c->is_argb32) + XRenderComposite (wm->xdpy, PictOpOver, + dc->picture, None, + priv->root_buffer, + win_geom->x, win_geom->y, 0, 0, + win_geom->x + geom.x, + win_geom->y + geom.y, + win_geom->width, win_geom->height); + else + XRenderComposite (wm->xdpy, PictOpOver, + dc->picture, priv->trans_picture, + priv->root_buffer, + win_geom->x, win_geom->y, 0, 0, + win_geom->x + geom.x, + win_geom->y + geom.y, + win_geom->width, win_geom->height); + } + + XFixesDestroyRegion (wm->xdpy, shadow_region); + } + else /* GAUSSIAN */ + { + MBGeometry * win_geom = & wmc_temp->window->geometry; + + XFixesSetPictureClipRegion (wm->xdpy, priv->root_buffer, + 0, 0, dc->border_clip); + + if (is_translucent) + { + /* No shadows currently for transparent windows */ + XRenderComposite (wm->xdpy, PictOpOver, + dc->picture, priv->trans_picture, + priv->root_buffer, + win_geom->x, win_geom->y, 0, 0, + win_geom->x + geom.x, + win_geom->y + geom.y, + win_geom->width, win_geom->height); + } + else + { + /* Combine pregenerated shadow tiles */ + shadow_pic = + mb_wm_comp_mgr_xrender_shadow_gaussian_make_picture (mgr, + geom.width + priv->shadow_padding_width, + geom.height + priv->shadow_padding_height); + + XRenderComposite (wm->xdpy, PictOpOver, + priv->black_picture, + shadow_pic, + priv->root_buffer, + win_geom->x, win_geom->y, 0, 0, + geom.x + priv->shadow_dx, + geom.y + priv->shadow_dy, + geom.width + + priv->shadow_padding_width, + geom.height + + priv->shadow_padding_height); + + XRenderFreePicture (wm->xdpy, shadow_pic); + } + } + } + } + } + + XFixesSetPictureClipRegion (wm->xdpy, priv->root_buffer, 0, 0, None); + + XRenderComposite (wm->xdpy, PictOpSrc, priv->root_buffer, None, + priv->root_picture, + 0, 0, 0, 0, 0, 0, wm->xdpy_width, wm->xdpy_height); + + if (destroy_region) + XDamageDestroy (wm->xdpy, region); +} + +MBWMCompMgr * +mb_wm_comp_mgr_xrender_new (MBWindowManager *wm) +{ + MBWMObject *mgr; + + mgr = mb_wm_object_new (MB_WM_TYPE_COMP_MGR_DEFAULT, MBWMObjectPropWm, wm, NULL); + + return MB_WM_COMP_MGR (mgr); +} + diff --git a/matchbox/mb-wm-comp-mgr-xrender.h b/matchbox/mb-wm-comp-mgr-xrender.h new file mode 100644 index 0000000..15e52bd --- /dev/null +++ b/matchbox/mb-wm-comp-mgr-xrender.h @@ -0,0 +1,68 @@ +/* + * Matchbox Window Manager - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2002, 2004, 2007 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. + * + */ + +#ifndef _HAVE_MB_WM_COMP_MGR_DEFAULT_H +#define _HAVE_MB_WM_COMP_MGR_DEFAULT_H + +#include <matchbox/mb-wm-config.h> + +#define MB_WM_COMP_MGR_DEFAULT(c) ((MBWMCompMgrDefault*)(c)) +#define MB_WM_COMP_MGR_DEFAULT_CLASS(c) ((MBWMCompMgrDefaultClass*)(c)) +#define MB_WM_TYPE_COMP_MGR_DEFAULT (mb_wm_comp_mgr_xrender_class_type ()) + +typedef struct MBWMCompMgrDefault MBWMCompMgrDefault; +typedef struct MBWMCompMgrDefaultPrivate MBWMCompMgrDefaultPrivate; +typedef struct MBWMCompMgrDefaultClass MBWMCompMgrDefaultClass; + +#define MB_WM_COMP_MGR_DEFAULT_CLIENT(c) ((MBWMCompMgrDefaultClient*)(c)) +#define MB_WM_COMP_MGR_DEFAULT_CLIENT_CLASS(c) ((MBWMCompMgrDefaultClientClass*)(c)) +#define MB_WM_TYPE_COMP_MGR_DEFAULT_CLIENT (mb_wm_comp_mgr_xrender_client_class_type ()) + +typedef struct MBWMCompMgrDefaultClient MBWMCompMgrDefaultClient; +typedef struct MBWMCompMgrDefaultClientClass MBWMCompMgrDefaultClientClass; +typedef struct MBWMCompMgrDefaultClentPrivate MBWMCompMgrDefaultClientPrivate; + +struct MBWMCompMgrDefault +{ + MBWMCompMgr parent; + MBWMCompMgrDefaultPrivate *priv; +}; + +struct MBWMCompMgrDefaultClass +{ + MBWMCompMgrClass parent; +}; + +int +mb_wm_comp_mgr_xrender_class_type (); + +MBWMCompMgr* +mb_wm_comp_mgr_xrender_new (MBWindowManager *wm); + +struct MBWMCompMgrDefaultClientClass +{ + MBWMCompMgrClientClass parent; +}; + +int +mb_wm_comp_mgr_xrender_client_class_type (); + +#endif diff --git a/matchbox/mb-wm-comp-mgr.c b/matchbox/mb-wm-comp-mgr.c new file mode 100644 index 0000000..5aa6d6b --- /dev/null +++ b/matchbox/mb-wm-comp-mgr.c @@ -0,0 +1,482 @@ +/* + * Matchbox Window Manager - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2002, 2004, 2007 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 "matchbox.h" +#include "mb-wm-client.h" +#include "mb-wm-comp-mgr.h" +#include "mb-wm-theme.h" + +#include <X11/Xresource.h> +#include <X11/extensions/Xdamage.h> +#include <X11/extensions/Xrender.h> +#include <X11/extensions/Xcomposite.h> + +static void +mb_wm_comp_mgr_client_class_init (MBWMObjectClass *klass) +{ + MBWMCompMgrClientClass *c_klass = MB_WM_COMP_MGR_CLIENT_CLASS (klass); + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMCompMgrClient"; +#endif +} + +static int +mb_wm_comp_mgr_client_init (MBWMObject *obj, va_list vap) +{ + MBWMCompMgrClient *client = MB_WM_COMP_MGR_CLIENT (obj); + MBWindowManagerClient *wm_client = NULL; + MBWMObjectProp prop; + XRenderPictFormat *format; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + switch (prop) + { + case MBWMObjectPropClient: + wm_client = va_arg(vap, MBWindowManagerClient *); + break; + default: + MBWMO_PROP_EAT (vap, prop); + } + + prop = va_arg(vap, MBWMObjectProp); + } + + if (!wm_client) + return 0; + + client->wm_client = wm_client; + client->wm = wm_client->wmref; + + /* Check visual */ + format = XRenderFindVisualFormat (client->wm->xdpy, + wm_client->window->visual); + + if (format && format->type == PictTypeDirect && format->direct.alphaMask) + client->is_argb32 = True; + + return 1; +} + +static void +mb_wm_comp_mgr_client_destroy (MBWMObject* obj) +{ + MBWMCompMgrClient *client = MB_WM_COMP_MGR_CLIENT (obj); + MBWindowManagerClient *wm_client = client->wm_client; +} + +int +mb_wm_comp_mgr_client_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMCompMgrClientClass), + sizeof (MBWMCompMgrClient), + mb_wm_comp_mgr_client_init, + mb_wm_comp_mgr_client_destroy, + mb_wm_comp_mgr_client_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); + } + + return type; +} + +void +mb_wm_comp_mgr_client_hide (MBWMCompMgrClient * client) +{ + MBWMCompMgrClientClass *klass; + + if (!client) + return; + + klass = MB_WM_COMP_MGR_CLIENT_CLASS (MB_WM_OBJECT_GET_CLASS (client)); + + MBWM_ASSERT (klass->hide != NULL); + klass->hide (client); +} + +void +mb_wm_comp_mgr_client_show (MBWMCompMgrClient * client) +{ + MBWMCompMgrClientClass *klass; + + if (!client) + return; + + klass = MB_WM_COMP_MGR_CLIENT_CLASS (MB_WM_OBJECT_GET_CLASS (client)); + + MBWM_ASSERT (klass->show != NULL); + klass->show (client); +} + +void +mb_wm_comp_mgr_client_configure (MBWMCompMgrClient * client) +{ + MBWMCompMgrClientClass *klass; + + if (!client) + return; + + klass = MB_WM_COMP_MGR_CLIENT_CLASS (MB_WM_OBJECT_GET_CLASS (client)); + + MBWM_ASSERT (klass->configure != NULL); + klass->configure (client); +} + +void +mb_wm_comp_mgr_client_repair (MBWMCompMgrClient * client) +{ + MBWMCompMgrClientClass *klass; + + if (!client) + return; + + klass = MB_WM_COMP_MGR_CLIENT_CLASS (MB_WM_OBJECT_GET_CLASS (client)); + + MBWM_ASSERT (klass->repair != NULL); + klass->repair (client); +} + + +/* + * MBWMCompMgr object + * + * Base class for the composite manager, providing the common interface + * through which the manager is access by the MBWindowManager object. + */ +static void +mb_wm_comp_mgr_class_init (MBWMObjectClass *klass) +{ +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMCompMgr"; +#endif +} + +static int +mb_wm_comp_mgr_init (MBWMObject *obj, va_list vap) +{ + MBWMCompMgr * mgr = MB_WM_COMP_MGR (obj); + MBWindowManager * wm = NULL; + MBWMObjectProp prop; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + switch (prop) + { + case MBWMObjectPropWm: + wm = va_arg(vap, MBWindowManager *); + break; + default: + MBWMO_PROP_EAT (vap, prop); + } + + prop = va_arg(vap, MBWMObjectProp); + } + + if (!wm) + return 0; + + mgr->wm = wm; + mgr->disabled = True; + + return 1; +} + +static void +mb_wm_comp_mgr_destroy (MBWMObject* obj) +{ + MBWMCompMgr * mgr = MB_WM_COMP_MGR (obj); + + mb_wm_comp_mgr_turn_off (mgr); +} + +int +mb_wm_comp_mgr_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMCompMgrClass), + sizeof (MBWMCompMgr), + mb_wm_comp_mgr_init, + mb_wm_comp_mgr_destroy, + mb_wm_comp_mgr_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); + } + + return type; +} + + +Bool +mb_wm_comp_mgr_enabled (MBWMCompMgr *mgr) +{ + if (!mgr || mgr->disabled) + return False; + + return True; +} + +/* + * Registers new client with the manager (i.e., when a window is created). + */ +void +mb_wm_comp_mgr_register_client (MBWMCompMgr * mgr, + MBWindowManagerClient * client) +{ + MBWMCompMgrClass *klass; + + if (!client) + return; + + klass = MB_WM_COMP_MGR_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + MBWM_ASSERT (klass->register_client != NULL); + klass->register_client (mgr, client); +} + +/* + * Unregisters existing client (e.g., when window unmaps or is destroyed + */ +void +mb_wm_comp_mgr_unregister_client (MBWMCompMgr * mgr, + MBWindowManagerClient * client) +{ + MBWMCompMgrClass *klass; + + if (!client) + return; + + klass = MB_WM_COMP_MGR_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + if (!client->cm_client) + return; + + if (klass->unregister_client) + klass->unregister_client (mgr, client); + + mb_wm_object_unref (MB_WM_OBJECT (client->cm_client)); + client->cm_client = NULL; +} + +/* + * Called to render the entire composited scene on screen. + */ +void +mb_wm_comp_mgr_render (MBWMCompMgr *mgr) +{ + MBWMCompMgrClass *klass; + + if (!mgr) + return; + + klass = MB_WM_COMP_MGR_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + if (klass->render) + klass->render (mgr); +} + +void +mb_wm_comp_mgr_restack (MBWMCompMgr *mgr) +{ + MBWMCompMgrClass *klass; + + if (!mgr) + return; + + klass = MB_WM_COMP_MGR_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + if (klass->restack) + klass->restack (mgr); +} + +/* + * Called when a window we are interested in maps. + */ +void +mb_wm_comp_mgr_map_notify (MBWMCompMgr *mgr, MBWindowManagerClient *c) +{ + MBWMCompMgrClass *klass; + + if (!mgr || !c || !c->cm_client) + return; + + klass = MB_WM_COMP_MGR_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + if (klass->map_notify) + klass->map_notify (mgr, c); + + /* + * Run map event effect *before* we call show() on the actor + * (the effect will take care of showing the actor, and if not, show() gets + * called by mb_wm_comp_mgr_map_notify()). + */ + if (!mb_wm_client_is_hiding_from_desktop (c)) + mb_wm_comp_mgr_do_effect (mgr, c, MBWMCompMgrClientEventMap); + + if (c->cm_client) + mb_wm_comp_mgr_client_show (c->cm_client); +} + +void +mb_wm_comp_mgr_unmap_notify (MBWMCompMgr *mgr, MBWindowManagerClient *c) +{ + MBWMCompMgrClass *klass; + + if (!mgr || !c || !c->cm_client) + return; + + klass = MB_WM_COMP_MGR_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + if (!mb_wm_client_is_hiding_from_desktop (c)) + mb_wm_comp_mgr_do_effect (mgr, c, MBWMCompMgrClientEventUnmap); + + if (klass->unmap_notify) + klass->unmap_notify (mgr, c); + + /* + * NB: cannot call hide() here, as at this point an effect could be running + * -- the subclass needs to take care of this from the effect and/or + * unmap_notify() function. + */ +} + +/* + * Runs a transition-effect from client c1 to client c2; the reverse argument + * indicates notional direction of the transition + */ +void +mb_wm_comp_mgr_do_transition (MBWMCompMgr * mgr, + MBWindowManagerClient *c1, + MBWindowManagerClient *c2, + Bool reverse) +{ + MBWMCompMgrClass *klass; + + /* + * Transitions can only be done for clients of the same type, so + * check the types here. + */ + if (!mgr || !c1 || !c2 || + MB_WM_CLIENT_CLIENT_TYPE (c1) != MB_WM_CLIENT_CLIENT_TYPE (c1)) + return; + + klass = MB_WM_COMP_MGR_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + if (klass->client_transition) + klass->client_transition (mgr, c1, c2, reverse); +} + +void +mb_wm_comp_mgr_do_effect (MBWMCompMgr * mgr, + MBWindowManagerClient * client, + MBWMCompMgrClientEvent event) +{ + MBWMCompMgrClass *klass; + + /* + * Transitions can only be done for clients of the same type, so + * check the types here. + */ + if (!mgr || !client) + return; + + klass = MB_WM_COMP_MGR_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + if (klass->client_event) + klass->client_event (mgr, client, event); +} + +void +mb_wm_comp_mgr_select_desktop (MBWMCompMgr * mgr, + int desktop, + int old_desktop) +{ + MBWMCompMgrClass *klass + = MB_WM_COMP_MGR_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + if (klass->select_desktop) + klass->select_desktop (mgr, desktop, old_desktop); +} + +void +mb_wm_comp_mgr_turn_on (MBWMCompMgr *mgr) +{ + MBWindowManager *wm = mgr->wm; + MBWMCompMgrClass *klass + = MB_WM_COMP_MGR_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + MBWM_ASSERT (klass->turn_on != NULL); + klass->turn_on (mgr); + + mgr->damage_cb_id = + mb_wm_main_context_x_event_handler_add (wm->main_ctx, + None, + wm->damage_event_base + XDamageNotify, + (MBWMXEventFunc)klass->handle_damage, + mgr); +} + +void +mb_wm_comp_mgr_turn_off (MBWMCompMgr *mgr) +{ + MBWindowManager *wm = mgr->wm; + MBWMCompMgrClass *klass + = MB_WM_COMP_MGR_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + MBWM_ASSERT (klass->turn_off != NULL); + klass->turn_off (mgr); + + mb_wm_main_context_x_event_handler_remove (wm->main_ctx, + wm->damage_event_base + XDamageNotify, + mgr->damage_cb_id); + + mgr->damage_cb_id = 0; +} + +/* + * Returns true if the window identified by id xwin is a special window + * that belongs to the compositing manager and should, therefore, be left + * alone by MBWMWindow manager (e.g., the overalay window). + */ +Bool +mb_wm_comp_mgr_is_my_window (MBWMCompMgr * mgr, Window xwin) +{ + MBWMCompMgrClass *klass + = MB_WM_COMP_MGR_CLASS (MB_WM_OBJECT_GET_CLASS (mgr)); + + if (klass->my_window) + return klass->my_window (mgr, xwin); + + return False; +} + diff --git a/matchbox/mb-wm-comp-mgr.h b/matchbox/mb-wm-comp-mgr.h new file mode 100644 index 0000000..9e7224c --- /dev/null +++ b/matchbox/mb-wm-comp-mgr.h @@ -0,0 +1,179 @@ +/* + * Matchbox Window Manager - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2002, 2004, 2007 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. + * + */ + +#ifndef _HAVE_MB_WM_COMP_MGR_H +#define _HAVE_MB_WM_COMP_MGR_H + +#include <matchbox/mb-wm-client.h> + +#include <X11/extensions/Xdamage.h> + +/* XXX: We have a circular dependency between mb-wm-comp-mgr.h + * and mb-wm-client.h */ +#ifndef MB_WM_CLIENT_TYPEDEF_DEFINED +typedef struct MBWindowManagerClient MBWindowManagerClient; +#define MB_WM_CLIENT_TYPEDEF_DEFINED +#endif + +#define MB_WM_COMP_MGR(c) ((MBWMCompMgr*)(c)) +#define MB_WM_COMP_MGR_CLASS(c) ((MBWMCompMgrClass*)(c)) +#define MB_WM_TYPE_COMP_MGR (mb_wm_comp_mgr_class_type ()) + +/* XXX: We have a circular dependency between mb-wm-comp-mgr.h + * and mb-wm-client.h */ +#ifndef MB_WM_COMP_MGR_TYPEDEF_DEFINED +typedef struct MBWMCompMgr MBWMCompMgr; +#define MB_WM_COMP_MGR_TYPEDEF_DEFINED +#endif +typedef struct MBWMCompMgrClass MBWMCompMgrClass; + +#define MB_WM_COMP_MGR_CLIENT(c) ((MBWMCompMgrClient*)(c)) +#define MB_WM_COMP_MGR_CLIENT_CLASS(c) ((MBWMCompMgrClientClass*)(c)) +#define MB_WM_TYPE_COMP_MGR_CLIENT (mb_wm_comp_mgr_client_class_type ()) + +typedef struct MBWMCompMgrClient MBWMCompMgrClient; +typedef struct MBWMCompMgrClientClass MBWMCompMgrClientClass; + +struct MBWMCompMgr +{ + MBWMObject parent; + + MBWindowManager *wm; + Bool disabled; + unsigned long damage_cb_id; +}; + +struct MBWMCompMgrClass +{ + MBWMObjectClass parent; + + void (*register_client) (MBWMCompMgr * mgr, MBWindowManagerClient *c); + void (*unregister_client) (MBWMCompMgr * mgr, MBWindowManagerClient *c); + void (*turn_on) (MBWMCompMgr * mgr); + void (*turn_off) (MBWMCompMgr * mgr); + void (*render) (MBWMCompMgr * mgr); + void (*restack) (MBWMCompMgr * mgr); + void (*map_notify) (MBWMCompMgr * mgr, MBWindowManagerClient *c); + void (*unmap_notify) (MBWMCompMgr * mgr, MBWindowManagerClient *c); + Bool (*handle_damage) (XDamageNotifyEvent * xev, MBWMCompMgr * mgr); + Bool (*my_window) (MBWMCompMgr * mgr, Window xwin); + void (*client_event) (MBWMCompMgr * mgr, + MBWindowManagerClient *c1, + MBWMCompMgrClientEvent event); + void (*client_transition) (MBWMCompMgr * mgr, + MBWindowManagerClient *c1, + MBWindowManagerClient *c2, + Bool reverse); + + void (*select_desktop) (MBWMCompMgr * mgr, + int desktop, int old_desktop); +}; + +int +mb_wm_comp_mgr_class_type (); + +void +mb_wm_comp_mgr_register_client (MBWMCompMgr * mgr, MBWindowManagerClient *c); + +void +mb_wm_comp_mgr_unregister_client (MBWMCompMgr * mgr, + MBWindowManagerClient *client); + +void +mb_wm_comp_mgr_turn_off (MBWMCompMgr *mgr); + +void +mb_wm_comp_mgr_turn_on (MBWMCompMgr *mgr); + +void +mb_wm_comp_mgr_render (MBWMCompMgr *mgr); + +void +mb_wm_comp_mgr_restack (MBWMCompMgr *mgr); + +void +mb_wm_comp_mgr_map_notify (MBWMCompMgr *mgr, MBWindowManagerClient *c); + +void +mb_wm_comp_mgr_unmap_notify (MBWMCompMgr *mgr, MBWindowManagerClient *c); + +Bool +mb_wm_comp_mgr_enabled (MBWMCompMgr *mgr); + +Bool +mb_wm_comp_mgr_handle_events (MBWMCompMgr * mgr, XEvent *ev); + +Bool +mb_wm_comp_mgr_is_my_window (MBWMCompMgr * mgr, Window xwin); + +void +mb_wm_comp_mgr_do_transition (MBWMCompMgr * mgr, + MBWindowManagerClient * c1, + MBWindowManagerClient * c2, + Bool reverse); + +void +mb_wm_comp_mgr_do_effect (MBWMCompMgr * mgr, + MBWindowManagerClient * client, + MBWMCompMgrClientEvent event); + +void +mb_wm_comp_mgr_select_desktop (MBWMCompMgr * mgr, + int desktop, + int old_desktop); + +struct MBWMCompMgrClient +{ + MBWMObject parent; + + MBWindowManager * wm; + MBWindowManagerClient * wm_client; + + /* Make private ? */ + Bool is_argb32; +}; + +struct MBWMCompMgrClientClass +{ + MBWMObjectClass parent; + + void (*show) (MBWMCompMgrClient * client); + void (*hide) (MBWMCompMgrClient * client); + void (*repair) (MBWMCompMgrClient * client); + void (*configure) (MBWMCompMgrClient * client); +}; + +int +mb_wm_comp_mgr_client_class_type (); + +void +mb_wm_comp_mgr_client_show (MBWMCompMgrClient * client); + +void +mb_wm_comp_mgr_client_hide (MBWMCompMgrClient * client); + +void +mb_wm_comp_mgr_client_repair (MBWMCompMgrClient * client); + +void +mb_wm_comp_mgr_client_configure (MBWMCompMgrClient * client); + + +#endif diff --git a/matchbox/mb-wm-config.h.in b/matchbox/mb-wm-config.h.in new file mode 100644 index 0000000..8c8de50 --- /dev/null +++ b/matchbox/mb-wm-config.h.in @@ -0,0 +1,35 @@ + +#ifndef _MB_MW_CONFIG_H +#define _MB_WM_CONFIG_H + +/* Enable composite manager code */ +#define ENABLE_COMPOSITE @ENABLE_COMPOSITE@ + +/* Build PNG-image based theme engine */ +#define THEME_PNG @THEME_PNG@ + +/* Use pango for text layout */ +#define USE_PANGO @USE_PANGO@ + +/* Use pango for text layout */ +#define USE_PANGO @USE_PANGO@ + +/* Use clutter for compositing */ +#define ENABLE_CLUTTER_COMPOSITE_MANAGER @ENABLE_CLUTTER_COMPOSITE_MANAGER@ + +/* Use xrender compositing manager backend */ +#define ENABLE_XRENDER_COMPOSITE_MANAGER @ENABLE_XRENDER_COMPOSITE_MANAGER@ + +/* Include at least one of the default compositing manager backends */ +#define COMP_MGR_BACKEND @COMP_MGR_BACKEND@ + +/* Use glib main loop */ +#define USE_GLIB_MAINLOOP @USE_GLIB_MAINLOOP@ + +/* GTK Integration */ +#define USE_GTK @USE_GTK@ + +/* Debugging stuff */ +#define MBWM_WANT_DEBUG @MBWM_WANT_DEBUG@ + +#endif diff --git a/matchbox/mb-wm-debug.c b/matchbox/mb-wm-debug.c new file mode 100644 index 0000000..834ed30 --- /dev/null +++ b/matchbox/mb-wm-debug.c @@ -0,0 +1,78 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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-config.h" +#include "mb-wm-debug.h" + +#include <stdlib.h> + +#if MBWM_WANT_DEBUG +int mbwm_debug_flags = 0; + +static const struct { const char *key; MBWMDebugFlag flag; } debug_keys[] = { + { "misc", MBWM_DEBUG_MISC }, + { "client", MBWM_DEBUG_CLIENT }, + { "texture", MBWM_DEBUG_PROP }, + { "event", MBWM_DEBUG_EVENT }, + { "paint", MBWM_DEBUG_PAINT }, + { "trace", MBWM_DEBUG_TRACE }, + { "obj-ref", MBWM_DEBUG_OBJ_REF }, + { "obj-unref", MBWM_DEBUG_OBJ_UNREF }, + { "xas", MBWM_DEBUG_XAS }, + { "compositor",MBWM_DEBUG_COMPOSITOR }, + { "damage", MBWM_DEBUG_DAMAGE }, +}; +#endif + +void +mb_wm_debug_init (const char *debug_string) +{ +#if MBWM_WANT_DEBUG + char *end; + int n, i; + + if (debug_string == NULL) + return; + + if (streq(debug_string,"all")) + { + mbwm_debug_flags = 0xffff; + return; + } + + end = (char*)(debug_string + strlen(debug_string)); + + while (debug_string < end) + { + n = strcspn(debug_string, ","); + + for (i=0; i<(sizeof(debug_keys)/sizeof(debug_keys[0])); i++) + if (!strncmp(debug_string, debug_keys[i].key, n)) + mbwm_debug_flags |= debug_keys[i].flag; + + debug_string += (n + 1); + } +#else + if (debug_string != NULL) + mb_wm_util_warn ("You have requested debug messages and this matchbox " + "build doesn't have any!"); +#endif +} + diff --git a/matchbox/mb-wm-debug.h b/matchbox/mb-wm-debug.h new file mode 100644 index 0000000..4c10be4 --- /dev/null +++ b/matchbox/mb-wm-debug.h @@ -0,0 +1,48 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_DEBUG_H +#define _HAVE_MB_DEBUG_H + +#if MBWM_WANT_DEBUG + +typedef enum { + MBWM_DEBUG_MISC = 1 << 0, + MBWM_DEBUG_CLIENT = 1 << 1, + MBWM_DEBUG_PROP = 1 << 2, + MBWM_DEBUG_EVENT = 1 << 3, + MBWM_DEBUG_PAINT = 1 << 4, + MBWM_DEBUG_TRACE = 1 << 5, + MBWM_DEBUG_OBJ_REF = 1 << 6, + MBWM_DEBUG_OBJ_UNREF = 1 << 7, + MBWM_DEBUG_OBJ = MBWM_DEBUG_OBJ_REF | MBWM_DEBUG_OBJ_UNREF, + MBWM_DEBUG_XAS = 1 << 8, + MBWM_DEBUG_COMPOSITOR = 1 << 9, + MBWM_DEBUG_DAMAGE = 1 << 10, +} MBWMDebugFlag; + +extern int mbwm_debug_flags; + +#endif /* MBWM_WANT_DEBUG */ + +void +mb_wm_debug_init (const char *debug_string); + +#endif diff --git a/matchbox/mb-wm-decor.c b/matchbox/mb-wm-decor.c new file mode 100644 index 0000000..17a3ece --- /dev/null +++ b/matchbox/mb-wm-decor.c @@ -0,0 +1,1190 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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 "matchbox.h" +#include "mb-wm-theme.h" + +static void +mb_wm_decor_destroy (MBWMObject *obj); + +static void +mb_wm_decor_button_sync_window (MBWMDecorButton *button); + +static void +mb_wm_decor_class_init (MBWMObjectClass *klass) +{ +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMDecor"; +#endif +} + +static int +mb_wm_decor_init (MBWMObject *obj, va_list vap) +{ + MBWMDecor *decor = MB_WM_DECOR (obj); + MBWindowManager *wm = NULL; + MBWMDecorType type = 0; + MBWMObjectProp prop; + int abs_packing = 0; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + switch (prop) + { + case MBWMObjectPropWm: + wm = va_arg(vap, MBWindowManager *); + break; + case MBWMObjectPropDecorType: + type = va_arg(vap, MBWMDecorType); + break; + case MBWMObjectPropDecorAbsolutePacking: + abs_packing = va_arg(vap, int); + default: + MBWMO_PROP_EAT (vap, prop); + } + + prop = va_arg(vap, MBWMObjectProp); + } + + if (!wm) + return 0; + + decor->type = type; + decor->dirty = MBWMDecorDirtyFull; /* Needs painting */ + decor->absolute_packing = abs_packing; + + return 1; +} + +int +mb_wm_decor_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMDecorClass), + sizeof (MBWMDecor), + mb_wm_decor_init, + mb_wm_decor_destroy, + mb_wm_decor_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); + } + + return type; +} + +static void +mb_wm_decor_repaint (MBWMDecor *decor) +{ + MBWMTheme *theme = decor->parent_client->wmref->theme; + + mb_wm_theme_paint_decor (theme, decor); +} + +static void +mb_wm_decor_resize (MBWMDecor *decor) +{ + const MBGeometry *geom; + MBWMTheme *theme = decor->parent_client->wmref->theme; + MBWMList *l; + int btn_x_start, btn_x_end; + int abs_packing = decor->absolute_packing; + + geom = mb_wm_decor_get_geometry (decor); + + btn_x_end = geom->width; + btn_x_start = 0; + + l = decor->buttons; + + /* + * Notify theme of resize + */ + mb_wm_theme_resize_decor (theme, decor); + + if (abs_packing) + { + int width = btn_x_end; + + width /= 2; + + while (l) + { + int off_x, off_y, bw, bh; + + MBWMDecorButton *btn = (MBWMDecorButton *)l->data; + mb_wm_theme_get_button_position (theme, decor, btn->type, + &off_x, &off_y); + mb_wm_theme_get_button_size (theme, decor, btn->type, + &bw, &bh); + + mb_wm_decor_button_move_to (btn, off_x, off_y); + + /* + * We need to simulate packing when placing buttons at absolute + * positions (e.g., in png-based theme) so that we know the size + * of the area into which we can place the document title + */ + if (off_x + bw < width) + { + int x = off_x + bw; + + if (x > btn_x_start) + btn_x_start = x + 2; + } + else + { + if (off_x < btn_x_end) + btn_x_end = off_x - 2; + } + + l = l->next; + } + } + else + { + while (l) + { + int off_x, off_y; + + MBWMDecorButton *btn = (MBWMDecorButton *)l->data; + mb_wm_theme_get_button_position (theme, decor, btn->type, + &off_x, &off_y); + + if (btn->pack == MBWMDecorButtonPackEnd) + { + btn_x_end -= (btn->geom.width + off_x); + mb_wm_decor_button_move_to (btn, btn_x_end, off_y); + } + else + { + mb_wm_decor_button_move_to (btn, btn_x_start + off_x, off_y); + btn_x_start += btn->geom.width; + } + + l = l->next; + } + } + + decor->pack_start_x = btn_x_start; + decor->pack_end_x = btn_x_end; +} + +static Bool +mb_wm_decor_reparent (MBWMDecor *decor); + +static Bool +mb_wm_decor_release_handler (XButtonEvent *xev, + void *userdata) +{ + MBWMDecor *decor = userdata; + MBWindowManager *wm = decor->parent_client->wmref; + + mb_wm_main_context_x_event_handler_remove (wm->main_ctx, ButtonRelease, + decor->release_cb_id); + + decor->release_cb_id = 0; + + XUngrabPointer (wm->xdpy, CurrentTime); + + return False; +} + +static Bool +mb_wm_decor_press_handler (XButtonEvent *xev, + void *userdata) +{ + MBWMDecor *decor = userdata; + MBWindowManager *wm = decor->parent_client->wmref; + Bool retval = True; + + if (xev->window == decor->xwin) + { + XEvent ev; + MBGeometry geom; + int orig_x, orig_y; + int orig_p_x, orig_p_y; + + mb_wm_client_get_coverage (decor->parent_client, &geom); + + orig_x = geom.x; + orig_y = geom.y; + orig_p_x = xev->x_root; + orig_p_y = xev->y_root; + + /* + * This is bit tricky: we do a normal pointer drag and pull out any + * pointer events out of the queue; when we get a MotionEvent, we + * move the client window. However, for the move to propagete on screen + * (particularly with a compositor) we need to spin the main loop so + * that any queued up ConfigureNotify events get processed; + * unfortunately, this invariably results in the ButtonRelease event + * landing in the main loop and not in our sub-loop here. So, on the + * ButtonPress we install a ButtonRelease callback into the main loop + * and use that to release the grab. + */ + if (XGrabPointer(wm->xdpy, xev->subwindow, False, + ButtonPressMask|ButtonReleaseMask| + PointerMotionMask|EnterWindowMask|LeaveWindowMask, + GrabModeAsync, + GrabModeAsync, + None, None, CurrentTime) == GrabSuccess) + { + + decor->release_cb_id = mb_wm_main_context_x_event_handler_add ( + wm->main_ctx, + decor->xwin, + ButtonRelease, + (MBWMXEventFunc)mb_wm_decor_release_handler, + decor); + + for (;;) + { + /* + * If we have no release_cb installed, i.e., the ButtonRelease + * has already happened, quit this loop. + */ + if (!decor->release_cb_id) + break; + + XMaskEvent(wm->xdpy, + ButtonPressMask|ButtonReleaseMask| + PointerMotionMask|EnterWindowMask| + LeaveWindowMask, + &ev); + + switch (ev.type) + { + case MotionNotify: + { + Bool events_pending; + int event_count = 5; /*Limit how much we spin the loop*/ + XMotionEvent *pev = (XMotionEvent*)&ev; + int diff_x = pev->x_root - orig_p_x; + int diff_y = pev->y_root - orig_p_y; + + geom.x = orig_x + diff_x; + geom.y = orig_y + diff_y; + + mb_wm_client_request_geometry (decor->parent_client, + &geom, + MBWMClientReqGeomIsViaUserAction); + + do + { + events_pending = + mb_wm_main_context_spin_loop (wm->main_ctx); + + --event_count; + } while (events_pending && event_count); + } + break; + case ButtonRelease: + { + XUngrabPointer (wm->xdpy, CurrentTime); + return False; + } + default: + ; + } + } + } + } + + return retval; +} + +static Bool +mb_wm_decor_sync_window (MBWMDecor *decor) +{ + MBWindowManager *wm; + XSetWindowAttributes attr; + + if (decor->parent_client == NULL) + return False; + + wm = decor->parent_client->wmref; + + + if (decor->xwin == None) + { + attr.override_redirect = True; + attr.background_pixel = BlackPixel(wm->xdpy, wm->xscreen); + attr.event_mask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask; + + mb_wm_util_trap_x_errors(); + + decor->xwin + = XCreateWindow(wm->xdpy, + wm->root_win->xwindow, + decor->geom.x, + decor->geom.y, + decor->geom.width, + decor->geom.height, + 0, + CopyFromParent, + CopyFromParent, + CopyFromParent, + CWOverrideRedirect|CWBackPixel|CWEventMask, + &attr); + + MBWM_DBG("g is +%i+%i %ix%i", + decor->geom.x, + decor->geom.y, + decor->geom.width, + decor->geom.height); + + if (mb_wm_util_untrap_x_errors()) + return False; + + mb_wm_decor_resize(decor); + + mb_wm_util_list_foreach(decor->buttons, + (MBWMListForEachCB)mb_wm_decor_button_sync_window, + NULL); + + /* + * If this is a decor with buttons, then we install button press handler + * so we can drag the window, if it is movable. + */ + if (decor->type == MBWMDecorTypeNorth && + decor->parent_client->layout_hints & LayoutPrefMovable) + { + decor->press_cb_id = + mb_wm_main_context_x_event_handler_add (wm->main_ctx, + decor->xwin, + ButtonPress, + (MBWMXEventFunc)mb_wm_decor_press_handler, + decor); + } + + return mb_wm_decor_reparent (decor); + } + else + { + /* Resize */ + mb_wm_util_trap_x_errors(); + + XMoveResizeWindow(wm->xdpy, + decor->xwin, + decor->geom.x, + decor->geom.y, + decor->geom.width, + decor->geom.height); + + /* Next up sort buttons */ + + mb_wm_util_list_foreach(decor->buttons, + (MBWMListForEachCB)mb_wm_decor_button_sync_window, + NULL); + + if (mb_wm_util_untrap_x_errors()) + return False; + } + + return True; +} + +static Bool +mb_wm_decor_reparent (MBWMDecor *decor) +{ + MBWindowManager *wm; + + if (decor->parent_client == NULL) + return False; + + MBWM_MARK(); + + wm = decor->parent_client->wmref; + + /* FIXME: Check if we already have a parent here */ + + mb_wm_util_trap_x_errors(); + + XReparentWindow (wm->xdpy, + decor->xwin, + decor->parent_client->xwin_frame, + decor->geom.x, + decor->geom.y); + + if (mb_wm_util_untrap_x_errors()) + return False; + + return True; +} + +static void +mb_wm_decor_calc_geometry (MBWMDecor *decor) +{ + MBWindowManager *wm; + MBWindowManagerClient *client; + int n, s, w, e; + + if (decor->parent_client == NULL) + return; + + client = decor->parent_client; + wm = client->wmref; + + mb_wm_theme_get_decor_dimensions (wm->theme, client, + &n, &s, &w, &e); + + switch (decor->type) + { + case MBWMDecorTypeNorth: + decor->geom.x = 0; + decor->geom.y = 0; + decor->geom.height = n; + decor->geom.width = client->frame_geometry.width; + break; + case MBWMDecorTypeSouth: + decor->geom.x = 0; + decor->geom.y = client->window->geometry.height + n; + decor->geom.height = s; + decor->geom.width = client->frame_geometry.width; + break; + case MBWMDecorTypeWest: + decor->geom.x = 0; + decor->geom.y = n; + decor->geom.height = client->window->geometry.height; + decor->geom.width = w; + break; + case MBWMDecorTypeEast: + decor->geom.x = client->window->geometry.width + w; + decor->geom.y = n; + decor->geom.height = client->window->geometry.height; + decor->geom.width = e; + break; + default: + /* FIXME: some kind of callback for custom types here ? */ + break; + } + + MBWM_DBG("geom is +%i+%i %ix%i, Type %i", + decor->geom.x, + decor->geom.y, + decor->geom.width, + decor->geom.height, + decor->type); + + return; +} + +void +mb_wm_decor_handle_map (MBWMDecor *decor) +{ + /* Not needed as XMapSubWindows() is used */ +} + + +void +mb_wm_decor_handle_repaint (MBWMDecor *decor) +{ + MBWMList *l; + + if (decor->parent_client == NULL) + return; + + if (decor->dirty) + { + mb_wm_decor_repaint(decor); + + l = decor->buttons; + while (l) + { + MBWMDecorButton * button = l->data; + mb_wm_decor_button_handle_repaint (button); + + l = l->next; + } + + decor->dirty = MBWMDecorDirtyNot; + } +} + +void +mb_wm_decor_handle_resize (MBWMDecor *decor) +{ + if (decor->parent_client == NULL) + return; + + mb_wm_decor_calc_geometry (decor); + + mb_wm_decor_sync_window (decor); + + /* Fire resize callback */ + mb_wm_decor_resize(decor); + + /* Fire repaint callback */ + mb_wm_decor_mark_dirty (decor); +} + +MBWMDecor* +mb_wm_decor_new (MBWindowManager *wm, + MBWMDecorType type) +{ + MBWMObject *decor; + + decor = mb_wm_object_new (MB_WM_TYPE_DECOR, + MBWMObjectPropWm, wm, + MBWMObjectPropDecorType, type, + NULL); + + return MB_WM_DECOR(decor); +} + +Window +mb_wm_decor_get_x_window (MBWMDecor *decor) +{ + return decor->xwin; +} + +MBWMDecorType +mb_wm_decor_get_type (MBWMDecor *decor) +{ + return decor->type; +} + +MBWindowManagerClient* +mb_wm_decor_get_parent (MBWMDecor *decor) +{ + return decor->parent_client; +} + + +const MBGeometry* +mb_wm_decor_get_geometry (MBWMDecor *decor) +{ + return &decor->geom; +} + +int +mb_wm_decor_get_pack_start_x (MBWMDecor *decor) +{ + return decor->pack_start_x; +} + + +int +mb_wm_decor_get_pack_end_x (MBWMDecor *decor) +{ + return decor->pack_end_x; +} + + +/* Mark a client in need of a repaint */ +void +mb_wm_decor_mark_dirty (MBWMDecor *decor) +{ + decor->dirty |= MBWMDecorDirtyPaint; + + if (decor->parent_client) + mb_wm_client_decor_mark_dirty (decor->parent_client); +} + +void +mb_wm_decor_mark_title_dirty (MBWMDecor *decor) +{ + decor->dirty |= MBWMDecorDirtyTitle; + + if (decor->parent_client) + mb_wm_client_decor_mark_dirty (decor->parent_client); +} + +MBWMDecorDirtyState +mb_wm_decor_get_dirty_state (MBWMDecor *decor) +{ + return decor->dirty; +} + +void +mb_wm_decor_attach (MBWMDecor *decor, + MBWindowManagerClient *client) +{ + decor->parent_client = client; + client->decor = mb_wm_util_list_append(client->decor, decor); + + mb_wm_decor_mark_dirty (decor); + + return; +} + +void +mb_wm_decor_detach (MBWMDecor *decor) +{ +} + +static void +mb_wm_decor_destroy (MBWMObject* obj) +{ + MBWMDecor * decor = MB_WM_DECOR(obj); + MBWMList * l = decor->buttons; + MBWMMainContext * ctx = decor->parent_client->wmref->main_ctx; + + if (decor->themedata && decor->destroy_themedata) + { + decor->destroy_themedata (decor, decor->themedata); + decor->themedata = NULL; + decor->destroy_themedata = NULL; + } + + mb_wm_decor_detach (decor); + + while (l) + { + MBWMList * old = l; + mb_wm_object_unref (MB_WM_OBJECT (l->data)); + l = l->next; + free (old); + } + + if (decor->press_cb_id) + mb_wm_main_context_x_event_handler_remove (ctx, ButtonPress, + decor->press_cb_id); +} + +void +mb_wm_decor_set_theme_data (MBWMDecor * decor, void *userdata, + MBWMDecorDestroyUserData destroy) +{ + if (decor->themedata && decor->destroy_themedata) + decor->destroy_themedata (decor, decor->themedata); + + decor->themedata = userdata; + decor->destroy_themedata = destroy; +} + +void * +mb_wm_decor_get_theme_data (MBWMDecor * decor) +{ + return decor->themedata; +} + +/* Buttons */ +static void +mb_wm_decor_button_destroy (MBWMObject* obj); + +static void +mb_wm_decor_button_stock_button_action (MBWMDecorButton *button) +{ + MBWindowManagerClient *client = button->decor->parent_client; + MBWindowManager *wm = client->wmref; + + switch (button->type) + { + case MBWMDecorButtonClose: + mb_wm_client_deliver_delete (client); + break; + case MBWMDecorButtonMinimize: + mb_wm_client_iconize (client); + break; + case MBWMDecorButtonFullscreen: + mb_wm_client_set_state (client, + MBWM_ATOM_NET_WM_STATE_FULLSCREEN, + MBWMClientWindowStateChangeAdd); + break; + case MBWMDecorButtonAccept: + mb_wm_client_deliver_wm_protocol (client, + wm->atoms[MBWM_ATOM_NET_WM_CONTEXT_ACCEPT]); + break; + case MBWMDecorButtonHelp: + mb_wm_client_deliver_wm_protocol (client, + wm->atoms[MBWM_ATOM_NET_WM_CONTEXT_HELP]); + break; + case MBWMDecorButtonMenu: + mb_wm_client_deliver_wm_protocol (client, + wm->atoms[MBWM_ATOM_NET_WM_CONTEXT_CUSTOM]); + default: + break; + } + + return; +} + +void +mb_wm_decor_button_set_theme_data (MBWMDecorButton * button, void *themedata, + MBWMDecorButtonDestroyUserData destroy) +{ + if (button->themedata && button->destroy_themedata) + button->destroy_themedata (button, button->themedata); + + button->themedata = themedata; + button->destroy_themedata = destroy; +} + +void * +mb_wm_decor_button_get_theme_data (MBWMDecorButton * button) +{ + return button->themedata; +} + +static Bool +mb_wm_decor_button_press_handler (XButtonEvent *xev, + void *userdata) +{ + MBWMDecorButton *button = (MBWMDecorButton *)userdata; + MBWMDecor *decor = button->decor; + MBWindowManager *wm = decor->parent_client->wmref; + MBWMList *l = NULL; + Bool retval = True; + + if (xev->window == decor->xwin) + { + int xmin, ymin, xmax, ymax; + l = mb_wm_client_get_transients (decor->parent_client); + + /* Ignore events on the main window decor if transients other than + * input methods are present + */ + while (l) + { + MBWindowManagerClient * c = l->data; + + if (MB_WM_CLIENT_CLIENT_TYPE (c) != MBWMClientTypeInput && + mb_wm_client_is_modal (c)) + { + retval = True; + goto done; + } + + l = l->next; + } + + xmin = button->geom.x; + ymin = button->geom.y; + xmax = button->geom.x + button->geom.width; + ymax = button->geom.y + button->geom.height; + + if (xev->x < xmin || + xev->x > xmax || + xev->y < ymin || + xev->y > ymax) + { + retval = True; + goto done; + } + + if (button->state != MBWMDecorButtonStatePressed) + { + button->state = MBWMDecorButtonStatePressed; + mb_wm_theme_paint_button (wm->theme, button); + } + + if (button->press_activated) + { + XUngrabPointer(wm->xdpy, CurrentTime); + + mb_wm_client_deliver_message (decor->parent_client, + wm->atoms[MBWM_ATOM_MB_GRAB_TRANSFER], + xev->time, + xev->subwindow, + xev->button, 0, 0); + + XSync (wm->xdpy, False); /* Necessary */ + + if (button->press) + button->press(wm, button, button->userdata); + else + mb_wm_decor_button_stock_button_action (button); + } + else + { + XEvent ev; + + /* + * First, call the custom function if any. + */ + if (button->press) + button->press(wm, button, button->userdata); + + if (XGrabPointer(wm->xdpy, xev->subwindow, False, + ButtonPressMask|ButtonReleaseMask| + PointerMotionMask|EnterWindowMask|LeaveWindowMask, + GrabModeAsync, + GrabModeAsync, + None, None, CurrentTime) == GrabSuccess) + { + if (button->state == MBWMDecorButtonStateInactive) + { + button->state = MBWMDecorButtonStatePressed; + mb_wm_theme_paint_button (wm->theme, button); + } + + for (;;) + { + /* + * First of all, we make sure that all events are flushed + * out (this is necessary to ensure that all the events we + * are interested in are actually intercepted here). + */ + XSync (wm->xdpy, False); + + if (XCheckMaskEvent(wm->xdpy, + ButtonPressMask|ButtonReleaseMask| + PointerMotionMask|EnterWindowMask| + LeaveWindowMask, + &ev)) + { + switch (ev.type) + { + case MotionNotify: + { + XMotionEvent *pev = (XMotionEvent*)&ev; + + if (pev->x < xmin || pev->x > xmax || + pev->y < ymin || pev->y > ymax) + { + if (button->state == + MBWMDecorButtonStatePressed) + { + button->state = + MBWMDecorButtonStateInactive; + mb_wm_theme_paint_button (wm->theme,button); + } + } + else + { + if (button->state != + MBWMDecorButtonStatePressed) + { + button->state = MBWMDecorButtonStatePressed; + mb_wm_theme_paint_button (wm->theme,button); + } + } + } + break; + case EnterNotify: + if (button->state == MBWMDecorButtonStateInactive) + { + button->state = MBWMDecorButtonStatePressed; + mb_wm_theme_paint_button (wm->theme, button); + } + break; + case LeaveNotify: + if (button->state != MBWMDecorButtonStateInactive) + { + button->state = MBWMDecorButtonStateInactive; + mb_wm_theme_paint_button (wm->theme, button); + } + break; + case ButtonRelease: + { + XButtonEvent *pev = (XButtonEvent*)&ev; + + if (button->state != MBWMDecorButtonStateInactive) + { + button->state = MBWMDecorButtonStateInactive; + mb_wm_theme_paint_button (wm->theme, button); + } + + XUngrabPointer (wm->xdpy, CurrentTime); + XSync (wm->xdpy, False); /* necessary */ + + if (pev->x < xmin || pev->x > xmax || + pev->y < ymin || pev->y > ymax) + { + retval = False; + goto done; + } + + if (button->release) + button->release(wm, button, button->userdata); + else + mb_wm_decor_button_stock_button_action (button); + + return False; + } + } + } + else + { + /* + * No pending X event, so spin the main loop (this allows + * things like timers to work. + */ + mb_wm_main_context_spin_loop (wm->main_ctx); + } + } + } + } + + retval = False; + } + + done: + mb_wm_util_list_free (l); + return retval; +} + +static void +mb_wm_decor_button_class_init (MBWMObjectClass *klass) +{ +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMDecorButton"; +#endif +} + +static int +mb_wm_decor_button_init (MBWMObject *obj, va_list vap) +{ + MBWMDecorButton *button = MB_WM_DECOR_BUTTON (obj); + MBWindowManager *wm = NULL; + MBWMDecor *decor = NULL; + MBWMDecorButtonPressedFunc press = NULL; + MBWMDecorButtonReleasedFunc release = NULL; + MBWMDecorButtonFlags flags = 0; + MBWMDecorButtonType type = 0; + MBWMDecorButtonPack pack = MBWMDecorButtonPackEnd; + MBWMObjectProp prop; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + switch (prop) + { + case MBWMObjectPropWm: + wm = va_arg(vap, MBWindowManager *); + break; + case MBWMObjectPropDecor: + decor = va_arg(vap, MBWMDecor*); + break; + case MBWMObjectPropDecorButtonPressedFunc: + press = va_arg(vap, MBWMDecorButtonPressedFunc); + break; + case MBWMObjectPropDecorButtonReleasedFunc: + release = va_arg(vap, MBWMDecorButtonReleasedFunc); + break; + case MBWMObjectPropDecorButtonFlags: + flags = va_arg(vap, MBWMDecorButtonFlags); + break; + case MBWMObjectPropDecorButtonType: + type = va_arg(vap, MBWMDecorButtonType); + break; + case MBWMObjectPropDecorButtonPack: + pack = va_arg(vap, MBWMDecorButtonPack); + break; + default: + MBWMO_PROP_EAT (vap, prop); + } + + prop = va_arg(vap, MBWMObjectProp); + } + + if (!wm || !decor) + return 0; + + /* + * Decors must be attached before we can start adding buttons to them, + * otherwise we cannot work out the button geometry. + */ + MBWM_ASSERT (decor->parent_client); + + button->geom.width = 0; + button->geom.height = 0; + + mb_wm_theme_get_button_size (wm->theme, + decor, + type, + &button->geom.width, + &button->geom.height); + + button->press = press; + button->release = release; + button->decor = decor; + button->type = type; + button->pack = pack; + button->press_activated = mb_wm_theme_is_button_press_activated (wm->theme, + decor, + type); + + decor->buttons = mb_wm_util_list_append (decor->buttons, button); + + /* the decor assumes a reference, so add one for the caller */ + mb_wm_object_ref (obj); + + return 1; +} + +int +mb_wm_decor_button_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMDecorButtonClass), + sizeof (MBWMDecorButton), + mb_wm_decor_button_init, + mb_wm_decor_button_destroy, + mb_wm_decor_button_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); + } + + return type; +} + +static void +mb_wm_decor_button_destroy (MBWMObject* obj) +{ + MBWMDecorButton * button = MB_WM_DECOR_BUTTON (obj); + MBWMMainContext * ctx = button->decor->parent_client->wmref->main_ctx; + + if (button->userdata && button->destroy_userdata) + { + button->destroy_userdata (button, button->userdata); + button->userdata = NULL; + button->destroy_userdata = NULL; + } + + if (button->themedata && button->destroy_themedata) + { + button->destroy_themedata (button, button->themedata); + button->themedata = NULL; + button->destroy_themedata = NULL; + } + + mb_wm_main_context_x_event_handler_remove (ctx, ButtonPress, + button->press_cb_id); +} + +static void +mb_wm_decor_button_realize (MBWMDecorButton *button) +{ + MBWMDecor *decor = button->decor; + MBWindowManager *wm = decor->parent_client->wmref; + + button->press_cb_id = + mb_wm_main_context_x_event_handler_add (wm->main_ctx, + decor->xwin, + ButtonPress, + (MBWMXEventFunc)mb_wm_decor_button_press_handler, + button); + + button->realized = True; +} + +static void +mb_wm_decor_button_sync_window (MBWMDecorButton *button) +{ + if (!button->realized) + { + mb_wm_decor_button_realize (button); + } +} + +void +mb_wm_decor_button_show (MBWMDecorButton *button) +{ + button->visible = True; +} + +void +mb_wm_decor_button_hide (MBWMDecorButton *button) +{ + button->visible = False; +} + +void +mb_wm_decor_button_move_to (MBWMDecorButton *button, int x, int y) +{ + /* FIXME: set a sync flag so it know X movewindow is needed */ + button->geom.x = x; + button->geom.y = y; + + MBWM_DBG ("####### moving to %i, %i\n", button->geom.x, button->geom.y); +} + +MBWMDecorButton* +mb_wm_decor_button_new (MBWindowManager *wm, + MBWMDecorButtonType type, + MBWMDecorButtonPack pack, + MBWMDecor *decor, + MBWMDecorButtonPressedFunc press, + MBWMDecorButtonReleasedFunc release, + MBWMDecorButtonFlags flags) +{ + MBWMObject *button; + + button = mb_wm_object_new (MB_WM_TYPE_DECOR_BUTTON, + MBWMObjectPropWm, wm, + MBWMObjectPropDecorButtonType, type, + MBWMObjectPropDecorButtonPack, pack, + MBWMObjectPropDecor, decor, + MBWMObjectPropDecorButtonPressedFunc, press, + MBWMObjectPropDecorButtonReleasedFunc, release, + MBWMObjectPropDecorButtonFlags, flags, + NULL); + + return MB_WM_DECOR_BUTTON(button); +} + +MBWMDecorButton* +mb_wm_decor_button_stock_new (MBWindowManager *wm, + MBWMDecorButtonType type, + MBWMDecorButtonPack pack, + MBWMDecor *decor, + MBWMDecorButtonFlags flags) +{ + MBWMObject *button; + + button = mb_wm_object_new (MB_WM_TYPE_DECOR_BUTTON, + MBWMObjectPropWm, wm, + MBWMObjectPropDecorButtonType, type, + MBWMObjectPropDecorButtonPack, pack, + MBWMObjectPropDecor, decor, + MBWMObjectPropDecorButtonFlags, flags, + NULL); + + return MB_WM_DECOR_BUTTON(button); +} + +void +mb_wm_decor_button_handle_repaint (MBWMDecorButton *button) +{ + MBWMDecor * decor = button->decor; + MBWMTheme * theme = decor->parent_client->wmref->theme; + + if (decor->parent_client == NULL) + return; + + mb_wm_theme_paint_button (theme, button); +} + +void +mb_wm_decor_button_set_user_data (MBWMDecorButton * button, void *userdata, + MBWMDecorButtonDestroyUserData destroy) +{ + button->userdata = userdata; + button->destroy_userdata = destroy; +} + +void * +mb_wm_decor_button_get_user_data (MBWMDecorButton * button) +{ + return button->userdata; +} diff --git a/matchbox/mb-wm-decor.h b/matchbox/mb-wm-decor.h new file mode 100644 index 0000000..99d1347 --- /dev/null +++ b/matchbox/mb-wm-decor.h @@ -0,0 +1,248 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_WM_DECOR_H +#define _HAVE_MB_WM_DECOR_H + +#include <matchbox/mb-wm-client.h> + +#define MB_WM_DECOR(c) ((MBWMDecor*)(c)) +#define MB_WM_DECOR_CLASS(c) ((MBWMDecorClass*)(c)) +#define MB_WM_TYPE_DECOR (mb_wm_decor_class_type ()) + +typedef struct MBWMDecor MBWMDecor; +typedef struct MBWMDecorClass MBWMDecorClass; + +#define MB_WM_DECOR_BUTTON(c) ((MBWMDecorButton*)(c)) +#define MB_WM_DECOR_BUTTON_CLASS(c) ((MBWMDecorButtonClass*)(c)) +#define MB_WM_TYPE_DECOR_BUTTON (mb_wm_decor_button_class_type ()) + +typedef struct MBWMDecorButton MBWMDecorButton; +typedef struct MBWMDecorButtonClass MBWMDecorButtonClass; + +typedef void (*MBWMDecorButtonPressedFunc) (MBWindowManager *wm, + MBWMDecorButton *button, + void *userdata); + +typedef void (*MBWMDecorButtonReleasedFunc) (MBWindowManager *wm, + MBWMDecorButton *button, + void *userdata); + + +typedef void (*MBWMDecorDestroyUserData) (MBWMDecor *, void *); +typedef void (*MBWMDecorButtonDestroyUserData) (MBWMDecorButton *, void *); + +typedef enum MBWMDecorDirtyState +{ + MBWMDecorDirtyNot = 0, + MBWMDecorDirtyTitle = (1<<0), + MBWMDecorDirtyPaint = (1<<1), + + MBWMDecorDirtyFull = 0xffffffff, +} MBWMDecorDirtyState; + +struct MBWMDecor +{ + MBWMObject parent; + MBWMDecorType type; + Window xwin; + MBWindowManagerClient *parent_client; + MBGeometry geom; + MBWMDecorDirtyState dirty; + Bool absolute_packing; + MBWMList *buttons; + int pack_start_x; + int pack_end_x; + + unsigned long press_cb_id; + unsigned long release_cb_id; + + void *themedata; + MBWMDecorDestroyUserData destroy_themedata; +}; + +struct MBWMDecorClass +{ + MBWMObjectClass parent; +}; + + +MBWMDecor* +mb_wm_decor_new (MBWindowManager *wm, + MBWMDecorType type); + +void +mb_wm_decor_handle_repaint (MBWMDecor *decor); + +void +mb_wm_decor_handle_resize (MBWMDecor *decor); + +Window +mb_wm_decor_get_x_window (MBWMDecor *decor); + +MBWMDecorType +mb_wm_decor_get_type (MBWMDecor *decor); + +int +mb_wm_decor_get_pack_start_x (MBWMDecor *decor); + +int +mb_wm_decor_get_pack_end_x (MBWMDecor *decor); + +const MBGeometry* +mb_wm_decor_get_geometry (MBWMDecor *decor); + +MBWindowManagerClient* +mb_wm_decor_get_parent (MBWMDecor *decor); + +void +mb_wm_decor_mark_dirty (MBWMDecor *decor); + +void +mb_wm_decor_mark_title_dirty (MBWMDecor *decor); + +MBWMDecorDirtyState +mb_wm_decor_get_dirty_state (MBWMDecor *decor); + +void +mb_wm_decor_attach (MBWMDecor *decor, + MBWindowManagerClient *client); + +void +mb_wm_decor_detach (MBWMDecor *decor); + +void +mb_wm_decor_set_theme_data (MBWMDecor * decor, void *userdata, + MBWMDecorDestroyUserData destroy); + +void * +mb_wm_decor_get_theme_data (MBWMDecor* decor); + +typedef enum MBWMDecorButtonState +{ + MBWMDecorButtonStateInactive = 0, + MBWMDecorButtonStatePressed + +} MBWMDecorButtonState ; + +typedef enum MBWMDecorButtonType +{ + MBWMDecorButtonCustom = 0, + MBWMDecorButtonClose = 1, + MBWMDecorButtonMenu = 2, + MBWMDecorButtonMinimize = 3, + MBWMDecorButtonFullscreen = 4, + MBWMDecorButtonAccept = 5, + MBWMDecorButtonHelp = 6, +} MBWMDecorButtonType; + +typedef enum MBWMDecorButtonPack + { + MBWMDecorButtonPackStart = 0, + MBWMDecorButtonPackEnd = 1, + } +MBWMDecorButtonPack; + +struct MBWMDecorButton +{ + MBWMObject parent; + MBWMDecorButtonType type; + MBWMDecorButtonPack pack; + MBWMDecor *decor; + + MBGeometry geom; + + /* in priv ? */ + Bool visible; + Bool needs_sync; + Bool realized; + Bool press_activated; + MBWMDecorButtonState state; + + MBWMDecorButtonPressedFunc press; + MBWMDecorButtonReleasedFunc release; + + unsigned long press_cb_id; + + /* Data for any custom button callbacks */ + void *userdata; + MBWMDecorButtonDestroyUserData destroy_userdata; + + /* Data utilized by the theme engine */ + void *themedata; + MBWMDecorButtonDestroyUserData destroy_themedata; + +}; + +struct MBWMDecorButtonClass +{ + MBWMObjectClass parent; + + /* + show; + hide; + realize; ?? + */ +}; + +void +mb_wm_decor_button_show (MBWMDecorButton *button); + +void +mb_wm_decor_button_hide (MBWMDecorButton *button); + +void +mb_wm_decor_button_move_to (MBWMDecorButton *button, int x, int y); + +void +mb_wm_decor_button_handle_repaint (MBWMDecorButton *button); + +MBWMDecorButton* +mb_wm_decor_button_new (MBWindowManager *wm, + MBWMDecorButtonType type, + MBWMDecorButtonPack pack, + MBWMDecor *decor, + MBWMDecorButtonPressedFunc press, + MBWMDecorButtonReleasedFunc release, + MBWMDecorButtonFlags flags); + + +MBWMDecorButton* +mb_wm_decor_button_stock_new (MBWindowManager *wm, + MBWMDecorButtonType type, + MBWMDecorButtonPack pack, + MBWMDecor *decor, + MBWMDecorButtonFlags flags); + +void +mb_wm_decor_button_set_user_data (MBWMDecorButton * button, void *userdata, + MBWMDecorButtonDestroyUserData destroy); + +void * +mb_wm_decor_button_get_user_data (MBWMDecorButton * button); + +void +mb_wm_decor_button_set_theme_data (MBWMDecorButton * button, void *themedata, + MBWMDecorButtonDestroyUserData destroy); + +void * +mb_wm_decor_button_get_theme_data (MBWMDecorButton* button); + +#endif diff --git a/matchbox/mb-wm-keys.c b/matchbox/mb-wm-keys.c new file mode 100644 index 0000000..41d416c --- /dev/null +++ b/matchbox/mb-wm-keys.c @@ -0,0 +1,379 @@ +#include "matchbox.h" +#include <ctype.h> /* isalpha etc */ + +struct MBWMKeys /* FIXME: Probably do want to hide these here */ +{ + MBWMList *bindings; /* Always points to first binding */ + + int MetaMask; + int HyperMask; + int SuperMask; + int AltMask; + int ModeMask; + int NumLockMask; + int ScrollLockMask; + int lock_mask; + +}; + +static Bool +keysym_needs_shift (MBWindowManager *wm, KeySym keysym) +{ + int min_kc, max_kc, keycode, col; + KeySym k; + + XDisplayKeycodes(wm->xdpy, &min_kc, &max_kc); + + for (keycode = min_kc; keycode <= max_kc; keycode++) + for (col = 0; + (k = XKeycodeToKeysym (wm->xdpy, keycode, col)) != NoSymbol; + col++) + if (k == keysym && col == 1) + return True; + + return False; +} + +static Bool +key_binding_set_grab (MBWindowManager *wm, + MBWMKeyBinding *key, + Bool ungrab) +{ + int ignored_mask = 0; + + MBWM_ASSERT (wm->keys != NULL); + + /* Needed to grab all Locked combo's too */ + while (ignored_mask < (int) wm->keys->lock_mask) + { + if (ignored_mask & ~(wm->keys->lock_mask)) + { + ++ignored_mask; + continue; + } + + if (ungrab) + { + MBWM_DBG("ungrabbing %i , %i", + XKeysymToKeycode(wm->xdpy, key->keysym), + key->modifier_mask); + + XUngrabKey(wm->xdpy, + XKeysymToKeycode(wm->xdpy, key->keysym), + key->modifier_mask | ignored_mask, + wm->root_win->xwindow); + } + else + { + int result; + + mb_wm_util_trap_x_errors(); + + MBWM_DBG ("grabbing keycode: %i, keysym %li, mask: %i", + XKeysymToKeycode(wm->xdpy, key->keysym), + key->keysym, + key->modifier_mask | ignored_mask); + + XGrabKey(wm->xdpy, XKeysymToKeycode(wm->xdpy, key->keysym), + key->modifier_mask | ignored_mask, + wm->root_win->xwindow, True, GrabModeAsync, GrabModeAsync); + + result = mb_wm_util_untrap_x_errors(); + + if (result != Success) + { + if (result == BadAccess) + mb_wm_util_warn ("Some other program is already using the key %s with modifiers %x as a binding\n", + (XKeysymToString(key->keysym)) ? XKeysymToString (key->keysym) : "unknown", + key->modifier_mask | ignored_mask ); + else + mb_wm_util_warn ("Unable to grab the key %s with modifiers %x as a binding\n", + (XKeysymToString(key->keysym)) ? XKeysymToString (key->keysym) : "unknown", + key->modifier_mask | ignored_mask ); + return False; + } + } + + ++ignored_mask; + } + + return True; +} + +void +mb_wm_keys_binding_remove_all (MBWindowManager *wm) +{ + +} + +void +mb_wm_keys_binding_remove (MBWindowManager *wm, + MBWMKeyBinding *binding) +{ + + key_binding_set_grab (wm, binding, True); +} + +MBWMKeyBinding* +mb_wm_keys_binding_add (MBWindowManager *wm, + KeySym ks, + int mask, + MBWMKeyPressedFunc press_func, + MBWMKeyDestroyFunc destroy_func, + void *userdata) +{ + MBWMKeyBinding *binding = NULL; + MBWMKeys *keys = wm->keys; + + MBWM_ASSERT (wm->keys != NULL); + + binding = mb_wm_util_malloc0(sizeof(MBWMKeyBinding)); + + binding->keysym = ks; + binding->modifier_mask = mask; + binding->pressed = press_func; + binding->destroy = destroy_func; + binding->userdata = userdata; + + if (key_binding_set_grab (wm, binding, False)) + { + keys->bindings = mb_wm_util_list_append(keys->bindings, binding); + return binding; + } + + /* Grab failed */ + free(binding); + return NULL; +} + +MBWMKeyBinding* +mb_wm_keys_binding_add_with_spec (MBWindowManager *wm, + const char *keystr, + MBWMKeyPressedFunc press_func, + MBWMKeyDestroyFunc destroy_func, + void *userdata) +{ + char *orig, *p, *q, *keydef = NULL; + int i = 0, mask = 0; + Bool want_shift = False; + KeySym ks; + MBWMKeys *keys = wm->keys; + MBWMKeyBinding *binding = NULL; + + struct { char *def; int mask; } lookup[] = + { + { "ctrl", ControlMask }, + { "alt", keys->AltMask }, + { "meta", keys->MetaMask }, + { "super",keys->SuperMask }, + { "hyper",keys->HyperMask }, + { "mod1", Mod1Mask }, + { "mod2", Mod2Mask }, + { "mod3", Mod3Mask }, + { "mod4", Mod4Mask }, + { "mod5", Mod5Mask }, + { "shift",-1 }, + { NULL, 0 } + }; + + orig = p = strdup(keystr); + + /* parse '<mod><mod><mod>key' */ + + while (*p != '\0') + { + Bool found = False; + + if (*p == '<') + { + q = ++p; i = 0; + + while (*q != '\0' && *q != '>') + q++; + + if (*q == '\0') + goto out; /* Parse error */ + + while (lookup[i].def != NULL && !found) + { + if (!strncasecmp(p, lookup[i].def, q-p) && lookup[i].mask) + { + if (lookup[i].mask == -1) + want_shift = True; + else + mask |= lookup[i].mask; + found = True; + } + i++; + } + + if (found) + p = q; + else + goto out; + } + else if (!isspace(*p)) + { + keydef = p; + break; + } + + p++; + } + + if (!keydef) + goto out; + + MBWM_DBG("keydefinition is %s, want_shift is %i", keydef, want_shift); + + if ((ks = XStringToKeysym(keydef)) == (KeySym)NULL) + { + if (islower(keydef[0])) /* Try again, changing case */ + keydef[0] = toupper(keydef[0]); + else + keydef[0] = tolower(keydef[0]); + + if ((ks = XStringToKeysym(keydef)) == (KeySym)NULL) + { + mb_wm_util_warn ("Cant find keysym for %s", keydef); + goto out; + } + } + + if (keysym_needs_shift(wm, ks) || want_shift) + mask |= ShiftMask; + + /* If we grab keycode 0, we end up grabbing the entire keyboard.. */ + if (XKeysymToKeycode(wm->xdpy, ks) == 0 && mask == 0) + { + MBWM_DBG("Cant find a keycode for keysym %li", ks); + goto out; + } + + binding = mb_wm_keys_binding_add (wm, ks, mask, + press_func, destroy_func, userdata); + + out: + + free (orig); + return binding; +} + +void /* FIXME: rename */ +mb_wm_keys_press (MBWindowManager *wm, + KeySym keysym, + int modifier_mask) +{ + MBWMList *iter; + MBWMKeyBinding *binding; + + if (!wm->keys) + return; + + MBWM_DBG ("Looking up keysym <%li>, ( mask %i )", keysym, modifier_mask); + + iter = wm->keys->bindings; + + while (iter) + { + int ignored_mask = 0; + + binding = (MBWMKeyBinding*)iter->data; + + MBWM_DBG ("Checking up keysym <%li>, ( mask %i )", + binding->keysym, + binding->modifier_mask); + + /* FIXME: Assumes multiple bindings per key */ + while (ignored_mask < (int) wm->keys->lock_mask) + { + if (ignored_mask & ~(wm->keys->lock_mask)) + { + ++ignored_mask; + continue; + } + + if (binding->pressed + && binding->keysym == keysym + && binding->modifier_mask|ignored_mask == modifier_mask) + { + binding->pressed(wm, binding, binding->userdata); + break; + } + + ++ignored_mask; + } + + iter = mb_wm_util_list_next(iter); + } +} + + +Bool +mb_wm_keys_init(MBWindowManager *wm) +{ + int mod_idx, mod_key, col, kpm; + XModifierKeymap *mod_map; + MBWMKeys *keys; + + mod_map = XGetModifierMapping(wm->xdpy); + + keys = wm->keys = mb_wm_util_malloc0(sizeof(MBWMKeys)); + + /* Figure out modifier masks */ + + kpm = mod_map->max_keypermod; + for (mod_idx = 0; mod_idx < 8; mod_idx++) + for (mod_key = 0; mod_key < kpm; mod_key++) + { + KeySym last_sym = 0; + for (col = 0; col < 4; col += 2) + { + KeyCode code = mod_map->modifiermap[mod_idx * kpm + mod_key]; + KeySym sym = (code ? XKeycodeToKeysym(wm->xdpy, code, col) : 0); + + if (sym == last_sym) continue; + last_sym = sym; + + switch (sym) + { + case XK_Mode_switch: + /* XXX store_modifier("Mode_switch", mode_bit); */ + break; + case XK_Meta_L: + case XK_Meta_R: + keys->MetaMask |= (1 << mod_idx); + break; + case XK_Super_L: + case XK_Super_R: + keys->SuperMask |= (1 << mod_idx); + break; + case XK_Hyper_L: + case XK_Hyper_R: + keys->HyperMask |= (1 << mod_idx); + break; + case XK_Alt_L: + case XK_Alt_R: + keys->AltMask |= (1 << mod_idx); + break; + case XK_Num_Lock: + keys->NumLockMask |= (1 << mod_idx); + break; + case XK_Scroll_Lock: + keys->ScrollLockMask |= (1 << mod_idx); + break; + } + } + } + + /* XXX check this. assume alt <=> meta if only either set */ + if (!keys->AltMask) keys->AltMask = keys->MetaMask; + if (!keys->MetaMask) keys->MetaMask = keys->AltMask; + + keys->lock_mask = keys->ScrollLockMask | keys->NumLockMask | LockMask; + + if (mod_map) XFreeModifiermap(mod_map); + + return True; +} + diff --git a/matchbox/mb-wm-keys.h b/matchbox/mb-wm-keys.h new file mode 100644 index 0000000..eb5608c --- /dev/null +++ b/matchbox/mb-wm-keys.h @@ -0,0 +1,76 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_WM_KEYS_H +#define _HAVE_MB_WM_KEYS_H + +#include <matchbox/matchbox.h> + +typedef struct MBWMKeys MBWMKeys; +typedef struct MBWMKeyBinding MBWMKeyBinding; + +typedef void (*MBWMKeyPressedFunc) (MBWindowManager *wm, + MBWMKeyBinding *binding, + void *userdata); + +typedef void (*MBWMKeyDestroyFunc) (MBWindowManager *wm, + MBWMKeyBinding *binding, + void *userdata); + +struct MBWMKeyBinding +{ + KeySym keysym; + int modifier_mask; + MBWMKeyPressedFunc pressed; + MBWMKeyDestroyFunc destroy; + void *userdata; + /* FIXME: free func */ +}; + +void +mb_wm_keys_binding_remove_all (MBWindowManager *wm); + +void +mb_wm_keys_binding_remove (MBWindowManager *wm, + MBWMKeyBinding *binding); +MBWMKeyBinding* +mb_wm_keys_binding_add (MBWindowManager *wm, + KeySym ks, + int mask, + MBWMKeyPressedFunc press_func, + MBWMKeyDestroyFunc destroy_func, + void *userdata); + +MBWMKeyBinding* +mb_wm_keys_binding_add_with_spec (MBWindowManager *wm, + const char *keystr, + MBWMKeyPressedFunc press_func, + MBWMKeyDestroyFunc destroy_func, + void *userdata); + +void +mb_wm_keys_press (MBWindowManager *wm, + KeySym keysym, + int modifier_mask); + +Bool +mb_wm_keys_init (MBWindowManager *wm); + +#endif diff --git a/matchbox/mb-wm-layout.c b/matchbox/mb-wm-layout.c new file mode 100644 index 0000000..7e92998 --- /dev/null +++ b/matchbox/mb-wm-layout.c @@ -0,0 +1,753 @@ +#include "matchbox.h" +#include "mb-wm-client-input.h" + +static void +mb_wm_layout_real_update (MBWMLayout * layout); +static void +mb_wm_layout_real_layout_panels (MBWMLayout *layout, MBGeometry * avail_geom); +static void +mb_wm_layout_real_layout_input (MBWMLayout *layout, MBGeometry * avail_geom); +static void +mb_wm_layout_real_layout_free (MBWMLayout *layout, MBGeometry * avail_geom); +static void +mb_wm_layout_real_layout_fullscreen (MBWMLayout *layout, MBGeometry * avail_geom); + +static void +mb_wm_layout_class_init (MBWMObjectClass *klass) +{ + MBWMLayoutClass * layout_class = MB_WM_LAYOUT_CLASS (klass); + + layout_class->update = mb_wm_layout_real_update; + layout_class->layout_panels = mb_wm_layout_real_layout_panels; + layout_class->layout_input = mb_wm_layout_real_layout_input; + layout_class->layout_free = mb_wm_layout_real_layout_free; + layout_class->layout_fullscreen = mb_wm_layout_real_layout_fullscreen; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMLayout"; +#endif +} + +static void +mb_wm_layout_destroy (MBWMObject *this) +{ +} + +static int +mb_wm_layout_init (MBWMObject *this, va_list vap) +{ + MBWMLayout *layout = MB_WM_LAYOUT (this); + MBWMObjectProp prop; + MBWindowManager *wm = NULL; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + switch (prop) + { + case MBWMObjectPropWm: + wm = va_arg(vap, MBWindowManager *); + break; + default: + MBWMO_PROP_EAT (vap, prop); + } + + prop = va_arg(vap, MBWMObjectProp); + } + + MBWM_ASSERT (wm); + + layout->wm = wm; + + return 1; +} + +int +mb_wm_layout_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMLayoutClass), + sizeof (MBWMLayout), + mb_wm_layout_init, + mb_wm_layout_destroy, + mb_wm_layout_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); + } + + return type; +} + +MBWMLayout* +mb_wm_layout_new (MBWindowManager *wm) +{ + MBWMLayout *layout; + + layout = MB_WM_LAYOUT (mb_wm_object_new (MB_WM_TYPE_LAYOUT, + MBWMObjectPropWm, wm, + NULL)); + return layout; +} + +Bool +mb_wm_layout_clip_geometry (MBGeometry *geom, + MBGeometry *min, + int flags) +{ + Bool changed = False; + + if (flags & SET_X) + if (geom->x < min->x || geom->x > min->x + min->width) + { + geom->x = min->x; + changed = True; + } + + if (flags & SET_Y) + if (geom->y < min->y || geom->y > min->y + min->height) + { + geom->y = min->y; + changed = True; + } + + if (flags & SET_WIDTH) + if (geom->x + geom->width > min->x + min->width) + { + int old_width = geom->width; + + geom->width = min->x + min->width - geom->x; + + /* + * if we are about to reduce the width, and are asked also to set the x + * coord, see if we can move the window left so it can be bigger. + */ + if ((flags & SET_X) && old_width > geom->width && geom->x > min->x) + { + int w_diff = old_width - geom->width; + int x = geom->x - w_diff; + int x_diff; + + if (x < min->x) + x = min->x; + + x_diff = geom->x - x; + + geom->x = x; + geom->width += x_diff; + } + + changed = True; + } + + if (flags & SET_HEIGHT) + if (geom->y + geom->height > min->y + min->height) + { + int old_height = geom->height; + + geom->height = min->y + min->height - geom->y; + + /* + * if we are about to reduce the height, and are asked also to set the + * y coord, see if we can move the window up so it can be bigger. + */ + if ((flags & SET_Y) && old_height > geom->height && geom->y > min->y) + { + int h_diff = old_height - geom->height; + int y = geom->y - h_diff; + int y_diff; + + if (y < min->y) + y = min->y; + + y_diff = geom->y - y; + + geom->y = y; + geom->height += y_diff; + } + changed = True; + } + + return changed; +} + +Bool +mb_wm_layout_maximise_geometry (MBGeometry *geom, + MBGeometry *max, + int flags) +{ + Bool changed = False; + + if (flags & SET_X && geom->x != max->x) + { + geom->x = max->x; + changed = True; + } + + if (flags & SET_Y && geom->y != max->y) + { + geom->y = max->y; + changed = True; + } + + if (flags & SET_WIDTH && geom->width != max->width) + { + geom->width = max->width; + changed = True; + } + + if (flags & SET_HEIGHT && geom->height != max->height) + { + geom->height = max->height; + changed = True; + } + + return changed; +} + +static void +mb_wm_layout_real_layout_panels (MBWMLayout *layout, MBGeometry * avail_geom) +{ + MBWindowManager *wm = layout->wm; + MBWindowManagerClient *client; + MBGeometry coverage; + Bool need_change; + + /* FIXME: need to enumerate by *age* in case multiple panels ? */ + mb_wm_stack_enumerate(wm, client) + if ((mb_wm_client_get_layout_hints(client) & LayoutPrefReserveEdgeNorth) && + (mb_wm_client_get_layout_hints(client) & LayoutPrefVisible)) + { + int flags = SET_Y; + + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefFixedX)) + flags |= SET_X | SET_WIDTH; + + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, flags); + /* Too high */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_HEIGHT); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + /* FIXME: what if this returns False ? */ + + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefOverlaps)) + { + avail_geom->y = coverage.y + coverage.height; + avail_geom->height = avail_geom->height - coverage.height; + } + } + + mb_wm_stack_enumerate(wm, client) + if ((mb_wm_client_get_layout_hints(client) & LayoutPrefReserveEdgeSouth) && + (mb_wm_client_get_layout_hints(client) & LayoutPrefVisible)) + { + int y; + + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefFixedX)) + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + SET_X | SET_WIDTH); + else + need_change = False; + + y = avail_geom->y + avail_geom->height - coverage.height; + + if (y != coverage.y) + { + coverage.y = y; + need_change = True; + } + + /* Too high */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_HEIGHT); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefOverlaps)) + avail_geom->height = avail_geom->height - coverage.height; + } + + mb_wm_stack_enumerate(wm, client) + if ((mb_wm_client_get_layout_hints(client) & LayoutPrefReserveEdgeWest) && + (mb_wm_client_get_layout_hints(client) & LayoutPrefVisible)) + { + int flags = SET_X; + + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefFixedY)) + flags |= SET_Y | SET_HEIGHT; + + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + flags); + /* Too wide */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_WIDTH); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefOverlaps)) + { + avail_geom->x = coverage.x + coverage.width; + avail_geom->width = avail_geom->width - coverage.width; + } + } + + + mb_wm_stack_enumerate(wm, client) + if ((mb_wm_client_get_layout_hints(client) & LayoutPrefReserveEdgeEast) && + (mb_wm_client_get_layout_hints(client) & LayoutPrefVisible)) + { + int x; + + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefFixedY)) + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + SET_Y|SET_HEIGHT); + else + need_change = False; + + x = avail_geom->x + avail_geom->width - coverage.width; + + if (x != coverage.x) + { + coverage.x = x; + need_change = True; + } + + /* Too wide */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_WIDTH); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefOverlaps)) + avail_geom->width = avail_geom->width - coverage.width; + } +} + +static void +mb_wm_layout_real_layout_input (MBWMLayout *layout, MBGeometry * avail_geom) +{ + MBWindowManager *wm = layout->wm; + MBWindowManagerClient *client; + MBGeometry coverage; + Bool need_change; + + mb_wm_stack_enumerate(wm, client) + if (mb_wm_client_get_layout_hints (client) == + (LayoutPrefReserveNorth|LayoutPrefVisible)) + { + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + SET_X|SET_Y|SET_WIDTH); + + if (client->transient_for && + (client->transient_for->window->ewmh_state & + MBWMClientWindowEWMHStateFullscreen) && + coverage.width != wm->xdpy_width) + { + coverage.width = wm->xdpy_width; + coverage.x = 0; + need_change = True; + } + + /* Too high */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_HEIGHT); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + /* FIXME: what if this returns False ? */ + + avail_geom->y = coverage.y + coverage.height; + avail_geom->height = avail_geom->height - coverage.height; + } + + mb_wm_stack_enumerate(wm, client) + if (mb_wm_client_get_layout_hints (client) == + (LayoutPrefReserveSouth|LayoutPrefVisible)) + { + int y; + + mb_wm_client_get_coverage (client, &coverage); + + /* First of all, tweak the y value so that we start with position + * that as much as possible respects the request for south edge + * for the current geometry. For example, the hildon input method + * initially maps with height 1 and y 399; it then requests resize + * to some sensible height, but does not adjust the y value. + */ + y = avail_geom->y + avail_geom->height - coverage.height; + + if (y < 0) + y = 0; + + if (y != coverage.y) + need_change = True; + + coverage.y = y; + + /* set x and width */ + need_change |= mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + SET_X|SET_WIDTH); + + if (client->transient_for && + (client->transient_for->window->ewmh_state & + MBWMClientWindowEWMHStateFullscreen) && + coverage.width != wm->xdpy_width) + { + coverage.width = wm->xdpy_width; + coverage.x = 0; + need_change = True; + } + + /* Too high */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_HEIGHT); + + if (coverage.y != avail_geom->y + avail_geom->height - coverage.height) + { + coverage.y = avail_geom->y + avail_geom->height - coverage.height; + need_change = True; + } + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + + avail_geom->height = avail_geom->height - coverage.height; + } + + + mb_wm_stack_enumerate(wm, client) + if (mb_wm_client_get_layout_hints (client) == + (LayoutPrefReserveWest|LayoutPrefVisible)) + { + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + SET_X|SET_Y|SET_HEIGHT); + + if (client->transient_for && + (client->transient_for->window->ewmh_state & + MBWMClientWindowEWMHStateFullscreen) && + coverage.height != wm->xdpy_height) + { + coverage.height = wm->xdpy_height; + coverage.y = 0; + need_change = True; + } + + /* Too wide */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_WIDTH); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + + avail_geom->x = coverage.x + coverage.width; + avail_geom->width = avail_geom->width - coverage.width; + } + + + mb_wm_stack_enumerate(wm, client) + if (mb_wm_client_get_layout_hints (client) == + (LayoutPrefReserveEast|LayoutPrefVisible)) + { + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + SET_Y|SET_HEIGHT); + + if (client->transient_for && + (client->transient_for->window->ewmh_state & + MBWMClientWindowEWMHStateFullscreen) && + coverage.height != wm->xdpy_height) + { + coverage.height = wm->xdpy_height; + coverage.y = 0; + need_change = True; + } + + /* Too wide */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_WIDTH); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + + if (coverage.x != avail_geom->x + avail_geom->width - coverage.width) + { + coverage.x = avail_geom->x + avail_geom->width - coverage.width; + need_change = True; + } + + avail_geom->width = avail_geom->width - coverage.width; + } +} + +static void +mb_wm_layout_real_layout_free (MBWMLayout *layout, MBGeometry * avail_geom) +{ + MBWindowManager *wm = layout->wm; + MBWindowManagerClient *client; + MBGeometry coverage; + Bool need_change; + int min_x, max_x, min_y, max_y; + + mb_wm_stack_enumerate(wm, client) + if (mb_wm_client_get_layout_hints (client) == + (LayoutPrefGrowToFreeSpace|LayoutPrefVisible)) + { + mb_wm_client_get_coverage (client, &coverage); + + if (coverage.x != avail_geom->x + || coverage.width != avail_geom->width + || coverage.y != avail_geom->y + || coverage.height != avail_geom->height) + { + MBWM_DBG("available geom for free space: %i+%i %ix%i", + min_x, min_y, max_x - min_x, max_y - min_y); + + coverage.width = avail_geom->width; + coverage.height = avail_geom->height; + coverage.x = avail_geom->x; + coverage.y = avail_geom->y; + + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + } + } + + mb_wm_stack_enumerate(wm, client) + { + MBWMClientLayoutHints hints = mb_wm_client_get_layout_hints (client); + + if ((hints & LayoutPrefPositionFree) && (hints & LayoutPrefVisible) && + !(hints & (LayoutPrefFixedX|LayoutPrefFixedY))) + { + /* Clip if needed */ + mb_wm_client_get_coverage (client, &coverage); + + need_change = mb_wm_layout_clip_geometry (&coverage, + avail_geom, + SET_X | SET_Y | + SET_HEIGHT | SET_WIDTH); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + } + } + +} + +static void +mb_wm_layout_real_layout_fullscreen (MBWMLayout *layout, MBGeometry * avail_geom) +{ + MBWindowManager *wm = layout->wm; + MBWindowManagerClient *client; + MBGeometry coverage; + + mb_wm_stack_enumerate(wm, client) + if (mb_wm_client_get_layout_hints (client) == + (LayoutPrefFullscreen|LayoutPrefVisible)) + { + MBWMList *l = mb_wm_client_get_transients (client); + + mb_wm_client_get_coverage (client, &coverage); + + /* See if this client comes with an input method and if so, + * adjust the available geometry accordingly + */ + while (l) + { + MBWindowManagerClient * c = l->data; + + if (MB_WM_CLIENT_CLIENT_TYPE (c) == MBWMClientTypeInput) + { + MBGeometry geom; + mb_wm_client_get_coverage (c, &geom); + + if (mb_wm_client_get_layout_hints (c) == + (LayoutPrefReserveSouth|LayoutPrefVisible)) + { + if (geom.y < avail_geom->y + avail_geom->height) + { + avail_geom->height = geom.y - avail_geom->y; + } + } + else if (mb_wm_client_get_layout_hints (c) == + (LayoutPrefReserveNorth|LayoutPrefVisible)) + { + if (geom.height && geom.height + geom.y > avail_geom->y) + { + int y = avail_geom->y; + + avail_geom->y = geom.y + geom.height; + avail_geom->height -= y - avail_geom->y; + } + } + else if (mb_wm_client_get_layout_hints (c) == + (LayoutPrefReserveWest|LayoutPrefVisible)) + { + if (geom.x < avail_geom->x + avail_geom->width) + { + avail_geom->width = geom.x - avail_geom->x; + } + } + else if (mb_wm_client_get_layout_hints (c) == + (LayoutPrefReserveEast|LayoutPrefVisible)) + { + if (geom.width && geom.width + geom.x > avail_geom->x) + { + int x = avail_geom->x; + + avail_geom->x = geom.x + geom.width; + avail_geom->width -= x - avail_geom->x; + } + } + + break; + } + + l = l->next; + } + + if (coverage.x != avail_geom->x + || coverage.width != avail_geom->width + || coverage.y != avail_geom->y + || coverage.height != avail_geom->height) + { + coverage.width = avail_geom->width; + coverage.height = avail_geom->height; + coverage.x = avail_geom->x; + coverage.y = avail_geom->y; + + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + } + + mb_wm_util_list_free (l); + } +} + +static void +mb_wm_layout_real_update (MBWMLayout * layout) +{ + MBWMLayoutClass *klass; + MBWindowManager *wm = layout->wm; + MBGeometry avail_geom; + + klass = MB_WM_LAYOUT_CLASS (MB_WM_OBJECT_GET_CLASS (layout)); + + MBWM_ASSERT (klass->layout_panels); + MBWM_ASSERT (klass->layout_input); + MBWM_ASSERT (klass->layout_free); + MBWM_ASSERT (klass->layout_fullscreen); + + mb_wm_get_display_geometry (wm, &avail_geom); + + /* + cycle through clients, laying out each in below order. + Note they must have LayoutPrefVisible set. + + LayoutPrefReserveEdgeNorth + LayoutPrefReserveEdgeSouth + + LayoutPrefReserveEdgeEast + LayoutPrefReserveEdgeWest + + LayoutPrefReserveNorth + LayoutPrefReserveSouth + + LayoutPrefReserveEast + LayoutPrefReserveWest + + LayoutPrefGrowToFreeSpace + + LayoutPrefFullscreen + + XXX need to check they are mapped too + + foreach client with LayoutPrefReserveEdgeNorth & LayoutPrefVisible + grab there current geometry + does it fit well into current restraints ( min_, max_ ) + yes leave + no resize so it does, mark dirty + set min_x, max_y, min_y, max_y to current size + repeat for next condition + + mb_wm_client_get_coverage (MBWindowManagerClient *client, + MBGeometry *coverage) + + */ + + klass->layout_panels (layout, &avail_geom); + klass->layout_input (layout, &avail_geom); + klass->layout_free (layout, &avail_geom); + + mb_wm_get_display_geometry (wm, &avail_geom); + klass->layout_fullscreen (layout, &avail_geom); +} + +void +mb_wm_layout_update (MBWMLayout * layout) +{ + MBWMLayoutClass *klass; + + klass = MB_WM_LAYOUT_CLASS (MB_WM_OBJECT_GET_CLASS (layout)); + + MBWM_ASSERT (klass->update); + + klass->update (layout); +} diff --git a/matchbox/mb-wm-layout.h b/matchbox/mb-wm-layout.h new file mode 100644 index 0000000..1fe97de --- /dev/null +++ b/matchbox/mb-wm-layout.h @@ -0,0 +1,80 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_WM_LAYOUT_MANAGER_H +#define _HAVE_MB_WM_LAYOUT_MANAGER_H + +#include <matchbox/mb-wm-object.h> +#include <matchbox/mb-wm-types.h> + +#define MB_WM_LAYOUT(c) ((MBWMLayout*)(c)) +#define MB_WM_LAYOUT_CLASS(c) ((MBWMLayoutClass*)(c)) +#define MB_WM_TYPE_LAYOUT (mb_wm_layout_class_type ()) + +typedef struct MBWMLayout MBWMLayout; +typedef struct MBWMLayoutClass MBWMLayoutClass; + +struct MBWMLayout +{ + MBWMObject parent; + + MBWindowManager *wm; +}; + +struct MBWMLayoutClass +{ + MBWMObjectClass parent; + + void (*update) (MBWMLayout *layout); + + void (*layout_panels) (MBWMLayout *layout, MBGeometry *avail_geom); + void (*layout_input) (MBWMLayout *layout, MBGeometry *avail_geom); + void (*layout_free) (MBWMLayout *layout, MBGeometry *avail_geom); + void (*layout_fullscreen) (MBWMLayout *layout, MBGeometry *avail_geom); +}; + +int +mb_wm_layout_class_type (); + +MBWMLayout* +mb_wm_layout_new (MBWindowManager *wm); + +void +mb_wm_layout_update (MBWMLayout *layout); + +/* These are intended for use by subclasses of MBWMLayout */ + +#define SET_X (1<<1) +#define SET_Y (1<<2) +#define SET_WIDTH (1<<3) +#define SET_HEIGHT (1<<4) +#define SET_ALL (SET_X|SET_Y|SET_WIDTH|SET_HEIGHT) + +Bool +mb_wm_layout_maximise_geometry (MBGeometry *geom, + MBGeometry *max, + int flags); + +Bool +mb_wm_layout_clip_geometry (MBGeometry *geom, + MBGeometry *min, + int flags); + +#endif diff --git a/matchbox/mb-wm-macros.h b/matchbox/mb-wm-macros.h new file mode 100644 index 0000000..5c8ab4b --- /dev/null +++ b/matchbox/mb-wm-macros.h @@ -0,0 +1,104 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_MACROS_H +#define _HAVE_MB_MACROS_H + +#if MBWM_WANT_DEBUG +#define MBWM_NOTE(type,x,a...) do { \ + if (mbwm_debug_flags & MBWM_DEBUG_##type) \ + { fprintf (stderr, "[" #type "] " __FILE__ ":%d,%s() " ": " x "\n", __LINE__, __func__, ##a); } \ + } while (0); + +#define MBWM_MARK() MBWM_NOTE(MISC, "== mark ==") +#define MBWM_DBG(x, a...) MBWM_NOTE(MISC, x, ##a) + +#include <execinfo.h> + +#define _MBWM_TRACE(type,x,a...) \ +if (mbwm_debug_flags & MBWM_DEBUG_##type) \ +do \ +{ \ + void *trace[10]; \ + size_t depth, i; \ + char **strings; \ + \ + fprintf (stderr, __FILE__ ":%d,%s(): " x "\n", \ + __LINE__, __func__, ##a); \ + \ + depth = backtrace (trace, sizeof(trace)/sizeof(void*)); \ + strings = backtrace_symbols (trace, depth); \ + \ + for (i = 1; i < depth; ++i) \ + { \ + char * s = strings[i]; \ + while (s && *s && *s != '(') \ + s++; \ + \ + if (s && *s) \ + s++; \ + \ + fprintf (stderr, " %s\n", s); \ + } \ + free (strings); \ +}while (0) + +#define MBWM_TRACE() _MBWM_TRACE(TRACE, "### TRACE ###") +#define MBWM_TRACE_MSG(type,x,a...) _MBWM_TRACE(type, x, ##a) + +#else /* !MBWM_ENABLE_DEBUG */ + +#define MBWM_NOTE(type,x,a...) +#define MBWM_DBG(x, a...) +#define MBWM_TRACE() +#define MBWM_TRACE_MSG(type,x,a...) +#define MBWM_MARK() + +#endif /* MBWM_ENABLE_DEBUG */ + +#define MBWM_WANT_ASSERT 1 + +#if (MBWM_WANT_ASSERT) +#include <assert.h> +#define MBWM_ASSERT(x) assert(x) +#else +#define MBWM_ASSERT(x) do {} while (0) +#endif + +/* FIXME: ifdef this with compile time flag */ +#define mbwm_return_if_fail(expr) do { \ + if LIKELY(expr) { } else \ + { \ + mb_wm_util_warn (__FILE__ ":%d,%s() " ":" #expr "failed" ,\ + __LINE__, __func__); \ + return; \ + } \ + } while(0); + +#define mbwm_return_val_if_fail(expr,val) do { \ + if LIKELY(expr) { } else \ + { \ + mb_wm_util_warn (__FILE__ ":%d,%s() " ":" #expr "failed" ,\ + __LINE__, __func__); \ + return val; \ + } \ + } while(0); + +#endif diff --git a/matchbox/mb-wm-main-context.c b/matchbox/mb-wm-main-context.c new file mode 100644 index 0000000..71d5d6c --- /dev/null +++ b/matchbox/mb-wm-main-context.c @@ -0,0 +1,1116 @@ +#include "mb-wm-main-context.h" + +#include <sys/time.h> +#include <poll.h> +#include <limits.h> +#include <fcntl.h> + +#if ENABLE_COMPOSITE +#include <X11/extensions/Xdamage.h> +#endif + +#define MBWM_CTX_MAX_TIMEOUT 100 + +#if MBWM_WANT_DEBUG + +static const char *MBWMDEBUGEvents[] = { + "error", + "reply", + "KeyPress", + "KeyRelease", + "ButtonPress", + "ButtonRelease", + "MotionNotify", + "EnterNotify", + "LeaveNotify", + "FocusIn", + "FocusOut", + "KeymapNotify", + "Expose", + "GraphicsExpose", + "NoExpose", + "VisibilityNotify", + "CreateNotify", + "DestroyNotify", + "UnmapNotify", + "MapNotify", + "MapRequest", + "ReparentNotify", + "ConfigureNotify", + "ConfigureRequest", + "GravityNotify", + "ResizeRequest", + "CirculateNotify", + "CirculateRequest", + "PropertyNotify", + "SelectionClear", + "SelectionRequest", + "SelectionNotify", + "ColormapNotify", + "ClientMessage", + "MappingNotify", +}; + +#endif + +static Bool +mb_wm_main_context_check_timeouts (MBWMMainContext *ctx); + +static Bool +mb_wm_main_context_check_fd_watches (MBWMMainContext * ctx); + +static Bool +mb_wm_main_context_spin_xevent (MBWMMainContext *ctx); + +struct MBWMTimeOutEventInfo +{ + int ms; + MBWindowManagerTimeOutFunc func; + void *userdata; + unsigned long id; + struct timeval triggers; + +}; + +struct MBWMFdWatchInfo{ + MBWMIOChannel *channel; + MBWMIOCondition events; + MBWindowManagerFdWatchFunc func; + void *userdata; + unsigned long id; +}; + +static void +mb_wm_main_context_class_init (MBWMObjectClass *klass) +{ +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMMainContext"; +#endif +} + +static void +mb_wm_main_context_destroy (MBWMObject *this) +{ +} + +#if USE_GLIB_MAINLOOP +gboolean +mb_wm_main_context_gloop_xevent (gpointer userdata) +{ + MBWMMainContext * ctx = userdata; + MBWindowManager * wm = ctx->wm; + + while (mb_wm_main_context_spin_xevent (ctx)); + + if (wm->sync_type) + mb_wm_sync (wm); + + return TRUE; +} +#endif + +static int +mb_wm_main_context_init (MBWMObject *this, va_list vap) +{ + MBWMMainContext *ctx = MB_WM_MAIN_CONTEXT (this); + MBWindowManager *wm = NULL; + MBWMObjectProp prop; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + if (prop == MBWMObjectPropWm) + { + wm = va_arg(vap, MBWindowManager *); + break; + } + else + MBWMO_PROP_EAT (vap, prop); + + prop = va_arg (vap, MBWMObjectProp); + } + + ctx->wm = wm; + + return 1; +} + +int +mb_wm_main_context_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMMainContextClass), + sizeof (MBWMMainContext), + mb_wm_main_context_init, + mb_wm_main_context_destroy, + mb_wm_main_context_class_init + }; + type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); + } + + return type; +} + +MBWMMainContext* +mb_wm_main_context_new (MBWindowManager *wm) +{ + MBWMMainContext *ctx; + + ctx = MB_WM_MAIN_CONTEXT (mb_wm_object_new (MB_WM_TYPE_MAIN_CONTEXT, + MBWMObjectPropWm, wm, + NULL)); + + return ctx; +} + +Bool +mb_wm_main_context_handle_x_event (XEvent *xev, + MBWMMainContext *ctx) +{ + MBWindowManager *wm = ctx->wm; + MBWMList *iter; + Window xwin = xev->xany.window; + +#if (MBWM_WANT_DEBUG) + { + if (mbwm_debug_flags & MBWM_DEBUG_EVENT) + { + MBWindowManagerClient *ev_client; + + ev_client = mb_wm_managed_client_from_xwindow(wm, xev->xany.window); + + printf (" @ XEvent: '%s:%i' for %lx %s%s\n", + xev->type < sizeof (MBWMDEBUGEvents)/sizeof(MBWMDEBUGEvents[0]) + ? MBWMDEBUGEvents[xev->type] : "unknown", + xev->type, + xev->xany.window, + xev->xany.window == wm->root_win->xwindow ? "(root)" : "", + ev_client ? ev_client->name : "" + ); + } + } +#endif + +#define XE_ITER_GET_FUNC(i) (((MBWMXEventFuncInfo *)((i)->data))->func) +#define XE_ITER_GET_DATA(i) ((MBWMXEventFuncInfo *)((i)->data))->userdata +#define XE_ITER_GET_XWIN(i) ((MBWMXEventFuncInfo *)((i)->data))->xwindow + +#if ENABLE_COMPOSITE + if (xev->type == wm->damage_event_base + XDamageNotify) + { + iter = ctx->event_funcs.damage_notify; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWMXEventFunc)XE_ITER_GET_FUNC(iter) + (xev, XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + } + else +#endif + switch (xev->type) + { + case ClientMessage: + /* + * TODO -- perhaps this should not be special-cased. + */ + if (xev->xany.window == wm->root_win->xwindow || + ((XClientMessageEvent *)xev)->message_type == + wm->atoms[MBWM_ATOM_NET_ACTIVE_WINDOW] || + ((XClientMessageEvent *)xev)->message_type == + wm->atoms[MBWM_ATOM_NET_WM_STATE]) + { + mb_wm_root_window_handle_message (wm->root_win, + (XClientMessageEvent *)xev); + } + + iter = ctx->event_funcs.client_message; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWindowManagerClientMessageFunc)XE_ITER_GET_FUNC(iter) + ((XClientMessageEvent*)&xev->xclient, + XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + break; + case Expose: + break; + case MapRequest: + iter = ctx->event_funcs.map_request; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWindowManagerMapRequestFunc)XE_ITER_GET_FUNC(iter) + ((XMapRequestEvent*)&xev->xmaprequest, + XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + break; + case MapNotify: + iter = ctx->event_funcs.map_notify; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWindowManagerMapNotifyFunc)XE_ITER_GET_FUNC(iter) + ((XMapEvent*)&xev->xmap, + XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + break; + case UnmapNotify: +#if MBWM_WANT_DEBUG + if (mbwm_debug_flags & MBWM_DEBUG_EVENT) + { + XUnmapEvent * uev = & xev->xunmap; + printf (" window %x, event %x, %d\n", + uev->window, + uev->event, + uev->from_configure); + } +#endif + xwin = xev->xunmap.window; + iter = ctx->event_funcs.unmap_notify; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWindowManagerUnmapNotifyFunc)XE_ITER_GET_FUNC(iter) + ((XUnmapEvent*)&xev->xunmap, + XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + break; + case DestroyNotify: + iter = ctx->event_funcs.destroy_notify; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWindowManagerDestroyNotifyFunc)XE_ITER_GET_FUNC(iter) + ((XDestroyWindowEvent*)&xev->xdestroywindow, + XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + break; + case ConfigureNotify: +#if MBWM_WANT_DEBUG + if (mbwm_debug_flags & MBWM_DEBUG_EVENT) + { + XConfigureEvent * cev = & xev->xconfigure; + printf (" window %x, event %x, [%d,%d;%dx%d]\n", + cev->window, + cev->event, + cev->x, + cev->y, + cev->width, + cev->height); + } +#endif + xwin = xev->xconfigure.window; + iter = ctx->event_funcs.configure_notify; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWindowManagerConfigureNotifyFunc)XE_ITER_GET_FUNC(iter) + ((XConfigureEvent*)&xev->xconfigure, + XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + break; + case ConfigureRequest: +#if MBWM_WANT_DEBUG + if (mbwm_debug_flags & MBWM_DEBUG_EVENT) + { + XConfigureRequestEvent * cev = & xev->xconfigurerequest; + printf (" window %x, parent %x, [%d,%d;%dx%d]\n", + cev->window, + cev->parent, + cev->x, + cev->y, + cev->width, + cev->height); + } +#endif + xwin = xev->xconfigurerequest.window; + iter = ctx->event_funcs.configure_request; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWindowManagerConfigureRequestFunc)XE_ITER_GET_FUNC(iter) + ((XConfigureRequestEvent*)&xev->xconfigurerequest, + XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + break; + case KeyPress: + iter = ctx->event_funcs.key_press; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWindowManagerKeyPressFunc)XE_ITER_GET_FUNC(iter) + ((XKeyEvent*)&xev->xkey, + XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + break; + case PropertyNotify: +#if MBWM_WANT_DEBUG + if (mbwm_debug_flags & MBWM_DEBUG_EVENT) + { + XPropertyEvent * pev = & xev->xproperty; + char * prop = XGetAtomName (wm->xdpy, pev->atom); + printf (" window %x, prop %s, state %d\n", + pev->window, + prop, + pev->state); + + if (prop) + XFree (prop); + } +#endif + xwin = xev->xproperty.window; + iter = ctx->event_funcs.property_notify; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWindowManagerPropertyNotifyFunc)XE_ITER_GET_FUNC(iter) + ((XPropertyEvent*)&xev->xproperty, + XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + break; + case ButtonPress: + iter = ctx->event_funcs.button_press; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWindowManagerButtonPressFunc)XE_ITER_GET_FUNC(iter) + ((XButtonEvent*)&xev->xbutton, + XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + break; + case ButtonRelease: + iter = ctx->event_funcs.button_release; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWindowManagerButtonReleaseFunc)XE_ITER_GET_FUNC(iter) + ((XButtonEvent*)&xev->xbutton, + XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + break; + case MotionNotify: + iter = ctx->event_funcs.motion_notify; + + while (iter) + { + Window msg_xwin = XE_ITER_GET_XWIN(iter); + MBWMList * next = iter->next; + + if (msg_xwin == None || msg_xwin == xwin) + { + if (!(MBWindowManagerMotionNotifyFunc)XE_ITER_GET_FUNC(iter) + ((XMotionEvent*)&xev->xmotion, + XE_ITER_GET_DATA(iter))) + break; + } + + iter = next; + } + break; + } + + return False; +} + +static Bool +mb_wm_main_context_spin_xevent (MBWMMainContext *ctx) +{ + MBWindowManager * wm = ctx->wm; + XEvent xev; + + if (!XEventsQueued (wm->xdpy, QueuedAfterFlush)) + return False; + + XNextEvent(wm->xdpy, &xev); + + mb_wm_main_context_handle_x_event (&xev, ctx); + + return (XEventsQueued (wm->xdpy, QueuedAfterReading) != 0); +} + +static Bool +mb_wm_main_context_spin_xevent_blocking (MBWMMainContext *ctx) +{ + MBWindowManager * wm = ctx->wm; + XEvent xev; + + XNextEvent(wm->xdpy, &xev); + + mb_wm_main_context_handle_x_event (&xev, ctx); + + return (XEventsQueued (wm->xdpy, QueuedAfterReading) != 0); +} + +void +mb_wm_main_context_loop (MBWMMainContext *ctx) +{ +#if ! USE_GLIB_MAINLOOP + MBWindowManager * wm = ctx->wm; + + while (True) + { + Bool sources; + + sources = mb_wm_main_context_check_timeouts (ctx); + sources |= mb_wm_main_context_check_fd_watches (ctx); + + if (!sources) + { + /* No timeouts, idles, etc. -- wait for next + * X event + */ + mb_wm_main_context_spin_xevent_blocking (ctx); + } + else + { + /* Process any pending xevents */ + while (mb_wm_main_context_spin_xevent (ctx)); + } + + if (wm->sync_type) + mb_wm_sync (wm); + } +#endif +} + +Bool +mb_wm_main_context_spin_loop (MBWMMainContext *ctx) +{ +#if USE_GLIB_MAINLOOP + g_main_context_iteration (NULL, FALSE); + return g_main_context_pending (NULL); +#else + return mb_wm_main_context_spin_xevent (ctx); +#endif +} + + +unsigned long +mb_wm_main_context_x_event_handler_add (MBWMMainContext *ctx, + Window xwin, + int type, + MBWMXEventFunc func, + void *userdata) +{ + static unsigned long ids = 0; + MBWMXEventFuncInfo * func_info; +#if ENABLE_COMPOSITE + MBWindowManager * wm = ctx->wm; +#endif + + ++ids; + + func_info = mb_wm_util_malloc0(sizeof(MBWMXEventFuncInfo)); + func_info->func = func; + func_info->xwindow = xwin; + func_info->userdata = userdata; + func_info->id = ids; + +#if ENABLE_COMPOSITE + if (type == wm->damage_event_base + XDamageNotify) + { + ctx->event_funcs.damage_notify = + mb_wm_util_list_append (ctx->event_funcs.damage_notify, func_info); + } + else +#endif + switch (type) + { + case Expose: + break; + case MapRequest: + ctx->event_funcs.map_request = + mb_wm_util_list_append (ctx->event_funcs.map_request, func_info); + break; + case MapNotify: + ctx->event_funcs.map_notify= + mb_wm_util_list_append (ctx->event_funcs.map_notify, func_info); + break; + case UnmapNotify: + ctx->event_funcs.unmap_notify= + mb_wm_util_list_append (ctx->event_funcs.unmap_notify, func_info); + break; + case DestroyNotify: + ctx->event_funcs.destroy_notify = + mb_wm_util_list_append (ctx->event_funcs.destroy_notify, func_info); + break; + case ConfigureNotify: + ctx->event_funcs.configure_notify = + mb_wm_util_list_append (ctx->event_funcs.configure_notify, func_info); + break; + case ConfigureRequest: + ctx->event_funcs.configure_request = + mb_wm_util_list_append (ctx->event_funcs.configure_request, func_info); + break; + case KeyPress: + ctx->event_funcs.key_press = + mb_wm_util_list_append (ctx->event_funcs.key_press, func_info); + break; + case PropertyNotify: + ctx->event_funcs.property_notify = + mb_wm_util_list_append (ctx->event_funcs.property_notify, func_info); + break; + case ButtonPress: + ctx->event_funcs.button_press = + mb_wm_util_list_append (ctx->event_funcs.button_press, func_info); + break; + case ButtonRelease: + ctx->event_funcs.button_release = + mb_wm_util_list_append (ctx->event_funcs.button_release, func_info); + break; + case MotionNotify: + ctx->event_funcs.motion_notify = + mb_wm_util_list_append (ctx->event_funcs.motion_notify, func_info); + break; + case ClientMessage: + ctx->event_funcs.client_message = + mb_wm_util_list_append (ctx->event_funcs.client_message, func_info); + break; + + default: + break; + } + + return ids; +} + +void +mb_wm_main_context_x_event_handler_remove (MBWMMainContext *ctx, + int type, + unsigned long id) +{ + MBWMList * l = NULL; + MBWMList **l_start; + +#if ENABLE_COMPOSITE + MBWindowManager * wm = ctx->wm; + + if (type == wm->damage_event_base + XDamageNotify) + { + l_start = &ctx->event_funcs.damage_notify; + } + else +#endif + switch (type) + { + case Expose: + break; + case MapRequest: + l_start = &ctx->event_funcs.map_request; + break; + case MapNotify: + l_start = &ctx->event_funcs.map_notify; + break; + case UnmapNotify: + l_start = &ctx->event_funcs.unmap_notify; + break; + case DestroyNotify: + l_start = &ctx->event_funcs.destroy_notify; + break; + case ConfigureNotify: + l_start = &ctx->event_funcs.configure_notify; + break; + case ConfigureRequest: + l_start = &ctx->event_funcs.configure_request; + break; + case KeyPress: + l_start = &ctx->event_funcs.key_press; + break; + case PropertyNotify: + l_start = &ctx->event_funcs.property_notify; + break; + case ButtonPress: + l_start = &ctx->event_funcs.button_press; + break; + case ButtonRelease: + l_start = &ctx->event_funcs.button_release; + break; + case MotionNotify: + l_start = &ctx->event_funcs.motion_notify; + break; + case ClientMessage: + l_start = &ctx->event_funcs.client_message; + break; + + default: + break; + } + + l = *l_start; + + while (l) + { + MBWMXEventFuncInfo * info = l->data; + + if (info->id == id) + { + MBWMList * prev = l->prev; + MBWMList * next = l->next; + + if (prev) + prev->next = next; + else + *l_start = next; + + if (next) + next->prev = prev; + + free (info); + free (l); + + return; + } + + l = l->next; + } +} + +#if ! USE_GLIB_MAINLOOP +static void +mb_wm_main_context_timeout_setup (MBWMTimeOutEventInfo * tinfo, + struct timeval * current_time) +{ + int sec = tinfo->ms / 1000; + int usec = (tinfo->ms - sec *1000) * 1000; + + sec += current_time->tv_sec; + usec += current_time->tv_usec; + + if (usec >= 1000000) + { + usec -= 1000000; + sec++; + } + + tinfo->triggers.tv_sec = sec; + tinfo->triggers.tv_usec = usec; +} + +static Bool +mb_wm_main_context_handle_timeout (MBWMTimeOutEventInfo *tinfo, + struct timeval *current_time) +{ + if (tinfo->triggers.tv_sec < current_time->tv_sec || + (tinfo->triggers.tv_sec == current_time->tv_sec && + tinfo->triggers.tv_usec <= current_time->tv_usec)) + { + if (!tinfo->func (tinfo->userdata)) + return False; + + mb_wm_main_context_timeout_setup (tinfo, current_time); + } + + return True; +} + +/* + * Returns false if no timeouts are present + */ +static Bool +mb_wm_main_context_check_timeouts (MBWMMainContext *ctx) +{ + MBWMList * l = mb_wm_util_list_get_first(ctx->event_funcs.timeout); + struct timeval current_time; + + if (!l) + return False; + + gettimeofday (¤t_time, NULL); + + while (l) + { + MBWMTimeOutEventInfo * tinfo = l->data; + unsigned long tid = tinfo->id; + + if (!mb_wm_main_context_handle_timeout (tinfo, ¤t_time)) + { + /* Timeout handler notified it can be removed, do so now */ + mb_wm_main_context_timeout_handler_remove (ctx,tid); + /* To avoid race condition, restart at front of list */ + l = mb_wm_util_list_get_first(ctx->event_funcs.timeout); + } + else + l = mb_wm_util_list_next(l); + } + return True; +} +#endif /* !USE_GLIB_MAINLOOP */ + +unsigned long +mb_wm_main_context_timeout_handler_add (MBWMMainContext *ctx, + int ms, + MBWindowManagerTimeOutFunc func, + void *userdata) +{ +#if ! USE_GLIB_MAINLOOP + static unsigned long ids = 0; + MBWMTimeOutEventInfo * tinfo; + struct timeval current_time; + + ++ids; + + tinfo = mb_wm_util_malloc0 (sizeof (MBWMTimeOutEventInfo)); + tinfo->func = func; + tinfo->id = ids; + tinfo->ms = ms; + tinfo->userdata = userdata; + + gettimeofday (¤t_time, NULL); + mb_wm_main_context_timeout_setup (tinfo, ¤t_time); + + ctx->event_funcs.timeout = + mb_wm_util_list_append (ctx->event_funcs.timeout, tinfo); + + return ids; + +#else + return g_timeout_add (ms, func, userdata); +#endif +} + +void +mb_wm_main_context_timeout_handler_remove (MBWMMainContext *ctx, + unsigned long id) +{ +#if ! USE_GLIB_MAINLOOP + MBWMList * l = ctx->event_funcs.timeout; + + while (l) + { + MBWMTimeOutEventInfo * info = l->data; + + if (info->id == id) + { + /* Reset list head after entry removal */ + ctx->event_funcs.timeout = + mb_wm_util_list_remove(ctx->event_funcs.timeout, l->data); + return; + } + + l = mb_wm_util_list_next(l); + } +#else + g_source_remove (id); +#endif +} + +unsigned long +mb_wm_main_context_fd_watch_add (MBWMMainContext *ctx, + MBWMIOChannel *channel, + MBWMIOCondition events, + MBWindowManagerFdWatchFunc func, + void *userdata) +{ +#if ! USE_GLIB_MAINLOOP + static unsigned long ids = 0; + MBWMFdWatchInfo * finfo; + struct pollfd * fds; + + ++ids; + + finfo = mb_wm_util_malloc0 (sizeof (MBWMFdWatchInfo)); + finfo->func = func; + finfo->id = ids; + finfo->channel = channel; + finfo->events = events; + finfo->userdata = userdata; + + ctx->event_funcs.fd_watch = + mb_wm_util_list_append (ctx->event_funcs.fd_watch, finfo); + + ctx->n_poll_fds++; + ctx->poll_fds = realloc (ctx->poll_fds, sizeof (struct pollfd)); + + fds = ctx->poll_fds + (ctx->n_poll_fds - 1); + fds->fd = *channel; + fds->events = events; + + return ids; + +#else + return g_io_add_watch (channel, events, func, userdata); +#endif +} + +void +mb_wm_main_context_fd_watch_remove (MBWMMainContext *ctx, + unsigned long id) +{ +#if ! USE_GLIB_MAINLOOP + MBWMList * l = ctx->event_funcs.fd_watch; + + while (l) + { + MBWMFdWatchInfo * info = l->data; + + if (info->id == id) + { + MBWMList * prev = l->prev; + MBWMList * next = l->next; + + if (prev) + prev->next = next; + else + ctx->event_funcs.fd_watch = next; + + if (next) + next->prev = prev; + + free (info); + free (l); + + return; + } + + l = l->next; + } + + ctx->n_poll_fds--; + ctx->poll_cache_dirty = True; +#else + g_source_remove (id); +#endif +} + +MBWMIOChannel * +mb_wm_main_context_io_channel_new (int fd) +{ +#if ! USE_GLIB_MAINLOOP + MBWMIOChannel * c = mb_wm_util_malloc0 (sizeof (MBWMIOChannel)); + *c = fd; + return c; +#else + return g_io_channel_unix_new (fd); +#endif +} + + +void +mb_wm_main_context_io_channel_destroy (MBWMIOChannel * channel) +{ +#if ! USE_GLIB_MAINLOOP + if (channel) + free (channel); +#else + g_io_channel_unref (channel); +#endif +} + +int +mb_wm_main_context_io_channel_get_fd (MBWMIOChannel * channel) +{ +#if ! USE_GLIB_MAINLOOP + return *channel; +#else + g_io_channel_unix_get_fd (channel); +#endif +} + +#if ! USE_GLIB_MAINLOOP +static void +mb_wm_main_context_setup_poll_cache (MBWMMainContext *ctx) +{ + MBWMList *l = ctx->event_funcs.fd_watch; + int i = 0; + + if (!ctx->poll_cache_dirty) + return; + + ctx->poll_fds = realloc (ctx->poll_fds, ctx->n_poll_fds); + + while (l) + { + MBWMFdWatchInfo *info = l->data; + + ctx->poll_fds[i].fd = *(info->channel); + ctx->poll_fds[i].events = info->events; + + l = l->next; + ++i; + } + + ctx->poll_cache_dirty = False; +} + +static Bool +mb_wm_main_context_check_fd_watches (MBWMMainContext * ctx) +{ + int ret; + int i = 0; + MBWMList * l = ctx->event_funcs.fd_watch; + Bool removal = False; + + if (!ctx->n_poll_fds) + return False; + + mb_wm_main_context_setup_poll_cache (ctx); + + ret = poll (ctx->poll_fds, ctx->n_poll_fds, 0); + + if (ret < 0) + { + MBWM_DBG ("Poll failed."); + return True; + } + + if (ret == 0) + return True; + + while (l) + { + MBWMFdWatchInfo *info = l->data; + + if (ctx->poll_fds[i].revents & ctx->poll_fds[i].events) + { + Bool zap = !info->func (info->channel, ctx->poll_fds[i].revents, + info->userdata); + + if (zap) + { + MBWMList * prev = l->prev; + MBWMList * next = l->next; + + if (prev) + prev->next = next; + else + ctx->event_funcs.fd_watch = next; + + if (next) + next->prev = prev; + + free (info); + free (l); + + ctx->n_poll_fds--; + + removal = True; + + l = next; + } + else + l = l->next; + } + else + l = l->next; + + ++i; + } + + ctx->poll_cache_dirty = removal; + + return True; +} +#endif diff --git a/matchbox/mb-wm-main-context.h b/matchbox/mb-wm-main-context.h new file mode 100644 index 0000000..f022f81 --- /dev/null +++ b/matchbox/mb-wm-main-context.h @@ -0,0 +1,150 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_MAIN_CONTEXT_H +#define _HAVE_MB_MAIN_CONTEXT_H + +#include <matchbox/mb-wm-object.h> +#include <matchbox/mb-window-manager.h> +#include <poll.h> + +#define MB_WM_MAIN_CONTEXT(c) ((MBWMMainContext*)(c)) +#define MB_WM_MAIN_CONTEXT_CLASS(c) ((MBWMMainContextClass*)(c)) +#define MB_WM_TYPE_MAIN_CONTEXT (mb_wm_main_context_class_type ()) +#define MB_WM_IS_MAIN_CONTEXT(c) (MB_WM_OBJECT_TYPE(c)==MB_WM_TYPE_MAIN_CONTEXT) + +/* XXX: we have a circular dependency between mb-wm-main-context.h + * and mb-window-manager.h */ +#ifndef MB_WM_MAIN_CONTEXT_TYPEDEF_DEFINED +typedef struct MBWMMainContext MBWMMainContext; +#define MB_WM_MAIN_CONTEXT_TYPEDEF_DEFINED +#endif +typedef struct MBWMMainContextClass MBWMMainContextClass; + +typedef Bool (*MBWMMainContextXEventFunc) (XEvent * xev, void * userdata); + +typedef struct MBWMEventFuncs +{ + /* FIXME: figure our X wrap / unwrap mechanism */ + MBWMList *map_notify; + MBWMList *unmap_notify; + MBWMList *map_request; + MBWMList *destroy_notify; + MBWMList *configure_request; + MBWMList *configure_notify; + MBWMList *key_press; + MBWMList *property_notify; + MBWMList *button_press; + MBWMList *button_release; + MBWMList *motion_notify; + MBWMList *client_message; + +#if ENABLE_COMPOSITE + MBWMList *damage_notify; +#endif + +#if ! USE_GLIB_MAINLOOP + MBWMList *timeout; + MBWMList *fd_watch; +#endif +} +MBWMEventFuncs; + +struct MBWMMainContext +{ + MBWMObject parent; + + MBWindowManager *wm; + + MBWMEventFuncs event_funcs; + struct pollfd *poll_fds; + int n_poll_fds; + Bool poll_cache_dirty; +}; + +struct MBWMMainContextClass +{ + MBWMObjectClass parent; +}; + +int +mb_wm_main_context_class_type (); + +MBWMMainContext* +mb_wm_main_context_new(MBWindowManager *wm); + +unsigned long +mb_wm_main_context_x_event_handler_add (MBWMMainContext *ctx, + Window xwin, + int type, + MBWMXEventFunc func, + void *userdata); + +void +mb_wm_main_context_x_event_handler_remove (MBWMMainContext *ctx, + int type, + unsigned long id); + +unsigned long +mb_wm_main_context_timeout_handler_add (MBWMMainContext *ctx, + int ms, + MBWindowManagerTimeOutFunc func, + void *userdata); + +void +mb_wm_main_context_timeout_handler_remove (MBWMMainContext *ctx, + unsigned long id); + +MBWMIOChannel * +mb_wm_main_context_io_channel_new (int fd); + +void +mb_wm_main_context_io_channel_destroy (MBWMIOChannel * channel); + +int +mb_wm_main_context_io_channel_get_fd (MBWMIOChannel * channel); + +unsigned long +mb_wm_main_context_fd_watch_add (MBWMMainContext *ctx, + MBWMIOChannel *channel, + MBWMIOCondition events, + MBWindowManagerFdWatchFunc func, + void *userdata); + +void +mb_wm_main_context_fd_watch_remove (MBWMMainContext *ctx, + unsigned long id); + +#if USE_GLIB_MAINLOOP +gboolean +mb_wm_main_context_gloop_xevent (gpointer userdata); +#endif + +Bool +mb_wm_main_context_handle_x_event (XEvent *xev, + MBWMMainContext *ctx); + +void +mb_wm_main_context_loop (MBWMMainContext *ctx); + +Bool +mb_wm_main_context_spin_loop (MBWMMainContext *ctx); + +#endif diff --git a/matchbox/mb-wm-object-props.h b/matchbox/mb-wm-object-props.h new file mode 100644 index 0000000..c0c46ac --- /dev/null +++ b/matchbox/mb-wm-object-props.h @@ -0,0 +1,104 @@ + +#ifndef _HAVE_MB_OBJECT_PROPS_H +#define _HAVE_MB_OBJECT_PROPS_H + +#include <matchbox/mb-wm-types.h> + +/* + * MBWMObject construction properties + * + * NB: the properties are only used at construction time, being passed to + * mb_wm_object_new (); they cannot be set or queried subsequently. + * + * Property ids are numerical; this allows us (a) to avoid excessive use of + * strcmp during object creation (passing 6 properties to the constructor + * would result in each _init function doing 21 strcmp() call to retrieve it's + * values), and (b) handle unknown properties safely. + * + * Property arguments can only be of the following types: + * + * int, + * long, + * long long, + * void*, + * + * plus their unsigned variants. + * + * The property id is a 32-bit value, constructed as follows: + * + * Bits 31-4 : numerical id that uniquely identifies this property. Bits 31-24 + * are reserved for private properties of any objects built out of + * tree to avoid clashing with default properties; with default + * properties bits 31-24 are always 0 (_MBWMObjectPropLastGlobal + * represent the highest numerical id of a default property). + * + * Bits 3-0 : Size of the property argument, as returned by sizeof(). + * + * Since properties always come in id-value pairs, when an object _init() + * function encounters a property it does not know, it needs to eat the + * argument, the MBWMO_PROP_EAT() macro is provided for this purpose. + */ +#define _MKOPROP(n, type) (((1<<4)+(n<<4))|sizeof(type)) + +#define MBWMO_PROP_EAT(_ovap, prop) \ +do \ +{ \ + int size = (prop & 0x0000000f); \ + \ + if (size == sizeof (int)) \ + va_arg (_ovap, int); \ + else if (size == sizeof (void *)) \ + va_arg (_ovap, void *); \ + else if (size == sizeof (long)) \ + va_arg (_ovap, long); \ + else if (size == sizeof (long long)) \ + va_arg (_ovap, long long); \ +}while (0) + +typedef enum MBWMObjectProp + { + MBWMObjectPropWidth = _MKOPROP(0, int), + MBWMObjectPropHeight = _MKOPROP(1, int), + MBWMObjectPropXwindow = _MKOPROP(2, Window), + MBWMObjectPropArgc = _MKOPROP(3, int), + MBWMObjectPropArgv = _MKOPROP(4, void*), + MBWMObjectPropWm = _MKOPROP(5, void*), + MBWMObjectPropClient = _MKOPROP(6, void*), + MBWMObjectPropClientWindow = _MKOPROP(7, void*), + + MBWMObjectPropDecor = _MKOPROP(8, void*), + MBWMObjectPropDecorType = _MKOPROP(9, MBWMDecorType), + MBWMObjectPropDecorUserData = _MKOPROP(10, void*), + MBWMObjectPropDecorAbsolutePacking = _MKOPROP(11, int), + + MBWMObjectPropDecorButtonRepaintFunc = _MKOPROP(12, void*), + MBWMObjectPropDecorButtonPressedFunc = _MKOPROP(13, void*), + MBWMObjectPropDecorButtonReleasedFunc = _MKOPROP(14, void*), + MBWMObjectPropDecorButtonFlags = _MKOPROP(15, MBWMDecorButtonFlags), + MBWMObjectPropDecorButtonType = _MKOPROP(16, int), + MBWMObjectPropDecorButtonPack = _MKOPROP(17, int), + + MBWMObjectPropThemePath = _MKOPROP(18, void*), + MBWMObjectPropThemeImg = _MKOPROP(19, void*), + MBWMObjectPropThemeXmlClients = _MKOPROP(20, void*), + MBWMObjectPropThemeColorLowlight = _MKOPROP(21, void*), + MBWMObjectPropThemeColorShadow = _MKOPROP(22, void*), + MBWMObjectPropThemeShadowType = _MKOPROP(23, int), + MBWMObjectPropThemeCompositing = _MKOPROP(24, int), + MBWMObjectPropThemeShaped = _MKOPROP(25, int), + + MBWMObjectPropCompMgrEffectType = _MKOPROP(26, int), + MBWMObjectPropCompMgrEffectDuration = _MKOPROP(27, unsigned long), + MBWMObjectPropCompMgrEffectGravity = _MKOPROP(28, unsigned long), + MBWMObjectPropCompMgrClutterEffectTimeline = _MKOPROP(29, void*), + MBWMObjectPropCompMgrClutterEffectBehaviour = _MKOPROP(30, void*), + + MBWMObjectPropDpy = _MKOPROP(31, void*), + + _MBWMObjectPropLastGlobal = 0x00fffff0, + } +MBWMObjectProp; + +#undef _MKOPROP + +#endif diff --git a/matchbox/mb-wm-object.c b/matchbox/mb-wm-object.c new file mode 100644 index 0000000..63f4ffc --- /dev/null +++ b/matchbox/mb-wm-object.c @@ -0,0 +1,482 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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-config.h" +#include "mb-wm-object.h" +#include "mb-wm-util.h" +#include "mb-wm-macros.h" + +#if MBWM_WANT_DEBUG +#include <execinfo.h> +#endif +#include <stdlib.h> +#include <string.h> + +static MBWMObjectClassInfo **ObjectClassesInfo = NULL; +static MBWMObjectClass **ObjectClasses = NULL; +static int ObjectClassesAllocated = 0; +static int NObjectClasses = 0; + +#if MBWM_WANT_DEBUG +#define MBWM_OBJECT_TRACE_DEPTH 3 +/* + * Increased for each ref call and decreased for each unref call + */ +MBWMList *alloc_objects = NULL; + +void +mb_wm_object_dump () +{ + MBWMList * l = alloc_objects; + + if (!l) + { + fprintf (stderr, "=== There currently are no allocated objects === \n"); + return; + } + + fprintf (stderr, "=== Currently allocated objects === \n"); + + while (l) + { + int i; + MBWMObject * o = l->data; + const MBWMObjectClass * k = MB_WM_OBJECT_GET_CLASS (o); + + fprintf (stderr, "Object of type %s, allocated from:\n", + k->klass_name); + + + for (i = 1; i < MBWM_OBJECT_TRACE_DEPTH; ++i) + { + char * s = o->trace_strings[i]; + while (s && *s && *s != '(') + s++; + + fprintf (stderr, " %s\n", s); + } + + l = l->next; + } + + fprintf (stderr, "=== Currently allocated objects end === \n"); + +} + +#endif + +#define N_CLASSES_PREALLOC 10 +#define N_CLASSES_REALLOC_STEP 5 + +void +mb_wm_object_init(void) +{ + ObjectClasses = mb_wm_util_malloc0 (sizeof(void*) * N_CLASSES_PREALLOC); + ObjectClassesInfo = mb_wm_util_malloc0 (sizeof(void*) * N_CLASSES_PREALLOC); + + if (ObjectClasses && ObjectClassesInfo) + ObjectClassesAllocated = N_CLASSES_PREALLOC; +} + +static void +mb_wm_object_class_init_recurse (MBWMObjectClass *klass, + MBWMObjectClass *parent) +{ + if (parent->parent) + mb_wm_object_class_init_recurse (klass, parent->parent); + + if (parent->class_init) + parent->class_init (klass); +} + +static void +mb_wm_object_class_init (MBWMObjectClass *klass) +{ + if (klass->parent) + mb_wm_object_class_init_recurse (klass, klass->parent); + + if (klass->class_init) + klass->class_init (klass); +} + +int +mb_wm_object_register_class (MBWMObjectClassInfo *info, + int parent_type, + int flags) +{ + MBWMObjectClass *klass; + + if (NObjectClasses >= ObjectClassesAllocated) + { + int byte_len; + int new_offset; + int new_byte_len; + + new_offset = ObjectClassesAllocated; + ObjectClassesAllocated += N_CLASSES_REALLOC_STEP; + + byte_len = sizeof(void *) * (ObjectClassesAllocated); + new_byte_len = sizeof(void *) * (ObjectClassesAllocated - new_offset); + + ObjectClasses = realloc (ObjectClasses, byte_len); + ObjectClassesInfo = realloc (ObjectClassesInfo, byte_len); + + if (!ObjectClasses || !ObjectClassesInfo) + return 0; + + memset (ObjectClasses + new_offset , 0, new_byte_len); + memset (ObjectClassesInfo + new_offset, 0, new_byte_len); + } + + ObjectClassesInfo[NObjectClasses] = info; + + klass = mb_wm_util_malloc0(info->klass_size); + klass->init = info->instance_init; + klass->destroy = info->instance_destroy; + klass->class_init = info->class_init; + klass->type = NObjectClasses + 1; + + if (parent_type != 0) + klass->parent = ObjectClasses[parent_type-1]; + + ObjectClasses[NObjectClasses] = klass; + + mb_wm_object_class_init (klass); + + return 1 + NObjectClasses++; +} + +void * +mb_wm_object_ref (MBWMObject *this) +{ + if (!this) + { + MBWM_DBG("### Warning: called with NULL ###"); + return this; + } + + this->refcnt++; + + MBWM_TRACE_MSG (OBJ_REF, "### REF ###"); + + return this; +} + +static void +mb_wm_object_destroy_recursive (const MBWMObjectClass * klass, + MBWMObject *this) +{ + /* Destruction needs to happen top to bottom */ + MBWMObjectClass *parent_klass = klass->parent; + + if (klass->destroy) + klass->destroy (this); + + if (parent_klass) + mb_wm_object_destroy_recursive (parent_klass, this); +} + +void +mb_wm_object_unref (MBWMObject *this) +{ + if (!this) + { + MBWM_DBG("### Warning: called with NULL ###"); + return; + } + + + this->refcnt--; + + if (this->refcnt == 0) + { + MBWM_NOTE (OBJ_UNREF, "=== DESTROYING OBJECT type %d ===", + this->klass->type); + + mb_wm_object_destroy_recursive (MB_WM_OBJECT_GET_CLASS (this), + this); + + free (this); + +#if MBWM_WANT_DEBUG + alloc_objects = mb_wm_util_list_remove (alloc_objects, this); +#endif + } +} + +static int +mb_wm_object_init_recurse (MBWMObject *obj, MBWMObjectClass *parent, + va_list vap) +{ + va_list vap2; + + va_copy (vap2, vap); + + if (parent->parent) + if (!mb_wm_object_init_recurse (obj, parent->parent, vap2)) + return 0; + + if (parent->init) + if (!parent->init (obj, vap)) + return 0; + + va_end (vap2); + + return 1; +} + +static int +mb_wm_object_init_object (MBWMObject *obj, va_list vap) +{ + va_list vap2; + + va_copy(vap2, vap); + + if (obj->klass->parent) + if (!mb_wm_object_init_recurse (obj, obj->klass->parent, vap2)) + return 0; + + if (obj->klass->init) + if (!obj->klass->init(obj, vap)) + return 0; + + va_end(vap2); + + return 1; +} + + +MBWMObject* +mb_wm_object_new (int type, ...) +{ + MBWMObjectClassInfo *info; + MBWMObject *obj; + va_list vap; + + va_start(vap, type); + + info = ObjectClassesInfo[type-1]; + + obj = mb_wm_util_malloc0 (info->instance_size); + + obj->klass = MB_WM_OBJECT_CLASS(ObjectClasses[type-1]); + + if (!mb_wm_object_init_object (obj, vap)) + { + free (obj); + return NULL; + } + + + mb_wm_object_ref (obj); + + va_end(vap); + +#if MBWM_WANT_DEBUG + { + void * trace[MBWM_OBJECT_TRACE_DEPTH]; + + alloc_objects = mb_wm_util_list_append (alloc_objects, obj); + obj->trace_depth = backtrace (trace, sizeof(trace)/sizeof(void*)); + obj->trace_strings = backtrace_symbols (trace, obj->trace_depth); + } +#endif + + return obj; +} + +const MBWMObjectClass* +mb_wm_object_get_class (MBWMObject *this) +{ + return this->klass; +} + +unsigned long +mb_wm_object_signal_connect (MBWMObject *obj, + unsigned long signal, + MBWMObjectCallbackFunc func, + void *userdata) +{ + static unsigned long id_counter = 0; + MBWMFuncInfo *func_info; + + MBWM_ASSERT(func != NULL); + + func_info = mb_wm_util_malloc0(sizeof(MBWMFuncInfo)); + func_info->func = (void*)func; + func_info->userdata = userdata; + func_info->data = mb_wm_object_ref (obj); + func_info->signal = signal; + func_info->id = id_counter++; + + obj->callbacks = + mb_wm_util_list_append (obj->callbacks, func_info); + + return func_info->id; +} + +void +mb_wm_object_signal_disconnect (MBWMObject *obj, + unsigned long id) +{ + MBWMList *item = obj->callbacks; + + while (item) + { + MBWMFuncInfo* info = item->data; + + if (info->id == id) + { + MBWMList * prev = item->prev; + MBWMList * next = item->next; + + if (prev) + prev->next = next; + else + obj->callbacks = next; + + if (next) + next->prev = prev; + + mb_wm_object_unref (MB_WM_OBJECT (info->data)); + + free (info); + free (item); + + return; + } + + item = item->next; + } + + MBWM_DBG ("### Warning: did not find signal handler %d ###", id); +} + +void +mb_wm_object_signal_emit (MBWMObject *obj, + unsigned long signal) +{ + MBWMList *item = obj->callbacks; + + while (item) + { + MBWMFuncInfo* info = item->data; + + if (info->signal & signal) + { + if (((MBWMObjectCallbackFunc)info->func) (obj, + signal, + info->userdata)) + { + break; + } + } + + item = item->next; + } +} + +#if 0 + +/* ----- Test code -------- */ + +typedef struct Foo +{ + MBWMObject parent; + + int hello; +} +Foo; + +typedef struct FooClass +{ + MBWMObjectClass parent; + +} +FooClass; + +void +mb_wm_foo_init (MBWMObject *obj) +{ + printf("%s() called\n", __func__); +} + +void +mb_wm_foo_destroy (MBWMObject *obj) +{ + printf("%s() called\n", __func__); +} + +int +mb_wm_foo_get_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (FooClass), + sizeof (Foo), /* Instance */ + mb_wm_foo_init, + mb_wm_foo_destroy, + NULL + }; + + type = mb_wm_object_register_class (&info); + + printf("type: %i\n", type); + } + + return type; +} + +Foo* +mb_wm_foo_new (int val) +{ + Foo *foo; + + foo = (Foo*)mb_wm_object_new (mb_wm_foo_get_class_type ()); + + /* call init */ + + foo->hello = val; + + return foo; +} + + +int +main (int argc, char **argv) +{ + Foo *foo, *foo2; + + mb_wm_object_init(); + + printf("%s() called init, about to call new\n", __func__); + + foo = mb_wm_foo_new (10); + foo2 = mb_wm_foo_new (10); + + printf("%s() foo->hello is %i\n", __func__, foo->hello); + + mb_wm_object_unref (MB_WM_OBJECT(foo)); +} + +#endif diff --git a/matchbox/mb-wm-object.h b/matchbox/mb-wm-object.h new file mode 100644 index 0000000..336558f --- /dev/null +++ b/matchbox/mb-wm-object.h @@ -0,0 +1,130 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_OBJECT_H +#define _HAVE_MB_OBJECT_H + +#include <stdarg.h> +#include <matchbox/mb-wm-object-props.h> + +typedef struct MBWMObject MBWMObject; +typedef struct MBWMObjectClass MBWMObjectClass; + +typedef void (*MBWMObjFunc) (MBWMObject* obj); +typedef int (*MBWMObjVargFunc) (MBWMObject* obj, va_list vap); +typedef void (*MBWMClassFunc) (MBWMObjectClass* klass); + +#define MB_WM_TYPE_OBJECT 0 +#define MB_WM_OBJECT(x) ((MBWMObject*)(x)) +#define MB_WM_OBJECT_CLASS(x) ((MBWMObjectClass*)(x)) +#define MB_WM_OBJECT_TYPE(x) (((MBWMObject*)(x))->klass->type) +#define MB_WM_OBJECT_GET_CLASS(x) (mb_wm_object_get_class (MB_WM_OBJECT(x))) +#define MB_WM_OBJECT_GET_PARENT_CLASS(x) \ + ((mb_wm_object_get_class (MB_WM_OBJECT(x)))->parent) + +typedef enum MBWMObjectClassType +{ + MB_WM_OBJECT_TYPE_CLASS = 0, + MB_WM_OBJECT_TYPE_ABSTRACT, + MB_WM_OBJECT_TYPE_SINGLETON +} +MBWMObjectClassType; + +typedef struct MBWMObjectClassInfo +{ + size_t klass_size; + size_t instance_size; + MBWMObjVargFunc instance_init; + MBWMObjFunc instance_destroy; + MBWMClassFunc class_init; +} +MBWMObjectClassInfo; + +struct MBWMObjectClass +{ + int type; + MBWMObjectClass *parent; + MBWMObjVargFunc init; + MBWMObjFunc destroy; + MBWMClassFunc class_init; + +#if MBWM_WANT_DEBUG + const char *klass_name; +#endif +}; + +struct MBWMObject +{ + MBWMObjectClass *klass; + int refcnt; + + MBWMList *callbacks; + +#if MBWM_WANT_DEBUG + char **trace_strings; + int trace_depth; +#endif +}; + +/* returns True to stop signal emission */ +typedef Bool (*MBWMObjectCallbackFunc) (MBWMObject *obj, + int mask, + void *userdata); + + +void +mb_wm_object_init(void); + +int +mb_wm_object_register_class (MBWMObjectClassInfo *info, + int parent_type, + int flags); + +void * +mb_wm_object_ref (MBWMObject *this); + +void +mb_wm_object_unref (MBWMObject *this); + +MBWMObject* +mb_wm_object_new (int type, ...); + +const MBWMObjectClass* +mb_wm_object_get_class (MBWMObject *this); + +unsigned long +mb_wm_object_signal_connect (MBWMObject *obj, + unsigned long signal, + MBWMObjectCallbackFunc func, + void *userdata); + +void +mb_wm_object_signal_disconnect (MBWMObject *obj, + unsigned long id); + +void +mb_wm_object_signal_emit (MBWMObject *obj, unsigned long signal); + +#if MBWM_WANT_DEBUG +void +mb_wm_object_dump (); +#endif + +#endif diff --git a/matchbox/mb-wm-props.c b/matchbox/mb-wm-props.c new file mode 100644 index 0000000..fbf538a --- /dev/null +++ b/matchbox/mb-wm-props.c @@ -0,0 +1,202 @@ +#include "matchbox.h" +#include "xas.h" + +MBWMCookie +mb_wm_property_req (MBWindowManager *wm, + Window win, + Atom property, + long offset, + long length, + Bool delete, + Atom req_type) +{ + XasCookie cookie; + + cookie = xas_get_property(wm->xas_context, + win, + property, + offset, + length, + delete, + req_type); + + return (MBWMCookie)cookie; +} + + +Status +mb_wm_property_reply (MBWindowManager *wm, + MBWMCookie cookie, + Atom *actual_type_return, + int *actual_format_return, + unsigned long *nitems_return, + unsigned long *bytes_after_return, + unsigned char **prop_return, + int *x_error_code) +{ + return xas_get_property_reply(wm->xas_context, + (XasCookie)cookie, + actual_type_return, + actual_format_return, + nitems_return, + bytes_after_return, + prop_return, + x_error_code); +} + +void* +mb_wm_property_get_reply_and_validate (MBWindowManager *wm, + MBWMCookie cookie, + Atom expected_type, + int expected_format, + int expected_n_items, + int *n_items_ret, + int *x_error_code) +{ + Atom actual_type_return; + int actual_format_return; + unsigned long nitems_return; + unsigned long bytes_after_return; + unsigned char *prop_data = NULL; + + *x_error_code = 0; + + xas_get_property_reply(wm->xas_context, + (XasCookie)cookie, + &actual_type_return, + &actual_format_return, + &nitems_return, + &bytes_after_return, + &prop_data, + x_error_code); + + if (*x_error_code || prop_data == NULL) + goto fail; + + if (expected_format && actual_format_return != expected_format) + goto fail; + + if (expected_n_items && nitems_return != expected_n_items) + goto fail; + + if (n_items_ret) + *n_items_ret = nitems_return; + + return prop_data; + + fail: + + if (prop_data) + XFree(prop_data); + + return NULL; +} + + + +Bool +mb_wm_property_have_reply (MBWindowManager *wm, + MBWMCookie cookie) +{ + return xas_have_reply(wm->xas_context, (XasCookie)cookie); +} + + +MBWMCookie +mb_wm_xwin_get_attributes (MBWindowManager *wm, + Window win) +{ + return xas_get_window_attributes(wm->xas_context, win); +} + +MBWMCookie +mb_wm_xwin_get_geometry (MBWindowManager *wm, + Drawable d) +{ + return xas_get_geometry(wm->xas_context, d); +} + +MBWMClientWindowAttributes* +mb_wm_xwin_get_attributes_reply (MBWindowManager *wm, + MBWMCookie cookie, + int *x_error_code) +{ + return (MBWMClientWindowAttributes*) + xas_get_window_attributes_reply(wm->xas_context, + cookie, + x_error_code); +} + +Status +mb_wm_xwin_get_geometry_reply (MBWindowManager *wm, + XasCookie cookie, + MBGeometry *geom_return, + unsigned int *border_width_return, + unsigned int *depth_return, + int *x_error_code) +{ + return xas_get_geometry_reply (wm->xas_context, + cookie, + &geom_return->x, + &geom_return->y, + &geom_return->width, + &geom_return->height, + border_width_return, + depth_return, + x_error_code); +} + + +void +mb_wm_props_send_x_message (MBWindowManager *wm, + Window xwin_src, + Window xwin_dest, + Atom delivery_atom, + unsigned long data0, + unsigned long data1, + unsigned long data2, + unsigned long data3, + unsigned long data4, + unsigned long mask) +{ + XEvent ev; + + memset(&ev, 0, sizeof(ev)); + + ev.xclient.type = ClientMessage; + ev.xclient.window = xwin_src; + ev.xclient.message_type = delivery_atom; + ev.xclient.format = 32; + ev.xclient.data.l[0] = data0; + ev.xclient.data.l[1] = data1; + ev.xclient.data.l[2] = data2; + ev.xclient.data.l[3] = data3; + ev.xclient.data.l[4] = data4; + + if (!mask) + mask = NoEventMask; + + /* FIXME: traps */ + + XSendEvent(wm->xdpy, xwin_dest, False, mask, &ev); + XSync(wm->xdpy, False); + +} + +void +mb_wm_props_sync_root_props (MBWindowManager *wm) +{ + + + + +} + +void +mb_wm_props_root_message (MBWindowManager *wm) +{ + + + + +} diff --git a/matchbox/mb-wm-props.h b/matchbox/mb-wm-props.h new file mode 100644 index 0000000..07418ca --- /dev/null +++ b/matchbox/mb-wm-props.h @@ -0,0 +1,118 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_WM_PROPS_H +#define _HAVE_MB_WM_PROPS_H + +MBWMCookie +mb_wm_property_req (MBWindowManager *wm, + Window win, + Atom property, + long offset, + long length, + Bool delete, + Atom req_type); + +Status +mb_wm_property_reply (MBWindowManager *wm, + MBWMCookie cookie, + Atom *actual_type_return, + int *actual_format_return, + unsigned long *nitems_return, + unsigned long *bytes_after_return, + unsigned char **prop_return, + int *x_error_code); + +void* +mb_wm_property_get_reply_and_validate (MBWindowManager *wm, + MBWMCookie cookie, + Atom expected_type, + int expected_format, + int expected_n_items, + int *n_items_ret, + int *x_error_code); +Bool +mb_wm_property_have_reply (MBWindowManager *wm, + MBWMCookie cookie); + +/* FIXME: mb_wm_xwin_* calls to go else where */ + +MBWMCookie +mb_wm_xwin_get_attributes (MBWindowManager *wm, + Window win); + +MBWMCookie +mb_wm_xwin_get_geometry (MBWindowManager *wm, + Drawable d); + +MBWMClientWindowAttributes* +mb_wm_xwin_get_attributes_reply (MBWindowManager *wm, + MBWMCookie cookie, + int *x_error_code); +Status +mb_wm_xwin_get_geometry_reply (MBWindowManager *wm, + XasCookie cookie, + MBGeometry *geom_return, + unsigned int *border_width_return, + unsigned int *depth_return, + int *x_error_code); + +void +mb_wm_props_send_x_message (MBWindowManager *wm, + Window xwin_src, + Window xwin_dest, + Atom delivery_atom, + unsigned long data0, + unsigned long data1, + unsigned long data2, + unsigned long data3, + unsigned long data4, + unsigned long mask); + +/* Utils */ + +#define mb_wm_property_cardinal_req(wm, win, prop) \ + mb_wm_property_req ((wm), \ + (win), \ + (prop), \ + 0, /* offset */ \ + 1024L, /* Length, FIXME: Check this */ \ + False, \ + XA_CARDINAL); + +#define mb_wm_property_atom_req(wm, win, prop) \ + mb_wm_property_req ((wm), \ + (win), \ + (prop), \ + 0, /* offset */ \ + 1024L, /* Length, FIXME: Check this */ \ + False, \ + XA_ATOM); + +#define mb_wm_property_utf8_req(wm, win, prop) \ + mb_wm_property_req ((wm), \ + (win), \ + (prop), \ + 0, /* offset */ \ + 1024L, /* Length, FIXME: Check this */ \ + False, \ + (wm)->atoms[MBWM_ATOM_UTF8_STRING]); + +#endif diff --git a/matchbox/mb-wm-root-window.c b/matchbox/mb-wm-root-window.c new file mode 100644 index 0000000..21b8617 --- /dev/null +++ b/matchbox/mb-wm-root-window.c @@ -0,0 +1,436 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2007 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 "matchbox.h" + +#include "mb-wm-theme.h" +#include "mb-wm-client-dialog.h" +#include "mb-wm-client-app.h" + +#include <X11/Xmd.h> + +static void +mb_wm_root_window_class_init (MBWMObjectClass *klass) +{ + MBWMRootWindowClass *rw_class; + + MBWM_MARK(); + + rw_class = (MBWMRootWindowClass *)klass; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMRootWindow"; +#endif +} + +static void +mb_wm_root_window_destroy (MBWMObject *this) +{ +} + +static Bool +mb_wm_root_window_init_attributes (MBWMRootWindow * win); + +static void +mb_wm_root_window_init_properties (MBWMRootWindow * win); + +static int +mb_wm_root_window_init (MBWMObject *this, va_list vap) +{ + MBWMRootWindow *root_window = MB_WM_ROOT_WINDOW (this); + MBWindowManager *wm; + MBWMObjectProp prop; + XSetWindowAttributes attr; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + if (prop == MBWMObjectPropWm) + { + wm = va_arg(vap, MBWindowManager *); + break; + } + else + MBWMO_PROP_EAT (vap, prop); + + prop = va_arg(vap, MBWMObjectProp); + } + + root_window->wm = wm; + root_window->xwindow = RootWindow(wm->xdpy, wm->xscreen); + + if (!mb_wm_root_window_init_attributes (root_window)) + { + MBWM_DBG ("Failed to initialize root window attributes."); + abort (); + } + + attr.override_redirect = True; + root_window->hidden_window = XCreateWindow(wm->xdpy, + root_window->xwindow, + -200, -200, 5, 5, 0, + CopyFromParent, + CopyFromParent, + CopyFromParent, + CWOverrideRedirect, &attr); + + mb_wm_root_window_init_properties (root_window); + + return 1; +} + +int +mb_wm_root_window_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMRootWindowClass), + sizeof (MBWMRootWindow), + mb_wm_root_window_init, + mb_wm_root_window_destroy, + mb_wm_root_window_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); + } + + return type; +} + +MBWMRootWindow* +mb_wm_root_window_get (MBWindowManager *wm) +{ + static MBWMRootWindow * root_window = NULL; + + if (!root_window) + { + root_window + = MB_WM_ROOT_WINDOW (mb_wm_object_new (MB_WM_TYPE_ROOT_WINDOW, + MBWMObjectPropWm, wm, + NULL)); + } + else + mb_wm_object_ref (MB_WM_OBJECT (root_window)); + + return root_window; +} + +static Bool +mb_wm_root_window_init_attributes (MBWMRootWindow * win) +{ + XSetWindowAttributes sattr; + MBWindowManager *wm = win->wm; + + sattr.event_mask = SubstructureRedirectMask + |SubstructureNotifyMask + |StructureNotifyMask + |PropertyChangeMask; + + mb_wm_util_trap_x_errors(); + + XChangeWindowAttributes(wm->xdpy, win->xwindow, CWEventMask, &sattr); + + XSync(wm->xdpy, False); + + if (mb_wm_util_untrap_x_errors()) + { + /* FIXME: Error codes */ + mb_wm_util_fatal_error("Unable to manage display - " + "another window manager already active?"); + return False; + } + + XSelectInput(wm->xdpy, win->xwindow, sattr.event_mask); + + return True; +} + +void +mb_wm_root_window_update_supported_props (MBWMRootWindow *win) +{ + MBWindowManager *wm = win->wm; + Window rwin = win->xwindow; + CARD32 num_supported = 0; + + /* + * Supported info + */ + Atom supported[] = { + wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_TOOLBAR], + wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_DOCK], + wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_DIALOG], + wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_DESKTOP], + wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_SPLASH], + wm->atoms[MBWM_ATOM_NET_WM_WINDOW_TYPE_MENU], + wm->atoms[MBWM_ATOM_NET_WM_STATE], + wm->atoms[MBWM_ATOM_NET_WM_STATE_FULLSCREEN], + wm->atoms[MBWM_ATOM_NET_WM_STATE_MODAL], + wm->atoms[MBWM_ATOM_NET_SUPPORTED], + wm->atoms[MBWM_ATOM_NET_CLIENT_LIST], + wm->atoms[MBWM_ATOM_NET_NUMBER_OF_DESKTOPS], + wm->atoms[MBWM_ATOM_NET_ACTIVE_WINDOW], + wm->atoms[MBWM_ATOM_NET_SUPPORTING_WM_CHECK], + wm->atoms[MBWM_ATOM_NET_CLOSE_WINDOW], + wm->atoms[MBWM_ATOM_NET_CURRENT_DESKTOP], + wm->atoms[MBWM_ATOM_NET_CLIENT_LIST_STACKING], + wm->atoms[MBWM_ATOM_NET_SHOWING_DESKTOP], + wm->atoms[MBWM_ATOM_NET_WM_NAME], + wm->atoms[MBWM_ATOM_NET_WM_ICON], + wm->atoms[MBWM_ATOM_NET_WM_ALLOWED_ACTIONS], + wm->atoms[MBWM_ATOM_NET_WM_ACTION_MOVE], + wm->atoms[MBWM_ATOM_NET_WM_ACTION_FULLSCREEN], + wm->atoms[MBWM_ATOM_NET_WM_ACTION_CLOSE], + wm->atoms[MBWM_ATOM_NET_STARTUP_ID], + wm->atoms[MBWM_ATOM_NET_WM_PING], + wm->atoms[MBWM_ATOM_NET_WORKAREA], + wm->atoms[MBWM_ATOM_NET_DESKTOP_GEOMETRY], + wm->atoms[MBWM_ATOM_NET_WM_PING], + wm->atoms[MBWM_ATOM_NET_WM_PID], + wm->atoms[MBWM_ATOM_CM_TRANSLUCENCY], + wm->atoms[MBWM_ATOM_NET_WM_FULL_PLACEMENT], + wm->atoms[MBWM_ATOM_NET_FRAME_EXTENTS], + 0, 0, 0 + }; + + num_supported = sizeof(supported)/sizeof(Atom) - 3; + + /* Check to see if the theme supports help / accept buttons */ + if (wm->theme) + { + if (mb_wm_theme_supports (wm->theme, + MBWMThemeCapsFrameMainButtonActionAccept)) + supported[num_supported++]=wm->atoms[MBWM_ATOM_NET_WM_CONTEXT_ACCEPT]; + + if (mb_wm_theme_supports (wm->theme, + MBWMThemeCapsFrameMainButtonActionHelp)) + supported[num_supported++] = wm->atoms[MBWM_ATOM_NET_WM_CONTEXT_HELP]; + + if (mb_wm_theme_supports (wm->theme, + MBWMThemeCapsFrameMainButtonActionCustom)) + supported[num_supported++]=wm->atoms[MBWM_ATOM_NET_WM_CONTEXT_CUSTOM]; + } + + XChangeProperty(wm->xdpy, rwin, wm->atoms[MBWM_ATOM_NET_SUPPORTED], + XA_ATOM, 32, PropModeReplace, (unsigned char *)supported, + num_supported); + + if (wm->theme) + { + if (wm->theme->path) + XChangeProperty(wm->xdpy, rwin, wm->atoms[MBWM_ATOM_MB_THEME], + XA_STRING, 8, PropModeReplace, + (unsigned char *)wm->theme->path, + strlen (wm->theme->path) + 1); + else + XDeleteProperty (wm->xdpy, rwin, wm->atoms[MBWM_ATOM_MB_THEME]); + } +} + +static void +mb_wm_root_window_init_properties (MBWMRootWindow * win) +{ + MBWindowManager *wm = win->wm; + Window rwin = win->xwindow; + Window hwin = win->hidden_window; + + CARD32 card32; + unsigned long val[2]; + char *app_name = "matchbox"; + + val[0] = hwin; + + /* Window name */ + XChangeProperty(wm->xdpy, hwin, + wm->atoms[MBWM_ATOM_NET_WM_NAME], + wm->atoms[MBWM_ATOM_UTF8_STRING], + 8, PropModeReplace, + (unsigned char *)app_name, strlen(app_name)+1); + + XStoreName(wm->xdpy, hwin, app_name); + + /* Crack Needed to stop gnome session hanging ? */ + XChangeProperty(wm->xdpy, rwin, + wm->atoms[MBWM_ATOM_WIN_SUPPORTING_WM_CHECK], + XA_WINDOW, 32, PropModeReplace, (unsigned char *)val, + 1); + + XChangeProperty(wm->xdpy, hwin, + wm->atoms[MBWM_ATOM_WIN_SUPPORTING_WM_CHECK], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *)val, 1); + + /* Correct way of doing it */ + XChangeProperty(wm->xdpy, rwin, + wm->atoms[MBWM_ATOM_NET_SUPPORTING_WM_CHECK], + XA_WINDOW, 32, PropModeReplace, (unsigned char *)val, + 1); + + XChangeProperty(wm->xdpy, hwin, + wm->atoms[MBWM_ATOM_NET_SUPPORTING_WM_CHECK], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *)val, 1); + + mb_wm_root_window_update_supported_props (win); + + /* + * Desktop info + */ + card32 = 1; + XChangeProperty(wm->xdpy, rwin, wm->atoms[MBWM_ATOM_NET_NUMBER_OF_DESKTOPS], + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)&card32, 1); + + --card32; + XChangeProperty(wm->xdpy, rwin, wm->atoms[MBWM_ATOM_NET_CURRENT_DESKTOP], + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)&card32, 1); + + XChangeProperty(wm->xdpy, rwin, wm->atoms[MBWM_ATOM_NET_SHOWING_DESKTOP], + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)&card32, 1); + + val[0] = 0; + val[1] = 0; + + XChangeProperty(wm->xdpy, rwin, wm->atoms[MBWM_ATOM_NET_DESKTOP_VIEWPORT], + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)&val[0], 2); + + XSync(wm->xdpy, False); +} + +int +mb_wm_root_window_handle_message (MBWMRootWindow *win, XClientMessageEvent *e) +{ + MBWindowManager *wm = win->wm; + MBWindowManagerClient *c = NULL; + + if (e->message_type == wm->atoms[MBWM_ATOM_NET_ACTIVE_WINDOW]) + { + Window xwin = e->window; + + if ((c = mb_wm_managed_client_from_xwindow (wm, xwin)) != NULL) + mb_wm_activate_client (wm, c); + + return 1; + } + else if (e->message_type == wm->atoms[MBWM_ATOM_NET_CLOSE_WINDOW]) + { + if ((c = mb_wm_managed_client_from_xwindow(wm, e->window)) != NULL) + mb_wm_client_deliver_delete(c); + + return 1; + } + else if (e->message_type == wm->atoms[MBWM_ATOM_WM_PROTOCOLS] + && e->data.l[0] == wm->atoms[MBWM_ATOM_NET_WM_PING]) + { + if ((c = mb_wm_managed_client_from_xwindow(wm, e->data.l[2])) != NULL) + mb_wm_handle_ping_reply (wm, c); + return 1; + } + else if (e->message_type == wm->atoms[MBWM_ATOM_NET_WM_STATE]) + { + MBWMClientWindowStateChange state_op = 0; + + if (e->data.l[0] == 0) + state_op = MBWMClientWindowStateChangeRemove; + else if (e->data.l[0] == 1) + state_op = MBWMClientWindowStateChangeAdd; + else if (e->data.l[0] == 2) + state_op = MBWMClientWindowStateChangeToggle; + + if (e->data.l[1] == wm->atoms[MBWM_ATOM_NET_WM_STATE_FULLSCREEN] + && ((c = mb_wm_managed_client_from_xwindow(wm, e->window)) != NULL) + && MB_WM_IS_CLIENT_APP (c)) + { + mb_wm_client_set_state (c, + MBWM_ATOM_NET_WM_STATE_FULLSCREEN, + state_op); + } + else if (e->data.l[1] == wm->atoms[MBWM_ATOM_NET_WM_STATE_ABOVE] + && ((c = mb_wm_managed_client_from_xwindow(wm, e->window)) != + NULL) + && MB_WM_IS_CLIENT_DIALOG (c)) + { + mb_wm_client_set_state (c, + MBWM_ATOM_NET_WM_STATE_ABOVE, + state_op); + } + return 1; + } + + else if (e->message_type == wm->atoms[MBWM_ATOM_WM_CHANGE_STATE]) + { + switch (e->data.l[0]) + { + case IconicState: + if ((c = mb_wm_managed_client_from_xwindow (wm, e->window))) + mb_wm_client_iconize (c); + + default: + MBWM_DBG ("Unhandled value %d for WM_CHANGE_STATE ClientMessage", + e->data.l[0]); + } + } + else if (e->message_type == wm->atoms[MBWM_ATOM_NET_SHOWING_DESKTOP]) + { + mb_wm_handle_show_desktop (wm, e->data.l[0]); + return 1; + } + else if (e->message_type == wm->atoms[MBWM_ATOM_NET_CURRENT_DESKTOP]) + { + mb_wm_select_desktop (wm, e->data.l[0]); + return 1; + } + else if (e->message_type == wm->atoms[MBWM_ATOM_MB_COMMAND]) + { + switch (e->data.l[0]) + { + case MB_CMD_EXIT: + exit(0); + case MB_CMD_NEXT: + mb_wm_cycle_apps (wm, False); + break; + case MB_CMD_PREV: + mb_wm_cycle_apps (wm, True); + break; + case MB_CMD_DESKTOP: + mb_wm_toggle_desktop (wm); + break; +#if ENABLE_COMPOSITE + case MB_CMD_COMPOSITE: + if (mb_wm_compositing_enabled (wm)) + mb_wm_compositing_off (wm); + else + mb_wm_compositing_on (wm); + break; +#endif + default: + /*FIXME -- not implemented yet */ + case MB_CMB_KEYS_RELOAD: + ; + } + } + + return 0; +} diff --git a/matchbox/mb-wm-root-window.h b/matchbox/mb-wm-root-window.h new file mode 100644 index 0000000..3d98006 --- /dev/null +++ b/matchbox/mb-wm-root-window.h @@ -0,0 +1,60 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2007 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. + * + */ + +#ifndef _HAVE_MB_WM_ROOT_WINDOW_H +#define _HAVE_MB_WM_ROOT_WINDOW_H + +#include <matchbox/mb-wm-object.h> +#include <matchbox/mb-window-manager.h> + +typedef struct MBWMRootWindow MBWMRootWindow; +typedef struct MBWMRootWindowClass MBWMRootWindowClass; + +#define MB_WM_ROOT_WINDOW(c) ((MBWMRootWindow*)(c)) +#define MB_WM_ROOT_WINDOW_CLASS(c) ((MBWMRootWindowClass*)(c)) +#define MB_WM_TYPE_ROOT_WINDOW (mb_wm_root_window_class_type ()) + +struct MBWMRootWindow +{ + MBWMObject parent; + + Window xwindow; + Window hidden_window; + MBWindowManager *wm; +}; + +struct MBWMRootWindowClass +{ + MBWMObjectClass parent; +}; + +MBWMRootWindow * +mb_wm_root_window_get (MBWindowManager *wm); + +int +mb_wm_root_window_class_type (); + +int +mb_wm_root_window_handle_message(MBWMRootWindow *win, XClientMessageEvent *e); + +void +mb_wm_root_window_update_supported_props (MBWMRootWindow *win); + +#endif diff --git a/matchbox/mb-wm-stack.c b/matchbox/mb-wm-stack.c new file mode 100644 index 0000000..af5c4a3 --- /dev/null +++ b/matchbox/mb-wm-stack.c @@ -0,0 +1,325 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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 "matchbox.h" + + +static void +mb_wm_stack_ensure_trans_foreach (MBWindowManagerClient *client, void *data) +{ + MBWMList * t = mb_wm_client_get_transients (client); + + mb_wm_stack_move_top (client); + + mb_wm_util_list_foreach + (t, (MBWMListForEachCB) mb_wm_stack_ensure_trans_foreach, NULL); + + mb_wm_util_list_free (t); +} + +void +mb_wm_stack_dump (MBWindowManager *wm) +{ +#if (MBWM_WANT_DEBUG) + MBWindowManagerClient *client; + MBWMStackLayerType stacking_layer; + + fprintf(stderr, "\n==== window stack =====\n"); + + mb_wm_stack_enumerate_reverse (wm, client) + { + MBWindowManagerClient *trans_client = client; + int i = 0, j = 0; + char prefix[128] = {0}; + + while ((trans_client = mb_wm_client_get_transient_for(trans_client))) + i++; + + if (i) + { + for (j=0;j<=i*2;j+=2) + { prefix[j] = ' '; prefix[j+1] = ' '; } + + strcpy(&prefix[i*2], " +--"); + } + + stacking_layer = mb_wm_client_get_stacking_layer (client); + + fprintf(stderr, "%s XID: %lx NAME: %s, type %d, layer %d\n", + prefix, + MB_WM_CLIENT_XWIN(client), + client->window->name ? client->window->name : "unknown", + MB_WM_CLIENT_CLIENT_TYPE (client), + stacking_layer); + } + + fprintf(stderr, "======================\n\n"); +#endif +} + +void +mb_wm_stack_ensure (MBWindowManager *wm) +{ + MBWindowManagerClient *client, *seen, *next; + int i; + MBWMStackLayerType stacking_layer; + + if (wm->stack_bottom == NULL) + return; + + /* Ensure the window stack is corrent; + * - with respect to client layer types + * - transients are stacked within these layers also + * + * We need to be careful here as we modify stacking list + * in place while enumerating it. + * + * FIXME: This isn't optimal + */ + + /* bottom -> top on layer types */ + for (i=1; i<N_MBWMStackLayerTypes; i++) + { + /* Push each layer type to top, handling transients */ + client = wm->stack_bottom; + seen = NULL; + + while (client != seen && client != NULL) + { + /* get the next valid client ( ignore transients ) before + * modifying the list + */ + next = client->stacked_above; + + while (next && mb_wm_client_get_transient_for (next)) + next = next->stacked_above; + + stacking_layer = mb_wm_client_get_stacking_layer (client); + + if (stacking_layer == i + && mb_wm_client_get_transient_for (client) == NULL) + { + /* Keep track of the first client modified so we + * know when to stop iterating. + */ + if (seen == NULL) + seen = client; + + mb_wm_client_stack (client, 0); + } + client = next; + } + } + + mb_wm_stack_dump (wm); +} + +void +mb_wm_stack_insert_above_client (MBWindowManagerClient *client, + MBWindowManagerClient *client_below) +{ + MBWindowManager *wm = client->wmref; + + MBWM_ASSERT (client != NULL); + + if (client_below == NULL) + { + /* NULL so nothing below add at bottom */ + if (wm->stack_bottom) + { + client->stacked_above = wm->stack_bottom; + wm->stack_bottom->stacked_below = client; + } + + wm->stack_bottom = client; + } + else + { + client->stacked_below = client_below; + client->stacked_above = client_below->stacked_above; + if (client->stacked_below) client->stacked_below->stacked_above = client; + if (client->stacked_above) client->stacked_above->stacked_below = client; + } + + if (client_below == wm->stack_top) + wm->stack_top = client; + + wm->stack_n_clients++; +} + + +void +mb_wm_stack_append_top (MBWindowManagerClient *client) +{ + MBWindowManager *wm = client->wmref; + + mb_wm_stack_insert_above_client(client, wm->stack_top); +} + +void +mb_wm_stack_prepend_bottom (MBWindowManagerClient *client) +{ + mb_wm_stack_insert_above_client(client, NULL); +} + +void +mb_wm_stack_move_client_above_type (MBWindowManagerClient *client, + MBWMClientType type_below) +{ + MBWindowManager *wm = client->wmref; + MBWindowManagerClient *highest_client = NULL; + + highest_client = mb_wm_stack_get_highest_by_type (wm, type_below); + + if (highest_client) + mb_wm_stack_move_above_client(client, highest_client); +} + + +void +mb_wm_stack_move_above_client (MBWindowManagerClient *client, + MBWindowManagerClient *client_below) +{ + if (client == client_below) return; + + MBWM_ASSERT (client != NULL); + MBWM_ASSERT (client_below != NULL); + + mb_wm_stack_remove(client); + mb_wm_stack_insert_above_client(client, client_below); +} + +MBWindowManagerClient* +mb_wm_stack_get_highest_by_type (MBWindowManager *wm, + MBWMClientType type) +{ + MBWindowManagerClient *c = NULL; + + mb_wm_stack_enumerate_reverse (wm,c) + if (MB_WM_CLIENT_CLIENT_TYPE(c) & type) + return c; + + return NULL; +} + +MBWindowManagerClient* +mb_wm_stack_get_lowest_by_type(MBWindowManager *w, MBWMClientType wanted_type) + +{ + MBWindowManagerClient *c = NULL; + + mb_wm_stack_enumerate(w,c) + if (MB_WM_CLIENT_CLIENT_TYPE(c) & wanted_type) + return c; + + return NULL; +} + +MBWindowManagerClient * +mb_wm_stack_cycle_by_type(MBWindowManager *wm, MBWMClientType type, + Bool reverse) +{ + if (reverse) + { + MBWindowManagerClient *prev, *highest; + + highest = mb_wm_stack_get_highest_by_type (wm, type); + + if (!highest) + return highest; + + prev = highest->stacked_below; + + while (prev && (!(type & MB_WM_CLIENT_CLIENT_TYPE (prev)))) + { + prev = prev->stacked_below; + } + + if (prev && highest && prev != highest) + { + mb_wm_stack_move_above_client (prev, highest); + } + + return prev; + } + else + { + MBWindowManagerClient *lowest, *highest; + + lowest = mb_wm_stack_get_lowest_by_type (wm, type); + highest = mb_wm_stack_get_highest_by_type (wm, type); + + if (lowest && highest && lowest != highest) + { + mb_wm_stack_move_above_client (lowest, highest); + } + + return lowest; + } +} + +void +mb_wm_stack_remove (MBWindowManagerClient *client) +{ + MBWindowManager *wm = client->wmref; + Bool change = False; + + if (wm->stack_top == wm->stack_bottom) + { + if (wm->stack_top != client) + { + MBWM_DBG("Client stack corruption !!!"); + } + else + wm->stack_top = wm->stack_bottom = NULL; + } + else + { + if (client == wm->stack_top) + { + wm->stack_top = client->stacked_below; + change = True; + } + + if (client == wm->stack_bottom) + { + wm->stack_bottom = client->stacked_above; + change = True; + } + + if (client->stacked_below != NULL) + { + client->stacked_below->stacked_above = client->stacked_above; + change = True; + } + if (client->stacked_above != NULL) + { + client->stacked_above->stacked_below = client->stacked_below; + change = True; + } + } + + client->stacked_above = client->stacked_below = NULL; + + if (change) + wm->stack_n_clients--; +} + + diff --git a/matchbox/mb-wm-stack.h b/matchbox/mb-wm-stack.h new file mode 100644 index 0000000..58afc45 --- /dev/null +++ b/matchbox/mb-wm-stack.h @@ -0,0 +1,96 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_WM_STACK_H_ +#define _HAVE_MB_WM_STACK_H_ + +#define mb_wm_stack_enumerate(w,c) \ + if ((w)->stack_bottom) \ + for ((c)=(w)->stack_bottom; (c) != NULL; (c)=(c)->stacked_above) + +#define mb_wm_stack_enumerate_reverse(w,c) \ + if ((w)->stack_top) \ + for ((c)=(w)->stack_top; (c) != NULL; (c)=(c)->stacked_below) + +#define mb_wm_stack_enumerate_transients(w,c,t) \ + if ((w)->stack_bottom) \ + for ((c)=(w)->stack_bottom; (c) != NULL; (c)=(c)->stacked_above) \ + if ((c)->trans == (t)) + + +#define mb_wm_stack_move_top(c) \ + mb_wm_stack_move_above_client((c), (c)->wmref->stack_top) + +/* +#define mb_wm_stack_add_bottom(c) \ + stack_move_below_client((c), (c)->wmref->stack_bottom) +*/ + +#define mb_wm_stack_empty(w) \ + ((w)->stack_bottom == NULL) + +#define mb_wm_stack_size(w) \ + (w)->stack_n_clients + +void +mb_wm_stack_ensure (MBWindowManager *wm); + +void +mb_wm_stack_insert_above_client (MBWindowManagerClient *client, + MBWindowManagerClient *client_below); + +void +mb_wm_stack_append_top (MBWindowManagerClient *client); + +void +mb_wm_stack_prepend_bottom (MBWindowManagerClient *client); + +void +mb_wm_stack_move_client_above_type (MBWindowManagerClient *client, + MBWMClientType type_below); + +void +mb_wm_stack_move_client_above_type (MBWindowManagerClient *client, + MBWMClientType type_below); + +MBWindowManagerClient * +mb_wm_stack_cycle_by_type(MBWindowManager *w, MBWMClientType type, + Bool reverse); + +void +mb_wm_stack_move_above_client (MBWindowManagerClient *client, + MBWindowManagerClient *client_below); + +MBWindowManagerClient* +mb_wm_stack_get_highest_by_type(MBWindowManager *w, + MBWMClientType wanted_type); + + +MBWindowManagerClient* +mb_wm_stack_get_lowest_by_type(MBWindowManager *w, + MBWMClientType wanted_type); + +void +mb_wm_stack_remove (MBWindowManagerClient *client); + +void +mb_wm_stack_dump (MBWindowManager *wm); + +#endif diff --git a/matchbox/mb-wm-theme-png.c b/matchbox/mb-wm-theme-png.c new file mode 100644 index 0000000..a747691 --- /dev/null +++ b/matchbox/mb-wm-theme-png.c @@ -0,0 +1,1306 @@ +#include <math.h> +#include <png.h> + +#include "mb-wm-theme-png.h" +#include "mb-wm-theme-xml.h" + +#include <X11/Xft/Xft.h> + +#ifdef HAVE_XEXT +#include <X11/extensions/shape.h> +#endif + +static int +mb_wm_theme_png_ximg (MBWMThemePng * theme, const char * img); + +static unsigned char* +mb_wm_theme_png_load_file (const char *file, int *width, int *height); + +static void +mb_wm_theme_png_paint_decor (MBWMTheme *theme, MBWMDecor *decor); + +static void +mb_wm_theme_png_paint_button (MBWMTheme *theme, MBWMDecorButton *button); + +static void +mb_wm_theme_png_get_decor_dimensions (MBWMTheme *, MBWindowManagerClient *, + int*, int*, int*, int*); + +static MBWMDecor * +mb_wm_theme_png_create_decor (MBWMTheme*, MBWindowManagerClient *, + MBWMDecorType); + +static void +mb_wm_theme_png_resize_decor (MBWMTheme *theme, MBWMDecor *decor); + +static void +mb_wm_theme_png_get_button_size (MBWMTheme *, MBWMDecor *, + MBWMDecorButtonType, int *, int *); + +static void +mb_wm_theme_png_get_button_position (MBWMTheme *, MBWMDecor *, + MBWMDecorButtonType, + int *, int *); + +static void +mb_wm_theme_png_class_init (MBWMObjectClass *klass) +{ + MBWMThemeClass *t_class = MB_WM_THEME_CLASS (klass); + + t_class->paint_decor = mb_wm_theme_png_paint_decor; + t_class->paint_button = mb_wm_theme_png_paint_button; + t_class->decor_dimensions = mb_wm_theme_png_get_decor_dimensions; + t_class->button_size = mb_wm_theme_png_get_button_size; + t_class->button_position = mb_wm_theme_png_get_button_position; + t_class->create_decor = mb_wm_theme_png_create_decor; + t_class->resize_decor = mb_wm_theme_png_resize_decor; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMThemePng"; +#endif +} + +static void +mb_wm_theme_png_destroy (MBWMObject *obj) +{ + MBWMThemePng * theme = MB_WM_THEME_PNG (obj); + Display * dpy = MB_WM_THEME (obj)->wm->xdpy; + + XRenderFreePicture (dpy, theme->xpic); + XFreePixmap (dpy, theme->xdraw); + + if (theme->shape_mask) + XFreePixmap (dpy, theme->shape_mask); +} + +static int +mb_wm_theme_png_init (MBWMObject *obj, va_list vap) +{ + MBWMThemePng *p_theme = MB_WM_THEME_PNG (obj); + MBWMTheme *theme = MB_WM_THEME (obj); + MBWMObjectProp prop; + char *img = NULL; +#if USE_PANGO + Display *xdpy = theme->wm->xdpy; + int xscreen = theme->wm->xscreen; +#endif + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + switch (prop) + { + case MBWMObjectPropThemeImg: + img = va_arg(vap, char *); + break; + default: + MBWMO_PROP_EAT (vap, prop); + } + + prop = va_arg(vap, MBWMObjectProp); + } + + if (!img || !mb_wm_theme_png_ximg (p_theme, img)) + return 0; + +#if USE_PANGO + p_theme->context = pango_xft_get_context (xdpy, xscreen); + p_theme->fontmap = pango_xft_get_font_map (xdpy, xscreen); +#endif + + return 1; +} + +int +mb_wm_theme_png_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMThemePngClass), + sizeof (MBWMThemePng), + mb_wm_theme_png_init, + mb_wm_theme_png_destroy, + mb_wm_theme_png_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_THEME, 0); + } + + return type; +} + +struct DecorData +{ + Pixmap xpix; + Pixmap shape_mask; + GC gc_mask; + XftDraw *xftdraw; + XftColor clr; +#if USE_PANGO + PangoFont *font; +#else + XftFont *font; +#endif +}; + +static void +decordata_free (MBWMDecor * decor, void *data) +{ + struct DecorData * dd = data; + Display * xdpy = decor->parent_client->wmref->xdpy; + + XFreePixmap (xdpy, dd->xpix); + + if (dd->shape_mask) + XFreePixmap (xdpy, dd->shape_mask); + + if (dd->gc_mask) + XFreeGC (xdpy, dd->gc_mask); + + XftDrawDestroy (dd->xftdraw); + +#if USE_PANGO + if (dd->font) + g_object_unref (dd->font); +#else + if (dd->font) + XftFontClose (xdpy, dd->font); +#endif + + free (dd); +} + +struct ButtonData +{ + Pixmap xpix_i; + XftDraw *xftdraw_i; + Pixmap xpix_a; + XftDraw *xftdraw_a; +}; + +static void +buttondata_free (MBWMDecorButton * button, void *data) +{ + struct ButtonData * bd = data; + Display * xdpy = button->decor->parent_client->wmref->xdpy; + + XFreePixmap (xdpy, bd->xpix_i); + XftDrawDestroy (bd->xftdraw_i); + XFreePixmap (xdpy, bd->xpix_a); + XftDrawDestroy (bd->xftdraw_a); + + free (bd); +} + +#if !USE_PANGO +static XftFont * +xft_load_font(MBWMDecor * decor, MBWMXmlDecor *d) +{ + char desc[512]; + XftFont *font; + Display * xdpy = decor->parent_client->wmref->xdpy; + int xscreen = decor->parent_client->wmref->xscreen; + int font_size = d->font_size ? d->font_size : 18; + + if (d->font_units == MBWMXmlFontUnitsPixels) + { + font_size = mb_wm_util_pixels_to_points (decor->parent_client->wmref, + font_size); + } + + snprintf (desc, sizeof (desc), "%s-%i", + d->font_family ? d->font_family : "Sans", + font_size); + + font = XftFontOpenName (xdpy, xscreen, desc); + + return font; +} +#endif + +static void +mb_wm_theme_png_paint_button (MBWMTheme *theme, MBWMDecorButton *button) +{ + MBWMDecor * decor; + MBWMThemePng * p_theme = MB_WM_THEME_PNG (theme); + MBWindowManagerClient * client; + MBWMClientType c_type;; + MBWMXmlClient * c; + MBWMXmlDecor * d; + MBWMXmlButton * b; + + /* + * We do not paint inactive buttons, as they get painted with the decor + */ + decor = button->decor; + client = mb_wm_decor_get_parent (decor); + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type)) && + (b = mb_wm_xml_button_find_by_type (d->buttons, button->type))) + { + Display * xdpy = theme->wm->xdpy; + int xscreen = theme->wm->xscreen; + struct DecorData * ddata = mb_wm_decor_get_theme_data (decor); + struct ButtonData * bdata; + int x, y; + + if (!ddata) + return; + + bdata = mb_wm_decor_button_get_theme_data (button); + + if (!bdata) + { + int a_x = b->active_x > -1 ? b->active_x : b->x; + int a_y = b->active_y > -1 ? b->active_y : b->y; + + int i_x = b->inactive_x > -1 ? b->inactive_x : b->x; + int i_y = b->inactive_y > -1 ? b->inactive_y : b->y; + + bdata = malloc (sizeof (struct ButtonData)); + + bdata->xpix_a = XCreatePixmap(xdpy, decor->xwin, + button->geom.width, button->geom.height, + DefaultDepth(xdpy, xscreen)); + + bdata->xftdraw_a = XftDrawCreate (xdpy, bdata->xpix_a, + DefaultVisual (xdpy, xscreen), + DefaultColormap (xdpy, xscreen)); + + bdata->xpix_i = XCreatePixmap(xdpy, decor->xwin, + button->geom.width, button->geom.height, + DefaultDepth(xdpy, xscreen)); + + bdata->xftdraw_i = XftDrawCreate (xdpy, bdata->xpix_i, + DefaultVisual (xdpy, xscreen), + DefaultColormap (xdpy, xscreen)); + + /* + * If the background color is set for the parent decor, we do a fill + * with the parent color first, then composite the decor image over, + * and finally composite the button image. (This way we can paint the + * button with a simple PictOpSrc, rather than having to do + * composting on each draw). + */ + if (d->clr_bg.set) + { + XRenderColor rclr2; + + rclr2.red = (int)(d->clr_bg.r * (double)0xffff); + rclr2.green = (int)(d->clr_bg.g * (double)0xffff); + rclr2.blue = (int)(d->clr_bg.b * (double)0xffff); + + /* Fill the inactive image */ + XRenderFillRectangle (xdpy, PictOpSrc, + XftDrawPicture (bdata->xftdraw_i), &rclr2, + 0, 0, b->width, b->height); + + /* Composite the decor over */ + XRenderComposite (xdpy, PictOpOver, + p_theme->xpic, + None, + XftDrawPicture (bdata->xftdraw_i), + b->x, b->y, 0, 0, 0, 0, b->width, b->height); + + /* Copy inactive button to the active one */ + XRenderComposite (xdpy, PictOpSrc, + XftDrawPicture (bdata->xftdraw_i), + None, + XftDrawPicture (bdata->xftdraw_a), + 0, 0, 0, 0, 0, 0, b->width, b->height); + + /* Composite inactive and active image on top */ + XRenderComposite (xdpy, PictOpOver, + p_theme->xpic, + None, + XftDrawPicture (bdata->xftdraw_i), + i_x, i_y, 0, 0, 0, 0, b->width, b->height); + + XRenderComposite (xdpy, PictOpOver, + p_theme->xpic, + None, + XftDrawPicture (bdata->xftdraw_a), + a_x, a_y, 0, 0, 0, 0, b->width, b->height); + } + else + { + XRenderComposite (xdpy, PictOpSrc, + p_theme->xpic, + None, + XftDrawPicture (bdata->xftdraw_i), + i_x, i_y, 0, 0, 0, 0, b->width, b->height); + + XRenderComposite (xdpy, PictOpSrc, + p_theme->xpic, + None, + XftDrawPicture (bdata->xftdraw_a), + a_x, a_y, 0, 0, 0, 0, b->width, b->height); + } + + mb_wm_decor_button_set_theme_data (button, bdata, buttondata_free); + } + + /* Here we automagically determine if the button should be left or + * right aligned in the case that a decor is expanded wider than + * the template image. If the coordinate comes before the point + * where decor padding is added, it's left aligned else it's + * right aligned. If no padding hints were given in the theme.xml, + * then we assume padding happens in the center. + * Note: we look at pad_length because pad_offset could be 0 + */ + x = b->x - d->x; + if (x > (d->pad_length ? d->pad_offset : d->width/2) ) + x = decor->geom.width - (d->x + d->width - b->x); + + y = b->y - d->y; + + XRenderComposite (xdpy, PictOpSrc, + button->state == MBWMDecorButtonStatePressed ? + XftDrawPicture (bdata->xftdraw_a) : + XftDrawPicture (bdata->xftdraw_i), + None, + XftDrawPicture (ddata->xftdraw), + 0, 0, 0, 0, x, y, b->width, b->height); + + XClearWindow (xdpy, decor->xwin); + } +} + +static void +mb_wm_theme_png_resize_decor (MBWMTheme *theme, MBWMDecor *decor) +{ + /* + * Clear any data we have stored with the theme; this will force + * resize on the next paint + */ + mb_wm_decor_set_theme_data (decor, NULL, NULL); +} + +static void +mb_wm_theme_png_paint_decor (MBWMTheme *theme, MBWMDecor *decor) +{ + MBWMThemePng * p_theme = MB_WM_THEME_PNG (theme); + MBWindowManagerClient * client = decor->parent_client; + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMXmlClient * c; + MBWMXmlDecor * d; + Display * xdpy = theme->wm->xdpy; + int xscreen = theme->wm->xscreen; + struct DecorData * data = mb_wm_decor_get_theme_data (decor); + const char * title; + int x, y; + int operator = PictOpSrc; + Bool shaped; + + if (!((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type)))) + return; + +#ifdef HAVE_XEXT + shaped = theme->shaped && c->shaped && !mb_wm_client_is_argb32 (client); +#endif + + if (data && (mb_wm_decor_get_dirty_state (decor) & MBWMDecorDirtyTitle)) + { + /* + * If the decor title is dirty, and we already have the data, + * free it and recreate (since the old title is already composited + * in the cached image). + */ + mb_wm_decor_set_theme_data (decor, NULL, NULL); + data = NULL; + } + + if (!data) + { + XRenderColor rclr; + + data = mb_wm_util_malloc0 (sizeof (struct DecorData)); + data->xpix = XCreatePixmap(xdpy, decor->xwin, + decor->geom.width, decor->geom.height, + DefaultDepth(xdpy, xscreen)); + + +#ifdef HAVE_XEXT + if (shaped) + { + data->shape_mask = + XCreatePixmap(xdpy, decor->xwin, + decor->geom.width, decor->geom.height, 1); + + data->gc_mask = XCreateGC (xdpy, data->shape_mask, 0, NULL); + } +#endif + data->xftdraw = XftDrawCreate (xdpy, data->xpix, + DefaultVisual (xdpy, xscreen), + DefaultColormap (xdpy, xscreen)); + + /* + * If the background color is set, we fill the pixmaps with it, + * and then overlay the the PNG image over (this allows a theme + * to provide a monochromatic PNG that can be toned, e.g., Sato) + */ + if (d->clr_bg.set) + { + XRenderColor rclr2; + + operator = PictOpOver; + + rclr2.red = (int)(d->clr_bg.r * (double)0xffff); + rclr2.green = (int)(d->clr_bg.g * (double)0xffff); + rclr2.blue = (int)(d->clr_bg.b * (double)0xffff); + + XRenderFillRectangle (xdpy, PictOpSrc, + XftDrawPicture (data->xftdraw), &rclr2, + 0, 0, + decor->geom.width, decor->geom.height); + } + + rclr.red = 0; + rclr.green = 0; + rclr.blue = 0; + rclr.alpha = 0xffff; + + if (d->clr_fg.set) + { + rclr.red = (int)(d->clr_fg.r * (double)0xffff); + rclr.green = (int)(d->clr_fg.g * (double)0xffff); + rclr.blue = (int)(d->clr_fg.b * (double)0xffff); + } + + XftColorAllocValue (xdpy, DefaultVisual (xdpy, xscreen), + DefaultColormap (xdpy, xscreen), + &rclr, &data->clr); + +#if USE_PANGO + { + PangoFontDescription * pdesc; + char desc[512]; + + snprintf (desc, sizeof (desc), "%s %i%s", + d->font_family ? d->font_family : "Sans", + d->font_size ? d->font_size : 18, + d->font_units == MBWMXmlFontUnitsPoints ? "" : "px"); + + pdesc = pango_font_description_from_string (desc); + + data->font = pango_font_map_load_font (p_theme->fontmap, + p_theme->context, + pdesc); + + pango_font_description_free (pdesc); + } +#else + data->font = xft_load_font (decor, d); +#endif + XSetWindowBackgroundPixmap(xdpy, decor->xwin, data->xpix); + + mb_wm_decor_set_theme_data (decor, data, decordata_free); + } + + /* + * Since we want to support things like rounded corners, but still + * have the decor resizable, we need to paint it in stages + * + * We assume that the decor image is exact in it's major axis, + * i.e., North and South decors provide image of the exactly correct + * height, and West and East of width. + */ + if (decor->type == MBWMDecorTypeNorth || + decor->type == MBWMDecorTypeSouth) + { + if (decor->geom.width < d->width) + { + /* The decor is smaller than the template, cut bit from the + * midle + */ + int width1 = decor->geom.width / 2; + int width2 = decor->geom.width - width1; + int x2 = d->x + d->width - width2; + + XRenderComposite(xdpy, operator, + p_theme->xpic, + None, + XftDrawPicture (data->xftdraw), + d->x, d->y, 0, 0, 0, 0, + width1, d->height); + + XRenderComposite(xdpy, operator, + p_theme->xpic, + None, + XftDrawPicture (data->xftdraw), + x2 , d->y, 0, 0, + width1, 0, + width2, d->height); + +#ifdef HAVE_XEXT + if (shaped) + { + XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask, + data->gc_mask, + d->x, d->y, width1, d->height, 0, 0); + XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask, + data->gc_mask, + x2, d->y, width2, d->height, width1, 0); + } +#endif + } + else if (decor->geom.width == d->width) + { + /* Exact match */ + XRenderComposite(xdpy, operator, + p_theme->xpic, + None, + XftDrawPicture (data->xftdraw), + d->x, d->y, 0, 0, + 0, 0, d->width, d->height); + +#ifdef HAVE_XEXT + if (shaped) + { + XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask, + data->gc_mask, + d->x, d->y, d->width, d->height, 0, 0); + } +#endif + } + else + { + /* The decor is bigger than the template, draw extra bit from + * the middle + */ + int pad_offset = d->pad_offset; + int pad_length = d->pad_length; + int gap_length = decor->geom.width - d->width; + + if (!pad_length) + { + pad_length = + decor->geom.width > 30 ? 10 : decor->geom.width / 4 + 1; + pad_offset = (d->width / 2) - (pad_length / 2); + } + + XRenderComposite(xdpy, operator, + p_theme->xpic, + None, + XftDrawPicture (data->xftdraw), + d->x, d->y, 0, 0, + 0, 0, + pad_offset, d->height); + + /* TODO: can we do this as one scaled operation? */ + for (x = pad_offset; x < pad_offset + gap_length; x += pad_length) + XRenderComposite(xdpy, operator, + p_theme->xpic, + None, + XftDrawPicture (data->xftdraw), + d->x + pad_offset, d->y, 0, 0, + x, 0, + pad_length, + d->height); + + XRenderComposite(xdpy, operator, + p_theme->xpic, + None, + XftDrawPicture (data->xftdraw), + d->x + pad_offset, d->y, 0, 0, + pad_offset + gap_length, 0, + d->width - pad_offset, d->height); + +#ifdef HAVE_XEXT + if (shaped) + { + XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask, + data->gc_mask, + d->x, d->y, + pad_offset, d->height, + 0, 0); + + for (x = pad_offset; x < pad_offset + gap_length; x += pad_length) + XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask, + data->gc_mask, + d->x + pad_offset, d->y, + d->width - pad_offset, d->height, + x, 0); + + XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask, + data->gc_mask, + d->x + pad_offset, d->y, + d->width - pad_offset, d->height, + pad_offset + gap_length, 0); + } +#endif + } + } + else + { + if (decor->geom.height < d->height) + { + /* The decor is smaller than the template, cut bit from the + * midle + */ + int height1 = decor->geom.height / 2; + int height2 = decor->geom.height - height1; + int y2 = d->y + d->height - height2; + + XRenderComposite(xdpy, operator, + p_theme->xpic, + None, + XftDrawPicture (data->xftdraw), + d->x, d->y, 0, 0, + 0, 0, + d->width, height1); + + XRenderComposite(xdpy, operator, + p_theme->xpic, + None, + XftDrawPicture (data->xftdraw), + d->x , y2, 0, 0, + 0, height1, + d->width, height2); + +#ifdef HAVE_XEXT + if (shaped) + { + XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask, + data->gc_mask, + d->x, d->y, d->width, height1, 0, 0); + XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask, + data->gc_mask, + d->x, y2, d->width, height2, 0, height1); + } +#endif + } + else if (decor->geom.height == d->height) + { + /* Exact match */ + XRenderComposite(xdpy, operator, + p_theme->xpic, + None, + XftDrawPicture (data->xftdraw), + d->x, d->y, 0, 0, + 0, 0, + d->width, d->height); + +#ifdef HAVE_XEXT + if (shaped) + { + XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask, + data->gc_mask, + d->x, d->y, d->width, d->height, 0, 0); + } +#endif + } + else + { + /* The decor is bigger than the template, draw extra bit from + * the middle + */ + int pad_offset = d->pad_offset; + int pad_length = d->pad_length; + int gap_length = decor->geom.height - d->height; + + if (!pad_length) + { + pad_length = + decor->geom.height > 30 ? 10 : decor->geom.height / 4 + 1; + pad_offset = (d->height / 2) - (pad_length / 2); + } + + XRenderComposite(xdpy, operator, + p_theme->xpic, + None, + XftDrawPicture (data->xftdraw), + d->x, d->y, 0, 0, 0, 0, + d->width, pad_offset); + + /* TODO: can we do this as one scaled operation? */ + for (y = pad_offset; y < pad_offset + gap_length; y += pad_length) + XRenderComposite(xdpy, operator, + p_theme->xpic, + None, + XftDrawPicture (data->xftdraw), + d->x, d->y + pad_offset, 0, 0, 0, y, + d->width, + pad_length); + + XRenderComposite(xdpy, operator, + p_theme->xpic, + None, + XftDrawPicture (data->xftdraw), + d->x , d->y + pad_offset, 0, 0, + 0, pad_offset + gap_length, + d->width, d->height - pad_offset); + +#ifdef HAVE_XEXT + if (shaped) + { + XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask, + data->gc_mask, + d->x, d->y, + d->width, pad_offset, + 0, 0); + + for (y = pad_offset; y < pad_offset + gap_length; y += pad_length) + XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask, + data->gc_mask, + d->x, d->y + pad_offset, + d->width, pad_length, + 0, y); + + XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask, + data->gc_mask, + d->x, d->y + pad_offset, + d->width, d->height - pad_offset, + 0, pad_offset + gap_length); + } +#endif + } + } + + if (d->show_title && + (title = mb_wm_client_get_name (client)) && + data->font) + { + XRectangle rec; + + int pack_start_x = mb_wm_decor_get_pack_start_x (decor); + int pack_end_x = mb_wm_decor_get_pack_end_x (decor); + int west_width = mb_wm_client_frame_west_width (client); + int y, ascent, descent; + int len = strlen (title); + +#if USE_PANGO + PangoFontMetrics * mtx; + PangoGlyphString * glyphs; + GList * items, *l; + PangoRectangle rect; + int xoff = 0; + + mtx = pango_font_get_metrics (data->font, NULL); + + ascent = PANGO_PIXELS (pango_font_metrics_get_ascent (mtx)); + descent = PANGO_PIXELS (pango_font_metrics_get_descent (mtx)); + + pango_font_metrics_unref (mtx); +#else + ascent = data->font->ascent; + descent = data->font->descent; +#endif + + y = (decor->geom.height - (ascent + descent)) / 2 + ascent; + + rec.x = 0; + rec.y = 0; + rec.width = pack_end_x - 2; + rec.height = d->height; + + XftDrawSetClipRectangles (data->xftdraw, 0, 0, &rec, 1); + +#if USE_PANGO + glyphs = pango_glyph_string_new (); + + /* + * Run the pango rendering pipeline on this text and draw with + * the xft backend (why Pango does not provide a convenience + * API for something as common as drawing a string escapes me). + */ + items = pango_itemize (p_theme->context, title, 0, len, NULL, NULL); + + l = items; + while (l) + { + PangoItem * item = l->data; + + item->analysis.font = data->font; + + pango_shape (title, len, &item->analysis, glyphs); + + pango_xft_render (data->xftdraw, + &data->clr, + data->font, + glyphs, + xoff + west_width + pack_start_x, y); + + /* Advance position */ + pango_glyph_string_extents (glyphs, data->font, NULL, &rect); + xoff += PANGO_PIXELS (rect.width); + + l = l->next; + } + + if (glyphs) + pango_glyph_string_free (glyphs); + + g_list_free (items); +#else + XftDrawStringUtf8(data->xftdraw, + &data->clr, + data->font, + west_width + pack_start_x, y, + title, len); +#endif + + /* Unset the clipping rectangle */ + rec.width = decor->geom.width; + rec.height = decor->geom.height; + + XftDrawSetClipRectangles (data->xftdraw, 0, 0, &rec, 1); + } + +#ifdef HAVE_XEXT + if (shaped) + { + XShapeCombineMask (xdpy, decor->xwin, + ShapeBounding, 0, 0, + data->shape_mask, ShapeSet); + + XShapeCombineShape (xdpy, + client->xwin_frame, + ShapeBounding, decor->geom.x, decor->geom.y, + decor->xwin, + ShapeBounding, ShapeUnion); + } +#endif + XClearWindow (xdpy, decor->xwin); +} + +static void +construct_buttons (MBWMThemePng * theme, MBWMDecor * decor, MBWMXmlDecor *d) +{ + MBWindowManagerClient *client = decor->parent_client; + MBWindowManager *wm = client->wmref; + MBWMDecorButton *button; + + if (d) + { + MBWMList * l = d->buttons; + while (l) + { + MBWMXmlButton * b = l->data; + + button = mb_wm_decor_button_stock_new (wm, + b->type, + b->packing, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + l = l->next; + } + } +} + +static MBWMDecor * +mb_wm_theme_png_create_decor (MBWMTheme *theme, + MBWindowManagerClient *client, + MBWMDecorType type) +{ + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMDecor *decor = NULL; + MBWindowManager *wm = client->wmref; + MBWMXmlClient *c; + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type))) + { + MBWMXmlDecor *d; + + d = mb_wm_xml_decor_find_by_type (c->decors, type); + + if (d) + { + decor = mb_wm_decor_new (wm, type); + decor->absolute_packing = True; + mb_wm_decor_attach (decor, client); + construct_buttons (MB_WM_THEME_PNG (theme), decor, d); + } + } + + return decor; +} + +static void +mb_wm_theme_png_get_button_size (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *width, + int *height) +{ + MBWindowManagerClient * client = decor->parent_client; + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMThemePng *p_theme = MB_WM_THEME_PNG (theme); + MBWMXmlClient * c; + MBWMXmlDecor * d; + + /* FIXME -- assumes button on the north decor only */ + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type))) + { + MBWMXmlButton * b = mb_wm_xml_button_find_by_type (d->buttons, type); + + if (b) + { + if (width) + *width = b->width; + + if (height) + *height = b->height; + + return; + } + } + + if (width) + *width = 0; + + if (height) + *height = 0; +} + +static void +mb_wm_theme_png_get_button_position (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *x, + int *y) +{ + MBWindowManagerClient * client = decor->parent_client; + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMThemePng *p_theme = MB_WM_THEME_PNG (theme); + MBWMXmlClient * c; + MBWMXmlDecor * d; + + /* FIXME -- assumes button on the north decor only */ + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type))) + { + MBWMXmlButton * b = mb_wm_xml_button_find_by_type (d->buttons, type); + + if (b) + { + if (x) + { + int button_x; + + /* Here we automagically determine if the button should be left or + * right aligned in the case that a decor is expanded wider than + * the template image. If the coordinate comes before the point + * where decor padding is added, it's left aligned else it's + * right aligned. If no padding hints were given in the theme.xml, + * then we assume padding happens in the center. + * Note: we look at pad_length because pad_offset could be 0 + */ + button_x = b->x - d->x; + if (button_x > (d->pad_length ? d->pad_offset : d->width/2) ) + button_x = decor->geom.width - (d->x + d->width - b->x); + + *x = button_x; + } + + if (y) + *y = b->y - d->y; + + return; + } + } + + if (x) + *x = 0; + + if (y) + *y = 0; +} + +static void +mb_wm_theme_png_get_decor_dimensions (MBWMTheme *theme, + MBWindowManagerClient *client, + int *north, + int *south, + int *west, + int *east) +{ + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMThemePng *p_theme = MB_WM_THEME_PNG (theme); + MBWMXmlClient * c; + MBWMXmlDecor * d; + + /* FIXME -- assumes button on the north decor only */ + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type))) + { + if (north) + { + d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeNorth); + + if (d) + *north = d->height; + else + *north = 0; + } + + if (south) + { + d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeSouth); + + if (d) + *south = d->height; + else + *south = 0; + } + + if (west) + { + d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeWest); + + if (d) + *west = d->width; + else + *west = 0; + } + + if (east) + { + d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeEast); + + if (d) + *east = d->width; + else + *east = 0; + } + + return; + } + + if (north) + *north = 0; + + if (south) + *south = 0; + + if (west) + *west = 0; + + if (east) + *east = 0; +} + +/* + * From matchbox-keyboard + */ +static unsigned char* +mb_wm_theme_png_load_file (const char *file, + int *width, + int *height) +{ + FILE *fd; + unsigned char *data; + unsigned char header[8]; + int bit_depth, color_type; + + png_uint_32 png_width, png_height, i, rowbytes; + png_structp png_ptr; + png_infop info_ptr; + png_bytep *row_pointers; + + if ((fd = fopen (file, "rb")) == NULL) + return NULL; + + fread (header, 1, 8, fd); + if (!png_check_sig (header, 8)) + { + fclose(fd); + return NULL; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + fclose(fd); + return NULL; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct (&png_ptr, NULL, NULL); + fclose(fd); + return NULL; + } + + if (setjmp (png_ptr->jmpbuf)) + { + png_destroy_read_struct( &png_ptr, &info_ptr, NULL); + fclose(fd); + return NULL; + } + + png_init_io (png_ptr, fd); + png_set_sig_bytes (png_ptr, 8); + png_read_info (png_ptr, info_ptr); + png_get_IHDR (png_ptr, info_ptr, &png_width, &png_height, &bit_depth, + &color_type, NULL, NULL, NULL); + + *width = (int) png_width; + *height = (int) png_height; + + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + if (bit_depth < 8) + png_set_packing(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + /* Add alpha */ + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_RGB) + png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER); + + if (color_type == PNG_COLOR_TYPE_PALETTE || + png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand (png_ptr); + + png_read_update_info (png_ptr, info_ptr); + + /* allocate space for data and row pointers */ + rowbytes = png_get_rowbytes (png_ptr, info_ptr); + data = (unsigned char*) malloc ((rowbytes * (*height + 1))); + row_pointers = (png_bytep *) malloc ((*height) * sizeof (png_bytep)); + + if (!data || !row_pointers) + { + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + + if (data) + free (data); + + if (row_pointers) + free (row_pointers); + + return NULL; + } + + for (i = 0; i < *height; i++) + row_pointers[i] = data + i * rowbytes; + + png_read_image (png_ptr, row_pointers); + png_read_end (png_ptr, NULL); + + free (row_pointers); + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + fclose(fd); + + return data; +} + +static int +mb_wm_theme_png_ximg (MBWMThemePng * theme, const char * img) +{ + MBWindowManager * wm = MB_WM_THEME (theme)->wm; + Display * dpy = wm->xdpy; + int screen = wm->xscreen; + + XImage * ximg, * shape_img; + GC gc, gcm; + int x; + int y; + int width; + int height; + XRenderPictFormat *ren_fmt; + XRenderPictureAttributes ren_attr; + unsigned char * p; + unsigned char * png_data = mb_wm_theme_png_load_file (img, &width, &height); + Bool shaped = MB_WM_THEME (theme)->shaped; + + if (!png_data || !width || !height) + return 0; + + ren_fmt = XRenderFindStandardFormat(dpy, PictStandardARGB32); + + theme->xdraw = + XCreatePixmap (dpy, RootWindow(dpy,screen), width, height, ren_fmt->depth); + + if (shaped) + theme->shape_mask = + XCreatePixmap (dpy, RootWindow(dpy,screen), width, height, 1); + + XSync (dpy, False); + + ren_attr.dither = True; + ren_attr.component_alpha = True; + ren_attr.repeat = False; + + gc = XCreateGC (dpy, theme->xdraw, 0, NULL); + + if (shaped) + gcm = XCreateGC (dpy, theme->shape_mask, 0, NULL); + + ximg = XCreateImage (dpy, DefaultVisual (dpy, screen), + ren_fmt->depth, ZPixmap, + 0, NULL, width, height, 32, 0); + + ximg->data = malloc (ximg->bytes_per_line * ximg->height); + + if (shaped) + { + shape_img = XCreateImage (dpy, DefaultVisual (dpy, screen), + 1, ZPixmap, + 0, NULL, width, height, 8, 0); + + shape_img->data = malloc (shape_img->bytes_per_line * shape_img->height); + } + + p = png_data; + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + unsigned char a, r, g, b; + r = *p++; g = *p++; b = *p++; a = *p++; + r = (r * (a + 1)) / 256; + g = (g * (a + 1)) / 256; + b = (b * (a + 1)) / 256; + + XPutPixel (ximg, x, y, (a << 24) | (r << 16) | (g << 8) | b); + + if (shaped) + { + XPutPixel (shape_img, x, y, a ? 1 : 0); + } + } + + XPutImage (dpy, theme->xdraw, gc, ximg, 0, 0, 0, 0, width, height); + + if (shaped) + XPutImage (dpy, theme->shape_mask, gcm, shape_img, + 0, 0, 0, 0, width, height); + + theme->xpic = XRenderCreatePicture (dpy, theme->xdraw, ren_fmt, + CPRepeat|CPDither|CPComponentAlpha, + &ren_attr); + + free (ximg->data); + ximg->data = NULL; + XDestroyImage (ximg); + XFreeGC (dpy, gc); + + if (shaped) + { + free (shape_img->data); + shape_img->data = NULL; + XDestroyImage (shape_img); + XFreeGC (dpy, gcm); + } + + free (png_data); + + return 1; +} diff --git a/matchbox/mb-wm-theme-png.h b/matchbox/mb-wm-theme-png.h new file mode 100644 index 0000000..e97e538 --- /dev/null +++ b/matchbox/mb-wm-theme-png.h @@ -0,0 +1,56 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_WM_THEME_PNG_H +#define _HAVE_MB_WM_THEME_PNG_H + +#include <matchbox/mb-wm-config.h> +#include <matchbox/mb-wm-theme.h> + +#include <X11/extensions/Xrender.h> + +#if USE_PANGO +#include <pango/pango.h> +#include <pango/pangoxft.h> +#endif + +struct MBWMThemePngClass +{ + MBWMThemeClass parent; + +}; + +struct MBWMThemePng +{ + MBWMTheme parent; + + Pixmap xdraw; + Picture xpic; + Pixmap shape_mask; + +#if USE_PANGO + PangoContext * context; + PangoFontMap * fontmap; +#endif +}; + +int mb_wm_theme_png_class_type (void); + +#endif diff --git a/matchbox/mb-wm-theme-xml.c b/matchbox/mb-wm-theme-xml.c new file mode 100644 index 0000000..e075b7b --- /dev/null +++ b/matchbox/mb-wm-theme-xml.c @@ -0,0 +1,212 @@ +/* + * Matchbox Window Manager 2 - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2007 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-theme-xml.h" +#include "mb-wm-theme.h" + +/***************************************************************** + * XML Parser stuff + */ +MBWMXmlButton * +mb_wm_xml_button_new () +{ + MBWMXmlButton * b = mb_wm_util_malloc0 (sizeof (MBWMXmlButton)); + + b->x = -1; + b->y = -1; + b->active_x = -1; + b->active_y = -1; + b->inactive_x = -1; + b->inactive_y = -1; + + return b; +} + +void +mb_wm_xml_button_free (MBWMXmlButton * b) +{ + free (b); +} + +MBWMXmlDecor * +mb_wm_xml_decor_new () +{ + MBWMXmlDecor * d = mb_wm_util_malloc0 (sizeof (MBWMXmlDecor)); + return d; +} + +void +mb_wm_xml_decor_free (MBWMXmlDecor * d) +{ + MBWMList * l; + + if (!d) + return; + + l = d->buttons; + while (l) + { + MBWMXmlButton * b = l->data; + MBWMList * n = l->next; + mb_wm_xml_button_free (b); + free (l); + + l = n; + } + + if (d->font_family) + free (d->font_family); + + free (d); +} + +MBWMXmlClient * +mb_wm_xml_client_new () +{ + MBWMXmlClient * c = mb_wm_util_malloc0 (sizeof (MBWMXmlClient)); + + c->x = -1; + c->y = -1; + c->width = -1; + c->height = -1; + + return c; +} + +void +mb_wm_xml_client_free (MBWMXmlClient * c) +{ + MBWMList * l; + + if (!c) + return; + + l = c->decors; + while (l) + { + MBWMXmlDecor * d = l->data; + MBWMList * n = l->next; + mb_wm_xml_decor_free (d); + free (l); + + l = n; + } + + free (c); +} + +void +mb_wm_xml_clr_from_string (MBWMColor * clr, const char *s) +{ + int r, g, b, a; + + if (!s || *s != '#') + { + clr->set = False; + return; + } + + sscanf (s+1,"%2x%2x%2x%2x", &r, &g, &b, &a); + clr->r = (double) r / 255.0; + clr->g = (double) g / 255.0; + clr->b = (double) b / 255.0; + clr->a = (double) a / 255.0; + + clr->set = True; +} + +MBWMXmlClient * +mb_wm_xml_client_find_by_type (MBWMList *l, MBWMClientType type) +{ + while (l) + { + MBWMXmlClient * c = l->data; + if (c->type == type) + return c; + + l = l->next; + } + + return NULL; +} + +MBWMXmlDecor * +mb_wm_xml_decor_find_by_type (MBWMList *l, MBWMDecorType type) +{ + while (l) + { + MBWMXmlDecor * d = l->data; + if (d->type == type) + return d; + + l = l->next; + } + + return NULL; +} + +MBWMXmlButton * +mb_wm_xml_button_find_by_type (MBWMList *l, MBWMDecorButtonType type) +{ + while (l) + { + MBWMXmlButton * b = l->data; + if (b->type == type) + return b; + + l = l->next; + } + + return NULL; +} + +#if 0 +void +mb_wm_xml_client_dump (MBWMList * l) +{ + printf ("=== XML Clients =====\n"); + while (l) + { + MBWMXmlClient * c = l->data; + MBWMList *l2 = c->decors; + printf ("===== client type %d =====\n", c->type); + + while (l2) + { + MBWMXmlDecor * d = l2->data; + MBWMList *l3 = d->buttons; + printf ("======= decor type %d =====\n", d->type); + + while (l3) + { + MBWMXmlButton * b = l3->data; + printf ("========= button type %d =====\n", d->type); + + l3 = l3->next; + } + + l2 = l2->next; + } + + l = l->next; + } + printf ("=== XML Clients End =====\n"); +} +#endif diff --git a/matchbox/mb-wm-theme-xml.h b/matchbox/mb-wm-theme-xml.h new file mode 100644 index 0000000..26a38fc --- /dev/null +++ b/matchbox/mb-wm-theme-xml.h @@ -0,0 +1,106 @@ +#ifndef _HAVE_MB_WM_THEME_PRIVATE_H +#define _HAVE_MB_WM_THEME_PRIVATE_H + +#include <matchbox/matchbox.h> +#include <matchbox/mb-wm-theme.h> +/* + * Helper structs for xml theme + */ +typedef struct Button +{ + MBWMDecorButtonType type; + MBWMDecorButtonPack packing; + + MBWMColor clr_fg; + MBWMColor clr_bg; + + int x; + int y; + int width; + int height; + + /* Needed by png themes */ + int active_x; + int active_y; + + int inactive_x; + int inactive_y; + + int press_activated; +} MBWMXmlButton; + +typedef enum _MBWMXmlFontUnits +{ + MBWMXmlFontUnitsPixels, + MBWMXmlFontUnitsPoints, +} MBWMXmlFontUnits; + +typedef struct Decor +{ + MBWMDecorType type; + + MBWMColor clr_fg; + MBWMColor clr_bg; + + int x; + int y; + int width; + int height; + int pad_offset; + int pad_length; + int show_title; + + int font_size; + MBWMXmlFontUnits font_units; + char * font_family; + + MBWMList * buttons; +}MBWMXmlDecor; + +typedef struct Client +{ + MBWMClientType type; + + int x; + int y; + int width; + int height; + + Bool shaped; + + MBWMList *decors; + + MBWMClientLayoutHints layout_hints; +}MBWMXmlClient; + +MBWMXmlButton * +mb_wm_xml_button_new (); + +void +mb_wm_xml_button_free (MBWMXmlButton * b); + +MBWMXmlDecor * +mb_wm_xml_decor_new (); + +void +mb_wm_xml_decor_free (MBWMXmlDecor * d); + +MBWMXmlClient * +mb_wm_xml_client_new (); + +void +mb_wm_xml_client_free (MBWMXmlClient * c); + +MBWMXmlClient * +mb_wm_xml_client_find_by_type (MBWMList *l, MBWMClientType type); + +MBWMXmlDecor * +mb_wm_xml_decor_find_by_type (MBWMList *l, MBWMDecorType type); + +MBWMXmlButton * +mb_wm_xml_button_find_by_type (MBWMList *l, MBWMDecorButtonType type); + +void +mb_wm_xml_clr_from_string (MBWMColor * clr, const char *s); + +#endif diff --git a/matchbox/mb-wm-theme.c b/matchbox/mb-wm-theme.c new file mode 100644 index 0000000..93e8fe7 --- /dev/null +++ b/matchbox/mb-wm-theme.c @@ -0,0 +1,2099 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2005 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-theme.h" +#include "mb-wm-theme-xml.h" + +#include <sys/stat.h> +#include <expat.h> +#include <X11/Xft/Xft.h> + +#define SIMPLE_FRAME_TITLEBAR_HEIGHT 40 +#define SIMPLE_FRAME_EDGE_SIZE 3 + +MBWMThemeCustomClientTypeFunc custom_client_type_func = NULL; +void *custom_client_type_func_data = NULL; + +MBWMThemeCustomButtonTypeFunc custom_button_type_func = NULL; +void *custom_button_type_func_data = NULL; + +MBWMThemeCustomThemeTypeFunc custom_theme_type_func = NULL; +void *custom_theme_type_func_data = NULL; + +MBWMThemeCustomThemeAllocFunc custom_theme_alloc_func = NULL; + +static void +xml_element_start_cb (void *data, const char *tag, const char **expat_attr); + +static void +xml_element_end_cb (void *data, const char *tag); + +static void +xml_stack_free (MBWMList *stack); + +static void +mb_wm_theme_simple_paint_decor (MBWMTheme *theme, MBWMDecor *decor); +static void +mb_wm_theme_simple_paint_button (MBWMTheme *theme, MBWMDecorButton *button); +static void +mb_wm_theme_simple_get_decor_dimensions (MBWMTheme *, MBWindowManagerClient *, + int *, int *, int *, int *); +static void +mb_wm_theme_simple_get_button_size (MBWMTheme *, MBWMDecor *, + MBWMDecorButtonType, int *, int *); +static void +mb_wm_theme_simple_get_button_position (MBWMTheme *, MBWMDecor *, + MBWMDecorButtonType, int*, int*); +static MBWMDecor * +mb_wm_theme_simple_create_decor (MBWMTheme *, MBWindowManagerClient *, + MBWMDecorType); + +static void +mb_wm_theme_class_init (MBWMObjectClass *klass) +{ + MBWMThemeClass *t_class = MB_WM_THEME_CLASS (klass); + + t_class->paint_decor = mb_wm_theme_simple_paint_decor; + t_class->paint_button = mb_wm_theme_simple_paint_button; + t_class->decor_dimensions = mb_wm_theme_simple_get_decor_dimensions; + t_class->button_size = mb_wm_theme_simple_get_button_size; + t_class->button_position = mb_wm_theme_simple_get_button_position; + t_class->create_decor = mb_wm_theme_simple_create_decor; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMTheme"; +#endif +} + +static void +mb_wm_theme_destroy (MBWMObject *obj) +{ + MBWMTheme *theme = MB_WM_THEME (obj); + + if (theme->path) + free (theme->path); + + MBWMList *l = theme->xml_clients; + + while (l) + { + MBWMXmlClient * c = l->data; + MBWMList * n = l->next; + mb_wm_xml_client_free (c); + free (l); + + l = n; + } +} + +static int +mb_wm_theme_init (MBWMObject *obj, va_list vap) +{ + MBWMTheme *theme = MB_WM_THEME (obj); + MBWindowManager *wm = NULL; + MBWMObjectProp prop; + MBWMList *xml_clients = NULL; + char *path = NULL; + MBWMColor *clr_lowlight = NULL; + MBWMColor *clr_shadow = NULL; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + switch (prop) + { + case MBWMObjectPropWm: + wm = va_arg(vap, MBWindowManager *); + break; + case MBWMObjectPropThemePath: + path = va_arg(vap, char *); + break; + case MBWMObjectPropThemeXmlClients: + xml_clients = va_arg(vap, MBWMList *); + break; + case MBWMObjectPropThemeColorLowlight: + clr_lowlight = va_arg(vap, MBWMColor *); + break; + case MBWMObjectPropThemeColorShadow: + clr_shadow = va_arg(vap, MBWMColor *); + break; + case MBWMObjectPropThemeShadowType: + theme->shadow_type = va_arg(vap, int); + break; + case MBWMObjectPropThemeCompositing: + theme->compositing = va_arg(vap, int); + break; + case MBWMObjectPropThemeShaped: + theme->shaped = va_arg(vap, int); + break; + + default: + MBWMO_PROP_EAT (vap, prop); + } + + prop = va_arg(vap, MBWMObjectProp); + } + + theme->wm = wm; + theme->xml_clients = xml_clients; + + if (path) + theme->path = strdup (path); + + if (clr_shadow && clr_shadow->set) + { + theme->color_shadow.r = clr_shadow->r; + theme->color_shadow.g = clr_shadow->g; + theme->color_shadow.b = clr_shadow->b; + theme->color_shadow.a = clr_shadow->a; + } + else + { + theme->color_shadow.r = 0.0; + theme->color_shadow.g = 0.0; + theme->color_shadow.b = 0.0; + theme->color_shadow.a = 0.95; + } + + if (clr_lowlight && clr_lowlight->set) + { + theme->color_lowlight.r = clr_lowlight->r; + theme->color_lowlight.g = clr_lowlight->g; + theme->color_lowlight.b = clr_lowlight->b; + theme->color_lowlight.a = clr_lowlight->a; + } + else + { + theme->color_lowlight.r = 0.0; + theme->color_lowlight.g = 0.0; + theme->color_lowlight.b = 0.0; + theme->color_lowlight.a = 0.55; + } + + return 1; +} + +int +mb_wm_theme_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMThemeClass), + sizeof (MBWMTheme), + mb_wm_theme_init, + mb_wm_theme_destroy, + mb_wm_theme_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); + } + + return type; +} + +Bool +mb_wm_theme_is_button_press_activated (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type) +{ + MBWindowManagerClient * client; + MBWMXmlClient * c; + MBWMXmlDecor * d; + MBWMXmlButton * b; + MBWMClientType c_type; + + if (!theme || !theme->xml_clients || !decor || !decor->parent_client) + return False; + + client = decor->parent_client; + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type)) && + (b = mb_wm_xml_button_find_by_type (d->buttons, type))) + { + return b->press_activated; + } + + return False; +} + +void +mb_wm_theme_get_button_size (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *width, + int *height) +{ + MBWMThemeClass *klass; + + MBWM_ASSERT (decor && decor->parent_client); + + if (!theme || !decor || !decor->parent_client) + return; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->button_size) + klass->button_size (theme, decor, type, width, height); +} + +/* + * If the parent decor uses absolute postioning, the returned values + * are absolute. If the decor does packing, these values are added to + * calculated button position. + */ +void +mb_wm_theme_get_button_position (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *x, + int *y) +{ + MBWMThemeClass *klass; + + MBWM_ASSERT (decor && decor->parent_client); + + if (!theme || !decor || !decor->parent_client) + return; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->button_position) + klass->button_position (theme, decor, type, x, y); + else + { + if (x) + *x = 2; + + if (y) + *y = 2; + } +} + +void +mb_wm_theme_get_decor_dimensions (MBWMTheme *theme, + MBWindowManagerClient *client, + int *north, + int *south, + int *west, + int *east) +{ + MBWMThemeClass *klass; + + MBWM_ASSERT (client); + + if (!theme || !client) + return; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->decor_dimensions) + klass->decor_dimensions (theme, client, north, south, west, east); +} + +void +mb_wm_theme_paint_decor (MBWMTheme *theme, MBWMDecor *decor) +{ + MBWMThemeClass *klass; + + if (!theme) + return; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->paint_decor) + klass->paint_decor (theme, decor); +} + +void +mb_wm_theme_paint_button (MBWMTheme *theme, MBWMDecorButton *button) +{ + MBWMThemeClass *klass; + + if (!theme) + return; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->paint_button) + klass->paint_button (theme, button); +} + +Bool +mb_wm_theme_supports (MBWMTheme *theme, MBWMThemeCaps capability) +{ + if (theme) + return False; + + return ((capability & theme->caps) != False); +} + +typedef enum +{ + XML_CTX_UNKNOWN = 0, + XML_CTX_THEME, + XML_CTX_CLIENT, + XML_CTX_DECOR, + XML_CTX_BUTTON, + XML_CTX_IMG, +} XmlCtx; + +struct stack_data +{ + XmlCtx ctx; + void *data; +}; + +struct expat_data +{ + XML_Parser par; + int theme_type; + int version; + MBWMList *xml_clients; + char *img; + MBWMList *stack; + MBWMColor color_lowlight; + MBWMColor color_shadow; + MBWMCompMgrShadowType shadow_type; + Bool compositing; + Bool shaped; +}; + +MBWMTheme * +mb_wm_theme_new (MBWindowManager * wm, const char * theme_path) +{ + MBWMTheme *theme = NULL; + int theme_type = 0; + char *path = NULL; + char buf[256]; + XML_Parser par = NULL; + FILE *file = NULL; + MBWMList *xml_clients = NULL; + char *img = NULL; + MBWMColor clr_lowlight; + MBWMColor clr_shadow; + MBWMCompMgrShadowType shadow_type; + Bool compositing; + Bool shaped; + struct stat st; + + /* + * If no theme specified, we try to load the default one, if that fails, + * we automatically fallback on the built-in defaults. + */ + if (!theme_path) + theme_path = "Default"; + + /* Attempt to parse the xml theme, if any, retrieving the theme type + * + * NB: We cannot do this in the _init function, since we need to know the + * type *before* we can create the underlying object on which the + * init method operates. + */ + + if (*theme_path == '/') + { + if (!stat (theme_path, &st)) + path = (char *) theme_path; + } + else + { + const char *home = getenv("HOME"); + int size; + + if (home) + { + const char *fmt = "%s/.themes/%s/matchbox2/theme.xml"; + + size = strlen (theme_path) + strlen (fmt) + strlen (home); + path = alloca (size); + snprintf (path, size, fmt, home, theme_path); + + if (stat (path, &st)) + path = NULL; + } + + if (!path) + { + const char * fmt = "%s/themes/%s/matchbox2/theme.xml"; + + size = strlen (theme_path) + strlen (fmt) + strlen (DATADIR); + path = alloca (size); + snprintf (path, size, fmt, DATADIR, theme_path); + + if (stat (path, &st)) + path = NULL; + } + } + + if (path) + { + struct expat_data udata; + + if (!(file = fopen (path, "r")) || + !(par = XML_ParserCreate(NULL))) + { + goto default_theme; + } + + memset (&udata, 0, sizeof (struct expat_data)); + udata.compositing = True; + udata.par = par; + + XML_SetElementHandler (par, + xml_element_start_cb, + xml_element_end_cb); + + XML_SetUserData(par, (void *)&udata); + + while (fgets (buf, sizeof (buf), file) && + XML_Parse(par, buf, strlen(buf), 0)); + + XML_Parse(par, NULL, 0, 1); + + if (udata.version == 2) + { + theme_type = udata.theme_type; + xml_clients = udata.xml_clients; + + if (udata.img) + { + if (*udata.img == '/') + img = udata.img; + else + { + int len = strlen (path) + strlen (udata.img); + char * s; + char * p = malloc (len + 1); + strncpy (p, path, len); + + s = strrchr (p, '/'); + + if (s) + { + *(s+1) = 0; + strcat (p, udata.img); + } + else + { + strncpy (p, udata.img, len); + } + + img = p; + free (udata.img); + } + } + } + + clr_lowlight.r = udata.color_lowlight.r; + clr_lowlight.g = udata.color_lowlight.g; + clr_lowlight.b = udata.color_lowlight.b; + clr_lowlight.a = udata.color_lowlight.a; + clr_lowlight.set = udata.color_lowlight.set; + + clr_shadow.r = udata.color_shadow.r; + clr_shadow.g = udata.color_shadow.g; + clr_shadow.b = udata.color_shadow.b; + clr_shadow.a = udata.color_shadow.a; + clr_shadow.set = udata.color_shadow.set; + + shadow_type = udata.shadow_type; + compositing = udata.compositing; + shaped = udata.shaped; + + xml_stack_free (udata.stack); + } + + if (custom_theme_alloc_func) + { + theme = + custom_theme_alloc_func (theme_type, + MBWMObjectPropWm, wm, + MBWMObjectPropThemePath, path, + MBWMObjectPropThemeImg, img, + MBWMObjectPropThemeXmlClients, xml_clients, + MBWMObjectPropThemeColorLowlight, &clr_lowlight, + MBWMObjectPropThemeColorShadow, &clr_shadow, + MBWMObjectPropThemeShadowType, shadow_type, + MBWMObjectPropThemeCompositing, compositing, + MBWMObjectPropThemeShaped, shaped, + NULL); + } + else if (theme_type) + { + theme = + MB_WM_THEME (mb_wm_object_new (theme_type, + MBWMObjectPropWm, wm, + MBWMObjectPropThemePath, path, + MBWMObjectPropThemeImg, img, + MBWMObjectPropThemeXmlClients, xml_clients, + MBWMObjectPropThemeColorLowlight, &clr_lowlight, + MBWMObjectPropThemeColorShadow, &clr_shadow, + MBWMObjectPropThemeShadowType, shadow_type, + MBWMObjectPropThemeCompositing, compositing, + MBWMObjectPropThemeShaped, shaped, + NULL)); + } + + default_theme: + + if (!theme) + { + theme = MB_WM_THEME (mb_wm_object_new ( + MB_WM_TYPE_THEME, + MBWMObjectPropWm, wm, + MBWMObjectPropThemeXmlClients, xml_clients, + MBWMObjectPropThemeColorLowlight, &clr_lowlight, + MBWMObjectPropThemeColorShadow, &clr_shadow, + MBWMObjectPropThemeShadowType, shadow_type, + MBWMObjectPropThemeCompositing, compositing, + MBWMObjectPropThemeShaped, shaped, + NULL)); + } + + if (par) + XML_ParserFree (par); + + if (file) + fclose (file); + + if (img) + free (img); + + return theme; +} + +MBWMDecor * +mb_wm_theme_create_decor (MBWMTheme *theme, + MBWindowManagerClient *client, + MBWMDecorType type) +{ + MBWMThemeClass *klass; + + MBWM_ASSERT (client); + + if (!theme || !client) + return NULL; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->create_decor) + return klass->create_decor (theme, client, type); + + return NULL; +} + +void +mb_wm_theme_resize_decor (MBWMTheme *theme, MBWMDecor *decor) +{ + MBWMThemeClass *klass; + + MBWM_ASSERT (decor); + + if (!theme || !decor) + return; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->resize_decor) + klass->resize_decor (theme, decor); +} + +MBWMClientLayoutHints +mb_wm_theme_get_client_layout_hints (MBWMTheme * theme, + MBWindowManagerClient * client) +{ + MBWMXmlClient * c; + MBWMClientType c_type; + + if (!client || !theme) + return 0; + + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if (!theme->xml_clients || + !(c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type))) + { + return 0; + } + + return c->layout_hints; +} + +/* + * Returns True if the theme prescribes at least one value for the geometry + */ +Bool +mb_wm_theme_get_client_geometry (MBWMTheme * theme, + MBWindowManagerClient * client, + MBGeometry * geom) +{ + MBWMXmlClient * c; + MBWMClientType c_type; + + if (!geom || !client || !theme) + return False; + + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if (!theme || !theme->xml_clients || + !(c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) || + (c->x < 0 && c->y < 0 && c->width < 0 && c->height < 0)) + { + return False; + } + + geom->x = c->x; + geom->y = c->y; + geom->width = c->width; + geom->height = c->height; + + return True; +} + +Bool +mb_wm_theme_is_client_shaped (MBWMTheme * theme, + MBWindowManagerClient * client) +{ +#ifdef HAVE_XEXT + MBWMXmlClient * c; + MBWMClientType c_type; + + if (!client || !theme || !theme->shaped || client->is_argb32) + return False; + + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if (theme->xml_clients && + (c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type))) + { + return c->shaped; + } + + return False; +#else + return False; +#endif +} + +/* + * Retrieves color to be used for lowlighting (16-bit rgba) + */ +void +mb_wm_theme_get_lowlight_color (MBWMTheme * theme, + unsigned int * red, + unsigned int * green, + unsigned int * blue, + unsigned int * alpha) +{ + if (theme) + { + if (red) + *red = (unsigned int)(theme->color_lowlight.r * (double)0xffff); + + if (green) + *green = (unsigned int)(theme->color_lowlight.g * (double)0xffff); + + if (blue) + *blue = (unsigned int)(theme->color_lowlight.b * (double)0xffff); + + if (alpha) + *alpha = (unsigned int)(theme->color_lowlight.a * (double)0xffff); + + return; + } + + if (red) + *red = 0; + + if (green) + *green = 0; + + if (blue) + *blue = 0; + + if (*alpha) + *alpha = 0x8d8d; +} + +/* + * Retrieves color to be used for lowlighting (16-bit rgba) + */ +void +mb_wm_theme_get_shadow_color (MBWMTheme * theme, + unsigned int * red, + unsigned int * green, + unsigned int * blue, + unsigned int * alpha) +{ + if (theme) + { + if (red) + *red = (unsigned int)(theme->color_shadow.r * (double)0xffff); + + if (green) + *green = (unsigned int)(theme->color_shadow.g * (double)0xffff); + + if (blue) + *blue = (unsigned int)(theme->color_shadow.b * (double)0xffff); + + if (alpha) + *alpha = (unsigned int)(theme->color_shadow.a * (double)0xffff); + + return; + } + + if (red) + *red = 0; + + if (green) + *green = 0; + + if (blue) + *blue = 0; + + if (*alpha) + *alpha = 0xff00; +} + +MBWMCompMgrShadowType +mb_wm_theme_get_shadow_type (MBWMTheme * theme) +{ + if (!theme) + return MBWM_COMP_MGR_SHADOW_NONE; + + return theme->shadow_type; +} + +Bool +mb_wm_theme_use_compositing_mgr (MBWMTheme * theme) +{ + if (!theme) + return False; + + return theme->compositing; +} + +/* + * Expat callback stuff + */ + +static void +xml_stack_push (MBWMList ** stack, XmlCtx ctx) +{ + struct stack_data * s = malloc (sizeof (struct stack_data)); + + s->ctx = ctx; + s->data = NULL; + + *stack = mb_wm_util_list_prepend (*stack, s); +} + +static XmlCtx +xml_stack_top_ctx (MBWMList *stack) +{ + struct stack_data * s = stack->data; + + return s->ctx; +} + +static void * +xml_stack_top_data (MBWMList *stack) +{ + struct stack_data * s = stack->data; + + return s->data; +} + +static void +xml_stack_top_set_data (MBWMList *stack, void * data) +{ + struct stack_data * s = stack->data; + + s->data = data; +} + +static void +xml_stack_pop (MBWMList ** stack) +{ + MBWMList * top = *stack; + struct stack_data * s = top->data; + + *stack = top->next; + free (s); + free (top); +} + +static void +xml_stack_free (MBWMList *stack) +{ + MBWMList * l = stack; + while (l) + { + MBWMList * n = l->next; + free (l->data); + free (l); + + l = n; + } +} + +static void +xml_element_start_cb (void *data, const char *tag, const char **expat_attr) +{ + struct expat_data * exd = data; + + MBWM_DBG ("tag <%s>\n", tag); + + if (!strcmp (tag, "theme")) + { + MBWMColor clr; + const char ** p = expat_attr; + + xml_stack_push (&exd->stack, XML_CTX_THEME); + + while (*p) + { + if (!strcmp (*p, "engine-version")) + exd->version = atoi (*(p+1)); + else if (!strcmp (*p, "engine-type")) + { + if (!strcmp (*(p+1), "default")) + exd->theme_type = MB_WM_TYPE_THEME; +#if THEME_PNG + else if (!strcmp (*(p+1), "png")) + exd->theme_type = MB_WM_TYPE_THEME_PNG; +#endif + else if (custom_theme_type_func) + exd->theme_type = custom_theme_type_func (*(p+1), + custom_theme_type_func_data); + } + else if (!strcmp (*p, "shaped")) + { + if (!strcmp (*(p+1), "yes") || !strcmp (*(p+1), "1")) + exd->shaped = 1; + } + else if (!strcmp (*p, "color-shadow")) + { + mb_wm_xml_clr_from_string (&clr, *(p+1)); + + if (clr.set) + { + exd->color_shadow.r = clr.r; + exd->color_shadow.g = clr.g; + exd->color_shadow.b = clr.b; + exd->color_shadow.a = clr.a; + exd->color_shadow.set = True; + } + } + else if (!strcmp (*p, "color-lowlight")) + { + mb_wm_xml_clr_from_string (&clr, *(p+1)); + + if (clr.set) + { + exd->color_lowlight.r = clr.r; + exd->color_lowlight.g = clr.g; + exd->color_lowlight.b = clr.b; + exd->color_lowlight.a = clr.a; + exd->color_lowlight.set = True; + } + } + else if (!strcmp (*p, "shadow-type")) + { + if (!strcmp (*(p+1), "simple")) + exd->shadow_type = MBWM_COMP_MGR_SHADOW_SIMPLE; + else if (!strcmp (*(p+1), "gaussian")) + exd->shadow_type = MBWM_COMP_MGR_SHADOW_GAUSSIAN; + } + else if (!strcmp (*p, "compositing")) + { + if (!strcmp (*(p+1), "yes") || !strcmp (*(p+1), "1")) + exd->compositing = True; + else + exd->compositing = False; + } + + p += 2; + } + } + + if (!strcmp (tag, "client")) + { + MBWMXmlClient * c = mb_wm_xml_client_new (); + const char **p = expat_attr; + + XmlCtx ctx = xml_stack_top_ctx (exd->stack); + + xml_stack_push (&exd->stack, XML_CTX_CLIENT); + + if (ctx != XML_CTX_THEME) + { + MBWM_DBG ("Expected context theme"); + return; + } + + while (*p) + { + if (!strcmp (*p, "type")) + { + if (!strcmp (*(p+1), "app")) + c->type = MBWMClientTypeApp; + else if (!strcmp (*(p+1), "dialog")) + c->type = MBWMClientTypeDialog; + else if (!strcmp (*(p+1), "panel")) + c->type = MBWMClientTypePanel; + else if (!strcmp (*(p+1), "input")) + c->type = MBWMClientTypeInput; + else if (!strcmp (*(p+1), "desktop")) + c->type = MBWMClientTypeDesktop; + else if (!strcmp (*(p+1), "notification")) + c->type = MBWMClientTypeNote; + else if (custom_client_type_func) + c->type = custom_client_type_func (*(p+1), + custom_client_type_func_data); + } + else if (!strcmp (*p, "shaped")) + { + if (!strcmp (*(p+1), "yes") || !strcmp (*(p+1), "1")) + c->shaped = 1; + } + else if (!strcmp (*p, "width")) + { + c->width = atoi (*(p+1)); + } + else if (!strcmp (*p, "height")) + { + c->height = atoi (*(p+1)); + } + else if (!strcmp (*p, "x")) + { + c->x = atoi (*(p+1)); + } + else if (!strcmp (*p, "y")) + { + c->y = atoi (*(p+1)); + } + else if (!strcmp (*p, "layout-hints") && *(p+1)) + { + /* comma-separate list of hints */ + char * duph = strdup (*(p+1)); + char * comma; + char * h = duph; + + while (h) + { + comma = strchr (h, ','); + + if (comma) + *comma = 0; + + if (!strcmp (h, "reserve-edge-north")) + { + c->layout_hints |= LayoutPrefReserveEdgeNorth; + } + else if (!strcmp (h, "reserve-edge-south")) + { + c->layout_hints |= LayoutPrefReserveEdgeSouth; + } + else if (!strcmp (h, "reserve-edge-west")) + { + c->layout_hints |= LayoutPrefReserveEdgeWest; + } + else if (!strcmp (h, "reserve-edge-east")) + { + c->layout_hints |= LayoutPrefReserveEdgeEast; + } + if (!strcmp (h, "reserve-north")) + { + c->layout_hints |= LayoutPrefReserveNorth; + } + else if (!strcmp (h, "reserve-south")) + { + c->layout_hints |= LayoutPrefReserveSouth; + } + else if (!strcmp (h, "reserve-west")) + { + c->layout_hints |= LayoutPrefReserveWest; + } + else if (!strcmp (h, "reserve-east")) + { + c->layout_hints |= LayoutPrefReserveEast; + } + else if (!strcmp (h, "grow")) + { + c->layout_hints |= LayoutPrefGrowToFreeSpace; + } + else if (!strcmp (h, "free")) + { + c->layout_hints |= LayoutPrefPositionFree; + } + else if (!strcmp (h, "full-screen")) + { + c->layout_hints |= LayoutPrefFullscreen; + } + else if (!strcmp (h, "fixed-x")) + { + c->layout_hints |= LayoutPrefFixedX; + } + else if (!strcmp (h, "fixed-y")) + { + c->layout_hints |= LayoutPrefFixedY; + } + else if (!strcmp (h, "overlaps")) + { + c->layout_hints |= LayoutPrefOverlaps; + } + + if (comma) + h = comma + 1; + else + break; + } + + free (duph); + } + + p += 2; + } + + if (!c->type) + mb_wm_xml_client_free (c); + else + { + exd->xml_clients = mb_wm_util_list_prepend (exd->xml_clients, c); + xml_stack_top_set_data (exd->stack, c); + } + + + return; + } + + if (!strcmp (tag, "decor")) + { + MBWMXmlDecor * d = mb_wm_xml_decor_new (); + const char **p = expat_attr; + XmlCtx ctx = xml_stack_top_ctx (exd->stack); + MBWMXmlClient * c = xml_stack_top_data (exd->stack); + + xml_stack_push (&exd->stack, XML_CTX_DECOR); + + if (ctx != XML_CTX_CLIENT || !c) + { + MBWM_DBG ("Expected context client"); + return; + } + + while (*p) + { + if (!strcmp (*p, "color-fg")) + mb_wm_xml_clr_from_string (&d->clr_fg, *(p+1)); + else if (!strcmp (*p, "color-bg")) + mb_wm_xml_clr_from_string (&d->clr_bg, *(p+1)); + else if (!strcmp (*p, "type")) + { + if (!strcmp (*(p+1), "north")) + d->type = MBWMDecorTypeNorth; + else if (!strcmp (*(p+1), "south")) + d->type = MBWMDecorTypeSouth; + else if (!strcmp (*(p+1), "east")) + d->type = MBWMDecorTypeEast; + else if (!strcmp (*(p+1), "west")) + d->type = MBWMDecorTypeWest; + } + else if (!strcmp (*p, "template-width")) + { + d->width = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-height")) + { + d->height = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-x")) + { + d->x = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-y")) + { + d->y = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-pad-offset")) + { + d->pad_offset = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-pad-length")) + { + d->pad_length = atoi (*(p+1)); + } + else if (!strcmp (*p, "font-size")) + { + char * end_size = NULL; + + d->font_units = MBWMXmlFontUnitsPixels; + d->font_size = strtol (*(p+1), &end_size, 0); + + if (end_size && *end_size) + { + if (*end_size == 'p') + { + switch (*(end_size+1)) + { + case 't': + d->font_units = MBWMXmlFontUnitsPoints; + break; + case 'x': + default: + ; + } + } + } + } + else if (!strcmp (*p, "font-family")) + { + d->font_family = strdup (*(p+1)); + } + else if (!strcmp (*p, "show-title")) + { + if (!strcmp (*(p+1), "yes") || !strcmp (*(p+1), "1")) + d->show_title = 1; + } + + p += 2; + } + + if (!d->type) + mb_wm_xml_decor_free (d); + else + { + c->decors = mb_wm_util_list_prepend (c->decors, d); + xml_stack_top_set_data (exd->stack, d); + } + + return; + } + + if (!strcmp (tag, "button")) + { + MBWMXmlButton * b = mb_wm_xml_button_new (); + const char **p = expat_attr; + XmlCtx ctx = xml_stack_top_ctx (exd->stack); + MBWMXmlDecor * d = xml_stack_top_data (exd->stack); + + xml_stack_push (&exd->stack, XML_CTX_BUTTON); + + if (ctx != XML_CTX_DECOR || !d) + { + MBWM_DBG ("Expected context decor"); + return; + } + + while (*p) + { + if (!strcmp (*p, "color-fg")) + mb_wm_xml_clr_from_string (&b->clr_fg, *(p+1)); + else if (!strcmp (*p, "color-bg")) + mb_wm_xml_clr_from_string (&b->clr_bg, *(p+1)); + else if (!strcmp (*p, "type")) + { + if (!strcmp (*(p+1), "minimize")) + b->type = MBWMDecorButtonMinimize; + else if (!strcmp (*(p+1), "close")) + b->type = MBWMDecorButtonClose; + else if (!strcmp (*(p+1), "menu")) + b->type = MBWMDecorButtonMenu; + else if (!strcmp (*(p+1), "accept")) + b->type = MBWMDecorButtonAccept; + else if (!strcmp (*(p+1), "fullscreen")) + b->type = MBWMDecorButtonFullscreen; + else if (!strcmp (*(p+1), "help")) + b->type = MBWMDecorButtonHelp; + else if (custom_button_type_func) + b->type = custom_button_type_func (*(p+1), + custom_button_type_func_data); + } + else if (!strcmp (*p, "packing")) + { + if (!strcmp (*(p+1), "end")) + b->packing = MBWMDecorButtonPackEnd; + else if (!strcmp (*(p+1), "start")) + b->packing = MBWMDecorButtonPackStart; + } + else if (!strcmp (*p, "template-x")) + { + b->x = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-y")) + { + b->y = atoi (*(p+1)); + } + else if (!strcmp (*p, "width")) + { + b->width = atoi (*(p+1)); + } + else if (!strcmp (*p, "height")) + { + b->height = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-active-x")) + { + b->active_x = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-active-y")) + { + b->active_y = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-inactive-x")) + { + b->inactive_x = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-inactive-y")) + { + b->inactive_y = atoi (*(p+1)); + } + else if (!strcmp (*p, "press-activated")) + { + if (!strcmp (*(p+1), "yes") || !strcmp (*(p+1), "1")) + b->press_activated = 1; + } + + p += 2; + } + + if (!b->type) + { + mb_wm_xml_button_free (b); + return; + } + + d->buttons = mb_wm_util_list_append (d->buttons, b); + + xml_stack_top_set_data (exd->stack, b); + + return; + } + + if (!strcmp (tag, "img")) + { + const char **p = expat_attr; + XmlCtx ctx = xml_stack_top_ctx (exd->stack); + + xml_stack_push (&exd->stack, XML_CTX_IMG); + + if (ctx != XML_CTX_THEME) + { + MBWM_DBG ("Expected context theme"); + return; + } + + while (*p) + { + if (!strcmp (*p, "src")) + { + exd->img = strdup (*(p+1)); + return; + } + + p += 2; + } + + return; + } + +} + +static void +xml_element_end_cb (void *data, const char *tag) +{ + struct expat_data * exd = data; + + XmlCtx ctx = xml_stack_top_ctx (exd->stack); + + MBWM_DBG ("tag </%s>\n", tag); + + if (!strcmp (tag, "theme")) + { + XML_StopParser (exd->par, 0); + } + else if (!strcmp (tag, "client")) + { + if (ctx == XML_CTX_CLIENT) + { + xml_stack_pop (&exd->stack); + } + else + MBWM_DBG ("Expected client on the top of the stack!"); + } + else if (!strcmp (tag, "decor")) + { + if (ctx == XML_CTX_DECOR) + { + xml_stack_pop (&exd->stack); + } + else + MBWM_DBG ("Expected decor on the top of the stack!"); + } + else if (!strcmp (tag, "button")) + { + if (ctx == XML_CTX_BUTTON) + { + xml_stack_pop (&exd->stack); + } + else + MBWM_DBG ("Expected button on the top of the stack!"); + } + else if (!strcmp (tag, "img")) + { + if (ctx == XML_CTX_IMG) + { + xml_stack_pop (&exd->stack); + } + else + MBWM_DBG ("Expected img on the top of the stack!"); + } +} + + + +static void +construct_buttons (MBWMTheme * theme, + MBWMDecor * decor, MBWMXmlDecor *d) +{ + MBWindowManagerClient *client = decor->parent_client; + MBWindowManager *wm = client->wmref; + MBWMDecorButton *button; + + if (d) + { + MBWMList * l = d->buttons; + while (l) + { + MBWMXmlButton * b = l->data; + + button = mb_wm_decor_button_stock_new (wm, + b->type, + b->packing, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + l = l->next; + } + + return; + } + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonClose, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + +#if 0 + /* + * We probably do not want this in the default client, but for now + * it is useful for testing purposes + */ + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonFullscreen, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonHelp, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonAccept, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonMinimize, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonMenu, + MBWMDecorButtonPackStart, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); +#endif +} + +struct DecorData +{ + Pixmap xpix; + XftDraw *xftdraw; + XftColor clr; + XftFont *font; +}; + +static void +decordata_free (MBWMDecor * decor, void *data) +{ + struct DecorData * dd = data; + Display * xdpy = decor->parent_client->wmref->xdpy; + + XFreePixmap (xdpy, dd->xpix); + + XftDrawDestroy (dd->xftdraw); + + if (dd->font) + XftFontClose (xdpy, dd->font); + + free (dd); +} + +static MBWMDecor * +mb_wm_theme_simple_create_decor (MBWMTheme *theme, + MBWindowManagerClient *client, + MBWMDecorType type) +{ + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMDecor *decor = NULL; + MBWindowManager *wm = client->wmref; + MBWMXmlClient *c; + + if (MB_WM_THEME (theme)->xml_clients && + (c = mb_wm_xml_client_find_by_type (MB_WM_THEME (theme)->xml_clients, + c_type))) + { + MBWMXmlDecor *d; + + d = mb_wm_xml_decor_find_by_type (c->decors, type); + + if (d) + { + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + construct_buttons (theme, decor, d); + } + + return decor; + } + + switch (c_type) + { + case MBWMClientTypeApp: + switch (type) + { + case MBWMDecorTypeNorth: + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + construct_buttons (theme, decor, NULL); + break; + default: + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + } + break; + + case MBWMClientTypeDialog: + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + break; + + case MBWMClientTypePanel: + case MBWMClientTypeDesktop: + case MBWMClientTypeInput: + default: + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + } + + return decor; +} + +static void +mb_wm_theme_simple_get_button_size (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *width, + int *height) +{ + MBWindowManagerClient * client = decor->parent_client; + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMXmlClient * c; + MBWMXmlDecor * d; + + /* FIXME -- assumes button on the north decor only */ + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type))) + { + MBWMXmlButton * b = mb_wm_xml_button_find_by_type (d->buttons, type); + + if (b) + { + if (width) + *width = b->width; + + if (height) + *height = b->height; + + return; + } + } + + /* + * These are defaults when no theme description was loaded + */ + switch (c_type) + { + case MBWMClientTypeApp: + case MBWMClientTypeDialog: + case MBWMClientTypePanel: + case MBWMClientTypeDesktop: + case MBWMClientTypeInput: + default: + if (width) + *width = SIMPLE_FRAME_TITLEBAR_HEIGHT-4; + + if (height) + *height = SIMPLE_FRAME_TITLEBAR_HEIGHT-4; + } +} + +static void +mb_wm_theme_simple_get_button_position (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *x, + int *y) +{ + MBWindowManagerClient * client = decor->parent_client; + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMXmlClient * c; + MBWMXmlDecor * d; + + /* FIXME -- assumes button on the north decor only */ + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type))) + { + MBWMXmlButton * b = mb_wm_xml_button_find_by_type (d->buttons, type); + + if (b) + { + if (x) + if (b->x >= 0) + *x = b->x; + else + *x = 2; + + if (y) + if (b->y >= 0) + *y = b->y; + else + *y = 2; + + return; + } + } + + if (x) + *x = 2; + + if (y) + *y = 2; +} + +static void +mb_wm_theme_simple_get_decor_dimensions (MBWMTheme *theme, + MBWindowManagerClient *client, + int *north, + int *south, + int *west, + int *east) +{ + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMXmlClient * c; + MBWMXmlDecor * d; + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type))) + { + if (north) + if ((d = mb_wm_xml_decor_find_by_type (c->decors,MBWMDecorTypeNorth))) + *north = d->height; + else + *north = SIMPLE_FRAME_TITLEBAR_HEIGHT; + + if (south) + if ((d = mb_wm_xml_decor_find_by_type (c->decors,MBWMDecorTypeSouth))) + *south = d->height; + else + *south = SIMPLE_FRAME_EDGE_SIZE; + + if (west) + if ((d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeWest))) + *west = d->width; + else + *west = SIMPLE_FRAME_EDGE_SIZE; + + if (east) + if ((d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeEast))) + *east = d->width; + else + *east = SIMPLE_FRAME_EDGE_SIZE; + + return; + } + + /* + * These are defaults when no theme description was loaded + */ + switch (c_type) + { + case MBWMClientTypeDialog: + case MBWMClientTypeApp: + if (north) + *north = SIMPLE_FRAME_TITLEBAR_HEIGHT; + + if (south) + *south = SIMPLE_FRAME_EDGE_SIZE; + + if (west) + *west = SIMPLE_FRAME_EDGE_SIZE; + + if (east) + *east = SIMPLE_FRAME_EDGE_SIZE; + break; + + case MBWMClientTypePanel: + case MBWMClientTypeDesktop: + case MBWMClientTypeInput: + default: + if (north) + *north = 0; + + if (south) + *south = 0; + + if (west) + *west = 0; + + if (east) + *east = 0; + } +} + +static unsigned long +pixel_from_clr (Display * dpy, int screen, MBWMColor * clr) +{ + XColor xcol; + + xcol.red = (int)(clr->r * (double)0xffff); + xcol.green = (int)(clr->g * (double)0xffff); + xcol.blue = (int)(clr->b * (double)0xffff); + xcol.flags = DoRed|DoGreen|DoBlue; + + XAllocColor (dpy, DefaultColormap (dpy, screen), &xcol); + + return xcol.pixel; +} + +static XftFont * +xft_load_font (MBWMDecor * decor, MBWMXmlDecor *d) +{ + char desc[512]; + XftFont * font; + Display * xdpy = decor->parent_client->wmref->xdpy; + int xscreen = decor->parent_client->wmref->xscreen; + int font_size; + + font_size = d && d->font_size ? d->font_size : SIMPLE_FRAME_TITLEBAR_HEIGHT / 2; + + if (!d || d->font_units == MBWMXmlFontUnitsPixels) + { + font_size = mb_wm_util_pixels_to_points (decor->parent_client->wmref, + font_size); + } + + snprintf (desc, sizeof (desc), "%s-%i", + d && d->font_family ? d->font_family : "Sans", + font_size); + + font = XftFontOpenName (xdpy, xscreen, desc); + + return font; +} + +static void +mb_wm_theme_simple_paint_decor (MBWMTheme *theme, MBWMDecor *decor) +{ + MBWMDecorType type; + const MBGeometry *geom; + MBWindowManagerClient *client; + Window xwin; + MBWindowManager *wm = theme->wm; + MBWMColor clr_bg; + MBWMColor clr_fg; + MBWMClientType c_type; + MBWMXmlClient *c = NULL; + MBWMXmlDecor *d = NULL; + struct DecorData *dd; + int x, y, w, h; + GC gc; + Display *xdpy = wm->xdpy; + int xscreen = wm->xscreen; + const char *title; + + clr_fg.r = 1.0; + clr_fg.g = 1.0; + clr_fg.b = 1.0; + + clr_bg.r = 0.5; + clr_bg.g = 0.5; + clr_bg.b = 0.5; + + client = mb_wm_decor_get_parent (decor); + xwin = mb_wm_decor_get_x_window (decor); + + if (client == NULL || xwin == None) + return; + + dd = mb_wm_decor_get_theme_data (decor); + + type = mb_wm_decor_get_type (decor); + geom = mb_wm_decor_get_geometry (decor); + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type))) + { + if (d->clr_fg.set) + { + clr_fg.r = d->clr_fg.r; + clr_fg.g = d->clr_fg.g; + clr_fg.b = d->clr_fg.b; + } + + if (d->clr_bg.set) + { + clr_bg.r = d->clr_bg.r; + clr_bg.g = d->clr_bg.g; + clr_bg.b = d->clr_bg.b; + } + } + + if (!dd) + { + XRenderColor rclr; + + dd = malloc (sizeof (struct DecorData)); + dd->xpix = XCreatePixmap(xdpy, xwin, + decor->geom.width, decor->geom.height, + DefaultDepth(xdpy, xscreen)); + + dd->xftdraw = XftDrawCreate (xdpy, dd->xpix, + DefaultVisual (xdpy, xscreen), + DefaultColormap (xdpy, xscreen)); + + rclr.red = (int)(clr_fg.r * (double)0xffff); + rclr.green = (int)(clr_fg.g * (double)0xffff); + rclr.blue = (int)(clr_fg.b * (double)0xffff); + rclr.alpha = 0xffff; + + XftColorAllocValue (xdpy, DefaultVisual (xdpy, xscreen), + DefaultColormap (xdpy, xscreen), + &rclr, &dd->clr); + + dd->font = xft_load_font (decor, d); + + XSetWindowBackgroundPixmap(xdpy, xwin, dd->xpix); + + mb_wm_decor_set_theme_data (decor, dd, decordata_free); + } + + gc = XCreateGC (xdpy, dd->xpix, 0, NULL); + + XSetLineAttributes (xdpy, gc, 1, LineSolid, CapProjecting, JoinMiter); + XSetBackground (xdpy, gc, pixel_from_clr (xdpy, xscreen, &clr_bg)); + XSetForeground (xdpy, gc, pixel_from_clr (xdpy, xscreen, &clr_bg)); + + w = geom->width; h = geom->height; x = geom->x; y = geom->y; + + XFillRectangle (xdpy, dd->xpix, gc, 0, 0, w, h); + + if (mb_wm_decor_get_type(decor) == MBWMDecorTypeNorth && + (title = mb_wm_client_get_name (client))) + { + XRectangle rec; + + int pack_start_x = mb_wm_decor_get_pack_start_x (decor); + int pack_end_x = mb_wm_decor_get_pack_end_x (decor); + int west_width = mb_wm_client_frame_west_width (client); + int y = (decor->geom.height - + (dd->font->ascent + dd->font->descent)) / 2 + + dd->font->ascent; + + rec.x = 0; + rec.y = 0; + rec.width = pack_end_x - 2; + rec.height = d ? d->height : SIMPLE_FRAME_TITLEBAR_HEIGHT; + + XftDrawSetClipRectangles (dd->xftdraw, 0, 0, &rec, 1); + + XftDrawStringUtf8(dd->xftdraw, + &dd->clr, + dd->font, + west_width + pack_start_x + (h / 5), y, + title, strlen (title)); + } + + XFreeGC (xdpy, gc); + + XClearWindow (xdpy, xwin); +} + +static void +mb_wm_theme_simple_paint_button (MBWMTheme *theme, MBWMDecorButton *button) +{ + MBWMDecor *decor; + MBWindowManagerClient *client; + Window xwin; + MBWindowManager *wm = theme->wm; + int x, y, w, h; + MBWMColor clr_bg; + MBWMColor clr_fg; + MBWMClientType c_type; + MBWMXmlClient *c = NULL; + MBWMXmlDecor *d = NULL; + MBWMXmlButton *b = NULL; + struct DecorData * dd; + GC gc; + Display *xdpy = wm->xdpy; + int xscreen = wm->xscreen; + + clr_fg.r = 1.0; + clr_fg.g = 1.0; + clr_fg.b = 1.0; + + clr_bg.r = 0.0; + clr_bg.g = 0.0; + clr_bg.b = 0.0; + + decor = button->decor; + client = mb_wm_decor_get_parent (decor); + xwin = decor->xwin; + dd = mb_wm_decor_get_theme_data (decor); + + if (client == NULL || xwin == None || dd->xpix == None) + return; + + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type)) && + (b = mb_wm_xml_button_find_by_type (d->buttons, button->type))) + { + clr_fg.r = b->clr_fg.r; + clr_fg.g = b->clr_fg.g; + clr_fg.b = b->clr_fg.b; + + clr_bg.r = b->clr_bg.r; + clr_bg.g = b->clr_bg.g; + clr_bg.b = b->clr_bg.b; + } + + w = button->geom.width; + h = button->geom.height; + x = button->geom.x; + y = button->geom.y; + + gc = XCreateGC (xdpy, dd->xpix, 0, NULL); + + XSetLineAttributes (xdpy, gc, 1, LineSolid, CapRound, JoinRound); + + + + if (button->state == MBWMDecorButtonStateInactive) + { + XSetForeground (xdpy, gc, pixel_from_clr (xdpy, xscreen, &clr_bg)); + } + else + { + /* FIXME -- think of a better way of doing this */ + MBWMColor clr; + clr.r = clr_bg.r + 0.2; + clr.g = clr_bg.g + 0.2; + clr.b = clr_bg.b + 0.2; + + XSetForeground (xdpy, gc, pixel_from_clr (xdpy, xscreen, &clr)); + } + + XFillRectangle (xdpy, dd->xpix, gc, x, y, w+1, h+1); + + XSetLineAttributes (xdpy, gc, 3, LineSolid, CapRound, JoinRound); + XSetForeground (xdpy, gc, pixel_from_clr (xdpy, xscreen, &clr_fg)); + + if (button->type == MBWMDecorButtonClose) + { + XDrawLine (xdpy, dd->xpix, gc, x + 3, y + 3, x + w - 3, y + h - 3); + XDrawLine (xdpy, dd->xpix, gc, x + 3, y + h - 3, x + w - 3, y + 3); + } + else if (button->type == MBWMDecorButtonFullscreen) + { + XDrawLine (xdpy, dd->xpix, gc, x + 3, y + 3, x + 3, y + h - 3); + XDrawLine (xdpy, dd->xpix, gc, x + 3, y + h - 3, x + w - 3, y + h - 3); + XDrawLine (xdpy, dd->xpix, gc, x + w - 3, y + h - 3, x + w - 3, y + 3); + XDrawLine (xdpy, dd->xpix, gc, x + w - 3, y + 3, x + 3, y + 3); + } + else if (button->type == MBWMDecorButtonMinimize) + { + XDrawLine (xdpy, dd->xpix, gc, x + 3, y + h - 5, x + w - 3, y + h - 5); + } + else if (button->type == MBWMDecorButtonHelp) + { + char desc[512]; + XftFont *font; + XRenderColor rclr; + XftColor clr; + XRectangle rec; + + snprintf (desc, sizeof (desc), "%s-%i:bold", + d && d->font_family ? d->font_family : "Sans", h*3/4); + + font = XftFontOpenName (xdpy, xscreen, desc); + + rclr.red = (int)(clr_fg.r * (double)0xffff); + rclr.green = (int)(clr_fg.g * (double)0xffff); + rclr.blue = (int)(clr_fg.b * (double)0xffff); + rclr.alpha = 0xffff; + + XftColorAllocValue (xdpy, DefaultVisual (xdpy, xscreen), + DefaultColormap (xdpy, xscreen), + &rclr, &clr); + + rec.x = x; + rec.y = y; + rec.width = w; + rec.height = h; + + XftDrawSetClipRectangles (dd->xftdraw, 0, 0, &rec, 1); + + XftDrawStringUtf8 (dd->xftdraw, &clr, font, + x + 4, + y + (h - (font->ascent + font->descent))/2 + + font->ascent, + "?", 1); + + XftFontClose (xdpy, font); + } + else if (button->type == MBWMDecorButtonMenu) + { + XSetLineAttributes (xdpy, gc, 3, LineSolid, CapRound, JoinMiter); + XDrawLine (xdpy, dd->xpix, gc, x + 3, y + 5, x + w/2, y + h - 5); + XDrawLine (xdpy, dd->xpix, gc, x + w/2, y + h - 5, x + w - 3, y + 5); + } + else if (button->type == MBWMDecorButtonAccept) + { + XDrawArc (xdpy, dd->xpix, gc, x + 4, y + 4, w - 8, h - 8, 0, 64 * 360); + } + + XFreeGC (xdpy, gc); + + XClearWindow (wm->xdpy, xwin); +} + +/* + * Installs a global handler that can be used to translate custom client type + * names to their numerical values. + * + * NB: this is not an object function, since we need it before we allocate the + * actual MBWMTheme object in the XML parser. + */ +void +mb_wm_theme_set_custom_client_type_func (MBWMThemeCustomClientTypeFunc func, + void *user_data) +{ + custom_client_type_func = func; + custom_client_type_func_data = user_data; +} + +/* + * Installs a global handler that can be used to translate custom theme names + * to their numerical (MBWMObject) values. + * + * NB: this is not an object function, since we need it before we allocate the + * actual MBWMTheme object in the XML parser. + */ +void +mb_wm_theme_set_custom_theme_type_func (MBWMThemeCustomThemeTypeFunc func, + void *user_data) +{ + custom_theme_type_func = func; + custom_theme_type_func_data = user_data; +} + +/* + * Installs a global handler that can be used to translate custom button names + * to their numerical values. + * + * NB: this is not an object function, since we need it before we allocate the + * actual MBWMTheme object in the XML parser. + */ +void +mb_wm_theme_set_custom_button_type_func (MBWMThemeCustomButtonTypeFunc func, + void *user_data) +{ + custom_button_type_func = func; + custom_button_type_func_data = user_data; +} + +/* + * Installs a global handler that can be used to allocate a custom + * MBWMThemeSubclass. + * + * NB: this is not an object function, since we need it before we allocate the + * actual MBWMTheme object in the XML parser. + */ +void +mb_wm_theme_set_custom_theme_alloc_func (MBWMThemeCustomThemeAllocFunc func) +{ + custom_theme_alloc_func = func; +} diff --git a/matchbox/mb-wm-theme.h b/matchbox/mb-wm-theme.h new file mode 100644 index 0000000..b0ce109 --- /dev/null +++ b/matchbox/mb-wm-theme.h @@ -0,0 +1,221 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_WM_THEME_H +#define _HAVE_MB_WM_THEME_H + +#include <matchbox/mb-wm-config.h> +#include <matchbox/mb-wm-object.h> +#include <matchbox/mb-wm-decor.h> + +#define MB_WM_THEME(c) ((MBWMTheme*)(c)) +#define MB_WM_THEME_CLASS(c) ((MBWMThemeClass*)(c)) +#define MB_WM_TYPE_THEME (mb_wm_theme_class_type ()) + +typedef struct MBWMTheme MBWMTheme; +typedef struct MBWMThemeClass MBWMThemeClass; + +#define MB_WM_THEME_PNG(c) ((MBWMThemePng*)(c)) +#define MB_WM_THEME_PNG_CLASS(c) ((MBWMThemePngClass*)(c)) +#define MB_WM_TYPE_THEME_PNG (mb_wm_theme_png_class_type ()) + +typedef struct MBWMThemePng MBWMThemePng; +typedef struct MBWMThemePngClass MBWMThemePngClass; + +typedef enum MBWMThemeCaps +{ + MBWMThemeCapsFrameMainButtonActionAccept = (1<<0), + MBWMThemeCapsFrameDlgButtonActionAccept = (1<<1), + MBWMThemeCapsFrameMainButtonActionHelp = (1<<2), + MBWMThemeCapsFrameDlgButtonActionHelp = (1<<3), + MBWMThemeCapsFrameMainButtonActionCustom = (1<<4), + MBWMThemeCapsFrameDlgButtonActionCustom = (1<<5), +} MBWMThemeCaps; + + +struct MBWMThemeClass +{ + MBWMObjectClass parent; + + void (*paint_decor) (MBWMTheme *theme, + MBWMDecor *decor); + + void (*paint_button) (MBWMTheme *theme, + MBWMDecorButton *button); + + void (*decor_dimensions) (MBWMTheme *theme, + MBWindowManagerClient *client, + int *north, + int *south, + int *west, + int *east); + + void (*button_size) (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *width, + int *height); + + void (*button_position) (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *x, + int *y); + + MBWMDecor* (*create_decor) (MBWMTheme *theme, + MBWindowManagerClient *client, + MBWMDecorType type); + + void (*resize_decor) (MBWMTheme *theme, + MBWMDecor *decor); +}; + +struct MBWMTheme +{ + MBWMObject parent; + + MBWindowManager *wm; + MBWMThemeCaps caps; + char *path; + MBWMList *xml_clients; + + Bool compositing; + Bool shaped; + MBWMColor color_lowlight; + MBWMColor color_shadow; + MBWMCompMgrShadowType shadow_type; +}; + +int +mb_wm_theme_class_type (); + +MBWMTheme * +mb_wm_theme_new (MBWindowManager * wm, const char * theme_path); + +void +mb_wm_theme_paint_decor (MBWMTheme *theme, + MBWMDecor *decor); + +void +mb_wm_theme_paint_button (MBWMTheme *theme, + MBWMDecorButton *button); + +Bool +mb_wm_theme_supports (MBWMTheme *theme, + MBWMThemeCaps capability); + +void +mb_wm_theme_get_decor_dimensions (MBWMTheme *theme, + MBWindowManagerClient *client, + int *north, + int *south, + int *west, + int *east); + +void +mb_wm_theme_get_button_size (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *width, + int *height); + +void +mb_wm_theme_get_button_position (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *x, + int *y); + +Bool +mb_wm_theme_is_button_press_activated (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type); + +MBWMDecor * +mb_wm_theme_create_decor (MBWMTheme *theme, + MBWindowManagerClient *client, + MBWMDecorType type); + +void +mb_wm_theme_resize_decor (MBWMTheme *theme, + MBWMDecor *decor); + +Bool +mb_wm_theme_get_client_geometry (MBWMTheme * theme, + MBWindowManagerClient * client, + MBGeometry * geom); + +MBWMClientLayoutHints +mb_wm_theme_get_client_layout_hints (MBWMTheme * theme, + MBWindowManagerClient * client); + +Bool +mb_wm_theme_is_client_shaped (MBWMTheme * theme, + MBWindowManagerClient * client); + +void +mb_wm_theme_get_lowlight_color (MBWMTheme * theme, + unsigned int * red, + unsigned int * green, + unsigned int * blue, + unsigned int * alpha); + +void +mb_wm_theme_get_shadow_color (MBWMTheme * theme, + unsigned int * red, + unsigned int * green, + unsigned int * blue, + unsigned int * alpha); + + +MBWMCompMgrShadowType +mb_wm_theme_get_shadow_type (MBWMTheme * theme); + +Bool +mb_wm_theme_use_compositing_mgr (MBWMTheme * theme); + +typedef unsigned int (*MBWMThemeCustomClientTypeFunc) (const char *type_name, + void *user_data); + +void +mb_wm_theme_set_custom_client_type_func (MBWMThemeCustomClientTypeFunc func, + void *user_data); + +typedef unsigned int (*MBWMThemeCustomButtonTypeFunc) (const char *type_name, + void *user_data); + +void +mb_wm_theme_set_custom_button_type_func (MBWMThemeCustomButtonTypeFunc func, + void *user_data); + +typedef unsigned int (*MBWMThemeCustomThemeTypeFunc) (const char *type_name, + void *user_data); + +void +mb_wm_theme_set_custom_theme_type_func (MBWMThemeCustomThemeTypeFunc func, + void *user_data); + + +typedef MBWMTheme * (*MBWMThemeCustomThemeAllocFunc) (int theme_type, ...); + +void +mb_wm_theme_set_custom_theme_alloc_func (MBWMThemeCustomThemeAllocFunc func); + +#endif diff --git a/matchbox/mb-wm-types.h b/matchbox/mb-wm-types.h new file mode 100644 index 0000000..61121e7 --- /dev/null +++ b/matchbox/mb-wm-types.h @@ -0,0 +1,395 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Matthew Allum <mallum@o-hand.com> + * + * Copyright (c) 2005 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. + * + */ + +#ifndef _HAVE_MB_WM_TYPES_H +#define _HAVE_MB_WM_TYPES_H + +#include <matchbox/mb-wm-config.h> + +#if USE_GLIB_MAINLOOP +#include <glib.h> +#endif + +#include <X11/Xlib.h> + +/* XXX: The plan is to eventually move this into mb-wm-window-manager.h + * the way we expose private struct members currently ends up resulting + * in *lots* of circular typedef dependencies! */ +typedef struct MBWindowManager MBWindowManager; + +typedef struct MBWMFuncInfo +{ + void *func; + void *data; + void *userdata; + unsigned long signal; + unsigned long id; +} MBWMFuncInfo; + +typedef struct MBGeometry +{ + int x,y; + int width,height; + +} MBGeometry; + +typedef struct MBWMList MBWMList; + +typedef void (*MBWMListForEachCB) (void *data, void *userdata); + +struct MBWMList +{ + MBWMList *next, *prev; + void *data; +}; + +typedef struct MBWMClientWindowAttributes /* Needs to be sorted */ +{ + Visual *visual; + Window root; + int class; + int bit_gravity; + int win_gravity; + int backing_store; + unsigned long backing_planes; + unsigned long backing_pixel; + Bool save_under; + Colormap colormap; + Bool map_installed; + int map_state; + long all_event_masks; + long your_event_mask; + long do_not_propagate_mask; + Bool override_redirect; + +} MBWMClientWindowAttributes ; + +typedef struct MBWMRgbaIcon +{ + int width; + int height; + unsigned long *pixels; +} MBWMRgbaIcon; + +typedef enum MBWMClientType +{ + MBWMClientTypeApp = (1 << 0), + MBWMClientTypeDialog = (1 << 1), + MBWMClientTypePanel = (1 << 2), + MBWMClientTypeDesktop = (1 << 3), + MBWMClientTypeInput = (1 << 4), + MBWMClientTypeMenu = (1 << 5), + MBWMClientTypeNote = (1 << 6), + MBWMClientTypeOverride = (1 << 7), + + MBWMClientTypeLast = MBWMClientTypeOverride, +} MBWMClientType; + +typedef enum _MBWMCompMgrClientEvent +{ + MBWMCompMgrClientEventNone = 0, + MBWMCompMgrClientEventMinimize, + MBWMCompMgrClientEventMap, + MBWMCompMgrClientEventUnmap, + + _MBWMCompMgrClientEventLast, +} MBWMCompMgrClientEvent; + +typedef enum _MBWMGravity +{ + MBWMGravityNone = 0, + MBWMGravityNorth, + MBWMGravityNorthEast, + MBWMGravityEast, + MBWMGravitySouthEast, + MBWMGravitySouth, + MBWMGravitySouthWest, + MBWMGravityWest, + MBWMGravityNorthWest, + MBWMGravityCenter, +} MBWMGravity; + +typedef unsigned long MBWMCookie; + +typedef enum MBWMAtom +{ + /* ICCCM */ + + MBWM_ATOM_WM_NAME = 0, + MBWM_ATOM_WM_STATE, + MBWM_ATOM_WM_HINTS, + MBWM_ATOM_WM_NORMAL_HINTS, + MBWM_ATOM_WM_CHANGE_STATE, + MBWM_ATOM_WM_PROTOCOLS, + MBWM_ATOM_WM_DELETE_WINDOW, + MBWM_ATOM_WM_COLORMAP_WINDOWS, + MBWM_ATOM_WM_CLIENT_MACHINE, + MBWM_ATOM_WM_TRANSIENT_FOR, + MBWM_ATOM_WM_TAKE_FOCUS, + + /* EWMH */ + + MBWM_ATOM_NET_WM_WINDOW_TYPE, + MBWM_ATOM_NET_WM_WINDOW_TYPE_NORMAL, + MBWM_ATOM_NET_WM_WINDOW_TYPE_TOOLBAR, + MBWM_ATOM_NET_WM_WINDOW_TYPE_INPUT, + MBWM_ATOM_NET_WM_WINDOW_TYPE_DOCK, + MBWM_ATOM_NET_WM_WINDOW_TYPE_MENU, + MBWM_ATOM_NET_WM_WINDOW_TYPE_POPUP_MENU, + MBWM_ATOM_NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + MBWM_ATOM_NET_WM_WINDOW_TYPE_DIALOG, + MBWM_ATOM_NET_WM_WINDOW_TYPE_SPLASH, + MBWM_ATOM_NET_WM_WINDOW_TYPE_DESKTOP, + MBWM_ATOM_NET_WM_WINDOW_TYPE_NOTIFICATION, + + MBWM_ATOM_NET_WM_STATE, + MBWM_ATOM_NET_WM_STATE_FULLSCREEN, + MBWM_ATOM_NET_WM_STATE_MODAL, + MBWM_ATOM_NET_WM_STATE_ABOVE, + MBWM_ATOM_NET_WM_STATE_STICKY, + MBWM_ATOM_NET_WM_STATE_MAXIMIZED_VERT, + MBWM_ATOM_NET_WM_STATE_MAXIMIZED_HORZ, + MBWM_ATOM_NET_WM_STATE_SHADED, + MBWM_ATOM_NET_WM_STATE_SKIP_TASKBAR, + MBWM_ATOM_NET_WM_STATE_SKIP_PAGER, + MBWM_ATOM_NET_WM_STATE_HIDDEN, + MBWM_ATOM_NET_WM_STATE_BELOW, + MBWM_ATOM_NET_WM_STATE_DEMANDS_ATTENTION, + + MBWM_ATOM_NET_SUPPORTED, + MBWM_ATOM_NET_CLIENT_LIST, + MBWM_ATOM_NET_NUMBER_OF_DESKTOPS, + MBWM_ATOM_NET_ACTIVE_WINDOW, + MBWM_ATOM_NET_SUPPORTING_WM_CHECK, + MBWM_ATOM_NET_CLOSE_WINDOW, + MBWM_ATOM_NET_WM_NAME, + MBWM_ATOM_NET_WM_USER_TIME, + + MBWM_ATOM_NET_CLIENT_LIST_STACKING, + MBWM_ATOM_NET_CURRENT_DESKTOP, + MBWM_ATOM_NET_WM_DESKTOP, + MBWM_ATOM_NET_WM_ICON, + MBWM_ATOM_NET_DESKTOP_GEOMETRY, + MBWM_ATOM_NET_WORKAREA, + MBWM_ATOM_NET_SHOWING_DESKTOP, + MBWM_ATOM_NET_DESKTOP_VIEWPORT, + MBWM_ATOM_NET_FRAME_EXTENTS, + MBWM_ATOM_NET_WM_FULL_PLACEMENT, + + MBWM_ATOM_NET_WM_ALLOWED_ACTIONS, + MBWM_ATOM_NET_WM_ACTION_MOVE, + MBWM_ATOM_NET_WM_ACTION_RESIZE, + MBWM_ATOM_NET_WM_ACTION_MINIMIZE, + MBWM_ATOM_NET_WM_ACTION_SHADE, + MBWM_ATOM_NET_WM_ACTION_STICK, + MBWM_ATOM_NET_WM_ACTION_MAXIMIZE_HORZ, + MBWM_ATOM_NET_WM_ACTION_MAXIMIZE_VERT, + MBWM_ATOM_NET_WM_ACTION_FULLSCREEN, + MBWM_ATOM_NET_WM_ACTION_CHANGE_DESKTOP, + MBWM_ATOM_NET_WM_ACTION_CLOSE, + + MBWM_ATOM_NET_WM_PING, + MBWM_ATOM_NET_WM_PID, + + /* Startup Notification */ + MBWM_ATOM_NET_STARTUP_ID, + + /* Misc */ + + MBWM_ATOM_UTF8_STRING, + MBWM_ATOM_MOTIF_WM_HINTS, + MBWM_ATOM_WIN_SUPPORTING_WM_CHECK, + + MBWM_ATOM_NET_WM_CONTEXT_HELP, + MBWM_ATOM_NET_WM_CONTEXT_ACCEPT, + MBWM_ATOM_NET_WM_CONTEXT_CUSTOM, + MBWM_ATOM_NET_WM_SYNC_REQUEST, + + MBWM_ATOM_CM_TRANSLUCENCY, + + MBWM_ATOM_MB_APP_WINDOW_LIST_STACKING, + MBWM_ATOM_MB_THEME, + MBWM_ATOM_MB_THEME_NAME, + MBWM_ATOM_MB_COMMAND, + MBWM_ATOM_MB_GRAB_TRANSFER, + MBWM_ATOM_MB_CURRENT_APP_WINDOW, + + /* FIXME: Custom/Unused to sort out + * + * MB_WM_STATE_DOCK_TITLEBAR, + * + * _NET_WM_SYNC_REQUEST_COUNTER, + * _NET_WM_SYNC_REQUEST, + * + * MB_CLIENT_EXEC_MAP, + * MB_CLIENT_STARTUP_LIST, + * MB_DOCK_TITLEBAR_SHOW_ON_DESKTOP, + * + * WINDOW_TYPE_MESSAGE, + * + */ + + MBWM_ATOM_COUNT + +} MBWMAtom; + +/* Event Callbacks */ + +typedef Bool (*MBWMXEventFunc) + (void *xev, + void *userdata); + +typedef Bool (*MBWindowManagerMapNotifyFunc) + (XMapEvent *xev, + void *userdata); + +typedef Bool (*MBWindowManagerClientMessageFunc) + (XClientMessageEvent *xev, + void *userdata); + +typedef Bool (*MBWindowManagerMapRequestFunc) + (XMapRequestEvent *xev, + void *userdata); + +typedef Bool (*MBWindowManagerUnmapNotifyFunc) + (XUnmapEvent *xev, + void *userdata); + +typedef Bool (*MBWindowManagerDestroyNotifyFunc) + (XDestroyWindowEvent *xev, + void *userdata); + +typedef Bool (*MBWindowManagerConfigureNotifyFunc) + (XConfigureEvent *xev, + void *userdata); + +typedef Bool (*MBWindowManagerConfigureRequestFunc) + (XConfigureRequestEvent *xev, + void *userdata); + +typedef Bool (*MBWindowManagerKeyPressFunc) + (XKeyEvent *xev, + void *userdata); + +typedef Bool (*MBWindowManagerPropertyNotifyFunc) + (XPropertyEvent *xev, + void *userdata); + +typedef Bool (*MBWindowManagerButtonPressFunc) + (XButtonEvent *xev, + void *userdata); + +typedef Bool (*MBWindowManagerButtonReleaseFunc) + (XButtonEvent *xev, + void *userdata); + +typedef Bool (*MBWindowManagerMotionNotifyFunc) + (XMotionEvent *xev, + void *userdata); + +typedef Bool (*MBWindowManagerTimeOutFunc) + (void *userdata); + +#if USE_GLIB_MAINLOOP +typedef GIOChannel MBWMIOChannel; +typedef GIOCondition MBWMIOCondition; +#else +typedef int MBWMIOChannel; +typedef int MBWMIOCondition; +#endif + +typedef Bool (*MBWindowManagerFdWatchFunc) + (MBWMIOChannel *channel, + MBWMIOCondition events, + void *userdata); + +typedef struct MBWMXEventFuncInfo +{ + MBWMXEventFunc func; + Window xwindow; + void *userdata; + unsigned long id; +} +MBWMXEventFuncInfo; + +typedef struct MBWMTimeOutEventInfo MBWMTimeOutEventInfo; +typedef struct MBWMFdWatchInfo MBWMFdWatchInfo; + +typedef enum MBWMDecorButtonFlags +{ + MB_WM_DECOR_BUTTON_INVISIBLE = (1<<1) + +} MBWMDecorButtonFlags; + +typedef enum MBWMDecorType +{ + MBWMDecorTypeNorth = 1, + MBWMDecorTypeSouth, + MBWMDecorTypeEast, + MBWMDecorTypeWest, + +} MBWMDecorType; + +typedef enum MBWMSyncType +{ + MBWMSyncStacking = (1<<1), + MBWMSyncGeometry = (1<<2), + MBWMSyncVisibility = (1<<3), + MBWMSyncDecor = (1<<4), + MBWMSyncConfigRequestAck = (1<<5), + MBWMSyncFullscreen = (1<<6), +} MBWMSyncType; + +typedef struct MBWMColor +{ + double r; + double g; + double b; + double a; + + Bool set; +}MBWMColor; + +typedef enum MBWMCompMgrShadowType +{ + MBWM_COMP_MGR_SHADOW_NONE = 0, + MBWM_COMP_MGR_SHADOW_SIMPLE, + MBWM_COMP_MGR_SHADOW_GAUSSIAN, +} MBWMCompMgrShadowType; + +typedef enum MBWMModality +{ + MBWMModalityNormal = 0, /* Handle modality per EWMH */ + MBWMModalitySystem, /* Treat all modal dialogs as if system modal */ + MBWMModalityNone, /* Ignore modality */ +}MBWMModality; + + +/* mb remote commands */ +#define MB_CMD_SET_THEME 1 +#define MB_CMD_EXIT 2 +#define MB_CMD_DESKTOP 3 +#define MB_CMD_NEXT 4 +#define MB_CMD_PREV 5 +#define MB_CMD_MISC 7 /* spare, used for debugging */ +#define MB_CMD_COMPOSITE 8 +#define MB_CMB_KEYS_RELOAD 9 + +#endif diff --git a/matchbox/mb-wm-util.c b/matchbox/mb-wm-util.c new file mode 100644 index 0000000..f99777c --- /dev/null +++ b/matchbox/mb-wm-util.c @@ -0,0 +1,275 @@ +#include "mb-wm-config.h" +#include "mb-wm-util.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> + +static int TrappedErrorCode = 0; +static int (*old_error_handler) (Display *, XErrorEvent *); + +static int +error_handler(Display *xdpy, + XErrorEvent *error) +{ + TrappedErrorCode = error->error_code; + return 0; +} + +void +mb_wm_util_trap_x_errors(void) +{ + /* MBWM_DBG("### X Errors Trapped ###"); */ + + TrappedErrorCode = 0; + old_error_handler = XSetErrorHandler(error_handler); +} + +int +mb_wm_util_untrap_x_errors(void) +{ + /* MBWM_DBG("### X Errors Untrapped (%i) ###", TrappedErrorCode); */ + + XSetErrorHandler(old_error_handler); + return TrappedErrorCode; +} + + +void* +mb_wm_util_malloc0(int size) +{ + void *p = NULL; + + p = malloc(size); + + if (p == NULL) + { + /* hook into some kind of out of memory */ + } + else + memset(p, 0, size); + + return p; +} + +Bool /* FIXME: define, inline ? */ +mb_geometry_compare (MBGeometry *g1, MBGeometry *g2) +{ + return (g1->x == g2->x + && g1->y == g2->y + && g1->width == g2->width + && g1->height == g2->height); +} + +Bool /* True if overlaps */ +mb_geometry_intersects (MBGeometry *g1, MBGeometry *g2) +{ + if ((g1->x > g2->x + g2->width) || + (g1->y > g2->y + g2->height) || + (g2->x > g1->x + g1->width) || + (g2->y > g1->y + g1->height)) + return False; + + return True; +} + + +void +mb_wm_util_fatal_error (char *msg) +{ + fprintf(stderr, "matchbox-window-manager: *Error* %s\n", msg); + exit(1); +} + +void +mb_wm_util_warn (const char *format, ...) +{ + va_list ap; + char *msg = NULL; + + va_start(ap, format); + vasprintf(&msg, format, ap); + va_end(ap); + + fprintf(stderr, "*MBWM Warning* %s\n", msg); + + if (msg) free(msg); +} + +MBWMList* +mb_wm_util_list_alloc_item(void) +{ + return mb_wm_util_malloc0(sizeof(MBWMList)); +} + +int +mb_wm_util_list_length(MBWMList *list) +{ + int result = 1; + + if (!list) + return 0; + + list = mb_wm_util_list_get_first(list); + + while ((list = mb_wm_util_list_next(list)) != NULL) + result++; + + return result; +} + +MBWMList* +mb_wm_util_list_get_last(MBWMList *list) +{ + if (list == NULL) + return NULL; + + while (list->next) + list = mb_wm_util_list_next(list); + return list; +} + +MBWMList* +mb_wm_util_list_get_first(MBWMList *list) +{ + if (list == NULL) + return NULL; + + while (list->prev) + list = mb_wm_util_list_prev(list); + return list; +} + +void* +mb_wm_util_list_get_nth_data(MBWMList *list, int n) +{ + if (list == NULL) + return NULL; + + list = mb_wm_util_list_get_first(list); + + while (list->next && n) + { + list = mb_wm_util_list_next(list); + n--; + } + + if (n) return NULL; + + return (void *)list->data; +} + +MBWMList* +mb_wm_util_list_prepend(MBWMList *list, void *data) +{ + MBWMList * l = mb_wm_util_list_alloc_item(); + + l->data = data; + l->next = list; + + if (list) + list->prev = l; + + return l; +} + +MBWMList* +mb_wm_util_list_append(MBWMList *list, void *data) +{ + if (list == NULL) + { + list = mb_wm_util_list_alloc_item(); + list->data = data; + } + else + { + MBWMList *last; + + last = mb_wm_util_list_get_last(list); + + last->next = mb_wm_util_list_alloc_item(); + last->next->prev = last; + last->next->data = data; + } + + return list; +} + +MBWMList* +mb_wm_util_list_remove(MBWMList *list, void *data) +{ + MBWMList *prev, *start; + + prev = NULL; + start = list = mb_wm_util_list_get_first(list); + + while (list) + { + if (list->data == data) + { + if (list->next) + list->next->prev = prev; + + if (prev) + prev->next = list->next; + else + start = list->next; + + free(list); + + return start; + } + + prev = list; + list = list->next; + } + + return NULL; +} + +void +mb_wm_util_list_foreach (const MBWMList *list, + MBWMListForEachCB func, + void *userdata) +{ + MBWMList *p = (MBWMList *) list; + + while (p) + { + func(p->data, userdata); + p = mb_wm_util_list_next(p); + } +} + +void +mb_wm_util_list_free (MBWMList * list) +{ + MBWMList * l = list; + + while (l) + { + MBWMList * f = l; + l = l->next; + + free (f); + } +} + + +MBWMRgbaIcon * +mb_wm_rgba_icon_new () +{ + return mb_wm_util_malloc0 (sizeof (MBWMRgbaIcon)); +} + + +void +mb_wm_rgba_icon_free (MBWMRgbaIcon *icon) +{ + if (icon->pixels) + free (icon->pixels); + + free (icon); +} + diff --git a/matchbox/mb-wm-util.h b/matchbox/mb-wm-util.h new file mode 100644 index 0000000..817cd74 --- /dev/null +++ b/matchbox/mb-wm-util.h @@ -0,0 +1,116 @@ +#ifndef _MB_HAVE_UTIL_H +#define _MB_HAVE_UTIL_H + +#include <matchbox/mb-wm-config.h> +#include <matchbox/mb-wm-types.h> + +/* See http://rlove.org/log/2005102601 */ +#if __GNUC__ >= 3 +#if ! USE_GLIB_MAINLOOP +# define inline __attribute__ ((always_inline)) +#endif +# define __pure__attribute__ ((pure)) +# define __const__attribute__ ((const)) +# define __noreturn__attribute__ ((noreturn)) +# define __malloc__attribute__ ((malloc)) +# define __must_check__attribute__ ((warn_unused_result)) +# define __deprecated__attribute__ ((deprecated)) +# define __used__attribute__ ((used)) +# define __unused__attribute__ ((unused)) +# define __packed__attribute__ ((packed)) +# define LIKELY(x)__builtin_expect (!!(x), 1) +# define UNLIKELY(x)__builtin_expect (!!(x), 0) +#else +# define inline/* no inline */ +# define __pure/* no pure */ +# define __const/* no const */ +# define __noreturn/* no noreturn */ +# define __malloc/* no malloc */ +# define __must_check/* no warn_unused_result */ +# define __deprecated/* no deprecated */ +# define __used/* no used */ +# define __unused/* no unused */ +# define __packed/* no packed */ +# define LIKELY(x)(x) +# define UNLIKELY(x)(x) +#endif + +#define streq(a,b) (strcmp(a,b) == 0) +#define strcaseeq(a,b) (strcasecmp(a,b) == 0) +#define unless(x) if (!(x)) + +#define MBWMChildMask (SubstructureRedirectMask|SubstructureNotifyMask) +#define MBWMButtonMask (ButtonPressMask|ButtonReleaseMask) +#define MBWMMouseMask (ButtonMask|PointerMotionMask) +#define MBWMKeyMask (KeyPressMask|KeyReleaseMask) + +void* +mb_wm_util_malloc0(int size); + +void +mb_wm_util_fatal_error(char *msg); + +void +mb_wm_util_warn (const char *format, ...); + +/* Misc */ + +Bool /* True if matching */ +mb_geometry_compare (MBGeometry *g1, MBGeometry *g2); + +Bool /* True if overlaps */ +mb_geometry_intersects (MBGeometry *g1, MBGeometry *g2); + +/* XErrors */ + +void +mb_wm_util_trap_x_errors(void); + +int +mb_wm_util_untrap_x_errors(void); + + +/* List */ + + +#define mb_wm_util_list_next(list) (list)->next +#define mb_wm_util_list_prev(list) (list)->prev +#define mb_wm_util_list_data(data) (list)->data + +MBWMList* +mb_wm_util_list_alloc_item(void); + +MBWMList* +mb_wm_util_list_remove(MBWMList *list, void *data); + +int +mb_wm_util_list_length(MBWMList *list); + +MBWMList* +mb_wm_util_list_get_last(MBWMList *list); + +MBWMList* +mb_wm_util_list_get_first(MBWMList *list); + +void* +mb_wm_util_list_get_nth_data(MBWMList *list, int n); + +MBWMList* +mb_wm_util_list_append(MBWMList *list, void *data); + +MBWMList* +mb_wm_util_list_prepend(MBWMList *list, void *data); + +void +mb_wm_util_list_foreach (const MBWMList *list, MBWMListForEachCB func, void *userdata); + +void +mb_wm_util_list_free (MBWMList * list); + +MBWMRgbaIcon * +mb_wm_rgba_icon_new (); + +void +mb_wm_rgba_icon_free (MBWMRgbaIcon *icon); + +#endif diff --git a/matchbox/tidy/tidy-texture-frame.c b/matchbox/tidy/tidy-texture-frame.c new file mode 100644 index 0000000..7ba4671 --- /dev/null +++ b/matchbox/tidy/tidy-texture-frame.c @@ -0,0 +1,641 @@ +/* tidy-texture-frame.h: Expandible texture actor + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:tidy-texture-frame + * @short_description: Stretch a texture to fit the entire allocation + * + * #TidyTextureFrame + * + */ + +#include <cogl/cogl.h> + +#include "tidy-texture-frame.h" + +#define TIDY_PARAM_READABLE \ + (G_PARAM_READABLE | \ + G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB) + +#define TIDY_PARAM_READWRITE \ + (G_PARAM_READABLE | G_PARAM_WRITABLE | \ + G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB) + +enum +{ + PROP_0, + + PROP_PARENT_TEXTURE, + + PROP_LEFT, + PROP_TOP, + PROP_RIGHT, + PROP_BOTTOM +}; + +G_DEFINE_TYPE (TidyTextureFrame, tidy_texture_frame, CLUTTER_TYPE_ACTOR); + +#define TIDY_TEXTURE_FRAME_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TIDY_TYPE_TEXTURE_FRAME, TidyTextureFramePrivate)) + +struct _TidyTextureFramePrivate +{ + ClutterTexture *parent_texture; + + gfloat left; + gfloat top; + gfloat right; + gfloat bottom; + + CoglHandle material; + + guint needs_paint : 1; +}; + +static void +tidy_texture_frame_get_preferred_width (ClutterActor *self, + gfloat for_height, + gfloat *min_width_p, + gfloat *natural_width_p) +{ + TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv; + + if (G_UNLIKELY (priv->parent_texture == NULL)) + { + if (min_width_p) + *min_width_p = 0; + + if (natural_width_p) + *natural_width_p = 0; + } + else + { + ClutterActorClass *klass; + + /* by directly querying the parent texture's class implementation + * we are going around any override mechanism the parent texture + * might have in place, and we ask directly for the original + * preferred width + */ + klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture); + klass->get_preferred_width (CLUTTER_ACTOR (priv->parent_texture), + for_height, + min_width_p, + natural_width_p); + } +} + +static void +tidy_texture_frame_get_preferred_height (ClutterActor *self, + gfloat for_width, + gfloat *min_height_p, + gfloat *natural_height_p) +{ + TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv; + + if (G_UNLIKELY (priv->parent_texture == NULL)) + { + if (min_height_p) + *min_height_p = 0; + + if (natural_height_p) + *natural_height_p = 0; + } + else + { + ClutterActorClass *klass; + + /* by directly querying the parent texture's class implementation + * we are going around any override mechanism the parent texture + * might have in place, and we ask directly for the original + * preferred height + */ + klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture); + klass->get_preferred_height (CLUTTER_ACTOR (priv->parent_texture), + for_width, + min_height_p, + natural_height_p); + } +} + +static void +tidy_texture_frame_realize (ClutterActor *self) +{ + TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv; + + if (priv->material != COGL_INVALID_HANDLE) + return; + + priv->material = cogl_material_new (); + + CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED); +} + +static void +tidy_texture_frame_unrealize (ClutterActor *self) +{ + TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv; + + if (priv->material == COGL_INVALID_HANDLE) + return; + + cogl_handle_unref (priv->material); + priv->material = COGL_INVALID_HANDLE; + + CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED); +} + +static void +tidy_texture_frame_paint (ClutterActor *self) +{ + TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv; + CoglHandle cogl_texture = COGL_INVALID_HANDLE; + ClutterActorBox box = { 0, }; + gfloat width, height; + gfloat tex_width, tex_height; + gfloat ex, ey; + gfloat tx1, ty1, tx2, ty2; + guint8 opacity; + + /* no need to paint stuff if we don't have a texture */ + if (G_UNLIKELY (priv->parent_texture == NULL)) + return; + + if (!priv->needs_paint) + return; + + /* parent texture may have been hidden, so need to make sure it gets + * realized + */ + if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture)) + clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture)); + + cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture); + if (cogl_texture == COGL_INVALID_HANDLE) + return; + + tex_width = cogl_texture_get_width (cogl_texture); + tex_height = cogl_texture_get_height (cogl_texture); + + clutter_actor_get_allocation_box (self, &box); + width = box.x2 - box.x1; + height = box.y2 - box.y1; + + tx1 = priv->left / tex_width; + tx2 = (tex_width - priv->right) / tex_width; + ty1 = priv->top / tex_height; + ty2 = (tex_height - priv->bottom) / tex_height; + + ex = width - priv->right; + if (ex < 0) + ex = priv->right; /* FIXME ? */ + + ey = height - priv->bottom; + if (ey < 0) + ey = priv->bottom; /* FIXME ? */ + + opacity = clutter_actor_get_paint_opacity (self); + + g_assert (priv->material != COGL_INVALID_HANDLE); + + /* set the source material using the parent texture's COGL handle */ + cogl_material_set_color4ub (priv->material, opacity, opacity, opacity, opacity); + cogl_material_set_layer (priv->material, 0, cogl_texture); + cogl_set_source (priv->material); + + /* top left corner */ + cogl_rectangle_with_texture_coords (0, 0, priv->left, priv->top, + 0.0, 0.0, + tx1, ty1); + + /* top middle */ + cogl_rectangle_with_texture_coords (priv->left, 0, ex, priv->top, + tx1, 0.0, + tx2, ty1); + + /* top right */ + cogl_rectangle_with_texture_coords (ex, 0, width, priv->top, + tx2, 0.0, + 1.0, ty1); + + /* mid left */ + cogl_rectangle_with_texture_coords (0, priv->top, priv->left, ey, + 0.0, ty1, + tx1, ty2); + + /* center */ + cogl_rectangle_with_texture_coords (priv->left, priv->top, ex, ey, + tx1, ty1, + tx2, ty2); + + /* mid right */ + cogl_rectangle_with_texture_coords (ex, priv->top, width, ey, + tx2, ty1, + 1.0, ty2); + + /* bottom left */ + cogl_rectangle_with_texture_coords (0, ey, priv->left, height, + 0.0, ty2, + tx1, 1.0); + + /* bottom center */ + cogl_rectangle_with_texture_coords (priv->left, ey, ex, height, + tx1, ty2, + tx2, 1.0); + + /* bottom right */ + cogl_rectangle_with_texture_coords (ex, ey, width, height, + tx2, ty2, + 1.0, 1.0); +} + +static inline void +tidy_texture_frame_set_frame_internal (TidyTextureFrame *frame, + gfloat left, + gfloat top, + gfloat right, + gfloat bottom) +{ + TidyTextureFramePrivate *priv = frame->priv; + GObject *gobject = G_OBJECT (frame); + gboolean changed = FALSE; + + g_object_freeze_notify (gobject); + + if (priv->top != top) + { + priv->top = top; + g_object_notify (gobject, "top"); + changed = TRUE; + } + + if (priv->right != right) + { + priv->right = right; + g_object_notify (gobject, "right"); + changed = TRUE; + } + + if (priv->bottom != bottom) + { + priv->bottom = bottom; + g_object_notify (gobject, "bottom"); + changed = TRUE; + } + + if (priv->left != left) + { + priv->left = left; + g_object_notify (gobject, "left"); + changed = TRUE; + } + + if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (frame)); + + g_object_thaw_notify (gobject); +} + +static void +tidy_texture_frame_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TidyTextureFrame *frame = TIDY_TEXTURE_FRAME (gobject); + TidyTextureFramePrivate *priv = frame->priv; + + switch (prop_id) + { + case PROP_PARENT_TEXTURE: + tidy_texture_frame_set_parent_texture (frame, + g_value_get_object (value)); + break; + + case PROP_TOP: + tidy_texture_frame_set_frame_internal (frame, + priv->left, + g_value_get_float (value), + priv->right, + priv->bottom); + break; + + case PROP_RIGHT: + tidy_texture_frame_set_frame_internal (frame, + priv->top, + g_value_get_float (value), + priv->bottom, + priv->left); + break; + + case PROP_BOTTOM: + tidy_texture_frame_set_frame_internal (frame, + priv->top, + priv->right, + g_value_get_float (value), + priv->left); + break; + + case PROP_LEFT: + tidy_texture_frame_set_frame_internal (frame, + priv->top, + priv->right, + priv->bottom, + g_value_get_float (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +tidy_texture_frame_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (gobject)->priv; + + switch (prop_id) + { + case PROP_PARENT_TEXTURE: + g_value_set_object (value, priv->parent_texture); + break; + + case PROP_LEFT: + g_value_set_float (value, priv->left); + break; + + case PROP_TOP: + g_value_set_float (value, priv->top); + break; + + case PROP_RIGHT: + g_value_set_float (value, priv->right); + break; + + case PROP_BOTTOM: + g_value_set_float (value, priv->bottom); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +tidy_texture_frame_dispose (GObject *gobject) +{ + TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (gobject)->priv; + + if (priv->parent_texture) + { + g_object_unref (priv->parent_texture); + priv->parent_texture = NULL; + } + + if (priv->material) + { + cogl_handle_unref (priv->material); + priv->material = COGL_INVALID_HANDLE; + } + + G_OBJECT_CLASS (tidy_texture_frame_parent_class)->dispose (gobject); +} + +static void +tidy_texture_frame_class_init (TidyTextureFrameClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (gobject_class, sizeof (TidyTextureFramePrivate)); + + actor_class->get_preferred_width = + tidy_texture_frame_get_preferred_width; + actor_class->get_preferred_height = + tidy_texture_frame_get_preferred_height; + actor_class->realize = tidy_texture_frame_realize; + actor_class->unrealize = tidy_texture_frame_unrealize; + actor_class->paint = tidy_texture_frame_paint; + + gobject_class->set_property = tidy_texture_frame_set_property; + gobject_class->get_property = tidy_texture_frame_get_property; + gobject_class->dispose = tidy_texture_frame_dispose; + + pspec = g_param_spec_object ("parent-texture", + "Parent Texture", + "The parent ClutterTexture", + CLUTTER_TYPE_TEXTURE, + TIDY_PARAM_READWRITE | + G_PARAM_CONSTRUCT); + g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec); + + pspec = g_param_spec_float ("left", + "Left", + "Left offset", + 0, G_MAXFLOAT, + 0, + TIDY_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_LEFT, pspec); + + pspec = g_param_spec_float ("top", + "Top", + "Top offset", + 0, G_MAXFLOAT, + 0, + TIDY_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_TOP, pspec); + + pspec = g_param_spec_float ("bottom", + "Bottom", + "Bottom offset", + 0, G_MAXFLOAT, + 0, + TIDY_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_BOTTOM, pspec); + + pspec = g_param_spec_float ("right", + "Right", + "Right offset", + 0, G_MAXFLOAT, + 0, + TIDY_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_RIGHT, pspec); +} + +static void +tidy_texture_frame_init (TidyTextureFrame *self) +{ + TidyTextureFramePrivate *priv; + + self->priv = priv = TIDY_TEXTURE_FRAME_GET_PRIVATE (self); + + priv->material = COGL_INVALID_HANDLE; +} + +/** + * tidy_texture_frame_new: + * @texture: a #ClutterTexture or %NULL + * @left: left margin preserving its content + * @top: top margin preserving its content + * @right: right margin preserving its content + * @bottom: bottom margin preserving its content + * + * A #TidyTextureFrame is a specialized texture that efficiently clones + * an area of the given @texture while keeping preserving portions of the + * same texture. + * + * A #TidyTextureFrame can be used to make a rectangular texture fit a + * given size without stretching its borders. + * + * Return value: the newly created #TidyTextureFrame + */ +ClutterActor* +tidy_texture_frame_new (ClutterTexture *texture, + gfloat left, + gfloat top, + gfloat right, + gfloat bottom) +{ + g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL); + + return g_object_new (TIDY_TYPE_TEXTURE_FRAME, + "parent-texture", texture, + "left", left, + "top", top, + "right", right, + "bottom", bottom, + NULL); +} + +ClutterTexture * +tidy_texture_frame_get_parent_texture (TidyTextureFrame *frame) +{ + g_return_val_if_fail (TIDY_IS_TEXTURE_FRAME (frame), NULL); + + return frame->priv->parent_texture; +} + +void +tidy_texture_frame_set_parent_texture (TidyTextureFrame *frame, + ClutterTexture *texture) +{ + TidyTextureFramePrivate *priv; + gboolean was_visible; + + g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame)); + g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture)); + + priv = frame->priv; + + was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame); + + if (priv->parent_texture == texture) + return; + + if (priv->parent_texture) + { + g_object_unref (priv->parent_texture); + priv->parent_texture = NULL; + + if (was_visible) + clutter_actor_hide (CLUTTER_ACTOR (frame)); + } + + if (texture) + { + priv->parent_texture = g_object_ref (texture); + + if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture)) + clutter_actor_show (CLUTTER_ACTOR (frame)); + } + + clutter_actor_queue_relayout (CLUTTER_ACTOR (frame)); + + g_object_notify (G_OBJECT (frame), "parent-texture"); +} + +void +tidy_texture_frame_set_frame (TidyTextureFrame *frame, + gfloat top, + gfloat right, + gfloat bottom, + gfloat left) +{ + g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame)); + + tidy_texture_frame_set_frame_internal (frame, top, right, bottom, left); +} + +void +tidy_texture_frame_get_frame (TidyTextureFrame *frame, + gfloat *top, + gfloat *right, + gfloat *bottom, + gfloat *left) +{ + TidyTextureFramePrivate *priv; + + g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame)); + + priv = frame->priv; + + if (top) + *top = priv->top; + + if (right) + *right = priv->right; + + if (bottom) + *bottom = priv->bottom; + + if (left) + *left = priv->left; +} + +/** + * tidy_texture_frame_set_needs_paint: + * @frame: a #TidyTextureframe + * @needs_paint: if %FALSE, the paint will be skipped + * + * Provides a hint to the texture frame that it is totally obscured + * and doesn't need to be painted. This would typically be called + * by a parent container if it detects the condition prior to + * painting its children and then unset afterwards. + * + * Since it is not supposed to have any effect on display, it does + * not queue a repaint. + */ +void +tidy_texture_frame_set_needs_paint (TidyTextureFrame *frame, + gboolean needs_paint) +{ + TidyTextureFramePrivate *priv; + + g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame)); + + priv = frame->priv; + + priv->needs_paint = needs_paint; +} diff --git a/matchbox/tidy/tidy-texture-frame.h b/matchbox/tidy/tidy-texture-frame.h new file mode 100644 index 0000000..71dd40c --- /dev/null +++ b/matchbox/tidy/tidy-texture-frame.h @@ -0,0 +1,84 @@ +/* tidy-texture-frame.h: Expandible texture actor + * + * Copyright (C) 2007, 2008 OpenedHand Ltd + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _HAVE_TIDY_TEXTURE_FRAME_H +#define _HAVE_TIDY_TEXTURE_FRAME_H + +#include <clutter/clutter.h> + +G_BEGIN_DECLS + +#define TIDY_TYPE_TEXTURE_FRAME (tidy_texture_frame_get_type ()) +#define TIDY_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_TEXTURE_FRAME, TidyTextureFrame)) +#define TIDY_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_TEXTURE_FRAME, TidyTextureFrameClass)) +#define TIDY_IS_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_TEXTURE_FRAME)) +#define TIDY_IS_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_TEXTURE_FRAME)) +#define TIDY_TEXTURE_FRAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_TEXTURE_FRAME, TidyTextureFrameClass)) + +typedef struct _TidyTextureFrame TidyTextureFrame; +typedef struct _TidyTextureFramePrivate TidyTextureFramePrivate; +typedef struct _TidyTextureFrameClass TidyTextureFrameClass; + +struct _TidyTextureFrame +{ + /*< private >*/ + ClutterActor parent_instance; + + TidyTextureFramePrivate *priv; +}; + +struct _TidyTextureFrameClass +{ + ClutterActorClass parent_class; + + /* padding for future expansion */ + void (*_clutter_box_1) (void); + void (*_clutter_box_2) (void); + void (*_clutter_box_3) (void); + void (*_clutter_box_4) (void); +}; + +GType tidy_texture_frame_get_type (void) G_GNUC_CONST; +ClutterActor * tidy_texture_frame_new (ClutterTexture *texture, + gfloat top, + gfloat right, + gfloat bottom, + gfloat left); +void tidy_texture_frame_set_parent_texture (TidyTextureFrame *frame, + ClutterTexture *texture); +ClutterTexture *tidy_texture_frame_get_parent_texture (TidyTextureFrame *frame); +void tidy_texture_frame_set_frame (TidyTextureFrame *frame, + gfloat top, + gfloat right, + gfloat bottom, + gfloat left); +void tidy_texture_frame_get_frame (TidyTextureFrame *frame, + gfloat *top, + gfloat *right, + gfloat *bottom, + gfloat *left); + +void tidy_texture_frame_set_needs_paint (TidyTextureFrame *frame, + gboolean needs_paint); + +G_END_DECLS + +#endif /* _HAVE_TIDY_TEXTURE_FRAME_H */ diff --git a/matchbox/xas.c b/matchbox/xas.c new file mode 100644 index 0000000..47329dc --- /dev/null +++ b/matchbox/xas.c @@ -0,0 +1,906 @@ +/* Asynchronous Xlib hack utiltys lib + * + * Copyright (c) 2005 Matthew Allum + * + * Contains portions of code from Metacity and Xlib thus; + * + * Copyright (C) 2002 Havoc Pennington + * Copyright (C) 1986, 1998 The Open Group + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation. + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of The Open Group shall not be + * used in advertising or otherwise to promote the sale, use or other dealings + * in this Software without prior written authorization from The Open Group. + */ + +#include "xas.h" + +#if MBWM_WANT_DEBUG +#include "mb-wm-debug.h" +#include <stdio.h> +#define XAS_DBG(x, a...) \ +if (mbwm_debug_flags & MBWM_DEBUG_XAS) \ + fprintf(stderr, __FILE__ ":%d,%s() " x "\n", __LINE__, __func__, ##a) +#else +#define XAS_DBG(x, a...) do {} while (0) +#endif +#define XAS_MARK() XAS_DBG("--mark--"); + +#ifdef MBWM_WANT_ASSERT +#include <assert.h> +#define XAS_ASSERT(x) assert(x) +#else +#define XAS_ASSERT(x) do {} while (0) +#endif + +#define XAS_ALIGN_VALUE(this, boundary) \ + (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1))) + +#define NEED_REPLIES +#include <X11/Xlibint.h> + +#ifndef NULL +#define NULL ((void*)0) +#endif + +typedef struct XasTask XasTask; +typedef struct XasTaskGetProperty XasTaskGetProperty; +typedef struct XasTaskGetWinAttr XasTaskGetWinAttr; +typedef struct XasTaskGetGeom XasTaskGetGeom; + +#define XAS_TASK(t) (XasTask*)(t) + +typedef enum XasTaskType +{ + XAS_TASK_UNKNOWN, + XAS_TASK_GET_PROPERTY, + XAS_TASK_GET_WIN_ATTR, + XAS_TASK_GET_GEOM, + +} XasTaskType; + +struct XasContext +{ + _XAsyncHandler async; + Display *xdpy; + XasTask *tasks_pending; + int n_tasks_pending; + XasTask *tasks_completed; + int n_tasks_completed; +}; + +struct XasTask +{ + XasTask *next; + XasTaskType type; + XasContext *ctx; + unsigned long request_seq; + Bool have_reply; + int error; + +}; + +struct XasTaskGetProperty +{ + XasTask task; + + Window window; + Atom property; + + Atom actual_type; + int actual_format; + + unsigned long n_items; + unsigned long bytes_after; + unsigned char *data; +}; + +struct XasTaskGetWinAttr +{ + XasTask task; + Window window; + XasWindowAttributes *attr; +}; + +struct XasTaskGetGeom +{ + XasTask task; + Drawable drw; + Window root; + int x; + int y; + unsigned int width; + unsigned int height; + unsigned int border; + unsigned int depth; +}; + +static XasTask* +task_list_append(XasTask *tasks, XasTask *task) +{ + XasTask *t = NULL; + + /* FIXME: use hash on seq_req key instead ? */ + + if (tasks == NULL) return task; + + for (t = tasks; t->next != NULL; t = t->next); + + t->next = task; + + return tasks; +} + +XasTask* +task_list_remove(XasTask *tasks, XasTask *task) +{ + XasTask *t = NULL, *p = NULL; + + if (task == tasks) + { + t = task->next; + tasks->next = NULL; + return t; + } + + p = tasks; + t = tasks->next; + + while (t != NULL) + { + if (t == task) + { + p->next = t->next; + task->next = NULL; + return tasks; + } + p = t; t = t->next; + } + + return tasks; +} + +static void +task_init(XasContext *ctx, XasTask *task, XasTaskType type) +{ + task->ctx = ctx; + task->next = NULL; + task->type = type; + task->request_seq = ctx->xdpy->request; + task->have_reply = False; +} + +static void +task_complete(XasContext *ctx, XasTask *task) +{ + ctx->tasks_pending = task_list_remove(ctx->tasks_pending, task); + ctx->n_tasks_pending--; + ctx->tasks_completed = task_list_append(ctx->tasks_completed, task); + ctx->n_tasks_completed++; +} + +static XasTask* +xas_find_task_for_request_seq(XasContext *ctx, + XasTask *task_list, + unsigned long request_seq) +{ + XasTask *task = NULL; + + task = task_list; + + while (task != NULL) + { + if (task->request_seq == request_seq) + return task; + task = task->next; + } + + XAS_DBG("Failed to find task\n"); + return NULL; +} + +static Bool +xas_async_get_geom_handler (XasContext *ctx, + XasTaskGetGeom *task, + xReply *rep, + char *buf, + int len) +{ + Display *dpy; + xGetGeometryReply *repl; + xGetGeometryReply replbuf; + + dpy = ctx->xdpy; + + task->task.have_reply = True; + + task_complete (ctx, XAS_TASK(task)); + + if (rep->generic.type == X_Error) + { + xError errbuf; + task->task.error = rep->error.errorCode; + _XGetAsyncReply (dpy, (char *)&errbuf, rep, buf, len, + (SIZEOF (xError) - SIZEOF (xReply)) >> 2, + False); + return True; + } + + repl = (xGetGeometryReply *) + _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len, + (SIZEOF(xGetGeometryReply) - SIZEOF(xReply)) >> 2, + True); + + task->root = repl->root; + task->x = cvtINT16toInt (repl->x); + task->y = cvtINT16toInt (repl->y); + task->width = repl->width; + task->height = repl->height; + task->border = repl->borderWidth; + task->depth = repl->depth; + + return True; +} + + +static Bool +xas_async_get_win_attr_handler (XasContext *ctx, + XasTaskGetWinAttr *task, + xReply *rep, + char *buf, + int len) +{ + Display *dpy; + xGetWindowAttributesReply replbuf; + xGetWindowAttributesReply *repl; + + dpy = ctx->xdpy; + + task->task.have_reply = True; + + task_complete (ctx, XAS_TASK(task)); + + if (rep->generic.type == X_Error) + { + xError errbuf; + task->task.error = rep->error.errorCode; + _XGetAsyncReply (dpy, (char *)&errbuf, rep, buf, len, + (SIZEOF (xError) - SIZEOF (xReply)) >> 2, + False); + return True; + } + + repl = (xGetWindowAttributesReply *) + _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len, + (SIZEOF(xGetWindowAttributesReply) - SIZEOF(xReply)) >> 2, + True); + + task->attr = (XasWindowAttributes *)Xmalloc(sizeof(XasWindowAttributes)); + + if (task->attr == NULL) + { + task->task.error = BadAlloc; + return True; + } + + task->attr->class = repl->class; + task->attr->bit_gravity = repl->bitGravity; + task->attr->win_gravity = repl->winGravity; + task->attr->backing_store = repl->backingStore; + task->attr->backing_planes = repl->backingBitPlanes; + task->attr->backing_pixel = repl->backingPixel; + task->attr->save_under = repl->saveUnder; + task->attr->colormap = repl->colormap; + task->attr->map_installed = repl->mapInstalled; + task->attr->map_state = repl->mapState; + task->attr->all_event_masks = repl->allEventMasks; + task->attr->your_event_mask = repl->yourEventMask; + task->attr->do_not_propagate_mask = repl->doNotPropagateMask; + task->attr->override_redirect = repl->override; + task->attr->visual = _XVIDtoVisual (dpy, repl->visualID); + + return True; +} + +static Bool +xas_async_get_property_handler (XasContext *ctx, + XasTaskGetProperty *task, + xReply *rep, + char *buf, + int len) +{ + Display *dpy; + xGetPropertyReply replbuf; + xGetPropertyReply *reply; + int bytes_read; + + /* Code and comments here wripped out of Metacity, Copyright + * Havoc Pennington. + */ + + /* FIXME: Really check this code is really reliable/safe.*/ + + dpy = ctx->xdpy; + + XAS_DBG ("seeing request seq %ld buflen %d", + dpy->last_request_read, len); + + task_complete (ctx, XAS_TASK(task)); + + /* read bytes so far */ + bytes_read = SIZEOF (xReply); + + if (rep->generic.type == X_Error) + { + xError errbuf; + + task->task.error = rep->error.errorCode; + + /* We return True (meaning we consumed the reply) + * because otherwise it would invoke the X error handler, + * and an async API is useless if you have to synchronously + * trap X errors. Also GetProperty can always fail, pretty + * much, so trapping errors is always what you want. + * + * We have to eat all the error reply data here. + * (kind of a charade as we know sizeof(xError) == sizeof(xReply)) + * + * Passing discard = True seems to break things; I don't understand + * why, because there should be no extra data in an error reply, + * right? + */ + _XGetAsyncReply (dpy, (char *)&errbuf, rep, buf, len, + (SIZEOF (xError) - bytes_read) >> 2, /* 32-bit words */ + False); /* really seems like it should be True */ + return True; + } + + XAS_DBG ("already read %d bytes reading %d more for total of %d; generic.length = %ld", + bytes_read, (SIZEOF (xGetPropertyReply) - bytes_read) >> 2, + SIZEOF (xGetPropertyReply), rep->generic.length); + + reply = (xGetPropertyReply *) + _XGetAsyncReply (dpy, (char *)&replbuf, rep, buf, len, + (SIZEOF (xGetPropertyReply) - bytes_read) >> 2, + False); /* False means expecting more data to follow, + * don't eat the rest of the reply + */ + + bytes_read = SIZEOF (xGetPropertyReply); + + XAS_DBG ("have reply propertyType = %ld format = %d n_items = %ld", + reply->propertyType, reply->format, reply->nItems); + + /* This is all copied from XGetWindowProperty(). Not sure we should + * LockDisplay(). Not sure I'm passing the right args to + * XGetAsyncData(). Not sure about a lot of things. + */ + + /* LockDisplay (dpy); */ + + if (reply->propertyType != None) + { + long nbytes, netbytes; + + /* this alignment macro from orbit2 */ + + switch (reply->format) + { + /* + * One extra byte is malloced than is needed to contain the property + * data, but this last byte is null terminated and convenient for + * returning string properties, so the client doesn't then have to + * recopy the string to make it null terminated. + */ + case 8: + nbytes = reply->nItems; + /* there's padding to word boundary */ + netbytes = XAS_ALIGN_VALUE (nbytes, 4); + if (nbytes + 1 > 0 && + (task->data = (unsigned char *) Xmalloc ((unsigned)nbytes + 1))) + { + + XAS_DBG ("already read %d bytes using %ld, more eating %ld more", + bytes_read, nbytes, netbytes); + + /* _XReadPad (dpy, (char *) task->data, netbytes); */ + _XGetAsyncData (dpy, task->data, buf, len, + bytes_read, nbytes, + netbytes); + } + break; + + case 16: + nbytes = reply->nItems * sizeof (short); + netbytes = reply->nItems << 1; + netbytes = XAS_ALIGN_VALUE (netbytes, 4); /* to word boundary */ + if (nbytes + 1 > 0 && + (task->data = (unsigned char *) Xmalloc ((unsigned)nbytes + 1))) + { + XAS_DBG ("%s: already read %d bytes using %ld more, eating %ld more\n", + __FUNCTION__, bytes_read, nbytes, netbytes); + + /* _XRead16Pad (dpy, (short *) task->data, netbytes); */ + _XGetAsyncData (dpy, task->data, buf, len, + bytes_read, nbytes, netbytes); + } + break; + + case 32: + /* NOTE buffer is in longs to match XGetWindowProperty() */ + nbytes = reply->nItems * sizeof (long); + netbytes = reply->nItems << 2; /* wire size always 32 bits though */ + if (nbytes + 1 > 0 && + (task->data = (unsigned char *) Xmalloc ((unsigned)nbytes + 1))) + { + XAS_DBG ("already read %d bytes using %ld more, eating %ld more", + bytes_read, nbytes, netbytes); + + /* We have to copy the XGetWindowProperty() crackrock + * and get format 32 as long even on 64-bit platforms. + */ + if (sizeof (long) == 8) + { + unsigned char *netdata; + unsigned char *lptr; + unsigned char *end_lptr; + + /* Store the 32-bit values in the end of the array */ + netdata = task->data + nbytes / 2; + + _XGetAsyncData (dpy, netdata, buf, len, + bytes_read, netbytes, + netbytes); + + /* Now move the 32-bit values to the front */ + + lptr = task->data; + end_lptr = task->data + nbytes; + while (lptr != end_lptr) + { + *(long*) lptr = *(CARD32*) netdata; + lptr += sizeof (long); + netdata += sizeof (CARD32); + } + } + else + { + /* Here the wire format matches our actual format */ + _XGetAsyncData (dpy, task->data, buf, len, + bytes_read, netbytes, + netbytes); + } + } + break; + + default: + /* + * This part of the code should never be reached. If it is, + * the server sent back a property with an invalid format. + * This is a BadImplementation error. + * + * However this async GetProperty API doesn't report errors + * via the standard X mechanism, so don't do anything about + * it, other than store it in task->error. + */ + task->task.error = BadImplementation; + nbytes = netbytes = 0L; + break; + } + + if (task->data == NULL) + { + task->task.error = BadAlloc; + + XAS_DBG ("already read %d bytes eating %ld", bytes_read, netbytes); + + /* _XEatData (dpy, (unsigned long) netbytes); */ + _XGetAsyncData (dpy, NULL, buf, len, bytes_read, 0, netbytes); + + /* UnlockDisplay (dpy); */ + return BadAlloc; /* not Success */ + } + + (task->data)[nbytes] = '\0'; + } + + XAS_DBG ("have data"); + + task->actual_type = reply->propertyType; + task->actual_format = reply->format; + task->n_items = reply->nItems; + task->bytes_after = reply->bytesAfter; + + /* UnlockDisplay (dpy); */ + + return True; +} + +static Bool +xas_async_handler (Display *dpy, + xReply *rep, + char *buf, + int len, + XPointer data) +{ + XasContext *ctx = (XasContext *)data; + XasTask *task; + + XAS_ASSERT(ctx->xdpy == dpy); + + if (!ctx->tasks_pending) + return False; + + task = xas_find_task_for_request_seq(ctx, + ctx->tasks_pending, + dpy->last_request_read); + if (!task) + return False; + + switch (task->type) + { + case XAS_TASK_GET_PROPERTY: + return xas_async_get_property_handler (ctx, + (XasTaskGetProperty*)task, + rep, buf, len); + case XAS_TASK_GET_WIN_ATTR: + return xas_async_get_win_attr_handler (ctx, + (XasTaskGetWinAttr*)task, + rep, buf, len); + case XAS_TASK_GET_GEOM: + return xas_async_get_geom_handler (ctx, + (XasTaskGetGeom*)task, + rep, buf, len); + case XAS_TASK_UNKNOWN: + default: + /* Should never get here */ + return False; + } + + return False; +} + +/* public */ + +XasContext* +xas_context_new(Display *xdpy) +{ + XasContext *ctx = NULL; + + ctx = (XasContext *)Xmalloc(sizeof(XasContext)); + + ctx->xdpy = xdpy; + ctx->async.next = xdpy->async_handlers; + ctx->async.handler = xas_async_handler; + ctx->async.data = (XPointer) ctx; + ctx->xdpy->async_handlers = &ctx->async; + + ctx->tasks_pending = NULL; + ctx->n_tasks_pending = 0; + ctx->tasks_completed = NULL; + ctx->n_tasks_completed = 0; + + return ctx; +} + +void +xas_context_destroy(XasContext *ctx) +{ + DeqAsyncHandler (ctx->xdpy, &ctx->async); + + /* FIXME: empty pending and completed lists */ + + free(ctx); +} + +XasCookie +xas_get_property(XasContext *ctx, + Window win, + Atom property, + long offset, + long length, + Bool delete, + Atom req_type) +{ + Display *dpy ; + XasTaskGetProperty *task; + xGetPropertyReq *req; + + LockDisplay (ctx->xdpy); + + dpy = ctx->xdpy; /* GetReq() needs this */ + + task = Xcalloc (1, sizeof (XasTaskGetProperty)); + if (task == NULL) + { + UnlockDisplay (dpy); + return 0; + } + + GetReq (GetProperty, req); + req->window = win; + req->property = property; + req->type = req_type; + req->delete = delete; + req->longOffset = offset; + req->longLength = length; + + /* needed ? error.sequenceNumber = ctx->xdpy->request;*/ + + task_init(ctx, XAS_TASK(task), XAS_TASK_GET_PROPERTY); + + task->window = win; + task->property = property; + + ctx->tasks_pending = task_list_append(ctx->tasks_pending, &task->task); + + ctx->n_tasks_pending ++; + + UnlockDisplay (dpy); + + SyncHandle (); + + return task->task.request_seq; +} + +Bool +xas_have_reply(XasContext *ctx, + XasCookie cookie) +{ + return (xas_find_task_for_request_seq(ctx, ctx->tasks_completed, cookie) != NULL); +} + + +Status +xas_get_property_reply(XasContext *ctx, + XasCookie cookie, + Atom *actual_type_return, + int *actual_format_return, + unsigned long *nitems_return, + unsigned long *bytes_after_return, + unsigned char **prop_return, + int *x_error_code) +{ + Display *dpy; + XasTaskGetProperty *task = NULL; + Bool result = False; + + dpy = ctx->xdpy; /* For SyncHandle (); */ + + if (x_error_code) *x_error_code = 0; /* No error as yet */ + + task = (XasTaskGetProperty *) xas_find_task_for_request_seq(ctx, + ctx->tasks_completed, + cookie); + if (task == NULL) + { + XAS_DBG("Failed to find cookie"); + return False; + } + + if (!task->task.error) + { + *actual_type_return = task->actual_type; + *actual_format_return = task->actual_format; + *nitems_return = task->n_items; + *bytes_after_return = task->bytes_after; + *prop_return = task->data; + + result = True; + } + else + { + XAS_DBG("is error"); + if (x_error_code) + *x_error_code = task->task.error; + result = False;; + } + + SyncHandle (); + + ctx->tasks_completed = task_list_remove(ctx->tasks_completed, + XAS_TASK(task)); + ctx->n_tasks_completed--; + + XFree (task); + + return result; +} + +XasCookie +xas_get_window_attributes(XasContext *ctx, + Window win) +{ + xResourceReq *req; + Display *dpy ; + XasTaskGetWinAttr *task; + + LockDisplay (ctx->xdpy); + + dpy = ctx->xdpy; /* GetReq() needs this */ + + task = Xcalloc (1, sizeof (XasTaskGetWinAttr)); + if (task == NULL) + { + UnlockDisplay (dpy); + return 0; + } + + GetResReq(GetWindowAttributes, win, req); + + task_init(ctx, XAS_TASK(task), XAS_TASK_GET_WIN_ATTR); + + task->window = win; + + ctx->tasks_pending = task_list_append(ctx->tasks_pending, &task->task); + ctx->n_tasks_pending ++; + + UnlockDisplay (dpy); + + SyncHandle (); + + return task->task.request_seq; +} + +XasWindowAttributes* +xas_get_window_attributes_reply(XasContext *ctx, + XasCookie cookie, + int *x_error_code) +{ + Display *dpy; + XasTaskGetWinAttr *task = NULL; + XasWindowAttributes *result = NULL; + + dpy = ctx->xdpy; /* For SyncHandle (); */ + + if (x_error_code) *x_error_code = 0; /* No error as yet */ + + task = (XasTaskGetWinAttr *) xas_find_task_for_request_seq(ctx, + ctx->tasks_completed, + cookie); + + if (task == NULL) + return NULL; + + if (task->task.type != XAS_TASK_GET_WIN_ATTR) + return NULL; + + if (!task->task.error) + { + result = task->attr; + } + else + { + XAS_DBG("is error"); + if (x_error_code) + *x_error_code = task->task.error; + } + + SyncHandle (); + + ctx->tasks_completed = task_list_remove(ctx->tasks_completed, + XAS_TASK(task)); + ctx->n_tasks_completed--; + + XFree (task); + + return result; +} + +XasCookie +xas_get_geometry(XasContext *ctx, + Drawable d) +{ + xResourceReq *req; + Display *dpy ; + XasTaskGetGeom *task; + + LockDisplay (ctx->xdpy); + + dpy = ctx->xdpy; /* GetReq() needs this */ + + task = Xcalloc (1, sizeof (XasTaskGetGeom)); + if (task == NULL) + { + UnlockDisplay (dpy); + return 0; + } + + GetResReq(GetGeometry, d, req); + + task_init(ctx, XAS_TASK(task), XAS_TASK_GET_GEOM); + + task->drw = d; + + ctx->tasks_pending = task_list_append(ctx->tasks_pending, &task->task); + ctx->n_tasks_pending ++; + + UnlockDisplay (dpy); + + SyncHandle (); + + return task->task.request_seq; +} + +Status +xas_get_geometry_reply (XasContext *ctx, + XasCookie cookie, + int *x_return, + int *y_return, + unsigned int *width_return, + unsigned int *height_return, + unsigned int *border_width_return, + unsigned int *depth_return, + int *x_error_code) +{ + Display *dpy; + XasTaskGetGeom *task = NULL; + Status result = False; + + dpy = ctx->xdpy; /* For SyncHandle (); */ + + if (x_error_code) *x_error_code = 0; /* No error as yet */ + + task = (XasTaskGetGeom *) xas_find_task_for_request_seq(ctx, + ctx->tasks_completed, + cookie); + + if (task == NULL) + { + XAS_DBG("Failed to find task\n"); + return False; + } + + if (task->task.type != XAS_TASK_GET_GEOM) + { + XAS_DBG("Found task, but type different to expected ( %i vs %i )\n", + task->task.type, XAS_TASK_GET_GEOM); + return False; + } + + if (!task->task.error) + { + *x_return = task->x; + *y_return = task->y; + *width_return = task->width; + *height_return = task->height; + *border_width_return = task->border; + *depth_return = task->depth; + result = True; + } + else + { + XAS_DBG("is error"); + if (x_error_code) + *x_error_code = task->task.error; + } + + SyncHandle (); + + ctx->tasks_completed = task_list_remove(ctx->tasks_completed, + XAS_TASK(task)); + ctx->n_tasks_completed--; + + XFree (task); + + return result; +} + + + diff --git a/matchbox/xas.h b/matchbox/xas.h new file mode 100644 index 0000000..e9b7ca8 --- /dev/null +++ b/matchbox/xas.h @@ -0,0 +1,114 @@ +/* Asynchronous Xlib hack utiltys lib + * + * Copyright (c) 2005 Matthew Allum + * + * Contains portions of code from Metacity and Xlib. + * + * Copyright (C) 2002 Havoc Pennington + * Copyright (C) 1986, 1998 The Open Group + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation. + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of The Open Group shall not be + * used in advertising or otherwise to promote the sale, use or other dealings + * in this Software without prior written authorization from The Open Group. + */ + +#ifndef _HAVE_XAS_H +#define _HAVE_XAS_H + +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +typedef struct XasContext XasContext; +typedef unsigned long XasCookie; +typedef struct XasWindowAttributes XasWindowAttributes; + +/* XWindowAttributes without geom info */ +struct XasWindowAttributes { + Visual *visual; + Window root; + int class; + int bit_gravity; + int win_gravity; + int backing_store; + unsigned long backing_planes; + unsigned long backing_pixel; + Bool save_under; + Colormap colormap; + Bool map_installed; + int map_state; + long all_event_masks; + long your_event_mask; + long do_not_propagate_mask; + Bool override_redirect; +}; + +XasContext* +xas_context_new(Display *xdpy); + +void +xas_context_destroy(XasContext *ctx); + +XasCookie +xas_get_property(XasContext *ctx, + Window win, + Atom property, + long offset, + long length, + Bool delete, + Atom req_type); + +Status +xas_get_property_reply(XasContext *ctx, + XasCookie cookie, + Atom *actual_type_return, + int *actual_format_return, + unsigned long *nitems_return, + unsigned long *bytes_after_return, + unsigned char **prop_return, + int *x_error_code); + +XasCookie +xas_get_window_attributes(XasContext *ctx, + Window win); + +XasWindowAttributes* +xas_get_window_attributes_reply(XasContext *ctx, + XasCookie cookie, + int *x_error_code); + +XasCookie +xas_get_geometry(XasContext *ctx, + Drawable d); + +Status +xas_get_geometry_reply (XasContext *ctx, + XasCookie cookie, + int *x_return, + int *y_return, + unsigned int *width_return, + unsigned int *height_return, + unsigned int *border_width_return, + unsigned int *depth_return, + int *x_error_code); +Bool +xas_have_reply(XasContext *ctx, + XasCookie cookie); + +#endif |