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.
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_
27 #include <kapplication.h>
29 #include <kxerrorhandler.h>
30 #include <kkeyserver.h>
41 QPointer
<Gesture
> gesture_handler
= NULL
;
43 Gesture::Gesture( bool /*enabled_P*/, QObject
* parent_P
)
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
)));
64 void Gesture::enable( bool enabled_P
)
66 if( _enabled
== enabled_P
)
72 void Gesture::set_exclude( Windowdef_list
* windows_P
)
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();
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?
97 kapp
->removeX11EventFilter( this );
101 void Gesture::active_window_changed( WId
)
106 void Gesture::register_handler( QObject
* receiver_P
, const char* slot_P
)
108 if( handlers
.contains( receiver_P
))
110 handlers
[ receiver_P
] = true;
111 connect( this, SIGNAL( handle_gesture( const QString
&, WId
)),
112 receiver_P
, slot_P
);
113 if( handlers
.count() == 1 )
117 void Gesture::unregister_handler( QObject
* receiver_P
, const char* slot_P
)
119 if( !handlers
.contains( receiver_P
))
121 handlers
.remove( receiver_P
);
122 disconnect( this, SIGNAL( handle_gesture( const QString
&, WId
)),
123 receiver_P
, slot_P
);
124 if( handlers
.count() == 0 )
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";
140 stroke
.record( ev_P
->xbutton
.x
, ev_P
->xbutton
.y
);
141 nostroke_timer
.start( timeout
);
143 start_x
= ev_P
->xbutton
.x_root
;
144 start_y
= ev_P
->xbutton
.y_root
;
147 else if( ev_P
->type
== ButtonRelease
&& ev_P
->xbutton
.button
== button
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 );
162 kDebug() << "GESTURE: got: " << gesture
;
163 emit
handle_gesture( gesture
, windows_handler
->window_at_position( start_x
, start_y
));
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 )
172 nostroke_timer
.stop();
173 stroke
.record( ev_P
->xmotion
.x
, ev_P
->xmotion
.y
);
178 void Gesture::stroke_timeout()
180 kDebug() << "GESTURE: timeout";
181 XAllowEvents( QX11Info::display(), AsyncPointer
, CurrentTime
);
182 XUngrabPointer( QX11Info::display(), CurrentTime
);
183 mouse_replay( false );
187 void Gesture::mouse_replay( bool release_P
)
189 bool was_enabled
= _enabled
;
191 Mouse::send_mouse_button( button
, release_P
);
192 enable( was_enabled
);
195 void Gesture::grab_mouse( bool 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
219 XGrabButton( QX11Info::display(), button
, mods
[ i
], QX11Info::appRootWindow(), False
,
220 ButtonPressMask
| ButtonReleaseMask
| mask
[ button
], GrabModeAsync
, GrabModeAsync
,
222 bool err
= handler
.error( true );
223 kDebug() << "Gesture grab:" << err
;
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
)
246 void Gesture::set_timeout( int timeout_P
)
254 points
= new point
[ MAX_POINTS
]; // CHECKME
271 bool Stroke::record( int x
, int y
)
273 if( point_count
>= MAX_POINTS
)
275 if( point_count
== -1 )
278 points
[ point_count
].x
= x
;
279 points
[ point_count
].y
= y
;
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
298 iy
-= fabs( dely
/ ( float ) delx
);
300 iy
+= fabs( dely
/ ( float ) delx
);
301 // add the interpolated point
303 if( point_count
>= MAX_POINTS
)
305 points
[ point_count
].x
= ix
;
306 points
[ point_count
].y
= ( int )iy
;
308 // add the last point
310 if( point_count
>= MAX_POINTS
)
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
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
334 ix
-= fabs( delx
/ ( float ) dely
);
336 ix
+= fabs( delx
/ ( float ) dely
);
337 // add the interpolated point
339 if( point_count
>= MAX_POINTS
)
341 points
[ point_count
].x
= ( int )ix
;
342 points
[ point_count
].y
= iy
;
344 // add the last point
346 if( point_count
>= MAX_POINTS
)
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
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
)
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
396 // build string by placing points in bins, collapsing bins and discarding
397 // those with too few points...
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.
406 prev_bin
= current_bin
;
407 if( prev_bin
== current_bin
)
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
)
417 ret_val
[ sequence_count
++ ] = prev_bin
+ '0';
419 // restart counting points in the new bin
421 prev_bin
= current_bin
;
425 // add the last run of points to the sequence
426 if( sequence_count
>= MAX_SEQUENCE
- 1 )
428 ret_val
[ sequence_count
++ ] = current_bin
+ '0';
429 ret_val
[ sequence_count
] = 0; // endmark
433 /* figure out which bin the point falls in */
434 int Stroke::bin( int x
, int y
)
448 } // namespace KHotKeys
450 #include "moc_gestures.cpp"