rtengine/src/app_framework/app.c
2024-07-15 16:34:39 +02:00

273 lines
8.1 KiB
C

#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 <stdbool.h>
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 <Windows.h>
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 <X11/Xlib.h>
#include <assert.h>
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