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