The open source OpenXR runtime
at prediction-2 448 lines 13 kB view raw
1// Copyright 2019-2022, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Microsoft Windows window code. 6 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 7 * @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com> 8 * @author Jakob Bornecrantz <jakob@collabora.com> 9 * @ingroup comp_main 10 */ 11 12#include <stdlib.h> 13#include <string.h> 14#include "xrt/xrt_compiler.h" 15#include "main/comp_window.h" 16#include "util/u_misc.h" 17#include "os/os_threading.h" 18 19 20#undef ALLOW_CLOSING_WINDOW 21 22/* 23 * 24 * Private structs. 25 * 26 */ 27 28/*! 29 * A Microsoft Windows window. 30 * 31 * @implements comp_target_swapchain 32 */ 33struct comp_window_mswin 34{ 35 struct comp_target_swapchain base; 36 struct os_thread_helper oth; 37 38 ATOM window_class; 39 HINSTANCE instance; 40 HWND window; 41 42 43 bool fullscreen_requested; 44 bool should_exit; 45 bool thread_started; 46 bool thread_exited; 47}; 48 49static WCHAR szWindowClass[] = L"Monado"; 50static WCHAR szWindowData[] = L"MonadoWindow"; 51 52#define COMP_ERROR_GETLASTERROR(C, MSG_WITH_PLACEHOLDER, MSG_WITHOUT_PLACEHOLDER) \ 53 do { \ 54 DWORD err = GetLastError(); \ 55 char *buf = NULL; \ 56 if (0 != FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, \ 57 LANG_SYSTEM_DEFAULT, (LPSTR)&buf, 256, NULL)) { \ 58 COMP_ERROR(C, MSG_WITH_PLACEHOLDER, buf); \ 59 LocalFree(buf); \ 60 } else { \ 61 COMP_ERROR(C, MSG_WITHOUT_PLACEHOLDER); \ 62 } \ 63 } while (0) 64/* 65 * 66 * Functions. 67 * 68 */ 69 70static void 71draw_window(HWND hWnd, struct comp_window_mswin *cwm) 72{ 73 ValidateRect(hWnd, NULL); 74} 75 76static LRESULT CALLBACK 77WndProc(HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam) 78{ 79 struct comp_window_mswin *cwm = GetPropW(hWnd, szWindowData); 80 81 if (!cwm) { 82 // This is before we've set up our window, or for some other helper window... 83 // We might want to handle messages differently in here. 84 return DefWindowProcW(hWnd, message, wParam, lParam); 85 } 86 struct comp_compositor *c = cwm->base.base.c; 87 switch (message) { 88 case WM_PAINT: 89 // COMP_INFO(c, "WM_PAINT"); 90 draw_window(hWnd, cwm); 91 break; 92 case WM_QUIT: 93 // COMP_INFO(c, "WM_QUIT"); 94 PostQuitMessage(0); 95 break; 96 case WM_CLOSE: 97 // COMP_INFO(c, "WM_CLOSE"); 98 cwm->should_exit = true; 99 DestroyWindow(hWnd); 100 cwm->window = NULL; 101 break; 102 case WM_DESTROY: 103 // COMP_INFO(c, "WM_DESTROY"); 104 // Post a quit message and return. 105 PostQuitMessage(0); 106 break; 107 default: return DefWindowProcW(hWnd, message, wParam, lParam); 108 } 109 return 0; 110} 111 112 113static inline struct vk_bundle * 114get_vk(struct comp_window_mswin *cwm) 115{ 116 return &cwm->base.base.c->base.vk; 117} 118 119static void 120comp_window_mswin_destroy(struct comp_target *ct) 121{ 122 struct comp_window_mswin *cwm = (struct comp_window_mswin *)ct; 123 124 // Stop the Windows thread first, destroy also stops the thread. 125 os_thread_helper_destroy(&cwm->oth); 126 127 comp_target_swapchain_cleanup(&cwm->base); 128 129 //! @todo 130 131 free(ct); 132} 133 134static void 135comp_window_mswin_update_window_title(struct comp_target *ct, const char *title) 136{ 137 struct comp_window_mswin *cwm = (struct comp_window_mswin *)ct; 138 //! @todo 139} 140 141static void 142comp_window_mswin_fullscreen(struct comp_window_mswin *w) 143{ 144 //! @todo 145} 146 147static VkResult 148comp_window_mswin_create_surface(struct comp_window_mswin *w, VkSurfaceKHR *out_surface) 149{ 150 struct vk_bundle *vk = get_vk(w); 151 VkResult ret; 152 153 VkWin32SurfaceCreateInfoKHR surface_info = { 154 .sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, 155 .hinstance = w->instance, 156 .hwnd = w->window, 157 }; 158 159 VkSurfaceKHR surface = VK_NULL_HANDLE; 160 ret = vk->vkCreateWin32SurfaceKHR( // 161 vk->instance, // 162 &surface_info, // 163 NULL, // 164 &surface); // 165 if (ret != VK_SUCCESS) { 166 COMP_ERROR(w->base.base.c, "vkCreateWin32SurfaceKHR: %s", vk_result_string(ret)); 167 return ret; 168 } 169 170 VK_NAME_SURFACE(vk, surface, "comp_window_mswin surface"); 171 *out_surface = surface; 172 173 return VK_SUCCESS; 174} 175 176static bool 177comp_window_mswin_init_swapchain(struct comp_target *ct, uint32_t width, uint32_t height) 178{ 179 struct comp_window_mswin *cwm = (struct comp_window_mswin *)ct; 180 VkResult ret; 181 182 ret = comp_window_mswin_create_surface(cwm, &cwm->base.surface.handle); 183 if (ret != VK_SUCCESS) { 184 COMP_ERROR(ct->c, "Failed to create surface '%s'!", vk_result_string(ret)); 185 return false; 186 } 187 188 //! @todo 189 190 return true; 191} 192 193 194static void 195comp_window_mswin_flush(struct comp_target *ct) 196{ 197 struct comp_window_mswin *cwm = (struct comp_window_mswin *)ct; 198} 199 200static void 201comp_window_mswin_window_loop(struct comp_window_mswin *cwm) 202{ 203 struct comp_target *ct = &cwm->base.base; 204 RECT rc = {0, 0, (LONG)(ct->width), (LONG)ct->height}; 205 206 COMP_INFO(ct->c, "Creating window"); 207 cwm->window = 208 CreateWindowExW(0, szWindowClass, L"Monado (Windowed)", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 209 rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, cwm->instance, NULL); 210 if (cwm->window == NULL) { 211 COMP_ERROR_GETLASTERROR(ct->c, "Failed to create window: %s", "Failed to create window"); 212 // parent thread will be notified (by caller) that we have exited. 213 return; 214 } 215 216 COMP_INFO(ct->c, "Setting window properties and showing window"); 217 SetPropW(cwm->window, szWindowData, cwm); 218 SetWindowLongPtr(cwm->window, GWLP_USERDATA, (LONG_PTR)(cwm)); 219 ShowWindow(cwm->window, SW_SHOWDEFAULT); 220 UpdateWindow(cwm->window); 221 222 COMP_INFO(ct->c, "Unblocking parent thread"); 223 // Unblock the parent thread now that we're successfully running. 224 { 225 os_thread_helper_lock(&cwm->oth); 226 cwm->thread_started = true; 227 os_thread_helper_signal_locked(&cwm->oth); 228 os_thread_helper_unlock(&cwm->oth); 229 } 230 COMP_INFO(ct->c, "Starting the Windows window message loop"); 231 232 bool done = false; 233 while (os_thread_helper_is_running(&cwm->oth)) { 234 // force handling messages. 235 MSG msg; 236 while (PeekMessageW(&msg, cwm->window, 0, 0, PM_REMOVE)) { 237 TranslateMessage(&msg); 238 DispatchMessageW(&msg); 239#ifdef ALLOW_CLOSING_WINDOW 240 /// @todo We need to bubble this up to multi-compositor 241 /// and the state tracker (as "instance lost") 242 if (msg.message == WM_QUIT) { 243 COMP_INFO(cwm->base.base.c, "Got WM_QUIT message"); 244 return; 245 } 246 if (msg.message == WM_DESTROY) { 247 COMP_INFO(cwm->base.base.c, "Got WM_DESTROY message"); 248 return; 249 } 250 if (cwm->should_exit) { 251 COMP_INFO(cwm->base.base.c, "Got 'should_exit' flag."); 252 return; 253 } 254#endif 255 } 256 } 257 if (cwm->window != NULL) { 258 // Got shut down by app code, not by a window message, so we still need to clean up our window. 259 if (0 == DestroyWindow(cwm->window)) { 260 COMP_ERROR_GETLASTERROR(ct->c, "DestroyWindow failed: %s", "DestroyWindow failed"); 261 } 262 cwm->window = NULL; 263 } 264} 265 266static void 267comp_window_mswin_mark_exited(struct comp_window_mswin *cwm) 268{ 269 // Unblock the parent thread to advise of our exit 270 os_thread_helper_lock(&cwm->oth); 271 cwm->thread_exited = true; 272 os_thread_helper_signal_locked(&cwm->oth); 273 os_thread_helper_unlock(&cwm->oth); 274} 275 276static void 277comp_window_mswin_thread(struct comp_window_mswin *cwm) 278{ 279 struct comp_target *ct = &cwm->base.base; 280 281 RECT rc = {0, 0, (LONG)(ct->width), (LONG)ct->height}; 282 283 WNDCLASSEXW wcex; 284 U_ZERO(&wcex); 285 wcex.cbSize = sizeof(WNDCLASSEXW); 286 wcex.style = CS_HREDRAW | CS_VREDRAW; 287 288 wcex.lpfnWndProc = WndProc; 289 wcex.cbClsExtra = 0; 290 wcex.cbWndExtra = 0; 291 wcex.hInstance = cwm->instance; 292 wcex.lpszClassName = szWindowClass; 293 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 294//! @todo icon 295#if 0 296 wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SAMPLEGUI)); 297 wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_SAMPLEGUI); 298 wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); 299#endif 300 COMP_INFO(ct->c, "Registering window class"); 301 ATOM window_class = RegisterClassExW(&wcex); 302 if (!window_class) { 303 COMP_ERROR_GETLASTERROR(ct->c, "Failed to register window class: %s", 304 "Failed to register window class"); 305 comp_window_mswin_mark_exited(cwm); 306 return; 307 } 308 309 comp_window_mswin_window_loop(cwm); 310 311 COMP_INFO(ct->c, "Unregistering window class"); 312 if (0 == UnregisterClassW((LPCWSTR)window_class, NULL)) { 313 COMP_ERROR_GETLASTERROR(ct->c, "Failed to unregister window class: %s", 314 "Failed to unregister window class"); 315 } 316 317 comp_window_mswin_mark_exited(cwm); 318} 319 320static void * 321comp_window_mswin_thread_func(void *ptr) 322{ 323 324 struct comp_window_mswin *cwm = (struct comp_window_mswin *)ptr; 325 os_thread_helper_name(&(cwm->oth), "Compositor Window Message Thread"); 326 327 comp_window_mswin_thread(cwm); 328 os_thread_helper_signal_stop(&cwm->oth); 329 COMP_INFO(cwm->base.base.c, "Windows window message thread now exiting."); 330 return NULL; 331} 332 333static bool 334comp_window_mswin_init(struct comp_target *ct) 335{ 336 struct comp_window_mswin *cwm = (struct comp_window_mswin *)ct; 337 cwm->instance = GetModuleHandle(NULL); 338 339 ct->width = 1280; 340 ct->height = 720; 341 342 if (os_thread_helper_start(&cwm->oth, comp_window_mswin_thread_func, cwm) != 0) { 343 COMP_ERROR(ct->c, "Failed to start Windows window message thread"); 344 return false; 345 } 346 347 // Wait for thread to start, create window, etc. 348 os_thread_helper_lock(&cwm->oth); 349 while (!cwm->thread_started && !cwm->thread_exited) { 350 os_thread_helper_wait_locked(&cwm->oth); 351 } 352 bool ret = cwm->thread_started && !cwm->thread_exited; 353 os_thread_helper_unlock(&cwm->oth); 354 return ret; 355} 356 357static void 358comp_window_mswin_configure(struct comp_window_mswin *w, int32_t width, int32_t height) 359{ 360 if (w->base.base.c->settings.fullscreen && !w->fullscreen_requested) { 361 COMP_DEBUG(w->base.base.c, "Setting full screen"); 362 comp_window_mswin_fullscreen(w); 363 w->fullscreen_requested = true; 364 } 365} 366 367#ifdef ALLOW_CLOSING_WINDOW 368/// @todo This is somehow triggering crashes in the multi-compositor, which is trying to run without things it needs, 369/// even though it didn't do this when we called the parent impl instead of inlining it. 370static bool 371comp_window_mswin_configure_check_ready(struct comp_target *ct) 372{ 373 struct comp_window_mswin *cwm = (struct comp_window_mswin *)ct; 374 return os_thread_helper_is_running(&cwm->oth) && cwm->base.swapchain.handle != VK_NULL_HANDLE; 375} 376#endif 377 378struct comp_target * 379comp_window_mswin_create(struct comp_compositor *c) 380{ 381 struct comp_window_mswin *w = U_TYPED_CALLOC(struct comp_window_mswin); 382 if (os_thread_helper_init(&w->oth) != 0) { 383 COMP_ERROR(c, "Failed to init Windows window message thread"); 384 free(w); 385 return NULL; 386 } 387 388 // The display timing code hasn't been tested on Windows and may be broken. 389 comp_target_swapchain_init_and_set_fnptrs(&w->base, COMP_TARGET_FORCE_FAKE_DISPLAY_TIMING); 390 391 w->base.base.name = "MS Windows"; 392 w->base.display = VK_NULL_HANDLE; 393 w->base.base.destroy = comp_window_mswin_destroy; 394 w->base.base.flush = comp_window_mswin_flush; 395 w->base.base.init_pre_vulkan = comp_window_mswin_init; 396 w->base.base.init_post_vulkan = comp_window_mswin_init_swapchain; 397 w->base.base.set_title = comp_window_mswin_update_window_title; 398#ifdef ALLOW_CLOSING_WINDOW 399 w->base.base.check_ready = comp_window_mswin_configure_check_ready; 400#endif 401 w->base.base.c = c; 402 403 return &w->base.base; 404} 405 406 407/* 408 * 409 * Factory 410 * 411 */ 412 413static const char *instance_extensions[] = { 414 VK_KHR_WIN32_SURFACE_EXTENSION_NAME, 415}; 416 417static bool 418detect(const struct comp_target_factory *ctf, struct comp_compositor *c) 419{ 420 return false; 421} 422 423static bool 424create_target(const struct comp_target_factory *ctf, struct comp_compositor *c, struct comp_target **out_ct) 425{ 426 struct comp_target *ct = comp_window_mswin_create(c); 427 if (ct == NULL) { 428 return false; 429 } 430 431 *out_ct = ct; 432 433 return true; 434} 435 436const struct comp_target_factory comp_target_factory_mswin = { 437 .name = "Microsoft Windows(TM)", 438 .identifier = "mswin", 439 .requires_vulkan_for_create = false, 440 .is_deferred = true, 441 .required_instance_version = 0, 442 .required_instance_extensions = instance_extensions, 443 .required_instance_extension_count = ARRAY_SIZE(instance_extensions), 444 .optional_device_extensions = NULL, 445 .optional_device_extension_count = 0, 446 .detect = detect, 447 .create_target = create_target, 448};