sunKbd.c revision e83940e6
1/* $Xorg: sunKbd.c,v 1.3 2000/08/17 19:48:30 cpqbld Exp $ */
2/*-
3 * Copyright 1987 by the Regents of the University of California
4 *
5 * Permission to use, copy, modify, and distribute this
6 * software and its documentation for any purpose and without
7 * fee is hereby granted, provided that the above copyright
8 * notice appear in all copies.  The University of California
9 * makes no representations about the suitability of this
10 * software for any purpose.  It is provided "as is" without
11 * express or implied warranty.
12 */
13
14/************************************************************
15Copyright 1987 by Sun Microsystems, Inc. Mountain View, CA.
16
17                    All Rights Reserved
18
19Permission  to  use,  copy,  modify,  and  distribute   this
20software  and  its documentation for any purpose and without
21fee is hereby granted, provided that the above copyright no-
22tice  appear  in all copies and that both that copyright no-
23tice and this permission notice appear in  supporting  docu-
24mentation,  and  that the names of Sun or The Open Group
25not be used in advertising or publicity pertaining to
26distribution  of  the software  without specific prior
27written permission. Sun and The Open Group make no
28representations about the suitability of this software for
29any purpose. It is provided "as is" without any express or
30implied warranty.
31
32SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO  THIS  SOFTWARE,
33INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
34NESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SUN BE  LI-
35ABLE  FOR  ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
36ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,  DATA  OR
37PROFITS,  WHETHER  IN  AN  ACTION OF CONTRACT, NEGLIGENCE OR
38OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
39THE USE OR PERFORMANCE OF THIS SOFTWARE.
40
41********************************************************/
42/* $XFree86: xc/programs/Xserver/hw/sun/sunKbd.c,v 1.9 2003/11/17 22:20:36 dawes Exp $ */
43
44#define NEED_EVENTS
45#include "sun.h"
46#include <X11/keysym.h>
47#include <X11/Sunkeysym.h>
48#include "mi.h"
49
50#include <X11/extensions/XKB.h>
51#include "xkbsrv.h"
52#include "xkbstr.h"
53
54#ifdef __sun
55#define SUN_LED_MASK	0x0f
56#else
57#define SUN_LED_MASK	0x07
58#endif
59#define MIN_KEYCODE	7	/* necessary to avoid the mouse buttons */
60#define MAX_KEYCODE	255	/* limited by the protocol */
61#define NUM_KEYCODES	(MAX_KEYCODE - MIN_KEYCODE + 1)
62#ifndef KB_SUN4
63#define KB_SUN4		4
64#endif
65
66#define Meta_Mask	Mod1Mask
67#define Mode_switch_Mask Mod2Mask
68#define Alt_Mask	Mod3Mask
69#define Num_Lock_Mask	Mod4Mask
70#define ScrollLockMask	Mod5Mask
71
72#define tvminus(tv, tv1, tv2)   /* tv = tv1 - tv2 */ \
73		if ((tv1).tv_usec < (tv2).tv_usec) { \
74		    (tv1).tv_usec += 1000000; \
75		    (tv1).tv_sec -= 1; \
76		} \
77		(tv).tv_usec = (tv1).tv_usec - (tv2).tv_usec; \
78		(tv).tv_sec = (tv1).tv_sec - (tv2).tv_sec;
79
80#define tvplus(tv, tv1, tv2)    /* tv = tv1 + tv2 */ \
81		(tv).tv_sec = (tv1).tv_sec + (tv2).tv_sec; \
82		(tv).tv_usec = (tv1).tv_usec + (tv2).tv_usec; \
83		if ((tv).tv_usec > 1000000) { \
84		    (tv).tv_usec -= 1000000; \
85		    (tv).tv_sec += 1; \
86		}
87
88/*
89 * Data private to any sun keyboard.
90 */
91typedef struct {
92    int		fd;
93    int		type;		/* Type of keyboard */
94    int		layout;		/* The layout of the keyboard */
95    int		click;		/* kbd click save state */
96    Leds	leds;		/* last known LED state */
97    KeySymsRec  keysym;		/* working keysym */
98    Firm_event	evbuf[SUN_MAXEVENTS];	/* Buffer for Firm_events */
99} sunKbdPrivRec, *sunKbdPrivPtr;
100
101static void sunKbdEvents(int, int, void *);
102static void sunKbdWait(void);
103static void sunInitModMap(const KeySymsRec *, CARD8 *);
104static int sunKbdGetEvents(DeviceIntPtr);
105static void sunKbdEnqueueEvent(DeviceIntPtr, Firm_event *);
106static void SwapLKeys(KeySymsRec *);
107static void SetLights(KeybdCtrl *, int);
108static KeyCode LookupKeyCode(KeySym, XkbDescPtr, KeySymsPtr);
109static void pseudoKey(DeviceIntPtr, Bool, KeyCode);
110static void DoLEDs(DeviceIntPtr, KeybdCtrl *, sunKbdPrivPtr);
111static int getKbdType(int);
112static int sunChangeKbdTranslation(int, Bool);
113
114DeviceIntPtr	sunKeyboardDevice = NULL;
115
116static void
117sunKbdEvents(int fd, int ready, void *data)
118{
119    int i, numEvents;
120    DeviceIntPtr device = (DeviceIntPtr)data;
121    DevicePtr pKeyboard = &device->public;
122    sunKbdPrivPtr pPriv = pKeyboard->devicePrivate;
123
124    input_lock();
125
126    do {
127	numEvents = sunKbdGetEvents(device);
128	for (i = 0; i < numEvents; i++) {
129	    sunKbdEnqueueEvent(device, &pPriv->evbuf[i]);
130	}
131    } while (numEvents == SUN_MAXEVENTS);
132
133    input_unlock();
134}
135
136static void
137sunKbdWait(void)
138{
139    static struct timeval lastChngKbdTransTv;
140    struct timeval tv;
141    struct timeval lastChngKbdDeltaTv;
142    unsigned int lastChngKbdDelta;
143
144    X_GETTIMEOFDAY(&tv);
145    if (!lastChngKbdTransTv.tv_sec)
146	lastChngKbdTransTv = tv;
147    tvminus(lastChngKbdDeltaTv, tv, lastChngKbdTransTv);
148    lastChngKbdDelta = TVTOMILLI(lastChngKbdDeltaTv);
149    if (lastChngKbdDelta < 750) {
150	unsigned wait;
151	/*
152         * We need to guarantee at least 750 milliseconds between
153	 * calls to KIOCTRANS. YUCK!
154	 */
155	wait = (750L - lastChngKbdDelta) * 1000L;
156        usleep (wait);
157        X_GETTIMEOFDAY(&tv);
158    }
159    lastChngKbdTransTv = tv;
160}
161
162static
163void SwapLKeys(KeySymsRec* keysyms)
164{
165    unsigned int i;
166    KeySym k;
167
168    for (i = 2; i < keysyms->maxKeyCode * keysyms->mapWidth; i++)
169	if (keysyms->map[i] == XK_L1 ||
170	    keysyms->map[i] == XK_L2 ||
171	    keysyms->map[i] == XK_L3 ||
172	    keysyms->map[i] == XK_L4 ||
173	    keysyms->map[i] == XK_L5 ||
174	    keysyms->map[i] == XK_L6 ||
175	    keysyms->map[i] == XK_L7 ||
176	    keysyms->map[i] == XK_L8 ||
177	    keysyms->map[i] == XK_L9 ||
178	    keysyms->map[i] == XK_L10) {
179	    /* yes, I could have done a clever two line swap! */
180	    k = keysyms->map[i - 2];
181	    keysyms->map[i - 2] = keysyms->map[i];
182	    keysyms->map[i] = k;
183	}
184}
185
186static void
187SetLights(KeybdCtrl* ctrl, int fd)
188{
189#ifdef KIOCSLED
190    static unsigned char led_tab[16] = {
191	0,
192#ifdef __sun
193	LED_NUM_LOCK,
194	LED_SCROLL_LOCK,
195	LED_SCROLL_LOCK | LED_NUM_LOCK,
196	LED_COMPOSE,
197	LED_COMPOSE | LED_NUM_LOCK,
198	LED_COMPOSE | LED_SCROLL_LOCK,
199	LED_COMPOSE | LED_SCROLL_LOCK | LED_NUM_LOCK,
200	LED_CAPS_LOCK,
201	LED_CAPS_LOCK | LED_NUM_LOCK,
202	LED_CAPS_LOCK | LED_SCROLL_LOCK,
203	LED_CAPS_LOCK | LED_SCROLL_LOCK | LED_NUM_LOCK,
204	LED_CAPS_LOCK | LED_COMPOSE,
205	LED_CAPS_LOCK | LED_COMPOSE | LED_NUM_LOCK,
206	LED_CAPS_LOCK | LED_COMPOSE | LED_SCROLL_LOCK,
207	LED_CAPS_LOCK | LED_COMPOSE | LED_SCROLL_LOCK | LED_NUM_LOCK
208#else
209	LED_CAPS_LOCK,
210	LED_NUM_LOCK,
211	LED_NUM_LOCK | LED_CAPS_LOCK,
212	LED_SCROLL_LOCK,
213	LED_SCROLL_LOCK | LED_CAPS_LOCK,
214	LED_SCROLL_LOCK | LED_NUM_LOCK,
215	LED_SCROLL_LOCK | LED_NUM_LOCK | LED_CAPS_LOCK,
216	LED_COMPOSE,
217	LED_COMPOSE | LED_CAPS_LOCK,
218	LED_COMPOSE | LED_NUM_LOCK,
219	LED_COMPOSE | LED_NUM_LOCK | LED_CAPS_LOCK,
220	LED_COMPOSE | LED_SCROLL_LOCK,
221	LED_COMPOSE | LED_SCROLL_LOCK | LED_CAPS_LOCK,
222	LED_COMPOSE | LED_SCROLL_LOCK | LED_NUM_LOCK,
223	LED_COMPOSE | LED_SCROLL_LOCK | LED_NUM_LOCK | LED_CAPS_LOCK,
224#endif
225    };
226    if (ioctl (fd, KIOCSLED, (caddr_t)&led_tab[ctrl->leds & SUN_LED_MASK]) == -1)
227	LogMessage(X_ERROR, "Failed to set keyboard lights\n");
228#endif
229}
230
231
232/*-
233 *-----------------------------------------------------------------------
234 * sunBell --
235 *	Ring the terminal/keyboard bell
236 *
237 * Results:
238 *	Ring the keyboard bell for an amount of time proportional to
239 *	"loudness."
240 *
241 * Side Effects:
242 *	None, really...
243 *
244 *-----------------------------------------------------------------------
245 */
246
247static void
248bell(int fd, int duration)
249{
250    int		    kbdCmd;   	    /* Command to give keyboard */
251
252    kbdCmd = KBD_CMD_BELL;
253    if (ioctl (fd, KIOCCMD, &kbdCmd) == -1) {
254 	LogMessage(X_ERROR, "Failed to activate bell\n");
255	return;
256    }
257    if (duration) usleep (duration);
258    kbdCmd = KBD_CMD_NOBELL;
259    if (ioctl (fd, KIOCCMD, &kbdCmd) == -1)
260	LogMessage(X_ERROR, "Failed to deactivate bell\n");
261}
262
263static void
264sunBell(int percent, DeviceIntPtr device, void *ctrl, int unused)
265{
266    KeybdCtrl*      kctrl = (KeybdCtrl*) ctrl;
267    sunKbdPrivPtr   pPriv = (sunKbdPrivPtr) device->public.devicePrivate;
268
269    if (percent == 0 || kctrl->bell == 0)
270 	return;
271
272    bell (pPriv->fd, kctrl->bell_duration * 1000);
273}
274
275void
276DDXRingBell(int volume, int pitch, int duration)
277{
278    DeviceIntPtr pKeyboard;
279    sunKbdPrivPtr pPriv;
280
281    pKeyboard = sunKeyboardDevice;
282    if (pKeyboard != NULL) {
283	pPriv = (sunKbdPrivPtr)pKeyboard->public.devicePrivate;
284	bell(pPriv->fd, duration * 1000);
285    }
286}
287
288
289#ifdef __sun
290#define XLED_NUM_LOCK    0x1
291#define XLED_COMPOSE     0x4
292#define XLED_SCROLL_LOCK 0x2
293#define XLED_CAPS_LOCK   0x8
294#else
295#define XLED_NUM_LOCK    0x2
296#define XLED_COMPOSE     0x8
297#define XLED_SCROLL_LOCK 0x4
298#define XLED_CAPS_LOCK   0x1
299#endif
300
301static KeyCode
302LookupKeyCode(KeySym keysym, XkbDescPtr xkb, KeySymsPtr syms)
303{
304    KeyCode i;
305    int ii, index = 0;
306
307    for (i = xkb->min_key_code; i < xkb->max_key_code; i++)
308	for (ii = 0; ii < syms->mapWidth; ii++)
309	    if (syms->map[index++] == keysym)
310		return i;
311    return 0;
312}
313
314static void
315pseudoKey(DeviceIntPtr device, Bool down, KeyCode keycode)
316{
317    int bit;
318    CARD8 modifiers;
319    CARD16 mask;
320    BYTE* kptr;
321
322    kptr = &device->key->down[keycode >> 3];
323    bit = 1 << (keycode & 7);
324    modifiers = device->key->xkbInfo->desc->map->modmap[keycode];
325    if (down) {
326	/* fool dix into thinking this key is now "down" */
327	int i;
328	*kptr |= bit;
329	for (i = 0, mask = 1; modifiers; i++, mask <<= 1)
330	    if (mask & modifiers) {
331		device->key->modifierKeyCount[i]++;
332		modifiers &= ~mask;
333	    }
334    } else {
335	/* fool dix into thinking this key is now "up" */
336	if (*kptr & bit) {
337	    int i;
338	    *kptr &= ~bit;
339	    for (i = 0, mask = 1; modifiers; i++, mask <<= 1)
340		if (mask & modifiers) {
341		    if (--device->key->modifierKeyCount[i] <= 0) {
342			device->key->modifierKeyCount[i] = 0;
343		    }
344		    modifiers &= ~mask;
345		}
346	}
347    }
348}
349
350static void
351DoLEDs(
352    DeviceIntPtr    device,	    /* Keyboard to alter */
353    KeybdCtrl* ctrl,
354    sunKbdPrivPtr pPriv
355)
356{
357    XkbDescPtr xkb;
358    KeySymsPtr syms;
359
360    xkb = device->key->xkbInfo->desc;
361    syms = XkbGetCoreMap(device);
362    if (!syms)
363	return;	/* XXX */
364
365    if ((ctrl->leds & XLED_CAPS_LOCK) && !(pPriv->leds & XLED_CAPS_LOCK))
366	    pseudoKey(device, TRUE,
367		LookupKeyCode(XK_Caps_Lock, xkb, syms));
368
369    if (!(ctrl->leds & XLED_CAPS_LOCK) && (pPriv->leds & XLED_CAPS_LOCK))
370	    pseudoKey(device, FALSE,
371		LookupKeyCode(XK_Caps_Lock, xkb, syms));
372
373    if ((ctrl->leds & XLED_NUM_LOCK) && !(pPriv->leds & XLED_NUM_LOCK))
374	    pseudoKey(device, TRUE,
375		LookupKeyCode(XK_Num_Lock, xkb, syms));
376
377    if (!(ctrl->leds & XLED_NUM_LOCK) && (pPriv->leds & XLED_NUM_LOCK))
378	    pseudoKey(device, FALSE,
379		LookupKeyCode(XK_Num_Lock, xkb, syms));
380
381    if ((ctrl->leds & XLED_SCROLL_LOCK) && !(pPriv->leds & XLED_SCROLL_LOCK))
382	    pseudoKey(device, TRUE,
383		LookupKeyCode(XK_Scroll_Lock, xkb, syms));
384
385    if (!(ctrl->leds & XLED_SCROLL_LOCK) && (pPriv->leds & XLED_SCROLL_LOCK))
386	    pseudoKey(device, FALSE,
387		LookupKeyCode(XK_Scroll_Lock, xkb, syms));
388
389    if ((ctrl->leds & XLED_COMPOSE) && !(pPriv->leds & XLED_COMPOSE))
390	    pseudoKey(device, TRUE,
391		LookupKeyCode(SunXK_Compose, xkb, syms));
392
393    if (!(ctrl->leds & XLED_COMPOSE) && (pPriv->leds & XLED_COMPOSE))
394	    pseudoKey(device, FALSE,
395		LookupKeyCode(SunXK_Compose, xkb, syms));
396
397    pPriv->leds = ctrl->leds & SUN_LED_MASK;
398    SetLights (ctrl, pPriv->fd);
399    free(syms->map);
400    free(syms);
401}
402
403/*-
404 *-----------------------------------------------------------------------
405 * sunKbdCtrl --
406 *	Alter some of the keyboard control parameters
407 *
408 * Results:
409 *	None.
410 *
411 * Side Effects:
412 *	Some...
413 *
414 *-----------------------------------------------------------------------
415 */
416
417static void
418sunKbdCtrl(DeviceIntPtr device, KeybdCtrl* ctrl)
419{
420    sunKbdPrivPtr pPriv = (sunKbdPrivPtr) device->public.devicePrivate;
421
422    if (pPriv->fd < 0) return;
423
424    if (ctrl->click != pPriv->click) {
425    	int kbdClickCmd;
426
427	pPriv->click = ctrl->click;
428	kbdClickCmd = pPriv->click ? KBD_CMD_CLICK : KBD_CMD_NOCLICK;
429    	if (ioctl (pPriv->fd, KIOCCMD, &kbdClickCmd) == -1)
430 	    LogMessage(X_ERROR, "Failed to set keyclick\n");
431    }
432    if ((pPriv->type == KB_SUN4) && (pPriv->leds != (ctrl->leds & SUN_LED_MASK)))
433	DoLEDs(device, ctrl, pPriv);
434}
435
436/*-
437 *-----------------------------------------------------------------------
438 * sunInitKbdNames --
439 *	Handle the XKB initialization
440 *
441 * Results:
442 *	None.
443 *
444 * Comments:
445 *     This function needs considerable work, in conjunctions with
446 *     the need to add geometry descriptions of Sun Keyboards.
447 *     It would also be nice to have #defines for all the keyboard
448 *     layouts so that we don't have to have these hard-coded
449 *     numbers.
450 *
451 *-----------------------------------------------------------------------
452 */
453static void
454sunInitKbdNames(XkbRMLVOSet *rmlvo, sunKbdPrivPtr pKbd)
455{
456#if 0 /* XXX to be revisited later */
457#ifndef XKBBUFSIZE
458#define XKBBUFSIZE 64
459#endif
460    static char keycodesbuf[XKBBUFSIZE];
461    static char geometrybuf[XKBBUFSIZE];
462    static char  symbolsbuf[XKBBUFSIZE];
463
464    names->keymap = NULL;
465    names->compat = "compat/complete";
466    names->types  = "types/complete";
467    names->keycodes = keycodesbuf;
468    names->geometry = geometrybuf;
469    names->symbols = symbolsbuf;
470    (void) strcpy (keycodesbuf, "keycodes/");
471    (void) strcpy (geometrybuf, "geometry/");
472    (void) strcpy (symbolsbuf, "symbols/");
473
474    /* keycodes & geometry */
475    switch (pKbd->type) {
476    case KB_SUN2:
477	(void) strcat (names->keycodes, "sun(type2)");
478	(void) strcat (names->geometry, "sun(type2)");
479	(void) strcat (names->symbols, "us(sun2)");
480	break;
481    case KB_SUN3:
482	(void) strcat (names->keycodes, "sun(type3)");
483	(void) strcat (names->geometry, "sun(type3)");
484	(void) strcat (names->symbols, "us(sun3)");
485	break;
486    case KB_SUN4:
487	/* First, catch "fully known" models */
488	switch (pKbd->layout) {
489	case 11:		/* type4, Sweden */
490	    (void) strcat (names->geometry, "sun(type4_se)");
491	    (void) strcat (names->keycodes,
492			   "sun(type4_se_swapctl)");
493	    (void) strcat (names->symbols,
494			   "sun/se(sun4)+se(fixdollar)");
495	    return;
496	    break;
497	case 43:		/* type5/5c, Sweden */
498	    (void) strcat (names->geometry, "sun(type5c_se)");
499	    (void) strcat (names->keycodes, "sun(type5_se)");
500	    (void) strcat (names->symbols,
501			   "sun/se(sun5)+se(fixdollar)");
502	    return;
503	    break;
504	case 90:		/* "Compact 1", Sweden (???) */
505	    break;		/* No specific mapping, yet */
506	default:
507	    break;
508	}
509
510	if (pKbd->layout == 19) {
511	    (void) strcat (names->keycodes, "sun(US101A)");
512	    (void) strcat (names->geometry, "pc101-NG"); /* XXX */
513	    (void) strcat (names->symbols, "us(pc101)");
514	} else if (pKbd->layout < 33) {
515	    (void) strcat (names->keycodes, "sun(type4)");
516	    (void) strcat (names->geometry, "sun(type4)");
517	    if (sunSwapLkeys)
518		(void) strcat (names->symbols, "sun/us(sun4ol)");
519	    else
520		(void) strcat (names->symbols, "sun/us(sun4)");
521	} else {
522	    switch (pKbd->layout) {
523	    case 33: case 80: /* U.S. */
524	    case 47: case 94: /* Korea */
525	    case 48: case 95: /* Taiwan */
526	    case 49: case 96: /* Japan */
527		(void) strcat (names->keycodes, "sun(type5)");
528		(void) strcat (names->geometry, "sun(type5)");
529		break;
530	    case 34: case 81: /* U.S. Unix */
531		(void) strcat (names->keycodes, "sun(type5)");
532		(void) strcat (names->geometry, "sun(type5unix)");
533		break;
534	    default:
535		(void) strcat (names->keycodes, "sun(type5_euro)");
536		(void) strcat (names->geometry, "sun(type5euro)");
537	    }
538
539	    if (sunSwapLkeys)
540		(void) strcat (names->symbols, "sun/us(sun5ol)");
541	    else
542		(void) strcat (names->symbols, "sun/us(sun5)");
543	}
544	break;
545    default:
546	names->keycodes = names->geometry = NULL;
547	break;
548    }
549
550    /* extra symbols */
551
552    if (pKbd->type == KB_SUN4) {
553	switch (pKbd->layout) {
554	case  4: case 36: case 83:
555	case  5: case 37: case 84:
556	case  6: case 38: case 85:
557	case  8: case 40: case 87:
558	case  9: case 41: case 88:
559	case 10: case 42: case 89:
560/*	case 11: case 43: case 90: */ /* handled earlier */
561	case 12: case 44: case 91:
562	case 13: case 45: case 92:
563	case 14: case 46: case 93:
564	    (void) strcat (names->symbols, "+iso9995-3(basic)"); break;
565	}
566    }
567
568    if (pKbd->type == KB_SUN4) {
569	switch (pKbd->layout) {
570	case  0: case  1: case 33: case 34: case 80: case 81:
571	    break;
572	case  3:
573	    (void) strcat (names->symbols, "+ca"); break;
574	case  4: case 36: case 83:
575	    (void) strcat (names->symbols, "+dk"); break;
576	case  5: case 37: case 84:
577	    (void) strcat (names->symbols, "+de"); break;
578	case  6: case 38: case 85:
579	    (void) strcat (names->symbols, "+it"); break;
580	case  8: case 40: case 87:
581	    (void) strcat (names->symbols, "+no"); break;
582	case  9: case 41: case 88:
583	    (void) strcat (names->symbols, "+pt"); break;
584	case 10: case 42: case 89:
585	    (void) strcat (names->symbols, "+es"); break;
586	    /* case 11: case 43: */ /* handled earlier */
587	case 90:
588	    (void) strcat (names->symbols, "+se"); break;
589	case 12: case 44: case 91:
590	    (void) strcat (names->symbols, "+fr_CH"); break;
591	case 13: case 45: case 92:
592	    (void) strcat (names->symbols, "+de_CH"); break;
593	case 14: case 46: case 93:
594	    (void) strcat (names->symbols, "+gb"); break; /* s/b en_UK */
595	case 52:
596	    (void) strcat (names->symbols, "+pl"); break;
597	case 53:
598	    (void) strcat (names->symbols, "+cs"); break;
599	case 54:
600	    (void) strcat (names->symbols, "+ru"); break;
601#if 0
602	/* don't have symbols defined for these yet, let them default */
603	case  2:
604	    (void) strcat (names->symbols, "+fr_BE"); break;
605	case  7: case 39: case 86:
606	    (void) strcat (names->symbols, "+nl"); break;
607	case 50: case 97:
608	    (void) strcat (names->symbols, "+fr_CA"); break;
609	case 16: case 47: case 94:
610	    (void) strcat (names->symbols, "+ko"); break;
611	case 17: case 48: case 95:
612	    (void) strcat (names->symbols, "+tw"); break;
613	case 32: case 49: case 96:
614	    (void) strcat (names->symbols, "+jp"); break;
615	case 51:
616	    (void) strcat (names->symbols, "+hu"); break;
617#endif
618	/*
619	 * by setting the symbols to NULL XKB will use the symbols in
620	 * the "default" keymap.
621	 */
622	default:
623	    names->symbols = NULL; return; break;
624	}
625    }
626#else
627    rmlvo->rules = "base";
628    rmlvo->model = "empty";
629    rmlvo->layout = "empty";
630    rmlvo->variant = NULL;
631    rmlvo->options = NULL;
632#endif
633}
634
635static int
636getKbdType(int fd)
637{
638/*
639 * The Sun 386i has system include files that preclude this pre SunOS 4.1
640 * test for the presence of a type 4 keyboard however it really doesn't
641 * matter since no 386i has ever been shipped with a type 3 keyboard.
642 * SunOS 4.1 no longer needs this kludge.
643 */
644#if !defined(i386) && !defined(KIOCGKEY)
645#define TYPE4KEYBOARDOVERRIDE
646#endif
647
648    int ii, type;
649
650    for (ii = 0; ii < 3; ii++) {
651	sunKbdWait();
652	(void)ioctl(fd, KIOCTYPE, &type);
653#ifdef TYPE4KEYBOARDOVERRIDE
654	/*
655	 * Magic. Look for a key which is non-existent on a real type
656	 * 3 keyboard but does exist on a type 4 keyboard.
657	 */
658	if (type == KB_SUN3) {
659	    struct kiockeymap key;
660
661	    key.kio_tablemask = 0;
662	    key.kio_station = 118;
663	    if (ioctl(fd, KIOCGKEY, &key) == -1) {
664		LogMessage(X_ERROR, "ioctl KIOCGKEY\n" );
665		FatalError("Can't KIOCGKEY on fd %d\n", fd);
666	    }
667	    if (key.kio_entry != HOLE)
668		type = KB_SUN4;
669	}
670#endif
671	switch (type) {
672	case KB_SUN2:
673	case KB_SUN3:
674	case KB_SUN4:
675	    return type;
676	default:
677	    sunChangeKbdTranslation(fd, FALSE);
678	    continue;
679	}
680    }
681    return -1;
682}
683
684/*-
685 *-----------------------------------------------------------------------
686 * sunKbdProc --
687 *	Handle the initialization, etc. of a keyboard.
688 *
689 * Results:
690 *	None.
691 *
692 *-----------------------------------------------------------------------
693 */
694
695int
696sunKbdProc(DeviceIntPtr device, int what)
697{
698    DevicePtr pKeyboard = &device->public;
699    sunKbdPrivPtr pPriv;
700    KeybdCtrl*	ctrl = &device->kbdfeed->ctrl;
701    XkbRMLVOSet rmlvo;
702    CARD8 workingModMap[MAP_LENGTH];
703    int type = -1, layout = -1, mapsize;
704    KeySymsPtr keysym;
705    KeySym *map;
706
707    switch (what) {
708    case DEVICE_INIT:
709	pPriv = malloc(sizeof(*pPriv));
710	if (pPriv == NULL) {
711	    LogMessage(X_ERROR, "Cannot allocate private data for keyboard\n");
712	    return !Success;
713	}
714	pPriv->fd = open("/dev/kbd", O_RDWR | O_NONBLOCK, 0);
715	if (pPriv->fd < 0) {
716	    LogMessage(X_ERROR, "Cannot open /dev/kbd, error %d\n", errno);
717	    free(pPriv);
718	    return !Success;
719	}
720
721	type = getKbdType(pPriv->fd);
722	if (type < 0)
723	    FatalError("Unsupported keyboard type %d\n", type);
724
725	switch (type) {
726	case KB_SUN2:
727	case KB_SUN3:
728	    /* No layout variation */
729	    LogMessage(X_INFO, "Sun type %d Keyboard\n", type);
730	    break;
731	case KB_SUN4:
732#define LAYOUT_US5	33
733	    (void)ioctl(pPriv->fd, KIOCLAYOUT, &layout);
734	    if (layout < 0 ||
735		layout > sunMaxLayout ||
736		sunType4KeyMaps[layout] == NULL)
737		FatalError("Unsupported keyboard type 4 layout %d\n", layout);
738	    /* Type 5 keyboard also treated as Type 4 layout variants */
739	    LogMessage(X_INFO, "Sun type %d Keyboard, layout %d\n",
740		layout >= LAYOUT_US5 ? 5 : 4, layout);
741	    break;
742	default:
743	    LogMessage(X_INFO, "Unknown keyboard type\n");
744	    break;
745        }
746
747	keysym = &sunKeySyms[type];
748	mapsize = ((int)keysym->maxKeyCode - (int)keysym->minKeyCode + 1)
749	    * keysym->mapWidth * sizeof(keysym->map[0]);
750	map = malloc(mapsize);
751	if (map == NULL) {
752	    LogMessage(X_ERROR, "Failed to allocate KeySym map\n");
753	    close(pPriv->fd);
754	    free(pPriv);
755	    return !Success;
756        }
757	if (type == KB_SUN4) {
758	    memcpy(map, sunType4KeyMaps[layout], mapsize);
759	} else {
760	    memcpy(map, sunKeySyms[type].map, mapsize);
761	}
762
763	pPriv->type = type;
764	pPriv->layout = layout;
765	pPriv->click = 0;
766	pPriv->leds = (Leds)0;
767	pPriv->keysym.map = map;
768	pPriv->keysym.minKeyCode = keysym->minKeyCode;
769	pPriv->keysym.maxKeyCode = keysym->maxKeyCode;
770	pPriv->keysym.mapWidth = keysym->mapWidth;
771
772	/* sunKbdCtrl() callback refers pKeyboard->devicePrivate */
773	pKeyboard->devicePrivate = pPriv;
774	pKeyboard->on = FALSE;
775
776	if (type == KB_SUN4 && sunSwapLkeys) {
777	    /* This could update pPriv->keysym.map */
778	    SwapLKeys(&pPriv->keysym);
779	}
780
781	if (pPriv->keysym.minKeyCode < MIN_KEYCODE) {
782	    pPriv->keysym.minKeyCode += MIN_KEYCODE;
783	    pPriv->keysym.maxKeyCode += MIN_KEYCODE;
784	}
785	if (pPriv->keysym.maxKeyCode > MAX_KEYCODE)
786	    pPriv->keysym.maxKeyCode = MAX_KEYCODE;
787
788	sunInitModMap(&pPriv->keysym, workingModMap);
789
790	sunInitKbdNames(&rmlvo, pPriv);
791#if 0 /* XXX needs more work for Xorg xkb */
792	InitKeyboardDeviceStruct(device, &rmlvo,
793				 sunBell, sunKbdCtrl);
794#else
795	XkbSetRulesDflts(&rmlvo);
796	InitKeyboardDeviceStruct(device, NULL,
797				 sunBell, sunKbdCtrl);
798	XkbApplyMappingChange(device, &pPriv->keysym,
799			      pPriv->keysym.minKeyCode,
800			      pPriv->keysym.maxKeyCode -
801			      pPriv->keysym.minKeyCode + 1,
802			      workingModMap, serverClient);
803#endif
804	break;
805
806    case DEVICE_ON:
807	pPriv = (sunKbdPrivPtr)pKeyboard->devicePrivate;
808	/*
809	 * Set the keyboard into "direct" mode and turn on
810	 * event translation.
811	 */
812	if (sunChangeKbdTranslation(pPriv->fd, TRUE) == -1)
813	    FatalError("Can't set keyboard translation\n");
814
815	SetNotifyFd(pPriv->fd, sunKbdEvents, X_NOTIFY_READ, device);
816
817	pKeyboard->on = TRUE;
818	break;
819
820    case DEVICE_OFF:
821	pPriv = (sunKbdPrivPtr)pKeyboard->devicePrivate;
822	if (pPriv->type == KB_SUN4) {
823	    /* dumb bug in Sun's keyboard! Turn off LEDS before resetting */
824	    pPriv->leds = 0;
825	    ctrl->leds = 0;
826	    SetLights(ctrl, pPriv->fd);
827	}
828	/*
829	 * Restore original keyboard directness and translation.
830	 */
831	if (sunChangeKbdTranslation(pPriv->fd, FALSE) == -1)
832	    FatalError("Can't reset keyboard translation\n");
833	RemoveNotifyFd(pPriv->fd);
834	pKeyboard->on = FALSE;
835	break;
836
837    case DEVICE_CLOSE:
838	pPriv = (sunKbdPrivPtr)pKeyboard->devicePrivate;
839	free(pPriv->keysym.map);
840	close(pPriv->fd);
841	free(pPriv);
842	pKeyboard->devicePrivate = NULL;
843	break;
844
845    case DEVICE_ABORT:
846	/*
847	 * Restore original keyboard directness and translation.
848	 */
849	pPriv = (sunKbdPrivPtr)pKeyboard->devicePrivate;
850	(void)sunChangeKbdTranslation(pPriv->fd, FALSE);
851	break;
852    }
853    return Success;
854}
855
856/*-------------------------------------------------------------------------
857 * sunInitModMap --
858 *	Initialize ModMap per specified KeyMap table.
859 *
860 * Results:
861 * 	None.
862 *
863 * Side Effects:
864 *	None.
865 *-----------------------------------------------------------------------*/
866static void
867sunInitModMap(
868    const KeySymsRec *KeySyms,	/* KeyMap data to set ModMap */
869    CARD8 *ModMap		/* ModMap to be initialized */
870)
871{
872    KeySym *k;
873    int i, min, max, width;
874
875    for (i = 0; i < MAP_LENGTH; i++)
876        ModMap[i] = NoSymbol;
877
878    min   = KeySyms->minKeyCode;
879    max   = KeySyms->maxKeyCode;
880    width = KeySyms->mapWidth;
881    for (i = min, k = KeySyms->map; i < max; i++, k += width) {
882	switch (*k) {
883
884	case XK_Shift_L:
885	case XK_Shift_R:
886	    ModMap[i] = ShiftMask;
887	    break;
888
889	case XK_Control_L:
890	case XK_Control_R:
891	    ModMap[i] = ControlMask;
892	    break;
893
894	case XK_Caps_Lock:
895	    ModMap[i] = LockMask;
896	    break;
897
898	case XK_Alt_L:
899	case XK_Alt_R:
900	    ModMap[i] = Alt_Mask;
901	    break;
902
903	case XK_Num_Lock:
904	    ModMap[i] = Num_Lock_Mask;
905	    break;
906
907	case XK_Scroll_Lock:
908	    ModMap[i] = ScrollLockMask;
909	    break;
910
911	case XK_Meta_L:
912	case XK_Meta_R:
913	    ModMap[i] = Meta_Mask;
914	    break;
915
916	case SunXK_AltGraph:
917	    ModMap[i] = Mode_switch_Mask;
918	    break;
919        }
920    }
921}
922
923/*-
924 *-----------------------------------------------------------------------
925 * sunKbdGetEvents --
926 *	Return the events waiting in the wings for the given keyboard.
927 *
928 * Results:
929 *	Update Firm_event buffer in DeviceIntPtr if events are received.
930 *	Return the number of received Firm_events in the buffer.
931 *
932 * Side Effects:
933 *	None.
934 *-----------------------------------------------------------------------
935 */
936
937static int
938sunKbdGetEvents(DeviceIntPtr device)
939{
940    DevicePtr pKeyboard = &device->public;
941    sunKbdPrivPtr pPriv = pKeyboard->devicePrivate;
942    int	nBytes;	 	   /* number of bytes of events available. */
943    int NumEvents = 0;
944
945    nBytes = read(pPriv->fd, pPriv->evbuf, sizeof(pPriv->evbuf));
946    if (nBytes == -1) {
947	if (errno != EWOULDBLOCK) {
948	    LogMessage(X_ERROR, "Unexpected error on reading keyboard\n");
949	    FatalError("Could not read the keyboard");
950	}
951    } else {
952	if (pKeyboard->on) {
953	    NumEvents = nBytes / sizeof(pPriv->evbuf[0]);
954	}
955    }
956    return NumEvents;
957}
958
959/*-
960 *-----------------------------------------------------------------------
961 * sunKbdEnqueueEvent --
962 *
963 *-----------------------------------------------------------------------
964 */
965
966static void
967sunKbdEnqueueEvent(DeviceIntPtr device, Firm_event *fe)
968{
969    BYTE		keycode;
970    int			type;
971
972    keycode = (fe->id & 0x7f) + MIN_KEYCODE;
973    type = ((fe->value == VKEY_UP) ? KeyRelease : KeyPress);
974    QueueKeyboardEvents(device, type, keycode);
975}
976
977
978/*-
979 *-----------------------------------------------------------------------
980 * sunChangeKbdTranslation
981 *	Makes operating system calls to set keyboard translation
982 *	and direction on or off.
983 *
984 * Results:
985 *	-1 if failure, else 0.
986 *
987 * Side Effects:
988 * 	Changes kernel management of keyboard.
989 *
990 *-----------------------------------------------------------------------
991 */
992static int
993sunChangeKbdTranslation(int fd, Bool makeTranslated)
994{
995    int 	tmp;
996#ifndef i386 /* { */
997    sigset_t	hold_mask, old_mask;
998#else /* }{ */
999    int		old_mask;
1000#endif /* } */
1001    int		toread;
1002    char	junk[8192];
1003
1004#ifndef i386 /* { */
1005    (void) sigfillset(&hold_mask);
1006    (void) sigprocmask(SIG_BLOCK, &hold_mask, &old_mask);
1007#else /* }{ */
1008    old_mask = sigblock (~0);
1009#endif /* } */
1010    sunKbdWait();
1011    if (makeTranslated) {
1012        /*
1013         * Next set the keyboard into "direct" mode and turn on
1014         * event translation. If either of these fails, we can't go
1015         * on.
1016         */
1017	tmp = 1;
1018	if (ioctl (fd, KIOCSDIRECT, &tmp) == -1) {
1019	    ErrorF("Setting keyboard direct mode\n");
1020	    return -1;
1021	}
1022	tmp = TR_UNTRANS_EVENT;
1023	if (ioctl (fd, KIOCTRANS, &tmp) == -1) {
1024	    ErrorF("Setting keyboard translation\n");
1025	    ErrorF ("sunChangeKbdTranslation: kbdFd=%d\n", fd);
1026	    return -1;
1027	}
1028    } else {
1029        /*
1030         * Next set the keyboard into "indirect" mode and turn off
1031         * event translation.
1032         */
1033	tmp = 0;
1034	(void)ioctl (fd, KIOCSDIRECT, &tmp);
1035	tmp = TR_ASCII;
1036	(void)ioctl (fd, KIOCTRANS, &tmp);
1037    }
1038    if (ioctl (fd, FIONREAD, &toread) != -1 && toread > 0) {
1039	while (toread) {
1040	    tmp = toread;
1041	    if (toread > sizeof (junk))
1042		tmp = sizeof (junk);
1043	    (void) read (fd, junk, tmp);
1044	    toread -= tmp;
1045	}
1046    }
1047#ifndef i386 /* { */
1048    (void) sigprocmask(SIG_SETMASK, &old_mask, NULL);
1049#else /* }{ */
1050    sigsetmask (old_mask);
1051#endif /* } */
1052    return 0;
1053}
1054