]> Dogcows Code - chaz/openbox/blob - openbox/client.c
make undecorated windows place according to their undecorated state (bug #5179)
[chaz/openbox] / openbox / client.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "client.h"
21 #include "debug.h"
22 #include "startupnotify.h"
23 #include "dock.h"
24 #include "screen.h"
25 #include "moveresize.h"
26 #include "ping.h"
27 #include "place.h"
28 #include "frame.h"
29 #include "session.h"
30 #include "event.h"
31 #include "grab.h"
32 #include "prompt.h"
33 #include "focus.h"
34 #include "focus_cycle.h"
35 #include "stacking.h"
36 #include "openbox.h"
37 #include "group.h"
38 #include "config.h"
39 #include "menuframe.h"
40 #include "keyboard.h"
41 #include "mouse.h"
42 #include "obrender/render.h"
43 #include "gettext.h"
44 #include "obt/display.h"
45 #include "obt/xqueue.h"
46 #include "obt/prop.h"
47
48 #ifdef HAVE_UNISTD_H
49 # include <unistd.h>
50 #endif
51
52 #ifdef HAVE_SIGNAL_H
53 # include <signal.h> /* for kill() */
54 #endif
55
56 #include <glib.h>
57 #include <X11/Xutil.h>
58
59 /*! The event mask to grab on client windows */
60 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
61 ColormapChangeMask)
62
63 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
64 ButtonMotionMask)
65
66 typedef struct
67 {
68 ObClientCallback func;
69 gpointer data;
70 } ClientCallback;
71
72 GList *client_list = NULL;
73
74 static GSList *client_destroy_notifies = NULL;
75 static RrImage *client_default_icon = NULL;
76
77 static void client_get_all(ObClient *self, gboolean real);
78 static void client_get_startup_id(ObClient *self);
79 static void client_get_session_ids(ObClient *self);
80 static void client_save_app_rule_values(ObClient *self);
81 static void client_get_area(ObClient *self);
82 static void client_get_desktop(ObClient *self);
83 static void client_get_state(ObClient *self);
84 static void client_get_shaped(ObClient *self);
85 static void client_get_colormap(ObClient *self);
86 static void client_set_desktop_recursive(ObClient *self,
87 guint target,
88 gboolean donthide,
89 gboolean dontraise);
90 static void client_change_allowed_actions(ObClient *self);
91 static void client_change_state(ObClient *self);
92 static void client_change_wm_state(ObClient *self);
93 static void client_apply_startup_state(ObClient *self,
94 gint x, gint y, gint w, gint h);
95 static void client_restore_session_state(ObClient *self);
96 static gboolean client_restore_session_stacking(ObClient *self);
97 static ObAppSettings *client_get_settings_state(ObClient *self);
98 static void client_update_transient_tree(ObClient *self,
99 ObGroup *oldgroup, ObGroup *newgroup,
100 gboolean oldgtran, gboolean newgtran,
101 ObClient* oldparent,
102 ObClient *newparent);
103 static void client_present(ObClient *self, gboolean here, gboolean raise,
104 gboolean unshade);
105 static GSList *client_search_all_top_parents_internal(ObClient *self,
106 gboolean bylayer,
107 ObStackingLayer layer);
108 static void client_call_notifies(ObClient *self, GSList *list);
109 static void client_ping_event(ObClient *self, gboolean dead);
110 static void client_prompt_kill(ObClient *self);
111 static gboolean client_can_steal_focus(ObClient *self,
112 gboolean allow_other_desktop,
113 gboolean request_from_user,
114 Time steal_time, Time launch_time);
115 static void client_setup_default_decor_and_functions(ObClient *self);
116 static void client_setup_decor_undecorated(ObClient *self);
117
118 void client_startup(gboolean reconfig)
119 {
120 client_default_icon = RrImageNewFromData(
121 ob_rr_icons, ob_rr_theme->def_win_icon,
122 ob_rr_theme->def_win_icon_w, ob_rr_theme->def_win_icon_h);
123
124 if (reconfig) return;
125
126 client_set_list();
127 }
128
129 void client_shutdown(gboolean reconfig)
130 {
131 RrImageUnref(client_default_icon);
132 client_default_icon = NULL;
133
134 if (reconfig) return;
135 }
136
137 static void client_call_notifies(ObClient *self, GSList *list)
138 {
139 GSList *it;
140
141 for (it = list; it; it = g_slist_next(it)) {
142 ClientCallback *d = it->data;
143 d->func(self, d->data);
144 }
145 }
146
147 void client_add_destroy_notify(ObClientCallback func, gpointer data)
148 {
149 ClientCallback *d = g_slice_new(ClientCallback);
150 d->func = func;
151 d->data = data;
152 client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
153 }
154
155 void client_remove_destroy_notify(ObClientCallback func)
156 {
157 GSList *it;
158
159 for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
160 ClientCallback *d = it->data;
161 if (d->func == func) {
162 g_slice_free(ClientCallback, d);
163 client_destroy_notifies =
164 g_slist_delete_link(client_destroy_notifies, it);
165 break;
166 }
167 }
168 }
169
170 void client_set_list(void)
171 {
172 Window *windows, *win_it;
173 GList *it;
174 guint size = g_list_length(client_list);
175
176 /* create an array of the window ids */
177 if (size > 0) {
178 windows = g_new(Window, size);
179 win_it = windows;
180 for (it = client_list; it; it = g_list_next(it), ++win_it)
181 *win_it = ((ObClient*)it->data)->window;
182 } else
183 windows = NULL;
184
185 OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW,
186 (gulong*)windows, size);
187
188 if (windows)
189 g_free(windows);
190
191 stacking_set_list();
192 }
193
194 void client_manage(Window window, ObPrompt *prompt)
195 {
196 ObClient *self;
197 XSetWindowAttributes attrib_set;
198 gboolean try_activate = FALSE;
199 gboolean do_activate;
200 ObAppSettings *settings;
201 gboolean transient = FALSE;
202 Rect place;
203 Time launch_time;
204 guint32 user_time;
205 gboolean obplaced;
206
207 ob_debug("Managing window: 0x%lx", window);
208
209 /* choose the events we want to receive on the CLIENT window
210 (ObPrompt windows can request events too) */
211 attrib_set.event_mask = CLIENT_EVENTMASK |
212 (prompt ? prompt->event_mask : 0);
213 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
214 XChangeWindowAttributes(obt_display, window,
215 CWEventMask|CWDontPropagate, &attrib_set);
216
217 /* create the ObClient struct, and populate it from the hints on the
218 window */
219 self = g_slice_new0(ObClient);
220 self->obwin.type = OB_WINDOW_CLASS_CLIENT;
221 self->window = window;
222 self->prompt = prompt;
223 self->managed = TRUE;
224
225 /* non-zero defaults */
226 self->wmstate = WithdrawnState; /* make sure it gets updated first time */
227 self->gravity = NorthWestGravity;
228 self->desktop = screen_num_desktops; /* always an invalid value */
229
230 /* get all the stuff off the window */
231 client_get_all(self, TRUE);
232
233 ob_debug("Window type: %d", self->type);
234 ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
235 ob_debug("Window name: %s class: %s role: %s title: %s",
236 self->name, self->class, self->role, self->title);
237
238 /* per-app settings override stuff from client_get_all, and return the
239 settings for other uses too. the returned settings is a shallow copy,
240 that needs to be freed with g_free(). */
241 settings = client_get_settings_state(self);
242
243 /* the session should get the last say though */
244 client_restore_session_state(self);
245
246 /* the per-app settings/session may have changed the decorations for
247 the window, so we setup decorations for that here. this is a special
248 case because we want to place the window according to these decoration
249 changes.
250 we do this before setting up the frame so that it will reflect the
251 decorations of the window as it will be placed on screen.
252 */
253 client_setup_decor_undecorated(self);
254
255 /* specify that if we exit, the window should not be destroyed and
256 should be reparented back to root automatically, unless we are managing
257 an internal ObPrompt window */
258 if (!self->prompt)
259 XChangeSaveSet(obt_display, window, SetModeInsert);
260
261 /* create the decoration frame for the client window */
262 self->frame = frame_new(self);
263
264 frame_grab_client(self->frame);
265
266 /* we've grabbed everything and set everything that we need to at mapping
267 time now */
268 grab_server(FALSE);
269
270 /* this needs to occur once we have a frame, since it sets a property on
271 the frame */
272 client_update_opacity(self);
273
274 /* don't put helper/modal windows on a different desktop if they are
275 related to the focused window. */
276 if (!screen_compare_desktops(self->desktop, screen_desktop) &&
277 focus_client && client_search_transient(focus_client, self) &&
278 (client_helper(self) || self->modal))
279 {
280 self->desktop = screen_desktop;
281 }
282
283 /* tell startup notification that this app started */
284 launch_time = sn_app_started(self->startup_id, self->class, self->name);
285
286 if (!OBT_PROP_GET32(self->window, NET_WM_USER_TIME, CARDINAL, &user_time))
287 user_time = event_time();
288
289 /* do this after we have a frame.. it uses the frame to help determine the
290 WM_STATE to apply. */
291 client_change_state(self);
292
293 /* add ourselves to the focus order */
294 focus_order_add_new(self);
295
296 /* do this to add ourselves to the stacking list in a non-intrusive way */
297 client_calc_layer(self);
298
299 /* focus the new window? */
300 if (ob_state() != OB_STATE_STARTING &&
301 (!self->session || self->session->focused) &&
302 /* this means focus=true for window is same as config_focus_new=true */
303 ((config_focus_new || settings->focus == 1) ||
304 client_search_focus_tree_full(self)) &&
305 /* NET_WM_USER_TIME 0 when mapping means don't focus */
306 (user_time != 0) &&
307 /* this checks for focus=false for the window */
308 settings->focus != 0 &&
309 focus_valid_target(self, self->desktop,
310 FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,
311 settings->focus == 1))
312 {
313 try_activate = TRUE;
314 }
315
316 /* remove the client's border */
317 XSetWindowBorderWidth(obt_display, self->window, 0);
318
319 /* adjust the frame to the client's size before showing or placing
320 the window */
321 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
322 frame_adjust_client_area(self->frame);
323
324 /* where the frame was placed is where the window was originally */
325 place = self->area;
326
327 ob_debug("Going to try activate new window? %s",
328 try_activate ? "yes" : "no");
329 if (try_activate)
330 do_activate = client_can_steal_focus(
331 self, settings->focus == 1,
332 (!!launch_time || settings->focus == 1),
333 event_time(), launch_time);
334 else
335 do_activate = FALSE;
336
337 /* figure out placement for the window if the window is new */
338 if (ob_state() == OB_STATE_RUNNING) {
339 ob_debug("Positioned: %s @ %d %d",
340 (!self->positioned ? "no" :
341 (self->positioned == PPosition ? "program specified" :
342 (self->positioned == USPosition ? "user specified" :
343 (self->positioned == (PPosition | USPosition) ?
344 "program + user specified" :
345 "BADNESS !?")))), place.x, place.y);
346
347 ob_debug("Sized: %s @ %d %d",
348 (!self->sized ? "no" :
349 (self->sized == PSize ? "program specified" :
350 (self->sized == USSize ? "user specified" :
351 (self->sized == (PSize | USSize) ?
352 "program + user specified" :
353 "BADNESS !?")))), place.width, place.height);
354
355 obplaced = place_client(self, do_activate, &place.x, &place.y,
356 settings);
357
358 /* watch for buggy apps that ask to be placed at (0,0) when there is
359 a strut there */
360 if (!obplaced && place.x == 0 && place.y == 0 &&
361 /* non-normal windows are allowed */
362 client_normal(self) &&
363 /* oldschool fullscreen windows are allowed */
364 !client_is_oldfullscreen(self, &place))
365 {
366 Rect *r;
367
368 r = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, NULL);
369 if (r->x || r->y) {
370 place.x = r->x;
371 place.y = r->y;
372 ob_debug("Moving buggy app from (0,0) to (%d,%d)", r->x, r->y);
373 }
374 g_slice_free(Rect, r);
375 }
376
377 /* make sure the window is visible. */
378 client_find_onscreen(self, &place.x, &place.y,
379 place.width, place.height,
380 /* non-normal clients has less rules, and
381 windows that are being restored from a
382 session do also. we can assume you want
383 it back where you saved it. Clients saying
384 they placed themselves are subjected to
385 harder rules, ones that are placed by
386 place.c or by the user are allowed partially
387 off-screen and on xinerama divides (ie,
388 it is up to the placement routines to avoid
389 the xinerama divides)
390
391 children and splash screens are forced on
392 screen, but i don't remember why i decided to
393 do that.
394 */
395 ob_state() == OB_STATE_RUNNING &&
396 (self->type == OB_CLIENT_TYPE_DIALOG ||
397 self->type == OB_CLIENT_TYPE_SPLASH ||
398 (!((self->positioned & USPosition) ||
399 settings->pos_given) &&
400 client_normal(self) &&
401 !self->session &&
402 /* don't move oldschool fullscreen windows to
403 fit inside the struts (fixes Acroread, which
404 makes its fullscreen window fit the screen
405 but it is not USSize'd or USPosition'd) */
406 !client_is_oldfullscreen(self, &place))));
407 }
408
409 /* if the window isn't user-sized, then make it fit inside
410 the visible screen area on its monitor. Use basically the same rules
411 for forcing the window on screen in the client_find_onscreen call.
412
413 do this after place_client, it chooses the monitor!
414
415 splash screens get "transient" set to TRUE by
416 the place_client call
417 */
418 if (ob_state() == OB_STATE_RUNNING &&
419 (transient ||
420 (!(self->sized & USSize || self->positioned & USPosition) &&
421 client_normal(self) &&
422 !self->session &&
423 /* don't shrink oldschool fullscreen windows to fit inside the
424 struts (fixes Acroread, which makes its fullscreen window
425 fit the screen but it is not USSize'd or USPosition'd) */
426 !client_is_oldfullscreen(self, &place))))
427 {
428 Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
429
430 /* get the size of the frame */
431 place.width += self->frame->size.left + self->frame->size.right;
432 place.height += self->frame->size.top + self->frame->size.bottom;
433
434 /* fit the window inside the area */
435 place.width = MIN(place.width, a->width);
436 place.height = MIN(place.height, a->height);
437
438 ob_debug("setting window size to %dx%d", place.width, place.height);
439
440 /* get the size of the client back */
441 place.width -= self->frame->size.left + self->frame->size.right;
442 place.height -= self->frame->size.top + self->frame->size.bottom;
443
444 g_slice_free(Rect, a);
445 }
446
447 ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
448 "some restrictions may apply",
449 self->window, place.x, place.y, place.width, place.height);
450 if (self->session)
451 ob_debug(" but session requested %d, %d %d x %d instead, "
452 "overriding",
453 self->session->x, self->session->y,
454 self->session->w, self->session->h);
455
456 /* do this after the window is placed, so the premax/prefullscreen numbers
457 won't be all wacko!!
458
459 this also places the window
460 */
461 client_apply_startup_state(self, place.x, place.y,
462 place.width, place.height);
463
464 /* set the initial value of the desktop hint, when one wasn't requested
465 on map. */
466 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
467
468 /* grab mouse bindings before showing the window */
469 mouse_grab_for_client(self, TRUE);
470
471 /* this has to happen before we try focus the window, but we want it to
472 happen after the client's stacking has been determined or it looks bad
473 */
474 {
475 gulong ignore_start;
476 if (!config_focus_under_mouse)
477 ignore_start = event_start_ignore_all_enters();
478
479 client_show(self);
480
481 if (!config_focus_under_mouse)
482 event_end_ignore_all_enters(ignore_start);
483 }
484
485 /* activate/hilight/raise the window */
486 if (try_activate) {
487 if (do_activate) {
488 gboolean stacked = client_restore_session_stacking(self);
489 client_present(self, FALSE, !stacked, TRUE);
490 }
491 else {
492 /* if the client isn't stealing focus, then hilite it so the user
493 knows it is there, but don't do this if we're restoring from a
494 session */
495 if (!client_restore_session_stacking(self))
496 client_hilite(self, TRUE);
497 }
498 }
499 else {
500 /* This may look rather odd. Well it's because new windows are added
501 to the stacking order non-intrusively. If we're not going to focus
502 the new window or hilite it, then we raise it to the top. This will
503 take affect for things that don't get focused like splash screens.
504 Also if you don't have focus_new enabled, then it's going to get
505 raised to the top. Legacy begets legacy I guess?
506 */
507 if (!client_restore_session_stacking(self))
508 stacking_raise(CLIENT_AS_WINDOW(self));
509 }
510
511 /* add to client list/map */
512 client_list = g_list_append(client_list, self);
513 window_add(&self->window, CLIENT_AS_WINDOW(self));
514
515 /* this has to happen after we're in the client_list */
516 if (STRUT_EXISTS(self->strut))
517 screen_update_areas();
518
519 /* update the list hints */
520 client_set_list();
521
522 /* free the ObAppSettings shallow copy */
523 g_slice_free(ObAppSettings, settings);
524
525 ob_debug("Managed window 0x%lx plate 0x%x (%s)",
526 window, self->frame->window, self->class);
527 }
528
529 ObClient *client_fake_manage(Window window)
530 {
531 ObClient *self;
532 ObAppSettings *settings;
533
534 ob_debug("Pretend-managing window: %lx", window);
535
536 /* do this minimal stuff to figure out the client's decorations */
537
538 self = g_slice_new0(ObClient);
539 self->window = window;
540
541 client_get_all(self, FALSE);
542 /* per-app settings override stuff, and return the settings for other
543 uses too. this returns a shallow copy that needs to be freed */
544 settings = client_get_settings_state(self);
545
546 /* create the decoration frame for the client window and adjust its size */
547 self->frame = frame_new(self);
548
549 client_apply_startup_state(self, self->area.x, self->area.y,
550 self->area.width, self->area.height);
551
552 ob_debug("gave extents left %d right %d top %d bottom %d",
553 self->frame->size.left, self->frame->size.right,
554 self->frame->size.top, self->frame->size.bottom);
555
556 /* free the ObAppSettings shallow copy */
557 g_slice_free(ObAppSettings, settings);
558
559 return self;
560 }
561
562 void client_unmanage_all(void)
563 {
564 while (client_list)
565 client_unmanage(client_list->data);
566 }
567
568 void client_unmanage(ObClient *self)
569 {
570 GSList *it;
571 gulong ignore_start;
572
573 ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
574 self->window, self->frame->window,
575 self->class, self->title ? self->title : "");
576
577 g_assert(self != NULL);
578
579 /* we dont want events no more. do this before hiding the frame so we
580 don't generate more events */
581 XSelectInput(obt_display, self->window, NoEventMask);
582
583 /* ignore enter events from the unmap so it doesnt mess with the focus */
584 if (!config_focus_under_mouse)
585 ignore_start = event_start_ignore_all_enters();
586
587 frame_hide(self->frame);
588 /* flush to send the hide to the server quickly */
589 XFlush(obt_display);
590
591 if (!config_focus_under_mouse)
592 event_end_ignore_all_enters(ignore_start);
593
594 mouse_grab_for_client(self, FALSE);
595
596 self->managed = FALSE;
597
598 /* remove the window from our save set, unless we are managing an internal
599 ObPrompt window */
600 if (!self->prompt)
601 XChangeSaveSet(obt_display, self->window, SetModeDelete);
602
603 /* update the focus lists */
604 focus_order_remove(self);
605 if (client_focused(self)) {
606 /* don't leave an invalid focus_client */
607 focus_client = NULL;
608 }
609
610 /* if we're prompting to kill the client, close that */
611 prompt_unref(self->kill_prompt);
612 self->kill_prompt = NULL;
613
614 client_list = g_list_remove(client_list, self);
615 stacking_remove(self);
616 window_remove(self->window);
617
618 /* once the client is out of the list, update the struts to remove its
619 influence */
620 if (STRUT_EXISTS(self->strut))
621 screen_update_areas();
622
623 client_call_notifies(self, client_destroy_notifies);
624
625 /* tell our parent(s) that we're gone */
626 for (it = self->parents; it; it = g_slist_next(it))
627 ((ObClient*)it->data)->transients =
628 g_slist_remove(((ObClient*)it->data)->transients,self);
629
630 /* tell our transients that we're gone */
631 for (it = self->transients; it; it = g_slist_next(it)) {
632 ((ObClient*)it->data)->parents =
633 g_slist_remove(((ObClient*)it->data)->parents, self);
634 /* we could be keeping our children in a higher layer */
635 client_calc_layer(it->data);
636 }
637
638 /* remove from its group */
639 if (self->group) {
640 group_remove(self->group, self);
641 self->group = NULL;
642 }
643
644 /* restore the window's original geometry so it is not lost */
645 {
646 Rect a;
647
648 a = self->area;
649
650 if (self->fullscreen)
651 a = self->pre_fullscreen_area;
652 else if (self->max_horz || self->max_vert) {
653 if (self->max_horz) {
654 a.x = self->pre_max_area.x;
655 a.width = self->pre_max_area.width;
656 }
657 if (self->max_vert) {
658 a.y = self->pre_max_area.y;
659 a.height = self->pre_max_area.height;
660 }
661 }
662
663 self->fullscreen = self->max_horz = self->max_vert = FALSE;
664 /* let it be moved and resized no matter what */
665 self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
666 self->decorations = 0; /* unmanaged windows have no decor */
667
668 /* give the client its border back */
669 XSetWindowBorderWidth(obt_display, self->window, self->border_width);
670
671 client_move_resize(self, a.x, a.y, a.width, a.height);
672 }
673
674 /* reparent the window out of the frame, and free the frame */
675 frame_release_client(self->frame);
676 frame_free(self->frame);
677 self->frame = NULL;
678
679 if (ob_state() != OB_STATE_EXITING) {
680 /* these values should not be persisted across a window
681 unmapping/mapping */
682 OBT_PROP_ERASE(self->window, NET_WM_DESKTOP);
683 OBT_PROP_ERASE(self->window, NET_WM_STATE);
684 OBT_PROP_ERASE(self->window, WM_STATE);
685 } else {
686 /* if we're left in an unmapped state, the client wont be mapped.
687 this is bad, since we will no longer be managing the window on
688 restart */
689 XMapWindow(obt_display, self->window);
690 }
691
692 /* these should not be left on the window ever. other window managers
693 don't necessarily use them and it will mess them up (like compiz) */
694 OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_NAME);
695 OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_ICON_NAME);
696
697 /* update the list hints */
698 client_set_list();
699
700 ob_debug("Unmanaged window 0x%lx", self->window);
701
702 /* free all data allocated in the client struct */
703 RrImageUnref(self->icon_set);
704 g_slist_free(self->transients);
705 g_free(self->startup_id);
706 g_free(self->wm_command);
707 g_free(self->title);
708 g_free(self->icon_title);
709 g_free(self->original_title);
710 g_free(self->name);
711 g_free(self->class);
712 g_free(self->role);
713 g_free(self->client_machine);
714 g_free(self->sm_client_id);
715 g_slice_free(ObClient, self);
716 }
717
718 void client_fake_unmanage(ObClient *self)
719 {
720 /* this is all that got allocated to get the decorations */
721
722 frame_free(self->frame);
723 g_slice_free(ObClient, self);
724 }
725
726 static gboolean client_can_steal_focus(ObClient *self,
727 gboolean allow_other_desktop,
728 gboolean request_from_user,
729 Time steal_time,
730 Time launch_time)
731 {
732 gboolean steal;
733 gboolean relative_focused;
734
735 steal = TRUE;
736
737 relative_focused = (focus_client != NULL &&
738 (client_search_focus_tree_full(self) != NULL ||
739 client_search_focus_group_full(self) != NULL));
740
741 /* This is focus stealing prevention */
742 ob_debug("Want to focus window 0x%x at time %u "
743 "launched at %u (last user interaction time %u) "
744 "request from %s, allow other desktop: %s, "
745 "desktop switch time %u",
746 self->window, steal_time, launch_time,
747 event_last_user_time,
748 (request_from_user ? "user" : "other"),
749 (allow_other_desktop ? "yes" : "no"),
750 screen_desktop_user_time);
751
752 /*
753 if no launch time is provided for an application, make one up.
754
755 if the window is related to other existing windows
756 and one of those windows was the last used
757 then we will give it a launch time equal to the last user time,
758 which will end up giving the window focus probably.
759 else
760 the window is related to other windows, but you are not working in
761 them?
762 seems suspicious, so we will give it a launch time of
763 NOW - STEAL_INTERVAL,
764 so it will be given focus only if we didn't use something else
765 during the steal interval.
766 else
767 the window is all on its own, so we can't judge it. give it a launch
768 time equal to the last user time, so it will probably take focus.
769
770 this way running things from a terminal will give them focus, but popups
771 without a launch time shouldn't steal focus so easily.
772 */
773
774 if (!launch_time) {
775 if (client_has_relative(self)) {
776 if (event_last_user_time && client_search_focus_group_full(self)) {
777 /* our relative is focused */
778 launch_time = event_last_user_time;
779 ob_debug("Unknown launch time, using %u - window in active "
780 "group", launch_time);
781 }
782 else if (!request_from_user) {
783 /* has relatives which are not being used. suspicious */
784 launch_time = event_time() - OB_EVENT_USER_TIME_DELAY;
785 ob_debug("Unknown launch time, using %u - window in inactive "
786 "group", launch_time);
787 }
788 else {
789 /* has relatives which are not being used, but the user seems
790 to want to go there! */
791 launch_time = event_last_user_time;
792 ob_debug("Unknown launch time, using %u - user request",
793 launch_time);
794 }
795 }
796 else {
797 /* the window is on its own, probably the user knows it is going
798 to appear */
799 launch_time = event_last_user_time;
800 ob_debug("Unknown launch time, using %u - independent window",
801 launch_time);
802 }
803 }
804
805 /* if it's on another desktop
806 and if allow_other_desktop is true, we generally let it steal focus.
807 but if it didn't come from the user, don't let it steal unless it was
808 launched before the user switched desktops.
809 focus, unless it was launched after we changed desktops and the request
810 came from the user
811 */
812 if (!screen_compare_desktops(screen_desktop, self->desktop)) {
813 /* must be allowed */
814 if (!allow_other_desktop) {
815 steal = FALSE;
816 ob_debug("Not focusing the window because its on another desktop");
817 }
818 /* if we don't know when the desktop changed, but request is from an
819 application, don't let it change desktop on you */
820 else if (!request_from_user) {
821 steal = FALSE;
822 ob_debug("Not focusing the window because non-user request");
823 }
824 }
825 /* If something is focused... */
826 else if (focus_client) {
827 /* If the user is working in another window right now, then don't
828 steal focus */
829 if (!relative_focused &&
830 event_last_user_time &&
831 /* last user time must be strictly > launch_time to block focus */
832 (event_time_after(event_last_user_time, launch_time) &&
833 event_last_user_time != launch_time) &&
834 event_time_after(event_last_user_time,
835 steal_time - OB_EVENT_USER_TIME_DELAY))
836 {
837 steal = FALSE;
838 ob_debug("Not focusing the window because the user is "
839 "working in another window that is not its relative");
840 }
841 /* Don't move focus if it's not going to go to this window
842 anyway */
843 else if (client_focus_target(self) != self) {
844 steal = FALSE;
845 ob_debug("Not focusing the window because another window "
846 "would get the focus anyway");
847 }
848 /* For requests that don't come from the user */
849 else if (!request_from_user) {
850 /* If the new window is a transient (and its relatives aren't
851 focused) */
852 if (client_has_parent(self) && !relative_focused) {
853 steal = FALSE;
854 ob_debug("Not focusing the window because it is a "
855 "transient, and its relatives aren't focused");
856 }
857 /* Don't steal focus from globally active clients.
858 I stole this idea from KWin. It seems nice.
859 */
860 else if (!(focus_client->can_focus || focus_client->focus_notify))
861 {
862 steal = FALSE;
863 ob_debug("Not focusing the window because a globally "
864 "active client has focus");
865 }
866 /* Don't move focus if the window is not visible on the current
867 desktop and none of its relatives are focused */
868 else if (!allow_other_desktop &&
869 !screen_compare_desktops(self->desktop, screen_desktop) &&
870 !relative_focused)
871 {
872 steal = FALSE;
873 ob_debug("Not focusing the window because it is on "
874 "another desktop and no relatives are focused ");
875 }
876 }
877 }
878
879 if (!steal)
880 ob_debug("Focus stealing prevention activated for %s at "
881 "time %u (last user interaction time %u)",
882 self->title, steal_time, event_last_user_time);
883 else
884 ob_debug("Allowing focus stealing for %s at time %u (last user "
885 "interaction time %u)",
886 self->title, steal_time, event_last_user_time);
887 return steal;
888 }
889
890 /*! Returns a new structure containing the per-app settings for this client.
891 The returned structure needs to be freed with g_free. */
892 static ObAppSettings *client_get_settings_state(ObClient *self)
893 {
894 ObAppSettings *settings;
895 GSList *it;
896
897 settings = config_create_app_settings();
898
899 for (it = config_per_app_settings; it; it = g_slist_next(it)) {
900 ObAppSettings *app = it->data;
901 gboolean match = TRUE;
902
903 g_assert(app->name != NULL || app->class != NULL ||
904 app->role != NULL || app->title != NULL ||
905 (signed)app->type >= 0);
906
907 if (app->name &&
908 !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
909 match = FALSE;
910 else if (app->class &&
911 !g_pattern_match(app->class,
912 strlen(self->class), self->class, NULL))
913 match = FALSE;
914 else if (app->role &&
915 !g_pattern_match(app->role,
916 strlen(self->role), self->role, NULL))
917 match = FALSE;
918 else if (app->title &&
919 !g_pattern_match(app->title,
920 strlen(self->title), self->title, NULL))
921 match = FALSE;
922 else if ((signed)app->type >= 0 && app->type != self->type) {
923 match = FALSE;
924 }
925
926 if (match) {
927 ob_debug("Window matching: %s", app->name);
928
929 /* copy the settings to our struct, overriding the existing
930 settings if they are not defaults */
931 config_app_settings_copy_non_defaults(app, settings);
932 }
933 }
934
935 if (settings->shade != -1)
936 self->shaded = !!settings->shade;
937 if (settings->decor != -1)
938 self->undecorated = !settings->decor;
939 if (settings->iconic != -1)
940 self->iconic = !!settings->iconic;
941 if (settings->skip_pager != -1)
942 self->skip_pager = !!settings->skip_pager;
943 if (settings->skip_taskbar != -1)
944 self->skip_taskbar = !!settings->skip_taskbar;
945
946 if (settings->max_vert != -1)
947 self->max_vert = !!settings->max_vert;
948 if (settings->max_horz != -1)
949 self->max_horz = !!settings->max_horz;
950
951 if (settings->fullscreen != -1)
952 self->fullscreen = !!settings->fullscreen;
953
954 if (settings->desktop) {
955 if (settings->desktop == DESKTOP_ALL)
956 self->desktop = settings->desktop;
957 else if (settings->desktop > 0 &&
958 settings->desktop <= screen_num_desktops)
959 self->desktop = settings->desktop - 1;
960 }
961
962 if (settings->layer == -1) {
963 self->below = TRUE;
964 self->above = FALSE;
965 }
966 else if (settings->layer == 0) {
967 self->below = FALSE;
968 self->above = FALSE;
969 }
970 else if (settings->layer == 1) {
971 self->below = FALSE;
972 self->above = TRUE;
973 }
974 return settings;
975 }
976
977 static void client_restore_session_state(ObClient *self)
978 {
979 GList *it;
980
981 ob_debug_type(OB_DEBUG_SM,
982 "Restore session for client %s", self->title);
983
984 if (!(it = session_state_find(self))) {
985 ob_debug_type(OB_DEBUG_SM,
986 "Session data not found for client %s", self->title);
987 return;
988 }
989
990 self->session = it->data;
991
992 ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s",
993 self->title);
994
995 RECT_SET_POINT(self->area, self->session->x, self->session->y);
996 self->positioned = USPosition;
997 self->sized = USSize;
998 if (self->session->w > 0)
999 self->area.width = self->session->w;
1000 if (self->session->h > 0)
1001 self->area.height = self->session->h;
1002 XResizeWindow(obt_display, self->window,
1003 self->area.width, self->area.height);
1004
1005 self->desktop = (self->session->desktop == DESKTOP_ALL ?
1006 self->session->desktop :
1007 MIN(screen_num_desktops - 1, self->session->desktop));
1008 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
1009
1010 self->shaded = self->session->shaded;
1011 self->iconic = self->session->iconic;
1012 self->skip_pager = self->session->skip_pager;
1013 self->skip_taskbar = self->session->skip_taskbar;
1014 self->fullscreen = self->session->fullscreen;
1015 self->above = self->session->above;
1016 self->below = self->session->below;
1017 self->max_horz = self->session->max_horz;
1018 self->max_vert = self->session->max_vert;
1019 self->undecorated = self->session->undecorated;
1020 }
1021
1022 static gboolean client_restore_session_stacking(ObClient *self)
1023 {
1024 GList *it, *mypos;
1025
1026 if (!self->session) return FALSE;
1027
1028 mypos = g_list_find(session_saved_state, self->session);
1029 if (!mypos) return FALSE;
1030
1031 /* start above me and look for the first client */
1032 for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
1033 GList *cit;
1034
1035 for (cit = client_list; cit; cit = g_list_next(cit)) {
1036 ObClient *c = cit->data;
1037 /* found a client that was in the session, so go below it */
1038 if (c->session == it->data) {
1039 stacking_below(CLIENT_AS_WINDOW(self),
1040 CLIENT_AS_WINDOW(cit->data));
1041 return TRUE;
1042 }
1043 }
1044 }
1045 return FALSE;
1046 }
1047
1048 void client_move_onscreen(ObClient *self, gboolean rude)
1049 {
1050 gint x = self->area.x;
1051 gint y = self->area.y;
1052 if (client_find_onscreen(self, &x, &y,
1053 self->area.width,
1054 self->area.height, rude)) {
1055 client_move(self, x, y);
1056 }
1057 }
1058
1059 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
1060 gboolean rude)
1061 {
1062 gint ox = *x, oy = *y;
1063 gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
1064 gint fw, fh;
1065 Rect desired;
1066 guint i;
1067 gboolean found_mon;
1068
1069 RECT_SET(desired, *x, *y, w, h);
1070 frame_rect_to_frame(self->frame, &desired);
1071
1072 /* get where the frame would be */
1073 frame_client_gravity(self->frame, x, y);
1074
1075 /* get the requested size of the window with decorations */
1076 fw = self->frame->size.left + w + self->frame->size.right;
1077 fh = self->frame->size.top + h + self->frame->size.bottom;
1078
1079 /* If rudeness wasn't requested, then still be rude in a given direction
1080 if the client is not moving, only resizing in that direction */
1081 if (!rude) {
1082 Point oldtl, oldtr, oldbl, oldbr;
1083 Point newtl, newtr, newbl, newbr;
1084 gboolean stationary_l, stationary_r, stationary_t, stationary_b;
1085
1086 POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
1087 POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
1088 self->frame->area.y + self->frame->area.height - 1);
1089 POINT_SET(oldtr, oldbr.x, oldtl.y);
1090 POINT_SET(oldbl, oldtl.x, oldbr.y);
1091
1092 POINT_SET(newtl, *x, *y);
1093 POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
1094 POINT_SET(newtr, newbr.x, newtl.y);
1095 POINT_SET(newbl, newtl.x, newbr.y);
1096
1097 /* is it moving or just resizing from some corner? */
1098 stationary_l = oldtl.x == newtl.x;
1099 stationary_r = oldtr.x == newtr.x;
1100 stationary_t = oldtl.y == newtl.y;
1101 stationary_b = oldbl.y == newbl.y;
1102
1103 /* if left edge is growing and didnt move right edge */
1104 if (stationary_r && newtl.x < oldtl.x)
1105 rudel = TRUE;
1106 /* if right edge is growing and didnt move left edge */
1107 if (stationary_l && newtr.x > oldtr.x)
1108 ruder = TRUE;
1109 /* if top edge is growing and didnt move bottom edge */
1110 if (stationary_b && newtl.y < oldtl.y)
1111 rudet = TRUE;
1112 /* if bottom edge is growing and didnt move top edge */
1113 if (stationary_t && newbl.y > oldbl.y)
1114 rudeb = TRUE;
1115 }
1116
1117 /* we iterate through every monitor that the window is at least partially
1118 on, to make sure it is obeying the rules on them all
1119
1120 if the window does not appear on any monitors, then use the first one
1121 */
1122 found_mon = FALSE;
1123 for (i = 0; i < screen_num_monitors; ++i) {
1124 Rect *a;
1125
1126 if (!screen_physical_area_monitor_contains(i, &desired)) {
1127 if (i < screen_num_monitors - 1 || found_mon)
1128 continue;
1129
1130 /* the window is not inside any monitor! so just use the first
1131 one */
1132 a = screen_area(self->desktop, 0, NULL);
1133 } else {
1134 found_mon = TRUE;
1135 a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
1136 }
1137
1138 /* This makes sure windows aren't entirely outside of the screen so you
1139 can't see them at all.
1140 It makes sure 10% of the window is on the screen at least. And don't
1141 let it move itself off the top of the screen, which would hide the
1142 titlebar on you. (The user can still do this if they want too, it's
1143 only limiting the application.
1144 */
1145 if (client_normal(self)) {
1146 if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
1147 *x = a->x + a->width - fw/10;
1148 if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
1149 *y = a->y + a->height - fh/10;
1150 if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
1151 *x = a->x - fw*9/10;
1152 if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
1153 *y = a->y - fh*9/10;
1154 }
1155
1156 /* This here doesn't let windows even a pixel outside the
1157 struts/screen. When called from client_manage, programs placing
1158 themselves are forced completely onscreen, while things like
1159 xterm -geometry resolution-width/2 will work fine. Trying to
1160 place it completely offscreen will be handled in the above code.
1161 Sorry for this confused comment, i am tired. */
1162 if (rudel && !self->strut.left && *x < a->x) *x = a->x;
1163 if (ruder && !self->strut.right && *x + fw > a->x + a->width)
1164 *x = a->x + MAX(0, a->width - fw);
1165
1166 if (rudet && !self->strut.top && *y < a->y) *y = a->y;
1167 if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
1168 *y = a->y + MAX(0, a->height - fh);
1169
1170 g_slice_free(Rect, a);
1171 }
1172
1173 /* get where the client should be */
1174 frame_frame_gravity(self->frame, x, y);
1175
1176 return ox != *x || oy != *y;
1177 }
1178
1179 static void client_get_all(ObClient *self, gboolean real)
1180 {
1181 /* this is needed for the frame to set itself up */
1182 client_get_area(self);
1183
1184 /* these things can change the decor and functions of the window */
1185
1186 client_get_mwm_hints(self);
1187 /* this can change the mwmhints for special cases */
1188 client_get_type_and_transientness(self);
1189 client_update_normal_hints(self);
1190
1191 /* set up the maximum possible decor/functions */
1192 client_setup_default_decor_and_functions(self);
1193
1194 client_get_state(self);
1195
1196 /* get the session related properties, these can change decorations
1197 from per-app settings */
1198 client_get_session_ids(self);
1199
1200 /* now we got everything that can affect the decorations */
1201 if (!real)
1202 return;
1203
1204 /* get this early so we have it for debugging */
1205 client_update_title(self);
1206
1207 /* save the values of the variables used for app rule matching */
1208 client_save_app_rule_values(self);
1209
1210 client_update_protocols(self);
1211
1212 client_update_wmhints(self);
1213 /* this may have already been called from client_update_wmhints */
1214 if (!self->parents && !self->transient_for_group)
1215 client_update_transient_for(self);
1216
1217 client_get_startup_id(self);
1218 client_get_desktop(self);/* uses transient data/group/startup id if a
1219 desktop is not specified */
1220 client_get_shaped(self);
1221
1222 {
1223 /* a couple type-based defaults for new windows */
1224
1225 /* this makes sure that these windows appear on all desktops */
1226 if (self->type == OB_CLIENT_TYPE_DESKTOP)
1227 self->desktop = DESKTOP_ALL;
1228 }
1229
1230 #ifdef SYNC
1231 client_update_sync_request_counter(self);
1232 #endif
1233
1234 client_get_colormap(self);
1235 client_update_strut(self);
1236 client_update_icons(self);
1237 client_update_icon_geometry(self);
1238 }
1239
1240 static void client_get_startup_id(ObClient *self)
1241 {
1242 if (!(OBT_PROP_GETS_UTF8(self->window, NET_STARTUP_ID, &self->startup_id)))
1243 if (self->group)
1244 OBT_PROP_GETS_UTF8(self->group->leader, NET_STARTUP_ID,
1245 &self->startup_id);
1246 }
1247
1248 static void client_get_area(ObClient *self)
1249 {
1250 XWindowAttributes wattrib;
1251 Status ret;
1252
1253 ret = XGetWindowAttributes(obt_display, self->window, &wattrib);
1254 g_assert(ret != BadWindow);
1255
1256 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1257 POINT_SET(self->root_pos, wattrib.x, wattrib.y);
1258 self->border_width = wattrib.border_width;
1259
1260 ob_debug("client area: %d %d %d %d bw %d", wattrib.x, wattrib.y,
1261 wattrib.width, wattrib.height, wattrib.border_width);
1262 }
1263
1264 static void client_get_desktop(ObClient *self)
1265 {
1266 guint32 d = screen_num_desktops; /* an always-invalid value */
1267
1268 if (OBT_PROP_GET32(self->window, NET_WM_DESKTOP, CARDINAL, &d)) {
1269 if (d >= screen_num_desktops && d != DESKTOP_ALL)
1270 self->desktop = screen_num_desktops - 1;
1271 else
1272 self->desktop = d;
1273 ob_debug("client requested desktop 0x%x", self->desktop);
1274 } else {
1275 GSList *it;
1276 gboolean first = TRUE;
1277 guint all = screen_num_desktops; /* not a valid value */
1278
1279 /* if they are all on one desktop, then open it on the
1280 same desktop */
1281 for (it = self->parents; it; it = g_slist_next(it)) {
1282 ObClient *c = it->data;
1283
1284 if (c->desktop == DESKTOP_ALL) continue;
1285
1286 if (first) {
1287 all = c->desktop;
1288 first = FALSE;
1289 }
1290 else if (all != c->desktop)
1291 all = screen_num_desktops; /* make it invalid */
1292 }
1293 if (all != screen_num_desktops) {
1294 self->desktop = all;
1295
1296 ob_debug("client desktop set from parents: 0x%x",
1297 self->desktop);
1298 }
1299 /* try get from the startup-notification protocol */
1300 else if (sn_get_desktop(self->startup_id, &self->desktop)) {
1301 if (self->desktop >= screen_num_desktops &&
1302 self->desktop != DESKTOP_ALL)
1303 self->desktop = screen_num_desktops - 1;
1304 ob_debug("client desktop set from startup-notification: 0x%x",
1305 self->desktop);
1306 }
1307 /* defaults to the current desktop */
1308 else {
1309 self->desktop = screen_desktop;
1310 ob_debug("client desktop set to the current desktop: %d",
1311 self->desktop);
1312 }
1313 }
1314 }
1315
1316 static void client_get_state(ObClient *self)
1317 {
1318 guint32 *state;
1319 guint num;
1320
1321 if (OBT_PROP_GETA32(self->window, NET_WM_STATE, ATOM, &state, &num)) {
1322 gulong i;
1323 for (i = 0; i < num; ++i) {
1324 if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
1325 self->modal = TRUE;
1326 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
1327 self->shaded = TRUE;
1328 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
1329 self->iconic = TRUE;
1330 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
1331 self->skip_taskbar = TRUE;
1332 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
1333 self->skip_pager = TRUE;
1334 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
1335 self->fullscreen = TRUE;
1336 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
1337 self->max_vert = TRUE;
1338 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
1339 self->max_horz = TRUE;
1340 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
1341 self->above = TRUE;
1342 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
1343 self->below = TRUE;
1344 else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
1345 self->demands_attention = TRUE;
1346 else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
1347 self->undecorated = TRUE;
1348 }
1349
1350 g_free(state);
1351 }
1352 }
1353
1354 static void client_get_shaped(ObClient *self)
1355 {
1356 self->shaped = FALSE;
1357 #ifdef SHAPE
1358 if (obt_display_extension_shape) {
1359 gint foo;
1360 guint ufoo;
1361 gint s;
1362
1363 XShapeSelectInput(obt_display, self->window, ShapeNotifyMask);
1364
1365 XShapeQueryExtents(obt_display, self->window, &s, &foo,
1366 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1367 &ufoo);
1368 self->shaped = !!s;
1369 }
1370 #endif
1371 }
1372
1373 void client_update_transient_for(ObClient *self)
1374 {
1375 Window t = None;
1376 ObClient *target = NULL;
1377 gboolean trangroup = FALSE;
1378
1379 if (XGetTransientForHint(obt_display, self->window, &t)) {
1380 if (t != self->window) { /* can't be transient to itself! */
1381 ObWindow *tw = window_find(t);
1382 /* if this happens then we need to check for it */
1383 g_assert(tw != CLIENT_AS_WINDOW(self));
1384 if (tw && WINDOW_IS_CLIENT(tw)) {
1385 /* watch out for windows with a parent that is something
1386 different, like a dockapp for example */
1387 target = WINDOW_AS_CLIENT(tw);
1388 }
1389 }
1390
1391 /* Setting the transient_for to Root is actually illegal, however
1392 applications from time have done this to specify transient for
1393 their group */
1394 if (!target && self->group && t == obt_root(ob_screen))
1395 trangroup = TRUE;
1396 } else if (self->group && self->transient)
1397 trangroup = TRUE;
1398
1399 client_update_transient_tree(self, self->group, self->group,
1400 self->transient_for_group, trangroup,
1401 client_direct_parent(self), target);
1402 self->transient_for_group = trangroup;
1403
1404 }
1405
1406 static void client_update_transient_tree(ObClient *self,
1407 ObGroup *oldgroup, ObGroup *newgroup,
1408 gboolean oldgtran, gboolean newgtran,
1409 ObClient* oldparent,
1410 ObClient *newparent)
1411 {
1412 GSList *it, *next;
1413 ObClient *c;
1414
1415 g_assert(!oldgtran || oldgroup);
1416 g_assert(!newgtran || newgroup);
1417 g_assert((!oldgtran && !oldparent) ||
1418 (oldgtran && !oldparent) ||
1419 (!oldgtran && oldparent));
1420 g_assert((!newgtran && !newparent) ||
1421 (newgtran && !newparent) ||
1422 (!newgtran && newparent));
1423
1424 /* * *
1425 Group transient windows are not allowed to have other group
1426 transient windows as their children.
1427 * * */
1428
1429 /* No change has occured */
1430 if (oldgroup == newgroup &&
1431 oldgtran == newgtran &&
1432 oldparent == newparent) return;
1433
1434 /** Remove the client from the transient tree **/
1435
1436 for (it = self->transients; it; it = next) {
1437 next = g_slist_next(it);
1438 c = it->data;
1439 self->transients = g_slist_delete_link(self->transients, it);
1440 c->parents = g_slist_remove(c->parents, self);
1441 }
1442 for (it = self->parents; it; it = next) {
1443 next = g_slist_next(it);
1444 c = it->data;
1445 self->parents = g_slist_delete_link(self->parents, it);
1446 c->transients = g_slist_remove(c->transients, self);
1447 }
1448
1449 /** Re-add the client to the transient tree **/
1450
1451 /* If we're transient for a group then we need to add ourselves to all our
1452 parents */
1453 if (newgtran) {
1454 for (it = newgroup->members; it; it = g_slist_next(it)) {
1455 c = it->data;
1456 if (c != self &&
1457 !client_search_top_direct_parent(c)->transient_for_group &&
1458 client_normal(c))
1459 {
1460 c->transients = g_slist_prepend(c->transients, self);
1461 self->parents = g_slist_prepend(self->parents, c);
1462 }
1463 }
1464 }
1465
1466 /* If we are now transient for a single window we need to add ourselves to
1467 its children
1468
1469 WARNING: Cyclical transient-ness is possible if two windows are
1470 transient for eachother.
1471 */
1472 else if (newparent &&
1473 /* don't make ourself its child if it is already our child */
1474 !client_is_direct_child(self, newparent) &&
1475 client_normal(newparent))
1476 {
1477 newparent->transients = g_slist_prepend(newparent->transients, self);
1478 self->parents = g_slist_prepend(self->parents, newparent);
1479 }
1480
1481 /* Add any group transient windows to our children. But if we're transient
1482 for the group, then other group transients are not our children.
1483
1484 WARNING: Cyclical transient-ness is possible. For e.g. if:
1485 A is transient for the group
1486 B is transient for A
1487 C is transient for B
1488 A can't be transient for C or we have a cycle
1489 */
1490 if (!newgtran && newgroup &&
1491 (!newparent ||
1492 !client_search_top_direct_parent(newparent)->transient_for_group) &&
1493 client_normal(self))
1494 {
1495 for (it = newgroup->members; it; it = g_slist_next(it)) {
1496 c = it->data;
1497 if (c != self && c->transient_for_group &&
1498 /* Don't make it our child if it is already our parent */
1499 !client_is_direct_child(c, self))
1500 {
1501 self->transients = g_slist_prepend(self->transients, c);
1502 c->parents = g_slist_prepend(c->parents, self);
1503 }
1504 }
1505 }
1506
1507 /** If we change our group transient-ness, our children change their
1508 effective group transient-ness, which affects how they relate to other
1509 group windows **/
1510
1511 for (it = self->transients; it; it = g_slist_next(it)) {
1512 c = it->data;
1513 if (!c->transient_for_group)
1514 client_update_transient_tree(c, c->group, c->group,
1515 c->transient_for_group,
1516 c->transient_for_group,
1517 client_direct_parent(c),
1518 client_direct_parent(c));
1519 }
1520 }
1521
1522 void client_get_mwm_hints(ObClient *self)
1523 {
1524 guint num;
1525 guint32 *hints;
1526
1527 self->mwmhints.flags = 0; /* default to none */
1528
1529 if (OBT_PROP_GETA32(self->window, MOTIF_WM_HINTS, MOTIF_WM_HINTS,
1530 &hints, &num)) {
1531 if (num >= OB_MWM_ELEMENTS) {
1532 self->mwmhints.flags = hints[0];
1533 self->mwmhints.functions = hints[1];
1534 self->mwmhints.decorations = hints[2];
1535 }
1536 g_free(hints);
1537 }
1538 }
1539
1540 void client_get_type_and_transientness(ObClient *self)
1541 {
1542 guint num, i;
1543 guint32 *val;
1544 Window t;
1545
1546 self->type = -1;
1547 self->transient = FALSE;
1548
1549 if (OBT_PROP_GETA32(self->window, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) {
1550 /* use the first value that we know about in the array */
1551 for (i = 0; i < num; ++i) {
1552 if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
1553 self->type = OB_CLIENT_TYPE_DESKTOP;
1554 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK))
1555 self->type = OB_CLIENT_TYPE_DOCK;
1556 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR))
1557 self->type = OB_CLIENT_TYPE_TOOLBAR;
1558 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
1559 self->type = OB_CLIENT_TYPE_MENU;
1560 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY))
1561 self->type = OB_CLIENT_TYPE_UTILITY;
1562 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH))
1563 self->type = OB_CLIENT_TYPE_SPLASH;
1564 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG))
1565 self->type = OB_CLIENT_TYPE_DIALOG;
1566 else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL))
1567 self->type = OB_CLIENT_TYPE_NORMAL;
1568 else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
1569 {
1570 /* prevent this window from getting any decor or
1571 functionality */
1572 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1573 OB_MWM_FLAG_DECORATIONS);
1574 self->mwmhints.decorations = 0;
1575 self->mwmhints.functions = 0;
1576 }
1577 if (self->type != (ObClientType) -1)
1578 break; /* grab the first legit type */
1579 }
1580 g_free(val);
1581 }
1582
1583 if (XGetTransientForHint(obt_display, self->window, &t))
1584 self->transient = TRUE;
1585
1586 if (self->type == (ObClientType) -1) {
1587 /*the window type hint was not set, which means we either classify
1588 ourself as a normal window or a dialog, depending on if we are a
1589 transient. */
1590 if (self->transient)
1591 self->type = OB_CLIENT_TYPE_DIALOG;
1592 else
1593 self->type = OB_CLIENT_TYPE_NORMAL;
1594 }
1595
1596 /* then, based on our type, we can update our transientness.. */
1597 if (self->type == OB_CLIENT_TYPE_DIALOG ||
1598 self->type == OB_CLIENT_TYPE_TOOLBAR ||
1599 self->type == OB_CLIENT_TYPE_MENU ||
1600 self->type == OB_CLIENT_TYPE_UTILITY)
1601 {
1602 self->transient = TRUE;
1603 }
1604 }
1605
1606 void client_update_protocols(ObClient *self)
1607 {
1608 guint32 *proto;
1609 guint num_ret, i;
1610
1611 self->focus_notify = FALSE;
1612 self->delete_window = FALSE;
1613
1614 if (OBT_PROP_GETA32(self->window, WM_PROTOCOLS, ATOM, &proto, &num_ret)) {
1615 for (i = 0; i < num_ret; ++i) {
1616 if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW))
1617 /* this means we can request the window to close */
1618 self->delete_window = TRUE;
1619 else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS))
1620 /* if this protocol is requested, then the window will be
1621 notified whenever we want it to receive focus */
1622 self->focus_notify = TRUE;
1623 else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING))
1624 /* if this protocol is requested, then the window will allow
1625 pings to determine if it is still alive */
1626 self->ping = TRUE;
1627 #ifdef SYNC
1628 else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST))
1629 /* if this protocol is requested, then resizing the
1630 window will be synchronized between the frame and the
1631 client */
1632 self->sync_request = TRUE;
1633 #endif
1634 }
1635 g_free(proto);
1636 }
1637 }
1638
1639 #ifdef SYNC
1640 void client_update_sync_request_counter(ObClient *self)
1641 {
1642 guint32 i;
1643
1644 if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
1645 {
1646 XSyncValue val;
1647
1648 self->sync_counter = i;
1649
1650 /* this must be set when managing a new window according to EWMH */
1651 XSyncIntToValue(&val, 0);
1652 XSyncSetCounter(obt_display, self->sync_counter, val);
1653 } else
1654 self->sync_counter = None;
1655 }
1656 #endif
1657
1658 static void client_get_colormap(ObClient *self)
1659 {
1660 XWindowAttributes wa;
1661
1662 if (XGetWindowAttributes(obt_display, self->window, &wa))
1663 client_update_colormap(self, wa.colormap);
1664 }
1665
1666 void client_update_colormap(ObClient *self, Colormap colormap)
1667 {
1668 if (colormap == self->colormap) return;
1669
1670 ob_debug("Setting client %s colormap: 0x%x", self->title, colormap);
1671
1672 if (client_focused(self)) {
1673 screen_install_colormap(self, FALSE); /* uninstall old one */
1674 self->colormap = colormap;
1675 screen_install_colormap(self, TRUE); /* install new one */
1676 } else
1677 self->colormap = colormap;
1678 }
1679
1680 void client_update_opacity(ObClient *self)
1681 {
1682 guint32 o;
1683
1684 if (OBT_PROP_GET32(self->window, NET_WM_WINDOW_OPACITY, CARDINAL, &o))
1685 OBT_PROP_SET32(self->frame->window, NET_WM_WINDOW_OPACITY, CARDINAL, o);
1686 else
1687 OBT_PROP_ERASE(self->frame->window, NET_WM_WINDOW_OPACITY);
1688 }
1689
1690 void client_update_normal_hints(ObClient *self)
1691 {
1692 XSizeHints size;
1693 glong ret;
1694
1695 /* defaults */
1696 self->min_ratio = 0.0f;
1697 self->max_ratio = 0.0f;
1698 SIZE_SET(self->size_inc, 1, 1);
1699 SIZE_SET(self->base_size, -1, -1);
1700 SIZE_SET(self->min_size, 0, 0);
1701 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1702
1703 /* get the hints from the window */
1704 if (XGetWMNormalHints(obt_display, self->window, &size, &ret)) {
1705 /* normal windows can't request placement! har har
1706 if (!client_normal(self))
1707 */
1708 self->positioned = (size.flags & (PPosition|USPosition));
1709 self->sized = (size.flags & (PSize|USSize));
1710
1711 if (size.flags & PWinGravity)
1712 self->gravity = size.win_gravity;
1713
1714 if (size.flags & PAspect) {
1715 if (size.min_aspect.y)
1716 self->min_ratio =
1717 (gfloat) size.min_aspect.x / size.min_aspect.y;
1718 if (size.max_aspect.y)
1719 self->max_ratio =
1720 (gfloat) size.max_aspect.x / size.max_aspect.y;
1721 }
1722
1723 if (size.flags & PMinSize)
1724 SIZE_SET(self->min_size, size.min_width, size.min_height);
1725
1726 if (size.flags & PMaxSize)
1727 SIZE_SET(self->max_size, size.max_width, size.max_height);
1728
1729 if (size.flags & PBaseSize)
1730 SIZE_SET(self->base_size, size.base_width, size.base_height);
1731
1732 if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1733 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1734
1735 ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
1736 self->min_size.width, self->min_size.height,
1737 self->max_size.width, self->max_size.height);
1738 ob_debug("size inc (%d %d) base size (%d %d)",
1739 self->size_inc.width, self->size_inc.height,
1740 self->base_size.width, self->base_size.height);
1741 }
1742 else
1743 ob_debug("Normal hints: not set");
1744 }
1745
1746 static void client_setup_default_decor_and_functions(ObClient *self)
1747 {
1748 /* start with everything (cept fullscreen) */
1749 self->decorations =
1750 (OB_FRAME_DECOR_TITLEBAR |
1751 OB_FRAME_DECOR_HANDLE |
1752 OB_FRAME_DECOR_GRIPS |
1753 OB_FRAME_DECOR_BORDER |
1754 OB_FRAME_DECOR_ICON |
1755 OB_FRAME_DECOR_ALLDESKTOPS |
1756 OB_FRAME_DECOR_ICONIFY |
1757 OB_FRAME_DECOR_MAXIMIZE |
1758 OB_FRAME_DECOR_SHADE |
1759 OB_FRAME_DECOR_CLOSE);
1760 self->functions =
1761 (OB_CLIENT_FUNC_RESIZE |
1762 OB_CLIENT_FUNC_MOVE |
1763 OB_CLIENT_FUNC_ICONIFY |
1764 OB_CLIENT_FUNC_MAXIMIZE |
1765 OB_CLIENT_FUNC_SHADE |
1766 OB_CLIENT_FUNC_CLOSE |
1767 OB_CLIENT_FUNC_BELOW |
1768 OB_CLIENT_FUNC_ABOVE |
1769 OB_CLIENT_FUNC_UNDECORATE);
1770
1771 if (!(self->min_size.width < self->max_size.width ||
1772 self->min_size.height < self->max_size.height))
1773 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1774
1775 switch (self->type) {
1776 case OB_CLIENT_TYPE_NORMAL:
1777 /* normal windows retain all of the possible decorations and
1778 functionality, and can be fullscreen */
1779 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1780 break;
1781
1782 case OB_CLIENT_TYPE_DIALOG:
1783 /* sometimes apps make dialog windows fullscreen for some reason (for
1784 e.g. kpdf does this..) */
1785 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1786 break;
1787
1788 case OB_CLIENT_TYPE_UTILITY:
1789 /* these windows don't have anything added or removed by default */
1790 break;
1791
1792 case OB_CLIENT_TYPE_MENU:
1793 case OB_CLIENT_TYPE_TOOLBAR:
1794 /* these windows can't iconify or maximize */
1795 self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
1796 OB_FRAME_DECOR_MAXIMIZE);
1797 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
1798 OB_CLIENT_FUNC_MAXIMIZE);
1799 break;
1800
1801 case OB_CLIENT_TYPE_SPLASH:
1802 /* these don't get get any decorations, and the only thing you can
1803 do with them is move them */
1804 self->decorations = 0;
1805 self->functions = OB_CLIENT_FUNC_MOVE;
1806 break;
1807
1808 case OB_CLIENT_TYPE_DESKTOP:
1809 /* these windows are not manipulated by the window manager */
1810 self->decorations = 0;
1811 self->functions = 0;
1812 break;
1813
1814 case OB_CLIENT_TYPE_DOCK:
1815 /* these windows are not manipulated by the window manager, but they
1816 can set below layer which has a special meaning */
1817 self->decorations = 0;
1818 self->functions = OB_CLIENT_FUNC_BELOW;
1819 break;
1820 }
1821
1822 /* If the client has no decor from its type (which never changes) then
1823 don't allow the user to "undecorate" the window. Otherwise, allow them
1824 to, even if there are motif hints removing the decor, because those
1825 may change these days (e.g. chromium) */
1826 if (self->decorations == 0)
1827 self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
1828
1829 /* Mwm Hints are applied subtractively to what has already been chosen for
1830 decor and functionality */
1831 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1832 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1833 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1834 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1835 {
1836 /* if the mwm hints request no handle or title, then all
1837 decorations are disabled, but keep the border if that's
1838 specified */
1839 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1840 self->decorations = OB_FRAME_DECOR_BORDER;
1841 else
1842 self->decorations = 0;
1843 }
1844 }
1845 }
1846
1847 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1848 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1849 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1850 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1851 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1852 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1853 /* dont let mwm hints kill any buttons
1854 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1855 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1856 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1857 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1858 */
1859 /* dont let mwm hints kill the close button
1860 if (! (self->mwmhints.functions & MwmFunc_Close))
1861 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1862 }
1863 }
1864
1865 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1866 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1867 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1868 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1869 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1870 self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
1871
1872 /* can't maximize without moving/resizing */
1873 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1874 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1875 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1876 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1877 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1878 }
1879 }
1880
1881 /*! Set up decor for a client based on its undecorated state. */
1882 static void client_setup_decor_undecorated(ObClient *self)
1883 {
1884 /* If the user requested no decorations, then remove all the decorations,
1885 except the border. But don't add a border if there wasn't one. */
1886 if (self->undecorated)
1887 self->decorations &= (config_theme_keepborder ?
1888 OB_FRAME_DECOR_BORDER : 0);
1889 }
1890
1891 void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
1892 {
1893 client_setup_default_decor_and_functions(self);
1894
1895 client_setup_decor_undecorated(self);
1896
1897 if (self->max_horz && self->max_vert) {
1898 /* once upon a time you couldn't resize maximized windows, that is not
1899 the case any more though !
1900
1901 but do kill the handle on fully maxed windows */
1902 self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
1903 }
1904
1905 /* if we don't have a titlebar, then we cannot shade! */
1906 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1907 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1908
1909 /* now we need to check against rules for the client's current state */
1910 if (self->fullscreen) {
1911 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1912 OB_CLIENT_FUNC_FULLSCREEN |
1913 OB_CLIENT_FUNC_ICONIFY);
1914 self->decorations = 0;
1915 }
1916
1917 client_change_allowed_actions(self);
1918
1919 if (reconfig)
1920 /* reconfigure to make sure decorations are updated */
1921 client_reconfigure(self, FALSE);
1922 }
1923
1924 static void client_change_allowed_actions(ObClient *self)
1925 {
1926 gulong actions[12];
1927 gint num = 0;
1928
1929 /* desktop windows are kept on all desktops */
1930 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1931 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
1932
1933 if (self->functions & OB_CLIENT_FUNC_SHADE)
1934 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
1935 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1936 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
1937 if (self->functions & OB_CLIENT_FUNC_MOVE)
1938 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
1939 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1940 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
1941 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1942 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
1943 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1944 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
1945 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1946 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
1947 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
1948 }
1949 if (self->functions & OB_CLIENT_FUNC_ABOVE)
1950 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
1951 if (self->functions & OB_CLIENT_FUNC_BELOW)
1952 actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
1953 if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
1954 actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
1955
1956 OBT_PROP_SETA32(self->window, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num);
1957
1958 /* make sure the window isn't breaking any rules now
1959
1960 don't check ICONIFY here. just cuz a window can't iconify doesnt mean
1961 it can't be iconified with its parent
1962 */
1963
1964 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1965 if (self->frame) client_shade(self, FALSE);
1966 else self->shaded = FALSE;
1967 }
1968 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1969 if (self->frame) client_fullscreen(self, FALSE);
1970 else self->fullscreen = FALSE;
1971 }
1972 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1973 self->max_vert)) {
1974 if (self->frame) client_maximize(self, FALSE, 0);
1975 else self->max_vert = self->max_horz = FALSE;
1976 }
1977 }
1978
1979 void client_update_wmhints(ObClient *self)
1980 {
1981 XWMHints *hints;
1982
1983 /* assume a window takes input if it doesn't specify */
1984 self->can_focus = TRUE;
1985
1986 if ((hints = XGetWMHints(obt_display, self->window)) != NULL) {
1987 gboolean ur;
1988
1989 if (hints->flags & InputHint)
1990 self->can_focus = hints->input;
1991
1992 /* only do this when first managing the window *AND* when we aren't
1993 starting up! */
1994 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1995 if (hints->flags & StateHint)
1996 self->iconic = hints->initial_state == IconicState;
1997
1998 ur = self->urgent;
1999 self->urgent = (hints->flags & XUrgencyHint);
2000 if (self->urgent && !ur)
2001 client_hilite(self, TRUE);
2002 else if (!self->urgent && ur && self->demands_attention)
2003 client_hilite(self, FALSE);
2004
2005 if (!(hints->flags & WindowGroupHint))
2006 hints->window_group = None;
2007
2008 /* did the group state change? */
2009 if (hints->window_group !=
2010 (self->group ? self->group->leader : None))
2011 {
2012 ObGroup *oldgroup = self->group;
2013
2014 /* remove from the old group if there was one */
2015 if (self->group) {
2016 group_remove(self->group, self);
2017 self->group = NULL;
2018 }
2019
2020 /* add ourself to the group if we have one */
2021 if (hints->window_group != None) {
2022 self->group = group_add(hints->window_group, self);
2023 }
2024
2025 /* Put ourselves into the new group's transient tree, and remove
2026 ourselves from the old group's */
2027 client_update_transient_tree(self, oldgroup, self->group,
2028 self->transient_for_group,
2029 self->transient_for_group,
2030 client_direct_parent(self),
2031 client_direct_parent(self));
2032
2033 /* Lastly, being in a group, or not, can change if the window is
2034 transient for anything.
2035
2036 The logic for this is:
2037 self->transient = TRUE always if the window wants to be
2038 transient for something, even if transient_for was NULL because
2039 it wasn't in a group before.
2040
2041 If parents was NULL and oldgroup was NULL we can assume
2042 that when we add the new group, it will become transient for
2043 something.
2044
2045 If transient_for_group is TRUE, then it must have already
2046 had a group. If it is getting a new group, the above call to
2047 client_update_transient_tree has already taken care of
2048 everything ! If it is losing all group status then it will
2049 no longer be transient for anything and that needs to be
2050 updated.
2051 */
2052 if (self->transient &&
2053 ((self->parents == NULL && oldgroup == NULL) ||
2054 (self->transient_for_group && !self->group)))
2055 client_update_transient_for(self);
2056 }
2057
2058 /* the WM_HINTS can contain an icon */
2059 if (hints->flags & IconPixmapHint)
2060 client_update_icons(self);
2061
2062 XFree(hints);
2063 }
2064
2065 focus_cycle_addremove(self, TRUE);
2066 }
2067
2068 void client_update_title(ObClient *self)
2069 {
2070 gchar *data = NULL;
2071 gchar *visible = NULL;
2072
2073 g_free(self->title);
2074 g_free(self->original_title);
2075
2076 /* try netwm */
2077 if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_NAME, &data)) {
2078 /* try old x stuff */
2079 if (!OBT_PROP_GETS(self->window, WM_NAME, &data)) {
2080 if (self->transient) {
2081 /*
2082 GNOME alert windows are not given titles:
2083 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
2084 */
2085 data = g_strdup("");
2086 } else
2087 data = g_strdup(_("Unnamed Window"));
2088 }
2089 }
2090 self->original_title = g_strdup(data);
2091
2092 if (self->client_machine) {
2093 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2094 g_free(data);
2095 } else
2096 visible = data;
2097
2098 if (self->not_responding) {
2099 data = visible;
2100 if (self->kill_level > 0)
2101 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2102 else
2103 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2104 g_free(data);
2105 }
2106
2107 OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, visible);
2108 self->title = visible;
2109
2110 if (self->frame)
2111 frame_adjust_title(self->frame);
2112
2113 /* update the icon title */
2114 data = NULL;
2115 g_free(self->icon_title);
2116
2117 /* try netwm */
2118 if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_ICON_NAME, &data))
2119 /* try old x stuff */
2120 if (!OBT_PROP_GETS(self->window, WM_ICON_NAME, &data))
2121 data = g_strdup(self->title);
2122
2123 if (self->client_machine) {
2124 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2125 g_free(data);
2126 } else
2127 visible = data;
2128
2129 if (self->not_responding) {
2130 data = visible;
2131 if (self->kill_level > 0)
2132 visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2133 else
2134 visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2135 g_free(data);
2136 }
2137
2138 OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, visible);
2139 self->icon_title = visible;
2140 }
2141
2142 void client_update_strut(ObClient *self)
2143 {
2144 guint num;
2145 guint32 *data;
2146 gboolean got = FALSE;
2147 StrutPartial strut;
2148
2149 if (OBT_PROP_GETA32(self->window, NET_WM_STRUT_PARTIAL, CARDINAL,
2150 &data, &num))
2151 {
2152 if (num == 12) {
2153 got = TRUE;
2154 STRUT_PARTIAL_SET(strut,
2155 data[0], data[2], data[1], data[3],
2156 data[4], data[5], data[8], data[9],
2157 data[6], data[7], data[10], data[11]);
2158 }
2159 g_free(data);
2160 }
2161
2162 if (!got &&
2163 OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) {
2164 if (num == 4) {
2165 const Rect *a;
2166
2167 got = TRUE;
2168
2169 /* use the screen's width/height */
2170 a = screen_physical_area_all_monitors();
2171
2172 STRUT_PARTIAL_SET(strut,
2173 data[0], data[2], data[1], data[3],
2174 a->y, a->y + a->height - 1,
2175 a->x, a->x + a->width - 1,
2176 a->y, a->y + a->height - 1,
2177 a->x, a->x + a->width - 1);
2178 }
2179 g_free(data);
2180 }
2181
2182 if (!got)
2183 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
2184 0, 0, 0, 0, 0, 0, 0, 0);
2185
2186 if (!PARTIAL_STRUT_EQUAL(strut, self->strut)) {
2187 self->strut = strut;
2188
2189 /* updating here is pointless while we're being mapped cuz we're not in
2190 the client list yet */
2191 if (self->frame)
2192 screen_update_areas();
2193 }
2194 }
2195
2196 void client_update_icons(ObClient *self)
2197 {
2198 guint num;
2199 guint32 *data;
2200 guint w, h, i, j;
2201 RrImage *img;
2202
2203 img = NULL;
2204
2205 /* grab the server, because we might be setting the window's icon and
2206 we don't want them to set it in between and we overwrite their own
2207 icon */
2208 grab_server(TRUE);
2209
2210 if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) {
2211 /* figure out how many valid icons are in here */
2212 i = 0;
2213 while (i + 2 < num) { /* +2 is to make sure there is a w and h */
2214 w = data[i++];
2215 h = data[i++];
2216 /* watch for the data being too small for the specified size,
2217 or for zero sized icons. */
2218 if (i + w*h > num || w == 0 || h == 0) {
2219 i += w*h;
2220 continue;
2221 }
2222
2223 /* convert it to the right bit order for ObRender */
2224 for (j = 0; j < w*h; ++j)
2225 data[i+j] =
2226 (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) +
2227 (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset) +
2228 (((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) +
2229 (((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset);
2230
2231 /* add it to the image cache as an original */
2232 if (!img)
2233 img = RrImageNewFromData(ob_rr_icons, &data[i], w, h);
2234 else
2235 RrImageAddFromData(img, &data[i], w, h);
2236
2237 i += w*h;
2238 }
2239
2240 g_free(data);
2241 }
2242
2243 /* if we didn't find an image from the NET_WM_ICON stuff, then try the
2244 legacy X hints */
2245 if (!img) {
2246 XWMHints *hints;
2247
2248 if ((hints = XGetWMHints(obt_display, self->window))) {
2249 if (hints->flags & IconPixmapHint) {
2250 gboolean xicon;
2251 obt_display_ignore_errors(TRUE);
2252 xicon = RrPixmapToRGBA(ob_rr_inst,
2253 hints->icon_pixmap,
2254 (hints->flags & IconMaskHint ?
2255 hints->icon_mask : None),
2256 (gint*)&w, (gint*)&h, &data);
2257 obt_display_ignore_errors(FALSE);
2258
2259 if (xicon) {
2260 if (w > 0 && h > 0) {
2261 if (!img)
2262 img = RrImageNewFromData(ob_rr_icons, data, w, h);
2263 else
2264 RrImageAddFromData(img, data, w, h);
2265 }
2266
2267 g_free(data);
2268 }
2269 }
2270 XFree(hints);
2271 }
2272 }
2273
2274 /* set the client's icons to be whatever we found */
2275 RrImageUnref(self->icon_set);
2276 self->icon_set = img;
2277
2278 /* if the client has no icon at all, then we set a default icon onto it.
2279 but, if it has parents, then one of them will have an icon already
2280 */
2281 if (!self->icon_set && !self->parents) {
2282 RrPixel32 *icon = ob_rr_theme->def_win_icon;
2283 gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */
2284
2285 w = ob_rr_theme->def_win_icon_w;
2286 h = ob_rr_theme->def_win_icon_h;
2287 ldata = g_new(gulong, w*h+2);
2288 ldata[0] = w;
2289 ldata[1] = h;
2290 for (i = 0; i < w*h; ++i)
2291 ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
2292 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
2293 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
2294 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
2295 OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, ldata, w*h+2);
2296 g_free(ldata);
2297 } else if (self->frame)
2298 /* don't draw the icon empty if we're just setting one now anyways,
2299 we'll get the property change any second */
2300 frame_adjust_icon(self->frame);
2301
2302 grab_server(FALSE);
2303 }
2304
2305 void client_update_icon_geometry(ObClient *self)
2306 {
2307 guint num;
2308 guint32 *data;
2309
2310 RECT_SET(self->icon_geometry, 0, 0, 0, 0);
2311
2312 if (OBT_PROP_GETA32(self->window, NET_WM_ICON_GEOMETRY, CARDINAL,
2313 &data, &num))
2314 {
2315 if (num == 4)
2316 /* don't let them set it with an area < 0 */
2317 RECT_SET(self->icon_geometry, data[0], data[1],
2318 MAX(data[2],0), MAX(data[3],0));
2319 g_free(data);
2320 }
2321 }
2322
2323 static void client_get_session_ids(ObClient *self)
2324 {
2325 guint32 leader;
2326 gboolean got;
2327 gchar *s;
2328 gchar **ss;
2329
2330 if (!OBT_PROP_GET32(self->window, WM_CLIENT_LEADER, WINDOW, &leader))
2331 leader = None;
2332
2333 /* get the SM_CLIENT_ID */
2334 if (leader && leader != self->window)
2335 OBT_PROP_GETS_XPCS(leader, SM_CLIENT_ID, &self->sm_client_id);
2336 else
2337 OBT_PROP_GETS_XPCS(self->window, SM_CLIENT_ID, &self->sm_client_id);
2338
2339 /* get the WM_CLASS (name and class). make them "" if they are not
2340 provided */
2341 got = OBT_PROP_GETSS_TYPE(self->window, WM_CLASS, STRING_NO_CC, &ss);
2342
2343 if (got) {
2344 if (ss[0]) {
2345 self->name = g_strdup(ss[0]);
2346 if (ss[1])
2347 self->class = g_strdup(ss[1]);
2348 }
2349 g_strfreev(ss);
2350 }
2351
2352 if (self->name == NULL) self->name = g_strdup("");
2353 if (self->class == NULL) self->class = g_strdup("");
2354
2355 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2356 got = OBT_PROP_GETS_XPCS(self->window, WM_WINDOW_ROLE, &s);
2357
2358 if (got)
2359 self->role = s;
2360 else
2361 self->role = g_strdup("");
2362
2363 /* get the WM_COMMAND */
2364 got = FALSE;
2365
2366 if (leader)
2367 got = OBT_PROP_GETSS(leader, WM_COMMAND, &ss);
2368 if (!got)
2369 got = OBT_PROP_GETSS(self->window, WM_COMMAND, &ss);
2370
2371 if (got) {
2372 /* merge/mash them all together */
2373 gchar *merge = NULL;
2374 gint i;
2375
2376 for (i = 0; ss[i]; ++i) {
2377 gchar *tmp = merge;
2378 if (merge)
2379 merge = g_strconcat(merge, ss[i], NULL);
2380 else
2381 merge = g_strconcat(ss[i], NULL);
2382 g_free(tmp);
2383 }
2384 g_strfreev(ss);
2385
2386 self->wm_command = merge;
2387 }
2388
2389 /* get the WM_CLIENT_MACHINE */
2390 got = FALSE;
2391 if (leader)
2392 got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, &s);
2393 if (!got)
2394 got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, &s);
2395
2396 if (got) {
2397 gchar localhost[128];
2398 guint32 pid;
2399
2400 gethostname(localhost, 127);
2401 localhost[127] = '\0';
2402 if (strcmp(localhost, s) != 0)
2403 self->client_machine = s;
2404 else
2405 g_free(s);
2406
2407 /* see if it has the PID set too (the PID requires that the
2408 WM_CLIENT_MACHINE be set) */
2409 if (OBT_PROP_GET32(self->window, NET_WM_PID, CARDINAL, &pid))
2410 self->pid = pid;
2411 }
2412 }
2413
2414 /*! Save the properties used for app matching rules, as seen by Openbox when
2415 the window mapped, so that users can still access them later if the app
2416 changes them */
2417 static void client_save_app_rule_values(ObClient *self)
2418 {
2419 const gchar *type;
2420
2421 OBT_PROP_SETS(self->window, OB_APP_ROLE, self->role);
2422 OBT_PROP_SETS(self->window, OB_APP_NAME, self->name);
2423 OBT_PROP_SETS(self->window, OB_APP_CLASS, self->class);
2424 OBT_PROP_SETS(self->window, OB_APP_TITLE, self->original_title);
2425
2426 switch (self->type) {
2427 case OB_CLIENT_TYPE_NORMAL:
2428 type = "normal"; break;
2429 case OB_CLIENT_TYPE_DIALOG:
2430 type = "dialog"; break;
2431 case OB_CLIENT_TYPE_UTILITY:
2432 type = "utility"; break;
2433 case OB_CLIENT_TYPE_MENU:
2434 type = "menu"; break;
2435 case OB_CLIENT_TYPE_TOOLBAR:
2436 type = "toolbar"; break;
2437 case OB_CLIENT_TYPE_SPLASH:
2438 type = "splash"; break;
2439 case OB_CLIENT_TYPE_DESKTOP:
2440 type = "desktop"; break;
2441 case OB_CLIENT_TYPE_DOCK:
2442 type = "dock"; break;
2443 }
2444 OBT_PROP_SETS(self->window, OB_APP_TYPE, type);
2445 }
2446
2447 static void client_change_wm_state(ObClient *self)
2448 {
2449 gulong state[2];
2450 glong old;
2451
2452 old = self->wmstate;
2453
2454 if (self->shaded || self->iconic ||
2455 (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
2456 {
2457 self->wmstate = IconicState;
2458 } else
2459 self->wmstate = NormalState;
2460
2461 if (old != self->wmstate) {
2462 OBT_PROP_MSG(ob_screen, self->window, KDE_WM_CHANGE_STATE,
2463 self->wmstate, 1, 0, 0, 0);
2464
2465 state[0] = self->wmstate;
2466 state[1] = None;
2467 OBT_PROP_SETA32(self->window, WM_STATE, WM_STATE, state, 2);
2468 }
2469 }
2470
2471 static void client_change_state(ObClient *self)
2472 {
2473 gulong netstate[12];
2474 guint num;
2475
2476 num = 0;
2477 if (self->modal)
2478 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
2479 if (self->shaded)
2480 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
2481 if (self->iconic)
2482 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
2483 if (self->skip_taskbar)
2484 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
2485 if (self->skip_pager)
2486 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
2487 if (self->fullscreen)
2488 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
2489 if (self->max_vert)
2490 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
2491 if (self->max_horz)
2492 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
2493 if (self->above)
2494 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
2495 if (self->below)
2496 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
2497 if (self->demands_attention)
2498 netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
2499 if (self->undecorated)
2500 netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
2501 OBT_PROP_SETA32(self->window, NET_WM_STATE, ATOM, netstate, num);
2502
2503 if (self->frame)
2504 frame_adjust_state(self->frame);
2505 }
2506
2507 ObClient *client_search_focus_tree(ObClient *self)
2508 {
2509 GSList *it;
2510 ObClient *ret;
2511
2512 for (it = self->transients; it; it = g_slist_next(it)) {
2513 if (client_focused(it->data)) return it->data;
2514 if ((ret = client_search_focus_tree(it->data))) return ret;
2515 }
2516 return NULL;
2517 }
2518
2519 ObClient *client_search_focus_tree_full(ObClient *self)
2520 {
2521 if (self->parents) {
2522 GSList *it;
2523
2524 for (it = self->parents; it; it = g_slist_next(it)) {
2525 ObClient *c = it->data;
2526 if ((c = client_search_focus_tree_full(c))) return c;
2527 }
2528
2529 return NULL;
2530 }
2531 else {
2532 /* this function checks the whole tree, the client_search_focus_tree
2533 does not, so we need to check this window */
2534 if (client_focused(self))
2535 return self;
2536 return client_search_focus_tree(self);
2537 }
2538 }
2539
2540 ObClient *client_search_focus_group_full(ObClient *self)
2541 {
2542 GSList *it;
2543
2544 if (self->group) {
2545 for (it = self->group->members; it; it = g_slist_next(it)) {
2546 ObClient *c = it->data;
2547
2548 if (client_focused(c)) return c;
2549 if ((c = client_search_focus_tree(it->data))) return c;
2550 }
2551 } else
2552 if (client_focused(self)) return self;
2553 return NULL;
2554 }
2555
2556 gboolean client_has_parent(ObClient *self)
2557 {
2558 return self->parents != NULL;
2559 }
2560
2561 gboolean client_has_children(ObClient *self)
2562 {
2563 return self->transients != NULL;
2564 }
2565
2566 gboolean client_is_oldfullscreen(const ObClient *self,
2567 const Rect *area)
2568 {
2569 const Rect *monitor, *allmonitors;
2570
2571 /* No decorations and fills the monitor = oldskool fullscreen.
2572 But not for maximized windows.
2573 */
2574
2575 if (self->decorations || self->max_horz || self->max_vert) return FALSE;
2576
2577 monitor = screen_physical_area_monitor(screen_find_monitor(area));
2578 allmonitors = screen_physical_area_all_monitors();
2579
2580 return (RECT_EQUAL(*area, *monitor) ||
2581 RECT_EQUAL(*area, *allmonitors));
2582 }
2583
2584 static ObStackingLayer calc_layer(ObClient *self)
2585 {
2586 ObStackingLayer l;
2587
2588 if (self->type == OB_CLIENT_TYPE_DESKTOP)
2589 l = OB_STACKING_LAYER_DESKTOP;
2590 else if (self->type == OB_CLIENT_TYPE_DOCK) {
2591 if (self->below) l = OB_STACKING_LAYER_NORMAL;
2592 else l = OB_STACKING_LAYER_ABOVE;
2593 }
2594 else if ((self->fullscreen ||
2595 client_is_oldfullscreen(self, &self->area)) &&
2596 /* you are fullscreen while you or your children are focused.. */
2597 (client_focused(self) || client_search_focus_tree(self) ||
2598 /* you can be fullscreen if you're on another desktop */
2599 (self->desktop != screen_desktop &&
2600 self->desktop != DESKTOP_ALL) ||
2601 /* and you can also be fullscreen if the focused client is on
2602 another monitor, or nothing else is focused */
2603 (!focus_client ||
2604 client_monitor(focus_client) != client_monitor(self))))
2605 l = OB_STACKING_LAYER_FULLSCREEN;
2606 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2607 else if (self->below) l = OB_STACKING_LAYER_BELOW;
2608 else l = OB_STACKING_LAYER_NORMAL;
2609
2610 return l;
2611 }
2612
2613 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2614 ObStackingLayer min)
2615 {
2616 ObStackingLayer old, own;
2617 GSList *it;
2618
2619 old = self->layer;
2620 own = calc_layer(self);
2621 self->layer = MAX(own, min);
2622
2623 if (self->layer != old) {
2624 stacking_remove(CLIENT_AS_WINDOW(self));
2625 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
2626 }
2627
2628 /* we've been restacked */
2629 self->visited = TRUE;
2630
2631 for (it = self->transients; it; it = g_slist_next(it))
2632 client_calc_layer_recursive(it->data, orig,
2633 self->layer);
2634 }
2635
2636 static void client_calc_layer_internal(ObClient *self)
2637 {
2638 GSList *sit;
2639
2640 /* transients take on the layer of their parents */
2641 sit = client_search_all_top_parents(self);
2642
2643 for (; sit; sit = g_slist_next(sit))
2644 client_calc_layer_recursive(sit->data, self, 0);
2645 }
2646
2647 void client_calc_layer(ObClient *self)
2648 {
2649 GList *it;
2650
2651 /* skip over stuff above fullscreen layer */
2652 for (it = stacking_list; it; it = g_list_next(it))
2653 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2654
2655 /* find the windows in the fullscreen layer, and mark them not-visited */
2656 for (; it; it = g_list_next(it)) {
2657 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2658 else if (WINDOW_IS_CLIENT(it->data))
2659 WINDOW_AS_CLIENT(it->data)->visited = FALSE;
2660 }
2661
2662 client_calc_layer_internal(self);
2663
2664 /* skip over stuff above fullscreen layer */
2665 for (it = stacking_list; it; it = g_list_next(it))
2666 if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2667
2668 /* now recalc any windows in the fullscreen layer which have not
2669 had their layer recalced already */
2670 for (; it; it = g_list_next(it)) {
2671 if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2672 else if (WINDOW_IS_CLIENT(it->data) &&
2673 !WINDOW_AS_CLIENT(it->data)->visited)
2674 client_calc_layer_internal(it->data);
2675 }
2676 }
2677
2678 gboolean client_should_show(ObClient *self)
2679 {
2680 if (self->iconic)
2681 return FALSE;
2682 if (client_normal(self) && screen_showing_desktop)
2683 return FALSE;
2684 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2685 return TRUE;
2686
2687 return FALSE;
2688 }
2689
2690 gboolean client_show(ObClient *self)
2691 {
2692 gboolean show = FALSE;
2693
2694 if (client_should_show(self)) {
2695 /* replay pending pointer event before showing the window, in case it
2696 should be going to something under the window */
2697 mouse_replay_pointer();
2698
2699 frame_show(self->frame);
2700 show = TRUE;
2701
2702 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2703 it needs to be in IconicState. This includes when it is on another
2704 desktop!
2705 */
2706 client_change_wm_state(self);
2707 }
2708 return show;
2709 }
2710
2711 gboolean client_hide(ObClient *self)
2712 {
2713 gboolean hide = FALSE;
2714
2715 if (!client_should_show(self)) {
2716 /* We don't need to ignore enter events here.
2717 The window can hide/iconify in 3 different ways:
2718 1 - through an x message. in this case we ignore all enter events
2719 caused by responding to the x message (unless underMouse)
2720 2 - by a keyboard action. in this case we ignore all enter events
2721 caused by the action
2722 3 - by a mouse action. in this case they are doing stuff with the
2723 mouse and focus _should_ move.
2724
2725 Also in action_end, we simulate an enter event that can't be ignored
2726 so trying to ignore them is futile in case 3 anyways
2727 */
2728
2729 /* replay pending pointer event before hiding the window, in case it
2730 should be going to the window */
2731 mouse_replay_pointer();
2732
2733 frame_hide(self->frame);
2734 hide = TRUE;
2735
2736 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2737 it needs to be in IconicState. This includes when it is on another
2738 desktop!
2739 */
2740 client_change_wm_state(self);
2741 }
2742 return hide;
2743 }
2744
2745 void client_showhide(ObClient *self)
2746 {
2747 if (!client_show(self))
2748 client_hide(self);
2749 }
2750
2751 gboolean client_normal(ObClient *self) {
2752 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2753 self->type == OB_CLIENT_TYPE_DOCK ||
2754 self->type == OB_CLIENT_TYPE_SPLASH);
2755 }
2756
2757 gboolean client_helper(ObClient *self)
2758 {
2759 return (self->type == OB_CLIENT_TYPE_UTILITY ||
2760 self->type == OB_CLIENT_TYPE_MENU ||
2761 self->type == OB_CLIENT_TYPE_TOOLBAR);
2762 }
2763
2764 gboolean client_mouse_focusable(ObClient *self)
2765 {
2766 return !(self->type == OB_CLIENT_TYPE_MENU ||
2767 self->type == OB_CLIENT_TYPE_TOOLBAR ||
2768 self->type == OB_CLIENT_TYPE_SPLASH ||
2769 self->type == OB_CLIENT_TYPE_DOCK);
2770 }
2771
2772 gboolean client_enter_focusable(ObClient *self)
2773 {
2774 /* you can focus desktops but it shouldn't on enter */
2775 return (client_mouse_focusable(self) &&
2776 self->type != OB_CLIENT_TYPE_DESKTOP);
2777 }
2778
2779 static void client_apply_startup_state(ObClient *self,
2780 gint x, gint y, gint w, gint h)
2781 {
2782 /* save the states that we are going to apply */
2783 gboolean iconic = self->iconic;
2784 gboolean fullscreen = self->fullscreen;
2785 gboolean undecorated = self->undecorated;
2786 gboolean shaded = self->shaded;
2787 gboolean demands_attention = self->demands_attention;
2788 gboolean max_horz = self->max_horz;
2789 gboolean max_vert = self->max_vert;
2790 Rect oldarea;
2791 gint l;
2792
2793 /* turn them all off in the client, so they won't affect the window
2794 being placed */
2795 self->iconic = self->fullscreen = self->undecorated = self->shaded =
2796 self->demands_attention = self->max_horz = self->max_vert = FALSE;
2797
2798 /* move the client to its placed position, or it it's already there,
2799 generate a ConfigureNotify telling the client where it is.
2800
2801 do this after adjusting the frame. otherwise it gets all weird and
2802 clients don't work right
2803
2804 do this before applying the states so they have the correct
2805 pre-max/pre-fullscreen values
2806 */
2807 client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
2808 ob_debug("placed window 0x%x at %d, %d with size %d x %d",
2809 self->window, x, y, w, h);
2810 /* save the area, and make it where it should be for the premax stuff */
2811 oldarea = self->area;
2812 RECT_SET(self->area, x, y, w, h);
2813
2814 /* apply the states. these are in a carefully crafted order.. */
2815
2816 if (iconic)
2817 client_iconify(self, TRUE, FALSE, TRUE);
2818 if (undecorated)
2819 client_set_undecorated(self, TRUE);
2820 if (shaded)
2821 client_shade(self, TRUE);
2822 if (demands_attention)
2823 client_hilite(self, TRUE);
2824
2825 if (max_vert && max_horz)
2826 client_maximize(self, TRUE, 0);
2827 else if (max_vert)
2828 client_maximize(self, TRUE, 2);
2829 else if (max_horz)
2830 client_maximize(self, TRUE, 1);
2831
2832 /* fullscreen removes the ability to apply other states */
2833 if (fullscreen)
2834 client_fullscreen(self, TRUE);
2835
2836 /* make sure client_setup_decor_and_functions() is called at least once */
2837 client_setup_decor_and_functions(self, FALSE);
2838
2839 /* if the window hasn't been configured yet, then do so now, in fact the
2840 x,y,w,h may _not_ be the same as the area rect, which can end up
2841 meaning that the client isn't properly moved/resized by the fullscreen
2842 function
2843 pho can cause this because it maps at size of the screen but not 0,0
2844 so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2845 then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2846 cuz thats where the pre-fullscreen will be. however the actual area is
2847 not, so this needs to be called even if we have fullscreened/maxed
2848 */
2849 self->area = oldarea;
2850 client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
2851
2852 /* nothing to do for the other states:
2853 skip_taskbar
2854 skip_pager
2855 modal
2856 above
2857 below
2858 */
2859 }
2860
2861 void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
2862 {
2863 /* these should be the current values. this is for when you're not moving,
2864 just resizing */
2865 g_assert(*x == self->area.x);
2866 g_assert(oldw == self->area.width);
2867
2868 /* horizontal */
2869 switch (self->gravity) {
2870 default:
2871 case NorthWestGravity:
2872 case WestGravity:
2873 case SouthWestGravity:
2874 case StaticGravity:
2875 case ForgetGravity:
2876 break;
2877 case NorthGravity:
2878 case CenterGravity:
2879 case SouthGravity:
2880 *x -= (neww - oldw) / 2;
2881 break;
2882 case NorthEastGravity:
2883 case EastGravity:
2884 case SouthEastGravity:
2885 *x -= neww - oldw;
2886 break;
2887 }
2888 }
2889
2890 void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
2891 {
2892 /* these should be the current values. this is for when you're not moving,
2893 just resizing */
2894 g_assert(*y == self->area.y);
2895 g_assert(oldh == self->area.height);
2896
2897 /* vertical */
2898 switch (self->gravity) {
2899 default:
2900 case NorthWestGravity:
2901 case NorthGravity:
2902 case NorthEastGravity:
2903 case StaticGravity:
2904 case ForgetGravity:
2905 break;
2906 case WestGravity:
2907 case CenterGravity:
2908 case EastGravity:
2909 *y -= (newh - oldh) / 2;
2910 break;
2911 case SouthWestGravity:
2912 case SouthGravity:
2913 case SouthEastGravity:
2914 *y -= newh - oldh;
2915 break;
2916 }
2917 }
2918
2919 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2920 gint *logicalw, gint *logicalh,
2921 gboolean user)
2922 {
2923 Rect desired = {*x, *y, *w, *h};
2924 frame_rect_to_frame(self->frame, &desired);
2925
2926 /* make the frame recalculate its dimensions n shit without changing
2927 anything visible for real, this way the constraints below can work with
2928 the updated frame dimensions. */
2929 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
2930
2931 /* cap any X windows at the size of an unsigned short */
2932 *w = MIN(*w,
2933 G_MAXUSHORT - self->frame->size.left - self->frame->size.right);
2934 *h = MIN(*h,
2935 G_MAXUSHORT - self->frame->size.top - self->frame->size.bottom);
2936
2937
2938 /* gets the frame's position */
2939 frame_client_gravity(self->frame, x, y);
2940
2941 /* these positions are frame positions, not client positions */
2942
2943 /* set the size and position if fullscreen */
2944 if (self->fullscreen) {
2945 const Rect *a;
2946 guint i;
2947
2948 i = screen_find_monitor(&desired);
2949 a = screen_physical_area_monitor(i);
2950
2951 *x = a->x;
2952 *y = a->y;
2953 *w = a->width;
2954 *h = a->height;
2955
2956 user = FALSE; /* ignore if the client can't be moved/resized when it
2957 is fullscreening */
2958 } else if (self->max_horz || self->max_vert) {
2959 Rect *a;
2960 guint i;
2961
2962 /* use all possible struts when maximizing to the full screen */
2963 i = screen_find_monitor(&desired);
2964 a = screen_area(self->desktop, i,
2965 (self->max_horz && self->max_vert ? NULL : &desired));
2966
2967 /* set the size and position if maximized */
2968 if (self->max_horz) {
2969 *x = a->x;
2970 *w = a->width - self->frame->size.left - self->frame->size.right;
2971 }
2972 if (self->max_vert) {
2973 *y = a->y;
2974 *h = a->height - self->frame->size.top - self->frame->size.bottom;
2975 }
2976
2977 user = FALSE; /* ignore if the client can't be moved/resized when it
2978 is maximizing */
2979
2980 g_slice_free(Rect, a);
2981 }
2982
2983 /* gets the client's position */
2984 frame_frame_gravity(self->frame, x, y);
2985
2986 /* work within the preferred sizes given by the window, these may have
2987 changed rather than it's requested width and height, so always run
2988 through this code */
2989 {
2990 gint basew, baseh, minw, minh;
2991 gint incw, inch, maxw, maxh;
2992 gfloat minratio, maxratio;
2993
2994 incw = self->size_inc.width;
2995 inch = self->size_inc.height;
2996 minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2997 0 : self->min_ratio;
2998 maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2999 0 : self->max_ratio;
3000
3001 /* base size is substituted with min size if not specified */
3002 if (self->base_size.width >= 0 || self->base_size.height >= 0) {
3003 basew = self->base_size.width;
3004 baseh = self->base_size.height;
3005 } else {
3006 basew = self->min_size.width;
3007 baseh = self->min_size.height;
3008 }
3009 /* min size is substituted with base size if not specified */
3010 if (self->min_size.width || self->min_size.height) {
3011 minw = self->min_size.width;
3012 minh = self->min_size.height;
3013 } else {
3014 minw = self->base_size.width;
3015 minh = self->base_size.height;
3016 }
3017
3018 /* This comment is no longer true */
3019 /* if this is a user-requested resize, then check against min/max
3020 sizes */
3021
3022 /* smaller than min size or bigger than max size? */
3023 if (*w > self->max_size.width) *w = self->max_size.width;
3024 if (*w < minw) *w = minw;
3025 if (*h > self->max_size.height) *h = self->max_size.height;
3026 if (*h < minh) *h = minh;
3027
3028 *w -= basew;
3029 *h -= baseh;
3030
3031 /* the sizes to used for maximized */
3032 maxw = *w;
3033 maxh = *h;
3034
3035 /* keep to the increments */
3036 *w /= incw;
3037 *h /= inch;
3038
3039 /* you cannot resize to nothing */
3040 if (basew + *w < 1) *w = 1 - basew;
3041 if (baseh + *h < 1) *h = 1 - baseh;
3042
3043 /* save the logical size */
3044 *logicalw = incw > 1 ? *w : *w + basew;
3045 *logicalh = inch > 1 ? *h : *h + baseh;
3046
3047 *w *= incw;
3048 *h *= inch;
3049
3050 /* if maximized/fs then don't use the size increments */
3051 if (self->fullscreen || self->max_horz) *w = maxw;
3052 if (self->fullscreen || self->max_vert) *h = maxh;
3053
3054 *w += basew;
3055 *h += baseh;
3056
3057 /* adjust the height to match the width for the aspect ratios.
3058 for this, min size is not substituted for base size ever. */
3059 *w -= self->base_size.width;
3060 *h -= self->base_size.height;
3061
3062 if (minratio)
3063 if (*h * minratio > *w) {
3064 *h = (gint)(*w / minratio);
3065
3066 /* you cannot resize to nothing */
3067 if (*h < 1) {
3068 *h = 1;
3069 *w = (gint)(*h * minratio);
3070 }
3071 }
3072 if (maxratio)
3073 if (*h * maxratio < *w) {
3074 *h = (gint)(*w / maxratio);
3075
3076 /* you cannot resize to nothing */
3077 if (*h < 1) {
3078 *h = 1;
3079 *w = (gint)(*h * minratio);
3080 }
3081 }
3082
3083 *w += self->base_size.width;
3084 *h += self->base_size.height;
3085 }
3086
3087 /* these override the above states! if you cant move you can't move! */
3088 if (user) {
3089 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
3090 *x = self->area.x;
3091 *y = self->area.y;
3092 }
3093 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
3094 *w = self->area.width;
3095 *h = self->area.height;
3096 }
3097 }
3098
3099 g_assert(*w > 0);
3100 g_assert(*h > 0);
3101 }
3102
3103 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
3104 gboolean user, gboolean final, gboolean force_reply)
3105 {
3106 Rect oldframe, oldclient;
3107 gboolean send_resize_client;
3108 gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
3109 gboolean fmoved, fresized;
3110 guint fdecor = self->frame->decorations;
3111 gboolean fhorz = self->frame->max_horz;
3112 gboolean fvert = self->frame->max_vert;
3113 gint logicalw, logicalh;
3114
3115 /* find the new x, y, width, and height (and logical size) */
3116 client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
3117
3118 /* set the logical size if things changed */
3119 if (!(w == self->area.width && h == self->area.height))
3120 SIZE_SET(self->logical_size, logicalw, logicalh);
3121
3122 /* figure out if we moved or resized or what */
3123 moved = (x != self->area.x || y != self->area.y);
3124 resized = (w != self->area.width || h != self->area.height);
3125
3126 oldframe = self->frame->area;
3127 oldclient = self->area;
3128 RECT_SET(self->area, x, y, w, h);
3129
3130 /* for app-requested resizes, always resize if 'resized' is true.
3131 for user-requested ones, only resize if final is true, or when
3132 resizing in redraw mode */
3133 send_resize_client = ((!user && resized) ||
3134 (user && (final ||
3135 (resized && config_resize_redraw))));
3136
3137 /* if the client is enlarging, then resize the client before the frame */
3138 if (send_resize_client && (w > oldclient.width || h > oldclient.height)) {
3139 XMoveResizeWindow(obt_display, self->window,
3140 self->frame->size.left, self->frame->size.top,
3141 MAX(w, oldclient.width), MAX(h, oldclient.height));
3142 frame_adjust_client_area(self->frame);
3143 }
3144
3145 /* find the frame's dimensions and move/resize it */
3146 fmoved = moved;
3147 fresized = resized;
3148
3149 /* if decorations changed, then readjust everything for the frame */
3150 if (self->decorations != fdecor ||
3151 self->max_horz != fhorz || self->max_vert != fvert)
3152 {
3153 fmoved = fresized = TRUE;
3154 }
3155
3156 /* adjust the frame */
3157 if (fmoved || fresized) {
3158 gulong ignore_start;
3159 if (!user)
3160 ignore_start = event_start_ignore_all_enters();
3161
3162 /* replay pending pointer event before move the window, in case it
3163 would change what window gets the event */
3164 mouse_replay_pointer();
3165
3166 frame_adjust_area(self->frame, fmoved, fresized, FALSE);
3167
3168 if (!user)
3169 event_end_ignore_all_enters(ignore_start);
3170 }
3171
3172 if (!user || final) {
3173 gint oldrx = self->root_pos.x;
3174 gint oldry = self->root_pos.y;
3175 /* we have reset the client to 0 border width, so don't include
3176 it in these coords */
3177 POINT_SET(self->root_pos,
3178 self->frame->area.x + self->frame->size.left -
3179 self->border_width,
3180 self->frame->area.y + self->frame->size.top -
3181 self->border_width);
3182 if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
3183 rootmoved = TRUE;
3184 }
3185
3186 /* This is kinda tricky and should not be changed.. let me explain!
3187
3188 When user = FALSE, then the request is coming from the application
3189 itself, and we are more strict about when to send a synthetic
3190 ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
3191 in this case (or send one if force_reply is true)
3192
3193 When user = TRUE, then the request is coming from "us", like when we
3194 maximize a window or something. In this case we are more lenient. We
3195 used to follow the same rules as above, but _Java_ Swing can't handle
3196 this. So just to appease Swing, when user = TRUE, we always send
3197 a synthetic ConfigureNotify to give the window its root coordinates.
3198 Lastly, if force_reply is TRUE, we always send a
3199 ConfigureNotify, which is needed during a resize with XSYNCronization.
3200 */
3201 if ((!user && !resized && (rootmoved || force_reply)) ||
3202 (user && ((!resized && force_reply) || (final && rootmoved))))
3203 {
3204 XEvent event;
3205
3206 event.type = ConfigureNotify;
3207 event.xconfigure.display = obt_display;
3208 event.xconfigure.event = self->window;
3209 event.xconfigure.window = self->window;
3210
3211 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
3212 self->title, self->root_pos.x, self->root_pos.y, w, h);
3213
3214 /* root window real coords */
3215 event.xconfigure.x = self->root_pos.x;
3216 event.xconfigure.y = self->root_pos.y;
3217 event.xconfigure.width = w;
3218 event.xconfigure.height = h;
3219 event.xconfigure.border_width = self->border_width;
3220 event.xconfigure.above = None;
3221 event.xconfigure.override_redirect = FALSE;
3222 XSendEvent(event.xconfigure.display, event.xconfigure.window,
3223 FALSE, StructureNotifyMask, &event);
3224 }
3225
3226 /* if the client is shrinking, then resize the frame before the client.
3227
3228 both of these resize sections may run, because the top one only resizes
3229 in the direction that is growing
3230 */
3231 if (send_resize_client && (w <= oldclient.width || h <= oldclient.height))
3232 {
3233 frame_adjust_client_area(self->frame);
3234 XMoveResizeWindow(obt_display, self->window,
3235 self->frame->size.left, self->frame->size.top, w, h);
3236 }
3237
3238 XFlush(obt_display);
3239
3240 /* if it moved between monitors, then this can affect the stacking
3241 layer of this window or others - for fullscreen windows.
3242 also if it changed to/from oldschool fullscreen then its layer may
3243 change
3244
3245 watch out tho, don't try change stacking stuff if the window is no
3246 longer being managed !
3247 */
3248 if (self->managed &&
3249 (screen_find_monitor(&self->frame->area) !=
3250 screen_find_monitor(&oldframe) ||
3251 (final && (client_is_oldfullscreen(self, &oldclient) !=
3252 client_is_oldfullscreen(self, &self->area)))))
3253 {
3254 client_calc_layer(self);
3255 }
3256 }
3257
3258 void client_fullscreen(ObClient *self, gboolean fs)
3259 {
3260 gint x, y, w, h;
3261
3262 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
3263 self->fullscreen == fs) return; /* already done */
3264
3265 self->fullscreen = fs;
3266 client_change_state(self); /* change the state hints on the client */
3267
3268 if (fs) {
3269 self->pre_fullscreen_area = self->area;
3270 self->pre_fullscreen_max_horz = self->max_horz;
3271 self->pre_fullscreen_max_vert = self->max_vert;
3272
3273 /* if the window is maximized, its area isn't all that meaningful.
3274 save its premax area instead. */
3275 if (self->max_horz) {
3276 self->pre_fullscreen_area.x = self->pre_max_area.x;
3277 self->pre_fullscreen_area.width = self->pre_max_area.width;
3278 }
3279 if (self->max_vert) {
3280 self->pre_fullscreen_area.y = self->pre_max_area.y;
3281 self->pre_fullscreen_area.height = self->pre_max_area.height;
3282 }
3283
3284 /* these will help configure_full figure out where to fullscreen
3285 the window */
3286 x = self->area.x;
3287 y = self->area.y;
3288 w = self->area.width;
3289 h = self->area.height;
3290 } else {
3291 g_assert(self->pre_fullscreen_area.width > 0 &&
3292 self->pre_fullscreen_area.height > 0);
3293
3294 self->max_horz = self->pre_fullscreen_max_horz;
3295 self->max_vert = self->pre_fullscreen_max_vert;
3296 if (self->max_horz) {
3297 self->pre_max_area.x = self->pre_fullscreen_area.x;
3298 self->pre_max_area.width = self->pre_fullscreen_area.width;
3299 }
3300 if (self->max_vert) {
3301 self->pre_max_area.y = self->pre_fullscreen_area.y;
3302 self->pre_max_area.height = self->pre_fullscreen_area.height;
3303 }
3304
3305 x = self->pre_fullscreen_area.x;
3306 y = self->pre_fullscreen_area.y;
3307 w = self->pre_fullscreen_area.width;
3308 h = self->pre_fullscreen_area.height;
3309 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
3310 }
3311
3312 ob_debug("Window %s going fullscreen (%d)",
3313 self->title, self->fullscreen);
3314
3315 if (fs) {
3316 /* make sure the window is on some monitor */
3317 client_find_onscreen(self, &x, &y, w, h, FALSE);
3318 }
3319
3320 client_setup_decor_and_functions(self, FALSE);
3321 client_move_resize(self, x, y, w, h);
3322
3323 /* and adjust our layer/stacking. do this after resizing the window,
3324 and applying decorations, because windows which fill the screen are
3325 considered "fullscreen" and it affects their layer */
3326 client_calc_layer(self);
3327
3328 if (fs) {
3329 /* try focus us when we go into fullscreen mode */
3330 client_focus(self);
3331 }
3332 }
3333
3334 static void client_iconify_recursive(ObClient *self,
3335 gboolean iconic, gboolean curdesk,
3336 gboolean hide_animation)
3337 {
3338 GSList *it;
3339 gboolean changed = FALSE;
3340
3341 if (self->iconic != iconic) {
3342 ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
3343 self->window);
3344
3345 if (iconic) {
3346 /* don't let non-normal windows iconify along with their parents
3347 or whatever */
3348 if (client_normal(self)) {
3349 self->iconic = iconic;
3350
3351 /* update the focus lists.. iconic windows go to the bottom of
3352 the list. this will also call focus_cycle_addremove(). */
3353 focus_order_to_bottom(self);
3354
3355 changed = TRUE;
3356 }
3357 } else {
3358 self->iconic = iconic;
3359
3360 if (curdesk && self->desktop != screen_desktop &&
3361 self->desktop != DESKTOP_ALL)
3362 client_set_desktop(self, screen_desktop, FALSE, FALSE);
3363
3364 /* this puts it after the current focused window, this will
3365 also cause focus_cycle_addremove() to be called for the
3366 client */
3367 focus_order_like_new(self);
3368
3369 changed = TRUE;
3370 }
3371 }
3372
3373 if (changed) {
3374 client_change_state(self);
3375 if (config_animate_iconify && !hide_animation)
3376 frame_begin_iconify_animation(self->frame, iconic);
3377 /* do this after starting the animation so it doesn't flash */
3378 client_showhide(self);
3379 }
3380
3381 /* iconify all direct transients, and deiconify all transients
3382 (non-direct too) */
3383 for (it = self->transients; it; it = g_slist_next(it))
3384 if (it->data != self)
3385 if (client_is_direct_child(self, it->data) || !iconic)
3386 client_iconify_recursive(it->data, iconic, curdesk,
3387 hide_animation);
3388 }
3389
3390 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
3391 gboolean hide_animation)
3392 {
3393 if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
3394 /* move up the transient chain as far as possible first */
3395 self = client_search_top_direct_parent(self);
3396 client_iconify_recursive(self, iconic, curdesk, hide_animation);
3397 }
3398 }
3399
3400 void client_maximize(ObClient *self, gboolean max, gint dir)
3401 {
3402 gint x, y, w, h;
3403
3404 g_assert(dir == 0 || dir == 1 || dir == 2);
3405 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
3406
3407 /* check if already done */
3408 if (max) {
3409 if (dir == 0 && self->max_horz && self->max_vert) return;
3410 if (dir == 1 && self->max_horz) return;
3411 if (dir == 2 && self->max_vert) return;
3412 } else {
3413 if (dir == 0 && !self->max_horz && !self->max_vert) return;
3414 if (dir == 1 && !self->max_horz) return;
3415 if (dir == 2 && !self->max_vert) return;
3416 }
3417
3418 /* these will help configure_full figure out which screen to fill with
3419 the window */
3420 x = self->area.x;
3421 y = self->area.y;
3422 w = self->area.width;
3423 h = self->area.height;
3424
3425 if (max) {
3426 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
3427 RECT_SET(self->pre_max_area,
3428 self->area.x, self->pre_max_area.y,
3429 self->area.width, self->pre_max_area.height);
3430 }
3431 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
3432 RECT_SET(self->pre_max_area,
3433 self->pre_max_area.x, self->area.y,
3434 self->pre_max_area.width, self->area.height);
3435 }
3436 } else {
3437 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
3438 g_assert(self->pre_max_area.width > 0);
3439
3440 x = self->pre_max_area.x;
3441 w = self->pre_max_area.width;
3442
3443 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
3444 0, self->pre_max_area.height);
3445 }
3446 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
3447 g_assert(self->pre_max_area.height > 0);
3448
3449 y = self->pre_max_area.y;
3450 h = self->pre_max_area.height;
3451
3452 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
3453 self->pre_max_area.width, 0);
3454 }
3455 }
3456
3457 if (dir == 0 || dir == 1) /* horz */
3458 self->max_horz = max;
3459 if (dir == 0 || dir == 2) /* vert */
3460 self->max_vert = max;
3461
3462 if (max) {
3463 /* make sure the window is on some monitor */
3464 client_find_onscreen(self, &x, &y, w, h, FALSE);
3465 }
3466
3467 client_change_state(self); /* change the state hints on the client */
3468
3469 client_setup_decor_and_functions(self, FALSE);
3470 client_move_resize(self, x, y, w, h);
3471 }
3472
3473 void client_shade(ObClient *self, gboolean shade)
3474 {
3475 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
3476 shade) || /* can't shade */
3477 self->shaded == shade) return; /* already done */
3478
3479 self->shaded = shade;
3480 client_change_state(self);
3481 client_change_wm_state(self); /* the window is being hidden/shown */
3482 /* resize the frame to just the titlebar */
3483 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
3484 }
3485
3486 static void client_ping_event(ObClient *self, gboolean dead)
3487 {
3488 if (self->not_responding != dead) {
3489 self->not_responding = dead;
3490 client_update_title(self);
3491
3492 if (dead)
3493 /* the client isn't responding, so ask to kill it */
3494 client_prompt_kill(self);
3495 else {
3496 /* it came back to life ! */
3497
3498 if (self->kill_prompt) {
3499 prompt_unref(self->kill_prompt);
3500 self->kill_prompt = NULL;
3501 }
3502
3503 self->kill_level = 0;
3504 }
3505 }
3506 }
3507
3508 void client_close(ObClient *self)
3509 {
3510 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3511
3512 /* if closing an internal obprompt, that is just cancelling it */
3513 if (self->prompt) {
3514 prompt_cancel(self->prompt);
3515 return;
3516 }
3517
3518 /* in the case that the client provides no means to requesting that it
3519 close, we just kill it */
3520 if (!self->delete_window)
3521 /* don't use client_kill(), we should only kill based on PID in
3522 response to a lack of PING replies */
3523 XKillClient(obt_display, self->window);
3524 else {
3525 /* request the client to close with WM_DELETE_WINDOW */
3526 OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS,
3527 OBT_PROP_ATOM(WM_DELETE_WINDOW), event_time(),
3528 0, 0, 0, NoEventMask);
3529
3530 /* we're trying to close the window, so see if it is responding. if it
3531 is not, then we will let them kill the window */
3532 if (self->ping)
3533 ping_start(self, client_ping_event);
3534
3535 /* if we already know the window isn't responding (maybe they clicked
3536 no in the kill dialog but it hasn't come back to life), then show
3537 the kill dialog */
3538 if (self->not_responding)
3539 client_prompt_kill(self);
3540 }
3541 }
3542
3543 #define OB_KILL_RESULT_NO 0
3544 #define OB_KILL_RESULT_YES 1
3545
3546 static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
3547 {
3548 ObClient *self = data;
3549
3550 if (result == OB_KILL_RESULT_YES)
3551 client_kill(self);
3552 return TRUE; /* call the cleanup func */
3553 }
3554
3555 static void client_kill_cleanup(ObPrompt *p, gpointer data)
3556 {
3557 ObClient *self = data;
3558
3559 g_assert(p == self->kill_prompt);
3560
3561 prompt_unref(self->kill_prompt);
3562 self->kill_prompt = NULL;
3563 }
3564
3565 static void client_prompt_kill(ObClient *self)
3566 {
3567 /* check if we're already prompting */
3568 if (!self->kill_prompt) {
3569 ObPromptAnswer answers[] = {
3570 { 0, OB_KILL_RESULT_NO },
3571 { 0, OB_KILL_RESULT_YES }
3572 };
3573 gchar *m;
3574 const gchar *y, *title;
3575
3576 title = self->original_title;
3577 if (title[0] == '\0') {
3578 /* empty string, so use its parent */
3579 ObClient *p = client_search_top_direct_parent(self);
3580 if (p) title = p->original_title;
3581 }
3582
3583 if (client_on_localhost(self)) {
3584 const gchar *sig;
3585
3586 if (self->kill_level == 0)
3587 sig = "terminate";
3588 else
3589 sig = "kill";
3590
3591 m = g_strdup_printf
3592 (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"),
3593 title, sig);
3594 y = _("End Process");
3595 }
3596 else {
3597 m = g_strdup_printf
3598 (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"),
3599 title);
3600 y = _("Disconnect");
3601 }
3602 /* set the dialog buttons' text */
3603 answers[0].text = _("Cancel"); /* "no" */
3604 answers[1].text = y; /* "yes" */
3605
3606 self->kill_prompt = prompt_new(m, NULL, answers,
3607 sizeof(answers)/sizeof(answers[0]),
3608 OB_KILL_RESULT_NO, /* default = no */
3609 OB_KILL_RESULT_NO, /* cancel = no */
3610 client_kill_requested,
3611 client_kill_cleanup,
3612 self);
3613 g_free(m);
3614 }
3615
3616 prompt_show(self->kill_prompt, self, TRUE);
3617 }
3618
3619 void client_kill(ObClient *self)
3620 {
3621 /* don't kill our own windows */
3622 if (self->prompt) return;
3623
3624 if (client_on_localhost(self) && self->pid) {
3625 /* running on the local host */
3626 if (self->kill_level == 0) {
3627 ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3628 self->window, self->pid);
3629 kill(self->pid, SIGTERM);
3630 ++self->kill_level;
3631
3632 /* show that we're trying to kill it */
3633 client_update_title(self);
3634 }
3635 else {
3636 ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
3637 self->window, self->pid);
3638 kill(self->pid, SIGKILL); /* kill -9 */
3639 }
3640 }
3641 else {
3642 /* running on a remote host */
3643 XKillClient(obt_display, self->window);
3644 }
3645 }
3646
3647 void client_hilite(ObClient *self, gboolean hilite)
3648 {
3649 if (self->demands_attention == hilite)
3650 return; /* no change */
3651
3652 /* don't allow focused windows to hilite */
3653 self->demands_attention = hilite && !client_focused(self);
3654 if (self->frame != NULL) { /* if we're mapping, just set the state */
3655 if (self->demands_attention) {
3656 frame_flash_start(self->frame);
3657
3658 /* if the window is on another desktop then raise it and make it
3659 the most recently used window */
3660 if (self->desktop != screen_desktop &&
3661 self->desktop != DESKTOP_ALL)
3662 {
3663 stacking_raise(CLIENT_AS_WINDOW(self));
3664 focus_order_to_top(self);
3665 }
3666 }
3667 else
3668 frame_flash_stop(self->frame);
3669 client_change_state(self);
3670 }
3671 }
3672
3673 static void client_set_desktop_recursive(ObClient *self,
3674 guint target,
3675 gboolean donthide,
3676 gboolean dontraise)
3677 {
3678 guint old;
3679 GSList *it;
3680
3681 if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
3682
3683 ob_debug("Setting desktop %u", target+1);
3684
3685 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3686
3687 old = self->desktop;
3688 self->desktop = target;
3689 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, target);
3690 /* the frame can display the current desktop state */
3691 frame_adjust_state(self->frame);
3692 /* 'move' the window to the new desktop */
3693 if (!donthide)
3694 client_hide(self);
3695 client_show(self);
3696 /* raise if it was not already on the desktop */
3697 if (old != DESKTOP_ALL && !dontraise)
3698 stacking_raise(CLIENT_AS_WINDOW(self));
3699 if (STRUT_EXISTS(self->strut))
3700 screen_update_areas();
3701 else
3702 /* the new desktop's geometry may be different, so we may need to
3703 resize, for example if we are maximized */
3704 client_reconfigure(self, FALSE);
3705
3706 focus_cycle_addremove(self, FALSE);
3707 }
3708
3709 /* move all transients */
3710 for (it = self->transients; it; it = g_slist_next(it))
3711 if (it->data != self)
3712 if (client_is_direct_child(self, it->data))
3713 client_set_desktop_recursive(it->data, target,
3714 donthide, dontraise);
3715 }
3716
3717 void client_set_desktop(ObClient *self, guint target,
3718 gboolean donthide, gboolean dontraise)
3719 {
3720 self = client_search_top_direct_parent(self);
3721 client_set_desktop_recursive(self, target, donthide, dontraise);
3722
3723 focus_cycle_addremove(NULL, TRUE);
3724 }
3725
3726 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3727 {
3728 while (child != parent && (child = client_direct_parent(child)));
3729 return child == parent;
3730 }
3731
3732 ObClient *client_search_modal_child(ObClient *self)
3733 {
3734 GSList *it;
3735 ObClient *ret;
3736
3737 for (it = self->transients; it; it = g_slist_next(it)) {
3738 ObClient *c = it->data;
3739 if ((ret = client_search_modal_child(c))) return ret;
3740 if (c->modal) return c;
3741 }
3742 return NULL;
3743 }
3744
3745 struct ObClientFindDestroyUnmap {
3746 Window window;
3747 gint ignore_unmaps;
3748 };
3749
3750 static gboolean find_destroy_unmap(XEvent *e, gpointer data)
3751 {
3752 struct ObClientFindDestroyUnmap *find = data;
3753 if (e->type == DestroyNotify)
3754 return e->xdestroywindow.window == find->window;
3755 if (e->type == UnmapNotify && e->xunmap.window == find->window)
3756 /* ignore the first $find->ignore_unmaps$ many unmap events */
3757 return --find->ignore_unmaps < 0;
3758 return FALSE;
3759 }
3760
3761 gboolean client_validate(ObClient *self)
3762 {
3763 struct ObClientFindDestroyUnmap find;
3764
3765 XSync(obt_display, FALSE); /* get all events on the server */
3766
3767 find.window = self->window;
3768 find.ignore_unmaps = self->ignore_unmaps;
3769 if (xqueue_exists_local(find_destroy_unmap, &find))
3770 return FALSE;
3771
3772 return TRUE;
3773 }
3774
3775 void client_set_wm_state(ObClient *self, glong state)
3776 {
3777 if (state == self->wmstate) return; /* no change */
3778
3779 switch (state) {
3780 case IconicState:
3781 client_iconify(self, TRUE, TRUE, FALSE);
3782 break;
3783 case NormalState:
3784 client_iconify(self, FALSE, TRUE, FALSE);
3785 break;
3786 }
3787 }
3788
3789 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3790 {
3791 gboolean shaded = self->shaded;
3792 gboolean fullscreen = self->fullscreen;
3793 gboolean undecorated = self->undecorated;
3794 gboolean max_horz = self->max_horz;
3795 gboolean max_vert = self->max_vert;
3796 gboolean modal = self->modal;
3797 gboolean iconic = self->iconic;
3798 gboolean demands_attention = self->demands_attention;
3799 gboolean above = self->above;
3800 gboolean below = self->below;
3801 gint i;
3802 gboolean value;
3803
3804 if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
3805 action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
3806 action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
3807 /* an invalid action was passed to the client message, ignore it */
3808 return;
3809
3810 for (i = 0; i < 2; ++i) {
3811 Atom state = i == 0 ? data1 : data2;
3812
3813 if (!state) continue;
3814
3815 /* if toggling, then pick whether we're adding or removing */
3816 if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
3817 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
3818 value = modal;
3819 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
3820 value = self->max_vert;
3821 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
3822 value = self->max_horz;
3823 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
3824 value = shaded;
3825 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
3826 value = self->skip_taskbar;
3827 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
3828 value = self->skip_pager;
3829 else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
3830 value = self->iconic;
3831 else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
3832 value = fullscreen;
3833 else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
3834 value = self->above;
3835 else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
3836 value = self->below;
3837 else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
3838 value = self->demands_attention;
3839 else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
3840 value = undecorated;
3841 else
3842 g_assert_not_reached();
3843 action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3844 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3845 }
3846
3847 value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD);
3848 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3849 modal = value;
3850 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3851 max_vert = value;
3852 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3853 max_horz = value;
3854 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3855 shaded = value;
3856 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3857 self->skip_taskbar = value;
3858 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3859 self->skip_pager = value;
3860 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3861 iconic = value;
3862 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3863 fullscreen = value;
3864 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3865 above = value;
3866 /* only unset below when setting above, otherwise you can't get to
3867 the normal layer */
3868 if (value)
3869 below = FALSE;
3870 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3871 /* and vice versa */
3872 if (value)
3873 above = FALSE;
3874 below = value;
3875 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3876 demands_attention = value;
3877 } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3878 undecorated = value;
3879 }
3880 }
3881
3882 if (max_horz != self->max_horz || max_vert != self->max_vert) {
3883 if (max_horz != self->max_horz && max_vert != self->max_vert) {
3884 /* toggling both */
3885 if (max_horz == max_vert) { /* both going the same way */
3886 client_maximize(self, max_horz, 0);
3887 } else {
3888 client_maximize(self, max_horz, 1);
3889 client_maximize(self, max_vert, 2);
3890 }
3891 } else {
3892 /* toggling one */
3893 if (max_horz != self->max_horz)
3894 client_maximize(self, max_horz, 1);
3895 else
3896 client_maximize(self, max_vert, 2);
3897 }
3898 }
3899 /* change fullscreen state before shading, as it will affect if the window
3900 can shade or not */
3901 if (fullscreen != self->fullscreen)
3902 client_fullscreen(self, fullscreen);
3903 if (shaded != self->shaded)
3904 client_shade(self, shaded);
3905 if (undecorated != self->undecorated)
3906 client_set_undecorated(self, undecorated);
3907 if (above != self->above || below != self->below) {
3908 self->above = above;
3909 self->below = below;
3910 client_calc_layer(self);
3911 }
3912
3913 if (modal != self->modal) {
3914 self->modal = modal;
3915 /* when a window changes modality, then its stacking order with its
3916 transients needs to change */
3917 stacking_raise(CLIENT_AS_WINDOW(self));
3918
3919 /* it also may get focused. if something is focused that shouldn't
3920 be focused anymore, then move the focus */
3921 if (focus_client && client_focus_target(focus_client) != focus_client)
3922 client_focus(focus_client);
3923 }
3924
3925 if (iconic != self->iconic)
3926 client_iconify(self, iconic, FALSE, FALSE);
3927
3928 if (demands_attention != self->demands_attention)
3929 client_hilite(self, demands_attention);
3930
3931 client_change_state(self); /* change the hint to reflect these changes */
3932
3933 focus_cycle_addremove(self, TRUE);
3934 }
3935
3936 ObClient *client_focus_target(ObClient *self)
3937 {
3938 ObClient *child = NULL;
3939
3940 child = client_search_modal_child(self);
3941 if (child) return child;
3942 return self;
3943 }
3944
3945 gboolean client_can_focus(ObClient *self)
3946 {
3947 /* choose the correct target */
3948 self = client_focus_target(self);
3949
3950 if (!self->frame->visible)
3951 return FALSE;
3952
3953 if (!(self->can_focus || self->focus_notify))
3954 return FALSE;
3955
3956 return TRUE;
3957 }
3958
3959 gboolean client_focus(ObClient *self)
3960 {
3961 if (!client_validate(self)) return FALSE;
3962
3963 /* we might not focus this window, so if we have modal children which would
3964 be focused instead, bring them to this desktop */
3965 client_bring_modal_windows(self);
3966
3967 /* choose the correct target */
3968 self = client_focus_target(self);
3969
3970 if (!client_can_focus(self)) {
3971 ob_debug_type(OB_DEBUG_FOCUS,
3972 "Client %s can't be focused", self->title);
3973 return FALSE;
3974 }
3975
3976 /* if we have helper windows they should be there with the window */
3977 client_bring_helper_windows(self);
3978
3979 ob_debug_type(OB_DEBUG_FOCUS,
3980 "Focusing client \"%s\" (0x%x) at time %u",
3981 self->title, self->window, event_time());
3982
3983 /* if using focus_delay, stop the timer now so that focus doesn't
3984 go moving on us */
3985 event_halt_focus_delay();
3986
3987 obt_display_ignore_errors(TRUE);
3988
3989 if (self->can_focus) {
3990 /* This can cause a BadMatch error with CurrentTime, or if an app
3991 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3992 XSetInputFocus(obt_display, self->window, RevertToPointerRoot,
3993 event_time());
3994 }
3995
3996 if (self->focus_notify) {
3997 XEvent ce;
3998 ce.xclient.type = ClientMessage;
3999 ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
4000 ce.xclient.display = obt_display;
4001 ce.xclient.window = self->window;
4002 ce.xclient.format = 32;
4003 ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
4004 ce.xclient.data.l[1] = event_time();
4005 ce.xclient.data.l[2] = 0l;
4006 ce.xclient.data.l[3] = 0l;
4007 ce.xclient.data.l[4] = 0l;
4008 XSendEvent(obt_display, self->window, FALSE, NoEventMask, &ce);
4009 }
4010
4011 obt_display_ignore_errors(FALSE);
4012
4013 ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
4014 obt_display_error_occured);
4015 return !obt_display_error_occured;
4016 }
4017
4018 static void client_present(ObClient *self, gboolean here, gboolean raise,
4019 gboolean unshade)
4020 {
4021 if (client_normal(self) && screen_showing_desktop)
4022 screen_show_desktop(FALSE, self);
4023 if (self->iconic)
4024 client_iconify(self, FALSE, here, FALSE);
4025 if (self->desktop != DESKTOP_ALL &&
4026 self->desktop != screen_desktop)
4027 {
4028 if (here)
4029 client_set_desktop(self, screen_desktop, FALSE, TRUE);
4030 else
4031 screen_set_desktop(self->desktop, FALSE);
4032 } else if (!self->frame->visible)
4033 /* if its not visible for other reasons, then don't mess
4034 with it */
4035 return;
4036 if (self->shaded && unshade)
4037 client_shade(self, FALSE);
4038 if (raise)
4039 stacking_raise(CLIENT_AS_WINDOW(self));
4040
4041 client_focus(self);
4042 }
4043
4044 /* this function exists to map to the net_active_window message in the ewmh */
4045 void client_activate(ObClient *self, gboolean desktop,
4046 gboolean here, gboolean raise,
4047 gboolean unshade, gboolean user)
4048 {
4049 self = client_focus_target(self);
4050
4051 if (client_can_steal_focus(self, desktop, user, event_time(), CurrentTime))
4052 client_present(self, here, raise, unshade);
4053 else
4054 client_hilite(self, TRUE);
4055 }
4056
4057 static void client_bring_windows_recursive(ObClient *self,
4058 guint desktop,
4059 gboolean helpers,
4060 gboolean modals,
4061 gboolean iconic)
4062 {
4063 GSList *it;
4064
4065 for (it = self->transients; it; it = g_slist_next(it))
4066 client_bring_windows_recursive(it->data, desktop,
4067 helpers, modals, iconic);
4068
4069 if (((helpers && client_helper(self)) ||
4070 (modals && self->modal)) &&
4071 (!screen_compare_desktops(self->desktop, desktop) ||
4072 (iconic && self->iconic)))
4073 {
4074 if (iconic && self->iconic)
4075 client_iconify(self, FALSE, TRUE, FALSE);
4076 else
4077 client_set_desktop(self, desktop, FALSE, FALSE);
4078 }
4079 }
4080
4081 void client_bring_helper_windows(ObClient *self)
4082 {
4083 client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
4084 }
4085
4086 void client_bring_modal_windows(ObClient *self)
4087 {
4088 client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
4089 }
4090
4091 gboolean client_focused(ObClient *self)
4092 {
4093 return self == focus_client;
4094 }
4095
4096 RrImage* client_icon(ObClient *self)
4097 {
4098 RrImage *ret = NULL;
4099
4100 if (self->icon_set)
4101 ret = self->icon_set;
4102 else if (self->parents) {
4103 GSList *it;
4104 for (it = self->parents; it && !ret; it = g_slist_next(it))
4105 ret = client_icon(it->data);
4106 }
4107 if (!ret)
4108 ret = client_default_icon;
4109 return ret;
4110 }
4111
4112 void client_set_layer(ObClient *self, gint layer)
4113 {
4114 if (layer < 0) {
4115 self->below = TRUE;
4116 self->above = FALSE;
4117 } else if (layer == 0) {
4118 self->below = self->above = FALSE;
4119 } else {
4120 self->below = FALSE;
4121 self->above = TRUE;
4122 }
4123 client_calc_layer(self);
4124 client_change_state(self); /* reflect this in the state hints */
4125 }
4126
4127 void client_set_undecorated(ObClient *self, gboolean undecorated)
4128 {
4129 if (self->undecorated != undecorated &&
4130 /* don't let it undecorate if the function is missing, but let
4131 it redecorate */
4132 (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
4133 {
4134 self->undecorated = undecorated;
4135 client_setup_decor_and_functions(self, TRUE);
4136 client_change_state(self); /* reflect this in the state hints */
4137 }
4138 }
4139
4140 guint client_monitor(ObClient *self)
4141 {
4142 return screen_find_monitor(&self->frame->area);
4143 }
4144
4145 ObClient *client_direct_parent(ObClient *self)
4146 {
4147 if (!self->parents) return NULL;
4148 if (self->transient_for_group) return NULL;
4149 return self->parents->data;
4150 }
4151
4152 ObClient *client_search_top_direct_parent(ObClient *self)
4153 {
4154 ObClient *p;
4155 while ((p = client_direct_parent(self))) self = p;
4156 return self;
4157 }
4158
4159 static GSList *client_search_all_top_parents_internal(ObClient *self,
4160 gboolean bylayer,
4161 ObStackingLayer layer)
4162 {
4163 GSList *ret;
4164 ObClient *p;
4165
4166 /* move up the direct transient chain as far as possible */
4167 while ((p = client_direct_parent(self)) &&
4168 (!bylayer || p->layer == layer))
4169 self = p;
4170
4171 if (!self->parents)
4172 ret = g_slist_prepend(NULL, self);
4173 else
4174 ret = g_slist_copy(self->parents);
4175
4176 return ret;
4177 }
4178
4179 GSList *client_search_all_top_parents(ObClient *self)
4180 {
4181 return client_search_all_top_parents_internal(self, FALSE, 0);
4182 }
4183
4184 GSList *client_search_all_top_parents_layer(ObClient *self)
4185 {
4186 return client_search_all_top_parents_internal(self, TRUE, self->layer);
4187 }
4188
4189 ObClient *client_search_focus_parent(ObClient *self)
4190 {
4191 GSList *it;
4192
4193 for (it = self->parents; it; it = g_slist_next(it))
4194 if (client_focused(it->data)) return it->data;
4195
4196 return NULL;
4197 }
4198
4199 ObClient *client_search_focus_parent_full(ObClient *self)
4200 {
4201 GSList *it;
4202 ObClient *ret = NULL;
4203
4204 for (it = self->parents; it; it = g_slist_next(it)) {
4205 if (client_focused(it->data))
4206 ret = it->data;
4207 else
4208 ret = client_search_focus_parent_full(it->data);
4209 if (ret) break;
4210 }
4211 return ret;
4212 }
4213
4214 ObClient *client_search_parent(ObClient *self, ObClient *search)
4215 {
4216 GSList *it;
4217
4218 for (it = self->parents; it; it = g_slist_next(it))
4219 if (it->data == search) return search;
4220
4221 return NULL;
4222 }
4223
4224 ObClient *client_search_transient(ObClient *self, ObClient *search)
4225 {
4226 GSList *sit;
4227
4228 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
4229 if (sit->data == search)
4230 return search;
4231 if (client_search_transient(sit->data, search))
4232 return search;
4233 }
4234 return NULL;
4235 }
4236
4237 static void detect_edge(Rect area, ObDirection dir,
4238 gint my_head, gint my_size,
4239 gint my_edge_start, gint my_edge_size,
4240 gint *dest, gboolean *near_edge)
4241 {
4242 gint edge_start, edge_size, head, tail;
4243 gboolean skip_head = FALSE, skip_tail = FALSE;
4244
4245 switch (dir) {
4246 case OB_DIRECTION_NORTH:
4247 case OB_DIRECTION_SOUTH:
4248 edge_start = area.x;
4249 edge_size = area.width;
4250 break;
4251 case OB_DIRECTION_EAST:
4252 case OB_DIRECTION_WEST:
4253 edge_start = area.y;
4254 edge_size = area.height;
4255 break;
4256 default:
4257 g_assert_not_reached();
4258 }
4259
4260 /* do we collide with this window? */
4261 if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
4262 edge_start, edge_size))
4263 return;
4264
4265 switch (dir) {
4266 case OB_DIRECTION_NORTH:
4267 head = RECT_BOTTOM(area);
4268 tail = RECT_TOP(area);
4269 break;
4270 case OB_DIRECTION_SOUTH:
4271 head = RECT_TOP(area);
4272 tail = RECT_BOTTOM(area);
4273 break;
4274 case OB_DIRECTION_WEST:
4275 head = RECT_RIGHT(area);
4276 tail = RECT_LEFT(area);
4277 break;
4278 case OB_DIRECTION_EAST:
4279 head = RECT_LEFT(area);
4280 tail = RECT_RIGHT(area);
4281 break;
4282 default:
4283 g_assert_not_reached();
4284 }
4285 switch (dir) {
4286 case OB_DIRECTION_NORTH:
4287 case OB_DIRECTION_WEST:
4288 /* check if our window is past the head of this window */
4289 if (my_head <= head + 1)
4290 skip_head = TRUE;
4291 /* check if our window's tail is past the tail of this window */
4292 if (my_head + my_size - 1 <= tail)
4293 skip_tail = TRUE;
4294 /* check if the head of this window is closer than the previously
4295 chosen edge (take into account that the previously chosen
4296 edge might have been a tail, not a head) */
4297 if (head + (*near_edge ? 0 : my_size) <= *dest)
4298 skip_head = TRUE;
4299 /* check if the tail of this window is closer than the previously
4300 chosen edge (take into account that the previously chosen
4301 edge might have been a head, not a tail) */
4302 if (tail - (!*near_edge ? 0 : my_size) <= *dest)
4303 skip_tail = TRUE;
4304 break;
4305 case OB_DIRECTION_SOUTH:
4306 case OB_DIRECTION_EAST:
4307 /* check if our window is past the head of this window */
4308 if (my_head >= head - 1)
4309 skip_head = TRUE;
4310 /* check if our window's tail is past the tail of this window */
4311 if (my_head - my_size + 1 >= tail)
4312 skip_tail = TRUE;
4313 /* check if the head of this window is closer than the previously
4314 chosen edge (take into account that the previously chosen
4315 edge might have been a tail, not a head) */
4316 if (head - (*near_edge ? 0 : my_size) >= *dest)
4317 skip_head = TRUE;
4318 /* check if the tail of this window is closer than the previously
4319 chosen edge (take into account that the previously chosen
4320 edge might have been a head, not a tail) */
4321 if (tail + (!*near_edge ? 0 : my_size) >= *dest)
4322 skip_tail = TRUE;
4323 break;
4324 default:
4325 g_assert_not_reached();
4326 }
4327
4328 ob_debug("my head %d size %d", my_head, my_size);
4329 ob_debug("head %d tail %d dest %d", head, tail, *dest);
4330 if (!skip_head) {
4331 ob_debug("using near edge %d", head);
4332 *dest = head;
4333 *near_edge = TRUE;
4334 }
4335 else if (!skip_tail) {
4336 ob_debug("using far edge %d", tail);
4337 *dest = tail;
4338 *near_edge = FALSE;
4339 }
4340 }
4341
4342 void client_find_edge_directional(ObClient *self, ObDirection dir,
4343 gint my_head, gint my_size,
4344 gint my_edge_start, gint my_edge_size,
4345 gint *dest, gboolean *near_edge)
4346 {
4347 GList *it;
4348 Rect *a;
4349 Rect dock_area;
4350 gint edge;
4351 guint i;
4352
4353 a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
4354 &self->frame->area);
4355
4356 switch (dir) {
4357 case OB_DIRECTION_NORTH:
4358 edge = RECT_TOP(*a) - 1;
4359 break;
4360 case OB_DIRECTION_SOUTH:
4361 edge = RECT_BOTTOM(*a) + 1;
4362 break;
4363 case OB_DIRECTION_EAST:
4364 edge = RECT_RIGHT(*a) + 1;
4365 break;
4366 case OB_DIRECTION_WEST:
4367 edge = RECT_LEFT(*a) - 1;
4368 break;
4369 default:
4370 g_assert_not_reached();
4371 }
4372 /* default to the far edge, then narrow it down */
4373 *dest = edge;
4374 *near_edge = TRUE;
4375
4376 /* search for edges of monitors */
4377 for (i = 0; i < screen_num_monitors; ++i) {
4378 Rect *area = screen_area(self->desktop, i, NULL);
4379 detect_edge(*area, dir, my_head, my_size, my_edge_start,
4380 my_edge_size, dest, near_edge);
4381 g_slice_free(Rect, area);
4382 }
4383
4384 /* search for edges of clients */
4385 for (it = client_list; it; it = g_list_next(it)) {
4386 ObClient *cur = it->data;
4387
4388 /* skip windows to not bump into */
4389 if (cur == self)
4390 continue;
4391 if (cur->iconic)
4392 continue;
4393 if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
4394 cur->desktop != screen_desktop)
4395 continue;
4396
4397 ob_debug("trying window %s", cur->title);
4398
4399 detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
4400 my_edge_size, dest, near_edge);
4401 }
4402 dock_get_area(&dock_area);
4403 detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
4404 my_edge_size, dest, near_edge);
4405
4406 g_slice_free(Rect, a);
4407 }
4408
4409 void client_find_move_directional(ObClient *self, ObDirection dir,
4410 gint *x, gint *y)
4411 {
4412 gint head, size;
4413 gint e, e_start, e_size;
4414 gboolean near;
4415
4416 switch (dir) {
4417 case OB_DIRECTION_EAST:
4418 head = RECT_RIGHT(self->frame->area);
4419 size = self->frame->area.width;
4420 e_start = RECT_TOP(self->frame->area);
4421 e_size = self->frame->area.height;
4422 break;
4423 case OB_DIRECTION_WEST:
4424 head = RECT_LEFT(self->frame->area);
4425 size = self->frame->area.width;
4426 e_start = RECT_TOP(self->frame->area);
4427 e_size = self->frame->area.height;
4428 break;
4429 case OB_DIRECTION_NORTH:
4430 head = RECT_TOP(self->frame->area);
4431 size = self->frame->area.height;
4432 e_start = RECT_LEFT(self->frame->area);
4433 e_size = self->frame->area.width;
4434 break;
4435 case OB_DIRECTION_SOUTH:
4436 head = RECT_BOTTOM(self->frame->area);
4437 size = self->frame->area.height;
4438 e_start = RECT_LEFT(self->frame->area);
4439 e_size = self->frame->area.width;
4440 break;
4441 default:
4442 g_assert_not_reached();
4443 }
4444
4445 client_find_edge_directional(self, dir, head, size,
4446 e_start, e_size, &e, &near);
4447 *x = self->frame->area.x;
4448 *y = self->frame->area.y;
4449 switch (dir) {
4450 case OB_DIRECTION_EAST:
4451 if (near) e -= self->frame->area.width;
4452 else e++;
4453 *x = e;
4454 break;
4455 case OB_DIRECTION_WEST:
4456 if (near) e++;
4457 else e -= self->frame->area.width;
4458 *x = e;
4459 break;
4460 case OB_DIRECTION_NORTH:
4461 if (near) e++;
4462 else e -= self->frame->area.height;
4463 *y = e;
4464 break;
4465 case OB_DIRECTION_SOUTH:
4466 if (near) e -= self->frame->area.height;
4467 else e++;
4468 *y = e;
4469 break;
4470 default:
4471 g_assert_not_reached();
4472 }
4473 frame_frame_gravity(self->frame, x, y);
4474 }
4475
4476 void client_find_resize_directional(ObClient *self, ObDirection side,
4477 gboolean grow,
4478 gint *x, gint *y, gint *w, gint *h)
4479 {
4480 gint head;
4481 gint e, e_start, e_size, delta;
4482 gboolean near;
4483 ObDirection dir;
4484
4485 switch (side) {
4486 case OB_DIRECTION_EAST:
4487 head = RECT_RIGHT(self->frame->area) +
4488 (self->size_inc.width - 1) * (grow ? 1 : 0);
4489 e_start = RECT_TOP(self->frame->area);
4490 e_size = self->frame->area.height;
4491 dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
4492 break;
4493 case OB_DIRECTION_WEST:
4494 head = RECT_LEFT(self->frame->area) -
4495 (self->size_inc.width - 1) * (grow ? 1 : 0);
4496 e_start = RECT_TOP(self->frame->area);
4497 e_size = self->frame->area.height;
4498 dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
4499 break;
4500 case OB_DIRECTION_NORTH:
4501 head = RECT_TOP(self->frame->area) -
4502 (self->size_inc.height - 1) * (grow ? 1 : 0);
4503 e_start = RECT_LEFT(self->frame->area);
4504 e_size = self->frame->area.width;
4505 dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
4506 break;
4507 case OB_DIRECTION_SOUTH:
4508 head = RECT_BOTTOM(self->frame->area) +
4509 (self->size_inc.height - 1) * (grow ? 1 : 0);
4510 e_start = RECT_LEFT(self->frame->area);
4511 e_size = self->frame->area.width;
4512 dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
4513 break;
4514 default:
4515 g_assert_not_reached();
4516 }
4517
4518 ob_debug("head %d dir %d", head, dir);
4519 client_find_edge_directional(self, dir, head, 1,
4520 e_start, e_size, &e, &near);
4521 ob_debug("edge %d", e);
4522 *x = self->frame->area.x;
4523 *y = self->frame->area.y;
4524 *w = self->frame->area.width;
4525 *h = self->frame->area.height;
4526 switch (side) {
4527 case OB_DIRECTION_EAST:
4528 if (grow == near) --e;
4529 delta = e - RECT_RIGHT(self->frame->area);
4530 *w += delta;
4531 break;
4532 case OB_DIRECTION_WEST:
4533 if (grow == near) ++e;
4534 delta = RECT_LEFT(self->frame->area) - e;
4535 *x -= delta;
4536 *w += delta;
4537 break;
4538 case OB_DIRECTION_NORTH:
4539 if (grow == near) ++e;
4540 delta = RECT_TOP(self->frame->area) - e;
4541 *y -= delta;
4542 *h += delta;
4543 break;
4544 case OB_DIRECTION_SOUTH:
4545 if (grow == near) --e;
4546 delta = e - RECT_BOTTOM(self->frame->area);
4547 *h += delta;
4548 break;
4549 default:
4550 g_assert_not_reached();
4551 }
4552 frame_frame_gravity(self->frame, x, y);
4553 *w -= self->frame->size.left + self->frame->size.right;
4554 *h -= self->frame->size.top + self->frame->size.bottom;
4555 }
4556
4557 ObClient* client_under_pointer(void)
4558 {
4559 gint x, y;
4560 GList *it;
4561 ObClient *ret = NULL;
4562
4563 if (screen_pointer_pos(&x, &y)) {
4564 for (it = stacking_list; it; it = g_list_next(it)) {
4565 if (WINDOW_IS_CLIENT(it->data)) {
4566 ObClient *c = WINDOW_AS_CLIENT(it->data);
4567 if (c->frame->visible &&
4568 /* check the desktop, this is done during desktop
4569 switching and windows are shown/hidden status is not
4570 reliable */
4571 (c->desktop == screen_desktop ||
4572 c->desktop == DESKTOP_ALL) &&
4573 /* ignore all animating windows */
4574 !frame_iconify_animating(c->frame) &&
4575 RECT_CONTAINS(c->frame->area, x, y))
4576 {
4577 ret = c;
4578 break;
4579 }
4580 }
4581 }
4582 }
4583 return ret;
4584 }
4585
4586 gboolean client_has_group_siblings(ObClient *self)
4587 {
4588 return self->group && self->group->members->next;
4589 }
4590
4591 gboolean client_has_relative(ObClient *self)
4592 {
4593 return client_has_parent(self) ||
4594 client_has_group_siblings(self) ||
4595 client_has_children(self);
4596 }
4597
4598 /*! Returns TRUE if the client is running on the same machine as Openbox */
4599 gboolean client_on_localhost(ObClient *self)
4600 {
4601 return self->client_machine == NULL;
4602 }
This page took 0.248595 seconds and 5 git commands to generate.