aboutsummaryrefslogtreecommitdiff
path: root/src/platform
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform')
-rw-r--r--src/platform/bootstrap/Bootstrap.cpp6
-rw-r--r--src/platform/bootstrap/CMakeLists.txt2
-rw-r--r--src/platform/graphics/cairo/CairoPainter.cpp6
-rw-r--r--src/platform/gui/sdl/OpenGLRenderer.cpp226
-rw-r--r--src/platform/gui/sdl/UiApplication.cpp14
-rw-r--r--src/platform/gui/sdl/Window.cpp46
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