1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, see: <http://www.gnu.org/licenses/>
14 */
15
16 /* ---------------------------- included header files ---------------------- */
17
18 #include "config.h"
19
20 #include <stdio.h>
21
22 #include "libs/fvwmlib.h"
23 #include "libs/charmap.h"
24 #include "libs/wcontext.h"
25 #include "libs/ClientMsg.h"
26 #include "libs/Grab.h"
27 #include "libs/Parse.h"
28 #include "libs/Strings.h"
29 #include "fvwm.h"
30 #include "externs.h"
31 #include "execcontext.h"
32 #include "eventhandler.h"
33 #include "bindings.h"
34 #include "misc.h"
35 #include "screen.h"
36 #include "focus.h"
37 #include "borders.h"
38 #include "frame.h"
39 #include "virtual.h"
40 #include "stack.h"
41 #include "geometry.h"
42 #include "colormaps.h"
43 #include "add_window.h"
44
45 /* ---------------------------- local definitions -------------------------- */
46
47 /* ---------------------------- local macros ------------------------------- */
48
49 /* ---------------------------- imports ------------------------------------ */
50
51 /* ---------------------------- included code files ------------------------ */
52
53 /* ---------------------------- local types -------------------------------- */
54
55 typedef struct
56 {
57 unsigned do_allow_force_broadcast : 1;
58 unsigned do_forbid_warp : 1;
59 unsigned do_force : 1;
60 unsigned is_focus_by_flip_focus_cmd : 1;
61 unsigned client_entered : 1;
62 fpol_set_focus_by_t set_by;
63 } sftfwin_args_t;
64
65 /* ---------------------------- forward declarations ----------------------- */
66
67 /* ---------------------------- local variables ---------------------------- */
68
69 static Bool lastFocusType;
70 /* Last window which Fvwm gave the focus to NOT the window that really has the
71 * focus */
72 static FvwmWindow *ScreenFocus = NULL;
73 /* Window which had focus before the pointer moved to a different screen. */
74 static FvwmWindow *LastScreenFocus = NULL;
75
76 /* ---------------------------- exported variables (globals) --------------- */
77
78 /* ---------------------------- interface functions ------------------------ */
79
_focus_set(Window w,FvwmWindow * fw)80 void _focus_set(Window w, FvwmWindow *fw)
81 {
82 Scr.focus_in_requested_window = fw;
83 XSetInputFocus(dpy, w, RevertToParent, CurrentTime);
84
85 return;
86 }
87
_focus_reset(void)88 void _focus_reset(void)
89 {
90 Scr.focus_in_requested_window = NULL;
91 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
92
93 return;
94 }
95
96 /* ---------------------------- local functions ---------------------------- */
97
focus_get_fpol_context_flag(fpol_context_t * fpol_context,int context)98 static Bool focus_get_fpol_context_flag(
99 fpol_context_t *fpol_context, int context)
100 {
101 int flag;
102
103 switch (context)
104 {
105 case C_WINDOW:
106 case C_EWMH_DESKTOP:
107 flag = fpol_context->client;
108 break;
109 case C_ICON:
110 flag = fpol_context->icon;
111 break;
112 default:
113 flag = fpol_context->decor;
114 break;
115 }
116
117 return (flag) ? True : False;
118 }
119
120 /*
121 * Helper functions for setting the focus
122 */
__try_program_focus(Window w,const FvwmWindow * fw)123 static void __try_program_focus(Window w, const FvwmWindow *fw)
124 {
125 if (fw && WM_TAKES_FOCUS(fw) &&
126 FP_DO_FOCUS_BY_PROGRAM(FW_FOCUS_POLICY(fw)))
127 {
128 send_clientmessage(dpy, w, _XA_WM_TAKE_FOCUS, fev_get_evtime());
129 }
130
131 return;
132 }
133
__try_forbid_user_focus(Window w,FvwmWindow * fw)134 static Bool __try_forbid_user_focus(
135 Window w, FvwmWindow *fw)
136 {
137 if (fw == NULL ||
138 fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)) == True)
139 {
140 return False;
141 }
142 if (WM_TAKES_FOCUS(fw))
143 {
144 /* give it a chance to take the focus itself */
145 __try_program_focus(w, fw);
146 XFlush(dpy);
147 }
148 else
149 {
150 /* make sure the window is not hilighted */
151 border_draw_decorations(
152 fw, PART_ALL, False, False, CLEAR_ALL, NULL, NULL);
153 }
154
155 return True;
156 }
157
__check_allow_focus(Window w,FvwmWindow * fw,fpol_set_focus_by_t set_by)158 static Bool __check_allow_focus(
159 Window w, FvwmWindow *fw, fpol_set_focus_by_t set_by)
160 {
161 FvwmWindow *sf;
162
163 if (fw == NULL || set_by == FOCUS_SET_FORCE)
164 {
165 /* always allow to delete focus */
166 return True;
167 }
168 sf = get_focus_window();
169 if (!FP_IS_LENIENT(FW_FOCUS_POLICY(fw)) &&
170 !focus_does_accept_input_focus(fw) &&
171 sf != NULL && sf->Desk == Scr.CurrentDesk)
172 {
173 /* window does not want focus */
174 return False;
175 }
176 if (fpol_query_allow_set_focus(&FW_FOCUS_POLICY(fw), set_by))
177 {
178 return True;
179 }
180
181 return False;
182 }
183
__update_windowlist(FvwmWindow * fw,fpol_set_focus_by_t set_by,int is_focus_by_flip_focus_cmd)184 static void __update_windowlist(
185 FvwmWindow *fw, fpol_set_focus_by_t set_by,
186 int is_focus_by_flip_focus_cmd)
187 {
188 lastFocusType = (is_focus_by_flip_focus_cmd) ? True : False;
189 if (fw == NULL || focus_is_focused(fw) || fw == &Scr.FvwmRoot ||
190 IS_SCHEDULED_FOR_DESTROY(fw))
191 {
192 return;
193 }
194 /* Watch out: fw may not be on the windowlist and the windowlist may be
195 * empty */
196 if (!is_focus_by_flip_focus_cmd &&
197 (FP_DO_SORT_WINDOWLIST_BY(FW_FOCUS_POLICY(fw)) ==
198 FPOL_SORT_WL_BY_OPEN ||
199 set_by == FOCUS_SET_BY_FUNCTION))
200 {
201 /* move the windowlist around so that fw is at the top */
202 FvwmWindow *fw2;
203
204 /* find the window on the windowlist */
205 for (fw2 = &Scr.FvwmRoot; fw2 && fw2 != fw; fw2 = fw2->next)
206 {
207 /* nothing */
208 }
209 if (fw2)
210 {
211 /* the window is on the (non-zero length) windowlist */
212 /* make fw2 point to the last window on the list */
213 while (fw2->next)
214 {
215 fw2 = fw2->next;
216 }
217
218 /* close the ends of the windowlist */
219 fw2->next = Scr.FvwmRoot.next;
220 Scr.FvwmRoot.next->prev = fw2;
221
222 /* make fw the new start of the list */
223 Scr.FvwmRoot.next = fw;
224 /* open the closed loop windowlist */
225 fw->prev->next = NULL;
226 fw->prev = &Scr.FvwmRoot;
227 }
228 }
229 else
230 {
231 /* pluck window from list and deposit at top */
232 /* remove fw from list */
233 if (fw->prev)
234 {
235 fw->prev->next = fw->next;
236 }
237 if (fw->next)
238 {
239 fw->next->prev = fw->prev;
240 }
241 /* insert fw at start */
242 fw->next = Scr.FvwmRoot.next;
243 if (Scr.FvwmRoot.next)
244 {
245 Scr.FvwmRoot.next->prev = fw;
246 }
247 Scr.FvwmRoot.next = fw;
248 fw->prev = &Scr.FvwmRoot;
249 }
250
251 return;
252 }
253
__try_other_screen_focus(const FvwmWindow * fw)254 static Bool __try_other_screen_focus(const FvwmWindow *fw)
255 {
256 if (fw == NULL && !Scr.flags.is_pointer_on_this_screen)
257 {
258 FvwmWindow *sf;
259
260 sf = get_focus_window();
261 set_focus_window(NULL);
262 if (sf != NULL)
263 {
264 focus_grab_buttons(sf);
265 }
266 /* DV (25-Nov-2000): Don't give the Scr.NoFocusWin the focus
267 * here. This would steal the focus from the other screen's
268 * root window again. */
269 return True;
270 }
271
272 return False;
273 }
274
275 /*
276 * Sets the input focus to the indicated window.
277 */
__set_focus_to_fwin(Window w,FvwmWindow * fw,sftfwin_args_t * args)278 static void __set_focus_to_fwin(
279 Window w, FvwmWindow *fw, sftfwin_args_t *args)
280 {
281 FvwmWindow *sf;
282
283 if (__try_forbid_user_focus(w, fw) == True)
284 {
285 return;
286 }
287 __try_program_focus(w, fw);
288 if (__check_allow_focus(w, fw, args->set_by) == False)
289 {
290 return;
291 }
292 __update_windowlist(fw, args->set_by, args->is_focus_by_flip_focus_cmd);
293 if (__try_other_screen_focus(fw) == True)
294 {
295 return;
296 }
297
298 if (fw && !args->do_forbid_warp)
299 {
300 if (IS_ICONIFIED(fw))
301 {
302 rectangle r;
303 Bool rc;
304
305 rc = get_visible_icon_geometry(fw, &r);
306 if (!rc || !IsRectangleOnThisPage(&r, fw->Desk))
307 {
308 fw = NULL;
309 w = Scr.NoFocusWin;
310 }
311 }
312 else if (!IsRectangleOnThisPage(&(fw->g.frame), fw->Desk))
313 {
314 fw = NULL;
315 w = Scr.NoFocusWin;
316 }
317 }
318
319 sf = get_focus_window();
320 if (fw == NULL)
321 {
322 FOCUS_SET(Scr.NoFocusWin, NULL);
323 set_focus_window(NULL);
324 Scr.UnknownWinFocused = None;
325 XFlush(dpy);
326 return;
327 }
328 /* RBW - allow focus to go to a NoIconTitle icon window so
329 * auto-raise will work on it. */
330 if (IS_ICONIFIED(fw))
331 {
332 Bool is_window_selected = False;
333
334 if (FW_W_ICON_TITLE(fw))
335 {
336 w = FW_W_ICON_TITLE(fw);
337 is_window_selected = True;
338 }
339 if ((!is_window_selected || WAS_ICON_HINT_PROVIDED(fw)) &&
340 FW_W_ICON_PIXMAP(fw))
341 {
342 w = FW_W_ICON_PIXMAP(fw);
343 }
344 }
345
346 if (FP_IS_LENIENT(FW_FOCUS_POLICY(fw)))
347 {
348 FOCUS_SET(w, fw);
349 set_focus_window(fw);
350 if (args->do_allow_force_broadcast)
351 {
352 SET_FOCUS_CHANGE_BROADCAST_PENDING(fw, 1);
353 }
354 Scr.UnknownWinFocused = None;
355 }
356 else if (focus_does_accept_input_focus(fw))
357 {
358 /* Window will accept input focus */
359 if (Scr.StolenFocusWin == w && Scr.UnknownWinFocused != None)
360 {
361 /* Without this FocusIn is not generated on the
362 * window if it was focuesed when the unmanaged
363 * window took focus. */
364 FOCUS_SET(Scr.NoFocusWin, NULL);
365
366 }
367 FOCUS_SET(w, fw);
368 set_focus_window(fw);
369 if (fw)
370 {
371 if (args->do_allow_force_broadcast)
372 {
373 SET_FOCUS_CHANGE_BROADCAST_PENDING(fw, 1);
374 }
375 }
376 Scr.UnknownWinFocused = None;
377 }
378 else if (sf && sf->Desk == Scr.CurrentDesk)
379 {
380 /* Window doesn't want focus. Leave focus alone */
381 }
382 else
383 {
384 FOCUS_SET(Scr.NoFocusWin, NULL);
385 set_focus_window(NULL);
386 }
387 XFlush(dpy);
388
389 return;
390 }
391
set_focus_to_fwin(Window w,FvwmWindow * fw,sftfwin_args_t * args)392 static void set_focus_to_fwin(
393 Window w, FvwmWindow *fw, sftfwin_args_t *args)
394 {
395 FvwmWindow *sf;
396
397 sf = get_focus_window();
398 if (!args->do_force && fw == sf)
399 {
400 focus_grab_buttons(sf);
401 return;
402 }
403 __set_focus_to_fwin(w, fw, args);
404 /* Make sure the button grabs on the new and the old focused windows
405 * are up to date. */
406 if (args->client_entered)
407 {
408 focus_grab_buttons_client_entered(fw);
409 }
410 else
411 {
412 focus_grab_buttons(fw);
413 }
414 /* RBW -- don't call this twice for the same window! */
415 if (fw != get_focus_window())
416 {
417 if (args->client_entered)
418 {
419 focus_grab_buttons_client_entered(get_focus_window());
420 }
421 else
422 {
423 focus_grab_buttons(get_focus_window());
424 }
425 }
426
427 return;
428 }
429
430 /*
431 *
432 * Moves pointer to specified window
433 *
434 */
warp_to_fvwm_window(const exec_context_t * exc,int warp_x,int x_unit,int warp_y,int y_unit,int do_raise)435 static void warp_to_fvwm_window(
436 const exec_context_t *exc, int warp_x, int x_unit, int warp_y,
437 int y_unit, int do_raise)
438 {
439 int dx,dy;
440 int cx,cy;
441 int x,y;
442 FvwmWindow *t = exc->w.fw;
443
444 if (t == (FvwmWindow *)0 ||
445 (IS_ICONIFIED(t) && FW_W_ICON_TITLE(t) == None))
446 {
447 return;
448 }
449 if (t->Desk != Scr.CurrentDesk)
450 {
451 goto_desk(t->Desk);
452 }
453 if (IS_ICONIFIED(t))
454 {
455 rectangle g;
456 Bool rc;
457
458 rc = get_visible_icon_title_geometry(t, &g);
459 if (rc == False)
460 {
461 get_visible_icon_picture_geometry(t, &g);
462 }
463 cx = g.x + g.width / 2;
464 cy = g.y + g.height / 2;
465 }
466 else
467 {
468 cx = t->g.frame.x + t->g.frame.width/2;
469 cy = t->g.frame.y + t->g.frame.height/2;
470 }
471 dx = (cx + Scr.Vx) / Scr.MyDisplayWidth * Scr.MyDisplayWidth;
472 dy = (cy + Scr.Vy) / Scr.MyDisplayHeight * Scr.MyDisplayHeight;
473 if (dx != Scr.Vx || dy != Scr.Vy)
474 {
475 MoveViewport(dx, dy, True);
476 }
477 if (IS_ICONIFIED(t))
478 {
479 rectangle g;
480 Bool rc;
481
482 rc = get_visible_icon_title_geometry(t, &g);
483 if (rc == False)
484 {
485 get_visible_icon_picture_geometry(t, &g);
486 }
487 x = g.x + g.width / 2;
488 y = g.y + g.height / 2;
489 }
490 else
491 {
492 if (x_unit != Scr.MyDisplayWidth && warp_x >= 0)
493 {
494 x = t->g.frame.x + warp_x;
495 }
496 else if (x_unit != Scr.MyDisplayWidth)
497 {
498 x = t->g.frame.x + t->g.frame.width + warp_x;
499 }
500 else if (warp_x >= 0)
501 {
502 x = t->g.frame.x +
503 (t->g.frame.width - 1) * warp_x / 100;
504 }
505 else
506 {
507 x = t->g.frame.x +
508 (t->g.frame.width - 1) * (100 + warp_x) / 100;
509 }
510
511 if (y_unit != Scr.MyDisplayHeight && warp_y >= 0)
512 {
513 y = t->g.frame.y + warp_y;
514 }
515 else if (y_unit != Scr.MyDisplayHeight)
516 {
517 y = t->g.frame.y + t->g.frame.height + warp_y;
518 }
519 else if (warp_y >= 0)
520 {
521 y = t->g.frame.y +
522 (t->g.frame.height - 1) * warp_y / 100;
523 }
524 else
525 {
526 y = t->g.frame.y +
527 (t->g.frame.height - 1) * (100 + warp_y) / 100;
528 }
529 }
530 FWarpPointerUpdateEvpos(
531 exc->x.elast, dpy, None, Scr.Root, 0, 0, 0, 0, x, y);
532 if (do_raise)
533 {
534 RaiseWindow(t, False);
535 }
536 /* If the window is still not visible, make it visible! */
537 if (t->g.frame.x + t->g.frame.width < 0 ||
538 t->g.frame.y + t->g.frame.height < 0 ||
539 t->g.frame.x >= Scr.MyDisplayWidth ||
540 t->g.frame.y >= Scr.MyDisplayHeight)
541 {
542 frame_setup_window(
543 t, 0, 0, t->g.frame.width, t->g.frame.height, False);
544 FWarpPointerUpdateEvpos(
545 exc->x.elast, dpy, None, Scr.Root, 0, 0, 0, 0, 2, 2);
546 }
547
548 return;
549 }
550
focus_query_grab_buttons(FvwmWindow * fw,Bool client_entered)551 static Bool focus_query_grab_buttons(FvwmWindow *fw, Bool client_entered)
552 {
553 Bool flag;
554 Bool is_focused;
555
556 if (fw->Desk != Scr.CurrentDesk || IS_ICONIFIED(fw))
557 {
558 return False;
559 }
560 is_focused = focus_is_focused(fw);
561 if (!is_focused && FP_DO_FOCUS_CLICK_CLIENT(FW_FOCUS_POLICY(fw)))
562 {
563 return True;
564 }
565 if (is_on_top_of_layer_and_above_unmanaged(fw))
566 {
567 return False;
568 }
569 if (is_focused)
570 {
571 flag = FP_DO_RAISE_FOCUSED_CLIENT_CLICK(FW_FOCUS_POLICY(fw));
572 }
573 else
574 {
575 flag = FP_DO_RAISE_UNFOCUSED_CLIENT_CLICK(FW_FOCUS_POLICY(fw));
576 }
577
578 return (flag) ? True : False;
579 }
580
__restore_focus_after_unmap(const FvwmWindow * fw,Bool do_skip_marked_transients)581 static FvwmWindow *__restore_focus_after_unmap(
582 const FvwmWindow *fw, Bool do_skip_marked_transients)
583 {
584 FvwmWindow *t = NULL;
585 FvwmWindow *set_focus_to = NULL;
586
587 t = get_transientfor_fvwmwindow(fw);
588 if (t != NULL &&
589 FP_DO_RELEASE_FOCUS_TRANSIENT(FW_FOCUS_POLICY(fw)) &&
590 !FP_DO_OVERRIDE_RELEASE_FOCUS(FW_FOCUS_POLICY(t)) &&
591 t->Desk == fw->Desk &&
592 (!do_skip_marked_transients || !IS_IN_TRANSIENT_SUBTREE(t)))
593 {
594 set_focus_to = t;
595 }
596 else if (t == NULL && FP_DO_RELEASE_FOCUS(FW_FOCUS_POLICY(fw)))
597 {
598 for (t = fw->next; t != NULL && set_focus_to == NULL;
599 t = t->next)
600 {
601 if (!FP_DO_OVERRIDE_RELEASE_FOCUS(
602 FW_FOCUS_POLICY(t)) &&
603 t->Desk == fw->Desk && !DO_SKIP_CIRCULATE(t) &&
604 !(IS_ICONIFIED(t) && (DO_SKIP_ICON_CIRCULATE(t) ||
605 IS_ICON_SUPPRESSED(t))) &&
606 (!do_skip_marked_transients ||
607 !IS_IN_TRANSIENT_SUBTREE(t)))
608 {
609 /* If it is on a different desk we have to look
610 * for another window */
611 set_focus_to = t;
612 }
613 }
614 }
615 if (set_focus_to && set_focus_to != fw &&
616 set_focus_to->Desk == fw->Desk)
617 {
618 /* Don't transfer focus to windows on other desks */
619 SetFocusWindow(set_focus_to, True, FOCUS_SET_FORCE);
620 }
621 if (focus_is_focused(fw))
622 {
623 DeleteFocus(True);
624 }
625
626 return set_focus_to;
627 }
628
629 /*
630 *
631 * Moves focus to specified window; only to be called bay Focus and FlipFocus
632 *
633 */
__activate_window_by_command(F_CMD_ARGS,int is_focus_by_flip_focus_cmd)634 static void __activate_window_by_command(
635 F_CMD_ARGS, int is_focus_by_flip_focus_cmd)
636 {
637 int cx;
638 int cy;
639 Bool do_not_warp;
640 sftfwin_args_t sf_args;
641 FvwmWindow * const fw = exc->w.fw;
642
643 memset(&sf_args, 0, sizeof(sf_args));
644 sf_args.do_allow_force_broadcast = 1;
645 sf_args.is_focus_by_flip_focus_cmd = is_focus_by_flip_focus_cmd;
646 sf_args.set_by = FOCUS_SET_BY_FUNCTION;
647 sf_args.client_entered = 0;
648 if (fw == NULL || !FP_DO_FOCUS_BY_FUNCTION(FW_FOCUS_POLICY(fw)))
649 {
650 UngrabEm(GRAB_NORMAL);
651 if (fw)
652 {
653 /* give the window a chance to take the focus itself */
654 sf_args.do_forbid_warp = 1;
655 sf_args.do_force = 0;
656 set_focus_to_fwin(FW_W(fw), fw, &sf_args);
657 }
658 return;
659 }
660
661 do_not_warp = StrEquals(PeekToken(action, NULL), "NoWarp");
662 if (!do_not_warp)
663 {
664 if (fw->Desk != Scr.CurrentDesk)
665 {
666 goto_desk(fw->Desk);
667 }
668 if (IS_ICONIFIED(fw))
669 {
670 rectangle g;
671 Bool rc;
672
673 rc = get_visible_icon_title_geometry(fw, &g);
674 if (rc == False)
675 {
676 get_visible_icon_picture_geometry(fw, &g);
677 }
678 cx = g.x + g.width / 2;
679 cy = g.y + g.height / 2;
680 }
681 else
682 {
683 cx = fw->g.frame.x + fw->g.frame.width/2;
684 cy = fw->g.frame.y + fw->g.frame.height/2;
685 }
686 if (
687 cx < 0 || cx >= Scr.MyDisplayWidth ||
688 cy < 0 || cy >= Scr.MyDisplayHeight)
689 {
690 int dx;
691 int dy;
692
693 dx = ((cx + Scr.Vx) / Scr.MyDisplayWidth) *
694 Scr.MyDisplayWidth;
695 dy = ((cy + Scr.Vy) / Scr.MyDisplayHeight) *
696 Scr.MyDisplayHeight;
697 MoveViewport(dx, dy, True);
698 }
699 #if 0 /* can not happen */
700 /* If the window is still not visible, make it visible! */
701 if (fw->g.frame.x + fw->g.frame.width < 0 ||
702 fw->g.frame.y + fw->g.frame.height < 0 ||
703 fw->g.frame.x >= Scr.MyDisplayWidth ||
704 fw->g.frame.y >= Scr.MyDisplayHeight)
705 {
706 frame_setup_window(
707 fw, 0, 0, fw->g.frame.width,
708 fw->g.frame.height, False);
709 if (
710 FP_DO_WARP_POINTER_ON_FOCUS_FUNC(
711 FW_FOCUS_POLICY(fw)))
712 {
713 FWarpPointerUpdateEvpos(
714 exc->x.elast, dpy, None, Scr.Root, 0,
715 0, 0, 0, 2, 2);
716 }
717 }
718 #endif
719 }
720 UngrabEm(GRAB_NORMAL);
721
722 if (fw->Desk == Scr.CurrentDesk)
723 {
724 FvwmWindow *sf;
725
726 sf = get_focus_window();
727 sf_args.do_forbid_warp = !!do_not_warp;
728 sf_args.do_force = 0;
729 sf_args.client_entered = 0;
730 set_focus_to_fwin(FW_W(fw), fw, &sf_args);
731 if (sf != get_focus_window())
732 {
733 /* Ignore EnterNotify event while we are waiting for
734 * this window to be focused. */
735 Scr.focus_in_pending_window = sf;
736 }
737 }
738
739 return;
740 }
741
__focus_grab_one_button(FvwmWindow * fw,int button,int grab_buttons)742 static void __focus_grab_one_button(
743 FvwmWindow *fw, int button, int grab_buttons)
744 {
745 Bool do_grab;
746
747 do_grab = (grab_buttons & (1 << button));
748 if ((do_grab & (1 << button)) == (fw->grabbed_buttons & (1 << button)))
749 {
750 return;
751 }
752 if (do_grab)
753 {
754 XGrabButton(
755 dpy, button + 1, AnyModifier, FW_W_PARENT(fw), True,
756 ButtonPressMask, GrabModeSync, GrabModeAsync, None,
757 None);
758 /* Set window flags accordingly as we grab or ungrab. */
759 fw->grabbed_buttons |= (1 << button);
760 }
761 else
762 {
763 XUngrabButton(dpy, button + 1, AnyModifier, FW_W_PARENT(fw));
764 fw->grabbed_buttons &= ~(1 << button);
765 }
766
767 return;
768 }
769
770 /* ---------------------------- interface functions ------------------------ */
771
focus_does_accept_input_focus(const FvwmWindow * fw)772 Bool focus_does_accept_input_focus(const FvwmWindow *fw)
773 {
774 return (!fw || !fw->wmhints ||
775 !(fw->wmhints->flags & InputHint) || fw->wmhints->input);
776 }
777
focus_is_focused(const FvwmWindow * fw)778 Bool focus_is_focused(const FvwmWindow *fw)
779 {
780 return (fw && fw == ScreenFocus);
781 }
782
focus_query_click_to_raise(FvwmWindow * fw,Bool is_focused,int context)783 Bool focus_query_click_to_raise(
784 FvwmWindow *fw, Bool is_focused, int context)
785 {
786 fpol_context_t *c;
787
788 if (is_focused)
789 {
790 c = &FP_DO_RAISE_FOCUSED_CLICK(FW_FOCUS_POLICY(fw));
791 }
792 else
793 {
794 c = &FP_DO_RAISE_UNFOCUSED_CLICK(FW_FOCUS_POLICY(fw));
795 }
796
797 return focus_get_fpol_context_flag(c, context);
798 }
799
focus_query_click_to_focus(FvwmWindow * fw,int context)800 Bool focus_query_click_to_focus(
801 FvwmWindow *fw, int context)
802 {
803 fpol_context_t *c;
804
805 c = &FP_DO_FOCUS_CLICK(FW_FOCUS_POLICY(fw));
806
807 return focus_get_fpol_context_flag(c, context);
808 }
809
810 /* Takes as input the window that wants the focus and the one that currently
811 * has the focus and returns if the new window should get it. */
focus_query_open_grab_focus(FvwmWindow * fw,FvwmWindow * focus_win)812 Bool focus_query_open_grab_focus(FvwmWindow *fw, FvwmWindow *focus_win)
813 {
814 if (fw == NULL)
815 {
816 return False;
817 }
818 focus_win = get_focus_window();
819 if (focus_win != NULL &&
820 FP_DO_OVERRIDE_GRAB_FOCUS(FW_FOCUS_POLICY(focus_win)))
821 {
822 /* Don't steal the focus from the current window */
823 return False;
824 }
825 validate_transientfor(fw);
826 if (IS_TRANSIENT(fw) && FW_W_TRANSIENTFOR(fw) != Scr.Root)
827 {
828 if (focus_win != NULL &&
829 FP_DO_GRAB_FOCUS_TRANSIENT(FW_FOCUS_POLICY(fw)) &&
830 FW_W(focus_win) == FW_W_TRANSIENTFOR(fw))
831 {
832 /* it's a transient and its transientfor currently has
833 * focus. */
834 return True;
835 }
836 }
837 else
838 {
839 if (FP_DO_GRAB_FOCUS(FW_FOCUS_POLICY(fw)) &&
840 (focus_win == NULL ||
841 !FP_DO_OVERRIDE_GRAB_FOCUS(FW_FOCUS_POLICY(focus_win))))
842 {
843 return True;
844 }
845 }
846
847 return False;
848 }
849
850 /* Returns true if the focus has to be restored to a different window after
851 * unmapping. */
focus_query_close_release_focus(const FvwmWindow * fw)852 Bool focus_query_close_release_focus(const FvwmWindow *fw)
853 {
854 if (fw == NULL || fw != get_focus_window())
855 {
856 return False;
857 }
858 if (!IS_TRANSIENT(fw) &&
859 (FW_W_TRANSIENTFOR(fw) == Scr.Root ||
860 FP_DO_GRAB_FOCUS(FW_FOCUS_POLICY(fw))))
861 {
862 return True;
863 }
864 else if (IS_TRANSIENT(fw) &&
865 FP_DO_GRAB_FOCUS_TRANSIENT(FW_FOCUS_POLICY(fw)))
866 {
867 return True;
868 }
869
870 return False;
871 }
872
__focus_grab_buttons(FvwmWindow * fw,Bool client_entered)873 static void __focus_grab_buttons(FvwmWindow *fw, Bool client_entered)
874 {
875 int i;
876 Bool do_grab_window = False;
877 int grab_buttons;
878
879 if (fw == NULL || IS_SCHEDULED_FOR_DESTROY(fw) || !IS_MAPPED(fw))
880 {
881 /* It is pointless to grab buttons on dieing windows. Buttons
882 * can not be grabbed when the window is unmapped. */
883 return;
884 }
885 grab_buttons = Scr.buttons2grab;
886 do_grab_window = focus_query_grab_buttons(fw, client_entered);
887 if (do_grab_window == True)
888 {
889 grab_buttons |= FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(fw));
890 }
891 if (grab_buttons != fw->grabbed_buttons)
892 {
893 MyXGrabServer(dpy);
894 for (i = 0; i < NUMBER_OF_EXTENDED_MOUSE_BUTTONS; i++)
895 {
896 __focus_grab_one_button(fw, i, grab_buttons);
897 }
898 MyXUngrabServer (dpy);
899 }
900
901 return;
902 }
903
focus_grab_buttons(FvwmWindow * fw)904 void focus_grab_buttons(FvwmWindow *fw)
905 {
906 __focus_grab_buttons(fw, False);
907 }
908
focus_grab_buttons_client_entered(FvwmWindow * fw)909 void focus_grab_buttons_client_entered(FvwmWindow *fw)
910 {
911 __focus_grab_buttons(fw, True);
912 }
913
focus_grab_buttons_on_layer(int layer)914 void focus_grab_buttons_on_layer(int layer)
915 {
916 FvwmWindow *fw;
917
918 for (
919 fw = Scr.FvwmRoot.stack_next;
920 fw != &Scr.FvwmRoot && fw->layer >= layer;
921 fw = fw->stack_next)
922 {
923 if (fw->layer == layer)
924 {
925 focus_grab_buttons(fw);
926 }
927 }
928
929 return;
930 }
931
focus_grab_buttons_all(void)932 void focus_grab_buttons_all(void)
933 {
934 FvwmWindow *fw;
935
936 for (fw = Scr.FvwmRoot.next; fw != NULL; fw = fw->next)
937 {
938 focus_grab_buttons(fw);
939 }
940
941 return;
942 }
943
_SetFocusWindow(FvwmWindow * fw,Bool do_allow_force_broadcast,fpol_set_focus_by_t set_by,Bool client_entered)944 void _SetFocusWindow(
945 FvwmWindow *fw, Bool do_allow_force_broadcast,
946 fpol_set_focus_by_t set_by, Bool client_entered)
947 {
948 sftfwin_args_t sf_args;
949
950 memset(&sf_args, 0, sizeof(sf_args));
951 sf_args.do_allow_force_broadcast = !!do_allow_force_broadcast;
952 sf_args.is_focus_by_flip_focus_cmd = 0;
953 sf_args.do_forbid_warp = 0;
954 sf_args.do_force = 1;
955 sf_args.set_by = set_by;
956 if (client_entered)
957 {
958 sf_args.client_entered = 1;
959 }
960 else
961 {
962 sf_args.client_entered = 0;
963 }
964 set_focus_to_fwin(FW_W(fw), fw, &sf_args);
965
966 return;
967 }
968
_ReturnFocusWindow(FvwmWindow * fw)969 void _ReturnFocusWindow(FvwmWindow *fw)
970 {
971 sftfwin_args_t sf_args;
972
973 memset(&sf_args, 0, sizeof(sf_args));
974 sf_args.do_allow_force_broadcast = 1;
975 sf_args.is_focus_by_flip_focus_cmd = 0;
976 sf_args.do_forbid_warp = 1;
977 sf_args.client_entered = 0;
978 sf_args.do_force = 0;
979 sf_args.set_by = FOCUS_SET_FORCE;
980 set_focus_to_fwin(FW_W(fw), fw, &sf_args);
981
982 return;
983 }
984
_DeleteFocus(Bool do_allow_force_broadcast)985 void _DeleteFocus(Bool do_allow_force_broadcast)
986 {
987 sftfwin_args_t sf_args;
988
989 memset(&sf_args, 0, sizeof(sf_args));
990 sf_args.do_allow_force_broadcast = !!do_allow_force_broadcast;
991 sf_args.is_focus_by_flip_focus_cmd = 0;
992 sf_args.do_forbid_warp = 0;
993 sf_args.client_entered = 0;
994 sf_args.do_force = 0;
995 sf_args.set_by = FOCUS_SET_FORCE;
996 set_focus_to_fwin(Scr.NoFocusWin, NULL, &sf_args);
997
998 return;
999 }
1000
_ForceDeleteFocus(void)1001 void _ForceDeleteFocus(void)
1002 {
1003 sftfwin_args_t sf_args;
1004
1005 memset(&sf_args, 0, sizeof(sf_args));
1006 sf_args.do_allow_force_broadcast = 1;
1007 sf_args.is_focus_by_flip_focus_cmd = 0;
1008 sf_args.do_forbid_warp = 0;
1009 sf_args.client_entered = 0;
1010 sf_args.do_force = 1;
1011 sf_args.set_by = FOCUS_SET_FORCE;
1012 set_focus_to_fwin(Scr.NoFocusWin, NULL, &sf_args);
1013
1014 return;
1015 }
1016
1017 /* When a window is unmapped (or destroyed) this function takes care of
1018 * adjusting the focus window appropriately. */
restore_focus_after_unmap(const FvwmWindow * fw,Bool do_skip_marked_transients)1019 void restore_focus_after_unmap(
1020 const FvwmWindow *fw, Bool do_skip_marked_transients)
1021 {
1022 extern FvwmWindow *colormap_win;
1023 FvwmWindow *set_focus_to = NULL;
1024
1025 if (focus_is_focused(fw))
1026 {
1027 set_focus_to = __restore_focus_after_unmap(
1028 fw, do_skip_marked_transients);
1029 }
1030 if (fw == Scr.pushed_window)
1031 {
1032 Scr.pushed_window = NULL;
1033 }
1034 if (fw == colormap_win)
1035 {
1036 InstallWindowColormaps(set_focus_to);
1037 }
1038
1039 return;
1040 }
1041
IsLastFocusSetByMouse(void)1042 Bool IsLastFocusSetByMouse(void)
1043 {
1044 return lastFocusType;
1045 }
1046
1047
1048 /* same as above, but forces to regrab buttons on the window under the pointer
1049 * if necessary */
focus_grab_buttons_on_pointer_window(void)1050 void focus_grab_buttons_on_pointer_window(void)
1051 {
1052 Window w;
1053 FvwmWindow *fw;
1054
1055 if (!FQueryPointer(
1056 dpy, Scr.Root, &JunkRoot, &w, &JunkX, &JunkY, &JunkX,
1057 &JunkY, &JunkMask))
1058 {
1059 /* pointer is not on this screen */
1060 return;
1061 }
1062 if (XFindContext(dpy, w, FvwmContext, (caddr_t *) &fw) == XCNOENT)
1063 {
1064 /* pointer is not over a window */
1065 return;
1066 }
1067 focus_grab_buttons(fw);
1068
1069 return;
1070 }
1071
1072 /* functions to access ScreenFocus, LastScreenFocus and PreviousFocus */
get_focus_window(void)1073 FvwmWindow *get_focus_window(void)
1074 {
1075 return ScreenFocus;
1076 }
1077
set_focus_window(FvwmWindow * fw)1078 void set_focus_window(FvwmWindow *fw)
1079 {
1080 ScreenFocus = fw;
1081
1082 return;
1083 }
1084
get_last_screen_focus_window(void)1085 FvwmWindow *get_last_screen_focus_window(void)
1086 {
1087 return LastScreenFocus;
1088 }
1089
set_last_screen_focus_window(FvwmWindow * fw)1090 void set_last_screen_focus_window(FvwmWindow *fw)
1091 {
1092 LastScreenFocus = fw;
1093
1094 return;
1095 }
1096
update_last_screen_focus_window(FvwmWindow * fw)1097 void update_last_screen_focus_window(FvwmWindow *fw)
1098 {
1099 if (fw == LastScreenFocus)
1100 {
1101 LastScreenFocus = NULL;
1102 }
1103
1104 return;
1105 }
1106
set_focus_model(FvwmWindow * fw)1107 void set_focus_model(FvwmWindow *fw)
1108 {
1109 if (!focus_does_accept_input_focus(fw))
1110 {
1111 if (WM_TAKES_FOCUS(fw))
1112 {
1113 fw->focus_model = FM_GLOBALLY_ACTIVE;
1114 }
1115 else
1116 {
1117 fw->focus_model = FM_NO_INPUT;
1118 }
1119 }
1120 else
1121 {
1122 if (WM_TAKES_FOCUS(fw))
1123 {
1124 fw->focus_model = FM_LOCALLY_ACTIVE;
1125 }
1126 else
1127 {
1128 fw->focus_model = FM_PASSIVE;
1129 }
1130 }
1131
1132 return;
1133 }
1134
1135 /* This function is part of a hack to make focus handling work better with
1136 * applications that use the passive focus model but manage focus in their own
1137 * sub windows and should thus use the locally active focus model instead.
1138 * There are many examples like netscape or ddd. */
focus_force_refresh_focus(const FvwmWindow * fw)1139 void focus_force_refresh_focus(const FvwmWindow *fw)
1140 {
1141 XWindowAttributes winattrs;
1142
1143 MyXGrabServer(dpy);
1144 if (XGetWindowAttributes(dpy, FW_W(fw), &winattrs))
1145 {
1146 XSelectInput(
1147 dpy, FW_W(fw),
1148 winattrs.your_event_mask & ~FocusChangeMask);
1149 FOCUS_SET(FW_W(fw), NULL /* we don't expect an event */);
1150 XSelectInput(dpy, FW_W(fw), winattrs.your_event_mask);
1151 }
1152 MyXUngrabServer(dpy);
1153
1154 return;
1155 }
1156
refresh_focus(const FvwmWindow * fw)1157 void refresh_focus(const FvwmWindow *fw)
1158 {
1159 Bool do_refresh = False;
1160
1161 if (fw == NULL || !focus_is_focused(fw))
1162 {
1163 /* only refresh the focus on the currently focused window */
1164 return;
1165 }
1166
1167 /* only refresh the focus for windows with the passive focus model so
1168 * that we don't disturb focus handling more than necessary */
1169 switch (fw->focus_model)
1170 {
1171 case FM_PASSIVE:
1172 do_refresh = True;
1173 break;
1174 case FM_NO_INPUT:
1175 case FM_GLOBALLY_ACTIVE:
1176 case FM_LOCALLY_ACTIVE:
1177 default:
1178 do_refresh = False;
1179 break;
1180 }
1181 if (do_refresh)
1182 {
1183 focus_force_refresh_focus(fw);
1184 }
1185
1186 return;
1187 }
1188
1189 /* ---------------------------- builtin commands --------------------------- */
1190
CMD_FlipFocus(F_CMD_ARGS)1191 void CMD_FlipFocus(F_CMD_ARGS)
1192 {
1193 /* Reorder the window list */
1194 __activate_window_by_command(F_PASS_ARGS, 1);
1195
1196 return;
1197 }
1198
CMD_Focus(F_CMD_ARGS)1199 void CMD_Focus(F_CMD_ARGS)
1200 {
1201 __activate_window_by_command(F_PASS_ARGS, 0);
1202
1203 return;
1204 }
1205
CMD_WarpToWindow(F_CMD_ARGS)1206 void CMD_WarpToWindow(F_CMD_ARGS)
1207 {
1208 int val1_unit, val2_unit, n;
1209 int val1, val2;
1210 int do_raise;
1211 char *next;
1212 char *token;
1213
1214 next = GetNextToken(action, &token);
1215 if (StrEquals(token, "!raise"))
1216 {
1217 do_raise = 0;
1218 action = next;
1219 }
1220 else if (StrEquals(token, "raise"))
1221 {
1222 do_raise = 1;
1223 action = next;
1224 }
1225 else
1226 {
1227 do_raise = 1;
1228 }
1229 n = GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit);
1230 if (exc->w.wcontext != C_UNMANAGED)
1231 {
1232 if (n == 2)
1233 {
1234 warp_to_fvwm_window(
1235 exc, val1, val1_unit, val2, val2_unit,
1236 do_raise);
1237 }
1238 else
1239 {
1240 warp_to_fvwm_window(exc, 0, 0, 0, 0, do_raise);
1241 }
1242 }
1243 else
1244 {
1245 int x = 0;
1246 int y = 0;
1247
1248 if (n == 2)
1249 {
1250 int wx;
1251 int wy;
1252 int ww;
1253 int wh;
1254
1255 if (!XGetGeometry(
1256 dpy, exc->w.w, &JunkRoot, &wx, &wy,
1257 (unsigned int*)&ww, (unsigned int*)&wh,
1258 (unsigned int*)&JunkBW,
1259 (unsigned int*)&JunkDepth))
1260 {
1261 return;
1262 }
1263 if (val1_unit != Scr.MyDisplayWidth)
1264 {
1265 x = val1;
1266 }
1267 else
1268 {
1269 x = (ww - 1) * val1 / 100;
1270 }
1271 if (val2_unit != Scr.MyDisplayHeight)
1272 {
1273 y = val2;
1274 }
1275 else
1276 {
1277 y = (wh - 1) * val2 / 100;
1278 }
1279 if (x < 0)
1280 {
1281 x += ww;
1282 }
1283 if (y < 0)
1284 {
1285 y += wh;
1286 }
1287 }
1288 FWarpPointerUpdateEvpos(
1289 exc->x.elast, dpy, None, exc->w.w, 0, 0, 0, 0, x, y);
1290 }
1291
1292 return;
1293 }
1294