]> Dogcows Code - chaz/openbox/blob - openbox/screen.c
7814dce4f453fee0bef36081f8aad51cae08c258
[chaz/openbox] / openbox / screen.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 screen.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "openbox.h"
22 #include "dock.h"
23 #include "xerror.h"
24 #include "prop.h"
25 #include "grab.h"
26 #include "startupnotify.h"
27 #include "moveresize.h"
28 #include "config.h"
29 #include "screen.h"
30 #include "client.h"
31 #include "session.h"
32 #include "frame.h"
33 #include "event.h"
34 #include "focus.h"
35 #include "popup.h"
36 #include "extensions.h"
37 #include "render/render.h"
38 #include "gettext.h"
39
40 #include <X11/Xlib.h>
41 #ifdef HAVE_UNISTD_H
42 # include <sys/types.h>
43 # include <unistd.h>
44 #endif
45 #include <assert.h>
46
47 /*! The event mask to grab on the root window */
48 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
49 EnterWindowMask | LeaveWindowMask | \
50 SubstructureRedirectMask | FocusChangeMask | \
51 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask)
52
53 guint screen_num_desktops;
54 guint screen_num_monitors;
55 guint screen_desktop;
56 guint screen_last_desktop;
57 Size screen_physical_size;
58 gboolean screen_showing_desktop;
59 DesktopLayout screen_desktop_layout;
60 gchar **screen_desktop_names;
61 Window screen_support_win;
62 Time screen_desktop_user_time = CurrentTime;
63
64 static Rect **area; /* array of desktop holding array of xinerama areas */
65 static Rect *monitor_area;
66
67 static ObPagerPopup *desktop_cycle_popup;
68
69 static gboolean replace_wm()
70 {
71 gchar *wm_sn;
72 Atom wm_sn_atom;
73 Window current_wm_sn_owner;
74 Time timestamp;
75
76 wm_sn = g_strdup_printf("WM_S%d", ob_screen);
77 wm_sn_atom = XInternAtom(ob_display, wm_sn, FALSE);
78 g_free(wm_sn);
79
80 current_wm_sn_owner = XGetSelectionOwner(ob_display, wm_sn_atom);
81 if (current_wm_sn_owner == screen_support_win)
82 current_wm_sn_owner = None;
83 if (current_wm_sn_owner) {
84 if (!ob_replace_wm) {
85 g_message(_("A window manager is already running on screen %d"),
86 ob_screen);
87 return FALSE;
88 }
89 xerror_set_ignore(TRUE);
90 xerror_occured = FALSE;
91
92 /* We want to find out when the current selection owner dies */
93 XSelectInput(ob_display, current_wm_sn_owner, StructureNotifyMask);
94 XSync(ob_display, FALSE);
95
96 xerror_set_ignore(FALSE);
97 if (xerror_occured)
98 current_wm_sn_owner = None;
99 }
100
101 {
102 /* Generate a timestamp */
103 XEvent event;
104
105 XSelectInput(ob_display, screen_support_win, PropertyChangeMask);
106
107 XChangeProperty(ob_display, screen_support_win,
108 prop_atoms.wm_class, prop_atoms.string,
109 8, PropModeAppend, NULL, 0);
110 XWindowEvent(ob_display, screen_support_win,
111 PropertyChangeMask, &event);
112
113 XSelectInput(ob_display, screen_support_win, NoEventMask);
114
115 timestamp = event.xproperty.time;
116 }
117
118 XSetSelectionOwner(ob_display, wm_sn_atom, screen_support_win,
119 timestamp);
120
121 if (XGetSelectionOwner(ob_display, wm_sn_atom) != screen_support_win) {
122 g_message(_("Could not acquire window manager selection on screen %d"),
123 ob_screen);
124 return FALSE;
125 }
126
127 /* Wait for old window manager to go away */
128 if (current_wm_sn_owner) {
129 XEvent event;
130 gulong wait = 0;
131 const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
132
133 while (wait < timeout) {
134 if (XCheckWindowEvent(ob_display, current_wm_sn_owner,
135 StructureNotifyMask, &event) &&
136 event.type == DestroyNotify)
137 break;
138 g_usleep(G_USEC_PER_SEC / 10);
139 wait += G_USEC_PER_SEC / 10;
140 }
141
142 if (wait >= timeout) {
143 g_message(_("The WM on screen %d is not exiting"), ob_screen);
144 return FALSE;
145 }
146 }
147
148 /* Send client message indicating that we are now the WM */
149 prop_message(RootWindow(ob_display, ob_screen), prop_atoms.manager,
150 timestamp, wm_sn_atom, screen_support_win, 0,
151 SubstructureNotifyMask);
152
153 return TRUE;
154 }
155
156 gboolean screen_annex(const gchar *program_name)
157 {
158 XSetWindowAttributes attrib;
159 pid_t pid;
160 gint i, num_support;
161 Atom *prop_atoms_start, *wm_supported_pos;
162 gulong *supported;
163
164 /* create the netwm support window */
165 attrib.override_redirect = TRUE;
166 screen_support_win = XCreateWindow(ob_display,
167 RootWindow(ob_display, ob_screen),
168 -100, -100, 1, 1, 0,
169 CopyFromParent, InputOutput,
170 CopyFromParent,
171 CWOverrideRedirect, &attrib);
172 XMapWindow(ob_display, screen_support_win);
173 XLowerWindow(ob_display, screen_support_win);
174
175 if (!replace_wm()) {
176 XDestroyWindow(ob_display, screen_support_win);
177 return FALSE;
178 }
179
180 xerror_set_ignore(TRUE);
181 xerror_occured = FALSE;
182 XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
183 ROOT_EVENTMASK);
184 xerror_set_ignore(FALSE);
185 if (xerror_occured) {
186 g_message(_("A window manager is already running on screen %d"),
187 ob_screen);
188
189 XDestroyWindow(ob_display, screen_support_win);
190 return FALSE;
191 }
192
193
194 screen_set_root_cursor();
195
196 /* set the OPENBOX_PID hint */
197 pid = getpid();
198 PROP_SET32(RootWindow(ob_display, ob_screen),
199 openbox_pid, cardinal, pid);
200
201 /* set supporting window */
202 PROP_SET32(RootWindow(ob_display, ob_screen),
203 net_supporting_wm_check, window, screen_support_win);
204
205 /* set properties on the supporting window */
206 PROP_SETS(screen_support_win, net_wm_name, program_name);
207 PROP_SET32(screen_support_win, net_supporting_wm_check,
208 window, screen_support_win);
209
210 /* set the _NET_SUPPORTED_ATOMS hint */
211
212 /* this is all the atoms after net_supported in the prop_atoms struct */
213 prop_atoms_start = (Atom*)&prop_atoms;
214 wm_supported_pos = (Atom*)&(prop_atoms.net_supported);
215 num_support = sizeof(prop_atoms) / sizeof(Atom) -
216 (wm_supported_pos - prop_atoms_start) - 1;
217 i = 0;
218 supported = g_new(gulong, num_support);
219 supported[i++] = prop_atoms.net_supporting_wm_check;
220 supported[i++] = prop_atoms.net_wm_full_placement;
221 supported[i++] = prop_atoms.net_current_desktop;
222 supported[i++] = prop_atoms.net_number_of_desktops;
223 supported[i++] = prop_atoms.net_desktop_geometry;
224 supported[i++] = prop_atoms.net_desktop_viewport;
225 supported[i++] = prop_atoms.net_active_window;
226 supported[i++] = prop_atoms.net_workarea;
227 supported[i++] = prop_atoms.net_client_list;
228 supported[i++] = prop_atoms.net_client_list_stacking;
229 supported[i++] = prop_atoms.net_desktop_names;
230 supported[i++] = prop_atoms.net_close_window;
231 supported[i++] = prop_atoms.net_desktop_layout;
232 supported[i++] = prop_atoms.net_showing_desktop;
233 supported[i++] = prop_atoms.net_wm_name;
234 supported[i++] = prop_atoms.net_wm_visible_name;
235 supported[i++] = prop_atoms.net_wm_icon_name;
236 supported[i++] = prop_atoms.net_wm_visible_icon_name;
237 supported[i++] = prop_atoms.net_wm_desktop;
238 supported[i++] = prop_atoms.net_wm_strut;
239 supported[i++] = prop_atoms.net_wm_strut_partial;
240 supported[i++] = prop_atoms.net_wm_icon;
241 supported[i++] = prop_atoms.net_wm_icon_geometry;
242 supported[i++] = prop_atoms.net_wm_window_type;
243 supported[i++] = prop_atoms.net_wm_window_type_desktop;
244 supported[i++] = prop_atoms.net_wm_window_type_dock;
245 supported[i++] = prop_atoms.net_wm_window_type_toolbar;
246 supported[i++] = prop_atoms.net_wm_window_type_menu;
247 supported[i++] = prop_atoms.net_wm_window_type_utility;
248 supported[i++] = prop_atoms.net_wm_window_type_splash;
249 supported[i++] = prop_atoms.net_wm_window_type_dialog;
250 supported[i++] = prop_atoms.net_wm_window_type_normal;
251 supported[i++] = prop_atoms.net_wm_allowed_actions;
252 supported[i++] = prop_atoms.net_wm_action_move;
253 supported[i++] = prop_atoms.net_wm_action_resize;
254 supported[i++] = prop_atoms.net_wm_action_minimize;
255 supported[i++] = prop_atoms.net_wm_action_shade;
256 supported[i++] = prop_atoms.net_wm_action_maximize_horz;
257 supported[i++] = prop_atoms.net_wm_action_maximize_vert;
258 supported[i++] = prop_atoms.net_wm_action_fullscreen;
259 supported[i++] = prop_atoms.net_wm_action_change_desktop;
260 supported[i++] = prop_atoms.net_wm_action_close;
261 supported[i++] = prop_atoms.net_wm_state;
262 supported[i++] = prop_atoms.net_wm_state_modal;
263 supported[i++] = prop_atoms.net_wm_state_maximized_vert;
264 supported[i++] = prop_atoms.net_wm_state_maximized_horz;
265 supported[i++] = prop_atoms.net_wm_state_shaded;
266 supported[i++] = prop_atoms.net_wm_state_skip_taskbar;
267 supported[i++] = prop_atoms.net_wm_state_skip_pager;
268 supported[i++] = prop_atoms.net_wm_state_hidden;
269 supported[i++] = prop_atoms.net_wm_state_fullscreen;
270 supported[i++] = prop_atoms.net_wm_state_above;
271 supported[i++] = prop_atoms.net_wm_state_below;
272 supported[i++] = prop_atoms.net_wm_state_demands_attention;
273 supported[i++] = prop_atoms.net_moveresize_window;
274 supported[i++] = prop_atoms.net_wm_moveresize;
275 supported[i++] = prop_atoms.net_wm_user_time;
276 supported[i++] = prop_atoms.net_frame_extents;
277 supported[i++] = prop_atoms.net_startup_id;
278 #ifdef SYNC
279 supported[i++] = prop_atoms.net_wm_sync_request;
280 supported[i++] = prop_atoms.net_wm_sync_request_counter;
281 #endif
282
283 supported[i++] = prop_atoms.kde_wm_change_state;
284 supported[i++] = prop_atoms.kde_net_wm_frame_strut;
285 supported[i++] = prop_atoms.kde_net_wm_window_type_override;
286
287 supported[i++] = prop_atoms.openbox_wm_state_undecorated;
288 supported[i++] = prop_atoms.openbox_pid;
289 supported[i++] = prop_atoms.openbox_config;
290 supported[i++] = prop_atoms.openbox_control;
291 g_assert(i == num_support);
292
293 PROP_SETA32(RootWindow(ob_display, ob_screen),
294 net_supported, atom, supported, num_support);
295 g_free(supported);
296
297 return TRUE;
298 }
299
300 void screen_startup(gboolean reconfig)
301 {
302 guint i, numnames;
303 gchar **names;
304 GSList *it;
305 guint32 d;
306
307 desktop_cycle_popup = pager_popup_new(FALSE);
308 pager_popup_height(desktop_cycle_popup, POPUP_HEIGHT);
309
310 if (reconfig)
311 return;
312
313 /* get the initial size */
314 screen_resize();
315
316 /* get the desktop names */
317 numnames = g_slist_length(config_desktops_names);
318 names = g_new(gchar*, numnames + 1);
319 names[numnames] = NULL;
320 for (i = 0, it = config_desktops_names; it; ++i, it = g_slist_next(it))
321 names[i] = g_strdup(it->data);
322
323 /* set the root window property */
324 PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,names);
325
326 g_strfreev(names);
327
328 /* set the number of desktops */
329 screen_num_desktops = 0;
330 screen_set_num_desktops(config_desktops_num);
331
332 /* start on the current desktop when a wm was already running */
333 if (PROP_GET32(RootWindow(ob_display, ob_screen),
334 net_current_desktop, cardinal, &d) &&
335 d < screen_num_desktops)
336 {
337 screen_set_desktop(d, FALSE);
338 } else if (session_desktop >= 0)
339 screen_set_desktop(MIN((guint)session_desktop,
340 screen_num_desktops), FALSE);
341 else
342 screen_set_desktop(MIN(config_screen_firstdesk,
343 screen_num_desktops) - 1, FALSE);
344
345 /* don't start in showing-desktop mode */
346 screen_showing_desktop = FALSE;
347 PROP_SET32(RootWindow(ob_display, ob_screen),
348 net_showing_desktop, cardinal, screen_showing_desktop);
349
350 screen_update_layout();
351 }
352
353 void screen_shutdown(gboolean reconfig)
354 {
355 Rect **r;
356
357 pager_popup_free(desktop_cycle_popup);
358
359 if (reconfig)
360 return;
361
362 XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
363 NoEventMask);
364
365 /* we're not running here no more! */
366 PROP_ERASE(RootWindow(ob_display, ob_screen), openbox_pid);
367 /* not without us */
368 PROP_ERASE(RootWindow(ob_display, ob_screen), net_supported);
369 /* don't keep this mode */
370 PROP_ERASE(RootWindow(ob_display, ob_screen), net_showing_desktop);
371
372 XDestroyWindow(ob_display, screen_support_win);
373
374 g_strfreev(screen_desktop_names);
375 screen_desktop_names = NULL;
376
377 for (r = area; *r; ++r)
378 g_free(*r);
379 g_free(area);
380 area = NULL;
381 }
382
383 void screen_resize()
384 {
385 static gint oldw = 0, oldh = 0;
386 gint w, h;
387 GList *it;
388 gulong geometry[2];
389
390 w = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
391 h = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen));
392
393 if (w == oldw && h == oldh) return;
394
395 oldw = w; oldh = h;
396
397 /* Set the _NET_DESKTOP_GEOMETRY hint */
398 screen_physical_size.width = geometry[0] = w;
399 screen_physical_size.height = geometry[1] = h;
400 PROP_SETA32(RootWindow(ob_display, ob_screen),
401 net_desktop_geometry, cardinal, geometry, 2);
402
403 if (ob_state() == OB_STATE_STARTING)
404 return;
405
406 screen_update_areas();
407 dock_configure();
408
409 for (it = client_list; it; it = g_list_next(it))
410 client_move_onscreen(it->data, FALSE);
411 }
412
413 void screen_set_num_desktops(guint num)
414 {
415 guint old;
416 gulong *viewport;
417 GList *it;
418
419 g_assert(num > 0);
420
421 if (screen_num_desktops == num) return;
422
423 old = screen_num_desktops;
424 screen_num_desktops = num;
425 PROP_SET32(RootWindow(ob_display, ob_screen),
426 net_number_of_desktops, cardinal, num);
427
428 /* set the viewport hint */
429 viewport = g_new0(gulong, num * 2);
430 PROP_SETA32(RootWindow(ob_display, ob_screen),
431 net_desktop_viewport, cardinal, viewport, num * 2);
432 g_free(viewport);
433
434 /* the number of rows/columns will differ */
435 screen_update_layout();
436
437 /* move windows on desktops that will no longer exist! */
438 for (it = client_list; it; it = g_list_next(it)) {
439 ObClient *c = it->data;
440 if (c->desktop >= num && c->desktop != DESKTOP_ALL)
441 client_set_desktop(c, num - 1, FALSE);
442 }
443
444 /* change our struts/area to match (after moving windows) */
445 screen_update_areas();
446
447 /* may be some unnamed desktops that we need to fill in with names
448 (after updating the areas so the popup can resize) */
449 screen_update_desktop_names();
450
451 /* change our desktop if we're on one that no longer exists! */
452 if (screen_desktop >= screen_num_desktops)
453 screen_set_desktop(num - 1, TRUE);
454 }
455
456 void screen_set_desktop(guint num, gboolean dofocus)
457 {
458 ObClient *c;
459 GList *it;
460 guint old;
461
462 g_assert(num < screen_num_desktops);
463
464 old = screen_desktop;
465 screen_desktop = num;
466 PROP_SET32(RootWindow(ob_display, ob_screen),
467 net_current_desktop, cardinal, num);
468
469 if (old == num) return;
470
471 screen_last_desktop = old;
472
473 ob_debug("Moving to desktop %d\n", num+1);
474
475 if (moveresize_client)
476 client_set_desktop(moveresize_client, num, TRUE);
477
478 /* show windows before hiding the rest to lessen the enter/leave events */
479
480 /* show windows from top to bottom */
481 for (it = stacking_list; it; it = g_list_next(it)) {
482 if (WINDOW_IS_CLIENT(it->data)) {
483 ObClient *c = it->data;
484 client_show(c);
485 }
486 }
487
488 /* have to try focus here because when you leave an empty desktop
489 there is no focus out to watch for
490
491 do this before hiding the windows so if helper windows are coming
492 with us, they don't get hidden
493 */
494 if (dofocus && (c = focus_fallback_target(TRUE, focus_client))) {
495 /* only do the flicker reducing stuff ahead of time if we are going
496 to call xsetinputfocus on the window ourselves. otherwise there is
497 no guarantee the window will actually take focus.. */
498 if (c->can_focus) {
499 /* do this here so that if you switch desktops to a window with
500 helper windows then the helper windows won't flash */
501 client_bring_helper_windows(c);
502 /* reduce flicker by hiliting now rather than waiting for the
503 server FocusIn event */
504 frame_adjust_focus(c->frame, TRUE);
505 }
506 client_focus(c);
507 }
508
509 /* hide windows from bottom to top */
510 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
511 if (WINDOW_IS_CLIENT(it->data)) {
512 ObClient *c = it->data;
513 client_hide(c);
514 }
515 }
516
517 event_ignore_queued_enters();
518
519 if (event_curtime != CurrentTime)
520 screen_desktop_user_time = event_curtime;
521 }
522
523 static void get_row_col(guint d, guint *r, guint *c)
524 {
525 switch (screen_desktop_layout.orientation) {
526 case OB_ORIENTATION_HORZ:
527 switch (screen_desktop_layout.start_corner) {
528 case OB_CORNER_TOPLEFT:
529 *r = d / screen_desktop_layout.columns;
530 *c = d % screen_desktop_layout.columns;
531 break;
532 case OB_CORNER_BOTTOMLEFT:
533 *r = screen_desktop_layout.rows - 1 -
534 d / screen_desktop_layout.columns;
535 *c = d % screen_desktop_layout.columns;
536 break;
537 case OB_CORNER_TOPRIGHT:
538 *r = d / screen_desktop_layout.columns;
539 *c = screen_desktop_layout.columns - 1 -
540 d % screen_desktop_layout.columns;
541 break;
542 case OB_CORNER_BOTTOMRIGHT:
543 *r = screen_desktop_layout.rows - 1 -
544 d / screen_desktop_layout.columns;
545 *c = screen_desktop_layout.columns - 1 -
546 d % screen_desktop_layout.columns;
547 break;
548 }
549 break;
550 case OB_ORIENTATION_VERT:
551 switch (screen_desktop_layout.start_corner) {
552 case OB_CORNER_TOPLEFT:
553 *r = d % screen_desktop_layout.rows;
554 *c = d / screen_desktop_layout.rows;
555 break;
556 case OB_CORNER_BOTTOMLEFT:
557 *r = screen_desktop_layout.rows - 1 -
558 d % screen_desktop_layout.rows;
559 *c = d / screen_desktop_layout.rows;
560 break;
561 case OB_CORNER_TOPRIGHT:
562 *r = d % screen_desktop_layout.rows;
563 *c = screen_desktop_layout.columns - 1 -
564 d / screen_desktop_layout.rows;
565 break;
566 case OB_CORNER_BOTTOMRIGHT:
567 *r = screen_desktop_layout.rows - 1 -
568 d % screen_desktop_layout.rows;
569 *c = screen_desktop_layout.columns - 1 -
570 d / screen_desktop_layout.rows;
571 break;
572 }
573 break;
574 }
575 }
576
577 static guint translate_row_col(guint r, guint c)
578 {
579 switch (screen_desktop_layout.orientation) {
580 case OB_ORIENTATION_HORZ:
581 switch (screen_desktop_layout.start_corner) {
582 case OB_CORNER_TOPLEFT:
583 return r % screen_desktop_layout.rows *
584 screen_desktop_layout.columns +
585 c % screen_desktop_layout.columns;
586 case OB_CORNER_BOTTOMLEFT:
587 return (screen_desktop_layout.rows - 1 -
588 r % screen_desktop_layout.rows) *
589 screen_desktop_layout.columns +
590 c % screen_desktop_layout.columns;
591 case OB_CORNER_TOPRIGHT:
592 return r % screen_desktop_layout.rows *
593 screen_desktop_layout.columns +
594 (screen_desktop_layout.columns - 1 -
595 c % screen_desktop_layout.columns);
596 case OB_CORNER_BOTTOMRIGHT:
597 return (screen_desktop_layout.rows - 1 -
598 r % screen_desktop_layout.rows) *
599 screen_desktop_layout.columns +
600 (screen_desktop_layout.columns - 1 -
601 c % screen_desktop_layout.columns);
602 }
603 case OB_ORIENTATION_VERT:
604 switch (screen_desktop_layout.start_corner) {
605 case OB_CORNER_TOPLEFT:
606 return c % screen_desktop_layout.columns *
607 screen_desktop_layout.rows +
608 r % screen_desktop_layout.rows;
609 case OB_CORNER_BOTTOMLEFT:
610 return c % screen_desktop_layout.columns *
611 screen_desktop_layout.rows +
612 (screen_desktop_layout.rows - 1 -
613 r % screen_desktop_layout.rows);
614 case OB_CORNER_TOPRIGHT:
615 return (screen_desktop_layout.columns - 1 -
616 c % screen_desktop_layout.columns) *
617 screen_desktop_layout.rows +
618 r % screen_desktop_layout.rows;
619 case OB_CORNER_BOTTOMRIGHT:
620 return (screen_desktop_layout.columns - 1 -
621 c % screen_desktop_layout.columns) *
622 screen_desktop_layout.rows +
623 (screen_desktop_layout.rows - 1 -
624 r % screen_desktop_layout.rows);
625 }
626 }
627 g_assert_not_reached();
628 return 0;
629 }
630
631 void screen_desktop_popup(guint d, gboolean show)
632 {
633 Rect *a;
634
635 if (!show) {
636 pager_popup_hide(desktop_cycle_popup);
637 } else {
638 a = screen_physical_area_monitor(0);
639 pager_popup_position(desktop_cycle_popup, CenterGravity,
640 a->x + a->width / 2, a->y + a->height / 2);
641 pager_popup_max_width(desktop_cycle_popup,
642 MAX(a->width/3, POPUP_WIDTH));
643 pager_popup_show(desktop_cycle_popup, screen_desktop_names[d], d);
644 }
645 }
646
647 guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
648 gboolean dialog, gboolean done, gboolean cancel)
649 {
650 guint d, r, c;
651
652 d = screen_desktop;
653
654 if ((cancel || done) && dialog)
655 goto show_cycle_dialog;
656
657 get_row_col(d, &r, &c);
658
659 if (linear) {
660 switch (dir) {
661 case OB_DIRECTION_EAST:
662 if (d < screen_num_desktops - 1)
663 ++d;
664 else if (wrap)
665 d = 0;
666 break;
667 case OB_DIRECTION_WEST:
668 if (d > 0)
669 --d;
670 else if (wrap)
671 d = screen_num_desktops - 1;
672 break;
673 default:
674 assert(0);
675 return screen_desktop;
676 }
677 } else {
678 switch (dir) {
679 case OB_DIRECTION_EAST:
680 ++c;
681 if (c >= screen_desktop_layout.columns) {
682 if (wrap) {
683 c = 0;
684 } else {
685 d = screen_desktop;
686 goto show_cycle_dialog;
687 }
688 }
689 d = translate_row_col(r, c);
690 if (d >= screen_num_desktops) {
691 if (wrap) {
692 ++c;
693 } else {
694 d = screen_desktop;
695 goto show_cycle_dialog;
696 }
697 }
698 break;
699 case OB_DIRECTION_WEST:
700 --c;
701 if (c >= screen_desktop_layout.columns) {
702 if (wrap) {
703 c = screen_desktop_layout.columns - 1;
704 } else {
705 d = screen_desktop;
706 goto show_cycle_dialog;
707 }
708 }
709 d = translate_row_col(r, c);
710 if (d >= screen_num_desktops) {
711 if (wrap) {
712 --c;
713 } else {
714 d = screen_desktop;
715 goto show_cycle_dialog;
716 }
717 }
718 break;
719 case OB_DIRECTION_SOUTH:
720 ++r;
721 if (r >= screen_desktop_layout.rows) {
722 if (wrap) {
723 r = 0;
724 } else {
725 d = screen_desktop;
726 goto show_cycle_dialog;
727 }
728 }
729 d = translate_row_col(r, c);
730 if (d >= screen_num_desktops) {
731 if (wrap) {
732 ++r;
733 } else {
734 d = screen_desktop;
735 goto show_cycle_dialog;
736 }
737 }
738 break;
739 case OB_DIRECTION_NORTH:
740 --r;
741 if (r >= screen_desktop_layout.rows) {
742 if (wrap) {
743 r = screen_desktop_layout.rows - 1;
744 } else {
745 d = screen_desktop;
746 goto show_cycle_dialog;
747 }
748 }
749 d = translate_row_col(r, c);
750 if (d >= screen_num_desktops) {
751 if (wrap) {
752 --r;
753 } else {
754 d = screen_desktop;
755 goto show_cycle_dialog;
756 }
757 }
758 break;
759 default:
760 assert(0);
761 return d = screen_desktop;
762 }
763
764 d = translate_row_col(r, c);
765 }
766
767 show_cycle_dialog:
768 if (dialog && !cancel && !done) {
769 screen_desktop_popup(d, TRUE);
770 } else
771 screen_desktop_popup(0, FALSE);
772 return d;
773 }
774
775 void screen_update_layout()
776 {
777 ObOrientation orient;
778 ObCorner corner;
779 guint rows;
780 guint cols;
781 guint32 *data;
782 guint num;
783 gboolean valid = FALSE;
784
785 if (PROP_GETA32(RootWindow(ob_display, ob_screen),
786 net_desktop_layout, cardinal, &data, &num)) {
787 if (num == 3 || num == 4) {
788
789 if (data[0] == prop_atoms.net_wm_orientation_vert)
790 orient = OB_ORIENTATION_VERT;
791 else if (data[0] == prop_atoms.net_wm_orientation_horz)
792 orient = OB_ORIENTATION_HORZ;
793 else
794 goto screen_update_layout_bail;
795
796 if (num < 4)
797 corner = OB_CORNER_TOPLEFT;
798 else {
799 if (data[3] == prop_atoms.net_wm_topleft)
800 corner = OB_CORNER_TOPLEFT;
801 else if (data[3] == prop_atoms.net_wm_topright)
802 corner = OB_CORNER_TOPRIGHT;
803 else if (data[3] == prop_atoms.net_wm_bottomright)
804 corner = OB_CORNER_BOTTOMRIGHT;
805 else if (data[3] == prop_atoms.net_wm_bottomleft)
806 corner = OB_CORNER_BOTTOMLEFT;
807 else
808 goto screen_update_layout_bail;
809 }
810
811 cols = data[1];
812 rows = data[2];
813
814 /* fill in a zero rows/columns */
815 if ((cols == 0 && rows == 0)) { /* both 0's is bad data.. */
816 goto screen_update_layout_bail;
817 } else {
818 if (cols == 0) {
819 cols = screen_num_desktops / rows;
820 if (rows * cols < screen_num_desktops)
821 cols++;
822 if (rows * cols >= screen_num_desktops + cols)
823 rows--;
824 } else if (rows == 0) {
825 rows = screen_num_desktops / cols;
826 if (cols * rows < screen_num_desktops)
827 rows++;
828 if (cols * rows >= screen_num_desktops + rows)
829 cols--;
830 }
831 }
832
833 /* bounds checking */
834 if (orient == OB_ORIENTATION_HORZ) {
835 cols = MIN(screen_num_desktops, cols);
836 rows = MIN(rows, (screen_num_desktops + cols - 1) / cols);
837 cols = screen_num_desktops / rows +
838 !!(screen_num_desktops % rows);
839 } else {
840 rows = MIN(screen_num_desktops, rows);
841 cols = MIN(cols, (screen_num_desktops + rows - 1) / rows);
842 rows = screen_num_desktops / cols +
843 !!(screen_num_desktops % cols);
844 }
845
846 valid = TRUE;
847 }
848 screen_update_layout_bail:
849 g_free(data);
850 }
851
852 if (!valid) {
853 /* defaults */
854 orient = OB_ORIENTATION_HORZ;
855 corner = OB_CORNER_TOPLEFT;
856 rows = 1;
857 cols = screen_num_desktops;
858 }
859
860 screen_desktop_layout.orientation = orient;
861 screen_desktop_layout.start_corner = corner;
862 screen_desktop_layout.rows = rows;
863 screen_desktop_layout.columns = cols;
864 }
865
866 void screen_update_desktop_names()
867 {
868 guint i;
869
870 /* empty the array */
871 g_strfreev(screen_desktop_names);
872 screen_desktop_names = NULL;
873
874 if (PROP_GETSS(RootWindow(ob_display, ob_screen),
875 net_desktop_names, utf8, &screen_desktop_names))
876 for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
877 else
878 i = 0;
879 if (i < screen_num_desktops) {
880 screen_desktop_names = g_renew(gchar*, screen_desktop_names,
881 screen_num_desktops + 1);
882 screen_desktop_names[screen_num_desktops] = NULL;
883 for (; i < screen_num_desktops; ++i)
884 screen_desktop_names[i] = g_strdup_printf("desktop %i", i + 1);
885 }
886
887 /* resize the pager for these names */
888 pager_popup_text_width_to_strings(desktop_cycle_popup,
889 screen_desktop_names,
890 screen_num_desktops);
891 }
892
893 void screen_show_desktop(gboolean show, gboolean restore_focus)
894 {
895 GList *it;
896
897 if (show == screen_showing_desktop) return; /* no change */
898
899 screen_showing_desktop = show;
900
901 if (show) {
902 /* bottom to top */
903 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
904 if (WINDOW_IS_CLIENT(it->data)) {
905 ObClient *client = it->data;
906 client_showhide(client);
907 }
908 }
909 } else {
910 /* top to bottom */
911 for (it = stacking_list; it; it = g_list_next(it)) {
912 if (WINDOW_IS_CLIENT(it->data)) {
913 ObClient *client = it->data;
914 client_showhide(client);
915 }
916 }
917 }
918
919 if (show) {
920 /* focus desktop */
921 for (it = focus_order; it; it = g_list_next(it)) {
922 ObClient *c = it->data;
923 if (c->type == OB_CLIENT_TYPE_DESKTOP &&
924 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
925 client_focus(it->data))
926 break;
927 }
928 } else if (restore_focus) {
929 ObClient *c;
930
931 /* use NULL for the "old" argument because the desktop was focused
932 and we don't want to fallback to the desktop by default */
933 if ((c = focus_fallback_target(TRUE, NULL)))
934 client_focus(c);
935 }
936
937 show = !!show; /* make it boolean */
938 PROP_SET32(RootWindow(ob_display, ob_screen),
939 net_showing_desktop, cardinal, show);
940 }
941
942 void screen_install_colormap(ObClient *client, gboolean install)
943 {
944 if (client == NULL) {
945 if (install)
946 XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
947 else
948 XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
949 } else {
950 xerror_set_ignore(TRUE);
951 if (install) {
952 if (client->colormap != None)
953 XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
954 } else
955 XUninstallColormap(RrDisplay(ob_rr_inst), client->colormap);
956 xerror_set_ignore(FALSE);
957 }
958 }
959
960 static inline void
961 screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area,
962 gint edge, Strut *ret)
963 {
964 if (s->left &&
965 ((s->left_end <= s->left_start) ||
966 (RECT_TOP(*monitor_area) < s->left_end &&
967 RECT_BOTTOM(*monitor_area) > s->left_start)))
968 ret->left = MAX(ret->left, edge);
969 }
970
971 static inline void
972 screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area,
973 gint edge, Strut *ret)
974 {
975 if (s->top &&
976 ((s->top_end <= s->top_start) ||
977 (RECT_LEFT(*monitor_area) < s->top_end &&
978 RECT_RIGHT(*monitor_area) > s->top_start)))
979 ret->top = MAX(ret->top, edge);
980 }
981
982 static inline void
983 screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area,
984 gint edge, Strut *ret)
985 {
986 if (s->right &&
987 ((s->right_end <= s->right_start) ||
988 (RECT_TOP(*monitor_area) < s->right_end &&
989 RECT_BOTTOM(*monitor_area) > s->right_start)))
990 ret->right = MAX(ret->right, edge);
991 }
992
993 static inline void
994 screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area,
995 gint edge, Strut *ret)
996 {
997 if (s->bottom &&
998 ((s->bottom_end <= s->bottom_start) ||
999 (RECT_LEFT(*monitor_area) < s->bottom_end &&
1000 RECT_RIGHT(*monitor_area) > s->bottom_start)))
1001 ret->bottom = MAX(ret->bottom, edge);
1002 }
1003
1004 void screen_update_areas()
1005 {
1006 guint i, x;
1007 gulong *dims;
1008 GList *it;
1009 gint o;
1010
1011 g_free(monitor_area);
1012 extensions_xinerama_screens(&monitor_area, &screen_num_monitors);
1013
1014 if (area) {
1015 for (i = 0; area[i]; ++i)
1016 g_free(area[i]);
1017 g_free(area);
1018 }
1019
1020 area = g_new(Rect*, screen_num_desktops + 2);
1021 for (i = 0; i < screen_num_desktops + 1; ++i)
1022 area[i] = g_new0(Rect, screen_num_monitors + 1);
1023 area[i] = NULL;
1024
1025 dims = g_new(gulong, 4 * screen_num_desktops);
1026
1027 for (i = 0; i < screen_num_desktops + 1; ++i) {
1028 Strut *struts;
1029 gint l, r, t, b;
1030
1031 struts = g_new0(Strut, screen_num_monitors);
1032
1033 /* calc the xinerama areas */
1034 for (x = 0; x < screen_num_monitors; ++x) {
1035 area[i][x] = monitor_area[x];
1036 if (x == 0) {
1037 l = monitor_area[x].x;
1038 t = monitor_area[x].y;
1039 r = monitor_area[x].x + monitor_area[x].width - 1;
1040 b = monitor_area[x].y + monitor_area[x].height - 1;
1041 } else {
1042 l = MIN(l, monitor_area[x].x);
1043 t = MIN(t, monitor_area[x].y);
1044 r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1);
1045 b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1);
1046 }
1047 }
1048 RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1);
1049
1050 /* apply the struts */
1051
1052 /* find the left-most xin heads, i do this in 2 loops :| */
1053 o = area[i][0].x;
1054 for (x = 1; x < screen_num_monitors; ++x)
1055 o = MIN(o, area[i][x].x);
1056
1057 for (x = 0; x < screen_num_monitors; ++x) {
1058 for (it = client_list; it; it = g_list_next(it)) {
1059 ObClient *c = it->data;
1060 screen_area_add_strut_left(&c->strut,
1061 &monitor_area[x],
1062 o + c->strut.left - area[i][x].x,
1063 &struts[x]);
1064 }
1065 screen_area_add_strut_left(&dock_strut,
1066 &monitor_area[x],
1067 o + dock_strut.left - area[i][x].x,
1068 &struts[x]);
1069
1070 area[i][x].x += struts[x].left;
1071 area[i][x].width -= struts[x].left;
1072 }
1073
1074 /* find the top-most xin heads, i do this in 2 loops :| */
1075 o = area[i][0].y;
1076 for (x = 1; x < screen_num_monitors; ++x)
1077 o = MIN(o, area[i][x].y);
1078
1079 for (x = 0; x < screen_num_monitors; ++x) {
1080 for (it = client_list; it; it = g_list_next(it)) {
1081 ObClient *c = it->data;
1082 screen_area_add_strut_top(&c->strut,
1083 &monitor_area[x],
1084 o + c->strut.top - area[i][x].y,
1085 &struts[x]);
1086 }
1087 screen_area_add_strut_top(&dock_strut,
1088 &monitor_area[x],
1089 o + dock_strut.top - area[i][x].y,
1090 &struts[x]);
1091
1092 area[i][x].y += struts[x].top;
1093 area[i][x].height -= struts[x].top;
1094 }
1095
1096 /* find the right-most xin heads, i do this in 2 loops :| */
1097 o = area[i][0].x + area[i][0].width - 1;
1098 for (x = 1; x < screen_num_monitors; ++x)
1099 o = MAX(o, area[i][x].x + area[i][x].width - 1);
1100
1101 for (x = 0; x < screen_num_monitors; ++x) {
1102 for (it = client_list; it; it = g_list_next(it)) {
1103 ObClient *c = it->data;
1104 screen_area_add_strut_right(&c->strut,
1105 &monitor_area[x],
1106 (area[i][x].x +
1107 area[i][x].width - 1) -
1108 (o - c->strut.right),
1109 &struts[x]);
1110 }
1111 screen_area_add_strut_right(&dock_strut,
1112 &monitor_area[x],
1113 (area[i][x].x +
1114 area[i][x].width - 1) -
1115 (o - dock_strut.right),
1116 &struts[x]);
1117
1118 area[i][x].width -= struts[x].right;
1119 }
1120
1121 /* find the bottom-most xin heads, i do this in 2 loops :| */
1122 o = area[i][0].y + area[i][0].height - 1;
1123 for (x = 1; x < screen_num_monitors; ++x)
1124 o = MAX(o, area[i][x].y + area[i][x].height - 1);
1125
1126 for (x = 0; x < screen_num_monitors; ++x) {
1127 for (it = client_list; it; it = g_list_next(it)) {
1128 ObClient *c = it->data;
1129 screen_area_add_strut_bottom(&c->strut,
1130 &monitor_area[x],
1131 (area[i][x].y +
1132 area[i][x].height - 1) - \
1133 (o - c->strut.bottom),
1134 &struts[x]);
1135 }
1136 screen_area_add_strut_bottom(&dock_strut,
1137 &monitor_area[x],
1138 (area[i][x].y +
1139 area[i][x].height - 1) - \
1140 (o - dock_strut.bottom),
1141 &struts[x]);
1142
1143 area[i][x].height -= struts[x].bottom;
1144 }
1145
1146 l = RECT_LEFT(area[i][0]);
1147 t = RECT_TOP(area[i][0]);
1148 r = RECT_RIGHT(area[i][0]);
1149 b = RECT_BOTTOM(area[i][0]);
1150 for (x = 1; x < screen_num_monitors; ++x) {
1151 l = MIN(l, RECT_LEFT(area[i][x]));
1152 t = MIN(l, RECT_TOP(area[i][x]));
1153 r = MAX(r, RECT_RIGHT(area[i][x]));
1154 b = MAX(b, RECT_BOTTOM(area[i][x]));
1155 }
1156 RECT_SET(area[i][screen_num_monitors], l, t,
1157 r - l + 1, b - t + 1);
1158
1159 /* XXX optimize when this is run? */
1160
1161 /* the area has changed, adjust all the maximized
1162 windows */
1163 for (it = client_list; it; it = g_list_next(it)) {
1164 ObClient *c = it->data;
1165 if (i < screen_num_desktops) {
1166 if (c->desktop == i)
1167 client_reconfigure(c);
1168 } else if (c->desktop == DESKTOP_ALL)
1169 client_reconfigure(c);
1170 }
1171 if (i < screen_num_desktops) {
1172 /* don't set these for the 'all desktops' area */
1173 dims[(i * 4) + 0] = area[i][screen_num_monitors].x;
1174 dims[(i * 4) + 1] = area[i][screen_num_monitors].y;
1175 dims[(i * 4) + 2] = area[i][screen_num_monitors].width;
1176 dims[(i * 4) + 3] = area[i][screen_num_monitors].height;
1177 }
1178
1179 g_free(struts);
1180 }
1181
1182 PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
1183 dims, 4 * screen_num_desktops);
1184
1185 g_free(dims);
1186 }
1187
1188 Rect *screen_area(guint desktop)
1189 {
1190 return screen_area_monitor(desktop, screen_num_monitors);
1191 }
1192
1193 Rect *screen_area_monitor(guint desktop, guint head)
1194 {
1195 if (head > screen_num_monitors)
1196 return NULL;
1197 if (desktop >= screen_num_desktops) {
1198 if (desktop == DESKTOP_ALL)
1199 return &area[screen_num_desktops][head];
1200 return NULL;
1201 }
1202 return &area[desktop][head];
1203 }
1204
1205 guint screen_find_monitor(Rect *search)
1206 {
1207 guint i;
1208 guint most = 0;
1209 guint mostv = 0;
1210
1211 for (i = 0; i < screen_num_monitors; ++i) {
1212 Rect *area = screen_physical_area_monitor(i);
1213 if (RECT_INTERSECTS_RECT(*area, *search)) {
1214 Rect r;
1215 guint v;
1216
1217 RECT_SET_INTERSECTION(r, *area, *search);
1218 v = r.width * r.height;
1219
1220 if (v > mostv) {
1221 mostv = v;
1222 most = i;
1223 }
1224 }
1225 }
1226 return most;
1227 }
1228
1229 Rect *screen_physical_area()
1230 {
1231 return screen_physical_area_monitor(screen_num_monitors);
1232 }
1233
1234 Rect *screen_physical_area_monitor(guint head)
1235 {
1236 if (head > screen_num_monitors)
1237 return NULL;
1238 return &monitor_area[head];
1239 }
1240
1241 void screen_set_root_cursor()
1242 {
1243 if (sn_app_starting())
1244 XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1245 ob_cursor(OB_CURSOR_BUSY));
1246 else
1247 XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1248 ob_cursor(OB_CURSOR_POINTER));
1249 }
1250
1251 gboolean screen_pointer_pos(gint *x, gint *y)
1252 {
1253 Window w;
1254 gint i;
1255 guint u;
1256 gboolean ret;
1257
1258 ret = !!XQueryPointer(ob_display, RootWindow(ob_display, ob_screen),
1259 &w, &w, x, y, &i, &i, &u);
1260 if (!ret) {
1261 for (i = 0; i < ScreenCount(ob_display); ++i)
1262 if (i != ob_screen)
1263 if (XQueryPointer(ob_display, RootWindow(ob_display, i),
1264 &w, &w, x, y, &i, &i, &u))
1265 break;
1266 }
1267 return ret;
1268 }
This page took 0.084834 seconds and 3 git commands to generate.