~unity-team/compiz/plugins-main-trunk.fix_wrong_window_move_expo

« back to all changes in this revision

Viewing changes to ezoom/src/ezoom.cpp

  • Committer: David Barth
  • Date: 2011-03-29 16:36:40 UTC
  • Revision ID: david.barth@canonical.com-20110329163640-fpen5qsoo0lbjode
initial import from compiz-plugins-main_0.9.4git20110322.orig.tar.gz

Show diffs side-by-side

added added

removed removed

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