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.
26 #include "startupnotify.h"
27 #include "moveresize.h"
36 #include "extensions.h"
37 #include "render/render.h"
42 # include <sys/types.h>
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)
53 static gboolean
screen_validate_layout(ObDesktopLayout
*l
);
54 static gboolean
replace_wm();
55 static void screen_tell_ksplash();
57 guint screen_num_desktops
;
58 guint screen_num_monitors
;
60 guint screen_last_desktop
;
61 Size screen_physical_size
;
62 gboolean screen_showing_desktop
;
63 ObDesktopLayout screen_desktop_layout
;
64 gchar
**screen_desktop_names
;
65 Window screen_support_win
;
66 Time screen_desktop_user_time
= CurrentTime
;
68 static Rect
**area
; /* array of desktop holding array of xinerama areas */
69 static Rect
*monitor_area
;
71 static ObPagerPopup
*desktop_cycle_popup
;
73 static gboolean
replace_wm()
77 Window current_wm_sn_owner
;
80 wm_sn
= g_strdup_printf("WM_S%d", ob_screen
);
81 wm_sn_atom
= XInternAtom(ob_display
, wm_sn
, FALSE
);
84 current_wm_sn_owner
= XGetSelectionOwner(ob_display
, wm_sn_atom
);
85 if (current_wm_sn_owner
== screen_support_win
)
86 current_wm_sn_owner
= None
;
87 if (current_wm_sn_owner
) {
89 g_message(_("A window manager is already running on screen %d"),
93 xerror_set_ignore(TRUE
);
94 xerror_occured
= FALSE
;
96 /* We want to find out when the current selection owner dies */
97 XSelectInput(ob_display
, current_wm_sn_owner
, StructureNotifyMask
);
98 XSync(ob_display
, FALSE
);
100 xerror_set_ignore(FALSE
);
102 current_wm_sn_owner
= None
;
106 /* Generate a timestamp */
109 XSelectInput(ob_display
, screen_support_win
, PropertyChangeMask
);
111 XChangeProperty(ob_display
, screen_support_win
,
112 prop_atoms
.wm_class
, prop_atoms
.string
,
113 8, PropModeAppend
, NULL
, 0);
114 XWindowEvent(ob_display
, screen_support_win
,
115 PropertyChangeMask
, &event
);
117 XSelectInput(ob_display
, screen_support_win
, NoEventMask
);
119 timestamp
= event
.xproperty
.time
;
122 XSetSelectionOwner(ob_display
, wm_sn_atom
, screen_support_win
,
125 if (XGetSelectionOwner(ob_display
, wm_sn_atom
) != screen_support_win
) {
126 g_message(_("Could not acquire window manager selection on screen %d"),
131 /* Wait for old window manager to go away */
132 if (current_wm_sn_owner
) {
135 const gulong timeout
= G_USEC_PER_SEC
* 15; /* wait for 15s max */
137 while (wait
< timeout
) {
138 if (XCheckWindowEvent(ob_display
, current_wm_sn_owner
,
139 StructureNotifyMask
, &event
) &&
140 event
.type
== DestroyNotify
)
142 g_usleep(G_USEC_PER_SEC
/ 10);
143 wait
+= G_USEC_PER_SEC
/ 10;
146 if (wait
>= timeout
) {
147 g_message(_("The WM on screen %d is not exiting"), ob_screen
);
152 /* Send client message indicating that we are now the WM */
153 prop_message(RootWindow(ob_display
, ob_screen
), prop_atoms
.manager
,
154 timestamp
, wm_sn_atom
, screen_support_win
, 0,
155 SubstructureNotifyMask
);
160 gboolean
screen_annex()
162 XSetWindowAttributes attrib
;
165 Atom
*prop_atoms_start
, *wm_supported_pos
;
168 /* create the netwm support window */
169 attrib
.override_redirect
= TRUE
;
170 screen_support_win
= XCreateWindow(ob_display
,
171 RootWindow(ob_display
, ob_screen
),
173 CopyFromParent
, InputOutput
,
175 CWOverrideRedirect
, &attrib
);
176 XMapWindow(ob_display
, screen_support_win
);
177 XLowerWindow(ob_display
, screen_support_win
);
180 XDestroyWindow(ob_display
, screen_support_win
);
184 xerror_set_ignore(TRUE
);
185 xerror_occured
= FALSE
;
186 XSelectInput(ob_display
, RootWindow(ob_display
, ob_screen
),
188 xerror_set_ignore(FALSE
);
189 if (xerror_occured
) {
190 g_message(_("A window manager is already running on screen %d"),
193 XDestroyWindow(ob_display
, screen_support_win
);
197 screen_set_root_cursor();
199 /* set the OPENBOX_PID hint */
201 PROP_SET32(RootWindow(ob_display
, ob_screen
),
202 openbox_pid
, cardinal
, pid
);
204 /* set supporting window */
205 PROP_SET32(RootWindow(ob_display
, ob_screen
),
206 net_supporting_wm_check
, window
, screen_support_win
);
208 /* set properties on the supporting window */
209 PROP_SETS(screen_support_win
, net_wm_name
, "Openbox");
210 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 prop_atoms struct */
216 prop_atoms_start
= (Atom
*)&prop_atoms
;
217 wm_supported_pos
= (Atom
*)&(prop_atoms
.net_supported
);
218 num_support
= sizeof(prop_atoms
) / sizeof(Atom
) -
219 (wm_supported_pos
- prop_atoms_start
) - 1;
221 supported
= g_new(gulong
, num_support
);
222 supported
[i
++] = prop_atoms
.net_supporting_wm_check
;
223 supported
[i
++] = prop_atoms
.net_wm_full_placement
;
224 supported
[i
++] = prop_atoms
.net_current_desktop
;
225 supported
[i
++] = prop_atoms
.net_number_of_desktops
;
226 supported
[i
++] = prop_atoms
.net_desktop_geometry
;
227 supported
[i
++] = prop_atoms
.net_desktop_viewport
;
228 supported
[i
++] = prop_atoms
.net_active_window
;
229 supported
[i
++] = prop_atoms
.net_workarea
;
230 supported
[i
++] = prop_atoms
.net_client_list
;
231 supported
[i
++] = prop_atoms
.net_client_list_stacking
;
232 supported
[i
++] = prop_atoms
.net_desktop_names
;
233 supported
[i
++] = prop_atoms
.net_close_window
;
234 supported
[i
++] = prop_atoms
.net_desktop_layout
;
235 supported
[i
++] = prop_atoms
.net_showing_desktop
;
236 supported
[i
++] = prop_atoms
.net_wm_name
;
237 supported
[i
++] = prop_atoms
.net_wm_visible_name
;
238 supported
[i
++] = prop_atoms
.net_wm_icon_name
;
239 supported
[i
++] = prop_atoms
.net_wm_visible_icon_name
;
240 supported
[i
++] = prop_atoms
.net_wm_desktop
;
241 supported
[i
++] = prop_atoms
.net_wm_strut
;
242 supported
[i
++] = prop_atoms
.net_wm_strut_partial
;
243 supported
[i
++] = prop_atoms
.net_wm_icon
;
244 supported
[i
++] = prop_atoms
.net_wm_icon_geometry
;
245 supported
[i
++] = prop_atoms
.net_wm_window_type
;
246 supported
[i
++] = prop_atoms
.net_wm_window_type_desktop
;
247 supported
[i
++] = prop_atoms
.net_wm_window_type_dock
;
248 supported
[i
++] = prop_atoms
.net_wm_window_type_toolbar
;
249 supported
[i
++] = prop_atoms
.net_wm_window_type_menu
;
250 supported
[i
++] = prop_atoms
.net_wm_window_type_utility
;
251 supported
[i
++] = prop_atoms
.net_wm_window_type_splash
;
252 supported
[i
++] = prop_atoms
.net_wm_window_type_dialog
;
253 supported
[i
++] = prop_atoms
.net_wm_window_type_normal
;
254 supported
[i
++] = prop_atoms
.net_wm_allowed_actions
;
255 supported
[i
++] = prop_atoms
.net_wm_action_move
;
256 supported
[i
++] = prop_atoms
.net_wm_action_resize
;
257 supported
[i
++] = prop_atoms
.net_wm_action_minimize
;
258 supported
[i
++] = prop_atoms
.net_wm_action_shade
;
259 supported
[i
++] = prop_atoms
.net_wm_action_maximize_horz
;
260 supported
[i
++] = prop_atoms
.net_wm_action_maximize_vert
;
261 supported
[i
++] = prop_atoms
.net_wm_action_fullscreen
;
262 supported
[i
++] = prop_atoms
.net_wm_action_change_desktop
;
263 supported
[i
++] = prop_atoms
.net_wm_action_close
;
264 supported
[i
++] = prop_atoms
.net_wm_action_above
;
265 supported
[i
++] = prop_atoms
.net_wm_action_below
;
266 supported
[i
++] = prop_atoms
.net_wm_state
;
267 supported
[i
++] = prop_atoms
.net_wm_state_modal
;
268 supported
[i
++] = prop_atoms
.net_wm_state_maximized_vert
;
269 supported
[i
++] = prop_atoms
.net_wm_state_maximized_horz
;
270 supported
[i
++] = prop_atoms
.net_wm_state_shaded
;
271 supported
[i
++] = prop_atoms
.net_wm_state_skip_taskbar
;
272 supported
[i
++] = prop_atoms
.net_wm_state_skip_pager
;
273 supported
[i
++] = prop_atoms
.net_wm_state_hidden
;
274 supported
[i
++] = prop_atoms
.net_wm_state_fullscreen
;
275 supported
[i
++] = prop_atoms
.net_wm_state_above
;
276 supported
[i
++] = prop_atoms
.net_wm_state_below
;
277 supported
[i
++] = prop_atoms
.net_wm_state_demands_attention
;
278 supported
[i
++] = prop_atoms
.net_moveresize_window
;
279 supported
[i
++] = prop_atoms
.net_wm_moveresize
;
280 supported
[i
++] = prop_atoms
.net_wm_user_time
;
281 supported
[i
++] = prop_atoms
.net_wm_user_time_window
;
282 supported
[i
++] = prop_atoms
.net_frame_extents
;
283 supported
[i
++] = prop_atoms
.net_request_frame_extents
;
284 supported
[i
++] = prop_atoms
.net_restack_window
;
285 supported
[i
++] = prop_atoms
.net_startup_id
;
287 supported
[i
++] = prop_atoms
.net_wm_sync_request
;
288 supported
[i
++] = prop_atoms
.net_wm_sync_request_counter
;
291 supported
[i
++] = prop_atoms
.kde_wm_change_state
;
292 supported
[i
++] = prop_atoms
.kde_net_wm_frame_strut
;
293 supported
[i
++] = prop_atoms
.kde_net_wm_window_type_override
;
295 supported
[i
++] = prop_atoms
.ob_wm_action_undecorate
;
296 supported
[i
++] = prop_atoms
.ob_wm_state_undecorated
;
297 supported
[i
++] = prop_atoms
.openbox_pid
;
298 supported
[i
++] = prop_atoms
.ob_theme
;
299 supported
[i
++] = prop_atoms
.ob_control
;
300 g_assert(i
== num_support
);
302 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
303 net_supported
, atom
, supported
, num_support
);
306 screen_tell_ksplash();
311 static void screen_tell_ksplash()
316 argv
= g_new(gchar
*, 6);
317 argv
[0] = g_strdup("dcop");
318 argv
[1] = g_strdup("ksplash");
319 argv
[2] = g_strdup("ksplash");
320 argv
[3] = g_strdup("upAndRunning(QString)");
321 argv
[4] = g_strdup("wm started");
324 /* tell ksplash through the dcop server command line interface */
325 g_spawn_async(NULL
, argv
, NULL
,
326 G_SPAWN_SEARCH_PATH
| G_SPAWN_DO_NOT_REAP_CHILD
,
327 NULL
, NULL
, NULL
, NULL
);
330 /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
331 hear it anyways. perhaps it is for old ksplash. or new ksplash. or
332 something. oh well. */
333 e
.xclient
.type
= ClientMessage
;
334 e
.xclient
.display
= ob_display
;
335 e
.xclient
.window
= RootWindow(ob_display
, ob_screen
);
336 e
.xclient
.message_type
=
337 XInternAtom(ob_display
, "_KDE_SPLASH_PROGRESS", False
);
338 e
.xclient
.format
= 8;
339 strcpy(e
.xclient
.data
.b
, "wm started");
340 XSendEvent(ob_display
, RootWindow(ob_display
, ob_screen
),
341 False
, SubstructureNotifyMask
, &e
);
344 void screen_startup(gboolean reconfig
)
346 gchar
**names
= NULL
;
348 gboolean namesexist
= FALSE
;
350 desktop_cycle_popup
= pager_popup_new(FALSE
);
351 pager_popup_height(desktop_cycle_popup
, POPUP_HEIGHT
);
354 /* update the pager popup's width */
355 pager_popup_text_width_to_strings(desktop_cycle_popup
,
356 screen_desktop_names
,
357 screen_num_desktops
);
361 /* get the initial size */
364 /* have names already been set for the desktops? */
365 if (PROP_GETSS(RootWindow(ob_display
, ob_screen
),
366 net_desktop_names
, utf8
, &names
))
372 /* if names don't exist and we have session names, set those.
373 do this stuff BEFORE setting the number of desktops, because that
374 will create default names for them
376 if (!namesexist
&& session_desktop_names
!= NULL
) {
380 /* get the desktop names */
381 numnames
= g_slist_length(session_desktop_names
);
382 names
= g_new(gchar
*, numnames
+ 1);
383 names
[numnames
] = NULL
;
384 for (i
= 0, it
= session_desktop_names
; it
; ++i
, it
= g_slist_next(it
))
385 names
[i
] = g_strdup(it
->data
);
387 /* set the root window property */
388 PROP_SETSS(RootWindow(ob_display
, ob_screen
), net_desktop_names
,names
);
393 /* set the number of desktops, if it's not already set.
395 this will also set the default names from the config file up for
396 desktops that don't have names yet */
397 screen_num_desktops
= 0;
398 if (PROP_GET32(RootWindow(ob_display
, ob_screen
),
399 net_number_of_desktops
, cardinal
, &d
))
400 screen_set_num_desktops(d
);
401 /* restore from session if possible */
402 else if (session_num_desktops
)
403 screen_set_num_desktops(session_num_desktops
);
405 screen_set_num_desktops(config_desktops_num
);
407 screen_desktop
= screen_num_desktops
; /* something invalid */
408 /* start on the current desktop when a wm was already running */
409 if (PROP_GET32(RootWindow(ob_display
, ob_screen
),
410 net_current_desktop
, cardinal
, &d
) &&
411 d
< screen_num_desktops
)
413 screen_set_desktop(d
, FALSE
);
414 } else if (session_desktop
>= 0)
415 screen_set_desktop(MIN((guint
)session_desktop
,
416 screen_num_desktops
), FALSE
);
418 screen_set_desktop(MIN(config_screen_firstdesk
,
419 screen_num_desktops
) - 1, FALSE
);
420 screen_last_desktop
= screen_desktop
;
422 /* don't start in showing-desktop mode */
423 screen_showing_desktop
= FALSE
;
424 PROP_SET32(RootWindow(ob_display
, ob_screen
),
425 net_showing_desktop
, cardinal
, screen_showing_desktop
);
427 if (session_desktop_layout_present
&&
428 screen_validate_layout(&session_desktop_layout
))
430 screen_desktop_layout
= session_desktop_layout
;
433 screen_update_layout();
436 void screen_shutdown(gboolean reconfig
)
440 pager_popup_free(desktop_cycle_popup
);
445 XSelectInput(ob_display
, RootWindow(ob_display
, ob_screen
),
448 /* we're not running here no more! */
449 PROP_ERASE(RootWindow(ob_display
, ob_screen
), openbox_pid
);
451 PROP_ERASE(RootWindow(ob_display
, ob_screen
), net_supported
);
452 /* don't keep this mode */
453 PROP_ERASE(RootWindow(ob_display
, ob_screen
), net_showing_desktop
);
455 XDestroyWindow(ob_display
, screen_support_win
);
457 g_strfreev(screen_desktop_names
);
458 screen_desktop_names
= NULL
;
460 for (r
= area
; *r
; ++r
)
468 static gint oldw
= 0, oldh
= 0;
473 w
= WidthOfScreen(ScreenOfDisplay(ob_display
, ob_screen
));
474 h
= HeightOfScreen(ScreenOfDisplay(ob_display
, ob_screen
));
476 if (w
== oldw
&& h
== oldh
) return;
480 /* Set the _NET_DESKTOP_GEOMETRY hint */
481 screen_physical_size
.width
= geometry
[0] = w
;
482 screen_physical_size
.height
= geometry
[1] = h
;
483 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
484 net_desktop_geometry
, cardinal
, geometry
, 2);
486 if (ob_state() == OB_STATE_STARTING
)
489 screen_update_areas();
492 for (it
= client_list
; it
; it
= g_list_next(it
))
493 client_move_onscreen(it
->data
, FALSE
);
496 void screen_set_num_desktops(guint num
)
504 if (screen_num_desktops
== num
) return;
506 old
= screen_num_desktops
;
507 screen_num_desktops
= num
;
508 PROP_SET32(RootWindow(ob_display
, ob_screen
),
509 net_number_of_desktops
, cardinal
, num
);
511 /* set the viewport hint */
512 viewport
= g_new0(gulong
, num
* 2);
513 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
514 net_desktop_viewport
, cardinal
, viewport
, num
* 2);
517 /* the number of rows/columns will differ */
518 screen_update_layout();
520 /* move windows on desktops that will no longer exist! */
521 for (it
= client_list
; it
; it
= g_list_next(it
)) {
522 ObClient
*c
= it
->data
;
523 if (c
->desktop
>= num
&& c
->desktop
!= DESKTOP_ALL
)
524 client_set_desktop(c
, num
- 1, FALSE
);
527 /* change our struts/area to match (after moving windows) */
528 screen_update_areas();
530 /* may be some unnamed desktops that we need to fill in with names
531 (after updating the areas so the popup can resize) */
532 screen_update_desktop_names();
534 /* change our desktop if we're on one that no longer exists! */
535 if (screen_desktop
>= screen_num_desktops
)
536 screen_set_desktop(num
- 1, TRUE
);
539 void screen_set_desktop(guint num
, gboolean dofocus
)
545 g_assert(num
< screen_num_desktops
);
547 old
= screen_desktop
;
548 screen_desktop
= num
;
550 if (old
== num
) return;
552 PROP_SET32(RootWindow(ob_display
, ob_screen
),
553 net_current_desktop
, cardinal
, num
);
555 screen_last_desktop
= old
;
557 ob_debug("Moving to desktop %d\n", num
+1);
559 if (moveresize_client
)
560 client_set_desktop(moveresize_client
, num
, TRUE
);
562 /* show windows before hiding the rest to lessen the enter/leave events */
564 /* show windows from top to bottom */
565 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
566 if (WINDOW_IS_CLIENT(it
->data
)) {
567 ObClient
*c
= it
->data
;
572 if (focus_client
&& ((client_normal(focus_client
) &&
573 focus_client
->desktop
== DESKTOP_ALL
) ||
574 focus_client
->desktop
== screen_desktop
))
577 /* have to try focus here because when you leave an empty desktop
578 there is no focus out to watch for. also, we have different rules
579 here. we always allow it to look under the mouse pointer if
580 config_focus_last is FALSE
582 do this before hiding the windows so if helper windows are coming
583 with us, they don't get hidden
585 if (dofocus
&& (c
= focus_fallback(TRUE
, !config_focus_last
)))
587 /* only do the flicker reducing stuff ahead of time if we are going
588 to call xsetinputfocus on the window ourselves. otherwise there is
589 no guarantee the window will actually take focus.. */
591 /* reduce flicker by hiliting now rather than waiting for the
592 server FocusIn event */
593 frame_adjust_focus(c
->frame
, TRUE
);
594 /* do this here so that if you switch desktops to a window with
595 helper windows then the helper windows won't flash */
596 client_bring_helper_windows(c
);
600 /* hide windows from bottom to top */
601 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
)) {
602 if (WINDOW_IS_CLIENT(it
->data
)) {
603 ObClient
*c
= it
->data
;
608 event_ignore_all_queued_enters();
610 if (event_curtime
!= CurrentTime
)
611 screen_desktop_user_time
= event_curtime
;
614 static void get_row_col(guint d
, guint
*r
, guint
*c
)
616 switch (screen_desktop_layout
.orientation
) {
617 case OB_ORIENTATION_HORZ
:
618 switch (screen_desktop_layout
.start_corner
) {
619 case OB_CORNER_TOPLEFT
:
620 *r
= d
/ screen_desktop_layout
.columns
;
621 *c
= d
% screen_desktop_layout
.columns
;
623 case OB_CORNER_BOTTOMLEFT
:
624 *r
= screen_desktop_layout
.rows
- 1 -
625 d
/ screen_desktop_layout
.columns
;
626 *c
= d
% screen_desktop_layout
.columns
;
628 case OB_CORNER_TOPRIGHT
:
629 *r
= d
/ screen_desktop_layout
.columns
;
630 *c
= screen_desktop_layout
.columns
- 1 -
631 d
% screen_desktop_layout
.columns
;
633 case OB_CORNER_BOTTOMRIGHT
:
634 *r
= screen_desktop_layout
.rows
- 1 -
635 d
/ screen_desktop_layout
.columns
;
636 *c
= screen_desktop_layout
.columns
- 1 -
637 d
% screen_desktop_layout
.columns
;
641 case OB_ORIENTATION_VERT
:
642 switch (screen_desktop_layout
.start_corner
) {
643 case OB_CORNER_TOPLEFT
:
644 *r
= d
% screen_desktop_layout
.rows
;
645 *c
= d
/ screen_desktop_layout
.rows
;
647 case OB_CORNER_BOTTOMLEFT
:
648 *r
= screen_desktop_layout
.rows
- 1 -
649 d
% screen_desktop_layout
.rows
;
650 *c
= d
/ screen_desktop_layout
.rows
;
652 case OB_CORNER_TOPRIGHT
:
653 *r
= d
% screen_desktop_layout
.rows
;
654 *c
= screen_desktop_layout
.columns
- 1 -
655 d
/ screen_desktop_layout
.rows
;
657 case OB_CORNER_BOTTOMRIGHT
:
658 *r
= screen_desktop_layout
.rows
- 1 -
659 d
% screen_desktop_layout
.rows
;
660 *c
= screen_desktop_layout
.columns
- 1 -
661 d
/ screen_desktop_layout
.rows
;
668 static guint
translate_row_col(guint r
, guint c
)
670 switch (screen_desktop_layout
.orientation
) {
671 case OB_ORIENTATION_HORZ
:
672 switch (screen_desktop_layout
.start_corner
) {
673 case OB_CORNER_TOPLEFT
:
674 return r
% screen_desktop_layout
.rows
*
675 screen_desktop_layout
.columns
+
676 c
% screen_desktop_layout
.columns
;
677 case OB_CORNER_BOTTOMLEFT
:
678 return (screen_desktop_layout
.rows
- 1 -
679 r
% screen_desktop_layout
.rows
) *
680 screen_desktop_layout
.columns
+
681 c
% screen_desktop_layout
.columns
;
682 case OB_CORNER_TOPRIGHT
:
683 return r
% screen_desktop_layout
.rows
*
684 screen_desktop_layout
.columns
+
685 (screen_desktop_layout
.columns
- 1 -
686 c
% screen_desktop_layout
.columns
);
687 case OB_CORNER_BOTTOMRIGHT
:
688 return (screen_desktop_layout
.rows
- 1 -
689 r
% screen_desktop_layout
.rows
) *
690 screen_desktop_layout
.columns
+
691 (screen_desktop_layout
.columns
- 1 -
692 c
% screen_desktop_layout
.columns
);
694 case OB_ORIENTATION_VERT
:
695 switch (screen_desktop_layout
.start_corner
) {
696 case OB_CORNER_TOPLEFT
:
697 return c
% screen_desktop_layout
.columns
*
698 screen_desktop_layout
.rows
+
699 r
% screen_desktop_layout
.rows
;
700 case OB_CORNER_BOTTOMLEFT
:
701 return c
% screen_desktop_layout
.columns
*
702 screen_desktop_layout
.rows
+
703 (screen_desktop_layout
.rows
- 1 -
704 r
% screen_desktop_layout
.rows
);
705 case OB_CORNER_TOPRIGHT
:
706 return (screen_desktop_layout
.columns
- 1 -
707 c
% screen_desktop_layout
.columns
) *
708 screen_desktop_layout
.rows
+
709 r
% screen_desktop_layout
.rows
;
710 case OB_CORNER_BOTTOMRIGHT
:
711 return (screen_desktop_layout
.columns
- 1 -
712 c
% screen_desktop_layout
.columns
) *
713 screen_desktop_layout
.rows
+
714 (screen_desktop_layout
.rows
- 1 -
715 r
% screen_desktop_layout
.rows
);
718 g_assert_not_reached();
722 void screen_desktop_popup(guint d
, gboolean show
)
727 pager_popup_hide(desktop_cycle_popup
);
729 a
= screen_physical_area_monitor(0);
730 pager_popup_position(desktop_cycle_popup
, CenterGravity
,
731 a
->x
+ a
->width
/ 2, a
->y
+ a
->height
/ 2);
732 pager_popup_icon_size_multiplier(desktop_cycle_popup
,
733 (screen_desktop_layout
.columns
/
734 screen_desktop_layout
.rows
) / 2,
735 (screen_desktop_layout
.rows
/
736 screen_desktop_layout
.columns
) / 2);
737 pager_popup_max_width(desktop_cycle_popup
,
738 MAX(a
->width
/3, POPUP_WIDTH
));
739 pager_popup_show(desktop_cycle_popup
, screen_desktop_names
[d
], d
);
743 guint
screen_cycle_desktop(ObDirection dir
, gboolean wrap
, gboolean linear
,
744 gboolean dialog
, gboolean done
, gboolean cancel
)
747 static guint d
= (guint
)-1;
753 if ((cancel
|| done
) && dialog
)
754 goto show_cycle_dialog
;
757 get_row_col(d
, &r
, &c
);
761 case OB_DIRECTION_EAST
:
762 if (d
< screen_num_desktops
- 1)
767 case OB_DIRECTION_WEST
:
771 d
= screen_num_desktops
- 1;
775 return screen_desktop
;
779 case OB_DIRECTION_EAST
:
781 if (c
>= screen_desktop_layout
.columns
) {
785 goto show_cycle_dialog
;
787 d
= translate_row_col(r
, c
);
788 if (d
>= screen_num_desktops
) {
793 goto show_cycle_dialog
;
797 case OB_DIRECTION_WEST
:
799 if (c
>= screen_desktop_layout
.columns
) {
801 c
= screen_desktop_layout
.columns
- 1;
803 goto show_cycle_dialog
;
805 d
= translate_row_col(r
, c
);
806 if (d
>= screen_num_desktops
) {
811 goto show_cycle_dialog
;
815 case OB_DIRECTION_SOUTH
:
817 if (r
>= screen_desktop_layout
.rows
) {
821 goto show_cycle_dialog
;
823 d
= translate_row_col(r
, c
);
824 if (d
>= screen_num_desktops
) {
829 goto show_cycle_dialog
;
833 case OB_DIRECTION_NORTH
:
835 if (r
>= screen_desktop_layout
.rows
) {
837 r
= screen_desktop_layout
.rows
- 1;
839 goto show_cycle_dialog
;
841 d
= translate_row_col(r
, c
);
842 if (d
>= screen_num_desktops
) {
847 goto show_cycle_dialog
;
853 return d
= screen_desktop
;
856 d
= translate_row_col(r
, c
);
860 if (dialog
&& !cancel
&& !done
) {
861 screen_desktop_popup(d
, TRUE
);
863 screen_desktop_popup(0, FALSE
);
866 if (!dialog
|| cancel
|| done
)
872 static gboolean
screen_validate_layout(ObDesktopLayout
*l
)
874 if (l
->columns
== 0 && l
->rows
== 0) /* both 0's is bad data.. */
877 /* fill in a zero rows/columns */
878 if (l
->columns
== 0) {
879 l
->columns
= screen_num_desktops
/ l
->rows
;
880 if (l
->rows
* l
->columns
< screen_num_desktops
)
882 if (l
->rows
* l
->columns
>= screen_num_desktops
+ l
->columns
)
884 } else if (l
->rows
== 0) {
885 l
->rows
= screen_num_desktops
/ l
->columns
;
886 if (l
->columns
* l
->rows
< screen_num_desktops
)
888 if (l
->columns
* l
->rows
>= screen_num_desktops
+ l
->rows
)
892 /* bounds checking */
893 if (l
->orientation
== OB_ORIENTATION_HORZ
) {
894 l
->columns
= MIN(screen_num_desktops
, l
->columns
);
895 l
->rows
= MIN(l
->rows
,
896 (screen_num_desktops
+ l
->columns
- 1) / l
->columns
);
897 l
->columns
= screen_num_desktops
/ l
->rows
+
898 !!(screen_num_desktops
% l
->rows
);
900 l
->rows
= MIN(screen_num_desktops
, l
->rows
);
901 l
->columns
= MIN(l
->columns
,
902 (screen_num_desktops
+ l
->rows
- 1) / l
->rows
);
903 l
->rows
= screen_num_desktops
/ l
->columns
+
904 !!(screen_num_desktops
% l
->columns
);
909 void screen_update_layout()
916 screen_desktop_layout
.orientation
= OB_ORIENTATION_HORZ
;
917 screen_desktop_layout
.start_corner
= OB_CORNER_TOPLEFT
;
918 screen_desktop_layout
.rows
= 1;
919 screen_desktop_layout
.columns
= screen_num_desktops
;
921 if (PROP_GETA32(RootWindow(ob_display
, ob_screen
),
922 net_desktop_layout
, cardinal
, &data
, &num
)) {
923 if (num
== 3 || num
== 4) {
925 if (data
[0] == prop_atoms
.net_wm_orientation_vert
)
926 l
.orientation
= OB_ORIENTATION_VERT
;
927 else if (data
[0] == prop_atoms
.net_wm_orientation_horz
)
928 l
.orientation
= OB_ORIENTATION_HORZ
;
933 l
.start_corner
= OB_CORNER_TOPLEFT
;
935 if (data
[3] == prop_atoms
.net_wm_topleft
)
936 l
.start_corner
= OB_CORNER_TOPLEFT
;
937 else if (data
[3] == prop_atoms
.net_wm_topright
)
938 l
.start_corner
= OB_CORNER_TOPRIGHT
;
939 else if (data
[3] == prop_atoms
.net_wm_bottomright
)
940 l
.start_corner
= OB_CORNER_BOTTOMRIGHT
;
941 else if (data
[3] == prop_atoms
.net_wm_bottomleft
)
942 l
.start_corner
= OB_CORNER_BOTTOMLEFT
;
950 if (screen_validate_layout(&l
))
951 screen_desktop_layout
= l
;
958 void screen_update_desktop_names()
962 /* empty the array */
963 g_strfreev(screen_desktop_names
);
964 screen_desktop_names
= NULL
;
966 if (PROP_GETSS(RootWindow(ob_display
, ob_screen
),
967 net_desktop_names
, utf8
, &screen_desktop_names
))
968 for (i
= 0; screen_desktop_names
[i
] && i
< screen_num_desktops
; ++i
);
971 if (i
< screen_num_desktops
) {
974 screen_desktop_names
= g_renew(gchar
*, screen_desktop_names
,
975 screen_num_desktops
+ 1);
976 screen_desktop_names
[screen_num_desktops
] = NULL
;
978 it
= g_slist_nth(config_desktops_names
, i
);
980 for (; i
< screen_num_desktops
; ++i
) {
981 if (it
&& ((char*)it
->data
)[0]) /* not empty */
982 /* use the names from the config file when possible */
983 screen_desktop_names
[i
] = g_strdup(it
->data
);
985 /* make up a nice name if it's not though */
986 screen_desktop_names
[i
] = g_strdup_printf(_("desktop %i"),
988 if (it
) it
= g_slist_next(it
);
991 /* if we changed any names, then set the root property so we can
992 all agree on the names */
993 PROP_SETSS(RootWindow(ob_display
, ob_screen
), net_desktop_names
,
994 screen_desktop_names
);
997 /* resize the pager for these names */
998 pager_popup_text_width_to_strings(desktop_cycle_popup
,
999 screen_desktop_names
,
1000 screen_num_desktops
);
1003 void screen_show_desktop(gboolean show
, ObClient
*show_only
)
1007 if (show
== screen_showing_desktop
) return; /* no change */
1009 screen_showing_desktop
= show
;
1012 /* hide windows bottom to top */
1013 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
)) {
1014 if (WINDOW_IS_CLIENT(it
->data
)) {
1015 ObClient
*client
= it
->data
;
1016 client_showhide(client
);
1021 /* restore windows top to bottom */
1022 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
1023 if (WINDOW_IS_CLIENT(it
->data
)) {
1024 ObClient
*client
= it
->data
;
1025 if (client_should_show(client
)) {
1026 if (!show_only
|| client
== show_only
)
1027 client_show(client
);
1029 client_iconify(client
, TRUE
, FALSE
, TRUE
);
1036 /* focus the desktop */
1037 for (it
= focus_order
; it
; it
= g_list_next(it
)) {
1038 ObClient
*c
= it
->data
;
1039 if (c
->type
== OB_CLIENT_TYPE_DESKTOP
&&
1040 (c
->desktop
== screen_desktop
|| c
->desktop
== DESKTOP_ALL
) &&
1041 client_focus(it
->data
))
1045 else if (!show_only
) {
1048 if ((c
= focus_fallback(TRUE
, FALSE
))) {
1049 /* only do the flicker reducing stuff ahead of time if we are going
1050 to call xsetinputfocus on the window ourselves. otherwise there
1051 is no guarantee the window will actually take focus.. */
1053 /* reduce flicker by hiliting now rather than waiting for the
1054 server FocusIn event */
1055 frame_adjust_focus(c
->frame
, TRUE
);
1060 show
= !!show
; /* make it boolean */
1061 PROP_SET32(RootWindow(ob_display
, ob_screen
),
1062 net_showing_desktop
, cardinal
, show
);
1065 void screen_install_colormap(ObClient
*client
, gboolean install
)
1067 if (client
== NULL
|| client
->colormap
== None
) {
1069 XInstallColormap(RrDisplay(ob_rr_inst
), RrColormap(ob_rr_inst
));
1071 XUninstallColormap(RrDisplay(ob_rr_inst
), RrColormap(ob_rr_inst
));
1073 xerror_set_ignore(TRUE
);
1075 XInstallColormap(RrDisplay(ob_rr_inst
), client
->colormap
);
1077 XUninstallColormap(RrDisplay(ob_rr_inst
), client
->colormap
);
1078 xerror_set_ignore(FALSE
);
1083 screen_area_add_strut_left(const StrutPartial
*s
, const Rect
*monitor_area
,
1084 gint edge
, Strut
*ret
)
1087 ((s
->left_end
<= s
->left_start
) ||
1088 (RECT_TOP(*monitor_area
) < s
->left_end
&&
1089 RECT_BOTTOM(*monitor_area
) > s
->left_start
)))
1090 ret
->left
= MAX(ret
->left
, edge
);
1094 screen_area_add_strut_top(const StrutPartial
*s
, const Rect
*monitor_area
,
1095 gint edge
, Strut
*ret
)
1098 ((s
->top_end
<= s
->top_start
) ||
1099 (RECT_LEFT(*monitor_area
) < s
->top_end
&&
1100 RECT_RIGHT(*monitor_area
) > s
->top_start
)))
1101 ret
->top
= MAX(ret
->top
, edge
);
1105 screen_area_add_strut_right(const StrutPartial
*s
, const Rect
*monitor_area
,
1106 gint edge
, Strut
*ret
)
1109 ((s
->right_end
<= s
->right_start
) ||
1110 (RECT_TOP(*monitor_area
) < s
->right_end
&&
1111 RECT_BOTTOM(*monitor_area
) > s
->right_start
)))
1112 ret
->right
= MAX(ret
->right
, edge
);
1116 screen_area_add_strut_bottom(const StrutPartial
*s
, const Rect
*monitor_area
,
1117 gint edge
, Strut
*ret
)
1120 ((s
->bottom_end
<= s
->bottom_start
) ||
1121 (RECT_LEFT(*monitor_area
) < s
->bottom_end
&&
1122 RECT_RIGHT(*monitor_area
) > s
->bottom_start
)))
1123 ret
->bottom
= MAX(ret
->bottom
, edge
);
1126 void screen_update_areas()
1133 g_free(monitor_area
);
1134 extensions_xinerama_screens(&monitor_area
, &screen_num_monitors
);
1137 for (i
= 0; area
[i
]; ++i
)
1142 area
= g_new(Rect
*, screen_num_desktops
+ 2);
1143 for (i
= 0; i
< screen_num_desktops
+ 1; ++i
)
1144 area
[i
] = g_new0(Rect
, screen_num_monitors
+ 1);
1147 dims
= g_new(gulong
, 4 * screen_num_desktops
);
1149 for (i
= 0; i
< screen_num_desktops
+ 1; ++i
) {
1153 struts
= g_new0(Strut
, screen_num_monitors
);
1155 /* calc the xinerama areas */
1156 for (x
= 0; x
< screen_num_monitors
; ++x
) {
1157 area
[i
][x
] = monitor_area
[x
];
1159 l
= monitor_area
[x
].x
;
1160 t
= monitor_area
[x
].y
;
1161 r
= monitor_area
[x
].x
+ monitor_area
[x
].width
- 1;
1162 b
= monitor_area
[x
].y
+ monitor_area
[x
].height
- 1;
1164 l
= MIN(l
, monitor_area
[x
].x
);
1165 t
= MIN(t
, monitor_area
[x
].y
);
1166 r
= MAX(r
, monitor_area
[x
].x
+ monitor_area
[x
].width
- 1);
1167 b
= MAX(b
, monitor_area
[x
].y
+ monitor_area
[x
].height
- 1);
1170 RECT_SET(area
[i
][x
], l
, t
, r
- l
+ 1, b
- t
+ 1);
1172 /* apply the struts */
1174 /* find the left-most xin heads, i do this in 2 loops :| */
1176 for (x
= 1; x
< screen_num_monitors
; ++x
)
1177 o
= MIN(o
, area
[i
][x
].x
);
1179 for (x
= 0; x
< screen_num_monitors
; ++x
) {
1180 for (it
= client_list
; it
; it
= g_list_next(it
)) {
1181 ObClient
*c
= it
->data
;
1182 screen_area_add_strut_left(&c
->strut
,
1184 o
+ c
->strut
.left
- area
[i
][x
].x
,
1187 screen_area_add_strut_left(&dock_strut
,
1189 o
+ dock_strut
.left
- area
[i
][x
].x
,
1192 area
[i
][x
].x
+= struts
[x
].left
;
1193 area
[i
][x
].width
-= struts
[x
].left
;
1196 /* find the top-most xin heads, i do this in 2 loops :| */
1198 for (x
= 1; x
< screen_num_monitors
; ++x
)
1199 o
= MIN(o
, area
[i
][x
].y
);
1201 for (x
= 0; x
< screen_num_monitors
; ++x
) {
1202 for (it
= client_list
; it
; it
= g_list_next(it
)) {
1203 ObClient
*c
= it
->data
;
1204 screen_area_add_strut_top(&c
->strut
,
1206 o
+ c
->strut
.top
- area
[i
][x
].y
,
1209 screen_area_add_strut_top(&dock_strut
,
1211 o
+ dock_strut
.top
- area
[i
][x
].y
,
1214 area
[i
][x
].y
+= struts
[x
].top
;
1215 area
[i
][x
].height
-= struts
[x
].top
;
1218 /* find the right-most xin heads, i do this in 2 loops :| */
1219 o
= area
[i
][0].x
+ area
[i
][0].width
- 1;
1220 for (x
= 1; x
< screen_num_monitors
; ++x
)
1221 o
= MAX(o
, area
[i
][x
].x
+ area
[i
][x
].width
- 1);
1223 for (x
= 0; x
< screen_num_monitors
; ++x
) {
1224 for (it
= client_list
; it
; it
= g_list_next(it
)) {
1225 ObClient
*c
= it
->data
;
1226 screen_area_add_strut_right(&c
->strut
,
1229 area
[i
][x
].width
- 1) -
1230 (o
- c
->strut
.right
),
1233 screen_area_add_strut_right(&dock_strut
,
1236 area
[i
][x
].width
- 1) -
1237 (o
- dock_strut
.right
),
1240 area
[i
][x
].width
-= struts
[x
].right
;
1243 /* find the bottom-most xin heads, i do this in 2 loops :| */
1244 o
= area
[i
][0].y
+ area
[i
][0].height
- 1;
1245 for (x
= 1; x
< screen_num_monitors
; ++x
)
1246 o
= MAX(o
, area
[i
][x
].y
+ area
[i
][x
].height
- 1);
1248 for (x
= 0; x
< screen_num_monitors
; ++x
) {
1249 for (it
= client_list
; it
; it
= g_list_next(it
)) {
1250 ObClient
*c
= it
->data
;
1251 screen_area_add_strut_bottom(&c
->strut
,
1254 area
[i
][x
].height
- 1) - \
1255 (o
- c
->strut
.bottom
),
1258 screen_area_add_strut_bottom(&dock_strut
,
1261 area
[i
][x
].height
- 1) - \
1262 (o
- dock_strut
.bottom
),
1265 area
[i
][x
].height
-= struts
[x
].bottom
;
1268 l
= RECT_LEFT(area
[i
][0]);
1269 t
= RECT_TOP(area
[i
][0]);
1270 r
= RECT_RIGHT(area
[i
][0]);
1271 b
= RECT_BOTTOM(area
[i
][0]);
1272 for (x
= 1; x
< screen_num_monitors
; ++x
) {
1273 l
= MIN(l
, RECT_LEFT(area
[i
][x
]));
1274 t
= MIN(l
, RECT_TOP(area
[i
][x
]));
1275 r
= MAX(r
, RECT_RIGHT(area
[i
][x
]));
1276 b
= MAX(b
, RECT_BOTTOM(area
[i
][x
]));
1278 RECT_SET(area
[i
][screen_num_monitors
], l
, t
,
1279 r
- l
+ 1, b
- t
+ 1);
1281 /* XXX optimize when this is run? */
1283 /* the area has changed, adjust all the maximized
1285 for (it
= client_list
; it
; it
= g_list_next(it
)) {
1286 ObClient
*c
= it
->data
;
1287 if (i
< screen_num_desktops
) {
1288 if (c
->desktop
== i
)
1289 client_reconfigure(c
);
1290 } else if (c
->desktop
== DESKTOP_ALL
)
1291 client_reconfigure(c
);
1293 if (i
< screen_num_desktops
) {
1294 /* don't set these for the 'all desktops' area */
1295 dims
[(i
* 4) + 0] = area
[i
][screen_num_monitors
].x
;
1296 dims
[(i
* 4) + 1] = area
[i
][screen_num_monitors
].y
;
1297 dims
[(i
* 4) + 2] = area
[i
][screen_num_monitors
].width
;
1298 dims
[(i
* 4) + 3] = area
[i
][screen_num_monitors
].height
;
1304 PROP_SETA32(RootWindow(ob_display
, ob_screen
), net_workarea
, cardinal
,
1305 dims
, 4 * screen_num_desktops
);
1310 Rect
*screen_area(guint desktop
)
1312 return screen_area_monitor(desktop
, screen_num_monitors
);
1315 Rect
*screen_area_monitor(guint desktop
, guint head
)
1317 if (head
> screen_num_monitors
)
1319 if (desktop
>= screen_num_desktops
) {
1320 if (desktop
== DESKTOP_ALL
)
1321 return &area
[screen_num_desktops
][head
];
1324 return &area
[desktop
][head
];
1327 guint
screen_find_monitor(Rect
*search
)
1333 for (i
= 0; i
< screen_num_monitors
; ++i
) {
1334 Rect
*area
= screen_physical_area_monitor(i
);
1335 if (RECT_INTERSECTS_RECT(*area
, *search
)) {
1339 RECT_SET_INTERSECTION(r
, *area
, *search
);
1340 v
= r
.width
* r
.height
;
1351 Rect
*screen_physical_area()
1353 return screen_physical_area_monitor(screen_num_monitors
);
1356 Rect
*screen_physical_area_monitor(guint head
)
1358 if (head
> screen_num_monitors
)
1360 return &monitor_area
[head
];
1363 void screen_set_root_cursor()
1365 if (sn_app_starting())
1366 XDefineCursor(ob_display
, RootWindow(ob_display
, ob_screen
),
1367 ob_cursor(OB_CURSOR_BUSYPOINTER
));
1369 XDefineCursor(ob_display
, RootWindow(ob_display
, ob_screen
),
1370 ob_cursor(OB_CURSOR_POINTER
));
1373 gboolean
screen_pointer_pos(gint
*x
, gint
*y
)
1380 ret
= !!XQueryPointer(ob_display
, RootWindow(ob_display
, ob_screen
),
1381 &w
, &w
, x
, y
, &i
, &i
, &u
);
1383 for (i
= 0; i
< ScreenCount(ob_display
); ++i
)
1385 if (XQueryPointer(ob_display
, RootWindow(ob_display
, i
),
1386 &w
, &w
, x
, y
, &i
, &i
, &u
))