1 module xkeybind;
2 
3 import x11.X;
4 import x11.keysym;
5 import x11.Xlib;
6 import std..string : toStringz;
7 import std.typecons : NotImplementedError;
8 
9 ///
10 alias XKeybindHandler = void delegate(ModifierKey mods, int key);
11 
12 private struct KeyHandlerEntry
13 {
14 	ModifierKey mods;
15 	int key;
16 	XKeybindHandler handler;
17 }
18 
19 /// Use Modifier for binding keys
20 enum ModifierKey : uint
21 {
22 	///
23 	Shift = ShiftMask,
24 	///
25 	Control = ControlMask,
26 	///
27 	Mod1 = Mod1Mask,
28 	///
29 	Mod2 = Mod2Mask,
30 	///
31 	Mod3 = Mod3Mask,
32 	///
33 	Mod4 = Mod4Mask,
34 	///
35 	Mod5 = Mod5Mask,
36 	///
37 	Any = AnyModifier
38 }
39 
40 /// Modifier keys in this system (Get assigned after calling XKeyBind.load)
41 final struct Modifier
42 {
43 	///
44 	static ModifierKey Shift = ModifierKey.Shift;
45 	///
46 	static ModifierKey Control = ModifierKey.Control;
47 	///
48 	static ModifierKey Alt;
49 	///
50 	static ModifierKey AltR;
51 	///
52 	static ModifierKey SuperR;
53 	///
54 	static ModifierKey SuperL;
55 	///
56 	static ModifierKey HyperR;
57 	///
58 	static ModifierKey HyperL;
59 	///
60 	static ModifierKey MetaR;
61 	///
62 	static ModifierKey MetaL;
63 	///
64 	static ModifierKey Any = ModifierKey.Any;
65 }
66 
67 ///
68 bool parseKey(Display* display, string key, out ModifierKey mods, out int keycode)
69 {
70 	import std..string : strip, split, toLower;
71 
72 	//dfmt off
73 	ModifierKey[string] modName = [
74 		"shift" : Modifier.Shift,
75 		"control" : Modifier.Control,
76 		"ctrl" : Modifier.Control,
77 		"alt" : Modifier.Alt,
78 		"altl" : Modifier.Alt,
79 		"altgr" : Modifier.AltR,
80 		"altr" : Modifier.AltR,
81 		"super" : Modifier.SuperL,
82 		"superl" : Modifier.SuperL,
83 		"superr" : Modifier.SuperR,
84 		"hyper" : Modifier.HyperL,
85 		"hyperl" : Modifier.HyperL,
86 		"hyperr" : Modifier.HyperR,
87 		"meta" : Modifier.MetaL,
88 		"metal" : Modifier.MetaL,
89 		"metar" : Modifier.MetaR,
90 	];
91 	//dfmt on
92 	string[] parts = key.split('-');
93 	ModifierKey _mods;
94 	if (parts.length > 1)
95 	{
96 		foreach (part; parts[0 .. $ - 1])
97 		{
98 			if (part.strip.length == 0)
99 				return false; // A--B
100 			string mod = part.strip.toLower;
101 			auto ptr = mod in modName;
102 			if (!ptr)
103 				return false;
104 			_mods |= *ptr;
105 		}
106 	}
107 	auto keysym = XStringToKeysym(cast(char*)(parts[$ - 1] ~ '\0').ptr);
108 	if (keysym == NoSymbol)
109 		return false;
110 	keycode = XKeysymToKeycode(display, keysym);
111 	mods = _mods;
112 	return true;
113 }
114 
115 ///
116 final class XKeyBind
117 {
118 public:
119 	/// Creates a display and loads XKeyBind
120 	static void load(string port = ":0")
121 	{
122 		load(XOpenDisplay(cast(char*) port.toStringz));
123 	}
124 
125 	/// Loads XKeyBind from an existing display
126 	static void load(Display* displ)
127 	{
128 		display = displ;
129 		root = DefaultRootWindow(displ);
130 
131 		XModifierKeymap* modmap = XGetModifierMapping(displ);
132 		auto key_numlock = XKeysymToKeycode(displ, XK_Num_Lock);
133 		auto key_alt = XKeysymToKeycode(displ, XK_Alt_L);
134 		auto key_altr = XKeysymToKeycode(displ, XK_Alt_R);
135 		auto key_superr = XKeysymToKeycode(displ, XK_Super_R);
136 		auto key_superl = XKeysymToKeycode(displ, XK_Super_L);
137 		auto key_hyperl = XKeysymToKeycode(displ, XK_Hyper_L);
138 		auto key_hyperr = XKeysymToKeycode(displ, XK_Hyper_R);
139 		auto key_metal = XKeysymToKeycode(displ, XK_Meta_L);
140 		auto key_metar = XKeysymToKeycode(displ, XK_Meta_R);
141 
142 		for (int i = 3; i < 8; i++)
143 		{
144 			for (int j = 0; j < modmap.max_keypermod; j++)
145 			{
146 				auto ckey = modmap.modifiermap[i * modmap.max_keypermod + j];
147 				if (key_numlock && ckey == key_numlock)
148 					Numlock = cast(ModifierKey) 1 << i;
149 				if (key_alt && ckey == key_alt)
150 					Modifier.Alt = cast(ModifierKey) 1 << i;
151 				if (key_altr && ckey == key_altr)
152 					Modifier.AltR = cast(ModifierKey) 1 << i;
153 				if (key_superr && ckey == key_superr)
154 					Modifier.SuperR = cast(ModifierKey) 1 << i;
155 				if (key_superl && ckey == key_superl)
156 					Modifier.SuperL = cast(ModifierKey) 1 << i;
157 				if (key_hyperl && ckey == key_hyperl)
158 					Modifier.HyperL = cast(ModifierKey) 1 << i;
159 				if (key_hyperr && ckey == key_hyperr)
160 					Modifier.HyperR = cast(ModifierKey) 1 << i;
161 				if (key_metal && ckey == key_metal)
162 					Modifier.MetaL = cast(ModifierKey) 1 << i;
163 				if (key_metar && ckey == key_metar)
164 					Modifier.MetaR = cast(ModifierKey) 1 << i;
165 			}
166 		}
167 
168 		ignoreMods = [Lock];
169 		ignoreMask = Lock;
170 		if (Numlock)
171 		{
172 			ignoreMods = [Numlock, Lock, Numlock | Lock];
173 			ignoreMask = Numlock | Lock;
174 		}
175 		ignoreMask = ~ignoreMask;
176 	}
177 
178 	/// Checks for key presses and calls handlers
179 	static void update()
180 	{
181 		while (XCheckWindowEvent(display, root, KeyPressMask, &event))
182 		{
183 			if (event.type == KeyPress)
184 			{
185 				int key = event.xkey.keycode;
186 				ModifierKey mods = cast(ModifierKey)(event.xkey.state & ignoreMask);
187 				foreach (KeyHandlerEntry bind; binds)
188 				{
189 					if (bind.key == key && bind.mods == mods)
190 						bind.handler(mods, key);
191 				}
192 			}
193 		}
194 	}
195 
196 	/// Binds the key without binding keys when numlock or lock are active
197 	static void bindExact(ModifierKey mods, int keycode, XKeybindHandler handler)
198 	{
199 		XGrabKey(display, keycode, cast(uint) mods, root, 0, GrabModeAsync, GrabModeAsync);
200 		binds ~= KeyHandlerEntry(mods, keycode, handler);
201 	}
202 
203 	///
204 	static void bind(ModifierKey mods, int keycode, XKeybindHandler handler)
205 	{
206 		bindExact(mods, keycode, handler);
207 		foreach (mod; ignoreMods)
208 			bindExact(mod | mods, keycode, handler);
209 	}
210 
211 	///
212 	static void bind(string key, XKeybindHandler handler)
213 	{
214 		ModifierKey mods;
215 		int code;
216 		parseKey(display, key, mods, code);
217 		bind(mods, code, handler);
218 	}
219 
220 	///
221 	static void unbindExact(ModifierKey mods, int keycode)
222 	{
223 		XUngrabKey(display, keycode, cast(uint) mods, root);
224 	}
225 
226 	///
227 	static void unbind(ModifierKey mods, int keycode)
228 	{
229 		unbindExact(mods, keycode);
230 		foreach (mod; ignoreMods)
231 			unbindExact(mod | mods, keycode);
232 	}
233 
234 	///
235 	static void unbind(string key)
236 	{
237 		ModifierKey mods;
238 		int code;
239 		parseKey(display, key, mods, code);
240 		unbind(mods, code);
241 	}
242 
243 private:
244 	static ModifierKey Numlock;
245 	static ModifierKey Lock = cast(ModifierKey) LockMask;
246 	static ModifierKey[] ignoreMods;
247 	static ModifierKey ignoreMask;
248 	static KeyHandlerEntry[] binds;
249 	static Display* display;
250 	static Window root;
251 	static XEvent event;
252 }