super amazing enter event skipping.
[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 "xerror.h"
24 #include "prop.h"
25 #include "grab.h"
26 #include "startupnotify.h"
27 #include "moveresize.h"
28 #include "config.h"
29 #include "screen.h"
30 #include "client.h"
31 #include "session.h"
32 #include "frame.h"
33 #include "event.h"
34 #include "focus.h"
35 #include "popup.h"
36 #include "extensions.h"
37 #include "render/render.h"
38 #include "gettext.h"
39
40 #include <X11/Xlib.h>
41 #ifdef HAVE_UNISTD_H
42 #  include <sys/types.h>
43 #  include <unistd.h>
44 #endif
45 #include <assert.h>
46
47 /*! The event mask to grab on the root window */
48 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
49                         EnterWindowMask | LeaveWindowMask | \
50                         SubstructureRedirectMask | FocusChangeMask | \
51                         ButtonPressMask | ButtonReleaseMask | ButtonMotionMask)
52
53 static gboolean screen_validate_layout(ObDesktopLayout *l);
54 static gboolean replace_wm();
55 static void     screen_tell_ksplash();
56
57 guint    screen_num_desktops;
58 guint    screen_num_monitors;
59 guint    screen_desktop;
60 guint    screen_last_desktop;
61 Size     screen_physical_size;
62 gboolean screen_showing_desktop;
63 ObDesktopLayout screen_desktop_layout;
64 gchar  **screen_desktop_names;
65 Window   screen_support_win;
66 Time     screen_desktop_user_time = CurrentTime;
67
68 static Rect  **area; /* array of desktop holding array of xinerama areas */
69 static Rect  *monitor_area;
70
71 static ObPagerPopup *desktop_cycle_popup;
72
73 static gboolean replace_wm()
74 {
75     gchar *wm_sn;
76     Atom wm_sn_atom;
77     Window current_wm_sn_owner;
78     Time timestamp;
79
80     wm_sn = g_strdup_printf("WM_S%d", ob_screen);
81     wm_sn_atom = XInternAtom(ob_display, wm_sn, FALSE);
82     g_free(wm_sn);
83
84     current_wm_sn_owner = XGetSelectionOwner(ob_display, wm_sn_atom);
85     if (current_wm_sn_owner == screen_support_win)
86         current_wm_sn_owner = None;
87     if (current_wm_sn_owner) {
88         if (!ob_replace_wm) {
89             g_message(_("A window manager is already running on screen %d"),
90                       ob_screen);
91             return FALSE;
92         }
93         xerror_set_ignore(TRUE);
94         xerror_occured = FALSE;
95
96         /* We want to find out when the current selection owner dies */
97         XSelectInput(ob_display, current_wm_sn_owner, StructureNotifyMask);
98         XSync(ob_display, FALSE);
99
100         xerror_set_ignore(FALSE);
101         if (xerror_occured)
102             current_wm_sn_owner = None;
103     }
104
105     {
106         /* Generate a timestamp */
107         XEvent event;
108
109         XSelectInput(ob_display, screen_support_win, PropertyChangeMask);
110
111         XChangeProperty(ob_display, screen_support_win,
112                         prop_atoms.wm_class, prop_atoms.string,
113                         8, PropModeAppend, NULL, 0);
114         XWindowEvent(ob_display, screen_support_win,
115                      PropertyChangeMask, &event);
116
117         XSelectInput(ob_display, screen_support_win, NoEventMask);
118
119         timestamp = event.xproperty.time;
120     }
121
122     XSetSelectionOwner(ob_display, wm_sn_atom, screen_support_win,
123                        timestamp);
124
125     if (XGetSelectionOwner(ob_display, wm_sn_atom) != screen_support_win) {
126         g_message(_("Could not acquire window manager selection on screen %d"),
127                   ob_screen);
128         return FALSE;
129     }
130
131     /* Wait for old window manager to go away */
132     if (current_wm_sn_owner) {
133       XEvent event;
134       gulong wait = 0;
135       const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
136
137       while (wait < timeout) {
138           if (XCheckWindowEvent(ob_display, current_wm_sn_owner,
139                                 StructureNotifyMask, &event) &&
140               event.type == DestroyNotify)
141               break;
142           g_usleep(G_USEC_PER_SEC / 10);
143           wait += G_USEC_PER_SEC / 10;
144       }
145
146       if (wait >= timeout) {
147           g_message(_("The WM on screen %d is not exiting"), ob_screen);
148           return FALSE;
149       }
150     }
151
152     /* Send client message indicating that we are now the WM */
153     prop_message(RootWindow(ob_display, ob_screen), prop_atoms.manager,
154                  timestamp, wm_sn_atom, screen_support_win, 0,
155                  SubstructureNotifyMask);
156
157     return TRUE;
158 }
159
160 gboolean screen_annex()
161 {
162     XSetWindowAttributes attrib;
163     pid_t pid;
164     gint i, num_support;
165     Atom *prop_atoms_start, *wm_supported_pos;
166     gulong *supported;
167
168     /* create the netwm support window */
169     attrib.override_redirect = TRUE;
170     screen_support_win = XCreateWindow(ob_display,
171                                        RootWindow(ob_display, ob_screen),
172                                        -100, -100, 1, 1, 0,
173                                        CopyFromParent, InputOutput,
174                                        CopyFromParent,
175                                        CWOverrideRedirect, &attrib);
176     XMapWindow(ob_display, screen_support_win);
177     XLowerWindow(ob_display, screen_support_win);
178
179     if (!replace_wm()) {
180         XDestroyWindow(ob_display, screen_support_win);
181         return FALSE;
182     }
183
184     xerror_set_ignore(TRUE);
185     xerror_occured = FALSE;
186     XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
187                  ROOT_EVENTMASK);
188     xerror_set_ignore(FALSE);
189     if (xerror_occured) {
190         g_message(_("A window manager is already running on screen %d"),
191                   ob_screen);
192
193         XDestroyWindow(ob_display, screen_support_win);
194         return FALSE;
195     }
196
197     screen_set_root_cursor();
198
199     /* set the OPENBOX_PID hint */
200     pid = getpid();
201     PROP_SET32(RootWindow(ob_display, ob_screen),
202                openbox_pid, cardinal, pid);
203
204     /* set supporting window */
205     PROP_SET32(RootWindow(ob_display, ob_screen),
206                net_supporting_wm_check, window, screen_support_win);
207
208     /* set properties on the supporting window */
209     PROP_SETS(screen_support_win, net_wm_name, "Openbox");
210     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 prop_atoms struct */
216     prop_atoms_start = (Atom*)&prop_atoms;
217     wm_supported_pos = (Atom*)&(prop_atoms.net_supported);
218     num_support = sizeof(prop_atoms) / sizeof(Atom) -
219         (wm_supported_pos - prop_atoms_start) - 1;
220     i = 0;
221     supported = g_new(gulong, num_support);
222     supported[i++] = prop_atoms.net_supporting_wm_check;
223     supported[i++] = prop_atoms.net_wm_full_placement;
224     supported[i++] = prop_atoms.net_current_desktop;
225     supported[i++] = prop_atoms.net_number_of_desktops;
226     supported[i++] = prop_atoms.net_desktop_geometry;
227     supported[i++] = prop_atoms.net_desktop_viewport;
228     supported[i++] = prop_atoms.net_active_window;
229     supported[i++] = prop_atoms.net_workarea;
230     supported[i++] = prop_atoms.net_client_list;
231     supported[i++] = prop_atoms.net_client_list_stacking;
232     supported[i++] = prop_atoms.net_desktop_names;
233     supported[i++] = prop_atoms.net_close_window;
234     supported[i++] = prop_atoms.net_desktop_layout;
235     supported[i++] = prop_atoms.net_showing_desktop;
236     supported[i++] = prop_atoms.net_wm_name;
237     supported[i++] = prop_atoms.net_wm_visible_name;
238     supported[i++] = prop_atoms.net_wm_icon_name;
239     supported[i++] = prop_atoms.net_wm_visible_icon_name;
240     supported[i++] = prop_atoms.net_wm_desktop;
241     supported[i++] = prop_atoms.net_wm_strut;
242     supported[i++] = prop_atoms.net_wm_strut_partial;
243     supported[i++] = prop_atoms.net_wm_icon;
244     supported[i++] = prop_atoms.net_wm_icon_geometry;
245     supported[i++] = prop_atoms.net_wm_window_type;
246     supported[i++] = prop_atoms.net_wm_window_type_desktop;
247     supported[i++] = prop_atoms.net_wm_window_type_dock;
248     supported[i++] = prop_atoms.net_wm_window_type_toolbar;
249     supported[i++] = prop_atoms.net_wm_window_type_menu;
250     supported[i++] = prop_atoms.net_wm_window_type_utility;
251     supported[i++] = prop_atoms.net_wm_window_type_splash;
252     supported[i++] = prop_atoms.net_wm_window_type_dialog;
253     supported[i++] = prop_atoms.net_wm_window_type_normal;
254     supported[i++] = prop_atoms.net_wm_allowed_actions;
255     supported[i++] = prop_atoms.net_wm_action_move;
256     supported[i++] = prop_atoms.net_wm_action_resize;
257     supported[i++] = prop_atoms.net_wm_action_minimize;
258     supported[i++] = prop_atoms.net_wm_action_shade;
259     supported[i++] = prop_atoms.net_wm_action_maximize_horz;
260     supported[i++] = prop_atoms.net_wm_action_maximize_vert;
261     supported[i++] = prop_atoms.net_wm_action_fullscreen;
262     supported[i++] = prop_atoms.net_wm_action_change_desktop;
263     supported[i++] = prop_atoms.net_wm_action_close;
264     supported[i++] = prop_atoms.net_wm_action_above;
265     supported[i++] = prop_atoms.net_wm_action_below;
266     supported[i++] = prop_atoms.net_wm_state;
267     supported[i++] = prop_atoms.net_wm_state_modal;
268     supported[i++] = prop_atoms.net_wm_state_maximized_vert;
269     supported[i++] = prop_atoms.net_wm_state_maximized_horz;
270     supported[i++] = prop_atoms.net_wm_state_shaded;
271     supported[i++] = prop_atoms.net_wm_state_skip_taskbar;
272     supported[i++] = prop_atoms.net_wm_state_skip_pager;
273     supported[i++] = prop_atoms.net_wm_state_hidden;
274     supported[i++] = prop_atoms.net_wm_state_fullscreen;
275     supported[i++] = prop_atoms.net_wm_state_above;
276     supported[i++] = prop_atoms.net_wm_state_below;
277     supported[i++] = prop_atoms.net_wm_state_demands_attention;
278     supported[i++] = prop_atoms.net_moveresize_window;
279     supported[i++] = prop_atoms.net_wm_moveresize;
280     supported[i++] = prop_atoms.net_wm_user_time;
281     supported[i++] = prop_atoms.net_wm_user_time_window;
282     supported[i++] = prop_atoms.net_frame_extents;
283     supported[i++] = prop_atoms.net_request_frame_extents;
284     supported[i++] = prop_atoms.net_restack_window;
285     supported[i++] = prop_atoms.net_startup_id;
286 #ifdef SYNC
287     supported[i++] = prop_atoms.net_wm_sync_request;
288     supported[i++] = prop_atoms.net_wm_sync_request_counter;
289 #endif
290
291     supported[i++] = prop_atoms.kde_wm_change_state;
292     supported[i++] = prop_atoms.kde_net_wm_frame_strut;
293     supported[i++] = prop_atoms.kde_net_wm_window_type_override;
294
295     supported[i++] = prop_atoms.ob_wm_action_undecorate;
296     supported[i++] = prop_atoms.ob_wm_state_undecorated;
297     supported[i++] = prop_atoms.openbox_pid;
298     supported[i++] = prop_atoms.ob_theme;
299     supported[i++] = prop_atoms.ob_control;
300     g_assert(i == num_support);
301
302     PROP_SETA32(RootWindow(ob_display, ob_screen),
303                 net_supported, atom, supported, num_support);
304     g_free(supported);
305
306     screen_tell_ksplash();
307
308     return TRUE;
309 }
310
311 static void screen_tell_ksplash()
312 {
313     XEvent e;
314     char **argv;
315
316     argv = g_new(gchar*, 6);
317     argv[0] = g_strdup("dcop");
318     argv[1] = g_strdup("ksplash");
319     argv[2] = g_strdup("ksplash");
320     argv[3] = g_strdup("upAndRunning(QString)");
321     argv[4] = g_strdup("wm started");
322     argv[5] = NULL;
323
324     /* tell ksplash through the dcop server command line interface */
325     g_spawn_async(NULL, argv, NULL,
326                   G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD |
327                   G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
328                   NULL, NULL, NULL, NULL);
329     g_strfreev(argv);
330
331     /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
332        hear it anyways. perhaps it is for old ksplash. or new ksplash. or
333        something. oh well. */
334     e.xclient.type = ClientMessage;
335     e.xclient.display = ob_display;
336     e.xclient.window = RootWindow(ob_display, ob_screen);
337     e.xclient.message_type =
338         XInternAtom(ob_display, "_KDE_SPLASH_PROGRESS", False );
339     e.xclient.format = 8;
340     strcpy(e.xclient.data.b, "wm started");
341     XSendEvent(ob_display, RootWindow(ob_display, ob_screen),
342                False, SubstructureNotifyMask, &e );
343 }
344
345 void screen_startup(gboolean reconfig)
346 {
347     gchar **names = NULL;
348     guint32 d;
349     gboolean namesexist = FALSE;
350
351     desktop_cycle_popup = pager_popup_new(FALSE);
352     pager_popup_height(desktop_cycle_popup, POPUP_HEIGHT);
353
354     if (reconfig) {
355         /* update the pager popup's width */
356         pager_popup_text_width_to_strings(desktop_cycle_popup,
357                                           screen_desktop_names,
358                                           screen_num_desktops);
359         return;
360     }
361
362     /* get the initial size */
363     screen_resize();
364
365     /* have names already been set for the desktops? */
366     if (PROP_GETSS(RootWindow(ob_display, ob_screen),
367                    net_desktop_names, utf8, &names))
368     {
369         g_strfreev(names);
370         namesexist = TRUE;
371     }
372
373     /* if names don't exist and we have session names, set those.
374        do this stuff BEFORE setting the number of desktops, because that
375        will create default names for them
376     */
377     if (!namesexist && session_desktop_names != NULL) {
378         guint i, numnames;
379         GSList *it;
380
381         /* get the desktop names */
382         numnames = g_slist_length(session_desktop_names);
383         names = g_new(gchar*, numnames + 1);
384         names[numnames] = NULL;
385         for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
386             names[i] = g_strdup(it->data);
387
388         /* set the root window property */
389         PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,names);
390
391         g_strfreev(names);
392     }
393
394     /* set the number of desktops, if it's not already set.
395
396        this will also set the default names from the config file up for
397        desktops that don't have names yet */
398     screen_num_desktops = 0;
399     if (PROP_GET32(RootWindow(ob_display, ob_screen),
400                    net_number_of_desktops, cardinal, &d))
401         screen_set_num_desktops(d);
402     /* restore from session if possible */
403     else if (session_num_desktops)
404         screen_set_num_desktops(session_num_desktops);
405     else
406         screen_set_num_desktops(config_desktops_num);
407
408     screen_desktop = screen_num_desktops;  /* something invalid */
409     /* start on the current desktop when a wm was already running */
410     if (PROP_GET32(RootWindow(ob_display, ob_screen),
411                    net_current_desktop, cardinal, &d) &&
412         d < screen_num_desktops)
413     {
414         screen_set_desktop(d, FALSE);
415     } else if (session_desktop >= 0)
416         screen_set_desktop(MIN((guint)session_desktop,
417                                screen_num_desktops), FALSE);
418     else
419         screen_set_desktop(MIN(config_screen_firstdesk,
420                                screen_num_desktops) - 1, FALSE);
421     screen_last_desktop = screen_desktop;
422
423     /* don't start in showing-desktop mode */
424     screen_showing_desktop = FALSE;
425     PROP_SET32(RootWindow(ob_display, ob_screen),
426                net_showing_desktop, cardinal, screen_showing_desktop);
427
428     if (session_desktop_layout_present &&
429         screen_validate_layout(&session_desktop_layout))
430     {
431         screen_desktop_layout = session_desktop_layout;
432     }
433     else
434         screen_update_layout();
435 }
436
437 void screen_shutdown(gboolean reconfig)
438 {
439     Rect **r;
440
441     pager_popup_free(desktop_cycle_popup);
442
443     if (reconfig)
444         return;
445
446     XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
447                  NoEventMask);
448
449     /* we're not running here no more! */
450     PROP_ERASE(RootWindow(ob_display, ob_screen), openbox_pid);
451     /* not without us */
452     PROP_ERASE(RootWindow(ob_display, ob_screen), net_supported);
453     /* don't keep this mode */
454     PROP_ERASE(RootWindow(ob_display, ob_screen), net_showing_desktop);
455
456     XDestroyWindow(ob_display, screen_support_win);
457
458     g_strfreev(screen_desktop_names);
459     screen_desktop_names = NULL;
460
461     for (r = area; *r; ++r)
462         g_free(*r);
463     g_free(area);
464     area = NULL;
465 }
466
467 void screen_resize()
468 {
469     static gint oldw = 0, oldh = 0;
470     gint w, h;
471     GList *it;
472     gulong geometry[2];
473
474     w = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
475     h = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen));
476
477     if (w == oldw && h == oldh) return;
478
479     oldw = w; oldh = h;
480
481     /* Set the _NET_DESKTOP_GEOMETRY hint */
482     screen_physical_size.width = geometry[0] = w;
483     screen_physical_size.height = geometry[1] = h;
484     PROP_SETA32(RootWindow(ob_display, ob_screen),
485                 net_desktop_geometry, cardinal, geometry, 2);
486
487     if (ob_state() == OB_STATE_STARTING)
488         return;
489
490     screen_update_areas();
491     dock_configure();
492
493     for (it = client_list; it; it = g_list_next(it))
494         client_move_onscreen(it->data, FALSE);
495 }
496
497 void screen_set_num_desktops(guint num)
498 {
499     guint old;
500     gulong *viewport;
501     GList *it;
502
503     g_assert(num > 0);
504
505     if (screen_num_desktops == num) return;
506
507     old = screen_num_desktops;
508     screen_num_desktops = num;
509     PROP_SET32(RootWindow(ob_display, ob_screen),
510                net_number_of_desktops, cardinal, num);
511
512     /* set the viewport hint */
513     viewport = g_new0(gulong, num * 2);
514     PROP_SETA32(RootWindow(ob_display, ob_screen),
515                 net_desktop_viewport, cardinal, viewport, num * 2);
516     g_free(viewport);
517
518     /* the number of rows/columns will differ */
519     screen_update_layout();
520
521     /* move windows on desktops that will no longer exist! */
522     for (it = client_list; it; it = g_list_next(it)) {
523         ObClient *c = it->data;
524         if (c->desktop >= num && c->desktop != DESKTOP_ALL)
525             client_set_desktop(c, num - 1, FALSE);
526     }
527  
528     /* change our struts/area to match (after moving windows) */
529     screen_update_areas();
530
531     /* may be some unnamed desktops that we need to fill in with names
532      (after updating the areas so the popup can resize) */
533     screen_update_desktop_names();
534
535     /* change our desktop if we're on one that no longer exists! */
536     if (screen_desktop >= screen_num_desktops)
537         screen_set_desktop(num - 1, TRUE);
538 }
539
540 void screen_set_desktop(guint num, gboolean dofocus)
541 {
542     ObClient *c;
543     GList *it;
544     guint old;
545     gulong ignore_start;
546      
547     g_assert(num < screen_num_desktops);
548
549     old = screen_desktop;
550     screen_desktop = num;
551
552     if (old == num) return;
553
554     PROP_SET32(RootWindow(ob_display, ob_screen),
555                net_current_desktop, cardinal, num);
556
557     screen_last_desktop = old;
558
559     ob_debug("Moving to desktop %d\n", num+1);
560
561     /* ignore enter events caused by the move */
562     ignore_start = event_start_ignore_all_enters();
563
564     if (moveresize_client)
565         client_set_desktop(moveresize_client, num, TRUE);
566
567     /* show windows before hiding the rest to lessen the enter/leave events */
568
569     /* show windows from top to bottom */
570     for (it = stacking_list; it; it = g_list_next(it)) {
571         if (WINDOW_IS_CLIENT(it->data)) {
572             ObClient *c = it->data;
573             client_show(c);
574         }
575     }
576
577     if (focus_client && ((client_normal(focus_client) &&
578                           focus_client->desktop == DESKTOP_ALL) ||
579                          focus_client->desktop == screen_desktop))
580         dofocus = FALSE;
581
582     /* have to try focus here because when you leave an empty desktop
583        there is no focus out to watch for. also, we have different rules
584        here. we always allow it to look under the mouse pointer if
585        config_focus_last is FALSE
586
587        do this before hiding the windows so if helper windows are coming
588        with us, they don't get hidden
589     */
590     if (dofocus && (c = focus_fallback(TRUE, !config_focus_last)))
591     {
592         /* only do the flicker reducing stuff ahead of time if we are going
593            to call xsetinputfocus on the window ourselves. otherwise there is
594            no guarantee the window will actually take focus.. */
595         if (c->can_focus) {
596             /* reduce flicker by hiliting now rather than waiting for the
597                server FocusIn event */
598             frame_adjust_focus(c->frame, TRUE);
599             /* do this here so that if you switch desktops to a window with
600                helper windows then the helper windows won't flash */
601             client_bring_helper_windows(c);
602         }
603     }
604
605     /* hide windows from bottom to top */
606     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
607         if (WINDOW_IS_CLIENT(it->data)) {
608             ObClient *c = it->data;
609             client_hide(c);
610         }
611     }
612
613     event_end_ignore_all_enters(ignore_start);
614
615     if (event_curtime != CurrentTime)
616         screen_desktop_user_time = event_curtime;
617 }
618
619 static void get_row_col(guint d, guint *r, guint *c)
620 {
621     switch (screen_desktop_layout.orientation) {
622     case OB_ORIENTATION_HORZ:
623         switch (screen_desktop_layout.start_corner) {
624         case OB_CORNER_TOPLEFT:
625             *r = d / screen_desktop_layout.columns;
626             *c = d % screen_desktop_layout.columns;
627             break;
628         case OB_CORNER_BOTTOMLEFT:
629             *r = screen_desktop_layout.rows - 1 -
630                 d / screen_desktop_layout.columns;
631             *c = d % screen_desktop_layout.columns;
632             break;
633         case OB_CORNER_TOPRIGHT:
634             *r = d / screen_desktop_layout.columns;
635             *c = screen_desktop_layout.columns - 1 -
636                 d % screen_desktop_layout.columns;
637             break;
638         case OB_CORNER_BOTTOMRIGHT:
639             *r = screen_desktop_layout.rows - 1 -
640                 d / screen_desktop_layout.columns;
641             *c = screen_desktop_layout.columns - 1 -
642                 d % screen_desktop_layout.columns;
643             break;
644         }
645         break;
646     case OB_ORIENTATION_VERT:
647         switch (screen_desktop_layout.start_corner) {
648         case OB_CORNER_TOPLEFT:
649             *r = d % screen_desktop_layout.rows;
650             *c = d / screen_desktop_layout.rows;
651             break;
652         case OB_CORNER_BOTTOMLEFT:
653             *r = screen_desktop_layout.rows - 1 -
654                 d % screen_desktop_layout.rows;
655             *c = d / screen_desktop_layout.rows;
656             break;
657         case OB_CORNER_TOPRIGHT:
658             *r = d % screen_desktop_layout.rows;
659             *c = screen_desktop_layout.columns - 1 -
660                 d / screen_desktop_layout.rows;
661             break;
662         case OB_CORNER_BOTTOMRIGHT:
663             *r = screen_desktop_layout.rows - 1 -
664                 d % screen_desktop_layout.rows;
665             *c = screen_desktop_layout.columns - 1 -
666                 d / screen_desktop_layout.rows;
667             break;
668         }
669         break;
670     }
671 }
672
673 static guint translate_row_col(guint r, guint c)
674 {
675     switch (screen_desktop_layout.orientation) {
676     case OB_ORIENTATION_HORZ:
677         switch (screen_desktop_layout.start_corner) {
678         case OB_CORNER_TOPLEFT:
679             return r % screen_desktop_layout.rows *
680                 screen_desktop_layout.columns +
681                 c % screen_desktop_layout.columns;
682         case OB_CORNER_BOTTOMLEFT:
683             return (screen_desktop_layout.rows - 1 -
684                     r % screen_desktop_layout.rows) *
685                 screen_desktop_layout.columns +
686                 c % screen_desktop_layout.columns;
687         case OB_CORNER_TOPRIGHT:
688             return r % screen_desktop_layout.rows *
689                 screen_desktop_layout.columns +
690                 (screen_desktop_layout.columns - 1 -
691                  c % screen_desktop_layout.columns);
692         case OB_CORNER_BOTTOMRIGHT:
693             return (screen_desktop_layout.rows - 1 -
694                     r % screen_desktop_layout.rows) *
695                 screen_desktop_layout.columns +
696                 (screen_desktop_layout.columns - 1 -
697                  c % screen_desktop_layout.columns);
698         }
699     case OB_ORIENTATION_VERT:
700         switch (screen_desktop_layout.start_corner) {
701         case OB_CORNER_TOPLEFT:
702             return c % screen_desktop_layout.columns *
703                 screen_desktop_layout.rows +
704                 r % screen_desktop_layout.rows;
705         case OB_CORNER_BOTTOMLEFT:
706             return c % screen_desktop_layout.columns *
707                 screen_desktop_layout.rows +
708                 (screen_desktop_layout.rows - 1 -
709                  r % screen_desktop_layout.rows);
710         case OB_CORNER_TOPRIGHT:
711             return (screen_desktop_layout.columns - 1 -
712                     c % screen_desktop_layout.columns) *
713                 screen_desktop_layout.rows +
714                 r % screen_desktop_layout.rows;
715         case OB_CORNER_BOTTOMRIGHT:
716             return (screen_desktop_layout.columns - 1 -
717                     c % screen_desktop_layout.columns) *
718                 screen_desktop_layout.rows +
719                 (screen_desktop_layout.rows - 1 -
720                  r % screen_desktop_layout.rows);
721         }
722     }
723     g_assert_not_reached();
724     return 0;
725 }
726
727 void screen_desktop_popup(guint d, gboolean show)
728 {
729     Rect *a;
730
731     if (!show) {
732         pager_popup_hide(desktop_cycle_popup);
733     } else {
734         a = screen_physical_area_monitor(0);
735         pager_popup_position(desktop_cycle_popup, CenterGravity,
736                              a->x + a->width / 2, a->y + a->height / 2);
737         pager_popup_icon_size_multiplier(desktop_cycle_popup,
738                                          (screen_desktop_layout.columns /
739                                           screen_desktop_layout.rows) / 2,
740                                          (screen_desktop_layout.rows/
741                                           screen_desktop_layout.columns) / 2);
742         pager_popup_max_width(desktop_cycle_popup,
743                               MAX(a->width/3, POPUP_WIDTH));
744         pager_popup_show(desktop_cycle_popup, screen_desktop_names[d], d);
745     }
746 }
747
748 guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
749                            gboolean dialog, gboolean done, gboolean cancel)
750 {
751     guint r, c;
752     static guint d = (guint)-1;
753     guint ret, oldd;
754
755     if (d == (guint)-1)
756         d = screen_desktop;
757
758     if ((cancel || done) && dialog)
759         goto show_cycle_dialog;
760
761     oldd = d;
762     get_row_col(d, &r, &c);
763
764     if (linear) {
765         switch (dir) {
766         case OB_DIRECTION_EAST:
767             if (d < screen_num_desktops - 1)
768                 ++d;
769             else if (wrap)
770                 d = 0;
771             break;
772         case OB_DIRECTION_WEST:
773             if (d > 0)
774                 --d;
775             else if (wrap)
776                 d = screen_num_desktops - 1;
777             break;
778         default:
779             assert(0);
780             return screen_desktop;
781         }
782     } else {
783         switch (dir) {
784         case OB_DIRECTION_EAST:
785             ++c;
786             if (c >= screen_desktop_layout.columns) {
787                 if (wrap)
788                     c = 0;
789                 else
790                     goto show_cycle_dialog;
791             }
792             d = translate_row_col(r, c);
793             if (d >= screen_num_desktops) {
794                 if (wrap) {
795                     ++c;
796                 } else {
797                     d = oldd;
798                     goto show_cycle_dialog;
799                 }
800             }
801             break;
802         case OB_DIRECTION_WEST:
803             --c;
804             if (c >= screen_desktop_layout.columns) {
805                 if (wrap)
806                     c = screen_desktop_layout.columns - 1;
807                 else
808                     goto show_cycle_dialog;
809             }
810             d = translate_row_col(r, c);
811             if (d >= screen_num_desktops) {
812                 if (wrap) {
813                     --c;
814                 } else {
815                     d = oldd;
816                     goto show_cycle_dialog;
817                 }
818             }
819             break;
820         case OB_DIRECTION_SOUTH:
821             ++r;
822             if (r >= screen_desktop_layout.rows) {
823                 if (wrap)
824                     r = 0;
825                 else
826                     goto show_cycle_dialog;
827             }
828             d = translate_row_col(r, c);
829             if (d >= screen_num_desktops) {
830                 if (wrap) {
831                     ++r;
832                 } else {
833                     d = oldd;
834                     goto show_cycle_dialog;
835                 }
836             }
837             break;
838         case OB_DIRECTION_NORTH:
839             --r;
840             if (r >= screen_desktop_layout.rows) {
841                 if (wrap)
842                     r = screen_desktop_layout.rows - 1;
843                 else
844                     goto show_cycle_dialog;
845             }
846             d = translate_row_col(r, c);
847             if (d >= screen_num_desktops) {
848                 if (wrap) {
849                     --r;
850                 } else {
851                     d = oldd;
852                     goto show_cycle_dialog;
853                 }
854             }
855             break;
856         default:
857             assert(0);
858             return d = screen_desktop;
859         }
860
861         d = translate_row_col(r, c);
862     }
863
864 show_cycle_dialog:
865     if (dialog && !cancel && !done) {
866         screen_desktop_popup(d, TRUE);
867     } else
868         screen_desktop_popup(0, FALSE);
869     ret = d;
870
871     if (!dialog || cancel || done)
872         d = (guint)-1;
873
874     return ret;
875 }
876
877 static gboolean screen_validate_layout(ObDesktopLayout *l)
878 {
879     if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
880         return FALSE;
881
882     /* fill in a zero rows/columns */
883     if (l->columns == 0) {
884         l->columns = screen_num_desktops / l->rows;
885         if (l->rows * l->columns < screen_num_desktops)
886             l->columns++;
887         if (l->rows * l->columns >= screen_num_desktops + l->columns)
888             l->rows--;
889     } else if (l->rows == 0) {
890         l->rows = screen_num_desktops / l->columns;
891         if (l->columns * l->rows < screen_num_desktops)
892             l->rows++;
893         if (l->columns * l->rows >= screen_num_desktops + l->rows)
894             l->columns--;
895     }
896
897     /* bounds checking */
898     if (l->orientation == OB_ORIENTATION_HORZ) {
899         l->columns = MIN(screen_num_desktops, l->columns);
900         l->rows = MIN(l->rows,
901                       (screen_num_desktops + l->columns - 1) / l->columns);
902         l->columns = screen_num_desktops / l->rows +
903             !!(screen_num_desktops % l->rows);
904     } else {
905         l->rows = MIN(screen_num_desktops, l->rows);
906         l->columns = MIN(l->columns,
907                          (screen_num_desktops + l->rows - 1) / l->rows);
908         l->rows = screen_num_desktops / l->columns +
909             !!(screen_num_desktops % l->columns);
910     }
911     return TRUE;
912 }
913
914 void screen_update_layout()
915
916 {
917     ObDesktopLayout l;
918     guint32 *data;
919     guint num;
920
921     screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
922     screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
923     screen_desktop_layout.rows = 1;
924     screen_desktop_layout.columns = screen_num_desktops;
925
926     if (PROP_GETA32(RootWindow(ob_display, ob_screen),
927                     net_desktop_layout, cardinal, &data, &num)) {
928         if (num == 3 || num == 4) {
929             
930             if (data[0] == prop_atoms.net_wm_orientation_vert)
931                 l.orientation = OB_ORIENTATION_VERT;
932             else if (data[0] == prop_atoms.net_wm_orientation_horz)
933                 l.orientation = OB_ORIENTATION_HORZ;
934             else
935                 return;
936
937             if (num < 4)
938                 l.start_corner = OB_CORNER_TOPLEFT;
939             else {
940                 if (data[3] == prop_atoms.net_wm_topleft)
941                     l.start_corner = OB_CORNER_TOPLEFT;
942                 else if (data[3] == prop_atoms.net_wm_topright)
943                     l.start_corner = OB_CORNER_TOPRIGHT;
944                 else if (data[3] == prop_atoms.net_wm_bottomright)
945                     l.start_corner = OB_CORNER_BOTTOMRIGHT;
946                 else if (data[3] == prop_atoms.net_wm_bottomleft)
947                     l.start_corner = OB_CORNER_BOTTOMLEFT;
948                 else
949                     return;
950             }
951
952             l.columns = data[1];
953             l.rows = data[2];
954
955             if (screen_validate_layout(&l))
956                 screen_desktop_layout = l;
957
958             g_free(data);
959         }
960     }
961 }
962
963 void screen_update_desktop_names()
964 {
965     guint i;
966
967     /* empty the array */
968     g_strfreev(screen_desktop_names);
969     screen_desktop_names = NULL;
970
971     if (PROP_GETSS(RootWindow(ob_display, ob_screen),
972                    net_desktop_names, utf8, &screen_desktop_names))
973         for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
974     else
975         i = 0;
976     if (i < screen_num_desktops) {
977         GSList *it;
978
979         screen_desktop_names = g_renew(gchar*, screen_desktop_names,
980                                        screen_num_desktops + 1);
981         screen_desktop_names[screen_num_desktops] = NULL;
982
983         it = g_slist_nth(config_desktops_names, i);
984
985         for (; i < screen_num_desktops; ++i) {
986             if (it && ((char*)it->data)[0]) /* not empty */
987                 /* use the names from the config file when possible */
988                 screen_desktop_names[i] = g_strdup(it->data);
989             else
990                 /* make up a nice name if it's not though */
991                 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
992                                                           i + 1);
993             if (it) it = g_slist_next(it);
994         }
995
996         /* if we changed any names, then set the root property so we can
997            all agree on the names */
998         PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,
999                    screen_desktop_names);
1000     }
1001
1002     /* resize the pager for these names */
1003     pager_popup_text_width_to_strings(desktop_cycle_popup,
1004                                       screen_desktop_names,
1005                                       screen_num_desktops);
1006 }
1007
1008 void screen_show_desktop(gboolean show, ObClient *show_only)
1009 {
1010     GList *it;
1011      
1012     if (show == screen_showing_desktop) return; /* no change */
1013
1014     screen_showing_desktop = show;
1015
1016     if (show) {
1017         /* hide windows bottom to top */
1018         for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1019             if (WINDOW_IS_CLIENT(it->data)) {
1020                 ObClient *client = it->data;
1021                 client_showhide(client);
1022             }
1023         }
1024     }
1025     else {
1026         /* restore windows top to bottom */
1027         for (it = stacking_list; it; it = g_list_next(it)) {
1028             if (WINDOW_IS_CLIENT(it->data)) {
1029                 ObClient *client = it->data;
1030                 if (client_should_show(client)) {
1031                     if (!show_only || client == show_only)
1032                         client_show(client);
1033                     else
1034                         client_iconify(client, TRUE, FALSE, TRUE);
1035                 }
1036             }
1037         }
1038     }
1039
1040     if (show) {
1041         /* focus the desktop */
1042         for (it = focus_order; it; it = g_list_next(it)) {
1043             ObClient *c = it->data;
1044             if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1045                 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1046                 client_focus(it->data))
1047                 break;
1048         }
1049     }
1050     else if (!show_only) {
1051         ObClient *c;
1052
1053         if ((c = focus_fallback(TRUE, FALSE))) {
1054             /* only do the flicker reducing stuff ahead of time if we are going
1055                to call xsetinputfocus on the window ourselves. otherwise there
1056                is no guarantee the window will actually take focus.. */
1057             if (c->can_focus) {
1058                 /* reduce flicker by hiliting now rather than waiting for the
1059                    server FocusIn event */
1060                 frame_adjust_focus(c->frame, TRUE);
1061             }
1062         }
1063     }
1064
1065     show = !!show; /* make it boolean */
1066     PROP_SET32(RootWindow(ob_display, ob_screen),
1067                net_showing_desktop, cardinal, show);
1068 }
1069
1070 void screen_install_colormap(ObClient *client, gboolean install)
1071 {
1072     if (client == NULL || client->colormap == None) {
1073         if (install)
1074             XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
1075         else
1076             XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
1077     } else {
1078         xerror_set_ignore(TRUE);
1079         if (install)
1080             XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
1081         else
1082             XUninstallColormap(RrDisplay(ob_rr_inst), client->colormap);
1083         xerror_set_ignore(FALSE);
1084     }
1085 }
1086
1087 static inline void
1088 screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area,
1089                            gint edge, Strut *ret)
1090 {
1091     if (s->left &&
1092         ((s->left_end <= s->left_start) ||
1093          (RECT_TOP(*monitor_area) < s->left_end &&
1094           RECT_BOTTOM(*monitor_area) > s->left_start)))
1095         ret->left = MAX(ret->left, edge);
1096 }
1097
1098 static inline void
1099 screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area,
1100                           gint edge, Strut *ret)
1101 {
1102     if (s->top &&
1103         ((s->top_end <= s->top_start) ||
1104          (RECT_LEFT(*monitor_area) < s->top_end &&
1105           RECT_RIGHT(*monitor_area) > s->top_start)))
1106         ret->top = MAX(ret->top, edge);
1107 }
1108
1109 static inline void
1110 screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area,
1111                             gint edge, Strut *ret)
1112 {
1113     if (s->right &&
1114         ((s->right_end <= s->right_start) ||
1115          (RECT_TOP(*monitor_area) < s->right_end &&
1116           RECT_BOTTOM(*monitor_area) > s->right_start)))
1117         ret->right = MAX(ret->right, edge);
1118 }
1119
1120 static inline void
1121 screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area,
1122                              gint edge, Strut *ret)
1123 {
1124     if (s->bottom &&
1125         ((s->bottom_end <= s->bottom_start) ||
1126          (RECT_LEFT(*monitor_area) < s->bottom_end &&
1127           RECT_RIGHT(*monitor_area) > s->bottom_start)))
1128         ret->bottom = MAX(ret->bottom, edge);
1129 }
1130
1131 void screen_update_areas()
1132 {
1133     guint i, x;
1134     gulong *dims;
1135     GList *it;
1136     gint o;
1137
1138     g_free(monitor_area);
1139     extensions_xinerama_screens(&monitor_area, &screen_num_monitors);
1140
1141     if (area) {
1142         for (i = 0; area[i]; ++i)
1143             g_free(area[i]);
1144         g_free(area);
1145     }
1146
1147     area = g_new(Rect*, screen_num_desktops + 2);
1148     for (i = 0; i < screen_num_desktops + 1; ++i)
1149         area[i] = g_new0(Rect, screen_num_monitors + 1);
1150     area[i] = NULL;
1151      
1152     dims = g_new(gulong, 4 * screen_num_desktops);
1153
1154     for (i = 0; i < screen_num_desktops + 1; ++i) {
1155         Strut *struts;
1156         gint l, r, t, b;
1157
1158         struts = g_new0(Strut, screen_num_monitors);
1159
1160         /* calc the xinerama areas */
1161         for (x = 0; x < screen_num_monitors; ++x) {
1162             area[i][x] = monitor_area[x];
1163             if (x == 0) {
1164                 l = monitor_area[x].x;
1165                 t = monitor_area[x].y;
1166                 r = monitor_area[x].x + monitor_area[x].width - 1;
1167                 b = monitor_area[x].y + monitor_area[x].height - 1;
1168             } else {
1169                 l = MIN(l, monitor_area[x].x);
1170                 t = MIN(t, monitor_area[x].y);
1171                 r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1);
1172                 b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1);
1173             }
1174         }
1175         RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1);
1176
1177         /* apply the struts */
1178
1179         /* find the left-most xin heads, i do this in 2 loops :| */
1180         o = area[i][0].x;
1181         for (x = 1; x < screen_num_monitors; ++x)
1182             o = MIN(o, area[i][x].x);
1183
1184         for (x = 0; x < screen_num_monitors; ++x) {
1185             for (it = client_list; it; it = g_list_next(it)) {
1186                 ObClient *c = it->data;
1187                 screen_area_add_strut_left(&c->strut,
1188                                            &monitor_area[x],
1189                                            o + c->strut.left - area[i][x].x,
1190                                            &struts[x]);
1191             }
1192             screen_area_add_strut_left(&dock_strut,
1193                                        &monitor_area[x],
1194                                        o + dock_strut.left - area[i][x].x,
1195                                        &struts[x]);
1196
1197             area[i][x].x += struts[x].left;
1198             area[i][x].width -= struts[x].left;
1199         }
1200
1201         /* find the top-most xin heads, i do this in 2 loops :| */
1202         o = area[i][0].y;
1203         for (x = 1; x < screen_num_monitors; ++x)
1204             o = MIN(o, area[i][x].y);
1205
1206         for (x = 0; x < screen_num_monitors; ++x) {
1207             for (it = client_list; it; it = g_list_next(it)) {
1208                 ObClient *c = it->data;
1209                 screen_area_add_strut_top(&c->strut,
1210                                            &monitor_area[x],
1211                                            o + c->strut.top - area[i][x].y,
1212                                            &struts[x]);
1213             }
1214             screen_area_add_strut_top(&dock_strut,
1215                                       &monitor_area[x],
1216                                       o + dock_strut.top - area[i][x].y,
1217                                       &struts[x]);
1218
1219             area[i][x].y += struts[x].top;
1220             area[i][x].height -= struts[x].top;
1221         }
1222
1223         /* find the right-most xin heads, i do this in 2 loops :| */
1224         o = area[i][0].x + area[i][0].width - 1;
1225         for (x = 1; x < screen_num_monitors; ++x)
1226             o = MAX(o, area[i][x].x + area[i][x].width - 1);
1227
1228         for (x = 0; x < screen_num_monitors; ++x) {
1229             for (it = client_list; it; it = g_list_next(it)) {
1230                 ObClient *c = it->data;
1231                 screen_area_add_strut_right(&c->strut,
1232                                            &monitor_area[x],
1233                                            (area[i][x].x +
1234                                             area[i][x].width - 1) -
1235                                             (o - c->strut.right),
1236                                             &struts[x]);
1237             }
1238             screen_area_add_strut_right(&dock_strut,
1239                                         &monitor_area[x],
1240                                         (area[i][x].x +
1241                                          area[i][x].width - 1) -
1242                                         (o - dock_strut.right),
1243                                         &struts[x]);
1244
1245             area[i][x].width -= struts[x].right;
1246         }
1247
1248         /* find the bottom-most xin heads, i do this in 2 loops :| */
1249         o = area[i][0].y + area[i][0].height - 1;
1250         for (x = 1; x < screen_num_monitors; ++x)
1251             o = MAX(o, area[i][x].y + area[i][x].height - 1);
1252
1253         for (x = 0; x < screen_num_monitors; ++x) {
1254             for (it = client_list; it; it = g_list_next(it)) {
1255                 ObClient *c = it->data;
1256                 screen_area_add_strut_bottom(&c->strut,
1257                                              &monitor_area[x],
1258                                              (area[i][x].y +
1259                                               area[i][x].height - 1) - \
1260                                              (o - c->strut.bottom),
1261                                              &struts[x]);
1262             }
1263             screen_area_add_strut_bottom(&dock_strut,
1264                                          &monitor_area[x],
1265                                          (area[i][x].y +
1266                                           area[i][x].height - 1) - \
1267                                          (o - dock_strut.bottom),
1268                                          &struts[x]);
1269
1270             area[i][x].height -= struts[x].bottom;
1271         }
1272
1273         l = RECT_LEFT(area[i][0]);
1274         t = RECT_TOP(area[i][0]);
1275         r = RECT_RIGHT(area[i][0]);
1276         b = RECT_BOTTOM(area[i][0]);
1277         for (x = 1; x < screen_num_monitors; ++x) {
1278             l = MIN(l, RECT_LEFT(area[i][x]));
1279             t = MIN(l, RECT_TOP(area[i][x]));
1280             r = MAX(r, RECT_RIGHT(area[i][x]));
1281             b = MAX(b, RECT_BOTTOM(area[i][x]));
1282         }
1283         RECT_SET(area[i][screen_num_monitors], l, t,
1284                  r - l + 1, b - t + 1);
1285
1286         /* XXX optimize when this is run? */
1287
1288         /* the area has changed, adjust all the maximized 
1289            windows */
1290         for (it = client_list; it; it = g_list_next(it)) {
1291             ObClient *c = it->data; 
1292             if (i < screen_num_desktops) {
1293                 if (c->desktop == i)
1294                     client_reconfigure(c);
1295             } else if (c->desktop == DESKTOP_ALL)
1296                 client_reconfigure(c);
1297         }
1298         if (i < screen_num_desktops) {
1299             /* don't set these for the 'all desktops' area */
1300             dims[(i * 4) + 0] = area[i][screen_num_monitors].x;
1301             dims[(i * 4) + 1] = area[i][screen_num_monitors].y;
1302             dims[(i * 4) + 2] = area[i][screen_num_monitors].width;
1303             dims[(i * 4) + 3] = area[i][screen_num_monitors].height;
1304         }
1305
1306         g_free(struts);
1307     }
1308
1309     PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
1310                 dims, 4 * screen_num_desktops);
1311
1312     g_free(dims);
1313 }
1314
1315 Rect *screen_area(guint desktop)
1316 {
1317     return screen_area_monitor(desktop, screen_num_monitors);
1318 }
1319
1320 Rect *screen_area_monitor(guint desktop, guint head)
1321 {
1322     if (head > screen_num_monitors)
1323         return NULL;
1324     if (desktop >= screen_num_desktops) {
1325         if (desktop == DESKTOP_ALL)
1326             return &area[screen_num_desktops][head];
1327         return NULL;
1328     }
1329     return &area[desktop][head];
1330 }
1331
1332 guint screen_find_monitor(Rect *search)
1333 {
1334     guint i;
1335     guint most = 0;
1336     guint mostv = 0;
1337
1338     for (i = 0; i < screen_num_monitors; ++i) {
1339         Rect *area = screen_physical_area_monitor(i);
1340         if (RECT_INTERSECTS_RECT(*area, *search)) {
1341             Rect r;
1342             guint v;
1343
1344             RECT_SET_INTERSECTION(r, *area, *search);
1345             v = r.width * r.height;
1346
1347             if (v > mostv) {
1348                 mostv = v;
1349                 most = i;
1350             }
1351         }
1352     }
1353     return most;
1354 }
1355
1356 Rect *screen_physical_area()
1357 {
1358     return screen_physical_area_monitor(screen_num_monitors);
1359 }
1360
1361 Rect *screen_physical_area_monitor(guint head)
1362 {
1363     if (head > screen_num_monitors)
1364         return NULL;
1365     return &monitor_area[head];
1366 }
1367
1368 void screen_set_root_cursor()
1369 {
1370     if (sn_app_starting())
1371         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1372                       ob_cursor(OB_CURSOR_BUSYPOINTER));
1373     else
1374         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1375                       ob_cursor(OB_CURSOR_POINTER));
1376 }
1377
1378 gboolean screen_pointer_pos(gint *x, gint *y)
1379 {
1380     Window w;
1381     gint i;
1382     guint u;
1383     gboolean ret;
1384
1385     ret = !!XQueryPointer(ob_display, RootWindow(ob_display, ob_screen),
1386                           &w, &w, x, y, &i, &i, &u);
1387     if (!ret) {
1388         for (i = 0; i < ScreenCount(ob_display); ++i)
1389             if (i != ob_screen)
1390                 if (XQueryPointer(ob_display, RootWindow(ob_display, i),
1391                                   &w, &w, x, y, &i, &i, &u))
1392                     break;
1393     }
1394     return ret;
1395 }