]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
7e1623bd61df8957cc94350cfff3f34462c10e6f
[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) 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 "debug.h"
21 #include "event.h"
22 #include "openbox.h"
23 #include "grab.h"
24 #include "framerender.h"
25 #include "client.h"
26 #include "config.h"
27 #include "frame.h"
28 #include "screen.h"
29 #include "group.h"
30 #include "prop.h"
31 #include "keyboard.h"
32 #include "focus.h"
33 #include "stacking.h"
34 #include "popup.h"
35 #include "render/render.h"
36
37 #include <X11/Xlib.h>
38 #include <glib.h>
39 #include <assert.h>
40
41 #define FOCUS_INDICATOR_WIDTH 6
42
43 ObClient *focus_client = NULL;
44 GList *focus_order = NULL;
45 ObClient *focus_cycle_target = NULL;
46
47 struct {
48 InternalWindow top;
49 InternalWindow left;
50 InternalWindow right;
51 InternalWindow bottom;
52 } focus_indicator;
53
54 RrAppearance *a_focus_indicator;
55 RrColor *color_white;
56
57 static ObIconPopup *focus_cycle_popup;
58
59 static gboolean valid_focus_target(ObClient *ft,
60 gboolean all_desktops,
61 gboolean dock_windows,
62 gboolean desktop_windows);
63 static void focus_cycle_destroy_notify(ObClient *client, gpointer data);
64
65 static Window createWindow(Window parent, gulong mask,
66 XSetWindowAttributes *attrib)
67 {
68 return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
69 RrDepth(ob_rr_inst), InputOutput,
70 RrVisual(ob_rr_inst), mask, attrib);
71
72 }
73
74 void focus_startup(gboolean reconfig)
75 {
76 focus_cycle_popup = icon_popup_new(TRUE);
77
78 if (!reconfig) {
79 XSetWindowAttributes attr;
80
81 client_add_destroy_notify(focus_cycle_destroy_notify, NULL);
82
83 /* start with nothing focused */
84 focus_nothing();
85
86 focus_indicator.top.obwin.type = Window_Internal;
87 focus_indicator.left.obwin.type = Window_Internal;
88 focus_indicator.right.obwin.type = Window_Internal;
89 focus_indicator.bottom.obwin.type = Window_Internal;
90
91 attr.override_redirect = True;
92 attr.background_pixel = BlackPixel(ob_display, ob_screen);
93 focus_indicator.top.win =
94 createWindow(RootWindow(ob_display, ob_screen),
95 CWOverrideRedirect | CWBackPixel, &attr);
96 focus_indicator.left.win =
97 createWindow(RootWindow(ob_display, ob_screen),
98 CWOverrideRedirect | CWBackPixel, &attr);
99 focus_indicator.right.win =
100 createWindow(RootWindow(ob_display, ob_screen),
101 CWOverrideRedirect | CWBackPixel, &attr);
102 focus_indicator.bottom.win =
103 createWindow(RootWindow(ob_display, ob_screen),
104 CWOverrideRedirect | CWBackPixel, &attr);
105
106 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.top));
107 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.left));
108 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.right));
109 stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.bottom));
110
111 color_white = RrColorNew(ob_rr_inst, 0xff, 0xff, 0xff);
112
113 a_focus_indicator = RrAppearanceNew(ob_rr_inst, 4);
114 a_focus_indicator->surface.grad = RR_SURFACE_SOLID;
115 a_focus_indicator->surface.relief = RR_RELIEF_FLAT;
116 a_focus_indicator->surface.primary = RrColorNew(ob_rr_inst,
117 0, 0, 0);
118 a_focus_indicator->texture[0].type = RR_TEXTURE_LINE_ART;
119 a_focus_indicator->texture[0].data.lineart.color = color_white;
120 a_focus_indicator->texture[1].type = RR_TEXTURE_LINE_ART;
121 a_focus_indicator->texture[1].data.lineart.color = color_white;
122 a_focus_indicator->texture[2].type = RR_TEXTURE_LINE_ART;
123 a_focus_indicator->texture[2].data.lineart.color = color_white;
124 a_focus_indicator->texture[3].type = RR_TEXTURE_LINE_ART;
125 a_focus_indicator->texture[3].data.lineart.color = color_white;
126 }
127 }
128
129 void focus_shutdown(gboolean reconfig)
130 {
131 icon_popup_free(focus_cycle_popup);
132
133 if (!reconfig) {
134 client_remove_destroy_notify(focus_cycle_destroy_notify);
135
136 /* reset focus to root */
137 XSetInputFocus(ob_display, PointerRoot, RevertToNone, CurrentTime);
138
139 RrColorFree(color_white);
140
141 RrAppearanceFree(a_focus_indicator);
142
143 XDestroyWindow(ob_display, focus_indicator.top.win);
144 XDestroyWindow(ob_display, focus_indicator.left.win);
145 XDestroyWindow(ob_display, focus_indicator.right.win);
146 XDestroyWindow(ob_display, focus_indicator.bottom.win);
147 }
148 }
149
150 static void push_to_top(ObClient *client)
151 {
152 focus_order = g_list_remove(focus_order, client);
153 focus_order = g_list_prepend(focus_order, client);
154 }
155
156 void focus_set_client(ObClient *client)
157 {
158 Window active;
159
160 ob_debug_type(OB_DEBUG_FOCUS,
161 "focus_set_client 0x%lx\n", client ? client->window : 0);
162
163 /* uninstall the old colormap, and install the new one */
164 screen_install_colormap(focus_client, FALSE);
165 screen_install_colormap(client, TRUE);
166
167 /* in the middle of cycling..? kill it. CurrentTime is fine, time won't
168 be used.
169 */
170 if (focus_cycle_target)
171 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
172
173 focus_client = client;
174
175 if (client != NULL) {
176 /* move to the top of the list */
177 push_to_top(client);
178 /* remove hiliting from the window when it gets focused */
179 client_hilite(client, FALSE);
180 }
181
182 /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
183 if (ob_state() != OB_STATE_EXITING) {
184 active = client ? client->window : None;
185 PROP_SET32(RootWindow(ob_display, ob_screen),
186 net_active_window, window, active);
187 }
188 }
189
190 static ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *old)
191 {
192 GList *it;
193 ObClient *target = NULL;
194 ObClient *desktop = NULL;
195
196 ob_debug_type(OB_DEBUG_FOCUS, "trying pointer stuff\n");
197 if (config_focus_follow && !config_focus_last)
198 {
199 if ((target = client_under_pointer()))
200 if (allow_refocus || target != old)
201 if (client_normal(target) && client_can_focus(target)) {
202 ob_debug_type(OB_DEBUG_FOCUS, "found in pointer stuff\n");
203 return target;
204 }
205 }
206
207 #if 0
208 /* try for group relations */
209 if (old->group) {
210 GSList *sit;
211
212 for (it = focus_order[screen_desktop]; it; it = g_list_next(it))
213 for (sit = old->group->members; sit; sit = g_slist_next(sit))
214 if (sit->data == it->data)
215 if (sit->data != old && client_normal(sit->data))
216 if (client_can_focus(sit->data))
217 return sit->data;
218 }
219 #endif
220
221 ob_debug_type(OB_DEBUG_FOCUS, "trying omnipresentness\n");
222 if (allow_refocus && old && old->desktop == DESKTOP_ALL &&
223 client_normal(old))
224 {
225 return old;
226 }
227
228
229 ob_debug_type(OB_DEBUG_FOCUS, "trying the focus order\n");
230 for (it = focus_order; it; it = g_list_next(it))
231 if (allow_refocus || it->data != old) {
232 ObClient *c = it->data;
233 /* fallback focus to a window if:
234 1. it is actually focusable, cuz if it's not then we're sending
235 focus off to nothing. this includes if it is visible right now
236 2. it is on the current desktop. this ignores omnipresent
237 windows, which are problematic in their own rite.
238 3. it is a normal type window, don't fall back onto a dock or
239 a splashscreen or a desktop window (save the desktop as a
240 backup fallback though)
241 */
242 if (client_can_focus(c))
243 {
244 if (c->desktop == screen_desktop && client_normal(c)) {
245 ob_debug_type(OB_DEBUG_FOCUS, "found in focus order\n");
246 return it->data;
247 } else if (c->type == OB_CLIENT_TYPE_DESKTOP &&
248 desktop == NULL)
249 desktop = c;
250 }
251 }
252
253 /* as a last resort fallback to the desktop window if there is one.
254 (if there's more than one, then the one most recently focused.)
255 */
256 ob_debug_type(OB_DEBUG_FOCUS, "found desktop: \n", !!desktop);
257 return desktop;
258 }
259
260 ObClient* focus_fallback(gboolean allow_refocus)
261 {
262 ObClient *new;
263 ObClient *old;
264
265 old = focus_client;
266 new = focus_fallback_target(allow_refocus, focus_client);
267
268 /* unfocus any focused clients.. they can be focused by Pointer events
269 and such, and then when we try focus them, we won't get a FocusIn
270 event at all for them. */
271 focus_nothing();
272
273 if (new)
274 client_focus(new);
275
276 return new;
277 }
278
279 void focus_nothing()
280 {
281 /* Install our own colormap */
282 if (focus_client != NULL) {
283 screen_install_colormap(focus_client, FALSE);
284 screen_install_colormap(NULL, TRUE);
285 }
286
287 /* Don't set focus_client to NULL here. It will be set to NULL when the
288 FocusOut event comes. Otherwise, if we focus nothing and then focus the
289 same window again, The focus code says nothing changed, but focus_client
290 ends up being NULL anyways.
291 focus_client = NULL;
292 */
293
294 /* if there is a grab going on, then we need to cancel it. if we move
295 focus during the grab, applications will get NotifyWhileGrabbed events
296 and ignore them !
297
298 actions should not rely on being able to move focus during an
299 interactive grab.
300 */
301 if (keyboard_interactively_grabbed())
302 keyboard_interactive_cancel();
303
304 /* when nothing will be focused, send focus to the backup target */
305 XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
306 event_curtime);
307 }
308
309 static gchar *popup_get_name(ObClient *c, ObClient **nametarget)
310 {
311 ObClient *p;
312 gchar *title = NULL;
313 const gchar *desk = NULL;
314 gchar *ret;
315
316 /* find our highest direct parent, including non-normal windows */
317 for (p = c; p->transient_for && p->transient_for != OB_TRAN_GROUP;
318 p = p->transient_for);
319
320 if (c->desktop != DESKTOP_ALL && c->desktop != screen_desktop)
321 desk = screen_desktop_names[c->desktop];
322
323 /* use the transient's parent's title/icon if we don't have one */
324 if (p != c && !strcmp("", (c->iconic ? c->icon_title : c->title)))
325 title = g_strdup(p->iconic ? p->icon_title : p->title);
326
327 if (title == NULL)
328 title = g_strdup(c->iconic ? c->icon_title : c->title);
329
330 if (desk)
331 ret = g_strdup_printf("%s [%s]", title, desk);
332 else {
333 ret = title;
334 title = NULL;
335 }
336 g_free(title);
337
338 /* set this only if we're returning true and they asked for it */
339 if (ret && nametarget) *nametarget = p;
340 return ret;
341 }
342
343 static void popup_cycle(ObClient *c, gboolean show,
344 gboolean all_desktops, gboolean dock_windows,
345 gboolean desktop_windows)
346 {
347 gchar *showtext = NULL;
348 ObClient *showtarget;
349
350 if (!show) {
351 icon_popup_hide(focus_cycle_popup);
352 return;
353 }
354
355 /* do this stuff only when the dialog is first showing */
356 if (!focus_cycle_popup->popup->mapped &&
357 !focus_cycle_popup->popup->delay_mapped)
358 {
359 Rect *a;
360 gchar **names;
361 GList *targets = NULL, *it;
362 gint n = 0, i;
363
364 /* position the popup */
365 a = screen_physical_area_monitor(0);
366 icon_popup_position(focus_cycle_popup, CenterGravity,
367 a->x + a->width / 2, a->y + a->height / 2);
368 icon_popup_height(focus_cycle_popup, POPUP_HEIGHT);
369 icon_popup_min_width(focus_cycle_popup, POPUP_WIDTH);
370 icon_popup_max_width(focus_cycle_popup,
371 MAX(a->width/3, POPUP_WIDTH));
372
373
374 /* make its width to be the width of all the possible titles */
375
376 /* build a list of all the valid focus targets */
377 for (it = focus_order; it; it = g_list_next(it)) {
378 ObClient *ft = it->data;
379 if (valid_focus_target(ft, all_desktops, dock_windows
380 , desktop_windows))
381 {
382 targets = g_list_prepend(targets, ft);
383 ++n;
384 }
385 }
386 /* make it null terminated so we can use g_strfreev */
387 names = g_new(char*, n+1);
388 for (it = targets, i = 0; it; it = g_list_next(it), ++i) {
389 ObClient *ft = it->data, *t;
390 names[i] = popup_get_name(ft, &t);
391
392 /* little optimization.. save this text and client, so we dont
393 have to get it again */
394 if (ft == c) {
395 showtext = g_strdup(names[i]);
396 showtarget = t;
397 }
398 }
399 names[n] = NULL;
400
401 icon_popup_text_width_to_strings(focus_cycle_popup, names, n);
402 g_strfreev(names);
403 }
404
405
406 if (!showtext) showtext = popup_get_name(c, &showtarget);
407 icon_popup_show(focus_cycle_popup, showtext,
408 client_icon(showtarget, 48, 48));
409 g_free(showtext);
410 }
411
412 static void focus_cycle_destroy_notify(ObClient *client, gpointer data)
413 {
414 /* end cycling if the target disappears. CurrentTime is fine, time won't
415 be used
416 */
417 if (focus_cycle_target == client)
418 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
419 }
420
421 void focus_cycle_draw_indicator()
422 {
423 if (!focus_cycle_target) {
424 XUnmapWindow(ob_display, focus_indicator.top.win);
425 XUnmapWindow(ob_display, focus_indicator.left.win);
426 XUnmapWindow(ob_display, focus_indicator.right.win);
427 XUnmapWindow(ob_display, focus_indicator.bottom.win);
428
429 /* kill enter events cause by this unmapping */
430 event_ignore_queued_enters();
431 } else {
432 /*
433 if (focus_cycle_target)
434 frame_adjust_focus(focus_cycle_target->frame, FALSE);
435 frame_adjust_focus(focus_cycle_target->frame, TRUE);
436 */
437 gint x, y, w, h;
438 gint wt, wl, wr, wb;
439
440 wt = wl = wr = wb = FOCUS_INDICATOR_WIDTH;
441
442 x = focus_cycle_target->frame->area.x;
443 y = focus_cycle_target->frame->area.y;
444 w = focus_cycle_target->frame->area.width;
445 h = wt;
446
447 XMoveResizeWindow(ob_display, focus_indicator.top.win,
448 x, y, w, h);
449 a_focus_indicator->texture[0].data.lineart.x1 = 0;
450 a_focus_indicator->texture[0].data.lineart.y1 = h-1;
451 a_focus_indicator->texture[0].data.lineart.x2 = 0;
452 a_focus_indicator->texture[0].data.lineart.y2 = 0;
453 a_focus_indicator->texture[1].data.lineart.x1 = 0;
454 a_focus_indicator->texture[1].data.lineart.y1 = 0;
455 a_focus_indicator->texture[1].data.lineart.x2 = w-1;
456 a_focus_indicator->texture[1].data.lineart.y2 = 0;
457 a_focus_indicator->texture[2].data.lineart.x1 = w-1;
458 a_focus_indicator->texture[2].data.lineart.y1 = 0;
459 a_focus_indicator->texture[2].data.lineart.x2 = w-1;
460 a_focus_indicator->texture[2].data.lineart.y2 = h-1;
461 a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
462 a_focus_indicator->texture[3].data.lineart.y1 = h-1;
463 a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
464 a_focus_indicator->texture[3].data.lineart.y2 = h-1;
465 RrPaint(a_focus_indicator, focus_indicator.top.win,
466 w, h);
467
468 x = focus_cycle_target->frame->area.x;
469 y = focus_cycle_target->frame->area.y;
470 w = wl;
471 h = focus_cycle_target->frame->area.height;
472
473 XMoveResizeWindow(ob_display, focus_indicator.left.win,
474 x, y, w, h);
475 a_focus_indicator->texture[0].data.lineart.x1 = w-1;
476 a_focus_indicator->texture[0].data.lineart.y1 = 0;
477 a_focus_indicator->texture[0].data.lineart.x2 = 0;
478 a_focus_indicator->texture[0].data.lineart.y2 = 0;
479 a_focus_indicator->texture[1].data.lineart.x1 = 0;
480 a_focus_indicator->texture[1].data.lineart.y1 = 0;
481 a_focus_indicator->texture[1].data.lineart.x2 = 0;
482 a_focus_indicator->texture[1].data.lineart.y2 = h-1;
483 a_focus_indicator->texture[2].data.lineart.x1 = 0;
484 a_focus_indicator->texture[2].data.lineart.y1 = h-1;
485 a_focus_indicator->texture[2].data.lineart.x2 = w-1;
486 a_focus_indicator->texture[2].data.lineart.y2 = h-1;
487 a_focus_indicator->texture[3].data.lineart.x1 = w-1;
488 a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
489 a_focus_indicator->texture[3].data.lineart.x2 = w-1;
490 a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
491 RrPaint(a_focus_indicator, focus_indicator.left.win,
492 w, h);
493
494 x = focus_cycle_target->frame->area.x +
495 focus_cycle_target->frame->area.width - wr;
496 y = focus_cycle_target->frame->area.y;
497 w = wr;
498 h = focus_cycle_target->frame->area.height ;
499
500 XMoveResizeWindow(ob_display, focus_indicator.right.win,
501 x, y, w, h);
502 a_focus_indicator->texture[0].data.lineart.x1 = 0;
503 a_focus_indicator->texture[0].data.lineart.y1 = 0;
504 a_focus_indicator->texture[0].data.lineart.x2 = w-1;
505 a_focus_indicator->texture[0].data.lineart.y2 = 0;
506 a_focus_indicator->texture[1].data.lineart.x1 = w-1;
507 a_focus_indicator->texture[1].data.lineart.y1 = 0;
508 a_focus_indicator->texture[1].data.lineart.x2 = w-1;
509 a_focus_indicator->texture[1].data.lineart.y2 = h-1;
510 a_focus_indicator->texture[2].data.lineart.x1 = w-1;
511 a_focus_indicator->texture[2].data.lineart.y1 = h-1;
512 a_focus_indicator->texture[2].data.lineart.x2 = 0;
513 a_focus_indicator->texture[2].data.lineart.y2 = h-1;
514 a_focus_indicator->texture[3].data.lineart.x1 = 0;
515 a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
516 a_focus_indicator->texture[3].data.lineart.x2 = 0;
517 a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
518 RrPaint(a_focus_indicator, focus_indicator.right.win,
519 w, h);
520
521 x = focus_cycle_target->frame->area.x;
522 y = focus_cycle_target->frame->area.y +
523 focus_cycle_target->frame->area.height - wb;
524 w = focus_cycle_target->frame->area.width;
525 h = wb;
526
527 XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
528 x, y, w, h);
529 a_focus_indicator->texture[0].data.lineart.x1 = 0;
530 a_focus_indicator->texture[0].data.lineart.y1 = 0;
531 a_focus_indicator->texture[0].data.lineart.x2 = 0;
532 a_focus_indicator->texture[0].data.lineart.y2 = h-1;
533 a_focus_indicator->texture[1].data.lineart.x1 = 0;
534 a_focus_indicator->texture[1].data.lineart.y1 = h-1;
535 a_focus_indicator->texture[1].data.lineart.x2 = w-1;
536 a_focus_indicator->texture[1].data.lineart.y2 = h-1;
537 a_focus_indicator->texture[2].data.lineart.x1 = w-1;
538 a_focus_indicator->texture[2].data.lineart.y1 = h-1;
539 a_focus_indicator->texture[2].data.lineart.x2 = w-1;
540 a_focus_indicator->texture[2].data.lineart.y2 = 0;
541 a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
542 a_focus_indicator->texture[3].data.lineart.y1 = 0;
543 a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
544 a_focus_indicator->texture[3].data.lineart.y2 = 0;
545 RrPaint(a_focus_indicator, focus_indicator.bottom.win,
546 w, h);
547
548 XMapWindow(ob_display, focus_indicator.top.win);
549 XMapWindow(ob_display, focus_indicator.left.win);
550 XMapWindow(ob_display, focus_indicator.right.win);
551 XMapWindow(ob_display, focus_indicator.bottom.win);
552 }
553 }
554
555 static gboolean has_valid_group_siblings_on_desktop(ObClient *ft,
556 gboolean all_desktops)
557
558 {
559 GSList *it;
560
561 if (!ft->group) return FALSE;
562
563 for (it = ft->group->members; it; it = g_slist_next(it)) {
564 ObClient *c = it->data;
565 /* check that it's not a helper window to avoid infinite recursion */
566 if (c != ft && !client_helper(c) &&
567 valid_focus_target(c, all_desktops, FALSE, FALSE))
568 {
569 return TRUE;
570 }
571 }
572 return FALSE;
573 }
574
575 /*! @param allow_helpers This is used for calling itself recursively while
576 checking helper windows. */
577 static gboolean valid_focus_target(ObClient *ft,
578 gboolean all_desktops,
579 gboolean dock_windows,
580 gboolean desktop_windows)
581 {
582 gboolean ok = FALSE;
583
584 /* it's on this desktop unless you want all desktops.
585
586 do this check first because it will usually filter out the most
587 windows */
588 ok = (all_desktops || ft->desktop == screen_desktop ||
589 ft->desktop == DESKTOP_ALL);
590
591 /* the window can receive focus somehow */
592 ok = ok && (ft->can_focus || ft->focus_notify);
593
594 /* it's the right type of window */
595 if (dock_windows || desktop_windows)
596 ok = ok && ((dock_windows && ft->type == OB_CLIENT_TYPE_DOCK) ||
597 (desktop_windows && ft->type == OB_CLIENT_TYPE_DESKTOP));
598 else
599 /* normal non-helper windows are valid targets */
600 ok = ok &&
601 ((client_normal(ft) && !client_helper(ft))
602 ||
603 /* helper windows are valid targets it... */
604 (client_helper(ft) &&
605 /* ...a window in its group already has focus ... */
606 ((focus_client && ft->group == focus_client->group) ||
607 /* ... or if there are no other windows in its group
608 that can be cycled to instead */
609 !has_valid_group_siblings_on_desktop(ft, all_desktops))));
610
611 /* it's not set to skip the taskbar (unless it is a type that would be
612 expected to set this hint */
613 ok = ok && ((ft->type == OB_CLIENT_TYPE_DOCK ||
614 ft->type == OB_CLIENT_TYPE_DESKTOP ||
615 ft->type == OB_CLIENT_TYPE_TOOLBAR ||
616 ft->type == OB_CLIENT_TYPE_MENU ||
617 ft->type == OB_CLIENT_TYPE_UTILITY) ||
618 !ft->skip_taskbar);
619
620 /* it's not going to just send fous off somewhere else (modal window) */
621 ok = ok && ft == client_focus_target(ft);
622
623 return ok;
624 }
625
626 void focus_cycle(gboolean forward, gboolean all_desktops,
627 gboolean dock_windows, gboolean desktop_windows,
628 gboolean linear, gboolean interactive,
629 gboolean dialog, gboolean done, gboolean cancel)
630 {
631 static ObClient *first = NULL;
632 static ObClient *t = NULL;
633 static GList *order = NULL;
634 GList *it, *start, *list;
635 ObClient *ft = NULL;
636
637 if (interactive) {
638 if (cancel) {
639 focus_cycle_target = NULL;
640 goto done_cycle;
641 } else if (done)
642 goto done_cycle;
643
644 if (!focus_order)
645 goto done_cycle;
646
647 if (!first) first = focus_client;
648
649 if (linear) list = client_list;
650 else list = focus_order;
651 } else {
652 if (!focus_order)
653 goto done_cycle;
654 list = client_list;
655 }
656 if (!focus_cycle_target) focus_cycle_target = focus_client;
657
658 start = it = g_list_find(list, focus_cycle_target);
659 if (!start) /* switched desktops or something? */
660 start = it = forward ? g_list_last(list) : g_list_first(list);
661 if (!start) goto done_cycle;
662
663 do {
664 if (forward) {
665 it = it->next;
666 if (it == NULL) it = g_list_first(list);
667 } else {
668 it = it->prev;
669 if (it == NULL) it = g_list_last(list);
670 }
671 ft = it->data;
672 if (valid_focus_target(ft, all_desktops, dock_windows,
673 desktop_windows))
674 {
675 if (interactive) {
676 if (ft != focus_cycle_target) { /* prevents flicker */
677 focus_cycle_target = ft;
678 focus_cycle_draw_indicator();
679 }
680 /* same arguments as valid_focus_target */
681 popup_cycle(ft, dialog, all_desktops, dock_windows,
682 desktop_windows);
683 return;
684 } else if (ft != focus_cycle_target) {
685 focus_cycle_target = ft;
686 done = TRUE;
687 break;
688 }
689 }
690 } while (it != start);
691
692 done_cycle:
693 if (done && focus_cycle_target)
694 client_activate(focus_cycle_target, FALSE, TRUE);
695
696 t = NULL;
697 first = NULL;
698 focus_cycle_target = NULL;
699 g_list_free(order);
700 order = NULL;
701
702 if (interactive) {
703 focus_cycle_draw_indicator();
704 popup_cycle(ft, FALSE, FALSE, FALSE, FALSE);
705 }
706
707 return;
708 }
709
710 /* this be mostly ripped from fvwm */
711 static ObClient *focus_find_directional(ObClient *c, ObDirection dir,
712 gboolean dock_windows,
713 gboolean desktop_windows)
714 {
715 gint my_cx, my_cy, his_cx, his_cy;
716 gint offset = 0;
717 gint distance = 0;
718 gint score, best_score;
719 ObClient *best_client, *cur;
720 GList *it;
721
722 if(!client_list)
723 return NULL;
724
725 /* first, find the centre coords of the currently focused window */
726 my_cx = c->frame->area.x + c->frame->area.width / 2;
727 my_cy = c->frame->area.y + c->frame->area.height / 2;
728
729 best_score = -1;
730 best_client = NULL;
731
732 for(it = g_list_first(client_list); it; it = g_list_next(it)) {
733 cur = it->data;
734
735 /* the currently selected window isn't interesting */
736 if(cur == c)
737 continue;
738 if (cur->type == OB_CLIENT_TYPE_DOCK && !dock_windows)
739 continue;
740 if (cur->type == OB_CLIENT_TYPE_DESKTOP && !desktop_windows)
741 continue;
742 if (!client_normal(cur) &&
743 cur->type != OB_CLIENT_TYPE_DOCK &&
744 cur->type != OB_CLIENT_TYPE_DESKTOP)
745 continue;
746 /* using c->desktop instead of screen_desktop doesn't work if the
747 * current window was omnipresent, hope this doesn't have any other
748 * side effects */
749 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
750 continue;
751 if(cur->iconic)
752 continue;
753 if(!(client_focus_target(cur) == cur &&
754 client_can_focus(cur)))
755 continue;
756
757 /* find the centre coords of this window, from the
758 * currently focused window's point of view */
759 his_cx = (cur->frame->area.x - my_cx)
760 + cur->frame->area.width / 2;
761 his_cy = (cur->frame->area.y - my_cy)
762 + cur->frame->area.height / 2;
763
764 if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
765 dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
766 gint tx;
767 /* Rotate the diagonals 45 degrees counterclockwise.
768 * To do this, multiply the matrix /+h +h\ with the
769 * vector (x y). \-h +h/
770 * h = sqrt(0.5). We can set h := 1 since absolute
771 * distance doesn't matter here. */
772 tx = his_cx + his_cy;
773 his_cy = -his_cx + his_cy;
774 his_cx = tx;
775 }
776
777 switch(dir) {
778 case OB_DIRECTION_NORTH:
779 case OB_DIRECTION_SOUTH:
780 case OB_DIRECTION_NORTHEAST:
781 case OB_DIRECTION_SOUTHWEST:
782 offset = (his_cx < 0) ? -his_cx : his_cx;
783 distance = ((dir == OB_DIRECTION_NORTH ||
784 dir == OB_DIRECTION_NORTHEAST) ?
785 -his_cy : his_cy);
786 break;
787 case OB_DIRECTION_EAST:
788 case OB_DIRECTION_WEST:
789 case OB_DIRECTION_SOUTHEAST:
790 case OB_DIRECTION_NORTHWEST:
791 offset = (his_cy < 0) ? -his_cy : his_cy;
792 distance = ((dir == OB_DIRECTION_WEST ||
793 dir == OB_DIRECTION_NORTHWEST) ?
794 -his_cx : his_cx);
795 break;
796 }
797
798 /* the target must be in the requested direction */
799 if(distance <= 0)
800 continue;
801
802 /* Calculate score for this window. The smaller the better. */
803 score = distance + offset;
804
805 /* windows more than 45 degrees off the direction are
806 * heavily penalized and will only be chosen if nothing
807 * else within a million pixels */
808 if(offset > distance)
809 score += 1000000;
810
811 if(best_score == -1 || score < best_score)
812 best_client = cur,
813 best_score = score;
814 }
815
816 return best_client;
817 }
818
819 void focus_directional_cycle(ObDirection dir, gboolean dock_windows,
820 gboolean desktop_windows, gboolean interactive,
821 gboolean dialog, gboolean done, gboolean cancel)
822 {
823 static ObClient *first = NULL;
824 ObClient *ft = NULL;
825
826 if (!interactive)
827 return;
828
829 if (cancel) {
830 focus_cycle_target = NULL;
831 goto done_cycle;
832 } else if (done)
833 goto done_cycle;
834
835 if (!focus_order)
836 goto done_cycle;
837
838 if (!first) first = focus_client;
839 if (!focus_cycle_target) focus_cycle_target = focus_client;
840
841 if (focus_cycle_target)
842 ft = focus_find_directional(focus_cycle_target, dir, dock_windows,
843 desktop_windows);
844 else {
845 GList *it;
846
847 for (it = focus_order; it; it = g_list_next(it))
848 if (valid_focus_target(it->data, FALSE, dock_windows,
849 desktop_windows))
850 ft = it->data;
851 }
852
853 if (ft) {
854 if (ft != focus_cycle_target) {/* prevents flicker */
855 focus_cycle_target = ft;
856 focus_cycle_draw_indicator();
857 }
858 }
859 if (focus_cycle_target) {
860 /* same arguments as valid_focus_target */
861 popup_cycle(focus_cycle_target, dialog, FALSE, dock_windows,
862 desktop_windows);
863 if (dialog)
864 return;
865 }
866
867
868 done_cycle:
869 if (done && focus_cycle_target)
870 client_activate(focus_cycle_target, FALSE, TRUE);
871
872 first = NULL;
873 focus_cycle_target = NULL;
874
875 focus_cycle_draw_indicator();
876 popup_cycle(ft, FALSE, FALSE, FALSE, FALSE);
877
878 return;
879 }
880
881 void focus_order_add_new(ObClient *c)
882 {
883 if (c->iconic)
884 focus_order_to_top(c);
885 else {
886 g_assert(!g_list_find(focus_order, c));
887 /* if there are any iconic windows, put this above them in the order,
888 but if there are not, then put it under the currently focused one */
889 if (focus_order && ((ObClient*)focus_order->data)->iconic)
890 focus_order = g_list_insert(focus_order, c, 0);
891 else
892 focus_order = g_list_insert(focus_order, c, 1);
893 }
894 }
895
896 void focus_order_remove(ObClient *c)
897 {
898 focus_order = g_list_remove(focus_order, c);
899 }
900
901 void focus_order_to_top(ObClient *c)
902 {
903 focus_order = g_list_remove(focus_order, c);
904 if (!c->iconic) {
905 focus_order = g_list_prepend(focus_order, c);
906 } else {
907 GList *it;
908
909 /* insert before first iconic window */
910 for (it = focus_order;
911 it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
912 focus_order = g_list_insert_before(focus_order, it, c);
913 }
914 }
915
916 void focus_order_to_bottom(ObClient *c)
917 {
918 focus_order = g_list_remove(focus_order, c);
919 if (c->iconic) {
920 focus_order = g_list_append(focus_order, c);
921 } else {
922 GList *it;
923
924 /* insert before first iconic window */
925 for (it = focus_order;
926 it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
927 focus_order = g_list_insert_before(focus_order, it, c);
928 }
929 }
930
931 ObClient *focus_order_find_first(guint desktop)
932 {
933 GList *it;
934 for (it = focus_order; it; it = g_list_next(it)) {
935 ObClient *c = it->data;
936 if (c->desktop == desktop || c->desktop == DESKTOP_ALL)
937 return c;
938 }
939 return NULL;
940 }
This page took 0.073629 seconds and 4 git commands to generate.