]> Dogcows Code - chaz/openbox/blob - openbox/screen.c
add support for _NET_WM_USER_TIME_WINDOW. round 1 ! ding.
[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_wm_user_time_window;
277 supported[i++] = prop_atoms.net_frame_extents;
278 supported[i++] = prop_atoms.net_startup_id;
279 #ifdef SYNC
280 supported[i++] = prop_atoms.net_wm_sync_request;
281 supported[i++] = prop_atoms.net_wm_sync_request_counter;
282 #endif
283
284 supported[i++] = prop_atoms.kde_wm_change_state;
285 supported[i++] = prop_atoms.kde_net_wm_frame_strut;
286 supported[i++] = prop_atoms.kde_net_wm_window_type_override;
287
288 supported[i++] = prop_atoms.openbox_wm_state_undecorated;
289 supported[i++] = prop_atoms.openbox_pid;
290 supported[i++] = prop_atoms.openbox_config;
291 supported[i++] = prop_atoms.openbox_control;
292 g_assert(i == num_support);
293
294 PROP_SETA32(RootWindow(ob_display, ob_screen),
295 net_supported, atom, supported, num_support);
296 g_free(supported);
297
298 return TRUE;
299 }
300
301 void screen_startup(gboolean reconfig)
302 {
303 guint i, numnames;
304 gchar **names;
305 GSList *it;
306 guint32 d;
307
308 desktop_cycle_popup = pager_popup_new(FALSE);
309 pager_popup_height(desktop_cycle_popup, POPUP_HEIGHT);
310
311 if (reconfig)
312 return;
313
314 /* get the initial size */
315 screen_resize();
316
317 /* get the desktop names */
318 numnames = g_slist_length(config_desktops_names);
319 names = g_new(gchar*, numnames + 1);
320 names[numnames] = NULL;
321 for (i = 0, it = config_desktops_names; it; ++i, it = g_slist_next(it))
322 names[i] = g_strdup(it->data);
323
324 /* set the root window property */
325 PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,names);
326
327 g_strfreev(names);
328
329 /* set the number of desktops */
330 screen_num_desktops = 0;
331 screen_set_num_desktops(config_desktops_num);
332
333 /* start on the current desktop when a wm was already running */
334 if (PROP_GET32(RootWindow(ob_display, ob_screen),
335 net_current_desktop, cardinal, &d) &&
336 d < screen_num_desktops)
337 {
338 screen_set_desktop(d, FALSE);
339 } else if (session_desktop >= 0)
340 screen_set_desktop(MIN((guint)session_desktop,
341 screen_num_desktops), FALSE);
342 else
343 screen_set_desktop(MIN(config_screen_firstdesk,
344 screen_num_desktops) - 1, FALSE);
345
346 /* don't start in showing-desktop mode */
347 screen_showing_desktop = FALSE;
348 PROP_SET32(RootWindow(ob_display, ob_screen),
349 net_showing_desktop, cardinal, screen_showing_desktop);
350
351 screen_update_layout();
352 }
353
354 void screen_shutdown(gboolean reconfig)
355 {
356 Rect **r;
357
358 pager_popup_free(desktop_cycle_popup);
359
360 if (reconfig)
361 return;
362
363 XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
364 NoEventMask);
365
366 /* we're not running here no more! */
367 PROP_ERASE(RootWindow(ob_display, ob_screen), openbox_pid);
368 /* not without us */
369 PROP_ERASE(RootWindow(ob_display, ob_screen), net_supported);
370 /* don't keep this mode */
371 PROP_ERASE(RootWindow(ob_display, ob_screen), net_showing_desktop);
372
373 XDestroyWindow(ob_display, screen_support_win);
374
375 g_strfreev(screen_desktop_names);
376 screen_desktop_names = NULL;
377
378 for (r = area; *r; ++r)
379 g_free(*r);
380 g_free(area);
381 area = NULL;
382 }
383
384 void screen_resize()
385 {
386 static gint oldw = 0, oldh = 0;
387 gint w, h;
388 GList *it;
389 gulong geometry[2];
390
391 w = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
392 h = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen));
393
394 if (w == oldw && h == oldh) return;
395
396 oldw = w; oldh = h;
397
398 /* Set the _NET_DESKTOP_GEOMETRY hint */
399 screen_physical_size.width = geometry[0] = w;
400 screen_physical_size.height = geometry[1] = h;
401 PROP_SETA32(RootWindow(ob_display, ob_screen),
402 net_desktop_geometry, cardinal, geometry, 2);
403
404 if (ob_state() == OB_STATE_STARTING)
405 return;
406
407 screen_update_areas();
408 dock_configure();
409
410 for (it = client_list; it; it = g_list_next(it))
411 client_move_onscreen(it->data, FALSE);
412 }
413
414 void screen_set_num_desktops(guint num)
415 {
416 guint old;
417 gulong *viewport;
418 GList *it;
419
420 g_assert(num > 0);
421
422 if (screen_num_desktops == num) return;
423
424 old = screen_num_desktops;
425 screen_num_desktops = num;
426 PROP_SET32(RootWindow(ob_display, ob_screen),
427 net_number_of_desktops, cardinal, num);
428
429 /* set the viewport hint */
430 viewport = g_new0(gulong, num * 2);
431 PROP_SETA32(RootWindow(ob_display, ob_screen),
432 net_desktop_viewport, cardinal, viewport, num * 2);
433 g_free(viewport);
434
435 /* the number of rows/columns will differ */
436 screen_update_layout();
437
438 /* move windows on desktops that will no longer exist! */
439 for (it = client_list; it; it = g_list_next(it)) {
440 ObClient *c = it->data;
441 if (c->desktop >= num && c->desktop != DESKTOP_ALL)
442 client_set_desktop(c, num - 1, FALSE);
443 }
444
445 /* change our struts/area to match (after moving windows) */
446 screen_update_areas();
447
448 /* may be some unnamed desktops that we need to fill in with names
449 (after updating the areas so the popup can resize) */
450 screen_update_desktop_names();
451
452 /* change our desktop if we're on one that no longer exists! */
453 if (screen_desktop >= screen_num_desktops)
454 screen_set_desktop(num - 1, TRUE);
455 }
456
457 void screen_set_desktop(guint num, gboolean dofocus)
458 {
459 ObClient *c;
460 GList *it;
461 guint old;
462
463 g_assert(num < screen_num_desktops);
464
465 old = screen_desktop;
466 screen_desktop = num;
467 PROP_SET32(RootWindow(ob_display, ob_screen),
468 net_current_desktop, cardinal, num);
469
470 if (old == num) return;
471
472 screen_last_desktop = old;
473
474 ob_debug("Moving to desktop %d\n", num+1);
475
476 if (moveresize_client)
477 client_set_desktop(moveresize_client, num, TRUE);
478
479 /* show windows before hiding the rest to lessen the enter/leave events */
480
481 /* show windows from top to bottom */
482 for (it = stacking_list; it; it = g_list_next(it)) {
483 if (WINDOW_IS_CLIENT(it->data)) {
484 ObClient *c = it->data;
485 client_show(c);
486 }
487 }
488
489 /* have to try focus here because when you leave an empty desktop
490 there is no focus out to watch for
491
492 do this before hiding the windows so if helper windows are coming
493 with us, they don't get hidden
494 */
495 if (dofocus && (c = focus_fallback_target(TRUE, focus_client))) {
496 /* only do the flicker reducing stuff ahead of time if we are going
497 to call xsetinputfocus on the window ourselves. otherwise there is
498 no guarantee the window will actually take focus.. */
499 if (c->can_focus) {
500 /* do this here so that if you switch desktops to a window with
501 helper windows then the helper windows won't flash */
502 client_bring_helper_windows(c);
503 /* reduce flicker by hiliting now rather than waiting for the
504 server FocusIn event */
505 frame_adjust_focus(c->frame, TRUE);
506 }
507 client_focus(c);
508 }
509
510 /* hide windows from bottom to top */
511 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
512 if (WINDOW_IS_CLIENT(it->data)) {
513 ObClient *c = it->data;
514 client_hide(c);
515 }
516 }
517
518 event_ignore_queued_enters();
519
520 if (event_curtime != CurrentTime)
521 screen_desktop_user_time = event_curtime;
522 }
523
524 static void get_row_col(guint d, guint *r, guint *c)
525 {
526 switch (screen_desktop_layout.orientation) {
527 case OB_ORIENTATION_HORZ:
528 switch (screen_desktop_layout.start_corner) {
529 case OB_CORNER_TOPLEFT:
530 *r = d / screen_desktop_layout.columns;
531 *c = d % screen_desktop_layout.columns;
532 break;
533 case OB_CORNER_BOTTOMLEFT:
534 *r = screen_desktop_layout.rows - 1 -
535 d / screen_desktop_layout.columns;
536 *c = d % screen_desktop_layout.columns;
537 break;
538 case OB_CORNER_TOPRIGHT:
539 *r = d / screen_desktop_layout.columns;
540 *c = screen_desktop_layout.columns - 1 -
541 d % screen_desktop_layout.columns;
542 break;
543 case OB_CORNER_BOTTOMRIGHT:
544 *r = screen_desktop_layout.rows - 1 -
545 d / screen_desktop_layout.columns;
546 *c = screen_desktop_layout.columns - 1 -
547 d % screen_desktop_layout.columns;
548 break;
549 }
550 break;
551 case OB_ORIENTATION_VERT:
552 switch (screen_desktop_layout.start_corner) {
553 case OB_CORNER_TOPLEFT:
554 *r = d % screen_desktop_layout.rows;
555 *c = d / screen_desktop_layout.rows;
556 break;
557 case OB_CORNER_BOTTOMLEFT:
558 *r = screen_desktop_layout.rows - 1 -
559 d % screen_desktop_layout.rows;
560 *c = d / screen_desktop_layout.rows;
561 break;
562 case OB_CORNER_TOPRIGHT:
563 *r = d % screen_desktop_layout.rows;
564 *c = screen_desktop_layout.columns - 1 -
565 d / screen_desktop_layout.rows;
566 break;
567 case OB_CORNER_BOTTOMRIGHT:
568 *r = screen_desktop_layout.rows - 1 -
569 d % screen_desktop_layout.rows;
570 *c = screen_desktop_layout.columns - 1 -
571 d / screen_desktop_layout.rows;
572 break;
573 }
574 break;
575 }
576 }
577
578 static guint translate_row_col(guint r, guint c)
579 {
580 switch (screen_desktop_layout.orientation) {
581 case OB_ORIENTATION_HORZ:
582 switch (screen_desktop_layout.start_corner) {
583 case OB_CORNER_TOPLEFT:
584 return r % screen_desktop_layout.rows *
585 screen_desktop_layout.columns +
586 c % screen_desktop_layout.columns;
587 case OB_CORNER_BOTTOMLEFT:
588 return (screen_desktop_layout.rows - 1 -
589 r % screen_desktop_layout.rows) *
590 screen_desktop_layout.columns +
591 c % screen_desktop_layout.columns;
592 case OB_CORNER_TOPRIGHT:
593 return r % screen_desktop_layout.rows *
594 screen_desktop_layout.columns +
595 (screen_desktop_layout.columns - 1 -
596 c % screen_desktop_layout.columns);
597 case OB_CORNER_BOTTOMRIGHT:
598 return (screen_desktop_layout.rows - 1 -
599 r % screen_desktop_layout.rows) *
600 screen_desktop_layout.columns +
601 (screen_desktop_layout.columns - 1 -
602 c % screen_desktop_layout.columns);
603 }
604 case OB_ORIENTATION_VERT:
605 switch (screen_desktop_layout.start_corner) {
606 case OB_CORNER_TOPLEFT:
607 return c % screen_desktop_layout.columns *
608 screen_desktop_layout.rows +
609 r % screen_desktop_layout.rows;
610 case OB_CORNER_BOTTOMLEFT:
611 return c % screen_desktop_layout.columns *
612 screen_desktop_layout.rows +
613 (screen_desktop_layout.rows - 1 -
614 r % screen_desktop_layout.rows);
615 case OB_CORNER_TOPRIGHT:
616 return (screen_desktop_layout.columns - 1 -
617 c % screen_desktop_layout.columns) *
618 screen_desktop_layout.rows +
619 r % screen_desktop_layout.rows;
620 case OB_CORNER_BOTTOMRIGHT:
621 return (screen_desktop_layout.columns - 1 -
622 c % screen_desktop_layout.columns) *
623 screen_desktop_layout.rows +
624 (screen_desktop_layout.rows - 1 -
625 r % screen_desktop_layout.rows);
626 }
627 }
628 g_assert_not_reached();
629 return 0;
630 }
631
632 void screen_desktop_popup(guint d, gboolean show)
633 {
634 Rect *a;
635
636 if (!show) {
637 pager_popup_hide(desktop_cycle_popup);
638 } else {
639 a = screen_physical_area_monitor(0);
640 pager_popup_position(desktop_cycle_popup, CenterGravity,
641 a->x + a->width / 2, a->y + a->height / 2);
642 pager_popup_max_width(desktop_cycle_popup,
643 MAX(a->width/3, POPUP_WIDTH));
644 pager_popup_show(desktop_cycle_popup, screen_desktop_names[d], d);
645 }
646 }
647
648 guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
649 gboolean dialog, gboolean done, gboolean cancel)
650 {
651 guint d, r, c;
652
653 d = screen_desktop;
654
655 if ((cancel || done) && dialog)
656 goto show_cycle_dialog;
657
658 get_row_col(d, &r, &c);
659
660 if (linear) {
661 switch (dir) {
662 case OB_DIRECTION_EAST:
663 if (d < screen_num_desktops - 1)
664 ++d;
665 else if (wrap)
666 d = 0;
667 break;
668 case OB_DIRECTION_WEST:
669 if (d > 0)
670 --d;
671 else if (wrap)
672 d = screen_num_desktops - 1;
673 break;
674 default:
675 assert(0);
676 return screen_desktop;
677 }
678 } else {
679 switch (dir) {
680 case OB_DIRECTION_EAST:
681 ++c;
682 if (c >= screen_desktop_layout.columns) {
683 if (wrap) {
684 c = 0;
685 } else {
686 d = screen_desktop;
687 goto show_cycle_dialog;
688 }
689 }
690 d = translate_row_col(r, c);
691 if (d >= screen_num_desktops) {
692 if (wrap) {
693 ++c;
694 } else {
695 d = screen_desktop;
696 goto show_cycle_dialog;
697 }
698 }
699 break;
700 case OB_DIRECTION_WEST:
701 --c;
702 if (c >= screen_desktop_layout.columns) {
703 if (wrap) {
704 c = screen_desktop_layout.columns - 1;
705 } else {
706 d = screen_desktop;
707 goto show_cycle_dialog;
708 }
709 }
710 d = translate_row_col(r, c);
711 if (d >= screen_num_desktops) {
712 if (wrap) {
713 --c;
714 } else {
715 d = screen_desktop;
716 goto show_cycle_dialog;
717 }
718 }
719 break;
720 case OB_DIRECTION_SOUTH:
721 ++r;
722 if (r >= screen_desktop_layout.rows) {
723 if (wrap) {
724 r = 0;
725 } else {
726 d = screen_desktop;
727 goto show_cycle_dialog;
728 }
729 }
730 d = translate_row_col(r, c);
731 if (d >= screen_num_desktops) {
732 if (wrap) {
733 ++r;
734 } else {
735 d = screen_desktop;
736 goto show_cycle_dialog;
737 }
738 }
739 break;
740 case OB_DIRECTION_NORTH:
741 --r;
742 if (r >= screen_desktop_layout.rows) {
743 if (wrap) {
744 r = screen_desktop_layout.rows - 1;
745 } else {
746 d = screen_desktop;
747 goto show_cycle_dialog;
748 }
749 }
750 d = translate_row_col(r, c);
751 if (d >= screen_num_desktops) {
752 if (wrap) {
753 --r;
754 } else {
755 d = screen_desktop;
756 goto show_cycle_dialog;
757 }
758 }
759 break;
760 default:
761 assert(0);
762 return d = screen_desktop;
763 }
764
765 d = translate_row_col(r, c);
766 }
767
768 show_cycle_dialog:
769 if (dialog && !cancel && !done) {
770 screen_desktop_popup(d, TRUE);
771 } else
772 screen_desktop_popup(0, FALSE);
773 return d;
774 }
775
776 void screen_update_layout()
777 {
778 ObOrientation orient;
779 ObCorner corner;
780 guint rows;
781 guint cols;
782 guint32 *data;
783 guint num;
784 gboolean valid = FALSE;
785
786 if (PROP_GETA32(RootWindow(ob_display, ob_screen),
787 net_desktop_layout, cardinal, &data, &num)) {
788 if (num == 3 || num == 4) {
789
790 if (data[0] == prop_atoms.net_wm_orientation_vert)
791 orient = OB_ORIENTATION_VERT;
792 else if (data[0] == prop_atoms.net_wm_orientation_horz)
793 orient = OB_ORIENTATION_HORZ;
794 else
795 goto screen_update_layout_bail;
796
797 if (num < 4)
798 corner = OB_CORNER_TOPLEFT;
799 else {
800 if (data[3] == prop_atoms.net_wm_topleft)
801 corner = OB_CORNER_TOPLEFT;
802 else if (data[3] == prop_atoms.net_wm_topright)
803 corner = OB_CORNER_TOPRIGHT;
804 else if (data[3] == prop_atoms.net_wm_bottomright)
805 corner = OB_CORNER_BOTTOMRIGHT;
806 else if (data[3] == prop_atoms.net_wm_bottomleft)
807 corner = OB_CORNER_BOTTOMLEFT;
808 else
809 goto screen_update_layout_bail;
810 }
811
812 cols = data[1];
813 rows = data[2];
814
815 /* fill in a zero rows/columns */
816 if ((cols == 0 && rows == 0)) { /* both 0's is bad data.. */
817 goto screen_update_layout_bail;
818 } else {
819 if (cols == 0) {
820 cols = screen_num_desktops / rows;
821 if (rows * cols < screen_num_desktops)
822 cols++;
823 if (rows * cols >= screen_num_desktops + cols)
824 rows--;
825 } else if (rows == 0) {
826 rows = screen_num_desktops / cols;
827 if (cols * rows < screen_num_desktops)
828 rows++;
829 if (cols * rows >= screen_num_desktops + rows)
830 cols--;
831 }
832 }
833
834 /* bounds checking */
835 if (orient == OB_ORIENTATION_HORZ) {
836 cols = MIN(screen_num_desktops, cols);
837 rows = MIN(rows, (screen_num_desktops + cols - 1) / cols);
838 cols = screen_num_desktops / rows +
839 !!(screen_num_desktops % rows);
840 } else {
841 rows = MIN(screen_num_desktops, rows);
842 cols = MIN(cols, (screen_num_desktops + rows - 1) / rows);
843 rows = screen_num_desktops / cols +
844 !!(screen_num_desktops % cols);
845 }
846
847 valid = TRUE;
848 }
849 screen_update_layout_bail:
850 g_free(data);
851 }
852
853 if (!valid) {
854 /* defaults */
855 orient = OB_ORIENTATION_HORZ;
856 corner = OB_CORNER_TOPLEFT;
857 rows = 1;
858 cols = screen_num_desktops;
859 }
860
861 screen_desktop_layout.orientation = orient;
862 screen_desktop_layout.start_corner = corner;
863 screen_desktop_layout.rows = rows;
864 screen_desktop_layout.columns = cols;
865 }
866
867 void screen_update_desktop_names()
868 {
869 guint i;
870
871 /* empty the array */
872 g_strfreev(screen_desktop_names);
873 screen_desktop_names = NULL;
874
875 if (PROP_GETSS(RootWindow(ob_display, ob_screen),
876 net_desktop_names, utf8, &screen_desktop_names))
877 for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
878 else
879 i = 0;
880 if (i < screen_num_desktops) {
881 screen_desktop_names = g_renew(gchar*, screen_desktop_names,
882 screen_num_desktops + 1);
883 screen_desktop_names[screen_num_desktops] = NULL;
884 for (; i < screen_num_desktops; ++i)
885 screen_desktop_names[i] = g_strdup_printf("desktop %i", i + 1);
886 }
887
888 /* resize the pager for these names */
889 pager_popup_text_width_to_strings(desktop_cycle_popup,
890 screen_desktop_names,
891 screen_num_desktops);
892 }
893
894 void screen_show_desktop(gboolean show, gboolean restore_focus)
895 {
896 GList *it;
897
898 if (show == screen_showing_desktop) return; /* no change */
899
900 screen_showing_desktop = show;
901
902 if (show) {
903 /* bottom to top */
904 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
905 if (WINDOW_IS_CLIENT(it->data)) {
906 ObClient *client = it->data;
907 client_showhide(client);
908 }
909 }
910 } else {
911 /* top to bottom */
912 for (it = stacking_list; it; it = g_list_next(it)) {
913 if (WINDOW_IS_CLIENT(it->data)) {
914 ObClient *client = it->data;
915 client_showhide(client);
916 }
917 }
918 }
919
920 if (show) {
921 /* focus desktop */
922 for (it = focus_order; it; it = g_list_next(it)) {
923 ObClient *c = it->data;
924 if (c->type == OB_CLIENT_TYPE_DESKTOP &&
925 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
926 client_focus(it->data))
927 break;
928 }
929 } else if (restore_focus) {
930 ObClient *c;
931
932 /* use NULL for the "old" argument because the desktop was focused
933 and we don't want to fallback to the desktop by default */
934 if ((c = focus_fallback_target(TRUE, NULL)))
935 client_focus(c);
936 }
937
938 show = !!show; /* make it boolean */
939 PROP_SET32(RootWindow(ob_display, ob_screen),
940 net_showing_desktop, cardinal, show);
941 }
942
943 void screen_install_colormap(ObClient *client, gboolean install)
944 {
945 if (client == NULL) {
946 if (install)
947 XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
948 else
949 XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
950 } else {
951 xerror_set_ignore(TRUE);
952 if (install) {
953 if (client->colormap != None)
954 XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
955 } else
956 XUninstallColormap(RrDisplay(ob_rr_inst), client->colormap);
957 xerror_set_ignore(FALSE);
958 }
959 }
960
961 static inline void
962 screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area,
963 gint edge, Strut *ret)
964 {
965 if (s->left &&
966 ((s->left_end <= s->left_start) ||
967 (RECT_TOP(*monitor_area) < s->left_end &&
968 RECT_BOTTOM(*monitor_area) > s->left_start)))
969 ret->left = MAX(ret->left, edge);
970 }
971
972 static inline void
973 screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area,
974 gint edge, Strut *ret)
975 {
976 if (s->top &&
977 ((s->top_end <= s->top_start) ||
978 (RECT_LEFT(*monitor_area) < s->top_end &&
979 RECT_RIGHT(*monitor_area) > s->top_start)))
980 ret->top = MAX(ret->top, edge);
981 }
982
983 static inline void
984 screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area,
985 gint edge, Strut *ret)
986 {
987 if (s->right &&
988 ((s->right_end <= s->right_start) ||
989 (RECT_TOP(*monitor_area) < s->right_end &&
990 RECT_BOTTOM(*monitor_area) > s->right_start)))
991 ret->right = MAX(ret->right, edge);
992 }
993
994 static inline void
995 screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area,
996 gint edge, Strut *ret)
997 {
998 if (s->bottom &&
999 ((s->bottom_end <= s->bottom_start) ||
1000 (RECT_LEFT(*monitor_area) < s->bottom_end &&
1001 RECT_RIGHT(*monitor_area) > s->bottom_start)))
1002 ret->bottom = MAX(ret->bottom, edge);
1003 }
1004
1005 void screen_update_areas()
1006 {
1007 guint i, x;
1008 gulong *dims;
1009 GList *it;
1010 gint o;
1011
1012 g_free(monitor_area);
1013 extensions_xinerama_screens(&monitor_area, &screen_num_monitors);
1014
1015 if (area) {
1016 for (i = 0; area[i]; ++i)
1017 g_free(area[i]);
1018 g_free(area);
1019 }
1020
1021 area = g_new(Rect*, screen_num_desktops + 2);
1022 for (i = 0; i < screen_num_desktops + 1; ++i)
1023 area[i] = g_new0(Rect, screen_num_monitors + 1);
1024 area[i] = NULL;
1025
1026 dims = g_new(gulong, 4 * screen_num_desktops);
1027
1028 for (i = 0; i < screen_num_desktops + 1; ++i) {
1029 Strut *struts;
1030 gint l, r, t, b;
1031
1032 struts = g_new0(Strut, screen_num_monitors);
1033
1034 /* calc the xinerama areas */
1035 for (x = 0; x < screen_num_monitors; ++x) {
1036 area[i][x] = monitor_area[x];
1037 if (x == 0) {
1038 l = monitor_area[x].x;
1039 t = monitor_area[x].y;
1040 r = monitor_area[x].x + monitor_area[x].width - 1;
1041 b = monitor_area[x].y + monitor_area[x].height - 1;
1042 } else {
1043 l = MIN(l, monitor_area[x].x);
1044 t = MIN(t, monitor_area[x].y);
1045 r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1);
1046 b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1);
1047 }
1048 }
1049 RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1);
1050
1051 /* apply the struts */
1052
1053 /* find the left-most xin heads, i do this in 2 loops :| */
1054 o = area[i][0].x;
1055 for (x = 1; x < screen_num_monitors; ++x)
1056 o = MIN(o, area[i][x].x);
1057
1058 for (x = 0; x < screen_num_monitors; ++x) {
1059 for (it = client_list; it; it = g_list_next(it)) {
1060 ObClient *c = it->data;
1061 screen_area_add_strut_left(&c->strut,
1062 &monitor_area[x],
1063 o + c->strut.left - area[i][x].x,
1064 &struts[x]);
1065 }
1066 screen_area_add_strut_left(&dock_strut,
1067 &monitor_area[x],
1068 o + dock_strut.left - area[i][x].x,
1069 &struts[x]);
1070
1071 area[i][x].x += struts[x].left;
1072 area[i][x].width -= struts[x].left;
1073 }
1074
1075 /* find the top-most xin heads, i do this in 2 loops :| */
1076 o = area[i][0].y;
1077 for (x = 1; x < screen_num_monitors; ++x)
1078 o = MIN(o, area[i][x].y);
1079
1080 for (x = 0; x < screen_num_monitors; ++x) {
1081 for (it = client_list; it; it = g_list_next(it)) {
1082 ObClient *c = it->data;
1083 screen_area_add_strut_top(&c->strut,
1084 &monitor_area[x],
1085 o + c->strut.top - area[i][x].y,
1086 &struts[x]);
1087 }
1088 screen_area_add_strut_top(&dock_strut,
1089 &monitor_area[x],
1090 o + dock_strut.top - area[i][x].y,
1091 &struts[x]);
1092
1093 area[i][x].y += struts[x].top;
1094 area[i][x].height -= struts[x].top;
1095 }
1096
1097 /* find the right-most xin heads, i do this in 2 loops :| */
1098 o = area[i][0].x + area[i][0].width - 1;
1099 for (x = 1; x < screen_num_monitors; ++x)
1100 o = MAX(o, area[i][x].x + area[i][x].width - 1);
1101
1102 for (x = 0; x < screen_num_monitors; ++x) {
1103 for (it = client_list; it; it = g_list_next(it)) {
1104 ObClient *c = it->data;
1105 screen_area_add_strut_right(&c->strut,
1106 &monitor_area[x],
1107 (area[i][x].x +
1108 area[i][x].width - 1) -
1109 (o - c->strut.right),
1110 &struts[x]);
1111 }
1112 screen_area_add_strut_right(&dock_strut,
1113 &monitor_area[x],
1114 (area[i][x].x +
1115 area[i][x].width - 1) -
1116 (o - dock_strut.right),
1117 &struts[x]);
1118
1119 area[i][x].width -= struts[x].right;
1120 }
1121
1122 /* find the bottom-most xin heads, i do this in 2 loops :| */
1123 o = area[i][0].y + area[i][0].height - 1;
1124 for (x = 1; x < screen_num_monitors; ++x)
1125 o = MAX(o, area[i][x].y + area[i][x].height - 1);
1126
1127 for (x = 0; x < screen_num_monitors; ++x) {
1128 for (it = client_list; it; it = g_list_next(it)) {
1129 ObClient *c = it->data;
1130 screen_area_add_strut_bottom(&c->strut,
1131 &monitor_area[x],
1132 (area[i][x].y +
1133 area[i][x].height - 1) - \
1134 (o - c->strut.bottom),
1135 &struts[x]);
1136 }
1137 screen_area_add_strut_bottom(&dock_strut,
1138 &monitor_area[x],
1139 (area[i][x].y +
1140 area[i][x].height - 1) - \
1141 (o - dock_strut.bottom),
1142 &struts[x]);
1143
1144 area[i][x].height -= struts[x].bottom;
1145 }
1146
1147 l = RECT_LEFT(area[i][0]);
1148 t = RECT_TOP(area[i][0]);
1149 r = RECT_RIGHT(area[i][0]);
1150 b = RECT_BOTTOM(area[i][0]);
1151 for (x = 1; x < screen_num_monitors; ++x) {
1152 l = MIN(l, RECT_LEFT(area[i][x]));
1153 t = MIN(l, RECT_TOP(area[i][x]));
1154 r = MAX(r, RECT_RIGHT(area[i][x]));
1155 b = MAX(b, RECT_BOTTOM(area[i][x]));
1156 }
1157 RECT_SET(area[i][screen_num_monitors], l, t,
1158 r - l + 1, b - t + 1);
1159
1160 /* XXX optimize when this is run? */
1161
1162 /* the area has changed, adjust all the maximized
1163 windows */
1164 for (it = client_list; it; it = g_list_next(it)) {
1165 ObClient *c = it->data;
1166 if (i < screen_num_desktops) {
1167 if (c->desktop == i)
1168 client_reconfigure(c);
1169 } else if (c->desktop == DESKTOP_ALL)
1170 client_reconfigure(c);
1171 }
1172 if (i < screen_num_desktops) {
1173 /* don't set these for the 'all desktops' area */
1174 dims[(i * 4) + 0] = area[i][screen_num_monitors].x;
1175 dims[(i * 4) + 1] = area[i][screen_num_monitors].y;
1176 dims[(i * 4) + 2] = area[i][screen_num_monitors].width;
1177 dims[(i * 4) + 3] = area[i][screen_num_monitors].height;
1178 }
1179
1180 g_free(struts);
1181 }
1182
1183 PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
1184 dims, 4 * screen_num_desktops);
1185
1186 g_free(dims);
1187 }
1188
1189 Rect *screen_area(guint desktop)
1190 {
1191 return screen_area_monitor(desktop, screen_num_monitors);
1192 }
1193
1194 Rect *screen_area_monitor(guint desktop, guint head)
1195 {
1196 if (head > screen_num_monitors)
1197 return NULL;
1198 if (desktop >= screen_num_desktops) {
1199 if (desktop == DESKTOP_ALL)
1200 return &area[screen_num_desktops][head];
1201 return NULL;
1202 }
1203 return &area[desktop][head];
1204 }
1205
1206 guint screen_find_monitor(Rect *search)
1207 {
1208 guint i;
1209 guint most = 0;
1210 guint mostv = 0;
1211
1212 for (i = 0; i < screen_num_monitors; ++i) {
1213 Rect *area = screen_physical_area_monitor(i);
1214 if (RECT_INTERSECTS_RECT(*area, *search)) {
1215 Rect r;
1216 guint v;
1217
1218 RECT_SET_INTERSECTION(r, *area, *search);
1219 v = r.width * r.height;
1220
1221 if (v > mostv) {
1222 mostv = v;
1223 most = i;
1224 }
1225 }
1226 }
1227 return most;
1228 }
1229
1230 Rect *screen_physical_area()
1231 {
1232 return screen_physical_area_monitor(screen_num_monitors);
1233 }
1234
1235 Rect *screen_physical_area_monitor(guint head)
1236 {
1237 if (head > screen_num_monitors)
1238 return NULL;
1239 return &monitor_area[head];
1240 }
1241
1242 void screen_set_root_cursor()
1243 {
1244 if (sn_app_starting())
1245 XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1246 ob_cursor(OB_CURSOR_BUSY));
1247 else
1248 XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1249 ob_cursor(OB_CURSOR_POINTER));
1250 }
1251
1252 gboolean screen_pointer_pos(gint *x, gint *y)
1253 {
1254 Window w;
1255 gint i;
1256 guint u;
1257 gboolean ret;
1258
1259 ret = !!XQueryPointer(ob_display, RootWindow(ob_display, ob_screen),
1260 &w, &w, x, y, &i, &i, &u);
1261 if (!ret) {
1262 for (i = 0; i < ScreenCount(ob_display); ++i)
1263 if (i != ob_screen)
1264 if (XQueryPointer(ob_display, RootWindow(ob_display, i),
1265 &w, &w, x, y, &i, &i, &u))
1266 break;
1267 }
1268 return ret;
1269 }
This page took 0.093867 seconds and 5 git commands to generate.