]> Dogcows Code - chaz/openbox/blob - openbox/client.c
Fix window width/height going less than 1 on some platforms (bug #5246)
[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 (gint)G_MAXUSHORT
2934 - self->frame->size.left - self->frame->size.right);
2935 *h = MIN(*h,
2936 (gint)G_MAXUSHORT
2937 - self->frame->size.top - self->frame->size.bottom);
2938
2939 /* gets the frame's position */
2940 frame_client_gravity(self->frame, x, y);
2941
2942 /* these positions are frame positions, not client positions */
2943
2944 /* set the size and position if fullscreen */
2945 if (self->fullscreen) {
2946 const Rect *a;
2947 guint i;
2948
2949 i = screen_find_monitor(&desired);
2950 a = screen_physical_area_monitor(i);
2951
2952 *x = a->x;
2953 *y = a->y;
2954 *w = a->width;
2955 *h = a->height;
2956
2957 user = FALSE; /* ignore if the client can't be moved/resized when it
2958 is fullscreening */
2959 } else if (self->max_horz || self->max_vert) {
2960 Rect *a;
2961 guint i;
2962
2963 /* use all possible struts when maximizing to the full screen */
2964 i = screen_find_monitor(&desired);
2965 a = screen_area(self->desktop, i,
2966 (self->max_horz && self->max_vert ? NULL : &desired));
2967
2968 /* set the size and position if maximized */
2969 if (self->max_horz) {
2970 *x = a->x;
2971 *w = a->width - self->frame->size.left - self->frame->size.right;
2972 }
2973 if (self->max_vert) {
2974 *y = a->y;
2975 *h = a->height - self->frame->size.top - self->frame->size.bottom;
2976 }
2977
2978 user = FALSE; /* ignore if the client can't be moved/resized when it
2979 is maximizing */
2980
2981 g_slice_free(Rect, a);
2982 }
2983
2984 /* gets the client's position */
2985 frame_frame_gravity(self->frame, x, y);
2986
2987 /* work within the preferred sizes given by the window, these may have
2988 changed rather than it's requested width and height, so always run
2989 through this code */
2990 {
2991 gint basew, baseh, minw, minh;
2992 gint incw, inch, maxw, maxh;
2993 gfloat minratio, maxratio;
2994
2995 incw = self->size_inc.width;
2996 inch = self->size_inc.height;
2997 minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2998 0 : self->min_ratio;
2999 maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
3000 0 : self->max_ratio;
3001
3002 /* base size is substituted with min size if not specified */
3003 if (self->base_size.width >= 0 || self->base_size.height >= 0) {
3004 basew = self->base_size.width;
3005 baseh = self->base_size.height;
3006 } else {
3007 basew = self->min_size.width;
3008 baseh = self->min_size.height;
3009 }
3010 /* min size is substituted with base size if not specified */
3011 if (self->min_size.width || self->min_size.height) {
3012 minw = self->min_size.width;
3013 minh = self->min_size.height;
3014 } else {
3015 minw = self->base_size.width;
3016 minh = self->base_size.height;
3017 }
3018
3019 /* This comment is no longer true */
3020 /* if this is a user-requested resize, then check against min/max
3021 sizes */
3022
3023 /* smaller than min size or bigger than max size? */
3024 if (*w > self->max_size.width) *w = self->max_size.width;
3025 if (*w < minw) *w = minw;
3026 if (*h > self->max_size.height) *h = self->max_size.height;
3027 if (*h < minh) *h = minh;
3028
3029 *w -= basew;
3030 *h -= baseh;
3031
3032 /* the sizes to used for maximized */
3033 maxw = *w;
3034 maxh = *h;
3035
3036 /* keep to the increments */
3037 *w /= incw;
3038 *h /= inch;
3039
3040 /* you cannot resize to nothing */
3041 if (basew + *w < 1) *w = 1 - basew;
3042 if (baseh + *h < 1) *h = 1 - baseh;
3043
3044 /* save the logical size */
3045 *logicalw = incw > 1 ? *w : *w + basew;
3046 *logicalh = inch > 1 ? *h : *h + baseh;
3047
3048 *w *= incw;
3049 *h *= inch;
3050
3051 /* if maximized/fs then don't use the size increments */
3052 if (self->fullscreen || self->max_horz) *w = maxw;
3053 if (self->fullscreen || self->max_vert) *h = maxh;
3054
3055 *w += basew;
3056 *h += baseh;
3057
3058 /* adjust the height to match the width for the aspect ratios.
3059 for this, min size is not substituted for base size ever. */
3060 *w -= self->base_size.width;
3061 *h -= self->base_size.height;
3062
3063 if (minratio)
3064 if (*h * minratio > *w) {
3065 *h = (gint)(*w / minratio);
3066
3067 /* you cannot resize to nothing */
3068 if (*h < 1) {
3069 *h = 1;
3070 *w = (gint)(*h * minratio);
3071 }
3072 }
3073 if (maxratio)
3074 if (*h * maxratio < *w) {
3075 *h = (gint)(*w / maxratio);
3076
3077 /* you cannot resize to nothing */
3078 if (*h < 1) {
3079 *h = 1;
3080 *w = (gint)(*h * minratio);
3081 }
3082 }
3083
3084 *w += self->base_size.width;
3085 *h += self->base_size.height;
3086 }
3087
3088 /* these override the above states! if you cant move you can't move! */
3089 if (user) {
3090 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
3091 *x = self->area.x;
3092 *y = self->area.y;
3093 }
3094 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
3095 *w = self->area.width;
3096 *h = self->area.height;
3097 }
3098 }
3099
3100 g_assert(*w > 0);
3101 g_assert(*h > 0);
3102 }
3103
3104 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
3105 gboolean user, gboolean final, gboolean force_reply)
3106 {
3107 Rect oldframe, oldclient;
3108 gboolean send_resize_client;
3109 gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
3110 gboolean fmoved, fresized;
3111 guint fdecor = self->frame->decorations;
3112 gboolean fhorz = self->frame->max_horz;
3113 gboolean fvert = self->frame->max_vert;
3114 gint logicalw, logicalh;
3115
3116 /* find the new x, y, width, and height (and logical size) */
3117 client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
3118
3119 /* set the logical size if things changed */
3120 if (!(w == self->area.width && h == self->area.height))
3121 SIZE_SET(self->logical_size, logicalw, logicalh);
3122
3123 /* figure out if we moved or resized or what */
3124 moved = (x != self->area.x || y != self->area.y);
3125 resized = (w != self->area.width || h != self->area.height);
3126
3127 oldframe = self->frame->area;
3128 oldclient = self->area;
3129 RECT_SET(self->area, x, y, w, h);
3130
3131 /* for app-requested resizes, always resize if 'resized' is true.
3132 for user-requested ones, only resize if final is true, or when
3133 resizing in redraw mode */
3134 send_resize_client = ((!user && resized) ||
3135 (user && (final ||
3136 (resized && config_resize_redraw))));
3137
3138 /* if the client is enlarging, then resize the client before the frame */
3139 if (send_resize_client && (w > oldclient.width || h > oldclient.height)) {
3140 XMoveResizeWindow(obt_display, self->window,
3141 self->frame->size.left, self->frame->size.top,
3142 MAX(w, oldclient.width), MAX(h, oldclient.height));
3143 frame_adjust_client_area(self->frame);
3144 }
3145
3146 /* find the frame's dimensions and move/resize it */
3147 fmoved = moved;
3148 fresized = resized;
3149
3150 /* if decorations changed, then readjust everything for the frame */
3151 if (self->decorations != fdecor ||
3152 self->max_horz != fhorz || self->max_vert != fvert)
3153 {
3154 fmoved = fresized = TRUE;
3155 }
3156
3157 /* adjust the frame */
3158 if (fmoved || fresized) {
3159 gulong ignore_start;
3160 if (!user)
3161 ignore_start = event_start_ignore_all_enters();
3162
3163 /* replay pending pointer event before move the window, in case it
3164 would change what window gets the event */
3165 mouse_replay_pointer();
3166
3167 frame_adjust_area(self->frame, fmoved, fresized, FALSE);
3168
3169 if (!user)
3170 event_end_ignore_all_enters(ignore_start);
3171 }
3172
3173 if (!user || final) {
3174 gint oldrx = self->root_pos.x;
3175 gint oldry = self->root_pos.y;
3176 /* we have reset the client to 0 border width, so don't include
3177 it in these coords */
3178 POINT_SET(self->root_pos,
3179 self->frame->area.x + self->frame->size.left -
3180 self->border_width,
3181 self->frame->area.y + self->frame->size.top -
3182 self->border_width);
3183 if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
3184 rootmoved = TRUE;
3185 }
3186
3187 /* This is kinda tricky and should not be changed.. let me explain!
3188
3189 When user = FALSE, then the request is coming from the application
3190 itself, and we are more strict about when to send a synthetic
3191 ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
3192 in this case (or send one if force_reply is true)
3193
3194 When user = TRUE, then the request is coming from "us", like when we
3195 maximize a window or something. In this case we are more lenient. We
3196 used to follow the same rules as above, but _Java_ Swing can't handle
3197 this. So just to appease Swing, when user = TRUE, we always send
3198 a synthetic ConfigureNotify to give the window its root coordinates.
3199 Lastly, if force_reply is TRUE, we always send a
3200 ConfigureNotify, which is needed during a resize with XSYNCronization.
3201 */
3202 if ((!user && !resized && (rootmoved || force_reply)) ||
3203 (user && ((!resized && force_reply) || (final && rootmoved))))
3204 {
3205 XEvent event;
3206
3207 event.type = ConfigureNotify;
3208 event.xconfigure.display = obt_display;
3209 event.xconfigure.event = self->window;
3210 event.xconfigure.window = self->window;
3211
3212 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
3213 self->title, self->root_pos.x, self->root_pos.y, w, h);
3214
3215 /* root window real coords */
3216 event.xconfigure.x = self->root_pos.x;
3217 event.xconfigure.y = self->root_pos.y;
3218 event.xconfigure.width = w;
3219 event.xconfigure.height = h;
3220 event.xconfigure.border_width = self->border_width;
3221 event.xconfigure.above = None;
3222 event.xconfigure.override_redirect = FALSE;
3223 XSendEvent(event.xconfigure.display, event.xconfigure.window,
3224 FALSE, StructureNotifyMask, &event);
3225 }
3226
3227 /* if the client is shrinking, then resize the frame before the client.
3228
3229 both of these resize sections may run, because the top one only resizes
3230 in the direction that is growing
3231 */
3232 if (send_resize_client && (w <= oldclient.width || h <= oldclient.height))
3233 {
3234 frame_adjust_client_area(self->frame);
3235 XMoveResizeWindow(obt_display, self->window,
3236 self->frame->size.left, self->frame->size.top, w, h);
3237 }
3238
3239 XFlush(obt_display);
3240
3241 /* if it moved between monitors, then this can affect the stacking
3242 layer of this window or others - for fullscreen windows.
3243 also if it changed to/from oldschool fullscreen then its layer may
3244 change
3245
3246 watch out tho, don't try change stacking stuff if the window is no
3247 longer being managed !
3248 */
3249 if (self->managed &&
3250 (screen_find_monitor(&self->frame->area) !=
3251 screen_find_monitor(&oldframe) ||
3252 (final && (client_is_oldfullscreen(self, &oldclient) !=
3253 client_is_oldfullscreen(self, &self->area)))))
3254 {
3255 client_calc_layer(self);
3256 }
3257 }
3258
3259 void client_fullscreen(ObClient *self, gboolean fs)
3260 {
3261 gint x, y, w, h;
3262
3263 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
3264 self->fullscreen == fs) return; /* already done */
3265
3266 self->fullscreen = fs;
3267 client_change_state(self); /* change the state hints on the client */
3268
3269 if (fs) {
3270 self->pre_fullscreen_area = self->area;
3271 self->pre_fullscreen_max_horz = self->max_horz;
3272 self->pre_fullscreen_max_vert = self->max_vert;
3273
3274 /* if the window is maximized, its area isn't all that meaningful.
3275 save its premax area instead. */
3276 if (self->max_horz) {
3277 self->pre_fullscreen_area.x = self->pre_max_area.x;
3278 self->pre_fullscreen_area.width = self->pre_max_area.width;
3279 }
3280 if (self->max_vert) {
3281 self->pre_fullscreen_area.y = self->pre_max_area.y;
3282 self->pre_fullscreen_area.height = self->pre_max_area.height;
3283 }
3284
3285 /* these will help configure_full figure out where to fullscreen
3286 the window */
3287 x = self->area.x;
3288 y = self->area.y;
3289 w = self->area.width;
3290 h = self->area.height;
3291 } else {
3292 g_assert(self->pre_fullscreen_area.width > 0 &&
3293 self->pre_fullscreen_area.height > 0);
3294
3295 self->max_horz = self->pre_fullscreen_max_horz;
3296 self->max_vert = self->pre_fullscreen_max_vert;
3297 if (self->max_horz) {
3298 self->pre_max_area.x = self->pre_fullscreen_area.x;
3299 self->pre_max_area.width = self->pre_fullscreen_area.width;
3300 }
3301 if (self->max_vert) {
3302 self->pre_max_area.y = self->pre_fullscreen_area.y;
3303 self->pre_max_area.height = self->pre_fullscreen_area.height;
3304 }
3305
3306 x = self->pre_fullscreen_area.x;
3307 y = self->pre_fullscreen_area.y;
3308 w = self->pre_fullscreen_area.width;
3309 h = self->pre_fullscreen_area.height;
3310 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
3311 }
3312
3313 ob_debug("Window %s going fullscreen (%d)",
3314 self->title, self->fullscreen);
3315
3316 if (fs) {
3317 /* make sure the window is on some monitor */
3318 client_find_onscreen(self, &x, &y, w, h, FALSE);
3319 }
3320
3321 client_setup_decor_and_functions(self, FALSE);
3322 client_move_resize(self, x, y, w, h);
3323
3324 /* and adjust our layer/stacking. do this after resizing the window,
3325 and applying decorations, because windows which fill the screen are
3326 considered "fullscreen" and it affects their layer */
3327 client_calc_layer(self);
3328
3329 if (fs) {
3330 /* try focus us when we go into fullscreen mode */
3331 client_focus(self);
3332 }
3333 }
3334
3335 static void client_iconify_recursive(ObClient *self,
3336 gboolean iconic, gboolean curdesk,
3337 gboolean hide_animation)
3338 {
3339 GSList *it;
3340 gboolean changed = FALSE;
3341
3342 if (self->iconic != iconic) {
3343 ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
3344 self->window);
3345
3346 if (iconic) {
3347 /* don't let non-normal windows iconify along with their parents
3348 or whatever */
3349 if (client_normal(self)) {
3350 self->iconic = iconic;
3351
3352 /* update the focus lists.. iconic windows go to the bottom of
3353 the list. this will also call focus_cycle_addremove(). */
3354 focus_order_to_bottom(self);
3355
3356 changed = TRUE;
3357 }
3358 } else {
3359 self->iconic = iconic;
3360
3361 if (curdesk && self->desktop != screen_desktop &&
3362 self->desktop != DESKTOP_ALL)
3363 client_set_desktop(self, screen_desktop, FALSE, FALSE);
3364
3365 /* this puts it after the current focused window, this will
3366 also cause focus_cycle_addremove() to be called for the
3367 client */
3368 focus_order_like_new(self);
3369
3370 changed = TRUE;
3371 }
3372 }
3373
3374 if (changed) {
3375 client_change_state(self);
3376 if (config_animate_iconify && !hide_animation)
3377 frame_begin_iconify_animation(self->frame, iconic);
3378 /* do this after starting the animation so it doesn't flash */
3379 client_showhide(self);
3380 }
3381
3382 /* iconify all direct transients, and deiconify all transients
3383 (non-direct too) */
3384 for (it = self->transients; it; it = g_slist_next(it))
3385 if (it->data != self)
3386 if (client_is_direct_child(self, it->data) || !iconic)
3387 client_iconify_recursive(it->data, iconic, curdesk,
3388 hide_animation);
3389 }
3390
3391 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
3392 gboolean hide_animation)
3393 {
3394 if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
3395 /* move up the transient chain as far as possible first */
3396 self = client_search_top_direct_parent(self);
3397 client_iconify_recursive(self, iconic, curdesk, hide_animation);
3398 }
3399 }
3400
3401 void client_maximize(ObClient *self, gboolean max, gint dir)
3402 {
3403 gint x, y, w, h;
3404
3405 g_assert(dir == 0 || dir == 1 || dir == 2);
3406 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
3407
3408 /* check if already done */
3409 if (max) {
3410 if (dir == 0 && self->max_horz && self->max_vert) return;
3411 if (dir == 1 && self->max_horz) return;
3412 if (dir == 2 && self->max_vert) return;
3413 } else {
3414 if (dir == 0 && !self->max_horz && !self->max_vert) return;
3415 if (dir == 1 && !self->max_horz) return;
3416 if (dir == 2 && !self->max_vert) return;
3417 }
3418
3419 /* these will help configure_full figure out which screen to fill with
3420 the window */
3421 x = self->area.x;
3422 y = self->area.y;
3423 w = self->area.width;
3424 h = self->area.height;
3425
3426 if (max) {
3427 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
3428 RECT_SET(self->pre_max_area,
3429 self->area.x, self->pre_max_area.y,
3430 self->area.width, self->pre_max_area.height);
3431 }
3432 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
3433 RECT_SET(self->pre_max_area,
3434 self->pre_max_area.x, self->area.y,
3435 self->pre_max_area.width, self->area.height);
3436 }
3437 } else {
3438 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
3439 g_assert(self->pre_max_area.width > 0);
3440
3441 x = self->pre_max_area.x;
3442 w = self->pre_max_area.width;
3443
3444 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
3445 0, self->pre_max_area.height);
3446 }
3447 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
3448 g_assert(self->pre_max_area.height > 0);
3449
3450 y = self->pre_max_area.y;
3451 h = self->pre_max_area.height;
3452
3453 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
3454 self->pre_max_area.width, 0);
3455 }
3456 }
3457
3458 if (dir == 0 || dir == 1) /* horz */
3459 self->max_horz = max;
3460 if (dir == 0 || dir == 2) /* vert */
3461 self->max_vert = max;
3462
3463 if (max) {
3464 /* make sure the window is on some monitor */
3465 client_find_onscreen(self, &x, &y, w, h, FALSE);
3466 }
3467
3468 client_change_state(self); /* change the state hints on the client */
3469
3470 client_setup_decor_and_functions(self, FALSE);
3471 client_move_resize(self, x, y, w, h);
3472 }
3473
3474 void client_shade(ObClient *self, gboolean shade)
3475 {
3476 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
3477 shade) || /* can't shade */
3478 self->shaded == shade) return; /* already done */
3479
3480 self->shaded = shade;
3481 client_change_state(self);
3482 client_change_wm_state(self); /* the window is being hidden/shown */
3483 /* resize the frame to just the titlebar */
3484 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
3485 }
3486
3487 static void client_ping_event(ObClient *self, gboolean dead)
3488 {
3489 if (self->not_responding != dead) {
3490 self->not_responding = dead;
3491 client_update_title(self);
3492
3493 if (dead)
3494 /* the client isn't responding, so ask to kill it */
3495 client_prompt_kill(self);
3496 else {
3497 /* it came back to life ! */
3498
3499 if (self->kill_prompt) {
3500 prompt_unref(self->kill_prompt);
3501 self->kill_prompt = NULL;
3502 }
3503
3504 self->kill_level = 0;
3505 }
3506 }
3507 }
3508
3509 void client_close(ObClient *self)
3510 {
3511 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3512
3513 /* if closing an internal obprompt, that is just cancelling it */
3514 if (self->prompt) {
3515 prompt_cancel(self->prompt);
3516 return;
3517 }
3518
3519 /* in the case that the client provides no means to requesting that it
3520 close, we just kill it */
3521 if (!self->delete_window)
3522 /* don't use client_kill(), we should only kill based on PID in
3523 response to a lack of PING replies */
3524 XKillClient(obt_display, self->window);
3525 else {
3526 /* request the client to close with WM_DELETE_WINDOW */
3527 OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS,
3528 OBT_PROP_ATOM(WM_DELETE_WINDOW), event_time(),
3529 0, 0, 0, NoEventMask);
3530
3531 /* we're trying to close the window, so see if it is responding. if it
3532 is not, then we will let them kill the window */
3533 if (self->ping)
3534 ping_start(self, client_ping_event);
3535
3536 /* if we already know the window isn't responding (maybe they clicked
3537 no in the kill dialog but it hasn't come back to life), then show
3538 the kill dialog */
3539 if (self->not_responding)
3540 client_prompt_kill(self);
3541 }
3542 }
3543
3544 #define OB_KILL_RESULT_NO 0
3545 #define OB_KILL_RESULT_YES 1
3546
3547 static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
3548 {
3549 ObClient *self = data;
3550
3551 if (result == OB_KILL_RESULT_YES)
3552 client_kill(self);
3553 return TRUE; /* call the cleanup func */
3554 }
3555
3556 static void client_kill_cleanup(ObPrompt *p, gpointer data)
3557 {
3558 ObClient *self = data;
3559
3560 g_assert(p == self->kill_prompt);
3561
3562 prompt_unref(self->kill_prompt);
3563 self->kill_prompt = NULL;
3564 }
3565
3566 static void client_prompt_kill(ObClient *self)
3567 {
3568 /* check if we're already prompting */
3569 if (!self->kill_prompt) {
3570 ObPromptAnswer answers[] = {
3571 { 0, OB_KILL_RESULT_NO },
3572 { 0, OB_KILL_RESULT_YES }
3573 };
3574 gchar *m;
3575 const gchar *y, *title;
3576
3577 title = self->original_title;
3578 if (title[0] == '\0') {
3579 /* empty string, so use its parent */
3580 ObClient *p = client_search_top_direct_parent(self);
3581 if (p) title = p->original_title;
3582 }
3583
3584 if (client_on_localhost(self)) {
3585 const gchar *sig;
3586
3587 if (self->kill_level == 0)
3588 sig = "terminate";
3589 else
3590 sig = "kill";
3591
3592 m = g_strdup_printf
3593 (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"),
3594 title, sig);
3595 y = _("End Process");
3596 }
3597 else {
3598 m = g_strdup_printf
3599 (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"),
3600 title);
3601 y = _("Disconnect");
3602 }
3603 /* set the dialog buttons' text */
3604 answers[0].text = _("Cancel"); /* "no" */
3605 answers[1].text = y; /* "yes" */
3606
3607 self->kill_prompt = prompt_new(m, NULL, answers,
3608 sizeof(answers)/sizeof(answers[0]),
3609 OB_KILL_RESULT_NO, /* default = no */
3610 OB_KILL_RESULT_NO, /* cancel = no */
3611 client_kill_requested,
3612 client_kill_cleanup,
3613 self);
3614 g_free(m);
3615 }
3616
3617 prompt_show(self->kill_prompt, self, TRUE);
3618 }
3619
3620 void client_kill(ObClient *self)
3621 {
3622 /* don't kill our own windows */
3623 if (self->prompt) return;
3624
3625 if (client_on_localhost(self) && self->pid) {
3626 /* running on the local host */
3627 if (self->kill_level == 0) {
3628 ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3629 self->window, self->pid);
3630 kill(self->pid, SIGTERM);
3631 ++self->kill_level;
3632
3633 /* show that we're trying to kill it */
3634 client_update_title(self);
3635 }
3636 else {
3637 ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
3638 self->window, self->pid);
3639 kill(self->pid, SIGKILL); /* kill -9 */
3640 }
3641 }
3642 else {
3643 /* running on a remote host */
3644 XKillClient(obt_display, self->window);
3645 }
3646 }
3647
3648 void client_hilite(ObClient *self, gboolean hilite)
3649 {
3650 if (self->demands_attention == hilite)
3651 return; /* no change */
3652
3653 /* don't allow focused windows to hilite */
3654 self->demands_attention = hilite && !client_focused(self);
3655 if (self->frame != NULL) { /* if we're mapping, just set the state */
3656 if (self->demands_attention) {
3657 frame_flash_start(self->frame);
3658
3659 /* if the window is on another desktop then raise it and make it
3660 the most recently used window */
3661 if (self->desktop != screen_desktop &&
3662 self->desktop != DESKTOP_ALL)
3663 {
3664 stacking_raise(CLIENT_AS_WINDOW(self));
3665 focus_order_to_top(self);
3666 }
3667 }
3668 else
3669 frame_flash_stop(self->frame);
3670 client_change_state(self);
3671 }
3672 }
3673
3674 static void client_set_desktop_recursive(ObClient *self,
3675 guint target,
3676 gboolean donthide,
3677 gboolean dontraise)
3678 {
3679 guint old;
3680 GSList *it;
3681
3682 if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
3683
3684 ob_debug("Setting desktop %u", target+1);
3685
3686 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3687
3688 old = self->desktop;
3689 self->desktop = target;
3690 OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, target);
3691 /* the frame can display the current desktop state */
3692 frame_adjust_state(self->frame);
3693 /* 'move' the window to the new desktop */
3694 if (!donthide)
3695 client_hide(self);
3696 client_show(self);
3697 /* raise if it was not already on the desktop */
3698 if (old != DESKTOP_ALL && !dontraise)
3699 stacking_raise(CLIENT_AS_WINDOW(self));
3700 if (STRUT_EXISTS(self->strut))
3701 screen_update_areas();
3702 else
3703 /* the new desktop's geometry may be different, so we may need to
3704 resize, for example if we are maximized */
3705 client_reconfigure(self, FALSE);
3706
3707 focus_cycle_addremove(self, FALSE);
3708 }
3709
3710 /* move all transients */
3711 for (it = self->transients; it; it = g_slist_next(it))
3712 if (it->data != self)
3713 if (client_is_direct_child(self, it->data))
3714 client_set_desktop_recursive(it->data, target,
3715 donthide, dontraise);
3716 }
3717
3718 void client_set_desktop(ObClient *self, guint target,
3719 gboolean donthide, gboolean dontraise)
3720 {
3721 self = client_search_top_direct_parent(self);
3722 client_set_desktop_recursive(self, target, donthide, dontraise);
3723
3724 focus_cycle_addremove(NULL, TRUE);
3725 }
3726
3727 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3728 {
3729 while (child != parent && (child = client_direct_parent(child)));
3730 return child == parent;
3731 }
3732
3733 ObClient *client_search_modal_child(ObClient *self)
3734 {
3735 GSList *it;
3736 ObClient *ret;
3737
3738 for (it = self->transients; it; it = g_slist_next(it)) {
3739 ObClient *c = it->data;
3740 if ((ret = client_search_modal_child(c))) return ret;
3741 if (c->modal) return c;
3742 }
3743 return NULL;
3744 }
3745
3746 struct ObClientFindDestroyUnmap {
3747 Window window;
3748 gint ignore_unmaps;
3749 };
3750
3751 static gboolean find_destroy_unmap(XEvent *e, gpointer data)
3752 {
3753 struct ObClientFindDestroyUnmap *find = data;
3754 if (e->type == DestroyNotify)
3755 return e->xdestroywindow.window == find->window;
3756 if (e->type == UnmapNotify && e->xunmap.window == find->window)
3757 /* ignore the first $find->ignore_unmaps$ many unmap events */
3758 return --find->ignore_unmaps < 0;
3759 return FALSE;
3760 }
3761
3762 gboolean client_validate(ObClient *self)
3763 {
3764 struct ObClientFindDestroyUnmap find;
3765
3766 XSync(obt_display, FALSE); /* get all events on the server */
3767
3768 find.window = self->window;
3769 find.ignore_unmaps = self->ignore_unmaps;
3770 if (xqueue_exists_local(find_destroy_unmap, &find))
3771 return FALSE;
3772
3773 return TRUE;
3774 }
3775
3776 void client_set_wm_state(ObClient *self, glong state)
3777 {
3778 if (state == self->wmstate) return; /* no change */
3779
3780 switch (state) {
3781 case IconicState:
3782 client_iconify(self, TRUE, TRUE, FALSE);
3783 break;
3784 case NormalState:
3785 client_iconify(self, FALSE, TRUE, FALSE);
3786 break;
3787 }
3788 }
3789
3790 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3791 {
3792 gboolean shaded = self->shaded;
3793 gboolean fullscreen = self->fullscreen;
3794 gboolean undecorated = self->undecorated;
3795 gboolean max_horz = self->max_horz;
3796 gboolean max_vert = self->max_vert;
3797 gboolean modal = self->modal;
3798 gboolean iconic = self->iconic;
3799 gboolean demands_attention = self->demands_attention;
3800 gboolean above = self->above;
3801 gboolean below = self->below;
3802 gint i;
3803 gboolean value;
3804
3805 if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
3806 action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
3807 action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
3808 /* an invalid action was passed to the client message, ignore it */
3809 return;
3810
3811 for (i = 0; i < 2; ++i) {
3812 Atom state = i == 0 ? data1 : data2;
3813
3814 if (!state) continue;
3815
3816 /* if toggling, then pick whether we're adding or removing */
3817 if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
3818 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
3819 value = modal;
3820 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
3821 value = self->max_vert;
3822 else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
3823 value = self->max_horz;
3824 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
3825 value = shaded;
3826 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
3827 value = self->skip_taskbar;
3828 else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
3829 value = self->skip_pager;
3830 else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
3831 value = self->iconic;
3832 else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
3833 value = fullscreen;
3834 else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
3835 value = self->above;
3836 else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
3837 value = self->below;
3838 else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
3839 value = self->demands_attention;
3840 else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
3841 value = undecorated;
3842 else
3843 g_assert_not_reached();
3844 action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3845 OBT_PROP_ATOM(NET_WM_STATE_ADD);
3846 }
3847
3848 value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD);
3849 if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3850 modal = value;
3851 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3852 max_vert = value;
3853 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3854 max_horz = value;
3855 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3856 shaded = value;
3857 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3858 self->skip_taskbar = value;
3859 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3860 self->skip_pager = value;
3861 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3862 iconic = value;
3863 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3864 fullscreen = value;
3865 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3866 above = value;
3867 /* only unset below when setting above, otherwise you can't get to
3868 the normal layer */
3869 if (value)
3870 below = FALSE;
3871 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3872 /* and vice versa */
3873 if (value)
3874 above = FALSE;
3875 below = value;
3876 } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3877 demands_attention = value;
3878 } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3879 undecorated = value;
3880 }
3881 }
3882
3883 if (max_horz != self->max_horz || max_vert != self->max_vert) {
3884 if (max_horz != self->max_horz && max_vert != self->max_vert) {
3885 /* toggling both */
3886 if (max_horz == max_vert) { /* both going the same way */
3887 client_maximize(self, max_horz, 0);
3888 } else {
3889 client_maximize(self, max_horz, 1);
3890 client_maximize(self, max_vert, 2);
3891 }
3892 } else {
3893 /* toggling one */
3894 if (max_horz != self->max_horz)
3895 client_maximize(self, max_horz, 1);
3896 else
3897 client_maximize(self, max_vert, 2);
3898 }
3899 }
3900 /* change fullscreen state before shading, as it will affect if the window
3901 can shade or not */
3902 if (fullscreen != self->fullscreen)
3903 client_fullscreen(self, fullscreen);
3904 if (shaded != self->shaded)
3905 client_shade(self, shaded);
3906 if (undecorated != self->undecorated)
3907 client_set_undecorated(self, undecorated);
3908 if (above != self->above || below != self->below) {
3909 self->above = above;
3910 self->below = below;
3911 client_calc_layer(self);
3912 }
3913
3914 if (modal != self->modal) {
3915 self->modal = modal;
3916 /* when a window changes modality, then its stacking order with its
3917 transients needs to change */
3918 stacking_raise(CLIENT_AS_WINDOW(self));
3919
3920 /* it also may get focused. if something is focused that shouldn't
3921 be focused anymore, then move the focus */
3922 if (focus_client && client_focus_target(focus_client) != focus_client)
3923 client_focus(focus_client);
3924 }
3925
3926 if (iconic != self->iconic)
3927 client_iconify(self, iconic, FALSE, FALSE);
3928
3929 if (demands_attention != self->demands_attention)
3930 client_hilite(self, demands_attention);
3931
3932 client_change_state(self); /* change the hint to reflect these changes */
3933
3934 focus_cycle_addremove(self, TRUE);
3935 }
3936
3937 ObClient *client_focus_target(ObClient *self)
3938 {
3939 ObClient *child = NULL;
3940
3941 child = client_search_modal_child(self);
3942 if (child) return child;
3943 return self;
3944 }
3945
3946 gboolean client_can_focus(ObClient *self)
3947 {
3948 /* choose the correct target */
3949 self = client_focus_target(self);
3950
3951 if (!self->frame->visible)
3952 return FALSE;
3953
3954 if (!(self->can_focus || self->focus_notify))
3955 return FALSE;
3956
3957 return TRUE;
3958 }
3959
3960 gboolean client_focus(ObClient *self)
3961 {
3962 if (!client_validate(self)) return FALSE;
3963
3964 /* we might not focus this window, so if we have modal children which would
3965 be focused instead, bring them to this desktop */
3966 client_bring_modal_windows(self);
3967
3968 /* choose the correct target */
3969 self = client_focus_target(self);
3970
3971 if (!client_can_focus(self)) {
3972 ob_debug_type(OB_DEBUG_FOCUS,
3973 "Client %s can't be focused", self->title);
3974 return FALSE;
3975 }
3976
3977 /* if we have helper windows they should be there with the window */
3978 client_bring_helper_windows(self);
3979
3980 ob_debug_type(OB_DEBUG_FOCUS,
3981 "Focusing client \"%s\" (0x%x) at time %u",
3982 self->title, self->window, event_time());
3983
3984 /* if using focus_delay, stop the timer now so that focus doesn't
3985 go moving on us */
3986 event_halt_focus_delay();
3987
3988 obt_display_ignore_errors(TRUE);
3989
3990 if (self->can_focus) {
3991 /* This can cause a BadMatch error with CurrentTime, or if an app
3992 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3993 XSetInputFocus(obt_display, self->window, RevertToPointerRoot,
3994 event_time());
3995 }
3996
3997 if (self->focus_notify) {
3998 XEvent ce;
3999 ce.xclient.type = ClientMessage;
4000 ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
4001 ce.xclient.display = obt_display;
4002 ce.xclient.window = self->window;
4003 ce.xclient.format = 32;
4004 ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
4005 ce.xclient.data.l[1] = event_time();
4006 ce.xclient.data.l[2] = 0l;
4007 ce.xclient.data.l[3] = 0l;
4008 ce.xclient.data.l[4] = 0l;
4009 XSendEvent(obt_display, self->window, FALSE, NoEventMask, &ce);
4010 }
4011
4012 obt_display_ignore_errors(FALSE);
4013
4014 ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
4015 obt_display_error_occured);
4016 return !obt_display_error_occured;
4017 }
4018
4019 static void client_present(ObClient *self, gboolean here, gboolean raise,
4020 gboolean unshade)
4021 {
4022 if (client_normal(self) && screen_showing_desktop)
4023 screen_show_desktop(FALSE, self);
4024 if (self->iconic)
4025 client_iconify(self, FALSE, here, FALSE);
4026 if (self->desktop != DESKTOP_ALL &&
4027 self->desktop != screen_desktop)
4028 {
4029 if (here)
4030 client_set_desktop(self, screen_desktop, FALSE, TRUE);
4031 else
4032 screen_set_desktop(self->desktop, FALSE);
4033 } else if (!self->frame->visible)
4034 /* if its not visible for other reasons, then don't mess
4035 with it */
4036 return;
4037 if (self->shaded && unshade)
4038 client_shade(self, FALSE);
4039 if (raise)
4040 stacking_raise(CLIENT_AS_WINDOW(self));
4041
4042 client_focus(self);
4043 }
4044
4045 /* this function exists to map to the net_active_window message in the ewmh */
4046 void client_activate(ObClient *self, gboolean desktop,
4047 gboolean here, gboolean raise,
4048 gboolean unshade, gboolean user)
4049 {
4050 self = client_focus_target(self);
4051
4052 if (client_can_steal_focus(self, desktop, user, event_time(), CurrentTime))
4053 client_present(self, here, raise, unshade);
4054 else
4055 client_hilite(self, TRUE);
4056 }
4057
4058 static void client_bring_windows_recursive(ObClient *self,
4059 guint desktop,
4060 gboolean helpers,
4061 gboolean modals,
4062 gboolean iconic)
4063 {
4064 GSList *it;
4065
4066 for (it = self->transients; it; it = g_slist_next(it))
4067 client_bring_windows_recursive(it->data, desktop,
4068 helpers, modals, iconic);
4069
4070 if (((helpers && client_helper(self)) ||
4071 (modals && self->modal)) &&
4072 (!screen_compare_desktops(self->desktop, desktop) ||
4073 (iconic && self->iconic)))
4074 {
4075 if (iconic && self->iconic)
4076 client_iconify(self, FALSE, TRUE, FALSE);
4077 else
4078 client_set_desktop(self, desktop, FALSE, FALSE);
4079 }
4080 }
4081
4082 void client_bring_helper_windows(ObClient *self)
4083 {
4084 client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
4085 }
4086
4087 void client_bring_modal_windows(ObClient *self)
4088 {
4089 client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
4090 }
4091
4092 gboolean client_focused(ObClient *self)
4093 {
4094 return self == focus_client;
4095 }
4096
4097 RrImage* client_icon(ObClient *self)
4098 {
4099 RrImage *ret = NULL;
4100
4101 if (self->icon_set)
4102 ret = self->icon_set;
4103 else if (self->parents) {
4104 GSList *it;
4105 for (it = self->parents; it && !ret; it = g_slist_next(it))
4106 ret = client_icon(it->data);
4107 }
4108 if (!ret)
4109 ret = client_default_icon;
4110 return ret;
4111 }
4112
4113 void client_set_layer(ObClient *self, gint layer)
4114 {
4115 if (layer < 0) {
4116 self->below = TRUE;
4117 self->above = FALSE;
4118 } else if (layer == 0) {
4119 self->below = self->above = FALSE;
4120 } else {
4121 self->below = FALSE;
4122 self->above = TRUE;
4123 }
4124 client_calc_layer(self);
4125 client_change_state(self); /* reflect this in the state hints */
4126 }
4127
4128 void client_set_undecorated(ObClient *self, gboolean undecorated)
4129 {
4130 if (self->undecorated != undecorated &&
4131 /* don't let it undecorate if the function is missing, but let
4132 it redecorate */
4133 (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
4134 {
4135 self->undecorated = undecorated;
4136 client_setup_decor_and_functions(self, TRUE);
4137 client_change_state(self); /* reflect this in the state hints */
4138 }
4139 }
4140
4141 guint client_monitor(ObClient *self)
4142 {
4143 return screen_find_monitor(&self->frame->area);
4144 }
4145
4146 ObClient *client_direct_parent(ObClient *self)
4147 {
4148 if (!self->parents) return NULL;
4149 if (self->transient_for_group) return NULL;
4150 return self->parents->data;
4151 }
4152
4153 ObClient *client_search_top_direct_parent(ObClient *self)
4154 {
4155 ObClient *p;
4156 while ((p = client_direct_parent(self))) self = p;
4157 return self;
4158 }
4159
4160 static GSList *client_search_all_top_parents_internal(ObClient *self,
4161 gboolean bylayer,
4162 ObStackingLayer layer)
4163 {
4164 GSList *ret;
4165 ObClient *p;
4166
4167 /* move up the direct transient chain as far as possible */
4168 while ((p = client_direct_parent(self)) &&
4169 (!bylayer || p->layer == layer))
4170 self = p;
4171
4172 if (!self->parents)
4173 ret = g_slist_prepend(NULL, self);
4174 else
4175 ret = g_slist_copy(self->parents);
4176
4177 return ret;
4178 }
4179
4180 GSList *client_search_all_top_parents(ObClient *self)
4181 {
4182 return client_search_all_top_parents_internal(self, FALSE, 0);
4183 }
4184
4185 GSList *client_search_all_top_parents_layer(ObClient *self)
4186 {
4187 return client_search_all_top_parents_internal(self, TRUE, self->layer);
4188 }
4189
4190 ObClient *client_search_focus_parent(ObClient *self)
4191 {
4192 GSList *it;
4193
4194 for (it = self->parents; it; it = g_slist_next(it))
4195 if (client_focused(it->data)) return it->data;
4196
4197 return NULL;
4198 }
4199
4200 ObClient *client_search_focus_parent_full(ObClient *self)
4201 {
4202 GSList *it;
4203 ObClient *ret = NULL;
4204
4205 for (it = self->parents; it; it = g_slist_next(it)) {
4206 if (client_focused(it->data))
4207 ret = it->data;
4208 else
4209 ret = client_search_focus_parent_full(it->data);
4210 if (ret) break;
4211 }
4212 return ret;
4213 }
4214
4215 ObClient *client_search_parent(ObClient *self, ObClient *search)
4216 {
4217 GSList *it;
4218
4219 for (it = self->parents; it; it = g_slist_next(it))
4220 if (it->data == search) return search;
4221
4222 return NULL;
4223 }
4224
4225 ObClient *client_search_transient(ObClient *self, ObClient *search)
4226 {
4227 GSList *sit;
4228
4229 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
4230 if (sit->data == search)
4231 return search;
4232 if (client_search_transient(sit->data, search))
4233 return search;
4234 }
4235 return NULL;
4236 }
4237
4238 static void detect_edge(Rect area, ObDirection dir,
4239 gint my_head, gint my_size,
4240 gint my_edge_start, gint my_edge_size,
4241 gint *dest, gboolean *near_edge)
4242 {
4243 gint edge_start, edge_size, head, tail;
4244 gboolean skip_head = FALSE, skip_tail = FALSE;
4245
4246 switch (dir) {
4247 case OB_DIRECTION_NORTH:
4248 case OB_DIRECTION_SOUTH:
4249 edge_start = area.x;
4250 edge_size = area.width;
4251 break;
4252 case OB_DIRECTION_EAST:
4253 case OB_DIRECTION_WEST:
4254 edge_start = area.y;
4255 edge_size = area.height;
4256 break;
4257 default:
4258 g_assert_not_reached();
4259 }
4260
4261 /* do we collide with this window? */
4262 if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
4263 edge_start, edge_size))
4264 return;
4265
4266 switch (dir) {
4267 case OB_DIRECTION_NORTH:
4268 head = RECT_BOTTOM(area);
4269 tail = RECT_TOP(area);
4270 break;
4271 case OB_DIRECTION_SOUTH:
4272 head = RECT_TOP(area);
4273 tail = RECT_BOTTOM(area);
4274 break;
4275 case OB_DIRECTION_WEST:
4276 head = RECT_RIGHT(area);
4277 tail = RECT_LEFT(area);
4278 break;
4279 case OB_DIRECTION_EAST:
4280 head = RECT_LEFT(area);
4281 tail = RECT_RIGHT(area);
4282 break;
4283 default:
4284 g_assert_not_reached();
4285 }
4286 switch (dir) {
4287 case OB_DIRECTION_NORTH:
4288 case OB_DIRECTION_WEST:
4289 /* check if our window is past the head of this window */
4290 if (my_head <= head + 1)
4291 skip_head = TRUE;
4292 /* check if our window's tail is past the tail of this window */
4293 if (my_head + my_size - 1 <= tail)
4294 skip_tail = TRUE;
4295 /* check if the head of this window is closer than the previously
4296 chosen edge (take into account that the previously chosen
4297 edge might have been a tail, not a head) */
4298 if (head + (*near_edge ? 0 : my_size) <= *dest)
4299 skip_head = TRUE;
4300 /* check if the tail of this window is closer than the previously
4301 chosen edge (take into account that the previously chosen
4302 edge might have been a head, not a tail) */
4303 if (tail - (!*near_edge ? 0 : my_size) <= *dest)
4304 skip_tail = TRUE;
4305 break;
4306 case OB_DIRECTION_SOUTH:
4307 case OB_DIRECTION_EAST:
4308 /* check if our window is past the head of this window */
4309 if (my_head >= head - 1)
4310 skip_head = TRUE;
4311 /* check if our window's tail is past the tail of this window */
4312 if (my_head - my_size + 1 >= tail)
4313 skip_tail = TRUE;
4314 /* check if the head of this window is closer than the previously
4315 chosen edge (take into account that the previously chosen
4316 edge might have been a tail, not a head) */
4317 if (head - (*near_edge ? 0 : my_size) >= *dest)
4318 skip_head = TRUE;
4319 /* check if the tail of this window is closer than the previously
4320 chosen edge (take into account that the previously chosen
4321 edge might have been a head, not a tail) */
4322 if (tail + (!*near_edge ? 0 : my_size) >= *dest)
4323 skip_tail = TRUE;
4324 break;
4325 default:
4326 g_assert_not_reached();
4327 }
4328
4329 ob_debug("my head %d size %d", my_head, my_size);
4330 ob_debug("head %d tail %d dest %d", head, tail, *dest);
4331 if (!skip_head) {
4332 ob_debug("using near edge %d", head);
4333 *dest = head;
4334 *near_edge = TRUE;
4335 }
4336 else if (!skip_tail) {
4337 ob_debug("using far edge %d", tail);
4338 *dest = tail;
4339 *near_edge = FALSE;
4340 }
4341 }
4342
4343 void client_find_edge_directional(ObClient *self, ObDirection dir,
4344 gint my_head, gint my_size,
4345 gint my_edge_start, gint my_edge_size,
4346 gint *dest, gboolean *near_edge)
4347 {
4348 GList *it;
4349 Rect *a;
4350 Rect dock_area;
4351 gint edge;
4352 guint i;
4353
4354 a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
4355 &self->frame->area);
4356
4357 switch (dir) {
4358 case OB_DIRECTION_NORTH:
4359 edge = RECT_TOP(*a) - 1;
4360 break;
4361 case OB_DIRECTION_SOUTH:
4362 edge = RECT_BOTTOM(*a) + 1;
4363 break;
4364 case OB_DIRECTION_EAST:
4365 edge = RECT_RIGHT(*a) + 1;
4366 break;
4367 case OB_DIRECTION_WEST:
4368 edge = RECT_LEFT(*a) - 1;
4369 break;
4370 default:
4371 g_assert_not_reached();
4372 }
4373 /* default to the far edge, then narrow it down */
4374 *dest = edge;
4375 *near_edge = TRUE;
4376
4377 /* search for edges of monitors */
4378 for (i = 0; i < screen_num_monitors; ++i) {
4379 Rect *area = screen_area(self->desktop, i, NULL);
4380 detect_edge(*area, dir, my_head, my_size, my_edge_start,
4381 my_edge_size, dest, near_edge);
4382 g_slice_free(Rect, area);
4383 }
4384
4385 /* search for edges of clients */
4386 for (it = client_list; it; it = g_list_next(it)) {
4387 ObClient *cur = it->data;
4388
4389 /* skip windows to not bump into */
4390 if (cur == self)
4391 continue;
4392 if (cur->iconic)
4393 continue;
4394 if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
4395 cur->desktop != screen_desktop)
4396 continue;
4397
4398 ob_debug("trying window %s", cur->title);
4399
4400 detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
4401 my_edge_size, dest, near_edge);
4402 }
4403 dock_get_area(&dock_area);
4404 detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
4405 my_edge_size, dest, near_edge);
4406
4407 g_slice_free(Rect, a);
4408 }
4409
4410 void client_find_move_directional(ObClient *self, ObDirection dir,
4411 gint *x, gint *y)
4412 {
4413 gint head, size;
4414 gint e, e_start, e_size;
4415 gboolean near;
4416
4417 switch (dir) {
4418 case OB_DIRECTION_EAST:
4419 head = RECT_RIGHT(self->frame->area);
4420 size = self->frame->area.width;
4421 e_start = RECT_TOP(self->frame->area);
4422 e_size = self->frame->area.height;
4423 break;
4424 case OB_DIRECTION_WEST:
4425 head = RECT_LEFT(self->frame->area);
4426 size = self->frame->area.width;
4427 e_start = RECT_TOP(self->frame->area);
4428 e_size = self->frame->area.height;
4429 break;
4430 case OB_DIRECTION_NORTH:
4431 head = RECT_TOP(self->frame->area);
4432 size = self->frame->area.height;
4433 e_start = RECT_LEFT(self->frame->area);
4434 e_size = self->frame->area.width;
4435 break;
4436 case OB_DIRECTION_SOUTH:
4437 head = RECT_BOTTOM(self->frame->area);
4438 size = self->frame->area.height;
4439 e_start = RECT_LEFT(self->frame->area);
4440 e_size = self->frame->area.width;
4441 break;
4442 default:
4443 g_assert_not_reached();
4444 }
4445
4446 client_find_edge_directional(self, dir, head, size,
4447 e_start, e_size, &e, &near);
4448 *x = self->frame->area.x;
4449 *y = self->frame->area.y;
4450 switch (dir) {
4451 case OB_DIRECTION_EAST:
4452 if (near) e -= self->frame->area.width;
4453 else e++;
4454 *x = e;
4455 break;
4456 case OB_DIRECTION_WEST:
4457 if (near) e++;
4458 else e -= self->frame->area.width;
4459 *x = e;
4460 break;
4461 case OB_DIRECTION_NORTH:
4462 if (near) e++;
4463 else e -= self->frame->area.height;
4464 *y = e;
4465 break;
4466 case OB_DIRECTION_SOUTH:
4467 if (near) e -= self->frame->area.height;
4468 else e++;
4469 *y = e;
4470 break;
4471 default:
4472 g_assert_not_reached();
4473 }
4474 frame_frame_gravity(self->frame, x, y);
4475 }
4476
4477 void client_find_resize_directional(ObClient *self, ObDirection side,
4478 gboolean grow,
4479 gint *x, gint *y, gint *w, gint *h)
4480 {
4481 gint head;
4482 gint e, e_start, e_size, delta;
4483 gboolean near;
4484 ObDirection dir;
4485
4486 switch (side) {
4487 case OB_DIRECTION_EAST:
4488 head = RECT_RIGHT(self->frame->area) +
4489 (self->size_inc.width - 1) * (grow ? 1 : 0);
4490 e_start = RECT_TOP(self->frame->area);
4491 e_size = self->frame->area.height;
4492 dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
4493 break;
4494 case OB_DIRECTION_WEST:
4495 head = RECT_LEFT(self->frame->area) -
4496 (self->size_inc.width - 1) * (grow ? 1 : 0);
4497 e_start = RECT_TOP(self->frame->area);
4498 e_size = self->frame->area.height;
4499 dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
4500 break;
4501 case OB_DIRECTION_NORTH:
4502 head = RECT_TOP(self->frame->area) -
4503 (self->size_inc.height - 1) * (grow ? 1 : 0);
4504 e_start = RECT_LEFT(self->frame->area);
4505 e_size = self->frame->area.width;
4506 dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
4507 break;
4508 case OB_DIRECTION_SOUTH:
4509 head = RECT_BOTTOM(self->frame->area) +
4510 (self->size_inc.height - 1) * (grow ? 1 : 0);
4511 e_start = RECT_LEFT(self->frame->area);
4512 e_size = self->frame->area.width;
4513 dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
4514 break;
4515 default:
4516 g_assert_not_reached();
4517 }
4518
4519 ob_debug("head %d dir %d", head, dir);
4520 client_find_edge_directional(self, dir, head, 1,
4521 e_start, e_size, &e, &near);
4522 ob_debug("edge %d", e);
4523 *x = self->frame->area.x;
4524 *y = self->frame->area.y;
4525 *w = self->frame->area.width;
4526 *h = self->frame->area.height;
4527 switch (side) {
4528 case OB_DIRECTION_EAST:
4529 if (grow == near) --e;
4530 delta = e - RECT_RIGHT(self->frame->area);
4531 *w += delta;
4532 break;
4533 case OB_DIRECTION_WEST:
4534 if (grow == near) ++e;
4535 delta = RECT_LEFT(self->frame->area) - e;
4536 *x -= delta;
4537 *w += delta;
4538 break;
4539 case OB_DIRECTION_NORTH:
4540 if (grow == near) ++e;
4541 delta = RECT_TOP(self->frame->area) - e;
4542 *y -= delta;
4543 *h += delta;
4544 break;
4545 case OB_DIRECTION_SOUTH:
4546 if (grow == near) --e;
4547 delta = e - RECT_BOTTOM(self->frame->area);
4548 *h += delta;
4549 break;
4550 default:
4551 g_assert_not_reached();
4552 }
4553 frame_frame_gravity(self->frame, x, y);
4554 *w -= self->frame->size.left + self->frame->size.right;
4555 *h -= self->frame->size.top + self->frame->size.bottom;
4556 }
4557
4558 ObClient* client_under_pointer(void)
4559 {
4560 gint x, y;
4561 GList *it;
4562 ObClient *ret = NULL;
4563
4564 if (screen_pointer_pos(&x, &y)) {
4565 for (it = stacking_list; it; it = g_list_next(it)) {
4566 if (WINDOW_IS_CLIENT(it->data)) {
4567 ObClient *c = WINDOW_AS_CLIENT(it->data);
4568 if (c->frame->visible &&
4569 /* check the desktop, this is done during desktop
4570 switching and windows are shown/hidden status is not
4571 reliable */
4572 (c->desktop == screen_desktop ||
4573 c->desktop == DESKTOP_ALL) &&
4574 /* ignore all animating windows */
4575 !frame_iconify_animating(c->frame) &&
4576 RECT_CONTAINS(c->frame->area, x, y))
4577 {
4578 ret = c;
4579 break;
4580 }
4581 }
4582 }
4583 }
4584 return ret;
4585 }
4586
4587 gboolean client_has_group_siblings(ObClient *self)
4588 {
4589 return self->group && self->group->members->next;
4590 }
4591
4592 gboolean client_has_relative(ObClient *self)
4593 {
4594 return client_has_parent(self) ||
4595 client_has_group_siblings(self) ||
4596 client_has_children(self);
4597 }
4598
4599 /*! Returns TRUE if the client is running on the same machine as Openbox */
4600 gboolean client_on_localhost(ObClient *self)
4601 {
4602 return self->client_machine == NULL;
4603 }
This page took 0.24403 seconds and 5 git commands to generate.