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