2
* Copyright © 2005 Novell, Inc.
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
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.
23
* Author: David Reveman <davidr@novell.com>
30
#include <X11/cursorfont.h>
32
#include <core/atoms.h>
35
COMPIZ_PLUGIN_20090315 (move, MovePluginVTable)
38
moveInitiate (CompAction *action,
39
CompAction::State state,
40
CompOption::Vector &options)
47
xid = CompOption::getIntOptionNamed (options, "window");
49
w = screen->findWindow (xid);
50
if (w && (w->actions () & CompWindowActionMoveMask))
55
bool sourceExternalApp;
57
CompScreen *s = screen;
59
mods = CompOption::getIntOptionNamed (options, "modifiers", 0);
61
x = CompOption::getIntOptionNamed (options, "x", w->geometry ().x () +
62
(w->size ().width () / 2));
63
y = CompOption::getIntOptionNamed (options, "y", w->geometry ().y () +
64
(w->size ().height () / 2));
66
button = CompOption::getIntOptionNamed (options, "button", -1);
68
if (s->otherGrabExist ("move", NULL))
74
if (w->type () & (CompWindowTypeDesktopMask |
75
CompWindowTypeDockMask |
76
CompWindowTypeFullscreenMask))
79
if (w->overrideRedirect ())
82
if (state & CompAction::StateInitButton)
83
action->setState (action->state () | CompAction::StateTermButton);
87
XDestroyRegion (ms->region);
91
ms->status = RectangleOut;
93
ms->savedX = w->serverGeometry ().x ();
94
ms->savedY = w->serverGeometry ().y ();
103
CompOption::getBoolOptionNamed (options, "external", false);
104
ms->yConstrained = sourceExternalApp && ms->optionGetConstrainY ();
106
ms->origState = w->state ();
108
workArea = s->getWorkareaForOutput (w->outputDevice ());
110
ms->snapBackY = w->serverGeometry ().y () - workArea.y ();
111
ms->snapOffY = y - workArea.y ();
114
ms->grab = s->pushGrab (ms->moveCursor, "move");
118
unsigned int grabMask = CompWindowGrabMoveMask |
119
CompWindowGrabButtonMask;
121
if (sourceExternalApp)
122
grabMask |= CompWindowGrabExternalAppMask;
126
ms->releaseButton = button;
128
w->grabNotify (x, y, mods, grabMask);
130
/* Click raise happens implicitly on buttons 1, 2 and 3 so don't
131
* restack this window again if the action buttonbinding was from
132
* one of those buttons */
133
if (screen->getOption ("raise_on_click")->value ().b () &&
134
button != Button1 && button != Button2 && button != Button3)
135
w->updateAttributes (CompStackingUpdateModeAboveFullscreen);
137
if (state & CompAction::StateInitKey)
141
xRoot = w->geometry ().x () + (w->size ().width () / 2);
142
yRoot = w->geometry ().y () + (w->size ().height () / 2);
144
s->warpPointer (xRoot - pointerX, yRoot - pointerY);
147
if (ms->moveOpacity != OPAQUE)
152
mw->cWindow->addDamage ();
154
mw->gWindow->glPaintSetEnabled (mw, true);
163
moveTerminate (CompAction *action,
164
CompAction::State state,
165
CompOption::Vector &options)
167
MOVE_SCREEN (screen);
171
if (state & CompAction::StateCancel)
172
ms->w->move (ms->savedX - ms->w->geometry ().x (),
173
ms->savedY - ms->w->geometry ().y (), false);
175
ms->w->syncPosition ();
177
/* update window attributes as window constraints may have
178
changed - needed e.g. if a maximized window was moved
179
to another output device */
180
ms->w->updateAttributes (CompStackingUpdateModeNone);
182
ms->w->ungrabNotify ();
186
screen->removeGrab (ms->grab, NULL);
190
if (ms->moveOpacity != OPAQUE)
195
mw->cWindow->addDamage ();
197
mw->gWindow->glPaintSetEnabled (mw, false);
201
ms->releaseButton = 0;
204
action->setState (action->state () & ~(CompAction::StateTermKey |
205
CompAction::StateTermButton));
210
/* creates a region containing top and bottom struts. only struts that are
211
outside the screen workarea are considered. */
213
moveGetYConstrainRegion (CompScreen *s)
222
region = XCreateRegion ();
226
r.rects = &r.extents;
227
r.numRects = r.size = 1;
229
r.extents.x1 = MINSHORT;
232
r.extents.y2 = s->height ();
234
XUnionRegion (&r, region, region);
236
r.extents.x1 = s->width ();
237
r.extents.x2 = MAXSHORT;
239
XUnionRegion (&r, region, region);
241
for (i = 0; i < s->outputDevs ().size (); i++)
243
XUnionRegion (s->outputDevs ()[i].region (), region, region);
245
workArea = s->getWorkareaForOutput (i);
246
extents = s->outputDevs ()[i].region ()->extents;
248
foreach (w, s->windows ())
255
r.extents.x1 = w->struts ()->top.x;
256
r.extents.y1 = w->struts ()->top.y;
257
r.extents.x2 = r.extents.x1 + w->struts ()->top.width;
258
r.extents.y2 = r.extents.y1 + w->struts ()->top.height;
260
if (r.extents.x1 < extents.x1)
261
r.extents.x1 = extents.x1;
262
if (r.extents.x2 > extents.x2)
263
r.extents.x2 = extents.x2;
264
if (r.extents.y1 < extents.y1)
265
r.extents.y1 = extents.y1;
266
if (r.extents.y2 > extents.y2)
267
r.extents.y2 = extents.y2;
269
if (r.extents.x1 < r.extents.x2 && r.extents.y1 < r.extents.y2)
271
if (r.extents.y2 <= workArea.y ())
272
XSubtractRegion (region, &r, region);
275
r.extents.x1 = w->struts ()->bottom.x;
276
r.extents.y1 = w->struts ()->bottom.y;
277
r.extents.x2 = r.extents.x1 + w->struts ()->bottom.width;
278
r.extents.y2 = r.extents.y1 + w->struts ()->bottom.height;
280
if (r.extents.x1 < extents.x1)
281
r.extents.x1 = extents.x1;
282
if (r.extents.x2 > extents.x2)
283
r.extents.x2 = extents.x2;
284
if (r.extents.y1 < extents.y1)
285
r.extents.y1 = extents.y1;
286
if (r.extents.y2 > extents.y2)
287
r.extents.y2 = extents.y2;
289
if (r.extents.x1 < r.extents.x2 && r.extents.y1 < r.extents.y2)
291
if (r.extents.y1 >= workArea.bottom ())
292
XSubtractRegion (region, &r, region);
302
moveHandleMotionEvent (CompScreen *s,
317
wX = w->geometry ().x ();
318
wY = w->geometry ().y ();
319
wWidth = w->geometry ().width () +
320
w->geometry ().border () * 2;
321
wHeight = w->geometry ().height () +
322
w->geometry ().border () * 2;
324
ms->x += xRoot - lastPointerX;
325
ms->y += yRoot - lastPointerY;
327
if (w->type () & CompWindowTypeFullscreenMask)
339
workArea = s->getWorkareaForOutput (w->outputDevice ());
341
if (ms->yConstrained)
344
ms->region = moveGetYConstrainRegion (s);
346
/* make sure that the top border extents or the top row of
347
pixels are within what is currently our valid screen
351
int x, y, width, height;
354
x = wX + dx - w->border ().left;
355
y = wY + dy - w->border ().top;
356
width = wWidth + w->border ().left + w->border ().right;
357
height = w->border ().top ? w->border ().top : 1;
359
status = XRectInRegion (ms->region, x, y,
360
(unsigned int) width,
361
(unsigned int) height);
363
/* only constrain movement if previous position was valid */
364
if (ms->status == RectangleIn)
366
int xStatus = status;
368
while (dx && xStatus != RectangleIn)
370
xStatus = XRectInRegion (ms->region,
372
(unsigned int) width,
373
(unsigned int) height);
375
if (xStatus != RectangleIn)
376
dx += (dx < 0) ? 1 : -1;
378
x = wX + dx - w->border ().left;
381
while (dy && status != RectangleIn)
383
status = XRectInRegion (ms->region,
385
(unsigned int) width,
386
(unsigned int) height);
388
if (status != RectangleIn)
389
dy += (dy < 0) ? 1 : -1;
391
y = wY + dy - w->border ().top;
401
if (ms->optionGetSnapoffMaximized ())
403
if (w->state () & CompWindowStateMaximizedVertMask)
405
if (abs (yRoot - workArea.y () - ms->snapOffY) >= SNAP_OFF)
407
if (!s->otherGrabExist ("move", NULL))
409
int width = w->serverGeometry ().width ();
411
w->saveMask () |= CWX | CWY;
413
if (w->saveMask ()& CWWidth)
414
width = w->saveWc ().width;
416
w->saveWc ().x = xRoot - (width >> 1);
417
w->saveWc ().y = yRoot + (w->border ().top >> 1);
423
ms->snapOffY = ms->snapBackY;
429
else if (ms->origState & CompWindowStateMaximizedVertMask)
431
if (abs (yRoot - workArea.y () - ms->snapBackY) < SNAP_BACK)
433
if (!s->otherGrabExist ("move", NULL))
437
/* update server position before maximizing
438
window again so that it is maximized on
442
w->maximize (ms->origState);
444
wy = workArea.y () + (w->border ().top >> 1);
445
wy += w->sizeHints ().height_inc >> 1;
447
s->warpPointer (0, wy - pointerY);
455
if (w->state () & CompWindowStateMaximizedVertMask)
457
min = workArea.y () + w->border ().top;
458
max = workArea.bottom () - w->border ().bottom - wHeight;
462
else if (wY + dy > max)
466
if (w->state () & CompWindowStateMaximizedHorzMask)
468
if (wX > (int) s->width () ||
469
wX + w->size ().width () < 0)
475
min = workArea.x () + w->border ().left;
476
max = workArea.right () - w->border ().right - wWidth;
480
else if (wX + dx > max)
487
w->move (wX + dx - w->geometry ().x (),
488
wY + dy - w->geometry ().y (), false);
490
if (ms->optionGetLazyPositioning () &&
491
ms->hasCompositing &&
492
!MoveWindow::get (ms->w)->mLocked)
494
/* FIXME: This form of lazy positioning is broken and should
495
be replaced asap. Current code exists just to avoid a
496
major performance regression in the 0.5.2 release. */
497
w->serverGeometry ().setX (w->geometry ().x ());
498
w->serverGeometry ().setY (w->geometry ().y ());
511
/* FIXME: This is a hack to prevent a race condition
512
* when core is processing ConfigureNotify events. It
513
* MUST be removed after 0.9.6 when we can break ABI
514
* and do lazy positioning correctly ! */
517
MoveScreen::handleCompizEvent (const char *plugin, const char *event, CompOption::Vector &options)
521
if (std::string ("core") == std::string (plugin))
523
if (std::string ("lock_position") == std::string (event))
525
Window xid = CompOption::getIntOptionNamed (options, "window", 0);
526
int lock = CompOption::getIntOptionNamed (options, "active", 0);
528
if (xid == ROOTPARENT (w))
529
MoveWindow::get (w)->mLocked = lock ? true : false;
534
screen->handleCompizEvent (plugin, event, options);
538
MoveScreen::handleEvent (XEvent *event)
540
switch (event->type) {
543
if (event->xbutton.root == screen->root ())
547
if (releaseButton == -1 ||
548
releaseButton == (int) event->xbutton.button)
550
moveTerminate (&optionGetInitiateButton (),
551
CompAction::StateTermButton,
558
if (event->xkey.root == screen->root ())
564
for (i = 0; i < NUM_KEYS; i++)
566
if (event->xkey.keycode == key[i])
568
XWarpPointer (screen->dpy (), None, None,
570
mKeys[i].dx * KEY_MOVE_INC,
571
mKeys[i].dy * KEY_MOVE_INC);
579
if (event->xmotion.root == screen->root ())
580
moveHandleMotionEvent (screen, pointerX, pointerY);
584
if (event->xcrossing.root == screen->root ())
585
moveHandleMotionEvent (screen, pointerX, pointerY);
588
if (event->xclient.message_type == Atoms::wmMoveResize)
591
unsigned long type = (unsigned long) event->xclient.data.l[2];
593
MOVE_SCREEN (screen);
595
if (type == WmMoveResizeMove ||
596
type == WmMoveResizeMoveKeyboard)
598
w = screen->findWindow (event->xclient.window);
601
CompOption::Vector o;
603
o.push_back (CompOption ("window", CompOption::TypeInt));
604
o[0].value ().set ((int) event->xclient.window);
606
o.push_back (CompOption ("external",
607
CompOption::TypeBool));
608
o[1].value ().set (true);
610
if (event->xclient.data.l[2] == WmMoveResizeMoveKeyboard)
612
moveInitiate (&optionGetInitiateKey (),
613
CompAction::StateInitKey, o);
618
/* TODO: not only button 1 */
619
if (pointerMods & Button1Mask)
621
o.push_back (CompOption ("modifiers", CompOption::TypeInt));
622
o[2].value ().set ((int) pointerMods);
624
o.push_back (CompOption ("x", CompOption::TypeInt));
625
o[3].value ().set ((int) event->xclient.data.l[0]);
627
o.push_back (CompOption ("y", CompOption::TypeInt));
628
o[4].value ().set ((int) event->xclient.data.l[1]);
630
o.push_back (CompOption ("button", CompOption::TypeInt));
631
o[5].value ().set ((int) (event->xclient.data.l[3] ?
632
event->xclient.data.l[3] : -1));
634
moveInitiate (&optionGetInitiateButton (),
635
CompAction::StateInitButton, o);
637
moveHandleMotionEvent (screen, pointerX, pointerY);
642
else if (ms->w && type == WmMoveResizeCancel)
644
if (ms->w->id () == event->xclient.window)
646
moveTerminate (&optionGetInitiateButton (),
647
CompAction::StateCancel, noOptions);
648
moveTerminate (&optionGetInitiateKey (),
649
CompAction::StateCancel, noOptions);
656
if (w && w->id () == event->xdestroywindow.window)
658
moveTerminate (&optionGetInitiateButton (), 0, noOptions);
659
moveTerminate (&optionGetInitiateKey (), 0, noOptions);
663
if (w && w->id () == event->xunmap.window)
665
moveTerminate (&optionGetInitiateButton (), 0, noOptions);
666
moveTerminate (&optionGetInitiateKey (), 0, noOptions);
672
screen->handleEvent (event);
676
MoveWindow::glPaint (const GLWindowPaintAttrib &attrib,
677
const GLMatrix &transform,
678
const CompRegion ®ion,
681
GLWindowPaintAttrib sAttrib = attrib;
684
MOVE_SCREEN (screen);
688
if (ms->w == window && ms->moveOpacity != OPAQUE)
690
/* modify opacity of windows that are not active */
691
sAttrib.opacity = (sAttrib.opacity * ms->moveOpacity) >> 16;
695
status = gWindow->glPaint (sAttrib, transform, region, mask);
701
MoveScreen::updateOpacity ()
703
moveOpacity = (optionGetOpacity () * OPAQUE) / 100;
706
MoveScreen::MoveScreen (CompScreen *screen) :
707
PluginClassHandler<MoveScreen,CompScreen> (screen),
710
status (RectangleOut),
713
hasCompositing (false),
719
for (unsigned int i = 0; i < NUM_KEYS; i++)
720
key[i] = XKeysymToKeycode (screen->dpy (),
721
XStringToKeysym (mKeys[i].name));
723
moveCursor = XCreateFontCursor (screen->dpy (), XC_fleur);
724
if (CompositeScreen::get (screen))
726
CompositeScreen::get (screen)->compositingActive ();
728
optionSetOpacityNotify (boost::bind (&MoveScreen::updateOpacity, this));
730
optionSetInitiateButtonInitiate (moveInitiate);
731
optionSetInitiateButtonTerminate (moveTerminate);
733
optionSetInitiateKeyInitiate (moveInitiate);
734
optionSetInitiateKeyTerminate (moveTerminate);
736
ScreenInterface::setHandler (screen);
739
MoveScreen::~MoveScreen ()
742
XDestroyRegion (region);
745
XFreeCursor (screen->dpy (), moveCursor);
749
MovePluginVTable::init ()
751
if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))