menus.c revision 6d8e82c3
1/*****************************************************************************/
2/*
3
4Copyright 1989, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25
26*/
27/**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
28/**                          Salt Lake City, Utah                           **/
29/**                        Cambridge, Massachusetts                         **/
30/**                                                                         **/
31/**                           All Rights Reserved                           **/
32/**                                                                         **/
33/**    Permission to use, copy, modify, and distribute this software and    **/
34/**    its documentation  for  any  purpose  and  without  fee is hereby    **/
35/**    granted, provided that the above copyright notice appear  in  all    **/
36/**    copies and that both  that  copyright  notice  and  this  permis-    **/
37/**    sion  notice appear in supporting  documentation,  and  that  the    **/
38/**    name of Evans & Sutherland not be used in advertising    **/
39/**    in publicity pertaining to distribution of the  software  without    **/
40/**    specific, written prior permission.                                  **/
41/**                                                                         **/
42/**    EVANS & SUTHERLAND DISCLAIMs ALL WARRANTIES WITH REGARD    **/
43/**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
44/**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND    **/
45/**    BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
46/**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
47/**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
48/**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
49/**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
50/*****************************************************************************/
51
52/***********************************************************************
53 *
54 * twm menu code
55 *
56 * 17-Nov-87 Thomas E. LaStrange                File created
57 *
58 ***********************************************************************/
59
60#ifdef HAVE_CONFIG_H
61#include "config.h"
62#endif
63
64#include <stdio.h>
65#include <X11/Xos.h>
66#include "twm.h"
67#include "gc.h"
68#include "menus.h"
69#include "resize.h"
70#include "events.h"
71#include "util.h"
72#include "parse.h"
73#include "screen.h"
74#include "menus.h"
75#include "iconmgr.h"
76#include "add_window.h"
77#include "icons.h"
78#include "session.h"
79#include <X11/Xmu/CharSet.h>
80#include <X11/extensions/sync.h>
81#include <X11/SM/SMlib.h>
82
83int RootFunction = 0;
84MenuRoot *ActiveMenu = NULL;            /**< the active menu */
85MenuItem *ActiveItem = NULL;            /**< the active menu item */
86int MoveFunction;                       /**< either F_MOVE or F_FORCEMOVE */
87int WindowMoved = FALSE;
88int menuFromFrameOrWindowOrTitlebar = FALSE;
89
90int ConstMove = FALSE;          /**< constrained move variables */
91int ConstMoveDir;
92int ConstMoveX;
93int ConstMoveY;
94int ConstMoveXL;
95int ConstMoveXR;
96int ConstMoveYT;
97int ConstMoveYB;
98
99/* Globals used to keep track of whether the mouse has moved during
100   a resize function. */
101int ResizeOrigX;
102int ResizeOrigY;
103
104int MenuDepth = 0;              /**< number of menus up */
105static struct {
106    int x;
107    int y;
108} MenuOrigins[MAXMENUDEPTH];
109static Cursor LastCursor;
110
111static Bool belongs_to_twm_window(TwmWindow *t, Window w);
112static void Identify(TwmWindow *t);
113static void send_clientmessage(Window w, Atom a, Time timestamp);
114static void BumpWindowColormap(TwmWindow *tmp, int inc);
115static int DeferExecution(int context, int func, Cursor cursor);
116static Bool NeedToDefer(MenuRoot *root);
117static void DestroyMenu(MenuRoot *menu);
118static void MakeMenu(MenuRoot *mr);
119static void Execute(const char *s);
120static void HideIconManager(void);
121static void WarpAlongRing(XButtonEvent *ev, Bool forward);
122static int WarpThere(TwmWindow *t);
123static void WarpToWindow(TwmWindow *t);
124
125#define SHADOWWIDTH 5           /* in pixels */
126
127/**
128 * initialize menu roots
129 */
130void
131InitMenus(void)
132{
133    int i, j, k;
134
135    for (i = 0; i < MAX_BUTTONS + 1; i++)
136        for (j = 0; j < NUM_CONTEXTS; j++)
137            for (k = 0; k < MOD_SIZE; k++) {
138                Scr->Mouse[i][j][k].func = 0;
139                Scr->Mouse[i][j][k].item = NULL;
140            }
141
142    Scr->DefaultFunction.func = 0;
143    Scr->WindowFunction.func = 0;
144}
145
146void
147InitMenusFirst(void)
148{
149    FuncKey *key;
150
151    for (key = Scr->FuncKeyRoot.next; key != NULL;) {
152        FuncKey *tmp = key;
153
154        free(key->name);
155        key = key->next;
156        free(tmp);
157    }
158    Scr->FuncKeyRoot.next = NULL;
159}
160
161/**
162 * add a function key to the list
163 *
164 *  \param name     the name of the key
165 *  \param cont     the context to look for the key press in
166 *  \param mods2    modifier keys that need to be pressed
167 *  \param func     the function to perform
168 *  \param win_name the window name (if any)
169 *  \param action   the action string associated with the function (if any)
170 */
171Bool
172AddFuncKey(char *name, int cont, int mods2, int func, char *win_name,
173           char *action)
174{
175    FuncKey *tmp;
176    KeySym keysym;
177    KeyCode keycode;
178
179    /*
180     * Don't let a 0 keycode go through, since that means AnyKey to the
181     * XGrabKey call in GrabKeys().
182     */
183    if ((keysym = XStringToKeysym(name)) == NoSymbol ||
184        (keycode = XKeysymToKeycode(dpy, keysym)) == 0) {
185        return False;
186    }
187
188    /* see if there already is a key defined for this context */
189    for (tmp = Scr->FuncKeyRoot.next; tmp != NULL; tmp = tmp->next) {
190        if (tmp->keysym == keysym && tmp->cont == cont && tmp->mods == mods2)
191            break;
192    }
193
194    if (tmp == NULL) {
195        tmp = (FuncKey *) malloc(sizeof(FuncKey));
196        tmp->next = Scr->FuncKeyRoot.next;
197        Scr->FuncKeyRoot.next = tmp;
198    }
199
200    tmp->name = name;
201    tmp->keysym = keysym;
202    tmp->keycode = keycode;
203    tmp->cont = cont;
204    tmp->mods = mods2;
205    tmp->func = func;
206    tmp->win_name = win_name;
207    tmp->action = action;
208
209    return True;
210}
211
212int
213CreateTitleButton(const char *name, int func, const char *action,
214                  MenuRoot *menuroot, Bool rightside, Bool append)
215{
216    TitleButton *tb = (TitleButton *) malloc(sizeof(TitleButton));
217
218    if (!tb) {
219        twmWarning("unable to allocate %lu bytes for title button",
220                   (unsigned long) sizeof(TitleButton));
221        return 0;
222    }
223
224    tb->next = NULL;
225    tb->name = name;            /* note that we are not copying */
226    tb->bitmap = None;          /* WARNING, values not set yet */
227    tb->width = 0;              /* see InitTitlebarButtons */
228    tb->height = 0;             /* ditto */
229    tb->func = func;
230    tb->action = action;
231    tb->menuroot = menuroot;
232    tb->rightside = rightside;
233    if (rightside) {
234        Scr->TBInfo.nright++;
235    }
236    else {
237        Scr->TBInfo.nleft++;
238    }
239
240    /*
241     * Cases for list:
242     *
243     *     1.  empty list, prepend left       put at head of list
244     *     2.  append left, prepend right     put in between left and right
245     *     3.  append right                   put at tail of list
246     *
247     * Do not refer to widths and heights yet since buttons not created
248     * (since fonts not loaded and heights not known).
249     */
250    if ((!Scr->TBInfo.head) || ((!append) && (!rightside))) {   /* 1 */
251        tb->next = Scr->TBInfo.head;
252        Scr->TBInfo.head = tb;
253    }
254    else if (append && rightside) {     /* 3 */
255        TitleButton *t;
256
257        /* SUPPRESS 530 */
258        for (t = Scr->TBInfo.head; t->next; t = t->next);
259        t->next = tb;
260        tb->next = NULL;
261    }
262    else {                      /* 2 */
263        TitleButton *t, *prev = NULL;
264
265        for (t = Scr->TBInfo.head; t && !t->rightside; t = t->next) {
266            prev = t;
267        }
268        if (prev) {
269            tb->next = prev->next;
270            prev->next = tb;
271        }
272        else {
273            tb->next = Scr->TBInfo.head;
274            Scr->TBInfo.head = tb;
275        }
276    }
277
278    return 1;
279}
280
281/**
282 * Do all the necessary stuff to load in a titlebar button.  If we can't find
283 * the button, then put in a question; if we can't find the question mark,
284 * something is wrong and we are probably going to be in trouble later on.
285 */
286void
287InitTitlebarButtons(void)
288{
289    TitleButton *tb;
290    int h;
291
292    /*
293     * initialize dimensions
294     */
295    Scr->TBInfo.width = (Scr->TitleHeight -
296                         2 * (Scr->FramePadding + Scr->ButtonIndent));
297    Scr->TBInfo.pad = ((Scr->TitlePadding > 1)
298                       ? ((Scr->TitlePadding + 1) / 2) : 1);
299    h = Scr->TBInfo.width - 2 * Scr->TBInfo.border;
300
301    /*
302     * add in some useful buttons and bindings so that novices can still
303     * use the system.
304     */
305    if (!Scr->NoDefaults) {
306        /* insert extra buttons */
307        if (!CreateTitleButton(TBPM_ICONIFY, F_ICONIFY, "", (MenuRoot *) NULL,
308                               False, False)) {
309            twmWarning("unable to add iconify button");
310        }
311        if (!CreateTitleButton(TBPM_RESIZE, F_RESIZE, "", (MenuRoot *) NULL,
312                               True, True)) {
313            twmWarning("unable to add resize button");
314        }
315        AddDefaultBindings();
316    }
317    ComputeCommonTitleOffsets();
318
319    /*
320     * load in images and do appropriate centering
321     */
322
323    for (tb = Scr->TBInfo.head; tb; tb = tb->next) {
324        tb->bitmap = FindBitmap(tb->name, &tb->width, &tb->height);
325        if (!tb->bitmap) {
326            tb->bitmap = FindBitmap(TBPM_QUESTION, &tb->width, &tb->height);
327            if (!tb->bitmap) {  /* cannot happen (see util.c) */
328                twmWarning("unable to add titlebar button \"%s\"", tb->name);
329            }
330        }
331
332        tb->dstx = (int) (((unsigned) h - tb->width + 1) / 2);
333        if (tb->dstx < 0) {     /* clip to minimize copying */
334            tb->srcx = -(tb->dstx);
335            tb->width = (unsigned) h;
336            tb->dstx = 0;
337        }
338        else {
339            tb->srcx = 0;
340        }
341        tb->dsty = (int) (((unsigned) h - tb->height + 1) / 2);
342        if (tb->dsty < 0) {
343            tb->srcy = -(tb->dsty);
344            tb->height = (unsigned) h;
345            tb->dsty = 0;
346        }
347        else {
348            tb->srcy = 0;
349        }
350    }
351}
352
353void
354PaintEntry(MenuRoot *mr, MenuItem *mi, int exposure)
355{
356    int y_offset;
357    int text_y;
358    GC gc;
359
360#ifdef DEBUG_MENUS
361    fprintf(stderr, "Paint entry\n");
362#endif
363    y_offset = mi->item_num * Scr->EntryHeight;
364    text_y = y_offset + Scr->MenuFont.y;
365
366    if (mi->func != F_TITLE) {
367
368        if (mi->state) {
369            XSetForeground(dpy, Scr->NormalGC, mi->hi_back);
370
371            XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
372                           (unsigned) mr->width, (unsigned) Scr->EntryHeight);
373
374            MyFont_ChangeGC(mi->hi_fore, mi->hi_back, &Scr->MenuFont);
375
376            MyFont_DrawString(dpy, mr->w, &Scr->MenuFont, Scr->NormalGC, mi->x,
377                              text_y, mi->item, mi->strlen);
378
379            gc = Scr->NormalGC;
380        }
381        else {
382            if (mi->user_colors || !exposure) {
383                XSetForeground(dpy, Scr->NormalGC, mi->back);
384
385                XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
386                               (unsigned) mr->width,
387                               (unsigned) Scr->EntryHeight);
388
389                MyFont_ChangeGC(mi->fore, mi->back, &Scr->MenuFont);
390                gc = Scr->NormalGC;
391            }
392            else
393                gc = Scr->MenuGC;
394
395            MyFont_DrawString(dpy, mr->w, &Scr->MenuFont, gc,
396                              mi->x, text_y, mi->item, mi->strlen);
397
398        }
399
400        if (mi->func == F_MENU) {
401            int x, y;
402
403            /* create the pull right pixmap if needed */
404            if (Scr->pullPm == None) {
405                Scr->pullPm = CreateMenuIcon(Scr->MenuFont.height,
406                                             &Scr->pullW, &Scr->pullH);
407            }
408            x = (int) ((unsigned) mr->width - (Scr->pullW + 5));
409            y = (int) ((unsigned) y_offset +
410                       (((unsigned) Scr->MenuFont.height - Scr->pullH) / 2));
411            XCopyPlane(dpy, Scr->pullPm, mr->w, gc, 0, 0, Scr->pullW,
412                       Scr->pullH, x, y, 1);
413        }
414    }
415    else {
416        XSetForeground(dpy, Scr->NormalGC, mi->back);
417
418        /* fill the rectangle with the title background color */
419        XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
420                       (unsigned) mr->width, (unsigned) Scr->EntryHeight);
421
422        {
423            int y;
424
425            XSetForeground(dpy, Scr->NormalGC, mi->fore);
426            /* now draw the dividing lines */
427            if (y_offset)
428                XDrawLine(dpy, mr->w, Scr->NormalGC, 0, y_offset,
429                          mr->width, y_offset);
430            y = ((mi->item_num + 1) * Scr->EntryHeight) - 1;
431            XDrawLine(dpy, mr->w, Scr->NormalGC, 0, y, mr->width, y);
432        }
433
434        MyFont_ChangeGC(mi->fore, mi->back, &Scr->MenuFont);
435        /* finally render the title */
436        MyFont_DrawString(dpy, mr->w, &Scr->MenuFont, Scr->NormalGC, mi->x,
437                          text_y, mi->item, mi->strlen);
438    }
439}
440
441void
442PaintMenu(MenuRoot *mr, XEvent *e)
443{
444    MenuItem *mi;
445
446    for (mi = mr->first; mi != NULL; mi = mi->next) {
447        int y_offset = mi->item_num * Scr->EntryHeight;
448
449        /* be smart about handling the expose, redraw only the entries
450         * that we need to
451         */
452        if (e->xexpose.y < (y_offset + Scr->EntryHeight) &&
453            (e->xexpose.y + e->xexpose.height) > y_offset) {
454            PaintEntry(mr, mi, True);
455        }
456    }
457    XSync(dpy, 0);
458}
459
460static Bool fromMenu;
461
462void
463UpdateMenu(void)
464{
465    MenuItem *mi;
466    int i, x, y, x_root, y_root, entry;
467    int done;
468    MenuItem *badItem = NULL;
469    XPointer context_data;
470    unsigned udummy = 0;
471    Window wdummy = None;
472
473    fromMenu = TRUE;
474
475    while (TRUE) {
476        /* block until there is an event */
477        if (!menuFromFrameOrWindowOrTitlebar) {
478            XMaskEvent(dpy,
479                       ButtonPressMask | ButtonReleaseMask |
480                       EnterWindowMask | ExposureMask |
481                       VisibilityChangeMask | LeaveWindowMask |
482                       ButtonMotionMask, &Event);
483        }
484        if (Event.type == MotionNotify) {
485            /* discard any extra motion events before a release */
486            while (XCheckMaskEvent(dpy,
487                                   ButtonMotionMask | ButtonReleaseMask,
488                                   &Event))
489                if (Event.type == ButtonRelease)
490                    break;
491        }
492
493        if (!DispatchEvent())
494            continue;
495
496        if (Event.type == ButtonRelease || Cancel) {
497            menuFromFrameOrWindowOrTitlebar = FALSE;
498            fromMenu = FALSE;
499            return;
500        }
501
502        if (Event.type != MotionNotify)
503            continue;
504
505        if (!ActiveMenu)
506            continue;
507
508        done = FALSE;
509        XQueryPointer(dpy, ActiveMenu->w, &wdummy, &wdummy,
510                      &x_root, &y_root, &x, &y, &udummy);
511
512        /* if we haven't received the enter notify yet, wait */
513        if (!ActiveMenu->entered)
514            continue;
515
516        if (XFindContext(dpy, ActiveMenu->w, ScreenContext, &context_data) == 0)
517            Scr = (struct ScreenInfo *) context_data;
518
519        if (x < 0 || y < 0 || x >= ActiveMenu->width || y >= ActiveMenu->height) {
520            if (ActiveItem && ActiveItem->func != F_TITLE) {
521                ActiveItem->state = 0;
522                PaintEntry(ActiveMenu, ActiveItem, False);
523            }
524            ActiveItem = NULL;
525            continue;
526        }
527
528        /* look for the entry that the mouse is in */
529        entry = y / Scr->EntryHeight;
530        for (i = 0, mi = ActiveMenu->first; mi != NULL; i++, mi = mi->next) {
531            if (i == entry)
532                break;
533        }
534
535        /* if there is an active item, we might have to turn it off */
536        if (ActiveItem) {
537            /* is the active item the one we are on ? */
538            if (ActiveItem->item_num == entry && ActiveItem->state)
539                done = TRUE;
540
541            /* if we weren't on the active entry, let's turn the old
542             * active one off
543             */
544            if (!done && ActiveItem->func != F_TITLE) {
545                ActiveItem->state = 0;
546                PaintEntry(ActiveMenu, ActiveItem, False);
547            }
548        }
549
550        /* if we weren't on the active item, change the active item and turn
551         * it on
552         */
553        if (!done) {
554            ActiveItem = mi;
555            if (ActiveItem && ActiveItem->func != F_TITLE && !ActiveItem->state) {
556                ActiveItem->state = 1;
557                PaintEntry(ActiveMenu, ActiveItem, False);
558            }
559        }
560
561        /* now check to see if we were over the arrow of a pull right entry */
562        if (ActiveItem && ActiveItem->func == F_MENU &&
563            ((ActiveMenu->width - x) < (ActiveMenu->width >> 1))) {
564            MenuRoot *save = ActiveMenu;
565            int savex = MenuOrigins[MenuDepth - 1].x;
566            int savey = MenuOrigins[MenuDepth - 1].y;
567
568            if (MenuDepth < MAXMENUDEPTH) {
569                PopUpMenu(ActiveItem->sub,
570                          (savex + (ActiveMenu->width >> 1)),
571                          (savey + ActiveItem->item_num * Scr->EntryHeight)
572                          /*(savey + ActiveItem->item_num * Scr->EntryHeight +
573                             (Scr->EntryHeight >> 1)) */
574                          , False);
575            }
576            else if (!badItem) {
577                Bell(XkbBI_MinorError, 0, None);
578                badItem = ActiveItem;
579            }
580
581            /* if the menu did get popped up, unhighlight the active item */
582            if (save != ActiveMenu && ActiveItem->state) {
583                ActiveItem->state = 0;
584                PaintEntry(save, ActiveItem, False);
585                ActiveItem = NULL;
586            }
587        }
588        if (badItem != ActiveItem)
589            badItem = NULL;
590        XFlush(dpy);
591    }
592
593}
594
595/**
596 * create a new menu root
597 *
598 *  \param name  the name of the menu root
599 */
600MenuRoot *
601NewMenuRoot(const char *name)
602{
603    MenuRoot *tmp;
604
605#define UNUSED_PIXEL ((unsigned long) (~0))     /* more than 24 bits */
606
607    tmp = (MenuRoot *) malloc(sizeof(MenuRoot));
608    tmp->hi_fore = UNUSED_PIXEL;
609    tmp->hi_back = UNUSED_PIXEL;
610    tmp->name = name;
611    tmp->prev = NULL;
612    tmp->first = NULL;
613    tmp->last = NULL;
614    tmp->items = 0;
615    tmp->width = 0;
616    tmp->mapped = NEVER_MAPPED;
617    tmp->pull = FALSE;
618    tmp->w = None;
619    tmp->shadow = None;
620    tmp->real_menu = FALSE;
621
622    if (Scr->MenuList == NULL) {
623        Scr->MenuList = tmp;
624        Scr->MenuList->next = NULL;
625    }
626
627    if (Scr->LastMenu == NULL) {
628        Scr->LastMenu = tmp;
629        Scr->LastMenu->next = NULL;
630    }
631    else {
632        Scr->LastMenu->next = tmp;
633        Scr->LastMenu = tmp;
634        Scr->LastMenu->next = NULL;
635    }
636
637    if (strcmp(name, TWM_WINDOWS) == 0)
638        Scr->Windows = tmp;
639
640    return (tmp);
641}
642
643/**
644 * add an item to a root menu
645 *
646 *  \param menu   pointer to the root menu to add the item
647 *  \param item   the text to appear in the menu
648 *  \param action the string to possibly execute
649 *  \param sub    the menu root if it is a pull-right entry
650 *  \param func   the numeric function
651 *  \param fore   foreground color string
652 *  \param back   background color string
653 */
654MenuItem *
655AddToMenu(MenuRoot *menu, const char *item, const char *action,
656          MenuRoot *sub, int func, const char *fore, const char *back)
657{
658    MenuItem *tmp;
659    int width;
660
661#ifdef DEBUG_MENUS
662    fprintf(stderr, "adding menu item=\"%s\", action=%s, sub=%p, f=%d\n",
663            item, action ? action : "<null>", sub, func);
664#endif
665
666    tmp = (MenuItem *) malloc(sizeof(MenuItem));
667    tmp->root = menu;
668
669    if (menu->first == NULL) {
670        menu->first = tmp;
671        tmp->prev = NULL;
672    }
673    else {
674        menu->last->next = tmp;
675        tmp->prev = menu->last;
676    }
677    menu->last = tmp;
678
679    tmp->item = item;
680    tmp->strlen = (short) strlen(item);
681    tmp->action = action;
682    tmp->next = NULL;
683    tmp->sub = NULL;
684    tmp->state = 0;
685    tmp->func = (short) func;
686
687    if (!Scr->HaveFonts)
688        CreateFonts();
689    width = MyFont_TextWidth(&Scr->MenuFont, item, tmp->strlen);
690    if (width <= 0)
691        width = 1;
692    if (width > menu->width)
693        menu->width = (short) width;
694
695    tmp->user_colors = FALSE;
696    if (Scr->Monochrome == COLOR && fore != NULL) {
697        int save;
698
699        save = Scr->FirstTime;
700        Scr->FirstTime = TRUE;
701        GetColor(COLOR, &tmp->fore, fore);
702        GetColor(COLOR, &tmp->back, back);
703        Scr->FirstTime = (short) save;
704        tmp->user_colors = TRUE;
705    }
706    if (sub != NULL) {
707        tmp->sub = sub;
708        menu->pull = TRUE;
709    }
710    tmp->item_num = menu->items++;
711
712    return (tmp);
713}
714
715void
716MakeMenus(void)
717{
718    MenuRoot *mr;
719
720    for (mr = Scr->MenuList; mr != NULL; mr = mr->next) {
721        if (mr->real_menu == FALSE)
722            continue;
723
724        MakeMenu(mr);
725    }
726}
727
728static void
729MakeMenu(MenuRoot *mr)
730{
731    MenuItem *start, *end, *cur, *tmp;
732    XColor f1, f2, f3;
733    XColor b1, b2, b3;
734    XColor save_fore, save_back;
735    int i;
736    XSetWindowAttributes attributes;
737    Colormap cmap = Scr->TwmRoot.cmaps.cwins[0]->colormap->c;
738
739    Scr->EntryHeight = Scr->MenuFont.height + 4;
740
741    /* let's first size the window accordingly */
742    if (mr->mapped == NEVER_MAPPED) {
743        int width;
744        unsigned long valuemask;
745
746        if (mr->pull == TRUE) {
747            mr->width = (short) (mr->width + (16 + 10));
748        }
749
750        width = mr->width + 10;
751
752        for (cur = mr->first; cur != NULL; cur = cur->next) {
753            if (cur->func != F_TITLE)
754                cur->x = 5;
755            else {
756                cur->x =
757                    (short) (width -
758                             MyFont_TextWidth(&Scr->MenuFont, cur->item,
759                                              cur->strlen));
760                cur->x /= 2;
761            }
762        }
763        mr->height = (short) (mr->items * Scr->EntryHeight);
764        mr->width = (short) (mr->width + 10);
765
766        if (Scr->Shadow) {
767            /*
768             * Make sure that you don't draw into the shadow window or else
769             * the background bits there will get saved
770             */
771            valuemask = (CWBackPixel | CWBorderPixel);
772            attributes.background_pixel = Scr->MenuShadowColor;
773            attributes.border_pixel = Scr->MenuShadowColor;
774            if (Scr->SaveUnder) {
775                valuemask |= CWSaveUnder;
776                attributes.save_under = True;
777            }
778            mr->shadow = XCreateWindow(dpy, Scr->Root, 0, 0,
779                                       (unsigned int) mr->width,
780                                       (unsigned int) mr->height,
781                                       (unsigned int) 0,
782                                       CopyFromParent,
783                                       (unsigned int) CopyFromParent,
784                                       (Visual *) CopyFromParent,
785                                       valuemask, &attributes);
786        }
787
788        valuemask = (CWBackPixel | CWBorderPixel | CWEventMask);
789        attributes.background_pixel = Scr->MenuC.back;
790        attributes.border_pixel = Scr->MenuBorderColor;
791        attributes.event_mask = (ExposureMask | EnterWindowMask);
792        if (Scr->SaveUnder) {
793            valuemask |= CWSaveUnder;
794            attributes.save_under = True;
795        }
796        if (Scr->BackingStore) {
797            valuemask |= CWBackingStore;
798            attributes.backing_store = Always;
799        }
800        mr->w = XCreateWindow(dpy, Scr->Root, 0, 0, (unsigned int) mr->width,
801                              (unsigned int) mr->height,
802                              (unsigned int) Scr->MenuBorderWidth,
803                              CopyFromParent, (unsigned int) CopyFromParent,
804                              (Visual *) CopyFromParent,
805                              valuemask, &attributes);
806
807        XSaveContext(dpy, mr->w, MenuContext, (XPointer) mr);
808        XSaveContext(dpy, mr->w, ScreenContext, (XPointer) Scr);
809
810        mr->mapped = UNMAPPED;
811    }
812
813    /* get the default colors into the menus */
814    for (tmp = mr->first; tmp != NULL; tmp = tmp->next) {
815        if (!tmp->user_colors) {
816            if (tmp->func != F_TITLE) {
817                tmp->fore = Scr->MenuC.fore;
818                tmp->back = Scr->MenuC.back;
819            }
820            else {
821                tmp->fore = Scr->MenuTitleC.fore;
822                tmp->back = Scr->MenuTitleC.back;
823            }
824        }
825
826        if (mr->hi_fore != UNUSED_PIXEL) {
827            tmp->hi_fore = mr->hi_fore;
828            tmp->hi_back = mr->hi_back;
829        }
830        else {
831            tmp->hi_fore = tmp->back;
832            tmp->hi_back = tmp->fore;
833        }
834    }
835
836    if (Scr->Monochrome == MONOCHROME || !Scr->InterpolateMenuColors)
837        return;
838
839    start = mr->first;
840
841    while (TRUE) {
842        int num;
843        int fred, fgreen, fblue;
844        int bred, bgreen, bblue;
845
846        for (; start != NULL; start = start->next) {
847            if (start->user_colors)
848                break;
849        }
850        if (start == NULL)
851            break;
852
853        for (end = start->next; end != NULL; end = end->next) {
854            if (end->user_colors)
855                break;
856        }
857        if (end == NULL)
858            break;
859
860        /* we have a start and end to interpolate between */
861        num = end->item_num - start->item_num;
862
863        f1.pixel = start->fore;
864        XQueryColor(dpy, cmap, &f1);
865        f2.pixel = end->fore;
866        XQueryColor(dpy, cmap, &f2);
867
868        b1.pixel = start->back;
869        XQueryColor(dpy, cmap, &b1);
870        b2.pixel = end->back;
871        XQueryColor(dpy, cmap, &b2);
872
873        fred = ((int) f2.red - (int) f1.red) / num;
874        fgreen = ((int) f2.green - (int) f1.green) / num;
875        fblue = ((int) f2.blue - (int) f1.blue) / num;
876
877        bred = ((int) b2.red - (int) b1.red) / num;
878        bgreen = ((int) b2.green - (int) b1.green) / num;
879        bblue = ((int) b2.blue - (int) b1.blue) / num;
880
881        f3 = f1;
882        f3.flags = DoRed | DoGreen | DoBlue;
883
884        b3 = b1;
885        b3.flags = DoRed | DoGreen | DoBlue;
886
887        num -= 1;
888        for (i = 0, cur = start->next; i < num && cur; i++, cur = cur->next) {
889#define AddColor(target,source) target = (unsigned short)(target + source)
890            AddColor(f3.red, fred);
891            AddColor(f3.green, fgreen);
892            AddColor(f3.blue, fblue);
893            save_fore = f3;
894
895            AddColor(b3.red, bred);
896            AddColor(b3.green, bgreen);
897            AddColor(b3.blue, bblue);
898            save_back = b3;
899
900            XAllocColor(dpy, cmap, &f3);
901            XAllocColor(dpy, cmap, &b3);
902            cur->hi_back = cur->fore = f3.pixel;
903            cur->hi_fore = cur->back = b3.pixel;
904            cur->user_colors = True;
905
906            f3 = save_fore;
907            b3 = save_back;
908        }
909        start = end;
910    }
911}
912
913/**
914 * pop up a pull down menu.
915 *
916 *  \param menu   the root pointer of the menu to pop up
917 *  \param x,y    location of upper left of menu
918 *  \param center whether or not to center horizontally over position
919 */
920Bool
921PopUpMenu(MenuRoot *menu, int x, int y, Bool center)
922{
923    TwmWindow **WindowNames;
924    TwmWindow *tmp_win2, *tmp_win3;
925    int (*compar) (const char *, const char *) =
926        (Scr->CaseSensitive ? strcmp : XmuCompareISOLatin1);
927
928    if (!menu)
929        return False;
930
931    InstallRootColormap();
932
933    if (menu == Scr->Windows) {
934        TwmWindow *tmp_win;
935        int WindowNameCount;
936
937        /* this is the twm windows menu,  let's go ahead and build it */
938
939        DestroyMenu(menu);
940
941        menu->first = NULL;
942        menu->last = NULL;
943        menu->items = 0;
944        menu->width = 0;
945        menu->mapped = NEVER_MAPPED;
946        AddToMenu(menu, "TWM Windows", NULLSTR, NULL, F_TITLE, NULLSTR,
947                  NULLSTR);
948
949        for (tmp_win = Scr->TwmRoot.next, WindowNameCount = 0;
950             tmp_win != NULL; tmp_win = tmp_win->next)
951            WindowNameCount++;
952
953        if (WindowNameCount != 0) {
954            int i;
955
956            WindowNames = (TwmWindow **)
957                malloc(sizeof(TwmWindow *) * (size_t) WindowNameCount);
958            WindowNames[0] = Scr->TwmRoot.next;
959
960            for (tmp_win = Scr->TwmRoot.next->next, WindowNameCount = 1;
961                 tmp_win != NULL; tmp_win = tmp_win->next, WindowNameCount++) {
962                tmp_win2 = tmp_win;
963                for (i = 0; i < WindowNameCount; i++) {
964                    if ((*compar) (tmp_win2->name, WindowNames[i]->name) < 0) {
965                        tmp_win3 = tmp_win2;
966                        tmp_win2 = WindowNames[i];
967                        WindowNames[i] = tmp_win3;
968                    }
969                }
970                WindowNames[WindowNameCount] = tmp_win2;
971            }
972            for (i = 0; i < WindowNameCount; i++) {
973                AddToMenu(menu, WindowNames[i]->name, (char *) WindowNames[i],
974                          NULL, F_POPUP, NULL, NULL);
975            }
976            free(WindowNames);
977        }
978
979        MakeMenu(menu);
980    }
981
982    if (menu->w == None || menu->items == 0)
983        return False;
984
985    /* Prevent recursively bringing up menus. */
986    if (menu->mapped == MAPPED)
987        return False;
988
989    /*
990     * Dynamically set the parent;  this allows pull-ups to also be main
991     * menus, or to be brought up from more than one place.
992     */
993    menu->prev = ActiveMenu;
994
995    XGrabPointer(dpy, Scr->Root, True,
996                 ButtonPressMask | ButtonReleaseMask |
997                 ButtonMotionMask | PointerMotionHintMask,
998                 GrabModeAsync, GrabModeAsync,
999                 Scr->Root, Scr->MenuCursor, CurrentTime);
1000
1001    ActiveMenu = menu;
1002    menu->mapped = MAPPED;
1003    menu->entered = FALSE;
1004
1005    if (center) {
1006        x -= (menu->width / 2);
1007        y -= (Scr->EntryHeight / 2);    /* sticky menus would be nice here */
1008    }
1009
1010    /*
1011     * clip to screen
1012     */
1013    if (x + menu->width > Scr->MyDisplayWidth) {
1014        x = Scr->MyDisplayWidth - menu->width;
1015    }
1016    if (x < 0)
1017        x = 0;
1018    if (y + menu->height > Scr->MyDisplayHeight) {
1019        y = Scr->MyDisplayHeight - menu->height;
1020    }
1021    if (y < 0)
1022        y = 0;
1023
1024    MenuOrigins[MenuDepth].x = x;
1025    MenuOrigins[MenuDepth].y = y;
1026    MenuDepth++;
1027
1028    XMoveWindow(dpy, menu->w, x, y);
1029    if (Scr->Shadow) {
1030        XMoveWindow(dpy, menu->shadow, x + SHADOWWIDTH, y + SHADOWWIDTH);
1031    }
1032    if (Scr->Shadow) {
1033        XRaiseWindow(dpy, menu->shadow);
1034    }
1035    XMapRaised(dpy, menu->w);
1036    if (Scr->Shadow) {
1037        XMapWindow(dpy, menu->shadow);
1038    }
1039    XSync(dpy, 0);
1040    return True;
1041}
1042
1043/**
1044 * unhighlight the current menu selection and take down the menus
1045 */
1046void
1047PopDownMenu(void)
1048{
1049    MenuRoot *tmp;
1050
1051    if (ActiveMenu == NULL)
1052        return;
1053
1054    if (ActiveItem) {
1055        ActiveItem->state = 0;
1056        PaintEntry(ActiveMenu, ActiveItem, False);
1057    }
1058
1059    for (tmp = ActiveMenu; tmp != NULL; tmp = tmp->prev) {
1060        if (Scr->Shadow) {
1061            XUnmapWindow(dpy, tmp->shadow);
1062        }
1063        XUnmapWindow(dpy, tmp->w);
1064        tmp->mapped = UNMAPPED;
1065        UninstallRootColormap();
1066    }
1067
1068    XFlush(dpy);
1069    ActiveMenu = NULL;
1070    ActiveItem = NULL;
1071    MenuDepth = 0;
1072    if (Context == C_WINDOW || Context == C_FRAME || Context == C_TITLE)
1073        menuFromFrameOrWindowOrTitlebar = TRUE;
1074}
1075
1076/**
1077 * look for a menu root
1078 *
1079 *  \return a pointer to the menu root structure
1080 *
1081 *  \param name the name of the menu root
1082 */
1083MenuRoot *
1084FindMenuRoot(const char *name)
1085{
1086    MenuRoot *tmp;
1087
1088    for (tmp = Scr->MenuList; tmp != NULL; tmp = tmp->next) {
1089        if (strcmp(name, tmp->name) == 0)
1090            return (tmp);
1091    }
1092    return NULL;
1093}
1094
1095static Bool
1096belongs_to_twm_window(TwmWindow *t, Window w)
1097{
1098    if (!t)
1099        return False;
1100
1101    if (w == t->frame || w == t->title_w || w == t->hilite_w ||
1102        w == t->icon_w || w == t->icon_bm_w)
1103        return True;
1104
1105    if (t && t->titlebuttons) {
1106        TBWindow *tbw;
1107        int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
1108
1109        for (tbw = t->titlebuttons; nb > 0; tbw++, nb--) {
1110            if (tbw->window == w)
1111                return True;
1112        }
1113    }
1114    return False;
1115}
1116
1117static void
1118resizeFromCenter(Window w, TwmWindow *tmp_win)
1119{
1120    int lastx, lasty, bw2;
1121    XEvent event;
1122    int dummy = 0;
1123    unsigned udummy = 0;
1124    Window wdummy = None;
1125
1126    bw2 = tmp_win->frame_bw * 2;
1127    AddingW = tmp_win->attr.width + bw2;
1128    AddingH = tmp_win->attr.height + tmp_win->title_height + bw2;
1129    XGetGeometry(dpy, w, &wdummy, &origDragX, &origDragY,
1130                 (unsigned int *) &DragWidth, (unsigned int *) &DragHeight,
1131                 &udummy, &udummy);
1132    XWarpPointer(dpy, None, w, 0, 0, 0, 0, DragWidth / 2, DragHeight / 2);
1133    XQueryPointer(dpy, Scr->Root, &wdummy,
1134                  &wdummy, &dummy, &dummy, &AddingX, &AddingY, &udummy);
1135    lastx = -10000;
1136    lasty = -10000;
1137    MenuStartResize(tmp_win, origDragX, origDragY, DragWidth, DragHeight);
1138    while (TRUE) {
1139        XMaskEvent(dpy, ButtonPressMask | PointerMotionMask, &event);
1140
1141        if (event.type == MotionNotify) {
1142            /* discard any extra motion events before a release */
1143            while (XCheckMaskEvent(dpy,
1144                                   ButtonMotionMask | ButtonPressMask, &event))
1145                if (event.type == ButtonPress)
1146                    break;
1147        }
1148
1149        if (event.type == ButtonPress) {
1150            MenuEndResize(tmp_win);
1151            XMoveResizeWindow(dpy, w, AddingX, AddingY, (unsigned) AddingW,
1152                              (unsigned) AddingH);
1153            break;
1154        }
1155
1156        /* if (!DispatchEvent ()) continue; */
1157
1158        if (event.type != MotionNotify) {
1159            continue;
1160        }
1161
1162        /*
1163         * XXX - if we are going to do a loop, we ought to consider
1164         * using multiple GXxor lines so that we don't need to
1165         * grab the server.
1166         */
1167        XQueryPointer(dpy, Scr->Root, &wdummy, &wdummy,
1168                      &dummy, &dummy, &AddingX, &AddingY, &udummy);
1169
1170        if (lastx != AddingX || lasty != AddingY) {
1171            MenuDoResize(AddingX, AddingY, tmp_win);
1172
1173            lastx = AddingX;
1174            lasty = AddingY;
1175        }
1176
1177    }
1178}
1179
1180/** \fn ExecuteFunction
1181 * execute a twm root function.
1182 *
1183 *  \param func     the function to execute
1184 *  \param action   the menu action to execute
1185 *  \param w        the window to execute this function on
1186 *  \param tmp_win  the twm window structure
1187 *  \param event    the event that caused the function
1188 *  \param context  the context in which the button was pressed
1189 *  \param pulldown flag indicating execution from pull down menu
1190 *
1191 *  \return TRUE if should continue with remaining actions,
1192 *           else FALSE to abort
1193 */
1194
1195static int
1196WarpThere(TwmWindow *t)
1197{
1198    if (Scr->WarpUnmapped || t->mapped) {
1199        if (!t->mapped)
1200            DeIconify(t);
1201        if (!Scr->NoRaiseWarp)
1202            XRaiseWindow(dpy, t->frame);
1203        WarpToWindow(t);
1204        return 1;
1205    }
1206    return 0;
1207}
1208
1209int
1210ExecuteFunction(int func, const char *action, Window w, TwmWindow *tmp_win,
1211                XEvent *eventp, int context, int pulldown)
1212{
1213    static Time last_time = 0;
1214    char tmp[200];
1215    char *ptr;
1216    char buff[MAX_FILE_SIZE];
1217    int count, fd;
1218    Window rootw;
1219    int origX, origY;
1220    int do_next_action = TRUE;
1221    int moving_icon = FALSE;
1222    Bool fromtitlebar = False;
1223    unsigned bw = 0;
1224    int dummy = 0;
1225    unsigned udummy = 0;
1226    Window wdummy = None;
1227
1228    RootFunction = 0;
1229    if (Cancel)
1230        return TRUE;            /* XXX should this be FALSE? */
1231
1232    switch (func) {
1233    case F_UPICONMGR:
1234    case F_LEFTICONMGR:
1235    case F_RIGHTICONMGR:
1236    case F_DOWNICONMGR:
1237    case F_FORWICONMGR:
1238    case F_BACKICONMGR:
1239    case F_NEXTICONMGR:
1240    case F_PREVICONMGR:
1241    case F_NOP:
1242    case F_TITLE:
1243    case F_DELTASTOP:
1244    case F_RAISELOWER:
1245    case F_WARPTOSCREEN:
1246    case F_WARPTO:
1247    case F_WARPRING:
1248    case F_WARPTOICONMGR:
1249    case F_WARPNEXT:
1250    case F_WARPPREV:
1251    case F_COLORMAP:
1252        break;
1253    default:
1254        XGrabPointer(dpy, Scr->Root, True,
1255                     ButtonPressMask | ButtonReleaseMask,
1256                     GrabModeAsync, GrabModeAsync,
1257                     Scr->Root, Scr->WaitCursor, CurrentTime);
1258        break;
1259    }
1260
1261    switch (func) {
1262    case F_NOP:
1263    case F_TITLE:
1264        break;
1265
1266    case F_DELTASTOP:
1267        if (WindowMoved)
1268            do_next_action = FALSE;
1269        break;
1270
1271    case F_RESTART:
1272    {
1273        XSync(dpy, 0);
1274        Reborder(eventp->xbutton.time);
1275        XSync(dpy, 0);
1276        if (smcConn)
1277            SmcCloseConnection(smcConn, 0, NULL);
1278        execvp(*Argv, Argv);
1279        twmWarning("unable to restart:  %s", *Argv);
1280        break;
1281    }
1282
1283    case F_UPICONMGR:
1284    case F_DOWNICONMGR:
1285    case F_LEFTICONMGR:
1286    case F_RIGHTICONMGR:
1287    case F_FORWICONMGR:
1288    case F_BACKICONMGR:
1289        MoveIconManager(func);
1290        break;
1291
1292    case F_NEXTICONMGR:
1293    case F_PREVICONMGR:
1294        JumpIconManager(func);
1295        break;
1296
1297    case F_SHOWLIST:
1298        if (Scr->NoIconManagers)
1299            break;
1300        DeIconify(Scr->iconmgr.twm_win);
1301        XRaiseWindow(dpy, Scr->iconmgr.twm_win->frame);
1302        break;
1303
1304    case F_HIDELIST:
1305        if (Scr->NoIconManagers)
1306            break;
1307        HideIconManager();
1308        break;
1309
1310    case F_SORTICONMGR:
1311        if (DeferExecution(context, func, Scr->SelectCursor))
1312            return TRUE;
1313
1314        {
1315            int save_sort;
1316
1317            save_sort = Scr->SortIconMgr;
1318            Scr->SortIconMgr = TRUE;
1319
1320            if (context == C_ICONMGR)
1321                SortIconManager((IconMgr *) NULL);
1322            else if (tmp_win->iconmgr)
1323                SortIconManager(tmp_win->iconmgrp);
1324            else
1325                Bell(XkbBI_Info, 0, tmp_win->w);
1326
1327            Scr->SortIconMgr = (short) save_sort;
1328        }
1329        break;
1330
1331    case F_IDENTIFY:
1332        if (DeferExecution(context, func, Scr->SelectCursor))
1333            return TRUE;
1334
1335        Identify(tmp_win);
1336        break;
1337
1338    case F_VERSION:
1339        Identify((TwmWindow *) NULL);
1340        break;
1341
1342    case F_AUTORAISE:
1343        if (DeferExecution(context, func, Scr->SelectCursor))
1344            return TRUE;
1345
1346        tmp_win->auto_raise = !tmp_win->auto_raise;
1347        if (tmp_win->auto_raise)
1348            ++(Scr->NumAutoRaises);
1349        else
1350            --(Scr->NumAutoRaises);
1351        break;
1352
1353    case F_BEEP:
1354        Bell(XkbBI_Info, 0, None);
1355        break;
1356
1357    case F_POPUP:
1358        tmp_win = (TwmWindow *) action;
1359        if (Scr->WindowFunction.func != 0) {
1360            ExecuteFunction(Scr->WindowFunction.func,
1361                            Scr->WindowFunction.item->action,
1362                            w, tmp_win, eventp, C_FRAME, FALSE);
1363        }
1364        else {
1365            DeIconify(tmp_win);
1366            XRaiseWindow(dpy, tmp_win->frame);
1367        }
1368        break;
1369
1370    case F_RESIZE:
1371        EventHandler[EnterNotify] = HandleUnknown;
1372        EventHandler[LeaveNotify] = HandleUnknown;
1373        if (DeferExecution(context, func, Scr->MoveCursor))
1374            return TRUE;
1375
1376        PopDownMenu();
1377
1378        if (pulldown)
1379            XWarpPointer(dpy, None, Scr->Root,
1380                         0, 0, 0, 0, eventp->xbutton.x_root,
1381                         eventp->xbutton.y_root);
1382
1383        if (w != tmp_win->icon_w) {     /* can't resize icons */
1384
1385            if ((Context == C_FRAME || Context == C_WINDOW ||
1386                 Context == C_TITLE)
1387                && fromMenu)
1388                resizeFromCenter(w, tmp_win);
1389            else {
1390                /*
1391                 * see if this is being done from the titlebar
1392                 */
1393                fromtitlebar =
1394                    belongs_to_twm_window(tmp_win, eventp->xbutton.window);
1395
1396                /* Save pointer position so we can tell if it was moved or
1397                   not during the resize. */
1398                ResizeOrigX = eventp->xbutton.x_root;
1399                ResizeOrigY = eventp->xbutton.y_root;
1400
1401                StartResize(eventp, tmp_win, fromtitlebar);
1402
1403                do {
1404                    XMaskEvent(dpy,
1405                               ButtonPressMask | ButtonReleaseMask |
1406                               EnterWindowMask | LeaveWindowMask |
1407                               ButtonMotionMask, &Event);
1408
1409                    if (fromtitlebar && Event.type == ButtonPress) {
1410                        fromtitlebar = False;
1411                        continue;
1412                    }
1413
1414                    if (Event.type == MotionNotify) {
1415                        /* discard any extra motion events before a release */
1416                        while
1417                            (XCheckMaskEvent
1418                             (dpy, ButtonMotionMask | ButtonReleaseMask,
1419                              &Event))
1420                            if (Event.type == ButtonRelease)
1421                                break;
1422                    }
1423
1424                    if (!DispatchEvent())
1425                        continue;
1426
1427                } while (!(Event.type == ButtonRelease || Cancel));
1428                return TRUE;
1429            }
1430        }
1431        break;
1432
1433    case F_ZOOM:
1434    case F_HORIZOOM:
1435    case F_FULLZOOM:
1436    case F_LEFTZOOM:
1437    case F_RIGHTZOOM:
1438    case F_TOPZOOM:
1439    case F_BOTTOMZOOM:
1440        if (DeferExecution(context, func, Scr->SelectCursor))
1441            return TRUE;
1442        fullzoom(tmp_win, func);
1443        break;
1444
1445    case F_MOVE:
1446    case F_FORCEMOVE:
1447        if (DeferExecution(context, func, Scr->MoveCursor))
1448            return TRUE;
1449
1450        PopDownMenu();
1451        rootw = eventp->xbutton.root;
1452        MoveFunction = func;
1453
1454        if (pulldown)
1455            XWarpPointer(dpy, None, Scr->Root,
1456                         0, 0, 0, 0, eventp->xbutton.x_root,
1457                         eventp->xbutton.y_root);
1458
1459        EventHandler[EnterNotify] = HandleUnknown;
1460        EventHandler[LeaveNotify] = HandleUnknown;
1461
1462        if (!Scr->NoGrabServer || !Scr->OpaqueMove) {
1463            XGrabServer(dpy);
1464        }
1465        XGrabPointer(dpy, eventp->xbutton.root, True, ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | PointerMotionMask,       /* PointerMotionHintMask */
1466                     GrabModeAsync, GrabModeAsync,
1467                     Scr->Root, Scr->MoveCursor, CurrentTime);
1468
1469        if (context == C_ICON && tmp_win->icon_w) {
1470            w = tmp_win->icon_w;
1471            DragX = eventp->xbutton.x;
1472            DragY = eventp->xbutton.y;
1473            moving_icon = TRUE;
1474        }
1475
1476        else if (w != tmp_win->icon_w) {
1477            XTranslateCoordinates(dpy, w, tmp_win->frame,
1478                                  eventp->xbutton.x,
1479                                  eventp->xbutton.y,
1480                                  &DragX, &DragY, &wdummy);
1481
1482            w = tmp_win->frame;
1483        }
1484
1485        DragWindow = None;
1486
1487        XGetGeometry(dpy, w, &wdummy, &origDragX, &origDragY,
1488                     (unsigned int *) &DragWidth, (unsigned int *) &DragHeight,
1489                     &bw, &udummy);
1490
1491        origX = eventp->xbutton.x_root;
1492        origY = eventp->xbutton.y_root;
1493        CurrentDragX = origDragX;
1494        CurrentDragY = origDragY;
1495
1496        /*
1497         * only do the constrained move if timer is set; need to check it
1498         * in case of stupid or wicked fast servers
1499         */
1500        if (ConstrainedMoveTime &&
1501            (eventp->xbutton.time - last_time) < (Time) ConstrainedMoveTime) {
1502            int width, height;
1503
1504            ConstMove = TRUE;
1505            ConstMoveDir = MOVE_NONE;
1506            ConstMoveX =
1507                (int) ((unsigned) eventp->xbutton.x_root - (unsigned) DragX -
1508                       bw);
1509            ConstMoveY =
1510                (int) ((unsigned) eventp->xbutton.y_root - (unsigned) DragY -
1511                       bw);
1512            width = (int) ((unsigned) DragWidth + 2 * bw);
1513            height = (int) ((unsigned) DragHeight + 2 * bw);
1514            ConstMoveXL = ConstMoveX + width / 3;
1515            ConstMoveXR = ConstMoveX + 2 * (width / 3);
1516            ConstMoveYT = ConstMoveY + height / 3;
1517            ConstMoveYB = ConstMoveY + 2 * (height / 3);
1518
1519            XWarpPointer(dpy, None, w,
1520                         0, 0, 0, 0, DragWidth / 2, DragHeight / 2);
1521
1522            XQueryPointer(dpy, w, &wdummy, &wdummy,
1523                          &dummy, &dummy, &DragX, &DragY, &udummy);
1524        }
1525        last_time = eventp->xbutton.time;
1526
1527        if (!Scr->OpaqueMove) {
1528            InstallRootColormap();
1529            if (!Scr->MoveDelta) {
1530                /*
1531                 * Draw initial outline.  This was previously done the
1532                 * first time though the outer loop by dropping out of
1533                 * the XCheckMaskEvent inner loop down to one of the
1534                 * MoveOutline's below.
1535                 */
1536                MoveOutline(rootw,
1537                            (int) ((unsigned) origDragX - bw),
1538                            (int) ((unsigned) origDragY - bw),
1539                            (int) ((unsigned) DragWidth + 2 * bw),
1540                            (int) ((unsigned) DragHeight + 2 * bw),
1541                            tmp_win->frame_bw,
1542                            moving_icon ? 0 : tmp_win->title_height);
1543                /*
1544                 * This next line causes HandleReleaseNotify to call
1545                 * XRaiseWindow().  This is solely to preserve the
1546                 * previous behaviour that raises a window being moved
1547                 * on button release even if you never actually moved
1548                 * any distance (unless you move less than MoveDelta or
1549                 * NoRaiseMove is set or OpaqueMove is set).
1550                 */
1551                DragWindow = w;
1552            }
1553        }
1554
1555        /*
1556         * see if this is being done from the titlebar
1557         */
1558        fromtitlebar = belongs_to_twm_window(tmp_win, eventp->xbutton.window);
1559
1560        if (menuFromFrameOrWindowOrTitlebar) {
1561            /* warp the pointer to the middle of the window */
1562            XWarpPointer(dpy, None, Scr->Root, 0, 0, 0, 0,
1563                         origDragX + DragWidth / 2, origDragY + DragHeight / 2);
1564            XFlush(dpy);
1565        }
1566
1567        while (TRUE) {
1568            long releaseEvent = menuFromFrameOrWindowOrTitlebar ?
1569                ButtonPress : ButtonRelease;
1570            long movementMask = menuFromFrameOrWindowOrTitlebar ?
1571                PointerMotionMask : ButtonMotionMask;
1572
1573            /* block until there is an interesting event */
1574            XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask |
1575                       EnterWindowMask | LeaveWindowMask |
1576                       ExposureMask | movementMask |
1577                       VisibilityChangeMask, &Event);
1578
1579            /* throw away enter and leave events until release */
1580            if (Event.xany.type == EnterNotify ||
1581                Event.xany.type == LeaveNotify)
1582                continue;
1583
1584            if (Event.type == MotionNotify) {
1585                /* discard any extra motion events before a logical release */
1586                while (XCheckMaskEvent(dpy,
1587                                       movementMask | releaseEvent, &Event))
1588                    if (Event.type == releaseEvent)
1589                        break;
1590            }
1591
1592            /* test to see if we have a second button press to abort move */
1593            if (!menuFromFrameOrWindowOrTitlebar && !MovedFromKeyPress) {
1594                if (Event.type == ButtonPress && DragWindow != None) {
1595                    if (Scr->OpaqueMove)
1596                        XMoveWindow(dpy, DragWindow, origDragX, origDragY);
1597                    else
1598                        MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0);
1599                    DragWindow = None;
1600                }
1601            }
1602            if (fromtitlebar && Event.type == ButtonPress) {
1603                fromtitlebar = False;
1604                CurrentDragX = origX = Event.xbutton.x_root;
1605                CurrentDragY = origY = Event.xbutton.y_root;
1606
1607                XTranslateCoordinates(dpy, rootw, tmp_win->frame,
1608                                      origX, origY, &DragX, &DragY, &wdummy);
1609                continue;
1610            }
1611
1612            if (!DispatchEvent2())
1613                continue;
1614
1615            if (Cancel) {
1616                WindowMoved = FALSE;
1617                if (!Scr->OpaqueMove)
1618                    UninstallRootColormap();
1619                return TRUE;    /* XXX should this be FALSE? */
1620            }
1621            if (Event.type == releaseEvent) {
1622                MoveOutline(rootw, 0, 0, 0, 0, 0, 0);
1623                if (moving_icon &&
1624                    ((CurrentDragX != origDragX || CurrentDragY != origDragY)))
1625                    tmp_win->icon_moved = TRUE;
1626                if (!Scr->OpaqueMove && menuFromFrameOrWindowOrTitlebar)
1627                    XMoveWindow(dpy, DragWindow,
1628                                Event.xbutton.x_root - DragWidth / 2,
1629                                Event.xbutton.y_root - DragHeight / 2);
1630                break;
1631            }
1632
1633            /* something left to do only if the pointer moved */
1634            if (Event.type != MotionNotify)
1635                continue;
1636
1637            XQueryPointer(dpy, rootw, &(eventp->xmotion.root), &wdummy,
1638                          &(eventp->xmotion.x_root), &(eventp->xmotion.y_root),
1639                          &dummy, &dummy, &udummy);
1640
1641            if (DragWindow == None &&
1642                abs(eventp->xmotion.x_root - origX) < Scr->MoveDelta &&
1643                abs(eventp->xmotion.y_root - origY) < Scr->MoveDelta)
1644                continue;
1645
1646            WindowMoved = TRUE;
1647            DragWindow = w;
1648
1649            if (!Scr->NoRaiseMove && Scr->OpaqueMove)   /* can't restore... */
1650                XRaiseWindow(dpy, DragWindow);
1651
1652            if (ConstMove) {
1653                switch (ConstMoveDir) {
1654                case MOVE_NONE:
1655                    if (eventp->xmotion.x_root < ConstMoveXL ||
1656                        eventp->xmotion.x_root > ConstMoveXR)
1657                        ConstMoveDir = MOVE_HORIZ;
1658
1659                    if (eventp->xmotion.y_root < ConstMoveYT ||
1660                        eventp->xmotion.y_root > ConstMoveYB)
1661                        ConstMoveDir = MOVE_VERT;
1662
1663                    XQueryPointer(dpy, DragWindow, &wdummy, &wdummy,
1664                                  &dummy, &dummy, &DragX, &DragY, &udummy);
1665                    break;
1666
1667                case MOVE_VERT:
1668                    ConstMoveY =
1669                        (int) ((unsigned) eventp->xmotion.y_root -
1670                               (unsigned) DragY - bw);
1671                    break;
1672
1673                case MOVE_HORIZ:
1674                    ConstMoveX =
1675                        (int) ((unsigned) eventp->xmotion.x_root -
1676                               (unsigned) DragX - bw);
1677                    break;
1678                }
1679
1680                if (ConstMoveDir != MOVE_NONE) {
1681                    int xl, yt, w2, h;
1682
1683                    xl = ConstMoveX;
1684                    yt = ConstMoveY;
1685                    w2 = (int) ((unsigned) DragWidth + 2 * bw);
1686                    h = (int) ((unsigned) DragHeight + 2 * bw);
1687
1688                    if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) {
1689                        int xr = xl + w2;
1690                        int yb = yt + h;
1691
1692                        if (xl < 0)
1693                            xl = 0;
1694                        if (xr > Scr->MyDisplayWidth)
1695                            xl = Scr->MyDisplayWidth - w2;
1696
1697                        if (yt < 0)
1698                            yt = 0;
1699                        if (yb > Scr->MyDisplayHeight)
1700                            yt = Scr->MyDisplayHeight - h;
1701                    }
1702                    CurrentDragX = xl;
1703                    CurrentDragY = yt;
1704                    if (Scr->OpaqueMove)
1705                        XMoveWindow(dpy, DragWindow, xl, yt);
1706                    else
1707                        MoveOutline(eventp->xmotion.root, xl, yt, w2, h,
1708                                    tmp_win->frame_bw,
1709                                    moving_icon ? 0 : tmp_win->title_height);
1710                }
1711            }
1712            else if (DragWindow != None) {
1713                int xl, yt, w2, h;
1714
1715                if (!menuFromFrameOrWindowOrTitlebar) {
1716                    xl = (int) ((unsigned) eventp->xmotion.x_root -
1717                                (unsigned) DragX - bw);
1718                    yt = (int) ((unsigned) eventp->xmotion.y_root -
1719                                (unsigned) DragY - bw);
1720                }
1721                else {
1722                    xl = (int) (eventp->xmotion.x_root - (DragWidth / 2));
1723                    yt = (int) (eventp->xmotion.y_root - (DragHeight / 2));
1724                }
1725                w2 = (int) ((unsigned) DragWidth + 2 * bw);
1726                h = (int) ((unsigned) DragHeight + 2 * bw);
1727
1728                if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) {
1729                    int xr = xl + w2;
1730                    int yb = yt + h;
1731
1732                    if (xl < 0)
1733                        xl = 0;
1734                    if (xr > Scr->MyDisplayWidth)
1735                        xl = Scr->MyDisplayWidth - w2;
1736
1737                    if (yt < 0)
1738                        yt = 0;
1739                    if (yb > Scr->MyDisplayHeight)
1740                        yt = Scr->MyDisplayHeight - h;
1741                }
1742
1743                CurrentDragX = xl;
1744                CurrentDragY = yt;
1745                if (Scr->OpaqueMove)
1746                    XMoveWindow(dpy, DragWindow, xl, yt);
1747                else
1748                    MoveOutline(eventp->xmotion.root, xl, yt, w2, h,
1749                                tmp_win->frame_bw,
1750                                moving_icon ? 0 : tmp_win->title_height);
1751            }
1752
1753        }
1754        MovedFromKeyPress = False;
1755
1756        if (!Scr->OpaqueMove && DragWindow == None)
1757            UninstallRootColormap();
1758
1759        break;
1760
1761    case F_FUNCTION:
1762    {
1763        MenuRoot *mroot;
1764        MenuItem *mitem;
1765
1766        if ((mroot = FindMenuRoot(action)) == NULL) {
1767            twmWarning("couldn't find function \"%s\"", action);
1768            return TRUE;
1769        }
1770
1771        if (NeedToDefer(mroot) &&
1772            DeferExecution(context, func, Scr->SelectCursor))
1773            return TRUE;
1774        else {
1775            for (mitem = mroot->first; mitem != NULL; mitem = mitem->next) {
1776                if (!ExecuteFunction(mitem->func, mitem->action, w,
1777                                     tmp_win, eventp, context, pulldown))
1778                    break;
1779            }
1780        }
1781    }
1782        break;
1783
1784    case F_DEICONIFY:
1785    case F_ICONIFY:
1786        if (DeferExecution(context, func, Scr->SelectCursor))
1787            return TRUE;
1788
1789        if (tmp_win->icon) {
1790            DeIconify(tmp_win);
1791        }
1792        else if (func == F_ICONIFY) {
1793            Iconify(tmp_win, eventp->xbutton.x_root - 5,
1794                    eventp->xbutton.y_root - 5);
1795        }
1796        break;
1797
1798    case F_RAISELOWER:
1799        if (DeferExecution(context, func, Scr->SelectCursor))
1800            return TRUE;
1801
1802        if (!WindowMoved) {
1803            XWindowChanges xwc;
1804
1805            xwc.stack_mode = Opposite;
1806            if (w != tmp_win->icon_w)
1807                w = tmp_win->frame;
1808            XConfigureWindow(dpy, w, CWStackMode, &xwc);
1809        }
1810        break;
1811
1812    case F_RAISE:
1813        if (DeferExecution(context, func, Scr->SelectCursor))
1814            return TRUE;
1815
1816        /* check to make sure raise is not from the WindowFunction */
1817        if (w == tmp_win->icon_w && Context != C_ROOT)
1818            XRaiseWindow(dpy, tmp_win->icon_w);
1819        else
1820            XRaiseWindow(dpy, tmp_win->frame);
1821
1822        break;
1823
1824    case F_LOWER:
1825        if (DeferExecution(context, func, Scr->SelectCursor))
1826            return TRUE;
1827
1828        if (w == tmp_win->icon_w)
1829            XLowerWindow(dpy, tmp_win->icon_w);
1830        else
1831            XLowerWindow(dpy, tmp_win->frame);
1832
1833        break;
1834
1835    case F_FOCUS:
1836        if (DeferExecution(context, func, Scr->SelectCursor))
1837            return TRUE;
1838
1839        if (tmp_win->icon == FALSE) {
1840            if (!Scr->FocusRoot && Scr->Focus == tmp_win) {
1841                FocusOnRoot();
1842            }
1843            else {
1844                if (Scr->Focus != NULL) {
1845                    SetBorder(Scr->Focus, False);
1846                    if (Scr->Focus->hilite_w)
1847                        XUnmapWindow(dpy, Scr->Focus->hilite_w);
1848                }
1849
1850                InstallWindowColormaps(0, tmp_win);
1851                if (tmp_win->hilite_w)
1852                    XMapWindow(dpy, tmp_win->hilite_w);
1853                SetBorder(tmp_win, True);
1854                if (!tmp_win->wmhints || tmp_win->wmhints->input)
1855                    SetFocus(tmp_win, eventp->xbutton.time);
1856                Scr->FocusRoot = FALSE;
1857                Scr->Focus = tmp_win;
1858            }
1859        }
1860        break;
1861
1862    case F_DESTROY:
1863        if (DeferExecution(context, func, Scr->DestroyCursor))
1864            return TRUE;
1865
1866        if (tmp_win->iconmgr)
1867            Bell(XkbBI_MinorError, 0, tmp_win->w);
1868        else
1869            XKillClient(dpy, tmp_win->w);
1870        break;
1871
1872    case F_DELETE:
1873        if (DeferExecution(context, func, Scr->DestroyCursor))
1874            return TRUE;
1875
1876        if (tmp_win->iconmgr)   /* don't send ourself a message */
1877            HideIconManager();
1878        else if (tmp_win->protocols & DoesWmDeleteWindow)
1879            SendDeleteWindowMessage(tmp_win, LastTimestamp());
1880        else
1881            Bell(XkbBI_MinorError, 0, tmp_win->w);
1882        break;
1883
1884    case F_SAVEYOURSELF:
1885        if (DeferExecution(context, func, Scr->SelectCursor))
1886            return TRUE;
1887
1888        if (tmp_win->protocols & DoesWmSaveYourself)
1889            SendSaveYourselfMessage(tmp_win, LastTimestamp());
1890        else
1891            Bell(XkbBI_MinorError, 0, tmp_win->w);
1892        break;
1893
1894    case F_CIRCLEUP:
1895        XCirculateSubwindowsUp(dpy, Scr->Root);
1896        break;
1897
1898    case F_CIRCLEDOWN:
1899        XCirculateSubwindowsDown(dpy, Scr->Root);
1900        break;
1901
1902    case F_EXEC:
1903        PopDownMenu();
1904        if (!Scr->NoGrabServer) {
1905            XUngrabServer(dpy);
1906            XSync(dpy, 0);
1907        }
1908        Execute(action);
1909        break;
1910
1911    case F_UNFOCUS:
1912        FocusOnRoot();
1913        break;
1914
1915    case F_CUT:
1916        strcpy(tmp, action);
1917        strcat(tmp, "\n");
1918        XStoreBytes(dpy, tmp, (int) strlen(tmp));
1919        break;
1920
1921    case F_CUTFILE:
1922        ptr = XFetchBytes(dpy, &count);
1923        if (ptr) {
1924            if (sscanf(ptr, "%s", tmp) == 1) {
1925                XFree(ptr);
1926                ptr = ExpandFilename(tmp);
1927                if (ptr) {
1928                    fd = open(ptr, O_RDONLY);
1929                    if (fd >= 0) {
1930                        count = (int) read(fd, buff, MAX_FILE_SIZE - 1);
1931                        if (count > 0)
1932                            XStoreBytes(dpy, buff, count);
1933                        close(fd);
1934                    }
1935                    else {
1936                        twmWarning("unable to open cut file \"%s\"", tmp);
1937                    }
1938                    free(ptr);
1939                }
1940            }
1941            else {
1942                XFree(ptr);
1943            }
1944        }
1945        else {
1946            twmWarning("cut buffer is empty");
1947        }
1948        break;
1949
1950    case F_WARPTOSCREEN:
1951    {
1952        if (strcmp(action, WARPSCREEN_NEXT) == 0) {
1953            WarpToScreen(Scr->screen + 1, 1);
1954        }
1955        else if (strcmp(action, WARPSCREEN_PREV) == 0) {
1956            WarpToScreen(Scr->screen - 1, -1);
1957        }
1958        else if (strcmp(action, WARPSCREEN_BACK) == 0) {
1959            WarpToScreen(PreviousScreen, 0);
1960        }
1961        else {
1962            WarpToScreen(atoi(action), 0);
1963        }
1964    }
1965        break;
1966
1967    case F_COLORMAP:
1968    {
1969        if (strcmp(action, COLORMAP_NEXT) == 0) {
1970            BumpWindowColormap(tmp_win, 1);
1971        }
1972        else if (strcmp(action, COLORMAP_PREV) == 0) {
1973            BumpWindowColormap(tmp_win, -1);
1974        }
1975        else {
1976            BumpWindowColormap(tmp_win, 0);
1977        }
1978    }
1979        break;
1980
1981    case F_WARPPREV:
1982    case F_WARPNEXT:
1983    {
1984        TwmWindow *t;
1985        TwmWindow *of, *l, *n;
1986        int c = 0;
1987
1988#define wseq(w) (func == F_WARPNEXT ? (w)->next : (w)->prev)
1989#define nwin(w) ((w) && (n=wseq(w)) != NULL && n != &Scr->TwmRoot ? n : l)
1990#define bwin(w) (!(w)||(w)->iconmgr||(w)==of||!(Scr->WarpUnmapped||(w)->mapped))
1991
1992        of = (Scr->Focus ? Scr->Focus : &Scr->TwmRoot);
1993
1994        for (t = Scr->TwmRoot.next; t; t = t->next)
1995            if (!bwin(t))
1996                break;
1997        if (!t)
1998            break;              /* no windows we can use */
1999
2000        if (func == F_WARPPREV)
2001            for (l = of; l->next; l = l->next);
2002        else
2003            l = Scr->TwmRoot.next;
2004
2005        for (t = of; bwin(t) && c < 2; t = nwin(t))
2006            if (t == of)
2007                c++;
2008
2009        if (bwin(t) || c >= 2) {
2010            Bell(XkbBI_MinorError, 0, None);
2011        }
2012        else {
2013            static TwmWindow *savedwarp = NULL;
2014
2015            if (of && of == savedwarp) {
2016                Iconify(of, 0, 0);
2017                savedwarp = NULL;
2018            }
2019            if (!t->mapped)
2020                savedwarp = t;
2021            else
2022                savedwarp = NULL;
2023            WarpThere(t);
2024        }
2025        break;
2026    }
2027
2028    case F_WARPTO:
2029    {
2030        TwmWindow *t;
2031        int len;
2032
2033        len = (int) strlen(action);
2034
2035        for (t = Scr->TwmRoot.next; t != NULL; t = t->next) {
2036            if (!strncmp(action, t->name, (size_t) len))
2037                if (WarpThere(t))
2038                    break;
2039        }
2040        if (!t) {
2041            for (t = Scr->TwmRoot.next; t != NULL; t = t->next) {
2042                if (!strncmp(action, t->xclass.res_name, (size_t) len))
2043                    if (WarpThere(t))
2044                        break;
2045            }
2046            if (!t) {
2047                for (t = Scr->TwmRoot.next; t != NULL; t = t->next) {
2048                    if (!strncmp(action, t->xclass.res_class, (size_t) len))
2049                        if (WarpThere(t))
2050                            break;
2051                }
2052            }
2053        }
2054
2055        if (!t)
2056            Bell(XkbBI_MinorError, 0, None);
2057    }
2058        break;
2059
2060    case F_WARPTOICONMGR:
2061    {
2062        TwmWindow *t;
2063        int len;
2064        Window raisewin = None, iconwin = None;
2065
2066        len = (int) strlen(action);
2067        if (len == 0) {
2068            if (tmp_win && tmp_win->list) {
2069                raisewin = tmp_win->list->iconmgr->twm_win->frame;
2070                iconwin = tmp_win->list->icon;
2071            }
2072            else if (Scr->iconmgr.active) {
2073                raisewin = Scr->iconmgr.twm_win->frame;
2074                iconwin = Scr->iconmgr.active->w;
2075            }
2076        }
2077        else {
2078            for (t = Scr->TwmRoot.next; t != NULL; t = t->next) {
2079                if (strncmp(action, t->icon_name, (size_t) len) == 0) {
2080                    if (t->list && t->list->iconmgr->twm_win->mapped) {
2081                        raisewin = t->list->iconmgr->twm_win->frame;
2082                        iconwin = t->list->icon;
2083                        break;
2084                    }
2085                }
2086            }
2087        }
2088
2089        if (raisewin) {
2090            XRaiseWindow(dpy, raisewin);
2091            XWarpPointer(dpy, None, iconwin, 0, 0, 0, 0, 5, 5);
2092        }
2093        else {
2094            Bell(XkbBI_MinorError, 0, None);
2095        }
2096    }
2097        break;
2098
2099    case F_WARPRING:
2100        switch (action[0]) {
2101        case 'n':
2102            WarpAlongRing(&eventp->xbutton, True);
2103            break;
2104        case 'p':
2105            WarpAlongRing(&eventp->xbutton, False);
2106            break;
2107        default:
2108            Bell(XkbBI_MinorError, 0, None);
2109            break;
2110        }
2111        break;
2112
2113    case F_FILE:
2114        ptr = ExpandFilename(action);
2115        if (ptr) {
2116            fd = open(ptr, O_RDONLY);
2117            if (fd >= 0) {
2118                count = (int) read(fd, buff, MAX_FILE_SIZE - 1);
2119                if (count > 0)
2120                    XStoreBytes(dpy, buff, count);
2121
2122                close(fd);
2123            }
2124            else {
2125                twmWarning("unable to open file \"%s\"", ptr);
2126            }
2127            free(ptr);
2128        }
2129        else {
2130            twmWarning("error expanding filename");
2131        }
2132        break;
2133
2134    case F_REFRESH:
2135    {
2136        XSetWindowAttributes attributes;
2137        unsigned long valuemask;
2138
2139        valuemask = (CWBackPixel | CWBackingStore | CWSaveUnder);
2140        attributes.background_pixel = Scr->Black;
2141        attributes.backing_store = NotUseful;
2142        attributes.save_under = False;
2143        w = XCreateWindow(dpy, Scr->Root, 0, 0,
2144                          (unsigned int) Scr->MyDisplayWidth,
2145                          (unsigned int) Scr->MyDisplayHeight,
2146                          (unsigned int) 0,
2147                          CopyFromParent, (unsigned int) CopyFromParent,
2148                          (Visual *) CopyFromParent, valuemask, &attributes);
2149        XMapWindow(dpy, w);
2150        XDestroyWindow(dpy, w);
2151        XFlush(dpy);
2152    }
2153        break;
2154
2155    case F_WINREFRESH:
2156        if (DeferExecution(context, func, Scr->SelectCursor))
2157            return TRUE;
2158
2159        if (context == C_ICON && tmp_win->icon_w)
2160            w = XCreateSimpleWindow(dpy, tmp_win->icon_w,
2161                                    0, 0, 9999, 9999, 0, Scr->Black,
2162                                    Scr->Black);
2163        else
2164            w = XCreateSimpleWindow(dpy, tmp_win->frame,
2165                                    0, 0, 9999, 9999, 0, Scr->Black,
2166                                    Scr->Black);
2167
2168        XMapWindow(dpy, w);
2169        XDestroyWindow(dpy, w);
2170        XFlush(dpy);
2171        break;
2172
2173    case F_QUIT:
2174        Done(NULL, NULL);
2175        break;
2176
2177    case F_PRIORITY:
2178        if (HasSync) {
2179            if (DeferExecution(context, func, Scr->SelectCursor))
2180                return TRUE;
2181            (void) XSyncSetPriority(dpy, tmp_win->w, atoi(action));
2182        }
2183        break;
2184    case F_STARTWM:
2185        execlp("/bin/sh", "sh", "-c", action, (void *) NULL);
2186        twmWarning("unable to start:  %s", *Argv);
2187        break;
2188
2189    }
2190
2191    if (ButtonPressed == -1)
2192        XUngrabPointer(dpy, CurrentTime);
2193    return do_next_action;
2194}
2195
2196/**
2197 * defer the execution of a function to the next button press if the context
2198 *  is C_ROOT
2199 *
2200 *  \param context the context in which the mouse button was pressed
2201 *      \param func    the function to defer
2202 *  \param cursor  cursor the cursor to display while waiting
2203 */
2204static int
2205DeferExecution(int context, int func, Cursor cursor)
2206{
2207    if (context == C_ROOT) {
2208        LastCursor = cursor;
2209        XGrabPointer(dpy, Scr->Root, True,
2210                     ButtonPressMask | ButtonReleaseMask,
2211                     GrabModeAsync, GrabModeAsync,
2212                     Scr->Root, cursor, CurrentTime);
2213
2214        RootFunction = func;
2215
2216        return (TRUE);
2217    }
2218
2219    return (FALSE);
2220}
2221
2222/**
2223 *regrab the pointer with the LastCursor;
2224 */
2225void
2226ReGrab(void)
2227{
2228    XGrabPointer(dpy, Scr->Root, True,
2229                 ButtonPressMask | ButtonReleaseMask,
2230                 GrabModeAsync, GrabModeAsync,
2231                 Scr->Root, LastCursor, CurrentTime);
2232}
2233
2234/**
2235 * checks each function in the list to see if it is one that needs
2236 * to be deferred.
2237 *
2238 *  \param root the menu root to check
2239 */
2240static Bool
2241NeedToDefer(MenuRoot *root)
2242{
2243    MenuItem *mitem;
2244
2245    for (mitem = root->first; mitem != NULL; mitem = mitem->next) {
2246        switch (mitem->func) {
2247        case F_IDENTIFY:
2248        case F_RESIZE:
2249        case F_MOVE:
2250        case F_FORCEMOVE:
2251        case F_DEICONIFY:
2252        case F_ICONIFY:
2253        case F_RAISELOWER:
2254        case F_RAISE:
2255        case F_LOWER:
2256        case F_FOCUS:
2257        case F_DESTROY:
2258        case F_WINREFRESH:
2259        case F_ZOOM:
2260        case F_FULLZOOM:
2261        case F_HORIZOOM:
2262        case F_RIGHTZOOM:
2263        case F_LEFTZOOM:
2264        case F_TOPZOOM:
2265        case F_BOTTOMZOOM:
2266        case F_AUTORAISE:
2267            return TRUE;
2268        }
2269    }
2270    return FALSE;
2271}
2272
2273/*
2274 * We cannot safely free a value passed to putenv, but we can cache the most
2275 * recent value, reducing the memory leaks.
2276 */
2277#define cache_env(saved) \
2278    if (saved == NULL || strcmp(saved, update)) { \
2279        saved = update; \
2280    } else { \
2281        free(update); \
2282        update = saved; \
2283    }
2284
2285static void
2286Execute(const char *s)
2287{
2288    static const char display_eqls[] = "DISPLAY=";
2289    static char *main_display;
2290    static char *exec_display;
2291
2292    char *ds = DisplayString(dpy);
2293    char *colon;
2294    char *oldDisplay = NULL;
2295    char *value;
2296    Bool restorevar = False;
2297
2298    value = getenv("DISPLAY");
2299    oldDisplay = strdup(value ? value : "");
2300
2301    /*
2302     * Build a display string using the current screen number, so that
2303     * X programs which get fired up from a menu come up on the screen
2304     * that they were invoked from, unless specifically overridden on
2305     * their command line.
2306     */
2307    colon = strrchr(ds, ':');
2308    if (colon) {                /* if host[:]:dpy */
2309        size_t need = sizeof(display_eqls) + strlen(ds) + 10;
2310        char *update = (char *) malloc(need);
2311
2312        if (update != NULL) {
2313            char *dot1;
2314
2315            strcpy(update, display_eqls);
2316            strcat(update, ds);
2317            colon = strrchr(update, ':');
2318            dot1 = strchr(colon, '.');  /* first period after colon */
2319            if (dot1 == NULL)
2320                dot1 = colon + strlen(colon);   /* if not there, append */
2321            (void) sprintf(dot1, ".%d", Scr->screen);
2322            cache_env(exec_display);
2323            if (strcmp(update, oldDisplay)) {
2324                putenv(update);
2325                restorevar = True;
2326            }
2327        }
2328    }
2329
2330    (void) system(s);
2331
2332    if (restorevar) {
2333        size_t need = sizeof(display_eqls) + strlen(oldDisplay);
2334        char *update = (char *) malloc(need);
2335
2336        if (update != NULL) {
2337            strcat(strcpy(update, display_eqls), oldDisplay);
2338            cache_env(main_display);
2339            putenv(update);
2340        }
2341    }
2342    free(oldDisplay);
2343}
2344
2345/**
2346 * put input focus on the root window.
2347 */
2348void
2349FocusOnRoot(void)
2350{
2351    SetFocus((TwmWindow *) NULL, LastTimestamp());
2352    if (Scr->Focus != NULL) {
2353        SetBorder(Scr->Focus, False);
2354        if (Scr->Focus->hilite_w)
2355            XUnmapWindow(dpy, Scr->Focus->hilite_w);
2356    }
2357    InstallWindowColormaps(0, &Scr->TwmRoot);
2358    Scr->Focus = NULL;
2359    Scr->FocusRoot = TRUE;
2360}
2361
2362void
2363DeIconify(TwmWindow *tmp_win)
2364{
2365    TwmWindow *t;
2366
2367    /* de-iconify the main window */
2368    if (tmp_win->icon) {
2369        if (tmp_win->icon_on)
2370            Zoom(tmp_win->icon_w, tmp_win->frame);
2371        else if (tmp_win->group != (Window) 0) {
2372            for (t = Scr->TwmRoot.next; t != NULL; t = t->next) {
2373                if (tmp_win->group == t->w && t->icon_on) {
2374                    Zoom(t->icon_w, tmp_win->frame);
2375                    break;
2376                }
2377            }
2378        }
2379    }
2380
2381    XMapWindow(dpy, tmp_win->w);
2382    tmp_win->mapped = TRUE;
2383    if (Scr->NoRaiseDeicon)
2384        XMapWindow(dpy, tmp_win->frame);
2385    else
2386        XMapRaised(dpy, tmp_win->frame);
2387    SetMapStateProp(tmp_win, NormalState);
2388
2389    if (tmp_win->icon_w) {
2390        XUnmapWindow(dpy, tmp_win->icon_w);
2391        IconDown(tmp_win);
2392    }
2393    if (tmp_win->list)
2394        XUnmapWindow(dpy, tmp_win->list->icon);
2395    if ((Scr->WarpCursor ||
2396         LookInList(Scr->WarpCursorL, tmp_win->full_name, &tmp_win->xclass)) &&
2397        tmp_win->icon)
2398        WarpToWindow(tmp_win);
2399    tmp_win->icon = FALSE;
2400    tmp_win->icon_on = FALSE;
2401
2402    /* now de-iconify transients */
2403    for (t = Scr->TwmRoot.next; t != NULL; t = t->next) {
2404        if (t->transient && t->transientfor == tmp_win->w) {
2405            if (t->icon_on)
2406                Zoom(t->icon_w, t->frame);
2407            else
2408                Zoom(tmp_win->icon_w, t->frame);
2409
2410            XMapWindow(dpy, t->w);
2411            t->mapped = TRUE;
2412            if (Scr->NoRaiseDeicon)
2413                XMapWindow(dpy, t->frame);
2414            else
2415                XMapRaised(dpy, t->frame);
2416            SetMapStateProp(t, NormalState);
2417
2418            if (t->icon_w) {
2419                XUnmapWindow(dpy, t->icon_w);
2420                IconDown(t);
2421            }
2422            if (t->list)
2423                XUnmapWindow(dpy, t->list->icon);
2424            t->icon = FALSE;
2425            t->icon_on = FALSE;
2426        }
2427    }
2428
2429    XSync(dpy, 0);
2430}
2431
2432void
2433Iconify(TwmWindow *tmp_win, int def_x, int def_y)
2434{
2435    TwmWindow *t;
2436    int iconify;
2437    XWindowAttributes winattrs;
2438    unsigned long eventMask;
2439
2440    iconify = ((!tmp_win->iconify_by_unmapping) || tmp_win->transient);
2441    if (iconify) {
2442        if (tmp_win->icon_w == (Window) 0)
2443            CreateIconWindow(tmp_win, def_x, def_y);
2444        else
2445            IconUp(tmp_win);
2446        XMapRaised(dpy, tmp_win->icon_w);
2447    }
2448    if (tmp_win->list)
2449        XMapWindow(dpy, tmp_win->list->icon);
2450
2451    XGetWindowAttributes(dpy, tmp_win->w, &winattrs);
2452    eventMask = (unsigned long) winattrs.your_event_mask;
2453
2454    /* iconify transients first */
2455    for (t = Scr->TwmRoot.next; t != NULL; t = t->next) {
2456        if (t->transient && t->transientfor == tmp_win->w) {
2457            if (iconify) {
2458                if (t->icon_on)
2459                    Zoom(t->icon_w, tmp_win->icon_w);
2460                else
2461                    Zoom(t->frame, tmp_win->icon_w);
2462            }
2463
2464            /*
2465             * Prevent the receipt of an UnmapNotify, since that would
2466             * cause a transition to the Withdrawn state.
2467             */
2468            t->mapped = FALSE;
2469            XSelectInput(dpy, t->w,
2470                         (long) (eventMask &
2471                                 (unsigned long) (~StructureNotifyMask)));
2472            XUnmapWindow(dpy, t->w);
2473            XSelectInput(dpy, t->w, (long) eventMask);
2474            XUnmapWindow(dpy, t->frame);
2475            if (t->icon_w)
2476                XUnmapWindow(dpy, t->icon_w);
2477            SetMapStateProp(t, IconicState);
2478            SetBorder(t, False);
2479            if (t == Scr->Focus) {
2480                SetFocus((TwmWindow *) NULL, LastTimestamp());
2481                Scr->Focus = NULL;
2482                Scr->FocusRoot = TRUE;
2483            }
2484            if (t->list)
2485                XMapWindow(dpy, t->list->icon);
2486            t->icon = TRUE;
2487            t->icon_on = FALSE;
2488        }
2489    }
2490
2491    if (iconify)
2492        Zoom(tmp_win->frame, tmp_win->icon_w);
2493
2494    /*
2495     * Prevent the receipt of an UnmapNotify, since that would
2496     * cause a transition to the Withdrawn state.
2497     */
2498    tmp_win->mapped = FALSE;
2499    XSelectInput(dpy, tmp_win->w,
2500                 (long) (eventMask & (unsigned long) (~StructureNotifyMask)));
2501    XUnmapWindow(dpy, tmp_win->w);
2502    XSelectInput(dpy, tmp_win->w, (long) eventMask);
2503    XUnmapWindow(dpy, tmp_win->frame);
2504    SetMapStateProp(tmp_win, IconicState);
2505
2506    SetBorder(tmp_win, False);
2507    if (tmp_win == Scr->Focus) {
2508        SetFocus((TwmWindow *) NULL, LastTimestamp());
2509        Scr->Focus = NULL;
2510        Scr->FocusRoot = TRUE;
2511    }
2512    tmp_win->icon = TRUE;
2513    if (iconify)
2514        tmp_win->icon_on = TRUE;
2515    else
2516        tmp_win->icon_on = FALSE;
2517    XSync(dpy, 0);
2518}
2519
2520static void
2521Identify(TwmWindow *t)
2522{
2523    int i, n, width, height;
2524    int x, y;
2525    unsigned int wwidth, wheight, bw, depth;
2526    Window junk;
2527    int px, py, dummy;
2528    unsigned udummy;
2529    Window wdummy = None;
2530
2531    n = 0;
2532    snprintf(Info[n++], INFO_SIZE, "Twm version:  %s, Release %s",
2533             XVENDORNAME, APP_VERSION);
2534    Info[n++][0] = '\0';
2535
2536    if (t) {
2537        XGetGeometry(dpy, t->w, &wdummy, &dummy, &dummy,
2538                     &wwidth, &wheight, &bw, &depth);
2539        (void) XTranslateCoordinates(dpy, t->w, Scr->Root, 0, 0, &x, &y, &junk);
2540        snprintf(Info[n++], INFO_SIZE,
2541                 "Name             = \"%s\"", t->full_name);
2542        snprintf(Info[n++], INFO_SIZE,
2543                 "Class.res_name   = \"%s\"", t->xclass.res_name);
2544        snprintf(Info[n++], INFO_SIZE,
2545                 "Class.res_class  = \"%s\"", t->xclass.res_class);
2546        Info[n++][0] = '\0';
2547        snprintf(Info[n++], INFO_SIZE,
2548                 "Geometry/root    = %ux%u+%d+%d", wwidth, wheight, x, y);
2549        snprintf(Info[n++], INFO_SIZE, "Border width     = %u", bw);
2550        snprintf(Info[n++], INFO_SIZE, "Depth            = %u", depth);
2551        if (HasSync) {
2552            int priority;
2553
2554            (void) XSyncGetPriority(dpy, t->w, &priority);
2555            snprintf(Info[n++], INFO_SIZE, "Priority         = %d", priority);
2556        }
2557    }
2558
2559    Info[n++][0] = '\0';
2560    snprintf(Info[n++], INFO_SIZE, "Click to dismiss....");
2561
2562    /* figure out the width and height of the info window */
2563    height = n * (Scr->DefaultFont.height + 2);
2564    width = 1;
2565    for (i = 0; i < n; i++) {
2566        int twidth = MyFont_TextWidth(&Scr->DefaultFont, Info[i],
2567                                      (int) strlen(Info[i]));
2568
2569        if (twidth > width)
2570            width = twidth;
2571    }
2572    if (InfoLines)
2573        XUnmapWindow(dpy, Scr->InfoWindow);
2574
2575    width += 10;                /* some padding */
2576    if (XQueryPointer(dpy, Scr->Root, &wdummy, &wdummy, &px, &py,
2577                      &dummy, &dummy, &udummy)) {
2578        px -= (width / 2);
2579        py -= (height / 3);
2580        if (px + width + BW2 >= Scr->MyDisplayWidth)
2581            px = Scr->MyDisplayWidth - width - BW2;
2582        if (py + height + BW2 >= Scr->MyDisplayHeight)
2583            py = Scr->MyDisplayHeight - height - BW2;
2584        if (px < 0)
2585            px = 0;
2586        if (py < 0)
2587            py = 0;
2588    }
2589    else {
2590        px = py = 0;
2591    }
2592    XMoveResizeWindow(dpy, Scr->InfoWindow, px, py, (unsigned) width,
2593                      (unsigned) height);
2594    XMapRaised(dpy, Scr->InfoWindow);
2595    InfoLines = n;
2596}
2597
2598void
2599SetMapStateProp(TwmWindow *tmp_win, int state)
2600{
2601    unsigned long data[2];      /* "suggested" by ICCCM version 1 */
2602
2603    data[0] = (unsigned long) state;
2604    data[1] = (unsigned long) (tmp_win->iconify_by_unmapping ? None :
2605                               tmp_win->icon_w);
2606
2607    XChangeProperty(dpy, tmp_win->w, _XA_WM_STATE, _XA_WM_STATE, 32,
2608                    PropModeReplace, (unsigned char *) data, 2);
2609}
2610
2611Bool
2612GetWMState(Window w, int *statep, Window *iwp)
2613{
2614    Atom actual_type;
2615    int actual_format;
2616    unsigned long nitems, bytesafter;
2617    unsigned char *prop_return = NULL;
2618    Bool retval = False;
2619
2620    if (XGetWindowProperty(dpy, w, _XA_WM_STATE, 0L, 2L, False, _XA_WM_STATE,
2621                           &actual_type, &actual_format, &nitems, &bytesafter,
2622                           &prop_return) != Success || !prop_return)
2623        return False;
2624
2625    if (nitems <= 2) {          /* "suggested" by ICCCM version 1 */
2626        unsigned long *datap = (unsigned long *) prop_return;
2627
2628        *statep = (int) datap[0];
2629        *iwp = (Window) datap[1];
2630        retval = True;
2631    }
2632
2633    XFree(prop_return);
2634    return retval;
2635}
2636
2637void
2638WarpToScreen(int n, int inc)
2639{
2640    Window dumwin;
2641    int x, y, dumint;
2642    unsigned int dummask;
2643    ScreenInfo *newscr = NULL;
2644
2645    while (!newscr) {
2646        /* wrap around */
2647        if (n < 0)
2648            n = NumScreens - 1;
2649        else if (n >= NumScreens)
2650            n = 0;
2651
2652        newscr = ScreenList[n];
2653        if (!newscr) {          /* make sure screen is managed */
2654            if (inc) {          /* walk around the list */
2655                n += inc;
2656                continue;
2657            }
2658            twmWarning("unable to warp to unmanaged screen %d", n);
2659            Bell(XkbBI_MinorError, 0, None);
2660            return;
2661        }
2662    }
2663
2664    if (Scr->screen == n)
2665        return;                 /* already on that screen */
2666
2667    PreviousScreen = Scr->screen;
2668    XQueryPointer(dpy, Scr->Root, &dumwin, &dumwin, &x, &y,
2669                  &dumint, &dumint, &dummask);
2670
2671    XWarpPointer(dpy, None, newscr->Root, 0, 0, 0, 0, x, y);
2672    return;
2673}
2674
2675/**
2676 * rotate our internal copy of WM_COLORMAP_WINDOWS
2677 */
2678static void
2679BumpWindowColormap(TwmWindow *tmp, int inc)
2680{
2681    ColormapWindow **cwins;
2682
2683    if (!tmp)
2684        return;
2685
2686    if (inc && tmp->cmaps.number_cwins > 0) {
2687        cwins = (ColormapWindow **)
2688            malloc(sizeof(ColormapWindow *) * (size_t) tmp->cmaps.number_cwins);
2689        if (cwins) {
2690            int i;
2691            int previously_installed;
2692
2693            /* SUPPRESS 560 */
2694            if ((previously_installed = (Scr->cmapInfo.cmaps == &tmp->cmaps &&
2695                                         tmp->cmaps.number_cwins))) {
2696                for (i = tmp->cmaps.number_cwins; i-- > 0;)
2697                    tmp->cmaps.cwins[i]->colormap->state = 0;
2698            }
2699
2700            for (i = 0; i < tmp->cmaps.number_cwins; i++) {
2701                int j = i - inc;
2702
2703                if (j >= tmp->cmaps.number_cwins)
2704                    j -= tmp->cmaps.number_cwins;
2705                else if (j < 0)
2706                    j += tmp->cmaps.number_cwins;
2707                cwins[j] = tmp->cmaps.cwins[i];
2708            }
2709
2710            free(tmp->cmaps.cwins);
2711
2712            tmp->cmaps.cwins = cwins;
2713
2714            if (tmp->cmaps.number_cwins > 1)
2715                bzero(tmp->cmaps.scoreboard,
2716                      ColormapsScoreboardLength(&tmp->cmaps));
2717
2718            if (previously_installed)
2719                InstallWindowColormaps(PropertyNotify, (TwmWindow *) NULL);
2720        }
2721    }
2722    else
2723        FetchWmColormapWindows(tmp);
2724}
2725
2726static void
2727HideIconManager(void)
2728{
2729    SetMapStateProp(Scr->iconmgr.twm_win, WithdrawnState);
2730    XUnmapWindow(dpy, Scr->iconmgr.twm_win->frame);
2731    if (Scr->iconmgr.twm_win->icon_w)
2732        XUnmapWindow(dpy, Scr->iconmgr.twm_win->icon_w);
2733    Scr->iconmgr.twm_win->mapped = FALSE;
2734    Scr->iconmgr.twm_win->icon = TRUE;
2735}
2736
2737void
2738SetBorder(TwmWindow *tmp, Bool onoroff)
2739{
2740    if (tmp->highlight) {
2741        if (onoroff) {
2742            XSetWindowBorder(dpy, tmp->frame, tmp->border);
2743            if (tmp->title_w)
2744                XSetWindowBorder(dpy, tmp->title_w, tmp->border);
2745        }
2746        else {
2747            XSetWindowBorderPixmap(dpy, tmp->frame, tmp->gray);
2748            if (tmp->title_w)
2749                XSetWindowBorderPixmap(dpy, tmp->title_w, tmp->gray);
2750        }
2751    }
2752}
2753
2754static void
2755DestroyMenu(MenuRoot *menu)
2756{
2757    MenuItem *item;
2758
2759    if (menu->w) {
2760        XDeleteContext(dpy, menu->w, MenuContext);
2761        XDeleteContext(dpy, menu->w, ScreenContext);
2762        if (Scr->Shadow)
2763            XDestroyWindow(dpy, menu->shadow);
2764        XDestroyWindow(dpy, menu->w);
2765    }
2766
2767    for (item = menu->first; item;) {
2768        MenuItem *tmp = item;
2769
2770        item = item->next;
2771        free(tmp);
2772    }
2773}
2774
2775/*
2776 * warping routines
2777 */
2778
2779static void
2780WarpAlongRing(XButtonEvent *ev, Bool forward)
2781{
2782    TwmWindow *r, *head;
2783
2784    if (Scr->RingLeader)
2785        head = Scr->RingLeader;
2786    else if (!(head = Scr->Ring))
2787        return;
2788
2789    if (forward) {
2790        for (r = head->ring.next; r != head; r = r->ring.next) {
2791            if (!r || r->mapped)
2792                break;
2793        }
2794    }
2795    else {
2796        for (r = head->ring.prev; r != head; r = r->ring.prev) {
2797            if (!r || r->mapped)
2798                break;
2799        }
2800    }
2801
2802    if (r && r != head) {
2803        TwmWindow *p = Scr->RingLeader, *t;
2804        XPointer context_data;
2805
2806        Scr->RingLeader = r;
2807        WarpToWindow(r);
2808
2809        if (XFindContext(dpy, ev->window, TwmContext, &context_data) == 0)
2810            t = (TwmWindow *) context_data;
2811        else
2812            t = NULL;
2813
2814        if (p && p->mapped && p == t) {
2815            p->ring.cursor_valid = True;
2816            p->ring.curs_x = ev->x_root - t->frame_x;
2817            p->ring.curs_y = ev->y_root - t->frame_y;
2818            if (p->ring.curs_x < -p->frame_bw ||
2819                p->ring.curs_x >= p->frame_width + p->frame_bw ||
2820                p->ring.curs_y < -p->frame_bw ||
2821                p->ring.curs_y >= p->frame_height + p->frame_bw) {
2822                /* somehow out of window */
2823                p->ring.curs_x = p->frame_width / 2;
2824                p->ring.curs_y = p->frame_height / 2;
2825            }
2826        }
2827    }
2828}
2829
2830static void
2831WarpToWindow(TwmWindow *t)
2832{
2833    int x, y;
2834
2835    if (t->auto_raise || !Scr->NoRaiseWarp)
2836        AutoRaiseWindow(t);
2837    if (t->ring.cursor_valid) {
2838        x = t->ring.curs_x;
2839        y = t->ring.curs_y;
2840    }
2841    else {
2842        x = t->frame_width / 2;
2843        y = t->frame_height / 2;
2844    }
2845    XWarpPointer(dpy, None, t->frame, 0, 0, 0, 0, x, y);
2846}
2847
2848/*
2849 * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
2850 * client messages will have the following form:
2851 *
2852 *     event type       ClientMessage
2853 *     message type     _XA_WM_PROTOCOLS
2854 *     window           tmp->w
2855 *     format           32
2856 *     data[0]          message atom
2857 *     data[1]          time stamp
2858 */
2859static void
2860send_clientmessage(Window w, Atom a, Time timestamp)
2861{
2862    XClientMessageEvent ev;
2863    memset(&ev, '\0', sizeof(ev));
2864
2865    ev.type = ClientMessage;
2866    ev.window = w;
2867    ev.message_type = _XA_WM_PROTOCOLS;
2868    ev.format = 32;
2869    ev.data.l[0] = (long) a;
2870    ev.data.l[1] = (long) timestamp;
2871    XSendEvent(dpy, w, False, 0L, (XEvent *) &ev);
2872}
2873
2874void
2875SendDeleteWindowMessage(TwmWindow *tmp, Time timestamp)
2876{
2877    send_clientmessage(tmp->w, _XA_WM_DELETE_WINDOW, timestamp);
2878}
2879
2880void
2881SendSaveYourselfMessage(TwmWindow *tmp, Time timestamp)
2882{
2883    send_clientmessage(tmp->w, _XA_WM_SAVE_YOURSELF, timestamp);
2884}
2885
2886void
2887SendTakeFocusMessage(TwmWindow *tmp, Time timestamp)
2888{
2889    send_clientmessage(tmp->w, _XA_WM_TAKE_FOCUS, timestamp);
2890}
2891