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