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