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