menus.c revision 4a1eb5db
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		    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	if (ptr) {
2175	    fd = open(ptr, O_RDONLY);
2176	    if (fd >= 0)
2177	    {
2178		count = read(fd, buff, MAX_FILE_SIZE - 1);
2179		if (count > 0)
2180		    XStoreBytes(dpy, buff, count);
2181
2182		close(fd);
2183	    }
2184	    else
2185	    {
2186		fprintf (stderr, "%s:  unable to open file \"%s\"\n",
2187			 ProgramName, ptr);
2188	    }
2189	    free(ptr);
2190	} else {
2191	    fprintf (stderr, "%s: error expanding filename\n", ProgramName);
2192	}
2193	break;
2194
2195    case F_REFRESH:
2196	{
2197	    XSetWindowAttributes attributes;
2198	    unsigned long valuemask;
2199
2200	    valuemask = (CWBackPixel | CWBackingStore | CWSaveUnder);
2201	    attributes.background_pixel = Scr->Black;
2202	    attributes.backing_store = NotUseful;
2203	    attributes.save_under = False;
2204	    w = XCreateWindow (dpy, Scr->Root, 0, 0,
2205			       (unsigned int) Scr->MyDisplayWidth,
2206			       (unsigned int) Scr->MyDisplayHeight,
2207			       (unsigned int) 0,
2208			       CopyFromParent, (unsigned int) CopyFromParent,
2209			       (Visual *) CopyFromParent, valuemask,
2210			       &attributes);
2211	    XMapWindow (dpy, w);
2212	    XDestroyWindow (dpy, w);
2213	    XFlush (dpy);
2214	}
2215	break;
2216
2217    case F_WINREFRESH:
2218	if (DeferExecution(context, func, Scr->SelectCursor))
2219	    return TRUE;
2220
2221	if (context == C_ICON && tmp_win->icon_w)
2222	    w = XCreateSimpleWindow(dpy, tmp_win->icon_w,
2223		0, 0, 9999, 9999, 0, Scr->Black, Scr->Black);
2224	else
2225	    w = XCreateSimpleWindow(dpy, tmp_win->frame,
2226		0, 0, 9999, 9999, 0, Scr->Black, Scr->Black);
2227
2228	XMapWindow(dpy, w);
2229	XDestroyWindow(dpy, w);
2230	XFlush(dpy);
2231	break;
2232
2233    case F_QUIT:
2234	Done(NULL, NULL);
2235	break;
2236
2237    case F_PRIORITY:
2238	if (HasSync)
2239	{
2240	    if (DeferExecution (context, func, Scr->SelectCursor))
2241		return TRUE;
2242	    (void)XSyncSetPriority(dpy, tmp_win->w, atoi(action));
2243        }
2244	break;
2245   case F_STARTWM:
2246	execlp("/bin/sh", "sh", "-c", action, (void *)NULL);
2247	fprintf (stderr, "%s:  unable to start:  %s\n", ProgramName, *Argv);
2248	break;
2249
2250    }
2251
2252    if (ButtonPressed == -1) XUngrabPointer(dpy, CurrentTime);
2253    return do_next_action;
2254}
2255
2256
2257
2258/**
2259 * defer the execution of a function to the next button press if the context
2260 *  is C_ROOT
2261 *
2262 *  \param context the context in which the mouse button was pressed
2263 *	\param func    the function to defer
2264 *  \param cursor  cursor the cursor to display while waiting
2265 */
2266static int
2267DeferExecution(int context, int func, Cursor cursor)
2268{
2269  if (context == C_ROOT)
2270    {
2271	LastCursor = cursor;
2272	XGrabPointer(dpy, Scr->Root, True,
2273	    ButtonPressMask | ButtonReleaseMask,
2274	    GrabModeAsync, GrabModeAsync,
2275	    Scr->Root, cursor, CurrentTime);
2276
2277	RootFunction = func;
2278
2279	return (TRUE);
2280    }
2281
2282    return (FALSE);
2283}
2284
2285
2286
2287/**
2288 *regrab the pointer with the LastCursor;
2289 */
2290void
2291ReGrab(void)
2292{
2293    XGrabPointer(dpy, Scr->Root, True,
2294	ButtonPressMask | ButtonReleaseMask,
2295	GrabModeAsync, GrabModeAsync,
2296	Scr->Root, LastCursor, CurrentTime);
2297}
2298
2299
2300
2301/**
2302 * checks each function in the list to see if it is one that needs
2303 * to be deferred.
2304 *
2305 *  \param root the menu root to check
2306 */
2307static Bool
2308NeedToDefer(MenuRoot *root)
2309{
2310    MenuItem *mitem;
2311
2312    for (mitem = root->first; mitem != NULL; mitem = mitem->next)
2313    {
2314	switch (mitem->func)
2315	{
2316	case F_IDENTIFY:
2317	case F_RESIZE:
2318	case F_MOVE:
2319	case F_FORCEMOVE:
2320	case F_DEICONIFY:
2321	case F_ICONIFY:
2322	case F_RAISELOWER:
2323	case F_RAISE:
2324	case F_LOWER:
2325	case F_FOCUS:
2326	case F_DESTROY:
2327	case F_WINREFRESH:
2328	case F_ZOOM:
2329	case F_FULLZOOM:
2330	case F_HORIZOOM:
2331        case F_RIGHTZOOM:
2332        case F_LEFTZOOM:
2333        case F_TOPZOOM:
2334        case F_BOTTOMZOOM:
2335	case F_AUTORAISE:
2336	    return TRUE;
2337	}
2338    }
2339    return FALSE;
2340}
2341
2342
2343
2344static void
2345Execute(const char *s)
2346{
2347	/* FIXME: is all this stuff needed?  There could be security problems here. */
2348    static char buf[256];
2349    char *ds = DisplayString (dpy);
2350    char *colon, *dot1;
2351    char oldDisplay[256];
2352    char *doisplay;
2353    int restorevar = 0;
2354
2355    oldDisplay[0] = '\0';
2356    doisplay=getenv("DISPLAY");
2357    if (doisplay)
2358	strcpy (oldDisplay, doisplay);
2359
2360    /*
2361     * Build a display string using the current screen number, so that
2362     * X programs which get fired up from a menu come up on the screen
2363     * that they were invoked from, unless specifically overridden on
2364     * their command line.
2365     */
2366    colon = strrchr (ds, ':');
2367    if (colon) {			/* if host[:]:dpy */
2368	strcpy (buf, "DISPLAY=");
2369	strcat (buf, ds);
2370	colon = buf + 8 + (colon - ds);	/* use version in buf */
2371	dot1 = strchr (colon, '.');	/* first period after colon */
2372	if (!dot1) dot1 = colon + strlen (colon);  /* if not there, append */
2373	(void) sprintf (dot1, ".%d", Scr->screen);
2374	putenv (buf);
2375	restorevar = 1;
2376    }
2377
2378    (void) system (s);
2379
2380    if (restorevar) {		/* why bother? */
2381	(void) snprintf (buf, sizeof(buf), "DISPLAY=%s", oldDisplay);
2382	putenv (buf);
2383    }
2384}
2385
2386
2387
2388/**
2389 * put input focus on the root window.
2390 */
2391void
2392FocusOnRoot(void)
2393{
2394    SetFocus ((TwmWindow *) NULL, LastTimestamp());
2395    if (Scr->Focus != NULL)
2396    {
2397	SetBorder (Scr->Focus, False);
2398	if (Scr->Focus->hilite_w) XUnmapWindow (dpy, Scr->Focus->hilite_w);
2399    }
2400    InstallWindowColormaps(0, &Scr->TwmRoot);
2401    Scr->Focus = NULL;
2402    Scr->FocusRoot = TRUE;
2403}
2404
2405void
2406DeIconify(TwmWindow *tmp_win)
2407{
2408    TwmWindow *t;
2409
2410    /* de-iconify the main window */
2411    if (tmp_win->icon)
2412    {
2413	if (tmp_win->icon_on)
2414	    Zoom(tmp_win->icon_w, tmp_win->frame);
2415	else if (tmp_win->group != (Window) 0)
2416	{
2417	    for (t = Scr->TwmRoot.next; t != NULL; t = t->next)
2418	    {
2419		if (tmp_win->group == t->w && t->icon_on)
2420		{
2421		    Zoom(t->icon_w, tmp_win->frame);
2422		    break;
2423		}
2424	    }
2425	}
2426    }
2427
2428    XMapWindow(dpy, tmp_win->w);
2429    tmp_win->mapped = TRUE;
2430    if (Scr->NoRaiseDeicon)
2431	XMapWindow(dpy, tmp_win->frame);
2432    else
2433	XMapRaised(dpy, tmp_win->frame);
2434    SetMapStateProp(tmp_win, NormalState);
2435
2436    if (tmp_win->icon_w) {
2437	XUnmapWindow(dpy, tmp_win->icon_w);
2438	IconDown (tmp_win);
2439    }
2440    if (tmp_win->list)
2441	XUnmapWindow(dpy, tmp_win->list->icon);
2442    if ((Scr->WarpCursor ||
2443	 LookInList(Scr->WarpCursorL, tmp_win->full_name, &tmp_win->class)) &&
2444	tmp_win->icon)
2445      WarpToWindow (tmp_win);
2446    tmp_win->icon = FALSE;
2447    tmp_win->icon_on = FALSE;
2448
2449
2450    /* now de-iconify transients */
2451	for (t = Scr->TwmRoot.next; t != NULL; t = t->next)
2452	{
2453	  if (t->transient && t->transientfor == tmp_win->w)
2454	    {
2455	      if (t->icon_on)
2456		Zoom(t->icon_w, t->frame);
2457	      else
2458		Zoom(tmp_win->icon_w, t->frame);
2459
2460	      XMapWindow(dpy, t->w);
2461	      t->mapped = TRUE;
2462	      if (Scr->NoRaiseDeicon)
2463		XMapWindow(dpy, t->frame);
2464	      else
2465		XMapRaised(dpy, t->frame);
2466	      SetMapStateProp(t, NormalState);
2467
2468	      if (t->icon_w) {
2469		XUnmapWindow(dpy, t->icon_w);
2470		IconDown (t);
2471	      }
2472	      if (t->list) XUnmapWindow(dpy, t->list->icon);
2473	      t->icon = FALSE;
2474	      t->icon_on = FALSE;
2475	    }
2476	}
2477
2478    XSync (dpy, 0);
2479}
2480
2481
2482
2483void
2484Iconify(TwmWindow *tmp_win, int def_x, int def_y)
2485{
2486    TwmWindow *t;
2487    int iconify;
2488    XWindowAttributes winattrs;
2489    unsigned long eventMask;
2490
2491    iconify = ((!tmp_win->iconify_by_unmapping) || tmp_win->transient);
2492    if (iconify)
2493    {
2494	if (tmp_win->icon_w == (Window) 0)
2495	    CreateIconWindow(tmp_win, def_x, def_y);
2496	else
2497	    IconUp(tmp_win);
2498	XMapRaised(dpy, tmp_win->icon_w);
2499    }
2500    if (tmp_win->list)
2501	XMapWindow(dpy, tmp_win->list->icon);
2502
2503    XGetWindowAttributes(dpy, tmp_win->w, &winattrs);
2504    eventMask = winattrs.your_event_mask;
2505
2506    /* iconify transients first */
2507    for (t = Scr->TwmRoot.next; t != NULL; t = t->next)
2508      {
2509	if (t->transient && t->transientfor == tmp_win->w)
2510	  {
2511	    if (iconify)
2512	      {
2513		if (t->icon_on)
2514			Zoom(t->icon_w, tmp_win->icon_w);
2515		else
2516		  Zoom(t->frame, tmp_win->icon_w);
2517	      }
2518
2519	    /*
2520	     * Prevent the receipt of an UnmapNotify, since that would
2521	     * cause a transition to the Withdrawn state.
2522	     */
2523	    t->mapped = FALSE;
2524	    XSelectInput(dpy, t->w, eventMask & ~StructureNotifyMask);
2525	    XUnmapWindow(dpy, t->w);
2526	    XSelectInput(dpy, t->w, eventMask);
2527	    XUnmapWindow(dpy, t->frame);
2528	    if (t->icon_w)
2529	      XUnmapWindow(dpy, t->icon_w);
2530	    SetMapStateProp(t, IconicState);
2531	    SetBorder (t, False);
2532	    if (t == Scr->Focus)
2533	      {
2534		SetFocus ((TwmWindow *) NULL, LastTimestamp());
2535		Scr->Focus = NULL;
2536		Scr->FocusRoot = TRUE;
2537	      }
2538	    if (t->list) XMapWindow(dpy, t->list->icon);
2539	    t->icon = TRUE;
2540	    t->icon_on = FALSE;
2541	  }
2542      }
2543
2544    if (iconify)
2545	Zoom(tmp_win->frame, tmp_win->icon_w);
2546
2547    /*
2548     * Prevent the receipt of an UnmapNotify, since that would
2549     * cause a transition to the Withdrawn state.
2550     */
2551    tmp_win->mapped = FALSE;
2552    XSelectInput(dpy, tmp_win->w, eventMask & ~StructureNotifyMask);
2553    XUnmapWindow(dpy, tmp_win->w);
2554    XSelectInput(dpy, tmp_win->w, eventMask);
2555    XUnmapWindow(dpy, tmp_win->frame);
2556    SetMapStateProp(tmp_win, IconicState);
2557
2558    SetBorder (tmp_win, False);
2559    if (tmp_win == Scr->Focus)
2560    {
2561	SetFocus ((TwmWindow *) NULL, LastTimestamp());
2562	Scr->Focus = NULL;
2563	Scr->FocusRoot = TRUE;
2564    }
2565    tmp_win->icon = TRUE;
2566    if (iconify)
2567	tmp_win->icon_on = TRUE;
2568    else
2569	tmp_win->icon_on = FALSE;
2570    XSync (dpy, 0);
2571}
2572
2573
2574
2575static void
2576Identify (TwmWindow *t)
2577{
2578    int i, n, twidth, width, height;
2579    int x, y;
2580    unsigned int wwidth, wheight, bw, depth;
2581    Window junk;
2582    int px, py, dummy;
2583    unsigned udummy;
2584
2585    n = 0;
2586    snprintf(Info[n++], INFO_SIZE, "Twm version:  %s", Version);
2587    Info[n++][0] = '\0';
2588
2589    if (t) {
2590	XGetGeometry (dpy, t->w, &JunkRoot, &JunkX, &JunkY,
2591		      &wwidth, &wheight, &bw, &depth);
2592	(void) XTranslateCoordinates (dpy, t->w, Scr->Root, 0, 0,
2593				      &x, &y, &junk);
2594	snprintf(Info[n++], INFO_SIZE,
2595		 "Name             = \"%s\"", t->full_name);
2596	snprintf(Info[n++], INFO_SIZE,
2597		 "Class.res_name   = \"%s\"", t->class.res_name);
2598	snprintf(Info[n++], INFO_SIZE,
2599		 "Class.res_class  = \"%s\"", t->class.res_class);
2600	Info[n++][0] = '\0';
2601	snprintf(Info[n++], INFO_SIZE,
2602		 "Geometry/root    = %dx%d+%d+%d", wwidth, wheight, x, y);
2603	snprintf(Info[n++], INFO_SIZE, "Border width     = %d", bw);
2604	snprintf(Info[n++], INFO_SIZE, "Depth            = %d", depth);
2605	if (HasSync)
2606	{
2607	    int priority;
2608	    (void)XSyncGetPriority(dpy, t->w, &priority);
2609	    snprintf(Info[n++], INFO_SIZE, "Priority         = %d", priority);
2610	}
2611    }
2612
2613    Info[n++][0] = '\0';
2614    snprintf(Info[n++], INFO_SIZE, "Click to dismiss....");
2615
2616    /* figure out the width and height of the info window */
2617    height = n * (Scr->DefaultFont.height+2);
2618    width = 1;
2619    for (i = 0; i < n; i++)
2620    {
2621	twidth = MyFont_TextWidth(&Scr->DefaultFont, Info[i],
2622	    strlen(Info[i]));
2623	if (twidth > width)
2624	    width = twidth;
2625    }
2626    if (InfoLines) XUnmapWindow(dpy, Scr->InfoWindow);
2627
2628    width += 10;		/* some padding */
2629    if (XQueryPointer (dpy, Scr->Root, &JunkRoot, &JunkChild, &px, &py,
2630		       &dummy, &dummy, &udummy)) {
2631	px -= (width / 2);
2632	py -= (height / 3);
2633	if (px + width + BW2 >= Scr->MyDisplayWidth)
2634	  px = Scr->MyDisplayWidth - width - BW2;
2635	if (py + height + BW2 >= Scr->MyDisplayHeight)
2636	  py = Scr->MyDisplayHeight - height - BW2;
2637	if (px < 0) px = 0;
2638	if (py < 0) py = 0;
2639    } else {
2640	px = py = 0;
2641    }
2642    XMoveResizeWindow(dpy, Scr->InfoWindow, px, py, width, height);
2643    XMapRaised(dpy, Scr->InfoWindow);
2644    InfoLines = n;
2645}
2646
2647
2648void
2649SetMapStateProp(TwmWindow *tmp_win, int state)
2650{
2651    unsigned long data[2];		/* "suggested" by ICCCM version 1 */
2652
2653    data[0] = (unsigned long) state;
2654    data[1] = (unsigned long) (tmp_win->iconify_by_unmapping ? None :
2655			   tmp_win->icon_w);
2656
2657    XChangeProperty (dpy, tmp_win->w, _XA_WM_STATE, _XA_WM_STATE, 32,
2658		 PropModeReplace, (unsigned char *) data, 2);
2659}
2660
2661
2662
2663Bool
2664GetWMState (Window w, int *statep, Window *iwp)
2665{
2666    Atom actual_type;
2667    int actual_format;
2668    unsigned long nitems, bytesafter;
2669    unsigned char *prop_return = NULL;
2670    Bool retval = False;
2671
2672    if (XGetWindowProperty (dpy, w, _XA_WM_STATE, 0L, 2L, False, _XA_WM_STATE,
2673			    &actual_type, &actual_format, &nitems, &bytesafter,
2674			    &prop_return) != Success || !prop_return)
2675      return False;
2676
2677    if (nitems <= 2) {			/* "suggested" by ICCCM version 1 */
2678	unsigned long *datap = (unsigned long *) prop_return;
2679	*statep = (int) datap[0];
2680	*iwp = (Window) datap[1];
2681	retval = True;
2682    }
2683
2684    XFree (prop_return);
2685    return retval;
2686}
2687
2688
2689void
2690WarpToScreen (int n, int inc)
2691{
2692    Window dumwin;
2693    int x, y, dumint;
2694    unsigned int dummask;
2695    ScreenInfo *newscr = NULL;
2696
2697    while (!newscr) {
2698					/* wrap around */
2699	if (n < 0)
2700	  n = NumScreens - 1;
2701	else if (n >= NumScreens)
2702	  n = 0;
2703
2704	newscr = ScreenList[n];
2705	if (!newscr) {			/* make sure screen is managed */
2706	    if (inc) {			/* walk around the list */
2707		n += inc;
2708		continue;
2709	    }
2710	    fprintf (stderr, "%s:  unable to warp to unmanaged screen %d\n",
2711		     ProgramName, n);
2712	    Bell(XkbBI_MinorError,0,None);
2713	    return;
2714	}
2715    }
2716
2717    if (Scr->screen == n) return;	/* already on that screen */
2718
2719    PreviousScreen = Scr->screen;
2720    XQueryPointer (dpy, Scr->Root, &dumwin, &dumwin, &x, &y,
2721		   &dumint, &dumint, &dummask);
2722
2723    XWarpPointer (dpy, None, newscr->Root, 0, 0, 0, 0, x, y);
2724    return;
2725}
2726
2727
2728
2729
2730/**
2731 * rotate our internal copy of WM_COLORMAP_WINDOWS
2732 */
2733static void
2734BumpWindowColormap (TwmWindow *tmp, int inc)
2735{
2736    int i, j, previously_installed;
2737    ColormapWindow **cwins;
2738
2739    if (!tmp) return;
2740
2741    if (inc && tmp->cmaps.number_cwins > 0) {
2742	cwins = malloc(sizeof(ColormapWindow *) * tmp->cmaps.number_cwins);
2743	if (cwins) {
2744	    if ((previously_installed =
2745		/* SUPPRESS 560 */(Scr->cmapInfo.cmaps == &tmp->cmaps &&
2746	        tmp->cmaps.number_cwins))) {
2747		for (i = tmp->cmaps.number_cwins; i-- > 0; )
2748		    tmp->cmaps.cwins[i]->colormap->state = 0;
2749	    }
2750
2751	    for (i = 0; i < tmp->cmaps.number_cwins; i++) {
2752		j = i - inc;
2753		if (j >= tmp->cmaps.number_cwins)
2754		    j -= tmp->cmaps.number_cwins;
2755		else if (j < 0)
2756		    j += tmp->cmaps.number_cwins;
2757		cwins[j] = tmp->cmaps.cwins[i];
2758	    }
2759
2760	    free(tmp->cmaps.cwins);
2761
2762	    tmp->cmaps.cwins = cwins;
2763
2764	    if (tmp->cmaps.number_cwins > 1)
2765		bzero (tmp->cmaps.scoreboard,
2766		       ColormapsScoreboardLength(&tmp->cmaps));
2767
2768	    if (previously_installed)
2769		InstallWindowColormaps(PropertyNotify, (TwmWindow *) NULL);
2770	}
2771    } else
2772	FetchWmColormapWindows (tmp);
2773}
2774
2775
2776static void
2777HideIconManager (void)
2778{
2779    SetMapStateProp (Scr->iconmgr.twm_win, WithdrawnState);
2780    XUnmapWindow(dpy, Scr->iconmgr.twm_win->frame);
2781    if (Scr->iconmgr.twm_win->icon_w)
2782      XUnmapWindow (dpy, Scr->iconmgr.twm_win->icon_w);
2783    Scr->iconmgr.twm_win->mapped = FALSE;
2784    Scr->iconmgr.twm_win->icon = TRUE;
2785}
2786
2787
2788
2789void
2790SetBorder (TwmWindow *tmp, Bool onoroff)
2791{
2792    if (tmp->highlight) {
2793	if (onoroff) {
2794	    XSetWindowBorder (dpy, tmp->frame, tmp->border);
2795	    if (tmp->title_w)
2796	      XSetWindowBorder (dpy, tmp->title_w, tmp->border);
2797	} else {
2798	    XSetWindowBorderPixmap (dpy, tmp->frame, tmp->gray);
2799	    if (tmp->title_w)
2800	      XSetWindowBorderPixmap (dpy, tmp->title_w, tmp->gray);
2801	}
2802    }
2803}
2804
2805
2806static void
2807DestroyMenu (MenuRoot *menu)
2808{
2809    MenuItem *item;
2810
2811    if (menu->w) {
2812	XDeleteContext (dpy, menu->w, MenuContext);
2813	XDeleteContext (dpy, menu->w, ScreenContext);
2814	if (Scr->Shadow) XDestroyWindow (dpy, menu->shadow);
2815	XDestroyWindow(dpy, menu->w);
2816    }
2817
2818    for (item = menu->first; item; ) {
2819	MenuItem *tmp = item;
2820	item = item->next;
2821	free (tmp);
2822    }
2823}
2824
2825
2826
2827/*
2828 * warping routines
2829 */
2830
2831static void
2832WarpAlongRing (XButtonEvent *ev, Bool forward)
2833{
2834    TwmWindow *r, *head;
2835
2836    if (Scr->RingLeader)
2837      head = Scr->RingLeader;
2838    else if (!(head = Scr->Ring))
2839      return;
2840
2841    if (forward) {
2842	for (r = head->ring.next; r != head; r = r->ring.next) {
2843	    if (!r || r->mapped) break;
2844	}
2845    } else {
2846	for (r = head->ring.prev; r != head; r = r->ring.prev) {
2847	    if (!r || r->mapped) break;
2848	}
2849    }
2850
2851    if (r && r != head) {
2852	TwmWindow *p = Scr->RingLeader, *t;
2853	XPointer context_data;
2854
2855	Scr->RingLeader = r;
2856	WarpToWindow (r);
2857
2858	if (XFindContext (dpy, ev->window, TwmContext, &context_data) == 0)
2859	    t = (TwmWindow *) context_data;
2860	else
2861	    t = NULL;
2862
2863	if (p && p->mapped && p == t) {
2864	    p->ring.cursor_valid = True;
2865	    p->ring.curs_x = ev->x_root - t->frame_x;
2866	    p->ring.curs_y = ev->y_root - t->frame_y;
2867	    if (p->ring.curs_x < -p->frame_bw ||
2868		p->ring.curs_x >= p->frame_width + p->frame_bw ||
2869		p->ring.curs_y < -p->frame_bw ||
2870		p->ring.curs_y >= p->frame_height + p->frame_bw) {
2871		/* somehow out of window */
2872		p->ring.curs_x = p->frame_width / 2;
2873		p->ring.curs_y = p->frame_height / 2;
2874	    }
2875	}
2876    }
2877}
2878
2879
2880
2881static void
2882WarpToWindow (TwmWindow *t)
2883{
2884    int x, y;
2885
2886    if (t->auto_raise || !Scr->NoRaiseWarp) AutoRaiseWindow (t);
2887    if (t->ring.cursor_valid) {
2888	x = t->ring.curs_x;
2889	y = t->ring.curs_y;
2890    } else {
2891	x = t->frame_width / 2;
2892	y = t->frame_height / 2;
2893    }
2894    XWarpPointer (dpy, None, t->frame, 0, 0, 0, 0, x, y);
2895}
2896
2897
2898
2899
2900/*
2901 * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
2902 * client messages will have the following form:
2903 *
2904 *     event type	ClientMessage
2905 *     message type	_XA_WM_PROTOCOLS
2906 *     window		tmp->w
2907 *     format		32
2908 *     data[0]		message atom
2909 *     data[1]		time stamp
2910 */
2911static void
2912send_clientmessage (Window w, Atom a, Time timestamp)
2913{
2914    XClientMessageEvent ev;
2915
2916    ev.type = ClientMessage;
2917    ev.window = w;
2918    ev.message_type = _XA_WM_PROTOCOLS;
2919    ev.format = 32;
2920    ev.data.l[0] = a;
2921    ev.data.l[1] = timestamp;
2922    XSendEvent (dpy, w, False, 0L, (XEvent *) &ev);
2923}
2924
2925void
2926SendDeleteWindowMessage (TwmWindow *tmp, Time timestamp)
2927{
2928    send_clientmessage (tmp->w, _XA_WM_DELETE_WINDOW, timestamp);
2929}
2930
2931void
2932SendSaveYourselfMessage (TwmWindow *tmp, Time timestamp)
2933{
2934    send_clientmessage (tmp->w, _XA_WM_SAVE_YOURSELF, timestamp);
2935}
2936
2937void
2938SendTakeFocusMessage (TwmWindow *tmp, Time timestamp)
2939{
2940    send_clientmessage (tmp->w, _XA_WM_TAKE_FOCUS, timestamp);
2941}
2942