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