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.
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.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 /* This module is based on Twm, but has been siginificantly modified
20 * Copyright 1988 by Evans & Sutherland Computer Corporation,
21 * Salt Lake City, Utah
22 * Portions Copyright 1989 by the Massachusetts Institute of Technology
23 * Cambridge, Massachusetts
27 * Permission to use, copy, modify, and distribute this software and
28 * its documentation for any purpose and without fee is hereby
29 * granted, provided that the above copyright notice appear in all
30 * copies and that both that copyright notice and this permis-
31 * sion notice appear in supporting documentation, and that the
32 * names of Evans & Sutherland and M.I.T. not be used in advertising
33 * in publicity pertaining to distribution of the software without
34 * specific, written prior permission.
36 * EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD
37 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
38 * ABILITY AND FITNESS, IN NO EVENT SHALL EVANS & SUTHERLAND OR
39 * M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAM-
40 * AGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
41 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
42 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
43 * OR PERFORMANCE OF THIS SOFTWARE.
46 /* ---------------------------- included header files ---------------------- */
50 #if HAVE_SYS_BSDTYPES_H
51 #include <sys/bsdtypes.h>
56 #include <X11/Xatom.h>
58 #include "libs/ftime.h"
59 #include "libs/fvwmlib.h"
60 #include "libs/System.h"
61 #include "libs/Grab.h"
62 #include "libs/Parse.h"
63 #include "libs/ColorUtils.h"
64 #include "libs/FShape.h"
65 #include "libs/PictureBase.h"
66 #include "libs/Colorset.h"
67 #include "libs/charmap.h"
68 #include "libs/wcontext.h"
72 #include "functions.h"
78 #include "eventhandler.h"
79 #include "eventmask.h"
80 #include "libs/fvwmsignal.h"
81 #include "module_list.h"
82 #include "module_interface.h"
86 #include "add_window.h"
97 #include "decorations.h"
100 #include "colormaps.h"
101 #include "colorset.h"
104 #endif /* HAVE_STROKE */
106 /* ---------------------------- local definitions -------------------------- */
109 #define XUrgencyHint (1L << 8)
112 #define CR_MOVERESIZE_MASK (CWX | CWY | CWWidth | CWHeight | CWBorderWidth)
114 /* ---------------------------- local macros ------------------------------- */
116 /* ---------------------------- imports ------------------------------------ */
118 extern void StartupStuff(void);
120 /* ---------------------------- included code files ------------------------ */
122 /* ---------------------------- local types -------------------------------- */
124 typedef void (*PFEH
)(const evh_args_t
*ea
);
130 Bool do_return_true_cr
;
131 unsigned long cr_value_mask
;
133 unsigned long ret_type
;
134 } check_if_event_args
;
138 unsigned do_forbid_function
: 1;
139 unsigned do_focus
: 1;
140 unsigned do_swallow_click
: 1;
141 unsigned do_raise
: 1;
144 typedef struct event_group
149 struct event_group
*next
;
152 /* ---------------------------- forward declarations ----------------------- */
154 /* ---------------------------- local variables ---------------------------- */
156 static int Button
= 0;
157 static const FvwmWindow
*xcrossing_last_grab_window
= NULL
;
158 STROKE_CODE(static int send_motion
);
159 STROKE_CODE(static char sequence
[STROKE_MAX_SEQUENCE
+ 1]);
160 static event_group_t
*base_event_group
= NULL
;
162 /* ---------------------------- exported variables (globals) --------------- */
164 int last_event_type
= 0;
165 Window PressedW
= None
;
167 /* ---------------------------- local functions ---------------------------- */
169 static void fake_map_unmap_notify(const FvwmWindow
*fw
, int event_type
)
172 XWindowAttributes winattrs
= {0};
174 if (!XGetWindowAttributes(dpy
, FW_W(fw
), &winattrs
))
180 winattrs
.your_event_mask
& ~StructureNotifyMask
);
181 client_event
.type
= event_type
;
182 client_event
.xmap
.display
= dpy
;
183 client_event
.xmap
.event
= FW_W(fw
);
184 client_event
.xmap
.window
= FW_W(fw
);
188 client_event
.xmap
.override_redirect
= False
;
191 client_event
.xunmap
.from_configure
= False
;
194 /* not possible if called correctly */
198 dpy
, FW_W(fw
), False
, StructureNotifyMask
, &client_event
);
199 XSelectInput(dpy
, FW_W(fw
), winattrs
.your_event_mask
);
205 static Bool
test_map_request(
206 Display
*display
, XEvent
*event
, XPointer arg
)
208 check_if_event_args
*cie_args
;
211 cie_args
= (check_if_event_args
*)arg
;
212 cie_args
->ret_does_match
= False
;
213 if (event
->type
== MapRequest
&&
214 event
->xmaprequest
.window
== cie_args
->w
)
216 cie_args
->ret_type
= MapRequest
;
217 cie_args
->ret_does_match
= True
;
218 rc
= cie_args
->do_return_true
;
222 cie_args
->ret_type
= 0;
226 /* Yes, it is correct that this function always returns False. */
230 /* Test for ICCCM2 withdraw requests by syntetic events on the root window */
231 static Bool
test_withdraw_request(
232 Display
*display
, XEvent
*event
, XPointer arg
)
234 check_if_event_args
*cie_args
;
237 cie_args
= (check_if_event_args
*)arg
;
238 cie_args
->ret_does_match
= False
;
239 if (event
->type
== UnmapNotify
&&
240 event
->xunmap
.window
== cie_args
->w
&&
241 event
->xany
.send_event
== True
&&
242 event
->xunmap
.event
== FW_W(&Scr
.FvwmRoot
))
244 cie_args
->ret_type
= UnmapNotify
;
245 cie_args
->ret_does_match
= True
;
246 rc
= cie_args
->do_return_true
;
250 cie_args
->ret_type
= 0;
257 Bool
test_button_event(
258 Display
*display
, XEvent
*event
, XPointer arg
)
260 if (event
->type
== ButtonPress
|| event
->type
== ButtonRelease
)
268 Bool
test_typed_window_event(
269 Display
*display
, XEvent
*event
, XPointer arg
)
271 test_typed_window_event_args
*ta
= (test_typed_window_event_args
*)arg
;
273 if (event
->xany
.window
== ta
->w
&&
274 event
->xany
.type
== ta
->event_type
&&
275 event
->xproperty
.atom
== ta
->atom
)
283 static Bool
test_resizing_event(
284 Display
*display
, XEvent
*event
, XPointer arg
)
286 check_if_event_args
*cie_args
;
289 cie_args
= (check_if_event_args
*)arg
;
290 cie_args
->ret_does_match
= False
;
291 if (event
->xany
.window
!= cie_args
->w
)
298 case ConfigureRequest
:
299 if ((event
->xconfigurerequest
.value_mask
&
300 cie_args
->cr_value_mask
) != 0)
302 cie_args
->ret_type
= ConfigureRequest
;
303 cie_args
->ret_does_match
= True
;
304 rc
= cie_args
->do_return_true_cr
;
308 if (event
->xproperty
.atom
== XA_WM_NORMAL_HINTS
)
310 cie_args
->ret_type
= PropertyNotify
;
311 cie_args
->ret_does_match
= True
;
312 rc
= cie_args
->do_return_true
;
318 /* Yes, it is correct that this function may always returns False. */
322 static inline void __handle_cr_on_unmanaged(XConfigureRequestEvent
*cre
)
327 xwcm
= (cre
->value_mask
& CR_MOVERESIZE_MASK
);
330 xwc
.width
= cre
->width
;
331 xwc
.height
= cre
->height
;
332 xwc
.border_width
= cre
->border_width
;
333 XConfigureWindow(dpy
, cre
->window
, xwcm
, &xwc
);
338 static inline void __handle_cr_on_icon(
339 XConfigureRequestEvent
*cre
, FvwmWindow
*fw
)
344 xwcm
= (cre
->value_mask
& CR_MOVERESIZE_MASK
);
347 xwc
.width
= cre
->width
;
348 xwc
.height
= cre
->height
;
349 xwc
.border_width
= cre
->border_width
;
350 if (FW_W_ICON_PIXMAP(fw
) == cre
->window
)
354 if (cre
->value_mask
& CWBorderWidth
)
356 fw
->icon_border_width
= cre
->border_width
;
358 bw
= fw
->icon_border_width
;
359 if ((cre
->value_mask
& (CWWidth
| CWHeight
)) ==
360 (CWWidth
| CWHeight
))
362 set_icon_picture_size(
363 fw
, cre
->width
+ 2 * bw
, cre
->height
+ 2 * bw
);
366 set_icon_position(fw
, cre
->x
, cre
->y
);
367 broadcast_icon_geometry(fw
, False
);
368 XConfigureWindow(dpy
, cre
->window
, xwcm
, &xwc
);
369 if (cre
->window
!= FW_W_ICON_PIXMAP(fw
) &&
370 FW_W_ICON_PIXMAP(fw
) != None
)
374 get_icon_picture_geometry(fw
, &g
);
377 xwcm
= cre
->value_mask
& (CWX
| CWY
);
379 dpy
, FW_W_ICON_PIXMAP(fw
), xwcm
, &xwc
);
381 if (FW_W_ICON_TITLE(fw
) != None
)
385 get_icon_title_geometry(fw
, &g
);
388 xwcm
= cre
->value_mask
& (CWX
| CWY
);
390 dpy
, FW_W_ICON_TITLE(fw
), xwcm
, &xwc
);
396 static inline void __handle_cr_on_shaped(FvwmWindow
*fw
)
398 /* suppress compiler warnings w/o shape extension */
404 if (FShapeQueryExtents(
405 dpy
, FW_W(fw
), &boundingShaped
, &i
, &i
, &u
, &u
, &b
,
408 fw
->wShaped
= boundingShaped
;
418 static inline void __handle_cr_restack(
419 int *ret_do_send_event
, XConfigureRequestEvent
*cre
, FvwmWindow
*fw
)
423 FvwmWindow
*fw2
= NULL
;
425 if (cre
->value_mask
& CWSibling
)
428 dpy
, cre
->above
, FvwmContext
,
429 (caddr_t
*)&fw2
) == XCNOENT
)
438 if (cre
->detail
!= Above
&& cre
->detail
!= Below
)
440 HandleUnusualStackmodes(
441 cre
->detail
, fw
, cre
->window
, fw2
, cre
->above
);
443 /* only allow clients to restack windows within their layer */
444 else if (fw2
== NULL
|| compare_window_layers(fw2
, fw
) != 0)
449 RaiseWindow(fw
, True
);
452 LowerWindow(fw
, True
);
458 xwc
.sibling
= FW_W_FRAME(fw2
);
459 xwc
.stack_mode
= cre
->detail
;
460 xwcm
= CWSibling
| CWStackMode
;
461 XConfigureWindow(dpy
, FW_W_FRAME(fw
), xwcm
, &xwc
);
463 /* Maintain the condition that icon windows are stacked
464 * immediately below their frame
466 xwc
.sibling
= FW_W_FRAME(fw
);
467 xwc
.stack_mode
= Below
;
468 xwcm
= CWSibling
| CWStackMode
;
469 if (FW_W_ICON_TITLE(fw
) != None
)
472 dpy
, FW_W_ICON_TITLE(fw
), xwcm
, &xwc
);
474 if (FW_W_ICON_PIXMAP(fw
) != None
)
477 dpy
, FW_W_ICON_PIXMAP(fw
), xwcm
, &xwc
);
480 if (cre
->detail
== Below
)
482 xwc
.sibling
= FW_W_FRAME(fw2
);
483 xwc
.stack_mode
= Below
;
484 xwcm
= CWSibling
| CWStackMode
;
485 if (FW_W_ICON_TITLE(fw2
) != None
)
488 dpy
, FW_W_ICON_TITLE(fw2
), xwcm
, &xwc
);
490 if (FW_W_ICON_PIXMAP(fw2
) != None
)
493 dpy
, FW_W_ICON_PIXMAP(fw2
), xwcm
,
497 /* Maintain the stacking order ring */
498 if (cre
->detail
== Above
)
500 remove_window_from_stack_ring(fw
);
501 add_window_to_stack_ring_after(
502 fw
, get_prev_window_in_stack_ring(fw2
));
504 else /* cre->detail == Below */
506 remove_window_from_stack_ring(fw
);
507 add_window_to_stack_ring_after(fw
, fw2
);
509 BroadcastRestackThisWindow(fw
);
511 /* srt (28-Apr-2001): Tk needs a ConfigureNotify event after a
512 * raise, otherwise it would hang for two seconds */
513 *ret_do_send_event
= 1;
518 static inline void __cr_get_static_position(
519 rectangle
*ret_g
, FvwmWindow
*fw
, XConfigureRequestEvent
*cre
,
522 if (cre
->value_mask
& CWX
)
524 ret_g
->x
= cre
->x
- b
->top_left
.width
;
528 ret_g
->x
= fw
->g
.frame
.x
;
530 if (cre
->value_mask
& CWY
)
532 ret_g
->y
= cre
->y
- b
->top_left
.height
;
536 ret_g
->y
= fw
->g
.frame
.y
;
542 static inline void __cr_get_grav_position(
543 rectangle
*ret_g
, FvwmWindow
*fw
, XConfigureRequestEvent
*cre
,
549 gravity_get_offsets(fw
->hints
.win_gravity
, &grav_x
, &grav_y
);
550 if (cre
->value_mask
& CWX
)
552 ret_g
->x
= cre
->x
- ((grav_x
+ 1) * b
->total_size
.width
) / 2;
556 ret_g
->x
= fw
->g
.frame
.x
;
558 if (cre
->value_mask
& CWY
)
560 ret_g
->y
= cre
->y
- ((grav_y
+ 1) * b
->total_size
.height
) / 2;
564 ret_g
->y
= fw
->g
.frame
.y
;
570 /* Try to detect whether the application uses the ICCCM way of moving its
571 * window or the traditional way, always assuming StaticGravity. */
572 static inline void __cr_detect_icccm_move(
573 FvwmWindow
*fw
, XConfigureRequestEvent
*cre
, size_borders
*b
)
587 if (CR_MOTION_METHOD(fw
) != CR_MOTION_METHOD_AUTO
)
589 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
593 "_cdim: --- already detected (pid %d) %p"
594 " '%s'\n", HAS_EWMH_WM_PID(fw
), fw
,
599 if (HAS_EWMH_WM_PID(fw
))
601 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
604 stderr
,"_cdim: +++ has ewmh_wm_pid: icccm"
605 " %p '%s'\n", fw
, fw
->visible_name
);
607 SET_CR_MOTION_METHOD(fw
, CR_MOTION_METHOD_USE_GRAV
);
608 SET_CR_MOTION_METHOD_DETECTED(fw
, 1);
611 if (fw
->ewmh_window_type
!= EWMH_WINDOW_TYPE_NONE_ID
)
613 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
616 stderr
, "_cdim: +++ has ewmh_window_type:"
617 " icccm %p '%s'\n", fw
,
620 SET_CR_MOTION_METHOD(fw
, CR_MOTION_METHOD_USE_GRAV
);
621 SET_CR_MOTION_METHOD_DETECTED(fw
, 1);
624 if (FShapesSupported
&& fw
->wShaped
)
626 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
629 stderr
, "_cdim: --- shaped window %p "
630 "'%s'\n", fw
, fw
->visible_name
);
632 /* no detection for shaped windows */
635 if (fw
->hints
.win_gravity
== StaticGravity
)
637 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
640 stderr
, "_cdim: --- using StaticGravity"
641 " %p '%s'\n", fw
, fw
->visible_name
);
645 has_x
= (cre
->value_mask
& CWX
);
646 has_y
= (cre
->value_mask
& CWY
);
647 if (!has_x
&& !has_y
)
649 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
652 stderr
, "_cdim: --- not moved %p '%s'\n",
653 fw
, fw
->visible_name
);
657 __cr_get_grav_position(&grav_g
, fw
, cre
, b
);
658 __cr_get_static_position(&static_g
, fw
, cre
, b
);
659 if (static_g
.x
== grav_g
.x
)
661 /* both methods have the same result; ignore */
664 if (static_g
.y
== grav_g
.y
)
666 /* both methods have the same result; ignore */
669 if (!has_x
&& !has_y
)
671 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
674 stderr
, "_cdim: --- not moved %p '%s'\n",
675 fw
, fw
->visible_name
);
679 dg_g
.x
= grav_g
.x
- fw
->g
.frame
.x
;
680 dg_g
.y
= grav_g
.y
- fw
->g
.frame
.y
;
681 ds_g
.x
= static_g
.x
- fw
->g
.frame
.x
;
682 ds_g
.y
= static_g
.y
- fw
->g
.frame
.y
;
683 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
686 stderr
, "s %3d/%3d %2d/%2d, g %3d/%3d %2d/%2d: ",
687 static_g
.x
, static_g
.y
, ds_g
.x
, ds_g
.y
, grav_g
.x
,
688 grav_g
.y
, dg_g
.x
, dg_g
.y
);
690 /* check full screen */
691 if ((cre
->value_mask
& (CWX
| CWY
)) == (CWX
| CWY
) &&
693 cre
->width
== Scr
.MyDisplayWidth
&&
694 cre
->height
== Scr
.MyDisplayHeight
)
696 if (grav_g
.x
== -b
->top_left
.width
&&
697 grav_g
.y
== -b
->top_left
.height
)
699 /* Window is fullscreen using the ICCCM way. */
700 SET_CR_MOTION_METHOD(fw
, CR_MOTION_METHOD_USE_GRAV
);
701 SET_CR_MOTION_METHOD_DETECTED(fw
, 1);
702 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
705 stderr
, "+++ fullscreen icccm %p"
706 " '%s'\n", fw
, fw
->visible_name
);
710 else if (static_g
.x
== -b
->top_left
.width
&&
711 static_g
.y
== -b
->top_left
.height
)
713 /* Window is fullscreen using the traditional way. */
714 SET_CR_MOTION_METHOD(fw
, CR_MOTION_METHOD_STATIC_GRAV
);
715 SET_CR_MOTION_METHOD_DETECTED(fw
, 1);
716 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
719 stderr
, "+++ fullscreen traditional"
726 /* check travelling across the screen */
727 if (has_x
&& dg_g
.x
== 0 && ds_g
.x
!= 0 &&
728 has_y
&& dg_g
.y
== 0 && ds_g
.y
!= 0)
730 /* The traditional way causes a shift by the border width or
731 * height. Use ICCCM way. */
732 SET_CR_MOTION_METHOD(fw
, CR_MOTION_METHOD_USE_GRAV
);
733 SET_CR_MOTION_METHOD_DETECTED(fw
, 1);
734 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
737 stderr
, "+++ travelling icccm %p '%s'\n",
738 fw
, fw
->visible_name
);
742 if (has_x
&& dg_g
.x
!= 0 && ds_g
.x
== 0 &&
743 has_y
&& dg_g
.y
!= 0 && ds_g
.y
== 0)
745 /* The ICCCM way causes a shift by the border width or height.
746 * Use traditional way. */
747 SET_CR_MOTION_METHOD(fw
, CR_MOTION_METHOD_STATIC_GRAV
);
748 SET_CR_MOTION_METHOD_DETECTED(fw
, 1);
749 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
752 stderr
, "+++ travelling traditional %p"
753 " '%s'\n", fw
, fw
->visible_name
);
757 /* check placement near border */
758 w
= (cre
->value_mask
& CWWidth
) ?
759 cre
->width
+ b
->total_size
.width
: fw
->g
.frame
.width
;
760 h
= (cre
->value_mask
& CWHeight
) ?
761 cre
->height
+ b
->total_size
.height
: fw
->g
.frame
.height
;
764 mx
= CR_MOTION_METHOD_AUTO
;
766 else if (static_g
.x
== 0 || static_g
.x
+ w
== Scr
.MyDisplayWidth
)
768 mx
= CR_MOTION_METHOD_STATIC_GRAV
;
770 else if (grav_g
.x
== 0 || grav_g
.x
+ w
== Scr
.MyDisplayWidth
)
772 mx
= CR_MOTION_METHOD_USE_GRAV
;
776 mx
= CR_MOTION_METHOD_AUTO
;
780 my
= CR_MOTION_METHOD_AUTO
;
782 else if (static_g
.y
== 0 || static_g
.y
+ h
== Scr
.MyDisplayHeight
)
784 my
= CR_MOTION_METHOD_STATIC_GRAV
;
786 else if (grav_g
.y
== 0 || grav_g
.y
+ h
== Scr
.MyDisplayHeight
)
788 my
= CR_MOTION_METHOD_USE_GRAV
;
792 my
= CR_MOTION_METHOD_AUTO
;
794 m
= (mx
!= CR_MOTION_METHOD_AUTO
) ? mx
: my
;
795 if (m
!= CR_MOTION_METHOD_AUTO
)
797 /* Window was placed next to the display border. */
798 if (m
== my
|| my
== CR_MOTION_METHOD_AUTO
)
800 SET_CR_MOTION_METHOD(fw
, m
);
801 SET_CR_MOTION_METHOD_DETECTED(fw
, 1);
802 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
805 stderr
, "+++ near border %s %p "
807 CR_MOTION_METHOD_USE_GRAV
)
808 ? "icccm" : "traditional", fw
,
814 if (Scr
.bo
.do_debug_cr_motion_method
== 1)
817 stderr
, "--- not detected %p '%s'\n", fw
,
824 #define EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
825 /* This is not a good idea because this interferes with changes in the size
826 * hints of the window. However, it is impossible to be completely safe here.
827 * For example, if the client changes the size inc, then resizes the size of
828 * its window and then changes the size inc again - all in one batch - then
829 * the WM will read the *second* size inc upon the *first* event and use the
830 * wrong one in the ConfigureRequest calculations. */
831 /* dv (31 Mar 2002): The code now handles these situations, so enable it
833 #ifdef EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
834 static inline int __merge_cr_moveresize(
835 const evh_args_t
*ea
, XConfigureRequestEvent
*cre
, FvwmWindow
*fw
,
840 XConfigureRequestEvent
*ecre
;
841 check_if_event_args args
;
843 args
.w
= cre
->window
;
844 args
.do_return_true
= False
;
845 args
.do_return_true_cr
= True
;
846 args
.cr_value_mask
= CR_MOVERESIZE_MASK
;
847 args
.ret_does_match
= False
;
850 for (cn_count
= 0; 1; )
857 exec_context_changes_t ecc
;
860 dpy
, &e
, test_resizing_event
, (XPointer
)&args
);
861 ecre
= &e
.xconfigurerequest
;
862 if (args
.ret_does_match
== False
)
866 else if (args
.ret_type
== PropertyNotify
)
868 /* Can't merge events with a PropertyNotify in
869 * between. The event is still on the queue. */
872 else if (args
.ret_type
!= ConfigureRequest
)
874 /* not good. unselected event type! */
877 /* Event was not yet removed from the queue but stored in e. */
880 vma
= cre
->value_mask
& ecre
->value_mask
;
881 vmo
= cre
->value_mask
| ecre
->value_mask
;
882 if (((vma
& xm
) == 0 && (vmo
& xm
) == xm
) ||
883 ((vma
& ym
) == 0 && (vmo
& ym
) == ym
))
885 /* can't merge events since location of window might
889 /* Finally remove the event from the queue */
890 FCheckIfEvent(dpy
, &e
, test_resizing_event
, (XPointer
)&args
);
891 /* partially handle the event */
892 ecre
->value_mask
&= ~args
.cr_value_mask
;
893 ea2
.exc
= exc_clone_context(ea
->exc
, &ecc
, ECC_ETRIGGER
);
894 HandleConfigureRequest(&ea2
);
895 exc_destroy_context(ea2
.exc
);
896 /* collect the size/position changes */
897 if (ecre
->value_mask
& CWX
)
901 if (ecre
->value_mask
& CWY
)
905 if (ecre
->value_mask
& CWWidth
)
907 cre
->width
= ecre
->width
;
909 if (ecre
->value_mask
& CWHeight
)
911 cre
->height
= ecre
->height
;
913 if (ecre
->value_mask
& CWBorderWidth
)
915 cre
->border_width
= ecre
->border_width
;
917 cre
->value_mask
|= (ecre
->value_mask
& CR_MOVERESIZE_MASK
);
925 static inline int __handle_cr_on_client(
926 int *ret_do_send_event
, XConfigureRequestEvent cre
,
927 const evh_args_t
*ea
, FvwmWindow
*fw
, Bool force
, int force_gravity
)
932 size_rect constr_dim
;
933 size_rect oldnew_dim
;
940 cre
= ea
->exc
->x
.etrigger
->xconfigurerequest
;
942 if ((cre
.value_mask
& (CWWidth
| CWHeight
| CWX
| CWY
)) == 0)
947 get_window_borders(fw
, &b
);
948 #ifdef EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
949 /* Merge all pending ConfigureRequests for the window into a single
950 * event. However, we can not do this if the window uses the motion
951 * method autodetection because the merged event might confuse the
953 if (ea
&& CR_MOTION_METHOD(fw
) != CR_MOTION_METHOD_AUTO
)
955 cn_count
= __merge_cr_moveresize(ea
, &cre
, fw
, &b
);
960 "cre: %d(%d) %d(%d) %d(%d)x%d(%d) fw 0x%08x w 0x%08x "
962 cre
.x
, (int)(cre
.value_mask
& CWX
),
963 cre
.y
, (int)(cre
.value_mask
& CWY
),
964 cre
.width
, (int)(cre
.value_mask
& CWWidth
),
965 cre
.height
, (int)(cre
.value_mask
& CWHeight
),
966 (int)FW_W_FRAME(fw
), (int)FW_W(fw
), (int)cre
.window
,
967 (fw
->name
.name
) ? fw
->name
.name
: "");
969 /* Don't modify frame_g fields before calling SetupWindow! */
970 memset(&d_g
, 0, sizeof(d_g
));
972 if (HAS_NEW_WM_NORMAL_HINTS(fw
))
974 /* get the latest size hints */
976 GetWindowSizeHints(fw
);
977 SET_HAS_NEW_WM_NORMAL_HINTS(fw
, 0);
979 if (!HAS_OVERRIDE_SIZE_HINTS(fw
) && (fw
->hints
.flags
& PMaxSize
))
981 /* Java workaround */
982 if (cre
.height
> fw
->hints
.max_height
&&
983 fw
->hints
.max_height
<= BROKEN_MAXSIZE_LIMIT
)
985 fw
->hints
.max_height
= DEFAULT_MAX_MAX_WINDOW_HEIGHT
;
986 cre
.value_mask
|= CWHeight
;
988 if (cre
.width
> fw
->hints
.max_width
&&
989 fw
->hints
.max_width
<= BROKEN_MAXSIZE_LIMIT
)
991 fw
->hints
.max_width
= DEFAULT_MAX_MAX_WINDOW_WIDTH
;
992 cre
.value_mask
|= CWWidth
;
995 if (!HAS_OVERRIDE_SIZE_HINTS(fw
) && (fw
->hints
.flags
& PMinSize
))
997 if (cre
.width
< fw
->hints
.min_width
&&
998 fw
->hints
.min_width
>= BROKEN_MINSIZE_LIMIT
)
1000 fw
->hints
.min_width
= 1;
1001 cre
.value_mask
|= CWWidth
;
1003 if (cre
.height
< fw
->hints
.min_height
&&
1004 fw
->hints
.min_height
>= BROKEN_MINSIZE_LIMIT
)
1006 fw
->hints
.min_height
= 1;
1007 cre
.value_mask
|= CWHeight
;
1010 if (IS_SHADED(fw
) ||
1011 !is_function_allowed(F_MOVE
, NULL
, fw
, RQORIG_PROGRAM
, False
))
1013 /* forbid shaded applications to move their windows */
1014 cre
.value_mask
&= ~(CWX
| CWY
);
1015 /* resend the old geometry */
1016 *ret_do_send_event
= 1;
1018 if (IS_MAXIMIZED(fw
))
1020 /* dont allow clients to resize maximized windows */
1021 cre
.value_mask
&= ~(CWWidth
| CWHeight
);
1022 /* resend the old geometry */
1023 *ret_do_send_event
= 1;
1028 !is_function_allowed(
1029 F_RESIZE
, NULL
, fw
, RQORIG_PROGRAM
, False
))
1031 cre
.value_mask
&= ~(CWWidth
| CWHeight
);
1032 *ret_do_send_event
= 1;
1035 if (cre
.value_mask
& CWBorderWidth
)
1038 fw
->attr_backup
.border_width
= cre
.border_width
;
1040 if (!force
&& CR_MOTION_METHOD(fw
) == CR_MOTION_METHOD_AUTO
)
1042 __cr_detect_icccm_move(fw
, &cre
, &b
);
1044 if (force_gravity
> ForgetGravity
&& force_gravity
<= StaticGravity
)
1046 gravity
= force_gravity
;
1050 gravity
= fw
->hints
.win_gravity
;
1054 direction_t gravity_dir
;
1056 get_unshaded_geometry(fw
, ¤t_g
);
1057 /* the shade direction overrides the window's gravity */
1058 gravity_dir
= gravity_grav_to_dir(gravity
);
1059 gravity_dir
= gravity_override_dir(
1060 gravity_dir
, SHADED_DIR(fw
));
1061 gravity
= gravity_dir_to_grav(gravity_dir
);
1065 current_g
= fw
->g
.frame
;
1067 if (!(cre
.value_mask
& (CWX
| CWY
)))
1072 CR_MOTION_METHOD(fw
) == CR_MOTION_METHOD_USE_GRAV
) &&
1073 gravity
!= StaticGravity
)
1080 gravity_get_offsets(gravity
, &grav_x
, &grav_y
);
1081 if (cre
.value_mask
& CWX
)
1084 ((grav_x
+ 1) * b
.total_size
.width
) / 2;
1085 d_g
.x
= ref_x
- current_g
.x
;
1087 if (cre
.value_mask
& CWY
)
1090 ((grav_y
+ 1) * b
.total_size
.height
) / 2;
1091 d_g
.y
= ref_y
- current_g
.y
;
1094 else /* ..._USE_GRAV or ..._AUTO */
1096 /* default: traditional cr handling */
1097 if (cre
.value_mask
& CWX
)
1099 d_g
.x
= cre
.x
- current_g
.x
- b
.top_left
.width
;
1101 if (cre
.value_mask
& CWY
)
1103 d_g
.y
= cre
.y
- current_g
.y
- b
.top_left
.height
;
1107 if (cre
.value_mask
& CWHeight
)
1110 (WINDOW_FREAKED_OUT_SIZE
- b
.total_size
.height
))
1112 d_g
.height
= cre
.height
-
1113 (current_g
.height
- b
.total_size
.height
);
1117 /* Ignore height changes to astronomically large
1118 * windows (needed for XEmacs 20.4); don't care if the
1119 * window is shaded here - we won't use 'height' in
1121 * Inform the buggy app about the size that *we* want
1124 *ret_do_send_event
= 1;
1127 if (cre
.value_mask
& CWWidth
)
1129 if (cre
.width
< (WINDOW_FREAKED_OUT_SIZE
- b
.total_size
.width
))
1131 d_g
.width
= cre
.width
-
1132 (current_g
.width
- b
.total_size
.width
);
1137 *ret_do_send_event
= 1;
1141 /* SetupWindow (x,y) are the location of the upper-left outer corner
1142 * and are passed directly to XMoveResizeWindow (frame). The
1143 * (width,height) are the inner size of the frame. The inner width is
1144 * the same as the requested client window width; the inner height is
1145 * the same as the requested client window height plus any title bar
1148 oldnew_dim
.width
= new_g
.width
+ d_g
.width
;
1149 oldnew_dim
.height
= new_g
.height
+ d_g
.height
;
1150 constr_dim
.width
= oldnew_dim
.width
;
1151 constr_dim
.height
= oldnew_dim
.height
;
1153 fw
, NULL
, &constr_dim
.width
, &constr_dim
.height
, 0, 0,
1154 CS_UPDATE_MAX_DEFECT
);
1155 d_g
.width
+= (constr_dim
.width
- oldnew_dim
.width
);
1156 d_g
.height
+= (constr_dim
.height
- oldnew_dim
.height
);
1157 if ((cre
.value_mask
& CWX
) && d_g
.width
)
1159 new_g
.x
= current_g
.x
+ d_g
.x
;
1160 new_g
.width
= current_g
.width
+ d_g
.width
;
1162 else if ((cre
.value_mask
& CWX
) && !d_g
.width
)
1164 new_g
.x
= current_g
.x
+ d_g
.x
;
1166 else if (!(cre
.value_mask
& CWX
) && d_g
.width
)
1168 gravity_resize(gravity
, &new_g
, d_g
.width
, 0);
1170 if ((cre
.value_mask
& CWY
) && d_g
.height
)
1172 new_g
.y
= current_g
.y
+ d_g
.y
;
1173 new_g
.height
= current_g
.height
+ d_g
.height
;
1175 else if ((cre
.value_mask
& CWY
) && !d_g
.height
)
1177 new_g
.y
= current_g
.y
+ d_g
.y
;
1179 else if (!(cre
.value_mask
& CWY
) && d_g
.height
)
1181 gravity_resize(gravity
, &new_g
, 0, d_g
.height
);
1184 if (new_g
.x
== current_g
.x
&& new_g
.y
== current_g
.y
&&
1185 new_g
.width
== current_g
.width
&&
1186 new_g
.height
== current_g
.height
)
1188 /* Window will not be moved or resized; send a synthetic
1189 * ConfigureNotify. */
1190 *ret_do_send_event
= 1;
1192 else if ((cre
.value_mask
& CWX
) || (cre
.value_mask
& CWY
) ||
1193 d_g
.width
|| d_g
.height
)
1197 fw
->g
.normal
= new_g
;
1198 get_shaded_geometry(fw
, &new_g
, &new_g
);
1200 frame_setup_window_app_request(
1201 fw
, new_g
.x
, new_g
.y
, new_g
.width
, new_g
.height
,
1203 /* make sure the window structure has the new position */
1204 update_absolute_geometry(fw
);
1205 maximize_adjust_offset(fw
);
1206 GNOME_SetWinArea(fw
);
1208 else if (DO_FORCE_NEXT_CR(fw
))
1210 *ret_do_send_event
= 1;
1212 SET_FORCE_NEXT_CR(fw
, 0);
1213 SET_FORCE_NEXT_PN(fw
, 0);
1218 void __handle_configure_request(
1219 XConfigureRequestEvent cre
, const evh_args_t
*ea
, FvwmWindow
*fw
,
1220 Bool force
, int force_gravity
)
1222 int do_send_event
= 0;
1225 /* According to the July 27, 1988 ICCCM draft, we should ignore size
1226 * and position fields in the WM_NORMAL_HINTS property when we map a
1227 * window. Instead, we'll read the current geometry. Therefore, we
1228 * should respond to configuration requests for windows which have
1229 * never been mapped. */
1232 __handle_cr_on_unmanaged(&cre
);
1235 if (cre
.window
== FW_W_ICON_TITLE(fw
) ||
1236 cre
.window
== FW_W_ICON_PIXMAP(fw
))
1238 __handle_cr_on_icon(&cre
, fw
);
1240 if (FShapesSupported
)
1242 __handle_cr_on_shaped(fw
);
1244 if (fw
!= NULL
&& cre
.window
== FW_W(fw
))
1246 cn_count
= __handle_cr_on_client(
1247 &do_send_event
, cre
, ea
, fw
, force
, force_gravity
);
1249 /* Stacking order change requested. Handle this *after* geometry
1250 * changes, since we need the new geometry in occlusion calculations */
1251 if ((cre
.value_mask
& CWStackMode
) &&
1252 (!DO_IGNORE_RESTACK(fw
) || force
))
1254 __handle_cr_restack(&do_send_event
, &cre
, fw
);
1257 /* This causes some ddd windows not to be drawn properly. Reverted back
1258 * to the old method in frame_setup_window. */
1259 /* domivogt (15-Oct-1999): enabled this to work around buggy apps that
1260 * ask for a nonsense height and expect that they really get it. */
1261 if (cn_count
== 0 && do_send_event
)
1265 else if (cn_count
> 0)
1269 for ( ; cn_count
> 0; cn_count
--)
1271 SendConfigureNotify(
1272 fw
, fw
->g
.frame
.x
, fw
->g
.frame
.y
, fw
->g
.frame
.width
,
1273 fw
->g
.frame
.height
, 0, True
);
1284 static Bool
__predicate_button_click(
1285 Display
*display
, XEvent
*event
, XPointer arg
)
1287 if (event
->type
== ButtonPress
|| event
->type
== ButtonRelease
)
1295 /* Helper function for __handle_focus_raise_click(). */
1296 static Bool
__test_for_motion(int x0
, int y0
)
1303 /* Query the pointer to do this. We can't check for events here since
1304 * the events are still needed if the pointer moves. */
1306 /* However, some special mouse (e.g., a touchpad with the
1307 * synaptic driver) may handle a double click in a special way
1308 * (for dragging through short touching and holding down the
1309 * finger on the touchpad). Bascially, when you execute a
1310 * double click the first button release is queued after the
1311 * second _physical_ mouse release happen. It seems that
1312 * FQueryPointer may not work as expected: it does not see
1313 * that the button is released on a double click. So, we need
1314 * to check for a button press in the future to avoid a fvwm
1315 * lockup! (olicha 2004-01-31) */
1317 for (x
= x0
, y
= y0
; FQueryPointer(
1318 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
, &JunkX
, &JunkY
,
1319 &x
, &y
, &mask
) == True
; usleep(20000))
1321 if ((mask
& DEFAULT_ALL_BUTTONS_MASK
) == 0)
1323 /* all buttons are released */
1326 else if (abs(x
- x0
) >= Scr
.MoveThreshold
||
1327 abs(y
- y0
) >= Scr
.MoveThreshold
)
1329 /* the pointer has moved */
1332 if (FCheckPeekIfEvent(dpy
, &e
, __predicate_button_click
, NULL
))
1334 /* click in the future */
1339 /* The predicate procedure finds no match, no event
1340 * has been removed from the queue and XFlush was
1341 * called. Nothing to do */
1345 /* pointer has moved off screen */
1349 /* Helper function for __handle_focus_raise_click(). */
1350 static void __check_click_to_focus_or_raise(
1351 hfrc_ret_t
*ret_args
, const exec_context_t
*exc
)
1353 FvwmWindow
* const fw
= exc
->w
.fw
;
1354 const XEvent
*te
= exc
->x
.etrigger
;
1357 unsigned is_client_click
: 1;
1358 unsigned is_focused
: 1;
1361 f
.is_focused
= !!focus_is_focused(fw
);
1362 f
.is_client_click
= (exc
->w
.wcontext
== C_WINDOW
||
1363 exc
->w
.wcontext
== C_EWMH_DESKTOP
);
1364 /* check if we need to raise and/or focus the window */
1365 ret_args
->do_focus
= focus_query_click_to_focus(fw
, exc
->w
.wcontext
);
1366 if (f
.is_client_click
&& !ret_args
->do_focus
&&
1367 !f
.is_focused
&& FP_DO_FOCUS_BY_PROGRAM(FW_FOCUS_POLICY(fw
)) &&
1368 !fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw
)))
1370 /* Give the window a chance to to take focus itself */
1371 ret_args
->do_focus
= 1;
1373 if (ret_args
->do_focus
&& focus_is_focused(fw
))
1375 ret_args
->do_focus
= 0;
1377 ret_args
->do_raise
=
1378 focus_query_click_to_raise(fw
, f
.is_focused
, exc
->w
.wcontext
);
1379 #define EXPERIMENTAL_ROU_HANDLING_V2
1380 #ifdef EXPERIMENTAL_ROU_HANDLING_V2
1381 /* RBW -- Dang! This works without the one in HandleEnterNotify! */
1382 if (ret_args
->do_raise
&& is_on_top_of_layer_and_above_unmanaged(fw
))
1384 if (ret_args
->do_raise
&& is_on_top_of_layer(fw
))
1387 ret_args
->do_raise
= 0;
1389 if ((ret_args
->do_focus
&&
1390 FP_DO_IGNORE_FOCUS_CLICK_MOTION(FW_FOCUS_POLICY(fw
))) ||
1391 (ret_args
->do_raise
&&
1392 FP_DO_IGNORE_RAISE_CLICK_MOTION(FW_FOCUS_POLICY(fw
))))
1394 /* Pass further events to the application and check if a button
1395 * release or motion event occurs next. If we don't do this
1396 * here, the pointer will seem to be frozen in
1397 * __test_for_motion(). */
1398 XAllowEvents(dpy
, ReplayPointer
, CurrentTime
);
1399 if (__test_for_motion(te
->xbutton
.x_root
, te
->xbutton
.y_root
))
1401 /* the pointer was moved, process event normally */
1402 ret_args
->do_focus
= 0;
1403 ret_args
->do_raise
= 0;
1406 if (ret_args
->do_focus
|| ret_args
->do_raise
)
1408 if (!((ret_args
->do_focus
&&
1409 FP_DO_ALLOW_FUNC_FOCUS_CLICK(FW_FOCUS_POLICY(fw
))) ||
1410 (ret_args
->do_raise
&&
1411 FP_DO_ALLOW_FUNC_RAISE_CLICK(FW_FOCUS_POLICY(fw
)))))
1413 ret_args
->do_forbid_function
= 1;
1415 if (!((ret_args
->do_focus
&&
1416 FP_DO_PASS_FOCUS_CLICK(FW_FOCUS_POLICY(fw
))) ||
1417 (ret_args
->do_raise
&&
1418 FP_DO_PASS_RAISE_CLICK(FW_FOCUS_POLICY(fw
)))))
1420 ret_args
->do_swallow_click
= 1;
1427 /* Finds out if the click on a window must be used to focus or raise it. */
1428 static void __handle_focus_raise_click(
1429 hfrc_ret_t
*ret_args
, const exec_context_t
*exc
)
1431 memset(ret_args
, 0, sizeof(*ret_args
));
1432 if (exc
->w
.fw
== NULL
)
1436 /* check for proper click button and modifiers*/
1437 if (FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc
->w
.fw
)) != 0 &&
1438 !(FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc
->w
.fw
)) &
1439 (1 << (exc
->x
.etrigger
->xbutton
.button
- 1))))
1441 /* wrong button, handle click normally */
1444 else if (FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc
->w
.fw
)) !=
1445 FPOL_ANY_MODIFIER
&&
1447 FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc
->w
.fw
))) !=
1448 MaskUsedModifiers(exc
->x
.etrigger
->xbutton
.state
))
1450 /* right button but wrong modifiers, handle click normally */
1455 __check_click_to_focus_or_raise(ret_args
, exc
);
1461 /* Helper function for HandleButtonPress */
1462 static Bool
__is_bpress_window_handled(const exec_context_t
*exc
)
1465 const XEvent
*te
= exc
->x
.etrigger
;
1467 if (exc
->w
.fw
== NULL
)
1469 if ((te
->xbutton
.window
!= Scr
.Root
||
1470 te
->xbutton
.subwindow
!= None
) &&
1471 !is_pan_frame(te
->xbutton
.window
))
1473 /* Ignore events in unmanaged windows or subwindows of
1482 eventw
= (te
->xbutton
.subwindow
!= None
&&
1483 te
->xany
.window
!= FW_W(exc
->w
.fw
)) ?
1484 te
->xbutton
.subwindow
: te
->xany
.window
;
1485 if (is_frame_hide_window(eventw
) || eventw
== FW_W_FRAME(exc
->w
.fw
))
1490 dpy
, eventw
, &JunkRoot
, &JunkX
, &JunkY
,
1491 (unsigned int*)&JunkWidth
, (unsigned int*)&JunkHeight
,
1492 (unsigned int*)&JunkBW
, (unsigned int*)&JunkDepth
))
1494 /* The window has already died. */
1501 /* Helper function for __handle_bpress_on_managed */
1502 static Bool
__handle_click_to_focus(const exec_context_t
*exc
)
1504 fpol_set_focus_by_t set_by
;
1506 switch (exc
->w
.wcontext
)
1509 case C_EWMH_DESKTOP
:
1510 set_by
= FOCUS_SET_BY_CLICK_CLIENT
;
1513 set_by
= FOCUS_SET_BY_CLICK_ICON
;
1516 set_by
= FOCUS_SET_BY_CLICK_DECOR
;
1519 SetFocusWindow(exc
->w
.fw
, True
, set_by
);
1520 focus_grab_buttons(exc
->w
.fw
);
1521 if (focus_is_focused(exc
->w
.fw
) && !IS_ICONIFIED(exc
->w
.fw
))
1523 border_draw_decorations(
1524 exc
->w
.fw
, PART_ALL
, True
, True
, CLEAR_ALL
, NULL
,
1528 return focus_is_focused(exc
->w
.fw
);
1531 /* Helper function for __handle_bpress_on_managed */
1532 static Bool
__handle_click_to_raise(const exec_context_t
*exc
)
1537 is_focused
= focus_is_focused(exc
->w
.fw
);
1538 if (focus_query_click_to_raise(exc
->w
.fw
, is_focused
, True
))
1546 /* Helper function for HandleButtonPress */
1547 static void __handle_bpress_stroke(void)
1549 STROKE_CODE(stroke_init());
1550 STROKE_CODE(send_motion
= True
);
1555 /* Helper function for __handle_bpress_on_managed */
1556 static Bool
__handle_bpress_action(
1557 const exec_context_t
*exc
, char *action
)
1563 if (!action
|| *action
== 0)
1568 /* draw pressed in decorations */
1569 part
= border_context_to_parts(exc
->w
.wcontext
);
1570 do_force
= (part
& PART_TITLEBAR
) ? True
: False
;
1571 border_draw_decorations(
1572 exc
->w
.fw
, part
, (Scr
.Hilite
== exc
->w
.fw
), do_force
,
1573 CLEAR_ALL
, NULL
, NULL
);
1574 /* execute the action */
1575 if (IS_ICONIFIED(exc
->w
.fw
))
1577 /* release the pointer since it can't do harm over an icon */
1578 XAllowEvents(dpy
, AsyncPointer
, CurrentTime
);
1580 execute_function(NULL
, exc
, action
, 0);
1581 if (exc
->w
.wcontext
!= C_WINDOW
&& exc
->w
.wcontext
!= C_NO_CONTEXT
)
1583 WaitForButtonsUp(True
);
1586 /* redraw decorations */
1588 if (check_if_fvwm_window_exists(exc
->w
.fw
))
1590 part
= border_context_to_parts(exc
->w
.wcontext
);
1591 do_force
= (part
& PART_TITLEBAR
) ? True
: False
;
1592 border_draw_decorations(
1593 exc
->w
.fw
, part
, (Scr
.Hilite
== exc
->w
.fw
), do_force
,
1594 CLEAR_ALL
, NULL
, NULL
);
1600 /* Handles button presses on the root window. */
1601 static void __handle_bpress_on_root(const exec_context_t
*exc
)
1606 __handle_bpress_stroke();
1607 /* search for an appropriate mouse binding */
1608 action
= CheckBinding(
1609 Scr
.AllBindings
, STROKE_ARG(0) exc
->x
.etrigger
->xbutton
.button
,
1610 exc
->x
.etrigger
->xbutton
.state
, GetUnusedModifiers(), C_ROOT
,
1611 BIND_BUTTONPRESS
, NULL
, NULL
);
1612 if (action
&& *action
)
1614 const exec_context_t
*exc2
;
1615 exec_context_changes_t ecc
;
1617 ecc
.w
.wcontext
= C_ROOT
;
1618 exc2
= exc_clone_context(exc
, &ecc
, ECC_WCONTEXT
);
1619 execute_function(NULL
, exc2
, action
, 0);
1620 exc_destroy_context(exc2
);
1621 WaitForButtonsUp(True
);
1625 /* do gnome buttonpress forwarding if win == root */
1626 GNOME_ProxyButtonEvent(exc
->x
.etrigger
);
1632 /* Handles button presses on unmanaged windows */
1633 static void __handle_bpress_on_unmanaged(const exec_context_t
*exc
)
1635 /* Pass the event to the application. */
1636 XAllowEvents(dpy
, ReplayPointer
, CurrentTime
);
1642 /* Handles button presses on managed windows */
1643 static void __handle_bpress_on_managed(const exec_context_t
*exc
)
1647 FvwmWindow
* const fw
= exc
->w
.fw
;
1650 e
= exc
->x
.etrigger
;
1651 /* Now handle click to focus and click to raise. */
1652 __handle_focus_raise_click(&f
, exc
);
1653 PressedW
= (f
.do_forbid_function
) ? None
: exc
->w
.w
;
1656 if (!__handle_click_to_focus(exc
))
1658 /* Window didn't accept the focus; pass the click to
1659 * the application. */
1660 f
.do_swallow_click
= 0;
1665 if (__handle_click_to_raise(exc
) == True
)
1667 /* We can't raise the window immediately because the
1668 * action bound to the click might be "Lower" or
1669 * "RaiseLower". So mark the window as scheduled to be
1670 * raised after the binding is executed. Functions that
1671 * modify the stacking order will reset this flag. */
1672 SET_SCHEDULED_FOR_RAISE(fw
, 1);
1675 /* handle bindings */
1676 if (!f
.do_forbid_function
)
1678 /* stroke bindings */
1679 __handle_bpress_stroke();
1680 /* mouse bindings */
1681 action
= CheckBinding(
1682 Scr
.AllBindings
, STROKE_ARG(0) e
->xbutton
.button
,
1683 e
->xbutton
.state
, GetUnusedModifiers(),
1684 exc
->w
.wcontext
, BIND_BUTTONPRESS
, &fw
->class,
1686 if (__handle_bpress_action(exc
, action
))
1688 f
.do_swallow_click
= 1;
1691 /* raise the window */
1692 if (IS_SCHEDULED_FOR_RAISE(fw
))
1694 /* Now that we know the action did not restack the window we
1696 * dv (10-Aug-2002): We can safely raise the window after
1697 * redrawing it since all the decorations are drawn in the
1698 * window background and no Expose event is generated. */
1699 RaiseWindow(fw
, False
);
1700 SET_SCHEDULED_FOR_RAISE(fw
, 0);
1703 if (!f
.do_swallow_click
)
1705 /* pass the click to the application */
1706 XAllowEvents(dpy
, ReplayPointer
, CurrentTime
);
1709 else if (f
.do_focus
|| f
.do_raise
)
1711 WaitForButtonsUp(True
);
1717 /* restore focus stolen by unmanaged */
1718 static void __refocus_stolen_focus_win(const evh_args_t
*ea
)
1720 FOCUS_SET(Scr
.StolenFocusWin
);
1721 ea
->exc
->x
.etrigger
->xfocus
.window
= Scr
.StolenFocusWin
;
1722 ea
->exc
->x
.etrigger
->type
= FocusIn
;
1723 Scr
.UnknownWinFocused
= None
;
1724 Scr
.StolenFocusWin
= None
;
1725 dispatch_event(ea
->exc
->x
.etrigger
);
1730 /* ---------------------------- event handlers ----------------------------- */
1732 void HandleButtonPress(const evh_args_t
*ea
)
1734 DBUG("HandleButtonPress", "Routine Entered");
1736 GrabEm(CRS_NONE
, GRAB_PASSIVE
);
1737 if (__is_bpress_window_handled(ea
->exc
) == False
)
1739 __handle_bpress_on_unmanaged(ea
->exc
);
1741 else if (ea
->exc
->w
.fw
!= NULL
)
1743 __handle_bpress_on_managed(ea
->exc
);
1747 __handle_bpress_on_root(ea
->exc
);
1749 UngrabEm(GRAB_PASSIVE
);
1755 void HandleButtonRelease(const evh_args_t
*ea
)
1760 const XEvent
*te
= ea
->exc
->x
.etrigger
;
1763 DBUG("HandleButtonRelease", "Routine Entered");
1764 send_motion
= False
;
1765 stroke_trans (sequence
);
1766 DBUG("HandleButtonRelease",sequence
);
1767 /* Allows modifier to work (Only R context works here). */
1768 real_modifier
= te
->xbutton
.state
- (1 << (7 + te
->xbutton
.button
));
1769 if (ea
->exc
->w
.fw
== NULL
)
1776 class = &ea
->exc
->w
.fw
->class;
1777 name
= ea
->exc
->w
.fw
->name
.name
;
1779 /* need to search for an appropriate stroke binding */
1780 action
= CheckBinding(
1781 Scr
.AllBindings
, sequence
, te
->xbutton
.button
, real_modifier
,
1782 GetUnusedModifiers(), ea
->exc
->w
.wcontext
, BIND_STROKE
,
1784 /* got a match, now process it */
1785 if (action
!= NULL
&& (action
[0] != 0))
1787 execute_function(NULL
, ea
->exc
, action
, 0);
1788 WaitForButtonsUp(True
);
1793 * do gnome buttonpress forwarding if win == root
1795 if (Scr
.Root
== te
->xany
.window
)
1797 GNOME_ProxyButtonEvent(te
);
1803 #endif /* HAVE_STROKE */
1805 void HandleClientMessage(const evh_args_t
*ea
)
1807 const XEvent
*te
= ea
->exc
->x
.etrigger
;
1808 FvwmWindow
* const fw
= ea
->exc
->w
.fw
;
1810 DBUG("HandleClientMessage", "Routine Entered");
1812 /* Process GNOME and EWMH Messages */
1813 if (GNOME_ProcessClientMessage(ea
->exc
))
1817 else if (EWMH_ProcessClientMessage(ea
->exc
))
1822 /* handle deletion of tear out menus */
1823 if (fw
&& IS_TEAR_OFF_MENU(fw
) && te
->xclient
.format
== 32 &&
1824 te
->xclient
.data
.l
[0] == _XA_WM_DELETE_WINDOW
)
1826 menu_close_tear_off_menu(fw
);
1830 if (te
->xclient
.message_type
== _XA_WM_CHANGE_STATE
&&
1831 fw
&& te
->xclient
.data
.l
[0] == IconicState
&& !IS_ICONIFIED(fw
))
1833 const exec_context_t
*exc
;
1834 exec_context_changes_t ecc
;
1836 ecc
.w
.wcontext
= C_WINDOW
;
1837 exc
= exc_clone_context(ea
->exc
, &ecc
, ECC_WCONTEXT
);
1838 execute_function(NULL
, exc
, "Iconify", 0);
1839 exc_destroy_context(exc
);
1843 /* FIXME: Is this safe enough ? I guess if clients behave
1844 * according to ICCCM and send these messages only if they
1845 * grabbed the pointer, it is OK */
1847 extern Atom _XA_WM_COLORMAP_NOTIFY
;
1848 if (te
->xclient
.message_type
== _XA_WM_COLORMAP_NOTIFY
)
1850 set_client_controls_colormaps(te
->xclient
.data
.l
[1]);
1855 /* CKH - if we get here, it was an unknown client message, so send
1856 * it to the client if it was in a window we know about. I'm not so
1857 * sure this should be done or not, since every other window manager
1858 * I've looked at doesn't. But it might be handy for a free drag and
1859 * drop setup being developed for Linux. */
1860 /* TA: 20091231 - But this confuses QT Drag and Drop since it handles
1861 * processing XSendEvents in an odd order. For now, workaround this
1862 * by using a BugOpts option.
1866 if ((!Scr
.bo
.do_enable_qt_drag_n_drop_workaround
) &&
1867 (te
->xclient
.window
!= FW_W(fw
)))
1872 e
.xclient
.window
= FW_W(fw
);
1873 FSendEvent(dpy
, FW_W(fw
), False
, NoEventMask
, &e
);
1878 void HandleColormapNotify(const evh_args_t
*ea
)
1880 colormap_handle_colormap_notify(ea
);
1885 void HandleConfigureRequest(const evh_args_t
*ea
)
1887 const XEvent
*te
= ea
->exc
->x
.etrigger
;
1888 XConfigureRequestEvent cre
;
1889 FvwmWindow
*fw
= ea
->exc
->w
.fw
;
1891 DBUG("HandleConfigureRequest", "Routine Entered");
1893 cre
= te
->xconfigurerequest
;
1894 /* te->xany.window is te->.xconfigurerequest.parent, so the context
1895 * window may be wrong. */
1896 if (XFindContext(dpy
, cre
.window
, FvwmContext
, (caddr_t
*)&fw
) ==
1901 __handle_configure_request(cre
, ea
, fw
, False
, ForgetGravity
);
1906 void HandleDestroyNotify(const evh_args_t
*ea
)
1908 DBUG("HandleDestroyNotify", "Routine Entered");
1910 destroy_window(ea
->exc
->w
.fw
);
1911 EWMH_ManageKdeSysTray(
1912 ea
->exc
->x
.etrigger
->xdestroywindow
.window
,
1913 ea
->exc
->x
.etrigger
->type
);
1914 EWMH_WindowDestroyed();
1915 GNOME_SetClientList();
1920 #define DEBUG_ENTERNOTIFY 0
1921 #if DEBUG_ENTERNOTIFY
1922 static int ecount
=0;
1923 #define ENTER_DBG(x) fprintf x;
1925 #define ENTER_DBG(x)
1927 void HandleEnterNotify(const evh_args_t
*ea
)
1929 const XEnterWindowEvent
*ewp
;
1932 static Bool is_initial_ungrab_pending
= True
;
1933 Bool is_tear_off_menu
;
1934 const XEvent
*te
= ea
->exc
->x
.etrigger
;
1935 FvwmWindow
* const fw
= ea
->exc
->w
.fw
;
1937 DBUG("HandleEnterNotify", "Routine Entered");
1938 ewp
= &te
->xcrossing
;
1939 ENTER_DBG((stderr
, "++++++++ en (%d): fw 0x%08x w 0x%08x sw 0x%08xmode 0x%x detail 0x%x '%s'\n", ++ecount
, (int)fw
, (int)ewp
->window
, (int)ewp
->subwindow
, ewp
->mode
, ewp
->detail
, fw
?fw
->visible_name
:"(none)"));
1942 ewp
->window
== Scr
.Root
&&
1943 ewp
->detail
== NotifyInferior
&& ewp
->mode
== NotifyNormal
)
1945 /* pointer left subwindow */
1947 MX_ENTER_WINDOW
, 3, (long)Scr
.Root
, (long)NULL
,
1951 ewp
->window
== Scr
.Root
&&
1952 ewp
->detail
== NotifyNonlinearVirtual
)
1954 /* pointer entered screen */
1956 MX_ENTER_WINDOW
, 3, (long)Scr
.Root
, (long)NULL
,
1959 if (Scr
.ColormapFocus
== COLORMAP_FOLLOWS_MOUSE
)
1961 if (fw
&& !IS_ICONIFIED(fw
) && ewp
->window
== FW_W(fw
))
1963 InstallWindowColormaps(fw
);
1967 /* make sure its for one of our windows */
1968 /* handle a subwindow cmap */
1969 InstallWindowColormaps(NULL
);
1974 EnterSubWindowColormap(ewp
->window
);
1976 if (Scr
.flags
.is_wire_frame_displayed
)
1978 ENTER_DBG((stderr
, "en: exit: iwfd\n"));
1979 /* Ignore EnterNotify events while a window is resized or moved
1980 * as a wire frame; otherwise the window list may be screwed
1986 if (ewp
->window
!= FW_W_FRAME(fw
) &&
1987 ewp
->window
!= FW_W_PARENT(fw
) &&
1988 ewp
->window
!= FW_W(fw
) &&
1989 ewp
->window
!= FW_W_ICON_TITLE(fw
) &&
1990 ewp
->window
!= FW_W_ICON_PIXMAP(fw
))
1992 /* Ignore EnterNotify that received by any of the sub
1993 * windows that don't handle this event. unclutter
1994 * triggers these events sometimes, re focusing an
1995 * unfocused window under the pointer */
1996 ENTER_DBG((stderr
, "en: exit: funny window\n"));
2000 if (Scr
.focus_in_pending_window
!= NULL
)
2002 ENTER_DBG((stderr
, "en: exit: fipw\n"));
2003 /* Ignore EnterNotify event while we are waiting for a window to
2004 * receive focus via Focus or FlipFocus commands. */
2005 focus_grab_buttons(fw
);
2008 if (ewp
->mode
== NotifyGrab
)
2010 ENTER_DBG((stderr
, "en: exit: NotifyGrab\n"));
2013 else if (ewp
->mode
== NotifyNormal
)
2015 ENTER_DBG((stderr
, "en: NotifyNormal\n"));
2016 if (ewp
->detail
== NotifyNonlinearVirtual
&&
2017 ewp
->focus
== False
&& ewp
->subwindow
!= None
)
2019 /* This takes care of some buggy apps that forget that
2020 * one of their dialog subwindows has the focus after
2021 * popping up a selection list several times (ddd,
2022 * netscape). I'm not convinced that this does not
2023 * break something else. */
2024 ENTER_DBG((stderr
, "en: NN: refreshing focus\n"));
2028 else if (ewp
->mode
== NotifyUngrab
)
2030 ENTER_DBG((stderr
, "en: NotifyUngrab\n"));
2031 /* Ignore events generated by grabbing or ungrabbing the
2032 * pointer. However, there is no way to prevent the client
2033 * application from handling this event and, for example,
2034 * grabbing the focus. This will interfere with functions that
2035 * transferred the focus to a different window. */
2036 if (is_initial_ungrab_pending
)
2038 ENTER_DBG((stderr
, "en: NU: initial ungrab pending (lgw = NULL)\n"));
2039 is_initial_ungrab_pending
= False
;
2040 xcrossing_last_grab_window
= NULL
;
2044 if (ewp
->detail
== NotifyNonlinearVirtual
&&
2045 ewp
->focus
== False
&& ewp
->subwindow
!= None
)
2047 /* see comment above */
2048 ENTER_DBG((stderr
, "en: NU: refreshing focus\n"));
2051 if (fw
&& fw
== xcrossing_last_grab_window
)
2053 ENTER_DBG((stderr
, "en: exit: NU: is last grab window\n"));
2054 if (ewp
->window
== FW_W_FRAME(fw
) ||
2055 ewp
->window
== FW_W_ICON_TITLE(fw
) ||
2056 ewp
->window
== FW_W_ICON_PIXMAP(fw
))
2058 ENTER_DBG((stderr
, "en: exit: NU: last grab window = NULL\n"));
2059 xcrossing_last_grab_window
= NULL
;
2061 focus_grab_buttons(fw
);
2067 if (ewp
->window
!= FW_W_FRAME(fw
) &&
2068 ewp
->window
!= FW_W_ICON_TITLE(fw
) &&
2069 ewp
->window
!= FW_W_ICON_PIXMAP(fw
))
2071 ENTER_DBG((stderr
, "en: exit: NU: not frame window\n"));
2072 focus_grab_buttons(fw
);
2080 is_initial_ungrab_pending
= False
;
2083 /* look for a matching leaveNotify which would nullify this EnterNotify
2086 * RBW - if we're in startup, this is a coerced focus, so we don't
2087 * want to save the event time, or exit prematurely.
2089 * Ignore LeaveNotify events for tear out menus - handled by menu code
2092 (fw
&& IS_TEAR_OFF_MENU(fw
) && ewp
->window
== FW_W(fw
));
2093 if (!fFvwmInStartup
&& !is_tear_off_menu
&&
2094 FCheckTypedWindowEvent(dpy
, ewp
->window
, LeaveNotify
, &d
))
2096 if (d
.xcrossing
.mode
== NotifyNormal
&&
2097 d
.xcrossing
.detail
!= NotifyInferior
)
2099 ENTER_DBG((stderr
, "en: exit: found LeaveNotify\n"));
2104 if (ewp
->window
== Scr
.Root
)
2106 FvwmWindow
*lf
= get_last_screen_focus_window();
2108 if (!Scr
.flags
.is_pointer_on_this_screen
)
2110 Scr
.flags
.is_pointer_on_this_screen
= 1;
2111 if (lf
&& lf
!= &Scr
.FvwmRoot
&&
2112 !FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(lf
)))
2114 SetFocusWindow(lf
, True
, FOCUS_SET_FORCE
);
2116 else if (lf
!= &Scr
.FvwmRoot
)
2122 /* This was the first EnterNotify event for the
2123 * root window - ignore */
2125 set_last_screen_focus_window(NULL
);
2127 else if (!(sf
= get_focus_window()) ||
2128 FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf
)))
2133 Scr
.UnknownWinFocused
!= None
&& sf
!= NULL
&&
2134 FW_W(sf
) == Scr
.StolenFocusWin
)
2136 __refocus_stolen_focus_win(ea
);
2138 if (Scr
.ColormapFocus
== COLORMAP_FOLLOWS_MOUSE
)
2140 InstallWindowColormaps(NULL
);
2142 focus_grab_buttons(lf
);
2147 Scr
.flags
.is_pointer_on_this_screen
= 1;
2150 /* An EnterEvent in one of the PanFrameWindows activates the Paging or
2152 if (is_pan_frame(ewp
->window
))
2154 char *edge_command
= NULL
;
2157 Scr
.UnknownWinFocused
!= None
&&
2158 (sf
= get_focus_window()) != NULL
&&
2159 FW_W(sf
) == Scr
.StolenFocusWin
)
2161 __refocus_stolen_focus_win(ea
);
2163 /* check for edge commands */
2164 if (ewp
->window
== Scr
.PanFrameTop
.win
)
2166 edge_command
= Scr
.PanFrameTop
.command
;
2168 else if (ewp
->window
== Scr
.PanFrameBottom
.win
)
2170 edge_command
= Scr
.PanFrameBottom
.command
;
2172 else if (ewp
->window
== Scr
.PanFrameLeft
.win
)
2174 edge_command
= Scr
.PanFrameLeft
.command
;
2176 else if (ewp
->window
== Scr
.PanFrameRight
.win
)
2178 edge_command
= Scr
.PanFrameRight
.command
;
2180 if (edge_command
&& ewp
->mode
== NotifyUngrab
&&
2181 ewp
->detail
== NotifyAncestor
)
2185 else if (edge_command
)
2187 execute_function(NULL
, ea
->exc
, edge_command
, 0);
2191 /* no edge command for this pan frame - so we do
2197 /* this was in the HandleMotionNotify before, HEDU */
2198 Scr
.flags
.is_pointer_on_this_screen
= 1;
2201 &e
, Scr
.EdgeScrollX
, Scr
.EdgeScrollY
, &JunkX
,
2202 &JunkY
, &delta_x
, &delta_y
, True
, True
, False
,
2211 if (IS_EWMH_DESKTOP(FW_W(fw
)))
2214 MX_ENTER_WINDOW
, 3, (long)Scr
.Root
, (long)NULL
,
2218 if (ewp
->window
== FW_W_FRAME(fw
) ||
2219 ewp
->window
== FW_W_ICON_TITLE(fw
) ||
2220 ewp
->window
== FW_W_ICON_PIXMAP(fw
))
2223 MX_ENTER_WINDOW
, 3, (long)FW_W(fw
),
2224 (long)FW_W_FRAME(fw
), (unsigned long)fw
);
2226 sf
= get_focus_window();
2227 if (sf
&& fw
!= sf
&& FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf
)))
2229 ENTER_DBG((stderr
, "en: delete focus\n"));
2232 focus_grab_buttons(fw
);
2233 if (FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw
)))
2235 ENTER_DBG((stderr
, "en: set mousey focus\n"));
2236 if (ewp
->window
== FW_W(fw
))
2238 /* Event is for the client window...*/
2239 #ifndef EXPERIMENTAL_ROU_HANDLING_V2
2240 /* RBW -- This may still be needed at times, I'm not
2242 SetFocusWindowClientEntered(
2243 fw
, True
, FOCUS_SET_BY_ENTER
);
2245 SetFocusWindow(fw
, True
, FOCUS_SET_BY_ENTER
);
2250 /* Event is for the frame...*/
2251 SetFocusWindow(fw
, True
, FOCUS_SET_BY_ENTER
);
2254 else if (focus_is_focused(fw
) && focus_does_accept_input_focus(fw
))
2256 /* We have to refresh the focus window here in case we left the
2257 * focused fvwm window. Motif apps may lose the input focus
2258 * otherwise. But do not try to refresh the focus of
2259 * applications that want to handle it themselves. */
2260 focus_force_refresh_focus(fw
);
2264 /* Give the window a chance to grab the buttons needed for
2266 focus_grab_buttons(sf
);
2269 Scr
.UnknownWinFocused
!= None
&& sf
!= NULL
&&
2270 FW_W(sf
) == Scr
.StolenFocusWin
)
2272 __refocus_stolen_focus_win(ea
);
2274 /* We get an EnterNotify with mode == UnGrab when fvwm releases the
2275 * grab held during iconification. We have to ignore this, or icon
2276 * title will be initially raised. */
2277 if (IS_ICONIFIED(fw
) && (ewp
->mode
== NotifyNormal
) &&
2278 (ewp
->window
== FW_W_ICON_PIXMAP(fw
) ||
2279 ewp
->window
== FW_W_ICON_TITLE(fw
)) &&
2280 FW_W_ICON_PIXMAP(fw
) != None
)
2282 SET_ICON_ENTERED(fw
, 1);
2283 DrawIconWindow(fw
, True
, False
, False
, False
, NULL
);
2285 /* Check for tear off menus */
2286 if (is_tear_off_menu
)
2288 menu_enter_tear_off_menu(ea
->exc
);
2294 void HandleExpose(const evh_args_t
*ea
)
2297 FvwmWindow
* const fw
= ea
->exc
->w
.fw
;
2299 e
= *ea
->exc
->x
.etrigger
;
2301 /* This doesn't work well. Sometimes, the expose count is zero although
2302 * dozens of expose events are pending. This happens all the time
2303 * during a shading animation. Simply flush expose events
2304 * unconditionally. */
2305 if (e
.xexpose
.count
!= 0)
2307 flush_accumulate_expose(e
.xexpose
.window
, &e
);
2310 flush_accumulate_expose(e
.xexpose
.window
, &e
);
2316 if (e
.xany
.window
== FW_W_ICON_TITLE(fw
) ||
2317 e
.xany
.window
== FW_W_ICON_PIXMAP(fw
))
2319 DrawIconWindow(fw
, True
, True
, False
, False
, &e
);
2322 else if (IS_TEAR_OFF_MENU(fw
) && e
.xany
.window
== FW_W(fw
))
2324 /* refresh the contents of the torn out menu */
2325 menu_expose(&e
, NULL
);
2331 void HandleFocusIn(const evh_args_t
*ea
)
2335 Window focus_w
= None
;
2336 Window focus_fw
= None
;
2339 FvwmWindow
*ffw_old
= get_focus_window();
2341 Bool do_force_broadcast
= False
;
2342 Bool is_unmanaged_focused
= False
;
2343 static Window last_focus_w
= None
;
2344 static Window last_focus_fw
= None
;
2345 static Bool was_nothing_ever_focused
= True
;
2346 FvwmWindow
*fw
= ea
->exc
->w
.fw
;
2348 DBUG("HandleFocusIn", "Routine Entered");
2350 Scr
.focus_in_pending_window
= NULL
;
2351 /* This is a hack to make the PointerKey command work */
2352 if (ea
->exc
->x
.etrigger
->xfocus
.detail
!= NotifyPointer
)
2355 w
= ea
->exc
->x
.etrigger
->xany
.window
;
2357 while (FCheckTypedEvent(dpy
, FocusIn
, &d
))
2360 if (d
.xfocus
.detail
!= NotifyPointer
)
2372 if (XFindContext(dpy
, w
, FvwmContext
, (caddr_t
*) &fw
) == XCNOENT
)
2377 Scr
.UnknownWinFocused
= None
;
2380 if (w
!= Scr
.NoFocusWin
)
2382 Scr
.UnknownWinFocused
= w
;
2383 Scr
.StolenFocusWin
=
2384 (ffw_old
!= NULL
) ? FW_W(ffw_old
) : None
;
2386 is_unmanaged_focused
= True
;
2388 /* Only show a non-focused window as focused,
2389 * if the focus is on unmanaged and flickering qt dialogs
2390 * workaround is on. */
2391 if (!Scr
.bo
.do_enable_flickering_qt_dialogs_workaround
||
2392 !is_unmanaged_focused
)
2394 border_draw_decorations(
2395 Scr
.Hilite
, PART_ALL
, False
, True
, CLEAR_ALL
,
2397 if (Scr
.ColormapFocus
== COLORMAP_FOLLOWS_FOCUS
)
2399 if ((Scr
.Hilite
)&&(!IS_ICONIFIED(Scr
.Hilite
)))
2401 InstallWindowColormaps(Scr
.Hilite
);
2405 InstallWindowColormaps(NULL
);
2409 /* Not very useful if no window that fvwm and its modules know
2410 * about has the focus. */
2411 fc
= GetColor(DEFAULT_FORE_COLOR
);
2412 bc
= GetColor(DEFAULT_BACK_COLOR
);
2414 else if (fw
!= Scr
.Hilite
||
2415 /* domivogt (16-May-2000): This check is necessary to force
2416 * sending a M_FOCUS_CHANGE packet after an unmanaged window
2417 * was focused. Otherwise fvwm would believe that Scr.Hilite
2418 * was still focused and not send any info to the modules. */
2419 last_focus_fw
== None
||
2420 IS_FOCUS_CHANGE_BROADCAST_PENDING(fw
) ||
2421 fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw
)))
2423 do_force_broadcast
= IS_FOCUS_CHANGE_BROADCAST_PENDING(fw
);
2424 SET_FOCUS_CHANGE_BROADCAST_PENDING(fw
, 0);
2425 if (fw
!= Scr
.Hilite
&&
2426 fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw
)))
2428 border_draw_decorations(
2429 fw
, PART_ALL
, True
, True
, CLEAR_ALL
, NULL
,
2433 focus_fw
= FW_W_FRAME(fw
);
2434 fc
= fw
->hicolors
.fore
;
2435 bc
= fw
->hicolors
.back
;
2436 set_focus_window(fw
);
2437 if (Scr
.ColormapFocus
== COLORMAP_FOLLOWS_FOCUS
)
2439 if ((Scr
.Hilite
)&&(!IS_ICONIFIED(Scr
.Hilite
)))
2441 InstallWindowColormaps(Scr
.Hilite
);
2445 InstallWindowColormaps(NULL
);
2453 if (was_nothing_ever_focused
|| last_focus_fw
== None
||
2454 focus_w
!= last_focus_w
|| focus_fw
!= last_focus_fw
||
2457 if (!Scr
.bo
.do_enable_flickering_qt_dialogs_workaround
||
2458 !is_unmanaged_focused
)
2461 M_FOCUS_CHANGE
, 5, (long)focus_w
,
2463 (unsigned long)IsLastFocusSetByMouse(),
2464 (long)fc
, (long)bc
);
2465 EWMH_SetActiveWindow(focus_w
);
2467 last_focus_w
= focus_w
;
2468 last_focus_fw
= focus_fw
;
2469 was_nothing_ever_focused
= False
;
2471 if ((sf
= get_focus_window()) != ffw_old
)
2473 focus_grab_buttons(sf
);
2474 focus_grab_buttons(ffw_old
);
2480 void HandleFocusOut(const evh_args_t
*ea
)
2482 if (Scr
.UnknownWinFocused
!= None
&& Scr
.StolenFocusWin
!= None
&&
2483 ea
->exc
->x
.etrigger
->xfocus
.window
== Scr
.UnknownWinFocused
)
2485 __refocus_stolen_focus_win(ea
);
2491 void __handle_key(const evh_args_t
*ea
, Bool is_press
)
2497 const XEvent
*te
= ea
->exc
->x
.etrigger
;
2498 const FvwmWindow
* const fw
= ea
->exc
->w
.fw
;
2499 Bool is_second_binding
;
2500 const XClassHint
*winClass1
, *winClass2
;
2502 char *name1
, *name2
;
2503 const exec_context_t
*exc
;
2504 exec_context_changes_t ecc
;
2508 /* Here's a real hack - some systems have two keys with the
2509 * same keysym and different keycodes. This converts all
2510 * the cases to one keycode. */
2511 kc
= XKeysymToKeycode(dpy
, XKeycodeToKeysym(dpy
, te
->xkey
.keycode
, 0));
2513 /* Check if there is something bound to the key */
2515 sf
= get_focus_window();
2518 tmp
.res_name
= tmp
.res_class
= name1
= "root";
2524 winClass1
= &sf
->class;
2525 name1
= sf
->name
.name
;
2526 kcontext
= (sf
== fw
? ea
->exc
->w
.wcontext
: C_WINDOW
);
2531 tmp
.res_name
= tmp
.res_class
= name2
= "root";
2536 winClass2
= &fw
->class;
2537 name2
= fw
->name
.name
;
2539 /* Searching the binding list with a different 'type' value
2540 * (ie. BIND_KEYPRESS vs BIND_PKEYPRESS) doesn't make a difference.
2541 * The different context value does though. */
2542 action
= CheckTwoBindings(
2543 &is_second_binding
, Scr
.AllBindings
, STROKE_ARG(0) kc
,
2544 te
->xkey
.state
, GetUnusedModifiers(), kcontext
, BIND_KEYPRESS
,
2545 winClass1
, name1
, ea
->exc
->w
.wcontext
, BIND_PKEYPRESS
,
2552 XAllowEvents(dpy
, AsyncKeyboard
, CurrentTime
);
2556 if (is_second_binding
== False
)
2559 ecc
.w
.wcontext
= kcontext
;
2560 exc
= exc_clone_context(
2561 ea
->exc
, &ecc
, ECC_FW
| ECC_WCONTEXT
);
2563 execute_function(NULL
, exc
, action
, 0);
2564 if (is_second_binding
== False
)
2566 exc_destroy_context(exc
);
2568 XAllowEvents(dpy
, AsyncKeyboard
, CurrentTime
);
2572 /* if we get here, no function key was bound to the key. Send it
2573 * to the client if it was in a window we know about. */
2574 sf
= get_focus_window();
2575 if (sf
&& te
->xkey
.window
!= FW_W(sf
))
2580 e
.xkey
.window
= FW_W(sf
);
2582 dpy
, e
.xkey
.window
, False
,
2583 (is_press
)? KeyPressMask
:KeyReleaseMask
, &e
);
2585 else if (fw
&& te
->xkey
.window
!= FW_W(fw
))
2590 e
.xkey
.window
= FW_W(fw
);
2592 dpy
, e
.xkey
.window
, False
,
2593 (is_press
)? KeyPressMask
:KeyReleaseMask
, &e
);
2595 XAllowEvents(dpy
, AsyncKeyboard
, CurrentTime
);
2600 void HandleKeyPress(const evh_args_t
*ea
)
2602 __handle_key(ea
, True
);
2605 void HandleKeyRelease(const evh_args_t
*ea
)
2607 __handle_key(ea
, False
);
2610 void HandleLeaveNotify(const evh_args_t
*ea
)
2612 const XLeaveWindowEvent
*lwp
;
2613 const XEvent
*te
= ea
->exc
->x
.etrigger
;
2614 FvwmWindow
* const fw
= ea
->exc
->w
.fw
;
2616 DBUG("HandleLeaveNotify", "Routine Entered");
2618 ENTER_DBG((stderr
, "-------- ln (%d): fw 0x%08x w 0x%08x sw 0x%08x mode 0x%x detail 0x%x '%s'\n", ++ecount
, (int)fw
, (int)te
->xcrossing
.window
, (int)te
->xcrossing
.subwindow
, te
->xcrossing
.mode
, te
->xcrossing
.detail
, fw
?fw
->visible_name
:"(none)"));
2619 lwp
= &te
->xcrossing
;
2621 lwp
->window
== Scr
.Root
&&
2622 lwp
->detail
== NotifyInferior
&& lwp
->mode
== NotifyNormal
)
2624 /* pointer entered subwindow */
2626 MX_LEAVE_WINDOW
, 3, (long)Scr
.Root
, (long)NULL
,
2630 lwp
->window
== Scr
.Root
&&
2631 lwp
->detail
== NotifyNonlinearVirtual
)
2633 /* pointer left screen */
2635 MX_LEAVE_WINDOW
, 3, (long)Scr
.Root
, (long)NULL
,
2638 /* Ignore LeaveNotify events while a window is resized or moved as a
2639 * wire frame; otherwise the window list may be screwed up. */
2640 if (Scr
.flags
.is_wire_frame_displayed
)
2644 if (lwp
->mode
!= NotifyNormal
)
2646 /* Ignore events generated by grabbing or ungrabbing the
2647 * pointer. However, there is no way to prevent the client
2648 * application from handling this event and, for example,
2649 * grabbing the focus. This will interfere with functions that
2650 * transferred the focus to a different window. It is
2651 * necessary to check for LeaveNotify events on the client
2652 * window too in case buttons are not grabbed on it. */
2653 if (lwp
->mode
== NotifyGrab
&& fw
&&
2654 (lwp
->window
== FW_W_FRAME(fw
) ||
2655 lwp
->window
== FW_W(fw
) ||
2656 lwp
->window
== FW_W_ICON_TITLE(fw
) ||
2657 lwp
->window
== FW_W_ICON_PIXMAP(fw
)))
2659 ENTER_DBG((stderr
, "ln: *** lgw = 0x%08x\n", (int)fw
));
2660 xcrossing_last_grab_window
= fw
;
2662 #ifdef FOCUS_EXPANDS_TITLE
2663 if (fw
&& IS_ICONIFIED(fw
))
2665 SET_ICON_ENTERED(fw
, 0);
2667 fw
, True
, False
, False
, False
, NULL
);
2672 /* CDE-like behaviour of raising the icon title if the icon
2673 gets the focus (in particular if the cursor is over the icon) */
2674 if (fw
&& IS_ICONIFIED(fw
))
2676 SET_ICON_ENTERED(fw
,0);
2677 DrawIconWindow(fw
, True
, False
, False
, False
, NULL
);
2680 /* An LeaveEvent in one of the PanFrameWindows activates
2681 an EdgeLeaveCommand. */
2682 if (is_pan_frame(lwp
->window
))
2684 char *edge_command_leave
= NULL
;
2686 /* check for edge commands */
2687 if (lwp
->window
== Scr
.PanFrameTop
.win
)
2689 edge_command_leave
= Scr
.PanFrameTop
.command_leave
;
2691 else if (lwp
->window
== Scr
.PanFrameBottom
.win
)
2693 edge_command_leave
= Scr
.PanFrameBottom
.command_leave
;
2695 else if (lwp
->window
== Scr
.PanFrameLeft
.win
)
2697 edge_command_leave
= Scr
.PanFrameLeft
.command_leave
;
2699 else if (lwp
->window
== Scr
.PanFrameRight
.win
)
2701 edge_command_leave
= Scr
.PanFrameRight
.command_leave
;
2703 if (edge_command_leave
&& lwp
->mode
== NotifyUngrab
&&
2704 lwp
->detail
== NotifyAncestor
)
2708 else if (edge_command_leave
)
2710 execute_function(NULL
, ea
->exc
, edge_command_leave
, 0);
2715 /* If we leave the root window, then we're really moving
2716 * another screen on a multiple screen display, and we
2717 * need to de-focus and unhighlight to make sure that we
2718 * don't end up with more than one highlighted window at a time */
2719 if (lwp
->window
== Scr
.Root
&&
2720 /* domivogt (16-May-2000): added this test because somehow fvwm
2721 * sometimes gets a LeaveNotify on the root window although it is
2723 Scr
.NumberOfScreens
> 1)
2725 if (lwp
->mode
== NotifyNormal
)
2727 if (lwp
->detail
!= NotifyInferior
)
2729 FvwmWindow
*sf
= get_focus_window();
2731 Scr
.flags
.is_pointer_on_this_screen
= 0;
2732 set_last_screen_focus_window(sf
);
2737 if (Scr
.Hilite
!= NULL
)
2739 border_draw_decorations(
2740 Scr
.Hilite
, PART_ALL
, False
,
2741 True
, CLEAR_ALL
, NULL
, NULL
);
2748 /* handle a subwindow cmap */
2749 LeaveSubWindowColormap(te
->xany
.window
);
2752 (lwp
->window
== FW_W_FRAME(fw
) ||
2753 lwp
->window
== FW_W_ICON_TITLE(fw
) ||
2754 lwp
->window
== FW_W_ICON_PIXMAP(fw
)))
2757 MX_LEAVE_WINDOW
, 3, (long)FW_W(fw
),
2758 (long)FW_W_FRAME(fw
), (unsigned long)fw
);
2764 void HandleMapNotify(const evh_args_t
*ea
)
2766 Bool is_on_this_page
= False
;
2767 const XEvent
*te
= ea
->exc
->x
.etrigger
;
2768 FvwmWindow
* const fw
= ea
->exc
->w
.fw
;
2770 DBUG("HandleMapNotify", "Routine Entered");
2774 if (te
->xmap
.override_redirect
== True
&&
2775 te
->xmap
.window
!= Scr
.NoFocusWin
)
2777 XSelectInput(dpy
, te
->xmap
.window
, XEVMASK_ORW
);
2779 Scr
.UnknownWinFocused
= te
->xmap
.window
;
2783 if (te
->xmap
.window
== FW_W_FRAME(fw
))
2785 /* Now that we know the frame is mapped after capturing the
2786 * window we do not need StructureNotifyMask events anymore. */
2787 XSelectInput(dpy
, FW_W_FRAME(fw
), XEVMASK_FRAMEW
);
2790 /* Except for identifying over-ride redirect window mappings, we
2791 * don't need or want windows associated with the
2792 * SubstructureNotifyMask */
2793 if (te
->xmap
.event
!= te
->xmap
.window
)
2797 SET_MAP_PENDING(fw
, 0);
2798 /* don't map if the event was caused by a de-iconify */
2799 if (IS_ICONIFY_PENDING(fw
))
2804 /* Make sure at least part of window is on this page before giving it
2806 is_on_this_page
= IsRectangleOnThisPage(&(fw
->g
.frame
), fw
->Desk
);
2809 * Need to do the grab to avoid race condition of having server send
2810 * MapNotify to client before the frame gets mapped; this is bad because
2811 * the client would think that the window has a chance of being viewable
2812 * when it really isn't.
2814 MyXGrabServer (dpy
);
2815 if (FW_W_ICON_TITLE(fw
))
2817 XUnmapWindow(dpy
, FW_W_ICON_TITLE(fw
));
2819 if (FW_W_ICON_PIXMAP(fw
) != None
)
2821 XUnmapWindow(dpy
, FW_W_ICON_PIXMAP(fw
));
2823 XMapSubwindows(dpy
, FW_W_FRAME(fw
));
2824 if (fw
->Desk
== Scr
.CurrentDesk
)
2826 XMapWindow(dpy
, FW_W_FRAME(fw
));
2828 if (IS_ICONIFIED(fw
))
2831 M_DEICONIFY
, 3, (long)FW_W(fw
), (long)FW_W_FRAME(fw
),
2837 M_MAP
, 3, (long)FW_W(fw
), (long)FW_W_FRAME(fw
),
2841 if (is_on_this_page
&&
2842 focus_query_open_grab_focus(fw
, get_focus_window()) == True
)
2844 SetFocusWindow(fw
, True
, FOCUS_SET_FORCE
);
2846 border_draw_decorations(
2847 fw
, PART_ALL
, (fw
== get_focus_window()) ? True
: False
, True
,
2848 CLEAR_ALL
, NULL
, NULL
);
2849 MyXUngrabServer (dpy
);
2851 SET_ICONIFIED(fw
, 0);
2852 SET_ICON_UNMAPPED(fw
, 0);
2853 if (DO_ICONIFY_AFTER_MAP(fw
))
2855 initial_window_options_t win_opts
;
2857 /* finally, if iconification was requested before the window
2858 * was mapped, request it now. */
2859 memset(&win_opts
, 0, sizeof(win_opts
));
2860 Iconify(fw
, &win_opts
);
2861 SET_ICONIFY_AFTER_MAP(fw
, 0);
2863 focus_grab_buttons_on_layer(fw
->layer
);
2868 void HandleMappingNotify(const evh_args_t
*ea
)
2870 XRefreshKeyboardMapping(&ea
->exc
->x
.etrigger
->xmapping
);
2875 void HandleMapRequest(const evh_args_t
*ea
)
2877 DBUG("HandleMapRequest", "Routine Entered");
2881 /* Just map the damn thing, decorations are added later
2882 * in CaptureAllWindows. */
2883 XMapWindow(dpy
, ea
->exc
->x
.etrigger
->xmaprequest
.window
);
2886 HandleMapRequestKeepRaised(ea
, None
, NULL
, NULL
);
2891 void HandleMapRequestKeepRaised(
2892 const evh_args_t
*ea
, Window KeepRaised
, FvwmWindow
*ReuseWin
,
2893 initial_window_options_t
*win_opts
)
2895 Bool is_on_this_page
= False
;
2896 Bool is_new_window
= False
;
2899 initial_window_options_t win_opts_bak
;
2902 extern Bool Restarting
;
2903 const char *initial_map_command
;
2905 initial_map_command
= NULL
;
2906 if (win_opts
== NULL
)
2908 memset(&win_opts_bak
, 0, sizeof(win_opts_bak
));
2909 win_opts
= &win_opts_bak
;
2912 if (ReuseWin
== NULL
)
2916 pw
= ea
->exc
->x
.etrigger
->xmaprequest
.parent
;
2917 if (XFindContext(dpy
, ew
, FvwmContext
, (caddr_t
*)&fw
) ==
2922 if (fw
!= NULL
&& IS_MAP_PENDING(fw
))
2924 /* The window is already going to be mapped, no need to
2934 if (fw
== NULL
&& EWMH_IsKdeSysTrayWindow(ew
))
2936 /* This means that the window is swallowed by kicker and that
2937 * kicker restart or exit. As we should assume that kicker
2938 * restart we should return here, if not we go into trouble
2942 if (!win_opts
->flags
.do_override_ppos
)
2947 /* If the window has never been mapped before ... */
2948 if (!fw
|| (fw
&& DO_REUSE_DESTROYED(fw
)))
2950 check_if_event_args args
;
2954 args
.do_return_true
= True
;
2955 args
.do_return_true_cr
= False
;
2958 dpy
, &dummy
, test_withdraw_request
,
2960 /* The window is moved back to the WithdrawnState
2961 * immideately. Don't map it.
2963 * However, send make sure that a WM_STATE
2964 * PropertyNotify event is sent to the window.
2969 unsigned long nitems
, bytes_remain
;
2970 unsigned char *prop
;
2974 dpy
, ew
, _XA_WM_STATE
, 0L, 3L, False
,
2975 _XA_WM_STATE
, &atype
, &aformat
,
2976 &nitems
,&bytes_remain
,&prop
)
2982 XDeleteProperty(dpy
, ew
, _XA_WM_STATE
);
2987 ev
.type
= PropertyNotify
;
2990 ev
.atom
= _XA_WM_STATE
;
2991 ev
.time
= fev_get_evtime();
2992 ev
.state
= PropertyDelete
;
3003 /* Add decorations. */
3005 &initial_map_command
, ea
->exc
, ReuseWin
, win_opts
);
3006 if (fw
== AW_NO_WINDOW
)
3010 else if (fw
== AW_UNMANAGED
)
3012 XMapWindow(dpy
, ew
);
3015 is_new_window
= True
;
3018 * Make sure at least part of window is on this page
3019 * before giving it focus...
3021 is_on_this_page
= IsRectangleOnThisPage(&(fw
->g
.frame
), fw
->Desk
);
3022 if (KeepRaised
!= None
)
3024 XRaiseWindow(dpy
, KeepRaised
);
3026 /* If it's not merely iconified, and we have hints, use them. */
3028 if (IS_ICONIFIED(fw
))
3030 /* If no hints, or currently an icon, just "deiconify" */
3033 else if (IS_MAPPED(fw
))
3035 /* the window is already mapped - fake a MapNotify event */
3036 fake_map_unmap_notify(fw
, MapNotify
);
3042 if (fw
->wmhints
&& (fw
->wmhints
->flags
& StateHint
))
3044 state
= fw
->wmhints
->initial_state
;
3048 state
= NormalState
;
3050 if (win_opts
->initial_state
!= DontCareState
)
3052 state
= win_opts
->initial_state
;
3062 if (fw
->Desk
== Scr
.CurrentDesk
)
3066 SET_MAP_PENDING(fw
, 1);
3067 XMapWindow(dpy
, FW_W_FRAME(fw
));
3068 XMapWindow(dpy
, FW_W(fw
));
3069 SetMapStateProp(fw
, NormalState
);
3070 if (Scr
.flags
.is_map_desk_in_progress
)
3072 do_grab_focus
= False
;
3074 else if (!is_on_this_page
)
3076 do_grab_focus
= False
;
3078 else if (focus_query_open_grab_focus(
3079 fw
, get_focus_window()) ==
3082 do_grab_focus
= True
;
3086 do_grab_focus
= False
;
3091 fw
, True
, FOCUS_SET_FORCE
);
3095 /* make sure the old focused window
3096 * still has grabbed all necessary
3099 get_focus_window());
3104 #ifndef ICCCM2_UNMAP_WINDOW_PATCH
3105 /* nope, this is forbidden by the ICCCM2 */
3106 XMapWindow(dpy
, FW_W(fw
));
3107 SetMapStateProp(fw
, NormalState
);
3109 /* Since we will not get a MapNotify, set the
3110 * IS_MAPPED flag manually. */
3112 SetMapStateProp(fw
, IconicState
);
3113 /* fake that the window was mapped to allow
3114 * modules to swallow it */
3116 M_MAP
, 3, (long)FW_W(fw
),
3117 (long)FW_W_FRAME(fw
),
3121 /* TA: 20090125: We *have* to handle
3122 * InitialMapCommand here and not in AddWindow() to
3123 * allow for correct timings when the window is truly
3124 * mapped. (c.f. things like Iconify.)
3127 /* TA: 20091212: But only do this when we're *not*
3128 * restarting -- the window is still mapped, but gets
3129 * recaptured -- we don't want to trigger this event
3130 * again. Otherwise we end up toggling the state of
3131 * the window in situations where the
3132 * InitialMapCommand is Iconify or Maximize, for
3135 if ((initial_map_command
!= NULL
) &&
3136 (!Restarting
&& Scr
.flags
.are_windows_captured
))
3138 execute_function_override_window(
3140 (char *)initial_map_command
, 0, fw
);
3142 MyXUngrabServer(dpy
);
3148 /* the window will not be mapped - fake a
3149 * MapNotify and an UnmapNotify event. Can't
3150 * remember exactly why this is necessary, but
3151 * probably something w/ (de)iconify state
3153 fake_map_unmap_notify(fw
, MapNotify
);
3154 fake_map_unmap_notify(fw
, UnmapNotify
);
3156 if (win_opts
->flags
.is_iconified_by_parent
||
3157 ((tmp
= get_transientfor_fvwmwindow(fw
)) &&
3160 win_opts
->flags
.is_iconified_by_parent
= 0;
3161 SET_ICONIFIED_BY_PARENT(fw
, 1);
3163 if (USE_ICON_POSITION_HINT(fw
) && fw
->wmhints
&&
3164 (fw
->wmhints
->flags
& IconPositionHint
))
3166 win_opts
->default_icon_x
= fw
->wmhints
->icon_x
;
3167 win_opts
->default_icon_y
= fw
->wmhints
->icon_y
;
3169 Iconify(fw
, win_opts
);
3176 M_WINDOWSHADE
, 3, (long)FW_W(fw
), (long)FW_W_FRAME(fw
),
3179 /* If the newly mapped window overlaps the focused window, make sure
3180 * ClickToFocusRaises and MouseFocusClickRaises work again. */
3181 sf
= get_focus_window();
3184 focus_grab_buttons(sf
);
3186 if (win_opts
->flags
.is_menu
)
3189 SET_MAP_PENDING(fw
, 0);
3191 EWMH_SetClientList();
3192 EWMH_SetClientListStacking();
3193 GNOME_SetClientList();
3199 void HandleMotionNotify(const evh_args_t
*ea
)
3201 DBUG("HandleMotionNotify", "Routine Entered");
3203 if (send_motion
== True
)
3206 ea
->exc
->x
.etrigger
->xmotion
.x
,
3207 ea
->exc
->x
.etrigger
->xmotion
.y
);
3212 #endif /* HAVE_STROKE */
3214 void HandlePropertyNotify(const evh_args_t
*ea
)
3216 Bool OnThisPage
= False
;
3217 Bool has_icon_changed
= False
;
3218 Bool has_icon_pixmap_hint_changed
= False
;
3219 Bool has_icon_window_hint_changed
= False
;
3220 FlocaleNameString new_name
= { NoName
, NULL
};
3221 int old_wmhints_flags
;
3222 const XEvent
*te
= ea
->exc
->x
.etrigger
;
3223 char *urgency_action
= NULL
;
3224 FvwmWindow
* const fw
= ea
->exc
->w
.fw
;
3226 DBUG("HandlePropertyNotify", "Routine Entered");
3228 if (te
->xproperty
.window
== Scr
.Root
&&
3229 te
->xproperty
.state
== PropertyNewValue
&&
3230 (te
->xproperty
.atom
== _XA_XSETROOT_ID
||
3231 te
->xproperty
.atom
== _XA_XROOTPMAP_ID
))
3233 /* background change */
3234 /* _XA_XSETROOT_ID is used by fvwm-root, xli and more (xv sends
3235 * no property notify?). _XA_XROOTPMAP_ID is used by Esetroot
3236 * compatible program: the problem here is that with some
3237 * Esetroot compatible program we get the message _before_ the
3238 * background change. This is fixed with Esetroot 9.2 (not yet
3239 * released, 2002-01-14) */
3241 /* update icon window with some alpha and tear-off menu */
3244 for (t
= Scr
.FvwmRoot
.next
; t
!= NULL
; t
= t
->next
)
3248 int b_cs
= t
->icon_background_cs
;
3249 Bool draw_picture
= False
;
3250 Bool draw_title
= False
;
3252 /* redraw ParentRelative tear-off menu */
3253 menu_redraw_transparent_tear_off_menu(t
, True
);
3255 if (!IS_ICONIFIED(t
) || IS_ICON_SUPPRESSED(t
))
3259 if (Scr
.Hilite
== t
)
3261 if (t
->icon_title_cs_hi
>= 0)
3263 t_cs
= cs
= t
->icon_title_cs_hi
;
3272 if (t
->icon_title_cs
>= 0)
3274 t_cs
= cs
= t
->icon_title_cs
;
3281 if (t
->icon_alphaPixmap
!= None
||
3283 Colorset
[cs
].icon_alpha_percent
< 100) ||
3284 CSET_IS_TRANSPARENT_PR(b_cs
) ||
3285 (!IS_ICON_SHAPED(t
) &&
3286 t
->icon_background_padding
> 0))
3288 draw_picture
= True
;
3290 if (CSET_IS_TRANSPARENT_PR(t_cs
))
3294 if (draw_title
|| draw_picture
)
3297 t
, draw_title
, draw_picture
, False
,
3298 draw_picture
, NULL
);
3301 if (te
->xproperty
.atom
== _XA_XROOTPMAP_ID
)
3303 update_root_transparent_colorset(te
->xproperty
.atom
);
3305 BroadcastPropertyChange(
3306 MX_PROPERTY_CHANGE_BACKGROUND
, 0, 0, "");
3315 dpy
, FW_W(fw
), &JunkRoot
, &JunkX
, &JunkY
,
3316 (unsigned int*)&JunkWidth
, (unsigned int*)&JunkHeight
,
3317 (unsigned int*)&JunkBW
, (unsigned int*)&JunkDepth
) == 0)
3323 * Make sure at least part of window is on this page
3324 * before giving it focus...
3326 OnThisPage
= IsRectangleOnThisPage(&(fw
->g
.frame
), fw
->Desk
);
3328 switch (te
->xproperty
.atom
)
3330 case XA_WM_TRANSIENT_FOR
:
3331 flush_property_notify(XA_WM_TRANSIENT_FOR
, FW_W(fw
));
3332 if (setup_transientfor(fw
) == True
)
3334 RaiseWindow(fw
, False
);
3339 flush_property_notify(XA_WM_NAME
, FW_W(fw
));
3340 if (HAS_EWMH_WM_NAME(fw
))
3344 FlocaleGetNameProperty(XGetWMName
, dpy
, FW_W(fw
), &new_name
);
3345 if (new_name
.name
== NULL
)
3347 FlocaleFreeNameProperty(&new_name
);
3350 if (strlen(new_name
.name
) > MAX_WINDOW_NAME_LEN
)
3352 /* limit to prevent hanging X server */
3353 (new_name
.name
)[MAX_WINDOW_NAME_LEN
] = 0;
3355 if (fw
->name
.name
&& strcmp(new_name
.name
, fw
->name
.name
) == 0)
3357 /* migo: some apps update their names every second */
3358 /* griph: make sure we don't free the property if it
3360 if (new_name
.name
!= fw
->name
.name
)
3362 FlocaleFreeNameProperty(&new_name
);
3367 free_window_names(fw
, True
, False
);
3368 fw
->name
= new_name
;
3369 SET_NAME_CHANGED(fw
, 1);
3370 if (fw
->name
.name
== NULL
)
3372 fw
->name
.name
= NoName
; /* must not happen */
3374 setup_visible_name(fw
, False
);
3375 BroadcastWindowIconNames(fw
, True
, False
);
3377 /* fix the name in the title bar */
3378 if (!IS_ICONIFIED(fw
))
3380 border_draw_decorations(
3381 fw
, PART_TITLE
, (Scr
.Hilite
== fw
), True
,
3382 CLEAR_ALL
, NULL
, NULL
);
3384 EWMH_SetVisibleName(fw
, False
);
3386 * if the icon name is NoName, set the name of the icon to be
3387 * the same as the window
3389 if (!WAS_ICON_NAME_PROVIDED(fw
))
3391 fw
->icon_name
= fw
->name
;
3392 setup_visible_name(fw
, True
);
3393 BroadcastWindowIconNames(fw
, False
, True
);
3398 case XA_WM_ICON_NAME
:
3399 flush_property_notify(XA_WM_ICON_NAME
, FW_W(fw
));
3400 if (HAS_EWMH_WM_ICON_NAME(fw
))
3404 FlocaleGetNameProperty(
3405 XGetWMIconName
, dpy
, FW_W(fw
), &new_name
);
3406 if (new_name
.name
== NULL
)
3408 FlocaleFreeNameProperty(&new_name
);
3411 if (new_name
.name
&& strlen(new_name
.name
) > MAX_ICON_NAME_LEN
)
3413 /* limit to prevent hanging X server */
3414 (new_name
.name
)[MAX_ICON_NAME_LEN
] = 0;
3416 if (fw
->icon_name
.name
&&
3417 strcmp(new_name
.name
, fw
->icon_name
.name
) == 0)
3419 /* migo: some apps update their names every second */
3420 /* griph: make sure we don't free the property if it
3422 if (new_name
.name
!= fw
->icon_name
.name
)
3424 FlocaleFreeNameProperty(&new_name
);
3429 free_window_names(fw
, False
, True
);
3430 fw
->icon_name
= new_name
;
3431 SET_WAS_ICON_NAME_PROVIDED(fw
, 1);
3432 if (fw
->icon_name
.name
== NULL
)
3434 /* currently never happens */
3435 fw
->icon_name
.name
= fw
->name
.name
;
3436 SET_WAS_ICON_NAME_PROVIDED(fw
, 0);
3438 setup_visible_name(fw
, True
);
3439 BroadcastWindowIconNames(fw
, False
, True
);
3441 EWMH_SetVisibleName(fw
, True
);
3445 flush_property_notify(XA_WM_HINTS
, FW_W(fw
));
3446 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3447 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3449 old_wmhints_flags
= 0;
3452 old_wmhints_flags
= fw
->wmhints
->flags
;
3453 XFree ((char *) fw
->wmhints
);
3456 if (fw
->wmhints
== NULL
)
3462 * rebuild icon if the client either provides an icon
3463 * pixmap or window or has reset the hints to `no icon'.
3465 if ((fw
->wmhints
->flags
& IconPixmapHint
) ||
3466 (old_wmhints_flags
& IconPixmapHint
))
3468 ICON_DBG((stderr
, "hpn: iph changed (%d) '%s'\n", !!(int)(fw
->wmhints
->flags
& IconPixmapHint
), fw
->name
));
3469 has_icon_pixmap_hint_changed
= True
;
3471 if ((fw
->wmhints
->flags
& IconWindowHint
) ||
3472 (old_wmhints_flags
& IconWindowHint
))
3474 ICON_DBG((stderr
, "hpn: iwh changed (%d) '%s'\n", !!(int)(fw
->wmhints
->flags
& IconWindowHint
), fw
->name
));
3475 has_icon_window_hint_changed
= True
;
3476 SET_USE_EWMH_ICON(fw
, False
);
3478 increase_icon_hint_count(fw
);
3479 if (has_icon_window_hint_changed
||
3480 has_icon_pixmap_hint_changed
)
3482 if (ICON_OVERRIDE_MODE(fw
) == ICON_OVERRIDE
)
3484 ICON_DBG((stderr
, "hpn: icon override '%s'\n", fw
->name
));
3485 has_icon_changed
= False
;
3487 else if (ICON_OVERRIDE_MODE(fw
) ==
3488 NO_ACTIVE_ICON_OVERRIDE
)
3490 if (has_icon_pixmap_hint_changed
)
3492 if (WAS_ICON_HINT_PROVIDED(fw
) ==
3495 ICON_DBG((stderr
, "hpn: using further iph '%s'\n", fw
->name
));
3496 has_icon_changed
= True
;
3498 else if (fw
->icon_bitmap_file
==
3500 fw
->icon_bitmap_file
==
3503 ICON_DBG((stderr
, "hpn: using first iph '%s'\n", fw
->name
));
3504 has_icon_changed
= True
;
3508 /* ignore the first icon pixmap
3509 * hint if the application did
3510 * not provide it from the
3512 ICON_DBG((stderr
, "hpn: first iph ignored '%s'\n", fw
->name
));
3513 has_icon_changed
= False
;
3516 else if (has_icon_window_hint_changed
)
3518 ICON_DBG((stderr
, "hpn: using iwh '%s'\n", fw
->name
));
3519 has_icon_changed
= True
;
3523 ICON_DBG((stderr
, "hpn: iwh not changed, hint ignored '%s'\n", fw
->name
));
3524 has_icon_changed
= False
;
3527 else /* NO_ICON_OVERRIDE */
3529 ICON_DBG((stderr
, "hpn: using hint '%s'\n", fw
->name
));
3530 has_icon_changed
= True
;
3533 if (USE_EWMH_ICON(fw
))
3535 has_icon_changed
= False
;
3538 if (has_icon_changed
)
3540 ICON_DBG((stderr
, "hpn: icon changed '%s'\n", fw
->name
));
3541 /* Okay, the icon hint has changed and style
3542 * options tell us to honour this change. Now
3543 * let's see if we have to use the application
3544 * provided pixmap or window (if any), the icon
3545 * file provided by the window's style or the
3546 * default style's icon. */
3547 if (fw
->icon_bitmap_file
== Scr
.DefaultIcon
)
3549 fw
->icon_bitmap_file
= NULL
;
3551 if (!fw
->icon_bitmap_file
&&
3552 !(fw
->wmhints
->flags
&
3553 (IconPixmapHint
|IconWindowHint
)))
3555 fw
->icon_bitmap_file
=
3557 Scr
.DefaultIcon
: NULL
;
3559 fw
->iconPixmap
= (Window
)NULL
;
3560 ChangeIconPixmap(fw
);
3564 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3565 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3566 * Treat urgency changes by calling user-settable functions.
3567 * These could e.g. deiconify and raise the window or
3568 * temporarily change the decor. */
3569 if (!(old_wmhints_flags
& XUrgencyHint
) &&
3570 (fw
->wmhints
->flags
& XUrgencyHint
))
3572 urgency_action
= "Function UrgencyFunc";
3574 if ((old_wmhints_flags
& XUrgencyHint
) &&
3575 !(fw
->wmhints
->flags
& XUrgencyHint
))
3577 urgency_action
= "Function UrgencyDoneFunc";
3581 const exec_context_t
*exc
;
3582 exec_context_changes_t ecc
;
3585 ecc
.w
.wcontext
= C_WINDOW
;
3586 exc
= exc_clone_context(
3587 ea
->exc
, &ecc
, ECC_FW
| ECC_WCONTEXT
);
3588 execute_function(NULL
, exc
, urgency_action
, 0);
3589 exc_destroy_context(exc
);
3592 case XA_WM_NORMAL_HINTS
:
3593 /* just mark wm normal hints as changed and look them up when
3594 * the next ConfigureRequest w/ x, y, width or height set
3596 SET_HAS_NEW_WM_NORMAL_HINTS(fw
, 1);
3599 if (te
->xproperty
.atom
== _XA_WM_PROTOCOLS
)
3601 FetchWmProtocols (fw
);
3603 else if (te
->xproperty
.atom
== _XA_WM_COLORMAP_WINDOWS
)
3605 FetchWmColormapWindows (fw
); /* frees old data */
3606 ReInstallActiveColormap();
3608 else if (te
->xproperty
.atom
== _XA_WM_STATE
)
3610 if (fw
&& OnThisPage
&& focus_is_focused(fw
) &&
3611 FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw
)))
3613 /* refresh the focus - why? */
3614 focus_force_refresh_focus(fw
);
3619 EWMH_ProcessPropertyNotify(ea
->exc
);
3625 void HandleReparentNotify(const evh_args_t
*ea
)
3627 const XEvent
*te
= ea
->exc
->x
.etrigger
;
3628 FvwmWindow
* const fw
= ea
->exc
->w
.fw
;
3634 if (te
->xreparent
.parent
== Scr
.Root
)
3636 /* Ignore reparenting to the root window. In some cases these
3637 * events are selected although the window is no longer
3641 if (te
->xreparent
.parent
!= FW_W_FRAME(fw
))
3643 /* window was reparented by someone else, destroy the frame */
3644 SetMapStateProp(fw
, WithdrawnState
);
3645 EWMH_RestoreInitialStates(fw
, te
->type
);
3646 if (!IS_TEAR_OFF_MENU(fw
))
3648 XRemoveFromSaveSet(dpy
, te
->xreparent
.window
);
3649 XSelectInput(dpy
, te
->xreparent
.window
, NoEventMask
);
3653 XSelectInput(dpy
, te
->xreparent
.window
, XEVMASK_MENUW
);
3655 discard_events(XEVMASK_FRAMEW
);
3657 EWMH_ManageKdeSysTray(te
->xreparent
.window
, te
->type
);
3658 EWMH_WindowDestroyed();
3664 void HandleSelectionRequest(const evh_args_t
*ea
)
3666 icccm2_handle_selection_request(ea
->exc
->x
.etrigger
);
3671 void HandleSelectionClear(const evh_args_t
*ea
)
3673 icccm2_handle_selection_clear();
3678 void HandleShapeNotify(const evh_args_t
*ea
)
3680 FvwmWindow
* const fw
= ea
->exc
->w
.fw
;
3682 DBUG("HandleShapeNotify", "Routine Entered");
3684 if (FShapesSupported
)
3686 const FShapeEvent
*sev
=
3687 (const FShapeEvent
*)(ea
->exc
->x
.etrigger
);
3693 if (sev
->kind
!= FShapeBounding
)
3698 fw
, fw
->g
.frame
.width
, fw
->g
.frame
.height
, sev
->shaped
);
3699 GNOME_SetWinArea(fw
);
3700 EWMH_SetFrameStrut(fw
);
3701 if (!IS_ICONIFIED(fw
))
3703 border_redraw_decorations(fw
);
3710 void HandleUnmapNotify(const evh_args_t
*ea
)
3716 const XEvent
*te
= ea
->exc
->x
.etrigger
;
3719 Bool must_return
= False
;
3720 Bool do_map
= False
;
3721 FvwmWindow
* const fw
= ea
->exc
->w
.fw
;
3725 DBUG("HandleUnmapNotify", "Routine Entered");
3727 /* Don't ignore events as described below. */
3728 if (te
->xunmap
.event
!= te
->xunmap
.window
&&
3729 (te
->xunmap
.event
!= Scr
.Root
|| !te
->xunmap
.send_event
))
3735 * The July 27, 1988 ICCCM spec states that a client wishing to switch
3736 * to WithdrawnState should send a synthetic UnmapNotify with the
3737 * event field set to (pseudo-)root, in case the window is already
3738 * unmapped (which is the case for fvwm for IconicState).
3739 * Unfortunately, we looked for the FvwmContext using that field, so
3740 * try the window field also. */
3746 dpy
, te
->xunmap
.window
, FvwmContext
,
3747 (caddr_t
*)&fw
) == XCNOENT
)
3753 pw
= FW_W_PARENT(fw
);
3754 if (te
->xunmap
.window
== FW_W_FRAME(fw
))
3756 SET_ICONIFY_PENDING(fw
, 0);
3766 Bool is_map_request_pending
;
3767 check_if_event_args args
;
3769 args
.w
= te
->xunmap
.window
;
3770 args
.do_return_true
= False
;
3771 args
.do_return_true_cr
= False
;
3772 /* Using FCheckTypedWindowEvent() does not work here. I don't
3773 * have the slightest idea why, but using FCheckIfEvent() with
3774 * the appropriate predicate procedure works fine. */
3775 FCheckIfEvent(dpy
, &dummy
, test_map_request
, (XPointer
)&args
);
3776 /* Unfortunately, there is no procedure in X that simply tests
3777 * if an event of a certain type in on the queue without
3778 * waiting and without removing it from the queue.
3779 * XCheck...Event() does not wait but removes the event while
3780 * XPeek...() does not remove the event but waits. To solve
3781 * this, the predicate procedure sets a flag in the passed in
3782 * structure and returns False unconditionally. */
3783 is_map_request_pending
= (args
.ret_does_match
== True
);
3784 if (!is_map_request_pending
)
3786 XUnmapWindow(dpy
, te
->xunmap
.window
);
3789 if (fw
== Scr
.Hilite
)
3793 focus_grabbed
= focus_query_close_release_focus(fw
);
3794 restore_focus_after_unmap(fw
, False
);
3795 if (!IS_MAPPED(fw
) && !IS_ICONIFIED(fw
))
3801 * The program may have unmapped the client window, from either
3802 * NormalState or IconicState. Handle the transition to WithdrawnState.
3804 * We need to reparent the window back to the root (so that fvwm exiting
3805 * won't cause it to get mapped) and then throw away all state (pretend
3806 * that we've received a DestroyNotify).
3808 if (!FCheckTypedWindowEvent(
3809 dpy
, te
->xunmap
.window
, DestroyNotify
, &dummy
) &&
3810 XTranslateCoordinates(
3811 dpy
, te
->xunmap
.window
, Scr
.Root
, 0, 0, &dstx
, &dsty
,
3815 SetMapStateProp(fw
, WithdrawnState
);
3816 EWMH_RestoreInitialStates(fw
, te
->type
);
3817 if (FCheckTypedWindowEvent(
3818 dpy
, te
->xunmap
.window
, ReparentNotify
, &dummy
))
3820 if (fw
->attr_backup
.border_width
)
3822 XSetWindowBorderWidth(
3823 dpy
, te
->xunmap
.window
,
3824 fw
->attr_backup
.border_width
);
3826 if ((!IS_ICON_SUPPRESSED(fw
))&&
3828 (fw
->wmhints
->flags
& IconWindowHint
)))
3830 XUnmapWindow(dpy
, fw
->wmhints
->icon_window
);
3835 RestoreWithdrawnLocation(fw
, False
, Scr
.Root
);
3837 if (!IS_TEAR_OFF_MENU(fw
))
3839 XRemoveFromSaveSet(dpy
, te
->xunmap
.window
);
3840 XSelectInput(dpy
, te
->xunmap
.window
, NoEventMask
);
3843 MyXUngrabServer(dpy
);
3844 if (FCheckTypedWindowEvent(dpy
, pw
, MapRequest
, &map_event
))
3846 /* the client tried to map the window again while it
3847 * was still inside the decoration windows */
3852 if (focus_grabbed
== True
)
3854 CoerceEnterNotifyOnCurrentWindow();
3856 EWMH_ManageKdeSysTray(te
->xunmap
.window
, te
->type
);
3857 EWMH_WindowDestroyed();
3858 GNOME_SetClientList();
3861 map_event
.xmaprequest
.window
= cw
;
3862 map_event
.xmaprequest
.parent
= Scr
.Root
;
3863 dispatch_event(&map_event
);
3864 /* note: we really should handle all map and unmap notify
3865 * events for that window in a loop here */
3871 void HandleVisibilityNotify(const evh_args_t
*ea
)
3873 FvwmWindow
* const fw
= ea
->exc
->w
.fw
;
3875 DBUG("HandleVisibilityNotify", "Routine Entered");
3877 if (fw
&& ea
->exc
->x
.etrigger
->xvisibility
.window
== FW_W_FRAME(fw
))
3879 switch (ea
->exc
->x
.etrigger
->xvisibility
.state
)
3881 case VisibilityUnobscured
:
3882 SET_FULLY_VISIBLE(fw
, 1);
3883 SET_PARTIALLY_VISIBLE(fw
, 1);
3885 case VisibilityPartiallyObscured
:
3886 SET_FULLY_VISIBLE(fw
, 0);
3887 SET_PARTIALLY_VISIBLE(fw
, 1);
3890 SET_FULLY_VISIBLE(fw
, 0);
3891 SET_PARTIALLY_VISIBLE(fw
, 0);
3894 /* Make sure the button grabs are up to date */
3895 focus_grab_buttons(fw
);
3901 /* ---------------------------- interface functions ------------------------ */
3903 /* Inform a client window of its geometry.
3905 * The input (frame) geometry will be translated to client geometry
3906 * before sending. */
3907 void SendConfigureNotify(
3908 FvwmWindow
*fw
, int x
, int y
, int w
, int h
, int bw
,
3909 Bool send_for_frame_too
)
3911 XEvent client_event
;
3914 if (!fw
|| IS_SHADED(fw
))
3918 client_event
.type
= ConfigureNotify
;
3919 client_event
.xconfigure
.display
= dpy
;
3920 client_event
.xconfigure
.event
= FW_W(fw
);
3921 client_event
.xconfigure
.window
= FW_W(fw
);
3922 get_window_borders(fw
, &b
);
3923 client_event
.xconfigure
.x
= x
+ b
.top_left
.width
;
3924 client_event
.xconfigure
.y
= y
+ b
.top_left
.height
;
3925 client_event
.xconfigure
.width
= w
- b
.total_size
.width
;
3926 client_event
.xconfigure
.height
= h
- b
.total_size
.height
;
3927 client_event
.xconfigure
.border_width
= bw
;
3928 client_event
.xconfigure
.above
= FW_W_FRAME(fw
);
3929 client_event
.xconfigure
.override_redirect
= False
;
3932 "send cn: %d %d %dx%d fw 0x%08x w 0x%08x ew 0x%08x '%s'\n",
3933 client_event
.xconfigure
.x
, client_event
.xconfigure
.y
,
3934 client_event
.xconfigure
.width
, client_event
.xconfigure
.height
,
3935 (int)FW_W_FRAME(fw
), (int)FW_W(fw
),
3936 (int)client_event
.xconfigure
.window
,
3937 (fw
->name
.name
) ? fw
->name
.name
: "");
3940 dpy
, FW_W(fw
), False
, StructureNotifyMask
, &client_event
);
3941 if (send_for_frame_too
)
3943 /* This is for buggy tk, which waits for the real
3944 * ConfigureNotify on frame instead of the synthetic one on w.
3945 * The geometry data in the event will not be correct for the
3946 * frame, but tk doesn't look at that data anyway. */
3947 client_event
.xconfigure
.event
= FW_W_FRAME(fw
);
3948 client_event
.xconfigure
.window
= FW_W_FRAME(fw
);
3950 dpy
, FW_W_FRAME(fw
), False
, StructureNotifyMask
,
3957 /* Add an event group to the event handler */
3958 int register_event_group(int event_base
, int event_count
, PFEH
*jump_table
)
3960 /* insert into the list */
3961 event_group_t
*group
;
3962 event_group_t
*position
= base_event_group
;
3963 event_group_t
*prev_position
= NULL
;
3967 position
->base
+ position
->count
< event_base
)
3969 prev_position
= position
;
3970 position
= position
->next
;
3972 if ((position
!= NULL
&& position
->base
< event_base
+ event_count
))
3974 /* there is already an event group registered at the specified
3975 * event range, or the base is before the base X events */
3979 /* create the group structure (these are not freed until fvwm exits) */
3980 group
= (event_group_t
*)safemalloc(sizeof(event_group_t
));
3981 group
->base
= event_base
;
3982 group
->count
= event_count
;
3983 group
->jump_table
= jump_table
;
3984 group
->next
= position
;
3985 if (prev_position
!= NULL
)
3987 prev_position
->next
= group
;
3991 base_event_group
= group
;
3999 ** InitEventHandlerJumpTable
4001 void InitEventHandlerJumpTable(void)
4003 static PFEH EventHandlerJumpTable
[LASTEvent
];
4006 for (i
=0; i
<LASTEvent
; i
++)
4008 EventHandlerJumpTable
[i
] = NULL
;
4010 EventHandlerJumpTable
[Expose
] = HandleExpose
;
4011 EventHandlerJumpTable
[DestroyNotify
] = HandleDestroyNotify
;
4012 EventHandlerJumpTable
[MapRequest
] = HandleMapRequest
;
4013 EventHandlerJumpTable
[MapNotify
] = HandleMapNotify
;
4014 EventHandlerJumpTable
[UnmapNotify
] = HandleUnmapNotify
;
4015 EventHandlerJumpTable
[ButtonPress
] = HandleButtonPress
;
4016 EventHandlerJumpTable
[EnterNotify
] = HandleEnterNotify
;
4017 EventHandlerJumpTable
[LeaveNotify
] = HandleLeaveNotify
;
4018 EventHandlerJumpTable
[FocusIn
] = HandleFocusIn
;
4019 EventHandlerJumpTable
[FocusOut
] = HandleFocusOut
;
4020 EventHandlerJumpTable
[ConfigureRequest
] = HandleConfigureRequest
;
4021 EventHandlerJumpTable
[ClientMessage
] = HandleClientMessage
;
4022 EventHandlerJumpTable
[PropertyNotify
] = HandlePropertyNotify
;
4023 EventHandlerJumpTable
[KeyPress
] = HandleKeyPress
;
4024 EventHandlerJumpTable
[KeyRelease
] = HandleKeyRelease
;
4025 EventHandlerJumpTable
[VisibilityNotify
] = HandleVisibilityNotify
;
4026 EventHandlerJumpTable
[ColormapNotify
] = HandleColormapNotify
;
4027 EventHandlerJumpTable
[SelectionClear
] = HandleSelectionClear
;
4028 EventHandlerJumpTable
[SelectionRequest
] = HandleSelectionRequest
;
4029 EventHandlerJumpTable
[ReparentNotify
] = HandleReparentNotify
;
4030 EventHandlerJumpTable
[MappingNotify
] = HandleMappingNotify
;
4031 STROKE_CODE(EventHandlerJumpTable
[ButtonRelease
] = HandleButtonRelease
);
4032 STROKE_CODE(EventHandlerJumpTable
[MotionNotify
] = HandleMotionNotify
);
4033 #ifdef MOUSE_DROPPINGS
4034 STROKE_CODE(stroke_init(dpy
,DefaultRootWindow(dpy
)));
4035 #else /* no MOUSE_DROPPINGS */
4036 STROKE_CODE(stroke_init());
4037 #endif /* MOUSE_DROPPINGS */
4038 if (register_event_group(0, LASTEvent
, EventHandlerJumpTable
))
4040 /* should never happen */
4041 fvwm_msg(ERR
, "InitEventHandlerJumpTable",
4042 "Faild to initialize event handlers");
4045 if (FShapesSupported
)
4047 static PFEH shape_jump_table
[FShapeNumberEvents
];
4049 for (i
= 0; i
< FShapeNumberEvents
; i
++)
4051 shape_jump_table
[i
] = NULL
;
4053 shape_jump_table
[FShapeNotify
] = HandleShapeNotify
;
4055 register_event_group(
4056 FShapeEventBase
, FShapeNumberEvents
,
4059 fvwm_msg(ERR
, "InitEventHandlerJumpTable",
4060 "Faild to init Shape event handler");
4068 /* handle a single X event */
4069 void dispatch_event(XEvent
*e
)
4071 Window w
= e
->xany
.window
;
4073 event_group_t
*event_group
;
4075 DBUG("dispatch_event", "Routine Entered");
4084 if (e
->xbutton
.subwindow
!= None
)
4086 w
= e
->xbutton
.subwindow
;
4089 w
= e
->xmaprequest
.window
;
4095 if (w
== Scr
.Root
||
4096 XFindContext(dpy
, w
, FvwmContext
, (caddr_t
*)&fw
) == XCNOENT
)
4100 last_event_type
= e
->type
;
4101 event_group
= base_event_group
;
4103 event_group
!= NULL
&&
4104 event_group
->base
+ event_group
->count
< e
->type
)
4106 event_group
= event_group
->next
;
4110 event_group
!= NULL
&&
4111 e
->type
- event_group
->base
< event_group
->count
&&
4112 event_group
->jump_table
[e
->type
- event_group
->base
] != NULL
)
4115 exec_context_changes_t ecc
;
4118 ecc
.type
= EXCT_EVENT
;
4120 ecc
.w
.wcontext
= GetContext(&fw
, fw
, e
, &dummyw
);
4123 ea
.exc
= exc_create_context(
4124 &ecc
, ECC_TYPE
| ECC_ETRIGGER
| ECC_FW
| ECC_W
|
4126 (*event_group
->jump_table
[e
->type
- event_group
->base
])(&ea
);
4127 exc_destroy_context(ea
.exc
);
4131 /* If we're using the C version of alloca, see if anything needs to be
4136 DBUG("dispatch_event", "Leaving Routine");
4141 /* ewmh configure request */
4142 void events_handle_configure_request(
4143 XConfigureRequestEvent cre
, FvwmWindow
*fw
, Bool force
,
4146 __handle_configure_request(cre
, NULL
, fw
, force
, force_gravity
);
4151 void HandleEvents(void)
4155 DBUG("HandleEvents", "Routine Entered");
4156 STROKE_CODE(send_motion
= False
);
4157 while (!isTerminated
)
4159 last_event_type
= 0;
4160 if (Scr
.flags
.is_window_scheduled_for_destroy
)
4162 destroy_scheduled_windows();
4164 if (Scr
.flags
.do_need_window_update
)
4166 flush_window_updates();
4168 if (My_XNextEvent(dpy
, &ev
))
4170 dispatch_event(&ev
);
4172 if (Scr
.flags
.do_need_style_list_update
)
4174 simplify_style_list();
4183 * Waits for next X or module event, fires off startup routines when startup
4184 * modules have finished or after a timeout if the user has specified a
4185 * command line module that doesn't quit or gets stuck.
4188 int My_XNextEvent(Display
*dpy
, XEvent
*event
)
4190 fd_set in_fdset
, out_fdset
;
4192 fmodule_list_itr moditr
;
4194 fmodule_input
*input
;
4195 static struct timeval timeout
;
4196 static struct timeval
*timeoutP
= &timeout
;
4198 DBUG("My_XNextEvent", "Routine Entered");
4200 /* check for any X events already queued up.
4201 * Side effect: this does an XFlush if no events are queued
4202 * Make sure nothing between here and the select causes further
4203 * X requests to be sent or the select may block even though
4204 * there are events in the queue */
4208 "My_XNextEvent", "taking care of queued up events"
4209 " & returning (1)");
4210 FNextEvent(dpy
, event
);
4214 /* check for termination of all startup modules */
4217 module_list_itr_init(&moditr
);
4218 module
= module_list_itr_next(&moditr
);
4219 for (; module
!= NULL
; module
= module_list_itr_next(&moditr
))
4221 if (MOD_IS_CMDLINE(module
) == 1)
4232 "Starting up after command lines modules");
4233 /* set an infinite timeout to stop ticking */
4235 /* This may cause X requests to be sent */
4238 return 0; /* so return without select()ing */
4242 /* Some signals can interrupt us while we wait for any action
4243 * on our descriptors. While some of these signals may be asking
4244 * fvwm to die, some might be harmless. Harmless interruptions
4245 * mean we have to start waiting all over again ... */
4249 Bool is_waiting_for_scheduled_command
= False
;
4250 static struct timeval
*old_timeoutP
= NULL
;
4252 /* The timeouts become undefined whenever the select returns,
4253 * and so we have to reinitialise them */
4254 ms
= squeue_get_next_ms();
4257 /* run scheduled commands */
4259 ms
= squeue_get_next_ms();
4260 /* should not happen anyway.
4261 * get_next_schedule_queue_ms() can't return 0 after a
4262 * call to execute_schedule_queue(). */
4270 timeout
.tv_sec
= 42;
4271 timeout
.tv_usec
= 0;
4275 /* scheduled commands are pending - don't wait too
4277 timeout
.tv_sec
= ms
/ 1000;
4278 timeout
.tv_usec
= 1000 * (ms
% 1000);
4279 old_timeoutP
= timeoutP
;
4280 timeoutP
= &timeout
;
4281 is_waiting_for_scheduled_command
= True
;
4285 FD_ZERO(&out_fdset
);
4286 FD_SET(x_fd
, &in_fdset
);
4288 /* nothing is done here if fvwm was compiled without session
4292 FD_SET(sm_fd
, &in_fdset
);
4295 module_list_itr_init(&moditr
);
4296 while ( (module
= module_list_itr_next(&moditr
)) != NULL
)
4298 FD_SET(MOD_READFD(module
), &in_fdset
);
4300 if (!FQUEUE_IS_EMPTY(&MOD_PIPEQUEUE(module
)))
4302 FD_SET(MOD_WRITEFD(module
), &out_fdset
);
4306 DBUG("My_XNextEvent", "waiting for module input/output");
4307 num_fd
= fvwmSelect(
4308 fvwmlib_max_fd
, &in_fdset
, &out_fdset
, 0, timeoutP
);
4309 if (is_waiting_for_scheduled_command
)
4311 timeoutP
= old_timeoutP
;
4314 /* Express route out of fvwm ... */
4319 } while (num_fd
< 0);
4323 /* Check for module input. */
4324 module_list_itr_init(&moditr
);
4325 while ( (module
= module_list_itr_next(&moditr
)) != NULL
)
4327 if (FD_ISSET(MOD_READFD(module
), &in_fdset
))
4329 input
= module_receive(module
);
4330 /* enqueue the received command */
4331 module_input_enqueue(input
);
4334 MOD_WRITEFD(module
) >= 0 &&
4335 FD_ISSET(MOD_WRITEFD(module
), &out_fdset
))
4337 DBUG("My_XNextEvent",
4338 "calling FlushMessageQueue");
4339 FlushMessageQueue(module
);
4343 /* execute any commands queued up */
4344 DBUG("My_XNextEvent", "executing module comand queue");
4345 ExecuteCommandQueue();
4347 /* cleanup dead modules */
4350 /* nothing is done here if fvwm was compiled without session
4352 if ((sm_fd
>= 0) && (FD_ISSET(sm_fd
, &in_fdset
)))
4360 /* select has timed out, things must have calmed down so let's
4364 fvwm_msg(ERR
, "My_XNextEvent",
4365 "Some command line modules have not quit, "
4366 "Starting up after timeout.\n");
4368 timeoutP
= NULL
; /* set an infinite timeout to stop
4370 reset_style_changes();
4371 Scr
.flags
.do_need_window_update
= 0;
4373 /* run scheduled commands if necessary */
4377 /* check for X events again, rather than return 0 and get called again
4381 DBUG("My_XNextEvent",
4382 "taking care of queued up events & returning (2)");
4383 FNextEvent(dpy
,event
);
4387 DBUG("My_XNextEvent", "leaving My_XNextEvent");
4394 * Find the Fvwm context for the event.
4397 int GetContext(FvwmWindow
**ret_fw
, FvwmWindow
*t
, const XEvent
*e
, Window
*w
)
4404 Bool is_key_event
= False
;
4406 win
= e
->xany
.window
;
4407 context
= C_NO_CONTEXT
;
4414 subw
= e
->xkey
.subwindow
;
4415 if (win
== Scr
.Root
&& subw
!= None
)
4417 /* Translate root coordinates into subwindow
4418 * coordinates. Necessary for key bindings that work
4419 * over unfocused windows. */
4421 XTranslateCoordinates(
4422 dpy
, Scr
.Root
, subw
, x
, y
, &x
, &y
, &subw
);
4423 XFindContext(dpy
, win
, FvwmContext
, (caddr_t
*) &t
);
4425 is_key_event
= True
;
4433 subw
= e
->xbutton
.subwindow
;
4435 if (t
&& win
== FW_W_FRAME(t
) && subw
!= None
)
4437 /* Translate frame coordinates into subwindow
4440 XTranslateCoordinates(
4441 dpy
, FW_W_FRAME(t
), subw
, x
, y
, &x
, &y
, &subw
);
4442 if (win
== FW_W_PARENT(t
))
4445 XTranslateCoordinates(
4446 dpy
, FW_W_PARENT(t
), subw
, x
, y
, &x
,
4452 XFindContext(dpy
, win
, FvwmContext
, (caddr_t
*)&t
);
4464 if (*w
== Scr
.NoFocusWin
)
4470 if (win
== FW_W_PARENT(t
))
4479 context
= frame_window_id_to_context(t
, *w
, &Button
);
4486 * Removes expose events for a specific window from the queue
4489 int flush_expose(Window w
)
4494 while (FCheckTypedWindowEvent(dpy
, w
, Expose
, &dummy
))
4502 /* same as above, but merges the expose rectangles into a single big one */
4503 int flush_accumulate_expose(Window w
, XEvent
*e
)
4507 int x1
= e
->xexpose
.x
;
4508 int y1
= e
->xexpose
.y
;
4509 int x2
= x1
+ e
->xexpose
.width
;
4510 int y2
= y1
+ e
->xexpose
.height
;
4512 while (FCheckTypedWindowEvent(dpy
, w
, Expose
, &dummy
))
4514 x1
= min(x1
, dummy
.xexpose
.x
);
4515 y1
= min(y1
, dummy
.xexpose
.y
);
4516 x2
= max(x2
, dummy
.xexpose
.x
+ dummy
.xexpose
.width
);
4517 y2
= max(y2
, dummy
.xexpose
.y
+ dummy
.xexpose
.height
);
4522 e
->xexpose
.width
= x2
- x1
;
4523 e
->xexpose
.height
= y2
- y1
;
4530 * Removes all expose events from the queue and does the necessary redraws
4533 void handle_all_expose(void)
4538 saved_event
= fev_save_event();
4540 while (FCheckMaskEvent(dpy
, ExposureMask
, &evdummy
))
4542 dispatch_event(&evdummy
);
4544 fev_restore_event(saved_event
);
4549 /* CoerceEnterNotifyOnCurrentWindow()
4550 * Pretends to get a HandleEnterNotify on the window that the pointer
4551 * currently is in so that the focus gets set correctly from the beginning.
4552 * Note that this presently only works if the current window is not
4553 * click_to_focus; I think that that behaviour is correct and desirable.
4555 void CoerceEnterNotifyOnCurrentWindow(void)
4561 exec_context_changes_t ecc
;
4566 dpy
, Scr
.Root
, &root
, &child
, &e
.xcrossing
.x_root
,
4567 &e
.xcrossing
.y_root
, &e
.xcrossing
.x
, &e
.xcrossing
.y
,
4569 if (f
== False
|| child
== None
)
4573 e
.xcrossing
.type
= EnterNotify
;
4574 e
.xcrossing
.window
= child
;
4575 e
.xcrossing
.subwindow
= None
;
4576 e
.xcrossing
.mode
= NotifyNormal
;
4577 e
.xcrossing
.detail
= NotifyAncestor
;
4578 e
.xcrossing
.same_screen
= True
;
4579 if (XFindContext(dpy
, child
, FvwmContext
, (caddr_t
*)&fw
) == XCNOENT
)
4585 XTranslateCoordinates(
4586 dpy
, Scr
.Root
, child
, e
.xcrossing
.x_root
,
4587 e
.xcrossing
.y_root
, &JunkX
, &JunkY
, &child
);
4588 if (child
== FW_W_PARENT(fw
))
4594 e
.xany
.window
= child
;
4597 e
.xcrossing
.focus
= (fw
== get_focus_window()) ? True
: False
;
4598 ecc
.type
= EXCT_NULL
;
4599 ecc
.x
.etrigger
= &e
;
4600 ea
.exc
= exc_create_context(&ecc
, ECC_TYPE
| ECC_ETRIGGER
);
4601 HandleEnterNotify(&ea
);
4602 exc_destroy_context(ea
.exc
);
4607 /* This function discards all queued up ButtonPress, ButtonRelease and
4608 * ButtonMotion events. */
4609 int discard_events(long event_mask
)
4615 for (count
= 0; FCheckMaskEvent(dpy
, event_mask
, &e
); count
++)
4623 /* This function discards all queued up ButtonPress, ButtonRelease and
4624 * ButtonMotion events. */
4625 int discard_window_events(Window w
, long event_mask
)
4631 for (count
= 0; FCheckWindowEvent(dpy
, w
, event_mask
, &e
); count
++)
4639 /* Similar function for certain types of PropertyNotify. */
4640 int flush_property_notify(Atom atom
, Window w
)
4644 test_typed_window_event_args args
;
4650 args
.event_type
= PropertyNotify
;
4652 /* Get rid of the events. */
4653 while (FCheckIfEvent(dpy
, &e
, test_typed_window_event
, (XPointer
)&args
))
4659 /* Wait for all mouse buttons to be released
4660 * This can ease some confusion on the part of the user sometimes
4662 * Discard superflous button events during this wait period. */
4663 void WaitForButtonsUp(Bool do_handle_expose
)
4667 long evmask
= ButtonPressMask
|ButtonReleaseMask
|ButtonMotionMask
|
4668 KeyPressMask
|KeyReleaseMask
;
4670 int use_wait_cursor
;
4673 if (FQueryPointer(dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
, &JunkX
, &JunkY
,
4674 &JunkX
, &JunkY
, &mask
) == False
)
4676 /* pointer is on a different screen - that's okay here */
4678 mask
&= DEFAULT_ALL_BUTTONS_MASK
;
4683 if (do_handle_expose
)
4685 evmask
|= ExposureMask
;
4687 GrabEm(None
, GRAB_NORMAL
);
4688 for (count
= 0, use_wait_cursor
= 0; mask
!= 0; count
++)
4690 /* handle expose events */
4691 XAllowEvents(dpy
, SyncPointer
, CurrentTime
);
4692 if (FCheckMaskEvent(dpy
, evmask
, &e
))
4697 if (e
.xbutton
.button
<=
4698 NUMBER_OF_MOUSE_BUTTONS
)
4700 bmask
= (Button1Mask
<<
4701 (e
.xbutton
.button
- 1));
4702 mask
= e
.xbutton
.state
& ~bmask
;
4715 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
,
4716 &JunkX
, &JunkY
, &JunkX
, &JunkY
, &mask
) ==
4719 /* pointer is on a different screen - that's
4722 mask
&= DEFAULT_ALL_BUTTONS_MASK
;
4725 if (use_wait_cursor
== 0 && count
== 20)
4727 GrabEm(CRS_WAIT
, GRAB_NORMAL
);
4728 use_wait_cursor
= 1;
4731 UngrabEm(GRAB_NORMAL
);
4732 if (use_wait_cursor
)
4734 UngrabEm(GRAB_NORMAL
);
4741 void sync_server(int toggle
)
4743 static Bool synced
= False
;
4747 toggle
= (synced
== False
);
4757 XSynchronize(dpy
, synced
);
4763 Bool
is_resizing_event_pending(
4767 check_if_event_args args
;
4770 args
.do_return_true
= False
;
4771 args
.do_return_true_cr
= False
;
4772 args
.cr_value_mask
= 0;
4773 args
.ret_does_match
= False
;
4775 FCheckIfEvent(dpy
, &e
, test_resizing_event
, (XPointer
)&args
);
4777 return args
.ret_does_match
;
4780 /* ---------------------------- builtin commands --------------------------- */
4782 void CMD_XSynchronize(F_CMD_ARGS
)
4786 toggle
= ParseToggleArgument(action
, NULL
, -1, 0);
4787 sync_server(toggle
);
4792 void CMD_XSync(F_CMD_ARGS
)