keymap-dump: Print NoAction actions too
[platform/upstream/libxkbcommon.git] / src / keymap-dump.c
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 /*
28  * Copyright © 2012 Intel Corporation
29  *
30  * Permission is hereby granted, free of charge, to any person obtaining a
31  * copy of this software and associated documentation files (the "Software"),
32  * to deal in the Software without restriction, including without limitation
33  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
34  * and/or sell copies of the Software, and to permit persons to whom the
35  * Software is furnished to do so, subject to the following conditions:
36  *
37  * The above copyright notice and this permission notice (including the next
38  * paragraph) shall be included in all copies or substantial portions of the
39  * Software.
40  *
41  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
44  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
46  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
47  * DEALINGS IN THE SOFTWARE.
48  *
49  * Author: Daniel Stone <daniel@fooishbar.org>
50  */
51
52 #include <config.h>
53
54 #include <stdio.h>
55 #include <ctype.h>
56 #include <stdlib.h>
57
58 #include "xkb-priv.h"
59 #include "text.h"
60
61 #define VMOD_HIDE_VALUE 0
62 #define VMOD_SHOW_VALUE 1
63 #define VMOD_COMMENT_VALUE 2
64
65 #define BUF_CHUNK_SIZE 4096
66
67 static bool
68 do_realloc(char **buf, size_t *size, size_t offset, size_t at_least)
69 {
70     char *new;
71
72     *size += BUF_CHUNK_SIZE;
73     if (at_least >= BUF_CHUNK_SIZE)
74         *size += at_least;
75
76     new = realloc(*buf, *size);
77     if (!new)
78         return false;
79     *buf = new;
80
81     memset(*buf + offset, 0, *size - offset);
82
83     return true;
84 }
85
86 /* This whole thing should be a function, but you can't call vsnprintf
87  * multiple times. */
88 #define check_write_buf(keymap, buf, size, offset, ...) \
89 do { \
90     size_t _printed; \
91     bool _ret = true; \
92     \
93     /* Concatenate the strings, and check whether or not the output was \
94      * truncated. */ \
95     while ((_printed = snprintf(*buf + *offset, *size - *offset, \
96                                 __VA_ARGS__)) >= \
97            (*size - *offset)) {\
98         /* If it was truncated, embiggen the string and roll from the top. */ \
99         if (!do_realloc(buf, size, *offset, _printed)) { \
100             fprintf(stderr, \
101                     "xkbcommon: failed to allocate %lu bytes for keymap\n", \
102                     (unsigned long) *size); \
103             free(*buf); \
104             *buf = NULL; \
105             _ret = false; \
106             break; \
107         } \
108     } \
109     if (_ret == true) \
110         *offset += _printed; \
111 } while (0)
112
113 #define write_buf(keymap, buf, size, offset, ...) \
114 do { \
115     check_write_buf(keymap, buf, size, offset, __VA_ARGS__); \
116     if (*buf == NULL) \
117         return false; \
118 } while (0)
119
120 static bool
121 write_vmods(struct xkb_keymap *keymap, char **buf, size_t *size, size_t *offset)
122 {
123     int num_vmods = 0;
124     int i;
125
126     for (i = 0; i < XkbNumVirtualMods; i++) {
127         if (!keymap->names->vmods[i])
128             continue;
129         if (num_vmods == 0)
130             write_buf(keymap, buf, size, offset, "\t\tvirtual_modifiers ");
131         else
132             write_buf(keymap, buf, size, offset, ",");
133         write_buf(keymap, buf, size, offset, "%s", keymap->names->vmods[i]);
134         num_vmods++;
135     }
136
137     if (num_vmods > 0)
138         write_buf(keymap, buf, size, offset, ";\n\n");
139
140     return true;
141 }
142
143 #define GET_TEXT_BUF_SIZE 512
144
145 #define append_get_text(...) do { \
146     int _size = snprintf(ret, GET_TEXT_BUF_SIZE, __VA_ARGS__); \
147     if (_size >= GET_TEXT_BUF_SIZE) \
148         return NULL; \
149 } while(0)
150
151 /* FIXME: Merge with src/xkbcomp/expr.c::modIndexNames. */
152 static const char *core_mod_names[] = {
153     "Shift",
154     "Lock",
155     "Control",
156     "Mod1",
157     "Mod2",
158     "Mod3",
159     "Mod4",
160     "Mod5",
161 };
162
163 static const char *
164 get_mod_index_text(uint8_t real_mod)
165 {
166     return core_mod_names[real_mod];
167 }
168
169 static char *
170 get_mod_mask_text(struct xkb_keymap *keymap, uint8_t real_mods, uint32_t vmods)
171 {
172     static char ret[GET_TEXT_BUF_SIZE], ret2[GET_TEXT_BUF_SIZE];
173     int i;
174
175     memset(ret, 0, GET_TEXT_BUF_SIZE);
176
177     if (real_mods == 0 && vmods == 0) {
178         strcpy(ret, "none");
179         return ret;
180     }
181
182     /* This is so broken.  If we have a real modmask of 0xff and a
183      * vmodmask, we'll get, e.g., all+RightControl.  But, it's what xkbfile
184      * does, so ... */
185     if (real_mods == 0xff) {
186         strcpy(ret, "all");
187     }
188     else if (real_mods) {
189         for (i = 0; i < XkbNumModifiers; i++) {
190             if (!(real_mods & (1 << i)))
191                 continue;
192             if (ret[0] != '\0') {
193                 strcpy(ret2, ret);
194                 append_get_text("%s+%s", ret2, core_mod_names[i]);
195             }
196             else {
197                 append_get_text("%s", core_mod_names[i]);
198             }
199         }
200     }
201
202     if (vmods == 0)
203         return ret;
204
205     for (i = 0; i < XkbNumVirtualMods; i++) {
206         if (!(vmods & (1 << i)))
207             continue;
208         if (ret[0] != '\0') {
209             strcpy(ret2, ret);
210             append_get_text("%s+%s", ret2, keymap->names->vmods[i]);
211         }
212         else {
213             append_get_text("%s", keymap->names->vmods[i]);
214         }
215     }
216
217     return ret;
218 }
219
220 static char *
221 get_indicator_state_text(uint8_t which)
222 {
223     int i;
224     static char ret[GET_TEXT_BUF_SIZE];
225     /* FIXME: Merge with ... something ... in xkbcomp? */
226     static const char *state_names[] = {
227         "base",
228         "latched",
229         "locked",
230         "effective",
231         "compat"
232     };
233
234     memset(ret, 0, GET_TEXT_BUF_SIZE);
235
236     which &= XkbIM_UseAnyMods;
237
238     if (which == 0) {
239         strcpy(ret, "0");
240         return NULL;
241     }
242
243     for (i = 0; which != 0; i++) {
244         if (!(which & (1 << i)))
245             continue;
246         which &= ~(1 << i);
247
248         if (ret[0] != '\0')
249             append_get_text("%s+%s", ret, state_names[i]);
250         else
251             append_get_text("%s", state_names[i]);
252     }
253
254     return ret;
255 }
256
257 static char *
258 get_control_mask_text(uint32_t control_mask)
259 {
260     int i;
261     static char ret[GET_TEXT_BUF_SIZE];
262     /* FIXME: Merge with ... something ... in xkbcomp. */
263     static const char *ctrl_names[] = {
264         "repeatKeys",
265         "slowKeys",
266         "bounceKeys",
267         "stickyKeys",
268         "mouseKeys",
269         "mouseKeysAccel",
270         "accessXKeys",
271         "accessXTimeout",
272         "accessXFeedback",
273         "audibleBell",
274         "overlay1",
275         "overlay2",
276         "ignoreGroupLock"
277     };
278
279     memset(ret, 0, GET_TEXT_BUF_SIZE);
280
281     control_mask &= XkbAllBooleanCtrlsMask;
282
283     if (control_mask == 0) {
284         strcpy(ret, "none");
285         return ret;
286     }
287     else if (control_mask == XkbAllBooleanCtrlsMask) {
288         strcpy(ret, "all");
289         return ret;
290     }
291
292     for (i = 0; control_mask; i++) {
293         if (!(control_mask & (1 << i)))
294             continue;
295         control_mask &= ~(1 << i);
296
297         if (ret[0] != '\0')
298             append_get_text("%s+%s", ret, ctrl_names[i]);
299         else
300             append_get_text("%s", ctrl_names[i]);
301     }
302
303     return ret;
304 }
305
306 static bool
307 write_keycodes(struct xkb_keymap *keymap, char **buf, size_t *size,
308                size_t *offset)
309 {
310     xkb_keycode_t key;
311     struct xkb_key_alias *alias;
312     int i;
313
314     write_buf(keymap, buf, size, offset, "\txkb_keycodes {\n");
315     write_buf(keymap, buf, size, offset, "\t\tminimum = %d;\n",
316               keymap->min_key_code);
317     write_buf(keymap, buf, size, offset, "\t\tmaximum = %d;\n",
318               keymap->max_key_code);
319
320     for (key = keymap->min_key_code; key <= keymap->max_key_code; key++) {
321         const char *alternate = "";
322
323         if (darray_item(keymap->names->keys, key).name[0] == '\0')
324             continue;
325         if (XkbcFindKeycodeByName(keymap,
326                                   darray_item(keymap->names->keys, key).name,
327                                   true) != key)
328             alternate = "alternate ";
329         write_buf(keymap, buf, size, offset, "\t\t%s%6s = %d;\n", alternate,
330                   XkbcKeyNameText(darray_item(keymap->names->keys, key).name),
331                                   key);
332     }
333
334     for (i = 0; i < XkbNumIndicators; i++) {
335         if (!keymap->names->indicators[i])
336             continue;
337         write_buf(keymap, buf, size, offset, "\t\tindicator %d = \"%s\";\n",
338                   i + 1, keymap->names->indicators[i]);
339     }
340
341
342     for (i = 0; i < darray_size(keymap->names->key_aliases); i++) {
343         alias = &darray_item(keymap->names->key_aliases, i);
344         write_buf(keymap, buf, size, offset, "\t\talias %6s = %6s;\n",
345                   XkbcKeyNameText(alias->alias),
346                   XkbcKeyNameText(alias->real));
347     }
348
349     write_buf(keymap, buf, size, offset, "\t};\n\n");
350     return true;
351 }
352
353 static bool
354 write_types(struct xkb_keymap *keymap, char **buf, size_t *size,
355             size_t *offset)
356 {
357     int i, n;
358     struct xkb_key_type *type;
359
360     write_buf(keymap, buf, size, offset, "\txkb_types {\n\n");
361     write_vmods(keymap, buf, size, offset);
362
363     for (i = 0; i < darray_size(keymap->map->types); i++) {
364         type = &darray_item(keymap->map->types, i);
365         write_buf(keymap, buf, size, offset, "\t\ttype \"%s\" {\n",
366                   type->name);
367         write_buf(keymap, buf, size, offset, "\t\t\tmodifiers= %s;\n",
368                   get_mod_mask_text(keymap, type->mods.real_mods,
369                                     type->mods.vmods));
370
371         for (n = 0; n < darray_size(type->map); n++) {
372             struct xkb_kt_map_entry *entry = &darray_item(type->map, n);
373             char *str;
374
375             str = get_mod_mask_text(keymap, entry->mods.real_mods,
376                                     entry->mods.vmods);
377             write_buf(keymap, buf, size, offset, "\t\t\tmap[%s]= Level%d;\n",
378                       str, entry->level + 1);
379
380             if (!type->preserve || (!type->preserve[n].real_mods &&
381                                     !type->preserve[n].vmods))
382                 continue;
383             write_buf(keymap, buf, size, offset, "\t\t\tpreserve[%s]= ", str);
384             write_buf(keymap, buf, size, offset, "%s;\n",
385                       get_mod_mask_text(keymap, type->preserve[n].real_mods,
386                                         type->preserve[n].vmods));
387         }
388
389         if (type->level_names) {
390             for (n = 0; n < type->num_levels; n++) {
391                 if (!type->level_names[n])
392                     continue;
393                 write_buf(keymap, buf, size, offset,
394                           "\t\t\tlevel_name[Level%d]= \"%s\";\n", n + 1,
395                           type->level_names[n]);
396             }
397         }
398         write_buf(keymap, buf, size, offset, "\t\t};\n");
399     }
400
401     write_buf(keymap, buf, size, offset, "\t};\n\n");
402     return true;
403 }
404
405 static bool
406 write_indicator_map(struct xkb_keymap *keymap, char **buf, size_t *size,
407                     size_t *offset, int num)
408 {
409     struct xkb_indicator_map *led = &keymap->indicators->maps[num];
410
411     write_buf(keymap, buf, size, offset, "\t\tindicator \"%s\" {\n",
412               keymap->names->indicators[num]);
413
414     if (led->which_groups) {
415         if (led->which_groups != XkbIM_UseEffective) {
416             write_buf(keymap, buf, size, offset, "\t\t\twhichGroupState= %s;\n",
417                       get_indicator_state_text(led->which_groups));
418         }
419         write_buf(keymap, buf, size, offset, "\t\t\tgroups= 0x%02x;\n",
420                   led->groups);
421     }
422
423     if (led->which_mods) {
424         if (led->which_mods != XkbIM_UseEffective) {
425             write_buf(keymap, buf, size, offset, "\t\t\twhichModState= %s;\n",
426                       get_indicator_state_text(led->which_mods));
427         }
428         write_buf(keymap, buf, size, offset, "\t\t\tmodifiers= %s;\n",
429                   get_mod_mask_text(keymap, led->mods.real_mods,
430                   led->mods.vmods));
431     }
432
433     if (led->ctrls) {
434         write_buf(keymap, buf, size, offset, "\t\t\tcontrols= %s;\n",
435                   get_control_mask_text(led->ctrls));
436     }
437
438     write_buf(keymap, buf, size, offset, "\t\t};\n");
439     return true;
440 }
441
442 static char *
443 get_interp_match_text(uint8_t type)
444 {
445     static char ret[16];
446
447     switch (type & XkbSI_OpMask) {
448         case XkbSI_NoneOf:
449             sprintf(ret, "NoneOf");
450             break;
451         case XkbSI_AnyOfOrNone:
452             sprintf(ret, "AnyOfOrNone");
453             break;
454         case XkbSI_AnyOf:
455             sprintf(ret, "AnyOf");
456             break;
457         case XkbSI_AllOf:
458             sprintf(ret, "AllOf");
459             break;
460         case XkbSI_Exactly:
461             sprintf(ret, "Exactly");
462             break;
463         default:
464             sprintf(ret, "0x%x", type & XkbSI_OpMask);
465             break;
466     }
467
468     return ret;
469 }
470
471 static bool
472 write_action(struct xkb_keymap *keymap, char **buf, size_t *size,
473              size_t *offset, union xkb_action *action, const char *prefix,
474              const char *suffix)
475 {
476     const char *type = NULL;
477     const char *args = NULL;
478
479     if (!prefix)
480         prefix = "";
481     if (!suffix)
482         suffix = "";
483
484     switch (action->any.type) {
485     case XkbSA_SetMods:
486         if (!type)
487             type = "SetMods";
488     case XkbSA_LatchMods:
489         if (!type)
490             type = "LatchMods";
491     case XkbSA_LockMods:
492         if (!type)
493             type = "LockMods";
494         if (action->mods.flags & XkbSA_UseModMapMods)
495             args = "modMapMods";
496         else
497             args = get_mod_mask_text(keymap, action->mods.real_mods,
498                                      action->mods.vmods);
499         write_buf(keymap, buf, size, offset, "%s%s(modifiers=%s%s%s)%s",
500                   prefix, type, args,
501                   (action->any.type != XkbSA_LockGroup &&
502                    action->mods.flags & XkbSA_ClearLocks) ? ",clearLocks" : "",
503                   (action->any.type != XkbSA_LockGroup &&
504                    action->mods.flags & XkbSA_LatchToLock) ? ",latchToLock" : "",
505                   suffix);
506         break;
507     case XkbSA_SetGroup:
508         if (!type)
509             type = "SetGroup";
510     case XkbSA_LatchGroup:
511         if (!type)
512             type = "LatchGroup";
513     case XkbSA_LockGroup:
514         if (!type)
515             type = "LockGroup";
516         write_buf(keymap, buf, size, offset, "%s%s(group=%s%d%s%s)%s",
517                   prefix, type,
518                   (!(action->group.flags & XkbSA_GroupAbsolute) &&
519                    action->group.group > 0) ? "+" : "",
520                   (action->group.flags & XkbSA_GroupAbsolute) ?
521                    action->group.group + 1 : action->group.group,
522                   (action->any.type != XkbSA_LockGroup &&
523                    action->group.flags & XkbSA_ClearLocks) ? ",clearLocks" : "",
524                   (action->any.type != XkbSA_LockGroup &&
525                    action->group.flags & XkbSA_LatchToLock) ? ",latchToLock" : "",
526                   suffix);
527         break;
528     case XkbSA_Terminate:
529         write_buf(keymap, buf, size, offset, "%sTerminate()%s", prefix, suffix);
530         break;
531     case XkbSA_MovePtr:
532         write_buf(keymap, buf, size, offset, "%sMovePtr(x=%s%d,y=%s%d%s)%s",
533                   prefix,
534                   (!(action->ptr.flags & XkbSA_MoveAbsoluteX) &&
535                    action->ptr.x >= 0) ? "+" : "",
536                   action->ptr.x,
537                   (!(action->ptr.flags & XkbSA_MoveAbsoluteY) &&
538                    action->ptr.y >= 0) ? "+" : "",
539                   action->ptr.y,
540                   (action->ptr.flags & XkbSA_NoAcceleration) ? ",!accel" : "",
541                   suffix);
542         break;
543     case XkbSA_PtrBtn:
544         if (!type)
545             type = "PtrBtn";
546     case XkbSA_LockPtrBtn:
547         if (!type) {
548             type = "LockPtrBtn";
549             switch (action->btn.flags & (XkbSA_LockNoUnlock | XkbSA_LockNoLock)) {
550             case XkbSA_LockNoUnlock:
551                 args = ",affect=lock";
552                 break;
553             case XkbSA_LockNoLock:
554                 args = ",affect=unlock";
555                 break;
556             case XkbSA_LockNoLock | XkbSA_LockNoUnlock:
557                 args = ",affect=neither";
558                 break;
559             default:
560                 args = ",affect=both";
561                 break;
562             }
563         }
564         else {
565             args = NULL;
566         }
567         write_buf(keymap, buf, size, offset, "%s%s(button=", prefix, type);
568         if (action->btn.button > 0 && action->btn.button <= 5)
569             write_buf(keymap, buf, size, offset, "%d", action->btn.button);
570         else
571             write_buf(keymap, buf, size, offset, "default");
572         if (action->btn.count)
573             write_buf(keymap, buf, size, offset, ",count=%d",
574                       action->btn.count);
575         if (args)
576             write_buf(keymap, buf, size, offset, "%s", args);
577         write_buf(keymap, buf, size, offset, ")%s", suffix);
578         break;
579     case XkbSA_SetPtrDflt:
580         write_buf(keymap, buf, size, offset, "%sSetPtrDflt(", prefix);
581         if (action->dflt.affect == XkbSA_AffectDfltBtn)
582             write_buf(keymap, buf, size, offset, "affect=button,button=%s%d",
583                       (!(action->dflt.flags & XkbSA_DfltBtnAbsolute) &&
584                        action->dflt.value >= 0) ? "+" : "",
585                       action->dflt.value);
586         write_buf(keymap, buf, size, offset, ")%s", suffix);
587         break;
588     case XkbSA_SwitchScreen:
589         write_buf(keymap, buf, size, offset,
590                   "%sSwitchScreen(screen=%s%d,%ssame)%s", prefix,
591                   (!(action->screen.flags & XkbSA_SwitchAbsolute) &&
592                    action->screen.screen >= 0) ? "+" : "",
593                   action->screen.screen,
594                   (action->screen.flags & XkbSA_SwitchApplication) ? "!" : "",
595                   suffix);
596         break;
597     /* Deprecated actions below here */
598     case XkbSA_SetControls:
599         if (!type)
600             type = "SetControls";
601     case XkbSA_LockControls:
602         if (!type)
603             type = "LockControls";
604         write_buf(keymap, buf, size, offset, "%s%s(controls=%s)%s",
605                   prefix, type, get_control_mask_text(action->ctrls.ctrls),
606                   suffix);
607         break;
608     case XkbSA_ISOLock:
609     case XkbSA_ActionMessage:
610     case XkbSA_RedirectKey:
611     case XkbSA_DeviceBtn:
612     case XkbSA_LockDeviceBtn:
613         /* XXX TODO */
614         write_buf(keymap, buf, size, offset, "%sNoAction()%s", prefix, suffix);
615         break;
616     case XkbSA_XFree86Private:
617     default:
618         write_buf(keymap, buf, size, offset,
619                   "%sPrivate(type=0x%02x,data[0]=0x%02x,data[1]=0x%02x,data[2]=0x%02x,data[3]=0x%02x,data[4]=0x%02x,data[5]=0x%02x,data[6]=0x%02x)%s",
620                   prefix, action->any.type, action->any.data[0],
621                   action->any.data[1], action->any.data[2],
622                   action->any.data[3], action->any.data[4],
623                   action->any.data[5], action->any.data[6], suffix);
624         break;
625     }
626
627     return true;
628 }
629
630 static bool
631 write_compat(struct xkb_keymap *keymap, char **buf, size_t *size,
632              size_t *offset)
633 {
634     int i;
635     struct xkb_sym_interpret *interp;
636
637     write_buf(keymap, buf, size, offset, "\txkb_compatibility {\n\n");
638
639     write_vmods(keymap, buf, size, offset);
640
641     write_buf(keymap, buf, size, offset, "\t\tinterpret.useModMapMods= AnyLevel;\n");
642     write_buf(keymap, buf, size, offset, "\t\tinterpret.repeat= false;\n");
643     write_buf(keymap, buf, size, offset, "\t\tinterpret.locking= false;\n");
644
645     for (i = 0; i < darray_size(keymap->compat->sym_interpret); i++) {
646         char keysym_name[64];
647         interp = &darray_item(keymap->compat->sym_interpret, i);
648
649         if (interp->sym == XKB_KEY_NoSymbol)
650             sprintf(keysym_name, "Any");
651         else
652             xkb_keysym_get_name(interp->sym, keysym_name, 64);
653
654         write_buf(keymap, buf, size, offset, "\t\tinterpret %s+%s(%s) {\n",
655                   keysym_name,
656                   get_interp_match_text(interp->match),
657                   get_mod_mask_text(keymap, interp->mods, 0));
658
659         if (interp->virtual_mod != XkbNoModifier) {
660             write_buf(keymap, buf, size, offset, "\t\t\tvirtualModifier= %s;\n",
661                       keymap->names->vmods[interp->virtual_mod]);
662         }
663
664         if (interp->match & XkbSI_LevelOneOnly)
665             write_buf(keymap, buf, size, offset, "\t\t\tuseModMapMods=level1;\n");
666         if (interp->flags & XkbSI_LockingKey)
667             write_buf(keymap, buf, size, offset, "\t\t\tlocking= true;\n");
668         if (interp->flags & XkbSI_AutoRepeat)
669             write_buf(keymap, buf, size, offset, "\t\t\trepeat= true;\n");
670
671         write_action(keymap, buf, size, offset, &interp->act,
672                      "\t\t\taction= ", ";\n");
673         write_buf(keymap, buf, size, offset, "\t\t};\n");
674     }
675
676     for (i = 0; i < XkbNumKbdGroups; i++) {
677         struct xkb_mods *gc;
678
679         gc = &keymap->compat->groups[i];
680         if (gc->real_mods == 0 && gc->vmods ==0)
681             continue;
682         write_buf(keymap, buf, size, offset,
683                   "\t\tgroup %d = %s;\n", i + 1,
684                   get_mod_mask_text(keymap, gc->real_mods, gc->vmods));
685     }
686
687     for (i = 0; i < XkbNumIndicators; i++) {
688         struct xkb_indicator_map *map = &keymap->indicators->maps[i];
689         if (map->flags == 0 && map->which_groups == 0 &&
690             map->groups == 0 && map->which_mods == 0 &&
691             map->mods.real_mods == 0 && map->mods.vmods == 0 &&
692             map->ctrls == 0)
693             continue;
694         write_indicator_map(keymap, buf, size, offset, i);
695     }
696
697     write_buf(keymap, buf, size, offset, "\t};\n\n");
698
699     return true;
700 }
701
702 static bool
703 write_keysyms(struct xkb_keymap *keymap, char **buf, size_t *size,
704               size_t *offset, xkb_keycode_t key, unsigned int group)
705 {
706     const xkb_keysym_t *syms;
707     int num_syms, level;
708 #define OUT_BUF_LEN 128
709     char out_buf[OUT_BUF_LEN];
710
711     for (level = 0; level < XkbKeyGroupWidth(keymap, key, group); level++) {
712         if (level != 0)
713             write_buf(keymap, buf, size, offset, ", ");
714         num_syms = xkb_key_get_syms_by_level(keymap, key, group, level,
715                                              &syms);
716         if (num_syms == 0) {
717             write_buf(keymap, buf, size, offset, "%15s", "NoSymbol");
718         }
719         else if (num_syms == 1) {
720             xkb_keysym_get_name(syms[0], out_buf, OUT_BUF_LEN);
721             write_buf(keymap, buf, size, offset, "%15s", out_buf);
722         }
723         else {
724             int s;
725             write_buf(keymap, buf, size, offset, "{ ");
726             for (s = 0; s < num_syms; s++) {
727                 if (s != 0)
728                     write_buf(keymap, buf, size, offset, ", ");
729                 xkb_keysym_get_name(syms[s], out_buf, OUT_BUF_LEN);
730                 write_buf(keymap, buf, size, offset, "%15s", out_buf);
731             }
732             write_buf(keymap, buf, size, offset, " }");
733         }
734     }
735 #undef OUT_BUF_LEN
736
737     return true;
738 }
739
740 static bool
741 write_symbols(struct xkb_keymap *keymap, char **buf, size_t *size,
742               size_t *offset)
743 {
744     struct xkb_client_map *map = keymap->map;
745     struct xkb_server_map *srv = keymap->server;
746     xkb_keycode_t key;
747     int group, tmp;
748     bool showActions;
749
750     write_buf(keymap, buf, size, offset, "\txkb_symbols {\n\n");
751
752     for (tmp = group = 0; group < XkbNumKbdGroups; group++) {
753         if (!keymap->names->groups[group])
754             continue;
755         write_buf(keymap, buf, size, offset,
756                   "\t\tname[group%d]=\"%s\";\n", group + 1,
757                   keymap->names->groups[group]);
758         tmp++;
759     }
760     if (tmp > 0)
761         write_buf(keymap, buf, size, offset, "\n");
762
763     for (key = keymap->min_key_code; key <= keymap->max_key_code; key++) {
764         bool simple = true;
765
766         if (xkb_key_num_groups(keymap, key) == 0)
767             continue;
768         if (XkbcFindKeycodeByName(keymap,
769                                   darray_item(keymap->names->keys, key).name,
770                                   true) != key)
771             continue;
772
773         write_buf(keymap, buf, size, offset, "\t\tkey %6s {",
774                   XkbcKeyNameText(darray_item(keymap->names->keys, key).name));
775         if (srv->explicit) {
776             if ((srv->explicit[key] & XkbExplicitKeyTypesMask)) {
777                 bool multi_type = false;
778                 int type = XkbKeyTypeIndex(keymap, key, 0);
779
780                 simple = false;
781
782                 for (group = 0; group < xkb_key_num_groups(keymap, key); group++) {
783                     if (XkbKeyTypeIndex(keymap, key, group) != type) {
784                         multi_type = true;
785                         break;
786                     }
787                 }
788                 if (multi_type) {
789                     for (group = 0;
790                          group < xkb_key_num_groups(keymap, key);
791                          group++) {
792                         if (!(srv->explicit[key] & (1 << group)))
793                             continue;
794                         type = XkbKeyTypeIndex(keymap, key, group);
795                         write_buf(keymap, buf, size, offset,
796                                   "\n\t\t\ttype[Group%d]= \"%s\",",
797                                   group + 1,
798                                   darray_item(map->types, type).name);
799                     }
800                 }
801                 else {
802                     write_buf(keymap, buf, size, offset,
803                               "\n\t\t\ttype= \"%s\",",
804                               darray_item(map->types, type).name);
805                 }
806             }
807             if (keymap->ctrls && (srv->explicit[key] & XkbExplicitAutoRepeatMask)) {
808                 if (keymap->ctrls->per_key_repeat[key / 8] & (1 << (key % 8)))
809                      write_buf(keymap, buf, size, offset,
810                                "\n\t\t\trepeat= Yes,");
811                 else
812                     write_buf(keymap, buf, size, offset,
813                               "\n\t\t\trepeat= No,");
814                 simple = false;
815             }
816             if (keymap->server->vmodmap[key] &&
817                 (srv->explicit[key] & XkbExplicitVModMapMask)) {
818                 write_buf(keymap, buf, size, offset, "\n\t\t\tvirtualMods= %s,",
819                           get_mod_mask_text(keymap, 0, keymap->server->vmodmap[key]));
820             }
821         }
822
823         switch (XkbOutOfRangeGroupAction(XkbKeyGroupInfo(keymap, key))) {
824             case XkbClampIntoRange:
825                 write_buf(keymap, buf, size, offset, "\n\t\t\tgroupsClamp,");
826                 break;
827             case XkbRedirectIntoRange:
828                 write_buf(keymap, buf, size, offset,
829                           "\n\t\t\tgroupsRedirect= Group%d,",
830                           XkbOutOfRangeGroupNumber(XkbKeyGroupInfo(keymap, key)) + 1);
831                 break;
832         }
833
834         if (srv->explicit == NULL ||
835             (srv->explicit[key] & XkbExplicitInterpretMask))
836             showActions = XkbKeyHasActions(keymap, key);
837         else
838             showActions = false;
839
840         if (xkb_key_num_groups(keymap, key) > 1 || showActions)
841             simple = false;
842
843         if (simple) {
844             write_buf(keymap, buf, size, offset, "\t[ ");
845             if (!write_keysyms(keymap, buf, size, offset, key, 0))
846                 return false;
847             write_buf(keymap, buf, size, offset, " ] };\n");
848         }
849         else {
850             union xkb_action *acts;
851             int level;
852
853             acts = XkbKeyActionsPtr(keymap, key);
854             for (group = 0; group < xkb_key_num_groups(keymap, key); group++) {
855                 if (group != 0)
856                     write_buf(keymap, buf, size, offset, ",");
857                 write_buf(keymap, buf, size, offset,
858                           "\n\t\t\tsymbols[Group%d]= [ ", group + 1);
859                 if (!write_keysyms(keymap, buf, size, offset, key, group))
860                     return false;
861                 write_buf(keymap, buf, size, offset, " ]");
862                 if (showActions) {
863                     write_buf(keymap, buf, size, offset,
864                               ",\n\t\t\tactions[Group%d]= [ ", group + 1);
865                     for (level = 0;
866                          level < XkbKeyGroupWidth(keymap, key, group);
867                          level++) {
868                         if (level != 0)
869                             write_buf(keymap, buf, size, offset, ", ");
870                         write_action(keymap, buf, size, offset, &acts[level],
871                                      NULL, NULL);
872                     }
873                     write_buf(keymap, buf, size, offset, " ]");
874                     acts += XkbKeyGroupsWidth(keymap, key);
875                 }
876             }
877             write_buf(keymap, buf, size, offset, "\n\t\t};\n");
878         }
879     }
880     if (map && map->modmap) {
881         for (key = keymap->min_key_code; key <= keymap->max_key_code; key++) {
882             int mod;
883             char name[5];
884
885             if (map->modmap[key] == 0)
886                 continue;
887
888             for (mod = 0; mod < XkbNumModifiers; mod++) {
889                 if (!(map->modmap[key] & (1 << mod)))
890                     continue;
891
892                 memcpy(name, darray_item(keymap->names->keys, key).name, 4);
893                 name[4]= '\0';
894
895                 write_buf(keymap, buf, size, offset,
896                           "\t\tmodifier_map %s { <%s> };\n",
897                           get_mod_index_text(mod), name);
898             }
899         }
900     }
901
902     write_buf(keymap, buf, size, offset, "\t};\n\n");
903     return true;
904 }
905
906 _X_EXPORT char *
907 xkb_map_get_as_string(struct xkb_keymap *keymap)
908 {
909     char *ret = NULL;
910     size_t size = 0;
911     size_t offset = 0;
912
913     check_write_buf(keymap, &ret, &size, &offset, "xkb_keymap {\n");
914     if (ret == NULL)
915         return NULL;
916     if (!write_keycodes(keymap, &ret, &size, &offset))
917         return NULL;
918     if (!write_types(keymap, &ret, &size, &offset))
919         return NULL;
920     if (!write_compat(keymap, &ret, &size, &offset))
921         return NULL;
922     if (!write_symbols(keymap, &ret, &size, &offset))
923         return NULL;
924     check_write_buf(keymap, &ret, &size, &offset, "};\n");
925     if (ret == NULL)
926         return NULL;
927
928     return ret;
929 }