~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to khotkeys/libkhotkeysprivate/triggers/gestures.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
 
 
3
 KHotKeys
 
4
 
 
5
 Copyright (C) 1999-2002 Lubos Lunak <l.lunak@kde.org>
 
6
 
 
7
 Distributed under the terms of the GNU General Public License version 2.
 
8
 
 
9
****************************************************************************/
 
10
 
 
11
#include "gestures.h"
 
12
 
 
13
#if 0
 
14
#include <stdlib.h>
 
15
#endif
 
16
#include <math.h>
 
17
 
 
18
#include "khotkeysglobal.h"
 
19
 
 
20
 
 
21
#include <kapplication.h>
 
22
#include <kdebug.h>
 
23
#include <kxerrorhandler.h>
 
24
#include <kkeyserver.h>
 
25
#include "input.h"
 
26
#include "windows_handler.h"
 
27
#include "action_data/action_data.h"
 
28
 
 
29
#include "windows_helper/window_selection_list.h"
 
30
 
 
31
// only necessary for circumventing bug #173606, see below
 
32
#include <QX11Info>
 
33
#include <X11/Xlib.h>
 
34
//#include <fixx11h.h>
 
35
 
 
36
// #include "voices.h"
 
37
 
 
38
namespace KHotKeys
 
39
{
 
40
 
 
41
QPointer<Gesture> gesture_handler = NULL;
 
42
 
 
43
Gesture::Gesture( bool enabled_P, QObject* parent_P )
 
44
        : QWidget(NULL)
 
45
        , _enabled( false )
 
46
        , recording( false )
 
47
        , button( 0 )
 
48
        , exclude( NULL )
 
49
    {
 
50
    kDebug() << enabled_P;
 
51
    (void) new DeleteObject( this, parent_P );
 
52
    nostroke_timer.setSingleShot( true );
 
53
    connect( &nostroke_timer, SIGNAL( timeout()), SLOT( stroke_timeout()));
 
54
    connect( windows_handler, SIGNAL( active_window_changed( WId )),
 
55
        SLOT( active_window_changed( WId )));
 
56
    }
 
57
 
 
58
 
 
59
Gesture::~Gesture()
 
60
    {
 
61
    enable( false );
 
62
    }
 
63
 
 
64
 
 
65
void Gesture::enable( bool enabled_P )
 
66
    {
 
67
    kDebug() << enabled_P;
 
68
    if( _enabled == enabled_P )
 
69
        return;
 
70
    _enabled = enabled_P;
 
71
    update_grab();
 
72
    }
 
73
 
 
74
void Gesture::set_exclude( Windowdef_list* windows_P )
 
75
    {
 
76
    delete exclude;
 
77
    // check for count() > 0 - empty exclude list means no window is excluded,
 
78
    // but empty Windowdef_list matches everything
 
79
    if( windows_P != NULL && windows_P->count() > 0 )
 
80
        exclude = windows_P->copy();
 
81
    else
 
82
        exclude = NULL;
 
83
    update_grab();
 
84
    }
 
85
 
 
86
void Gesture::update_grab()
 
87
    {
 
88
    kDebug() << "Enabled:" << _enabled;
 
89
    kDebug() << "Handler:" << handlers.count();
 
90
    kDebug() << "Exclude:" << exclude << " Match? " << (exclude && exclude->match( Window_data( windows_handler->active_window())));
 
91
 
 
92
    if( _enabled && handlers.count() > 0
 
93
        && ( exclude == NULL || !exclude->match( Window_data( windows_handler->active_window()))))
 
94
        {
 
95
        kapp->removeX11EventFilter( this ); // avoid being installed twice
 
96
        kapp->installX11EventFilter( this );
 
97
        // CHECKME grab only when there's at least one gesture?
 
98
        grab_mouse( true );
 
99
        }
 
100
    else
 
101
        {
 
102
        grab_mouse( false );
 
103
        kapp->removeX11EventFilter( this );
 
104
        }
 
105
    }
 
106
 
 
107
void Gesture::active_window_changed( WId )
 
108
    {
 
109
    update_grab();
 
110
    }
 
111
 
 
112
void Gesture::handleScore( ActionData* const data, const qreal score )
 
113
    {
 
114
    if(score > maxScore)
 
115
        {
 
116
        maxScore = score;
 
117
        bestFit = data;
 
118
        }
 
119
    }
 
120
 
 
121
void Gesture::register_handler( QObject* receiver_P, const char* slot_P )
 
122
    {
 
123
    if( handlers.contains( receiver_P ))
 
124
        return;
 
125
    handlers[ receiver_P ] = true;
 
126
    // connect directly because we want to be sure that all triggers submitted
 
127
    // their scores back to this object before executing the best match we
 
128
    // could find.
 
129
    connect( this, SIGNAL( handle_gesture( const StrokePoints& )),
 
130
            receiver_P, slot_P,
 
131
            Qt::DirectConnection
 
132
            );
 
133
    connect( receiver_P, SIGNAL( gotScore( ActionData* const,  const qreal ) ),
 
134
             this, SLOT( handleScore( ActionData* const, const qreal ) ),
 
135
            Qt::DirectConnection
 
136
            );
 
137
    if( handlers.count() == 1 )
 
138
        update_grab();
 
139
    }
 
140
 
 
141
void Gesture::unregister_handler( QObject* receiver_P, const char* slot_P )
 
142
    {
 
143
    if( !handlers.contains( receiver_P ))
 
144
        return;
 
145
    handlers.remove( receiver_P );
 
146
 
 
147
    disconnect( this, SIGNAL( handle_gesture( const StrokePoints& )),
 
148
            receiver_P, slot_P
 
149
            );
 
150
    disconnect( receiver_P, SIGNAL( gotScore( ActionData* const, const qreal ) ),
 
151
                this, SLOT( handleScore( ActionData* const, const qreal ) )
 
152
            );
 
153
    if( handlers.count() == 0 )
 
154
        update_grab();
 
155
    }
 
156
 
 
157
bool Gesture::x11Event( XEvent* ev_P )
 
158
    {
 
159
/*      kDebug() << "   ( type = " << ev_P->type << " )" << KeyRelease << " " << KeyPress ;
 
160
        if( ev_P->type == XKeyPress || ev_P->type == XKeyRelease )
 
161
        {
 
162
            return voice_handler->x11Event( ev_P );
 
163
    }*/
 
164
 
 
165
    if( ev_P->type == ButtonPress && ev_P->xbutton.button == button )
 
166
        {
 
167
        kDebug() << "GESTURE: mouse press";
 
168
        stroke.reset();
 
169
        stroke.record( ev_P->xbutton.x, ev_P->xbutton.y );
 
170
        nostroke_timer.start( timeout );
 
171
        recording = true;
 
172
        start_x = ev_P->xbutton.x_root;
 
173
        start_y = ev_P->xbutton.y_root;
 
174
        return true;
 
175
        }
 
176
    // if stroke is finished... postprocess the data and send a signal.
 
177
    // then wait for incoming matching scores and execute the best fit.
 
178
    else if( ev_P->type == ButtonRelease && ev_P->xbutton.button == button
 
179
        && recording )
 
180
        {
 
181
        recording = false;
 
182
        nostroke_timer.stop();
 
183
        stroke.record( ev_P->xbutton.x, ev_P->xbutton.y );
 
184
        StrokePoints gesture( stroke.processData() );
 
185
        if( gesture.isEmpty() )
 
186
            {
 
187
            kDebug() << "GESTURE: replay";
 
188
            XAllowEvents( QX11Info::display(), AsyncPointer, CurrentTime );
 
189
            XUngrabPointer( QX11Info::display(), CurrentTime );
 
190
            mouse_replay( true );
 
191
            return true;
 
192
            }
 
193
 
 
194
        // prepare for the incoming scores from different triggers
 
195
        maxScore = 0.0;
 
196
        bestFit = NULL;
 
197
 
 
198
        emit handle_gesture( gesture );
 
199
        // the signal is emitted directly, so we get all trigger scores before
 
200
        // the next lines are executed. bestFit should now contain
 
201
        // a pointer to the ActionData with the best-matching gesture.
 
202
 
 
203
        if( bestFit != NULL )
 
204
            {
 
205
            // set up the windows_handler
 
206
            WId window = windows_handler->window_at_position( start_x, start_y );
 
207
            windows_handler->set_action_window( window );
 
208
            // then execute the action associated with the best match.
 
209
            bestFit->execute();
 
210
            }
 
211
 
 
212
        return true;
 
213
        }
 
214
    else if( ev_P->type == MotionNotify && recording )
 
215
        { // ignore small initial movement
 
216
        if( nostroke_timer.isActive()
 
217
            && abs( start_x - ev_P->xmotion.x_root ) < 10
 
218
            && abs( start_y - ev_P->xmotion.y_root ) < 10
 
219
        )
 
220
            return true;
 
221
        nostroke_timer.stop();
 
222
 
 
223
        stroke.record( ev_P->xmotion.x, ev_P->xmotion.y );
 
224
        }
 
225
    return false;
 
226
    }
 
227
 
 
228
 
 
229
void Gesture::stroke_timeout()
 
230
    {
 
231
    kDebug() << "GESTURE: timeout";
 
232
    XAllowEvents( QX11Info::display(), AsyncPointer, CurrentTime );
 
233
    XUngrabPointer( QX11Info::display(), CurrentTime );
 
234
    mouse_replay( false );
 
235
    
 
236
    // for xorg-server 1.7 to 1.9 RC4: disable drag'n'drop support to evade bug #173606
 
237
    if( VendorRelease( QX11Info::display() ) < 10899905 &&  VendorRelease( QX11Info::display() ) >= 10700000 )
 
238
        mouse_replay( true );
 
239
      
 
240
    recording = false;
 
241
    }
 
242
 
 
243
void Gesture::mouse_replay( bool release_P )
 
244
    {
 
245
    bool was_enabled = _enabled;
 
246
    enable( false );
 
247
    Mouse::send_mouse_button( button, release_P );
 
248
    enable( was_enabled );
 
249
    }
 
250
 
 
251
void Gesture::grab_mouse( bool grab_P )
 
252
    {
 
253
    kDebug() << grab_P;
 
254
 
 
255
    if( grab_P )
 
256
        {
 
257
        kDebug() << "gesture grab";
 
258
        Q_ASSERT( button != 0 );
 
259
        KXErrorHandler handler;
 
260
        static int mask[] = { 0, Button1MotionMask, Button2MotionMask, Button3MotionMask,
 
261
            Button4MotionMask, Button5MotionMask, ButtonMotionMask, ButtonMotionMask,
 
262
            ButtonMotionMask, ButtonMotionMask };
 
263
#define XCapL KKeyServer::modXLock()
 
264
#define XNumL KKeyServer::modXNumLock()
 
265
#define XScrL KKeyServer::modXScrollLock()
 
266
        unsigned int mods[ 8 ] =
 
267
            {
 
268
            0, XCapL, XNumL, XNumL | XCapL,
 
269
            XScrL, XScrL | XCapL,
 
270
            XScrL | XNumL, XScrL | XNumL | XCapL
 
271
            };
 
272
#undef XCapL
 
273
#undef XNumL
 
274
#undef XScrL
 
275
        for( int i = 0;
 
276
             i < 8;
 
277
             ++i )
 
278
            XGrabButton( QX11Info::display(), button, mods[ i ], QX11Info::appRootWindow(), False,
 
279
                ButtonPressMask | ButtonReleaseMask | mask[ button ], GrabModeAsync, GrabModeAsync,
 
280
                None, None );
 
281
        bool err = handler.error( true );
 
282
        kDebug() << "Gesture grab:" << err;
 
283
        }
 
284
    else
 
285
        {
 
286
        kDebug() << "Gesture ungrab";
 
287
        XUngrabButton( QX11Info::display(), button, AnyModifier, QX11Info::appRootWindow());
 
288
        }
 
289
    }
 
290
 
 
291
void Gesture::set_mouse_button( unsigned int button_P )
 
292
    {
 
293
    if( button == button_P )
 
294
        return;
 
295
    if( !_enabled )
 
296
        {
 
297
        button = button_P;
 
298
        return;
 
299
        }
 
300
    grab_mouse( false );
 
301
    button = button_P;
 
302
    grab_mouse( true );
 
303
    }
 
304
 
 
305
void Gesture::set_timeout( int timeout_P )
 
306
    {
 
307
    timeout = timeout_P;
 
308
    }
 
309
 
 
310
 
 
311
 
 
312
// Definitions for Gesture end here, Definitions for Stroke following.
 
313
 
 
314
 
 
315
Stroke::Stroke()
 
316
    {
 
317
    reset();
 
318
    points = new point[ MAX_POINTS ]; // CHECKME
 
319
    }
 
320
 
 
321
Stroke::~Stroke()
 
322
    {
 
323
    delete[] points;
 
324
    }
 
325
 
 
326
void Stroke::reset()
 
327
    {
 
328
    min_x = 10000;
 
329
    min_y = 10000;
 
330
    max_x = -1;
 
331
    max_y = -1;
 
332
    point_count = -1;
 
333
    }
 
334
 
 
335
 
 
336
bool Stroke::record( int x, int y )
 
337
    {
 
338
    if( point_count == -1 )
 
339
        {
 
340
        ++point_count;
 
341
        points[ point_count ].x = x;
 
342
        points[ point_count ].y = y;
 
343
 
 
344
        // start metrics
 
345
        min_x = max_x = x;
 
346
        min_y = max_y = y;
 
347
        }
 
348
    else
 
349
        {
 
350
        ++point_count;
 
351
        if( point_count >= MAX_POINTS )
 
352
            return false;
 
353
        points[ point_count ].x = x;
 
354
        points[ point_count ].y = y;
 
355
 
 
356
        // update metrics
 
357
        if( x < min_x )
 
358
            min_x = x;
 
359
        if( x > max_x )
 
360
            max_x = x;
 
361
        if( y < min_y )
 
362
            min_y = y;
 
363
        if( y > max_y )
 
364
            max_y = y;
 
365
 
 
366
        }
 
367
    return true;
 
368
    }
 
369
 
 
370
 
 
371
// Compute some additional data from the raw point coordinates and store
 
372
// it all in a new data structure to be passed on and saved.
 
373
 
 
374
StrokePoints Stroke::processData()
 
375
    {
 
376
    if(point_count < 2 )
 
377
        return StrokePoints(); // empty vector
 
378
 
 
379
    int n = point_count-1;
 
380
 
 
381
    StrokePoints results(n);
 
382
 
 
383
    // calculate s, where s is the length of a stroke up to the current point
 
384
    // (first loop) divided by the total stroke length (second loop)
 
385
    qreal strokelength = 0.0;
 
386
    results[0].s = 0.0;
 
387
 
 
388
    for (int i = 0; i < n-1; i++)
 
389
        {
 
390
        strokelength += hypot(points[i+1].x - points[i].x, points[i+1].y - points[i].y);
 
391
        results[i+1].s = strokelength;
 
392
        }
 
393
 
 
394
    for (int i = 0; i < n; i++)
 
395
        results[i].s /= strokelength;
 
396
 
 
397
 
 
398
    // check which axis is longer...
 
399
    int scaleX = max_x - min_x;
 
400
    int scaleY = max_y - min_y;
 
401
    qreal scale = (scaleX > scaleY) ? scaleX : scaleY;
 
402
 
 
403
    // ...and scale the stroke coordinates to a new size depending on this axis
 
404
    // (saving into the new data structure for higher precision)
 
405
    for (int i = 0; i < n; i++)
 
406
        {
 
407
        results[i].x = (points[i].x-(min_x+max_x)/2.0)/scale + 0.5;
 
408
        results[i].y = (points[i].y-(min_y+max_y)/2.0)/scale + 0.5;
 
409
        }
 
410
 
 
411
 
 
412
    // calculate values of delta_s and angle for the points     by simple comparison
 
413
    // with the respective successor.
 
414
    // delta_s is the distance to the successor in the same units as s.
 
415
    // angle is the angle to the successor in units of pi.
 
416
    for (int i = 0; i < n-1; i++)
 
417
        {
 
418
        results[i].delta_s = results[i+1].s - results[i].s;
 
419
        results[i].angle = atan2(results[i+1].y - results[i].y, results[i+1].x - results[i].x)/M_PI;
 
420
        }
 
421
 
 
422
    // last point of result would need special logic, so we simply discard it -
 
423
    // there's enough points anyway
 
424
    results.pop_back();
 
425
 
 
426
    return results;
 
427
    }
 
428
 
 
429
} // namespace KHotKeys
 
430
 
 
431
#include "moc_gestures.cpp"