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