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