functions_win_moveresize.c revision 0bbfda8a
1/*
2 * Functions related to moving/resizing windows.
3 */
4
5#include "ctwm.h"
6
7#include <stdio.h>
8#include <stdlib.h>
9
10#include "colormaps.h"
11#include "events.h"
12#include "event_handlers.h"
13#include "functions.h"
14#include "functions_defs.h"
15#include "functions_internal.h"
16#include "icons.h"
17#include "otp.h"
18#include "parse.h"
19#include "screen.h"
20#include "util.h"
21#include "vscreen.h"
22#include "win_decorations.h"
23#include "win_ops.h"
24#include "win_resize.h"
25#include "win_utils.h"
26#include "workspace_manager.h"
27
28
29/*
30 * MoveFillDir-ectional specifiers, used in jump/pack/fill
31 */
32typedef enum {
33	MFD_BOTTOM,
34	MFD_LEFT,
35	MFD_RIGHT,
36	MFD_TOP,
37} MoveFillDir;
38static int FindConstraint(TwmWindow *tmp_win, MoveFillDir direction);
39
40/* Internal util */
41static bool belongs_to_twm_window(TwmWindow *t, Window w);
42
43
44/*
45 * Constrained move variables
46 *
47 * Used in the resize handling, but needed over in event code for
48 * ButtonRelease as well.
49 */
50bool ConstMove = false;
51CMoveDir ConstMoveDir;
52int ConstMoveX;
53int ConstMoveY;
54int ConstMoveXL;
55int ConstMoveXR;
56int ConstMoveYT;
57int ConstMoveYB;
58
59/*
60 * Which move-ish function is in progress.  This is _almost_ really a
61 * local var in the movewindow() function, but we also reference it in
62 * the HandleButtonRelease() event handler because that has to know
63 * which move variant we're doing to figure out whether it has to
64 * constrain the final coordinates in various ways.
65 */
66int MoveFunction;
67
68/*
69 * Globals used to keep track of whether the mouse has moved during a
70 * resize function.
71 */
72int ResizeOrigX;
73int ResizeOrigY;
74
75
76
77/*
78 * Now, on to the actual handlers.
79 */
80
81
82/*
83 *********************************************************
84 *
85 * First, the various methods of moving windows around.
86 *
87 *********************************************************
88 */
89
90/*
91 * Simple f.move and related
92 */
93static void movewindow(EF_FULLPROTO);
94DFHANDLER(move)
95{
96	movewindow(EF_ARGS);
97}
98DFHANDLER(forcemove)
99{
100	movewindow(EF_ARGS);
101}
102DFHANDLER(movepack)
103{
104	movewindow(EF_ARGS);
105}
106DFHANDLER(movepush)
107{
108	movewindow(EF_ARGS);
109}
110
111/* f.move and friends backend */
112static void
113movewindow(EF_FULLPROTO)
114{
115	int origX, origY;
116	bool moving_icon;
117	bool fromtitlebar;
118	const Window dragroot = Scr->XineramaRoot;
119	const Window rootw = eventp->xbutton.root;
120
121	/* Better not be a menu open */
122	PopDownMenu();
123
124	/* Stash up just which f.move* variant we are */
125	MoveFunction = func;
126
127	/*
128	 * Figure whether we're moving opaquely.
129	 */
130	if(tmp_win->OpaqueMove) {
131		if(Scr->OpaqueMoveThreshold >= 200) {
132			Scr->OpaqueMove = true;
133		}
134		else {
135			const unsigned long sw = tmp_win->frame_width
136			                         * tmp_win->frame_height;
137			const unsigned long ss = Scr->rootw  * Scr->rooth;
138			const float sf = Scr->OpaqueMoveThreshold / 100.0;
139
140			if(sw > (ss * sf)) {
141				Scr->OpaqueMove = false;
142			}
143			else {
144				Scr->OpaqueMove = true;
145			}
146		}
147	}
148	else {
149		Scr->OpaqueMove = false;
150	}
151
152	/* If it's in a WindowBox, adjust coordinates as necessary */
153	if(tmp_win->winbox) {
154		XTranslateCoordinates(dpy, dragroot, tmp_win->winbox->window,
155		                      eventp->xbutton.x_root, eventp->xbutton.y_root,
156		                      &(eventp->xbutton.x_root), &(eventp->xbutton.y_root), &JunkChild);
157	}
158
159	/*
160	 * XXX pulldown=true only when we're triggering from a ButtonRelease
161	 * in a menu, and this warp should only be going somewhere if we hit
162	 * the winbox case above and had to translate the coordinates?  But,
163	 * in that case, the coordinates would be changed to be relative to
164	 * the winbox window, and here we're positioning relative to Root?
165	 */
166	if(pulldown)
167		XWarpPointer(dpy, None, Scr->Root,
168		             0, 0, 0, 0, eventp->xbutton.x_root, eventp->xbutton.y_root);
169
170	/*
171	 * Stub out handlers for enter/leave notifications while we do stuff.
172	 * They get reset toward the end of the ButtonRelease handler.
173	 */
174	EventHandler[EnterNotify] = HandleUnknown;
175	EventHandler[LeaveNotify] = HandleUnknown;
176
177	if(!Scr->NoGrabServer || !Scr->OpaqueMove) {
178		XGrabServer(dpy);
179	}
180
181	/*
182	 * Setup size for the window showing current location as we move it.
183	 * The same window is used for resize ops too, where it might be a
184	 * different size.
185	 */
186	Scr->SizeStringOffset = SIZE_HINDENT;
187	XResizeWindow(dpy, Scr->SizeWindow,
188	              Scr->SizeStringWidth + SIZE_HINDENT * 2,
189	              Scr->SizeFont.height + SIZE_VINDENT * 2);
190	XMapRaised(dpy, Scr->SizeWindow);
191
192	/*
193	 * Use XGrabPointer() to configure how we get events locations
194	 * reported relative to what root.
195	 */
196	{
197		const Window grabwin = (tmp_win->winbox ? tmp_win->winbox->window
198		                        : Scr->XineramaRoot);
199
200		XGrabPointer(dpy, grabwin, True,
201		             ButtonPressMask | ButtonReleaseMask |
202		             ButtonMotionMask | PointerMotionMask,
203		             GrabModeAsync, GrabModeAsync, grabwin, Scr->MoveCursor,
204		             CurrentTime);
205	}
206
207	/*
208	 * Set w to what we're actually moving.  If it's an icon, we always
209	 * move it opaquely anyway.  If it's a window (that's not iconofied),
210	 * we move the frame.
211	 */
212	moving_icon = false;
213	if(context == C_ICON && tmp_win->icon && tmp_win->icon->w) {
214		w = tmp_win->icon->w;
215		DragX = eventp->xbutton.x;
216		DragY = eventp->xbutton.y;
217		moving_icon = true;
218		if(tmp_win->OpaqueMove) {
219			Scr->OpaqueMove = true;
220		}
221	}
222	else if(! tmp_win->icon || w != tmp_win->icon->w) {
223		XTranslateCoordinates(dpy, w, tmp_win->frame,
224		                      eventp->xbutton.x,
225		                      eventp->xbutton.y,
226		                      &DragX, &DragY, &JunkChild);
227
228		w = tmp_win->frame;
229	}
230
231	DragWindow = None;
232
233	/* Get x/y relative to parent window, i.e. the virtual screen, Root.
234	 * XMoveWindow() moves are relative to this.
235	 * MoveOutline()s however are drawn from the XineramaRoot since they
236	 * may cross virtual screens.
237	 */
238	XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY,
239	             &DragWidth, &DragHeight, &DragBW,
240	             &JunkDepth);
241
242	origX = eventp->xbutton.x_root;
243	origY = eventp->xbutton.y_root;
244	CurrentDragX = origDragX;
245	CurrentDragY = origDragY;
246
247	/*
248	 * Setup ConstrainedMove if this is a double-click.  That means
249	 * setting the flags, and moving the pointer off to the middle of the
250	 * window.
251	 *
252	 * Only do the constrained move if timer is set; need to check it
253	 * in case of stupid or wicked fast servers
254	 */
255	if(ConstrainedMoveTime &&
256	                (eventp->xbutton.time - last_time) < ConstrainedMoveTime) {
257		int width, height;
258
259		ConstMove = true;
260		ConstMoveDir = MOVE_NONE;
261		ConstMoveX = eventp->xbutton.x_root - DragX - DragBW;
262		ConstMoveY = eventp->xbutton.y_root - DragY - DragBW;
263		width = DragWidth + 2 * DragBW;
264		height = DragHeight + 2 * DragBW;
265		ConstMoveXL = ConstMoveX + width / 3;
266		ConstMoveXR = ConstMoveX + 2 * (width / 3);
267		ConstMoveYT = ConstMoveY + height / 3;
268		ConstMoveYB = ConstMoveY + 2 * (height / 3);
269
270		XWarpPointer(dpy, None, w,
271		             0, 0, 0, 0, DragWidth / 2, DragHeight / 2);
272
273		XQueryPointer(dpy, w, &JunkRoot, &JunkChild,
274		              &JunkX, &JunkY, &DragX, &DragY, &JunkMask);
275	}
276	last_time = eventp->xbutton.time;
277
278	/* If not moving opaquely, setup the outline bits */
279	if(!Scr->OpaqueMove) {
280		InstallRootColormap();
281		if(!Scr->MoveDelta) {
282			/*
283			 * Draw initial outline.  This was previously done the
284			 * first time though the outer loop by dropping out of
285			 * the XCheckMaskEvent inner loop down to one of the
286			 * MoveOutline's below.
287			 */
288			MoveOutline(dragroot,
289			            origDragX - DragBW + Scr->currentvs->x,
290			            origDragY - DragBW + Scr->currentvs->y,
291			            DragWidth + 2 * DragBW, DragHeight + 2 * DragBW,
292			            tmp_win->frame_bw,
293			            moving_icon ? 0 : tmp_win->title_height + tmp_win->frame_bw3D);
294			/*
295			 * This next line causes HandleReleaseNotify to call
296			 * XRaiseWindow().  This is solely to preserve the
297			 * previous behaviour that raises a window being moved
298			 * on button release even if you never actually moved
299			 * any distance (unless you move less than MoveDelta or
300			 * NoRaiseMove is set or OpaqueMove is set).
301			 */
302			DragWindow = w;
303		}
304	}
305
306	/*
307	 * Init whether triggered from something on the titlebar (e.g., a
308	 * TitleButton bound to f.move).  We need to keep this var in a scope
309	 * outside the event loop below because the resetting of it in there
310	 * is supposed to have effect on future loops.
311	 */
312	fromtitlebar = belongs_to_twm_window(tmp_win, eventp->xbutton.window);
313
314	if(menuFromFrameOrWindowOrTitlebar) {
315		/* warp the pointer to the middle of the window */
316		XWarpPointer(dpy, None, Scr->Root, 0, 0, 0, 0,
317		             origDragX + DragWidth / 2,
318		             origDragY + DragHeight / 2);
319		XFlush(dpy);
320	}
321
322	/* Fill in the position window with where we're starting */
323	DisplayPosition(tmp_win, CurrentDragX, CurrentDragY);
324
325	/*
326	 * Internal event loop for doing the moving.
327	 */
328	while(1) {
329		const long releaseEvent = menuFromFrameOrWindowOrTitlebar ?
330		                          ButtonPress : ButtonRelease;
331		const long movementMask = menuFromFrameOrWindowOrTitlebar ?
332		                          PointerMotionMask : ButtonMotionMask;
333
334		/* block until there is an interesting event */
335		XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask |
336		           EnterWindowMask | LeaveWindowMask |
337		           ExposureMask | movementMask |
338		           VisibilityChangeMask, &Event);
339
340		/* throw away enter and leave events until release */
341		if(Event.xany.type == EnterNotify ||
342		                Event.xany.type == LeaveNotify) {
343			continue;
344		}
345
346		/* discard any extra motion events before a logical release */
347		if(Event.type == MotionNotify) {
348			while(XCheckMaskEvent(dpy, movementMask | releaseEvent, &Event))
349				if(Event.type == releaseEvent) {
350					break;
351				}
352		}
353
354		/* test to see if we have a second button press to abort move */
355		if(!menuFromFrameOrWindowOrTitlebar) {
356			if(Event.type == ButtonPress && DragWindow != None) {
357				Cursor cur;
358				if(Scr->OpaqueMove) {
359					XMoveWindow(dpy, DragWindow, origDragX, origDragY);
360					if(moving_icon) {
361						tmp_win->icon->w_x = origDragX;
362						tmp_win->icon->w_y = origDragY;
363					}
364				}
365				else {
366					MoveOutline(dragroot, 0, 0, 0, 0, 0, 0);
367				}
368				DragWindow = None;
369
370				XUnmapWindow(dpy, Scr->SizeWindow);
371				cur = LeftButt;
372				if(Event.xbutton.button == Button2) {
373					cur = MiddleButt;
374				}
375				else if(Event.xbutton.button >= Button3) {
376					cur = RightButt;
377				}
378
379				XGrabPointer(dpy, Scr->Root, True,
380				             ButtonReleaseMask | ButtonPressMask,
381				             GrabModeAsync, GrabModeAsync,
382				             Scr->Root, cur, CurrentTime);
383				func_reset_cursor = false;  // Leave cursor alone
384				return;
385			}
386		}
387
388		if(fromtitlebar && Event.type == ButtonPress) {
389			fromtitlebar = false;
390			CurrentDragX = origX = Event.xbutton.x_root;
391			CurrentDragY = origY = Event.xbutton.y_root;
392			XTranslateCoordinates(dpy, rootw, tmp_win->frame,
393			                      origX, origY,
394			                      &DragX, &DragY, &JunkChild);
395			continue;
396		}
397
398		if(!DispatchEvent2()) {
399			continue;
400		}
401
402		if(Cancel) {
403			WindowMoved = false;
404			if(!Scr->OpaqueMove) {
405				UninstallRootColormap();
406			}
407			func_reset_cursor = false;  // Leave cursor alone
408			return;
409		}
410		if(Event.type == releaseEvent) {
411			MoveOutline(dragroot, 0, 0, 0, 0, 0, 0);
412			if(moving_icon &&
413			                ((CurrentDragX != origDragX ||
414			                  CurrentDragY != origDragY))) {
415				tmp_win->icon_moved = true;
416			}
417			if(!Scr->OpaqueMove && menuFromFrameOrWindowOrTitlebar) {
418				int xl = Event.xbutton.x_root - (DragWidth  / 2),
419				    yt = Event.xbutton.y_root - (DragHeight / 2);
420				if(!moving_icon &&
421				                (MoveFunction == F_MOVEPACK || MoveFunction == F_MOVEPUSH)) {
422					TryToPack(tmp_win, &xl, &yt);
423				}
424				XMoveWindow(dpy, DragWindow, xl, yt);
425			}
426			if(menuFromFrameOrWindowOrTitlebar) {
427				DragWindow = None;
428			}
429			break;
430		}
431
432		/* something left to do only if the pointer moved */
433		if(Event.type != MotionNotify) {
434			continue;
435		}
436
437		/* Get info about where the pointer is */
438		XQueryPointer(dpy, rootw, &(eventp->xmotion.root), &JunkChild,
439		              &(eventp->xmotion.x_root), &(eventp->xmotion.y_root),
440		              &JunkX, &JunkY, &JunkMask);
441
442		/*
443		 * Tweak up for root.  XXX Is this even right?  There are too
444		 * many Root's, and this corrects for a specific one, but I'm not
445		 * sure it's the right one...
446		 */
447		FixRootEvent(eventp);
448
449		/* Tweak for window box, if this is in one */
450		if(tmp_win->winbox) {
451			XTranslateCoordinates(dpy, dragroot, tmp_win->winbox->window,
452			                      eventp->xmotion.x_root, eventp->xmotion.y_root,
453			                      &(eventp->xmotion.x_root), &(eventp->xmotion.y_root), &JunkChild);
454		}
455
456		/*
457		 * If we haven't moved MoveDelta yet, we're not yet sure we're
458		 * doing anything, so just loop back around.
459		 */
460		if(DragWindow == None &&
461		                abs(eventp->xmotion.x_root - origX) < Scr->MoveDelta &&
462		                abs(eventp->xmotion.y_root - origY) < Scr->MoveDelta) {
463			continue;
464		}
465
466		/*
467		 * Now we know we're moving whatever the window is.
468		 */
469		DragWindow = w;
470
471		/* Raise when the move starts if we should */
472		if(!Scr->NoRaiseMove && Scr->OpaqueMove && !WindowMoved) {
473			TwmWindow *t;
474
475			/*
476			 * XXX In several of the error cases listed in here, it's
477			 * seems almost that we should just abort the whole move
478			 * process immediately if any of them are hit, because things
479			 * get nonsensical.
480			 */
481
482			/* Find TwmWindow bits related to what we're dragging */
483			if(XFindContext(dpy, DragWindow, TwmContext, (XPointer *) &t) == XCNOENT) {
484				fprintf(stderr, "%s(): Can't find TwmWindow.\n", __func__);
485				/* XXX abort? */
486				t = NULL;
487			}
488
489			if(t != tmp_win) {
490				fprintf(stderr, "%s(): DragWindow isn't tmp_win!\n", __func__);
491				/* XXX abort? */
492			}
493
494			if(t == NULL) {
495				/* Don't try doing this stuff... */
496			}
497			else if(DragWindow == t->frame) {
498				if(moving_icon) {
499					fprintf(stderr, "%s(): moving_icon is true incorrectly!\n",
500					        __func__);
501				}
502				OtpRaise(t, WinWin);
503			}
504			else if(t->icon && DragWindow == t->icon->w) {
505				if(!moving_icon) {
506					fprintf(stderr, "%s(): moving_icon is false incorrectly!\n",
507					        __func__);
508				}
509				OtpRaise(t, IconWin);
510			}
511			else {
512				fprintf(stderr, "%s(): Couldn't figure what to raise.\n",
513				        __func__);
514			}
515		}
516
517		WindowMoved = true;
518
519		/*
520		 * Handle moving the step
521		 */
522		if(ConstMove) {
523			/* Did we already decide it's constrained?  Do that. */
524			switch(ConstMoveDir) {
525				case MOVE_NONE: {
526					/* Haven't figured direction yet, so do so */
527					if(eventp->xmotion.x_root < ConstMoveXL ||
528					                eventp->xmotion.x_root > ConstMoveXR) {
529						ConstMoveDir = MOVE_HORIZ;
530					}
531
532					if(eventp->xmotion.y_root < ConstMoveYT ||
533					                eventp->xmotion.y_root > ConstMoveYB) {
534						ConstMoveDir = MOVE_VERT;
535					}
536
537					XQueryPointer(dpy, DragWindow, &JunkRoot, &JunkChild,
538					              &JunkX, &JunkY, &DragX, &DragY, &JunkMask);
539					break;
540				}
541
542				/* We know which dir it's contrained to, so figure amount */
543				case MOVE_VERT:
544					ConstMoveY = eventp->xmotion.y_root - DragY - DragBW;
545					break;
546
547				case MOVE_HORIZ:
548					ConstMoveX = eventp->xmotion.x_root - DragX - DragBW;
549					break;
550			}
551
552			/* We've got a move to do, so do it */
553			if(ConstMoveDir != MOVE_NONE) {
554				int xl, yt, width, height;
555
556				xl = ConstMoveX;
557				yt = ConstMoveY;
558				width = DragWidth + 2 * DragBW;
559				height = DragHeight + 2 * DragBW;
560
561				if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) {
562					TryToGrid(tmp_win, &xl, &yt);
563				}
564				if(!moving_icon && MoveFunction == F_MOVEPUSH && Scr->OpaqueMove) {
565					TryToPush(tmp_win, xl, yt);
566				}
567
568				if(!moving_icon &&
569				                (MoveFunction == F_MOVEPACK || MoveFunction == F_MOVEPUSH)) {
570					TryToPack(tmp_win, &xl, &yt);
571				}
572
573				if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) {
574					ConstrainByBorders(tmp_win, &xl, width, &yt, height);
575				}
576				CurrentDragX = xl;
577				CurrentDragY = yt;
578				if(Scr->OpaqueMove) {
579					if(MoveFunction == F_MOVEPUSH && !moving_icon) {
580						SetupWindow(tmp_win, xl, yt,
581						            tmp_win->frame_width, tmp_win->frame_height, -1);
582					}
583					else {
584						XMoveWindow(dpy, DragWindow, xl, yt);
585						if(moving_icon) {
586							tmp_win->icon->w_x = xl;
587							tmp_win->icon->w_y = yt;
588						}
589					}
590					WMapSetupWindow(tmp_win, xl, yt, -1, -1);
591				}
592				else {
593					MoveOutline(dragroot, xl + Scr->currentvs->x,
594					            yt + Scr->currentvs->y, width, height,
595					            tmp_win->frame_bw,
596					            moving_icon ? 0 : tmp_win->title_height + tmp_win->frame_bw3D);
597				}
598			}
599		}
600		else if(DragWindow != None) {
601			/*
602			 * There's a non-constrained move to process
603			 *
604			 * This is split out for virtual screens.  In that case, it's
605			 * possible to drag windows from one workspace to another, and
606			 * as such, these need to be adjusted to the root, rather
607			 * than this virtual screen...
608			 */
609			const int xroot = eventp->xmotion.x_root;
610			const int yroot = eventp->xmotion.y_root;
611			const int width  = DragWidth + 2 * DragBW;
612			const int height = DragHeight + 2 * DragBW;
613			int xl, yt;
614
615			if(!menuFromFrameOrWindowOrTitlebar) {
616				xl = xroot - DragX - DragBW;
617				yt = yroot - DragY - DragBW;
618			}
619			else {
620				xl = xroot - (DragWidth / 2);
621				yt = yroot - (DragHeight / 2);
622			}
623
624			if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) {
625				TryToGrid(tmp_win, &xl, &yt);
626			}
627			if(!moving_icon && MoveFunction == F_MOVEPUSH && Scr->OpaqueMove) {
628				TryToPush(tmp_win, xl, yt);
629			}
630
631			if(!moving_icon &&
632			                (MoveFunction == F_MOVEPACK || MoveFunction == F_MOVEPUSH)) {
633				TryToPack(tmp_win, &xl, &yt);
634			}
635
636			if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) {
637				ConstrainByBorders(tmp_win, &xl, width, &yt, height);
638			}
639
640			CurrentDragX = xl;
641			CurrentDragY = yt;
642			if(Scr->OpaqueMove) {
643				if(MoveFunction == F_MOVEPUSH && !moving_icon) {
644					SetupWindow(tmp_win, xl, yt,
645					            tmp_win->frame_width, tmp_win->frame_height, -1);
646				}
647				else {
648					XMoveWindow(dpy, DragWindow, xl, yt);
649					if(moving_icon) {
650						tmp_win->icon->w_x = xl;
651						tmp_win->icon->w_y = yt;
652					}
653				}
654				if(! moving_icon) {
655					WMapSetupWindow(tmp_win, xl, yt, -1, -1);
656				}
657			}
658			else {
659				MoveOutline(dragroot, xl + Scr->currentvs->x,
660				            yt + Scr->currentvs->y, width, height,
661				            tmp_win->frame_bw,
662				            moving_icon ? 0 : tmp_win->title_height + tmp_win->frame_bw3D);
663			}
664		}
665
666		/* We've moved a step, so update the displayed position */
667		DisplayPosition(tmp_win, CurrentDragX, CurrentDragY);
668	}
669
670	/* Done, so hide away the position display window */
671	XUnmapWindow(dpy, Scr->SizeWindow);
672
673	/* Restore colormap if we replaced it */
674	if(!Scr->OpaqueMove && DragWindow == None) {
675		UninstallRootColormap();
676	}
677
678	return;
679}
680
681
682/*
683 * f.pack -- moving until collision
684 *
685 * XXX Collapse this down; no need for an extra level of indirection on
686 * the function calling.
687 */
688static void packwindow(TwmWindow *tmp_win, const char *direction);
689DFHANDLER(pack)
690{
691	if(tmp_win->squeezed) {
692		XBell(dpy, 0);
693		return;
694	}
695	packwindow(tmp_win, action);
696}
697
698static void
699packwindow(TwmWindow *tmp_win, const char *direction)
700{
701	int          cons, newx, newy;
702	int          x, y, px, py, junkX, junkY;
703	unsigned int junkK;
704	Window       junkW;
705
706	if(!strcmp(direction,   "left")) {
707		cons  = FindConstraint(tmp_win, MFD_LEFT);
708		if(cons == -1) {
709			return;
710		}
711		newx  = cons;
712		newy  = tmp_win->frame_y;
713	}
714	else if(!strcmp(direction,  "right")) {
715		cons  = FindConstraint(tmp_win, MFD_RIGHT);
716		if(cons == -1) {
717			return;
718		}
719		newx  = cons;
720		newx -= tmp_win->frame_width + 2 * tmp_win->frame_bw;
721		newy  = tmp_win->frame_y;
722	}
723	else if(!strcmp(direction,    "top")) {
724		cons  = FindConstraint(tmp_win, MFD_TOP);
725		if(cons == -1) {
726			return;
727		}
728		newx  = tmp_win->frame_x;
729		newy  = cons;
730	}
731	else if(!strcmp(direction, "bottom")) {
732		cons  = FindConstraint(tmp_win, MFD_BOTTOM);
733		if(cons == -1) {
734			return;
735		}
736		newx  = tmp_win->frame_x;
737		newy  = cons;
738		newy -= tmp_win->frame_height + 2 * tmp_win->frame_bw;
739	}
740	else {
741		return;
742	}
743
744	XQueryPointer(dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &x, &y, &junkK);
745	px = x - tmp_win->frame_x + newx;
746	py = y - tmp_win->frame_y + newy;
747	XWarpPointer(dpy, Scr->Root, Scr->Root, 0, 0, 0, 0, px, py);
748	OtpRaise(tmp_win, WinWin);
749	XMoveWindow(dpy, tmp_win->frame, newx, newy);
750	SetupWindow(tmp_win, newx, newy, tmp_win->frame_width,
751	            tmp_win->frame_height, -1);
752}
753
754
755/*
756 * f.jump* -- moving incrementally in various directions
757 */
758static void jump(TwmWindow *tmp_win, MoveFillDir direction, const char *action);
759DFHANDLER(jumpleft)
760{
761	jump(tmp_win, MFD_LEFT, action);
762}
763DFHANDLER(jumpright)
764{
765	jump(tmp_win, MFD_RIGHT, action);
766}
767DFHANDLER(jumpdown)
768{
769	jump(tmp_win, MFD_BOTTOM, action);
770}
771DFHANDLER(jumpup)
772{
773	jump(tmp_win, MFD_TOP, action);
774}
775
776static void
777jump(TwmWindow *tmp_win, MoveFillDir direction, const char *action)
778{
779	int          fx, fy, px, py, step, status, cons;
780	int          fwidth, fheight;
781	int          junkX, junkY;
782	unsigned int junkK;
783	Window       junkW;
784
785	if(tmp_win->squeezed) {
786		XBell(dpy, 0);
787		return;
788	}
789
790	if(! action) {
791		return;
792	}
793	status = sscanf(action, "%d", &step);
794	if(status != 1) {
795		return;
796	}
797	if(step < 1) {
798		return;
799	}
800
801	fx = tmp_win->frame_x;
802	fy = tmp_win->frame_y;
803	XQueryPointer(dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &px, &py, &junkK);
804	px -= fx;
805	py -= fy;
806
807	fwidth  = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
808	fheight = tmp_win->frame_height + 2 * tmp_win->frame_bw;
809	switch(direction) {
810		case MFD_LEFT:
811			cons  = FindConstraint(tmp_win, MFD_LEFT);
812			if(cons == -1) {
813				return;
814			}
815			fx -= step * Scr->XMoveGrid;
816			if(fx < cons) {
817				fx = cons;
818			}
819			break;
820		case MFD_RIGHT:
821			cons  = FindConstraint(tmp_win, MFD_RIGHT);
822			if(cons == -1) {
823				return;
824			}
825			fx += step * Scr->XMoveGrid;
826			if(fx + fwidth > cons) {
827				fx = cons - fwidth;
828			}
829			break;
830		case MFD_TOP:
831			cons  = FindConstraint(tmp_win, MFD_TOP);
832			if(cons == -1) {
833				return;
834			}
835			fy -= step * Scr->YMoveGrid;
836			if(fy < cons) {
837				fy = cons;
838			}
839			break;
840		case MFD_BOTTOM:
841			cons  = FindConstraint(tmp_win, MFD_BOTTOM);
842			if(cons == -1) {
843				return;
844			}
845			fy += step * Scr->YMoveGrid;
846			if(fy + fheight > cons) {
847				fy = cons - fheight;
848			}
849			break;
850	}
851	/* Pebl Fixme: don't warp if jump happens through iconmgr */
852	XWarpPointer(dpy, Scr->Root, Scr->Root, 0, 0, 0, 0, fx + px, fy + py);
853	if(!Scr->NoRaiseMove) {
854		OtpRaise(tmp_win, WinWin);
855	}
856	SetupWindow(tmp_win, fx, fy, tmp_win->frame_width, tmp_win->frame_height, -1);
857}
858
859
860
861/*
862 *********************************************************
863 *
864 * Next up, straightforward resizing operations
865 *
866 *********************************************************
867 */
868
869/*
870 * Standard f.resize
871 */
872DFHANDLER(resize)
873{
874	PopDownMenu();
875	if(tmp_win->squeezed) {
876		XBell(dpy, 0);
877		return;
878	}
879	EventHandler[EnterNotify] = HandleUnknown;
880	EventHandler[LeaveNotify] = HandleUnknown;
881
882	OpaqueResizeSize(tmp_win);
883
884	if(pulldown)
885		XWarpPointer(dpy, None, Scr->Root,
886		             0, 0, 0, 0, eventp->xbutton.x_root, eventp->xbutton.y_root);
887
888	if(!tmp_win->icon || (w != tmp_win->icon->w)) {         /* can't resize icons */
889
890		/*        fromMenu = False;  ????? */
891		if((Context == C_FRAME || Context == C_WINDOW || Context == C_TITLE
892		                || Context == C_ROOT)
893		                && cur_fromMenu()) {
894			resizeFromCenter(w, tmp_win);
895		}
896		else {
897			/*
898			 * see if this is being done from the titlebar
899			 */
900			bool from3dborder = (eventp->xbutton.window == tmp_win->frame);
901			bool fromtitlebar = !from3dborder &&
902			                    belongs_to_twm_window(tmp_win, eventp->xbutton.window);
903
904			/* Save pointer position so we can tell if it was moved or
905			   not during the resize. */
906			ResizeOrigX = eventp->xbutton.x_root;
907			ResizeOrigY = eventp->xbutton.y_root;
908
909			StartResize(eventp, tmp_win, fromtitlebar, from3dborder);
910			func_reset_cursor = false;  // Leave special cursor alone
911
912			do {
913				XMaskEvent(dpy,
914				           ButtonPressMask | ButtonReleaseMask |
915				           EnterWindowMask | LeaveWindowMask |
916				           ButtonMotionMask | VisibilityChangeMask | ExposureMask, &Event);
917
918				if(fromtitlebar && Event.type == ButtonPress) {
919					fromtitlebar = false;
920					continue;
921				}
922
923				if(Event.type == MotionNotify) {
924					/* discard any extra motion events before a release */
925					while
926					(XCheckMaskEvent
927					                (dpy, ButtonMotionMask | ButtonReleaseMask, &Event))
928						if(Event.type == ButtonRelease) {
929							break;
930						}
931				}
932
933				if(!DispatchEvent2()) {
934					continue;
935				}
936
937			}
938			while(!(Event.type == ButtonRelease || Cancel));
939		}
940	}
941	return;
942}
943
944
945/*
946 * The various zoom resizes
947 */
948DFHANDLER(zoom)
949{
950	fullzoom(tmp_win, func);
951}
952DFHANDLER(horizoom)
953{
954	fullzoom(tmp_win, func);
955}
956DFHANDLER(fullzoom)
957{
958	fullzoom(tmp_win, func);
959}
960DFHANDLER(fullscreenzoom)
961{
962	fullzoom(tmp_win, func);
963}
964DFHANDLER(leftzoom)
965{
966	fullzoom(tmp_win, func);
967}
968DFHANDLER(rightzoom)
969{
970	fullzoom(tmp_win, func);
971}
972DFHANDLER(topzoom)
973{
974	fullzoom(tmp_win, func);
975}
976DFHANDLER(bottomzoom)
977{
978	fullzoom(tmp_win, func);
979}
980
981
982/*
983 * f.fill - resizing until collision
984 *
985 * XXX Similar to f.pack's, collapse away this extra level of function.
986 */
987static void fillwindow(TwmWindow *tmp_win, const char *direction);
988DFHANDLER(fill)
989{
990	if(tmp_win->squeezed) {
991		XBell(dpy, 0);
992		return;
993	}
994	fillwindow(tmp_win, action);
995}
996
997static void
998fillwindow(TwmWindow *tmp_win, const char *direction)
999{
1000	int cons, newx, newy, save;
1001	unsigned int neww, newh;
1002	int i;
1003	const int winx = tmp_win->frame_x;
1004	const int winy = tmp_win->frame_y;
1005	const int winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
1006	const int winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
1007
1008	if(!strcmp(direction, "left")) {
1009		cons = FindConstraint(tmp_win, MFD_LEFT);
1010		if(cons == -1) {
1011			return;
1012		}
1013		newx = cons;
1014		newy = tmp_win->frame_y;
1015		neww = winw + winx - newx;
1016		newh = winh;
1017		neww -= 2 * tmp_win->frame_bw;
1018		newh -= 2 * tmp_win->frame_bw;
1019		ConstrainSize(tmp_win, &neww, &newh);
1020	}
1021	else if(!strcmp(direction, "right")) {
1022		for(i = 0; i < 2; i++) {
1023			cons = FindConstraint(tmp_win, MFD_RIGHT);
1024			if(cons == -1) {
1025				return;
1026			}
1027			newx = tmp_win->frame_x;
1028			newy = tmp_win->frame_y;
1029			neww = cons - winx;
1030			newh = winh;
1031			save = neww;
1032			neww -= 2 * tmp_win->frame_bw;
1033			newh -= 2 * tmp_win->frame_bw;
1034			ConstrainSize(tmp_win, &neww, &newh);
1035			if((neww != winw) || (newh != winh) ||
1036			                (cons == Scr->rootw - Scr->BorderRight)) {
1037				break;
1038			}
1039			neww = save;
1040			SetupWindow(tmp_win, newx, newy, neww, newh, -1);
1041		}
1042	}
1043	else if(!strcmp(direction, "top")) {
1044		cons = FindConstraint(tmp_win, MFD_TOP);
1045		if(cons == -1) {
1046			return;
1047		}
1048		newx = tmp_win->frame_x;
1049		newy = cons;
1050		neww = winw;
1051		newh = winh + winy - newy;
1052		neww -= 2 * tmp_win->frame_bw;
1053		newh -= 2 * tmp_win->frame_bw;
1054		ConstrainSize(tmp_win, &neww, &newh);
1055	}
1056	else if(!strcmp(direction, "bottom")) {
1057		for(i = 0; i < 2; i++) {
1058			cons = FindConstraint(tmp_win, MFD_BOTTOM);
1059			if(cons == -1) {
1060				return;
1061			}
1062			newx = tmp_win->frame_x;
1063			newy = tmp_win->frame_y;
1064			neww = winw;
1065			newh = cons - winy;
1066			save = newh;
1067			neww -= 2 * tmp_win->frame_bw;
1068			newh -= 2 * tmp_win->frame_bw;
1069			ConstrainSize(tmp_win, &neww, &newh);
1070			if((neww != winw) || (newh != winh) ||
1071			                (cons == Scr->rooth - Scr->BorderBottom)) {
1072				break;
1073			}
1074			newh = save;
1075			SetupWindow(tmp_win, newx, newy, neww, newh, -1);
1076		}
1077	}
1078	else if(!strcmp(direction, "vertical")) {
1079		if(tmp_win->zoomed == ZOOM_NONE) {
1080			tmp_win->save_frame_height = tmp_win->frame_height;
1081			tmp_win->save_frame_width = tmp_win->frame_width;
1082			tmp_win->save_frame_y = tmp_win->frame_y;
1083			tmp_win->save_frame_x = tmp_win->frame_x;
1084
1085			tmp_win->frame_y++;
1086			newy = FindConstraint(tmp_win, MFD_TOP);
1087			tmp_win->frame_y--;
1088			newh = FindConstraint(tmp_win, MFD_BOTTOM) - newy;
1089			newh -= 2 * tmp_win->frame_bw;
1090
1091			newx = tmp_win->frame_x;
1092			neww = tmp_win->frame_width;
1093
1094			ConstrainSize(tmp_win, &neww, &newh);
1095
1096			/* if the bottom of the window has moved up
1097			 * it will be pushed down */
1098			if(newy + newh < tmp_win->save_frame_y + tmp_win->save_frame_height) {
1099				newy = tmp_win->save_frame_y +
1100				       tmp_win->save_frame_height - newh;
1101			}
1102			tmp_win->zoomed = F_ZOOM;
1103			SetupWindow(tmp_win, newx, newy, neww, newh, -1);
1104		}
1105		else {
1106			fullzoom(tmp_win, tmp_win->zoomed);
1107		}
1108		return;
1109	}
1110	else {
1111		return;
1112	}
1113	SetupWindow(tmp_win, newx, newy, neww, newh, -1);
1114}
1115
1116
1117
1118/*
1119 *********************************************************
1120 *
1121 * Resizing/moving to specified geometries
1122 *
1123 *********************************************************
1124 */
1125
1126/*
1127 * Resizing to a window's idea of its "normal" size, from WM_NORMAL_HINTS
1128 * property.
1129 */
1130DFHANDLER(initsize)
1131{
1132	int grav, x, y;
1133	unsigned int width, height, swidth, sheight;
1134
1135	grav = ((tmp_win->hints.flags & PWinGravity)
1136	        ? tmp_win->hints.win_gravity : NorthWestGravity);
1137
1138	if(!(tmp_win->hints.flags & USSize) && !(tmp_win->hints.flags & PSize)) {
1139		return;
1140	}
1141
1142	width  = tmp_win->hints.width  + 2 * tmp_win->frame_bw3D;
1143	height  = tmp_win->hints.height + 2 * tmp_win->frame_bw3D +
1144	          tmp_win->title_height;
1145	ConstrainSize(tmp_win, &width, &height);
1146
1147	x  = tmp_win->frame_x;
1148	y  = tmp_win->frame_y;
1149	swidth = tmp_win->frame_width;
1150	sheight = tmp_win->frame_height;
1151
1152	switch(grav) {
1153		case ForgetGravity:
1154		case StaticGravity:
1155		case NorthWestGravity:
1156		case NorthGravity:
1157		case WestGravity:
1158		case CenterGravity:
1159			break;
1160
1161		case NorthEastGravity:
1162		case EastGravity:
1163			x += swidth - width;
1164			break;
1165
1166		case SouthWestGravity:
1167		case SouthGravity:
1168			y += sheight - height;
1169			break;
1170
1171		case SouthEastGravity:
1172			x += swidth - width;
1173			y += sheight - height;
1174			break;
1175	}
1176
1177	SetupWindow(tmp_win, x, y, width, height, -1);
1178	return;
1179}
1180
1181
1182/*
1183 * Setting a window to a specific specified geometry string.
1184 */
1185DFHANDLER(moveresize)
1186{
1187	int x, y, mask;
1188	unsigned int width, height;
1189	int px = 20, py = 30;
1190
1191	mask = XParseGeometry(action, &x, &y, &width, &height);
1192	if(!(mask &  WidthValue)) {
1193		width = tmp_win->frame_width;
1194	}
1195	else {
1196		width += 2 * tmp_win->frame_bw3D;
1197	}
1198	if(!(mask & HeightValue)) {
1199		height = tmp_win->frame_height;
1200	}
1201	else {
1202		height += 2 * tmp_win->frame_bw3D + tmp_win->title_height;
1203	}
1204	ConstrainSize(tmp_win, &width, &height);
1205	if(mask & XValue) {
1206		if(mask & XNegative) {
1207			x += Scr->rootw  - width;
1208		}
1209	}
1210	else {
1211		x = tmp_win->frame_x;
1212	}
1213	if(mask & YValue) {
1214		if(mask & YNegative) {
1215			y += Scr->rooth - height;
1216		}
1217	}
1218	else {
1219		y = tmp_win->frame_y;
1220	}
1221
1222	{
1223		int          junkX, junkY;
1224		unsigned int junkK;
1225		Window       junkW;
1226		XQueryPointer(dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &px, &py, &junkK);
1227	}
1228	px -= tmp_win->frame_x;
1229	if(px > width) {
1230		px = width / 2;
1231	}
1232	py -= tmp_win->frame_y;
1233	if(py > height) {
1234		px = height / 2;
1235	}
1236
1237	SetupWindow(tmp_win, x, y, width, height, -1);
1238	XWarpPointer(dpy, Scr->Root, Scr->Root, 0, 0, 0, 0, x + px, y + py);
1239	return;
1240}
1241
1242
1243/*
1244 * Making a specified alteration to a window's size
1245 */
1246DFHANDLER(changesize)
1247{
1248	/* XXX Only use of this func; should we collapse? */
1249	ChangeSize(action, tmp_win);
1250}
1251
1252
1253/*
1254 * Stashing and flipping back to a geometry
1255 */
1256DFHANDLER(savegeometry)
1257{
1258	savegeometry(tmp_win);
1259}
1260
1261DFHANDLER(restoregeometry)
1262{
1263	restoregeometry(tmp_win);
1264}
1265
1266
1267
1268
1269/*
1270 *********************************************************
1271 *
1272 * Misc utils used in the above
1273 *
1274 *********************************************************
1275 */
1276
1277/*
1278 * Used in the various move/fill/pack/etc bits
1279 */
1280static int
1281FindConstraint(TwmWindow *tmp_win, MoveFillDir direction)
1282{
1283	TwmWindow  *t;
1284	int ret;
1285	const int winx = tmp_win->frame_x;
1286	const int winy = tmp_win->frame_y;
1287	const int winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
1288	const int winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
1289
1290	switch(direction) {
1291		case MFD_LEFT:
1292			if(winx < Scr->BorderLeft) {
1293				return -1;
1294			}
1295			ret = Scr->BorderLeft;
1296			break;
1297		case MFD_RIGHT:
1298			if(winx + winw > Scr->rootw - Scr->BorderRight) {
1299				return -1;
1300			}
1301			ret = Scr->rootw - Scr->BorderRight;
1302			break;
1303		case MFD_TOP:
1304			if(winy < Scr->BorderTop) {
1305				return -1;
1306			}
1307			ret = Scr->BorderTop;
1308			break;
1309		case MFD_BOTTOM:
1310			if(winy + winh > Scr->rooth - Scr->BorderBottom) {
1311				return -1;
1312			}
1313			ret = Scr->rooth - Scr->BorderBottom;
1314			break;
1315		default:
1316			return -1;
1317	}
1318	for(t = Scr->FirstWindow; t != NULL; t = t->next) {
1319		const int w = t->frame_width  + 2 * t->frame_bw;
1320		const int h = t->frame_height + 2 * t->frame_bw;
1321
1322		if(t == tmp_win) {
1323			continue;
1324		}
1325		if(!visible(t)) {
1326			continue;
1327		}
1328		if(!t->mapped) {
1329			continue;
1330		}
1331
1332		switch(direction) {
1333			case MFD_LEFT:
1334				if(winx        <= t->frame_x + w) {
1335					continue;
1336				}
1337				if(winy        >= t->frame_y + h) {
1338					continue;
1339				}
1340				if(winy + winh <= t->frame_y) {
1341					continue;
1342				}
1343				ret = MAX(ret, t->frame_x + w);
1344				break;
1345			case MFD_RIGHT:
1346				if(winx + winw >= t->frame_x) {
1347					continue;
1348				}
1349				if(winy        >= t->frame_y + h) {
1350					continue;
1351				}
1352				if(winy + winh <= t->frame_y) {
1353					continue;
1354				}
1355				ret = MIN(ret, t->frame_x);
1356				break;
1357			case MFD_TOP:
1358				if(winy        <= t->frame_y + h) {
1359					continue;
1360				}
1361				if(winx        >= t->frame_x + w) {
1362					continue;
1363				}
1364				if(winx + winw <= t->frame_x) {
1365					continue;
1366				}
1367				ret = MAX(ret, t->frame_y + h);
1368				break;
1369			case MFD_BOTTOM:
1370				if(winy + winh >= t->frame_y) {
1371					continue;
1372				}
1373				if(winx        >= t->frame_x + w) {
1374					continue;
1375				}
1376				if(winx + winw <= t->frame_x) {
1377					continue;
1378				}
1379				ret = MIN(ret, t->frame_y);
1380				break;
1381		}
1382	}
1383	return ret;
1384}
1385
1386
1387/*
1388 * Is Window w part of the conglomerate of metawindows we put around the
1389 * real window for TwmWindow t?  Note that this does _not_ check if w is
1390 * the actual window we built the TwmWindow t around.
1391 */
1392static bool
1393belongs_to_twm_window(TwmWindow *t, Window w)
1394{
1395	/* Safety */
1396	if(!t) {
1397		return false;
1398	}
1399
1400	/* Part of the framing we put around the window? */
1401	if(w == t->frame || w == t->title_w
1402	                || w == t->hilite_wl || w == t->hilite_wr) {
1403		return true;
1404	}
1405
1406	/* Part of the icon bits? */
1407	if(t->icon && (w == t->icon->w || w == t->icon->bm_w)) {
1408		return true;
1409	}
1410
1411	/* One of the title button windows? */
1412	if(t->titlebuttons) {
1413		TBWindow *tbw;
1414		int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
1415		for(tbw = t->titlebuttons ; nb > 0 ; tbw++, nb--) {
1416			if(tbw->window == w) {
1417				return true;
1418			}
1419		}
1420	}
1421
1422	/* Then no */
1423	return false;
1424}
1425