1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 screen.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
24 #include "startupnotify.h"
25 #include "moveresize.h"
33 #include "focus_cycle.h"
36 #include "obrender/render.h"
38 #include "obt/display.h"
39 #include "obt/xqueue.h"
44 # include <sys/types.h>
49 /*! The event mask to grab on the root window */
50 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
51 EnterWindowMask | LeaveWindowMask | \
52 SubstructureRedirectMask | FocusChangeMask | \
53 ButtonPressMask | ButtonReleaseMask)
55 static gboolean screen_validate_layout(ObDesktopLayout *l);
56 static gboolean replace_wm(void);
57 //static void screen_tell_ksplash(void);
58 static void screen_fallback_focus(void);
60 guint screen_num_desktops;
61 guint screen_num_monitors;
63 guint screen_last_desktop;
64 ObScreenShowDestopMode screen_show_desktop_mode;
65 ObDesktopLayout screen_desktop_layout;
66 gchar **screen_desktop_names;
67 Window screen_support_win;
68 Time screen_desktop_user_time = CurrentTime;
70 static Size screen_physical_size;
71 static guint screen_old_desktop;
72 static gboolean screen_desktop_timeout = TRUE;
73 static guint screen_desktop_timer = 0;
74 /*! An array of desktops, holding an array of areas per monitor */
75 static Rect *monitor_area = NULL;
76 /*! An array of desktops, holding an array of struts */
77 static GSList *struts_top = NULL;
78 static GSList *struts_left = NULL;
79 static GSList *struts_right = NULL;
80 static GSList *struts_bottom = NULL;
82 static ObPagerPopup *desktop_popup;
83 static guint desktop_popup_timer = 0;
84 static gboolean desktop_popup_perm;
86 /*! The number of microseconds that you need to be on a desktop before it will
87 replace the remembered "last desktop" */
88 #define REMEMBER_LAST_DESKTOP_TIME 750
90 static gboolean replace_wm(void)
94 Window current_wm_sn_owner;
97 wm_sn = g_strdup_printf("WM_S%d", ob_screen);
98 wm_sn_atom = XInternAtom(obt_display, wm_sn, FALSE);
101 current_wm_sn_owner = XGetSelectionOwner(obt_display, wm_sn_atom);
102 if (current_wm_sn_owner == screen_support_win)
103 current_wm_sn_owner = None;
104 if (current_wm_sn_owner) {
105 if (!ob_replace_wm) {
106 g_message(_("A window manager is already running on screen %d"),
110 obt_display_ignore_errors(TRUE);
112 /* We want to find out when the current selection owner dies */
113 XSelectInput(obt_display, current_wm_sn_owner, StructureNotifyMask);
114 XSync(obt_display, FALSE);
116 obt_display_ignore_errors(FALSE);
117 if (obt_display_error_occured)
118 current_wm_sn_owner = None;
121 timestamp = event_time();
123 XSetSelectionOwner(obt_display, wm_sn_atom, screen_support_win,
126 if (XGetSelectionOwner(obt_display, wm_sn_atom) != screen_support_win) {
127 g_message(_("Could not acquire window manager selection on screen %d"),
132 /* Wait for old window manager to go away */
133 if (current_wm_sn_owner) {
135 const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
136 ObtXQueueWindowType wt;
138 wt.window = current_wm_sn_owner;
139 wt.type = DestroyNotify;
141 while (wait < timeout) {
142 /* Checks the local queue and incoming events for this event */
143 if (xqueue_exists_local(xqueue_match_window_type, &wt))
145 g_usleep(G_USEC_PER_SEC / 10);
146 wait += G_USEC_PER_SEC / 10;
149 if (wait >= timeout) {
150 g_message(_("The WM on screen %d is not exiting"), ob_screen);
155 /* Send client message indicating that we are now the WM */
156 obt_prop_message(ob_screen, obt_root(ob_screen), OBT_PROP_ATOM(MANAGER),
157 timestamp, wm_sn_atom, screen_support_win, 0, 0,
158 SubstructureNotifyMask);
163 gboolean screen_annex(void)
165 XSetWindowAttributes attrib;
170 /* create the netwm support window */
171 attrib.override_redirect = TRUE;
172 attrib.event_mask = PropertyChangeMask | KeyPressMask | KeyReleaseMask;
173 screen_support_win = XCreateWindow(obt_display, obt_root(ob_screen),
175 CopyFromParent, InputOutput,
177 CWEventMask | CWOverrideRedirect,
179 XMapWindow(obt_display, screen_support_win);
180 XLowerWindow(obt_display, screen_support_win);
183 XDestroyWindow(obt_display, screen_support_win);
187 obt_display_ignore_errors(TRUE);
188 XSelectInput(obt_display, obt_root(ob_screen), ROOT_EVENTMASK);
189 obt_display_ignore_errors(FALSE);
190 if (obt_display_error_occured) {
191 g_message(_("A window manager is already running on screen %d"),
194 XDestroyWindow(obt_display, screen_support_win);
198 screen_set_root_cursor();
200 /* set the OPENBOX_PID hint */
202 OBT_PROP_SET32(obt_root(ob_screen), OPENBOX_PID, CARDINAL, pid);
204 /* set supporting window */
205 OBT_PROP_SET32(obt_root(ob_screen),
206 NET_SUPPORTING_WM_CHECK, WINDOW, screen_support_win);
208 /* set properties on the supporting window */
209 OBT_PROP_SETS(screen_support_win, NET_WM_NAME, "Openbox");
210 OBT_PROP_SET32(screen_support_win, NET_SUPPORTING_WM_CHECK,
211 WINDOW, screen_support_win);
213 /* set the _NET_SUPPORTED_ATOMS hint */
215 /* this is all the atoms after NET_SUPPORTED in the ObtPropAtoms enum */
216 num_support = OBT_PROP_NUM_ATOMS - OBT_PROP_NET_SUPPORTED - 1;
218 supported = g_new(gulong, num_support);
219 supported[i++] = OBT_PROP_ATOM(NET_SUPPORTING_WM_CHECK);
220 supported[i++] = OBT_PROP_ATOM(NET_WM_FULL_PLACEMENT);
221 supported[i++] = OBT_PROP_ATOM(NET_CURRENT_DESKTOP);
222 supported[i++] = OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS);
223 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_GEOMETRY);
224 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_VIEWPORT);
225 supported[i++] = OBT_PROP_ATOM(NET_ACTIVE_WINDOW);
226 supported[i++] = OBT_PROP_ATOM(NET_WORKAREA);
227 supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST);
228 supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST_STACKING);
229 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_NAMES);
230 supported[i++] = OBT_PROP_ATOM(NET_CLOSE_WINDOW);
231 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_LAYOUT);
232 supported[i++] = OBT_PROP_ATOM(NET_SHOWING_DESKTOP);
233 supported[i++] = OBT_PROP_ATOM(NET_WM_NAME);
234 supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_NAME);
235 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_NAME);
236 supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_ICON_NAME);
237 supported[i++] = OBT_PROP_ATOM(NET_WM_DESKTOP);
238 supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT);
239 supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL);
240 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON);
241 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY);
242 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE);
243 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP);
244 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK);
245 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR);
246 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU);
247 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY);
248 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH);
249 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG);
250 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL);
251 supported[i++] = OBT_PROP_ATOM(NET_WM_ALLOWED_ACTIONS);
252 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_OPACITY);
253 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
254 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
255 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
256 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
257 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
258 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
259 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
260 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
261 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
262 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
263 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
264 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE);
265 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
266 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
267 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
268 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
269 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
270 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
271 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
272 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
273 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
274 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
275 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
276 supported[i++] = OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW);
277 supported[i++] = OBT_PROP_ATOM(NET_WM_MOVERESIZE);
278 supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME);
280 supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME_WINDOW);
282 supported[i++] = OBT_PROP_ATOM(NET_FRAME_EXTENTS);
283 supported[i++] = OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS);
284 supported[i++] = OBT_PROP_ATOM(NET_RESTACK_WINDOW);
285 supported[i++] = OBT_PROP_ATOM(NET_STARTUP_ID);
287 supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
288 supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER);
290 supported[i++] = OBT_PROP_ATOM(NET_WM_PID);
291 supported[i++] = OBT_PROP_ATOM(NET_WM_PING);
293 supported[i++] = OBT_PROP_ATOM(KDE_WM_CHANGE_STATE);
294 supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_FRAME_STRUT);
295 supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
297 supported[i++] = OBT_PROP_ATOM(OB_FOCUS);
298 supported[i++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
299 supported[i++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
300 supported[i++] = OBT_PROP_ATOM(OPENBOX_PID);
301 supported[i++] = OBT_PROP_ATOM(OB_THEME);
302 supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE);
303 supported[i++] = OBT_PROP_ATOM(OB_LAST_DESKTOP);
304 supported[i++] = OBT_PROP_ATOM(OB_CONTROL);
305 supported[i++] = OBT_PROP_ATOM(OB_VERSION);
306 supported[i++] = OBT_PROP_ATOM(OB_APP_ROLE);
307 supported[i++] = OBT_PROP_ATOM(OB_APP_TITLE);
308 supported[i++] = OBT_PROP_ATOM(OB_APP_NAME);
309 supported[i++] = OBT_PROP_ATOM(OB_APP_CLASS);
310 supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_NAME);
311 supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_CLASS);
312 supported[i++] = OBT_PROP_ATOM(OB_APP_TYPE);
313 g_assert(i == num_support);
315 OBT_PROP_SETA32(obt_root(ob_screen),
316 NET_SUPPORTED, ATOM, supported, num_support);
319 OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION,
322 //screen_tell_ksplash();
327 static void screen_tell_ksplash(void)
332 argv = g_new(gchar*, 6);
333 argv[0] = g_strdup("dcop");
334 argv[1] = g_strdup("ksplash");
335 argv[2] = g_strdup("ksplash");
336 argv[3] = g_strdup("upAndRunning(QString)");
337 argv[4] = g_strdup("wm started");
340 /* tell ksplash through the dcop server command line interface */
341 g_spawn_async(NULL, argv, NULL,
342 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD |
343 G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
344 NULL, NULL, NULL, NULL);
347 /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
348 hear it anyways. perhaps it is for old ksplash. or new ksplash. or
349 something. oh well. */
350 e.xclient.type = ClientMessage;
351 e.xclient.display = obt_display;
352 e.xclient.window = obt_root(ob_screen);
353 e.xclient.message_type =
354 XInternAtom(obt_display, "_KDE_SPLASH_PROGRESS", False);
355 e.xclient.format = 8;
356 strcpy(e.xclient.data.b, "wm started");
357 XSendEvent(obt_display, obt_root(ob_screen),
358 False, SubstructureNotifyMask, &e);
361 void screen_startup(gboolean reconfig)
363 gchar **names = NULL;
365 gboolean namesexist = FALSE;
367 desktop_popup = pager_popup_new();
368 desktop_popup_perm = FALSE;
369 pager_popup_height(desktop_popup, POPUP_HEIGHT);
372 /* update the pager popup's width */
373 pager_popup_text_width_to_strings(desktop_popup,
374 screen_desktop_names,
375 screen_num_desktops);
379 /* get the initial size */
382 /* have names already been set for the desktops? */
383 if (OBT_PROP_GETSS_UTF8(obt_root(ob_screen), NET_DESKTOP_NAMES, &names)) {
388 /* if names don't exist and we have session names, set those.
389 do this stuff BEFORE setting the number of desktops, because that
390 will create default names for them
392 if (!namesexist && session_desktop_names != NULL) {
396 /* get the desktop names */
397 numnames = g_slist_length(session_desktop_names);
398 names = g_new(gchar*, numnames + 1);
399 names[numnames] = NULL;
400 for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
401 names[i] = g_strdup(it->data);
403 /* set the root window property */
404 OBT_PROP_SETSS(obt_root(ob_screen),
405 NET_DESKTOP_NAMES, (const gchar*const*)names);
410 /* set the number of desktops, if it's not already set.
412 this will also set the default names from the config file up for
413 desktops that don't have names yet */
414 screen_num_desktops = 0;
415 if (OBT_PROP_GET32(obt_root(ob_screen),
416 NET_NUMBER_OF_DESKTOPS, CARDINAL, &d))
418 if (d != config_desktops_num) {
419 /* TRANSLATORS: If you need to specify a different order of the
420 arguments, you can use %1$d for the first one and %2$d for the
421 second one. For example,
422 "The current session has %2$d desktops, but Openbox is configured for %1$d ..." */
423 g_warning(ngettext("Openbox is configured for %d desktop, but the current session has %d. Overriding the Openbox configuration.", "Openbox is configured for %d desktops, but the current session has %d. Overriding the Openbox configuration.", config_desktops_num),
424 config_desktops_num, d);
426 screen_set_num_desktops(d);
428 /* restore from session if possible */
429 else if (session_num_desktops)
430 screen_set_num_desktops(session_num_desktops);
432 screen_set_num_desktops(config_desktops_num);
434 screen_desktop = screen_num_desktops; /* something invalid */
435 /* start on the current desktop when a wm was already running */
436 if (OBT_PROP_GET32(obt_root(ob_screen),
437 NET_CURRENT_DESKTOP, CARDINAL, &d) &&
438 d < screen_num_desktops)
440 screen_set_desktop(d, FALSE);
441 } else if (session_desktop >= 0)
442 screen_set_desktop(MIN((guint)session_desktop,
443 screen_num_desktops), FALSE);
445 screen_set_desktop(MIN(config_screen_firstdesk,
446 screen_num_desktops) - 1, FALSE);
447 OBT_PROP_GET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, &screen_last_desktop);
448 if (screen_last_desktop < 0 || screen_last_desktop >= screen_num_desktops) {
449 screen_last_desktop = screen_desktop;
450 OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
453 /* don't start in showing-desktop mode */
454 screen_show_desktop_mode = SCREEN_SHOW_DESKTOP_NO;
455 OBT_PROP_SET32(obt_root(ob_screen),
456 NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop());
458 if (session_desktop_layout_present &&
459 screen_validate_layout(&session_desktop_layout))
461 screen_desktop_layout = session_desktop_layout;
464 screen_update_layout();
467 void screen_shutdown(gboolean reconfig)
469 pager_popup_free(desktop_popup);
474 XSelectInput(obt_display, obt_root(ob_screen), NoEventMask);
476 /* we're not running here no more! */
477 OBT_PROP_ERASE(obt_root(ob_screen), OPENBOX_PID);
479 OBT_PROP_ERASE(obt_root(ob_screen), NET_SUPPORTED);
480 /* don't keep this mode */
481 OBT_PROP_ERASE(obt_root(ob_screen), NET_SHOWING_DESKTOP);
483 XDestroyWindow(obt_display, screen_support_win);
485 g_strfreev(screen_desktop_names);
486 screen_desktop_names = NULL;
489 void screen_resize(void)
495 w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
496 h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
498 /* Set the _NET_DESKTOP_GEOMETRY hint */
499 screen_physical_size.width = geometry[0] = w;
500 screen_physical_size.height = geometry[1] = h;
501 OBT_PROP_SETA32(obt_root(ob_screen),
502 NET_DESKTOP_GEOMETRY, CARDINAL, geometry, 2);
504 if (ob_state() != OB_STATE_RUNNING)
507 /* this calls screen_update_areas(), which we need ! */
510 // bug: this is done in screen_update_areas() already
511 // for (it = client_list; it; it = g_list_next(it)) {
512 // client_move_onscreen(it->data, FALSE);
513 // client_reconfigure(it->data, FALSE);
517 void screen_set_num_desktops(guint num)
520 GList *it, *stacking_copy;
524 if (screen_num_desktops == num) return;
526 screen_num_desktops = num;
527 OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num);
529 /* set the viewport hint */
530 viewport = g_new0(gulong, num * 2);
531 OBT_PROP_SETA32(obt_root(ob_screen),
532 NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2);
535 /* the number of rows/columns will differ */
536 screen_update_layout();
538 /* move windows on desktops that will no longer exist!
539 make a copy of the list cuz we're changing it */
540 stacking_copy = g_list_copy(stacking_list);
541 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
542 if (WINDOW_IS_CLIENT(it->data)) {
543 ObClient *c = it->data;
544 if (c->desktop != DESKTOP_ALL && c->desktop >= num)
545 client_set_desktop(c, num - 1, FALSE, TRUE);
546 /* raise all the windows that are on the current desktop which
548 else if (screen_desktop == num - 1 &&
549 (c->desktop == DESKTOP_ALL ||
550 c->desktop == screen_desktop))
551 stacking_raise(CLIENT_AS_WINDOW(c));
554 g_list_free(stacking_copy);
556 /* change our struts/area to match (after moving windows) */
557 screen_update_areas();
559 /* may be some unnamed desktops that we need to fill in with names
560 (after updating the areas so the popup can resize) */
561 screen_update_desktop_names();
563 /* change our desktop if we're on one that no longer exists! */
564 if (screen_desktop >= screen_num_desktops)
565 screen_set_desktop(num - 1, TRUE);
568 static void screen_fallback_focus(void)
573 /* only allow omnipresent windows to get focus on desktop change if
574 an omnipresent window is already focused (it'll keep focus probably, but
575 maybe not depending on mouse-focus options) */
576 allow_omni = focus_client && (client_normal(focus_client) &&
577 focus_client->desktop == DESKTOP_ALL);
579 /* the client moved there already so don't move focus. prevent flicker
580 on sendtodesktop + follow */
581 if (focus_client && focus_client->desktop == screen_desktop)
584 /* have to try focus here because when you leave an empty desktop
585 there is no focus out to watch for. also, we have different rules
586 here. we always allow it to look under the mouse pointer if
587 config_focus_last is FALSE
589 do this before hiding the windows so if helper windows are coming
590 with us, they don't get hidden
592 if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
595 /* only do the flicker reducing stuff ahead of time if we are going
596 to call xsetinputfocus on the window ourselves. otherwise there is
597 no guarantee the window will actually take focus.. */
599 /* reduce flicker by hiliting now rather than waiting for the
600 server FocusIn event */
601 frame_adjust_focus(c->frame, TRUE);
602 /* do this here so that if you switch desktops to a window with
603 helper windows then the helper windows won't flash */
604 client_bring_helper_windows(c);
609 static gboolean last_desktop_func(gpointer data)
611 screen_desktop_timeout = TRUE;
612 OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
613 screen_desktop_timer = 0;
614 return FALSE; /* don't repeat */
617 void screen_set_desktop(guint num, gboolean dofocus)
623 g_assert(num < screen_num_desktops);
625 previous = screen_desktop;
626 screen_desktop = num;
628 if (previous == num) return;
630 OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num);
632 /* This whole thing decides when/how to save the screen_last_desktop so
633 that it can be restored later if you want */
634 if (screen_desktop_timeout) {
635 /* If screen_desktop_timeout is true, then we've been on this desktop
636 long enough and we can save it as the last desktop. */
638 if (screen_last_desktop == previous)
639 /* this is the startup state only */
640 screen_old_desktop = screen_desktop;
642 /* save the "last desktop" as the "old desktop" */
643 screen_old_desktop = screen_last_desktop;
644 /* save the desktop we're coming from as the "last desktop" */
645 screen_last_desktop = previous;
649 /* If screen_desktop_timeout is false, then we just got to this desktop
650 and we are moving away again. */
652 if (screen_desktop == screen_last_desktop) {
653 /* If we are moving to the "last desktop" .. */
654 if (previous == screen_old_desktop) {
655 /* .. from the "old desktop", change the last desktop to
656 be where we are coming from */
657 screen_last_desktop = screen_old_desktop;
659 else if (screen_last_desktop == screen_old_desktop) {
660 /* .. and also to the "old desktop", change the "last
661 desktop" to be where we are coming from */
662 screen_last_desktop = previous;
665 /* .. from some other desktop, then set the "last desktop" to
666 be the saved "old desktop", i.e. where we were before the
668 screen_last_desktop = screen_old_desktop;
672 /* If we are moving to any desktop besides the "last desktop"..
673 (this is the normal case) */
674 if (screen_desktop == screen_old_desktop) {
675 /* If moving to the "old desktop", which is not the
676 "last desktop", don't save anything */
678 else if (previous == screen_old_desktop) {
679 /* If moving from the "old desktop", and not to the
680 "last desktop", don't save anything */
682 else if (screen_last_desktop == screen_old_desktop) {
683 /* If the "last desktop" is the same as "old desktop" and
684 you're not moving to the "last desktop" then save where
685 we're coming from as the "last desktop" */
686 screen_last_desktop = previous;
689 /* If the "last desktop" is different from the "old desktop"
690 and you're not moving to the "last desktop", then don't save
695 screen_desktop_timeout = FALSE;
696 if (screen_desktop_timer) g_source_remove(screen_desktop_timer);
697 screen_desktop_timer = g_timeout_add(REMEMBER_LAST_DESKTOP_TIME,
698 last_desktop_func, NULL);
700 ob_debug("Moving to desktop %d", num+1);
702 if (ob_state() == OB_STATE_RUNNING)
703 screen_show_desktop_popup(screen_desktop, FALSE);
705 /* ignore enter events caused by the move */
706 ignore_start = event_start_ignore_all_enters();
708 if (moveresize_client)
709 client_set_desktop(moveresize_client, num, TRUE, FALSE);
711 /* show windows before hiding the rest to lessen the enter/leave events */
713 /* show windows from top to bottom */
714 for (it = stacking_list; it; it = g_list_next(it)) {
715 if (WINDOW_IS_CLIENT(it->data)) {
716 ObClient *c = it->data;
721 if (dofocus) screen_fallback_focus();
723 /* hide windows from bottom to top */
724 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
725 if (WINDOW_IS_CLIENT(it->data)) {
726 ObClient *c = it->data;
727 if (client_hide(c)) {
728 if (c == focus_client) {
729 /* c was focused and we didn't do fallback clearly so make
730 sure openbox doesnt still consider the window focused.
731 this happens when using NextWindow with allDesktops,
732 since it doesnt want to move focus on desktop change,
733 but the focus is not going to stay with the current
734 window, which has now disappeared.
735 only do this if the client was actually hidden,
736 otherwise it can keep focus. */
737 focus_set_client(NULL);
743 focus_cycle_addremove(NULL, TRUE);
745 event_end_ignore_all_enters(ignore_start);
747 if (event_source_time() != CurrentTime)
748 screen_desktop_user_time = event_source_time();
751 void screen_add_desktop(gboolean current)
755 /* ignore enter events caused by this */
756 ignore_start = event_start_ignore_all_enters();
758 screen_set_num_desktops(screen_num_desktops+1);
760 /* move all the clients over */
764 for (it = client_list; it; it = g_list_next(it)) {
765 ObClient *c = it->data;
766 if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
767 /* don't move direct children, they'll be moved with their
768 parent - which will have to be on the same desktop */
769 !client_direct_parent(c))
771 ob_debug("moving window %s", c->title);
772 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
777 event_end_ignore_all_enters(ignore_start);
780 void screen_remove_desktop(gboolean current)
782 guint rmdesktop, movedesktop;
783 GList *it, *stacking_copy;
786 if (screen_num_desktops <= 1) return;
788 /* ignore enter events caused by this */
789 ignore_start = event_start_ignore_all_enters();
791 /* what desktop are we removing and moving to? */
793 rmdesktop = screen_desktop;
795 rmdesktop = screen_num_desktops - 1;
796 if (rmdesktop < screen_num_desktops - 1)
797 movedesktop = rmdesktop + 1;
799 movedesktop = rmdesktop;
801 /* make a copy of the list cuz we're changing it */
802 stacking_copy = g_list_copy(stacking_list);
803 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
804 if (WINDOW_IS_CLIENT(it->data)) {
805 ObClient *c = it->data;
806 guint d = c->desktop;
807 if (d != DESKTOP_ALL && d >= movedesktop &&
808 /* don't move direct children, they'll be moved with their
809 parent - which will have to be on the same desktop */
810 !client_direct_parent(c))
812 ob_debug("moving window %s", c->title);
813 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
815 /* raise all the windows that are on the current desktop which
817 if ((screen_desktop == rmdesktop - 1 ||
818 screen_desktop == rmdesktop) &&
819 (d == DESKTOP_ALL || d == screen_desktop))
821 stacking_raise(CLIENT_AS_WINDOW(c));
822 ob_debug("raising window %s", c->title);
826 g_list_free(stacking_copy);
828 /* fallback focus like we're changing desktops */
829 if (screen_desktop < screen_num_desktops - 1) {
830 screen_fallback_focus();
831 ob_debug("fake desktop change");
834 screen_set_num_desktops(screen_num_desktops-1);
836 event_end_ignore_all_enters(ignore_start);
839 static void get_row_col(guint d, guint *r, guint *c)
841 switch (screen_desktop_layout.orientation) {
842 case OB_ORIENTATION_HORZ:
843 switch (screen_desktop_layout.start_corner) {
844 case OB_CORNER_TOPLEFT:
845 *r = d / screen_desktop_layout.columns;
846 *c = d % screen_desktop_layout.columns;
848 case OB_CORNER_BOTTOMLEFT:
849 *r = screen_desktop_layout.rows - 1 -
850 d / screen_desktop_layout.columns;
851 *c = d % screen_desktop_layout.columns;
853 case OB_CORNER_TOPRIGHT:
854 *r = d / screen_desktop_layout.columns;
855 *c = screen_desktop_layout.columns - 1 -
856 d % screen_desktop_layout.columns;
858 case OB_CORNER_BOTTOMRIGHT:
859 *r = screen_desktop_layout.rows - 1 -
860 d / screen_desktop_layout.columns;
861 *c = screen_desktop_layout.columns - 1 -
862 d % screen_desktop_layout.columns;
866 case OB_ORIENTATION_VERT:
867 switch (screen_desktop_layout.start_corner) {
868 case OB_CORNER_TOPLEFT:
869 *r = d % screen_desktop_layout.rows;
870 *c = d / screen_desktop_layout.rows;
872 case OB_CORNER_BOTTOMLEFT:
873 *r = screen_desktop_layout.rows - 1 -
874 d % screen_desktop_layout.rows;
875 *c = d / screen_desktop_layout.rows;
877 case OB_CORNER_TOPRIGHT:
878 *r = d % screen_desktop_layout.rows;
879 *c = screen_desktop_layout.columns - 1 -
880 d / screen_desktop_layout.rows;
882 case OB_CORNER_BOTTOMRIGHT:
883 *r = screen_desktop_layout.rows - 1 -
884 d % screen_desktop_layout.rows;
885 *c = screen_desktop_layout.columns - 1 -
886 d / screen_desktop_layout.rows;
893 static guint translate_row_col(guint r, guint c)
895 switch (screen_desktop_layout.orientation) {
896 case OB_ORIENTATION_HORZ:
897 switch (screen_desktop_layout.start_corner) {
898 case OB_CORNER_TOPLEFT:
899 return r % screen_desktop_layout.rows *
900 screen_desktop_layout.columns +
901 c % screen_desktop_layout.columns;
902 case OB_CORNER_BOTTOMLEFT:
903 return (screen_desktop_layout.rows - 1 -
904 r % screen_desktop_layout.rows) *
905 screen_desktop_layout.columns +
906 c % screen_desktop_layout.columns;
907 case OB_CORNER_TOPRIGHT:
908 return r % screen_desktop_layout.rows *
909 screen_desktop_layout.columns +
910 (screen_desktop_layout.columns - 1 -
911 c % screen_desktop_layout.columns);
912 case OB_CORNER_BOTTOMRIGHT:
913 return (screen_desktop_layout.rows - 1 -
914 r % screen_desktop_layout.rows) *
915 screen_desktop_layout.columns +
916 (screen_desktop_layout.columns - 1 -
917 c % screen_desktop_layout.columns);
919 case OB_ORIENTATION_VERT:
920 switch (screen_desktop_layout.start_corner) {
921 case OB_CORNER_TOPLEFT:
922 return c % screen_desktop_layout.columns *
923 screen_desktop_layout.rows +
924 r % screen_desktop_layout.rows;
925 case OB_CORNER_BOTTOMLEFT:
926 return c % screen_desktop_layout.columns *
927 screen_desktop_layout.rows +
928 (screen_desktop_layout.rows - 1 -
929 r % screen_desktop_layout.rows);
930 case OB_CORNER_TOPRIGHT:
931 return (screen_desktop_layout.columns - 1 -
932 c % screen_desktop_layout.columns) *
933 screen_desktop_layout.rows +
934 r % screen_desktop_layout.rows;
935 case OB_CORNER_BOTTOMRIGHT:
936 return (screen_desktop_layout.columns - 1 -
937 c % screen_desktop_layout.columns) *
938 screen_desktop_layout.rows +
939 (screen_desktop_layout.rows - 1 -
940 r % screen_desktop_layout.rows);
943 g_assert_not_reached();
947 static gboolean hide_desktop_popup_func(gpointer data)
949 pager_popup_hide(desktop_popup);
950 desktop_popup_timer = 0;
951 return FALSE; /* don't repeat */
954 void screen_show_desktop_popup(guint d, gboolean perm)
958 /* 0 means don't show the popup */
959 if (!config_desktop_popup_time) return;
961 a = screen_physical_area_primary(FALSE);
962 pager_popup_position(desktop_popup, CenterGravity,
963 a->x + a->width / 2, a->y + a->height / 2);
964 pager_popup_icon_size_multiplier(desktop_popup,
965 (screen_desktop_layout.columns /
966 screen_desktop_layout.rows) / 2,
967 (screen_desktop_layout.rows/
968 screen_desktop_layout.columns) / 2);
969 pager_popup_max_width(desktop_popup,
970 MAX(a->width/3, POPUP_WIDTH));
971 pager_popup_show(desktop_popup, screen_desktop_names[d], d);
973 if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
974 desktop_popup_timer = 0;
975 if (!perm && !desktop_popup_perm)
976 /* only hide if its not already being show permanently */
977 desktop_popup_timer = g_timeout_add(config_desktop_popup_time,
978 hide_desktop_popup_func,
981 desktop_popup_perm = TRUE;
984 void screen_hide_desktop_popup(void)
986 if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
987 desktop_popup_timer = 0;
988 pager_popup_hide(desktop_popup);
989 desktop_popup_perm = FALSE;
992 guint screen_find_desktop(guint from, ObDirection dir,
993 gboolean wrap, gboolean linear)
999 get_row_col(d, &r, &c);
1002 case OB_DIRECTION_EAST:
1003 if (d < screen_num_desktops - 1)
1010 case OB_DIRECTION_WEST:
1014 d = screen_num_desktops - 1;
1019 g_assert_not_reached();
1024 case OB_DIRECTION_EAST:
1026 if (c >= screen_desktop_layout.columns) {
1032 d = translate_row_col(r, c);
1033 if (d >= screen_num_desktops) {
1040 case OB_DIRECTION_WEST:
1042 if (c >= screen_desktop_layout.columns) {
1044 c = screen_desktop_layout.columns - 1;
1048 d = translate_row_col(r, c);
1049 if (d >= screen_num_desktops) {
1056 case OB_DIRECTION_SOUTH:
1058 if (r >= screen_desktop_layout.rows) {
1064 d = translate_row_col(r, c);
1065 if (d >= screen_num_desktops) {
1072 case OB_DIRECTION_NORTH:
1074 if (r >= screen_desktop_layout.rows) {
1076 r = screen_desktop_layout.rows - 1;
1080 d = translate_row_col(r, c);
1081 if (d >= screen_num_desktops) {
1089 g_assert_not_reached();
1093 d = translate_row_col(r, c);
1098 static gboolean screen_validate_layout(ObDesktopLayout *l)
1100 if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
1103 /* fill in a zero rows/columns */
1104 if (l->columns == 0) {
1105 l->columns = screen_num_desktops / l->rows;
1106 if (l->rows * l->columns < screen_num_desktops)
1108 if (l->rows * l->columns >= screen_num_desktops + l->columns)
1110 } else if (l->rows == 0) {
1111 l->rows = screen_num_desktops / l->columns;
1112 if (l->columns * l->rows < screen_num_desktops)
1114 if (l->columns * l->rows >= screen_num_desktops + l->rows)
1118 /* bounds checking */
1119 if (l->orientation == OB_ORIENTATION_HORZ) {
1120 l->columns = MIN(screen_num_desktops, l->columns);
1121 l->rows = MIN(l->rows,
1122 (screen_num_desktops + l->columns - 1) / l->columns);
1123 l->columns = screen_num_desktops / l->rows +
1124 !!(screen_num_desktops % l->rows);
1126 l->rows = MIN(screen_num_desktops, l->rows);
1127 l->columns = MIN(l->columns,
1128 (screen_num_desktops + l->rows - 1) / l->rows);
1129 l->rows = screen_num_desktops / l->columns +
1130 !!(screen_num_desktops % l->columns);
1135 void screen_update_layout(void)
1142 screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
1143 screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
1144 screen_desktop_layout.rows = 1;
1145 screen_desktop_layout.columns = screen_num_desktops;
1147 if (OBT_PROP_GETA32(obt_root(ob_screen),
1148 NET_DESKTOP_LAYOUT, CARDINAL, &data, &num)) {
1149 if (num == 3 || num == 4) {
1151 if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_VERT))
1152 l.orientation = OB_ORIENTATION_VERT;
1153 else if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_HORZ))
1154 l.orientation = OB_ORIENTATION_HORZ;
1159 l.start_corner = OB_CORNER_TOPLEFT;
1161 if (data[3] == OBT_PROP_ATOM(NET_WM_TOPLEFT))
1162 l.start_corner = OB_CORNER_TOPLEFT;
1163 else if (data[3] == OBT_PROP_ATOM(NET_WM_TOPRIGHT))
1164 l.start_corner = OB_CORNER_TOPRIGHT;
1165 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMRIGHT))
1166 l.start_corner = OB_CORNER_BOTTOMRIGHT;
1167 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMLEFT))
1168 l.start_corner = OB_CORNER_BOTTOMLEFT;
1173 l.columns = data[1];
1176 if (screen_validate_layout(&l))
1177 screen_desktop_layout = l;
1184 void screen_update_desktop_names(void)
1188 /* empty the array */
1189 g_strfreev(screen_desktop_names);
1190 screen_desktop_names = NULL;
1192 if (OBT_PROP_GETSS(obt_root(ob_screen),
1193 NET_DESKTOP_NAMES, &screen_desktop_names))
1194 for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
1197 if (i < screen_num_desktops) {
1200 screen_desktop_names = g_renew(gchar*, screen_desktop_names,
1201 screen_num_desktops + 1);
1202 screen_desktop_names[screen_num_desktops] = NULL;
1204 it = g_slist_nth(config_desktops_names, i);
1206 for (; i < screen_num_desktops; ++i) {
1207 if (it && ((char*)it->data)[0]) /* not empty */
1208 /* use the names from the config file when possible */
1209 screen_desktop_names[i] = g_strdup(it->data);
1211 /* make up a nice name if it's not though */
1212 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
1214 if (it) it = g_slist_next(it);
1217 /* if we changed any names, then set the root property so we can
1218 all agree on the names */
1219 OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
1220 (const gchar*const*)screen_desktop_names);
1223 /* resize the pager for these names */
1224 pager_popup_text_width_to_strings(desktop_popup,
1225 screen_desktop_names,
1226 screen_num_desktops);
1229 void screen_show_desktop(ObScreenShowDestopMode show_mode, ObClient *show_only)
1233 ObScreenShowDestopMode before_mode = screen_show_desktop_mode;
1235 gboolean showing_before = screen_showing_desktop();
1236 screen_show_desktop_mode = show_mode;
1237 gboolean showing_after = screen_showing_desktop();
1239 if (showing_before == showing_after) {
1241 screen_show_desktop_mode = before_mode;
1245 if (screen_show_desktop_mode == SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE &&
1248 /* If we're showing the desktop until the show-mode is toggled, we
1249 don't allow breaking out of showing-desktop mode unless we're
1250 showing all the windows again. */
1251 screen_show_desktop_mode = before_mode;
1255 if (showing_after) {
1256 /* hide windows bottom to top */
1257 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1258 if (WINDOW_IS_CLIENT(it->data)) {
1259 ObClient *client = it->data;
1260 client_showhide(client);
1265 /* restore windows top to bottom */
1266 for (it = stacking_list; it; it = g_list_next(it)) {
1267 if (WINDOW_IS_CLIENT(it->data)) {
1268 ObClient *client = it->data;
1269 if (client_should_show(client)) {
1270 if (!show_only || client == show_only)
1271 client_show(client);
1273 client_iconify(client, TRUE, FALSE, TRUE);
1279 if (showing_after) {
1280 /* focus the desktop */
1281 for (it = focus_order; it; it = g_list_next(it)) {
1282 ObClient *c = it->data;
1283 if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1284 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1285 client_focus(it->data))
1289 else if (!show_only) {
1292 if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
1293 /* only do the flicker reducing stuff ahead of time if we are going
1294 to call xsetinputfocus on the window ourselves. otherwise there
1295 is no guarantee the window will actually take focus.. */
1297 /* reduce flicker by hiliting now rather than waiting for the
1298 server FocusIn event */
1299 frame_adjust_focus(c->frame, TRUE);
1304 OBT_PROP_SET32(obt_root(ob_screen),
1305 NET_SHOWING_DESKTOP,
1310 gboolean screen_showing_desktop()
1312 switch (screen_show_desktop_mode) {
1313 case SCREEN_SHOW_DESKTOP_NO:
1315 case SCREEN_SHOW_DESKTOP_UNTIL_WINDOW:
1316 case SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE:
1319 g_assert_not_reached();
1323 void screen_install_colormap(ObClient *client, gboolean install)
1325 if (client == NULL || client->colormap == None) {
1327 XInstallColormap(obt_display, RrColormap(ob_rr_inst));
1329 XUninstallColormap(obt_display, RrColormap(ob_rr_inst));
1331 obt_display_ignore_errors(TRUE);
1333 XInstallColormap(obt_display, client->colormap);
1335 XUninstallColormap(obt_display, client->colormap);
1336 obt_display_ignore_errors(FALSE);
1342 StrutPartial *strut;
1345 #define RESET_STRUT_LIST(sl) \
1347 g_slice_free(ObScreenStrut, (sl)->data); \
1348 sl = g_slist_delete_link(sl, sl); \
1351 #define ADD_STRUT_TO_LIST(sl, d, s) \
1353 ObScreenStrut *ss = g_slice_new(ObScreenStrut); \
1356 sl = g_slist_prepend(sl, ss); \
1359 #define VALIDATE_STRUTS(sl, side, max) \
1362 for (it = sl; it; it = g_slist_next(it)) { \
1363 ObScreenStrut *ss = it->data; \
1364 ss->strut->side = MIN(max, ss->strut->side); \
1368 static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
1374 XineramaScreenInfo *info;
1377 if (ob_debug_xinerama) {
1378 gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1379 gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1381 *xin_areas = g_new(Rect, *nxin + 1);
1382 RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
1383 RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
1386 else if (obt_display_extension_xinerama &&
1387 (info = XineramaQueryScreens(obt_display, &n))) {
1389 *xin_areas = g_new(Rect, *nxin + 1);
1390 for (i = 0; i < *nxin; ++i)
1391 RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
1392 info[i].width, info[i].height);
1398 *xin_areas = g_new(Rect, *nxin + 1);
1399 RECT_SET((*xin_areas)[0], 0, 0,
1400 WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1401 HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1404 /* returns one extra with the total area in it */
1405 l = (*xin_areas)[0].x;
1406 t = (*xin_areas)[0].y;
1407 r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
1408 b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
1409 for (i = 1; i < *nxin; ++i) {
1410 l = MIN(l, (*xin_areas)[i].x);
1411 t = MIN(l, (*xin_areas)[i].y);
1412 r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
1413 b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
1415 RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
1417 for (i = 0; i < *nxin; ++i)
1418 ob_debug("Monitor %d @ %d,%d %dx%d\n", i,
1419 (*xin_areas)[i].x, (*xin_areas)[i].y,
1420 (*xin_areas)[i].width, (*xin_areas)[i].height);
1421 ob_debug("Full desktop @ %d,%d %dx%d\n",
1422 (*xin_areas)[i].x, (*xin_areas)[i].y,
1423 (*xin_areas)[i].width, (*xin_areas)[i].height);
1426 void screen_update_areas(void)
1430 GList *it, *onscreen;
1432 /* collect the clients that are on screen */
1434 for (it = client_list; it; it = g_list_next(it)) {
1435 if (client_monitor(it->data) != screen_num_monitors)
1436 onscreen = g_list_prepend(onscreen, it->data);
1439 g_free(monitor_area);
1440 get_xinerama_screens(&monitor_area, &screen_num_monitors);
1442 /* set up the user-specified margins */
1443 config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1444 config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1445 config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1446 config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1447 config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
1448 config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1449 config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
1450 config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1452 RESET_STRUT_LIST(struts_left);
1453 RESET_STRUT_LIST(struts_top);
1454 RESET_STRUT_LIST(struts_right);
1455 RESET_STRUT_LIST(struts_bottom);
1457 /* collect the struts */
1458 for (it = client_list; it; it = g_list_next(it)) {
1459 ObClient *c = it->data;
1461 ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
1463 ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
1465 ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
1466 if (c->strut.bottom)
1467 ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
1469 if (dock_strut.left)
1470 ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
1472 ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
1473 if (dock_strut.right)
1474 ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
1475 if (dock_strut.bottom)
1476 ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
1478 if (config_margins.left)
1479 ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
1480 if (config_margins.top)
1481 ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
1482 if (config_margins.right)
1483 ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
1484 if (config_margins.bottom)
1485 ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
1487 VALIDATE_STRUTS(struts_left, left,
1488 monitor_area[screen_num_monitors].width / 2);
1489 VALIDATE_STRUTS(struts_right, right,
1490 monitor_area[screen_num_monitors].width / 2);
1491 VALIDATE_STRUTS(struts_top, top,
1492 monitor_area[screen_num_monitors].height / 2);
1493 VALIDATE_STRUTS(struts_bottom, bottom,
1494 monitor_area[screen_num_monitors].height / 2);
1496 dims = g_new(gulong, 4 * screen_num_desktops);
1497 for (i = 0; i < screen_num_desktops; ++i) {
1498 Rect *area = screen_area(i, SCREEN_AREA_ALL_MONITORS, NULL);
1499 dims[i*4+0] = area->x;
1500 dims[i*4+1] = area->y;
1501 dims[i*4+2] = area->width;
1502 dims[i*4+3] = area->height;
1503 g_slice_free(Rect, area);
1506 /* set the legacy workarea hint to the union of all the monitors */
1507 OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL,
1508 dims, 4 * screen_num_desktops);
1510 /* the area has changed, adjust all the windows if they need it */
1511 for (it = onscreen; it; it = g_list_next(it))
1512 client_reconfigure(it->data, FALSE);
1518 Rect* screen_area_all_monitors(guint desktop)
1523 a = screen_area_monitor(desktop, 0);
1525 /* combine all the monitors together */
1526 for (i = 1; i < screen_num_monitors; ++i) {
1527 Rect *m = screen_area_monitor(desktop, i);
1530 l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
1531 t = MIN(RECT_TOP(*a), RECT_TOP(*m));
1532 r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
1533 b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
1535 RECT_SET(*a, l, t, r - l + 1, b - t + 1);
1544 #define STRUT_LEFT_IN_SEARCH(s, search) \
1545 (RANGES_INTERSECT(search->y, search->height, \
1546 s->left_start, s->left_end - s->left_start + 1))
1547 #define STRUT_RIGHT_IN_SEARCH(s, search) \
1548 (RANGES_INTERSECT(search->y, search->height, \
1549 s->right_start, s->right_end - s->right_start + 1))
1550 #define STRUT_TOP_IN_SEARCH(s, search) \
1551 (RANGES_INTERSECT(search->x, search->width, \
1552 s->top_start, s->top_end - s->top_start + 1))
1553 #define STRUT_BOTTOM_IN_SEARCH(s, search) \
1554 (RANGES_INTERSECT(search->x, search->width, \
1555 s->bottom_start, s->bottom_end - s->bottom_start + 1))
1557 #define STRUT_LEFT_IGNORE(s, us, search) \
1558 (head == SCREEN_AREA_ALL_MONITORS && us && \
1559 RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
1560 #define STRUT_RIGHT_IGNORE(s, us, search) \
1561 (head == SCREEN_AREA_ALL_MONITORS && us && \
1562 RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
1563 #define STRUT_TOP_IGNORE(s, us, search) \
1564 (head == SCREEN_AREA_ALL_MONITORS && us && \
1565 RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
1566 #define STRUT_BOTTOM_IGNORE(s, us, search) \
1567 (head == SCREEN_AREA_ALL_MONITORS && us && \
1568 RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
1570 Rect* screen_area(guint desktop, guint head, Rect *search)
1576 gboolean us = search != NULL; /* user provided search */
1578 g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
1579 g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
1580 head == SCREEN_AREA_ALL_MONITORS);
1581 g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
1583 /* find any struts for this monitor
1584 which will be affecting the search area.
1587 /* search everything if search is null */
1589 if (head < screen_num_monitors) search = &monitor_area[head];
1590 else search = &monitor_area[screen_num_monitors];
1592 if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
1594 /* al is "all left" meaning the furthest left you can get, l is our
1595 "working left" meaning our current strut edge which we're calculating
1598 /* only include monitors which the search area lines up with */
1599 if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
1600 l = RECT_RIGHT(monitor_area[screen_num_monitors]);
1601 t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1602 r = RECT_LEFT(monitor_area[screen_num_monitors]);
1603 b = RECT_TOP(monitor_area[screen_num_monitors]);
1604 for (i = 0; i < screen_num_monitors; ++i) {
1605 /* add the monitor if applicable */
1606 if (RANGES_INTERSECT(search->x, search->width,
1607 monitor_area[i].x, monitor_area[i].width))
1609 t = MIN(t, RECT_TOP(monitor_area[i]));
1610 b = MAX(b, RECT_BOTTOM(monitor_area[i]));
1612 if (RANGES_INTERSECT(search->y, search->height,
1613 monitor_area[i].y, monitor_area[i].height))
1615 l = MIN(l, RECT_LEFT(monitor_area[i]));
1616 r = MAX(r, RECT_RIGHT(monitor_area[i]));
1620 l = RECT_LEFT(monitor_area[screen_num_monitors]);
1621 t = RECT_TOP(monitor_area[screen_num_monitors]);
1622 r = RECT_RIGHT(monitor_area[screen_num_monitors]);
1623 b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1626 for (d = 0; d < screen_num_desktops; ++d) {
1627 if (d != desktop && desktop != DESKTOP_ALL) continue;
1629 for (i = 0; i < screen_num_monitors; ++i) {
1630 if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
1632 for (it = struts_left; it; it = g_slist_next(it)) {
1633 ObScreenStrut *s = it->data;
1634 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1635 STRUT_LEFT_IN_SEARCH(s->strut, search) &&
1636 !STRUT_LEFT_IGNORE(s->strut, us, search))
1637 l = MAX(l, RECT_LEFT(monitor_area[screen_num_monitors])
1640 for (it = struts_top; it; it = g_slist_next(it)) {
1641 ObScreenStrut *s = it->data;
1642 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1643 STRUT_TOP_IN_SEARCH(s->strut, search) &&
1644 !STRUT_TOP_IGNORE(s->strut, us, search))
1645 t = MAX(t, RECT_TOP(monitor_area[screen_num_monitors])
1648 for (it = struts_right; it; it = g_slist_next(it)) {
1649 ObScreenStrut *s = it->data;
1650 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1651 STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
1652 !STRUT_RIGHT_IGNORE(s->strut, us, search))
1653 r = MIN(r, RECT_RIGHT(monitor_area[screen_num_monitors])
1656 for (it = struts_bottom; it; it = g_slist_next(it)) {
1657 ObScreenStrut *s = it->data;
1658 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1659 STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
1660 !STRUT_BOTTOM_IGNORE(s->strut, us, search))
1661 b = MIN(b, RECT_BOTTOM(monitor_area[screen_num_monitors])
1662 - s->strut->bottom);
1665 /* limit to this monitor */
1667 l = MAX(l, RECT_LEFT(monitor_area[i]));
1668 t = MAX(t, RECT_TOP(monitor_area[i]));
1669 r = MIN(r, RECT_RIGHT(monitor_area[i]));
1670 b = MIN(b, RECT_BOTTOM(monitor_area[i]));
1675 a = g_slice_new(Rect);
1678 a->width = r - l + 1;
1679 a->height = b - t + 1;
1688 guint screen_find_monitor(const Rect *search)
1691 guint mostpx_index = screen_num_monitors;
1693 guint closest_distance_index = screen_num_monitors;
1694 guint closest_distance = G_MAXUINT;
1695 GSList *counted = NULL;
1697 /* we want to count the number of pixels search has on each monitor, but not
1698 double count. so if a pixel is counted on monitor A then we should not
1699 count it again on monitor B. in the end we want to return the monitor
1700 that had the most pixels counted under this scheme.
1702 this assumes that monitors earlier in the list are more desirable to be
1703 considered the search area's monitor. we try the configured primary
1704 monitor first, so it gets the highest preference.
1706 if we have counted an area A, then we want to subtract the intersection
1707 of A with the area on future monitors.
1708 but now consider if we count an area B that intersects A. we want to
1709 subtract the area B from that counted on future monitors, but not
1710 subtract the intersection of A and B twice! so we would add the
1711 intersection of A and B back, to account for it being subtracted both
1714 this is the idea behind the algorithm. we always subtract the full area
1715 for monitor M intersected with the search area. we'll call that AREA.
1716 but then we go through the list |counted| and for each rectangle in
1717 the list that is being subtracted from future monitors, we insert a
1718 request to add back the intersection of the subtracted rect with AREA.
1719 vice versa for a rect in |counted| that is getting added back.
1722 if (config_primary_monitor_index < screen_num_monitors) {
1723 const Rect *monitor;
1724 Rect on_current_monitor;
1727 monitor = screen_physical_area_monitor(config_primary_monitor_index);
1729 if (RECT_INTERSECTS_RECT(*monitor, *search)) {
1730 RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
1731 area = RECT_AREA(on_current_monitor);
1733 if (area > mostpx) {
1735 mostpx_index = config_primary_monitor_index;
1738 /* add the intersection rect on the current monitor to the
1739 counted list. that's easy for the first one, we just mark it for
1742 RectArithmetic *ra = g_slice_new(RectArithmetic);
1743 ra->r = on_current_monitor;
1744 ra->subtract = TRUE;
1745 counted = g_slist_prepend(counted, ra);
1750 for (i = 0; i < screen_num_monitors; ++i) {
1751 const Rect *monitor;
1752 Rect on_current_monitor;
1756 monitor = screen_physical_area_monitor(i);
1758 if (!RECT_INTERSECTS_RECT(*monitor, *search)) {
1759 /* If we don't intersect then find the distance between the search
1760 rect and the monitor. We'll use the closest monitor from this
1761 metric if none of the monitors intersect. */
1762 guint distance = rect_manhatten_distance(*monitor, *search);
1764 if (distance < closest_distance) {
1765 closest_distance = distance;
1766 closest_distance_index = i;
1771 if (i == config_primary_monitor_index)
1772 continue; /* already did this one */
1774 RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
1775 area = RECT_AREA(on_current_monitor);
1777 /* remove pixels we already counted on any previous monitors. */
1778 for (it = counted; it; it = g_slist_next(it)) {
1779 RectArithmetic *ra = it->data;
1782 RECT_SET_INTERSECTION(intersection, ra->r, *search);
1783 if (ra->subtract) area -= RECT_AREA(intersection);
1784 else area += RECT_AREA(intersection);
1787 if (area > mostpx) {
1792 /* add the intersection rect on the current monitor I to the counted
1794 but now we need to compensate for every rectangle R already in the
1795 counted list, and add a new rect R' that is the intersection of
1796 R and I, but with the reverse subtraction/addition operation.
1798 for (it = counted; it; it = g_slist_next(it)) {
1799 RectArithmetic *saved = it->data;
1801 if (!RECT_INTERSECTS_RECT(saved->r, on_current_monitor))
1803 /* we are going to subtract our rect from future monitors, but
1804 part of it may already be being subtracted/added, so compensate
1805 to not double add/subtract. */
1806 RectArithmetic *reverse = g_slice_new(RectArithmetic);
1807 RECT_SET_INTERSECTION(reverse->r, saved->r, on_current_monitor);
1808 reverse->subtract = !saved->subtract;
1809 /* prepend so we can continue thru the list uninterupted */
1810 counted = g_slist_prepend(counted, reverse);
1813 RectArithmetic *ra = g_slice_new(RectArithmetic);
1814 ra->r = on_current_monitor;
1815 ra->subtract = TRUE;
1816 counted = g_slist_prepend(counted, ra);
1821 g_slice_free(RectArithmetic, counted->data);
1822 counted = g_slist_delete_link(counted, counted);
1825 if (mostpx_index < screen_num_monitors)
1826 return mostpx_index;
1828 g_assert(closest_distance_index < screen_num_monitors);
1829 return closest_distance_index;
1832 const Rect* screen_physical_area_all_monitors(void)
1834 return screen_physical_area_monitor(screen_num_monitors);
1837 const Rect* screen_physical_area_monitor(guint head)
1839 g_assert(head <= screen_num_monitors);
1841 return &monitor_area[head];
1844 gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
1846 g_assert(head <= screen_num_monitors);
1848 return RECT_INTERSECTS_RECT(monitor_area[head], *search);
1851 guint screen_monitor_active(void)
1853 if (moveresize_client)
1854 return client_monitor(moveresize_client);
1855 else if (focus_client)
1856 return client_monitor(focus_client);
1858 return screen_monitor_pointer();
1861 const Rect* screen_physical_area_active(void)
1863 return screen_physical_area_monitor(screen_monitor_active());
1866 guint screen_monitor_primary(gboolean fixed)
1868 if (config_primary_monitor_index > 0) {
1869 if (config_primary_monitor_index-1 < screen_num_monitors)
1870 return config_primary_monitor_index - 1;
1876 else if (config_primary_monitor == OB_PLACE_MONITOR_ACTIVE)
1877 return screen_monitor_active();
1878 else /* config_primary_monitor == OB_PLACE_MONITOR_MOUSE */
1879 return screen_monitor_pointer();
1882 const Rect* screen_physical_area_primary(gboolean fixed)
1884 return screen_physical_area_monitor(screen_monitor_primary(fixed));
1887 void screen_set_root_cursor(void)
1889 if (sn_app_starting())
1890 XDefineCursor(obt_display, obt_root(ob_screen),
1891 ob_cursor(OB_CURSOR_BUSY));
1893 XDefineCursor(obt_display, obt_root(ob_screen),
1894 ob_cursor(OB_CURSOR_POINTER));
1897 guint screen_find_monitor_point(guint x, guint y)
1900 RECT_SET(mon, x, y, 1, 1);
1901 return screen_find_monitor(&mon);
1904 guint screen_monitor_pointer()
1907 if (!screen_pointer_pos(&x, &y))
1909 return screen_find_monitor_point(x, y);
1912 gboolean screen_pointer_pos(gint *x, gint *y)
1919 ret = !!XQueryPointer(obt_display, obt_root(ob_screen),
1920 &w, &w, x, y, &i, &i, &u);
1922 for (i = 0; i < ScreenCount(obt_display); ++i)
1924 if (XQueryPointer(obt_display, obt_root(i),
1925 &w, &w, x, y, &i, &i, &u))
1931 gboolean screen_compare_desktops(guint a, guint b)
1933 if (a == DESKTOP_ALL)
1935 if (b == DESKTOP_ALL)
1940 void screen_apply_gravity_point(gint *x, gint *y, gint width, gint height,
1941 const GravityPoint *position, const Rect *area)
1943 if (position->x.center)
1944 *x = area->width / 2 - width / 2;
1946 *x = position->x.pos;
1947 if (position->x.denom)
1948 *x = (*x * area->width) / position->x.denom;
1949 if (position->x.opposite)
1950 *x = area->width - width - *x;
1953 if (position->y.center)
1954 *y = area->height / 2 - height / 2;
1956 *y = position->y.pos;
1957 if (position->y.denom)
1958 *y = (*y * area->height) / position->y.denom;
1959 if (position->y.opposite)
1960 *y = area->height - height - *y;