#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include <xkb-config.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <math.h>
#include <X11/X.h>
#include <X11/Xproto.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include "misc.h"
#include "inputstr.h"
#include "opaque.h"
#include "property.h"
#include "scrnintstr.h"
#define XKBSRV_NEED_FILE_FUNCS
#include <xkbsrv.h>
#include "xkbgeom.h"
#include <X11/extensions/XKMformat.h>
#include "xkbfile.h"
#include "xkb.h"
#define CREATE_ATOM(s) MakeAtom(s,sizeof(s)-1,1)
#if defined(__alpha) || defined(__alpha__)
#define LED_COMPOSE 2
#define LED_CAPS 3
#define LED_SCROLL 4
#define LED_NUM 5
#define PHYS_LEDS 0x1f
#else
#ifdef sun
#define LED_NUM 1
#define LED_SCROLL 2
#define LED_COMPOSE 3
#define LED_CAPS 4
#define PHYS_LEDS 0x0f
#else
#define LED_CAPS 1
#define LED_NUM 2
#define LED_SCROLL 3
#define PHYS_LEDS 0x07
#endif
#endif
#define MAX_TOC 16
typedef struct _SrvXkmInfo {
DeviceIntPtr dev;
FILE * file;
XkbDescPtr xkb;
} SrvXkmInfo;
#ifndef XKB_DFLT_RULES_PROP
#define XKB_DFLT_RULES_PROP TRUE
#endif
char * XkbBaseDirectory= XKB_BASE_DIRECTORY;
char * XkbBinDirectory= XKB_BIN_DIRECTORY;
static int XkbWantAccessX= 0;
static char * XkbRulesDflt= NULL;
static char * XkbModelDflt= NULL;
static char * XkbLayoutDflt= NULL;
static char * XkbVariantDflt= NULL;
static char * XkbOptionsDflt= NULL;
static char * XkbRulesUsed= NULL;
static char * XkbModelUsed= NULL;
static char * XkbLayoutUsed= NULL;
static char * XkbVariantUsed= NULL;
static char * XkbOptionsUsed= NULL;
static XkbDescPtr xkb_cached_map = NULL;
static Bool XkbWantRulesProp= XKB_DFLT_RULES_PROP;
void
XkbGetRulesDflts(XkbRMLVOSet *rmlvo)
{
if (XkbRulesDflt) rmlvo->rules = XkbRulesDflt;
else rmlvo->rules = XKB_DFLT_RULES;
if (XkbModelDflt) rmlvo->model= XkbModelDflt;
else rmlvo->model= XKB_DFLT_MODEL;
if (XkbLayoutDflt) rmlvo->layout= XkbLayoutDflt;
else rmlvo->layout= XKB_DFLT_LAYOUT;
if (XkbVariantDflt) rmlvo->variant= XkbVariantDflt;
else rmlvo->variant= XKB_DFLT_VARIANT;
if (XkbOptionsDflt) rmlvo->options= XkbOptionsDflt;
else rmlvo->options= XKB_DFLT_OPTIONS;
rmlvo->rules = strdup(rmlvo->rules);
rmlvo->model = strdup(rmlvo->model);
rmlvo->layout = strdup(rmlvo->layout);
rmlvo->variant = strdup(rmlvo->variant);
rmlvo->options = strdup(rmlvo->options);
}
void
XkbFreeRMLVOSet(XkbRMLVOSet *rmlvo, Bool freeRMLVO)
{
if (!rmlvo)
return;
free(rmlvo->rules);
free(rmlvo->model);
free(rmlvo->layout);
free(rmlvo->variant);
free(rmlvo->options);
if (freeRMLVO)
free(rmlvo);
else
memset(rmlvo, 0, sizeof(XkbRMLVOSet));
}
static Bool
XkbWriteRulesProp(ClientPtr client, pointer closure)
{
int len,out;
Atom name;
char * pval;
len= (XkbRulesUsed?strlen(XkbRulesUsed):0);
len+= (XkbModelUsed?strlen(XkbModelUsed):0);
len+= (XkbLayoutUsed?strlen(XkbLayoutUsed):0);
len+= (XkbVariantUsed?strlen(XkbVariantUsed):0);
len+= (XkbOptionsUsed?strlen(XkbOptionsUsed):0);
if (len<1)
return TRUE;
len+= 5;
name= MakeAtom(_XKB_RF_NAMES_PROP_ATOM,strlen(_XKB_RF_NAMES_PROP_ATOM),1);
if (name==None) {
ErrorF("[xkb] Atom error: %s not created\n",_XKB_RF_NAMES_PROP_ATOM);
return TRUE;
}
pval= (char*) malloc(len);
if (!pval) {
ErrorF("[xkb] Allocation error: %s proprerty not created\n",
_XKB_RF_NAMES_PROP_ATOM);
return TRUE;
}
out= 0;
if (XkbRulesUsed) {
strcpy(&pval[out],XkbRulesUsed);
out+= strlen(XkbRulesUsed);
}
pval[out++]= '\0';
if (XkbModelUsed) {
strcpy(&pval[out],XkbModelUsed);
out+= strlen(XkbModelUsed);
}
pval[out++]= '\0';
if (XkbLayoutUsed) {
strcpy(&pval[out],XkbLayoutUsed);
out+= strlen(XkbLayoutUsed);
}
pval[out++]= '\0';
if (XkbVariantUsed) {
strcpy(&pval[out],XkbVariantUsed);
out+= strlen(XkbVariantUsed);
}
pval[out++]= '\0';
if (XkbOptionsUsed) {
strcpy(&pval[out],XkbOptionsUsed);
out+= strlen(XkbOptionsUsed);
}
pval[out++]= '\0';
if (out!=len) {
ErrorF("[xkb] Internal Error! bad size (%d!=%d) for _XKB_RULES_NAMES\n",
out,len);
}
dixChangeWindowProperty(serverClient, screenInfo.screens[0]->root, name, XA_STRING, 8,
PropModeReplace, len, pval, TRUE);
free(pval);
return TRUE;
}
static void
XkbSetRulesUsed(XkbRMLVOSet *rmlvo)
{
free(XkbRulesUsed);
XkbRulesUsed= (rmlvo->rules?_XkbDupString(rmlvo->rules):NULL);
free(XkbModelUsed);
XkbModelUsed= (rmlvo->model?_XkbDupString(rmlvo->model):NULL);
free(XkbLayoutUsed);
XkbLayoutUsed= (rmlvo->layout?_XkbDupString(rmlvo->layout):NULL);
free(XkbVariantUsed);
XkbVariantUsed= (rmlvo->variant?_XkbDupString(rmlvo->variant):NULL);
free(XkbOptionsUsed);
XkbOptionsUsed= (rmlvo->options?_XkbDupString(rmlvo->options):NULL);
if (XkbWantRulesProp)
QueueWorkProc(XkbWriteRulesProp,NULL,NULL);
return;
}
void
XkbSetRulesDflts(XkbRMLVOSet *rmlvo)
{
if (rmlvo->rules) {
free(XkbRulesDflt);
XkbRulesDflt= _XkbDupString(rmlvo->rules);
}
if (rmlvo->model) {
free(XkbModelDflt);
XkbModelDflt= _XkbDupString(rmlvo->model);
}
if (rmlvo->layout) {
free(XkbLayoutDflt);
XkbLayoutDflt= _XkbDupString(rmlvo->layout);
}
if (rmlvo->variant) {
free(XkbVariantDflt);
XkbVariantDflt= _XkbDupString(rmlvo->variant);
}
if (rmlvo->options) {
free(XkbOptionsDflt);
XkbOptionsDflt= _XkbDupString(rmlvo->options);
}
return;
}
void
XkbDeleteRulesDflts(void)
{
free(XkbRulesDflt);
XkbRulesDflt = NULL;
free(XkbModelDflt);
XkbModelDflt = NULL;
free(XkbLayoutDflt);
XkbLayoutDflt = NULL;
free(XkbVariantDflt);
XkbVariantDflt = NULL;
free(XkbOptionsDflt);
XkbOptionsDflt = NULL;
XkbFreeKeyboard(xkb_cached_map, XkbAllComponentsMask, TRUE);
xkb_cached_map = NULL;
}
#define DIFFERS(a, b) (strcmp((a) ? (a) : "", (b) ? (b) : "") != 0)
static Bool
XkbCompareUsedRMLVO(XkbRMLVOSet *rmlvo)
{
if (DIFFERS(rmlvo->rules, XkbRulesUsed) ||
DIFFERS(rmlvo->model, XkbModelUsed) ||
DIFFERS(rmlvo->layout, XkbLayoutUsed) ||
DIFFERS(rmlvo->variant, XkbVariantUsed) ||
DIFFERS(rmlvo->options, XkbOptionsUsed))
return FALSE;
return TRUE;
}
#undef DIFFERS
#include "xkbDflts.h"
static Bool
XkbInitKeyTypes(XkbDescPtr xkb)
{
if (xkb->defined & XkmTypesMask)
return TRUE;
initTypeNames(NULL);
if (XkbAllocClientMap(xkb,XkbKeyTypesMask,num_dflt_types)!=Success)
return FALSE;
if (XkbCopyKeyTypes(dflt_types,xkb->map->types,num_dflt_types)!=
Success) {
return FALSE;
}
xkb->map->size_types= xkb->map->num_types= num_dflt_types;
return TRUE;
}
static void
XkbInitRadioGroups(XkbSrvInfoPtr xkbi)
{
xkbi->nRadioGroups = 0;
xkbi->radioGroups = NULL;
return;
}
static Status
XkbInitCompatStructs(XkbDescPtr xkb)
{
register int i;
XkbCompatMapPtr compat;
if (xkb->defined & XkmCompatMapMask)
return TRUE;
if (XkbAllocCompatMap(xkb,XkbAllCompatMask,num_dfltSI)!=Success)
return BadAlloc;
compat = xkb->compat;
if (compat->sym_interpret) {
compat->num_si = num_dfltSI;
memcpy((char *)compat->sym_interpret,(char *)dfltSI,sizeof(dfltSI));
}
for (i=0;i<XkbNumKbdGroups;i++) {
compat->groups[i]= compatMap.groups[i];
if (compat->groups[i].vmods!=0) {
unsigned mask;
mask= XkbMaskForVMask(xkb,compat->groups[i].vmods);
compat->groups[i].mask= compat->groups[i].real_mods|mask;
}
else compat->groups[i].mask= compat->groups[i].real_mods;
}
return Success;
}
static void
XkbInitSemantics(XkbDescPtr xkb)
{
XkbInitKeyTypes(xkb);
XkbInitCompatStructs(xkb);
return;
}
static Status
XkbInitNames(XkbSrvInfoPtr xkbi)
{
XkbDescPtr xkb;
XkbNamesPtr names;
Status rtrn;
Atom unknown;
xkb= xkbi->desc;
if ((rtrn=XkbAllocNames(xkb,XkbAllNamesMask,0,0))!=Success)
return rtrn;
unknown= CREATE_ATOM("unknown");
names = xkb->names;
if (names->keycodes==None) names->keycodes= unknown;
if (names->geometry==None) names->geometry= unknown;
if (names->phys_symbols==None) names->phys_symbols= unknown;
if (names->symbols==None) names->symbols= unknown;
if (names->types==None) names->types= unknown;
if (names->compat==None) names->compat= unknown;
if (!(xkb->defined & XkmVirtualModsMask)) {
if (names->vmods[vmod_NumLock]==None)
names->vmods[vmod_NumLock]= CREATE_ATOM("NumLock");
if (names->vmods[vmod_Alt]==None)
names->vmods[vmod_Alt]= CREATE_ATOM("Alt");
if (names->vmods[vmod_AltGr]==None)
names->vmods[vmod_AltGr]= CREATE_ATOM("ModeSwitch");
}
if (!(xkb->defined & XkmIndicatorsMask) ||
!(xkb->defined & XkmGeometryMask)) {
initIndicatorNames(NULL,xkb);
if (names->indicators[LED_CAPS-1]==None)
names->indicators[LED_CAPS-1] = CREATE_ATOM("Caps Lock");
if (names->indicators[LED_NUM-1]==None)
names->indicators[LED_NUM-1] = CREATE_ATOM("Num Lock");
if (names->indicators[LED_SCROLL-1]==None)
names->indicators[LED_SCROLL-1] = CREATE_ATOM("Scroll Lock");
#ifdef LED_COMPOSE
if (names->indicators[LED_COMPOSE-1]==None)
names->indicators[LED_COMPOSE-1] = CREATE_ATOM("Compose");
#endif
}
if (xkb->geom!=NULL)
names->geometry= xkb->geom->name;
else names->geometry= unknown;
return Success;
}
static Status
XkbInitIndicatorMap(XkbSrvInfoPtr xkbi)
{
XkbDescPtr xkb;
XkbIndicatorPtr map;
XkbSrvLedInfoPtr sli;
xkb= xkbi->desc;
if (XkbAllocIndicatorMaps(xkb)!=Success)
return BadAlloc;
if (!(xkb->defined & XkmIndicatorsMask)) {
map= xkb->indicators;
map->phys_indicators = PHYS_LEDS;
map->maps[LED_CAPS-1].flags= XkbIM_NoExplicit;
map->maps[LED_CAPS-1].which_mods= XkbIM_UseLocked;
map->maps[LED_CAPS-1].mods.mask= LockMask;
map->maps[LED_CAPS-1].mods.real_mods= LockMask;
map->maps[LED_NUM-1].flags= XkbIM_NoExplicit;
map->maps[LED_NUM-1].which_mods= XkbIM_UseLocked;
map->maps[LED_NUM-1].mods.mask= 0;
map->maps[LED_NUM-1].mods.real_mods= 0;
map->maps[LED_NUM-1].mods.vmods= vmod_NumLockMask;
map->maps[LED_SCROLL-1].flags= XkbIM_NoExplicit;
map->maps[LED_SCROLL-1].which_mods= XkbIM_UseLocked;
map->maps[LED_SCROLL-1].mods.mask= Mod3Mask;
map->maps[LED_SCROLL-1].mods.real_mods= Mod3Mask;
}
sli= XkbFindSrvLedInfo(xkbi->device,XkbDfltXIClass,XkbDfltXIId,0);
if (sli)
XkbCheckIndicatorMaps(xkbi->device,sli,XkbAllIndicatorsMask);
return Success;
}
static Status
XkbInitControls(DeviceIntPtr pXDev,XkbSrvInfoPtr xkbi)
{
XkbDescPtr xkb;
XkbControlsPtr ctrls;
xkb= xkbi->desc;
if (XkbAllocControls(xkb,XkbAllControlsMask)!=Success)
FatalError("Couldn't allocate keyboard controls\n");
ctrls= xkb->ctrls;
if (!(xkb->defined & XkmSymbolsMask))
ctrls->num_groups = 1;
ctrls->groups_wrap = XkbSetGroupInfo(1,XkbWrapIntoRange,0);
ctrls->internal.mask = 0;
ctrls->internal.real_mods = 0;
ctrls->internal.vmods = 0;
ctrls->ignore_lock.mask = 0;
ctrls->ignore_lock.real_mods = 0;
ctrls->ignore_lock.vmods = 0;
ctrls->enabled_ctrls = XkbAccessXTimeoutMask|XkbRepeatKeysMask|
XkbMouseKeysAccelMask|XkbAudibleBellMask|
XkbIgnoreGroupLockMask;
if (XkbWantAccessX)
ctrls->enabled_ctrls|= XkbAccessXKeysMask;
AccessXInit(pXDev);
return Success;
}
_X_EXPORT Bool
InitKeyboardDeviceStruct(DeviceIntPtr dev, XkbRMLVOSet *rmlvo,
BellProcPtr bell_func, KbdCtrlProcPtr ctrl_func)
{
int i;
unsigned int check;
XkbSrvInfoPtr xkbi;
XkbDescPtr xkb;
XkbSrvLedInfoPtr sli;
XkbChangesRec changes;
XkbEventCauseRec cause;
XkbRMLVOSet rmlvo_dflts = { NULL };
if (dev->key || dev->kbdfeed)
return FALSE;
if (!rmlvo)
{
rmlvo = &rmlvo_dflts;
XkbGetRulesDflts(rmlvo);
}
memset(&changes, 0, sizeof(changes));
XkbSetCauseUnknown(&cause);
dev->key = calloc(1, sizeof(*dev->key));
if (!dev->key) {
ErrorF("XKB: Failed to allocate key class\n");
return FALSE;
}
dev->key->sourceid = dev->id;
dev->kbdfeed = calloc(1, sizeof(*dev->kbdfeed));
if (!dev->kbdfeed) {
ErrorF("XKB: Failed to allocate key feedback class\n");
goto unwind_key;
}
xkbi = calloc(1, sizeof(*xkbi));
if (!xkbi) {
ErrorF("XKB: Failed to allocate XKB info\n");
goto unwind_kbdfeed;
}
dev->key->xkbInfo = xkbi;
if (xkb_cached_map && !XkbCompareUsedRMLVO(rmlvo)) {
XkbFreeKeyboard(xkb_cached_map, XkbAllComponentsMask, TRUE);
xkb_cached_map = NULL;
}
if (xkb_cached_map)
LogMessageVerb(X_INFO, 4, "XKB: Reusing cached keymap\n");
else {
xkb_cached_map = XkbCompileKeymap(dev, rmlvo);
if (!xkb_cached_map) {
ErrorF("XKB: Failed to compile keymap\n");
goto unwind_info;
}
}
xkb = XkbAllocKeyboard();
if (!xkb) {
ErrorF("XKB: Failed to allocate keyboard description\n");
goto unwind_info;
}
if (!XkbCopyKeymap(xkb, xkb_cached_map)) {
ErrorF("XKB: Failed to copy keymap\n");
goto unwind_desc;
}
xkb->defined = xkb_cached_map->defined;
xkb->flags = xkb_cached_map->flags;
xkb->device_spec = xkb_cached_map->device_spec;
xkbi->desc = xkb;
if (xkb->min_key_code == 0)
xkb->min_key_code = 8;
if (xkb->max_key_code == 0)
xkb->max_key_code = 255;
i = XkbNumKeys(xkb) / 3 + 1;
if (XkbAllocClientMap(xkb, XkbAllClientInfoMask, 0) != Success)
goto unwind_desc;
if (XkbAllocServerMap(xkb, XkbAllServerInfoMask, i) != Success)
goto unwind_desc;
xkbi->dfltPtrDelta = 1;
xkbi->device = dev;
XkbInitSemantics(xkb);
XkbInitNames(xkbi);
XkbInitRadioGroups(xkbi);
XkbInitControls(dev, xkbi);
XkbInitIndicatorMap(xkbi);
XkbUpdateActions(dev, xkb->min_key_code, XkbNumKeys(xkb), &changes,
&check, &cause);
InitFocusClassDeviceStruct(dev);
xkbi->kbdProc = ctrl_func;
dev->kbdfeed->BellProc = bell_func;
dev->kbdfeed->CtrlProc = XkbDDXKeybdCtrlProc;
dev->kbdfeed->ctrl = defaultKeyboardControl;
if (dev->kbdfeed->ctrl.autoRepeat)
xkb->ctrls->enabled_ctrls |= XkbRepeatKeysMask;
memcpy(dev->kbdfeed->ctrl.autoRepeats, xkb->ctrls->per_key_repeat,
XkbPerKeyBitArraySize);
sli = XkbFindSrvLedInfo(dev, XkbDfltXIClass, XkbDfltXIId, 0);
if (sli)
XkbCheckIndicatorMaps(dev, sli, XkbAllIndicatorsMask);
else
DebugF("XKB: No indicator feedback in XkbFinishInit!\n");
dev->kbdfeed->CtrlProc(dev,&dev->kbdfeed->ctrl);
XkbSetRulesDflts(rmlvo);
XkbSetRulesUsed(rmlvo);
XkbFreeRMLVOSet(&rmlvo_dflts, FALSE);
return TRUE;
unwind_desc:
XkbFreeKeyboard(xkb, 0, TRUE);
unwind_info:
free(xkbi);
dev->key->xkbInfo = NULL;
unwind_kbdfeed:
free(dev->kbdfeed);
dev->kbdfeed = NULL;
unwind_key:
free(dev->key);
dev->key = NULL;
return FALSE;
}
void
XkbFreeInfo(XkbSrvInfoPtr xkbi)
{
free(xkbi->radioGroups);
xkbi->radioGroups = NULL;
if (xkbi->mouseKeyTimer) {
TimerFree(xkbi->mouseKeyTimer);
xkbi->mouseKeyTimer= NULL;
}
if (xkbi->slowKeysTimer) {
TimerFree(xkbi->slowKeysTimer);
xkbi->slowKeysTimer= NULL;
}
if (xkbi->bounceKeysTimer) {
TimerFree(xkbi->bounceKeysTimer);
xkbi->bounceKeysTimer= NULL;
}
if (xkbi->repeatKeyTimer) {
TimerFree(xkbi->repeatKeyTimer);
xkbi->repeatKeyTimer= NULL;
}
if (xkbi->krgTimer) {
TimerFree(xkbi->krgTimer);
xkbi->krgTimer= NULL;
}
xkbi->beepType= _BEEP_NONE;
if (xkbi->beepTimer) {
TimerFree(xkbi->beepTimer);
xkbi->beepTimer= NULL;
}
if (xkbi->desc) {
XkbFreeKeyboard(xkbi->desc,XkbAllComponentsMask,TRUE);
xkbi->desc= NULL;
}
free(xkbi);
return;
}
extern int XkbDfltRepeatDelay;
extern int XkbDfltRepeatInterval;
extern unsigned short XkbDfltAccessXTimeout;
extern unsigned int XkbDfltAccessXTimeoutMask;
extern unsigned int XkbDfltAccessXFeedback;
extern unsigned char XkbDfltAccessXOptions;
int
XkbProcessArguments(int argc,char *argv[],int i)
{
if (strncmp(argv[i], "-xkbdir", 7) == 0) {
if(++i < argc) {
#if !defined(WIN32) && !defined(__CYGWIN__)
if (getuid() != geteuid()) {
LogMessage(X_WARNING, "-xkbdir is not available for setuid X servers\n");
return -1;
} else
#endif
{
if (strlen(argv[i]) < PATH_MAX) {
XkbBaseDirectory= argv[i];
return 2;
} else {
LogMessage(X_ERROR, "-xkbdir pathname too long\n");
return -1;
}
}
}
else {
return -1;
}
}
else if ((strncmp(argv[i],"-accessx",8)==0)||
(strncmp(argv[i],"+accessx",8)==0)) {
int j=1;
if (argv[i][0]=='-')
XkbWantAccessX= 0;
else {
XkbWantAccessX= 1;
if ( ((i+1)<argc) && (isdigit(argv[i+1][0])) ) {
XkbDfltAccessXTimeout = atoi(argv[++i]);
j++;
if ( ((i+1)<argc) && (isdigit(argv[i+1][0])) ) {
XkbDfltAccessXTimeoutMask=(unsigned int)
strtol(argv[++i],NULL,16);
j++;
}
if ( ((i+1)<argc) && (isdigit(argv[i+1][0])) ) {
if (argv[++i][0] == '1' )
XkbDfltAccessXFeedback=XkbAccessXFeedbackMask;
else
XkbDfltAccessXFeedback=0;
j++;
}
if ( ((i+1)<argc) && (isdigit(argv[i+1][0])) ) {
XkbDfltAccessXOptions=(unsigned char)
strtol(argv[++i],NULL,16);
j++;
}
}
}
return j;
}
if ((strcmp(argv[i], "-ardelay") == 0) ||
(strcmp (argv[i], "-ar1") == 0)) {
if (++i >= argc) UseMsg ();
XkbDfltRepeatDelay = (long)atoi(argv[i]);
return 2;
}
if ((strcmp(argv[i], "-arinterval") == 0) ||
(strcmp (argv[i], "-ar2") == 0)) {
if (++i >= argc) UseMsg ();
XkbDfltRepeatInterval = (long)atoi(argv[i]);
return 2;
}
return 0;
}
void
XkbUseMsg(void)
{
ErrorF("[+-]accessx [ timeout [ timeout_mask [ feedback [ options_mask] ] ] ]\n");
ErrorF(" enable/disable accessx key sequences\n");
ErrorF("-ardelay set XKB autorepeat delay\n");
ErrorF("-arinterval set XKB autorepeat interval\n");
}