1 /*
2 * key.c - Key bindings configuration management
3 *
4 * Copyright © 2008-2009 Julien Danjou <julien@danjou.info>
5 * Copyright © 2008 Pierre Habouzit <madcoder@debian.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 */
22
23 /** awesome key API
24 *
25 * Furthermore to the classes described here, one can also use signals as
26 * described in @{signals}.
27 *
28 * Some signal names are starting with a dot. These dots are artefacts from
29 * the documentation generation, you get the real signal name by
30 * removing the starting dot.
31 *
32 * @author Julien Danjou <julien@danjou.info>
33 * @copyright 2008-2009 Julien Danjou
34 * @classmod key
35 */
36
37 #include "objects/key.h"
38 #include "common/xutil.h"
39 #include "xkb.h"
40
41 /* XStringToKeysym() */
42 #include <X11/Xlib.h>
43 #include <xkbcommon/xkbcommon.h>
44 #include <glib.h>
45
46 lua_class_t key_class;
47
48 /** Key object.
49 *
50 * @tfield string key The key to trigger an event.
51 * @tfield string keysym Same as key, but return the name of the key symbol. It
52 * can be identical to key, but for characters like '.' it will return
53 * 'period'.
54 * @tfield table modifiers The modifier key that should be pressed while the
55 * key is pressed. An array with all the modifiers. Valid modifiers are: Any,
56 * Mod1, Mod2, Mod3, Mod4, Mod5, Shift, Lock and Control.
57 * @table key
58 */
59
60 /**
61 * @signal press
62 */
63
64 /**
65 * @signal property::key
66 */
67
68 /**
69 * @signal property::modifiers
70 */
71
72 /**
73 * @signal release
74 */
75
76 /** Get the number of instances.
77 *
78 * @return The number of key objects alive.
79 * @function instances
80 */
81
82 /** Set a __index metamethod for all key instances.
83 * @tparam function cb The meta-method
84 * @function set_index_miss_handler
85 */
86
87 /** Set a __newindex metamethod for all key instances.
88 * @tparam function cb The meta-method
89 * @function set_newindex_miss_handler
90 */
91
92 static void
luaA_keystore(lua_State * L,int ud,const char * str,ssize_t len)93 luaA_keystore(lua_State *L, int ud, const char *str, ssize_t len)
94 {
95 if(len <= 0 || !str)
96 return;
97
98 keyb_t *key = luaA_checkudata(L, ud, &key_class);
99
100 if(len == 1)
101 {
102 key->keycode = 0;
103 key->keysym = str[0];
104 }
105 else if(str[0] == '#')
106 {
107 key->keycode = atoi(str + 1);
108 key->keysym = 0;
109 }
110 else
111 {
112 key->keycode = 0;
113
114 if((key->keysym = XStringToKeysym(str)) == NoSymbol )
115 {
116 glong length;
117 gunichar unicode;
118
119 if(!g_utf8_validate(str, -1, NULL))
120 {
121 luaA_warn(L, "failed to convert \"%s\" into keysym (invalid UTF-8 string)", str);
122 return;
123 }
124
125 length = g_utf8_strlen(str, -1); /* This function counts combining characters. */
126 if(length <= 0)
127 {
128 luaA_warn(L, "failed to convert \"%s\" into keysym (empty UTF-8 string)", str);
129 return;
130 }
131 else if(length > 1)
132 {
133 gchar *composed = g_utf8_normalize(str, -1, G_NORMALIZE_DEFAULT_COMPOSE);
134 if(g_utf8_strlen(composed, -1) != 1)
135 {
136 p_delete(&composed);
137 luaA_warn(L, "failed to convert \"%s\" into keysym (failed to compose a single character)", str);
138 return;
139 }
140 unicode = g_utf8_get_char(composed);
141 p_delete(&composed);
142 }
143 else
144 unicode = g_utf8_get_char(str);
145
146 if(unicode == (gunichar)-1 || unicode == (gunichar)-2)
147 {
148 luaA_warn(L, "failed to convert \"%s\" into keysym (neither keysym nor single unicode)", str);
149 return;
150 }
151
152 /* Unicode-to-Keysym Conversion
153 *
154 * http://www.x.org/releases/X11R7.7/doc/xproto/x11protocol.html#keysym_encoding
155 */
156 if(unicode <= 0x0ff)
157 key->keysym = unicode;
158 else if(unicode >= 0x100 && unicode <= 0x10ffff)
159 key->keysym = unicode | (1 << 24);
160 else
161 {
162 luaA_warn(L, "failed to convert \"%s\" into keysym (unicode out of range): \"%u\"", str, unicode);
163 return;
164 }
165 }
166 }
167
168 luaA_object_emit_signal(L, ud, "property::key", 0);
169 }
170
171 /** Create a new key object.
172 * \param L The Lua VM state.
173 * \return The number of elements pushed on stack.
174 */
175 static int
luaA_key_new(lua_State * L)176 luaA_key_new(lua_State *L)
177 {
178 return luaA_class_new(L, &key_class);
179 }
180
181 /** Set a key array with a Lua table.
182 * \param L The Lua VM state.
183 * \param oidx The index of the object to store items into.
184 * \param idx The index of the Lua table.
185 * \param keys The array key to fill.
186 */
187 void
luaA_key_array_set(lua_State * L,int oidx,int idx,key_array_t * keys)188 luaA_key_array_set(lua_State *L, int oidx, int idx, key_array_t *keys)
189 {
190 luaA_checktable(L, idx);
191
192 foreach(key, *keys)
193 luaA_object_unref_item(L, oidx, *key);
194
195 key_array_wipe(keys);
196 key_array_init(keys);
197
198 lua_pushnil(L);
199 while(lua_next(L, idx))
200 if(luaA_toudata(L, -1, &key_class))
201 key_array_append(keys, luaA_object_ref_item(L, oidx, -1));
202 else
203 lua_pop(L, 1);
204 }
205
206 /** Push an array of key as an Lua table onto the stack.
207 * \param L The Lua VM state.
208 * \param oidx The index of the object to get items from.
209 * \param keys The key array to push.
210 * \return The number of elements pushed on stack.
211 */
212 int
luaA_key_array_get(lua_State * L,int oidx,key_array_t * keys)213 luaA_key_array_get(lua_State *L, int oidx, key_array_t *keys)
214 {
215 lua_createtable(L, keys->len, 0);
216 for(int i = 0; i < keys->len; i++)
217 {
218 luaA_object_push_item(L, oidx, keys->tab[i]);
219 lua_rawseti(L, -2, i + 1);
220 }
221 return 1;
222 }
223
224 /** Push a modifier set to a Lua table.
225 * \param L The Lua VM state.
226 * \param modifiers The modifier.
227 * \return The number of elements pushed on stack.
228 */
229 int
luaA_pushmodifiers(lua_State * L,uint16_t modifiers)230 luaA_pushmodifiers(lua_State *L, uint16_t modifiers)
231 {
232 lua_newtable(L);
233 {
234 int i = 1;
235 for(uint32_t maski = XCB_MOD_MASK_SHIFT; maski <= XCB_BUTTON_MASK_ANY; maski <<= 1)
236 if(maski & modifiers)
237 {
238 const char *mod;
239 size_t slen;
240 xutil_key_mask_tostr(maski, &mod, &slen);
241 lua_pushlstring(L, mod, slen);
242 lua_rawseti(L, -2, i++);
243 }
244 }
245 return 1;
246 }
247
248 /** Take a modifier table from the stack and return modifiers mask.
249 * \param L The Lua VM state.
250 * \param ud The index of the table.
251 * \return The mask value.
252 */
253 uint16_t
luaA_tomodifiers(lua_State * L,int ud)254 luaA_tomodifiers(lua_State *L, int ud)
255 {
256 luaA_checktable(L, ud);
257 ssize_t len = luaA_rawlen(L, ud);
258 uint16_t mod = XCB_NONE;
259 for(int i = 1; i <= len; i++)
260 {
261 lua_rawgeti(L, ud, i);
262 const char *key = luaL_checkstring(L, -1);
263 mod |= xutil_key_mask_fromstr(key);
264 lua_pop(L, 1);
265 }
266 return mod;
267 }
268
269 static int
luaA_key_set_modifiers(lua_State * L,keyb_t * k)270 luaA_key_set_modifiers(lua_State *L, keyb_t *k)
271 {
272 k->modifiers = luaA_tomodifiers(L, -1);
273 luaA_object_emit_signal(L, -3, "property::modifiers", 0);
274 return 0;
275 }
276
LUA_OBJECT_EXPORT_PROPERTY(key,keyb_t,modifiers,luaA_pushmodifiers)277 LUA_OBJECT_EXPORT_PROPERTY(key, keyb_t, modifiers, luaA_pushmodifiers)
278
279 /* It's caller's responsibility to release the returned string. */
280 char *
281 key_get_keysym_name(xkb_keysym_t keysym)
282 {
283 const ssize_t bufsize = 64;
284 char *buf = p_new(char, bufsize);
285 ssize_t len;
286
287 if((len = xkb_keysym_get_name(keysym, buf, bufsize)) == -1)
288 {
289 p_delete(&buf);
290 return NULL;
291 }
292 if(len + 1 > bufsize)
293 {
294 p_realloc(&buf, len + 1);
295 if(xkb_keysym_get_name(keysym, buf, len + 1) != len)
296 {
297 p_delete(&buf);
298 return NULL;
299 }
300 }
301 return buf;
302 }
303
304 static int
luaA_key_get_key(lua_State * L,keyb_t * k)305 luaA_key_get_key(lua_State *L, keyb_t *k)
306 {
307 if(k->keycode)
308 {
309 char buf[12];
310 int slen = snprintf(buf, sizeof(buf), "#%u", k->keycode);
311 lua_pushlstring(L, buf, slen);
312 }
313 else
314 {
315 char *name = key_get_keysym_name(k->keysym);
316 if(!name)
317 return 0;
318 lua_pushstring(L, name);
319 p_delete(&name);
320 }
321 return 1;
322 }
323
324 static int
luaA_key_get_keysym(lua_State * L,keyb_t * k)325 luaA_key_get_keysym(lua_State *L, keyb_t *k)
326 {
327 char *name = key_get_keysym_name(k->keysym);
328 if(!name)
329 return 0;
330 lua_pushstring(L, name);
331 p_delete(&name);
332 return 1;
333 }
334
335 static int
luaA_key_set_key(lua_State * L,keyb_t * k)336 luaA_key_set_key(lua_State *L, keyb_t *k)
337 {
338 size_t klen;
339 const char *key = luaL_checklstring(L, -1, &klen);
340 luaA_keystore(L, -3, key, klen);
341 return 0;
342 }
343
344 void
key_class_setup(lua_State * L)345 key_class_setup(lua_State *L)
346 {
347 static const struct luaL_Reg key_methods[] =
348 {
349 LUA_CLASS_METHODS(key)
350 { "__call", luaA_key_new },
351 { NULL, NULL }
352 };
353
354 static const struct luaL_Reg key_meta[] =
355 {
356 LUA_OBJECT_META(key)
357 LUA_CLASS_META
358 { NULL, NULL },
359 };
360
361 luaA_class_setup(L, &key_class, "key", NULL,
362 (lua_class_allocator_t) key_new, NULL, NULL,
363 luaA_class_index_miss_property, luaA_class_newindex_miss_property,
364 key_methods, key_meta);
365 luaA_class_add_property(&key_class, "key",
366 (lua_class_propfunc_t) luaA_key_set_key,
367 (lua_class_propfunc_t) luaA_key_get_key,
368 (lua_class_propfunc_t) luaA_key_set_key);
369 luaA_class_add_property(&key_class, "keysym",
370 NULL,
371 (lua_class_propfunc_t) luaA_key_get_keysym,
372 NULL);
373 luaA_class_add_property(&key_class, "modifiers",
374 (lua_class_propfunc_t) luaA_key_set_modifiers,
375 (lua_class_propfunc_t) luaA_key_get_modifiers,
376 (lua_class_propfunc_t) luaA_key_set_modifiers);
377 }
378
379 /* @DOC_cobject_COMMON@ */
380
381 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
382