#include #include #include #include namespace { [[noreturn]] void sdl_fail(std::string const & message) { throw std::runtime_error(message + SDL_GetError()); } struct sdl_initializer { sdl_initializer() { if (SDL_Init(SDL_INIT_VIDEO) != 0) sdl_fail("Failed to initialize SDL2: "); } ~sdl_initializer() { SDL_Quit(); } }; } namespace psemek::util::pimpl { template <> struct impl { sdl_initializer init; SDL_Window * window = nullptr; SDL_GLContext gl_context = nullptr; bool running = false; ~impl() { if (gl_context) SDL_GL_DeleteContext(gl_context); if (window) SDL_DestroyWindow(window); } }; } namespace psemek::app { app::app(std::string const & name) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, gl::sys::GetLeastMajorVersion()); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, gl::sys::GetLeastMinorVersion()); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); impl().window = SDL_CreateWindow(name.data(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_MAXIMIZED); if (!impl().window) sdl_fail("Failed to create window: "); impl().gl_context = SDL_GL_CreateContext(impl().window); if (!impl().gl_context) sdl_fail("Failed to create OpenGL context: "); SDL_GL_MakeCurrent(impl().window, impl().gl_context); if (!gl::sys::LoadFunctions()) throw std::runtime_error("Failed to load OpenGL functions"); log::info() << "Initialized OpenGL " << gl::sys::GetMajorVersion() << '.' << gl::sys::GetMinorVersion(); } app::~app() {} bool app::running() const { return impl().running; } void app::on_resize(int width, int height) { gl::Viewport(0, 0, width, height); } void app::on_focus_gained() {} void app::on_focus_lost() {} void app::on_quit() { impl().running = false; } void app::on_mouse_move(int, int) {} void app::on_mouse_wheel(int) {} void app::on_left_button_down() {} void app::on_left_button_up() {} void app::on_middle_button_down() {} void app::on_middle_button_up() {} void app::on_right_button_down() {} void app::on_right_button_up() {} void app::on_key_down(SDL_Keycode) {} void app::on_key_up(SDL_Keycode) {} void app::poll_events() { for (SDL_Event e; SDL_PollEvent(&e);) switch (e.type) { case SDL_QUIT: on_quit(); break; case SDL_WINDOWEVENT: switch (e.window.event) { case SDL_WINDOWEVENT_RESIZED: on_resize(e.window.data1, e.window.data2); break; case SDL_WINDOWEVENT_FOCUS_GAINED: on_focus_gained(); break; case SDL_WINDOWEVENT_FOCUS_LOST: on_focus_lost(); break; } break; case SDL_MOUSEMOTION: on_mouse_move(e.motion.x, e.motion.y); break; case SDL_MOUSEWHEEL: on_mouse_wheel(e.wheel.y); break; case SDL_MOUSEBUTTONDOWN: switch (e.button.button) { case SDL_BUTTON_LEFT: on_left_button_down(); break; case SDL_BUTTON_MIDDLE: on_middle_button_down(); break; case SDL_BUTTON_RIGHT: on_right_button_down(); break; } break; case SDL_MOUSEBUTTONUP: switch (e.button.button) { case SDL_BUTTON_LEFT: on_left_button_up(); break; case SDL_BUTTON_MIDDLE: on_middle_button_up(); break; case SDL_BUTTON_RIGHT: on_right_button_up(); break; } break; case SDL_KEYDOWN: on_key_down(e.key.keysym.sym); break; case SDL_KEYUP: on_key_up(e.key.keysym.sym); break; } } void app::update() {} void app::draw() { gl::ClearColor(0.7f, 0.7f, 1.f, 1.f); gl::Clear(gl::COLOR_BUFFER_BIT); } void app::run() { impl().running = true; while (running()) { poll_events(); if (!running()) break; update(); draw(); SDL_GL_SwapWindow(impl().window); } } void app::show_cursor(bool show) { SDL_ShowCursor(show ? SDL_TRUE : SDL_FALSE); } }