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