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