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