]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
when ending actions which can move windows, and we were using the mouse, instead...
[chaz/openbox] / openbox / focus.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 focus.c for the Openbox window manager
4 Copyright (c) 2003 Ben Jansens
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "debug.h"
20 #include "event.h"
21 #include "openbox.h"
22 #include "grab.h"
23 #include "framerender.h"
24 #include "client.h"
25 #include "config.h"
26 #include "frame.h"
27 #include "screen.h"
28 #include "group.h"
29 #include "prop.h"
30 #include "focus.h"
31 #include "stacking.h"
32 #include "popup.h"
33
34 #include <X11/Xlib.h>
35 #include <glib.h>
36 #include <assert.h>
37
38 ObClient *focus_client;
39 GList **focus_order; /* these lists are created when screen_startup
40 sets the number of desktops */
41 ObClient *focus_cycle_target;
42
43 static ObIconPopup *focus_cycle_popup;
44
45 void focus_startup(gboolean reconfig)
46 {
47 focus_cycle_popup = icon_popup_new(TRUE);
48
49 if (!reconfig)
50 /* start with nothing focused */
51 focus_set_client(NULL);
52 }
53
54 void focus_shutdown(gboolean reconfig)
55 {
56 guint i;
57
58 icon_popup_free(focus_cycle_popup);
59
60 if (!reconfig) {
61 for (i = 0; i < screen_num_desktops; ++i)
62 g_list_free(focus_order[i]);
63 g_free(focus_order);
64
65 /* reset focus to root */
66 XSetInputFocus(ob_display, PointerRoot, RevertToPointerRoot,
67 event_lasttime);
68 }
69 }
70
71 static void push_to_top(ObClient *client)
72 {
73 guint desktop;
74
75 desktop = client->desktop;
76 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
77 focus_order[desktop] = g_list_remove(focus_order[desktop], client);
78 focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
79 }
80
81 void focus_set_client(ObClient *client)
82 {
83 Window active;
84 ObClient *old;
85
86 #ifdef DEBUG_FOCUS
87 ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
88 #endif
89
90 /* uninstall the old colormap, and install the new one */
91 screen_install_colormap(focus_client, FALSE);
92 screen_install_colormap(client, TRUE);
93
94 if (client == NULL) {
95 /* when nothing will be focused, send focus to the backup target */
96 XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
97 event_lasttime);
98 XSync(ob_display, FALSE);
99 }
100
101 /* in the middle of cycling..? kill it. */
102 if (focus_cycle_target)
103 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE);
104
105 old = focus_client;
106 focus_client = client;
107
108 /* move to the top of the list */
109 if (client != NULL)
110 push_to_top(client);
111
112 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
113 if (ob_state() != OB_STATE_EXITING) {
114 active = client ? client->window : None;
115 PROP_SET32(RootWindow(ob_display, ob_screen),
116 net_active_window, window, active);
117 }
118 }
119
120 static gboolean focus_under_pointer()
121 {
122 ObClient *c;
123
124 if ((c = client_under_pointer()))
125 return client_normal(c) && client_focus(c);
126 return FALSE;
127 }
128
129 /* finds the first transient that isn't 'skip' and ensure's that client_normal
130 is true for it */
131 static ObClient *find_transient_recursive(ObClient *c, ObClient *top, ObClient *skip)
132 {
133 GSList *it;
134 ObClient *ret;
135
136 for (it = c->transients; it; it = it->next) {
137 if (it->data == top) return NULL;
138 ret = find_transient_recursive(it->data, top, skip);
139 if (ret && ret != skip && client_normal(ret)) return ret;
140 if (it->data != skip && client_normal(it->data)) return it->data;
141 }
142 return NULL;
143 }
144
145 static gboolean focus_fallback_transient(ObClient *top, ObClient *old)
146 {
147 ObClient *target = find_transient_recursive(top, top, old);
148 if (!target) {
149 /* make sure client_normal is true always */
150 if (!client_normal(top))
151 return FALSE;
152 target = top; /* no transient, keep the top */
153 }
154 return client_focus(target);
155 }
156
157 void focus_fallback(ObFocusFallbackType type)
158 {
159 GList *it;
160 ObClient *old = NULL;
161
162 old = focus_client;
163
164 /* unfocus any focused clients.. they can be focused by Pointer events
165 and such, and then when I try focus them, I won't get a FocusIn event
166 at all for them.
167 */
168 focus_set_client(NULL);
169
170 if (config_focus_follow && focus_under_pointer())
171 return;
172
173 if (type == OB_FOCUS_FALLBACK_UNFOCUSING && old) {
174 /* try for transient relations */
175 if (old->transient_for) {
176 if (old->transient_for == OB_TRAN_GROUP) {
177 for (it = focus_order[screen_desktop]; it; it = it->next) {
178 GSList *sit;
179
180 for (sit = old->group->members; sit; sit = sit->next)
181 if (sit->data == it->data)
182 if (focus_fallback_transient(sit->data, old))
183 return;
184 }
185 } else {
186 if (focus_fallback_transient(old->transient_for, old))
187 return;
188 }
189 }
190
191 #if 0
192 /* try for group relations */
193 if (old->group) {
194 GSList *sit;
195
196 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
197 for (sit = old->group->members; sit; sit = sit->next)
198 if (sit->data == it->data)
199 if (sit->data != old && client_normal(sit->data))
200 if (client_can_focus(sit->data)) {
201 gboolean r = client_focus(sit->data);
202 assert(r);
203 return;
204 }
205 }
206 #endif
207 }
208
209 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
210 if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
211 if (client_normal(it->data) &&
212 /* dont fall back to 'anonymous' fullscreen windows. theres no
213 checks for this is in transient/group fallbacks, so they can
214 be fallback targets there. */
215 !((ObClient*)it->data)->fullscreen &&
216 client_can_focus(it->data)) {
217 gboolean r = client_focus(it->data);
218 assert(r);
219 return;
220 }
221
222 /* nothing to focus, and already set it to none above */
223 }
224
225 static void popup_cycle(ObClient *c, gboolean show)
226 {
227 if (!show) {
228 icon_popup_hide(focus_cycle_popup);
229 } else {
230 Rect *a;
231 ObClient *p = c;
232 char *title;
233
234 a = screen_physical_area_monitor(0);
235 icon_popup_position(focus_cycle_popup, CenterGravity,
236 a->x + a->width / 2, a->y + a->height / 2);
237 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
238 icon_popup_show(focus_cycle_popup, c->title,
239 client_icon(c, a->height/16, a->height/16));
240 */
241 /* XXX the size and the font extents need to be related on some level
242 */
243 icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
244
245 /* use the transient's parent's title/icon */
246 while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
247 p = p->transient_for;
248
249 if (p == c)
250 title = NULL;
251 else
252 title = g_strconcat((c->iconic ? c->icon_title : c->title),
253 " - ",
254 (p->iconic ? p->icon_title : p->title),
255 NULL);
256
257 icon_popup_show(focus_cycle_popup,
258 (title ? title :
259 (c->iconic ? c->icon_title : c->title)),
260 client_icon(p, 48, 48));
261 g_free(title);
262 }
263 }
264
265 static gboolean valid_focus_target(ObClient *ft)
266 {
267 /* we don't use client_can_focus here, because that doesn't let you
268 focus an iconic window, but we want to be able to, so we just check
269 if the focus flags on the window allow it, and its on the current
270 desktop */
271 return (ft->transients == NULL && client_normal(ft) &&
272 ((ft->can_focus || ft->focus_notify) &&
273 !ft->skip_taskbar &&
274 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)));
275 }
276
277 void focus_cycle(gboolean forward, gboolean linear,
278 gboolean dialog, gboolean done, gboolean cancel)
279 {
280 static ObClient *first = NULL;
281 static ObClient *t = NULL;
282 static GList *order = NULL;
283 GList *it, *start, *list;
284 ObClient *ft;
285
286 if (cancel) {
287 if (focus_cycle_target)
288 frame_adjust_focus(focus_cycle_target->frame, FALSE);
289 if (focus_client)
290 frame_adjust_focus(focus_client->frame, TRUE);
291 focus_cycle_target = NULL;
292 goto done_cycle;
293 } else if (done && dialog) {
294 goto done_cycle;
295 }
296
297 if (!focus_order[screen_desktop])
298 goto done_cycle;
299
300 if (!first) first = focus_client;
301 if (!focus_cycle_target) focus_cycle_target = focus_client;
302
303 if (linear) list = client_list;
304 else list = focus_order[screen_desktop];
305
306 start = it = g_list_find(list, focus_cycle_target);
307 if (!start) /* switched desktops or something? */
308 start = it = forward ? g_list_last(list) : g_list_first(list);
309 if (!start) goto done_cycle;
310
311 do {
312 if (forward) {
313 it = it->next;
314 if (it == NULL) it = g_list_first(list);
315 } else {
316 it = it->prev;
317 if (it == NULL) it = g_list_last(list);
318 }
319 /*ft = client_focus_target(it->data);*/
320 ft = it->data;
321 if (valid_focus_target(ft)) {
322 if (ft != focus_cycle_target) { /* prevents flicker */
323 if (focus_cycle_target)
324 frame_adjust_focus(focus_cycle_target->frame, FALSE);
325 focus_cycle_target = ft;
326 frame_adjust_focus(focus_cycle_target->frame, TRUE);
327 }
328 popup_cycle(ft, dialog);
329 return;
330 }
331 } while (it != start);
332
333 done_cycle:
334 if (done && focus_cycle_target)
335 client_activate(focus_cycle_target, FALSE);
336
337 t = NULL;
338 first = NULL;
339 focus_cycle_target = NULL;
340 g_list_free(order);
341 order = NULL;
342
343 popup_cycle(ft, FALSE);
344
345 return;
346 }
347
348 void focus_directional_cycle(ObDirection dir,
349 gboolean dialog, gboolean done, gboolean cancel)
350 {
351 static ObClient *first = NULL;
352 ObClient *ft = NULL;
353
354 if (cancel) {
355 if (focus_cycle_target)
356 frame_adjust_focus(focus_cycle_target->frame, FALSE);
357 if (focus_client)
358 frame_adjust_focus(focus_client->frame, TRUE);
359 focus_cycle_target = NULL;
360 goto done_cycle;
361 } else if (done && dialog) {
362 goto done_cycle;
363 }
364
365 if (!focus_order[screen_desktop])
366 goto done_cycle;
367
368 if (!first) first = focus_client;
369 if (!focus_cycle_target) focus_cycle_target = focus_client;
370
371 if (focus_cycle_target)
372 ft = client_find_directional(focus_cycle_target, dir);
373 else {
374 GList *it;
375
376 for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
377 if (valid_focus_target(it->data))
378 ft = it->data;
379 }
380
381 if (ft) {
382 if (ft != focus_cycle_target) {/* prevents flicker */
383 if (focus_cycle_target)
384 frame_adjust_focus(focus_cycle_target->frame, FALSE);
385 focus_cycle_target = ft;
386 frame_adjust_focus(focus_cycle_target->frame, TRUE);
387 }
388 }
389 if (focus_cycle_target) {
390 popup_cycle(focus_cycle_target, dialog);
391 if (dialog)
392 return;
393 }
394
395
396 done_cycle:
397 if (done && focus_cycle_target)
398 client_activate(focus_cycle_target, FALSE);
399
400 first = NULL;
401 focus_cycle_target = NULL;
402
403 popup_cycle(ft, FALSE);
404
405 return;
406 }
407
408 void focus_order_add_new(ObClient *c)
409 {
410 guint d, i;
411
412 if (c->iconic)
413 focus_order_to_top(c);
414 else {
415 d = c->desktop;
416 if (d == DESKTOP_ALL) {
417 for (i = 0; i < screen_num_desktops; ++i) {
418 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
419 focus_order[i] = g_list_insert(focus_order[i], c, 0);
420 else
421 focus_order[i] = g_list_insert(focus_order[i], c, 1);
422 }
423 } else
424 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
425 focus_order[d] = g_list_insert(focus_order[d], c, 0);
426 else
427 focus_order[d] = g_list_insert(focus_order[d], c, 1);
428 }
429 }
430
431 void focus_order_remove(ObClient *c)
432 {
433 guint d, i;
434
435 d = c->desktop;
436 if (d == DESKTOP_ALL) {
437 for (i = 0; i < screen_num_desktops; ++i)
438 focus_order[i] = g_list_remove(focus_order[i], c);
439 } else
440 focus_order[d] = g_list_remove(focus_order[d], c);
441 }
442
443 static void to_top(ObClient *c, guint d)
444 {
445 focus_order[d] = g_list_remove(focus_order[d], c);
446 if (!c->iconic) {
447 focus_order[d] = g_list_prepend(focus_order[d], c);
448 } else {
449 GList *it;
450
451 /* insert before first iconic window */
452 for (it = focus_order[d];
453 it && !((ObClient*)it->data)->iconic; it = it->next);
454 focus_order[d] = g_list_insert_before(focus_order[d], it, c);
455 }
456 }
457
458 void focus_order_to_top(ObClient *c)
459 {
460 guint d, i;
461
462 d = c->desktop;
463 if (d == DESKTOP_ALL) {
464 for (i = 0; i < screen_num_desktops; ++i)
465 to_top(c, i);
466 } else
467 to_top(c, d);
468 }
469
470 static void to_bottom(ObClient *c, guint d)
471 {
472 focus_order[d] = g_list_remove(focus_order[d], c);
473 if (c->iconic) {
474 focus_order[d] = g_list_append(focus_order[d], c);
475 } else {
476 GList *it;
477
478 /* insert before first iconic window */
479 for (it = focus_order[d];
480 it && !((ObClient*)it->data)->iconic; it = it->next);
481 g_list_insert_before(focus_order[d], it, c);
482 }
483 }
484
485 void focus_order_to_bottom(ObClient *c)
486 {
487 guint d, i;
488
489 d = c->desktop;
490 if (d == DESKTOP_ALL) {
491 for (i = 0; i < screen_num_desktops; ++i)
492 to_bottom(c, i);
493 } else
494 to_bottom(c, d);
495 }
This page took 0.059207 seconds and 4 git commands to generate.