#include <@APP_INCLUDE@> #include #include #include "platform.hpp" #include #include #include #ifdef ENABLE_VULKAN #include "gfx_vulkan.hpp" #endif #ifdef ENABLE_METAL #include "gfx_metal.hpp" #endif #include "gfx_dummy.hpp" #ifdef PLATFORM_WINDOWS #include #pragma comment(lib, "windowsapp") #endif @APP_CLASS@* app = nullptr; GFX* gfx_interface = nullptr; std::vector windows; std::map renderers; SDL_Window* main_window = nullptr; SDL_Window* get_window(const platform::window_ptr index) { for(auto& window : windows) { if(window == index) return window; } return nullptr; } SDL_Window* get_window_by_sdl_id(const Uint32 id) { for(auto& window : windows) { if(SDL_GetWindowID(window) == id) return window; } return nullptr; } #ifdef ENABLE_METAL void* create_metal_surface(platform::window_ptr window, void* surface_creation_info); void* get_next_metal_drawable(platform::window_ptr window); #endif #ifdef ENABLE_VULKAN void* create_vulkan_surface(platform::window_ptr window, void* surface_creation_info); #endif static std::map inputToKeyCode = { { {InputButton::C, SDL_SCANCODE_C}, {InputButton::V, SDL_SCANCODE_V}, {InputButton::X, SDL_SCANCODE_X}, {InputButton::Y, SDL_SCANCODE_Y}, {InputButton::Z, SDL_SCANCODE_Z}, {InputButton::Backspace, SDL_SCANCODE_BACKSPACE}, {InputButton::Enter, SDL_SCANCODE_RETURN}, {InputButton::W, SDL_SCANCODE_W}, {InputButton::A, SDL_SCANCODE_A}, {InputButton::S, SDL_SCANCODE_S}, {InputButton::D, SDL_SCANCODE_D}, {InputButton::Q, SDL_SCANCODE_Q}, {InputButton::Shift, SDL_SCANCODE_LSHIFT}, {InputButton::Alt, SDL_SCANCODE_LALT}, {InputButton::Super, SDL_SCANCODE_APPLICATION}, {InputButton::Escape, SDL_SCANCODE_ESCAPE}, {InputButton::Tab, SDL_SCANCODE_TAB}, {InputButton::Ctrl, SDL_SCANCODE_LCTRL}, {InputButton::Space, SDL_SCANCODE_SPACE}, {InputButton::LeftArrow, SDL_SCANCODE_LEFT}, {InputButton::RightArrow, SDL_SCANCODE_RIGHT} }}; /* * Platform functions" */ const char* platform::get_name() { return SDL_GetPlatform(); } bool platform::supports_feature(const PlatformFeature feature) { if(feature == PlatformFeature::Windowing) return true; return false; } platform::window_ptr platform::open_window(const std::string_view title, const prism::Rectangle rect, const WindowFlags flags) { auto& win = windows.emplace_back(); int sdl_flags = SDL_WINDOW_ALLOW_HIGHDPI; if(gfx_interface->required_context() == GFXContext::Vulkan) sdl_flags |= SDL_WINDOW_VULKAN; if(flags & WindowFlags::Borderless) sdl_flags |= SDL_WINDOW_BORDERLESS; if(flags & WindowFlags::Resizable) sdl_flags |= SDL_WINDOW_RESIZABLE; if(flags & WindowFlags::Hidden) sdl_flags |= SDL_WINDOW_HIDDEN; auto resolution = platform::get_monitor_resolution(); int real_x = rect.offset.x; int real_y = rect.offset.y; if(rect.offset.x <= -1 || rect.offset.x > resolution.extent.width) real_x = SDL_WINDOWPOS_CENTERED; if(rect.offset.y <= -1 || rect.offset.x > resolution.extent.height) real_y = SDL_WINDOWPOS_CENTERED; int real_width = rect.extent.width; int real_height = rect.extent.height; if(rect.extent.width <= -1 || rect.extent.width > resolution.extent.width) real_width = 640; if(rect.extent.height <= -1 || rect.extent.height > resolution.extent.height) real_height = 480; win = SDL_CreateWindow(title.data(), real_x, real_y, real_width, real_height, sdl_flags); if(windows.size() == 1) main_window = win; #ifdef ENABLE_METAL if(gfx_interface->required_context() == GFXContext::Metal) { SDL_Renderer* renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); renderers[win] = renderer; } #endif engine->add_window((void*)win, win, {static_cast(real_width), static_cast(real_height)}); app->initialize_render(); return win; } bool platform::is_main_window(platform::window_ptr index) { return index == main_window; } void platform::close_window(const platform::window_ptr index) { auto window = get_window(index); engine->remove_window(index); SDL_DestroyWindow(window); utility::erase(windows, window); } void platform::force_quit() { SDL_Quit(); } float platform::get_monitor_dpi() { float dpi = 1.0f; if (!SDL_GetDisplayDPI(0, &dpi, nullptr, nullptr)) dpi = dpi / 96.0f; return dpi; } prism::Rectangle platform::get_monitor_resolution() { SDL_Rect r; SDL_GetDisplayBounds(0, &r); return {r.x, r.y, static_cast(r.w), static_cast(r.h)}; } prism::Rectangle platform::get_monitor_work_area() { SDL_Rect r = {}; SDL_GetDisplayUsableBounds(0, &r); return {r.x, r.y, static_cast(r.w), static_cast(r.h)}; } prism::Offset platform::get_window_position(const platform::window_ptr index) { auto window = get_window(index); int x, y; SDL_GetWindowPosition(window, &x, &y); return {(int32_t)x, (int32_t)y}; } prism::Extent platform::get_window_size(const platform::window_ptr index) { auto window = get_window(index); int width, height; SDL_GetWindowSize(window, &width, &height); return {(uint32_t)width, (uint32_t)height}; } prism::Extent platform::get_window_drawable_size(const platform::window_ptr index) { auto window = get_window(index); int width, height; SDL_Vulkan_GetDrawableSize(window, &width, &height); return {(uint32_t)width, (uint32_t)height}; } bool platform::is_window_focused(const platform::window_ptr index) { auto window = get_window(index); return (SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS) != 0; } void platform::set_window_focused(const platform::window_ptr index) { auto window = get_window(index); SDL_RaiseWindow(window); } void platform::set_window_position(const platform::window_ptr index, const prism::Offset offset) { auto window = get_window(index); SDL_SetWindowPosition(window, offset.x, offset.y); } void platform::set_window_size(const platform::window_ptr index, const prism::Extent extent) { auto window = get_window(index); SDL_SetWindowSize(window, extent.width, extent.height); } void platform::set_window_title(const platform::window_ptr index, const std::string_view title) { auto window = get_window(index); SDL_SetWindowTitle(window, title.data()); } void platform::show_window(const platform::window_ptr index) { auto window = get_window(index); SDL_ShowWindow(window); } bool platform::get_key_down(const InputButton key) { const Uint8 *state = SDL_GetKeyboardState(NULL); return state[inputToKeyCode[key]] && state[SDL_SCANCODE_DOWN]; } int platform::get_keycode(const InputButton key) { return inputToKeyCode[key]; } prism::Offset platform::get_cursor_position() { int x, y; SDL_GetMouseState(&x, &y); return {(int32_t)x, (int32_t)y}; } prism::Offset platform::get_screen_cursor_position() { int x, y; SDL_GetGlobalMouseState(&x, &y); return {(int32_t)x, (int32_t)y}; } bool platform::get_mouse_button_down(const int button) { Uint8 sdl_button = SDL_BUTTON_LEFT; switch(button) { case 0: sdl_button = SDL_BUTTON_LEFT; break; case 1: sdl_button = SDL_BUTTON_RIGHT; break; case 2: sdl_button = SDL_BUTTON_MIDDLE; break; } return (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(sdl_button)) != 0; } float mouse_wheel_x, mouse_wheel_y; std::tuple platform::get_wheel_delta() { return {mouse_wheel_x, mouse_wheel_y}; } std::tuple platform::get_right_stick_position() { return {0.0f, 0.0f}; } std::tuple platform::get_left_stick_position() { return {0.0f, 0.0f}; } void platform::capture_mouse(const bool capture) { SDL_SetRelativeMouseMode((SDL_bool)capture); } void platform::begin_text_input() { SDL_StartTextInput(); } void platform::end_text_input() { SDL_StopTextInput(); } bool platform::supports_context(GFXContext context) { #ifdef ENABLE_VULKAN if(context == GFXContext::Vulkan) return true; #endif #ifdef ENABLE_METAL if(context == GFXContext::Metal) return true; #endif if(context == GFXContext::None) return true; return false; } void platform::initialize_context(const GFXContext context) { #ifdef ENABLE_METAL if(context == GFXContext::Metal) { SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); } #endif } void* platform::get_context_information() { #ifdef ENABLE_VULKAN if(gfx_interface->required_context() == GFXContext::Vulkan) { // dummy window auto dummy = SDL_CreateWindow("", 0, 0, 1, 1, SDL_WINDOW_VULKAN); unsigned int count = 0; SDL_Vulkan_GetInstanceExtensions(dummy, &count, nullptr); std::vector extensions(count); SDL_Vulkan_GetInstanceExtensions(dummy, &count, extensions.data()); SDL_DestroyWindow(dummy); auto info = new vulkan_information(); info->surface_extensions = extensions; return (void*)info; } #endif return nullptr; } void* platform::create_surface(window_ptr window, void* surface_creation_info) { #ifdef ENABLE_VULKAN if(gfx_interface->required_context() == GFXContext::Vulkan) { return create_vulkan_surface(window, surface_creation_info); } #endif #ifdef ENABLE_METAL if(gfx_interface->required_context() == GFXContext::Metal) { return create_metal_surface(window, surface_creation_info); } #endif return nullptr; } void* platform::get_next_image(window_ptr window) { #ifdef ENABLE_METAL if(gfx_interface->required_context() == GFXContext::Metal) { return get_next_metal_drawable(window); } #endif return nullptr; } template void try_initialize() { if(gfx_interface == nullptr) { auto backend = new GFXBackend(); const bool supported = backend->is_supported(); if(!supported) { prism::log("Failed to initialize GFX backend... trying next..."); } else { gfx_interface = backend; platform::initialize_context(gfx_interface->required_context()); } } } struct gfx_backend_initializer { gfx_backend_initializer(std::string_view n, std::function f) : name(n), init_func(f) {} std::string_view name; std::function init_func; }; std::vector gfx_backend_order; int main(int argc, char* argv[]) { SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER); engine = new prism::engine(argc, argv); // determine gfx context at the beginning #ifdef ENABLE_METAL gfx_backend_order.emplace_back(gfx_backend_initializer{"metal", []{ try_initialize(); }}); #endif #ifdef ENABLE_VULKAN gfx_backend_order.emplace_back(gfx_backend_initializer{"vulkan", []{ try_initialize(); }}); #endif gfx_backend_order.emplace_back(gfx_backend_initializer{"dummy", []{ try_initialize(); }}); for(auto arg : engine->command_line_arguments) { utility::move_to_front(gfx_backend_order, [arg](gfx_backend_initializer& init) { return arg == "-" + std::string(init.name); }); } for(auto backend : gfx_backend_order) { backend.init_func(); } app = new @APP_CLASS@(); engine->set_app(app); GFXCreateInfo info = {}; if(gfx_interface->initialize(info)) { engine->set_gfx(gfx_interface); } else { return -1; } app_main(engine); auto end = std::chrono::high_resolution_clock::now(); while(!engine->is_quitting()) { SDL_Event event = {}; while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT: engine->quit(); break; case SDL_MOUSEWHEEL: { mouse_wheel_x = event.wheel.x; mouse_wheel_y = event.wheel.y; } break; case SDL_MOUSEBUTTONDOWN: { int engine_button = 0; if(event.button.button == SDL_BUTTON_RIGHT) engine_button = 1; else if(event.button.button == SDL_BUTTON_MIDDLE) engine_button = 2; engine->process_mouse_down(engine_button, {0, 0}); } break; case SDL_KEYDOWN: { engine->process_key_down(event.key.keysym.scancode); } break; case SDL_KEYUP: { engine->process_key_up(event.key.keysym.scancode); } break; case SDL_WINDOWEVENT: { if (event.window.event == SDL_WINDOWEVENT_RESIZED) { auto window = get_window_by_sdl_id(event.window.windowID); if(window != nullptr) engine->resize(window, {static_cast(event.window.data1), static_cast(event.window.data2)}); } else if(event.window.event == SDL_WINDOWEVENT_MOVED) { auto window = get_window_by_sdl_id(event.window.windowID); if(window != nullptr) engine->move(window); } else if(event.window.event == SDL_WINDOWEVENT_CLOSE) { engine->quit(); } } break; case SDL_TEXTINPUT: { engine->process_text_input(event.text.text); } break; } } if(engine->is_quitting()) break; auto begin = std::chrono::high_resolution_clock::now(); float deltatime = (float)std::chrono::duration_cast(begin - end).count() / 1000000000ULL; end = begin; engine->update(deltatime); engine->begin_frame(deltatime); for(auto window : windows) engine->render(window); engine->end_frame(); } engine->prepare_quit(); return 0; } #ifdef PLATFORM_WINDOWS PlatformTheme platform::get_theme() { using namespace winrt::Windows::UI::ViewManagement; // TODO: figure out if this works pre-anniversary update/other windows other than 10 UISettings settings; auto background = settings.GetColorValue(UIColorType::Background); auto foreground = settings.GetColorValue(UIColorType::Foreground); if (background == winrt::Windows::UI::Colors::White()) return PlatformTheme::Light; else return PlatformTheme::Dark; } #else PlatformTheme platform::get_theme() { return PlatformTheme::Light; } #endif