1 /*
2  * Copyright (C) 1997-2009, Michael Jennings
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies of the Software, its documentation and marketing & publicity
13  * materials, and acknowledgment shall be given in the documentation, materials
14  * and software packages that this Software was used.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 static const char cvs_ident[] = "$Id: events.c 51650 2010-08-26 01:34:13Z lucas $";
25 
26 #include "config.h"
27 #include "feature.h"
28 
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <limits.h>
33 
34 #include "startup.h"
35 #include "actions.h"
36 #include "buttons.h"
37 #include "command.h"
38 #include "e.h"
39 #include "events.h"
40 #include "font.h"
41 #include "menus.h"
42 #include "options.h"
43 #include "pixmap.h"
44 #include "profile.h"
45 #include "screen.h"
46 #include "scrollbar.h"
47 #include "term.h"
48 #include "windows.h"
49 #ifdef ESCREEN
50 #  include "screamcfg.h"
51 #endif
52 
53 unsigned char paused = 0;
54 event_master_t event_master;
55 event_dispatcher_data_t primary_data;
56 mouse_button_state_t button_state = { 0, 0, 0, 0, 0, 0, 0, 0 };
57 
58 /********** The Event Handling Subsystem **********/
59 void
event_init_subsystem(event_dispatcher_t primary_dispatcher,event_dispatcher_init_t init)60 event_init_subsystem(event_dispatcher_t primary_dispatcher, event_dispatcher_init_t init)
61 {
62 
63     /* Initialize the subsystem with the main event dispatcher */
64     event_master.num_dispatchers = 1;
65     event_master.dispatchers = (event_dispatcher_t *) MALLOC(sizeof(event_dispatcher_t));
66     event_master.dispatchers[0] = (event_dispatcher_t) primary_dispatcher;
67     (init) ();                  /* Initialize the dispatcher's data */
68 }
69 
70 void
event_register_dispatcher(event_dispatcher_t func,event_dispatcher_init_t init)71 event_register_dispatcher(event_dispatcher_t func, event_dispatcher_init_t init)
72 {
73 
74     /* Add a secondary event dispatcher */
75     event_master.num_dispatchers++;
76     event_master.dispatchers =
77         (event_dispatcher_t *) REALLOC(event_master.dispatchers, sizeof(event_dispatcher_t) * event_master.num_dispatchers);
78     event_master.dispatchers[event_master.num_dispatchers - 1] = (event_dispatcher_t) func;
79     (init) ();                  /* Initialize the dispatcher's data */
80 }
81 
82 void
event_dispatch(event_t * event)83 event_dispatch(event_t *event)
84 {
85 
86     register unsigned char i;
87     register unsigned char handled;
88 
89     /* No debugging stuff here.  If you need it, take it out when done.  This should be ultra-fast. -- mej */
90     for (i = 0; i < event_master.num_dispatchers; i++) {
91         handled = (event_master.dispatchers[i]) (event);
92         if (handled) {
93             break;
94         }
95     }
96 }
97 
98 void
event_data_add_mywin(event_dispatcher_data_t * data,Window win)99 event_data_add_mywin(event_dispatcher_data_t *data, Window win)
100 {
101 
102     ASSERT(data != NULL);
103 
104     if (data->num_my_windows > 0) {
105         (data->num_my_windows)++;
106         data->my_windows = (Window *) REALLOC(data->my_windows, sizeof(Window) * data->num_my_windows);
107         data->my_windows[data->num_my_windows - 1] = win;
108     } else {
109         data->num_my_windows = 1;
110         data->my_windows = (Window *) MALLOC(sizeof(Window));
111         data->my_windows[0] = win;
112     }
113 }
114 
115 void
event_data_add_parent(event_dispatcher_data_t * data,Window win)116 event_data_add_parent(event_dispatcher_data_t *data, Window win)
117 {
118 
119     ASSERT(data != NULL);
120 
121     if (data->num_my_parents > 0) {
122         (data->num_my_parents)++;
123         data->my_parents = (Window *) REALLOC(data->my_parents, sizeof(Window) * data->num_my_parents);
124         data->my_parents[data->num_my_parents - 1] = win;
125     } else {
126         data->num_my_parents = 1;
127         data->my_parents = (Window *) MALLOC(sizeof(Window));
128         data->my_parents[0] = win;
129     }
130 }
131 
132 void
event_init_primary_dispatcher(void)133 event_init_primary_dispatcher(void)
134 {
135 
136     MEMSET(&primary_data, 0, sizeof(event_dispatcher_data_t));
137 
138     EVENT_DATA_ADD_HANDLER(primary_data, KeyPress, handle_key_press);
139     EVENT_DATA_ADD_HANDLER(primary_data, PropertyNotify, handle_property_notify);
140     EVENT_DATA_ADD_HANDLER(primary_data, DestroyNotify, handle_destroy_notify);
141     EVENT_DATA_ADD_HANDLER(primary_data, ClientMessage, handle_client_message);
142     EVENT_DATA_ADD_HANDLER(primary_data, MappingNotify, handle_mapping_notify);
143     EVENT_DATA_ADD_HANDLER(primary_data, VisibilityNotify, handle_visibility_notify);
144     EVENT_DATA_ADD_HANDLER(primary_data, EnterNotify, handle_enter_notify);
145     EVENT_DATA_ADD_HANDLER(primary_data, LeaveNotify, handle_leave_notify);
146     EVENT_DATA_ADD_HANDLER(primary_data, FocusIn, handle_focus_in);
147     EVENT_DATA_ADD_HANDLER(primary_data, FocusOut, handle_focus_out);
148     EVENT_DATA_ADD_HANDLER(primary_data, ConfigureNotify, handle_configure_notify);
149     EVENT_DATA_ADD_HANDLER(primary_data, SelectionClear, handle_selection_clear);
150     EVENT_DATA_ADD_HANDLER(primary_data, SelectionNotify, handle_selection_notify);
151     EVENT_DATA_ADD_HANDLER(primary_data, SelectionRequest, handle_selection_request);
152     EVENT_DATA_ADD_HANDLER(primary_data, GraphicsExpose, handle_expose);
153     EVENT_DATA_ADD_HANDLER(primary_data, Expose, handle_expose);
154     EVENT_DATA_ADD_HANDLER(primary_data, ButtonPress, handle_button_press);
155     EVENT_DATA_ADD_HANDLER(primary_data, ButtonRelease, handle_button_release);
156     EVENT_DATA_ADD_HANDLER(primary_data, MotionNotify, handle_motion_notify);
157 
158     event_data_add_mywin(&primary_data, TermWin.parent);
159     event_data_add_mywin(&primary_data, TermWin.vt);
160 
161 #ifdef PIXMAP_SUPPORT
162     if (desktop_window != None) {
163         event_data_add_parent(&primary_data, desktop_window);
164     }
165 #endif
166 }
167 
168 unsigned char
event_win_is_mywin(register event_dispatcher_data_t * data,Window win)169 event_win_is_mywin(register event_dispatcher_data_t *data, Window win)
170 {
171 
172     register unsigned short i;
173 
174     ASSERT_RVAL(data != NULL, 0);
175 
176     for (i = 0; i < data->num_my_windows; i++) {
177         if (data->my_windows[i] == win) {
178             return 1;
179         }
180     }
181     return 0;
182 }
183 
184 unsigned char
event_win_is_parent(register event_dispatcher_data_t * data,Window win)185 event_win_is_parent(register event_dispatcher_data_t *data, Window win)
186 {
187 
188     register unsigned short i;
189 
190     ASSERT_RVAL(data != NULL, 0);
191 
192     for (i = 0; i < data->num_my_parents; i++) {
193         if (data->my_parents[i] == win) {
194             return 1;
195         }
196     }
197     return 0;
198 }
199 
200 unsigned char
handle_key_press(event_t * ev)201 handle_key_press(event_t *ev)
202 {
203     XWMHints *wm_hints;
204 #ifdef COUNT_X_EVENTS
205     static unsigned long keypress_cnt = 0;
206 #endif
207 
208     PROF_INIT(handle_key_press);
209     D_EVENTS(("handle_key_press(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
210 #if UNUSED_BLOCK
211     REQUIRE_RVAL(XEVENT_IS_MYWIN(ev, &primary_data), 0);
212 #endif
213 
214     COUNT_EVENT(keypress_cnt);
215     if (!(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_NO_INPUT))) {
216         lookup_key(ev);
217     }
218     if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_URG_ALERT)) {
219         wm_hints = XGetWMHints(Xdisplay, TermWin.parent);
220         wm_hints->flags &= ~XUrgencyHint;
221         XSetWMHints(Xdisplay, TermWin.parent, wm_hints);
222         XFree(wm_hints);
223     }
224     PROF_DONE(handle_key_press);
225     PROF_TIME(handle_key_press);
226     return 1;
227 }
228 
229 unsigned char
handle_property_notify(event_t * ev)230 handle_property_notify(event_t *ev)
231 {
232     D_EVENTS(("handle_property_notify(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
233 
234 #ifdef PIXMAP_OFFSET
235     if (background_is_trans()) {
236         Window win;
237         Pixmap pmap;
238 
239         if ((ev->xany.window == TermWin.parent) || (ev->xany.window == Xroot)) {
240             D_EVENTS(("On %s.  prop (_WIN_WORKSPACE) == 0x%08x, ev->xproperty.atom == 0x%08x\n",
241                       ((ev->xany.window == Xroot) ? "the root window" : "TermWin.parent"), (int) props[PROP_DESKTOP],
242                       (int) ev->xproperty.atom));
243             if (ev->xproperty.atom == props[PROP_DESKTOP]) {
244                 win = get_desktop_window();
245                 if (win == (Window) 1) {
246                     /* The desktop window is unchanged.  Ignore this event. */
247                     return 1;
248                 }
249                 /* The desktop window has changed somehow. */
250                 if (desktop_window == None) {
251                     free_desktop_pixmap();
252                     FOREACH_IMAGE(if (image_mode_is(idx, MODE_TRANS)) {
253                                   image_set_mode(idx, MODE_IMAGE); image_allow_mode(idx, ALLOW_IMAGE);}
254                     );
255                     return 1;
256                 }
257                 pmap = get_desktop_pixmap();
258                 if (pmap == (Pixmap) 1) {
259                     /* Pixmap is unchanged */
260                     return 1;
261                 }
262                 redraw_images_by_mode(MODE_TRANS | MODE_VIEWPORT);
263                 return 1;
264             }
265         }
266         if (ev->xany.window == desktop_window) {
267             D_EVENTS(("On desktop_window [0x%08x].  prop (_XROOTPMAP_ID) == %d, ev->xproperty.atom == %d\n",
268                       (int) desktop_window, (int) props[PROP_TRANS_PIXMAP], (int) ev->xproperty.atom));
269             if (ev->xproperty.atom == props[PROP_TRANS_PIXMAP]) {
270                 pmap = get_desktop_pixmap();
271                 if (pmap == (Pixmap) 1) {
272                     /* Pixmap is unchanged */
273                     return 1;
274                 }
275                 redraw_images_by_mode(MODE_TRANS | MODE_VIEWPORT);
276                 return 1;
277             }
278         }
279     }
280 #endif
281     if ((ev->xany.window == Xroot) && (image_mode_any(MODE_AUTO))) {
282         D_EVENTS(("On the root window.  prop (ENLIGHTENMENT_COMMS) == %d, ev->xproperty.atom == %d\n", (int) props[PROP_ENL_COMMS],
283                   (int) ev->xproperty.atom));
284         if ((props[PROP_ENL_COMMS] != None) && (ev->xproperty.atom == props[PROP_ENL_COMMS])) {
285             if ((enl_ipc_get_win()) != None) {
286 #ifdef PIXMAP_SUPPORT
287                 redraw_images_by_mode(MODE_AUTO);
288 #endif
289             }
290         }
291     }
292     if (ev->xany.window == TermWin.vt) {
293         D_EVENTS(("PropertyNotify on term window for atom %d, state %d.  Selection atoms are %d and %d.\n", ev->xproperty.atom,
294                   ev->xproperty.state, props[PROP_SELECTION_DEST], props[PROP_SELECTION_INCR]));
295         if (ev->xproperty.atom == props[PROP_SELECTION_DEST] && ev->xproperty.state == PropertyNewValue) {
296             D_SELECT(("Fetching next part of incremental selection.\n"));
297             selection_fetch(ev->xproperty.window, ev->xproperty.atom, True);
298         }
299     }
300     return 1;
301 }
302 
303 unsigned char
handle_destroy_notify(event_t * ev)304 handle_destroy_notify(event_t *ev)
305 {
306     D_EVENTS(("handle_destroy_notify(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
307 
308     if (ev->xdestroywindow.window == ipc_win) {
309         D_EVENTS((" -> IPC window 0x%08x changed/destroyed.  Clearing ipc_win.\n", ipc_win));
310         XSelectInput(Xdisplay, ipc_win, None);
311         ipc_win = None;
312         (void) check_image_ipc(1);
313         return 1;
314     } else if (XEVENT_IS_MYWIN(ev, &primary_data)) {
315         /* One of our main windows was deleted.  Exit cleanly. */
316         D_EVENTS((" -> Primary window destroyed.  Terminating.\n"));
317         exit(0);
318         ASSERT_NOTREACHED_RVAL(1);
319     }
320     return 0;
321 }
322 
323 unsigned char
handle_client_message(event_t * ev)324 handle_client_message(event_t *ev)
325 {
326 
327     D_EVENTS(("handle_client_message(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
328 
329     REQUIRE_RVAL(XEVENT_IS_MYWIN(ev, &primary_data), 0);
330 
331     if (ev->xclient.format == 32 && ev->xclient.data.l[0] == (signed) props[PROP_DELETE_WINDOW])
332         exit(EXIT_SUCCESS);
333     if (ev->xclient.format == 8 && ev->xclient.message_type == props[PROP_ENL_MSG]) {
334         char buff[13];
335         unsigned char i;
336 
337         for (i = 0; i < 12; i++) {
338             buff[i] = ev->xclient.data.b[i + 8];
339         }
340         buff[12] = 0;
341         D_ENL(("Discarding unexpected Enlightenment IPC message:  \"%s\"\n", buff));
342         return 1;
343     }
344 #ifdef OFFIX_DND
345     /* OffiX Dnd (drag 'n' drop) protocol */
346     if (ev->xclient.message_type == props[PROP_DND_PROTOCOL]
347         && ((ev->xclient.data.l[0] == DndFile) || (ev->xclient.data.l[0] == DndDir) || (ev->xclient.data.l[0] == DndLink))) {
348         /* Get DnD data */
349         Atom ActualType;
350         int ActualFormat;
351         unsigned char *data;
352         unsigned long Size, RemainingBytes;
353 
354         XGetWindowProperty(Xdisplay, Xroot, props[PROP_DND_SELECTION], 0L, 1000000L, False, AnyPropertyType, &ActualType,
355                            &ActualFormat, &Size, &RemainingBytes, &data);
356         if (data) {
357             XChangeProperty(Xdisplay, Xroot, XA_CUT_BUFFER0, XA_STRING, 8, PropModeReplace, data, strlen(data));
358             selection_paste(XA_CUT_BUFFER0);
359             XSetInputFocus(Xdisplay, Xroot, RevertToNone, CurrentTime);
360             XFree(data);
361         }
362         return 1;
363     }
364 #endif /* OFFIX_DND */
365     if ((XInternAtom(Xdisplay, "_FVWM_COLORTUNER", True) == ev->xclient.message_type) && ev->xclient.send_event) {
366         if (ev->xclient.data.l[0] >= 0 && ev->xclient.data.l[0] <= 31) {
367             PixColors[(int) ev->xclient.data.l[0]] = ev->xclient.data.l[1];
368 
369             if ((int) ev->xclient.data.l[0] == bgColor) {
370                 XFocusChangeEvent fev;
371                 event_t *pfev;
372 
373                 fev.type = FocusIn;
374                 fev.send_event = 1;
375                 fev.display = Xdisplay;
376                 fev.window = ev->xany.window;
377                 pfev = (event_t *) & fev;
378                 handle_focus_in(pfev);
379                 redraw_image(image_bg);
380             }
381             scr_touch();
382             scr_refresh(refresh_type);
383         }
384         return 1;
385     }
386     return 1;
387 }
388 
389 unsigned char
handle_mapping_notify(event_t * ev)390 handle_mapping_notify(event_t *ev)
391 {
392 
393     D_EVENTS(("handle_mapping_notify(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
394 
395     XRefreshKeyboardMapping(&(ev->xmapping));
396     get_modifiers();
397     return 1;
398 }
399 
400 unsigned char
handle_visibility_notify(event_t * ev)401 handle_visibility_notify(event_t *ev)
402 {
403 
404     D_EVENTS(("handle_visibility_notify(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
405 
406     REQUIRE_RVAL(XEVENT_IS_MYWIN(ev, &primary_data), 0);
407     switch (ev->xvisibility.state) {
408         case VisibilityUnobscured:
409             D_X11(("Window completely visible.  Using FAST_REFRESH.\n"));
410             refresh_type = FAST_REFRESH;
411             break;
412         case VisibilityPartiallyObscured:
413             D_X11(("Window partially hidden.  Using SLOW_REFRESH.\n"));
414             refresh_type = SLOW_REFRESH;
415             break;
416         default:
417             D_X11(("Window completely hidden.  Using NO_REFRESH.\n"));
418             refresh_type = NO_REFRESH;
419             break;
420     }
421     return 1;
422 }
423 
424 unsigned char
handle_enter_notify(event_t * ev)425 handle_enter_notify(event_t *ev)
426 {
427 
428     D_EVENTS(("handle_enter_notify(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
429 
430     REQUIRE_RVAL(XEVENT_IS_MYWIN(ev, &primary_data), 0);
431 
432     if (ev->xany.window == TermWin.vt) {
433         if (images[image_bg].norm != images[image_bg].selected) {
434             images[image_bg].current = images[image_bg].selected;
435             redraw_image(image_bg);
436         }
437         return 1;
438     }
439     return 0;
440 }
441 
442 unsigned char
handle_leave_notify(event_t * ev)443 handle_leave_notify(event_t *ev)
444 {
445 
446     D_EVENTS(("handle_leave_notify(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
447 
448     REQUIRE_RVAL(XEVENT_IS_MYWIN(ev, &primary_data), 0);
449 
450     if (ev->xany.window == TermWin.vt) {
451         if (images[image_bg].norm != images[image_bg].selected) {
452             images[image_bg].current = images[image_bg].norm;
453             redraw_image(image_bg);
454         }
455         return 1;
456     }
457     return 0;
458 }
459 
460 unsigned char
handle_focus_in(event_t * ev)461 handle_focus_in(event_t *ev)
462 {
463     XWMHints *wm_hints;
464 
465     D_EVENTS(("handle_focus_in(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
466 
467     REQUIRE_RVAL(XEVENT_IS_MYWIN(ev, &primary_data), 0);
468 
469     if (!TermWin.focus) {
470         Window unused_root, child;
471         int unused_root_x, unused_root_y;
472         unsigned int unused_mask;
473 
474         TermWin.focus = 1;
475         XQueryPointer(Xdisplay, TermWin.parent, &unused_root, &child, &unused_root_x, &unused_root_y, &(ev->xbutton.x),
476                       &(ev->xbutton.y), &unused_mask);
477         if (child == TermWin.vt) {
478             if (images[image_bg].current != images[image_bg].selected) {
479                 images[image_bg].current = images[image_bg].selected;
480                 redraw_image(image_bg);
481             }
482         } else {
483             if (images[image_bg].current != images[image_bg].norm) {
484                 images[image_bg].current = images[image_bg].norm;
485                 redraw_image(image_bg);
486             }
487         }
488         if (BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_SCROLLBAR_POPUP)) {
489             map_scrollbar(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_SCROLLBAR));
490         } else {
491             scrollbar_set_focus(TermWin.focus);
492             scrollbar_draw(IMAGE_STATE_NORMAL, MODE_SOLID);
493         }
494         bbar_draw_all(IMAGE_STATE_NORMAL, MODE_SOLID);
495 #ifdef USE_XIM
496         if (xim_input_context)
497             XSetICFocus(xim_input_context);
498 #endif
499         if (BITFIELD_IS_SET(vt_options, VT_OPTIONS_URG_ALERT)) {
500             wm_hints = XGetWMHints(Xdisplay, TermWin.parent);
501             wm_hints->flags &= ~XUrgencyHint;
502             XSetWMHints(Xdisplay, TermWin.parent, wm_hints);
503             XFree(wm_hints);
504         }
505     }
506     return 1;
507 }
508 
509 unsigned char
handle_focus_out(event_t * ev)510 handle_focus_out(event_t *ev)
511 {
512 
513     D_EVENTS(("handle_focus_out(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
514 
515     REQUIRE_RVAL(XEVENT_IS_MYWIN(ev, &primary_data), 0);
516     if (TermWin.focus) {
517         TermWin.focus = 0;
518         if (images[image_bg].current != images[image_bg].disabled) {
519             images[image_bg].current = images[image_bg].disabled;
520             redraw_image(image_bg);
521         }
522         if (BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_SCROLLBAR_POPUP)) {
523             map_scrollbar(0);
524         } else {
525             scrollbar_set_focus(TermWin.focus);
526             scrollbar_draw(IMAGE_STATE_DISABLED, MODE_SOLID);
527         }
528         bbar_draw_all(IMAGE_STATE_DISABLED, MODE_SOLID);
529 #ifdef USE_XIM
530         if (xim_input_context)
531             XUnsetICFocus(xim_input_context);
532 #endif
533     }
534     return 1;
535 }
536 
537 unsigned char
handle_configure_notify(event_t * ev)538 handle_configure_notify(event_t *ev)
539 {
540     D_EVENTS(("handle_configure_notify(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
541 
542     REQUIRE_RVAL(XEVENT_IS_MYWIN(ev, &primary_data), 0);
543 
544     while (XCheckTypedWindowEvent(Xdisplay, ev->xany.window, ConfigureNotify, ev)) {
545         D_EVENTS(("New event:  Window 0x%08x, %dx%d at %d, %d\n", ev->xany.window, ev->xconfigure.width, ev->xconfigure.height,
546                   ev->xconfigure.x, ev->xconfigure.y));
547     }
548     if (ev->xany.window == TermWin.parent) {
549         int x = ev->xconfigure.x, y = ev->xconfigure.y;
550         unsigned int width = ev->xconfigure.width, height = ev->xconfigure.height;
551 
552         D_EVENTS((" -> TermWin.parent is %ldx%ld at (%d, %d).  Internal cache data shows %dx%d at (%hd, %hd).  send_event is %d\n",
553                   width, height, x, y, szHint.width, szHint.height, TermWin.x, TermWin.y, ev->xconfigure.send_event));
554         /* If the font change count is non-zero, this event is telling us we resized ourselves. */
555         if (font_chg > 0) {
556             font_chg--;
557         }
558         if ((width != (unsigned int) szHint.width) || (height != (unsigned int) szHint.height)) {
559             /* We were resized externally.  Handle the resize. */
560             D_EVENTS((" -> External resize detected.\n"));
561             handle_resize(width, height);
562 #ifdef USE_XIM
563             xim_set_status_position();
564 #endif
565             /* A resize requires the additional handling of a move */
566             if (ev->xconfigure.send_event) {
567                 handle_move(x, y);
568             }
569         } else if (((x != TermWin.x) || (y != TermWin.y)) && (ev->xconfigure.send_event)) {
570             /* There was an external move, but no resize.  Handle the move. */
571             D_EVENTS((" -> External move detected.\n"));
572             handle_move(x, y);
573         } else {
574             D_EVENTS((" -> Bogus ConfigureNotify detected, ignoring.\n"));
575         }
576     }
577     return 1;
578 }
579 
580 unsigned char
handle_selection_clear(event_t * ev)581 handle_selection_clear(event_t *ev)
582 {
583 
584     D_EVENTS(("handle_selection_clear(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
585 
586     selection_clear();
587     return 1;
588 }
589 
590 unsigned char
handle_selection_notify(event_t * ev)591 handle_selection_notify(event_t *ev)
592 {
593 
594     D_EVENTS(("handle_selection_notify(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
595 
596     selection_fetch(ev->xselection.requestor, ev->xselection.property, True);
597     return 1;
598 }
599 
600 unsigned char
handle_selection_request(event_t * ev)601 handle_selection_request(event_t *ev)
602 {
603 
604     D_EVENTS(("handle_selection_request(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
605 
606     selection_send(&(ev->xselectionrequest));
607     return 1;
608 }
609 
610 unsigned char
handle_expose(event_t * ev)611 handle_expose(event_t *ev)
612 {
613     PROF_INIT(handle_expose);
614     D_EVENTS(("handle_expose(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
615 
616     REQUIRE_RVAL(XEVENT_IS_MYWIN(ev, &primary_data), 0);
617     if (ev->xany.window == TermWin.vt
618 #ifdef PIXMAP_SUPPORT
619         && buffer_pixmap == None
620 #endif
621         ) {
622         if (refresh_type == NO_REFRESH) {
623             refresh_type = FAST_REFRESH;
624         }
625         scr_expose(ev->xexpose.x, ev->xexpose.y, ev->xexpose.width, ev->xexpose.height);
626     } else {
627 
628         XEvent unused_xevent;
629 
630         while (XCheckTypedWindowEvent(Xdisplay, ev->xany.window, Expose, &unused_xevent));
631         while (XCheckTypedWindowEvent(Xdisplay, ev->xany.window, GraphicsExpose, &unused_xevent));
632     }
633     PROF_DONE(handle_expose);
634     PROF_TIME(handle_expose);
635     return 1;
636 }
637 
638 unsigned char
handle_button_press(event_t * ev)639 handle_button_press(event_t *ev)
640 {
641 
642     D_EVENTS(("handle_button_press(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
643 
644     REQUIRE_RVAL(XEVENT_IS_MYWIN(ev, &primary_data), 0);
645 
646     if (action_dispatch(ev, 0)) {
647         button_state.ignore_release = 1;
648         return 1;
649     }
650 
651     button_state.bypass_keystate = (ev->xbutton.state & (Mod1Mask | ShiftMask));
652     button_state.report_mode = (button_state.bypass_keystate ? 0 : ((PrivateModes & PrivMode_mouse_report) ? 1 : 0));
653 
654     if (ev->xany.window == TermWin.vt) {
655         if (ev->xbutton.subwindow == None) {
656             if (button_state.report_mode) {
657                 if (PrivateModes & PrivMode_MouseX10) {
658                     /* no state info allowed */
659                     ev->xbutton.state = 0;
660                 }
661 #ifdef MOUSE_REPORT_DOUBLECLICK
662                 if (ev->xbutton.button == Button1) {
663                     if (ev->xbutton.time - button_state.button_press < MULTICLICK_TIME)
664                         button_state.clicks++;
665                     else
666                         button_state.clicks = 1;
667                 }
668 #else
669                 button_state.clicks = 1;
670 #endif /* MOUSE_REPORT_DOUBLECLICK */
671                 mouse_report(&(ev->xbutton));
672             } else {
673                 switch (ev->xbutton.button) {
674                     case Button1:
675                         if ((button_state.last_button_press == 1)
676                             && (ev->xbutton.time - button_state.button_press < MULTICLICK_TIME)) {
677                             button_state.clicks++;
678                         } else {
679                             button_state.clicks = 1;
680                         }
681                         selection_click(button_state.clicks, ev->xbutton.x, ev->xbutton.y);
682                         button_state.last_button_press = 1;
683                         break;
684 
685                     case Button3:
686                         if ((button_state.last_button_press == 3)
687                             && (ev->xbutton.time - button_state.button_press < MULTICLICK_TIME)) {
688                             selection_rotate(ev->xbutton.x, ev->xbutton.y);
689                         } else {
690                             selection_extend(ev->xbutton.x, ev->xbutton.y, 1);
691                         }
692                         button_state.last_button_press = 3;
693                         break;
694 #ifdef MOUSEWHEEL
695                         /* This section activates the following bindings:
696                          *
697                          * Mousewheel Up               -- Scroll up 1 page
698                          * Ctrl + Mousewheel Up        -- Scroll up 5 pages
699                          * Shift + Mousewheel Up       -- Scroll up 1 line
700                          * Alt + Mousewheel Up         -- Send PgUp to tty
701                          * Alt + Ctrl + Mousewheel Up  -- Send 5 PgUp's to tty
702                          * Alt + Shift + Mousewheel Up -- Send Up Arrow to tty
703                          *
704                          * Mousewheel Down               -- Scroll down 1 page
705                          * Ctrl + Mousewheel Down        -- Scroll down 5 pages
706                          * Shift + Mousewheel Down       -- Scroll down 1 line
707                          * Alt + Mousewheel Down         -- Send PgDn to tty
708                          * Alt + Ctrl + Mousewheel Down  -- Send 5 PgDn's to tty
709                          * Alt + Shift + Mousewheel Down -- Send Down Arrow to tty
710                          *
711                          * Note that the number of lines which constitute a "page" is equal to the number
712                          * of text rows in the terminal window.  The context lines are subtracted out *after*
713                          * the conversion is done.  In other words, scrolling 5 pages means scrolling
714                          * (5 * LINES_PER_PAGE) - CONTEXT_LINES
715                          *    _not_
716                          * (LINES_PER_PAGE - CONTEXT_LINES) * 5
717                          *
718                          * This is also true for the scroll() function in script.c.
719                          */
720                     case Button4:
721                         if (action_check_modifiers(MOD_CTRL, ev->xbutton.state)) {
722                             scr_page(UP, (TermWin.nrow * 5) - CONTEXT_LINES);
723                         } else if (action_check_modifiers(MOD_SHIFT, ev->xbutton.state)) {
724                             scr_page(UP, 1);
725                         } else if (action_check_modifiers(MOD_ALT, ev->xbutton.state)) {
726                             tt_write("\033[5~", 4);
727                         } else if (action_check_modifiers((MOD_ALT | MOD_SHIFT), ev->xbutton.state)) {
728                             tt_write("\033[A", 3);
729                         } else if (action_check_modifiers((MOD_ALT | MOD_CTRL), ev->xbutton.state)) {
730                             tt_write("\033[5~\033[5~\033[5~\033[5~\033[5~", 20);
731                         } else {
732                             scr_page(UP, TermWin.nrow - CONTEXT_LINES);
733                         }
734                         button_state.last_button_press = 4;
735                         break;
736                     case Button5:
737                         if (action_check_modifiers(MOD_CTRL, ev->xbutton.state)) {
738                             scr_page(DN, (TermWin.nrow * 5) - CONTEXT_LINES);
739                         } else if (action_check_modifiers(MOD_SHIFT, ev->xbutton.state)) {
740                             scr_page(DN, 1);
741                         } else if (action_check_modifiers(MOD_ALT, ev->xbutton.state)) {
742                             tt_write("\033[6~", 4);
743                         } else if (action_check_modifiers((MOD_ALT | MOD_SHIFT), ev->xbutton.state)) {
744                             tt_write("\033[B", 3);
745                         } else if (action_check_modifiers((MOD_ALT | MOD_CTRL), ev->xbutton.state)) {
746                             tt_write("\033[6~\033[6~\033[6~\033[6~\033[6~", 20);
747                         } else {
748                             scr_page(DN, TermWin.nrow - CONTEXT_LINES);
749                         }
750                         button_state.last_button_press = 5;
751                         break;
752 #endif
753                     default:
754                         break;
755                 }
756             }
757             button_state.button_press = ev->xbutton.time;
758             return (1);
759         }
760     }
761     return 0;
762 }
763 
764 unsigned char
handle_button_release(event_t * ev)765 handle_button_release(event_t *ev)
766 {
767     D_EVENTS(("handle_button_release(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
768 
769     if (button_state.ignore_release == 1) {
770         button_state.ignore_release = 0;
771         return 0;
772     }
773 
774     REQUIRE_RVAL(XEVENT_IS_MYWIN(ev, &primary_data), 0);
775     button_state.mouse_offset = 0;
776     button_state.report_mode = (button_state.bypass_keystate ? 0 : ((PrivateModes & PrivMode_mouse_report) ? 1 : 0));
777 
778     if (ev->xany.window == TermWin.vt) {
779         if (ev->xbutton.subwindow == None) {
780             if (button_state.report_mode) {
781                 switch (PrivateModes & PrivMode_mouse_report) {
782                     case PrivMode_MouseX10:
783                         break;
784 
785                     case PrivMode_MouseX11:
786                         ev->xbutton.state = button_state.bypass_keystate;
787                         ev->xbutton.button = AnyButton;
788                         mouse_report(&(ev->xbutton));
789                         break;
790                 }
791                 return (1);
792             }
793             /*
794              * dumb hack to compensate for the failure of click-and-drag
795              * when overriding mouse reporting
796              */
797             if ((PrivateModes & PrivMode_mouse_report) && (button_state.bypass_keystate) && (ev->xbutton.button == Button1)
798                 && (clickOnce())) {
799                 selection_extend(ev->xbutton.x, ev->xbutton.y, 0);
800             }
801             switch (ev->xbutton.button) {
802                 case Button1:
803                 case Button3:
804                     selection_make(ev->xbutton.time);
805                     break;
806 
807                 case Button2:
808                     selection_paste(XA_PRIMARY);
809                     break;
810                 default:
811                     break;
812             }
813         }
814     }
815     return 0;
816 }
817 
818 unsigned char
handle_motion_notify(event_t * ev)819 handle_motion_notify(event_t *ev)
820 {
821 #ifdef COUNT_X_EVENTS
822     static unsigned long motion_cnt = 0;
823 #endif
824 
825     PROF_INIT(handle_motion_notify);
826     D_EVENTS(("handle_motion_notify(ev [%8p] on window 0x%08x)\n", ev, ev->xany.window));
827 
828     COUNT_EVENT(motion_cnt);
829 
830     REQUIRE_RVAL(XEVENT_IS_MYWIN(ev, &primary_data), 0);
831 
832 #ifdef HAVE_TWIN
833     /* It's really silly that Twin uses mouse drag codes that are
834      * different from the ones that xterm uses.
835      */
836     if ((PrivateModes & PrivMode_mouse_report) && !(button_state.bypass_keystate)) {
837         twin_mouse_drag_report(&(ev->xbutton));
838         return 1;
839     }
840 #endif
841 
842     if (ev->xany.window == TermWin.vt) {
843         if (ev->xbutton.state & (Button1Mask | Button3Mask)) {
844             Window unused_root, unused_child;
845             int unused_root_x, unused_root_y;
846             unsigned int unused_mask;
847 
848             while (XCheckTypedWindowEvent(Xdisplay, TermWin.vt, MotionNotify, ev));
849             XQueryPointer(Xdisplay, TermWin.vt, &unused_root, &unused_child, &unused_root_x, &unused_root_y, &(ev->xbutton.x),
850                           &(ev->xbutton.y), &unused_mask);
851 #ifdef MOUSE_THRESHOLD
852             /* deal with a `jumpy' mouse */
853             if ((ev->xmotion.time - button_state.button_press) > MOUSE_THRESHOLD)
854 #endif
855                 selection_extend((ev->xbutton.x), (ev->xbutton.y), (ev->xbutton.state & Button3Mask));
856         }
857         PROF_DONE(handle_motion_notify);
858         PROF_TIME(handle_motion_notify);
859         return 1;
860     }
861     PROF_DONE(handle_motion_notify);
862     PROF_TIME(handle_motion_notify);
863     return 1;
864 }
865 
866 unsigned char
process_x_event(event_t * ev)867 process_x_event(event_t *ev)
868 {
869 #ifdef COUNT_X_EVENTS
870     static unsigned long event_cnt = 0;
871 #endif
872 
873     COUNT_EVENT(event_cnt);
874 #if 0
875     D_EVENTS(("process_x_event(ev [%8p] %s on window 0x%08x)\n", ev, event_type_to_name(ev->xany.type), ev->xany.window));
876 #endif
877     if (primary_data.handlers[ev->type]) {
878         return ((primary_data.handlers[ev->type]) (ev));
879     }
880     return (0);
881 }
882 
883 XErrorHandler
xerror_handler(Display * display,XErrorEvent * event)884 xerror_handler(Display * display, XErrorEvent * event)
885 {
886 
887     char err_string[2048];
888 
889     strcpy(err_string, "");
890     XGetErrorText(display, event->error_code, err_string, sizeof(err_string));
891     libast_print_error("XError in function %s, resource 0x%08x (request %d.%d):  %s (error %d)\n",
892                 request_code_to_name(event->request_code), (int) event->resourceid, event->request_code, event->minor_code,
893                 err_string, event->error_code);
894 #if DEBUG > DEBUG_X11
895     if (DEBUG_LEVEL >= DEBUG_X11) {
896         dump_stack_trace();
897     }
898 #endif
899     libast_print_error("Attempting to continue...\n");
900     return 0;
901 }
902