]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/screen.c
Save screen_last_desktop to the root prop _OB_LAST_DESKTOP.
[mikachu/openbox.git] / openbox / screen.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    screen.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "openbox.h"
22 #include "dock.h"
23 #include "grab.h"
24 #include "startupnotify.h"
25 #include "moveresize.h"
26 #include "config.h"
27 #include "screen.h"
28 #include "client.h"
29 #include "session.h"
30 #include "frame.h"
31 #include "event.h"
32 #include "focus.h"
33 #include "focus_cycle.h"
34 #include "popup.h"
35 #include "version.h"
36 #include "obrender/render.h"
37 #include "gettext.h"
38 #include "obt/display.h"
39 #include "obt/xqueue.h"
40 #include "obt/prop.h"
41
42 #include <X11/Xlib.h>
43 #ifdef HAVE_UNISTD_H
44 #  include <sys/types.h>
45 #  include <unistd.h>
46 #endif
47 #include <assert.h>
48
49 /*! The event mask to grab on the root window */
50 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
51                         EnterWindowMask | LeaveWindowMask | \
52                         SubstructureRedirectMask | FocusChangeMask | \
53                         ButtonPressMask | ButtonReleaseMask)
54
55 static gboolean screen_validate_layout(ObDesktopLayout *l);
56 static gboolean replace_wm(void);
57 static void     screen_fallback_focus(void);
58
59 guint                  screen_num_desktops;
60 guint                  screen_num_monitors;
61 guint                  screen_desktop;
62 guint                  screen_last_desktop;
63 ObScreenShowDestopMode screen_show_desktop_mode;
64 ObDesktopLayout        screen_desktop_layout;
65 gchar                **screen_desktop_names;
66 Window                 screen_support_win;
67 Time                   screen_desktop_user_time = CurrentTime;
68
69 static Size     screen_physical_size;
70 static guint    screen_old_desktop;
71 static gboolean screen_desktop_timeout = TRUE;
72 static guint    screen_desktop_timer = 0;
73 /*! An array of desktops, holding an array of areas per monitor */
74 static Rect  *monitor_area = NULL;
75 /*! An array of desktops, holding an array of struts */
76 static GSList *struts_top = NULL;
77 static GSList *struts_left = NULL;
78 static GSList *struts_right = NULL;
79 static GSList *struts_bottom = NULL;
80
81 static ObPagerPopup *desktop_popup;
82 static guint         desktop_popup_timer = 0;
83 static gboolean      desktop_popup_perm;
84
85 /*! The number of microseconds that you need to be on a desktop before it will
86   replace the remembered "last desktop" */
87 #define REMEMBER_LAST_DESKTOP_TIME 750
88
89 static gboolean replace_wm(void)
90 {
91     gchar *wm_sn;
92     Atom wm_sn_atom;
93     Window current_wm_sn_owner;
94     Time timestamp;
95
96     wm_sn = g_strdup_printf("WM_S%d", ob_screen);
97     wm_sn_atom = XInternAtom(obt_display, wm_sn, FALSE);
98     g_free(wm_sn);
99
100     current_wm_sn_owner = XGetSelectionOwner(obt_display, wm_sn_atom);
101     if (current_wm_sn_owner == screen_support_win)
102         current_wm_sn_owner = None;
103     if (current_wm_sn_owner) {
104         if (!ob_replace_wm) {
105             g_message(_("A window manager is already running on screen %d"),
106                       ob_screen);
107             return FALSE;
108         }
109         obt_display_ignore_errors(TRUE);
110
111         /* We want to find out when the current selection owner dies */
112         XSelectInput(obt_display, current_wm_sn_owner, StructureNotifyMask);
113         XSync(obt_display, FALSE);
114
115         obt_display_ignore_errors(FALSE);
116         if (obt_display_error_occured)
117             current_wm_sn_owner = None;
118     }
119
120     timestamp = event_time();
121
122     XSetSelectionOwner(obt_display, wm_sn_atom, screen_support_win,
123                        timestamp);
124
125     if (XGetSelectionOwner(obt_display, wm_sn_atom) != screen_support_win) {
126         g_message(_("Could not acquire window manager selection on screen %d"),
127                   ob_screen);
128         return FALSE;
129     }
130
131     /* Wait for old window manager to go away */
132     if (current_wm_sn_owner) {
133       gulong wait = 0;
134       const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
135       ObtXQueueWindowType wt;
136
137       wt.window = current_wm_sn_owner;
138       wt.type = DestroyNotify;
139
140       while (wait < timeout) {
141           /* Checks the local queue and incoming events for this event */
142           if (xqueue_exists_local(xqueue_match_window_type, &wt))
143               break;
144           g_usleep(G_USEC_PER_SEC / 10);
145           wait += G_USEC_PER_SEC / 10;
146       }
147
148       if (wait >= timeout) {
149           g_message(_("The WM on screen %d is not exiting"), ob_screen);
150           return FALSE;
151       }
152     }
153
154     /* Send client message indicating that we are now the WM */
155     obt_prop_message(ob_screen, obt_root(ob_screen), OBT_PROP_ATOM(MANAGER),
156                      timestamp, wm_sn_atom, screen_support_win, 0, 0,
157                      SubstructureNotifyMask);
158
159     return TRUE;
160 }
161
162 gboolean screen_annex(void)
163 {
164     XSetWindowAttributes attrib;
165     pid_t pid;
166     gint i, num_support;
167     gulong *supported;
168
169     /* create the netwm support window */
170     attrib.override_redirect = TRUE;
171     attrib.event_mask = PropertyChangeMask;
172     screen_support_win = XCreateWindow(obt_display, obt_root(ob_screen),
173                                        -100, -100, 1, 1, 0,
174                                        CopyFromParent, InputOutput,
175                                        CopyFromParent,
176                                        CWEventMask | CWOverrideRedirect,
177                                        &attrib);
178     XMapWindow(obt_display, screen_support_win);
179     XLowerWindow(obt_display, screen_support_win);
180
181     if (!replace_wm()) {
182         XDestroyWindow(obt_display, screen_support_win);
183         return FALSE;
184     }
185
186     obt_display_ignore_errors(TRUE);
187     XSelectInput(obt_display, obt_root(ob_screen), ROOT_EVENTMASK);
188     obt_display_ignore_errors(FALSE);
189     if (obt_display_error_occured) {
190         g_message(_("A window manager is already running on screen %d"),
191                   ob_screen);
192
193         XDestroyWindow(obt_display, screen_support_win);
194         return FALSE;
195     }
196
197     screen_set_root_cursor();
198
199     /* set the OPENBOX_PID hint */
200     pid = getpid();
201     OBT_PROP_SET32(obt_root(ob_screen), OPENBOX_PID, CARDINAL, pid);
202
203     /* set supporting window */
204     OBT_PROP_SET32(obt_root(ob_screen),
205                    NET_SUPPORTING_WM_CHECK, WINDOW, screen_support_win);
206
207     /* set properties on the supporting window */
208     OBT_PROP_SETS(screen_support_win, NET_WM_NAME, "Openbox");
209     OBT_PROP_SET32(screen_support_win, NET_SUPPORTING_WM_CHECK,
210                    WINDOW, screen_support_win);
211
212     /* set the _NET_SUPPORTED_ATOMS hint */
213
214     /* this is all the atoms after NET_SUPPORTED in the ObtPropAtoms enum */
215     num_support = OBT_PROP_NUM_ATOMS - OBT_PROP_NET_SUPPORTED - 1;
216     i = 0;
217     supported = g_new(gulong, num_support);
218     supported[i++] = OBT_PROP_ATOM(NET_SUPPORTING_WM_CHECK);
219     supported[i++] = OBT_PROP_ATOM(NET_WM_FULL_PLACEMENT);
220     supported[i++] = OBT_PROP_ATOM(NET_CURRENT_DESKTOP);
221     supported[i++] = OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS);
222     supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_GEOMETRY);
223     supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_VIEWPORT);
224     supported[i++] = OBT_PROP_ATOM(NET_ACTIVE_WINDOW);
225     supported[i++] = OBT_PROP_ATOM(NET_WORKAREA);
226     supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST);
227     supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST_STACKING);
228     supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_NAMES);
229     supported[i++] = OBT_PROP_ATOM(NET_CLOSE_WINDOW);
230     supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_LAYOUT);
231     supported[i++] = OBT_PROP_ATOM(NET_SHOWING_DESKTOP);
232     supported[i++] = OBT_PROP_ATOM(NET_WM_NAME);
233     supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_NAME);
234     supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_NAME);
235     supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_ICON_NAME);
236     supported[i++] = OBT_PROP_ATOM(NET_WM_DESKTOP);
237     supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT);
238     supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL);
239     supported[i++] = OBT_PROP_ATOM(NET_WM_ICON);
240     supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY);
241     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE);
242     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP);
243     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK);
244     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR);
245     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU);
246     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY);
247     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH);
248     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG);
249     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL);
250     supported[i++] = OBT_PROP_ATOM(NET_WM_ALLOWED_ACTIONS);
251     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_OPACITY);
252     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
253     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
254     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
255     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
256     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
257     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
258     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
259     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
260     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
261     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
262     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
263     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE);
264     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
265     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
266     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
267     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
268     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
269     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
270     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
271     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
272     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
273     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
274     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
275     supported[i++] = OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW);
276     supported[i++] = OBT_PROP_ATOM(NET_WM_MOVERESIZE);
277     supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME);
278 /*
279     supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME_WINDOW);
280 */
281     supported[i++] = OBT_PROP_ATOM(NET_FRAME_EXTENTS);
282     supported[i++] = OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS);
283     supported[i++] = OBT_PROP_ATOM(NET_RESTACK_WINDOW);
284     supported[i++] = OBT_PROP_ATOM(NET_STARTUP_ID);
285 #ifdef SYNC
286     supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
287     supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER);
288 #endif
289     supported[i++] = OBT_PROP_ATOM(NET_WM_PID);
290     supported[i++] = OBT_PROP_ATOM(NET_WM_PING);
291
292     supported[i++] = OBT_PROP_ATOM(KDE_WM_CHANGE_STATE);
293     supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_FRAME_STRUT);
294     supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
295
296     supported[i++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
297     supported[i++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
298     supported[i++] = OBT_PROP_ATOM(OPENBOX_PID);
299     supported[i++] = OBT_PROP_ATOM(OB_THEME);
300     supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE);
301     supported[i++] = OBT_PROP_ATOM(OB_LAST_DESKTOP);
302     supported[i++] = OBT_PROP_ATOM(OB_CONTROL);
303     supported[i++] = OBT_PROP_ATOM(OB_VERSION);
304     supported[i++] = OBT_PROP_ATOM(OB_APP_ROLE);
305     supported[i++] = OBT_PROP_ATOM(OB_APP_TITLE);
306     supported[i++] = OBT_PROP_ATOM(OB_APP_NAME);
307     supported[i++] = OBT_PROP_ATOM(OB_APP_CLASS);
308     supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_NAME);
309     supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_CLASS);
310     supported[i++] = OBT_PROP_ATOM(OB_APP_TYPE);
311     g_assert(i == num_support);
312
313     OBT_PROP_SETA32(obt_root(ob_screen),
314                     NET_SUPPORTED, ATOM, supported, num_support);
315     g_free(supported);
316
317     OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION,
318                   OPENBOX_VERSION);
319
320     return TRUE;
321 }
322
323 void screen_startup(gboolean reconfig)
324 {
325     gchar **names = NULL;
326     guint32 d;
327     gboolean namesexist = FALSE;
328
329     desktop_popup = pager_popup_new();
330     desktop_popup_perm = FALSE;
331     pager_popup_height(desktop_popup, POPUP_HEIGHT);
332
333     if (reconfig) {
334         /* update the pager popup's width */
335         pager_popup_text_width_to_strings(desktop_popup,
336                                           screen_desktop_names,
337                                           screen_num_desktops);
338         return;
339     }
340
341     /* get the initial size */
342     screen_resize();
343
344     /* have names already been set for the desktops? */
345     if (OBT_PROP_GETSS_UTF8(obt_root(ob_screen), NET_DESKTOP_NAMES, &names)) {
346         g_strfreev(names);
347         namesexist = TRUE;
348     }
349
350     /* if names don't exist and we have session names, set those.
351        do this stuff BEFORE setting the number of desktops, because that
352        will create default names for them
353     */
354     if (!namesexist && session_desktop_names != NULL) {
355         guint i, numnames;
356         GSList *it;
357
358         /* get the desktop names */
359         numnames = g_slist_length(session_desktop_names);
360         names = g_new(gchar*, numnames + 1);
361         names[numnames] = NULL;
362         for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
363             names[i] = g_strdup(it->data);
364
365         /* set the root window property */
366         OBT_PROP_SETSS(obt_root(ob_screen),
367                        NET_DESKTOP_NAMES, (const gchar*const*)names);
368
369         g_strfreev(names);
370     }
371
372     /* set the number of desktops, if it's not already set.
373
374        this will also set the default names from the config file up for
375        desktops that don't have names yet */
376     screen_num_desktops = 0;
377     if (OBT_PROP_GET32(obt_root(ob_screen),
378                        NET_NUMBER_OF_DESKTOPS, CARDINAL, &d))
379     {
380         if (d != config_desktops_num) {
381             /* TRANSLATORS: If you need to specify a different order of the
382                arguments, you can use %1$d for the first one and %2$d for the
383                second one. For example,
384                "The current session has %2$d desktops, but Openbox is configured for %1$d ..." */
385             g_warning(ngettext("Openbox is configured for %d desktop, but the current session has %d.  Overriding the Openbox configuration.", "Openbox is configured for %d desktops, but the current session has %d.  Overriding the Openbox configuration.", config_desktops_num),
386                       config_desktops_num, d);
387         }
388         screen_set_num_desktops(d);
389     }
390     /* restore from session if possible */
391     else if (session_num_desktops)
392         screen_set_num_desktops(session_num_desktops);
393     else
394         screen_set_num_desktops(config_desktops_num);
395
396     screen_desktop = screen_num_desktops;  /* something invalid */
397     /* start on the current desktop when a wm was already running */
398     if (OBT_PROP_GET32(obt_root(ob_screen),
399                        NET_CURRENT_DESKTOP, CARDINAL, &d) &&
400         d < screen_num_desktops)
401     {
402         screen_set_desktop(d, FALSE);
403     } else if (session_desktop >= 0)
404         screen_set_desktop(MIN((guint)session_desktop,
405                                screen_num_desktops), FALSE);
406     else
407         screen_set_desktop(MIN(config_screen_firstdesk,
408                                screen_num_desktops) - 1, FALSE);
409     OBT_PROP_GET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, &screen_last_desktop);
410     if (screen_last_desktop < 0 || screen_last_desktop >= screen_num_desktops) {
411         screen_last_desktop = screen_desktop;
412         OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
413     }
414
415     /* don't start in showing-desktop mode */
416     screen_show_desktop_mode = SCREEN_SHOW_DESKTOP_NO;
417     OBT_PROP_SET32(obt_root(ob_screen),
418                    NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop());
419
420     if (session_desktop_layout_present &&
421         screen_validate_layout(&session_desktop_layout))
422     {
423         screen_desktop_layout = session_desktop_layout;
424     }
425     else
426         screen_update_layout();
427 }
428
429 void screen_shutdown(gboolean reconfig)
430 {
431     pager_popup_free(desktop_popup);
432
433     if (reconfig)
434         return;
435
436     XSelectInput(obt_display, obt_root(ob_screen), NoEventMask);
437
438     /* we're not running here no more! */
439     OBT_PROP_ERASE(obt_root(ob_screen), OPENBOX_PID);
440     /* not without us */
441     OBT_PROP_ERASE(obt_root(ob_screen), NET_SUPPORTED);
442     /* don't keep this mode */
443     OBT_PROP_ERASE(obt_root(ob_screen), NET_SHOWING_DESKTOP);
444
445     XDestroyWindow(obt_display, screen_support_win);
446
447     g_strfreev(screen_desktop_names);
448     screen_desktop_names = NULL;
449 }
450
451 void screen_resize(void)
452 {
453     gint w, h;
454     GList *it;
455     gulong geometry[2];
456
457     w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
458     h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
459
460     /* Set the _NET_DESKTOP_GEOMETRY hint */
461     screen_physical_size.width = geometry[0] = w;
462     screen_physical_size.height = geometry[1] = h;
463     OBT_PROP_SETA32(obt_root(ob_screen),
464                     NET_DESKTOP_GEOMETRY, CARDINAL, geometry, 2);
465
466     if (ob_state() != OB_STATE_RUNNING)
467         return;
468
469     /* this calls screen_update_areas(), which we need ! */
470     dock_configure();
471
472     for (it = client_list; it; it = g_list_next(it)) {
473         client_move_onscreen(it->data, FALSE);
474         client_reconfigure(it->data, FALSE);
475     }
476 }
477
478 void screen_set_num_desktops(guint num)
479 {
480     gulong *viewport;
481     GList *it, *stacking_copy;
482
483     g_assert(num > 0);
484
485     if (screen_num_desktops == num) return;
486
487     screen_num_desktops = num;
488     OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num);
489
490     /* set the viewport hint */
491     viewport = g_new0(gulong, num * 2);
492     OBT_PROP_SETA32(obt_root(ob_screen),
493                     NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2);
494     g_free(viewport);
495
496     /* the number of rows/columns will differ */
497     screen_update_layout();
498
499     /* move windows on desktops that will no longer exist!
500        make a copy of the list cuz we're changing it */
501     stacking_copy = g_list_copy(stacking_list);
502     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
503         if (WINDOW_IS_CLIENT(it->data)) {
504             ObClient *c = it->data;
505             if (c->desktop != DESKTOP_ALL && c->desktop >= num)
506                 client_set_desktop(c, num - 1, FALSE, TRUE);
507             /* raise all the windows that are on the current desktop which
508                is being merged */
509             else if (screen_desktop == num - 1 &&
510                      (c->desktop == DESKTOP_ALL ||
511                       c->desktop == screen_desktop))
512                 stacking_raise(CLIENT_AS_WINDOW(c));
513         }
514     }
515     g_list_free(stacking_copy);
516
517     /* change our struts/area to match (after moving windows) */
518     screen_update_areas();
519
520     /* may be some unnamed desktops that we need to fill in with names
521        (after updating the areas so the popup can resize) */
522     screen_update_desktop_names();
523
524     /* change our desktop if we're on one that no longer exists! */
525     if (screen_desktop >= screen_num_desktops)
526         screen_set_desktop(num - 1, TRUE);
527 }
528
529 static void screen_fallback_focus(void)
530 {
531     ObClient *c;
532     gboolean allow_omni;
533
534     /* only allow omnipresent windows to get focus on desktop change if
535        an omnipresent window is already focused (it'll keep focus probably, but
536        maybe not depending on mouse-focus options) */
537     allow_omni = focus_client && (client_normal(focus_client) &&
538                                   focus_client->desktop == DESKTOP_ALL);
539
540     /* the client moved there already so don't move focus. prevent flicker
541        on sendtodesktop + follow */
542     if (focus_client && focus_client->desktop == screen_desktop)
543         return;
544
545     /* have to try focus here because when you leave an empty desktop
546        there is no focus out to watch for. also, we have different rules
547        here. we always allow it to look under the mouse pointer if
548        config_focus_last is FALSE
549
550        do this before hiding the windows so if helper windows are coming
551        with us, they don't get hidden
552     */
553     if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
554                             !allow_omni)))
555     {
556         /* only do the flicker reducing stuff ahead of time if we are going
557            to call xsetinputfocus on the window ourselves. otherwise there is
558            no guarantee the window will actually take focus.. */
559         if (c->can_focus) {
560             /* reduce flicker by hiliting now rather than waiting for the
561                server FocusIn event */
562             frame_adjust_focus(c->frame, TRUE);
563             /* do this here so that if you switch desktops to a window with
564                helper windows then the helper windows won't flash */
565             client_bring_helper_windows(c);
566         }
567     }
568 }
569
570 static gboolean last_desktop_func(gpointer data)
571 {
572     screen_desktop_timeout = TRUE;
573     OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
574     screen_desktop_timer = 0;
575     return FALSE; /* don't repeat */
576 }
577
578 void screen_set_desktop(guint num, gboolean dofocus)
579 {
580     GList *it;
581     guint previous;
582     gulong ignore_start;
583
584     g_assert(num < screen_num_desktops);
585
586     previous = screen_desktop;
587     screen_desktop = num;
588
589     if (previous == num) return;
590
591     OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num);
592
593     /* This whole thing decides when/how to save the screen_last_desktop so
594        that it can be restored later if you want */
595     if (screen_desktop_timeout) {
596         /* If screen_desktop_timeout is true, then we've been on this desktop
597            long enough and we can save it as the last desktop. */
598
599         if (screen_last_desktop == previous)
600             /* this is the startup state only */
601             screen_old_desktop = screen_desktop;
602         else {
603             /* save the "last desktop" as the "old desktop" */
604             screen_old_desktop = screen_last_desktop;
605             /* save the desktop we're coming from as the "last desktop" */
606             screen_last_desktop = previous;
607         }
608     }
609     else {
610         /* If screen_desktop_timeout is false, then we just got to this desktop
611            and we are moving away again. */
612
613         if (screen_desktop == screen_last_desktop) {
614             /* If we are moving to the "last desktop" .. */
615             if (previous == screen_old_desktop) {
616                 /* .. from the "old desktop", change the last desktop to
617                    be where we are coming from */
618                 screen_last_desktop = screen_old_desktop;
619             }
620             else if (screen_last_desktop == screen_old_desktop) {
621                 /* .. and also to the "old desktop", change the "last
622                    desktop" to be where we are coming from */
623                 screen_last_desktop = previous;
624             }
625             else {
626                 /* .. from some other desktop, then set the "last desktop" to
627                    be the saved "old desktop", i.e. where we were before the
628                    "last desktop" */
629                 screen_last_desktop = screen_old_desktop;
630             }
631         }
632         else {
633             /* If we are moving to any desktop besides the "last desktop"..
634                (this is the normal case) */
635             if (screen_desktop == screen_old_desktop) {
636                 /* If moving to the "old desktop", which is not the
637                    "last desktop", don't save anything */
638             }
639             else if (previous == screen_old_desktop) {
640                 /* If moving from the "old desktop", and not to the
641                    "last desktop", don't save anything */
642             }
643             else if (screen_last_desktop == screen_old_desktop) {
644                 /* If the "last desktop" is the same as "old desktop" and
645                    you're not moving to the "last desktop" then save where
646                    we're coming from as the "last desktop" */
647                 screen_last_desktop = previous;
648             }
649             else {
650                 /* If the "last desktop" is different from the "old desktop"
651                    and you're not moving to the "last desktop", then don't save
652                    anything */
653             }
654         }
655     }
656     screen_desktop_timeout = FALSE;
657     if (screen_desktop_timer) g_source_remove(screen_desktop_timer);
658     screen_desktop_timer = g_timeout_add(REMEMBER_LAST_DESKTOP_TIME,
659                                          last_desktop_func, NULL);
660
661     ob_debug("Moving to desktop %d", num+1);
662
663     if (ob_state() == OB_STATE_RUNNING)
664         screen_show_desktop_popup(screen_desktop, FALSE);
665
666     /* ignore enter events caused by the move */
667     ignore_start = event_start_ignore_all_enters();
668
669     if (moveresize_client)
670         client_set_desktop(moveresize_client, num, TRUE, FALSE);
671
672     /* show windows before hiding the rest to lessen the enter/leave events */
673
674     /* show windows from top to bottom */
675     for (it = stacking_list; it; it = g_list_next(it)) {
676         if (WINDOW_IS_CLIENT(it->data)) {
677             ObClient *c = it->data;
678             client_show(c);
679         }
680     }
681
682     if (dofocus) screen_fallback_focus();
683
684     /* hide windows from bottom to top */
685     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
686         if (WINDOW_IS_CLIENT(it->data)) {
687             ObClient *c = it->data;
688             if (client_hide(c)) {
689                 if (c == focus_client) {
690                     /* c was focused and we didn't do fallback clearly so make
691                        sure openbox doesnt still consider the window focused.
692                        this happens when using NextWindow with allDesktops,
693                        since it doesnt want to move focus on desktop change,
694                        but the focus is not going to stay with the current
695                        window, which has now disappeared.
696                        only do this if the client was actually hidden,
697                        otherwise it can keep focus. */
698                     focus_set_client(NULL);
699                 }
700             }
701         }
702     }
703
704     focus_cycle_addremove(NULL, TRUE);
705
706     event_end_ignore_all_enters(ignore_start);
707
708     if (event_source_time() != CurrentTime)
709         screen_desktop_user_time = event_source_time();
710 }
711
712 void screen_add_desktop(gboolean current)
713 {
714     gulong ignore_start;
715
716     /* ignore enter events caused by this */
717     ignore_start = event_start_ignore_all_enters();
718
719     screen_set_num_desktops(screen_num_desktops+1);
720
721     /* move all the clients over */
722     if (current) {
723         GList *it;
724
725         for (it = client_list; it; it = g_list_next(it)) {
726             ObClient *c = it->data;
727             if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
728                 /* don't move direct children, they'll be moved with their
729                    parent - which will have to be on the same desktop */
730                 !client_direct_parent(c))
731             {
732                 ob_debug("moving window %s", c->title);
733                 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
734             }
735         }
736     }
737
738     event_end_ignore_all_enters(ignore_start);
739 }
740
741 void screen_remove_desktop(gboolean current)
742 {
743     guint rmdesktop, movedesktop;
744     GList *it, *stacking_copy;
745     gulong ignore_start;
746
747     if (screen_num_desktops <= 1) return;
748
749     /* ignore enter events caused by this */
750     ignore_start = event_start_ignore_all_enters();
751
752     /* what desktop are we removing and moving to? */
753     if (current)
754         rmdesktop = screen_desktop;
755     else
756         rmdesktop = screen_num_desktops - 1;
757     if (rmdesktop < screen_num_desktops - 1)
758         movedesktop = rmdesktop + 1;
759     else
760         movedesktop = rmdesktop;
761
762     /* make a copy of the list cuz we're changing it */
763     stacking_copy = g_list_copy(stacking_list);
764     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
765         if (WINDOW_IS_CLIENT(it->data)) {
766             ObClient *c = it->data;
767             guint d = c->desktop;
768             if (d != DESKTOP_ALL && d >= movedesktop &&
769                 /* don't move direct children, they'll be moved with their
770                    parent - which will have to be on the same desktop */
771                 !client_direct_parent(c))
772             {
773                 ob_debug("moving window %s", c->title);
774                 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
775             }
776             /* raise all the windows that are on the current desktop which
777                is being merged */
778             if ((screen_desktop == rmdesktop - 1 ||
779                  screen_desktop == rmdesktop) &&
780                 (d == DESKTOP_ALL || d == screen_desktop))
781             {
782                 stacking_raise(CLIENT_AS_WINDOW(c));
783                 ob_debug("raising window %s", c->title);
784             }
785         }
786     }
787     g_list_free(stacking_copy);
788
789     /* fallback focus like we're changing desktops */
790     if (screen_desktop < screen_num_desktops - 1) {
791         screen_fallback_focus();
792         ob_debug("fake desktop change");
793     }
794
795     screen_set_num_desktops(screen_num_desktops-1);
796
797     event_end_ignore_all_enters(ignore_start);
798 }
799
800 static void get_row_col(guint d, guint *r, guint *c)
801 {
802     switch (screen_desktop_layout.orientation) {
803     case OB_ORIENTATION_HORZ:
804         switch (screen_desktop_layout.start_corner) {
805         case OB_CORNER_TOPLEFT:
806             *r = d / screen_desktop_layout.columns;
807             *c = d % screen_desktop_layout.columns;
808             break;
809         case OB_CORNER_BOTTOMLEFT:
810             *r = screen_desktop_layout.rows - 1 -
811                 d / screen_desktop_layout.columns;
812             *c = d % screen_desktop_layout.columns;
813             break;
814         case OB_CORNER_TOPRIGHT:
815             *r = d / screen_desktop_layout.columns;
816             *c = screen_desktop_layout.columns - 1 -
817                 d % screen_desktop_layout.columns;
818             break;
819         case OB_CORNER_BOTTOMRIGHT:
820             *r = screen_desktop_layout.rows - 1 -
821                 d / screen_desktop_layout.columns;
822             *c = screen_desktop_layout.columns - 1 -
823                 d % screen_desktop_layout.columns;
824             break;
825         }
826         break;
827     case OB_ORIENTATION_VERT:
828         switch (screen_desktop_layout.start_corner) {
829         case OB_CORNER_TOPLEFT:
830             *r = d % screen_desktop_layout.rows;
831             *c = d / screen_desktop_layout.rows;
832             break;
833         case OB_CORNER_BOTTOMLEFT:
834             *r = screen_desktop_layout.rows - 1 -
835                 d % screen_desktop_layout.rows;
836             *c = d / screen_desktop_layout.rows;
837             break;
838         case OB_CORNER_TOPRIGHT:
839             *r = d % screen_desktop_layout.rows;
840             *c = screen_desktop_layout.columns - 1 -
841                 d / screen_desktop_layout.rows;
842             break;
843         case OB_CORNER_BOTTOMRIGHT:
844             *r = screen_desktop_layout.rows - 1 -
845                 d % screen_desktop_layout.rows;
846             *c = screen_desktop_layout.columns - 1 -
847                 d / screen_desktop_layout.rows;
848             break;
849         }
850         break;
851     }
852 }
853
854 static guint translate_row_col(guint r, guint c)
855 {
856     switch (screen_desktop_layout.orientation) {
857     case OB_ORIENTATION_HORZ:
858         switch (screen_desktop_layout.start_corner) {
859         case OB_CORNER_TOPLEFT:
860             return r % screen_desktop_layout.rows *
861                 screen_desktop_layout.columns +
862                 c % screen_desktop_layout.columns;
863         case OB_CORNER_BOTTOMLEFT:
864             return (screen_desktop_layout.rows - 1 -
865                     r % screen_desktop_layout.rows) *
866                 screen_desktop_layout.columns +
867                 c % screen_desktop_layout.columns;
868         case OB_CORNER_TOPRIGHT:
869             return r % screen_desktop_layout.rows *
870                 screen_desktop_layout.columns +
871                 (screen_desktop_layout.columns - 1 -
872                  c % screen_desktop_layout.columns);
873         case OB_CORNER_BOTTOMRIGHT:
874             return (screen_desktop_layout.rows - 1 -
875                     r % screen_desktop_layout.rows) *
876                 screen_desktop_layout.columns +
877                 (screen_desktop_layout.columns - 1 -
878                  c % screen_desktop_layout.columns);
879         }
880     case OB_ORIENTATION_VERT:
881         switch (screen_desktop_layout.start_corner) {
882         case OB_CORNER_TOPLEFT:
883             return c % screen_desktop_layout.columns *
884                 screen_desktop_layout.rows +
885                 r % screen_desktop_layout.rows;
886         case OB_CORNER_BOTTOMLEFT:
887             return c % screen_desktop_layout.columns *
888                 screen_desktop_layout.rows +
889                 (screen_desktop_layout.rows - 1 -
890                  r % screen_desktop_layout.rows);
891         case OB_CORNER_TOPRIGHT:
892             return (screen_desktop_layout.columns - 1 -
893                     c % screen_desktop_layout.columns) *
894                 screen_desktop_layout.rows +
895                 r % screen_desktop_layout.rows;
896         case OB_CORNER_BOTTOMRIGHT:
897             return (screen_desktop_layout.columns - 1 -
898                     c % screen_desktop_layout.columns) *
899                 screen_desktop_layout.rows +
900                 (screen_desktop_layout.rows - 1 -
901                  r % screen_desktop_layout.rows);
902         }
903     }
904     g_assert_not_reached();
905     return 0;
906 }
907
908 static gboolean hide_desktop_popup_func(gpointer data)
909 {
910     pager_popup_hide(desktop_popup);
911     desktop_popup_timer = 0;
912     return FALSE; /* don't repeat */
913 }
914
915 void screen_show_desktop_popup(guint d, gboolean perm)
916 {
917     const Rect *a;
918
919     /* 0 means don't show the popup */
920     if (!config_desktop_popup_time) return;
921
922     a = screen_physical_area_primary(FALSE);
923     pager_popup_position(desktop_popup, CenterGravity,
924                          a->x + a->width / 2, a->y + a->height / 2);
925     pager_popup_icon_size_multiplier(desktop_popup,
926                                      (screen_desktop_layout.columns /
927                                       screen_desktop_layout.rows) / 2,
928                                      (screen_desktop_layout.rows/
929                                       screen_desktop_layout.columns) / 2);
930     pager_popup_max_width(desktop_popup,
931                           MAX(a->width/3, POPUP_WIDTH));
932     pager_popup_show(desktop_popup, screen_desktop_names[d], d);
933
934     if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
935     desktop_popup_timer = 0;
936     if (!perm && !desktop_popup_perm)
937         /* only hide if its not already being show permanently */
938         desktop_popup_timer = g_timeout_add(config_desktop_popup_time,
939                                             hide_desktop_popup_func,
940                                             desktop_popup);
941     if (perm)
942         desktop_popup_perm = TRUE;
943 }
944
945 void screen_hide_desktop_popup(void)
946 {
947     if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
948     desktop_popup_timer = 0;
949     pager_popup_hide(desktop_popup);
950     desktop_popup_perm = FALSE;
951 }
952
953 guint screen_find_desktop(guint from, ObDirection dir,
954                           gboolean wrap, gboolean linear)
955 {
956     guint r, c;
957     guint d;
958
959     d = from;
960     get_row_col(d, &r, &c);
961     if (linear) {
962         switch (dir) {
963         case OB_DIRECTION_EAST:
964             if (d < screen_num_desktops - 1)
965                 ++d;
966             else if (wrap)
967                 d = 0;
968             else
969                 return from;
970             break;
971         case OB_DIRECTION_WEST:
972             if (d > 0)
973                 --d;
974             else if (wrap)
975                 d = screen_num_desktops - 1;
976             else
977                 return from;
978             break;
979         default:
980             g_assert_not_reached();
981             return from;
982         }
983     } else {
984         switch (dir) {
985         case OB_DIRECTION_EAST:
986             ++c;
987             if (c >= screen_desktop_layout.columns) {
988                 if (wrap)
989                     c = 0;
990                 else
991                     return from;
992             }
993             d = translate_row_col(r, c);
994             if (d >= screen_num_desktops) {
995                 if (wrap)
996                     ++c;
997                 else
998                     return from;
999             }
1000             break;
1001         case OB_DIRECTION_WEST:
1002             --c;
1003             if (c >= screen_desktop_layout.columns) {
1004                 if (wrap)
1005                     c = screen_desktop_layout.columns - 1;
1006                 else
1007                     return from;
1008             }
1009             d = translate_row_col(r, c);
1010             if (d >= screen_num_desktops) {
1011                 if (wrap)
1012                     --c;
1013                 else
1014                     return from;
1015             }
1016             break;
1017         case OB_DIRECTION_SOUTH:
1018             ++r;
1019             if (r >= screen_desktop_layout.rows) {
1020                 if (wrap)
1021                     r = 0;
1022                 else
1023                     return from;
1024             }
1025             d = translate_row_col(r, c);
1026             if (d >= screen_num_desktops) {
1027                 if (wrap)
1028                     ++r;
1029                 else
1030                     return from;
1031             }
1032             break;
1033         case OB_DIRECTION_NORTH:
1034             --r;
1035             if (r >= screen_desktop_layout.rows) {
1036                 if (wrap)
1037                     r = screen_desktop_layout.rows - 1;
1038                 else
1039                     return from;
1040             }
1041             d = translate_row_col(r, c);
1042             if (d >= screen_num_desktops) {
1043                 if (wrap)
1044                     --r;
1045                 else
1046                     return from;
1047             }
1048             break;
1049         default:
1050             g_assert_not_reached();
1051             return from;
1052         }
1053
1054         d = translate_row_col(r, c);
1055     }
1056     return d;
1057 }
1058
1059 static gboolean screen_validate_layout(ObDesktopLayout *l)
1060 {
1061     if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
1062         return FALSE;
1063
1064     /* fill in a zero rows/columns */
1065     if (l->columns == 0) {
1066         l->columns = screen_num_desktops / l->rows;
1067         if (l->rows * l->columns < screen_num_desktops)
1068             l->columns++;
1069         if (l->rows * l->columns >= screen_num_desktops + l->columns)
1070             l->rows--;
1071     } else if (l->rows == 0) {
1072         l->rows = screen_num_desktops / l->columns;
1073         if (l->columns * l->rows < screen_num_desktops)
1074             l->rows++;
1075         if (l->columns * l->rows >= screen_num_desktops + l->rows)
1076             l->columns--;
1077     }
1078
1079     /* bounds checking */
1080     if (l->orientation == OB_ORIENTATION_HORZ) {
1081         l->columns = MIN(screen_num_desktops, l->columns);
1082         l->rows = MIN(l->rows,
1083                       (screen_num_desktops + l->columns - 1) / l->columns);
1084         l->columns = screen_num_desktops / l->rows +
1085             !!(screen_num_desktops % l->rows);
1086     } else {
1087         l->rows = MIN(screen_num_desktops, l->rows);
1088         l->columns = MIN(l->columns,
1089                          (screen_num_desktops + l->rows - 1) / l->rows);
1090         l->rows = screen_num_desktops / l->columns +
1091             !!(screen_num_desktops % l->columns);
1092     }
1093     return TRUE;
1094 }
1095
1096 void screen_update_layout(void)
1097
1098 {
1099     ObDesktopLayout l;
1100     guint32 *data;
1101     guint num;
1102
1103     screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
1104     screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
1105     screen_desktop_layout.rows = 1;
1106     screen_desktop_layout.columns = screen_num_desktops;
1107
1108     if (OBT_PROP_GETA32(obt_root(ob_screen),
1109                         NET_DESKTOP_LAYOUT, CARDINAL, &data, &num)) {
1110         if (num == 3 || num == 4) {
1111
1112             if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_VERT))
1113                 l.orientation = OB_ORIENTATION_VERT;
1114             else if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_HORZ))
1115                 l.orientation = OB_ORIENTATION_HORZ;
1116             else
1117                 return;
1118
1119             if (num < 4)
1120                 l.start_corner = OB_CORNER_TOPLEFT;
1121             else {
1122                 if (data[3] == OBT_PROP_ATOM(NET_WM_TOPLEFT))
1123                     l.start_corner = OB_CORNER_TOPLEFT;
1124                 else if (data[3] == OBT_PROP_ATOM(NET_WM_TOPRIGHT))
1125                     l.start_corner = OB_CORNER_TOPRIGHT;
1126                 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMRIGHT))
1127                     l.start_corner = OB_CORNER_BOTTOMRIGHT;
1128                 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMLEFT))
1129                     l.start_corner = OB_CORNER_BOTTOMLEFT;
1130                 else
1131                     return;
1132             }
1133
1134             l.columns = data[1];
1135             l.rows = data[2];
1136
1137             if (screen_validate_layout(&l))
1138                 screen_desktop_layout = l;
1139
1140             g_free(data);
1141         }
1142     }
1143 }
1144
1145 void screen_update_desktop_names(void)
1146 {
1147     guint i;
1148
1149     /* empty the array */
1150     g_strfreev(screen_desktop_names);
1151     screen_desktop_names = NULL;
1152
1153     if (OBT_PROP_GETSS(obt_root(ob_screen),
1154                        NET_DESKTOP_NAMES, &screen_desktop_names))
1155         for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
1156     else
1157         i = 0;
1158     if (i < screen_num_desktops) {
1159         GSList *it;
1160
1161         screen_desktop_names = g_renew(gchar*, screen_desktop_names,
1162                                        screen_num_desktops + 1);
1163         screen_desktop_names[screen_num_desktops] = NULL;
1164
1165         it = g_slist_nth(config_desktops_names, i);
1166
1167         for (; i < screen_num_desktops; ++i) {
1168             if (it && ((char*)it->data)[0]) /* not empty */
1169                 /* use the names from the config file when possible */
1170                 screen_desktop_names[i] = g_strdup(it->data);
1171             else
1172                 /* make up a nice name if it's not though */
1173                 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
1174                                                           i + 1);
1175             if (it) it = g_slist_next(it);
1176         }
1177
1178         /* if we changed any names, then set the root property so we can
1179            all agree on the names */
1180         OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
1181                        (const gchar*const*)screen_desktop_names);
1182     }
1183
1184     /* resize the pager for these names */
1185     pager_popup_text_width_to_strings(desktop_popup,
1186                                       screen_desktop_names,
1187                                       screen_num_desktops);
1188 }
1189
1190 void screen_show_desktop(ObScreenShowDestopMode show_mode, ObClient *show_only)
1191 {
1192     GList *it;
1193
1194     ObScreenShowDestopMode before_mode = screen_show_desktop_mode;
1195
1196     gboolean showing_before = screen_showing_desktop();
1197     screen_show_desktop_mode = show_mode;
1198     gboolean showing_after = screen_showing_desktop();
1199
1200     if (showing_before == showing_after) {
1201         /* No change. */
1202         screen_show_desktop_mode = before_mode;
1203         return;
1204     }
1205
1206     if (screen_show_desktop_mode == SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE &&
1207         show_only != NULL)
1208     {
1209         /* If we're showing the desktop until the show-mode is toggled, we
1210            don't allow breaking out of showing-desktop mode unless we're
1211            showing all the windows again. */
1212         screen_show_desktop_mode = before_mode;
1213         return;
1214     }
1215
1216     if (showing_after) {
1217         /* hide windows bottom to top */
1218         for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1219             if (WINDOW_IS_CLIENT(it->data)) {
1220                 ObClient *client = it->data;
1221                 client_showhide(client);
1222             }
1223         }
1224     }
1225     else {
1226         /* restore windows top to bottom */
1227         for (it = stacking_list; it; it = g_list_next(it)) {
1228             if (WINDOW_IS_CLIENT(it->data)) {
1229                 ObClient *client = it->data;
1230                 if (client_should_show(client)) {
1231                     if (!show_only || client == show_only)
1232                         client_show(client);
1233                     else
1234                         client_iconify(client, TRUE, FALSE, TRUE);
1235                 }
1236             }
1237         }
1238     }
1239
1240     if (showing_after) {
1241         /* focus the desktop */
1242         for (it = focus_order; it; it = g_list_next(it)) {
1243             ObClient *c = it->data;
1244             if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1245                 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1246                 client_focus(it->data))
1247                 break;
1248         }
1249     }
1250     else if (!show_only) {
1251         ObClient *c;
1252
1253         if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
1254             /* only do the flicker reducing stuff ahead of time if we are going
1255                to call xsetinputfocus on the window ourselves. otherwise there
1256                is no guarantee the window will actually take focus.. */
1257             if (c->can_focus) {
1258                 /* reduce flicker by hiliting now rather than waiting for the
1259                    server FocusIn event */
1260                 frame_adjust_focus(c->frame, TRUE);
1261             }
1262         }
1263     }
1264
1265     OBT_PROP_SET32(obt_root(ob_screen),
1266                    NET_SHOWING_DESKTOP,
1267                    CARDINAL,
1268                    !!showing_after);
1269 }
1270
1271 gboolean screen_showing_desktop()
1272 {
1273     switch (screen_show_desktop_mode) {
1274     case SCREEN_SHOW_DESKTOP_NO:
1275         return FALSE;
1276     case SCREEN_SHOW_DESKTOP_UNTIL_WINDOW:
1277     case SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE:
1278         return TRUE;
1279     }
1280     g_assert_not_reached();
1281     return FALSE;
1282 }
1283
1284 void screen_install_colormap(ObClient *client, gboolean install)
1285 {
1286     if (client == NULL || client->colormap == None) {
1287         if (install)
1288             XInstallColormap(obt_display, RrColormap(ob_rr_inst));
1289         else
1290             XUninstallColormap(obt_display, RrColormap(ob_rr_inst));
1291     } else {
1292         obt_display_ignore_errors(TRUE);
1293         if (install)
1294             XInstallColormap(obt_display, client->colormap);
1295         else
1296             XUninstallColormap(obt_display, client->colormap);
1297         obt_display_ignore_errors(FALSE);
1298     }
1299 }
1300
1301 typedef struct {
1302     guint desktop;
1303     StrutPartial *strut;
1304 } ObScreenStrut;
1305
1306 #define RESET_STRUT_LIST(sl) \
1307     while (sl) { \
1308         g_slice_free(ObScreenStrut, (sl)->data); \
1309         sl = g_slist_delete_link(sl, sl); \
1310     }
1311
1312 #define ADD_STRUT_TO_LIST(sl, d, s) \
1313 { \
1314     ObScreenStrut *ss = g_slice_new(ObScreenStrut); \
1315     ss->desktop = d; \
1316     ss->strut = s;  \
1317     sl = g_slist_prepend(sl, ss); \
1318 }
1319
1320 #define VALIDATE_STRUTS(sl, side, max) \
1321 { \
1322     GSList *it; \
1323     for (it = sl; it; it = g_slist_next(it)) { \
1324       ObScreenStrut *ss = it->data; \
1325       ss->strut->side = MIN(max, ss->strut->side); \
1326     } \
1327 }
1328
1329 static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
1330 {
1331     guint i;
1332     gint l, r, t, b;
1333 #ifdef XINERAMA
1334     gint n;
1335     XineramaScreenInfo *info;
1336 #endif
1337
1338     if (ob_debug_xinerama) {
1339         gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1340         gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1341         *nxin = 2;
1342         *xin_areas = g_new(Rect, *nxin + 1);
1343         RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
1344         RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
1345     }
1346 #ifdef XINERAMA
1347     else if (obt_display_extension_xinerama &&
1348              (info = XineramaQueryScreens(obt_display, &n))) {
1349         *nxin = n;
1350         *xin_areas = g_new(Rect, *nxin + 1);
1351         for (i = 0; i < *nxin; ++i)
1352             RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
1353                      info[i].width, info[i].height);
1354         XFree(info);
1355     }
1356 #endif
1357     else {
1358         *nxin = 1;
1359         *xin_areas = g_new(Rect, *nxin + 1);
1360         RECT_SET((*xin_areas)[0], 0, 0,
1361                  WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1362                  HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1363     }
1364
1365     /* returns one extra with the total area in it */
1366     l = (*xin_areas)[0].x;
1367     t = (*xin_areas)[0].y;
1368     r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
1369     b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
1370     for (i = 1; i < *nxin; ++i) {
1371         l = MIN(l, (*xin_areas)[i].x);
1372         t = MIN(l, (*xin_areas)[i].y);
1373         r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
1374         b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
1375     }
1376     RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
1377
1378     for (i = 0; i < *nxin; ++i)
1379         ob_debug("Monitor %d @ %d,%d %dx%d\n", i,
1380                  (*xin_areas)[i].x, (*xin_areas)[i].y,
1381                  (*xin_areas)[i].width, (*xin_areas)[i].height);
1382     ob_debug("Full desktop @ %d,%d %dx%d\n",
1383              (*xin_areas)[i].x, (*xin_areas)[i].y,
1384              (*xin_areas)[i].width, (*xin_areas)[i].height);
1385 }
1386
1387 void screen_update_areas(void)
1388 {
1389     guint i;
1390     gulong *dims;
1391     GList *it, *onscreen;
1392
1393     /* collect the clients that are on screen */
1394     onscreen = NULL;
1395     for (it = client_list; it; it = g_list_next(it)) {
1396         if (client_monitor(it->data) != screen_num_monitors)
1397             onscreen = g_list_prepend(onscreen, it->data);
1398     }
1399
1400     g_free(monitor_area);
1401     get_xinerama_screens(&monitor_area, &screen_num_monitors);
1402
1403     /* set up the user-specified margins */
1404     config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1405     config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1406     config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1407     config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1408     config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
1409     config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1410     config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
1411     config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1412
1413     RESET_STRUT_LIST(struts_left);
1414     RESET_STRUT_LIST(struts_top);
1415     RESET_STRUT_LIST(struts_right);
1416     RESET_STRUT_LIST(struts_bottom);
1417
1418     /* collect the struts */
1419     for (it = client_list; it; it = g_list_next(it)) {
1420         ObClient *c = it->data;
1421         if (c->strut.left)
1422             ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
1423         if (c->strut.top)
1424             ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
1425         if (c->strut.right)
1426             ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
1427         if (c->strut.bottom)
1428             ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
1429     }
1430     if (dock_strut.left)
1431         ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
1432     if (dock_strut.top)
1433         ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
1434     if (dock_strut.right)
1435         ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
1436     if (dock_strut.bottom)
1437         ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
1438
1439     if (config_margins.left)
1440         ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
1441     if (config_margins.top)
1442         ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
1443     if (config_margins.right)
1444         ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
1445     if (config_margins.bottom)
1446         ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
1447
1448     VALIDATE_STRUTS(struts_left, left,
1449                     monitor_area[screen_num_monitors].width / 2);
1450     VALIDATE_STRUTS(struts_right, right,
1451                     monitor_area[screen_num_monitors].width / 2);
1452     VALIDATE_STRUTS(struts_top, top,
1453                     monitor_area[screen_num_monitors].height / 2);
1454     VALIDATE_STRUTS(struts_bottom, bottom,
1455                     monitor_area[screen_num_monitors].height / 2);
1456
1457     dims = g_new(gulong, 4 * screen_num_desktops);
1458     for (i = 0; i < screen_num_desktops; ++i) {
1459         Rect *area = screen_area(i, SCREEN_AREA_ALL_MONITORS, NULL);
1460         dims[i*4+0] = area->x;
1461         dims[i*4+1] = area->y;
1462         dims[i*4+2] = area->width;
1463         dims[i*4+3] = area->height;
1464         g_slice_free(Rect, area);
1465     }
1466
1467     /* set the legacy workarea hint to the union of all the monitors */
1468     OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL,
1469                     dims, 4 * screen_num_desktops);
1470
1471     /* the area has changed, adjust all the windows if they need it */
1472     for (it = onscreen; it; it = g_list_next(it))
1473         client_reconfigure(it->data, FALSE);
1474
1475     g_free(dims);
1476 }
1477
1478 #if 0
1479 Rect* screen_area_all_monitors(guint desktop)
1480 {
1481     guint i;
1482     Rect *a;
1483
1484     a = screen_area_monitor(desktop, 0);
1485
1486     /* combine all the monitors together */
1487     for (i = 1; i < screen_num_monitors; ++i) {
1488         Rect *m = screen_area_monitor(desktop, i);
1489         gint l, r, t, b;
1490
1491         l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
1492         t = MIN(RECT_TOP(*a), RECT_TOP(*m));
1493         r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
1494         b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
1495
1496         RECT_SET(*a, l, t, r - l + 1, b - t + 1);
1497
1498         g_free(m);
1499     }
1500
1501     return a;
1502 }
1503 #endif
1504
1505 #define STRUT_LEFT_IN_SEARCH(s, search) \
1506     (RANGES_INTERSECT(search->y, search->height, \
1507                       s->left_start, s->left_end - s->left_start + 1))
1508 #define STRUT_RIGHT_IN_SEARCH(s, search) \
1509     (RANGES_INTERSECT(search->y, search->height, \
1510                       s->right_start, s->right_end - s->right_start + 1))
1511 #define STRUT_TOP_IN_SEARCH(s, search) \
1512     (RANGES_INTERSECT(search->x, search->width, \
1513                       s->top_start, s->top_end - s->top_start + 1))
1514 #define STRUT_BOTTOM_IN_SEARCH(s, search) \
1515     (RANGES_INTERSECT(search->x, search->width, \
1516                       s->bottom_start, s->bottom_end - s->bottom_start + 1))
1517
1518 #define STRUT_LEFT_IGNORE(s, us, search) \
1519     (head == SCREEN_AREA_ALL_MONITORS && us && \
1520      RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
1521 #define STRUT_RIGHT_IGNORE(s, us, search) \
1522     (head == SCREEN_AREA_ALL_MONITORS && us && \
1523      RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
1524 #define STRUT_TOP_IGNORE(s, us, search) \
1525     (head == SCREEN_AREA_ALL_MONITORS && us && \
1526      RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
1527 #define STRUT_BOTTOM_IGNORE(s, us, search) \
1528     (head == SCREEN_AREA_ALL_MONITORS && us && \
1529      RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
1530
1531 Rect* screen_area(guint desktop, guint head, Rect *search)
1532 {
1533     Rect *a;
1534     GSList *it;
1535     gint l, r, t, b;
1536     guint i, d;
1537     gboolean us = search != NULL; /* user provided search */
1538
1539     g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
1540     g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
1541              head == SCREEN_AREA_ALL_MONITORS);
1542     g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
1543
1544     /* find any struts for this monitor
1545        which will be affecting the search area.
1546     */
1547
1548     /* search everything if search is null */
1549     if (!search) {
1550         if (head < screen_num_monitors) search = &monitor_area[head];
1551         else search = &monitor_area[screen_num_monitors];
1552     }
1553     if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
1554
1555     /* al is "all left" meaning the furthest left you can get, l is our
1556        "working left" meaning our current strut edge which we're calculating
1557     */
1558
1559     /* only include monitors which the search area lines up with */
1560     if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
1561         l = RECT_RIGHT(monitor_area[screen_num_monitors]);
1562         t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1563         r = RECT_LEFT(monitor_area[screen_num_monitors]);
1564         b = RECT_TOP(monitor_area[screen_num_monitors]);
1565         for (i = 0; i < screen_num_monitors; ++i) {
1566             /* add the monitor if applicable */
1567             if (RANGES_INTERSECT(search->x, search->width,
1568                                  monitor_area[i].x, monitor_area[i].width))
1569             {
1570                 t = MIN(t, RECT_TOP(monitor_area[i]));
1571                 b = MAX(b, RECT_BOTTOM(monitor_area[i]));
1572             }
1573             if (RANGES_INTERSECT(search->y, search->height,
1574                                  monitor_area[i].y, monitor_area[i].height))
1575             {
1576                 l = MIN(l, RECT_LEFT(monitor_area[i]));
1577                 r = MAX(r, RECT_RIGHT(monitor_area[i]));
1578             }
1579         }
1580     } else {
1581         l = RECT_LEFT(monitor_area[screen_num_monitors]);
1582         t = RECT_TOP(monitor_area[screen_num_monitors]);
1583         r = RECT_RIGHT(monitor_area[screen_num_monitors]);
1584         b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1585     }
1586
1587     for (d = 0; d < screen_num_desktops; ++d) {
1588         if (d != desktop && desktop != DESKTOP_ALL) continue;
1589
1590         for (i = 0; i < screen_num_monitors; ++i) {
1591             if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
1592
1593             for (it = struts_left; it; it = g_slist_next(it)) {
1594                 ObScreenStrut *s = it->data;
1595                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1596                     STRUT_LEFT_IN_SEARCH(s->strut, search) &&
1597                     !STRUT_LEFT_IGNORE(s->strut, us, search))
1598                     l = MAX(l, RECT_LEFT(monitor_area[screen_num_monitors])
1599                                + s->strut->left);
1600             }
1601             for (it = struts_top; it; it = g_slist_next(it)) {
1602                 ObScreenStrut *s = it->data;
1603                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1604                     STRUT_TOP_IN_SEARCH(s->strut, search) &&
1605                     !STRUT_TOP_IGNORE(s->strut, us, search))
1606                     t = MAX(t, RECT_TOP(monitor_area[screen_num_monitors])
1607                                + s->strut->top);
1608             }
1609             for (it = struts_right; it; it = g_slist_next(it)) {
1610                 ObScreenStrut *s = it->data;
1611                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1612                     STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
1613                     !STRUT_RIGHT_IGNORE(s->strut, us, search))
1614                     r = MIN(r, RECT_RIGHT(monitor_area[screen_num_monitors])
1615                                - s->strut->right);
1616             }
1617             for (it = struts_bottom; it; it = g_slist_next(it)) {
1618                 ObScreenStrut *s = it->data;
1619                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1620                     STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
1621                     !STRUT_BOTTOM_IGNORE(s->strut, us, search))
1622                     b = MIN(b, RECT_BOTTOM(monitor_area[screen_num_monitors])
1623                                - s->strut->bottom);
1624             }
1625
1626             /* limit to this monitor */
1627             if (head == i) {
1628                 l = MAX(l, RECT_LEFT(monitor_area[i]));
1629                 t = MAX(t, RECT_TOP(monitor_area[i]));
1630                 r = MIN(r, RECT_RIGHT(monitor_area[i]));
1631                 b = MIN(b, RECT_BOTTOM(monitor_area[i]));
1632             }
1633         }
1634     }
1635
1636     a = g_slice_new(Rect);
1637     a->x = l;
1638     a->y = t;
1639     a->width = r - l + 1;
1640     a->height = b - t + 1;
1641     return a;
1642 }
1643
1644 typedef struct {
1645     Rect r;
1646     gboolean subtract;
1647 } RectArithmetic;
1648
1649 guint screen_find_monitor(const Rect *search)
1650 {
1651     guint i;
1652     guint mostpx_index = screen_num_monitors;
1653     glong mostpx = 0;
1654     guint closest_distance_index = screen_num_monitors;
1655     guint closest_distance = G_MAXUINT;
1656     GSList *counted = NULL;
1657
1658     /* we want to count the number of pixels search has on each monitor, but not
1659        double count.  so if a pixel is counted on monitor A then we should not
1660        count it again on monitor B. in the end we want to return the monitor
1661        that had the most pixels counted under this scheme.
1662
1663        this assumes that monitors earlier in the list are more desirable to be
1664        considered the search area's monitor.  we try the configured primary
1665        monitor first, so it gets the highest preference.
1666
1667        if we have counted an area A, then we want to subtract the intersection
1668        of A with the area on future monitors.
1669        but now consider if we count an area B that intersects A. we want to
1670        subtract the area B from that counted on future monitors, but not
1671        subtract the intersection of A and B twice! so we would add the
1672        intersection of A and B back, to account for it being subtracted both
1673        for A and B.
1674
1675        this is the idea behind the algorithm.  we always subtract the full area
1676        for monitor M intersected with the search area. we'll call that AREA.
1677        but then we go through the list |counted| and for each rectangle in
1678        the list that is being subtracted from future monitors, we insert a
1679        request to add back the intersection of the subtracted rect with AREA.
1680        vice versa for a rect in |counted| that is getting added back.
1681     */
1682
1683     if (config_primary_monitor_index < screen_num_monitors) {
1684         const Rect *monitor;
1685         Rect on_current_monitor;
1686         glong area;
1687
1688         monitor = screen_physical_area_monitor(config_primary_monitor_index);
1689
1690         if (RECT_INTERSECTS_RECT(*monitor, *search)) {
1691             RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
1692             area = RECT_AREA(on_current_monitor);
1693
1694             if (area > mostpx) {
1695                 mostpx = area;
1696                 mostpx_index = config_primary_monitor_index;
1697             }
1698
1699             /* add the intersection rect on the current monitor to the
1700                counted list. that's easy for the first one, we just mark it for
1701                subtraction */
1702             {
1703                 RectArithmetic *ra = g_slice_new(RectArithmetic);
1704                 ra->r = on_current_monitor;
1705                 ra->subtract = TRUE;
1706                 counted = g_slist_prepend(counted, ra);
1707             }
1708         }
1709     }
1710
1711     for (i = 0; i < screen_num_monitors; ++i) {
1712         const Rect *monitor;
1713         Rect on_current_monitor;
1714         glong area;
1715         GSList *it;
1716
1717         monitor = screen_physical_area_monitor(i);
1718
1719         if (!RECT_INTERSECTS_RECT(*monitor, *search)) {
1720             /* If we don't intersect then find the distance between the search
1721                rect and the monitor. We'll use the closest monitor from this
1722                metric if none of the monitors intersect. */
1723             guint distance = rect_manhatten_distance(*monitor, *search);
1724
1725             if (distance < closest_distance) {
1726                 closest_distance = distance;
1727                 closest_distance_index = i;
1728             }
1729             continue;
1730         }
1731
1732         if (i == config_primary_monitor_index)
1733             continue;  /* already did this one */
1734
1735         RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
1736         area = RECT_AREA(on_current_monitor);
1737
1738         /* remove pixels we already counted on any previous monitors. */
1739         for (it = counted; it; it = g_slist_next(it)) {
1740             RectArithmetic *ra = it->data;
1741             Rect intersection;
1742
1743             RECT_SET_INTERSECTION(intersection, ra->r, *search);
1744             if (ra->subtract) area -= RECT_AREA(intersection);
1745             else area += RECT_AREA(intersection);
1746         }
1747
1748         if (area > mostpx) {
1749             mostpx = area;
1750             mostpx_index = i;
1751         }
1752
1753         /* add the intersection rect on the current monitor I to the counted
1754            list.
1755            but now we need to compensate for every rectangle R already in the
1756            counted list, and add a new rect R' that is the intersection of
1757            R and I, but with the reverse subtraction/addition operation.
1758         */
1759         for (it = counted; it; it = g_slist_next(it)) {
1760             RectArithmetic *saved = it->data;
1761
1762             if (!RECT_INTERSECTS_RECT(saved->r, on_current_monitor))
1763                 continue;
1764             /* we are going to subtract our rect from future monitors, but
1765                part of it may already be being subtracted/added, so compensate
1766                to not double add/subtract. */
1767             RectArithmetic *reverse = g_slice_new(RectArithmetic);
1768             RECT_SET_INTERSECTION(reverse->r, saved->r, on_current_monitor);
1769             reverse->subtract = !saved->subtract;
1770             /* prepend so we can continue thru the list uninterupted */
1771             counted = g_slist_prepend(counted, reverse);
1772         }
1773         {
1774             RectArithmetic *ra = g_slice_new(RectArithmetic);
1775             ra->r = on_current_monitor;
1776             ra->subtract = TRUE;
1777             counted = g_slist_prepend(counted, ra);
1778         }
1779     }
1780
1781     while (counted) {
1782         g_slice_free(RectArithmetic, counted->data);
1783         counted = g_slist_delete_link(counted, counted);
1784     }
1785
1786     if (mostpx_index < screen_num_monitors)
1787         return mostpx_index;
1788
1789     g_assert(closest_distance_index < screen_num_monitors);
1790     return closest_distance_index;
1791 }
1792
1793 const Rect* screen_physical_area_all_monitors(void)
1794 {
1795     return screen_physical_area_monitor(screen_num_monitors);
1796 }
1797
1798 const Rect* screen_physical_area_monitor(guint head)
1799 {
1800     g_assert(head <= screen_num_monitors);
1801
1802     return &monitor_area[head];
1803 }
1804
1805 gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
1806 {
1807     g_assert(head <= screen_num_monitors);
1808     g_assert(search);
1809     return RECT_INTERSECTS_RECT(monitor_area[head], *search);
1810 }
1811
1812 guint screen_monitor_active(void)
1813 {
1814     if (moveresize_client)
1815         return client_monitor(moveresize_client);
1816     else if (focus_client)
1817         return client_monitor(focus_client);
1818     else
1819         return screen_monitor_pointer();
1820 }
1821
1822 const Rect* screen_physical_area_active(void)
1823 {
1824     return screen_physical_area_monitor(screen_monitor_active());
1825 }
1826
1827 guint screen_monitor_primary(gboolean fixed)
1828 {
1829     if (config_primary_monitor_index > 0) {
1830         if (config_primary_monitor_index-1 < screen_num_monitors)
1831             return config_primary_monitor_index - 1;
1832         else
1833             return 0;
1834     }
1835     else if (fixed)
1836         return 0;
1837     else if (config_primary_monitor == OB_PLACE_MONITOR_ACTIVE)
1838         return screen_monitor_active();
1839     else /* config_primary_monitor == OB_PLACE_MONITOR_MOUSE */
1840         return screen_monitor_pointer();
1841 }
1842
1843 const Rect* screen_physical_area_primary(gboolean fixed)
1844 {
1845     return screen_physical_area_monitor(screen_monitor_primary(fixed));
1846 }
1847
1848 void screen_set_root_cursor(void)
1849 {
1850     if (sn_app_starting())
1851         XDefineCursor(obt_display, obt_root(ob_screen),
1852                       ob_cursor(OB_CURSOR_BUSYPOINTER));
1853     else
1854         XDefineCursor(obt_display, obt_root(ob_screen),
1855                       ob_cursor(OB_CURSOR_POINTER));
1856 }
1857
1858 guint screen_find_monitor_point(guint x, guint y)
1859 {
1860     Rect mon;
1861     RECT_SET(mon, x, y, 1, 1);
1862     return screen_find_monitor(&mon);
1863 }
1864
1865 guint screen_monitor_pointer()
1866 {
1867     gint x, y;
1868     if (!screen_pointer_pos(&x, &y))
1869         x = y = 0;
1870     return screen_find_monitor_point(x, y);
1871 }
1872
1873 gboolean screen_pointer_pos(gint *x, gint *y)
1874 {
1875     gint i;
1876     gboolean ret;
1877
1878     /* we don't care about any of these return values, but we can't pass NULL */
1879     Window w;
1880     guint u;
1881     gint j;
1882
1883     ret = !!XQueryPointer(obt_display, obt_root(ob_screen),
1884                           &w, &w, x, y, &j, &j, &u);
1885     if (!ret) {
1886         for (i = 0; i < ScreenCount(obt_display); ++i)
1887             if (i != ob_screen)
1888                 if ((ret=XQueryPointer(obt_display, obt_root(i),
1889                                   &w, &w, x, y, &j, &j, &u)))
1890                     break;
1891     }
1892     return ret;
1893 }
1894
1895 gboolean screen_compare_desktops(guint a, guint b)
1896 {
1897     if (a == DESKTOP_ALL)
1898         a = screen_desktop;
1899     if (b == DESKTOP_ALL)
1900         b = screen_desktop;
1901     return a == b;
1902 }
1903
1904 void screen_apply_gravity_point(gint *x, gint *y, gint width, gint height,
1905                                 const GravityPoint *position, const Rect *area)
1906 {
1907     if (position->x.center)
1908         *x = area->width / 2 - width / 2;
1909     else {
1910         *x = position->x.pos;
1911         if (position->x.denom)
1912             *x = (*x * area->width) / position->x.denom;
1913         if (position->x.opposite)
1914             *x = area->width - width - *x;
1915     }
1916
1917     if (position->y.center)
1918         *y = area->height / 2 - height / 2;
1919     else {
1920         *y = position->y.pos;
1921         if (position->y.denom)
1922             *y = (*y * area->height) / position->y.denom;
1923         if (position->y.opposite)
1924             *y = area->height - height - *y;
1925     }
1926
1927     *x += area->x;
1928     *y += area->y;
1929 }