menus.c revision c0833b5b
1/*****************************************************************************/
2/**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
3/**                          Salt Lake City, Utah                           **/
4/**  Portions Copyright 1989 by the Massachusetts Institute of Technology   **/
5/**                        Cambridge, Massachusetts                         **/
6/**                                                                         **/
7/**                           All Rights Reserved                           **/
8/**                                                                         **/
9/**    Permission to use, copy, modify, and distribute this software and    **/
10/**    its documentation  for  any  purpose  and  without  fee is hereby    **/
11/**    granted, provided that the above copyright notice appear  in  all    **/
12/**    copies and that both  that  copyright  notice  and  this  permis-    **/
13/**    sion  notice appear in supporting  documentation,  and  that  the    **/
14/**    names of Evans & Sutherland and M.I.T. not be used in advertising    **/
15/**    in publicity pertaining to distribution of the  software  without    **/
16/**    specific, written prior permission.                                  **/
17/**                                                                         **/
18/**    EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD    **/
19/**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
20/**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND OR    **/
21/**    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
22/**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
23/**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
24/**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
25/**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
26/*****************************************************************************/
27/*
28 *  [ ctwm ]
29 *
30 *  Copyright 1992 Claude Lecommandeur.
31 *
32 * Permission to use, copy, modify  and distribute this software  [ctwm] and
33 * its documentation for any purpose is hereby granted without fee, provided
34 * that the above  copyright notice appear  in all copies and that both that
35 * copyright notice and this permission notice appear in supporting documen-
36 * tation, and that the name of  Claude Lecommandeur not be used in adverti-
37 * sing or  publicity  pertaining to  distribution of  the software  without
38 * specific, written prior permission. Claude Lecommandeur make no represen-
39 * tations  about the suitability  of this software  for any purpose.  It is
40 * provided "as is" without express or implied warranty.
41 *
42 * Claude Lecommandeur DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
43 * INCLUDING ALL  IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS.  IN NO
44 * EVENT SHALL  Claude Lecommandeur  BE LIABLE FOR ANY SPECIAL,  INDIRECT OR
45 * CONSEQUENTIAL  DAMAGES OR ANY  DAMAGES WHATSOEVER  RESULTING FROM LOSS OF
46 * USE, DATA  OR PROFITS,  WHETHER IN AN ACTION  OF CONTRACT,  NEGLIGENCE OR
47 * OTHER  TORTIOUS ACTION,  ARISING OUT OF OR IN  CONNECTION WITH THE USE OR
48 * PERFORMANCE OF THIS SOFTWARE.
49 *
50 * Author:  Claude Lecommandeur [ lecom@sic.epfl.ch ][ April 1992 ]
51 */
52
53
54/***********************************************************************
55 *
56 * $XConsortium: menus.c,v 1.186 91/07/17 13:58:00 dave Exp $
57 *
58 * twm menu code
59 *
60 * 17-Nov-87 Thomas E. LaStrange		File created
61 *
62 * Do the necessary modification to be integrated in ctwm.
63 * Can no longer be used for the standard twm.
64 *
65 * 22-April-92 Claude Lecommandeur.
66 *
67 *
68 ***********************************************************************/
69
70#if defined(USE_SIGNALS) && defined(__sgi)
71#  define _BSD_SIGNALS
72#endif
73
74#include <stdio.h>
75#include <signal.h>
76
77#ifdef VMS
78#include <stdlib.h>
79#include <string.h>
80#include <unixio.h>
81#include <file.h>
82#include <decw$include/Xos.h>
83#include <decw$include/Xatom.h>
84#else
85#include <X11/Xos.h>
86#include <X11/Xatom.h>
87#endif
88#include "twm.h"
89#include "ctwm.h"
90#include "gc.h"
91#include "menus.h"
92#include "resize.h"
93#include "events.h"
94#include "list.h"
95#include "util.h"
96#include "parse.h"
97#include "screen.h"
98#include "icons.h"
99#include "add_window.h"
100#include "windowbox.h"
101#include "workmgr.h"
102#include "cursor.h"
103#include "gnomewindefs.h"
104#ifdef SOUNDS
105#  include "sound.h"
106#endif
107#ifdef VMS
108#  include <X11Xmu/CharSet.h>
109#  include <decw$bitmaps/menu12.xbm>
110#  include <X11SM/SMlib.h>
111#  include "vms_cmd_services.h"
112#  include <lib$routines.h>
113#else
114#  include <X11/Xmu/CharSet.h>
115#  include <X11/SM/SMlib.h>
116#endif
117#include "version.h"
118
119#if defined(MACH) || defined(__MACH__) || defined(sony_news) || defined(NeXT)
120#define lrand48 random
121#endif
122#if defined(VMS) || defined(__DARWIN__)
123#define lrand48 rand
124#endif
125
126#ifndef VMS
127#define MAX(x,y) ((x)>(y)?(x):(y))
128#define MIN(x,y) ((x)<(y)?(x):(y))
129#endif
130#define ABS(x) ((x)<0?-(x):(x))
131
132int RootFunction = 0;
133MenuRoot *ActiveMenu = NULL;		/* the active menu */
134MenuItem *ActiveItem = NULL;		/* the active menu item */
135int MoveFunction;			/* either F_MOVE or F_FORCEMOVE */
136int WindowMoved = FALSE;
137int menuFromFrameOrWindowOrTitlebar = FALSE;
138char *CurrentSelectedWorkspace;
139int AlternateKeymap;
140Bool AlternateContext;
141
142extern char *captivename;
143
144int ConstMove = FALSE;		/* constrained move variables */
145int ConstMoveDir;
146int ConstMoveX;
147int ConstMoveY;
148int ConstMoveXL;
149int ConstMoveXR;
150int ConstMoveYT;
151int ConstMoveYB;
152
153/* Globals used to keep track of whether the mouse has moved during
154   a resize function. */
155int ResizeOrigX;
156int ResizeOrigY;
157
158int MenuDepth = 0;		/* number of menus up */
159static struct {
160    int x;
161    int y;
162} MenuOrigins[MAXMENUDEPTH];
163static Cursor LastCursor;
164static Bool addingdefaults = False;
165
166void jump (TwmWindow *tmp_win, int  direction, char *action);
167void waitamoment (float timeout);
168
169extern char *Action;
170extern int Context;
171extern TwmWindow *ButtonWindow, *Tmp_win;
172extern XEvent ButtonEvent;
173extern char *InitFile;
174extern int ConstrainedMoveTime;
175static void Identify (TwmWindow *t);
176
177#define SHADOWWIDTH 5			/* in pixels */
178
179#ifdef GNOME
180  extern Atom _XA_WIN_STATE;
181#endif /* GNOME */
182
183
184
185/***********************************************************************
186 *
187 *  Procedure:
188 *	InitMenus - initialize menu roots
189 *
190 ***********************************************************************
191 */
192
193void InitMenus(void)
194{
195    Scr->DefaultFunction.func = 0;
196    Scr->WindowFunction.func  = 0;
197    Scr->ChangeWorkspaceFunction.func  = 0;
198    Scr->DeIconifyFunction.func  = 0;
199    Scr->IconifyFunction.func  = 0;
200
201    Scr->FuncKeyRoot.next = NULL;
202    Scr->FuncButtonRoot.next = NULL;
203}
204
205
206
207/***********************************************************************
208 *
209 *  Procedure:
210 *	AddFuncKey - add a function key to the list
211 *
212 *  Inputs:
213 *	name	- the name of the key
214 *	cont	- the context to look for the key press in
215 *	mods	- modifier keys that need to be pressed
216 *	func	- the function to perform
217 *	win_name- the window name (if any)
218 *	action	- the action string associated with the function (if any)
219 *
220 ***********************************************************************
221 */
222
223Bool AddFuncKey (char *name, int cont, int mods, int func,
224		 MenuRoot *menu, char *win_name, char *action)
225{
226    FuncKey *tmp;
227    KeySym keysym;
228    KeyCode keycode;
229
230    /*
231     * Don't let a 0 keycode go through, since that means AnyKey to the
232     * XGrabKey call in GrabKeys().
233     */
234    if ((keysym = XStringToKeysym(name)) == NoSymbol ||
235	(keycode = XKeysymToKeycode(dpy, keysym)) == 0)
236    {
237	return False;
238    }
239
240    /* see if there already is a key defined for this context */
241    for (tmp = Scr->FuncKeyRoot.next; tmp != NULL; tmp = tmp->next)
242    {
243	if (tmp->keysym == keysym &&
244	    tmp->cont == cont &&
245	    tmp->mods == mods)
246	    break;
247    }
248    if (tmp && addingdefaults) return (True);
249
250    if (tmp == NULL)
251    {
252	tmp = (FuncKey *) malloc(sizeof(FuncKey));
253	tmp->next = Scr->FuncKeyRoot.next;
254	Scr->FuncKeyRoot.next = tmp;
255    }
256
257    tmp->name = name;
258    tmp->keysym = keysym;
259    tmp->keycode = keycode;
260    tmp->cont = cont;
261    tmp->mods = mods;
262    tmp->func = func;
263    tmp->menu = menu;
264    tmp->win_name = win_name;
265    tmp->action = action;
266
267    return True;
268}
269
270/***********************************************************************
271 *
272 *  Procedure:
273 *	AddFuncButton - add a function button to the list
274 *
275 *  Inputs:
276 *	num	- the num of the button
277 *	cont	- the context to look for the key press in
278 *	mods	- modifier keys that need to be pressed
279 *	func	- the function to perform
280 *	menu    - the menu (if any)
281 *	item	- the menu item (if any)
282 *
283 ***********************************************************************
284 */
285
286Bool AddFuncButton (int num, int cont, int mods, int func,
287		    MenuRoot *menu, MenuItem *item)
288{
289    FuncButton *tmp;
290
291    /* see if there already is a key defined for this context */
292    for (tmp = Scr->FuncButtonRoot.next; tmp != NULL; tmp = tmp->next) {
293	if ((tmp->num == num) && (tmp->cont == cont) && (tmp->mods == mods))
294	    break;
295    }
296    if (tmp && addingdefaults) return (True);
297
298    if (tmp == NULL) {
299	tmp = (FuncButton*) malloc (sizeof (FuncButton));
300	tmp->next = Scr->FuncButtonRoot.next;
301	Scr->FuncButtonRoot.next = tmp;
302    }
303
304    tmp->num  = num;
305    tmp->cont = cont;
306    tmp->mods = mods;
307    tmp->func = func;
308    tmp->menu = menu;
309    tmp->item = item;
310
311    return True;
312}
313
314
315
316static TitleButton *cur_tb = NULL;
317
318void ModifyCurrentTB(int button, int mods, int func, char *action,
319		     MenuRoot *menuroot)
320{
321    TitleButtonFunc *tbf;
322
323    if (!cur_tb) {
324        fprintf (stderr, "%s: can't find titlebutton\n", ProgramName);
325	return;
326    }
327    for (tbf = cur_tb->funs; tbf; tbf = tbf->next) {
328	if (tbf->num == button && tbf->mods == mods)
329	    break;
330    }
331    if (!tbf) {
332	tbf = (TitleButtonFunc *)malloc(sizeof(TitleButtonFunc));
333	if (!tbf) {
334	    fprintf (stderr, "%s: out of memory\n", ProgramName);
335	    return;
336	}
337	tbf->next = cur_tb->funs;
338	cur_tb->funs = tbf;
339    }
340    tbf->num = button;
341    tbf->mods = mods;
342    tbf->func = func;
343    tbf->action = action;
344    tbf->menuroot = menuroot;
345}
346
347int CreateTitleButton (char *name, int func, char *action, MenuRoot *menuroot,
348		       Bool rightside, Bool append)
349{
350    int button;
351    cur_tb = (TitleButton *) malloc (sizeof(TitleButton));
352
353    if (!cur_tb) {
354	fprintf (stderr,
355		 "%s:  unable to allocate %lu bytes for title button\n",
356		 ProgramName, (unsigned long) sizeof(TitleButton));
357	return 0;
358    }
359
360    cur_tb->next = NULL;
361    cur_tb->name = name;                      /* note that we are not copying */
362    cur_tb->image = None;                     /* WARNING, values not set yet */
363    cur_tb->width = 0;                        /* see InitTitlebarButtons */
364    cur_tb->height = 0;                       /* ditto */
365    cur_tb->rightside = rightside;
366    cur_tb->funs = NULL;
367    if (rightside) {
368	Scr->TBInfo.nright++;
369    } else {
370	Scr->TBInfo.nleft++;
371    }
372
373    for(button = 0; button < MAX_BUTTONS; button++){
374	ModifyCurrentTB(button + 1, 0, func, action, menuroot);
375    }
376
377    /*
378     * Cases for list:
379     *
380     *     1.  empty list, prepend left       put at head of list
381     *     2.  append left, prepend right     put in between left and right
382     *     3.  append right                   put at tail of list
383     *
384     * Do not refer to widths and heights yet since buttons not created
385     * (since fonts not loaded and heights not known).
386     */
387    if ((!Scr->TBInfo.head) || ((!append) && (!rightside))) {	/* 1 */
388	cur_tb->next = Scr->TBInfo.head;
389	Scr->TBInfo.head = cur_tb;
390    } else if (append && rightside) {	/* 3 */
391	register TitleButton *t;
392	for /* SUPPRESS 530 */
393	  (t = Scr->TBInfo.head; t->next; t = t->next);
394	t->next = cur_tb;
395	cur_tb->next = NULL;
396    } else {				/* 2 */
397	register TitleButton *t, *prev = NULL;
398	for (t = Scr->TBInfo.head; t && !t->rightside; t = t->next) {
399	    prev = t;
400	}
401	if (prev) {
402	    cur_tb->next = prev->next;
403	    prev->next = cur_tb;
404	} else {
405	    cur_tb->next = Scr->TBInfo.head;
406	    Scr->TBInfo.head = cur_tb;
407	}
408    }
409
410    return 1;
411}
412
413
414
415/*
416 * InitTitlebarButtons - Do all the necessary stuff to load in a titlebar
417 * button.  If we can't find the button, then put in a question; if we can't
418 * find the question mark, something is wrong and we are probably going to be
419 * in trouble later on.
420 */
421void InitTitlebarButtons (void)
422{
423    TitleButton *tb;
424    int h;
425
426    /*
427     * initialize dimensions
428     */
429    Scr->TBInfo.width = (Scr->TitleHeight -
430			 2 * (Scr->FramePadding + Scr->ButtonIndent));
431    if (Scr->use3Dtitles)
432	Scr->TBInfo.pad = ((Scr->TitlePadding > 1)
433		       ? ((Scr->TitlePadding + 1) / 2) : 0);
434    else
435	Scr->TBInfo.pad = ((Scr->TitlePadding > 1)
436		       ? ((Scr->TitlePadding + 1) / 2) : 1);
437    h = Scr->TBInfo.width - 2 * Scr->TBInfo.border;
438
439    /*
440     * add in some useful buttons and bindings so that novices can still
441     * use the system.
442     */
443    if (!Scr->NoDefaults) {
444	/* insert extra buttons */
445	if (Scr->use3Dtitles) {
446	    if (!CreateTitleButton (TBPM_3DDOT, F_ICONIFY, "", (MenuRoot *) NULL,
447				False, False)) {
448	        fprintf (stderr, "%s:  unable to add iconify button\n", ProgramName);
449	    }
450	    if (!CreateTitleButton (TBPM_3DRESIZE, F_RESIZE, "", (MenuRoot *) NULL,
451				True, True)) {
452	        fprintf (stderr, "%s:  unable to add resize button\n", ProgramName);
453	    }
454	}
455	else {
456	    if (!CreateTitleButton (TBPM_ICONIFY, F_ICONIFY, "", (MenuRoot *) NULL,
457				False, False)) {
458	        fprintf (stderr, "%s:  unable to add iconify button\n", ProgramName);
459	    }
460	    if (!CreateTitleButton (TBPM_RESIZE, F_RESIZE, "", (MenuRoot *) NULL,
461				True, True)) {
462	        fprintf (stderr, "%s:  unable to add resize button\n", ProgramName);
463	    }
464	}
465	addingdefaults = True;
466	AddDefaultBindings ();
467	addingdefaults = False;
468    }
469    ComputeCommonTitleOffsets ();
470
471    /*
472     * load in images and do appropriate centering
473     */
474
475    for (tb = Scr->TBInfo.head; tb; tb = tb->next) {
476	tb->image = GetImage (tb->name, Scr->TitleC);
477	if (!tb->image) {
478	    tb->image = GetImage (TBPM_QUESTION, Scr->TitleC);
479	    if (!tb->image) {		/* cannot happen (see util.c) */
480		fprintf (stderr, "%s:  unable to add titlebar button \"%s\"\n",
481			 ProgramName, tb->name);
482	    }
483	}
484	tb->width  = tb->image->width;
485	tb->height = tb->image->height;
486	tb->dstx = (h - tb->width + 1) / 2;
487	if (tb->dstx < 0) {		/* clip to minimize copying */
488	    tb->srcx = -(tb->dstx);
489	    tb->width = h;
490	    tb->dstx = 0;
491	} else {
492	    tb->srcx = 0;
493	}
494	tb->dsty = (h - tb->height + 1) / 2;
495	if (tb->dsty < 0) {
496	    tb->srcy = -(tb->dsty);
497	    tb->height = h;
498	    tb->dsty = 0;
499	} else {
500	    tb->srcy = 0;
501	}
502    }
503}
504
505
506void PaintEntry(MenuRoot *mr, MenuItem *mi, int exposure)
507{
508    if (Scr->use3Dmenus)
509	Paint3DEntry (mr, mi, exposure);
510    else
511	PaintNormalEntry (mr, mi, exposure);
512    if (mi->state) mr->lastactive = mi;
513}
514
515void Paint3DEntry(MenuRoot *mr, MenuItem *mi, int exposure)
516{
517    int y_offset;
518    int text_y;
519    GC gc;
520
521    y_offset = mi->item_num * Scr->EntryHeight + Scr->MenuShadowDepth;
522    text_y = y_offset + Scr->MenuFont.y + 2;
523
524    if (mi->func != F_TITLE) {
525	int x, y;
526
527	gc = Scr->NormalGC;
528	if (mi->state) {
529	    Draw3DBorder (mr->w, Scr->MenuShadowDepth, y_offset,
530			mr->width - 2 * Scr->MenuShadowDepth, Scr->EntryHeight, 1,
531			mi->highlight, off, True, False);
532	    FB(mi->highlight.fore, mi->highlight.back);
533	    XmbDrawImageString(dpy, mr->w, Scr->MenuFont.font_set, gc,
534		mi->x + Scr->MenuShadowDepth, text_y, mi->item, mi->strlen);
535	}
536	else {
537	    if (mi->user_colors || !exposure) {
538		XSetForeground (dpy, gc, mi->normal.back);
539		XFillRectangle (dpy, mr->w, gc,
540			Scr->MenuShadowDepth, y_offset,
541			mr->width - 2 * Scr->MenuShadowDepth, Scr->EntryHeight);
542		FB (mi->normal.fore, mi->normal.back);
543	    }
544	    else {
545		gc = Scr->MenuGC;
546	    }
547	    XmbDrawImageString (dpy, mr->w, Scr->MenuFont.font_set, gc,
548				mi->x + Scr->MenuShadowDepth, text_y,
549				mi->item, mi->strlen);
550	    if (mi->separated) {
551		FB (Scr->MenuC.shadd, Scr->MenuC.shadc);
552		XDrawLine (dpy, mr->w, Scr->NormalGC,
553				Scr->MenuShadowDepth,
554				y_offset + Scr->EntryHeight - 2,
555				mr->width - Scr->MenuShadowDepth,
556				y_offset + Scr->EntryHeight - 2);
557		FB (Scr->MenuC.shadc, Scr->MenuC.shadd);
558		XDrawLine (dpy, mr->w, Scr->NormalGC,
559				Scr->MenuShadowDepth,
560				y_offset + Scr->EntryHeight - 1,
561				mr->width - Scr->MenuShadowDepth,
562				y_offset + Scr->EntryHeight - 1);
563	    }
564	}
565
566	if (mi->func == F_MENU) {
567	    /* create the pull right pixmap if needed */
568	    if (Scr->pullPm == None)
569	    {
570		Scr->pullPm = Create3DMenuIcon (Scr->MenuFont.height, &Scr->pullW,
571				&Scr->pullH, Scr->MenuC);
572	    }
573	    x = mr->width - Scr->pullW - Scr->MenuShadowDepth - 2;
574	    y = y_offset + ((Scr->MenuFont.height - Scr->pullH) / 2) + 2;
575	    XCopyArea (dpy, Scr->pullPm, mr->w, gc, 0, 0, Scr->pullW, Scr->pullH, x, y);
576	}
577    }
578    else
579    {
580	Draw3DBorder (mr->w, Scr->MenuShadowDepth, y_offset,
581			mr->width - 2 * Scr->MenuShadowDepth, Scr->EntryHeight, 1,
582			mi->normal, off, True, False);
583	FB (mi->normal.fore, mi->normal.back);
584	XmbDrawImageString(dpy, mr->w, Scr->MenuFont.font_set, Scr->NormalGC,
585			   mi->x + 2, text_y, mi->item, mi->strlen);
586    }
587}
588
589
590
591void PaintNormalEntry(MenuRoot *mr, MenuItem *mi, int exposure)
592{
593    int y_offset;
594    int text_y;
595    GC gc;
596
597    y_offset = mi->item_num * Scr->EntryHeight;
598    text_y = y_offset + Scr->MenuFont.y;
599
600    if (mi->func != F_TITLE)
601    {
602	int x, y;
603
604	if (mi->state)
605	{
606	    XSetForeground(dpy, Scr->NormalGC, mi->highlight.back);
607
608	    XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
609		mr->width, Scr->EntryHeight);
610	    FB(mi->highlight.fore, mi->highlight.back);
611	    XmbDrawString(dpy, mr->w, Scr->MenuFont.font_set, Scr->NormalGC,
612			  mi->x, text_y, mi->item, mi->strlen);
613
614	    gc = Scr->NormalGC;
615	}
616	else
617	{
618	    if (mi->user_colors || !exposure)
619	    {
620		XSetForeground(dpy, Scr->NormalGC, mi->normal.back);
621
622		XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
623		    mr->width, Scr->EntryHeight);
624
625		FB(mi->normal.fore, mi->normal.back);
626		gc = Scr->NormalGC;
627	    }
628	    else {
629		gc = Scr->MenuGC;
630	    }
631	    XmbDrawString(dpy, mr->w, Scr->MenuFont.font_set, gc, mi->x,
632			  text_y, mi->item, mi->strlen);
633	    if (mi->separated)
634		XDrawLine (dpy, mr->w, gc, 0, y_offset + Scr->EntryHeight - 1,
635				mr->width, y_offset + Scr->EntryHeight - 1);
636	}
637
638	if (mi->func == F_MENU)
639	{
640	    /* create the pull right pixmap if needed */
641	    if (Scr->pullPm == None)
642	    {
643		Scr->pullPm = CreateMenuIcon (Scr->MenuFont.height,
644					     &Scr->pullW, &Scr->pullH);
645	    }
646	    x = mr->width - Scr->pullW - 5;
647	    y = y_offset + ((Scr->MenuFont.height - Scr->pullH) / 2);
648	    XCopyPlane(dpy, Scr->pullPm, mr->w, gc, 0, 0,
649		Scr->pullW, Scr->pullH, x, y, 1);
650	}
651    }
652    else
653    {
654	int y;
655
656	XSetForeground(dpy, Scr->NormalGC, mi->normal.back);
657
658	/* fill the rectangle with the title background color */
659	XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
660	    mr->width, Scr->EntryHeight);
661
662	{
663	    XSetForeground(dpy, Scr->NormalGC, mi->normal.fore);
664	    /* now draw the dividing lines */
665	    if (y_offset)
666	      XDrawLine (dpy, mr->w, Scr->NormalGC, 0, y_offset,
667			 mr->width, y_offset);
668	    y = ((mi->item_num+1) * Scr->EntryHeight)-1;
669	    XDrawLine(dpy, mr->w, Scr->NormalGC, 0, y, mr->width, y);
670	}
671
672	FB(mi->normal.fore, mi->normal.back);
673	/* finally render the title */
674	XmbDrawString(dpy, mr->w, Scr->MenuFont.font_set, Scr->NormalGC, mi->x,
675		      text_y, mi->item, mi->strlen);
676    }
677}
678
679void PaintMenu(MenuRoot *mr, XEvent *e)
680{
681    MenuItem *mi;
682
683    if (Scr->use3Dmenus) {
684	Draw3DBorder (mr->w, 0, 0, mr->width, mr->height,
685		Scr->MenuShadowDepth, Scr->MenuC, off, False, False);
686    }
687    for (mi = mr->first; mi != NULL; mi = mi->next)
688    {
689	int y_offset = mi->item_num * Scr->EntryHeight;
690
691	/* be smart about handling the expose, redraw only the entries
692	 * that we need to
693	 */
694	if (e->xexpose.y <= (y_offset + Scr->EntryHeight) &&
695	    (e->xexpose.y + e->xexpose.height) >= y_offset)
696	{
697	    PaintEntry(mr, mi, True);
698	}
699    }
700    XSync(dpy, 0);
701}
702
703
704
705void MakeWorkspacesMenu (void)
706{
707    static char **actions = NULL;
708    WorkSpace *wlist;
709    char **act;
710
711    if (! Scr->Workspaces) return;
712    AddToMenu (Scr->Workspaces, "TWM Workspaces", NULLSTR, NULL, F_TITLE, NULLSTR, NULLSTR);
713    if (! actions) {
714	int count = 0;
715
716        for (wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL; wlist = wlist->next) {
717            count++;
718        }
719	count++;
720	actions = (char**) malloc (count * sizeof (char*));
721	act = actions;
722        for (wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL; wlist = wlist->next) {
723	    *act = (char*) malloc (strlen ("WGOTO : ") + strlen (wlist->name) + 1);
724	    sprintf (*act, "WGOTO : %s", wlist->name);
725	    act++;
726	}
727	*act = NULL;
728    }
729    act = actions;
730    for (wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL; wlist = wlist->next) {
731        AddToMenu (Scr->Workspaces, wlist->name, *act, Scr->Windows, F_MENU, NULL, NULL);
732	act++;
733    }
734    Scr->Workspaces->pinned = False;
735    MakeMenu (Scr->Workspaces);
736}
737
738static Bool fromMenu;
739
740int UpdateMenu(void)
741{
742    MenuItem *mi;
743    int i, x, y, x_root, y_root, entry;
744    int done;
745    MenuItem *badItem = NULL;
746
747    fromMenu = TRUE;
748
749    while (TRUE)
750    {
751	/* block until there is an event */
752        if (!menuFromFrameOrWindowOrTitlebar) {
753	  XMaskEvent(dpy,
754		     ButtonPressMask | ButtonReleaseMask |
755		     KeyPressMask | KeyReleaseMask |
756		     EnterWindowMask | ExposureMask |
757		     VisibilityChangeMask | LeaveWindowMask |
758		     ButtonMotionMask, &Event);
759	}
760	if (Event.type == MotionNotify) {
761	    /* discard any extra motion events before a release */
762	    while(XCheckMaskEvent(dpy,
763		ButtonMotionMask | ButtonReleaseMask, &Event))
764		if (Event.type == ButtonRelease)
765		    break;
766	}
767
768	if (!DispatchEvent ())
769	    continue;
770
771	if ((! ActiveMenu) || Cancel) {
772	  menuFromFrameOrWindowOrTitlebar = FALSE;
773	  fromMenu = FALSE;
774	  return (0);
775	}
776
777	if (Event.type != MotionNotify)
778	    continue;
779
780	done = FALSE;
781	XQueryPointer( dpy, ActiveMenu->w, &JunkRoot, &JunkChild,
782	    &x_root, &y_root, &x, &y, &JunkMask);
783
784	/* if we haven't recieved the enter notify yet, wait */
785	if (ActiveMenu && !ActiveMenu->entered)
786	    continue;
787
788	if (XFindContext(dpy, ActiveMenu->w, ScreenContext, (XPointer *)&Scr)
789	    != XCSUCCESS)
790	    continue;
791
792	if (x < 0 || y < 0 ||
793	    x >= ActiveMenu->width || y >= ActiveMenu->height)
794	{
795	    if (ActiveItem && ActiveItem->func != F_TITLE)
796	    {
797		ActiveItem->state = 0;
798		PaintEntry(ActiveMenu, ActiveItem, False);
799	    }
800	    ActiveItem = NULL;
801	    continue;
802	}
803
804	/* look for the entry that the mouse is in */
805	entry = y / Scr->EntryHeight;
806	for (i = 0, mi = ActiveMenu->first; mi != NULL; i++, mi=mi->next)
807	{
808	    if (i == entry)
809		break;
810	}
811
812	/* if there is an active item, we might have to turn it off */
813	if (ActiveItem)
814	{
815	    /* is the active item the one we are on ? */
816	    if (ActiveItem->item_num == entry && ActiveItem->state)
817		done = TRUE;
818
819	    /* if we weren't on the active entry, let's turn the old
820	     * active one off
821	     */
822	    if (!done && ActiveItem->func != F_TITLE)
823	    {
824		ActiveItem->state = 0;
825		PaintEntry(ActiveMenu, ActiveItem, False);
826	    }
827	}
828
829	/* if we weren't on the active item, change the active item and turn
830	 * it on
831	 */
832	if (!done)
833	{
834	    ActiveItem = mi;
835	    if (ActiveItem && ActiveItem->func != F_TITLE && !ActiveItem->state)
836	    {
837		ActiveItem->state = 1;
838		PaintEntry(ActiveMenu, ActiveItem, False);
839	    }
840	}
841
842	/* now check to see if we were over the arrow of a pull right entry */
843	if (ActiveItem && ActiveItem->func == F_MENU &&
844	   ((ActiveMenu->width - x) < (ActiveMenu->width / 3)))
845	{
846	    MenuRoot *save = ActiveMenu;
847	    int savex = MenuOrigins[MenuDepth - 1].x;
848	    int savey = MenuOrigins[MenuDepth - 1].y;
849
850	    if (MenuDepth < MAXMENUDEPTH) {
851		if (ActiveMenu == Scr->Workspaces)
852		    CurrentSelectedWorkspace = ActiveItem->item;
853		PopUpMenu (ActiveItem->sub,
854			   (savex + (((2 * ActiveMenu->width) / 3) - 1)),
855			   (savey + ActiveItem->item_num * Scr->EntryHeight)
856			   /*(savey + ActiveItem->item_num * Scr->EntryHeight +
857			    (Scr->EntryHeight >> 1))*/, False);
858		CurrentSelectedWorkspace = NULL;
859	    } else if (!badItem) {
860		XBell (dpy, 0);
861		badItem = ActiveItem;
862	    }
863
864	    /* if the menu did get popped up, unhighlight the active item */
865	    if (save != ActiveMenu && ActiveItem->state)
866	    {
867		ActiveItem->state = 0;
868		PaintEntry(save, ActiveItem, False);
869		ActiveItem = NULL;
870	    }
871	}
872	if (badItem != ActiveItem) badItem = NULL;
873	XFlush(dpy);
874    }
875}
876
877
878
879/***********************************************************************
880 *
881 *  Procedure:
882 *	NewMenuRoot - create a new menu root
883 *
884 *  Returned Value:
885 *	(MenuRoot *)
886 *
887 *  Inputs:
888 *	name	- the name of the menu root
889 *
890 ***********************************************************************
891 */
892
893MenuRoot *NewMenuRoot(char *name)
894{
895    MenuRoot *tmp;
896
897#define UNUSED_PIXEL ((unsigned long) (~0))	/* more than 24 bits */
898
899    tmp = (MenuRoot *) malloc(sizeof(MenuRoot));
900    tmp->highlight.fore = UNUSED_PIXEL;
901    tmp->highlight.back = UNUSED_PIXEL;
902    tmp->name = name;
903    tmp->prev = NULL;
904    tmp->first = NULL;
905    tmp->last = NULL;
906    tmp->defaultitem = NULL;
907    tmp->items = 0;
908    tmp->width = 0;
909    tmp->mapped = NEVER_MAPPED;
910    tmp->pull = FALSE;
911    tmp->w = None;
912    tmp->shadow = None;
913    tmp->real_menu = FALSE;
914
915    if (Scr->MenuList == NULL)
916    {
917	Scr->MenuList = tmp;
918	Scr->MenuList->next = NULL;
919    }
920
921    if (Scr->LastMenu == NULL)
922    {
923	Scr->LastMenu = tmp;
924	Scr->LastMenu->next = NULL;
925    }
926    else
927    {
928	Scr->LastMenu->next = tmp;
929	Scr->LastMenu = tmp;
930	Scr->LastMenu->next = NULL;
931    }
932
933    if (strcmp(name, TWM_WINDOWS) == 0)
934	Scr->Windows = tmp;
935
936    if (strcmp(name, TWM_ICONS) == 0)
937	Scr->Icons = tmp;
938
939    if (strcmp(name, TWM_WORKSPACES) == 0) {
940	Scr->Workspaces = tmp;
941	if (!Scr->Windows) NewMenuRoot (TWM_WINDOWS);
942    }
943    if (strcmp(name, TWM_ALLWINDOWS) == 0)
944	Scr->AllWindows = tmp;
945
946    /* Added by dl 2004 */
947    if (strcmp(name, TWM_ALLICONS) == 0)
948    Scr->AllIcons = tmp;
949
950    /* Added by Dan Lilliehorn (dl@dl.nu) 2000-02-29       */
951    if (strcmp(name, TWM_KEYS) == 0)
952	Scr->Keys = tmp;
953
954    if (strcmp(name, TWM_VISIBLE) == 0)
955      Scr->Visible = tmp;
956
957    /* End addition */
958
959    return (tmp);
960}
961
962
963
964/***********************************************************************
965 *
966 *  Procedure:
967 *	AddToMenu - add an item to a root menu
968 *
969 *  Returned Value:
970 *	(MenuItem *)
971 *
972 *  Inputs:
973 *	menu	- pointer to the root menu to add the item
974 *	item	- the text to appear in the menu
975 *	action	- the string to possibly execute
976 *	sub	- the menu root if it is a pull-right entry
977 *	func	- the numeric function
978 *	fore	- foreground color string
979 *	back	- background color string
980 *
981 ***********************************************************************
982 */
983
984MenuItem *AddToMenu(MenuRoot *menu, char *item, char *action,
985		    MenuRoot *sub, int func, char *fore, char *back)
986{
987    MenuItem *tmp;
988    int width;
989    char *itemname;
990    XRectangle ink_rect;
991    XRectangle logical_rect;
992
993#ifdef DEBUG_MENUS
994    fprintf(stderr, "adding menu item=\"%s\", action=%s, sub=%d, f=%d\n",
995	item, action, sub, func);
996#endif
997
998    tmp = (MenuItem *) malloc(sizeof(MenuItem));
999    tmp->root = menu;
1000
1001    if (menu->first == NULL)
1002    {
1003	menu->first = tmp;
1004	tmp->prev = NULL;
1005    }
1006    else
1007    {
1008	menu->last->next = tmp;
1009	tmp->prev = menu->last;
1010    }
1011    menu->last = tmp;
1012
1013    if ((menu == Scr->Workspaces) ||
1014	(menu == Scr->Windows) ||
1015	(menu == Scr->Icons) ||
1016	(menu == Scr->AllWindows) ||
1017
1018	/* Added by dl 2004 */
1019	(menu == Scr->AllIcons) ||
1020
1021	/* Added by Dan Lillehorn (dl@dl.nu) 2000-02-29 */
1022	(menu == Scr->Keys) ||
1023	(menu == Scr->Visible)) {
1024
1025	itemname = item;
1026    } else
1027    if (*item == '*') {
1028	itemname = item + 1;
1029	menu->defaultitem = tmp;
1030    }
1031    else {
1032	itemname = item;
1033    }
1034
1035    tmp->item = itemname;
1036    tmp->strlen = strlen(itemname);
1037    tmp->action = action;
1038    tmp->next = NULL;
1039    tmp->sub = NULL;
1040    tmp->state = 0;
1041    tmp->func = func;
1042    tmp->separated = 0;
1043
1044    if (!Scr->HaveFonts) CreateFonts();
1045
1046    XmbTextExtents(Scr->MenuFont.font_set,
1047		   itemname, tmp->strlen,
1048		   &ink_rect, &logical_rect);
1049    width = logical_rect.width;
1050
1051    if (width <= 0)
1052	width = 1;
1053    if (width > menu->width)
1054	menu->width = width;
1055
1056    tmp->user_colors = FALSE;
1057    if (Scr->Monochrome == COLOR && fore != NULL)
1058    {
1059	int save;
1060
1061	save = Scr->FirstTime;
1062	Scr->FirstTime = TRUE;
1063	GetColor(COLOR, &tmp->normal.fore, fore);
1064	GetColor(COLOR, &tmp->normal.back, back);
1065	if (Scr->use3Dmenus && !Scr->BeNiceToColormap) GetShadeColors (&tmp->normal);
1066	Scr->FirstTime = save;
1067	tmp->user_colors = TRUE;
1068    }
1069    if (sub != NULL)
1070    {
1071	tmp->sub = sub;
1072	menu->pull = TRUE;
1073    }
1074    tmp->item_num = menu->items++;
1075
1076    return (tmp);
1077}
1078
1079
1080void MakeMenus(void)
1081{
1082    MenuRoot *mr;
1083
1084    for (mr = Scr->MenuList; mr != NULL; mr = mr->next)
1085    {
1086	if (mr->real_menu == FALSE)
1087	    continue;
1088
1089	mr->pinned = False;
1090	MakeMenu(mr);
1091    }
1092}
1093
1094
1095
1096int MakeMenu(MenuRoot *mr)
1097{
1098    MenuItem *start, *end, *cur, *tmp;
1099    XColor f1, f2, f3;
1100    XColor b1, b2, b3;
1101    XColor save_fore, save_back;
1102    int num, i;
1103    int fred, fgreen, fblue;
1104    int bred, bgreen, bblue;
1105    int width, borderwidth;
1106    unsigned long valuemask;
1107    XSetWindowAttributes attributes;
1108    Colormap cmap = Scr->RootColormaps.cwins[0]->colormap->c;
1109    XRectangle ink_rect;
1110    XRectangle logical_rect;
1111
1112    Scr->EntryHeight = Scr->MenuFont.height + 4;
1113
1114    /* lets first size the window accordingly */
1115    if (mr->mapped == NEVER_MAPPED)
1116    {
1117	if (mr->pull == TRUE) {
1118	    mr->width += 16 + 10;
1119	}
1120	width = mr->width + 10;
1121	for (cur = mr->first; cur != NULL; cur = cur->next) {
1122	    if (cur->func != F_TITLE)
1123		cur->x = 5;
1124	    else {
1125		XmbTextExtents(Scr->MenuFont.font_set, cur->item, cur->strlen,
1126			       &ink_rect, &logical_rect);
1127		cur->x = width - logical_rect.width;
1128		cur->x /= 2;
1129	    }
1130	}
1131	mr->height = mr->items * Scr->EntryHeight;
1132	mr->width += 10;
1133	if (Scr->use3Dmenus) {
1134	    mr->width  += 2 * Scr->MenuShadowDepth;
1135	    mr->height += 2 * Scr->MenuShadowDepth;
1136	}
1137	if (Scr->Shadow && ! mr->pinned)
1138	{
1139	    /*
1140	     * Make sure that you don't draw into the shadow window or else
1141	     * the background bits there will get saved
1142	     */
1143	    valuemask = (CWBackPixel | CWBorderPixel);
1144	    attributes.background_pixel = Scr->MenuShadowColor;
1145	    attributes.border_pixel = Scr->MenuShadowColor;
1146	    if (Scr->SaveUnder) {
1147		valuemask |= CWSaveUnder;
1148		attributes.save_under = True;
1149	    }
1150	    mr->shadow = XCreateWindow (dpy, Scr->Root, 0, 0,
1151					(unsigned int) mr->width,
1152					(unsigned int) mr->height,
1153					(unsigned int)0,
1154					CopyFromParent,
1155					(unsigned int) CopyFromParent,
1156					(Visual *) CopyFromParent,
1157					valuemask, &attributes);
1158	}
1159
1160	valuemask = (CWBackPixel | CWBorderPixel | CWEventMask);
1161	attributes.background_pixel = Scr->MenuC.back;
1162	attributes.border_pixel = Scr->MenuC.fore;
1163	if (mr->pinned) {
1164	    attributes.event_mask = (ExposureMask | EnterWindowMask
1165				| LeaveWindowMask | ButtonPressMask
1166				| ButtonReleaseMask | PointerMotionMask
1167				| ButtonMotionMask
1168				);
1169	    attributes.cursor = Scr->MenuCursor;
1170	    valuemask |= CWCursor;
1171	}
1172	else
1173	    attributes.event_mask = (ExposureMask | EnterWindowMask);
1174
1175	if (Scr->SaveUnder && ! mr->pinned) {
1176	    valuemask |= CWSaveUnder;
1177	    attributes.save_under = True;
1178	}
1179	if (Scr->BackingStore) {
1180	    valuemask |= CWBackingStore;
1181	    attributes.backing_store = Always;
1182	}
1183	borderwidth = Scr->use3Dmenus ? 0 : 1;
1184	mr->w = XCreateWindow (dpy, Scr->Root, 0, 0, (unsigned int) mr->width,
1185			       (unsigned int) mr->height, (unsigned int) borderwidth,
1186			       CopyFromParent, (unsigned int) CopyFromParent,
1187			       (Visual *) CopyFromParent,
1188			       valuemask, &attributes);
1189
1190
1191	XSaveContext(dpy, mr->w, MenuContext, (XPointer)mr);
1192	XSaveContext(dpy, mr->w, ScreenContext, (XPointer)Scr);
1193
1194	mr->mapped = UNMAPPED;
1195    }
1196
1197    if (Scr->use3Dmenus && (Scr->Monochrome == COLOR) &&  (mr->highlight.back == UNUSED_PIXEL)) {
1198	XColor xcol;
1199	char colname [32];
1200	short save;
1201
1202	xcol.pixel = Scr->MenuC.back;
1203	XQueryColor (dpy, cmap, &xcol);
1204	sprintf (colname, "#%04x%04x%04x",
1205		5 * ((int)xcol.red   / 6),
1206		5 * ((int)xcol.green / 6),
1207		5 * ((int)xcol.blue  / 6));
1208	save = Scr->FirstTime;
1209	Scr->FirstTime = True;
1210	GetColor (Scr->Monochrome, &mr->highlight.back, colname);
1211	Scr->FirstTime = save;
1212    }
1213
1214    if (Scr->use3Dmenus && (Scr->Monochrome == COLOR) && (mr->highlight.fore == UNUSED_PIXEL)) {
1215	XColor xcol;
1216	char colname [32];
1217	short save;
1218
1219	xcol.pixel = Scr->MenuC.fore;
1220	XQueryColor (dpy, cmap, &xcol);
1221	sprintf (colname, "#%04x%04x%04x",
1222		5 * ((int)xcol.red   / 6),
1223		5 * ((int)xcol.green / 6),
1224		5 * ((int)xcol.blue  / 6));
1225	save = Scr->FirstTime;
1226	Scr->FirstTime = True;
1227	GetColor (Scr->Monochrome, &mr->highlight.fore, colname);
1228	Scr->FirstTime = save;
1229    }
1230    if (Scr->use3Dmenus && !Scr->BeNiceToColormap) GetShadeColors (&mr->highlight);
1231
1232    /* get the default colors into the menus */
1233    for (tmp = mr->first; tmp != NULL; tmp = tmp->next)
1234    {
1235	if (!tmp->user_colors) {
1236	    if (tmp->func != F_TITLE) {
1237		tmp->normal.fore = Scr->MenuC.fore;
1238		tmp->normal.back = Scr->MenuC.back;
1239	    } else {
1240		tmp->normal.fore = Scr->MenuTitleC.fore;
1241		tmp->normal.back = Scr->MenuTitleC.back;
1242	    }
1243	}
1244
1245	if (mr->highlight.fore != UNUSED_PIXEL)
1246	{
1247	    tmp->highlight.fore = mr->highlight.fore;
1248	    tmp->highlight.back = mr->highlight.back;
1249	}
1250	else
1251	{
1252	    tmp->highlight.fore = tmp->normal.back;
1253	    tmp->highlight.back = tmp->normal.fore;
1254	}
1255	if (Scr->use3Dmenus && !Scr->BeNiceToColormap) {
1256	    if (tmp->func != F_TITLE)
1257		GetShadeColors (&tmp->highlight);
1258	    else
1259		GetShadeColors (&tmp->normal);
1260	}
1261    }
1262    mr->pmenu = NULL;
1263
1264    if (Scr->Monochrome == MONOCHROME || !Scr->InterpolateMenuColors)
1265	return 0;
1266
1267    start = mr->first;
1268    while (TRUE)
1269    {
1270	for (; start != NULL; start = start->next)
1271	{
1272	    if (start->user_colors)
1273		break;
1274	}
1275	if (start == NULL)
1276	    break;
1277
1278	for (end = start->next; end != NULL; end = end->next)
1279	{
1280	    if (end->user_colors)
1281		break;
1282	}
1283	if (end == NULL)
1284	    break;
1285
1286	/* we have a start and end to interpolate between */
1287	num = end->item_num - start->item_num;
1288
1289	f1.pixel = start->normal.fore;
1290	XQueryColor(dpy, cmap, &f1);
1291	f2.pixel = end->normal.fore;
1292	XQueryColor(dpy, cmap, &f2);
1293
1294	b1.pixel = start->normal.back;
1295	XQueryColor(dpy, cmap, &b1);
1296	b2.pixel = end->normal.back;
1297	XQueryColor(dpy, cmap, &b2);
1298
1299	fred = ((int)f2.red - (int)f1.red) / num;
1300	fgreen = ((int)f2.green - (int)f1.green) / num;
1301	fblue = ((int)f2.blue - (int)f1.blue) / num;
1302
1303	bred = ((int)b2.red - (int)b1.red) / num;
1304	bgreen = ((int)b2.green - (int)b1.green) / num;
1305	bblue = ((int)b2.blue - (int)b1.blue) / num;
1306
1307	f3 = f1;
1308	f3.flags = DoRed | DoGreen | DoBlue;
1309
1310	b3 = b1;
1311	b3.flags = DoRed | DoGreen | DoBlue;
1312
1313	start->highlight.back = start->normal.fore;
1314	start->highlight.fore = start->normal.back;
1315	num -= 1;
1316	for (i = 0, cur = start->next; i < num; i++, cur = cur->next)
1317	{
1318	    f3.red += fred;
1319	    f3.green += fgreen;
1320	    f3.blue += fblue;
1321	    save_fore = f3;
1322
1323	    b3.red += bred;
1324	    b3.green += bgreen;
1325	    b3.blue += bblue;
1326	    save_back = b3;
1327
1328	    XAllocColor(dpy, cmap, &f3);
1329	    XAllocColor(dpy, cmap, &b3);
1330	    cur->highlight.back = cur->normal.fore = f3.pixel;
1331	    cur->highlight.fore = cur->normal.back = b3.pixel;
1332	    cur->user_colors = True;
1333
1334	    f3 = save_fore;
1335	    b3 = save_back;
1336	}
1337	start = end;
1338	start->highlight.back = start->normal.fore;
1339	start->highlight.fore = start->normal.back;
1340    }
1341    return 1;
1342}
1343
1344
1345
1346/***********************************************************************
1347 *
1348 *  Procedure:
1349 *	PopUpMenu - pop up a pull down menu
1350 *
1351 *  Inputs:
1352 *	menu	- the root pointer of the menu to pop up
1353 *	x, y	- location of upper left of menu
1354 *      center	- whether or not to center horizontally over position
1355 *
1356 ***********************************************************************
1357 */
1358
1359Bool PopUpMenu (MenuRoot *menu, int x, int y, Bool center)
1360{
1361    int WindowNameCount;
1362    TwmWindow **WindowNames;
1363    TwmWindow *tmp_win2,*tmp_win3;
1364    int i;
1365    int xl, yt;
1366    Bool clipped;
1367#ifdef CLAUDE
1368    char tmpname3 [256], tmpname4 [256];
1369    int hasmoz = 0;
1370#endif
1371    if (!menu) return False;
1372
1373    InstallRootColormap();
1374
1375    if ((menu == Scr->Windows) ||
1376	(menu == Scr->Icons) ||
1377	(menu == Scr->AllWindows) ||
1378	/* Added by Dan 'dl' Lilliehorn 040607 */
1379	(menu == Scr->AllIcons) ||
1380	/* Added by Dan Lilliehorn (dl@dl.nu) 2000-02-29 */
1381	(menu == Scr->Visible))
1382    {
1383	TwmWindow *tmp_win;
1384	WorkSpace *ws;
1385	Boolean all, icons, visible_, allicons; /* visible, allicons:
1386						  Added by dl */
1387	int func;
1388
1389	/* this is the twm windows menu,  let's go ahead and build it */
1390
1391	all = (menu == Scr->AllWindows);
1392	icons = (menu == Scr->Icons);
1393	visible_ = (menu == Scr->Visible);    /* Added by dl */
1394	allicons = (menu == Scr->AllIcons);
1395	DestroyMenu (menu);
1396
1397	menu->first = NULL;
1398	menu->last = NULL;
1399	menu->items = 0;
1400	menu->width = 0;
1401	menu->mapped = NEVER_MAPPED;
1402	menu->highlight.fore = UNUSED_PIXEL;
1403	menu->highlight.back = UNUSED_PIXEL;
1404	if (menu == Scr->Windows)
1405  	    AddToMenu(menu, "TWM Windows", NULLSTR, NULL, F_TITLE,NULLSTR,NULLSTR);
1406	else
1407	if (menu == Scr->Icons)
1408  	    AddToMenu(menu, "TWM Icons", NULLSTR, NULL, F_TITLE, NULLSTR, NULLSTR);
1409	else
1410	if (menu == Scr->Visible) /* Added by dl 2000 */
1411	    AddToMenu(menu, "TWM Visible", NULLSTR, NULL, F_TITLE, NULLSTR, NULLSTR);
1412	else
1413	if (menu == Scr->AllIcons) /* Added by dl 2004 */
1414	    AddToMenu(menu, "TWM All Icons", NULLSTR, NULL, F_TITLE, NULLSTR, NULLSTR);
1415	else
1416  	    AddToMenu(menu, "TWM All Windows", NULLSTR, NULL, F_TITLE,NULLSTR,NULLSTR);
1417
1418	ws = NULL;
1419
1420	if (! (all || allicons)
1421	    && CurrentSelectedWorkspace && Scr->workSpaceManagerActive) {
1422	    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
1423		if (strcmp (ws->name, CurrentSelectedWorkspace) == 0) break;
1424	    }
1425	}
1426	if (!Scr->currentvs) return False;
1427	if (!ws) ws = Scr->currentvs->wsw->currentwspc;
1428
1429        for (tmp_win = Scr->FirstWindow, WindowNameCount = 0;
1430             tmp_win != NULL;
1431             tmp_win = tmp_win->next) {
1432	  if (tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) continue;
1433	  if (Scr->ShortAllWindowsMenus && (tmp_win->wspmgr || tmp_win->iconmgr)) continue;
1434
1435	  if (!(all || allicons) && !OCCUPY (tmp_win, ws)) continue;
1436	  if (allicons && !tmp_win->isicon) continue;
1437	  if (icons && !tmp_win->isicon) continue;
1438	  if (visible_ && tmp_win->isicon) continue;  /* added by dl */
1439	  WindowNameCount++;
1440	}
1441        WindowNames = (TwmWindow **)malloc(sizeof(TwmWindow *)*WindowNameCount);
1442	WindowNameCount = 0;
1443        for (tmp_win = Scr->FirstWindow;
1444             tmp_win != NULL;
1445             tmp_win = tmp_win->next)
1446        {
1447	    if (LookInList (Scr->IconMenuDontShow, tmp_win->full_name, &tmp_win->class)) continue;
1448
1449	    if (tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) continue;
1450	    if (Scr->ShortAllWindowsMenus &&
1451		tmp_win == Scr->currentvs->wsw->twm_win) continue;
1452	    if (Scr->ShortAllWindowsMenus && tmp_win->iconmgr) continue;
1453
1454	    if (!(all || allicons)&& ! OCCUPY (tmp_win, ws)) continue;
1455	    if (allicons && !tmp_win->isicon) continue;
1456	    if (icons && !tmp_win->isicon) continue;
1457	    if (visible_ && tmp_win->isicon) continue;  /* added by dl */
1458            tmp_win2 = tmp_win;
1459
1460            for (i = 0; i < WindowNameCount; i++) {
1461		int compresult;
1462		char *tmpname1, *tmpname2;
1463		tmpname1 = tmp_win2->name;
1464		tmpname2 = WindowNames[i]->name;
1465#ifdef CLAUDE
1466		if (strlen (tmpname1) == 1) tmpname1 = " No title";
1467		if (strlen (tmpname2) == 1) tmpname2 = " No title";
1468
1469                if (!strncasecmp (tmp_win2->class.res_class, "navigator", 9) ||
1470		    !strncasecmp (tmp_win2->class.res_class, "mozilla",   7)) {
1471		  tmpname3 [0] = ' '; tmpname3 [1] = '\0';
1472		  strcat (tmpname3, tmpname1);
1473		} else {
1474		  strcpy (tmpname3, tmpname1);
1475		}
1476                if (!strncasecmp (WindowNames[i]->class.res_class, "navigator", 9) ||
1477		    !strncasecmp (WindowNames[i]->class.res_class, "mozilla",   7)) {
1478		  tmpname4 [0] = ' '; tmpname4 [1] = '\0';
1479		  strcat (tmpname4, tmpname2);
1480		} else {
1481		  strcpy (tmpname4, tmpname2);
1482		}
1483		tmpname1 = tmpname3;
1484		tmpname2 = tmpname4;
1485#endif
1486		if (Scr->CaseSensitive)
1487		    compresult = strcmp(tmpname1,tmpname2);
1488		else
1489		    compresult = XmuCompareISOLatin1(tmpname1,tmpname2);
1490                if (compresult < 0) {
1491                    tmp_win3 = tmp_win2;
1492                    tmp_win2 = WindowNames[i];
1493                    WindowNames[i] = tmp_win3;
1494                }
1495            }
1496            WindowNames[WindowNameCount] = tmp_win2;
1497	    WindowNameCount++;
1498        }
1499	func = (all || allicons || CurrentSelectedWorkspace) ? F_WINWARP :
1500	      F_POPUP;
1501        for (i = 0; i < WindowNameCount; i++)
1502        {
1503	    char *tmpname;
1504	    tmpname = WindowNames[i]->name;
1505#ifdef CLAUDE
1506	    if (!strncasecmp (WindowNames[i]->class.res_class, "navigator", 9) ||
1507		!strncasecmp (WindowNames[i]->class.res_class, "mozilla",   7) ||
1508		!strncasecmp (WindowNames[i]->class.res_class, "netscape",  8) ||
1509		!strncasecmp (WindowNames[i]->class.res_class, "konqueror", 9)) {
1510	      hasmoz = 1;
1511	    }
1512	    if (hasmoz && strncasecmp (WindowNames[i]->class.res_class, "navigator", 9) &&
1513		          strncasecmp (WindowNames[i]->class.res_class, "mozilla",   7) &&
1514		          strncasecmp (WindowNames[i]->class.res_class, "netscape",  8) &&
1515		          strncasecmp (WindowNames[i]->class.res_class, "konqueror", 9)) {
1516	      menu->last->separated = 1;
1517	      hasmoz = 0;
1518	    }
1519#endif
1520            AddToMenu(menu, tmpname, (char *)WindowNames[i],
1521                      NULL, func,NULL,NULL);
1522        }
1523        free(WindowNames);
1524
1525	menu->pinned = False;
1526	MakeMenu(menu);
1527    }
1528
1529    /* Keys added by dl */
1530
1531    if (menu == Scr->Keys) {
1532	FuncKey *tmpKey;
1533	char *tmpStr, *tmpStr2;
1534	char modStr[5];
1535	char *oldact = 0;
1536	int oldmod = 0;
1537	int tmpLen;
1538
1539	DestroyMenu (menu);
1540
1541	menu->first = NULL;
1542	menu->last = NULL;
1543	menu->items = 0;
1544	menu->width = 0;
1545	menu->mapped = NEVER_MAPPED;
1546	menu->highlight.fore = UNUSED_PIXEL;
1547	menu->highlight.back = UNUSED_PIXEL;
1548
1549	AddToMenu(menu, "Twm Keys", NULLSTR, NULL, F_TITLE, NULLSTR, NULLSTR);
1550
1551	for (tmpKey = Scr->FuncKeyRoot.next; tmpKey != NULL;  tmpKey = tmpKey->next) {
1552	    if (tmpKey->func != F_EXEC) continue;
1553	    if ((tmpKey->action == oldact) && (tmpKey->mods == oldmod)) continue;
1554	    strcpy (modStr, "");
1555	    switch (tmpKey->mods) {
1556	        case  1: strcpy (modStr, "S");     break;
1557	        case  4: strcpy (modStr, "C");     break;
1558		case  5: strcpy (modStr, "S + C"); break;
1559		case  8: strcpy (modStr, "M");     break;
1560		case  9: strcpy (modStr, "S + M"); break;
1561		case 12: strcpy (modStr, "C + M"); break;
1562		default: break;
1563	    }
1564	    tmpLen = (strlen (tmpKey->name) + strlen (modStr) + 5);
1565	    tmpStr = malloc (sizeof(char) * tmpLen);
1566	    sprintf (tmpStr,"[%s + %s]", tmpKey->name, modStr);
1567	    tmpStr2 = malloc (sizeof(char) * (strlen (tmpKey->action) + tmpLen + 2));
1568	    sprintf (tmpStr2, "%s %s", tmpStr, tmpKey->action);
1569
1570	    AddToMenu (menu, tmpStr2, tmpKey->action, NULL, tmpKey->func, NULLSTR, NULLSTR);
1571	    oldact = tmpKey->action;
1572	    oldmod = tmpKey->mods;
1573	}
1574	menu->pinned = False;
1575	MakeMenu(menu);
1576    }
1577    if (menu->w == None || menu->items == 0) return False;
1578
1579    /* Prevent recursively bringing up menus. */
1580    if ((!menu->pinned) && (menu->mapped == MAPPED)) return False;
1581
1582    /*
1583     * Dynamically set the parent;  this allows pull-ups to also be main
1584     * menus, or to be brought up from more than one place.
1585     */
1586    menu->prev = ActiveMenu;
1587
1588    if (menu->pinned) {
1589	ActiveMenu    = menu;
1590	menu->mapped  = MAPPED;
1591	menu->entered = TRUE;
1592	MenuOrigins [MenuDepth].x = menu->x;
1593	MenuOrigins [MenuDepth].y = menu->y;
1594	MenuDepth++;
1595
1596	XRaiseWindow (dpy, menu->w);
1597	return (True);
1598    }
1599
1600    XGrabPointer(dpy, Scr->Root, True,
1601	ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
1602	ButtonMotionMask | PointerMotionHintMask,
1603	GrabModeAsync, GrabModeAsync,
1604	Scr->Root,
1605	Scr->MenuCursor, CurrentTime);
1606
1607    XGrabKeyboard (dpy, Scr->Root, True, GrabModeAsync, GrabModeAsync, CurrentTime);
1608
1609    ActiveMenu = menu;
1610    menu->mapped = MAPPED;
1611    menu->entered = FALSE;
1612
1613    if (center) {
1614	x -= (menu->width / 2);
1615	y -= (Scr->EntryHeight / 2);	/* sticky menus would be nice here */
1616    }
1617
1618    /*
1619    * clip to screen
1620    */
1621    clipped = FALSE;
1622    if (x + menu->width > Scr->rootw) {
1623	x = Scr->rootw - menu->width;
1624	clipped = TRUE;
1625    }
1626    if (x < 0) {
1627	x = 0;
1628	clipped = TRUE;
1629    }
1630    if (y + menu->height > Scr->rooth) {
1631	y = Scr->rooth - menu->height;
1632	clipped = TRUE;
1633    }
1634    if (y < 0) {
1635	y = 0;
1636	clipped = TRUE;
1637    }
1638    MenuOrigins[MenuDepth].x = x;
1639    MenuOrigins[MenuDepth].y = y;
1640    MenuDepth++;
1641
1642    if (Scr->Root != Scr->CaptiveRoot) {
1643      XReparentWindow (dpy, menu->shadow, Scr->Root, x, y);
1644      XReparentWindow (dpy, menu->w, Scr->Root, x, y);
1645    } else
1646      XMoveWindow (dpy, menu->w, x, y);
1647    if (Scr->Shadow) {
1648	XMoveWindow  (dpy, menu->shadow, x + SHADOWWIDTH, y + SHADOWWIDTH);
1649	XRaiseWindow (dpy, menu->shadow);
1650    }
1651    XMapRaised(dpy, menu->w);
1652if (!Scr->NoWarpToMenuTitle && clipped && center) {
1653	xl = x + (menu->width      / 2);
1654	yt = y + (Scr->EntryHeight / 2);
1655	XWarpPointer (dpy, Scr->Root, Scr->Root, x, y, menu->width, menu->height, xl, yt);
1656    }
1657    if (Scr->Shadow) XMapWindow (dpy, menu->shadow);
1658    XSync(dpy, 0);
1659    return True;
1660}
1661
1662
1663
1664/***********************************************************************
1665 *
1666 *  Procedure:
1667 *	PopDownMenu - unhighlight the current menu selection and
1668 *		take down the menus
1669 *
1670 ***********************************************************************
1671 */
1672
1673int PopDownMenu(void)
1674{
1675    MenuRoot *tmp;
1676
1677    if (ActiveMenu == NULL)
1678	return (1);
1679
1680    if (ActiveItem)
1681    {
1682	ActiveItem->state = 0;
1683	PaintEntry(ActiveMenu, ActiveItem, False);
1684    }
1685
1686    for (tmp = ActiveMenu; tmp != NULL; tmp = tmp->prev)
1687    {
1688	if (! tmp->pinned) HideMenu (tmp);
1689	UninstallRootColormap();
1690    }
1691
1692    XFlush(dpy);
1693    ActiveMenu = NULL;
1694    ActiveItem = NULL;
1695    MenuDepth = 0;
1696    XUngrabKeyboard (dpy, CurrentTime);
1697    if (Context == C_WINDOW || Context == C_FRAME || Context == C_TITLE || Context == C_ICON)
1698      menuFromFrameOrWindowOrTitlebar = TRUE;
1699
1700    return 1;
1701}
1702
1703
1704
1705Bool HideMenu (MenuRoot *menu)
1706{
1707    if (!menu) return False;
1708
1709    if (Scr->Shadow) {
1710	XUnmapWindow (dpy, menu->shadow);
1711    }
1712    XUnmapWindow (dpy, menu->w);
1713    menu->mapped = UNMAPPED;
1714
1715    return True;
1716}
1717
1718/***********************************************************************
1719 *
1720 *  Procedure:
1721 *	FindMenuRoot - look for a menu root
1722 *
1723 *  Returned Value:
1724 *	(MenuRoot *)  - a pointer to the menu root structure
1725 *
1726 *  Inputs:
1727 *	name	- the name of the menu root
1728 *
1729 ***********************************************************************
1730 */
1731
1732MenuRoot *FindMenuRoot(char *name)
1733{
1734    MenuRoot *tmp;
1735
1736    for (tmp = Scr->MenuList; tmp != NULL; tmp = tmp->next)
1737    {
1738	if (strcmp(name, tmp->name) == 0)
1739	    return (tmp);
1740    }
1741    return NULL;
1742}
1743
1744
1745
1746static Bool belongs_to_twm_window (register TwmWindow *t, register Window w)
1747{
1748    if (!t) return False;
1749
1750    if (w == t->frame || w == t->title_w || w == t->hilite_wl || w == t->hilite_wr ||
1751	(t->icon && (w == t->icon->w || w == t->icon->bm_w))) return True;
1752
1753    if (t && t->titlebuttons) {
1754	register TBWindow *tbw;
1755	register int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
1756	for (tbw = t->titlebuttons; nb > 0; tbw++, nb--) {
1757	    if (tbw->window == w) return True;
1758	}
1759    }
1760    return False;
1761}
1762
1763
1764
1765
1766/***********************************************************************
1767 *
1768 *  Procedure:
1769 *	resizeFromCenter -
1770 *
1771 ***********************************************************************
1772 */
1773
1774void resizeFromCenter(Window w, TwmWindow *tmp_win)
1775{
1776  int lastx, lasty, bw2;
1777  int namelen;
1778  XRectangle inc_rect;
1779  XRectangle logical_rect;
1780
1781  namelen = strlen (tmp_win->name);
1782  bw2 = tmp_win->frame_bw * 2;
1783  AddingW = tmp_win->attr.width + bw2 + 2 * tmp_win->frame_bw3D;
1784  AddingH = tmp_win->attr.height + tmp_win->title_height + bw2 + 2 * tmp_win->frame_bw3D;
1785
1786  XmbTextExtents(Scr->SizeFont.font_set, tmp_win->name, namelen,
1787		 &inc_rect, &logical_rect);
1788
1789  XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY,
1790	       &DragWidth, &DragHeight,
1791	       &JunkBW, &JunkDepth);
1792
1793  XWarpPointer(dpy, None, w,
1794	       0, 0, 0, 0, DragWidth/2, DragHeight/2);
1795  XQueryPointer (dpy, Scr->Root, &JunkRoot,
1796		 &JunkChild, &JunkX, &JunkY,
1797		 &AddingX, &AddingY, &JunkMask);
1798
1799  lastx = -10000;
1800  lasty = -10000;
1801
1802  MenuStartResize(tmp_win, origDragX, origDragY, DragWidth, DragHeight);
1803  while (TRUE)
1804    {
1805      XMaskEvent(dpy,
1806		 ButtonPressMask | PointerMotionMask | ExposureMask, &Event);
1807
1808      if (Event.type == MotionNotify) {
1809	/* discard any extra motion events before a release */
1810	while(XCheckMaskEvent(dpy,
1811			      ButtonMotionMask | ButtonPressMask, &Event))
1812	  if (Event.type == ButtonPress)
1813	    break;
1814      }
1815
1816      if (Event.type == ButtonPress)
1817	{
1818	  MenuEndResize(tmp_win);
1819	  XMoveResizeWindow(dpy, w, AddingX, AddingY, AddingW, AddingH);
1820	  break;
1821	}
1822
1823      if (Event.type != MotionNotify) {
1824	(void)DispatchEvent2 ();
1825	continue;
1826      }
1827
1828      /*
1829       * XXX - if we are going to do a loop, we ought to consider
1830       * using multiple GXxor lines so that we don't need to
1831       * grab the server.
1832       */
1833      XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkChild,
1834		    &JunkX, &JunkY, &AddingX, &AddingY, &JunkMask);
1835
1836      if (lastx != AddingX || lasty != AddingY)
1837	{
1838	  MenuDoResize(AddingX, AddingY, tmp_win);
1839
1840	  lastx = AddingX;
1841	  lasty = AddingY;
1842	}
1843
1844    }
1845}
1846
1847
1848
1849/***********************************************************************
1850 *
1851 *  Procedure:
1852 *	ExecuteFunction - execute a twm root function
1853 *
1854 *  Inputs:
1855 *	func	- the function to execute
1856 *	action	- the menu action to execute
1857 *	w	- the window to execute this function on
1858 *	tmp_win	- the twm window structure
1859 *	event	- the event that caused the function
1860 *	context - the context in which the button was pressed
1861 *	pulldown- flag indicating execution from pull down menu
1862 *
1863 *  Returns:
1864 *	TRUE if should continue with remaining actions else FALSE to abort
1865 *
1866 ***********************************************************************
1867 */
1868
1869int ExecuteFunction(int func, void *action, Window w, TwmWindow *tmp_win,
1870		    XEvent *eventp, int context, int pulldown)
1871{
1872    static Time last_time = 0;
1873    char tmp[200];
1874    char *ptr;
1875    char buff[MAX_FILE_SIZE];
1876    int count, fd;
1877    Window rootw;
1878    int origX, origY;
1879    int do_next_action = TRUE;
1880    int moving_icon = FALSE;
1881    Bool fromtitlebar = False;
1882    Bool from3dborder = False;
1883    TwmWindow *t;
1884
1885    RootFunction = 0;
1886    if (Cancel)
1887	return TRUE;			/* XXX should this be FALSE? */
1888
1889    switch (func)
1890    {
1891    case F_UPICONMGR:
1892    case F_LEFTICONMGR:
1893    case F_RIGHTICONMGR:
1894    case F_DOWNICONMGR:
1895    case F_FORWICONMGR:
1896    case F_BACKICONMGR:
1897    case F_NEXTICONMGR:
1898    case F_PREVICONMGR:
1899    case F_NOP:
1900    case F_TITLE:
1901    case F_DELTASTOP:
1902    case F_RAISELOWER:
1903    case F_WARPTOSCREEN:
1904    case F_WARPTO:
1905    case F_WARPRING:
1906    case F_WARPTOICONMGR:
1907    case F_COLORMAP:
1908    case F_ALTKEYMAP:
1909    case F_ALTCONTEXT:
1910	break;
1911
1912    default:
1913        XGrabPointer(dpy, Scr->Root, True,
1914            ButtonPressMask | ButtonReleaseMask,
1915            GrabModeAsync, GrabModeAsync,
1916            Scr->Root, Scr->WaitCursor, CurrentTime);
1917	break;
1918    }
1919
1920    switch (func)
1921    {
1922#ifdef SOUNDS
1923    case F_TOGGLESOUND:
1924	toggle_sound();
1925	break;
1926    case F_REREADSOUNDS:
1927	reread_sounds();
1928	break;
1929#endif
1930    case F_NOP:
1931    case F_TITLE:
1932	break;
1933
1934    case F_DELTASTOP:
1935	if (WindowMoved) do_next_action = FALSE;
1936	break;
1937
1938    case F_RESTART: {
1939	DoRestart(eventp->xbutton.time);
1940	break;
1941    }
1942    case F_UPICONMGR:
1943    case F_DOWNICONMGR:
1944    case F_LEFTICONMGR:
1945    case F_RIGHTICONMGR:
1946    case F_FORWICONMGR:
1947    case F_BACKICONMGR:
1948	MoveIconManager(func);
1949        break;
1950
1951    case F_FORWMAPICONMGR:
1952    case F_BACKMAPICONMGR:
1953	MoveMappedIconManager(func);
1954	break;
1955
1956    case F_NEXTICONMGR:
1957    case F_PREVICONMGR:
1958	JumpIconManager(func);
1959        break;
1960
1961    case F_SHOWLIST:
1962	if (Scr->NoIconManagers) break;
1963	ShowIconManager ();
1964	break;
1965
1966    case F_STARTANIMATION :
1967	StartAnimation ();
1968	break;
1969
1970    case F_STOPANIMATION :
1971	StopAnimation ();
1972	break;
1973
1974    case F_SPEEDUPANIMATION :
1975	ModifyAnimationSpeed (1);
1976	break;
1977
1978    case F_SLOWDOWNANIMATION :
1979	ModifyAnimationSpeed (-1);
1980	break;
1981
1982    case F_HIDELIST:
1983	if (Scr->NoIconManagers) break;
1984	HideIconManager ();
1985	break;
1986
1987    case F_SHOWWORKMGR:
1988	if (! Scr->workSpaceManagerActive) break;
1989	DeIconify (Scr->currentvs->wsw->twm_win);
1990	RaiseWindow(Scr->currentvs->wsw->twm_win);
1991	break;
1992
1993    case F_HIDEWORKMGR:
1994	if (! Scr->workSpaceManagerActive) break;
1995	Iconify (Scr->currentvs->wsw->twm_win, eventp->xbutton.x_root - 5,
1996		     eventp->xbutton.y_root - 5);
1997	break;
1998
1999    case F_TOGGLEWORKMGR:
2000	if (! Scr->workSpaceManagerActive) break;
2001	if (Scr->currentvs->wsw->twm_win->mapped)
2002	    Iconify (Scr->currentvs->wsw->twm_win, eventp->xbutton.x_root - 5,
2003		     eventp->xbutton.y_root - 5);
2004	else {
2005	    DeIconify (Scr->currentvs->wsw->twm_win);
2006	    RaiseWindow(Scr->currentvs->wsw->twm_win);
2007	}
2008	break;
2009
2010    case F_TOGGLESTATE :
2011	WMapToggleState (Scr->currentvs);
2012	break;
2013
2014    case F_SETBUTTONSTATE :
2015	WMapSetButtonsState (Scr->currentvs);
2016	break;
2017
2018    case F_SETMAPSTATE :
2019	WMapSetMapState (Scr->currentvs);
2020	break;
2021
2022    case F_PIN :
2023	if (! ActiveMenu) break;
2024	if (ActiveMenu->pinned) {
2025	    XUnmapWindow (dpy, ActiveMenu->w);
2026	    ActiveMenu->mapped = UNMAPPED;
2027	}
2028	else {
2029	    XWindowAttributes attr;
2030	    MenuRoot *menu;
2031
2032	    if (ActiveMenu->pmenu == NULL) {
2033		menu  = (MenuRoot*) malloc (sizeof (struct MenuRoot));
2034		*menu = *ActiveMenu;
2035		menu->pinned = True;
2036		menu->mapped = NEVER_MAPPED;
2037		menu->width -= 10;
2038		if (menu->pull) menu->width -= 16 + 10;
2039		MakeMenu (menu);
2040		ActiveMenu->pmenu = menu;
2041	    }
2042	    else menu = ActiveMenu->pmenu;
2043	    if (menu->mapped == MAPPED) break;
2044	    XGetWindowAttributes (dpy, ActiveMenu->w, &attr);
2045	    menu->x = attr.x;
2046	    menu->y = attr.y;
2047	    XMoveWindow (dpy, menu->w, menu->x, menu->y);
2048	    XMapRaised  (dpy, menu->w);
2049	    menu->mapped = MAPPED;
2050	}
2051	PopDownMenu();
2052	break;
2053
2054    case F_MOVEMENU:
2055	break;
2056
2057    case F_FITTOCONTENT :
2058	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2059	if (!tmp_win->iswinbox) {
2060	    XBell (dpy, 0);
2061	    break;
2062	}
2063	fittocontent (tmp_win);
2064	break;
2065
2066    case F_VANISH:
2067	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2068
2069	WMgrRemoveFromCurrentWorkSpace (Scr->currentvs, tmp_win);
2070	break;
2071
2072    case F_WARPHERE:
2073	WMgrAddToCurrentWorkSpaceAndWarp (Scr->currentvs, action);
2074	break;
2075
2076    case F_ADDTOWORKSPACE:
2077	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2078	AddToWorkSpace (action, tmp_win);
2079	break;
2080
2081    case F_REMOVEFROMWORKSPACE:
2082	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2083	RemoveFromWorkSpace (action, tmp_win);
2084	break;
2085
2086    case F_TOGGLEOCCUPATION:
2087	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2088	ToggleOccupation (action, tmp_win);
2089	break;
2090
2091    case F_MOVETONEXTWORKSPACE:
2092		if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2093		MoveToNextWorkSpace(Scr->currentvs,tmp_win);
2094		break;
2095
2096    case F_MOVETOPREVWORKSPACE:
2097		if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2098		MoveToPrevWorkSpace(Scr->currentvs,tmp_win);
2099		break;
2100
2101    case F_MOVETONEXTWORKSPACEANDFOLLOW:
2102		if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2103		MoveToNextWorkSpaceAndFollow(Scr->currentvs,tmp_win);
2104		break;
2105
2106    case F_MOVETOPREVWORKSPACEANDFOLLOW:
2107		if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2108		MoveToPrevWorkSpaceAndFollow(Scr->currentvs,tmp_win);
2109		break;
2110
2111    case F_SORTICONMGR:
2112	if (DeferExecution(context, func, Scr->SelectCursor))
2113	    return TRUE;
2114
2115	{
2116	    int save_sort;
2117
2118	    save_sort = Scr->SortIconMgr;
2119	    Scr->SortIconMgr = TRUE;
2120
2121	    if (context == C_ICONMGR)
2122		SortIconManager((IconMgr *) NULL);
2123	    else if (tmp_win->iconmgr)
2124		SortIconManager(tmp_win->iconmgrp);
2125	    else
2126		XBell(dpy, 0);
2127
2128	    Scr->SortIconMgr = save_sort;
2129	}
2130	break;
2131
2132    case F_ALTKEYMAP: {
2133	int alt, stat_;
2134
2135	if (! action) return TRUE;
2136	stat_ = sscanf (action, "%d", &alt);
2137	if (stat_ != 1) return TRUE;
2138	if ((alt < 1) || (alt > 5)) return TRUE;
2139	AlternateKeymap = Alt1Mask << (alt - 1);
2140	XGrabPointer (dpy, Scr->Root, True, ButtonPressMask | ButtonReleaseMask,
2141			GrabModeAsync, GrabModeAsync,
2142			Scr->Root, Scr->AlterCursor, CurrentTime);
2143	XGrabKeyboard (dpy, Scr->Root, True, GrabModeAsync, GrabModeAsync, CurrentTime);
2144	return TRUE;
2145    }
2146
2147    case F_ALTCONTEXT: {
2148	AlternateContext = True;
2149	XGrabPointer (dpy, Scr->Root, False, ButtonPressMask | ButtonReleaseMask,
2150			GrabModeAsync, GrabModeAsync,
2151			Scr->Root, Scr->AlterCursor, CurrentTime);
2152	XGrabKeyboard (dpy, Scr->Root, False, GrabModeAsync, GrabModeAsync, CurrentTime);
2153	return TRUE;
2154    }
2155    case F_IDENTIFY:
2156	if (DeferExecution(context, func, Scr->SelectCursor))
2157	    return TRUE;
2158
2159	Identify(tmp_win);
2160	break;
2161
2162    case F_INITSIZE: {
2163	int grav, x, y;
2164	unsigned int width, height, swidth, sheight;
2165
2166	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2167	grav = ((tmp_win->hints.flags & PWinGravity)
2168		      ? tmp_win->hints.win_gravity : NorthWestGravity);
2169
2170	if (!(tmp_win->hints.flags & USSize) && !(tmp_win->hints.flags & PSize)) break;
2171
2172	width  = tmp_win->hints.width  + 2 * tmp_win->frame_bw3D;
2173	height  = tmp_win->hints.height + 2 * tmp_win->frame_bw3D + tmp_win->title_height;
2174	ConstrainSize (tmp_win, &width, &height);
2175
2176	x  = tmp_win->frame_x;
2177	y  = tmp_win->frame_y;
2178	swidth = tmp_win->frame_width;
2179	sheight = tmp_win->frame_height;
2180	switch (grav) {
2181	    case ForgetGravity :
2182	    case StaticGravity :
2183	    case NorthWestGravity :
2184	    case NorthGravity :
2185	    case WestGravity :
2186	    case CenterGravity :
2187		break;
2188
2189	    case NorthEastGravity :
2190	    case EastGravity :
2191		x += swidth - width;
2192		break;
2193
2194	    case SouthWestGravity :
2195	    case SouthGravity :
2196		y += sheight - height;
2197		break;
2198
2199	    case SouthEastGravity :
2200		x += swidth - width;
2201		y += sheight - height;
2202		break;
2203	}
2204	SetupWindow (tmp_win, x, y, width, height, -1);
2205	break;
2206    }
2207
2208    case F_MOVERESIZE: {
2209	int x, y, mask;
2210	unsigned int width, height;
2211	int px = 20, py = 30;
2212
2213	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2214	mask = XParseGeometry (action, &x, &y, &width, &height);
2215	if (!(mask &  WidthValue)) width = tmp_win->frame_width;
2216	else width += 2 * tmp_win->frame_bw3D;
2217	if (!(mask & HeightValue)) height = tmp_win->frame_height;
2218	else height += 2 * tmp_win->frame_bw3D + tmp_win->title_height;
2219	ConstrainSize (tmp_win, &width, &height);
2220	if (mask & XValue) {
2221	    if (mask & XNegative) x += Scr->rootw  - width;
2222	} else x = tmp_win->frame_x;
2223	if (mask & YValue) {
2224	    if (mask & YNegative) y += Scr->rooth - height;
2225	} else y = tmp_win->frame_y;
2226
2227	{
2228	    int		 junkX, junkY;
2229	    unsigned int junkK;
2230	    Window	 junkW;
2231	    XQueryPointer (dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &px, &py, &junkK);
2232	}
2233	px -= tmp_win->frame_x; if (px > width) px = width / 2;
2234	py -= tmp_win->frame_y; if (py > height) px = height / 2;
2235	SetupWindow (tmp_win, x, y, width, height, -1);
2236	XWarpPointer (dpy, Scr->Root, Scr->Root, 0, 0, 0, 0, x + px, y + py);
2237	break;
2238    }
2239
2240    case F_VERSION:
2241	Identify ((TwmWindow *) NULL);
2242	break;
2243
2244    case F_AUTORAISE:
2245	if (DeferExecution(context, func, Scr->SelectCursor))
2246	    return TRUE;
2247
2248	tmp_win->auto_raise = !tmp_win->auto_raise;
2249	if (tmp_win->auto_raise) ++(Scr->NumAutoRaises);
2250	else --(Scr->NumAutoRaises);
2251	break;
2252
2253    case F_AUTOLOWER:
2254	if (DeferExecution(context, func, Scr->SelectCursor))
2255	    return TRUE;
2256
2257	tmp_win->auto_lower = !tmp_win->auto_lower;
2258	if (tmp_win->auto_lower) ++(Scr->NumAutoLowers);
2259	else --(Scr->NumAutoLowers);
2260	break;
2261
2262    case F_BEEP:
2263	XBell(dpy, 0);
2264	break;
2265
2266    case F_POPUP:
2267	tmp_win = (TwmWindow *)action;
2268	if (! tmp_win) break;
2269	if (Scr->WindowFunction.func != 0)
2270	{
2271	   ExecuteFunction(Scr->WindowFunction.func,
2272			   Scr->WindowFunction.item->action,
2273			   w, tmp_win, eventp, C_FRAME, FALSE);
2274	}
2275	else
2276	{
2277	    DeIconify(tmp_win);
2278	    RaiseWindow (tmp_win);
2279	}
2280	break;
2281
2282    case F_WINWARP:
2283	tmp_win = (TwmWindow *)action;
2284
2285	if (! tmp_win) break;
2286	if (Scr->WarpUnmapped || tmp_win->mapped) {
2287	    if (!tmp_win->mapped) DeIconify (tmp_win);
2288	    WarpToWindow (tmp_win, Scr->RaiseOnWarp);
2289	}
2290	break;
2291
2292    case F_RESIZE:
2293	EventHandler[EnterNotify] = HandleUnknown;
2294	EventHandler[LeaveNotify] = HandleUnknown;
2295	if (DeferExecution(context, func, Scr->MoveCursor))
2296	    return TRUE;
2297
2298	PopDownMenu();
2299	if (tmp_win->squeezed) {
2300	    XBell (dpy, 0);
2301	    break;
2302	}
2303	if (tmp_win->OpaqueResize) {
2304	    /*
2305	     * OpaqueResize defaults to a thousand.  Assume that any number
2306	     * >= 1000 is "infinity" and don't bother calculating.
2307	     */
2308	    if (Scr->OpaqueResizeThreshold >= 1000)
2309		Scr->OpaqueResize = TRUE;
2310	    else {
2311		/*
2312		 * scrsz will hold the number of pixels in your resolution,
2313		 * which can get big.  [signed] int may not cut it.
2314		 */
2315		unsigned long winsz, scrsz;
2316		winsz = tmp_win->frame_width * tmp_win->frame_height;
2317		scrsz = Scr->rootw  * Scr->rooth;
2318		if (winsz > (scrsz * (Scr->OpaqueResizeThreshold / 100.0)))
2319		    Scr->OpaqueResize = FALSE;
2320		else
2321		    Scr->OpaqueResize = TRUE;
2322	    }
2323	}
2324	else
2325	    Scr->OpaqueResize = FALSE;
2326
2327	if (pulldown)
2328	    XWarpPointer(dpy, None, Scr->Root,
2329		0, 0, 0, 0, eventp->xbutton.x_root, eventp->xbutton.y_root);
2330
2331	if (!tmp_win->icon || (w != tmp_win->icon->w)) {	/* can't resize icons */
2332
2333/*	  fromMenu = False;  ????? */
2334	  if ((Context == C_FRAME || Context == C_WINDOW || Context == C_TITLE)
2335	      && fromMenu)
2336	    resizeFromCenter(w, tmp_win);
2337	  else {
2338	    /*
2339	     * see if this is being done from the titlebar
2340	     */
2341	    from3dborder = (eventp->xbutton.window == tmp_win->frame);
2342	    fromtitlebar = !from3dborder &&
2343	      belongs_to_twm_window (tmp_win, eventp->xbutton.window);
2344
2345	    /* Save pointer position so we can tell if it was moved or
2346	       not during the resize. */
2347	    ResizeOrigX = eventp->xbutton.x_root;
2348	    ResizeOrigY = eventp->xbutton.y_root;
2349
2350	    StartResize (eventp, tmp_win, fromtitlebar, from3dborder);
2351
2352	    do {
2353		XMaskEvent(dpy,
2354			   ButtonPressMask | ButtonReleaseMask |
2355			   EnterWindowMask | LeaveWindowMask |
2356			   ButtonMotionMask | VisibilityChangeMask | ExposureMask, &Event);
2357
2358		if (fromtitlebar && Event.type == ButtonPress) {
2359		    fromtitlebar = False;
2360		    continue;
2361		}
2362
2363	    	if (Event.type == MotionNotify) {
2364		  /* discard any extra motion events before a release */
2365		  while
2366		    (XCheckMaskEvent
2367		     (dpy, ButtonMotionMask | ButtonReleaseMask, &Event))
2368		      if (Event.type == ButtonRelease)
2369			break;
2370		}
2371
2372	      if (!DispatchEvent2 ()) continue;
2373
2374	    } while (!(Event.type == ButtonRelease || Cancel));
2375	    return TRUE;
2376	  }
2377	}
2378	break;
2379
2380
2381    case F_ZOOM:
2382    case F_HORIZOOM:
2383    case F_FULLZOOM:
2384    case F_LEFTZOOM:
2385    case F_RIGHTZOOM:
2386    case F_TOPZOOM:
2387    case F_BOTTOMZOOM:
2388	if (DeferExecution(context, func, Scr->SelectCursor))
2389	    return TRUE;
2390	if (tmp_win->squeezed) {
2391	    XBell(dpy, 0);
2392	    break;
2393	}
2394	fullzoom(tmp_win, func);
2395	break;
2396
2397    case F_PACK:
2398	if (DeferExecution(context, func, Scr->SelectCursor)) return TRUE;
2399	if (tmp_win->squeezed) { XBell(dpy, 0); break; }
2400	packwindow (tmp_win, action);
2401	break;
2402
2403    case F_FILL:
2404	if (DeferExecution(context, func, Scr->SelectCursor)) return TRUE;
2405	if (tmp_win->squeezed) { XBell(dpy, 0); break; }
2406	fillwindow (tmp_win, action);
2407	break;
2408
2409    case F_JUMPLEFT:
2410	if (DeferExecution(context, func, Scr->MoveCursor)) return TRUE;
2411	if (tmp_win->squeezed) { XBell(dpy, 0); break; }
2412	jump (tmp_win, J_LEFT, action);
2413	break;
2414    case F_JUMPRIGHT:
2415	if (DeferExecution(context, func, Scr->MoveCursor)) return TRUE;
2416	if (tmp_win->squeezed) { XBell(dpy, 0); break; }
2417	jump (tmp_win, J_RIGHT, action);
2418	break;
2419    case F_JUMPDOWN:
2420	if (DeferExecution(context, func, Scr->MoveCursor)) return TRUE;
2421	if (tmp_win->squeezed) { XBell(dpy, 0); break; }
2422	jump (tmp_win, J_BOTTOM, action);
2423	break;
2424    case F_JUMPUP:
2425	if (DeferExecution(context, func, Scr->MoveCursor)) return TRUE;
2426	if (tmp_win->squeezed) { XBell(dpy, 0); break; }
2427	jump (tmp_win, J_TOP, action);
2428	break;
2429
2430    case F_SAVEGEOMETRY:
2431	if (DeferExecution(context, func, Scr->SelectCursor)) return TRUE;
2432	savegeometry (tmp_win);
2433	break;
2434
2435    case F_RESTOREGEOMETRY:
2436	if (DeferExecution(context, func, Scr->SelectCursor)) return TRUE;
2437	restoregeometry (tmp_win);
2438	break;
2439
2440    case F_HYPERMOVE: {
2441	Bool	cont = True;
2442	Window	root = RootWindow (dpy, Scr->screen);
2443	Cursor	cursor;
2444	CaptiveCTWM cctwm0, cctwm;
2445
2446	if (DeferExecution(context, func, Scr->MoveCursor)) return TRUE;
2447
2448	if (tmp_win->iswinbox || tmp_win->wspmgr) {
2449	    XBell (dpy, 0);
2450	    break;
2451	}
2452	cctwm0 = GetCaptiveCTWMUnderPointer ();
2453	cursor = MakeStringCursor (cctwm0.name);
2454	free (cctwm0.name);
2455	if (DeferExecution (context, func, Scr->MoveCursor)) return TRUE;
2456
2457	XGrabPointer (dpy, root, True,
2458		ButtonPressMask | ButtonMotionMask | ButtonReleaseMask,
2459		GrabModeAsync, GrabModeAsync, root, cursor, CurrentTime);
2460	while (cont) {
2461	    XMaskEvent (dpy, ButtonPressMask | ButtonMotionMask |
2462				ButtonReleaseMask, &Event);
2463	    switch (Event.xany.type) {
2464		case ButtonPress :
2465		    cont = False;
2466		    break;
2467
2468		case ButtonRelease :
2469		    cont = False;
2470		    cctwm = GetCaptiveCTWMUnderPointer ();
2471		    free (cctwm.name);
2472		    if (cctwm.root == Scr->Root) break;
2473		    SetNoRedirect (tmp_win->w);
2474		    XUngrabButton (dpy, AnyButton, AnyModifier, tmp_win->w);
2475		    XReparentWindow (dpy, tmp_win->w, cctwm.root, 0, 0);
2476		    XMapWindow (dpy, tmp_win->w);
2477		    break;
2478
2479		case MotionNotify :
2480		    cctwm = GetCaptiveCTWMUnderPointer ();
2481		    if (cctwm.root != cctwm0.root) {
2482			XFreeCursor (dpy, cursor);
2483			cursor = MakeStringCursor (cctwm.name);
2484			cctwm0 = cctwm;
2485			XChangeActivePointerGrab (dpy,
2486				ButtonPressMask | ButtonMotionMask | ButtonReleaseMask,
2487				cursor, CurrentTime);
2488		    }
2489		    free (cctwm.name);
2490		    break;
2491	    }
2492	}
2493	ButtonPressed = -1;
2494	XUngrabPointer (dpy, CurrentTime);
2495	XFreeCursor (dpy, cursor);
2496	break;
2497    }
2498
2499    case F_MOVE:
2500    case F_FORCEMOVE:
2501    case F_MOVEPACK:
2502    case F_MOVEPUSH: {
2503        Window grabwin, dragroot;
2504
2505	if (DeferExecution(context, func, Scr->MoveCursor))
2506	    return TRUE;
2507
2508	PopDownMenu();
2509	if (tmp_win->OpaqueMove) {
2510	    int sw, ss;
2511	    float sf;
2512
2513	    sw = tmp_win->frame_width * tmp_win->frame_height;
2514	    ss = Scr->rootw  * Scr->rooth;
2515	    sf = Scr->OpaqueMoveThreshold / 100.0;
2516	    if (sw > (ss * sf))
2517		Scr->OpaqueMove = FALSE;
2518	    else
2519		Scr->OpaqueMove = TRUE;
2520	}
2521	else
2522	    Scr->OpaqueMove = FALSE;
2523
2524	dragroot = Scr->XineramaRoot;
2525
2526	if (tmp_win->winbox) {
2527	    XTranslateCoordinates (dpy, dragroot, tmp_win->winbox->window,
2528		eventp->xbutton.x_root, eventp->xbutton.y_root,
2529		&(eventp->xbutton.x_root), &(eventp->xbutton.y_root), &JunkChild);
2530	}
2531	rootw = eventp->xbutton.root;
2532	MoveFunction = func;
2533
2534	if (pulldown)
2535	    XWarpPointer(dpy, None, Scr->Root,
2536		0, 0, 0, 0, eventp->xbutton.x_root, eventp->xbutton.y_root);
2537
2538	EventHandler[EnterNotify] = HandleUnknown;
2539	EventHandler[LeaveNotify] = HandleUnknown;
2540
2541	if (!Scr->NoGrabServer || !Scr->OpaqueMove) {
2542	    XGrabServer(dpy);
2543	}
2544
2545	Scr->SizeStringOffset = SIZE_HINDENT;
2546	XResizeWindow (dpy, Scr->SizeWindow,
2547		   Scr->SizeStringWidth + SIZE_HINDENT * 2,
2548		   Scr->SizeFont.height + SIZE_VINDENT * 2);
2549	XMapRaised (dpy, Scr->SizeWindow);
2550
2551	grabwin = Scr->XineramaRoot;
2552	if (tmp_win->winbox) grabwin = tmp_win->winbox->window;
2553	XGrabPointer(dpy, grabwin, True,
2554	    ButtonPressMask | ButtonReleaseMask |
2555	    ButtonMotionMask | PointerMotionMask, /* PointerMotionHintMask */
2556	    GrabModeAsync, GrabModeAsync, grabwin, Scr->MoveCursor, CurrentTime);
2557
2558	if (context == C_ICON && tmp_win->icon && tmp_win->icon->w)
2559	{
2560	    w = tmp_win->icon->w;
2561	    DragX = eventp->xbutton.x;
2562	    DragY = eventp->xbutton.y;
2563	    moving_icon = TRUE;
2564	    if (tmp_win->OpaqueMove) Scr->OpaqueMove = TRUE;
2565	}
2566
2567	else if (! tmp_win->icon || w != tmp_win->icon->w)
2568	{
2569	    XTranslateCoordinates(dpy, w, tmp_win->frame,
2570		eventp->xbutton.x,
2571		eventp->xbutton.y,
2572		&DragX, &DragY, &JunkChild);
2573
2574	    w = tmp_win->frame;
2575	}
2576
2577	DragWindow = None;
2578
2579	/* Get x/y relative to parent window, i.e. the virtual screen, Root.
2580	 * XMoveWindow() moves are relative to this.
2581	 * MoveOutline()s however are drawn from the XineramaRoot since they
2582	 * may cross virtual screens.
2583	 */
2584	XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY,
2585	    &DragWidth, &DragHeight, &DragBW,
2586	    &JunkDepth);
2587
2588	JunkBW = DragBW;
2589	origX = eventp->xbutton.x_root;
2590	origY = eventp->xbutton.y_root;
2591	CurrentDragX = origDragX;
2592	CurrentDragY = origDragY;
2593
2594	/*
2595	 * only do the constrained move if timer is set; need to check it
2596	 * in case of stupid or wicked fast servers
2597	 */
2598	if (ConstrainedMoveTime &&
2599	    (eventp->xbutton.time - last_time) < ConstrainedMoveTime)
2600	{
2601	    int width, height;
2602
2603	    ConstMove = TRUE;
2604	    ConstMoveDir = MOVE_NONE;
2605	    ConstMoveX = eventp->xbutton.x_root - DragX - JunkBW;
2606	    ConstMoveY = eventp->xbutton.y_root - DragY - JunkBW;
2607	    width = DragWidth + 2 * JunkBW;
2608	    height = DragHeight + 2 * JunkBW;
2609	    ConstMoveXL = ConstMoveX + width/3;
2610	    ConstMoveXR = ConstMoveX + 2*(width/3);
2611	    ConstMoveYT = ConstMoveY + height/3;
2612	    ConstMoveYB = ConstMoveY + 2*(height/3);
2613
2614	    XWarpPointer(dpy, None, w,
2615		0, 0, 0, 0, DragWidth/2, DragHeight/2);
2616
2617	    XQueryPointer(dpy, w, &JunkRoot, &JunkChild,
2618		&JunkX, &JunkY, &DragX, &DragY, &JunkMask);
2619	}
2620	last_time = eventp->xbutton.time;
2621
2622	if (!Scr->OpaqueMove)
2623	{
2624	    InstallRootColormap();
2625	    if (!Scr->MoveDelta)
2626	    {
2627		/*
2628		 * Draw initial outline.  This was previously done the
2629		 * first time though the outer loop by dropping out of
2630		 * the XCheckMaskEvent inner loop down to one of the
2631		 * MoveOutline's below.
2632		 */
2633		MoveOutline(dragroot,
2634		    origDragX - JunkBW + Scr->currentvs->x,
2635		    origDragY - JunkBW + Scr->currentvs->y,
2636		    DragWidth + 2 * JunkBW, DragHeight + 2 * JunkBW,
2637		    tmp_win->frame_bw,
2638		    moving_icon ? 0 : tmp_win->title_height + tmp_win->frame_bw3D);
2639		/*
2640		 * This next line causes HandleReleaseNotify to call
2641		 * XRaiseWindow().  This is solely to preserve the
2642		 * previous behaviour that raises a window being moved
2643		 * on button release even if you never actually moved
2644		 * any distance (unless you move less than MoveDelta or
2645		 * NoRaiseMove is set or OpaqueMove is set).
2646		 */
2647		DragWindow = w;
2648	    }
2649	}
2650
2651	/*
2652	 * see if this is being done from the titlebar
2653	 */
2654	fromtitlebar = belongs_to_twm_window (tmp_win, eventp->xbutton.window);
2655
2656	if (menuFromFrameOrWindowOrTitlebar) {
2657	  /* warp the pointer to the middle of the window */
2658	  XWarpPointer(dpy, None, Scr->Root, 0, 0, 0, 0,
2659		       origDragX + DragWidth / 2,
2660		       origDragY + DragHeight / 2);
2661	  XFlush(dpy);
2662	}
2663
2664	DisplayPosition (tmp_win, CurrentDragX, CurrentDragY);
2665	while (TRUE)
2666	{
2667	    long releaseEvent = menuFromFrameOrWindowOrTitlebar ?
2668	                          ButtonPress : ButtonRelease;
2669	    long movementMask = menuFromFrameOrWindowOrTitlebar ?
2670	                          PointerMotionMask : ButtonMotionMask;
2671
2672	    /* block until there is an interesting event */
2673	    XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask |
2674				    EnterWindowMask | LeaveWindowMask |
2675				    ExposureMask | movementMask |
2676				    VisibilityChangeMask, &Event);
2677
2678	    /* throw away enter and leave events until release */
2679	    if (Event.xany.type == EnterNotify ||
2680		Event.xany.type == LeaveNotify) continue;
2681
2682	    if (Event.type == MotionNotify) {
2683		/* discard any extra motion events before a logical release */
2684		while(XCheckMaskEvent(dpy,
2685		    movementMask | releaseEvent, &Event))
2686		  if (Event.type == releaseEvent) {
2687		    break;
2688		  }
2689	    }
2690
2691	    /* test to see if we have a second button press to abort move */
2692	    if (!menuFromFrameOrWindowOrTitlebar)
2693	      if (Event.type == ButtonPress && DragWindow != None) {
2694		Cursor cur;
2695		if (Scr->OpaqueMove) {
2696		  XMoveWindow (dpy, DragWindow, origDragX, origDragY);
2697		} else {
2698		  MoveOutline(dragroot, 0, 0, 0, 0, 0, 0);
2699		}
2700		DragWindow = None;
2701
2702		XUnmapWindow (dpy, Scr->SizeWindow);
2703		cur = LeftButt;
2704		if (Event.xbutton.button == Button2)
2705		    cur = MiddleButt;
2706		else if (Event.xbutton.button >= Button3)
2707		    cur = RightButt;
2708
2709		XGrabPointer (dpy, Scr->Root, True,
2710		    ButtonReleaseMask | ButtonPressMask,
2711		    GrabModeAsync, GrabModeAsync,
2712		    Scr->Root, cur, CurrentTime);
2713		return TRUE;
2714	      }
2715
2716	    if (fromtitlebar && Event.type == ButtonPress) {
2717		fromtitlebar = False;
2718		CurrentDragX = origX = Event.xbutton.x_root;
2719		CurrentDragY = origY = Event.xbutton.y_root;
2720		XTranslateCoordinates (dpy, rootw, tmp_win->frame,
2721				       origX, origY,
2722				       &DragX, &DragY, &JunkChild);
2723		continue;
2724	    }
2725
2726	    if (!DispatchEvent2 ()) continue;
2727
2728	    if (Cancel)
2729	    {
2730		WindowMoved = FALSE;
2731		if (!Scr->OpaqueMove)
2732		    UninstallRootColormap();
2733		return TRUE;	/* XXX should this be FALSE? */
2734	    }
2735	    if (Event.type == releaseEvent)
2736	    {
2737		MoveOutline(dragroot, 0, 0, 0, 0, 0, 0);
2738		if (moving_icon &&
2739		    ((CurrentDragX != origDragX ||
2740		      CurrentDragY != origDragY)))
2741		    tmp_win->icon_moved = TRUE;
2742		if (!Scr->OpaqueMove && menuFromFrameOrWindowOrTitlebar) {
2743		    int xl = Event.xbutton.x_root - (DragWidth  / 2),
2744		        yt = Event.xbutton.y_root - (DragHeight / 2);
2745		    if (!moving_icon &&
2746		       (MoveFunction == F_MOVEPACK || MoveFunction == F_MOVEPUSH))
2747			TryToPack (tmp_win, &xl, &yt);
2748		    XMoveWindow(dpy, DragWindow, xl, yt);
2749		}
2750		if (menuFromFrameOrWindowOrTitlebar) DragWindow = None;
2751		break;
2752	    }
2753
2754	    /* something left to do only if the pointer moved */
2755	    if (Event.type != MotionNotify)
2756		continue;
2757
2758	    XQueryPointer(dpy, rootw, &(eventp->xmotion.root), &JunkChild,
2759		&(eventp->xmotion.x_root), &(eventp->xmotion.y_root),
2760		&JunkX, &JunkY, &JunkMask);
2761
2762	    FixRootEvent (eventp);
2763	    if (tmp_win->winbox) {
2764		XTranslateCoordinates (dpy, dragroot, tmp_win->winbox->window,
2765		    eventp->xmotion.x_root, eventp->xmotion.y_root,
2766		    &(eventp->xmotion.x_root), &(eventp->xmotion.y_root), &JunkChild);
2767	    }
2768	    if (DragWindow == None &&
2769		abs(eventp->xmotion.x_root - origX) < Scr->MoveDelta &&
2770	        abs(eventp->xmotion.y_root - origY) < Scr->MoveDelta)
2771		continue;
2772
2773	    DragWindow = w;
2774
2775	    if (!Scr->NoRaiseMove && Scr->OpaqueMove && !WindowMoved)
2776	      RaiseFrame(DragWindow);
2777
2778	    WindowMoved = TRUE;
2779
2780	    if (ConstMove)
2781	    {
2782		switch (ConstMoveDir)
2783		{
2784		    case MOVE_NONE:
2785			if (eventp->xmotion.x_root < ConstMoveXL ||
2786			    eventp->xmotion.x_root > ConstMoveXR)
2787			    ConstMoveDir = MOVE_HORIZ;
2788
2789			if (eventp->xmotion.y_root < ConstMoveYT ||
2790			    eventp->xmotion.y_root > ConstMoveYB)
2791			    ConstMoveDir = MOVE_VERT;
2792
2793			XQueryPointer(dpy, DragWindow, &JunkRoot, &JunkChild,
2794			    &JunkX, &JunkY, &DragX, &DragY, &JunkMask);
2795			break;
2796
2797		    case MOVE_VERT:
2798			ConstMoveY = eventp->xmotion.y_root - DragY - JunkBW;
2799			break;
2800
2801		    case MOVE_HORIZ:
2802			ConstMoveX= eventp->xmotion.x_root - DragX - JunkBW;
2803			break;
2804		}
2805
2806		if (ConstMoveDir != MOVE_NONE)
2807		{
2808		    int xl, yt, width, height;
2809
2810		    xl = ConstMoveX;
2811		    yt = ConstMoveY;
2812		    width = DragWidth + 2 * JunkBW;
2813		    height = DragHeight + 2 * JunkBW;
2814
2815		    if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE)
2816		        TryToGrid (tmp_win, &xl, &yt);
2817		    if (!moving_icon && MoveFunction == F_MOVEPUSH && Scr->OpaqueMove)
2818			TryToPush (tmp_win, xl, yt, 0);
2819
2820		    if (!moving_icon &&
2821			(MoveFunction == F_MOVEPACK || MoveFunction == F_MOVEPUSH))
2822			TryToPack (tmp_win, &xl, &yt);
2823
2824		    if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE)
2825		    {
2826                        ConstrainByBorders (tmp_win, &xl, width, &yt, height);
2827		    }
2828		    CurrentDragX = xl;
2829		    CurrentDragY = yt;
2830		    if (Scr->OpaqueMove) {
2831		      if (MoveFunction == F_MOVEPUSH && !moving_icon) {
2832			SetupWindow (tmp_win, xl, yt,
2833				     tmp_win->frame_width, tmp_win->frame_height, -1);
2834		      } else {
2835			XMoveWindow(dpy, DragWindow, xl, yt);
2836		      }
2837			WMapSetupWindow (tmp_win, xl, yt, -1, -1);
2838		    }
2839		    else {
2840			MoveOutline(dragroot, xl + Scr->currentvs->x,
2841			    yt + Scr->currentvs->y, width, height,
2842			    tmp_win->frame_bw,
2843			    moving_icon ? 0 : tmp_win->title_height + tmp_win->frame_bw3D);
2844		    }
2845		}
2846	    }
2847	    else if (DragWindow != None)
2848	    {
2849		int xroot, yroot;
2850		int xl, yt, width, height;
2851
2852
2853		/*
2854		 * this is split out for virtual screens.  In that case, it's
2855		 * possible to drag windows from one workspace to another, and
2856		 * as such, these need to be adjusted to the root, rather
2857		 * than this virtual screen...
2858		 */
2859		xroot = eventp->xmotion.x_root;
2860		yroot = eventp->xmotion.y_root;
2861
2862		if (!menuFromFrameOrWindowOrTitlebar) {
2863		  xl = xroot - DragX - JunkBW;
2864		  yt = yroot - DragY - JunkBW;
2865		}
2866		else {
2867		  xl = xroot - (DragWidth / 2);
2868		  yt = yroot - (DragHeight / 2);
2869		}
2870		width = DragWidth + 2 * JunkBW;
2871		height = DragHeight + 2 * JunkBW;
2872
2873		if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE)
2874		    TryToGrid (tmp_win, &xl, &yt);
2875		if (!moving_icon && MoveFunction == F_MOVEPUSH && Scr->OpaqueMove)
2876		    TryToPush (tmp_win, xl, yt, 0);
2877
2878		if (!moving_icon &&
2879		    (MoveFunction == F_MOVEPACK || MoveFunction == F_MOVEPUSH))
2880		    TryToPack (tmp_win, &xl, &yt);
2881
2882		if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE)
2883		{
2884                    ConstrainByBorders (tmp_win, &xl, width, &yt, height);
2885		}
2886
2887		CurrentDragX = xl;
2888		CurrentDragY = yt;
2889		if (Scr->OpaqueMove) {
2890		  if (MoveFunction == F_MOVEPUSH && !moving_icon) {
2891		        SetupWindow (tmp_win, xl, yt,
2892				tmp_win->frame_width, tmp_win->frame_height, -1);
2893		  } else {
2894			XMoveWindow(dpy, DragWindow, xl, yt);
2895		  }
2896		  if (! moving_icon) WMapSetupWindow (tmp_win, xl, yt, -1, -1);
2897		}
2898		else {
2899		    MoveOutline(dragroot, xl + Scr->currentvs->x,
2900			yt + Scr->currentvs->y, width, height,
2901			tmp_win->frame_bw,
2902			moving_icon ? 0 : tmp_win->title_height + tmp_win->frame_bw3D);
2903		}
2904	    }
2905	    DisplayPosition (tmp_win, CurrentDragX, CurrentDragY);
2906	}
2907	XUnmapWindow (dpy, Scr->SizeWindow);
2908
2909	if (!Scr->OpaqueMove && DragWindow == None)
2910	    UninstallRootColormap();
2911        break;
2912    }
2913    case F_MOVETITLEBAR:
2914    {
2915        Window grabwin;
2916	int deltax = 0, newx = 0;
2917	int origNum;
2918	SqueezeInfo *si;
2919
2920	if (DeferExecution(context, func, Scr->MoveCursor))
2921	    return TRUE;
2922
2923	PopDownMenu();
2924	if (tmp_win->squeezed ||
2925		!tmp_win->squeeze_info ||
2926		!tmp_win->title_w ||
2927		context == C_ICON ) {
2928	    XBell (dpy, 0);
2929	    break;
2930	}
2931
2932	/* If the SqueezeInfo isn't copied yet, do it now */
2933	if (!tmp_win->squeeze_info_copied) {
2934	    SqueezeInfo *s = malloc(sizeof(SqueezeInfo));
2935	    if (!s)
2936		break;
2937	    *s = *tmp_win->squeeze_info;
2938	    tmp_win->squeeze_info = s;
2939	    tmp_win->squeeze_info_copied = 1;
2940	}
2941	si = tmp_win->squeeze_info;
2942
2943	if (si->denom != 0) {
2944	    int target_denom = tmp_win->frame_width;
2945	    /*
2946	     * If not pixel based, scale the denominator to equal the
2947	     * window width, so the numerator equals pixels.
2948	     * That way we can just modify it by pixel units, just
2949	     * like the other case.
2950	     */
2951
2952	    if (si->denom != target_denom) {
2953		float scale = (float)target_denom / si->denom;
2954		si->num *= scale;
2955		si->denom = target_denom; /* s->denom *= scale; */
2956	    }
2957	}
2958
2959	/* now move the mouse */
2960	if (tmp_win->winbox) {
2961	    XTranslateCoordinates (dpy, Scr->Root, tmp_win->winbox->window,
2962		eventp->xbutton.x_root, eventp->xbutton.y_root,
2963		&eventp->xbutton.x_root, &eventp->xbutton.y_root, &JunkChild);
2964	}
2965	/*
2966	 * the event is always a button event, since key events
2967	 * are "weeded out" - although incompletely only
2968	 * F_MOVE and F_RESIZE - in HandleKeyPress().
2969	 */
2970	rootw = eventp->xbutton.root;
2971
2972	EventHandler[EnterNotify] = HandleUnknown;
2973	EventHandler[LeaveNotify] = HandleUnknown;
2974
2975	if (!Scr->NoGrabServer) {
2976	    XGrabServer(dpy);
2977	}
2978
2979	grabwin = Scr->Root;
2980	if (tmp_win->winbox) grabwin = tmp_win->winbox->window;
2981	XGrabPointer(dpy, grabwin, True,
2982	    ButtonPressMask | ButtonReleaseMask |
2983	    ButtonMotionMask | PointerMotionMask, /* PointerMotionHintMask */
2984	    GrabModeAsync, GrabModeAsync, grabwin, Scr->MoveCursor, CurrentTime);
2985
2986#if 0	/* what's this for ? */
2987	if (! tmp_win->icon || w != tmp_win->icon->w)
2988	{
2989	    XTranslateCoordinates(dpy, w, tmp_win->frame,
2990		eventp->xbutton.x,
2991		eventp->xbutton.y,
2992		&DragX, &DragY, &JunkChild);
2993
2994	    w = tmp_win->frame;
2995	}
2996#endif
2997
2998	DragWindow = None;
2999
3000	XGetGeometry(dpy, tmp_win->title_w, &JunkRoot, &origDragX, &origDragY,
3001	    &DragWidth, &DragHeight, &DragBW,
3002	    &JunkDepth);
3003
3004	origX = eventp->xbutton.x_root;
3005	origNum = si->num;
3006
3007	if (menuFromFrameOrWindowOrTitlebar) {
3008	  /* warp the pointer to the middle of the window */
3009	  XWarpPointer(dpy, None, Scr->Root, 0, 0, 0, 0,
3010		       origDragX + DragWidth / 2,
3011		       origDragY + DragHeight / 2);
3012	  XFlush(dpy);
3013	}
3014
3015	while (TRUE)
3016	{
3017	    long releaseEvent = menuFromFrameOrWindowOrTitlebar ?
3018	                          ButtonPress : ButtonRelease;
3019	    long movementMask = menuFromFrameOrWindowOrTitlebar ?
3020	                          PointerMotionMask : ButtonMotionMask;
3021
3022	    /* block until there is an interesting event */
3023	    XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask |
3024				    EnterWindowMask | LeaveWindowMask |
3025				    ExposureMask | movementMask |
3026				    VisibilityChangeMask, &Event);
3027
3028	    /* throw away enter and leave events until release */
3029	    if (Event.xany.type == EnterNotify ||
3030		Event.xany.type == LeaveNotify) continue;
3031
3032	    if (Event.type == MotionNotify) {
3033		/* discard any extra motion events before a logical release */
3034		while (XCheckMaskEvent(dpy,
3035					movementMask | releaseEvent, &Event)) {
3036		    if (Event.type == releaseEvent) {
3037			break;
3038		    }
3039		}
3040	    }
3041
3042	    if (!DispatchEvent2())
3043		continue;
3044
3045	    if (Event.type == releaseEvent)
3046		break;
3047
3048	    /* something left to do only if the pointer moved */
3049	    if (Event.type != MotionNotify)
3050		continue;
3051
3052	    /* get current pointer pos, useful when there is lag */
3053	    XQueryPointer(dpy, rootw, &eventp->xmotion.root, &JunkChild,
3054		&eventp->xmotion.x_root, &eventp->xmotion.y_root,
3055		&JunkX, &JunkY, &JunkMask);
3056
3057	    FixRootEvent(eventp);
3058	    if (tmp_win->winbox) {
3059		XTranslateCoordinates(dpy, Scr->Root, tmp_win->winbox->window,
3060		    eventp->xmotion.x_root, eventp->xmotion.y_root,
3061		    &eventp->xmotion.x_root, &eventp->xmotion.y_root, &JunkChild);
3062	    }
3063
3064	    if (!Scr->NoRaiseMove && Scr->OpaqueMove && !WindowMoved)
3065	      RaiseFrame(w);
3066
3067	    deltax = eventp->xmotion.x_root - origX;
3068	    newx = origNum + deltax;
3069
3070	    /*
3071	     * Clamp to left and right.
3072	     * If we're in pixel size, keep within [ 0, frame_width >.
3073	     * If we're proportional, don't cross the 0.
3074	     * Also don't let the nominator get bigger than the denominator.
3075	     * Keep within [ -denom, -1] or [ 0, denom >.
3076	     */
3077	    {
3078		int wtmp = tmp_win->frame_width; /* or si->denom; if it were != 0 */
3079		if (origNum < 0) {
3080		    if (newx >= 0)
3081			newx = -1;
3082		    else if (newx < -wtmp)
3083			newx = -wtmp;
3084		} else if (origNum >= 0) {
3085		    if (newx < 0)
3086			newx = 0;
3087		    else if (newx >= wtmp)
3088			newx = wtmp - 1;
3089		}
3090	    }
3091
3092	    si->num = newx;
3093	    /* This, finally, actually moves the title bar */
3094	    /* XXX pressing a second button should cancel and undo this */
3095	    SetFrameShape(tmp_win);
3096	}
3097	break;
3098    }
3099    case F_FUNCTION:
3100	{
3101	    MenuRoot *mroot;
3102	    MenuItem *mitem;
3103
3104	    if ((mroot = FindMenuRoot(action)) == NULL)
3105	    {
3106		if (!action) action = "undef";
3107		fprintf (stderr, "%s: couldn't find function \"%s\"\n",
3108			 ProgramName, (char *)action);
3109		return TRUE;
3110	    }
3111
3112	    if (NeedToDefer(mroot) && DeferExecution(context, func, Scr->SelectCursor))
3113		return TRUE;
3114	    else
3115	    {
3116		for (mitem = mroot->first; mitem != NULL; mitem = mitem->next)
3117		{
3118		    if (!ExecuteFunction (mitem->func, mitem->action, w,
3119					  tmp_win, eventp, context, pulldown))
3120		      /* pebl FIXME: the focus should be updated here,
3121			 or the function would operate on the same window */
3122		      break;
3123		}
3124	    }
3125	}
3126	break;
3127
3128    case F_DEICONIFY:
3129    case F_ICONIFY:
3130	if (DeferExecution(context, func, Scr->SelectCursor))
3131	    return TRUE;
3132
3133	if (tmp_win->isicon)
3134	{
3135	    DeIconify(tmp_win);
3136	}
3137        else if (func == F_ICONIFY)
3138	{
3139	    Iconify (tmp_win, eventp->xbutton.x_root - 5,
3140		     eventp->xbutton.y_root - 5);
3141	}
3142	break;
3143
3144    case F_SQUEEZE:
3145	if (DeferExecution(context, func, Scr->SelectCursor))
3146	    return TRUE;
3147
3148	Squeeze (tmp_win);
3149	break;
3150
3151    case F_SHOWBGRD:
3152	ShowBackground (Scr->currentvs);
3153	break;
3154
3155    case F_RAISELOWER:
3156	if (DeferExecution(context, func, Scr->SelectCursor))
3157	    return TRUE;
3158
3159	if (!WindowMoved) {
3160	    if (tmp_win->icon && w == tmp_win->icon->w) {
3161		RaiseLowerFrame(w, ONTOP_DEFAULT);
3162	    } else {
3163		RaiseLower(tmp_win);
3164		WMapRaiseLower (tmp_win);
3165	    }
3166	}
3167	break;
3168
3169    case F_RAISE:
3170	if (DeferExecution(context, func, Scr->SelectCursor))
3171	    return TRUE;
3172
3173	/* check to make sure raise is not from the WindowFunction */
3174	if (tmp_win->icon && (w == tmp_win->icon->w) && Context != C_ROOT)
3175	    XRaiseWindow(dpy, tmp_win->icon->w);
3176	else {
3177	    RaiseWindow (tmp_win);
3178	    WMapRaise   (tmp_win);
3179	}
3180	break;
3181
3182    case F_LOWER:
3183	if (DeferExecution(context, func, Scr->SelectCursor))
3184	    return TRUE;
3185
3186	if (tmp_win->icon && (w == tmp_win->icon->w))
3187	    XLowerWindow(dpy, tmp_win->icon->w);
3188	else {
3189	    LowerWindow(tmp_win);
3190	    WMapLower (tmp_win);
3191	}
3192	break;
3193
3194    case F_RAISEICONS:
3195	for (t = Scr->FirstWindow; t != NULL; t = t->next) {
3196	    if (t->icon && t->icon->w) {
3197		XRaiseWindow (dpy, t->icon->w);
3198	    }
3199	}
3200	break;
3201
3202    case F_FOCUS:
3203	if (DeferExecution(context, func, Scr->SelectCursor))
3204	    return TRUE;
3205
3206	if (tmp_win->isicon == FALSE)
3207	{
3208	    if (!Scr->FocusRoot && Scr->Focus == tmp_win)
3209	    {
3210		FocusOnRoot();
3211	    }
3212	    else
3213	    {
3214		InstallWindowColormaps (0, tmp_win);
3215		SetFocus (tmp_win, eventp->xbutton.time);
3216		Scr->FocusRoot = FALSE;
3217	    }
3218	}
3219	break;
3220
3221    case F_DESTROY:
3222	if (DeferExecution(context, func, Scr->DestroyCursor))
3223	    return TRUE;
3224
3225	if (tmp_win->iconmgr || tmp_win->iswinbox || tmp_win->wspmgr
3226	    || (Scr->workSpaceMgr.occupyWindow
3227		&& tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win)) {
3228	    XBell(dpy, 0);
3229	    break;
3230	}
3231	XKillClient(dpy, tmp_win->w);
3232	if (ButtonPressed != -1) {
3233	    XEvent kev;
3234
3235	    XMaskEvent (dpy, ButtonReleaseMask, &kev);
3236	    if (kev.xbutton.window == tmp_win->w) kev.xbutton.window = Scr->Root;
3237	    XPutBackEvent (dpy, &kev);
3238	}
3239	break;
3240
3241    case F_DELETE:
3242	if (DeferExecution(context, func, Scr->DestroyCursor))
3243	    return TRUE;
3244
3245	if (tmp_win->iconmgr) {		/* don't send ourself a message */
3246	    HideIconManager ();
3247	    break;
3248	}
3249	if (tmp_win->iswinbox || tmp_win->wspmgr
3250	    || (Scr->workSpaceMgr.occupyWindow
3251		&& tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win)) {
3252	    XBell (dpy, 0);
3253	    break;
3254	}
3255	if (tmp_win->protocols & DoesWmDeleteWindow) {
3256	    SendDeleteWindowMessage (tmp_win, LastTimestamp());
3257	    if (ButtonPressed != -1) {
3258		XEvent kev;
3259
3260		XMaskEvent (dpy, ButtonReleaseMask, &kev);
3261		if (kev.xbutton.window == tmp_win->w) kev.xbutton.window = Scr->Root;
3262		XPutBackEvent (dpy, &kev);
3263	    }
3264	    break;
3265	}
3266	XBell (dpy, 0);
3267	break;
3268
3269    case F_DELETEORDESTROY:
3270	if (DeferExecution(context, func, Scr->DestroyCursor)) return TRUE;
3271
3272	if (tmp_win->iconmgr) {
3273	    HideIconManager ();
3274	    break;
3275	}
3276	if (tmp_win->iswinbox || tmp_win->wspmgr
3277	    || (Scr->workSpaceMgr.occupyWindow
3278		&& tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win)) {
3279	    XBell (dpy, 0);
3280	    break;
3281	}
3282	if (tmp_win->protocols & DoesWmDeleteWindow) {
3283	    SendDeleteWindowMessage (tmp_win, LastTimestamp());
3284	} else {
3285	    XKillClient(dpy, tmp_win->w);
3286	}
3287	if (ButtonPressed != -1) {
3288	    XEvent kev;
3289
3290	    XMaskEvent (dpy, ButtonReleaseMask, &kev);
3291	    if (kev.xbutton.window == tmp_win->w) kev.xbutton.window = Scr->Root;
3292	    XPutBackEvent (dpy, &kev);
3293	}
3294	break;
3295
3296    case F_SAVEYOURSELF:
3297	if (DeferExecution (context, func, Scr->SelectCursor))
3298	  return TRUE;
3299
3300	if (tmp_win->protocols & DoesWmSaveYourself)
3301	  SendSaveYourselfMessage (tmp_win, LastTimestamp());
3302	else
3303	  XBell (dpy, 0);
3304	break;
3305
3306    case F_CIRCLEUP:
3307	XCirculateSubwindowsUp(dpy, Scr->Root);
3308	break;
3309
3310    case F_CIRCLEDOWN:
3311	XCirculateSubwindowsDown(dpy, Scr->Root);
3312	break;
3313
3314    case F_EXEC:
3315	PopDownMenu();
3316	if (!Scr->NoGrabServer) {
3317	    XUngrabServer (dpy);
3318	    XSync (dpy, 0);
3319	}
3320	XUngrabPointer (dpy, CurrentTime);
3321	XSync (dpy, 0);
3322	Execute(action);
3323	break;
3324
3325    case F_UNFOCUS:
3326	FocusOnRoot();
3327	break;
3328
3329    case F_CUT:
3330	strcpy(tmp, action);
3331	strcat(tmp, "\n");
3332	XStoreBytes(dpy, tmp, strlen(tmp));
3333	break;
3334
3335    case F_CUTFILE:
3336	ptr = XFetchBytes(dpy, &count);
3337	if (ptr) {
3338	    if (sscanf (ptr, "%s", tmp) == 1) {
3339		XFree (ptr);
3340		ptr = ExpandFilename(tmp);
3341		if (ptr) {
3342#ifdef VMS
3343		    fd = open (ptr, O_RDONLY, 0);
3344#else
3345		    fd = open (ptr, 0);
3346#endif
3347		    if (fd >= 0) {
3348			count = read (fd, buff, MAX_FILE_SIZE - 1);
3349			if (count > 0) XStoreBytes (dpy, buff, count);
3350			close(fd);
3351		    } else {
3352			fprintf (stderr,
3353				 "%s:  unable to open cut file \"%s\"\n",
3354				 ProgramName, tmp);
3355		    }
3356		    if (ptr != tmp) free (ptr);
3357		}
3358	    } else {
3359		XFree(ptr);
3360	    }
3361	} else {
3362	    fprintf(stderr, "%s:  cut buffer is empty\n", ProgramName);
3363	}
3364	break;
3365
3366    case F_WARPTOSCREEN:
3367	{
3368	    if (strcmp (action, WARPSCREEN_NEXT) == 0) {
3369		WarpToScreen (Scr->screen + 1, 1);
3370	    } else if (strcmp (action, WARPSCREEN_PREV) == 0) {
3371		WarpToScreen (Scr->screen - 1, -1);
3372	    } else if (strcmp (action, WARPSCREEN_BACK) == 0) {
3373		WarpToScreen (PreviousScreen, 0);
3374	    } else {
3375		WarpToScreen (atoi (action), 0);
3376	    }
3377	}
3378	break;
3379
3380    case F_COLORMAP:
3381	{
3382	    if (strcmp (action, COLORMAP_NEXT) == 0) {
3383		BumpWindowColormap (tmp_win, 1);
3384	    } else if (strcmp (action, COLORMAP_PREV) == 0) {
3385		BumpWindowColormap (tmp_win, -1);
3386	    } else {
3387		BumpWindowColormap (tmp_win, 0);
3388	    }
3389	}
3390	break;
3391
3392    case F_WARPTO:
3393	{
3394	    register TwmWindow *tw;
3395	    int len;
3396
3397	    len = strlen(action);
3398
3399#ifdef WARPTO_FROM_ICONMGR
3400		if (len == 0 && tmp_win && tmp_win->iconmgr)
3401		{
3402			printf ("curren iconmgr entry: %s", tmp_win->iconmgr->Current);
3403		}
3404#endif /* #ifdef WARPTO_FROM_ICONMGR */
3405	    for (tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
3406		if (!strncmp(action, tw->full_name, len)) break;
3407		if (match (action, tw->full_name)) break;
3408	    }
3409	    if (!tw) {
3410		for (tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
3411		    if (!strncmp(action, tw->class.res_name, len)) break;
3412		    if (match (action, tw->class.res_name)) break;
3413		}
3414		if (!tw) {
3415		    for (tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
3416			if (!strncmp(action, tw->class.res_class, len)) break;
3417			if (match (action, tw->class.res_class)) break;
3418		    }
3419		}
3420	    }
3421
3422	    if (tw) {
3423		if (Scr->WarpUnmapped || tw->mapped) {
3424		    if (!tw->mapped) DeIconify (tw);
3425		    WarpToWindow (tw, Scr->RaiseOnWarp);
3426		}
3427	    } else {
3428		XBell (dpy, 0);
3429	    }
3430	}
3431	break;
3432
3433    case F_WARPTOICONMGR:
3434	{
3435	    TwmWindow *tw;
3436	    int len;
3437	    Window raisewin = None, iconwin = None;
3438
3439	    len = strlen(action);
3440	    if (len == 0) {
3441		if (tmp_win && tmp_win->iconmanagerlist) {
3442		    raisewin = tmp_win->iconmanagerlist->iconmgr->twm_win->frame;
3443		    iconwin = tmp_win->iconmanagerlist->icon;
3444		} else if (Scr->iconmgr->active) {
3445		    raisewin = Scr->iconmgr->twm_win->frame;
3446		    iconwin = Scr->iconmgr->active->w;
3447		}
3448	    } else {
3449		for (tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
3450		    if (strncmp (action, tw->icon_name, len) == 0) {
3451			if (tw->iconmanagerlist &&
3452			    tw->iconmanagerlist->iconmgr->twm_win->mapped) {
3453			    raisewin = tw->iconmanagerlist->iconmgr->twm_win->frame;
3454			    break;
3455			}
3456		    }
3457		}
3458	    }
3459
3460	    if (raisewin) {
3461		RaiseFrame(raisewin);
3462		XWarpPointer (dpy, None, iconwin, 0,0,0,0, 5, 5);
3463	    } else {
3464		XBell (dpy, 0);
3465	    }
3466	}
3467	break;
3468
3469    case F_RING:  /* Taken from vtwm version 5.3 */
3470	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
3471	if ( tmp_win->ring.next || tmp_win->ring.prev ) {
3472	    /* It's in the ring, let's take it out. */
3473	   TwmWindow *prev = tmp_win->ring.prev, *next = tmp_win->ring.next;
3474
3475	    /*
3476	    * 1. Unlink window
3477	    * 2. If window was only thing in ring, null out ring
3478	    * 3. If window was ring leader, set to next (or null)
3479	    */
3480	    if (prev) prev->ring.next = next;
3481	    if (next) next->ring.prev = prev;
3482	    if (Scr->Ring == tmp_win)
3483		Scr->Ring = (next != tmp_win ? next : (TwmWindow *) NULL);
3484
3485	    if (!Scr->Ring || Scr->RingLeader == tmp_win)
3486		Scr->RingLeader = Scr->Ring;
3487	    tmp_win->ring.next = tmp_win->ring.prev = NULL;
3488	} else {
3489	    /* Not in the ring, so put it in. */
3490	    if (Scr->Ring) {
3491		tmp_win->ring.next = Scr->Ring->ring.next;
3492		if (Scr->Ring->ring.next->ring.prev)
3493		    Scr->Ring->ring.next->ring.prev = tmp_win;
3494		Scr->Ring->ring.next = tmp_win;
3495		tmp_win->ring.prev = Scr->Ring;
3496	    } else {
3497		tmp_win->ring.next = tmp_win->ring.prev = Scr->Ring = tmp_win;
3498	    }
3499	}
3500	/*tmp_win->ring.cursor_valid = False;*/
3501	break;
3502
3503    case F_WARPRING:
3504	switch (((char *)action)[0]) {
3505	  case 'n':
3506	    WarpAlongRing (&eventp->xbutton, True);
3507	    break;
3508	  case 'p':
3509	    WarpAlongRing (&eventp->xbutton, False);
3510	    break;
3511	  default:
3512	    XBell (dpy, 0);
3513	    break;
3514	}
3515	break;
3516
3517    case F_FILE:
3518	action = ExpandFilename(action);
3519#ifdef VMS
3520	fd = open (action, O_RDONLY, 0);
3521#else
3522	fd = open(action, 0);
3523#endif
3524	if (fd >= 0)
3525	{
3526	    count = read(fd, buff, MAX_FILE_SIZE - 1);
3527	    if (count > 0)
3528		XStoreBytes(dpy, buff, count);
3529
3530	    close(fd);
3531	}
3532	else
3533	{
3534	    fprintf (stderr, "%s:  unable to open file \"%s\"\n",
3535		     ProgramName, (char *)action);
3536	}
3537	free(action);
3538	break;
3539
3540    case F_REFRESH:
3541	{
3542	    XSetWindowAttributes attributes;
3543	    unsigned long valuemask;
3544
3545	    valuemask = (CWBackPixel | CWBackingStore | CWSaveUnder);
3546	    attributes.background_pixel = Scr->Black;
3547	    attributes.backing_store = NotUseful;
3548	    attributes.save_under = False;
3549	    w = XCreateWindow (dpy, Scr->Root, 0, 0,
3550			       (unsigned int) Scr->rootw,
3551			       (unsigned int) Scr->rooth,
3552			       (unsigned int) 0,
3553			       CopyFromParent, (unsigned int) CopyFromParent,
3554			       (Visual *) CopyFromParent, valuemask,
3555			       &attributes);
3556	    XMapWindow (dpy, w);
3557	    XDestroyWindow (dpy, w);
3558	    XFlush (dpy);
3559	}
3560	break;
3561
3562    case F_OCCUPY:
3563	if (DeferExecution(context, func, Scr->SelectCursor))
3564	    return TRUE;
3565	Occupy (tmp_win);
3566	break;
3567
3568    case F_OCCUPYALL:
3569	if (DeferExecution(context, func, Scr->SelectCursor))
3570	    return TRUE;
3571	OccupyAll (tmp_win);
3572	break;
3573
3574    case F_GOTOWORKSPACE:
3575	GotoWorkSpaceByName (Scr->currentvs, action);
3576	break;
3577
3578    case F_PREVWORKSPACE:
3579	GotoPrevWorkSpace (Scr->currentvs);
3580	break;
3581
3582    case F_NEXTWORKSPACE:
3583	GotoNextWorkSpace (Scr->currentvs);
3584	break;
3585
3586    case F_RIGHTWORKSPACE:
3587	GotoRightWorkSpace (Scr->currentvs);
3588	break;
3589
3590    case F_LEFTWORKSPACE:
3591	GotoLeftWorkSpace (Scr->currentvs);
3592	break;
3593
3594    case F_UPWORKSPACE:
3595	GotoUpWorkSpace (Scr->currentvs);
3596	break;
3597
3598    case F_DOWNWORKSPACE:
3599	GotoDownWorkSpace (Scr->currentvs);
3600	break;
3601
3602    case F_MENU:
3603	if (action && ! strncmp (action, "WGOTO : ", 8)) {
3604	    GotoWorkSpaceByName (/* XXXXX */ Scr->currentvs,
3605		((char *)action) + 8);
3606	}
3607	else {
3608	    MenuItem *item;
3609
3610	    item = ActiveItem;
3611	    while (item && item->sub) {
3612		if (!item->sub->defaultitem) break;
3613		if (item->sub->defaultitem->func != F_MENU) break;
3614		item = item->sub->defaultitem;
3615	    }
3616	    if (item && item->sub && item->sub->defaultitem) {
3617		ExecuteFunction (item->sub->defaultitem->func,
3618				 item->sub->defaultitem->action,
3619				 w, tmp_win, eventp, context, pulldown);
3620	    }
3621	}
3622	break;
3623
3624    case F_WINREFRESH:
3625	if (DeferExecution(context, func, Scr->SelectCursor))
3626	    return TRUE;
3627
3628	if (context == C_ICON && tmp_win->icon && tmp_win->icon->w)
3629	    w = XCreateSimpleWindow(dpy, tmp_win->icon->w,
3630		0, 0, 9999, 9999, 0, Scr->Black, Scr->Black);
3631	else
3632	    w = XCreateSimpleWindow(dpy, tmp_win->frame,
3633		0, 0, 9999, 9999, 0, Scr->Black, Scr->Black);
3634
3635	XMapWindow(dpy, w);
3636	XDestroyWindow(dpy, w);
3637	XFlush(dpy);
3638	break;
3639
3640    case F_ADOPTWINDOW:
3641	adoptWindow ();
3642	break;
3643
3644    case F_TRACE:
3645	DebugTrace (action);
3646	break;
3647
3648    case F_CHANGESIZE:
3649	ChangeSize (action, tmp_win);
3650	break;
3651
3652    case F_QUIT:
3653	Done(0);
3654	break;
3655    }
3656
3657    if (ButtonPressed == -1) XUngrabPointer(dpy, CurrentTime);
3658    return do_next_action;
3659}
3660
3661
3662
3663/***********************************************************************
3664 *
3665 *  Procedure:
3666 *	DeferExecution - defer the execution of a function to the
3667 *	    next button press if the context is C_ROOT
3668 *
3669 *  Inputs:
3670 *	context	- the context in which the mouse button was pressed
3671 *	func	- the function to defer
3672 *	cursor	- the cursor to display while waiting
3673 *
3674 ***********************************************************************
3675 */
3676
3677int DeferExecution(int context, int func, Cursor cursor)
3678{
3679    if ((context == C_ROOT) || (context == C_ALTERNATE))
3680    {
3681	LastCursor = cursor;
3682	if (func == F_ADOPTWINDOW) {
3683	    XGrabPointer(dpy, Scr->Root, True,
3684		ButtonPressMask | ButtonReleaseMask,
3685		GrabModeAsync, GrabModeAsync,
3686		None, cursor, CurrentTime);
3687	} else {
3688	    XGrabPointer(dpy, Scr->Root, True,
3689		ButtonPressMask | ButtonReleaseMask,
3690		GrabModeAsync, GrabModeAsync,
3691		Scr->Root, cursor, CurrentTime);
3692	}
3693	RootFunction = func;
3694
3695	return (TRUE);
3696    }
3697
3698    return (FALSE);
3699}
3700
3701
3702
3703/***********************************************************************
3704 *
3705 *  Procedure:
3706 *	ReGrab - regrab the pointer with the LastCursor;
3707 *
3708 ***********************************************************************
3709 */
3710
3711void ReGrab(void)
3712{
3713    XGrabPointer(dpy, Scr->Root, True,
3714	ButtonPressMask | ButtonReleaseMask,
3715	GrabModeAsync, GrabModeAsync,
3716	Scr->Root, LastCursor, CurrentTime);
3717}
3718
3719
3720
3721/***********************************************************************
3722 *
3723 *  Procedure:
3724 *	NeedToDefer - checks each function in the list to see if it
3725 *		is one that needs to be defered.
3726 *
3727 *  Inputs:
3728 *	root	- the menu root to check
3729 *
3730 ***********************************************************************
3731 */
3732
3733int NeedToDefer(MenuRoot *root)
3734{
3735    MenuItem *mitem;
3736
3737    for (mitem = root->first; mitem != NULL; mitem = mitem->next)
3738    {
3739	switch (mitem->func)
3740	{
3741	case F_IDENTIFY:
3742	case F_RESIZE:
3743	case F_MOVE:
3744	case F_FORCEMOVE:
3745	case F_DEICONIFY:
3746	case F_ICONIFY:
3747	case F_RAISELOWER:
3748	case F_RAISE:
3749	case F_LOWER:
3750	case F_FOCUS:
3751	case F_DESTROY:
3752	case F_WINREFRESH:
3753	case F_ZOOM:
3754	case F_FULLZOOM:
3755	case F_HORIZOOM:
3756        case F_RIGHTZOOM:
3757        case F_LEFTZOOM:
3758        case F_TOPZOOM:
3759        case F_BOTTOMZOOM:
3760        case F_SQUEEZE:
3761	case F_AUTORAISE:
3762	case F_AUTOLOWER:
3763	    return TRUE;
3764	}
3765    }
3766    return FALSE;
3767}
3768
3769
3770
3771/***********************************************************************
3772 *
3773 *  Procedure:
3774 *	Execute - execute the string by /bin/sh
3775 *
3776 *  Inputs:
3777 *	s	- the string containing the command
3778 *
3779 ***********************************************************************
3780 */
3781
3782void Execute(char *s)
3783{
3784#ifdef VMS
3785    createProcess(s);
3786#else
3787    static char buf[256];
3788    char *ds = DisplayString (dpy);
3789    char *colon, *dot1;
3790    char oldDisplay[256];
3791    char *doisplay;
3792    int restorevar = 0;
3793    Bool replace;
3794    char *subs, *name, *news;
3795    int len;
3796
3797    oldDisplay[0] = '\0';
3798    doisplay=getenv("DISPLAY");
3799    if (doisplay)
3800	strcpy (oldDisplay, doisplay);
3801
3802    /*
3803     * Build a display string using the current screen number, so that
3804     * X programs which get fired up from a menu come up on the screen
3805     * that they were invoked from, unless specifically overridden on
3806     * their command line.
3807     */
3808    colon = strrchr (ds, ':');
3809    if (colon) {			/* if host[:]:dpy */
3810	strcpy (buf, "DISPLAY=");
3811	strcat (buf, ds);
3812	colon = buf + 8 + (colon - ds);	/* use version in buf */
3813	dot1 = strchr (colon, '.');	/* first period after colon */
3814	if (!dot1) dot1 = colon + strlen (colon);  /* if not there, append */
3815	(void) sprintf (dot1, ".%d", Scr->screen);
3816	putenv (buf);
3817	restorevar = 1;
3818    }
3819    replace = False;
3820    subs = strstr (s, "$currentworkspace");
3821    name = GetCurrentWorkSpaceName (Scr->currentvs);
3822    if (subs && name) {
3823	len = strlen (s) - strlen ("$currentworkspace") + strlen (name);
3824	news = (char*) malloc (len + 1);
3825	*subs = '\0';
3826	strcpy (news, s);
3827	*subs = '$';
3828	strcat (news, name);
3829	subs += strlen ("$currentworkspace");
3830	strcat (news, subs);
3831	s = news;
3832	replace = True;
3833    }
3834    subs = strstr (s, "$redirect");
3835    if (subs) {
3836	if (captive) {
3837	    name = (char*) malloc (21 + strlen (captivename) + 1);
3838	    sprintf (name, "-xrm 'ctwm.redirect:%s'", captivename);
3839	} else {
3840	    name = (char*) malloc (1);
3841	    *name = '\0';
3842	}
3843	len = strlen (s) - strlen ("$redirect") + strlen (name);
3844	news = (char*) malloc (len + 1);
3845	*subs = '\0';
3846	strcpy (news, s);
3847	*subs = '$';
3848	strcat (news, name);
3849	subs += strlen ("$redirect");
3850	strcat (news, subs);
3851	s = news;
3852	free (name);
3853	replace = True;
3854    }
3855#ifdef USE_SIGNALS
3856  {
3857    SigProc	sig;
3858
3859    sig = signal (SIGALRM, SIG_IGN);
3860    (void) system (s);
3861    signal (SIGALRM, sig);
3862  }
3863#else  /* USE_SIGNALS */
3864    (void) system (s);
3865#endif  /* USE_SIGNALS */
3866
3867    if (restorevar) {		/* why bother? */
3868	(void) sprintf (buf, "DISPLAY=%s", oldDisplay);
3869	putenv (buf);
3870    }
3871    if (replace) free (s);
3872#endif
3873}
3874
3875
3876
3877Window lowerontop = -1;
3878
3879void PlaceTransients (TwmWindow *tmp_win, int where)
3880{
3881    int	sp, sc;
3882    TwmWindow *t;
3883    XWindowChanges xwc;
3884    xwc.stack_mode = where;
3885
3886    sp = tmp_win->frame_width * tmp_win->frame_height;
3887    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
3888	if (t != tmp_win &&
3889	    ((t->transient && t->transientfor == tmp_win->w) ||
3890	     t->group == tmp_win->w)) {
3891	    if (t->frame) {
3892		sc = t->frame_width * t->frame_height;
3893		if (sc < ((sp * Scr->TransientOnTop) / 100)) {
3894		    xwc.sibling = tmp_win->frame;
3895		    XConfigureWindow(dpy, t->frame, CWSibling | CWStackMode, &xwc);
3896		    if (lowerontop == t->frame) {
3897			lowerontop = (Window)-1;
3898		    }
3899		}
3900	    }
3901	}
3902    }
3903}
3904
3905#include <assert.h>
3906
3907void PlaceOntop (int ontop, int where)
3908{
3909    TwmWindow *t;
3910    XWindowChanges xwc;
3911    xwc.stack_mode = where;
3912
3913    lowerontop = (Window)-1;
3914
3915    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
3916	if (t->ontoppriority > ontop) {
3917	    XConfigureWindow(dpy, t->frame, CWStackMode, &xwc);
3918	    PlaceTransients(t, Above);
3919	    if (lowerontop == (Window)-1) {
3920		lowerontop = t->frame;
3921	    }
3922	}
3923    }
3924}
3925
3926void MapRaised (TwmWindow *tmp_win)
3927{
3928    XMapWindow(dpy, tmp_win->frame);
3929    RaiseWindow(tmp_win);
3930}
3931
3932void RaiseWindow (TwmWindow *tmp_win)
3933{
3934    XWindowChanges xwc;
3935    int xwcm;
3936
3937    if (tmp_win->ontoppriority == ONTOP_MAX) {
3938	XRaiseWindow(dpy, tmp_win->frame);
3939	if (lowerontop == (Window)-1) {
3940	    lowerontop = tmp_win->frame;
3941	} else if (lowerontop == tmp_win->frame) {
3942	    lowerontop = (Window)-1;
3943	}
3944    } else {
3945	if (lowerontop == (Window)-1) {
3946	    PlaceOntop(tmp_win->ontoppriority, Above);
3947	}
3948	xwcm = CWStackMode;
3949	if (lowerontop != (Window)-1) {
3950	    xwc.stack_mode = Below;
3951	    xwc.sibling = lowerontop;
3952	    xwcm |= CWSibling;
3953	} else {
3954	    xwc.stack_mode = Above;
3955	}
3956	XConfigureWindow(dpy, tmp_win->frame, xwcm, &xwc);
3957    }
3958    PlaceTransients(tmp_win, Above);
3959}
3960
3961void RaiseLower (TwmWindow *tmp_win)
3962{
3963    XWindowChanges xwc;
3964
3965    PlaceOntop(tmp_win->ontoppriority, Below);
3966    PlaceTransients(tmp_win, Below);
3967    lowerontop = (Window)-1;
3968    xwc.stack_mode = Opposite;
3969    XConfigureWindow(dpy, tmp_win->frame, CWStackMode, &xwc);
3970    PlaceOntop(tmp_win->ontoppriority, Above);
3971    PlaceTransients(tmp_win, Above);
3972}
3973
3974void RaiseLowerFrame (Window frame, int ontop)
3975{
3976    XWindowChanges xwc;
3977
3978    PlaceOntop(ontop, Below);
3979    lowerontop = (Window)-1;
3980    xwc.stack_mode = Opposite;
3981    XConfigureWindow(dpy, frame, CWStackMode, &xwc);
3982    PlaceOntop(ontop, Above);
3983}
3984
3985void LowerWindow (TwmWindow *tmp_win)
3986{
3987    XLowerWindow(dpy, tmp_win->frame);
3988    if (tmp_win->frame == lowerontop) {
3989	lowerontop = (Window)-1;
3990    }
3991    PlaceTransients(tmp_win, Above);
3992}
3993
3994void RaiseFrame (Window frame)
3995{
3996    TwmWindow *tmp_win;
3997
3998    tmp_win = GetTwmWindow(frame);
3999
4000    if (tmp_win != NULL) {
4001	RaiseWindow(tmp_win);
4002    } else {
4003	XRaiseWindow(dpy, frame);
4004    }
4005}
4006
4007/***********************************************************************
4008 *
4009 *  Procedure:
4010 *	FocusOnRoot - put input focus on the root window
4011 *
4012 ***********************************************************************
4013 */
4014
4015void FocusOnRoot(void)
4016{
4017    SetFocus ((TwmWindow *) NULL, LastTimestamp());
4018    InstallColormaps(0, &Scr->RootColormaps);
4019    if (! Scr->ClickToFocus) Scr->FocusRoot = TRUE;
4020}
4021
4022static void ReMapOne(TwmWindow *t, TwmWindow *leader)
4023{
4024    if (t->icon_on)
4025	Zoom(t->icon->w, t->frame);
4026    else if (leader->icon)
4027	Zoom(leader->icon->w, t->frame);
4028
4029    if (!t->squeezed)
4030	XMapWindow(dpy, t->w);
4031    t->mapped = TRUE;
4032    if (Scr->Root != Scr->CaptiveRoot)	/* XXX dubious test */
4033	XReparentWindow (dpy, t->frame, Scr->Root, t->frame_x, t->frame_y);
4034    if (Scr->NoRaiseDeicon)
4035	XMapWindow(dpy, t->frame);
4036    else
4037	MapRaised(t);
4038    SetMapStateProp(t, NormalState);
4039
4040    if (t->icon && t->icon->w) {
4041	XUnmapWindow(dpy, t->icon->w);
4042	IconDown(t);
4043	if (Scr->ShrinkIconTitles)
4044	    t->icon->title_shrunk = True;
4045    }
4046    if (t->iconmanagerlist) {
4047	WList *wl;
4048
4049	for (wl = t->iconmanagerlist; wl != NULL; wl = wl->nextv)
4050	    XUnmapWindow(dpy, wl->icon);
4051    }
4052    t->isicon = FALSE;
4053    t->icon_on = FALSE;
4054    WMapDeIconify(t);
4055}
4056
4057static void ReMapTransients(TwmWindow *tmp_win)
4058{
4059    TwmWindow *t;
4060
4061    /* find t such that it is a transient or group member window */
4062    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
4063	if (t != tmp_win &&
4064		((t->transient && t->transientfor == tmp_win->w) ||
4065		 (t->group == tmp_win->w && t->isicon))) {
4066	    ReMapOne(t, tmp_win);
4067	}
4068    }
4069}
4070
4071void DeIconify(TwmWindow *tmp_win)
4072{
4073    TwmWindow *t = tmp_win;
4074    int isicon = FALSE;
4075
4076    /* de-iconify the main window */
4077    if (Scr->WindowMask)
4078	XRaiseWindow (dpy, Scr->WindowMask);
4079    if (tmp_win->isicon)
4080    {
4081	isicon = TRUE;
4082	if (tmp_win->icon_on && tmp_win->icon && tmp_win->icon->w)
4083	    Zoom(tmp_win->icon->w, tmp_win->frame);
4084	else if (tmp_win->group != (Window) 0)
4085	{
4086	    t = GetTwmWindow(tmp_win->group);
4087	    if (t && t->icon_on && t->icon && t->icon->w)
4088	    {
4089		Zoom(t->icon->w, tmp_win->frame);
4090	    }
4091	}
4092    }
4093
4094    ReMapOne(tmp_win, t);
4095
4096    if (isicon &&
4097	(Scr->WarpCursor ||
4098	 LookInList(Scr->WarpCursorL, tmp_win->full_name, &tmp_win->class)))
4099      WarpToWindow (tmp_win, 0);
4100
4101    /* now de-iconify and window group transients */
4102    ReMapTransients(tmp_win);
4103
4104    if (! Scr->WindowMask && Scr->DeIconifyFunction.func != 0) {
4105	char *action;
4106	XEvent event;
4107
4108	action = Scr->DeIconifyFunction.item ?
4109		Scr->DeIconifyFunction.item->action : NULL;
4110	ExecuteFunction (Scr->DeIconifyFunction.func, action,
4111			   (Window) 0, tmp_win, &event, C_ROOT, FALSE);
4112    }
4113    XSync (dpy, 0);
4114}
4115
4116
4117static void UnmapTransients(TwmWindow *tmp_win, int iconify, unsigned long eventMask)
4118{
4119    TwmWindow *t;
4120
4121    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
4122	if (t != tmp_win &&
4123		((t->transient && t->transientfor == tmp_win->w) ||
4124		 t->group == tmp_win->w)) {
4125	    if (iconify) {
4126		if (t->icon_on)
4127		    Zoom(t->icon->w, tmp_win->icon->w);
4128		else if (tmp_win->icon)
4129		    Zoom(t->frame, tmp_win->icon->w);
4130	    }
4131
4132	    /*
4133	     * Prevent the receipt of an UnmapNotify, since that would
4134	     * cause a transition to the Withdrawn state.
4135	     */
4136	    t->mapped = FALSE;
4137	    XSelectInput(dpy, t->w, eventMask & ~StructureNotifyMask);
4138	    XUnmapWindow(dpy, t->w);
4139	    XUnmapWindow(dpy, t->frame);
4140	    XSelectInput(dpy, t->w, eventMask);
4141	    if (t->icon && t->icon->w) XUnmapWindow(dpy, t->icon->w);
4142	    SetMapStateProp(t, IconicState);
4143	    if (t == Scr->Focus) {
4144		SetFocus ((TwmWindow *) NULL, LastTimestamp());
4145		if (! Scr->ClickToFocus) Scr->FocusRoot = TRUE;
4146	    }
4147	    if (t->iconmanagerlist)
4148		XMapWindow(dpy, t->iconmanagerlist->icon);
4149	    t->isicon = TRUE;
4150	    t->icon_on = FALSE;
4151	    WMapIconify (t);
4152	}
4153    }
4154}
4155
4156void Iconify(TwmWindow *tmp_win, int def_x, int def_y)
4157{
4158    TwmWindow *t;
4159    int iconify;
4160    XWindowAttributes winattrs;
4161    unsigned long eventMask;
4162    WList *wl;
4163    Window leader = (Window)-1;
4164    Window blanket = (Window)-1;
4165
4166    iconify = (!tmp_win->iconify_by_unmapping);
4167    t = (TwmWindow*) 0;
4168    if (tmp_win->transient) {
4169	leader = tmp_win->transientfor;
4170	t = GetTwmWindow(leader);
4171    }
4172    else
4173    if ((leader = tmp_win->group) != 0 && leader != tmp_win->w) {
4174	t = GetTwmWindow(leader);
4175    }
4176    if (t && t->icon_on) iconify = False;
4177    if (iconify)
4178    {
4179	if (!tmp_win->icon || !tmp_win->icon->w)
4180	    CreateIconWindow(tmp_win, def_x, def_y);
4181	else
4182	    IconUp(tmp_win);
4183	if (visible (tmp_win)) {
4184	    if (Scr->WindowMask) {
4185		XRaiseWindow (dpy, Scr->WindowMask);
4186		XMapWindow(dpy, tmp_win->icon->w);
4187	    }
4188	    else
4189		XMapRaised(dpy, tmp_win->icon->w);
4190	}
4191    }
4192    if (tmp_win->iconmanagerlist) {
4193      for (wl = tmp_win->iconmanagerlist; wl != NULL; wl = wl->nextv) {
4194	XMapWindow(dpy, wl->icon);
4195      }
4196    }
4197
4198    XGetWindowAttributes(dpy, tmp_win->w, &winattrs);
4199    eventMask = winattrs.your_event_mask;
4200
4201    /* iconify transients and window group first */
4202    UnmapTransients(tmp_win, iconify, eventMask);
4203
4204    if (iconify) Zoom(tmp_win->frame, tmp_win->icon->w);
4205
4206    /*
4207     * Prevent the receipt of an UnmapNotify, since that would
4208     * cause a transition to the Withdrawn state.
4209     */
4210    tmp_win->mapped = FALSE;
4211
4212    if ((Scr->IconifyStyle != ICONIFY_NORMAL) && !Scr->WindowMask) {
4213	XSetWindowAttributes attr;
4214	XGetWindowAttributes(dpy, tmp_win->frame, &winattrs);
4215	attr.backing_store = NotUseful;
4216	attr.save_under    = False;
4217	blanket = XCreateWindow (dpy, Scr->Root, winattrs.x, winattrs.y,
4218				 winattrs.width, winattrs.height, (unsigned int) 0,
4219				 CopyFromParent, (unsigned int) CopyFromParent,
4220				 (Visual *) CopyFromParent, CWBackingStore | CWSaveUnder, &attr);
4221	XMapWindow (dpy, blanket);
4222    }
4223    XSelectInput(dpy, tmp_win->w, eventMask & ~StructureNotifyMask);
4224    XUnmapWindow(dpy, tmp_win->w);
4225    XUnmapWindow(dpy, tmp_win->frame);
4226    XSelectInput(dpy, tmp_win->w, eventMask);
4227    SetMapStateProp(tmp_win, IconicState);
4228
4229    if ((Scr->IconifyStyle != ICONIFY_NORMAL) && !Scr->WindowMask) {
4230      switch (Scr->IconifyStyle) {
4231        case ICONIFY_MOSAIC:  MosaicFade    (tmp_win, blanket); break;
4232        case ICONIFY_ZOOMIN:  ZoomInWindow  (tmp_win, blanket); break;
4233        case ICONIFY_ZOOMOUT: ZoomOutWindow (tmp_win, blanket); break;
4234	case ICONIFY_SWEEP:   SweepWindow   (tmp_win, blanket); break;
4235      }
4236      XDestroyWindow (dpy, blanket);
4237    }
4238    if (tmp_win == Scr->Focus) {
4239	SetFocus ((TwmWindow *) NULL, LastTimestamp());
4240	if (! Scr->ClickToFocus) Scr->FocusRoot = TRUE;
4241    }
4242    tmp_win->isicon = TRUE;
4243    tmp_win->icon_on = iconify ? TRUE : FALSE;
4244    WMapIconify (tmp_win);
4245    if (! Scr->WindowMask && Scr->IconifyFunction.func != 0) {
4246	char *action;
4247	XEvent event;
4248
4249	action = Scr->IconifyFunction.item ? Scr->IconifyFunction.item->action : NULL;
4250	ExecuteFunction (Scr->IconifyFunction.func, action,
4251			   (Window) 0, tmp_win, &event, C_ROOT, FALSE);
4252    }
4253    XSync (dpy, 0);
4254}
4255
4256void AutoSqueeze (TwmWindow *tmp_win)
4257{
4258    if (tmp_win->iconmgr) return;
4259    if (Scr->RaiseWhenAutoUnSqueeze && tmp_win->squeezed) XRaiseWindow (dpy, tmp_win->frame);
4260    Squeeze (tmp_win);
4261}
4262
4263void Squeeze (TwmWindow *tmp_win)
4264{
4265    long fx, fy, savex, savey;
4266    int  neww, newh, south;
4267    int	 grav = ((tmp_win->hints.flags & PWinGravity)
4268		      ? tmp_win->hints.win_gravity : NorthWestGravity);
4269    XWindowAttributes winattrs;
4270    unsigned long eventMask;
4271#ifdef GNOME
4272    unsigned char	*prop;
4273    unsigned long	nitems, bytesafter;
4274    Atom		actual_type;
4275    int			actual_format;
4276    long		gwkspc;
4277#endif /* GNOME */
4278    if (tmp_win->squeezed) {
4279	tmp_win->squeezed = False;
4280#ifdef GNOME
4281 	XGetWindowAttributes (dpy, tmp_win->w, &winattrs);
4282	eventMask = winattrs.your_event_mask;
4283	XSelectInput (dpy, tmp_win->w, eventMask & ~PropertyChangeMask);
4284 	if (XGetWindowProperty (dpy, tmp_win->w, _XA_WIN_STATE, 0L, 32, False,
4285			       XA_CARDINAL, &actual_type, &actual_format,
4286			       &nitems, &bytesafter, &prop)
4287	    != Success || nitems == 0) {
4288	    gwkspc = 0;
4289	} else {
4290	    gwkspc = (int)*prop;
4291	    XFree ((char *)prop);
4292	}
4293 	gwkspc &= ~WIN_STATE_SHADED;
4294 	XChangeProperty (dpy, tmp_win->w, _XA_WIN_STATE, XA_CARDINAL, 32,
4295			 PropModeReplace, (unsigned char *)&gwkspc, 1);
4296	XSelectInput(dpy, tmp_win->w, eventMask);
4297#endif /* GNOME */
4298	if (!tmp_win->isicon) XMapWindow (dpy, tmp_win->w);
4299	SetupWindow (tmp_win, tmp_win->actual_frame_x, tmp_win->actual_frame_y,
4300		     tmp_win->actual_frame_width, tmp_win->actual_frame_height, -1);
4301	ReMapTransients(tmp_win);
4302	return;
4303    }
4304
4305    newh = tmp_win->title_height + 2 * tmp_win->frame_bw3D;
4306    if (newh < 3) { XBell (dpy, 0); return; }
4307    switch (grav) {
4308	case SouthWestGravity :
4309	case SouthGravity :
4310	case SouthEastGravity :
4311	    south = True; break;
4312	default :
4313	    south = False; break;
4314    }
4315    if (tmp_win->title_height && !tmp_win->AlwaysSqueezeToGravity) south = False;
4316
4317    tmp_win->squeezed = True;
4318    tmp_win->actual_frame_width  = tmp_win->frame_width;
4319    tmp_win->actual_frame_height = tmp_win->frame_height;
4320    savex = fx = tmp_win->frame_x;
4321    savey = fy = tmp_win->frame_y;
4322    neww  = tmp_win->actual_frame_width;
4323    if (south) fy += tmp_win->frame_height - newh;
4324    if (tmp_win->squeeze_info) {
4325	fx  += tmp_win->title_x - tmp_win->frame_bw3D;
4326	neww = tmp_win->title_width + 2 * (tmp_win->frame_bw + tmp_win->frame_bw3D);
4327    }
4328    XGetWindowAttributes(dpy, tmp_win->w, &winattrs);
4329    eventMask = winattrs.your_event_mask;
4330#ifdef GNOME
4331    XSelectInput (dpy, tmp_win->w, eventMask & ~(StructureNotifyMask | PropertyChangeMask));
4332    if (XGetWindowProperty (dpy, tmp_win->w, _XA_WIN_STATE, 0L, 32, False,
4333			    XA_CARDINAL, &actual_type, &actual_format, &nitems,
4334			    &bytesafter, &prop)
4335	!= Success || nitems == 0) {
4336	gwkspc = 0;
4337    } else {
4338	gwkspc = (int)*prop;
4339	XFree ((char *)prop);
4340    }
4341    gwkspc |= WIN_STATE_SHADED;
4342    XChangeProperty (dpy, tmp_win->w, _XA_WIN_STATE, XA_CARDINAL, 32,
4343		     PropModeReplace, (unsigned char *)&gwkspc, 1);
4344#else
4345    XSelectInput (dpy, tmp_win->w, eventMask & ~StructureNotifyMask);
4346#endif /* GNOME */
4347    XUnmapWindow(dpy, tmp_win->w);
4348    XSelectInput(dpy, tmp_win->w, eventMask);
4349
4350    if (fx + neww >= Scr->rootw - Scr->BorderRight)
4351        fx = Scr->rootw - Scr->BorderRight - neww;
4352    if (fy + newh >= Scr->rooth - Scr->BorderBottom)
4353        fy = Scr->rooth - Scr->BorderBottom - newh;
4354    SetupWindow (tmp_win, fx, fy, neww, newh, -1);
4355    tmp_win->actual_frame_x = savex;
4356    tmp_win->actual_frame_y = savey;
4357
4358    /* Now make the group members disappear */
4359    UnmapTransients(tmp_win, 0, eventMask);
4360}
4361
4362static void Identify (TwmWindow *t)
4363{
4364    int i, n, twidth, width, height;
4365    int x, y;
4366    unsigned int wwidth, wheight, bw, depth;
4367    Window junk;
4368    int px, py, dummy;
4369    unsigned udummy;
4370    unsigned char	*prop;
4371    unsigned long	nitems, bytesafter;
4372    Atom		actual_type;
4373    int			actual_format;
4374    XRectangle inc_rect;
4375    XRectangle logical_rect;
4376    Bool first = True;
4377
4378    n = 0;
4379    (void) sprintf(Info[n++], "Twm version:  %s", Version);
4380    (void) sprintf(Info[n], "Compile time options :");
4381#ifdef XPM
4382    (void) strcat (Info[n], " XPM");
4383    first = False;
4384#endif
4385#ifdef IMCONV
4386    if (!first) (void) strcat(Info[n], ", ");
4387    (void) strcat (Info[n], "IMCONV");
4388    first = False;
4389#endif
4390#ifdef USEM4
4391    if (!first) (void) strcat(Info[n], ", ");
4392    (void) strcat (Info[n], "USEM4");
4393    first = False;
4394#endif
4395#ifdef GNOME
4396    if (!first) (void) strcat(Info[n], ", ");
4397    (void) strcat (Info[n], "GNOME");
4398    first = False;
4399#endif
4400#ifdef SOUNDS
4401    if (!first) (void) strcat(Info[n], ", ");
4402    (void) strcat (Info[n], "SOUNDS");
4403    first = False;
4404#endif
4405#ifdef DEBUG
4406    if (!first) (void) strcat(Info[n], ", ");
4407    (void) strcat (Info[n], "debug");
4408    first = False;
4409#endif
4410    if (!first) (void) strcat(Info[n], ", ");
4411    (void) strcat (Info[n], "I18N");
4412    first = False;
4413    n++;
4414    Info[n++][0] = '\0';
4415
4416    if (t) {
4417	XGetGeometry (dpy, t->w, &JunkRoot, &JunkX, &JunkY,
4418		      &wwidth, &wheight, &bw, &depth);
4419	(void) XTranslateCoordinates (dpy, t->w, Scr->Root, 0, 0,
4420				      &x, &y, &junk);
4421	(void) sprintf(Info[n++], "Name               = \"%s\"", t->full_name);
4422	(void) sprintf(Info[n++], "Class.res_name     = \"%s\"", t->class.res_name);
4423	(void) sprintf(Info[n++], "Class.res_class    = \"%s\"", t->class.res_class);
4424	Info[n++][0] = '\0';
4425	(void) sprintf(Info[n++], "Geometry/root (UL)  = %dx%d+%d+%d (Inner: %dx%d+%d+%d)",
4426		       wwidth + 2 * (bw + t->frame_bw3D),
4427		       wheight + 2 * (bw + t->frame_bw3D) + t->title_height,
4428		       x - (bw + t->frame_bw3D),
4429		       y - (bw + t->frame_bw3D + t->title_height),
4430		       wwidth, wheight, x, y);
4431	(void) sprintf(Info[n++], "Geometry/root (LR)  = %dx%d-%d-%d (Inner: %dx%d-%d-%d)",
4432		       wwidth + 2 * (bw + t->frame_bw3D),
4433		       wheight + 2 * (bw + t->frame_bw3D) + t->title_height,
4434		       Scr->rootw - (x + wwidth + bw + t->frame_bw3D),
4435		       Scr->rooth - (y + wheight + bw + t->frame_bw3D),
4436		       wwidth, wheight,
4437		       Scr->rootw - (x + wwidth), Scr->rooth - (y + wheight));
4438	(void) sprintf(Info[n++], "Border width       = %d", bw);
4439	(void) sprintf(Info[n++], "3D border width    = %d", t->frame_bw3D);
4440	(void) sprintf(Info[n++], "Depth              = %d", depth);
4441
4442	if (XGetWindowProperty (dpy, t->w, _XA_WM_CLIENT_MACHINE, 0L, 64, False,
4443				XA_STRING, &actual_type, &actual_format, &nitems,
4444				&bytesafter, &prop) == Success) {
4445	    if (nitems && prop) {
4446		(void) sprintf(Info[n++], "Client machine     = %s", (char*)prop);
4447		XFree ((char *) prop);
4448	    }
4449	}
4450	Info[n++][0] = '\0';
4451    }
4452
4453    (void) sprintf(Info[n++], "Click to dismiss....");
4454
4455    /* figure out the width and height of the info window */
4456    height = n * (Scr->DefaultFont.height+2);
4457    width = 1;
4458    for (i = 0; i < n; i++)
4459    {
4460	XmbTextExtents(Scr->DefaultFont.font_set, Info[i],
4461		       strlen(Info[i]), &inc_rect, &logical_rect);
4462
4463	twidth = logical_rect.width;
4464	if (twidth > width)
4465	    width = twidth;
4466    }
4467    if (InfoLines) XUnmapWindow(dpy, Scr->InfoWindow);
4468
4469    width += 10;		/* some padding */
4470    height += 10;		/* some padding */
4471    if (XQueryPointer (dpy, Scr->Root, &JunkRoot, &JunkChild,
4472		       &dummy, &dummy, &px, &py, &udummy)) {
4473	px -= (width / 2);
4474	py -= (height / 3);
4475	if (px + width + BW2 >= Scr->rootw)
4476	  px = Scr->rootw - width - BW2;
4477	if (py + height + BW2 >= Scr->rooth)
4478	  py = Scr->rooth - height - BW2;
4479	if (px < 0) px = 0;
4480	if (py < 0) py = 0;
4481    } else {
4482	px = py = 0;
4483    }
4484    XMoveResizeWindow(dpy, Scr->InfoWindow, px, py, width, height);
4485    XMapRaised(dpy, Scr->InfoWindow);
4486    InfoLines  = n;
4487    InfoWidth  = width;
4488    InfoHeight = height;
4489}
4490
4491
4492
4493 void SetMapStateProp(TwmWindow *tmp_win, int state)
4494{
4495    unsigned long data[2];		/* "suggested" by ICCCM version 1 */
4496
4497    data[0] = (unsigned long) state;
4498    data[1] = (unsigned long) (tmp_win->iconify_by_unmapping ? None :
4499			   (tmp_win->icon ? tmp_win->icon->w : None));
4500
4501    XChangeProperty (dpy, tmp_win->w, _XA_WM_STATE, _XA_WM_STATE, 32,
4502		 PropModeReplace, (unsigned char *) data, 2);
4503}
4504
4505
4506
4507Bool GetWMState (Window w, int *statep, Window *iwp)
4508{
4509    Atom actual_type;
4510    int actual_format;
4511    unsigned long nitems, bytesafter;
4512    unsigned long *datap = NULL;
4513    Bool retval = False;
4514
4515    if (XGetWindowProperty (dpy, w, _XA_WM_STATE, 0L, 2L, False, _XA_WM_STATE,
4516			    &actual_type, &actual_format, &nitems, &bytesafter,
4517			    (unsigned char **) &datap) != Success || !datap)
4518      return False;
4519
4520    if (nitems <= 2) {			/* "suggested" by ICCCM version 1 */
4521	*statep = (int) datap[0];
4522	*iwp = (Window) datap[1];
4523	retval = True;
4524    }
4525
4526    XFree ((char *) datap);
4527    return retval;
4528}
4529
4530
4531
4532int WarpToScreen (int n, int inc)
4533{
4534    Window dumwin;
4535    int x, y, dumint;
4536    unsigned int dummask;
4537    ScreenInfo *newscr = NULL;
4538
4539    while (!newscr) {
4540					/* wrap around */
4541	if (n < 0)
4542	  n = NumScreens - 1;
4543	else if (n >= NumScreens)
4544	  n = 0;
4545
4546	newscr = ScreenList[n];
4547	if (!newscr) {			/* make sure screen is managed */
4548	    if (inc) {			/* walk around the list */
4549		n += inc;
4550		continue;
4551	    }
4552	    fprintf (stderr, "%s:  unable to warp to unmanaged screen %d\n",
4553		     ProgramName, n);
4554	    XBell (dpy, 0);
4555	    return (1);
4556	}
4557    }
4558
4559    if (Scr->screen == n) return (0);	/* already on that screen */
4560
4561    PreviousScreen = Scr->screen;
4562    XQueryPointer (dpy, Scr->Root, &dumwin, &dumwin, &x, &y,
4563		   &dumint, &dumint, &dummask);
4564
4565    XWarpPointer (dpy, None, newscr->Root, 0, 0, 0, 0, x, y);
4566    Scr = newscr;
4567    return (0);
4568}
4569
4570
4571
4572
4573/*
4574 * BumpWindowColormap - rotate our internal copy of WM_COLORMAP_WINDOWS
4575 */
4576
4577int BumpWindowColormap (TwmWindow *tmp, int inc)
4578{
4579    int i, j, previously_installed;
4580    ColormapWindow **cwins;
4581
4582    if (!tmp) return (1);
4583
4584    if (inc && tmp->cmaps.number_cwins > 0) {
4585	cwins = (ColormapWindow **) malloc(sizeof(ColormapWindow *)*
4586					   tmp->cmaps.number_cwins);
4587	if (cwins) {
4588	    if ((previously_installed =
4589		 /* SUPPRESS 560 */(Scr->cmapInfo.cmaps == &tmp->cmaps &&
4590				    tmp->cmaps.number_cwins))) {
4591		for (i = tmp->cmaps.number_cwins; i-- > 0; )
4592		    tmp->cmaps.cwins[i]->colormap->state = 0;
4593	    }
4594
4595	    for (i = 0; i < tmp->cmaps.number_cwins; i++) {
4596		j = i - inc;
4597		if (j >= tmp->cmaps.number_cwins)
4598		    j -= tmp->cmaps.number_cwins;
4599		else if (j < 0)
4600		    j += tmp->cmaps.number_cwins;
4601		cwins[j] = tmp->cmaps.cwins[i];
4602	    }
4603
4604	    free((char *) tmp->cmaps.cwins);
4605
4606	    tmp->cmaps.cwins = cwins;
4607
4608	    if (tmp->cmaps.number_cwins > 1)
4609		memset (tmp->cmaps.scoreboard, 0,
4610		       ColormapsScoreboardLength(&tmp->cmaps));
4611
4612	    if (previously_installed) {
4613		InstallColormaps(PropertyNotify, NULL);
4614	    }
4615	}
4616    } else
4617	FetchWmColormapWindows (tmp);
4618    return (1);
4619}
4620
4621
4622
4623void ShowIconManager (void)
4624{
4625    IconMgr   *i;
4626    WorkSpace *wl;
4627
4628    if (! Scr->workSpaceManagerActive) return;
4629
4630    if (Scr->NoIconManagers) return;
4631    for (wl = Scr->workSpaceMgr.workSpaceList; wl != NULL; wl = wl->next) {
4632	for (i = wl->iconmgr; i != NULL; i = i->next) {
4633	    if (i->count == 0) continue;
4634	    if (visible (i->twm_win)) {
4635		SetMapStateProp (i->twm_win, NormalState);
4636		XMapWindow (dpy, i->twm_win->w);
4637		MapRaised (i->twm_win);
4638		if (i->twm_win->icon && i->twm_win->icon->w)
4639		    XUnmapWindow (dpy, i->twm_win->icon->w);
4640	    }
4641	    i->twm_win->mapped = TRUE;
4642	    i->twm_win->isicon = FALSE;
4643	}
4644    }
4645}
4646
4647
4648void HideIconManager (void)
4649{
4650    IconMgr   *i;
4651    WorkSpace *wl;
4652
4653    if (Scr->NoIconManagers) return;
4654    for (wl = Scr->workSpaceMgr.workSpaceList; wl != NULL; wl = wl->next) {
4655	for (i = wl->iconmgr; i != NULL; i = i->next) {
4656	    SetMapStateProp (i->twm_win, WithdrawnState);
4657	    XUnmapWindow(dpy, i->twm_win->frame);
4658	    if (i->twm_win->icon && i->twm_win->icon->w) XUnmapWindow (dpy, i->twm_win->icon->w);
4659	    i->twm_win->mapped = FALSE;
4660	    i->twm_win->isicon = TRUE;
4661	}
4662    }
4663}
4664
4665
4666
4667
4668void DestroyMenu (MenuRoot *menu)
4669{
4670    MenuItem *item;
4671
4672    if (menu->w) {
4673	XDeleteContext (dpy, menu->w, MenuContext);
4674	XDeleteContext (dpy, menu->w, ScreenContext);
4675	if (Scr->Shadow) XDestroyWindow (dpy, menu->shadow);
4676	XDestroyWindow(dpy, menu->w);
4677    }
4678
4679    for (item = menu->first; item; ) {
4680	MenuItem *tmp = item;
4681	item = item->next;
4682	free ((char *) tmp);
4683    }
4684}
4685
4686
4687
4688/*
4689 * warping routines
4690 */
4691
4692void WarpAlongRing (XButtonEvent *ev, Bool forward)
4693{
4694    TwmWindow *r, *head;
4695
4696    if (Scr->RingLeader)
4697      head = Scr->RingLeader;
4698    else if (!(head = Scr->Ring))
4699      return;
4700
4701    if (forward) {
4702	for (r = head->ring.next; r != head; r = r->ring.next) {
4703	    if (!r) break;
4704	    if (r->mapped && (Scr->WarpRingAnyWhere || visible (r))) break;
4705	}
4706    } else {
4707	for (r = head->ring.prev; r != head; r = r->ring.prev) {
4708	    if (!r) break;
4709	    if (r->mapped && (Scr->WarpRingAnyWhere || visible (r))) break;
4710	}
4711    }
4712
4713    /* Note: (Scr->Focus != r) is necessary when we move to a workspace that
4714       has a single window and we want warping to warp to it. */
4715    if (r && (r != head || Scr->Focus != r)) {
4716	TwmWindow *p = Scr->RingLeader, *t;
4717
4718	Scr->RingLeader = r;
4719	WarpToWindow (r, 1);
4720
4721	if (p && p->mapped &&
4722	    (t = GetTwmWindow(ev->window)) &&
4723	    p == t) {
4724	    p->ring.cursor_valid = True;
4725	    p->ring.curs_x = ev->x_root - t->frame_x;
4726	    p->ring.curs_y = ev->y_root - t->frame_y;
4727#ifdef DEBUG
4728	    fprintf(stderr, "WarpAlongRing: cursor_valid := True; x := %d (%d-%d), y := %d (%d-%d)\n", Tmp_win->ring.curs_x, ev->x_root, t->frame_x, Tmp_win->ring.curs_y, ev->y_root, t->frame_y);
4729#endif
4730	    /*
4731	     * The check if the cursor position is inside the window is now
4732	     * done in WarpToWindow().
4733	     */
4734	}
4735    }
4736}
4737
4738
4739
4740void WarpToWindow (TwmWindow *t, int must_raise)
4741{
4742    int x, y;
4743
4744    if (t->ring.cursor_valid) {
4745	x = t->ring.curs_x;
4746	y = t->ring.curs_y;
4747#ifdef DEBUG
4748	fprintf(stderr, "WarpToWindow: cursor_valid; x == %d, y == %d\n", x, y);
4749#endif
4750
4751	/*
4752	 * XXX is this correct with 3D borders? Easier check possible?
4753	 * frame_bw is for the left border.
4754	 */
4755	if (x < t->frame_bw)
4756	    x = t->frame_bw;
4757	if (x >= t->frame_width + t->frame_bw)
4758	    x  = t->frame_width + t->frame_bw - 1;
4759	if (y < t->title_height + t->frame_bw)
4760	    y = t->title_height + t->frame_bw;
4761	if (y >= t->frame_height + t->frame_bw)
4762	    y  = t->frame_height + t->frame_bw - 1;
4763#ifdef DEBUG
4764	fprintf(stderr, "WarpToWindow: adjusted    ; x := %d, y := %d\n", x, y);
4765#endif
4766    } else {
4767	x = t->frame_width / 2;
4768	y = t->frame_height / 2;
4769#ifdef DEBUG
4770	fprintf(stderr, "WarpToWindow: middle; x := %d, y := %d\n", x, y);
4771#endif
4772    }
4773#if 0
4774    int dest_x, dest_y;
4775    Window child;
4776
4777    /*
4778     * Check if the proposed position actually is visible. If not, raise the window.
4779     * "If the coordinates are contained in a mapped
4780     * child of dest_w, that child is returned to child_return."
4781     * We'll need to check for the right child window; the frame probably.
4782     * (What about XXX window boxes?)
4783     *
4784     * Alternatively, use XQueryPointer() which returns the root window
4785     * the pointer is in, but XXX that won't work for VirtualScreens.
4786     */
4787    if (XTranslateCoordinates(dpy, t->frame, Scr->Root, x, y, &dest_x, &dest_y, &child)) {
4788	if (child != t->frame)
4789	    must_raise = 1;
4790    }
4791#endif
4792    if (t->auto_raise || must_raise) AutoRaiseWindow (t);
4793    if (! visible (t)) {
4794	WorkSpace *wlist;
4795
4796	for (wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL; wlist = wlist->next) {
4797	    if (OCCUPY (t, wlist)) break;
4798	}
4799	if (wlist != NULL) GotoWorkSpace (Scr->currentvs, wlist);
4800    }
4801    XWarpPointer (dpy, None, Scr->Root, 0, 0, 0, 0, x + t->frame_x, y + t->frame_y);
4802#ifdef DEBUG
4803    {
4804	Window root_return;
4805	Window child_return;
4806	int root_x_return;
4807	int root_y_return;
4808	int win_x_return;
4809	int win_y_return;
4810	unsigned int mask_return;
4811
4812	if (XQueryPointer(dpy, t->frame, &root_return, &child_return, &root_x_return, &root_y_return, &win_x_return, &win_y_return, &mask_return)) {
4813	    fprintf(stderr, "XQueryPointer: root_return=%x, child_return=%x, root_x_return=%d, root_y_return=%d, win_x_return=%d, win_y_return=%d\n", root_return, child_return, root_x_return, root_y_return, win_x_return, win_y_return);
4814	}
4815    }
4816#endif
4817}
4818
4819
4820
4821
4822/*
4823 * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
4824 * client messages will have the following form:
4825 *
4826 *     event type	ClientMessage
4827 *     message type	_XA_WM_PROTOCOLS
4828 *     window		tmp->w
4829 *     format		32
4830 *     data[0]		message atom
4831 *     data[1]		time stamp
4832 */
4833static void send_clientmessage (Window w, Atom a, Time timestamp)
4834{
4835    XClientMessageEvent ev;
4836
4837    ev.type = ClientMessage;
4838    ev.window = w;
4839    ev.message_type = _XA_WM_PROTOCOLS;
4840    ev.format = 32;
4841    ev.data.l[0] = a;
4842    ev.data.l[1] = timestamp;
4843    XSendEvent (dpy, w, False, 0L, (XEvent *) &ev);
4844}
4845
4846void SendDeleteWindowMessage (TwmWindow *tmp, Time timestamp)
4847{
4848    send_clientmessage (tmp->w, _XA_WM_DELETE_WINDOW, timestamp);
4849}
4850
4851void SendSaveYourselfMessage (TwmWindow *tmp, Time timestamp)
4852{
4853    send_clientmessage (tmp->w, _XA_WM_SAVE_YOURSELF, timestamp);
4854}
4855
4856
4857void SendTakeFocusMessage (TwmWindow *tmp, Time timestamp)
4858{
4859    send_clientmessage (tmp->w, _XA_WM_TAKE_FOCUS, timestamp);
4860}
4861
4862int MoveMenu (XEvent *eventp)
4863{
4864    int    XW, YW, newX, newY, cont;
4865    Bool   newev;
4866    unsigned long event_mask;
4867    XEvent ev;
4868
4869    if (! ActiveMenu) return (1);
4870    if (! ActiveMenu->pinned) return (1);
4871
4872    XW = eventp->xbutton.x_root - ActiveMenu->x;
4873    YW = eventp->xbutton.y_root - ActiveMenu->y;
4874    XGrabPointer (dpy, ActiveMenu->w, True,
4875		ButtonPressMask  | ButtonReleaseMask | ButtonMotionMask,
4876		GrabModeAsync, GrabModeAsync,
4877		None, Scr->MoveCursor, CurrentTime);
4878
4879    newX = ActiveMenu->x;
4880    newY = ActiveMenu->y;
4881    cont = TRUE;
4882    event_mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask | ExposureMask;
4883    XMaskEvent (dpy, event_mask, &ev);
4884    while (cont) {
4885	ev.xbutton.x_root -= Scr->rootx;
4886	ev.xbutton.y_root -= Scr->rooty;
4887	switch (ev.xany.type) {
4888	    case ButtonRelease :
4889		cont = FALSE;
4890	    case MotionNotify :
4891		if (!cont) {
4892		    newev = False;
4893		    while (XCheckMaskEvent (dpy, ButtonMotionMask | ButtonReleaseMask, &ev)) {
4894			newev = True;
4895			if (ev.type == ButtonRelease) break;
4896		    }
4897		    if (ev.type == ButtonRelease) continue;
4898		    if (newev) {
4899			ev.xbutton.x_root -= Scr->rootx;
4900			ev.xbutton.y_root -= Scr->rooty;
4901		    }
4902		}
4903        	newX = ev.xbutton.x_root - XW;
4904        	newY = ev.xbutton.y_root - YW;
4905		if (Scr->DontMoveOff)
4906                {
4907                    ConstrainByBorders1 (&newX, ActiveMenu->width,
4908                                        &newY, ActiveMenu->height);
4909		}
4910		XMoveWindow (dpy, ActiveMenu->w, newX, newY);
4911		XMaskEvent (dpy, event_mask, &ev);
4912		break;
4913	    case ButtonPress :
4914		cont = FALSE;
4915		newX = ActiveMenu->x;
4916		newY = ActiveMenu->y;
4917		break;
4918	    case Expose:
4919	    case NoExpose:
4920                Event = ev;
4921                DispatchEvent ();
4922		XMaskEvent (dpy, event_mask, &ev);
4923		break;
4924	}
4925    }
4926    XUngrabPointer (dpy, CurrentTime);
4927    if (ev.xany.type == ButtonRelease) ButtonPressed = -1;
4928    /*XPutBackEvent (dpy, &ev);*/
4929    XMoveWindow (dpy, ActiveMenu->w, newX, newY);
4930    ActiveMenu->x = newX;
4931    ActiveMenu->y = newY;
4932    MenuOrigins [MenuDepth - 1].x = newX;
4933    MenuOrigins [MenuDepth - 1].y = newY;
4934
4935    return (1);
4936}
4937
4938/***********************************************************************
4939 *
4940 *  Procedure:
4941 *      DisplayPosition - display the position in the dimensions window
4942 *
4943 *  Inputs:
4944 *      tmp_win - the current twm window
4945 *      x, y    - position of the window
4946 *
4947 ***********************************************************************
4948 */
4949
4950void DisplayPosition (TwmWindow *tmp_win, int x, int y)
4951{
4952    char str [100];
4953    char signx = '+';
4954    char signy = '+';
4955
4956    if (x < 0) {
4957	x = -x;
4958	signx = '-';
4959    }
4960    if (y < 0) {
4961	y = -y;
4962	signy = '-';
4963    }
4964    (void) sprintf (str, " %c%-4d %c%-4d ", signx, x, signy, y);
4965    XRaiseWindow (dpy, Scr->SizeWindow);
4966
4967    Draw3DBorder (Scr->SizeWindow, 0, 0,
4968		Scr->SizeStringOffset + Scr->SizeStringWidth + SIZE_HINDENT,
4969		Scr->SizeFont.height + SIZE_VINDENT * 2,
4970		2, Scr->DefaultC, off, False, False);
4971
4972    FB(Scr->DefaultC.fore, Scr->DefaultC.back);
4973    XmbDrawImageString (dpy, Scr->SizeWindow, Scr->SizeFont.font_set,
4974			Scr->NormalGC, Scr->SizeStringOffset,
4975			Scr->SizeFont.ascent + SIZE_VINDENT , str, 13);
4976}
4977
4978void MosaicFade (TwmWindow *tmp_win, Window blanket)
4979{
4980    int		srect;
4981    int		i, j, nrects;
4982    Pixmap	mask;
4983    GC		gc;
4984    XGCValues	gcv;
4985    XRectangle *rectangles;
4986    int  width = tmp_win->frame_width;
4987    int height = tmp_win->frame_height;
4988
4989    srect = (width < height) ? (width / 20) : (height / 20);
4990    mask  = XCreatePixmap (dpy, blanket, width, height, 1);
4991
4992    gcv.foreground = 1;
4993    gc = XCreateGC (dpy, mask, GCForeground, &gcv);
4994    XFillRectangle (dpy, mask, gc, 0, 0, width, height);
4995    gcv.function = GXclear;
4996    XChangeGC (dpy, gc, GCFunction, &gcv);
4997
4998    nrects = ((width * height) / (srect * srect)) / 10;
4999    rectangles = (XRectangle*) malloc (nrects * sizeof (XRectangle));
5000    for (j = 0; j < nrects; j++) {
5001	rectangles [j].width  = srect;
5002	rectangles [j].height = srect;
5003    }
5004    for (i = 0; i < 10; i++) {
5005	for (j = 0; j < nrects; j++) {
5006	    rectangles [j].x = ((lrand48 () %  width) / srect) * srect;
5007	    rectangles [j].y = ((lrand48 () % height) / srect) * srect;
5008	}
5009	XFillRectangles (dpy, mask, gc, rectangles, nrects);
5010	XShapeCombineMask (dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet);
5011	XFlush (dpy);
5012	waitamoment (0.020);
5013    }
5014    XFreePixmap (dpy, mask);
5015    XFreeGC (dpy, gc);
5016    free (rectangles);
5017}
5018
5019void ZoomInWindow (TwmWindow *tmp_win, Window blanket)
5020{
5021  Pixmap	mask;
5022  GC		gc, gcn;
5023  XGCValues	gcv;
5024
5025  int i, nsteps = 20;
5026  int w = tmp_win->frame_width;
5027  int h = tmp_win->frame_height;
5028  int step = (MAX (w, h)) / (2.0 * nsteps);
5029
5030  mask = XCreatePixmap (dpy, blanket, w, h, 1);
5031  gcv.foreground = 1;
5032  gc  = XCreateGC (dpy, mask, GCForeground, &gcv);
5033  gcv.function = GXclear;
5034  gcn = XCreateGC (dpy, mask, GCForeground | GCFunction, &gcv);
5035
5036  for (i = 0; i < nsteps; i++) {
5037    XFillRectangle (dpy, mask, gcn, 0, 0, w, h);
5038    XFillArc (dpy, mask, gc, (w / 2) - ((nsteps - i) * step),
5039	                     (h / 2) - ((nsteps - i) * step),
5040	                     2 * (nsteps - i) * step,
5041	                     2 * (nsteps - i) * step,
5042	                     0, 360*64);
5043    XShapeCombineMask (dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet);
5044    XFlush (dpy);
5045    waitamoment (0.020);
5046  }
5047}
5048
5049void ZoomOutWindow (TwmWindow *tmp_win, Window blanket)
5050{
5051  Pixmap	mask;
5052  GC		gc;
5053  XGCValues	gcv;
5054
5055  int i, nsteps = 20;
5056  int w = tmp_win->frame_width;
5057  int h = tmp_win->frame_height;
5058  int step = (MAX (w, h)) / (2.0 * nsteps);
5059
5060  mask  = XCreatePixmap (dpy, blanket, w, h, 1);
5061  gcv.foreground = 1;
5062  gc = XCreateGC (dpy, mask, GCForeground, &gcv);
5063  XFillRectangle (dpy, mask, gc, 0, 0, w, h);
5064  gcv.function = GXclear;
5065  XChangeGC (dpy, gc, GCFunction, &gcv);
5066
5067  for (i = 0; i < nsteps; i++) {
5068    XFillArc (dpy, mask, gc, (w / 2) - (i * step),
5069	                     (h / 2) - (i * step),
5070	                     2 * i * step,
5071	                     2 * i * step,
5072	                     0, 360*64);
5073    XShapeCombineMask (dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet);
5074    XFlush (dpy);
5075    waitamoment (0.020);
5076  }
5077}
5078
5079void FadeWindow (TwmWindow *tmp_win, Window blanket)
5080{
5081  Pixmap	mask, stipple;
5082  GC		gc;
5083  XGCValues	gcv;
5084  static unsigned char stipple_bits[] = { 0x0F, 0x0F,
5085					  0xF0, 0xF0,
5086					  0x0F, 0x0F,
5087					  0xF0, 0xF0,
5088					  0x0F, 0x0F,
5089					  0xF0, 0xF0,
5090					  0x0F, 0x0F,
5091					  0xF0, 0xF0,
5092  };
5093  int w = tmp_win->frame_width;
5094  int h = tmp_win->frame_height;
5095
5096  stipple = XCreateBitmapFromData (dpy, blanket, (char *)stipple_bits, 8, 8);
5097  mask    = XCreatePixmap (dpy, blanket, w, h, 1);
5098  gcv.background = 0;
5099  gcv.foreground = 1;
5100  gcv.stipple    = stipple;
5101  gcv.fill_style = FillOpaqueStippled;
5102  gc = XCreateGC (dpy, mask, GCBackground | GCForeground | GCFillStyle | GCStipple, &gcv);
5103  XFillRectangle (dpy, mask, gc, 0, 0, w, h);
5104
5105  XShapeCombineMask (dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet);
5106  XFlush (dpy);
5107  waitamoment (10.0);
5108  XFreePixmap (dpy, stipple);
5109}
5110
5111void SweepWindow (TwmWindow *tmp_win, Window	blanket)
5112{
5113  float step = 0.0;
5114  int i, nsteps = 20;
5115  int dir = 0, dist = tmp_win->frame_x, dist1;
5116
5117  dist1 = tmp_win->frame_y;
5118  if (dist1 < dist) { dir = 1; dist = dist1; }
5119  dist1 = tmp_win->vs->w - (tmp_win->frame_x + tmp_win->frame_width);
5120  if (dist1 < dist) { dir = 2; dist = dist1; }
5121  dist1 = tmp_win->vs->h - (tmp_win->frame_y + tmp_win->frame_height);
5122  if (dist1 < dist) { dir = 3; dist = dist1; }
5123
5124  switch (dir) {
5125    case 0: step = tmp_win->frame_x + tmp_win->frame_width;  break;
5126    case 1: step = tmp_win->frame_y + tmp_win->frame_height; break;
5127    case 2: step = tmp_win->vs->w - tmp_win->frame_x;        break;
5128    case 3: step = tmp_win->vs->h - tmp_win->frame_y;        break;
5129  }
5130  step /= (float) nsteps;
5131  step /= (float) nsteps;
5132  for (i = 0; i < 20; i++) {
5133    int x = tmp_win->frame_x;
5134    int y = tmp_win->frame_y;
5135    switch (dir) {
5136      case 0: x -= i * i * step; break;
5137      case 1: y -= i * i * step; break;
5138      case 2: x += i * i * step; break;
5139      case 3: y += i * i * step; break;
5140    }
5141    XMoveWindow (dpy, blanket, x, y);
5142    XFlush (dpy);
5143    waitamoment (0.020);
5144  }
5145}
5146
5147void waitamoment (float timeout)
5148{
5149#ifdef VMS
5150  lib$wait (&timeout);
5151#else
5152  struct timeval timeoutstruct;
5153  int usec = timeout * 1000000;
5154  timeoutstruct.tv_usec = usec % (unsigned long) 1000000;
5155  timeoutstruct.tv_sec  = usec / (unsigned long) 1000000;
5156  select (0, (void *) 0, (void *) 0, (void *) 0, &timeoutstruct);
5157#endif
5158}
5159
5160void packwindow (TwmWindow *tmp_win, char *direction)
5161{
5162    int			cons, newx, newy;
5163    int			x, y, px, py, junkX, junkY;
5164    unsigned int	junkK;
5165    Window		junkW;
5166
5167    if (!strcmp (direction,   "left")) {
5168	cons  = FindConstraint (tmp_win, J_LEFT);
5169	if (cons == -1) return;
5170    	newx  = cons;
5171	newy  = tmp_win->frame_y;
5172    } else
5173    if (!strcmp (direction,  "right")) {
5174	cons  = FindConstraint (tmp_win, J_RIGHT);
5175	if (cons == -1) return;
5176    	newx  = cons;
5177	newx -= tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5178	newy  = tmp_win->frame_y;
5179    } else
5180    if (!strcmp (direction,    "top")) {
5181	cons  = FindConstraint (tmp_win, J_TOP);
5182	if (cons == -1) return;
5183	newx  = tmp_win->frame_x;
5184    	newy  = cons;
5185    } else
5186    if (!strcmp (direction, "bottom")) {
5187	cons  = FindConstraint (tmp_win, J_BOTTOM);
5188	if (cons == -1) return;
5189	newx  = tmp_win->frame_x;
5190    	newy  = cons;
5191	newy -= tmp_win->frame_height  + 2 * tmp_win->frame_bw;
5192    } else return;
5193
5194    XQueryPointer (dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &x, &y, &junkK);
5195    px = x - tmp_win->frame_x + newx;
5196    py = y - tmp_win->frame_y + newy;
5197    XWarpPointer (dpy, Scr->Root, Scr->Root, 0, 0, 0, 0, px, py);
5198    XRaiseWindow(dpy, tmp_win->frame);
5199    XMoveWindow (dpy, tmp_win->frame, newx, newy);
5200    SetupWindow (tmp_win, newx, newy, tmp_win->frame_width, tmp_win->frame_height, -1);
5201}
5202
5203void fillwindow (TwmWindow *tmp_win, char *direction)
5204{
5205    int	cons, newx, newy, save;
5206    unsigned int neww, newh;
5207    int	i;
5208    int	winx = tmp_win->frame_x;
5209    int	winy = tmp_win->frame_y;
5210    int	winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5211    int	winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5212
5213    if (!strcmp (direction, "left")) {
5214	cons = FindConstraint (tmp_win, J_LEFT);
5215	if (cons == -1) return;
5216    	newx = cons;
5217	newy = tmp_win->frame_y;
5218	neww = winw + winx - newx;
5219	newh = winh;
5220	neww -= 2 * tmp_win->frame_bw;
5221	newh -= 2 * tmp_win->frame_bw;
5222	ConstrainSize (tmp_win, &neww, &newh);
5223    } else
5224    if (!strcmp (direction, "right")) {
5225	for (i = 0; i < 2; i++) {
5226	    cons = FindConstraint (tmp_win, J_RIGHT);
5227	    if (cons == -1) return;
5228    	    newx = tmp_win->frame_x;
5229	    newy = tmp_win->frame_y;
5230    	    neww = cons - winx;
5231	    newh = winh;
5232	    save = neww;
5233	    neww -= 2 * tmp_win->frame_bw;
5234	    newh -= 2 * tmp_win->frame_bw;
5235	    ConstrainSize (tmp_win, &neww, &newh);
5236	    if ((neww != winw) || (newh != winh) ||
5237                (cons == Scr->rootw - Scr->BorderRight))
5238                break;
5239	    neww = save;
5240	    SetupWindow (tmp_win, newx, newy, neww, newh, -1);
5241	}
5242    } else
5243    if (!strcmp (direction, "top")) {
5244	cons = FindConstraint (tmp_win, J_TOP);
5245	if (cons == -1) return;
5246    	newx = tmp_win->frame_x;
5247	newy = cons;
5248	neww = winw;
5249	newh = winh + winy - newy;
5250	neww -= 2 * tmp_win->frame_bw;
5251	newh -= 2 * tmp_win->frame_bw;
5252	ConstrainSize (tmp_win, &neww, &newh);
5253    } else
5254    if (!strcmp (direction, "bottom")) {
5255	for (i = 0; i < 2; i++) {
5256	    cons = FindConstraint (tmp_win, J_BOTTOM);
5257	    if (cons == -1) return;
5258    	    newx = tmp_win->frame_x;
5259	    newy = tmp_win->frame_y;
5260    	    neww = winw;
5261	    newh = cons - winy;
5262	    save = newh;
5263	    neww -= 2 * tmp_win->frame_bw;
5264	    newh -= 2 * tmp_win->frame_bw;
5265	    ConstrainSize (tmp_win, &neww, &newh);
5266	    if ((neww != winw) || (newh != winh) ||
5267                (cons == Scr->rooth - Scr->BorderBottom))
5268                break;
5269	    newh = save;
5270	    SetupWindow (tmp_win, newx, newy, neww, newh, -1);
5271	}
5272	}
5273	else if (!strcmp (direction, "vertical"))
5274	{
5275		if (tmp_win->zoomed == ZOOM_NONE)
5276		{
5277			tmp_win->save_frame_height = tmp_win->frame_height;
5278			tmp_win->save_frame_width = tmp_win->frame_width;
5279			tmp_win->save_frame_y = tmp_win->frame_y;
5280			tmp_win->save_frame_x = tmp_win->frame_x;
5281
5282			tmp_win->frame_y++;
5283			newy = FindConstraint (tmp_win, J_TOP);
5284			tmp_win->frame_y--;
5285			newh = FindConstraint (tmp_win, J_BOTTOM) - newy;
5286			newh -= 2 * tmp_win->frame_bw;
5287
5288			newx = tmp_win->frame_x;
5289			neww = tmp_win->frame_width;
5290
5291			ConstrainSize (tmp_win, &neww, &newh);
5292
5293			/* if the bottom of the window has moved up
5294			 * it will be pushed down */
5295			if (newy + newh <
5296			    tmp_win->save_frame_y + tmp_win->save_frame_height)
5297			  newy = tmp_win->save_frame_y +
5298			    tmp_win->save_frame_height - newh;
5299			tmp_win->zoomed = F_ZOOM;
5300			SetupWindow (tmp_win, newx, newy, neww, newh, -1);
5301		}
5302		else
5303		{
5304			fullzoom (tmp_win, tmp_win->zoomed);
5305		}
5306		return;
5307	}
5308    else return;
5309    SetupWindow (tmp_win, newx, newy, neww, newh, -1);
5310}
5311
5312void jump (TwmWindow *tmp_win, int  direction, char *action)
5313{
5314    int			fx, fy, px, py, step, status, cons;
5315    int			fwidth, fheight;
5316    int			junkX, junkY;
5317    unsigned int	junkK;
5318    Window		junkW;
5319
5320    if (! action) return;
5321    status = sscanf (action, "%d", &step);
5322    if (status != 1) return;
5323    if (step < 1) return;
5324
5325    fx = tmp_win->frame_x;
5326    fy = tmp_win->frame_y;
5327    XQueryPointer (dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &px, &py, &junkK);
5328    px -= fx; py -= fy;
5329
5330    fwidth  = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5331    fheight = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5332    switch (direction) {
5333	case J_LEFT   :
5334	    cons  = FindConstraint (tmp_win, J_LEFT);
5335	    if (cons == -1) return;
5336	    fx -= step * Scr->XMoveGrid;
5337	    if (fx < cons) fx = cons;
5338	    break;
5339	case J_RIGHT  :
5340	    cons  = FindConstraint (tmp_win, J_RIGHT);
5341	    if (cons == -1) return;
5342	    fx += step * Scr->XMoveGrid;
5343	    if (fx + fwidth > cons) fx = cons - fwidth;
5344	    break;
5345	case J_TOP    :
5346	    cons  = FindConstraint (tmp_win, J_TOP);
5347	    if (cons == -1) return;
5348	    fy -= step * Scr->YMoveGrid;
5349	    if (fy < cons) fy = cons;
5350	    break;
5351	case J_BOTTOM :
5352	    cons  = FindConstraint (tmp_win, J_BOTTOM);
5353	    if (cons == -1) return;
5354	    fy += step * Scr->YMoveGrid;
5355	    if (fy + fheight > cons) fy = cons - fheight;
5356	    break;
5357    }
5358    /* Pebl Fixme: don't warp if jump happens through iconmgr */
5359    XWarpPointer (dpy, Scr->Root, Scr->Root, 0, 0, 0, 0, fx + px, fy + py);
5360    if (!Scr->NoRaiseMove)
5361        XRaiseWindow (dpy, tmp_win->frame);
5362    SetupWindow (tmp_win, fx, fy, tmp_win->frame_width, tmp_win->frame_height, -1);
5363}
5364
5365int FindConstraint (TwmWindow *tmp_win, int direction)
5366{
5367    TwmWindow	*t;
5368    int		w, h;
5369    int		winx = tmp_win->frame_x;
5370    int		winy = tmp_win->frame_y;
5371    int		winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5372    int		winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5373    int 	ret;
5374
5375    switch (direction) {
5376	case J_LEFT   : if (winx < Scr->BorderLeft) return -1;
5377			ret = Scr->BorderLeft; break;
5378	case J_RIGHT  : if (winx + winw > Scr->rootw - Scr->BorderRight) return -1;
5379			ret = Scr->rootw - Scr->BorderRight; break;
5380	case J_TOP    : if (winy < Scr->BorderTop) return -1;
5381			ret = Scr->BorderTop; break;
5382	case J_BOTTOM : if (winy + winh > Scr->rooth - Scr->BorderBottom) return -1;
5383			ret = Scr->rooth - Scr->BorderBottom; break;
5384	default       : return -1;
5385    }
5386    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
5387	if (t == tmp_win) continue;
5388	if (!visible (t)) continue;
5389	if (!t->mapped) continue;
5390	w = t->frame_width  + 2 * t->frame_bw;
5391	h = t->frame_height + 2 * t->frame_bw;
5392
5393	switch (direction) {
5394	    case J_LEFT :
5395		if (winx        <= t->frame_x + w) continue;
5396		if (winy        >= t->frame_y + h) continue;
5397		if (winy + winh <= t->frame_y    ) continue;
5398		ret = MAX (ret, t->frame_x + w);
5399		break;
5400	    case J_RIGHT :
5401		if (winx + winw >= t->frame_x    ) continue;
5402		if (winy        >= t->frame_y + h) continue;
5403		if (winy + winh <= t->frame_y    ) continue;
5404		ret = MIN (ret, t->frame_x);
5405		break;
5406	    case J_TOP :
5407		if (winy        <= t->frame_y + h) continue;
5408		if (winx        >= t->frame_x + w) continue;
5409		if (winx + winw <= t->frame_x    ) continue;
5410		ret = MAX (ret, t->frame_y + h);
5411		break;
5412	    case J_BOTTOM :
5413		if (winy + winh >= t->frame_y    ) continue;
5414		if (winx        >= t->frame_x + w) continue;
5415		if (winx + winw <= t->frame_x    ) continue;
5416		ret = MIN (ret, t->frame_y);
5417		break;
5418	}
5419    }
5420    return ret;
5421}
5422
5423void TryToPack (TwmWindow *tmp_win, int *x, int *y)
5424{
5425    TwmWindow	*t;
5426    int		newx, newy;
5427    int		w, h;
5428    int		winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5429    int		winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5430
5431    newx = *x;
5432    newy = *y;
5433    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
5434	if (t == tmp_win) continue;
5435	if (t->winbox != tmp_win->winbox) continue;
5436	if (t->vs != tmp_win->vs) continue;
5437	if (!t->mapped) continue;
5438
5439	w = t->frame_width  + 2 * t->frame_bw;
5440	h = t->frame_height + 2 * t->frame_bw;
5441	if (newx >= t->frame_x + w) continue;
5442	if (newy >= t->frame_y + h) continue;
5443	if (newx + winw <= t->frame_x) continue;
5444	if (newy + winh <= t->frame_y) continue;
5445
5446	if (newx + Scr->MovePackResistance > t->frame_x + w) { /* left */
5447	    newx = MAX (newx, t->frame_x + w);
5448	    continue;
5449	}
5450	if (newx + winw < t->frame_x + Scr->MovePackResistance) { /* right */
5451	    newx = MIN (newx, t->frame_x - winw);
5452	    continue;
5453	}
5454	if (newy + Scr->MovePackResistance > t->frame_y + h) { /* top */
5455	    newy = MAX (newy, t->frame_y + h);
5456	    continue;
5457	}
5458	if (newy + winh < t->frame_y + Scr->MovePackResistance) { /* bottom */
5459	    newy = MIN (newy, t->frame_y - winh);
5460	    continue;
5461	}
5462    }
5463    *x = newx;
5464    *y = newy;
5465}
5466
5467void TryToPush (TwmWindow *tmp_win, int x, int y, int dir)
5468{
5469    TwmWindow	*t;
5470    int		newx, newy, ndir;
5471    Boolean	move;
5472    int		w, h;
5473    int		winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5474    int		winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5475
5476    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
5477	if (t == tmp_win) continue;
5478	if (t->winbox != tmp_win->winbox) continue;
5479	if (t->vs != tmp_win->vs) continue;
5480	if (!t->mapped) continue;
5481
5482	w = t->frame_width  + 2 * t->frame_bw;
5483	h = t->frame_height + 2 * t->frame_bw;
5484	if (x >= t->frame_x + w) continue;
5485	if (y >= t->frame_y + h) continue;
5486	if (x + winw <= t->frame_x) continue;
5487	if (y + winh <= t->frame_y) continue;
5488
5489	move = False;
5490	if ((dir == 0 || dir == J_LEFT) &&
5491	    (x + Scr->MovePackResistance > t->frame_x + w)) {
5492	    newx = x - w;
5493	    newy = t->frame_y;
5494	    ndir = J_LEFT;
5495	    move = True;
5496	}
5497	else
5498	if ((dir == 0 || dir == J_RIGHT) &&
5499	   (x + winw < t->frame_x + Scr->MovePackResistance)) {
5500	    newx = x + winw;
5501	    newy = t->frame_y;
5502	    ndir = J_RIGHT;
5503	    move = True;
5504	}
5505	else
5506	if ((dir == 0 || dir == J_TOP) &&
5507	    (y + Scr->MovePackResistance > t->frame_y + h)) {
5508	    newx = t->frame_x;
5509	    newy = y - h;
5510	    ndir = J_TOP;
5511	    move = True;
5512	}
5513	else
5514	if ((dir == 0 || dir == J_BOTTOM) &&
5515	    (y + winh < t->frame_y + Scr->MovePackResistance)) {
5516	    newx = t->frame_x;
5517	    newy = y + winh;
5518	    ndir = J_BOTTOM;
5519	    move = True;
5520	}
5521	if (move) {
5522	    TryToPush (t, newx, newy, ndir);
5523	    TryToPack (t, &newx, &newy);
5524            ConstrainByBorders (tmp_win,
5525				&newx, t->frame_width  + 2 * t->frame_bw,
5526                                &newy, t->frame_height + 2 * t->frame_bw);
5527	    SetupWindow (t, newx, newy, t->frame_width, t->frame_height, -1);
5528	}
5529    }
5530}
5531
5532void TryToGrid (TwmWindow *tmp_win, int *x, int *y)
5533{
5534    int	w    = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5535    int	h    = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5536    int	grav = ((tmp_win->hints.flags & PWinGravity)
5537		      ? tmp_win->hints.win_gravity : NorthWestGravity);
5538
5539    switch (grav) {
5540	case ForgetGravity :
5541	case StaticGravity :
5542	case NorthWestGravity :
5543	case NorthGravity :
5544	case WestGravity :
5545	case CenterGravity :
5546	    *x = ((*x - Scr->BorderLeft) / Scr->XMoveGrid) * Scr->XMoveGrid
5547                + Scr->BorderLeft;
5548	    *y = ((*y - Scr->BorderTop) / Scr->YMoveGrid) * Scr->YMoveGrid
5549                + Scr->BorderTop;
5550	    break;
5551	case NorthEastGravity :
5552	case EastGravity :
5553	    *x = (((*x + w - Scr->BorderLeft) / Scr->XMoveGrid) *
5554                  Scr->XMoveGrid) - w + Scr->BorderLeft;
5555	    *y = ((*y - Scr->BorderTop) / Scr->YMoveGrid) *
5556                Scr->YMoveGrid + Scr->BorderTop;
5557	    break;
5558	case SouthWestGravity :
5559	case SouthGravity :
5560	    *x = ((*x - Scr->BorderLeft) / Scr->XMoveGrid) * Scr->XMoveGrid
5561                + Scr->BorderLeft;
5562	    *y = (((*y + h - Scr->BorderTop) / Scr->YMoveGrid) * Scr->YMoveGrid)
5563                - h + Scr->BorderTop;
5564	    break;
5565	case SouthEastGravity :
5566	    *x = (((*x + w - Scr->BorderLeft) / Scr->XMoveGrid) *
5567                  Scr->XMoveGrid) - w + Scr->BorderLeft;
5568	    *y = (((*y + h - Scr->BorderTop) / Scr->YMoveGrid) *
5569                  Scr->YMoveGrid) - h + Scr->BorderTop;
5570	    break;
5571    }
5572}
5573
5574int WarpCursorToDefaultEntry (MenuRoot *menu)
5575{
5576    MenuItem	*item;
5577    Window	 root;
5578    int		 i, x, y, xl, yt;
5579    unsigned int w, h, bw, d;
5580
5581    for (i = 0, item = menu->first; item != menu->last; item = item->next) {
5582	if (item == menu->defaultitem) break;
5583	i++;
5584     }
5585     if (!XGetGeometry (dpy, menu->w, &root, &x, &y, &w, &h, &bw, &d)) return 0;
5586     xl = x + (menu->width / 2);
5587     yt = y + (i + 0.5) * Scr->EntryHeight;
5588
5589     XWarpPointer (dpy, Scr->Root, Scr->Root,
5590		   Event.xbutton.x_root, Event.xbutton.y_root,
5591		   menu->width, menu->height, xl, yt);
5592     return 1;
5593}
5594
5595