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#elif defined(HAVE_CONFIG_H)
30#include <config.h>
31#endif
32
33#include <stdio.h>
34#include <ctype.h>
35#include <stdlib.h>
36
37#include <X11/Xos.h>
38
39
40#include <X11/Xlib.h>
41#include <X11/XKBlib.h>
42#include <X11/extensions/XKBgeom.h>
43
44#include "XKMformat.h"
45#include "XKBfileInt.h"
46
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{
58    char *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(Display *dpy, Atom atm, unsigned format)
73{
74    char *rtrn, *tmp;
75
76    tmp = XkbAtomGetString(dpy, atm);
77    if (tmp != NULL) {
78        int len;
79
80        len = strlen(tmp) + 1;
81        if (len > BUFFER_SIZE)
82            len = BUFFER_SIZE - 2;
83        rtrn = tbGetBuffer(len);
84        strncpy(rtrn, tmp, len);
85        rtrn[len] = '\0';
86        _XkbFree(tmp);
87    }
88    else {
89        rtrn = tbGetBuffer(1);
90        rtrn[0] = '\0';
91    }
92    if (format == XkbCFile) {
93        for (tmp = rtrn; *tmp != '\0'; tmp++) {
94            if ((tmp == rtrn) && (!isalpha(*tmp)))
95                *tmp = '_';
96            else if (!isalnum(*tmp))
97                *tmp = '_';
98        }
99    }
100    return XkbStringText(rtrn, format);
101}
102
103/***====================================================================***/
104
105char *
106XkbVModIndexText(Display *dpy, XkbDescPtr xkb, unsigned ndx, unsigned format)
107{
108    register int len;
109    register Atom *vmodNames;
110    char *rtrn, *tmp;
111
112    if (xkb && xkb->names)
113        vmodNames = xkb->names->vmods;
114    else
115        vmodNames = NULL;
116
117    tmp = NULL;
118    if (ndx >= XkbNumVirtualMods)
119        tmp = strdup("illegal");
120    else if (vmodNames && (vmodNames[ndx] != None))
121        tmp = XkbAtomGetString(dpy, vmodNames[ndx]);
122    if (tmp == NULL) {
123        tmp = (char *) _XkbAlloc(20 * sizeof(char));
124        snprintf(tmp, 20, "%d", ndx);
125    }
126
127    len = strlen(tmp) + 1;
128    if (format == XkbCFile)
129        len += 5;
130    if (len >= BUFFER_SIZE)
131        len = BUFFER_SIZE - 1;
132    rtrn = tbGetBuffer(len);
133    if (format == XkbCFile) {
134        snprintf(rtrn, len, "vmod_%s", tmp);
135    }
136    else
137        strncpy(rtrn, tmp, len);
138    _XkbFree(tmp);
139    return rtrn;
140}
141
142char *
143XkbVModMaskText(Display *       dpy,
144                XkbDescPtr      xkb,
145                unsigned        modMask,
146                unsigned        mask,
147                unsigned        format)
148{
149    register int i, bit;
150    int len;
151    char *mm, *rtrn;
152    char *str, buf[BUFFER_SIZE];
153
154    if ((modMask == 0) && (mask == 0)) {
155        const int rtrnsize = 5;
156        rtrn = tbGetBuffer(rtrnsize);
157        if (format == XkbCFile)
158            snprintf(rtrn, rtrnsize, "0");
159        else
160            snprintf(rtrn, rtrnsize, "none");
161        return rtrn;
162    }
163    if (modMask != 0)
164        mm = XkbModMaskText(modMask, format);
165    else
166        mm = NULL;
167
168    str = buf;
169    buf[0] = '\0';
170    if (mask) {
171        char *tmp;
172
173        for (i = 0, bit = 1; i < XkbNumVirtualMods; i++, bit <<= 1) {
174            if (mask & bit) {
175                tmp = XkbVModIndexText(dpy, xkb, i, format);
176                len = strlen(tmp) + 1 + (str == buf ? 0 : 1);
177                if (format == XkbCFile)
178                    len += 4;
179                if ((str - (buf + len)) <= BUFFER_SIZE) {
180                    if (str != buf) {
181                        if (format == XkbCFile)
182                            *str++ = '|';
183                        else
184                            *str++ = '+';
185                        len--;
186                    }
187                }
188                if (format == XkbCFile)
189                    sprintf(str, "%sMask", tmp);
190                else
191                    strcpy(str, tmp);
192                str = &str[len - 1];
193            }
194        }
195        str = buf;
196    }
197    else
198        str = NULL;
199    if (mm)
200        len = strlen(mm);
201    else
202        len = 0;
203    if (str)
204        len += strlen(str) + (mm == NULL ? 0 : 1);
205    if (len >= BUFFER_SIZE)
206        len = BUFFER_SIZE - 1;
207    rtrn = tbGetBuffer(len + 1);
208    rtrn[0] = '\0';
209
210    if (mm != NULL) {
211        i = strlen(mm);
212        if (i > len)
213            i = len;
214        strcpy(rtrn, mm);
215    }
216    else {
217        i = 0;
218    }
219    if (str != NULL) {
220        if (mm != NULL) {
221            if (format == XkbCFile)
222                strcat(rtrn, "|");
223            else
224                strcat(rtrn, "+");
225        }
226        strncat(rtrn, str, len - i);
227    }
228    rtrn[len] = '\0';
229    return rtrn;
230}
231
232static const char *modNames[XkbNumModifiers] = {
233    "Shift", "Lock", "Control", "Mod1", "Mod2", "Mod3", "Mod4", "Mod5"
234};
235
236char *
237XkbModIndexText(unsigned ndx, unsigned format)
238{
239    char *rtrn;
240    char buf[100];
241
242    if (format == XkbCFile) {
243        if (ndx < XkbNumModifiers)
244            snprintf(buf, sizeof(buf), "%sMapIndex", modNames[ndx]);
245        else if (ndx == XkbNoModifier)
246            snprintf(buf, sizeof(buf), "XkbNoModifier");
247        else
248            snprintf(buf, sizeof(buf), "0x%02x", ndx);
249    }
250    else {
251        if (ndx < XkbNumModifiers)
252            strcpy(buf, modNames[ndx]);
253        else if (ndx == XkbNoModifier)
254            strcpy(buf, "none");
255        else
256            snprintf(buf, sizeof(buf), "ILLEGAL_%02x", ndx);
257    }
258    rtrn = tbGetBuffer(strlen(buf) + 1);
259    strcpy(rtrn, buf);
260    return rtrn;
261}
262
263char *
264XkbModMaskText(unsigned mask, unsigned format)
265{
266    register int i, bit;
267    char buf[64], *rtrn;
268
269    if ((mask & 0xff) == 0xff) {
270        if (format == XkbCFile)
271            strcpy(buf, "0xff");
272        else
273            strcpy(buf, "all");
274    }
275    else if ((mask & 0xff) == 0) {
276        if (format == XkbCFile)
277            strcpy(buf, "0");
278        else
279            strcpy(buf, "none");
280    }
281    else {
282        char *str = buf;
283
284        buf[0] = '\0';
285        for (i = 0, bit = 1; i < XkbNumModifiers; i++, bit <<= 1) {
286            if (mask & bit) {
287                if (str != buf) {
288                    if (format == XkbCFile)
289                        *str++ = '|';
290                    else
291                        *str++ = '+';
292                }
293                strcpy(str, modNames[i]);
294                str = &str[strlen(str)];
295                if (format == XkbCFile) {
296                    strcpy(str, "Mask");
297                    str += 4;
298                }
299            }
300        }
301    }
302    rtrn = tbGetBuffer(strlen(buf) + 1);
303    strcpy(rtrn, buf);
304    return rtrn;
305}
306
307/***====================================================================***/
308
309/*ARGSUSED*/
310char *
311XkbConfigText(unsigned config, unsigned format)
312{
313    static char *buf;
314    const int bufsize = 32;
315
316    buf = tbGetBuffer(bufsize);
317    switch (config) {
318    case XkmSemanticsFile:
319        strcpy(buf, "Semantics");
320        break;
321    case XkmLayoutFile:
322        strcpy(buf, "Layout");
323        break;
324    case XkmKeymapFile:
325        strcpy(buf, "Keymap");
326        break;
327    case XkmGeometryFile:
328    case XkmGeometryIndex:
329        strcpy(buf, "Geometry");
330        break;
331    case XkmTypesIndex:
332        strcpy(buf, "Types");
333        break;
334    case XkmCompatMapIndex:
335        strcpy(buf, "CompatMap");
336        break;
337    case XkmSymbolsIndex:
338        strcpy(buf, "Symbols");
339        break;
340    case XkmIndicatorsIndex:
341        strcpy(buf, "Indicators");
342        break;
343    case XkmKeyNamesIndex:
344        strcpy(buf, "KeyNames");
345        break;
346    case XkmVirtualModsIndex:
347        strcpy(buf, "VirtualMods");
348        break;
349    default:
350        snprintf(buf, bufsize, "unknown(%d)", config);
351        break;
352    }
353    return buf;
354}
355
356/***====================================================================***/
357
358char *
359XkbKeysymText(KeySym sym, unsigned format)
360{
361    static char buf[32], *rtrn;
362
363    if (sym == NoSymbol)
364        strcpy(rtrn = buf, "NoSymbol");
365    else if ((rtrn = XKeysymToString(sym)) == NULL) {
366        snprintf(buf, sizeof(buf), "0x%lx", (long) sym);
367        rtrn = buf;
368    }
369    else if (format == XkbCFile) {
370        snprintf(buf, sizeof(buf), "XK_%s", rtrn);
371        rtrn = buf;
372    }
373    return rtrn;
374}
375
376char *
377XkbKeyNameText(char *name, unsigned format)
378{
379    char *buf;
380
381    if (format == XkbCFile) {
382        buf = tbGetBuffer(5);
383        memcpy(buf, name, 4);
384        buf[4] = '\0';
385    }
386    else {
387        int len;
388
389        buf = tbGetBuffer(7);
390        buf[0] = '<';
391        memcpy(&buf[1], name, 4);
392        buf[5] = '\0';
393        len = strlen(buf);
394        buf[len++] = '>';
395        buf[len] = '\0';
396    }
397    return buf;
398}
399
400/***====================================================================***/
401
402static char *siMatchText[5] = {
403    "NoneOf", "AnyOfOrNone", "AnyOf", "AllOf", "Exactly"
404};
405
406char *
407XkbSIMatchText(unsigned type, unsigned format)
408{
409    static char buf[40];
410
411    char *rtrn;
412
413    switch (type & XkbSI_OpMask) {
414    case XkbSI_NoneOf:      rtrn = siMatchText[0]; break;
415    case XkbSI_AnyOfOrNone: rtrn = siMatchText[1]; break;
416    case XkbSI_AnyOf:       rtrn = siMatchText[2]; break;
417    case XkbSI_AllOf:       rtrn = siMatchText[3]; break;
418    case XkbSI_Exactly:     rtrn = siMatchText[4]; break;
419    default:
420        snprintf(buf, sizeof(buf), "0x%x", type & XkbSI_OpMask);
421        return buf;
422    }
423    if (format == XkbCFile) {
424        if (type & XkbSI_LevelOneOnly)
425            snprintf(buf, sizeof(buf), "XkbSI_LevelOneOnly|XkbSI_%s", rtrn);
426        else
427            snprintf(buf, sizeof(buf), "XkbSI_%s", rtrn);
428        rtrn = buf;
429    }
430    return rtrn;
431}
432
433/***====================================================================***/
434
435static const char *imWhichNames[] = {
436    "base",
437    "latched",
438    "locked",
439    "effective",
440    "compat"
441};
442
443char *
444XkbIMWhichStateMaskText(unsigned use_which, unsigned format)
445{
446    int len, bufsize;
447    unsigned i, bit, tmp;
448    char *buf;
449
450    if (use_which == 0) {
451        buf = tbGetBuffer(2);
452        strcpy(buf, "0");
453        return buf;
454    }
455    tmp = use_which & XkbIM_UseAnyMods;
456    for (len = i = 0, bit = 1; tmp != 0; i++, bit <<= 1) {
457        if (tmp & bit) {
458            tmp &= ~bit;
459            len += strlen(imWhichNames[i]) + 1;
460            if (format == XkbCFile)
461                len += 9;
462        }
463    }
464    bufsize = len + 1;
465    buf = tbGetBuffer(bufsize);
466    tmp = use_which & XkbIM_UseAnyMods;
467    for (len = i = 0, bit = 1; tmp != 0; i++, bit <<= 1) {
468        if (tmp & bit) {
469            tmp &= ~bit;
470            if (format == XkbCFile) {
471                if (len != 0)
472                    buf[len++] = '|';
473                snprintf(&buf[len], bufsize - len,
474                         "XkbIM_Use%s", imWhichNames[i]);
475                buf[len + 9] = toupper(buf[len + 9]);
476            }
477            else {
478                if (len != 0)
479                    buf[len++] = '+';
480                snprintf(&buf[len], bufsize - len,
481                         "%s", imWhichNames[i]);
482            }
483            len += strlen(&buf[len]);
484        }
485    }
486    return buf;
487}
488
489char *
490XkbAccessXDetailText(unsigned state, unsigned format)
491{
492    char *buf;
493    const char *prefix;
494    const int bufsize = 32;
495
496    buf = tbGetBuffer(bufsize);
497    if (format == XkbMessage)
498        prefix = "";
499    else
500        prefix = "XkbAXN_";
501    switch (state) {
502    case XkbAXN_SKPress:
503        snprintf(buf, bufsize, "%sSKPress", prefix);
504        break;
505    case XkbAXN_SKAccept:
506        snprintf(buf, bufsize, "%sSKAccept", prefix);
507        break;
508    case XkbAXN_SKRelease:
509        snprintf(buf, bufsize, "%sSKRelease", prefix);
510        break;
511    case XkbAXN_SKReject:
512        snprintf(buf, bufsize, "%sSKReject", prefix);
513        break;
514    case XkbAXN_BKAccept:
515        snprintf(buf, bufsize, "%sBKAccept", prefix);
516        break;
517    case XkbAXN_BKReject:
518        snprintf(buf, bufsize, "%sBKReject", prefix);
519        break;
520    case XkbAXN_AXKWarning:
521        snprintf(buf, bufsize, "%sAXKWarning", prefix);
522        break;
523    default:
524        snprintf(buf, bufsize, "ILLEGAL");
525        break;
526    }
527    return buf;
528}
529
530static const char *nknNames[] = {
531    "keycodes", "geometry", "deviceID"
532};
533#define	NUM_NKN	(sizeof(nknNames)/sizeof(char *))
534
535char *
536XkbNKNDetailMaskText(unsigned detail, unsigned format)
537{
538    char *buf;
539    const char *prefix, *suffix;
540    register int i;
541    register unsigned bit;
542    int len, plen, slen;
543
544    if ((detail & XkbAllNewKeyboardEventsMask) == 0) {
545        const char *tmp = "";
546
547        if (format == XkbCFile)
548            tmp = "0";
549        else if (format == XkbMessage)
550            tmp = "none";
551        buf = tbGetBuffer(strlen(tmp) + 1);
552        strcpy(buf, tmp);
553        return buf;
554    }
555    else if ((detail & XkbAllNewKeyboardEventsMask) ==
556             XkbAllNewKeyboardEventsMask) {
557        const char *tmp;
558
559        if (format == XkbCFile)
560            tmp = "XkbAllNewKeyboardEventsMask";
561        else
562            tmp = "all";
563        buf = tbGetBuffer(strlen(tmp) + 1);
564        strcpy(buf, tmp);
565        return buf;
566    }
567    if (format == XkbMessage) {
568        prefix = "";
569        suffix = "";
570        slen = plen = 0;
571    }
572    else {
573        prefix = "XkbNKN_";
574        plen = 7;
575        if (format == XkbCFile)
576            suffix = "Mask";
577        else
578            suffix = "";
579        slen = strlen(suffix);
580    }
581    for (len = 0, i = 0, bit = 1; i < NUM_NKN; i++, bit <<= 1) {
582        if (detail & bit) {
583            if (len != 0)
584                len += 1;       /* room for '+' or '|' */
585            len += plen + slen + strlen(nknNames[i]);
586        }
587    }
588    buf = tbGetBuffer(len + 1);
589    buf[0] = '\0';
590    for (len = 0, i = 0, bit = 1; i < NUM_NKN; i++, bit <<= 1) {
591        if (detail & bit) {
592            if (len != 0) {
593                if (format == XkbCFile)
594                    buf[len++] = '|';
595                else
596                    buf[len++] = '+';
597            }
598            if (plen) {
599                strcpy(&buf[len], prefix);
600                len += plen;
601            }
602            strcpy(&buf[len], nknNames[i]);
603            len += strlen(nknNames[i]);
604            if (slen) {
605                strcpy(&buf[len], suffix);
606                len += slen;
607            }
608        }
609    }
610    buf[len++] = '\0';
611    return buf;
612}
613
614static const char *ctrlNames[] = {
615    "repeatKeys",
616    "slowKeys",
617    "bounceKeys",
618    "stickyKeys",
619    "mouseKeys",
620    "mouseKeysAccel",
621    "accessXKeys",
622    "accessXTimeout",
623    "accessXFeedback",
624    "audibleBell",
625    "overlay1",
626    "overlay2",
627    "ignoreGroupLock"
628};
629
630char *
631XkbControlsMaskText(unsigned ctrls, unsigned format)
632{
633    int len;
634    unsigned i, bit, tmp;
635    char *buf;
636
637    if (ctrls == 0) {
638        buf = tbGetBuffer(5);
639        if (format == XkbCFile)
640            strcpy(buf, "0");
641        else
642            strcpy(buf, "none");
643        return buf;
644    }
645    tmp = ctrls & XkbAllBooleanCtrlsMask;
646    for (len = i = 0, bit = 1; tmp != 0; i++, bit <<= 1) {
647        if (tmp & bit) {
648            tmp &= ~bit;
649            len += strlen(ctrlNames[i]) + 1;
650            if (format == XkbCFile)
651                len += 7;
652        }
653    }
654    buf = tbGetBuffer(len + 1);
655    tmp = ctrls & XkbAllBooleanCtrlsMask;
656    for (len = i = 0, bit = 1; tmp != 0; i++, bit <<= 1) {
657        if (tmp & bit) {
658            tmp &= ~bit;
659            if (format == XkbCFile) {
660                if (len != 0)
661                    buf[len++] = '|';
662                sprintf(&buf[len], "Xkb%sMask", ctrlNames[i]);
663                buf[len + 3] = toupper(buf[len + 3]);
664            }
665            else {
666                if (len != 0)
667                    buf[len++] = '+';
668                sprintf(&buf[len], "%s", ctrlNames[i]);
669            }
670            len += strlen(&buf[len]);
671        }
672    }
673    return buf;
674}
675
676/***====================================================================***/
677
678char *
679XkbStringText(char *str, unsigned format)
680{
681    char *buf;
682    register char *in, *out;
683    int len;
684    Bool ok;
685
686    if (str == NULL) {
687        buf = tbGetBuffer(2);
688        buf[0] = '\0';
689        return buf;
690    }
691    else if (format == XkbXKMFile)
692        return str;
693    for (ok = True, len = 0, in = str; *in != '\0'; in++, len++) {
694        if (!isprint(*in)) {
695            ok = False;
696            switch (*in) {
697            case '\n':
698            case '\t':
699            case '\v':
700            case '\b':
701            case '\r':
702            case '\f':
703                len++;
704                break;
705            default:
706                len += 4;
707                break;
708            }
709        }
710    }
711    if (ok)
712        return str;
713    buf = tbGetBuffer(len + 1);
714    for (in = str, out = buf; *in != '\0'; in++) {
715        if (isprint(*in))
716            *out++ = *in;
717        else {
718            *out++ = '\\';
719            if (*in == '\n')
720                *out++ = 'n';
721            else if (*in == '\t')
722                *out++ = 't';
723            else if (*in == '\v')
724                *out++ = 'v';
725            else if (*in == '\b')
726                *out++ = 'b';
727            else if (*in == '\r')
728                *out++ = 'r';
729            else if (*in == '\f')
730                *out++ = 'f';
731            else if ((*in == '\033') && (format == XkbXKMFile)) {
732                *out++ = 'e';
733            }
734            else {
735                *out++ = '0';
736                sprintf(out, "%o", (unsigned char)*in);
737                while (*out != '\0')
738                    out++;
739            }
740        }
741    }
742    *out++ = '\0';
743    return buf;
744}
745
746/***====================================================================***/
747
748char *
749XkbGeomFPText(int val, unsigned format)
750{
751    int whole, frac;
752    char *buf;
753    const int bufsize = 12;
754
755    buf = tbGetBuffer(bufsize);
756    if (format == XkbCFile) {
757        snprintf(buf, bufsize, "%d", val);
758    }
759    else {
760        whole = val / XkbGeomPtsPerMM;
761        frac = abs(val % XkbGeomPtsPerMM);
762        if (frac != 0) {
763            if (val < 0)
764            {
765                int wholeabs;
766                wholeabs = abs(whole);
767                snprintf(buf, bufsize, "-%d.%d", wholeabs, frac);
768            }
769            else
770                snprintf(buf, bufsize, "%d.%d", whole, frac);
771        }
772        else
773            snprintf(buf, bufsize, "%d", whole);
774    }
775    return buf;
776}
777
778char *
779XkbDoodadTypeText(unsigned type, unsigned format)
780{
781    char *buf;
782
783    if (format == XkbCFile) {
784        const int bufsize = 24;
785        buf = tbGetBuffer(bufsize);
786        if (type == XkbOutlineDoodad)
787            strcpy(buf, "XkbOutlineDoodad");
788        else if (type == XkbSolidDoodad)
789            strcpy(buf, "XkbSolidDoodad");
790        else if (type == XkbTextDoodad)
791            strcpy(buf, "XkbTextDoodad");
792        else if (type == XkbIndicatorDoodad)
793            strcpy(buf, "XkbIndicatorDoodad");
794        else if (type == XkbLogoDoodad)
795            strcpy(buf, "XkbLogoDoodad");
796        else
797            snprintf(buf, bufsize, "UnknownDoodad%d", type);
798    }
799    else {
800        const int bufsize = 12;
801        buf = tbGetBuffer(bufsize);
802        if (type == XkbOutlineDoodad)
803            strcpy(buf, "outline");
804        else if (type == XkbSolidDoodad)
805            strcpy(buf, "solid");
806        else if (type == XkbTextDoodad)
807            strcpy(buf, "text");
808        else if (type == XkbIndicatorDoodad)
809            strcpy(buf, "indicator");
810        else if (type == XkbLogoDoodad)
811            strcpy(buf, "logo");
812        else
813            snprintf(buf, bufsize, "unknown%d", type);
814    }
815    return buf;
816}
817
818static char *actionTypeNames[XkbSA_NumActions] = {
819    "NoAction",
820    "SetMods",      "LatchMods",    "LockMods",
821    "SetGroup",     "LatchGroup",   "LockGroup",
822    "MovePtr",
823    "PtrBtn",       "LockPtrBtn",
824    "SetPtrDflt",
825    "ISOLock",
826    "Terminate",    "SwitchScreen",
827    "SetControls",  "LockControls",
828    "ActionMessage",
829    "RedirectKey",
830    "DeviceBtn",    "LockDeviceBtn"
831};
832
833char *
834XkbActionTypeText(unsigned type, unsigned format)
835{
836    static char buf[32];
837    char *rtrn;
838
839    if (type <= XkbSA_LastAction) {
840        rtrn = actionTypeNames[type];
841        if (format == XkbCFile) {
842            snprintf(buf, sizeof(buf), "XkbSA_%s", rtrn);
843            return buf;
844        }
845        return rtrn;
846    }
847    snprintf(buf, sizeof(buf), "Private");
848    return buf;
849}
850
851/***====================================================================***/
852
853static int
854TryCopyStr(char *to, const char *from, int *pLeft)
855{
856    register int len;
857
858    if (*pLeft > 0) {
859        len = strlen(from);
860        if (len < ((*pLeft) - 3)) {
861            strcat(to, from);
862            *pLeft -= len;
863            return True;
864        }
865    }
866    *pLeft = -1;
867    return False;
868}
869
870/*ARGSUSED*/
871static Bool
872CopyNoActionArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
873                 char *buf, int *sz)
874{
875    return True;
876}
877
878static Bool
879CopyModActionArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
880                  char *buf, int *sz)
881{
882    XkbModAction *act;
883    unsigned tmp;
884
885    act = &action->mods;
886    tmp = XkbModActionVMods(act);
887    TryCopyStr(buf, "modifiers=", sz);
888    if (act->flags & XkbSA_UseModMapMods)
889        TryCopyStr(buf, "modMapMods", sz);
890    else if (act->real_mods || tmp) {
891        TryCopyStr(buf,
892                   XkbVModMaskText(dpy, xkb, act->real_mods, tmp, XkbXKBFile),
893                   sz);
894    }
895    else
896        TryCopyStr(buf, "none", sz);
897    if (act->type == XkbSA_LockMods) {
898        switch (act->flags & (XkbSA_LockNoUnlock | XkbSA_LockNoLock)) {
899        case XkbSA_LockNoLock:
900            TryCopyStr(buf, ",affect=unlock", sz);
901            break;
902        case XkbSA_LockNoUnlock:
903            TryCopyStr(buf, ",affect=lock", sz);
904            break;
905        case XkbSA_LockNoUnlock|XkbSA_LockNoLock:
906            TryCopyStr(buf, ",affect=neither", sz);
907            break;
908        default:
909            break;
910        }
911        return True;
912    }
913    if (act->flags & XkbSA_ClearLocks)
914        TryCopyStr(buf, ",clearLocks", sz);
915    if (act->flags & XkbSA_LatchToLock)
916        TryCopyStr(buf, ",latchToLock", sz);
917    return True;
918}
919
920/*ARGSUSED*/
921static Bool
922CopyGroupActionArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
923                    char *buf, int *sz)
924{
925    XkbGroupAction *act;
926    char tbuf[32];
927
928    act = &action->group;
929    TryCopyStr(buf, "group=", sz);
930    if (act->flags & XkbSA_GroupAbsolute)
931        snprintf(tbuf, sizeof(tbuf), "%d", XkbSAGroup(act) + 1);
932    else if (XkbSAGroup(act) < 0)
933        snprintf(tbuf, sizeof(tbuf), "%d", XkbSAGroup(act));
934    else
935        snprintf(tbuf, sizeof(tbuf), "+%d", XkbSAGroup(act));
936    TryCopyStr(buf, tbuf, sz);
937    if (act->type == XkbSA_LockGroup)
938        return True;
939    if (act->flags & XkbSA_ClearLocks)
940        TryCopyStr(buf, ",clearLocks", sz);
941    if (act->flags & XkbSA_LatchToLock)
942        TryCopyStr(buf, ",latchToLock", sz);
943    return True;
944}
945
946/*ARGSUSED*/
947static Bool
948CopyMovePtrArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
949                char *buf, int *sz)
950{
951    XkbPtrAction *act;
952    int x, y;
953    char tbuf[32];
954
955    act = &action->ptr;
956    x = XkbPtrActionX(act);
957    y = XkbPtrActionY(act);
958    if ((act->flags & XkbSA_MoveAbsoluteX) || (x < 0))
959        snprintf(tbuf, sizeof(tbuf), "x=%d", x);
960    else
961        snprintf(tbuf, sizeof(tbuf), "x=+%d", x);
962    TryCopyStr(buf, tbuf, sz);
963
964    if ((act->flags & XkbSA_MoveAbsoluteY) || (y < 0))
965        snprintf(tbuf, sizeof(tbuf), ",y=%d", y);
966    else
967        snprintf(tbuf, sizeof(tbuf), ",y=+%d", y);
968    TryCopyStr(buf, tbuf, sz);
969    if (act->flags & XkbSA_NoAcceleration)
970        TryCopyStr(buf, ",!accel", sz);
971    return True;
972}
973
974/*ARGSUSED*/
975static Bool
976CopyPtrBtnArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
977               char *buf, int *sz)
978{
979    XkbPtrBtnAction *act;
980    char tbuf[32];
981
982    act = &action->btn;
983    TryCopyStr(buf, "button=", sz);
984    if ((act->button > 0) && (act->button < 6)) {
985        snprintf(tbuf, sizeof(tbuf), "%d", act->button);
986        TryCopyStr(buf, tbuf, sz);
987    }
988    else
989        TryCopyStr(buf, "default", sz);
990    if (act->count > 0) {
991        snprintf(tbuf, sizeof(tbuf), ",count=%d", act->count);
992        TryCopyStr(buf, tbuf, sz);
993    }
994    if (action->type == XkbSA_LockPtrBtn) {
995        switch (act->flags & (XkbSA_LockNoUnlock | XkbSA_LockNoLock)) {
996        case XkbSA_LockNoLock:
997            snprintf(tbuf, sizeof(tbuf), ",affect=unlock");
998            break;
999        case XkbSA_LockNoUnlock:
1000            snprintf(tbuf, sizeof(tbuf), ",affect=lock");
1001            break;
1002        case XkbSA_LockNoUnlock | XkbSA_LockNoLock:
1003            snprintf(tbuf, sizeof(tbuf), ",affect=neither");
1004            break;
1005        default:
1006            snprintf(tbuf, sizeof(tbuf), ",affect=both");
1007            break;
1008        }
1009        TryCopyStr(buf, tbuf, sz);
1010    }
1011    return True;
1012}
1013
1014/*ARGSUSED*/
1015static Bool
1016CopySetPtrDfltArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
1017                   char *buf, int *sz)
1018{
1019    XkbPtrDfltAction *act;
1020    char tbuf[32];
1021
1022    act = &action->dflt;
1023    if (act->affect == XkbSA_AffectDfltBtn) {
1024        TryCopyStr(buf, "affect=button,button=", sz);
1025        if ((act->flags & XkbSA_DfltBtnAbsolute) ||
1026            (XkbSAPtrDfltValue(act) < 0))
1027            snprintf(tbuf, sizeof(tbuf), "%d", XkbSAPtrDfltValue(act));
1028        else
1029            snprintf(tbuf, sizeof(tbuf), "+%d", XkbSAPtrDfltValue(act));
1030        TryCopyStr(buf, tbuf, sz);
1031    }
1032    return True;
1033}
1034
1035static Bool
1036CopyISOLockArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
1037                char *buf, int *sz)
1038{
1039    XkbISOAction *act;
1040    char tbuf[64];
1041
1042    act = &action->iso;
1043    if (act->flags & XkbSA_ISODfltIsGroup) {
1044        TryCopyStr(tbuf, "group=", sz);
1045        if (act->flags & XkbSA_GroupAbsolute)
1046            snprintf(tbuf, sizeof(tbuf), "%d", XkbSAGroup(act) + 1);
1047        else if (XkbSAGroup(act) < 0)
1048            snprintf(tbuf, sizeof(tbuf), "%d", XkbSAGroup(act));
1049        else
1050            snprintf(tbuf, sizeof(tbuf), "+%d", XkbSAGroup(act));
1051        TryCopyStr(buf, tbuf, sz);
1052    }
1053    else {
1054        unsigned tmp;
1055
1056        tmp = XkbModActionVMods(act);
1057        TryCopyStr(buf, "modifiers=", sz);
1058        if (act->flags & XkbSA_UseModMapMods)
1059            TryCopyStr(buf, "modMapMods", sz);
1060        else if (act->real_mods || tmp) {
1061            if (act->real_mods) {
1062                TryCopyStr(buf, XkbModMaskText(act->real_mods, XkbXKBFile), sz);
1063                if (tmp)
1064                    TryCopyStr(buf, "+", sz);
1065            }
1066            if (tmp)
1067                TryCopyStr(buf, XkbVModMaskText(dpy, xkb, 0, tmp, XkbXKBFile),
1068                           sz);
1069        }
1070        else
1071            TryCopyStr(buf, "none", sz);
1072    }
1073    TryCopyStr(buf, ",affect=", sz);
1074    if ((act->affect & XkbSA_ISOAffectMask) == 0) {
1075        TryCopyStr(buf, "all", sz);
1076    }
1077    else if ((act->affect & XkbSA_ISOAffectMask) == XkbSA_ISOAffectMask) {
1078        TryCopyStr(buf, "none", sz);
1079    }
1080    else {
1081        int nOut = 0;
1082
1083        if ((act->affect & XkbSA_ISONoAffectMods) == 0) {
1084            TryCopyStr(buf, "mods", sz);
1085            nOut++;
1086        }
1087        if ((act->affect & XkbSA_ISONoAffectGroup) == 0) {
1088            snprintf(tbuf, sizeof(tbuf), "%sgroups", (nOut > 0 ? "+" : ""));
1089            TryCopyStr(buf, tbuf, sz);
1090            nOut++;
1091        }
1092        if ((act->affect & XkbSA_ISONoAffectPtr) == 0) {
1093            snprintf(tbuf, sizeof(tbuf), "%spointer", (nOut > 0 ? "+" : ""));
1094            TryCopyStr(buf, tbuf, sz);
1095            nOut++;
1096        }
1097        if ((act->affect & XkbSA_ISONoAffectCtrls) == 0) {
1098            snprintf(tbuf, sizeof(tbuf), "%scontrols", (nOut > 0 ? "+" : ""));
1099            TryCopyStr(buf, tbuf, sz);
1100            nOut++;
1101        }
1102    }
1103    switch (act->flags & (XkbSA_LockNoUnlock | XkbSA_LockNoLock)) {
1104    case XkbSA_LockNoLock:
1105        TryCopyStr(buf, "+unlock", sz);
1106        break;
1107    case XkbSA_LockNoUnlock:
1108        TryCopyStr(buf, "+lock", sz);
1109        break;
1110    case XkbSA_LockNoUnlock | XkbSA_LockNoLock:
1111        TryCopyStr(buf, "+neither", sz);
1112        break;
1113    default: ;
1114    }
1115    return True;
1116}
1117
1118/*ARGSUSED*/
1119static Bool
1120CopySwitchScreenArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
1121                     char *buf, int *sz)
1122{
1123    XkbSwitchScreenAction *act;
1124    char tbuf[32];
1125
1126    act = &action->screen;
1127    if ((act->flags & XkbSA_SwitchAbsolute) || (XkbSAScreen(act) < 0))
1128        snprintf(tbuf, sizeof(tbuf), "screen=%d", XkbSAScreen(act));
1129    else
1130        snprintf(tbuf, sizeof(tbuf), "screen=+%d", XkbSAScreen(act));
1131    TryCopyStr(buf, tbuf, sz);
1132    if (act->flags & XkbSA_SwitchApplication)
1133        TryCopyStr(buf, ",!same", sz);
1134    else
1135        TryCopyStr(buf, ",same", sz);
1136    return True;
1137}
1138
1139/*ARGSUSED*/
1140static Bool
1141CopySetLockControlsArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
1142                        char *buf, int *sz)
1143{
1144    XkbCtrlsAction *act;
1145    unsigned tmp;
1146    char tbuf[32];
1147
1148    act = &action->ctrls;
1149    tmp = XkbActionCtrls(act);
1150    TryCopyStr(buf, "controls=", sz);
1151    if (tmp == 0)
1152        TryCopyStr(buf, "none", sz);
1153    else if ((tmp & XkbAllBooleanCtrlsMask) == XkbAllBooleanCtrlsMask)
1154        TryCopyStr(buf, "all", sz);
1155    else {
1156        int nOut = 0;
1157
1158        if (tmp & XkbRepeatKeysMask) {
1159            snprintf(tbuf, sizeof(tbuf), "%sRepeatKeys", (nOut > 0 ? "+" : ""));
1160            TryCopyStr(buf, tbuf, sz);
1161            nOut++;
1162        }
1163        if (tmp & XkbSlowKeysMask) {
1164            snprintf(tbuf, sizeof(tbuf), "%sSlowKeys", (nOut > 0 ? "+" : ""));
1165            TryCopyStr(buf, tbuf, sz);
1166            nOut++;
1167        }
1168        if (tmp & XkbBounceKeysMask) {
1169            snprintf(tbuf, sizeof(tbuf), "%sBounceKeys", (nOut > 0 ? "+" : ""));
1170            TryCopyStr(buf, tbuf, sz);
1171            nOut++;
1172        }
1173        if (tmp & XkbStickyKeysMask) {
1174            snprintf(tbuf, sizeof(tbuf), "%sStickyKeys", (nOut > 0 ? "+" : ""));
1175            TryCopyStr(buf, tbuf, sz);
1176            nOut++;
1177        }
1178        if (tmp & XkbMouseKeysMask) {
1179            snprintf(tbuf, sizeof(tbuf), "%sMouseKeys", (nOut > 0 ? "+" : ""));
1180            TryCopyStr(buf, tbuf, sz);
1181            nOut++;
1182        }
1183        if (tmp & XkbMouseKeysAccelMask) {
1184            snprintf(tbuf, sizeof(tbuf), "%sMouseKeysAccel", (nOut > 0 ? "+" : ""));
1185            TryCopyStr(buf, tbuf, sz);
1186            nOut++;
1187        }
1188        if (tmp & XkbAccessXKeysMask) {
1189            snprintf(tbuf, sizeof(tbuf), "%sAccessXKeys", (nOut > 0 ? "+" : ""));
1190            TryCopyStr(buf, tbuf, sz);
1191            nOut++;
1192        }
1193        if (tmp & XkbAccessXTimeoutMask) {
1194            snprintf(tbuf, sizeof(tbuf), "%sAccessXTimeout", (nOut > 0 ? "+" : ""));
1195            TryCopyStr(buf, tbuf, sz);
1196            nOut++;
1197        }
1198        if (tmp & XkbAccessXFeedbackMask) {
1199            snprintf(tbuf, sizeof(tbuf), "%sAccessXFeedback", (nOut > 0 ? "+" : ""));
1200            TryCopyStr(buf, tbuf, sz);
1201            nOut++;
1202        }
1203        if (tmp & XkbAudibleBellMask) {
1204            snprintf(tbuf, sizeof(tbuf), "%sAudibleBell", (nOut > 0 ? "+" : ""));
1205            TryCopyStr(buf, tbuf, sz);
1206            nOut++;
1207        }
1208        if (tmp & XkbOverlay1Mask) {
1209            snprintf(tbuf, sizeof(tbuf), "%sOverlay1", (nOut > 0 ? "+" : ""));
1210            TryCopyStr(buf, tbuf, sz);
1211            nOut++;
1212        }
1213        if (tmp & XkbOverlay2Mask) {
1214            snprintf(tbuf, sizeof(tbuf), "%sOverlay2", (nOut > 0 ? "+" : ""));
1215            TryCopyStr(buf, tbuf, sz);
1216            nOut++;
1217        }
1218        if (tmp & XkbIgnoreGroupLockMask) {
1219            snprintf(tbuf, sizeof(tbuf), "%sIgnoreGroupLock", (nOut > 0 ? "+" : ""));
1220            TryCopyStr(buf, tbuf, sz);
1221            nOut++;
1222        }
1223    }
1224    if (action->type == XkbSA_LockControls) {
1225        switch (act->flags & (XkbSA_LockNoUnlock | XkbSA_LockNoLock)) {
1226        case XkbSA_LockNoLock:
1227            TryCopyStr(buf, ",affect=unlock", sz);
1228            break;
1229        case XkbSA_LockNoUnlock:
1230            TryCopyStr(buf, ",affect=lock", sz);
1231            break;
1232        case XkbSA_LockNoUnlock | XkbSA_LockNoLock:
1233            TryCopyStr(buf, ",affect=neither", sz);
1234            break;
1235        default: ;
1236        }
1237    }
1238    return True;
1239}
1240
1241/*ARGSUSED*/
1242static Bool
1243CopyActionMessageArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
1244                      char *buf, int *sz)
1245{
1246    XkbMessageAction *act;
1247    unsigned all;
1248    char tbuf[32];
1249
1250    act = &action->msg;
1251    all = XkbSA_MessageOnPress | XkbSA_MessageOnRelease;
1252    TryCopyStr(buf, "report=", sz);
1253    if ((act->flags & all) == 0)
1254        TryCopyStr(buf, "none", sz);
1255    else if ((act->flags & all) == all)
1256        TryCopyStr(buf, "all", sz);
1257    else if (act->flags & XkbSA_MessageOnPress)
1258        TryCopyStr(buf, "KeyPress", sz);
1259    else
1260        TryCopyStr(buf, "KeyRelease", sz);
1261    snprintf(tbuf, sizeof(tbuf), ",data[0]=0x%02x", act->message[0]);
1262    TryCopyStr(buf, tbuf, sz);
1263    snprintf(tbuf, sizeof(tbuf), ",data[1]=0x%02x", act->message[1]);
1264    TryCopyStr(buf, tbuf, sz);
1265    snprintf(tbuf, sizeof(tbuf), ",data[2]=0x%02x", act->message[2]);
1266    TryCopyStr(buf, tbuf, sz);
1267    snprintf(tbuf, sizeof(tbuf), ",data[3]=0x%02x", act->message[3]);
1268    TryCopyStr(buf, tbuf, sz);
1269    snprintf(tbuf, sizeof(tbuf), ",data[4]=0x%02x", act->message[4]);
1270    TryCopyStr(buf, tbuf, sz);
1271    snprintf(tbuf, sizeof(tbuf), ",data[5]=0x%02x", act->message[5]);
1272    TryCopyStr(buf, tbuf, sz);
1273    if (act->flags & XkbSA_MessageGenKeyEvent)
1274        TryCopyStr(buf, ",genKeyEvent", sz);
1275    return True;
1276}
1277
1278static Bool
1279CopyRedirectKeyArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
1280                    char *buf, int *sz)
1281{
1282    XkbRedirectKeyAction *act;
1283    char tbuf[32], *tmp;
1284    unsigned kc;
1285    unsigned vmods, vmods_mask;
1286
1287    act = &action->redirect;
1288    kc = act->new_key;
1289    vmods = XkbSARedirectVMods(act);
1290    vmods_mask = XkbSARedirectVModsMask(act);
1291    if (xkb && xkb->names && xkb->names->keys && (kc <= xkb->max_key_code) &&
1292        (xkb->names->keys[kc].name[0] != '\0')) {
1293        char *kn;
1294
1295        kn = XkbKeyNameText(xkb->names->keys[kc].name, XkbXKBFile);
1296        snprintf(tbuf, sizeof(tbuf), "key=%s", kn);
1297    }
1298    else
1299        snprintf(tbuf, sizeof(tbuf), "key=%d", kc);
1300    TryCopyStr(buf, tbuf, sz);
1301    if ((act->mods_mask == 0) && (vmods_mask == 0))
1302        return True;
1303    if ((act->mods_mask == XkbAllModifiersMask) &&
1304        (vmods_mask == XkbAllVirtualModsMask)) {
1305        tmp = XkbVModMaskText(dpy, xkb, act->mods, vmods, XkbXKBFile);
1306        TryCopyStr(buf, ",mods=", sz);
1307        TryCopyStr(buf, tmp, sz);
1308    }
1309    else {
1310        if ((act->mods_mask & act->mods) || (vmods_mask & vmods)) {
1311            tmp = XkbVModMaskText(dpy, xkb, act->mods_mask & act->mods,
1312                                  vmods_mask & vmods, XkbXKBFile);
1313            TryCopyStr(buf, ",mods= ", sz);
1314            TryCopyStr(buf, tmp, sz);
1315        }
1316        if ((act->mods_mask & (~act->mods)) || (vmods_mask & (~vmods))) {
1317            tmp = XkbVModMaskText(dpy, xkb, act->mods_mask & (~act->mods),
1318                                  vmods_mask & (~vmods), XkbXKBFile);
1319            TryCopyStr(buf, ",clearMods= ", sz);
1320            TryCopyStr(buf, tmp, sz);
1321        }
1322    }
1323    return True;
1324}
1325
1326/*ARGSUSED*/
1327static Bool
1328CopyDeviceBtnArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
1329                  char *buf, int *sz)
1330{
1331    XkbDeviceBtnAction *act;
1332    char tbuf[32];
1333
1334    act = &action->devbtn;
1335    snprintf(tbuf, sizeof(tbuf), "device= %d", act->device);
1336    TryCopyStr(buf, tbuf, sz);
1337    TryCopyStr(buf, ",button=", sz);
1338    snprintf(tbuf, sizeof(tbuf), "%d", act->button);
1339    TryCopyStr(buf, tbuf, sz);
1340    if (act->count > 0) {
1341        snprintf(tbuf, sizeof(tbuf), ",count=%d", act->count);
1342        TryCopyStr(buf, tbuf, sz);
1343    }
1344    if (action->type == XkbSA_LockDeviceBtn) {
1345        switch (act->flags & (XkbSA_LockNoUnlock | XkbSA_LockNoLock)) {
1346        case XkbSA_LockNoLock:
1347            snprintf(tbuf, sizeof(tbuf), ",affect=unlock");
1348            break;
1349        case XkbSA_LockNoUnlock:
1350            snprintf(tbuf, sizeof(tbuf), ",affect=lock");
1351            break;
1352        case XkbSA_LockNoUnlock | XkbSA_LockNoLock:
1353            snprintf(tbuf, sizeof(tbuf), ",affect=neither");
1354            break;
1355        default:
1356            snprintf(tbuf, sizeof(tbuf), ",affect=both");
1357            break;
1358        }
1359        TryCopyStr(buf, tbuf, sz);
1360    }
1361    return True;
1362}
1363
1364/*ARGSUSED*/
1365static Bool
1366CopyOtherArgs(Display *dpy, XkbDescPtr xkb, XkbAction *action,
1367              char *buf, int *sz)
1368{
1369    XkbAnyAction *act;
1370    char tbuf[32];
1371
1372    act = &action->any;
1373    snprintf(tbuf, sizeof(tbuf), "type=0x%02x", act->type);
1374    TryCopyStr(buf, tbuf, sz);
1375    snprintf(tbuf, sizeof(tbuf), ",data[0]=0x%02x", act->data[0]);
1376    TryCopyStr(buf, tbuf, sz);
1377    snprintf(tbuf, sizeof(tbuf), ",data[1]=0x%02x", act->data[1]);
1378    TryCopyStr(buf, tbuf, sz);
1379    snprintf(tbuf, sizeof(tbuf), ",data[2]=0x%02x", act->data[2]);
1380    TryCopyStr(buf, tbuf, sz);
1381    snprintf(tbuf, sizeof(tbuf), ",data[3]=0x%02x", act->data[3]);
1382    TryCopyStr(buf, tbuf, sz);
1383    snprintf(tbuf, sizeof(tbuf), ",data[4]=0x%02x", act->data[4]);
1384    TryCopyStr(buf, tbuf, sz);
1385    snprintf(tbuf, sizeof(tbuf), ",data[5]=0x%02x", act->data[5]);
1386    TryCopyStr(buf, tbuf, sz);
1387    snprintf(tbuf, sizeof(tbuf), ",data[6]=0x%02x", act->data[6]);
1388    TryCopyStr(buf, tbuf, sz);
1389    return True;
1390}
1391
1392typedef Bool (*actionCopy) (Display *   /* dpy */ ,
1393                            XkbDescPtr  /* xkb */ ,
1394                            XkbAction * /* action */ ,
1395                            char *      /* buf */ ,
1396                            int *       /* sz */
1397    );
1398
1399static actionCopy copyActionArgs[XkbSA_NumActions] = {
1400    CopyNoActionArgs            /* NoAction     */ ,
1401    CopyModActionArgs           /* SetMods      */ ,
1402    CopyModActionArgs           /* LatchMods    */ ,
1403    CopyModActionArgs           /* LockMods     */ ,
1404    CopyGroupActionArgs         /* SetGroup     */ ,
1405    CopyGroupActionArgs         /* LatchGroup   */ ,
1406    CopyGroupActionArgs         /* LockGroup    */ ,
1407    CopyMovePtrArgs             /* MovePtr      */ ,
1408    CopyPtrBtnArgs              /* PtrBtn       */ ,
1409    CopyPtrBtnArgs              /* LockPtrBtn   */ ,
1410    CopySetPtrDfltArgs          /* SetPtrDflt   */ ,
1411    CopyISOLockArgs             /* ISOLock      */ ,
1412    CopyNoActionArgs            /* Terminate    */ ,
1413    CopySwitchScreenArgs        /* SwitchScreen */ ,
1414    CopySetLockControlsArgs     /* SetControls  */ ,
1415    CopySetLockControlsArgs     /* LockControls */ ,
1416    CopyActionMessageArgs       /* ActionMessage */ ,
1417    CopyRedirectKeyArgs         /* RedirectKey  */ ,
1418    CopyDeviceBtnArgs           /* DeviceBtn    */ ,
1419    CopyDeviceBtnArgs           /* LockDeviceBtn */
1420};
1421
1422#define	ACTION_SZ	256
1423
1424char *
1425XkbActionText(Display *dpy, XkbDescPtr xkb, XkbAction *action, unsigned format)
1426{
1427    char buf[ACTION_SZ], *tmp;
1428    int sz;
1429
1430    if (format == XkbCFile) {
1431        snprintf(buf, sizeof(buf),
1432                "{ %20s, { 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x } }",
1433                XkbActionTypeText(action->type, XkbCFile),
1434                action->any.data[0], action->any.data[1], action->any.data[2],
1435                action->any.data[3], action->any.data[4], action->any.data[5],
1436                action->any.data[6]);
1437    }
1438    else {
1439        snprintf(buf, sizeof(buf),
1440                 "%s(", XkbActionTypeText(action->type, XkbXKBFile));
1441        sz = ACTION_SZ - strlen(buf) + 2;       /* room for close paren and NULL */
1442        if (action->type < (unsigned) XkbSA_NumActions)
1443            (*copyActionArgs[action->type]) (dpy, xkb, action, buf, &sz);
1444        else
1445            CopyOtherArgs(dpy, xkb, action, buf, &sz);
1446        TryCopyStr(buf, ")", &sz);
1447    }
1448    tmp = tbGetBuffer(strlen(buf) + 1);
1449    if (tmp != NULL)
1450        strcpy(tmp, buf);
1451    return tmp;
1452}
1453
1454char *
1455XkbBehaviorText(XkbDescPtr xkb, XkbBehavior * behavior, unsigned format)
1456{
1457    char buf[256], *tmp;
1458
1459    if (format == XkbCFile) {
1460        if (behavior->type == XkbKB_Default)
1461            snprintf(buf, sizeof(buf), "{   0,    0 }");
1462        else
1463            snprintf(buf, sizeof(buf), "{ %3d, 0x%02x }", behavior->type, behavior->data);
1464    }
1465    else {
1466        unsigned type, permanent;
1467
1468        type = behavior->type & XkbKB_OpMask;
1469        permanent = ((behavior->type & XkbKB_Permanent) != 0);
1470
1471        if (type == XkbKB_Lock) {
1472            snprintf(buf, sizeof(buf), "lock= %s", (permanent ? "Permanent" : "True"));
1473        }
1474        else if (type == XkbKB_RadioGroup) {
1475            int g;
1476            char *tmp;
1477            size_t tmpsize;
1478
1479            g = ((behavior->data) & (~XkbKB_RGAllowNone)) + 1;
1480            if (XkbKB_RGAllowNone & behavior->data) {
1481                snprintf(buf, sizeof(buf), "allowNone,");
1482                tmp = &buf[strlen(buf)];
1483            }
1484            else
1485                tmp = buf;
1486            tmpsize = sizeof(buf) - (tmp - buf);
1487            if (permanent)
1488                snprintf(tmp, tmpsize, "permanentRadioGroup= %d", g);
1489            else
1490                snprintf(tmp, tmpsize, "radioGroup= %d", g);
1491        }
1492        else if ((type == XkbKB_Overlay1) || (type == XkbKB_Overlay2)) {
1493            int ndx, kc;
1494            char *kn;
1495
1496            ndx = ((type == XkbKB_Overlay1) ? 1 : 2);
1497            kc = behavior->data;
1498            if ((xkb) && (xkb->names) && (xkb->names->keys))
1499                kn = XkbKeyNameText(xkb->names->keys[kc].name, XkbXKBFile);
1500            else {
1501                static char tbuf[8];
1502
1503                snprintf(tbuf, sizeof(tbuf), "%d", kc);
1504                kn = tbuf;
1505            }
1506            if (permanent)
1507                snprintf(buf, sizeof(buf), "permanentOverlay%d= %s", ndx, kn);
1508            else
1509                snprintf(buf, sizeof(buf), "overlay%d= %s", ndx, kn);
1510        }
1511    }
1512    tmp = tbGetBuffer(strlen(buf) + 1);
1513    if (tmp != NULL)
1514        strcpy(tmp, buf);
1515    return tmp;
1516}
1517
1518/***====================================================================***/
1519
1520char *
1521XkbIndentText(unsigned size)
1522{
1523    static char buf[32];
1524    register int i;
1525
1526    if (size > 31)
1527        size = 31;
1528
1529    for (i = 0; i < size; i++) {
1530        buf[i] = ' ';
1531    }
1532    buf[size] = '\0';
1533    return buf;
1534}
1535
1536
1537/***====================================================================***/
1538
1539#define	PIXEL_MAX	65535
1540
1541Bool
1542XkbLookupCanonicalRGBColor(char *def, XColor *color)
1543{
1544    int tmp;
1545
1546    if (_XkbStrCaseEqual(def, "black")) {
1547        color->red = color->green = color->blue = 0;
1548        return True;
1549    }
1550    else if (_XkbStrCaseEqual(def, "white")) {
1551        color->red = color->green = color->blue = PIXEL_MAX;
1552        return True;
1553    }
1554    else if ((sscanf(def, "grey%d", &tmp) == 1) ||
1555             (sscanf(def, "gray%d", &tmp) == 1) ||
1556             (sscanf(def, "Grey%d", &tmp) == 1) ||
1557             (sscanf(def, "Gray%d", &tmp) == 1)) {
1558        if ((tmp > 0) && (tmp <= 100)) {
1559            tmp = (PIXEL_MAX * tmp) / 100;
1560            color->red = color->green = color->blue = tmp;
1561            return True;
1562        }
1563    }
1564    else if ((tmp = (_XkbStrCaseEqual(def, "red") * 100)) ||
1565             (sscanf(def, "red%d", &tmp) == 1)) {
1566        if ((tmp > 0) && (tmp <= 100)) {
1567            tmp = (PIXEL_MAX * tmp) / 100;
1568            color->red = tmp;
1569            color->green = color->blue = 0;
1570            return True;
1571        }
1572    }
1573    else if ((tmp = (_XkbStrCaseEqual(def, "green") * 100)) ||
1574             (sscanf(def, "green%d", &tmp) == 1)) {
1575        if ((tmp > 0) && (tmp <= 100)) {
1576            tmp = (PIXEL_MAX * tmp) / 100;
1577            color->green = tmp;
1578            color->red = color->blue = 0;
1579            return True;
1580        }
1581    }
1582    else if ((tmp = (_XkbStrCaseEqual(def, "blue") * 100)) ||
1583             (sscanf(def, "blue%d", &tmp) == 1)) {
1584        if ((tmp > 0) && (tmp <= 100)) {
1585            tmp = (PIXEL_MAX * tmp) / 100;
1586            color->blue = tmp;
1587            color->red = color->green = 0;
1588            return True;
1589        }
1590    }
1591    else if ((tmp = (_XkbStrCaseEqual(def, "magenta") * 100)) ||
1592             (sscanf(def, "magenta%d", &tmp) == 1)) {
1593        if ((tmp > 0) && (tmp <= 100)) {
1594            tmp = (PIXEL_MAX * tmp) / 100;
1595            color->green = 0;
1596            color->red = color->blue = tmp;
1597            return True;
1598        }
1599    }
1600    else if ((tmp = (_XkbStrCaseEqual(def, "cyan") * 100)) ||
1601             (sscanf(def, "cyan%d", &tmp) == 1)) {
1602        if ((tmp > 0) && (tmp <= 100)) {
1603            tmp = (PIXEL_MAX * tmp) / 100;
1604            color->red = 0;
1605            color->green = color->blue = tmp;
1606            return True;
1607        }
1608    }
1609    else if ((tmp = (_XkbStrCaseEqual(def, "yellow") * 100)) ||
1610             (sscanf(def, "yellow%d", &tmp) == 1)) {
1611        if ((tmp > 0) && (tmp <= 100)) {
1612            tmp = (PIXEL_MAX * tmp) / 100;
1613            color->blue = 0;
1614            color->red = color->green = tmp;
1615            return True;
1616        }
1617    }
1618    return False;
1619}
1620
1621