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 /**
20  * \file events.c
21  *
22  *  This file is part of the XForms library package.
23  *  Copyright (c) 1996-2002  T.C. Zhao and Mark Overmars
24  *  All rights reserved.
25  *
26  *  Events handlers for the application window
27  */
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include "include/forms.h"
34 #include "flinternal.h"
35 #include "private/flsnprintf.h"
36 
37 
38 static void handle_input_object( FL_OBJECT * obj,
39                                  int         event );
40 
41 /*** Global event handlers for all windows ******/
42 
43 static FL_APPEVENT_CB fli_event_callback;
44 static void *fli_user_data;
45 
46 FL_OBJECT *fli_handled_obj = NULL;
47 FL_OBJECT *fli_handled_parent = NULL;
48 
49 
50 /***************************************
51  * Function returns 1 if the event is consumed so it will never
52  * reach the application window event queue
53  ***************************************/
54 
55 int
fli_handle_event_callbacks(XEvent * xev)56 fli_handle_event_callbacks( XEvent * xev )
57 {
58     Window win = ( ( XAnyEvent * ) xev )->window;
59     FLI_WIN *fwin = fli_app_win;
60 
61     while ( fwin && fwin->win != win )
62         fwin = fwin->next;
63 
64     if ( ! fwin )
65     {
66         /* If there's a callback for events for independendly created user
67            windows that returns 0 the event has been handled, otherwise
68            ignore it */
69 
70         if ( fli_event_callback && ! fli_event_callback( xev, fli_user_data ) )
71             return 1;
72 
73         M_warn( "fli_handle_event_callbacks", "Unknown window = %ld",
74                 xev->xany.window );
75         fli_xevent_name( "Ignored", xev );
76         return 1;
77     }
78 
79     if (    fwin->pre_emptive
80          && fwin->pre_emptive( xev, fwin->pre_emptive_data ) == FL_PREEMPT )
81         return 1;
82 
83     if ( fwin->callback[ xev->type ] )
84     {
85         fwin->callback[ xev->type ]( xev, fwin->user_data[ xev->type ] );
86         return 1;
87     }
88 
89     return 0;
90 }
91 
92 
93 /***************************************
94  * Sets the callback routine for the events
95  ***************************************/
96 
97 FL_APPEVENT_CB
fl_set_event_callback(FL_APPEVENT_CB callback,void * user_data)98 fl_set_event_callback( FL_APPEVENT_CB callback,
99                        void *         user_data )
100 {
101     FL_APPEVENT_CB old = fli_event_callback;
102 
103     fli_event_callback = callback;
104     fli_user_data = user_data;
105     return old;
106 }
107 
108 
109 /********* End of Application Window management ***********}*****/
110 
111 
112 /*************** THE OBJECT EVENTS *************{******/
113 /*************** CALL-BACK ROUTINE HANDLING ***********/
114 
115 /* Normally, the object queue doesn't have to be large and a default
116    size is sufficient. In the case that more objects need to be
117    stored the queue isn't increased by just another element but
118    instead by the same number of objects we started with, reducing
119    the number of calls of malloc() a bit */
120 
121 #define FLI_QSIZE         64            /* chunk size of object queue */
122 
123 typedef struct FLI_OBJECT_QUEUE_ENTRY_ {
124     FL_OBJECT *                      obj;
125     int                              ret;
126     int                              event;
127     struct FLI_OBJECT_QUEUE_ENTRY_ * next;
128 } FLI_OBJECT_QUEUE_ENTRY;
129 
130 typedef struct FLI_OBJECT_QUEUE_ {
131     FLI_OBJECT_QUEUE_ENTRY * head;       /* here objects get added to */
132     FLI_OBJECT_QUEUE_ENTRY * tail;       /* and here they get removed from */
133     FLI_OBJECT_QUEUE_ENTRY * empty;      /* linked list of empty entries */
134     FLI_OBJECT_QUEUE_ENTRY * blocks;     /* pointer to linked list of blocks */
135 } FLI_OBJECT_QUEUE;
136 
137 static FLI_OBJECT_QUEUE obj_queue = { NULL, NULL, NULL, NULL };
138 
139 
140 /***************************************************
141  * Function for creating/extending the object queue
142  * (gets called automatically the first time an
143  * object gets pushed on the queue, so no previous
144  * call, e.g. from fl_initialize(), is necessary)
145  ***************************************************/
146 
147 static void
extend_obj_queue(void)148 extend_obj_queue( void )
149 {
150     FLI_OBJECT_QUEUE_ENTRY *p = fl_malloc( ( FLI_QSIZE + 1 ) * sizeof *p );
151     size_t i;
152 
153     /* The first element of the (new) area is used for book-keeping purposes */
154 
155     p->next = obj_queue.blocks;
156     obj_queue.blocks = p++;
157 
158     /* The rest gets added to (or makes up) the empty list */
159 
160     obj_queue.empty = p;
161 
162     for ( i = 0; i < FLI_QSIZE - 1; p++, i++ )
163         p->next = p + 1;
164 
165     p->next = NULL;
166 }
167 
168 
169 /******************************************************
170  * Fuction for removing the object queue, should be
171  * called when all forms and application windows have
172  * been closed to get rid of allocated memory.
173  ******************************************************/
174 
175 void
fli_obj_queue_delete(void)176 fli_obj_queue_delete( void )
177 {
178     FLI_OBJECT_QUEUE_ENTRY *b;
179 
180     while ( ( b = obj_queue.blocks ) != NULL )
181     {
182         obj_queue.blocks = b->next;
183         fl_free( b );
184     }
185 
186     obj_queue.tail = obj_queue.head = obj_queue.empty = NULL;
187 }
188 
189 
190 /***********************************************************
191  * Function for appending a new element to the object queue
192  ***********************************************************/
193 
194 static void
add_to_obj_queue(FL_OBJECT * obj,int event)195 add_to_obj_queue( FL_OBJECT * obj,
196                   int         event )
197 {
198     if ( obj == NULL )
199         return;
200 
201     if ( obj_queue.empty == NULL )
202         extend_obj_queue( );
203 
204     if ( obj_queue.head )
205         obj_queue.head = obj_queue.head->next = obj_queue.empty;
206     else
207         obj_queue.tail = obj_queue.head = obj_queue.empty;
208 
209     obj_queue.empty = obj_queue.empty->next;
210 
211     obj_queue.head->next = NULL;
212     obj_queue.head->obj = obj;
213     obj_queue.head->event = event;
214     if ( obj != FL_EVENT )
215         obj_queue.head->ret = obj->returned;
216 }
217 
218 
219 /*****************************************************************
220  * Function for fetching the oldest element from the object queue
221  *****************************************************************/
222 
223 static FL_OBJECT *
get_from_obj_queue(int * event)224 get_from_obj_queue( int * event )
225 {
226     FLI_OBJECT_QUEUE_ENTRY *t = obj_queue.tail;
227 
228     if ( t == NULL )
229         return NULL;
230 
231     if ( t->next == NULL )
232         obj_queue.tail = obj_queue.head = NULL;
233     else
234         obj_queue.tail = t->next;
235 
236     t->next = obj_queue.empty;
237     obj_queue.empty = t;
238 
239     if ( t->obj != FL_EVENT )
240         t->obj->returned = t->ret;
241 
242     if ( event )
243         *event = t->event;
244 
245     return t->obj;
246 }
247 
248 
249 /*************************************************************************
250  * Function for removing all entries for a certain object from the queue.
251  * This routine is called as part of hiding and deletion of an object.
252  *************************************************************************/
253 
254 void
fli_object_qflush_object(FL_OBJECT * obj)255 fli_object_qflush_object( FL_OBJECT * obj )
256 {
257     FLI_OBJECT_QUEUE_ENTRY *c,
258                            *p;
259 
260     while ( obj_queue.tail && obj_queue.tail->obj == obj )
261         get_from_obj_queue( NULL );
262 
263     if ( ! obj_queue.tail )
264         return;
265 
266     p = obj_queue.tail;
267     c = p->next;
268 
269     while ( c )
270     {
271         if ( c->obj == obj )
272         {
273             p->next = c->next;
274             c->next = obj_queue.empty;
275             obj_queue.empty = c;
276         }
277         else
278             p = c;
279 
280         c = p->next;
281     }
282 }
283 
284 
285 /**********************************************************************
286  * Function for removing all entries for a certain form from the queue
287  * - here the object handler must be executed for FL_INPUT objects.
288  * This should be called as part of free_form process.
289  **********************************************************************/
290 
291 void
fli_object_qflush(FL_FORM * form)292 fli_object_qflush( FL_FORM * form )
293 {
294     FLI_OBJECT_QUEUE_ENTRY *c,
295                            *p;
296 
297     while (    obj_queue.tail
298             && obj_queue.tail->obj != FL_EVENT
299             && obj_queue.tail->obj->form == form )
300     {
301         if ( obj_queue.tail->obj->objclass == FL_INPUT )
302             handle_input_object( obj_queue.tail->obj,
303                                  obj_queue.tail->event );
304         get_from_obj_queue( NULL );
305     }
306 
307     if ( ! obj_queue.tail )
308         return;
309 
310     for ( p = obj_queue.tail, c = p->next; c != NULL; c = p->next )
311         if ( c->obj != FL_EVENT && c->obj->form == form )
312         {
313             if ( c->obj->objclass == FL_INPUT )
314                 handle_input_object( c->obj, c->event );
315 
316             p->next = c->next;
317             c->next = obj_queue.empty;
318             obj_queue.empty = c;
319         }
320         else
321             p = c;
322 }
323 
324 
325 /***************************************
326  * Adds an object to the queue
327  ***************************************/
328 
329 void
fli_object_qenter(FL_OBJECT * obj,int event)330 fli_object_qenter( FL_OBJECT * obj,
331                    int         event )
332 {
333     if ( ! obj )
334     {
335         M_err( "fli_object_qenter", "NULL object" );
336         return;
337     }
338 
339 #ifndef DELAYED_ACTION
340     if (    obj != FL_EVENT
341          && ( ! obj->form || ! obj->visible || obj->active <= 0 ) )
342     {
343 #if FL_DEBUG >= ML_DEBUG
344         M_err( "fli_object_qenter", "Bad object" );
345 #endif
346         return;
347     }
348 
349     /* Please note: if 'DELAYED_ACTION' should ever be switched on don't
350        forget to deal correctly with also handling callbacks of parent
351        objects (if the object entered is a child object) */
352 
353     if ( obj != FL_EVENT )
354     {
355         if ( obj->object_callback )
356         {
357             XFlush( flx->display );
358             fli_context->last_event = event;
359             obj->object_callback( obj, obj->argument );
360             fli_context->last_event = FL_NOEVENT;
361             return;
362         }
363         else if ( obj->form->form_callback )
364         {
365             XFlush( flx->display );
366             fli_context->last_event = event;
367             obj->form->form_callback( obj, obj->form->form_cb_data );
368             fli_context->last_event = FL_NOEVENT;
369             return;
370         }
371     }
372 #endif /* ! DELAYED_ACTION */
373 
374     add_to_obj_queue( obj, event );
375 }
376 
377 
378 /***************************************
379  * Returns a pointer to the oldest element in the object queue
380  ***************************************/
381 
382 FL_OBJECT *
fli_object_qtest(void)383 fli_object_qtest( void )
384 {
385     return obj_queue.tail ? obj_queue.tail->obj : NULL;
386 }
387 
388 
389 /***************************************
390  * Filter out result bits that don't fit what the object is set up to
391  * return, and make sure the FL_RETURN_END_CHANGED bit is set correctly,
392  * which requires that both FL_RETURN_CHANGED and FL_RETURN_END are set
393  ***************************************/
394 
395 void
fli_filter_returns(FL_OBJECT * obj)396 fli_filter_returns( FL_OBJECT * obj )
397 {
398     if (    obj->how_return & FL_RETURN_END_CHANGED
399          && obj->returned & FL_RETURN_CHANGED
400          && obj->returned & FL_RETURN_END )
401     {
402         obj->returned |= FL_RETURN_END_CHANGED;
403         obj->returned &= ~ ( FL_RETURN_CHANGED | FL_RETURN_END );
404     }
405 
406     if ( obj->how_return != FL_RETURN_NONE )
407         obj->returned &= obj->how_return | FL_RETURN_TRIGGERED;
408     else
409         obj->returned = FL_RETURN_NONE;
410 }
411 
412 
413 /***************************************
414  * Reads an object from the queue, calls callbacks for the object (if
415  * they exist) or passes it on to the user via fl_do_forms() etc.
416  ***************************************/
417 
418 FL_OBJECT *
fli_object_qread(void)419 fli_object_qread( void )
420 {
421     int event = -1;
422     FL_OBJECT *obj = get_from_obj_queue( &event );
423 
424     if ( obj == FL_EVENT )
425         return obj;
426 
427     if ( ! obj || ! obj->form )
428         return NULL;
429 
430     /* If the object has a callback execute it and return NULL unless the
431        object is a child object (in that case we're supposed to also check
432        for callbacks for the parent etc.). It's also important to make
433        sure the object didn't get deleted within its callback - if that's
434        the case it would be catastrophic to check for the parent... */
435 
436     if ( obj->object_callback )
437     {
438         fli_handled_obj = obj;
439 
440         fli_context->last_event = event;
441         obj->object_callback( obj, obj->argument );
442         fli_context->last_event = FL_NOEVENT;
443 
444         if ( fli_handled_obj )
445             obj->returned = FL_RETURN_NONE;
446 
447         if ( ! fli_handled_obj || ! obj->parent )
448             return NULL;
449     }
450 
451     /* If the object is a child object check if there is a callback for
452        the parent and execute that (and return NULL in that case). In
453        between also check if there are further events for other childs
454        of the same parent in the queue and also execute their callbacks.
455        And keep in mind that execution of one of these callbacks may
456        delete the object (and even its parent...) */
457 
458     if ( obj->parent )
459     {
460         obj = obj->parent;
461         fli_filter_returns( obj );
462 
463         while ( obj->parent )
464         {
465             if ( ! obj->returned )
466                 return NULL;
467 
468             if ( obj->object_callback )
469             {
470                 fli_handled_obj = obj;
471                 fli_context->last_event = event;
472                 obj->object_callback( obj, obj->argument );
473                 fli_context->last_event = FL_NOEVENT;
474                 if ( fli_handled_obj )
475                     obj->returned = FL_RETURN_NONE;
476                 else
477                     return NULL;
478             }
479 
480             obj = obj->parent;
481             fli_filter_returns( obj );
482         }
483 
484         fli_handled_parent = obj;
485 
486         while ( fli_handled_parent )
487         {
488             FL_OBJECT *n,
489                       *p;
490 
491             if (    ! ( n = fli_object_qtest( ) )
492                  || n == FL_EVENT
493                  || ! n->parent )
494                 break;
495 
496             p = n->parent;
497             while ( p->parent )
498                 p = p->parent;
499 
500             if ( p != obj )
501                 break;
502 
503             n = get_from_obj_queue( &event );
504             do
505             {
506                 fli_filter_returns( n );
507                 if ( ! n->returned )
508                     break;
509 
510                 if ( n->object_callback )
511                 {
512                     fli_handled_obj = n;
513                     fli_context->last_event = event;
514                     n->object_callback( n, n->argument );
515                     fli_context->last_event = FL_NOEVENT;
516                     if ( fli_handled_obj )
517                         n->returned = FL_RETURN_NONE;
518                     else
519                         break;
520                 }
521             } while ( fli_handled_parent && ( n = n->parent ) != obj );
522 
523             fli_filter_returns( obj );
524         }
525 
526         if ( ! fli_handled_parent )
527             return NULL;
528     }
529 
530     /* If we arrive here the original object either was a child object
531        or it had no callback. Run either the parents callback or the forms
532        callback (if there's one). */
533 
534     if ( ! obj->returned )
535         return NULL;
536     else if ( obj->object_callback  )
537     {
538         fli_handled_obj = obj;
539         fli_context->last_event = event;
540         obj->object_callback( obj, obj->argument );
541         fli_context->last_event = FL_NOEVENT;
542         if ( fli_handled_obj )
543             obj->returned = FL_RETURN_NONE;
544         return NULL;
545     }
546     else if ( obj->form->form_callback )
547     {
548         fli_handled_obj = obj;
549         fli_context->last_event = event;
550         obj->form->form_callback( obj, obj->form->form_cb_data );
551         fli_context->last_event = FL_NOEVENT;
552         if ( fli_handled_obj )
553             obj->returned = FL_RETURN_NONE;
554         return NULL;
555     }
556 
557     if ( obj->child && obj->returned == FL_RETURN_NONE)
558         return NULL;
559 
560     return obj;
561 }
562 
563 
564 /***************************************
565  * This is mainly used to handle the input correctly when a form
566  * is being hidden
567  ***************************************/
568 
569 static void
handle_input_object(FL_OBJECT * obj,int event)570 handle_input_object( FL_OBJECT * obj,
571                      int         event )
572 {
573     if ( obj != FL_EVENT || ! obj->form )
574         return;
575 
576     fli_context->last_event = event;
577     if ( obj->object_callback )
578         obj->object_callback( obj, obj->argument );
579     else if ( obj->form->form_callback )
580         obj->form->form_callback( obj, obj->form->form_cb_data );
581     fli_context->last_event = FL_NOEVENT;
582 }
583 
584 
585 /***************** End of object queue handling *****************/
586 
587 
588 
589 /**************** Normal Events ********************/
590 
591 
592 typedef struct FL_EVENT_QUEUE_ENTRY_ {
593     XEvent                         xev;
594     struct FL_EVENT_QUEUE_ENTRY_ * next;
595 } FL_EVENT_QUEUE_ENTRY;
596 
597 
598 typedef struct FL_EVENT_QUEUE_ {
599     FL_EVENT_QUEUE_ENTRY * head;       /* here events get added to */
600     FL_EVENT_QUEUE_ENTRY * tail;       /* and here they get removed from */
601     FL_EVENT_QUEUE_ENTRY * empty;      /* linked list of empty entries */
602     FL_EVENT_QUEUE_ENTRY * blocks;     /* pointer to linked list of blocks */
603     unsigned long          count;
604 } FL_EVENT_QUEUE;
605 
606 static FL_EVENT_QUEUE event_queue = { NULL, NULL, NULL, NULL, 0 };
607 
608 
609 /***************************************************
610  * Function for creating/extending the event queue
611  * (gets called automatically the first time an
612  * event gets pushed onto the queue, so no initia-
613  * lization, e.g. from fl_initialize(), is needed)
614  ***************************************************/
615 
616 static void
extend_event_queue(void)617 extend_event_queue( void )
618 {
619     FL_EVENT_QUEUE_ENTRY *p = fl_malloc( ( FLI_QSIZE + 1 ) * sizeof *p );
620     size_t i;
621 
622     /* The first element of the area gets used for book-keeping purposes */
623 
624     p->next = event_queue.blocks;
625     event_queue.blocks = p++;
626 
627     /* The rest gets added to (or makes up) the empty list */
628 
629     event_queue.empty = p;
630 
631     for ( i = 0; i < FLI_QSIZE - 1; p++, i++ )
632         p->next = p + 1;
633 
634     p->next = NULL;
635 }
636 
637 
638 /******************************************************
639  * Fuction for removing the event queue, should be
640  * called when all forms and application windows have
641  * been closed to get rid of allocated memory.
642  ******************************************************/
643 
644 void
fli_event_queue_delete(void)645 fli_event_queue_delete( void )
646 {
647     FL_EVENT_QUEUE_ENTRY *b;
648 
649     while ( ( b = event_queue.blocks ) != NULL )
650     {
651         event_queue.blocks = b->next;
652         fl_free( b );
653     }
654 
655     event_queue.tail = event_queue.head = event_queue.empty = NULL;
656 }
657 
658 
659 /***********************************************************
660  * Function for appending a new element to the event queue
661  ***********************************************************/
662 
663 static void
add_to_event_queue(XEvent * xev)664 add_to_event_queue( XEvent * xev )
665 {
666     if ( event_queue.empty == NULL )
667         extend_event_queue( );
668 
669     if ( event_queue.head )
670         event_queue.head = event_queue.head->next = event_queue.empty;
671     else
672         event_queue.tail = event_queue.head = event_queue.empty;
673 
674     event_queue.empty = event_queue.empty->next;
675 
676     event_queue.head->next = NULL;
677     event_queue.head->xev = *xev;
678     event_queue.count++;
679 }
680 
681 
682 /****************************************************************
683  * Function for removing the oldest element form the event queue
684  ****************************************************************/
685 
686 static XEvent
get_from_event_queue(void)687 get_from_event_queue( void )
688 {
689     FL_EVENT_QUEUE_ENTRY *t = event_queue.tail;
690 
691     if ( t->next == NULL )
692         event_queue.tail = event_queue.head = NULL;
693     else
694         event_queue.tail = t->next;
695 
696     t->next = event_queue.empty;
697     event_queue.empty = t;
698 
699     return t->xev;
700 }
701 
702 
703 /***************************************
704  * Replacement for the Xlib XPutBackEvent() function:
705  * allows to push back an event onto the queue
706  ***************************************/
707 
708 void
fl_XPutBackEvent(XEvent * xev)709 fl_XPutBackEvent( XEvent * xev )
710 {
711     static int mm;
712 
713     if ( xev->type != ClientMessage && fli_handle_event_callbacks( xev ) )
714         return;
715 
716     /* These must have come from simulating double buffering, throw them away */
717 
718     if ( xev->type == NoExpose )
719     {
720         if ( ++mm % 20 == 0 )
721         {
722             M_warn( "fl_XPutbackEvent", "20 NoExpose discarded" );
723             mm = 0;
724         }
725 
726         return;
727     }
728 
729     fli_xevent_name( "fl_XPutBackEvent", xev );
730     add_to_event_queue( xev );
731 }
732 
733 
734 /***************************************
735  * Replacement for the Xlib XEventsQueued() function: returns
736  * if there are any events in the event queue.
737  ***************************************/
738 
739 int
fl_XEventsQueued(int mode FL_UNUSED_ARG)740 fl_XEventsQueued( int mode  FL_UNUSED_ARG )
741 {
742     if ( event_queue.tail == NULL )
743     {
744         if ( fl_display == None )
745             return 0;
746 
747         fli_treat_interaction_events( 0 );
748         fli_treat_user_events( );
749     }
750 
751     return event_queue.tail != NULL;
752 }
753 
754 
755 /***************************************
756  * Replacement for the Xlib XNextEvent() function: copies the oldest
757  * event into the XEvent structure and removes it from the queue. If
758  * the queue is empty it blocks until an event has been received.
759  ***************************************/
760 
761 int
fl_XNextEvent(XEvent * xev)762 fl_XNextEvent( XEvent * xev )
763 {
764     if ( fl_display == None )
765         return 0;
766 
767     while ( event_queue.tail == NULL )
768     {
769         if ( fl_display == None )
770             return 0;
771 
772         fli_treat_interaction_events( 1 );
773         fli_treat_user_events( );
774     }
775 
776     *xev = get_from_event_queue( );
777     return 1;
778 }
779 
780 
781 /***************************************
782  * Replacement for the Xlib XPeekEvent() function: returns a copy
783  * of the first event avaialable but does not remove it. Blocks
784  * if there is no event until a new one has arrived.
785  ***************************************/
786 
787 int
fl_XPeekEvent(XEvent * xev)788 fl_XPeekEvent( XEvent * xev )
789 {
790     if ( fl_display == None )
791         return 0;
792 
793     while ( event_queue.tail == NULL )
794     {
795         if ( fl_display == None )
796             return 0;
797 
798         fli_treat_interaction_events( 1 );
799         fli_treat_user_events( );
800     }
801 
802     *xev = event_queue.tail->xev;
803     return 1;
804 }
805 
806 
807 /***************************************
808  * Get all user events and treat them: either "consume" them by
809  * calling the callback routine or put them onto the internal
810  * object queue for later retrival
811  ***************************************/
812 
813 void
fli_treat_user_events(void)814 fli_treat_user_events( void )
815 {
816     XEvent xev;
817 
818     while ( fl_display != None && event_queue.count )
819     {
820         if ( fli_event_callback )
821         {
822             fl_XNextEvent( &xev );
823             fli_event_callback( &xev, fli_user_data );
824         }
825         else
826             fli_object_qenter( FL_EVENT, FL_NOEVENT );
827 
828         event_queue.count--;
829     }
830 }
831 
832 
833 /******************** DEBUG use only *****************/
834 
835 
836 #define NV( a ) { #a, a }
837 
838 typedef struct
839 {
840     const char * name;
841     int          type;
842 } ev_name;
843 
844 
845 static ev_name evname[ ] =
846 {
847     NV( 0 ),
848     NV( 1 ),
849     NV( KeyPress ),
850     NV( KeyRelease ),
851     NV( ButtonPress ),
852     NV( ButtonRelease ),
853     NV( MotionNotify ),
854     NV( EnterNotify ),
855     NV( LeaveNotify ),
856     NV( FocusIn ),
857     NV( FocusOut ),
858     NV( KeymapNotify ),
859     NV( Expose ),
860     NV( GraphicsExpose ),
861     NV( NoExpose ),
862     NV( VisibilityNotify ),
863     NV( CreateNotify ),
864     NV( DestroyNotify ),
865     NV( UnmapNotify ),
866     NV( MapNotify ),
867     NV( MapRequest ),
868     NV( ReparentNotify ),
869     NV( ConfigureNotify ),
870     NV( ConfigureRequest ),
871     NV( GravityNotify ),
872     NV( ResizeRequest ),
873     NV( CirculateNotify ),
874     NV( CirculateRequest ),
875     NV( PropertyNotify ),
876     NV( SelectionClear ),
877     NV( SelectionRequest ),
878     NV( SelectionNotify ),
879     NV( ColormapNotify ),
880     NV( ClientMessage ),
881     NV( MappingNotify )
882 };
883 
884 
885 /***************************************
886  ***************************************/
887 
888 const char *
fli_get_xevent_name(const XEvent * xev)889 fli_get_xevent_name( const XEvent *xev )
890 {
891       size_t i;
892       static char buf[ 128 ];
893 
894       for ( i = KeyPress; i < LASTEvent; i++ )
895       {
896           if ( evname[ i ].type == xev->type )
897           {
898               fli_snprintf( buf, sizeof buf, "%s(0x%x)",
899                             evname[ i ].name, xev->type );
900               return buf;
901           }
902       }
903 
904       return "unknown event";
905 }
906 
907 
908 /***************************************
909  ***************************************/
910 
911 XEvent *
fl_print_xevent_name(const char * where,const XEvent * xev)912 fl_print_xevent_name( const char *   where,
913                       const XEvent * xev )
914 {
915     size_t i,
916            known;
917     Window win = ( ( XAnyEvent * ) xev )->window;
918 
919     for ( i = KeyPress, known = 0; ! known && i < LASTEvent; i++ )
920         if ( evname[ i ].type == xev->type )
921         {
922             fprintf( stderr, "%s Event (%d, win = %ld serial = %ld) %s ",
923                      where ? where : "",
924                      xev->type, win, ( ( XAnyEvent * ) xev)->serial,
925                      evname[ i ].name );
926 
927             if ( xev->type == Expose )
928                 fprintf( stderr, "count = %d serial = %ld\n",
929                          xev->xexpose.count, xev->xexpose.serial );
930             else if ( xev->type == LeaveNotify || xev->type == EnterNotify )
931                 fprintf(stderr, "Mode %s\n", xev->xcrossing.mode == NotifyGrab ?
932                         "Grab" :
933                         ( xev->xcrossing.mode == NotifyNormal ?
934                           "Normal" : "UnGrab" ) );
935             else if ( xev->type == MotionNotify )
936                 fprintf(stderr, "Mode %s\n",
937                         xev->xmotion.is_hint ? "Hint" : "Normal" );
938             else if ( xev->type == ConfigureNotify )
939                 fprintf( stderr, "(x = %d y = %d w = %d h = %d) %s\n",
940                          xev->xconfigure.x, xev->xconfigure.y,
941                          xev->xconfigure.width, xev->xconfigure.height,
942                          xev->xconfigure.send_event ? "Syn" : "Non-Syn" );
943             else if ( xev->type == ButtonPress )
944                 fprintf( stderr, "button: %d\n", xev->xbutton.button );
945             else if ( xev->type == ButtonRelease )
946                 fprintf( stderr, "button: %d\n", xev->xbutton.button );
947             else
948                 fputc( '\n', stderr );
949             known = 1;
950         }
951 
952     if ( ! known )
953         fprintf( stderr, "Unknown event %d, win = %ld\n", xev->type, win );
954 
955     return ( XEvent * ) xev;
956 }
957 
958 
959 /***************************************
960  ***************************************/
961 
962 XEvent *
fli_xevent_name(const char * where,const XEvent * xev)963 fli_xevent_name( const char *   where,
964                 const XEvent * xev )
965 {
966 
967     if ( fli_cntl.debug >= 2 )
968         fl_print_xevent_name( where, xev );
969 
970     return ( XEvent * ) xev;
971 }
972 
973 
974 /***************************************
975  ***************************************/
976 
977 static int
badwin_handler(Display * dpy FL_UNUSED_ARG,XErrorEvent * xev)978 badwin_handler( Display *     dpy  FL_UNUSED_ARG,
979                 XErrorEvent * xev )
980 {
981     if ( xev->type != BadWindow && xev->type != BadDrawable )
982         M_err( "badwin_handler",
983                "X error happened when expecting only BadWindow/Drawable\n" );
984     return 0;
985 }
986 
987 
988 /***********************************************************************
989  * Received an Expose event ev, see if next event is the same as the
990  * the current one, drop it if it is, but we need consolidate all the
991  * dirty rectangles into one.
992  *
993  * Must not block.
994  ************************************************************************/
995 
996 static void
compress_redraw(XEvent * ev)997 compress_redraw( XEvent * ev )
998 {
999     XEvent expose_ev;
1000     Window win = ev->xexpose.window;
1001     Region reg = XCreateRegion( );
1002     XRectangle rec;
1003 
1004     /* Original comment: this is theoretically not correct as we can't peek
1005        ahead and ignore the events in between, but it works in XForms as we
1006        always update the form size and position when dealing with Expose event.
1007 
1008        This has been changed a bit since 1.0.90: There was a problem with
1009        e.g. KDE or Gnome when they were set up to redraw also during resizing
1010        and the mouse was moved around rather fast. We collect now not only
1011        Expose events, compressing them to a single one, covering the combined
1012        area of all of them, but also ConfigureNotify events. If there was one
1013        or more ConfigureNotify events we put back the "consolidated" Expose
1014        event onto the event queue and return the last ConfigureNotify event
1015        instead of the original Expose event we got started with. This hope-
1016        fully is not only a solution that covers all cases but also keeps
1017        the numbers of redraws to a minimum. The only drawback is that in the
1018        do_interaction_step() function, handling the Expose event, one has to
1019        check if the area specified by the event isn't larger than the (new)
1020        size of the window and prune it if necessary.                  JTT */
1021 
1022     /* Collect all Expose events, combining their areas */
1023 
1024     do {
1025         rec.x      = ev->xexpose.x;
1026         rec.y      = ev->xexpose.y;
1027         rec.width  = ev->xexpose.width;
1028         rec.height = ev->xexpose.height;
1029 
1030         XUnionRectWithRegion( &rec, reg, reg );
1031 
1032     } while ( XCheckTypedWindowEvent( flx->display, win, Expose, ev ) );
1033 
1034     /* Set the area of the last events to that of the "consolidated" event
1035        and make a backup copy */
1036 
1037     XClipBox( reg, &rec );
1038 
1039     ev->xexpose.x = rec.x;
1040     ev->xexpose.y = rec.y;
1041     ev->xexpose.width = rec.width;
1042     ev->xexpose.height = rec.height;
1043 
1044     expose_ev = *ev;
1045 
1046     XDestroyRegion( reg );
1047 
1048     /* Now get all ConfigureNotify events */
1049 
1050     while ( XCheckTypedWindowEvent( flx->display, win, ConfigureNotify, ev ) )
1051         /*empty */ ;
1052 
1053     /* If there was at least one ConfigureNotify event put the "consolidated"
1054        Expose event back onto the event queue and return the last
1055        ConfigureNotify event we got, otherwise the Expose event itself.
1056 
1057        Since e.g. KDE and Gnome can send the ConfigureNotify event artificially
1058        to achieve an update of the display while resizing is still going on,
1059        the 'send_event' member of the XEvent structure might be set. On the
1060        other hand, in do_interaction_step(), where the events are handled,
1061        this member is checked for to get around a bug in mwm. So we got to
1062        reset it here to avoid the event getting flagged as spurious. This
1063        hopefully won't interfere with the mwm bug detection since it's for
1064        cases were a ConfigureNotify gets send, but no corresponding Expose
1065        events, and in this case we wouldn't have ended up here... */
1066 
1067     if ( ev->type == ConfigureNotify )
1068     {
1069         XPutBackEvent( flx->display, &expose_ev );
1070         ev->xconfigure.send_event = 0;
1071     }
1072 }
1073 
1074 
1075 /***************************************
1076  ***************************************/
1077 
1078 static void
compress_motion(XEvent * xme)1079 compress_motion( XEvent * xme )
1080 {
1081     Window win = xme->xmotion.window;
1082     unsigned long evm = PointerMotionMask | ButtonMotionMask;
1083 
1084     if ( xme->type != MotionNotify )
1085         return;
1086 
1087     do
1088     {
1089 #if FL_DEBUG >= ML_DEBUG
1090         M_info2( "compress_motion", "win = %ld (%d, %d) %s",
1091                  xme->xany.window, xme->xmotion.x, xme->xmotion.y,
1092                  xme->xmotion.is_hint ? "hint" : "" )
1093 #endif
1094             /* empty */ ;
1095     } while ( XCheckWindowEvent( flx->display, win, evm, xme ) );
1096 
1097     if ( xme->xmotion.is_hint )
1098     {
1099         int ( *old )( Display *, XErrorEvent * );
1100 
1101         /* We must protect against BadWindow here, because we have only
1102            looked for Motion events, and there could be a Destroy event
1103            which makes the XQueryPointer fail as the window is deleted. */
1104 
1105         old = XSetErrorHandler( badwin_handler );
1106         fl_get_win_mouse( xme->xmotion.window,
1107                           &xme->xmotion.x, &xme->xmotion.y,
1108                           &xme->xmotion.state );
1109         XSetErrorHandler( old );
1110         xme->xmotion.is_hint = 0;
1111     }
1112 }
1113 
1114 
1115 /***************************************
1116  ***************************************/
1117 
1118 void
fli_compress_event(XEvent * xev,unsigned long mask)1119 fli_compress_event( XEvent *      xev,
1120                     unsigned long mask )
1121 {
1122     if ( xev->type == Expose && mask & ExposureMask )
1123         compress_redraw( xev );
1124     else if (    xev->type == MotionNotify
1125               && mask & ( PointerMotionMask | ButtonMotionMask ) )
1126         compress_motion( xev );
1127 }
1128 
1129 
1130 /***************************************
1131  ***************************************/
1132 
1133 int
fl_keysym_pressed(KeySym k)1134 fl_keysym_pressed( KeySym k )
1135 {
1136     char kvec[ 32 ];
1137     KeyCode code;
1138 
1139     if ( ( code = XKeysymToKeycode( flx->display, k ) ) == NoSymbol )
1140     {
1141         M_warn( "fl_keysym_pressed", "Bad KeySym %d", ( int ) k );
1142         return 0;
1143     }
1144 
1145     XQueryKeymap( flx->display, kvec );
1146     return 1 & ( kvec[ code / 8 ] >> ( code & 7 ) );
1147 }
1148 
1149 
1150 /***************************************
1151  * Add an event
1152  ***************************************/
1153 
1154 long
fl_addto_selected_xevent(Window win,long mask)1155 fl_addto_selected_xevent( Window win,
1156                           long   mask )
1157 {
1158     XWindowAttributes xwa;
1159 
1160     XGetWindowAttributes( flx->display, win, &xwa );
1161     xwa.your_event_mask |= mask;
1162 
1163     /* On some SGI machines, 'your_event_mask' has bogus value 0x80??????,
1164        causing an X protocol error. Fix this here */
1165 
1166     xwa.your_event_mask &= AllEventsMask;
1167     XSelectInput( flx->display, win, xwa.your_event_mask );
1168 
1169     return xwa.your_event_mask;
1170 }
1171 
1172 
1173 /***************************************
1174  ***************************************/
1175 
1176 long
fl_remove_selected_xevent(Window win,long mask)1177 fl_remove_selected_xevent( Window win,
1178                            long   mask )
1179 {
1180     XWindowAttributes xwa;
1181 
1182     XGetWindowAttributes( flx->display, win, &xwa );
1183     xwa.your_event_mask &= ~mask;
1184 
1185     /* On some SGI machines 'your_event_mask' has bogus value of 0x80??????,
1186        causing an X protocol error. Fix this here */
1187 
1188     xwa.your_event_mask &= AllEventsMask;
1189     XSelectInput( flx->display, win, xwa.your_event_mask );
1190 
1191     return xwa.your_event_mask;
1192 }
1193 
1194 
1195 /*
1196  * Local variables:
1197  * tab-width: 4
1198  * indent-tabs-mode: nil
1199  * End:
1200  */
1201