1 /*
2  * Copyright © 2005 Novell, Inc.
3  * Copyright (C) 2007, 2008 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  *	- Original zoom plug-in; David Reveman <davidr@novell.com>
27  *	- Most features beyond basic zoom;
28  *	  Kristian Lyngstol <kristian@bohemians.org>
29  *
30  * Description:
31  *
32  * This plug-in offers zoom functionality with focus tracking,
33  * fit-to-window actions, mouse panning, zoom area locking. Without
34  * disabling input.
35  *
36  * Note on actual zoom process
37  *
38  * The animation is done in preparePaintScreen, while instant movements
39  * are done by calling updateActualTranslate () after updating the
40  * translations. This causes [xyz]trans to be re-calculated. We keep track
41  * of each head separately.
42  *
43  * Note on input
44  *
45  * We can not redirect input yet, but this plug-in offers two fundamentally
46  * different approaches to achieve input enabled zoom:
47  *
48  * 1.
49  * Always have the zoomed area be in sync with the mouse cursor. This binds
50  * the zoom area to the mouse position at any given time. It allows using
51  * the original mouse cursor drawn by X, and is technically very safe.
52  * First used in Beryl's inputzoom.
53  *
54  * 2.
55  * Hide the real cursor and draw our own where it would be when zoomed in.
56  * This allows us to navigate with the mouse without constantly moving the
57  * zoom area. This is fairly close to what we want in the end when input
58  * redirection is available.
59  *
60  * This second method has one huge issue, which is bugged XFixes. After
61  * hiding the cursor once with XFixes, some mouse cursors will simply be
62  * invisible. The Firefox loading cursor being one of them.
63  *
64  * An other minor annoyance is that mouse sensitivity seems to increase as
65  * you zoom in, since the mouse isn't really zoomed at all.
66  *
67  * Todo:
68  *  - Different multi head modes
69  */
70 
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <math.h>
75 #include <sys/time.h>
76 #include <time.h>
77 
78 #include <compiz-core.h>
79 #include <compiz-mousepoll.h>
80 
81 static CompMetadata zoomMetadata;
82 
83 static int displayPrivateIndex;
84 
85 typedef enum _ZdOpt
86 {
87     DOPT_INITIATE = 0,
88     DOPT_IN,
89     DOPT_OUT,
90     DOPT_IN_KEY,
91     DOPT_OUT_KEY,
92     DOPT_SPECIFIC_1,
93     DOPT_SPECIFIC_2,
94     DOPT_SPECIFIC_3,
95     DOPT_SPECIFIC_LEVEL_1,
96     DOPT_SPECIFIC_LEVEL_2,
97     DOPT_SPECIFIC_LEVEL_3,
98     DOPT_SPECIFIC_TARGET_FOCUS,
99     DOPT_PAN_LEFT,
100     DOPT_PAN_RIGHT,
101     DOPT_PAN_UP,
102     DOPT_PAN_DOWN,
103     DOPT_FIT_TO_WINDOW,
104     DOPT_CENTER_MOUSE,
105     DOPT_FIT_TO_ZOOM,
106     DOPT_ENSURE_VISIBILITY,
107     DOPT_SET_ZOOM_AREA,
108     DOPT_LOCK_ZOOM,
109     DOPT_ZOOM_BOX,
110     DOPT_NUM
111 } ZoomDisplayOptions;
112 
113 typedef enum _ZsOpt
114 {
115     SOPT_FOLLOW_FOCUS = 0,
116     SOPT_SPEED,
117     SOPT_TIMESTEP,
118     SOPT_ZOOM_FACTOR,
119     SOPT_FILTER_LINEAR,
120     SOPT_SYNC_MOUSE,
121     SOPT_FOCUS_DELAY,
122     SOPT_PAN_FACTOR,
123     SOPT_FOCUS_FIT_WINDOW,
124     SOPT_ALLWAYS_FOCUS_FIT_WINDOW,
125     SOPT_SCALE_MOUSE,
126     SOPT_SCALE_MOUSE_DYNAMIC,
127     SOPT_SCALE_MOUSE_STATIC,
128     SOPT_HIDE_ORIGINAL_MOUSE,
129     SOPT_RESTRAIN_MOUSE,
130     SOPT_RESTRAIN_MARGIN,
131     SOPT_MOUSE_PAN,
132     SOPT_MINIMUM_ZOOM,
133     SOPT_AUTOSCALE_MIN,
134     SOPT_NUM
135 } ZoomScreenOptions;
136 
137 typedef enum {
138     NORTHEAST,
139     NORTHWEST,
140     SOUTHEAST,
141     SOUTHWEST,
142     CENTER
143 } ZoomGravity;
144 
145 typedef enum {
146     NORTH,
147     SOUTH,
148     EAST,
149     WEST
150 } ZoomEdge;
151 
152 typedef struct _CursorTexture
153 {
154     Bool       isSet;
155     GLuint     texture;
156     CompScreen *screen;
157     int        width;
158     int        height;
159     int        hotX;
160     int        hotY;
161 } CursorTexture;
162 
163 typedef struct _ZoomDisplay {
164     HandleEventProc handleEvent;
165     MousePollFunc   *mpFunc;
166 
167     int		    screenPrivateIndex;
168     Bool            fixesSupported;
169     int             fixesEventBase;
170     int             fixesErrorBase;
171     Bool            canHideCursor;
172     CompOption      opt[DOPT_NUM];
173 } ZoomDisplay;
174 
175 /* Stores an actual zoom-setup. This can later be used to store/restore
176  * zoom areas on the fly.
177  *
178  * [xy]Translate and newZoom are target values, and [xy]Translate always
179  * ranges from -0.5 to 0.5.
180  *
181  * currentZoom is actual zoomed value
182  *
183  * real[XY]Translate are the currently used values in the same range as
184  * [xy]Translate, and [xy]trans is adjusted for the zoom level in place.
185  * [xyz]trans should never be modified except in updateActualTranslates()
186  *
187  * viewport is a mask of the viewport, or ~0 for "any".
188  */
189 typedef struct _ZoomArea {
190     int               output;
191     unsigned long int viewport;
192     GLfloat           currentZoom;
193     GLfloat           newZoom;
194     GLfloat           xVelocity;
195     GLfloat           yVelocity;
196     GLfloat           zVelocity;
197     GLfloat           xTranslate;
198     GLfloat           yTranslate;
199     GLfloat           realXTranslate;
200     GLfloat           realYTranslate;
201     GLfloat           xtrans;
202     GLfloat           ytrans;
203     Bool              locked;
204 } ZoomArea;
205 
206 typedef struct _ZoomScreen {
207     PreparePaintScreenProc preparePaintScreen;
208     DonePaintScreenProc	   donePaintScreen;
209     PaintOutputProc	   paintOutput;
210     PositionPollingHandle  pollHandle;
211     CompOption             opt[SOPT_NUM];
212     ZoomArea               *zooms;
213     int                    nZooms;
214     int                    mouseX;
215     int                    mouseY;
216     unsigned long int      grabbed;
217     int	                   grabIndex; // for zoomBox
218     time_t                 lastChange;
219     CursorTexture          cursor;
220     Bool                   cursorInfoSelected;
221     Bool                   cursorHidden;
222     Box			   box;
223 } ZoomScreen;
224 
225 static void syncCenterToMouse (CompScreen *s);
226 static void updateMouseInterval (CompScreen *s, int x, int y);
227 static void cursorZoomActive (CompScreen *s);
228 static void cursorZoomInactive (CompScreen *s);
229 static void restrainCursor (CompScreen *s, int out);
230 
231 static void drawCursor (CompScreen          *s,
232 			CompOutput          *output,
233 			const CompTransform *transform);
234 static void convertToZoomedTarget (CompScreen *s,
235 				   int	      out,
236 				   int	      x,
237 				   int	      y,
238 				   int	      *resultX,
239 				   int	      *resultY);
240 static void
241 convertToZoomed (CompScreen *s,
242 		 int        out,
243 		 int        x,
244 		 int        y,
245 		 int        *resultX,
246 		 int        *resultY);
247 
248 #define GET_ZOOM_DISPLAY(d)				      \
249     ((ZoomDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
250 
251 #define ZOOM_DISPLAY(d)		           \
252     ZoomDisplay *zd = GET_ZOOM_DISPLAY (d)
253 
254 #define GET_ZOOM_SCREEN(s, zd)				         \
255     ((ZoomScreen *) (s)->base.privates[(zd)->screenPrivateIndex].ptr)
256 
257 #define ZOOM_SCREEN(s)						        \
258     ZoomScreen *zs = GET_ZOOM_SCREEN (s, GET_ZOOM_DISPLAY (s->display))
259 
260 #define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
261 
262 /* Checks if a specific screen grab exist. DO NOT USE THIS.
263  * This is a temporary fix that SHOULD be removed asap.
264  * See comments in drawCursor.
265  */
266 
267 static inline Bool
dontuseScreengrabExist(CompScreen * s,char * grab)268 dontuseScreengrabExist (CompScreen * s, char * grab)
269 {
270     int i;
271     for (i = 0; i < s->maxGrab; i++)
272 	if (s->grabs[i].active && !strcmp(s->grabs[i].name, grab))
273 	    return TRUE;
274     return FALSE;
275 }
276 
277 /* Check if the output is valid */
278 static inline Bool
outputIsZoomArea(CompScreen * s,int out)279 outputIsZoomArea (CompScreen *s, int out)
280 {
281     ZOOM_SCREEN (s);
282 
283     if (out < 0 || out >= zs->nZooms)
284 	return FALSE;
285     return TRUE;
286 }
287 
288 /* Check if zoom is active on the output specified */
289 static inline Bool
isActive(CompScreen * s,int out)290 isActive (CompScreen *s, int out)
291 {
292     ZOOM_SCREEN (s);
293 
294     if (!outputIsZoomArea (s, out))
295 	return FALSE;
296     if (zs->grabbed & (1 << zs->zooms[out].output))
297 	return TRUE;
298     return FALSE;
299 }
300 
301 /* Check if we are zoomed out and not going anywhere
302  * (similar to isActive but based on actual zoom, not grab)
303  */
304 static inline Bool
isZoomed(CompScreen * s,int out)305 isZoomed (CompScreen *s, int out)
306 {
307     ZOOM_SCREEN (s);
308 
309     if (!outputIsZoomArea (s, out))
310 	return FALSE;
311 
312     if (zs->zooms[out].currentZoom != 1.0f
313 	|| zs->zooms[out].newZoom != 1.0f)
314 	return TRUE;
315 
316     if (zs->zooms[out].zVelocity != 0.0f)
317 	return TRUE;
318 
319     return FALSE;
320 }
321 
322 /* Returns the distance to the defined edge in zoomed pixels.  */
323 static int
distanceToEdge(CompScreen * s,int out,ZoomEdge edge)324 distanceToEdge (CompScreen *s, int out, ZoomEdge edge)
325 {
326     int        x1,y1,x2,y2;
327     CompOutput *o = &s->outputDev[out];
328 
329     if (!isActive (s, out))
330 	return 0;
331     convertToZoomedTarget (s, out, o->region.extents.x2,
332 			   o->region.extents.y2, &x2, &y2);
333     convertToZoomedTarget (s, out, o->region.extents.x1,
334 			   o->region.extents.y1, &x1, &y1);
335     switch (edge)
336     {
337 	case NORTH: return o->region.extents.y1 - y1;
338 	case SOUTH: return y2 - o->region.extents.y2;
339 	case EAST: return x2 - o->region.extents.x2;
340 	case WEST: return o->region.extents.x1 - x1;
341     }
342     return 0; // Never reached.
343 }
344 
345 /* Update/set translations based on zoom level and real translate.  */
346 static void
updateActualTranslates(ZoomArea * za)347 updateActualTranslates (ZoomArea *za)
348 {
349     za->xtrans = -za->realXTranslate * (1.0f - za->currentZoom);
350     za->ytrans = za->realYTranslate * (1.0f - za->currentZoom);
351 }
352 
353 /* Returns true if the head in question is currently moving.
354  * Since we don't always bother resetting everything when
355  * canceling zoom, we check for the condition of being completely
356  * zoomed out and not zooming in/out first.
357  */
358 static Bool
isInMovement(CompScreen * s,int out)359 isInMovement (CompScreen *s, int out)
360 {
361     ZOOM_SCREEN (s);
362 
363     if (zs->zooms[out].currentZoom == 1.0f &&
364 	zs->zooms[out].newZoom == 1.0f &&
365 	zs->zooms[out].zVelocity == 0.0f)
366 	return FALSE;
367     if (zs->zooms[out].currentZoom != zs->zooms[out].newZoom ||
368 	zs->zooms[out].xVelocity || zs->zooms[out].yVelocity ||
369 	zs->zooms[out].zVelocity)
370 	return TRUE;
371     if (zs->zooms[out].xTranslate != zs->zooms[out].realXTranslate ||
372 	zs->zooms[out].yTranslate != zs->zooms[out].realYTranslate)
373 	return TRUE;
374     return FALSE;
375 }
376 
377 /* Set the initial values of a zoom area.  */
378 static void
initialiseZoomArea(ZoomArea * za,int out)379 initialiseZoomArea (ZoomArea *za, int out)
380 {
381     za->output = out;
382     za->currentZoom = 1.0f;
383     za->newZoom = 1.0f;
384     za->xVelocity = 0.0f;
385     za->yVelocity = 0.0f;
386     za->zVelocity = 0.0f;
387     za->xTranslate = 0.0f;
388     za->yTranslate = 0.0f;
389     za->realXTranslate = 0.0f;
390     za->realYTranslate = 0.0f;
391     za->viewport = ~0;
392     za->locked = FALSE;
393     updateActualTranslates (za);
394 }
395 
396 /* Adjust the velocity in the z-direction.  */
397 static void
adjustZoomVelocity(CompScreen * s,int out,float chunk)398 adjustZoomVelocity (CompScreen *s, int out, float chunk)
399 {
400     float d, adjust, amount;
401     ZOOM_SCREEN (s);
402 
403     d = (zs->zooms[out].newZoom - zs->zooms[out].currentZoom) * 75.0f;
404 
405     adjust = d * 0.002f;
406     amount = fabs (d);
407     if (amount < 1.0f)
408 	amount = 1.0f;
409     else if (amount > 5.0f)
410 	amount = 5.0f;
411 
412     zs->zooms[out].zVelocity =
413 	(amount * zs->zooms[out].zVelocity + adjust) / (amount + 1.0f);
414 
415     if (fabs (d) < 0.1f && fabs (zs->zooms[out].zVelocity) < 0.005f)
416     {
417 	zs->zooms[out].currentZoom = zs->zooms[out].newZoom;
418 	zs->zooms[out].zVelocity = 0.0f;
419     }
420     else
421     {
422 	zs->zooms[out].currentZoom += (zs->zooms[out].zVelocity * chunk) /
423 	    s->redrawTime;
424     }
425 }
426 
427 /* Adjust the X/Y velocity based on target translation and real
428  * translation. */
429 static void
adjustXYVelocity(CompScreen * s,int out,float chunk)430 adjustXYVelocity (CompScreen *s, int out, float chunk)
431 {
432     float xdiff, ydiff;
433     float xadjust, yadjust;
434     float xamount, yamount;
435     ZOOM_SCREEN (s);
436 
437     zs->zooms[out].xVelocity /= 1.25f;
438     zs->zooms[out].yVelocity /= 1.25f;
439     xdiff =
440 	(zs->zooms[out].xTranslate - zs->zooms[out].realXTranslate) *
441 	75.0f;
442     ydiff =
443 	(zs->zooms[out].yTranslate - zs->zooms[out].realYTranslate) *
444 	75.0f;
445     xadjust = xdiff * 0.002f;
446     yadjust = ydiff * 0.002f;
447     xamount = fabs (xdiff);
448     yamount = fabs (ydiff);
449 
450     if (xamount < 1.0f)
451 	    xamount = 1.0f;
452     else if (xamount > 5.0)
453 	    xamount = 5.0f;
454 
455     if (yamount < 1.0f)
456 	    yamount = 1.0f;
457     else if (yamount > 5.0)
458 	    yamount = 5.0f;
459 
460     zs->zooms[out].xVelocity =
461 	(xamount * zs->zooms[out].xVelocity + xadjust) / (xamount + 1.0f);
462     zs->zooms[out].yVelocity =
463 	(yamount * zs->zooms[out].yVelocity + yadjust) / (yamount + 1.0f);
464 
465     if ((fabs(xdiff) < 0.1f && fabs (zs->zooms[out].xVelocity) < 0.005f) &&
466 	(fabs(ydiff) < 0.1f && fabs (zs->zooms[out].yVelocity) < 0.005f))
467     {
468 	zs->zooms[out].realXTranslate = zs->zooms[out].xTranslate;
469 	zs->zooms[out].realYTranslate = zs->zooms[out].yTranslate;
470 	zs->zooms[out].xVelocity = 0.0f;
471 	zs->zooms[out].yVelocity = 0.0f;
472 	return;
473     }
474 
475     zs->zooms[out].realXTranslate +=
476 	(zs->zooms[out].xVelocity * chunk) / s->redrawTime;
477     zs->zooms[out].realYTranslate +=
478 	(zs->zooms[out].yVelocity * chunk) / s->redrawTime;
479 }
480 
481 /* Animate the movement (if any) in preparation of a paint screen.  */
482 static void
zoomPreparePaintScreen(CompScreen * s,int msSinceLastPaint)483 zoomPreparePaintScreen (CompScreen *s,
484 			int	   msSinceLastPaint)
485 {
486     ZOOM_SCREEN (s);
487 
488     if (zs->grabbed)
489     {
490 	int   steps;
491 	float amount, chunk;
492 
493 	amount = msSinceLastPaint * 0.05f * zs->opt[SOPT_SPEED].value.f;
494 	steps  = amount / (0.5f * zs->opt[SOPT_TIMESTEP].value.f);
495 	if (!steps)
496 	       	steps = 1;
497 	chunk  = amount / (float) steps;
498 	while (steps--)
499 	{
500 	    int out;
501 	    for (out = 0; out < zs->nZooms; out++)
502 	    {
503 		if (!isInMovement (s, out) || !isActive (s, out))
504 		    continue;
505 
506 		adjustXYVelocity (s, out, chunk);
507 		adjustZoomVelocity (s, out, chunk);
508 		updateActualTranslates (&zs->zooms[out]);
509 		if (!isZoomed (s, out))
510 		{
511 		    zs->zooms[out].xVelocity = zs->zooms[out].yVelocity =
512 			0.0f;
513 		    zs->grabbed &= ~(1 << zs->zooms[out].output);
514 		}
515 	    }
516 	}
517 	if (zs->opt[SOPT_SYNC_MOUSE].value.b)
518 	    syncCenterToMouse (s);
519     }
520     UNWRAP (zs, s, preparePaintScreen);
521     (*s->preparePaintScreen) (s, msSinceLastPaint);
522     WRAP (zs, s, preparePaintScreen, zoomPreparePaintScreen);
523 }
524 
525 /* Damage screen if we're still moving.  */
526 static void
zoomDonePaintScreen(CompScreen * s)527 zoomDonePaintScreen (CompScreen *s)
528 {
529     ZOOM_SCREEN (s);
530 
531     if (zs->grabbed)
532     {
533 	int out;
534 	for (out = 0; out < zs->nZooms; out++)
535 	{
536 	    if (isInMovement (s, out) && isActive (s,out))
537 	    {
538 		damageScreen (s);
539 		break;
540 	    }
541 	}
542     }
543     UNWRAP (zs, s, donePaintScreen);
544     (*s->donePaintScreen) (s);
545     WRAP (zs, s, donePaintScreen, zoomDonePaintScreen);
546 }
547 /* Draws a box from the screen coordinates inx1,iny1 to inx2,iny2 */
548 static void
drawBox(CompScreen * s,const CompTransform * transform,CompOutput * output,Box box)549 drawBox (CompScreen          *s,
550 	 const CompTransform *transform,
551 	 CompOutput          *output,
552 	 Box                 box)
553 {
554     CompTransform zTransform = *transform;
555     int           x1,x2,y1,y2;
556     int		  inx1, inx2, iny1, iny2;
557     int	          out = output->id;
558 
559     transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &zTransform);
560     convertToZoomed (s, out, box.x1, box.y1, &inx1, &iny1);
561     convertToZoomed (s, out, box.x2, box.y2, &inx2, &iny2);
562 
563     x1 = MIN (inx1, inx2);
564     y1 = MIN (iny1, iny2);
565     x2 = MAX (inx1, inx2);
566     y2 = MAX (iny1, iny2);
567     glPushMatrix ();
568     glLoadMatrixf (zTransform.m);
569     glDisableClientState (GL_TEXTURE_COORD_ARRAY);
570     glEnable (GL_BLEND);
571     glColor4us (0x2fff, 0x2fff, 0x4fff, 0x4fff);
572     glRecti (x1,y2,x2,y1);
573     glColor4us (0x2fff, 0x2fff, 0x4fff, 0x9fff);
574     glBegin (GL_LINE_LOOP);
575     glVertex2i (x1, y1);
576     glVertex2i (x2, y1);
577     glVertex2i (x2, y2);
578     glVertex2i (x1, y2);
579     glEnd ();
580     glColor4usv (defaultColor);
581     glDisable (GL_BLEND);
582     glEnableClientState (GL_TEXTURE_COORD_ARRAY);
583     glPopMatrix ();
584 }
585 /* Apply the zoom if we are grabbed.
586  * Make sure to use the correct filter.
587  */
588 static Bool
zoomPaintOutput(CompScreen * s,const ScreenPaintAttrib * sAttrib,const CompTransform * transform,Region region,CompOutput * output,unsigned int mask)589 zoomPaintOutput (CompScreen		 *s,
590 		 const ScreenPaintAttrib *sAttrib,
591 		 const CompTransform	 *transform,
592 		 Region		         region,
593 		 CompOutput		 *output,
594 		 unsigned int		 mask)
595 {
596     Bool status;
597     int	 out = output->id;
598     ZOOM_SCREEN (s);
599 
600 
601     if (isActive (s, out))
602     {
603 	ScreenPaintAttrib sa = *sAttrib;
604 	int		  saveFilter;
605 	CompTransform     zTransform = *transform;
606 
607 	mask &= ~PAINT_SCREEN_REGION_MASK;
608 	mask |= PAINT_SCREEN_CLEAR_MASK;
609 
610 	matrixScale (&zTransform,
611 		     1.0f / zs->zooms[out].currentZoom,
612 		     1.0f / zs->zooms[out].currentZoom,
613 		     1.0f);
614 	matrixTranslate (&zTransform,
615 			 zs->zooms[out].xtrans,
616 			 zs->zooms[out].ytrans,
617 			 0);
618 
619 	mask |= PAINT_SCREEN_TRANSFORMED_MASK;
620 	saveFilter = s->filter[SCREEN_TRANS_FILTER];
621 
622 	if (zs->opt[SOPT_FILTER_LINEAR].value.b)
623 	    s->filter[SCREEN_TRANS_FILTER] = COMP_TEXTURE_FILTER_GOOD;
624 	else
625 	    s->filter[SCREEN_TRANS_FILTER] = COMP_TEXTURE_FILTER_FAST;
626 
627 	UNWRAP (zs, s, paintOutput);
628 	status =
629 	    (*s->paintOutput) (s, &sa, &zTransform, region, output, mask);
630 	WRAP (zs, s, paintOutput, zoomPaintOutput);
631 	drawCursor (s, output, transform);
632 
633 	s->filter[SCREEN_TRANS_FILTER] = saveFilter;
634     }
635     else
636     {
637 	UNWRAP (zs, s, paintOutput);
638 	status = (*s->paintOutput) (s,
639 				    sAttrib,
640 				    transform,
641 				    region,
642 				    output,
643 				    mask);
644 	WRAP (zs, s, paintOutput, zoomPaintOutput);
645     }
646     if (zs->grabIndex)
647 	drawBox (s, transform, output, zs->box);
648 
649     return status;
650 }
651 
652 /* Makes sure we're not attempting to translate too far.
653  * We are restricted to 0.5 to not go beyond the end
654  * of the screen/head.  */
655 static inline void
constrainZoomTranslate(CompScreen * s)656 constrainZoomTranslate (CompScreen *s)
657 {
658     int out;
659     ZOOM_SCREEN (s);
660 
661     for (out = 0; out < zs->nZooms; out++)
662     {
663 	if (zs->zooms[out].xTranslate > 0.5f)
664 	    zs->zooms[out].xTranslate = 0.5f;
665 	else if (zs->zooms[out].xTranslate < -0.5f)
666 	    zs->zooms[out].xTranslate = -0.5f;
667 
668 	if (zs->zooms[out].yTranslate > 0.5f)
669 	    zs->zooms[out].yTranslate = 0.5f;
670 	else if (zs->zooms[out].yTranslate < -0.5f)
671 	    zs->zooms[out].yTranslate = -0.5f;
672     }
673 }
674 
675 /* Functions for adjusting the zoomed area.
676  * These are the core of the zoom plug-in; Anything wanting
677  * to adjust the zoomed area must use setCenter or setZoomArea
678  * and setScale or front ends to them.  */
679 
680 /* Sets the center of the zoom area to X,Y.
681  * We have to be able to warp the pointer here: If we are moved by
682  * anything except mouse movement, we have to sync the
683  * mouse pointer. This is to allow input, and is NOT necessary
684  * when input redirection is available to us or if we're cheating
685  * and using a scaled mouse cursor to imitate IR.
686  * The center is not the center of the screen. This is the target-center;
687  * that is, it's the point that's the same regardless of zoom level.
688  */
689 static void
setCenter(CompScreen * s,int x,int y,Bool instant)690 setCenter (CompScreen *s, int x, int y, Bool instant)
691 {
692     int         out = outputDeviceForPoint (s, x,y);
693     CompOutput  *o = &s->outputDev[out];
694     ZOOM_SCREEN (s);
695 
696     if (zs->zooms[out].locked)
697 	return;
698 
699     zs->zooms[out].xTranslate = (float)
700 	((x - o->region.extents.x1) - o->width  / 2) / (o->width);
701     zs->zooms[out].yTranslate = (float)
702 	((y - o->region.extents.y1) - o->height / 2) / (o->height);
703 
704     if (instant)
705     {
706 	zs->zooms[out].realXTranslate = zs->zooms[out].xTranslate;
707 	zs->zooms[out].realYTranslate = zs->zooms[out].yTranslate;
708 	zs->zooms[out].yVelocity = 0.0f;
709 	zs->zooms[out].xVelocity = 0.0f;
710 	updateActualTranslates (&zs->zooms[out]);
711     }
712 
713     if (zs->opt[SOPT_MOUSE_PAN].value.b)
714 	restrainCursor (s, out);
715 }
716 
717 /* Zooms the area described.
718  * The math could probably be cleaned up, but should be correct now. */
719 static void
setZoomArea(CompScreen * s,int x,int y,int width,int height,Bool instant)720 setZoomArea (CompScreen *s,
721 	     int        x,
722 	     int        y,
723 	     int        width,
724 	     int        height,
725 	     Bool       instant)
726 {
727     int         out = outputDeviceForGeometry (s, x, y, width, height, 0);
728     CompOutput  *o = &s->outputDev[out];
729     ZOOM_SCREEN (s);
730 
731     if (zs->zooms[out].newZoom == 1.0f)
732 	return;
733 
734     if (zs->zooms[out].locked)
735 	return;
736     zs->zooms[out].xTranslate =
737 	 (float) -((o->width/2) - (x + (width/2) - o->region.extents.x1))
738 	/ (o->width);
739     zs->zooms[out].xTranslate /= (1.0f - zs->zooms[out].newZoom);
740     zs->zooms[out].yTranslate =
741 	(float) -((o->height/2) - (y + (height/2) - o->region.extents.y1))
742 	/ (o->height);
743     zs->zooms[out].yTranslate /= (1.0f - zs->zooms[out].newZoom);
744     constrainZoomTranslate (s);
745 
746     if (instant)
747     {
748 	zs->zooms[out].realXTranslate = zs->zooms[out].xTranslate;
749 	zs->zooms[out].realYTranslate = zs->zooms[out].yTranslate;
750 	updateActualTranslates (&zs->zooms[out]);
751     }
752 
753     if (zs->opt[SOPT_MOUSE_PAN].value.b)
754 	restrainCursor (s, out);
755 }
756 
757 /* Moves the zoom area to the window specified */
758 static void
zoomAreaToWindow(CompWindow * w)759 zoomAreaToWindow (CompWindow *w)
760 {
761     int left = w->serverX - w->input.left;
762     int width = w->width + w->input.left + w->input.right;
763     int top = w->serverY - w->input.top;
764     int height = w->height + w->input.top + w->input.bottom;
765 
766     setZoomArea (w->screen, left, top, width, height, FALSE);
767 }
768 
769 /* Pans the zoomed area vertically/horizontally by * value * zs->panFactor
770  * TODO: Fix output. */
771 static void
panZoom(CompScreen * s,int xvalue,int yvalue)772 panZoom (CompScreen *s, int xvalue, int yvalue)
773 {
774     int out;
775     ZOOM_SCREEN (s);
776 
777     for (out = 0; out < zs->nZooms; out++)
778     {
779 	zs->zooms[out].xTranslate +=
780 	    zs->opt[SOPT_PAN_FACTOR].value.f * xvalue *
781 	    zs->zooms[out].currentZoom;
782 	zs->zooms[out].yTranslate +=
783 	    zs->opt[SOPT_PAN_FACTOR].value.f * yvalue *
784 	    zs->zooms[out].currentZoom;
785     }
786 
787     constrainZoomTranslate (s);
788 }
789 
790 /* Enables polling of mouse position, and refreshes currently
791  * stored values.
792  */
793 static void
enableMousePolling(CompScreen * s)794 enableMousePolling (CompScreen *s)
795 {
796     ZOOM_SCREEN (s);
797     ZOOM_DISPLAY (s->display);
798     zs->pollHandle =
799 	(*zd->mpFunc->addPositionPolling) (s, updateMouseInterval);
800     zs->lastChange = time(NULL);
801     (*zd->mpFunc->getCurrentPosition) (s, &zs->mouseX, &zs->mouseY);
802 }
803 
804 /* Sets the zoom (or scale) level.
805  * Cleans up if we are suddenly zoomed out.
806  */
807 static void
setScale(CompScreen * s,int out,float value)808 setScale (CompScreen *s, int out, float value)
809 {
810     ZOOM_SCREEN(s);
811 
812     if (zs->zooms[out].locked)
813 	return;
814 
815     if (value >= 1.0f)
816 	value = 1.0f;
817     else
818     {
819 	if (!zs->pollHandle)
820 	    enableMousePolling (s);
821 	zs->grabbed |= (1 << zs->zooms[out].output);
822 	cursorZoomActive (s);
823     }
824 
825     if (value == 1.0f)
826     {
827 	zs->zooms[out].xTranslate = 0.0f;
828 	zs->zooms[out].yTranslate = 0.0f;
829 	cursorZoomInactive (s);
830     }
831 
832     if (value < zs->opt[SOPT_MINIMUM_ZOOM].value.f)
833 	value = zs->opt[SOPT_MINIMUM_ZOOM].value.f;
834 
835     zs->zooms[out].newZoom = value;
836     damageScreen(s);
837 }
838 
839 /* Sets the zoom factor to the bigger of the two floats supplied.
840  * Convenience function for setting the scale factor for an area.
841  */
842 static inline void
setScaleBigger(CompScreen * s,int out,float x,float y)843 setScaleBigger (CompScreen *s, int out, float x, float y)
844 {
845     setScale (s, out, x > y ? x : y);
846 }
847 
848 /* Mouse code...
849  * This takes care of keeping the mouse in sync with the zoomed area and
850  * vice versa.
851  * See heading for description.
852  */
853 
854 /* Syncs the center, based on translations, back to the mouse.
855  * This should be called when doing non-IR zooming and moving the zoom
856  * area based on events other than mouse movement.
857  */
858 static void
syncCenterToMouse(CompScreen * s)859 syncCenterToMouse (CompScreen *s)
860 {
861     int         x, y;
862     int         out;
863     CompOutput  *o;
864     ZOOM_SCREEN (s);
865 
866     out = outputDeviceForPoint (s, zs->mouseX, zs->mouseY);
867     o = &s->outputDev[out];
868 
869     if (!isInMovement (s, out))
870 	return;
871 
872     x = (int) ((zs->zooms[out].realXTranslate * s->width) +
873 	       (o->width / 2) + o->region.extents.x1);
874     y = (int) ((zs->zooms[out].realYTranslate * s->height) +
875 	       (o->height / 2) + o->region.extents.y1);
876 
877     if ((x != zs->mouseX || y != zs->mouseY)
878 	&& zs->grabbed && zs->zooms[out].newZoom != 1.0f)
879     {
880 	warpPointer (s, x - pointerX , y - pointerY );
881 	zs->mouseX = x;
882 	zs->mouseY = y;
883     }
884 }
885 
886 /* Convert the point X,Y to where it would be when zoomed.  */
887 static void
convertToZoomed(CompScreen * s,int out,int x,int y,int * resultX,int * resultY)888 convertToZoomed (CompScreen *s,
889 		 int        out,
890 		 int        x,
891 		 int        y,
892 		 int        *resultX,
893 		 int        *resultY)
894 {
895     CompOutput  *o = &s->outputDev[out];
896     ZoomArea    *za;
897     ZOOM_SCREEN (s);
898 
899     za = &zs->zooms[out];
900     x -= o->region.extents.x1;
901     y -= o->region.extents.y1;
902     *resultX = x - (za->realXTranslate *
903 		    (1.0f - za->currentZoom) * o->width) - o->width/2;
904     *resultX /= za->currentZoom;
905     *resultX += o->width/2;
906     *resultX += o->region.extents.x1;
907     *resultY = y - (za->realYTranslate *
908 		    (1.0f - za->currentZoom) * o->height) - o->height/2;
909     *resultY /= za->currentZoom;
910     *resultY += o->height/2;
911     *resultY += o->region.extents.y1;
912 }
913 
914 /* Same but use targeted translation, not real */
915 static void
convertToZoomedTarget(CompScreen * s,int out,int x,int y,int * resultX,int * resultY)916 convertToZoomedTarget (CompScreen *s,
917 		       int	  out,
918 		       int	  x,
919 		       int	  y,
920 		       int	  *resultX,
921 		       int	  *resultY)
922 {
923     CompOutput  *o = &s->outputDev[out];
924     ZoomArea    *za;
925     ZOOM_SCREEN (s);
926 
927     za = &zs->zooms[out];
928     x -= o->region.extents.x1;
929     y -= o->region.extents.y1;
930     *resultX = x - (za->xTranslate *
931 		    (1.0f - za->newZoom) * o->width) - o->width/2;
932     *resultX /= za->newZoom;
933     *resultX += o->width/2;
934     *resultX += o->region.extents.x1;
935     *resultY = y - (za->yTranslate *
936 		    (1.0f - za->newZoom) * o->height) - o->height/2;
937     *resultY /= za->newZoom;
938     *resultY += o->height/2;
939     *resultY += o->region.extents.y1;
940 }
941 
942 /* Make sure the given point + margin is visible;
943  * Translate to make it visible if necessary.
944  * Returns false if the point isn't on a actively zoomed head
945  * or the area is locked. */
946 static Bool
ensureVisibility(CompScreen * s,int x,int y,int margin)947 ensureVisibility (CompScreen *s, int x, int y, int margin)
948 {
949     int         zoomX, zoomY;
950     int         out;
951     CompOutput  *o;
952     ZOOM_SCREEN (s);
953 
954     out = outputDeviceForPoint (s, x, y);
955     if (!isActive (s, out))
956 	return FALSE;
957 
958     o = &s->outputDev[out];
959     convertToZoomedTarget (s, out, x, y, &zoomX, &zoomY);
960     ZoomArea *za = &zs->zooms[out];
961     if (za->locked)
962 	return FALSE;
963 
964 #define FACTOR (za->newZoom / (1.0f - za->newZoom))
965     if (zoomX + margin > o->region.extents.x2)
966 	za->xTranslate +=
967 	    (FACTOR * (float) (zoomX + margin - o->region.extents.x2)) /
968 	    (float) o->width;
969     else if (zoomX - margin < o->region.extents.x1)
970 	za->xTranslate +=
971 	    (FACTOR * (float) (zoomX - margin - o->region.extents.x1)) /
972 	    (float) o->width;
973 
974     if (zoomY + margin > o->region.extents.y2)
975 	za->yTranslate +=
976 	    (FACTOR * (float) (zoomY + margin - o->region.extents.y2)) /
977 	    (float) o->height;
978     else if (zoomY - margin < o->region.extents.y1)
979 	za->yTranslate +=
980 	    (FACTOR * (float) (zoomY - margin - o->region.extents.y1)) /
981 	    (float) o->height;
982 #undef FACTOR
983     constrainZoomTranslate (s);
984     return TRUE;
985 }
986 
987 /* Attempt to ensure the visibility of an area defined by x1/y1 and x2/y2.
988  * See ensureVisibility () for details.
989  *
990  * This attempts to find the translations that leaves the biggest part of
991  * the area visible.
992  *
993  * gravity defines what part of the window that should get
994  * priority if it isn't possible to fit all of it.
995  */
996 static void
ensureVisibilityArea(CompScreen * s,int x1,int y1,int x2,int y2,int margin,ZoomGravity gravity)997 ensureVisibilityArea (CompScreen  *s,
998 		      int         x1,
999 		      int         y1,
1000 		      int         x2,
1001 		      int         y2,
1002 		      int         margin,
1003 		      ZoomGravity gravity)
1004 {
1005     int        targetX, targetY, targetW, targetH;
1006     int        out;
1007     CompOutput *o;
1008     ZOOM_SCREEN (s);
1009 
1010     out = outputDeviceForPoint (s, x1 + (x2-x1/2), y1 + (y2-y1/2));
1011     o = &s->outputDev[out];
1012 
1013 #define WIDTHOK (float)(x2-x1) / (float)o->width < zs->zooms[out].newZoom
1014 #define HEIGHTOK (float)(y2-y1) / (float)o->height < zs->zooms[out].newZoom
1015 
1016     if (WIDTHOK &&
1017 	HEIGHTOK) {
1018 	ensureVisibility (s, x1, y1, margin);
1019 	ensureVisibility (s, x2, y2, margin);
1020 	return;
1021     }
1022 
1023     switch (gravity)
1024     {
1025 	case NORTHWEST:
1026 	    targetX = x1;
1027 	    targetY = y1;
1028 	    if (WIDTHOK)
1029 		targetW = x2 - x1;
1030 	    else
1031 		targetW = o->width * zs->zooms[out].newZoom;
1032 	    if (HEIGHTOK)
1033 		targetH = y2 - y1;
1034 	    else
1035 		targetH = o->height * zs->zooms[out].newZoom;
1036 	    break;
1037 	case NORTHEAST:
1038 	    targetY = y1;
1039 	    if (WIDTHOK)
1040 	    {
1041 		targetX = x1;
1042 		targetW = x2-x1;
1043 	    }
1044 	    else
1045 	    {
1046 		targetX = x2 - o->width * zs->zooms[out].newZoom;
1047 		targetW = o->width * zs->zooms[out].newZoom;
1048 	    }
1049 
1050 	    if (HEIGHTOK)
1051 		targetH = y2-y1;
1052 	    else
1053 		targetH = o->height * zs->zooms[out].newZoom;
1054 	    break;
1055 	case SOUTHWEST:
1056 	    targetX = x1;
1057 	    if (WIDTHOK)
1058 		targetW = x2-x1;
1059 	    else
1060 		targetW = o->width * zs->zooms[out].newZoom;
1061 	    if (HEIGHTOK)
1062 	    {
1063 		targetY = y1;
1064 		targetH = y2-y1;
1065 	    }
1066 	    else
1067 	    {
1068 		targetY = y2 - (o->width * zs->zooms[out].newZoom);
1069 		targetH = o->width * zs->zooms[out].newZoom;
1070 	    }
1071 	    break;
1072 	case SOUTHEAST:
1073 	    if (WIDTHOK)
1074 	    {
1075 		targetX = x1;
1076 		targetW = x2-x1;
1077 	    }
1078 	    else
1079 	    {
1080 		targetW = o->width * zs->zooms[out].newZoom;
1081 		targetX = x2 - targetW;
1082 	    }
1083 
1084 	    if (HEIGHTOK)
1085 	    {
1086 		targetY = y1;
1087 		targetH = y2 - y1;
1088 	    }
1089 	    else
1090 	    {
1091 		targetH = o->height * zs->zooms[out].newZoom;
1092 		targetY = y2 - targetH;
1093 	    }
1094 	    break;
1095 	case CENTER:
1096 	    setCenter (s, x1 + (x2 - x1 / 2), y1 + (y2 - y1 / 2), FALSE);
1097 	    return;
1098 	    break;
1099     }
1100 
1101     setZoomArea (s, targetX, targetY, targetW, targetH, FALSE);
1102     return ;
1103 }
1104 
1105 /* Ensures that the cursor is visible on the given head.
1106  * Note that we check if currentZoom is 1.0f, because that often means that
1107  * mouseX and mouseY is not up-to-date (since the polling timer just
1108  * started).
1109  */
1110 static void
restrainCursor(CompScreen * s,int out)1111 restrainCursor (CompScreen *s, int out)
1112 {
1113     int         x1, y1, x2, y2, margin;
1114     int         diffX = 0, diffY = 0;
1115     int         north, south, east, west;
1116     float       z;
1117     CompOutput  *o = &s->outputDev[out];
1118     ZOOM_SCREEN (s);
1119     ZOOM_DISPLAY (s->display);
1120 
1121     z = zs->zooms[out].newZoom;
1122     margin = zs->opt[SOPT_RESTRAIN_MARGIN].value.i;
1123     north = distanceToEdge (s, out, NORTH);
1124     south = distanceToEdge (s, out, SOUTH);
1125     east = distanceToEdge (s, out, EAST);
1126     west = distanceToEdge (s, out, WEST);
1127 
1128     if (zs->zooms[out].currentZoom == 1.0f)
1129     {
1130 	zs->lastChange = time(NULL);
1131 	(*zd->mpFunc->getCurrentPosition) (s, &zs->mouseX, &zs->mouseY);
1132     }
1133 
1134     convertToZoomedTarget (s, out, zs->mouseX - zs->cursor.hotX,
1135 			   zs->mouseY - zs->cursor.hotY, &x1, &y1);
1136     convertToZoomedTarget
1137 	(s, out,
1138 	 zs->mouseX - zs->cursor.hotX + zs->cursor.width,
1139 	 zs->mouseY - zs->cursor.hotY + zs->cursor.height,
1140 	 &x2, &y2);
1141 
1142     if ((x2 - x1 > o->region.extents.x2 - o->region.extents.x1) ||
1143        (y2 - y1 > o->region.extents.y2 - o->region.extents.y1))
1144 	return;
1145     if (x2 > o->region.extents.x2 - margin && east > 0)
1146 	diffX = x2 - o->region.extents.x2 + margin;
1147     else if (x1 < o->region.extents.x1 + margin && west > 0)
1148 	diffX = x1 - o->region.extents.x1 - margin;
1149 
1150     if (y2 > o->region.extents.y2 - margin && south > 0)
1151 	diffY = y2 - o->region.extents.y2 + margin;
1152     else if (y1 < o->region.extents.y1 + margin && north > 0)
1153 	diffY = y1 - o->region.extents.y1 - margin;
1154 
1155     if (abs(diffX)*z > 0  || abs(diffY)*z > 0)
1156 	warpPointer (s,
1157 		(int) (zs->mouseX - pointerX) -  (int) ((float)diffX * z),
1158 		(int) (zs->mouseY - pointerY) -  (int) ((float)diffY * z));
1159 }
1160 
1161 /* Check if the cursor is still visible.
1162  * We also make sure to activate/deactivate cursor scaling here
1163  * so we turn on/off the pointer if it moves from one head to another.
1164  * FIXME: Detect an actual output change instead of spamming.
1165  * FIXME: The second ensureVisibility (sync with restrain).
1166  */
1167 static void
cursorMoved(CompScreen * s)1168 cursorMoved (CompScreen *s)
1169 {
1170     int         out;
1171     ZOOM_SCREEN (s);
1172 
1173     out = outputDeviceForPoint (s, zs->mouseX, zs->mouseY);
1174     if (isActive (s, out))
1175     {
1176 	if (zs->opt[SOPT_RESTRAIN_MOUSE].value.b)
1177 	    restrainCursor (s, out);
1178 
1179 	if (zs->opt[SOPT_MOUSE_PAN].value.b)
1180 	{
1181 	    ensureVisibilityArea (s,
1182 				  zs->mouseX - zs->cursor.hotX,
1183 				  zs->mouseY - zs->cursor.hotY,
1184 				  zs->mouseX + zs->cursor.width -
1185 				  zs->cursor.hotX,
1186 				  zs->mouseY + zs->cursor.height -
1187 				  zs->cursor.hotY,
1188 				  zs->opt[SOPT_RESTRAIN_MARGIN].value.i,
1189 				  NORTHWEST);
1190 	}
1191 
1192 	cursorZoomActive (s);
1193     }
1194     else
1195     {
1196 	cursorZoomInactive (s);
1197     }
1198 }
1199 
1200 /* Update the mouse position.
1201  * Based on the zoom engine in use, we will have to move the zoom area.
1202  * This might have to be added to a timer.
1203  */
1204 static void
updateMousePosition(CompScreen * s,int x,int y)1205 updateMousePosition (CompScreen *s, int x, int y)
1206 {
1207     ZOOM_SCREEN(s);
1208     int out;
1209     zs->mouseX = x;
1210     zs->mouseY = y;
1211     out = outputDeviceForPoint (s, zs->mouseX, zs->mouseY);
1212     zs->lastChange = time(NULL);
1213     if (zs->opt[SOPT_SYNC_MOUSE].value.b && !isInMovement (s, out))
1214 	setCenter (s, zs->mouseX, zs->mouseY, TRUE);
1215     cursorMoved (s);
1216     damageScreen (s);
1217 }
1218 
1219 /* Timeout handler to poll the mouse. Returns false (and thereby does not
1220  * get re-added to the queue) when zoom is not active. */
1221 static void
updateMouseInterval(CompScreen * s,int x,int y)1222 updateMouseInterval (CompScreen *s, int x, int y)
1223 {
1224     ZOOM_SCREEN (s);
1225 
1226     updateMousePosition(s, x, y);
1227 
1228     if (!zs->grabbed)
1229     {
1230 	ZOOM_DISPLAY (s->display);
1231 	if (zs->pollHandle)
1232 		(*zd->mpFunc->removePositionPolling) (s, zs->pollHandle);
1233 	zs->pollHandle = 0;
1234 	cursorMoved (s);
1235     }
1236 }
1237 
1238 /* Free a cursor */
1239 static void
freeCursor(CursorTexture * cursor)1240 freeCursor (CursorTexture * cursor)
1241 {
1242     if (!cursor->isSet)
1243 	return;
1244 
1245     makeScreenCurrent (cursor->screen);
1246     cursor->isSet = FALSE;
1247     glDeleteTextures (1, &cursor->texture);
1248     cursor->texture = 0;
1249 }
1250 
1251 /* Translate into place and draw the scaled cursor.  */
1252 static void
drawCursor(CompScreen * s,CompOutput * output,const CompTransform * transform)1253 drawCursor (CompScreen          *s,
1254 	    CompOutput          *output,
1255 	    const CompTransform *transform)
1256 {
1257     int         out = output->id;
1258     ZOOM_SCREEN (s);
1259 
1260     if (zs->cursor.isSet)
1261     {
1262 	CompTransform sTransform = *transform;
1263 	float	      scaleFactor;
1264 	int           ax, ay, x, y;
1265 
1266 	/* This is a hack because these transformations are wrong when
1267 	 * we're working exposed. Expo is capable of telling where the
1268 	 * real mouse is despite zoom, so we don't have to disable the
1269 	 * zoom. We do, however, have to show the original pointer.
1270 	 */
1271 	if (dontuseScreengrabExist (s, "expo"))
1272 	{
1273 	    cursorZoomInactive (s);
1274 	    return;
1275 	}
1276 
1277 	transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform);
1278 	convertToZoomed (s, out, zs->mouseX, zs->mouseY, &ax, &ay);
1279         glPushMatrix ();
1280 	glLoadMatrixf (sTransform.m);
1281 	glTranslatef ((float) ax, (float) ay, 0.0f);
1282 	if (zs->opt[SOPT_SCALE_MOUSE_DYNAMIC].value.b)
1283 	    scaleFactor = 1.0f / zs->zooms[out].currentZoom;
1284 	else
1285 	    scaleFactor = 1.0f / zs->opt[SOPT_SCALE_MOUSE_STATIC].value.f;
1286 	glScalef (scaleFactor,
1287 		  scaleFactor,
1288 		  1.0f);
1289 	x = -zs->cursor.hotX;
1290 	y = -zs->cursor.hotY;
1291 
1292 	glEnable (GL_BLEND);
1293 	glBindTexture (GL_TEXTURE_RECTANGLE_ARB, zs->cursor.texture);
1294 	glEnable (GL_TEXTURE_RECTANGLE_ARB);
1295 
1296 	glBegin (GL_QUADS);
1297 	glTexCoord2d (0, 0);
1298 	glVertex2f (x, y);
1299 	glTexCoord2d (0, zs->cursor.height);
1300 	glVertex2f (x, y + zs->cursor.height);
1301 	glTexCoord2d (zs->cursor.width, zs->cursor.height);
1302 	glVertex2f (x + zs->cursor.width, y + zs->cursor.height);
1303 	glTexCoord2d (zs->cursor.width, 0);
1304 	glVertex2f (x + zs->cursor.width, y);
1305 	glEnd ();
1306 	glDisable (GL_BLEND);
1307 	glBindTexture (GL_TEXTURE_RECTANGLE_ARB, 0);
1308 	glDisable (GL_TEXTURE_RECTANGLE_ARB);
1309 	glPopMatrix ();
1310     }
1311 }
1312 
1313 /* Create (if necessary) a texture to store the cursor,
1314  * fetch the cursor with XFixes. Store it.  */
1315 static void
zoomUpdateCursor(CompScreen * s,CursorTexture * cursor)1316 zoomUpdateCursor (CompScreen * s, CursorTexture * cursor)
1317 {
1318     unsigned char *pixels;
1319     int           i;
1320     Display       *dpy = s->display->display;
1321     ZOOM_SCREEN   (s);
1322 
1323     if (!cursor->isSet)
1324     {
1325 	cursor->isSet = TRUE;
1326 	cursor->screen = s;
1327 	makeScreenCurrent (s);
1328 	glEnable (GL_TEXTURE_RECTANGLE_ARB);
1329 	glGenTextures (1, &cursor->texture);
1330 	glBindTexture (GL_TEXTURE_RECTANGLE_ARB, cursor->texture);
1331 
1332 	if (zs->opt[SOPT_FILTER_LINEAR].value.b &&
1333 	    s->display->textureFilter != GL_NEAREST)
1334 	{
1335 	    glTexParameteri (GL_TEXTURE_RECTANGLE_ARB,
1336 			     GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1337 	    glTexParameteri (GL_TEXTURE_RECTANGLE_ARB,
1338 			     GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1339 	}
1340 	else
1341 	{
1342 	    glTexParameteri (GL_TEXTURE_RECTANGLE_ARB,
1343 			     GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1344 	    glTexParameteri (GL_TEXTURE_RECTANGLE_ARB,
1345 			     GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1346 	}
1347 	glTexParameteri (GL_TEXTURE_RECTANGLE_ARB,
1348 			 GL_TEXTURE_WRAP_S, GL_CLAMP);
1349 	glTexParameteri (GL_TEXTURE_RECTANGLE_ARB,
1350 			 GL_TEXTURE_WRAP_T, GL_CLAMP);
1351     } else {
1352 	makeScreenCurrent (cursor->screen);
1353 	glEnable (GL_TEXTURE_RECTANGLE_ARB);
1354     }
1355 
1356     XFixesCursorImage *ci = XFixesGetCursorImage(dpy);
1357     /* Hack to avoid changing to an invisible (bugged)cursor image.
1358      * Example: The animated Firefox cursors.
1359      */
1360     if (ci->width <= 1 && ci->height <= 1)
1361     {
1362 	XFree (ci);
1363 	return;
1364     }
1365 
1366 
1367     cursor->width = ci->width;
1368     cursor->height = ci->height;
1369     cursor->hotX = ci->xhot;
1370     cursor->hotY = ci->yhot;
1371     pixels = malloc(ci->width * ci->height * 4);
1372 
1373     if (!pixels)
1374     {
1375 	XFree (ci);
1376 	return;
1377     }
1378 
1379     for (i = 0; i < ci->width * ci->height; i++)
1380     {
1381 	unsigned long pix = ci->pixels[i];
1382 	pixels[i * 4] = pix & 0xff;
1383 	pixels[(i * 4) + 1] = (pix >> 8) & 0xff;
1384 	pixels[(i * 4) + 2] = (pix >> 16) & 0xff;
1385 	pixels[(i * 4) + 3] = (pix >> 24) & 0xff;
1386     }
1387 
1388     glBindTexture (GL_TEXTURE_RECTANGLE_ARB, cursor->texture);
1389     glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, cursor->width,
1390 		  cursor->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
1391     glBindTexture (GL_TEXTURE_RECTANGLE_ARB, 0);
1392     glDisable (GL_TEXTURE_RECTANGLE_ARB);
1393     XFree (ci);
1394     free (pixels);
1395 }
1396 
1397 /* We are no longer zooming the cursor, so display it.  */
1398 static void
cursorZoomInactive(CompScreen * s)1399 cursorZoomInactive (CompScreen *s)
1400 {
1401     ZOOM_DISPLAY (s->display);
1402     ZOOM_SCREEN (s);
1403 
1404     if (!zd->fixesSupported)
1405 	return;
1406 
1407     if (zs->cursorInfoSelected)
1408     {
1409 	zs->cursorInfoSelected = FALSE;
1410 	XFixesSelectCursorInput (s->display->display, s->root, 0);
1411     }
1412 
1413     if (zs->cursor.isSet)
1414     {
1415 	freeCursor (&zs->cursor);
1416     }
1417 
1418     if (zs->cursorHidden)
1419     {
1420 	zs->cursorHidden = FALSE;
1421 	XFixesShowCursor (s->display->display, s->root);
1422     }
1423 }
1424 
1425 /* Cursor zoom is active: We need to hide the original,
1426  * register for Cursor notifies and display the new one.
1427  * This can be called multiple times, not just on initial
1428  * activation.
1429  */
1430 static void
cursorZoomActive(CompScreen * s)1431 cursorZoomActive (CompScreen *s)
1432 {
1433     ZOOM_DISPLAY (s->display);
1434     ZOOM_SCREEN (s);
1435 
1436     if (!zd->fixesSupported)
1437 	return;
1438     if (!zs->opt[SOPT_SCALE_MOUSE].value.b)
1439 	return;
1440 
1441     if (!zs->cursorInfoSelected)
1442     {
1443 	zs->cursorInfoSelected = TRUE;
1444         XFixesSelectCursorInput (s->display->display, s->root,
1445 				 XFixesDisplayCursorNotifyMask);
1446 	zoomUpdateCursor (s, &zs->cursor);
1447     }
1448     if (zd->canHideCursor && !zs->cursorHidden &&
1449 	zs->opt[SOPT_HIDE_ORIGINAL_MOUSE].value.b)
1450     {
1451 	zs->cursorHidden = TRUE;
1452 	XFixesHideCursor (s->display->display, s->root);
1453     }
1454 }
1455 
1456 /* Set the zoom area
1457  * This is an interface for scripting.
1458  * int32:x1: left x coordinate
1459  * int32:y1: top y coordinate
1460  * int32:x2: right x
1461  * int32:y2: bottom y
1462  * x2 and y2 can be omitted to assume x1==x2+1 y1==y2+1
1463  * boolean:scale: True if we should modify the zoom level, false to just
1464  *                adjust the movement/translation.
1465  * boolean:restrain: True to warp the pointer so it's visible.
1466  */
1467 static Bool
setZoomAreaAction(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1468 setZoomAreaAction (CompDisplay     *d,
1469 		   CompAction      *action,
1470 		   CompActionState state,
1471 		   CompOption      *option,
1472 		   int		   nOption)
1473 {
1474     CompScreen *s;
1475     Window     xid;
1476 
1477     xid = getIntOptionNamed (option, nOption, "root", 0);
1478     s = findScreenAtDisplay (d, xid);
1479 
1480     if (s)
1481     {
1482 	int        x1, y1, x2, y2, out;
1483 	Bool       scale, restrain;
1484 	CompOutput *o;
1485 
1486 	x1 = getIntOptionNamed (option, nOption, "x1", -1);
1487 	y1 = getIntOptionNamed (option, nOption, "y1", -1);
1488 	x2 = getIntOptionNamed (option, nOption, "x2", -1);
1489 	y2 = getIntOptionNamed (option, nOption, "y2", -1);
1490 	scale = getBoolOptionNamed (option, nOption, "scale", FALSE);
1491 	restrain = getBoolOptionNamed (option, nOption, "restrain", FALSE);
1492 
1493 	if (x1 < 0 || y1 < 0)
1494 	    return FALSE;
1495 
1496 	if (x2 < 0)
1497 	    x2 = x1 + 1;
1498 
1499 	if (y2 < 0)
1500 	    y2 = y1 + 1;
1501 
1502 	out = outputDeviceForPoint (s, x1, y1);
1503 #define WIDTH (x2 - x1)
1504 #define HEIGHT (y2 - y1)
1505 	setZoomArea (s, x1, y1, WIDTH, HEIGHT, FALSE);
1506 	o = &s->outputDev[out];
1507 	if (scale && WIDTH && HEIGHT)
1508 	    setScaleBigger (s, out, (float) WIDTH/o->width,
1509 			    (float) HEIGHT/o->height);
1510 #undef WIDTH
1511 #undef HEIGHT
1512 	if (restrain)
1513 	    restrainCursor (s, out);
1514     }
1515     return TRUE;
1516 }
1517 
1518 /* Ensure visibility of an area defined by x1->x2/y1->y2
1519  * int:x1: left X coordinate
1520  * int:x2: right X Coordinate
1521  * int:y1: top Y coordinate
1522  * int:y2: bottom Y coordinate
1523  * bool:scale: zoom out if necessary to ensure visibility
1524  * bool:restrain: Restrain the mouse cursor
1525  * int:margin: The margin to use (default: 0)
1526  * if x2/y2 is omitted, it is ignored.
1527  */
1528 static Bool
ensureVisibilityAction(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1529 ensureVisibilityAction (CompDisplay     *d,
1530 			CompAction      *action,
1531 			CompActionState state,
1532 			CompOption      *option,
1533 			int		nOption)
1534 {
1535     CompScreen *s;
1536     Window     xid;
1537 
1538     xid = getIntOptionNamed (option, nOption, "root", 0);
1539     s = findScreenAtDisplay (d, xid);
1540 
1541     if (s)
1542     {
1543 	int        x1, y1, x2, y2, margin, out;
1544 	Bool       scale, restrain;
1545 	CompOutput *o;
1546 
1547 	x1 = getIntOptionNamed (option, nOption, "x1", -1);
1548 	y1 = getIntOptionNamed (option, nOption, "y1", -1);
1549 	x2 = getIntOptionNamed (option, nOption, "x2", -1);
1550 	y2 = getIntOptionNamed (option, nOption, "y2", -1);
1551 	margin = getBoolOptionNamed (option, nOption, "margin", 0);
1552 	scale = getBoolOptionNamed (option, nOption, "scale", FALSE);
1553 	restrain = getBoolOptionNamed (option, nOption, "restrain", FALSE);
1554 	if (x1 < 0 || y1 < 0)
1555 	    return FALSE;
1556 	if (x2 < 0)
1557 	    y2 = y1 + 1;
1558 	out = outputDeviceForPoint (s, x1, y1);
1559 	ensureVisibility (s, x1, y1, margin);
1560 	if (x2 >= 0 && y2 >= 0)
1561 	    ensureVisibility (s, x2, y2, margin);
1562 	o = &s->outputDev[out];
1563 #define WIDTH (x2 - x1)
1564 #define HEIGHT (y2 - y1)
1565 	if (scale && WIDTH && HEIGHT)
1566 	    setScaleBigger (s, out, (float) WIDTH/o->width,
1567 			    (float) HEIGHT/o->height);
1568 #undef WIDTH
1569 #undef HEIGHT
1570 	if (restrain)
1571 	    restrainCursor (s, out);
1572     }
1573     return TRUE;
1574 }
1575 
1576 static Bool
zoomBoxActivate(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1577 zoomBoxActivate (CompDisplay     *d,
1578 		 CompAction      *action,
1579 		 CompActionState state,
1580 		 CompOption      *option,
1581 		 int		 nOption)
1582 {
1583     CompScreen *s;
1584     int        xid;
1585 
1586     xid = getIntOptionNamed (option, nOption, "root", 0);
1587     s = findScreenAtDisplay (d, xid);
1588 
1589     if (s)
1590     {
1591 	ZOOM_SCREEN (s);
1592 	zs->grabIndex = pushScreenGrab (s, None, "ezoom");
1593 	zs->box.x1 = pointerX;
1594 	zs->box.y1 = pointerY;
1595 	zs->box.x2 = pointerX;
1596 	zs->box.y2 = pointerY;
1597 	if (state & CompActionStateInitButton)
1598 	    action->state |= CompActionStateTermButton;
1599     return TRUE;
1600     }
1601     return FALSE;
1602 }
1603 
1604 static Bool
zoomBoxDeactivate(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1605 zoomBoxDeactivate (CompDisplay     *d,
1606 		   CompAction      *action,
1607 		   CompActionState state,
1608 		   CompOption      *option,
1609 		   int	           nOption)
1610 {
1611     CompScreen *s;
1612     int        xid;
1613 
1614     xid = getIntOptionNamed (option, nOption, "root", 0);
1615 
1616     for (s = d->screens; s; s = s->next)
1617     {
1618 	int x, y, width, height;
1619 	ZOOM_SCREEN (s);
1620 
1621 	if (zs->grabIndex)
1622 	{
1623 	    int        out;
1624 	    CompOutput *o;
1625 
1626 	    removeScreenGrab (s, zs->grabIndex, NULL);
1627 	    zs->grabIndex = 0;
1628 
1629 	    zs->box.x2 = pointerX;
1630 	    zs->box.y2 = pointerY;
1631 
1632 	    x = MIN (zs->box.x1, zs->box.x2);
1633 	    y = MIN (zs->box.y1, zs->box.y2);
1634 	    width = MAX (zs->box.x1, zs->box.x2) - x;
1635 	    height = MAX (zs->box.y1, zs->box.y2) - y;
1636 
1637 	    out = outputDeviceForGeometry (s, x,y,width,height,0);
1638 	    o = &s->outputDev[out];
1639 	    setScaleBigger (s, out, (float) width/o->width, (float)
1640 			    height/o->height);
1641 	    setZoomArea (s, x,y,width,height,FALSE);
1642 	}
1643     }
1644     return FALSE;
1645 }
1646 
1647 /* Zoom in to the area pointed to by the mouse.
1648  */
1649 static Bool
zoomIn(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1650 zoomIn (CompDisplay     *d,
1651 	CompAction      *action,
1652 	CompActionState state,
1653 	CompOption      *option,
1654 	int		nOption)
1655 {
1656     CompScreen *s;
1657     Window     xid;
1658 
1659     xid = getIntOptionNamed (option, nOption, "root", 0);
1660     s = findScreenAtDisplay (d, xid);
1661 
1662     if (s)
1663     {
1664 	int out = outputDeviceForPoint (s, pointerX, pointerY);
1665 	ZOOM_SCREEN (s);
1666 
1667 	if (zs->opt[SOPT_SYNC_MOUSE].value.b && !isInMovement (s, out))
1668 	    setCenter (s, pointerX, pointerY, TRUE);
1669 
1670 	setScale (s, out,
1671 		  zs->zooms[out].newZoom /
1672 		  zs->opt[SOPT_ZOOM_FACTOR].value.f);
1673     }
1674     return TRUE;
1675 }
1676 
1677 /* Locks down the current zoom area
1678  */
1679 static Bool
lockZoomAction(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1680 lockZoomAction (CompDisplay     *d,
1681 		CompAction      *action,
1682 		CompActionState state,
1683 		CompOption      *option,
1684 		int		nOption)
1685 {
1686     CompScreen *s;
1687     Window     xid;
1688 
1689     xid = getIntOptionNamed (option, nOption, "root", 0);
1690     s = findScreenAtDisplay (d, xid);
1691 
1692     if (s)
1693     {
1694 	int out = outputDeviceForPoint (s, pointerX, pointerY);
1695 	ZOOM_SCREEN (s);
1696 	zs->zooms[out].locked = !zs->zooms[out].locked;
1697     }
1698     return TRUE;
1699 }
1700 
1701 /* Zoom to a specific level.
1702  * target defines the target zoom level.
1703  * First set the scale level and mark the display as grabbed internally (to
1704  * catch the FocusIn event). Either target the focused window or the mouse,
1705  * depending on settings.
1706  * FIXME: A bit of a mess...
1707  */
1708 static Bool
zoomSpecific(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption,float target)1709 zoomSpecific (CompDisplay     *d,
1710 	      CompAction      *action,
1711 	      CompActionState state,
1712 	      CompOption      *option,
1713 	      int	      nOption,
1714 	      float	      target)
1715 {
1716     CompScreen *s;
1717     Window     xid;
1718 
1719     xid = getIntOptionNamed (option, nOption, "root", 0);
1720     s = findScreenAtDisplay (d, xid);
1721 
1722     if (s)
1723     {
1724 	int          x, y;
1725 	int          out = outputDeviceForPoint (s, pointerX, pointerY);
1726 	CompWindow   *w;
1727 	ZOOM_DISPLAY (d);
1728 	ZOOM_SCREEN  (s);
1729 
1730 	if (target == 1.0f && zs->zooms[out].newZoom == 1.0f)
1731 	    return FALSE;
1732 	if (otherScreenGrabExist (s, NULL))
1733 	    return FALSE;
1734 
1735 	setScale (s, out, target);
1736 
1737 	w = findWindowAtDisplay(d, d->activeWindow);
1738 	if (zd->opt[DOPT_SPECIFIC_TARGET_FOCUS].value.b
1739 	    && w && w->screen->root == s->root)
1740 	{
1741 	    zoomAreaToWindow (w);
1742 	}
1743 	else
1744 	{
1745 	    x = getIntOptionNamed (option, nOption, "x", 0);
1746 	    y = getIntOptionNamed (option, nOption, "y", 0);
1747 	    setCenter (s, x, y, FALSE);
1748 	}
1749     }
1750     return TRUE;
1751 }
1752 
1753 static Bool
zoomSpecific1(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1754 zoomSpecific1 (CompDisplay     *d,
1755 	       CompAction      *action,
1756 	       CompActionState state,
1757 	       CompOption      *option,
1758 	       int	       nOption)
1759 {
1760     ZOOM_DISPLAY (d);
1761 
1762     return zoomSpecific (d, action, state, option, nOption,
1763 			 zd->opt[DOPT_SPECIFIC_LEVEL_1].value.f);
1764 }
1765 
1766 static Bool
zoomSpecific2(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1767 zoomSpecific2 (CompDisplay     *d,
1768 	       CompAction      *action,
1769 	       CompActionState state,
1770 	       CompOption      *option,
1771 	       int	       nOption)
1772 {
1773     ZOOM_DISPLAY (d);
1774 
1775     return zoomSpecific (d, action, state, option, nOption,
1776 			 zd->opt[DOPT_SPECIFIC_LEVEL_2].value.f);
1777 }
1778 
1779 static Bool
zoomSpecific3(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1780 zoomSpecific3 (CompDisplay     *d,
1781 	       CompAction      *action,
1782 	       CompActionState state,
1783 	       CompOption      *option,
1784 	       int	       nOption)
1785 {
1786     ZOOM_DISPLAY (d);
1787 
1788     return zoomSpecific (d, action, state, option, nOption,
1789 			 zd->opt[DOPT_SPECIFIC_LEVEL_3].value.f);
1790 }
1791 
1792 /* Zooms to fit the active window to the screen without cutting
1793  * it off and targets it.
1794  */
1795 static Bool
zoomToWindow(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1796 zoomToWindow (CompDisplay     *d,
1797 	      CompAction      *action,
1798 	      CompActionState state,
1799 	      CompOption      *option,
1800 	      int	      nOption)
1801 {
1802     int        width, height, out;
1803     Window     xid;
1804     CompScreen *s;
1805     CompWindow *w;
1806     CompOutput *o;
1807 
1808     xid = getIntOptionNamed (option, nOption, "root", 0);
1809     s = findScreenAtDisplay (d, xid);
1810 
1811     if (!s)
1812 	return TRUE;
1813 
1814     xid = getIntOptionNamed (option, nOption, "window", 0);
1815     w = findWindowAtDisplay (d, xid);
1816     if (!w || w->screen->root != s->root)
1817 	return TRUE;
1818     width = w->width + w->input.left + w->input.right;
1819     height = w->height + w->input.top + w->input.bottom;
1820     out = outputDeviceForWindow (w);
1821     o = &s->outputDev[out];
1822     setScaleBigger (s, out, (float) width/o->width,
1823 		    (float) height/o->height);
1824     zoomAreaToWindow (w);
1825     return TRUE;
1826 }
1827 
1828 static Bool
zoomPanLeft(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1829 zoomPanLeft (CompDisplay     *d,
1830 	     CompAction      *action,
1831 	     CompActionState state,
1832 	     CompOption      *option,
1833 	     int	     nOption)
1834 {
1835     CompScreen *s;
1836     Window     xid;
1837 
1838     xid = getIntOptionNamed (option, nOption, "root", 0);
1839     s = findScreenAtDisplay (d, xid);
1840     if (!s)
1841 	return TRUE;
1842 
1843     panZoom (s, -1, 0);
1844     return TRUE;
1845 }
1846 static Bool
zoomPanRight(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1847 zoomPanRight (CompDisplay     *d,
1848 	      CompAction      *action,
1849 	      CompActionState state,
1850 	      CompOption      *option,
1851 	      int	      nOption)
1852 {
1853     CompScreen *s;
1854     Window     xid;
1855 
1856     xid = getIntOptionNamed (option, nOption, "root", 0);
1857     s = findScreenAtDisplay (d, xid);
1858     if (!s)
1859 	return TRUE;
1860     panZoom (s, 1, 0);
1861     return TRUE;
1862 }
1863 static Bool
zoomPanUp(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1864 zoomPanUp (CompDisplay     *d,
1865 	   CompAction      *action,
1866 	   CompActionState state,
1867 	   CompOption      *option,
1868 	   int		   nOption)
1869 {
1870     CompScreen *s;
1871     Window     xid;
1872 
1873     xid = getIntOptionNamed (option, nOption, "root", 0);
1874     s = findScreenAtDisplay (d, xid);
1875     if (!s)
1876 	return TRUE;
1877     panZoom (s, 0, -1);
1878     return TRUE;
1879 }
1880 
1881 static Bool
zoomPanDown(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1882 zoomPanDown (CompDisplay     *d,
1883 	     CompAction      *action,
1884 	     CompActionState state,
1885 	     CompOption      *option,
1886 	     int	     nOption)
1887 {
1888     CompScreen *s;
1889     Window     xid;
1890 
1891     xid = getIntOptionNamed (option, nOption, "root", 0);
1892     s = findScreenAtDisplay (d, xid);
1893     if (!s)
1894 	return TRUE;
1895     panZoom (s, 0, 1);
1896     return TRUE;
1897 }
1898 
1899 /* Centers the mouse based on zoom level and translation.
1900  */
1901 static Bool
zoomCenterMouse(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1902 zoomCenterMouse (CompDisplay     *d,
1903 		 CompAction      *action,
1904 		 CompActionState state,
1905 		 CompOption      *option,
1906 		 int		 nOption)
1907 {
1908     int        out;
1909     CompScreen *s;
1910     Window     xid;
1911     ZoomScreen *zs;
1912 
1913     xid = getIntOptionNamed (option, nOption, "root", 0);
1914     s = findScreenAtDisplay (d, xid);
1915     if (!s)
1916 	return TRUE;
1917 
1918     zs = GET_ZOOM_SCREEN (s, GET_ZOOM_DISPLAY (d));
1919     out = outputDeviceForPoint (s, pointerX, pointerY);
1920     warpPointer (s,
1921 		 (int) (s->outputDev[out].width/2 +
1922 			s->outputDev[out].region.extents.x1 - pointerX)
1923 		 + ((float) s->outputDev[out].width *
1924 			-zs->zooms[out].xtrans),
1925 		 (int) (s->outputDev[out].height/2 +
1926 			s->outputDev[out].region.extents.y1 - pointerY)
1927 		 + ((float) s->outputDev[out].height *
1928 			zs->zooms[out].ytrans));
1929     return TRUE;
1930 }
1931 
1932 /* Resize a window to fit the zoomed area.
1933  * This could probably do with some moving-stuff too.
1934  * IE: Move the zoom area afterwards. And ensure
1935  * the window isn't resized off-screen.
1936  */
1937 static Bool
zoomFitWindowToZoom(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1938 zoomFitWindowToZoom (CompDisplay     *d,
1939 		     CompAction      *action,
1940 		     CompActionState state,
1941 		     CompOption      *option,
1942 		     int	     nOption)
1943 {
1944     int            out;
1945     unsigned int   mask = CWWidth | CWHeight;
1946     Window         xid;
1947     CompScreen     *s;
1948     XWindowChanges xwc;
1949     CompWindow     *w;
1950     ZoomScreen     *zs;
1951 
1952     xid = getIntOptionNamed (option, nOption, "window", 0);
1953     w = findWindowAtDisplay (d, xid);
1954     if (!w)
1955 	return TRUE;
1956 
1957     s = w->screen;
1958     out = outputDeviceForWindow (w);
1959     zs = GET_ZOOM_SCREEN (s, GET_ZOOM_DISPLAY (d));
1960     xwc.x = w->serverX;
1961     xwc.y = w->serverY;
1962     xwc.width = (int) (s->outputDev[out].width *
1963 		       zs->zooms[out].currentZoom -
1964 		       (int) ((w->input.left + w->input.right)));
1965     xwc.height = (int) (s->outputDev[out].height *
1966 			zs->zooms[out].currentZoom -
1967 			(int) ((w->input.top + w->input.bottom)));
1968 
1969     constrainNewWindowSize (w,
1970 			    xwc.width,
1971 			    xwc.height,
1972 			    &xwc.width,
1973 			    &xwc.height);
1974 
1975     if (xwc.width == w->serverWidth)
1976 	mask &= ~CWWidth;
1977 
1978     if (xwc.height == w->serverHeight)
1979 	mask &= ~CWHeight;
1980 
1981     if (w->mapNum && (mask & (CWWidth | CWHeight)))
1982 	sendSyncRequest (w);
1983 
1984     configureXWindow (w, mask, &xwc);
1985     return TRUE;
1986 }
1987 
1988 static Bool
zoomInitiate(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1989 zoomInitiate (CompDisplay     *d,
1990 	      CompAction      *action,
1991 	      CompActionState state,
1992 	      CompOption      *option,
1993 	      int	      nOption)
1994 {
1995     zoomIn (d, action, state, option, nOption);
1996 
1997     if (state & CompActionStateInitKey)
1998 	action->state |= CompActionStateTermKey;
1999 
2000     if (state & CompActionStateInitButton)
2001 	action->state |= CompActionStateTermButton;
2002 
2003     return TRUE;
2004 }
2005 
2006 static Bool
zoomOut(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)2007 zoomOut (CompDisplay     *d,
2008 	 CompAction      *action,
2009 	 CompActionState state,
2010 	 CompOption      *option,
2011 	 int	         nOption)
2012 {
2013     CompScreen *s;
2014     Window     xid;
2015 
2016     xid = getIntOptionNamed (option, nOption, "root", 0);
2017     s = findScreenAtDisplay (d, xid);
2018     if (s)
2019     {
2020 	int out = outputDeviceForPoint (s, pointerX, pointerY);
2021 	ZOOM_SCREEN (s);
2022 
2023 	setScale (s, out,
2024 		  zs->zooms[out].newZoom *
2025 		  zs->opt[SOPT_ZOOM_FACTOR].value.f);
2026     }
2027 
2028     return TRUE;
2029 }
2030 
2031 static Bool
zoomTerminate(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)2032 zoomTerminate (CompDisplay     *d,
2033 	       CompAction      *action,
2034 	       CompActionState state,
2035 	       CompOption      *option,
2036 	       int	       nOption)
2037 {
2038     CompScreen *s;
2039     Window     xid;
2040 
2041     xid = getIntOptionNamed (option, nOption, "root", 0);
2042 
2043     for (s = d->screens; s; s = s->next)
2044     {
2045 	int out;
2046 	ZOOM_SCREEN (s);
2047 
2048 	if (xid && s->root != xid)
2049 	    continue;
2050 
2051 	out = outputDeviceForPoint (s, pointerX, pointerY);
2052 
2053 	if (zs->grabbed)
2054 	{
2055 	    zs->zooms[out].newZoom = 1.0f;
2056 	    damageScreen (s);
2057 	}
2058     }
2059     action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
2060     return FALSE;
2061 }
2062 
2063 /* Focus-track related event handling.
2064  * The lastMapped is a hack to ensure that newly mapped windows are
2065  * caught even if the grab that (possibly) triggered them affected
2066  * the mode. Windows created by a key binding (like creating a terminal
2067  * on a key binding) tends to trigger FocusIn events with mode other than
2068  * Normal. This works around this problem.
2069  * FIXME: Cleanup.
2070  * TODO: Avoid maximized windows.
2071  */
2072 static void
focusTrack(CompDisplay * d,XEvent * event)2073 focusTrack (CompDisplay *d,
2074 	    XEvent *event)
2075 {
2076     int           out;
2077     static Window lastMapped = 0;
2078     CompWindow    *w;
2079     ZoomScreen    *zs;
2080 
2081     if (event->type == MapNotify)
2082     {
2083 	lastMapped = event->xmap.window;
2084 	return;
2085     }
2086     else if (event->type != FocusIn)
2087 	return;
2088 
2089     if ((event->xfocus.mode != NotifyNormal)
2090 	&& (lastMapped != event->xfocus.window))
2091 	return;
2092 
2093     lastMapped = 0;
2094     w = findWindowAtDisplay(d, event->xfocus.window);
2095     if (w == NULL || w->id == d->activeWindow)
2096 	return;
2097 
2098     zs = GET_ZOOM_SCREEN (w->screen, GET_ZOOM_DISPLAY (d));
2099 
2100     if (time(NULL) - zs->lastChange < zs->opt[SOPT_FOCUS_DELAY].value.i ||
2101 	!zs->opt[SOPT_FOLLOW_FOCUS].value.b)
2102 	return;
2103 
2104     out = outputDeviceForWindow (w);
2105     if (!isActive (w->screen, out) &&
2106 	!zs->opt[SOPT_ALLWAYS_FOCUS_FIT_WINDOW].value.b)
2107 	return;
2108     if (zs->opt[SOPT_FOCUS_FIT_WINDOW].value.b)
2109     {
2110 	int width = w->width + w->input.left + w->input.right;
2111 	int height = w->height + w->input.top + w->input.bottom;
2112 	float scale = MAX ((float) width/w->screen->outputDev[out].width,
2113 			   (float) height/w->screen->outputDev[out].height);
2114 	if (scale > zs->opt[SOPT_AUTOSCALE_MIN].value.f)
2115 		setScale (w->screen, out, scale);
2116     }
2117     zoomAreaToWindow (w);
2118 }
2119 
2120 /* Event handler. Pass focus-related events on and handle XFixes events. */
2121 static void
zoomHandleEvent(CompDisplay * d,XEvent * event)2122 zoomHandleEvent (CompDisplay *d,
2123 		 XEvent      *event)
2124 {
2125     CompScreen *s;
2126     XMotionEvent *mev;
2127     ZOOM_DISPLAY(d);
2128 
2129     switch (event->type) {
2130 	case MotionNotify:
2131 	    mev =  (XMotionEvent *) event;
2132 	    s = findScreenAtDisplay (d, mev->root);
2133 	    if (s)
2134 	    {
2135 		ZOOM_SCREEN (s);
2136 		if (zs->grabIndex)
2137 		{
2138 		    zs->box.x2 = pointerX;
2139 		    zs->box.y2 = pointerY;
2140 		    damageScreen(s);
2141 		}
2142 	    }
2143 	    break;
2144 	case FocusIn:
2145 	case MapNotify:
2146 	    focusTrack (d, event);
2147 	    break;
2148 	default:
2149 	    if (event->type == zd->fixesEventBase + XFixesCursorNotify)
2150 	    {
2151 		XFixesCursorNotifyEvent *cev = (XFixesCursorNotifyEvent *)
2152 		    event;
2153 		s = findScreenAtDisplay (d, cev->window);
2154 		if (s)
2155 		{
2156 		    ZOOM_SCREEN(s);
2157 		    if (zs->cursor.isSet)
2158 			zoomUpdateCursor (s, &zs->cursor);
2159 		}
2160 	    }
2161 	    break;
2162     }
2163     UNWRAP (zd, d, handleEvent);
2164     (*d->handleEvent) (d, event);
2165     WRAP (zd, d, handleEvent, zoomHandleEvent);
2166 }
2167 
2168 /* Settings etc., boring stuff */
2169 static const CompMetadataOptionInfo zoomDisplayOptionInfo[] = {
2170     { "initiate", "key", 0, zoomInitiate, zoomTerminate },
2171     { "zoom_in_button", "button", 0, zoomIn, 0 },
2172     { "zoom_out_button", "button", 0, zoomOut, 0 },
2173     { "zoom_in_key", "key", 0, zoomIn, 0 },
2174     { "zoom_out_key", "key", 0, zoomOut, 0 },
2175     { "zoom_specific_1_key", "key", 0, zoomSpecific1, 0 },
2176     { "zoom_specific_2_key", "key", 0, zoomSpecific2, 0 },
2177     { "zoom_specific_3_key", "key", 0, zoomSpecific3, 0 },
2178     { "zoom_spec1", "float", "<min>0.1</min><max>1.0</max><default>1.0</default>", 0, 0 },
2179     { "zoom_spec2", "float", "<min>0.1</min><max>1.0</max><default>0.5</default>", 0, 0 },
2180     { "zoom_spec3", "float", "<min>0.1</min><max>1.0</max><default>0.2</default>", 0, 0 },
2181     { "spec_target_focus", "bool", "<default>true</default>", 0, 0 },
2182     { "pan_left_key", "key", 0, zoomPanLeft, 0 },
2183     { "pan_right_key", "key", 0, zoomPanRight, 0 },
2184     { "pan_up_key", "key", 0, zoomPanUp, 0 },
2185     { "pan_down_key", "key", 0, zoomPanDown, 0 },
2186     { "fit_to_window_key", "key", 0, zoomToWindow, 0 },
2187     { "center_mouse_key", "key", 0, zoomCenterMouse, 0 },
2188     { "fit_to_zoom_key", "key", 0, zoomFitWindowToZoom, 0 },
2189     { "ensure_visibility", "action", 0, ensureVisibilityAction, 0},
2190     { "set_zoom_area", "action", 0, setZoomAreaAction, 0},
2191     { "lock_zoom_key", "key", 0, lockZoomAction, 0},
2192     { "zoom_box_button", "button", 0, zoomBoxActivate, zoomBoxDeactivate},
2193 };
2194 
2195 static const CompMetadataOptionInfo zoomScreenOptionInfo[] = {
2196     { "follow_focus", "bool", 0, 0, 0 },
2197     { "speed", "float", "<min>0.01</min>", 0, 0 },
2198     { "timestep", "float", "<min>0.1</min>", 0, 0 },
2199     { "zoom_factor", "float", "<min>1.01</min>", 0, 0 },
2200     { "filter_linear", "bool", 0, 0, 0 },
2201     { "sync_mouse", "bool", 0, 0, 0 },
2202     { "follow_focus_delay", "int", "<min>0</min>", 0, 0 },
2203     { "pan_factor", "float", "<min>0.001</min><default>0.1</default>", 0, 0 },
2204     { "focus_fit_window", "bool", "<default>false</default>", 0, 0 },
2205     { "always_focus_fit_window", "bool", "<default>false</default>", 0, 0 },
2206     { "scale_mouse", "bool", "<default>false</default>", 0, 0 },
2207     { "scale_mouse_dynamic", "bool", "<default>true</default>", 0, 0 },
2208     { "scale_mouse_static", "float", "<default>0.8</default>", 0, 0 },
2209     { "hide_original_mouse", "bool", "<default>false</default>", 0, 0 },
2210     { "restrain_mouse", "bool", "<default>false</default>", 0, 0 },
2211     { "restrain_margin", "int", "<default>5</default>", 0, 0 },
2212     { "mouse_pan", "bool", "<default>false</default>", 0, 0 },
2213     { "minimum_zoom", "float", "<max>1.00</max>", 0, 0 },
2214     { "autoscale_min", "float", "<max>1.00</max>", 0, 0 }
2215 };
2216 
2217 static CompOption *
zoomGetScreenOptions(CompPlugin * plugin,CompScreen * screen,int * count)2218 zoomGetScreenOptions (CompPlugin *plugin,
2219 		      CompScreen *screen,
2220 		      int	 *count)
2221 {
2222     ZOOM_SCREEN (screen);
2223 
2224     *count = NUM_OPTIONS (zs);
2225     return zs->opt;
2226 }
2227 
2228 static Bool
zoomSetScreenOption(CompPlugin * plugin,CompScreen * screen,const char * name,CompOptionValue * value)2229 zoomSetScreenOption (CompPlugin      *plugin,
2230 		     CompScreen      *screen,
2231 		     const char	     *name,
2232 		     CompOptionValue *value)
2233 {
2234     CompOption *o;
2235     int	       index;
2236 
2237     ZOOM_SCREEN (screen);
2238 
2239     o = compFindOption (zs->opt, NUM_OPTIONS (zs), name, &index);
2240     if (!o)
2241 	return FALSE;
2242 
2243     return compSetScreenOption (screen, o, value);
2244 }
2245 
2246 static CompOption *
zoomGetDisplayOptions(CompPlugin * plugin,CompDisplay * display,int * count)2247 zoomGetDisplayOptions (CompPlugin  *plugin,
2248 		       CompDisplay *display,
2249 		       int	   *count)
2250 {
2251     ZOOM_DISPLAY (display);
2252     *count = NUM_OPTIONS (zd);
2253     return zd->opt;
2254 }
2255 
2256 static Bool
zoomSetDisplayOption(CompPlugin * plugin,CompDisplay * display,const char * name,CompOptionValue * value)2257 zoomSetDisplayOption (CompPlugin      *plugin,
2258 		      CompDisplay     *display,
2259 		      const char      *name,
2260 		      CompOptionValue *value)
2261 {
2262     CompOption *o;
2263     int	       index;
2264     ZOOM_DISPLAY (display);
2265 
2266     o = compFindOption (zd->opt, NUM_OPTIONS (zd), name, &index);
2267     if (!o)
2268 	return FALSE;
2269 
2270     return compSetDisplayOption (display, o, value);
2271 }
2272 
2273 
2274 static Bool
zoomInitDisplay(CompPlugin * p,CompDisplay * d)2275 zoomInitDisplay (CompPlugin  *p,
2276 		 CompDisplay *d)
2277 {
2278     int         minor, major;
2279     int		index;
2280     ZoomDisplay *zd;
2281 
2282     if (!checkPluginABI ("core", CORE_ABIVERSION))
2283 	return FALSE;
2284 
2285     if (!checkPluginABI ("mousepoll", MOUSEPOLL_ABIVERSION))
2286 	return FALSE;
2287 
2288     if (!getPluginDisplayIndex (d, "mousepoll", &index))
2289 	return FALSE;
2290 
2291     zd = malloc (sizeof (ZoomDisplay));
2292     if (!zd)
2293 	return FALSE;
2294     if (!compInitDisplayOptionsFromMetadata (d,
2295 					     &zoomMetadata,
2296 					     zoomDisplayOptionInfo,
2297 					     zd->opt,
2298 					     DOPT_NUM))
2299     {
2300 	free (zd);
2301 	return FALSE;
2302     }
2303 
2304     zd->mpFunc = d->base.privates[index].ptr;
2305     zd->screenPrivateIndex = allocateScreenPrivateIndex (d);
2306     if (zd->screenPrivateIndex < 0)
2307     {
2308 	compFiniDisplayOptions (d, zd->opt, DOPT_NUM);
2309 	free (zd);
2310 	return FALSE;
2311     }
2312 
2313     zd->fixesSupported =
2314 	XFixesQueryExtension(d->display,
2315 			     &zd->fixesEventBase,
2316 			     &zd->fixesErrorBase);
2317 
2318     XFixesQueryVersion(d->display, &major, &minor);
2319     if (major >= 4)
2320 	zd->canHideCursor = TRUE;
2321     else
2322 	zd->canHideCursor = FALSE;
2323 
2324     WRAP (zd, d, handleEvent, zoomHandleEvent);
2325     d->base.privates[displayPrivateIndex].ptr = zd;
2326     return TRUE;
2327 }
2328 
2329 static void
zoomFiniDisplay(CompPlugin * p,CompDisplay * d)2330 zoomFiniDisplay (CompPlugin  *p,
2331 		 CompDisplay *d)
2332 {
2333     ZOOM_DISPLAY (d);
2334     freeScreenPrivateIndex (d, zd->screenPrivateIndex);
2335     UNWRAP (zd, d, handleEvent);
2336     compFiniDisplayOptions (d, zd->opt, DOPT_NUM);
2337     free (zd);
2338 }
2339 
2340 static Bool
zoomInitScreen(CompPlugin * p,CompScreen * s)2341 zoomInitScreen (CompPlugin *p,
2342 		CompScreen *s)
2343 {
2344     int          i;
2345     ZoomScreen   *zs;
2346     ZOOM_DISPLAY (s->display);
2347 
2348     zs = malloc (sizeof (ZoomScreen));
2349     if (!zs)
2350 	return FALSE;
2351 
2352     if (!compInitScreenOptionsFromMetadata (s,
2353 					    &zoomMetadata,
2354 					    zoomScreenOptionInfo,
2355 					    zs->opt,
2356 					    SOPT_NUM))
2357     {
2358 	free (zs);
2359 	return FALSE;
2360     }
2361 
2362     zs->grabIndex = 0;
2363     zs->nZooms = s->nOutputDev;
2364     zs->zooms = malloc (sizeof (ZoomArea) * zs->nZooms);
2365     for (i = 0; i < zs->nZooms; i ++ )
2366     {
2367 	/* zs->grabbed is a mask ... Thus this limit */
2368 	if (i > sizeof (long int) * 8)
2369 	    break;
2370 	initialiseZoomArea (&zs->zooms[i], i);
2371     }
2372     zs->lastChange = 0;
2373     zs->grabbed = 0;
2374     zs->mouseX = -1;
2375     zs->mouseY = -1;
2376     zs->cursorInfoSelected = FALSE;
2377     zs->cursor.isSet = FALSE;
2378     zs->cursorHidden = FALSE;
2379     zs->pollHandle = 0;
2380 
2381     WRAP (zs, s, preparePaintScreen, zoomPreparePaintScreen);
2382     WRAP (zs, s, donePaintScreen, zoomDonePaintScreen);
2383     WRAP (zs, s, paintOutput, zoomPaintOutput);
2384 
2385     s->base.privates[zd->screenPrivateIndex].ptr = zs;
2386     return TRUE;
2387 }
2388 
2389 static void
zoomFiniScreen(CompPlugin * p,CompScreen * s)2390 zoomFiniScreen (CompPlugin *p,
2391 		CompScreen *s)
2392 {
2393     ZOOM_DISPLAY (s->display);
2394     ZOOM_SCREEN (s);
2395 
2396     UNWRAP (zs, s, preparePaintScreen);
2397     UNWRAP (zs, s, donePaintScreen);
2398     UNWRAP (zs, s, paintOutput);
2399     if (zs->pollHandle)
2400 	    (*zd->mpFunc->removePositionPolling) (s, zs->pollHandle);
2401 
2402     if (zs->zooms)
2403 	free (zs->zooms);
2404 
2405     damageScreen (s); // If we are unloaded and zoomed in.
2406     cursorZoomInactive (s);
2407     compFiniScreenOptions (s, zs->opt, SOPT_NUM);
2408     free (zs);
2409 }
2410 
2411 static CompBool
zoomInitObject(CompPlugin * p,CompObject * o)2412 zoomInitObject (CompPlugin *p,
2413 		CompObject *o)
2414 {
2415     static InitPluginObjectProc dispTab[] = {
2416 	(InitPluginObjectProc) 0, /* InitCore */
2417 	(InitPluginObjectProc) zoomInitDisplay,
2418 	(InitPluginObjectProc) zoomInitScreen
2419     };
2420 
2421     RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
2422 }
2423 
2424 static void
zoomFiniObject(CompPlugin * p,CompObject * o)2425 zoomFiniObject (CompPlugin *p,
2426 		CompObject *o)
2427 {
2428     static FiniPluginObjectProc dispTab[] = {
2429 	(FiniPluginObjectProc) 0, /* FiniCore */
2430 	(FiniPluginObjectProc) zoomFiniDisplay,
2431 	(FiniPluginObjectProc) zoomFiniScreen
2432     };
2433 
2434     DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
2435 }
2436 
2437 static Bool
zoomInit(CompPlugin * p)2438 zoomInit (CompPlugin *p)
2439 {
2440     if (!compInitPluginMetadataFromInfo (&zoomMetadata,
2441 					 p->vTable->name,
2442 					 zoomDisplayOptionInfo,
2443 					 DOPT_NUM,
2444 					 zoomScreenOptionInfo,
2445 					 SOPT_NUM))
2446 	return FALSE;
2447 
2448     displayPrivateIndex = allocateDisplayPrivateIndex ();
2449     if (displayPrivateIndex < 0)
2450     {
2451 	compFiniMetadata (&zoomMetadata);
2452 	return FALSE;
2453     }
2454     compAddMetadataFromFile (&zoomMetadata, p->vTable->name);
2455     return TRUE;
2456 }
2457 
2458 static CompOption *
zoomGetObjectOptions(CompPlugin * plugin,CompObject * object,int * count)2459 zoomGetObjectOptions (CompPlugin *plugin,
2460 		      CompObject *object,
2461 		      int	 *count)
2462 {
2463     static GetPluginObjectOptionsProc dispTab[] = {
2464 	(GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
2465 	(GetPluginObjectOptionsProc) zoomGetDisplayOptions,
2466 	(GetPluginObjectOptionsProc) zoomGetScreenOptions
2467     };
2468 
2469     *count = 0;
2470 
2471     RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
2472 		     NULL, (plugin, object, count));
2473 }
2474 
2475 static CompBool
zoomSetObjectOption(CompPlugin * plugin,CompObject * object,const char * name,CompOptionValue * value)2476 zoomSetObjectOption (CompPlugin      *plugin,
2477 		     CompObject      *object,
2478 		     const char      *name,
2479 		     CompOptionValue *value)
2480 {
2481     static SetPluginObjectOptionProc dispTab[] = {
2482 	(SetPluginObjectOptionProc) 0, /* SetCoreOption */
2483 	(SetPluginObjectOptionProc) zoomSetDisplayOption,
2484 	(SetPluginObjectOptionProc) zoomSetScreenOption
2485     };
2486 
2487     RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
2488 		     (plugin, object, name, value));
2489 }
2490 
2491 static void
zoomFini(CompPlugin * p)2492 zoomFini (CompPlugin *p)
2493 {
2494     freeDisplayPrivateIndex (displayPrivateIndex);
2495     compFiniMetadata (&zoomMetadata);
2496 }
2497 
2498 static CompMetadata *
zoomGetMetadata(CompPlugin * plugin)2499 zoomGetMetadata (CompPlugin *plugin)
2500 {
2501     return &zoomMetadata;
2502 }
2503 
2504 CompPluginVTable zoomVTable = {
2505     "ezoom",
2506     zoomGetMetadata,
2507     zoomInit,
2508     zoomFini,
2509     zoomInitObject,
2510     zoomFiniObject,
2511     zoomGetObjectOptions,
2512     zoomSetObjectOption
2513 };
2514 
2515 CompPluginVTable *
getCompPluginInfo20070830(void)2516 getCompPluginInfo20070830 (void)
2517 {
2518     return &zoomVTable;
2519 }
2520