Daniele Bartolini 3 лет назад
Родитель
Сommit
dcc2de9cc3

+ 44 - 0
src/core/thread/mpsc_queue.inl

@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2012-2022 Daniele Bartolini et al.
+ * License: https://github.com/crownengine/crown/blob/master/LICENSE
+ */
+
+#pragma once
+
+#include "core/thread/spsc_queue.inl"
+#include "core/thread/scoped_mutex.inl"
+
+namespace crown
+{
+/// Multi Producer Single Consumer event queue.
+/// Used to pass data efficiently between multiple producers and a consumer thread.
+///
+/// @ingroup Core
+template<typename T, int MAX_NUM_ITEMS>
+struct MPSCQueue
+{
+	SPSCQueue<T, MAX_NUM_ITEMS> _queue;
+	Mutex _mutex;
+
+	///
+	MPSCQueue(Allocator &a)
+		: _queue(a)
+	{
+	}
+
+	///
+	bool push(const T &ev)
+	{
+		ScopedMutex sm(_mutex);
+		return _queue.push(ev);
+	}
+
+	///
+	bool pop(T &ev)
+	{
+		ScopedMutex sm(_mutex);
+		return _queue.pop(ev);
+	}
+};
+
+} // namespace crown

+ 73 - 0
src/core/thread/spsc_queue.inl

@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2012-2022 Daniele Bartolini et al.
+ * License: https://github.com/crownengine/crown/blob/master/LICENSE
+ */
+
+#pragma once
+
+#include "core/memory/allocator.h"
+#include <atomic>
+
+namespace crown
+{
+/// Single Producer Single Consumer event queue.
+/// Used to pass data efficiently between two threads.
+/// https://www.irif.fr/~guatto/papers/sbac13.pdf
+///
+/// @ingroup Core
+template<typename T, int MAX_NUM_ITEMS>
+struct SPSCQueue
+{
+	CE_ALIGN_DECL(CROWN_CACHE_LINE_SIZE, std::atomic_int _tail);
+	CE_ALIGN_DECL(CROWN_CACHE_LINE_SIZE, std::atomic_int _head);
+	Allocator *_allocator;
+	T *_queue;
+
+	///
+	SPSCQueue(Allocator &a)
+		: _tail(0)
+		, _head(0)
+		, _allocator(&a)
+		, _queue(NULL)
+	{
+		_queue = (T *)_allocator->allocate(sizeof(T) * MAX_NUM_ITEMS);
+	}
+
+	///
+	~SPSCQueue()
+	{
+		_allocator->deallocate(_queue);
+	}
+
+	///
+	bool push(const T &ev)
+	{
+		const int tail = _tail.load(std::memory_order_relaxed);
+		const int head = _head.load(std::memory_order_acquire);
+		const int tail_next = (tail + 1) % MAX_NUM_ITEMS;
+
+		if (CE_UNLIKELY(tail_next == head))
+			return false;
+
+		_queue[tail] = ev;
+		_tail.store(tail_next, std::memory_order_release);
+		return true;
+	}
+
+	///
+	bool pop(T &ev)
+	{
+		const int head = _head.load(std::memory_order_relaxed);
+		const int tail = _tail.load(std::memory_order_acquire);
+		const int head_next = (head + 1) % MAX_NUM_ITEMS;
+
+		if (CE_UNLIKELY(head == tail))
+			return false;
+
+		ev = _queue[head];
+		_head.store(head_next, std::memory_order_release);
+		return true;
+	}
+};
+
+} // namespace crown

+ 7 - 27
src/device/device_event_queue.inl

@@ -6,7 +6,7 @@
 #pragma once
 
 #include "device/types.h"
-#include <atomic>
+#include "core/thread/spsc_queue.inl"
 #include <string.h> // memcpy
 
 namespace crown
@@ -18,14 +18,12 @@ namespace crown
 /// @ingroup Device
 struct DeviceEventQueue
 {
-	CE_ALIGN_DECL(CROWN_CACHE_LINE_SIZE, std::atomic_int _tail);
-	CE_ALIGN_DECL(CROWN_CACHE_LINE_SIZE, std::atomic_int _head);
 #define MAX_OS_EVENTS 128
-	OsEvent _queue[MAX_OS_EVENTS];
+	SPSCQueue<OsEvent, MAX_OS_EVENTS> _queue;
 
-	DeviceEventQueue()
-		: _tail(0)
-		, _head(0)
+	///
+	DeviceEventQueue(Allocator &a)
+		: _queue(a)
 	{
 	}
 
@@ -96,30 +94,12 @@ struct DeviceEventQueue
 
 	bool push_event(const OsEvent &ev)
 	{
-		const int tail = _tail.load(std::memory_order_relaxed);
-		const int head = _head.load(std::memory_order_acquire);
-		const int tail_next = (tail + 1) % MAX_OS_EVENTS;
-
-		if (CE_UNLIKELY(tail_next == head))
-			return false;
-
-		_queue[tail] = ev;
-		_tail.store(tail_next, std::memory_order_release);
-		return true;
+		return _queue.push(ev);
 	}
 
 	bool pop_event(OsEvent &ev)
 	{
-		const int head = _head.load(std::memory_order_relaxed);
-		const int tail = _tail.load(std::memory_order_acquire);
-		const int head_next = (head + 1) % MAX_OS_EVENTS;
-
-		if (CE_UNLIKELY(head == tail))
-			return false;
-
-		ev = _queue[head];
-		_head.store(head_next, std::memory_order_release);
-		return true;
+		return _queue.pop(ev);
 	}
 };
 

+ 12 - 3
src/device/main_android.cpp

@@ -34,6 +34,12 @@ struct AndroidDevice
 	Thread _main_thread;
 	DeviceOptions *_opts;
 
+	AndroidDevice(Allocator &a)
+		: _queue(a)
+		, _opts(NULL)
+	{
+	}
+
 	void run(struct android_app *app, DeviceOptions &opts)
 	{
 		_opts = &opts;
@@ -314,11 +320,11 @@ namespace display
 
 } // namespace display
 
-static AndroidDevice s_advc;
+static AndroidDevice *s_android_device;
 
 bool next_event(OsEvent &ev)
 {
-	return s_advc._queue.pop_event(ev);
+	return s_android_device->_queue.pop_event(ev);
 }
 
 } // namespace crown
@@ -333,7 +339,10 @@ void android_main(struct android_app *app)
 	DeviceOptions opts(default_allocator(), 0, NULL);
 	opts._asset_manager = app->activity->assetManager;
 
-	crown::s_advc.run(app, opts);
+	s_android_device = CE_NEW(default_allocator(), AndroidDevice)(default_allocator());
+	s_android_device->run(app, opts);
+	CE_DELETE(default_allocator(), s_android_device);
+
 	guid_globals::shutdown();
 	memory_globals::shutdown();
 }

+ 62 - 58
src/device/main_linux.cpp

@@ -313,7 +313,7 @@ struct LinuxDevice
 	s16 _mouse_last_y;
 	CursorMode::Enum _cursor_mode;
 
-	LinuxDevice()
+	LinuxDevice(Allocator &a)
 		: _x11_display(NULL)
 		, _wm_delete_window(None)
 		, _net_wm_state(None)
@@ -323,6 +323,7 @@ struct LinuxDevice
 		, _x11_hidden_cursor(None)
 		, _x11_detectable_autorepeat(false)
 		, _screen_config(NULL)
+		, _queue(a)
 		, _x11_window(None)
 		, _mouse_last_x(INT16_MAX)
 		, _mouse_last_y(INT16_MAX)
@@ -605,7 +606,7 @@ struct LinuxDevice
 	}
 };
 
-static LinuxDevice s_ldvc;
+static LinuxDevice *s_linux_device;
 
 struct WindowX11 : public Window
 {
@@ -615,11 +616,11 @@ struct WindowX11 : public Window
 
 	void open(u16 x, u16 y, u16 width, u16 height, u32 parent)
 	{
-		int screen = DefaultScreen(s_ldvc._x11_display);
-		int depth = DefaultDepth(s_ldvc._x11_display, screen);
-		Visual *visual = DefaultVisual(s_ldvc._x11_display, screen);
+		int screen = DefaultScreen(s_linux_device->_x11_display);
+		int depth = DefaultDepth(s_linux_device->_x11_display, screen);
+		Visual *visual = DefaultVisual(s_linux_device->_x11_display, screen);
 
-		::Window root_window = RootWindow(s_ldvc._x11_display, screen);
+		::Window root_window = RootWindow(s_linux_device->_x11_display, screen);
 		::Window parent_window = (parent == 0) ? root_window : (::Window)parent;
 
 		// Create main window
@@ -640,12 +641,12 @@ struct WindowX11 : public Window
 				;
 		} else {
 			XWindowAttributes parent_attrs;
-			XGetWindowAttributes(s_ldvc._x11_display, parent_window, &parent_attrs);
+			XGetWindowAttributes(s_linux_device->_x11_display, parent_window, &parent_attrs);
 			depth = parent_attrs.depth;
 			visual = parent_attrs.visual;
 		}
 
-		s_ldvc._x11_window = XCreateWindow(s_ldvc._x11_display
+		s_linux_device->_x11_window = XCreateWindow(s_linux_device->_x11_display
 			, parent_window
 			, x
 			, y
@@ -658,23 +659,23 @@ struct WindowX11 : public Window
 			, CWBorderPixel | CWEventMask
 			, &win_attribs
 			);
-		CE_ASSERT(s_ldvc._x11_window != None, "XCreateWindow: error");
+		CE_ASSERT(s_linux_device->_x11_window != None, "XCreateWindow: error");
 
-		XSetWMProtocols(s_ldvc._x11_display, s_ldvc._x11_window, &s_ldvc._wm_delete_window, 1);
+		XSetWMProtocols(s_linux_device->_x11_display, s_linux_device->_x11_window, &s_linux_device->_wm_delete_window, 1);
 
-		XMapRaised(s_ldvc._x11_display, s_ldvc._x11_window);
+		XMapRaised(s_linux_device->_x11_display, s_linux_device->_x11_window);
 	}
 
 	void close()
 	{
-		XDestroyWindow(s_ldvc._x11_display, s_ldvc._x11_window);
+		XDestroyWindow(s_linux_device->_x11_display, s_linux_device->_x11_window);
 	}
 
 	void bgfx_setup()
 	{
 		bgfx::PlatformData pd;
-		pd.ndt          = s_ldvc._x11_display;
-		pd.nwh          = (void *)(uintptr_t)s_ldvc._x11_window;
+		pd.ndt          = s_linux_device->_x11_display;
+		pd.nwh          = (void *)(uintptr_t)s_linux_device->_x11_window;
 		pd.context      = NULL;
 		pd.backBuffer   = NULL;
 		pd.backBufferDS = NULL;
@@ -683,41 +684,41 @@ struct WindowX11 : public Window
 
 	void show()
 	{
-		XMapRaised(s_ldvc._x11_display, s_ldvc._x11_window);
+		XMapRaised(s_linux_device->_x11_display, s_linux_device->_x11_window);
 	}
 
 	void hide()
 	{
-		XUnmapWindow(s_ldvc._x11_display, s_ldvc._x11_window);
+		XUnmapWindow(s_linux_device->_x11_display, s_linux_device->_x11_window);
 	}
 
 	void resize(u16 width, u16 height)
 	{
-		XResizeWindow(s_ldvc._x11_display, s_ldvc._x11_window, width, height);
-		XFlush(s_ldvc._x11_display);
+		XResizeWindow(s_linux_device->_x11_display, s_linux_device->_x11_window, width, height);
+		XFlush(s_linux_device->_x11_display);
 	}
 
 	void move(u16 x, u16 y)
 	{
-		XMoveWindow(s_ldvc._x11_display, s_ldvc._x11_window, x, y);
+		XMoveWindow(s_linux_device->_x11_display, s_linux_device->_x11_window, x, y);
 	}
 
 	void maximize_or_restore(bool maximize)
 	{
 		XEvent xev;
 		xev.type = ClientMessage;
-		xev.xclient.window = s_ldvc._x11_window;
-		xev.xclient.message_type = s_ldvc._net_wm_state;
+		xev.xclient.window = s_linux_device->_x11_window;
+		xev.xclient.message_type = s_linux_device->_net_wm_state;
 		xev.xclient.format = 32;
 		xev.xclient.data.l[0] = maximize ? 1 : 0; // 0 = remove property, 1 = set property
-		xev.xclient.data.l[1] = s_ldvc._net_wm_state_maximized_horz;
-		xev.xclient.data.l[2] = s_ldvc._net_wm_state_maximized_vert;
-		XSendEvent(s_ldvc._x11_display, DefaultRootWindow(s_ldvc._x11_display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
+		xev.xclient.data.l[1] = s_linux_device->_net_wm_state_maximized_horz;
+		xev.xclient.data.l[2] = s_linux_device->_net_wm_state_maximized_vert;
+		XSendEvent(s_linux_device->_x11_display, DefaultRootWindow(s_linux_device->_x11_display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
 	}
 
 	void minimize()
 	{
-		XIconifyWindow(s_ldvc._x11_display, s_ldvc._x11_window, DefaultScreen(s_ldvc._x11_display));
+		XIconifyWindow(s_linux_device->_x11_display, s_linux_device->_x11_window, DefaultScreen(s_linux_device->_x11_display));
 	}
 
 	void maximize()
@@ -735,7 +736,7 @@ struct WindowX11 : public Window
 		static char buf[512];
 		memset(buf, 0, sizeof(buf));
 		char *name;
-		XFetchName(s_ldvc._x11_display, s_ldvc._x11_window, &name);
+		XFetchName(s_linux_device->_x11_display, s_linux_device->_x11_window, &name);
 		strncpy(buf, name, sizeof(buf) - 1);
 		XFree(name);
 		return buf;
@@ -743,19 +744,19 @@ struct WindowX11 : public Window
 
 	void set_title(const char *title)
 	{
-		XStoreName(s_ldvc._x11_display, s_ldvc._x11_window, title);
+		XStoreName(s_linux_device->_x11_display, s_linux_device->_x11_window, title);
 	}
 
 	void *handle()
 	{
-		return (void *)(uintptr_t)s_ldvc._x11_window;
+		return (void *)(uintptr_t)s_linux_device->_x11_window;
 	}
 
 	void show_cursor(bool show)
 	{
-		XDefineCursor(s_ldvc._x11_display
-			, s_ldvc._x11_window
-			, show ? None : s_ldvc._x11_hidden_cursor
+		XDefineCursor(s_linux_device->_x11_display
+			, s_linux_device->_x11_window
+			, show ? None : s_linux_device->_x11_hidden_cursor
 			);
 	}
 
@@ -763,37 +764,37 @@ struct WindowX11 : public Window
 	{
 		XEvent xev;
 		xev.xclient.type = ClientMessage;
-		xev.xclient.window = s_ldvc._x11_window;
-		xev.xclient.message_type = s_ldvc._net_wm_state;
+		xev.xclient.window = s_linux_device->_x11_window;
+		xev.xclient.message_type = s_linux_device->_net_wm_state;
 		xev.xclient.format = 32;
 		xev.xclient.data.l[0] = full ? 1 : 0;
-		xev.xclient.data.l[1] = s_ldvc._net_wm_state_fullscreen;
-		XSendEvent(s_ldvc._x11_display, DefaultRootWindow(s_ldvc._x11_display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
+		xev.xclient.data.l[1] = s_linux_device->_net_wm_state_fullscreen;
+		XSendEvent(s_linux_device->_x11_display, DefaultRootWindow(s_linux_device->_x11_display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
 	}
 
 	void set_cursor(MouseCursor::Enum cursor)
 	{
-		XDefineCursor(s_ldvc._x11_display, s_ldvc._x11_window, _x11_cursors[cursor]);
+		XDefineCursor(s_linux_device->_x11_display, s_linux_device->_x11_window, _x11_cursors[cursor]);
 	}
 
 	void set_cursor_mode(CursorMode::Enum mode)
 	{
-		if (mode == s_ldvc._cursor_mode)
+		if (mode == s_linux_device->_cursor_mode)
 			return;
 
-		s_ldvc._cursor_mode = mode;
+		s_linux_device->_cursor_mode = mode;
 
 		if (mode == CursorMode::DISABLED) {
 			XWindowAttributes window_attribs;
-			XGetWindowAttributes(s_ldvc._x11_display, s_ldvc._x11_window, &window_attribs);
+			XGetWindowAttributes(s_linux_device->_x11_display, s_linux_device->_x11_window, &window_attribs);
 			unsigned width = window_attribs.width;
 			unsigned height = window_attribs.height;
-			s_ldvc._mouse_last_x = width/2;
-			s_ldvc._mouse_last_y = height/2;
+			s_linux_device->_mouse_last_x = width/2;
+			s_linux_device->_mouse_last_y = height/2;
 
-			XWarpPointer(s_ldvc._x11_display
+			XWarpPointer(s_linux_device->_x11_display
 				, None
-				, s_ldvc._x11_window
+				, s_linux_device->_x11_window
 				, 0
 				, 0
 				, 0
@@ -801,20 +802,20 @@ struct WindowX11 : public Window
 				, width/2
 				, height/2
 				);
-			XGrabPointer(s_ldvc._x11_display
-				, s_ldvc._x11_window
+			XGrabPointer(s_linux_device->_x11_display
+				, s_linux_device->_x11_window
 				, True
 				, ButtonPressMask | ButtonReleaseMask | PointerMotionMask
 				, GrabModeAsync
 				, GrabModeAsync
-				, s_ldvc._x11_window
-				, s_ldvc._x11_hidden_cursor
+				, s_linux_device->_x11_window
+				, s_linux_device->_x11_hidden_cursor
 				, CurrentTime
 				);
-			XFlush(s_ldvc._x11_display);
+			XFlush(s_linux_device->_x11_display);
 		} else if (mode == CursorMode::NORMAL) {
-			XUngrabPointer(s_ldvc._x11_display, CurrentTime);
-			XFlush(s_ldvc._x11_display);
+			XUngrabPointer(s_linux_device->_x11_display, CurrentTime);
+			XFlush(s_linux_device->_x11_display);
 		}
 	}
 };
@@ -838,7 +839,7 @@ struct DisplayXRandr : public Display
 	void modes(Array<DisplayMode> &modes)
 	{
 		int num = 0;
-		XRRScreenSize *sizes = XRRConfigSizes(s_ldvc._screen_config, &num);
+		XRRScreenSize *sizes = XRRConfigSizes(s_linux_device->_screen_config, &num);
 
 		if (!sizes)
 			return;
@@ -855,14 +856,14 @@ struct DisplayXRandr : public Display
 	void set_mode(u32 id)
 	{
 		int num = 0;
-		XRRScreenSize *sizes = XRRConfigSizes(s_ldvc._screen_config, &num);
+		XRRScreenSize *sizes = XRRConfigSizes(s_linux_device->_screen_config, &num);
 
 		if (!sizes || (int)id >= num)
 			return;
 
-		XRRSetScreenConfig(s_ldvc._x11_display
-			, s_ldvc._screen_config
-			, RootWindow(s_ldvc._x11_display, DefaultScreen(s_ldvc._x11_display))
+		XRRSetScreenConfig(s_linux_device->_x11_display
+			, s_linux_device->_screen_config
+			, RootWindow(s_linux_device->_x11_display, DefaultScreen(s_linux_device->_x11_display))
 			, (int)id
 			, RR_Rotate_0
 			, CurrentTime
@@ -886,7 +887,7 @@ namespace display
 
 bool next_event(OsEvent &ev)
 {
-	return s_ldvc._queue.pop_event(ev);
+	return s_linux_device->_queue.pop_event(ev);
 }
 
 } // namespace crown
@@ -934,8 +935,11 @@ int main(int argc, char **argv)
 	}
 #endif
 
-	if (ec == EXIT_SUCCESS)
-		ec = s_ldvc.run(&opts);
+	if (ec == EXIT_SUCCESS) {
+		s_linux_device = CE_NEW(default_allocator(), LinuxDevice)(default_allocator());
+		ec = s_linux_device->run(&opts);
+		CE_DELETE(default_allocator(), s_linux_device);
+	}
 
 	return ec;
 }

+ 31 - 27
src/device/main_windows.cpp

@@ -289,9 +289,10 @@ struct WindowsDevice
 	s16 _mouse_last_y;
 	CursorMode::Enum _cursor_mode;
 
-	WindowsDevice()
+	WindowsDevice(Allocator &a)
 		: _hwnd(NULL)
 		, _hcursor(NULL)
+		, _queue(a)
 		, _mouse_last_x(INT16_MAX)
 		, _mouse_last_y(INT16_MAX)
 		, _cursor_mode(CursorMode::NORMAL)
@@ -588,11 +589,11 @@ struct WindowsDevice
 	static LRESULT CALLBACK window_proc(HWND hwnd, UINT id, WPARAM wparam, LPARAM lparam);
 };
 
-static WindowsDevice s_wdvc;
+static WindowsDevice *s_windows_device;
 
 LRESULT CALLBACK WindowsDevice::window_proc(HWND hwnd, UINT id, WPARAM wparam, LPARAM lparam)
 {
-	return s_wdvc.pump_events(hwnd, id, wparam, lparam);
+	return s_windows_device->pump_events(hwnd, id, wparam, lparam);
 }
 
 struct WindowWin : public Window
@@ -624,18 +625,18 @@ struct WindowWin : public Window
 	{
 		bgfx::PlatformData pd;
 		memset(&pd, 0, sizeof(pd));
-		pd.nwh = s_wdvc._hwnd;
+		pd.nwh = s_windows_device->_hwnd;
 		bgfx::setPlatformData(pd);
 	}
 
 	void show()
 	{
-		ShowWindow(s_wdvc._hwnd, SW_SHOW);
+		ShowWindow(s_windows_device->_hwnd, SW_SHOW);
 	}
 
 	void hide()
 	{
-		ShowWindow(s_wdvc._hwnd, SW_HIDE);
+		ShowWindow(s_windows_device->_hwnd, SW_HIDE);
 	}
 
 	void resize(u16 width, u16 height)
@@ -643,7 +644,7 @@ struct WindowWin : public Window
 		_width = width;
 		_height = height;
 
-		DWORD style = GetWindowLongA(s_wdvc._hwnd, GWL_STYLE);
+		DWORD style = GetWindowLongA(s_windows_device->_hwnd, GWL_STYLE);
 		RECT rect;
 		rect.left   = 0;
 		rect.top    = 0;
@@ -651,7 +652,7 @@ struct WindowWin : public Window
 		rect.bottom = _height;
 		AdjustWindowRect(&rect, style, FALSE);
 
-		MoveWindow(s_wdvc._hwnd
+		MoveWindow(s_windows_device->_hwnd
 			, _x
 			, _y
 			, rect.right - rect.left
@@ -669,36 +670,36 @@ struct WindowWin : public Window
 
 	void minimize()
 	{
-		ShowWindow(s_wdvc._hwnd, SW_MINIMIZE);
+		ShowWindow(s_windows_device->_hwnd, SW_MINIMIZE);
 	}
 
 	void maximize()
 	{
-		ShowWindow(s_wdvc._hwnd, SW_MAXIMIZE);
+		ShowWindow(s_windows_device->_hwnd, SW_MAXIMIZE);
 	}
 
 	void restore()
 	{
-		ShowWindow(s_wdvc._hwnd, SW_RESTORE);
+		ShowWindow(s_windows_device->_hwnd, SW_RESTORE);
 	}
 
 	const char *title()
 	{
 		static char buf[512];
 		memset(buf, 0, sizeof(buf));
-		GetWindowText(s_wdvc._hwnd, buf, sizeof(buf));
+		GetWindowText(s_windows_device->_hwnd, buf, sizeof(buf));
 		return buf;
 	}
 
 	void set_title(const char *title)
 	{
-		SetWindowText(s_wdvc._hwnd, title);
+		SetWindowText(s_windows_device->_hwnd, title);
 	}
 
 	void show_cursor(bool show)
 	{
-		s_wdvc._hcursor = show ? LoadCursorA(NULL, IDC_ARROW) : NULL;
-		SetCursor(s_wdvc._hcursor);
+		s_windows_device->_hcursor = show ? LoadCursorA(NULL, IDC_ARROW) : NULL;
+		SetCursor(s_windows_device->_hcursor);
 	}
 
 	void set_fullscreen(bool /*fullscreen*/)
@@ -707,26 +708,26 @@ struct WindowWin : public Window
 
 	void set_cursor(MouseCursor::Enum cursor)
 	{
-		s_wdvc._hcursor = _win_cursors[cursor];
-		SetCursor(s_wdvc._hcursor);
+		s_windows_device->_hcursor = _win_cursors[cursor];
+		SetCursor(s_windows_device->_hcursor);
 	}
 
 	void set_cursor_mode(CursorMode::Enum mode)
 	{
-		if (mode == s_wdvc._cursor_mode)
+		if (mode == s_windows_device->_cursor_mode)
 			return;
-		s_wdvc._cursor_mode = mode;
+		s_windows_device->_cursor_mode = mode;
 
 		if (mode == CursorMode::DISABLED) {
 			RECT clipRect;
-			GetWindowRect(s_wdvc._hwnd, &clipRect);
+			GetWindowRect(s_windows_device->_hwnd, &clipRect);
 			unsigned width = clipRect.right - clipRect.left;
 			unsigned height = clipRect.bottom - clipRect.top;
 
-			s_wdvc._mouse_last_x = width/2;
-			s_wdvc._mouse_last_y = height/2;
+			s_windows_device->_mouse_last_x = width/2;
+			s_windows_device->_mouse_last_y = height/2;
 			POINT mouse_pos = { (long)width/2, (long)height/2 };
-			ClientToScreen(s_wdvc._hwnd, &mouse_pos);
+			ClientToScreen(s_windows_device->_hwnd, &mouse_pos);
 			SetCursorPos(mouse_pos.x, mouse_pos.y);
 
 			show_cursor(false);
@@ -739,7 +740,7 @@ struct WindowWin : public Window
 
 	void *handle()
 	{
-		return (void *)(uintptr_t)s_wdvc._hwnd;
+		return (void *)(uintptr_t)s_windows_device->_hwnd;
 	}
 };
 
@@ -784,7 +785,7 @@ namespace display
 
 bool next_event(OsEvent &ev)
 {
-	return s_wdvc._queue.pop_event(ev);
+	return s_windows_device->_queue.pop_event(ev);
 }
 
 } // namespace crown
@@ -845,8 +846,11 @@ int main(int argc, char **argv)
 	}
 #endif
 
-	if (ec == EXIT_SUCCESS)
-		ec = s_wdvc.run(&opts);
+	if (ec == EXIT_SUCCESS) {
+		s_windows_device = CE_NEW(default_allocator(), WindowsDevice)(default_allocator());
+		ec = s_windows_device->run(&opts);
+		CE_DELETE(default_allocator(), s_windows_device);
+	}
 
 	FreeConsole();
 	WSACleanup();