]> Dogcows Code - chaz/openbox/blob - openbox/focus.c
4c663c3f124eabd30a223489263f1f24b6eb2120
[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 ObClient *focus_client = NULL;
41 GList *focus_order = NULL;
42 ObClient *focus_cycle_target = NULL;
43
44 struct {
45 InternalWindow top;
46 InternalWindow left;
47 InternalWindow right;
48 InternalWindow bottom;
49 } focus_indicator;
50
51 RrAppearance *a_focus_indicator;
52 RrColor *color_white;
53
54 static ObIconPopup *focus_cycle_popup;
55
56 static void focus_cycle_destructor(ObClient *client, gpointer data)
57 {
58 /* end cycling if the target disappears. CurrentTime is fine, time won't
59 be used
60 */
61 if (focus_cycle_target == client)
62 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
63 }
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_destructor(focus_cycle_destructor, 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_destructor(focus_cycle_destructor);
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);
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 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
236 2. it is validated. if the window is about to disappear, then
237 don't try focus it.
238 3. it is visible on the current desktop. this ignores
239 omnipresent windows, which are problematic in their own rite.
240 4. it's not iconic
241 5. it is a normal type window, don't fall back onto a dock or
242 a splashscreen or a desktop window (save the desktop as a
243 backup fallback though)
244 */
245 if (client_can_focus(c) && !c->iconic)
246 {
247 if (c->desktop == screen_desktop && client_normal(c)) {
248 ob_debug_type(OB_DEBUG_FOCUS, "found in focus order\n");
249 return it->data;
250 } else if ((c->desktop == screen_desktop ||
251 c->desktop == DESKTOP_ALL) &&
252 c->type == OB_CLIENT_TYPE_DESKTOP &&
253 desktop == NULL)
254 desktop = c;
255 }
256 }
257
258 /* as a last resort fallback to the desktop window if there is one.
259 (if there's more than one, then the one most recently focused.)
260 */
261 ob_debug_type(OB_DEBUG_FOCUS, "found desktop: \n", !!desktop);
262 return desktop;
263 }
264
265 void focus_fallback(gboolean allow_refocus)
266 {
267 ObClient *new;
268 ObClient *old = focus_client;
269
270 /* unfocus any focused clients.. they can be focused by Pointer events
271 and such, and then when I try focus them, I won't get a FocusIn event
272 at all for them.
273 */
274 focus_nothing();
275
276 if ((new = focus_fallback_target(allow_refocus, old)))
277 client_focus(new);
278 }
279
280 void focus_nothing()
281 {
282 /* Install our own colormap */
283 if (focus_client != NULL) {
284 screen_install_colormap(focus_client, FALSE);
285 screen_install_colormap(NULL, TRUE);
286 }
287
288 focus_client = NULL;
289
290 /* when nothing will be focused, send focus to the backup target */
291 XSetInputFocus(ob_display, screen_support_win, RevertToPointerRoot,
292 event_curtime);
293 }
294
295 static void popup_cycle(ObClient *c, gboolean show)
296 {
297 if (!show) {
298 icon_popup_hide(focus_cycle_popup);
299 } else {
300 Rect *a;
301 ObClient *p = c;
302 gchar *title = NULL;
303
304 a = screen_physical_area_monitor(0);
305 icon_popup_position(focus_cycle_popup, CenterGravity,
306 a->x + a->width / 2, a->y + a->height / 2);
307 /* icon_popup_size(focus_cycle_popup, a->height/2, a->height/16);
308 icon_popup_show(focus_cycle_popup, c->title,
309 client_icon(c, a->height/16, a->height/16));
310 */
311 /* XXX the size and the font extents need to be related on some level
312 */
313 icon_popup_size(focus_cycle_popup, POPUP_WIDTH, POPUP_HEIGHT);
314
315 /* use the transient's parent's title/icon */
316 while (p->transient_for && p->transient_for != OB_TRAN_GROUP)
317 p = p->transient_for;
318
319 if (p != c && !strcmp("", (c->iconic ? c->icon_title : c->title)))
320 title = g_strdup(p->iconic ? p->icon_title : p->title);
321 /*title = g_strconcat((c->iconic ? c->icon_title : c->title),
322 " - ",
323 (p->iconic ? p->icon_title : p->title),
324 NULL);
325 */
326 icon_popup_show(focus_cycle_popup,
327 (title ? title :
328 (c->iconic ? c->icon_title : c->title)),
329 client_icon(p, 48, 48));
330 g_free(title);
331 }
332 }
333
334 void focus_cycle_draw_indicator()
335 {
336 if (!focus_cycle_target) {
337 XUnmapWindow(ob_display, focus_indicator.top.win);
338 XUnmapWindow(ob_display, focus_indicator.left.win);
339 XUnmapWindow(ob_display, focus_indicator.right.win);
340 XUnmapWindow(ob_display, focus_indicator.bottom.win);
341
342 /* kill enter events cause by this unmapping */
343 event_ignore_queued_enters();
344 } else {
345 /*
346 if (focus_cycle_target)
347 frame_adjust_focus(focus_cycle_target->frame, FALSE);
348 frame_adjust_focus(focus_cycle_target->frame, TRUE);
349 */
350 gint x, y, w, h;
351 gint wt, wl, wr, wb;
352
353 wt = wl = wr = wb = MAX(3,
354 MAX(1, MAX(ob_rr_theme->paddingx,
355 ob_rr_theme->paddingy)) * 2 +
356 ob_rr_theme->fbwidth * 2);
357
358 x = focus_cycle_target->frame->area.x;
359 y = focus_cycle_target->frame->area.y;
360 w = focus_cycle_target->frame->area.width;
361 h = wt;
362
363 XMoveResizeWindow(ob_display, focus_indicator.top.win,
364 x, y, w, h);
365 a_focus_indicator->texture[0].data.lineart.x1 = 0;
366 a_focus_indicator->texture[0].data.lineart.y1 = h-1;
367 a_focus_indicator->texture[0].data.lineart.x2 = 0;
368 a_focus_indicator->texture[0].data.lineart.y2 = 0;
369 a_focus_indicator->texture[1].data.lineart.x1 = 0;
370 a_focus_indicator->texture[1].data.lineart.y1 = 0;
371 a_focus_indicator->texture[1].data.lineart.x2 = w-1;
372 a_focus_indicator->texture[1].data.lineart.y2 = 0;
373 a_focus_indicator->texture[2].data.lineart.x1 = w-1;
374 a_focus_indicator->texture[2].data.lineart.y1 = 0;
375 a_focus_indicator->texture[2].data.lineart.x2 = w-1;
376 a_focus_indicator->texture[2].data.lineart.y2 = h-1;
377 a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
378 a_focus_indicator->texture[3].data.lineart.y1 = h-1;
379 a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
380 a_focus_indicator->texture[3].data.lineart.y2 = h-1;
381 RrPaint(a_focus_indicator, focus_indicator.top.win,
382 w, h);
383
384 x = focus_cycle_target->frame->area.x;
385 y = focus_cycle_target->frame->area.y;
386 w = wl;
387 h = focus_cycle_target->frame->area.height;
388
389 XMoveResizeWindow(ob_display, focus_indicator.left.win,
390 x, y, w, h);
391 a_focus_indicator->texture[0].data.lineart.x1 = w-1;
392 a_focus_indicator->texture[0].data.lineart.y1 = 0;
393 a_focus_indicator->texture[0].data.lineart.x2 = 0;
394 a_focus_indicator->texture[0].data.lineart.y2 = 0;
395 a_focus_indicator->texture[1].data.lineart.x1 = 0;
396 a_focus_indicator->texture[1].data.lineart.y1 = 0;
397 a_focus_indicator->texture[1].data.lineart.x2 = 0;
398 a_focus_indicator->texture[1].data.lineart.y2 = h-1;
399 a_focus_indicator->texture[2].data.lineart.x1 = 0;
400 a_focus_indicator->texture[2].data.lineart.y1 = h-1;
401 a_focus_indicator->texture[2].data.lineart.x2 = w-1;
402 a_focus_indicator->texture[2].data.lineart.y2 = h-1;
403 a_focus_indicator->texture[3].data.lineart.x1 = w-1;
404 a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
405 a_focus_indicator->texture[3].data.lineart.x2 = w-1;
406 a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
407 RrPaint(a_focus_indicator, focus_indicator.left.win,
408 w, h);
409
410 x = focus_cycle_target->frame->area.x +
411 focus_cycle_target->frame->area.width - wr;
412 y = focus_cycle_target->frame->area.y;
413 w = wr;
414 h = focus_cycle_target->frame->area.height ;
415
416 XMoveResizeWindow(ob_display, focus_indicator.right.win,
417 x, y, w, h);
418 a_focus_indicator->texture[0].data.lineart.x1 = 0;
419 a_focus_indicator->texture[0].data.lineart.y1 = 0;
420 a_focus_indicator->texture[0].data.lineart.x2 = w-1;
421 a_focus_indicator->texture[0].data.lineart.y2 = 0;
422 a_focus_indicator->texture[1].data.lineart.x1 = w-1;
423 a_focus_indicator->texture[1].data.lineart.y1 = 0;
424 a_focus_indicator->texture[1].data.lineart.x2 = w-1;
425 a_focus_indicator->texture[1].data.lineart.y2 = h-1;
426 a_focus_indicator->texture[2].data.lineart.x1 = w-1;
427 a_focus_indicator->texture[2].data.lineart.y1 = h-1;
428 a_focus_indicator->texture[2].data.lineart.x2 = 0;
429 a_focus_indicator->texture[2].data.lineart.y2 = h-1;
430 a_focus_indicator->texture[3].data.lineart.x1 = 0;
431 a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
432 a_focus_indicator->texture[3].data.lineart.x2 = 0;
433 a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
434 RrPaint(a_focus_indicator, focus_indicator.right.win,
435 w, h);
436
437 x = focus_cycle_target->frame->area.x;
438 y = focus_cycle_target->frame->area.y +
439 focus_cycle_target->frame->area.height - wb;
440 w = focus_cycle_target->frame->area.width;
441 h = wb;
442
443 XMoveResizeWindow(ob_display, focus_indicator.bottom.win,
444 x, y, w, h);
445 a_focus_indicator->texture[0].data.lineart.x1 = 0;
446 a_focus_indicator->texture[0].data.lineart.y1 = 0;
447 a_focus_indicator->texture[0].data.lineart.x2 = 0;
448 a_focus_indicator->texture[0].data.lineart.y2 = h-1;
449 a_focus_indicator->texture[1].data.lineart.x1 = 0;
450 a_focus_indicator->texture[1].data.lineart.y1 = h-1;
451 a_focus_indicator->texture[1].data.lineart.x2 = w-1;
452 a_focus_indicator->texture[1].data.lineart.y2 = h-1;
453 a_focus_indicator->texture[2].data.lineart.x1 = w-1;
454 a_focus_indicator->texture[2].data.lineart.y1 = h-1;
455 a_focus_indicator->texture[2].data.lineart.x2 = w-1;
456 a_focus_indicator->texture[2].data.lineart.y2 = 0;
457 a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
458 a_focus_indicator->texture[3].data.lineart.y1 = 0;
459 a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
460 a_focus_indicator->texture[3].data.lineart.y2 = 0;
461 RrPaint(a_focus_indicator, focus_indicator.bottom.win,
462 w, h);
463
464 XMapWindow(ob_display, focus_indicator.top.win);
465 XMapWindow(ob_display, focus_indicator.left.win);
466 XMapWindow(ob_display, focus_indicator.right.win);
467 XMapWindow(ob_display, focus_indicator.bottom.win);
468 }
469 }
470
471 static gboolean valid_focus_target(ObClient *ft, gboolean dock_windows)
472 {
473 gboolean ok = FALSE;
474 /* we don't use client_can_focus here, because that doesn't let you
475 focus an iconic window, but we want to be able to, so we just check
476 if the focus flags on the window allow it, and its on the current
477 desktop */
478 if (dock_windows)
479 ok = ft->type == OB_CLIENT_TYPE_DOCK;
480 else
481 ok = (ft->type == OB_CLIENT_TYPE_NORMAL ||
482 ft->type == OB_CLIENT_TYPE_DIALOG ||
483 (!client_has_group_siblings(ft) &&
484 (ft->type == OB_CLIENT_TYPE_TOOLBAR ||
485 ft->type == OB_CLIENT_TYPE_MENU ||
486 ft->type == OB_CLIENT_TYPE_UTILITY)));
487 ok = ok && (ft->can_focus || ft->focus_notify);
488 ok = ok && !ft->skip_pager;
489 ok = ok && (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL);
490 ok = ok && ft == client_focus_target(ft);
491 return ok;
492 /*
493 {
494 GSList *it;
495
496 for (it = ft->transients; it; it = g_slist_next(it)) {
497 ObClient *c = it->data;
498
499 if (c->frame->visible)
500 return FALSE;
501 }
502 return TRUE;
503 }
504 */
505 }
506
507 void focus_cycle(gboolean forward, gboolean dock_windows,
508 gboolean linear, gboolean interactive,
509 gboolean dialog, gboolean done, gboolean cancel)
510 {
511 static ObClient *first = NULL;
512 static ObClient *t = NULL;
513 static GList *order = NULL;
514 GList *it, *start, *list;
515 ObClient *ft = NULL;
516
517 if (interactive) {
518 if (cancel) {
519 focus_cycle_target = NULL;
520 goto done_cycle;
521 } else if (done)
522 goto done_cycle;
523
524 if (!focus_order)
525 goto done_cycle;
526
527 if (!first) first = focus_client;
528
529 if (linear) list = client_list;
530 else list = focus_order;
531 } else {
532 if (!focus_order)
533 goto done_cycle;
534 list = client_list;
535 }
536 if (!focus_cycle_target) focus_cycle_target = focus_client;
537
538 start = it = g_list_find(list, focus_cycle_target);
539 if (!start) /* switched desktops or something? */
540 start = it = forward ? g_list_last(list) : g_list_first(list);
541 if (!start) goto done_cycle;
542
543 do {
544 if (forward) {
545 it = it->next;
546 if (it == NULL) it = g_list_first(list);
547 } else {
548 it = it->prev;
549 if (it == NULL) it = g_list_last(list);
550 }
551 ft = it->data;
552 if (valid_focus_target(ft, dock_windows)) {
553 if (interactive) {
554 if (ft != focus_cycle_target) { /* prevents flicker */
555 focus_cycle_target = ft;
556 focus_cycle_draw_indicator();
557 }
558 popup_cycle(ft, dialog);
559 return;
560 } else if (ft != focus_cycle_target) {
561 focus_cycle_target = ft;
562 done = TRUE;
563 break;
564 }
565 }
566 } while (it != start);
567
568 done_cycle:
569 if (done && focus_cycle_target)
570 client_activate(focus_cycle_target, FALSE, TRUE);
571
572 t = NULL;
573 first = NULL;
574 focus_cycle_target = NULL;
575 g_list_free(order);
576 order = NULL;
577
578 if (interactive) {
579 focus_cycle_draw_indicator();
580 popup_cycle(ft, FALSE);
581 }
582
583 return;
584 }
585
586 /* this be mostly ripped from fvwm */
587 ObClient *focus_find_directional(ObClient *c, ObDirection dir,
588 gboolean dock_windows)
589 {
590 gint my_cx, my_cy, his_cx, his_cy;
591 gint offset = 0;
592 gint distance = 0;
593 gint score, best_score;
594 ObClient *best_client, *cur;
595 GList *it;
596
597 if(!client_list)
598 return NULL;
599
600 /* first, find the centre coords of the currently focused window */
601 my_cx = c->frame->area.x + c->frame->area.width / 2;
602 my_cy = c->frame->area.y + c->frame->area.height / 2;
603
604 best_score = -1;
605 best_client = NULL;
606
607 for(it = g_list_first(client_list); it; it = g_list_next(it)) {
608 cur = it->data;
609
610 /* the currently selected window isn't interesting */
611 if(cur == c)
612 continue;
613 if (!dock_windows && !client_normal(cur))
614 continue;
615 if (dock_windows && cur->type != OB_CLIENT_TYPE_DOCK)
616 continue;
617 /* using c->desktop instead of screen_desktop doesn't work if the
618 * current window was omnipresent, hope this doesn't have any other
619 * side effects */
620 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
621 continue;
622 if(cur->iconic)
623 continue;
624 if(!(client_focus_target(cur) == cur &&
625 client_can_focus(cur)))
626 continue;
627
628 /* find the centre coords of this window, from the
629 * currently focused window's point of view */
630 his_cx = (cur->frame->area.x - my_cx)
631 + cur->frame->area.width / 2;
632 his_cy = (cur->frame->area.y - my_cy)
633 + cur->frame->area.height / 2;
634
635 if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
636 dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
637 gint tx;
638 /* Rotate the diagonals 45 degrees counterclockwise.
639 * To do this, multiply the matrix /+h +h\ with the
640 * vector (x y). \-h +h/
641 * h = sqrt(0.5). We can set h := 1 since absolute
642 * distance doesn't matter here. */
643 tx = his_cx + his_cy;
644 his_cy = -his_cx + his_cy;
645 his_cx = tx;
646 }
647
648 switch(dir) {
649 case OB_DIRECTION_NORTH:
650 case OB_DIRECTION_SOUTH:
651 case OB_DIRECTION_NORTHEAST:
652 case OB_DIRECTION_SOUTHWEST:
653 offset = (his_cx < 0) ? -his_cx : his_cx;
654 distance = ((dir == OB_DIRECTION_NORTH ||
655 dir == OB_DIRECTION_NORTHEAST) ?
656 -his_cy : his_cy);
657 break;
658 case OB_DIRECTION_EAST:
659 case OB_DIRECTION_WEST:
660 case OB_DIRECTION_SOUTHEAST:
661 case OB_DIRECTION_NORTHWEST:
662 offset = (his_cy < 0) ? -his_cy : his_cy;
663 distance = ((dir == OB_DIRECTION_WEST ||
664 dir == OB_DIRECTION_NORTHWEST) ?
665 -his_cx : his_cx);
666 break;
667 }
668
669 /* the target must be in the requested direction */
670 if(distance <= 0)
671 continue;
672
673 /* Calculate score for this window. The smaller the better. */
674 score = distance + offset;
675
676 /* windows more than 45 degrees off the direction are
677 * heavily penalized and will only be chosen if nothing
678 * else within a million pixels */
679 if(offset > distance)
680 score += 1000000;
681
682 if(best_score == -1 || score < best_score)
683 best_client = cur,
684 best_score = score;
685 }
686
687 return best_client;
688 }
689
690 void focus_directional_cycle(ObDirection dir, gboolean dock_windows,
691 gboolean interactive,
692 gboolean dialog, gboolean done, gboolean cancel)
693 {
694 static ObClient *first = NULL;
695 ObClient *ft = NULL;
696
697 if (!interactive)
698 return;
699
700 if (cancel) {
701 focus_cycle_target = NULL;
702 goto done_cycle;
703 } else if (done)
704 goto done_cycle;
705
706 if (!focus_order)
707 goto done_cycle;
708
709 if (!first) first = focus_client;
710 if (!focus_cycle_target) focus_cycle_target = focus_client;
711
712 if (focus_cycle_target)
713 ft = focus_find_directional(focus_cycle_target, dir, dock_windows);
714 else {
715 GList *it;
716
717 for (it = focus_order; it; it = g_list_next(it))
718 if (valid_focus_target(it->data, dock_windows))
719 ft = it->data;
720 }
721
722 if (ft) {
723 if (ft != focus_cycle_target) {/* prevents flicker */
724 focus_cycle_target = ft;
725 focus_cycle_draw_indicator();
726 }
727 }
728 if (focus_cycle_target) {
729 popup_cycle(focus_cycle_target, dialog);
730 if (dialog)
731 return;
732 }
733
734
735 done_cycle:
736 if (done && focus_cycle_target)
737 client_activate(focus_cycle_target, FALSE, TRUE);
738
739 first = NULL;
740 focus_cycle_target = NULL;
741
742 focus_cycle_draw_indicator();
743 popup_cycle(ft, FALSE);
744
745 return;
746 }
747
748 void focus_order_add_new(ObClient *c)
749 {
750 if (c->iconic)
751 focus_order_to_top(c);
752 else {
753 g_assert(!g_list_find(focus_order, c));
754 /* if there are any iconic windows, put this above them in the order,
755 but if there are not, then put it under the currently focused one */
756 if (focus_order && ((ObClient*)focus_order->data)->iconic)
757 focus_order = g_list_insert(focus_order, c, 0);
758 else
759 focus_order = g_list_insert(focus_order, c, 1);
760 }
761 }
762
763 void focus_order_remove(ObClient *c)
764 {
765 focus_order = g_list_remove(focus_order, c);
766 }
767
768 void focus_order_to_top(ObClient *c)
769 {
770 focus_order = g_list_remove(focus_order, c);
771 if (!c->iconic) {
772 focus_order = g_list_prepend(focus_order, c);
773 } else {
774 GList *it;
775
776 /* insert before first iconic window */
777 for (it = focus_order;
778 it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
779 focus_order = g_list_insert_before(focus_order, it, c);
780 }
781 }
782
783 void focus_order_to_bottom(ObClient *c)
784 {
785 focus_order = g_list_remove(focus_order, c);
786 if (c->iconic) {
787 focus_order = g_list_append(focus_order, c);
788 } else {
789 GList *it;
790
791 /* insert before first iconic window */
792 for (it = focus_order;
793 it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
794 focus_order = g_list_insert_before(focus_order, it, c);
795 }
796 }
797
798 ObClient *focus_order_find_first(guint desktop)
799 {
800 GList *it;
801 for (it = focus_order; it; it = g_list_next(it)) {
802 ObClient *c = it->data;
803 if (c->desktop == desktop || c->desktop == DESKTOP_ALL)
804 return c;
805 }
806 return NULL;
807 }
This page took 0.068703 seconds and 4 git commands to generate.