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