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