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