X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fscreen.c;h=eb2e487745ff5317d94b309707a6c8727460f435;hb=4d50b21835d7dd00ecc40efd64c5573d7e048500;hp=5e3b36e255bae28af48dc5448ae18133d0dbe938;hpb=32390c6b6dceeb72e4e4ceb0e79ba4d8c8ede9a3;p=chaz%2Fopenbox diff --git a/openbox/screen.c b/openbox/screen.c index 5e3b36e2..eb2e4877 100644 --- a/openbox/screen.c +++ b/openbox/screen.c @@ -1,103 +1,212 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + screen.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003 Ben Jansens + + 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 of the License, 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. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "debug.h" #include "openbox.h" #include "dock.h" +#include "xerror.h" #include "prop.h" -#include "startup.h" -#include "timer.h" +#include "grab.h" +#include "startupnotify.h" +#include "moveresize.h" #include "config.h" #include "screen.h" #include "client.h" #include "frame.h" +#include "event.h" #include "focus.h" -#include "dispatch.h" +#include "popup.h" #include "extensions.h" -#include "../render/render.h" - -#ifdef USE_LIBSN -# define SN_API_NOT_YET_FROZEN -# include -#endif +#include "render/render.h" #include #ifdef HAVE_UNISTD_H # include # include #endif +#include /*! The event mask to grab on the root window */ #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \ - EnterWindowMask | LeaveWindowMask | \ - SubstructureNotifyMask | SubstructureRedirectMask | \ - ButtonPressMask | ButtonReleaseMask | ButtonMotionMask) - -guint screen_num_desktops = 0; -guint screen_desktop = 0; + EnterWindowMask | LeaveWindowMask | \ + SubstructureNotifyMask | SubstructureRedirectMask | \ + ButtonPressMask | ButtonReleaseMask | ButtonMotionMask) + +guint screen_num_desktops; +guint screen_num_monitors; +guint screen_desktop; +guint screen_last_desktop; Size screen_physical_size; gboolean screen_showing_desktop; DesktopLayout screen_desktop_layout; -char **screen_desktop_names = NULL; +gchar **screen_desktop_names; +Window screen_support_win; -static Rect *area = NULL; -static Strut *strut = NULL; -static Window support_window = None; +static Rect **area; /* array of desktop holding array of xinerama areas */ +static Rect *monitor_area; -#ifdef USE_LIBSN -static SnMonitorContext *sn_context; -static int sn_busy_cnt; -static Timer *sn_timer = NULL; +static ObPagerPopup *desktop_cycle_popup; -static void sn_event_func(SnMonitorEvent *event, void *data); -#endif +static gboolean replace_wm() +{ + gchar *wm_sn; + Atom wm_sn_atom; + Window current_wm_sn_owner; + Time timestamp; + + wm_sn = g_strdup_printf("WM_S%d", ob_screen); + wm_sn_atom = XInternAtom(ob_display, wm_sn, FALSE); + g_free(wm_sn); + + current_wm_sn_owner = XGetSelectionOwner(ob_display, wm_sn_atom); + if (current_wm_sn_owner == screen_support_win) + current_wm_sn_owner = None; + if (current_wm_sn_owner) { + if (!ob_replace_wm) { + g_warning("A window manager is already running on screen %d", + ob_screen); + return FALSE; + } + xerror_set_ignore(TRUE); + xerror_occured = FALSE; -static void screen_update_area(); -static void set_root_cursor(); + /* We want to find out when the current selection owner dies */ + XSelectInput(ob_display, current_wm_sn_owner, StructureNotifyMask); + XSync(ob_display, FALSE); -static gboolean running; -static int another_running(Display *d, XErrorEvent *e) -{ - (void)d;(void)e; - g_message("A window manager is already running on screen %d", - ob_screen); - running = TRUE; - return -1; + xerror_set_ignore(FALSE); + if (xerror_occured) + current_wm_sn_owner = None; + } + + { + /* Generate a timestamp */ + XEvent event; + + XSelectInput(ob_display, screen_support_win, PropertyChangeMask); + + XChangeProperty(ob_display, screen_support_win, + prop_atoms.wm_class, prop_atoms.string, + 8, PropModeAppend, NULL, 0); + XWindowEvent(ob_display, screen_support_win, + PropertyChangeMask, &event); + + XSelectInput(ob_display, screen_support_win, NoEventMask); + + timestamp = event.xproperty.time; + } + + XSetSelectionOwner(ob_display, wm_sn_atom, screen_support_win, + timestamp); + + if (XGetSelectionOwner(ob_display, wm_sn_atom) != screen_support_win) { + g_warning("Could not acquire window manager selection on screen %d", + ob_screen); + return FALSE; + } + + /* Wait for old window manager to go away */ + if (current_wm_sn_owner) { + XEvent event; + gulong wait = 0; + const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */ + + while (wait < timeout) { + if (XCheckWindowEvent(ob_display, current_wm_sn_owner, + StructureNotifyMask, &event) && + event.type == DestroyNotify) + break; + g_usleep(G_USEC_PER_SEC / 10); + wait += G_USEC_PER_SEC / 10; + } + + if (wait >= timeout) { + g_warning("Timeout expired while waiting for the current WM to die " + "on screen %d", ob_screen); + return FALSE; + } + } + + /* Send client message indicating that we are now the WM */ + prop_message(RootWindow(ob_display, ob_screen), prop_atoms.manager, + timestamp, wm_sn_atom, 0, 0, SubstructureNotifyMask); + + + return TRUE; } gboolean screen_annex() { - XErrorHandler old; + XSetWindowAttributes attrib; pid_t pid; - int i, num_support; - guint32 *supported; + gint i, num_support; + gulong *supported; - running = FALSE; - old = XSetErrorHandler(another_running); - XSelectInput(ob_display, ob_root, ROOT_EVENTMASK); - XSync(ob_display, FALSE); - XSetErrorHandler(old); - if (running) - return FALSE; + /* create the netwm support window */ + attrib.override_redirect = TRUE; + screen_support_win = XCreateWindow(ob_display, + RootWindow(ob_display, ob_screen), + -100, -100, 1, 1, 0, + CopyFromParent, InputOutput, + CopyFromParent, + CWOverrideRedirect, &attrib); + XMapRaised(ob_display, screen_support_win); + + if (!replace_wm()) { + XDestroyWindow(ob_display, screen_support_win); + return FALSE; + } + + xerror_set_ignore(TRUE); + xerror_occured = FALSE; + XSelectInput(ob_display, RootWindow(ob_display, ob_screen), + ROOT_EVENTMASK); + xerror_set_ignore(FALSE); + if (xerror_occured) { + g_warning("A window manager is already running on screen %d", + ob_screen); + + XDestroyWindow(ob_display, screen_support_win); + return FALSE; + } - g_message("Managing screen %d", ob_screen); - set_root_cursor(); + screen_set_root_cursor(); /* set the OPENBOX_PID hint */ pid = getpid(); - PROP_SET32(ob_root, openbox_pid, cardinal, pid); - - /* create the netwm support window */ - support_window = XCreateSimpleWindow(ob_display, ob_root, 0,0,1,1,0,0,0); + PROP_SET32(RootWindow(ob_display, ob_screen), + openbox_pid, cardinal, pid); /* set supporting window */ - PROP_SET32(ob_root, net_supporting_wm_check, window, support_window); + PROP_SET32(RootWindow(ob_display, ob_screen), + net_supporting_wm_check, window, screen_support_win); /* set properties on the supporting window */ - PROP_SETS(support_window, net_wm_name, "Openbox"); - PROP_SET32(support_window, net_supporting_wm_check, window,support_window); + PROP_SETS(screen_support_win, net_wm_name, "Openbox"); + PROP_SET32(screen_support_win, net_supporting_wm_check, + window, screen_support_win); /* set the _NET_SUPPORTED_ATOMS hint */ - num_support = 61; + num_support = 51; i = 0; - supported = g_new(guint32, num_support); + supported = g_new(gulong, num_support); supported[i++] = prop_atoms.net_current_desktop; supported[i++] = prop_atoms.net_number_of_desktops; supported[i++] = prop_atoms.net_desktop_geometry; @@ -148,119 +257,133 @@ gboolean screen_annex() supported[i++] = prop_atoms.net_wm_state_below; supported[i++] = prop_atoms.net_moveresize_window; supported[i++] = prop_atoms.net_wm_moveresize; - supported[i++] = prop_atoms.net_wm_moveresize_size_topleft; - supported[i++] = prop_atoms.net_wm_moveresize_size_top; - supported[i++] = prop_atoms.net_wm_moveresize_size_topright; - supported[i++] = prop_atoms.net_wm_moveresize_size_right; - supported[i++] = prop_atoms.net_wm_moveresize_size_bottomright; - supported[i++] = prop_atoms.net_wm_moveresize_size_bottom; - supported[i++] = prop_atoms.net_wm_moveresize_size_bottomleft; - supported[i++] = prop_atoms.net_wm_moveresize_size_left; - supported[i++] = prop_atoms.net_wm_moveresize_move; - supported[i++] = prop_atoms.net_wm_moveresize_size_keyboard; - supported[i++] = prop_atoms.net_wm_moveresize_move_keyboard; + supported[i++] = prop_atoms.ob_wm_state_undecorated; g_assert(i == num_support); /* supported[] = prop_atoms.net_wm_action_stick; */ - PROP_SETA32(ob_root, net_supported, atom, supported, num_support); + PROP_SETA32(RootWindow(ob_display, ob_screen), + net_supported, atom, supported, num_support); g_free(supported); return TRUE; } -void screen_startup() +void screen_startup(gboolean reconfig) { GSList *it; guint i; - /* get the initial size */ - screen_resize(WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen)), - HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen))); + desktop_cycle_popup = pager_popup_new(FALSE); + + if (!reconfig) + /* get the initial size */ + screen_resize(); /* set the names */ - screen_desktop_names = g_new(char*, + screen_desktop_names = g_new(gchar*, g_slist_length(config_desktops_names) + 1); - for (i = 0, it = config_desktops_names; it; ++i, it = it->next) + for (i = 0, it = config_desktops_names; it; ++i, it = g_slist_next(it)) screen_desktop_names[i] = it->data; /* dont strdup */ screen_desktop_names[i] = NULL; - PROP_SETSS(ob_root, net_desktop_names, screen_desktop_names); + PROP_SETSS(RootWindow(ob_display, ob_screen), + net_desktop_names, screen_desktop_names); g_free(screen_desktop_names); /* dont free the individual strings */ screen_desktop_names = NULL; - screen_num_desktops = 0; + if (!reconfig) + screen_num_desktops = 0; screen_set_num_desktops(config_desktops_num); - if (startup_desktop >= screen_num_desktops) - startup_desktop = 0; - screen_desktop = startup_desktop; - screen_set_desktop(startup_desktop); + if (!reconfig) { + screen_set_desktop(MIN(config_screen_firstdesk, screen_num_desktops) - 1); - /* don't start in showing-desktop mode */ - screen_showing_desktop = FALSE; - PROP_SET32(ob_root, net_showing_desktop, cardinal, screen_showing_desktop); + /* don't start in showing-desktop mode */ + screen_showing_desktop = FALSE; + PROP_SET32(RootWindow(ob_display, ob_screen), + net_showing_desktop, cardinal, screen_showing_desktop); - screen_update_layout(); - -#ifdef USE_LIBSN - sn_context = sn_monitor_context_new(ob_sn_display, ob_screen, - sn_event_func, NULL, NULL); - sn_busy_cnt = 0; -#endif + screen_update_layout(); + } } -void screen_shutdown() +void screen_shutdown(gboolean reconfig) { - XSelectInput(ob_display, ob_root, NoEventMask); + Rect **r; + + pager_popup_free(desktop_cycle_popup); + + if (!reconfig) { + XSelectInput(ob_display, RootWindow(ob_display, ob_screen), + NoEventMask); - PROP_ERASE(ob_root, openbox_pid); /* we're not running here no more! */ - PROP_ERASE(ob_root, net_supported); /* not without us */ - PROP_ERASE(ob_root, net_showing_desktop); /* don't keep this mode */ + /* we're not running here no more! */ + PROP_ERASE(RootWindow(ob_display, ob_screen), openbox_pid); + /* not without us */ + PROP_ERASE(RootWindow(ob_display, ob_screen), net_supported); + /* don't keep this mode */ + PROP_ERASE(RootWindow(ob_display, ob_screen), net_showing_desktop); - XDestroyWindow(ob_display, support_window); + XDestroyWindow(ob_display, screen_support_win); + } g_strfreev(screen_desktop_names); - g_free(strut); + screen_desktop_names = NULL; + for (r = area; *r; ++r) + g_free(*r); g_free(area); + area = NULL; } -void screen_resize(int w, int h) +void screen_resize() { + static gint oldw = 0, oldh = 0; + gint w, h; GList *it; - guint32 geometry[2]; + gulong geometry[2]; + + w = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen)); + h = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen)); + + if (w == oldw && h == oldh) return; + + oldw = w; oldh = h; /* Set the _NET_DESKTOP_GEOMETRY hint */ - geometry[0] = w; - geometry[1] = h; - PROP_SETA32(ob_root, net_desktop_geometry, cardinal, geometry, 2); - screen_physical_size.width = geometry[0]; - screen_physical_size.height = geometry[1]; + screen_physical_size.width = geometry[0] = w; + screen_physical_size.height = geometry[1] = h; + PROP_SETA32(RootWindow(ob_display, ob_screen), + net_desktop_geometry, cardinal, geometry, 2); - if (ob_state == State_Starting) - return; + if (ob_state() == OB_STATE_STARTING) + return; + screen_update_areas(); dock_configure(); - screen_update_struts(); - for (it = client_list; it; it = it->next) - client_move_onscreen(it->data); + for (it = client_list; it; it = g_list_next(it)) + client_move_onscreen(it->data, FALSE); } void screen_set_num_desktops(guint num) { guint i, old; - guint32 *viewport; + gulong *viewport; GList *it; g_assert(num > 0); + if (screen_num_desktops == num) return; + old = screen_num_desktops; screen_num_desktops = num; - PROP_SET32(ob_root, net_number_of_desktops, cardinal, num); + PROP_SET32(RootWindow(ob_display, ob_screen), + net_number_of_desktops, cardinal, num); /* set the viewport hint */ - viewport = g_new0(guint32, num * 2); - PROP_SETA32(ob_root, net_desktop_viewport, cardinal, viewport, num * 2); + viewport = g_new0(gulong, num * 2); + PROP_SETA32(RootWindow(ob_display, ob_screen), + net_desktop_viewport, cardinal, viewport, num * 2); g_free(viewport); /* the number of rows/columns will differ */ @@ -269,145 +392,447 @@ void screen_set_num_desktops(guint num) /* may be some unnamed desktops that we need to fill in with names */ screen_update_desktop_names(); - /* update the focus lists */ - /* free our lists for the desktops which have disappeared */ - for (i = num; i < old; ++i) - g_list_free(focus_order[i]); - /* realloc the array */ - focus_order = g_renew(GList*, focus_order, num); - /* set the new lists to be empty */ - for (i = old; i < num; ++i) - focus_order[i] = NULL; - /* move windows on desktops that will no longer exist! */ - for (it = client_list; it != NULL; it = it->next) { - Client *c = it->data; + for (it = client_list; it; it = g_list_next(it)) { + ObClient *c = it->data; if (c->desktop >= num && c->desktop != DESKTOP_ALL) client_set_desktop(c, num - 1, FALSE); } - + /* change our struts/area to match (after moving windows) */ - screen_update_struts(); - - dispatch_ob(Event_Ob_NumDesktops, num, old); + screen_update_areas(); /* change our desktop if we're on one that no longer exists! */ if (screen_desktop >= screen_num_desktops) - screen_set_desktop(num - 1); + screen_set_desktop(num - 1); + + /* update the focus lists */ + /* free our lists for the desktops which have disappeared */ + for (i = num; i < old; ++i) + g_list_free(focus_order[i]); + /* realloc the array */ + focus_order = g_renew(GList*, focus_order, num); + /* set the new lists to be empty */ + for (i = old; i < num; ++i) + focus_order[i] = NULL; } void screen_set_desktop(guint num) { GList *it; guint old; - XEvent e; g_assert(num < screen_num_desktops); old = screen_desktop; screen_desktop = num; - PROP_SET32(ob_root, net_current_desktop, cardinal, num); + PROP_SET32(RootWindow(ob_display, ob_screen), + net_current_desktop, cardinal, num); if (old == num) return; - g_message("Moving to desktop %d", num+1); + screen_last_desktop = old; + + ob_debug("Moving to desktop %d\n", num+1); + + if (moveresize_client) + client_set_desktop(moveresize_client, num, TRUE); /* show windows before hiding the rest to lessen the enter/leave events */ /* show windows from top to bottom */ - for (it = stacking_list; it != NULL; it = it->next) { + for (it = stacking_list; it; it = g_list_next(it)) { if (WINDOW_IS_CLIENT(it->data)) { - Client *c = it->data; - if (!c->frame->visible && client_should_show(c)) + ObClient *c = it->data; + if (client_should_show(c)) frame_show(c->frame); } } /* hide windows from bottom to top */ - for (it = g_list_last(stacking_list); it != NULL; it = it->prev) { + for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) { if (WINDOW_IS_CLIENT(it->data)) { - Client *c = it->data; + ObClient *c = it->data; if (c->frame->visible && !client_should_show(c)) frame_hide(c->frame); } } - /* focus the last focused window on the desktop, and ignore enter events - from the switch so it doesnt mess with the focus */ - while (XCheckTypedEvent(ob_display, EnterNotify, &e)); - focus_fallback(Fallback_Desktop); + event_ignore_queued_enters(); + + focus_hilite = focus_fallback_target(OB_FOCUS_FALLBACK_NOFOCUS); + if (focus_hilite) { + frame_adjust_focus(focus_hilite->frame, TRUE); + + /*! + When this focus_client check is not used, you can end up with races, + as demonstrated with gnome-panel, sometmies the window you click on + another desktop ends up losing focus cuz of the focus change here. + */ + /*if (!focus_client)*/ + client_focus(focus_hilite); + } +} + +static void get_row_col(guint d, guint *r, guint *c) +{ + switch (screen_desktop_layout.orientation) { + case OB_ORIENTATION_HORZ: + switch (screen_desktop_layout.start_corner) { + case OB_CORNER_TOPLEFT: + *r = d / screen_desktop_layout.columns; + *c = d % screen_desktop_layout.columns; + break; + case OB_CORNER_BOTTOMLEFT: + *r = screen_desktop_layout.rows - 1 - + d / screen_desktop_layout.columns; + *c = d % screen_desktop_layout.columns; + break; + case OB_CORNER_TOPRIGHT: + *r = d / screen_desktop_layout.columns; + *c = screen_desktop_layout.columns - 1 - + d % screen_desktop_layout.columns; + break; + case OB_CORNER_BOTTOMRIGHT: + *r = screen_desktop_layout.rows - 1 - + d / screen_desktop_layout.columns; + *c = screen_desktop_layout.columns - 1 - + d % screen_desktop_layout.columns; + break; + } + break; + case OB_ORIENTATION_VERT: + switch (screen_desktop_layout.start_corner) { + case OB_CORNER_TOPLEFT: + *r = d % screen_desktop_layout.rows; + *c = d / screen_desktop_layout.rows; + break; + case OB_CORNER_BOTTOMLEFT: + *r = screen_desktop_layout.rows - 1 - + d % screen_desktop_layout.rows; + *c = d / screen_desktop_layout.rows; + break; + case OB_CORNER_TOPRIGHT: + *r = d % screen_desktop_layout.rows; + *c = screen_desktop_layout.columns - 1 - + d / screen_desktop_layout.rows; + break; + case OB_CORNER_BOTTOMRIGHT: + *r = screen_desktop_layout.rows - 1 - + d % screen_desktop_layout.rows; + *c = screen_desktop_layout.columns - 1 - + d / screen_desktop_layout.rows; + break; + } + break; + } +} + +static guint translate_row_col(guint r, guint c) +{ + switch (screen_desktop_layout.orientation) { + case OB_ORIENTATION_HORZ: + switch (screen_desktop_layout.start_corner) { + case OB_CORNER_TOPLEFT: + return r % screen_desktop_layout.rows * + screen_desktop_layout.columns + + c % screen_desktop_layout.columns; + case OB_CORNER_BOTTOMLEFT: + return (screen_desktop_layout.rows - 1 - + r % screen_desktop_layout.rows) * + screen_desktop_layout.columns + + c % screen_desktop_layout.columns; + case OB_CORNER_TOPRIGHT: + return r % screen_desktop_layout.rows * + screen_desktop_layout.columns + + (screen_desktop_layout.columns - 1 - + c % screen_desktop_layout.columns); + case OB_CORNER_BOTTOMRIGHT: + return (screen_desktop_layout.rows - 1 - + r % screen_desktop_layout.rows) * + screen_desktop_layout.columns + + (screen_desktop_layout.columns - 1 - + c % screen_desktop_layout.columns); + } + case OB_ORIENTATION_VERT: + switch (screen_desktop_layout.start_corner) { + case OB_CORNER_TOPLEFT: + return c % screen_desktop_layout.columns * + screen_desktop_layout.rows + + r % screen_desktop_layout.rows; + case OB_CORNER_BOTTOMLEFT: + return c % screen_desktop_layout.columns * + screen_desktop_layout.rows + + (screen_desktop_layout.rows - 1 - + r % screen_desktop_layout.rows); + case OB_CORNER_TOPRIGHT: + return (screen_desktop_layout.columns - 1 - + c % screen_desktop_layout.columns) * + screen_desktop_layout.rows + + r % screen_desktop_layout.rows; + case OB_CORNER_BOTTOMRIGHT: + return (screen_desktop_layout.columns - 1 - + c % screen_desktop_layout.columns) * + screen_desktop_layout.rows + + (screen_desktop_layout.rows - 1 - + r % screen_desktop_layout.rows); + } + } + g_assert_not_reached(); + return 0; +} + +void screen_desktop_popup(guint d, gboolean show) +{ + Rect *a; + + if (!show) { + pager_popup_hide(desktop_cycle_popup); + } else { + a = screen_physical_area_monitor(0); + pager_popup_position(desktop_cycle_popup, CenterGravity, + a->x + a->width / 2, a->y + a->height / 2); + /* XXX the size and the font extents need to be related on some level + */ + pager_popup_size(desktop_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT); + + pager_popup_set_text_align(desktop_cycle_popup, RR_JUSTIFY_CENTER); + + pager_popup_show(desktop_cycle_popup, screen_desktop_names[d], d); + } +} + +guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear, + gboolean dialog, gboolean done, gboolean cancel) +{ + static gboolean first = TRUE; + static guint origd, d; + guint r, c; + + if (cancel) { + d = origd; + goto done_cycle; + } else if (done && dialog) { + goto done_cycle; + } + if (first) { + first = FALSE; + d = origd = screen_desktop; + } + + get_row_col(d, &r, &c); + + if (linear) { + switch (dir) { + case OB_DIRECTION_EAST: + if (d < screen_num_desktops - 1) + ++d; + else if (wrap) + d = 0; + break; + case OB_DIRECTION_WEST: + if (d > 0) + --d; + else if (wrap) + d = screen_num_desktops - 1; + break; + default: + assert(0); + return screen_desktop; + } + } else { + switch (dir) { + case OB_DIRECTION_EAST: + ++c; + if (c >= screen_desktop_layout.columns) { + if (wrap) { + c = 0; + } else { + d = screen_desktop; + goto show_cycle_dialog; + } + } + d = translate_row_col(r, c); + if (d >= screen_num_desktops) { + if (wrap) { + ++c; + } else { + d = screen_desktop; + goto show_cycle_dialog; + } + } + break; + case OB_DIRECTION_WEST: + --c; + if (c >= screen_desktop_layout.columns) { + if (wrap) { + c = screen_desktop_layout.columns - 1; + } else { + d = screen_desktop; + goto show_cycle_dialog; + } + } + d = translate_row_col(r, c); + if (d >= screen_num_desktops) { + if (wrap) { + --c; + } else { + d = screen_desktop; + goto show_cycle_dialog; + } + } + break; + case OB_DIRECTION_SOUTH: + ++r; + if (r >= screen_desktop_layout.rows) { + if (wrap) { + r = 0; + } else { + d = screen_desktop; + goto show_cycle_dialog; + } + } + d = translate_row_col(r, c); + if (d >= screen_num_desktops) { + if (wrap) { + ++r; + } else { + d = screen_desktop; + goto show_cycle_dialog; + } + } + break; + case OB_DIRECTION_NORTH: + --r; + if (r >= screen_desktop_layout.rows) { + if (wrap) { + r = screen_desktop_layout.rows - 1; + } else { + d = screen_desktop; + goto show_cycle_dialog; + } + } + d = translate_row_col(r, c); + if (d >= screen_num_desktops) { + if (wrap) { + --r; + } else { + d = screen_desktop; + goto show_cycle_dialog; + } + } + break; + default: + assert(0); + return d = screen_desktop; + } + + d = translate_row_col(r, c); + } + +show_cycle_dialog: + if (dialog) { + screen_desktop_popup(d, TRUE); + return d; + } + +done_cycle: + first = TRUE; + + screen_desktop_popup(0, FALSE); - dispatch_ob(Event_Ob_Desktop, num, old); + return d; } void screen_update_layout() { - guint32 *data = NULL; + ObOrientation orient; + ObCorner corner; + guint rows; + guint cols; + guint32 *data; guint num; + gboolean valid = FALSE; - /* defaults */ - screen_desktop_layout.orientation = prop_atoms.net_wm_orientation_horz; - screen_desktop_layout.start_corner = prop_atoms.net_wm_topleft; - screen_desktop_layout.rows = 1; - screen_desktop_layout.columns = screen_num_desktops; - - if (PROP_GETA32(ob_root, net_desktop_layout, cardinal, &data, &num)) { + if (PROP_GETA32(RootWindow(ob_display, ob_screen), + net_desktop_layout, cardinal, &data, &num)) { if (num == 3 || num == 4) { + if (data[0] == prop_atoms.net_wm_orientation_vert) - screen_desktop_layout.orientation = data[0]; - if (num == 3) - screen_desktop_layout.start_corner = - prop_atoms.net_wm_topright; + orient = OB_ORIENTATION_VERT; + else if (data[0] == prop_atoms.net_wm_orientation_horz) + orient = OB_ORIENTATION_HORZ; + else + goto screen_update_layout_bail; + + if (num < 4) + corner = OB_CORNER_TOPLEFT; else { - if (data[3] == prop_atoms.net_wm_topright) - screen_desktop_layout.start_corner = data[3]; + if (data[3] == prop_atoms.net_wm_topleft) + corner = OB_CORNER_TOPLEFT; + else if (data[3] == prop_atoms.net_wm_topright) + corner = OB_CORNER_TOPRIGHT; else if (data[3] == prop_atoms.net_wm_bottomright) - screen_desktop_layout.start_corner = data[3]; + corner = OB_CORNER_BOTTOMRIGHT; else if (data[3] == prop_atoms.net_wm_bottomleft) - screen_desktop_layout.start_corner = data[3]; + corner = OB_CORNER_BOTTOMLEFT; + else + goto screen_update_layout_bail; } + cols = data[1]; + rows = data[2]; + /* fill in a zero rows/columns */ - if (!(data[1] == 0 && data[2] == 0)) { /* both 0's is bad data.. */ - if (data[1] == 0) { - data[1] = (screen_num_desktops + - screen_num_desktops % data[2]) / data[2]; - } else if (data[2] == 0) { - data[2] = (screen_num_desktops + - screen_num_desktops % data[1]) / data[1]; + if ((cols == 0 && rows == 0)) { /* both 0's is bad data.. */ + goto screen_update_layout_bail; + } else { + if (cols == 0) { + cols = screen_num_desktops / rows; + if (rows * cols < screen_num_desktops) + cols++; + if (rows * cols >= screen_num_desktops + cols) + rows--; + } else if (rows == 0) { + rows = screen_num_desktops / cols; + if (cols * rows < screen_num_desktops) + rows++; + if (cols * rows >= screen_num_desktops + rows) + cols--; } - screen_desktop_layout.columns = data[1]; - screen_desktop_layout.rows = data[2]; } /* bounds checking */ - if (screen_desktop_layout.orientation == - prop_atoms.net_wm_orientation_horz) { - if (screen_desktop_layout.rows > screen_num_desktops) - screen_desktop_layout.rows = screen_num_desktops; - if (screen_desktop_layout.columns > - ((screen_num_desktops + screen_num_desktops % - screen_desktop_layout.rows) / - screen_desktop_layout.rows)) - screen_desktop_layout.columns = - (screen_num_desktops + screen_num_desktops % - screen_desktop_layout.rows) / - screen_desktop_layout.rows; + if (orient == OB_ORIENTATION_HORZ) { + cols = MIN(screen_num_desktops, cols); + rows = MIN(rows, (screen_num_desktops + cols - 1) / cols); + cols = screen_num_desktops / rows + + !!(screen_num_desktops % rows); } else { - if (screen_desktop_layout.columns > screen_num_desktops) - screen_desktop_layout.columns = screen_num_desktops; - if (screen_desktop_layout.rows > - ((screen_num_desktops + screen_num_desktops % - screen_desktop_layout.columns) / - screen_desktop_layout.columns)) - screen_desktop_layout.rows = - (screen_num_desktops + screen_num_desktops % - screen_desktop_layout.columns) / - screen_desktop_layout.columns; + rows = MIN(screen_num_desktops, rows); + cols = MIN(cols, (screen_num_desktops + rows - 1) / rows); + rows = screen_num_desktops / cols + + !!(screen_num_desktops % cols); } + + valid = TRUE; } - g_free(data); + screen_update_layout_bail: + g_free(data); + } + + if (!valid) { + /* defaults */ + orient = OB_ORIENTATION_HORZ; + corner = OB_CORNER_TOPLEFT; + rows = 1; + cols = screen_num_desktops; } + + screen_desktop_layout.orientation = orient; + screen_desktop_layout.start_corner = corner; + screen_desktop_layout.rows = rows; + screen_desktop_layout.columns = cols; } void screen_update_desktop_names() @@ -418,16 +843,17 @@ void screen_update_desktop_names() g_strfreev(screen_desktop_names); screen_desktop_names = NULL; - if (PROP_GETSS(ob_root, net_desktop_names, utf8, &screen_desktop_names)) + if (PROP_GETSS(RootWindow(ob_display, ob_screen), + net_desktop_names, utf8, &screen_desktop_names)) for (i = 0; screen_desktop_names[i] && i <= screen_num_desktops; ++i); else i = 0; if (i <= screen_num_desktops) { - screen_desktop_names = g_renew(char*, screen_desktop_names, + screen_desktop_names = g_renew(gchar*, screen_desktop_names, screen_num_desktops + 1); screen_desktop_names[screen_num_desktops] = NULL; for (; i < screen_num_desktops; ++i) - screen_desktop_names[i] = g_strdup("Unnamed Desktop"); + screen_desktop_names[i] = g_strdup_printf("Desktop %i", i + 1); } } @@ -440,249 +866,335 @@ void screen_show_desktop(gboolean show) screen_showing_desktop = show; if (show) { - /* bottom to top */ - for (it = g_list_last(stacking_list); it != NULL; it = it->prev) { + /* bottom to top */ + for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) { if (WINDOW_IS_CLIENT(it->data)) { - Client *client = it->data; + ObClient *client = it->data; if (client->frame->visible && !client_should_show(client)) frame_hide(client->frame); } - } + } } else { /* top to bottom */ - for (it = stacking_list; it != NULL; it = it->next) { + for (it = stacking_list; it; it = g_list_next(it)) { if (WINDOW_IS_CLIENT(it->data)) { - Client *client = it->data; + ObClient *client = it->data; if (!client->frame->visible && client_should_show(client)) frame_show(client->frame); } - } + } } if (show) { /* focus desktop */ - for (it = focus_order[screen_desktop]; it; it = it->next) - if (((Client*)it->data)->type == Type_Desktop && + for (it = focus_order[screen_desktop]; it; it = g_list_next(it)) + if (((ObClient*)it->data)->type == OB_CLIENT_TYPE_DESKTOP && client_focus(it->data)) break; } else { - focus_fallback(Fallback_NoFocus); + focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS); } show = !!show; /* make it boolean */ - PROP_SET32(ob_root, net_showing_desktop, cardinal, show); - - dispatch_ob(Event_Ob_ShowDesktop, show, 0); + PROP_SET32(RootWindow(ob_display, ob_screen), + net_showing_desktop, cardinal, show); } -void screen_install_colormap(Client *client, gboolean install) +void screen_install_colormap(ObClient *client, gboolean install) { XWindowAttributes wa; if (client == NULL) { - if (install) - XInstallColormap(ob_display, render_colormap); - else - XUninstallColormap(ob_display, render_colormap); + if (install) + XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst)); + else + XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst)); } else { - if (XGetWindowAttributes(ob_display, client->window, &wa) && + if (XGetWindowAttributes(ob_display, client->window, &wa) && wa.colormap != None) { xerror_set_ignore(TRUE); - if (install) - XInstallColormap(ob_display, wa.colormap); - else - XUninstallColormap(ob_display, wa.colormap); + if (install) + XInstallColormap(RrDisplay(ob_rr_inst), wa.colormap); + else + XUninstallColormap(RrDisplay(ob_rr_inst), wa.colormap); xerror_set_ignore(FALSE); - } + } } } -void screen_update_struts() +static inline void +screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area, + gint edge, Strut *ret) { - GList *it; - guint i; - - g_free(strut); - strut = g_new0(Strut, screen_num_desktops + 1); - - for (it = client_list; it != NULL; it = it->next) { - Client *c = it->data; - if (c->iconic) continue; /* these dont count in the strut */ - - if (c->desktop == 0xffffffff) { - for (i = 0; i < screen_num_desktops; ++i) - STRUT_ADD(strut[i], c->strut); - } else { - g_assert(c->desktop < screen_num_desktops); - STRUT_ADD(strut[c->desktop], c->strut); - } - /* apply to the 'all desktops' strut */ - STRUT_ADD(strut[screen_num_desktops], c->strut); - } + if (s->left && + ((s->left_end <= s->left_start) || + (RECT_TOP(*monitor_area) < s->left_end && + RECT_BOTTOM(*monitor_area) > s->left_start))) + ret->left = MAX(ret->left, edge); +} - for (i = 0; i < screen_num_desktops; ++i) - STRUT_ADD(strut[i], dock_strut); +static inline void +screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area, + gint edge, Strut *ret) +{ + if (s->top && + ((s->top_end <= s->top_start) || + (RECT_LEFT(*monitor_area) < s->top_end && + RECT_RIGHT(*monitor_area) > s->top_start))) + ret->top = MAX(ret->top, edge); +} - screen_update_area(); +static inline void +screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area, + gint edge, Strut *ret) +{ + if (s->right && + ((s->right_end <= s->right_start) || + (RECT_TOP(*monitor_area) < s->right_end && + RECT_BOTTOM(*monitor_area) > s->right_start))) + ret->right = MAX(ret->right, edge); } -static void screen_update_area() +static inline void +screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area, + gint edge, Strut *ret) { - guint i; - guint32 *dims; + if (s->bottom && + ((s->bottom_end <= s->bottom_start) || + (RECT_LEFT(*monitor_area) < s->bottom_end && + RECT_RIGHT(*monitor_area) > s->bottom_start))) + ret->bottom = MAX(ret->bottom, edge); +} - g_free(area); - area = g_new0(Rect, screen_num_desktops + 1); +void screen_update_areas() +{ + guint i, x; + gulong *dims; + GList *it; + gint o; + + g_free(monitor_area); + extensions_xinerama_screens(&monitor_area, &screen_num_monitors); + + if (area) { + for (i = 0; area[i]; ++i) + g_free(area[i]); + g_free(area); + } + + area = g_new(Rect*, screen_num_desktops + 2); + for (i = 0; i < screen_num_desktops + 1; ++i) + area[i] = g_new0(Rect, screen_num_monitors + 1); + area[i] = NULL; - dims = g_new(guint32, 4 * screen_num_desktops); + dims = g_new(gulong, 4 * screen_num_desktops); + for (i = 0; i < screen_num_desktops + 1; ++i) { - Rect old_area = area[i]; -/* - #ifdef XINERAMA - // reset to the full areas - if (isXineramaActive()) - xineramaUsableArea = getXineramaAreas(); - #endif // XINERAMA -*/ + Strut *struts; + gint l, r, t, b; + + struts = g_new0(Strut, screen_num_monitors); + + /* calc the xinerama areas */ + for (x = 0; x < screen_num_monitors; ++x) { + area[i][x] = monitor_area[x]; + if (x == 0) { + l = monitor_area[x].x; + t = monitor_area[x].y; + r = monitor_area[x].x + monitor_area[x].width - 1; + b = monitor_area[x].y + monitor_area[x].height - 1; + } else { + l = MIN(l, monitor_area[x].x); + t = MIN(t, monitor_area[x].y); + r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1); + b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1); + } + } + RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1); + + /* apply the struts */ + + /* find the left-most xin heads, i do this in 2 loops :| */ + o = area[i][0].x; + for (x = 1; x < screen_num_monitors; ++x) + o = MIN(o, area[i][x].x); + + for (x = 0; x < screen_num_monitors; ++x) { + for (it = client_list; it; it = g_list_next(it)) { + ObClient *c = it->data; + screen_area_add_strut_left(&c->strut, + &monitor_area[x], + o + c->strut.left - area[i][x].x, + &struts[x]); + } + screen_area_add_strut_left(&dock_strut, + &monitor_area[x], + o + dock_strut.left - area[i][x].x, + &struts[x]); - RECT_SET(area[i], strut[i].left, strut[i].top, - screen_physical_size.width - (strut[i].left + - strut[i].right), - screen_physical_size.height - (strut[i].top + - strut[i].bottom)); - -/* - #ifdef XINERAMA - if (isXineramaActive()) { - // keep each of the ximerama-defined areas inside the strut - RectList::iterator xit, xend = xineramaUsableArea.end(); - for (xit = xineramaUsableArea.begin(); xit != xend; ++xit) { - if (xit->x() < usableArea.x()) { - xit->setX(usableArea.x()); - xit->setWidth(xit->width() - usableArea.x()); - } - if (xit->y() < usableArea.y()) { - xit->setY(usableArea.y()); - xit->setHeight(xit->height() - usableArea.y()); - } - if (xit->x() + xit->width() > usableArea.width()) - xit->setWidth(usableArea.width() - xit->x()); - if (xit->y() + xit->height() > usableArea.height()) - xit->setHeight(usableArea.height() - xit->y()); - } - } - #endif // XINERAMA -*/ - if (!RECT_EQUAL(old_area, area[i])) { - /* the area has changed, adjust all the maximized windows */ - GList *it; - for (it = client_list; it; it = it->next) { - Client *c = it->data; - if (i < screen_num_desktops) { - if (c->desktop == i) - client_remaximize(c); - } else { - /* the 'all desktops' size */ - if (c->desktop == DESKTOP_ALL) - client_remaximize(c); - } - } - } - - /* don't set these for the 'all desktops' area */ - if (i < screen_num_desktops) { - dims[(i * 4) + 0] = area[i].x; - dims[(i * 4) + 1] = area[i].y; - dims[(i * 4) + 2] = area[i].width; - dims[(i * 4) + 3] = area[i].height; - } + area[i][x].x += struts[x].left; + area[i][x].width -= struts[x].left; + } + + /* find the top-most xin heads, i do this in 2 loops :| */ + o = area[i][0].y; + for (x = 1; x < screen_num_monitors; ++x) + o = MIN(o, area[i][x].y); + + for (x = 0; x < screen_num_monitors; ++x) { + for (it = client_list; it; it = g_list_next(it)) { + ObClient *c = it->data; + screen_area_add_strut_top(&c->strut, + &monitor_area[x], + o + c->strut.top - area[i][x].y, + &struts[x]); + } + screen_area_add_strut_top(&dock_strut, + &monitor_area[x], + o + dock_strut.top - area[i][x].y, + &struts[x]); + + area[i][x].y += struts[x].top; + area[i][x].height -= struts[x].top; + } + + /* find the right-most xin heads, i do this in 2 loops :| */ + o = area[i][0].x + area[i][0].width - 1; + for (x = 1; x < screen_num_monitors; ++x) + o = MAX(o, area[i][x].x + area[i][x].width - 1); + + for (x = 0; x < screen_num_monitors; ++x) { + for (it = client_list; it; it = g_list_next(it)) { + ObClient *c = it->data; + screen_area_add_strut_right(&c->strut, + &monitor_area[x], + (area[i][x].x + + area[i][x].width - 1) - + (o - c->strut.right), + &struts[x]); + } + screen_area_add_strut_right(&dock_strut, + &monitor_area[x], + (area[i][x].x + + area[i][x].width - 1) - + (o - dock_strut.right), + &struts[x]); + + area[i][x].width -= struts[x].right; + } + + /* find the bottom-most xin heads, i do this in 2 loops :| */ + o = area[i][0].y + area[i][0].height - 1; + for (x = 1; x < screen_num_monitors; ++x) + o = MAX(o, area[i][x].y + area[i][x].height - 1); + + for (x = 0; x < screen_num_monitors; ++x) { + for (it = client_list; it; it = g_list_next(it)) { + ObClient *c = it->data; + screen_area_add_strut_bottom(&c->strut, + &monitor_area[x], + (area[i][x].y + + area[i][x].height - 1) - \ + (o - c->strut.bottom), + &struts[x]); + } + screen_area_add_strut_bottom(&dock_strut, + &monitor_area[x], + (area[i][x].y + + area[i][x].height - 1) - \ + (o - dock_strut.bottom), + &struts[x]); + + area[i][x].height -= struts[x].bottom; + } + + l = RECT_LEFT(area[i][0]); + t = RECT_TOP(area[i][0]); + r = RECT_RIGHT(area[i][0]); + b = RECT_BOTTOM(area[i][0]); + for (x = 1; x < screen_num_monitors; ++x) { + l = MIN(l, RECT_LEFT(area[i][x])); + t = MIN(l, RECT_TOP(area[i][x])); + r = MAX(r, RECT_RIGHT(area[i][x])); + b = MAX(b, RECT_BOTTOM(area[i][x])); + } + RECT_SET(area[i][screen_num_monitors], l, t, + r - l + 1, b - t + 1); + + /* XXX optimize when this is run? */ + + /* the area has changed, adjust all the maximized + windows */ + for (it = client_list; it; it = g_list_next(it)) { + ObClient *c = it->data; + if (i < screen_num_desktops) { + if (c->desktop == i) + client_reconfigure(c); + } else if (c->desktop == DESKTOP_ALL) + client_reconfigure(c); + } + if (i < screen_num_desktops) { + /* don't set these for the 'all desktops' area */ + dims[(i * 4) + 0] = area[i][screen_num_monitors].x; + dims[(i * 4) + 1] = area[i][screen_num_monitors].y; + dims[(i * 4) + 2] = area[i][screen_num_monitors].width; + dims[(i * 4) + 3] = area[i][screen_num_monitors].height; + } + + g_free(struts); } - PROP_SETA32(ob_root, net_workarea, cardinal, - dims, 4 * screen_num_desktops); + + PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal, + dims, 4 * screen_num_desktops); + g_free(dims); } Rect *screen_area(guint desktop) { - if (desktop >= screen_num_desktops) { - if (desktop == DESKTOP_ALL) - return &area[screen_num_desktops]; - return NULL; - } - return &area[desktop]; + return screen_area_monitor(desktop, screen_num_monitors); } -Strut *screen_strut(guint desktop) +Rect *screen_area_monitor(guint desktop, guint head) { + if (head > screen_num_monitors) + return NULL; if (desktop >= screen_num_desktops) { - if (desktop == DESKTOP_ALL) - return &strut[screen_num_desktops]; - return NULL; + if (desktop == DESKTOP_ALL) + return &area[screen_num_desktops][head]; + return NULL; } - return &strut[desktop]; + return &area[desktop][head]; } -static void set_root_cursor() +Rect *screen_physical_area() { -#ifdef USE_LIBSN - if (sn_busy_cnt) - XDefineCursor(ob_display, ob_root, ob_cursors.busy); - else -#endif - XDefineCursor(ob_display, ob_root, ob_cursors.ptr); + return screen_physical_area_monitor(screen_num_monitors); } -#ifdef USE_LIBSN -static void sn_timeout(void *data) +Rect *screen_physical_area_monitor(guint head) { - timer_stop(sn_timer); - sn_timer = NULL; - sn_busy_cnt = 0; - - set_root_cursor(); + if (head > screen_num_monitors) + return NULL; + return &monitor_area[head]; } -static void sn_event_func(SnMonitorEvent *ev, void *data) +void screen_set_root_cursor() { - SnStartupSequence *seq; - const char *seq_id, *bin_name; - int cnt = sn_busy_cnt; - - if (!(seq = sn_monitor_event_get_startup_sequence(ev))) - return; - - seq_id = sn_startup_sequence_get_id(seq); - bin_name = sn_startup_sequence_get_binary_name(seq); - - if (!(seq_id && bin_name)) - return; + if (sn_app_starting()) + XDefineCursor(ob_display, RootWindow(ob_display, ob_screen), + ob_cursor(OB_CURSOR_BUSY)); + else + XDefineCursor(ob_display, RootWindow(ob_display, ob_screen), + ob_cursor(OB_CURSOR_POINTER)); +} - switch (sn_monitor_event_get_type(ev)) { - case SN_MONITOR_EVENT_INITIATED: - ++sn_busy_cnt; - if (sn_timer) - timer_stop(sn_timer); - /* 30 second timeout for apps to start */ - sn_timer = timer_start(30 * 1000000, sn_timeout, NULL); - break; - case SN_MONITOR_EVENT_CHANGED: - break; - case SN_MONITOR_EVENT_COMPLETED: - if (sn_busy_cnt) --sn_busy_cnt; - if (sn_timer) { - timer_stop(sn_timer); - sn_timer = NULL; - } - break; - case SN_MONITOR_EVENT_CANCELED: - if (sn_busy_cnt) --sn_busy_cnt; - if (sn_timer) { - timer_stop(sn_timer); - sn_timer = NULL; - } - }; +gboolean screen_pointer_pos(gint *x, gint *y) +{ + Window w; + gint i; + guint u; - if (sn_busy_cnt != cnt) - set_root_cursor(); + return !!XQueryPointer(ob_display, RootWindow(ob_display, ob_screen), + &w, &w, x, y, &i, &i, &u); } -#endif