1 /*
2 *
3 * Compiz scale plugin addon plugin
4 *
5 * scaleaddon.cpp
6 *
7 * Copyright : (C) 2007 by Danny Baumann
8 * E-mail : maniac@opencompositing.org
9 *
10 * Organic scale mode taken from Beryl's scale.c, written by
11 * Copyright : (C) 2006 Diogo Ferreira
12 * E-mail : diogo@underdev.org
13 *
14 * Ported to Compiz 0.9 by:
15 * Copyright : (C) 2009 by Sam Spilsbury
16 * E-mail : smspillaz@gmail.com
17 *
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version 2
21 * of the License, or (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 *
29 */
30
31 #include "scaleaddon.h"
32 #include <iostream>
33
34 COMPIZ_PLUGIN_20090315 (scaleaddon, ScaleAddonPluginVTable);
35
36 bool textAvailable;
37
38 void
39 ScaleAddonWindow::renderTitle ()
40 {
41 CompText::Attrib attrib;
42 float scale;
43 int titleOpt;
44
45 ADDON_SCREEN (screen);
46
47 if (!textAvailable)
48 return;
49
50 text.clear ();
51
52 if (!sWindow->hasSlot ())
53 return;
54
55 titleOpt = as->optionGetWindowTitle ();
56
57 if (titleOpt == ScaleaddonOptions::WindowTitleNoDisplay)
58 return;
59
60 if (titleOpt == ScaleaddonOptions::WindowTitleHighlightedWindowOnly &&
61 as->highlightedWindow != window->id ())
62 {
63 return;
64 }
65
66 scale = sWindow->getSlot ().scale;
67 attrib.maxWidth = window->width () * scale;
68 attrib.maxHeight = window->height () * scale;
69
70 attrib.family = "Sans";
71 attrib.size = as->optionGetTitleSize ();
72 attrib.color[0] = as->optionGetFontColorRed ();
73 attrib.color[1] = as->optionGetFontColorGreen ();
74 attrib.color[2] = as->optionGetFontColorBlue ();
75 attrib.color[3] = as->optionGetFontColorAlpha ();
76
77 attrib.flags = CompText::WithBackground | CompText::Ellipsized;
78 if (as->optionGetTitleBold ())
79 attrib.flags |= CompText::StyleBold;
80
81 attrib.bgHMargin = as->optionGetBorderSize ();
82 attrib.bgVMargin = as->optionGetBorderSize ();
83 attrib.bgColor[0] = as->optionGetBackColorRed ();
84 attrib.bgColor[1] = as->optionGetBackColorGreen ();
85 attrib.bgColor[2] = as->optionGetBackColorBlue ();
86 attrib.bgColor[3] = as->optionGetBackColorAlpha ();
87
88 text.renderWindowTitle (window->id (),
89 as->sScreen->getType () == ScaleTypeAll,
90 attrib);
91 }
92
93 void
94 ScaleAddonWindow::drawTitle (const GLMatrix &transform)
95 {
96 float x, y, width, height;
97 ScalePosition pos = sWindow->getCurrentPosition ();
98 CompRect geom = window->borderRect ();
99
100 width = text.getWidth ();
101 height = text.getHeight ();
102
103 x = pos.x () + window->x () + geom.width () * pos.scale / 2 - width / 2;
104 y = pos.y () + window->y () + geom.height () * pos.scale / 2 - height / 2;
105
106 text.draw (transform, floor (x), floor (y), 1.0f);
107 }
108
109 void
110 ScaleAddonWindow::drawHighlight (const GLMatrix &transform)
111 {
112 GLint oldBlendSrc, oldBlendDst;
113 GLushort colorData[4];
114 GLfloat vertexData[12];
115 GLVertexBuffer *streamingBuffer = GLVertexBuffer::streamingBuffer ();
116 float x, y, width, height;
117 ScalePosition pos = sWindow->getCurrentPosition ();
118 CompRect geom = window->borderRect ();
119
120 ADDON_SCREEN (screen);
121
122 #ifdef USE_GLES
123 GLint oldBlendSrcAlpha, oldBlendDstAlpha;
124 #endif
125
126 if (rescaled)
127 return;
128
129 x = pos.x () + window->x () - (window->border ().left * pos.scale);
130 y = pos.y () + window->y () - (window->border ().top * pos.scale);
131 width = geom.width () * pos.scale;
132 height = geom.height () * pos.scale;
133
134 /* we use a poor replacement for roundf()
135 * (available in C99 only) here */
136 x = floor (x + 0.5f);
137 y = floor (y + 0.5f);
138
139 #ifdef USE_GLES
140 glGetIntegerv (GL_BLEND_SRC_RGB, &oldBlendSrc);
141 glGetIntegerv (GL_BLEND_DST_RGB, &oldBlendDst);
142 glGetIntegerv (GL_BLEND_SRC_ALPHA, &oldBlendSrcAlpha);
143 glGetIntegerv (GL_BLEND_DST_ALPHA, &oldBlendDstAlpha);
144 #else
145 GLboolean wasBlend = glIsEnabled (GL_BLEND);
146 glGetIntegerv (GL_BLEND_SRC, &oldBlendSrc);
147 glGetIntegerv (GL_BLEND_DST, &oldBlendDst);
148
149 if (!wasBlend)
150 glEnable (GL_BLEND);
151
152 #endif
153
154 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
155
156 streamingBuffer->begin (GL_TRIANGLE_STRIP);
157
158 colorData[0] = as->optionGetHighlightColorRed ();
159 colorData[1] = as->optionGetHighlightColorGreen ();
160 colorData[2] = as->optionGetHighlightColorBlue ();
161 colorData[3] = as->optionGetHighlightColorAlpha ();
162
163 streamingBuffer->addColors (1, colorData);
164
165 vertexData[0] = x;
166 vertexData[1] = y;
167 vertexData[2] = 0.0f;
168 vertexData[3] = x;
169 vertexData[4] = y + height;
170 vertexData[5] = 0.0f;
171 vertexData[6] = x + width;
172 vertexData[7] = y;
173 vertexData[8] = 0.0f;
174 vertexData[9] = x + width;
175 vertexData[10] = y + height;
176 vertexData[11] = 0.0f;
177
178 streamingBuffer->addVertices (4, vertexData);
179
180 streamingBuffer->end ();
181 streamingBuffer->render (transform);
182
183 #ifdef USE_GLES
184 glBlendFuncSeparate (oldBlendSrc, oldBlendDst,
185 oldBlendSrcAlpha, oldBlendDstAlpha);
186 #else
187 if (!wasBlend)
188 glDisable (GL_BLEND);
189 glBlendFunc (oldBlendSrc, oldBlendDst);
190 #endif
191 }
192
193 void
194 ScaleAddonScreen::checkWindowHighlight ()
195 {
196 if (highlightedWindow != lastHighlightedWindow)
197 {
198 CompWindow *w;
199
200 w = screen->findWindow (highlightedWindow);
201 if (w)
202 {
203 ADDON_WINDOW (w);
204 aw->renderTitle ();
205 aw->cWindow->addDamage ();
206 }
207
208 w = screen->findWindow (lastHighlightedWindow);
209 if (w)
210 {
211 ADDON_WINDOW (w);
212 aw->renderTitle ();
213 aw->cWindow->addDamage (w);
214 }
215
216 lastHighlightedWindow = highlightedWindow;
217 }
218 }
219
220 bool
221 ScaleAddonScreen::closeWindow (CompAction *action,
222 CompAction::State state,
223 CompOption::Vector options)
224 {
225 CompWindow *w;
226
227 if (!sScreen->hasGrab ())
228 return false;
229
230 w = screen->findWindow (highlightedWindow);
231 if (w)
232 w->close (screen->getCurrentTime ());
233
234 return true;
235 }
236
237 bool
238 ScaleAddonScreen::pullWindow (CompAction *action,
239 CompAction::State state,
240 CompOption::Vector options)
241 {
242 CompWindow *w;
243
Condition "!this->sScreen->hasGrab()", taking false branch
244 if (!sScreen->hasGrab ())
245 return false;
246
247 w = screen->findWindow (highlightedWindow);
Condition "w", taking true branch
248 if (w)
249 {
250 int x, y, xOffset, yOffset;
251 CompPoint vp;
252
253 vp = w->defaultViewport ();
254
255 xOffset = (screen->vp ().x () - vp.x ()) * screen->width ();
256 yOffset = (screen->vp ().y () - vp.y ()) * screen->height ();
257
258 x = w->x () + xOffset;
259 y = w->y () + yOffset;
260
Condition "this->optionGetConstrainPullToScreen()", taking false branch
261 if (optionGetConstrainPullToScreen ())
262 {
263 CompRect workArea, extents;
264
265 workArea = screen->outputDevs ()[w->outputDevice ()].workArea ();
266 extents = w->borderRect ();
267
268 extents.setX (extents.x () + xOffset);
269 extents.setY (extents.y () + yOffset);
270
271 if (extents.x1 () < workArea.x1 ())
272 x += workArea.x1 () - extents.x1 ();
273 else if (extents.x2 () > workArea.x2 ())
274 x += workArea.x2 () - extents.x2 ();
275
276 if (extents.y1 () < workArea.y1 ())
277 y += workArea.y1 () - extents.y1 ();
278 else if (extents.y2 () > workArea.y2 ())
279 y += workArea.y2 () - extents.y2 ();
End of if statement
280 }
281
Condition "x != w->x()", taking true branch
282 if (x != w->x () || y != w->y ())
283 {
284 ScalePosition pos, oldPos;
285 ADDON_WINDOW (w);
286
287 oldPos = aw->sWindow->getCurrentPosition ();
288
289 w->moveToViewportPosition (x, y, true);
290
291 /* Select this window when ending scale */
292 aw->sWindow->scaleSelectWindow ();
293
294 /* stop scaled window dissapearing */
295 pos.setX (oldPos.x () - xOffset);
296 pos.setY (oldPos.y () - yOffset);
297
Condition "this->optionGetExitAfterPull()", taking true branch
298 if (optionGetExitAfterPull ())
299 {
300 CompAction *action;
301 CompOption::Vector o;
302 CompOption *opt;
303
304 o.push_back (CompOption ("root", CompOption::TypeInt));
305 o[0].value ().set ((int) screen->root ());
306
CID 12472 - NULL_RETURNS
Function "CompOption::findOption(CompOption::Vector &, CompString, unsigned int *)" returns null (checked 81 out of 84 times).
Assigning: "opt" = null return value from "CompOption::findOption(CompOption::Vector &, CompString, unsigned int *)".
307 opt = CompOption::findOption (sScreen->getOptions (),
308 "initiate_key", 0);
CID 12472 - NULL_RETURNS
Dereferencing a pointer that might be null "opt" when calling "CompOption::value()".
309 action = &opt->value ().action ();
310
311 if (action->terminate ())
312 action->terminate () (action, 0, o);
313 }
314 else
315 {
316 ScaleSlot slot = aw->sWindow->getSlot ();
317
318 /* provide a simple animation */
319 aw->cWindow->addDamage ();
320
321 pos.setX (oldPos.x () - slot.width () / 20);
322 pos.setY (oldPos.y () - slot.height () / 20);
323 pos.scale = oldPos.scale * 1.1f;
324
325 aw->sWindow->setCurrentPosition (pos);
326
327 aw->cWindow->addDamage ();
328 }
329 }
330 }
331
332 return true;
333 }
334
335 bool
336 ScaleAddonScreen::zoomWindow (CompAction *action,
337 CompAction::State state,
338 CompOption::Vector options)
339 {
340 CompWindow *w;
341
342 if (!sScreen->hasGrab ())
343 return false;
344
345 w = screen->findWindow (highlightedWindow);
346 if (w)
347 {
348 CompRect output;
349 int head;
350
351 ADDON_WINDOW (w);
352
353 if (!aw->sWindow->hasSlot ())
354 return false;
355
356 head = screen->outputDeviceForPoint (aw->sWindow->getSlot ().pos ());
357 output = screen->outputDevs ()[head];
358
359 /* damage old rect */
360 aw->cWindow->addDamage ();
361
362 if (!aw->rescaled)
363 {
364 ScaleSlot slot = aw->sWindow->getSlot ();
365 int x1, x2, y1, y2;
366 CompRect geom = w->borderRect ();
367
368 aw->oldAbove = w->next;
369 w->raise ();
370
371 /* backup old values */
372 aw->origSlot = slot;
373 aw->rescaled = true;
374
375 x1 = output.centerX () - geom.width () / 2 + w->border ().left;
376 y1 = output.centerY () - geom.height () / 2 + w->border ().top;
377 x2 = slot.x () + geom.width ();
378 y2 = slot.y () + geom.height ();
379
380 slot.scale = 1.0f;
381 slot.setGeometry (x1, y1, x2 - x1, y2 - y1);
382
383 aw->sWindow->setSlot (slot);
384 }
385 else
386 {
387 if (aw->oldAbove)
388 w->restackBelow (aw->oldAbove);
389
390 aw->rescaled = false;
391 aw->sWindow->setSlot (aw->origSlot);
392 }
393
394 /* slot size may have changed, so
395 * update window title */
396 aw->renderTitle ();
397
398 aw->cWindow->addDamage ();
399 }
400
401 return true;
402 }
403
404 void
405 ScaleAddonScreen::handleEvent (XEvent *event)
406 {
407 screen->handleEvent (event);
408
409 switch (event->type)
410 {
411 case PropertyNotify:
412 if (event->xproperty.atom == XA_WM_NAME && sScreen->hasGrab ())
413 {
414 CompWindow *w;
415
416 w = screen->findWindow (event->xproperty.window);
417 if (w)
418 {
419 ADDON_WINDOW (w);
420 aw->renderTitle ();
421 aw->cWindow->addDamage ();
422 }
423 }
424 break;
425 case MotionNotify:
426 if (sScreen->hasGrab ())
427 {
428 highlightedWindow = sScreen->getHoveredWindow ();
429 checkWindowHighlight ();
430 }
431 break;
432 default:
433 break;
434 }
435 }
436
437 void
438 ScaleAddonWindow::scalePaintDecoration (const GLWindowPaintAttrib &attrib,
439 const GLMatrix &transform,
440 const CompRegion ®ion,
441 unsigned int mask)
442 {
443 ScaleScreen::State state;
444
445 ADDON_SCREEN (screen);
446
447 state = as->sScreen->getState ();
448 sWindow->scalePaintDecoration (attrib, transform, region, mask);
449
450 if (state == ScaleScreen::Wait || state == ScaleScreen::Out)
451 {
452 if (as->optionGetWindowHighlight ())
453 {
454 if (window->id () == as->highlightedWindow)
455 drawHighlight (transform);
456 }
457
458 if (textAvailable)
459 drawTitle (transform);
460 }
461 }
462
463 void
464 ScaleAddonWindow::scaleSelectWindow ()
465 {
466 ADDON_SCREEN (screen);
467
468 as->highlightedWindow = window->id ();
469 as->checkWindowHighlight ();
470
471 sWindow->scaleSelectWindow ();
472 }
473
474 void
475 ScaleAddonScreen::donePaint ()
476 {
477 ScaleScreen::State state = sScreen->getState ();
478
479 if (state != ScaleScreen::Idle && lastState == ScaleScreen::Idle)
480 {
481 foreach (CompWindow *w, screen->windows ())
482 ScaleAddonWindow::get (w)->renderTitle ();
483 }
484 else if (state == ScaleScreen::Idle && lastState != ScaleScreen::Idle)
485 {
486 foreach (CompWindow *w, screen->windows ())
487 ScaleAddonWindow::get (w)->text.clear ();
488 }
489
490 if (state == ScaleScreen::Out && lastState != ScaleScreen::Out)
491 {
492 lastHighlightedWindow = None;
493 checkWindowHighlight ();
494 }
495
496 lastState = state;
497
498 cScreen->donePaint ();
499 }
500
501 void
502 ScaleAddonScreen::handleCompizEvent (const char *pluginName,
503 const char *eventName,
504 CompOption::Vector &options)
505 {
506 screen->handleCompizEvent (pluginName, eventName, options);
507
508 if ((strcmp (pluginName, "scale") == 0) &&
509 (strcmp (eventName, "activate") == 0))
510 {
511 bool activated =
512 CompOption::getBoolOptionNamed (options, "active", false);
513
514 if (activated)
515 {
516 screen->addAction (&optionGetCloseKey ());
517 screen->addAction (&optionGetZoomKey ());
518 screen->addAction (&optionGetPullKey ());
519 screen->addAction (&optionGetCloseButton ());
520 screen->addAction (&optionGetZoomButton ());
521 screen->addAction (&optionGetPullButton ());
522
523 /* TODO: or better
524 ad->highlightedWindow = sd->selectedWindow;
525 here? do we want to show up the highlight without
526 mouse move initially? */
527
528 highlightedWindow = None;
529 lastHighlightedWindow = None;
530 checkWindowHighlight ();
531 }
532 else
533 {
534 foreach (CompWindow *w, screen->windows ())
535 {
536 ADDON_WINDOW (w);
537 aw->rescaled = false;
538 }
539
540 screen->removeAction (&optionGetCloseKey ());
541 screen->removeAction (&optionGetZoomKey ());
542 screen->removeAction (&optionGetPullKey ());
543 screen->removeAction (&optionGetCloseButton ());
544 screen->removeAction (&optionGetZoomButton ());
545 screen->removeAction (&optionGetPullButton ());
546 }
547 }
548 }
549
550 /**
551 * experimental organic layout method
552 * inspired by smallwindows (smallwindows.sf.net) by Jens Egeblad
553 * FIXME: broken.
554 * */
555 #if 0
556 static const double ORGANIC_STEP = 0.05f;
557 static int
558 organicCompareWindows (const void *elem1,
559 const void *elem2)
560 {
561 CompWindow *w1 = *((CompWindow **) elem1);
562 CompWindow *w2 = *((CompWindow **) elem2);
563
564 return (WIN_X (w1) + WIN_Y (w1)) - (WIN_X (w2) + WIN_Y (w2));
565 }
566
567 static double
568 layoutOrganicCalculateOverlap (CompScreen *s,
569 int win,
570 int x,
571 int y)
572 {
573 int x1, y1, x2, y2;
574 int overlapX, overlapY;
575 int xMin, xMax, yMin, yMax;
576 double result = -0.01;
577
578 SCALE_SCREEN ();
579 ADDON_SCREEN ();
580
581 x1 = x;
582 y1 = y;
583 x2 = x1 + WIN_W (ss->windows[win]) * as->scale;
584 y2 = y1 + WIN_H (ss->windows[win]) * as->scale;
585
586 for (int i = 0; i < ss->nWindows; i++)
587 {
588 if (i == win)
589 continue;
590
591 overlapX = overlapY = 0;
592 xMax = MAX (ss->slots[i].x1, x1);
593 xMin = MIN (ss->slots[i].x1 + WIN_W (ss->windows[i]) * as->scale, x2);
594 if (xMax <= xMin)
595 overlapX = xMin - xMax;
596
597 yMax = MAX (ss->slots[i].y1, y1);
598 yMin = MIN (ss->slots[i].y1 + WIN_H (ss->windows[i]) * as->scale, y2);
599
600 if (yMax <= yMin)
601 overlapY = yMin - yMax;
602
603 result += (double)overlapX * overlapY;
604 }
605
606 return result;
607 }
608
609 static double
610 layoutOrganicFindBestHorizontalPosition (CompScreen *s,
611 int win,
612 int *bestX,
613 int areaWidth)
614 {
615 int i, y1, y2, w;
616 double bestOverlap = 1e31, overlap;
617
618 SCALE_SCREEN ();
619 ADDON_SCREEN ();
620
621 y1 = ss->slots[win].y1;
622 y2 = ss->slots[win].y1 + WIN_H (ss->windows[win]) * as->scale;
623
624 w = WIN_W (ss->windows[win]) * as->scale;
625 *bestX = ss->slots[win].x1;
626
627 for (i = 0; i < ss->nWindows; i++)
628 {
629 CompWindow *lw = ss->windows[i];
630 if (i == win)
631 continue;
632
633 if (ss->slots[i].y1 < y2 &&
634 ss->slots[i].y1 + WIN_H (lw) * as->scale > y1)
635 {
636 if (ss->slots[i].x1 - w >= 0)
637 {
638 double overlap;
639
640 overlap = layoutOrganicCalculateOverlap (s, win,
641 ss->slots[i].x1 - w,
642 y1);
643
644 if (overlap < bestOverlap)
645 {
646 *bestX = ss->slots[i].x1 - w;
647 bestOverlap = overlap;
648 }
649 }
650 if (WIN_W (lw) * as->scale + ss->slots[i].x1 + w < areaWidth)
651 {
652 double overlap;
653
654 overlap = layoutOrganicCalculateOverlap (s, win,
655 ss->slots[i].x1 +
656 WIN_W (lw) * as->scale,
657 y1);
658
659 if (overlap < bestOverlap)
660 {
661 *bestX = ss->slots[i].x1 + WIN_W (lw) * as->scale;
662 bestOverlap = overlap;
663 }
664 }
665 }
666 }
667
668 overlap = layoutOrganicCalculateOverlap (s, win, 0, y1);
669 if (overlap < bestOverlap)
670 {
671 *bestX = 0;
672 bestOverlap = overlap;
673 }
674
675 overlap = layoutOrganicCalculateOverlap (s, win, areaWidth - w, y1);
676 if (overlap < bestOverlap)
677 {
678 *bestX = areaWidth - w;
679 bestOverlap = overlap;
680 }
681
682 return bestOverlap;
683 }
684
685 static double
686 layoutOrganicFindBestVerticalPosition (CompScreen *s,
687 int win,
688 int *bestY,
689 int areaHeight)
690 {
691 int i, x1, x2, h;
692 double bestOverlap = 1e31, overlap;
693
694 SCALE_SCREEN ();
695 ADDON_SCREEN ();
696
697 x1 = ss->slots[win].x1;
698 x2 = ss->slots[win].x1 + WIN_W (ss->windows[win]) * as->scale;
699 h = WIN_H (ss->windows[win]) * as->scale;
700 *bestY = ss->slots[win].y1;
701
702 for (i = 0; i < ss->nWindows; i++)
703 {
704 CompWindow *w = ss->windows[i];
705
706 if (i == win)
707 continue;
708
709 if (ss->slots[i].x1 < x2 &&
710 ss->slots[i].x1 + WIN_W (w) * as->scale > x1)
711 {
712 if (ss->slots[i].y1 - h >= 0 && ss->slots[i].y1 < areaHeight)
713 {
714 double overlap;
715 overlap = layoutOrganicCalculateOverlap (s, win, x1,
716 ss->slots[i].y1 - h);
717 if (overlap < bestOverlap)
718 {
719 *bestY = ss->slots[i].y1 - h;
720 bestOverlap = overlap;
721 }
722 }
723 if (WIN_H (w) * as->scale + ss->slots[i].y1 > 0 &&
724 WIN_H (w) * as->scale + h + ss->slots[i].y1 < areaHeight)
725 {
726 double overlap;
727
728 overlap = layoutOrganicCalculateOverlap (s, win, x1,
729 WIN_H (w) * as->scale +
730 ss->slots[i].y1);
731
732 if (overlap < bestOverlap)
733 {
734 *bestY = ss->slots[i].y1 + WIN_H(w) * as->scale;
735 bestOverlap = overlap;
736 }
737 }
738 }
739 }
740
741 overlap = layoutOrganicCalculateOverlap (s, win, x1, 0);
742 if (overlap < bestOverlap)
743 {
744 *bestY = 0;
745 bestOverlap = overlap;
746 }
747
748 overlap = layoutOrganicCalculateOverlap (s, win, x1, areaHeight - h);
749 if (overlap < bestOverlap)
750 {
751 *bestY = areaHeight - h;
752 bestOverlap = overlap;
753 }
754
755 return bestOverlap;
756 }
757
758 static bool
759 layoutOrganicLocalSearch (CompScreen *s,
760 int areaWidth,
761 int areaHeight)
762 {
763 bool improvement;
764 int i;
765 double totalOverlap;
766
767 SCALE_SCREEN ();
768
769 do
770 {
771 improvement = false;
772 for (i = 0; i < ss->nWindows; i++)
773 {
774 bool improved;
775
776 do
777 {
778 int newX, newY;
779 double oldOverlap, overlapH, overlapV;
780
781 improved = false;
782 oldOverlap = layoutOrganicCalculateOverlap (s, i,
783 ss->slots[i].x1,
784 ss->slots[i].y1);
785
786 overlapH = layoutOrganicFindBestHorizontalPosition (s, i,
787 &newX,
788 areaWidth);
789 overlapV = layoutOrganicFindBestVerticalPosition (s, i,
790 &newY,
791 areaHeight);
792
793 if (overlapH < oldOverlap - 0.1 ||
794 overlapV < oldOverlap - 0.1)
795 {
796 improved = true;
797 improvement = true;
798 if (overlapV > overlapH)
799 ss->slots[i].x1 = newX;
800 else
801 ss->slots[i].y1 = newY;
802 }
803 }
804 while (improved);
805 }
806 }
807 while (improvement);
808
809 totalOverlap = 0.0;
810 for (i = 0; i < ss->nWindows; i++)
811 {
812 totalOverlap += layoutOrganicCalculateOverlap (s, i,
813 ss->slots[i].x1,
814 ss->slots[i].y1);
815 }
816
817 return (totalOverlap > 0.1);
818 }
819
820 static void
821 layoutOrganicRemoveOverlap (CompScreen *s,
822 int areaWidth,
823 int areaHeight)
824 {
825 int i, spacing;
826 CompWindow *w;
827
828 SCALE_SCREEN ();
829 ADDON_SCREEN ();
830
831 spacing = ss->opt[SCALE_SCREEN_OPTION_SPACING].value.i;
832
833 while (layoutOrganicLocalSearch (s, areaWidth, areaHeight))
834 {
835 for (i = 0; i < ss->nWindows; i++)
836 {
837 int centerX, centerY;
838 int newX, newY, newWidth, newHeight;
839
840 w = ss->windows[i];
841
842 centerX = ss->slots[i].x1 + WIN_W (w) / 2;
843 centerY = ss->slots[i].y1 + WIN_H (w) / 2;
844
845 newWidth = (int)((1.0 - ORGANIC_STEP) *
846 (double)WIN_W (w)) - spacing / 2;
847 newHeight = (int)((1.0 - ORGANIC_STEP) *
848 (double)WIN_H (w)) - spacing / 2;
849 newX = centerX - (newWidth / 2);
850 newY = centerY - (newHeight / 2);
851
852 ss->slots[i].x1 = newX;
853 ss->slots[i].y1 = newY;
854 ss->slots[i].x2 = newX + WIN_W (w);
855 ss->slots[i].y2 = newY + WIN_H (w);
856 }
857 as->scale -= ORGANIC_STEP;
858 }
859 }
860
861 static bool
862 layoutOrganicThumbs (CompScreen *s)
863 {
864 CompWindow *w;
865 int i, moMode;
866 XRectangle workArea;
867
868 SCALE_SCREEN ();
869 ADDON_SCREEN ();
870
871 moMode = ss->opt[SCALE_SCREEN_OPTION_MULTIOUTPUT_MODE].value.i;
872
873 switch (moMode) {
874 case SCALE_MOMODE_ALL:
875 workArea = s->workArea;
876 break;
877 case SCALE_MOMODE_CURRENT:
878 default:
879 workArea = s->outputDev[s->currentOutputDev].workArea;
880 break;
881 }
882
883 as->scale = 1.0f;
884
885 qsort (ss->windows, ss->nWindows, sizeof(CompWindow *),
886 organicCompareWindows);
887
888 for (i = 0; i < ss->nWindows; i++)
889 {
890 w = ss->windows[i];
891 SCALE_WINDOW (w);
892
893 sWindow->slot = &ss->slots[i];
894 ss->slots[i].x1 = WIN_X (w) - workArea.x;
895 ss->slots[i].y1 = WIN_Y (w) - workArea.y;
896 ss->slots[i].x2 = WIN_X (w) + WIN_W (w) - workArea.x;
897 ss->slots[i].y2 = WIN_Y (w) + WIN_H (w) - workArea.y;
898
899 if (ss->slots[i].x1 < 0)
900 {
901 ss->slots[i].x2 += abs (ss->slots[i].x1);
902 ss->slots[i].x1 = 0;
903 }
904 if (ss->slots[i].x2 > workArea.width - workArea.x)
905 {
906 ss->slots[i].x1 -= abs (ss->slots[i].x2 - workArea.width);
907 ss->slots[i].x2 = workArea.width - workArea.x;
908 }
909
910 if (ss->slots[i].y1 < 0)
911 {
912 ss->slots[i].y2 += abs (ss->slots[i].y1);
913 ss->slots[i].y1 = 0;
914 }
915 if (ss->slots[i].y2 > workArea.height - workArea.y)
916 {
917 ss->slots[i].y1 -= abs (ss->slots[i].y2 -
918 workArea.height - workArea.y);
919 ss->slots[i].y2 = workArea.height - workArea.y;
920 }
921 }
922
923 ss->nSlots = ss->nWindows;
924
925 layoutOrganicRemoveOverlap (s, workArea.width - workArea.x,
926 workArea.height - workArea.y);
927 for (i = 0; i < ss->nWindows; i++)
928 {
929 w = ss->windows[i];
930 SCALE_WINDOW (w);
931
932 if (ss->type == ScaleTypeGroup)
933 raiseWindow (ss->windows[i]);
934
935 ss->slots[i].x1 += w->input.left + workArea.x;
936 ss->slots[i].x2 += w->input.left + workArea.x;
937 ss->slots[i].y1 += w->input.top + workArea.y;
938 ss->slots[i].y2 += w->input.top + workArea.y;
939 sWindow->adjust = true;
940 }
941
942 return true;
943 }
944
945 #endif
946
947 /*
948 * Inspired by KWin - the KDE Window Manager
949 * presentwindows.cpp
950 * Copyright (C) 2007 Rivo Laks <rivolaks@hot.ee>
951 * Copyright (C) 2008 Lucas Murray <lmurray@undefinedfire.com>
952 *
953 */
954
955 bool
956 ScaleAddonScreen::isOverlappingAny (ScaleWindow *w,
957 std::map <ScaleWindow *, CompRegion> targets,
958 const CompRegion &border)
959 {
960 if (border.intersects (targets[w]))
961 return true;
962 // Is there a better way to do this?
963 std::map <ScaleWindow *, CompRegion>::const_iterator i;
964 for (i = targets.begin (); i != targets.end (); ++i)
965 {
966 if (w == (*i).first)
967 continue;
968 if (targets[w].intersects ((*i).second))
969 return true;
970 }
971 return false;
972 }
973
974 bool
975 ScaleAddonScreen::layoutNaturalThumbs ()
976 {
977 ScaleScreen::WindowList windows = ScaleScreen::get (screen)->getWindows ();
978 bool overlapping;
979 CompRect area = screen->workArea ();
980 CompRect bounds = area;
981 std::map <ScaleWindow *, CompRegion> targets;
982 std::map <ScaleWindow *, int> directions;
983 int direction = 0;
984 int iterCount = 0;
985
986 if (windows.size () == 1)
987 {
988 // Just move the window to its original location to save time
989 if (screen->fullscreenOutput ().workArea ().contains (windows.front ()->window->geometry ()))
990 {
991 ScaleSlot slot ((CompRect &) windows.front ()->window->geometry ());
992 windows.front ()->setSlot (slot);
993 return true;
994 }
995 }
996
997 foreach (ScaleWindow *w, windows)
998 {
999 bounds = CompRegion (bounds).united (w->window->outputRect ()).boundingRect ();
1000 targets[w] = CompRegion (w->window->outputRect ());
1001 // Reuse the unused "slot" as a preferred direction attribute. This is used when the window
1002 // is on the edge of the screen to try to use as much screen real estate as possible.
1003 directions[w] = direction;
1004 direction++;
1005 if (direction == 4)
1006 direction = 0;
1007 }
1008
1009 do
1010 {
1011 overlapping = false;
1012 foreach (ScaleWindow *w, windows)
1013 {
1014 foreach (ScaleWindow *e, windows)
1015 {
1016 if (e->window->id () != w->window->id () && targets[w].intersects (targets[e]))
1017 {
1018 int moveX = targets[w].boundingRect ().centerX () - targets[e].boundingRect ().centerX ();
1019 int moveY = targets[w].boundingRect ().centerY () - targets[e].boundingRect ().centerY ();
1020 //int xSection, ySection;
1021 // Overlap detected, determine direction to push
1022
1023 overlapping = true;
1024
1025 moveX /= optionGetNaturalPrecision ();
1026 moveY /= optionGetNaturalPrecision ();
1027
1028 /* Force movement */
1029 if (moveX == 0)
1030 moveX = optionGetNaturalPrecision ();
1031 if (moveY == 0)
1032 moveY = optionGetNaturalPrecision ();
1033
1034 targets[w] = targets[w].translated (moveX, moveY);
1035 targets[e] = targets[e].translated (-moveX, -moveY);
1036
1037 /* Try to keep the bounding rect the same aspect as the screen so that more
1038 * screen real estate is utilised. We do this by splitting the screen into nine
1039 * equal sections, if the window center is in any of the corner sections pull the
1040 * window towards the outer corner. If it is in any of the other edge sections
1041 * alternate between each corner on that edge. We don't want to determine it
1042 * randomly as it will not produce consistant locations when using the filter.
1043 * Only move one window so we don't cause large amounts of unnecessary zooming
1044 * in some situations. We need to do this even when expanding later just in case
1045 * all windows are the same size.
1046 * (We are using an old bounding rect for this, hopefully it doesn't matter)
1047 * FIXME: Disabled for now
1048 *
1049 xSection = (targets[w].boundingRect ().x () - bounds.x ()) / (bounds.width () / 3);
1050 ySection = (targets[w].boundingRect ().y () - bounds.y ()) / (bounds.height () / 3);
1051 moveX = 0;
1052 moveY = 0;
1053 if (xSection != 1 || ySection != 1) // Remove this if you want the center to pull as well
1054 {
1055 if (xSection == 1)
1056 xSection = (directions[w] / 2 ? 2 : 0);
1057 if (ySection == 1)
1058 ySection = (directions[w] % 2 ? 2 : 0);
1059 }
1060
1061 if (xSection == 0 && ySection == 0)
1062 {
1063 moveX = bounds.left () - targets[w].boundingRect ().centerX ();
1064 moveY = bounds.top () - targets[w].boundingRect ().centerY ();
1065 }
1066 if (xSection == 2 && ySection == 0)
1067 {
1068 moveX = bounds.right () - targets[w].boundingRect ().centerX ();
1069 moveY = bounds.top () - targets[w].boundingRect ().centerY ();
1070 }
1071 if (xSection == 2 && ySection == 2)
1072 {
1073 moveX = bounds.right () - targets[w].boundingRect ().centerX ();
1074 moveY = bounds.bottom () - targets[w].boundingRect ().centerY ();
1075 }
1076 if (xSection == 0 && ySection == 2)
1077 {
1078 moveX = bounds.left () - targets[w].boundingRect ().centerX ();
1079 moveY = bounds.right () - targets[w].boundingRect ().centerY ();
1080 }
1081 if (moveX != 0 || moveY != 0)
1082 targets[w].translate (moveX, moveY);
1083 */
1084 }
1085
1086 // Update bounding rect
1087 bounds = CompRegion (bounds).united (targets[w]).boundingRect ();
1088 bounds = CompRegion (bounds).united (targets[e]).boundingRect ();
1089 }
1090 }
1091 }
1092 while (overlapping);
1093
1094 // Work out scaling by getting the most top-left and most bottom-right window coords.
1095 // The 20's and 10's are so that the windows don't touch the edge of the screen.
1096 double scale;
1097 if (bounds == area)
1098 scale = 1.0; // Don't add borders to the screen
1099 else if (area.width () / double (bounds.width ()) < area.height () / double (bounds.height ()))
1100 scale = (area.width () - 20) / double (bounds.width ());
1101 else
1102 scale = (area.height () - 20) / double (bounds.height ());
1103 // Make bounding rect fill the screen size for later steps
1104 bounds = CompRect (
1105 bounds.x () - (area.width () - 20 - bounds.width () * scale ) / 2 - 10 / scale,
1106 bounds.y () - (area.height () - 20 - bounds.height () * scale ) / 2 - 10 / scale,
1107 area.width () / scale,
1108 area.height () / scale
1109 );
1110
1111 // Move all windows back onto the screen and set their scale
1112 foreach (ScaleWindow *w, windows)
1113 {
1114 targets[w] = CompRect (
1115 (targets[w].boundingRect ().x () - bounds.x () ) * scale + area.x (),
1116 (targets[w].boundingRect ().y () - bounds.y ()) * scale + area.y (),
1117 targets[w].boundingRect ().width () * scale,
1118 targets[w].boundingRect ().height () * scale
1119 );
1120 ScaleSlot slt (targets[w].boundingRect ());
1121 slt.scale = scale;
1122 slt.filled = true;
1123
1124 w->setSlot (slt);
1125 }
1126
1127 // Don't expand onto or over the border
1128 CompRegion borderRegion = CompRegion (area);
1129 CompRegion areaRegion = CompRegion (area);
1130 borderRegion.translate (-200, -200);
1131 borderRegion.shrink (-200, -200); // actually expands the region
1132 areaRegion.translate (10 / scale, 10 / scale);
1133 areaRegion.shrink (10 / scale, 10 / scale);
1134
1135 borderRegion ^= areaRegion;
1136
1137 bool moved = false;
1138 do
1139 {
1140 moved = false;
1141 foreach (ScaleWindow *w, windows)
1142 {
1143 CompRegion oldRegion;
1144
1145 // This may cause some slight distortion if the windows are enlarged a large amount
1146 int widthDiff = optionGetNaturalPrecision ();
1147 int heightDiff = ((w->window->height () / w->window->width ()) *
1148 (targets[w].boundingRect ().width() + widthDiff)) - targets[w].boundingRect ().height ();
1149 int xDiff = widthDiff / 2; // Also move a bit in the direction of the enlarge, allows the
1150 int yDiff = heightDiff / 2; // center windows to be enlarged if there is gaps on the side.
1151
1152 // Attempt enlarging to the top-right
1153 oldRegion = targets[w];
1154 targets[w] = CompRegion (
1155 targets[w].boundingRect ().x () + xDiff,
1156 targets[w].boundingRect ().y () - yDiff - heightDiff,
1157 targets[w].boundingRect ().width () + widthDiff,
1158 targets[w].boundingRect ().height () + heightDiff
1159 );
1160 if (isOverlappingAny (w, targets, borderRegion))
1161 targets[w] = oldRegion;
1162 else
1163 moved = true;
1164
1165 // Attempt enlarging to the bottom-right
1166 oldRegion = targets[w];
1167 targets[w] = CompRegion(
1168 targets[w].boundingRect ().x () + xDiff,
1169 targets[w].boundingRect ().y () + yDiff,
1170 targets[w].boundingRect ().width () + widthDiff,
1171 targets[w].boundingRect ().height () + heightDiff
1172 );
1173 if (isOverlappingAny (w, targets, borderRegion))
1174 targets[w] = oldRegion;
1175 else
1176 moved = true;
1177
1178 // Attempt enlarging to the bottom-left
1179 oldRegion = targets[w];
1180 targets[w] = CompRegion (
1181 targets[w].boundingRect ().x() - xDiff - widthDiff,
1182 targets[w].boundingRect ().y() + yDiff,
1183 targets[w].boundingRect ().width() + widthDiff,
1184 targets[w].boundingRect ().height() + heightDiff
1185 );
1186 if (isOverlappingAny (w, targets, borderRegion))
1187 targets[w] = oldRegion;
1188 else
1189 moved = true;
1190
1191 // Attempt enlarging to the top-left
1192 oldRegion = targets[w];
1193 targets[w] = CompRegion (
1194 targets[w].boundingRect ().x() - xDiff - widthDiff,
1195 targets[w].boundingRect ().y() - yDiff - heightDiff,
1196 targets[w].boundingRect ().width() + widthDiff,
1197 targets[w].boundingRect ().height() + heightDiff
1198 );
1199 if (isOverlappingAny (w, targets, borderRegion))
1200 targets[w] = oldRegion;
1201 else
1202 moved = true;
1203 }
1204
1205 iterCount++;
1206 }
1207 while (moved && iterCount < 100);
1208
1209 // The expanding code above can actually enlarge windows over 1.0/2.0 scale, we don't like this
1210 // We can't add this to the loop above as it would cause a never-ending loop so we have to make
1211 // do with the less-than-optimal space usage with using this method.
1212 foreach (ScaleWindow *w, windows)
1213 {
1214 double scale = targets[w].boundingRect ().width() / double( w->window->width());
1215 if (scale > 2.0 || (scale > 1.0 && (w->window->width() > 300 || w->window->height() > 300)))
1216 {
1217 scale = (w->window->width () > 300 || w->window->height () > 300) ? 1.0 : 2.0;
1218 targets[w] = CompRegion (
1219 targets[w].boundingRect ().center().x() - int (w->window->width() * scale) / 2,
1220 targets[w].boundingRect ().center().y() - int (w->window->height () * scale) / 2,
1221 w->window->width() * scale,
1222 w->window->height() * scale
1223 );
1224 }
1225 }
1226
1227 return true;
1228
1229 }
1230
1231 bool
1232 ScaleAddonScreen::layoutSlotsAndAssignWindows ()
1233 {
1234 bool status;
1235
1236 switch (optionGetLayoutMode ())
1237 {
1238 case LayoutModeNatural:
1239 status = layoutNaturalThumbs ();
1240 break;
1241 case LayoutModeNormal:
1242 default:
1243 status = sScreen->layoutSlotsAndAssignWindows ();
1244 break;
1245 }
1246
1247 return status;
1248 }
1249
1250 void
1251 ScaleAddonScreen::optionChanged (CompOption *opt,
1252 ScaleaddonOptions::Options num)
1253 {
1254 switch (num)
1255 {
1256 case ScaleaddonOptions::WindowTitle:
1257 case ScaleaddonOptions::TitleBold:
1258 case ScaleaddonOptions::TitleSize:
1259 case ScaleaddonOptions::BorderSize:
1260 case ScaleaddonOptions::FontColor:
1261 case ScaleaddonOptions::BackColor:
1262 if (textAvailable)
1263 {
1264 foreach (CompWindow *w, screen->windows ())
1265 {
1266 ADDON_WINDOW (w);
1267 aw->renderTitle ();
1268 }
1269 }
1270 break;
1271 default:
1272 break;
1273 }
1274 }
1275
1276 ScaleAddonScreen::ScaleAddonScreen (CompScreen *) :
1277 PluginClassHandler <ScaleAddonScreen, CompScreen> (screen),
1278 cScreen (CompositeScreen::get (screen)),
1279 sScreen (ScaleScreen::get (screen)),
1280 highlightedWindow (0),
1281 lastHighlightedWindow (0),
1282 lastState (ScaleScreen::Idle),
1283 scale (1.0f)
1284 {
1285 CompAction::CallBack cb;
1286 ChangeNotify notify;
1287
1288 ScreenInterface::setHandler (screen, true);
1289 CompositeScreenInterface::setHandler (cScreen, true);
1290 ScaleScreenInterface::setHandler (sScreen, true);
1291
1292 cb = boost::bind (&ScaleAddonScreen::closeWindow, this, _1, _2, _3);
1293 optionSetCloseKeyInitiate (cb);
1294 optionSetCloseButtonInitiate (cb);
1295
1296 cb = boost::bind (&ScaleAddonScreen::zoomWindow, this, _1, _2, _3);
1297 optionSetZoomKeyInitiate (cb);
1298 optionSetZoomButtonInitiate (cb);
1299
1300 cb = boost::bind (&ScaleAddonScreen::pullWindow, this, _1, _2, _3);
1301 optionSetPullKeyInitiate (cb);
1302 optionSetPullButtonInitiate (cb);
1303
1304 notify = boost::bind (&ScaleAddonScreen::optionChanged, this, _1, _2);
1305 optionSetWindowTitleNotify (notify);
1306 optionSetTitleBoldNotify (notify);
1307 optionSetTitleSizeNotify (notify);
1308 optionSetBorderSizeNotify (notify);
1309 optionSetFontColorNotify (notify);
1310 optionSetBackColorNotify (notify);
1311 }
1312
1313 ScaleAddonWindow::ScaleAddonWindow (CompWindow *window) :
1314 PluginClassHandler <ScaleAddonWindow, CompWindow> (window),
1315 window (window),
1316 sWindow (ScaleWindow::get (window)),
1317 cWindow (CompositeWindow::get (window)),
1318 rescaled (false),
1319 oldAbove (NULL)
1320 {
1321 ScaleWindowInterface::setHandler (sWindow);
1322 }
1323
1324 bool
1325 ScaleAddonPluginVTable::init ()
1326 {
1327 if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION) ||
1328 !CompPlugin::checkPluginABI ("composite", COMPIZ_COMPOSITE_ABI) ||
1329 !CompPlugin::checkPluginABI ("opengl", COMPIZ_OPENGL_ABI) ||
1330 !CompPlugin::checkPluginABI ("scale", COMPIZ_SCALE_ABI))
1331 return false;
1332
1333 if (!CompPlugin::checkPluginABI ("text", COMPIZ_TEXT_ABI))
1334 {
1335 compLogMessage ("scaleaddon", CompLogLevelInfo,
1336 "Text Plugin not loaded, no text will be drawn.");
1337 textAvailable = false;
1338 }
1339 else
1340 textAvailable = true;
1341
1342 return true;
1343 }