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