1 module iota.controls.system;
2 
3 version (Windows) {
4 	import core.sys.windows.windows;
5 	
6 	import core.sys.windows.wtypes;
7 	import iota.controls.keyboard;
8 	import iota.controls.mouse;
9 	import std.utf;
10 }
11 import iota.controls.types;
12 import iota.controls.keybscancodes : translateSC;
13 import iota.etc.charcode;
14 import iota.etc.window;
15 
16 /** 
17  * Implements a class for system-wide events.
18  *
19  * Under Windows, this class also handles keyboard and mouse inputs, because of Microsoft of course (Why? You did great
20  * with WASAPI and relatively good with MIDI!)
21  *
22  * Note: Polling might suck up some more advanced windowing-related events. If they really needed, then just add 
23  * handling of them to it with OOP magic!
24  */
25 public class System : InputDevice {
26 	version (Windows) {
27 		package Keyboard		keyb;		///Pointer to the default, non-virtual keyboard.
28 		version (iota_use_utf8) {
29 			///Character input converted to UTF-8
30 			char[8]				lastChar;
31 		} else {
32 			///Character input converted to UTF-32
33 			dchar				lastChar;
34 		}
35 		package Mouse			mouse;		///Pointer to the default, non-virtual mouse.
36 		protected int[2]		lastMousePos;///Last position of the mouse cursor.
37 		protected size_t		winCount;	///Window counter.
38 	}
39 	package this() nothrow {
40 		version (Windows) {
41 			keyb = new Keyboard();
42 			mouse = new Mouse();
43 		}
44 	}
45 	public override int poll(ref InputEvent output) @nogc nothrow {
46 		version (Windows) {
47 			int GET_X_LPARAM(LPARAM lParam) @nogc nothrow pure {
48 				return cast(int)(cast(short) LOWORD(lParam));
49         	}
50 
51 			int GET_Y_LPARAM(LPARAM lParam) @nogc nothrow pure {
52 				return cast(int)(cast(short) HIWORD(lParam));
53 			}
54 			tryAgain:
55 			MSG msg;
56 			BOOL bret = PeekMessage(&msg, allAppWindows[winCount], 0, 0, PM_REMOVE);
57 			if (bret) {
58 				if (keyb.isTextInputEn()) {
59 					TranslateMessage(&msg);
60 				}
61 				version (iota_hi_prec_timestamp) {
62 					output.timestamp = MonoTime.currTime();
63 				} else {
64 					output.timestamp = msg.time;
65 				}
66 				output.handle = allAppWindows[winCount];
67 				switch (msg.message & 0xFF_FF) {
68 					case WM_CHAR, WM_SYSCHAR:
69 						output.type = InputEventType.TextInput;
70 						output.source = keyb;
71 						version (iota_use_utf8) {
72 							lastChar[0] = cast(char)(msg.wParam);
73 							output.textIn.text = lastChar[0..1];
74 						} else {
75 							lastChar = cast(dchar)(msg.wParam);
76 							output.textIn.text = (&lastChar)[0..1];
77 						}
78 						output.textIn.isClipboard = false;
79 						break;
80 					case WM_UNICHAR, WM_DEADCHAR, WM_SYSDEADCHAR:
81 						output.type = InputEventType.TextInput;
82 						output.source = keyb;
83 						version (iota_use_utf8) {
84 							
85 						} else {
86 							lastChar = cast(dchar)(msg.wParam);
87 							output.textIn.text = (&lastChar)[0..1];
88 						}
89 						output.textIn.isClipboard = false;
90 						break;
91 					case WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP:
92 						output.type = InputEventType.Keyboard;
93 						output.source = keyb;
94 						if ((msg.message & 0xFF_FF) == WM_KEYDOWN || (msg.message & 0xFF_FF) == WM_SYSKEYDOWN)
95 							output.button.dir = 1;
96 						else
97 							output.button.dir = 0;
98 						output.button.id = translateSC(cast(uint)msg.wParam);
99 						output.button.repeat = (msg.lParam & 0xFF_FF) < 255 ? cast(ubyte)(msg.lParam & 0xFF) : 0xFF;
100 						output.button.aux = keyb.getModifiers();
101 						break;
102 					case 0x020E , WM_MOUSEWHEEL:
103 						output.type = InputEventType.MouseScroll;
104 						output.source = mouse;
105 						if ((msg.message & 0xFF_FF) == 0x020E)
106 							output.mouseSE.xS = GET_WHEEL_DELTA_WPARAM(msg.wParam);
107 						else
108 							output.mouseSE.yS = GET_WHEEL_DELTA_WPARAM(msg.wParam);
109 						output.mouseSE.x = GET_X_LPARAM(msg.lParam);
110 						output.mouseSE.y = GET_Y_LPARAM(msg.lParam);
111 						lastMousePos[0] = output.mouseSE.x;
112 						lastMousePos[1] = output.mouseSE.y;
113 						break;
114 					case WM_MOUSEMOVE:
115 						output.type = InputEventType.MouseMove;
116 						output.source = mouse;
117 						if (msg.wParam & MK_LBUTTON)
118 							output.mouseME.buttons |= MouseButtonFlags.Left;
119 						if (msg.wParam & MK_RBUTTON)
120 							output.mouseME.buttons |= MouseButtonFlags.Right;
121 						if (msg.wParam & MK_MBUTTON)
122 							output.mouseME.buttons |= MouseButtonFlags.Middle;
123 						if (msg.wParam & MK_XBUTTON1)
124 							output.mouseME.buttons |= MouseButtonFlags.Prev;
125 						if (msg.wParam & MK_XBUTTON2)
126 							output.mouseME.buttons |= MouseButtonFlags.Next;
127 						output.mouseME.x = GET_X_LPARAM(msg.lParam);
128 						output.mouseME.y = GET_Y_LPARAM(msg.lParam);
129 						output.mouseME.xD = output.mouseME.x - lastMousePos[0];
130 						output.mouseME.yD = output.mouseME.y - lastMousePos[1];
131 						lastMousePos[0] = output.mouseME.x;
132 						lastMousePos[1] = output.mouseME.y;
133 						break;
134 					case WM_LBUTTONUP, WM_LBUTTONDOWN, WM_LBUTTONDBLCLK, WM_MBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONDBLCLK,
135 					WM_RBUTTONUP, WM_RBUTTONDOWN, WM_RBUTTONDBLCLK, WM_XBUTTONUP, WM_XBUTTONDOWN, WM_XBUTTONDBLCLK:
136 						output.type = InputEventType.MouseMove;
137 						output.source = mouse;
138 						output.mouseCE.x = GET_X_LPARAM(msg.lParam);
139 						output.mouseCE.y = GET_Y_LPARAM(msg.lParam);
140 						output.mouseCE.repeat = 0;
141 						switch (msg.message & 0xFF_FF) {
142 							case WM_LBUTTONUP:
143 								output.mouseCE.dir = 0;
144 								output.mouseCE.button = MouseButtons.Left;
145 								break;
146 							case WM_LBUTTONDOWN:
147 								output.mouseCE.dir = 1;
148 								output.mouseCE.button = MouseButtons.Left;
149 								break;
150 							case WM_LBUTTONDBLCLK:
151 								output.mouseCE.repeat = 1;
152 								goto case WM_LBUTTONDOWN;
153 							case WM_RBUTTONUP:
154 								output.mouseCE.dir = 0;
155 								output.mouseCE.button = MouseButtons.Right;
156 								break;
157 							case WM_RBUTTONDOWN:
158 								output.mouseCE.dir = 1;
159 								output.mouseCE.button = MouseButtons.Right;
160 								break;
161 							case WM_RBUTTONDBLCLK:
162 								output.mouseCE.repeat = 1;
163 								goto case WM_RBUTTONDOWN;
164 							case WM_MBUTTONUP:
165 								output.mouseCE.dir = 0;
166 								output.mouseCE.button = MouseButtons.Middle;
167 								break;
168 							case WM_MBUTTONDOWN:
169 								output.mouseCE.dir = 1;
170 								output.mouseCE.button = MouseButtons.Middle;
171 								break;
172 							case WM_MBUTTONDBLCLK:
173 								output.mouseCE.repeat = 1;
174 								goto case WM_MBUTTONDOWN;
175 							case WM_XBUTTONUP:
176 								output.mouseCE.dir = 0;
177 								output.mouseCE.button = HIWORD(msg.wParam) == 1 ? MouseButtons.Next : MouseButtons.Prev;
178 								break;
179 							case WM_XBUTTONDOWN:
180 								output.mouseCE.dir = 1;
181 								output.mouseCE.button = HIWORD(msg.wParam) == 1 ? MouseButtons.Next : MouseButtons.Prev;
182 								break;
183 							case WM_XBUTTONDBLCLK:
184 								output.mouseCE.repeat = 1;
185 								goto case WM_XBUTTONDOWN;
186 							default:
187 
188 								break;
189 						}
190 						break;
191 					case WM_QUIT:
192 						output.type = InputEventType.ApplExit;
193 						output.source = this;
194 						break;
195 					case WM_SIZE:
196 						output.type = InputEventType.WindowResize;
197 						output.source = this;
198 						break;
199 					default:
200 						goto tryAgain;
201 				}
202 			} else {
203 				winCount++;
204 				if (winCount < allAppWindows.length)
205 					goto tryAgain;
206 				else
207 					winCount = 0;
208 				return 0;
209 			}
210 
211 			return 1; // TODO: implement
212 		}
213 	}
214 }