1
/****************************************************************************
5
Copyright (C) 1999-2002 Lubos Lunak <l.lunak@kde.org>
7
Distributed under the terms of the GNU General Public License version 2.
9
****************************************************************************/
18
#include "khotkeysglobal.h"
21
#include <kapplication.h>
23
#include <kxerrorhandler.h>
24
#include <kkeyserver.h>
26
#include "windows_handler.h"
27
#include "action_data/action_data.h"
29
#include "windows_helper/window_selection_list.h"
31
// only necessary for circumventing bug #173606, see below
34
//#include <fixx11h.h>
36
// #include "voices.h"
41
QPointer<Gesture> gesture_handler = NULL;
43
Gesture::Gesture( bool enabled_P, QObject* parent_P )
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 )));
65
void Gesture::enable( bool enabled_P )
67
kDebug() << enabled_P;
68
if( _enabled == enabled_P )
74
void Gesture::set_exclude( Windowdef_list* windows_P )
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();
86
void Gesture::update_grab()
88
kDebug() << "Enabled:" << _enabled;
89
kDebug() << "Handler:" << handlers.count();
90
kDebug() << "Exclude:" << exclude << " Match? " << (exclude && exclude->match( Window_data( windows_handler->active_window())));
92
if( _enabled && handlers.count() > 0
93
&& ( exclude == NULL || !exclude->match( Window_data( windows_handler->active_window()))))
95
kapp->removeX11EventFilter( this ); // avoid being installed twice
96
kapp->installX11EventFilter( this );
97
// CHECKME grab only when there's at least one gesture?
103
kapp->removeX11EventFilter( this );
107
void Gesture::active_window_changed( WId )
112
void Gesture::handleScore( ActionData* const data, const qreal score )
121
void Gesture::register_handler( QObject* receiver_P, const char* slot_P )
123
if( handlers.contains( receiver_P ))
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
129
connect( this, SIGNAL( handle_gesture( const StrokePoints& )),
133
connect( receiver_P, SIGNAL( gotScore( ActionData* const, const qreal ) ),
134
this, SLOT( handleScore( ActionData* const, const qreal ) ),
137
if( handlers.count() == 1 )
141
void Gesture::unregister_handler( QObject* receiver_P, const char* slot_P )
143
if( !handlers.contains( receiver_P ))
145
handlers.remove( receiver_P );
147
disconnect( this, SIGNAL( handle_gesture( const StrokePoints& )),
150
disconnect( receiver_P, SIGNAL( gotScore( ActionData* const, const qreal ) ),
151
this, SLOT( handleScore( ActionData* const, const qreal ) )
153
if( handlers.count() == 0 )
157
bool Gesture::x11Event( XEvent* ev_P )
159
/* kDebug() << " ( type = " << ev_P->type << " )" << KeyRelease << " " << KeyPress ;
160
if( ev_P->type == XKeyPress || ev_P->type == XKeyRelease )
162
return voice_handler->x11Event( ev_P );
165
if( ev_P->type == ButtonPress && ev_P->xbutton.button == button )
167
kDebug() << "GESTURE: mouse press";
169
stroke.record( ev_P->xbutton.x, ev_P->xbutton.y );
170
nostroke_timer.start( timeout );
172
start_x = ev_P->xbutton.x_root;
173
start_y = ev_P->xbutton.y_root;
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
182
nostroke_timer.stop();
183
stroke.record( ev_P->xbutton.x, ev_P->xbutton.y );
184
StrokePoints gesture( stroke.processData() );
185
if( gesture.isEmpty() )
187
kDebug() << "GESTURE: replay";
188
XAllowEvents( QX11Info::display(), AsyncPointer, CurrentTime );
189
XUngrabPointer( QX11Info::display(), CurrentTime );
190
mouse_replay( true );
194
// prepare for the incoming scores from different triggers
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.
203
if( bestFit != NULL )
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.
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
221
nostroke_timer.stop();
223
stroke.record( ev_P->xmotion.x, ev_P->xmotion.y );
229
void Gesture::stroke_timeout()
231
kDebug() << "GESTURE: timeout";
232
XAllowEvents( QX11Info::display(), AsyncPointer, CurrentTime );
233
XUngrabPointer( QX11Info::display(), CurrentTime );
234
mouse_replay( false );
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 );
243
void Gesture::mouse_replay( bool release_P )
245
bool was_enabled = _enabled;
247
Mouse::send_mouse_button( button, release_P );
248
enable( was_enabled );
251
void Gesture::grab_mouse( bool grab_P )
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 ] =
268
0, XCapL, XNumL, XNumL | XCapL,
269
XScrL, XScrL | XCapL,
270
XScrL | XNumL, XScrL | XNumL | XCapL
278
XGrabButton( QX11Info::display(), button, mods[ i ], QX11Info::appRootWindow(), False,
279
ButtonPressMask | ButtonReleaseMask | mask[ button ], GrabModeAsync, GrabModeAsync,
281
bool err = handler.error( true );
282
kDebug() << "Gesture grab:" << err;
286
kDebug() << "Gesture ungrab";
287
XUngrabButton( QX11Info::display(), button, AnyModifier, QX11Info::appRootWindow());
291
void Gesture::set_mouse_button( unsigned int button_P )
293
if( button == button_P )
305
void Gesture::set_timeout( int timeout_P )
312
// Definitions for Gesture end here, Definitions for Stroke following.
318
points = new point[ MAX_POINTS ]; // CHECKME
336
bool Stroke::record( int x, int y )
338
if( point_count == -1 )
341
points[ point_count ].x = x;
342
points[ point_count ].y = y;
351
if( point_count >= MAX_POINTS )
353
points[ point_count ].x = x;
354
points[ point_count ].y = y;
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.
374
StrokePoints Stroke::processData()
377
return StrokePoints(); // empty vector
379
int n = point_count-1;
381
StrokePoints results(n);
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;
388
for (int i = 0; i < n-1; i++)
390
strokelength += hypot(points[i+1].x - points[i].x, points[i+1].y - points[i].y);
391
results[i+1].s = strokelength;
394
for (int i = 0; i < n; i++)
395
results[i].s /= strokelength;
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;
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++)
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;
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++)
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;
422
// last point of result would need special logic, so we simply discard it -
423
// there's enough points anyway
429
} // namespace KHotKeys
431
#include "moc_gestures.cpp"