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