diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/platform/bootstrap/Bootstrap.cpp | 6 | ||||
| -rw-r--r-- | src/platform/bootstrap/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/platform/graphics/cairo/CairoPainter.cpp | 6 | ||||
| -rw-r--r-- | src/platform/gui/sdl/OpenGLRenderer.cpp | 226 | ||||
| -rw-r--r-- | src/platform/gui/sdl/UiApplication.cpp | 14 | ||||
| -rw-r--r-- | src/platform/gui/sdl/Window.cpp | 46 |
6 files changed, 283 insertions, 17 deletions
diff --git a/src/platform/bootstrap/Bootstrap.cpp b/src/platform/bootstrap/Bootstrap.cpp index bcf12613..48cc4f71 100644 --- a/src/platform/bootstrap/Bootstrap.cpp +++ b/src/platform/bootstrap/Bootstrap.cpp @@ -5,6 +5,8 @@ #elif defined(__APPLE__) #include "cru/platform/gui/osx/UiApplication.h" #elif defined(__unix) +#include "cru/platform/graphics/cairo/CairoGraphicsFactory.h" +#include "cru/platform/gui/sdl/UiApplication.h" #include "cru/platform/gui/xcb/UiApplication.h" #else #endif @@ -16,7 +18,9 @@ cru::platform::gui::IUiApplication* CreateUiApplication() { #elif defined(__APPLE__) return new cru::platform::gui::osx::OsxUiApplication(); #else - return new cru::platform::gui::xcb::XcbUiApplication(); + // return new cru::platform::gui::xcb::XcbUiApplication(); + return new cru::platform::gui::sdl::SdlUiApplication( + new cru::platform::graphics::cairo::CairoGraphicsFactory(), true); #endif NotImplemented(); } diff --git a/src/platform/bootstrap/CMakeLists.txt b/src/platform/bootstrap/CMakeLists.txt index 24cdff2b..1d1f0ccc 100644 --- a/src/platform/bootstrap/CMakeLists.txt +++ b/src/platform/bootstrap/CMakeLists.txt @@ -19,7 +19,7 @@ elseif(APPLE) target_link_libraries(CruPlatformBootstrap PUBLIC CruPlatformGuiOsx) elseif(UNIX) target_link_libraries(CruPlatformGraphicsBootstrap PUBLIC CruPlatformGraphicsCairo) - target_link_libraries(CruPlatformBootstrap PUBLIC CruPlatformGuiXcb) + target_link_libraries(CruPlatformBootstrap PUBLIC CruPlatformGuiSdl CruPlatformGuiXcb) else() target_link_libraries(CruPlatformGraphicsBootstrap PUBLIC CruBase) target_link_libraries(CruPlatformBootstrap PUBLIC CruBase) diff --git a/src/platform/graphics/cairo/CairoPainter.cpp b/src/platform/graphics/cairo/CairoPainter.cpp index 1db00efa..97ef7fe0 100644 --- a/src/platform/graphics/cairo/CairoPainter.cpp +++ b/src/platform/graphics/cairo/CairoPainter.cpp @@ -242,10 +242,16 @@ void CairoPainter::EndDraw() { if (device) { cairo_device_flush(device); } + + if (end_draw_callback_) end_draw_callback_(); } valid_ = false; } +void CairoPainter::SetEndDrawCallback(std::function<void()> action) { + end_draw_callback_ = std::move(action); +} + void CairoPainter::CheckValidation() { if (!valid_) { throw ReuseException("Painter already ended drawing."); diff --git a/src/platform/gui/sdl/OpenGLRenderer.cpp b/src/platform/gui/sdl/OpenGLRenderer.cpp index 7f732cb3..7e8fdf33 100644 --- a/src/platform/gui/sdl/OpenGLRenderer.cpp +++ b/src/platform/gui/sdl/OpenGLRenderer.cpp @@ -1,9 +1,46 @@ #include "cru/platform/gui/sdl/OpenGLRenderer.h" +#include "cru/base/Base.h" #include "cru/base/log/Logger.h" +#include "cru/platform/graphics/cairo/CairoPainter.h" +#include "cru/platform/gui/sdl/Base.h" #include "cru/platform/gui/sdl/Window.h" +#include <SDL3/SDL_video.h> +#include <cairo.h> + namespace cru::platform::gui::sdl { -SdlOpenGLRenderer::SdlOpenGLRenderer(SdlWindow* window) { +namespace { +constexpr float kVertices[] = {-1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, -1.0f, + 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, + 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f}; +constexpr unsigned int kIndices[] = {0, 1, 2, 0, 2, 3}; + +constexpr auto kVertexShader = R"(#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec2 aTexCoord; + +out vec2 TexCoord; + +void main() +{ + gl_Position = vec4(aPos, 1.0); + TexCoord = aTexCoord; +})"; + +constexpr auto kFragmentShader = R"(#version 330 core +out vec4 FragColor; + +in vec2 TexCoord; + +uniform sampler2D ourTexture; + +void main() +{ + FragColor = texture(ourTexture, TexCoord); +})"; +} // namespace + +SdlOpenGLRenderer::SdlOpenGLRenderer(SdlWindow* window, int width, int height) { assert(window); assert(window->GetSdlWindow()); @@ -12,20 +49,201 @@ SdlOpenGLRenderer::SdlOpenGLRenderer(SdlWindow* window) { sdl_window_id_ = window->GetSdlWindowId(); sdl_gl_context_ = SDL_GL_CreateContext(sdl_window_); + gl_texture_ = 0; + cairo_surface_ = nullptr; + cairo_ = nullptr; if (!sdl_gl_context_) { throw SdlException("Failed to create sdl gl context."); } - CheckSdlReturn(SDL_GL_MakeCurrent(sdl_window_, sdl_gl_context_)); + auto _ = MakeContextCurrent(); auto version = gladLoadGLContext(&glad_gl_context_, SDL_GL_GetProcAddress); - CRU_LOG_TAG_DEBUG("SDL window id {}, openGL version: {}.", sdl_window_id_, - version); + CRU_LOG_TAG_DEBUG("SDL window id {}, openGL version: {}.{}.", sdl_window_id_, + GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); + + glad_gl_context_.Enable(GL_BLEND); + glad_gl_context_.BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + gl_shader_program_ = CreateGLProgram(); + + glad_gl_context_.GenVertexArrays(1, &gl_vertex_array_); + glad_gl_context_.GenBuffers(1, &gl_vertex_buffer_); + glad_gl_context_.GenBuffers(1, &gl_element_buffer_); + + glad_gl_context_.BindVertexArray(gl_vertex_array_); + + glad_gl_context_.BindBuffer(GL_ARRAY_BUFFER, gl_vertex_buffer_); + glad_gl_context_.BufferData(GL_ARRAY_BUFFER, sizeof(kVertices), + static_cast<const void*>(kVertices), + GL_STATIC_DRAW); + + glad_gl_context_.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_element_buffer_); + glad_gl_context_.BufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kIndices), + static_cast<const void*>(kIndices), + GL_STATIC_DRAW); + + glad_gl_context_.VertexAttribPointer( + 0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), static_cast<void*>(0)); + glad_gl_context_.EnableVertexAttribArray(0); + glad_gl_context_.VertexAttribPointer( + 1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), + reinterpret_cast<void*>(3 * sizeof(float))); + glad_gl_context_.EnableVertexAttribArray(1); + + Resize(width, height); } SdlOpenGLRenderer::~SdlOpenGLRenderer() { + glad_gl_context_.DeleteBuffers(1, &gl_vertex_buffer_); + glad_gl_context_.DeleteBuffers(1, &gl_element_buffer_); + glad_gl_context_.DeleteTextures(1, &gl_texture_); + + if (cairo_) { + cairo_destroy(cairo_); + cairo_ = nullptr; + } + + if (cairo_surface_) { + cairo_surface_destroy(cairo_surface_); + cairo_surface_ = nullptr; + } + CheckSdlReturn(SDL_GL_DestroyContext(sdl_gl_context_)); } +void SdlOpenGLRenderer::Resize(int width, int height) { + CRU_LOG_TAG_DEBUG("Resize to {} x {}", width, height); + + width_ = width; + height_ = height; + + auto _ = MakeContextCurrent(); + + if (cairo_) { + cairo_destroy(cairo_); + cairo_ = nullptr; + } + + if (cairo_surface_) { + cairo_surface_destroy(cairo_surface_); + cairo_surface_ = nullptr; + } + + if (gl_texture_) { + glad_gl_context_.DeleteTextures(1, &gl_texture_); + gl_texture_ = 0; + } + + cairo_surface_ = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + auto status = cairo_surface_status(cairo_surface_); + if (status != CAIRO_STATUS_SUCCESS) { + throw Exception("Failed to create cairo surface."); + } + + cairo_ = cairo_create(cairo_surface_); + status = cairo_status(cairo_); + if (status != CAIRO_STATUS_SUCCESS) { + throw Exception("Failed to create cairo context."); + } + + glad_gl_context_.Viewport(0, 0, width, height); + + glad_gl_context_.GenTextures(1, &gl_texture_); + glad_gl_context_.BindTexture(GL_TEXTURE_2D, gl_texture_); + glad_gl_context_.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + glad_gl_context_.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + GL_NEAREST); +} + +std::unique_ptr<graphics::IPainter> SdlOpenGLRenderer::BeginPaint() { + assert(cairo_surface_); + assert(gl_texture_); + + auto painter = std::make_unique<graphics::cairo::CairoPainter>( + nullptr, cairo_, false, cairo_surface_); + painter->SetEndDrawCallback([this] { Present(); }); + return painter; +} + +void SdlOpenGLRenderer::Present() { + assert(cairo_surface_); + assert(gl_texture_); + + auto _ = MakeContextCurrent(); + + auto data = cairo_image_surface_get_data(cairo_surface_); + glad_gl_context_.TexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, width_, height_, 0, + GL_BGRA, GL_UNSIGNED_BYTE, data); + + glad_gl_context_.UseProgram(gl_shader_program_); + glad_gl_context_.ActiveTexture(GL_TEXTURE0); + glad_gl_context_.BindTexture(GL_TEXTURE_2D, gl_texture_); + glad_gl_context_.BindVertexArray(gl_vertex_array_); + glad_gl_context_.BindBuffer(GL_ARRAY_BUFFER, gl_vertex_buffer_); + glad_gl_context_.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_element_buffer_); + + glad_gl_context_.DrawElements( + GL_TRIANGLES, sizeof(kIndices) / sizeof(*kIndices), GL_UNSIGNED_INT, 0); + CheckSdlReturn(SDL_GL_SwapWindow(sdl_window_)); +} + +GLuint SdlOpenGLRenderer::CreateGLProgram() { + auto check_shader = [this](std::string_view name, GLuint shader) { + int success; + char infoLog[512]; + glad_gl_context_.GetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + glad_gl_context_.GetShaderInfoLog(shader, 512, nullptr, infoLog); + CRU_LOG_TAG_ERROR("Failed to compile OpenGL {} shader: {}", name, + infoLog); + } + }; + + auto check_program = [this](std::string_view name, GLuint program) { + int success; + char infoLog[512]; + glad_gl_context_.GetProgramiv(program, GL_COMPILE_STATUS, &success); + if (!success) { + glad_gl_context_.GetProgramInfoLog(program, 512, nullptr, infoLog); + CRU_LOG_TAG_ERROR("Failed to link OpenGL {} program: {}", name, infoLog); + } + }; + + auto vertex_shader = glad_gl_context_.CreateShader(GL_VERTEX_SHADER); + glad_gl_context_.ShaderSource(vertex_shader, 1, &kVertexShader, nullptr); + glad_gl_context_.CompileShader(vertex_shader); + check_shader("vertex", vertex_shader); + + auto fragment_shader = glad_gl_context_.CreateShader(GL_FRAGMENT_SHADER); + glad_gl_context_.ShaderSource(fragment_shader, 1, &kFragmentShader, nullptr); + glad_gl_context_.CompileShader(fragment_shader); + check_shader("fragment", fragment_shader); + + auto shader_program = glad_gl_context_.CreateProgram(); + glad_gl_context_.AttachShader(shader_program, vertex_shader); + glad_gl_context_.AttachShader(shader_program, fragment_shader); + glad_gl_context_.LinkProgram(shader_program); + check_program("main", shader_program); + + glad_gl_context_.DeleteShader(fragment_shader); + glad_gl_context_.DeleteShader(vertex_shader); + + return shader_program; +} + +Guard SdlOpenGLRenderer::MakeContextCurrent() { + auto old_context = SDL_GL_GetCurrentContext(); + auto old_window = SDL_GL_GetCurrentWindow(); + + CheckSdlReturn(SDL_GL_MakeCurrent(sdl_window_, sdl_gl_context_)); + + return Guard([old_context, old_window] { + CheckSdlReturn(SDL_GL_MakeCurrent(old_window, old_context)); + }); +} + } // namespace cru::platform::gui::sdl diff --git a/src/platform/gui/sdl/UiApplication.cpp b/src/platform/gui/sdl/UiApplication.cpp index 42c50f4d..6064159d 100644 --- a/src/platform/gui/sdl/UiApplication.cpp +++ b/src/platform/gui/sdl/UiApplication.cpp @@ -25,6 +25,12 @@ SdlUiApplication::SdlUiApplication(graphics::IGraphicsFactory* graphics_factory, CheckSdlReturn(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)); empty_event_type_ = SDL_RegisterEvents(1); + CheckSdlReturn(SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)); + CheckSdlReturn(SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3)); + CheckSdlReturn(SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2)); + CheckSdlReturn(SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_CORE)); + cursor_manager_ = std::make_unique<SdlCursorManager>(); clipboard_ = std::make_unique<SdlClipboard>(); } @@ -49,8 +55,12 @@ int SdlUiApplication::Run() { auto timeout = timers_.NextTimeout(std::chrono::steady_clock::now()); SDL_Event event; - CheckSdlReturn(timeout ? SDL_WaitEventTimeout(&event, timeout->count()) - : SDL_WaitEvent(&event)); + if (timeout) { + SDL_WaitEventTimeout(&event, timeout->count()); + } else { + CheckSdlReturn(SDL_WaitEvent(&event)); + } + if (event.type == SDL_EVENT_QUIT) { break; } diff --git a/src/platform/gui/sdl/Window.cpp b/src/platform/gui/sdl/Window.cpp index c3772fbe..d06f8abc 100644 --- a/src/platform/gui/sdl/Window.cpp +++ b/src/platform/gui/sdl/Window.cpp @@ -4,6 +4,7 @@ #include "cru/platform/GraphicsBase.h" #include "cru/platform/graphics/NullPainter.h" #include "cru/platform/graphics/Painter.h" +#include "cru/platform/gui/Cursor.h" #include "cru/platform/gui/Window.h" #include "cru/platform/gui/sdl/Base.h" #include "cru/platform/gui/sdl/Cursor.h" @@ -179,15 +180,25 @@ void SdlWindow::SetToForeground() { void SdlWindow::RequestRepaint() { if (!sdl_window_) return; - NotImplemented(); +#ifdef __unix + repaint_timer_canceler_.Reset(application_->SetImmediate([this] { + PaintEvent_.Raise(nullptr); + NativePaintEventArgs args{{{}, GetClientSize()}}; + Paint1Event_.Raise(args); + renderer_->Present(); + })); +#endif } std::unique_ptr<graphics::IPainter> SdlWindow::BeginPaint() { if (!sdl_window_) { return std::make_unique<graphics::NullPainter>(); } - +#ifdef __unix + return renderer_->BeginPaint(); +#else NotImplemented(); +#endif } IInputMethodContext* SdlWindow::GetInputMethodContext() { @@ -220,7 +231,7 @@ Thickness SdlWindow::GetBorderThickness() { void SdlWindow::DoCreateWindow() { assert(!sdl_window_); - SDL_WindowFlags flags = SDL_WINDOW_HIDDEN; + SDL_WindowFlags flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; #ifdef __unix flags |= SDL_WINDOW_OPENGL; @@ -251,7 +262,9 @@ void SdlWindow::DoCreateWindow() { DoUpdateCursor(); #ifdef __unix - UnixOnCreateWindow(); + int width, height; + CheckSdlReturn(SDL_GetWindowSizeInPixels(sdl_window_, &width, &height)); + UnixOnCreate(width, height); #endif } @@ -286,7 +299,12 @@ void SdlWindow::DoUpdateTitle() { void SdlWindow::DoUpdateCursor() { assert(sdl_window_); - auto cursor = CheckPlatform<SdlCursor>(cursor_, GetPlatformId()); + auto cursor = CheckPlatform<SdlCursor>( + cursor_ ? cursor_ + : application_->GetCursorManager()->GetSystemCursor( + SystemCursorType::Arrow), + GetPlatformId()); + CheckSdlReturn(SDL_SetCursor(cursor->GetSdlCursor())); CheckSdlReturn(SDL_SyncWindow(sdl_window_)); } @@ -343,6 +361,11 @@ bool SdlWindow::HandleEvent(const SDL_Event* event) { case SDL_EVENT_WINDOW_RESIZED: { client_rect_.width = event->window.data1; client_rect_.height = event->window.data2; +#ifdef __unix + int width, height; + CheckSdlReturn(SDL_GetWindowSizeInPixels(sdl_window_, &width, &height)); + UnixOnResize(width, height); +#endif ResizeEvent_.Raise(client_rect_.GetSize()); return true; } @@ -378,7 +401,7 @@ bool SdlWindow::HandleEvent(const SDL_Event* event) { VisibilityChangeEvent_.Raise(WindowVisibilityType::Hide); DestroyEvent_.Raise(nullptr); #ifdef __unix - UnixOnDestroyWindow(); + UnixOnDestroy(); #endif sdl_window_ = nullptr; sdl_window_id_ = 0; @@ -417,12 +440,17 @@ bool SdlWindow::HandleEvent(const SDL_Event* event) { } #ifdef __unix -void SdlWindow::UnixOnCreateWindow() { +void SdlWindow::UnixOnCreate(int width, int height) { assert(sdl_window_); - renderer_ = std::make_unique<SdlOpenGLRenderer>(this); + renderer_ = std::make_unique<SdlOpenGLRenderer>(this, width, height); } -void SdlWindow::UnixOnDestroyWindow() { renderer_ = nullptr; } +void SdlWindow::UnixOnDestroy() { renderer_ = nullptr; } + +void SdlWindow::UnixOnResize(int width, int height) { + assert(sdl_window_); + renderer_->Resize(width, height); +} #endif } // namespace cru::platform::gui::sdl |
