1 /*
2 * Copyright 1988 by Evans & Sutherland Computer Corporation,
3 * Salt Lake City, Utah
4 * Portions Copyright 1989 by the Massachusetts Institute of Technology
5 * Cambridge, Massachusetts
6 *
7 * Copyright 1992 Claude Lecommandeur.
8 */
9
10 /***********************************************************************
11 *
12 * $XConsortium: events.c,v 1.182 91/07/17 13:59:14 dave Exp $
13 *
14 * twm event handling
15 *
16 * 17-Nov-87 Thomas E. LaStrange File created
17 *
18 * Do the necessary modification to be integrated in ctwm.
19 * Can no longer be used for the standard twm.
20 *
21 * 22-April-92 Claude Lecommandeur.
22 *
23 *
24 ***********************************************************************/
25
26 #include "ctwm.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <sys/time.h>
31
32 #include <X11/Xatom.h>
33 #include <X11/extensions/shape.h>
34
35 #include "add_window.h"
36 #include "animate.h"
37 #include "clicktofocus.h"
38 #include "colormaps.h"
39 #include "ctwm_atoms.h"
40 #include "events.h"
41 #include "event_handlers.h"
42 #include "event_internal.h"
43 #include "event_names.h"
44 #include "functions.h"
45 #include "functions_defs.h"
46 #include "gram.tab.h"
47 #include "iconmgr.h"
48 #include "icons.h"
49 #include "image.h"
50 #include "list.h"
51 #include "occupation.h"
52 #include "otp.h"
53 #include "parse.h"
54 #include "screen.h"
55 #include "util.h"
56 #include "vscreen.h"
57 #include "win_decorations.h"
58 #include "win_iconify.h"
59 #include "win_ops.h"
60 #include "win_regions.h"
61 #include "win_resize.h"
62 #include "win_utils.h"
63 #include "workspace_manager.h"
64 #include "workspace_utils.h"
65
66
67 static void do_key_menu(MenuRoot *menu, /* menu to pop up */
68 Window w); /* invoking window or None */
69
70 /* Only called from HandleFocusChange() */
71 static void HandleFocusIn(void);
72 static void HandleFocusOut(void);
73
74 /*
75 * This currently needs to live in the broader scope because of how it's
76 * used in deferred function handling.
77 */
78 static char *Action;
79
80 static TwmWindow *ButtonWindow; /* button press window structure */
81
82 static void SendTakeFocusMessage(TwmWindow *tmp, Time timestamp);
83
84
set_mask_ignore(unsigned int modifier)85 static unsigned int set_mask_ignore(unsigned int modifier)
86 {
87 modifier &= ~Scr->IgnoreModifier;
88
89 return modifier;
90 }
91
92
93 /***********************************************************************
94 *
95 * Procedure:
96 * HandleColormapNotify - colormap notify event handler
97 *
98 * This procedure handles both a client changing its own colormap, and
99 * a client explicitly installing its colormap itself (only the window
100 * manager should do that, so we must set it correctly).
101 *
102 ***********************************************************************
103 */
104
HandleColormapNotify(void)105 void HandleColormapNotify(void)
106 {
107 XColormapEvent *cevent = (XColormapEvent *) &Event;
108 ColormapWindow *cwin, **cwins;
109 TwmColormap *cmap;
110 int lost, won, n, number_cwins;
111
112 /* if (! Tmp_win) return; */
113 if(XFindContext(dpy, cevent->window, ColormapContext,
114 (XPointer *)&cwin) == XCNOENT) {
115 return;
116 }
117 cmap = cwin->colormap;
118
119 if(cevent->new) {
120 if(XFindContext(dpy, cevent->colormap, ColormapContext,
121 (XPointer *)&cwin->colormap) == XCNOENT) {
122 cwin->colormap = CreateTwmColormap(cevent->colormap);
123 }
124 else {
125 cwin->colormap->refcnt++;
126 }
127
128 cmap->refcnt--;
129
130 if(cevent->state == ColormapUninstalled) {
131 cmap->state &= ~CM_INSTALLED;
132 }
133 else {
134 cmap->state |= CM_INSTALLED;
135 }
136
137 if(cmap->state & CM_INSTALLABLE) {
138 InstallColormaps(ColormapNotify, NULL);
139 }
140
141 if(cmap->refcnt == 0) {
142 XDeleteContext(dpy, cmap->c, ColormapContext);
143 free(cmap);
144 }
145
146 return;
147 }
148
149 if(cevent->state == ColormapUninstalled &&
150 (cmap->state & CM_INSTALLABLE)) {
151 if(!(cmap->state & CM_INSTALLED)) {
152 return;
153 }
154 cmap->state &= ~CM_INSTALLED;
155
156 if(!ColortableThrashing) {
157 ColortableThrashing = true;
158 XSync(dpy, 0);
159 }
160
161 if(cevent->serial >= Scr->cmapInfo.first_req) {
162 number_cwins = Scr->cmapInfo.cmaps->number_cwins;
163
164 /*
165 * Find out which colortables collided.
166 */
167
168 cwins = Scr->cmapInfo.cmaps->cwins;
169 for(lost = won = -1, n = 0;
170 (lost == -1 || won == -1) && n < number_cwins;
171 n++) {
172 if(lost == -1 && cwins[n] == cwin) {
173 lost = n; /* This is the window which lost its colormap */
174 continue;
175 }
176
177 if(won == -1 &&
178 cwins[n]->colormap->install_req == cevent->serial) {
179 won = n; /* This is the window whose colormap caused */
180 continue; /* the de-install of the previous colormap */
181 }
182 }
183
184 /*
185 ** Cases are:
186 ** Both the request and the window were found:
187 ** One of the installs made honoring the WM_COLORMAP
188 ** property caused another of the colormaps to be
189 ** de-installed, just mark the scoreboard.
190 **
191 ** Only the request was found:
192 ** One of the installs made honoring the WM_COLORMAP
193 ** property caused a window not in the WM_COLORMAP
194 ** list to lose its map. This happens when the map
195 ** it is losing is one which is trying to be installed,
196 ** but is getting getting de-installed by another map
197 ** in this case, we'll get a scoreable event later,
198 ** this one is meaningless.
199 **
200 ** Neither the request nor the window was found:
201 ** Somebody called installcolormap, but it doesn't
202 ** affect the WM_COLORMAP windows. This case will
203 ** probably never occur.
204 **
205 ** Only the window was found:
206 ** One of the WM_COLORMAP windows lost its colormap
207 ** but it wasn't one of the requests known. This is
208 ** probably because someone did an "InstallColormap".
209 ** The colormap policy is "enforced" by re-installing
210 ** the colormaps which are believed to be correct.
211 */
212
213 if(won != -1) {
214 if(lost != -1) {
215 /* lower diagonal index calculation */
216 if(lost > won) {
217 n = lost * (lost - 1) / 2 + won;
218 }
219 else {
220 n = won * (won - 1) / 2 + lost;
221 }
222 Scr->cmapInfo.cmaps->scoreboard[n] = 1;
223 }
224 else {
225 /*
226 ** One of the cwin installs caused one of the cwin
227 ** colormaps to be de-installed, so I'm sure to get an
228 ** UninstallNotify for the cwin I know about later.
229 ** I haven't got it yet, or the test of CM_INSTALLED
230 ** above would have failed. Turning the CM_INSTALLED
231 ** bit back on makes sure we get back here to score
232 ** the collision.
233 */
234 cmap->state |= CM_INSTALLED;
235 }
236 }
237 else if(lost != -1) {
238 InstallColormaps(ColormapNotify, NULL);
239 }
240 else {
241 ColortableThrashing = false; /* Gross Hack for HP WABI. CL. */
242 }
243 }
244 }
245
246 else if(cevent->state == ColormapUninstalled) {
247 cmap->state &= ~CM_INSTALLED;
248 }
249
250 else if(cevent->state == ColormapInstalled) {
251 cmap->state |= CM_INSTALLED;
252 }
253 }
254
255
256 /*
257 * LastFocusEvent -- skip over focus in/out events for this
258 * window.
259 */
260
LastFocusEvent(Window w,XEvent * first)261 static XEvent *LastFocusEvent(Window w, XEvent *first)
262 {
263 static XEvent current;
264 XEvent *last, new;
265
266 new = *first;
267 last = NULL;
268
269 do {
270 if((new.type == FocusIn || new.type == FocusOut)
271 && new.xfocus.mode == NotifyNormal
272 && (new.xfocus.detail == NotifyNonlinear
273 || new.xfocus.detail == NotifyPointer
274 || new.xfocus.detail == NotifyAncestor
275 || (new.xfocus.detail == NotifyNonlinearVirtual)
276 )) {
277 current = new;
278 last = ¤t;
279 }
280
281 #ifdef TRACE_FOCUS
282 fprintf(stderr, "%s(): Focus%s 0x%x mode=%d, detail=%d\n",
283 __func__, new.xfocus.type == FocusIn ? "In" : "Out",
284 Tmp_win, new.xfocus.mode, new.xfocus.detail);
285 #endif
286
287 }
288 while(XCheckWindowEvent(dpy, w, FocusChangeMask, &new));
289 return last;
290 }
291
292
293 /*
294 * Focus change handlers.
295 *
296 * Depending on how events get called, these are sometimes redundant, as
297 * the Enter event handler does practically all of this anyway. But
298 * there are presumably ways we can wind up Focus'ing a window without
299 * Enter'ing it as well.
300 *
301 * It's also a little convoluted how these wind up getting called. With
302 * most events, we call a handler, then handle that event. However, with
303 * focus, we troll through our list of pending Focus-related events for
304 * the window and just handle the last one, since some could pile up
305 * fast. That means that, even if we get called for a FocusIn event,
306 * there might be a FocusOut later in the queue, and _that_'s the one we
307 * pick up and handle, and we discard the rest [for that window]. So,
308 * the event handling code calls a single entry point for both types, and
309 * then it figures out which backend handler to actually fire.
310 */
311 void
HandleFocusChange(void)312 HandleFocusChange(void)
313 {
314 XEvent *event;
315
316 /* If there's no event window, nothing to do */
317 if(!Tmp_win) {
318 return;
319 }
320
321 /*
322 * Consume all the focus events for the window we're called about and
323 * grab the last one to process.
324 *
325 * XXX It should be guaranteed that the window in the X event in our
326 * global Event is the same as Tmp_win->w as the event dispatcher
327 * sets it so. Maybe we should do both checks on the same var for
328 * consistency though?
329 *
330 * It's not immediately clear how this can wind up returning nothing,
331 * but if it does, we don't have anything to do either.
332 */
333 event = LastFocusEvent(Event.xany.window, &Event);
334 if(event == NULL) {
335 return;
336 }
337
338 /*
339 * Icon managers don't do anything with focus events on themselves,
340 * so just skip back if this is one. Done after LastFocusEvent()
341 * call for efficiency, so we don't fall into this func multiple
342 * times if multiple events are queued for it.
343 */
344 if(Tmp_win->isiconmgr) {
345 return;
346 }
347
348 #ifdef TRACE_FOCUS
349 fprintf(stderr, "HandleFocus%s(): 0x%x (0x%x, 0x%x), mode=%d, "
350 "detail=%d\n",
351 (event->type == FocusIn ? "In" : "Out"),
352 Tmp_win, Tmp_win->w, event->window, event->mode,
353 event->detail);
354 #endif
355
356 /* And call actual handler */
357 if(event->type == FocusIn) {
358 HandleFocusIn();
359 }
360 else {
361 HandleFocusOut();
362 }
363 }
364
365
366 static void
HandleFocusIn(void)367 HandleFocusIn(void)
368 {
369 if(! Tmp_win->wmhints->input) {
370 return;
371 }
372 if(Scr->Focus == Tmp_win) {
373 return;
374 }
375
376 #ifdef EWMH
377 // Handle focus-dependent re-stacking of what we're moving out of.
378 if(Scr->Focus && OtpIsFocusDependent(Scr->Focus)) {
379 OtpUnfocusWindow(Scr->Focus);
380 // NULL's Scr->Focus
381 }
382 #endif
383
384 if(Tmp_win->AutoSqueeze && Tmp_win->squeezed) {
385 AutoSqueeze(Tmp_win);
386 }
387 SetFocusVisualAttributes(Tmp_win, true);
388
389 #ifdef EWMH
390 // Handle focus-dependent re-stacking of what we're moving in to.
391 if(Tmp_win && OtpIsFocusDependent(Tmp_win)) {
392 OtpFocusWindow(Tmp_win);
393 // Sets Scr->Focus
394 }
395 #endif
396
397 // Redundant in EWMH case
398 Scr->Focus = Tmp_win;
399 }
400
401
402 static void
HandleFocusOut(void)403 HandleFocusOut(void)
404 {
405 if(Scr->Focus != Tmp_win) {
406 return;
407 }
408 if(Scr->SloppyFocus) {
409 return;
410 }
411 if(Tmp_win->AutoSqueeze && !Tmp_win->squeezed) {
412 AutoSqueeze(Tmp_win);
413 }
414 SetFocusVisualAttributes(Tmp_win, false);
415
416 #ifdef EWMH
417 /*
418 * X-ref HandleFocusIn() comment. FocusOut is only leaving a window,
419 * not entering a new one, so there's only one we may need to
420 * restack.
421 */
422 if(Scr->Focus && OtpIsFocusDependent(Scr->Focus)) {
423 OtpUnfocusWindow(Scr->Focus);
424 // NULL's Scr->Focus
425 }
426 #endif
427
428 // Redundant in EWMH case
429 Scr->Focus = NULL;
430 }
431
432
433
434 /*
435 * Only sent if SubstructureNotifyMask is selected on the (root) window.
436 */
HandleCirculateNotify()437 void HandleCirculateNotify()
438 {
439 VirtualScreen *vs;
440 #ifdef DEBUG_EVENTS
441 fprintf(stderr, "HandleCirculateNotify\n");
442 fprintf(stderr, "event=%x window=%x place=%d\n",
443 (unsigned)Event.xcirculate.event,
444 (unsigned)Event.xcirculate.window,
445 Event.xcirculate.place);
446 #endif
447
448 for(vs = Scr->vScreenList; vs; vs = vs->next) {
449 if(Event.xcirculate.event == vs->window) {
450 TwmWindow *twm_win = GetTwmWindow(Event.xcirculate.window);
451
452 if(twm_win) {
453 WinType wt;
454
455 if(Event.xcirculate.window == twm_win->frame) {
456 wt = WinWin;
457 }
458 else if(twm_win->icon &&
459 Event.xcirculate.window == twm_win->icon->w) {
460 wt = IconWin;
461 }
462 else {
463 return;
464 }
465
466 OtpHandleCirculateNotify(vs,
467 twm_win, wt,
468 Event.xcirculate.place);
469 }
470 }
471 }
472 }
473
474 /***********************************************************************
475 *
476 * Procedure:
477 * HandleVisibilityNotify - visibility notify event handler
478 *
479 * This routine keeps track of visibility events so that colormap
480 * installation can keep the maximum number of useful colormaps
481 * installed at one time.
482 *
483 ***********************************************************************
484 */
485
HandleVisibilityNotify(void)486 void HandleVisibilityNotify(void)
487 {
488 XVisibilityEvent *vevent = (XVisibilityEvent *) &Event;
489 ColormapWindow *cwin;
490 TwmColormap *cmap;
491
492 if(XFindContext(dpy, vevent->window, ColormapContext,
493 (XPointer *)&cwin) == XCNOENT) {
494 return;
495 }
496
497 /*
498 * when Saber complains about retreiving an <int> from an <unsigned int>
499 * just type "touch vevent->state" and "cont"
500 */
501 cmap = cwin->colormap;
502 if((cmap->state & CM_INSTALLABLE) &&
503 vevent->state != cwin->visibility &&
504 (vevent->state == VisibilityFullyObscured ||
505 cwin->visibility == VisibilityFullyObscured) &&
506 cmap->w == cwin->w) {
507 cwin->visibility = vevent->state;
508 InstallWindowColormaps(VisibilityNotify, NULL);
509 }
510 else {
511 cwin->visibility = vevent->state;
512 }
513 }
514
515
516 /***********************************************************************
517 *
518 * Procedure:
519 * HandleKeyRelease - key release event handler
520 *
521 ***********************************************************************
522 */
523
HandleKeyRelease(void)524 void HandleKeyRelease(void)
525 {
526 if(Tmp_win == Scr->currentvs->wsw->twm_win) {
527 WMgrHandleKeyReleaseEvent(Scr->currentvs, &Event);
528 }
529 }
530
531
532
533 /*
534 * HandleKeyPress - key press event handler
535 *
536 * When a key is pressed, we may do various things with it. If we're in
537 * a menu, various keybindings move around in it, others get silently
538 * ignored. Else, we look through the various bindings set in the config
539 * file and invoke whatever should be. If none of that matches, and it
540 * seems like some window should have focus, pass the event down to that
541 * window.
542 */
HandleKeyPress(void)543 void HandleKeyPress(void)
544 {
545 /*
546 * If the Info window (f.identify/f.version) is currently up, any key
547 * press will drop it away.
548 */
549 if(Scr->InfoWindow.mapped) {
550 XUnmapWindow(dpy, Scr->InfoWindow.win);
551 Scr->InfoWindow.mapped = false;
552 }
553
554
555 /*
556 * If a menu is up, we interpret various keys as moving around or
557 * doing things in the menu. No other key bindings or usages are
558 * considered.
559 */
560 if(ActiveMenu != NULL) {
561 MenuItem *item;
562 char *keynam;
563 KeySym keysym;
564 Window junkW;
565
566 item = NULL;
567
568 /* What key was pressed? */
569 keysym = XLookupKeysym((XKeyEvent *) &Event, 0);
570 if(! keysym) {
571 return;
572 }
573 keynam = XKeysymToString(keysym);
574 if(! keynam) {
575 return;
576 }
577
578
579 /*
580 * Initial handling of the various keystrokes. Most keys are
581 * completely handled here; we only descend out into later for
582 * for Return/Right keys that do invoke-y stuff on menu entries.
583 */
584 if(keysym == XK_Down || keysym == XK_space) {
585 /*
586 * Down or Spacebar moves us to the next entry in the menu,
587 * looping back around to the top when it falls off the
588 * bottom.
589 *
590 * Start with our X and (current+height)Y, then wrap around
591 * to the top (Y)/into the menu (X) as necessary.
592 */
593 int xx = Event.xkey.x;
594 int yy = Event.xkey.y + Scr->EntryHeight;
595 int wx, wy;
596 XTranslateCoordinates(dpy, Scr->Root, ActiveMenu->w, xx, yy, &wx, &wy, &junkW);
597 if((wy < 0) || (wy > ActiveMenu->height)) {
598 yy -= (wy - (Scr->EntryHeight / 2) - 2);
599 }
600 if((wx < 0) || (wx > ActiveMenu->width)) {
601 xx -= (wx - (ActiveMenu->width / 2));
602 }
603
604 /*
605 * Move the pointer there. We'll get a Motion notify from
606 * the X server as a result, which will fall into the loop in
607 * UpdateMenu() and handle re-highlighting etc.
608 */
609 XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y,
610 ActiveMenu->width, ActiveMenu->height, xx, yy);
611 return;
612 }
613 else if(keysym == XK_Up || keysym == XK_BackSpace) {
614 /*
615 * Up/Backspace move up an entry, with details similar in
616 * reverse to the above.
617 */
618 int xx = Event.xkey.x;
619 int yy = Event.xkey.y - Scr->EntryHeight;
620 int wx, wy;
621 XTranslateCoordinates(dpy, Scr->Root, ActiveMenu->w, xx, yy, &wx, &wy, &junkW);
622 if((wy < 0) || (wy > ActiveMenu->height)) {
623 yy -= (wy - ActiveMenu->height + (Scr->EntryHeight / 2) + 2);
624 }
625 if((wx < 0) || (wx > ActiveMenu->width)) {
626 xx -= (wx - (ActiveMenu->width / 2));
627 }
628 XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y,
629 ActiveMenu->width, ActiveMenu->height, xx, yy);
630 return;
631 }
632 else if(keysym == XK_Right || keysym == XK_Return) {
633 /*
634 * Right/Return mean we're invoking some entry item, so we
635 * take note of where we are for activating at the end of
636 * this set of conditionals.
637 *
638 * Follow this down into the following if(item) block for
639 * details, particularly in the subtle differences between
640 * Right and Return on f.menu entries.
641 */
642 item = ActiveItem;
643 }
644 else if(keysym == XK_Left || keysym == XK_Escape) {
645 /*
646 * Left/Escape back up to a higher menu level, or out totally
647 * from the top.
648 */
649 MenuRoot *menu;
650
651 /* Leave pinned menus alone though */
652 if(ActiveMenu->pinned) {
653 return;
654 }
655
656 /* Top-level? Clear out and leave menu mode totally. */
657 if(!ActiveMenu->prev || MenuDepth == 1) {
658 PopDownMenu();
659 XUngrabPointer(dpy, CurrentTime);
660 return;
661 }
662
663 /*
664 * We're in a sub level. Figure out various stuff for where
665 * we are and where we should be in the up-level, clear out
666 * the windows for this level, and warp us up there.
667 */
668 int xx = Event.xkey.x;
669 int yy = Event.xkey.y;
670 int wx, wy;
671 menu = ActiveMenu->prev;
672 XTranslateCoordinates(dpy, Scr->Root, menu->w, xx, yy, &wx, &wy, &junkW);
673 xx -= (wx - (menu->width / 2));
674 if(menu->lastactive)
675 yy -= (wy - menu->lastactive->item_num * Scr->EntryHeight -
676 (Scr->EntryHeight / 2) - 2);
677 else {
678 yy -= (wy - (Scr->EntryHeight / 2) - 2);
679 }
680 XUnmapWindow(dpy, ActiveMenu->w);
681 if(Scr->Shadow) {
682 XUnmapWindow(dpy, ActiveMenu->shadow);
683 }
684 XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y,
685 menu->width, menu->height, xx, yy);
686 return;
687 }
688 else if(strlen(keynam) == 1) {
689 /*
690 * This would mean pressing a more normal (e.g., letter/num)
691 * key. These find the first entry starting with a matching
692 * character and jump to it.
693 */
694 MenuItem *startitem;
695 int xx = Event.xkey.x;
696 int yy = Event.xkey.y;
697 int wx, wy;
698
699 startitem = ActiveItem ? ActiveItem : ActiveMenu->first;
700 item = startitem->next;
701 if(item == NULL) {
702 item = ActiveMenu->first;
703 }
704 unsigned int keymod = (Event.xkey.state & mods_used);
705 keymod = set_mask_ignore(keymod);
706
707 while(item != startitem) {
708 bool matched = false;
709 size_t offset = 0;
710 switch(item->item [0]) {
711 case '^' :
712 if((keymod & ControlMask) &&
713 (keynam [0] == Tolower(item->item [1]))) {
714 matched = true;
715 }
716 break;
717 case '~' :
718 if((keymod & Mod1Mask) &&
719 (keynam [0] == Tolower(item->item [1]))) {
720 matched = true;
721 }
722 break;
723 case ' ' :
724 offset = 1;
725 default :
726 if(((Scr->IgnoreCaseInMenuSelection) &&
727 (keynam [0] == Tolower(item->item [offset]))) ||
728
729 ((keymod & ShiftMask) && Isupper(item->item [offset]) &&
730 (keynam [0] == Tolower(item->item [offset]))) ||
731
732 (!(keymod & ShiftMask) && Islower(item->item [offset]) &&
733 (keynam [0] == item->item [offset]))) {
734 matched = true;
735 }
736 break;
737 }
738 if(matched) {
739 break;
740 }
741 item = item->next;
742 if(item == NULL) {
743 item = ActiveMenu->first;
744 }
745 }
746 if(item == startitem) {
747 return;
748 }
749 wx = ActiveMenu->width / 2;
750 wy = (item->item_num * Scr->EntryHeight) + (Scr->EntryHeight / 2) + 2;
751 XTranslateCoordinates(dpy, ActiveMenu->w, Scr->Root, wx, wy, &xx, &yy, &junkW);
752 XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y,
753 ActiveMenu->width, ActiveMenu->height, xx, yy);
754 return;
755 }
756 else {
757 /* Other keys get ignored */
758 return;
759 }
760
761
762 /*
763 * So if we get here, the key pressed was a Right/Return on an
764 * entry to select it (chosen entry now in item). Every other
765 * case is have been completely handled in the block above and
766 * would have already returned.
767 *
768 * So item should always be the entry we just tried to invoke.
769 * I'm not sure how it could be empty, but if it is, we just hop
770 * ourselves out of the menu. Otherwise, we do whatever we want
771 * to do with the entry type we're on.
772 */
773 if(item) {
774 switch(item->func) {
775 /* f.nop and f.title, we just silently let pass */
776 case 0 :
777 case F_TITLE :
778 break;
779
780 /* If it's a f.menu, there's more magic to do */
781 case F_MENU: {
782 /*
783 * Return is treated separately from Right. It
784 * "invokes" the menu item, which immediately calls
785 * whatever the default menu entry is (which may be
786 * nothing).
787 */
788 if(!strcmp(keynam, "Return")) {
789 if(ActiveMenu == Scr->Workspaces) {
790 /*
791 * f.menu "TwmWorkspaces". The "invocation"
792 * of this jumps to the workspace in
793 * question, as if it were a default entry of
794 * f.gotoworkspace.
795 *
796 * XXX Grody magic. Maybe this should be
797 * unwound to a default entry...
798 */
799 PopDownMenu();
800 XUngrabPointer(dpy, CurrentTime);
801 GotoWorkSpaceByName(Scr->currentvs, item->action + 8);
802 }
803 else {
804 /*
805 * Calling the f.menu handler invokes the
806 * default action. We handle popping out of
807 * menus ourselves.
808 */
809 ExecuteFunction(item->func, item->action,
810 ButtonWindow ? ButtonWindow->frame : None,
811 ButtonWindow, &Event, Context, false);
812 PopDownMenu();
813 }
814
815 /*
816 * Whatever invocation Return does is done, so we
817 * are too.
818 */
819 return;
820 }
821
822 /*
823 * Right arrow causes opening up a sub-f.menu. Open
824 * it up in the appropriate place, [re-]set
825 * highlights, and call do_key_menu() to do a lot of
826 * the internals of it.
827 */
828 int xx = Event.xkey.x;
829 int yy = Event.xkey.y;
830 int wx, wy;
831 XTranslateCoordinates(dpy, Scr->Root, ActiveMenu->w, xx, yy,
832 &wx, &wy, &junkW);
833 if(ActiveItem) {
834 ActiveItem->state = 0;
835 PaintEntry(ActiveMenu, ActiveItem, false);
836 ActiveItem = NULL;
837 }
838 xx -= (wx - ActiveMenu->width);
839 yy -= (wy - item->item_num * Scr->EntryHeight - (Scr->EntryHeight / 2) - 2);
840 Event.xkey.x_root = xx;
841 Event.xkey.y_root = yy;
842 XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y,
843 ActiveMenu->width, ActiveMenu->height, xx, yy);
844 if(ActiveMenu == Scr->Workspaces) {
845 CurrentSelectedWorkspace = item->item;
846 }
847 do_key_menu(item->sub, None);
848 CurrentSelectedWorkspace = NULL;
849 break;
850 }
851
852 /*
853 * Any other f.something. Pop down the menu (unless
854 * we're trying to pin it up), and invoke the function.
855 */
856 default :
857 if(item->func != F_PIN) {
858 PopDownMenu();
859 }
860 ExecuteFunction(item->func, item->action,
861 ButtonWindow ? ButtonWindow->frame : None,
862 ButtonWindow, &Event, Context, false);
863 }
864
865 /* Done whatever invocation of the entry we need */
866 }
867 else {
868 /* Was no item; pop out of the menu */
869 PopDownMenu();
870 XUngrabPointer(dpy, CurrentTime);
871 }
872
873 /*
874 * We're done handling the keypress in a menu, so there's nothing
875 * else to do.
876 */
877 return;
878 }
879
880
881 /*
882 * Not in a menu, so we loop through our various bindings. First,
883 * figure out what context we're in. This goes in a global var,
884 * presumably because stuff way down the chain of invoking some item
885 * may need to refer up to it.
886 */
887 Context = C_NO_CONTEXT;
888 if(Event.xany.window == Scr->Root) {
889 if(AlternateContext) {
890 XUngrabPointer(dpy, CurrentTime);
891 XUngrabKeyboard(dpy, CurrentTime);
892 AlternateContext = false;
893 Context = C_ALTERNATE;
894 }
895 else if(AlternateKeymap && Event.xkey.subwindow) {
896 Tmp_win = GetTwmWindow(Event.xkey.subwindow);
897 if(Tmp_win) {
898 Event.xany.window = Tmp_win->w;
899 }
900 }
901 else {
902 Context = C_ROOT;
903 }
904 }
905 if(Tmp_win) {
906 if(0) {
907 /* Dummy to simplify constructions of else if's */
908 }
909 #ifdef EWMH_DESKTOP_ROOT
910 else if(Tmp_win->ewmhWindowType == wt_Desktop) {
911 fprintf(stderr, "HandleKeyPress: wt_Desktop -> C_ROOT\n");
912 Context = C_ROOT;
913 }
914 #endif
915 else if(Event.xany.window == Tmp_win->title_w) {
916 Context = C_TITLE;
917 }
918 else if(Event.xany.window == Tmp_win->w) {
919 Context = C_WINDOW;
920 }
921 else if(Tmp_win->icon && (Event.xany.window == Tmp_win->icon->w)) {
922 Context = C_ICON;
923 }
924 else if(Event.xany.window == Tmp_win->frame) {
925 Context = C_FRAME;
926 }
927 else if(Tmp_win->iconmanagerlist) {
928 if(Event.xany.window == Tmp_win->iconmanagerlist->w ||
929 Event.xany.window == Tmp_win->iconmanagerlist->icon) {
930 Context = C_ICONMGR;
931 }
932 }
933 if(Tmp_win->iswspmgr) {
934 Context = C_WORKSPACE;
935 }
936 }
937
938 /*
939 * We've figured out the Context. Now see what modifiers we might
940 * have set...
941 */
942 unsigned int modifier = (Event.xkey.state | AlternateKeymap) & mods_used;
943 modifier = set_mask_ignore(modifier);
944 if(AlternateKeymap) {
945 XUngrabPointer(dpy, CurrentTime);
946 XUngrabKeyboard(dpy, CurrentTime);
947 AlternateKeymap = 0;
948 }
949
950
951 /*
952 * Loop over our key bindings and do its thing if we find a matching
953 * one.
954 */
955 for(FuncKey *key = Scr->FuncKeyRoot.next; key != NULL; key = key->next) {
956 /*
957 * Is this what we're trying to invoke? Gotta be the right key,
958 * and right modifier; those are easy.
959 *
960 * Context is tougher; that has to match what we're expecting as
961 * well, except in the case of C_NAME, which we always have to
962 * check to see if it'll match any windows. So if we have the
963 * right key and modifier, and it's a C_NAME context, it's a
964 * "maybe" match and we have to go through the checks.
965 */
966 if(key->keycode != Event.xkey.keycode ||
967 key->mods != modifier ||
968 (key->cont != Context && key->cont != C_NAME)) {
969 /* Nope, not yet */
970 continue;
971 }
972
973 /* 'k, it's a match (or potential match, in C_NAME case) */
974
975 /*
976 * Weed out the functions that don't make sense to execute from a
977 * key press
978 *
979 * TODO: add keyboard moving/resizing of windows.
980 */
981 if(key->func == F_MOVE || key->func == F_RESIZE) {
982 return;
983 }
984
985 if(key->cont != C_NAME) {
986 /* Normal context binding; do what it wants */
987 if(key->func == F_MENU) {
988 /*
989 * f.menu doesn't call the f.menu handler; we directly
990 * do_key_menu() to pull it up.
991 *
992 * Note this is "we called f.menu from a keybinding", not
993 * "we hit f.menu inside a menu we had up"; that's above.
994 */
995 ButtonWindow = Tmp_win;
996 do_key_menu(key->menu, (Window) None);
997 }
998 else {
999 #ifdef EWMH_DESKTOP_ROOT
1000 if(Context == C_ROOT && Tmp_win != NULL) {
1001 Context = C_WINDOW;
1002 fprintf(stderr, "HandleKeyPress: wt_Desktop -> C_WINDOW\n");
1003 }
1004 #endif /* EWMH */
1005 ExecuteFunction(key->func, key->action, Event.xany.window,
1006 Tmp_win, &Event, Context, false);
1007 if(!AlternateKeymap && !AlternateContext) {
1008 XUngrabPointer(dpy, CurrentTime);
1009 }
1010 }
1011 return;
1012 }
1013 else {
1014 /*
1015 * By-name binding (i.e., quoted string for the context
1016 * argument in config; see the manual). Find windows
1017 * matching that name and invoke on them, if any.
1018 *
1019 * This is the 'maybe' case above; we don't know whether this
1020 * does something until we try it. If we don't get a match,
1021 * we loop back around and keep going through our functions
1022 * until we do.
1023 */
1024 bool matched = false;
1025 const size_t len = strlen(key->win_name);
1026
1027 /* try and match the name first */
1028 for(Tmp_win = Scr->FirstWindow; Tmp_win != NULL;
1029 Tmp_win = Tmp_win->next) {
1030 if(!strncmp(key->win_name, Tmp_win->name, len)) {
1031 matched = true;
1032 ExecuteFunction(key->func, key->action, Tmp_win->frame,
1033 Tmp_win, &Event, C_FRAME, false);
1034 if(!AlternateKeymap && !AlternateContext) {
1035 XUngrabPointer(dpy, CurrentTime);
1036 }
1037 }
1038 }
1039
1040 /* now try the res_name */
1041 if(!matched) {
1042 for(Tmp_win = Scr->FirstWindow; Tmp_win != NULL;
1043 Tmp_win = Tmp_win->next) {
1044 if(!strncmp(key->win_name, Tmp_win->class.res_name, len)) {
1045 matched = true;
1046 ExecuteFunction(key->func, key->action, Tmp_win->frame,
1047 Tmp_win, &Event, C_FRAME, false);
1048 if(!AlternateKeymap && !AlternateContext) {
1049 XUngrabPointer(dpy, CurrentTime);
1050 }
1051 }
1052 }
1053 }
1054
1055 /* now try the res_class */
1056 if(!matched) {
1057 for(Tmp_win = Scr->FirstWindow; Tmp_win != NULL;
1058 Tmp_win = Tmp_win->next) {
1059 if(!strncmp(key->win_name, Tmp_win->class.res_class, len)) {
1060 matched = true;
1061 ExecuteFunction(key->func, key->action, Tmp_win->frame,
1062 Tmp_win, &Event, C_FRAME, false);
1063 if(!AlternateKeymap && !AlternateContext) {
1064 XUngrabPointer(dpy, CurrentTime);
1065 }
1066 }
1067 }
1068 }
1069
1070 /*
1071 * If we wound up invoking something, we're done, so return.
1072 * If we didn't, we fall through to the next loop through our
1073 * defined bindings.
1074 *
1075 * By-name bindings are unique in this; normal contexts
1076 * couldn't have multiple matches, so that side of things
1077 * finishes when it deals with its found match. But with
1078 * by-name we could have multiple bindings of a given
1079 * button/modifier with different names, so we have to go
1080 * back around to the next run through the for() loop.
1081 */
1082 if(matched) {
1083 return;
1084 }
1085 } // regular context or by-name?
1086 } // Loop over all bindings
1087
1088
1089 /*
1090 * If we get here, no function key was bound to the key. Send it to
1091 * the client if it was in a window we know about. Mostly this
1092 * doesn't happen; clients with focus get their events more directly,
1093 * but special cases may cause this.
1094 */
1095 if(Tmp_win) {
1096 if(Context == C_WORKSPACE) {
1097 WMgrHandleKeyPressEvent(Scr->currentvs, &Event);
1098 return;
1099 }
1100 if(Context == C_ICON ||
1101 Context == C_FRAME ||
1102 Context == C_TITLE ||
1103 Context == C_ICONMGR) {
1104 Event.xkey.window = Tmp_win->w;
1105 XSendEvent(dpy, Tmp_win->w, False, KeyPressMask, &Event);
1106 }
1107 }
1108
1109
1110 /* And done */
1111 }
1112
1113
1114
1115 /***********************************************************************
1116 *
1117 * Procedure:
1118 * HandlePropertyNotify - property notify event handler
1119 *
1120 ***********************************************************************
1121 */
1122
HandlePropertyNotify(void)1123 void HandlePropertyNotify(void)
1124 {
1125 Atom actual = None;
1126 int actual_format;
1127 unsigned long nitems, bytesafter;
1128 unsigned long valuemask; /* mask for create windows */
1129 XSetWindowAttributes attributes; /* attributes for create windows */
1130 Pixmap pm;
1131 Icon *icon;
1132
1133
1134 /* watch for standard colormap changes */
1135 if(Event.xproperty.window == Scr->Root) {
1136
1137 if(Event.xproperty.atom == XA_WM_CURRENTWORKSPACE) {
1138 unsigned char *prop;
1139 switch(Event.xproperty.state) {
1140 case PropertyNewValue:
1141 if(XGetWindowProperty(dpy, Scr->Root, XA_WM_CURRENTWORKSPACE,
1142 0L, 200L, False, XA_STRING, &actual, &actual_format,
1143 &nitems, &bytesafter, &prop) == Success) {
1144 if(nitems == 0) {
1145 return;
1146 }
1147 GotoWorkSpaceByName(Scr->vScreenList, (char *)prop);
1148 XFree(prop);
1149 }
1150 return;
1151
1152 default:
1153 return;
1154 }
1155 }
1156 switch(Event.xproperty.state) {
1157 case PropertyNewValue: {
1158 XStandardColormap *maps = NULL;
1159 int nmaps;
1160
1161 if(XGetRGBColormaps(dpy, Scr->Root, &maps, &nmaps,
1162 Event.xproperty.atom)) {
1163 /* if got one, then replace any existing entry */
1164 InsertRGBColormap(Event.xproperty.atom, maps, nmaps, true);
1165 }
1166 return;
1167 }
1168
1169 case PropertyDelete:
1170 RemoveRGBColormap(Event.xproperty.atom);
1171 return;
1172 }
1173 }
1174
1175 if(!Tmp_win) {
1176 return; /* unknown window */
1177 }
1178
1179 #define MAX_NAME_LEN 200L /* truncate to this many */
1180
1181 switch(Event.xproperty.atom) {
1182 case XA_WM_NAME: {
1183 char *prop = GetWMPropertyString(Tmp_win->w, XA_WM_NAME);
1184 if(prop == NULL) {
1185 // Clear
1186 FreeWMPropertyString(Tmp_win->names.wm_name);
1187 Tmp_win->names.wm_name = NULL;
1188 apply_window_name(Tmp_win);
1189 return;
1190 }
1191
1192 if(Tmp_win->names.wm_name != NULL
1193 && strcmp(Tmp_win->names.wm_name, prop) == 0) {
1194 /* No change, just free and skip out */
1195 free(prop);
1196 return;
1197 }
1198
1199 /* It's changing, free the old and bring in the new */
1200 FreeWMPropertyString(Tmp_win->names.wm_name);
1201 Tmp_win->names.wm_name = prop;
1202
1203 /* Kick the reset process */
1204 apply_window_name(Tmp_win);
1205
1206 break;
1207 }
1208
1209 case XA_WM_ICON_NAME: {
1210 char *prop = GetWMPropertyString(Tmp_win->w, XA_WM_ICON_NAME);
1211 if(prop == NULL) {
1212 // Clear
1213 FreeWMPropertyString(Tmp_win->names.wm_icon_name);
1214 Tmp_win->names.wm_icon_name = NULL;
1215 apply_window_icon_name(Tmp_win);
1216 return;
1217 }
1218
1219 /* No change? Nothing to do. */
1220 if(Tmp_win->names.wm_icon_name != NULL
1221 && strcmp(Tmp_win->names.wm_icon_name, prop) == 0) {
1222 free(prop);
1223 return;
1224 }
1225
1226 /* It's changing, free the old and bring in the new */
1227 FreeWMPropertyString(Tmp_win->names.wm_icon_name);
1228 Tmp_win->names.wm_icon_name = prop;
1229
1230 /* And show the new */
1231 apply_window_icon_name(Tmp_win);
1232
1233 break;
1234 }
1235
1236 case XA_WM_HINTS: {
1237 {
1238 XWMHints *nhints = XGetWMHints(dpy, Event.xany.window);
1239 if(!nhints) {
1240 /*
1241 * I guess this means that window removed the
1242 * property completely. Just keep using what we
1243 * already have for it though; we gotta have
1244 * something, and whatever it last said is probably
1245 * more reasonable than getting completely new
1246 * synthetic hints anyway.
1247 */
1248 break;
1249 }
1250
1251 XFree(Tmp_win->wmhints);
1252 Tmp_win->wmhints = munge_wmhints(Tmp_win, nhints);
1253 }
1254
1255 icon = Tmp_win->icon;
1256
1257 /*
1258 * If there already is an icon found in a way that has priority
1259 * over these hints, disable the flags and remove them from
1260 * consideration, now and in the future.
1261 */
1262 if(Tmp_win->forced ||
1263 (icon && icon->match == match_net_wm_icon)) {
1264 Tmp_win->wmhints->flags &= ~(IconWindowHint | IconPixmapHint | IconMaskHint);
1265 }
1266
1267 if(Tmp_win->wmhints->flags & IconWindowHint) {
1268 if(icon && icon->w) {
1269 int icon_x, icon_y;
1270
1271 /*
1272 * There's already an icon window.
1273 * Try to find out where it is; if we succeed, move the new
1274 * window to where the old one is.
1275 */
1276 if(XGetGeometry(dpy, icon->w, &JunkRoot, &icon_x,
1277 &icon_y, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth)) {
1278 /*
1279 * Move the new icon window to where the old one was.
1280 */
1281 XMoveWindow(dpy, Tmp_win->wmhints->icon_window, icon_x,
1282 icon_y);
1283 }
1284
1285 /*
1286 * If the window is iconic, map the new icon window.
1287 */
1288 if(Tmp_win->isicon) {
1289 XMapWindow(dpy, Tmp_win->wmhints->icon_window);
1290 }
1291
1292 /*
1293 * Now, if the old window isn't ours, unmap it, otherwise
1294 * just get rid of it completely.
1295 */
1296 if(icon->w_not_ours) {
1297 if(icon->w != Tmp_win->wmhints->icon_window) {
1298 XUnmapWindow(dpy, icon->w);
1299 }
1300 }
1301 else {
1302 XDestroyWindow(dpy, icon->w);
1303 }
1304
1305 /*
1306 * The new icon window isn't our window, so note that fact
1307 * so that we don't treat it as ours.
1308 */
1309 icon->w_not_ours = true;
1310
1311 /*
1312 * Now make the new window the icon window for this window,
1313 * and set it up to work as such (select for key presses
1314 * and button presses/releases, set up the contexts for it,
1315 * and define the cursor for it).
1316 */
1317 icon->w = Tmp_win->wmhints->icon_window;
1318 XSelectInput(dpy, icon->w,
1319 KeyPressMask | ButtonPressMask | ButtonReleaseMask);
1320 XSaveContext(dpy, icon->w, TwmContext, (XPointer)Tmp_win);
1321 XSaveContext(dpy, icon->w, ScreenContext, (XPointer)Scr);
1322 XDefineCursor(dpy, icon->w, Scr->IconCursor);
1323 }
1324 }
1325
1326 if(icon && icon->w &&
1327 (Tmp_win->wmhints->flags & IconPixmapHint)) {
1328 int x;
1329 unsigned int IconDepth;
1330
1331 if(!XGetGeometry(dpy, Tmp_win->wmhints->icon_pixmap, &JunkRoot,
1332 &JunkX, &JunkY, (unsigned int *)&icon->width,
1333 (unsigned int *)&icon->height, &JunkBW,
1334 &IconDepth)) {
1335 return;
1336 }
1337
1338 pm = XCreatePixmap(dpy, Scr->Root, icon->width,
1339 icon->height, Scr->d_depth);
1340
1341 FB(icon->iconc.fore, icon->iconc.back);
1342
1343 if(IconDepth == Scr->d_depth)
1344 XCopyArea(dpy, Tmp_win->wmhints->icon_pixmap, pm, Scr->NormalGC,
1345 0, 0, icon->width, icon->height, 0, 0);
1346 else
1347 XCopyPlane(dpy, Tmp_win->wmhints->icon_pixmap, pm, Scr->NormalGC,
1348 0, 0, icon->width, icon->height, 0, 0, 1);
1349
1350 if(icon->image) {
1351 /* Release the existing Image: it may be a shared one (UnknownIcon) */
1352 ReleaseIconImage(icon);
1353 /* conjure up a new Image */
1354 Image *image = AllocImage();
1355 image->pixmap = pm;
1356 image->width = icon->width;
1357 image->height = icon->height;
1358 icon->image = image;
1359 icon->match = match_icon_pixmap_hint;
1360 }
1361
1362 valuemask = CWBackPixmap;
1363 attributes.background_pixmap = pm;
1364
1365 if(icon->bm_w) {
1366 XDestroyWindow(dpy, icon->bm_w);
1367 }
1368
1369 x = GetIconOffset(icon);
1370 icon->bm_w =
1371 XCreateWindow(dpy, icon->w, x, 0,
1372 icon->width,
1373 icon->height,
1374 0, Scr->d_depth,
1375 CopyFromParent, Scr->d_visual,
1376 valuemask, &attributes);
1377
1378 if(!(Tmp_win->wmhints->flags & IconMaskHint)) {
1379 XRectangle rect;
1380
1381 rect.x = x;
1382 rect.y = 0;
1383 rect.width = icon->width;
1384 rect.height = icon->height;
1385 XShapeCombineRectangles(dpy, icon->w, ShapeBounding, 0,
1386 0, &rect, 1, ShapeUnion, 0);
1387 }
1388 XMapSubwindows(dpy, icon->w);
1389 RedoIconName(Tmp_win);
1390 }
1391 if(icon && icon->w &&
1392 (Tmp_win->wmhints->flags & IconMaskHint) &&
1393 icon->match == match_icon_pixmap_hint) {
1394 /* Only set the mask if the pixmap came from a WM_HINTS too,
1395 * for easier resource management.
1396 */
1397 int x;
1398 Pixmap mask;
1399 GC gc;
1400 unsigned int IconWidth, IconHeight, IconDepth;
1401
1402 if(!XGetGeometry(dpy, Tmp_win->wmhints->icon_mask, &JunkRoot,
1403 &JunkX, &JunkY, &IconWidth, &IconHeight, &JunkBW,
1404 &IconDepth)) {
1405 return;
1406 }
1407 if(IconDepth != 1) {
1408 return;
1409 }
1410
1411 mask = XCreatePixmap(dpy, Scr->Root, IconWidth, IconHeight, 1);
1412 if(!mask) {
1413 return;
1414 }
1415 gc = XCreateGC(dpy, mask, 0, NULL);
1416 if(!gc) {
1417 return;
1418 }
1419 XCopyArea(dpy, Tmp_win->wmhints->icon_mask, mask, gc,
1420 0, 0, IconWidth, IconHeight, 0, 0);
1421 XFreeGC(dpy, gc);
1422 x = GetIconOffset(icon);
1423 XShapeCombineMask(dpy, icon->bm_w, ShapeBounding, 0, 0, mask,
1424 ShapeSet);
1425 XShapeCombineMask(dpy, icon->w, ShapeBounding, x, 0, mask,
1426 ShapeSet);
1427 if(icon->image) {
1428 if(icon->image->mask) {
1429 XFreePixmap(dpy, icon->image->mask);
1430 }
1431 icon->image->mask = mask;
1432 RedoIconName(Tmp_win);
1433 }
1434 }
1435 if(Tmp_win->wmhints->flags & IconPixmapHint) {
1436 AutoPopupMaybe(Tmp_win);
1437 }
1438
1439 break;
1440 }
1441
1442 case XA_WM_NORMAL_HINTS: {
1443 GetWindowSizeHints(Tmp_win);
1444 break;
1445 }
1446 default: {
1447 if(Event.xproperty.atom == XA_WM_COLORMAP_WINDOWS) {
1448 FetchWmColormapWindows(Tmp_win); /* frees old data */
1449 break;
1450 }
1451 else if(Event.xproperty.atom == XA_WM_PROTOCOLS) {
1452 FetchWmProtocols(Tmp_win);
1453 break;
1454 }
1455 else if(Event.xproperty.atom == XA_WM_OCCUPATION) {
1456 unsigned char *prop;
1457 if(XGetWindowProperty(dpy, Tmp_win->w, Event.xproperty.atom, 0L, MAX_NAME_LEN,
1458 False,
1459 XA_STRING, &actual, &actual_format, &nitems,
1460 &bytesafter, &prop) != Success ||
1461 actual == None) {
1462 return;
1463 }
1464 ChangeOccupation(Tmp_win, GetMaskFromProperty(prop, nitems));
1465 XFree(prop);
1466 }
1467 else if(Event.xproperty.atom == XA_CTWM_WM_NAME) {
1468 char *prop = GetWMPropertyString(Tmp_win->w, XA_CTWM_WM_NAME);
1469 if(prop == NULL) {
1470 // Clearing
1471 FreeWMPropertyString(Tmp_win->names.ctwm_wm_name);
1472 Tmp_win->names.ctwm_wm_name = NULL;
1473 apply_window_name(Tmp_win);
1474 return;
1475 }
1476
1477 if(Tmp_win->names.ctwm_wm_name != NULL
1478 && strcmp(Tmp_win->names.ctwm_wm_name,
1479 prop) == 0) {
1480 /* No change, just free and skip out */
1481 free(prop);
1482 return;
1483 }
1484
1485 /* It's changing, free the old and bring in the new */
1486 FreeWMPropertyString(Tmp_win->names.ctwm_wm_name);
1487 Tmp_win->names.ctwm_wm_name = prop;
1488
1489 /* Kick the reset process */
1490 apply_window_name(Tmp_win);
1491
1492 return;
1493 }
1494 else if(Event.xproperty.atom == XA_CTWM_WM_ICON_NAME) {
1495 char *prop = GetWMPropertyString(Tmp_win->w,
1496 XA_CTWM_WM_ICON_NAME);
1497 if(prop == NULL) {
1498 // Clearing
1499 FreeWMPropertyString(Tmp_win->names.ctwm_wm_icon_name);
1500 Tmp_win->names.ctwm_wm_icon_name = NULL;
1501 apply_window_icon_name(Tmp_win);
1502 return;
1503 }
1504
1505 if(Tmp_win->names.ctwm_wm_icon_name != NULL
1506 && strcmp(Tmp_win->names.ctwm_wm_icon_name,
1507 prop) == 0) {
1508 /* No change, just free and skip out */
1509 free(prop);
1510 return;
1511 }
1512
1513 /* It's changing, free the old and bring in the new */
1514 FreeWMPropertyString(Tmp_win->names.ctwm_wm_icon_name);
1515 Tmp_win->names.ctwm_wm_icon_name = prop;
1516
1517 /* Kick the reset process */
1518 apply_window_icon_name(Tmp_win);
1519
1520 break;
1521 }
1522 #ifdef EWMH
1523 else if(EwmhHandlePropertyNotify(&Event.xproperty, Tmp_win)) {
1524 /* event handled */
1525 }
1526 #endif /* EWMH */
1527 break;
1528 }
1529 }
1530 }
1531
1532
1533 /***********************************************************************
1534 *
1535 * Procedure:
1536 * HandleClientMessage - client message event handler
1537 *
1538 ***********************************************************************
1539 */
1540
HandleClientMessage(void)1541 void HandleClientMessage(void)
1542 {
1543
1544 if(Event.xclient.message_type == XA_WM_CHANGE_STATE) {
1545 if(Tmp_win != NULL) {
1546 if(Event.xclient.data.l[0] == IconicState && !Tmp_win->isicon) {
1547 XEvent button;
1548 XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkChild,
1549 &(button.xmotion.x_root),
1550 &(button.xmotion.y_root),
1551 &JunkX, &JunkY, &JunkMask);
1552
1553 ExecuteFunction(F_ICONIFY, NULL, Event.xany.window,
1554 Tmp_win, &button, FRAME, false);
1555 XUngrabPointer(dpy, CurrentTime);
1556 }
1557 }
1558 return;
1559 }
1560
1561 #ifdef EWMH
1562 if(EwmhClientMessage(&Event.xclient)) {
1563 return;
1564 }
1565 #endif
1566
1567 else if((Event.xclient.message_type == XA_WM_PROTOCOLS) &&
1568 (Event.xclient.data.l[0] == XA_WM_END_OF_ANIMATION)) {
1569 if(Animating > 0) {
1570 Animating--;
1571 }
1572 else {
1573 fprintf(stderr, "!! end of unknown animation !!\n");
1574 }
1575 }
1576 }
1577
1578
1579 /***********************************************************************
1580 *
1581 * Procedure:
1582 * HandleExpose - expose event handler
1583 *
1584 ***********************************************************************
1585 */
1586
1587 static void flush_expose(Window w);
1588
HandleExpose(void)1589 void HandleExpose(void)
1590 {
1591 MenuRoot *tmp;
1592 VirtualScreen *vs;
1593
1594 if(XFindContext(dpy, Event.xany.window, MenuContext, (XPointer *)&tmp) == 0) {
1595 PaintMenu(tmp, &Event);
1596 return;
1597 }
1598
1599 if(Event.xexpose.count != 0) {
1600 return;
1601 }
1602
1603 if(Event.xany.window == Scr->InfoWindow.win && Scr->InfoWindow.mapped) {
1604 draw_info_window();
1605 flush_expose(Event.xany.window);
1606 }
1607 else if(Tmp_win != NULL) {
1608 if(Scr->use3Dborders && (Event.xany.window == Tmp_win->frame)) {
1609 PaintBorders(Tmp_win, ((Tmp_win == Scr->Focus) ? true : false));
1610 flush_expose(Event.xany.window);
1611 return;
1612 }
1613 else if(Event.xany.window == Tmp_win->title_w) {
1614 PaintTitle(Tmp_win);
1615 flush_expose(Event.xany.window);
1616 return;
1617 }
1618 else if(Tmp_win->icon && (Event.xany.window == Tmp_win->icon->w) &&
1619 ! Scr->NoIconTitlebar &&
1620 ! LookInList(Scr->NoIconTitle, Tmp_win->name, &Tmp_win->class)) {
1621 PaintIcon(Tmp_win);
1622 flush_expose(Event.xany.window);
1623 return;
1624 }
1625 else if(Tmp_win->titlebuttons) {
1626 int i;
1627 TBWindow *tbw;
1628 Window w = Event.xany.window;
1629 int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
1630
1631 /*
1632 * This looks an awful lot like a manual reimplementation of
1633 * PaintTitleButtons(). It's not quite though, it's just
1634 * looking up one button to paint it. And it would be a
1635 * little grody trying to shoehorn it in.
1636 */
1637 for(i = 0, tbw = Tmp_win->titlebuttons; i < nb; i++, tbw++) {
1638 if(w == tbw->window) {
1639 PaintTitleButton(Tmp_win, tbw);
1640 flush_expose(tbw->window);
1641 return;
1642 }
1643 }
1644 }
1645 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
1646 if(Tmp_win == vs->wsw->twm_win) {
1647 WMgrHandleExposeEvent(vs, &Event);
1648 flush_expose(Event.xany.window);
1649 return;
1650 }
1651 }
1652 if(Tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) {
1653 /*
1654 * The occupyWindow has a bunch of sub-windows for each
1655 * button in it, and each of them wind up getting Expose
1656 * events kicked for them. The upshot is that we re-paint
1657 * the occupy window once for itself, and then once for each
1658 * button inside it. We'll always get one for the window
1659 * itself, so just paint it for that one, and ignore the rest
1660 * of the events.
1661 *
1662 * XXX Maybe a better solution is just to mask off Expose
1663 * events for the other windows...
1664 */
1665 if(Event.xany.window == Scr->workSpaceMgr.occupyWindow->w) {
1666 PaintOccupyWindow();
1667 flush_expose(Event.xany.window);
1668 }
1669 return;
1670 }
1671 else if(Tmp_win->iconmanagerlist) {
1672 WList *iconmanagerlist = Tmp_win->iconmanagerlist;
1673
1674 if(Event.xany.window == iconmanagerlist->w) {
1675 DrawIconManagerIconName(Tmp_win);
1676 flush_expose(Event.xany.window);
1677 return;
1678 }
1679 else if(Event.xany.window == iconmanagerlist->icon) {
1680 ShowIconifiedIcon(Tmp_win);
1681 flush_expose(Event.xany.window);
1682 return;
1683 }
1684 }
1685 }
1686 }
1687
1688
remove_window_from_ring(TwmWindow * tmp)1689 static void remove_window_from_ring(TwmWindow *tmp)
1690 {
1691 TwmWindow *prev = tmp->ring.prev, *next = tmp->ring.next;
1692
1693 if(enter_win == tmp) {
1694 enter_flag = false;
1695 enter_win = NULL;
1696 }
1697 if(raise_win == Tmp_win) {
1698 raise_win = NULL;
1699 }
1700 if(leave_win == tmp) {
1701 leave_flag = false;
1702 leave_win = NULL;
1703 }
1704 if(lower_win == Tmp_win) {
1705 lower_win = NULL;
1706 }
1707
1708 /*
1709 * 1. Unlink window
1710 * 2. If window was only thing in ring, null out ring
1711 * 3. If window was ring leader, set to next (or null)
1712 */
1713 if(prev) {
1714 prev->ring.next = next;
1715 }
1716 if(next) {
1717 next->ring.prev = prev;
1718 }
1719 if(Scr->Ring == tmp) {
1720 Scr->Ring = (next != tmp ? next : NULL);
1721 }
1722
1723 if(!Scr->Ring || Scr->RingLeader == tmp) {
1724 Scr->RingLeader = Scr->Ring;
1725 }
1726 }
1727
1728
1729 /***********************************************************************
1730 *
1731 * Procedure:
1732 * HandleDestroyNotify - DestroyNotify event handler
1733 *
1734 ***********************************************************************
1735 */
1736
HandleDestroyNotify(void)1737 void HandleDestroyNotify(void)
1738 {
1739 /*
1740 * Warning, this is also called by HandleUnmapNotify; if it ever needs to
1741 * look at the event, HandleUnmapNotify will have to mash the UnmapNotify
1742 * into a DestroyNotify.
1743 */
1744
1745 if(Tmp_win == NULL) {
1746 return;
1747 }
1748
1749 RemoveWindowFromRegion(Tmp_win);
1750
1751 if(Tmp_win->icon != NULL) {
1752 OtpRemove(Tmp_win, IconWin);
1753 }
1754 OtpRemove(Tmp_win, WinWin);
1755
1756 #ifdef EWMH
1757 /* Remove the old window from the EWMH client list */
1758 EwmhDeleteClientWindow(Tmp_win);
1759 EwmhSet_NET_CLIENT_LIST_STACKING();
1760 #endif /* EWMH */
1761 if(Tmp_win == Scr->Focus) {
1762 Scr->Focus = NULL;
1763 FocusOnRoot();
1764 }
1765 if(Scr->SaveWorkspaceFocus) {
1766 struct WorkSpace *ws;
1767 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
1768 if(ws->save_focus == Tmp_win) {
1769 ws->save_focus = NULL;
1770 }
1771 }
1772 }
1773 XDeleteContext(dpy, Tmp_win->w, TwmContext);
1774 XDeleteContext(dpy, Tmp_win->w, ScreenContext);
1775 XDeleteContext(dpy, Tmp_win->frame, TwmContext);
1776 XDeleteContext(dpy, Tmp_win->frame, ScreenContext);
1777 if(Tmp_win->icon && Tmp_win->icon->w) {
1778 XDeleteContext(dpy, Tmp_win->icon->w, TwmContext);
1779 XDeleteContext(dpy, Tmp_win->icon->w, ScreenContext);
1780 }
1781 if(Tmp_win->title_height) {
1782 int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
1783
1784 XDeleteContext(dpy, Tmp_win->title_w, TwmContext);
1785 XDeleteContext(dpy, Tmp_win->title_w, ScreenContext);
1786 if(Tmp_win->hilite_wl) {
1787 XDeleteContext(dpy, Tmp_win->hilite_wl, TwmContext);
1788 XDeleteContext(dpy, Tmp_win->hilite_wl, ScreenContext);
1789 }
1790 if(Tmp_win->hilite_wr) {
1791 XDeleteContext(dpy, Tmp_win->hilite_wr, TwmContext);
1792 XDeleteContext(dpy, Tmp_win->hilite_wr, ScreenContext);
1793 }
1794 if(Tmp_win->lolite_wr) {
1795 XDeleteContext(dpy, Tmp_win->lolite_wr, TwmContext);
1796 XDeleteContext(dpy, Tmp_win->lolite_wr, ScreenContext);
1797 }
1798 if(Tmp_win->lolite_wl) {
1799 XDeleteContext(dpy, Tmp_win->lolite_wl, TwmContext);
1800 XDeleteContext(dpy, Tmp_win->lolite_wl, ScreenContext);
1801 }
1802 if(Tmp_win->titlebuttons) {
1803 int i;
1804
1805 for(i = 0; i < nb; i++) {
1806 XDeleteContext(dpy, Tmp_win->titlebuttons[i].window,
1807 TwmContext);
1808 XDeleteContext(dpy, Tmp_win->titlebuttons[i].window,
1809 ScreenContext);
1810 }
1811 }
1812 /*
1813 * The hilite_wl etc windows don't need to be XDestroyWindow()ed
1814 * since that will happen when the parent is destroyed (??)
1815 */
1816 }
1817
1818 if(Scr->cmapInfo.cmaps == &Tmp_win->cmaps) {
1819 InstallColormaps(DestroyNotify, &Scr->RootColormaps);
1820 }
1821
1822 /*
1823 * TwmWindows contain the following pointers
1824 *
1825 * 1. (obsolete)
1826 * 2. name
1827 * 3. icon_name
1828 * 4. wmhints
1829 * 5. class.res_name
1830 * 6. class.res_class
1831 * 7. list
1832 * 8. iconmgrp
1833 * 9. cwins
1834 * 10. titlebuttons
1835 * 11. window ring
1836 * 12. squeeze_info (delete if squeeze_info_copied)
1837 * 13. HiliteImage
1838 * 14. iconslist
1839 */
1840 WMapRemoveWindow(Tmp_win);
1841 if(Tmp_win->gray) {
1842 XFreePixmap(dpy, Tmp_win->gray);
1843 }
1844
1845 /*
1846 * According to the manual page, the following destroys all child windows
1847 * of the frame too, which is most of the windows we're concerned with, so
1848 * anything related to them must be done before here.
1849 * Icons are not child windows.
1850 */
1851 XDestroyWindow(dpy, Tmp_win->frame);
1852 DeleteIconsList(Tmp_win); /* 14 */
1853 if(Tmp_win->icon) {
1854 Icon *icon = Tmp_win->icon;
1855 if(icon->w && !icon->w_not_ours) {
1856 IconDown(Tmp_win);
1857 }
1858 DeleteIcon(icon);
1859 Tmp_win->icon = NULL;
1860 }
1861 Tmp_win->occupation = 0;
1862 RemoveIconManager(Tmp_win); /* 7 */
1863 if(Scr->FirstWindow == Tmp_win) {
1864 Scr->FirstWindow = Tmp_win->next;
1865 }
1866 if(Tmp_win->prev != NULL) {
1867 Tmp_win->prev->next = Tmp_win->next;
1868 }
1869 if(Tmp_win->next != NULL) {
1870 Tmp_win->next->prev = Tmp_win->prev;
1871 }
1872 if(Tmp_win->auto_raise) {
1873 Scr->NumAutoRaises--;
1874 }
1875 if(Tmp_win->auto_lower) {
1876 Scr->NumAutoLowers--;
1877 }
1878
1879 FreeWMPropertyString(Tmp_win->names.ctwm_wm_name); // 2
1880 FreeWMPropertyString(Tmp_win->names.wm_name); // 2
1881 FreeWMPropertyString(Tmp_win->names.ctwm_wm_icon_name); // 3
1882 FreeWMPropertyString(Tmp_win->names.wm_icon_name); // 3
1883 #ifdef EWMH
1884 FreeWMPropertyString(Tmp_win->names.net_wm_name); // 2
1885 FreeWMPropertyString(Tmp_win->names.net_wm_icon_name); // 3
1886 #endif
1887
1888 XFree(Tmp_win->wmhints); /* 4 */
1889 if(Tmp_win->class.res_name && Tmp_win->class.res_name != NoName) { /* 5 */
1890 XFree(Tmp_win->class.res_name);
1891 }
1892 if(Tmp_win->class.res_class && Tmp_win->class.res_class != NoName) { /* 6 */
1893 XFree(Tmp_win->class.res_class);
1894 }
1895 free_cwins(Tmp_win); /* 9 */
1896 if(Tmp_win->titlebuttons) { /* 10 */
1897 free(Tmp_win->titlebuttons);
1898 Tmp_win->titlebuttons = NULL;
1899 }
1900
1901 remove_window_from_ring(Tmp_win); /* 11 */
1902 if(Tmp_win->squeeze_info_copied) { /* 12 */
1903 free(Tmp_win->squeeze_info);
1904 Tmp_win->squeeze_info = NULL;
1905 }
1906 DeleteHighlightWindows(Tmp_win); /* 13 */
1907
1908 free(Tmp_win);
1909 Tmp_win = NULL;
1910
1911 if(Scr->ClickToFocus || Scr->SloppyFocus) {
1912 set_last_window(Scr->currentvs->wsw->currentwspc);
1913 }
1914 }
1915
1916
1917 void
HandleCreateNotify(void)1918 HandleCreateNotify(void)
1919 {
1920 #ifdef DEBUG_EVENTS
1921 fprintf(stderr, "CreateNotify w = 0x%x\n",
1922 (unsigned)Event.xcreatewindow.window);
1923 fflush(stderr);
1924 XBell(dpy, 0);
1925 XSync(dpy, 0);
1926 #endif
1927 }
1928
1929
1930 /***********************************************************************
1931 *
1932 * Procedure:
1933 * HandleMapRequest - MapRequest event handler
1934 *
1935 ***********************************************************************
1936 */
1937
HandleMapRequest(void)1938 void HandleMapRequest(void)
1939 {
1940 int zoom_save;
1941
1942 Event.xany.window = Event.xmaprequest.window;
1943 Tmp_win = GetTwmWindow(Event.xany.window);
1944
1945 /* If the window has never been mapped before ... */
1946 if(Tmp_win == NULL) {
1947 /* Add decorations. */
1948 VirtualScreen *vs = Scr->currentvs;
1949
1950 Tmp_win = AddWindow(Event.xany.window,
1951 AWT_NORMAL,
1952 NULL,
1953 vs);
1954 if(Tmp_win == NULL) {
1955 return;
1956 }
1957 #ifdef EWMH
1958 /* add the new window to the EWMH client list */
1959 EwmhAddClientWindow(Tmp_win);
1960 EwmhSet_NET_CLIENT_LIST_STACKING();
1961
1962 /* Tell it whatever we think of it */
1963 EwmhSet_NET_WM_STATE(Tmp_win, EWMH_STATE_ALL);
1964 #endif /* EWMH */
1965 }
1966 else {
1967 /*
1968 * If the window has been unmapped by the client, it won't be listed
1969 * in the icon manager. Add it again, if requested.
1970 */
1971 if(Tmp_win->iconmanagerlist == NULL) {
1972 AddIconManager(Tmp_win);
1973 }
1974 }
1975
1976 if(Tmp_win->isiconmgr) {
1977 return;
1978 }
1979 if(Tmp_win->squeezed) {
1980 return;
1981 }
1982
1983 if(Scr->WindowMask) {
1984 XRaiseWindow(dpy, Scr->WindowMask);
1985 }
1986
1987 /* If it's not merely iconified, and we have hints, use them. */
1988 if(! Tmp_win->isicon) {
1989 int state;
1990 Window icon;
1991
1992 state = NormalState;
1993 /* use WM_STATE if enabled */
1994 if(!(RestartPreviousState && GetWMState(Tmp_win->w, &state, &icon) &&
1995 (state == NormalState || state == IconicState || state == InactiveState))) {
1996 if(Tmp_win->wmhints->flags & StateHint) {
1997 state = Tmp_win->wmhints->initial_state;
1998 }
1999 }
2000 switch(state) {
2001 case DontCareState:
2002 case NormalState:
2003 case ZoomState:
2004 if(Tmp_win->StartSqueezed) {
2005 Squeeze(Tmp_win);
2006 }
2007 else {
2008 XMapWindow(dpy, Tmp_win->w);
2009 }
2010 XMapWindow(dpy, Tmp_win->frame);
2011 SetMapStateProp(Tmp_win, NormalState);
2012 SetRaiseWindow(Tmp_win);
2013 Tmp_win->mapped = true;
2014 if(Scr->ClickToFocus && Tmp_win->wmhints->input) {
2015 SetFocus(Tmp_win, CurrentTime);
2016 }
2017 /* kai */
2018 if(Scr->AutoFocusToTransients &&
2019 Tmp_win->istransient &&
2020 Tmp_win->wmhints->input) {
2021 SetFocus(Tmp_win, CurrentTime);
2022 }
2023 break;
2024
2025 case InactiveState:
2026 if(!OCCUPY(Tmp_win, Scr->currentvs->wsw->currentwspc) &&
2027 HandlingEvents && /* to avoid warping during startup */
2028 LookInList(Scr->WarpOnDeIconify, Tmp_win->name, &Tmp_win->class)) {
2029 if(!Scr->NoRaiseDeicon) {
2030 OtpRaise(Tmp_win, WinWin);
2031 }
2032 AddToWorkSpace(Scr->currentvs->wsw->currentwspc->name, Tmp_win);
2033 }
2034 Tmp_win->mapped = true;
2035 if(Tmp_win->UnmapByMovingFarAway) {
2036 XMoveWindow(dpy, Tmp_win->frame, Scr->rootw + 1, Scr->rooth + 1);
2037 XMapWindow(dpy, Tmp_win->w);
2038 XMapWindow(dpy, Tmp_win->frame);
2039 }
2040 if(Tmp_win->StartSqueezed) {
2041 Squeeze(Tmp_win);
2042 }
2043 break;
2044
2045 case IconicState:
2046 zoom_save = Scr->DoZoom;
2047 Scr->DoZoom = false;
2048 Iconify(Tmp_win, -100, -100);
2049 Scr->DoZoom = zoom_save;
2050 break;
2051 }
2052 }
2053 /* If no hints, or currently an icon, just "deiconify" */
2054 else {
2055 if(!OCCUPY(Tmp_win, Scr->currentvs->wsw->currentwspc) &&
2056 LookInList(Scr->WarpOnDeIconify, Tmp_win->name, &Tmp_win->class)) {
2057 AddToWorkSpace(Scr->currentvs->wsw->currentwspc->name, Tmp_win);
2058 }
2059 if(1/*OCCUPY (Tmp_win, Scr->workSpaceMgr.activeWSPC)*/) {
2060 if(Tmp_win->StartSqueezed) {
2061 Squeeze(Tmp_win);
2062 }
2063 DeIconify(Tmp_win);
2064 SetRaiseWindow(Tmp_win);
2065 }
2066 else {
2067 Tmp_win->mapped = true;
2068 }
2069 }
2070 if(Tmp_win->mapped) {
2071 WMapMapWindow(Tmp_win);
2072 }
2073 MaybeAnimate = true;
2074 }
2075
2076
2077 /***********************************************************************
2078 *
2079 * Procedure:
2080 * HandleMapNotify - MapNotify event handler
2081 *
2082 ***********************************************************************
2083 */
2084
HandleMapNotify(void)2085 void HandleMapNotify(void)
2086 {
2087 if(Tmp_win == NULL) {
2088 return;
2089 }
2090
2091 /*
2092 * Need to do the grab to avoid race condition of having server send
2093 * MapNotify to client before the frame gets mapped; this is bad because
2094 * the client would think that the window has a chance of being viewable
2095 * when it really isn't.
2096 */
2097
2098 XGrabServer(dpy);
2099 if(Tmp_win->icon && Tmp_win->icon->w) {
2100 XUnmapWindow(dpy, Tmp_win->icon->w);
2101 }
2102 if(Tmp_win->title_w) {
2103 XMapSubwindows(dpy, Tmp_win->title_w);
2104 }
2105 XMapSubwindows(dpy, Tmp_win->frame);
2106 if(Scr->Focus != Tmp_win && Tmp_win->hilite_wl) {
2107 XUnmapWindow(dpy, Tmp_win->hilite_wl);
2108 }
2109 if(Scr->Focus != Tmp_win && Tmp_win->hilite_wr) {
2110 XUnmapWindow(dpy, Tmp_win->hilite_wr);
2111 }
2112 if(Scr->Focus == Tmp_win && Tmp_win->lolite_wl) {
2113 XUnmapWindow(dpy, Tmp_win->lolite_wl);
2114 }
2115 if(Scr->Focus == Tmp_win && Tmp_win->lolite_wr) {
2116 XUnmapWindow(dpy, Tmp_win->lolite_wr);
2117 }
2118
2119 XMapWindow(dpy, Tmp_win->frame);
2120 XUngrabServer(dpy);
2121 XFlush(dpy);
2122 Tmp_win->mapped = true;
2123 Tmp_win->isicon = false;
2124 Tmp_win->icon_on = false;
2125 }
2126
2127
2128 /***********************************************************************
2129 *
2130 * Procedure:
2131 * HandleUnmapNotify - UnmapNotify event handler
2132 *
2133 ***********************************************************************
2134 */
2135
HandleUnmapNotify(void)2136 void HandleUnmapNotify(void)
2137 {
2138 int dstx, dsty;
2139 Window dumwin;
2140
2141 /*
2142 * The July 27, 1988 ICCCM spec states that a client wishing to switch
2143 * to WithdrawnState should send a synthetic UnmapNotify with the
2144 * event field set to (pseudo-)root, in case the window is already
2145 * unmapped (which is the case for twm for IconicState). Unfortunately,
2146 * we looked for the TwmContext using that field, so try the window
2147 * field also.
2148 */
2149 if(Tmp_win == NULL) {
2150 Event.xany.window = Event.xunmap.window;
2151 Tmp_win = GetTwmWindow(Event.xany.window);
2152 }
2153
2154 if(Tmp_win == NULL || Event.xunmap.window == Tmp_win->frame ||
2155 (Tmp_win->icon && Event.xunmap.window == Tmp_win->icon->w) ||
2156 (!Tmp_win->mapped && !Tmp_win->isicon)) {
2157 return;
2158 }
2159 /*
2160 if (Tmp_win == NULL || (!Tmp_win->mapped && !Tmp_win->isicon))
2161 return;
2162 */
2163 /*
2164 * The program may have unmapped the client window, from either
2165 * NormalState or IconicState. Handle the transition to WithdrawnState.
2166 *
2167 * We need to reparent the window back to the root (so that twm exiting
2168 * won't cause it to get mapped) and then throw away all state (pretend
2169 * that we've received a DestroyNotify).
2170 */
2171 /* Is it the correct behaviour ???
2172 XDeleteProperty (dpy, Tmp_win->w, XA_WM_OCCUPATION);
2173 */
2174 #ifdef EWMH
2175 EwmhUnmapNotify(Tmp_win);
2176 #endif /* EWMH */
2177 XGrabServer(dpy);
2178 if(XTranslateCoordinates(dpy, Event.xunmap.window, Tmp_win->attr.root,
2179 0, 0, &dstx, &dsty, &dumwin)) {
2180 XEvent ev;
2181 Bool reparented = XCheckTypedWindowEvent(dpy, Event.xunmap.window,
2182 ReparentNotify, &ev);
2183 SetMapStateProp(Tmp_win, WithdrawnState);
2184 if(reparented) {
2185 if(Tmp_win->old_bw) XSetWindowBorderWidth(dpy,
2186 Event.xunmap.window,
2187 Tmp_win->old_bw);
2188 if(Tmp_win->wmhints->flags & IconWindowHint) {
2189 XUnmapWindow(dpy, Tmp_win->wmhints->icon_window);
2190 }
2191 }
2192 else {
2193 XReparentWindow(dpy, Event.xunmap.window, Tmp_win->attr.root,
2194 dstx, dsty);
2195 RestoreWithdrawnLocation(Tmp_win);
2196 }
2197 XRemoveFromSaveSet(dpy, Event.xunmap.window);
2198 XSelectInput(dpy, Event.xunmap.window, NoEventMask);
2199 HandleDestroyNotify(); /* do not need to mash event before */
2200 } /* else window no longer exists and we'll get a destroy notify */
2201 XUngrabServer(dpy);
2202 XFlush(dpy);
2203 }
2204
2205
2206 /***********************************************************************
2207 *
2208 * Procedure:
2209 * HandleMotionNotify - MotionNotify event handler
2210 *
2211 ***********************************************************************
2212 */
2213
HandleMotionNotify(void)2214 void HandleMotionNotify(void)
2215 {
2216 if(ResizeWindow != (Window) 0) {
2217 XQueryPointer(dpy, Event.xany.window,
2218 &(Event.xmotion.root), &JunkChild,
2219 &(Event.xmotion.x_root), &(Event.xmotion.y_root),
2220 &(Event.xmotion.x), &(Event.xmotion.y),
2221 &JunkMask);
2222
2223 FixRootEvent(&Event);
2224 /* Set WindowMoved appropriately so that f.deltastop will
2225 work with resize as well as move. */
2226 if(abs(Event.xmotion.x - ResizeOrigX) >= Scr->MoveDelta
2227 || abs(Event.xmotion.y - ResizeOrigY) >= Scr->MoveDelta) {
2228 WindowMoved = true;
2229 }
2230
2231 Tmp_win = GetTwmWindow(ResizeWindow);
2232 if(Tmp_win && Tmp_win->winbox) {
2233 XTranslateCoordinates(dpy, Scr->Root, Tmp_win->winbox->window,
2234 Event.xmotion.x_root, Event.xmotion.y_root,
2235 &(Event.xmotion.x_root), &(Event.xmotion.y_root), &JunkChild);
2236 }
2237 DoResize(Event.xmotion.x_root, Event.xmotion.y_root, Tmp_win);
2238 }
2239 else if(Scr->BorderCursors && Tmp_win && Event.xany.window == Tmp_win->frame) {
2240 SetBorderCursor(Tmp_win, Event.xmotion.x, Event.xmotion.y);
2241 }
2242 }
2243
2244
2245 /***********************************************************************
2246 *
2247 * Procedure:
2248 * HandleButtonRelease - ButtonRelease event handler
2249 *
2250 ***********************************************************************
2251 */
HandleButtonRelease(void)2252 void HandleButtonRelease(void)
2253 {
2254 int xl, yt, w, h;
2255 unsigned mask;
2256
2257 if(Scr->InfoWindow.mapped) { /* delete info box on 2nd button release */
2258 if(Context == C_IDENTIFY) {
2259 XUnmapWindow(dpy, Scr->InfoWindow.win);
2260 Scr->InfoWindow.mapped = false;
2261 Context = C_NO_CONTEXT;
2262 }
2263 }
2264
2265 if(DragWindow != None) {
2266 MoveOutline(Scr->XineramaRoot, 0, 0, 0, 0, 0, 0);
2267
2268 Tmp_win = GetTwmWindow(DragWindow);
2269 if(Tmp_win->winbox) {
2270 XTranslateCoordinates(dpy, Scr->Root, Tmp_win->winbox->window,
2271 Event.xbutton.x_root, Event.xbutton.y_root,
2272 &(Event.xbutton.x_root), &(Event.xbutton.y_root), &JunkChild);
2273 }
2274 if(DragWindow == Tmp_win->frame) {
2275 xl = Event.xbutton.x_root - DragX - Tmp_win->frame_bw;
2276 yt = Event.xbutton.y_root - DragY - Tmp_win->frame_bw;
2277 w = DragWidth + 2 * Tmp_win->frame_bw;
2278 h = DragHeight + 2 * Tmp_win->frame_bw;
2279 }
2280 else {
2281 xl = Event.xbutton.x_root - DragX - DragBW;
2282 yt = Event.xbutton.y_root - DragY - DragBW;
2283 w = DragWidth + 2 * DragBW;
2284 h = DragHeight + 2 * DragBW;
2285 }
2286
2287 if(ConstMove) {
2288 if(ConstMoveDir == MOVE_HORIZ) {
2289 yt = ConstMoveY;
2290 }
2291
2292 if(ConstMoveDir == MOVE_VERT) {
2293 xl = ConstMoveX;
2294 }
2295
2296 if(ConstMoveDir == MOVE_NONE) {
2297 yt = ConstMoveY;
2298 xl = ConstMoveX;
2299 }
2300 }
2301
2302 if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) {
2303 TryToGrid(Tmp_win, &xl, &yt);
2304 }
2305 if(MoveFunction == F_MOVEPUSH &&
2306 Scr->OpaqueMove &&
2307 DragWindow == Tmp_win->frame) {
2308 TryToPush(Tmp_win, xl, yt);
2309 }
2310 if(MoveFunction == F_MOVEPACK ||
2311 (MoveFunction == F_MOVEPUSH &&
2312 DragWindow == Tmp_win->frame)) {
2313 TryToPack(Tmp_win, &xl, &yt);
2314 }
2315 if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) {
2316 ConstrainByBorders(Tmp_win, &xl, w, &yt, h);
2317 }
2318
2319 CurrentDragX = xl;
2320 CurrentDragY = yt;
2321 /*
2322 * sometimes getScreenOf() replies with the wrong window when moving
2323 * y to a negative number. Need to figure out why... [XXX]
2324 * It seems to be because the first XTranslateCoordinates() doesn't
2325 * translate the coordinates to be relative to XineramaRoot.
2326 * That in turn is probably because a window is visible in a ws or
2327 * vs where it shouldn't (inconsistent with its occupation).
2328 * A problem of this kind was fixed with f.adoptwindow but remains
2329 * with f.hypermove.
2330 * As a result, the window remains in the original vs/ws and
2331 * is irretrievably moved out of view. [Rhialto]
2332 */
2333 if(xl < 0 || yt < 0 || xl > Scr->rootw || yt > Scr->rooth) {
2334 int odestx, odesty;
2335 int destx, desty;
2336 Window cr;
2337 VirtualScreen *newvs;
2338
2339 XTranslateCoordinates(dpy, Tmp_win->vs->window,
2340 Scr->XineramaRoot, xl, yt, &odestx, &odesty, &cr);
2341
2342 newvs = findIfVScreenOf(odestx, odesty);
2343
2344 if(newvs && newvs->wsw && newvs->wsw->currentwspc) {
2345 XTranslateCoordinates(dpy, Scr->XineramaRoot,
2346 newvs->window, odestx, odesty,
2347 &destx, &desty, &cr);
2348 AddToWorkSpace(newvs->wsw->currentwspc->name, Tmp_win);
2349 RemoveFromWorkSpace(Tmp_win->vs->wsw->currentwspc->name, Tmp_win);
2350 xl = destx;
2351 yt = desty;
2352 }
2353 }
2354 if(DragWindow == Tmp_win->frame) {
2355 SetupWindow(Tmp_win, xl, yt,
2356 Tmp_win->frame_width, Tmp_win->frame_height, -1);
2357 }
2358 else {
2359 XMoveWindow(dpy, DragWindow, xl, yt);
2360 if(DragWindow == Tmp_win->icon->w) {
2361 Tmp_win->icon->w_x = xl;
2362 Tmp_win->icon->w_y = yt;
2363 }
2364 }
2365
2366 if(!Scr->NoRaiseMove) { /* && !Scr->OpaqueMove) opaque already did */
2367 if(DragWindow == Tmp_win->frame) {
2368 OtpRaise(Tmp_win, WinWin);
2369 }
2370 else if(Tmp_win->icon && DragWindow == Tmp_win->icon->w) {
2371 OtpRaise(Tmp_win, IconWin);
2372 }
2373 else {
2374 fprintf(stderr, "ERROR -- events.c:2815\n");
2375 }
2376 }
2377
2378 if(!Scr->OpaqueMove) {
2379 UninstallRootColormap();
2380 }
2381 else {
2382 XSync(dpy, 0);
2383 }
2384
2385 if(Scr->NumAutoRaises) {
2386 enter_flag = true;
2387 enter_win = NULL;
2388 raise_win = ((DragWindow == Tmp_win->frame && !Scr->NoRaiseMove)
2389 ? Tmp_win : NULL);
2390 }
2391
2392 /* CCC equivalent code for auto lower not needed? */
2393
2394 #if 0
2395 if(Scr->NumAutoLowers) {
2396 leave_flag = true;
2397 leave_win = NULL;
2398 lower_win = ((DragWindow == Tmp_win->frame)
2399 ? Tmp_win : NULL);
2400 }
2401 #endif
2402
2403 DragWindow = (Window) 0;
2404 ConstMove = false;
2405 }
2406
2407 if(ResizeWindow != (Window) 0) {
2408 EndResize();
2409 }
2410
2411 if(ActiveMenu != NULL && RootFunction == 0) {
2412 if(ActiveItem) {
2413 int func = ActiveItem->func;
2414 Action = ActiveItem->action;
2415 switch(func) {
2416 case F_TITLE:
2417 if(Scr->StayUpMenus) {
2418 ButtonPressed = -1;
2419 if(Scr->WarpToDefaultMenuEntry && ActiveMenu->defaultitem) {
2420 WarpCursorToDefaultEntry(ActiveMenu);
2421 }
2422 return;
2423 }
2424 break;
2425 case F_MOVE:
2426 case F_FORCEMOVE:
2427 case F_DESTROY:
2428 case F_DELETE:
2429 case F_DELETEORDESTROY:
2430 ButtonPressed = -1;
2431 break;
2432 case F_CIRCLEUP:
2433 case F_CIRCLEDOWN:
2434 case F_REFRESH:
2435 case F_WARPTOSCREEN:
2436 PopDownMenu();
2437 break;
2438 default:
2439 break;
2440 }
2441 if(func != F_PIN && func != F_MENU) {
2442 PopDownMenu();
2443 }
2444 ExecuteFunction(func, Action,
2445 ButtonWindow ? ButtonWindow->frame : None,
2446 ButtonWindow, &Event, Context, true);
2447 Context = C_NO_CONTEXT;
2448 ButtonWindow = NULL;
2449
2450 /* if we are not executing a defered command, then take down the
2451 * menu
2452 */
2453 if(ActiveMenu) {
2454 PopDownMenu();
2455 }
2456 }
2457 else if(Scr->StayUpMenus && !ActiveMenu->entered) {
2458 ButtonPressed = -1;
2459 if(Scr->WarpToDefaultMenuEntry && ActiveMenu->defaultitem) {
2460 WarpCursorToDefaultEntry(ActiveMenu);
2461 }
2462 return;
2463 }
2464 else {
2465 PopDownMenu();
2466 }
2467 }
2468
2469 mask = (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask);
2470 switch(Event.xbutton.button) {
2471 case Button1:
2472 mask &= ~Button1Mask;
2473 break;
2474 case Button2:
2475 mask &= ~Button2Mask;
2476 break;
2477 case Button3:
2478 mask &= ~Button3Mask;
2479 break;
2480 case Button4:
2481 mask &= ~Button4Mask;
2482 break;
2483 case Button5:
2484 mask &= ~Button5Mask;
2485 break;
2486 }
2487
2488 if(RootFunction != 0 ||
2489 ResizeWindow != None ||
2490 DragWindow != None) {
2491 ButtonPressed = -1;
2492 }
2493
2494 if(AlternateKeymap || AlternateContext) {
2495 ButtonPressed = -1;
2496 return;
2497 }
2498
2499 if(RootFunction == 0 &&
2500 (Event.xbutton.state & mask) == 0 &&
2501 DragWindow == None &&
2502 ResizeWindow == None) {
2503 XUngrabPointer(dpy, CurrentTime);
2504 XUngrabServer(dpy);
2505 XFlush(dpy);
2506 EventHandler[EnterNotify] = HandleEnterNotify;
2507 EventHandler[LeaveNotify] = HandleLeaveNotify;
2508 ButtonPressed = -1;
2509 if(DownIconManager) {
2510 DownIconManager->down = false;
2511 if(Scr->Highlight) {
2512 DrawIconManagerBorder(DownIconManager, false);
2513 }
2514 DownIconManager = NULL;
2515 }
2516 Cancel = false;
2517 }
2518 }
2519
2520
2521 /*
2522 * Pop up a submenu as a result of moving the mouse right on its entry.
2523 */
do_menu(MenuRoot * menu,Window w)2524 static void do_menu(MenuRoot *menu, /* menu to pop up */
2525 Window w) /* invoking window or None */
2526 {
2527 int x = Event.xbutton.x_root;
2528 int y = Event.xbutton.y_root;
2529 bool center;
2530
2531 if(!Scr->NoGrabServer) {
2532 XGrabServer(dpy);
2533 }
2534 if(w) {
2535 int h = Scr->TBInfo.width - Scr->TBInfo.border;
2536 Window child;
2537
2538 XTranslateCoordinates(dpy, w, Scr->Root, 0, h, &x, &y, &child);
2539 center = false;
2540 }
2541 else {
2542 center = true;
2543 }
2544 if(PopUpMenu(menu, x, y, center)) {
2545 UpdateMenu();
2546 }
2547 else {
2548 XBell(dpy, 0);
2549 }
2550 }
2551
2552
2553 /*
2554 * Pop up a submenu as a result of hitting the Right arrow key while on
2555 * its entry. We should try folding these two together a bit more.
2556 */
do_key_menu(MenuRoot * menu,Window w)2557 static void do_key_menu(MenuRoot *menu, /* menu to pop up */
2558 Window w) /* invoking window or None */
2559 {
2560 int x = Event.xkey.x_root;
2561 int y = Event.xkey.y_root;
2562 bool center;
2563
2564 /* I don't think this is necessary.
2565 if (!Scr->NoGrabServer) XGrabServer(dpy);
2566 */
2567 if(w) {
2568 int h = Scr->TBInfo.width - Scr->TBInfo.border;
2569 Window child;
2570
2571 XTranslateCoordinates(dpy, w, Scr->Root, 0, h, &x, &y, &child);
2572 center = false;
2573 }
2574 else {
2575 center = true;
2576 }
2577 if(PopUpMenu(menu, x, y, center)) {
2578 /*
2579 * Note: UpdateMenu() has the internal re-capture of the event
2580 * loop to handle in-menu stuff, so this won't actually return
2581 * until we somehow exit out of that [sub]menu.
2582 */
2583 UpdateMenu();
2584 }
2585 else {
2586 XBell(dpy, 0);
2587 }
2588
2589 }
2590
2591
2592 /***********************************************************************
2593 *
2594 * Procedure:
2595 * HandleButtonPress - ButtonPress event handler
2596 *
2597 ***********************************************************************
2598 */
HandleButtonPress(void)2599 void HandleButtonPress(void)
2600 {
2601 unsigned int modifier;
2602 Cursor cur;
2603 MenuRoot *mr;
2604 FuncButton *tmp = 0;
2605 int func = 0;
2606 Window w;
2607
2608
2609 /* pop down the menu, if any */
2610
2611 if(XFindContext(dpy, Event.xbutton.window, MenuContext,
2612 (XPointer *) &mr) != XCSUCCESS) {
2613 mr = NULL;
2614 }
2615 if(ActiveMenu && (! ActiveMenu->pinned) &&
2616 (Event.xbutton.subwindow != ActiveMenu->w)) {
2617 PopDownMenu();
2618 return;
2619 }
2620 if((ActiveMenu != NULL) && (RootFunction != 0) && (mr != ActiveMenu)) {
2621 PopDownMenu();
2622 }
2623
2624 XSync(dpy, 0);
2625 /* XXX - remove? */
2626
2627 /* want menus if we have info box */
2628 if(ButtonPressed != -1 && !Scr->InfoWindow.mapped) {
2629 /* we got another butt press in addition to one still held
2630 * down, we need to cancel the operation we were doing
2631 */
2632 Cancel = true;
2633 CurrentDragX = origDragX;
2634 CurrentDragY = origDragY;
2635 if(!menuFromFrameOrWindowOrTitlebar) {
2636 if(Scr->OpaqueMove && DragWindow != None) {
2637 XMoveWindow(dpy, DragWindow, origDragX, origDragY);
2638 }
2639 else {
2640 MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0);
2641 }
2642 }
2643 XUnmapWindow(dpy, Scr->SizeWindow);
2644 if(!Scr->OpaqueMove) {
2645 UninstallRootColormap();
2646 }
2647 ResizeWindow = None;
2648 DragWindow = None;
2649 cur = LeftButt;
2650 if(Event.xbutton.button == Button2) {
2651 cur = MiddleButt;
2652 }
2653 else if(Event.xbutton.button >= Button3) {
2654 cur = RightButt;
2655 }
2656
2657 XGrabPointer(dpy, Scr->Root, True,
2658 ButtonReleaseMask | ButtonPressMask,
2659 GrabModeAsync, GrabModeAsync,
2660 Scr->Root, cur, CurrentTime);
2661
2662 return;
2663 }
2664 else {
2665 ButtonPressed = Event.xbutton.button;
2666 }
2667
2668 if((ActiveMenu != NULL) && (ActiveMenu->pinned)) {
2669 if(Event.xbutton.window == ActiveMenu->w) {
2670 modifier = (Event.xbutton.state & mods_used);
2671 modifier = set_mask_ignore(modifier);
2672 if((ActiveItem && (ActiveItem->func == F_TITLE)) || (modifier == Mod1Mask)) {
2673 MoveMenu(&Event);
2674 /*ButtonPressed = -1;*/
2675 }
2676 }
2677 Context = C_ROOT;
2678 return;
2679 }
2680
2681 if(ResizeWindow != None ||
2682 DragWindow != None ||
2683 ActiveMenu != NULL) {
2684 return;
2685 }
2686
2687 /* check the title bar buttons */
2688 if(Tmp_win && Tmp_win->title_height && Tmp_win->titlebuttons) {
2689 int i;
2690 TBWindow *tbw;
2691 TitleButtonFunc *tbf;
2692 int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
2693
2694 modifier = Event.xbutton.state & mods_used;
2695 modifier = set_mask_ignore(modifier);
2696
2697 for(i = 0, tbw = Tmp_win->titlebuttons; i < nb; i++, tbw++) {
2698 if(Event.xany.window == tbw->window) {
2699 for(tbf = tbw->info->funs; tbf; tbf = tbf->next) {
2700 if(tbf->num == ButtonPressed
2701 && tbf->mods == modifier) {
2702 switch(tbf->func) {
2703 /*
2704 * Opening up a menu doesn't use the f.menu
2705 * handler, we use our do_menu(); x-ref
2706 * comments in the handler for details.
2707 */
2708 case F_MENU :
2709 Context = C_TITLE;
2710 ButtonWindow = Tmp_win;
2711 do_menu(tbf->menuroot, tbw->window);
2712 break;
2713
2714 default :
2715 ExecuteFunction(tbf->func, tbf->action,
2716 Event.xany.window, Tmp_win,
2717 &Event, C_TITLE, false);
2718 }
2719 return;
2720 }
2721 }
2722 }
2723 }
2724 }
2725
2726 Context = C_NO_CONTEXT;
2727
2728 if(Event.xany.window == Scr->InfoWindow.win) {
2729 Context = C_IDENTIFY;
2730 }
2731
2732 if(Event.xany.window == Scr->Root) {
2733 if(AlternateContext) {
2734 XUngrabPointer(dpy, CurrentTime);
2735 XUngrabKeyboard(dpy, CurrentTime);
2736 AlternateContext = false;
2737 Context = C_ALTERNATE;
2738 }
2739 else if(AlternateKeymap && Event.xbutton.subwindow) {
2740 int dx, dy;
2741 Window child;
2742
2743 w = Event.xbutton.subwindow;
2744 Tmp_win = GetTwmWindow(w);
2745 if(Tmp_win) {
2746 Event.xany.window = Tmp_win->frame;
2747 XTranslateCoordinates(dpy, Scr->Root, Tmp_win->frame,
2748 Event.xbutton.x, Event.xbutton.y, &dx, &dy, &child);
2749 Event.xbutton.x = dx;
2750 Event.xbutton.x = dy;
2751 Event.xbutton.subwindow = child;
2752 }
2753 }
2754 else {
2755 Context = C_ROOT;
2756 }
2757 }
2758 if(Tmp_win) {
2759 if(Tmp_win->iconmanagerlist && (RootFunction != 0) &&
2760 ((Event.xany.window == Tmp_win->iconmanagerlist->icon) ||
2761 (Event.xany.window == Tmp_win->iconmanagerlist->w))) {
2762 int x, y;
2763
2764 Tmp_win = Tmp_win->iconmanagerlist->iconmgr->twm_win;
2765 XTranslateCoordinates(dpy, Event.xany.window, Tmp_win->w,
2766 Event.xbutton.x, Event.xbutton.y,
2767 &x, &y, &JunkChild);
2768
2769 Event.xbutton.x = x - Tmp_win->frame_bw3D;
2770 Event.xbutton.y = y - Tmp_win->title_height - Tmp_win->frame_bw3D;
2771 Event.xany.window = Tmp_win->w;
2772 Context = C_WINDOW;
2773 }
2774 #ifdef EWMH_DESKTOP_ROOT
2775 else if(Tmp_win->ewmhWindowType == wt_Desktop) {
2776 fprintf(stderr, "HandleButtonPress: wt_Desktop -> C_ROOT\n");
2777 Context = C_ROOT;
2778 }
2779 #endif
2780 else if(Event.xany.window == Tmp_win->title_w) {
2781 if(Scr->ClickToFocus && Tmp_win->wmhints->input) {
2782 SetFocus(Tmp_win, CurrentTime);
2783 }
2784 Context = C_TITLE;
2785 }
2786 else if(Event.xany.window == Tmp_win->w) {
2787 if(Scr->ClickToFocus || Scr->RaiseOnClick) {
2788 if(Scr->ClickToFocus && Tmp_win->wmhints->input) {
2789 SetFocus(Tmp_win, CurrentTime);
2790 }
2791 if(Scr->RaiseOnClick) {
2792 OtpRaise(Tmp_win, WinWin);
2793 WMapRaise(Tmp_win);
2794 }
2795 XSync(dpy, 0);
2796 XAllowEvents(dpy, ReplayPointer, CurrentTime);
2797 XSync(dpy, 0);
2798 ButtonPressed = -1;
2799 return;
2800 }
2801 else {
2802 printf("ERROR! ERROR! ERROR! YOU SHOULD NOT BE HERE!!!\n");
2803 Context = C_WINDOW;
2804 }
2805 }
2806 else if(Tmp_win->icon && (Event.xany.window == Tmp_win->icon->w)) {
2807 Context = C_ICON;
2808 }
2809 else if(Event.xany.window == Tmp_win->frame) {
2810 Window chwin;
2811
2812 /* since we now place a button grab on the frame instead
2813 * of the window, (see GrabButtons() in add_window.c), we
2814 * need to figure out where the pointer exactly is before
2815 * assigning Context. If the pointer is on the application
2816 * window we will change the event structure to look as if
2817 * it came from the application window.
2818 */
2819 if(Event.xbutton.subwindow == Tmp_win->w) {
2820 XTranslateCoordinates(dpy, Event.xany.window, Tmp_win->w,
2821 Event.xbutton.x, Event.xbutton.y,
2822 &Event.xbutton.x, &Event.xbutton.y,
2823 &chwin);
2824 Event.xbutton.window = Tmp_win->w;
2825
2826 if(Tmp_win->iswinbox && chwin) {
2827 int x, y;
2828 XTranslateCoordinates(dpy, Tmp_win->w, chwin,
2829 Event.xbutton.x, Event.xbutton.y,
2830 &x, &y, &chwin);
2831 if(chwin && (Tmp_win = GetTwmWindow(chwin))) {
2832 Event.xany.window = chwin;
2833 Event.xbutton.x = x;
2834 Event.xbutton.y = y;
2835 }
2836 }
2837 Context = C_WINDOW;
2838 }
2839 else if(Event.xbutton.subwindow
2840 && (Event.xbutton.subwindow == Tmp_win->title_w)) {
2841 Context = C_TITLE;
2842 }
2843 else {
2844 Context = C_FRAME;
2845 }
2846 if(Scr->ClickToFocus && Tmp_win->wmhints->input) {
2847 SetFocus(Tmp_win, CurrentTime);
2848 }
2849 }
2850 else if(Tmp_win->iswspmgr ||
2851 (Tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win)) {
2852 /*Context = C_WINDOW; probably a typo */
2853 Context = C_WORKSPACE;
2854 }
2855 else if(Tmp_win->iconmanagerlist) {
2856 if((Event.xany.window == Tmp_win->iconmanagerlist->icon) ||
2857 (Event.xany.window == Tmp_win->iconmanagerlist->w)) {
2858 Tmp_win->iconmanagerlist->down = true;
2859 if(Scr->Highlight) {
2860 DrawIconManagerBorder(Tmp_win->iconmanagerlist, false);
2861 }
2862 DownIconManager = Tmp_win->iconmanagerlist;
2863 Context = C_ICONMGR;
2864 }
2865 }
2866 }
2867
2868 /* this section of code checks to see if we were in the middle of
2869 * a command executed from a menu
2870 */
2871 if(RootFunction != 0) {
2872 if(Event.xany.window == Scr->Root) {
2873 Window win;
2874 int x, y;
2875
2876 /* if the window was the Root, we don't know for sure it
2877 * it was the root. We must check to see if it happened to be
2878 * inside of a client that was getting button press events.
2879 */
2880 XTranslateCoordinates(dpy, Scr->Root, Scr->Root,
2881 Event.xbutton.x,
2882 Event.xbutton.y,
2883 &x, &y, &Event.xany.window);
2884
2885 if(Event.xany.window != 0 &&
2886 (Tmp_win = GetTwmWindow(Event.xany.window))) {
2887 if(Tmp_win->iswinbox) {
2888 XTranslateCoordinates(dpy, Scr->Root, Event.xany.window,
2889 x, y, &x, &y, &win);
2890 XTranslateCoordinates(dpy, Event.xany.window, win,
2891 x, y, &x, &y, &win);
2892 if(win != 0) {
2893 Event.xany.window = win;
2894 }
2895 }
2896 }
2897 if(Event.xany.window == 0 ||
2898 !(Tmp_win = GetTwmWindow(Event.xany.window))) {
2899 RootFunction = 0;
2900 XBell(dpy, 0);
2901 return;
2902 }
2903 XTranslateCoordinates(dpy, Scr->Root, Event.xany.window,
2904 Event.xbutton.x,
2905 Event.xbutton.y,
2906 &x, &y, &JunkChild);
2907
2908 Event.xbutton.x = x;
2909 Event.xbutton.y = y;
2910 Context = C_WINDOW;
2911 }
2912 else if(mr != NULL) {
2913 RootFunction = 0;
2914 XBell(dpy, 0);
2915 return;
2916 }
2917
2918 /* make sure we are not trying to move an identify window */
2919 if(Event.xany.window != Scr->InfoWindow.win) {
2920 /*
2921 * X-ref comment at top of file about Action; this is where
2922 * we need to use its broader lifespan.
2923 */
2924 ExecuteFunction(RootFunction, Action, Event.xany.window,
2925 Tmp_win, &Event, Context, false);
2926 }
2927
2928 RootFunction = 0;
2929 return;
2930 }
2931
2932 ButtonWindow = Tmp_win;
2933
2934 /* if we get to here, we have to execute a function or pop up a
2935 * menu
2936 */
2937 modifier = (Event.xbutton.state | AlternateKeymap) & mods_used;
2938 modifier = set_mask_ignore(modifier);
2939 if(AlternateKeymap) {
2940 XUngrabPointer(dpy, CurrentTime);
2941 XUngrabKeyboard(dpy, CurrentTime);
2942 AlternateKeymap = 0;
2943 }
2944 if((Context == C_NO_CONTEXT) || (Context == C_IDENTIFY)) {
2945 return;
2946 }
2947
2948 RootFunction = 0;
2949
2950 /* see if there already is a key defined for this context */
2951 for(tmp = Scr->FuncButtonRoot.next; tmp != NULL; tmp = tmp->next) {
2952 if((tmp->num == Event.xbutton.button) &&
2953 (tmp->cont == Context) && (tmp->mods == modifier)) {
2954 break;
2955 }
2956 }
2957 if(tmp) {
2958 func = tmp->func;
2959 switch(func) {
2960 /*
2961 * f.menu isn't invoked, it's handle magically. Other funcs
2962 * we just invoke. X-ref the f.menu handler for details.
2963 */
2964 case F_MENU :
2965 do_menu(tmp->menu, (Window) None);
2966 break;
2967
2968 default :
2969 if(func != 0) {
2970 Action = tmp->item ? tmp->item->action : NULL;
2971 #ifdef EWMH_DESKTOP_ROOT
2972 if(Context == C_ROOT && Tmp_win != NULL) {
2973 Context = C_WINDOW;
2974 fprintf(stderr, "HandleButtonPress: wt_Desktop -> C_WINDOW\n");
2975 }
2976 #endif /* EWMH */
2977 ExecuteFunction(func,
2978 Action, Event.xany.window, Tmp_win, &Event, Context, false);
2979 }
2980 }
2981 }
2982 else {
2983 if(Tmp_win == Scr->currentvs->wsw->twm_win) {
2984 WMgrHandleButtonEvent(Scr->currentvs, &Event);
2985 return;
2986 }
2987 }
2988 if(Tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) {
2989 OccupyHandleButtonEvent(&Event);
2990 }
2991 else if(func == 0 && Scr->DefaultFunction.func != 0) {
2992 if(Scr->DefaultFunction.func == F_MENU) {
2993 do_menu(Scr->DefaultFunction.menu, (Window) None);
2994 }
2995 else {
2996 Action = Scr->DefaultFunction.item ?
2997 Scr->DefaultFunction.item->action : NULL;
2998 ExecuteFunction(Scr->DefaultFunction.func, Action,
2999 Event.xany.window, Tmp_win, &Event, Context, false);
3000 }
3001 }
3002 }
3003
3004
3005 /***********************************************************************
3006 *
3007 * Procedure:
3008 * HENQueueScanner - EnterNotify event q scanner
3009 *
3010 * Looks at the queued events and determines if any matching
3011 * LeaveNotify events or EnterEvents deriving from the
3012 * termination of a grab are behind this event to allow
3013 * skipping of unnecessary processing.
3014 *
3015 ***********************************************************************
3016 */
3017
3018 typedef struct HENScanArgs {
3019 Window w; /* Window we are currently entering */
3020 Bool leaves; /* Any LeaveNotifies found for this window */
3021 Bool inferior; /* Was NotifyInferior the mode for LeaveNotify */
3022 Bool enters; /* Any EnterNotify events with NotifyUngrab */
3023 } HENScanArgs;
3024
3025 /* ARGSUSED*/
HENQueueScanner(Display * display,XEvent * ev,char * _args)3026 static Bool HENQueueScanner(Display *display, XEvent *ev, char *_args)
3027 {
3028 HENScanArgs *args = (void *)_args;
3029
3030 if(ev->type == LeaveNotify) {
3031 if(ev->xcrossing.window == args->w &&
3032 ev->xcrossing.mode == NotifyNormal) {
3033 args->leaves = True;
3034 /*
3035 * Only the last event found matters for the Inferior field.
3036 */
3037 args->inferior =
3038 (ev->xcrossing.detail == NotifyInferior);
3039 }
3040 }
3041 else if(ev->type == EnterNotify) {
3042 if(ev->xcrossing.mode == NotifyUngrab) {
3043 args->enters = True;
3044 }
3045 }
3046
3047 return (False);
3048 }
3049
3050
3051 /***********************************************************************
3052 *
3053 * Procedure:
3054 * HandleEnterNotify - EnterNotify event handler
3055 *
3056 ***********************************************************************
3057 */
3058
HandleEnterNotify(void)3059 void HandleEnterNotify(void)
3060 {
3061 MenuRoot *mr, *tmp;
3062 XEnterWindowEvent *ewp = &Event.xcrossing;
3063 HENScanArgs scanArgs;
3064 XEvent dummy;
3065 VirtualScreen *vs;
3066
3067 /*
3068 * if we aren't in the middle of menu processing
3069 */
3070 if(!ActiveMenu) {
3071 /*
3072 * We're not interested in pseudo Enter/Leave events generated
3073 * from grab initiations.
3074 */
3075 if(ewp->mode == NotifyGrab) {
3076 return;
3077 }
3078
3079 /*
3080 * Scan for Leave and Enter Notify events to see if we can avoid some
3081 * unnecessary processing.
3082 */
3083 scanArgs.w = ewp->window;
3084 scanArgs.leaves = scanArgs.enters = False;
3085 XCheckIfEvent(dpy, &dummy, HENQueueScanner, (void *) &scanArgs);
3086
3087 /*
3088 * if entering root window, restore twm default colormap so that
3089 * titlebars are legible
3090 */
3091 if(ewp->window == Scr->Root) {
3092 Window forus_ret;
3093 int focus_rev;
3094
3095 if(!scanArgs.leaves && !scanArgs.enters) {
3096 InstallColormaps(EnterNotify, &Scr->RootColormaps);
3097 }
3098 if(! Scr->FocusRoot) {
3099 return;
3100 }
3101 XGetInputFocus(dpy, &forus_ret, &focus_rev);
3102 if((forus_ret != PointerRoot) && (forus_ret != None)) {
3103 SetFocus(NULL, Event.xcrossing.time);
3104 }
3105 return;
3106 }
3107 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
3108 if(ewp->window == vs->window) {
3109 Scr->Root = vs->window;
3110 Scr->rootx = Scr->crootx + vs->x;
3111 Scr->rooty = Scr->crooty + vs->y;
3112 Scr->rootw = vs->w;
3113 Scr->rooth = vs->h;
3114 Scr->currentvs = vs;
3115 #if 0
3116 fprintf(stderr, "entering new vs : 0x%x, 0x%x, %d, %d, %d, %d\n",
3117 vs, Scr->Root, vs->x, vs->y, vs->w, vs->h);
3118 #endif
3119 return;
3120 }
3121 }
3122
3123 /* Handle RaiseDelay, if any.....
3124 */
3125 if(RaiseDelay > 0) {
3126 if(Tmp_win && Tmp_win->auto_raise &&
3127 (!Tmp_win->iconmanagerlist ||
3128 Tmp_win->iconmanagerlist->w != ewp->window)) {
3129 ColormapWindow *cwin;
3130 static struct timeval tout, timeout = {0, 12500};
3131
3132 if(XFindContext(dpy, Tmp_win->w, ColormapContext,
3133 (XPointer *)&cwin) == XCNOENT) {
3134 cwin = NULL;
3135 }
3136
3137 if((ewp->detail != NotifyInferior
3138 || Tmp_win->frame == ewp->window)
3139 && (!cwin || cwin->visibility != VisibilityUnobscured)) {
3140 int x, y, px, py, d, i;
3141 Window w;
3142
3143 XQueryPointer(dpy, Scr->Root, &w, &w, &px, &py,
3144 &d, &d, (unsigned int *)&d);
3145
3146 /* The granularity of RaiseDelay is about 25 ms.
3147 * The timeout variable is set to 12.5 ms since we
3148 * pass this way twice each time a twm window is
3149 * entered.
3150 */
3151 for(i = 25; i < RaiseDelay; i += 25) {
3152 tout = timeout;
3153 select(0, NULL, NULL, NULL, &tout);
3154 /* Did we leave this window already? */
3155 scanArgs.w = ewp->window;
3156 scanArgs.leaves = scanArgs.enters = False;
3157 XCheckIfEvent(dpy, &dummy, HENQueueScanner,
3158 (void *) &scanArgs);
3159 if(scanArgs.leaves && !scanArgs.inferior) {
3160 return;
3161 }
3162
3163 XQueryPointer(dpy, Scr->Root, &w, &w, &x, &y,
3164 &d, &d, (unsigned int *)&d);
3165
3166 /* Has the pointer moved? If so reset the loop cnt.
3167 * We want the pointer to be still for RaiseDelay
3168 * milliseconds before terminating the loop
3169 */
3170 if(x != px || y != py) {
3171 i = 0;
3172 px = x;
3173 py = y;
3174 }
3175 }
3176 }
3177 }
3178
3179 /*
3180 * Scan for Leave and Enter Notify events to see if we can avoid some
3181 * unnecessary processing.
3182 */
3183 scanArgs.w = ewp->window;
3184 scanArgs.leaves = scanArgs.enters = False;
3185 XCheckIfEvent(dpy, &dummy, HENQueueScanner, (void *) &scanArgs);
3186
3187 /*
3188 * if entering root window, restore twm default colormap so that
3189 * titlebars are legible
3190 */
3191 if(ewp->window == Scr->Root) {
3192 if(!scanArgs.leaves && !scanArgs.enters) {
3193 InstallColormaps(EnterNotify, &Scr->RootColormaps);
3194 }
3195 return;
3196 }
3197 }
3198 /* End of RaiseDelay modification. */
3199
3200 /*
3201 * if we have an event for a specific one of our windows
3202 */
3203 if(Tmp_win) {
3204 /*
3205 * If currently in PointerRoot mode (indicated by FocusRoot), then
3206 * focus on this window
3207 */
3208 if(Scr->FocusRoot && (!scanArgs.leaves || scanArgs.inferior)) {
3209 bool accinput;
3210
3211 if(Scr->ShrinkIconTitles &&
3212 Tmp_win->icon &&
3213 ewp->window == Tmp_win->icon->w &&
3214 ewp->detail != NotifyInferior) {
3215 if(Scr->AutoRaiseIcons) {
3216 OtpRaise(Tmp_win, IconWin);
3217 }
3218 ExpandIconTitle(Tmp_win);
3219 return;
3220 }
3221
3222 if(Tmp_win->iconmanagerlist) {
3223 CurrentIconManagerEntry(Tmp_win->iconmanagerlist);
3224 }
3225
3226 accinput = Tmp_win->mapped && Tmp_win->wmhints->input;
3227 if(Tmp_win->iconmanagerlist &&
3228 ewp->window == Tmp_win->iconmanagerlist->w &&
3229 !accinput &&
3230 Tmp_win->iconmanagerlist->iconmgr &&
3231 Tmp_win->iconmanagerlist->iconmgr->twm_win) {
3232 SetFocus(Tmp_win->iconmanagerlist->iconmgr->twm_win,
3233 CurrentTime);
3234 return;
3235 }
3236
3237 if(Tmp_win->mapped) {
3238 /*
3239 * unhighlight old focus window
3240 */
3241
3242 /*
3243 * If entering the frame or the icon manager, then do
3244 * "window activation things":
3245 *
3246 * 1. <highlighting is not done here any more>
3247 * 2. install frame colormap
3248 * 3. <frame and highlight border not set here>
3249 * 4. focus on client window to forward typing
3250 * 4a. same as 4 but for icon mgr w/with NoTitleFocus
3251 * 5. send WM_TAKE_FOCUS if requested
3252 */
3253 if(Scr->BorderCursors && ewp->window == Tmp_win->frame) {
3254 SetBorderCursor(Tmp_win, ewp->x, ewp->y);
3255 }
3256 if(ewp->window == Tmp_win->frame ||
3257 (Scr->IconManagerFocus &&
3258 Tmp_win->iconmanagerlist &&
3259 ewp->window == Tmp_win->iconmanagerlist->w)) {
3260
3261 if(!scanArgs.leaves && !scanArgs.enters) {
3262 InstallColormaps(EnterNotify, /* 2 */
3263 &Scr->RootColormaps);
3264 }
3265
3266 /*
3267 * Event is in the frame or the icon mgr:
3268 *
3269 * "4" -- TitleFocus is set: windows should get
3270 * focus as long as they accept input.
3271 *
3272 * "4a" - If TitleFocus is not set, windows should get
3273 * the focus if the event was in the icon mgr
3274 * (as long as they accept input).
3275 *
3276 */
3277
3278 /* If the window takes input... */
3279 if(Tmp_win->wmhints->input) {
3280
3281 /* if 4 or 4a, focus on the window */
3282 if(Scr->TitleFocus ||
3283 (Tmp_win->iconmanagerlist &&
3284 (Tmp_win->iconmanagerlist->w == ewp->window))) {
3285 SetFocus(Tmp_win, ewp->time);
3286 }
3287 }
3288
3289 if(Scr->TitleFocus &&
3290 (Tmp_win->protocols & DoesWmTakeFocus)) { /* 5 */
3291
3292 /* for both locally or globally active */
3293 SendTakeFocusMessage(Tmp_win, ewp->time);
3294 }
3295 else if(!Scr->TitleFocus
3296 && Tmp_win->wmhints->input
3297 && Event.xcrossing.focus) {
3298 SynthesiseFocusIn(Tmp_win->w);
3299 }
3300
3301 }
3302 else if(ewp->window == Tmp_win->w) {
3303 /*
3304 * If we are entering the application window, install
3305 * its colormap(s).
3306 */
3307 if(Scr->BorderCursors) {
3308 SetBorderCursor(Tmp_win, -1000, -1000);
3309 }
3310 if(!scanArgs.leaves || scanArgs.inferior) {
3311 InstallWindowColormaps(EnterNotify, Tmp_win);
3312 }
3313
3314 if(Event.xcrossing.focus) {
3315 SynthesiseFocusIn(Tmp_win->w);
3316 }
3317
3318 /* must deal with WM_TAKE_FOCUS clients now, if
3319 we're not in TitleFocus mode */
3320
3321 if(!(Scr->TitleFocus) &&
3322 (Tmp_win->protocols & DoesWmTakeFocus)) {
3323
3324 /* locally active clients need help from WM
3325 to get the input focus */
3326
3327 if(Tmp_win->wmhints->input) {
3328 SetFocus(Tmp_win, ewp->time);
3329 }
3330
3331 /* for both locally & globally active clnts */
3332
3333 SendTakeFocusMessage(Tmp_win, ewp->time);
3334 }
3335 }
3336 } /* end if Tmp_win->mapped */
3337 if(ewp->window == Tmp_win->wmhints->icon_window &&
3338 (!scanArgs.leaves || scanArgs.inferior)) {
3339 InstallWindowColormaps(EnterNotify, Tmp_win);
3340 }
3341 } /* end if FocusRoot */
3342 else if(Scr->BorderCursors && (ewp->window == Tmp_win->w)) {
3343 SetBorderCursor(Tmp_win, -1000, -1000);
3344 }
3345 /*
3346 * If this window is to be autoraised, mark it so
3347 */
3348 if(Tmp_win->auto_raise) {
3349 enter_win = Tmp_win;
3350 if(enter_flag == false) {
3351 AutoRaiseWindow(Tmp_win);
3352 }
3353 }
3354 else if(enter_flag && raise_win == Tmp_win) {
3355 enter_win = Tmp_win;
3356 }
3357 /*
3358 * set ring leader
3359 */
3360 if(Tmp_win->ring.next && (!enter_flag || raise_win == enter_win)) {
3361 Scr->RingLeader = Tmp_win;
3362 }
3363 XSync(dpy, 0);
3364 return;
3365 } /* end if Tmp_win */
3366 } /* end if !ActiveMenu */
3367
3368 /*
3369 * Find the menu that we are dealing with now; punt if unknown
3370 */
3371 if(XFindContext(dpy, ewp->window, MenuContext, (XPointer *)&mr) != XCSUCCESS) {
3372 return;
3373 }
3374
3375 if(! ActiveMenu && mr->pinned && (RootFunction == 0)) {
3376 PopUpMenu(mr, 0, 0, false);
3377 Context = C_ROOT;
3378 UpdateMenu();
3379 return;
3380 }
3381 mr->entered = true;
3382 if(RootFunction == 0) {
3383 for(tmp = ActiveMenu; tmp; tmp = tmp->prev) {
3384 if(tmp == mr) {
3385 break;
3386 }
3387 }
3388 if(! tmp) {
3389 return;
3390 }
3391
3392 for(tmp = ActiveMenu; tmp != mr; tmp = tmp->prev) {
3393 if(tmp->pinned) {
3394 break;
3395 }
3396 HideMenu(tmp);
3397 MenuDepth--;
3398 }
3399 UninstallRootColormap();
3400
3401 if(ActiveItem) {
3402 ActiveItem->state = 0;
3403 PaintEntry(ActiveMenu, ActiveItem, false);
3404 }
3405 ActiveItem = NULL;
3406 ActiveMenu = mr;
3407 if(1/*Scr->StayUpMenus*/) {
3408 int i, x, y, x_root, y_root, entry;
3409 MenuItem *mi;
3410
3411 XQueryPointer(dpy, ActiveMenu->w, &JunkRoot, &JunkChild, &x_root, &y_root,
3412 &x, &y, &JunkMask);
3413 if((x > 0) && (y > 0) && (x < ActiveMenu->width) && (y < ActiveMenu->height)) {
3414 entry = y / Scr->EntryHeight;
3415 for(i = 0, mi = ActiveMenu->first; mi != NULL; i++, mi = mi->next) {
3416 if(i == entry) {
3417 break;
3418 }
3419 }
3420 if(mi) {
3421 ActiveItem = mi;
3422 ActiveItem->state = 1;
3423 PaintEntry(ActiveMenu, ActiveItem, false);
3424 }
3425 }
3426 }
3427 if(ActiveMenu->pinned) {
3428 XUngrabPointer(dpy, CurrentTime);
3429 }
3430 }
3431 return;
3432 }
3433
3434
3435 /***********************************************************************
3436 *
3437 * Procedure:
3438 * HLNQueueScanner - LeaveNotify event q scanner
3439 *
3440 * Looks at the queued events and determines if any
3441 * EnterNotify events are behind this event to allow
3442 * skipping of unnecessary processing.
3443 *
3444 ***********************************************************************
3445 */
3446
3447 typedef struct HLNScanArgs {
3448 Window w; /* The window getting the LeaveNotify */
3449 Bool enters; /* Any EnterNotify event at all */
3450 Bool matches; /* Any matching EnterNotify events */
3451 } HLNScanArgs;
3452
3453 /* ARGSUSED*/
HLNQueueScanner(Display * display,XEvent * ev,char * _args)3454 static Bool HLNQueueScanner(Display *display, XEvent *ev, char *_args)
3455 {
3456 HLNScanArgs *args = (void *)_args;
3457
3458 if(ev->type == EnterNotify && ev->xcrossing.mode != NotifyGrab) {
3459 args->enters = True;
3460 if(ev->xcrossing.window == args->w) {
3461 args->matches = True;
3462 }
3463 }
3464
3465 return (False);
3466 }
3467
3468
3469 /***********************************************************************
3470 *
3471 * Procedure:
3472 * HandleLeaveNotify - LeaveNotify event handler
3473 *
3474 ***********************************************************************
3475 */
3476
HandleLeaveNotify(void)3477 void HandleLeaveNotify(void)
3478 {
3479 bool inicon;
3480
3481 if(ActiveMenu && ActiveMenu->pinned
3482 && (Event.xcrossing.window == ActiveMenu->w)) {
3483 PopDownMenu();
3484 }
3485
3486 if(Tmp_win == NULL) {
3487 /* No window to be Leave'ing, so nothing much to do... */
3488 return;
3489 }
3490
3491 /*
3492 * We're not interested in pseudo Enter/Leave events generated
3493 * from grab initiations and terminations.
3494 */
3495 if(Event.xcrossing.mode != NotifyNormal) {
3496 return;
3497 }
3498
3499 if(Scr->ShrinkIconTitles &&
3500 Tmp_win->icon &&
3501 Event.xcrossing.window == Tmp_win->icon->w &&
3502 Event.xcrossing.detail != NotifyInferior) {
3503 ShrinkIconTitle(Tmp_win);
3504 return;
3505 }
3506
3507 // Are we Leave'ing the icon manager entry for the Tmp_win in
3508 // question, or some other part of the window itself?
3509 inicon = (Tmp_win->iconmanagerlist &&
3510 Tmp_win->iconmanagerlist->w == Event.xcrossing.window);
3511
3512 if(Scr->RingLeader && Scr->RingLeader == Tmp_win &&
3513 (Event.xcrossing.detail != NotifyInferior &&
3514 Event.xcrossing.window != Tmp_win->w)) {
3515 #ifdef DEBUG
3516 fprintf(stderr,
3517 "HandleLeaveNotify: Event.xcrossing.window %x != Tmp_win->w %x\n",
3518 Event.xcrossing.window, Tmp_win->w);
3519 #endif
3520 if(!inicon) {
3521 if(Event.xcrossing.window != Tmp_win->frame /*was: Tmp_win->mapped*/) {
3522 Tmp_win->ring.cursor_valid = false;
3523 #ifdef DEBUG
3524 fprintf(stderr, "HandleLeaveNotify: cursor_valid = false\n");
3525 #endif
3526 }
3527 else { /* Event.xcrossing.window == Tmp_win->frame */
3528 Tmp_win->ring.cursor_valid = true;
3529 Tmp_win->ring.curs_x = (Event.xcrossing.x_root -
3530 Tmp_win->frame_x);
3531 Tmp_win->ring.curs_y = (Event.xcrossing.y_root -
3532 Tmp_win->frame_y);
3533 #ifdef DEBUG
3534 fprintf(stderr,
3535 "HandleLeaveNotify: cursor_valid = true; x = %d (%d-%d), y = %d (%d-%d)\n",
3536 Tmp_win->ring.curs_x, Event.xcrossing.x_root, Tmp_win->frame_x,
3537 Tmp_win->ring.curs_y, Event.xcrossing.y_root, Tmp_win->frame_y);
3538 #endif
3539 }
3540 }
3541 Scr->RingLeader = NULL;
3542 }
3543
3544
3545 /*
3546 * Are we moving focus based on the leave? There are 2 steps to
3547 * this:
3548 * - Scr->FocusRoot is our "are we automatically changing focus
3549 * based on the leave" flag. This gets unset when ClickToFocus
3550 * is config'd or a window is f.focus'd.
3551 * - Then we check the detail for the focus leaving. We're only
3552 * getting here for normal entry/exits. Most cases outside of
3553 * the icon manager peek ahead in the event queue for any
3554 * Enter's, and don't do anything if there are; if there were,
3555 * we'd be moving the focus when we get them, so no point doing
3556 * it twice. So the remainder here assumes there aren't any
3557 * Enters waiting.
3558 *
3559 * See
3560 * <https://www.x.org/releases/X11R7.7/doc/libX11/libX11/libX11.html#Normal_EntryExit_Events>
3561 * for details of the cases. So, when do we want to un-set the
3562 * focus? Let's look at each doc'd value...
3563 * - NotifyAncestor means we're leaving a subwindow for its
3564 * parent. That means the root, so, yep, we want to yield
3565 * focus up to it.
3566 * - NotifyNonLinear means we're leaving a window for something
3567 * that isn't a parent or child. So we should probably yield
3568 * focus in this case too.
3569 * - NotifyInferior means we're leaving a window for a
3570 * subwindow of it. From the WM perspective, that means
3571 * we're leaving focus where it was.
3572 * - NotifyVirtual means we're in the middle of an ascending
3573 * sequence. Nothing to do; the Ancestor handling already
3574 * did the job.
3575 * - NotifyNonLinearVirtual is another "in the middle" case, so
3576 * we skip handling there too; the endpoints will do
3577 * whatever's necessary.
3578 */
3579 if(Scr->FocusRoot
3580 && Event.xcrossing.detail != NotifyInferior
3581 && Event.xcrossing.detail != NotifyVirtual
3582 && Event.xcrossing.detail != NotifyNonlinearVirtual
3583 ) {
3584 HLNScanArgs scanArgs;
3585 XEvent dummy;
3586
3587 /*
3588 * Scan for EnterNotify events to see if we can avoid some
3589 * unnecessary processing.
3590 */
3591 scanArgs.w = Event.xcrossing.window;
3592 scanArgs.enters = scanArgs.matches = False;
3593 XCheckIfEvent(dpy, &dummy, HLNQueueScanner,
3594 (char *) &scanArgs);
3595
3596 if((inicon && Scr->IconManagerFocus)
3597 || (Event.xcrossing.window == Tmp_win->frame
3598 && !scanArgs.matches)
3599 ) {
3600 // Defocusing window because we moved out of its entry in an
3601 // icon manager, or because we moved out of its frame.
3602
3603 // Nothing to do if we were in the icon manager, and the
3604 // window's either unmapped or doesn't accept input. XXX Is
3605 // the inicon flag needed here? If it's not mapped, we
3606 // presumably couldn't have gotten a Leave on its frame
3607 // anyway, and if it's not accepting input, we probably don't
3608 // need to focus out anyway? Left conditional because this
3609 // matches historical behavior prior to some rework here, but
3610 // revisit.
3611 if(inicon && (!Tmp_win->mapped || !Tmp_win->wmhints->input)) {
3612 return;
3613 }
3614
3615 // Shift away focus
3616 if(Scr->TitleFocus || Tmp_win->protocols & DoesWmTakeFocus) {
3617 SetFocus(NULL, Event.xcrossing.time);
3618 }
3619
3620 // If we're in the icon manager, we need to take a FocusOut
3621 // event for the window, since it wouldn't have gotten one.
3622 // If we're in the frame, we fake one anyway as historical
3623 // code says "pretend there was a focus out as sometimes we
3624 // don't get one".
3625 if(Event.xcrossing.focus) {
3626 SynthesiseFocusOut(Tmp_win->w);
3627 }
3628 }
3629 else if(Event.xcrossing.window == Tmp_win->w && !scanArgs.enters) {
3630 // Flipping colormaps around because we moved out of the
3631 // window.
3632 InstallColormaps(LeaveNotify, &Scr->RootColormaps);
3633 }
3634 }
3635
3636 /* Autolower modification. */
3637 if(Tmp_win->auto_lower) {
3638 leave_win = Tmp_win;
3639 if(leave_flag == false) {
3640 AutoLowerWindow(Tmp_win);
3641 }
3642 }
3643 else if(leave_flag && lower_win == Tmp_win) {
3644 leave_win = Tmp_win;
3645 }
3646
3647 XSync(dpy, 0);
3648 return;
3649 }
3650
3651
3652 /***********************************************************************
3653 *
3654 * Procedure:
3655 * HandleConfigureRequest - ConfigureRequest event handler
3656 *
3657 ***********************************************************************
3658 */
3659
HandleConfigureRequest(void)3660 void HandleConfigureRequest(void)
3661 {
3662 XWindowChanges xwc;
3663 unsigned long xwcm;
3664 int x, y, width, height, bw;
3665 int gravx, gravy;
3666 XConfigureRequestEvent *cre = &Event.xconfigurerequest;
3667 bool sendEvent;
3668
3669 #ifdef DEBUG_EVENTS
3670 fprintf(stderr, "ConfigureRequest\n");
3671 if(cre->value_mask & CWX) {
3672 fprintf(stderr, " x = %d\n", cre->x);
3673 }
3674 if(cre->value_mask & CWY) {
3675 fprintf(stderr, " y = %d\n", cre->y);
3676 }
3677 if(cre->value_mask & CWWidth) {
3678 fprintf(stderr, " width = %d\n", cre->width);
3679 }
3680 if(cre->value_mask & CWHeight) {
3681 fprintf(stderr, " height = %d\n", cre->height);
3682 }
3683 if(cre->value_mask & CWSibling) {
3684 fprintf(stderr, " above = 0x%x\n", (unsigned)cre->above);
3685 }
3686 if(cre->value_mask & CWStackMode) {
3687 fprintf(stderr, " stack = %d\n", cre->detail);
3688 }
3689 #endif
3690
3691 /*
3692 * Event.xany.window is Event.xconfigurerequest.parent, so Tmp_win will
3693 * be wrong
3694 */
3695 Event.xany.window = cre->window; /* mash parent field */
3696 Tmp_win = GetTwmWindow(cre->window);
3697
3698 /*
3699 * According to the July 27, 1988 ICCCM draft, we should ignore size and
3700 * position fields in the WM_NORMAL_HINTS property when we map a window.
3701 * Instead, we'll read the current geometry. Therefore, we should respond
3702 * to configuration requests for windows which have never been mapped.
3703 */
3704 if(!Tmp_win || (Tmp_win->icon && (Tmp_win->icon->w == cre->window))) {
3705 xwcm = cre->value_mask &
3706 (CWX | CWY | CWWidth | CWHeight | CWBorderWidth);
3707 xwc.x = cre->x;
3708 xwc.y = cre->y;
3709 xwc.width = cre->width;
3710 xwc.height = cre->height;
3711 xwc.border_width = cre->border_width;
3712 XConfigureWindow(dpy, Event.xany.window, xwcm, &xwc);
3713 return;
3714 }
3715
3716 sendEvent = false;
3717 if((cre->value_mask & CWStackMode) && Tmp_win->stackmode) {
3718 TwmWindow *otherwin;
3719
3720 if(cre->value_mask & CWSibling) {
3721 otherwin = GetTwmWindow(cre->above);
3722 if(otherwin) {
3723 OtpForcePlacement(Tmp_win, cre->detail, otherwin);
3724 }
3725 else {
3726 fprintf(stderr, "XConfigureRequest: unkown otherwin\n");
3727 }
3728 }
3729 else {
3730 switch(cre->detail) {
3731 case TopIf:
3732 case Above:
3733 OtpRaise(Tmp_win, WinWin);
3734 break;
3735 case BottomIf:
3736 case Below:
3737 OtpLower(Tmp_win, WinWin);
3738 break;
3739 case Opposite:
3740 OtpRaiseLower(Tmp_win, WinWin);
3741 break;
3742 default:
3743 ;
3744 }
3745 }
3746 sendEvent = true;
3747 }
3748
3749
3750 /* Don't modify frame_XXX fields before calling SetupWindow! */
3751 x = Tmp_win->frame_x;
3752 y = Tmp_win->frame_y;
3753 width = Tmp_win->frame_width;
3754 height = Tmp_win->frame_height;
3755 bw = Tmp_win->frame_bw;
3756
3757 /*
3758 * Section 4.1.5 of the ICCCM states that the (x,y) coordinates in the
3759 * configure request are for the upper-left outer corner of the window.
3760 * This means that we need to adjust for the additional title height as
3761 * well as for any border width changes that we decide to allow. The
3762 * current window gravity is to be used in computing the adjustments, just
3763 * as when initially locating the window. Note that if we do decide to
3764 * allow border width changes, we will need to send the synthetic
3765 * ConfigureNotify event.
3766 */
3767 GetGravityOffsets(Tmp_win, &gravx, &gravy);
3768
3769 if(cre->value_mask & CWBorderWidth) {
3770 int bwdelta = cre->border_width - Tmp_win->old_bw; /* posit growth */
3771 if(bwdelta && Scr->ClientBorderWidth) { /* if change allowed */
3772 x += gravx * bwdelta; /* change default values only */
3773 y += gravy * bwdelta; /* ditto */
3774 bw = cre->border_width;
3775 if(Tmp_win->title_height) {
3776 height += bwdelta;
3777 }
3778 x += (gravx < 0) ? bwdelta : -bwdelta;
3779 y += (gravy < 0) ? bwdelta : -bwdelta;
3780 }
3781 Tmp_win->old_bw = cre->border_width; /* for restoring */
3782 }
3783
3784 if((cre->value_mask & CWX)) { /* override even if border change */
3785 x = cre->x - bw;
3786 x -= ((gravx < 0) ? 0 : Tmp_win->frame_bw3D);
3787 }
3788 if((cre->value_mask & CWY)) {
3789 y = cre->y - ((gravy < 0) ? 0 : Tmp_win->title_height) - bw;
3790 y -= ((gravy < 0) ? 0 : Tmp_win->frame_bw3D);
3791 }
3792
3793 if(cre->value_mask & CWWidth) {
3794 width = cre->width + 2 * Tmp_win->frame_bw3D;
3795 }
3796 if(cre->value_mask & CWHeight) {
3797 height = cre->height + Tmp_win->title_height + 2 * Tmp_win->frame_bw3D;
3798 }
3799
3800 if(width != Tmp_win->frame_width || height != Tmp_win->frame_height) {
3801 unzoom(Tmp_win);
3802 }
3803
3804 /* Workaround for Java 1.4 bug that freezes the application whenever
3805 * a new window is displayed. (When UsePPosition is on and either
3806 * UseThreeDBorders or BorderWidth 0 is set.)
3807 */
3808 if(!bw) {
3809 sendEvent = true;
3810 }
3811
3812 /*
3813 * SetupWindow (x,y) are the location of the upper-left outer corner and
3814 * are passed directly to XMoveResizeWindow (frame). The (width,height)
3815 * are the inner size of the frame. The inner width is the same as the
3816 * requested client window width; the inner height is the same as the
3817 * requested client window height plus any title bar slop.
3818 */
3819 #ifdef DEBUG_EVENTS
3820 fprintf(stderr, "SetupFrame(x=%d, y=%d, width=%d, height=%d, bw=%d)\n",
3821 x, y, width, height, bw);
3822 #endif
3823 SetupFrame(Tmp_win, x, y, width, height, bw, sendEvent);
3824 }
3825
3826
3827 /***********************************************************************
3828 *
3829 * Procedure:
3830 * HandleShapeNotify - shape notification event handler
3831 *
3832 ***********************************************************************
3833 */
3834 void
HandleShapeNotify(void)3835 HandleShapeNotify(void)
3836 {
3837 XShapeEvent *sev = (XShapeEvent *) &Event;
3838
3839 if(Tmp_win == NULL) {
3840 return;
3841 }
3842 if(sev->kind != ShapeBounding) {
3843 return;
3844 }
3845 if(!Tmp_win->wShaped && sev->shaped) {
3846 XShapeCombineMask(dpy, Tmp_win->frame, ShapeClip, 0, 0, None,
3847 ShapeSet);
3848 }
3849 Tmp_win->wShaped = sev->shaped;
3850 SetFrameShape(Tmp_win);
3851 }
3852
3853 /***********************************************************************
3854 *
3855 * Procedure:
3856 * HandleSelectionClear - selection lost event handler
3857 *
3858 ***********************************************************************
3859 */
3860 #ifdef EWMH
3861 void
HandleSelectionClear(void)3862 HandleSelectionClear(void)
3863 {
3864 XSelectionClearEvent *sev = (XSelectionClearEvent *) &Event;
3865
3866 if(sev->window == Scr->icccm_Window) {
3867 EwmhSelectionClear(sev);
3868 }
3869 }
3870 #endif
3871
3872
3873 /***********************************************************************
3874 *
3875 * Procedure:
3876 * HandleUnknown - unknown event handler
3877 *
3878 ***********************************************************************
3879 */
3880
HandleUnknown(void)3881 void HandleUnknown(void)
3882 {
3883 #ifdef DEBUG_EVENTS
3884 fprintf(stderr, "HandleUnknown: Event.type = %d\n", Event.type);
3885 #endif
3886 }
3887
3888
flush_expose(Window w)3889 static void flush_expose(Window w)
3890 {
3891 XEvent dummy;
3892
3893 while(XCheckTypedWindowEvent(dpy, w, Expose, &dummy)) {
3894 /* nada */;
3895 }
3896 }
3897
3898
3899 /* Util func used a few times above */
3900 static void
SendTakeFocusMessage(TwmWindow * tmp,Time timestamp)3901 SendTakeFocusMessage(TwmWindow *tmp, Time timestamp)
3902 {
3903 send_clientmessage(tmp->w, XA_WM_TAKE_FOCUS, timestamp);
3904 }
3905