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