]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
cac6fc7959101e8adcadd1e801dc56a96c231129
[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 static void focus_cycle_destructor(ObClient *client, gpointer data)
46 {
47 /* end cycling if the target disappears */
48 if (focus_cycle_target == client)
49 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE);
50 }
51
52 void focus_startup(gboolean reconfig)
53 {
54 focus_cycle_popup = icon_popup_new(TRUE);
55
56 if (!reconfig) {
57 client_add_destructor(focus_cycle_destructor, NULL);
58
59 /* start with nothing focused */
60 focus_set_client(NULL);
61 }
62 }
63
64 void focus_shutdown(gboolean reconfig)
65 {
66 guint i;
67
68 icon_popup_free(focus_cycle_popup);
69
70 if (!reconfig) {
71 client_remove_destructor(focus_cycle_destructor);
72
73 for (i = 0; i < screen_num_desktops; ++i)
74 g_list_free(focus_order[i]);
75 g_free(focus_order);
76
77 /* reset focus to root */
78 XSetInputFocus(ob_display, PointerRoot, RevertToNone, event_lasttime);
79 }
80 }
81
82 static void push_to_top(ObClient *client)
83 {
84 guint desktop;
85
86 desktop = client->desktop;
87 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
88 focus_order[desktop] = g_list_remove(focus_order[desktop], client);
89 focus_order[desktop] = g_list_prepend(focus_order[desktop], client);
90 }
91
92 void focus_set_client(ObClient *client)
93 {
94 Window active;
95 ObClient *old;
96
97 #ifdef DEBUG_FOCUS
98 ob_debug("focus_set_client 0x%lx\n", client ? client->window : 0);
99 #endif
100
101 /* uninstall the old colormap, and install the new one */
102 screen_install_colormap(focus_client, FALSE);
103 screen_install_colormap(client, TRUE);
104
105 if (client == NULL) {
106 #ifdef DEBUG_FOCUS
107 ob_debug("actively focusing NONWINDOW\n");
108 #endif
109 /* when nothing will be focused, send focus to the backup target */
110 XSetInputFocus(ob_display, screen_support_win, RevertToNone,
111 event_lasttime);
112 XSync(ob_display, FALSE);
113 }
114
115 /* in the middle of cycling..? kill it. */
116 if (focus_cycle_target)
117 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE);
118
119 old = focus_client;
120 focus_client = client;
121
122 /* move to the top of the list */
123 if (client != NULL)
124 push_to_top(client);
125
126 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
127 if (ob_state() != OB_STATE_EXITING) {
128 active = client ? client->window : None;
129 PROP_SET32(RootWindow(ob_display, ob_screen),
130 net_active_window, window, active);
131 }
132 }
133
134 /* finds the first transient that isn't 'skip' and ensure's that client_normal
135 is true for it */
136 static ObClient *find_transient_recursive(ObClient *c, ObClient *top, ObClient *skip)
137 {
138 GSList *it;
139 ObClient *ret;
140
141 for (it = c->transients; it; it = it->next) {
142 if (it->data == top) return NULL;
143 ret = find_transient_recursive(it->data, top, skip);
144 if (ret && ret != skip && client_normal(ret)) return ret;
145 if (it->data != skip && client_normal(it->data)) return it->data;
146 }
147 return NULL;
148 }
149
150 static ObClient* focus_fallback_transient(ObClient *top, ObClient *old)
151 {
152 ObClient *target = find_transient_recursive(top, top, old);
153 if (!target) {
154 /* make sure client_normal is true always */
155 if (!client_normal(top))
156 return NULL;
157 target = top; /* no transient, keep the top */
158 }
159 if (client_can_focus(target))
160 return target;
161 else
162 return NULL;
163 }
164
165 ObClient* focus_fallback_target(ObFocusFallbackType type)
166 {
167 GList *it;
168 ObClient *old = NULL;
169 ObClient *target = NULL;
170
171 old = focus_client;
172
173 if (type == OB_FOCUS_FALLBACK_UNFOCUSING && old) {
174 if (old->transient_for) {
175 gboolean trans = FALSE;
176
177 if (!config_focus_follow)
178 trans = TRUE;
179 else {
180 if ((target = client_under_pointer()) &&
181 client_search_transient
182 (client_search_top_transient(target), old))
183 {
184 trans = TRUE;
185 }
186 }
187
188 /* try for transient relations */
189 if (trans) {
190 if (old->transient_for == OB_TRAN_GROUP) {
191 for (it = focus_order[screen_desktop]; it; it = it->next) {
192 GSList *sit;
193
194 for (sit = old->group->members; sit; sit = sit->next)
195 if (sit->data == it->data)
196 if ((target =
197 focus_fallback_transient(sit->data, old)))
198 return target;
199 }
200 } else {
201 if ((target =
202 focus_fallback_transient(old->transient_for, old)))
203 return target;
204 }
205 }
206 }
207 }
208
209 if (config_focus_follow) {
210 if ((target = client_under_pointer()))
211 if (client_normal(target) && client_can_focus(target))
212 return target;
213 }
214
215 #if 0
216 /* try for group relations */
217 if (old->group) {
218 GSList *sit;
219
220 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
221 for (sit = old->group->members; sit; sit = sit->next)
222 if (sit->data == it->data)
223 if (sit->data != old && client_normal(sit->data))
224 if (client_can_focus(sit->data))
225 return sit->data;
226 }
227 #endif
228
229 for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
230 if (type != OB_FOCUS_FALLBACK_UNFOCUSING || it->data != old)
231 if (client_normal(it->data) && client_can_focus(it->data))
232 return it->data;
233
234 return NULL;
235 }
236
237 void focus_fallback(ObFocusFallbackType type)
238 {
239 ObClient *new;
240
241 /* unfocus any focused clients.. they can be focused by Pointer events
242 and such, and then when I try focus them, I won't get a FocusIn event
243 at all for them.
244 */
245 focus_set_client(NULL);
246
247 if ((new = focus_fallback_target(type)))
248 client_focus(new);
249 }
250
251 static void popup_cycle(ObClient *c, gboolean show)
252 {
253 if (!show) {
254 icon_popup_hide(focus_cycle_popup);
255 } else {
256 Rect *a;
257 ObClient *p = c;
258 char *title;
259
260 a = screen_physical_area_monitor(0);
261 icon_popup_position(focus_cycle_popup, CenterGravity,
262 a->x + a->width / 2, a->y + a->height / 2);
263 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
264 icon_popup_show(focus_cycle_popup, c->title,
265 client_icon(c, a->height/16, a->height/16));
266 */
267 /* XXX the size and the font extents need to be related on some level
268 */
269 icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
270
271 /* use the transient's parent's title/icon */
272 while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
273 p = p->transient_for;
274
275 if (p == c)
276 title = NULL;
277 else
278 title = g_strconcat((c->iconic ? c->icon_title : c->title),
279 " - ",
280 (p->iconic ? p->icon_title : p->title),
281 NULL);
282
283 icon_popup_show(focus_cycle_popup,
284 (title ? title :
285 (c->iconic ? c->icon_title : c->title)),
286 client_icon(p, 48, 48));
287 g_free(title);
288 }
289 }
290
291 static gboolean valid_focus_target(ObClient *ft)
292 {
293 /* we don't use client_can_focus here, because that doesn't let you
294 focus an iconic window, but we want to be able to, so we just check
295 if the focus flags on the window allow it, and its on the current
296 desktop */
297 return (!ft->transients && client_normal(ft) &&
298 ((ft->can_focus || ft->focus_notify) &&
299 !ft->skip_taskbar &&
300 (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)));
301 }
302
303 void focus_cycle(gboolean forward, gboolean linear,
304 gboolean dialog, gboolean done, gboolean cancel)
305 {
306 static ObClient *first = NULL;
307 static ObClient *t = NULL;
308 static GList *order = NULL;
309 GList *it, *start, *list;
310 ObClient *ft = NULL;
311
312 if (cancel) {
313 if (focus_cycle_target)
314 frame_adjust_focus(focus_cycle_target->frame, FALSE);
315 if (focus_client)
316 frame_adjust_focus(focus_client->frame, TRUE);
317 focus_cycle_target = NULL;
318 goto done_cycle;
319 } else if (done && dialog) {
320 goto done_cycle;
321 }
322
323 if (!focus_order[screen_desktop])
324 goto done_cycle;
325
326 if (!first) first = focus_client;
327 if (!focus_cycle_target) focus_cycle_target = focus_client;
328
329 if (linear) list = client_list;
330 else list = focus_order[screen_desktop];
331
332 start = it = g_list_find(list, focus_cycle_target);
333 if (!start) /* switched desktops or something? */
334 start = it = forward ? g_list_last(list) : g_list_first(list);
335 if (!start) goto done_cycle;
336
337 do {
338 if (forward) {
339 it = it->next;
340 if (it == NULL) it = g_list_first(list);
341 } else {
342 it = it->prev;
343 if (it == NULL) it = g_list_last(list);
344 }
345 ft = it->data;
346 if (valid_focus_target(ft)) {
347 if (ft != focus_cycle_target) { /* prevents flicker */
348 if (focus_cycle_target)
349 frame_adjust_focus(focus_cycle_target->frame, FALSE);
350 focus_cycle_target = ft;
351 frame_adjust_focus(focus_cycle_target->frame, TRUE);
352 }
353 popup_cycle(ft, dialog);
354 return;
355 }
356 } while (it != start);
357
358 done_cycle:
359 if (done && focus_cycle_target)
360 client_activate(focus_cycle_target, FALSE);
361
362 t = NULL;
363 first = NULL;
364 focus_cycle_target = NULL;
365 g_list_free(order);
366 order = NULL;
367
368 popup_cycle(ft, FALSE);
369
370 return;
371 }
372
373 void focus_directional_cycle(ObDirection dir,
374 gboolean dialog, gboolean done, gboolean cancel)
375 {
376 static ObClient *first = NULL;
377 ObClient *ft = NULL;
378
379 if (cancel) {
380 if (focus_cycle_target)
381 frame_adjust_focus(focus_cycle_target->frame, FALSE);
382 if (focus_client)
383 frame_adjust_focus(focus_client->frame, TRUE);
384 focus_cycle_target = NULL;
385 goto done_cycle;
386 } else if (done && dialog) {
387 goto done_cycle;
388 }
389
390 if (!focus_order[screen_desktop])
391 goto done_cycle;
392
393 if (!first) first = focus_client;
394 if (!focus_cycle_target) focus_cycle_target = focus_client;
395
396 if (focus_cycle_target)
397 ft = client_find_directional(focus_cycle_target, dir);
398 else {
399 GList *it;
400
401 for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
402 if (valid_focus_target(it->data))
403 ft = it->data;
404 }
405
406 if (ft) {
407 if (ft != focus_cycle_target) {/* prevents flicker */
408 if (focus_cycle_target)
409 frame_adjust_focus(focus_cycle_target->frame, FALSE);
410 focus_cycle_target = ft;
411 frame_adjust_focus(focus_cycle_target->frame, TRUE);
412 }
413 }
414 if (focus_cycle_target) {
415 popup_cycle(focus_cycle_target, dialog);
416 if (dialog)
417 return;
418 }
419
420
421 done_cycle:
422 if (done && focus_cycle_target)
423 client_activate(focus_cycle_target, FALSE);
424
425 first = NULL;
426 focus_cycle_target = NULL;
427
428 popup_cycle(ft, FALSE);
429
430 return;
431 }
432
433 void focus_order_add_new(ObClient *c)
434 {
435 guint d, i;
436
437 if (c->iconic)
438 focus_order_to_top(c);
439 else {
440 d = c->desktop;
441 if (d == DESKTOP_ALL) {
442 for (i = 0; i < screen_num_desktops; ++i) {
443 if (focus_order[i] && ((ObClient*)focus_order[i]->data)->iconic)
444 focus_order[i] = g_list_insert(focus_order[i], c, 0);
445 else
446 focus_order[i] = g_list_insert(focus_order[i], c, 1);
447 }
448 } else
449 if (focus_order[d] && ((ObClient*)focus_order[d]->data)->iconic)
450 focus_order[d] = g_list_insert(focus_order[d], c, 0);
451 else
452 focus_order[d] = g_list_insert(focus_order[d], c, 1);
453 }
454 }
455
456 void focus_order_remove(ObClient *c)
457 {
458 guint d, i;
459
460 d = c->desktop;
461 if (d == DESKTOP_ALL) {
462 for (i = 0; i < screen_num_desktops; ++i)
463 focus_order[i] = g_list_remove(focus_order[i], c);
464 } else
465 focus_order[d] = g_list_remove(focus_order[d], c);
466 }
467
468 static void to_top(ObClient *c, guint d)
469 {
470 focus_order[d] = g_list_remove(focus_order[d], c);
471 if (!c->iconic) {
472 focus_order[d] = g_list_prepend(focus_order[d], c);
473 } else {
474 GList *it;
475
476 /* insert before first iconic window */
477 for (it = focus_order[d];
478 it && !((ObClient*)it->data)->iconic; it = it->next);
479 focus_order[d] = g_list_insert_before(focus_order[d], it, c);
480 }
481 }
482
483 void focus_order_to_top(ObClient *c)
484 {
485 guint d, i;
486
487 d = c->desktop;
488 if (d == DESKTOP_ALL) {
489 for (i = 0; i < screen_num_desktops; ++i)
490 to_top(c, i);
491 } else
492 to_top(c, d);
493 }
494
495 static void to_bottom(ObClient *c, guint d)
496 {
497 focus_order[d] = g_list_remove(focus_order[d], c);
498 if (c->iconic) {
499 focus_order[d] = g_list_append(focus_order[d], c);
500 } else {
501 GList *it;
502
503 /* insert before first iconic window */
504 for (it = focus_order[d];
505 it && !((ObClient*)it->data)->iconic; it = it->next);
506 g_list_insert_before(focus_order[d], it, c);
507 }
508 }
509
510 void focus_order_to_bottom(ObClient *c)
511 {
512 guint d, i;
513
514 d = c->desktop;
515 if (d == DESKTOP_ALL) {
516 for (i = 0; i < screen_num_desktops; ++i)
517 to_bottom(c, i);
518 } else
519 to_bottom(c, d);
520 }
This page took 0.058892 seconds and 3 git commands to generate.