1 /*
2 * This file is part of the XForms library package.
3 *
4 * XForms is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation; either version 2.1, or
7 * (at your option) any later version.
8 *
9 * XForms is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with XForms. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include <ctype.h>
24 #include "include/forms.h"
25 #include "flinternal.h"
26
27
28 static void do_interaction_step( int );
29 static int get_next_event_or_idle( int,
30 FL_FORM **,
31 XEvent * );
32 static void handle_keyboard_event( XEvent * xev,
33 int formevent );
34 static void handle_EnterNotify_event( FL_FORM * );
35 static void handle_LeaveNotify_event( void );
36 static void handle_MotionNotify_event( FL_FORM * );
37 static void handle_ButtonPress_event( FL_FORM * evform );
38 static void handle_ButtonRelease_event( FL_FORM * evform );
39 static void handle_Expose_event( FL_FORM *,
40 FL_FORM ** );
41 static void handle_ConfigureNotify_event( FL_FORM *,
42 FL_FORM ** );
43 static void handle_ClientMessage_event( FL_FORM * form,
44 void * xev );
45 static int form_event_queued( XEvent *,
46 int );
47
48 /* Waiting time (in ms) for fl_check_forms() and fl_check_only_forms().
49 Originally this value was 10 ms. */
50
51 #define SHORT_PAUSE 1
52
53
54 /* How frequently to generate FL_STEP event, in milliseconds is set here.
55 * These are modified if an idle callback exists */
56
57 static int delta_msec = FLI_TIMER_RES;
58 static XEvent st_xev;
59
60 extern void ( * fli_handle_signal )( void ); /* defined in signal.c */
61 extern int ( * fli_handle_clipboard )( void * ); /* defined in clipboard.c */
62
63 /* When set results in behaviour as in version 1.0.90 and before where
64 clicking on a non-input object didn't make an input object lose focus
65 shortly and thus no "end of editing" was signaled to the user */
66
67 static int end_event_for_input = FL_INPUT_END_EVENT_ALWAYS;
68
69
70 /***************************************
71 * Function for switching between "old" and "new" input handling
72 * when another non-input object is clicked on
73 ***************************************/
74
75 int
fl_input_end_return_handling(int type)76 fl_input_end_return_handling( int type )
77 {
78 int old_end_event_for_input = end_event_for_input;
79
80 end_event_for_input = type;
81 return old_end_event_for_input;
82 }
83
84
85 /***************************************
86 ***************************************/
87
88 static int
fli_XLookupString(XKeyEvent * xkey,char * buf,int buflen,KeySym * ks)89 fli_XLookupString( XKeyEvent * xkey,
90 char * buf,
91 int buflen,
92 KeySym * ks )
93 {
94 int len = INT_MIN;
95
96 if ( ! fli_context->xic )
97 len = XLookupString( xkey, buf, buflen, ks, 0 );
98 else
99 {
100 Status status;
101
102 len = XmbLookupString( fli_context->xic, xkey, buf, buflen, ks,
103 &status );
104
105 if ( status == XBufferOverflow )
106 len = -len;
107 }
108
109 return len;
110 }
111
112
113 /***************************************
114 * Converts 'state' member of the 'xkey' member of an XEvent
115 * into the corresponding mouse button number
116 ***************************************/
117
118 static int
xmask2button(unsigned int mask)119 xmask2button( unsigned int mask )
120 {
121 if ( mask & Button1Mask )
122 return FL_LEFT_MOUSE;
123
124 if ( mask & Button2Mask )
125 return FL_MIDDLE_MOUSE;
126
127 if ( mask & Button3Mask )
128 return FL_RIGHT_MOUSE;
129
130 if ( mask & Button4Mask )
131 return FL_SCROLLUP_MOUSE;
132
133 if ( mask & Button5Mask )
134 return FL_SCROLLDOWN_MOUSE;
135
136 return 0;
137 }
138
139
140 /***************************************
141 * A radio object is pushed
142 ***************************************/
143
144 void
fli_do_radio_push(FL_OBJECT * obj,FL_Coord x,FL_Coord y,int key,void * xev,int no_callbacks)145 fli_do_radio_push( FL_OBJECT * obj,
146 FL_Coord x,
147 FL_Coord y,
148 int key,
149 void * xev,
150 int no_callbacks )
151 {
152 FL_OBJECT *o = obj;
153
154 if ( ! obj || ! obj->radio )
155 return;
156
157 /* If this radio button does not belong to any group we have to search
158 the entire form, otherwise just treat the members of the group */
159
160 if ( obj->group_id == 0 )
161 {
162 for ( o = obj->form->first; o; o = o->next )
163 if ( o != obj
164 && o->radio
165 && o->group_id == 0
166 && fl_get_button( o ) )
167 {
168 fli_handle_object( o, FL_RELEASE, x, y, key, xev, 0 );
169 break;
170 }
171 }
172 else
173 {
174 while ( o->prev && o->prev->objclass != FL_BEGIN_GROUP )
175 o = o->prev;
176
177 for ( ; o && o->objclass != FL_END_GROUP; o = o->next )
178 if ( o != obj && o->radio && fl_get_button( o ) )
179 {
180 fli_handle_object( o, FL_RELEASE, x, y, key, xev, 0 );
181 break;
182 }
183 }
184
185 if ( ! no_callbacks )
186 fli_handle_object( obj, FL_PUSH, x, y, key, xev, 1 );
187 }
188
189
190 /***************************************
191 ***************************************/
192
193 static int
do_shortcut(FL_FORM * form,int key,FL_Coord x,FL_Coord y,XEvent * xev)194 do_shortcut( FL_FORM * form,
195 int key,
196 FL_Coord x,
197 FL_Coord y,
198 XEvent * xev )
199 {
200 int key1,
201 key2;
202 FL_OBJECT *obj;
203 long *s;
204
205 key1 = key2 = key;
206
207 /* Check for ALT modifier key */
208
209 if ( fl_keypressed( XK_Alt_L ) || fl_keypressed( XK_Alt_R ) )
210 {
211 if ( key < 256 )
212 {
213 key1 = FL_ALT_MASK + ( islower( ( int ) key ) ?
214 toupper( ( int ) key ) : key );
215 key2 = FL_ALT_MASK + key;
216 }
217 else
218 key1 = key2 = FL_ALT_MASK + key;
219 }
220
221 M_info( "do_shortcut", "win = %ld key = %d %d %d",
222 form->window, key, key1, key2 );
223
224 /* Check if an object has this as a shortcut */
225
226 for ( obj = form->first; obj; obj = obj->next )
227 {
228 if ( ! obj->shortcut || ! obj->active || ! obj->visible)
229 continue;
230
231 for ( s = obj->shortcut; *s; s++ )
232 {
233 if ( *s != key1 && *s != key2 )
234 continue;
235
236 if ( obj->objclass == FL_INPUT )
237 {
238 if ( obj != form->focusobj )
239 {
240 fli_handle_object( form->focusobj, FL_UNFOCUS,
241 x, y, 0, xev, 1 );
242 fli_handle_object( obj, FL_FOCUS, x, y, 0, xev, 1 );
243 }
244 }
245 else
246 {
247 if ( obj->radio )
248 fli_do_radio_push( obj, x, y, FL_MBUTTON1, xev, 0 );
249
250 XAutoRepeatOff( flx->display );
251 if ( ! obj->radio )
252 fli_handle_object( obj, FL_SHORTCUT, x, y, key1, xev, 1 );
253 fli_context->mouse_button = FL_SHORTCUT + key1;
254
255 /* This is not exactly correct as shortcut might quit,
256 fl_finish() will restore the keyboard state */
257
258 if ( fli_keybdcontrol.auto_repeat_mode == AutoRepeatModeOn )
259 XAutoRepeatOn( flx->display );
260 }
261
262 return 1;
263 }
264 }
265
266 return 0;
267 }
268
269
270 /***************************************
271 ***************************************/
272
273 int
fli_do_shortcut(FL_FORM * form,int key,FL_Coord x,FL_Coord y,XEvent * xev)274 fli_do_shortcut( FL_FORM * form,
275 int key,
276 FL_Coord x,
277 FL_Coord y,
278 XEvent * xev )
279 {
280 int ret = do_shortcut( form, key, x, y, xev );
281
282 if ( ! ret )
283 {
284 if ( form->child )
285 ret = do_shortcut( form->child, key, x, y, xev );
286 if ( ! ret && form->parent )
287 ret = do_shortcut( form->parent, key, x, y, xev );
288 }
289
290 return ret;
291 }
292
293
294 /***************************************
295 ***************************************/
296
297 static void
handle_keyboard(FL_FORM * form,int key,FL_Coord x,FL_Coord y,void * xev)298 handle_keyboard( FL_FORM * form,
299 int key,
300 FL_Coord x,
301 FL_Coord y,
302 void * xev )
303 {
304 FL_OBJECT *obj,
305 *special;
306
307 /* Always check shortcut first */
308
309 if ( fli_do_shortcut( form, key, x, y, xev ) )
310 return;
311
312 /* Focus policy is done as follows: Input object has the highest
313 priority. Next comes the object that wants special keys, finally
314 followed by 'mouseobj', having the lowest proiority. */
315
316 special = fli_find_first( form, FLI_FIND_KEYSPECIAL, 0, 0 );
317 obj = special ?
318 fli_find_object( special->next, FLI_FIND_KEYSPECIAL, 0, 0 ) : NULL;
319
320 /* If two or more objects want keyboard input none will get it and
321 keyboard input will go to mouseobj instead */
322
323 if ( obj && obj != special )
324 special = fli_int.mouseobj;
325
326 if ( form->focusobj )
327 {
328 FL_OBJECT *focusobj = form->focusobj;
329
330 /* Handle special keys first */
331
332 if ( key > 255 )
333 {
334 if ( IsLeft( key )
335 || IsRight( key )
336 || IsHome( key )
337 || IsEnd( key ) )
338 fli_handle_object( focusobj, FL_KEYPRESS, x, y, key, xev, 1 );
339 else if ( ( IsUp( key )
340 || IsDown( key )
341 || IsPageUp( key )
342 || IsPageDown( key ) )
343 && focusobj->wantkey & FL_KEY_TAB )
344 fli_handle_object( focusobj, FL_KEYPRESS, x, y, key, xev, 1 );
345 else if ( special && special->wantkey & FL_KEY_SPECIAL )
346 {
347 /* Moving the cursor in input field that does not have focus
348 looks weird */
349
350 if ( special->objclass != FL_INPUT )
351 fli_handle_object( special, FL_KEYPRESS,
352 x, y, key, xev, 1 );
353 }
354 else if ( key == XK_BackSpace || key == XK_Delete )
355 fli_handle_object( focusobj, FL_KEYPRESS, x, y, key, xev, 1 );
356 return;
357 }
358
359 /* The <Tab> and <Return> keys move the focus to the next or previous
360 input object (depending on <SHIFT> being pressed also, and for
361 <Return> only if the current focus object hasn't set FL_KEY_TAB as
362 it's the case for multiline input objects) */
363
364 if ( key == '\t'
365 || ( key == '\r' && ! ( focusobj->wantkey & FL_KEY_TAB ) ) )
366 {
367 fli_handle_object( focusobj, FL_UNFOCUS, x, y, 0, xev, 1 );
368
369 if ( ( ( XKeyEvent * ) xev )->state & fli_context->navigate_mask )
370 {
371 if ( ! ( obj = fli_find_object_backwards( focusobj->prev,
372 FLI_FIND_INPUT,
373 0, 0 ) ) )
374 obj = fli_find_last( form, FLI_FIND_INPUT, 0, 0 );
375 }
376 else /* search forward */
377 {
378 if ( ! ( obj = fli_find_object( focusobj->next,
379 FLI_FIND_INPUT, 0, 0 ) ) )
380
381 obj = fli_find_first( form, FLI_FIND_INPUT, 0, 0 );
382 }
383
384 fli_handle_object( obj, FL_FOCUS, x, y, 0, xev, 1 );
385 }
386 else if ( focusobj->wantkey != FL_KEY_SPECIAL )
387 fli_handle_object( focusobj, FL_KEYPRESS, x, y, key, xev, 1 );
388 return;
389 }
390
391 /* Keyboard input is not wanted */
392
393 if ( ! special || special->wantkey == 0 )
394 return;
395
396 /* Space is an exception for browser */
397
398 if ( ( ( key > 255 || key == ' ' )
399 && special->wantkey & FL_KEY_SPECIAL )
400 || ( key < 255 && special->wantkey & FL_KEY_NORMAL )
401 || ( special->wantkey == FL_KEY_ALL ) )
402 fli_handle_object( special, FL_KEYPRESS, x, y, key, xev, 1 );
403 }
404
405
406 /***************************************
407 * Updates a form according to an event
408 ***************************************/
409
410 void
fli_handle_form(FL_FORM * form,int event,int key,XEvent * xev)411 fli_handle_form( FL_FORM * form,
412 int event,
413 int key,
414 XEvent * xev )
415 {
416 FL_OBJECT *obj = NULL;
417 FL_Coord x,
418 y;
419
420 if ( ! form || form->visible != FL_VISIBLE )
421 return;
422
423 if ( form->deactivated && event != FL_DRAW )
424 return;
425
426 if ( form->parent_obj
427 && ! form->parent_obj->active
428 && event != FL_DRAW )
429 return;
430
431 if ( event != FL_STEP )
432 fli_set_form_window( form );
433
434 if ( fli_int.query_age > 0 && fli_int.mouseform )
435 {
436 fl_get_form_mouse( fli_int.mouseform, &fli_int.mousex, &fli_int.mousey,
437 &fli_int.keymask );
438 if ( event != FL_KEYPRESS )
439 key = xmask2button( fli_int.keymask );
440 fli_int.query_age = 0;
441 }
442
443 x = fli_int.mousex;
444 y = fli_int.mousey;
445
446 /* Except for step and draw events search for the object the event is
447 for */
448
449 if ( event != FL_STEP && event != FL_DRAW )
450 obj = fli_find_last( form, FLI_FIND_MOUSE, x, y );
451
452 switch ( event )
453 {
454 case FL_DRAW: /* form must be redrawn completely */
455 fl_redraw_form( form );
456 break;
457
458 case FL_ENTER: /* mouse did enter the form */
459 fli_int.mouseobj = obj;
460 fli_handle_object( fli_int.mouseobj, FL_ENTER, x, y, 0, xev, 1 );
461 break;
462
463 case FL_LEAVE: /* mouse left the form */
464 fli_handle_object( fli_int.mouseobj, FL_LEAVE, x, y, 0, xev, 1 );
465 if ( fli_int.pushobj == fli_int.mouseobj )
466 fli_int.pushobj = NULL;
467 fli_int.mouseobj = NULL;
468 break;
469
470 case FL_PUSH: /* mouse button was pushed inside the form */
471 /* Change focus: If an input object has the focus make it lose it
472 (and thus report changes) and then set the focus to either the
473 object that got pushed (if it's an input object) or back to the
474 original one. Then we have to recheck that the object the
475 FL_PUSH was for is still active - it may have become deactivated
476 due to the handler for the object that became unfocused! */
477
478 if ( obj )
479 {
480 if ( form->focusobj
481 && form->focusobj != obj
482 && ( obj->input || end_event_for_input ) )
483 {
484 FL_OBJECT *old_focusobj = form->focusobj;
485
486 fli_handle_object( old_focusobj, FL_UNFOCUS,
487 x, y, key, xev, 1 );
488
489 if ( ! obj->input || ! obj->active )
490 fli_handle_object( old_focusobj, FL_FOCUS,
491 x, y, key, xev, 1 );
492 }
493
494 if ( obj->input && obj->active )
495 fli_handle_object( obj, FL_FOCUS, x, y, key, xev, 1 );
496 }
497
498 if ( form->focusobj )
499 fli_int.keyform = form;
500
501 if ( ! obj || ! obj->active )
502 break;
503
504 /* Radio button only get handled on button release, other objects
505 get the button press unless focus is overriden */
506
507 if ( ! obj->radio
508 && ( ! obj->input
509 || ( obj->input && obj->active && obj->focus ) ) )
510 {
511 fli_int.pushobj = obj;
512 fli_handle_object( obj, FL_PUSH, x, y, key, xev, 1 );
513 }
514 else if ( obj->radio )
515 fli_do_radio_push( obj, x, y, key, xev, 0 );
516 break;
517
518 case FL_RELEASE : /* mouse button was released inside the form */
519 if ( fli_int.pushobj )
520 {
521 obj = fli_int.pushobj;
522 fli_int.pushobj = NULL;
523
524 fli_handle_object( obj, FL_RELEASE, x, y, key, xev, 1 );
525 }
526 break;
527
528 case FL_MOTION: /* mouse position changed in the form */
529 /* "Pushable" objects always get FL_MOTION events. Since there's
530 no direct EnterNotify or LeaveNotify event for objects we
531 "fake" them when an object gets entered or left. */
532
533 if ( fli_int.pushobj != NULL )
534 fli_handle_object( fli_int.pushobj, FL_MOTION, x, y, key,
535 xev, 1 );
536 else if ( obj != fli_int.mouseobj )
537 {
538 fli_handle_object( fli_int.mouseobj, FL_LEAVE, x, y, 0,
539 xev, 1 );
540 fli_handle_object( fli_int.mouseobj = obj, FL_ENTER,
541 x, y, 0, xev, 1 );
542 }
543
544 /* Objects can declare that they want FL_MOTION events even
545 though they're not "pushable" objects e.g. because they
546 have some internal structure that depends on the mouse
547 position (e.g. choice and counter objects). */
548
549 if ( obj != fli_int.pushobj && obj && obj->want_motion )
550 fli_handle_object( obj, FL_MOTION, x, y, key, xev, 1 );
551
552 break;
553
554 case FL_KEYPRESS: /* key was pressed */
555 handle_keyboard( form, key, x, y, xev );
556 break;
557
558 case FL_STEP: /* simple step */
559 obj = fli_find_first( form, FLI_FIND_AUTOMATIC, 0, 0 );
560
561 if ( obj )
562 fli_set_form_window( form ); /* set only if required */
563
564 while ( obj )
565 {
566 fli_handle_object( obj, FL_STEP, x, y, 0, xev, 1 );
567 obj = fli_find_object( obj->next, FLI_FIND_AUTOMATIC, 0, 0 );
568 }
569 break;
570
571 case FL_UPDATE:
572 /* "Pushable" objects may request an FL_UPDATE event by an
573 artificial (but not very precise) timer.*/
574
575 if ( fli_int.pushobj && fli_int.pushobj->want_update )
576 fli_handle_object( fli_int.pushobj, FL_UPDATE, x, y, key,
577 xev, 1 );
578 break;
579
580 case FL_MOVEORIGIN:
581 case FL_OTHER:
582 /* Need to dispatch it through all objects and monitor the status
583 of forms as it may get closed */
584
585 for ( obj = form->first; obj && form->visible == FL_VISIBLE;
586 obj = obj->next )
587 if ( obj->visible )
588 fli_handle_object( obj, event, x, y, key, xev, 0 );
589 break;
590 }
591 }
592
593
594 static int preemptive_consumed( FL_FORM *,
595 int,
596 XEvent * );
597
598 /***************************************
599 * Given an X event check for which of our forms it is
600 ***************************************/
601
602 FL_FORM *
fli_find_event_form(XEvent * xev)603 fli_find_event_form( XEvent * xev )
604 {
605 return fl_win_to_form( ( ( XAnyEvent * ) xev )->window );
606 }
607
608
609 /***************************************
610 ***************************************/
611
612 const XEvent *
fl_last_event(void)613 fl_last_event( void )
614 {
615 return &st_xev;
616 }
617
618
619 static int ignored_fake_configure;
620
621
622 /***************************************
623 ***************************************/
624
625 static int
button_is_really_down(void)626 button_is_really_down( void )
627 {
628 FL_Coord x,
629 y;
630 unsigned int km;
631
632 fl_get_mouse( &x, &y, &km );
633
634 return button_down( km );
635 }
636
637
638 /***************************************
639 * Handle all events in the queue and flush output buffer
640 ***************************************/
641
642 void
fli_treat_interaction_events(int wait_io)643 fli_treat_interaction_events( int wait_io )
644 {
645 XEvent xev;
646
647 if ( fl_display == None )
648 return;
649
650 /* If no event is present output buffer will be flushed. If one exists
651 XNextEvent() in do_interaction_step() will flush the output buffer */
652
653 do
654 do_interaction_step( wait_io );
655 while ( form_event_queued( &xev, QueuedAfterFlush ) );
656 }
657
658
659 /***************************************
660 ***************************************/
661
662 static void
do_interaction_step(int wait_io)663 do_interaction_step( int wait_io )
664 {
665 FL_FORM *evform = NULL;
666 static FL_FORM *redraw_form = NULL;
667
668 if ( ! get_next_event_or_idle( wait_io, &evform, &st_xev ) )
669 return;
670
671 /* Got an event for one of the forms */
672
673 #if FL_DEBUG >= ML_WARN
674 if ( st_xev.type != MotionNotify || fli_cntl.debug > 2 )
675 fli_xevent_name( "MainLoop", &st_xev );
676 #endif
677
678 fli_compress_event( &st_xev, evform->compress_mask );
679
680 fli_int.query_age++;
681
682 /* Run user raw callbacks for events, we're done if we get told that
683 we're not supposed to do anything else with the event */
684
685 if ( preemptive_consumed( evform, st_xev.type, &st_xev ) )
686 return;
687
688 /* Otherwise we need to handle the event ourself... */
689
690 switch ( st_xev.type )
691 {
692 case MappingNotify:
693 XRefreshKeyboardMapping( ( XMappingEvent * ) &st_xev );
694 break;
695
696 case FocusIn:
697 if ( evform->focusobj )
698 fli_int.keyform = evform;
699
700 if ( fli_context->xic )
701 XSetICValues( fli_context->xic,
702 XNFocusWindow, st_xev.xfocus.window,
703 XNClientWindow, st_xev.xfocus.window,
704 ( char * ) NULL );
705 break;
706
707 case FocusOut:
708 fli_int.keyform = NULL;
709 break;
710
711 case KeyPress:
712 handle_keyboard_event( &st_xev, FL_KEYPRESS );
713 break;
714
715 case KeyRelease:
716 handle_keyboard_event( &st_xev, FL_KEYRELEASE );
717 break;
718
719 case EnterNotify:
720 handle_EnterNotify_event( evform );
721 break;
722
723 case LeaveNotify:
724 handle_LeaveNotify_event( );
725 break;
726
727 case MotionNotify:
728 handle_MotionNotify_event( evform );
729 break;
730
731 case ButtonPress:
732 handle_ButtonPress_event( evform );
733 break;
734
735 case ButtonRelease:
736 handle_ButtonRelease_event( evform );
737 break;
738
739 case Expose:
740 handle_Expose_event( evform, &redraw_form );
741 break;
742
743 case ConfigureNotify:
744 handle_ConfigureNotify_event( evform, &redraw_form );
745 break;
746
747 case ClientMessage:
748 handle_ClientMessage_event( evform, &st_xev );
749 break;
750
751 case DestroyNotify: /* only sub-form gets this due to parent destroy */
752 fl_hide_form( evform );
753 break;
754
755 case SelectionClear:
756 case SelectionRequest:
757 case SelectionNotify:
758 if ( ! fli_handle_clipboard || fli_handle_clipboard( &st_xev ) < 0 )
759 fli_handle_form( evform, FL_OTHER, 0, &st_xev );
760 break;
761
762 default:
763 fli_handle_form( evform, FL_OTHER, 0, &st_xev );
764 break;
765 }
766 }
767
768
769 /***************************************
770 ***************************************/
771
772 void
fli_handle_idling(XEvent * xev,long msec,int do_idle_cb)773 fli_handle_idling( XEvent * xev,
774 long msec,
775 int do_idle_cb )
776 {
777 static int within_idle_cb = 0; /* Flag used to avoid an idle callback
778 being called from within itself */
779
780 /* Sleep a bit while being on the lookout for async IO events */
781
782 fli_watch_io( fli_context->io_rec, msec );
783
784 /* Deal with signals */
785
786 if ( fli_handle_signal )
787 fli_handle_signal( );
788
789 /* Make sure we have an up-to-date set of data for the mouse position
790 and the state of the keyboard and mouse buttons */
791
792 if ( fli_int.query_age > 0 && fli_int.mouseform )
793 {
794 fl_get_form_mouse( fli_int.mouseform, &fli_int.mousex, &fli_int.mousey,
795 &fli_int.keymask );
796 fli_int.query_age = 0;
797 xev->xmotion.time = CurrentTime;
798 }
799 else
800 xev->xmotion.time += msec;
801
802 /* FL_UPDATE and automatic handlers as well as idle callbacks get a
803 synthetic MotionNotify event. Make it up, then call the handler. */
804
805 xev->type = MotionNotify;
806 xev->xany.window = fli_int.mouseform ?
807 fli_int.mouseform->window : None;
808 xev->xany.send_event = 1;
809 xev->xmotion.state = fli_int.keymask;
810 xev->xmotion.x = fli_int.mousex;
811 xev->xmotion.y = fli_int.mousey;
812 xev->xmotion.is_hint = 0;
813
814 /* We need to send an FL_UPDATE while a mouse button is down to "pushable"
815 objects that want it (currently touch button, slider, choice, textbox
816 and counter objects) */
817
818 if ( button_down( fli_int.keymask )
819 && fli_int.pushobj
820 && fli_int.pushobj->want_update
821 && fli_int.mouseform )
822 fli_handle_form( fli_int.mouseform, FL_UPDATE,
823 xmask2button( fli_int.keymask ), xev );
824
825 /* Handle automatic tasks */
826
827 if ( fli_int.auto_count )
828 {
829 int i;
830
831 for ( i = 0; i < fli_int.formnumb; i++ )
832 if ( fli_int.forms[ i ]->num_auto_objects )
833 fli_handle_form( fli_int.forms[ i ], FL_STEP, 0, xev );
834 }
835
836 /* If there's a user idle callback invoke it (unless we got called with
837 'do_idle_cb' set to false or we're already running the idle callback) */
838
839 if ( do_idle_cb
840 && ! within_idle_cb
841 && fli_context->idle_rec
842 && fli_context->idle_rec->callback )
843 {
844 within_idle_cb = 1;
845 fli_context->idle_rec->callback( xev, fli_context->idle_rec->data );
846 within_idle_cb = 0;
847 }
848 }
849
850
851 /***************************************
852 ***************************************/
853
854 static int
get_next_event_or_idle(int wait_io,FL_FORM ** form,XEvent * xev)855 get_next_event_or_idle( int wait_io,
856 FL_FORM ** form,
857 XEvent * xev )
858 {
859 static unsigned int cnt = 0;
860 long msec;
861
862 /* Timeouts should be as precise as possible, so check them each time
863 round. Since they may dictate how long we're going to wait if there
864 is no event determine how how much time we will have to wait now */
865
866 if ( ! wait_io )
867 msec = SHORT_PAUSE;
868 else if ( fli_int.auto_count
869 || fli_int.pushobj
870 || fli_context->idle_rec )
871 msec = delta_msec;
872 else
873 msec = FL_min( delta_msec * 3, 300 );
874
875 if ( fli_context->timeout_rec )
876 fli_handle_timeouts( &msec );
877
878 /* Skip checking for an X event after 10 events, thus giving X events
879 a 10:1 priority over async IO, UPDATE events, automatic handlers and
880 idle callbacks etc. */
881
882 if ( ++cnt % 11 && XEventsQueued( flx->display, QueuedAfterFlush ) )
883 {
884 XNextEvent( flx->display, xev );
885
886 /* There might be an input manager that wants the event - FilterEvent()
887 returns true if this is the case and we are supposed to disregard
888 the event */
889
890 if ( fli_context->xic && XFilterEvent( xev, None ) )
891 return 0;
892
893 /* Find the form the event is for - if it's for one of "our" forms just
894 return, indicating that there;s something to be done, otherwise it
895 must be for e.g. a canvas window and thus has be put on the internal
896 event queue */
897
898 if ( ( *form = fli_find_event_form( xev ) ) != NULL )
899 return 1;
900
901 /* Please note: we do event compression before the user ever sees the
902 events. This is a bit questionable, at least for mouse movements,
903 since a user may want to get all events (e.g. because s/he wants
904 to draw something exactly following the mouse movements). If this
905 would be changed then care would have to be taken that in the mask
906 for MotionNotify PointerMotionHintMask is *not* set (see the
907 fli_xevent_to_mask() function in appwin.c) since that keeps most
908 motion events from coming through! */
909
910 fli_compress_event( xev,
911 ExposureMask
912 | PointerMotionMask
913 | ButtonMotionMask );
914
915 fl_XPutBackEvent( xev );
916 }
917 else
918 {
919 cnt = 0;
920 fli_handle_idling( &st_xev, msec, 1 );
921 }
922
923 return 0;
924 }
925
926
927 /***************************************
928 * Handling for KeyPress and KeyRelease events (indicated by either FL_KEYPRESS
929 * or FL_KEYRELEASE as the second argument)
930 ***************************************/
931
932 static void
handle_keyboard_event(XEvent * xev,int formevent)933 handle_keyboard_event( XEvent * xev,
934 int formevent )
935 {
936 Window win = xev->xkey.window;
937 KeySym keysym = 0;
938 unsigned char keybuf[ 227 ];
939 int kbuflen;
940
941 fli_int.mousex = xev->xkey.x;
942 fli_int.mousey = xev->xkey.y;
943 fli_int.keymask = xev->xkey.state;
944 fli_int.query_age = 0;
945
946 /* Before doing anything save the current modifiers key for the handlers */
947
948 if ( win
949 && ( ! fli_int.keyform
950 || fli_get_visible_forms_index( fli_int.keyform ) < 0 ) )
951 fli_int.keyform = fl_win_to_form( win );
952
953 /* Switch keyboard input only if different top-level form */
954
955 if ( fli_int.keyform && fli_int.keyform->window != win )
956 {
957 M_warn( "handle_keyboard_event", "pointer/keybd focus differ" );
958
959 if ( fli_int.keyform->child
960 && fli_int.keyform->child->window != win
961 && fli_int.keyform->parent
962 && fli_int.keyform->parent->window != win )
963 fli_int.keyform = fl_win_to_form( win );
964 }
965
966 if ( ! fli_int.keyform )
967 return;
968
969 kbuflen = fli_XLookupString( ( XKeyEvent * ) xev, ( char * ) keybuf,
970 sizeof keybuf, &keysym );
971
972 if ( kbuflen < 0 )
973 {
974 if ( kbuflen != INT_MIN )
975 M_err( "handle_keyboard_event", "keyboad buffer overflow?" );
976 else
977 M_err( "handle_keyboard_event", "fli_XLookupString failed?" );
978
979 return;
980 }
981
982 /* Ignore modifier keys as they don't cause action and are taken care
983 of by the lookupstring routine */
984
985 if ( IsModifierKey( keysym ) )
986 /* empty */ ;
987 else if ( IsTab( keysym ) )
988 {
989 /* Fake a tab key, on some systems shift+tab do not generate a tab */
990
991 fli_handle_form( fli_int.keyform, formevent, '\t', xev );
992 }
993 else if ( IsCursorKey( keysym ) || kbuflen == 0 )
994 fli_handle_form( fli_int.keyform, formevent, keysym, xev );
995 else
996 {
997 unsigned char *ch;
998
999 /* All regular keys, including mapped strings */
1000
1001 for ( ch = keybuf; ch < keybuf + kbuflen && fli_int.keyform; ch++ )
1002 fli_handle_form( fli_int.keyform, formevent, *ch, xev );
1003 }
1004 }
1005
1006
1007 /***************************************
1008 * Handling of EnterNotiy events
1009 ***************************************/
1010
1011 static void
handle_EnterNotify_event(FL_FORM * evform)1012 handle_EnterNotify_event( FL_FORM * evform )
1013 {
1014 Window win = st_xev.xany.window;
1015
1016 fli_int.mousex = st_xev.xcrossing.x;
1017 fli_int.mousey = st_xev.xcrossing.y;
1018 fli_int.keymask = st_xev.xcrossing.state;
1019 fli_int.query_age = 0;
1020
1021 if ( button_down( fli_int.keymask )
1022 && st_xev.xcrossing.mode != NotifyUngrab )
1023 return;
1024
1025 if ( fli_int.mouseform )
1026 fli_handle_form( fli_int.mouseform, FL_LEAVE,
1027 xmask2button( fli_int.keymask ), &st_xev );
1028
1029 if ( evform )
1030 {
1031 fli_int.mouseform = evform;
1032
1033 /* This is necessary because the window might be un-managed. To be
1034 friendly to other applications, grab focus only if absolutely
1035 necessary */
1036
1037 if ( ! fli_int.mouseform->deactivated
1038 && ! st_xev.xcrossing.focus && fli_int.unmanaged_count > 0 )
1039 {
1040 fli_check_key_focus( "EnterNotify", win );
1041 fl_winfocus( win );
1042 }
1043
1044 fli_handle_form( fli_int.mouseform, FL_ENTER,
1045 xmask2button( fli_int.keymask ), &st_xev );
1046 }
1047 #if FL_DEBUG >= ML_DEBUG
1048 else
1049 M_err( "handle_EnterNotify_event", "Null form" );
1050 #endif
1051 }
1052
1053
1054 /***************************************
1055 * Handling of LeaveNotiy events
1056 ***************************************/
1057
1058 static void
handle_LeaveNotify_event(void)1059 handle_LeaveNotify_event( void )
1060 {
1061 fli_int.mousex = st_xev.xcrossing.x;
1062 fli_int.mousey = st_xev.xcrossing.y;
1063 fli_int.keymask = st_xev.xcrossing.state;
1064 fli_int.query_age = 0;
1065
1066 if ( button_down( fli_int.keymask )
1067 && st_xev.xcrossing.mode == NotifyNormal )
1068 return;
1069
1070 /* olvwm sends LeaveNotify with NotifyGrab whenever button is clicked,
1071 ignore it. Due to Xpopup grab, (maybe Wm bug?), end grab can also
1072 generate this event. We can tell these two situations by doing a real
1073 button_down test (as opposed to relying on the keymask in event) */
1074
1075 if ( st_xev.xcrossing.mode == NotifyGrab && button_is_really_down( ) )
1076 return;
1077
1078 if ( ! fli_int.mouseform )
1079 return;
1080
1081 fli_handle_form( fli_int.mouseform, FL_LEAVE,
1082 xmask2button( fli_int.keymask ), &st_xev );
1083 }
1084
1085
1086 /***************************************
1087 * Handling of MotionNotify events
1088 ***************************************/
1089
1090 static void
handle_MotionNotify_event(FL_FORM * evform)1091 handle_MotionNotify_event( FL_FORM * evform )
1092 {
1093 Window win = st_xev.xany.window;
1094
1095 fli_int.keymask = st_xev.xmotion.state;
1096 fli_int.mousex = st_xev.xmotion.x;
1097 fli_int.mousey = st_xev.xmotion.y;
1098 fli_int.query_age = 0;
1099
1100 if ( ! fli_int.mouseform )
1101 {
1102 M_warn( "handle_MotionNotify_event", "event win = %ld", win );
1103 return;
1104 }
1105
1106 /* If there's an object that has the mouse focus but the event isn't for
1107 the form of this object the mouse position reported is not relative
1108 to that window and we need to adjust it. */
1109
1110 if ( fli_int.mouseform->window != win )
1111 {
1112 fli_int.mousex += evform->x - fli_int.mouseform->x;
1113 fli_int.mousey += evform->y - fli_int.mouseform->y;
1114 }
1115
1116 fli_handle_form( fli_int.mouseform, FL_MOTION,
1117 xmask2button( fli_int.keymask ), &st_xev );
1118 }
1119
1120
1121 /***************************************
1122 * Handling of ButttonPress events
1123 ***************************************/
1124
1125 static void
handle_ButtonPress_event(FL_FORM * evform FL_UNUSED_ARG)1126 handle_ButtonPress_event( FL_FORM * evform FL_UNUSED_ARG )
1127 {
1128 fli_int.mousex = st_xev.xbutton.x;
1129 fli_int.mousey = st_xev.xbutton.y;
1130 fli_int.keymask = st_xev.xbutton.state
1131 | ( Button1Mask << ( st_xev.xbutton.button - 1 ) );
1132 fli_int.query_age = 0;
1133
1134 fli_context->mouse_button = st_xev.xbutton.button;
1135 if ( metakey_down( fli_int.keymask ) && st_xev.xbutton.button == 2 )
1136 fli_print_version( 1 );
1137 else
1138 fli_handle_form( fli_int.mouseform, FL_PUSH,
1139 st_xev.xbutton.button, &st_xev );
1140 }
1141
1142
1143 /***************************************
1144 * Handling of ButttonRelease events
1145 ***************************************/
1146
1147 static void
handle_ButtonRelease_event(FL_FORM * evform)1148 handle_ButtonRelease_event( FL_FORM * evform )
1149 {
1150 fli_int.mousex = st_xev.xbutton.x;
1151 fli_int.mousey = st_xev.xbutton.y;
1152 fli_int.keymask = st_xev.xbutton.state
1153 & ~ ( Button1Mask << ( st_xev.xbutton.button - 1 ) );
1154 fli_int.query_age = 0;
1155
1156 fli_context->mouse_button = st_xev.xbutton.button;
1157
1158 /* Before the button was released (but after the press) a new form window
1159 may have been created, just below the mouse. In that case the mouse
1160 form, which is the one to receive the release event isn't the one that
1161 actually gets the event - it goes instead to the newly opened form
1162 window. And in this case the coordinates of where the mouse was release
1163 are relative to the new window but all the functions called for the
1164 object in the original mouse form expect them to be relative to the
1165 previous form. Thus we need to adjust these coordinates to be relative
1166 to the original mouse form window instead of the window opened since
1167 the mouse press. Thanks to Werner Heisch for finding this weired
1168 problem... */
1169
1170 if ( fli_int.mouseform )
1171 {
1172 if ( fli_int.mouseform != evform )
1173 {
1174 st_xev.xbutton.x = fli_int.mousex +=
1175 evform->x - fli_int.mouseform->x;
1176 st_xev.xbutton.y = fli_int.mousey +=
1177 evform->y - fli_int.mouseform->y;
1178 }
1179
1180 fli_handle_form( fli_int.mouseform, FL_RELEASE,
1181 st_xev.xbutton.button, &st_xev );
1182 }
1183
1184 fli_int.mouseform = evform;
1185 }
1186
1187
1188 /***************************************
1189 * Handling of Expose events
1190 ***************************************/
1191
1192 static void
handle_Expose_event(FL_FORM * evform,FL_FORM ** redraw_form)1193 handle_Expose_event( FL_FORM * evform,
1194 FL_FORM ** redraw_form )
1195 {
1196 if ( ! evform )
1197 return;
1198
1199 /* If 'redraw_form' is the same as 'evform' we actually got a
1200 ConfigureNotify before that isn't handled yet and the data for the
1201 Exposure event must be modified - set clipping to the complete area
1202 of the form since we got to redraw it completely. */
1203
1204 if ( *redraw_form == evform )
1205 {
1206 st_xev.xexpose.x = 0;
1207 st_xev.xexpose.y = 0;
1208 st_xev.xexpose.width = evform->w;
1209 st_xev.xexpose.height = evform->h;
1210 *redraw_form = NULL;
1211 }
1212 else
1213 {
1214 if ( st_xev.xexpose.x + st_xev.xexpose.width > evform->w )
1215 st_xev.xexpose.width = evform->w - st_xev.xexpose.x;
1216 if ( st_xev.xexpose.y + st_xev.xexpose.height > evform->h )
1217 st_xev.xexpose.height = evform->h - st_xev.xexpose.y;
1218 }
1219
1220 fli_set_global_clipping( st_xev.xexpose.x, st_xev.xexpose.y,
1221 st_xev.xexpose.width, st_xev.xexpose.height );
1222
1223 /* Run into trouble by ignoring configure notify */
1224
1225 if ( ignored_fake_configure )
1226 {
1227 FL_Coord neww,
1228 newh;
1229
1230 M_warn( "handle_Expose_event", "Run into trouble - correcting it" );
1231 fl_get_winsize( evform->window, &neww, &newh );
1232 fli_scale_form( evform, ( double ) neww / evform->w,
1233 ( double ) newh / evform->h );
1234 ignored_fake_configure = 0;
1235 }
1236
1237 fli_handle_form( evform, FL_DRAW, 0, &st_xev );
1238
1239 fli_unset_global_clipping( );
1240 }
1241
1242
1243 /***************************************
1244 * Handling of ConfigureNotify events
1245 ***************************************/
1246
1247 static void
handle_ConfigureNotify_event(FL_FORM * evform,FL_FORM ** redraw_form)1248 handle_ConfigureNotify_event( FL_FORM * evform,
1249 FL_FORM ** redraw_form )
1250 {
1251 Window win = st_xev.xany.window;
1252 int old_w = evform->w;
1253 int old_h = evform->h;
1254
1255 if ( ! evform )
1256 return;
1257
1258 if ( ! st_xev.xconfigure.send_event )
1259 fl_get_winorigin( win, &evform->x, &evform->y );
1260 else
1261 {
1262 evform->x = st_xev.xconfigure.x;
1263 evform->y = st_xev.xconfigure.y;
1264 M_warn( "handle_ConfigureNotify_event", "WMConfigure:x = %d y = %d"
1265 "w = %d h = %d", evform->x, evform->y, st_xev.xconfigure.width,
1266 st_xev.xconfigure.height );
1267 }
1268
1269 /* mwm sends bogus ConfigureNotify randomly without following up with a
1270 redraw event, but it does set send_event. The check is somewhat
1271 dangerous, use 'ignored_fake_configure' to make sure when we got expose
1272 we can respond correctly. The correct fix is always to get window
1273 geometry in Expose handler, but that has a two-way traffic overhead */
1274
1275 ignored_fake_configure = st_xev.xconfigure.send_event
1276 && ( st_xev.xconfigure.width != evform->w
1277 || st_xev.xconfigure.height != evform->h );
1278
1279 /* Dragging a form across the screen changes its absolute x, y coords.
1280 Objects that themselves contain forms should ensure that they are up to
1281 date. */
1282
1283 fli_handle_form( evform, FL_MOVEORIGIN, 0, &st_xev );
1284
1285 if ( st_xev.xconfigure.send_event )
1286 return;
1287
1288 /* Can't just set form->{w,h}. Need to take care of obj gravity */
1289
1290 fli_scale_form( evform, ( double ) st_xev.xconfigure.width / evform->w,
1291 ( double ) st_xev.xconfigure.height / evform->h );
1292
1293 /* If both the width and the height got smaller (or one got smaller and
1294 the other one remained unchanged) we're not going to get an Expose
1295 event, so we need to redraw the form. If only one of the lengths got
1296 smaller or remained unchanged while the other got larger the next
1297 (compressed) Expose event will only cover the added part. In this
1298 case store the forms address so on the next Expose event we receive
1299 for it its full area will be redrawn. */
1300
1301 if ( evform->w <= old_w && evform->h <= old_h )
1302 fl_redraw_form( evform );
1303 else if ( ! ( evform->w > old_w && evform->h > old_h ) )
1304 *redraw_form = evform;
1305 }
1306
1307
1308 /***************************************
1309 * Handling of ClientMessage events, intercepts WM_DELETE_WINDOW messages
1310 ***************************************/
1311
1312 static void
handle_ClientMessage_event(FL_FORM * form,void * xev)1313 handle_ClientMessage_event( FL_FORM * form,
1314 void * xev )
1315 {
1316 XClientMessageEvent *xcm = xev;
1317 static Atom atom_protocol;
1318 static Atom atom_del_win = None;
1319
1320 if ( ! atom_del_win )
1321 {
1322 atom_protocol = XInternAtom( xcm->display, "WM_PROTOCOLS", 0 );
1323 atom_del_win = XInternAtom( xcm->display, "WM_DELETE_WINDOW", 0 );
1324 }
1325
1326 /* On message for deletion of top-level window quit unless handlers are
1327 installed */
1328
1329 if ( xcm->message_type == atom_protocol
1330 && ( Atom ) xcm->data.l[ 0 ] == atom_del_win )
1331 {
1332 if ( form->close_callback )
1333 {
1334 if ( form->close_callback( form, form->close_data ) != FL_IGNORE
1335 && form->visible == FL_VISIBLE )
1336 fl_hide_form( form );
1337
1338 if ( form->sort_of_modal )
1339 fl_activate_all_forms( );
1340 }
1341 else if ( fli_context->atclose )
1342 {
1343 if ( fli_context->atclose( form,
1344 fli_context->close_data ) != FL_IGNORE )
1345 exit( 1 );
1346 }
1347 else
1348 exit( 1 );
1349 }
1350 else /* pump it through current form */
1351 fli_handle_form( form, FL_OTHER, 0, xev );
1352 }
1353
1354
1355 /***************************************
1356 * Checks all forms. Does not wait.
1357 ***************************************/
1358
1359 FL_OBJECT *
fl_check_forms(void)1360 fl_check_forms( void )
1361 {
1362 FL_OBJECT *obj;
1363
1364 if ( ! ( obj = fli_object_qread( ) ) )
1365 {
1366 fli_treat_interaction_events( 0 );
1367 fli_treat_user_events( );
1368 obj = fli_object_qread( );
1369
1370 if ( fl_display == None )
1371 return NULL;
1372 }
1373
1374 return obj;
1375 }
1376
1377
1378 /***************************************
1379 * Same as fl_check_forms() but never returns FL_EVENT.
1380 ***************************************/
1381
1382 FL_OBJECT *
fl_check_only_forms(void)1383 fl_check_only_forms( void )
1384 {
1385 FL_OBJECT *obj;
1386
1387 if ( ! ( obj = fli_object_qread( ) ) )
1388 {
1389 fli_treat_interaction_events( 0 );
1390 obj = fli_object_qread( );
1391
1392 if ( fl_display == None )
1393 return NULL;
1394 }
1395
1396 return obj;
1397 }
1398
1399
1400 /***************************************
1401 * Checks all forms and keeps checking as long as nothing happens.
1402 ***************************************/
1403
1404 FL_OBJECT *
fl_do_forms(void)1405 fl_do_forms( void )
1406 {
1407 FL_OBJECT *obj;
1408
1409 while ( ! ( obj = fli_object_qread( ) ) )
1410 {
1411 fli_treat_interaction_events( 1 );
1412 fli_treat_user_events( );
1413
1414 if ( fl_display == None )
1415 return NULL;
1416 }
1417
1418 return obj;
1419 }
1420
1421
1422 /***************************************
1423 * Same as fl_do_forms() but never returns FL_EVENT.
1424 ***************************************/
1425
1426 FL_OBJECT *
fl_do_only_forms(void)1427 fl_do_only_forms( void )
1428 {
1429 FL_OBJECT *obj;
1430
1431 while ( ! ( obj = fli_object_qread( ) ) )
1432 {
1433 fli_treat_interaction_events( 1 );
1434
1435 if ( fl_display == None )
1436 return NULL;
1437 }
1438
1439 if ( obj == FL_EVENT )
1440 M_warn( "fl_do_only_forms", "Shouldn't happen" );
1441
1442 return obj;
1443 }
1444
1445
1446 /***************************************
1447 ***************************************/
1448
1449 static int
form_event_queued(XEvent * xev,int mode)1450 form_event_queued( XEvent * xev,
1451 int mode )
1452 {
1453 if ( fl_display == None )
1454 return 0;
1455
1456 if ( XEventsQueued( flx->display, mode ) )
1457 {
1458 XPeekEvent( flx->display, xev );
1459 return fli_find_event_form( xev ) != NULL;
1460 }
1461
1462 return 0;
1463 }
1464
1465
1466 /***************************************
1467 ***************************************/
1468
1469 static int
preemptive_consumed(FL_FORM * form,int type,XEvent * xev)1470 preemptive_consumed( FL_FORM * form,
1471 int type,
1472 XEvent * xev )
1473 {
1474 if ( ! form || ! form->evmask || form->deactivated )
1475 return 0;
1476
1477 if ( ( form->evmask & FL_ALL_EVENT ) == FL_ALL_EVENT
1478 && form->all_callback )
1479 return form->all_callback( form, xev );
1480
1481 switch ( type )
1482 {
1483 case ButtonPress:
1484 if ( form->evmask & ButtonPressMask
1485 && form->push_callback )
1486 return form->push_callback( form, xev );
1487 break;
1488
1489 case ButtonRelease:
1490 if ( form->evmask & ButtonReleaseMask
1491 && form->push_callback )
1492 return form->push_callback( form, xev );
1493 break;
1494
1495 case KeyPress:
1496 if ( form->evmask & KeyPressMask
1497 && form->key_callback )
1498 return form->key_callback( form, xev );
1499 break;
1500
1501 case KeyRelease:
1502 if ( form->evmask & KeyRelease
1503 && form->key_callback )
1504 return form->key_callback( form, xev );
1505 break;
1506
1507 case EnterNotify:
1508 if ( form->evmask & EnterWindowMask
1509 && form->crossing_callback )
1510 return form->crossing_callback( form, xev );
1511 break;
1512
1513 case LeaveNotify:
1514 if ( form->evmask & LeaveWindowMask
1515 && form->crossing_callback )
1516 return form->crossing_callback( form, xev );
1517 break;
1518
1519 case MotionNotify:
1520 if ( form->evmask & ( ButtonMotionMask | PointerMotionMask )
1521 && form->motion_callback )
1522 return form->motion_callback( form, xev );
1523 }
1524
1525 return 0;
1526 }
1527
1528
1529 /***************************************
1530 * Returns the current state of the mouse buttons
1531 ***************************************/
1532
1533 long
fl_mouse_button(void)1534 fl_mouse_button( void )
1535 {
1536 return fli_context->mouse_button;
1537 }
1538
1539
1540 /***************************************
1541 * Returns the event currently being handled - only makes sense to
1542 * call this from within an object or form callback.
1543 ***************************************/
1544
1545 int
fl_current_event(void)1546 fl_current_event( void )
1547 {
1548 return fli_context->last_event;
1549 }
1550
1551
1552 /***************************************
1553 ***************************************/
1554
1555 FLI_TARGET *
fli_internal_init(void)1556 fli_internal_init( void )
1557 {
1558 static FLI_TARGET *default_flx;
1559
1560 if ( ! default_flx )
1561 default_flx = fl_calloc( 1, sizeof *default_flx );
1562
1563 return flx = default_flx;
1564 }
1565
1566
1567 /***************************************
1568 * fl_display is exposed to the outside world. Bad
1569 ***************************************/
1570
1571 void
fli_switch_target(FLI_TARGET * newtarget)1572 fli_switch_target( FLI_TARGET * newtarget )
1573 {
1574 flx = newtarget;
1575 fl_display = flx->display;
1576 }
1577
1578
1579 /***************************************
1580 ***************************************/
1581
1582 void
fli_restore_target(void)1583 fli_restore_target( void )
1584 {
1585 fli_internal_init( );
1586 fl_display = flx->display;
1587 }
1588
1589
1590 /***************************************
1591 * Currently only a single idle callback is support
1592 ***************************************/
1593
1594 static void
add_idle_callback(FL_APPEVENT_CB cb,void * data)1595 add_idle_callback( FL_APPEVENT_CB cb,
1596 void * data )
1597 {
1598 if ( ! cb )
1599 {
1600 fli_safe_free( fli_context->idle_rec );
1601 return;
1602 }
1603
1604 if ( ! fli_context->idle_rec )
1605 fli_context->idle_rec = fl_malloc( sizeof *fli_context->idle_rec );
1606
1607 fli_context->idle_rec->callback = cb;
1608 fli_context->idle_rec->data = data;
1609 }
1610
1611
1612 /***************************************
1613 * Sets an idle callback
1614 ***************************************/
1615
1616 FL_APPEVENT_CB
fl_set_idle_callback(FL_APPEVENT_CB callback,void * user_data)1617 fl_set_idle_callback( FL_APPEVENT_CB callback,
1618 void * user_data )
1619 {
1620 FL_APPEVENT_CB old =
1621 fli_context->idle_rec ? fli_context->idle_rec->callback : NULL;
1622
1623 add_idle_callback( callback, user_data );
1624
1625 /* If we have idle callbacks, decrease the wait time */
1626
1627 delta_msec = FLI_TIMER_RES * ( callback ? 0.8 : 1.0 );
1628 fli_context->idle_delta = delta_msec;
1629
1630 return old;
1631 }
1632
1633
1634 /***************************************
1635 ***************************************/
1636
1637 void
fl_set_idle_delta(long delta)1638 fl_set_idle_delta( long delta )
1639 {
1640 if ( delta < 0 )
1641 delta = FLI_TIMER_RES;
1642 else if ( delta == 0 )
1643 delta = FLI_TIMER_RES / 10;
1644
1645 delta_msec = delta;
1646 fli_context->idle_delta = delta;
1647 }
1648
1649
1650 /*
1651 * Local variables:
1652 * tab-width: 4
1653 * indent-tabs-mode: nil
1654 * End:
1655 */
1656