Add copyright notice
[forms.git] / src / F_Linux_Keyboard.C
blobff041270396b4b98e365929774177e9b0952cb88
2  /*
3   *   Copyright (C) 2007, Harbour, All rights reserved.
4   */
6 #include <F_Linux_Keyboard.H>
7 #include <F_Log.H>
8 #include <fcntl.h>
9 #include <termios.h>
10 #include <unistd.h>
11 #include <sys/time.h>
12 #include <linux/kd.h>
13 #include <linux/keyboard.h>
14 #include <sys/ioctl.h>
15 #include <errno.h>
17 using namespace F;
19 static int tty_fd = -1;
20 const int F_STDIN_FILENO = 0;
21 static termios old_attr;
22 static sighandler_t old_sigio;
23 F_Linux_Keyboard *F_Linux_Keyboard::this_;
25 static int map_vt100_control_kode(unsigned char kode, unsigned int mods)
27  switch (kode) {
28    case 0x11:
29      return 'q'; // ctrl+q
30    case 0x17:
31      return 'w'; // ctrl+w
32    case 0x5:
33      return 'e'; // ctrl+e
34    case 0x12:
35      return 'r'; // ctrl+r
36    case 0x14:
37      return 't'; // ctrl+t
38    case 0x19:
39      return 'y'; // ctrl+y
40    case 0x15:
41      return 'u'; // ctrl+u
42    case 0xf:
43      return 'o'; // ctrl+o
44    case 0x10:
45      return 'p'; // ctrl+p
46    case 0x1:
47      return 'a'; // ctrl+a
48    case 0x13:
49      return 's'; // ctrl+s
50    case 0x4:
51      return 'd'; // ctrl+d
52    case 0x6:
53      return 'f'; // ctrl+f
54    case 0xb:
55      return 'k'; // ctrl+k
56    case 0xc:
57      return 'l'; // ctrl+l
58    case 0x18:
59      return 'x'; // ctrl+x
60    case 0x16:
61      return 'v'; // ctrl+v
62    case 0x2:
63      return 'b'; // ctrl+b
64    case 0xe:
65      return 'n'; // ctrl+n
66    case 0x1d:
67      return ']'; // ctrl+]
68    case 0x0:
69      return '~'; // ctrl+~
70    case 0x1e:
71      return '6'; // ctrl+6
72    case 0x1f:
73      return '-'; // ctrl+7, ctrl+-
74    case 0x7:
75      return F_Beep;
76    case 0x8:
77    case 0x7f:
78      return F_BackSpace;
79    case 0x9:
80      return F_Tab;
81    case 0x1b:
82      return F_Escape;
83    case 0x0d:
84    case 0x0a:
85      return F_Enter;
86    default:
87      break;
88  }
89   return 0;
92 static int map_vt100_5_kode(unsigned char *key_seq, unsigned int mods)
94   if ((key_seq[1] == '[') && (key_seq[4] == '~')) {
95     switch (key_seq[3]) {
96       case '7':
97         return F_F6;
98       case '8':
99         return F_F7;
100       case '9':
101         return F_F8;
102       case '0':
103         return F_F9;
104       case '1':
105         return F_F10;
106       case '3':
107         return F_F11;
108       case '4':
109         return F_F12;
110       case '5':
111         if (mods & F_SHIFT)
112           return F_F3;
113       case '6':
114         if (mods & F_SHIFT)
115           return F_F4;
116       case '2':
117         if (mods & F_SHIFT)
118           return F_F8;
119       default:
120         break;
121     }
122   }
123   return 0;
126 static int map_vt100_4_kode(unsigned char *key_seq, unsigned int mods)
128   if ((key_seq[1] == key_seq[2]) && (key_seq[1] == '[')) {
129     switch (key_seq[3]) {
130       case 'A':
131         return F_F1;
132       case 'B':
133         return F_F2;
134       case 'C':
135         return F_F3;
136       case 'D':
137         return F_F4;
138       case 'E':
139         return F_F5;
140       default:
141         break;
142     }
143   }
144   if ((key_seq[1] == '[') && (key_seq[3] == '~')) {
145     switch (key_seq[2]) {
146       case '1':
147         return F_Home;
148       case '2':
149         return F_Insert;
150       case '3':
151         return F_Delete;
152       case '4':
153         return F_End;
154       case '5':
155         return F_Page_Up;
156       case '6':
157         return F_Page_Down;
158       default:
159         break;
160     }
161   }
162   return 0;
165 static int map_vt100_3_kode(unsigned char *key_seq, unsigned int mods)
167   if (key_seq[1] == '[') {
168     switch (key_seq[2]) {
169       case 'A':
170         return F_Up;
171       case 'B':
172         return F_Down;
173       case 'C':
174         return F_Right;
175       case 'D':
176         return F_Left;
177       case 'G':
178         return F_Center;
179       default:
180         break;
181     }
182   }
183   return 0;
186 // kode => keyboard code
188 // TODO: maybe load terminfo database and map against it ?
191 static bool is_printable(unsigned char c)
193  if ((c > 31) && (c != 0x7f))
194    return true;
195  return false;
198 static int map_vt100_kode(unsigned char *key_seq, unsigned int mods, int len)
200  int key = 0;
201  if (len > 5) { // now we do not know anything about such long seqs
202    debug("Bad vt100 kode len - %d", len);
203    return 0;
205  switch (len) {
206    case 1:
207      // control codes
208      if (is_printable(key_seq[0]))
209        return key_seq[0];
210      if ((key = map_vt100_control_kode(key_seq[0], mods)))
211        return key;
212      break;
213    case 2: // alt + key ?
214      if (is_printable(key_seq[1]))
215        return key_seq[1];
216      if ((key = map_vt100_control_kode(key_seq[1], mods)))
217        return key;
218      break;
219    case 3:
220      if ((key = map_vt100_3_kode(key_seq, mods)))
221        return key;
222      break;
223    case 4:
224      if ((key = map_vt100_4_kode(key_seq, mods)))
225        return key;
226      break;
227    case 5:
228      if ((key = map_vt100_5_kode(key_seq, mods)))
229        return key;
230      break;
231    default:
232      break;
234   debug("Unknown keycode seq len %d (mods - 0x%x):", len, mods);
235   for (int i = 0; i < len; i++)
236     debug("linux_kbd: seq[%d] = 0x%x \'%c\'", i, key_seq[i],
237       (key_seq[i] > 31) ? key_seq[i] : ' ');
238  return 0;
241 #define F_MAX_KEY_SEQ   16
243 void F_Linux_Keyboard::kbd_handler(void)
245  unsigned char key_seq[F_MAX_KEY_SEQ];
246  int res = read(tty_fd, key_seq, F_MAX_KEY_SEQ);
247  if (res < 0) {
248   log("kbd_handler", FATAL_LEVEL, "tty_fd read error - %d", res);
249   return;
251  int key, shifts_state = 6;
252  unsigned char locks_state;
253  if (ioctl(tty_fd, TIOCLINUX, &shifts_state) < 0)
254    log("kbd_handler", FATAL_LEVEL, "ioctl: %s", strerror(errno));
255  if (ioctl(tty_fd, KDGKBLED, &locks_state) < 0)
256    log("kbd_handler", FATAL_LEVEL, "ioctl: %s", strerror(errno));
257  F_Event_t ev;
258  // fill event
259  ev.dev = F_KEYBOARD;
260  ev.type = F_KEY_PRESS;
261  ev.delay = 0;
262  // map shifts and locks states
263  // TODO: get the ALTGR lock state here too
264  ev.kbd.modifiers  = (locks_state & LED_SCR) ? F_SCROLL_LOCK : 0;
265  ev.kbd.modifiers |= (locks_state & LED_NUM) ? F_NUM_LOCK : 0;
266  ev.kbd.modifiers |= (locks_state & LED_CAP) ? F_CAPS_LOCK : 0;
267  ev.kbd.modifiers |= (shifts_state & (1 << KG_SHIFT)) ? F_SHIFT : 0;
268  ev.kbd.modifiers |= (shifts_state & (1 << KG_CTRL)) ? F_CTRL : 0;
269  ev.kbd.modifiers |= (shifts_state & (1 << KG_ALT)) ? F_ALT : 0;
270  ev.kbd.modifiers |= (shifts_state & (1 << KG_ALTGR)) ? F_META : 0;
271  // check for normal input or selection
272  bool is_pr = true;
273  for (int i = 0; i < res; i++) {
274    if (!is_printable(key_seq[i]))
275      is_pr = false;
277  if (is_pr) {
278    for (int i = 0; i < res; i++) {
279      ev.kbd.key = key_seq[i];
280      this_->add_event(ev);
281   }
282    return;
283  } else if (!(key = map_vt100_kode(key_seq, ev.kbd.modifiers, res)))
284            return;
285  ev.kbd.key = key;
286  this_->add_event(ev);
289 F_Linux_Keyboard::F_Linux_Keyboard()
291  this_ = this;
292  // open tty/stdin
293  tty_fd = open(ttyname(F_STDIN_FILENO), O_RDONLY);
294  if (tty_fd < 0)
295   tty_fd = F_STDIN_FILENO;
296  // test if kbd is in K_XLATE mode
297  int arg;
298  if (ioctl(tty_fd, KDGKBMODE, &arg) < 0) {
299    log("linux_keyboard", FATAL_LEVEL, "ioctl: %s", strerror(errno));
300    exit(-1);
302  if (arg != K_XLATE) {
303    log("linux_keyboard", FATAL_LEVEL,
304      "Sorry, only XLATE keyboard mode is supported");
305    exit(-1);
307  // tune fd
308  if (fcntl(tty_fd, F_SETFL,
309    fcntl(tty_fd, F_GETFL) | (O_NONBLOCK | O_ASYNC)) < 0) {
310      log("linux_keyboard", FATAL_LEVEL, "Can't set mode for your tty, bye.");
311      exit(-1);
313  stored_ = false;
314  mode_set();
315  old_sigio = signal(SIGIO, (sighandler_t) F_Linux_Keyboard::kbd_handler); 
316  log("linux_kbd", CHAT_LEVEL, "Found and enabled.");
317  good_ = true;
318  enabled_ = true;
321 void F_Linux_Keyboard::mode_set()
323  if (!stored_) {
324   tcgetattr(tty_fd, &old_attr);
325   termios new_attr = old_attr;
326   new_attr.c_lflag &= ~(ICANON | ECHO);
327   new_attr.c_cc[VMIN] = 1;
328   new_attr.c_cc[VTIME] = 0;
329   new_attr.c_iflag &= ~(INPCK | ISTRIP | IXON);
330   new_attr.c_oflag |= OPOST | ONLCR;
331   tcsetattr(tty_fd, TCSANOW, &new_attr);
332   stored_ = true;
336 void F_Linux_Keyboard::mode_restore()
338  if (stored_) {
339    tcsetattr(tty_fd, TCSANOW, &old_attr);
340    stored_ = false;
344 F_Linux_Keyboard::~F_Linux_Keyboard()
346  mode_restore();
347  close(tty_fd);
348  tty_fd = -1;
349  signal(SIGIO, old_sigio);