Better error handling when getting sizehints fails
[notion.git] / mod_xkbevents / mod_xkbevents.c
blob215aecd8e7cc8b682b1cd703aac28fa0ef579314
1 /*
2 * Ion xkb events
4 * Copyright (c) Sergey Redin 2006.
5 * Copyright (c) Etan Reisner 2011.
7 * Released under the MIT License.
8 */
10 #include <X11/XKBlib.h>
11 #include <X11/extensions/XKB.h>
13 #include "ioncore/event.h"
14 #include "ioncore/global.h"
15 #include "ioncore/xwindow.h"
17 #include "exports.h"
19 static int xkb_event_code, xkb_error_code;
20 WHook *xkb_group_event=NULL, *xkb_bell_event=NULL;
22 INTRSTRUCT(WAnyParams);
23 DECLSTRUCT(WAnyParams){
24 bool send_event; /* True => synthetically generated */
25 Time time; /* server time when event generated */
26 unsigned int device; /* Xkb device ID, will not be XkbUseCoreKbd */
29 INTRSTRUCT(WGroupParams);
30 DECLSTRUCT(WGroupParams){
31 WAnyParams any;
33 int group;
34 int base_group;
35 int latched_group;
36 int locked_group;
39 INTRSTRUCT(WBellParams);
40 DECLSTRUCT(WBellParams){
41 WAnyParams any;
43 int percent;
44 int pitch;
45 int duration;
46 unsigned int bell_class;
47 unsigned int bell_id;
48 char *name;
49 WClientWin *window;
50 bool event_only;
53 /*{{{ Module information */
55 #include "version.h"
57 char mod_xkbevents_ion_api_version[]=ION_API_VERSION;
59 /*}}} Module information */
61 static char *get_atom_name(Atom atom)
63 char *xatomname, *atomname;
65 xatomname = XGetAtomName(ioncore_g.dpy, atom);
66 atomname = scopy(xatomname);
67 XFree(xatomname);
69 return atomname;
72 static bool docall(ExtlFn fn, ExtlTab t)
74 bool ret;
76 extl_protect(NULL);
77 ret=extl_call(fn, "t", NULL, t);
78 extl_unprotect(NULL);
80 extl_unref_table(t);
82 return ret;
85 #define MRSH_ANY(PRM,TAB) \
86 extl_table_sets_b(TAB, "send_event", PRM->any.send_event); \
87 extl_table_sets_i(TAB, "time", PRM->any.time); \
88 extl_table_sets_i(TAB, "device", PRM->any.device)
90 static bool mrsh_group_extl(ExtlFn fn, WGroupParams *param)
92 ExtlTab t=extl_create_table();
94 MRSH_ANY(param,t);
95 if(param->group!=-1)
96 extl_table_sets_i(t, "group", param->group + 1);
97 if(param->base_group!=-1)
98 extl_table_sets_i(t, "base", param->base_group + 1);
99 if(param->latched_group!=-1)
100 extl_table_sets_i(t, "latched", param->latched_group + 1);
101 if(param->locked_group!=-1)
102 extl_table_sets_i(t, "locked", param->locked_group + 1);
104 return docall(fn, t);
107 static bool mrsh_bell_extl(ExtlFn fn, WBellParams *param)
109 ExtlTab t=extl_create_table();
111 MRSH_ANY(param,t);
112 extl_table_sets_i(t, "percent", param->percent);
113 extl_table_sets_i(t, "pitch", param->pitch);
114 extl_table_sets_i(t, "duration", param->duration);
116 extl_table_sets_i(t, "bell_class", param->bell_class);
117 extl_table_sets_i(t, "bell_id", param->bell_id);
119 if(param->name){
120 extl_table_sets_s(t, "name", param->name);
121 free(param->name);
124 if(param->window)
125 extl_table_sets_o(t, "window", (Obj*)param->window);
127 extl_table_sets_b(t, "event_only", param->event_only);
129 return docall(fn, t);
132 #define PARAM_ANY(PRM,NM) \
133 PRM.any.send_event=kev->NM.send_event; \
134 PRM.any.time=kev->NM.time; \
135 PRM.any.device=kev->NM.device
137 #define CHANGED(NM,FLD) (kev->state.changed&XkbGroup##NM##Mask)?kev->state.FLD:-1
139 bool handle_xkb_event(XEvent *ev)
141 void *p=NULL;
142 WHook *hook=NULL;
143 XkbEvent *kev=NULL;
144 WHookMarshallExtl *fn=NULL;
146 if(ev->type!=xkb_event_code)
147 return FALSE;
149 kev=(XkbEvent*)ev;
151 switch(kev->any.xkb_type){
152 case XkbStateNotify:
154 WGroupParams p2;
155 p=&p2;
157 hook=xkb_group_event;
158 fn=(WHookMarshallExtl*)mrsh_group_extl;
160 PARAM_ANY(p2,state);
162 p2.group=CHANGED(State,group);
163 p2.base_group=CHANGED(Base,base_group);
164 p2.latched_group=CHANGED(Latch,latched_group);
165 p2.locked_group=CHANGED(Lock,locked_group);
167 break;
168 case XkbBellNotify:
170 WBellParams p2;
171 p=&p2;
173 hook=xkb_bell_event;
174 fn=(WHookMarshallExtl*)mrsh_bell_extl;
176 PARAM_ANY(p2,bell);
178 p2.percent=kev->bell.percent;
179 p2.pitch=kev->bell.pitch;
180 p2.duration=kev->bell.duration;
182 p2.bell_class=kev->bell.bell_class;
183 p2.bell_id=kev->bell.bell_id;
185 p2.name=NULL;
186 if(kev->bell.name!=None)
187 p2.name=get_atom_name(kev->bell.name);
189 p2.window=NULL;
190 if(kev->bell.window!=None)
191 p2.window=XWINDOW_REGION_OF_T(kev->bell.window, WClientWin);
193 p2.event_only=kev->bell.event_only;
195 break;
198 if(hook && p && fn)
199 hook_call_p(hook, p, fn);
201 return FALSE;
204 #undef CHANGED
206 #undef PARAM_ANY
209 /*EXTL_DOC
210 * Set the current XKB group. See \code{XkbLockGroup}(3) manual page
211 * for details. See xkbion.lua for example use.
213 EXTL_EXPORT
214 int mod_xkbevents_lock_group(int state)
216 return XkbLockGroup(ioncore_g.dpy, XkbUseCoreKbd, state);
219 /*EXTL_DOC
220 * Latch modifiers. See \code{XkbLatchModifiers}(3) manual page
221 * for details.
223 EXTL_EXPORT
224 int mod_xkbevents_lock_modifiers(int affect, int values)
226 return XkbLockModifiers(ioncore_g.dpy, XkbUseCoreKbd, affect, values);
229 /*{{{ Init & deinit */
231 /* ion never does this though it looks to me like that leaks (though I suppose
232 * that doesn't matter if modules can't ever be unloaded at runtime.
233 void deinit_hooks()
238 #define INIT_HOOK_(NM) \
239 NM=mainloop_register_hook(#NM, create_hook()); \
240 if(NM==NULL) return FALSE;
242 static bool init_hooks()
244 INIT_HOOK_(xkb_group_event);
245 INIT_HOOK_(xkb_bell_event);
247 return TRUE;
250 #undef INIT_HOOK_
252 void mod_xkbevents_deinit()
254 mod_xkbevents_unregister_exports();
257 bool mod_xkbevents_init()
259 int opcode;
260 int major=XkbMajorVersion;
261 int minor=XkbMinorVersion;
263 if(!XkbLibraryVersion(&major,&minor)){
264 warn(TR("X library built with XKB version %d.%02d but mod_xkbevents was built with XKB version %d.%02d. Going to try to work anyway."), major, minor, XkbMajorVersion, XkbMinorVersion);
267 if(!XkbQueryExtension(ioncore_g.dpy,&opcode,&xkb_event_code,&xkb_error_code,&major,&minor)>0){
268 if ((major!=0)||(minor!=0))
269 warn(TR("Server supports incompatible XKB version %d.%02d. Going to try to work anyway."), major, minor);
270 else
271 warn(TR("XkbQueryExtension failed. Going to try to work anyway."));
274 if(!init_hooks())
275 return FALSE;
277 if(!mod_xkbevents_register_exports())
278 return FALSE;
280 if(!hook_add(ioncore_handle_event_alt, (void (*)())handle_xkb_event))
281 return FALSE;
283 /* Select for the specific XkbState events we care about. */
284 XkbSelectEventDetails(ioncore_g.dpy, XkbUseCoreKbd, XkbStateNotify,
285 XkbGroupStateMask|XkbGroupBaseMask|XkbGroupLockMask,
286 XkbGroupStateMask|XkbGroupBaseMask|XkbGroupLockMask);
288 /* Select for all XkbBell events (we can't select for less). */
289 XkbSelectEvents(ioncore_g.dpy, XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask);
291 return TRUE;
294 /*}}} Init & deinit */