/* * The Idea behind this is quite simple: when all managed windows are closed * and the WM exits, there should have been an unref call for each ref call. To * test do something like * * export DISPLAY=:whatever; * export MB_DEBUG=obj-ref,obj-unref * matchbox-window-manager-2-simple & * gedit * kill -TERM $(pidof gedit) * kill -TERM $(pidof matchbox-window-manager-2-simple) * * If you see '=== object count at exit x ===' then we either have a leak * (x > 0) or are unrefing a dangling pointer (x < 0). */ static void signal_handler (int sig) { if (sig == SIGTERM) { int count; mb_wm_object_unref (MB_WM_OBJECT (wm)); mb_wm_object_dump (); exit (sig); } }
int main(int argc, char **argv) { #if MBWM_WANT_DEBUG struct sigaction sa; sigfillset(&sa.sa_mask); sa.sa_handler = signal_handler; sigaction(SIGTERM, &sa, NULL); #endif mb_wm_object_init(); wm = maemo_window_manager_new(argc, argv); mb_wm_init (wm); if (wm == NULL) mb_wm_util_fatal_error("OOM?"); mb_wm_keys_binding_add_with_spec (wm, "<alt>d", key_binding_func, NULL, (void*)KEY_ACTION_TOGGLE_DESKTOP); mb_wm_keys_binding_add_with_spec (wm, "<alt>n", key_binding_func, NULL, (void*)KEY_ACTION_PAGE_NEXT); mb_wm_keys_binding_add_with_spec (wm, "<alt>p", key_binding_func, NULL, (void*)KEY_ACTION_PAGE_PREV); mb_wm_main_loop (wm); mb_wm_object_unref (MB_WM_OBJECT (wm)); #if MBWM_WANT_DEBUG mb_wm_object_dump (); #endif return 1; }
static void mb_wm_decor_destroy (MBWMObject* obj) { MBWMDecor * decor = MB_WM_DECOR(obj); MBWMList * l; 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); for (l = decor->buttons; l; l = l->next) { mb_wm_decor_button_unrealize((MBWMDecorButton *)l->data); mb_wm_object_unref (MB_WM_OBJECT (l->data)); /* we don't free, because the dispose handler of the decor button * removes itself */ } if (decor->buttons) mb_wm_util_list_free (decor->buttons); if (decor->press_cb_id) mb_wm_main_context_x_event_handler_remove (ctx, ButtonPress, decor->press_cb_id); if (decor->release_cb_id) mb_wm_main_context_x_event_handler_remove (ctx, ButtonRelease, decor->release_cb_id); if (decor->xwin != None) { mb_wm_util_async_trap_x_errors(decor->parent_client->wmref->xdpy); XDestroyWindow (decor->parent_client->wmref->xdpy, decor->xwin); mb_wm_util_async_untrap_x_errors(); } memset (decor, 0, sizeof (*decor)); }
int main(int argc, char **argv) { Display * dpy = NULL; #if MBWM_WANT_DEBUG struct sigaction sa; sigfillset(&sa.sa_mask); sa.sa_handler = signal_handler; sigaction(SIGTERM, &sa, NULL); #endif mb_wm_object_init(); #if USE_GTK printf ("initializing gtk\n"); gtk_init (&argc, &argv); dpy = GDK_DISPLAY(); #endif #if ENABLE_CLUTTER_COMPOSITE_MANAGER /* * If using clutter, we share the display connection, and hook * our xevent handler into the clutter main loop. * * If we are also doing gtk integration, we need to make clutter to * use the gtk display connection. */ if (dpy) clutter_x11_set_display (dpy); #if USE_GTK clutter_x11_disable_event_retrieval (); #endif clutter_init (&argc, &argv); if (!dpy) dpy = clutter_x11_get_default_display (); #endif wm = mb_wm_new_with_dpy (argc, argv, dpy); mb_wm_init (wm); if (wm == NULL) mb_wm_util_fatal_error("OOM?"); mb_wm_keys_binding_add_with_spec (wm, "<alt>d", key_binding_func, NULL, (void*)KEY_ACTION_TOGGLE_DESKTOP); mb_wm_keys_binding_add_with_spec (wm, "<alt>n", key_binding_func, NULL, (void*)KEY_ACTION_PAGE_NEXT); mb_wm_keys_binding_add_with_spec (wm, "<alt>p", key_binding_func, NULL, (void*)KEY_ACTION_PAGE_PREV); mb_wm_main_loop(wm); mb_wm_object_unref (MB_WM_OBJECT (wm)); #if MBWM_WANT_DEBUG mb_wm_object_dump (); #endif return 1; }
static Bool mb_wm_decor_button_press_handler (XButtonEvent *xev, void *userdata) { MBWMDecorButton *button = (MBWMDecorButton *)userdata; MBWMDecor *decor = button->decor; MBWindowManager *wm; MBWMList *transients = NULL; Bool retval = True; Bool unref_parent_client = False; if (!button->realized || !decor || !decor->parent_client) return False; wm = decor->parent_client->wmref; mb_wm_object_ref (MB_WM_OBJECT(button)); if (xev->window == decor->xwin) { int xmin, ymin, xmax, ymax; MBWMList *l; transients = 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) { XUngrabPointer(wm->xdpy, CurrentTime); retval = True; if (xev->type != ButtonRelease || xev->x < decor->geom.x || xev->x > decor->geom.x+decor->geom.width || xev->y < decor->geom.y || xev->y > decor->geom.x+decor->geom.height) goto done; g_debug("%s not on button -- send GRAB_TRANSFER", __FUNCTION__); 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 */ goto done; } if (xev->type != ButtonPress) { retval = True; goto done; } g_debug("%s on button", __FUNCTION__); 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; int status; /* * First, call the custom function if any. */ if (button->press) button->press(wm, button, button->userdata); mb_wm_util_async_trap_x_errors(wm->xdpy); status = XGrabPointer(wm->xdpy, xev->subwindow, False, ButtonPressMask|ButtonReleaseMask| PointerMotionMask|EnterWindowMask|LeaveWindowMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); mb_wm_util_async_untrap_x_errors(); if (status == GrabSuccess) { /* set up release handler to catch ButtonRelease while we * are spinning the main loop */ decor->release_cb_id = mb_wm_main_context_x_event_handler_add ( wm->main_ctx, xev->subwindow, ButtonRelease, (MBWMXEventFunc)mb_wm_decor_release_handler, decor); if (button->state == MBWMDecorButtonStateInactive) { button->state = MBWMDecorButtonStatePressed; mb_wm_theme_paint_button (wm->theme, button); } /* reference the client for the loop, since it could be * unreffed while we are handling events there */ mb_wm_object_ref (MB_WM_OBJECT(decor->parent_client)); unref_parent_client = True; 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); /* * Someone might destroy the window while we are waiting for * the events here. */ if (!button->realized) { /* if the window disappeared, ungrab was done by X. Just remove the handler */ mb_wm_main_context_x_event_handler_remove ( wm->main_ctx, ButtonRelease, decor->release_cb_id); decor->release_cb_id = 0; retval = False; goto done; } if (!decor->release_cb_id) { /* the handler was called while we spinned the loop */ if (button->state == MBWMDecorButtonStatePressed) { button->state = MBWMDecorButtonStateInactive; mb_wm_theme_paint_button (wm->theme, button); } retval = False; goto done; } 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 */ mb_wm_main_context_x_event_handler_remove ( wm->main_ctx, ButtonRelease, decor->release_cb_id); decor->release_cb_id = 0; 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); retval = False; goto done; } } } else { /* * No pending X event, so spin the main loop (this allows * things like timers to work. */ if (!mb_wm_main_context_spin_loop (wm->main_ctx)) /* no events, sleep a while so we don't busy loop */ g_usleep (1000 * 100); } } } } retval = False; } done: mb_wm_util_list_free (transients); mb_wm_object_unref (MB_WM_OBJECT(button)); if (unref_parent_client) mb_wm_object_unref (MB_WM_OBJECT(decor->parent_client)); return retval; }