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