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