1 /*
2  * Copyright © 2005 Novell, Inc.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software
5  * and its documentation for any purpose is hereby granted without
6  * fee, provided that the above copyright notice appear in all copies
7  * and that both that copyright notice and this permission notice
8  * appear in supporting documentation, and that the name of
9  * Novell, Inc. not be used in advertising or publicity pertaining to
10  * distribution of the software without specific, written prior permission.
11  * Novell, Inc. makes no representations about the suitability of this
12  * software for any purpose. It is provided "as is" without express or
13  * implied warranty.
14  *
15  * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17  * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
21  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: David Reveman <davidr@novell.com>
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include <X11/cursorfont.h>
31 
32 #include <compiz-core.h>
33 
34 static CompMetadata moveMetadata;
35 
36 struct _MoveKeys {
37     char *name;
38     int  dx;
39     int  dy;
40 } mKeys[] = {
41     { "Left",  -1,  0 },
42     { "Right",  1,  0 },
43     { "Up",     0, -1 },
44     { "Down",   0,  1 }
45 };
46 
47 #define NUM_KEYS (sizeof (mKeys) / sizeof (mKeys[0]))
48 
49 #define KEY_MOVE_INC 24
50 
51 #define SNAP_BACK 20
52 #define SNAP_OFF  100
53 
54 static int displayPrivateIndex;
55 
56 #define MOVE_DISPLAY_OPTION_INITIATE_BUTTON   0
57 #define MOVE_DISPLAY_OPTION_INITIATE_KEY      1
58 #define MOVE_DISPLAY_OPTION_OPACITY	      2
59 #define MOVE_DISPLAY_OPTION_CONSTRAIN_Y	      3
60 #define MOVE_DISPLAY_OPTION_SNAPOFF_MAXIMIZED 4
61 #define MOVE_DISPLAY_OPTION_LAZY_POSITIONING  5
62 #define MOVE_DISPLAY_OPTION_NUM		      6
63 
64 typedef struct _MoveDisplay {
65     int		    screenPrivateIndex;
66     HandleEventProc handleEvent;
67 
68     CompOption opt[MOVE_DISPLAY_OPTION_NUM];
69 
70     CompWindow *w;
71     int	       savedX;
72     int	       savedY;
73     int	       x;
74     int	       y;
75     Region     region;
76     int        status;
77     Bool       constrainY;
78     KeyCode    key[NUM_KEYS];
79 
80     int releaseButton;
81 
82     GLushort moveOpacity;
83 } MoveDisplay;
84 
85 typedef struct _MoveScreen {
86     PaintWindowProc paintWindow;
87 
88     int grabIndex;
89 
90     Cursor moveCursor;
91 
92     unsigned int origState;
93 
94     int	snapOffY;
95     int	snapBackY;
96 } MoveScreen;
97 
98 #define GET_MOVE_DISPLAY(d)					  \
99     ((MoveDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
100 
101 #define MOVE_DISPLAY(d)		           \
102     MoveDisplay *md = GET_MOVE_DISPLAY (d)
103 
104 #define GET_MOVE_SCREEN(s, md)					      \
105     ((MoveScreen *) (s)->base.privates[(md)->screenPrivateIndex].ptr)
106 
107 #define MOVE_SCREEN(s)						        \
108     MoveScreen *ms = GET_MOVE_SCREEN (s, GET_MOVE_DISPLAY (s->display))
109 
110 #define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
111 
112 static Bool
moveInitiate(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)113 moveInitiate (CompDisplay     *d,
114 	      CompAction      *action,
115 	      CompActionState state,
116 	      CompOption      *option,
117 	      int	      nOption)
118 {
119     CompWindow *w;
120     Window     xid;
121 
122     MOVE_DISPLAY (d);
123 
124     xid = getIntOptionNamed (option, nOption, "window", 0);
125 
126     w = findWindowAtDisplay (d, xid);
127     if (w && (w->actions & CompWindowActionMoveMask))
128     {
129 	XRectangle   workArea;
130 	unsigned int mods;
131 	int          x, y, button;
132 	Bool         sourceExternalApp;
133 
134 	MOVE_SCREEN (w->screen);
135 
136 	mods = getIntOptionNamed (option, nOption, "modifiers", 0);
137 
138 	x = getIntOptionNamed (option, nOption, "x",
139 			       w->attrib.x + (w->width / 2));
140 	y = getIntOptionNamed (option, nOption, "y",
141 			       w->attrib.y + (w->height / 2));
142 
143 	button = getIntOptionNamed (option, nOption, "button", -1);
144 
145 	if (otherScreenGrabExist (w->screen, "move", NULL))
146 	    return FALSE;
147 
148 	if (md->w)
149 	    return FALSE;
150 
151 	if (w->type & (CompWindowTypeDesktopMask |
152 		       CompWindowTypeDockMask    |
153 		       CompWindowTypeFullscreenMask))
154 	    return FALSE;
155 
156 	if (w->attrib.override_redirect)
157 	    return FALSE;
158 
159 	if (state & CompActionStateInitButton)
160 	    action->state |= CompActionStateTermButton;
161 
162 	if (md->region)
163 	{
164 	    XDestroyRegion (md->region);
165 	    md->region = NULL;
166 	}
167 
168 	md->status = RectangleOut;
169 
170 	md->savedX = w->serverX;
171 	md->savedY = w->serverY;
172 
173 	md->x = 0;
174 	md->y = 0;
175 
176 	sourceExternalApp = getBoolOptionNamed (option, nOption, "external",
177 						FALSE);
178 	md->constrainY = sourceExternalApp &&
179 			 md->opt[MOVE_DISPLAY_OPTION_CONSTRAIN_Y].value.b;
180 
181 	lastPointerX = x;
182 	lastPointerY = y;
183 
184 	ms->origState = w->state;
185 
186 	getWorkareaForOutput (w->screen,
187 			      outputDeviceForWindow (w),
188 			      &workArea);
189 
190 	ms->snapBackY = w->serverY - workArea.y;
191 	ms->snapOffY  = y - workArea.y;
192 
193 	if (!ms->grabIndex)
194 	    ms->grabIndex = pushScreenGrab (w->screen, ms->moveCursor, "move");
195 
196 	if (ms->grabIndex)
197 	{
198 	    unsigned int grabMask = CompWindowGrabMoveMask |
199 				    CompWindowGrabButtonMask;
200 
201 	    if (sourceExternalApp)
202 		grabMask |= CompWindowGrabExternalAppMask;
203 
204 	    md->w = w;
205 
206 	    md->releaseButton = button;
207 
208 	    (w->screen->windowGrabNotify) (w, x, y, mods, grabMask);
209 
210 	    if (d->opt[COMP_DISPLAY_OPTION_RAISE_ON_CLICK].value.b)
211 		updateWindowAttributes (w,
212 					CompStackingUpdateModeAboveFullscreen);
213 
214 	    if (state & CompActionStateInitKey)
215 	    {
216 		int xRoot, yRoot;
217 
218 		xRoot = w->attrib.x + (w->width  / 2);
219 		yRoot = w->attrib.y + (w->height / 2);
220 
221 		warpPointer (w->screen, xRoot - pointerX, yRoot - pointerY);
222 	    }
223 
224 	    if (md->moveOpacity != OPAQUE)
225 		addWindowDamage (w);
226 	}
227     }
228 
229     return FALSE;
230 }
231 
232 static Bool
moveTerminate(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)233 moveTerminate (CompDisplay     *d,
234 	       CompAction      *action,
235 	       CompActionState state,
236 	       CompOption      *option,
237 	       int	       nOption)
238 {
239     MOVE_DISPLAY (d);
240 
241     if (md->w)
242     {
243 	MOVE_SCREEN (md->w->screen);
244 
245 	if (state & CompActionStateCancel)
246 	    moveWindow (md->w,
247 			md->savedX - md->w->attrib.x,
248 			md->savedY - md->w->attrib.y,
249 			TRUE, FALSE);
250 
251 	syncWindowPosition (md->w);
252 
253 	/* update window size as window constraints may have
254 	   changed - needed e.g. if a maximized window was moved
255 	   to another output device */
256 	updateWindowSize (md->w);
257 
258 	(md->w->screen->windowUngrabNotify) (md->w);
259 
260 	if (ms->grabIndex)
261 	{
262 	    removeScreenGrab (md->w->screen, ms->grabIndex, NULL);
263 	    ms->grabIndex = 0;
264 	}
265 
266 	if (md->moveOpacity != OPAQUE)
267 	    addWindowDamage (md->w);
268 
269 	md->w             = 0;
270 	md->releaseButton = 0;
271     }
272 
273     action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
274 
275     return FALSE;
276 }
277 
278 /* creates a region containing top and bottom struts. only struts that are
279    outside the screen workarea are considered. */
280 static Region
moveGetYConstrainRegion(CompScreen * s)281 moveGetYConstrainRegion (CompScreen *s)
282 {
283     CompWindow *w;
284     Region     region;
285     REGION     r;
286     XRectangle workArea;
287     BoxRec     extents;
288     int	       i;
289 
290     region = XCreateRegion ();
291     if (!region)
292 	return NULL;
293 
294     r.rects    = &r.extents;
295     r.numRects = r.size = 1;
296 
297     r.extents.x1 = MINSHORT;
298     r.extents.y1 = 0;
299     r.extents.x2 = 0;
300     r.extents.y2 = s->height;
301 
302     XUnionRegion (&r, region, region);
303 
304     r.extents.x1 = s->width;
305     r.extents.x2 = MAXSHORT;
306 
307     XUnionRegion (&r, region, region);
308 
309     for (i = 0; i < s->nOutputDev; i++)
310     {
311 	XUnionRegion (&s->outputDev[i].region, region, region);
312 
313 	getWorkareaForOutput (s, i, &workArea);
314 	extents = s->outputDev[i].region.extents;
315 
316 	for (w = s->windows; w; w = w->next)
317 	{
318 	    if (!w->mapNum)
319 		continue;
320 
321 	    if (w->struts)
322 	    {
323 		r.extents.x1 = w->struts->top.x;
324 		r.extents.y1 = w->struts->top.y;
325 		r.extents.x2 = r.extents.x1 + w->struts->top.width;
326 		r.extents.y2 = r.extents.y1 + w->struts->top.height;
327 
328 		if (r.extents.x1 < extents.x1)
329 		    r.extents.x1 = extents.x1;
330 		if (r.extents.x2 > extents.x2)
331 		    r.extents.x2 = extents.x2;
332 		if (r.extents.y1 < extents.y1)
333 		    r.extents.y1 = extents.y1;
334 		if (r.extents.y2 > extents.y2)
335 		    r.extents.y2 = extents.y2;
336 
337 		if (r.extents.x1 < r.extents.x2 && r.extents.y1 < r.extents.y2)
338 		{
339 		    if (r.extents.y2 <= workArea.y)
340 			XSubtractRegion (region, &r, region);
341 		}
342 
343 		r.extents.x1 = w->struts->bottom.x;
344 		r.extents.y1 = w->struts->bottom.y;
345 		r.extents.x2 = r.extents.x1 + w->struts->bottom.width;
346 		r.extents.y2 = r.extents.y1 + w->struts->bottom.height;
347 
348 		if (r.extents.x1 < extents.x1)
349 		    r.extents.x1 = extents.x1;
350 		if (r.extents.x2 > extents.x2)
351 		    r.extents.x2 = extents.x2;
352 		if (r.extents.y1 < extents.y1)
353 		    r.extents.y1 = extents.y1;
354 		if (r.extents.y2 > extents.y2)
355 		    r.extents.y2 = extents.y2;
356 
357 		if (r.extents.x1 < r.extents.x2 && r.extents.y1 < r.extents.y2)
358 		{
359 		    if (r.extents.y1 >= (workArea.y + workArea.height))
360 			XSubtractRegion (region, &r, region);
361 		}
362 	    }
363 	}
364     }
365 
366     return region;
367 }
368 
369 static void
moveHandleMotionEvent(CompScreen * s,int xRoot,int yRoot)370 moveHandleMotionEvent (CompScreen *s,
371 		       int	  xRoot,
372 		       int	  yRoot)
373 {
374     MOVE_SCREEN (s);
375 
376     if (ms->grabIndex)
377     {
378 	CompWindow *w;
379 	int	   dx, dy;
380 	int	   wX, wY;
381 	int	   wWidth, wHeight;
382 
383 	MOVE_DISPLAY (s->display);
384 
385 	w = md->w;
386 
387 	wX      = w->serverX;
388 	wY      = w->serverY;
389 	wWidth  = w->serverWidth  + w->serverBorderWidth * 2;
390 	wHeight = w->serverHeight + w->serverBorderWidth * 2;
391 
392 	md->x += xRoot - lastPointerX;
393 	md->y += yRoot - lastPointerY;
394 
395 	if (w->type & CompWindowTypeFullscreenMask)
396 	{
397 	    dx = dy = 0;
398 	}
399 	else
400 	{
401 	    XRectangle workArea;
402 	    int	       min, max;
403 
404 	    dx = md->x;
405 	    dy = md->y;
406 
407 	    getWorkareaForOutput (s,
408 				  outputDeviceForWindow (w),
409 				  &workArea);
410 
411 	    if (md->constrainY)
412 	    {
413 		if (!md->region)
414 		    md->region = moveGetYConstrainRegion (s);
415 
416 		/* make sure that the top frame extents or the top row of
417 		   pixels are within what is currently our valid screen
418 		   region */
419 		if (md->region)
420 		{
421 		    int x, y, width, height;
422 		    int status;
423 
424 		    x	   = wX + dx - w->input.left;
425 		    y	   = wY + dy - w->input.top;
426 		    width  = wWidth + w->input.left + w->input.right;
427 		    height = w->input.top ? w->input.top : 1;
428 
429 		    status = XRectInRegion (md->region, x, y, width, height);
430 
431 		    /* only constrain movement if previous position was valid */
432 		    if (md->status == RectangleIn)
433 		    {
434 			int xStatus = status;
435 
436 			while (dx && xStatus != RectangleIn)
437 			{
438 			    xStatus = XRectInRegion (md->region,
439 						     x, y - dy,
440 						     width, height);
441 
442 			    if (xStatus != RectangleIn)
443 				dx += (dx < 0) ? 1 : -1;
444 
445 			    x = wX + dx - w->input.left;
446 			}
447 
448 			while (dy && status != RectangleIn)
449 			{
450 			    status = XRectInRegion (md->region,
451 						    x, y,
452 						    width, height);
453 
454 			    if (status != RectangleIn)
455 				dy += (dy < 0) ? 1 : -1;
456 
457 			    y = wY + dy - w->input.top;
458 			}
459 		    }
460 		    else
461 		    {
462 			md->status = status;
463 		    }
464 		}
465 	    }
466 
467 	    if (md->opt[MOVE_DISPLAY_OPTION_SNAPOFF_MAXIMIZED].value.b)
468 	    {
469 		if (w->state & CompWindowStateMaximizedVertMask)
470 		{
471 		    if (abs ((yRoot - workArea.y) - ms->snapOffY) >= SNAP_OFF)
472 		    {
473 			if (!otherScreenGrabExist (s, "move", NULL))
474 			{
475 			    int width = w->serverWidth;
476 
477 			    w->saveMask |= CWX | CWY;
478 
479 			    if (w->saveMask & CWWidth)
480 				width = w->saveWc.width;
481 
482 			    w->saveWc.x = xRoot - (width >> 1);
483 			    w->saveWc.y = yRoot + (w->input.top >> 1);
484 
485 			    md->x = md->y = 0;
486 
487 			    maximizeWindow (w, 0);
488 
489 			    ms->snapOffY = ms->snapBackY;
490 
491 			    return;
492 			}
493 		    }
494 		}
495 		else if (ms->origState & CompWindowStateMaximizedVertMask)
496 		{
497 		    if (abs ((yRoot - workArea.y) - ms->snapBackY) < SNAP_BACK)
498 		    {
499 			if (!otherScreenGrabExist (s, "move", NULL))
500 			{
501 			    int wy;
502 
503 			    /* update server position before maximizing
504 			       window again so that it is maximized on
505 			       correct output */
506 			    syncWindowPosition (w);
507 
508 			    maximizeWindow (w, ms->origState);
509 
510 			    wy  = workArea.y + (w->input.top >> 1);
511 			    wy += w->sizeHints.height_inc >> 1;
512 
513 			    warpPointer (s, 0, wy - pointerY);
514 
515 			    return;
516 			}
517 		    }
518 		}
519 	    }
520 
521 	    if (w->state & CompWindowStateMaximizedVertMask)
522 	    {
523 		min = workArea.y + w->input.top;
524 		max = workArea.y + workArea.height - w->input.bottom - wHeight;
525 
526 		if (wY + dy < min)
527 		    dy = min - wY;
528 		else if (wY + dy > max)
529 		    dy = max - wY;
530 	    }
531 
532 	    if (w->state & CompWindowStateMaximizedHorzMask)
533 	    {
534 		if (wX > s->width || wX + w->width < 0)
535 		    return;
536 
537 		if (wX + wWidth < 0)
538 		    return;
539 
540 		min = workArea.x + w->input.left;
541 		max = workArea.x + workArea.width - w->input.right - wWidth;
542 
543 		if (wX + dx < min)
544 		    dx = min - wX;
545 		else if (wX + dx > max)
546 		    dx = max - wX;
547 	    }
548 	}
549 
550 	if (dx || dy)
551 	{
552 	    moveWindow (w,
553 			wX + dx - w->attrib.x,
554 			wY + dy - w->attrib.y,
555 			TRUE, FALSE);
556 
557 	    if (md->opt[MOVE_DISPLAY_OPTION_LAZY_POSITIONING].value.b)
558 	    {
559 		/* FIXME: This form of lazy positioning is broken and should
560 		   be replaced asap. Current code exists just to avoid a
561 		   major performance regression in the 0.5.2 release. */
562 		w->serverX = w->attrib.x;
563 		w->serverY = w->attrib.y;
564 	    }
565 	    else
566 	    {
567 		syncWindowPosition (w);
568 	    }
569 
570 	    md->x -= dx;
571 	    md->y -= dy;
572 	}
573     }
574 }
575 
576 static void
moveHandleEvent(CompDisplay * d,XEvent * event)577 moveHandleEvent (CompDisplay *d,
578 		 XEvent      *event)
579 {
580     CompScreen *s;
581 
582     MOVE_DISPLAY (d);
583 
584     switch (event->type) {
585     case ButtonPress:
586     case ButtonRelease:
587 	s = findScreenAtDisplay (d, event->xbutton.root);
588 	if (s)
589 	{
590 	    MOVE_SCREEN (s);
591 
592 	    if (ms->grabIndex)
593 	    {
594 		if (md->releaseButton == -1 ||
595 		    md->releaseButton == event->xbutton.button)
596 		{
597 		    CompAction *action;
598 		    int        opt = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
599 
600 		    action = &md->opt[opt].value.action;
601 		    moveTerminate (d, action, CompActionStateTermButton,
602 				   NULL, 0);
603 		}
604 	    }
605 	}
606 	break;
607     case KeyPress:
608 	s = findScreenAtDisplay (d, event->xkey.root);
609 	if (s)
610 	{
611 	    MOVE_SCREEN (s);
612 
613 	    if (ms->grabIndex)
614 	    {
615 		int i;
616 
617 		for (i = 0; i < NUM_KEYS; i++)
618 		{
619 		    if (event->xkey.keycode == md->key[i])
620 		    {
621 			XWarpPointer (d->display, None, None, 0, 0, 0, 0,
622 				      mKeys[i].dx * KEY_MOVE_INC,
623 				      mKeys[i].dy * KEY_MOVE_INC);
624 			break;
625 		    }
626 		}
627 	    }
628 	}
629 	break;
630     case MotionNotify:
631 	s = findScreenAtDisplay (d, event->xmotion.root);
632 	if (s)
633 	    moveHandleMotionEvent (s, pointerX, pointerY);
634 	break;
635     case EnterNotify:
636     case LeaveNotify:
637 	s = findScreenAtDisplay (d, event->xcrossing.root);
638 	if (s)
639 	    moveHandleMotionEvent (s, pointerX, pointerY);
640 	break;
641     case ClientMessage:
642 	if (event->xclient.message_type == d->wmMoveResizeAtom)
643 	{
644 	    CompWindow *w;
645 
646 	    if (event->xclient.data.l[2] == WmMoveResizeMove ||
647 		event->xclient.data.l[2] == WmMoveResizeMoveKeyboard)
648 	    {
649 		w = findWindowAtDisplay (d, event->xclient.window);
650 		if (w)
651 		{
652 		    CompOption o[6];
653 		    int	       xRoot, yRoot;
654 		    int	       option;
655 
656 		    o[0].type    = CompOptionTypeInt;
657 		    o[0].name    = "window";
658 		    o[0].value.i = event->xclient.window;
659 
660 		    o[1].type    = CompOptionTypeBool;
661 		    o[1].name    = "external";
662 		    o[1].value.b = TRUE;
663 
664 		    if (event->xclient.data.l[2] == WmMoveResizeMoveKeyboard)
665 		    {
666 			option = MOVE_DISPLAY_OPTION_INITIATE_KEY;
667 
668 			moveInitiate (d, &md->opt[option].value.action,
669 				      CompActionStateInitKey,
670 				      o, 2);
671 		    }
672 		    else
673 		    {
674 			unsigned int mods;
675 			Window	     root, child;
676 			int	     i;
677 
678 			option = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
679 
680 			XQueryPointer (d->display, w->screen->root,
681 				       &root, &child, &xRoot, &yRoot,
682 				       &i, &i, &mods);
683 
684 			/* TODO: not only button 1 */
685 			if (mods & Button1Mask)
686 			{
687 			    o[2].type	 = CompOptionTypeInt;
688 			    o[2].name	 = "modifiers";
689 			    o[2].value.i = mods;
690 
691 			    o[3].type	 = CompOptionTypeInt;
692 			    o[3].name	 = "x";
693 			    o[3].value.i = event->xclient.data.l[0];
694 
695 			    o[4].type	 = CompOptionTypeInt;
696 			    o[4].name	 = "y";
697 			    o[4].value.i = event->xclient.data.l[1];
698 
699 			    o[5].type    = CompOptionTypeInt;
700 			    o[5].name    = "button";
701 			    o[5].value.i = event->xclient.data.l[3] ?
702 				           event->xclient.data.l[3] : -1;
703 
704 			    moveInitiate (d,
705 					  &md->opt[option].value.action,
706 					  CompActionStateInitButton,
707 					  o, 6);
708 
709 			    moveHandleMotionEvent (w->screen, xRoot, yRoot);
710 			}
711 		    }
712 		}
713 	    }
714 	    else if (md->w && event->xclient.data.l[2] == WmMoveResizeCancel)
715 	    {
716 		if (md->w->id == event->xclient.window)
717 		{
718 		    int option;
719 
720 		    option = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
721 		    moveTerminate (d,
722 				   &md->opt[option].value.action,
723 				   CompActionStateCancel, NULL, 0);
724 		    option = MOVE_DISPLAY_OPTION_INITIATE_KEY;
725 		    moveTerminate (d,
726 				   &md->opt[option].value.action,
727 				   CompActionStateCancel, NULL, 0);
728 		}
729 	    }
730 	}
731 	break;
732     case DestroyNotify:
733 	if (md->w && md->w->id == event->xdestroywindow.window)
734 	{
735 	    int option;
736 
737 	    option = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
738 	    moveTerminate (d,
739 			   &md->opt[option].value.action,
740 			   0, NULL, 0);
741 	    option = MOVE_DISPLAY_OPTION_INITIATE_KEY;
742 	    moveTerminate (d,
743 			   &md->opt[option].value.action,
744 			   0, NULL, 0);
745 	}
746 	break;
747     case UnmapNotify:
748 	if (md->w && md->w->id == event->xunmap.window)
749 	{
750 	    int option;
751 
752 	    option = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
753 	    moveTerminate (d,
754 			   &md->opt[option].value.action,
755 			   0, NULL, 0);
756 	    option = MOVE_DISPLAY_OPTION_INITIATE_KEY;
757 	    moveTerminate (d,
758 			   &md->opt[option].value.action,
759 			   0, NULL, 0);
760 	}
761     default:
762 	break;
763     }
764 
765     UNWRAP (md, d, handleEvent);
766     (*d->handleEvent) (d, event);
767     WRAP (md, d, handleEvent, moveHandleEvent);
768 }
769 
770 static Bool
movePaintWindow(CompWindow * w,const WindowPaintAttrib * attrib,const CompTransform * transform,Region region,unsigned int mask)771 movePaintWindow (CompWindow		 *w,
772 		 const WindowPaintAttrib *attrib,
773 		 const CompTransform	 *transform,
774 		 Region			 region,
775 		 unsigned int		 mask)
776 {
777     WindowPaintAttrib sAttrib;
778     CompScreen	      *s = w->screen;
779     Bool	      status;
780 
781     MOVE_SCREEN (s);
782 
783     if (ms->grabIndex)
784     {
785 	MOVE_DISPLAY (s->display);
786 
787 	if (md->w == w && md->moveOpacity != OPAQUE)
788 	{
789 	    /* modify opacity of windows that are not active */
790 	    sAttrib = *attrib;
791 	    attrib  = &sAttrib;
792 
793 	    sAttrib.opacity = (sAttrib.opacity * md->moveOpacity) >> 16;
794 	}
795     }
796 
797     UNWRAP (ms, s, paintWindow);
798     status = (*s->paintWindow) (w, attrib, transform, region, mask);
799     WRAP (ms, s, paintWindow, movePaintWindow);
800 
801     return status;
802 }
803 
804 static CompOption *
moveGetDisplayOptions(CompPlugin * plugin,CompDisplay * display,int * count)805 moveGetDisplayOptions (CompPlugin  *plugin,
806 		       CompDisplay *display,
807 		       int	   *count)
808 {
809     MOVE_DISPLAY (display);
810 
811     *count = NUM_OPTIONS (md);
812     return md->opt;
813 }
814 
815 static Bool
moveSetDisplayOption(CompPlugin * plugin,CompDisplay * display,const char * name,CompOptionValue * value)816 moveSetDisplayOption (CompPlugin      *plugin,
817 		      CompDisplay     *display,
818 		      const char      *name,
819 		      CompOptionValue *value)
820 {
821     CompOption *o;
822     int	       index;
823 
824     MOVE_DISPLAY (display);
825 
826     o = compFindOption (md->opt, NUM_OPTIONS (md), name, &index);
827     if (!o)
828 	return FALSE;
829 
830     switch (index) {
831     case MOVE_DISPLAY_OPTION_OPACITY:
832 	if (compSetIntOption (o, value))
833 	{
834 	    md->moveOpacity = (o->value.i * OPAQUE) / 100;
835 	    return TRUE;
836 	}
837 	break;
838     default:
839 	return compSetDisplayOption (display, o, value);
840     }
841 
842     return FALSE;
843 }
844 
845 static const CompMetadataOptionInfo moveDisplayOptionInfo[] = {
846     { "initiate_button", "button", 0, moveInitiate, moveTerminate },
847     { "initiate_key", "key", 0, moveInitiate, moveTerminate },
848     { "opacity", "int", "<min>0</min><max>100</max>", 0, 0 },
849     { "constrain_y", "bool", 0, 0, 0 },
850     { "snapoff_maximized", "bool", 0, 0, 0 },
851     { "lazy_positioning", "bool", 0, 0, 0 }
852 };
853 
854 static Bool
moveInitDisplay(CompPlugin * p,CompDisplay * d)855 moveInitDisplay (CompPlugin  *p,
856 		 CompDisplay *d)
857 {
858     MoveDisplay *md;
859     int	        i;
860 
861     if (!checkPluginABI ("core", CORE_ABIVERSION))
862 	return FALSE;
863 
864     md = malloc (sizeof (MoveDisplay));
865     if (!md)
866 	return FALSE;
867 
868     if (!compInitDisplayOptionsFromMetadata (d,
869 					     &moveMetadata,
870 					     moveDisplayOptionInfo,
871 					     md->opt,
872 					     MOVE_DISPLAY_OPTION_NUM))
873     {
874 	free (md);
875 	return FALSE;
876     }
877 
878     md->screenPrivateIndex = allocateScreenPrivateIndex (d);
879     if (md->screenPrivateIndex < 0)
880     {
881 	compFiniDisplayOptions (d, md->opt, MOVE_DISPLAY_OPTION_NUM);
882 	free (md);
883 	return FALSE;
884     }
885 
886     md->moveOpacity =
887 	(md->opt[MOVE_DISPLAY_OPTION_OPACITY].value.i * OPAQUE) / 100;
888 
889     md->w             = 0;
890     md->region        = NULL;
891     md->status        = RectangleOut;
892     md->releaseButton = 0;
893     md->constrainY    = FALSE;
894 
895     for (i = 0; i < NUM_KEYS; i++)
896 	md->key[i] = XKeysymToKeycode (d->display,
897 				       XStringToKeysym (mKeys[i].name));
898 
899     WRAP (md, d, handleEvent, moveHandleEvent);
900 
901     d->base.privates[displayPrivateIndex].ptr = md;
902 
903     return TRUE;
904 }
905 
906 static void
moveFiniDisplay(CompPlugin * p,CompDisplay * d)907 moveFiniDisplay (CompPlugin  *p,
908 		 CompDisplay *d)
909 {
910     MOVE_DISPLAY (d);
911 
912     freeScreenPrivateIndex (d, md->screenPrivateIndex);
913 
914     UNWRAP (md, d, handleEvent);
915 
916     compFiniDisplayOptions (d, md->opt, MOVE_DISPLAY_OPTION_NUM);
917 
918     if (md->region)
919 	XDestroyRegion (md->region);
920 
921     free (md);
922 }
923 
924 static Bool
moveInitScreen(CompPlugin * p,CompScreen * s)925 moveInitScreen (CompPlugin *p,
926 		CompScreen *s)
927 {
928     MoveScreen *ms;
929 
930     MOVE_DISPLAY (s->display);
931 
932     ms = malloc (sizeof (MoveScreen));
933     if (!ms)
934 	return FALSE;
935 
936     ms->grabIndex = 0;
937 
938     ms->moveCursor = XCreateFontCursor (s->display->display, XC_fleur);
939 
940     WRAP (ms, s, paintWindow, movePaintWindow);
941 
942     s->base.privates[md->screenPrivateIndex].ptr = ms;
943 
944     return TRUE;
945 }
946 
947 static void
moveFiniScreen(CompPlugin * p,CompScreen * s)948 moveFiniScreen (CompPlugin *p,
949 		CompScreen *s)
950 {
951     MOVE_SCREEN (s);
952 
953     UNWRAP (ms, s, paintWindow);
954 
955     if (ms->moveCursor)
956 	XFreeCursor (s->display->display, ms->moveCursor);
957 
958     free (ms);
959 }
960 
961 static CompBool
moveInitObject(CompPlugin * p,CompObject * o)962 moveInitObject (CompPlugin *p,
963 		CompObject *o)
964 {
965     static InitPluginObjectProc dispTab[] = {
966 	(InitPluginObjectProc) 0, /* InitCore */
967 	(InitPluginObjectProc) moveInitDisplay,
968 	(InitPluginObjectProc) moveInitScreen
969     };
970 
971     RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
972 }
973 
974 static void
moveFiniObject(CompPlugin * p,CompObject * o)975 moveFiniObject (CompPlugin *p,
976 		CompObject *o)
977 {
978     static FiniPluginObjectProc dispTab[] = {
979 	(FiniPluginObjectProc) 0, /* FiniCore */
980 	(FiniPluginObjectProc) moveFiniDisplay,
981 	(FiniPluginObjectProc) moveFiniScreen
982     };
983 
984     DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
985 }
986 
987 static CompOption *
moveGetObjectOptions(CompPlugin * plugin,CompObject * object,int * count)988 moveGetObjectOptions (CompPlugin *plugin,
989 		      CompObject *object,
990 		      int	 *count)
991 {
992     static GetPluginObjectOptionsProc dispTab[] = {
993 	(GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
994 	(GetPluginObjectOptionsProc) moveGetDisplayOptions
995     };
996 
997     *count = 0;
998     RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
999 		     (void *) count, (plugin, object, count));
1000 }
1001 
1002 static CompBool
moveSetObjectOption(CompPlugin * plugin,CompObject * object,const char * name,CompOptionValue * value)1003 moveSetObjectOption (CompPlugin      *plugin,
1004 		     CompObject      *object,
1005 		     const char      *name,
1006 		     CompOptionValue *value)
1007 {
1008     static SetPluginObjectOptionProc dispTab[] = {
1009 	(SetPluginObjectOptionProc) 0, /* SetCoreOption */
1010 	(SetPluginObjectOptionProc) moveSetDisplayOption
1011     };
1012 
1013     RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
1014 		     (plugin, object, name, value));
1015 }
1016 
1017 static Bool
moveInit(CompPlugin * p)1018 moveInit (CompPlugin *p)
1019 {
1020     if (!compInitPluginMetadataFromInfo (&moveMetadata,
1021 					 p->vTable->name,
1022 					 moveDisplayOptionInfo,
1023 					 MOVE_DISPLAY_OPTION_NUM,
1024 					 0, 0))
1025 	return FALSE;
1026 
1027     displayPrivateIndex = allocateDisplayPrivateIndex ();
1028     if (displayPrivateIndex < 0)
1029     {
1030 	compFiniMetadata (&moveMetadata);
1031 	return FALSE;
1032     }
1033 
1034     compAddMetadataFromFile (&moveMetadata, p->vTable->name);
1035 
1036     return TRUE;
1037 }
1038 
1039 static void
moveFini(CompPlugin * p)1040 moveFini (CompPlugin *p)
1041 {
1042     freeDisplayPrivateIndex (displayPrivateIndex);
1043     compFiniMetadata (&moveMetadata);
1044 }
1045 
1046 static CompMetadata *
moveGetMetadata(CompPlugin * plugin)1047 moveGetMetadata (CompPlugin *plugin)
1048 {
1049     return &moveMetadata;
1050 }
1051 
1052 CompPluginVTable moveVTable = {
1053     "move",
1054     moveGetMetadata,
1055     moveInit,
1056     moveFini,
1057     moveInitObject,
1058     moveFiniObject,
1059     moveGetObjectOptions,
1060     moveSetObjectOption
1061 };
1062 
1063 CompPluginVTable *
getCompPluginInfo20070830(void)1064 getCompPluginInfo20070830 (void)
1065 {
1066     return &moveVTable;
1067 }
1068