Skip to content

Instantly share code, notes, and snippets.

@vtorri
Created August 8, 2023 05:27
Show Gist options
  • Save vtorri/0d8b29b8158a9e2e0a75d8a284a8f95b to your computer and use it in GitHub Desktop.
Save vtorri/0d8b29b8158a9e2e0a75d8a284a8f95b to your computer and use it in GitHub Desktop.
cross platform windowing system, proof of concept
#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