]> 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 "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
365 static void desktop_popup_new()
366 {
367         guint i;
368         desktop_popup = g_new(ObPagerPopup*, screen_num_monitors);
369         for (i = 0; i < screen_num_monitors; i++) {
370             desktop_popup[i] = pager_popup_new();
371             desktop_popup[i]->popup->a_text->texture[0].data.text.font = ob_rr_theme->menu_title_font;
372             pager_popup_height(desktop_popup[i], POPUP_HEIGHT);
373
374             /* update the pager popup's width */
375             if (screen_desktop_names)
376                 pager_popup_text_width_to_strings(desktop_popup[i],
377                                                   screen_desktop_names,
378                                                   screen_num_desktops);
379         }
380
381 }
382
383 void screen_startup(gboolean reconfig)
384 {
385     gchar **names = NULL;
386     guint32 d;
387     gboolean namesexist = FALSE;
388
389     desktop_popup_perm = FALSE;
390
391     if (reconfig) {
392         desktop_popup_new();
393         return;
394     } else {
395         if (desktop_popup)
396             ob_debug("desktop_popup wasn't NULL when expected %x", desktop_popup);
397         desktop_popup = NULL;
398     }
399
400     /* get the initial size */
401     screen_resize();
402
403     /* have names already been set for the desktops? */
404     if (OBT_PROP_GETSS_UTF8(obt_root(ob_screen), NET_DESKTOP_NAMES, &names)) {
405         g_strfreev(names);
406         namesexist = TRUE;
407     }
408
409     /* if names don't exist and we have session names, set those.
410        do this stuff BEFORE setting the number of desktops, because that
411        will create default names for them
412     */
413     if (!namesexist && session_desktop_names != NULL) {
414         guint i, numnames;
415         GSList *it;
416
417         /* get the desktop names */
418         numnames = g_slist_length(session_desktop_names);
419         names = g_new(gchar*, numnames + 1);
420         names[numnames] = NULL;
421         for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
422             names[i] = g_strdup(it->data);
423
424         /* set the root window property */
425         OBT_PROP_SETSS(obt_root(ob_screen),
426                        NET_DESKTOP_NAMES, (const gchar*const*)names);
427
428         g_strfreev(names);
429     }
430
431     /* set the number of desktops, if it's not already set.
432
433        this will also set the default names from the config file up for
434        desktops that don't have names yet */
435     screen_num_desktops = 0;
436     if (OBT_PROP_GET32(obt_root(ob_screen),
437                        NET_NUMBER_OF_DESKTOPS, CARDINAL, &d))
438     {
439         if (d != config_desktops_num) {
440             /* TRANSLATORS: If you need to specify a different order of the
441                arguments, you can use %1$d for the first one and %2$d for the
442                second one. For example,
443                "The current session has %2$d desktops, but Openbox is configured for %1$d ..." */
444             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),
445                       config_desktops_num, d);
446         }
447         screen_set_num_desktops(d);
448     }
449     /* restore from session if possible */
450     else if (session_num_desktops)
451         screen_set_num_desktops(session_num_desktops);
452     else
453         screen_set_num_desktops(config_desktops_num);
454
455     screen_desktop = screen_num_desktops;  /* something invalid */
456     /* start on the current desktop when a wm was already running */
457     if (OBT_PROP_GET32(obt_root(ob_screen),
458                        NET_CURRENT_DESKTOP, CARDINAL, &d) &&
459         d < screen_num_desktops)
460     {
461         screen_set_desktop(d, FALSE);
462     } else if (session_desktop >= 0)
463         screen_set_desktop(MIN((guint)session_desktop,
464                                screen_num_desktops), FALSE);
465     else
466         screen_set_desktop(MIN(config_screen_firstdesk,
467                                screen_num_desktops) - 1, FALSE);
468     OBT_PROP_GET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, &screen_last_desktop);
469     if (screen_last_desktop < 0 || screen_last_desktop >= screen_num_desktops) {
470         screen_last_desktop = screen_desktop;
471         OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
472     }
473
474     /* don't start in showing-desktop mode */
475     screen_show_desktop_mode = SCREEN_SHOW_DESKTOP_NO;
476     OBT_PROP_SET32(obt_root(ob_screen),
477                    NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop());
478
479     if (session_desktop_layout_present &&
480         screen_validate_layout(&session_desktop_layout))
481     {
482         screen_desktop_layout = session_desktop_layout;
483     }
484     else
485         screen_update_layout();
486 }
487
488 static void desktop_popup_free(guint n)
489 {
490     guint i;
491     for (i = 0; i < n; i++) {
492         pager_popup_free(desktop_popup[i]);
493     }
494     g_free(desktop_popup);
495     desktop_popup = NULL;
496 }
497
498 void screen_shutdown(gboolean reconfig)
499 {
500     desktop_popup_free(screen_num_monitors);
501
502     if (reconfig)
503         return;
504
505     XSelectInput(obt_display, obt_root(ob_screen), NoEventMask);
506
507     /* we're not running here no more! */
508     OBT_PROP_ERASE(obt_root(ob_screen), OPENBOX_PID);
509     /* not without us */
510     OBT_PROP_ERASE(obt_root(ob_screen), NET_SUPPORTED);
511     /* don't keep this mode */
512     OBT_PROP_ERASE(obt_root(ob_screen), NET_SHOWING_DESKTOP);
513
514     XDestroyWindow(obt_display, screen_support_win);
515
516     g_strfreev(screen_desktop_names);
517     screen_desktop_names = NULL;
518 }
519
520 void screen_resize(void)
521 {
522     gint w, h;
523     GList *it;
524     gulong geometry[2];
525
526     w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
527     h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
528
529     /* Set the _NET_DESKTOP_GEOMETRY hint */
530     screen_physical_size.width = geometry[0] = w;
531     screen_physical_size.height = geometry[1] = h;
532     OBT_PROP_SETA32(obt_root(ob_screen),
533                     NET_DESKTOP_GEOMETRY, CARDINAL, geometry, 2);
534
535     if (ob_state() != OB_STATE_RUNNING)
536         return;
537
538     /* this calls screen_update_areas(), which we need ! */
539     dock_configure();
540     edges_configure();
541
542     // bug: this is done in screen_update_areas() already
543 //    for (it = client_list; it; it = g_list_next(it)) {
544 //        client_move_onscreen(it->data, FALSE);
545 //        client_reconfigure(it->data, FALSE);
546 //    }
547 }
548
549 void screen_set_num_desktops(guint num)
550 {
551     gulong *viewport;
552     GList *it, *stacking_copy;
553
554     g_assert(num > 0);
555
556     if (screen_num_desktops == num) return;
557
558     screen_num_desktops = num;
559     OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num);
560
561     /* set the viewport hint */
562     viewport = g_new0(gulong, num * 2);
563     OBT_PROP_SETA32(obt_root(ob_screen),
564                     NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2);
565     g_free(viewport);
566
567     /* the number of rows/columns will differ */
568     screen_update_layout();
569
570     /* move windows on desktops that will no longer exist!
571        make a copy of the list cuz we're changing it */
572     stacking_copy = g_list_copy(stacking_list);
573     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
574         if (WINDOW_IS_CLIENT(it->data)) {
575             ObClient *c = it->data;
576             if (c->desktop != DESKTOP_ALL && c->desktop >= num)
577                 client_set_desktop(c, num - 1, FALSE, TRUE);
578             /* raise all the windows that are on the current desktop which
579                is being merged */
580             else if (screen_desktop == num - 1 &&
581                      (c->desktop == DESKTOP_ALL ||
582                       c->desktop == screen_desktop))
583                 stacking_raise(CLIENT_AS_WINDOW(c));
584         }
585     }
586     g_list_free(stacking_copy);
587
588     /* change our struts/area to match (after moving windows) */
589     screen_update_areas();
590
591     /* may be some unnamed desktops that we need to fill in with names
592        (after updating the areas so the popup can resize) */
593     screen_update_desktop_names();
594
595     /* change our desktop if we're on one that no longer exists! */
596     if (screen_desktop >= screen_num_desktops)
597         screen_set_desktop(num - 1, TRUE);
598 }
599
600 static void screen_fallback_focus(void)
601 {
602     ObClient *c;
603     gboolean allow_omni;
604
605     /* only allow omnipresent windows to get focus on desktop change if
606        an omnipresent window is already focused (it'll keep focus probably, but
607        maybe not depending on mouse-focus options) */
608     allow_omni = focus_client && (client_normal(focus_client) &&
609                                   focus_client->desktop == DESKTOP_ALL);
610
611     /* the client moved there already so don't move focus. prevent flicker
612        on sendtodesktop + follow */
613     if (focus_client && focus_client->desktop == screen_desktop)
614         return;
615
616     /* have to try focus here because when you leave an empty desktop
617        there is no focus out to watch for. also, we have different rules
618        here. we always allow it to look under the mouse pointer if
619        config_focus_last is FALSE
620
621        do this before hiding the windows so if helper windows are coming
622        with us, they don't get hidden
623     */
624     if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
625                             !allow_omni)))
626     {
627         /* only do the flicker reducing stuff ahead of time if we are going
628            to call xsetinputfocus on the window ourselves. otherwise there is
629            no guarantee the window will actually take focus.. */
630         if (c->can_focus) {
631             /* reduce flicker by hiliting now rather than waiting for the
632                server FocusIn event */
633             frame_adjust_focus(c->frame, TRUE);
634             /* do this here so that if you switch desktops to a window with
635                helper windows then the helper windows won't flash */
636             client_bring_helper_windows(c);
637         }
638     }
639 }
640
641 static gboolean last_desktop_func(gpointer data)
642 {
643     screen_desktop_timeout = TRUE;
644     OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
645     screen_desktop_timer = 0;
646     return FALSE; /* don't repeat */
647 }
648
649 void screen_set_desktop(guint num, gboolean dofocus)
650 {
651     GList *it;
652     guint previous;
653     gulong ignore_start;
654
655     g_assert(num < screen_num_desktops);
656
657     previous = screen_desktop;
658     screen_desktop = num;
659
660     if (ob_state() == OB_STATE_RUNNING)
661         screen_show_desktop_popup(screen_desktop, FALSE);
662
663     if (previous == num) return;
664
665     OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num);
666
667     /* This whole thing decides when/how to save the screen_last_desktop so
668        that it can be restored later if you want */
669     if (screen_desktop_timeout) {
670         /* If screen_desktop_timeout is true, then we've been on this desktop
671            long enough and we can save it as the last desktop. */
672
673         if (screen_last_desktop == previous)
674             /* this is the startup state only */
675             screen_old_desktop = screen_desktop;
676         else {
677             /* save the "last desktop" as the "old desktop" */
678             screen_old_desktop = screen_last_desktop;
679             /* save the desktop we're coming from as the "last desktop" */
680             screen_last_desktop = previous;
681         }
682     }
683     else {
684         /* If screen_desktop_timeout is false, then we just got to this desktop
685            and we are moving away again. */
686
687         if (screen_desktop == screen_last_desktop) {
688             /* If we are moving to the "last desktop" .. */
689             if (previous == screen_old_desktop) {
690                 /* .. from the "old desktop", change the last desktop to
691                    be where we are coming from */
692                 screen_last_desktop = screen_old_desktop;
693             }
694             else if (screen_last_desktop == screen_old_desktop) {
695                 /* .. and also to the "old desktop", change the "last
696                    desktop" to be where we are coming from */
697                 screen_last_desktop = previous;
698             }
699             else {
700                 /* .. from some other desktop, then set the "last desktop" to
701                    be the saved "old desktop", i.e. where we were before the
702                    "last desktop" */
703                 screen_last_desktop = screen_old_desktop;
704             }
705         }
706         else {
707             /* If we are moving to any desktop besides the "last desktop"..
708                (this is the normal case) */
709             if (screen_desktop == screen_old_desktop) {
710                 /* If moving to the "old desktop", which is not the
711                    "last desktop", don't save anything */
712             }
713             else if (previous == screen_old_desktop) {
714                 /* If moving from the "old desktop", and not to the
715                    "last desktop", don't save anything */
716             }
717             else if (screen_last_desktop == screen_old_desktop) {
718                 /* If the "last desktop" is the same as "old desktop" and
719                    you're not moving to the "last desktop" then save where
720                    we're coming from as the "last desktop" */
721                 screen_last_desktop = previous;
722             }
723             else {
724                 /* If the "last desktop" is different from the "old desktop"
725                    and you're not moving to the "last desktop", then don't save
726                    anything */
727             }
728         }
729     }
730     screen_desktop_timeout = FALSE;
731     if (screen_desktop_timer) g_source_remove(screen_desktop_timer);
732     screen_desktop_timer = g_timeout_add(REMEMBER_LAST_DESKTOP_TIME,
733                                          last_desktop_func, NULL);
734
735     ob_debug("Moving to desktop %d", num+1);
736
737     /* ignore enter events caused by the move */
738     ignore_start = event_start_ignore_all_enters();
739
740     if (moveresize_client)
741         client_set_desktop(moveresize_client, num, TRUE, FALSE);
742
743     /* show windows before hiding the rest to lessen the enter/leave events */
744
745     /* show windows from top to bottom */
746     for (it = stacking_list; it; it = g_list_next(it)) {
747         if (WINDOW_IS_CLIENT(it->data)) {
748             ObClient *c = it->data;
749             client_show(c);
750         }
751     }
752
753     if (dofocus) screen_fallback_focus();
754
755     /* hide windows from bottom to top */
756     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
757         if (WINDOW_IS_CLIENT(it->data)) {
758             ObClient *c = it->data;
759             if (client_hide(c)) {
760                 if (c == focus_client) {
761                     /* c was focused and we didn't do fallback clearly so make
762                        sure openbox doesnt still consider the window focused.
763                        this happens when using NextWindow with allDesktops,
764                        since it doesnt want to move focus on desktop change,
765                        but the focus is not going to stay with the current
766                        window, which has now disappeared.
767                        only do this if the client was actually hidden,
768                        otherwise it can keep focus. */
769                     focus_set_client(NULL);
770                 }
771             }
772         }
773     }
774
775     focus_cycle_addremove(NULL, TRUE);
776
777     event_end_ignore_all_enters(ignore_start);
778
779     if (event_source_time() != CurrentTime)
780         screen_desktop_user_time = event_source_time();
781 }
782
783 void screen_add_desktop(gboolean current)
784 {
785     gulong ignore_start;
786
787     /* ignore enter events caused by this */
788     ignore_start = event_start_ignore_all_enters();
789
790     screen_set_num_desktops(screen_num_desktops+1);
791
792     /* move all the clients over */
793     if (current) {
794         GList *it;
795
796         for (it = client_list; it; it = g_list_next(it)) {
797             ObClient *c = it->data;
798             if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
799                 /* don't move direct children, they'll be moved with their
800                    parent - which will have to be on the same desktop */
801                 !client_direct_parent(c))
802             {
803                 ob_debug("moving window %s", c->title);
804                 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
805             }
806         }
807     }
808
809     event_end_ignore_all_enters(ignore_start);
810 }
811
812 void screen_remove_desktop(gboolean current)
813 {
814     guint rmdesktop, movedesktop;
815     GList *it, *stacking_copy;
816     gulong ignore_start;
817
818     if (screen_num_desktops <= 1) return;
819
820     /* ignore enter events caused by this */
821     ignore_start = event_start_ignore_all_enters();
822
823     /* what desktop are we removing and moving to? */
824     if (current)
825         rmdesktop = screen_desktop;
826     else
827         rmdesktop = screen_num_desktops - 1;
828     if (rmdesktop < screen_num_desktops - 1)
829         movedesktop = rmdesktop + 1;
830     else
831         movedesktop = rmdesktop;
832
833     /* make a copy of the list cuz we're changing it */
834     stacking_copy = g_list_copy(stacking_list);
835     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
836         if (WINDOW_IS_CLIENT(it->data)) {
837             ObClient *c = it->data;
838             guint d = c->desktop;
839             if (d != DESKTOP_ALL && d >= movedesktop &&
840                 /* don't move direct children, they'll be moved with their
841                    parent - which will have to be on the same desktop */
842                 !client_direct_parent(c))
843             {
844                 ob_debug("moving window %s", c->title);
845                 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
846             }
847             /* raise all the windows that are on the current desktop which
848                is being merged */
849             if ((screen_desktop == rmdesktop - 1 ||
850                  screen_desktop == rmdesktop) &&
851                 (d == DESKTOP_ALL || d == screen_desktop))
852             {
853                 stacking_raise(CLIENT_AS_WINDOW(c));
854                 ob_debug("raising window %s", c->title);
855             }
856         }
857     }
858     g_list_free(stacking_copy);
859
860     /* fallback focus like we're changing desktops */
861     if (screen_desktop < screen_num_desktops - 1) {
862         screen_fallback_focus();
863         ob_debug("fake desktop change");
864     }
865
866     screen_set_num_desktops(screen_num_desktops-1);
867
868     event_end_ignore_all_enters(ignore_start);
869 }
870
871 static void get_row_col(guint d, guint *r, guint *c)
872 {
873     switch (screen_desktop_layout.orientation) {
874     case OB_ORIENTATION_HORZ:
875         switch (screen_desktop_layout.start_corner) {
876         case OB_CORNER_TOPLEFT:
877             *r = d / screen_desktop_layout.columns;
878             *c = d % screen_desktop_layout.columns;
879             break;
880         case OB_CORNER_BOTTOMLEFT:
881             *r = screen_desktop_layout.rows - 1 -
882                 d / screen_desktop_layout.columns;
883             *c = d % screen_desktop_layout.columns;
884             break;
885         case OB_CORNER_TOPRIGHT:
886             *r = d / screen_desktop_layout.columns;
887             *c = screen_desktop_layout.columns - 1 -
888                 d % screen_desktop_layout.columns;
889             break;
890         case OB_CORNER_BOTTOMRIGHT:
891             *r = screen_desktop_layout.rows - 1 -
892                 d / screen_desktop_layout.columns;
893             *c = screen_desktop_layout.columns - 1 -
894                 d % screen_desktop_layout.columns;
895             break;
896         }
897         break;
898     case OB_ORIENTATION_VERT:
899         switch (screen_desktop_layout.start_corner) {
900         case OB_CORNER_TOPLEFT:
901             *r = d % screen_desktop_layout.rows;
902             *c = d / screen_desktop_layout.rows;
903             break;
904         case OB_CORNER_BOTTOMLEFT:
905             *r = screen_desktop_layout.rows - 1 -
906                 d % screen_desktop_layout.rows;
907             *c = d / screen_desktop_layout.rows;
908             break;
909         case OB_CORNER_TOPRIGHT:
910             *r = d % screen_desktop_layout.rows;
911             *c = screen_desktop_layout.columns - 1 -
912                 d / screen_desktop_layout.rows;
913             break;
914         case OB_CORNER_BOTTOMRIGHT:
915             *r = screen_desktop_layout.rows - 1 -
916                 d % screen_desktop_layout.rows;
917             *c = screen_desktop_layout.columns - 1 -
918                 d / screen_desktop_layout.rows;
919             break;
920         }
921         break;
922     }
923 }
924
925 static guint translate_row_col(guint r, guint c)
926 {
927     switch (screen_desktop_layout.orientation) {
928     case OB_ORIENTATION_HORZ:
929         switch (screen_desktop_layout.start_corner) {
930         case OB_CORNER_TOPLEFT:
931             return r % screen_desktop_layout.rows *
932                 screen_desktop_layout.columns +
933                 c % screen_desktop_layout.columns;
934         case OB_CORNER_BOTTOMLEFT:
935             return (screen_desktop_layout.rows - 1 -
936                     r % screen_desktop_layout.rows) *
937                 screen_desktop_layout.columns +
938                 c % screen_desktop_layout.columns;
939         case OB_CORNER_TOPRIGHT:
940             return r % screen_desktop_layout.rows *
941                 screen_desktop_layout.columns +
942                 (screen_desktop_layout.columns - 1 -
943                  c % screen_desktop_layout.columns);
944         case OB_CORNER_BOTTOMRIGHT:
945             return (screen_desktop_layout.rows - 1 -
946                     r % screen_desktop_layout.rows) *
947                 screen_desktop_layout.columns +
948                 (screen_desktop_layout.columns - 1 -
949                  c % screen_desktop_layout.columns);
950         }
951     case OB_ORIENTATION_VERT:
952         switch (screen_desktop_layout.start_corner) {
953         case OB_CORNER_TOPLEFT:
954             return c % screen_desktop_layout.columns *
955                 screen_desktop_layout.rows +
956                 r % screen_desktop_layout.rows;
957         case OB_CORNER_BOTTOMLEFT:
958             return c % screen_desktop_layout.columns *
959                 screen_desktop_layout.rows +
960                 (screen_desktop_layout.rows - 1 -
961                  r % screen_desktop_layout.rows);
962         case OB_CORNER_TOPRIGHT:
963             return (screen_desktop_layout.columns - 1 -
964                     c % screen_desktop_layout.columns) *
965                 screen_desktop_layout.rows +
966                 r % screen_desktop_layout.rows;
967         case OB_CORNER_BOTTOMRIGHT:
968             return (screen_desktop_layout.columns - 1 -
969                     c % screen_desktop_layout.columns) *
970                 screen_desktop_layout.rows +
971                 (screen_desktop_layout.rows - 1 -
972                  r % screen_desktop_layout.rows);
973         }
974     }
975     g_assert_not_reached();
976     return 0;
977 }
978
979 static gboolean hide_desktop_popup_func(gpointer data)
980 {
981     guint i;
982
983     desktop_popup_timer = 0;
984
985     for (i = 0; i < screen_num_monitors; i++) {
986         pager_popup_hide(desktop_popup[i]);
987     }
988     return FALSE; /* don't repeat */
989 }
990
991 void screen_show_desktop_popup(guint d, gboolean perm)
992 {
993     const Rect *a;
994     guint i;
995
996     /* 0 means don't show the popup */
997     if (!config_desktop_popup_time) return;
998
999     for (i = 0; i < screen_num_monitors; i++) {
1000         a = screen_physical_area_monitor(i);
1001         pager_popup_position(desktop_popup[i], CenterGravity,
1002                              a->x + a->width / 2, a->y + a->height / 2);
1003         pager_popup_icon_size_multiplier(desktop_popup[i],
1004                                          (screen_desktop_layout.columns /
1005                                           screen_desktop_layout.rows) / 2,
1006                                          (screen_desktop_layout.rows/
1007                                           screen_desktop_layout.columns) / 2);
1008         pager_popup_max_width(desktop_popup[i],
1009                               MAX(a->width/3, POPUP_WIDTH));
1010         pager_popup_show(desktop_popup[i], screen_desktop_names[d], d);
1011     }
1012     if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
1013     desktop_popup_timer = 0;
1014     if (!perm && !desktop_popup_perm)
1015         /* only hide if its not already being show permanently */
1016         desktop_popup_timer = g_timeout_add(config_desktop_popup_time,
1017                                             hide_desktop_popup_func,
1018                                             desktop_popup);
1019     if (perm)
1020         desktop_popup_perm = TRUE;
1021 }
1022
1023 void screen_hide_desktop_popup(void)
1024 {
1025     if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
1026     desktop_popup_timer = 0;
1027     desktop_popup_perm = FALSE;
1028     guint i;
1029
1030     for (i = 0; i < screen_num_monitors; i++) {
1031         pager_popup_hide(desktop_popup[i]);
1032     }
1033 }
1034
1035 guint screen_find_desktop(guint from, ObDirection dir,
1036                           gboolean wrap, gboolean linear)
1037 {
1038     guint r, c;
1039     guint d;
1040
1041     d = from;
1042     get_row_col(d, &r, &c);
1043     if (linear) {
1044         switch (dir) {
1045         case OB_DIRECTION_EAST:
1046             if (d < screen_num_desktops - 1)
1047                 ++d;
1048             else if (wrap)
1049                 d = 0;
1050             else
1051                 return from;
1052             break;
1053         case OB_DIRECTION_WEST:
1054             if (d > 0)
1055                 --d;
1056             else if (wrap)
1057                 d = screen_num_desktops - 1;
1058             else
1059                 return from;
1060             break;
1061         default:
1062             g_assert_not_reached();
1063             return from;
1064         }
1065     } else {
1066         switch (dir) {
1067         case OB_DIRECTION_EAST:
1068             ++c;
1069             if (c >= screen_desktop_layout.columns) {
1070                 if (wrap)
1071                     c = 0;
1072                 else
1073                     return from;
1074             }
1075             d = translate_row_col(r, c);
1076             if (d >= screen_num_desktops) {
1077                 if (wrap)
1078                     ++c;
1079                 else
1080                     return from;
1081             }
1082             break;
1083         case OB_DIRECTION_WEST:
1084             --c;
1085             if (c >= screen_desktop_layout.columns) {
1086                 if (wrap)
1087                     c = screen_desktop_layout.columns - 1;
1088                 else
1089                     return from;
1090             }
1091             d = translate_row_col(r, c);
1092             if (d >= screen_num_desktops) {
1093                 if (wrap)
1094                     --c;
1095                 else
1096                     return from;
1097             }
1098             break;
1099         case OB_DIRECTION_SOUTH:
1100             ++r;
1101             if (r >= screen_desktop_layout.rows) {
1102                 if (wrap)
1103                     r = 0;
1104                 else
1105                     return from;
1106             }
1107             d = translate_row_col(r, c);
1108             if (d >= screen_num_desktops) {
1109                 if (wrap)
1110                     ++r;
1111                 else
1112                     return from;
1113             }
1114             break;
1115         case OB_DIRECTION_NORTH:
1116             --r;
1117             if (r >= screen_desktop_layout.rows) {
1118                 if (wrap)
1119                     r = screen_desktop_layout.rows - 1;
1120                 else
1121                     return from;
1122             }
1123             d = translate_row_col(r, c);
1124             if (d >= screen_num_desktops) {
1125                 if (wrap)
1126                     --r;
1127                 else
1128                     return from;
1129             }
1130             break;
1131         default:
1132             g_assert_not_reached();
1133             return from;
1134         }
1135
1136         d = translate_row_col(r, c);
1137     }
1138     return d;
1139 }
1140
1141 static gboolean screen_validate_layout(ObDesktopLayout *l)
1142 {
1143     if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
1144         return FALSE;
1145
1146     /* fill in a zero rows/columns */
1147     if (l->columns == 0) {
1148         l->columns = screen_num_desktops / l->rows;
1149         if (l->rows * l->columns < screen_num_desktops)
1150             l->columns++;
1151         if (l->rows * l->columns >= screen_num_desktops + l->columns)
1152             l->rows--;
1153     } else if (l->rows == 0) {
1154         l->rows = screen_num_desktops / l->columns;
1155         if (l->columns * l->rows < screen_num_desktops)
1156             l->rows++;
1157         if (l->columns * l->rows >= screen_num_desktops + l->rows)
1158             l->columns--;
1159     }
1160
1161     /* bounds checking */
1162     if (l->orientation == OB_ORIENTATION_HORZ) {
1163         l->columns = MIN(screen_num_desktops, l->columns);
1164         l->rows = MIN(l->rows,
1165                       (screen_num_desktops + l->columns - 1) / l->columns);
1166         l->columns = screen_num_desktops / l->rows +
1167             !!(screen_num_desktops % l->rows);
1168     } else {
1169         l->rows = MIN(screen_num_desktops, l->rows);
1170         l->columns = MIN(l->columns,
1171                          (screen_num_desktops + l->rows - 1) / l->rows);
1172         l->rows = screen_num_desktops / l->columns +
1173             !!(screen_num_desktops % l->columns);
1174     }
1175     return TRUE;
1176 }
1177
1178 void screen_update_layout(void)
1179
1180 {
1181     ObDesktopLayout l;
1182     guint32 *data;
1183     guint num;
1184
1185     screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
1186     screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
1187     screen_desktop_layout.rows = 1;
1188     screen_desktop_layout.columns = screen_num_desktops;
1189
1190     if (OBT_PROP_GETA32(obt_root(ob_screen),
1191                         NET_DESKTOP_LAYOUT, CARDINAL, &data, &num)) {
1192         if (num == 3 || num == 4) {
1193
1194             if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_VERT))
1195                 l.orientation = OB_ORIENTATION_VERT;
1196             else if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_HORZ))
1197                 l.orientation = OB_ORIENTATION_HORZ;
1198             else
1199                 return;
1200
1201             if (num < 4)
1202                 l.start_corner = OB_CORNER_TOPLEFT;
1203             else {
1204                 if (data[3] == OBT_PROP_ATOM(NET_WM_TOPLEFT))
1205                     l.start_corner = OB_CORNER_TOPLEFT;
1206                 else if (data[3] == OBT_PROP_ATOM(NET_WM_TOPRIGHT))
1207                     l.start_corner = OB_CORNER_TOPRIGHT;
1208                 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMRIGHT))
1209                     l.start_corner = OB_CORNER_BOTTOMRIGHT;
1210                 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMLEFT))
1211                     l.start_corner = OB_CORNER_BOTTOMLEFT;
1212                 else
1213                     return;
1214             }
1215
1216             l.columns = data[1];
1217             l.rows = data[2];
1218
1219             if (screen_validate_layout(&l))
1220                 screen_desktop_layout = l;
1221
1222             g_free(data);
1223         }
1224     }
1225 }
1226
1227 void screen_update_desktop_names(void)
1228 {
1229     guint i;
1230
1231     /* empty the array */
1232     g_strfreev(screen_desktop_names);
1233     screen_desktop_names = NULL;
1234
1235     if (OBT_PROP_GETSS(obt_root(ob_screen),
1236                        NET_DESKTOP_NAMES, &screen_desktop_names))
1237         for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
1238     else
1239         i = 0;
1240     if (i < screen_num_desktops) {
1241         GSList *it;
1242
1243         screen_desktop_names = g_renew(gchar*, screen_desktop_names,
1244                                        screen_num_desktops + 1);
1245         screen_desktop_names[screen_num_desktops] = NULL;
1246
1247         it = g_slist_nth(config_desktops_names, i);
1248
1249         for (; i < screen_num_desktops; ++i) {
1250             if (it && ((char*)it->data)[0]) /* not empty */
1251                 /* use the names from the config file when possible */
1252                 screen_desktop_names[i] = g_strdup(it->data);
1253             else
1254                 /* make up a nice name if it's not though */
1255                 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
1256                                                           i + 1);
1257             if (it) it = g_slist_next(it);
1258         }
1259
1260         /* if we changed any names, then set the root property so we can
1261            all agree on the names */
1262         OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
1263                        (const gchar*const*)screen_desktop_names);
1264     }
1265
1266     /* resize the pager for these names */
1267     for (i = 0; i < screen_num_monitors; i++) {
1268         pager_popup_text_width_to_strings(desktop_popup[i],
1269                                           screen_desktop_names,
1270                                           screen_num_desktops);
1271     }
1272 }
1273
1274 void screen_show_desktop(ObScreenShowDestopMode show_mode, ObClient *show_only)
1275 {
1276     GList *it;
1277
1278     ObScreenShowDestopMode before_mode = screen_show_desktop_mode;
1279
1280     gboolean showing_before = screen_showing_desktop();
1281     screen_show_desktop_mode = show_mode;
1282     gboolean showing_after = screen_showing_desktop();
1283
1284     if (showing_before == showing_after) {
1285         /* No change. */
1286         screen_show_desktop_mode = before_mode;
1287         return;
1288     }
1289
1290     if (screen_show_desktop_mode == SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE &&
1291         show_only != NULL)
1292     {
1293         /* If we're showing the desktop until the show-mode is toggled, we
1294            don't allow breaking out of showing-desktop mode unless we're
1295            showing all the windows again. */
1296         screen_show_desktop_mode = before_mode;
1297         return;
1298     }
1299
1300     if (showing_after) {
1301         /* hide windows bottom to top */
1302         for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1303             if (WINDOW_IS_CLIENT(it->data)) {
1304                 ObClient *client = it->data;
1305                 client_showhide(client);
1306             }
1307         }
1308     }
1309     else {
1310         /* restore windows top to bottom */
1311         for (it = stacking_list; it; it = g_list_next(it)) {
1312             if (WINDOW_IS_CLIENT(it->data)) {
1313                 ObClient *client = it->data;
1314                 if (client_should_show(client)) {
1315                     if (!show_only || client == show_only)
1316                         client_show(client);
1317                     else
1318                         client_iconify(client, TRUE, FALSE, TRUE);
1319                 }
1320             }
1321         }
1322     }
1323
1324     if (showing_after) {
1325         /* focus the desktop */
1326         for (it = focus_order; it; it = g_list_next(it)) {
1327             ObClient *c = it->data;
1328             if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1329                 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1330                 client_focus(it->data))
1331                 break;
1332         }
1333     }
1334     else if (!show_only) {
1335         ObClient *c;
1336
1337         if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
1338             /* only do the flicker reducing stuff ahead of time if we are going
1339                to call xsetinputfocus on the window ourselves. otherwise there
1340                is no guarantee the window will actually take focus.. */
1341             if (c->can_focus) {
1342                 /* reduce flicker by hiliting now rather than waiting for the
1343                    server FocusIn event */
1344                 frame_adjust_focus(c->frame, TRUE);
1345             }
1346         }
1347     }
1348
1349     OBT_PROP_SET32(obt_root(ob_screen),
1350                    NET_SHOWING_DESKTOP,
1351                    CARDINAL,
1352                    !!showing_after);
1353 }
1354
1355 gboolean screen_showing_desktop()
1356 {
1357     switch (screen_show_desktop_mode) {
1358     case SCREEN_SHOW_DESKTOP_NO:
1359         return FALSE;
1360     case SCREEN_SHOW_DESKTOP_UNTIL_WINDOW:
1361     case SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE:
1362         return TRUE;
1363     }
1364     g_assert_not_reached();
1365     return FALSE;
1366 }
1367
1368 void screen_install_colormap(ObClient *client, gboolean install)
1369 {
1370     if (client == NULL || client->colormap == None) {
1371         if (install)
1372             XInstallColormap(obt_display, RrColormap(ob_rr_inst));
1373         else
1374             XUninstallColormap(obt_display, RrColormap(ob_rr_inst));
1375     } else {
1376         obt_display_ignore_errors(TRUE);
1377         if (install)
1378             XInstallColormap(obt_display, client->colormap);
1379         else
1380             XUninstallColormap(obt_display, client->colormap);
1381         obt_display_ignore_errors(FALSE);
1382     }
1383 }
1384
1385 typedef struct {
1386     guint desktop;
1387     StrutPartial *strut;
1388 } ObScreenStrut;
1389
1390 #define RESET_STRUT_LIST(sl) \
1391     while (sl) { \
1392         g_slice_free(ObScreenStrut, (sl)->data); \
1393         sl = g_slist_delete_link(sl, sl); \
1394     }
1395
1396 #define ADD_STRUT_TO_LIST(sl, d, s) \
1397 { \
1398     ObScreenStrut *ss = g_slice_new(ObScreenStrut); \
1399     ss->desktop = d; \
1400     ss->strut = s;  \
1401     sl = g_slist_prepend(sl, ss); \
1402 }
1403
1404 #define VALIDATE_STRUTS(sl, side, max) \
1405 { \
1406     GSList *it; \
1407     for (it = sl; it; it = g_slist_next(it)) { \
1408       ObScreenStrut *ss = it->data; \
1409       ss->strut->side = MIN(max, ss->strut->side); \
1410     } \
1411 }
1412
1413 static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
1414 {
1415     guint i;
1416     gint l, r, t, b;
1417 #ifdef XINERAMA
1418     gint n;
1419     XineramaScreenInfo *info;
1420 #endif
1421
1422     if (ob_debug_xinerama) {
1423         gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1424         gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1425         *nxin = 2;
1426         *xin_areas = g_new(Rect, *nxin + 1);
1427         RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
1428         RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
1429     } else if (config_emulate_xinerama) {
1430     *nxin = 2;
1431     *xin_areas = g_new(Rect, *nxin + 1);
1432     RECT_SET((*xin_areas)[0], 0, 0,
1433                  WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)) / 2,
1434                  HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1435     RECT_SET((*xin_areas)[1],
1436                  WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)) / 2,
1437                  0,
1438                  WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)) / 2,
1439                  HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1440     RECT_SET((*xin_areas)[*nxin], 0, 0,
1441                  WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1442                  HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1443     }
1444 #ifdef XINERAMA
1445     else if (obt_display_extension_xinerama &&
1446              (info = XineramaQueryScreens(obt_display, &n))) {
1447         *nxin = n;
1448         *xin_areas = g_new(Rect, *nxin + 1);
1449         for (i = 0; i < *nxin; ++i)
1450             RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
1451                      info[i].width, info[i].height);
1452         XFree(info);
1453     }
1454 #endif
1455     else {
1456         *nxin = 1;
1457         *xin_areas = g_new(Rect, *nxin + 1);
1458         RECT_SET((*xin_areas)[0], 0, 0,
1459                  WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1460                  HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1461     }
1462
1463     /* returns one extra with the total area in it */
1464     l = (*xin_areas)[0].x;
1465     t = (*xin_areas)[0].y;
1466     r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
1467     b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
1468     for (i = 1; i < *nxin; ++i) {
1469         l = MIN(l, (*xin_areas)[i].x);
1470         t = MIN(l, (*xin_areas)[i].y);
1471         r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
1472         b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
1473     }
1474     RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
1475
1476     for (i = 0; i < *nxin; ++i)
1477         ob_debug("Monitor %d @ %d,%d %dx%d\n", i,
1478                  (*xin_areas)[i].x, (*xin_areas)[i].y,
1479                  (*xin_areas)[i].width, (*xin_areas)[i].height);
1480     ob_debug("Full desktop @ %d,%d %dx%d\n",
1481              (*xin_areas)[i].x, (*xin_areas)[i].y,
1482              (*xin_areas)[i].width, (*xin_areas)[i].height);
1483 }
1484
1485 void screen_update_areas(void)
1486 {
1487     guint i, old_num_monitors = screen_num_monitors;
1488     gulong *dims;
1489     GList *it, *onscreen;
1490
1491     /* collect the clients that are on screen */
1492     onscreen = NULL;
1493     for (it = client_list; it; it = g_list_next(it)) {
1494         if (client_monitor(it->data) != screen_num_monitors)
1495             onscreen = g_list_prepend(onscreen, it->data);
1496     }
1497
1498     g_free(monitor_area);
1499     get_xinerama_screens(&monitor_area, &screen_num_monitors);
1500     if (screen_num_monitors != old_num_monitors) {
1501         if (desktop_popup)
1502             desktop_popup_free(old_num_monitors);
1503         desktop_popup_new();
1504     }
1505
1506     /* set up the user-specified margins */
1507     config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1508     config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1509     config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1510     config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1511     config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
1512     config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1513     config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
1514     config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1515
1516     RESET_STRUT_LIST(struts_left);
1517     RESET_STRUT_LIST(struts_top);
1518     RESET_STRUT_LIST(struts_right);
1519     RESET_STRUT_LIST(struts_bottom);
1520
1521     /* collect the struts */
1522     for (it = client_list; it; it = g_list_next(it)) {
1523         ObClient *c = it->data;
1524         if (c->strut.left)
1525             ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
1526         if (c->strut.top)
1527             ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
1528         if (c->strut.right)
1529             ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
1530         if (c->strut.bottom)
1531             ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
1532     }
1533     if (dock_strut.left)
1534         ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
1535     if (dock_strut.top)
1536         ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
1537     if (dock_strut.right)
1538         ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
1539     if (dock_strut.bottom)
1540         ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
1541
1542     if (config_margins.left)
1543         ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
1544     if (config_margins.top)
1545         ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
1546     if (config_margins.right)
1547         ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
1548     if (config_margins.bottom)
1549         ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
1550
1551     VALIDATE_STRUTS(struts_left, left,
1552                     monitor_area[screen_num_monitors].width / 2);
1553     VALIDATE_STRUTS(struts_right, right,
1554                     monitor_area[screen_num_monitors].width / 2);
1555     VALIDATE_STRUTS(struts_top, top,
1556                     monitor_area[screen_num_monitors].height / 2);
1557     VALIDATE_STRUTS(struts_bottom, bottom,
1558                     monitor_area[screen_num_monitors].height / 2);
1559
1560     dims = g_new(gulong, 4 * screen_num_desktops);
1561     for (i = 0; i < screen_num_desktops; ++i) {
1562         Rect *area = screen_area(i, SCREEN_AREA_ALL_MONITORS, NULL);
1563         dims[i*4+0] = area->x;
1564         dims[i*4+1] = area->y;
1565         dims[i*4+2] = area->width;
1566         dims[i*4+3] = area->height;
1567         g_slice_free(Rect, area);
1568     }
1569
1570     /* set the legacy workarea hint to the union of all the monitors */
1571     OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL,
1572                     dims, 4 * screen_num_desktops);
1573
1574     /* the area has changed, adjust all the windows if they need it */
1575     for (it = onscreen; it; it = g_list_next(it))
1576         client_reconfigure(it->data, FALSE);
1577
1578     g_free(dims);
1579 }
1580
1581 #if 0
1582 Rect* screen_area_all_monitors(guint desktop)
1583 {
1584     guint i;
1585     Rect *a;
1586
1587     a = screen_area_monitor(desktop, 0);
1588
1589     /* combine all the monitors together */
1590     for (i = 1; i < screen_num_monitors; ++i) {
1591         Rect *m = screen_area_monitor(desktop, i);
1592         gint l, r, t, b;
1593
1594         l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
1595         t = MIN(RECT_TOP(*a), RECT_TOP(*m));
1596         r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
1597         b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
1598
1599         RECT_SET(*a, l, t, r - l + 1, b - t + 1);
1600
1601         g_free(m);
1602     }
1603
1604     return a;
1605 }
1606 #endif
1607
1608 #define STRUT_LEFT_IN_SEARCH(s, search) \
1609     (RANGES_INTERSECT(search->y, search->height, \
1610                       s->left_start, s->left_end - s->left_start + 1))
1611 #define STRUT_RIGHT_IN_SEARCH(s, search) \
1612     (RANGES_INTERSECT(search->y, search->height, \
1613                       s->right_start, s->right_end - s->right_start + 1))
1614 #define STRUT_TOP_IN_SEARCH(s, search) \
1615     (RANGES_INTERSECT(search->x, search->width, \
1616                       s->top_start, s->top_end - s->top_start + 1))
1617 #define STRUT_BOTTOM_IN_SEARCH(s, search) \
1618     (RANGES_INTERSECT(search->x, search->width, \
1619                       s->bottom_start, s->bottom_end - s->bottom_start + 1))
1620
1621 #define STRUT_LEFT_IGNORE(s, us, search) \
1622     (head == SCREEN_AREA_ALL_MONITORS && us && \
1623      RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
1624 #define STRUT_RIGHT_IGNORE(s, us, search) \
1625     (head == SCREEN_AREA_ALL_MONITORS && us && \
1626      RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
1627 #define STRUT_TOP_IGNORE(s, us, search) \
1628     (head == SCREEN_AREA_ALL_MONITORS && us && \
1629      RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
1630 #define STRUT_BOTTOM_IGNORE(s, us, search) \
1631     (head == SCREEN_AREA_ALL_MONITORS && us && \
1632      RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
1633
1634 Rect* screen_area(guint desktop, guint head, Rect *search)
1635 {
1636     Rect *a;
1637     GSList *it;
1638     gint l, r, t, b;
1639     guint i, d;
1640     gboolean us = search != NULL; /* user provided search */
1641
1642     g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
1643     g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
1644              head == SCREEN_AREA_ALL_MONITORS);
1645     g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
1646
1647     /* find any struts for this monitor
1648        which will be affecting the search area.
1649     */
1650
1651     /* search everything if search is null */
1652     if (!search) {
1653         if (head < screen_num_monitors) search = &monitor_area[head];
1654         else search = &monitor_area[screen_num_monitors];
1655     }
1656     if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
1657
1658     /* al is "all left" meaning the furthest left you can get, l is our
1659        "working left" meaning our current strut edge which we're calculating
1660     */
1661
1662     /* only include monitors which the search area lines up with */
1663     if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
1664         l = RECT_RIGHT(monitor_area[screen_num_monitors]);
1665         t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1666         r = RECT_LEFT(monitor_area[screen_num_monitors]);
1667         b = RECT_TOP(monitor_area[screen_num_monitors]);
1668         for (i = 0; i < screen_num_monitors; ++i) {
1669             /* add the monitor if applicable */
1670             if (RANGES_INTERSECT(search->x, search->width,
1671                                  monitor_area[i].x, monitor_area[i].width))
1672             {
1673                 t = MIN(t, RECT_TOP(monitor_area[i]));
1674                 b = MAX(b, RECT_BOTTOM(monitor_area[i]));
1675             }
1676             if (RANGES_INTERSECT(search->y, search->height,
1677                                  monitor_area[i].y, monitor_area[i].height))
1678             {
1679                 l = MIN(l, RECT_LEFT(monitor_area[i]));
1680                 r = MAX(r, RECT_RIGHT(monitor_area[i]));
1681             }
1682         }
1683     } else {
1684         l = RECT_LEFT(monitor_area[screen_num_monitors]);
1685         t = RECT_TOP(monitor_area[screen_num_monitors]);
1686         r = RECT_RIGHT(monitor_area[screen_num_monitors]);
1687         b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1688     }
1689
1690     for (d = 0; d < screen_num_desktops; ++d) {
1691         if (d != desktop && desktop != DESKTOP_ALL) continue;
1692
1693         for (i = 0; i < screen_num_monitors; ++i) {
1694             if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
1695
1696             for (it = struts_left; it; it = g_slist_next(it)) {
1697                 ObScreenStrut *s = it->data;
1698                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1699                     STRUT_LEFT_IN_SEARCH(s->strut, search) &&
1700                     !STRUT_LEFT_IGNORE(s->strut, us, search))
1701                     l = MAX(l, RECT_LEFT(monitor_area[screen_num_monitors])
1702                                + s->strut->left);
1703             }
1704             for (it = struts_top; it; it = g_slist_next(it)) {
1705                 ObScreenStrut *s = it->data;
1706                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1707                     STRUT_TOP_IN_SEARCH(s->strut, search) &&
1708                     !STRUT_TOP_IGNORE(s->strut, us, search))
1709                     t = MAX(t, RECT_TOP(monitor_area[screen_num_monitors])
1710                                + s->strut->top);
1711             }
1712             for (it = struts_right; it; it = g_slist_next(it)) {
1713                 ObScreenStrut *s = it->data;
1714                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1715                     STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
1716                     !STRUT_RIGHT_IGNORE(s->strut, us, search))
1717                     r = MIN(r, RECT_RIGHT(monitor_area[screen_num_monitors])
1718                                - s->strut->right);
1719             }
1720             for (it = struts_bottom; it; it = g_slist_next(it)) {
1721                 ObScreenStrut *s = it->data;
1722                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1723                     STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
1724                     !STRUT_BOTTOM_IGNORE(s->strut, us, search))
1725                     b = MIN(b, RECT_BOTTOM(monitor_area[screen_num_monitors])
1726                                - s->strut->bottom);
1727             }
1728
1729             /* limit to this monitor */
1730             if (head == i) {
1731                 l = MAX(l, RECT_LEFT(monitor_area[i]));
1732                 t = MAX(t, RECT_TOP(monitor_area[i]));
1733                 r = MIN(r, RECT_RIGHT(monitor_area[i]));
1734                 b = MIN(b, RECT_BOTTOM(monitor_area[i]));
1735             }
1736         }
1737     }
1738
1739     a = g_slice_new(Rect);
1740     a->x = l;
1741     a->y = t;
1742     a->width = r - l + 1;
1743     a->height = b - t + 1;
1744     return a;
1745 }
1746
1747 typedef struct {
1748     Rect r;
1749     gboolean subtract;
1750 } RectArithmetic;
1751
1752 guint screen_find_monitor(const Rect *search)
1753 {
1754     guint i;
1755     guint mostpx_index = screen_num_monitors;
1756     glong mostpx = 0;
1757     guint closest_distance_index = screen_num_monitors;
1758     guint closest_distance = G_MAXUINT;
1759     GSList *counted = NULL;
1760
1761     /* we want to count the number of pixels search has on each monitor, but not
1762        double count.  so if a pixel is counted on monitor A then we should not
1763        count it again on monitor B. in the end we want to return the monitor
1764        that had the most pixels counted under this scheme.
1765
1766        this assumes that monitors earlier in the list are more desirable to be
1767        considered the search area's monitor.  we try the configured primary
1768        monitor first, so it gets the highest preference.
1769
1770        if we have counted an area A, then we want to subtract the intersection
1771        of A with the area on future monitors.
1772        but now consider if we count an area B that intersects A. we want to
1773        subtract the area B from that counted on future monitors, but not
1774        subtract the intersection of A and B twice! so we would add the
1775        intersection of A and B back, to account for it being subtracted both
1776        for A and B.
1777
1778        this is the idea behind the algorithm.  we always subtract the full area
1779        for monitor M intersected with the search area. we'll call that AREA.
1780        but then we go through the list |counted| and for each rectangle in
1781        the list that is being subtracted from future monitors, we insert a
1782        request to add back the intersection of the subtracted rect with AREA.
1783        vice versa for a rect in |counted| that is getting added back.
1784     */
1785
1786     if (config_primary_monitor_index < screen_num_monitors) {
1787         const Rect *monitor;
1788         Rect on_current_monitor;
1789         glong area;
1790
1791         monitor = screen_physical_area_monitor(config_primary_monitor_index);
1792
1793         if (RECT_INTERSECTS_RECT(*monitor, *search)) {
1794             RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
1795             area = RECT_AREA(on_current_monitor);
1796
1797             if (area > mostpx) {
1798                 mostpx = area;
1799                 mostpx_index = config_primary_monitor_index;
1800             }
1801
1802             /* add the intersection rect on the current monitor to the
1803                counted list. that's easy for the first one, we just mark it for
1804                subtraction */
1805             {
1806                 RectArithmetic *ra = g_slice_new(RectArithmetic);
1807                 ra->r = on_current_monitor;
1808                 ra->subtract = TRUE;
1809                 counted = g_slist_prepend(counted, ra);
1810             }
1811         }
1812     }
1813
1814     for (i = 0; i < screen_num_monitors; ++i) {
1815         const Rect *monitor;
1816         Rect on_current_monitor;
1817         glong area;
1818         GSList *it;
1819
1820         monitor = screen_physical_area_monitor(i);
1821
1822         if (!RECT_INTERSECTS_RECT(*monitor, *search)) {
1823             /* If we don't intersect then find the distance between the search
1824                rect and the monitor. We'll use the closest monitor from this
1825                metric if none of the monitors intersect. */
1826             guint distance = rect_manhatten_distance(*monitor, *search);
1827
1828             if (distance < closest_distance) {
1829                 closest_distance = distance;
1830                 closest_distance_index = i;
1831             }
1832             continue;
1833         }
1834
1835         if (i == config_primary_monitor_index)
1836             continue;  /* already did this one */
1837
1838         RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
1839         area = RECT_AREA(on_current_monitor);
1840
1841         /* remove pixels we already counted on any previous monitors. */
1842         for (it = counted; it; it = g_slist_next(it)) {
1843             RectArithmetic *ra = it->data;
1844             Rect intersection;
1845
1846             RECT_SET_INTERSECTION(intersection, ra->r, *search);
1847             if (ra->subtract) area -= RECT_AREA(intersection);
1848             else area += RECT_AREA(intersection);
1849         }
1850
1851         if (area > mostpx) {
1852             mostpx = area;
1853             mostpx_index = i;
1854         }
1855
1856         /* add the intersection rect on the current monitor I to the counted
1857            list.
1858            but now we need to compensate for every rectangle R already in the
1859            counted list, and add a new rect R' that is the intersection of
1860            R and I, but with the reverse subtraction/addition operation.
1861         */
1862         for (it = counted; it; it = g_slist_next(it)) {
1863             RectArithmetic *saved = it->data;
1864
1865             if (!RECT_INTERSECTS_RECT(saved->r, on_current_monitor))
1866                 continue;
1867             /* we are going to subtract our rect from future monitors, but
1868                part of it may already be being subtracted/added, so compensate
1869                to not double add/subtract. */
1870             RectArithmetic *reverse = g_slice_new(RectArithmetic);
1871             RECT_SET_INTERSECTION(reverse->r, saved->r, on_current_monitor);
1872             reverse->subtract = !saved->subtract;
1873             /* prepend so we can continue thru the list uninterupted */
1874             counted = g_slist_prepend(counted, reverse);
1875         }
1876         {
1877             RectArithmetic *ra = g_slice_new(RectArithmetic);
1878             ra->r = on_current_monitor;
1879             ra->subtract = TRUE;
1880             counted = g_slist_prepend(counted, ra);
1881         }
1882     }
1883
1884     while (counted) {
1885         g_slice_free(RectArithmetic, counted->data);
1886         counted = g_slist_delete_link(counted, counted);
1887     }
1888
1889     if (mostpx_index < screen_num_monitors)
1890         return mostpx_index;
1891
1892     g_assert(closest_distance_index < screen_num_monitors);
1893     return closest_distance_index;
1894 }
1895
1896 const Rect* screen_physical_area_all_monitors(void)
1897 {
1898     return screen_physical_area_monitor(screen_num_monitors);
1899 }
1900
1901 const Rect* screen_physical_area_monitor(guint head)
1902 {
1903     g_assert(head <= screen_num_monitors);
1904
1905     return &monitor_area[head];
1906 }
1907
1908 gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
1909 {
1910     g_assert(head <= screen_num_monitors);
1911     g_assert(search);
1912     return RECT_INTERSECTS_RECT(monitor_area[head], *search);
1913 }
1914
1915 guint screen_monitor_active(void)
1916 {
1917     if (moveresize_client)
1918         return client_monitor(moveresize_client);
1919     else if (focus_client)
1920         return client_monitor(focus_client);
1921     else
1922         return screen_monitor_pointer();
1923 }
1924
1925 const Rect* screen_physical_area_active(void)
1926 {
1927     return screen_physical_area_monitor(screen_monitor_active());
1928 }
1929
1930 guint screen_monitor_primary(gboolean fixed)
1931 {
1932     if (config_primary_monitor_index > 0) {
1933         if (config_primary_monitor_index-1 < screen_num_monitors)
1934             return config_primary_monitor_index - 1;
1935         else
1936             return 0;
1937     }
1938     else if (fixed)
1939         return 0;
1940     else if (config_primary_monitor == OB_PLACE_MONITOR_ACTIVE)
1941         return screen_monitor_active();
1942     else /* config_primary_monitor == OB_PLACE_MONITOR_MOUSE */
1943         return screen_monitor_pointer();
1944 }
1945
1946 const Rect* screen_physical_area_primary(gboolean fixed)
1947 {
1948     return screen_physical_area_monitor(screen_monitor_primary(fixed));
1949 }
1950
1951 void screen_set_root_cursor(void)
1952 {
1953     if (sn_app_starting())
1954         XDefineCursor(obt_display, obt_root(ob_screen),
1955                       ob_cursor(OB_CURSOR_BUSY));
1956     else
1957         XDefineCursor(obt_display, obt_root(ob_screen),
1958                       ob_cursor(OB_CURSOR_POINTER));
1959 }
1960
1961 guint screen_find_monitor_point(guint x, guint y)
1962 {
1963     Rect mon;
1964     RECT_SET(mon, x, y, 1, 1);
1965     return screen_find_monitor(&mon);
1966 }
1967
1968 guint screen_monitor_pointer()
1969 {
1970     gint x, y;
1971     if (!screen_pointer_pos(&x, &y))
1972         x = y = 0;
1973     return screen_find_monitor_point(x, y);
1974 }
1975
1976 gboolean screen_pointer_pos(gint *x, gint *y)
1977 {
1978     gint i;
1979     gboolean ret;
1980
1981     /* we don't care about any of these return values, but we can't pass NULL */
1982     Window w;
1983     guint u;
1984     gint j;
1985
1986     ret = !!XQueryPointer(obt_display, obt_root(ob_screen),
1987                           &w, &w, x, y, &j, &j, &u);
1988     if (!ret) {
1989         for (i = 0; i < ScreenCount(obt_display); ++i)
1990             if (i != ob_screen)
1991                 if ((ret=XQueryPointer(obt_display, obt_root(i),
1992                                   &w, &w, x, y, &j, &j, &u)))
1993                     break;
1994     }
1995     return ret;
1996 }
1997
1998 gboolean screen_compare_desktops(guint a, guint b)
1999 {
2000     if (a == DESKTOP_ALL)
2001         a = screen_desktop;
2002     if (b == DESKTOP_ALL)
2003         b = screen_desktop;
2004     return a == b;
2005 }
2006
2007 void screen_apply_gravity_point(gint *x, gint *y, gint width, gint height,
2008                                 const GravityPoint *position, const Rect *area)
2009 {
2010     if (position->x.center)
2011         *x = area->width / 2 - width / 2;
2012     else {
2013         *x = position->x.pos;
2014         if (position->x.denom)
2015             *x = (*x * area->width) / position->x.denom;
2016         if (position->x.opposite)
2017             *x = area->width - width - *x;
2018     }
2019
2020     if (position->y.center)
2021         *y = area->height / 2 - height / 2;
2022     else {
2023         *y = position->y.pos;
2024         if (position->y.denom)
2025             *y = (*y * area->height) / position->y.denom;
2026         if (position->y.opposite)
2027             *y = area->height - height - *y;
2028     }
2029
2030     *x += area->x;
2031     *y += area->y;
2032 }