1 /*
2 
3   Copyright (c) 2007-2013 uim Project https://github.com/uim/uim
4 
5   All rights reserved.
6 
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10 
11   1. Redistributions of source code must retain the above copyright
12      notice, this list of conditions and the following disclaimer.
13   2. Redistributions in binary form must reproduce the above copyright
14      notice, this list of conditions and the following disclaimer in the
15      documentation and/or other materials provided with the distribution.
16   3. Neither the name of authors nor the names of its contributors
17      may be used to endorse or promote products derived from this software
18      without specific prior written permission.
19 
20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30   SUCH DAMAGE.
31 
32 */
33 
34 #include <string.h>
35 
36 #include <X11/XKBlib.h>
37 
38 #include "uim-scm.h"
39 #include "uim-scm-abbrev.h"
40 #include "dynlib.h"
41 #include "uim-x-util.h"
42 
43 static XkbDescPtr xkb = NULL;
44 
45 static uim_lisp
xkb_set_display(uim_lisp lisp_display)46 xkb_set_display(uim_lisp lisp_display)
47 {
48     Display *c_display = (Display *)C_PTR(lisp_display);
49 
50     if (! XkbQueryExtension(c_display, NULL, NULL, NULL, NULL, NULL))
51 	return uim_scm_f();
52     if (xkb != NULL) XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
53     if ((xkb = XkbAllocKeyboard()) == NULL) return uim_scm_f();
54     xkb->dpy = c_display;
55 
56     return uim_scm_t();
57 }
58 
59 static uim_lisp
xkb_levels(XkbDescPtr xkb,KeyCode kc,int group)60 xkb_levels(XkbDescPtr xkb, KeyCode kc, int group)
61 {
62     int l, nlevels = XkbKeyGroupWidth(xkb, kc, group);
63     uim_lisp levels = uim_scm_null();
64 
65     for (l = nlevels - 1; l >= 0; l--) {
66 	int ukey = uim_x_keysym2ukey(XkbKeySymEntry(xkb, kc, l, group));
67 	levels = CONS(MAKE_INT(ukey), levels);
68     }
69 
70     return levels;
71 }
72 
73 static uim_lisp
xkb_groups(XkbDescPtr xkb,KeyCode kc)74 xkb_groups(XkbDescPtr xkb, KeyCode kc)
75 {
76     char name[XkbKeyNameLength + 1];
77     int g, ngroups;
78     uim_lisp groups;
79 
80     name[XkbKeyNameLength] = '\0';
81     strncpy(name, xkb->names->keys[kc].name, XkbKeyNameLength);
82     if (name[0] == '\0') return uim_scm_f();
83 
84     ngroups = XkbKeyNumGroups(xkb, kc);
85     if (ngroups == 0) return uim_scm_f();
86     groups = uim_scm_null();
87     for (g = ngroups - 1; g >= 0; g--)
88 	groups = CONS(xkb_levels(xkb, kc, g), groups);
89 
90     return CONS(MAKE_INT(kc), CONS(MAKE_SYM(name), groups));
91 }
92 
93 static uim_lisp
xkb_lib_display_readyp(void)94 xkb_lib_display_readyp(void)
95 {
96     return MAKE_BOOL(xkb != NULL && xkb->dpy != NULL);
97 }
98 
99 /*
100  * returns a list of which each element is of the form
101  *
102  *     (xkeycode xkbname group1 group2 ...)
103  *
104  * in which each of groupn is a list of keysyms at different shift
105  * levels available in the group, and xkbname, a scheme symbol, is a
106  * symbolic key name as returned by XKB.  Keysyms are returned after
107  * being converted to uim keys which can differ from X keysyms.
108  */
109 static uim_lisp
xkb_lib_get_map(void)110 xkb_lib_get_map(void)
111 {
112     int kc;
113     uim_lisp map;
114 
115     if (xkb == NULL || xkb->dpy == NULL) return uim_scm_f();
116 
117     if (XkbGetUpdatedMap(xkb->dpy, XkbAllClientInfoMask, xkb) != Success)
118 	return uim_scm_f();
119     if (XkbGetNames(xkb->dpy, XkbKeyNamesMask, xkb) != Success)
120 	return uim_scm_f();
121 
122     map = uim_scm_null();
123     for (kc = xkb->max_key_code; kc >= xkb->min_key_code; kc--) {
124 	uim_lisp groups = xkb_groups(xkb, kc);
125 	if (TRUEP(groups)) map = CONS(groups, map);
126     }
127 
128     return map;
129 }
130 
131 static uim_lisp
xkb_lib_get_groups_wrap_control(void)132 xkb_lib_get_groups_wrap_control(void)
133 {
134     unsigned char ngroups;
135 
136     if (xkb == NULL || xkb->dpy == NULL) return uim_scm_f();
137 
138     if (XkbGetControls(xkb->dpy, XkbGroupsWrapMask, xkb) != Success)
139 	return uim_scm_f();
140     ngroups = xkb->ctrls->num_groups;
141 
142     switch (xkb->ctrls->groups_wrap & (3L << 6)) {
143     case XkbWrapIntoRange:
144 	return LIST2(MAKE_SYM("wrap-into-range"), MAKE_INT(ngroups));
145     case XkbClampIntoRange:
146 	return LIST2(MAKE_SYM("clamp-into-range"), MAKE_INT(ngroups));
147     case XkbRedirectIntoRange:
148 	return LIST2(MAKE_INT(xkb->ctrls->groups_wrap & 0xf),
149 		     MAKE_INT(ngroups));
150     default:
151 	return uim_scm_f();
152     }
153 }
154 
155 static uim_lisp
xkb_lib_get_group(void)156 xkb_lib_get_group(void)
157 {
158     XkbStateRec state;
159 
160     if (xkb == NULL || xkb->dpy == NULL) return uim_scm_f();
161     if (XkbGetState(xkb->dpy, XkbUseCoreKbd, &state) != Success)
162 	return uim_scm_f();
163 
164     return MAKE_INT(state.group);
165 }
166 
167 static uim_lisp
xkb_open_display(void)168 xkb_open_display(void)
169 {
170     Display *display = XkbOpenDisplay(NULL, NULL, NULL, NULL, NULL, NULL);
171     if (display == NULL) return uim_scm_f();
172     return MAKE_PTR(display);
173 }
174 
175 void
uim_dynlib_instance_init(void)176 uim_dynlib_instance_init(void)
177 {
178     uim_scm_init_proc0("xkb-lib-display-ready?", xkb_lib_display_readyp);
179     uim_scm_init_proc0("xkb-lib-get-map", xkb_lib_get_map);
180     uim_scm_init_proc0("xkb-lib-get-groups-wrap-control",
181 		       xkb_lib_get_groups_wrap_control);
182     uim_scm_init_proc0("xkb-lib-get-group", xkb_lib_get_group);
183 
184     uim_scm_init_proc1("%xkb-set-display", xkb_set_display);
185     uim_scm_init_proc0("%xkb-open-display", xkb_open_display);
186 }
187 
188 void
uim_dynlib_instance_quit(void)189 uim_dynlib_instance_quit(void)
190 {
191     if (xkb != NULL) XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
192     xkb = NULL;
193 }
194