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