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