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 &lt;julien@danjou.info&gt;
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