1 /*
   2  * Copyright �� 2005 Novell, Inc.
   3  * Copyright (C) 2007, 2008,2010 Kristian Lyngst��l
   4  *
   5  * Permission to use, copy, modify, distribute, and sell this software
   6  * and its documentation for any purpose is hereby granted without
   7  * fee, provided that the above copyright notice appear in all copies
   8  * and that both that copyright notice and this permission notice
   9  * appear in supporting documentation, and that the name of
  10  * Novell, Inc. not be used in advertising or publicity pertaining to
  11  * distribution of the software without specific, written prior permission.
  12  * Novell, Inc. makes no representations about the suitability of this
  13  * software for any purpose. It is provided "as is" without express or
  14  * implied warranty.
  15  *
  16  * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
  18  * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
  20  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  21  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  22  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  23  *
  24  *
  25  * Author(s):
  26  *	- Most features beyond basic zoom;
  27  *	  Kristian Lyngstol <kristian@bohemians.org>
  28  *	- Original zoom plug-in; David Reveman <davidr@novell.com>
  29  *	- Original port to C++ by Sam Spilsbury <smspillaz@gmail.com>
  30  *
  31  * Description:
  32  *
  33  * This plug-in offers zoom functionality with focus tracking,
  34  * fit-to-window actions, mouse panning, zoom area locking. Without
  35  * disabling input.
  36  *
  37  * Note on actual zoom process
  38  *
  39  * The animation is done in preparePaintScreen, while instant movements
  40  * are done by calling updateActualTranslate () after updating the
  41  * translations. This causes [xyz]trans to be re-calculated. We keep track
  42  * of each head separately.
  43  *
  44  * Note on input
  45  *
  46  * We can not redirect input yet, but this plug-in offers two fundamentally
  47  * different approaches to achieve input enabled zoom:
  48  *
  49  * 1.
  50  * Always have the zoomed area be in sync with the mouse cursor. This binds
  51  * the zoom area to the mouse position at any given time. It allows using
  52  * the original mouse cursor drawn by X, and is technically very safe.
  53  * First used in Beryl's inputzoom.
  54  *
  55  * 2.
  56  * Hide the real cursor and draw our own where it would be when zoomed in.
  57  * This allows us to navigate with the mouse without constantly moving the
  58  * zoom area. This is fairly close to what we want in the end when input
  59  * redirection is available.
  60  *
  61  * This second method has one huge issue, which is bugged XFixes. After
  62  * hiding the cursor once with XFixes, some mouse cursors will simply be
  63  * invisible. The Firefox loading cursor being one of them.
  64  *
  65  * An other minor annoyance is that mouse sensitivity seems to increase as
  66  * you zoom in, since the mouse isn't really zoomed at all.
  67  *
  68  * Todo:
  69  *  - Walk through C++ port and adjust comments for 2010.
  70  *  - See if anyone misses the filter setting
  71  *  - Verify XFixes fix... err.
  72  *  - Different multi head modes
  73  */
  74 
  75 #include "ezoom.h"
  76 
  77 COMPIZ_PLUGIN_20090315 (ezoom, ZoomPluginVTable)
  78 
  79 
  80 /*
  81  * This toggles paint functions. We don't need to continually run code when we
  82  * are not doing anything
  83  */
  84 static inline void
  85 toggleFunctions (bool state)
  86 {
  87     ZOOM_SCREEN (screen);
  88 
  89     screen->handleEventSetEnabled (zs, state);
  90     zs->cScreen->preparePaintSetEnabled (zs, state);
  91     zs->gScreen->glPaintOutputSetEnabled (zs, state);
  92     zs->cScreen->donePaintSetEnabled (zs, state);
  93 }
  94 
  95 /* Check if the output is valid */
  96 static inline bool
  97 outputIsZoomArea (int out)
  98 {
  99     ZOOM_SCREEN (screen);
 100 
 101     if (out < 0)
 102 	return false;
 103     else if ((unsigned int) out >= zs->zooms.size ())
 104 	zs->zooms.resize (screen->outputDevs ().size ());
 105     return true;
 106 }
 107 
 108 /* Check if zoom is active on the output specified */
 109 static inline bool
 110 isActive (int out)
 111 {
 112     ZOOM_SCREEN (screen);
 113 
 114     if (!outputIsZoomArea (out))
 115 	return false;
 116     if (zs->grabbed & (1 << zs->zooms.at (out).output))
 117 	return true;
 118     return false;
 119 }
 120 
 121 /* Check if we are zoomed out and not going anywhere
 122  * (similar to isActive but based on actual zoom, not grab)
 123  */
 124 static inline bool
 125 isZoomed (int out)
 126 {
 127     ZOOM_SCREEN (screen);
 128 
 129     if (!outputIsZoomArea (out))
 130 	return false;
 131 
 132     if (zs->zooms.at (out).currentZoom != 1.0f
 133 	|| zs->zooms.at (out).newZoom != 1.0f)
 134 	return true;
 135 
 136     if (zs->zooms.at (out).zVelocity != 0.0f)
 137 	return true;
 138 
 139     return false;
 140 }
 141 
 142 /* Returns the distance to the defined edge in zoomed pixels.  */
 143 int
 144 EZoomScreen::distanceToEdge (int out, EZoomScreen::ZoomEdge edge)
 145 {
 146     int        x1,y1,x2,y2;
 147     CompOutput *o = &screen->outputDevs ().at (out);
 148 
 149     if (!isActive (out))
 150 	return 0;
 151     convertToZoomedTarget (out, o->region ()->extents.x2,
 152 			   o->region ()->extents.y2, &x2, &y2);
 153     convertToZoomedTarget (out, o->region ()->extents.x1,
 154 			   o->region ()->extents.y1, &x1, &y1);
 155     switch (edge)
 156     {
 157 	case NORTH: return o->region ()->extents.y1 - y1;
 158 	case SOUTH: return y2 - o->region ()->extents.y2;
 159 	case EAST: return x2 - o->region ()->extents.x2;
 160 	case WEST: return o->region ()->extents.x1 - x1;
 161     }
 162     return 0; // Never reached.
 163 }
 164 
 165 /* Update/set translations based on zoom level and real translate.  */
 166 void
 167 EZoomScreen::ZoomArea::updateActualTranslates ()
 168 {
 169     xtrans = -realXTranslate * (1.0f - currentZoom);
 170     ytrans = realYTranslate * (1.0f - currentZoom);
 171 }
 172 
 173 /* Returns true if the head in question is currently moving.
 174  * Since we don't always bother resetting everything when
 175  * canceling zoom, we check for the condition of being completely
 176  * zoomed out and not zooming in/out first.
 177  */
 178 bool
 179 EZoomScreen::isInMovement (int out)
 180 {
 181     if (zooms.at (out).currentZoom == 1.0f &&
 182 	zooms.at (out).newZoom == 1.0f &&
 183 	zooms.at (out).zVelocity == 0.0f)
 184 	return false;
 185     if (zooms.at (out).currentZoom != zooms.at (out).newZoom ||
 186 	zooms.at (out).xVelocity || zooms.at (out).yVelocity ||
 187 	zooms.at (out).zVelocity)
 188 	return true;
 189     if (zooms.at (out).xTranslate != zooms.at (out).realXTranslate ||
 190 	zooms.at (out).yTranslate != zooms.at (out).realYTranslate)
 191 	return true;
 192     return false;
 193 }
 194 
 195 /* Set the initial values of a zoom area.  */
 196 EZoomScreen::ZoomArea::ZoomArea (int out) :
 197     output (out),
 198     viewport (~0),
 199     currentZoom (1.0f),
 200     newZoom (1.0f),
 201     xVelocity (0.0f),
 202     yVelocity (0.0f),
 203     zVelocity (0.0f),
 204     xTranslate (0.0f),
 205     yTranslate (0.0f),
 206     realXTranslate (0.0f),
 207     realYTranslate (0.0f),
 208     locked (false)
 209 {
 210     updateActualTranslates ();
 211 }
 212 
 213 EZoomScreen::ZoomArea::ZoomArea () :
 214     output (0),
 215     viewport (~0),
 216     currentZoom (1.0f),
 217     newZoom (1.0f),
 218     xVelocity (0.0f),
 219     yVelocity (0.0f),
 220     zVelocity (0.0f),
 221     xTranslate (0.0f),
 222     yTranslate (0.0f),
 223     realXTranslate (0.0f),
 224     realYTranslate (0.0f),
 225     locked (false)
 226 {
CID 12573 - UNINIT_CTOR
Non-static class member "xtrans" is not initialized in this constructor nor in any functions that it calls.
Non-static class member "ytrans" is not initialized in this constructor nor in any functions that it calls.
 227 }
 228 /* Adjust the velocity in the z-direction.  */
 229 void
 230 EZoomScreen::adjustZoomVelocity (int out, float chunk)
 231 {
 232     float d, adjust, amount;
 233 
 234     d = (zooms.at (out).newZoom - zooms.at (out).currentZoom) * 75.0f;
 235 
 236     adjust = d * 0.002f;
 237     amount = fabs (d);
 238     if (amount < 1.0f)
 239 	amount = 1.0f;
 240     else if (amount > 5.0f)
 241 	amount = 5.0f;
 242 
 243     zooms.at (out).zVelocity =
 244 	(amount * zooms.at (out).zVelocity + adjust) / (amount + 1.0f);
 245 
 246     if (fabs (d) < 0.1f && fabs (zooms.at (out).zVelocity) < 0.005f)
 247     {
 248 	zooms.at (out).currentZoom = zooms.at (out).newZoom;
 249 	zooms.at (out).zVelocity = 0.0f;
 250     }
 251     else
 252     {
 253 	zooms.at (out).currentZoom += (zooms.at (out).zVelocity * chunk) /
 254 	    cScreen->redrawTime ();
 255     }
 256 }
 257 
 258 /* Adjust the X/Y velocity based on target translation and real
 259  * translation. */
 260 void
 261 EZoomScreen::adjustXYVelocity (int out, float chunk)
 262 {
 263     float xdiff, ydiff;
 264     float xadjust, yadjust;
 265     float xamount, yamount;
 266 
 267     zooms.at (out).xVelocity /= 1.25f;
 268     zooms.at (out).yVelocity /= 1.25f;
 269     xdiff =
 270 	(zooms.at (out).xTranslate - zooms.at (out).realXTranslate) *
 271 	75.0f;
 272     ydiff =
 273 	(zooms.at (out).yTranslate - zooms.at (out).realYTranslate) *
 274 	75.0f;
 275     xadjust = xdiff * 0.002f;
 276     yadjust = ydiff * 0.002f;
 277     xamount = fabs (xdiff);
 278     yamount = fabs (ydiff);
 279 
 280     if (xamount < 1.0f)
 281 	    xamount = 1.0f;
 282     else if (xamount > 5.0)
 283 	    xamount = 5.0f;
 284 
 285     if (yamount < 1.0f)
 286 	    yamount = 1.0f;
 287     else if (yamount > 5.0)
 288 	    yamount = 5.0f;
 289 
 290     zooms.at (out).xVelocity =
 291 	(xamount * zooms.at (out).xVelocity + xadjust) / (xamount + 1.0f);
 292     zooms.at (out).yVelocity =
 293 	(yamount * zooms.at (out).yVelocity + yadjust) / (yamount + 1.0f);
 294 
 295     if ((fabs(xdiff) < 0.1f && fabs (zooms.at (out).xVelocity) < 0.005f) &&
 296 	(fabs(ydiff) < 0.1f && fabs (zooms.at (out).yVelocity) < 0.005f))
 297     {
 298 	zooms.at (out).realXTranslate = zooms.at (out).xTranslate;
 299 	zooms.at (out).realYTranslate = zooms.at (out).yTranslate;
 300 	zooms.at (out).xVelocity = 0.0f;
 301 	zooms.at (out).yVelocity = 0.0f;
 302 	return;
 303     }
 304 
 305     zooms.at (out).realXTranslate +=
 306 	(zooms.at (out).xVelocity * chunk) / cScreen->redrawTime ();
 307     zooms.at (out).realYTranslate +=
 308 	(zooms.at (out).yVelocity * chunk) / cScreen->redrawTime ();
 309 }
 310 
 311 /* Animate the movement (if any) in preparation of a paint screen.  */
 312 void
 313 EZoomScreen::preparePaint (int	   msSinceLastPaint)
 314 {
 315     if (grabbed)
 316     {
 317 	int   steps;
 318 	float amount, chunk;
 319 
 320 	amount = msSinceLastPaint * 0.05f * optionGetSpeed ();
 321 	steps  = amount / (0.5f * optionGetTimestep ());
 322 	if (!steps)
 323 	       	steps = 1;
 324 	chunk  = amount / (float) steps;
 325 	while (steps--)
 326 	{
 327 	    unsigned int out;
 328 	    for (out = 0; out < zooms.size (); out++)
 329 	    {
 330 		if (!isInMovement (out) || !isActive (out))
 331 		    continue;
 332 
 333 		adjustXYVelocity (out, chunk);
 334 		adjustZoomVelocity (out, chunk);
 335 		zooms.at (out).updateActualTranslates ();
 336 		if (!isZoomed (out))
 337 		{
 338 		    zooms.at (out).xVelocity = zooms.at (out).yVelocity =
 339 			0.0f;
 340 		    grabbed &= ~(1 << zooms.at (out).output);
 341 		    if (!grabbed)
 342 		    {
 343 			cScreen->damageScreen ();
 344 			toggleFunctions (false);
 345 		    }
 346 		}
 347 	    }
 348 	}
 349 	if (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse)
 350 	    syncCenterToMouse ();
 351     }
 352 
 353     cScreen->preparePaint (msSinceLastPaint);
 354 }
 355 
 356 /* Damage screen if we're still moving.  */
 357 void
 358 EZoomScreen::donePaint ()
 359 {
 360     if (grabbed)
 361     {
 362 	unsigned int out;
 363 	for (out = 0; out < zooms.size (); out++)
 364 	{
 365 	    if (isInMovement (out) && isActive (out))
 366 	    {
 367 		cScreen->damageScreen ();
 368 		break;
 369 	    }
 370 	}
 371     }
 372     else if (grabIndex)
 373 	cScreen->damageScreen ();
 374     else
 375         toggleFunctions (false);
 376 
 377     cScreen->donePaint ();
 378 }
 379 /* Draws a box from the screen coordinates inx1,iny1 to inx2,iny2 */
 380 void
 381 EZoomScreen::drawBox (const GLMatrix &transform,
 382 		     CompOutput          *output,
 383 		     CompRect             box)
 384 {
 385     GLMatrix zTransform (transform);
 386     int           x1,x2,y1,y2;
 387     int		  inx1, inx2, iny1, iny2;
 388     int	          out = output->id ();
 389     GLushort colorData[4];
 390     GLfloat  vertexData[12];
 391     GLVertexBuffer *streamingBuffer = GLVertexBuffer::streamingBuffer ();
 392 
 393     zTransform.toScreenSpace (output, -DEFAULT_Z_CAMERA);
 394     convertToZoomed (out, box.x1 (), box.y1 (), &inx1, &iny1);
 395     convertToZoomed (out, box.x2 (), box.y2 (), &inx2, &iny2);
 396 
 397     x1 = MIN (inx1, inx2);
 398     y1 = MIN (iny1, iny2);
 399     x2 = MAX (inx1, inx2);
 400     y2 = MAX (iny1, iny2);
 401 
 402     streamingBuffer->begin (GL_TRIANGLE_STRIP);
 403 
 404     colorData[0] = 0x2fff;
 405     colorData[1] = 0x2fff;
 406     colorData[2] = 0x2fff;
 407     colorData[3] = 0x4fff;
 408 
 409     streamingBuffer->addColors (1, colorData);
 410 
 411     vertexData[0]  = x1;
 412     vertexData[1]  = y1;
 413     vertexData[2]  = 0.0f;
 414     vertexData[3]  = x1;
 415     vertexData[4]  = y2;
 416     vertexData[5]  = 0.0f;
 417     vertexData[6]  = x2;
 418     vertexData[7]  = y1;
 419     vertexData[8]  = 0.0f;
 420     vertexData[9]  = x2;
 421     vertexData[10] = y2;
 422     vertexData[11] = 0.0f;
 423 
 424     streamingBuffer->addVertices (4, vertexData);
 425 
 426     streamingBuffer->end ();
 427     streamingBuffer->render (zTransform);
 428 
 429 
 430     streamingBuffer->begin (GL_LINE_LOOP);
 431 
 432     colorData[0] = 0x2fff;
 433     colorData[1] = 0x2fff;
 434     colorData[2] = 0x4fff;
 435     colorData[3] = 0x9fff;
 436 
 437     streamingBuffer->addColors (1, colorData);
 438 
 439     vertexData[0]  = x1;
 440     vertexData[1]  = y1;
 441     vertexData[2]  = 0.0f;
 442     vertexData[3]  = x2;
 443     vertexData[4]  = y1;
 444     vertexData[5]  = 0.0f;
 445     vertexData[6]  = x2;
 446     vertexData[7]  = y2;
 447     vertexData[8]  = 0.0f;
 448     vertexData[9]  = x1;
 449     vertexData[10] = y2;
 450     vertexData[11] = 0.0f;
 451 
 452     streamingBuffer->addVertices (4, vertexData);
 453 
 454     streamingBuffer->end ();
 455     streamingBuffer->render (zTransform);
 456 }
 457 /* Apply the zoom if we are grabbed.
 458  * Make sure to use the correct filter.
 459  */
 460 bool
 461 EZoomScreen::glPaintOutput (const GLScreenPaintAttrib &attrib,
 462 			   const GLMatrix	     &transform,
 463 			   const CompRegion	     &region,
 464 			   CompOutput 		     *output,
 465 			   unsigned int		     mask)
 466 {
 467     bool status;
 468     int	 out = output->id ();
 469 
 470     if (isActive (out))
 471     {
 472 	GLScreenPaintAttrib sa = attrib;
 473 	GLMatrix            zTransform = transform;
 474 
 475 	mask &= ~PAINT_SCREEN_REGION_MASK;
 476 	mask |= PAINT_SCREEN_CLEAR_MASK;
 477 
 478 	zTransform.scale (1.0f / zooms.at (out).currentZoom,
 479 		          1.0f / zooms.at (out).currentZoom,
 480 		          1.0f);
 481 	zTransform.translate (zooms.at (out).xtrans,
 482 			      zooms.at (out).ytrans,
 483 			      0);
 484 
 485 	mask |= PAINT_SCREEN_TRANSFORMED_MASK;
 486 
 487 	status = gScreen->glPaintOutput (sa, zTransform, region, output, mask);
 488 
 489 	drawCursor (output, transform);
 490 
 491     }
 492     else
 493     {
 494 	status = gScreen->glPaintOutput (attrib, transform, region, output,
 495 									mask);
 496     }
 497     if (grabIndex)
 498 	drawBox (transform, output, box);
 499 
 500     return status;
 501 }
 502 
 503 /* Makes sure we're not attempting to translate too far.
 504  * We are restricted to 0.5 to not go beyond the end
 505  * of the screen/head.  */
 506 static inline void
 507 constrainZoomTranslate ()
 508 {
 509     unsigned int out;
 510     ZOOM_SCREEN (screen);
 511 
 512     for (out = 0; out < zs->zooms.size (); out++)
 513     {
 514 	if (zs->zooms.at (out).xTranslate > 0.5f)
 515 	    zs->zooms.at (out).xTranslate = 0.5f;
 516 	else if (zs->zooms.at (out).xTranslate < -0.5f)
 517 	    zs->zooms.at (out).xTranslate = -0.5f;
 518 
 519 	if (zs->zooms.at (out).yTranslate > 0.5f)
 520 	    zs->zooms.at (out).yTranslate = 0.5f;
 521 	else if (zs->zooms.at (out).yTranslate < -0.5f)
 522 	    zs->zooms.at (out).yTranslate = -0.5f;
 523     }
 524 }
 525 
 526 /* Functions for adjusting the zoomed area.
 527  * These are the core of the zoom plug-in; Anything wanting
 528  * to adjust the zoomed area must use setCenter or setZoomArea
 529  * and setScale or front ends to them.  */
 530 
 531 /* Sets the center of the zoom area to X,Y.
 532  * We have to be able to warp the pointer here: If we are moved by
 533  * anything except mouse movement, we have to sync the
 534  * mouse pointer. This is to allow input, and is NOT necessary
 535  * when input redirection is available to us or if we're cheating
 536  * and using a scaled mouse cursor to imitate IR.
 537  * The center is not the center of the screen. This is the target-center;
 538  * that is, it's the point that's the same regardless of zoom level.
 539  */
 540 void
 541 EZoomScreen::setCenter (int x, int y, bool instant)
 542 {
 543     int         out = screen->outputDeviceForPoint (x, y);
 544     CompOutput  *o = &screen->outputDevs ().at (out);
 545 
 546     if (zooms.at (out).locked)
 547 	return;
 548 
 549     zooms.at (out).xTranslate = (float)
 550 	((x - o->x1 ()) - o->width ()  / 2) / (o->width ());
 551     zooms.at (out).yTranslate = (float)
 552 	((y - o->y1 ()) - o->height () / 2) / (o->height ());
 553 
 554     if (instant)
 555     {
 556 	zooms.at (out).realXTranslate = zooms.at (out).xTranslate;
 557 	zooms.at (out).realYTranslate = zooms.at (out).yTranslate;
 558 	zooms.at (out).yVelocity = 0.0f;
 559 	zooms.at (out).xVelocity = 0.0f;
 560 	zooms.at (out).updateActualTranslates ();
 561     }
 562 
 563     if (optionGetZoomMode () == EzoomOptions::ZoomModePanArea)
 564 	restrainCursor (out);
 565 }
 566 
 567 /* Zooms the area described.
 568  * The math could probably be cleaned up, but should be correct now. */
 569 void
 570 EZoomScreen::setZoomArea (int        x,
 571 	     		 int        y,
 572 	     		 int        width,
 573 	     		 int        height,
 574 	     		 bool       instant)
 575 {
 576     CompWindow::Geometry outGeometry (x, y, width, height, 0);
 577     int         out = screen->outputDeviceForGeometry (outGeometry);
 578     CompOutput  *o = &screen->outputDevs ().at (out);
 579 
 580     if (zooms.at (out).newZoom == 1.0f)
 581 	return;
 582 
 583     if (zooms.at (out).locked)
 584 	return;
 585     zooms.at (out).xTranslate =
 586 	 (float) -((o->width () / 2) - (x + (width / 2) - o->x1 ()))
 587 	/ (o->width ());
 588     zooms.at (out).xTranslate /= (1.0f - zooms.at (out).newZoom);
 589     zooms.at (out).yTranslate =
 590 	(float) -((o->height () / 2) - (y + (height / 2) - o->y1 ()))
 591 	/ (o->height ());
 592     zooms.at (out).yTranslate /= (1.0f - zooms.at (out).newZoom);
 593     constrainZoomTranslate ();
 594 
 595     if (instant)
 596     {
 597 	zooms.at (out).realXTranslate = zooms.at (out).xTranslate;
 598 	zooms.at (out).realYTranslate = zooms.at (out).yTranslate;
 599 	zooms.at (out).updateActualTranslates ();
 600     }
 601 
 602     if (optionGetZoomMode () == EzoomOptions::ZoomModePanArea)
 603 	restrainCursor (out);
 604 }
 605 
 606 /* Moves the zoom area to the window specified */
 607 void
 608 EZoomScreen::areaToWindow (CompWindow *w)
 609 {
 610     int left = w->serverX () - w->border ().left;
 611     int width = w->width () + w->border ().left + w->border ().right;
 612     int top = w->serverY () - w->border ().top;
 613     int height = w->height ()  + w->border ().top + w->border ().bottom;
 614 
 615     setZoomArea (left, top, width, height, false);
 616 }
 617 
 618 /* Pans the zoomed area vertically/horizontally by * value * zs->panFactor
 619  * TODO: Fix output. */
 620 void
 621 EZoomScreen::panZoom (int xvalue, int yvalue)
 622 {
 623     unsigned int out;
 624 
 625     for (out = 0; out < zooms.size (); out++)
 626     {
 627 	zooms.at (out).xTranslate +=
 628 	    optionGetPanFactor () * xvalue *
 629 	    zooms.at (out).currentZoom;
 630 	zooms.at (out).yTranslate +=
 631 	    optionGetPanFactor () * yvalue *
 632 	    zooms.at (out).currentZoom;
 633     }
 634 
 635     constrainZoomTranslate ();
 636 }
 637 
 638 /* Enables polling of mouse position, and refreshes currently
 639  * stored values.
 640  */
 641 void
 642 EZoomScreen::enableMousePolling ()
 643 {
 644     pollHandle.start ();
 645     lastChange = time(NULL);
 646     mouse = MousePoller::getCurrentPosition ();
 647 }
 648 
 649 /* Sets the zoom (or scale) level.
 650  * Cleans up if we are suddenly zoomed out.
 651  */
 652 void
 653 EZoomScreen::setScale (int out, float value)
 654 {
 655     if (zooms.at (out).locked)
 656 	return;
 657 
 658     if (value >= 1.0f)
 659 	value = 1.0f;
 660     else
 661     {
 662 	if (!pollHandle.active ())
 663 	    enableMousePolling ();
 664 	grabbed |= (1 << zooms.at (out).output);
 665 	cursorZoomActive (out);
 666     }
 667 
 668     if (value == 1.0f)
 669     {
 670 	zooms.at (out).xTranslate = 0.0f;
 671 	zooms.at (out).yTranslate = 0.0f;
 672 	cursorZoomInactive ();
 673     }
 674 
 675     if (value < optionGetMinimumZoom ())
 676 	value = optionGetMinimumZoom ();
 677 
 678     zooms.at (out).newZoom = value;
 679     cScreen->damageScreen();
 680 }
 681 
 682 /* Sets the zoom factor to the bigger of the two floats supplied.
 683  * Convenience function for setting the scale factor for an area.
 684  */
 685 static inline void
 686 setScaleBigger (int out, float x, float y)
 687 {
 688     ZOOM_SCREEN (screen);
 689     zs->setScale (out, x > y ? x : y);
 690 }
 691 
 692 /* Mouse code...
 693  * This takes care of keeping the mouse in sync with the zoomed area and
 694  * vice versa.
 695  * See heading for description.
 696  */
 697 
 698 /* Syncs the center, based on translations, back to the mouse.
 699  * This should be called when doing non-IR zooming and moving the zoom
 700  * area based on events other than mouse movement.
 701  */
 702 void
 703 EZoomScreen::syncCenterToMouse ()
 704 {
 705     int         x, y;
 706     int         out;
 707     CompOutput  *o;
 708 
 709     out = screen->outputDeviceForPoint (mouse.x (), mouse.y ());
 710     o = &screen->outputDevs ().at (out);
 711 
 712     if (!isInMovement (out))
 713 	return;
 714 
 715     x = (int) ((zooms.at (out).realXTranslate * o->width ()) +
 716 	       (o->width () / 2) + o->x1 ());
 717     y = (int) ((zooms.at (out).realYTranslate * o->height ()) +
 718 	       (o->height () / 2) + o->y1 ());
 719 
 720     if ((x != mouse.x () || y != mouse.y ())
 721 	&& grabbed && zooms.at (out).newZoom != 1.0f)
 722     {
 723 	screen->warpPointer (x - pointerX , y - pointerY );
 724 	mouse.setX (x);
 725 	mouse.setY (y);
 726     }
 727 }
 728 
 729 /* Convert the point X,Y to where it would be when zoomed.  */
 730 void
 731 EZoomScreen::convertToZoomed (int        out,
 732 			     int        x,
 733 			     int        y,
 734 			     int        *resultX,
 735 			     int        *resultY)
 736 {
 737     CompOutput *o;
 738 
 739     if (!outputIsZoomArea (out))
 740     {
 741 	*resultX = x;
 742 	*resultY = y;
 743     }
 744 
 745     o = &screen->outputDevs ()[out];
 746     ZoomArea    &za = zooms.at (out);
 747 
 748     x -= o->x1 ();
 749     y -= o->y1 ();
 750     *resultX = x - (za.realXTranslate *
 751 		    (1.0f - za.currentZoom) * o->width ()) - o->width () / 2;
 752     *resultX /= za.currentZoom;
 753     *resultX += o->width () / 2;
 754     *resultX += o->x1 ();
 755     *resultY = y - (za.realYTranslate *
 756 		    (1.0f - za.currentZoom) * o->height ()) - o->height ()/ 2;
 757     *resultY /= za.currentZoom;
 758     *resultY += o->height ()/ 2;
 759     *resultY += o->y1 ();
 760 }
 761 
 762 /* Same but use targeted translation, not real */
 763 void
 764 EZoomScreen::convertToZoomedTarget (int	  out,
 765 			           int	  x,
 766 			           int	  y,
 767 			           int	  *resultX,
 768 			           int	  *resultY)
 769 {
 770     CompOutput *o = &screen->outputDevs ().at (out);
 771 
 772     if (!outputIsZoomArea (out))
 773     {
 774 	*resultX = x;
 775 	*resultY = y;
 776     }
 777 
 778     ZoomArea    &za = zooms.at (out);
 779 
 780     x -= o->x1 ();
 781     y -= o->y1 ();
 782     *resultX = x - (za.xTranslate *
 783 		    (1.0f - za.newZoom) * o->width ()) - o->width () / 2;
 784     *resultX /= za.newZoom;
 785     *resultX += o->width () / 2;
 786     *resultX += o->x1 ();
 787     *resultY = y - (za.yTranslate *
 788 		    (1.0f - za.newZoom) * o->height ()) - o->height ()/2;
 789     *resultY /= za.newZoom;
 790     *resultY += o->height () / 2;
 791     *resultY += o->y1 ();
 792 }
 793 
 794 /* Make sure the given point + margin is visible;
 795  * Translate to make it visible if necessary.
 796  * Returns false if the point isn't on a actively zoomed head
 797  * or the area is locked. */
 798 bool
 799 EZoomScreen::ensureVisibility (int x, int y, int margin)
 800 {
 801     int         zoomX, zoomY;
 802     int         out;
 803     CompOutput  *o;
 804 
 805     out = screen->outputDeviceForPoint (x, y);
 806     if (!isActive (out))
 807 	return false;
 808 
 809     o = &screen->outputDevs ().at (out);
 810     convertToZoomedTarget (out, x, y, &zoomX, &zoomY);
 811     ZoomArea &za = zooms.at (out);
 812     if (za.locked)
 813 	return false;
 814 
 815 #define FACTOR (za.newZoom / (1.0f - za.newZoom))
 816     if (zoomX + margin > o->x2 ())
 817 	za.xTranslate +=
 818 	    (FACTOR * (float) (zoomX + margin - o->x2 ())) /
 819 	    (float) o->width ();
 820     else if (zoomX - margin < o->x1 ())
 821 	za.xTranslate +=
 822 	    (FACTOR * (float) (zoomX - margin - o->x1 ())) /
 823 	    (float) o->width ();
 824 
 825     if (zoomY + margin > o->y2 ())
 826 	za.yTranslate +=
 827 	    (FACTOR * (float) (zoomY + margin - o->y2 ())) /
 828 	    (float) o->height ();
 829     else if (zoomY - margin < o->y1 ())
 830 	za.yTranslate +=
 831 	    (FACTOR * (float) (zoomY - margin - o->y1 ())) /
 832 	    (float) o->height ();
 833 #undef FACTOR
 834     constrainZoomTranslate ();
 835     return true;
 836 }
 837 
 838 /* Attempt to ensure the visibility of an area defined by x1/y1 and x2/y2.
 839  * See ensureVisibility () for details.
 840  *
 841  * This attempts to find the translations that leaves the biggest part of
 842  * the area visible.
 843  *
 844  * gravity defines what part of the window that should get
 845  * priority if it isn't possible to fit all of it.
 846  */
 847 void
 848 EZoomScreen::ensureVisibilityArea (int         x1,
 849 				  int         y1,
 850 				  int         x2,
 851 				  int         y2,
 852 				  int         margin,
 853 				  ZoomGravity gravity)
 854 {
 855     int        targetX, targetY, targetW, targetH;
 856     int        out;
 857     CompOutput *o;
 858 
 859     out = screen->outputDeviceForPoint (x1 + (x2-x1/2), y1 + (y2-y1/2));
 860     o = &screen->outputDevs ().at (out);
 861 
 862 #define WIDTHOK (float)(x2-x1) / (float)o->width () < zooms.at (out).newZoom
 863 #define HEIGHTOK (float)(y2-y1) / (float)o->height () < zooms.at (out).newZoom
 864 
 865     if (WIDTHOK &&
 866 	HEIGHTOK) {
 867 	ensureVisibility (x1, y1, margin);
 868 	ensureVisibility (x2, y2, margin);
 869 	return;
 870     }
 871 
 872     switch (gravity)
 873     {
 874 	case NORTHWEST:
 875 	    targetX = x1;
 876 	    targetY = y1;
 877 	    if (WIDTHOK)
 878 		targetW = x2 - x1;
 879 	    else
 880 		targetW = o->width () * zooms.at (out).newZoom;
 881 	    if (HEIGHTOK)
 882 		targetH = y2 - y1;
 883 	    else
 884 		targetH = o->height () * zooms.at (out).newZoom;
 885 	    break;
 886 	case NORTHEAST:
 887 	    targetY = y1;
 888 	    if (WIDTHOK)
 889 	    {
 890 		targetX = x1;
 891 		targetW = x2-x1;
 892 	    }
 893 	    else
 894 	    {
 895 		targetX = x2 - o->width () * zooms.at (out).newZoom;
 896 		targetW = o->width () * zooms.at (out).newZoom;
 897 	    }
 898 
 899 	    if (HEIGHTOK)
 900 		targetH = y2-y1;
 901 	    else
 902 		targetH = o->height () * zooms.at (out).newZoom;
 903 	    break;
 904 	case SOUTHWEST:
 905 	    targetX = x1;
 906 	    if (WIDTHOK)
 907 		targetW = x2-x1;
 908 	    else
 909 		targetW = o->width () * zooms.at (out).newZoom;
 910 	    if (HEIGHTOK)
 911 	    {
 912 		targetY = y1;
 913 		targetH = y2-y1;
 914 	    }
 915 	    else
 916 	    {
 917 		targetY = y2 - (o->width () * zooms.at (out).newZoom);
 918 		targetH = o->width () * zooms.at (out).newZoom;
 919 	    }
 920 	    break;
 921 	case SOUTHEAST:
 922 	    if (WIDTHOK)
 923 	    {
 924 		targetX = x1;
 925 		targetW = x2-x1;
 926 	    }
 927 	    else
 928 	    {
 929 		targetW = o->width () * zooms.at (out).newZoom;
 930 		targetX = x2 - targetW;
 931 	    }
 932 
 933 	    if (HEIGHTOK)
 934 	    {
 935 		targetY = y1;
 936 		targetH = y2 - y1;
 937 	    }
 938 	    else
 939 	    {
 940 		targetH = o->height () * zooms.at (out).newZoom;
 941 		targetY = y2 - targetH;
 942 	    }
 943 	    break;
 944 	case CENTER:
 945 	default:
 946 	    setCenter (x1 + (x2 - x1 / 2), y1 + (y2 - y1 / 2), false);
 947 	    return;
 948 	    break;
 949     }
 950 
 951     setZoomArea (targetX, targetY, targetW, targetH, false);
 952     return ;
 953 }
 954 
 955 /* Ensures that the cursor is visible on the given head.
 956  * Note that we check if currentZoom is 1.0f, because that often means that
 957  * mouseX and mouseY is not up-to-date (since the polling timer just
 958  * started).
 959  */
 960 void
 961 EZoomScreen::restrainCursor (int out)
 962 {
 963     int         x1, y1, x2, y2, margin;
 964     int         diffX = 0, diffY = 0;
 965     int         north, south, east, west;
 966     float       z;
 967     CompOutput  *o = &screen->outputDevs ().at (out);
 968 
 969     z = zooms.at (out).newZoom;
 970     margin = optionGetRestrainMargin ();
 971     north = distanceToEdge (out, NORTH);
 972     south = distanceToEdge (out, SOUTH);
 973     east = distanceToEdge (out, EAST);
 974     west = distanceToEdge (out, WEST);
 975 
 976     if (zooms.at (out).currentZoom == 1.0f)
 977     {
 978 	lastChange = time(NULL);
 979 	mouse = MousePoller::getCurrentPosition ();
 980     }
 981 
 982     convertToZoomedTarget (out, mouse.x () - cursor.hotX,
 983 			   mouse.y () - cursor.hotY, &x1, &y1);
 984     convertToZoomedTarget
 985 	(out,
 986 	 mouse.x () - cursor.hotX + cursor.width,
 987 	 mouse.y () - cursor.hotY + cursor.height,
 988 	 &x2, &y2);
 989 
 990     if ((x2 - x1 > o->x2 () - o->x1 ()) ||
 991        (y2 - y1 > o->y2 () - o->y1 ()))
 992 	return;
 993     if (x2 > o->x2 () - margin && east > 0)
 994 	diffX = x2 - o->x2 () + margin;
 995     else if (x1 < o->x1 () + margin && west > 0)
 996 	diffX = x1 - o->x1 () - margin;
 997 
 998     if (y2 > o->y2 () - margin && south > 0)
 999 	diffY = y2 - o->y2 () + margin;
1000     else if (y1 < o->y1 () + margin && north > 0)
1001 	diffY = y1 - o->y1 () - margin;
1002 
1003     if (abs(diffX)*z > 0  || abs(diffY)*z > 0)
1004 	screen->warpPointer ((int) (mouse.x () - pointerX) -
1005 						       (int) ((float)diffX * z),
1006 		     	     (int) (mouse.y () - pointerY) -
1007 						      (int) ((float)diffY * z));
1008 }
1009 
1010 /* Check if the cursor is still visible.
1011  * We also make sure to activate/deactivate cursor scaling here
1012  * so we turn on/off the pointer if it moves from one head to another.
1013  * FIXME: Detect an actual output change instead of spamming.
1014  * FIXME: The second ensureVisibility (sync with restrain).
1015  */
1016 void
1017 EZoomScreen::cursorMoved ()
1018 {
1019     int         out;
1020 
1021     out = screen->outputDeviceForPoint (mouse.x (), mouse.y ());
1022     if (isActive (out))
1023     {
1024 	if (optionGetRestrainMouse ())
1025 	    restrainCursor (out);
1026 
1027 	if (optionGetZoomMode () == EzoomOptions::ZoomModePanArea)
1028 	{
1029 	    ensureVisibilityArea (mouse.x () - cursor.hotX,
1030 				  mouse.y () - cursor.hotY,
1031 				  mouse.x () + cursor.width -
1032 				  cursor.hotX,
1033 				  mouse.y () + cursor.height -
1034 				  cursor.hotY,
1035 				  optionGetRestrainMargin (),
1036 				  NORTHWEST);
1037 	}
1038 
1039 	cursorZoomActive (out);
1040     }
1041     else
1042     {
1043 	cursorZoomInactive ();
1044     }
1045 }
1046 
1047 /* Update the mouse position.
1048  * Based on the zoom engine in use, we will have to move the zoom area.
1049  * This might have to be added to a timer.
1050  */
1051 void
1052 EZoomScreen::updateMousePosition (const CompPoint &p)
1053 {
1054     int out;
1055     mouse.setX (p.x ());
1056     mouse.setY (p.y ());
1057     out = screen->outputDeviceForPoint (mouse.x (), mouse.y ());
1058     lastChange = time(NULL);
1059     if (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse &&
1060         !isInMovement (out))
1061 	setCenter (mouse.x (), mouse.y (), true);
1062     cursorMoved ();
1063     cScreen->damageScreen ();
1064 }
1065 
1066 /* Timeout handler to poll the mouse. Returns false (and thereby does not
1067  * get re-added to the queue) when zoom is not active. */
1068 void
1069 EZoomScreen::updateMouseInterval (const CompPoint &p)
1070 {
1071     updateMousePosition (p);
1072 
1073     if (!grabbed)
1074     {
1075 	cursorMoved ();
1076 	if (pollHandle.active ())
1077 	    pollHandle.stop ();
1078     }
1079 }
1080 
1081 /* Free a cursor */
1082 void
1083 EZoomScreen::freeCursor (CursorTexture * cursor)
1084 {
1085     if (!cursor->isSet)
1086 	return;
1087 
1088     cursor->isSet = false;
1089     glDeleteTextures (1, &cursor->texture);
1090     cursor->texture = 0;
1091 }
1092 
1093 /* Translate into place and draw the scaled cursor.  */
1094 void
1095 EZoomScreen::drawCursor (CompOutput          *output,
1096 	    		const GLMatrix      &transform)
1097 {
1098     int         out = output->id ();
1099 
1100     if (cursor.isSet)
1101     {
1102 	GLMatrix      sTransform = transform;
1103 	float	      scaleFactor;
1104 	int           ax, ay, x, y;
1105 	GLfloat         textureData[8];
1106 	GLfloat         vertexData[12];
1107 	GLVertexBuffer *streamingBuffer = GLVertexBuffer::streamingBuffer ();
1108 
1109 	/*
1110 	 * XXX: expo knows how to handle mouse when zoomed, so we back off
1111 	 * when expo is active.
1112 	 */
1113 	if (screen->grabExist ( "expo"))
1114 	{
1115 	    cursorZoomInactive ();
1116 	    return;
1117 	}
1118 
1119 	sTransform.toScreenSpace (output, -DEFAULT_Z_CAMERA);
1120 	convertToZoomed (out, mouse.x (), mouse.y (), &ax, &ay);
1121 	sTransform.translate ((float) ax, (float) ay, 0.0f);
1122 
1123 	if (optionGetScaleMouseDynamic ())
1124 	    scaleFactor = 1.0f / zooms.at (out).currentZoom;
1125 	else
1126 	    scaleFactor = 1.0f / optionGetScaleMouseStatic ();
1127 
1128 	sTransform.scale (scaleFactor, scaleFactor, 1.0f);
1129 	x = -cursor.hotX;
1130 	y = -cursor.hotY;
1131 
1132 	glEnable (GL_BLEND);
1133 	glEnable (GL_TEXTURE_2D);
1134 	glBindTexture (GL_TEXTURE_2D, cursor.texture);
1135 
1136 	streamingBuffer->begin (GL_TRIANGLE_STRIP);
1137 
1138 	vertexData[0]  = x;
1139 	vertexData[1]  = y;
1140 	vertexData[2]  = 0.0f;
1141 	vertexData[3]  = x;
1142 	vertexData[4]  = y + cursor.height;
1143 	vertexData[5]  = 0.0f;
1144 	vertexData[6]  = x + cursor.width;
1145 	vertexData[7]  = y;
1146 	vertexData[8]  = 0.0f;
1147 	vertexData[9]  = x + cursor.width;
1148 	vertexData[10] = y + cursor.height;
1149 	vertexData[11] = 0.0f;
1150 
1151 	streamingBuffer->addVertices (4, vertexData);
1152 
1153 	textureData[0] = 0;
1154 	textureData[1] = 0;
1155 	textureData[2] = 0;
1156 	textureData[3] = 1;
1157 	textureData[4] = 1;
1158 	textureData[5] = 0;
1159 	textureData[6] = 1;
1160 	textureData[7] = 1;
1161 
1162 	streamingBuffer->addTexCoords (0, 4, textureData);
1163 
1164 	streamingBuffer->end ();
1165 	streamingBuffer->render (sTransform);
1166 
1167 	glBindTexture (GL_TEXTURE_2D, 0);
1168 	glDisable (GL_TEXTURE_2D);
1169 	glDisable (GL_BLEND);
1170     }
1171 }
1172 
1173 /* Create (if necessary) a texture to store the cursor,
1174  * fetch the cursor with XFixes. Store it.  */
1175 void
1176 EZoomScreen::updateCursor (CursorTexture * cursor)
1177 {
1178     unsigned char *pixels;
1179     int           i;
1180     Display       *dpy = screen->dpy ();
1181 
1182     if (!cursor->isSet)
1183     {
1184 	cursor->isSet = true;
1185 	cursor->screen = screen;
1186 
1187 	glEnable (GL_TEXTURE_2D);
1188 	glGenTextures (1, &cursor->texture);
1189 	glBindTexture (GL_TEXTURE_2D, cursor->texture);
1190 
1191 	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1192 	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1193 	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
1194 	                 gScreen->textureFilter ());
1195 	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 
1196 	                 gScreen->textureFilter ());
1197     }
1198     else {
1199 	glEnable (GL_TEXTURE_2D);
1200     }
1201 
1202     XFixesCursorImage *ci = XFixesGetCursorImage (dpy);
1203 
1204     if (ci)
1205     {
1206 	cursor->width = ci->width;
1207 	cursor->height = ci->height;
1208 	cursor->hotX = ci->xhot;
1209 	cursor->hotY = ci->yhot;
1210 	pixels = (unsigned char *) malloc (ci->width * ci->height * 4);
1211 
1212 	if (!pixels)
1213 	{
1214 	    XFree (ci);
1215 	    return;
1216 	}
1217 
1218 	for (i = 0; i < ci->width * ci->height; i++)
1219 	{
1220 	    unsigned long pix = ci->pixels[i];
1221 	    pixels[i * 4] = pix & 0xff;
1222 	    pixels[(i * 4) + 1] = (pix >> 8) & 0xff;
1223 	    pixels[(i * 4) + 2] = (pix >> 16) & 0xff;
1224 	    pixels[(i * 4) + 3] = (pix >> 24) & 0xff;
1225 	}
1226 
1227 	XFree (ci);
1228     }
1229     else
1230     {
1231 	/* Fallback R: 255 G: 255 B: 255 A: 255
1232 	 * FIXME: Draw a cairo mouse cursor */
1233 
1234 	cursor->width = 1;
1235 	cursor->height = 1;
1236 	cursor->hotX = 0;
1237 	cursor->hotY = 0;
1238 	pixels = (unsigned char *) malloc (cursor->width * cursor->height * 4);
1239 
1240 	if (!pixels)
1241 	    return;
1242 
1243 	for (i = 0; i < cursor->width * cursor->height; i++)
1244 	{
1245 	    unsigned long pix = 0x00ffffff;
1246 	    pixels[i * 4] = pix & 0xff;
1247 	    pixels[(i * 4) + 1] = (pix >> 8) & 0xff;
1248 	    pixels[(i * 4) + 2] = (pix >> 16) & 0xff;
1249 	    pixels[(i * 4) + 3] = (pix >> 24) & 0xff;
1250 	}
1251 
1252 	compLogMessage ("ezoom", CompLogLevelWarn, "unable to get system cursor image!");
1253     }
1254 
1255     glBindTexture (GL_TEXTURE_2D, cursor->texture);
1256     glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, cursor->width,
1257 		  cursor->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
1258     glBindTexture (GL_TEXTURE_2D, 0);
1259     glDisable (GL_TEXTURE_2D);
1260 	
1261     free (pixels);
1262 }
1263 
1264 /* We are no longer zooming the cursor, so display it.  */
1265 void
1266 EZoomScreen::cursorZoomInactive ()
1267 {
1268     if (!fixesSupported)
1269 	return;
1270 
1271     if (cursorInfoSelected)
1272     {
1273 	cursorInfoSelected = false;
1274 	XFixesSelectCursorInput (screen->dpy (), screen->root (), 0);
1275     }
1276 
1277     if (cursor.isSet)
1278     {
1279 	freeCursor (&cursor);
1280     }
1281 
1282     if (cursorHidden)
1283     {
1284 	cursorHidden = false;
1285 	XFixesShowCursor (screen->dpy (), screen->root ());
1286     }
1287 }
1288 
1289 /* Cursor zoom is active: We need to hide the original,
1290  * register for Cursor notifies and display the new one.
1291  * This can be called multiple times, not just on initial
1292  * activation.
1293  */
1294 void
1295 EZoomScreen::cursorZoomActive (int out)
1296 {
1297     if (!fixesSupported)
1298 	return;
1299 
1300     /* Force cursor hiding and mouse panning if this output is locked
1301      * and cursor hiding is not enabled and we are syncing the mouse
1302      */
1303 
1304     if (!optionGetScaleMouse () &&
1305         (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse &&
1306 	 optionGetHideOriginalMouse () &&
1307 	 !zooms.at (out).locked))
1308 	return;
1309 
1310     if (!cursorInfoSelected)
1311     {
1312 	cursorInfoSelected = true;
1313         XFixesSelectCursorInput (screen->dpy (), screen->root (),
1314 				 XFixesDisplayCursorNotifyMask);
1315 	updateCursor (&cursor);
1316     }
1317     if (canHideCursor && !cursorHidden &&
1318 	(optionGetHideOriginalMouse () ||
1319 	 zooms.at (out).locked))
1320     {
1321 	cursorHidden = true;
1322 	XFixesHideCursor (screen->dpy (), screen->root ());
1323     }
1324 }
1325 
1326 /* Set the zoom area
1327  * This is an interface for scripting.
1328  * int32:x1: left x coordinate
1329  * int32:y1: top y coordinate
1330  * int32:x2: right x
1331  * int32:y2: bottom y
1332  * x2 and y2 can be omitted to assume x1==x2+1 y1==y2+1
1333  * boolean:scale: True if we should modify the zoom level, false to just
1334  *                adjust the movement/translation.
1335  * boolean:restrain: True to warp the pointer so it's visible.
1336  */
1337 bool
1338 EZoomScreen::setZoomAreaAction (CompAction         *action,
1339 			       CompAction::State  state,
1340 			       CompOption::Vector options)
1341 {
1342     int        x1, y1, x2, y2, out;
1343     bool       scale, restrain;
1344     CompOutput *o;
1345 
1346     x1 = CompOption::getIntOptionNamed (options, "x1", -1);
1347     y1 = CompOption::getIntOptionNamed (options, "y1", -1);
1348     x2 = CompOption::getIntOptionNamed (options, "x2", -1);
1349     y2 = CompOption::getIntOptionNamed (options, "y2", -1);
1350     scale = CompOption::getBoolOptionNamed (options, "scale", false);
1351     restrain = CompOption::getBoolOptionNamed (options, "restrain", false);
1352 
1353     if (x1 < 0 || y1 < 0)
1354         return false;
1355 
1356     if (x2 < 0)
1357         x2 = x1 + 1;
1358 
1359     if (y2 < 0)
1360         y2 = y1 + 1;
1361 
1362     out = screen->outputDeviceForPoint (x1, y1);
1363 #define WIDTH (x2 - x1)
1364 #define HEIGHT (y2 - y1)
1365     setZoomArea (x1, y1, WIDTH, HEIGHT, false);
1366     o = &screen->outputDevs (). at(out);
1367     if (scale && WIDTH && HEIGHT)
1368         setScaleBigger (out, (float) WIDTH / o->width (),
1369 		        (float) HEIGHT / o->height ());
1370 #undef WIDTH
1371 #undef HEIGHT
1372 	if (restrain)
1373 	    restrainCursor (out);
1374 
1375     toggleFunctions (true);
1376 
1377     return true;
1378 }
1379 
1380 /* Ensure visibility of an area defined by x1->x2/y1->y2
1381  * int:x1: left X coordinate
1382  * int:x2: right X Coordinate
1383  * int:y1: top Y coordinate
1384  * int:y2: bottom Y coordinate
1385  * bool:scale: zoom out if necessary to ensure visibility
1386  * bool:restrain: Restrain the mouse cursor
1387  * int:margin: The margin to use (default: 0)
1388  * if x2/y2 is omitted, it is ignored.
1389  */
1390 bool
1391 EZoomScreen::ensureVisibilityAction (CompAction         *action,
1392 			            CompAction::State  state,
1393 			            CompOption::Vector options)
1394 {
1395     int        x1, y1, x2, y2, margin, out;
1396     bool       scale, restrain;
1397     CompOutput *o;
1398 
1399     x1 = CompOption::getIntOptionNamed (options, "x1", -1);
1400     y1 = CompOption::getIntOptionNamed (options, "y1", -1);
1401     x2 = CompOption::getIntOptionNamed (options, "x2", -1);
1402     y2 = CompOption::getIntOptionNamed (options, "y2", -1);
1403     margin = CompOption::getBoolOptionNamed (options, "margin", 0);
1404     scale = CompOption::getBoolOptionNamed (options, "scale", false);
1405     restrain = CompOption::getBoolOptionNamed (options, "restrain", false);
1406     if (x1 < 0 || y1 < 0)
1407         return false;
1408     if (x2 < 0)
1409         y2 = y1 + 1;
1410     out = screen->outputDeviceForPoint (x1, y1);
1411     ensureVisibility (x1, y1, margin);
1412     if (x2 >= 0 && y2 >= 0)
1413         ensureVisibility (x2, y2, margin);
1414     o = &screen->outputDevs (). at(out);
1415 #define WIDTH (x2 - x1)
1416 #define HEIGHT (y2 - y1)
1417     if (scale && WIDTH && HEIGHT)
1418         setScaleBigger (out, (float) WIDTH / o->width (),
1419 		        (float) HEIGHT / o->height ());
1420 #undef WIDTH
1421 #undef HEIGHT
1422     if (restrain)
1423 	restrainCursor (out);
1424 
1425     toggleFunctions (true);
1426 
1427     return true;
1428 }
1429 
1430 /* Finished here */
1431 
1432 bool
1433 EZoomScreen::zoomBoxActivate (CompAction         *action,
1434 			     CompAction::State  state,
1435 			     CompOption::Vector options)
1436 {
1437     grabIndex = screen->pushGrab (None, "ezoom");
1438     clickPos.setX (pointerX);
1439     clickPos.setY (pointerY);
1440     box.setGeometry (pointerX, pointerY, 0, 0);
1441     if (state & CompAction::StateInitButton)
1442         action->setState (action->state () | CompAction::StateTermButton);
1443 
1444     toggleFunctions (true);
1445 
1446     return true;
1447 }
1448 
1449 bool
1450 EZoomScreen::zoomBoxDeactivate (CompAction         *action,
1451 			       CompAction::State  state,
1452 			       CompOption::Vector options)
1453 {
1454     if (grabIndex)
1455     {
1456         int        out;
1457 	int	   x, y, width, height;
1458         CompOutput *o;
1459 
1460         screen->removeGrab (grabIndex, NULL);
1461         grabIndex = 0;
1462 
1463         if (pointerX < clickPos.x ())
1464         {
1465 	    box.setX (pointerX);
1466 	    box.setWidth (clickPos.x () - pointerX);
1467         }
1468         else
1469         {
1470 	    box.setWidth (pointerX - clickPos.x ());
1471         }
1472 
1473         if (pointerY < clickPos.y ())
1474         {
1475 	    box.setY (pointerY);
1476 	    box.setHeight (clickPos.y () - pointerY);
1477         }
1478         else
1479         {
1480 	    box.setHeight (pointerY - clickPos.y ());
1481         }
1482 
1483         x = MIN (box.x1 (), box.x2 ());
1484         y = MIN (box.y1 (), box.y2 ());
1485         width = MAX (box.x1 (), box.x2 ()) - x;
1486         height = MAX (box.y1 (), box.y2 ()) - y;
1487 
1488         CompWindow::Geometry outGeometry (x, y, width, height, 0);
1489 
1490         out = screen->outputDeviceForGeometry (outGeometry);
1491         o = &screen->outputDevs (). at (out);
1492         setScaleBigger (out, (float) width/o->width (), (float)
1493 		        height/o->height ());
1494         setZoomArea (x,y,width,height,false);
1495     }
1496 
1497     toggleFunctions (true);
1498 
1499     return true;
1500 }
1501 
1502 /* Zoom in to the area pointed to by the mouse.
1503  */
1504 bool
1505 EZoomScreen::zoomIn (CompAction         *action,
1506 		    CompAction::State  state,
1507 		    CompOption::Vector options)
1508 {
1509     int out = screen->outputDeviceForPoint (pointerX, pointerY);
1510 
1511     if (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse &&
1512 	!isInMovement (out))
1513         setCenter (pointerX, pointerY, true);
1514 
1515     setScale (out,
1516 	      zooms.at (out).newZoom /
1517 	      optionGetZoomFactor ());
1518 
1519     toggleFunctions (true);
1520 
1521     return true;
1522 }
1523 
1524 /* Locks down the current zoom area
1525  */
1526 bool
1527 EZoomScreen::lockZoomAction (CompAction         *action,
1528 			    CompAction::State  state,
1529 			    CompOption::Vector options)
1530 {
1531     int out = screen->outputDeviceForPoint (pointerX, pointerY);
1532     zooms.at (out).locked = !zooms.at (out).locked;
1533 
1534     return true;
1535 }
1536 
1537 /* Zoom to a specific level.
1538  * target defines the target zoom level.
1539  * First set the scale level and mark the display as grabbed internally (to
1540  * catch the FocusIn event). Either target the focused window or the mouse,
1541  * depending on settings.
1542  * FIXME: A bit of a mess...
1543  */
1544 bool
1545 EZoomScreen::zoomSpecific (CompAction         *action,
1546 			  CompAction::State  state,
1547 			  CompOption::Vector options,
1548 			  SpecificZoomTarget target)
1549 {
1550     int          out = screen->outputDeviceForPoint (pointerX, pointerY);
1551     float        zoom_level;
1552     CompWindow   *w;
1553 
1554     switch (target)
1555     {
1556 	case ZoomTargetFirst:
1557 	    zoom_level = optionGetZoomSpec1 ();
1558 	    break;
1559 	case ZoomTargetSecond:
1560 	    zoom_level = optionGetZoomSpec2 ();
1561 	    break;
1562 	case ZoomTargetThird:
1563 	    zoom_level = optionGetZoomSpec3 ();
1564 	    break;
1565 	default:
1566 	    return false;
1567     }
1568 
1569     if (zoom_level == 1.0f && zooms.at (out).newZoom == 1.0f)
1570         return false;
1571     if (screen->otherGrabExist (NULL))
1572         return false;
1573 
1574     setScale (out, zoom_level);
1575 
1576     w = screen->findWindow (screen->activeWindow ());
1577     if (optionGetSpecTargetFocus ()
1578         && w)
1579     {
1580         areaToWindow (w);
1581     }
1582     else
1583     {
1584         int x = CompOption::getIntOptionNamed (options, "x", 0);
1585         int y = CompOption::getIntOptionNamed (options, "y", 0);
1586         setCenter (x, y, false);
1587     }
1588 
1589     toggleFunctions (true);
1590 
1591     return true;
1592 }
1593 
1594 /* TODO: Add specific zoom boost::bind's */
1595 
1596 /* Zooms to fit the active window to the screen without cutting
1597  * it off and targets it.
1598  */
1599 bool
1600 EZoomScreen::zoomToWindow (CompAction         *action,
1601 			  CompAction::State  state,
1602 			  CompOption::Vector options)
1603 {
1604     int        width, height, out;
1605     Window     xid;
1606     CompWindow *w;
1607     CompOutput *o;
1608 
1609     xid = CompOption::getIntOptionNamed (options, "window", 0);
1610     w = screen->findWindow (xid);
1611     if (!w)
1612 	return true;
1613     width = w->width () + w->border ().left + w->border ().right;
1614     height = w->height () + w->border ().top + w->border ().bottom;
1615     out = screen->outputDeviceForGeometry (w->geometry ());
1616     o = &screen->outputDevs ().at (out);
1617     setScaleBigger (out, (float) width/o->width (),
1618 		    (float) height/o->height ());
1619     areaToWindow (w);
1620     toggleFunctions (true);
1621 
1622     return true;
1623 }
1624 
1625 bool
1626 EZoomScreen::zoomPan (CompAction         *action,
1627 		     CompAction::State  state,
1628 		     CompOption::Vector options,
1629 		     float		horizAmount,
1630 		     float		vertAmount)
1631 {
1632     panZoom (horizAmount, vertAmount);
1633 
1634     return true;
1635 }
1636 
1637 /* Centers the mouse based on zoom level and translation.
1638  */
1639 bool
1640 EZoomScreen::zoomCenterMouse (CompAction         *action,
1641 			     CompAction::State  state,
1642 			     CompOption::Vector options)
1643 {
1644     int        out;
1645 
1646 
1647     out = screen->outputDeviceForPoint (pointerX, pointerY);
1648     screen->warpPointer ((int) (screen->outputDevs ().at (out).width ()/2 +
1649 			screen->outputDevs ().at (out).x1 () - pointerX)
1650 			 + ((float) screen->outputDevs ().at (out).width () *
1651 				-zooms.at (out).xtrans),
1652 			 (int) (screen->outputDevs ().at (out).height ()/2 +
1653 				screen->outputDevs ().at (out).y1 () - pointerY)
1654 			 + ((float) screen->outputDevs ().at (out).height () *
1655 				zooms.at (out).ytrans));
1656     return true;
1657 }
1658 
1659 /* Resize a window to fit the zoomed area.
1660  * This could probably do with some moving-stuff too.
1661  * IE: Move the zoom area afterwards. And ensure
1662  * the window isn't resized off-screen.
1663  */
1664 bool
1665 EZoomScreen::zoomFitWindowToZoom (CompAction         *action,
1666 			         CompAction::State  state,
1667 			         CompOption::Vector options)
1668 {
1669     int            out;
1670     unsigned int   mask = CWWidth | CWHeight;
1671     XWindowChanges xwc;
1672     CompWindow     *w;
1673 
1674     w = screen->findWindow (CompOption::getIntOptionNamed (
1675 							 options, "window", 0));
1676     if (!w)
1677 	return true;
1678 
1679     out = screen->outputDeviceForGeometry (w->geometry ());
1680     xwc.x = w->serverX ();
1681     xwc.y = w->serverY ();
1682     xwc.width = (int) (screen->outputDevs ().at (out).width () *
1683 		       zooms.at (out).currentZoom -
1684 		       (int) ((w->border ().left + w->border ().right)));
1685     xwc.height = (int) (screen->outputDevs ().at (out).height () *
1686 			zooms.at (out).currentZoom -
1687 			(int) ((w->border ().top + w->border ().bottom)));
1688 
1689     w->constrainNewWindowSize (xwc.width,
1690 			       xwc.height,
1691 			       &xwc.width,
1692 			       &xwc.height);
1693 
1694 
1695     if (xwc.width == w->serverWidth ())
1696 	mask &= ~CWWidth;
1697 
1698     if (xwc.height == w->serverHeight ())
1699 	mask &= ~CWHeight;
1700 
1701     if (w->mapNum () && (mask & (CWWidth | CWHeight)))
1702 	w->sendSyncRequest ();
1703 
1704     w->configureXWindow (mask, &xwc);
1705 
1706     toggleFunctions (true);
1707 
1708     return true;
1709 }
1710 
1711 bool
1712 EZoomScreen::initiate (CompAction         *action,
1713 		      CompAction::State  state,
1714 		      CompOption::Vector options)
1715 {
1716     zoomIn (action, state, options);
1717 
1718     if (state & CompAction::StateInitKey)
1719 	action->setState (action->state () | CompAction::StateTermKey);
1720 
1721     if (state & CompAction::StateInitButton)
1722 	action->setState (action->state () | CompAction::StateTermButton);
1723 
1724     toggleFunctions (true);
1725 
1726     return true;
1727 }
1728 
1729 bool
1730 EZoomScreen::zoomOut (CompAction         *action,
1731 		     CompAction::State  state,
1732 		     CompOption::Vector options)
1733 {
1734     int out = screen->outputDeviceForPoint (pointerX, pointerY);
1735 
1736     setScale (out,
1737 	      zooms.at (out).newZoom *
1738 	      optionGetZoomFactor ());
1739 
1740     toggleFunctions (true);
1741 
1742     return true;
1743 }
1744 
1745 bool
1746 EZoomScreen::terminate (CompAction         *action,
1747 		       CompAction::State  state,
1748 		       CompOption::Vector options)
1749 {
1750     int out;
1751 
1752     out = screen->outputDeviceForPoint (pointerX, pointerY);
1753 
1754     if (grabbed)
1755     {
1756         zooms.at (out).newZoom = 1.0f;
1757         cScreen->damageScreen ();
1758     }
1759 
1760     toggleFunctions (true);
1761 
1762     action->setState (action->state () & ~(CompAction::StateTermKey |
1763 					   CompAction::StateTermButton));
1764     return false;
1765 
1766 }
1767 
1768 /* Focus-track related event handling.
1769  * The lastMapped is a hack to ensure that newly mapped windows are
1770  * caught even if the grab that (possibly) triggered them affected
1771  * the mode. Windows created by a key binding (like creating a terminal
1772  * on a key binding) tends to trigger FocusIn events with mode other than
1773  * Normal. This works around this problem.
1774  * FIXME: Cleanup.
1775  * TODO: Avoid maximized windows.
1776  */
1777 void
1778 EZoomScreen::focusTrack (XEvent *event)
1779 {
1780     int           out;
1781     static Window lastMapped = 0;
1782 
1783     CompWindow    *w;
1784 
1785     if (event->type == MapNotify)
1786     {
1787 	lastMapped = event->xmap.window;
1788 	return;
1789     }
1790     else if (event->type != FocusIn)
1791 	return;
1792 
1793     if ((event->xfocus.mode != NotifyNormal)
1794 	&& (lastMapped != event->xfocus.window))
1795 	return;
1796 
1797     lastMapped = 0;
1798     w = screen->findWindow (event->xfocus.window);
1799     if (w == NULL || w->id () == screen->activeWindow ())
1800 	return;
1801 
1802     if (time(NULL) - lastChange < optionGetFollowFocusDelay () ||
1803 	!optionGetFollowFocus ())
1804 	return;
1805 
1806     out = screen->outputDeviceForGeometry (w->geometry ());
1807     if (!isActive (out) &&
1808 	!optionGetAlwaysFocusFitWindow ())
1809 	return;
1810     if (optionGetFocusFitWindow ())
1811     {
1812 	int width = w->width () + w->border ().left + w->border ().right;
1813 	int height = w->height () + w->border ().top + w->border ().bottom;
1814 	float scale = MAX ((float) width/screen->outputDevs ().at(out).width (),
1815 			   (float) height/screen->outputDevs ().at (out).height ());
1816 	if (scale > optionGetAutoscaleMin ())
1817 		setScale (out, scale);
1818     }
1819 
1820     areaToWindow (w);
1821 
1822     toggleFunctions (true);
1823 }
1824 
1825 
1826 /* Event handler. Pass focus-related events on and handle XFixes events. */
1827 void
1828 EZoomScreen::handleEvent (XEvent *event)
1829 {
1830     switch (event->type) {
1831 	case MotionNotify:
1832 	    if (grabIndex)
1833 	    {
1834 	        if (pointerX < clickPos.x ())
1835 	        {
1836 		    box.setX (pointerX);
1837 		    box.setWidth (clickPos.x () - pointerX);
1838 	        }
1839 	        else
1840 	        {
1841 		    box.setWidth (pointerX - clickPos.x ());
1842 	        }
1843 
1844 	        if (pointerY < clickPos.y ())
1845 	        {
1846 		    box.setY (pointerY);
1847 		    box.setHeight (clickPos.y () - pointerY);
1848 	        }
1849 	        else
1850 	        {
1851 		    box.setHeight (pointerY - clickPos.y ());
1852 	        }
1853 	        cScreen->damageScreen ();
1854 	    }
1855 	    break;
1856 
1857 	case FocusIn:
1858 	case MapNotify:
1859 	    focusTrack (event);
1860 	    break;
1861 	default:
1862 	    if (event->type == fixesEventBase + XFixesCursorNotify)
1863 	    {
1864 		//XFixesCursorNotifyEvent *cev = (XFixesCursorNotifyEvent *)
1865 		    //event;
1866 		    if (cursor.isSet)
1867 			updateCursor (&cursor);
1868 	    }
1869 	    break;
1870     }
1871 
1872     screen->handleEvent (event);
1873 }
1874 
1875 /* TODO: Use this ctor carefully */
1876 
1877 EZoomScreen::CursorTexture::CursorTexture () :
1878     isSet (false),
1879     screen (0),
1880     width (0),
1881     height (0),
1882     hotX (0),
1883     hotY (0)
1884 {
1885 }
1886 
1887 EZoomScreen::EZoomScreen (CompScreen *screen) :
1888     PluginClassHandler <EZoomScreen, CompScreen> (screen),
1889     cScreen (CompositeScreen::get (screen)),
1890     gScreen (GLScreen::get (screen)),
1891     grabbed (0),
1892     grabIndex (0),
1893     lastChange (0),
1894     cursorInfoSelected (false),
1895     cursorHidden (false)
1896 {
1897     ScreenInterface::setHandler (screen, false);
1898     CompositeScreenInterface::setHandler (cScreen, false);
1899     GLScreenInterface::setHandler (gScreen, false);
1900 
1901     int major, minor;
1902     unsigned int n;
1903     fixesSupported =
1904 	XFixesQueryExtension(screen->dpy (),
1905 			     &fixesEventBase,
1906 			     &fixesErrorBase);
1907 
1908     XFixesQueryVersion (screen->dpy (), &major, &minor);
1909 
1910     if (major >= 4)
1911 	canHideCursor = true;
1912     else
1913 	canHideCursor = false;
1914 
1915     n = screen->outputDevs ().size ();
1916 
1917     for (unsigned int i = 0; i < n; i++)
1918     {
1919 	/* zs->grabbed is a mask ... Thus this limit */
1920 	if (i > sizeof (long int) * 8)
1921 	    break;
1922 	ZoomArea za (i);
1923 	zooms.push_back (za);
1924     }
1925 
1926     pollHandle.setCallback (boost::bind (
1927 				&EZoomScreen::updateMouseInterval, this, _1));
1928 
1929     optionSetZoomInButtonInitiate (boost::bind (&EZoomScreen::zoomIn, this, _1,
1930 						_2, _3));
1931     optionSetZoomOutButtonInitiate (boost::bind (&EZoomScreen::zoomOut, this, _1,
1932 						 _2, _3));
1933     optionSetZoomInKeyInitiate (boost::bind (&EZoomScreen::zoomIn, this, _1,
1934 						_2, _3));
1935     optionSetZoomOutKeyInitiate (boost::bind (&EZoomScreen::zoomOut, this, _1,
1936 						_2, _3));
1937 
1938     optionSetZoomSpecific1KeyInitiate (boost::bind (&EZoomScreen::zoomSpecific,
1939 						    this, _1, _2, _3,
1940 						    ZoomTargetFirst));
1941     optionSetZoomSpecific2KeyInitiate (boost::bind (&EZoomScreen::zoomSpecific,
1942 						    this, _1, _2, _3,
1943 						    ZoomTargetSecond));
1944     optionSetZoomSpecific3KeyInitiate (boost::bind (&EZoomScreen::zoomSpecific,
1945 						    this, _1, _2, _3,
1946 						    ZoomTargetThird));
1947 
1948     optionSetPanLeftKeyInitiate (boost::bind (&EZoomScreen::zoomPan, this, _1,
1949 					      _2, _3, -1, 0));
1950     optionSetPanRightKeyInitiate (boost::bind (&EZoomScreen::zoomPan, this, _1,
1951 					        _2, _3, 1, 0));
1952     optionSetPanUpKeyInitiate (boost::bind (&EZoomScreen::zoomPan, this, _1, _2,
1953 					     _3, 0, -1));
1954     optionSetPanDownKeyInitiate (boost::bind (&EZoomScreen::zoomPan, this, _1,
1955 					       _2, _3, 0, 1));
1956 
1957     optionSetFitToWindowKeyInitiate (boost::bind (&EZoomScreen::zoomToWindow,
1958 						  this, _1, _2, _3));
1959     optionSetCenterMouseKeyInitiate (boost::bind (&EZoomScreen::zoomCenterMouse,
1960 						  this, _1, _2, _3));
1961     optionSetFitToZoomKeyInitiate (boost::bind (
1962 					&EZoomScreen::zoomFitWindowToZoom, this,
1963 					_1, _2, _3));
1964 
1965 
1966     optionSetLockZoomKeyInitiate (boost::bind (&EZoomScreen::lockZoomAction,
1967 						this, _1, _2, _3));
1968     optionSetZoomBoxButtonInitiate (boost::bind (&EZoomScreen::zoomBoxActivate,
1969 						 this, _1, _2, _3));
1970     optionSetZoomBoxButtonTerminate (boost::bind (
1971 					&EZoomScreen::zoomBoxDeactivate, this,
1972 					_1, _2, _3));
1973     optionSetSetZoomAreaInitiate (boost::bind (
1974 					&EZoomScreen::setZoomAreaAction, this,
1975 					_1, _2, _3));
1976     optionSetEnsureVisibilityInitiate (boost::bind (
1977 					&EZoomScreen::ensureVisibilityAction, this,
1978 					_1, _2, _3));
1979 
1980 }
1981 
1982 EZoomScreen::~EZoomScreen ()
1983 {
1984     if (pollHandle.active ())
1985 	pollHandle.stop ();
1986 
1987     if (zooms.size ())
1988 	zooms.clear ();
1989 
1990     cScreen->damageScreen ();
1991     cursorZoomInactive ();
1992 }
1993 
1994 bool
1995 ZoomPluginVTable::init ()
1996 {
1997     if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION) ||
1998 	!CompPlugin::checkPluginABI ("composite", COMPIZ_COMPOSITE_ABI) ||
1999 	!CompPlugin::checkPluginABI ("opengl", COMPIZ_OPENGL_ABI) ||
2000 	!CompPlugin::checkPluginABI ("mousepoll", COMPIZ_MOUSEPOLL_ABI))
2001 	return false;
2002 
2003     return true;
2004 }