aboutsummaryrefslogtreecommitdiffstats
path: root/matchbox/mb-wm-decor.c
diff options
context:
space:
mode:
Diffstat (limited to 'matchbox/mb-wm-decor.c')
-rw-r--r--matchbox/mb-wm-decor.c1190
1 files changed, 1190 insertions, 0 deletions
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;
+}