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