1 /*
2 * input.cc
3 * User input (mouse and keyboard)
4 * AYM 1998-06-16
5 */
6
7
8 /*
9 This file is part of Yadex.
10
11 Yadex incorporates code from DEU 5.21 that was put in the public domain in
12 1994 by Rapha�l Quinet and Brendon Wyber.
13
14 The rest of Yadex is Copyright � 1997-2003 Andr� Majorel and others.
15
16 This program is free software; you can redistribute it and/or modify it under
17 the terms of the GNU General Public License as published by the Free Software
18 Foundation; either version 2 of the License, or (at your option) any later
19 version.
20
21 This program is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License along with
26 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
27 Place, Suite 330, Boston, MA 02111-1307, USA.
28 */
29
30
31 #include "yadex.h"
32 #ifdef Y_X11
33 #include <time.h> // nanosleep ()
34 #include <X11/Xlib.h>
35 #include <X11/Xutil.h> // XLookupString
36 #include <X11/keysym.h>
37 #include "gfx.h"
38
39
40 /* BGI keyboard input :
41 altkey (bios(2)) : 0x03 = shift
42 0x08 = alt
43 3bxx F1
44 3cxx F2
45 3dxx F3
46 3fxx F5
47 42xx F8
48 43xx F9
49 44xx F10
50 4bxx left
51 4dxx right
52 48xx up
53 50xx down
54 47xx home
55 4fxx end
56 49xx page up
57 51xx page down
58 52xx ins
59 53xx del
60 2exx alt-c
61 12xx alt-e
62 21xx alt-f
63 23xx alt-h
64 17xx alt-i
65 32xx alt-m
66 18xx alt-o
67 1fxx alt-s
68 */
69
70
71 /* This variable needs to be global because get_input_status() is called
72 from different places and some state needs to be saved (shift, ctrl,
73 alt...). I think there should be one instance of this type for each
74 window that can have focus. But one instance for all windows might be
75 enough. Multi-threading (one thread per edit window) is also an
76 issue. Have to think of it. */
77 input_status_t is;
78
79
80 /*
81 * init_input_status
82 * Initialize <is>. Must be called before using <is> or calling
83 * get_input_status().
84 */
init_input_status()85 void init_input_status ()
86 {
87 is.in_window = 0;
88 is.width = -1;
89 is.height = 0;
90 is.butl = 0;
91 is.butm = 0;
92 is.butr = 0;
93 is.shift = 0;
94 is.ctrl = 0;
95 is.alt = 0;
96 is.scroll_lock = 0;
97 }
98
99
100 /*
101 * Static table for get_input_status to convert keysyms to one
102 * of the YK_-something codes.
103 */
104 typedef struct
105 {
106 KeySym ks;
107 inpev_t key;
108 } key_info_t;
109 static const key_info_t key_info[] =
110 {
111 { XK_BackSpace, YK_BACKSPACE },
112 #ifdef XK_ISO_Left_Tab /* OpenServer 5.0 X11R5 doesn't have XK_ISO_Left_Tab */
113 { XK_ISO_Left_Tab, YK_BACKTAB },
114 #endif
115 { XK_Delete, YK_DEL, },
116 { XK_Down, YK_DOWN, },
117 { XK_End, YK_END, },
118 { XK_Escape, YK_ESC, },
119 { XK_F1, YK_F1, },
120 { XK_F2, YK_F2, },
121 { XK_F3, YK_F3, },
122 { XK_F4, YK_F4, },
123 { XK_F5, YK_F5, },
124 { XK_F6, YK_F6, },
125 { XK_F7, YK_F7, },
126 { XK_F8, YK_F8, },
127 { XK_F9, YK_F9, },
128 { XK_F10, YK_F10, },
129 { XK_Home, YK_HOME, },
130 { XK_Insert, YK_INS, },
131 { XK_Left, YK_LEFT, },
132 { XK_Linefeed, YK_RETURN, },
133 #ifdef XK_Page_Down /* HP-UX 10 doesn't have XK_Page_Down */
134 { XK_Page_Down, YK_PD, },
135 #endif
136 #ifdef XK_Page_Up /* HP-UX 10 doesn't have XK_Page_Up */
137 { XK_Page_Up, YK_PU, },
138 #endif
139 { XK_Return, YK_RETURN, },
140 { XK_Right, YK_RIGHT, },
141 { XK_Tab, YK_TAB, },
142 { XK_Up, YK_UP, },
143 };
144
145
146 /*
147 * get_input_status
148 * Get the next event and update <is> accordingly.
149 * If no event is available, waits for idle_sleep_ms ms
150 * and returns (it's used for the autoscroll feature).
151 */
get_input_status()152 void get_input_status ()
153 {
154 XEvent ev;
155
156 is.key = 0;
157
158 if (! dpy) /* Sanity check */
159 fatal_error ("get_input_status() called before XOpenDisplay()");
160 if (XPending (dpy) == 0)
161 {
162 // No event ? Wait for <idle_sleep_ms> ms before polling again.
163 #if defined Y_NANOSLEEP
164 struct timespec treq = { 0, 1000000l * idle_sleep_ms };
165 struct timespec trem;
166 nanosleep (&treq, &trem);
167 #elif defined Y_USLEEP
168 usleep (1000ul * idle_sleep_ms );
169 #else
170 ; // Neither nanosleep() no usleep() so be a CPU hog.
171 // FIXME: if autoscroll is turned off, could as well
172 // call XNextEvent and sleep for good.
173 #endif
174 return;
175 }
176
177 XNextEvent (dpy, &ev);
178
179 switch (ev.type)
180 {
181 /* Exposure */
182 case Expose :
183 #if 0
184 printf ("expose: send=%d w=%d x,y=%4d,%4d w,h=%4d,%4d c=%d\n",
185 (int) ev.xexpose.send_event,
186 (int) ev.xexpose.window,
187 (int) ev.xexpose.x,
188 (int) ev.xexpose.y,
189 (int) ev.xexpose.width,
190 (int) ev.xexpose.height,
191 (int) ev.xexpose.count);
192 #endif
193 if (ev.xexpose.window == win && ev.xexpose.count == 0)
194 is.key = YE_EXPOSE;
195 break;
196
197 /* Resize */
198 case ConfigureNotify :
199 #if 0
200 printf ("configure: x,y=%4d,%4d w,h=%4d,%4d\n",
201 (int) ev.xconfigure.x,
202 (int) ev.xconfigure.y,
203 (int) ev.xconfigure.width,
204 (int) ev.xconfigure.height);
205 #endif
206 if (is.width < 0 || ev.xconfigure.width != is.width
207 || ev.xconfigure.height != is.height)
208 {
209 is.key = YE_RESIZE;
210 is.width = ev.xconfigure.width;
211 is.height = ev.xconfigure.height;
212 }
213 break;
214
215 /* Mouse motion */
216 case EnterNotify :
217 is.key = YE_ENTER;
218 is.time = ev.xcrossing.time;
219 is.in_window = 1;
220 // Sanity
221 if (ev.xcrossing.x < 0) nf_bug ("xcrossing.x < 0"); // Paranoia
222 if (ev.xcrossing.y < 0) nf_bug ("xcrossing.y < 0"); // Paranoia
223 is.x = ev.xcrossing.x;
224 is.y = ev.xcrossing.y;
225 break;
226 case LeaveNotify :
227 is.key = YE_LEAVE;
228 is.time = ev.xcrossing.time;
229 is.in_window = 0; /* Should probably "release" buttons */
230 return;
231 break;
232 case MotionNotify :
233 is.key = YE_MOTION;
234 is.time = ev.xmotion.time;
235 if (ev.xmotion.x < 0) nf_bug ("xmotion.x < 0"); // Paranoia
236 if (ev.xmotion.y < 0) nf_bug ("xmotion.y < 0"); // Paranoia
237 is.x = ev.xmotion.x;
238 is.y = ev.xmotion.y;
239 #ifdef DEBUG
240 {
241 static bool first_time = true;
242 static int dxmin = INT_MAX;
243 static int dxmax = INT_MIN;
244 static int dymin = INT_MAX;
245 static int dymax = INT_MIN;
246 static int prevx = 0;
247 static int prevy = 0;
248 bool change = false;
249 if (! first_time)
250 {
251 int dx = prevx - ev.xmotion.x;
252 int dy = prevy - ev.xmotion.y;
253
254 if (dx < dxmin)
255 {
256 dxmin = dx;
257 change = true;
258 }
259 if (dx > dxmax)
260 {
261 dxmax = dx;
262 change = true;
263 }
264 if (dy < dymin)
265 {
266 dymin = dy;
267 change = true;
268 }
269 if (dy > dymax)
270 {
271 dymax = dy;
272 change = true;
273 }
274 }
275 prevx = ev.xmotion.x;
276 prevy = ev.xmotion.y;
277 first_time = false;
278 if (change)
279 printf ("Mouse: xmin=%d, xmax=%d, ymin=%d, ymax=%d\n",
280 dxmin, dxmax, dymin, dymax);
281 }
282 #endif
283 // DEBUG
284 break;
285
286 /* Mouse buttons */
287 case ButtonPress :
288 case ButtonRelease :
289 {
290 is.time = ev.xbutton.time;
291 int press = (ev.type == ButtonPress);
292 if (ev.xbutton.button == Button1)
293 {
294 is.key = press ? YE_BUTL_PRESS : YE_BUTL_RELEASE;
295 is.butl = press;
296 }
297 else if (ev.xbutton.button == Button2)
298 {
299 is.key = press ? YE_BUTM_PRESS : YE_BUTM_RELEASE;
300 is.butm = press;
301 }
302 else if (ev.xbutton.button == Button3)
303 {
304 is.key = press ? YE_BUTR_PRESS : YE_BUTR_RELEASE;
305 is.butr = press;
306 }
307 else if (ev.xbutton.button == Button4)
308 {
309 is.key = press ? YE_WHEEL_UP : 0;
310 }
311 else if (ev.xbutton.button == Button5)
312 {
313 is.key = press ? YE_WHEEL_DOWN : 0;
314 }
315 break;
316 }
317
318 /*
319 * Keyboard
320 * FIXME: need to handle NotifyKeymap event as well.
321 */
322 case KeyPress :
323 case KeyRelease :
324 {
325 KeySym ks;
326 int press;
327 unsigned char c;
328 int has_string;
329
330 is.time = ev.xkey.time;
331
332 press = (ev.type == KeyPress);
333
334 /* Convert keycode -> keysym + char. The keysym is useful for keys
335 such as cursor arrows that don't have an ASCII code. */
336 has_string = XLookupString ((XKeyEvent *) &ev, (char *) &c, 1, &ks, NULL);
337
338 /* The event says that Ctrl, Alt and Shift are not in the state we
339 thought they were. Don't panic ; it's just that we missed the
340 modifier key press/release event as it happened when we didn't
341 have focus. Adjust ourselves. */
342 if (!! (ev.xkey.state & ShiftMask) != is.shift)
343 is.shift = !! (ev.xkey.state & ShiftMask);
344 if (!! (ev.xkey.state & ControlMask) != is.ctrl)
345 is.ctrl = !! (ev.xkey.state & ControlMask);
346 if (!! (ev.xkey.state & Mod1Mask) != is.alt)
347 is.alt = !! (ev.xkey.state & Mod1Mask);
348
349 /* It's a modifier ? Remember its state */
350 switch (ks)
351 {
352 case XK_Shift_L:
353 case XK_Shift_R:
354 is.shift = press;
355 break;
356 case XK_Control_L:
357 case XK_Control_R:
358 is.ctrl = press;
359 break;
360 case XK_Alt_L:
361 case XK_Alt_R:
362 case XK_Meta_L:
363 case XK_Meta_R:
364 is.alt = press;
365 break;
366 }
367
368 /* Process ordinary keys */
369 if (press)
370 {
371 size_t n;
372 if (has_string)
373 is.key = c;
374 for (n = 0; n < sizeof key_info / sizeof *key_info; n++)
375 if (key_info[n].ks == ks)
376 {
377 is.key = key_info[n].key;
378 break;
379 }
380 if (is.key >= YK_ && is.key != YK_BACKTAB && is.shift)
381 is.key |= YK_SHIFT;
382 if (is.key >= YK_ && is.ctrl)
383 is.key |= YK_CTRL;
384 if (is.key != 0 && is.alt)
385 is.key |= YK_ALT;
386 }
387 #if 0
388 if (ev.type == KeyPress)
389 {
390 printf ("key=%04hXh", is.key);
391 if (is.key >= 0 && is.key <= UCHAR_MAX && isprint (is.key))
392 printf (" (%c)", (char) is.key);
393 putchar ('\n');
394 }
395 #endif
396 break;
397 }
398 } /* switch (ev.type) */
399 }
400
401 #endif /* #ifdef Y_X11 */
402
403
404 /*
405 * has_input_event
406 * Tells whether there are events in the input queue
407 */
has_input_event()408 int has_input_event ()
409 {
410 XEvent xev;
411 if (XCheckMaskEvent (dpy, 0xffffffff, &xev) == True)
412 {
413 XPutBackEvent (dpy, &xev);
414 return 1;
415 }
416 return 0;
417 }
418
419
420 /*
421 * have_key
422 * Return 0 if there is no key press in the input queue, <>0 else.
423 * This is a convenience function to replace bioskey(1).
424 */
have_key()425 int have_key ()
426 {
427 return 1; /* FIXME!! */
428 }
429
430
431 /*
432 * get_key
433 * Wait until the user presses a key and returns its code.
434 * This is a convenience function to replace bioskey(0).
435 */
get_key()436 int get_key ()
437 {
438 do
439 get_input_status ();
440 while (! event_is_key (is.key));
441 return is.key;
442 }
443
444
445 /*
446 * get_key_or_click
447 * Wait until the user presses a key or clicks the left button.
448 * In most cases, you should use this and not get_key().
449 */
get_key_or_click()450 void get_key_or_click ()
451 {
452 do
453 get_input_status ();
454 while (! event_is_key (is.key) && is.key != YE_BUTL_PRESS);
455
456 is.key = 0; // FIXME Shouldn't have to do that but EditorLoop() is broken
457 }
458
459
460 /*
461 * key_to_string
462 * Return a string corresponding to the key number k.
463 * Examples :
464 * - for k equal to 'a', returns "a",
465 * - for k equal to YK_ALT + 'a', returns "Alt-a".
466 * The string returned is guaranteed to have a length <= 50.
467 */
468 typedef struct
469 {
470 inpev_t key;
471 const char *string;
472 } key_string_t;
473 static const key_string_t key_string[] =
474 {
475 { ' ', "Space" },
476 { YK_BACKSPACE, "BS" },
477 { YK_BACKTAB, "Shift-Tab" },
478 { YK_DEL, "Del" },
479 { YK_DOWN, "Down" },
480 { YK_END, "End" },
481 { YK_ESC, "Esc" },
482 { YK_F1, "F1" },
483 { YK_F2, "F2" },
484 { YK_F3, "F3" },
485 { YK_F4, "F4" },
486 { YK_F5, "F5" },
487 { YK_F6, "F6" },
488 { YK_F7, "F7" },
489 { YK_F8, "F8" },
490 { YK_F9, "F9" },
491 { YK_F10, "F10" },
492 { YK_HOME, "Home" },
493 { YK_INS, "Ins" },
494 { YK_LEFT, "Left" },
495 { YK_PD, "Pgdn" },
496 { YK_PU, "Pgup" },
497 { YK_RETURN, "Return" },
498 { YK_RIGHT, "Right" },
499 { YK_TAB, "Tab" },
500 { YK_UP, "Up" },
501 };
502
key_to_string(inpev_t k)503 const char *key_to_string (inpev_t k)
504 {
505 static char buf[51];
506
507 // Is one of the special keys ?
508 size_t n;
509 const size_t nmax = sizeof key_string / sizeof *key_string;
510 for (n = 0; n < nmax; n++)
511 if (key_string[n].key == k)
512 break;
513
514 *buf = '\0';
515 if (k & YK_CTRL || (n == nmax && k <= 31))
516 {
517 al_saps (buf, "Ctrl-", sizeof buf - 1);
518 if (k & YK_CTRL)
519 k ^= YK_CTRL;
520 if (k <= 31)
521 k += 96; // Heavy ASCII-ism : 01h (^A) -> 61h ("a")
522 }
523 if (k & YK_ALT)
524 {
525 al_saps (buf, "Alt-", sizeof buf - 1);
526 k ^= YK_ALT;
527 }
528 if (k & YK_SHIFT)
529 {
530 al_saps (buf, "Shift-", sizeof buf - 1);
531 k ^= YK_SHIFT;
532 }
533
534 if (n == nmax)
535 if (k <= UCHAR_MAX && isprint (k))
536 al_sapc (buf, k, sizeof buf - 1);
537 else
538 {
539 al_sapc (buf, al_adigits[(k >> 12) & 15], sizeof buf - 1);
540 al_sapc (buf, al_adigits[(k >> 8) & 15], sizeof buf - 1);
541 al_sapc (buf, al_adigits[(k >> 4) & 15], sizeof buf - 1);
542 al_sapc (buf, al_adigits[(k >> 0) & 15], sizeof buf - 1);
543 }
544 else
545 al_saps (buf, key_string[n].string, sizeof buf - 1);
546
547 buf[sizeof buf - 1] = '\0'; /* Paranoia */
548 return buf;
549 }
550
551