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