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