7 #include "moveresize.h"
9 #include "extensions.h"
21 #include "render/render.h"
24 #include <X11/Xutil.h>
26 /*! The event mask to grab on client windows */
27 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
30 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
33 GList
*client_list
= NULL
;
35 static void client_get_all(ObClient
*self
);
36 static void client_toggle_border(ObClient
*self
, gboolean show
);
37 static void client_get_area(ObClient
*self
);
38 static void client_get_desktop(ObClient
*self
);
39 static void client_get_state(ObClient
*self
);
40 static void client_get_shaped(ObClient
*self
);
41 static void client_get_mwm_hints(ObClient
*self
);
42 static void client_get_gravity(ObClient
*self
);
43 static void client_showhide(ObClient
*self
);
44 static void client_change_allowed_actions(ObClient
*self
);
45 static void client_change_state(ObClient
*self
);
46 static void client_apply_startup_state(ObClient
*self
);
47 static void client_restore_session_state(ObClient
*self
);
54 void client_shutdown()
58 void client_set_list()
60 Window
*windows
, *win_it
;
62 guint size
= g_list_length(client_list
);
64 /* create an array of the window ids */
66 windows
= g_new(Window
, size
);
68 for (it
= client_list
; it
!= NULL
; it
= it
->next
, ++win_it
)
69 *win_it
= ((ObClient
*)it
->data
)->window
;
73 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
74 net_client_list
, window
, (guint32
*)windows
, size
);
83 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, void *data)
87 for (it = self->transients; it; it = it->next) {
88 if (!func(it->data, data)) return;
89 client_foreach_transient(it->data, func, data);
93 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, void *data)
95 if (self->transient_for) {
96 if (self->transient_for != OB_TRAN_GROUP) {
97 if (!func(self->transient_for, data)) return;
98 client_foreach_ancestor(self->transient_for, func, data);
102 for (it = self->group->members; it; it = it->next)
103 if (it->data != self &&
104 !((ObClient*)it->data)->transient_for) {
105 if (!func(it->data, data)) return;
106 client_foreach_ancestor(it->data, func, data);
113 void client_manage_all()
115 unsigned int i
, j
, nchild
;
118 XWindowAttributes attrib
;
120 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
121 &w
, &w
, &children
, &nchild
);
123 /* remove all icon windows from the list */
124 for (i
= 0; i
< nchild
; i
++) {
125 if (children
[i
] == None
) continue;
126 wmhints
= XGetWMHints(ob_display
, children
[i
]);
128 if ((wmhints
->flags
& IconWindowHint
) &&
129 (wmhints
->icon_window
!= children
[i
]))
130 for (j
= 0; j
< nchild
; j
++)
131 if (children
[j
] == wmhints
->icon_window
) {
139 for (i
= 0; i
< nchild
; ++i
) {
140 if (children
[i
] == None
)
142 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
143 if (attrib
.override_redirect
) continue;
145 if (attrib
.map_state
!= IsUnmapped
)
146 client_manage(children
[i
]);
151 /* stack them as they were on startup!
152 why with stacking_lower? Why, because then windows who aren't in the
153 stacking list are on the top where you can see them instead of buried
155 for (i
= startup_stack_size
; i
> 0; --i
) {
158 w
= startup_stack_order
[i
-1];
159 obw
= g_hash_table_lookup(window_map
, &w
);
161 g_assert(WINDOW_IS_CLIENT(obw
));
162 stacking_lower(CLIENT_AS_WINDOW(obw
));
165 g_free(startup_stack_order
);
166 startup_stack_order
= NULL
;
167 startup_stack_size
= 0;
169 if (config_focus_new
) {
172 active
= g_hash_table_lookup(window_map
, &startup_active
);
174 g_assert(WINDOW_IS_CLIENT(active
));
175 if (!client_focus(WINDOW_AS_CLIENT(active
)))
176 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
178 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
182 void client_manage(Window window
)
186 XWindowAttributes attrib
;
187 XSetWindowAttributes attrib_set
;
189 gboolean activate
= FALSE
;
193 /* check if it has already been unmapped by the time we started mapping
194 the grab does a sync so we don't have to here */
195 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
196 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
)) {
197 XPutBackEvent(ob_display
, &e
);
200 return; /* don't manage it */
203 /* make sure it isn't an override-redirect window */
204 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
205 attrib
.override_redirect
) {
207 return; /* don't manage it */
210 /* is the window a docking app */
211 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
212 if ((wmhint
->flags
& StateHint
) &&
213 wmhint
->initial_state
== WithdrawnState
) {
214 dock_add(window
, wmhint
);
222 ob_debug("Managing window: %lx\n", window
);
224 /* choose the events we want to receive on the CLIENT window */
225 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
226 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
227 XChangeWindowAttributes(ob_display
, window
,
228 CWEventMask
|CWDontPropagate
, &attrib_set
);
231 /* create the ObClient struct, and populate it from the hints on the
233 self
= g_new(ObClient
, 1);
234 self
->obwin
.type
= Window_Client
;
235 self
->window
= window
;
237 client_get_all(self
);
238 client_restore_session_state(self
);
240 client_change_state(self
);
242 /* remove the client's border (and adjust re gravity) */
243 client_toggle_border(self
, FALSE
);
245 /* specify that if we exit, the window should not be destroyed and should
246 be reparented back to root automatically */
247 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
249 /* create the decoration frame for the client window */
250 self
->frame
= frame_new();
252 frame_grab_client(self
->frame
, self
);
254 client_apply_startup_state(self
);
258 /* update the focus lists */
259 focus_order_add_new(self
);
261 stacking_add(CLIENT_AS_WINDOW(self
));
263 /* focus the new window? */
264 if (ob_state() != OB_STATE_STARTING
&& config_focus_new
&&
265 /* note the check against Type_Normal/Dialog, not client_normal(self),
266 which would also include other types. in this case we want more
267 strict rules for focus */
268 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
269 self
->type
== OB_CLIENT_TYPE_DIALOG
))
271 if (self
->desktop
!= screen_desktop
) {
272 /* activate the window */
275 gboolean group_foc
= FALSE
;
280 for (it
= self
->group
->members
; it
; it
= it
->next
)
282 if (client_focused(it
->data
))
290 (!self
->transient_for
&& (!self
->group
||
291 !self
->group
->members
->next
))) ||
292 client_search_focus_tree_full(self
) ||
294 !client_normal(focus_client
))
296 /* activate the window */
302 dispatch_client(Event_Client_New
, self
, 0, 0);
304 /* make sure the window is visible */
305 client_move_onscreen(self
, TRUE
);
307 screen_update_areas();
309 client_showhide(self
);
311 /* use client_focus instead of client_activate cuz client_activate does
312 stuff like switch desktops etc and I'm not interested in all that when
313 a window maps since its not based on an action from the user like
314 clicking a window to activate is. so keep the new window out of the way
316 if (activate
) client_focus(self
);
318 /* client_activate does this but we aret using it so we have to do it
320 if (screen_showing_desktop
)
321 screen_show_desktop(FALSE
);
323 /* add to client list/map */
324 client_list
= g_list_append(client_list
, self
);
325 g_hash_table_insert(window_map
, &self
->window
, self
);
327 /* update the list hints */
330 dispatch_client(Event_Client_Mapped
, self
, 0, 0);
332 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
335 void client_unmanage_all()
337 while (client_list
!= NULL
)
338 client_unmanage(client_list
->data
);
341 /* called by client_unmanage() to close any menus referencing this client */
342 void client_close_menus(gpointer key
, gpointer value
, gpointer self
)
344 if (((ObMenu
*)value
)->client
== (ObClient
*)self
)
345 menu_hide((ObMenu
*)value
);
348 void client_unmanage(ObClient
*self
)
353 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
355 dispatch_client(Event_Client_Destroy
, self
, 0, 0);
356 g_assert(self
!= NULL
);
358 /* remove the window from our save set */
359 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
361 /* we dont want events no more */
362 XSelectInput(ob_display
, self
->window
, NoEventMask
);
364 frame_hide(self
->frame
);
366 client_list
= g_list_remove(client_list
, self
);
367 stacking_remove(self
);
368 g_hash_table_remove(window_map
, &self
->window
);
370 /* update the focus lists */
371 focus_order_remove(self
);
373 /* once the client is out of the list, update the struts to remove it's
375 screen_update_areas();
377 /* tell our parent(s) that we're gone */
378 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
381 for (it
= self
->group
->members
; it
; it
= it
->next
)
382 if (it
->data
!= self
)
383 ((ObClient
*)it
->data
)->transients
=
384 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
385 } else if (self
->transient_for
) { /* transient of window */
386 self
->transient_for
->transients
=
387 g_slist_remove(self
->transient_for
->transients
, self
);
390 /* tell our transients that we're gone */
391 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
392 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
393 ((ObClient
*)it
->data
)->transient_for
= NULL
;
394 client_calc_layer(it
->data
);
398 if (moveresize_client
== self
)
399 moveresize_end(TRUE
);
401 /* close any windows that are attached to this window */
402 g_hash_table_foreach(menu_hash
, client_close_menus
, self
);
405 if (focus_client
== self
) {
408 /* focus the last focused window on the desktop, and ignore enter
409 events from the unmap so it doesnt mess with the focus */
410 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
411 client_unfocus(self
);
414 /* remove from its group */
416 group_remove(self
->group
, self
);
420 /* dispatch the unmapped event */
421 dispatch_client(Event_Client_Unmapped
, self
, 0, 0);
422 g_assert(self
!= NULL
);
424 /* give the client its border back */
425 client_toggle_border(self
, TRUE
);
427 /* reparent the window out of the frame, and free the frame */
428 frame_release_client(self
->frame
, self
);
431 if (ob_state() != OB_STATE_EXITING
) {
432 /* these values should not be persisted across a window
434 PROP_ERASE(self
->window
, net_wm_desktop
);
435 PROP_ERASE(self
->window
, net_wm_state
);
436 PROP_ERASE(self
->window
, wm_state
);
438 /* if we're left in an iconic state, the client wont be mapped. this is
439 bad, since we will no longer be managing the window on restart */
441 XMapWindow(ob_display
, self
->window
);
445 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
447 /* free all data allocated in the client struct */
448 g_slist_free(self
->transients
);
449 for (j
= 0; j
< self
->nicons
; ++j
)
450 g_free(self
->icons
[j
].data
);
451 if (self
->nicons
> 0)
454 g_free(self
->icon_title
);
460 /* update the list hints */
464 static void client_restore_session_state(ObClient
*self
)
468 s
= session_state_find(self
);
471 RECT_SET(self
->area
, s
->x
, s
->y
, s
->w
, s
->h
);
472 self
->positioned
= TRUE
;
473 XResizeWindow(ob_display
, self
->window
, s
->w
, s
->h
);
475 self
->desktop
= s
->desktop
== DESKTOP_ALL
? s
->desktop
:
476 MIN(screen_num_desktops
- 1, s
->desktop
);
477 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
479 self
->shaded
= s
->shaded
;
480 self
->iconic
= s
->iconic
;
481 self
->skip_pager
= s
->skip_pager
;
482 self
->skip_taskbar
= s
->skip_taskbar
;
483 self
->fullscreen
= s
->fullscreen
;
484 self
->above
= s
->above
;
485 self
->below
= s
->below
;
486 self
->max_horz
= s
->max_horz
;
487 self
->max_vert
= s
->max_vert
;
489 session_state_free(s
);
492 void client_move_onscreen(ObClient
*self
, gboolean rude
)
494 int x
= self
->area
.x
;
495 int y
= self
->area
.y
;
496 if (client_find_onscreen(self
, &x
, &y
,
497 self
->frame
->area
.width
,
498 self
->frame
->area
.height
, rude
)) {
499 client_configure(self
, OB_CORNER_TOPLEFT
, x
, y
,
500 self
->area
.width
, self
->area
.height
,
505 gboolean
client_find_onscreen(ObClient
*self
, int *x
, int *y
, int w
, int h
,
509 int ox
= *x
, oy
= *y
;
511 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
514 /* XXX watch for xinerama dead areas */
516 a
= screen_area(self
->desktop
);
517 if (!self
->strut
.right
&& *x
>= a
->x
+ a
->width
- 1)
518 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
;
519 if (!self
->strut
.bottom
&& *y
>= a
->y
+ a
->height
- 1)
520 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
;
521 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
- 1 < a
->x
)
523 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
- 1 < a
->y
)
527 /* this is my MOZILLA BITCHSLAP. oh ya it fucking feels good.
528 Java can suck it too. */
530 /* dont let windows map/move into the strut unless they
531 are bigger than the available area */
533 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
534 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
535 *x
= a
->x
+ a
->width
- w
;
537 if (h
<= a
->height
) {
538 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
539 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
540 *y
= a
->y
+ a
->height
- h
;
544 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
547 return ox
!= *x
|| oy
!= *y
;
550 static void client_toggle_border(ObClient
*self
, gboolean show
)
552 /* adjust our idea of where the client is, based on its border. When the
553 border is removed, the client should now be considered to be in a
555 when re-adding the border to the client, the same operation needs to be
557 int oldx
= self
->area
.x
, oldy
= self
->area
.y
;
558 int x
= oldx
, y
= oldy
;
559 switch(self
->gravity
) {
561 case NorthWestGravity
:
563 case SouthWestGravity
:
565 case NorthEastGravity
:
567 case SouthEastGravity
:
568 if (show
) x
-= self
->border_width
* 2;
569 else x
+= self
->border_width
* 2;
576 if (show
) x
-= self
->border_width
;
577 else x
+= self
->border_width
;
580 switch(self
->gravity
) {
582 case NorthWestGravity
:
584 case NorthEastGravity
:
586 case SouthWestGravity
:
588 case SouthEastGravity
:
589 if (show
) y
-= self
->border_width
* 2;
590 else y
+= self
->border_width
* 2;
597 if (show
) y
-= self
->border_width
;
598 else y
+= self
->border_width
;
605 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
607 /* move the client so it is back it the right spot _with_ its
609 if (x
!= oldx
|| y
!= oldy
)
610 XMoveWindow(ob_display
, self
->window
, x
, y
);
612 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
616 static void client_get_all(ObClient
*self
)
618 /* update EVERYTHING!! */
620 self
->ignore_unmaps
= 0;
624 self
->title
= self
->icon_title
= NULL
;
625 self
->title_count
= 1;
626 self
->name
= self
->class = self
->role
= NULL
;
627 self
->wmstate
= NormalState
;
628 self
->transient
= FALSE
;
629 self
->transients
= NULL
;
630 self
->transient_for
= NULL
;
632 self
->urgent
= FALSE
;
633 self
->positioned
= FALSE
;
634 self
->decorate
= TRUE
;
638 client_get_area(self
);
639 client_update_transient_for(self
);
640 client_update_wmhints(self
);
641 client_get_desktop(self
);
642 client_get_state(self
);
643 client_get_shaped(self
);
645 client_get_mwm_hints(self
);
646 client_get_type(self
);/* this can change the mwmhints for special cases */
648 client_update_protocols(self
);
650 client_get_gravity(self
); /* get the attribute gravity */
651 client_update_normal_hints(self
); /* this may override the attribute
654 /* got the type, the mwmhints, the protocols, and the normal hints
655 (min/max sizes), so we're ready to set up the decorations/functions */
656 client_setup_decor_and_functions(self
);
658 client_update_title(self
);
659 client_update_class(self
);
660 client_update_strut(self
);
661 client_update_icons(self
);
664 static void client_get_area(ObClient
*self
)
666 XWindowAttributes wattrib
;
669 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
670 g_assert(ret
!= BadWindow
);
672 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
673 self
->border_width
= wattrib
.border_width
;
676 static void client_get_desktop(ObClient
*self
)
678 guint32 d
= screen_num_desktops
; /* an always-invalid value */
680 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
681 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
682 self
->desktop
= screen_num_desktops
- 1;
686 gboolean trdesk
= FALSE
;
688 if (self
->transient_for
) {
689 if (self
->transient_for
!= OB_TRAN_GROUP
) {
690 self
->desktop
= self
->transient_for
->desktop
;
695 for (it
= self
->group
->members
; it
; it
= it
->next
)
696 if (it
->data
!= self
&&
697 !((ObClient
*)it
->data
)->transient_for
) {
698 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
705 /* defaults to the current desktop */
706 self
->desktop
= screen_desktop
;
708 if (self
->desktop
!= d
) {
709 /* set the desktop hint, to make sure that it always exists */
710 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
714 static void client_get_state(ObClient
*self
)
719 self
->modal
= self
->shaded
= self
->max_horz
= self
->max_vert
=
720 self
->fullscreen
= self
->above
= self
->below
= self
->iconic
=
721 self
->skip_taskbar
= self
->skip_pager
= FALSE
;
723 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
725 for (i
= 0; i
< num
; ++i
) {
726 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
728 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
730 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
732 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
733 self
->skip_taskbar
= TRUE
;
734 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
735 self
->skip_pager
= TRUE
;
736 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
737 self
->fullscreen
= TRUE
;
738 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
739 self
->max_vert
= TRUE
;
740 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
741 self
->max_horz
= TRUE
;
742 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
744 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
752 static void client_get_shaped(ObClient
*self
)
754 self
->shaped
= FALSE
;
756 if (extensions_shape
) {
761 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
763 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
764 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
766 self
->shaped
= (s
!= 0);
771 void client_update_transient_for(ObClient
*self
)
774 ObClient
*target
= NULL
;
776 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
777 self
->transient
= TRUE
;
778 if (t
!= self
->window
) { /* cant be transient to itself! */
779 target
= g_hash_table_lookup(window_map
, &t
);
780 /* if this happens then we need to check for it*/
781 g_assert(target
!= self
);
782 g_assert(!target
|| WINDOW_IS_CLIENT(target
));
784 if (!target
&& self
->group
) {
785 /* not transient to a client, see if it is transient for a
787 if (t
== self
->group
->leader
||
789 t
== RootWindow(ob_display
, ob_screen
)) {
790 /* window is a transient for its group! */
791 target
= OB_TRAN_GROUP
;
796 self
->transient
= FALSE
;
798 /* if anything has changed... */
799 if (target
!= self
->transient_for
) {
800 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
803 /* remove from old parents */
804 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
805 ObClient
*c
= it
->data
;
806 if (c
!= self
&& !c
->transient_for
)
807 c
->transients
= g_slist_remove(c
->transients
, self
);
809 } else if (self
->transient_for
!= NULL
) { /* transient of window */
810 /* remove from old parent */
811 self
->transient_for
->transients
=
812 g_slist_remove(self
->transient_for
->transients
, self
);
814 self
->transient_for
= target
;
815 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
818 /* add to new parents */
819 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
820 ObClient
*c
= it
->data
;
821 if (c
!= self
&& !c
->transient_for
)
822 c
->transients
= g_slist_append(c
->transients
, self
);
825 /* remove all transients which are in the group, that causes
826 circlular pointer hell of doom */
827 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
829 for (sit
= self
->transients
; sit
; sit
= next
) {
830 next
= g_slist_next(sit
);
831 if (sit
->data
== it
->data
)
833 g_slist_delete_link(self
->transients
, sit
);
836 } else if (self
->transient_for
!= NULL
) { /* transient of window */
837 /* add to new parent */
838 self
->transient_for
->transients
=
839 g_slist_append(self
->transient_for
->transients
, self
);
844 static void client_get_mwm_hints(ObClient
*self
)
849 self
->mwmhints
.flags
= 0; /* default to none */
851 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
853 if (num
>= OB_MWM_ELEMENTS
) {
854 self
->mwmhints
.flags
= hints
[0];
855 self
->mwmhints
.functions
= hints
[1];
856 self
->mwmhints
.decorations
= hints
[2];
862 void client_get_type(ObClient
*self
)
869 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
870 /* use the first value that we know about in the array */
871 for (i
= 0; i
< num
; ++i
) {
872 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
873 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
874 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
875 self
->type
= OB_CLIENT_TYPE_DOCK
;
876 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
877 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
878 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
879 self
->type
= OB_CLIENT_TYPE_MENU
;
880 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
881 self
->type
= OB_CLIENT_TYPE_UTILITY
;
882 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
883 self
->type
= OB_CLIENT_TYPE_SPLASH
;
884 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
885 self
->type
= OB_CLIENT_TYPE_DIALOG
;
886 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
887 self
->type
= OB_CLIENT_TYPE_NORMAL
;
888 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
889 /* prevent this window from getting any decor or
891 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
892 OB_MWM_FLAG_DECORATIONS
);
893 self
->mwmhints
.decorations
= 0;
894 self
->mwmhints
.functions
= 0;
896 if (self
->type
!= (ObClientType
) -1)
897 break; /* grab the first legit type */
902 if (self
->type
== (ObClientType
) -1) {
903 /*the window type hint was not set, which means we either classify
904 ourself as a normal window or a dialog, depending on if we are a
907 self
->type
= OB_CLIENT_TYPE_DIALOG
;
909 self
->type
= OB_CLIENT_TYPE_NORMAL
;
913 void client_update_protocols(ObClient
*self
)
918 self
->focus_notify
= FALSE
;
919 self
->delete_window
= FALSE
;
921 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
922 for (i
= 0; i
< num_return
; ++i
) {
923 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
924 /* this means we can request the window to close */
925 self
->delete_window
= TRUE
;
926 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
927 /* if this protocol is requested, then the window will be
928 notified whenever we want it to receive focus */
929 self
->focus_notify
= TRUE
;
935 static void client_get_gravity(ObClient
*self
)
937 XWindowAttributes wattrib
;
940 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
941 g_assert(ret
!= BadWindow
);
942 self
->gravity
= wattrib
.win_gravity
;
945 void client_update_normal_hints(ObClient
*self
)
949 int oldgravity
= self
->gravity
;
952 self
->min_ratio
= 0.0f
;
953 self
->max_ratio
= 0.0f
;
954 SIZE_SET(self
->size_inc
, 1, 1);
955 SIZE_SET(self
->base_size
, 0, 0);
956 SIZE_SET(self
->min_size
, 0, 0);
957 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
959 /* get the hints from the window */
960 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
961 self
->positioned
= !!(size
.flags
& (PPosition
|USPosition
));
963 if (size
.flags
& PWinGravity
) {
964 self
->gravity
= size
.win_gravity
;
966 /* if the client has a frame, i.e. has already been mapped and
967 is changing its gravity */
968 if (self
->frame
&& self
->gravity
!= oldgravity
) {
969 /* move our idea of the client's position based on its new
971 self
->area
.x
= self
->frame
->area
.x
;
972 self
->area
.y
= self
->frame
->area
.y
;
973 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
977 if (size
.flags
& PAspect
) {
978 if (size
.min_aspect
.y
)
979 self
->min_ratio
= (float)size
.min_aspect
.x
/ size
.min_aspect
.y
;
980 if (size
.max_aspect
.y
)
981 self
->max_ratio
= (float)size
.max_aspect
.x
/ size
.max_aspect
.y
;
984 if (size
.flags
& PMinSize
)
985 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
987 if (size
.flags
& PMaxSize
)
988 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
990 if (size
.flags
& PBaseSize
)
991 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
993 if (size
.flags
& PResizeInc
)
994 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
998 void client_setup_decor_and_functions(ObClient
*self
)
1000 /* start with everything (cept fullscreen) */
1001 self
->decorations
= (OB_FRAME_DECOR_TITLEBAR
|
1002 OB_FRAME_DECOR_HANDLE
|
1003 OB_FRAME_DECOR_GRIPS
|
1004 OB_FRAME_DECOR_BORDER
|
1005 OB_FRAME_DECOR_ICON
|
1006 OB_FRAME_DECOR_ALLDESKTOPS
|
1007 OB_FRAME_DECOR_ICONIFY
|
1008 OB_FRAME_DECOR_MAXIMIZE
|
1009 OB_FRAME_DECOR_SHADE
);
1010 self
->functions
= (OB_CLIENT_FUNC_RESIZE
|
1011 OB_CLIENT_FUNC_MOVE
|
1012 OB_CLIENT_FUNC_ICONIFY
|
1013 OB_CLIENT_FUNC_MAXIMIZE
|
1014 OB_CLIENT_FUNC_SHADE
);
1015 if (self
->delete_window
) {
1016 self
->functions
|= OB_CLIENT_FUNC_CLOSE
;
1017 self
->decorations
|= OB_FRAME_DECOR_CLOSE
;
1020 if (!(self
->min_size
.width
< self
->max_size
.width
||
1021 self
->min_size
.height
< self
->max_size
.height
))
1022 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1024 switch (self
->type
) {
1025 case OB_CLIENT_TYPE_NORMAL
:
1026 /* normal windows retain all of the possible decorations and
1027 functionality, and are the only windows that you can fullscreen */
1028 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1031 case OB_CLIENT_TYPE_DIALOG
:
1032 case OB_CLIENT_TYPE_UTILITY
:
1033 /* these windows cannot be maximized */
1034 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1037 case OB_CLIENT_TYPE_MENU
:
1038 case OB_CLIENT_TYPE_TOOLBAR
:
1039 /* these windows get less functionality */
1040 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1043 case OB_CLIENT_TYPE_DESKTOP
:
1044 case OB_CLIENT_TYPE_DOCK
:
1045 case OB_CLIENT_TYPE_SPLASH
:
1046 /* none of these windows are manipulated by the window manager */
1047 self
->decorations
= 0;
1048 self
->functions
= 0;
1052 /* Mwm Hints are applied subtractively to what has already been chosen for
1053 decor and functionality */
1054 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1055 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1056 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1057 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1058 /* if the mwm hints request no handle or title, then all
1059 decorations are disabled */
1060 self
->decorations
= 0;
1064 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1065 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1066 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1067 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1068 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1069 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1070 /* dont let mwm hints kill any buttons
1071 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1072 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1073 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1074 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1076 /* dont let mwm hints kill the close button
1077 if (! (self->mwmhints.functions & MwmFunc_Close))
1078 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1082 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1083 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1084 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1085 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1086 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1087 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1089 /* can't maximize without moving/resizing */
1090 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1091 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1092 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1093 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1094 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1097 /* finally, the user can have requested no decorations, which overrides
1099 if (!self
->decorate
)
1100 self
->decorations
= 0;
1102 /* if we don't have a titlebar, then we cannot shade! */
1103 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1104 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1106 /* now we need to check against rules for the client's current state */
1107 if (self
->fullscreen
) {
1108 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1109 OB_CLIENT_FUNC_FULLSCREEN
|
1110 OB_CLIENT_FUNC_ICONIFY
);
1111 self
->decorations
= 0;
1114 client_change_allowed_actions(self
);
1117 if (self
->decorations
!= self
->frame
->decorations
)
1118 /* adjust the client's decorations, etc. */
1119 client_reconfigure(self
);
1120 /* we actually have to do this twice :P
1121 the first time it removes the decorations, but now it may need to
1122 be reconstrained for being maximized etc, so calling this again
1123 will work with the new setup of decorations on the window */
1124 client_reconfigure(self
);
1126 /* this makes sure that these windows appear on all desktops */
1127 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
&&
1128 self
->desktop
!= DESKTOP_ALL
)
1130 self
->desktop
= DESKTOP_ALL
;
1135 static void client_change_allowed_actions(ObClient
*self
)
1140 /* desktop windows are kept on all desktops */
1141 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1142 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1144 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1145 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1146 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1147 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1148 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1149 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1150 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1151 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1152 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1153 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1154 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1155 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1156 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1157 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1158 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1161 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1163 /* make sure the window isn't breaking any rules now */
1165 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1166 if (self
->frame
) client_shade(self
, FALSE
);
1167 else self
->shaded
= FALSE
;
1169 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1170 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1171 else self
->iconic
= FALSE
;
1173 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1174 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1175 else self
->fullscreen
= FALSE
;
1177 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1179 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1180 else self
->max_vert
= self
->max_horz
= FALSE
;
1184 void client_reconfigure(ObClient
*self
)
1186 /* by making this pass FALSE for user, we avoid the emacs event storm where
1187 every configurenotify causes an update in its normal hints, i think this
1188 is generally what we want anyways... */
1189 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1190 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1193 void client_update_wmhints(ObClient
*self
)
1196 gboolean ur
= FALSE
;
1199 /* assume a window takes input if it doesnt specify */
1200 self
->can_focus
= TRUE
;
1202 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1203 if (hints
->flags
& InputHint
)
1204 self
->can_focus
= hints
->input
;
1206 /* only do this when first managing the window *AND* when we aren't
1208 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1209 if (hints
->flags
& StateHint
)
1210 self
->iconic
= hints
->initial_state
== IconicState
;
1212 if (hints
->flags
& XUrgencyHint
)
1215 if (!(hints
->flags
& WindowGroupHint
))
1216 hints
->window_group
= None
;
1218 /* did the group state change? */
1219 if (hints
->window_group
!=
1220 (self
->group
? self
->group
->leader
: None
)) {
1221 /* remove from the old group if there was one */
1222 if (self
->group
!= NULL
) {
1223 /* remove transients of the group */
1224 for (it
= self
->group
->members
; it
; it
= it
->next
)
1225 self
->transients
= g_slist_remove(self
->transients
,
1227 group_remove(self
->group
, self
);
1230 if (hints
->window_group
!= None
) {
1231 self
->group
= group_add(hints
->window_group
, self
);
1233 /* i can only have transients from the group if i am not
1235 if (!self
->transient_for
) {
1236 /* add other transients of the group that are already
1238 for (it
= self
->group
->members
; it
; it
= it
->next
) {
1239 ObClient
*c
= it
->data
;
1240 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1242 g_slist_append(self
->transients
, c
);
1247 /* because the self->transient flag wont change from this call,
1248 we don't need to update the window's type and such, only its
1249 transient_for, and the transients lists of other windows in
1250 the group may be affected */
1251 client_update_transient_for(self
);
1254 /* the WM_HINTS can contain an icon */
1255 client_update_icons(self
);
1260 if (ur
!= self
->urgent
) {
1262 ob_debug("Urgent Hint for 0x%lx: %s\n", self
->window
,
1264 /* fire the urgent callback if we're mapped, otherwise, wait until
1265 after we're mapped */
1267 dispatch_client(Event_Client_Urgent
, self
, self
->urgent
, 0);
1271 void client_update_title(ObClient
*self
)
1277 gboolean read_title
;
1279 g_free(self
->title
);
1282 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
))
1283 /* try old x stuff */
1284 if (!PROP_GETS(self
->window
, wm_name
, locale
, &data
))
1285 data
= g_strdup("Unnamed Window");
1287 /* look for duplicates and append a number */
1289 for (it
= client_list
; it
; it
= it
->next
)
1290 if (it
->data
!= self
) {
1291 ObClient
*c
= it
->data
;
1292 if (0 == strncmp(c
->title
, data
, strlen(data
)))
1293 nums
|= 1 << c
->title_count
;
1295 /* find first free number */
1296 for (i
= 1; i
<= 32; ++i
)
1297 if (!(nums
& (1 << i
))) {
1298 if (self
->title_count
== 1 || i
== 1)
1299 self
->title_count
= i
;
1302 /* dont display the number for the first window */
1303 if (self
->title_count
> 1) {
1304 char *vdata
, *ndata
;
1305 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1306 vdata
= g_strconcat(data
, ndata
, NULL
);
1312 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1317 frame_adjust_title(self
->frame
);
1319 /* update the icon title */
1321 g_free(self
->icon_title
);
1325 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1326 /* try old x stuff */
1327 if (!PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)) {
1328 data
= g_strdup(self
->title
);
1332 /* append the title count, dont display the number for the first window */
1333 if (read_title
&& self
->title_count
> 1) {
1334 char *vdata
, *ndata
;
1335 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1336 vdata
= g_strconcat(data
, ndata
, NULL
);
1342 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1344 self
->icon_title
= data
;
1347 void client_update_class(ObClient
*self
)
1352 if (self
->name
) g_free(self
->name
);
1353 if (self
->class) g_free(self
->class);
1354 if (self
->role
) g_free(self
->role
);
1356 self
->name
= self
->class = self
->role
= NULL
;
1358 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1360 self
->name
= g_strdup(data
[0]);
1362 self
->class = g_strdup(data
[1]);
1367 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1368 self
->role
= g_strdup(s
);
1370 if (self
->name
== NULL
) self
->name
= g_strdup("");
1371 if (self
->class == NULL
) self
->class = g_strdup("");
1372 if (self
->role
== NULL
) self
->role
= g_strdup("");
1375 void client_update_strut(ObClient
*self
)
1379 gboolean got
= FALSE
;
1381 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1385 STRUT_PARTIAL_SET(self
->strut
,
1386 data
[0], data
[2], data
[1], data
[3],
1387 data
[4], data
[5], data
[8], data
[9],
1388 data
[6], data
[7], data
[10], data
[11]);
1394 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1397 STRUT_PARTIAL_SET(self
->strut
,
1398 data
[0], data
[2], data
[1], data
[3],
1399 0, 0, 0, 0, 0, 0, 0, 0);
1405 STRUT_PARTIAL_SET(self
->strut
, 0, 0, 0, 0,
1406 0, 0, 0, 0, 0, 0, 0, 0);
1408 /* updating here is pointless while we're being mapped cuz we're not in
1409 the client list yet */
1411 screen_update_areas();
1414 void client_update_icons(ObClient
*self
)
1420 for (i
= 0; i
< self
->nicons
; ++i
)
1421 g_free(self
->icons
[i
].data
);
1422 if (self
->nicons
> 0)
1423 g_free(self
->icons
);
1426 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1427 /* figure out how many valid icons are in here */
1429 while (num
- i
> 2) {
1433 if (i
> num
|| w
*h
== 0) break;
1437 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1439 /* store the icons */
1441 for (j
= 0; j
< self
->nicons
; ++j
) {
1444 w
= self
->icons
[j
].width
= data
[i
++];
1445 h
= self
->icons
[j
].height
= data
[i
++];
1447 if (w
*h
== 0) continue;
1449 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1450 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1455 self
->icons
[j
].data
[t
] =
1456 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1457 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1458 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1459 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1465 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1466 kwm_win_icon
, &data
, &num
)) {
1469 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1470 xerror_set_ignore(TRUE
);
1471 if (!RrPixmapToRGBA(ob_rr_inst
,
1473 &self
->icons
[self
->nicons
-1].width
,
1474 &self
->icons
[self
->nicons
-1].height
,
1475 &self
->icons
[self
->nicons
-1].data
)) {
1476 g_free(&self
->icons
[self
->nicons
-1]);
1479 xerror_set_ignore(FALSE
);
1485 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1486 if (hints
->flags
& IconPixmapHint
) {
1488 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1489 xerror_set_ignore(TRUE
);
1490 if (!RrPixmapToRGBA(ob_rr_inst
,
1492 (hints
->flags
& IconMaskHint
?
1493 hints
->icon_mask
: None
),
1494 &self
->icons
[self
->nicons
-1].width
,
1495 &self
->icons
[self
->nicons
-1].height
,
1496 &self
->icons
[self
->nicons
-1].data
)){
1497 g_free(&self
->icons
[self
->nicons
-1]);
1500 xerror_set_ignore(FALSE
);
1507 frame_adjust_icon(self
->frame
);
1510 static void client_change_state(ObClient
*self
)
1513 guint32 netstate
[10];
1516 state
[0] = self
->wmstate
;
1518 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1522 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1524 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1526 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1527 if (self
->skip_taskbar
)
1528 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1529 if (self
->skip_pager
)
1530 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1531 if (self
->fullscreen
)
1532 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1534 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1536 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1538 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1540 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1541 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1543 client_calc_layer(self
);
1546 frame_adjust_state(self
->frame
);
1549 ObClient
*client_search_focus_tree(ObClient
*self
)
1554 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
1555 if (client_focused(it
->data
)) return it
->data
;
1556 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1561 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1563 if (self
->transient_for
) {
1564 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1565 return client_search_focus_tree_full(self
->transient_for
);
1568 gboolean recursed
= FALSE
;
1570 for (it
= self
->group
->members
; it
; it
= it
->next
)
1571 if (!((ObClient
*)it
->data
)->transient_for
) {
1573 if ((c
= client_search_focus_tree_full(it
->data
)))
1582 /* this function checks the whole tree, the client_search_focus_tree~
1583 does not, so we need to check this window */
1584 if (client_focused(self
))
1586 return client_search_focus_tree(self
);
1589 static ObStackingLayer
calc_layer(ObClient
*self
)
1593 if (self
->fullscreen
) l
= OB_STACKING_LAYER_FULLSCREEN
;
1594 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1595 l
= OB_STACKING_LAYER_DESKTOP
;
1596 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1597 if (!self
->below
) l
= OB_STACKING_LAYER_TOP
;
1598 else l
= OB_STACKING_LAYER_NORMAL
;
1600 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1601 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1602 else l
= OB_STACKING_LAYER_NORMAL
;
1607 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1608 ObStackingLayer l
, gboolean raised
)
1610 ObStackingLayer old
, own
;
1614 own
= calc_layer(self
);
1615 self
->layer
= l
> own
? l
: own
;
1617 for (it
= self
->transients
; it
; it
= it
->next
)
1618 client_calc_layer_recursive(it
->data
, orig
,
1619 l
, raised
? raised
: l
!= old
);
1621 if (!raised
&& l
!= old
)
1622 if (orig
->frame
) { /* only restack if the original window is managed */
1623 /* XXX add_non_intrusive ever? */
1624 stacking_remove(CLIENT_AS_WINDOW(self
));
1625 stacking_add(CLIENT_AS_WINDOW(self
));
1629 void client_calc_layer(ObClient
*self
)
1636 /* transients take on the layer of their parents */
1637 self
= client_search_top_transient(self
);
1639 l
= calc_layer(self
);
1641 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1644 gboolean
client_should_show(ObClient
*self
)
1646 if (self
->iconic
) return FALSE
;
1647 else if (!(self
->desktop
== screen_desktop
||
1648 self
->desktop
== DESKTOP_ALL
)) return FALSE
;
1649 else if (client_normal(self
) && screen_showing_desktop
) return FALSE
;
1654 static void client_showhide(ObClient
*self
)
1657 if (client_should_show(self
))
1658 frame_show(self
->frame
);
1660 frame_hide(self
->frame
);
1663 gboolean
client_normal(ObClient
*self
) {
1664 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
1665 self
->type
== OB_CLIENT_TYPE_DOCK
||
1666 self
->type
== OB_CLIENT_TYPE_SPLASH
);
1669 static void client_apply_startup_state(ObClient
*self
)
1671 /* these are in a carefully crafted order.. */
1674 self
->iconic
= FALSE
;
1675 client_iconify(self
, TRUE
, FALSE
);
1677 if (self
->fullscreen
) {
1678 self
->fullscreen
= FALSE
;
1679 client_fullscreen(self
, TRUE
, FALSE
);
1682 self
->shaded
= FALSE
;
1683 client_shade(self
, TRUE
);
1686 dispatch_client(Event_Client_Urgent
, self
, self
->urgent
, 0);
1688 if (self
->max_vert
&& self
->max_horz
) {
1689 self
->max_vert
= self
->max_horz
= FALSE
;
1690 client_maximize(self
, TRUE
, 0, FALSE
);
1691 } else if (self
->max_vert
) {
1692 self
->max_vert
= FALSE
;
1693 client_maximize(self
, TRUE
, 2, FALSE
);
1694 } else if (self
->max_horz
) {
1695 self
->max_horz
= FALSE
;
1696 client_maximize(self
, TRUE
, 1, FALSE
);
1699 /* nothing to do for the other states:
1708 void client_configure_full(ObClient
*self
, ObCorner anchor
,
1709 int x
, int y
, int w
, int h
,
1710 gboolean user
, gboolean final
,
1711 gboolean force_reply
)
1713 gboolean moved
= FALSE
, resized
= FALSE
;
1715 /* make the frame recalculate its dimentions n shit without changing
1716 anything visible for real, this way the constraints below can work with
1717 the updated frame dimensions. */
1718 frame_adjust_area(self
->frame
, TRUE
, TRUE
, TRUE
);
1720 /* gets the frame's position */
1721 frame_client_gravity(self
->frame
, &x
, &y
);
1723 /* these positions are frame positions, not client positions */
1725 /* set the size and position if fullscreen */
1726 if (self
->fullscreen
) {
1729 XF86VidModeModeLine mode
;
1734 i
= client_monitor(self
);
1735 a
= screen_physical_area_monitor(i
);
1738 if (i
== 0 && /* primary head */
1739 extensions_vidmode
&&
1740 XF86VidModeGetViewPort(ob_display
, ob_screen
, &x
, &y
) &&
1741 /* get the mode last so the mode.privsize isnt freed incorrectly */
1742 XF86VidModeGetModeLine(ob_display
, ob_screen
, &dot
, &mode
)) {
1747 if (mode
.privsize
) XFree(mode
.private);
1757 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
1761 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
1763 /* set the size and position if maximized */
1764 if (self
->max_horz
) {
1766 w
= a
->width
- self
->frame
->size
.left
- self
->frame
->size
.right
;
1768 if (self
->max_vert
) {
1770 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
1774 /* gets the client's position */
1775 frame_frame_gravity(self
->frame
, &x
, &y
);
1777 /* these override the above states! if you cant move you can't move! */
1779 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
1783 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
1784 w
= self
->area
.width
;
1785 h
= self
->area
.height
;
1789 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
1790 int basew
, baseh
, minw
, minh
;
1792 /* base size is substituted with min size if not specified */
1793 if (self
->base_size
.width
|| self
->base_size
.height
) {
1794 basew
= self
->base_size
.width
;
1795 baseh
= self
->base_size
.height
;
1797 basew
= self
->min_size
.width
;
1798 baseh
= self
->min_size
.height
;
1800 /* min size is substituted with base size if not specified */
1801 if (self
->min_size
.width
|| self
->min_size
.height
) {
1802 minw
= self
->min_size
.width
;
1803 minh
= self
->min_size
.height
;
1805 minw
= self
->base_size
.width
;
1806 minh
= self
->base_size
.height
;
1810 /* for interactive resizing. have to move half an increment in each
1813 /* how far we are towards the next size inc */
1814 int mw
= (w
- basew
) % self
->size_inc
.width
;
1815 int mh
= (h
- baseh
) % self
->size_inc
.height
;
1817 int aw
= self
->size_inc
.width
/ 2;
1818 int ah
= self
->size_inc
.height
/ 2;
1819 /* don't let us move into a new size increment */
1820 if (mw
+ aw
>= self
->size_inc
.width
)
1821 aw
= self
->size_inc
.width
- mw
- 1;
1822 if (mh
+ ah
>= self
->size_inc
.height
)
1823 ah
= self
->size_inc
.height
- mh
- 1;
1827 /* if this is a user-requested resize, then check against min/max
1830 /* smaller than min size or bigger than max size? */
1831 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
1832 if (w
< minw
) w
= minw
;
1833 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
1834 if (h
< minh
) h
= minh
;
1840 /* keep to the increments */
1841 w
/= self
->size_inc
.width
;
1842 h
/= self
->size_inc
.height
;
1844 /* you cannot resize to nothing */
1845 if (basew
+ w
< 1) w
= 1 - basew
;
1846 if (baseh
+ h
< 1) h
= 1 - baseh
;
1848 /* store the logical size */
1849 SIZE_SET(self
->logical_size
,
1850 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
1851 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
1853 w
*= self
->size_inc
.width
;
1854 h
*= self
->size_inc
.height
;
1860 /* adjust the height to match the width for the aspect ratios.
1861 for this, min size is not substituted for base size ever. */
1862 w
-= self
->base_size
.width
;
1863 h
-= self
->base_size
.height
;
1865 if (self
->min_ratio
)
1866 if (h
* self
->min_ratio
> w
) h
= (int)(w
/ self
->min_ratio
);
1867 if (self
->max_ratio
)
1868 if (h
* self
->max_ratio
< w
) h
= (int)(w
/ self
->max_ratio
);
1870 w
+= self
->base_size
.width
;
1871 h
+= self
->base_size
.height
;
1876 case OB_CORNER_TOPLEFT
:
1878 case OB_CORNER_TOPRIGHT
:
1879 x
-= w
- self
->area
.width
;
1881 case OB_CORNER_BOTTOMLEFT
:
1882 y
-= h
- self
->area
.height
;
1884 case OB_CORNER_BOTTOMRIGHT
:
1885 x
-= w
- self
->area
.width
;
1886 y
-= h
- self
->area
.height
;
1890 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
1891 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
1893 RECT_SET(self
->area
, x
, y
, w
, h
);
1895 /* for app-requested resizes, always resize if 'resized' is true.
1896 for user-requested ones, only resize if final is true, or when
1897 resizing in redraw mode */
1898 if ((!user
&& resized
) ||
1899 (user
&& (final
|| (resized
&& config_redraw_resize
))))
1900 XResizeWindow(ob_display
, self
->window
, w
, h
);
1902 /* move/resize the frame to match the request */
1904 if (self
->decorations
!= self
->frame
->decorations
)
1905 moved
= resized
= TRUE
;
1907 if (moved
|| resized
)
1908 frame_adjust_area(self
->frame
, moved
, resized
, FALSE
);
1910 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
1913 event
.type
= ConfigureNotify
;
1914 event
.xconfigure
.display
= ob_display
;
1915 event
.xconfigure
.event
= self
->window
;
1916 event
.xconfigure
.window
= self
->window
;
1918 /* root window real coords */
1919 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
1921 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
1923 event
.xconfigure
.width
= w
;
1924 event
.xconfigure
.height
= h
;
1925 event
.xconfigure
.border_width
= 0;
1926 event
.xconfigure
.above
= self
->frame
->plate
;
1927 event
.xconfigure
.override_redirect
= FALSE
;
1928 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
1929 FALSE
, StructureNotifyMask
, &event
);
1934 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
1938 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
1939 self
->fullscreen
== fs
) return; /* already done */
1941 self
->fullscreen
= fs
;
1942 client_change_state(self
); /* change the state hints on the client,
1943 and adjust out layer/stacking */
1947 guint32 dimensions
[4];
1948 dimensions
[0] = self
->area
.x
;
1949 dimensions
[1] = self
->area
.y
;
1950 dimensions
[2] = self
->area
.width
;
1951 dimensions
[3] = self
->area
.height
;
1953 PROP_SETA32(self
->window
, openbox_premax
, cardinal
,
1957 /* these are not actually used cuz client_configure will set them
1958 as appropriate when the window is fullscreened */
1965 /* pick some fallbacks... */
1966 a
= screen_area_monitor(self
->desktop
, 0);
1967 x
= a
->x
+ a
->width
/ 4;
1968 y
= a
->y
+ a
->height
/ 4;
1972 if (PROP_GETA32(self
->window
, openbox_premax
, cardinal
,
1973 (guint32
**)&dimensions
, &num
)) {
1984 client_setup_decor_and_functions(self
);
1986 client_configure(self
, OB_CORNER_TOPLEFT
, x
, y
, w
, h
, TRUE
, TRUE
);
1988 /* try focus us when we go into fullscreen mode */
1992 static void client_iconify_recursive(ObClient
*self
,
1993 gboolean iconic
, gboolean curdesk
)
1996 gboolean changed
= FALSE
;
1999 if (self
->iconic
!= iconic
) {
2000 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
2003 self
->iconic
= iconic
;
2006 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2007 self
->wmstate
= IconicState
;
2008 self
->ignore_unmaps
++;
2009 /* we unmap the client itself so that we can get MapRequest
2010 events, and because the ICCCM tells us to! */
2011 XUnmapWindow(ob_display
, self
->window
);
2013 /* update the focus lists.. iconic windows go to the bottom of
2014 the list, put the new iconic window at the 'top of the
2016 focus_order_to_top(self
);
2022 client_set_desktop(self
, screen_desktop
, FALSE
);
2023 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2024 XMapWindow(ob_display
, self
->window
);
2026 /* this puts it after the current focused window */
2027 focus_order_remove(self
);
2028 focus_order_add_new(self
);
2030 /* this is here cuz with the VIDMODE extension, the viewport can
2031 change while a fullscreen window is iconic, and when it
2032 uniconifies, it would be nice if it did so to the new position
2034 client_reconfigure(self
);
2041 client_change_state(self
);
2042 client_showhide(self
);
2043 screen_update_areas();
2045 dispatch_client(iconic
? Event_Client_Unmapped
: Event_Client_Mapped
,
2049 /* iconify all transients */
2050 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
)
2051 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2055 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2057 /* move up the transient chain as far as possible first */
2058 self
= client_search_top_transient(self
);
2060 client_iconify_recursive(client_search_top_transient(self
),
2064 void client_maximize(ObClient
*self
, gboolean max
, int dir
, gboolean savearea
)
2068 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2069 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2071 /* check if already done */
2073 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2074 if (dir
== 1 && self
->max_horz
) return;
2075 if (dir
== 2 && self
->max_vert
) return;
2077 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2078 if (dir
== 1 && !self
->max_horz
) return;
2079 if (dir
== 2 && !self
->max_vert
) return;
2082 /* work with the frame's coords */
2083 x
= self
->frame
->area
.x
;
2084 y
= self
->frame
->area
.y
;
2085 w
= self
->area
.width
;
2086 h
= self
->area
.height
;
2090 gint32 dimensions
[4];
2099 /* get the property off the window and use it for the dimensions
2100 we are already maxed on */
2101 if (PROP_GETA32(self
->window
, openbox_premax
, cardinal
,
2102 (guint32
**)&readdim
, &num
)) {
2104 if (self
->max_horz
) {
2105 dimensions
[0] = readdim
[0];
2106 dimensions
[2] = readdim
[2];
2108 if (self
->max_vert
) {
2109 dimensions
[1] = readdim
[1];
2110 dimensions
[3] = readdim
[3];
2116 PROP_SETA32(self
->window
, openbox_premax
, cardinal
,
2117 (guint32
*)dimensions
, 4);
2124 /* pick some fallbacks... */
2125 a
= screen_area_monitor(self
->desktop
, 0);
2126 if (dir
== 0 || dir
== 1) { /* horz */
2127 x
= a
->x
+ a
->width
/ 4;
2130 if (dir
== 0 || dir
== 2) { /* vert */
2131 y
= a
->y
+ a
->height
/ 4;
2135 if (PROP_GETA32(self
->window
, openbox_premax
, cardinal
,
2136 (guint32
**)&dimensions
, &num
)) {
2138 if (dir
== 0 || dir
== 1) { /* horz */
2142 if (dir
== 0 || dir
== 2) { /* vert */
2151 if (dir
== 0 || dir
== 1) /* horz */
2152 self
->max_horz
= max
;
2153 if (dir
== 0 || dir
== 2) /* vert */
2154 self
->max_vert
= max
;
2156 if (!self
->max_horz
&& !self
->max_vert
)
2157 PROP_ERASE(self
->window
, openbox_premax
);
2159 client_change_state(self
); /* change the state hints on the client */
2161 /* figure out where the client should be going */
2162 frame_frame_gravity(self
->frame
, &x
, &y
);
2163 client_configure(self
, OB_CORNER_TOPLEFT
, x
, y
, w
, h
, TRUE
, TRUE
);
2166 void client_shade(ObClient
*self
, gboolean shade
)
2168 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2169 shade
) || /* can't shade */
2170 self
->shaded
== shade
) return; /* already done */
2172 /* when we're iconic, don't change the wmstate */
2174 self
->wmstate
= shade
? IconicState
: NormalState
;
2175 self
->shaded
= shade
;
2176 client_change_state(self
);
2177 /* resize the frame to just the titlebar */
2178 frame_adjust_area(self
->frame
, FALSE
, FALSE
, FALSE
);
2181 void client_close(ObClient
*self
)
2185 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2188 XXX: itd be cool to do timeouts and shit here for killing the client's
2190 like... if the window is around after 5 seconds, then the close button
2191 turns a nice red, and if this function is called again, the client is
2195 ce
.xclient
.type
= ClientMessage
;
2196 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2197 ce
.xclient
.display
= ob_display
;
2198 ce
.xclient
.window
= self
->window
;
2199 ce
.xclient
.format
= 32;
2200 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2201 ce
.xclient
.data
.l
[1] = event_lasttime
;
2202 ce
.xclient
.data
.l
[2] = 0l;
2203 ce
.xclient
.data
.l
[3] = 0l;
2204 ce
.xclient
.data
.l
[4] = 0l;
2205 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2208 void client_kill(ObClient
*self
)
2210 XKillClient(ob_display
, self
->window
);
2213 void client_set_desktop_recursive(ObClient
*self
,
2214 guint target
, gboolean donthide
)
2219 if (target
!= self
->desktop
) {
2221 ob_debug("Setting desktop %u\n", target
+1);
2223 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2225 /* remove from the old desktop(s) */
2226 focus_order_remove(self
);
2228 old
= self
->desktop
;
2229 self
->desktop
= target
;
2230 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2231 /* the frame can display the current desktop state */
2232 frame_adjust_state(self
->frame
);
2233 /* 'move' the window to the new desktop */
2235 client_showhide(self
);
2236 /* raise if it was not already on the desktop */
2237 if (old
!= DESKTOP_ALL
)
2238 stacking_raise(CLIENT_AS_WINDOW(self
));
2239 screen_update_areas();
2241 /* add to the new desktop(s) */
2242 if (config_focus_new
)
2243 focus_order_to_top(self
);
2245 focus_order_to_bottom(self
);
2247 dispatch_client(Event_Client_Desktop
, self
, target
, old
);
2250 /* move all transients */
2251 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
)
2252 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2256 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2258 client_set_desktop_recursive(client_search_top_transient(self
),
2262 ObClient
*client_search_modal_child(ObClient
*self
)
2267 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
2268 ObClient
*c
= it
->data
;
2269 if ((ret
= client_search_modal_child(c
))) return ret
;
2270 if (c
->modal
) return c
;
2275 gboolean
client_validate(ObClient
*self
)
2279 XSync(ob_display
, FALSE
); /* get all events on the server */
2281 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2282 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2283 XPutBackEvent(ob_display
, &e
);
2290 void client_set_wm_state(ObClient
*self
, long state
)
2292 if (state
== self
->wmstate
) return; /* no change */
2296 client_iconify(self
, TRUE
, TRUE
);
2299 client_iconify(self
, FALSE
, TRUE
);
2304 void client_set_state(ObClient
*self
, Atom action
, long data1
, long data2
)
2306 gboolean shaded
= self
->shaded
;
2307 gboolean fullscreen
= self
->fullscreen
;
2308 gboolean max_horz
= self
->max_horz
;
2309 gboolean max_vert
= self
->max_vert
;
2312 if (!(action
== prop_atoms
.net_wm_state_add
||
2313 action
== prop_atoms
.net_wm_state_remove
||
2314 action
== prop_atoms
.net_wm_state_toggle
))
2315 /* an invalid action was passed to the client message, ignore it */
2318 for (i
= 0; i
< 2; ++i
) {
2319 Atom state
= i
== 0 ? data1
: data2
;
2321 if (!state
) continue;
2323 /* if toggling, then pick whether we're adding or removing */
2324 if (action
== prop_atoms
.net_wm_state_toggle
) {
2325 if (state
== prop_atoms
.net_wm_state_modal
)
2326 action
= self
->modal
? prop_atoms
.net_wm_state_remove
:
2327 prop_atoms
.net_wm_state_add
;
2328 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2329 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2330 prop_atoms
.net_wm_state_add
;
2331 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2332 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2333 prop_atoms
.net_wm_state_add
;
2334 else if (state
== prop_atoms
.net_wm_state_shaded
)
2335 action
= self
->shaded
? prop_atoms
.net_wm_state_remove
:
2336 prop_atoms
.net_wm_state_add
;
2337 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2338 action
= self
->skip_taskbar
?
2339 prop_atoms
.net_wm_state_remove
:
2340 prop_atoms
.net_wm_state_add
;
2341 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2342 action
= self
->skip_pager
?
2343 prop_atoms
.net_wm_state_remove
:
2344 prop_atoms
.net_wm_state_add
;
2345 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2346 action
= self
->fullscreen
?
2347 prop_atoms
.net_wm_state_remove
:
2348 prop_atoms
.net_wm_state_add
;
2349 else if (state
== prop_atoms
.net_wm_state_above
)
2350 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2351 prop_atoms
.net_wm_state_add
;
2352 else if (state
== prop_atoms
.net_wm_state_below
)
2353 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2354 prop_atoms
.net_wm_state_add
;
2357 if (action
== prop_atoms
.net_wm_state_add
) {
2358 if (state
== prop_atoms
.net_wm_state_modal
) {
2359 /* XXX raise here or something? */
2361 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2363 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2365 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2367 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2368 self
->skip_taskbar
= TRUE
;
2369 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2370 self
->skip_pager
= TRUE
;
2371 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2373 } else if (state
== prop_atoms
.net_wm_state_above
) {
2375 } else if (state
== prop_atoms
.net_wm_state_below
) {
2379 } else { /* action == prop_atoms.net_wm_state_remove */
2380 if (state
== prop_atoms
.net_wm_state_modal
) {
2381 self
->modal
= FALSE
;
2382 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2384 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2386 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2388 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2389 self
->skip_taskbar
= FALSE
;
2390 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2391 self
->skip_pager
= FALSE
;
2392 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2394 } else if (state
== prop_atoms
.net_wm_state_above
) {
2395 self
->above
= FALSE
;
2396 } else if (state
== prop_atoms
.net_wm_state_below
) {
2397 self
->below
= FALSE
;
2401 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2402 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2404 if (max_horz
== max_vert
) { /* both going the same way */
2405 client_maximize(self
, max_horz
, 0, TRUE
);
2407 client_maximize(self
, max_horz
, 1, TRUE
);
2408 client_maximize(self
, max_vert
, 2, TRUE
);
2412 if (max_horz
!= self
->max_horz
)
2413 client_maximize(self
, max_horz
, 1, TRUE
);
2415 client_maximize(self
, max_vert
, 2, TRUE
);
2418 /* change fullscreen state before shading, as it will affect if the window
2420 if (fullscreen
!= self
->fullscreen
)
2421 client_fullscreen(self
, fullscreen
, TRUE
);
2422 if (shaded
!= self
->shaded
)
2423 client_shade(self
, shaded
);
2424 client_calc_layer(self
);
2425 client_change_state(self
); /* change the hint to reflect these changes */
2428 ObClient
*client_focus_target(ObClient
*self
)
2432 /* if we have a modal child, then focus it, not us */
2433 child
= client_search_modal_child(self
);
2434 if (child
) return child
;
2438 gboolean
client_can_focus(ObClient
*self
)
2442 /* choose the correct target */
2443 self
= client_focus_target(self
);
2445 if (!self
->frame
->visible
)
2448 if (!((self
->can_focus
|| self
->focus_notify
) &&
2449 (self
->desktop
== screen_desktop
||
2450 self
->desktop
== DESKTOP_ALL
) &&
2454 /* do a check to see if the window has already been unmapped or destroyed
2455 do this intelligently while watching out for unmaps we've generated
2456 (ignore_unmaps > 0) */
2457 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2458 DestroyNotify
, &ev
)) {
2459 XPutBackEvent(ob_display
, &ev
);
2462 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2463 UnmapNotify
, &ev
)) {
2464 if (self
->ignore_unmaps
) {
2465 self
->ignore_unmaps
--;
2467 XPutBackEvent(ob_display
, &ev
);
2475 gboolean
client_focus(ObClient
*self
)
2477 /* choose the correct target */
2478 self
= client_focus_target(self
);
2480 if (!client_can_focus(self
)) {
2481 if (!self
->frame
->visible
) {
2482 /* update the focus lists */
2483 focus_order_to_top(self
);
2488 if (self
->can_focus
)
2489 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2490 I choose to use it always, hopefully to find errors quicker, if any
2491 are left. (I hate X. I hate focus events.) */
2492 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
2495 if (self
->focus_notify
) {
2497 ce
.xclient
.type
= ClientMessage
;
2498 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2499 ce
.xclient
.display
= ob_display
;
2500 ce
.xclient
.window
= self
->window
;
2501 ce
.xclient
.format
= 32;
2502 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2503 ce
.xclient
.data
.l
[1] = event_lasttime
;
2504 ce
.xclient
.data
.l
[2] = 0l;
2505 ce
.xclient
.data
.l
[3] = 0l;
2506 ce
.xclient
.data
.l
[4] = 0l;
2507 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2511 ob_debug("%sively focusing %lx at %d\n",
2512 (self
->can_focus
? "act" : "pass"),
2513 self
->window
, (int) event_lasttime
);
2516 /* Cause the FocusIn to come back to us. Important for desktop switches,
2517 since otherwise we'll have no FocusIn on the queue and send it off to
2518 the focus_backup. */
2519 XSync(ob_display
, FALSE
);
2523 void client_unfocus(ObClient
*self
)
2525 g_assert(focus_client
== self
);
2527 ob_debug("client_unfocus for %lx\n", self
->window
);
2529 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING
);
2532 void client_activate(ObClient
*self
)
2534 if (client_normal(self
) && screen_showing_desktop
)
2535 screen_show_desktop(FALSE
);
2537 client_iconify(self
, FALSE
, FALSE
);
2538 if (self
->desktop
!= DESKTOP_ALL
&&
2539 self
->desktop
!= screen_desktop
)
2540 screen_set_desktop(self
->desktop
);
2541 else if (!self
->frame
->visible
)
2542 /* if its not visible for other reasons, then don't mess
2546 client_shade(self
, FALSE
);
2548 stacking_raise(CLIENT_AS_WINDOW(self
));
2551 gboolean
client_focused(ObClient
*self
)
2553 return self
== focus_client
;
2556 ObClientIcon
*client_icon(ObClient
*self
, int w
, int h
)
2559 /* si is the smallest image >= req */
2560 /* li is the largest image < req */
2561 unsigned long size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2563 if (!self
->nicons
) return NULL
;
2565 for (i
= 0; i
< self
->nicons
; ++i
) {
2566 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2567 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2571 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2576 if (largest
== 0) /* didnt find one smaller than the requested size */
2577 return &self
->icons
[si
];
2578 return &self
->icons
[li
];
2581 /* this be mostly ripped from fvwm */
2582 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
2584 int my_cx
, my_cy
, his_cx
, his_cy
;
2587 int score
, best_score
;
2588 ObClient
*best_client
, *cur
;
2594 /* first, find the centre coords of the currently focused window */
2595 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
2596 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
2601 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
2604 /* the currently selected window isn't interesting */
2607 if (!client_normal(cur
))
2609 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2613 if(client_focus_target(cur
) == cur
&&
2614 !(cur
->can_focus
|| cur
->focus_notify
))
2617 /* find the centre coords of this window, from the
2618 * currently focused window's point of view */
2619 his_cx
= (cur
->frame
->area
.x
- my_cx
)
2620 + cur
->frame
->area
.width
/ 2;
2621 his_cy
= (cur
->frame
->area
.y
- my_cy
)
2622 + cur
->frame
->area
.height
/ 2;
2626 /* Rotate the diagonals 45 degrees counterclockwise.
2627 * To do this, multiply the matrix /+h +h\ with the
2628 * vector (x y). \-h +h/
2629 * h = sqrt(0.5). We can set h := 1 since absolute
2630 * distance doesn't matter here. */
2631 tx
= his_cx
+ his_cy
;
2632 his_cy
= -his_cx
+ his_cy
;
2637 case OB_DIRECTION_NORTH
:
2638 case OB_DIRECTION_SOUTH
:
2639 case OB_DIRECTION_NORTHEAST
:
2640 case OB_DIRECTION_SOUTHWEST
:
2641 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
2642 distance
= ((dir
== OB_DIRECTION_NORTH
||
2643 dir
== OB_DIRECTION_NORTHEAST
) ?
2646 case OB_DIRECTION_EAST
:
2647 case OB_DIRECTION_WEST
:
2648 case OB_DIRECTION_SOUTHEAST
:
2649 case OB_DIRECTION_NORTHWEST
:
2650 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
2651 distance
= ((dir
== OB_DIRECTION_WEST
||
2652 dir
== OB_DIRECTION_NORTHWEST
) ?
2657 /* the target must be in the requested direction */
2661 /* Calculate score for this window. The smaller the better. */
2662 score
= distance
+ offset
;
2664 /* windows more than 45 degrees off the direction are
2665 * heavily penalized and will only be chosen if nothing
2666 * else within a million pixels */
2667 if(offset
> distance
)
2670 if(best_score
== -1 || score
< best_score
)
2678 void client_set_layer(ObClient
*self
, int layer
)
2682 self
->above
= FALSE
;
2683 } else if (layer
== 0) {
2684 self
->below
= self
->above
= FALSE
;
2686 self
->below
= FALSE
;
2689 client_calc_layer(self
);
2690 client_change_state(self
); /* reflect this in the state hints */
2693 guint
client_monitor(ObClient
*self
)
2697 for (i
= 0; i
< screen_num_monitors
; ++i
) {
2698 Rect
*area
= screen_physical_area_monitor(i
);
2699 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
))
2702 if (i
== screen_num_monitors
) i
= 0;
2703 g_assert(i
< screen_num_monitors
);
2707 ObClient
*client_search_top_transient(ObClient
*self
)
2709 /* move up the transient chain as far as possible */
2710 if (self
->transient_for
) {
2711 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2712 return client_search_top_transient(self
->transient_for
);
2716 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2717 ObClient
*c
= it
->data
;
2719 /* checking transient_for prevents infinate loops! */
2720 if (c
!= self
&& !c
->transient_for
)
2731 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
2735 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
2736 if (sit
->data
== search
)
2738 if (client_search_transient(sit
->data
, search
))
2744 gchar
* client_get_sm_client_id(ObClient
*self
)
2748 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &id
) && self
->group
)
2749 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
, &id
);