1 /* coolnext.c - CNextEvent function and support, and various event
2 		handlers and utils
3    Copyright (C) 1996-2017 Paul Sheer
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307, USA.
19  */
20 
21 /* #define DEBUG */
22 /* #define DEBUG_ENTRY */
23 
24 #include <config.h>
25 #include <stdio.h>
26 #include <my_string.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 
30 #include <X11/Intrinsic.h>
31 #include "lkeysym.h"
32 
33 #include "stringtools.h"
34 #include "app_glob.c"
35 
36 #include "coolwidget.h"
37 #include "coollocal.h"
38 
39 #include "drawings.h"
40 #include "edit.h"
41 #include "editcmddef.h"
42 #include "widget3d.h"
43 
44 #include "mad.h"
45 
46 
47 
48 static int last_region = 0;
49 
50 int run_callbacks (CWidget * w, XEvent * xevent, CEvent * cwevent);
51 
52 
53 int (*global_alarm_callback[33]) (CWidget *, XEvent *, CEvent *) = {
54     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
55 };
56 extern Pixmap Cswitchon, Cswitchoff;
57 #ifdef HAVE_DND
58 extern Atom DndProtocol, OldDndProtocol;
59 #endif
60 
61 void add_to_focus_stack (Window w);
62 void selection_clear (void); /* from editwidget.c */
63 
64 int option_mouse_double_click = 300;
65 int option_middle_button_pastes = 1;
66 
67 /* converts button presses from buttons 2 through 5 to button presses from 2 only, also gets double clicks */
resolve_button(XEvent * xevent,CEvent * cwevent)68 void resolve_button (XEvent * xevent, CEvent * cwevent)
69 {
70     static Time thyme_press = 0, thyme_release = 0;
71     static Window window = 0;
72     static int x, y;
73     cwevent->state = xevent->xbutton.state;
74     if (cwevent->state & (Button2Mask | Button3Mask /*| Button4Mask | Button5Mask */ ))
75 	cwevent->state |= Button2Mask;
76     switch (xevent->type) {
77     case ButtonRepeat:
78     case ButtonRelease:
79     case ButtonPress:
80 	cwevent->button = xevent->xbutton.button;
81 	if (cwevent->button == Button4 || cwevent->button == Button5) {
82 	    /* ahaack: wheel mouse mapped as button 4 and 5 */
83 	    return;
84 	}
85 	if (cwevent->button == Button2 || cwevent->button == Button3
86 	    || cwevent->button == Button4 || cwevent->button == Button5) {
87 	    cwevent->button = Button2;
88 	}
89 	cwevent->x = xevent->xbutton.x;
90 	cwevent->y = xevent->xbutton.y;
91 	if (xevent->type != ButtonRepeat) {
92 	    if (window == xevent->xany.window)
93 		if (abs (x - cwevent->x) < 4 && abs (y - cwevent->y) < 4) {
94 		    if (abs (xevent->xmotion.time - thyme_press) < option_mouse_double_click &&
95 			xevent->type == ButtonPress)
96 			thyme_press = cwevent->double_click = 1;
97 		    if (abs (xevent->xmotion.time - thyme_release) < option_mouse_double_click &&
98 			xevent->type == ButtonRelease)
99 			thyme_release = cwevent->double_click = 1;
100 		}
101 	    if (xevent->type == ButtonPress)
102 		thyme_press = xevent->xbutton.time;
103 	    else
104 		thyme_release = xevent->xbutton.time;
105 	}
106 	x = xevent->xbutton.x;
107 	y = xevent->xbutton.y;
108 	break;
109     case MotionNotify:
110 	x = cwevent->x = xevent->xmotion.x;
111 	y = cwevent->y = xevent->xmotion.y;
112 	break;
113     }
114     window = xevent->xany.window;
115 }
116 
lower_window_callback(CWidget * w)117 static long lower_window_callback (CWidget * w)
118 {
119     if (w->position & WINDOW_ALWAYS_LOWERED)
120 	XLowerWindow (CDisplay, w->winid);
121     return 0;
122 }
123 
124 /*sends all windows that are marked top_bottom = 1 to the bottom */
CLowerWindows(void)125 void CLowerWindows (void)
126 {
127     for_all_widgets ((for_all_widgets_cb_t) lower_window_callback, 0, 0);
128 }
129 
raise_window_callback(CWidget * w)130 static long raise_window_callback (CWidget * w)
131 {
132     if (w->position & WINDOW_ALWAYS_RAISED)
133 	XRaiseWindow (CDisplay, w->winid);
134     return 0;
135 }
136 
137 /*sends all windows that are marked top_bottom = 2 to the top */
CRaiseWindows(void)138 void CRaiseWindows (void)
139 {
140     for_all_widgets ((for_all_widgets_cb_t) raise_window_callback, 0, 0);
141 }
142 
143 
144 
145 /* {{{ here is an internal event queue handler to send events without going through XLib */
146 
147 /*
148    We want to be able to send our own events internally
149    because XSendEvent sometimes doesn't force the an
150    event to be processed and the event sits on the queue
151    with its thumb up its arse.
152  */
153 
154 #define NUM_EVENTS_CACHED 256
155 
156 static unsigned char event_send_last = 0;
157 static unsigned char event_read_last = 0;
158 static XEvent event_sent[NUM_EVENTS_CACHED];
159 
160 /* returns 0, if buffer is full, else returns 1 */
push_event(XEvent * ev)161 int push_event (XEvent * ev)
162 {
163     unsigned char j = event_send_last + 1;
164     if (event_read_last == j) {		/* no more space */
165 #ifdef DEBUG
166 /* NLS ? */
167 	fprintf (stderr, "%s:%s:%d: *Warning* event stack full\n", PACKAGE, __FILE__, __LINE__);
168 #endif
169 	/* we are just going to ignore this */
170 	return 0;
171     }
172     if (ev->type == Expose || ev->type == InternalExpose) {	/* must handle expose counts also */
173 	unsigned char i = event_send_last - 1;
174 	XEvent *e;
175 	j = event_read_last - 1;
176 	ev->xexpose.count = 0;	/* this is the very last expose by definition */
177 	while (i != j) {	/* search backwards until a similar event is found */
178 	    if ((e = &(event_sent[i]))->xany.window == ev->xany.window) {
179 		if (e->type == ev->type) {
180 		    e->xexpose.count = 1;	/* we are not going to actually "count", but we must indicate if the queue isn't empty with a "1" */
181 		    break;
182 		}
183 	    }
184 	    i--;
185 	}
186     }
187     memcpy (&event_sent[event_send_last], ev, sizeof (XEvent));
188     event_send_last++;
189     return 1;
190 }
191 
192 extern int block_push_event;	/* see initapp.c */
193 
194 /* pops the oldest event, returns 0 if empty */
pop_event(XEvent * ev)195 int pop_event (XEvent * ev)
196 {
197     if (event_read_last == event_send_last)
198 	return 0;		/* "stack" is empty */
199     block_push_event = 1;
200     memcpy (ev, &event_sent[event_read_last], sizeof (XEvent));
201     memset (&event_sent[event_read_last], 0, sizeof (XEvent));
202     event_read_last++;
203     block_push_event = 0;
204     return 1;
205 }
206 
207 /* use this instead of XSextEvent to send an event to your own application */
CSendEvent(XEvent * e)208 int CSendEvent (XEvent * e)
209 {
210     int r = 0;
211     block_push_event = 1;
212     r = push_event (e);
213     block_push_event = 0;
214     return r;
215 }
216 
217 /* sends an event (type = msg) to the given widget */
CSendMessage(CWidget * w,int msg)218 int CSendMessage (CWidget * w, int msg)
219 {
220     CEvent cwevent;
221     XEvent xevent;
222 
223     if (!w)
224 	return 0;
225     memset (&cwevent, 0, sizeof (CEvent));
226     memset (&xevent, 0, sizeof (XEvent));
227 
228     xevent.type = cwevent.type = msg;
229     cwevent.kind = w->kind;
230     xevent.xany.window = cwevent.window = w->winid;
231 
232     cwevent.ident = "";
233     return run_callbacks (w, &xevent, &cwevent);
234 }
235 
CQueueSize()236 int CQueueSize ()
237 {
238     int r = event_read_last;
239     int s = event_send_last;
240     s -= r;
241     if (s < 0)
242 	return NUM_EVENTS_CACHED + s;
243     return s;
244 }
245 
246 /* returns nonzero if pending on either internal or X queue */
CPending()247 int CPending ()
248 {
249     if (CQueueSize ())
250 	return 1;
251     if (XEventsQueued (CDisplay, QueuedAfterFlush))
252 	return 1;
253     return 0;
254 }
255 
256 /*
257    does checks for expose events pending, if there is one on either queue then
258    it removes it and returns it.
259  */
CExposePending(Window w,XEvent * ev)260 int CExposePending (Window w, XEvent * ev)
261 {
262     XEvent *e;
263     int r;
264     unsigned char i = event_read_last;
265 
266     while (i != event_send_last) {
267 	if ((e = &(event_sent[i]))->xany.window == w)
268 	    if (e->type == Expose) {
269 		memcpy (ev, e, sizeof (XEvent));
270 		e->type = 0;
271 		return 1;
272 	    }
273 	i++;
274     }
275     memset(ev, '\0', sizeof(*ev));
276     r = XCheckWindowEvent (CDisplay, w, ExposureMask, ev);
277     return r && ev->type == Expose;
278 }
279 
280 /*
281    Searches the local queue for an event matching the window
282    and mask. Does NOT remove the event, returns non-zero if found.
283    a mask of 0 matches any type, only the masks below are supported:
284    add more masks as needed. A window of 0 matches any window.
285    event_return may be passed as 0 if unneeded.
286  */
CCheckWindowEvent(Window w,long event_mask,XEvent ** event_return)287 int CCheckWindowEvent (Window w, long event_mask, XEvent ** event_return)
288 {
289     unsigned char i = event_send_last - 1;
290     unsigned char j = event_read_last - 1;
291     static XEvent e;
292     static long mask[CLASTEvent] =
293     {99};
294 
295     memset (&e, 0, sizeof (e));
296 
297     if (!event_mask)
298 	event_mask = 0xFFFF;
299     if (mask[0] == 99) {
300 	memset (mask, 0, CLASTEvent * sizeof (long));
301 	mask[KeyPress] = KeyPressMask;
302 	mask[KeyRelease] = KeyReleaseMask;
303 	mask[ButtonPress] = ButtonPressMask;
304 	mask[ButtonRelease] = ButtonReleaseMask;
305 	mask[MotionNotify] = PointerMotionMask | ButtonMotionMask;
306 	mask[EnterNotify] = EnterWindowMask;
307 	mask[LeaveNotify] = LeaveWindowMask;
308 	mask[Expose] = ExposureMask;
309 	mask[ButtonRepeat] = ButtonReleaseMask | ButtonPressMask;
310     }
311     while (i != j) {
312 	if ((event_sent[i].xany.window == w || !w)
313 	    && (mask[event_sent[i].type] & event_mask)) {
314 	    if (event_return)
315 		*event_return = &(event_sent[i]);
316 	    return 1;
317 	}
318 	i--;
319     }
320 
321     if (!w) {
322 	if (XCheckMaskEvent (CDisplay, event_mask, &e))
323 	    goto send_event;
324     } else {
325 	if (XCheckWindowEvent (CDisplay, w, event_mask, &e))
326 	    goto send_event;
327     }
328     return 0;
329 
330   send_event:
331     CSendEvent (&e);
332     if (event_return)
333 	*event_return = &e;
334     return 1;
335 }
336 
CKeyPending(void)337 int CKeyPending (void)
338 {
339     XSync (CDisplay, 0);
340     return CCheckWindowEvent (0, KeyPressMask, 0);
341 }
342 
343 /* send an expose event via the internal queue */
CSendExpose(Window win,int x,int y,int w,int h)344 int CSendExpose (Window win, int x, int y, int w, int h)
345 {
346     XEvent e;
347     memset (&e, 0, sizeof (e));
348     e.xexpose.type = Expose;
349     e.xexpose.serial = 0;
350     e.xexpose.send_event = 1;
351     e.xexpose.display = CDisplay;
352     e.xexpose.window = win;
353     e.xexpose.x = x;
354     e.xexpose.y = y;
355     e.xexpose.width = w;
356     e.xexpose.height = h;
357     return CSendEvent (&e);
358 }
359 
360 
361 /* }}} end of internal queue handler */
362 
363 
364 /* {{{ here is an expose caching-amalgamating stack system */
365 
366 typedef struct {
367     short x1, y1, x2, y2;
368     Window w;
369     long error;
370     int count;
371 } CRegion;
372 
373 #define MAX_NUM_REGIONS 63
374 static CRegion regions[MAX_NUM_REGIONS + 1];
375 
376 #define area(c) abs(((c).x1-(c).x2)*((c).y1-(c).y2))
377 
add_regions(CRegion r1,CRegion r2)378 static CRegion add_regions (CRegion r1, CRegion r2)
379 {
380     CRegion r;
381     memset(&r, '\0', sizeof(r));
382     r.x2 = max (max (r1.x1, r1.x2), max (r2.x1, r2.x2));
383     r.x1 = min (min (r1.x1, r1.x2), min (r2.x1, r2.x2));
384     r.y2 = max (max (r1.y1, r1.y2), max (r2.y1, r2.y2));
385     r.y1 = min (min (r1.y1, r1.y2), min (r2.y1, r2.y2));
386     r.w = r2.w;
387     r.error = (long) area (r) - area (r1) - area (r2);
388     r.error = max (r.error, 0);
389     r.error += r1.error + r2.error;
390     r.count = min (r1.count, r2.count);
391     return r;
392 }
393 
394 /* returns 1 when the stack is full, 0 otherwise */
push_region(XExposeEvent * e)395 static int push_region (XExposeEvent * e)
396 {
397     CRegion p;
398     memset(&p, '\0', sizeof(p));
399 
400     p.x1 = e->x;
401     p.x2 = e->x + e->width;
402     p.y1 = e->y;
403     p.y2 = e->y + e->height;
404     p.w = e->window;
405     p.error = 0;
406     p.count = e->count;
407 
408     if (last_region) {		/* this amalgamates p with a region on the stack of the same window */
409 	CRegion q;
410 	int i;
411         memset(&q, '\0', sizeof(q));
412 	for (i = last_region - 1; i >= 0; i--) {
413 	    if (regions[i].w == p.w) {
414 		q = add_regions (regions[i], p);
415 		if (q.error < 100) {
416 		    regions[i] = q;	/* amalgamate region, else... */
417 		    return 0;
418 		}
419 	    }
420 	}
421     }
422 
423     regions[last_region++] = p;		/* ...store a completely new region */
424     if (last_region >= MAX_NUM_REGIONS) {
425 /* NLS ? */
426 	printf ("push_region(): last_region >= MAX_NUM_REGIONS\n");
427 	return 1;
428     }
429     return 0;
430 }
431 
432 /*
433    Pops the first region matching w, if w == 0 then pops
434    the first region, returns 1 on empty.
435  */
pop_region(XExposeEvent * e,Window w)436 static int pop_region (XExposeEvent * e, Window w)
437 {
438     e->type = 0;
439     if (last_region) {
440 	int i = 0;
441 	if (w == 0)
442 	    goto any_window;
443 	for (i = last_region - 1; i >= 0; i--) {
444 	    if (regions[i].w == w) {
445 	      any_window:;
446 		e->type = Expose;
447 		e->serial = e->send_event = 0;
448 		e->display = CDisplay;
449 		e->window = regions[i].w;
450 		e->x = min (regions[i].x1, regions[i].x2);
451 		e->y = min (regions[i].y1, regions[i].y2);
452 		e->width = abs (regions[i].x1 - regions[i].x2);
453 		e->height = abs (regions[i].y1 - regions[i].y2);
454 		e->count = regions[i].count;
455 		last_region--;
456 		memmove (&(regions[i]), &(regions[i + 1]), (last_region - i) * sizeof (CRegion));
457 		return 0;
458 	    }
459 	}
460     }
461     return 1;
462 }
463 
pop_all_regions(Window w)464 static void pop_all_regions (Window w)
465 {
466     XEvent e;
467     memset (&e, 0, sizeof (e));
468     while (!pop_region (&(e.xexpose), w)) {
469 	e.type = InternalExpose;
470 	CSendEvent (&e);
471     }
472 }
473 
474 
475 /* }}} end expose amalgamation stack system */
476 
477 
478 /* {{{ key conversion utilities */
479 
480 /* xim.c */
481 KeySym key_sym_xlat (XEvent * ev, char *x_lat);
482 
CKeySym(XEvent * e)483 KeySym CKeySym (XEvent * e)
484 {
485     return key_sym_xlat (e, 0);
486 }
487 
mod_type_key(KeySym x)488 int mod_type_key (KeySym x)
489 {
490     switch ((int) x) {
491 #ifdef XK_Shift_L
492     case XK_Shift_L:
493 #endif
494 #ifdef XK_Shift_R
495     case XK_Shift_R:
496 #endif
497 #ifdef XK_Control_L
498     case XK_Control_L:
499 #endif
500 #ifdef XK_Control_R
501     case XK_Control_R:
502 #endif
503 #ifdef XK_Caps_Lock
504     case XK_Caps_Lock:
505 #endif
506 #ifdef XK_Shift_Lock
507     case XK_Shift_Lock:
508 #endif
509 #ifdef XK_Meta_L
510     case XK_Meta_L:
511 #endif
512 #ifdef XK_Meta_R
513     case XK_Meta_R:
514 #endif
515 #ifdef XK_Alt_L
516     case XK_Alt_L:
517 #endif
518 #ifdef XK_Alt_R
519     case XK_Alt_R:
520 #endif
521 #ifdef XK_Super_L
522     case XK_Super_L:
523 #endif
524 #ifdef XK_Super_R
525     case XK_Super_R:
526 #endif
527 #ifdef XK_Hyper_L
528     case XK_Hyper_L:
529 #endif
530 #ifdef XK_Hyper_R
531     case XK_Hyper_R:
532 #endif
533 	return 1;
534     }
535     return 0;
536 }
537 
538 
539 /* get a 15 bit "almost unique" key sym that includes keyboard modifier
540    info in the top 3 bits */
CKeySymMod(XEvent * ev)541 short CKeySymMod (XEvent * ev)
542 {
543     KeySym p;
544     XEvent e;
545 #ifdef USE_XIM
546     XIC ic;
547 #endif
548     int state;
549     if (!ev)
550 	return 0;
551     e = *ev;
552     state = e.xkey.state;
553     e.xkey.state = 0;		/* want the raw key */
554 #ifdef USE_XIM
555     ic = CIC;
556 /* turn off IC 'cos we want raw translation */
557     CIC = 0;
558     p = CKeySym (&e);
559     CIC = ic;
560 #else
561     p = CKeySym (&e);
562 #endif
563     if (p && !mod_type_key (p)) {
564 	if (state & ShiftMask)
565 	    p ^= 0x1000;
566 	if (state & ControlMask)
567 	    p ^= 0x2000;
568 	if (state & MyAltMask)
569 	    p ^= 0x4000;
570 	p &= 0x7FFF;
571     } else
572 	p = 0;
573     return p;
574 }
575 
576 /* }}} key conversion utilities */
577 
578 
579 /* {{{ focus cycling */
580 
581 /* returns 1 if the key pressed is usually a key to goto next focus */
is_focus_change_key(KeySym k,int command)582 int is_focus_change_key (KeySym k, int command)
583 {
584     return (k == XK_Tab || k == XK_KP_Tab || k == XK_ISO_Left_Tab || k == XK_Down ||
585 	k == XK_Up || k == XK_Left || k == XK_Right || k == XK_KP_Down ||
586 	    k == XK_KP_Up || k == XK_KP_Left || k == XK_KP_Right || command == CK_Right
587 	    || command == CK_Left || command == CK_Tab || command == CK_Up || command == CK_Down);
588 }
589 
590 /* returns 1 if the key pressed is usually a key to goto previous focus */
is_focus_prev_key(KeySym k,int command,unsigned int state)591 int is_focus_prev_key (KeySym k, int command, unsigned int state)
592 {
593     return (k == XK_ISO_Left_Tab || (((state) & ShiftMask) &&
594     (k == XK_Tab || k == XK_KP_Tab || command == CK_Tab)) || k == XK_Left
595 	    || k == XK_Up || k == XK_KP_Left || k == XK_KP_Up || command == CK_Left || command == CK_Up);
596 }
597 
598 /* use above to in combination to make this next function unnecesary */
599 /* int is_focus_next_key(KeySym k, int command, unsigned int state) */
600 
601 /*
602    This shifts focus to the previous or next sibling widget.
603    (usually the tab key is used, but also responds to up, down,
604    left and right.)
605  */
CCheckTab(XEvent * xevent,CEvent * cwevent)606 static int CCheckTab (XEvent * xevent, CEvent * cwevent)
607 {
608     if (xevent->type == KeyPress) {
609 	KeySym k;
610 	CWidget *w;
611 	k = CKeySym (xevent);
612 	if (!k)
613 	    return 0;
614 	if (!is_focus_change_key (k, cwevent->command))
615 	    return 0;
616 
617 	w = CWidgetOfWindow (xevent->xany.window);
618 
619 	if (!w)
620 	    CFocus (CFindFirstDescendent (xevent->xany.window));
621 	else if (!w->takes_focus)
622 	    CFocus (CChildFocus (w));
623 	else if (is_focus_prev_key (k, cwevent->command, xevent->xkey.state))
624 	    CFocus (CPreviousFocus (w));
625 	else
626 	    CFocus (CNextFocus (w));
627 	return (CGetFocus () != xevent->xany.window);	/* was handled since the focus did actually change */
628     }
629     return 0;
630 }
631 
click_on_widget(CWidget * w)632 void click_on_widget (CWidget * w)
633 {
634     XEvent e;
635     CFocus (w);
636     if (w->options & WIDGET_HOTKEY_ACTIVATES) {
637 	memset (&e, 0, sizeof (XEvent));
638 	e.xbutton.type = ButtonPress;
639 	e.xbutton.window = w->winid;
640 	e.xbutton.button = Button1;
641 	CSendEvent (&e);
642 	e.xbutton.type = ButtonRelease;
643 	CSendEvent (&e);
644 	e.xbutton.type = LeaveNotify;
645 	CSendEvent (&e);
646     }
647 }
648 
649 
match_hotkey(KeySym a,KeySym b)650 int match_hotkey (KeySym a, KeySym b)
651 {
652     if (isalpha (a & 0xFF) && isalpha (b & 0xFF) && my_lower_case (a) == my_lower_case (b))
653 	return 1;
654     if (a == b)
655 	return 1;
656     return 0;
657 }
658 
659 /*
660    Check for hot keys of buttons, sends a ButtonPress to the button if the key found.
661  */
CCheckButtonHotKey(XEvent * xevent,CEvent * cwevent)662 static int CCheckButtonHotKey (XEvent * xevent, CEvent * cwevent)
663 {
664     if (xevent->type == KeyPress) {
665 	KeySym k;
666 	CWidget *w, *p;
667 	k = CKeySym (xevent);
668         if (!k)
669             return 0;
670 	w = CWidgetOfWindow (xevent->xany.window);
671 	if (!w)
672 	    w = CFindFirstDescendent (xevent->xany.window);
673 	else if (!w->takes_focus)
674 	    w = CChildFocus (w);
675 	p = w = CNextFocus (w);
676 	do {
677 	    if (!w)
678 		return 0;
679 	    if (match_hotkey (w->hotkey, k)) {
680 		click_on_widget (w);
681 		return 1;
682 	    }
683 	    w = CNextFocus (w);	/* check all sibling buttons for a hotkey */
684 	} while ((unsigned long) w != (unsigned long) p);
685     }
686     return 0;
687 }
688 
check_hotkey_callback(CWidget * w,long k)689 static long check_hotkey_callback (CWidget * w, long k)
690 {
691     if (w->takes_focus && !w->disabled)
692 	if (match_hotkey (w->hotkey, k)) {
693 	    click_on_widget (w);
694 	    return 1;
695 	}
696     return 0;
697 }
698 
699 /* checks all widgets for a hotkey if alt is pressed */
CCheckGlobalHotKey(XEvent * xevent,CEvent * cwevent)700 static int CCheckGlobalHotKey (XEvent * xevent, CEvent * cwevent)
701 {
702     KeySym k;
703     k = CKeySym (xevent);
704     if (!k)
705 	return 0;
706     if (xevent->type == KeyPress && (xevent->xkey.state & MyAltMask) && !(xevent->xkey.state & ControlMask))
707 	return for_all_widgets ((for_all_widgets_cb_t) check_hotkey_callback, (void *) k, 0);
708     return 0;
709 }
710 
711 
712 /* }}} */
713 
714 
715 #ifdef DEBUG_ENTRY
716 
717 char *event_names[] =
718 {
719     "??zero??",
720     "??one??",
721     "KeyPress",
722     "KeyRelease",
723     "ButtonPress",
724     "ButtonRelease",
725     "MotionNotify",
726     "EnterNotify",
727     "LeaveNotify",
728     "FocusIn",
729     "FocusOut",
730     "KeymapNotify",
731     "Expose",
732     "GraphicsExpose",
733     "NoExpose",
734     "VisibilityNotify",
735     "CreateNotify",
736     "DestroyNotify",
737     "UnmapNotify",
738     "MapNotify",
739     "MapRequest",
740     "ReparentNotify",
741     "ConfigureNotify",
742     "ConfigureRequest",
743     "GravityNotify",
744     "ResizeRequest",
745     "CirculateNotify",
746     "CirculateRequest",
747     "PropertyNotify",
748     "SelectionClear",
749     "SelectionRequest",
750     "SelectionNotify",
751     "ColormapNotify",
752     "ClientMessage",
753     "MappingNotify"
754 };
755 
756 int num_event_names = sizeof (event_names) / sizeof (num_event_names);
757 
758 #endif
759 
run_callbacks(CWidget * w,XEvent * xevent,CEvent * cwevent)760 int run_callbacks (CWidget * w, XEvent * xevent, CEvent * cwevent)
761 {
762     static char no_ident[33] = "";
763     int handled = 0;
764 
765     if (!cwevent->text)
766 	cwevent->text = no_ident;
767     if (!cwevent->ident)
768 	cwevent->ident = no_ident;
769 
770 #ifdef DEBUG_ENTRY
771 /* NLS ? */
772     printf ("Calling %s: Type=%s\n", w->ident, type < num_event_names ? event_names[type] : itoa (type));
773 #endif
774     if (w->eh) {
775 	char ident[33];
776 	int (*cb) (struct cool_widget *, XEvent *, CEvent *);
777 	cb = w->callback;
778 	strcpy (ident, w->ident);
779 	if (w->callback_before) {
780 	    handled |= (*(w->callback_before)) (w, xevent, cwevent);
781 	    if (w != CIdent (ident))	/* callback
782 	    could free widget. hack this line must go
783 	    here because ident could = "". so happens
784 	    that such widgets don't have callbacks */
785 		return handled;
786 	}
787 	handled |= (*(w->eh)) (w, xevent, cwevent);
788 	if (cb) {
789 	    if (w != CIdent (ident))	/* same logic */
790 		return handled;
791 	    if (cwevent->ident[0])
792 		handled |= (*(w->callback)) (w, xevent, cwevent);
793 	}
794     }
795 #ifdef DEBUG_ENTRY
796 /* NLS ? */
797     printf ("Returned\n");
798 #endif
799     return handled;
800 }
801 
802 /* sets the mapped member of widget whose window is w, returning the previous state */
set_mapped(Window win,int i)803 static int set_mapped (Window win, int i)
804 {
805     int y;
806     CWidget *w;
807     w = CWidgetOfWindow (win);
808     if (!w)
809 	return i;
810     y = w->mapped;
811     w->mapped = i;
812     return y;
813 }
814 
815 int compose_key_pressed = 0;
816 int compose_key_which = 0;
817 
818 static unsigned int key_board_state = 0;
819 
CGetKeyBoardState(void)820 unsigned int CGetKeyBoardState (void)
821 {
822     return key_board_state;
823 }
824 
825 extern int option_latin2;
826 
set_compose_key(XEvent * xevent,int type)827 static void set_compose_key (XEvent * xevent, int type)
828 {
829     KeySym compose_key;
830     compose_key = CKeySym (xevent);
831 
832     if (!option_latin2) {
833 	if (compose_key == MyComposeKey || compose_key == XK_Multi_key) {
834 	    compose_key_pressed = (type == KeyPress);
835 	} else {
836 	    if (!(xevent->xkey.state & ControlMask))
837 		compose_key_pressed = 0;
838 	}
839     } else {
840 #if defined(XK_dead_belowdot) && defined(XK_dead_acute)
841 	if (compose_key >= XK_dead_acute &&
842 	    compose_key <= XK_dead_belowdot) {
843 	    if (compose_key_pressed)
844 		return;
845 	    compose_key_pressed = 1;
846 	    compose_key_which = compose_key;
847 	} else
848 #endif
849 	{
850 	    if (!(xevent->xkey.state & ControlMask))
851 		if (compose_key != XK_Shift_L &&
852 		    compose_key != XK_Shift_R &&
853 		    compose_key != XK_Alt_L &&
854 		    compose_key != XK_Alt_R &&
855 		    compose_key != XK_Mode_switch) {
856 		    compose_key_pressed = 0;
857 		    compose_key_which = 0;
858 		}
859 	}
860     }
861 }
862 
863 /* returns cwevent->insert == -1 and cwevent->command == 0 if no significant key pressed */
translate_key(XEvent * xevent,CEvent * cwevent)864 void translate_key (XEvent * xevent, CEvent * cwevent)
865 {
866     cwevent->key = key_sym_xlat (xevent, cwevent->xlat);
867     if (!cwevent->key)
868 	cwevent->key = XK_VoidSymbol;
869     cwevent->state = xevent->xkey.state;
870     if (!edit_translate_key (xevent->xkey.keycode, cwevent->key, xevent->xkey.state, &cwevent->command, &cwevent->insert)) {
871 	cwevent->insert = -1;
872 	cwevent->command = 0;
873     }
874 }
875 
876 /* {{{ Toolhints */
877 
878 int option_toolhint_milliseconds = 1000;
879 
render_text_ordinary(Window win,int x,int y,char * q)880 static void render_text_ordinary (Window win, int x, int y, char *q)
881 {
882     char *p;
883     int h = 0;
884     for (;;) {
885 	if (!(p = strchr (q, '\n')))
886 	    p = q + strlen (q);
887 	CImageText (win, FONT_OFFSET_X + x, FONT_OFFSET_Y + h + y, q,
888 		    (unsigned long) p - (unsigned long) q);
889 	h += FONT_PIX_PER_LINE;
890 	if (!*p)
891 	    break;
892 	q = p + 1;
893     }
894 }
895 
eh_toolhint(CWidget * w,XEvent * xevent,CEvent * cwevent)896 static int eh_toolhint (CWidget * w, XEvent * xevent, CEvent * cwevent)
897 {
898     if (xevent->type == Expose)
899 	if (!xevent->xexpose.count && w->label) {
900 	    CSetColor (color_widget (4));
901 	    CSetBackgroundColor (color_widget (14));
902 	    render_text_ordinary (w->winid, 2, 2, w->label);
903 	    CSetColor (color_widget (0));
904 	    XDrawRectangle (CDisplay, w->winid, CGC, 0, 0, w->width - 1, w->height - 1);
905 	}
906     return 0;
907 }
908 
hide_toolhint(void)909 static void hide_toolhint (void)
910 {
911     CDestroyWidget ("_toolhint");
912 }
913 
show_toolhint(Window win,int xp,int yp)914 static void show_toolhint (Window win, int xp, int yp)
915 {
916     int width, height, x, y;
917     CWidget *w, *t;
918     w = CWidgetOfWindow (win);
919     if (!w)
920 	return;
921     if (w->parentid != CRoot)
922 	if (!CWidgetOfWindow (CGetFocus ()))
923 	    return;
924     if (!w->toolhint)
925 	return;
926     CDestroyWidget ("_toolhint");
927     CGetWindowPosition (win, CRoot, &x, &y);
928     CTextSize (&width, &height, w->toolhint);
929     t = CSetupWidget ("_toolhint", CRoot, x + xp + 16,
930 		    y + yp - 2, width + 4, height + 4, C_TOOLHINT_WIDGET,
931 		      ExposureMask, color_widget(14), 0);
932     t->eh = eh_toolhint;
933     t->label = (char *) strdup (w->toolhint);
934 }
935 
CSetToolHint(const char * ident,const char * text)936 void CSetToolHint (const char *ident, const char *text)
937 {
938     CWidget *w;
939     if (!text)
940 	return;
941     if (!*text)
942 	return;
943     w = CIdent (ident);
944     if (!w)
945 	return;
946     if (w->toolhint)
947 	free (w->toolhint);
948     w->toolhint = (char *) strdup (text);
949 }
950 
951 /* }}} Toolhints */
952 
953 /* {{{ fd selection */
954 
955 #ifndef FD_SETSIZE
956 #define FD_SETSIZE 256
957 #endif
958 
959 struct file_des_watch {
960     int fd, how;
961     void (*callback) (int, fd_set *, fd_set *, fd_set *, void *);
962     const char *file;
963     int line;
964     void *data;
965 };
966 
967 static struct file_des_watch *watch_table[FD_SETSIZE];
968 
969 static int watch_table_last = 0;
970 
971 /* returns non-zero if table is full */
_CAddWatch(char * file,int line,int fd,void (* callback)(int,fd_set *,fd_set *,fd_set *,void *),int how,void * data)972 int _CAddWatch (char *file, int line, int fd, void (*callback) (int, fd_set *, fd_set *, fd_set *, void *),
973 		int how, void *data)
974 {
975     int i;
976     if (!callback || fd < 0 || !how) {
977 	fprintf (stderr, "bad args to CAddWatch??");
978 	return 1;
979     }
980     for (i = 0; i < watch_table_last; i++) {
981 	if (!watch_table[i])
982 	    continue;
983 	if (watch_table[i]->callback == callback && watch_table[i]->fd == fd) {
984 	    watch_table[i]->how |= how;
985 	    return 0;
986 	}
987     }
988     for (i = 0; i < watch_table_last && watch_table[i]; i++);
989     if (i >= FD_SETSIZE) {
990 	fprintf (stderr, "watch table overflow??");
991 	return 1;
992     }
993     watch_table[i] = malloc (sizeof (struct file_des_watch));
994     watch_table[i]->callback = callback;
995     watch_table[i]->how = how;
996     watch_table[i]->fd = fd;
997     watch_table[i]->data = data;
998     watch_table[i]->file = file;
999     watch_table[i]->line = line;
1000     if (watch_table_last < i + 1)
1001 	watch_table_last = i + 1;
1002     return 0;
1003 }
1004 
CRemoveWatch(int fd,void (* callback)(int,fd_set *,fd_set *,fd_set *,void *),int how)1005 void CRemoveWatch (int fd, void (*callback) (int, fd_set *, fd_set *, fd_set *, void *), int how)
1006 {
1007     int i;
1008     for (i = 0; i < watch_table_last; i++) {
1009 	if (!watch_table[i])
1010 	    continue;
1011 	if (watch_table[i]->callback == callback && watch_table[i]->fd == fd) {
1012 	    watch_table[i]->how &= ~how;
1013 	    if (!watch_table[i]->how)
1014 		goto do_free;
1015 	    return;
1016 	}
1017     }
1018     return;
1019   do_free:
1020     free (watch_table[i]);
1021     watch_table[i] = 0;
1022     for (;;) {
1023 	if (!watch_table_last)
1024 	    break;
1025 	if (watch_table[watch_table_last - 1])
1026 	    break;
1027 	watch_table_last--;
1028     }
1029 }
1030 
remove_all_watch(void)1031 void remove_all_watch (void)
1032 {
1033     int i;
1034     for (i = 0; i < watch_table_last; i++) {
1035 	if (!watch_table[i])
1036 	    continue;
1037 	free (watch_table[i]);
1038 	watch_table[i] = 0;
1039     }
1040     watch_table_last = 0;
1041 }
1042 
1043 static int CIdle = 0;
1044 
CIsIdle(void)1045 int CIsIdle (void)
1046 {
1047     return CIdle;
1048 }
1049 
1050 extern void _alarmhandler (void);
1051 extern int got_alarm;
1052 
run_watches(void)1053 static int run_watches (void)
1054 {
1055     int r, n = 0, i, found_watch;
1056     fd_set reading, writing, error;
1057     FD_ZERO (&reading);
1058     FD_ZERO (&writing);
1059     FD_ZERO (&error);
1060     FD_SET (ConnectionNumber (CDisplay), &reading);
1061     n = max (n, ConnectionNumber (CDisplay));
1062     for (i = 0; i < watch_table_last; i++) {
1063 	if (!watch_table[i])
1064 	    continue;
1065 	if (watch_table[i]->how & WATCH_READING) {
1066 	    FD_SET (watch_table[i]->fd, &reading);
1067 	    n = max (n, watch_table[i]->fd);
1068 	}
1069 	if (watch_table[i]->how & WATCH_WRITING) {
1070 	    FD_SET (watch_table[i]->fd, &writing);
1071 	    n = max (n, watch_table[i]->fd);
1072 	}
1073 	if (watch_table[i]->how & WATCH_ERROR) {
1074 	    FD_SET (watch_table[i]->fd, &error);
1075 	    n = max (n, watch_table[i]->fd);
1076 	}
1077     }
1078 #if 0	/* Norbert Nemec <nobbi@cheerful.com> */
1079     if (!n)
1080 	return 0;
1081 #endif	/* Norbert Nemec <nobbi@cheerful.com> */
1082     r = select (n + 1, &reading, &writing, &error, 0);
1083     if (got_alarm)
1084 	_alarmhandler ();
1085     childhandler_ ();
1086     if (r <= 0)
1087 	return 0;
1088     if (FD_ISSET (ConnectionNumber (CDisplay), &reading)) {
1089 	CIdle = 0;
1090 	r = 1;
1091     } else {
1092 	CIdle = 1;
1093 	r = 0;
1094     }
1095 /* callbacks could add or remove watches, so we restart the loop after each callback */
1096     do {
1097 	found_watch = 0;
1098 	for (i = 0; i < watch_table_last; i++) {
1099 	    int fd;
1100 	    if (!watch_table[i])
1101 		continue;
1102 	    fd = watch_table[i]->fd;
1103 	    if (FD_ISSET (fd, &reading) && watch_table[i]->how & WATCH_READING) {
1104 /* printf ("check watch %s:%d\n", watch_table[i]->file, watch_table[i]->line); */
1105 		(*watch_table[i]->callback) (fd, &reading, &writing, &error, watch_table[i]->data);
1106 		FD_CLR (fd, &reading);
1107 		found_watch = 1;
1108 		break;
1109 	    }
1110 	    if (FD_ISSET (fd, &writing) && watch_table[i]->how & WATCH_WRITING) {
1111 		(*watch_table[i]->callback) (fd, &reading, &writing, &error, watch_table[i]->data);
1112 		FD_CLR (fd, &writing);
1113 		found_watch = 1;
1114 		break;
1115 	    }
1116 	    if (FD_ISSET (fd, &error) && watch_table[i]->how & WATCH_ERROR) {
1117 		(*watch_table[i]->callback) (fd, &reading, &writing, &error, watch_table[i]->data);
1118 		FD_CLR (fd, &error);
1119 		found_watch = 1;
1120 		break;
1121 	    }
1122 	}
1123     } while (found_watch);
1124     return r;
1125 }
1126 
1127 /* }}} fd selection */
1128 
1129 extern Atom ATOM_WM_PROTOCOLS, ATOM_WM_DELETE_WINDOW, ATOM_WM_TAKE_FOCUS;
1130 
1131 /*
1132    This is the core of the library. CNextEvent must be called continually.
1133    The calling application must only use CNextEvent as a block.
1134    CNextEvent does the following in sequence:
1135 
1136    1 check if event is AlarmEvent, yes: toggle cursor and return
1137    2 check MappingNotify and return
1138    3 cache expose event for later handling. No "raw" exposes are ever processed.
1139    they are first merged into courser exposes and resent via the internal
1140    queue; return
1141    4 check if an internal expose resulting from 3 above. If so rename it to Expose
1142    and continue
1143    5 Check for various other events and process/convert them
1144    6 look for a widget whose window matches .xany.window. If the widget is not
1145    a picture widget then call the widget event handling routine: eh_*
1146    Then call the widgets user event handler: ->callback
1147    7 do the same for picture widgets. These must come last so that the get drawn
1148    on top of other things if there is for exampla a image and a picture in
1149    the same window.
1150    8 if the event was a key event, and none of the handlers returned 1
1151    check the tab key for focus cycling.
1152 
1153    This returns cwevent->handled non-zero if the event was a key event and
1154    was handled, 0 otherwise.
1155  */
1156 
1157 int (*global_callback) (XEvent *x) = 0;
1158 extern int menu_grabbed;
1159 
1160 /* xevent or cwevent or both my be passed as NULL */
CNextEvent(XEvent * xevent,CEvent * cwevent)1161 void CNextEvent (XEvent * xevent, CEvent * cwevent)
1162 {
1163     static char idle = 1;
1164     static char no_ident[33];
1165     int i = 0;
1166     int handled = 0;
1167     CWidget *w = 0;
1168     XEvent private_xevent;
1169     CEvent private_cwevent;
1170     Window win;
1171 #ifdef HAVE_DND
1172     static Window drop_window = 0;
1173 #endif
1174     int type;
1175     static Window last_events_window1 = -2;
1176     static Window last_events_window2 = -2;
1177     static int last_widget1 = 0;
1178     static int last_widget2 = 0;
1179     static XEvent button_repeat;
1180     static int button_repeat_count = 0;
1181     static Window toolhint_window = 0;
1182     static int toolhint_count = 0, x_toolhint, y_toolhint;
1183     int next_event = 0;
1184 
1185     memset (&button_repeat, 0, sizeof (button_repeat));
1186 
1187     if (!xevent) {
1188         memset(&private_xevent, '\0', sizeof(private_xevent));
1189 	xevent = &private_xevent;
1190     }
1191     if (!cwevent) {
1192         memset(&private_cwevent, '\0', sizeof(private_cwevent));
1193 	cwevent = &private_cwevent;
1194     }
1195 
1196     if (!CPending ())		/* flush output; check if events */
1197 	pop_all_regions (0);	/* just make sure not outstanding exposes */
1198     while (!pop_event (xevent)) {	/* first check our own events, if none of our own coming, _then_ we look at the server */
1199 	if (QLength (CDisplay)) {
1200 	    memset (xevent, 0, sizeof (XEvent));
1201 	    XNextEvent (CDisplay, xevent);
1202 	    next_event = 1;
1203 	    break;
1204 	} else if (run_watches ()) {
1205 	    memset (xevent, 0, sizeof (XEvent));
1206 	    XNextEvent (CDisplay, xevent);
1207 	    next_event = 1;
1208 	    break;
1209 	}
1210     }
1211 
1212     memset (cwevent, 0, sizeof (CEvent));
1213     memset (no_ident, 0, 33);
1214     cwevent->text = no_ident;
1215     cwevent->ident = no_ident;
1216 
1217 /* used for rxvt stuff */
1218     if (global_callback)
1219 	if ((*global_callback) (xevent))
1220 	    return;
1221 
1222     if (next_event && xevent->xany.type != KeyRelease) {
1223 	if (XFilterEvent(xevent, CGetICFocus ())) {
1224 	    xevent->xany.type = 0;
1225 	    xevent->xany.window = 0;
1226 	    cwevent->type = 0;
1227 	    cwevent->kind = 0;
1228 	    return;
1229 	}
1230     }
1231 
1232     win = xevent->xany.window;
1233     type = xevent->type;
1234 
1235     switch (type) {
1236     case TickEvent:
1237 	if (idle == 1)		/* this will XSync at the end of a burst of events */
1238 	    XSync (CDisplay, 0);	/* this, dnd.c and CKeyPending above are the only places in the library where XSync is called */
1239 	if (button_repeat_count++ > 10)
1240 	    if (button_repeat.type == ButtonRepeat)
1241 		if (!(button_repeat_count % (ALRM_PER_SECOND / 25)))
1242 		    CSendEvent (&button_repeat);
1243 	if (toolhint_window && option_toolhint_milliseconds && !menu_grabbed) {
1244 	    toolhint_count++;
1245 	    if (toolhint_count > (ALRM_PER_SECOND * option_toolhint_milliseconds) / 1000) {
1246 		show_toolhint (toolhint_window, x_toolhint, y_toolhint);
1247 		toolhint_window = 0;
1248 	    }
1249 	}
1250 	idle++;
1251 	return;
1252     case AlarmEvent:
1253 	{
1254 	    static int last_root_x_return = 0, last_root_y_return = 0;
1255 	    static Window last_top = 0;
1256 	    int a;
1257 	    CWidget *pulled_menu;
1258 	    xevent->type = cwevent->type = AlarmEvent;
1259 	    toggle_cursor ();
1260 	    for (a = 0; a < 33; a++) {
1261 		if (global_alarm_callback[a]) {
1262 		    cwevent->type = type;
1263 		    cwevent->kind = C_ALARM_WIDGET;
1264 		    (*(global_alarm_callback[a])) (0, xevent, cwevent);
1265 		}
1266 	    }
1267 	}
1268 	return;
1269     case MappingNotify:
1270 	XRefreshKeyboardMapping (&(xevent->xmapping));
1271 	break;
1272     case Expose:{
1273 	    XEvent eev;
1274 	    memset(&eev, '\0', sizeof(eev));
1275 /* here we amalgamate exposes of the same window together and re-send them as InternalExpose events */
1276 	    if (push_region (&(xevent->xexpose))) {
1277 		pop_all_regions (win);
1278 	    } else {
1279 		for (;;) {
1280 		    if (CExposePending (win, &eev)) {
1281 			if (!push_region (&(eev.xexpose)))
1282 			    continue;
1283 		    }
1284 		    pop_all_regions (win);
1285 		    break;
1286 		}
1287 	    }
1288 	}
1289 	return;
1290     case InternalExpose:
1291 	type = xevent->type = Expose;
1292 	if (!xevent->xexpose.count)
1293 	    render_focus_border (win);
1294 	break;
1295     case EnterNotify:
1296 /* The dnd drag will trap all events except enter and leave. These can
1297    be used to trace which window the pointer has gotten into during
1298    a drag. */
1299 	toolhint_count = 0;
1300 	toolhint_window = xevent->xbutton.window;
1301 	hide_toolhint ();
1302 	x_toolhint = xevent->xbutton.x;
1303 	y_toolhint = xevent->xbutton.y;
1304 #ifdef HAVE_DND
1305 	drop_window = xevent->xbutton.window;
1306 #endif
1307 	break;
1308     case LeaveNotify:
1309 	toolhint_window = 0;
1310 	hide_toolhint ();
1311 	break;
1312     case MapNotify:
1313 	if (set_mapped (xevent->xmap.window, WINDOW_MAPPED) & WINDOW_FOCUS_WHEN_MAPPED)
1314 	    focus_window (xevent->xmap.window);
1315 	break;
1316     case FocusOut:
1317     case FocusIn:
1318 	hide_toolhint ();
1319 	toolhint_window = 0;
1320 	process_external_focus (win, type);
1321 	return;
1322 	break;
1323     case MotionNotify:
1324 #if 0
1325 	if (xevent->xmotion.button == Button2 && option_middle_button_pastes) {
1326 	    break;
1327 	}
1328 #endif
1329 	if (xevent->xmotion.window == toolhint_window) {
1330 	    hide_toolhint ();
1331 	    x_toolhint = xevent->xmotion.x;
1332 	    y_toolhint = xevent->xmotion.y;
1333 	}
1334 	break;
1335     case ButtonPress:
1336 	if (xevent->xbutton.button == Button2 && option_middle_button_pastes) {
1337 	    xevent->type = KeyPress;
1338 	    cwevent->command = CK_XPaste;
1339 	    cwevent->insert = -1;
1340 	}
1341 	key_board_state = xevent->xbutton.state;
1342 	hide_toolhint ();
1343 	toolhint_window = 0;
1344 	memcpy (&button_repeat, xevent, sizeof (XEvent));
1345 	button_repeat.type = ButtonRepeat;
1346 	button_repeat_count = 0;
1347 	break;
1348     case ButtonRelease:
1349 	if (xevent->xbutton.button == Button2 && option_middle_button_pastes) {
1350 	    break;
1351 	}
1352 	key_board_state = xevent->xbutton.state;
1353 	toolhint_window = 0;
1354 	button_repeat.type = 0;
1355 	break;
1356     case KeyPress:
1357 	key_board_state = xevent->xkey.state;
1358 	hide_toolhint ();
1359 	toolhint_window = 0;
1360 	translate_key (xevent, cwevent);
1361     case KeyRelease:
1362 	key_board_state = xevent->xkey.state;
1363 	win = xevent->xkey.window = CGetFocus ();
1364 	set_compose_key (xevent, type);
1365 	break;
1366     case ConfigureNotify:{
1367 	    CWidget *m;
1368 	    m = CWidgetOfWindow (win);
1369 	    if (!m)
1370 		m = CFindFirstDescendent (win);
1371 	    if (!m)
1372 		return;
1373 	    if (m->parentid != CRoot)
1374 		return;
1375 	    CSetSize (m, xevent->xconfigure.width, xevent->xconfigure.height);
1376 	}
1377 	return;
1378     case SelectionNotify:
1379 	if (xdnd_handle_drop_events (CDndClass, xevent))
1380 	    return;
1381 	break;
1382     case SelectionClear:
1383 	selection_clear ();
1384 	return;
1385     case UnmapNotify:
1386 	set_mapped (xevent->xmap.window, 0);
1387 	break;
1388     case ClientMessage:
1389 #ifdef HAVE_DND
1390 /* If we recieve a drop from dnd, we need to find the window in which the
1391    drop occurred. This will be the last window with an EnterNotify (above).
1392    Now we find the pointer coords relative to that window, and change the
1393    event to go to that window */
1394 	if ((xevent->xclient.message_type == DndProtocol && xevent->xclient.data.l[4] == 1)
1395 	    ||
1396 	    (xevent->xclient.message_type == OldDndProtocol && xevent->xclient.data.l[4] == 0)) {
1397 	    int x, y, rx, ry;
1398 	    Window root, child;
1399 	    unsigned int mask;
1400 	    win = xevent->xclient.window = drop_window;
1401 	    XQueryPointer (CDisplay, drop_window, &root, &child, &rx, &ry, &x, &y, &mask);
1402 	    xevent->xclient.data.l[3] = (long) x + (long) y *65536L;
1403 	}
1404 #else
1405 	if (xdnd_handle_drop_events (CDndClass, xevent))
1406 	    return;
1407 #endif
1408 	if (xevent->xclient.message_type == ATOM_WM_PROTOCOLS) {
1409 	    if (xevent->xclient.data.l[0] == ATOM_WM_DELETE_WINDOW) {
1410 		if (xevent->xclient.window == CFirstWindow) {
1411 		    XEvent exit_event;
1412 		    memset (&exit_event, 0, sizeof (exit_event));
1413 		    exit_event.type = QuitApplication;
1414 		    CSendEvent (&exit_event);
1415 		} else {
1416 		    CDestroyWidget ((CWidgetOfWindow (xevent->xclient.window))->ident);
1417 		}
1418 		return;
1419 	    }
1420 	    if (xevent->xclient.data.l[0] == ATOM_WM_TAKE_FOCUS) {
1421 		hide_toolhint ();
1422 		toolhint_window = 0;
1423 		process_external_focus (win, type);
1424 		return;
1425 	    }
1426 	}
1427 	break;
1428     }
1429     idle = 0;
1430 
1431     if (last_events_window1 == win && CIndex (last_widget1))	/* this will speed up the search a bit */
1432 	i = last_widget1 - 1;	/* by remembering the last two windows */
1433     else if (last_events_window2 == win && CIndex (last_widget2))
1434 	i = last_widget2 - 1;
1435 
1436 /* Now find if the event belongs to any of the widgets */
1437     while (last_widget > i++) {
1438 	if (!(w = CIndex (i)))
1439 	    continue;
1440 	if (w->winid != win)
1441 	    continue;
1442 	if (w->disabled)
1443 	    if (type != Expose && type != FocusOut && type != SelectionRequest && type != LeaveNotify && type != ClientMessage)
1444 		break;
1445 	if (w->kind == C_PICTURE_WIDGET)
1446 	    continue;
1447 
1448 	last_widget2 = last_widget1;
1449 	last_widget1 = i;
1450 	last_events_window2 = last_events_window1;
1451 	last_events_window1 = win;
1452 
1453 	cwevent->type = type;
1454 	cwevent->kind = w->kind;
1455 	cwevent->window = win;
1456 
1457 	handled |= run_callbacks (w, xevent, cwevent);
1458 	w = CIndex (i);
1459 	break;
1460     }
1461 
1462 #ifdef HAVE_PICTURE
1463 
1464     i = 0;
1465 /* picture exposes must come last so that they can be drawn on top of
1466    other widgets */
1467     if (type == Expose && w) {
1468 	while (last_widget > i++) {
1469 	    if (!(w = CIndex (i)))
1470 		continue;
1471 	    if (w->kind != C_PICTURE_WIDGET)
1472 		continue;
1473 	    if (w->parentid != xevent->xany.window)
1474 		continue;
1475 	    if (w->disabled && type != Expose)
1476 		continue;
1477 	    cwevent->type = type;
1478 	    cwevent->kind = w->kind;
1479 	    cwevent->window = xevent->xany.window;
1480 
1481 	    handled |= run_callbacks (w, xevent, cwevent);
1482 	    /*break; *//*no break here 'cos there may be two picture widgets in the same window */
1483 	}
1484     }
1485 #endif				/* ! HAVE_PICTURE */
1486 
1487     if (type == KeyPress && w) {
1488 	cwevent->handled = handled;
1489 	if (!handled)
1490 	    handled = CCheckTab (xevent, cwevent);
1491 	if (!handled)
1492 	    handled = CCheckButtonHotKey (xevent, cwevent);
1493 	if (!handled)
1494 	    handled = CCheckGlobalHotKey (xevent, cwevent);
1495     }
1496 #ifdef DEBUG_DOUBLE
1497     widget_check_magic ();
1498 #endif
1499 
1500     if (!cwevent->text)
1501 	cwevent->text = no_ident;
1502     if (!cwevent->ident)
1503 	cwevent->ident = no_ident;
1504 
1505     return;
1506 }
1507 
1508 
1509 int inbounds (int x, int y, int x1, int y1, int x2, int y2);
1510 
1511 
1512 /*-----------------------------------------------------------------------*/
eh_button(CWidget * w,XEvent * xevent,CEvent * cwevent)1513 int eh_button (CWidget * w, XEvent * xevent, CEvent * cwevent)
1514 {
1515     static Window last_win = 0;
1516     switch (xevent->type) {
1517     case MotionNotify:
1518 	break;
1519     case ButtonPress:
1520 	last_win = xevent->xbutton.window;
1521 	if (xevent->xbutton.button == Button1 || xevent->xbutton.button == Button2
1522 	    || xevent->xbutton.button == Button3) {
1523 	    w->options &= (0xFFFFFFFFUL - BUTTON_PRESSED - BUTTON_HIGHLIGHT);
1524 	    w->options |= BUTTON_PRESSED;
1525 	    CFocus (w);
1526 	    (*w->render) (w);
1527 	}
1528 	break;
1529     case KeyPress:
1530 	if ((cwevent->command != CK_Enter || w->kind == C_SWITCH_WIDGET)
1531 	    && cwevent->key != XK_space)
1532 	    break;
1533 	w->options &= (0xFFFFFFFFUL - BUTTON_PRESSED - BUTTON_HIGHLIGHT);
1534 	w->options |= BUTTON_PRESSED;
1535 	if (w->kind == C_SWITCH_WIDGET)
1536 	    toggle_radio_button (w);
1537 	cwevent->ident = w->ident;	/* return the event */
1538 	(*w->render) (w);
1539 	return 1;
1540     case KeyRelease:
1541 	w->options &= (0xFFFFFFFFUL - BUTTON_PRESSED - BUTTON_HIGHLIGHT);
1542 	(*w->render) (w);
1543 	break;
1544     case ButtonRelease:
1545 	last_win = 0;
1546 	if (xevent->xbutton.button == Button1 || xevent->xbutton.button == Button2
1547 	    || xevent->xbutton.button == Button3) {
1548 	    w->options &= (0xFFFFFFFFUL - BUTTON_PRESSED - BUTTON_HIGHLIGHT);
1549 	    w->options |= BUTTON_HIGHLIGHT;
1550 	    if (inbounds (xevent->xbutton.x, xevent->xbutton.y, 0, 0, w->width, w->height)) {
1551 		if (w->kind == C_SWITCH_WIDGET)
1552 		    toggle_radio_button (w);
1553 		cwevent->ident = w->ident;	/* return the event */
1554 		(*w->render) (w);
1555 		return 1;
1556 	    }
1557 	    (*w->render) (w);
1558 	}
1559 	return 0;
1560     case EnterNotify:
1561 	w->options &= ~(BUTTON_PRESSED | BUTTON_HIGHLIGHT);
1562 	w->options |= BUTTON_HIGHLIGHT | (last_win == xevent->xbutton.window ? BUTTON_PRESSED : 0);
1563 	(*w->render) (w);
1564 	break;
1565     case Expose:
1566 	if (xevent->xexpose.count)
1567 	    break;
1568 	(*w->render) (w);
1569 	break;
1570     case LeaveNotify:
1571 	w->options &= (0xFFFFFFFFUL - BUTTON_PRESSED - BUTTON_HIGHLIGHT);
1572 	(*w->render) (w);
1573 	break;
1574     }
1575     return 0;
1576 }
1577 
1578 /*-----------------------------------------------------------------------*/
eh_bitmap(CWidget * w,XEvent * xevent,CEvent * cwevent)1579 int eh_bitmap (CWidget * w, XEvent * xevent, CEvent * cwevent)
1580 {
1581     switch (xevent->type) {
1582     case Expose:
1583 	if (!xevent->xexpose.count)
1584 	    render_button (w);
1585 	break;
1586     }
1587     return 0;
1588 }
1589 
1590 extern struct look *look;
1591 
1592 /*-----------------------------------------------------------------------*/
eh_window(CWidget * w,XEvent * xevent,CEvent * cwevent)1593 int eh_window (CWidget * w, XEvent * xevent, CEvent * cwevent)
1594 {
1595     return (*look->window_handler) (w, xevent, cwevent);
1596 }
1597 
1598 /*-----------------------------------------------------------------------*/
eh_bar(CWidget * w,XEvent * xevent,CEvent * cwevent)1599 int eh_bar (CWidget * w, XEvent * xevent, CEvent * cwevent)
1600 {
1601     switch (xevent->type) {
1602 #if 0
1603     case ResizeNotify:
1604 	CSetWidgetSize (w->ident, xevent->xconfigure.width - WIDGET_SPACING * 2, 3);
1605 	break;
1606 #endif
1607     case Expose:
1608 	if (!xevent->xexpose.count)
1609 	    render_bar (w);
1610 	break;
1611     }
1612     return 0;
1613 }
1614 
1615 /*-----------------------------------------------------------------------*/
eh_progress(CWidget * w,XEvent * xevent,CEvent * cwevent)1616 int eh_progress (CWidget * w, XEvent * xevent, CEvent * cwevent)
1617 {
1618     switch (xevent->type) {
1619     case Expose:
1620 	if (!xevent->xexpose.count)
1621 	    render_progress (w);
1622 	break;
1623     }
1624     return 0;
1625 }
1626 
1627 
1628 /*-----------------------------------------------------------------------*/
eh_status(CWidget * w,XEvent * xevent,CEvent * cwevent)1629 int eh_status (CWidget * w, XEvent * xevent, CEvent * cwevent)
1630 {
1631     switch (xevent->type) {
1632     case Expose:
1633 	if (!xevent->xexpose.count)
1634 	    render_status (w, 1);
1635 	break;
1636     }
1637     return 0;
1638 }
1639 
1640 /*-----------------------------------------------------------------------*/
eh_text(CWidget * w,XEvent * xevent,CEvent * cwevent)1641 int eh_text (CWidget * w, XEvent * xevent, CEvent * cwevent)
1642 {
1643     switch (xevent->type) {
1644     case Expose:
1645 	if (!xevent->xexpose.count)
1646 	    render_text (w);
1647 	break;
1648     }
1649     return 0;
1650 }
1651 
1652 /*-----------------------------------------------------------------------*/
eh_sunken(CWidget * w,XEvent * xevent,CEvent * cwevent)1653 int eh_sunken (CWidget * w, XEvent * xevent, CEvent * cwevent)
1654 {
1655     switch (xevent->type) {
1656     case Expose:
1657 	if (!xevent->xexpose.count)
1658 	    render_sunken (w);
1659 	break;
1660     }
1661     return 0;
1662 }
1663 /*-----------------------------------------------------------------------*/
eh_bwimage(CWidget * w,XEvent * xevent,CEvent * cwevent)1664 int eh_bwimage (CWidget * w, XEvent * xevent, CEvent * cwevent)
1665 {
1666 /*      case C_8BITIMAGE_WIDGET:
1667    case C_BWIMAGE_WIDGET: */
1668 #ifdef HAVE_BWIMAGE
1669     switch (xevent->type) {
1670     case Expose:
1671 	render_bw_image (w, xevent->xexpose.x, xevent->xexpose.y, xevent->xexpose.width, xevent->xexpose.height);
1672 	break;
1673     case ButtonRelease:
1674     case ButtonPress:
1675     case MotionNotify:
1676 	resolve_button (xevent, cwevent);
1677 	cwevent->x -= 2;	/*subtract border */
1678 	cwevent->y -= 2;
1679 	cwevent->ident = w->ident;
1680 	break;
1681     }
1682 #endif
1683     return 0;
1684 }
1685 
1686 /*-----------------------------------------------------------------------*/
1687 
1688 extern int eh_textbox (CWidget * w, XEvent * xevent, CEvent * cwevent);
1689 extern int eh_textinput (CWidget * w, XEvent * xevent, CEvent * cwevent);
1690 extern int eh_scrollbar (CWidget * w, XEvent * xevent, CEvent * cwevent);
1691 extern int eh_unicode (CWidget * w, XEvent * xevent, CEvent * cwevent);
1692 
default_event_handler(int i)1693 int (*default_event_handler (int i)) (CWidget *, XEvent *, CEvent *) {
1694     switch (i) {
1695     case C_BITMAPBUTTON_WIDGET:
1696     case C_SWITCH_WIDGET:
1697     case C_BUTTON_WIDGET:
1698 	return eh_button;
1699     case C_WINDOW_WIDGET:
1700 	return eh_window;
1701     case C_BAR_WIDGET:
1702 	return eh_bar;
1703     case C_SUNKEN_WIDGET:
1704 	return eh_sunken;
1705     case C_HORSCROLL_WIDGET:
1706     case C_VERTSCROLL_WIDGET:
1707     case C_HORISCROLL_WIDGET:
1708 	return eh_scrollbar;
1709     case C_TEXTINPUT_WIDGET:
1710 	return eh_textinput;
1711     case C_TEXTBOX_WIDGET:
1712 	return eh_textbox;
1713     case C_TEXT_WIDGET:
1714 	return eh_text;
1715     case C_STATUS_WIDGET:
1716 	return eh_status;
1717     case C_8BITIMAGE_WIDGET:
1718     case C_BWIMAGE_WIDGET:
1719 	return eh_bwimage;
1720     case C_PROGRESS_WIDGET:
1721 	return eh_progress;
1722     case C_BITMAP_WIDGET:
1723 	return eh_bitmap;
1724 #ifdef HAVE_PICTURE
1725     case C_PICTURE_WIDGET:
1726 	return eh_picture;
1727 #endif
1728     case C_EDITOR_WIDGET:
1729 	return eh_editor;
1730     case C_UNICODE_WIDGET:
1731 	return eh_unicode;
1732     }
1733     return (int (*) (CWidget *, XEvent *, CEvent *)) 0;
1734 }
1735 
1736