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 }