#include "app.h" #include "main_loop.h" #include "runtime/aio.h" #include "runtime/buffer_manager.h" #include "runtime/config.h" #include "renderer/common/renderer_api.h" #include RT_CVAR_I(rt_Fullscreen, "Show window in fullscreen mode. [0/1] Default: 0", 1); RT_CVAR_I(rt_WindowWidth, "Window width. Default: 1024", 1024); RT_CVAR_I(rt_WindowHeight, "Window height. Default: 768", 768); #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include static LRESULT CALLBACK win32WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CLOSE: PostQuitMessage(0); return 0; default: return DefWindowProcW(hWnd, uMsg, wParam, lParam); } } RT_DLLEXPORT int rtWin32Entry(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow, rt_app_callbacks app_callbacks) { if (rtInitRuntime() != RT_SUCCESS) return 1; rtRegisterCVAR(&rt_Fullscreen); rtRegisterCVAR(&rt_WindowWidth); rtRegisterCVAR(&rt_WindowHeight); if (app_callbacks.RegisterCVars) app_callbacks.RegisterCVars(); WNDCLASSEXW wndclass = { .cbSize = sizeof(wndclass), .hInstance = hInstance, .lpszClassName = L"rtWndClass", .style = CS_OWNDC, .lpfnWndProc = win32WndProc, }; if (!RegisterClassExW(&wndclass)) { rtReportError("CORE", "RegisterClassEx failed: %u", GetLastError()); return 1; } HWND wnd = NULL; if (rt_Fullscreen.i) { /* Fullscreen window */ int w = GetSystemMetrics(SM_CXSCREEN); int h = GetSystemMetrics(SM_CYSCREEN); wnd = CreateWindowExW(WS_EX_APPWINDOW, L"rtWndClass", L"Voyage", WS_POPUP, 0, 0, w, h, NULL, NULL, hInstance, NULL); ShowWindow(wnd, SW_SHOW); } else { /* Windowed mode */ int w = rt_WindowWidth.i; int h = rt_WindowHeight.i; wnd = CreateWindowExW(WS_EX_APPWINDOW, L"rtWndClass", L"Voyage", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT, w, h, NULL, NULL, hInstance, NULL); ShowWindow(wnd, SW_SHOW); } if (!wnd) { rtReportError("CORE", "Failed to create the game window: %u", GetLastError()); return 1; } unsigned int window_width, window_height; { RECT r; GetClientRect(wnd, &r); window_width = r.right; window_height = r.bottom; } rt_renderer_init_info renderer_info = {.hWnd = wnd, .hInstance = hInstance, .width = window_width, .height = window_height, .is_fullscreen = rt_Fullscreen.i}; if (rtLoadRenderer() != RT_SUCCESS) { return 1; } if (g_renderer.Init(&renderer_info) != RT_SUCCESS) { return 1; } app_callbacks.Init(); if (rtInitMainLoop(app_callbacks.Update, app_callbacks.Render) != RT_SUCCESS) { return 1; } /* Main Loop */ bool keep_running = true; while (keep_running) { MSG msg; while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { keep_running = false; } else { TranslateMessage(&msg); DispatchMessageW(&msg); } } } app_callbacks.Shutdown(); rtShutdownMainLoop(); g_renderer.Shutdown(); DestroyWindow(wnd); UnregisterClassW(L"rtWndClass", hInstance); rtShutdownRuntime(); return 0; } #elif defined(RT_USE_XLIB) #include #include static void xlibSetFullscreen(Display *dpy, int screen, Window window, bool enable) { Atom wm_state = XInternAtom(dpy, "_NET_W_STATE", False); Atom wm_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); if (wm_state == None || wm_fullscreen == None) { rtLog("CORE", "Window manager does not support fullscreen mode."); return; } #define _NET_WM_STATE_REMOVE 0 #define _NET_WM_STATE_ADD 1 #define EVENT_SOURCE_APPLICATION 1 XEvent ev; ev.type = ClientMessage; ev.xclient.display = dpy; ev.xclient.window = window; ev.xclient.message_type = wm_state; ev.xclient.format = 32; ev.xclient.data.l[0] = (enable) ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; ev.xclient.data.l[1] = wm_fullscreen; ev.xclient.data.l[2] = 0; ev.xclient.data.l[3] = EVENT_SOURCE_APPLICATION; ev.xclient.data.l[4] = 0; Window root_window = XRootWindow(dpy, screen); long ev_mask = SubstructureRedirectMask; if (!XSendEvent(dpy, root_window, False, ev_mask, &ev)) { rtReportError("CORE", "Failed to send x11 fullscreen event."); } #undef _NET_WM_STATE_ADD #undef _NET_WM_STATE_REMOVE #undef EVENT_SOURCE_APPLICATION } RT_DLLEXPORT int rtXlibEntry(int argc, char **argv, rt_app_callbacks app_callbacks) { if (rtInitRuntime() != RT_SUCCESS) return 1; if (app_callbacks.RegisterCVars) app_callbacks.RegisterCVars(); Display *dpy = XOpenDisplay(NULL); if (!dpy) { rtReportError("CORE", "Failed to open default display"); return 1; } int screen = DefaultScreen(dpy); Window window; int w = rt_WindowWidth.i; int h = rt_WindowHeight.i; window = XCreateSimpleWindow(dpy, RootWindow(dpy, screen), 10, 10, w, h, 1, BlackPixel(dpy, screen), WhitePixel(dpy, screen)); XSelectInput(dpy, window, KeyPressMask); XMapWindow(dpy, window); XStoreName(dpy, window, "Voyage"); Atom wm_close = XInternAtom(dpy, "WM_DELETE_WINDOW", False); if (!wm_close) { rtReportError("CORE", "Failed to find WM_DELETE_WINDOW atom."); XDestroyWindow(dpy, window); XCloseDisplay(dpy); return 1; } XSetWMProtocols(dpy, window, &wm_close, 1); if (rt_Fullscreen.i) xlibSetFullscreen(dpy, screen, window, true); rt_renderer_init_info renderer_info = {.display = dpy, .window = window}; if (!rtInitGFX(&renderer_info)) { rtReportError("GFX", "Init failed."); return 1; } app_callbacks.Init(); /* Main Loop */ bool keep_running = true; while (keep_running) { while (XEventsQueued(dpy, QueuedAlready) > 0) { XEvent event; XNextEvent(dpy, &event); switch (event.type) { case KeyPress: break; case ButtonPressMask: /* Mouse down */ break; case PointerMotionMask: /* Mouse movement */ break; case ClientMessage: if (event.xclient.data.l[0] == (long)wm_close) { rtLog("CORE", "Received WM_DELETE_WINDOW"); keep_running = false; } break; } } } app_callbacks.Shutdown(); XDestroyWindow(dpy, window); XCloseDisplay(dpy); rtShutdownRuntime(); return 0; } #endif