Created
August 8, 2023 05:27
-
-
Save vtorri/0d8b29b8158a9e2e0a75d8a284a8f95b to your computer and use it in GitHub Desktop.
cross platform windowing system, proof of concept
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <system_error> | |
#include <string> | |
#include <cstring> | |
#ifdef _WIN32 | |
# ifndef WIN32_LEAN_AND_MEAN | |
# define WIN32_LEAN_AND_MEAN | |
# endif | |
# include <windows.h> | |
#else | |
# include <xcb/xcb.h> | |
# include <xcb/xcb_atom.h> | |
# include <xcb/xcb_keysyms.h> | |
#endif | |
#ifdef _WIN32 | |
class instance | |
{ | |
private: | |
HINSTANCE instance_; | |
public: | |
instance() | |
{ | |
instance_ = GetModuleHandle(nullptr); | |
if (!instance_) | |
throw std::system_error(ENOMEM, std::generic_category(), "Win32 instance"); | |
} | |
~instance() | |
{ | |
FreeLibrary(instance_); | |
} | |
HINSTANCE get() const { return instance_; } | |
}; | |
LRESULT CALLBACK | |
_window_procedure(HWND w, | |
UINT message, | |
WPARAM window_param, | |
LPARAM data_param) | |
{ | |
switch (message) | |
{ | |
case WM_CLOSE: | |
PostQuitMessage(0); | |
return 0; | |
case WM_KEYUP: | |
if (window_param == 'Q') | |
{ | |
PostQuitMessage(0); | |
} | |
return 0; | |
case WM_ERASEBKGND: | |
/* no need to erase back */ | |
return 1; | |
default: | |
return DefWindowProc(w, message, window_param, data_param); | |
} | |
} | |
#else | |
namespace xcb { | |
class connection | |
{ | |
private: | |
xcb_connection_t *c_; | |
public: | |
// will unlikely fail with these values | |
connection() { c_ = xcb_connect(nullptr, nullptr); } | |
~connection() { xcb_disconnect(c_); } | |
xcb_connection_t *get() const { return c_; } | |
}; | |
} // namespace xcb | |
#endif | |
class window | |
{ | |
private: | |
#ifdef _WIN32 | |
instance instance_; | |
HWND win_; | |
#else | |
static xcb::connection conn_; | |
const xcb_screen_t *screen_; | |
uint32_t win_; | |
#endif | |
public: | |
window(const std::string & title, int x, int y, int w, int h); | |
~window(); | |
void show() const; | |
static void run(); | |
}; | |
#ifndef _WIN32 | |
xcb::connection window::conn_; | |
#endif | |
#ifdef _WIN32 | |
window::window(const std::string & title, int x, int y, int w, int h) | |
: instance_() | |
{ | |
WNDCLASS wc; | |
RECT r; | |
SecureZeroMemory(&wc, sizeof(wc)); | |
wc.style = CS_HREDRAW | CS_VREDRAW; | |
wc.lpfnWndProc = _window_procedure; | |
wc.cbClsExtra = 0; | |
wc.cbWndExtra = 0; | |
wc.hInstance = instance_.get(); | |
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION); | |
wc.hCursor = LoadCursor (NULL, IDC_ARROW); | |
wc.hbrBackground = NULL; | |
wc.lpszMenuName = NULL; | |
wc.lpszClassName = "ThorVG"; | |
if(!RegisterClass(&wc)) | |
throw std::system_error(ENOMEM, std::generic_category(), "Win32 window class"); | |
r.left = 0; | |
r.top = 0; | |
r.right = w; | |
r.bottom = h; | |
if (!AdjustWindowRectEx(&r, | |
WS_OVERLAPPEDWINDOW | WS_SIZEBOX, | |
FALSE, | |
0U)) | |
throw std::system_error(ENOMEM, std::generic_category(), "Win32 window rectangle"); | |
win_ = CreateWindowEx(0U, | |
wc.lpszClassName, | |
title.c_str(), | |
WS_OVERLAPPEDWINDOW | WS_SIZEBOX, | |
x, y, | |
r.right - r.left, | |
r.bottom - r.top, | |
NULL, | |
NULL, | |
instance_.get(), | |
NULL); | |
if (!win_) | |
throw std::system_error(ENOMEM, std::generic_category(), "Win32 window creation"); | |
} | |
#else | |
window::window(const std::string & title, int x, int y, int w, int h) | |
{ | |
screen_ = xcb_setup_roots_iterator(xcb_get_setup(conn_.get())).data; | |
win_ = xcb_generate_id(window::conn_.get()); | |
uint32_t mask = 0; | |
uint32_t values[2]; | |
mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; | |
values[0] = screen_->white_pixel; | |
values[1] = XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE; | |
xcb_create_window(window::conn_.get(), | |
XCB_COPY_FROM_PARENT, | |
win_, | |
screen_->root, | |
x, y, | |
w, h, | |
10, | |
XCB_WINDOW_CLASS_INPUT_OUTPUT, | |
screen_->root_visual, | |
mask, values); | |
xcb_change_property(window::conn_.get(), | |
XCB_PROP_MODE_REPLACE, | |
win_, | |
XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, | |
strlen(title.c_str()), title.c_str()); | |
} | |
#endif | |
#ifdef _WIN32 | |
window::~window() | |
{ | |
DestroyWindow(win_); | |
UnregisterClass("ThorVG", instance_.get()); | |
} | |
#else | |
window::~window() | |
{ | |
} | |
#endif | |
#ifdef _WIN32 | |
void window::show() const | |
{ | |
ShowWindow(win_, SW_SHOWNORMAL); | |
} | |
#else | |
void window::show() const | |
{ | |
xcb_map_window(conn_.get(), win_); | |
xcb_flush(conn_.get()); | |
} | |
#endif | |
#ifdef _WIN32 | |
void window::run() | |
{ | |
while(1) | |
{ | |
MSG msg; | |
BOOL ret; | |
ret = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); | |
if (ret) | |
{ | |
do | |
{ | |
if (msg.message == WM_QUIT) | |
goto beach; | |
TranslateMessage(&msg); | |
DispatchMessageW(&msg); | |
} while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); | |
} | |
} | |
beach: | |
return; | |
} | |
#else | |
void window::run() | |
{ | |
xcb_keysym_t ksym = 0; | |
while (1) | |
{ | |
xcb_generic_event_t *e; | |
e = xcb_poll_for_event(conn_.get()); | |
if (e) | |
{ | |
switch (e->response_type & ~0x80) | |
{ | |
case XCB_EXPOSE: | |
break; | |
case XCB_KEY_RELEASE: | |
{ | |
xcb_key_release_event_t *ev; | |
ev = (xcb_key_release_event_t *)e; | |
xcb_key_symbols_t *ksyms = xcb_key_symbols_alloc(conn_.get()); | |
ksym = xcb_key_symbols_get_keysym(ksyms, ev->detail, 0); | |
xcb_key_symbols_free(ksyms); | |
break; | |
} | |
default: | |
break; | |
} | |
free(e); | |
} | |
if (ksym == 'q') | |
break; | |
} | |
} | |
#endif | |
int main() | |
{ | |
window w("test", 10, 10, 480, 320); | |
w.show(); | |
window::run(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment