FreeBASIC  0.91.0
io_multikey.c
Go to the documentation of this file.
1 /* Linux multikey function implementation */
2 
3 #include "../fb.h"
4 #include "../unix/fb_private_console.h"
5 #include <sys/ioctl.h>
6 #include <signal.h>
7 #include <linux/kd.h>
8 #include <linux/keyboard.h>
9 #include <linux/vt.h>
10 
11 #define KEY_BUFFER_SIZE 16
12 #define NUM_PAD_KEYS 17
13 
14 static int keyboard_init(void);
15 static void keyboard_exit(void);
16 
17 #ifndef DISABLE_X11
18 #include "../fb_private_hdynload.h"
19 #include "../unix/fb_private_scancodes_x11.h"
20 
21 typedef struct {
27 } X_FUNCS;
28 
29 static Display *display;
30 static FB_DYLIB xlib = NULL;
31 static X_FUNCS X = { NULL };
32 #endif
33 
34 static pid_t main_pid;
36 static unsigned char key_state[128];
37 static unsigned short key_buffer[KEY_BUFFER_SIZE], key_head, key_tail;
38 static int (*old_getch)(void);
39 static void (*gfx_save)(void);
40 static void (*gfx_restore)(void);
41 static void (*gfx_key_handler)(int, int, int, int);
42 
43 static const char pad_numlock_ascii[NUM_PAD_KEYS] = "0123456789+-*/\r,.";
44 static const char pad_ascii[NUM_PAD_KEYS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '+', '-', '*', '/', '\r', 0, 0 };
45 
46 static const unsigned char kernel_to_scancode[] = {
47  0, SC_ESCAPE, SC_1, SC_2,
48  SC_3, SC_4, SC_5, SC_6,
49  SC_7, SC_8, SC_9, SC_0,
51  SC_Q, SC_W, SC_E, SC_R,
52  SC_T, SC_Y, SC_U, SC_I,
55  SC_D, SC_F, SC_G, SC_H,
58  SC_Z, SC_X, SC_C, SC_V,
68  0, 0, SC_BACKSLASH, SC_F11,
69  SC_F12, 0, 0, 0,
70  0, 0, 0, 0,
72  SC_ALTGR, 0, SC_HOME, SC_UP,
75  0, 0, 0, 0,
76  0, 0, 0, 0,
77  0, 0, 0, 0,
79 };
80 
81 static int keyboard_console_getch(void)
82 {
83  int key = -1;
84 
85  BG_LOCK();
86 
87  if (key_head != key_tail) {
88  key = key_buffer[key_head];
89  key_head = (key_head + 1) & (KEY_BUFFER_SIZE - 1);
90  }
91 
92  BG_UNLOCK();
93 
94  return key;
95 }
96 
97 static void keyboard_console_handler(void)
98 {
99  unsigned char buffer[128], scancode;
100  int pressed, repeated, num_bytes, i, key, extended;
101  int vt, orig_vt;
102  struct kbentry entry;
103  struct vt_stat vt_state;
104 
105  num_bytes = read(key_fd, &buffer, sizeof(buffer));
106  if (num_bytes > 0) {
107  for (i = 0; i < num_bytes; i++) {
108  scancode = kernel_to_scancode[buffer[i] & 0x7F];
109  pressed = (buffer[i] & 0x80) ^ 0x80;
110  repeated = pressed && key_state[scancode];
111  key_state[scancode] = pressed;
112 
113  /* Since we took over keyboard control, we have to map our keypresses to ascii
114  * in order to report them in our own keyboard buffer */
115 
116  extended = 0;
117  switch (scancode) {
118  case SC_CAPSLOCK: if (pressed) key_leds ^= LED_CAP; break;
119  case SC_NUMLOCK: if (pressed) key_leds ^= LED_NUM; break;
120  case SC_SCROLLLOCK: if (pressed) key_leds ^= LED_SCR; break;
121  default:
122  extended = fb_hScancodeToExtendedKey( scancode );
123  break;
124  }
125 
126  /* Fill in kbentry struct for KDGKBENT query */
127  entry.kb_table = 0; /* modifier table */
128  if (key_state[SC_LSHIFT] || key_state[SC_RSHIFT])
129  entry.kb_table |= 0x1;
130  if (key_state[SC_ALTGR])
131  entry.kb_table |= 0x2;
132  if (key_state[SC_CONTROL])
133  entry.kb_table |= 0x4;
134  if (key_state[SC_ALT])
135  entry.kb_table |= 0x8;
136  entry.kb_index = scancode; /* keycode */
137  ioctl(key_fd, KDGKBENT, &entry);
138 
139  if (scancode == SC_BACKSPACE)
140  key = 8;
141  else if (entry.kb_value == K_NOSUCHMAP)
142  key = 0;
143  else {
144  key = KVAL(entry.kb_value);
145  switch (KTYP(entry.kb_value)) {
146  case KT_LETTER:
147  if (key_leds & LED_CAP)
148  key ^= 0x20;
149  break;
150  case KT_LATIN:
151  case KT_ASCII:
152  break;
153  case KT_PAD:
154  if (key < NUM_PAD_KEYS) {
155  if (key_leds & LED_NUM)
156  key = pad_numlock_ascii[key];
157  else
158  key = pad_ascii[key];
159  }
160  else
161  key = 0;
162  break;
163  case KT_SPEC:
164  if (scancode == SC_ENTER)
165  key = '\r';
166  break;
167  case KT_CONS:
168  vt = key + 1;
169  if( pressed && (ioctl(key_fd, VT_GETSTATE, &vt_state) >= 0) ) {
170  orig_vt = vt_state.v_active;
171  if (vt != orig_vt) {
172  if (__fb_con.gfx_exit) {
173  gfx_save();
174  ioctl(key_fd, KDSETMODE, KD_TEXT);
175  }
176  ioctl(key_fd, VT_ACTIVATE, vt);
177  ioctl(key_fd, VT_WAITACTIVE, vt);
178  while (ioctl(key_fd, VT_WAITACTIVE, orig_vt) < 0)
179  usleep(50000);
180  if (__fb_con.gfx_exit) {
181  ioctl(key_fd, KDSETMODE, KD_GRAPHICS);
182  gfx_restore();
183  }
184  memset(key_state, FALSE, 128);
185  } else {
186  key_state[scancode] = FALSE;
187  }
188  extended = 0;
189  }
190 
191  /* fallthrough */
192  default:
193  key = 0;
194  break;
195  }
196  }
197 
198  if( extended )
199  key = extended;
200 
201  if( pressed && key ) {
203  if (((key_tail + 1) & (KEY_BUFFER_SIZE - 1)) == key_head)
204  key_head = (key_head + 1) & (KEY_BUFFER_SIZE - 1);
205  key_tail = (key_tail + 1) & (KEY_BUFFER_SIZE - 1);
206  }
207 
208  if( gfx_key_handler )
209  gfx_key_handler( pressed, repeated, scancode, key );
210  }
211  }
212 
213  /* CTRL + C */
215  kill(main_pid, SIGINT);
216 }
217 
218 #ifndef DISABLE_X11
219 static void keyboard_x11_handler(void)
220 {
221  unsigned char keymap[32];
222  int i;
223 
224  if (!fb_hXTermHasFocus())
225  return;
226  X.QueryKeymap(display, keymap);
227  memset(key_state, FALSE, 128);
228  for (i = 0; i < 256; i++) {
229  if (keymap[i / 8] & (1 << (i & 0x7)))
231  }
232 }
233 #endif
234 
235 static int keyboard_init(void)
236 {
237 #ifndef DISABLE_X11
238  const char *funcs[] = {
239  "XOpenDisplay", "XCloseDisplay", "XQueryKeymap", "XDisplayKeycodes", "XGetKeyboardMapping", NULL
240  };
241 #endif
242  struct termios term;
243 
244  main_pid = getpid();
245  old_getch = __fb_con.keyboard_getch;
246 
247  if(__fb_con.inited == INIT_CONSOLE) {
248  key_fd = dup(__fb_con.h_in);
249 
250  term.c_iflag = 0;
251  term.c_cflag = CS8;
252  term.c_lflag = 0;
253  term.c_cc[VMIN] = 0;
254  term.c_cc[VTIME] = 0;
255 
256  if ((ioctl(key_fd, KDGKBMODE, &key_old_mode) < 0) ||
257  (tcsetattr(key_fd, TCSANOW, &term) < 0) ||
258  (ioctl(key_fd, KDSKBMODE, K_MEDIUMRAW) < 0)) {
259  close(key_fd);
260  return -1;
261  }
262  __fb_con.keyboard_handler = keyboard_console_handler;
263  __fb_con.keyboard_getch = keyboard_console_getch;
264  key_head = key_tail = 0;
265  ioctl(key_fd, KDGETLED, &key_leds);
266  }
267 
268 #ifndef DISABLE_X11
269  else {
270  xlib = fb_hDynLoad("libX11.so", funcs, (void **)&X);
271  if (!xlib)
272  return -1;
273 
274  display = X.OpenDisplay(NULL);
275  if (!display)
276  return -1;
277 
279 
281  __fb_con.keyboard_handler = keyboard_x11_handler;
282  }
283 #endif
284 
285  __fb_con.keyboard_init = keyboard_init;
286  __fb_con.keyboard_exit = keyboard_exit;
287 
288  return 0;
289 }
290 
291 static void keyboard_exit(void)
292 {
293  if (__fb_con.inited == INIT_CONSOLE) {
294  ioctl(key_fd, KDSKBMODE, key_old_mode);
295  close(key_fd);
296  key_fd = -1;
297  }
298 #ifndef DISABLE_X11
299  else if (__fb_con.inited == INIT_X11) {
303  }
304 #endif
305  __fb_con.keyboard_getch = old_getch;
306  __fb_con.keyboard_handler = NULL;
307  __fb_con.keyboard_exit = NULL;
308 }
309 
310 int fb_ConsoleMultikey(int scancode)
311 {
312  int res;
313 
314  if (!__fb_con.inited)
315  return FB_FALSE;
316 
317  BG_LOCK();
318 
320 
321  if ((!__fb_con.keyboard_handler) && (!keyboard_init())) {
322  /* Let the handler execute at least once to fill in states */
323  BG_UNLOCK();
324  usleep(50000);
325  BG_LOCK();
326  }
327 
328  res = key_state[scancode & 0x7F] ? FB_TRUE : FB_FALSE;
329 
330  BG_UNLOCK();
331 
332  return res;
333 }
334 
336  (
337  void (*gfx_exit)(void),
338  void (*save)(void),
339  void (*restore)(void),
340  void (*key_handler)(int, int, int, int)
341  )
342 {
343  BG_LOCK();
344 
346 
347  __fb_con.gfx_exit = gfx_exit;
348  if (gfx_exit) {
354  gfx_save = save;
355  gfx_restore = restore;
356  gfx_key_handler = key_handler;
357  if (keyboard_init()) {
358  BG_UNLOCK();
359  return -1;
360  }
361  ioctl(key_fd, KDSETMODE, KD_GRAPHICS);
362  } else {
363  if (key_fd >= 0) {
364  ioctl(key_fd, KDSETMODE, KD_TEXT);
365  keyboard_exit();
366  fb_hTermOut(SEQ_EXIT_GFX_MODE, 0, 0);
367  }
368  }
369 
370  BG_UNLOCK();
371 
372  return 0;
373 }