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