1 /* focus.c - records a history of focusses for reverting focus, also does focus cycling
2 Copyright (C) 1996-2017 Paul Sheer
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307, USA.
18 */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <my_string.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25
26 #include <X11/Intrinsic.h>
27
28 #include "stringtools.h"
29 #include "app_glob.c"
30
31 #include "coolwidget.h"
32 #include "coollocal.h"
33
34 #include "mad.h"
35
36 /* Focus stack: This stack remembers the focus history
37 so that when widgets are destroyed, the focus returns to
38 the most recent widget in the history. This is equivalent
39 to the revert_to in the XSetInputFocus() function,
40 however XSetInputFocus() has an effective history of
41 only one.
42
43 The commands to change focus are CFocus(CWidget *w)
44 */
45
46 extern struct look *look;
47
48 #define FOCUS_STACK_SIZE 128
49 static Window focus_stack[FOCUS_STACK_SIZE];
50 static int focus_sp = 0;
51 static Window current_focus = -1;
52 static Window current_ic_focus = -1;
53 static Window dnd_focus = -1;
54
save_current_focus_for_dnd(Window new_focus)55 void save_current_focus_for_dnd (Window new_focus)
56 {
57 dnd_focus = current_focus;
58 current_focus = new_focus;
59 }
60
restore_current_focus_for_dnd(void)61 void restore_current_focus_for_dnd (void)
62 {
63 current_focus = dnd_focus;
64 }
65
add_to_focus_stack(Window w)66 void add_to_focus_stack (Window w)
67 {
68 int i;
69 i = focus_sp;
70 while (i--)
71 if (focus_stack[i] == w) {
72 focus_sp = i + 1;
73 return;
74 }
75 if (focus_sp >= FOCUS_STACK_SIZE) {
76 #ifdef FOCUS_DEBUG
77 /* NLS ? */
78 printf ("add_to_focus_stack(): focus_sp overflow\n");
79 #endif
80 return;
81 }
82 focus_stack[focus_sp++] = w;
83 #ifdef FOCUS_DEBUG
84 /* NLS ? */
85 printf ("add_to_focus_stack(%x): focus_sp = %d\n", (unsigned int) w, focus_sp);
86 #endif
87 }
88
focus_stack_remove_window(Window w)89 void focus_stack_remove_window (Window w)
90 {
91 int i;
92 i = focus_sp;
93 while (i--)
94 if (focus_stack[i] == w) {
95 focus_stack[i] = 0;
96 while (focus_sp && !focus_stack[focus_sp - 1])
97 focus_sp--;
98 #ifdef FOCUS_DEBUG
99 /* NLS ? */
100 printf ("focus_stack_remove_window(): focus_sp = %d\n", focus_sp);
101 #endif
102 return;
103 }
104 }
105
CGetFocus(void)106 Window CGetFocus(void)
107 {
108 return current_focus;
109 }
110
CGetICFocus(void)111 Window CGetICFocus(void)
112 {
113 return current_ic_focus;
114 }
115
CFocusLast(void)116 void CFocusLast (void)
117 {
118 Window w;
119 if (!focus_sp)
120 return;
121 w = focus_stack[focus_sp - 1];
122 if (w == current_focus)
123 return;
124 if (w)
125 focus_window (w);
126 }
127
128 /*
129 Each main window must record its last focussed window. This is so
130 that when the window manager sets the focus to a main window. We
131 can look up what widget within that window last had the focus, and
132 move focus to that widget.
133 */
get_last_focussed_in_main(Window main)134 static Window *get_last_focussed_in_main (Window main)
135 {
136 static Window dummy;
137 CWidget *w;
138 w = CWidgetOfWindow (main);
139 if (w)
140 return &(w->last_child_focussed);
141 dummy = 0;
142 return &dummy; /* instead of 'return 0;' to prevent segfaults */
143 }
144
145 /* {{{ Focus border creation and rendering */
146
147 /* this is four windows that surround the focussed window */
148 struct focus_win focus_border =
149 {
150 0, 0, 0, 0, 0, 0, 0
151 };
152
get_focus_border_widget(void)153 Window get_focus_border_widget (void)
154 {
155 return focus_border.current;
156 }
157
158 /* draw a focus window around wodget w */
create_focus_border(CWidget * w,int border)159 void create_focus_border (CWidget * w, int border)
160 {
161 int x, y;
162 XSetWindowAttributes xswa;
163 xswa.colormap = CColormap;
164 xswa.bit_gravity = NorthWestGravity;
165 if (border > 2)
166 xswa.background_pixel = color_palette(17); /* for drag and drop highlight borders */
167 else
168 xswa.background_pixel = COLOR_FLAT;
169 if (w->parentid == CRoot) {
170 xswa.override_redirect = 1;
171 } else {
172 xswa.override_redirect = 0;
173 }
174
175 focus_border.border = border;
176 x = w->x;
177 y = w->y;
178
179 if (w->parentid == CRoot) {
180 Window child;
181 XTranslateCoordinates(CDisplay, w->winid, CRoot, 0, 0, &x, &y, &child);
182 }
183
184 focus_border.top =
185 XCreateWindow (CDisplay, w->parentid,
186 x - WIDGET_FOCUS_RING, y - WIDGET_FOCUS_RING,
187 w->width + WIDGET_FOCUS_RING * 2, WIDGET_FOCUS_RING,
188 0, CDepth, InputOutput, CVisual,
189 CWOverrideRedirect | CWColormap | CWBackPixel | CWBitGravity,
190 &xswa);
191 focus_border.bottom =
192 XCreateWindow (CDisplay, w->parentid,
193 x - WIDGET_FOCUS_RING, y + w->height,
194 w->width + WIDGET_FOCUS_RING * 2, WIDGET_FOCUS_RING,
195 0, CDepth, InputOutput, CVisual,
196 CWOverrideRedirect | CWColormap | CWBackPixel | CWBitGravity,
197 &xswa);
198 focus_border.left =
199 XCreateWindow (CDisplay, w->parentid,
200 x - WIDGET_FOCUS_RING, y,
201 WIDGET_FOCUS_RING, w->height,
202 0, CDepth, InputOutput, CVisual,
203 CWOverrideRedirect | CWColormap | CWBackPixel | CWBitGravity,
204 &xswa);
205 focus_border.right =
206 XCreateWindow (CDisplay, w->parentid,
207 x + w->width, y,
208 WIDGET_FOCUS_RING, w->height,
209 0, CDepth, InputOutput, CVisual,
210 CWOverrideRedirect | CWColormap | CWBackPixel | CWBitGravity,
211 &xswa);
212 focus_border.current = w->winid;
213 focus_border.width = w->width;
214 focus_border.height = w->height;
215 XSelectInput (CDisplay, focus_border.top, ExposureMask);
216 XSelectInput (CDisplay, focus_border.bottom, ExposureMask);
217 XSelectInput (CDisplay, focus_border.left, ExposureMask);
218 XSelectInput (CDisplay, focus_border.right, ExposureMask);
219 XMapWindow (CDisplay, focus_border.top);
220 XMapWindow (CDisplay, focus_border.bottom);
221 XMapWindow (CDisplay, focus_border.left);
222 XMapWindow (CDisplay, focus_border.right);
223 }
224
destroy_focus_border(void)225 void destroy_focus_border (void)
226 {
227 if (!focus_border.top)
228 return;
229 XDestroyWindow (CDisplay, focus_border.top);
230 XDestroyWindow (CDisplay, focus_border.bottom);
231 XDestroyWindow (CDisplay, focus_border.left);
232 XDestroyWindow (CDisplay, focus_border.right);
233 memset (&focus_border, 0, sizeof (focus_border));
234 }
235
window_of_focus_border(Window win)236 int window_of_focus_border (Window win)
237 {
238 if (!focus_border.top)
239 return 0;
240 if (win == focus_border.top)
241 return 1;
242 if (win == focus_border.bottom)
243 return 1;
244 if (win == focus_border.left)
245 return 1;
246 if (win == focus_border.right)
247 return 1;
248 return 0;
249 }
250
render_focus_border(Window win)251 void render_focus_border (Window win)
252 {
253 (*look->render_focus_border) (win);
254 }
255
set_ic_focus(CWidget * w)256 static void set_ic_focus (CWidget * w)
257 {
258 #ifdef USE_XIM
259 if (w->mainid) {
260 CIC = (CWidgetOfWindow (w->mainid))->input_context;
261 current_ic_focus = w->mainid;
262 } else {
263 CIC = w->input_context;
264 current_ic_focus = w->mainid;
265 }
266 if (CIC)
267 XSetICFocus (CIC);
268 #endif
269 }
270
271 /* }}} Focus border creation and rendering */
272
273 /* This is called when the app (and not the WM) wants to change focus. */
focus_widget(CWidget * w)274 static void focus_widget (CWidget * w)
275 {
276 CWidget *old;
277
278 if (current_focus == w->winid) {
279 #ifdef FOCUS_DEBUG
280 printf ("current_focus == w->winid, returning\n");
281 #endif
282 return;
283 }
284
285 destroy_focus_border ();
286 if ((w->options & WIDGET_TAKES_FOCUS_RING))
287 create_focus_border (w, 1);
288
289 old = CWidgetOfWindow (current_focus);
290
291 current_focus = w->winid; /* notify library of new focus */
292
293 CSendMessage (old, FocusOut); /* unfocus the current focus */
294
295 /* focus on the widgets main window if it has changed */
296 if (old) {
297 if (old->mainid != w->mainid)
298 goto set_main;
299 } else {
300 set_main:
301 /* set that main windows 'last_child_focussed' */
302 XSetInputFocus (CDisplay, w->mainid, RevertToNone, CurrentTime);
303 set_ic_focus (w);
304 }
305 *(get_last_focussed_in_main (w->mainid)) = w->winid;
306 #ifdef FOCUS_DEBUG
307 printf ("setting last_focus of %s = %s\n", (char *) CWidgetOfWindow (w->mainid), w->ident);
308 #endif
309
310 add_to_focus_stack (w->winid); /* record focus history */
311
312 /* focus the new widget */
313 CSendMessage (w, FocusIn);
314 }
315
focus_main(Window main,int type)316 static void focus_main (Window main, int type)
317 {
318 CWidget *w;
319
320 if (type == FocusOut) {
321 w = CWidgetOfWindow (current_focus);
322 #ifdef FOCUS_DEBUG
323 printf ("focus_main(Out): %s\n", w->ident);
324 #endif
325 current_focus = -1; /* inform the library that nothing is focussed */
326 CSendMessage (w, FocusOut);
327 destroy_focus_border ();
328 } else { /* type == FocusIn */
329 /* find the widget within this main that last had the focus: */
330 current_focus = *(get_last_focussed_in_main (main));
331
332 w = CWidgetOfWindow (current_focus);
333 #ifdef FOCUS_DEBUG
334 printf ("focus_main(In): %s\n", w->ident);
335 #endif
336 if (w) {
337 add_to_focus_stack (w->winid); /* record focus history */
338 CSendMessage (w, FocusIn); /* focus the new widget */
339 if ((w->options & WIDGET_TAKES_FOCUS_RING))
340 create_focus_border (w, 2);
341 set_ic_focus (w);
342 }
343 }
344 }
345
346 /*
347 This is called from CNextEvent when the WM sends
348 focus to a new different main window.
349 */
process_external_focus(Window win,int type)350 void process_external_focus (Window win, int type)
351 {
352 CWidget *w;
353 #ifdef FOCUS_DEBUG
354 printf ("Entering process_external_focus()\n");
355 #endif
356 w = CWidgetOfWindow (win);
357 if (!w) {
358 #ifdef FOCUS_DEBUG
359 printf ("Leaving process_external_focus() - not our window\n");
360 #endif
361 return;
362 }
363 #ifdef FOCUS_DEBUG
364 printf ("widget = %s\n", w->ident);
365 #endif
366 if (w->parentid == CRoot) { /* should always be true since only
367 main windows have FocusChangeMask */
368 focus_main (w->winid, type);
369 #ifdef FOCUS_DEBUG
370 printf ("Leaving process_external_focus() - main window\n");
371 #endif
372 return;
373 } else {
374 #ifdef FOCUS_DEBUG
375 printf ("Leaving process_external_focus() - not main window\n");
376 #endif
377 }
378 }
379
focus_window(Window win)380 void focus_window (Window win)
381 {
382 CWidget *w;
383 w = CWidgetOfWindow (win);
384 if (w)
385 CFocusNormal (w);
386 }
387
388 /* CFocus() is a macro for */
CFocusNormal(CWidget * w)389 void CFocusNormal (CWidget * w)
390 {
391 if (!w)
392 return;
393 if (!w->takes_focus)
394 return;
395 if (w->mapped & WINDOW_MAPPED) {
396 focus_widget (w);
397 } else {
398 w->mapped |= WINDOW_FOCUS_WHEN_MAPPED;
399 }
400 }
401
402 extern int option_never_raise_wm_windows;
403
CTryFocus(CWidget * w,int raise_wm_window)404 int CTryFocus (CWidget * w, int raise_wm_window)
405 {
406 if (!option_never_raise_wm_windows) {
407 CFocus (w);
408 if (raise_wm_window)
409 CRaiseWMWindow (w->ident);
410 return 1;
411 } else {
412 /* focus only if it does not involve switching focus of a WM window: */
413 CWidget *v;
414 Window *win;
415 v = CWidgetOfWindow (CGetFocus ());
416 if (v)
417 if (v->mainid == w->mainid) {
418 CFocus (w);
419 return 1;
420 }
421 #ifdef FOCUS_DEBUG
422 printf ("setting last_focussed to %s\n", w->ident);
423 #endif
424 win = get_last_focussed_in_main (w->mainid);
425 if (*win)
426 add_to_focus_stack (*win);
427 add_to_focus_stack ((*win = w->winid));
428 }
429 return 0;
430 }
431
CFocusDebug(CWidget * w,int line,char * file)432 void CFocusDebug (CWidget *w, int line, char *file)
433 {
434 /* NLS ? */
435 printf ("CFocus(%x): %s:%d (ident = %s)\n", (unsigned int) w->winid, file, line, w->ident);
436 CFocusNormal (w);
437 }
438
439
440 /* get next sibling of w that has takes_focus set (i.e. that takes user input of any sort) */
CNextFocus(CWidget * w)441 CWidget *CNextFocus (CWidget * w)
442 {
443 int i, j;
444 i = j = find_next_child_of (w->parentid, w->winid);
445 for (;;) {
446 if (!i) {
447 i = find_first_child_of (w->parentid);
448 if (!i)
449 return 0;
450 }
451 if (CIndex (i)->takes_focus && !CIndex(i)->disabled)
452 return CIndex (i);
453 w = CIndex (i);
454 i = find_next_child_of (w->parentid, w->winid);
455 if (i == j) /* done a round trip */
456 return 0;
457 }
458 }
459
460 /* previous sibling of same */
CPreviousFocus(CWidget * w)461 CWidget *CPreviousFocus (CWidget * w)
462 {
463 int i, j;
464 i = j = find_previous_child_of (w->parentid, w->winid);
465 for (;;) {
466 if (!i) {
467 i = find_last_child_of (w->parentid);
468 if (!i)
469 return 0;
470 }
471 if (CIndex (i)->takes_focus && !CIndex(i)->disabled)
472 return CIndex (i);
473 w = CIndex (i);
474 i = find_previous_child_of (w->parentid, w->winid);
475 if (i == j) /* done a round trip */
476 return 0;
477 }
478 }
479
480 /* first child of widget that takes focus (eg w is a window and
481 a button in the window is returned) */
CChildFocus(CWidget * w)482 CWidget *CChildFocus (CWidget * w)
483 {
484 int j, i = find_first_child_of (w->winid);
485 if(!i)
486 return 0;
487 w = CIndex (i);
488 if(w->takes_focus)
489 return w;
490 j = i = find_next_child_of (w->parentid, w->winid);
491 for (;;) {
492 if (!i) {
493 i = find_first_child_of (w->parentid);
494 if (!i)
495 return 0;
496 }
497 if (CIndex (i)->takes_focus)
498 return CIndex (i);
499 w = CIndex (i);
500 i = find_next_child_of (w->parentid, w->winid);
501 if (i == j) /* done a round trip */
502 return 0;
503 }
504 }
505
506 /* search for two generations down for the first descendent that is a widget.
507 If it does not take focus, then its first child is focussed.
508 If it has no children, the next descendent is searched for. */
CFindFirstDescendent(Window win)509 CWidget *CFindFirstDescendent (Window win)
510 {
511 int i, j;
512
513 i = find_first_child_of (win);
514 if (i) { /* is it a child ? */
515 if (CIndex (i)->takes_focus && !CIndex (i)->disabled) {
516 return (CIndex (i));
517 } else {
518 CWidget *w;
519 w = CChildFocus (CIndex (i));
520 if (w)
521 return w;
522 }
523 } else { /* not a child */
524 Window root, parent, *children = 0;
525 unsigned int nchildren = 0;
526 if (!win)
527 return 0;
528 XQueryTree (CDisplay, win, &root, &parent, &children, &nchildren);
529 if (!nchildren) {
530 if (children)
531 XFree (children);
532 return 0;
533 }
534 for (j = 0; j < nchildren; j++)
535 if ((i = find_first_child_of (children[j]))) { /* is it a grandchild ? */
536 if (CIndex (i)->takes_focus && !CIndex (i)->disabled) {
537 XFree (children);
538 return (CIndex (i));
539 } else {
540 CWidget *w;
541 w = CChildFocus (CIndex (i));
542 if (w) {
543 XFree (children);
544 return w;
545 }
546 }
547 }
548 XFree (children);
549 }
550 return 0; /* not a grandchild */
551 }
552
553