#include <@APP_INCLUDE@> #include #include "gfx_vulkan.hpp" #include "platform.hpp" #include #include #include #include #include #include #include #include @APP_CLASS@* app = nullptr; GFX* interface = nullptr; Display *dpy = nullptr; int mouse_x = -1, mouse_y = -1; bool mouse_down[2] = {false, false}; int window_width = -1, window_height = -1; xcb_connection_t* connection = nullptr; xcb_screen_t* screen = nullptr; struct WindowConnection { int identifier = 0; xcb_connection_t* connection; xcb_window_t window; }; std::vector window_connections; xcb_intern_atom_reply_t *atom_wm_delete_window; static std::map inputToKeyCode = { { {InputButton::A, 38}, {InputButton::W, 25}, {InputButton::S, 39}, {InputButton::D, 40}, {InputButton::Q, 24} }}; /* * Platform functions" */ const char* platform::get_name() { return "Linux"; } bool platform::supports_feature(const PlatformFeature feature) { if(feature == PlatformFeature::Windowing) return true; return false; } static inline xcb_intern_atom_reply_t* intern_atom_helper(xcb_connection_t *conn, bool only_if_exists, const char *str) { xcb_intern_atom_cookie_t cookie = xcb_intern_atom(conn, only_if_exists, strlen(str), str); return xcb_intern_atom_reply(conn, cookie, NULL); } int platform::open_window(const std::string_view title, const prism::Rectangle rect, const WindowFlags flags) { auto& win = window_connections.emplace_back(); win.connection = connection; win.identifier = window_connections.size() - 1; uint32_t mask; uint32_t values[32]; xcb_void_cookie_t cookie; mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; values[0] = screen->black_pixel; values[1] = XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE; win.window = xcb_generate_id(connection); cookie = xcb_create_window(win.connection, XCB_COPY_FROM_PARENT, win.window, screen->root, rect.offset.x, rect.offset.y, rect.extent.width, rect.extent.height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, mask, values); /* Magic code that will send notification when window is destroyed */ xcb_intern_atom_reply_t* reply = intern_atom_helper(connection, true, "WM_PROTOCOLS"); atom_wm_delete_window = intern_atom_helper(connection, false, "WM_DELETE_WINDOW"); xcb_change_property(connection, XCB_PROP_MODE_REPLACE, win.window, (*reply).atom, 4, 32, 1, &(*atom_wm_delete_window).atom); xcb_change_property(connection, XCB_PROP_MODE_REPLACE, win.window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, title.size(), title.data()); xcb_map_window(connection, win.window); xcb_flush(connection); window_width = rect.extent.width; window_height = rect.extent.height; engine->add_window((void*)&win, 0, rect.extent); app->initialize_render(); return win.identifier; } void platform::close_window(const int index) { } void platform::force_quit() { } float platform::get_window_dpi(const int index) { return 1.0; } float platform::get_monitor_dpi() { return 1.0; } prism::Rectangle platform::get_monitor_resolution() { // based off of https://stackoverflow.com/a/27141466 xcb_flush(connection); xcb_randr_get_screen_resources_cookie_t screenResCookie = {}; screenResCookie = xcb_randr_get_screen_resources(connection, screen->root); xcb_randr_get_screen_resources_reply_t* screenResReply = {}; screenResReply = xcb_randr_get_screen_resources_reply(connection, screenResCookie, 0); int crtcs_num = xcb_randr_get_screen_resources_crtcs_length(screenResReply); xcb_randr_crtc_t* firstCRTC = xcb_randr_get_screen_resources_crtcs(screenResReply); xcb_randr_get_crtc_info_cookie_t* crtcResCookie = new xcb_randr_get_crtc_info_cookie_t[crtcs_num]; for(int i = 0; i < crtcs_num; i++) crtcResCookie[i] = xcb_randr_get_crtc_info(connection, *(firstCRTC+i), 0); xcb_randr_get_crtc_info_reply_t** crtcResReply = new xcb_randr_get_crtc_info_reply_t*[crtcs_num]; for(int i = 0; i < crtcs_num; i++) crtcResReply[i] = xcb_randr_get_crtc_info_reply(connection, crtcResCookie[i], 0); for(int i = 0; i < crtcs_num; i++) { // just pick up the first monitor for now if(crtcResReply[i]) { prism::Rectangle rect; rect.extent.width = crtcResReply[i]->width; rect.extent.height = crtcResReply[i]->height; return rect; } } return {}; } prism::Rectangle platform::get_monitor_work_area() { return platform::get_monitor_resolution(); } prism::Offset platform::get_window_position(const int index) { } prism::Extent platform::get_window_size(const int index) { return {window_width, window_height}; } prism::Extent platform::get_window_drawable_size(const int index) { return {window_width, window_height}; } bool platform::is_window_focused(const int index) { } void platform::set_window_focused(const int index) { } void platform::set_window_position(const int index, const prism::Offset offset) { } void platform::set_window_size(const int index, const prism::Extent extent) { } void platform::set_window_title(const int index, const std::string_view title) { xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window_connections[index].window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, title.size(), title.data()); } bool platform::get_key_down(const InputButton key) { int keycode = inputToKeyCode[key]; char keys[32]; XQueryKeymap(dpy, keys); return keys[keycode/8]&(0x1<<(keycode%8)); } int platform::get_keycode(const InputButton key) { return inputToKeyCode[key]; } prism::Offset platform::get_cursor_position() { return {mouse_x, mouse_y}; } prism::Offset platform::get_screen_cursor_position() { return {mouse_x, mouse_y}; } bool platform::get_mouse_button_down(const int button) { return mouse_down[button]; } std::tuple platform::get_wheel_delta() { } std::tuple platform::get_right_stick_position() { } std::tuple platform::get_left_stick_position() { } void platform::capture_mouse(const bool capture) { } void platform::open_dialog(const bool existing, std::function returnFunction, bool openDirectory) { } void platform::save_dialog(std::function returnFunction) { } char* platform::translate_keycode(const unsigned int keycode) { char* array = new char[2]; XKeyEvent event; memset(&event, 0, sizeof(event)); event.type = 2; event.display = dpy; event.keycode = keycode; int count = XLookupString(&event, array, 1, nullptr, 0); array[1] = '\0'; return array; } int stdout_copy; int stderr_copy; void platform::mute_output() { stdout_copy = dup(STDOUT_FILENO); stderr_copy = dup(STDERR_FILENO); freopen("/dev/null", "a", stdout); freopen("/dev/null", "a", stderr); } void platform::unmute_output() { dup2(stdout_copy, STDOUT_FILENO); dup2(stderr_copy, STDERR_FILENO); close(stdout_copy); close(stderr_copy); } int main(int argc, char* argv[]) { dpy = XOpenDisplay(nullptr); const xcb_setup_t *setup; xcb_screen_iterator_t iter; int scr; connection = xcb_connect(NULL, &scr); setup = xcb_get_setup(connection); iter = xcb_setup_roots_iterator(setup); while (scr-- > 0) xcb_screen_next(&iter); screen = iter.data; engine = new prism::Engine(argc, argv); app = new @APP_CLASS@(); engine->set_app(app); GFXCreateInfo info = {}; interface = new GFXVulkan(); if(interface->initialize(info)) { engine->set_gfx(interface); } else { return -1; } app_main(engine); while(1) { xcb_generic_event_t* event = nullptr; while((event = xcb_poll_for_event (connection))) { switch(event->response_type & 0x7f) { case XCB_CLIENT_MESSAGE: { if ((*(xcb_client_message_event_t*)event).data.data32[0] == (*atom_wm_delete_window).atom) { engine->quit(); } } break; case XCB_MOTION_NOTIFY: { auto ev = (xcb_motion_notify_event_t *)event; //engine->process_mouse_move(ev->event_x, ev->event_y); mouse_x = ev->event_x; mouse_y = ev->event_y; } break; case XCB_BUTTON_PRESS: { xcb_button_press_event_t *press = (xcb_button_press_event_t *)event; int index = 0; if(press->detail == 3) index = 1; //engine->process_mouse(index, press->event_x, press->event_y); mouse_down[index] = true; mouse_x = press->event_x; mouse_y = press->event_y; } break; case XCB_BUTTON_RELEASE: { xcb_button_press_event_t *press = (xcb_button_press_event_t *)event; int index = 0; if(press->detail == 3) index = 1; //engine->process_mouse_released(index); mouse_down[index] = false; } break; case XCB_CONFIGURE_NOTIFY: { const xcb_configure_notify_event_t *cfgEvent = (const xcb_configure_notify_event_t *)event; int identifier = -1; for(auto window : window_connections) { if(window.window == cfgEvent->window) identifier = window.identifier; } window_width = cfgEvent->width; window_height = cfgEvent->height; engine->resize(identifier, {cfgEvent->width, cfgEvent->height}); } break; case XCB_KEY_PRESS: { const xcb_key_release_event_t *keyEvent = (const xcb_key_release_event_t *)event; //engine->process_key(keyEvent->detail); } break; case XCB_KEY_RELEASE: { const xcb_key_release_event_t *keyEvent = (const xcb_key_release_event_t *)event; //engine->process_key_up(keyEvent->detail); } break; } free(event); } if(engine->is_quitting()) break; engine->update(1.0 / 60.0); engine->begin_frame(1.0 / 60.0); engine->render(0); engine->end_frame(); } return 0; } PlatformTheme platform::get_theme() { return PlatformTheme::Light; }