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