Add xkb_map_get_as_string
[profile/ivi/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     if (action->any.type == XkbSA_NoAction)
485         return true;
486
487     switch (action->any.type) {
488     case XkbSA_SetMods:
489         if (!type)
490             type = "SetMods";
491     case XkbSA_LatchMods:
492         if (!type)
493             type = "LatchMods";
494     case XkbSA_LockMods:
495         if (!type)
496             type = "LockMods";
497         if (action->mods.flags & XkbSA_UseModMapMods)
498             args = "modMapMods";
499         else
500             args = get_mod_mask_text(keymap, action->mods.real_mods,
501                                      action->mods.vmods);
502         write_buf(keymap, buf, size, offset, "%s%s(modifiers=%s%s%s)%s",
503                   prefix, type, args,
504                   (action->any.type != XkbSA_LockGroup &&
505                    action->mods.flags & XkbSA_ClearLocks) ? ",clearLocks" : "",
506                   (action->any.type != XkbSA_LockGroup &&
507                    action->mods.flags & XkbSA_LatchToLock) ? ",latchToLock" : "",
508                   suffix);
509         break;
510     case XkbSA_SetGroup:
511         if (!type)
512             type = "SetGroup";
513     case XkbSA_LatchGroup:
514         if (!type)
515             type = "LatchGroup";
516     case XkbSA_LockGroup:
517         if (!type)
518             type = "LockGroup";
519         write_buf(keymap, buf, size, offset, "%s%s(group=%s%d%s%s)%s",
520                   prefix, type,
521                   (!(action->group.flags & XkbSA_GroupAbsolute) &&
522                    action->group.group > 0) ? "+" : "",
523                   (action->group.flags & XkbSA_GroupAbsolute) ?
524                    action->group.group + 1 : action->group.group,
525                   (action->any.type != XkbSA_LockGroup &&
526                    action->group.flags & XkbSA_ClearLocks) ? ",clearLocks" : "",
527                   (action->any.type != XkbSA_LockGroup &&
528                    action->group.flags & XkbSA_LatchToLock) ? ",latchToLock" : "",
529                   suffix);
530         break;
531     case XkbSA_Terminate:
532         write_buf(keymap, buf, size, offset, "%sTerminate()%s", prefix, suffix);
533         break;
534     case XkbSA_MovePtr:
535         write_buf(keymap, buf, size, offset, "%sMovePtr(x=%s%d,y=%s%d%s)%s",
536                   prefix,
537                   (!(action->ptr.flags & XkbSA_MoveAbsoluteX) &&
538                    action->ptr.x >= 0) ? "+" : "",
539                   action->ptr.x,
540                   (!(action->ptr.flags & XkbSA_MoveAbsoluteY) &&
541                    action->ptr.y >= 0) ? "+" : "",
542                   action->ptr.y,
543                   (action->ptr.flags & XkbSA_NoAcceleration) ? ",!accel" : "",
544                   suffix);
545         break;
546     case XkbSA_PtrBtn:
547         if (!type)
548             type = "PtrBtn";
549     case XkbSA_LockPtrBtn:
550         if (!type) {
551             type = "LockPtrBtn";
552             switch (action->btn.flags & (XkbSA_LockNoUnlock | XkbSA_LockNoLock)) {
553             case XkbSA_LockNoUnlock:
554                 args = ",affect=lock";
555                 break;
556             case XkbSA_LockNoLock:
557                 args = ",affect=unlock";
558                 break;
559             case XkbSA_LockNoLock | XkbSA_LockNoUnlock:
560                 args = ",affect=neither";
561                 break;
562             default:
563                 args = ",affect=both";
564                 break;
565             }
566         }
567         else {
568             args = NULL;
569         }
570         write_buf(keymap, buf, size, offset, "%s%s(button=", prefix, type);
571         if (action->btn.button > 0 && action->btn.button <= 5)
572             write_buf(keymap, buf, size, offset, "%d", action->btn.button);
573         else
574             write_buf(keymap, buf, size, offset, "default");
575         if (action->btn.count)
576             write_buf(keymap, buf, size, offset, ",count=%d",
577                       action->btn.count);
578         if (args)
579             write_buf(keymap, buf, size, offset, "%s", args);
580         write_buf(keymap, buf, size, offset, ")%s", suffix);
581         break;
582     case XkbSA_SetPtrDflt:
583         write_buf(keymap, buf, size, offset, "%sSetPtrDflt(", prefix);
584         if (action->dflt.affect == XkbSA_AffectDfltBtn)
585             write_buf(keymap, buf, size, offset, "affect=button,button=%s%d",
586                       (!(action->dflt.flags & XkbSA_DfltBtnAbsolute) &&
587                        action->dflt.value >= 0) ? "+" : "",
588                       action->dflt.value);
589         write_buf(keymap, buf, size, offset, ")%s", suffix);
590         break;
591     case XkbSA_SwitchScreen:
592         write_buf(keymap, buf, size, offset,
593                   "%sSwitchScreen(screen=%s%d,%ssame)%s", prefix,
594                   (!(action->screen.flags & XkbSA_SwitchAbsolute) &&
595                    action->screen.screen >= 0) ? "+" : "",
596                   action->screen.screen,
597                   (action->screen.flags & XkbSA_SwitchApplication) ? "!" : "",
598                   suffix);
599         break;
600     /* Deprecated actions below here */
601     case XkbSA_SetControls:
602         if (!type)
603             type = "SetControls";
604     case XkbSA_LockControls:
605         if (!type)
606             type = "LockControls";
607         write_buf(keymap, buf, size, offset, "%s%s(controls=%s)%s",
608                   prefix, type, get_control_mask_text(action->ctrls.ctrls),
609                   suffix);
610         break;
611     case XkbSA_ISOLock:
612     case XkbSA_ActionMessage:
613     case XkbSA_RedirectKey:
614     case XkbSA_DeviceBtn:
615     case XkbSA_LockDeviceBtn:
616         /* XXX TODO */
617         write_buf(keymap, buf, size, offset, "%sNoAction()%s", prefix, suffix);
618         break;
619     case XkbSA_XFree86Private:
620     default:
621         write_buf(keymap, buf, size, offset,
622                   "%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",
623                   prefix, action->any.type, action->any.data[0],
624                   action->any.data[1], action->any.data[2],
625                   action->any.data[3], action->any.data[4],
626                   action->any.data[5], action->any.data[6], suffix);
627         break;
628     }
629
630     return true;
631 }
632
633 static bool
634 write_compat(struct xkb_keymap *keymap, char **buf, size_t *size,
635              size_t *offset)
636 {
637     int i;
638     struct xkb_sym_interpret *interp;
639
640     write_buf(keymap, buf, size, offset, "\txkb_compatibility {\n\n");
641
642     write_vmods(keymap, buf, size, offset);
643
644     write_buf(keymap, buf, size, offset, "\t\tinterpret.useModMapMods= AnyLevel;\n");
645     write_buf(keymap, buf, size, offset, "\t\tinterpret.repeat= false;\n");
646     write_buf(keymap, buf, size, offset, "\t\tinterpret.locking= false;\n");
647
648     for (i = 0; i < darray_size(keymap->compat->sym_interpret); i++) {
649         char keysym_name[64];
650         interp = &darray_item(keymap->compat->sym_interpret, i);
651
652         if (interp->sym == XKB_KEY_NoSymbol)
653             sprintf(keysym_name, "Any");
654         else
655             xkb_keysym_get_name(interp->sym, keysym_name, 64);
656
657         write_buf(keymap, buf, size, offset, "\t\tinterpret %s+%s(%s) {\n",
658                   keysym_name,
659                   get_interp_match_text(interp->match),
660                   get_mod_mask_text(keymap, interp->mods, 0));
661
662         if (interp->virtual_mod != XkbNoModifier) {
663             write_buf(keymap, buf, size, offset, "\t\t\tvirtualModifier= %s;\n",
664                       keymap->names->vmods[interp->virtual_mod]);
665         }
666
667         if (interp->match & XkbSI_LevelOneOnly)
668             write_buf(keymap, buf, size, offset, "\t\t\tuseModMapMods=level1;\n");
669         if (interp->flags & XkbSI_LockingKey)
670             write_buf(keymap, buf, size, offset, "\t\t\tlocking= true;\n");
671         if (interp->flags & XkbSI_AutoRepeat)
672             write_buf(keymap, buf, size, offset, "\t\t\trepeat= true;\n");
673
674         write_action(keymap, buf, size, offset, &interp->act,
675                      "\t\t\taction= ", ";\n");
676         write_buf(keymap, buf, size, offset, "\t\t};\n");
677     }
678
679     for (i = 0; i < XkbNumKbdGroups; i++) {
680         struct xkb_mods *gc;
681
682         gc = &keymap->compat->groups[i];
683         if (gc->real_mods == 0 && gc->vmods ==0)
684             continue;
685         write_buf(keymap, buf, size, offset,
686                   "\t\tgroup %d = %s;\n", i + 1,
687                   get_mod_mask_text(keymap, gc->real_mods, gc->vmods));
688     }
689
690     for (i = 0; i < XkbNumIndicators; i++) {
691         struct xkb_indicator_map *map = &keymap->indicators->maps[i];
692         if (map->flags == 0 && map->which_groups == 0 &&
693             map->groups == 0 && map->which_mods == 0 &&
694             map->mods.real_mods == 0 && map->mods.vmods == 0 &&
695             map->ctrls == 0)
696             continue;
697         write_indicator_map(keymap, buf, size, offset, i);
698     }
699
700     write_buf(keymap, buf, size, offset, "\t};\n\n");
701
702     return true;
703 }
704
705 static bool
706 write_keysyms(struct xkb_keymap *keymap, char **buf, size_t *size,
707               size_t *offset, xkb_keycode_t key, unsigned int group)
708 {
709     const xkb_keysym_t *syms;
710     int num_syms, level;
711 #define OUT_BUF_LEN 128
712     char out_buf[OUT_BUF_LEN];
713
714     for (level = 0; level < XkbKeyGroupWidth(keymap, key, group); level++) {
715         if (level != 0)
716             write_buf(keymap, buf, size, offset, ", ");
717         num_syms = xkb_key_get_syms_by_level(keymap, key, group, level,
718                                              &syms);
719         if (num_syms == 0) {
720             write_buf(keymap, buf, size, offset, "%15s", "NoSymbol");
721         }
722         else if (num_syms == 1) {
723             xkb_keysym_get_name(syms[0], out_buf, OUT_BUF_LEN);
724             write_buf(keymap, buf, size, offset, "%15s", out_buf);
725         }
726         else {
727             int s;
728             write_buf(keymap, buf, size, offset, "{ ");
729             for (s = 0; s < num_syms; s++) {
730                 if (s != 0)
731                     write_buf(keymap, buf, size, offset, ", ");
732                 xkb_keysym_get_name(syms[s], out_buf, OUT_BUF_LEN);
733                 write_buf(keymap, buf, size, offset, "%15s", out_buf);
734             }
735             write_buf(keymap, buf, size, offset, " }");
736         }
737     }
738 #undef OUT_BUF_LEN
739
740     return true;
741 }
742
743 static bool
744 write_symbols(struct xkb_keymap *keymap, char **buf, size_t *size,
745               size_t *offset)
746 {
747     struct xkb_client_map *map = keymap->map;
748     struct xkb_server_map *srv = keymap->server;
749     xkb_keycode_t key;
750     int group, tmp;
751     bool showActions;
752
753     write_buf(keymap, buf, size, offset, "\txkb_symbols {\n\n");
754
755     for (tmp = group = 0; group < XkbNumKbdGroups; group++) {
756         if (!keymap->names->groups[group])
757             continue;
758         write_buf(keymap, buf, size, offset,
759                   "\t\tname[group%d]=\"%s\";\n", group + 1,
760                   keymap->names->groups[group]);
761         tmp++;
762     }
763     if (tmp > 0)
764         write_buf(keymap, buf, size, offset, "\n");
765
766     for (key = keymap->min_key_code; key <= keymap->max_key_code; key++) {
767         bool simple = true;
768
769         if (xkb_key_num_groups(keymap, key) == 0)
770             continue;
771         if (XkbcFindKeycodeByName(keymap,
772                                   darray_item(keymap->names->keys, key).name,
773                                   true) != key)
774             continue;
775
776         write_buf(keymap, buf, size, offset, "\t\tkey %6s {",
777                   XkbcKeyNameText(darray_item(keymap->names->keys, key).name));
778         if (srv->explicit) {
779             if ((srv->explicit[key] & XkbExplicitKeyTypesMask)) {
780                 bool multi_type = false;
781                 int type = XkbKeyTypeIndex(keymap, key, 0);
782
783                 simple = false;
784
785                 for (group = 0; group < xkb_key_num_groups(keymap, key); group++) {
786                     if (XkbKeyTypeIndex(keymap, key, group) != type) {
787                         multi_type = true;
788                         break;
789                     }
790                 }
791                 if (multi_type) {
792                     for (group = 0;
793                          group < xkb_key_num_groups(keymap, key);
794                          group++) {
795                         if (!(srv->explicit[key] & (1 << group)))
796                             continue;
797                         type = XkbKeyTypeIndex(keymap, key, group);
798                         write_buf(keymap, buf, size, offset,
799                                   "\n\t\t\ttype[Group%d]= \"%s\",",
800                                   group + 1,
801                                   darray_item(map->types, type).name);
802                     }
803                 }
804                 else {
805                     write_buf(keymap, buf, size, offset,
806                               "\n\t\t\ttype= \"%s\",",
807                               darray_item(map->types, type).name);
808                 }
809             }
810             if (keymap->ctrls && (srv->explicit[key] & XkbExplicitAutoRepeatMask)) {
811                 if (keymap->ctrls->per_key_repeat[key / 8] & (1 << (key % 8)))
812                      write_buf(keymap, buf, size, offset,
813                                "\n\t\t\trepeat= Yes,");
814                 else
815                     write_buf(keymap, buf, size, offset,
816                               "\n\t\t\trepeat= No,");
817                 simple = false;
818             }
819             if (keymap->server->vmodmap[key] &&
820                 (srv->explicit[key] & XkbExplicitVModMapMask)) {
821                 write_buf(keymap, buf, size, offset, "\n\t\t\tvirtualMods= %s,",
822                           get_mod_mask_text(keymap, 0, keymap->server->vmodmap[key]));
823             }
824         }
825
826         switch (XkbOutOfRangeGroupAction(XkbKeyGroupInfo(keymap, key))) {
827             case XkbClampIntoRange:
828                 write_buf(keymap, buf, size, offset, "\n\t\t\tgroupsClamp,");
829                 break;
830             case XkbRedirectIntoRange:
831                 write_buf(keymap, buf, size, offset,
832                           "\n\t\t\tgroupsRedirect= Group%d,",
833                           XkbOutOfRangeGroupNumber(XkbKeyGroupInfo(keymap, key)) + 1);
834                 break;
835         }
836
837         if (srv->explicit == NULL ||
838             (srv->explicit[key] & XkbExplicitInterpretMask))
839             showActions = XkbKeyHasActions(keymap, key);
840         else
841             showActions = false;
842
843         if (xkb_key_num_groups(keymap, key) > 1 || showActions)
844             simple = false;
845
846         if (simple) {
847             write_buf(keymap, buf, size, offset, "\t[ ");
848             if (!write_keysyms(keymap, buf, size, offset, key, 0))
849                 return false;
850             write_buf(keymap, buf, size, offset, " ] };\n");
851         }
852         else {
853             union xkb_action *acts;
854             int level;
855
856             acts = XkbKeyActionsPtr(keymap, key);
857             for (group = 0; group < xkb_key_num_groups(keymap, key); group++) {
858                 if (group != 0)
859                     write_buf(keymap, buf, size, offset, ",");
860                 write_buf(keymap, buf, size, offset,
861                           "\n\t\t\tsymbols[Group%d]= [ ", group + 1);
862                 if (!write_keysyms(keymap, buf, size, offset, key, group))
863                     return false;
864                 write_buf(keymap, buf, size, offset, " ]");
865                 if (showActions) {
866                     write_buf(keymap, buf, size, offset,
867                               ",\n\t\t\tactions[Group%d]= [ ", group + 1);
868                     for (level = 0;
869                          level < XkbKeyGroupWidth(keymap, key, group);
870                          level++) {
871                         if (level != 0)
872                             write_buf(keymap, buf, size, offset, ", ");
873                         write_action(keymap, buf, size, offset, &acts[level],
874                                      NULL, NULL);
875                     }
876                     write_buf(keymap, buf, size, offset, " ]");
877                     acts += XkbKeyGroupsWidth(keymap, key);
878                 }
879             }
880             write_buf(keymap, buf, size, offset, "\n\t\t};\n");
881         }
882     }
883     if (map && map->modmap) {
884         for (key = keymap->min_key_code; key <= keymap->max_key_code; key++) {
885             int mod;
886             char name[5];
887
888             if (map->modmap[key] == 0)
889                 continue;
890
891             for (mod = 0; mod < XkbNumModifiers; mod++) {
892                 if (!(map->modmap[key] & (1 << mod)))
893                     continue;
894
895                 memcpy(name, darray_item(keymap->names->keys, key).name, 4);
896                 name[4]= '\0';
897
898                 write_buf(keymap, buf, size, offset,
899                           "\t\tmodifier_map %s { <%s> };\n",
900                           get_mod_index_text(mod), name);
901             }
902         }
903     }
904
905     write_buf(keymap, buf, size, offset, "\t};\n\n");
906     return true;
907 }
908
909 _X_EXPORT char *
910 xkb_map_get_as_string(struct xkb_keymap *keymap)
911 {
912     char *ret = NULL;
913     size_t size = 0;
914     size_t offset = 0;
915
916     check_write_buf(keymap, &ret, &size, &offset, "xkb_keymap {\n");
917     if (ret == NULL)
918         return NULL;
919     if (!write_keycodes(keymap, &ret, &size, &offset))
920         return NULL;
921     if (!write_types(keymap, &ret, &size, &offset))
922         return NULL;
923     if (!write_compat(keymap, &ret, &size, &offset))
924         return NULL;
925     if (!write_symbols(keymap, &ret, &size, &offset))
926         return NULL;
927     check_write_buf(keymap, &ret, &size, &offset, "};\n");
928     if (ret == NULL)
929         return NULL;
930
931     return ret;
932 }