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