4 * Copyright (c) Sergey Redin 2006.
5 * Copyright (c) Etan Reisner 2011.
7 * Released under the MIT License.
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"
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
){
39 INTRSTRUCT(WBellParams
);
40 DECLSTRUCT(WBellParams
){
46 unsigned int bell_class
;
53 /*{{{ Module information */
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
);
72 static bool docall(ExtlFn fn
, ExtlTab t
)
77 ret
=extl_call(fn
, "t", NULL
, t
);
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();
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();
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
);
120 extl_table_sets_s(t
, "name", param
->name
);
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
)
144 WHookMarshallExtl
*fn
=NULL
;
146 if(ev
->type
!=xkb_event_code
)
151 switch(kev
->any
.xkb_type
){
157 hook
=xkb_group_event
;
158 fn
=(WHookMarshallExtl
*)mrsh_group_extl
;
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
);
174 fn
=(WHookMarshallExtl
*)mrsh_bell_extl
;
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
;
186 if(kev
->bell
.name
!=None
)
187 p2
.name
=get_atom_name(kev
->bell
.name
);
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
;
199 hook_call_p(hook
, p
, fn
);
210 * Set the current XKB group. See \code{XkbLockGroup}(3) manual page
211 * for details. See xkbion.lua for example use.
214 int mod_xkbevents_lock_group(int state
)
216 return XkbLockGroup(ioncore_g
.dpy
, XkbUseCoreKbd
, state
);
220 * Latch modifiers. See \code{XkbLatchModifiers}(3) manual page
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.
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
);
252 void mod_xkbevents_deinit()
254 mod_xkbevents_unregister_exports();
257 bool mod_xkbevents_init()
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
);
271 warn(TR("XkbQueryExtension failed. Going to try to work anyway."));
277 if(!mod_xkbevents_register_exports())
280 if(!hook_add(ioncore_handle_event_alt
, (void (*)())handle_xkb_event
))
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
);
294 /*}}} Init & deinit */