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