add tests for menu and menustyle commands.
[fvwm.git] / fvwm / events.c
blob9efe79ed8681244fa7d558f31f43d3ffea3826a5
1 /* This program is free software; you can redistribute it and/or modify
2 * it under the terms of the GNU General Public License as published by
3 * the Free Software Foundation; either version 2 of the License, or
4 * (at your option) any later version.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 /****************************************************************************
17 * This module is based on Twm, but has been siginificantly modified
18 * by Rob Nation
19 ****************************************************************************/
20 /*****************************************************************************/
21 /** Copyright 1988 by Evans & Sutherland Computer Corporation, **/
22 /** Salt Lake City, Utah **/
23 /** Portions Copyright 1989 by the Massachusetts Institute of Technology **/
24 /** Cambridge, Massachusetts **/
25 /** **/
26 /** All Rights Reserved **/
27 /** **/
28 /** Permission to use, copy, modify, and distribute this software and **/
29 /** its documentation for any purpose and without fee is hereby **/
30 /** granted, provided that the above copyright notice appear in all **/
31 /** copies and that both that copyright notice and this permis- **/
32 /** sion notice appear in supporting documentation, and that the **/
33 /** names of Evans & Sutherland and M.I.T. not be used in advertising **/
34 /** in publicity pertaining to distribution of the software without **/
35 /** specific, written prior permission. **/
36 /** **/
37 /** EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD **/
38 /** TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- **/
39 /** ABILITY AND FITNESS, IN NO EVENT SHALL EVANS & SUTHERLAND OR **/
40 /** M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAM- **/
41 /** AGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA **/
42 /** OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER **/
43 /** TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE **/
44 /** OR PERFORMANCE OF THIS SOFTWARE. **/
45 /*****************************************************************************/
48 /***********************************************************************
50 * fvwm event handling
52 ***********************************************************************/
54 #include "config.h"
56 #if HAVE_SYS_BSDTYPES_H
57 #include <sys/bsdtypes.h>
58 #endif
60 #include <stdio.h>
61 #include <string.h>
62 #include <sys/types.h>
63 #include <sys/time.h>
64 #include <unistd.h>
66 #include "libs/fvwmlib.h"
67 #include "fvwm.h"
68 #include "externs.h"
69 #include "cursor.h"
70 #include "functions.h"
71 #include "bindings.h"
72 #include "misc.h"
73 #include "screen.h"
74 #include "defaults.h"
75 #include "events.h"
76 #include "libs/Colorset.h"
77 #include "fvwmsignal.h"
78 #ifdef SHAPE
79 #include <X11/extensions/shape.h>
80 #endif /* SHAPE */
81 #include "module_interface.h"
82 #include "session.h"
83 #include "borders.h"
84 #include "colormaps.h"
85 #include "add_window.h"
86 #include "icccm2.h"
87 #include "icons.h"
88 #include "gnome.h"
89 #include "update.h"
90 #include "style.h"
91 #include "stack.h"
92 #include "geometry.h"
93 #include "focus.h"
94 #include "move_resize.h"
95 #include "virtual.h"
96 #ifdef HAVE_STROKE
97 #include "stroke.h"
98 #endif /* HAVE_STROKE */
100 #ifndef XUrgencyHint
101 #define XUrgencyHint (1L << 8)
102 #endif
104 extern void StartupStuff(void);
106 int Context = C_NO_CONTEXT; /* current button press context */
107 static int Button = 0;
108 FvwmWindow *ButtonWindow = NULL; /* button press window structure */
109 XEvent Event; /* the current event */
110 FvwmWindow *Tmp_win = NULL; /* the current fvwm window */
112 int last_event_type=0;
113 static Window last_event_window=0;
114 Time lastTimestamp = CurrentTime; /* until Xlib does this for us */
117 STROKE_CODE(static int send_motion;)
118 STROKE_CODE(static char sequence[MAX_SEQUENCE+1];)
120 #ifdef SHAPE
121 extern int ShapeEventBase;
122 void HandleShapeNotify(void);
123 #endif /* SHAPE */
125 Window PressedW;
128 ** LASTEvent is the number of X events defined - it should be defined
129 ** in X.h (to be like 35), but since extension (eg SHAPE) events are
130 ** numbered beyond LASTEvent, we need to use a bigger number than the
131 ** default, so let's undefine the default and use 256 instead.
133 #undef LASTEvent
134 #ifndef LASTEvent
135 #define LASTEvent 256
136 #endif /* !LASTEvent */
137 typedef void (*PFEH)(void);
138 PFEH EventHandlerJumpTable[LASTEvent];
140 /***********************************************************************
142 * Procedure:
143 * HandleFocusIn - handles focus in events
145 ************************************************************************/
146 void HandleFocusIn(void)
148 XEvent d;
149 Window w = None;
150 Window focus_w = None;
151 Window focus_fw = None;
152 Pixel fc = 0;
153 Pixel bc = 0;
154 FvwmWindow *ffw_old = Scr.Focus;
155 static Window last_focus_w = None;
156 static Window last_focus_fw = None;
157 static Bool is_never_focused = True;
159 DBUG("HandleFocusIn","Routine Entered");
161 /* This is a hack to make the PointerKey command work */
162 if (Event.xfocus.detail != NotifyPointer)
163 /**/
164 w= Event.xany.window;
165 while(XCheckTypedEvent(dpy,FocusIn,&d))
167 /* dito */
168 if (d.xfocus.detail != NotifyPointer)
169 /**/
170 w = d.xany.window;
172 /* dito */
173 if (w == None)
175 return;
177 /**/
178 if (XFindContext (dpy, w, FvwmContext, (caddr_t *) &Tmp_win) == XCNOENT)
180 Tmp_win = NULL;
183 if (!Tmp_win)
185 if (w != Scr.NoFocusWin)
187 Scr.UnknownWinFocused = w;
188 focus_w = w;
190 else
192 DrawDecorations(Scr.Hilite, DRAW_ALL, False, True, None);
193 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
195 if((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
197 InstallWindowColormaps(Scr.Hilite);
199 else
201 InstallWindowColormaps(NULL);
206 /* Not very useful if no window that fvwm and its modules know about has the
207 * focus. */
208 fc = GetColor("White");
209 bc = GetColor("Black");
211 else if (Tmp_win != Scr.Hilite
212 /* domivogt (16-May-2000): This check is necessary to force sending
213 * a M_FOCUS_CHANGE packet after an unmanaged window was focused.
214 * Otherwise fvwm would believe that Scr.Hilite was still focused and
215 * not send any info to the modules. */
216 || last_focus_fw == None)
218 DrawDecorations(Tmp_win, DRAW_ALL, True, True, None);
219 focus_w = Tmp_win->w;
220 focus_fw = Tmp_win->frame;
221 fc = Tmp_win->hicolors.fore;
222 bc = Tmp_win->hicolors.back;
223 Scr.Focus = Tmp_win;
224 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
226 if((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
228 InstallWindowColormaps(Scr.Hilite);
230 else
232 InstallWindowColormaps(NULL);
236 else
238 return;
240 if (is_never_focused || last_focus_fw == None ||
241 focus_w != last_focus_w || focus_fw != last_focus_fw)
243 BroadcastPacket(M_FOCUS_CHANGE, 5, focus_w, focus_fw,
244 (unsigned long)IsLastFocusSetByMouse(), fc, bc);
245 last_focus_w = focus_w;
246 last_focus_fw = focus_fw;
247 is_never_focused = False;
249 if (Scr.Focus != ffw_old)
251 focus_grab_buttons(Scr.Focus, True);
252 focus_grab_buttons(ffw_old, False);
256 /***********************************************************************
258 * Procedure:
259 * HandleKeyPress - key press event handler
261 ************************************************************************/
262 void HandleKeyPress(void)
264 char *action;
266 DBUG("HandleKeyPress","Routine Entered");
268 Context = GetContext(Tmp_win, &Event, &PressedW);
269 PressedW = None;
271 /* Here's a real hack - some systems have two keys with the
272 * same keysym and different keycodes. This converts all
273 * the cases to one keycode. */
274 Event.xkey.keycode =
275 XKeysymToKeycode(dpy,XKeycodeToKeysym(dpy,Event.xkey.keycode,0));
277 /* Check if there is something bound to the key */
278 action = CheckBinding(Scr.AllBindings, STROKE_ARG(0) Event.xkey.keycode,
279 Event.xkey.state, GetUnusedModifiers(), Context,
280 KEY_BINDING);
281 if (action != NULL)
283 ButtonWindow = Tmp_win;
284 ExecuteFunction(action, Tmp_win, &Event, Context, -1, 0, NULL);
285 ButtonWindow = NULL;
286 return;
289 /* if we get here, no function key was bound to the key. Send it
290 * to the client if it was in a window we know about.
292 if (Scr.Focus && Event.xkey.window != Scr.Focus->w)
294 Event.xkey.window = Scr.Focus->w;
295 XSendEvent(dpy, Scr.Focus->w, False, KeyPressMask, &Event);
297 else if (Tmp_win && Event.xkey.window != Tmp_win->w)
299 Event.xkey.window = Tmp_win->w;
300 XSendEvent(dpy, Tmp_win->w, False, KeyPressMask, &Event);
305 /***********************************************************************
307 * Procedure:
308 * HandlePropertyNotify - property notify event handler
310 ***********************************************************************/
311 void HandlePropertyNotify(void)
313 XTextProperty text_prop;
314 Bool OnThisPage = False;
315 Bool was_size_inc_set;
316 int old_wmhints_flags;
317 int old_width_inc;
318 int old_height_inc;
319 int old_base_width;
320 int old_base_height;
322 #ifdef I18N_MB
323 Atom actual = None;
324 int actual_format;
325 unsigned long nitems, bytesafter;
326 char *prop = NULL;
327 char **list;
328 int num;
329 #endif
331 DBUG("HandlePropertyNotify","Routine Entered");
333 if ((!Tmp_win)||
334 (XGetGeometry(dpy, Tmp_win->w, &JunkRoot, &JunkX, &JunkY,
335 &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) == 0))
336 return;
339 * Make sure at least part of window is on this page
340 * before giving it focus...
342 OnThisPage = IsRectangleOnThisPage(&(Tmp_win->frame_g), Tmp_win->Desk);
344 switch (Event.xproperty.atom)
346 case XA_WM_TRANSIENT_FOR:
348 if(XGetTransientForHint(dpy, Tmp_win->w, &Tmp_win->transientfor))
350 SET_TRANSIENT(Tmp_win, 1);
351 RaiseWindow(Tmp_win);
353 else
355 SET_TRANSIENT(Tmp_win, 0);
358 break;
360 case XA_WM_NAME:
361 #ifdef I18N_MB
362 if (XGetWindowProperty (dpy, Tmp_win->w, Event.xproperty.atom, 0L,
363 MAX_WINDOW_NAME_LEN, False, AnyPropertyType,
364 &actual, &actual_format, &nitems, &bytesafter,
365 (unsigned char **) &prop) != Success ||
366 actual == None)
367 return;
368 if (prop) {
369 if (actual == XA_STRING) {
370 /* STRING encoding, use this as it is */
371 free_window_names (Tmp_win, True, False);
372 Tmp_win->name = prop;
373 Tmp_win->name_list = NULL;
374 } else {
375 /* not STRING encoding, try to convert */
376 text_prop.value = prop;
377 text_prop.encoding = actual;
378 text_prop.format = actual_format;
379 text_prop.nitems = nitems;
380 if (
381 XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) >= Success
382 && num > 0 && *list) {
383 /* XXX: does not consider the conversion is REALLY succeeded */
384 XFree(prop); /* return of XGetWindowProperty() */
385 free_window_names (Tmp_win, True, False);
386 Tmp_win->name = *list;
387 Tmp_win->name_list = list;
388 } else {
389 if (list) XFreeStringList(list);
390 XFree(prop); /* return of XGetWindowProperty() */
391 if (!XGetWMName(dpy, Tmp_win->w, &text_prop))
392 return; /* why cannot read... */
393 free_window_names (Tmp_win, True, False);
394 Tmp_win->name = (char *)text_prop.value;
395 Tmp_win->name_list = NULL;
398 } else {
399 /* XXX: fallback to original behavior, is it needed ? */
400 if (!XGetWMName(dpy, Tmp_win->w, &text_prop))
401 return;
402 free_window_names (Tmp_win, True, False);
403 Tmp_win->name = (char *)text_prop.value;
404 Tmp_win->name_list = NULL;
406 #else
407 if (!XGetWMName(dpy, Tmp_win->w, &text_prop))
408 return;
409 free_window_names (Tmp_win, True, False);
410 Tmp_win->name = (char *)text_prop.value;
411 if (Tmp_win->name && strlen(Tmp_win->name) > MAX_WINDOW_NAME_LEN)
412 /* limit to prevent hanging X server */
413 Tmp_win->name[MAX_WINDOW_NAME_LEN] = 0;
414 #endif
416 SET_NAME_CHANGED(Tmp_win, 1);
418 if (Tmp_win->name == NULL)
419 Tmp_win->name = NoName;
420 BroadcastName(M_WINDOW_NAME,Tmp_win->w,Tmp_win->frame,
421 (unsigned long)Tmp_win,Tmp_win->name);
423 /* fix the name in the title bar */
424 if(!IS_ICONIFIED(Tmp_win))
425 DrawDecorations(
426 Tmp_win, DRAW_TITLE, (Scr.Hilite == Tmp_win), True, None);
429 * if the icon name is NoName, set the name of the icon to be
430 * the same as the window
432 if (Tmp_win->icon_name == NoName)
434 Tmp_win->icon_name = Tmp_win->name;
435 BroadcastName(M_ICON_NAME,Tmp_win->w,Tmp_win->frame,
436 (unsigned long)Tmp_win,Tmp_win->icon_name);
437 RedoIconName(Tmp_win);
439 break;
441 case XA_WM_ICON_NAME:
442 #ifdef I18N_MB
443 if (XGetWindowProperty (dpy, Tmp_win->w, Event.xproperty.atom, 0L,
444 MAX_ICON_NAME_LEN, False, AnyPropertyType,
445 &actual, &actual_format, &nitems, &bytesafter,
446 (unsigned char **) &prop) != Success ||
447 actual == None)
448 return;
449 if (prop) {
450 if (actual == XA_STRING) {
451 /* STRING encoding, use this as it is */
452 free_window_names (Tmp_win, False, True);
453 Tmp_win->icon_name = prop;
454 Tmp_win->icon_name_list = NULL;
455 } else {
456 /* not STRING encoding, try to convert */
457 text_prop.value = prop;
458 text_prop.encoding = actual;
459 text_prop.format = actual_format;
460 text_prop.nitems = nitems;
461 if (XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) >= Success
462 && num > 0 && *list) {
463 /* XXX: does not consider the conversion is REALLY succeeded */
464 XFree(prop); /* return of XGetWindowProperty() */
465 free_window_names (Tmp_win, False, True);
466 Tmp_win->icon_name = *list;
467 Tmp_win->icon_name_list = list;
468 } else {
469 if (list) XFreeStringList(list);
470 XFree(prop); /* return of XGetWindowProperty() */
471 if (!XGetWMIconName (dpy, Tmp_win->w, &text_prop))
472 return; /* why cannot read... */
473 free_window_names (Tmp_win, False, True);
474 Tmp_win->icon_name = (char *)text_prop.value;
475 Tmp_win->icon_name_list = NULL;
478 } else {
479 /* XXX: fallback to original behavior, is it needed ? */
480 if (!XGetWMIconName(dpy, Tmp_win->w, &text_prop))
481 return;
482 free_window_names (Tmp_win, False, True);
483 Tmp_win->icon_name = (char *)text_prop.value;
484 Tmp_win->icon_name_list = NULL;
486 #else
487 if (!XGetWMIconName (dpy, Tmp_win->w, &text_prop))
488 return;
489 free_window_names (Tmp_win, False, True);
490 Tmp_win->icon_name = (char *) text_prop.value;
491 if (Tmp_win->icon_name && strlen(Tmp_win->icon_name) >
492 MAX_ICON_NAME_LEN)
493 /* limit to prevent hanging X server */
494 Tmp_win->icon_name[MAX_ICON_NAME_LEN] = 0;
495 #endif
496 if (Tmp_win->icon_name == NULL)
497 Tmp_win->icon_name = NoName;
498 BroadcastName(M_ICON_NAME,Tmp_win->w,Tmp_win->frame,
499 (unsigned long)Tmp_win,Tmp_win->icon_name);
500 RedoIconName(Tmp_win);
501 break;
503 case XA_WM_HINTS:
504 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
505 the urgency flag is an ICCCM 2.0 addition to the WM_HINTS. */
506 old_wmhints_flags = 0;
507 if (Tmp_win->wmhints) {
508 old_wmhints_flags = Tmp_win->wmhints->flags;
509 XFree ((char *) Tmp_win->wmhints);
511 Tmp_win->wmhints = XGetWMHints(dpy, Event.xany.window);
513 if(Tmp_win->wmhints == NULL)
514 return;
517 * rebuild icon if the client either provides an icon
518 * pixmap or window or has reset the hints to `no icon'.
520 if ((Tmp_win->wmhints->flags & (IconPixmapHint|IconWindowHint)) ||
521 ((old_wmhints_flags & (IconPixmapHint|IconWindowHint)) !=
522 (Tmp_win->wmhints->flags & (IconPixmapHint|IconWindowHint))))
524 if(Tmp_win->icon_bitmap_file == Scr.DefaultIcon)
525 Tmp_win->icon_bitmap_file = NULL;
526 if(!Tmp_win->icon_bitmap_file &&
527 !(Tmp_win->wmhints->flags&(IconPixmapHint|IconWindowHint)))
529 Tmp_win->icon_bitmap_file =
530 (Scr.DefaultIcon) ? strdup(Scr.DefaultIcon) : NULL;
533 if (!IS_ICON_SUPPRESSED(Tmp_win) ||
534 (Tmp_win->wmhints->flags & IconWindowHint))
536 if (Tmp_win->icon_w)
537 XDestroyWindow(dpy,Tmp_win->icon_w);
538 XDeleteContext(dpy, Tmp_win->icon_w, FvwmContext);
539 if(IS_ICON_OURS(Tmp_win))
541 if(Tmp_win->icon_pixmap_w != None)
543 XDestroyWindow(dpy,Tmp_win->icon_pixmap_w);
544 XDeleteContext(dpy, Tmp_win->icon_pixmap_w, FvwmContext);
547 else
548 XUnmapWindow(dpy,Tmp_win->icon_pixmap_w);
550 Tmp_win->icon_w = None;
551 Tmp_win->icon_pixmap_w = None;
552 Tmp_win->iconPixmap = (Window)NULL;
553 if(IS_ICONIFIED(Tmp_win))
555 SET_ICONIFIED(Tmp_win, 0);
556 SET_ICON_UNMAPPED(Tmp_win, 0);
557 CreateIconWindow(Tmp_win,
558 Tmp_win->icon_g.x,Tmp_win->icon_g.y);
559 BroadcastPacket(M_ICONIFY, 7,
560 Tmp_win->w, Tmp_win->frame,
561 (unsigned long)Tmp_win,
562 Tmp_win->icon_g.x, Tmp_win->icon_g.y,
563 Tmp_win->icon_g.width, Tmp_win->icon_g.height);
564 /* domivogt (15-Sep-1999): BroadcastConfig informs modules of the
565 * configuration change including the iconified flag. So this
566 * flag must be set here. I'm not sure if the two calls of the
567 * SET_ICONIFIED macro after BroadcastConfig are necessary, but
568 * since it's only minimal overhead I prefer to be on the safe
569 * side. */
570 SET_ICONIFIED(Tmp_win, 1);
571 BroadcastConfig(M_CONFIGURE_WINDOW, Tmp_win);
572 SET_ICONIFIED(Tmp_win, 0);
574 if (!IS_ICON_SUPPRESSED(Tmp_win))
576 LowerWindow(Tmp_win);
577 AutoPlaceIcon(Tmp_win);
578 if(Tmp_win->Desk == Scr.CurrentDesk)
580 if(Tmp_win->icon_w)
581 XMapWindow(dpy, Tmp_win->icon_w);
582 if(Tmp_win->icon_pixmap_w != None)
583 XMapWindow(dpy, Tmp_win->icon_pixmap_w);
586 SET_ICONIFIED(Tmp_win, 1);
587 DrawIconWindow(Tmp_win);
591 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
592 the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
593 Treat urgency changes by calling user-settable functions.
594 These could e.g. deiconify and raise the window or temporarily
595 change the decor. */
596 if (!(old_wmhints_flags & XUrgencyHint) &&
597 (Tmp_win->wmhints->flags & XUrgencyHint))
599 ExecuteFunction(
600 "Function UrgencyFunc", Tmp_win, &Event, C_WINDOW, -1, 0, NULL);
603 if ((old_wmhints_flags & XUrgencyHint) &&
604 !(Tmp_win->wmhints->flags & XUrgencyHint))
606 ExecuteFunction(
607 "Function UrgencyDoneFunc", Tmp_win, &Event, C_WINDOW, -1, 0, NULL);
609 break;
610 case XA_WM_NORMAL_HINTS:
611 was_size_inc_set = IS_SIZE_INC_SET(Tmp_win);
612 old_width_inc = Tmp_win->hints.width_inc;
613 old_height_inc = Tmp_win->hints.height_inc;
614 old_base_width = Tmp_win->hints.base_width;
615 old_base_height = Tmp_win->hints.base_height;
616 GetWindowSizeHints(Tmp_win);
617 if (old_width_inc != Tmp_win->hints.width_inc ||
618 old_height_inc != Tmp_win->hints.height_inc)
620 int units_w;
621 int units_h;
622 int wdiff;
623 int hdiff;
625 if (!was_size_inc_set && old_width_inc == 1 && old_height_inc == 1)
627 /* This is a hack for xvile. It sets the _inc hints after it
628 * requested that the window is mapped but before it's really
629 * visible. */
630 /* do nothing */
632 else
634 /* we have to resize the unmaximized window to keep the size in
635 * resize increments constant */
636 units_w = Tmp_win->normal_g.width - 2 * Tmp_win->boundary_width -
637 old_base_width;
638 units_h = Tmp_win->normal_g.height - Tmp_win->title_g.height -
639 2 * Tmp_win->boundary_width - old_base_height;
640 units_w /= old_width_inc;
641 units_h /= old_height_inc;
643 /* update the 'invisible' geometry */
644 wdiff = units_w * (Tmp_win->hints.width_inc - old_width_inc) +
645 (Tmp_win->hints.base_width - old_base_width);
646 hdiff = units_h * (Tmp_win->hints.height_inc - old_height_inc) +
647 (Tmp_win->hints.base_height - old_base_height);
648 gravity_resize(
649 Tmp_win->hints.win_gravity, &Tmp_win->normal_g, wdiff, hdiff);
651 gravity_constrain_size(
652 Tmp_win->hints.win_gravity, Tmp_win, &Tmp_win->normal_g);
653 if (!IS_MAXIMIZED(Tmp_win))
655 rectangle new_g;
657 get_relative_geometry(&new_g, &Tmp_win->normal_g);
658 if (IS_SHADED(Tmp_win))
659 get_shaded_geometry(Tmp_win, &new_g, &new_g);
660 ForceSetupFrame(
661 Tmp_win, new_g.x, new_g.y, new_g.width, new_g.height, False);
663 else
665 int w;
666 int h;
668 maximize_adjust_offset(Tmp_win);
669 /* domivogt (07-Apr-2000): as terrible hack to work around a xterm
670 * bug: when the font size is changed in a xterm, xterm simply assumes
671 * that the wm will grant its new size. Of course this is wrong if
672 * the xterm is maximised. To make xterm happy, we first send a
673 * ConfigureNotify with the current (maximised) geometry + 1 pixel in
674 * height, then another one with the correct old geometry. Changing
675 * the font multiple times will cause the xterm to shrink because
676 * gravity_constrain_size doesn't know about the initially requested
677 * dimensions. */
678 w = Tmp_win->max_g.width;
679 h = Tmp_win->max_g.height;
680 gravity_constrain_size(
681 Tmp_win->hints.win_gravity, Tmp_win, &Tmp_win->max_g);
682 if (w != Tmp_win->max_g.width ||
683 h != Tmp_win->max_g.height)
685 rectangle new_g;
687 /* This is in case the size_inc changed and the old dimensions are
688 * not multiples of the new values. */
689 get_relative_geometry(&new_g, &Tmp_win->max_g);
690 if (IS_SHADED(Tmp_win))
691 get_shaded_geometry(Tmp_win, &new_g, &new_g);
692 ForceSetupFrame(
693 Tmp_win, new_g.x, new_g.y, new_g.width, new_g.height, False);
695 else
697 SendConfigureNotify(
698 Tmp_win, Tmp_win->frame_g.x, Tmp_win->frame_g.y,
699 Tmp_win->frame_g.width, Tmp_win->frame_g.height+1, 0, False);
700 XSync(dpy, 0);
701 /* free some CPU */
702 usleep(1);
703 SendConfigureNotify(
704 Tmp_win, Tmp_win->frame_g.x, Tmp_win->frame_g.y,
705 Tmp_win->frame_g.width, Tmp_win->frame_g.height, 0, False);
706 XSync(dpy, 0);
709 GNOME_SetWinArea(Tmp_win);
711 BroadcastConfig(M_CONFIGURE_WINDOW,Tmp_win);
712 break;
714 default:
715 if(Event.xproperty.atom == _XA_WM_PROTOCOLS)
716 FetchWmProtocols (Tmp_win);
717 else if (Event.xproperty.atom == _XA_WM_COLORMAP_WINDOWS)
719 FetchWmColormapWindows (Tmp_win); /* frees old data */
720 ReInstallActiveColormap();
722 else if(Event.xproperty.atom == _XA_WM_STATE)
724 if((Tmp_win != NULL)&&(HAS_CLICK_FOCUS(Tmp_win))
725 &&(Tmp_win == Scr.Focus))
727 if (OnThisPage)
729 Scr.Focus = NULL;
730 SetFocus(Tmp_win->w,Tmp_win,0);
734 break;
739 /***********************************************************************
741 * Procedure:
742 * HandleClientMessage - client message event handler
744 ************************************************************************/
745 void HandleClientMessage(void)
747 XEvent button;
749 DBUG("HandleClientMessage","Routine Entered");
751 /* Process GNOME Messages */
752 if (GNOME_ProcessClientMessage(Tmp_win, &Event))
754 return;
757 if ((Event.xclient.message_type == _XA_WM_CHANGE_STATE)&&
758 (Tmp_win)&&(Event.xclient.data.l[0]==IconicState)&&
759 !IS_ICONIFIED(Tmp_win))
761 XQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild,
762 &(button.xmotion.x_root),
763 &(button.xmotion.y_root),
764 &JunkX, &JunkY, &JunkMask);
765 button.type = 0;
766 ExecuteFunction("Iconify", Tmp_win, &button, C_FRAME, -1, 0, NULL);
767 return;
770 /* FIXME: Is this safe enough ? I guess if clients behave
771 according to ICCCM and send these messages only if they
772 when grabbed the pointer, it is OK */
774 extern Atom _XA_WM_COLORMAP_NOTIFY;
775 if (Event.xclient.message_type == _XA_WM_COLORMAP_NOTIFY) {
776 set_client_controls_colormaps(Event.xclient.data.l[1]);
777 return;
782 ** CKH - if we get here, it was an unknown client message, so send
783 ** it to the client if it was in a window we know about. I'm not so
784 ** sure this should be done or not, since every other window manager
785 ** I've looked at doesn't. But it might be handy for a free drag and
786 ** drop setup being developed for Linux.
788 if (Tmp_win)
790 if(Event.xclient.window != Tmp_win->w)
792 Event.xclient.window = Tmp_win->w;
793 XSendEvent(dpy, Tmp_win->w, False, NoEventMask, &Event);
798 /***********************************************************************
800 * Procedure:
801 * HandleExpose - expose event handler
803 ***********************************************************************/
804 void HandleExpose(void)
806 if (Event.xexpose.count != 0)
807 return;
809 DBUG("HandleExpose","Routine Entered");
811 if (Tmp_win)
813 draw_window_parts draw_parts;
815 if (Event.xany.window == Tmp_win->title_w)
816 draw_parts = DRAW_TITLE;
817 else if (Event.xany.window == Tmp_win->decor_w ||
818 Event.xany.window == Tmp_win->frame)
819 draw_parts = DRAW_FRAME;
820 else
821 draw_parts = DRAW_BUTTONS;
822 DrawDecorations(
823 Tmp_win, draw_parts, (Scr.Hilite == Tmp_win), True, Event.xany.window);
825 return;
830 /***********************************************************************
832 * Procedure:
833 * HandleDestroyNotify - DestroyNotify event handler
835 ***********************************************************************/
836 void HandleDestroyNotify(void)
838 DBUG("HandleDestroyNotify","Routine Entered");
840 destroy_window(Tmp_win);
841 GNOME_SetClientList();
847 /***********************************************************************
849 * Procedure:
850 * HandleMapRequest - MapRequest event handler
852 ************************************************************************/
853 void HandleMapRequest(void)
855 DBUG("HandleMapRequest","Routine Entered");
857 if (fFvwmInStartup)
859 /* Just map the damn thing, decorations are added later
860 * in CaptureAllWindows. */
861 XMapWindow (dpy, Event.xmaprequest.window);
862 return;
864 HandleMapRequestKeepRaised(None, NULL);
866 void HandleMapRequestKeepRaised(Window KeepRaised, FvwmWindow *ReuseWin)
868 extern long isIconicState;
869 extern Bool isIconifiedByParent;
870 extern Boolean PPosOverride;
871 Bool OnThisPage = False;
873 Event.xany.window = Event.xmaprequest.window;
875 if (ReuseWin == NULL)
877 if(XFindContext(dpy, Event.xany.window, FvwmContext,
878 (caddr_t *)&Tmp_win)==XCNOENT)
880 Tmp_win = NULL;
883 else
885 Tmp_win = ReuseWin;
888 if(!PPosOverride)
889 XFlush(dpy);
891 /* If the window has never been mapped before ... */
892 if(!Tmp_win || (Tmp_win && DO_REUSE_DESTROYED(Tmp_win)))
894 /* Add decorations. */
895 Tmp_win = AddWindow(Event.xany.window, ReuseWin);
896 if (Tmp_win == NULL)
897 return;
900 * Make sure at least part of window is on this page
901 * before giving it focus...
903 OnThisPage = IsRectangleOnThisPage(&(Tmp_win->frame_g), Tmp_win->Desk);
905 if(KeepRaised != None)
906 XRaiseWindow(dpy,KeepRaised);
907 /* If it's not merely iconified, and we have hints, use them. */
908 if (!IS_ICONIFIED(Tmp_win))
910 int state;
912 if(Tmp_win->wmhints && (Tmp_win->wmhints->flags & StateHint))
913 state = Tmp_win->wmhints->initial_state;
914 else
915 state = NormalState;
917 if(DO_START_ICONIC(Tmp_win))
918 state = IconicState;
920 if(isIconicState != DontCareState)
921 state = isIconicState;
923 MyXGrabServer(dpy);
924 switch (state)
926 case DontCareState:
927 case NormalState:
928 case InactiveState:
929 default:
930 if (Tmp_win->Desk == Scr.CurrentDesk)
932 XMapWindow(dpy, Tmp_win->w);
933 XMapWindow(dpy, Tmp_win->frame);
934 SET_MAP_PENDING(Tmp_win, 1);
935 SetMapStateProp(Tmp_win, NormalState);
936 if(((!IS_TRANSIENT(Tmp_win) ||
937 Tmp_win->transientfor == Scr.Root) &&
938 DO_GRAB_FOCUS(Tmp_win)) ||
939 (IS_TRANSIENT(Tmp_win) && DO_GRAB_FOCUS_TRANSIENT(Tmp_win) &&
940 Scr.Focus && Scr.Focus->w == Tmp_win->transientfor))
942 if (OnThisPage)
944 SetFocus(Tmp_win->w, Tmp_win, 1);
948 else
950 XMapWindow(dpy, Tmp_win->w);
951 SetMapStateProp(Tmp_win, NormalState);
953 break;
955 case IconicState:
956 if (isIconifiedByParent)
958 isIconifiedByParent = False;
959 SET_ICONIFIED_BY_PARENT(Tmp_win, 1);
961 if (Tmp_win->wmhints)
963 Iconify(Tmp_win, Tmp_win->wmhints->icon_x,
964 Tmp_win->wmhints->icon_y);
966 else
968 Iconify(Tmp_win, 0, 0);
970 break;
972 if(!PPosOverride)
973 XSync(dpy,0);
974 MyXUngrabServer(dpy);
976 /* If no hints, or currently an icon, just "deiconify" */
977 else
979 DeIconify(Tmp_win);
981 if (IS_SHADED(Tmp_win))
983 BroadcastPacket(M_WINDOWSHADE, 3, Tmp_win->w, Tmp_win->frame,
984 (unsigned long)Tmp_win);
987 if (!IS_ICONIFIED(Tmp_win) && Scr.Focus && Scr.Focus != Tmp_win &&
988 !is_on_top_of_layer(Scr.Focus))
990 if (Tmp_win->Desk == Scr.CurrentDesk &&
991 Tmp_win->frame_g.x + Tmp_win->frame_g.width > Scr.Focus->frame_g.x &&
992 Scr.Focus->frame_g.x + Scr.Focus->frame_g.width > Tmp_win->frame_g.x &&
993 Tmp_win->frame_g.y + Tmp_win->frame_g.height > Scr.Focus->frame_g.y &&
994 Scr.Focus->frame_g.y + Scr.Focus->frame_g.height > Tmp_win->frame_g.y)
996 /* The newly mapped window overlaps the focused window. Make sure
997 * ClickToFocusRaises and MouseFocusClickRaises work again.
999 * Note: There are many conditions under which we do not have to call
1000 * focus_grab_buttons(), but it is not worth the effort to write them
1001 * down here. Rather do some unnecessary work in this function. */
1002 focus_grab_buttons(Scr.Focus, True);
1006 /* Just to be on the safe side, we make sure that STARTICONIC
1007 can only influence the initial transition from withdrawn state. */
1008 SET_DO_START_ICONIC(Tmp_win, 0);
1009 if (DO_DELETE_ICON_MOVED(Tmp_win))
1011 SET_DELETE_ICON_MOVED(Tmp_win, 0);
1012 SET_ICON_MOVED(Tmp_win, 0);
1014 /* Clean out the global so that it isn't used on additional map events. */
1015 isIconicState = DontCareState;
1016 GNOME_SetClientList();
1020 /***********************************************************************
1022 * Procedure:
1023 * HandleMapNotify - MapNotify event handler
1025 ***********************************************************************/
1026 void HandleMapNotify(void)
1028 Bool OnThisPage = False;
1030 DBUG("HandleMapNotify","Routine Entered");
1032 if (!Tmp_win)
1034 if((Event.xmap.override_redirect == True)&&
1035 (Event.xmap.window != Scr.NoFocusWin))
1037 XSelectInput(dpy,Event.xmap.window,FocusChangeMask);
1038 Scr.UnknownWinFocused = Event.xmap.window;
1040 return;
1043 /* Except for identifying over-ride redirect window mappings, we
1044 * don't need or want windows associated with the substructurenotifymask */
1045 if(Event.xmap.event != Event.xmap.window)
1046 return;
1048 SET_MAP_PENDING(Tmp_win, 0);
1049 /* don't map if the event was caused by a de-iconify */
1050 if (IS_DEICONIFY_PENDING(Tmp_win))
1052 return;
1056 Make sure at least part of window is on this page
1057 before giving it focus...
1059 OnThisPage = IsRectangleOnThisPage(&(Tmp_win->frame_g), Tmp_win->Desk);
1062 * Need to do the grab to avoid race condition of having server send
1063 * MapNotify to client before the frame gets mapped; this is bad because
1064 * the client would think that the window has a chance of being viewable
1065 * when it really isn't.
1067 MyXGrabServer (dpy);
1068 if (Tmp_win->icon_w)
1069 XUnmapWindow(dpy, Tmp_win->icon_w);
1070 if(Tmp_win->icon_pixmap_w != None)
1071 XUnmapWindow(dpy, Tmp_win->icon_pixmap_w);
1072 XMapSubwindows(dpy, Tmp_win->frame);
1073 XMapSubwindows(dpy, Tmp_win->decor_w);
1075 if(Tmp_win->Desk == Scr.CurrentDesk)
1077 XMapWindow(dpy, Tmp_win->frame);
1080 if(IS_ICONIFIED(Tmp_win))
1081 BroadcastPacket(M_DEICONIFY, 3,
1082 Tmp_win->w, Tmp_win->frame, (unsigned long)Tmp_win);
1083 else
1084 BroadcastPacket(M_MAP, 3,
1085 Tmp_win->w,Tmp_win->frame, (unsigned long)Tmp_win);
1087 if((!IS_TRANSIENT(Tmp_win) && DO_GRAB_FOCUS(Tmp_win)) ||
1088 (IS_TRANSIENT(Tmp_win) && DO_GRAB_FOCUS_TRANSIENT(Tmp_win) &&
1089 Scr.Focus && Scr.Focus->w == Tmp_win->transientfor))
1091 if (OnThisPage)
1093 SetFocus(Tmp_win->w,Tmp_win,1);
1096 if((!(HAS_BORDER(Tmp_win)|HAS_TITLE(Tmp_win)))&&(Tmp_win->boundary_width <2))
1098 DrawDecorations(Tmp_win, DRAW_ALL, False, True, Tmp_win->decor_w);
1100 XSync(dpy,0);
1101 MyXUngrabServer (dpy);
1102 XFlush (dpy);
1103 SET_MAPPED(Tmp_win, 1);
1104 SET_ICONIFIED(Tmp_win, 0);
1105 SET_ICON_UNMAPPED(Tmp_win, 0);
1106 if (DO_ICONIFY_AFTER_MAP(Tmp_win))
1108 /* finally, if iconification was requested before the window was mapped,
1109 * request it now. */
1110 Iconify(Tmp_win, 0, 0);
1111 SET_ICONIFY_AFTER_MAP(Tmp_win, 0);
1116 /***********************************************************************
1118 * Procedure:
1119 * HandleUnmapNotify - UnmapNotify event handler
1121 ************************************************************************/
1122 void HandleUnmapNotify(void)
1124 int dstx, dsty;
1125 Window dumwin;
1126 XEvent dummy;
1127 extern FvwmWindow *colormap_win;
1128 int weMustUnmap;
1129 int focus_grabbed = 0;
1130 Bool must_return = False;
1132 DBUG("HandleUnmapNotify","Routine Entered");
1135 * Don't ignore events as described below.
1137 if(Event.xunmap.event != Event.xunmap.window &&
1138 (Event.xunmap.event != Scr.Root || !Event.xunmap.send_event))
1140 must_return = True;
1144 * The July 27, 1988 ICCCM spec states that a client wishing to switch
1145 * to WithdrawnState should send a synthetic UnmapNotify with the
1146 * event field set to (pseudo-)root, in case the window is already
1147 * unmapped (which is the case for fvwm for IconicState). Unfortunately,
1148 * we looked for the FvwmContext using that field, so try the window
1149 * field also.
1151 weMustUnmap = 0;
1152 if (!Tmp_win)
1154 Event.xany.window = Event.xunmap.window;
1155 weMustUnmap = 1;
1156 if (XFindContext(dpy, Event.xany.window,
1157 FvwmContext, (caddr_t *)&Tmp_win) == XCNOENT)
1158 Tmp_win = NULL;
1161 if(!Tmp_win)
1162 return;
1164 if (Event.xunmap.window == Tmp_win->frame)
1166 SET_DEICONIFY_PENDING(Tmp_win , 0);
1169 if (must_return)
1170 return;
1172 if(weMustUnmap)
1173 XUnmapWindow(dpy, Event.xunmap.window);
1175 if(Tmp_win == Scr.Hilite)
1176 Scr.Hilite = NULL;
1178 if(Scr.PreviousFocus == Tmp_win)
1179 Scr.PreviousFocus = NULL;
1181 focus_grabbed = (Tmp_win == Scr.Focus) &&
1182 ((!IS_TRANSIENT(Tmp_win) && DO_GRAB_FOCUS(Tmp_win)) ||
1183 (IS_TRANSIENT(Tmp_win) && DO_GRAB_FOCUS_TRANSIENT(Tmp_win)));
1185 if((Tmp_win == Scr.Focus)&&(HAS_CLICK_FOCUS(Tmp_win)))
1187 if(Tmp_win->next)
1188 SetFocus(Tmp_win->next->w, Tmp_win->next, 1);
1189 else
1190 SetFocus(Scr.NoFocusWin,NULL,1);
1193 if(Scr.Focus == Tmp_win)
1194 SetFocus(Scr.NoFocusWin,NULL,1);
1196 if(Tmp_win == Scr.pushed_window)
1197 Scr.pushed_window = NULL;
1199 if(Tmp_win == colormap_win)
1200 colormap_win = NULL;
1202 if (!IS_MAPPED(Tmp_win) && !IS_ICONIFIED(Tmp_win))
1204 return;
1207 MyXGrabServer(dpy);
1209 if(XCheckTypedWindowEvent (dpy, Event.xunmap.window, DestroyNotify,&dummy))
1211 destroy_window(Tmp_win);
1213 else
1215 * The program may have unmapped the client window, from either
1216 * NormalState or IconicState. Handle the transition to WithdrawnState.
1218 * We need to reparent the window back to the root (so that fvwm exiting
1219 * won't cause it to get mapped) and then throw away all state (pretend
1220 * that we've received a DestroyNotify).
1222 if (XTranslateCoordinates (dpy, Event.xunmap.window, Scr.Root,
1223 0, 0, &dstx, &dsty, &dumwin))
1225 XEvent ev;
1226 Bool reparented;
1228 reparented = XCheckTypedWindowEvent (dpy, Event.xunmap.window,
1229 ReparentNotify, &ev);
1230 SetMapStateProp (Tmp_win, WithdrawnState);
1231 if (reparented)
1233 if (Tmp_win->old_bw)
1234 XSetWindowBorderWidth (dpy, Event.xunmap.window, Tmp_win->old_bw);
1235 if((!IS_ICON_SUPPRESSED(Tmp_win))&&
1236 (Tmp_win->wmhints && (Tmp_win->wmhints->flags & IconWindowHint)))
1237 XUnmapWindow (dpy, Tmp_win->wmhints->icon_window);
1239 else
1241 RestoreWithdrawnLocation (Tmp_win,False);
1243 XRemoveFromSaveSet (dpy, Event.xunmap.window);
1244 XSelectInput (dpy, Event.xunmap.window, NoEventMask);
1245 destroy_window(Tmp_win); /* do not need to mash event before */
1247 * Flush any pending events for the window.
1249 /* Bzzt! it could be about to re-map */
1250 /* while(XCheckWindowEvent(dpy, Event.xunmap.window,
1251 StructureNotifyMask | PropertyChangeMask |
1252 ColormapChangeMask | VisibilityChangeMask |
1253 EnterWindowMask | LeaveWindowMask, &dummy));
1255 } /* else window no longer exists and we'll get a destroy notify */
1256 MyXUngrabServer(dpy);
1258 XFlush (dpy);
1260 if (focus_grabbed)
1262 CoerceEnterNotifyOnCurrentWindow();
1264 GNOME_SetClientList();
1268 /***********************************************************************
1270 * Procedure:
1271 * HandleButtonPress - ButtonPress event handler
1273 ***********************************************************************/
1274 void HandleButtonPress(void)
1276 int LocalContext;
1277 char *action;
1278 Window OldPressedW;
1279 Window eventw;
1281 DBUG("HandleButtonPress","Routine Entered");
1283 if (!Tmp_win && Event.xany.window != Scr.Root)
1285 /* event in unmanaged window or subwindow of a client */
1286 XSync(dpy,0);
1287 XAllowEvents(dpy,ReplayPointer,CurrentTime);
1288 XSync(dpy,0);
1289 return;
1291 if (Event.xbutton.subwindow != None &&
1292 (Tmp_win == None || Event.xany.window != Tmp_win->w))
1294 eventw = Event.xbutton.subwindow;
1296 else
1298 eventw = Event.xany.window;
1300 if (!XGetGeometry(dpy, eventw, &JunkRoot, &JunkX, &JunkY,
1301 &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth))
1303 /* The window has already died. Just pass the event to the application. */
1304 XSync(dpy,0);
1305 XAllowEvents(dpy,ReplayPointer,CurrentTime);
1306 XSync(dpy,0);
1307 return;
1309 if (Tmp_win && HAS_NEVER_FOCUS(Tmp_win))
1311 /* It might seem odd to try to focus a window that never is given focus by
1312 * fvwm, but the window might want to take focus itself, and SetFocus will
1313 * tell it to do so in this case instead of giving it focus. */
1314 SetFocus(Tmp_win->w, Tmp_win, 1);
1316 /* click to focus stuff goes here */
1317 if((Tmp_win)&&(HAS_CLICK_FOCUS(Tmp_win))&&(Tmp_win != Scr.Ungrabbed))
1319 SetFocus(Tmp_win->w,Tmp_win,1);
1320 /* RBW - 12/09/.1999- I'm not sure we need to check both cases, but
1321 I'll leave this as is for now. */
1322 if (!DO_NOT_RAISE_CLICK_FOCUS_CLICK(Tmp_win)
1323 #if 0
1324 /* DV - this forces that every focus click on the decorations raises
1325 * the window. This somewhat negates the ClickToFocusRaisesOff style.
1328 ((Event.xany.window != Tmp_win->w)&&
1329 (Event.xbutton.subwindow != Tmp_win->w)&&
1330 (Event.xany.window != Tmp_win->Parent)&&
1331 (Event.xbutton.subwindow != Tmp_win->Parent))
1332 #endif
1335 /* We can't raise the window immediately because the action bound to the
1336 * click might be "Lower" or "RaiseLower". So mark the window as scheduled
1337 * to be raised after the binding is executed. Functions that modify the
1338 * stacking order will reset this flag. */
1339 SET_SCHEDULED_FOR_RAISE(Tmp_win, 1);
1342 Context = GetContext(Tmp_win,&Event, &PressedW);
1343 if (!IS_ICONIFIED(Tmp_win) && Context == C_WINDOW)
1345 if (Tmp_win && IS_SCHEDULED_FOR_RAISE(Tmp_win))
1347 RaiseWindow(Tmp_win);
1348 SET_SCHEDULED_FOR_RAISE(Tmp_win, 0);
1350 XSync(dpy,0);
1351 /* pass click event to just clicked to focus window? Do not swallow the
1352 * click if the window didn't accept the focus */
1353 if (!DO_NOT_PASS_CLICK_FOCUS_CLICK(Tmp_win) || Scr.Focus != Tmp_win)
1355 XAllowEvents(dpy,ReplayPointer,CurrentTime);
1357 else /* don't pass click to just focused window */
1359 XAllowEvents(dpy,AsyncPointer,CurrentTime);
1361 XSync(dpy,0);
1362 return;
1364 if (!IS_ICONIFIED(Tmp_win))
1366 DrawDecorations(Tmp_win, DRAW_ALL, True, True, PressedW);
1369 else if (Tmp_win && Event.xbutton.window == Tmp_win->Parent &&
1370 (HAS_SLOPPY_FOCUS(Tmp_win) || HAS_MOUSE_FOCUS(Tmp_win) ||
1371 HAS_NEVER_FOCUS(Tmp_win)) &&
1372 DO_RAISE_MOUSE_FOCUS_CLICK(Tmp_win))
1374 FvwmWindow *tmp = Scr.Ungrabbed;
1377 RBW - Release the Parent grab here (whether we raise or not). We
1378 have to wait till this point or we would miss the raise click, which
1379 is not contemporaneous with the focus change.
1380 Scr.Ungrabbed should always be NULL here. I don't know anything
1381 useful we could do if it's not, other than ignore this window.
1383 if (!is_on_top_of_layer(Tmp_win) &&
1384 MaskUsedModifiers(Event.xbutton.state) == 0)
1386 RaiseWindow(Tmp_win);
1387 focus_grab_buttons(Tmp_win, True);
1388 Scr.Ungrabbed = tmp;
1389 XSync(dpy,0);
1390 XAllowEvents(dpy,ReplayPointer,CurrentTime);
1391 XSync(dpy,0);
1392 return;
1394 focus_grab_buttons(Tmp_win, True);
1395 Scr.Ungrabbed = tmp;
1398 XSync(dpy,0);
1399 XAllowEvents(dpy,ReplayPointer,CurrentTime);
1400 XSync(dpy,0);
1402 Context = GetContext(Tmp_win, &Event, &PressedW);
1403 LocalContext = Context;
1404 if (Tmp_win)
1406 if (Context == C_TITLE)
1407 DrawDecorations(
1408 Tmp_win, DRAW_TITLE, (Scr.Hilite == Tmp_win), True, None);
1409 else if (Context & (C_LALL | C_RALL))
1410 DrawDecorations(
1411 Tmp_win, DRAW_BUTTONS, (Scr.Hilite == Tmp_win), True, PressedW);
1412 else
1414 DrawDecorations(
1415 Tmp_win, DRAW_FRAME, (Scr.Hilite == Tmp_win),
1416 (HAS_DEPRESSABLE_BORDER(Tmp_win) && PressedW != None), PressedW);
1420 ButtonWindow = Tmp_win;
1422 /* we have to execute a function or pop up a menu */
1423 STROKE_CODE(stroke_init());
1424 STROKE_CODE(send_motion = TRUE);
1425 /* need to search for an appropriate mouse binding */
1426 action = CheckBinding(Scr.AllBindings, STROKE_ARG(0) Event.xbutton.button,
1427 Event.xbutton.state, GetUnusedModifiers(), Context,
1428 MOUSE_BINDING);
1429 if (action != NULL && (action[0] != 0))
1431 ExecuteFunction(
1432 action, Tmp_win, &Event, Context, -1, FUNC_DO_SYNC_BUTTONS, NULL);
1434 else
1437 * do gnome buttonpress forwarding if win == root
1439 if (Scr.Root == Event.xany.window)
1441 GNOME_ProxyButtonEvent(&Event);
1445 if (ButtonWindow && IS_SCHEDULED_FOR_RAISE(ButtonWindow))
1447 /* now that we know the action did not restack the window we can raise it.
1449 RaiseWindow(ButtonWindow);
1450 SET_SCHEDULED_FOR_RAISE(ButtonWindow, 0);
1453 OldPressedW = PressedW;
1454 PressedW = None;
1455 if (ButtonWindow && check_if_fvwm_window_exists(ButtonWindow))
1457 if (LocalContext == C_TITLE)
1458 DrawDecorations(
1459 ButtonWindow, DRAW_TITLE, (Scr.Hilite == ButtonWindow), True, None);
1460 else if (LocalContext & (C_LALL | C_RALL))
1461 DrawDecorations(
1462 ButtonWindow, DRAW_BUTTONS, (Scr.Hilite == ButtonWindow), True,
1463 OldPressedW);
1464 else
1465 DrawDecorations(
1466 ButtonWindow, DRAW_FRAME, (Scr.Hilite == ButtonWindow),
1467 HAS_DEPRESSABLE_BORDER(ButtonWindow), None);
1469 ButtonWindow = NULL;
1470 /* Release any automatic or passive grabs */
1471 XUngrabPointer(dpy, CurrentTime);
1474 #ifdef HAVE_STROKE
1475 /***********************************************************************
1477 * Procedure:
1478 * HandleButtonRelease - ButtonRelease event handler
1480 ************************************************************************/
1481 void HandleButtonRelease()
1483 char *action;
1484 int real_modifier;
1485 Window dummy;
1487 DBUG("HandleButtonRelease","Routine Entered");
1489 send_motion = FALSE;
1490 stroke_trans (sequence);
1492 DBUG("HandleButtonRelease",sequence);
1494 Context = GetContext(Tmp_win,&Event, &dummy);
1496 /* Allows modifier to work (Only R context works here). */
1497 real_modifier = Event.xbutton.state - (1 << (7 + Event.xbutton.button));
1499 /* need to search for an appropriate stroke binding */
1500 action = CheckBinding(
1501 Scr.AllBindings, sequence, Event.xbutton.button, real_modifier,
1502 GetUnusedModifiers(), Context, STROKE_BINDING);
1503 /* got a match, now process it */
1504 if (action != NULL && (action[0] != 0))
1506 ExecuteFunction(
1507 action, Tmp_win, &Event, Context, -1, FUNC_DO_SYNC_BUTTONS, NULL);
1509 else
1512 * do gnome buttonpress forwarding if win == root
1514 if (Scr.Root == Event.xany.window)
1516 GNOME_ProxyButtonEvent(&Event);
1522 /***********************************************************************
1524 * Procedure:
1525 * HandleMotionNotify - MotionNotify event handler
1527 ************************************************************************/
1528 void HandleMotionNotify()
1530 DBUG("HandleMotionNotify","Routine Entered");
1532 if (send_motion == TRUE)
1533 stroke_record (Event.xmotion.x,Event.xmotion.y);
1536 #endif /* HAVE_STROKE */
1538 /***********************************************************************
1540 * Procedure:
1541 * HandleEnterNotify - EnterNotify event handler
1543 ************************************************************************/
1544 void HandleEnterNotify(void)
1546 XEnterWindowEvent *ewp = &Event.xcrossing;
1547 XEvent d;
1549 DBUG("HandleEnterNotify","Routine Entered");
1551 /* Ignore EnterNotify events while a window is resized or moved as a wire
1552 * frame; otherwise the window list may be screwed up. */
1553 if (Scr.flags.is_wire_frame_displayed)
1554 return;
1556 /* look for a matching leaveNotify which would nullify this enterNotify */
1557 if(XCheckTypedWindowEvent (dpy, ewp->window, LeaveNotify, &d))
1560 * RBW - if we're in startup, this is a coerced focus, so we don't
1561 * want to save the event time, or exit prematurely.
1563 if (!fFvwmInStartup)
1565 StashEventTime(&d);
1566 if((d.xcrossing.mode==NotifyNormal)&&
1567 (d.xcrossing.detail!=NotifyInferior))
1568 return;
1572 /* an EnterEvent in one of the PanFrameWindows activates the Paging */
1573 if (ewp->window==Scr.PanFrameTop.win
1574 || ewp->window==Scr.PanFrameLeft.win
1575 || ewp->window==Scr.PanFrameRight.win
1576 || ewp->window==Scr.PanFrameBottom.win )
1578 int delta_x=0, delta_y=0;
1579 /* this was in the HandleMotionNotify before, HEDU */
1580 HandlePaging(Scr.EdgeScrollX,Scr.EdgeScrollY,
1581 &ewp->x_root,&ewp->y_root,
1582 &delta_x,&delta_y,True,True,False);
1583 return;
1586 if (ewp->window == Scr.Root)
1588 if (!Scr.Focus || HAS_MOUSE_FOCUS(Scr.Focus))
1590 SetFocus(Scr.NoFocusWin,NULL,1);
1592 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
1594 InstallWindowColormaps(NULL);
1596 return;
1599 /* make sure its for one of our windows */
1600 if (!Tmp_win)
1602 /* handle a subwindow cmap */
1603 EnterSubWindowColormap(Event.xany.window);
1604 return;
1607 if (HAS_MOUSE_FOCUS(Tmp_win) || HAS_SLOPPY_FOCUS(Tmp_win))
1609 SetFocus(Tmp_win->w,Tmp_win,1);
1611 else if (HAS_NEVER_FOCUS(Tmp_win))
1613 /* Give the window a chance to grab the buttons needed for raise-on-click */
1614 if (Scr.Focus != Tmp_win)
1616 focus_grab_buttons(Tmp_win, False);
1617 focus_grab_buttons(Scr.Focus, True);
1620 else if (HAS_CLICK_FOCUS(Tmp_win) && Tmp_win == Scr.Focus &&
1621 do_accept_input_focus(Tmp_win))
1623 /* We have to refresh the focus window here in case we left the focused
1624 * fvwm window. Motif apps may lose the input focus otherwise. But do not
1625 * try to refresh the focus of applications that want to handle it
1626 * themselves. */
1627 FOCUS_SET(Tmp_win->w);
1629 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
1631 if((!IS_ICONIFIED(Tmp_win))&&(Event.xany.window == Tmp_win->w))
1632 InstallWindowColormaps(Tmp_win);
1633 else
1634 InstallWindowColormaps(NULL);
1637 /* We get an EnterNotify with mode == UnGrab when fvwm releases
1638 the grab held during iconification. We have to ignore this,
1639 or icon title will be initially raised. */
1640 if (IS_ICONIFIED(Tmp_win) && (ewp->mode == NotifyNormal))
1642 SET_ICON_ENTERED(Tmp_win,1);
1643 DrawIconWindow(Tmp_win);
1646 return;
1650 /***********************************************************************
1652 * Procedure:
1653 * HandleLeaveNotify - LeaveNotify event handler
1655 ************************************************************************/
1656 void HandleLeaveNotify(void)
1658 DBUG("HandleLeaveNotify","Routine Entered");
1660 /* Ignore LeaveNotify events while a window is resized or moved as a wire
1661 * frame; otherwise the window list may be screwed up. */
1662 if (Scr.flags.is_wire_frame_displayed)
1663 return;
1665 /* CDE-like behaviour of raising the icon title if the icon
1666 gets the focus (in particular if the cursor is over the icon) */
1667 if (Tmp_win && IS_ICONIFIED(Tmp_win))
1669 SET_ICON_ENTERED(Tmp_win,0);
1670 DrawIconWindow (Tmp_win);
1673 /* If we leave the root window, then we're really moving
1674 * another screen on a multiple screen display, and we
1675 * need to de-focus and unhighlight to make sure that we
1676 * don't end up with more than one highlighted window at a time */
1677 if(Event.xcrossing.window == Scr.Root
1678 /* domivogt (16-May-2000): added this test because somehow fvwm sometimes
1679 * gets a LeaveNotify on the root window although it is single screen. */
1680 && Scr.NumberOfScreens > 1)
1682 if(Event.xcrossing.mode == NotifyNormal)
1684 if (Event.xcrossing.detail != NotifyInferior)
1686 if(Scr.Focus != NULL)
1687 SetFocus(Scr.NoFocusWin, NULL, 1);
1688 if(Scr.Hilite != NULL)
1689 DrawDecorations(Scr.Hilite, DRAW_ALL, False, True, None);
1693 else
1695 /* handle a subwindow cmap */
1696 LeaveSubWindowColormap(Event.xany.window);
1702 /***********************************************************************
1704 * Procedure:
1705 * HandleConfigureRequest - ConfigureRequest event handler
1707 ************************************************************************/
1708 void HandleConfigureRequest(void)
1710 rectangle new_g;
1711 int dx;
1712 int dy;
1713 int dw;
1714 int dh;
1715 int constr_w;
1716 int constr_h;
1717 int oldnew_w;
1718 int oldnew_h;
1719 XWindowChanges xwc;
1720 unsigned long xwcm;
1721 XConfigureRequestEvent *cre = &Event.xconfigurerequest;
1722 Bool do_send_event = False;
1724 DBUG("HandleConfigureRequest","Routine Entered");
1727 * Event.xany.window is Event.xconfigurerequest.parent, so Tmp_win will
1728 * be wrong
1730 Event.xany.window = cre->window; /* mash parent field */
1731 if (XFindContext (dpy, cre->window, FvwmContext, (caddr_t *) &Tmp_win) ==
1732 XCNOENT)
1733 Tmp_win = NULL;
1735 #define EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
1736 #ifdef EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
1737 /* merge all pending ConfigureRequests for the window into a single event */
1738 if (Tmp_win)
1740 XEvent e;
1741 XConfigureRequestEvent *ecre;
1743 /* free some CPU */
1744 usleep(1);
1745 while (XCheckTypedWindowEvent(dpy, cre->window, ConfigureRequest, &e))
1747 unsigned long vma;
1748 unsigned long vmo;
1749 const unsigned long xm = CWX | CWWidth;
1750 const unsigned long ym = CWY | CWHeight;
1752 ecre = &e.xconfigurerequest;
1753 vma = cre->value_mask & ecre->value_mask;
1754 vmo = cre->value_mask | ecre->value_mask;
1755 if (((vma & xm) == 0 && (vmo & xm) == xm) ||
1756 ((vma & ym) == 0 && (vmo & ym) == ym))
1758 /* can't merge events since location of window might get screwed up */
1759 XPutBackEvent(dpy, &e);
1760 break;
1762 if (ecre->value_mask & CWX)
1764 cre->value_mask |= CWX;
1765 cre->x = ecre->x;
1767 if (ecre->value_mask & CWY)
1769 cre->value_mask |= CWY;
1770 cre->y = ecre->y;
1772 if (ecre->value_mask & CWWidth)
1774 cre->value_mask |= CWWidth;
1775 cre->width = ecre->width;
1777 if (ecre->value_mask & CWHeight)
1779 cre->value_mask |= CWHeight;
1780 cre->height = ecre->height;
1782 if (ecre->value_mask & CWBorderWidth)
1784 cre->value_mask |= CWBorderWidth;
1785 cre->border_width = ecre->border_width;
1787 if (ecre->value_mask & CWStackMode)
1789 cre->value_mask &= !CWStackMode;
1790 cre->value_mask |= (ecre->value_mask & CWStackMode);
1791 cre->above = ecre->above;
1792 cre->detail = ecre->detail;
1794 } /* while */
1795 } /* if */
1796 #endif
1799 * According to the July 27, 1988 ICCCM draft, we should ignore size and
1800 * position fields in the WM_NORMAL_HINTS property when we map a window.
1801 * Instead, we'll read the current geometry. Therefore, we should respond
1802 * to configuration requests for windows which have never been mapped.
1804 if (!Tmp_win || cre->window == Tmp_win->icon_w ||
1805 cre->window == Tmp_win->icon_pixmap_w)
1807 xwcm = cre->value_mask &
1808 (CWX | CWY | CWWidth | CWHeight | CWBorderWidth);
1809 xwc.x = cre->x;
1810 xwc.y = cre->y;
1811 if((Tmp_win)&&((Tmp_win->icon_pixmap_w == cre->window)))
1813 Tmp_win->icon_p_height = cre->height+ cre->border_width +
1814 cre->border_width;
1816 else if((Tmp_win)&&((Tmp_win->icon_w == cre->window)))
1818 Tmp_win->icon_xl_loc = cre->x;
1819 Tmp_win->icon_g.x = cre->x +
1820 (Tmp_win->icon_g.width - Tmp_win->icon_p_width)/2;
1821 Tmp_win->icon_g.y = cre->y - Tmp_win->icon_p_height;
1822 if(!IS_ICON_UNMAPPED(Tmp_win))
1823 BroadcastPacket(M_ICON_LOCATION, 7,
1824 Tmp_win->w, Tmp_win->frame,
1825 (unsigned long)Tmp_win,
1826 Tmp_win->icon_g.x, Tmp_win->icon_g.y,
1827 Tmp_win->icon_p_width,
1828 Tmp_win->icon_g.height + Tmp_win->icon_p_height);
1830 xwc.width = cre->width;
1831 xwc.height = cre->height;
1832 xwc.border_width = cre->border_width;
1834 XConfigureWindow(dpy, Event.xany.window, xwcm, &xwc);
1836 if(Tmp_win)
1838 if (cre->window != Tmp_win->icon_pixmap_w &&
1839 Tmp_win->icon_pixmap_w != None)
1841 xwc.x = Tmp_win->icon_g.x;
1842 xwc.y = Tmp_win->icon_g.y - Tmp_win->icon_p_height;
1843 xwcm = cre->value_mask & (CWX | CWY);
1844 XConfigureWindow(dpy, Tmp_win->icon_pixmap_w, xwcm, &xwc);
1846 if(Tmp_win->icon_w != None)
1848 xwc.x = Tmp_win->icon_g.x;
1849 xwc.y = Tmp_win->icon_g.y;
1850 xwcm = cre->value_mask & (CWX | CWY);
1851 XConfigureWindow(dpy, Tmp_win->icon_w, xwcm, &xwc);
1854 if (!Tmp_win)
1855 return;
1858 #ifdef SHAPE
1859 if (ShapesSupported)
1861 int xws, yws, xbs, ybs;
1862 unsigned wws, hws, wbs, hbs;
1863 int boundingShaped, clipShaped;
1865 if (XShapeQueryExtents(dpy, Tmp_win->w,&boundingShaped, &xws, &yws, &wws,
1866 &hws,&clipShaped, &xbs, &ybs, &wbs, &hbs))
1868 Tmp_win->wShaped = boundingShaped;
1870 else
1872 Tmp_win->wShaped = 0;
1875 #endif /* SHAPE */
1877 if (cre->window == Tmp_win->w)
1879 #if 0
1880 fprintf(stderr, "cre: %d(%d) %d(%d) %d(%d)x%d(%d)\n",
1881 cre->x, (int)(cre->value_mask & CWX),
1882 cre->y, (int)(cre->value_mask & CWY),
1883 cre->width, (int)(cre->value_mask & CWWidth),
1884 cre->height, (int)(cre->value_mask & CWHeight));
1885 #endif
1886 /* Don't modify frame_XXX fields before calling SetupWindow! */
1887 dx = 0;
1888 dy = 0;
1889 dw = 0;
1890 dh = 0;
1892 /* for restoring */
1893 if (cre->value_mask & CWBorderWidth)
1895 Tmp_win->old_bw = cre->border_width;
1897 /* override even if border change */
1899 if (cre->value_mask & CWX)
1900 dx = cre->x - Tmp_win->frame_g.x - Tmp_win->boundary_width;
1901 if (cre->value_mask & CWY)
1902 dy = cre->y - Tmp_win->frame_g.y - Tmp_win->boundary_width -
1903 Tmp_win->title_g.height;
1904 if (cre->value_mask & CWWidth)
1905 dw = cre->width - (Tmp_win->frame_g.width - 2 * Tmp_win->boundary_width);
1907 if (cre->value_mask & CWHeight)
1909 if (cre->height < (WINDOW_FREAKED_OUT_HEIGHT - Tmp_win->title_g.height -
1910 2 * Tmp_win->boundary_width))
1912 dh = cre->height - (Tmp_win->frame_g.height -
1913 2 * Tmp_win->boundary_width -
1914 Tmp_win->title_g.height);
1916 else
1918 /* patch to ignore height changes to astronomically large windows
1919 * (needed for XEmacs 20.4); don't care if the window is shaded here -
1920 * we won't use 'height' in this case anyway */
1921 /* inform the buggy app about the size that *we* want */
1922 do_send_event = True;
1927 * SetupWindow (x,y) are the location of the upper-left outer corner and
1928 * are passed directly to XMoveResizeWindow (frame). The (width,height)
1929 * are the inner size of the frame. The inner width is the same as the
1930 * requested client window width; the inner height is the same as the
1931 * requested client window height plus any title bar slop.
1933 new_g = Tmp_win->frame_g;
1934 if (IS_SHADED(Tmp_win))
1935 new_g.height = Tmp_win->normal_g.height;
1936 oldnew_w = new_g.width + dw;
1937 oldnew_h = new_g.height + dh;
1938 constr_w = oldnew_w;
1939 constr_h = oldnew_h;
1940 constrain_size(
1941 Tmp_win, (unsigned int *)&constr_w, (unsigned int *)&constr_h, 0, 0,
1942 False);
1943 dw += (constr_w - oldnew_w);
1944 dh += (constr_h - oldnew_h);
1945 if (dx && dw)
1947 new_g.x = Tmp_win->frame_g.x + dx;
1948 new_g.width = Tmp_win->frame_g.width + dw;
1950 else if (dx && !dw)
1952 new_g.x = Tmp_win->frame_g.x + dx;
1954 else if (!dx && dw)
1956 gravity_resize(Tmp_win->hints.win_gravity, &new_g, dw, 0);
1958 if (dy && dh)
1960 new_g.y = Tmp_win->frame_g.y + dy;
1961 new_g.height = Tmp_win->frame_g.height + dh;
1963 else if (dy && !dh)
1965 new_g.y = Tmp_win->frame_g.y + dy;
1967 else if (!dy && dh)
1969 gravity_resize(Tmp_win->hints.win_gravity, &new_g, 0, dh);
1972 /* dont allow clients to resize maximized windows */
1973 if (!IS_MAXIMIZED(Tmp_win) || (!dw && !dh))
1975 if (IS_SHADED(Tmp_win))
1976 get_shaded_geometry(Tmp_win, &new_g, &new_g);
1977 SetupFrame(
1978 Tmp_win, new_g.x, new_g.y, new_g.width, new_g.height, False);
1980 /* make sure the window structure has the new position */
1981 update_absolute_geometry(Tmp_win);
1982 maximize_adjust_offset(Tmp_win);
1983 GNOME_SetWinArea(Tmp_win);
1986 /* Stacking order change requested... */
1987 /* Handle this *after* geometry changes, since we need the new
1988 geometry in occlusion calculations */
1989 if ( (cre->value_mask & CWStackMode) && !DO_IGNORE_RESTACK(Tmp_win) )
1991 FvwmWindow *otherwin = NULL;
1993 if (cre->value_mask & CWSibling)
1995 if (XFindContext (dpy, cre->above, FvwmContext,
1996 (caddr_t *) &otherwin) == XCNOENT)
1998 otherwin = NULL;
2002 if ((cre->detail != Above) && (cre->detail != Below))
2004 HandleUnusualStackmodes (cre->detail, Tmp_win, cre->window,
2005 otherwin, cre->above);
2007 /* only allow clients to restack windows within their layer */
2008 else if (!otherwin || compare_window_layers(otherwin, Tmp_win) != 0)
2010 switch (cre->detail)
2012 case Above:
2013 RaiseWindow (Tmp_win);
2014 break;
2015 case Below:
2016 LowerWindow (Tmp_win);
2017 break;
2020 else
2022 xwc.sibling = otherwin->frame;
2023 xwc.stack_mode = cre->detail;
2024 xwcm = CWSibling | CWStackMode;
2025 XConfigureWindow (dpy, Tmp_win->frame, xwcm, &xwc);
2027 /* Maintain the condition that icon windows are stacked
2028 immediately below their frame */
2029 /* 1. for Tmp_win */
2030 xwc.sibling = Tmp_win->frame;
2031 xwc.stack_mode = Below;
2032 xwcm = CWSibling | CWStackMode;
2033 if (Tmp_win->icon_w != None)
2035 XConfigureWindow(dpy, Tmp_win->icon_w, xwcm, &xwc);
2037 if (Tmp_win->icon_pixmap_w != None)
2039 XConfigureWindow(dpy, Tmp_win->icon_pixmap_w, xwcm, &xwc);
2042 /* 2. for otherwin */
2043 if (cre->detail == Below)
2045 xwc.sibling = otherwin->frame;
2046 xwc.stack_mode = Below;
2047 xwcm = CWSibling | CWStackMode;
2048 if (otherwin->icon_w != None)
2050 XConfigureWindow(dpy, otherwin->icon_w, xwcm, &xwc);
2052 if (otherwin->icon_pixmap_w != None)
2054 XConfigureWindow(dpy, otherwin->icon_pixmap_w, xwcm, &xwc);
2058 /* Maintain the stacking order ring */
2059 if (cre->detail == Above)
2061 remove_window_from_stack_ring(Tmp_win);
2062 add_window_to_stack_ring_after(
2063 Tmp_win, get_prev_window_in_stack_ring(otherwin));
2065 else /* cre->detail == Below */
2067 remove_window_from_stack_ring(Tmp_win);
2068 add_window_to_stack_ring_after(Tmp_win, otherwin);
2072 Let the modules know that Tmp_win changed its place
2073 in the stacking order
2075 BroadcastRestackThisWindow(Tmp_win);
2079 #if 1
2080 /* This causes some ddd windows not to be drawn properly. Reverted back to
2081 * the old method in SetupFrame. */
2082 /* domivogt (15-Oct-1999): enabled this to work around buggy apps that
2083 * ask for a nonsense height and expect that they really get it. */
2084 if (do_send_event)
2086 SendConfigureNotify(
2087 Tmp_win, Tmp_win->frame_g.x, Tmp_win->frame_g.y,
2088 new_g.width, new_g.height, cre->border_width, True);
2089 XSync(dpy,0);
2091 #endif
2094 /***********************************************************************
2096 * Procedure:
2097 * SendConfigureNotify - inform a client window of its geometry.
2099 * The input (frame) geometry will be translated to client geometry
2100 * before sending.
2102 ************************************************************************/
2103 void SendConfigureNotify(
2104 FvwmWindow *tmp_win, int x, int y, unsigned int w, unsigned int h, int bw,
2105 Bool send_for_frame_too)
2107 if (!tmp_win || IS_SHADED(tmp_win))
2108 return;
2110 XEvent client_event;
2112 client_event.type = ConfigureNotify;
2113 client_event.xconfigure.display = dpy;
2114 client_event.xconfigure.event = tmp_win->w;
2115 client_event.xconfigure.window = tmp_win->w;
2116 client_event.xconfigure.x = x + tmp_win->boundary_width;
2117 client_event.xconfigure.y = y + tmp_win->boundary_width +
2118 ((HAS_BOTTOM_TITLE(tmp_win)) ? 0 : tmp_win->title_g.height);
2119 client_event.xconfigure.width = w - 2 * tmp_win->boundary_width;
2120 client_event.xconfigure.height = h -
2121 2 * tmp_win->boundary_width - tmp_win->title_g.height;
2122 client_event.xconfigure.border_width = bw;
2123 client_event.xconfigure.above = tmp_win->frame;
2124 client_event.xconfigure.override_redirect = False;
2125 XSendEvent(dpy, tmp_win->w, False, StructureNotifyMask, &client_event);
2126 if (send_for_frame_too)
2128 /* This is for buggy tk, which waits for the real ConfigureNotify
2129 * on frame instead of the synthetic one on w. The geometry data
2130 * in the event will not be correct for the frame, but tk doesn't
2131 * look at that data anyway. */
2132 client_event.xconfigure.event = tmp_win->frame;
2133 client_event.xconfigure.window = tmp_win->frame;
2134 XSendEvent(dpy, tmp_win->frame, False,StructureNotifyMask,&client_event);
2139 /***********************************************************************
2141 * Procedure:
2142 * HandleShapeNotify - shape notification event handler
2144 ***********************************************************************/
2145 #ifdef SHAPE
2146 void HandleShapeNotify (void)
2148 DBUG("HandleShapeNotify","Routine Entered");
2150 if (ShapesSupported)
2152 XShapeEvent *sev = (XShapeEvent *) &Event;
2154 if (!Tmp_win)
2155 return;
2156 if (sev->kind != ShapeBounding)
2157 return;
2158 Tmp_win->wShaped = sev->shaped;
2159 SetShape(Tmp_win,Tmp_win->frame_g.width);
2162 #endif /* SHAPE*/
2164 /***********************************************************************
2166 * Procedure:
2167 * HandleVisibilityNotify - record fully visible windows for
2168 * use in the RaiseLower function and the OnTop type windows.
2170 ************************************************************************/
2171 void HandleVisibilityNotify(void)
2173 XVisibilityEvent *vevent = (XVisibilityEvent *) &Event;
2175 DBUG("HandleVisibilityNotify","Routine Entered");
2177 if(Tmp_win && Tmp_win->frame == last_event_window)
2179 if(vevent->state == VisibilityUnobscured)
2181 SET_FULLY_VISIBLE(Tmp_win, 1);
2182 SET_PARTIALLY_VISIBLE(Tmp_win, 1);
2184 else if (vevent->state == VisibilityPartiallyObscured)
2186 SET_FULLY_VISIBLE(Tmp_win, 0);
2187 SET_PARTIALLY_VISIBLE(Tmp_win, 1);
2189 else
2191 SET_FULLY_VISIBLE(Tmp_win, 0);
2192 SET_PARTIALLY_VISIBLE(Tmp_win, 0);
2198 /***************************************************************************
2200 * Waits for next X or module event, fires off startup routines when startup
2201 * modules have finished or after a timeout if the user has specified a
2202 * command line module that doesn't quit or gets stuck.
2204 ****************************************************************************/
2205 fd_set init_fdset;
2207 int My_XNextEvent(Display *dpy, XEvent *event)
2209 extern fd_set_size_t fd_width;
2210 extern int x_fd;
2211 fd_set in_fdset, out_fdset;
2212 Window targetWindow;
2213 int i;
2214 static struct timeval timeout = {42, 0};
2215 static struct timeval *timeoutP = &timeout;
2217 DBUG("My_XNextEvent","Routine Entered");
2219 /* include this next bit if HandleModuleInput() gets called anywhere else
2220 * with queueing turned on. Because this routine is the only place that
2221 * queuing is on _and_ ExecuteCommandQueue is always called immediately after
2222 * it is impossible for there to be anything in the queue at this point */
2223 #if 0
2224 /* execute any commands queued up */
2225 DBUG("My_XNextEvent", "executing module comand queue");
2226 ExecuteCommandQueue()
2227 #endif
2229 /* check for any X events already queued up.
2230 * Side effect: this does an XFlush if no events are queued
2231 * Make sure nothing between here and the select causes further X
2232 * requests to be sent or the select may block even though there
2233 * events in the queue */
2234 if(XPending(dpy)) {
2235 DBUG("My_XNextEvent","taking care of queued up events & returning (1)");
2236 XNextEvent(dpy,event);
2237 StashEventTime(event);
2238 return 1;
2241 DBUG("My_XNextEvent","no X events waiting - about to reap children");
2242 /* Zap all those zombies! */
2243 /* If we get to here, then there are no X events waiting to be processed.
2244 * Just take a moment to check for dead children. */
2245 ReapChildren();
2247 /* check for termination of all startup modules */
2248 if (fFvwmInStartup) {
2249 for(i=0;i<npipes;i++)
2250 if (FD_ISSET(i, &init_fdset))
2251 break;
2252 if (i == npipes || writePipes[i+1] == 0)
2254 DBUG("My_XNextEvent", "Starting up after command lines modules\n");
2255 timeoutP = NULL; /* set an infinite timeout to stop ticking */
2256 StartupStuff(); /* This may cause X requests to be sent */
2257 return 0; /* so return without select()ing */
2261 FD_ZERO(&in_fdset);
2262 FD_ZERO(&out_fdset);
2263 FD_SET(x_fd,&in_fdset);
2264 /* nothing is done here if fvwm was compiled without session support */
2265 if (sm_fd >= 0)
2266 FD_SET(sm_fd, &in_fdset);
2267 for(i=0; i<npipes; i++) {
2268 if(readPipes[i]>=0)
2269 FD_SET(readPipes[i], &in_fdset);
2270 if(pipeQueue[i]!= NULL)
2271 FD_SET(writePipes[i], &out_fdset);
2274 DBUG("My_XNextEvent","waiting for module input/output");
2275 if (fvwmSelect(fd_width, &in_fdset, &out_fdset, 0, timeoutP) > 0) {
2277 /* Check for module input. */
2278 for (i=0; i<npipes; i++) {
2279 if ((readPipes[i] >= 0) && FD_ISSET(readPipes[i], &in_fdset)) {
2280 if (read(readPipes[i], &targetWindow, sizeof(Window)) > 0) {
2281 DBUG("My_XNextEvent","calling HandleModuleInput");
2282 /* Add one module message to the queue */
2283 HandleModuleInput(targetWindow, i, NULL, True);
2284 } else {
2285 DBUG("My_XNextEvent","calling KillModule");
2286 KillModule(i);
2289 if ((writePipes[i] >= 0) && FD_ISSET(writePipes[i], &out_fdset)) {
2290 DBUG("My_XNextEvent","calling FlushMessageQueue");
2291 FlushMessageQueue(i);
2295 /* execute any commands queued up */
2296 DBUG("My_XNextEvent", "executing module comand queue");
2297 ExecuteCommandQueue();
2299 /* nothing is done here if fvwm was compiled without session support */
2300 if ((sm_fd >= 0) && (FD_ISSET(sm_fd, &in_fdset)))
2301 ProcessICEMsgs();
2303 } else {
2304 /* select has timed out, things must have calmed down so let's decorate */
2305 if (fFvwmInStartup) {
2306 fvwm_msg(ERR, "My_XNextEvent",
2307 "Some command line modules have not quit, "
2308 "Starting up after timeout.\n");
2309 StartupStuff();
2310 timeoutP = NULL; /* set an infinite timeout to stop ticking */
2311 reset_style_changes();
2312 Scr.flags.do_need_window_update = 0;
2316 /* check for X events again, rather than return 0 and get called again */
2317 if(XPending(dpy)) {
2318 DBUG("My_XNextEvent","taking care of queued up events & returning (2)");
2319 XNextEvent(dpy,event);
2320 StashEventTime(event);
2321 return 1;
2324 DBUG("My_XNextEvent","leaving My_XNextEvent");
2325 return 0;
2329 ** Procedure:
2330 ** InitEventHandlerJumpTable
2332 void InitEventHandlerJumpTable(void)
2334 int i;
2336 for (i=0; i<LASTEvent; i++)
2338 EventHandlerJumpTable[i] = NULL;
2340 EventHandlerJumpTable[Expose] = HandleExpose;
2341 EventHandlerJumpTable[DestroyNotify] = HandleDestroyNotify;
2342 EventHandlerJumpTable[MapRequest] = HandleMapRequest;
2343 EventHandlerJumpTable[MapNotify] = HandleMapNotify;
2344 EventHandlerJumpTable[UnmapNotify] = HandleUnmapNotify;
2345 EventHandlerJumpTable[ButtonPress] = HandleButtonPress;
2346 EventHandlerJumpTable[EnterNotify] = HandleEnterNotify;
2347 EventHandlerJumpTable[LeaveNotify] = HandleLeaveNotify;
2348 EventHandlerJumpTable[FocusIn] = HandleFocusIn;
2349 EventHandlerJumpTable[ConfigureRequest] = HandleConfigureRequest;
2350 EventHandlerJumpTable[ClientMessage] = HandleClientMessage;
2351 EventHandlerJumpTable[PropertyNotify] = HandlePropertyNotify;
2352 EventHandlerJumpTable[KeyPress] = HandleKeyPress;
2353 EventHandlerJumpTable[VisibilityNotify] = HandleVisibilityNotify;
2354 EventHandlerJumpTable[ColormapNotify] = HandleColormapNotify;
2355 #ifdef SHAPE
2356 if (ShapesSupported)
2357 EventHandlerJumpTable[ShapeEventBase+ShapeNotify] = HandleShapeNotify;
2358 #endif /* SHAPE */
2359 EventHandlerJumpTable[SelectionClear] = HandleSelectionClear;
2360 EventHandlerJumpTable[SelectionRequest] = HandleSelectionRequest;
2361 STROKE_CODE(EventHandlerJumpTable[ButtonRelease] = HandleButtonRelease);
2362 STROKE_CODE(EventHandlerJumpTable[MotionNotify] = HandleMotionNotify);
2363 #ifdef MOUSE_DROPPINGS
2364 STROKE_CODE(stroke_init(dpy,DefaultRootWindow(dpy)));
2365 #else /* no MOUSE_DROPPINGS */
2366 STROKE_CODE(stroke_init());
2367 #endif /* MOUSE_DROPPINGS */
2370 /***********************************************************************
2372 * Procedure:
2373 * DispatchEvent - handle a single X event stored in global var Event
2375 ************************************************************************/
2376 void DispatchEvent(Bool preserve_Tmp_win)
2378 Window w = Event.xany.window;
2379 FvwmWindow *s_Tmp_win = NULL;
2381 DBUG("DispatchEvent","Routine Entered");
2383 if (preserve_Tmp_win)
2384 s_Tmp_win = Tmp_win;
2385 StashEventTime(&Event);
2387 XFlush(dpy);
2388 if (XFindContext (dpy, w, FvwmContext, (caddr_t *) &Tmp_win) == XCNOENT)
2390 Tmp_win = NULL;
2392 last_event_type = Event.type;
2393 last_event_window = w;
2395 if (EventHandlerJumpTable[Event.type])
2397 (*EventHandlerJumpTable[Event.type])();
2400 #ifdef C_ALLOCA
2401 /* If we're using the C version of alloca, see if anything needs to be
2402 * freed up.
2404 alloca(0);
2405 #endif
2407 if (preserve_Tmp_win)
2408 Tmp_win = s_Tmp_win;
2409 DBUG("DispatchEvent","Leaving Routine");
2410 return;
2414 /***********************************************************************
2416 * Procedure:
2417 * HandleEvents - handle X events
2419 ************************************************************************/
2420 void HandleEvents(void)
2422 DBUG("HandleEvents","Routine Entered");
2423 STROKE_CODE(send_motion = FALSE);
2424 while ( !isTerminated )
2426 last_event_type = 0;
2427 if (Scr.flags.do_need_window_update)
2429 flush_window_updates();
2431 if (Scr.flags.do_need_style_list_update)
2433 simplify_style_list();
2435 if(My_XNextEvent(dpy, &Event))
2437 DispatchEvent(False);
2442 /***********************************************************************
2444 * Procedure:
2445 * Find the Fvwm context for the Event.
2447 ************************************************************************/
2448 int GetContext(FvwmWindow *t, XEvent *e, Window *w)
2450 int Context,i;
2452 Context = C_NO_CONTEXT;
2453 if (e->type == KeyPress && e->xkey.window == Scr.Root &&
2454 e->xkey.subwindow != None)
2456 /* Translate root coordinates into subwindow coordinates. Necessary for
2457 * key bindings that work over unfocused windows. */
2458 e->xkey.window = e->xkey.subwindow;
2459 XTranslateCoordinates(
2460 dpy, Scr.Root, e->xkey.subwindow, e->xkey.x, e->xkey.y, &(e->xkey.x),
2461 &(e->xkey.y), &(e->xkey.subwindow));
2462 XFindContext(dpy, e->xkey.window, FvwmContext, (caddr_t *) &t);
2463 Tmp_win = t;
2465 if (e->type == ButtonPress && t && e->xkey.window == t->frame &&
2466 e->xkey.subwindow != None)
2468 /* Translate frame coordinates into subwindow coordinates. */
2469 e->xkey.window = e->xkey.subwindow;
2470 XTranslateCoordinates(
2471 dpy, t->frame, e->xkey.subwindow, e->xkey.x, e->xkey.y, &(e->xkey.x),
2472 &(e->xkey.y), &(e->xkey.subwindow));
2473 if (e->xkey.window == t->Parent)
2475 e->xkey.window = e->xkey.subwindow;
2476 XTranslateCoordinates(
2477 dpy, t->Parent, e->xkey.subwindow, e->xkey.x, e->xkey.y, &(e->xkey.x),
2478 &(e->xkey.y), &(e->xkey.subwindow));
2481 if(!t)
2482 return C_ROOT;
2484 if (e->type == KeyPress && e->xkey.window == t->frame &&
2485 e->xkey.subwindow == t->decor_w)
2487 /* We can't get keyboard events on the decor_w directly beacause it is a
2488 * sibling of the parent window which gets all keyboard input. So we have to
2489 * grab keys on the frame and then translate the coordinates to find out in
2490 * which subwindow of the decor_w the event occured. */
2491 e->xkey.window = e->xkey.subwindow;
2492 XTranslateCoordinates(dpy, t->frame, t->decor_w, e->xkey.x, e->xkey.y,
2493 &JunkX, &JunkY, &(e->xkey.subwindow));
2495 *w= e->xany.window;
2497 if (*w == Scr.NoFocusWin)
2498 return C_ROOT;
2499 if (e->type == KeyPress && e->xkey.window == t->frame &&
2500 e->xkey.subwindow == t->decor_w)
2502 /* We can't get keyboard events on the decor_w directly beacause it is a
2503 * sibling of the parent window which gets all keyboard input. So we have to
2504 * grab keys on the frame and then translate the coordinates to find out in
2505 * which subwindow of the decor_w the event occured. */
2506 e->xkey.window = e->xkey.subwindow;
2507 XTranslateCoordinates(dpy, t->frame, t->decor_w, e->xkey.x, e->xkey.y,
2508 &JunkX, &JunkY, &(e->xkey.subwindow));
2510 *w= e->xany.window;
2512 if (*w == Scr.NoFocusWin)
2513 return C_ROOT;
2514 if (e->xkey.subwindow != None && e->xany.window != t->w)
2515 *w = e->xkey.subwindow;
2516 if (*w == Scr.Root)
2517 return C_ROOT;
2518 if (t)
2520 if (*w == t->title_w)
2521 Context = C_TITLE;
2522 else if (*w == t->w || *w == t->Parent || *w == t->frame)
2523 Context = C_WINDOW;
2524 else if (*w == t->icon_w || *w == t->icon_pixmap_w)
2525 Context = C_ICON;
2526 else if (*w == t->decor_w)
2527 Context = C_SIDEBAR;
2528 else
2530 for(i=0;i<4;i++)
2532 if(*w == t->corners[i])
2534 Context = C_FRAME;
2535 break;
2537 if(*w == t->sides[i])
2539 Context = C_SIDEBAR;
2540 break;
2543 if (i < 4)
2544 Button = i;
2545 else
2547 for (i = 0; i < NUMBER_OF_BUTTONS; i++)
2549 if (*w == t->button_w[i])
2551 if ((!(i & 1) && i / 2 < Scr.nr_left_buttons) ||
2552 ( (i & 1) && i / 2 < Scr.nr_right_buttons))
2554 Context = (1 << i) * C_L1;
2555 Button = i;
2556 break;
2561 } /* else */
2562 } /* if (t) */
2563 return Context;
2567 /**************************************************************************
2569 * Removes expose events for a specific window from the queue
2571 *************************************************************************/
2572 int flush_expose (Window w)
2574 XEvent dummy;
2575 int i=0;
2577 while (XCheckTypedWindowEvent (dpy, w, Expose, &dummy))
2578 i++;
2579 return i;
2583 /**************************************************************************
2585 * Removes all expose events from the queue and does the necessary redraws
2587 *************************************************************************/
2588 void handle_all_expose(void)
2590 XEvent old_event;
2592 memcpy(&old_event, &Event, sizeof(XEvent));
2593 XPending(dpy);
2594 while (XCheckMaskEvent(dpy, ExposureMask, &Event))
2596 DispatchEvent(True);
2598 memcpy(&Event, &old_event, sizeof(XEvent));
2600 return;
2604 /****************************************************************************
2606 * Records the time of the last processed event. Used in XSetInputFocus
2608 ****************************************************************************/
2609 Bool StashEventTime (XEvent *ev)
2611 Time NewTimestamp = CurrentTime;
2613 switch (ev->type)
2615 case KeyPress:
2616 case KeyRelease:
2617 NewTimestamp = ev->xkey.time;
2618 break;
2619 case ButtonPress:
2620 case ButtonRelease:
2621 NewTimestamp = ev->xbutton.time;
2622 break;
2623 case MotionNotify:
2624 NewTimestamp = ev->xmotion.time;
2625 break;
2626 case EnterNotify:
2627 case LeaveNotify:
2628 NewTimestamp = ev->xcrossing.time;
2629 break;
2630 case PropertyNotify:
2631 NewTimestamp = ev->xproperty.time;
2632 break;
2633 case SelectionClear:
2634 NewTimestamp = ev->xselectionclear.time;
2635 break;
2636 case SelectionRequest:
2637 NewTimestamp = ev->xselectionrequest.time;
2638 break;
2639 case SelectionNotify:
2640 NewTimestamp = ev->xselection.time;
2641 break;
2642 default:
2643 return False;
2645 /* Only update if the new timestamp is later than the old one, or
2646 * if the new one is from a time at least 30 seconds earlier than the
2647 * old one (in which case the system clock may have changed) */
2648 if (NewTimestamp > lastTimestamp ||
2649 lastTimestamp - NewTimestamp > CLOCK_SKEW_MS)
2650 lastTimestamp = NewTimestamp;
2651 return True;
2654 /* CoerceEnterNotifyOnCurrentWindow()
2655 * Pretends to get a HandleEnterNotify on the
2656 * window that the pointer currently is in so that
2657 * the focus gets set correctly from the beginning
2658 * Note that this presently only works if the current
2659 * window is not click_to_focus; I think that
2660 * that behaviour is correct and desirable. --11/08/97 gjb */
2661 void CoerceEnterNotifyOnCurrentWindow(void)
2663 extern FvwmWindow *Tmp_win; /* from events.c */
2664 Window child, root;
2665 int root_x, root_y;
2666 int win_x, win_y;
2667 Bool f = XQueryPointer(dpy, Scr.Root, &root,
2668 &child, &root_x, &root_y, &win_x, &win_y, &JunkMask);
2669 if (f && child != None) {
2670 Event.xany.window = child;
2671 if (XFindContext(dpy, child, FvwmContext, (caddr_t *) &Tmp_win) == XCNOENT)
2672 Tmp_win = NULL;
2673 HandleEnterNotify();
2674 Tmp_win = None;