1 module ws.wm.win32.wm; 2 3 version(Windows): 4 5 import 6 std.utf, 7 std.string, 8 std.conv, 9 10 ws.list, 11 ws.log, 12 ws.gui.input, 13 ws.gui.point, 14 ws.wm.win32.api, 15 ws.wm.win32.window, 16 ws.wm.baseWindowManager, 17 ws.wm; 18 19 __gshared: 20 21 22 class Win32WindowManager: BaseWindowManager { 23 24 T_wglChoosePixelFormatARB wglChoosePixelFormatARB; 25 T_wglCreateContextAttribsARB wglCreateContextAttribsARB; 26 27 HINSTANCE appInstance; 28 WNDCLASSW windowClass = {0}; 29 30 void load(string s)(){ 31 auto ptr = core.sys.windows.wingdi.wglGetProcAddress(s); 32 if(!ptr) 33 throw new Exception("failed to get function \"" ~ s ~ "\""); 34 mixin(s ~ " = cast(typeof(" ~ s ~ "))ptr;"); 35 } 36 37 this(){ 38 super(); 39 DerelictGL3.load(); 40 appInstance = GetModuleHandleW(null); 41 42 windowClass.lpfnWndProc = cast(WNDPROC)&internalEvents; 43 windowClass.hInstance = appInstance; 44 windowClass.hIcon = LoadIconW(null, IDI_APPLICATION); 45 windowClass.hCursor = LoadCursorW(null, IDC_ARROW); 46 windowClass.hbrBackground = cast(HBRUSH)GetStockObject(BLACK_BRUSH); 47 windowClass.lpszClassName = "wm::windowClass".toUTF16z(); 48 windowClass.style = CS_OWNDC; 49 RegisterClassW(&windowClass); 50 // the following is solely to retrieve wglChoosePixelFormat && wglCreateContext 51 HWND dummyWindow = CreateWindowExA( 52 0, "STATIC", "", WS_POPUP | WS_DISABLED, 53 0, 0, 1, 1, null, null, appInstance, null 54 ); 55 PIXELFORMATDESCRIPTOR dummyFormatDescriptor = { 56 (PIXELFORMATDESCRIPTOR).sizeof, 1, 4 | 32 | 1, 0, 8, 0, 57 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0 58 }; 59 HDC dummyDeviceContext = GetDC(dummyWindow); 60 int pixelFormat = ChoosePixelFormat(dummyDeviceContext, &dummyFormatDescriptor); 61 SetPixelFormat(dummyDeviceContext, pixelFormat, &dummyFormatDescriptor); 62 HGLRC dummyContext = wglCreateContext(dummyDeviceContext); 63 wglMakeCurrent(dummyDeviceContext, dummyContext); 64 try { 65 load!"wglChoosePixelFormatARB"(); 66 load!"wglCreateContextAttribsARB"(); 67 } catch (Exception e) 68 throw new Exception("OpenGL 3.3 not supported"); 69 wglMakeCurrent(null, null); 70 wglDeleteContext(dummyContext); 71 ReleaseDC(dummyWindow, dummyDeviceContext); 72 DestroyWindow(dummyWindow); 73 } 74 75 HINSTANCE getInstance(){ 76 return appInstance; 77 } 78 79 Window[] systemWindows(){ 80 Window[] list; 81 HWND h = GetTopWindow(null); 82 while(h){ 83 list ~= new Win32Window(h); 84 h = GetWindow(h, 2); 85 } 86 return list; 87 } 88 89 void processEvents(){ 90 MSG msg; 91 while(PeekMessageA(&msg, null, 0, 0, PM_REMOVE)){ 92 TranslateMessage(&msg); 93 DispatchMessageA(&msg); 94 } 95 foreach(e; eventQueue){ 96 e(); 97 } 98 eventQueue = []; 99 } 100 101 long[2] getCursorPos(){ 102 POINT point; 103 GetCursorPos(&point); 104 return [point.x, point.y]; 105 } 106 107 Win32Window findWindow(string title){ 108 HWND window = FindWindowW(null, title.toUTF16z()); 109 if(!window) 110 throw new WindowNotFound("Could not find window \"" ~ title ~ "\""); 111 return new Win32Window(window); 112 } 113 114 bool isKeyDown(Keyboard.key key){ 115 return GetKeyState(cast(int)key) < 0; 116 } 117 118 } 119 120 121 protected: 122 123 static HCURSOR getCursor(int i){ 124 return cast(LPWSTR)(cast(DWORD)(cast(WORD)i)); 125 } 126 127 HCURSOR[] MOUSE_CURSOR_TO_HCUR = [ 128 getCursor(32512), // IDC_ARROW 129 getCursor(32516), // IDC_UPARROW 130 131 getCursor(32513), // IDC_BEAM 132 133 getCursor(32646), // IDC_SIZEALL 134 getCursor(32645), // IDC_SIZENS 135 getCursor(32644), // IDC_SIZEWE 136 getCursor(32642), // IDC_SIZENWSE 137 getCursor(32643), // IDC_SIZENESW 138 getCursor(32643), // IDC_SIZENESW 139 getCursor(32642), // IDC_SIZENWSE 140 141 getCursor(32649), // IDC_HAND 142 143 getCursor(32512), // IDC_ARROW 144 null, 145 ]; 146 147 void delegate()[] eventQueue; 148 149 void delegate() translateEvent(Win32Window window, UINT msg, WPARAM wpar, LPARAM lpar){ 150 switch(msg){ 151 case WM_INPUT: 152 import ws.log; 153 byte[] bytes; 154 UINT bufferSize = RAWINPUT.sizeof; 155 156 GetRawInputData(cast(HRAWINPUT)lpar, RID_INPUT, NULL, &bufferSize, RAWINPUTHEADER.sizeof); 157 bytes.length = bufferSize; 158 159 if(GetRawInputData(cast(HRAWINPUT)lpar, RID_INPUT, bytes.ptr, &bufferSize, RAWINPUTHEADER.sizeof) == cast(UINT)-1) 160 return {}; 161 auto input = (cast(RAWINPUT[])bytes)[0]; 162 //import ws.io; writeln(cast(byte[RAWINPUT.sizeof])input); 163 if(input.header.dwType == RIM_TYPEMOUSE){ 164 if(input.mouse.usFlags & MOUSE_MOVE_ABSOLUTE){ 165 Log.info("absolute"); 166 }else{ 167 if(input.mouse.lLastX || input.mouse.lLastY) 168 return { window.onRawMouse(input.mouse.lLastX, input.mouse.lLastY); }; 169 } 170 } 171 break; 172 //case WM_PAINT: 173 case WM_SHOWWINDOW: 174 return { window.onShow; }; 175 case WM_CLOSE: 176 return { window.onDestroy; }; 177 case WM_SIZE: 178 return { 179 window.resized([LOWORD(lpar),HIWORD(lpar)]); 180 }; 181 case WM_KEYDOWN: 182 return { 183 Keyboard.key c = cast(Keyboard.key)toLower(cast(char)wpar); 184 Keyboard.set(c, true); 185 window.onKeyboard(c, true); 186 }; 187 case WM_KEYUP: 188 return { 189 auto c = cast(Keyboard.key)toLower(cast(char)wpar); 190 Keyboard.set(c, false); 191 window.onKeyboard(c, false); 192 }; 193 case WM_CHAR: 194 return { 195 window.onKeyboard(cast(dchar)wpar); 196 }; 197 case WM_ACTIVATE: 198 return { 199 window.onKeyboardFocus(LOWORD(wpar) > 0 ? true : false); 200 }; 201 case WM_SETCURSOR: 202 return { 203 SetCursor(MOUSE_CURSOR_TO_HCUR[cast(int)window.cursor]); 204 }; 205 case WM_MOUSEMOVE: 206 int x = GET_X_LPARAM(lpar); 207 int y = GET_Y_LPARAM(lpar); 208 if(!window.hasMouse){ 209 TRACKMOUSEEVENT tme = { 210 TRACKMOUSEEVENT.sizeof, 2, window.windowHandle, 0xFFFFFFFF 211 }; 212 TrackMouseEvent(&tme); 213 window.onMouseFocus(true); 214 window.hasMouse = true; 215 } 216 return { 217 window.onMouseMove(x, window.size.y-y); 218 }; 219 case WM_MOUSELEAVE: 220 return { 221 window.hasMouse = false; 222 window.onMouseFocus(false); 223 }; 224 case WM_LBUTTONDOWN: 225 return { 226 window.onMouseButton(Mouse.buttonLeft, true, LOWORD(lpar), window.size.y-HIWORD(lpar)); 227 }; 228 case WM_LBUTTONUP: 229 return { 230 window.onMouseButton(Mouse.buttonLeft, false, LOWORD(lpar), window.size.y-HIWORD(lpar)); 231 }; 232 case WM_MBUTTONDOWN: 233 return { 234 window.onMouseButton(Mouse.buttonMiddle, true, LOWORD(lpar), window.size.y-HIWORD(lpar)); 235 }; 236 case WM_MBUTTONUP: 237 return { 238 window.onMouseButton(Mouse.buttonMiddle, false, LOWORD(lpar), window.size.y-HIWORD(lpar)); 239 }; 240 case WM_RBUTTONDOWN: 241 return { 242 window.onMouseButton(Mouse.buttonRight, true, LOWORD(lpar), window.size.y-HIWORD(lpar)); 243 }; 244 case WM_RBUTTONUP: 245 return { 246 window.onMouseButton(Mouse.buttonRight, false, LOWORD(lpar), window.size.y-HIWORD(lpar)); 247 }; 248 case WM_MOUSEWHEEL: 249 return { 250 window.onMouseButton( 251 GET_WHEEL_DELTA_WPARAM(wpar) > 120 ? Mouse.wheelDown : Mouse.wheelUp, 252 true, LOWORD(lpar), window.size.y-HIWORD(lpar) 253 ); 254 }; 255 default:break; 256 } 257 return {}; 258 } 259 260 extern(Windows) 261 static LRESULT internalEvents(HWND window, UINT msg, WPARAM wpar, LPARAM lpar) nothrow { 262 try { 263 foreach(w; cast(List!Win32Window)wm.windows){ 264 if(w.handle == window){ 265 eventQueue ~= translateEvent(w, msg, wpar, lpar); 266 if(msg == WM_CLOSE) 267 return 0; 268 } 269 } 270 } catch(Throwable) 271 assert(0); 272 return DefWindowProcW(window, msg, wpar, lpar); 273 } 274