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