1 /* 2 Copyright Chris Jones 2020. 3 Distributed under the Boost Software License, Version 1.0. 4 See accompanying file Licence.txt or copy at... 5 https://www.boost.org/LICENSE_1_0.txt 6 */ 7 8 module win32; 9 10 version(Windows): 11 12 import window; 13 14 import std.stdio; 15 import core.sys.windows.windows; 16 import std..string; 17 import std.conv; 18 import dg2d; 19 20 static import gdi = core.sys.windows.wingdi; 21 22 pragma(lib, "gdi32"); 23 pragma(lib, "user32"); 24 25 void RegisterWindowClass() 26 { 27 Win32Window.hinstance = HINSTANCE(GetModuleHandleA(NULL)); 28 29 WNDCLASSEXA wcx; 30 wcx.cbSize = wcx.sizeof; 31 wcx.style = CS_DBLCLKS; 32 wcx.lpfnWndProc = &WindowProc; 33 wcx.cbClsExtra = 0; 34 wcx.cbWndExtra = (void*).sizeof; 35 wcx.hInstance = Win32Window.hinstance; 36 wcx.hIcon = NULL; 37 wcx.hCursor = LoadCursor(NULL, IDC_ARROW); 38 wcx.hbrBackground = NULL; 39 wcx.lpszMenuName = NULL; 40 wcx.lpszClassName = Win32Window.wndclass.ptr; 41 wcx.hIconSm = NULL; 42 43 RegisterClassExA(&wcx); 44 } 45 46 /* 47 Windows Message Loop 48 */ 49 50 int WindowsMessageLoop() 51 { 52 MSG msg; 53 while (GetMessageA(&msg, null, 0, 0)) 54 { 55 TranslateMessage(&msg); 56 DispatchMessageA(&msg); 57 } 58 return cast(int) msg.wParam; 59 } 60 61 /* 62 Window Proc, not sure what to do about catching Errors/Exceptions, seems to 63 just hang no matter what, tried assert(0), ExitProcess etc.. just doesnt close 64 */ 65 66 extern(Windows) 67 LRESULT WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) nothrow 68 { 69 try 70 { 71 auto window = cast(Win32Window) (cast(void*) GetWindowLongPtr(hwnd, GWLP_USERDATA)); 72 73 if (window is null) 74 return DefWindowProcA(hwnd, msg, wparam, lparam); 75 else 76 return window.windowProc(hwnd, msg, wparam, lparam); 77 } 78 catch (Exception e) 79 { 80 try { writeln(e.toString()); } 81 catch(Exception what) {} 82 PostQuitMessage(0); 83 return 0; 84 } 85 } 86 87 /* 88 Window class, bare bones WinAPI wrapper 89 */ 90 91 class Win32Window : IWindow 92 { 93 private: 94 95 HWND m_handle; 96 DWORD m_style; 97 DWORD m_exstyle; 98 string m_title; 99 int m_width; 100 int m_height; 101 Canvas m_canvas; 102 103 Widget m_client; 104 105 static immutable char[] wndclass = "GFXWindow"; 106 static HINSTANCE hinstance; 107 108 UINT_PTR m_timer; 109 110 public: 111 112 void create(int x, int y, int w, int h, string title) 113 { 114 if (m_handle) destroyWindow(); 115 116 m_width = w; 117 m_height = h; 118 119 m_style = WS_OVERLAPPEDWINDOW; 120 m_exstyle = WS_EX_APPWINDOW; 121 122 RECT rect; 123 rect.left = x; 124 rect.top = y; 125 rect.right = x+w; 126 rect.bottom = y+h; 127 128 AdjustWindowRectEx(&rect, m_style, false, m_exstyle); 129 130 m_handle = CreateWindowExA( 131 WS_EX_APPWINDOW, wndclass.ptr, toStringz(title), WS_OVERLAPPEDWINDOW, 132 rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 133 null, null, hinstance, NULL 134 ); 135 136 if (m_handle) 137 { 138 SetWindowLongPtrA(m_handle, GWLP_USERDATA, cast(LONG_PTR)( cast(void*)this )); 139 ShowWindow(m_handle, SW_SHOW); 140 141 m_timer = SetTimer(m_handle, 0, 1000/20, NULL); 142 } 143 else 144 { 145 writeln("oops... coud not create window"); 146 PostQuitMessage(0); 147 } 148 } 149 150 void destroyWindow() 151 { 152 if (m_handle) 153 { 154 DestroyWindow(m_handle); 155 m_handle = null; 156 } 157 } 158 159 this() 160 { 161 } 162 163 ~this() 164 { 165 destroyWindow(); 166 } 167 168 void setBounds(int x, int y, int w, int h) 169 { 170 assert((w >= 0) && (h >= 0)); 171 172 m_width = w; 173 m_height = h; 174 175 if (m_handle) 176 { 177 RECT r = RECT(x, y, x+w, y+h); 178 AdjustWindowRectEx(&r, m_style, false, m_exstyle); 179 MoveWindow(m_handle, r.left, r.top, r.right-r.left, 180 r.bottom-r.top, true); 181 } 182 } 183 184 bool isVisible() 185 { 186 if (m_handle) return (IsWindowVisible(m_handle) != 0); 187 return false; 188 } 189 190 void repaint() 191 { 192 InvalidateRect(m_handle,null,0); 193 } 194 195 void repaint(int x0, int y0, int x1, int y1) 196 { 197 RECT rect; 198 rect.left = x0; 199 rect.top = y0; 200 rect.right = x1; 201 rect.bottom = y1; 202 InvalidateRect(m_handle,&rect,0); 203 } 204 205 // Window proc handler 206 207 LRESULT windowProc(HWND hwnd, UINT msg, WPARAM _wparam, LPARAM _lparam) 208 { 209 WPARAM wparam = _wparam; 210 LPARAM lparam = _lparam; 211 212 switch (msg) 213 { 214 case(WM_PAINT): wm_Paint(); break; 215 case(WM_CLOSE): wm_Close(); break; 216 case(WM_DESTROY): wm_Destroy(); break; 217 case(WM_LBUTTONDOWN): wm_Mouse(MouseEvent.LeftDown, cast(uint)wparam, cast(uint)lparam); break; 218 case(WM_LBUTTONUP): wm_Mouse(MouseEvent.LeftUp, cast(uint)wparam, cast(uint)lparam); break; 219 case(WM_RBUTTONDOWN): wm_Mouse(MouseEvent.RightDown, cast(uint)wparam, cast(uint)lparam); break; 220 case(WM_RBUTTONUP): wm_Mouse(MouseEvent.RightUp, cast(uint)wparam, cast(uint)lparam); break; 221 case(WM_MOUSEMOVE): wm_Mouse(MouseEvent.Move, cast(uint)wparam, cast(uint)lparam); break; 222 case(WM_TIMER): wm_Timer(); break; 223 224 default: return DefWindowProc(hwnd, msg, wparam, lparam); // can't take m_handle there because of WM_CREATE 225 } 226 return 0; 227 } 228 229 void wm_Paint() 230 { 231 PAINTSTRUCT ps; 232 BeginPaint(m_handle, &ps); 233 int l = ps.rcPaint.left; 234 int t = ps.rcPaint.top; 235 int r = ps.rcPaint.right; 236 int b = ps.rcPaint.bottom; 237 238 if (m_canvas is null) m_canvas = new Canvas(r, b); 239 240 if ((m_canvas.width < r) || (m_canvas.height < b)) 241 { 242 m_canvas.resize(r, b); 243 } 244 245 m_canvas.resetView(); 246 m_canvas.setClip(l,t,r,b); 247 248 onPaint(m_canvas); 249 if (m_client !is null) m_client.internalPaint(m_canvas); 250 251 BITMAPINFO info; 252 info.bmiHeader.biSize = info.sizeof; 253 info.bmiHeader.biWidth = m_canvas.stride; 254 info.bmiHeader.biHeight = -m_canvas.height; 255 info.bmiHeader.biPlanes = 1; 256 info.bmiHeader.biBitCount = 32; 257 info.bmiHeader.biCompression = BI_RGB; 258 info.bmiHeader.biSizeImage = m_canvas.stride*m_canvas.height*4; 259 info.bmiHeader.biXPelsPerMeter = 0; 260 info.bmiHeader.biYPelsPerMeter = 0; 261 info.bmiHeader.biClrUsed = 0; 262 info.bmiHeader.biClrImportant = 0; 263 264 SetDIBitsToDevice( 265 ps.hdc, 0, 0, m_canvas.stride, m_canvas.height,0, 0, 0, 266 m_canvas.height, m_canvas.pixels, &info, DIB_RGB_COLORS); 267 268 EndPaint(m_handle, &ps); 269 } 270 271 void wm_Mouse(MouseEvent evt, uint wparam, uint lparam) 272 { 273 if (m_client !is null) 274 { 275 MouseMsg msg; 276 msg.event = evt; 277 msg.left = ((wparam & MK_LBUTTON) != 0); 278 msg.middle = ((wparam & MK_MBUTTON) != 0); 279 msg.right = ((wparam & MK_RBUTTON) != 0); 280 msg.x = cast(short)(lparam); // couldnt find GET_X_PARAM etc... 281 msg.y = cast(short)(lparam>>16); 282 m_client.internalMouse(msg); 283 } 284 } 285 286 void wm_Close() 287 { 288 PostQuitMessage(0); 289 } 290 291 void wm_Destroy() 292 { 293 m_handle = null; 294 } 295 296 void wm_Timer() 297 { 298 if (m_client !is null) m_client.internalTimer(); 299 } 300 301 void onPaint(Canvas canvas) 302 { 303 } 304 305 void setContent(Widget widget) 306 { 307 if (widget.m_parent !is null) widget.m_parent.removeChild(widget); 308 widget.m_window = this; 309 m_client = widget; 310 } 311 }