1/************************************************************
2 Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
3
4 Permission to use, copy, modify, and distribute this
5 software and its documentation for any purpose and without
6 fee is hereby granted, provided that the above copyright
7 notice appear in all copies and that both that copyright
8 notice and this permission notice appear in supporting
9 documentation, and that the name of Silicon Graphics not be
10 used in advertising or publicity pertaining to distribution
11 of the software without specific prior written permission.
12 Silicon Graphics makes no representation about the suitability
13 of this software for any purpose. It is provided "as is"
14 without any express or implied warranty.
15
16 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23 THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
25 ********************************************************/
26
27#ifdef HAVE_DIX_CONFIG_H
28#include <dix-config.h>
29#endif
30
31#include <stdio.h>
32#include <ctype.h>
33#include <stdlib.h>
34
35#include <X11/Xos.h>
36
37#include <X11/X.h>
38#include <X11/Xproto.h>
39#include <X11/extensions/XKMformat.h>
40#include "misc.h"
41#include "inputstr.h"
42#include "dix.h"
43#include "xkbstr.h"
44#define XKBSRV_NEED_FILE_FUNCS	1
45#include <xkbsrv.h>
46#include "xkbgeom.h"
47
48/***====================================================================***/
49
50#define	BUFFER_SIZE	512
51
52static char textBuffer[BUFFER_SIZE];
53static int  tbNext= 0;
54
55static char *
56tbGetBuffer(unsigned size)
57{
58char *rtrn;
59
60    if (size>=BUFFER_SIZE)
61	return NULL;
62    if ((BUFFER_SIZE-tbNext)<=size)
63	tbNext= 0;
64    rtrn= &textBuffer[tbNext];
65    tbNext+= size;
66    return rtrn;
67}
68
69/***====================================================================***/
70
71char *
72XkbAtomText(Atom atm,unsigned format)
73{
74const char	*atmstr;
75char	*rtrn,*tmp;
76
77    atmstr = NameForAtom(atm);
78    if (atmstr != NULL) {
79	int	len;
80	len= strlen(atmstr)+1;
81	if (len>BUFFER_SIZE)
82	    len= BUFFER_SIZE-2;
83	rtrn= tbGetBuffer(len);
84	strncpy(rtrn,atmstr,len);
85	rtrn[len]= '\0';
86    }
87    else {
88	rtrn= tbGetBuffer(1);
89	rtrn[0]= '\0';
90    }
91    if (format==XkbCFile) {
92	for (tmp=rtrn;*tmp!='\0';tmp++) {
93	    if ((tmp==rtrn)&&(!isalpha(*tmp)))
94		*tmp= '_';
95	    else if (!isalnum(*tmp))
96		*tmp= '_';
97	}
98    }
99    return XkbStringText(rtrn,format);
100}
101
102/***====================================================================***/
103
104char *
105XkbVModIndexText(XkbDescPtr xkb,unsigned ndx,unsigned format)
106{
107register int len;
108register Atom *vmodNames;
109char *rtrn;
110const char *tmp;
111char  numBuf[20];
112
113    if (xkb && xkb->names)
114	 vmodNames= xkb->names->vmods;
115    else vmodNames= NULL;
116
117    tmp= NULL;
118    if (ndx>=XkbNumVirtualMods)
119	 tmp= "illegal";
120    else if (vmodNames&&(vmodNames[ndx]!=None))
121	 tmp= NameForAtom(vmodNames[ndx]);
122    if (tmp==NULL) {
123	sprintf(numBuf,"%d",ndx);
124	tmp = numBuf;
125    }
126
127    len= strlen(tmp)+1;
128    if (format==XkbCFile)
129	len+= 4;
130    if (len>=BUFFER_SIZE)
131	len= BUFFER_SIZE-1;
132    rtrn= tbGetBuffer(len);
133    if (format==XkbCFile) {
134	 strcpy(rtrn,"vmod_");
135	 strncpy(&rtrn[5],tmp,len-4);
136    }
137    else strncpy(rtrn,tmp,len);
138    return rtrn;
139}
140
141char *
142XkbVModMaskText(        XkbDescPtr	xkb,
143			unsigned	modMask,
144			unsigned	mask,
145			unsigned	format)
146{
147register int i,bit;
148int	 len;
149char *mm,*rtrn;
150char *str,buf[BUFFER_SIZE];
151
152    if ((modMask==0)&&(mask==0)) {
153	rtrn= tbGetBuffer(5);
154	if (format==XkbCFile)
155	     sprintf(rtrn,"0");
156	else sprintf(rtrn,"none");
157	return rtrn;
158    }
159    if (modMask!=0)
160	 mm= XkbModMaskText(modMask,format);
161    else mm= NULL;
162
163    str= buf;
164    buf[0]= '\0';
165    if (mask) {
166	char *tmp;
167	for (i=0,bit=1;i<XkbNumVirtualMods;i++,bit<<=1) {
168	    if (mask&bit) {
169		tmp= XkbVModIndexText(xkb,i,format);
170		len= strlen(tmp)+1+(str==buf?0:1);
171		if (format==XkbCFile)
172		    len+= 4;
173		if ((str-(buf+len))<=BUFFER_SIZE) {
174		    if (str!=buf) {
175			if (format==XkbCFile)	*str++= '|';
176			else			*str++= '+';
177			len--;
178		    }
179		}
180		if (format==XkbCFile)
181		     sprintf(str,"%sMask",tmp);
182		else strcpy(str,tmp);
183		str= &str[len-1];
184	    }
185	}
186	str= buf;
187    }
188    else str= NULL;
189    if (mm)
190	len= strlen(mm);
191    else	len= 0;
192    if (str)
193	len+= strlen(str)+(mm==NULL?0:1);
194    if (len>=BUFFER_SIZE)
195	len= BUFFER_SIZE-1;
196    rtrn= tbGetBuffer(len+1);
197    rtrn[0]= '\0';
198
199    if (mm!=NULL) {
200	i= strlen(mm);
201	if (i>len)
202	    i= len;
203	strcpy(rtrn,mm);
204    }
205    else {
206	i=0;
207    }
208    if (str!=NULL) {
209	if (mm!=NULL) {
210	    if (format==XkbCFile)	strcat(rtrn,"|");
211	    else			strcat(rtrn,"+");
212	}
213	strncat(rtrn,str,len-i);
214    }
215    rtrn[len]= '\0';
216    return rtrn;
217}
218
219static char *modNames[XkbNumModifiers] = {
220    "Shift", "Lock", "Control", "Mod1", "Mod2", "Mod3", "Mod4", "Mod5"
221};
222
223char *
224XkbModIndexText(unsigned ndx,unsigned format)
225{
226char *	rtrn;
227char	buf[100];
228
229    if (format==XkbCFile) {
230	if (ndx<XkbNumModifiers)
231	     sprintf(buf,"%sMapIndex",modNames[ndx]);
232	else if (ndx==XkbNoModifier)
233	     sprintf(buf,"XkbNoModifier");
234	else sprintf(buf,"0x%02x",ndx);
235    }
236    else {
237	if (ndx<XkbNumModifiers)
238	     strcpy(buf,modNames[ndx]);
239	else if (ndx==XkbNoModifier)
240	     strcpy(buf,"none");
241	else sprintf(buf,"ILLEGAL_%02x",ndx);
242    }
243    rtrn= tbGetBuffer(strlen(buf)+1);
244    strcpy(rtrn,buf);
245    return rtrn;
246}
247
248char *
249XkbModMaskText(unsigned mask,unsigned format)
250{
251register int i,bit;
252char buf[64],*rtrn;
253
254    if ((mask&0xff)==0xff) {
255	if (format==XkbCFile) 		strcpy(buf,"0xff");
256	else				strcpy(buf,"all");
257    }
258    else if ((mask&0xff)==0) {
259	if (format==XkbCFile)		strcpy(buf,"0");
260	else				strcpy(buf,"none");
261    }
262    else {
263	char *str= buf;
264	buf[0]= '\0';
265	for (i=0,bit=1;i<XkbNumModifiers;i++,bit<<=1) {
266	    if (mask&bit) {
267		if (str!=buf) {
268		    if (format==XkbCFile)	*str++= '|';
269		    else			*str++= '+';
270		}
271		strcpy(str,modNames[i]);
272		str= &str[strlen(str)];
273		if (format==XkbCFile) {
274		    strcpy(str,"Mask");
275		    str+= 4;
276		}
277	    }
278	}
279    }
280    rtrn= tbGetBuffer(strlen(buf)+1);
281    strcpy(rtrn,buf);
282    return rtrn;
283}
284
285/***====================================================================***/
286
287/*ARGSUSED*/
288char *
289XkbConfigText(unsigned config,unsigned format)
290{
291static char *buf;
292
293    buf= tbGetBuffer(32);
294    switch (config) {
295	case XkmSemanticsFile:
296	    strcpy(buf,"Semantics");
297	    break;
298	case XkmLayoutFile:
299	    strcpy(buf,"Layout");
300	    break;
301	case XkmKeymapFile:
302	    strcpy(buf,"Keymap");
303	    break;
304	case XkmGeometryFile:
305	case XkmGeometryIndex:
306	    strcpy(buf,"Geometry");
307	    break;
308	case XkmTypesIndex:
309	    strcpy(buf,"Types");
310	    break;
311	case XkmCompatMapIndex:
312	    strcpy(buf,"CompatMap");
313	    break;
314	case XkmSymbolsIndex:
315	    strcpy(buf,"Symbols");
316	    break;
317	case XkmIndicatorsIndex:
318	    strcpy(buf,"Indicators");
319	    break;
320	case XkmKeyNamesIndex:
321	    strcpy(buf,"KeyNames");
322	    break;
323	case XkmVirtualModsIndex:
324	    strcpy(buf,"VirtualMods");
325	    break;
326	default:
327	    sprintf(buf,"unknown(%d)",config);
328	    break;
329    }
330    return buf;
331}
332
333/***====================================================================***/
334
335char *
336XkbKeysymText(KeySym sym,unsigned format)
337{
338static char buf[32],*rtrn;
339
340    if (sym==NoSymbol)
341	 strcpy(rtrn=buf,"NoSymbol");
342    else sprintf(rtrn=buf, "0x%lx", (long)sym);
343    return rtrn;
344}
345
346char *
347XkbKeyNameText(char *name,unsigned format)
348{
349char *buf;
350
351    if (format==XkbCFile) {
352	buf= tbGetBuffer(5);
353	memcpy(buf,name,4);
354	buf[4]= '\0';
355    }
356    else {
357	int len;
358	buf= tbGetBuffer(7);
359	buf[0]= '<';
360	memcpy(&buf[1],name,4);
361	buf[5]= '\0';
362	len= strlen(buf);
363	buf[len++]= '>';
364	buf[len]= '\0';
365    }
366    return buf;
367}
368
369/***====================================================================***/
370
371static char *siMatchText[5] = {
372	"NoneOf", "AnyOfOrNone", "AnyOf", "AllOf", "Exactly"
373};
374
375char *
376XkbSIMatchText(unsigned type,unsigned format)
377{
378static char buf[40];
379char *rtrn;
380
381    switch (type&XkbSI_OpMask) {
382	case XkbSI_NoneOf:	rtrn= siMatchText[0]; break;
383	case XkbSI_AnyOfOrNone:	rtrn= siMatchText[1]; break;
384	case XkbSI_AnyOf:	rtrn= siMatchText[2]; break;
385	case XkbSI_AllOf:	rtrn= siMatchText[3]; break;
386	case XkbSI_Exactly:	rtrn= siMatchText[4]; break;
387	default:		sprintf(buf,"0x%x",type&XkbSI_OpMask);
388				return buf;
389    }
390    if (format==XkbCFile) {
391	if (type&XkbSI_LevelOneOnly)
392	     sprintf(buf,"XkbSI_LevelOneOnly|XkbSI_%s",rtrn);
393	else sprintf(buf,"XkbSI_%s",rtrn);
394	rtrn= buf;
395    }
396    return rtrn;
397}
398
399/***====================================================================***/
400
401static char *imWhichNames[]= {
402	"base",
403	"latched",
404	"locked",
405	"effective",
406	"compat"
407};
408
409char *
410XkbIMWhichStateMaskText(unsigned use_which,unsigned format)
411{
412int		len;
413unsigned	i,bit,tmp;
414char *		buf;
415
416    if (use_which==0) {
417	buf= tbGetBuffer(2);
418	strcpy(buf,"0");
419	return buf;
420    }
421    tmp= use_which&XkbIM_UseAnyMods;
422    for (len=i=0,bit=1;tmp!=0;i++,bit<<=1) {
423	if (tmp&bit) {
424	    tmp&= ~bit;
425	    len+= strlen(imWhichNames[i])+1;
426	    if (format==XkbCFile)
427		len+= 9;
428	}
429    }
430    buf= tbGetBuffer(len+1);
431    tmp= use_which&XkbIM_UseAnyMods;
432    for (len=i=0,bit=1;tmp!=0;i++,bit<<=1) {
433	if (tmp&bit) {
434	    tmp&= ~bit;
435	    if (format==XkbCFile) {
436		if (len!=0)
437		    buf[len++]= '|';
438		sprintf(&buf[len],"XkbIM_Use%s",imWhichNames[i]);
439		buf[len+9]= toupper(buf[len+9]);
440	    }
441	    else {
442		if (len!=0)
443		    buf[len++]= '+';
444		sprintf(&buf[len],"%s",imWhichNames[i]);
445	    }
446	    len+= strlen(&buf[len]);
447	}
448    }
449    return buf;
450}
451
452static char *ctrlNames[] = {
453	"repeatKeys",
454	"slowKeys",
455	"bounceKeys",
456	"stickyKeys",
457	"mouseKeys",
458	"mouseKeysAccel",
459	"accessXKeys",
460	"accessXTimeout",
461	"accessXFeedback",
462	"audibleBell",
463	"overlay1",
464	"overlay2",
465	"ignoreGroupLock"
466};
467
468char *
469XkbControlsMaskText(unsigned ctrls,unsigned format)
470{
471int		len;
472unsigned	i,bit,tmp;
473char *		buf;
474
475    if (ctrls==0) {
476	buf= tbGetBuffer(5);
477	if (format==XkbCFile)
478	     strcpy(buf,"0");
479	else strcpy(buf,"none");
480	return buf;
481    }
482    tmp= ctrls&XkbAllBooleanCtrlsMask;
483    for (len=i=0,bit=1;tmp!=0;i++,bit<<=1) {
484	if (tmp&bit) {
485	    tmp&= ~bit;
486	    len+= strlen(ctrlNames[i])+1;
487	    if (format==XkbCFile)
488		len+= 7;
489	}
490    }
491    buf= tbGetBuffer(len+1);
492    tmp= ctrls&XkbAllBooleanCtrlsMask;
493    for (len=i=0,bit=1;tmp!=0;i++,bit<<=1) {
494	if (tmp&bit) {
495	    tmp&= ~bit;
496	    if (format==XkbCFile) {
497		if (len!=0)
498		    buf[len++]= '|';
499		sprintf(&buf[len],"Xkb%sMask",ctrlNames[i]);
500		buf[len+3]= toupper(buf[len+3]);
501	    }
502	    else {
503		if (len!=0)
504		    buf[len++]= '+';
505		sprintf(&buf[len],"%s",ctrlNames[i]);
506	    }
507	    len+= strlen(&buf[len]);
508	}
509    }
510    return buf;
511}
512
513/***====================================================================***/
514
515char *
516XkbStringText(char *str,unsigned format)
517{
518char *	buf;
519register char *in,*out;
520int	len;
521Bool	ok;
522
523    if (str==NULL) {
524	buf= tbGetBuffer(2);
525	buf[0]='\0';
526	return buf;
527    }
528    else if (format==XkbXKMFile)
529	return str;
530    for (ok= TRUE,len=0,in=str;*in!='\0';in++,len++) {
531	if (!isprint(*in)) {
532	    ok= FALSE;
533	    switch (*in) {
534		case '\n': case '\t': case '\v':
535		case '\b': case '\r': case '\f':
536		    len++;
537		    break;
538		default:
539		    len+= 4;
540		    break;
541	    }
542	}
543    }
544    if (ok)
545	return str;
546    buf= tbGetBuffer(len+1);
547    for (in=str,out=buf;*in!='\0';in++) {
548	if (isprint(*in))
549	    *out++= *in;
550	else {
551	    *out++= '\\';
552	    if (*in=='\n')	*out++= 'n';
553	    else if (*in=='\t')	*out++= 't';
554	    else if (*in=='\v')	*out++= 'v';
555	    else if (*in=='\b')	*out++= 'b';
556	    else if (*in=='\r')	*out++= 'r';
557	    else if (*in=='\f')	*out++= 'f';
558	    else if ((*in=='\033')&&(format==XkbXKMFile)) {
559		*out++= 'e';
560	    }
561	    else {
562		*out++= '0';
563		sprintf(out,"%o",*in);
564		while (*out!='\0')
565		    out++;
566	    }
567	}
568    }
569    *out++= '\0';
570    return buf;
571}
572
573/***====================================================================***/
574
575char *
576XkbGeomFPText(int val,unsigned format)
577{
578int	whole,frac;
579char *	buf;
580
581    buf= tbGetBuffer(12);
582    if (format==XkbCFile) {
583	sprintf(buf,"%d",val);
584    }
585    else {
586	whole= val/XkbGeomPtsPerMM;
587	frac= val%XkbGeomPtsPerMM;
588	if (frac!=0)
589	     sprintf(buf,"%d.%d",whole,frac);
590	else sprintf(buf,"%d",whole);
591    }
592    return buf;
593}
594
595char *
596XkbDoodadTypeText(unsigned type,unsigned format)
597{
598char *	buf;
599    if (format==XkbCFile) {
600	buf= tbGetBuffer(24);
601	if (type==XkbOutlineDoodad)	   strcpy(buf,"XkbOutlineDoodad");
602	else if (type==XkbSolidDoodad)	   strcpy(buf,"XkbSolidDoodad");
603	else if (type==XkbTextDoodad)	   strcpy(buf,"XkbTextDoodad");
604	else if (type==XkbIndicatorDoodad) strcpy(buf,"XkbIndicatorDoodad");
605	else if (type==XkbLogoDoodad)	   strcpy(buf,"XkbLogoDoodad");
606	else				   sprintf(buf,"UnknownDoodad%d",type);
607    }
608    else {
609	buf= tbGetBuffer(12);
610	if (type==XkbOutlineDoodad)	   strcpy(buf,"outline");
611	else if (type==XkbSolidDoodad)	   strcpy(buf,"solid");
612	else if (type==XkbTextDoodad)	   strcpy(buf,"text");
613	else if (type==XkbIndicatorDoodad) strcpy(buf,"indicator");
614	else if (type==XkbLogoDoodad)	   strcpy(buf,"logo");
615	else				   sprintf(buf,"unknown%d",type);
616    }
617    return buf;
618}
619
620static char *actionTypeNames[XkbSA_NumActions]= {
621    "NoAction",
622    "SetMods",      "LatchMods",    "LockMods",
623    "SetGroup",     "LatchGroup",   "LockGroup",
624    "MovePtr",
625    "PtrBtn",       "LockPtrBtn",
626    "SetPtrDflt",
627    "ISOLock",
628    "Terminate",    "SwitchScreen",
629    "SetControls",  "LockControls",
630    "ActionMessage",
631    "RedirectKey",
632    "DeviceBtn",    "LockDeviceBtn"
633};
634
635char *
636XkbActionTypeText(unsigned type,unsigned format)
637{
638static char buf[32];
639char *rtrn;
640
641    if (type<=XkbSA_LastAction) {
642	rtrn= actionTypeNames[type];
643	if (format==XkbCFile) {
644	    sprintf(buf,"XkbSA_%s",rtrn);
645	    return buf;
646	}
647	return rtrn;
648    }
649    sprintf(buf,"Private");
650    return buf;
651}
652
653/***====================================================================***/
654
655static int
656TryCopyStr(char *to,char *from,int *pLeft)
657{
658register int len;
659    if (*pLeft>0) {
660	len= strlen(from);
661	if (len<((*pLeft)-3)) {
662	    strcat(to,from);
663	    *pLeft-= len;
664	    return TRUE;
665	}
666    }
667    *pLeft= -1;
668    return FALSE;
669}
670
671/*ARGSUSED*/
672static Bool
673CopyNoActionArgs(XkbDescPtr xkb,XkbAction *action,char *buf,int*sz)
674{
675    return TRUE;
676}
677
678static Bool
679CopyModActionArgs(XkbDescPtr xkb,XkbAction *action,char *buf,
680								int* sz)
681{
682XkbModAction *	act;
683unsigned	tmp;
684
685    act= &action->mods;
686    tmp= XkbModActionVMods(act);
687    TryCopyStr(buf,"modifiers=",sz);
688    if (act->flags&XkbSA_UseModMapMods)
689	  TryCopyStr(buf,"modMapMods",sz);
690    else if (act->real_mods || tmp) {
691	 TryCopyStr(buf,
692		     XkbVModMaskText(xkb,act->real_mods,tmp,XkbXKBFile),
693		     sz);
694    }
695    else TryCopyStr(buf,"none",sz);
696    if (act->type==XkbSA_LockMods)
697	return TRUE;
698    if (act->flags&XkbSA_ClearLocks)
699	TryCopyStr(buf,",clearLocks",sz);
700    if (act->flags&XkbSA_LatchToLock)
701	TryCopyStr(buf,",latchToLock",sz);
702    return TRUE;
703}
704
705/*ARGSUSED*/
706static Bool
707CopyGroupActionArgs(XkbDescPtr xkb,XkbAction *action,char *buf,
708								int *sz)
709{
710XkbGroupAction *	act;
711char			tbuf[32];
712
713    act= &action->group;
714    TryCopyStr(buf,"group=",sz);
715    if (act->flags&XkbSA_GroupAbsolute)
716	 sprintf(tbuf,"%d",XkbSAGroup(act)+1);
717    else if (XkbSAGroup(act)<0)
718	 sprintf(tbuf,"%d",XkbSAGroup(act));
719    else sprintf(tbuf,"+%d",XkbSAGroup(act));
720    TryCopyStr(buf,tbuf,sz);
721    if (act->type==XkbSA_LockGroup)
722	return TRUE;
723    if (act->flags&XkbSA_ClearLocks)
724	TryCopyStr(buf,",clearLocks",sz);
725    if (act->flags&XkbSA_LatchToLock)
726	TryCopyStr(buf,",latchToLock",sz);
727    return TRUE;
728}
729
730/*ARGSUSED*/
731static Bool
732CopyMovePtrArgs(XkbDescPtr xkb,XkbAction *action,char *buf,int *sz)
733{
734XkbPtrAction *	act;
735int		x,y;
736char		tbuf[32];
737
738    act= &action->ptr;
739    x= XkbPtrActionX(act);
740    y= XkbPtrActionY(act);
741    if ((act->flags&XkbSA_MoveAbsoluteX)||(x<0))
742	 sprintf(tbuf,"x=%d",x);
743    else sprintf(tbuf,"x=+%d",x);
744    TryCopyStr(buf,tbuf,sz);
745
746    if ((act->flags&XkbSA_MoveAbsoluteY)||(y<0))
747	 sprintf(tbuf,",y=%d",y);
748    else sprintf(tbuf,",y=+%d",y);
749    TryCopyStr(buf,tbuf,sz);
750    if (act->flags&XkbSA_NoAcceleration)
751	TryCopyStr(buf,",!accel",sz);
752    return TRUE;
753}
754
755/*ARGSUSED*/
756static Bool
757CopyPtrBtnArgs(XkbDescPtr xkb,XkbAction *action,char *buf,int *sz)
758{
759XkbPtrBtnAction *	act;
760char			tbuf[32];
761
762    act= &action->btn;
763    TryCopyStr(buf,"button=",sz);
764    if ((act->button>0)&&(act->button<6)) {
765	 sprintf(tbuf,"%d",act->button);
766	 TryCopyStr(buf,tbuf,sz);
767    }
768    else TryCopyStr(buf,"default",sz);
769    if (act->count>0) {
770	sprintf(tbuf,",count=%d",act->count);
771	TryCopyStr(buf,tbuf,sz);
772    }
773    if (action->type==XkbSA_LockPtrBtn) {
774	switch (act->flags&(XkbSA_LockNoUnlock|XkbSA_LockNoLock)) {
775	    case XkbSA_LockNoLock:
776		sprintf(tbuf,",affect=unlock"); break;
777	    case XkbSA_LockNoUnlock:
778		sprintf(tbuf,",affect=lock"); break;
779	    case XkbSA_LockNoUnlock|XkbSA_LockNoLock:
780		sprintf(tbuf,",affect=neither"); break;
781	    default:
782		sprintf(tbuf,",affect=both"); break;
783	}
784	TryCopyStr(buf,tbuf,sz);
785    }
786    return TRUE;
787}
788
789/*ARGSUSED*/
790static Bool
791CopySetPtrDfltArgs(XkbDescPtr xkb,XkbAction *action,char *buf,
792								int *sz)
793{
794XkbPtrDfltAction *	act;
795char			tbuf[32];
796
797    act= &action->dflt;
798    if (act->affect==XkbSA_AffectDfltBtn) {
799	TryCopyStr(buf,"affect=button,button=",sz);
800	if ((act->flags&XkbSA_DfltBtnAbsolute)||(XkbSAPtrDfltValue(act)<0))
801	     sprintf(tbuf,"%d",XkbSAPtrDfltValue(act));
802	else sprintf(tbuf,"+%d",XkbSAPtrDfltValue(act));
803	TryCopyStr(buf,tbuf,sz);
804    }
805    return TRUE;
806}
807
808static Bool
809CopyISOLockArgs(XkbDescPtr xkb,XkbAction *action,char *buf,int *sz)
810{
811XkbISOAction *	act;
812char		tbuf[64];
813
814    act= &action->iso;
815    if (act->flags&XkbSA_ISODfltIsGroup) {
816	TryCopyStr(tbuf,"group=",sz);
817	if (act->flags&XkbSA_GroupAbsolute)
818	     sprintf(tbuf,"%d",XkbSAGroup(act)+1);
819	else if (XkbSAGroup(act)<0)
820	     sprintf(tbuf,"%d",XkbSAGroup(act));
821	else sprintf(tbuf,"+%d",XkbSAGroup(act));
822	TryCopyStr(buf,tbuf,sz);
823    }
824    else {
825	unsigned tmp;
826	tmp= XkbModActionVMods(act);
827	TryCopyStr(buf,"modifiers=",sz);
828	if (act->flags&XkbSA_UseModMapMods)
829	     TryCopyStr(buf,"modMapMods",sz);
830	else if (act->real_mods || tmp) {
831	    if (act->real_mods) {
832		TryCopyStr(buf,XkbModMaskText(act->real_mods,XkbXKBFile),sz);
833		if (tmp)
834		    TryCopyStr(buf,"+",sz);
835	    }
836	    if (tmp)
837		TryCopyStr(buf,XkbVModMaskText(xkb,0,tmp,XkbXKBFile),sz);
838	}
839	else TryCopyStr(buf,"none",sz);
840    }
841    TryCopyStr(buf,",affect=",sz);
842    if ((act->affect&XkbSA_ISOAffectMask)==0)
843	TryCopyStr(buf,"all",sz);
844    else {
845	int nOut= 0;
846	if ((act->affect&XkbSA_ISONoAffectMods)==0) {
847	    TryCopyStr(buf,"mods",sz);
848	    nOut++;
849	}
850	if ((act->affect&XkbSA_ISONoAffectGroup)==0) {
851	    sprintf(tbuf,"%sgroups",(nOut>0?"+":""));
852	    TryCopyStr(buf,tbuf,sz);
853	    nOut++;
854	}
855	if ((act->affect&XkbSA_ISONoAffectPtr)==0) {
856	    sprintf(tbuf,"%spointer",(nOut>0?"+":""));
857	    TryCopyStr(buf,tbuf,sz);
858	    nOut++;
859	}
860	if ((act->affect&XkbSA_ISONoAffectCtrls)==0) {
861	    sprintf(tbuf,"%scontrols",(nOut>0?"+":""));
862	    TryCopyStr(buf,tbuf,sz);
863	    nOut++;
864	}
865    }
866    return TRUE;
867}
868
869/*ARGSUSED*/
870static Bool
871CopySwitchScreenArgs(XkbDescPtr xkb,XkbAction *action,char *buf,
872								int *sz)
873{
874XkbSwitchScreenAction *	act;
875char			tbuf[32];
876
877    act= &action->screen;
878    if ((act->flags&XkbSA_SwitchAbsolute)||(XkbSAScreen(act)<0))
879	 sprintf(tbuf,"screen=%d",XkbSAScreen(act));
880    else sprintf(tbuf,"screen=+%d",XkbSAScreen(act));
881    TryCopyStr(buf,tbuf,sz);
882    if (act->flags&XkbSA_SwitchApplication)
883	 TryCopyStr(buf,",!same",sz);
884    else TryCopyStr(buf,",same",sz);
885    return TRUE;
886}
887
888/*ARGSUSED*/
889static Bool
890CopySetLockControlsArgs(XkbDescPtr xkb,XkbAction *action,
891							char *buf,int *sz)
892{
893XkbCtrlsAction *	act;
894unsigned		tmp;
895char			tbuf[32];
896
897    act= &action->ctrls;
898    tmp= XkbActionCtrls(act);
899    TryCopyStr(buf,"controls=",sz);
900    if (tmp==0)
901	TryCopyStr(buf,"none",sz);
902    else if ((tmp&XkbAllBooleanCtrlsMask)==XkbAllBooleanCtrlsMask)
903	TryCopyStr(buf,"all",sz);
904    else {
905	int nOut= 0;
906	if (tmp&XkbRepeatKeysMask) {
907	    sprintf(tbuf,"%sRepeatKeys",(nOut>0?"+":""));
908	    TryCopyStr(buf,tbuf,sz);
909	    nOut++;
910	}
911	if (tmp&XkbSlowKeysMask) {
912	    sprintf(tbuf,"%sSlowKeys",(nOut>0?"+":""));
913	    TryCopyStr(buf,tbuf,sz);
914	    nOut++;
915	}
916	if (tmp&XkbBounceKeysMask) {
917	    sprintf(tbuf,"%sBounceKeys",(nOut>0?"+":""));
918	    TryCopyStr(buf,tbuf,sz);
919	    nOut++;
920	}
921	if (tmp&XkbStickyKeysMask) {
922	    sprintf(tbuf,"%sStickyKeys",(nOut>0?"+":""));
923	    TryCopyStr(buf,tbuf,sz);
924	    nOut++;
925	}
926	if (tmp&XkbMouseKeysMask) {
927	    sprintf(tbuf,"%sMouseKeys",(nOut>0?"+":""));
928	    TryCopyStr(buf,tbuf,sz);
929	    nOut++;
930	}
931	if (tmp&XkbMouseKeysAccelMask) {
932	    sprintf(tbuf,"%sMouseKeysAccel",(nOut>0?"+":""));
933	    TryCopyStr(buf,tbuf,sz);
934	    nOut++;
935	}
936	if (tmp&XkbAccessXKeysMask) {
937	    sprintf(tbuf,"%sAccessXKeys",(nOut>0?"+":""));
938	    TryCopyStr(buf,tbuf,sz);
939	    nOut++;
940	}
941	if (tmp&XkbAccessXTimeoutMask) {
942	    sprintf(tbuf,"%sAccessXTimeout",(nOut>0?"+":""));
943	    TryCopyStr(buf,tbuf,sz);
944	    nOut++;
945	}
946	if (tmp&XkbAccessXFeedbackMask) {
947	    sprintf(tbuf,"%sAccessXFeedback",(nOut>0?"+":""));
948	    TryCopyStr(buf,tbuf,sz);
949	    nOut++;
950	}
951	if (tmp&XkbAudibleBellMask) {
952	    sprintf(tbuf,"%sAudibleBell",(nOut>0?"+":""));
953	    TryCopyStr(buf,tbuf,sz);
954	    nOut++;
955	}
956	if (tmp&XkbOverlay1Mask) {
957	    sprintf(tbuf,"%sOverlay1",(nOut>0?"+":""));
958	    TryCopyStr(buf,tbuf,sz);
959	    nOut++;
960	}
961	if (tmp&XkbOverlay2Mask) {
962	    sprintf(tbuf,"%sOverlay2",(nOut>0?"+":""));
963	    TryCopyStr(buf,tbuf,sz);
964	    nOut++;
965	}
966	if (tmp&XkbIgnoreGroupLockMask) {
967	    sprintf(tbuf,"%sIgnoreGroupLock",(nOut>0?"+":""));
968	    TryCopyStr(buf,tbuf,sz);
969	    nOut++;
970	}
971    }
972    return TRUE;
973}
974
975/*ARGSUSED*/
976static Bool
977CopyActionMessageArgs(XkbDescPtr xkb,XkbAction *action,char *buf,
978								int *sz)
979{
980XkbMessageAction *	act;
981unsigned		all;
982char			tbuf[32];
983
984    act= &action->msg;
985    all= XkbSA_MessageOnPress|XkbSA_MessageOnRelease;
986    TryCopyStr(buf,"report=",sz);
987    if ((act->flags&all)==0)
988	TryCopyStr(buf,"none",sz);
989    else if ((act->flags&all)==all)
990	TryCopyStr(buf,"all",sz);
991    else if (act->flags&XkbSA_MessageOnPress)
992	 TryCopyStr(buf,"KeyPress",sz);
993    else TryCopyStr(buf,"KeyRelease",sz);
994    sprintf(tbuf,",data[0]=0x%02x",act->message[0]); TryCopyStr(buf,tbuf,sz);
995    sprintf(tbuf,",data[1]=0x%02x",act->message[1]); TryCopyStr(buf,tbuf,sz);
996    sprintf(tbuf,",data[2]=0x%02x",act->message[2]); TryCopyStr(buf,tbuf,sz);
997    sprintf(tbuf,",data[3]=0x%02x",act->message[3]); TryCopyStr(buf,tbuf,sz);
998    sprintf(tbuf,",data[4]=0x%02x",act->message[4]); TryCopyStr(buf,tbuf,sz);
999    sprintf(tbuf,",data[5]=0x%02x",act->message[5]); TryCopyStr(buf,tbuf,sz);
1000    return TRUE;
1001}
1002
1003static Bool
1004CopyRedirectKeyArgs(XkbDescPtr xkb,XkbAction *action,char *buf,
1005								int *sz)
1006{
1007XkbRedirectKeyAction *	act;
1008char			tbuf[32],*tmp;
1009unsigned		kc;
1010unsigned		vmods,vmods_mask;
1011
1012    act= &action->redirect;
1013    kc= act->new_key;
1014    vmods= XkbSARedirectVMods(act);
1015    vmods_mask= XkbSARedirectVModsMask(act);
1016    if (xkb && xkb->names && xkb->names->keys && (kc<=xkb->max_key_code) &&
1017				(xkb->names->keys[kc].name[0]!='\0')) {
1018	char *kn;
1019	kn= XkbKeyNameText(xkb->names->keys[kc].name,XkbXKBFile);
1020	sprintf(tbuf,"key=%s",kn);
1021    }
1022    else sprintf(tbuf,"key=%d",kc);
1023    TryCopyStr(buf,tbuf,sz);
1024    if ((act->mods_mask==0)&&(vmods_mask==0))
1025	return TRUE;
1026    if ((act->mods_mask==XkbAllModifiersMask)&&
1027	(vmods_mask==XkbAllVirtualModsMask)) {
1028	tmp= XkbVModMaskText(xkb,act->mods,vmods,XkbXKBFile);
1029	TryCopyStr(buf,",mods=",sz);
1030	TryCopyStr(buf,tmp,sz);
1031    }
1032    else {
1033	if ((act->mods_mask&act->mods)||(vmods_mask&vmods)) {
1034	    tmp= XkbVModMaskText(xkb,act->mods_mask&act->mods,
1035					 vmods_mask&vmods,XkbXKBFile);
1036	    TryCopyStr(buf,",mods= ",sz);
1037	    TryCopyStr(buf,tmp,sz);
1038	}
1039	if ((act->mods_mask&(~act->mods))||(vmods_mask&(~vmods))) {
1040	    tmp= XkbVModMaskText(xkb,act->mods_mask&(~act->mods),
1041					 vmods_mask&(~vmods),XkbXKBFile);
1042	    TryCopyStr(buf,",clearMods= ",sz);
1043	    TryCopyStr(buf,tmp,sz);
1044	}
1045    }
1046    return TRUE;
1047}
1048
1049/*ARGSUSED*/
1050static Bool
1051CopyDeviceBtnArgs(XkbDescPtr xkb,XkbAction *action,char *buf,
1052								int *sz)
1053{
1054XkbDeviceBtnAction *	act;
1055char			tbuf[32];
1056
1057    act= &action->devbtn;
1058    sprintf(tbuf,"device= %d",act->device); TryCopyStr(buf,tbuf,sz);
1059    TryCopyStr(buf,",button=",sz);
1060    sprintf(tbuf,"%d",act->button);
1061    TryCopyStr(buf,tbuf,sz);
1062    if (act->count>0) {
1063	sprintf(tbuf,",count=%d",act->count);
1064	TryCopyStr(buf,tbuf,sz);
1065    }
1066    if (action->type==XkbSA_LockDeviceBtn) {
1067	switch (act->flags&(XkbSA_LockNoUnlock|XkbSA_LockNoLock)) {
1068	    case XkbSA_LockNoLock:
1069		sprintf(tbuf,",affect=unlock"); break;
1070	    case XkbSA_LockNoUnlock:
1071		sprintf(tbuf,",affect=lock"); break;
1072	    case XkbSA_LockNoUnlock|XkbSA_LockNoLock:
1073		sprintf(tbuf,",affect=neither"); break;
1074	    default:
1075		sprintf(tbuf,",affect=both"); break;
1076	}
1077	TryCopyStr(buf,tbuf,sz);
1078    }
1079    return TRUE;
1080}
1081
1082/*ARGSUSED*/
1083static Bool
1084CopyOtherArgs(XkbDescPtr xkb,XkbAction *action,char *buf,int *sz)
1085{
1086XkbAnyAction *	act;
1087char		tbuf[32];
1088
1089    act= &action->any;
1090    sprintf(tbuf,"type=0x%02x",act->type); TryCopyStr(buf,tbuf,sz);
1091    sprintf(tbuf,",data[0]=0x%02x",act->data[0]); TryCopyStr(buf,tbuf,sz);
1092    sprintf(tbuf,",data[1]=0x%02x",act->data[1]); TryCopyStr(buf,tbuf,sz);
1093    sprintf(tbuf,",data[2]=0x%02x",act->data[2]); TryCopyStr(buf,tbuf,sz);
1094    sprintf(tbuf,",data[3]=0x%02x",act->data[3]); TryCopyStr(buf,tbuf,sz);
1095    sprintf(tbuf,",data[4]=0x%02x",act->data[4]); TryCopyStr(buf,tbuf,sz);
1096    sprintf(tbuf,",data[5]=0x%02x",act->data[5]); TryCopyStr(buf,tbuf,sz);
1097    sprintf(tbuf,",data[6]=0x%02x",act->data[6]); TryCopyStr(buf,tbuf,sz);
1098    return TRUE;
1099}
1100
1101typedef	Bool	(*actionCopy)(
1102	XkbDescPtr 	/* xkb */,
1103	XkbAction *	/* action */,
1104	char *		/* buf */,
1105	int*		/* sz */
1106);
1107static actionCopy	copyActionArgs[XkbSA_NumActions] = {
1108	CopyNoActionArgs		/* NoAction	*/,
1109	CopyModActionArgs		/* SetMods	*/,
1110	CopyModActionArgs		/* LatchMods	*/,
1111	CopyModActionArgs		/* LockMods	*/,
1112	CopyGroupActionArgs		/* SetGroup	*/,
1113	CopyGroupActionArgs		/* LatchGroup	*/,
1114	CopyGroupActionArgs		/* LockGroup	*/,
1115	CopyMovePtrArgs			/* MovePtr	*/,
1116	CopyPtrBtnArgs			/* PtrBtn	*/,
1117	CopyPtrBtnArgs			/* LockPtrBtn	*/,
1118	CopySetPtrDfltArgs		/* SetPtrDflt	*/,
1119	CopyISOLockArgs			/* ISOLock	*/,
1120	CopyNoActionArgs		/* Terminate	*/,
1121	CopySwitchScreenArgs		/* SwitchScreen	*/,
1122	CopySetLockControlsArgs		/* SetControls	*/,
1123	CopySetLockControlsArgs		/* LockControls	*/,
1124	CopyActionMessageArgs		/* ActionMessage*/,
1125	CopyRedirectKeyArgs		/* RedirectKey	*/,
1126	CopyDeviceBtnArgs		/* DeviceBtn	*/,
1127	CopyDeviceBtnArgs		/* LockDeviceBtn*/
1128};
1129
1130#define	ACTION_SZ	256
1131
1132char *
1133XkbActionText(XkbDescPtr xkb,XkbAction *action,unsigned format)
1134{
1135char	buf[ACTION_SZ],*tmp;
1136int	sz;
1137
1138    if (format==XkbCFile) {
1139	sprintf(buf,
1140	    "{ %20s, { 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x } }",
1141	    XkbActionTypeText(action->type,XkbCFile),
1142	    action->any.data[0],action->any.data[1],action->any.data[2],
1143	    action->any.data[3],action->any.data[4],action->any.data[5],
1144	    action->any.data[6]);
1145    }
1146    else {
1147	sprintf(buf,"%s(",XkbActionTypeText(action->type,XkbXKBFile));
1148	sz= ACTION_SZ-strlen(buf)+2; /* room for close paren and NULL */
1149	if (action->type<(unsigned)XkbSA_NumActions)
1150	     (*copyActionArgs[action->type])(xkb,action,buf,&sz);
1151	else CopyOtherArgs(xkb,action,buf,&sz);
1152	TryCopyStr(buf,")",&sz);
1153    }
1154    tmp= tbGetBuffer(strlen(buf)+1);
1155    if (tmp!=NULL)
1156	strcpy(tmp,buf);
1157    return tmp;
1158}
1159
1160char *
1161XkbBehaviorText(XkbDescPtr xkb,XkbBehavior *behavior,unsigned format)
1162{
1163char	buf[256],*tmp;
1164
1165    if (format==XkbCFile) {
1166	if (behavior->type==XkbKB_Default)
1167	     sprintf(buf,"{   0,    0 }");
1168	else sprintf(buf,"{ %3d, 0x%02x }",behavior->type,behavior->data);
1169    }
1170    else {
1171	unsigned 	type,permanent;
1172	type= behavior->type&XkbKB_OpMask;
1173	permanent=((behavior->type&XkbKB_Permanent)!=0);
1174
1175	if (type==XkbKB_Lock) {
1176	    sprintf(buf,"lock= %s",(permanent?"Permanent":"TRUE"));
1177	}
1178	else if (type==XkbKB_RadioGroup) {
1179	    int 	g;
1180	    char	*tmp;
1181	    g= ((behavior->data)&(~XkbKB_RGAllowNone))+1;
1182	    if (XkbKB_RGAllowNone&behavior->data) {
1183		sprintf(buf,"allowNone,");
1184		tmp= &buf[strlen(buf)];
1185	    }
1186	    else tmp= buf;
1187	    if (permanent)
1188		 sprintf(tmp,"permanentRadioGroup= %d",g);
1189	    else sprintf(tmp,"radioGroup= %d",g);
1190	}
1191	else if ((type==XkbKB_Overlay1)||(type==XkbKB_Overlay2)) {
1192	    int ndx,kc;
1193	    char *kn;
1194
1195	    ndx= ((type==XkbKB_Overlay1)?1:2);
1196	    kc= behavior->data;
1197	    if ((xkb)&&(xkb->names)&&(xkb->names->keys))
1198		kn= XkbKeyNameText(xkb->names->keys[kc].name,XkbXKBFile);
1199	    else {
1200		static char tbuf[8];
1201		sprintf(tbuf,"%d",kc);
1202		kn= tbuf;
1203	    }
1204	    if (permanent)
1205		 sprintf(buf,"permanentOverlay%d= %s",ndx,kn);
1206	    else sprintf(buf,"overlay%d= %s",ndx,kn);
1207	}
1208    }
1209    tmp= tbGetBuffer(strlen(buf)+1);
1210    if (tmp!=NULL)
1211	strcpy(tmp,buf);
1212    return tmp;
1213}
1214
1215/***====================================================================***/
1216
1217char *
1218XkbIndentText(unsigned size)
1219{
1220static char buf[32];
1221register int i;
1222
1223    if (size>31)
1224	size= 31;
1225
1226    for (i=0;i<size;i++) {
1227	buf[i]= ' ';
1228    }
1229    buf[size]= '\0';
1230    return buf;
1231}
1232