2
* Compiz Fusion Grid plugin
4
* Copyright (c) 2008 Stephen Kennedy <suasol@gmail.com>
5
* Copyright (c) 2010 Scott Moreau <oreaus@gmail.com>
7
* This program is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU General Public License
9
* as published by the Free Software Foundation; either version 2
10
* of the License, or (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
19
* Plugin to act like winsplit revolution (http://www.winsplit-revolution.com/)
20
* use <Control><Alt>NUMPAD_KEY to move and tile your windows.
22
* Press the tiling keys several times to cycle through some tiling options.
27
static const GridProps gridProps[] =
47
GridScreen::slotToRect (CompWindow *w,
50
return CompRect (slot.x () + w->border ().left,
51
slot.y () + w->border ().top,
52
slot.width () - (w->border ().left + w->border ().right),
53
slot.height () - (w->border ().top + w->border ().bottom));
57
GridScreen::constrainSize (CompWindow *w,
63
result = slotToRect (w, slot);
65
if (w->constrainNewWindowSize (result.width (), result.height (), &cw, &ch))
67
/* constrained size may put window offscreen, adjust for that case */
68
int dx = result.x () + cw - workarea.right () + w->border ().right;
69
int dy = result.y () + ch - workarea.bottom () + w->border ().bottom;
72
result.setX (result.x () - dx);
74
result.setY (result.y () - dy);
77
result.setHeight (ch);
84
GridScreen::getPaintRectangle (CompRect &cRect)
86
if (edgeToGridType () != GridUnknown && optionGetDrawIndicator ())
89
cRect.setGeometry (0, 0, 0, 0);
93
applyProgress (int a, int b, float progress)
96
b - (ABS (a - b) * progress) :
97
b + (ABS (a - b) * progress);
101
GridScreen::setCurrentRect (Animation &anim)
103
anim.currentRect.setLeft (applyProgress (anim.targetRect.x1 (),
106
anim.currentRect.setRight (applyProgress (anim.targetRect.x2 (),
109
anim.currentRect.setTop (applyProgress (anim.targetRect.y1 (),
112
anim.currentRect.setBottom (applyProgress (anim.targetRect.y2 (),
118
GridScreen::initiateCommon (CompAction *action,
119
CompAction::State state,
120
CompOption::Vector &option,
127
xid = CompOption::getIntOptionNamed (option, "window");
128
cw = screen->findWindow (xid);
130
if (where == GridUnknown || screen->otherGrabExist ("move", NULL))
139
if (gw->lastTarget != where)
142
props = gridProps[where];
144
/* get current available area */
145
if (cw == mGrabWindow)
146
workarea = screen->getWorkareaForOutput
147
(screen->outputDeviceForPoint (pointerX, pointerY));
150
workarea = screen->getWorkareaForOutput (cw->outputDevice ());
152
if (props.numCellsX == 1)
155
if (!gw->isGridResized)
156
/* Store size not including borders when using a keybinding */
157
gw->originalSize = slotToRect(cw, cw->serverBorderRect ());
160
if ((cw->state () & MAXIMIZE_STATE) &&
161
(resize || optionGetSnapoffMaximized ()))
163
/* maximized state interferes with us, clear it */
167
if (where == GridMaximize && resize)
169
/* move the window to the correct output */
170
if (cw == mGrabWindow)
172
xwc.x = workarea.x () + 50;
173
xwc.y = workarea.y () + 50;
174
xwc.width = workarea.width ();
175
xwc.height = workarea.height ();
176
cw->configureXWindow (CWX | CWY, &xwc);
178
cw->maximize (MAXIMIZE_STATE);
179
gw->isGridResized = true;
180
gw->isGridMaximized = true;
181
for (unsigned int i = 0; i < animations.size (); i++)
182
animations.at (i).fadingOut = true;
187
* xxxSlot include decorations (it's the screen area occupied)
188
* xxxRect are undecorated (it's the constrained position
192
/* slice and dice to get desired slot - including decorations */
193
desiredSlot.setY (workarea.y () + props.gravityDown *
194
(workarea.height () / props.numCellsY));
195
desiredSlot.setHeight (workarea.height () / props.numCellsY);
196
desiredSlot.setX (workarea.x () + props.gravityRight *
197
(workarea.width () / props.numCellsX));
198
desiredSlot.setWidth (workarea.width () / props.numCellsX);
200
/* Adjust for constraints and decorations */
201
desiredRect = constrainSize (cw, desiredSlot);
202
/* Get current rect not including decorations */
203
currentRect.setGeometry (cw->serverX (), cw->serverY (),
205
cw->serverHeight ());
207
if (desiredRect.y () == currentRect.y () &&
208
desiredRect.height () == currentRect.height () &&
209
where != GridMaximize && gw->lastTarget == where)
211
int slotWidth25 = workarea.width () / 4;
212
int slotWidth33 = (workarea.width () / 3) + cw->border ().left;
213
int slotWidth17 = slotWidth33 - slotWidth25;
214
int slotWidth66 = workarea.width () - slotWidth33;
215
int slotWidth75 = workarea.width () - slotWidth25;
217
if (props.numCellsX == 2) /* keys (1, 4, 7, 3, 6, 9) */
219
if ((currentRect.width () == desiredRect.width () &&
220
currentRect.x () == desiredRect.x ()) ||
221
(gw->resizeCount < 1) || (gw->resizeCount > 5))
224
/* tricky, have to allow for window constraints when
225
* computing what the 33% and 66% offsets would be
227
switch (gw->resizeCount)
230
desiredSlot.setWidth (slotWidth66);
231
desiredSlot.setX (workarea.x () +
232
props.gravityRight * slotWidth33);
239
desiredSlot.setWidth (slotWidth33);
240
desiredSlot.setX (workarea.x () +
241
props.gravityRight * slotWidth66);
245
desiredSlot.setWidth (slotWidth25);
246
desiredSlot.setX (workarea.x () +
247
props.gravityRight * slotWidth75);
251
desiredSlot.setWidth (slotWidth75);
252
desiredSlot.setX (workarea.x () +
253
props.gravityRight * slotWidth25);
260
else /* keys (2, 5, 8) */
263
if ((currentRect.width () == desiredRect.width () &&
264
currentRect.x () == desiredRect.x ()) ||
265
(gw->resizeCount < 1) || (gw->resizeCount > 5))
268
switch (gw->resizeCount)
271
desiredSlot.setWidth (workarea.width () -
273
desiredSlot.setX (workarea.x () + slotWidth17);
277
desiredSlot.setWidth ((slotWidth25 * 2) +
279
desiredSlot.setX (workarea.x () +
280
(slotWidth25 - slotWidth17));
284
desiredSlot.setWidth ((slotWidth25 * 2));
285
desiredSlot.setX (workarea.x () + slotWidth25);
289
desiredSlot.setWidth (slotWidth33 -
290
(cw->border ().left + cw->border ().right));
291
desiredSlot.setX (workarea.x () + slotWidth33);
302
if (gw->resizeCount == 6)
305
desiredRect = constrainSize (cw, desiredSlot);
308
xwc.x = desiredRect.x ();
309
xwc.y = desiredRect.y ();
310
xwc.width = desiredRect.width ();
311
xwc.height = desiredRect.height ();
313
/* Store a copy of xwc since configureXWindow changes it's values */
314
XWindowChanges wc = xwc;
317
cw->sendSyncRequest ();
319
/* TODO: animate move+resize */
322
cw->configureXWindow (CWX | CWY | CWWidth | CWHeight, &xwc);
323
gw->isGridResized = true;
324
gw->isGridMaximized = false;
325
for (unsigned int i = 0; i < animations.size (); i++)
326
animations.at (i).fadingOut = true;
329
/* This centers a window if it could not be resized to the desired
330
* width. Without this, it can look buggy when desired width is
331
* beyond the minimum or maximum width of the window.
335
if ((cw->serverBorderRect ().width () >
336
desiredSlot.width ()) ||
337
cw->serverBorderRect ().width () <
338
desiredSlot.width ())
340
wc.x = (workarea.width () >> 1) -
341
((cw->serverBorderRect ().width () >> 1) -
343
cw->configureXWindow (CWX, &wc);
349
gw->lastTarget = where;
356
GridScreen::glPaintRectangle (const GLScreenPaintAttrib &sAttrib,
357
const GLMatrix &transform,
361
GLMatrix sTransform (transform);
362
std::vector<Animation>::iterator iter;
364
getPaintRectangle (rect);
366
for (unsigned int i = 0; i < animations.size (); i++)
367
setCurrentRect (animations.at (i));
371
sTransform.toScreenSpace (output, -DEFAULT_Z_CAMERA);
373
glLoadMatrixf (sTransform.getMatrix ());
375
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
378
for (iter = animations.begin (); iter != animations.end () && animating; iter++)
381
Animation& anim = *iter;
382
float alpha = (float) optionGetFillColorAlpha () / 65535.0f * anim.opacity;
385
glColor4f (((float) optionGetFillColorRed () / 65535.0f) * alpha,
386
((float) optionGetFillColorGreen () / 65535.0f) * alpha,
387
((float) optionGetFillColorBlue () / 65535.0f) * alpha,
391
glRecti (anim.currentRect.x1 (), anim.currentRect.y2 (),
392
anim.currentRect.x2 (), anim.currentRect.y1 ());
394
/* Set outline rect smaller to avoid damage issues */
395
anim.currentRect.setGeometry (anim.currentRect.x () + 1,
396
anim.currentRect.y () + 1,
397
anim.currentRect.width () - 2,
398
anim.currentRect.height () - 2);
401
color = optionGetOutlineColor ();
404
glColor4f (((float) optionGetOutlineColorRed () / 65535.0f) * alpha,
405
((float) optionGetOutlineColorGreen () / 65535.0f) * alpha,
406
((float) optionGetOutlineColorBlue () / 65535.0f) * alpha,
407
((float) optionGetOutlineColorAlpha () / 65535.0f) * anim.opacity);
409
glColor4us (color[0], color[1], color[2], anim.opacity * color[3]);
413
glBegin (GL_LINE_LOOP);
414
glVertex2i (anim.currentRect.x1 (), anim.currentRect.y1 ());
415
glVertex2i (anim.currentRect.x2 (), anim.currentRect.y1 ());
416
glVertex2i (anim.currentRect.x2 (), anim.currentRect.y2 ());
417
glVertex2i (anim.currentRect.x1 (), anim.currentRect.y2 ());
424
glColor4usv (optionGetFillColor ());
425
glRecti (rect.x1 (), rect.y2 (), rect.x2 (), rect.y1 ());
427
/* Set outline rect smaller to avoid damage issues */
428
rect.setGeometry (rect.x () + 1, rect.y () + 1,
429
rect.width () - 2, rect.height () - 2);
432
glColor4usv (optionGetOutlineColor ());
434
glBegin (GL_LINE_LOOP);
435
glVertex2i (rect.x1 (), rect.y1 ());
436
glVertex2i (rect.x2 (), rect.y1 ());
437
glVertex2i (rect.x2 (), rect.y2 ());
438
glVertex2i (rect.x1 (), rect.y2 ());
443
glColor4usv (defaultColor);
444
glDisable (GL_BLEND);
445
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
450
GridScreen::glPaintOutput (const GLScreenPaintAttrib &attrib,
451
const GLMatrix &matrix,
452
const CompRegion ®ion,
458
status = glScreen->glPaintOutput (attrib, matrix, region, output, mask);
460
glPaintRectangle (attrib, matrix, output);
466
GridScreen::edgeToGridType ()
472
ret = (GridType) optionGetLeftEdgeAction ();
475
ret = (GridType) optionGetRightEdgeAction ();
478
ret = (GridType) optionGetTopEdgeAction ();
481
ret = (GridType) optionGetBottomEdgeAction ();
484
ret = (GridType) optionGetTopLeftCornerAction ();
487
ret = (GridType) optionGetTopRightCornerAction ();
490
ret = (GridType) optionGetBottomLeftCornerAction ();
493
ret = (GridType) optionGetBottomRightCornerAction ();
505
GridScreen::handleEvent (XEvent *event)
509
screen->handleEvent (event);
511
if (event->type != MotionNotify || !mGrabWindow)
514
out = screen->outputDevs ().at (
515
screen->outputDeviceForPoint (CompPoint (pointerX, pointerY)));
517
/* Detect corners first */
519
if (pointerY > (out.y () + out.height () - optionGetBottomEdgeThreshold()) &&
520
pointerX < out.x () + optionGetLeftEdgeThreshold())
523
else if (pointerY > (out.y () + out.height () - optionGetBottomEdgeThreshold()) &&
524
pointerX > (out.x () + out.width () - optionGetRightEdgeThreshold()))
527
else if (pointerY < optionGetTopEdgeThreshold() &&
528
pointerX < optionGetLeftEdgeThreshold())
531
else if (pointerY < out.y () + optionGetTopEdgeThreshold() &&
532
pointerX > (out.x () + out.width () - optionGetRightEdgeThreshold()))
535
else if (pointerX < out.x () + optionGetLeftEdgeThreshold())
538
else if (pointerX > (out.x () + out.width () - optionGetRightEdgeThreshold()))
541
else if (pointerY < out.y () + optionGetTopEdgeThreshold())
544
else if (pointerY > (out.y () + out.height () - optionGetBottomEdgeThreshold()))
550
/* Detect when cursor enters another output */
552
currentWorkarea = screen->getWorkareaForOutput
553
(screen->outputDeviceForPoint (pointerX, pointerY));
554
if (lastWorkarea != currentWorkarea)
556
lastWorkarea = currentWorkarea;
559
cScreen->damageRegion (desiredSlot);
561
initiateCommon (0, 0, o, edgeToGridType (), false);
564
cScreen->damageRegion (desiredSlot);
567
/* Detect edge region change */
569
if (lastEdge != edge)
571
lastSlot = desiredSlot;
574
desiredSlot.setGeometry (0, 0, 0, 0);
577
cScreen->damageRegion (desiredSlot);
579
initiateCommon (0, 0, o, edgeToGridType (), false);
582
cScreen->damageRegion (desiredSlot);
584
if (lastSlot != desiredSlot)
586
if (animations.size ())
587
/* Begin fading previous animation instance */
588
animations.at (animations.size () - 1).fadingOut = true;
592
CompWindow *cw = screen->findWindow (screen->activeWindow ());
593
animations.push_back (Animation ());
594
int current = animations.size () - 1;
595
animations.at (current).fromRect = cw->serverBorderRect ();
596
animations.at (current).currentRect = cw->serverBorderRect ();
597
animations.at (current).timer = animations.at (current).duration;
598
animations.at (current).targetRect = desiredSlot;
600
if (lastEdge == NoEdge || !animating)
602
/* Cursor has entered edge region from non-edge region */
604
glScreen->glPaintOutputSetEnabled (this, true);
605
cScreen->preparePaintSetEnabled (this, true);
606
cScreen->donePaintSetEnabled (this, true);
614
GRID_WINDOW (screen->findWindow
615
(CompOption::getIntOptionNamed (o, "window")));
617
if ((gw->pointerBufDx > SNAPOFF_THRESHOLD ||
618
gw->pointerBufDy > SNAPOFF_THRESHOLD ||
619
gw->pointerBufDx < -SNAPOFF_THRESHOLD ||
620
gw->pointerBufDy < -SNAPOFF_THRESHOLD) &&
622
optionGetSnapbackWindows ())
623
restoreWindow (0, 0, o);
627
GridWindow::grabNotify (int x,
632
if (screen->grabExist ("move"))
634
gScreen->o.push_back (CompOption ("window", CompOption::TypeInt));
635
gScreen->o[0].value ().set ((int) window->id ());
637
screen->handleEventSetEnabled (gScreen, true);
638
gScreen->mGrabWindow = window;
639
pointerBufDx = pointerBufDy = 0;
641
if (!isGridResized && gScreen->optionGetSnapbackWindows ())
642
/* Store size not including borders when grabbing with cursor */
643
originalSize = gScreen->slotToRect(window,
644
window->serverBorderRect ());
647
if (screen->grabExist ("resize"))
649
isGridResized = false;
653
window->grabNotify (x, y, state, mask);
657
GridWindow::ungrabNotify ()
659
if (window == gScreen->mGrabWindow)
661
gScreen->initiateCommon
662
(0, 0, gScreen->o, gScreen->edgeToGridType (), true);
664
screen->handleEventSetEnabled (gScreen, false);
665
gScreen->mGrabWindow = NULL;
666
gScreen->cScreen->damageRegion (gScreen->desiredSlot);
669
gScreen->edge = NoEdge;
671
window->ungrabNotify ();
675
GridWindow::moveNotify (int dx, int dy, bool immediate)
677
window->moveNotify (dx, dy, immediate);
684
GridScreen::restoreWindow (CompAction *action,
685
CompAction::State state,
686
CompOption::Vector &option)
689
CompWindow *cw = screen->findWindow (screen->activeWindow ());
696
if (!gw->isGridResized)
699
if (gw->isGridMaximized & !(cw->state () & MAXIMIZE_STATE))
700
gw->isGridMaximized = false;
703
if (cw == mGrabWindow)
705
xwc.x = pointerX - (gw->originalSize.width () >> 1);
706
xwc.y = pointerY + (cw->border ().top >> 1);
710
xwc.x = gw->originalSize.x ();
711
xwc.y = gw->originalSize.y ();
713
xwc.width = gw->originalSize.width ();
714
xwc.height = gw->originalSize.height ();
716
cw->configureXWindow (CWX | CWY | CWWidth | CWHeight, &xwc);
717
gw->pointerBufDx = 0;
718
gw->pointerBufDy = 0;
720
gw->isGridResized = false;
727
GridScreen::snapbackOptionChanged (CompOption *option,
730
GRID_WINDOW (screen->findWindow
731
(CompOption::getIntOptionNamed (o, "window")));
732
gw->isGridResized = false;
733
gw->isGridMaximized = false;
738
GridScreen::preparePaint (int msSinceLastPaint)
740
std::vector<Animation>::iterator iter;
742
for (iter = animations.begin (); iter != animations.end (); iter++)
744
Animation& anim = *iter;
745
anim.timer -= msSinceLastPaint;
751
anim.opacity -= msSinceLastPaint * 0.002;
753
if (anim.opacity < 1.0f)
754
anim.opacity = anim.progress * anim.progress;
758
if (anim.opacity < 0)
761
anim.fadingOut = false;
762
anim.complete = true;
765
anim.progress = (anim.duration - anim.timer) / anim.duration;
768
cScreen->preparePaint (msSinceLastPaint);
772
GridScreen::donePaint ()
774
std::vector<Animation>::iterator iter;
776
for (iter = animations.begin (); iter != animations.end (); )
778
Animation& anim = *iter;
780
iter = animations.erase(iter);
785
if (animations.empty ())
787
cScreen->preparePaintSetEnabled (this, false);
788
cScreen->donePaintSetEnabled (this, false);
790
glScreen->glPaintOutputSetEnabled (this, false);
795
cScreen->damageScreen ();
797
cScreen->donePaint ();
800
Animation::Animation ()
803
fromRect = CompRect (0, 0, 0, 0);
804
targetRect = CompRect (0, 0, 0, 0);
805
currentRect = CompRect (0, 0, 0, 0);
814
GridScreen::GridScreen (CompScreen *screen) :
815
PluginClassHandler<GridScreen, CompScreen> (screen),
816
cScreen (CompositeScreen::get (screen)),
817
glScreen (GLScreen::get (screen)),
823
ScreenInterface::setHandler (screen, false);
824
CompositeScreenInterface::setHandler (cScreen, false);
825
GLScreenInterface::setHandler (glScreen, false);
827
edge = lastEdge = NoEdge;
828
currentWorkarea = lastWorkarea = screen->getWorkareaForOutput
829
(screen->outputDeviceForPoint (pointerX, pointerY));
833
#define GRIDSET(opt,where,resize) \
834
optionSet##opt##Initiate (boost::bind (&GridScreen::initiateCommon, this, \
835
_1, _2, _3, where, resize))
837
GRIDSET (PutCenterKey, GridCenter, true);
838
GRIDSET (PutLeftKey, GridLeft, true);
839
GRIDSET (PutRightKey, GridRight, true);
840
GRIDSET (PutTopKey, GridTop, true);
841
GRIDSET (PutBottomKey, GridBottom, true);
842
GRIDSET (PutTopleftKey, GridTopLeft, true);
843
GRIDSET (PutToprightKey, GridTopRight, true);
844
GRIDSET (PutBottomleftKey, GridBottomLeft, true);
845
GRIDSET (PutBottomrightKey, GridBottomRight, true);
846
GRIDSET (PutMaximizeKey, GridMaximize, true);
850
optionSetSnapbackWindowsNotify (boost::bind (&GridScreen::
851
snapbackOptionChanged, this, _1, _2));
853
optionSetPutRestoreKeyInitiate (boost::bind (&GridScreen::
854
restoreWindow, this, _1, _2, _3));
858
GridWindow::GridWindow (CompWindow *window) :
859
PluginClassHandler <GridWindow, CompWindow> (window),
861
gScreen (GridScreen::get (screen)),
862
isGridResized (false),
863
isGridMaximized (false),
867
lastTarget (GridUnknown)
869
WindowInterface::setHandler (window);
872
/* Initial plugin init function called. Checks to see if we are ABI
873
* compatible with core, otherwise unload */
876
GridPluginVTable::init ()
878
if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))