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
gboolean screen_showing_desktop;
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;
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_ACTION_MOVE);
253
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
254
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
255
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
256
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
257
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
258
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
259
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
260
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
261
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
262
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
263
supported[i++] = OBT_PROP_ATOM(NET_WM_STATE);
264
supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
265
supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
266
supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
267
supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
268
supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
269
supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
270
supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
271
supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
272
supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
273
supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
274
supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
275
supported[i++] = OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW);
276
supported[i++] = OBT_PROP_ATOM(NET_WM_MOVERESIZE);
277
supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME);
279
supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME_WINDOW);
281
supported[i++] = OBT_PROP_ATOM(NET_FRAME_EXTENTS);
282
supported[i++] = OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS);
283
supported[i++] = OBT_PROP_ATOM(NET_RESTACK_WINDOW);
284
supported[i++] = OBT_PROP_ATOM(NET_STARTUP_ID);
286
supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
287
supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER);
289
supported[i++] = OBT_PROP_ATOM(NET_WM_PID);
290
supported[i++] = OBT_PROP_ATOM(NET_WM_PING);
292
supported[i++] = OBT_PROP_ATOM(KDE_WM_CHANGE_STATE);
293
supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_FRAME_STRUT);
294
supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
296
supported[i++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
297
supported[i++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
298
supported[i++] = OBT_PROP_ATOM(OPENBOX_PID);
299
supported[i++] = OBT_PROP_ATOM(OB_THEME);
300
supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE);
301
supported[i++] = OBT_PROP_ATOM(OB_CONTROL);
302
supported[i++] = OBT_PROP_ATOM(OB_VERSION);
303
supported[i++] = OBT_PROP_ATOM(OB_APP_ROLE);
304
supported[i++] = OBT_PROP_ATOM(OB_APP_TITLE);
305
supported[i++] = OBT_PROP_ATOM(OB_APP_NAME);
306
supported[i++] = OBT_PROP_ATOM(OB_APP_CLASS);
307
supported[i++] = OBT_PROP_ATOM(OB_APP_TYPE);
308
g_assert(i == num_support);
310
OBT_PROP_SETA32(obt_root(ob_screen),
311
NET_SUPPORTED, ATOM, supported, num_support);
314
OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION,
317
screen_tell_ksplash();
322
static void screen_tell_ksplash(void)
327
argv = g_new(gchar*, 6);
328
argv[0] = g_strdup("dcop");
329
argv[1] = g_strdup("ksplash");
330
argv[2] = g_strdup("ksplash");
331
argv[3] = g_strdup("upAndRunning(QString)");
332
argv[4] = g_strdup("wm started");
335
/* tell ksplash through the dcop server command line interface */
336
g_spawn_async(NULL, argv, NULL,
337
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD |
338
G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
339
NULL, NULL, NULL, NULL);
342
/* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
343
hear it anyways. perhaps it is for old ksplash. or new ksplash. or
344
something. oh well. */
345
e.xclient.type = ClientMessage;
346
e.xclient.display = obt_display;
347
e.xclient.window = obt_root(ob_screen);
348
e.xclient.message_type =
349
XInternAtom(obt_display, "_KDE_SPLASH_PROGRESS", False);
350
e.xclient.format = 8;
351
strcpy(e.xclient.data.b, "wm started");
352
XSendEvent(obt_display, obt_root(ob_screen),
353
False, SubstructureNotifyMask, &e);
356
void screen_startup(gboolean reconfig)
358
gchar **names = NULL;
360
gboolean namesexist = FALSE;
362
desktop_popup = pager_popup_new();
363
desktop_popup_perm = FALSE;
364
pager_popup_height(desktop_popup, POPUP_HEIGHT);
367
/* update the pager popup's width */
368
pager_popup_text_width_to_strings(desktop_popup,
369
screen_desktop_names,
370
screen_num_desktops);
374
/* get the initial size */
377
/* have names already been set for the desktops? */
378
if (OBT_PROP_GETSS_UTF8(obt_root(ob_screen), NET_DESKTOP_NAMES, &names)) {
383
/* if names don't exist and we have session names, set those.
384
do this stuff BEFORE setting the number of desktops, because that
385
will create default names for them
387
if (!namesexist && session_desktop_names != NULL) {
391
/* get the desktop names */
392
numnames = g_slist_length(session_desktop_names);
393
names = g_new(gchar*, numnames + 1);
394
names[numnames] = NULL;
395
for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
396
names[i] = g_strdup(it->data);
398
/* set the root window property */
399
OBT_PROP_SETSS(obt_root(ob_screen),
400
NET_DESKTOP_NAMES, (const gchar*const*)names);
405
/* set the number of desktops, if it's not already set.
407
this will also set the default names from the config file up for
408
desktops that don't have names yet */
409
screen_num_desktops = 0;
410
if (OBT_PROP_GET32(obt_root(ob_screen),
411
NET_NUMBER_OF_DESKTOPS, CARDINAL, &d))
413
if (d != config_desktops_num) {
414
/* TRANSLATORS: If you need to specify a different order of the
415
arguments, you can use %1$d for the first one and %2$d for the
416
second one. For example,
417
"The current session has %2$d desktops, but Openbox is configured for %1$d ..." */
418
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),
419
config_desktops_num, d);
421
screen_set_num_desktops(d);
423
/* restore from session if possible */
424
else if (session_num_desktops)
425
screen_set_num_desktops(session_num_desktops);
427
screen_set_num_desktops(config_desktops_num);
429
screen_desktop = screen_num_desktops; /* something invalid */
430
/* start on the current desktop when a wm was already running */
431
if (OBT_PROP_GET32(obt_root(ob_screen),
432
NET_CURRENT_DESKTOP, CARDINAL, &d) &&
433
d < screen_num_desktops)
435
screen_set_desktop(d, FALSE);
436
} else if (session_desktop >= 0)
437
screen_set_desktop(MIN((guint)session_desktop,
438
screen_num_desktops), FALSE);
440
screen_set_desktop(MIN(config_screen_firstdesk,
441
screen_num_desktops) - 1, FALSE);
442
screen_last_desktop = screen_desktop;
444
/* don't start in showing-desktop mode */
445
screen_showing_desktop = FALSE;
446
OBT_PROP_SET32(obt_root(ob_screen),
447
NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop);
449
if (session_desktop_layout_present &&
450
screen_validate_layout(&session_desktop_layout))
452
screen_desktop_layout = session_desktop_layout;
455
screen_update_layout();
458
void screen_shutdown(gboolean reconfig)
460
pager_popup_free(desktop_popup);
465
XSelectInput(obt_display, obt_root(ob_screen), NoEventMask);
467
/* we're not running here no more! */
468
OBT_PROP_ERASE(obt_root(ob_screen), OPENBOX_PID);
470
OBT_PROP_ERASE(obt_root(ob_screen), NET_SUPPORTED);
471
/* don't keep this mode */
472
OBT_PROP_ERASE(obt_root(ob_screen), NET_SHOWING_DESKTOP);
474
XDestroyWindow(obt_display, screen_support_win);
476
g_strfreev(screen_desktop_names);
477
screen_desktop_names = NULL;
480
void screen_resize(void)
482
static gint oldw = 0, oldh = 0;
487
w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
488
h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
490
if (w == oldw && h == oldh) return;
494
/* Set the _NET_DESKTOP_GEOMETRY hint */
495
screen_physical_size.width = geometry[0] = w;
496
screen_physical_size.height = geometry[1] = h;
497
OBT_PROP_SETA32(obt_root(ob_screen),
498
NET_DESKTOP_GEOMETRY, CARDINAL, geometry, 2);
500
if (ob_state() != OB_STATE_RUNNING)
503
/* this calls screen_update_areas(), which we need ! */
507
for (it = client_list; it; it = g_list_next(it))
508
client_move_onscreen(it->data, FALSE);
511
void screen_set_num_desktops(guint num)
514
GList *it, *stacking_copy;
518
if (screen_num_desktops == num) return;
520
screen_num_desktops = num;
521
OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num);
523
/* set the viewport hint */
524
viewport = g_new0(gulong, num * 2);
525
OBT_PROP_SETA32(obt_root(ob_screen),
526
NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2);
529
/* the number of rows/columns will differ */
530
screen_update_layout();
532
/* move windows on desktops that will no longer exist!
533
make a copy of the list cuz we're changing it */
534
stacking_copy = g_list_copy(stacking_list);
535
for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
536
if (WINDOW_IS_CLIENT(it->data)) {
537
ObClient *c = it->data;
538
if (c->desktop != DESKTOP_ALL && c->desktop >= num)
539
client_set_desktop(c, num - 1, FALSE, TRUE);
540
/* raise all the windows that are on the current desktop which
542
else if (screen_desktop == num - 1 &&
543
(c->desktop == DESKTOP_ALL ||
544
c->desktop == screen_desktop))
545
stacking_raise(CLIENT_AS_WINDOW(c));
548
g_list_free(stacking_copy);
550
/* change our struts/area to match (after moving windows) */
551
screen_update_areas();
553
/* may be some unnamed desktops that we need to fill in with names
554
(after updating the areas so the popup can resize) */
555
screen_update_desktop_names();
557
/* change our desktop if we're on one that no longer exists! */
558
if (screen_desktop >= screen_num_desktops)
559
screen_set_desktop(num - 1, TRUE);
562
static void screen_fallback_focus(void)
567
/* only allow omnipresent windows to get focus on desktop change if
568
an omnipresent window is already focused (it'll keep focus probably, but
569
maybe not depending on mouse-focus options) */
570
allow_omni = focus_client && (client_normal(focus_client) &&
571
focus_client->desktop == DESKTOP_ALL);
573
/* the client moved there already so don't move focus. prevent flicker
574
on sendtodesktop + follow */
575
if (focus_client && focus_client->desktop == screen_desktop)
578
/* have to try focus here because when you leave an empty desktop
579
there is no focus out to watch for. also, we have different rules
580
here. we always allow it to look under the mouse pointer if
581
config_focus_last is FALSE
583
do this before hiding the windows so if helper windows are coming
584
with us, they don't get hidden
586
if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
589
/* only do the flicker reducing stuff ahead of time if we are going
590
to call xsetinputfocus on the window ourselves. otherwise there is
591
no guarantee the window will actually take focus.. */
593
/* reduce flicker by hiliting now rather than waiting for the
594
server FocusIn event */
595
frame_adjust_focus(c->frame, TRUE);
596
/* do this here so that if you switch desktops to a window with
597
helper windows then the helper windows won't flash */
598
client_bring_helper_windows(c);
603
static gboolean last_desktop_func(gpointer data)
605
screen_desktop_timeout = TRUE;
606
screen_desktop_timer = 0;
607
return FALSE; /* don't repeat */
610
void screen_set_desktop(guint num, gboolean dofocus)
616
g_assert(num < screen_num_desktops);
618
previous = screen_desktop;
619
screen_desktop = num;
621
if (previous == num) return;
623
OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num);
625
/* This whole thing decides when/how to save the screen_last_desktop so
626
that it can be restored later if you want */
627
if (screen_desktop_timeout) {
628
/* If screen_desktop_timeout is true, then we've been on this desktop
629
long enough and we can save it as the last desktop. */
631
if (screen_last_desktop == previous)
632
/* this is the startup state only */
633
screen_old_desktop = screen_desktop;
635
/* save the "last desktop" as the "old desktop" */
636
screen_old_desktop = screen_last_desktop;
637
/* save the desktop we're coming from as the "last desktop" */
638
screen_last_desktop = previous;
642
/* If screen_desktop_timeout is false, then we just got to this desktop
643
and we are moving away again. */
645
if (screen_desktop == screen_last_desktop) {
646
/* If we are moving to the "last desktop" .. */
647
if (previous == screen_old_desktop) {
648
/* .. from the "old desktop", change the last desktop to
649
be where we are coming from */
650
screen_last_desktop = screen_old_desktop;
652
else if (screen_last_desktop == screen_old_desktop) {
653
/* .. and also to the "old desktop", change the "last
654
desktop" to be where we are coming from */
655
screen_last_desktop = previous;
658
/* .. from some other desktop, then set the "last desktop" to
659
be the saved "old desktop", i.e. where we were before the
661
screen_last_desktop = screen_old_desktop;
665
/* If we are moving to any desktop besides the "last desktop"..
666
(this is the normal case) */
667
if (screen_desktop == screen_old_desktop) {
668
/* If moving to the "old desktop", which is not the
669
"last desktop", don't save anything */
671
else if (previous == screen_old_desktop) {
672
/* If moving from the "old desktop", and not to the
673
"last desktop", don't save anything */
675
else if (screen_last_desktop == screen_old_desktop) {
676
/* If the "last desktop" is the same as "old desktop" and
677
you're not moving to the "last desktop" then save where
678
we're coming from as the "last desktop" */
679
screen_last_desktop = previous;
682
/* If the "last desktop" is different from the "old desktop"
683
and you're not moving to the "last desktop", then don't save
688
screen_desktop_timeout = FALSE;
689
if (screen_desktop_timer) g_source_remove(screen_desktop_timer);
690
screen_desktop_timer = g_timeout_add(REMEMBER_LAST_DESKTOP_TIME,
691
last_desktop_func, NULL);
693
ob_debug("Moving to desktop %d", num+1);
695
if (ob_state() == OB_STATE_RUNNING)
696
screen_show_desktop_popup(screen_desktop, FALSE);
698
/* ignore enter events caused by the move */
699
ignore_start = event_start_ignore_all_enters();
701
if (moveresize_client)
702
client_set_desktop(moveresize_client, num, TRUE, FALSE);
704
/* show windows before hiding the rest to lessen the enter/leave events */
706
/* show windows from top to bottom */
707
for (it = stacking_list; it; it = g_list_next(it)) {
708
if (WINDOW_IS_CLIENT(it->data)) {
709
ObClient *c = it->data;
714
if (dofocus) screen_fallback_focus();
716
/* hide windows from bottom to top */
717
for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
718
if (WINDOW_IS_CLIENT(it->data)) {
719
ObClient *c = it->data;
720
if (client_hide(c)) {
721
if (c == focus_client) {
722
/* c was focused and we didn't do fallback clearly so make
723
sure openbox doesnt still consider the window focused.
724
this happens when using NextWindow with allDesktops,
725
since it doesnt want to move focus on desktop change,
726
but the focus is not going to stay with the current
727
window, which has now disappeared.
728
only do this if the client was actually hidden,
729
otherwise it can keep focus. */
730
focus_set_client(NULL);
736
focus_cycle_addremove(NULL, TRUE);
738
event_end_ignore_all_enters(ignore_start);
740
if (event_source_time() != CurrentTime)
741
screen_desktop_user_time = event_source_time();
744
void screen_add_desktop(gboolean current)
748
/* ignore enter events caused by this */
749
ignore_start = event_start_ignore_all_enters();
751
screen_set_num_desktops(screen_num_desktops+1);
753
/* move all the clients over */
757
for (it = client_list; it; it = g_list_next(it)) {
758
ObClient *c = it->data;
759
if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
760
/* don't move direct children, they'll be moved with their
761
parent - which will have to be on the same desktop */
762
!client_direct_parent(c))
764
ob_debug("moving window %s", c->title);
765
client_set_desktop(c, c->desktop+1, FALSE, TRUE);
770
event_end_ignore_all_enters(ignore_start);
773
void screen_remove_desktop(gboolean current)
775
guint rmdesktop, movedesktop;
776
GList *it, *stacking_copy;
779
if (screen_num_desktops <= 1) return;
781
/* ignore enter events caused by this */
782
ignore_start = event_start_ignore_all_enters();
784
/* what desktop are we removing and moving to? */
786
rmdesktop = screen_desktop;
788
rmdesktop = screen_num_desktops - 1;
789
if (rmdesktop < screen_num_desktops - 1)
790
movedesktop = rmdesktop + 1;
792
movedesktop = rmdesktop;
794
/* make a copy of the list cuz we're changing it */
795
stacking_copy = g_list_copy(stacking_list);
796
for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
797
if (WINDOW_IS_CLIENT(it->data)) {
798
ObClient *c = it->data;
799
guint d = c->desktop;
800
if (d != DESKTOP_ALL && d >= movedesktop &&
801
/* don't move direct children, they'll be moved with their
802
parent - which will have to be on the same desktop */
803
!client_direct_parent(c))
805
ob_debug("moving window %s", c->title);
806
client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
808
/* raise all the windows that are on the current desktop which
810
if ((screen_desktop == rmdesktop - 1 ||
811
screen_desktop == rmdesktop) &&
812
(d == DESKTOP_ALL || d == screen_desktop))
814
stacking_raise(CLIENT_AS_WINDOW(c));
815
ob_debug("raising window %s", c->title);
819
g_list_free(stacking_copy);
821
/* fallback focus like we're changing desktops */
822
if (screen_desktop < screen_num_desktops - 1) {
823
screen_fallback_focus();
824
ob_debug("fake desktop change");
827
screen_set_num_desktops(screen_num_desktops-1);
829
event_end_ignore_all_enters(ignore_start);
832
static void get_row_col(guint d, guint *r, guint *c)
834
switch (screen_desktop_layout.orientation) {
835
case OB_ORIENTATION_HORZ:
836
switch (screen_desktop_layout.start_corner) {
837
case OB_CORNER_TOPLEFT:
838
*r = d / screen_desktop_layout.columns;
839
*c = d % screen_desktop_layout.columns;
841
case OB_CORNER_BOTTOMLEFT:
842
*r = screen_desktop_layout.rows - 1 -
843
d / screen_desktop_layout.columns;
844
*c = d % screen_desktop_layout.columns;
846
case OB_CORNER_TOPRIGHT:
847
*r = d / screen_desktop_layout.columns;
848
*c = screen_desktop_layout.columns - 1 -
849
d % screen_desktop_layout.columns;
851
case OB_CORNER_BOTTOMRIGHT:
852
*r = screen_desktop_layout.rows - 1 -
853
d / screen_desktop_layout.columns;
854
*c = screen_desktop_layout.columns - 1 -
855
d % screen_desktop_layout.columns;
859
case OB_ORIENTATION_VERT:
860
switch (screen_desktop_layout.start_corner) {
861
case OB_CORNER_TOPLEFT:
862
*r = d % screen_desktop_layout.rows;
863
*c = d / screen_desktop_layout.rows;
865
case OB_CORNER_BOTTOMLEFT:
866
*r = screen_desktop_layout.rows - 1 -
867
d % screen_desktop_layout.rows;
868
*c = d / screen_desktop_layout.rows;
870
case OB_CORNER_TOPRIGHT:
871
*r = d % screen_desktop_layout.rows;
872
*c = screen_desktop_layout.columns - 1 -
873
d / screen_desktop_layout.rows;
875
case OB_CORNER_BOTTOMRIGHT:
876
*r = screen_desktop_layout.rows - 1 -
877
d % screen_desktop_layout.rows;
878
*c = screen_desktop_layout.columns - 1 -
879
d / screen_desktop_layout.rows;
886
static guint translate_row_col(guint r, guint c)
888
switch (screen_desktop_layout.orientation) {
889
case OB_ORIENTATION_HORZ:
890
switch (screen_desktop_layout.start_corner) {
891
case OB_CORNER_TOPLEFT:
892
return r % screen_desktop_layout.rows *
893
screen_desktop_layout.columns +
894
c % screen_desktop_layout.columns;
895
case OB_CORNER_BOTTOMLEFT:
896
return (screen_desktop_layout.rows - 1 -
897
r % screen_desktop_layout.rows) *
898
screen_desktop_layout.columns +
899
c % screen_desktop_layout.columns;
900
case OB_CORNER_TOPRIGHT:
901
return r % screen_desktop_layout.rows *
902
screen_desktop_layout.columns +
903
(screen_desktop_layout.columns - 1 -
904
c % screen_desktop_layout.columns);
905
case OB_CORNER_BOTTOMRIGHT:
906
return (screen_desktop_layout.rows - 1 -
907
r % screen_desktop_layout.rows) *
908
screen_desktop_layout.columns +
909
(screen_desktop_layout.columns - 1 -
910
c % screen_desktop_layout.columns);
912
case OB_ORIENTATION_VERT:
913
switch (screen_desktop_layout.start_corner) {
914
case OB_CORNER_TOPLEFT:
915
return c % screen_desktop_layout.columns *
916
screen_desktop_layout.rows +
917
r % screen_desktop_layout.rows;
918
case OB_CORNER_BOTTOMLEFT:
919
return c % screen_desktop_layout.columns *
920
screen_desktop_layout.rows +
921
(screen_desktop_layout.rows - 1 -
922
r % screen_desktop_layout.rows);
923
case OB_CORNER_TOPRIGHT:
924
return (screen_desktop_layout.columns - 1 -
925
c % screen_desktop_layout.columns) *
926
screen_desktop_layout.rows +
927
r % screen_desktop_layout.rows;
928
case OB_CORNER_BOTTOMRIGHT:
929
return (screen_desktop_layout.columns - 1 -
930
c % screen_desktop_layout.columns) *
931
screen_desktop_layout.rows +
932
(screen_desktop_layout.rows - 1 -
933
r % screen_desktop_layout.rows);
936
g_assert_not_reached();
940
static gboolean hide_desktop_popup_func(gpointer data)
942
pager_popup_hide(desktop_popup);
943
desktop_popup_timer = 0;
944
return FALSE; /* don't repeat */
947
void screen_show_desktop_popup(guint d, gboolean perm)
951
/* 0 means don't show the popup */
952
if (!config_desktop_popup_time) return;
954
a = screen_physical_area_primary(FALSE);
955
pager_popup_position(desktop_popup, CenterGravity,
956
a->x + a->width / 2, a->y + a->height / 2);
957
pager_popup_icon_size_multiplier(desktop_popup,
958
(screen_desktop_layout.columns /
959
screen_desktop_layout.rows) / 2,
960
(screen_desktop_layout.rows/
961
screen_desktop_layout.columns) / 2);
962
pager_popup_max_width(desktop_popup,
963
MAX(a->width/3, POPUP_WIDTH));
964
pager_popup_show(desktop_popup, screen_desktop_names[d], d);
966
if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
967
desktop_popup_timer = 0;
968
if (!perm && !desktop_popup_perm)
969
/* only hide if its not already being show permanently */
970
desktop_popup_timer = g_timeout_add(config_desktop_popup_time,
971
hide_desktop_popup_func,
974
desktop_popup_perm = TRUE;
977
void screen_hide_desktop_popup(void)
979
if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
980
desktop_popup_timer = 0;
981
pager_popup_hide(desktop_popup);
982
desktop_popup_perm = FALSE;
985
guint screen_find_desktop(guint from, ObDirection dir,
986
gboolean wrap, gboolean linear)
992
get_row_col(d, &r, &c);
995
case OB_DIRECTION_EAST:
996
if (d < screen_num_desktops - 1)
1003
case OB_DIRECTION_WEST:
1007
d = screen_num_desktops - 1;
1012
g_assert_not_reached();
1017
case OB_DIRECTION_EAST:
1019
if (c >= screen_desktop_layout.columns) {
1025
d = translate_row_col(r, c);
1026
if (d >= screen_num_desktops) {
1033
case OB_DIRECTION_WEST:
1035
if (c >= screen_desktop_layout.columns) {
1037
c = screen_desktop_layout.columns - 1;
1041
d = translate_row_col(r, c);
1042
if (d >= screen_num_desktops) {
1049
case OB_DIRECTION_SOUTH:
1051
if (r >= screen_desktop_layout.rows) {
1057
d = translate_row_col(r, c);
1058
if (d >= screen_num_desktops) {
1065
case OB_DIRECTION_NORTH:
1067
if (r >= screen_desktop_layout.rows) {
1069
r = screen_desktop_layout.rows - 1;
1073
d = translate_row_col(r, c);
1074
if (d >= screen_num_desktops) {
1082
g_assert_not_reached();
1086
d = translate_row_col(r, c);
1091
static gboolean screen_validate_layout(ObDesktopLayout *l)
1093
if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
1096
/* fill in a zero rows/columns */
1097
if (l->columns == 0) {
1098
l->columns = screen_num_desktops / l->rows;
1099
if (l->rows * l->columns < screen_num_desktops)
1101
if (l->rows * l->columns >= screen_num_desktops + l->columns)
1103
} else if (l->rows == 0) {
1104
l->rows = screen_num_desktops / l->columns;
1105
if (l->columns * l->rows < screen_num_desktops)
1107
if (l->columns * l->rows >= screen_num_desktops + l->rows)
1111
/* bounds checking */
1112
if (l->orientation == OB_ORIENTATION_HORZ) {
1113
l->columns = MIN(screen_num_desktops, l->columns);
1114
l->rows = MIN(l->rows,
1115
(screen_num_desktops + l->columns - 1) / l->columns);
1116
l->columns = screen_num_desktops / l->rows +
1117
!!(screen_num_desktops % l->rows);
1119
l->rows = MIN(screen_num_desktops, l->rows);
1120
l->columns = MIN(l->columns,
1121
(screen_num_desktops + l->rows - 1) / l->rows);
1122
l->rows = screen_num_desktops / l->columns +
1123
!!(screen_num_desktops % l->columns);
1128
void screen_update_layout(void)
1135
screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
1136
screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
1137
screen_desktop_layout.rows = 1;
1138
screen_desktop_layout.columns = screen_num_desktops;
1140
if (OBT_PROP_GETA32(obt_root(ob_screen),
1141
NET_DESKTOP_LAYOUT, CARDINAL, &data, &num)) {
1142
if (num == 3 || num == 4) {
1144
if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_VERT))
1145
l.orientation = OB_ORIENTATION_VERT;
1146
else if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_HORZ))
1147
l.orientation = OB_ORIENTATION_HORZ;
1152
l.start_corner = OB_CORNER_TOPLEFT;
1154
if (data[3] == OBT_PROP_ATOM(NET_WM_TOPLEFT))
1155
l.start_corner = OB_CORNER_TOPLEFT;
1156
else if (data[3] == OBT_PROP_ATOM(NET_WM_TOPRIGHT))
1157
l.start_corner = OB_CORNER_TOPRIGHT;
1158
else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMRIGHT))
1159
l.start_corner = OB_CORNER_BOTTOMRIGHT;
1160
else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMLEFT))
1161
l.start_corner = OB_CORNER_BOTTOMLEFT;
1166
l.columns = data[1];
1169
if (screen_validate_layout(&l))
1170
screen_desktop_layout = l;
1177
void screen_update_desktop_names(void)
1181
/* empty the array */
1182
g_strfreev(screen_desktop_names);
1183
screen_desktop_names = NULL;
1185
if (OBT_PROP_GETSS(obt_root(ob_screen),
1186
NET_DESKTOP_NAMES, &screen_desktop_names))
1187
for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
1190
if (i < screen_num_desktops) {
1193
screen_desktop_names = g_renew(gchar*, screen_desktop_names,
1194
screen_num_desktops + 1);
1195
screen_desktop_names[screen_num_desktops] = NULL;
1197
it = g_slist_nth(config_desktops_names, i);
1199
for (; i < screen_num_desktops; ++i) {
1200
if (it && ((char*)it->data)[0]) /* not empty */
1201
/* use the names from the config file when possible */
1202
screen_desktop_names[i] = g_strdup(it->data);
1204
/* make up a nice name if it's not though */
1205
screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
1207
if (it) it = g_slist_next(it);
1210
/* if we changed any names, then set the root property so we can
1211
all agree on the names */
1212
OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
1213
(const gchar*const*)screen_desktop_names);
1216
/* resize the pager for these names */
1217
pager_popup_text_width_to_strings(desktop_popup,
1218
screen_desktop_names,
1219
screen_num_desktops);
1222
void screen_show_desktop(gboolean show, ObClient *show_only)
1226
if (show == screen_showing_desktop) return; /* no change */
1228
screen_showing_desktop = show;
1231
/* hide windows bottom to top */
1232
for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1233
if (WINDOW_IS_CLIENT(it->data)) {
1234
ObClient *client = it->data;
1235
client_showhide(client);
1240
/* restore windows top to bottom */
1241
for (it = stacking_list; it; it = g_list_next(it)) {
1242
if (WINDOW_IS_CLIENT(it->data)) {
1243
ObClient *client = it->data;
1244
if (client_should_show(client)) {
1245
if (!show_only || client == show_only)
1246
client_show(client);
1248
client_iconify(client, TRUE, FALSE, TRUE);
1255
/* focus the desktop */
1256
for (it = focus_order; it; it = g_list_next(it)) {
1257
ObClient *c = it->data;
1258
if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1259
(c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1260
client_focus(it->data))
1264
else if (!show_only) {
1267
if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
1268
/* only do the flicker reducing stuff ahead of time if we are going
1269
to call xsetinputfocus on the window ourselves. otherwise there
1270
is no guarantee the window will actually take focus.. */
1272
/* reduce flicker by hiliting now rather than waiting for the
1273
server FocusIn event */
1274
frame_adjust_focus(c->frame, TRUE);
1279
show = !!show; /* make it boolean */
1280
OBT_PROP_SET32(obt_root(ob_screen), NET_SHOWING_DESKTOP, CARDINAL, show);
1283
void screen_install_colormap(ObClient *client, gboolean install)
1285
if (client == NULL || client->colormap == None) {
1287
XInstallColormap(obt_display, RrColormap(ob_rr_inst));
1289
XUninstallColormap(obt_display, RrColormap(ob_rr_inst));
1291
obt_display_ignore_errors(TRUE);
1293
XInstallColormap(obt_display, client->colormap);
1295
XUninstallColormap(obt_display, client->colormap);
1296
obt_display_ignore_errors(FALSE);
1302
StrutPartial *strut;
1305
#define RESET_STRUT_LIST(sl) \
1307
g_slice_free(ObScreenStrut, (sl)->data); \
1308
sl = g_slist_delete_link(sl, sl); \
1311
#define ADD_STRUT_TO_LIST(sl, d, s) \
1313
ObScreenStrut *ss = g_slice_new(ObScreenStrut); \
1316
sl = g_slist_prepend(sl, ss); \
1319
#define VALIDATE_STRUTS(sl, side, max) \
1322
for (it = sl; it; it = g_slist_next(it)) { \
1323
ObScreenStrut *ss = it->data; \
1324
ss->strut->side = MIN(max, ss->strut->side); \
1328
static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
1333
XineramaScreenInfo *info;
1336
if (ob_debug_xinerama) {
1337
gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1338
gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1340
*xin_areas = g_new(Rect, *nxin + 1);
1341
RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
1342
RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
1345
else if (obt_display_extension_xinerama &&
1346
(info = XineramaQueryScreens(obt_display, &n))) {
1348
*xin_areas = g_new(Rect, *nxin + 1);
1349
for (i = 0; i < *nxin; ++i)
1350
RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
1351
info[i].width, info[i].height);
1357
*xin_areas = g_new(Rect, *nxin + 1);
1358
RECT_SET((*xin_areas)[0], 0, 0,
1359
WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1360
HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1363
/* returns one extra with the total area in it */
1364
l = (*xin_areas)[0].x;
1365
t = (*xin_areas)[0].y;
1366
r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
1367
b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
1368
for (i = 1; i < *nxin; ++i) {
1369
l = MIN(l, (*xin_areas)[i].x);
1370
t = MIN(l, (*xin_areas)[i].y);
1371
r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
1372
b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
1374
RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
1377
void screen_update_areas(void)
1381
GList *it, *onscreen;
1383
/* collect the clients that are on screen */
1385
for (it = client_list; it; it = g_list_next(it)) {
1386
if (client_monitor(it->data) != screen_num_monitors)
1387
onscreen = g_list_prepend(onscreen, it->data);
1390
g_free(monitor_area);
1391
get_xinerama_screens(&monitor_area, &screen_num_monitors);
1393
/* set up the user-specified margins */
1394
config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1395
config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1396
config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1397
config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1398
config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
1399
config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1400
config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
1401
config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1403
RESET_STRUT_LIST(struts_left);
1404
RESET_STRUT_LIST(struts_top);
1405
RESET_STRUT_LIST(struts_right);
1406
RESET_STRUT_LIST(struts_bottom);
1408
/* collect the struts */
1409
for (it = client_list; it; it = g_list_next(it)) {
1410
ObClient *c = it->data;
1412
ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
1414
ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
1416
ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
1417
if (c->strut.bottom)
1418
ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
1420
if (dock_strut.left)
1421
ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
1423
ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
1424
if (dock_strut.right)
1425
ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
1426
if (dock_strut.bottom)
1427
ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
1429
if (config_margins.left)
1430
ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
1431
if (config_margins.top)
1432
ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
1433
if (config_margins.right)
1434
ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
1435
if (config_margins.bottom)
1436
ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
1438
VALIDATE_STRUTS(struts_left, left,
1439
monitor_area[screen_num_monitors].width / 2);
1440
VALIDATE_STRUTS(struts_right, right,
1441
monitor_area[screen_num_monitors].width / 2);
1442
VALIDATE_STRUTS(struts_top, top,
1443
monitor_area[screen_num_monitors].height / 2);
1444
VALIDATE_STRUTS(struts_bottom, bottom,
1445
monitor_area[screen_num_monitors].height / 2);
1447
dims = g_new(gulong, 4 * screen_num_desktops);
1448
for (i = 0; i < screen_num_desktops; ++i) {
1449
Rect *area = screen_area(i, SCREEN_AREA_ALL_MONITORS, NULL);
1450
dims[i*4+0] = area->x;
1451
dims[i*4+1] = area->y;
1452
dims[i*4+2] = area->width;
1453
dims[i*4+3] = area->height;
1454
g_slice_free(Rect, area);
1457
/* set the legacy workarea hint to the union of all the monitors */
1458
OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL,
1459
dims, 4 * screen_num_desktops);
1461
/* the area has changed, adjust all the windows if they need it */
1462
for (it = onscreen; it; it = g_list_next(it))
1463
client_reconfigure(it->data, FALSE);
1469
Rect* screen_area_all_monitors(guint desktop)
1474
a = screen_area_monitor(desktop, 0);
1476
/* combine all the monitors together */
1477
for (i = 1; i < screen_num_monitors; ++i) {
1478
Rect *m = screen_area_monitor(desktop, i);
1481
l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
1482
t = MIN(RECT_TOP(*a), RECT_TOP(*m));
1483
r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
1484
b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
1486
RECT_SET(*a, l, t, r - l + 1, b - t + 1);
1495
#define STRUT_LEFT_IN_SEARCH(s, search) \
1496
(RANGES_INTERSECT(search->y, search->height, \
1497
s->left_start, s->left_end - s->left_start + 1))
1498
#define STRUT_RIGHT_IN_SEARCH(s, search) \
1499
(RANGES_INTERSECT(search->y, search->height, \
1500
s->right_start, s->right_end - s->right_start + 1))
1501
#define STRUT_TOP_IN_SEARCH(s, search) \
1502
(RANGES_INTERSECT(search->x, search->width, \
1503
s->top_start, s->top_end - s->top_start + 1))
1504
#define STRUT_BOTTOM_IN_SEARCH(s, search) \
1505
(RANGES_INTERSECT(search->x, search->width, \
1506
s->bottom_start, s->bottom_end - s->bottom_start + 1))
1508
#define STRUT_LEFT_IGNORE(s, us, search) \
1509
(head == SCREEN_AREA_ALL_MONITORS && us && \
1510
RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
1511
#define STRUT_RIGHT_IGNORE(s, us, search) \
1512
(head == SCREEN_AREA_ALL_MONITORS && us && \
1513
RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
1514
#define STRUT_TOP_IGNORE(s, us, search) \
1515
(head == SCREEN_AREA_ALL_MONITORS && us && \
1516
RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
1517
#define STRUT_BOTTOM_IGNORE(s, us, search) \
1518
(head == SCREEN_AREA_ALL_MONITORS && us && \
1519
RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
1521
Rect* screen_area(guint desktop, guint head, Rect *search)
1527
gboolean us = search != NULL; /* user provided search */
1529
g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
1530
g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
1531
head == SCREEN_AREA_ALL_MONITORS);
1532
g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
1534
/* find any struts for this monitor
1535
which will be affecting the search area.
1538
/* search everything if search is null */
1540
if (head < screen_num_monitors) search = &monitor_area[head];
1541
else search = &monitor_area[screen_num_monitors];
1543
if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
1545
/* al is "all left" meaning the furthest left you can get, l is our
1546
"working left" meaning our current strut edge which we're calculating
1549
/* only include monitors which the search area lines up with */
1550
if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
1551
l = RECT_RIGHT(monitor_area[screen_num_monitors]);
1552
t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1553
r = RECT_LEFT(monitor_area[screen_num_monitors]);
1554
b = RECT_TOP(monitor_area[screen_num_monitors]);
1555
for (i = 0; i < screen_num_monitors; ++i) {
1556
/* add the monitor if applicable */
1557
if (RANGES_INTERSECT(search->x, search->width,
1558
monitor_area[i].x, monitor_area[i].width))
1560
t = MIN(t, RECT_TOP(monitor_area[i]));
1561
b = MAX(b, RECT_BOTTOM(monitor_area[i]));
1563
if (RANGES_INTERSECT(search->y, search->height,
1564
monitor_area[i].y, monitor_area[i].height))
1566
l = MIN(l, RECT_LEFT(monitor_area[i]));
1567
r = MAX(r, RECT_RIGHT(monitor_area[i]));
1571
l = RECT_LEFT(monitor_area[screen_num_monitors]);
1572
t = RECT_TOP(monitor_area[screen_num_monitors]);
1573
r = RECT_RIGHT(monitor_area[screen_num_monitors]);
1574
b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1577
for (d = 0; d < screen_num_desktops; ++d) {
1578
if (d != desktop && desktop != DESKTOP_ALL) continue;
1580
for (i = 0; i < screen_num_monitors; ++i) {
1581
if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
1583
for (it = struts_left; it; it = g_slist_next(it)) {
1584
ObScreenStrut *s = it->data;
1585
if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1586
STRUT_LEFT_IN_SEARCH(s->strut, search) &&
1587
!STRUT_LEFT_IGNORE(s->strut, us, search))
1588
l = MAX(l, RECT_LEFT(monitor_area[screen_num_monitors])
1591
for (it = struts_top; it; it = g_slist_next(it)) {
1592
ObScreenStrut *s = it->data;
1593
if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1594
STRUT_TOP_IN_SEARCH(s->strut, search) &&
1595
!STRUT_TOP_IGNORE(s->strut, us, search))
1596
t = MAX(t, RECT_TOP(monitor_area[screen_num_monitors])
1599
for (it = struts_right; it; it = g_slist_next(it)) {
1600
ObScreenStrut *s = it->data;
1601
if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1602
STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
1603
!STRUT_RIGHT_IGNORE(s->strut, us, search))
1604
r = MIN(r, RECT_RIGHT(monitor_area[screen_num_monitors])
1607
for (it = struts_bottom; it; it = g_slist_next(it)) {
1608
ObScreenStrut *s = it->data;
1609
if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1610
STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
1611
!STRUT_BOTTOM_IGNORE(s->strut, us, search))
1612
b = MIN(b, RECT_BOTTOM(monitor_area[screen_num_monitors])
1613
- s->strut->bottom);
1616
/* limit to this monitor */
1618
l = MAX(l, RECT_LEFT(monitor_area[i]));
1619
t = MAX(t, RECT_TOP(monitor_area[i]));
1620
r = MIN(r, RECT_RIGHT(monitor_area[i]));
1621
b = MIN(b, RECT_BOTTOM(monitor_area[i]));
1626
a = g_slice_new(Rect);
1629
a->width = r - l + 1;
1630
a->height = b - t + 1;
1634
guint screen_find_monitor(const Rect *search)
1637
guint most = screen_num_monitors;
1640
for (i = 0; i < screen_num_monitors; ++i) {
1641
const Rect *area = screen_physical_area_monitor(i);
1642
if (RECT_INTERSECTS_RECT(*area, *search)) {
1646
RECT_SET_INTERSECTION(r, *area, *search);
1647
v = r.width * r.height;
1655
return most < screen_num_monitors ? most : screen_monitor_primary(FALSE);
1658
const Rect* screen_physical_area_all_monitors(void)
1660
return screen_physical_area_monitor(screen_num_monitors);
1663
const Rect* screen_physical_area_monitor(guint head)
1665
g_assert(head <= screen_num_monitors);
1667
return &monitor_area[head];
1670
gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
1672
g_assert(head <= screen_num_monitors);
1674
return RECT_INTERSECTS_RECT(monitor_area[head], *search);
1677
guint screen_monitor_active(void)
1679
if (moveresize_client)
1680
return client_monitor(moveresize_client);
1681
else if (focus_client)
1682
return client_monitor(focus_client);
1684
return screen_monitor_pointer();
1687
const Rect* screen_physical_area_active(void)
1689
return screen_physical_area_monitor(screen_monitor_active());
1692
guint screen_monitor_primary(gboolean fixed)
1694
if (config_primary_monitor_index > 0) {
1695
if (config_primary_monitor_index-1 < screen_num_monitors)
1696
return config_primary_monitor_index - 1;
1702
else if (config_primary_monitor == OB_PLACE_MONITOR_ACTIVE)
1703
return screen_monitor_active();
1704
else /* config_primary_monitor == OB_PLACE_MONITOR_MOUSE */
1705
return screen_monitor_pointer();
1708
const Rect* screen_physical_area_primary(gboolean fixed)
1710
return screen_physical_area_monitor(screen_monitor_primary(fixed));
1713
void screen_set_root_cursor(void)
1715
if (sn_app_starting())
1716
XDefineCursor(obt_display, obt_root(ob_screen),
1717
ob_cursor(OB_CURSOR_BUSYPOINTER));
1719
XDefineCursor(obt_display, obt_root(ob_screen),
1720
ob_cursor(OB_CURSOR_POINTER));
1723
guint screen_find_monitor_point(guint x, guint y)
1726
RECT_SET(mon, x, y, 1, 1);
1727
return screen_find_monitor(&mon);
1730
guint screen_monitor_pointer()
1733
if (!screen_pointer_pos(&x, &y))
1735
return screen_find_monitor_point(x, y);
1738
gboolean screen_pointer_pos(gint *x, gint *y)
1745
ret = !!XQueryPointer(obt_display, obt_root(ob_screen),
1746
&w, &w, x, y, &i, &i, &u);
1748
for (i = 0; i < ScreenCount(obt_display); ++i)
1750
if (XQueryPointer(obt_display, obt_root(i),
1751
&w, &w, x, y, &i, &i, &u))
1757
gboolean screen_compare_desktops(guint a, guint b)
1759
if (a == DESKTOP_ALL)
1761
if (b == DESKTOP_ALL)