/* * Matchbox Window Manager II - A lightweight window manager not for the * desktop. * * Authored By Matthew Allum * Tomas Frydrych * * 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-window-type-app.h" #include "mb-wm-window-type-panel.h" #include "mb-wm-window-type-dialog.h" #include "mb-wm-window-type-desktop.h" #include "mb-wm-window-type-input.h" #include "mb-wm-window-type-note.h" #include "mb-wm-window-type-menu.h" #include "mb-wm-theme.h" #if ENABLE_COMPOSITE # include "mb-wm-comp-mgr.h" # include "mb-wm-window-type-override.h" # include # include # include #endif #include #include #ifdef HAVE_XFIXES #include /* Used to *really* hide cursor */ #endif #ifdef HAVE_XCURSOR #include #endif #include #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 typedef struct _MBWMManagerEventSource { MBWMManager *wm; GSource source; GPollFD event_poll_fd; } MBWMManagerEventSource; static gboolean mb_wm_manager_event_prepare (GSource *source, int *timeout); static gboolean mb_wm_manager_event_check (GSource *source); static gboolean mb_wm_manager_event_dispatch (GSource *source, GSourceFunc callback, void *user_data); static GSourceFuncs event_funcs = { mb_wm_manager_event_prepare, mb_wm_manager_event_check, mb_wm_manager_event_dispatch, NULL }; typedef struct _MBWMXlibFilterClosure { MBWMXlibFilterFunc filter; void *data; } MBWMXlibFilterClosure; static void mb_wm_process_cmdline (MBWMManager *wm); static void mb_wm_focus_client (MBWMManager *wm, MBWindowManagerClient *client); static Bool mb_wm_manager_activate_window_real (MBWMManager * wm, MBWindowManagerClient *c); static void mb_wm_update_root_win_rectangles (MBWMManager *wm); static Bool mb_wm_is_my_window (MBWMManager *wm, Window xwin, MBWindowManagerClient **client); static void mb_wm_real_get_desktop_geometry (MBWMManager *wm, MBGeometry *geom); static MBWindowManagerClient* mb_wm_client_new_func (MBWMManager *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 (MBWMManager * wm, const char * path) { /* * FIXME -- load the selected theme from some configuration */ return mb_wm_theme_new (wm, path); } static MBWMLayout * mb_wm_layout_new_real (MBWMManager *wm) { MBWMLayout * layout = mb_wm_layout_new (wm); if (!layout) mb_wm_util_fatal_error("OOM?"); return layout; } static void mb_wm_class_init (MBWMObjectClass *klass) { MBWMManagerClass *wm_class; MBWM_MARK(); wm_class = (MBWMManagerClass *)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_manager_activate_window_real; wm_class->layout_new = mb_wm_layout_new_real; wm_class->get_desktop_geometry = mb_wm_real_get_desktop_geometry; #if MBWM_WANT_DEBUG klass->klass_name = "MBWMManager"; #endif } static void mb_wm_destroy (MBWMObject *this) { MBWMManager * wm = MB_WM_MANAGER (this); GList *l = wm->clients; while (l) { GList * 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)); } static int mb_wm_manager_init (MBWMObject *this, va_list vap); int mb_wm_manager_class_type () { static int type = 0; if (UNLIKELY(type == 0)) { static MBWMObjectClassInfo info = { sizeof (MBWMManagerClass), sizeof (MBWMManager), mb_wm_manager_init, mb_wm_destroy, mb_wm_class_init }; type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); } return type; } MBWMManager* mb_wm_manager_new (int argc, char **argv) { MBWMManager *wm = NULL; wm = MB_WM_MANAGER (mb_wm_object_new (MB_WM_TYPE_MANAGER, MBWMObjectPropArgc, argc, MBWMObjectPropArgv, argv, NULL)); if (!wm) return wm; return wm; } MBWMManager* mb_wm_manager_new_with_dpy (int argc, char **argv, Display * dpy) { MBWMManager *wm = NULL; wm = MB_WM_MANAGER (mb_wm_object_new (MB_WM_TYPE_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) { MBWMManager *wm = (MBWMManager*)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) { MBWMManager *wm = (MBWMManager*)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_manager_manager_get_visible_main_window(wm); if (top == client) mb_wm_focus_client (wm, client); else mb_wm_manager_activate_window (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) { MBWMManager *wm = (MBWMManager*)userdata; MBWindowManagerClient *client = NULL; MBWM_MARK(); client = mb_wm_manager_managed_window_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_manager_unmanage_window (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 = g_list_remove (wm->clients, client); mb_wm_object_unref (MB_WM_OBJECT (client)); } } else mb_wm_manager_unmanage_window (wm, client, True); } return True; } static Bool mb_wm_handle_unmap_notify (XUnmapEvent *xev, void *userdata) { MBWMManager *wm = (MBWMManager*)userdata; MBWindowManagerClient *client = NULL; MBWM_MARK(); client = mb_wm_manager_managed_window_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_manager_unmanage_window (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_manager_compositing_enabled (wm)) mb_wm_comp_mgr_unmap_notify (wm->comp_mgr, client); #endif MBWM_DBG ("removing client %p\n", client); mb_wm_manager_unmanage_window (wm, client, True); } } } return True; } static Bool mb_wm_handle_property_notify (XPropertyEvent *xev, void *userdata) { MBWMManager *wm = (MBWMManager*)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_manager_set_theme_from_path (wm, theme_path); XFree (theme_path); } return True; } client = mb_wm_manager_managed_window_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) { MBWMManager * wm = (MBWMManager*)userdata; if (mb_wm_comp_mgr_enabled (wm->comp_mgr)) { MBWindowManagerClient *client; client = mb_wm_manager_managed_window_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) { MBWMManager * wm = (MBWMManager*)userdata; wm->xdpy_width = xev->width; wm->xdpy_height = xev->height; mb_wm_update_root_win_rectangles (wm); mb_wm_manager_display_sync_queue (wm, MBWMSyncGeometry); } static Bool mb_wm_handle_config_request (XConfigureRequestEvent *xev, void *userdata) { MBWMManager *wm = (MBWMManager*)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_manager_managed_window_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_manager_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 (MBWMManager *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) { MBWMManager *wm = (MBWMManager*)userdata; MBWindowManagerClient *client = NULL; MBWMManagerClass *wm_class = MB_WM_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_manager_manage_window (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) { MBWMManager *wm = (MBWMManager*)userdata; MBWindowManagerClient *client = NULL; MBWMManagerClass *wm_class = MB_WM_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_manager_activate_window (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_manager_manage_window (wm, client, True); return True; } static void stack_get_window_list (MBWMManager *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 (MBWMManager *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(); } unsigned long mb_wm_manager_add_event_handler (MBWMManager *wm, Window xwin, int type, MBWMXEventFunc func, void *userdata) { static unsigned long ids = 0; MBWMXEventFuncInfo * func_info; ++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) { wm->event_funcs.damage_notify = g_list_append (wm->event_funcs.damage_notify, func_info); } else #endif switch (type) { case Expose: break; case MapRequest: wm->event_funcs.map_request = g_list_append (wm->event_funcs.map_request, func_info); break; case MapNotify: wm->event_funcs.map_notify= g_list_append (wm->event_funcs.map_notify, func_info); break; case UnmapNotify: wm->event_funcs.unmap_notify= g_list_append (wm->event_funcs.unmap_notify, func_info); break; case DestroyNotify: wm->event_funcs.destroy_notify = g_list_append (wm->event_funcs.destroy_notify, func_info); break; case ConfigureNotify: wm->event_funcs.configure_notify = g_list_append (wm->event_funcs.configure_notify, func_info); break; case ConfigureRequest: wm->event_funcs.configure_request = g_list_append (wm->event_funcs.configure_request, func_info); break; case KeyPress: wm->event_funcs.key_press = g_list_append (wm->event_funcs.key_press, func_info); break; case PropertyNotify: wm->event_funcs.property_notify = g_list_append (wm->event_funcs.property_notify, func_info); break; case ButtonPress: wm->event_funcs.button_press = g_list_append (wm->event_funcs.button_press, func_info); break; case ButtonRelease: wm->event_funcs.button_release = g_list_append (wm->event_funcs.button_release, func_info); break; case MotionNotify: wm->event_funcs.motion_notify = g_list_append (wm->event_funcs.motion_notify, func_info); break; case ClientMessage: wm->event_funcs.client_message = g_list_append (wm->event_funcs.client_message, func_info); break; default: break; } return ids; } void mb_wm_manager_remove_event_handler (MBWMManager *wm, int type, unsigned long id) { GList * l = NULL; GList **l_start; #if ENABLE_COMPOSITE if (type == wm->damage_event_base + XDamageNotify) { l_start = &wm->event_funcs.damage_notify; } else #endif switch (type) { case Expose: break; case MapRequest: l_start = &wm->event_funcs.map_request; break; case MapNotify: l_start = &wm->event_funcs.map_notify; break; case UnmapNotify: l_start = &wm->event_funcs.unmap_notify; break; case DestroyNotify: l_start = &wm->event_funcs.destroy_notify; break; case ConfigureNotify: l_start = &wm->event_funcs.configure_notify; break; case ConfigureRequest: l_start = &wm->event_funcs.configure_request; break; case KeyPress: l_start = &wm->event_funcs.key_press; break; case PropertyNotify: l_start = &wm->event_funcs.property_notify; break; case ButtonPress: l_start = &wm->event_funcs.button_press; break; case ButtonRelease: l_start = &wm->event_funcs.button_release; break; case MotionNotify: l_start = &wm->event_funcs.motion_notify; break; case ClientMessage: l_start = &wm->event_funcs.client_message; break; default: break; } l = *l_start; while (l) { MBWMXEventFuncInfo * info = l->data; if (info->id == id) { GList * prev = l->prev; GList * next = l->next; if (prev) prev->next = next; else *l_start = next; if (next) next->prev = prev; free (info); g_list_free_1 (l); return; } l = l->next; } } static void process_xlib_event (MBWMManager *wm, XEvent *xev) { GList *iter; Window xwin = xev->xany.window; #if (MBWM_WANT_DEBUG) { if (mbwm_debug_flags & MBWM_DEBUG_EVENT) { MBWindowManagerClient *ev_client; ev_client = mb_wm_manager_managed_window_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 = wm->event_funcs.damage_notify; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * 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 = wm->event_funcs.client_message; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * 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 = wm->event_funcs.map_request; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * next = iter->next; if (msg_xwin == None || msg_xwin == xwin) { if (!(MBWMManagerMapRequestFunc)XE_ITER_GET_FUNC(iter) ((XMapRequestEvent*)&xev->xmaprequest, XE_ITER_GET_DATA(iter))) break; } iter = next; } break; case MapNotify: iter = wm->event_funcs.map_notify; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * next = iter->next; if (msg_xwin == None || msg_xwin == xwin) { if (!(MBWMManagerMapNotifyFunc)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 = wm->event_funcs.unmap_notify; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * next = iter->next; if (msg_xwin == None || msg_xwin == xwin) { if (!(MBWMManagerUnmapNotifyFunc)XE_ITER_GET_FUNC(iter) ((XUnmapEvent*)&xev->xunmap, XE_ITER_GET_DATA(iter))) break; } iter = next; } break; case DestroyNotify: iter = wm->event_funcs.destroy_notify; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * next = iter->next; if (msg_xwin == None || msg_xwin == xwin) { if (!(MBWMManagerDestroyNotifyFunc)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 = wm->event_funcs.configure_notify; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * next = iter->next; if (msg_xwin == None || msg_xwin == xwin) { if (!(MBWMManagerConfigureNotifyFunc)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 = wm->event_funcs.configure_request; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * next = iter->next; if (msg_xwin == None || msg_xwin == xwin) { if (!(MBWMManagerConfigureRequestFunc)XE_ITER_GET_FUNC(iter) ((XConfigureRequestEvent*)&xev->xconfigurerequest, XE_ITER_GET_DATA(iter))) break; } iter = next; } break; case KeyPress: iter = wm->event_funcs.key_press; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * next = iter->next; if (msg_xwin == None || msg_xwin == xwin) { if (!(MBWMManagerKeyPressFunc)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 = wm->event_funcs.property_notify; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * next = iter->next; if (msg_xwin == None || msg_xwin == xwin) { if (!(MBWMManagerPropertyNotifyFunc)XE_ITER_GET_FUNC(iter) ((XPropertyEvent*)&xev->xproperty, XE_ITER_GET_DATA(iter))) break; } iter = next; } break; case ButtonPress: iter = wm->event_funcs.button_press; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * next = iter->next; if (msg_xwin == None || msg_xwin == xwin) { if (!(MBWMManagerButtonPressFunc)XE_ITER_GET_FUNC(iter) ((XButtonEvent*)&xev->xbutton, XE_ITER_GET_DATA(iter))) break; } iter = next; } break; case ButtonRelease: iter = wm->event_funcs.button_release; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * next = iter->next; if (msg_xwin == None || msg_xwin == xwin) { if (!(MBWMManagerButtonReleaseFunc)XE_ITER_GET_FUNC(iter) ((XButtonEvent*)&xev->xbutton, XE_ITER_GET_DATA(iter))) break; } iter = next; } break; case MotionNotify: iter = wm->event_funcs.motion_notify; while (iter) { Window msg_xwin = XE_ITER_GET_XWIN(iter); GList * next = iter->next; if (msg_xwin == None || msg_xwin == xwin) { if (!(MBWMManagerMotionNotifyFunc)XE_ITER_GET_FUNC(iter) ((XMotionEvent*)&xev->xmotion, XE_ITER_GET_DATA(iter))) break; } iter = next; } break; } } static void process_events (MBWMManager *wm) { XEvent *xevent; while (xevent = g_queue_pop_tail (wm->event_queue)) { process_xlib_event (wm, xevent); g_slice_free (XEvent, xevent); } } static void manager_update (MBWMManager *wm) { /* Sync all changes to display */ MBWindowManagerClient *client = NULL; process_events (wm); if (!wm->sync_type) return; MBWM_MARK(); MBWM_TRACE (); /* XXX: really? why do we do this‽ */ 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 (MBWMManager *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; GList *l; list_size = g_list_length (wm->clients); wins = alloca (sizeof(Window) * list_size); app_wins = alloca (sizeof(Window) * list_size); if ((wm->flags & MBWMManagerFlagDesktop) && wm->desktop) { wins[cnt++] = MB_WM_CLIENT_XWIN(wm->desktop); } mb_wm_stack_enumerate (wm,c) { if (!(wm->flags & MBWMManagerFlagDesktop) || 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_manager_manage_window (MBWMManager *wm, MBWindowManagerClient *client, Bool activate) { /* Add to our list of managed clients */ MBWMSyncType sync_flags = MBWMSyncVisibility | MBWMSyncGeometry; if (client == NULL) return; wm->clients = g_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_manager_activate_window (wm, client); else mb_wm_client_show (client); mb_wm_manager_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_manager_unmanage_window (MBWMManager *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 = g_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_manager_unfocus_window (wm, client); if (client == wm->desktop) wm->desktop = NULL; if (destroy) mb_wm_object_unref (MB_WM_OBJECT(client)); mb_wm_manager_display_sync_queue (wm, sync_flags); } MBWindowManagerClient* mb_wm_manager_managed_window_from_xwindow(MBWMManager *wm, Window win) { MBWindowManagerClient *client = NULL; GList *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_manager_managed_window_from_frame (MBWMManager *wm, Window frame) { MBWindowManagerClient *client = NULL; GList *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; } void mb_wm_manager_main_loop (MBWMManager *wm) { GMainLoop * loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); g_main_loop_unref (loop); } static gboolean do_manager_update (MBWMManager *wm) { wm->do_update_idle = 0; manager_update (wm); return FALSE; } void mb_wm_manager_queue_update (MBWMManager *wm) { /* Note we use a priority slightly higher than the default * because we need to ensure we consume the events queued * by a source that has DEFUALT_IDLE priority */ if (!wm->do_update_idle) wm->do_update_idle = g_idle_add_full (G_PRIORITY_HIGH_IDLE, (GSourceFunc)do_manager_update, wm, NULL); } void mb_wm_manager_get_display_geometry (MBWMManager *wm, MBGeometry *geometry) { geometry->x = 0; geometry->y = 0; geometry->width = wm->xdpy_width; geometry->height = wm->xdpy_height; } void mb_wm_manager_display_sync_queue (MBWMManager* wm, MBWMSyncType sync) { wm->sync_type |= sync; } static void mb_wm_manage_preexistsing_wins (MBWMManager* wm) { unsigned int nwins, i; Window foowin1, foowin2, *wins; XWindowAttributes attr; MBWMManagerClass * wm_class = MB_WM_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_manager_manage_window() -- 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_manager_manage_window(wm, client, False); } else mb_wm_object_unref (MB_WM_OBJECT (win)); } } XFree(wins); } static void mb_wm_real_get_desktop_geometry (MBWMManager *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 (MBWMManager *wm, MBGeometry * geom) { MBWMManagerClass *wm_class; wm_class = (MBWMManagerClass *) 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 (MBWMManager *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_manager_register_window_type (void) { static int type_cnt = 0; return ++type_cnt; } static int mb_wm_manager_init_xdpy (MBWMManager * 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_manager_init_cursors (MBWMManager * wm) { XColor col; Pixmap pix = XCreatePixmap (wm->xdpy, wm->root_win->xwindow, 1, 1, 1); memset (&col, 0, sizeof (col)); wm->cursors[MBWMManagerCursorNone] = XCreatePixmapCursor (wm->xdpy, pix, pix, &col, &col, 1, 1); XFreePixmap (wm->xdpy, pix); wm->cursors[MBWMManagerCursorLeftPtr] = XCreateFontCursor(wm->xdpy, XC_left_ptr); MBWM_ASSERT (wm->cursors[_MBWMManagerCursorLast - 1] != 0); mb_wm_manager_set_cursor (wm, MBWMManagerCursorLeftPtr); } #if ENABLE_COMPOSITE static Bool mb_wm_manager_init_comp_extensions (MBWMManager *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 static gboolean check_xpending (MBWMManager *wm) { return XPending (wm->xdpy); } static gboolean events_pending (MBWMManager *wm) { return !g_queue_is_empty (wm->event_queue); } static gboolean mb_wm_manager_event_prepare (GSource *source, int *timeout) { MBWMManagerEventSource *event_source = (MBWMManagerEventSource *)source; MBWMManager *wm = event_source->wm; gboolean retval; /* TODO: mb_wm_threads_enter (); */ *timeout = -1; retval = check_xpending (wm); /* TODO: mb_wm_threads_leave (); */ return retval; } static gboolean mb_wm_manager_event_check (GSource *source) { MBWMManagerEventSource *event_source = (MBWMManagerEventSource *)source; MBWMManager *wm = event_source->wm; gboolean retval; /* TODO: mb_wm_threads_enter (); */ if (event_source->event_poll_fd.revents & G_IO_IN) retval = check_xpending (wm); else retval = FALSE; /* TODO: mb_wm_threads_leave (); */ return retval; } static gboolean event_translate (MBWMManager *wm, XEvent *xevent) { GList *l; for (l = wm->xlib_event_filters; l; l = l->next) { MBWMXlibFilterClosure *closure = l->data; if (closure->filter (xevent, closure->data) == MB_WM_XLIB_FILTER_REMOVE) return FALSE; } return TRUE; } static gboolean queue_event (MBWMManager *wm, XEvent *xevent) { if (event_translate (wm, xevent)) { XEvent *copy = g_slice_copy (sizeof (XEvent), xevent); /* push directly here to avoid copy of queue_put */ g_queue_push_head (wm->event_queue, copy); mb_wm_manager_queue_update (wm); return TRUE; } else return FALSE; } gboolean mb_wm_manager_handle_xlib_event (MBWMManager *wm, XEvent *xev) { return queue_event (wm, xev); } static void queue_events (MBWMManager *wm) { Display *xdisplay = wm->xdpy; while (XPending (xdisplay)) { XEvent xevent; XNextEvent (xdisplay, &xevent); queue_event (wm, &xevent); } } static gboolean mb_wm_manager_event_dispatch (GSource *source, GSourceFunc callback, void *user_data) { MBWMManagerEventSource *event_source = (MBWMManagerEventSource *)source; MBWMManager *wm = event_source->wm; /* TODO: mb_wm_threads_enter (); */ queue_events (wm); /* TODO: mb_wm_threads_leave (); */ return TRUE; } static GSource * mb_wm_manager_event_source_new (MBWMManager *wm) { GSource *source = g_source_new (&event_funcs, sizeof (MBWMManagerEventSource)); MBWMManagerEventSource *event_source = (MBWMManagerEventSource *) source; event_source->wm = wm; #if GLIB_CHECK_VERSION (2, 25, 8) g_source_set_name (source, "Matchbox X11 Event"); #endif event_source->event_poll_fd.fd = ConnectionNumber (wm->xdpy); event_source->event_poll_fd.events = G_IO_IN; g_source_add_poll (source, &event_source->event_poll_fd); g_source_set_can_recurse (source, TRUE); return source; } void mb_wm_manager_disable_x11_event_retrieval (MBWMManager *wm) { wm->event_retrieval_disabled = TRUE; } void mb_wm_manager_xlib_add_filter (MBWMManager *wm, MBWMXlibFilterFunc filter, void *data) { MBWMXlibFilterClosure *closure; closure = g_slice_new (MBWMXlibFilterClosure); closure->filter = filter; closure->data = data; wm->xlib_event_filters = g_list_prepend (wm->xlib_event_filters, closure); } void mv_wm_manager_xlib_remove_filter (MBWMManager *wm, MBWMXlibFilterFunc func, void *data) { GList *l; for (l = wm->xlib_event_filters; l; l = l->next) { MBWMXlibFilterClosure *closure = l->data; if (closure->filter == func && closure->data == data) { g_slice_free (MBWMXlibFilterClosure, closure); wm->xlib_event_filters = g_list_delete_link (wm->xlib_event_filters, l); return; } } } /* * This function must be called before the MBWMManager object can be * used. */ static void _mb_wm_manager_start (MBWMManager * wm, MBWMCompMgr *compositor) { MBWMManagerClass *wm_class; if (!wm->event_retrieval_disabled) { wm->event_source = mb_wm_manager_event_source_new (wm); g_source_attach (wm->event_source, NULL); } wm_class = (MBWMManagerClass *) MB_WM_OBJECT_GET_CLASS (wm); mb_wm_manager_set_theme_from_path (wm, wm->theme_path); MBWM_ASSERT (wm_class->layout_new); mb_wm_manager_set_layout (wm, wm_class->layout_new (wm)); #if ENABLE_COMPOSITE if (compositor) { wm->comp_mgr = mb_wm_object_ref (MB_WM_OBJECT (compositor)); mb_wm_manager_set_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; } void mb_wm_manager_start (MBWMManager *window_manager) { _mb_wm_manager_start (window_manager, NULL); } void mb_wm_manager_start_with_compositor (MBWMManager *window_manager, MBWMCompMgr *compositor) { _mb_wm_manager_start (window_manager, compositor); } static int mb_wm_manager_init (MBWMObject *this, va_list vap) { MBWMManager *wm = MB_WM_MANAGER (this); MBWMManagerClass *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 = (MBWMManagerClass *) 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_manager_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); wm->event_queue = g_queue_new (); #if ENABLE_COMPOSITE if (!mb_wm_manager_init_comp_extensions (wm)) return 0; #endif wm->root_win = mb_wm_root_window_get (wm); mb_wm_update_root_win_rectangles (wm); mb_wm_manager_add_event_handler (wm, None, MapRequest, (MBWMXEventFunc)mb_wm_handle_map_request, wm); #if ENABLE_COMPOSITE mb_wm_manager_add_event_handler (wm, None, MapNotify, (MBWMXEventFunc)mb_wm_handle_map_notify, wm); mb_wm_manager_add_event_handler (wm, None, ConfigureNotify, (MBWMXEventFunc)mb_wm_handle_composite_config_notify, wm); #endif mb_wm_manager_add_event_handler (wm, wm->root_win->xwindow, ConfigureNotify, (MBWMXEventFunc)mb_wm_handle_root_config_notify, wm); mb_wm_manager_add_event_handler (wm, None, ConfigureRequest, (MBWMXEventFunc)mb_wm_handle_config_request, wm); mb_wm_manager_add_event_handler (wm, None, PropertyNotify, (MBWMXEventFunc)mb_wm_handle_property_notify, wm); mb_wm_manager_add_event_handler (wm, None, DestroyNotify, (MBWMXEventFunc)mb_wm_handle_destroy_notify, wm); mb_wm_manager_add_event_handler (wm, None, UnmapNotify, (MBWMXEventFunc)mb_wm_handle_unmap_notify, wm); mb_wm_manager_add_event_handler (wm, None, KeyPress, (MBWMXEventFunc)mb_wm_handle_key_press, wm); mb_wm_manager_add_event_handler (wm, None, ButtonPress, (MBWMXEventFunc)mb_wm_handle_button_press, wm); mb_wm_keys_init(wm); mb_wm_manager_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 (MBWMManager *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 |= MBWMManagerFlagAlwaysReloadTheme; } else if (i < argc - 1) { /* These need to have a value after the name parameter */ if (!strcmp(argv[i], "-display")) { mb_wm_manager_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_manager_init_xdpy (wm, NULL)) return; } void mb_wm_manager_activate_window (MBWMManager * wm, MBWindowManagerClient *c) { MBWMManagerClass *wm_klass; wm_klass = MB_WM_MANAGER_CLASS (MB_WM_OBJECT_GET_CLASS (wm)); MBWM_ASSERT (wm_klass->client_activate); wm_klass->client_activate (wm, c); } static Bool mb_wm_manager_activate_window_real (MBWMManager * 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 & MBWMManagerFlagDesktop); /* * 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 |= MBWMManagerFlagDesktop; else wm->flags &= ~MBWMManagerFlagDesktop; 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_manager_display_sync_queue (wm, MBWMSyncStacking | MBWMSyncVisibility); return True; } MBWindowManagerClient* mb_wm_manager_manager_get_visible_main_window(MBWMManager *wm) { if ((wm->flags & MBWMManagerFlagDesktop) && wm->desktop) return wm->desktop; return mb_wm_stack_get_highest_by_type (wm, MBWMClientTypeApp); } void mb_wm_manager_handle_ping_reply (MBWMManager * wm, MBWindowManagerClient *c) { if (c == NULL) return; if (mb_wm_client_ping_in_progress (c)) { MBWMManagerClass *wm_klass; mb_wm_client_ping_stop (c); wm_klass = MB_WM_MANAGER_CLASS (MB_WM_OBJECT_GET_CLASS (wm)); if (wm_klass->client_responding) wm_klass->client_responding (wm, c); } } void mb_wm_manager_handle_hung_window (MBWMManager * wm, MBWindowManagerClient *c) { MBWMManagerClass *wm_klass; if (c == NULL) return; wm_klass = MB_WM_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_manager_toggle_desktop (MBWMManager * wm) { Bool show = !(wm->flags & MBWMManagerFlagDesktop); mb_wm_manager_handle_show_desktop (wm, show); } void mb_wm_manager_handle_show_desktop (MBWMManager * wm, Bool show) { if (!wm->desktop) return; if (show) mb_wm_manager_activate_window (wm, wm->desktop); else { MBWindowManagerClient * c = mb_wm_stack_get_highest_by_type (wm, MBWMClientTypeApp); if (c) mb_wm_manager_activate_window (wm, c); } } void mb_wm_manager_set_layout (MBWMManager *wm, MBWMLayout *layout) { wm->layout = layout; wm->sync_type |= (MBWMSyncGeometry | MBWMSyncVisibility); } static void mb_wm_focus_client (MBWMManager *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_manager_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_manager_unfocus_window (MBWMManager *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_manager_activate_window (wm, next); } void mb_wm_manager_cycle_apps (MBWMManager *wm, Bool reverse) { MBWindowManagerClient *old_top, *new_top; if (wm->flags & MBWMManagerFlagDesktop) { mb_wm_manager_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_manager_activate_window (wm, new_top); } } void mb_wm_manager_set_theme (MBWMManager *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 MBWMManager 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), MBWMManagerSignalThemeChange); XUngrabServer(wm->xdpy); } void mb_wm_manager_set_theme_from_path (MBWMManager *wm, const char *theme_path) { MBWMTheme *theme; MBWMManagerClass *wm_class; wm_class = MB_WM_MANAGER_CLASS (MB_WM_OBJECT_GET_CLASS (wm)); if (wm->theme) { if (!(wm->flags & MBWMManagerFlagAlwaysReloadTheme) && (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_manager_set_theme (wm, theme); } void mb_wm_manager_set_cursor (MBWMManager * wm, MBWMManagerCursor 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 == MBWMManagerCursorNone) { 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_manager_set_compositing_on (MBWMManager *wm) { #if ENABLE_COMPOSITE if (wm->comp_mgr && !mb_wm_comp_mgr_enabled (wm->comp_mgr)) mb_wm_comp_mgr_turn_on (wm->comp_mgr); #endif } void mb_wm_manager_set_compositing_off (MBWMManager * 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_manager_compositing_enabled (MBWMManager * wm) { #if ENABLE_COMPOSITE return mb_wm_comp_mgr_enabled (wm->comp_mgr); #else return False; #endif } MBWMModality mb_wm_manager_get_modality_type (MBWMManager * wm) { return wm->modality_type; } void mb_wm_manager_set_n_desktops (MBWMManager *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_manager_select_desktop (MBWMManager *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_manager_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_manager_compositing_enabled (wm)) mb_wm_comp_mgr_select_desktop (wm->comp_mgr, desktop, old_desktop); #endif } int mb_wm_manager_util_pixels_to_points (MBWMManager *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; }