1 /**
2  * keybinder handles binding keys globally on X11 + GTK.
3  */
4 module keybinder;
5 
6 import keybinder_c;
7 
8 /**
9  * Bind the key binding identified by $(D keys) to the given delegate.
10  *
11  * Only one callback per key sequence is supported.
12  *
13  * The callback will be called with a frequency according to the keyboard repeat settings. If a user
14  * holds down the relevant key sequence, the callback will be called once, then there will be a
15  * delay, then the callback will be called many times.
16  */
17 void bindGlobal(string keys, void delegate() dg)
18 in
19 {
20     assert(keys !in dgs);
21 }
22 body
23 {
24     import std.string : toStringz;
25     dgs[keys] = dg;
26     keybinder_bind_full(keys.toStringz, &handleKeys, null, null);
27 }
28 
29 /**
30  * Unbind everything on the given key combination.
31  */
32 void unbindGlobal(string keys)
33 {
34     import std.string : toStringz;
35     keybinder_unbind_all(keys.toStringz);
36     dgs.remove(keys);
37 }
38 
39 /**
40  * Initialize global keybinding support.
41  *
42  * libkeybinder requires you to be running with GTK+ and X11. This init function requires you to
43  * have initialized GTK+ yourself; you are responsible for maintaining a GTK+ main loop if you want
44  * to receive key events.
45  */
46 void init()
47 {
48     keybinder_init();
49 }
50 
51 /**
52  * Initialize global keybinding support.
53  *
54  * libkeybinder requires you to be running with GTK+ and X11. This init function handles GTK+
55  * initialization for you. In your program's main loop (or a frequent periodic callback), you must
56  * call the poll() function to check whether the key has been pressed.
57  */
58 void initNoGTK()
59 {
60     import core.sys.posix.dlfcn;
61     auto d = dlopen("libgtk-3.so".ptr, RTLD_LAZY);
62     auto gtk_init = cast(void function())dlsym(d, "gtk_init".ptr);
63     gtk_init();
64     keybinder_init();
65     gtkMainIterationDo = cast(typeof(gtkMainIterationDo))dlsym(d, "gtk_main_iteration_do".ptr);
66 }
67 
68 /**
69  * Check for new keypresses.
70  *
71  * If you already have a GTK+ main loop, you should not call this.
72  */
73 void poll()
74 {
75     if (!gtkMainIterationDo)
76     {
77         throw new Exception(
78                 "You must call initNoGTK() before calling poll().\n" ~
79                 "Note that if you have a GTK+ main loop, you don't need to call " ~
80                 "initNoGTK() or poll(); the GTK+ loop already handles this.");
81     }
82     gtkMainIterationDo(false);
83 }
84 
85 private:
86 
87 extern (C) bool function(bool) gtkMainIterationDo;
88 
89 void delegate()[string] dgs;
90 
91 private extern(C) void handleKeys(const char* keystring, void* data)
92 {
93     import std.string : fromStringz;
94     auto k = keystring.fromStringz;
95     auto v = k in dgs;
96     if (v is null)
97     {
98         // This shouldn't happen...
99         return;
100     }
101     (*v)();
102 }