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