darray: some changes for convenience
[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 %zu bytes for keymap\n", \
102                     *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     darray_foreach(alias, keymap->names->key_aliases)
343         write_buf(keymap, buf, size, offset, "\t\talias %6s = %6s;\n",
344                   XkbcKeyNameText(alias->alias),
345                   XkbcKeyNameText(alias->real));
346
347     write_buf(keymap, buf, size, offset, "\t};\n\n");
348     return true;
349 }
350
351 static bool
352 write_types(struct xkb_keymap *keymap, char **buf, size_t *size,
353             size_t *offset)
354 {
355     int n;
356     struct xkb_key_type *type;
357
358     write_buf(keymap, buf, size, offset, "\txkb_types {\n\n");
359     write_vmods(keymap, buf, size, offset);
360
361     darray_foreach(type, keymap->map->types) {
362         write_buf(keymap, buf, size, offset, "\t\ttype \"%s\" {\n",
363                   type->name);
364         write_buf(keymap, buf, size, offset, "\t\t\tmodifiers= %s;\n",
365                   get_mod_mask_text(keymap, type->mods.real_mods,
366                                     type->mods.vmods));
367
368         for (n = 0; n < darray_size(type->map); n++) {
369             struct xkb_kt_map_entry *entry = &darray_item(type->map, n);
370             char *str;
371
372             str = get_mod_mask_text(keymap, entry->mods.real_mods,
373                                     entry->mods.vmods);
374             write_buf(keymap, buf, size, offset, "\t\t\tmap[%s]= Level%d;\n",
375                       str, entry->level + 1);
376
377             if (!type->preserve || (!type->preserve[n].real_mods &&
378                                     !type->preserve[n].vmods))
379                 continue;
380             write_buf(keymap, buf, size, offset, "\t\t\tpreserve[%s]= ", str);
381             write_buf(keymap, buf, size, offset, "%s;\n",
382                       get_mod_mask_text(keymap, type->preserve[n].real_mods,
383                                         type->preserve[n].vmods));
384         }
385
386         if (type->level_names) {
387             for (n = 0; n < type->num_levels; n++) {
388                 if (!type->level_names[n])
389                     continue;
390                 write_buf(keymap, buf, size, offset,
391                           "\t\t\tlevel_name[Level%d]= \"%s\";\n", n + 1,
392                           type->level_names[n]);
393             }
394         }
395         write_buf(keymap, buf, size, offset, "\t\t};\n");
396     }
397
398     write_buf(keymap, buf, size, offset, "\t};\n\n");
399     return true;
400 }
401
402 static bool
403 write_indicator_map(struct xkb_keymap *keymap, char **buf, size_t *size,
404                     size_t *offset, int num)
405 {
406     struct xkb_indicator_map *led = &keymap->indicators->maps[num];
407
408     write_buf(keymap, buf, size, offset, "\t\tindicator \"%s\" {\n",
409               keymap->names->indicators[num]);
410
411     if (led->which_groups) {
412         if (led->which_groups != XkbIM_UseEffective) {
413             write_buf(keymap, buf, size, offset, "\t\t\twhichGroupState= %s;\n",
414                       get_indicator_state_text(led->which_groups));
415         }
416         write_buf(keymap, buf, size, offset, "\t\t\tgroups= 0x%02x;\n",
417                   led->groups);
418     }
419
420     if (led->which_mods) {
421         if (led->which_mods != XkbIM_UseEffective) {
422             write_buf(keymap, buf, size, offset, "\t\t\twhichModState= %s;\n",
423                       get_indicator_state_text(led->which_mods));
424         }
425         write_buf(keymap, buf, size, offset, "\t\t\tmodifiers= %s;\n",
426                   get_mod_mask_text(keymap, led->mods.real_mods,
427                   led->mods.vmods));
428     }
429
430     if (led->ctrls) {
431         write_buf(keymap, buf, size, offset, "\t\t\tcontrols= %s;\n",
432                   get_control_mask_text(led->ctrls));
433     }
434
435     write_buf(keymap, buf, size, offset, "\t\t};\n");
436     return true;
437 }
438
439 static char *
440 get_interp_match_text(uint8_t type)
441 {
442     static char ret[16];
443
444     switch (type & XkbSI_OpMask) {
445         case XkbSI_NoneOf:
446             sprintf(ret, "NoneOf");
447             break;
448         case XkbSI_AnyOfOrNone:
449             sprintf(ret, "AnyOfOrNone");
450             break;
451         case XkbSI_AnyOf:
452             sprintf(ret, "AnyOf");
453             break;
454         case XkbSI_AllOf:
455             sprintf(ret, "AllOf");
456             break;
457         case XkbSI_Exactly:
458             sprintf(ret, "Exactly");
459             break;
460         default:
461             sprintf(ret, "0x%x", type & XkbSI_OpMask);
462             break;
463     }
464
465     return ret;
466 }
467
468 static bool
469 write_action(struct xkb_keymap *keymap, char **buf, size_t *size,
470              size_t *offset, union xkb_action *action, const char *prefix,
471              const char *suffix)
472 {
473     const char *type = NULL;
474     const char *args = NULL;
475
476     if (!prefix)
477         prefix = "";
478     if (!suffix)
479         suffix = "";
480
481     switch (action->any.type) {
482     case XkbSA_SetMods:
483         if (!type)
484             type = "SetMods";
485     case XkbSA_LatchMods:
486         if (!type)
487             type = "LatchMods";
488     case XkbSA_LockMods:
489         if (!type)
490             type = "LockMods";
491         if (action->mods.flags & XkbSA_UseModMapMods)
492             args = "modMapMods";
493         else
494             args = get_mod_mask_text(keymap, action->mods.real_mods,
495                                      action->mods.vmods);
496         write_buf(keymap, buf, size, offset, "%s%s(modifiers=%s%s%s)%s",
497                   prefix, type, args,
498                   (action->any.type != XkbSA_LockGroup &&
499                    action->mods.flags & XkbSA_ClearLocks) ? ",clearLocks" : "",
500                   (action->any.type != XkbSA_LockGroup &&
501                    action->mods.flags & XkbSA_LatchToLock) ? ",latchToLock" : "",
502                   suffix);
503         break;
504     case XkbSA_SetGroup:
505         if (!type)
506             type = "SetGroup";
507     case XkbSA_LatchGroup:
508         if (!type)
509             type = "LatchGroup";
510     case XkbSA_LockGroup:
511         if (!type)
512             type = "LockGroup";
513         write_buf(keymap, buf, size, offset, "%s%s(group=%s%d%s%s)%s",
514                   prefix, type,
515                   (!(action->group.flags & XkbSA_GroupAbsolute) &&
516                    action->group.group > 0) ? "+" : "",
517                   (action->group.flags & XkbSA_GroupAbsolute) ?
518                    action->group.group + 1 : action->group.group,
519                   (action->any.type != XkbSA_LockGroup &&
520                    action->group.flags & XkbSA_ClearLocks) ? ",clearLocks" : "",
521                   (action->any.type != XkbSA_LockGroup &&
522                    action->group.flags & XkbSA_LatchToLock) ? ",latchToLock" : "",
523                   suffix);
524         break;
525     case XkbSA_Terminate:
526         write_buf(keymap, buf, size, offset, "%sTerminate()%s", prefix, suffix);
527         break;
528     case XkbSA_MovePtr:
529         write_buf(keymap, buf, size, offset, "%sMovePtr(x=%s%d,y=%s%d%s)%s",
530                   prefix,
531                   (!(action->ptr.flags & XkbSA_MoveAbsoluteX) &&
532                    action->ptr.x >= 0) ? "+" : "",
533                   action->ptr.x,
534                   (!(action->ptr.flags & XkbSA_MoveAbsoluteY) &&
535                    action->ptr.y >= 0) ? "+" : "",
536                   action->ptr.y,
537                   (action->ptr.flags & XkbSA_NoAcceleration) ? ",!accel" : "",
538                   suffix);
539         break;
540     case XkbSA_PtrBtn:
541         if (!type)
542             type = "PtrBtn";
543     case XkbSA_LockPtrBtn:
544         if (!type) {
545             type = "LockPtrBtn";
546             switch (action->btn.flags & (XkbSA_LockNoUnlock | XkbSA_LockNoLock)) {
547             case XkbSA_LockNoUnlock:
548                 args = ",affect=lock";
549                 break;
550             case XkbSA_LockNoLock:
551                 args = ",affect=unlock";
552                 break;
553             case XkbSA_LockNoLock | XkbSA_LockNoUnlock:
554                 args = ",affect=neither";
555                 break;
556             default:
557                 args = ",affect=both";
558                 break;
559             }
560         }
561         else {
562             args = NULL;
563         }
564         write_buf(keymap, buf, size, offset, "%s%s(button=", prefix, type);
565         if (action->btn.button > 0 && action->btn.button <= 5)
566             write_buf(keymap, buf, size, offset, "%d", action->btn.button);
567         else
568             write_buf(keymap, buf, size, offset, "default");
569         if (action->btn.count)
570             write_buf(keymap, buf, size, offset, ",count=%d",
571                       action->btn.count);
572         if (args)
573             write_buf(keymap, buf, size, offset, "%s", args);
574         write_buf(keymap, buf, size, offset, ")%s", suffix);
575         break;
576     case XkbSA_SetPtrDflt:
577         write_buf(keymap, buf, size, offset, "%sSetPtrDflt(", prefix);
578         if (action->dflt.affect == XkbSA_AffectDfltBtn)
579             write_buf(keymap, buf, size, offset, "affect=button,button=%s%d",
580                       (!(action->dflt.flags & XkbSA_DfltBtnAbsolute) &&
581                        action->dflt.value >= 0) ? "+" : "",
582                       action->dflt.value);
583         write_buf(keymap, buf, size, offset, ")%s", suffix);
584         break;
585     case XkbSA_SwitchScreen:
586         write_buf(keymap, buf, size, offset,
587                   "%sSwitchScreen(screen=%s%d,%ssame)%s", prefix,
588                   (!(action->screen.flags & XkbSA_SwitchAbsolute) &&
589                    action->screen.screen >= 0) ? "+" : "",
590                   action->screen.screen,
591                   (action->screen.flags & XkbSA_SwitchApplication) ? "!" : "",
592                   suffix);
593         break;
594     /* Deprecated actions below here */
595     case XkbSA_SetControls:
596         if (!type)
597             type = "SetControls";
598     case XkbSA_LockControls:
599         if (!type)
600             type = "LockControls";
601         write_buf(keymap, buf, size, offset, "%s%s(controls=%s)%s",
602                   prefix, type, get_control_mask_text(action->ctrls.ctrls),
603                   suffix);
604         break;
605     case XkbSA_ISOLock:
606     case XkbSA_ActionMessage:
607     case XkbSA_RedirectKey:
608     case XkbSA_DeviceBtn:
609     case XkbSA_LockDeviceBtn:
610     case XkbSA_NoAction:
611         /* XXX TODO */
612         write_buf(keymap, buf, size, offset, "%sNoAction()%s", prefix, suffix);
613         break;
614     case XkbSA_XFree86Private:
615     default:
616         write_buf(keymap, buf, size, offset,
617                   "%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",
618                   prefix, action->any.type, action->any.data[0],
619                   action->any.data[1], action->any.data[2],
620                   action->any.data[3], action->any.data[4],
621                   action->any.data[5], action->any.data[6], suffix);
622         break;
623     }
624
625     return true;
626 }
627
628 static bool
629 write_compat(struct xkb_keymap *keymap, char **buf, size_t *size,
630              size_t *offset)
631 {
632     int i;
633     struct xkb_sym_interpret *interp;
634
635     write_buf(keymap, buf, size, offset, "\txkb_compatibility {\n\n");
636
637     write_vmods(keymap, buf, size, offset);
638
639     write_buf(keymap, buf, size, offset, "\t\tinterpret.useModMapMods= AnyLevel;\n");
640     write_buf(keymap, buf, size, offset, "\t\tinterpret.repeat= false;\n");
641     write_buf(keymap, buf, size, offset, "\t\tinterpret.locking= false;\n");
642
643     darray_foreach(interp, keymap->compat->sym_interpret) {
644         char keysym_name[64];
645
646         if (interp->sym == XKB_KEY_NoSymbol)
647             sprintf(keysym_name, "Any");
648         else
649             xkb_keysym_get_name(interp->sym, keysym_name, 64);
650
651         write_buf(keymap, buf, size, offset, "\t\tinterpret %s+%s(%s) {\n",
652                   keysym_name,
653                   get_interp_match_text(interp->match),
654                   get_mod_mask_text(keymap, interp->mods, 0));
655
656         if (interp->virtual_mod != XkbNoModifier) {
657             write_buf(keymap, buf, size, offset, "\t\t\tvirtualModifier= %s;\n",
658                       keymap->names->vmods[interp->virtual_mod]);
659         }
660
661         if (interp->match & XkbSI_LevelOneOnly)
662             write_buf(keymap, buf, size, offset, "\t\t\tuseModMapMods=level1;\n");
663         if (interp->flags & XkbSI_LockingKey)
664             write_buf(keymap, buf, size, offset, "\t\t\tlocking= true;\n");
665         if (interp->flags & XkbSI_AutoRepeat)
666             write_buf(keymap, buf, size, offset, "\t\t\trepeat= true;\n");
667
668         write_action(keymap, buf, size, offset, &interp->act,
669                      "\t\t\taction= ", ";\n");
670         write_buf(keymap, buf, size, offset, "\t\t};\n");
671     }
672
673     for (i = 0; i < XkbNumKbdGroups; i++) {
674         struct xkb_mods *gc;
675
676         gc = &keymap->compat->groups[i];
677         if (gc->real_mods == 0 && gc->vmods ==0)
678             continue;
679         write_buf(keymap, buf, size, offset,
680                   "\t\tgroup %d = %s;\n", i + 1,
681                   get_mod_mask_text(keymap, gc->real_mods, gc->vmods));
682     }
683
684     for (i = 0; i < XkbNumIndicators; i++) {
685         struct xkb_indicator_map *map = &keymap->indicators->maps[i];
686         if (map->flags == 0 && map->which_groups == 0 &&
687             map->groups == 0 && map->which_mods == 0 &&
688             map->mods.real_mods == 0 && map->mods.vmods == 0 &&
689             map->ctrls == 0)
690             continue;
691         write_indicator_map(keymap, buf, size, offset, i);
692     }
693
694     write_buf(keymap, buf, size, offset, "\t};\n\n");
695
696     return true;
697 }
698
699 static bool
700 write_keysyms(struct xkb_keymap *keymap, char **buf, size_t *size,
701               size_t *offset, xkb_keycode_t key, unsigned int group)
702 {
703     const xkb_keysym_t *syms;
704     int num_syms, level;
705 #define OUT_BUF_LEN 128
706     char out_buf[OUT_BUF_LEN];
707
708     for (level = 0; level < XkbKeyGroupWidth(keymap, key, group); level++) {
709         if (level != 0)
710             write_buf(keymap, buf, size, offset, ", ");
711         num_syms = xkb_key_get_syms_by_level(keymap, key, group, level,
712                                              &syms);
713         if (num_syms == 0) {
714             write_buf(keymap, buf, size, offset, "%15s", "NoSymbol");
715         }
716         else if (num_syms == 1) {
717             xkb_keysym_get_name(syms[0], out_buf, OUT_BUF_LEN);
718             write_buf(keymap, buf, size, offset, "%15s", out_buf);
719         }
720         else {
721             int s;
722             write_buf(keymap, buf, size, offset, "{ ");
723             for (s = 0; s < num_syms; s++) {
724                 if (s != 0)
725                     write_buf(keymap, buf, size, offset, ", ");
726                 xkb_keysym_get_name(syms[s], out_buf, OUT_BUF_LEN);
727                 write_buf(keymap, buf, size, offset, "%15s", out_buf);
728             }
729             write_buf(keymap, buf, size, offset, " }");
730         }
731     }
732 #undef OUT_BUF_LEN
733
734     return true;
735 }
736
737 static bool
738 write_symbols(struct xkb_keymap *keymap, char **buf, size_t *size,
739               size_t *offset)
740 {
741     struct xkb_client_map *map = keymap->map;
742     struct xkb_server_map *srv = keymap->server;
743     xkb_keycode_t key;
744     int group, tmp;
745     bool showActions;
746
747     write_buf(keymap, buf, size, offset, "\txkb_symbols {\n\n");
748
749     for (tmp = group = 0; group < XkbNumKbdGroups; group++) {
750         if (!keymap->names->groups[group])
751             continue;
752         write_buf(keymap, buf, size, offset,
753                   "\t\tname[group%d]=\"%s\";\n", group + 1,
754                   keymap->names->groups[group]);
755         tmp++;
756     }
757     if (tmp > 0)
758         write_buf(keymap, buf, size, offset, "\n");
759
760     for (key = keymap->min_key_code; key <= keymap->max_key_code; key++) {
761         bool simple = true;
762
763         if (xkb_key_num_groups(keymap, key) == 0)
764             continue;
765         if (XkbcFindKeycodeByName(keymap,
766                                   darray_item(keymap->names->keys, key).name,
767                                   true) != key)
768             continue;
769
770         write_buf(keymap, buf, size, offset, "\t\tkey %6s {",
771                   XkbcKeyNameText(darray_item(keymap->names->keys, key).name));
772         if (srv->explicit) {
773             if ((srv->explicit[key] & XkbExplicitKeyTypesMask)) {
774                 bool multi_type = false;
775                 int type = XkbKeyTypeIndex(keymap, key, 0);
776
777                 simple = false;
778
779                 for (group = 0; group < xkb_key_num_groups(keymap, key); group++) {
780                     if (XkbKeyTypeIndex(keymap, key, group) != type) {
781                         multi_type = true;
782                         break;
783                     }
784                 }
785                 if (multi_type) {
786                     for (group = 0;
787                          group < xkb_key_num_groups(keymap, key);
788                          group++) {
789                         if (!(srv->explicit[key] & (1 << group)))
790                             continue;
791                         type = XkbKeyTypeIndex(keymap, key, group);
792                         write_buf(keymap, buf, size, offset,
793                                   "\n\t\t\ttype[Group%d]= \"%s\",",
794                                   group + 1,
795                                   darray_item(map->types, type).name);
796                     }
797                 }
798                 else {
799                     write_buf(keymap, buf, size, offset,
800                               "\n\t\t\ttype= \"%s\",",
801                               darray_item(map->types, type).name);
802                 }
803             }
804             if (keymap->ctrls && (srv->explicit[key] & XkbExplicitAutoRepeatMask)) {
805                 if (keymap->ctrls->per_key_repeat[key / 8] & (1 << (key % 8)))
806                      write_buf(keymap, buf, size, offset,
807                                "\n\t\t\trepeat= Yes,");
808                 else
809                     write_buf(keymap, buf, size, offset,
810                               "\n\t\t\trepeat= No,");
811                 simple = false;
812             }
813             if (keymap->server->vmodmap[key] &&
814                 (srv->explicit[key] & XkbExplicitVModMapMask)) {
815                 write_buf(keymap, buf, size, offset, "\n\t\t\tvirtualMods= %s,",
816                           get_mod_mask_text(keymap, 0, keymap->server->vmodmap[key]));
817             }
818         }
819
820         switch (XkbOutOfRangeGroupAction(XkbKeyGroupInfo(keymap, key))) {
821             case XkbClampIntoRange:
822                 write_buf(keymap, buf, size, offset, "\n\t\t\tgroupsClamp,");
823                 break;
824             case XkbRedirectIntoRange:
825                 write_buf(keymap, buf, size, offset,
826                           "\n\t\t\tgroupsRedirect= Group%d,",
827                           XkbOutOfRangeGroupNumber(XkbKeyGroupInfo(keymap, key)) + 1);
828                 break;
829         }
830
831         if (srv->explicit == NULL ||
832             (srv->explicit[key] & XkbExplicitInterpretMask))
833             showActions = XkbKeyHasActions(keymap, key);
834         else
835             showActions = false;
836
837         if (xkb_key_num_groups(keymap, key) > 1 || showActions)
838             simple = false;
839
840         if (simple) {
841             write_buf(keymap, buf, size, offset, "\t[ ");
842             if (!write_keysyms(keymap, buf, size, offset, key, 0))
843                 return false;
844             write_buf(keymap, buf, size, offset, " ] };\n");
845         }
846         else {
847             union xkb_action *acts;
848             int level;
849
850             acts = XkbKeyActionsPtr(keymap, key);
851             for (group = 0; group < xkb_key_num_groups(keymap, key); group++) {
852                 if (group != 0)
853                     write_buf(keymap, buf, size, offset, ",");
854                 write_buf(keymap, buf, size, offset,
855                           "\n\t\t\tsymbols[Group%d]= [ ", group + 1);
856                 if (!write_keysyms(keymap, buf, size, offset, key, group))
857                     return false;
858                 write_buf(keymap, buf, size, offset, " ]");
859                 if (showActions) {
860                     write_buf(keymap, buf, size, offset,
861                               ",\n\t\t\tactions[Group%d]= [ ", group + 1);
862                     for (level = 0;
863                          level < XkbKeyGroupWidth(keymap, key, group);
864                          level++) {
865                         if (level != 0)
866                             write_buf(keymap, buf, size, offset, ", ");
867                         write_action(keymap, buf, size, offset, &acts[level],
868                                      NULL, NULL);
869                     }
870                     write_buf(keymap, buf, size, offset, " ]");
871                     acts += XkbKeyGroupsWidth(keymap, key);
872                 }
873             }
874             write_buf(keymap, buf, size, offset, "\n\t\t};\n");
875         }
876     }
877     if (map && map->modmap) {
878         for (key = keymap->min_key_code; key <= keymap->max_key_code; key++) {
879             int mod;
880             char name[5];
881
882             if (map->modmap[key] == 0)
883                 continue;
884
885             for (mod = 0; mod < XkbNumModifiers; mod++) {
886                 if (!(map->modmap[key] & (1 << mod)))
887                     continue;
888
889                 memcpy(name, darray_item(keymap->names->keys, key).name, 4);
890                 name[4]= '\0';
891
892                 write_buf(keymap, buf, size, offset,
893                           "\t\tmodifier_map %s { <%s> };\n",
894                           get_mod_index_text(mod), name);
895             }
896         }
897     }
898
899     write_buf(keymap, buf, size, offset, "\t};\n\n");
900     return true;
901 }
902
903 _X_EXPORT char *
904 xkb_map_get_as_string(struct xkb_keymap *keymap)
905 {
906     char *ret = NULL;
907     size_t size = 0;
908     size_t offset = 0;
909
910     check_write_buf(keymap, &ret, &size, &offset, "xkb_keymap {\n");
911     if (ret == NULL)
912         return NULL;
913     if (!write_keycodes(keymap, &ret, &size, &offset))
914         return NULL;
915     if (!write_types(keymap, &ret, &size, &offset))
916         return NULL;
917     if (!write_compat(keymap, &ret, &size, &offset))
918         return NULL;
919     if (!write_symbols(keymap, &ret, &size, &offset))
920         return NULL;
921     check_write_buf(keymap, &ret, &size, &offset, "};\n");
922     if (ret == NULL)
923         return NULL;
924
925     return ret;
926 }