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