]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/kbd.c
perms
[plan9front.git] / sys / src / 9 / pc / kbd.c
1 /*
2  * keyboard input
3  */
4 #include        "u.h"
5 #include        "../port/lib.h"
6 #include        "mem.h"
7 #include        "dat.h"
8 #include        "fns.h"
9 #include        "io.h"
10 #include        "../port/error.h"
11
12 enum {
13         Data=           0x60,           /* data port */
14
15         Status=         0x64,           /* status port */
16          Inready=       0x01,           /*  input character ready */
17          Outbusy=       0x02,           /*  output busy */
18          Sysflag=       0x04,           /*  system flag */
19          Cmddata=       0x08,           /*  cmd==0, data==1 */
20          Inhibit=       0x10,           /*  keyboard/mouse inhibited */
21          Minready=      0x20,           /*  mouse character ready */
22          Rtimeout=      0x40,           /*  general timeout */
23          Parity=        0x80,
24
25         Cmd=            0x64,           /* command port (write only) */
26
27         Spec=           0xF800,         /* Unicode private space */
28         PF=             Spec|0x20,      /* num pad function key */
29         View=           Spec|0x00,      /* view (shift window up) */
30         KF=             0xF000,         /* function key (begin Unicode private space) */
31         Shift=          Spec|0x60,
32         Break=          Spec|0x61,
33         Ctrl=           Spec|0x62,
34         Latin=          Spec|0x63,
35         Caps=           Spec|0x64,
36         Num=            Spec|0x65,
37         Middle=         Spec|0x66,
38         Altgr=          Spec|0x67,
39         Kmouse=         Spec|0x100,
40         No=             0x00,           /* peter */
41
42         Home=           KF|13,
43         Up=             KF|14,
44         Pgup=           KF|15,
45         Print=          KF|16,
46         Left=           KF|17,
47         Right=          KF|18,
48         End=            KF|24,
49         Down=           View,
50         Pgdown=         KF|19,
51         Ins=            KF|20,
52         Del=            0x7F,
53         Scroll=         KF|21,
54
55         Nscan=  128,
56
57         Int=    0,                      /* kbscans indices */
58         Ext,
59         Nscans,
60 };
61
62 /*
63  * The codes at 0x79 and 0x7b are produced by the PFU Happy Hacking keyboard.
64  * A 'standard' keyboard doesn't produce anything above 0x58.
65  */
66 Rune kbtab[Nscan] = 
67 {
68 [0x00]  No,     0x1b,   '1',    '2',    '3',    '4',    '5',    '6',
69 [0x08]  '7',    '8',    '9',    '0',    '-',    '=',    '\b',   '\t',
70 [0x10]  'q',    'w',    'e',    'r',    't',    'y',    'u',    'i',
71 [0x18]  'o',    'p',    '[',    ']',    '\n',   Ctrl,   'a',    's',
72 [0x20]  'd',    'f',    'g',    'h',    'j',    'k',    'l',    ';',
73 [0x28]  '\'',   '`',    Shift,  '\\',   'z',    'x',    'c',    'v',
74 [0x30]  'b',    'n',    'm',    ',',    '.',    '/',    Shift,  '*',
75 [0x38]  Latin,  ' ',    Ctrl,   KF|1,   KF|2,   KF|3,   KF|4,   KF|5,
76 [0x40]  KF|6,   KF|7,   KF|8,   KF|9,   KF|10,  Num,    Scroll, '7',
77 [0x48]  '8',    '9',    '-',    '4',    '5',    '6',    '+',    '1',
78 [0x50]  '2',    '3',    '0',    '.',    No,     No,     No,     KF|11,
79 [0x58]  KF|12,  No,     No,     No,     No,     No,     No,     No,
80 [0x60]  No,     No,     No,     No,     No,     No,     No,     No,
81 [0x68]  No,     No,     No,     No,     No,     No,     No,     No,
82 [0x70]  No,     No,     No,     No,     No,     No,     No,     No,
83 [0x78]  No,     View,   No,     Up,     No,     No,     No,     No,
84 };
85
86 Rune kbtabshift[Nscan] =
87 {
88 [0x00]  No,     0x1b,   '!',    '@',    '#',    '$',    '%',    '^',
89 [0x08]  '&',    '*',    '(',    ')',    '_',    '+',    '\b',   '\t',
90 [0x10]  'Q',    'W',    'E',    'R',    'T',    'Y',    'U',    'I',
91 [0x18]  'O',    'P',    '{',    '}',    '\n',   Ctrl,   'A',    'S',
92 [0x20]  'D',    'F',    'G',    'H',    'J',    'K',    'L',    ':',
93 [0x28]  '"',    '~',    Shift,  '|',    'Z',    'X',    'C',    'V',
94 [0x30]  'B',    'N',    'M',    '<',    '>',    '?',    Shift,  '*',
95 [0x38]  Latin,  ' ',    Ctrl,   KF|1,   KF|2,   KF|3,   KF|4,   KF|5,
96 [0x40]  KF|6,   KF|7,   KF|8,   KF|9,   KF|10,  Num,    Scroll, '7',
97 [0x48]  '8',    '9',    '-',    '4',    '5',    '6',    '+',    '1',
98 [0x50]  '2',    '3',    '0',    '.',    No,     No,     No,     KF|11,
99 [0x58]  KF|12,  No,     No,     No,     No,     No,     No,     No,
100 [0x60]  No,     No,     No,     No,     No,     No,     No,     No,
101 [0x68]  No,     No,     No,     No,     No,     No,     No,     No,
102 [0x70]  No,     No,     No,     No,     No,     No,     No,     No,
103 [0x78]  No,     Up,     No,     Up,     No,     No,     No,     No,
104 };
105
106 Rune kbtabesc1[Nscan] =
107 {
108 [0x00]  No,     No,     No,     No,     No,     No,     No,     No,
109 [0x08]  No,     No,     No,     No,     No,     No,     No,     No,
110 [0x10]  No,     No,     No,     No,     No,     No,     No,     No,
111 [0x18]  No,     No,     No,     No,     '\n',   Ctrl,   No,     No,
112 [0x20]  No,     No,     No,     No,     No,     No,     No,     No,
113 [0x28]  No,     No,     Shift,  No,     No,     No,     No,     No,
114 [0x30]  No,     No,     No,     No,     No,     '/',    No,     Print,
115 [0x38]  Altgr,  No,     No,     No,     No,     No,     No,     No,
116 [0x40]  No,     No,     No,     No,     No,     No,     Break,  Home,
117 [0x48]  Up,     Pgup,   No,     Left,   No,     Right,  No,     End,
118 [0x50]  Down,   Pgdown, Ins,    Del,    No,     No,     No,     No,
119 [0x58]  No,     No,     No,     No,     No,     No,     No,     No,
120 [0x60]  No,     No,     No,     No,     No,     No,     No,     No,
121 [0x68]  No,     No,     No,     No,     No,     No,     No,     No,
122 [0x70]  No,     No,     No,     No,     No,     No,     No,     No,
123 [0x78]  No,     Up,     No,     No,     No,     No,     No,     No,
124 };
125
126 Rune kbtabaltgr[Nscan] =
127 {
128 [0x00]  No,     No,     No,     No,     No,     No,     No,     No,
129 [0x08]  No,     No,     No,     No,     No,     No,     No,     No,
130 [0x10]  No,     No,     No,     No,     No,     No,     No,     No,
131 [0x18]  No,     No,     No,     No,     '\n',   Ctrl,   No,     No,
132 [0x20]  No,     No,     No,     No,     No,     No,     No,     No,
133 [0x28]  No,     No,     Shift,  No,     No,     No,     No,     No,
134 [0x30]  No,     No,     No,     No,     No,     '/',    No,     Print,
135 [0x38]  Altgr,  No,     No,     No,     No,     No,     No,     No,
136 [0x40]  No,     No,     No,     No,     No,     No,     Break,  Home,
137 [0x48]  Up,     Pgup,   No,     Left,   No,     Right,  No,     End,
138 [0x50]  Down,   Pgdown, Ins,    Del,    No,     No,     No,     No,
139 [0x58]  No,     No,     No,     No,     No,     No,     No,     No,
140 [0x60]  No,     No,     No,     No,     No,     No,     No,     No,
141 [0x68]  No,     No,     No,     No,     No,     No,     No,     No,
142 [0x70]  No,     No,     No,     No,     No,     No,     No,     No,
143 [0x78]  No,     Up,     No,     No,     No,     No,     No,     No,
144 };
145
146 Rune kbtabctrl[Nscan] =
147 {
148 [0x00]  No,     '\e',    '\11',    '\12',    '\13',    '\14',    '\15',    '\16', 
149 [0x08]  '\17',    '\18',    '\19',    '\10',    '\r',    '\1d',    '\b',   '\t',
150 [0x10]  '\11',    '\17',    '\ 5',    '\12',    '\14',    '\19',    '\15',    '\t',
151 [0x18]  '\ f',    '\10',    '\e',    '\1d',    '\n',   Ctrl,   '\ 1',    '\13', 
152 [0x20]  '\ 4',    '\ 6',    '\a',    '\b',   '\n',   '\v',    '\f',    '\e', 
153 [0x28]  '\a',    No,     Shift,  '\1c',    '\1a',    '\18',    '\ 3',    '\16', 
154 [0x30]  '\ 2',    '\ e',    '\r',    '\f',    '\ e',    '\ f',    Shift,  '\n',
155 [0x38]  Latin,  No,     Ctrl,   '\ 5',    '\ 6',    '\a',    '\ 4',    '\ 5', 
156 [0x40]  '\ 6',    '\a',    '\f',    '\r',    '\ e',    '\ 5',    '\ 6',    '\17', 
157 [0x48]  '\18',    '\19',    '\r',    '\14',    '\15',    '\16',    '\v',    '\11', 
158 [0x50]  '\12',    '\13',    '\10',    '\ e',    No,     No,     No,     '\ f', 
159 [0x58]  '\f',    No,     No,     No,     No,     No,     No,     No,
160 [0x60]  No,     No,     No,     No,     No,     No,     No,     No,
161 [0x68]  No,     No,     No,     No,     No,     No,     No,     No,
162 [0x70]  No,     No,     No,     No,     No,     No,     No,     No,
163 [0x78]  No,     '\a',    No,     '\b',   No,     No,     No,     No,
164 };
165
166 enum
167 {
168         /* controller command byte */
169         Cscs1=          (1<<6),         /* scan code set 1 */
170         Cauxdis=        (1<<5),         /* mouse disable */
171         Ckbddis=        (1<<4),         /* kbd disable */
172         Csf=            (1<<2),         /* system flag */
173         Cauxint=        (1<<1),         /* mouse interrupt enable */
174         Ckbdint=        (1<<0),         /* kbd interrupt enable */
175 };
176
177 int mouseshifted;
178 void (*kbdmouse)(int);
179
180 static Lock i8042lock;
181 static uchar ccc;
182 static void (*auxputc)(int, int);
183 static int nokbd = 1;                   /* flag: no PS/2 keyboard */
184
185 /*
186  *  wait for output no longer busy
187  */
188 static int
189 outready(void)
190 {
191         int tries;
192
193         for(tries = 0; (inb(Status) & Outbusy); tries++){
194                 if(tries > 500)
195                         return -1;
196                 delay(2);
197         }
198         return 0;
199 }
200
201 /*
202  *  wait for input
203  */
204 static int
205 inready(void)
206 {
207         int tries;
208
209         for(tries = 0; !(inb(Status) & Inready); tries++){
210                 if(tries > 500)
211                         return -1;
212                 delay(2);
213         }
214         return 0;
215 }
216
217 /*
218  *  ask 8042 to reset the machine
219  */
220 void
221 i8042reset(void)
222 {
223         int i, x;
224
225         if(nokbd)
226                 return;
227
228         *((ushort*)KADDR(0x472)) = 0x1234;      /* BIOS warm-boot flag */
229
230         /*
231          *  newer reset the machine command
232          */
233         outready();
234         outb(Cmd, 0xFE);
235         outready();
236
237         /*
238          *  Pulse it by hand (old somewhat reliable)
239          */
240         x = 0xDF;
241         for(i = 0; i < 5; i++){
242                 x ^= 1;
243                 outready();
244                 outb(Cmd, 0xD1);
245                 outready();
246                 outb(Data, x);  /* toggle reset */
247                 delay(100);
248         }
249 }
250
251 int
252 i8042auxcmd(int cmd)
253 {
254         unsigned int c;
255         int tries;
256         static int badkbd;
257
258         if(badkbd)
259                 return -1;
260         c = 0;
261         tries = 0;
262
263         ilock(&i8042lock);
264         do{
265                 if(tries++ > 2)
266                         break;
267                 if(outready() < 0)
268                         break;
269                 outb(Cmd, 0xD4);
270                 if(outready() < 0)
271                         break;
272                 outb(Data, cmd);
273                 if(outready() < 0)
274                         break;
275                 if(inready() < 0)
276                         break;
277                 c = inb(Data);
278         } while(c == 0xFE || c == 0);
279         iunlock(&i8042lock);
280
281         if(c != 0xFA){
282                 print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd);
283                 badkbd = 1;     /* don't keep trying; there might not be one */
284                 return -1;
285         }
286         return 0;
287 }
288
289 int
290 i8042auxcmds(uchar *cmd, int ncmd)
291 {
292         int i;
293
294         ilock(&i8042lock);
295         for(i=0; i<ncmd; i++){
296                 if(outready() < 0)
297                         break;
298                 outb(Cmd, 0xD4);
299                 if(outready() < 0)
300                         break;
301                 outb(Data, cmd[i]);
302         }
303         iunlock(&i8042lock);
304         return i;
305 }
306
307 typedef struct Kbscan Kbscan;
308 struct Kbscan {
309         int     esc1;
310         int     esc2;
311         int     alt;
312         int     altgr;
313         int     caps;
314         int     ctl;
315         int     num;
316         int     shift;
317         int     collecting;
318         int     nk;
319         Rune    kc[5];
320         int     buttons;
321 };
322
323 Kbscan kbscans[Nscans]; /* kernel and external scan code state */
324
325 static int kdebug;
326
327 /*
328  * set keyboard's leds for lock states (scroll, numeric, caps).
329  *
330  * at least one keyboard (from Qtronics) also sets its numeric-lock
331  * behaviour to match the led state, though it has no numeric keypad,
332  * and some BIOSes bring the system up with numeric-lock set and no
333  * setting to change that.  this combination steals the keys for these
334  * characters and makes it impossible to generate them: uiolkjm&*().
335  * thus we'd like to be able to force the numeric-lock led (and behaviour) off.
336  */
337 static void
338 setleds(Kbscan *kbscan)
339 {
340         int leds;
341
342         if(nokbd || kbscan != &kbscans[Int])
343                 return;
344         leds = 0;
345         if(kbscan->num)
346                 leds |= 1<<1;
347         if(0 && kbscan->caps)           /* we don't implement caps lock */
348                 leds |= 1<<2;
349         ilock(&i8042lock);
350         outready();
351         outb(Data, 0xed);               /* `reset keyboard lock states' */
352         outready();
353         outb(Data, leds);
354         outready();
355         iunlock(&i8042lock);
356 }
357
358 /*
359  * Scan code processing
360  */
361 void
362 kbdputsc(int c, int external)
363 {
364         int i, keyup;
365         Kbscan *kbscan;
366
367         if(external)
368                 kbscan = &kbscans[Ext];
369         else
370                 kbscan = &kbscans[Int];
371
372         if(kdebug)
373                 print("sc %x ms %d\n", c, mouseshifted);
374         /*
375          *  e0's is the first of a 2 character sequence, e1 the first
376          *  of a 3 character sequence (on the safari)
377          */
378         if(c == 0xe0){
379                 kbscan->esc1 = 1;
380                 return;
381         } else if(c == 0xe1){
382                 kbscan->esc2 = 2;
383                 return;
384         }
385
386         keyup = c & 0x80;
387         c &= 0x7f;
388         if(c > sizeof kbtab){
389                 c |= keyup;
390                 if(c != 0xFF)   /* these come fairly often: CAPSLOCK U Y */
391                         print("unknown key %ux\n", c);
392                 return;
393         }
394
395         if(kbscan->esc1){
396                 c = kbtabesc1[c];
397                 kbscan->esc1 = 0;
398         } else if(kbscan->esc2){
399                 kbscan->esc2--;
400                 return;
401         } else if(kbscan->shift)
402                 c = kbtabshift[c];
403         else if(kbscan->altgr)
404                 c = kbtabaltgr[c];
405         else if(kbscan->ctl)
406                 c = kbtabctrl[c];
407         else
408                 c = kbtab[c];
409
410         if(kbscan->caps && c<='z' && c>='a')
411                 c += 'A' - 'a';
412
413         /*
414          *  keyup only important for shifts
415          */
416         if(keyup){
417                 switch(c){
418                 case Latin:
419                         kbscan->alt = 0;
420                         break;
421                 case Shift:
422                         kbscan->shift = 0;
423                         mouseshifted = 0;
424                         if(kdebug)
425                                 print("shiftclr\n");
426                         break;
427                 case Ctrl:
428                         kbscan->ctl = 0;
429                         break;
430                 case Altgr:
431                         kbscan->altgr = 0;
432                         break;
433                 case Kmouse|1:
434                 case Kmouse|2:
435                 case Kmouse|3:
436                 case Kmouse|4:
437                 case Kmouse|5:
438                         kbscan->buttons &= ~(1<<(c-Kmouse-1));
439                         if(kbdmouse)
440                                 kbdmouse(kbscan->buttons);
441                         break;
442                 }
443                 return;
444         }
445
446         /*
447          *  normal character
448          */
449         if(!(c & (Spec|KF))){
450                 if(kbscan->ctl)
451                         if(kbscan->alt && c == Del)
452                                 exit(0);
453                 if(!kbscan->collecting){
454                         kbdputc(kbdq, c);
455                         return;
456                 }
457                 kbscan->kc[kbscan->nk++] = c;
458                 c = latin1(kbscan->kc, kbscan->nk);
459                 if(c < -1)      /* need more keystrokes */
460                         return;
461                 if(c != -1)     /* valid sequence */
462                         kbdputc(kbdq, c);
463                 else    /* dump characters */
464                         for(i=0; i<kbscan->nk; i++)
465                                 kbdputc(kbdq, kbscan->kc[i]);
466                 kbscan->nk = 0;
467                 kbscan->collecting = 0;
468                 return;
469         } else {
470                 switch(c){
471                 case Caps:
472                         kbscan->caps ^= 1;
473                         return;
474                 case Num:
475                         kbscan->num ^= 1;
476                         if(!external)
477                                 setleds(kbscan);
478                         return;
479                 case Shift:
480                         kbscan->shift = 1;
481                         if(kdebug)
482                                 print("shift\n");
483                         mouseshifted = 1;
484                         return;
485                 case Latin:
486                         kbscan->alt = 1;
487                         /*
488                          * VMware and Qemu use Ctl-Alt as the key combination
489                          * to make the VM give up keyboard and mouse focus.
490                          * This has the unfortunate side effect that when you
491                          * come back into focus, Plan 9 thinks you want to type
492                          * a compose sequence (you just typed alt). 
493                          *
494                          * As a clumsy hack around this, we look for ctl-alt
495                          * and don't treat it as the start of a compose sequence.
496                          */
497                         if(!kbscan->ctl){
498                                 kbscan->collecting = 1;
499                                 kbscan->nk = 0;
500                         }
501                         return;
502                 case Ctrl:
503                         kbscan->ctl = 1;
504                         return;
505                 case Altgr:
506                         kbscan->altgr = 1;
507                         return;
508                 case Kmouse|1:
509                 case Kmouse|2:
510                 case Kmouse|3:
511                 case Kmouse|4:
512                 case Kmouse|5:
513                         kbscan->buttons |= 1<<(c-Kmouse-1);
514                         if(kbdmouse)
515                                 kbdmouse(kbscan->buttons);
516                         return;
517                 case KF|11:
518                         print("kbd debug on, F12 turns it off\n");
519                         kdebug = 1;
520                         break;
521                 case KF|12:
522                         kdebug = 0;
523                         break;
524                 }
525         }
526         kbdputc(kbdq, c);
527 }
528
529 /*
530  *  keyboard interrupt
531  */
532 static void
533 i8042intr(Ureg*, void*)
534 {
535         int s, c;
536
537         /*
538          *  get status
539          */
540         ilock(&i8042lock);
541         s = inb(Status);
542         if(!(s&Inready)){
543                 iunlock(&i8042lock);
544                 return;
545         }
546
547         /*
548          *  get the character
549          */
550         c = inb(Data);
551         iunlock(&i8042lock);
552
553         /*
554          *  if it's the aux port...
555          */
556         if(s & Minready){
557                 if(auxputc != nil)
558                         auxputc(c, kbscans[Int].shift);
559                 return;
560         }
561
562         kbdputsc(c, Int);
563 }
564
565 void
566 i8042auxenable(void (*putc)(int, int))
567 {
568         char *err = "i8042: aux init failed\n";
569
570         /* enable kbd/aux xfers and interrupts */
571         ccc &= ~Cauxdis;
572         ccc |= Cauxint;
573
574         ilock(&i8042lock);
575         if(outready() < 0)
576                 print(err);
577         outb(Cmd, 0x60);                        /* write control register */
578         if(outready() < 0)
579                 print(err);
580         outb(Data, ccc);
581         if(outready() < 0)
582                 print(err);
583         outb(Cmd, 0xA8);                        /* auxiliary device enable */
584         if(outready() < 0){
585                 iunlock(&i8042lock);
586                 return;
587         }
588         auxputc = putc;
589         intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux");
590         iunlock(&i8042lock);
591 }
592
593 static char *initfailed = "i8042: kbdinit failed\n";
594
595 static int
596 outbyte(int port, int c)
597 {
598         outb(port, c);
599         if(outready() < 0) {
600                 print(initfailed);
601                 return -1;
602         }
603         return 0;
604 }
605
606 void
607 kbdinit(void)
608 {
609         int c, try;
610
611         /* wait for a quiescent controller */
612         try = 1000;
613         while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
614                 if(c & Inready)
615                         inb(Data);
616                 delay(1);
617         }
618         if (try <= 0) {
619                 print(initfailed);
620                 return;
621         }
622
623         /* get current controller command byte */
624         outb(Cmd, 0x20);
625         if(inready() < 0){
626                 print("i8042: kbdinit can't read ccc\n");
627                 ccc = 0;
628         } else
629                 ccc = inb(Data);
630
631         /* enable kbd xfers and interrupts */
632         ccc &= ~Ckbddis;
633         ccc |= Csf | Ckbdint | Cscs1;
634         if(outready() < 0) {
635                 print(initfailed);
636                 return;
637         }
638
639         nokbd = 0;
640
641         /* disable mouse */
642         if (outbyte(Cmd, 0x60) < 0 || outbyte(Data, ccc) < 0)
643                 print("i8042: kbdinit mouse disable failed\n");
644 }
645
646 void
647 kbdenable(void)
648 {
649         kbdq = qopen(4*1024, 0, 0, 0);
650         if(kbdq == nil)
651                 panic("kbdinit");
652         qnoblock(kbdq, 1);
653
654         ioalloc(Data, 1, 0, "kbd");
655         ioalloc(Cmd, 1, 0, "kbd");
656
657         intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd");
658
659         kbscans[Int].num = 0;
660         setleds(&kbscans[Int]);
661 }
662
663 void
664 kbdputmap(ushort m, ushort scanc, Rune r)
665 {
666         if(scanc >= Nscan)
667                 error(Ebadarg);
668         switch(m) {
669         default:
670                 error(Ebadarg);
671         case 0:
672                 kbtab[scanc] = r;
673                 break;
674         case 1:
675                 kbtabshift[scanc] = r;
676                 break;
677         case 2:
678                 kbtabesc1[scanc] = r;
679                 break;
680         case 3:
681                 kbtabaltgr[scanc] = r;
682                 break;
683         case 4: 
684                 kbtabctrl[scanc] = r;
685                 break;
686         }
687 }
688
689 int
690 kbdgetmap(uint offset, int *t, int *sc, Rune *r)
691 {
692         if ((int)offset < 0)
693                 error(Ebadarg);
694         *t = offset/Nscan;
695         *sc = offset%Nscan;
696         switch(*t) {
697         default:
698                 return 0;
699         case 0:
700                 *r = kbtab[*sc];
701                 return 1;
702         case 1:
703                 *r = kbtabshift[*sc];
704                 return 1;
705         case 2:
706                 *r = kbtabesc1[*sc];
707                 return 1;
708         case 3:
709                 *r = kbtabaltgr[*sc];
710                 return 1;
711         case 4:
712                 *r = kbtabctrl[*sc];
713                 return 1;
714         }
715 }