add more spacing
[personal-kdebase.git] / workspace / khotkeys / libkhotkeysprivate / triggers / gestures.cpp
blob0e4612b37cc80507f63ccbe08e30b0c9dd189966
1 /****************************************************************************
3 KHotKeys
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 Based on LibStroke :
10 ( libstroke - an X11 stroke interface library
11 Copyright (c) 1996,1997,1998,1999 Mark F. Willey, ETLA Technical
12 There is a reference application available on the LibStroke Home Page:
13 http://www.etla.net/~willey/projects/libstroke/ )
15 ****************************************************************************/
17 #define _GESTURES_CPP_
19 #include "gestures.h"
21 #include <stdlib.h>
22 #include <math.h>
23 #include <assert.h>
25 #include <X11/Xlib.h>
27 #include <kapplication.h>
28 #include <kdebug.h>
29 #include <kxerrorhandler.h>
30 #include <kkeyserver.h>
32 #include "input.h"
33 #include "windows.h"
34 #include <QX11Info>
36 #include "voices.h"
38 namespace KHotKeys
41 QPointer<Gesture> gesture_handler = NULL;
43 Gesture::Gesture( bool /*enabled_P*/, QObject* parent_P )
44 : QWidget(NULL)
45 , _enabled( false )
46 , recording( false )
47 , button( 0 )
48 , exclude( NULL )
50 (void) new DeleteObject( this, parent_P );
51 nostroke_timer.setSingleShot( true );
52 connect( &nostroke_timer, SIGNAL( timeout()), SLOT( stroke_timeout()));
53 connect( windows_handler, SIGNAL( active_window_changed( WId )),
54 SLOT( active_window_changed( WId )));
58 Gesture::~Gesture()
60 enable( false );
64 void Gesture::enable( bool enabled_P )
66 if( _enabled == enabled_P )
67 return;
68 _enabled = enabled_P;
69 update_grab();
72 void Gesture::set_exclude( Windowdef_list* windows_P )
74 delete exclude;
75 // check for count() > 0 - empty exclude list means no window is excluded,
76 // but empty Windowdef_list matches everything
77 if( windows_P != NULL && windows_P->count() > 0 )
78 exclude = windows_P->copy();
79 else
80 exclude = NULL;
81 update_grab();
84 void Gesture::update_grab()
86 if( _enabled && handlers.count() > 0
87 && ( exclude == NULL || !exclude->match( Window_data( windows_handler->active_window()))))
89 kapp->removeX11EventFilter( this ); // avoid being installed twice
90 kapp->installX11EventFilter( this );
91 // CHECKME grab only when there's at least one gesture?
92 grab_mouse( true );
94 else
96 grab_mouse( false );
97 kapp->removeX11EventFilter( this );
101 void Gesture::active_window_changed( WId )
103 update_grab();
106 void Gesture::register_handler( QObject* receiver_P, const char* slot_P )
108 if( handlers.contains( receiver_P ))
109 return;
110 handlers[ receiver_P ] = true;
111 connect( this, SIGNAL( handle_gesture( const QString&, WId )),
112 receiver_P, slot_P );
113 if( handlers.count() == 1 )
114 update_grab();
117 void Gesture::unregister_handler( QObject* receiver_P, const char* slot_P )
119 if( !handlers.contains( receiver_P ))
120 return;
121 handlers.remove( receiver_P );
122 disconnect( this, SIGNAL( handle_gesture( const QString&, WId )),
123 receiver_P, slot_P );
124 if( handlers.count() == 0 )
125 update_grab();
128 bool Gesture::x11Event( XEvent* ev_P )
130 /* kDebug() << " ( type = " << ev_P->type << " )" << KeyRelease << " " << KeyPress ;
131 if( ev_P->type == XKeyPress || ev_P->type == XKeyRelease )
133 return voice_handler->x11Event( ev_P );
136 if( ev_P->type == ButtonPress && ev_P->xbutton.button == button )
138 kDebug() << "GESTURE: mouse press";
139 stroke.reset();
140 stroke.record( ev_P->xbutton.x, ev_P->xbutton.y );
141 nostroke_timer.start( timeout );
142 recording = true;
143 start_x = ev_P->xbutton.x_root;
144 start_y = ev_P->xbutton.y_root;
145 return true;
147 else if( ev_P->type == ButtonRelease && ev_P->xbutton.button == button
148 && recording )
150 recording = false;
151 nostroke_timer.stop();
152 stroke.record( ev_P->xbutton.x, ev_P->xbutton.y );
153 QString gesture( stroke.translate());
154 if( gesture.isEmpty())
156 kDebug() << "GESTURE: replay";
157 XAllowEvents( QX11Info::display(), AsyncPointer, CurrentTime );
158 XUngrabPointer( QX11Info::display(), CurrentTime );
159 mouse_replay( true );
160 return true;
162 kDebug() << "GESTURE: got: " << gesture;
163 emit handle_gesture( gesture, windows_handler->window_at_position( start_x, start_y ));
164 return true;
166 else if( ev_P->type == MotionNotify && recording )
167 { // ignore small initial movement
168 if( nostroke_timer.isActive()
169 && abs( start_x - ev_P->xmotion.x_root ) < 10
170 && abs( start_y - ev_P->xmotion.y_root ) < 10 )
171 return true;
172 nostroke_timer.stop();
173 stroke.record( ev_P->xmotion.x, ev_P->xmotion.y );
175 return false;
178 void Gesture::stroke_timeout()
180 kDebug() << "GESTURE: timeout";
181 XAllowEvents( QX11Info::display(), AsyncPointer, CurrentTime );
182 XUngrabPointer( QX11Info::display(), CurrentTime );
183 mouse_replay( false );
184 recording = false;
187 void Gesture::mouse_replay( bool release_P )
189 bool was_enabled = _enabled;
190 enable( false );
191 Mouse::send_mouse_button( button, release_P );
192 enable( was_enabled );
195 void Gesture::grab_mouse( bool grab_P )
197 if( grab_P )
199 assert( button != 0 );
200 KXErrorHandler handler;
201 static int mask[] = { 0, Button1MotionMask, Button2MotionMask, Button3MotionMask,
202 Button4MotionMask, Button5MotionMask, ButtonMotionMask, ButtonMotionMask,
203 ButtonMotionMask, ButtonMotionMask };
204 #define XCapL KKeyServer::modXLock()
205 #define XNumL KKeyServer::modXNumLock()
206 #define XScrL KKeyServer::modXScrollLock()
207 unsigned int mods[ 8 ] =
209 0, XCapL, XNumL, XNumL | XCapL,
210 XScrL, XScrL | XCapL,
211 XScrL | XNumL, XScrL | XNumL | XCapL
213 #undef XCapL
214 #undef XNumL
215 #undef XScrL
216 for( int i = 0;
217 i < 8;
218 ++i )
219 XGrabButton( QX11Info::display(), button, mods[ i ], QX11Info::appRootWindow(), False,
220 ButtonPressMask | ButtonReleaseMask | mask[ button ], GrabModeAsync, GrabModeAsync,
221 None, None );
222 bool err = handler.error( true );
223 kDebug() << "Gesture grab:" << err;
225 else
227 kDebug() << "Gesture ungrab";
228 XUngrabButton( QX11Info::display(), button, AnyModifier, QX11Info::appRootWindow());
232 void Gesture::set_mouse_button( unsigned int button_P )
234 if( button == button_P )
235 return;
236 if( !_enabled )
238 button = button_P;
239 return;
241 grab_mouse( false );
242 button = button_P;
243 grab_mouse( true );
246 void Gesture::set_timeout( int timeout_P )
248 timeout = timeout_P;
251 Stroke::Stroke()
253 reset();
254 points = new point[ MAX_POINTS ]; // CHECKME
257 Stroke::~Stroke()
259 delete[] points;
262 void Stroke::reset()
264 min_x = 10000;
265 min_y = 10000;
266 max_x = -1;
267 max_y = -1;
268 point_count = -1;
271 bool Stroke::record( int x, int y )
273 if( point_count >= MAX_POINTS )
274 return false;
275 if( point_count == -1 )
277 ++point_count;
278 points[ point_count ].x = x;
279 points[ point_count ].y = y;
280 min_x = max_x = x;
281 min_y = max_y = y;
283 else
285 // interpolate between last and current point
286 int delx = x - points[ point_count ].x;
287 int dely = y - points[ point_count ].y;
288 if( abs( delx ) > abs( dely )) // step by the greatest delta direction
290 float iy = points[ point_count ].y;
291 // go from the last point to the current, whatever direction it may be
292 for( int ix = points[ point_count ].x;
293 ( delx > 0 ) ? ( ix < x ) : ( ix > x );
294 ( delx > 0 ) ? ++ix : --ix )
296 // step the other axis by the correct increment
297 if( dely < 0 )
298 iy -= fabs( dely / ( float ) delx );
299 else
300 iy += fabs( dely / ( float ) delx );
301 // add the interpolated point
302 ++point_count;
303 if( point_count >= MAX_POINTS )
304 return false;
305 points[ point_count ].x = ix;
306 points[ point_count ].y = ( int )iy;
308 // add the last point
309 ++point_count;
310 if( point_count >= MAX_POINTS )
311 return false;
312 points[ point_count ].x = x;
313 points[ point_count ].y = y;
314 // update metrics, it's ok to do it only for the last point
315 if( x < min_x )
316 min_x = x;
317 if( x > max_x )
318 max_x = x;
319 if( y < min_y )
320 min_y = y;
321 if( y > max_y )
322 max_y = y;
324 else
325 { // same thing, but for dely larger than delx case...
326 float ix = points[ point_count ].x;
327 // go from the last point to the current, whatever direction it may be
328 for( int iy = points[ point_count ].y;
329 ( dely > 0 ) ? ( iy < y ) : ( iy > y );
330 ( dely > 0 ) ? ++iy : --iy )
332 // step the other axis by the correct increment
333 if( delx < 0 )
334 ix -= fabs( delx / ( float ) dely );
335 else
336 ix += fabs( delx / ( float ) dely );
337 // add the interpolated point
338 ++point_count;
339 if( point_count >= MAX_POINTS )
340 return false;
341 points[ point_count ].x = ( int )ix;
342 points[ point_count ].y = iy;
344 // add the last point
345 ++point_count;
346 if( point_count >= MAX_POINTS )
347 return false;
348 points[ point_count ].x = x;
349 points[ point_count ].y = y;
350 // update metrics, ts's ok to do it only for the last point
351 if( x < min_x )
352 min_x = x;
353 if( x > max_x )
354 max_x = x;
355 if( y < min_y )
356 min_y = y;
357 if( y > max_y )
358 max_y = y;
361 return true;
364 char* Stroke::translate( int min_bin_points_percentage_P, int scale_ratio_P, int min_points_P )
366 if( point_count < min_points_P )
367 return NULL;
368 // determine size of grid
369 delta_x = max_x - min_x;
370 delta_y = max_y - min_y;
371 if( delta_x > scale_ratio_P * delta_y )
373 int avg_y = ( max_y + min_y ) / 2;
374 min_y = avg_y - delta_x / 2;
375 max_y = avg_y + delta_x / 2;
376 delta_y = max_y - min_y;
378 else if( delta_y > scale_ratio_P * delta_x )
380 int avg_x = ( max_x + min_x ) / 2;
381 min_x = avg_x - delta_y / 2;
382 max_x = avg_x + delta_y / 2;
383 delta_x = max_x - min_x;
385 // calculate bin boundary positions
386 bound_x_1 = min_x + delta_x / 3;
387 bound_x_2 = min_x + 2 * delta_x / 3;
388 bound_y_1 = min_y + delta_y / 3;
389 bound_y_2 = min_y + 2 * delta_y / 3;
391 int sequence_count = 0;
392 // points-->sequence translation scratch variables
393 int prev_bin = 0;
394 int current_bin = 0;
395 int bin_count = 0;
396 // build string by placing points in bins, collapsing bins and discarding
397 // those with too few points...
398 for( int pos = 0;
399 pos <= point_count;
400 ++pos )
402 // figure out which bin the point falls in
403 current_bin = bin( points[ pos ].x, points[ pos ].y );
404 // if this is the first point, consider it the previous bin, too.
405 if( prev_bin == 0 )
406 prev_bin = current_bin;
407 if( prev_bin == current_bin )
408 bin_count++;
409 else
410 { // we are moving to a new bin -- consider adding to the sequence
411 // CHECKME tohle taky konfigurovatelne ?
412 if( bin_count >= ( min_bin_points_percentage_P * point_count / 100 )
413 || sequence_count == 0 )
415 if( sequence_count >= MAX_SEQUENCE )
416 return NULL;
417 ret_val[ sequence_count++ ] = prev_bin + '0';
419 // restart counting points in the new bin
420 bin_count=0;
421 prev_bin = current_bin;
425 // add the last run of points to the sequence
426 if( sequence_count >= MAX_SEQUENCE - 1 )
427 return NULL;
428 ret_val[ sequence_count++ ] = current_bin + '0';
429 ret_val[ sequence_count ] = 0; // endmark
430 return ret_val;
433 /* figure out which bin the point falls in */
434 int Stroke::bin( int x, int y )
436 int bin_num = 1;
437 if( x > bound_x_1 )
438 ++bin_num;
439 if( x > bound_x_2 )
440 ++bin_num;
441 if( y < bound_y_1 )
442 bin_num += 3;
443 if( y < bound_y_2 )
444 bin_num += 3;
445 return bin_num;
448 } // namespace KHotKeys
450 #include "moc_gestures.cpp"