]> Dogcows Code - chaz/openbox/blob - openbox/focus_cycle.c
remove {} just cuz
[chaz/openbox] / openbox / focus_cycle.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 focus_cycle.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 "focus_cycle.h"
21 #include "focus_cycle_indicator.h"
22 #include "focus_cycle_popup.h"
23 #include "client.h"
24 #include "frame.h"
25 #include "focus.h"
26 #include "screen.h"
27 #include "openbox.h"
28 #include "debug.h"
29 #include "group.h"
30
31 #include <X11/Xlib.h>
32 #include <glib.h>
33
34 ObClient *focus_cycle_target = NULL;
35 static gboolean focus_cycle_iconic_windows;
36 static gboolean focus_cycle_all_desktops;
37 static gboolean focus_cycle_dock_windows;
38 static gboolean focus_cycle_desktop_windows;
39
40 static gboolean focus_target_has_siblings (ObClient *ft,
41 gboolean iconic_windows,
42 gboolean all_desktops);
43 static ObClient *focus_find_directional (ObClient *c,
44 ObDirection dir,
45 gboolean dock_windows,
46 gboolean desktop_windows);
47
48 void focus_cycle_startup(gboolean reconfig)
49 {
50 if (reconfig) return;
51 }
52
53 void focus_cycle_shutdown(gboolean reconfig)
54 {
55 if (reconfig) return;
56 }
57
58 void focus_cycle_stop(ObClient *ifclient)
59 {
60 /* stop focus cycling if the given client is a valid focus target,
61 and so the cycling is being disrupted */
62 if (focus_cycle_target && ifclient &&
63 focus_cycle_target_valid(ifclient,
64 focus_cycle_iconic_windows,
65 focus_cycle_all_desktops,
66 focus_cycle_dock_windows,
67 focus_cycle_desktop_windows))
68 {
69 focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
70 focus_directional_cycle(0, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
71 }
72 }
73
74 /*! Returns if a focus target has valid group siblings that can be cycled
75 to in its place */
76 static gboolean focus_target_has_siblings(ObClient *ft,
77 gboolean iconic_windows,
78 gboolean all_desktops)
79
80 {
81 GSList *it;
82
83 if (!ft->group) return FALSE;
84
85 for (it = ft->group->members; it; it = g_slist_next(it)) {
86 ObClient *c = it->data;
87 /* check that it's not a helper window to avoid infinite recursion */
88 if (c != ft && !client_helper(c) &&
89 focus_cycle_target_valid(c, iconic_windows, all_desktops, FALSE,
90 FALSE))
91 {
92 return TRUE;
93 }
94 }
95 return FALSE;
96 }
97
98 gboolean focus_cycle_target_valid(ObClient *ft,
99 gboolean iconic_windows,
100 gboolean all_desktops,
101 gboolean dock_windows,
102 gboolean desktop_windows)
103 {
104 gboolean ok = FALSE;
105
106 /* it's on this desktop unless you want all desktops.
107
108 do this check first because it will usually filter out the most
109 windows */
110 ok = (all_desktops || ft->desktop == screen_desktop ||
111 ft->desktop == DESKTOP_ALL);
112
113 /* the window can receive focus somehow */
114 ok = ok && (ft->can_focus || ft->focus_notify);
115
116 /* the window is not iconic, or we're allowed to go to iconic ones */
117 ok = ok && (iconic_windows || !ft->iconic);
118
119 /* it's the right type of window */
120 if (dock_windows || desktop_windows)
121 ok = ok && ((dock_windows && ft->type == OB_CLIENT_TYPE_DOCK) ||
122 (desktop_windows && ft->type == OB_CLIENT_TYPE_DESKTOP));
123 /* modal windows are important and can always get focus if they are
124 visible and stuff, so don't change 'ok' based on their type */
125 else if (!ft->modal)
126 /* normal non-helper windows are valid targets */
127 ok = ok &&
128 ((client_normal(ft) && !client_helper(ft))
129 ||
130 /* helper windows are valid targets it... */
131 (client_helper(ft) &&
132 /* ...a window in its group already has focus ... */
133 ((focus_client && ft->group == focus_client->group) ||
134 /* ... or if there are no other windows in its group
135 that can be cycled to instead */
136 !focus_target_has_siblings(ft, iconic_windows, all_desktops))));
137
138 /* it's not set to skip the taskbar (unless it is a type that would be
139 expected to set this hint, or modal) */
140 ok = ok && ((ft->type == OB_CLIENT_TYPE_DOCK ||
141 ft->type == OB_CLIENT_TYPE_DESKTOP ||
142 ft->type == OB_CLIENT_TYPE_TOOLBAR ||
143 ft->type == OB_CLIENT_TYPE_MENU ||
144 ft->type == OB_CLIENT_TYPE_UTILITY) ||
145 ft->modal ||
146 !ft->skip_taskbar);
147
148 /* it's not going to just send focus off somewhere else (modal window),
149 unless that modal window is not one of our valid targets, then let
150 you choose this window and bring the modal one here */
151 {
152 ObClient *cft = client_focus_target(ft);
153 ok = ok && (ft == cft || !focus_cycle_target_valid(cft,
154 iconic_windows,
155 all_desktops,
156 dock_windows,
157 desktop_windows));
158 }
159
160 return ok;
161 }
162
163 void focus_cycle(gboolean forward, gboolean all_desktops,
164 gboolean dock_windows, gboolean desktop_windows,
165 gboolean linear, gboolean interactive,
166 gboolean dialog, gboolean done, gboolean cancel)
167 {
168 static ObClient *t = NULL;
169 static GList *order = NULL;
170 GList *it, *start, *list;
171 ObClient *ft = NULL;
172
173 if (interactive) {
174 if (cancel) {
175 focus_cycle_target = NULL;
176 goto done_cycle;
177 } else if (done)
178 goto done_cycle;
179
180 if (!focus_order)
181 goto done_cycle;
182
183 if (linear) list = client_list;
184 else list = focus_order;
185 } else {
186 if (!focus_order)
187 goto done_cycle;
188 list = client_list;
189 }
190
191
192 if (focus_cycle_target == NULL) {
193 focus_cycle_iconic_windows = TRUE;
194 focus_cycle_all_desktops = all_desktops;
195 focus_cycle_dock_windows = dock_windows;
196 focus_cycle_desktop_windows = desktop_windows;
197 start = it = g_list_find(list, focus_client);
198 } else
199 start = it = g_list_find(list, focus_cycle_target);
200
201 if (!start) /* switched desktops or something? */
202 start = it = forward ? g_list_last(list) : g_list_first(list);
203 if (!start) goto done_cycle;
204
205 do {
206 if (forward) {
207 it = it->next;
208 if (it == NULL) it = g_list_first(list);
209 } else {
210 it = it->prev;
211 if (it == NULL) it = g_list_last(list);
212 }
213 ft = it->data;
214 if (focus_cycle_target_valid(ft,
215 focus_cycle_iconic_windows,
216 focus_cycle_all_desktops,
217 focus_cycle_dock_windows,
218 focus_cycle_desktop_windows))
219 {
220 if (interactive) {
221 if (ft != focus_cycle_target) { /* prevents flicker */
222 focus_cycle_target = ft;
223 focus_cycle_draw_indicator(ft);
224 }
225 if (dialog)
226 /* same arguments as focus_target_valid */
227 focus_cycle_popup_show(ft,
228 focus_cycle_iconic_windows,
229 focus_cycle_all_desktops,
230 focus_cycle_dock_windows,
231 focus_cycle_desktop_windows);
232 return;
233 } else if (ft != focus_cycle_target) {
234 focus_cycle_target = ft;
235 done = TRUE;
236 break;
237 }
238 }
239 } while (it != start);
240
241 done_cycle:
242 if (done && focus_cycle_target)
243 client_activate(focus_cycle_target, FALSE, TRUE);
244
245 t = NULL;
246 focus_cycle_target = NULL;
247 g_list_free(order);
248 order = NULL;
249
250 if (interactive) {
251 focus_cycle_draw_indicator(NULL);
252 focus_cycle_popup_hide();
253 }
254
255 return;
256 }
257
258 /* this be mostly ripped from fvwm */
259 static ObClient *focus_find_directional(ObClient *c, ObDirection dir,
260 gboolean dock_windows,
261 gboolean desktop_windows)
262 {
263 gint my_cx, my_cy, his_cx, his_cy;
264 gint offset = 0;
265 gint distance = 0;
266 gint score, best_score;
267 ObClient *best_client, *cur;
268 GList *it;
269
270 if (!client_list)
271 return NULL;
272
273 /* first, find the centre coords of the currently focused window */
274 my_cx = c->frame->area.x + c->frame->area.width / 2;
275 my_cy = c->frame->area.y + c->frame->area.height / 2;
276
277 best_score = -1;
278 best_client = c;
279
280 for (it = g_list_first(client_list); it; it = g_list_next(it)) {
281 cur = it->data;
282
283 /* the currently selected window isn't interesting */
284 if (cur == c)
285 continue;
286 if (!focus_cycle_target_valid(it->data, FALSE, FALSE, dock_windows,
287 desktop_windows))
288 continue;
289
290 /* find the centre coords of this window, from the
291 * currently focused window's point of view */
292 his_cx = (cur->frame->area.x - my_cx)
293 + cur->frame->area.width / 2;
294 his_cy = (cur->frame->area.y - my_cy)
295 + cur->frame->area.height / 2;
296
297 if (dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
298 dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST)
299 {
300 gint tx;
301 /* Rotate the diagonals 45 degrees counterclockwise.
302 * To do this, multiply the matrix /+h +h\ with the
303 * vector (x y). \-h +h/
304 * h = sqrt(0.5). We can set h := 1 since absolute
305 * distance doesn't matter here. */
306 tx = his_cx + his_cy;
307 his_cy = -his_cx + his_cy;
308 his_cx = tx;
309 }
310
311 switch (dir) {
312 case OB_DIRECTION_NORTH:
313 case OB_DIRECTION_SOUTH:
314 case OB_DIRECTION_NORTHEAST:
315 case OB_DIRECTION_SOUTHWEST:
316 offset = (his_cx < 0) ? -his_cx : his_cx;
317 distance = ((dir == OB_DIRECTION_NORTH ||
318 dir == OB_DIRECTION_NORTHEAST) ?
319 -his_cy : his_cy);
320 break;
321 case OB_DIRECTION_EAST:
322 case OB_DIRECTION_WEST:
323 case OB_DIRECTION_SOUTHEAST:
324 case OB_DIRECTION_NORTHWEST:
325 offset = (his_cy < 0) ? -his_cy : his_cy;
326 distance = ((dir == OB_DIRECTION_WEST ||
327 dir == OB_DIRECTION_NORTHWEST) ?
328 -his_cx : his_cx);
329 break;
330 }
331
332 /* the target must be in the requested direction */
333 if (distance <= 0)
334 continue;
335
336 /* Calculate score for this window. The smaller the better. */
337 score = distance + offset;
338
339 /* windows more than 45 degrees off the direction are
340 * heavily penalized and will only be chosen if nothing
341 * else within a million pixels */
342 if (offset > distance)
343 score += 1000000;
344
345 if (best_score == -1 || score < best_score) {
346 best_client = cur;
347 best_score = score;
348 }
349 }
350
351 return best_client;
352 }
353
354 void focus_directional_cycle(ObDirection dir, gboolean dock_windows,
355 gboolean desktop_windows, gboolean interactive,
356 gboolean dialog, gboolean done, gboolean cancel)
357 {
358 static ObClient *first = NULL;
359 ObClient *ft = NULL;
360
361 if (!interactive)
362 return;
363
364 if (cancel) {
365 focus_cycle_target = NULL;
366 goto done_cycle;
367 } else if (done)
368 goto done_cycle;
369
370 if (!focus_order)
371 goto done_cycle;
372
373 if (focus_cycle_target == NULL) {
374 focus_cycle_iconic_windows = FALSE;
375 focus_cycle_all_desktops = FALSE;
376 focus_cycle_dock_windows = dock_windows;
377 focus_cycle_desktop_windows = desktop_windows;
378 }
379
380 if (!first) first = focus_client;
381
382 if (focus_cycle_target)
383 ft = focus_find_directional(focus_cycle_target, dir, dock_windows,
384 desktop_windows);
385 else if (first)
386 ft = focus_find_directional(first, dir, dock_windows, desktop_windows);
387 else {
388 GList *it;
389
390 for (it = focus_order; it; it = g_list_next(it))
391 if (focus_cycle_target_valid(it->data,
392 focus_cycle_iconic_windows,
393 focus_cycle_all_desktops,
394 focus_cycle_dock_windows,
395 focus_cycle_desktop_windows))
396 ft = it->data;
397 }
398
399 if (ft) {
400 if (ft != focus_cycle_target) {/* prevents flicker */
401 focus_cycle_target = ft;
402 focus_cycle_draw_indicator(ft);
403 }
404 }
405 if (focus_cycle_target && dialog)
406 /* same arguments as focus_target_valid */
407 focus_cycle_popup_single_show(focus_cycle_target,
408 focus_cycle_iconic_windows,
409 focus_cycle_all_desktops,
410 focus_cycle_dock_windows,
411 focus_cycle_desktop_windows);
412 return;
413
414 done_cycle:
415 if (done && focus_cycle_target)
416 client_activate(focus_cycle_target, FALSE, TRUE);
417
418 first = NULL;
419 focus_cycle_target = NULL;
420
421 focus_cycle_draw_indicator(NULL);
422 focus_cycle_popup_single_hide();
423
424 return;
425 }
This page took 0.057138 seconds and 5 git commands to generate.