mirror of
https://github.com/slendidev/lunar.git
synced 2026-01-30 16:28:58 +02:00
Compare commits
2 Commits
efa6e289b6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 92912a321c | |||
| e04f1cf291 |
@@ -23,6 +23,7 @@
|
|||||||
pkg-config
|
pkg-config
|
||||||
glslang
|
glslang
|
||||||
shaderc
|
shaderc
|
||||||
|
wayland-scanner
|
||||||
];
|
];
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
vulkan-loader
|
vulkan-loader
|
||||||
|
|||||||
40
meson.build
40
meson.build
@@ -1,4 +1,4 @@
|
|||||||
project('vr-compositor', 'cpp',
|
project('vr-compositor', 'c', 'cpp',
|
||||||
version: '0.1',
|
version: '0.1',
|
||||||
default_options: [
|
default_options: [
|
||||||
'cpp_std=c++26',
|
'cpp_std=c++26',
|
||||||
@@ -17,14 +17,32 @@ fastgltf_opts.set_override_option('werror', 'false')
|
|||||||
fastgltf = cmake.subproject('fastgltf', options: fastgltf_opts)
|
fastgltf = cmake.subproject('fastgltf', options: fastgltf_opts)
|
||||||
|
|
||||||
cc = meson.get_compiler('cpp')
|
cc = meson.get_compiler('cpp')
|
||||||
|
wl_mod = import('wayland')
|
||||||
|
|
||||||
wayland_dep = dependency('wayland-server', include_type: 'system')
|
wayland_dep = dependency('wayland-server', include_type: 'system')
|
||||||
|
wayland_client_dep = dependency('wayland-client', include_type: 'system')
|
||||||
vulkan_dep = dependency('vulkan', include_type: 'system')
|
vulkan_dep = dependency('vulkan', include_type: 'system')
|
||||||
openxr_dep = dependency('openxr', include_type: 'system')
|
openxr_dep = dependency('openxr', include_type: 'system')
|
||||||
zlib_dep = dependency('zlib', include_type: 'system')
|
zlib_dep = dependency('zlib', include_type: 'system')
|
||||||
sdl3_dep = dependency('sdl3', include_type: 'system')
|
sdl3_dep = dependency('sdl3', include_type: 'system')
|
||||||
libinput_dep = dependency('libinput', include_type: 'system')
|
libinput_dep = dependency('libinput', include_type: 'system')
|
||||||
libudev_dep = dependency('libudev', include_type: 'system')
|
libudev_dep = dependency('libudev', include_type: 'system')
|
||||||
|
wayland_protocol = wl_mod.scan_xml(
|
||||||
|
'protocols/wayland.xml',
|
||||||
|
client: false,
|
||||||
|
server: true,
|
||||||
|
)
|
||||||
|
wayland_protocol_source = wayland_protocol[0]
|
||||||
|
wayland_server_header = wayland_protocol[1]
|
||||||
|
|
||||||
|
xdg_shell_protocol = wl_mod.scan_xml(
|
||||||
|
'protocols/xdg-shell.xml',
|
||||||
|
client: true,
|
||||||
|
server: true,
|
||||||
|
)
|
||||||
|
xdg_shell_protocol_source = xdg_shell_protocol[0]
|
||||||
|
xdg_shell_client_header = xdg_shell_protocol[1]
|
||||||
|
xdg_shell_server_header = xdg_shell_protocol[2]
|
||||||
imgui_src = files(
|
imgui_src = files(
|
||||||
'thirdparty/imgui/imgui.cpp',
|
'thirdparty/imgui/imgui.cpp',
|
||||||
'thirdparty/imgui/imgui_draw.cpp',
|
'thirdparty/imgui/imgui_draw.cpp',
|
||||||
@@ -161,12 +179,24 @@ exe = executable('vr-compositor',
|
|||||||
'src/Loader.cpp',
|
'src/Loader.cpp',
|
||||||
'src/DescriptorWriter.cpp',
|
'src/DescriptorWriter.cpp',
|
||||||
'src/CPUTexture.cpp',
|
'src/CPUTexture.cpp',
|
||||||
|
'src/wayland/WaylandServer.cpp',
|
||||||
|
'src/wayland/Surface.cpp',
|
||||||
|
'src/wayland/Shm.cpp',
|
||||||
|
'src/wayland/protocols/CompositorProtocol.cpp',
|
||||||
|
'src/wayland/protocols/ShmProtocol.cpp',
|
||||||
|
'src/wayland/protocols/XdgShellProtocol.cpp',
|
||||||
'src/Skybox.cpp',
|
'src/Skybox.cpp',
|
||||||
'src/VulkanRenderer.cpp',
|
'src/VulkanRenderer.cpp',
|
||||||
'src/Application.cpp',
|
'src/Application.cpp',
|
||||||
|
wayland_protocol_source,
|
||||||
|
xdg_shell_protocol_source,
|
||||||
|
wayland_server_header,
|
||||||
|
xdg_shell_server_header,
|
||||||
],
|
],
|
||||||
|
c_args: ['-Wno-missing-variable-declarations'],
|
||||||
include_directories: [
|
include_directories: [
|
||||||
vkbootstrap_inc,
|
vkbootstrap_inc,
|
||||||
|
include_directories('.'),
|
||||||
imgui_inc,
|
imgui_inc,
|
||||||
'thirdparty/smath/include'
|
'thirdparty/smath/include'
|
||||||
],
|
],
|
||||||
@@ -176,3 +206,11 @@ exe = executable('vr-compositor',
|
|||||||
'--embed-dir=' + join_paths(meson.project_build_root(), 'shaders'),
|
'--embed-dir=' + join_paths(meson.project_build_root(), 'shaders'),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
executable('shm-life',
|
||||||
|
'tools/shm_life.cpp',
|
||||||
|
dependencies: [wayland_client_dep],
|
||||||
|
sources: [xdg_shell_protocol_source, xdg_shell_client_header],
|
||||||
|
c_args: ['-Wno-missing-variable-declarations'],
|
||||||
|
cpp_args: ['-Wno-cast-qual'],
|
||||||
|
)
|
||||||
|
|||||||
3314
protocols/wayland.xml
Normal file
3314
protocols/wayland.xml
Normal file
File diff suppressed because it is too large
Load Diff
1418
protocols/xdg-shell.xml
Normal file
1418
protocols/xdg-shell.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,8 @@
|
|||||||
#define XR_USE_GRAPHICS_API_VULKAN
|
#define XR_USE_GRAPHICS_API_VULKAN
|
||||||
#include <openxr/openxr_platform.h>
|
#include <openxr/openxr_platform.h>
|
||||||
|
|
||||||
|
#include "wayland-server-protocol.h"
|
||||||
|
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
# pragma clang diagnostic push
|
# pragma clang diagnostic push
|
||||||
# pragma clang diagnostic ignored "-Wreserved-identifier"
|
# pragma clang diagnostic ignored "-Wreserved-identifier"
|
||||||
@@ -59,6 +61,9 @@
|
|||||||
|
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "VulkanRenderer.h"
|
#include "VulkanRenderer.h"
|
||||||
|
#include "wayland/Shm.h"
|
||||||
|
#include "wayland/Surface.h"
|
||||||
|
#include "wayland/WaylandServer.h"
|
||||||
|
|
||||||
#if defined(TRACY_ENABLE)
|
#if defined(TRACY_ENABLE)
|
||||||
# include <tracy/Tracy.hpp>
|
# include <tracy/Tracy.hpp>
|
||||||
@@ -416,11 +421,11 @@ auto split_extension_list(std::string_view list) -> std::vector<std::string>
|
|||||||
if (start >= list.size()) {
|
if (start >= list.size()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto end = list.find(' ', start);
|
auto end { list.find(' ', start) };
|
||||||
if (end == std::string_view::npos) {
|
if (end == std::string_view::npos) {
|
||||||
end = list.size();
|
end = list.size();
|
||||||
}
|
}
|
||||||
auto token = list.substr(start, end - start);
|
auto token { list.substr(start, end - start) };
|
||||||
if (!token.empty()) {
|
if (!token.empty()) {
|
||||||
extensions.emplace_back(token);
|
extensions.emplace_back(token);
|
||||||
}
|
}
|
||||||
@@ -433,9 +438,9 @@ auto xr_rotate_vector(XrQuaternionf q, smath::Vec3 v) -> smath::Vec3
|
|||||||
{
|
{
|
||||||
smath::Vec3 u { q.x, q.y, q.z };
|
smath::Vec3 u { q.x, q.y, q.z };
|
||||||
float const s = q.w;
|
float const s = q.w;
|
||||||
auto const dot = u.dot(v);
|
auto const dot { u.dot(v) };
|
||||||
auto const u_dot = u.dot(u);
|
auto const u_dot { u.dot(u) };
|
||||||
auto const cross = u.cross(v);
|
auto const cross { u.cross(v) };
|
||||||
return (u * (2.0f * dot)) + (v * (s * s - u_dot)) + (cross * (2.0f * s));
|
return (u * (2.0f * dot)) + (v * (s * s - u_dot)) + (cross * (2.0f * s));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,6 +452,45 @@ auto xr_rotate_vector(XrQuaternionf q, smath::Vec3 v) -> smath::Vec3
|
|||||||
return std::string { buffer.data() };
|
return std::string { buffer.data() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto make_shm_texture(Lunar::Wayland::ShmBuffer const &buffer)
|
||||||
|
-> std::optional<Lunar::CPUTexture>
|
||||||
|
{
|
||||||
|
if (!buffer.pool || !buffer.data()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (buffer.width <= 0 || buffer.height <= 0 || buffer.stride <= 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (buffer.format != WL_SHM_FORMAT_ARGB8888
|
||||||
|
&& buffer.format != WL_SHM_FORMAT_XRGB8888) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const width { static_cast<uint32_t>(buffer.width) };
|
||||||
|
auto const height { static_cast<uint32_t>(buffer.height) };
|
||||||
|
auto const row_bytes { static_cast<size_t>(width) * 4 };
|
||||||
|
if (buffer.stride < static_cast<std::int32_t>(row_bytes)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> pixels(row_bytes * height);
|
||||||
|
auto const *src { reinterpret_cast<uint8_t const *>(buffer.data()) };
|
||||||
|
for (uint32_t y = 0; y < height; ++y) {
|
||||||
|
auto const *row_src { src + static_cast<size_t>(buffer.stride) * y };
|
||||||
|
auto const dst_y { height - y - 1 };
|
||||||
|
auto *row_dst { pixels.data() + row_bytes * dst_y };
|
||||||
|
std::memcpy(row_dst, row_src, row_bytes);
|
||||||
|
if (buffer.format == WL_SHM_FORMAT_XRGB8888) {
|
||||||
|
for (uint32_t x = 0; x < width; ++x) {
|
||||||
|
row_dst[x * 4 + 3] = 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lunar::CPUTexture(
|
||||||
|
std::move(pixels), width, height, vk::Format::eB8G8R8A8Unorm);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace Lunar {
|
namespace Lunar {
|
||||||
@@ -494,11 +538,12 @@ Application::Application()
|
|||||||
bool const has_display
|
bool const has_display
|
||||||
= (display_env && *display_env) || (wayland_env && *wayland_env);
|
= (display_env && *display_env) || (wayland_env && *wayland_env);
|
||||||
m_backend = has_display ? Backend::SDL : Backend::KMS;
|
m_backend = has_display ? Backend::SDL : Backend::KMS;
|
||||||
|
m_wayland = std::make_unique<Wayland::WaylandServer>(m_logger);
|
||||||
|
|
||||||
init_openxr();
|
init_openxr();
|
||||||
|
|
||||||
auto instance_extensions = std::span<std::string const> {};
|
auto instance_extensions { std::span<std::string const> {} };
|
||||||
auto device_extensions = std::span<std::string const> {};
|
auto device_extensions { std::span<std::string const> {} };
|
||||||
if (m_openxr) {
|
if (m_openxr) {
|
||||||
instance_extensions = m_openxr->instance_extensions;
|
instance_extensions = m_openxr->instance_extensions;
|
||||||
device_extensions = m_openxr->device_extensions;
|
device_extensions = m_openxr->device_extensions;
|
||||||
@@ -539,6 +584,8 @@ Application::Application()
|
|||||||
|
|
||||||
init_input();
|
init_input();
|
||||||
|
|
||||||
|
init_wayland();
|
||||||
|
|
||||||
if (m_backend == Backend::SDL)
|
if (m_backend == Backend::SDL)
|
||||||
mouse_captured(true);
|
mouse_captured(true);
|
||||||
|
|
||||||
@@ -593,12 +640,12 @@ auto Application::asset_directory() -> std::filesystem::path
|
|||||||
candidates.emplace_back(base / "lunar" / "assets");
|
candidates.emplace_back(base / "lunar" / "assets");
|
||||||
} };
|
} };
|
||||||
|
|
||||||
if (auto const *xdg_data_home = getenv("XDG_DATA_HOME");
|
if (auto const *xdg_data_home { getenv("XDG_DATA_HOME") };
|
||||||
xdg_data_home && *xdg_data_home) {
|
xdg_data_home && *xdg_data_home) {
|
||||||
add_xdg_path(xdg_data_home);
|
add_xdg_path(xdg_data_home);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto const *xdg_data_dirs = getenv("XDG_DATA_DIRS");
|
if (auto const *xdg_data_dirs { getenv("XDG_DATA_DIRS") };
|
||||||
xdg_data_dirs && *xdg_data_dirs) {
|
xdg_data_dirs && *xdg_data_dirs) {
|
||||||
std::string_view dirs_view { xdg_data_dirs };
|
std::string_view dirs_view { xdg_data_dirs };
|
||||||
size_t start { 0 };
|
size_t start { 0 };
|
||||||
@@ -647,6 +694,14 @@ auto Application::init_test_meshes() -> void
|
|||||||
m_test_meshes = std::move(*meshes);
|
m_test_meshes = std::move(*meshes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Application::init_wayland() -> void
|
||||||
|
{
|
||||||
|
auto const socket_name { m_wayland->socket_name() };
|
||||||
|
assert(setenv("WAYLAND_DISPLAY", socket_name.data(), true) == 0);
|
||||||
|
m_logger.info("Started Wayland display socket on {}", socket_name);
|
||||||
|
std::println("WAYLAND_DISPLAY={}", socket_name);
|
||||||
|
}
|
||||||
|
|
||||||
auto Application::run() -> void
|
auto Application::run() -> void
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -664,6 +719,10 @@ auto Application::run() -> void
|
|||||||
float fps { 0.0f };
|
float fps { 0.0f };
|
||||||
while (m_running) {
|
while (m_running) {
|
||||||
GZoneScopedN("Frame");
|
GZoneScopedN("Frame");
|
||||||
|
if (m_wayland) {
|
||||||
|
m_wayland->dispatch();
|
||||||
|
m_wayland->flush();
|
||||||
|
}
|
||||||
if (m_openxr) {
|
if (m_openxr) {
|
||||||
poll_openxr_events();
|
poll_openxr_events();
|
||||||
}
|
}
|
||||||
@@ -671,7 +730,7 @@ auto Application::run() -> void
|
|||||||
if (use_sdl) {
|
if (use_sdl) {
|
||||||
now = SDL_GetTicks();
|
now = SDL_GetTicks();
|
||||||
} else {
|
} else {
|
||||||
auto const now_tp = std::chrono::steady_clock::now();
|
auto const now_tp { std::chrono::steady_clock::now() };
|
||||||
now = static_cast<uint64_t>(
|
now = static_cast<uint64_t>(
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
now_tp.time_since_epoch())
|
now_tp.time_since_epoch())
|
||||||
@@ -816,9 +875,9 @@ auto Application::run() -> void
|
|||||||
|
|
||||||
if (!m_show_imgui) {
|
if (!m_show_imgui) {
|
||||||
m_camera.up = camera_up;
|
m_camera.up = camera_up;
|
||||||
auto const distance = target_distance > 0.0f
|
auto const distance { target_distance > 0.0f
|
||||||
? target_distance
|
? target_distance
|
||||||
: std::max(1.0f, m_cursor.r);
|
: std::max(1.0f, m_cursor.r) };
|
||||||
m_camera.target = m_camera.position + look_dir * distance;
|
m_camera.target = m_camera.position + look_dir * distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -952,15 +1011,15 @@ auto Application::run() -> void
|
|||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto record_scene = [&](VulkanRenderer::GL &gl) {
|
auto record_scene { [&](VulkanRenderer::GL &gl) {
|
||||||
GZoneScopedN("Render");
|
GZoneScopedN("Render");
|
||||||
auto view { smath::matrix_look_at(
|
auto view { smath::matrix_look_at(
|
||||||
m_camera.position, m_camera.target, m_camera.up) };
|
m_camera.position, m_camera.target, m_camera.up) };
|
||||||
auto const draw_extent = m_renderer->draw_extent();
|
auto const draw_extent { m_renderer->draw_extent() };
|
||||||
auto const aspect = draw_extent.height == 0
|
auto const aspect { draw_extent.height == 0
|
||||||
? 1.0f
|
? 1.0f
|
||||||
: static_cast<float>(draw_extent.width)
|
: static_cast<float>(draw_extent.width)
|
||||||
/ static_cast<float>(draw_extent.height);
|
/ static_cast<float>(draw_extent.height) };
|
||||||
auto projection { smath::matrix_perspective(
|
auto projection { smath::matrix_perspective(
|
||||||
m_camera.fovy, aspect, 0.1f, 10000.0f) };
|
m_camera.fovy, aspect, 0.1f, 10000.0f) };
|
||||||
projection[1][1] *= -1;
|
projection[1][1] *= -1;
|
||||||
@@ -976,7 +1035,7 @@ auto Application::run() -> void
|
|||||||
gl.set_texture();
|
gl.set_texture();
|
||||||
auto const &meshes { m_test_meshes };
|
auto const &meshes { m_test_meshes };
|
||||||
if (meshes.size() > 2 && !meshes[2]->surfaces.empty()) {
|
if (meshes.size() > 2 && !meshes[2]->surfaces.empty()) {
|
||||||
auto const &surface = meshes[2]->surfaces[0];
|
auto const &surface { meshes[2]->surfaces[0] };
|
||||||
gl.draw_mesh(meshes[2]->mesh_buffers,
|
gl.draw_mesh(meshes[2]->mesh_buffers,
|
||||||
view_projection
|
view_projection
|
||||||
* smath::translate(smath::Vec3 { 0.0f, 0.0f, -5.0f }),
|
* smath::translate(smath::Vec3 { 0.0f, 0.0f, -5.0f }),
|
||||||
@@ -1022,7 +1081,48 @@ auto Application::run() -> void
|
|||||||
if (m_openxr && m_openxr->hand_tracking_supported) {
|
if (m_openxr && m_openxr->hand_tracking_supported) {
|
||||||
render_hands(gl, view_projection);
|
render_hands(gl, view_projection);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
if (m_wayland) {
|
||||||
|
auto const wayland_draw_extent { m_renderer->draw_extent() };
|
||||||
|
auto const draw_width { static_cast<float>(
|
||||||
|
wayland_draw_extent.width) };
|
||||||
|
auto const draw_height { static_cast<float>(
|
||||||
|
wayland_draw_extent.height) };
|
||||||
|
if (draw_width > 0.0f && draw_height > 0.0f) {
|
||||||
|
gl.set_transform(smath::Mat4::identity());
|
||||||
|
gl.set_culling(false);
|
||||||
|
gl.use_pipeline(m_renderer->wayland_pipeline());
|
||||||
|
for (auto *surface : m_wayland->surfaces()) {
|
||||||
|
auto buffer { surface->current_buffer() };
|
||||||
|
if (!buffer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto texture { make_shm_texture(*buffer) };
|
||||||
|
if (!texture) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto const width { static_cast<float>(buffer->width) };
|
||||||
|
auto const height { static_cast<float>(
|
||||||
|
buffer->height) };
|
||||||
|
auto const size { smath::Vec2 {
|
||||||
|
(width / draw_width) * 2.0f,
|
||||||
|
(height / draw_height) * 2.0f,
|
||||||
|
} };
|
||||||
|
auto const pos { smath::Vec2 {
|
||||||
|
-1.0f, 1.0f - size.y() } };
|
||||||
|
auto image { m_renderer->create_image(
|
||||||
|
*texture, vk::ImageUsageFlagBits::eSampled) };
|
||||||
|
gl.set_texture(&image);
|
||||||
|
gl.draw_rectangle(pos, size);
|
||||||
|
gl.flush();
|
||||||
|
gl.set_texture(std::nullopt);
|
||||||
|
m_renderer->destroy_image_later(image);
|
||||||
|
}
|
||||||
|
gl.use_pipeline(m_renderer->mesh_pipeline());
|
||||||
|
gl.set_culling(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} };
|
||||||
|
|
||||||
if (xr_active) {
|
if (xr_active) {
|
||||||
if (m_openxr && m_openxr->session_running) {
|
if (m_openxr && m_openxr->session_running) {
|
||||||
@@ -1089,7 +1189,7 @@ auto Application::shutdown_input() -> void
|
|||||||
|
|
||||||
auto Application::init_openxr() -> void
|
auto Application::init_openxr() -> void
|
||||||
{
|
{
|
||||||
if (auto const *no_xr = getenv("LUNAR_NO_XR"); no_xr && *no_xr) {
|
if (auto const *no_xr { getenv("LUNAR_NO_XR") }; no_xr && *no_xr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_openxr = std::make_unique<OpenXrState>();
|
m_openxr = std::make_unique<OpenXrState>();
|
||||||
@@ -1551,7 +1651,8 @@ auto Application::init_openxr_session() -> void
|
|||||||
};
|
};
|
||||||
m_openxr->color_format = formats.front();
|
m_openxr->color_format = formats.front();
|
||||||
for (auto const preferred : preferred_formats) {
|
for (auto const preferred : preferred_formats) {
|
||||||
auto const found = std::find(formats.begin(), formats.end(), preferred);
|
auto const found { std::find(
|
||||||
|
formats.begin(), formats.end(), preferred) };
|
||||||
if (found != formats.end()) {
|
if (found != formats.end()) {
|
||||||
m_openxr->color_format = *found;
|
m_openxr->color_format = *found;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <SDL3/SDL_video.h>
|
#include <SDL3/SDL_video.h>
|
||||||
@@ -19,6 +18,7 @@
|
|||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "Skybox.h"
|
#include "Skybox.h"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
#include "wayland/Display.h"
|
||||||
|
|
||||||
struct libinput;
|
struct libinput;
|
||||||
struct libinput_event_keyboard;
|
struct libinput_event_keyboard;
|
||||||
@@ -29,6 +29,9 @@ namespace Lunar {
|
|||||||
|
|
||||||
struct VulkanRenderer;
|
struct VulkanRenderer;
|
||||||
struct OpenXrState;
|
struct OpenXrState;
|
||||||
|
namespace Wayland {
|
||||||
|
struct WaylandServer;
|
||||||
|
}
|
||||||
|
|
||||||
struct Application {
|
struct Application {
|
||||||
auto run() -> void;
|
auto run() -> void;
|
||||||
@@ -55,6 +58,7 @@ private:
|
|||||||
|
|
||||||
auto init_input() -> void;
|
auto init_input() -> void;
|
||||||
auto init_test_meshes() -> void;
|
auto init_test_meshes() -> void;
|
||||||
|
auto init_wayland() -> void;
|
||||||
auto asset_directory() -> std::filesystem::path;
|
auto asset_directory() -> std::filesystem::path;
|
||||||
auto shutdown_input() -> void;
|
auto shutdown_input() -> void;
|
||||||
auto process_libinput_events() -> void;
|
auto process_libinput_events() -> void;
|
||||||
@@ -90,6 +94,7 @@ private:
|
|||||||
libinput *m_libinput { nullptr };
|
libinput *m_libinput { nullptr };
|
||||||
|
|
||||||
std::unique_ptr<OpenXrState> m_openxr {};
|
std::unique_ptr<OpenXrState> m_openxr {};
|
||||||
|
std::unique_ptr<Wayland::WaylandServer> m_wayland {};
|
||||||
|
|
||||||
bool m_running { true };
|
bool m_running { true };
|
||||||
bool m_mouse_captured { false };
|
bool m_mouse_captured { false };
|
||||||
|
|||||||
@@ -76,4 +76,13 @@ CPUTexture::CPUTexture(std::filesystem::path const &path)
|
|||||||
stbi_image_free(data);
|
stbi_image_free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CPUTexture::CPUTexture(std::vector<uint8_t> pixels, uint32_t width,
|
||||||
|
uint32_t height, vk::Format format)
|
||||||
|
: pixels(std::move(pixels))
|
||||||
|
, width(width)
|
||||||
|
, height(height)
|
||||||
|
, format(format)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Lunar
|
} // namespace Lunar
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ struct CPUTexture {
|
|||||||
vk::Format format { vk::Format::eR8G8B8A8Unorm };
|
vk::Format format { vk::Format::eR8G8B8A8Unorm };
|
||||||
|
|
||||||
explicit CPUTexture(std::filesystem::path const &path);
|
explicit CPUTexture(std::filesystem::path const &path);
|
||||||
|
CPUTexture(std::vector<uint8_t> pixels, uint32_t width, uint32_t height,
|
||||||
|
vk::Format format);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Lunar
|
} // namespace Lunar
|
||||||
|
|||||||
@@ -62,10 +62,10 @@ auto VulkanRenderer::GL::begin_drawing(vk::CommandBuffer cmd,
|
|||||||
m_current_uv = { 0.0f, 0.0f };
|
m_current_uv = { 0.0f, 0.0f };
|
||||||
m_bound_texture = &m_renderer.m_vk.error_image;
|
m_bound_texture = &m_renderer.m_vk.error_image;
|
||||||
|
|
||||||
auto const extent = vk::Extent2D {
|
auto const extent { vk::Extent2D {
|
||||||
m_color_target->extent.width,
|
m_color_target->extent.width,
|
||||||
m_color_target->extent.height,
|
m_color_target->extent.height,
|
||||||
};
|
} };
|
||||||
|
|
||||||
vk::RenderingAttachmentInfo color_att {};
|
vk::RenderingAttachmentInfo color_att {};
|
||||||
vk::ClearValue clear {};
|
vk::ClearValue clear {};
|
||||||
@@ -189,7 +189,8 @@ auto VulkanRenderer::GL::set_culling(bool enabled) -> void
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_active_pipeline == &m_renderer.m_vk.mesh_pipeline
|
if (m_active_pipeline == &m_renderer.m_vk.mesh_pipeline
|
||||||
|| m_active_pipeline == &m_renderer.m_vk.mesh_pipeline_culled) {
|
|| m_active_pipeline == &m_renderer.m_vk.mesh_pipeline_culled
|
||||||
|
|| m_active_pipeline == &m_renderer.m_vk.wayland_pipeline) {
|
||||||
m_active_pipeline = enabled ? &m_renderer.m_vk.mesh_pipeline_culled
|
m_active_pipeline = enabled ? &m_renderer.m_vk.mesh_pipeline_culled
|
||||||
: &m_renderer.m_vk.mesh_pipeline;
|
: &m_renderer.m_vk.mesh_pipeline;
|
||||||
} else if (m_active_pipeline == &m_renderer.m_vk.triangle_pipeline
|
} else if (m_active_pipeline == &m_renderer.m_vk.triangle_pipeline
|
||||||
@@ -206,7 +207,7 @@ auto VulkanRenderer::GL::end() -> void
|
|||||||
if (!m_inside_primitive)
|
if (!m_inside_primitive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto const count = m_vertices.size() - m_primitive_start;
|
auto const count { m_vertices.size() - m_primitive_start };
|
||||||
emit_indices(m_primitive_start, count);
|
emit_indices(m_primitive_start, count);
|
||||||
m_inside_primitive = false;
|
m_inside_primitive = false;
|
||||||
}
|
}
|
||||||
@@ -220,8 +221,8 @@ auto VulkanRenderer::GL::flush() -> void
|
|||||||
auto const index_data_size { m_indices.size() * sizeof(uint32_t) };
|
auto const index_data_size { m_indices.size() * sizeof(uint32_t) };
|
||||||
auto const staging_size { vertex_data_size + index_data_size };
|
auto const staging_size { vertex_data_size + index_data_size };
|
||||||
|
|
||||||
auto staging = m_renderer.create_buffer(staging_size,
|
auto staging { m_renderer.create_buffer(staging_size,
|
||||||
vk::BufferUsageFlagBits::eTransferSrc, VMA_MEMORY_USAGE_CPU_ONLY);
|
vk::BufferUsageFlagBits::eTransferSrc, VMA_MEMORY_USAGE_CPU_ONLY) };
|
||||||
|
|
||||||
void *staging_dst = staging.info.pMappedData;
|
void *staging_dst = staging.info.pMappedData;
|
||||||
bool staging_mapped_here { false };
|
bool staging_mapped_here { false };
|
||||||
@@ -273,7 +274,8 @@ auto VulkanRenderer::GL::flush() -> void
|
|||||||
bind_pipeline_if_needed();
|
bind_pipeline_if_needed();
|
||||||
|
|
||||||
if (m_active_pipeline == &m_renderer.m_vk.mesh_pipeline
|
if (m_active_pipeline == &m_renderer.m_vk.mesh_pipeline
|
||||||
|| m_active_pipeline == &m_renderer.m_vk.mesh_pipeline_culled) {
|
|| m_active_pipeline == &m_renderer.m_vk.mesh_pipeline_culled
|
||||||
|
|| m_active_pipeline == &m_renderer.m_vk.wayland_pipeline) {
|
||||||
auto const image_set {
|
auto const image_set {
|
||||||
m_renderer.m_vk.get_current_frame().frame_descriptors.allocate(
|
m_renderer.m_vk.get_current_frame().frame_descriptors.allocate(
|
||||||
m_renderer.m_logger, m_renderer.m_vkb.dev.device,
|
m_renderer.m_logger, m_renderer.m_vkb.dev.device,
|
||||||
@@ -327,6 +329,8 @@ auto VulkanRenderer::GL::use_pipeline(Pipeline &pipeline) -> void
|
|||||||
resolved_pipeline = m_culling_enabled
|
resolved_pipeline = m_culling_enabled
|
||||||
? &m_renderer.m_vk.mesh_pipeline_culled
|
? &m_renderer.m_vk.mesh_pipeline_culled
|
||||||
: &m_renderer.m_vk.mesh_pipeline;
|
: &m_renderer.m_vk.mesh_pipeline;
|
||||||
|
} else if (&pipeline == &m_renderer.m_vk.wayland_pipeline) {
|
||||||
|
resolved_pipeline = &m_renderer.m_vk.wayland_pipeline;
|
||||||
} else if (&pipeline == &m_renderer.m_vk.triangle_pipeline
|
} else if (&pipeline == &m_renderer.m_vk.triangle_pipeline
|
||||||
|| &pipeline == &m_renderer.m_vk.triangle_pipeline_culled) {
|
|| &pipeline == &m_renderer.m_vk.triangle_pipeline_culled) {
|
||||||
resolved_pipeline = m_culling_enabled
|
resolved_pipeline = m_culling_enabled
|
||||||
@@ -369,23 +373,23 @@ auto VulkanRenderer::GL::pop_transform() -> void
|
|||||||
auto VulkanRenderer::GL::draw_rectangle(smath::Vec2 pos, smath::Vec2 size,
|
auto VulkanRenderer::GL::draw_rectangle(smath::Vec2 pos, smath::Vec2 size,
|
||||||
smath::Vec4 rect_color, float rotation) -> void
|
smath::Vec4 rect_color, float rotation) -> void
|
||||||
{
|
{
|
||||||
auto const half_size = size * 0.5f;
|
auto const half_size { size * 0.5f };
|
||||||
auto const center = pos + half_size;
|
auto const center { pos + half_size };
|
||||||
|
|
||||||
auto rotate = [&](smath::Vec2 const &p) {
|
auto rotate { [&](smath::Vec2 const &p) {
|
||||||
float const c = std::cos(rotation);
|
float const c = std::cos(rotation);
|
||||||
float const s = std::sin(rotation);
|
float const s = std::sin(rotation);
|
||||||
return smath::Vec2 { c * p.x() - s * p.y(), s * p.x() + c * p.y() };
|
return smath::Vec2 { c * p.x() - s * p.y(), s * p.x() + c * p.y() };
|
||||||
};
|
} };
|
||||||
|
|
||||||
auto const br
|
auto const br { center
|
||||||
= center + rotate(smath::Vec2 { half_size.x(), -half_size.y() });
|
+ rotate(smath::Vec2 { half_size.x(), -half_size.y() }) };
|
||||||
auto const tr
|
auto const tr { center
|
||||||
= center + rotate(smath::Vec2 { half_size.x(), half_size.y() });
|
+ rotate(smath::Vec2 { half_size.x(), half_size.y() }) };
|
||||||
auto const bl
|
auto const bl { center
|
||||||
= center + rotate(smath::Vec2 { -half_size.x(), -half_size.y() });
|
+ rotate(smath::Vec2 { -half_size.x(), -half_size.y() }) };
|
||||||
auto const tl
|
auto const tl { center
|
||||||
= center + rotate(smath::Vec2 { -half_size.x(), half_size.y() });
|
+ rotate(smath::Vec2 { -half_size.x(), half_size.y() }) };
|
||||||
|
|
||||||
begin(GeometryKind::Quads);
|
begin(GeometryKind::Quads);
|
||||||
|
|
||||||
@@ -710,6 +714,7 @@ VulkanRenderer::~VulkanRenderer()
|
|||||||
m_vk.triangle_pipeline_culled.reset();
|
m_vk.triangle_pipeline_culled.reset();
|
||||||
m_vk.mesh_pipeline.reset();
|
m_vk.mesh_pipeline.reset();
|
||||||
m_vk.mesh_pipeline_culled.reset();
|
m_vk.mesh_pipeline_culled.reset();
|
||||||
|
m_vk.wayland_pipeline.reset();
|
||||||
m_vk.default_sampler_linear.reset();
|
m_vk.default_sampler_linear.reset();
|
||||||
m_vk.default_sampler_nearest.reset();
|
m_vk.default_sampler_nearest.reset();
|
||||||
|
|
||||||
@@ -783,7 +788,7 @@ auto VulkanRenderer::set_antialiasing_immediate(AntiAliasingKind kind) -> void
|
|||||||
|
|
||||||
auto VulkanRenderer::apply_antialiasing(AntiAliasingKind kind) -> void
|
auto VulkanRenderer::apply_antialiasing(AntiAliasingKind kind) -> void
|
||||||
{
|
{
|
||||||
auto requested_samples = [&](AntiAliasingKind aa) {
|
auto requested_samples { [&](AntiAliasingKind aa) {
|
||||||
switch (aa) {
|
switch (aa) {
|
||||||
case AntiAliasingKind::NONE:
|
case AntiAliasingKind::NONE:
|
||||||
return vk::SampleCountFlagBits::e1;
|
return vk::SampleCountFlagBits::e1;
|
||||||
@@ -795,14 +800,14 @@ auto VulkanRenderer::apply_antialiasing(AntiAliasingKind kind) -> void
|
|||||||
return vk::SampleCountFlagBits::e8;
|
return vk::SampleCountFlagBits::e8;
|
||||||
}
|
}
|
||||||
return vk::SampleCountFlagBits::e1;
|
return vk::SampleCountFlagBits::e1;
|
||||||
}(kind);
|
}(kind) };
|
||||||
|
|
||||||
auto best_supported = [&](vk::SampleCountFlagBits requested) {
|
auto best_supported { [&](vk::SampleCountFlagBits requested) {
|
||||||
auto const supported = m_vk.supported_framebuffer_samples;
|
auto const supported { m_vk.supported_framebuffer_samples };
|
||||||
|
|
||||||
auto pick_if_supported = [&](vk::SampleCountFlagBits candidate) {
|
auto pick_if_supported { [&](vk::SampleCountFlagBits candidate) {
|
||||||
return (supported & candidate) == candidate;
|
return (supported & candidate) == candidate;
|
||||||
};
|
} };
|
||||||
|
|
||||||
if (requested >= vk::SampleCountFlagBits::e64
|
if (requested >= vk::SampleCountFlagBits::e64
|
||||||
&& pick_if_supported(vk::SampleCountFlagBits::e64)) {
|
&& pick_if_supported(vk::SampleCountFlagBits::e64)) {
|
||||||
@@ -829,9 +834,9 @@ auto VulkanRenderer::apply_antialiasing(AntiAliasingKind kind) -> void
|
|||||||
return vk::SampleCountFlagBits::e2;
|
return vk::SampleCountFlagBits::e2;
|
||||||
}
|
}
|
||||||
return vk::SampleCountFlagBits::e1;
|
return vk::SampleCountFlagBits::e1;
|
||||||
}(requested_samples);
|
}(requested_samples) };
|
||||||
|
|
||||||
auto kind_for_samples = [](vk::SampleCountFlagBits samples) {
|
auto kind_for_samples { [](vk::SampleCountFlagBits samples) {
|
||||||
switch (samples) {
|
switch (samples) {
|
||||||
case vk::SampleCountFlagBits::e2:
|
case vk::SampleCountFlagBits::e2:
|
||||||
return AntiAliasingKind::MSAA_2X;
|
return AntiAliasingKind::MSAA_2X;
|
||||||
@@ -842,9 +847,9 @@ auto VulkanRenderer::apply_antialiasing(AntiAliasingKind kind) -> void
|
|||||||
default:
|
default:
|
||||||
return AntiAliasingKind::NONE;
|
return AntiAliasingKind::NONE;
|
||||||
}
|
}
|
||||||
};
|
} };
|
||||||
|
|
||||||
auto const effective_kind = kind_for_samples(best_supported);
|
auto const effective_kind { kind_for_samples(best_supported) };
|
||||||
if (m_vk.antialiasing_kind == effective_kind
|
if (m_vk.antialiasing_kind == effective_kind
|
||||||
&& m_vk.msaa_samples == best_supported) {
|
&& m_vk.msaa_samples == best_supported) {
|
||||||
return;
|
return;
|
||||||
@@ -933,7 +938,7 @@ auto VulkanRenderer::immediate_submit(
|
|||||||
|
|
||||||
auto VulkanRenderer::setup_kms_surface() -> void
|
auto VulkanRenderer::setup_kms_surface() -> void
|
||||||
{
|
{
|
||||||
auto const devices = m_instance.enumeratePhysicalDevices();
|
auto const devices { m_instance.enumeratePhysicalDevices() };
|
||||||
if (devices.empty()) {
|
if (devices.empty()) {
|
||||||
m_logger.err("No Vulkan physical devices available for KMS");
|
m_logger.err("No Vulkan physical devices available for KMS");
|
||||||
throw std::runtime_error("App init fail");
|
throw std::runtime_error("App init fail");
|
||||||
@@ -942,10 +947,10 @@ auto VulkanRenderer::setup_kms_surface() -> void
|
|||||||
m_logger.info("Found {} Vulkan physical device(s)", devices.size());
|
m_logger.info("Found {} Vulkan physical device(s)", devices.size());
|
||||||
|
|
||||||
for (auto const &device : devices) {
|
for (auto const &device : devices) {
|
||||||
auto const props = device.getProperties();
|
auto const props { device.getProperties() };
|
||||||
m_logger.info("Checking device: {}", std::string(props.deviceName));
|
m_logger.info("Checking device: {}", std::string(props.deviceName));
|
||||||
|
|
||||||
auto const displays = device.getDisplayPropertiesKHR();
|
auto const displays { device.getDisplayPropertiesKHR() };
|
||||||
if (displays.empty()) {
|
if (displays.empty()) {
|
||||||
m_logger.info(" Device has no display properties");
|
m_logger.info(" Device has no display properties");
|
||||||
continue;
|
continue;
|
||||||
@@ -968,24 +973,24 @@ auto VulkanRenderer::setup_kms_surface() -> void
|
|||||||
|
|
||||||
m_logger.info(" Display has {} mode(s)", modes.size());
|
m_logger.info(" Display has {} mode(s)", modes.size());
|
||||||
|
|
||||||
auto const best_mode_it = std::max_element(modes.begin(),
|
auto const best_mode_it { std::max_element(modes.begin(),
|
||||||
modes.end(), [](auto const &lhs, auto const &rhs) {
|
modes.end(), [](auto const &lhs, auto const &rhs) {
|
||||||
auto const lhs_extent = lhs.parameters.visibleRegion;
|
auto const lhs_extent { lhs.parameters.visibleRegion };
|
||||||
auto const rhs_extent = rhs.parameters.visibleRegion;
|
auto const rhs_extent { rhs.parameters.visibleRegion };
|
||||||
auto const lhs_area
|
auto const lhs_area { static_cast<uint64_t>(
|
||||||
= static_cast<uint64_t>(lhs_extent.width)
|
lhs_extent.width)
|
||||||
* static_cast<uint64_t>(lhs_extent.height);
|
* static_cast<uint64_t>(lhs_extent.height) };
|
||||||
auto const rhs_area
|
auto const rhs_area { static_cast<uint64_t>(
|
||||||
= static_cast<uint64_t>(rhs_extent.width)
|
rhs_extent.width)
|
||||||
* static_cast<uint64_t>(rhs_extent.height);
|
* static_cast<uint64_t>(rhs_extent.height) };
|
||||||
if (lhs_area == rhs_area) {
|
if (lhs_area == rhs_area) {
|
||||||
return lhs.parameters.refreshRate
|
return lhs.parameters.refreshRate
|
||||||
< rhs.parameters.refreshRate;
|
< rhs.parameters.refreshRate;
|
||||||
}
|
}
|
||||||
return lhs_area < rhs_area;
|
return lhs_area < rhs_area;
|
||||||
});
|
}) };
|
||||||
|
|
||||||
auto const planes = device.getDisplayPlanePropertiesKHR();
|
auto const planes { device.getDisplayPlanePropertiesKHR() };
|
||||||
std::optional<uint32_t> plane_index;
|
std::optional<uint32_t> plane_index;
|
||||||
uint32_t plane_stack_index { 0 };
|
uint32_t plane_stack_index { 0 };
|
||||||
for (uint32_t i = 0; i < planes.size(); ++i) {
|
for (uint32_t i = 0; i < planes.size(); ++i) {
|
||||||
@@ -1009,7 +1014,7 @@ auto VulkanRenderer::setup_kms_surface() -> void
|
|||||||
m_logger.info(
|
m_logger.info(
|
||||||
" Found suitable display on plane {}", *plane_index);
|
" Found suitable display on plane {}", *plane_index);
|
||||||
|
|
||||||
auto const extent = best_mode_it->parameters.visibleRegion;
|
auto const extent { best_mode_it->parameters.visibleRegion };
|
||||||
KmsState state {};
|
KmsState state {};
|
||||||
state.display = display_props.display;
|
state.display = display_props.display;
|
||||||
state.mode = best_mode_it->displayMode;
|
state.mode = best_mode_it->displayMode;
|
||||||
@@ -1176,7 +1181,7 @@ auto VulkanRenderer::vk_init() -> void
|
|||||||
m_logger.warn("KMS display is not on the selected physical device");
|
m_logger.warn("KMS display is not on the selected physical device");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const props = m_physical_device.getProperties();
|
auto const props { m_physical_device.getProperties() };
|
||||||
m_vk.supported_framebuffer_samples
|
m_vk.supported_framebuffer_samples
|
||||||
= props.limits.framebufferColorSampleCounts
|
= props.limits.framebufferColorSampleCounts
|
||||||
& props.limits.framebufferDepthSampleCounts;
|
& props.limits.framebufferDepthSampleCounts;
|
||||||
@@ -1335,10 +1340,10 @@ auto VulkanRenderer::triangle_pipeline_init() -> void
|
|||||||
uint8_t triangle_vert_shader_data[] {
|
uint8_t triangle_vert_shader_data[] {
|
||||||
#embed "triangle_vert.spv"
|
#embed "triangle_vert.spv"
|
||||||
};
|
};
|
||||||
auto triangle_vert_shader = vkutil::load_shader_module(
|
auto triangle_vert_shader { vkutil::load_shader_module(
|
||||||
std::span<uint8_t>(
|
std::span<uint8_t>(
|
||||||
triangle_vert_shader_data, sizeof(triangle_vert_shader_data)),
|
triangle_vert_shader_data, sizeof(triangle_vert_shader_data)),
|
||||||
m_device);
|
m_device) };
|
||||||
if (!triangle_vert_shader) {
|
if (!triangle_vert_shader) {
|
||||||
m_logger.err("Failed to load triangle vert shader");
|
m_logger.err("Failed to load triangle vert shader");
|
||||||
}
|
}
|
||||||
@@ -1346,10 +1351,10 @@ auto VulkanRenderer::triangle_pipeline_init() -> void
|
|||||||
uint8_t triangle_frag_shader_data[] {
|
uint8_t triangle_frag_shader_data[] {
|
||||||
#embed "triangle_frag.spv"
|
#embed "triangle_frag.spv"
|
||||||
};
|
};
|
||||||
auto triangle_frag_shader = vkutil::load_shader_module(
|
auto triangle_frag_shader { vkutil::load_shader_module(
|
||||||
std::span<uint8_t>(
|
std::span<uint8_t>(
|
||||||
triangle_frag_shader_data, sizeof(triangle_frag_shader_data)),
|
triangle_frag_shader_data, sizeof(triangle_frag_shader_data)),
|
||||||
m_device);
|
m_device) };
|
||||||
if (!triangle_frag_shader) {
|
if (!triangle_frag_shader) {
|
||||||
m_logger.err("Failed to load triangle frag shader");
|
m_logger.err("Failed to load triangle frag shader");
|
||||||
}
|
}
|
||||||
@@ -1399,10 +1404,10 @@ auto VulkanRenderer::mesh_pipeline_init() -> void
|
|||||||
uint8_t triangle_vert_shader_data[] {
|
uint8_t triangle_vert_shader_data[] {
|
||||||
#embed "triangle_mesh_vert.spv"
|
#embed "triangle_mesh_vert.spv"
|
||||||
};
|
};
|
||||||
auto triangle_vert_shader = vkutil::load_shader_module(
|
auto triangle_vert_shader { vkutil::load_shader_module(
|
||||||
std::span<uint8_t>(
|
std::span<uint8_t>(
|
||||||
triangle_vert_shader_data, sizeof(triangle_vert_shader_data)),
|
triangle_vert_shader_data, sizeof(triangle_vert_shader_data)),
|
||||||
m_device);
|
m_device) };
|
||||||
if (!triangle_vert_shader) {
|
if (!triangle_vert_shader) {
|
||||||
m_logger.err("Failed to load triangle vert shader");
|
m_logger.err("Failed to load triangle vert shader");
|
||||||
}
|
}
|
||||||
@@ -1410,10 +1415,10 @@ auto VulkanRenderer::mesh_pipeline_init() -> void
|
|||||||
uint8_t triangle_frag_shader_data[] {
|
uint8_t triangle_frag_shader_data[] {
|
||||||
#embed "tex_image_frag.spv"
|
#embed "tex_image_frag.spv"
|
||||||
};
|
};
|
||||||
auto triangle_frag_shader = vkutil::load_shader_module(
|
auto triangle_frag_shader { vkutil::load_shader_module(
|
||||||
std::span<uint8_t>(
|
std::span<uint8_t>(
|
||||||
triangle_frag_shader_data, sizeof(triangle_frag_shader_data)),
|
triangle_frag_shader_data, sizeof(triangle_frag_shader_data)),
|
||||||
m_device);
|
m_device) };
|
||||||
if (!triangle_frag_shader) {
|
if (!triangle_frag_shader) {
|
||||||
m_logger.err("Failed to load triangle frag shader");
|
m_logger.err("Failed to load triangle frag shader");
|
||||||
}
|
}
|
||||||
@@ -1446,6 +1451,24 @@ auto VulkanRenderer::mesh_pipeline_init() -> void
|
|||||||
.set_depth_format(
|
.set_depth_format(
|
||||||
static_cast<VkFormat>(m_vk.depth_image.format));
|
static_cast<VkFormat>(m_vk.depth_image.format));
|
||||||
});
|
});
|
||||||
|
m_vk.wayland_pipeline
|
||||||
|
= builder.build_graphics([&](GraphicsPipelineBuilder &pipeline_builder)
|
||||||
|
-> GraphicsPipelineBuilder & {
|
||||||
|
return pipeline_builder
|
||||||
|
.set_shaders(
|
||||||
|
triangle_vert_shader.get(), triangle_frag_shader.get())
|
||||||
|
.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
|
||||||
|
.set_polygon_mode(VK_POLYGON_MODE_FILL)
|
||||||
|
.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE)
|
||||||
|
.set_multisampling(
|
||||||
|
static_cast<VkSampleCountFlagBits>(m_vk.msaa_samples))
|
||||||
|
.enable_blending_alpha_blend()
|
||||||
|
.disable_depth_testing()
|
||||||
|
.set_color_attachment_format(
|
||||||
|
static_cast<VkFormat>(m_vk.draw_image.format))
|
||||||
|
.set_depth_format(
|
||||||
|
static_cast<VkFormat>(m_vk.depth_image.format));
|
||||||
|
});
|
||||||
m_vk.mesh_pipeline_culled
|
m_vk.mesh_pipeline_culled
|
||||||
= builder.build_graphics([&](GraphicsPipelineBuilder &pipeline_builder)
|
= builder.build_graphics([&](GraphicsPipelineBuilder &pipeline_builder)
|
||||||
-> GraphicsPipelineBuilder & {
|
-> GraphicsPipelineBuilder & {
|
||||||
@@ -1574,21 +1597,21 @@ auto VulkanRenderer::default_data_init() -> void
|
|||||||
|
|
||||||
{
|
{
|
||||||
// Solid color images
|
// Solid color images
|
||||||
auto const white = smath::pack_unorm4x8(smath::Vec4 { 1, 1, 1, 1 });
|
auto const white { smath::pack_unorm4x8(smath::Vec4 { 1, 1, 1, 1 }) };
|
||||||
m_vk.white_image = create_image(&white, vk::Extent3D { 1, 1, 1 },
|
m_vk.white_image = create_image(&white, vk::Extent3D { 1, 1, 1 },
|
||||||
vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eSampled);
|
vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eSampled);
|
||||||
|
|
||||||
auto const black = smath::pack_unorm4x8(smath::Vec4 { 0, 0, 0, 1 });
|
auto const black { smath::pack_unorm4x8(smath::Vec4 { 0, 0, 0, 1 }) };
|
||||||
m_vk.black_image = create_image(&black, vk::Extent3D { 1, 1, 1 },
|
m_vk.black_image = create_image(&black, vk::Extent3D { 1, 1, 1 },
|
||||||
vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eSampled);
|
vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eSampled);
|
||||||
|
|
||||||
auto const gray
|
auto const gray { smath::pack_unorm4x8(
|
||||||
= smath::pack_unorm4x8(smath::Vec4 { 0.6f, 0.6f, 0.6f, 1 });
|
smath::Vec4 { 0.6f, 0.6f, 0.6f, 1 }) };
|
||||||
m_vk.gray_image = create_image(&gray, vk::Extent3D { 1, 1, 1 },
|
m_vk.gray_image = create_image(&gray, vk::Extent3D { 1, 1, 1 },
|
||||||
vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eSampled);
|
vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eSampled);
|
||||||
|
|
||||||
// Error checkerboard image
|
// Error checkerboard image
|
||||||
auto const magenta = smath::pack_unorm4x8(smath::Vec4 { 1, 0, 1, 1 });
|
auto const magenta { smath::pack_unorm4x8(smath::Vec4 { 1, 0, 1, 1 }) };
|
||||||
std::array<uint32_t, 16 * 16> checkerboard;
|
std::array<uint32_t, 16 * 16> checkerboard;
|
||||||
for (int x = 0; x < 16; x++) {
|
for (int x = 0; x < 16; x++) {
|
||||||
for (int y = 0; y < 16; y++) {
|
for (int y = 0; y < 16; y++) {
|
||||||
@@ -1632,7 +1655,7 @@ auto VulkanRenderer::render(std::function<void(GL &)> const &record) -> void
|
|||||||
|
|
||||||
process_render_commands();
|
process_render_commands();
|
||||||
|
|
||||||
auto &frame = m_vk.get_current_frame();
|
auto &frame { m_vk.get_current_frame() };
|
||||||
VK_CHECK(m_logger,
|
VK_CHECK(m_logger,
|
||||||
m_device.waitForFences(frame.render_fence.get(), true, 1'000'000'000));
|
m_device.waitForFences(frame.render_fence.get(), true, 1'000'000'000));
|
||||||
frame.deletion_queue.flush();
|
frame.deletion_queue.flush();
|
||||||
@@ -1645,8 +1668,8 @@ auto VulkanRenderer::render(std::function<void(GL &)> const &record) -> void
|
|||||||
auto raw_fence { static_cast<VkFence>(frame.render_fence.get()) };
|
auto raw_fence { static_cast<VkFence>(frame.render_fence.get()) };
|
||||||
VK_CHECK(m_logger, vkResetFences(m_vkb.dev.device, 1, &raw_fence));
|
VK_CHECK(m_logger, vkResetFences(m_vkb.dev.device, 1, &raw_fence));
|
||||||
|
|
||||||
auto const acquire_result = m_device.acquireNextImageKHR(
|
auto const acquire_result { m_device.acquireNextImageKHR(
|
||||||
m_vk.swapchain, 1'000'000'000, frame.swapchain_semaphore.get(), {});
|
m_vk.swapchain, 1'000'000'000, frame.swapchain_semaphore.get(), {}) };
|
||||||
if (acquire_result.result == vk::Result::eErrorOutOfDateKHR
|
if (acquire_result.result == vk::Result::eErrorOutOfDateKHR
|
||||||
|| acquire_result.result == vk::Result::eSuboptimalKHR) {
|
|| acquire_result.result == vk::Result::eSuboptimalKHR) {
|
||||||
if (m_use_kms) {
|
if (m_use_kms) {
|
||||||
@@ -1806,8 +1829,9 @@ auto VulkanRenderer::render(std::function<void(GL &)> const &record) -> void
|
|||||||
|
|
||||||
cmd.end();
|
cmd.end();
|
||||||
|
|
||||||
auto render_semaphore
|
auto render_semaphore {
|
||||||
= m_vk.present_semaphores.at(swapchain_image_idx).get();
|
m_vk.present_semaphores.at(swapchain_image_idx).get()
|
||||||
|
};
|
||||||
vk::PipelineStageFlags2 wait_stage
|
vk::PipelineStageFlags2 wait_stage
|
||||||
= vk::PipelineStageFlagBits2::eColorAttachmentOutput;
|
= vk::PipelineStageFlagBits2::eColorAttachmentOutput;
|
||||||
auto wait_info { vkinit::semaphore_submit_info(
|
auto wait_info { vkinit::semaphore_submit_info(
|
||||||
@@ -1826,7 +1850,7 @@ auto VulkanRenderer::render(std::function<void(GL &)> const &record) -> void
|
|||||||
present_info.setWaitSemaphores(render_semaphore);
|
present_info.setWaitSemaphores(render_semaphore);
|
||||||
present_info.setImageIndices(swapchain_image_idx);
|
present_info.setImageIndices(swapchain_image_idx);
|
||||||
|
|
||||||
auto const present_result = m_vk.graphics_queue.presentKHR(present_info);
|
auto const present_result { m_vk.graphics_queue.presentKHR(present_info) };
|
||||||
if (present_result == vk::Result::eErrorOutOfDateKHR
|
if (present_result == vk::Result::eErrorOutOfDateKHR
|
||||||
|| present_result == vk::Result::eSuboptimalKHR) {
|
|| present_result == vk::Result::eSuboptimalKHR) {
|
||||||
if (m_use_kms) {
|
if (m_use_kms) {
|
||||||
@@ -1854,7 +1878,7 @@ auto VulkanRenderer::render_to_image(vk::Image target_image,
|
|||||||
|
|
||||||
process_render_commands();
|
process_render_commands();
|
||||||
|
|
||||||
auto &frame = m_vk.get_current_frame();
|
auto &frame { m_vk.get_current_frame() };
|
||||||
VK_CHECK(m_logger,
|
VK_CHECK(m_logger,
|
||||||
m_device.waitForFences(frame.render_fence.get(), true, 1'000'000'000));
|
m_device.waitForFences(frame.render_fence.get(), true, 1'000'000'000));
|
||||||
frame.deletion_queue.flush();
|
frame.deletion_queue.flush();
|
||||||
@@ -2212,7 +2236,7 @@ auto VulkanRenderer::emit_frame_screenshot(FrameData &frame) -> void
|
|||||||
destination[i] = source[i + 2];
|
destination[i] = source[i + 2];
|
||||||
destination[i + 1] = source[i + 1];
|
destination[i + 1] = source[i + 1];
|
||||||
destination[i + 2] = source[i];
|
destination[i + 2] = source[i];
|
||||||
destination[i + 3] = source[i + 3];
|
destination[i + 3] = 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const screenshot_flags { vk::ImageUsageFlagBits::eSampled };
|
auto const screenshot_flags { vk::ImageUsageFlagBits::eSampled };
|
||||||
@@ -2483,25 +2507,29 @@ auto VulkanRenderer::create_image(void const *data, vk::Extent3D size,
|
|||||||
vk::SampleCountFlagBits::e1, mipmapped),
|
vk::SampleCountFlagBits::e1, mipmapped),
|
||||||
};
|
};
|
||||||
|
|
||||||
immediate_submit([&](vk::CommandBuffer cmd) {
|
immediate_submit(
|
||||||
vkutil::transition_image(cmd, new_image.image,
|
[&](vk::CommandBuffer cmd) {
|
||||||
vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
|
vkutil::transition_image(cmd, new_image.image,
|
||||||
|
vk::ImageLayout::eUndefined,
|
||||||
|
vk::ImageLayout::eTransferDstOptimal);
|
||||||
|
|
||||||
vk::BufferImageCopy copy_region {};
|
vk::BufferImageCopy copy_region {};
|
||||||
copy_region.imageSubresource.aspectMask
|
copy_region.imageSubresource.aspectMask
|
||||||
= vk::ImageAspectFlagBits::eColor;
|
= vk::ImageAspectFlagBits::eColor;
|
||||||
copy_region.imageSubresource.mipLevel = 0;
|
copy_region.imageSubresource.mipLevel = 0;
|
||||||
copy_region.imageSubresource.baseArrayLayer = 0;
|
copy_region.imageSubresource.baseArrayLayer = 0;
|
||||||
copy_region.imageSubresource.layerCount = 1;
|
copy_region.imageSubresource.layerCount = 1;
|
||||||
copy_region.imageExtent = size;
|
copy_region.imageExtent = size;
|
||||||
|
|
||||||
cmd.copyBufferToImage(upload_buffer.buffer, new_image.image,
|
cmd.copyBufferToImage(upload_buffer.buffer, new_image.image,
|
||||||
vk::ImageLayout::eTransferDstOptimal, copy_region);
|
vk::ImageLayout::eTransferDstOptimal, copy_region);
|
||||||
|
|
||||||
vkutil::transition_image(cmd, new_image.image,
|
vkutil::transition_image(cmd, new_image.image,
|
||||||
vk::ImageLayout::eTransferDstOptimal,
|
vk::ImageLayout::eTransferDstOptimal,
|
||||||
vk::ImageLayout::eShaderReadOnlyOptimal);
|
vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||||
});
|
},
|
||||||
|
/*flush_frame_deletion_queue=*/false,
|
||||||
|
/*clear_frame_descriptors=*/false);
|
||||||
|
|
||||||
if (mapped_here) {
|
if (mapped_here) {
|
||||||
vmaUnmapMemory(m_vk.allocator, upload_buffer.allocation);
|
vmaUnmapMemory(m_vk.allocator, upload_buffer.allocation);
|
||||||
@@ -2580,58 +2608,63 @@ auto VulkanRenderer::create_cubemap(std::span<uint8_t const> pixels,
|
|||||||
view_ci.subresourceRange.layerCount = 6;
|
view_ci.subresourceRange.layerCount = 6;
|
||||||
new_image.image_view = m_device.createImageView(view_ci);
|
new_image.image_view = m_device.createImageView(view_ci);
|
||||||
|
|
||||||
immediate_submit([&](vk::CommandBuffer cmd) {
|
immediate_submit(
|
||||||
vk::ImageMemoryBarrier to_transfer {};
|
[&](vk::CommandBuffer cmd) {
|
||||||
to_transfer.srcAccessMask = vk::AccessFlagBits::eNone;
|
vk::ImageMemoryBarrier to_transfer {};
|
||||||
to_transfer.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
|
to_transfer.srcAccessMask = vk::AccessFlagBits::eNone;
|
||||||
to_transfer.oldLayout = vk::ImageLayout::eUndefined;
|
to_transfer.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||||
to_transfer.newLayout = vk::ImageLayout::eTransferDstOptimal;
|
to_transfer.oldLayout = vk::ImageLayout::eUndefined;
|
||||||
to_transfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
to_transfer.newLayout = vk::ImageLayout::eTransferDstOptimal;
|
||||||
to_transfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
to_transfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
to_transfer.image = new_image.image;
|
to_transfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
to_transfer.subresourceRange.aspectMask
|
to_transfer.image = new_image.image;
|
||||||
= vk::ImageAspectFlagBits::eColor;
|
to_transfer.subresourceRange.aspectMask
|
||||||
to_transfer.subresourceRange.baseMipLevel = 0;
|
= vk::ImageAspectFlagBits::eColor;
|
||||||
to_transfer.subresourceRange.levelCount = 1;
|
to_transfer.subresourceRange.baseMipLevel = 0;
|
||||||
to_transfer.subresourceRange.baseArrayLayer = 0;
|
to_transfer.subresourceRange.levelCount = 1;
|
||||||
to_transfer.subresourceRange.layerCount = 6;
|
to_transfer.subresourceRange.baseArrayLayer = 0;
|
||||||
|
to_transfer.subresourceRange.layerCount = 6;
|
||||||
|
|
||||||
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
|
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
|
||||||
vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, to_transfer);
|
vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, to_transfer);
|
||||||
|
|
||||||
std::array<vk::BufferImageCopy, 6> regions {};
|
std::array<vk::BufferImageCopy, 6> regions {};
|
||||||
for (uint32_t layer = 0; layer < 6; ++layer) {
|
for (uint32_t layer = 0; layer < 6; ++layer) {
|
||||||
vk::BufferImageCopy region {};
|
vk::BufferImageCopy region {};
|
||||||
region.bufferOffset = face_bytes * layer;
|
region.bufferOffset = face_bytes * layer;
|
||||||
region.imageSubresource.aspectMask
|
region.imageSubresource.aspectMask
|
||||||
= vk::ImageAspectFlagBits::eColor;
|
= vk::ImageAspectFlagBits::eColor;
|
||||||
region.imageSubresource.mipLevel = 0;
|
region.imageSubresource.mipLevel = 0;
|
||||||
region.imageSubresource.baseArrayLayer = layer;
|
region.imageSubresource.baseArrayLayer = layer;
|
||||||
region.imageSubresource.layerCount = 1;
|
region.imageSubresource.layerCount = 1;
|
||||||
region.imageExtent = new_image.extent;
|
region.imageExtent = new_image.extent;
|
||||||
regions[layer] = region;
|
regions[layer] = region;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.copyBufferToImage(upload_buffer.buffer, new_image.image,
|
cmd.copyBufferToImage(upload_buffer.buffer, new_image.image,
|
||||||
vk::ImageLayout::eTransferDstOptimal, regions);
|
vk::ImageLayout::eTransferDstOptimal, regions);
|
||||||
|
|
||||||
vk::ImageMemoryBarrier to_read {};
|
vk::ImageMemoryBarrier to_read {};
|
||||||
to_read.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
to_read.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||||
to_read.dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
to_read.dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
||||||
to_read.oldLayout = vk::ImageLayout::eTransferDstOptimal;
|
to_read.oldLayout = vk::ImageLayout::eTransferDstOptimal;
|
||||||
to_read.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
to_read.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
||||||
to_read.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
to_read.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
to_read.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
to_read.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
to_read.image = new_image.image;
|
to_read.image = new_image.image;
|
||||||
to_read.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
|
to_read.subresourceRange.aspectMask
|
||||||
to_read.subresourceRange.baseMipLevel = 0;
|
= vk::ImageAspectFlagBits::eColor;
|
||||||
to_read.subresourceRange.levelCount = 1;
|
to_read.subresourceRange.baseMipLevel = 0;
|
||||||
to_read.subresourceRange.baseArrayLayer = 0;
|
to_read.subresourceRange.levelCount = 1;
|
||||||
to_read.subresourceRange.layerCount = 6;
|
to_read.subresourceRange.baseArrayLayer = 0;
|
||||||
|
to_read.subresourceRange.layerCount = 6;
|
||||||
|
|
||||||
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||||
vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {}, to_read);
|
vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {},
|
||||||
});
|
to_read);
|
||||||
|
},
|
||||||
|
/*flush_frame_deletion_queue=*/false,
|
||||||
|
/*clear_frame_descriptors=*/false);
|
||||||
|
|
||||||
if (mapped_here) {
|
if (mapped_here) {
|
||||||
vmaUnmapMemory(m_vk.allocator, upload_buffer.allocation);
|
vmaUnmapMemory(m_vk.allocator, upload_buffer.allocation);
|
||||||
@@ -2650,6 +2683,12 @@ auto VulkanRenderer::destroy_image(AllocatedImage const &img) -> void
|
|||||||
m_vk.allocator, static_cast<VkImage>(img.image), img.allocation);
|
m_vk.allocator, static_cast<VkImage>(img.image), img.allocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto VulkanRenderer::destroy_image_later(AllocatedImage img) -> void
|
||||||
|
{
|
||||||
|
m_vk.get_current_frame().deletion_queue.emplace(
|
||||||
|
[this, img]() { destroy_image(img); });
|
||||||
|
}
|
||||||
|
|
||||||
auto VulkanRenderer::create_buffer(size_t alloc_size,
|
auto VulkanRenderer::create_buffer(size_t alloc_size,
|
||||||
vk::BufferUsageFlags usage, VmaMemoryUsage memory_usage) -> AllocatedBuffer
|
vk::BufferUsageFlags usage, VmaMemoryUsage memory_usage) -> AllocatedBuffer
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ struct VulkanRenderer {
|
|||||||
auto create_cubemap(std::span<uint8_t const> pixels, uint32_t face_size,
|
auto create_cubemap(std::span<uint8_t const> pixels, uint32_t face_size,
|
||||||
vk::Format format, vk::ImageUsageFlags flags) -> AllocatedImage;
|
vk::Format format, vk::ImageUsageFlags flags) -> AllocatedImage;
|
||||||
auto destroy_image(AllocatedImage const &img) -> void;
|
auto destroy_image(AllocatedImage const &img) -> void;
|
||||||
|
auto destroy_image_later(AllocatedImage img) -> void;
|
||||||
auto rectangle_mesh() const -> GPUMeshBuffers const &
|
auto rectangle_mesh() const -> GPUMeshBuffers const &
|
||||||
{
|
{
|
||||||
return m_vk.rectangle;
|
return m_vk.rectangle;
|
||||||
@@ -186,6 +187,7 @@ struct VulkanRenderer {
|
|||||||
}
|
}
|
||||||
auto draw_extent() const -> vk::Extent2D { return m_vk.draw_extent; }
|
auto draw_extent() const -> vk::Extent2D { return m_vk.draw_extent; }
|
||||||
auto mesh_pipeline() -> Pipeline & { return m_vk.mesh_pipeline; }
|
auto mesh_pipeline() -> Pipeline & { return m_vk.mesh_pipeline; }
|
||||||
|
auto wayland_pipeline() -> Pipeline & { return m_vk.wayland_pipeline; }
|
||||||
auto triangle_pipeline() -> Pipeline & { return m_vk.triangle_pipeline; }
|
auto triangle_pipeline() -> Pipeline & { return m_vk.triangle_pipeline; }
|
||||||
auto device() const -> vk::Device { return m_device; }
|
auto device() const -> vk::Device { return m_device; }
|
||||||
auto instance() const -> vk::Instance { return m_instance; }
|
auto instance() const -> vk::Instance { return m_instance; }
|
||||||
@@ -359,6 +361,7 @@ private:
|
|||||||
Pipeline triangle_pipeline_culled;
|
Pipeline triangle_pipeline_culled;
|
||||||
Pipeline mesh_pipeline;
|
Pipeline mesh_pipeline;
|
||||||
Pipeline mesh_pipeline_culled;
|
Pipeline mesh_pipeline_culled;
|
||||||
|
Pipeline wayland_pipeline;
|
||||||
|
|
||||||
GPUMeshBuffers rectangle;
|
GPUMeshBuffers rectangle;
|
||||||
|
|
||||||
|
|||||||
113
src/wayland/Client.h
Normal file
113
src/wayland/Client.h
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <format>
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
#include "Display.h"
|
||||||
|
#include "List.h"
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
|
||||||
|
struct Client {
|
||||||
|
Client(wl_client *client)
|
||||||
|
: m_client { std::move(client) }
|
||||||
|
{
|
||||||
|
assert(m_client);
|
||||||
|
}
|
||||||
|
~Client() = default;
|
||||||
|
|
||||||
|
inline auto c_ptr() const -> wl_client * { return m_client; }
|
||||||
|
|
||||||
|
static auto from_link(wl_list *link) -> Client
|
||||||
|
{
|
||||||
|
return Client { wl_client_from_link(link) };
|
||||||
|
}
|
||||||
|
|
||||||
|
auto flush() { wl_client_flush(m_client); }
|
||||||
|
|
||||||
|
auto get_display() -> Display
|
||||||
|
{
|
||||||
|
return Display { wl_client_get_display(m_client) };
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_credentials() noexcept -> std::tuple<pid_t, uid_t, gid_t>
|
||||||
|
{
|
||||||
|
std::tuple<pid_t, uid_t, gid_t> ret {};
|
||||||
|
auto &[pid, uid, gid] { ret };
|
||||||
|
wl_client_get_credentials(m_client, &pid, &uid, &gid);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_fd() noexcept -> int { return wl_client_get_fd(m_client); }
|
||||||
|
|
||||||
|
auto get_object(uint32_t id) -> std::optional<wl_resource *>
|
||||||
|
{
|
||||||
|
if (auto *res { wl_client_get_object(m_client, id) }; res != NULL) {
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto post_implementation_error(std::string_view string)
|
||||||
|
{
|
||||||
|
wl_client_post_implementation_error(
|
||||||
|
m_client, "%.*s", static_cast<int>(string.size()), string.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
auto post_implementation_error(
|
||||||
|
std::format_string<Args...> fmt, Args &&...args)
|
||||||
|
{
|
||||||
|
post_implementation_error(
|
||||||
|
std::format(fmt, std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto add_destroy_listener(wl_listener *listener)
|
||||||
|
{
|
||||||
|
wl_client_add_destroy_listener(m_client, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto add_destroy_late_listener(wl_listener *listener)
|
||||||
|
{
|
||||||
|
wl_client_add_destroy_late_listener(m_client, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_link() -> wl_list * { return wl_client_get_link(m_client); }
|
||||||
|
|
||||||
|
auto add_resource_created_listener(wl_listener *listener)
|
||||||
|
{
|
||||||
|
wl_client_add_resource_created_listener(m_client, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto for_each_resource(
|
||||||
|
std::function<wl_iterator_result(wl_resource *)> const &fn) -> void
|
||||||
|
{
|
||||||
|
wl_client_for_each_resource(
|
||||||
|
m_client,
|
||||||
|
(wl_client_for_each_resource_iterator_func_t)[](
|
||||||
|
wl_resource * res, void *user_data)
|
||||||
|
->wl_iterator_result {
|
||||||
|
auto *f { static_cast<
|
||||||
|
std::function<wl_iterator_result(wl_resource *)> *>(
|
||||||
|
user_data) };
|
||||||
|
return (*f)(res);
|
||||||
|
},
|
||||||
|
const_cast<void *>(static_cast<void const *>(&fn)));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto set_max_buffer_size(size_t max_buffer_size)
|
||||||
|
{
|
||||||
|
wl_client_set_max_buffer_size(m_client, max_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wl_client *m_client {};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
103
src/wayland/Display.h
Normal file
103
src/wayland/Display.h
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <format>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
|
||||||
|
struct Display {
|
||||||
|
Display()
|
||||||
|
: m_display(wl_display_create())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Display(wl_display *display)
|
||||||
|
: m_display { std::move(display) }
|
||||||
|
, m_should_cleanup { false }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~Display()
|
||||||
|
{
|
||||||
|
if (!m_should_cleanup)
|
||||||
|
return;
|
||||||
|
wl_display_destroy_clients(m_display);
|
||||||
|
wl_display_destroy(m_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto c_ptr() const -> wl_display * { return m_display; }
|
||||||
|
|
||||||
|
auto set_global_filter(
|
||||||
|
wl_display_global_filter_func_t filter, void *data) noexcept
|
||||||
|
{
|
||||||
|
wl_display_set_global_filter(m_display, filter, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto next_serial() noexcept -> uint32_t
|
||||||
|
{
|
||||||
|
return wl_display_next_serial(m_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto set_default_max_buffer_size(size_t max_buffer_size) noexcept
|
||||||
|
{
|
||||||
|
wl_display_set_default_max_buffer_size(m_display, max_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto add_socket_fd(int fd)
|
||||||
|
{
|
||||||
|
if (wl_display_add_socket_fd(m_display, fd) == -1) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Failed to add socket fd to Wayland display");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto add_socket(char const *name)
|
||||||
|
{
|
||||||
|
if (wl_display_add_socket(m_display, name) == -1) {
|
||||||
|
throw std::runtime_error(std::format(
|
||||||
|
"Failed to add socket `{}` to Wayland display", name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto add_protocol_logger(wl_protocol_logger_func_t func, void *user_data)
|
||||||
|
-> wl_protocol_logger *
|
||||||
|
{
|
||||||
|
if (auto *logger {
|
||||||
|
wl_display_add_protocol_logger(m_display, func, user_data) };
|
||||||
|
logger != NULL) {
|
||||||
|
return logger;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Failed to add protocol logger to Wayland display");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto add_shm_format(uint32_t format) -> uint32_t *
|
||||||
|
{
|
||||||
|
if (auto *fmt { wl_display_add_shm_format(m_display, format) };
|
||||||
|
fmt != NULL) {
|
||||||
|
return fmt;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Failed to add SHM format to Wayland display");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_client_list() -> std::vector<wl_client *>
|
||||||
|
{
|
||||||
|
std::vector<wl_client *> ret {};
|
||||||
|
auto const list { wl_display_get_client_list(m_display) };
|
||||||
|
assert(list);
|
||||||
|
wl_client *client {};
|
||||||
|
wl_client_for_each(client, list) { ret.push_back(client); }
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wl_display *m_display {};
|
||||||
|
bool m_should_cleanup { true };
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
71
src/wayland/Global.h
Normal file
71
src/wayland/Global.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
#include "Client.h"
|
||||||
|
#include "Display.h"
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
|
||||||
|
struct Global {
|
||||||
|
Global() = delete;
|
||||||
|
|
||||||
|
explicit Global(Display &display, wl_interface const *interface,
|
||||||
|
int version, void *data, wl_global_bind_func_t bind)
|
||||||
|
: m_global {
|
||||||
|
wl_global_create(display.c_ptr(), interface, version, data, bind),
|
||||||
|
}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Global(wl_global *global)
|
||||||
|
: m_global { std::move(global) }
|
||||||
|
, m_should_cleanup { false }
|
||||||
|
{
|
||||||
|
assert(m_global);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Global()
|
||||||
|
{
|
||||||
|
if (!m_should_cleanup)
|
||||||
|
return;
|
||||||
|
wl_global_destroy(m_global);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto c_ptr() const -> wl_global * { return m_global; }
|
||||||
|
|
||||||
|
auto get_name(Client &client) const -> std::optional<uint32_t>
|
||||||
|
{
|
||||||
|
if (auto const ret { wl_global_get_name(m_global, client.c_ptr()) };
|
||||||
|
ret != 0) {
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto get_version() const -> uint32_t
|
||||||
|
{
|
||||||
|
return wl_global_get_version(m_global);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto get_display() const -> Display
|
||||||
|
{
|
||||||
|
return wl_global_get_display(m_global);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto get_interface() const -> wl_interface const *
|
||||||
|
{
|
||||||
|
return wl_global_get_interface(m_global);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wl_global *m_global {};
|
||||||
|
bool m_should_cleanup { true };
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
287
src/wayland/List.h
Normal file
287
src/wayland/List.h
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<typename T, wl_list T::*Member>
|
||||||
|
constexpr std::ptrdiff_t member_offset() noexcept
|
||||||
|
{
|
||||||
|
return reinterpret_cast<std::ptrdiff_t>(
|
||||||
|
&(reinterpret_cast<T const volatile *>(0)->*Member));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, wl_list T::*Member>
|
||||||
|
inline T *container_of(wl_list *node) noexcept
|
||||||
|
{
|
||||||
|
auto *p { reinterpret_cast<std::byte *>(node)
|
||||||
|
- member_offset<T, Member>() };
|
||||||
|
return reinterpret_cast<T *>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, wl_list T::*Member>
|
||||||
|
inline T const *container_of(wl_list const *node) noexcept
|
||||||
|
{
|
||||||
|
auto *p { reinterpret_cast<std::byte const *>(node)
|
||||||
|
- member_offset<T, Member>() };
|
||||||
|
return reinterpret_cast<T const *>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template<typename T, wl_list T::*Member> struct List {
|
||||||
|
struct Iterator {
|
||||||
|
using iterator_category = std::bidirectional_iterator_tag;
|
||||||
|
using value_type = T;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = T *;
|
||||||
|
using reference = T &;
|
||||||
|
|
||||||
|
Iterator() = default;
|
||||||
|
Iterator(wl_list *cur, wl_list *head)
|
||||||
|
: m_cur(cur)
|
||||||
|
, m_head(head)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator*() const -> reference
|
||||||
|
{
|
||||||
|
return *detail::container_of<T, Member>(m_cur);
|
||||||
|
}
|
||||||
|
auto operator->() const -> pointer
|
||||||
|
{
|
||||||
|
return detail::container_of<T, Member>(m_cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator++() -> Iterator &
|
||||||
|
{
|
||||||
|
m_cur = m_cur->next;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
auto operator++(int) -> Iterator
|
||||||
|
{
|
||||||
|
auto t { *this };
|
||||||
|
++(*this);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator--() -> Iterator &
|
||||||
|
{
|
||||||
|
m_cur = (m_cur == m_head) ? m_head->prev : m_cur->prev;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
auto operator--(int) -> Iterator
|
||||||
|
{
|
||||||
|
auto t { *this };
|
||||||
|
--(*this);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend auto operator==(Iterator a, Iterator b) -> bool
|
||||||
|
{
|
||||||
|
return a.m_cur == b.m_cur;
|
||||||
|
}
|
||||||
|
friend auto operator!=(Iterator a, Iterator b) -> bool
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wl_list *m_cur { nullptr };
|
||||||
|
wl_list *m_head { nullptr };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConstIterator {
|
||||||
|
using iterator_category = std::bidirectional_iterator_tag;
|
||||||
|
using value_type = T const;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = T const *;
|
||||||
|
using reference = T const &;
|
||||||
|
|
||||||
|
ConstIterator() = default;
|
||||||
|
ConstIterator(wl_list const *cur, wl_list const *head)
|
||||||
|
: m_cur(cur)
|
||||||
|
, m_head(head)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator*() const -> reference
|
||||||
|
{
|
||||||
|
return *detail::container_of<T, Member>(m_cur);
|
||||||
|
}
|
||||||
|
auto operator->() const -> pointer
|
||||||
|
{
|
||||||
|
return detail::container_of<T, Member>(m_cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator++() -> ConstIterator &
|
||||||
|
{
|
||||||
|
m_cur = m_cur->next;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
auto operator++(int) -> ConstIterator
|
||||||
|
{
|
||||||
|
auto t { *this };
|
||||||
|
++(*this);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator--() -> ConstIterator &
|
||||||
|
{
|
||||||
|
m_cur = (m_cur == m_head) ? m_head->prev : m_cur->prev;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
auto operator--(int) -> ConstIterator
|
||||||
|
{
|
||||||
|
auto t { *this };
|
||||||
|
--(*this);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend auto operator==(ConstIterator a, ConstIterator b) -> bool
|
||||||
|
{
|
||||||
|
return a.m_cur == b.m_cur;
|
||||||
|
}
|
||||||
|
friend auto operator!=(ConstIterator a, ConstIterator b) -> bool
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wl_list const *m_cur { nullptr };
|
||||||
|
wl_list const *m_head { nullptr };
|
||||||
|
};
|
||||||
|
|
||||||
|
List()
|
||||||
|
: m_head {}
|
||||||
|
, m_external_head { nullptr }
|
||||||
|
, m_should_cleanup { true }
|
||||||
|
{
|
||||||
|
wl_list_init(&m_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit List(
|
||||||
|
wl_list *existing_head, bool should_cleanup = false, bool init = false)
|
||||||
|
: m_head {}
|
||||||
|
, m_external_head { existing_head }
|
||||||
|
, m_should_cleanup { should_cleanup }
|
||||||
|
{
|
||||||
|
if (init && m_external_head)
|
||||||
|
wl_list_init(m_external_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
~List()
|
||||||
|
{
|
||||||
|
if (!m_should_cleanup)
|
||||||
|
return;
|
||||||
|
|
||||||
|
clear();
|
||||||
|
if (auto *h { head_ptr() }; h)
|
||||||
|
wl_list_init(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
List(List const &) = delete;
|
||||||
|
auto operator=(List const &) -> List & = delete;
|
||||||
|
|
||||||
|
List(List &&other) noexcept { move_from(other); }
|
||||||
|
|
||||||
|
auto operator=(List &&other) noexcept -> List &
|
||||||
|
{
|
||||||
|
if (this != &other) {
|
||||||
|
this->~List();
|
||||||
|
move_from(other);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto c_ptr() -> wl_list * { return head_ptr(); }
|
||||||
|
inline auto c_ptr() const -> wl_list const * { return head_ptr(); }
|
||||||
|
|
||||||
|
auto empty() const noexcept -> bool { return wl_list_empty(head_ptr()); }
|
||||||
|
auto length() const noexcept -> int { return wl_list_length(head_ptr()); }
|
||||||
|
|
||||||
|
auto push_front(T *elem) noexcept -> void
|
||||||
|
{
|
||||||
|
wl_list_insert(head_ptr(), &(elem->*Member));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto push_back(T *elem) noexcept -> void
|
||||||
|
{
|
||||||
|
auto *h { head_ptr() };
|
||||||
|
wl_list_insert(h->prev, &(elem->*Member));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto remove(T *elem) noexcept -> void
|
||||||
|
{
|
||||||
|
wl_list_remove(&(elem->*Member));
|
||||||
|
wl_list_init(&(elem->*Member));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto clear() noexcept -> void
|
||||||
|
{
|
||||||
|
auto *h { head_ptr() };
|
||||||
|
while (!wl_list_empty(h)) {
|
||||||
|
auto *node { h->next };
|
||||||
|
wl_list_remove(node);
|
||||||
|
wl_list_init(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto begin() noexcept -> Iterator
|
||||||
|
{
|
||||||
|
auto *h { head_ptr() };
|
||||||
|
return Iterator(h->next, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end() noexcept -> Iterator
|
||||||
|
{
|
||||||
|
auto *h { head_ptr() };
|
||||||
|
return Iterator(h, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto begin() const noexcept -> ConstIterator
|
||||||
|
{
|
||||||
|
auto const *h { head_ptr() };
|
||||||
|
return ConstIterator(h->next, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end() const noexcept -> ConstIterator
|
||||||
|
{
|
||||||
|
auto const *h { head_ptr() };
|
||||||
|
return ConstIterator(h, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto head_ptr() noexcept -> wl_list *
|
||||||
|
{
|
||||||
|
return m_external_head ? m_external_head : &m_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto head_ptr() const noexcept -> wl_list const *
|
||||||
|
{
|
||||||
|
return m_external_head ? m_external_head : &m_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto move_from(List &other) noexcept -> void
|
||||||
|
{
|
||||||
|
m_head = other.m_head;
|
||||||
|
m_external_head = other.m_external_head;
|
||||||
|
m_should_cleanup = other.m_should_cleanup;
|
||||||
|
|
||||||
|
other.m_external_head = nullptr;
|
||||||
|
other.m_should_cleanup = false;
|
||||||
|
wl_list_init(&other.m_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wl_list m_head {};
|
||||||
|
wl_list *m_external_head { nullptr };
|
||||||
|
bool m_should_cleanup { true };
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
43
src/wayland/Region.h
Normal file
43
src/wayland/Region.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
|
||||||
|
struct Region {
|
||||||
|
struct Box {
|
||||||
|
std::int32_t x {};
|
||||||
|
std::int32_t y {};
|
||||||
|
std::int32_t width {};
|
||||||
|
std::int32_t height {};
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit Region(wl_resource *resource)
|
||||||
|
: m_resource(resource)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resource() const -> wl_resource * { return m_resource; }
|
||||||
|
|
||||||
|
auto add(std::int32_t x, std::int32_t y, std::int32_t width,
|
||||||
|
std::int32_t height) -> void
|
||||||
|
{
|
||||||
|
m_boxes.push_back(Box { x, y, width, height });
|
||||||
|
}
|
||||||
|
|
||||||
|
auto subtract(std::int32_t x, std::int32_t y, std::int32_t width,
|
||||||
|
std::int32_t height) -> void
|
||||||
|
{
|
||||||
|
m_subtract_boxes.push_back(Box { x, y, width, height });
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wl_resource *m_resource {};
|
||||||
|
std::vector<Box> m_boxes {};
|
||||||
|
std::vector<Box> m_subtract_boxes {};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
96
src/wayland/Shm.cpp
Normal file
96
src/wayland/Shm.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include "Shm.h"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstring>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
|
||||||
|
ShmPool::ShmPool(int fd, std::size_t size, Logger &logger)
|
||||||
|
: m_logger(logger)
|
||||||
|
, m_fd(fd)
|
||||||
|
, m_size(size)
|
||||||
|
{
|
||||||
|
m_data = mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
|
||||||
|
if (m_data == MAP_FAILED) {
|
||||||
|
m_data = nullptr;
|
||||||
|
m_logger.err("Failed to mmap shm pool: {}", std::strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShmPool::~ShmPool()
|
||||||
|
{
|
||||||
|
if (m_data) {
|
||||||
|
munmap(m_data, m_size);
|
||||||
|
}
|
||||||
|
if (m_fd >= 0) {
|
||||||
|
close(m_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ShmPool::resize(std::size_t new_size) -> bool
|
||||||
|
{
|
||||||
|
if (!m_data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void *new_data = mremap(m_data, m_size, new_size, MREMAP_MAYMOVE);
|
||||||
|
if (new_data == MAP_FAILED) {
|
||||||
|
m_logger.err("Failed to resize shm pool: {}", std::strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_data = new_data;
|
||||||
|
m_size = new_size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ShmPool::data() const -> std::byte *
|
||||||
|
{
|
||||||
|
return static_cast<std::byte *>(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShmBuffer::ShmBuffer(std::shared_ptr<ShmPool> pool, wl_resource *resource,
|
||||||
|
std::int32_t offset, std::int32_t width, std::int32_t height,
|
||||||
|
std::int32_t stride, std::uint32_t format)
|
||||||
|
: pool(std::move(pool))
|
||||||
|
, resource(resource)
|
||||||
|
, offset(offset)
|
||||||
|
, width(width)
|
||||||
|
, height(height)
|
||||||
|
, stride(stride)
|
||||||
|
, format(format)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ShmBuffer::data() const -> std::byte *
|
||||||
|
{
|
||||||
|
if (!pool || !pool->data()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return pool->data() + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ShmBuffer::byte_size() const -> std::size_t
|
||||||
|
{
|
||||||
|
if (height <= 0 || stride <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return static_cast<std::size_t>(height) * static_cast<std::size_t>(stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto shm_buffer_from_resource(wl_resource *resource)
|
||||||
|
-> std::shared_ptr<ShmBuffer>
|
||||||
|
{
|
||||||
|
if (!resource) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto *handle { static_cast<std::shared_ptr<ShmBuffer> *>(
|
||||||
|
wl_resource_get_user_data(resource)) };
|
||||||
|
if (!handle) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return *handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
48
src/wayland/Shm.h
Normal file
48
src/wayland/Shm.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
#include "../Logger.h"
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
|
||||||
|
struct ShmPool {
|
||||||
|
explicit ShmPool(int fd, std::size_t size, Logger &logger);
|
||||||
|
~ShmPool();
|
||||||
|
|
||||||
|
auto resize(std::size_t new_size) -> bool;
|
||||||
|
auto data() const -> std::byte *;
|
||||||
|
auto size() const -> std::size_t { return m_size; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Logger &m_logger;
|
||||||
|
int m_fd { -1 };
|
||||||
|
std::size_t m_size { 0 };
|
||||||
|
void *m_data { nullptr };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShmBuffer {
|
||||||
|
ShmBuffer(std::shared_ptr<ShmPool> pool, wl_resource *resource,
|
||||||
|
std::int32_t offset, std::int32_t width, std::int32_t height,
|
||||||
|
std::int32_t stride, std::uint32_t format);
|
||||||
|
|
||||||
|
auto data() const -> std::byte *;
|
||||||
|
auto byte_size() const -> std::size_t;
|
||||||
|
|
||||||
|
std::shared_ptr<ShmPool> pool;
|
||||||
|
wl_resource *resource {};
|
||||||
|
std::int32_t offset {};
|
||||||
|
std::int32_t width {};
|
||||||
|
std::int32_t height {};
|
||||||
|
std::int32_t stride {};
|
||||||
|
std::uint32_t format {};
|
||||||
|
};
|
||||||
|
|
||||||
|
auto shm_buffer_from_resource(wl_resource *resource)
|
||||||
|
-> std::shared_ptr<ShmBuffer>;
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
28
src/wayland/Signal.h
Normal file
28
src/wayland/Signal.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
|
||||||
|
struct Signal {
|
||||||
|
Signal(wl_signal *signal)
|
||||||
|
: m_signal { std::move(signal) }
|
||||||
|
{
|
||||||
|
assert(m_signal);
|
||||||
|
}
|
||||||
|
~Signal() = default;
|
||||||
|
|
||||||
|
inline auto c_ptr() const -> wl_signal * { return m_signal; }
|
||||||
|
|
||||||
|
template<typename T = void> auto flush(T *data)
|
||||||
|
{
|
||||||
|
wl_signal_emit_mutable(m_signal, (void *)data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wl_signal *m_signal {};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
98
src/wayland/Surface.cpp
Normal file
98
src/wayland/Surface.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#include "Surface.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "WaylandServer.h"
|
||||||
|
|
||||||
|
#include "wayland-server-protocol.h"
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
|
||||||
|
Surface::Surface(WaylandServer &server, wl_resource *resource)
|
||||||
|
: m_server(server)
|
||||||
|
, m_resource(resource)
|
||||||
|
{
|
||||||
|
m_server.register_surface(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Surface::~Surface()
|
||||||
|
{
|
||||||
|
m_server.unregister_surface(this);
|
||||||
|
for (auto *callback : m_frame_callbacks) {
|
||||||
|
if (callback) {
|
||||||
|
wl_callback_send_done(callback, m_server.now_ms());
|
||||||
|
wl_resource_destroy(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Surface::attach(
|
||||||
|
std::shared_ptr<ShmBuffer> buffer, std::int32_t x, std::int32_t y) -> void
|
||||||
|
{
|
||||||
|
m_pending_buffer = std::move(buffer);
|
||||||
|
m_pending_offset_x = x;
|
||||||
|
m_pending_offset_y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Surface::damage(std::int32_t, std::int32_t, std::int32_t, std::int32_t)
|
||||||
|
-> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Surface::damage_buffer(
|
||||||
|
std::int32_t, std::int32_t, std::int32_t, std::int32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Surface::frame(wl_resource *callback) -> void
|
||||||
|
{
|
||||||
|
if (!callback) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_frame_callbacks.push_back(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Surface::commit() -> void
|
||||||
|
{
|
||||||
|
auto previous { m_current_buffer };
|
||||||
|
m_current_buffer = m_pending_buffer;
|
||||||
|
m_pending_buffer.reset();
|
||||||
|
|
||||||
|
if (previous && previous != m_current_buffer && previous->resource) {
|
||||||
|
wl_buffer_send_release(previous->resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_frame_callbacks.empty()) {
|
||||||
|
auto callbacks { std::move(m_frame_callbacks) };
|
||||||
|
auto done_time { m_server.now_ms() };
|
||||||
|
for (auto *callback : callbacks) {
|
||||||
|
if (callback) {
|
||||||
|
wl_callback_send_done(callback, done_time);
|
||||||
|
wl_resource_destroy(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Surface::set_opaque_region(std::shared_ptr<Region> region) -> void
|
||||||
|
{
|
||||||
|
m_opaque_region = std::move(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Surface::set_input_region(std::shared_ptr<Region> region) -> void
|
||||||
|
{
|
||||||
|
m_input_region = std::move(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Surface::set_buffer_transform(std::int32_t transform) -> void
|
||||||
|
{
|
||||||
|
m_buffer_transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Surface::set_buffer_scale(std::int32_t scale) -> void
|
||||||
|
{
|
||||||
|
m_buffer_scale = std::max(1, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
53
src/wayland/Surface.h
Normal file
53
src/wayland/Surface.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
#include "Region.h"
|
||||||
|
#include "Shm.h"
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
|
||||||
|
struct WaylandServer;
|
||||||
|
|
||||||
|
struct Surface {
|
||||||
|
explicit Surface(WaylandServer &server, wl_resource *resource);
|
||||||
|
~Surface();
|
||||||
|
|
||||||
|
auto resource() const -> wl_resource * { return m_resource; }
|
||||||
|
auto current_buffer() const -> std::shared_ptr<ShmBuffer> const &
|
||||||
|
{
|
||||||
|
return m_current_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto attach(std::shared_ptr<ShmBuffer> buffer, std::int32_t x,
|
||||||
|
std::int32_t y) -> void;
|
||||||
|
auto damage(std::int32_t x, std::int32_t y, std::int32_t width,
|
||||||
|
std::int32_t height) -> void;
|
||||||
|
auto damage_buffer(std::int32_t x, std::int32_t y, std::int32_t width,
|
||||||
|
std::int32_t height) -> void;
|
||||||
|
auto frame(wl_resource *callback) -> void;
|
||||||
|
auto commit() -> void;
|
||||||
|
auto set_opaque_region(std::shared_ptr<Region> region) -> void;
|
||||||
|
auto set_input_region(std::shared_ptr<Region> region) -> void;
|
||||||
|
auto set_buffer_transform(std::int32_t transform) -> void;
|
||||||
|
auto set_buffer_scale(std::int32_t scale) -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
WaylandServer &m_server;
|
||||||
|
wl_resource *m_resource {};
|
||||||
|
std::shared_ptr<ShmBuffer> m_pending_buffer {};
|
||||||
|
std::shared_ptr<ShmBuffer> m_current_buffer {};
|
||||||
|
std::shared_ptr<Region> m_opaque_region {};
|
||||||
|
std::shared_ptr<Region> m_input_region {};
|
||||||
|
std::vector<wl_resource *> m_frame_callbacks {};
|
||||||
|
std::int32_t m_buffer_transform { 0 };
|
||||||
|
std::int32_t m_buffer_scale { 1 };
|
||||||
|
std::int32_t m_pending_offset_x { 0 };
|
||||||
|
std::int32_t m_pending_offset_y { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
81
src/wayland/WaylandServer.cpp
Normal file
81
src/wayland/WaylandServer.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include "WaylandServer.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <span>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
|
||||||
|
WaylandServer::WaylandServer(Logger &logger)
|
||||||
|
: m_logger(logger)
|
||||||
|
{
|
||||||
|
m_loop = wl_display_get_event_loop(m_display.c_ptr());
|
||||||
|
if (!m_loop) {
|
||||||
|
throw std::runtime_error("Failed to get Wayland event loop");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *socket_name { wl_display_add_socket_auto(m_display.c_ptr()) };
|
||||||
|
if (!socket_name) {
|
||||||
|
throw std::runtime_error("Failed to create Wayland socket");
|
||||||
|
}
|
||||||
|
if (socket_name) {
|
||||||
|
m_socket_name = socket_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_logger.info("Wayland listening on {}", m_socket_name);
|
||||||
|
|
||||||
|
m_compositor_global = create_compositor_global();
|
||||||
|
m_shm_global = create_shm_global();
|
||||||
|
m_xdg_wm_base_global = create_xdg_wm_base_global();
|
||||||
|
}
|
||||||
|
|
||||||
|
WaylandServer::~WaylandServer() = default;
|
||||||
|
|
||||||
|
auto WaylandServer::dispatch() -> void
|
||||||
|
{
|
||||||
|
if (!m_loop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_event_loop_dispatch(m_loop, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto WaylandServer::flush() -> void
|
||||||
|
{
|
||||||
|
wl_display_flush_clients(m_display.c_ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto WaylandServer::now_ms() const -> std::uint32_t
|
||||||
|
{
|
||||||
|
using Clock = std::chrono::steady_clock;
|
||||||
|
auto now { Clock::now().time_since_epoch() };
|
||||||
|
auto ms {
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(now).count()
|
||||||
|
};
|
||||||
|
return static_cast<std::uint32_t>(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto WaylandServer::register_surface(Surface *surface) -> void
|
||||||
|
{
|
||||||
|
if (!surface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_surfaces.push_back(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto WaylandServer::unregister_surface(Surface *surface) -> void
|
||||||
|
{
|
||||||
|
if (!surface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto it { std::remove(m_surfaces.begin(), m_surfaces.end(), surface) };
|
||||||
|
m_surfaces.erase(it, m_surfaces.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto WaylandServer::surfaces() const -> std::span<Surface *const>
|
||||||
|
{
|
||||||
|
return { m_surfaces.data(), m_surfaces.size() };
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
|
#include "Surface.h"
|
||||||
50
src/wayland/WaylandServer.h
Normal file
50
src/wayland/WaylandServer.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <span>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
#include "../Logger.h"
|
||||||
|
|
||||||
|
#include "Display.h"
|
||||||
|
#include "Global.h"
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
|
||||||
|
struct Surface;
|
||||||
|
|
||||||
|
struct WaylandServer {
|
||||||
|
explicit WaylandServer(Logger &logger);
|
||||||
|
~WaylandServer();
|
||||||
|
|
||||||
|
auto display() -> Display & { return m_display; }
|
||||||
|
auto logger() -> Logger & { return m_logger; }
|
||||||
|
auto socket_name() const -> std::string_view { return m_socket_name; }
|
||||||
|
auto dispatch() -> void;
|
||||||
|
auto flush() -> void;
|
||||||
|
auto now_ms() const -> std::uint32_t;
|
||||||
|
auto register_surface(Surface *surface) -> void;
|
||||||
|
auto unregister_surface(Surface *surface) -> void;
|
||||||
|
auto surfaces() const -> std::span<Surface *const>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto create_compositor_global() -> std::unique_ptr<Global>;
|
||||||
|
auto create_shm_global() -> std::unique_ptr<Global>;
|
||||||
|
auto create_xdg_wm_base_global() -> std::unique_ptr<Global>;
|
||||||
|
|
||||||
|
Logger &m_logger;
|
||||||
|
Display m_display {};
|
||||||
|
wl_event_loop *m_loop { nullptr };
|
||||||
|
std::string m_socket_name {};
|
||||||
|
std::unique_ptr<Global> m_compositor_global {};
|
||||||
|
std::unique_ptr<Global> m_shm_global {};
|
||||||
|
std::unique_ptr<Global> m_xdg_wm_base_global {};
|
||||||
|
std::vector<Surface *> m_surfaces {};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
254
src/wayland/protocols/CompositorProtocol.cpp
Normal file
254
src/wayland/protocols/CompositorProtocol.cpp
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
#include "wayland-server-protocol.h"
|
||||||
|
|
||||||
|
#include "../Region.h"
|
||||||
|
#include "../Shm.h"
|
||||||
|
#include "../Surface.h"
|
||||||
|
#include "../WaylandServer.h"
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr std::uint32_t COMPOSITOR_VERSION = 4;
|
||||||
|
|
||||||
|
auto resource_version(wl_resource *resource) -> std::uint32_t
|
||||||
|
{
|
||||||
|
return std::min<std::uint32_t>(
|
||||||
|
wl_resource_get_version(resource), COMPOSITOR_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto region_from_resource(wl_resource *resource) -> std::shared_ptr<Region>
|
||||||
|
{
|
||||||
|
if (!resource) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto *handle { static_cast<std::shared_ptr<Region> *>(
|
||||||
|
wl_resource_get_user_data(resource)) };
|
||||||
|
if (!handle) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return *handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void region_destroy(wl_resource *resource)
|
||||||
|
{
|
||||||
|
auto *handle { static_cast<std::shared_ptr<Region> *>(
|
||||||
|
wl_resource_get_user_data(resource)) };
|
||||||
|
delete handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void region_handle_destroy(wl_client *, wl_resource *resource)
|
||||||
|
{
|
||||||
|
wl_resource_destroy(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
void region_handle_add(wl_client *, wl_resource *resource, std::int32_t x,
|
||||||
|
std::int32_t y, std::int32_t width, std::int32_t height)
|
||||||
|
{
|
||||||
|
if (auto region { region_from_resource(resource) }) {
|
||||||
|
region->add(x, y, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void region_handle_subtract(wl_client *, wl_resource *resource, std::int32_t x,
|
||||||
|
std::int32_t y, std::int32_t width, std::int32_t height)
|
||||||
|
{
|
||||||
|
if (auto region { region_from_resource(resource) }) {
|
||||||
|
region->subtract(x, y, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_region_interface const REGION_INTERFACE = {
|
||||||
|
.destroy = region_handle_destroy,
|
||||||
|
.add = region_handle_add,
|
||||||
|
.subtract = region_handle_subtract,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto surface_from_resource(wl_resource *resource) -> Surface *
|
||||||
|
{
|
||||||
|
return static_cast<Surface *>(wl_resource_get_user_data(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_destroy_resource(wl_resource *resource)
|
||||||
|
{
|
||||||
|
auto *surface { surface_from_resource(resource) };
|
||||||
|
delete surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_handle_destroy(wl_client *, wl_resource *resource)
|
||||||
|
{
|
||||||
|
wl_resource_destroy(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_handle_attach(wl_client *, wl_resource *resource,
|
||||||
|
wl_resource *buffer_resource, std::int32_t x, std::int32_t y)
|
||||||
|
{
|
||||||
|
auto *surface { surface_from_resource(resource) };
|
||||||
|
if (!surface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
surface->attach(shm_buffer_from_resource(buffer_resource), x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_handle_damage(wl_client *, wl_resource *resource, std::int32_t x,
|
||||||
|
std::int32_t y, std::int32_t width, std::int32_t height)
|
||||||
|
{
|
||||||
|
if (auto *surface { surface_from_resource(resource) }) {
|
||||||
|
surface->damage(x, y, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_handle_frame(
|
||||||
|
wl_client *client, wl_resource *resource, std::uint32_t callback_id)
|
||||||
|
{
|
||||||
|
auto *surface { surface_from_resource(resource) };
|
||||||
|
if (!surface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto version { wl_resource_get_version(resource) };
|
||||||
|
auto *callback_resource { wl_resource_create(
|
||||||
|
client, &wl_callback_interface, version, callback_id) };
|
||||||
|
if (!callback_resource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_resource_set_implementation(
|
||||||
|
callback_resource, nullptr, nullptr, nullptr);
|
||||||
|
surface->frame(callback_resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_handle_set_opaque_region(
|
||||||
|
wl_client *, wl_resource *resource, wl_resource *region_resource)
|
||||||
|
{
|
||||||
|
if (auto *surface { surface_from_resource(resource) }) {
|
||||||
|
surface->set_opaque_region(region_from_resource(region_resource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_handle_set_input_region(
|
||||||
|
wl_client *, wl_resource *resource, wl_resource *region_resource)
|
||||||
|
{
|
||||||
|
if (auto *surface { surface_from_resource(resource) }) {
|
||||||
|
surface->set_input_region(region_from_resource(region_resource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_handle_commit(wl_client *, wl_resource *resource)
|
||||||
|
{
|
||||||
|
if (auto *surface { surface_from_resource(resource) }) {
|
||||||
|
surface->commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_handle_set_buffer_transform(
|
||||||
|
wl_client *, wl_resource *resource, std::int32_t transform)
|
||||||
|
{
|
||||||
|
if (auto *surface { surface_from_resource(resource) }) {
|
||||||
|
surface->set_buffer_transform(transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_handle_set_buffer_scale(
|
||||||
|
wl_client *, wl_resource *resource, std::int32_t scale)
|
||||||
|
{
|
||||||
|
if (auto *surface { surface_from_resource(resource) }) {
|
||||||
|
surface->set_buffer_scale(scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_handle_damage_buffer(wl_client *, wl_resource *resource,
|
||||||
|
std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
|
||||||
|
{
|
||||||
|
if (auto *surface { surface_from_resource(resource) }) {
|
||||||
|
surface->damage_buffer(x, y, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void surface_handle_offset(
|
||||||
|
wl_client *, wl_resource *resource, std::int32_t x, std::int32_t y)
|
||||||
|
{
|
||||||
|
(void)resource;
|
||||||
|
(void)x;
|
||||||
|
(void)y;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_surface_interface const SURFACE_INTERFACE = {
|
||||||
|
.destroy = surface_handle_destroy,
|
||||||
|
.attach = surface_handle_attach,
|
||||||
|
.damage = surface_handle_damage,
|
||||||
|
.frame = surface_handle_frame,
|
||||||
|
.set_opaque_region = surface_handle_set_opaque_region,
|
||||||
|
.set_input_region = surface_handle_set_input_region,
|
||||||
|
.commit = surface_handle_commit,
|
||||||
|
.set_buffer_transform = surface_handle_set_buffer_transform,
|
||||||
|
.set_buffer_scale = surface_handle_set_buffer_scale,
|
||||||
|
.damage_buffer = surface_handle_damage_buffer,
|
||||||
|
.offset = surface_handle_offset,
|
||||||
|
};
|
||||||
|
|
||||||
|
void compositor_handle_create_surface(
|
||||||
|
wl_client *client, wl_resource *resource, std::uint32_t id)
|
||||||
|
{
|
||||||
|
auto *server { static_cast<WaylandServer *>(
|
||||||
|
wl_resource_get_user_data(resource)) };
|
||||||
|
if (!server) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto version { resource_version(resource) };
|
||||||
|
auto *surface_resource { wl_resource_create(
|
||||||
|
client, &wl_surface_interface, version, id) };
|
||||||
|
if (!surface_resource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto *surface { new Surface(*server, surface_resource) };
|
||||||
|
wl_resource_set_implementation(surface_resource, &SURFACE_INTERFACE,
|
||||||
|
surface, surface_destroy_resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compositor_handle_create_region(
|
||||||
|
wl_client *client, wl_resource *resource, std::uint32_t id)
|
||||||
|
{
|
||||||
|
auto version { wl_resource_get_version(resource) };
|
||||||
|
auto *region_resource { wl_resource_create(
|
||||||
|
client, &wl_region_interface, version, id) };
|
||||||
|
if (!region_resource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto region { std::make_shared<Region>(region_resource) };
|
||||||
|
auto *handle { new std::shared_ptr<Region>(std::move(region)) };
|
||||||
|
wl_resource_set_implementation(
|
||||||
|
region_resource, ®ION_INTERFACE, handle, region_destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_compositor_interface const COMPOSITOR_INTERFACE = {
|
||||||
|
.create_surface = compositor_handle_create_surface,
|
||||||
|
.create_region = compositor_handle_create_region,
|
||||||
|
};
|
||||||
|
|
||||||
|
void bind_compositor(
|
||||||
|
wl_client *client, void *data, std::uint32_t version, std::uint32_t id)
|
||||||
|
{
|
||||||
|
auto *server { static_cast<WaylandServer *>(data) };
|
||||||
|
auto *resource { wl_resource_create(client, &wl_compositor_interface,
|
||||||
|
std::min(version, COMPOSITOR_VERSION), id) };
|
||||||
|
if (!resource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_resource_set_implementation(
|
||||||
|
resource, &COMPOSITOR_INTERFACE, server, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
auto WaylandServer::create_compositor_global() -> std::unique_ptr<Global>
|
||||||
|
{
|
||||||
|
return std::make_unique<Global>(display(), &wl_compositor_interface,
|
||||||
|
COMPOSITOR_VERSION, this, bind_compositor);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
171
src/wayland/protocols/ShmProtocol.cpp
Normal file
171
src/wayland/protocols/ShmProtocol.cpp
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
#include "wayland-server-protocol.h"
|
||||||
|
|
||||||
|
#include "../Shm.h"
|
||||||
|
#include "../WaylandServer.h"
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr std::uint32_t SHM_VERSION = 2;
|
||||||
|
|
||||||
|
auto shm_pool_from_resource(wl_resource *resource) -> std::shared_ptr<ShmPool>
|
||||||
|
{
|
||||||
|
auto *handle { static_cast<std::shared_ptr<ShmPool> *>(
|
||||||
|
wl_resource_get_user_data(resource)) };
|
||||||
|
if (!handle) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return *handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shm_pool_destroy_resource(wl_resource *resource)
|
||||||
|
{
|
||||||
|
auto *handle { static_cast<std::shared_ptr<ShmPool> *>(
|
||||||
|
wl_resource_get_user_data(resource)) };
|
||||||
|
delete handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shm_buffer_destroy_resource(wl_resource *resource)
|
||||||
|
{
|
||||||
|
auto *handle { static_cast<std::shared_ptr<ShmBuffer> *>(
|
||||||
|
wl_resource_get_user_data(resource)) };
|
||||||
|
delete handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shm_pool_handle_destroy(wl_client *, wl_resource *resource)
|
||||||
|
{
|
||||||
|
wl_resource_destroy(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
void shm_pool_handle_resize(
|
||||||
|
wl_client *, wl_resource *resource, std::int32_t size)
|
||||||
|
{
|
||||||
|
auto pool { shm_pool_from_resource(resource) };
|
||||||
|
if (!pool) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (size <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pool->resize(static_cast<std::size_t>(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void shm_handle_release(wl_client *, wl_resource *) { }
|
||||||
|
|
||||||
|
void shm_pool_handle_create_buffer(wl_client *client, wl_resource *resource,
|
||||||
|
std::uint32_t id, std::int32_t offset, std::int32_t width,
|
||||||
|
std::int32_t height, std::int32_t stride, std::uint32_t format)
|
||||||
|
{
|
||||||
|
auto pool { shm_pool_from_resource(resource) };
|
||||||
|
if (!pool) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width <= 0 || height <= 0 || stride <= 0 || offset < 0) {
|
||||||
|
wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE,
|
||||||
|
"Invalid shm buffer geometry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format != WL_SHM_FORMAT_XRGB8888 && format != WL_SHM_FORMAT_ARGB8888) {
|
||||||
|
wl_resource_post_error(
|
||||||
|
resource, WL_SHM_ERROR_INVALID_FORMAT, "Unsupported shm format");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto required { static_cast<std::size_t>(offset)
|
||||||
|
+ static_cast<std::size_t>(height) * static_cast<std::size_t>(stride) };
|
||||||
|
if (required > pool->size()) {
|
||||||
|
wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE,
|
||||||
|
"Shm buffer size out of bounds");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *buffer_resource { wl_resource_create(
|
||||||
|
client, &wl_buffer_interface, 1, id) };
|
||||||
|
if (!buffer_resource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buffer { std::make_shared<ShmBuffer>(
|
||||||
|
pool, buffer_resource, offset, width, height, stride, format) };
|
||||||
|
auto *handle { new std::shared_ptr<ShmBuffer>(std::move(buffer)) };
|
||||||
|
wl_resource_set_implementation(
|
||||||
|
buffer_resource, nullptr, handle, shm_buffer_destroy_resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_shm_pool_interface const SHM_POOL_INTERFACE = {
|
||||||
|
.create_buffer = shm_pool_handle_create_buffer,
|
||||||
|
.destroy = shm_pool_handle_destroy,
|
||||||
|
.resize = shm_pool_handle_resize,
|
||||||
|
};
|
||||||
|
|
||||||
|
void shm_handle_create_pool(wl_client *client, wl_resource *resource,
|
||||||
|
std::uint32_t id, int fd, std::int32_t size)
|
||||||
|
{
|
||||||
|
auto *server
|
||||||
|
= static_cast<WaylandServer *>(wl_resource_get_user_data(resource));
|
||||||
|
if (!server) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (size <= 0) {
|
||||||
|
wl_resource_post_error(
|
||||||
|
resource, WL_SHM_ERROR_INVALID_STRIDE, "Invalid shm pool size");
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pool { std::make_shared<ShmPool>(
|
||||||
|
fd, static_cast<std::size_t>(size), server->logger()) };
|
||||||
|
if (!pool->data()) {
|
||||||
|
wl_resource_post_error(
|
||||||
|
resource, WL_SHM_ERROR_INVALID_FD, "Failed to mmap shm pool");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *pool_resource
|
||||||
|
= wl_resource_create(client, &wl_shm_pool_interface, 1, id);
|
||||||
|
if (!pool_resource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto *handle { new std::shared_ptr<ShmPool>(std::move(pool)) };
|
||||||
|
wl_resource_set_implementation(
|
||||||
|
pool_resource, &SHM_POOL_INTERFACE, handle, shm_pool_destroy_resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_shm_interface const SHM_INTERFACE = {
|
||||||
|
.create_pool = shm_handle_create_pool,
|
||||||
|
.release = shm_handle_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
void bind_shm(
|
||||||
|
wl_client *client, void *data, std::uint32_t version, std::uint32_t id)
|
||||||
|
{
|
||||||
|
auto *server { static_cast<WaylandServer *>(data) };
|
||||||
|
auto *resource { wl_resource_create(
|
||||||
|
client, &wl_shm_interface, std::min(version, SHM_VERSION), id) };
|
||||||
|
if (!resource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_resource_set_implementation(resource, &SHM_INTERFACE, server, nullptr);
|
||||||
|
wl_shm_send_format(resource, WL_SHM_FORMAT_XRGB8888);
|
||||||
|
wl_shm_send_format(resource, WL_SHM_FORMAT_ARGB8888);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
auto WaylandServer::create_shm_global() -> std::unique_ptr<Global>
|
||||||
|
{
|
||||||
|
return std::make_unique<Global>(
|
||||||
|
display(), &wl_shm_interface, SHM_VERSION, this, bind_shm);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
327
src/wayland/protocols/XdgShellProtocol.cpp
Normal file
327
src/wayland/protocols/XdgShellProtocol.cpp
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
#include "xdg-shell-server-protocol.h"
|
||||||
|
|
||||||
|
#include "../WaylandServer.h"
|
||||||
|
|
||||||
|
namespace Lunar::Wayland {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr std::uint32_t XDG_WM_BASE_VERSION = 7;
|
||||||
|
|
||||||
|
struct XdgSurface {
|
||||||
|
WaylandServer &server;
|
||||||
|
wl_resource *resource {};
|
||||||
|
wl_resource *surface_resource {};
|
||||||
|
wl_resource *toplevel_resource {};
|
||||||
|
std::uint32_t last_serial { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XdgToplevel {
|
||||||
|
WaylandServer &server;
|
||||||
|
wl_resource *resource {};
|
||||||
|
XdgSurface *surface {};
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct xdg_toplevel_interface const XDG_TOPLEVEL_INTERFACE_IMPL;
|
||||||
|
|
||||||
|
auto xdg_surface_from_resource(wl_resource *resource) -> XdgSurface *
|
||||||
|
{
|
||||||
|
return static_cast<XdgSurface *>(wl_resource_get_user_data(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_toplevel_from_resource(wl_resource *resource) -> XdgToplevel *
|
||||||
|
{
|
||||||
|
return static_cast<XdgToplevel *>(wl_resource_get_user_data(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto send_configure(XdgSurface &surface) -> void
|
||||||
|
{
|
||||||
|
std::uint32_t serial = surface.server.display().next_serial();
|
||||||
|
if (surface.toplevel_resource) {
|
||||||
|
wl_array states;
|
||||||
|
wl_array_init(&states);
|
||||||
|
xdg_toplevel_send_configure(surface.toplevel_resource, 0, 0, &states);
|
||||||
|
wl_array_release(&states);
|
||||||
|
}
|
||||||
|
surface.last_serial = serial;
|
||||||
|
xdg_surface_send_configure(surface.resource, serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_positioner_handle_destroy(wl_client *, wl_resource *resource) -> void
|
||||||
|
{
|
||||||
|
wl_resource_destroy(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_positioner_handle_set_size(
|
||||||
|
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_positioner_handle_set_anchor_rect(wl_client *, wl_resource *,
|
||||||
|
std::int32_t, std::int32_t, std::int32_t, std::int32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_positioner_handle_set_anchor(wl_client *, wl_resource *, std::uint32_t)
|
||||||
|
-> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_positioner_handle_set_gravity(
|
||||||
|
wl_client *, wl_resource *, std::uint32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_positioner_handle_set_constraint_adjustment(
|
||||||
|
wl_client *, wl_resource *, std::uint32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_positioner_handle_set_offset(
|
||||||
|
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_positioner_handle_set_reactive(wl_client *, wl_resource *) -> void { }
|
||||||
|
|
||||||
|
auto xdg_positioner_handle_set_parent_size(
|
||||||
|
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_positioner_handle_set_parent_configure(
|
||||||
|
wl_client *, wl_resource *, std::uint32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
struct xdg_positioner_interface const XDG_POSITIONER_INTERFACE_IMPL = {
|
||||||
|
.destroy = xdg_positioner_handle_destroy,
|
||||||
|
.set_size = xdg_positioner_handle_set_size,
|
||||||
|
.set_anchor_rect = xdg_positioner_handle_set_anchor_rect,
|
||||||
|
.set_anchor = xdg_positioner_handle_set_anchor,
|
||||||
|
.set_gravity = xdg_positioner_handle_set_gravity,
|
||||||
|
.set_constraint_adjustment
|
||||||
|
= xdg_positioner_handle_set_constraint_adjustment,
|
||||||
|
.set_offset = xdg_positioner_handle_set_offset,
|
||||||
|
.set_reactive = xdg_positioner_handle_set_reactive,
|
||||||
|
.set_parent_size = xdg_positioner_handle_set_parent_size,
|
||||||
|
.set_parent_configure = xdg_positioner_handle_set_parent_configure,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto xdg_surface_destroy_resource(wl_resource *resource) -> void
|
||||||
|
{
|
||||||
|
auto *surface { xdg_surface_from_resource(resource) };
|
||||||
|
delete surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_surface_handle_destroy(wl_client *, wl_resource *resource) -> void
|
||||||
|
{
|
||||||
|
wl_resource_destroy(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_surface_handle_get_toplevel(
|
||||||
|
wl_client *client, wl_resource *resource, std::uint32_t id) -> void
|
||||||
|
{
|
||||||
|
auto *surface { xdg_surface_from_resource(resource) };
|
||||||
|
if (!surface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto version { wl_resource_get_version(resource) };
|
||||||
|
auto *toplevel_resource { wl_resource_create(
|
||||||
|
client, &::xdg_toplevel_interface, version, id) };
|
||||||
|
if (!toplevel_resource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto *toplevel { new XdgToplevel {
|
||||||
|
surface->server, toplevel_resource, surface } };
|
||||||
|
surface->toplevel_resource = toplevel_resource;
|
||||||
|
wl_resource_set_implementation(toplevel_resource,
|
||||||
|
&XDG_TOPLEVEL_INTERFACE_IMPL, toplevel, [](wl_resource *res) {
|
||||||
|
auto *tl { xdg_toplevel_from_resource(res) };
|
||||||
|
if (tl && tl->surface && tl->surface->toplevel_resource == res) {
|
||||||
|
tl->surface->toplevel_resource = nullptr;
|
||||||
|
}
|
||||||
|
delete tl;
|
||||||
|
});
|
||||||
|
|
||||||
|
send_configure(*surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_surface_handle_get_popup(wl_client *, wl_resource *, std::uint32_t,
|
||||||
|
wl_resource *, wl_resource *) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_surface_handle_set_window_geometry(wl_client *, wl_resource *,
|
||||||
|
std::int32_t, std::int32_t, std::int32_t, std::int32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_surface_handle_ack_configure(
|
||||||
|
wl_client *, wl_resource *resource, std::uint32_t serial) -> void
|
||||||
|
{
|
||||||
|
if (auto *surface { xdg_surface_from_resource(resource) }) {
|
||||||
|
surface->last_serial = serial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct xdg_surface_interface const XDG_SURFACE_INTERFACE_IMPL = {
|
||||||
|
.destroy = xdg_surface_handle_destroy,
|
||||||
|
.get_toplevel = xdg_surface_handle_get_toplevel,
|
||||||
|
.get_popup = xdg_surface_handle_get_popup,
|
||||||
|
.set_window_geometry = xdg_surface_handle_set_window_geometry,
|
||||||
|
.ack_configure = xdg_surface_handle_ack_configure,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_destroy(wl_client *, wl_resource *resource) -> void
|
||||||
|
{
|
||||||
|
wl_resource_destroy(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_set_parent(wl_client *, wl_resource *, wl_resource *)
|
||||||
|
-> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_set_title(wl_client *, wl_resource *, char const *)
|
||||||
|
-> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_set_app_id(wl_client *, wl_resource *, char const *)
|
||||||
|
-> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_show_window_menu(wl_client *, wl_resource *,
|
||||||
|
wl_resource *, std::uint32_t, std::int32_t, std::int32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_move(
|
||||||
|
wl_client *, wl_resource *, wl_resource *, std::uint32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_resize(wl_client *, wl_resource *, wl_resource *,
|
||||||
|
std::uint32_t, std::uint32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_set_max_size(
|
||||||
|
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_set_min_size(
|
||||||
|
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_set_maximized(wl_client *, wl_resource *) -> void { }
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_unset_maximized(wl_client *, wl_resource *) -> void { }
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_set_fullscreen(
|
||||||
|
wl_client *, wl_resource *, wl_resource *) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_unset_fullscreen(wl_client *, wl_resource *) -> void {
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_toplevel_handle_set_minimized(wl_client *, wl_resource *) -> void { }
|
||||||
|
|
||||||
|
struct xdg_toplevel_interface const XDG_TOPLEVEL_INTERFACE_IMPL = {
|
||||||
|
.destroy = xdg_toplevel_handle_destroy,
|
||||||
|
.set_parent = xdg_toplevel_handle_set_parent,
|
||||||
|
.set_title = xdg_toplevel_handle_set_title,
|
||||||
|
.set_app_id = xdg_toplevel_handle_set_app_id,
|
||||||
|
.show_window_menu = xdg_toplevel_handle_show_window_menu,
|
||||||
|
.move = xdg_toplevel_handle_move,
|
||||||
|
.resize = xdg_toplevel_handle_resize,
|
||||||
|
.set_max_size = xdg_toplevel_handle_set_max_size,
|
||||||
|
.set_min_size = xdg_toplevel_handle_set_min_size,
|
||||||
|
.set_maximized = xdg_toplevel_handle_set_maximized,
|
||||||
|
.unset_maximized = xdg_toplevel_handle_unset_maximized,
|
||||||
|
.set_fullscreen = xdg_toplevel_handle_set_fullscreen,
|
||||||
|
.unset_fullscreen = xdg_toplevel_handle_unset_fullscreen,
|
||||||
|
.set_minimized = xdg_toplevel_handle_set_minimized,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto xdg_wm_base_handle_destroy(wl_client *, wl_resource *resource) -> void
|
||||||
|
{
|
||||||
|
wl_resource_destroy(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_wm_base_handle_create_positioner(
|
||||||
|
wl_client *client, wl_resource *resource, std::uint32_t id) -> void
|
||||||
|
{
|
||||||
|
auto version { wl_resource_get_version(resource) };
|
||||||
|
auto *positioner { wl_resource_create(
|
||||||
|
client, &::xdg_positioner_interface, version, id) };
|
||||||
|
if (!positioner) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_resource_set_implementation(
|
||||||
|
positioner, &XDG_POSITIONER_INTERFACE_IMPL, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_wm_base_handle_get_xdg_surface(wl_client *client,
|
||||||
|
wl_resource *resource, std::uint32_t id, wl_resource *surface_resource)
|
||||||
|
-> void
|
||||||
|
{
|
||||||
|
auto *server
|
||||||
|
= static_cast<WaylandServer *>(wl_resource_get_user_data(resource));
|
||||||
|
if (!server) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto version { wl_resource_get_version(resource) };
|
||||||
|
auto *xdg_surface_resource { wl_resource_create(
|
||||||
|
client, &::xdg_surface_interface, version, id) };
|
||||||
|
if (!xdg_surface_resource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto *surface { new XdgSurface {
|
||||||
|
*server, xdg_surface_resource, surface_resource, nullptr, 0 } };
|
||||||
|
wl_resource_set_implementation(xdg_surface_resource,
|
||||||
|
&XDG_SURFACE_INTERFACE_IMPL, surface, xdg_surface_destroy_resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xdg_wm_base_handle_pong(wl_client *, wl_resource *, std::uint32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
struct xdg_wm_base_interface const XDG_WM_BASE_INTERFACE_IMPL = {
|
||||||
|
.destroy = xdg_wm_base_handle_destroy,
|
||||||
|
.create_positioner = xdg_wm_base_handle_create_positioner,
|
||||||
|
.get_xdg_surface = xdg_wm_base_handle_get_xdg_surface,
|
||||||
|
.pong = xdg_wm_base_handle_pong,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto bind_xdg_wm_base(wl_client *client, void *data, std::uint32_t version,
|
||||||
|
std::uint32_t id) -> void
|
||||||
|
{
|
||||||
|
auto *server { static_cast<WaylandServer *>(data) };
|
||||||
|
auto *resource { wl_resource_create(client, &::xdg_wm_base_interface,
|
||||||
|
std::min(version, XDG_WM_BASE_VERSION), id) };
|
||||||
|
if (!resource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_resource_set_implementation(
|
||||||
|
resource, &XDG_WM_BASE_INTERFACE_IMPL, server, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
auto WaylandServer::create_xdg_wm_base_global() -> std::unique_ptr<Global>
|
||||||
|
{
|
||||||
|
return std::make_unique<Global>(display(), &::xdg_wm_base_interface,
|
||||||
|
XDG_WM_BASE_VERSION, this, bind_xdg_wm_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Lunar::Wayland
|
||||||
417
tools/shm_life.cpp
Normal file
417
tools/shm_life.cpp
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cmath>
|
||||||
|
#include <csignal>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <print>
|
||||||
|
#include <random>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
static wl_compositor *g_compositor;
|
||||||
|
static wl_shm *g_shm;
|
||||||
|
static xdg_wm_base *g_xdg_wm_base;
|
||||||
|
static xdg_surface *g_xdg_surface;
|
||||||
|
static xdg_toplevel *g_xdg_toplevel;
|
||||||
|
static bool g_configured;
|
||||||
|
static std::sig_atomic_t volatile g_running { 1 };
|
||||||
|
|
||||||
|
static auto handle_signal(int) -> void { g_running = 0; }
|
||||||
|
|
||||||
|
static auto registry_global(void *data, wl_registry *registry, uint32_t name,
|
||||||
|
char const *interface, uint32_t version) -> void
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
if (std::strcmp(interface, "wl_compositor") == 0) {
|
||||||
|
g_compositor = static_cast<wl_compositor *>(wl_registry_bind(registry,
|
||||||
|
name, &wl_compositor_interface, version < 4 ? version : 4));
|
||||||
|
} else if (std::strcmp(interface, "wl_shm") == 0) {
|
||||||
|
g_shm = static_cast<wl_shm *>(wl_registry_bind(
|
||||||
|
registry, name, &wl_shm_interface, version < 1 ? version : 1));
|
||||||
|
} else if (std::strcmp(interface, "xdg_wm_base") == 0) {
|
||||||
|
g_xdg_wm_base = static_cast<xdg_wm_base *>(wl_registry_bind(
|
||||||
|
registry, name, &xdg_wm_base_interface, version < 7 ? version : 7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto registry_global_remove(
|
||||||
|
void *data, wl_registry *registry, uint32_t name) -> void
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
(void)registry;
|
||||||
|
(void)name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static wl_registry_listener const registry_listener = {
|
||||||
|
.global = registry_global,
|
||||||
|
.global_remove = registry_global_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto xdg_wm_base_handle_ping(
|
||||||
|
void *, xdg_wm_base *wm_base, uint32_t serial) -> void
|
||||||
|
{
|
||||||
|
xdg_wm_base_pong(wm_base, serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
static xdg_wm_base_listener const xdg_wm_base_listener = {
|
||||||
|
.ping = xdg_wm_base_handle_ping,
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto xdg_surface_handle_configure(
|
||||||
|
void *, xdg_surface *surface, uint32_t serial) -> void
|
||||||
|
{
|
||||||
|
xdg_surface_ack_configure(surface, serial);
|
||||||
|
g_configured = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static xdg_surface_listener const xdg_surface_listener = {
|
||||||
|
.configure = xdg_surface_handle_configure,
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto xdg_toplevel_handle_configure(
|
||||||
|
void *, xdg_toplevel *, int32_t, int32_t, wl_array *) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto xdg_toplevel_handle_close(void *, xdg_toplevel *) -> void { }
|
||||||
|
|
||||||
|
static auto xdg_toplevel_handle_configure_bounds(
|
||||||
|
void *, xdg_toplevel *, int32_t, int32_t) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto xdg_toplevel_handle_wm_capabilities(
|
||||||
|
void *, xdg_toplevel *, wl_array *) -> void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static xdg_toplevel_listener const xdg_toplevel_listener = {
|
||||||
|
.configure = xdg_toplevel_handle_configure,
|
||||||
|
.close = xdg_toplevel_handle_close,
|
||||||
|
.configure_bounds = xdg_toplevel_handle_configure_bounds,
|
||||||
|
.wm_capabilities = xdg_toplevel_handle_wm_capabilities,
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto create_shm_file(size_t size) -> int
|
||||||
|
{
|
||||||
|
int fd { memfd_create("shm_life", 0) };
|
||||||
|
if (fd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ftruncate(fd, static_cast<off_t>(size)) != 0) {
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto main(int argc, char **argv) -> int
|
||||||
|
{
|
||||||
|
auto *display { wl_display_connect(nullptr) };
|
||||||
|
if (!display) {
|
||||||
|
std::println(std::cerr, "Failed to connect to Wayland display.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *registry { wl_display_get_registry(display) };
|
||||||
|
if (!registry) {
|
||||||
|
std::println(std::cerr, "Failed to fetch Wayland registry.");
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wl_registry_add_listener(registry, ®istry_listener, nullptr) < 0) {
|
||||||
|
std::println(std::cerr, "Failed to add registry listener.");
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (wl_display_roundtrip(display) < 0) {
|
||||||
|
std::println(std::cerr, "Failed to sync Wayland registry.");
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_compositor || !g_shm || !g_xdg_wm_base) {
|
||||||
|
std::println(std::cerr, "Missing compositor or xdg globals.");
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::signal(SIGINT, handle_signal);
|
||||||
|
std::signal(SIGTERM, handle_signal);
|
||||||
|
|
||||||
|
if (xdg_wm_base_add_listener(g_xdg_wm_base, &xdg_wm_base_listener, nullptr)
|
||||||
|
< 0) {
|
||||||
|
std::println(std::cerr, "Failed to add xdg_wm_base listener.");
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int width { 640 };
|
||||||
|
constexpr int height { 480 };
|
||||||
|
int cell_size { 5 };
|
||||||
|
if (argc > 1) {
|
||||||
|
long requested { std::strtol(argv[1], nullptr, 10) };
|
||||||
|
if (requested >= 2 && requested <= 80) {
|
||||||
|
cell_size = static_cast<int>(requested);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int grid_width { width / cell_size };
|
||||||
|
int grid_height { height / cell_size };
|
||||||
|
constexpr int stride { width * 4 };
|
||||||
|
constexpr size_t size { static_cast<size_t>(stride) * height };
|
||||||
|
int fd { create_shm_file(size) };
|
||||||
|
if (fd < 0) {
|
||||||
|
std::println(std::cerr, "Failed to create shm file.");
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *data { static_cast<uint32_t *>(
|
||||||
|
mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) };
|
||||||
|
if (data == MAP_FAILED) {
|
||||||
|
std::println(std::cerr, "Failed to mmap shm.");
|
||||||
|
close(fd);
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> grid(static_cast<size_t>(grid_width * grid_height), 0);
|
||||||
|
std::vector<uint8_t> next(static_cast<size_t>(grid_width * grid_height), 0);
|
||||||
|
std::minstd_rand rng(std::random_device {}());
|
||||||
|
std::bernoulli_distribution alive_dist(0.45);
|
||||||
|
std::vector<uint64_t> history;
|
||||||
|
float hue { 0.0f };
|
||||||
|
float hue_step { 1.5f };
|
||||||
|
|
||||||
|
auto hue_to_rgb { [](float hue_degrees) -> uint32_t {
|
||||||
|
float h { std::fmod(hue_degrees, 360.0f) / 360.0f };
|
||||||
|
float s { 1.0f };
|
||||||
|
float l { 0.5f };
|
||||||
|
auto hue_to_channel { [](float p, float q, float t) -> float {
|
||||||
|
if (t < 0.0f) {
|
||||||
|
t += 1.0f;
|
||||||
|
}
|
||||||
|
if (t > 1.0f) {
|
||||||
|
t -= 1.0f;
|
||||||
|
}
|
||||||
|
if (t < 1.0f / 6.0f) {
|
||||||
|
return p + (q - p) * 6.0f * t;
|
||||||
|
}
|
||||||
|
if (t < 1.0f / 2.0f) {
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
if (t < 2.0f / 3.0f) {
|
||||||
|
return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
} };
|
||||||
|
float r { l };
|
||||||
|
float g { l };
|
||||||
|
float b { l };
|
||||||
|
if (s > 0.0f) {
|
||||||
|
float q { l < 0.5f ? l * (1.0f + s) : l + s - l * s };
|
||||||
|
float p { 2.0f * l - q };
|
||||||
|
r = hue_to_channel(p, q, h + 1.0f / 3.0f);
|
||||||
|
g = hue_to_channel(p, q, h);
|
||||||
|
b = hue_to_channel(p, q, h - 1.0f / 3.0f);
|
||||||
|
}
|
||||||
|
uint8_t rr { static_cast<uint8_t>(r * 255.0f) };
|
||||||
|
uint8_t gg { static_cast<uint8_t>(g * 255.0f) };
|
||||||
|
uint8_t bb { static_cast<uint8_t>(b * 255.0f) };
|
||||||
|
return 0xff000000u | (uint32_t)rr << 16 | (uint32_t)gg << 8
|
||||||
|
| (uint32_t)bb;
|
||||||
|
} };
|
||||||
|
|
||||||
|
auto randomize_grid { [&]() -> void {
|
||||||
|
for (int y = 0; y < grid_height; ++y) {
|
||||||
|
for (int x = 0; x < grid_width; ++x) {
|
||||||
|
grid[y * grid_width + x] = alive_dist(rng) ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::fill(next.begin(), next.end(), 0);
|
||||||
|
history.clear();
|
||||||
|
} };
|
||||||
|
|
||||||
|
auto hash_grid { [&]() -> uint64_t {
|
||||||
|
uint64_t hash { 1469598103934665603ull };
|
||||||
|
for (uint8_t cell : grid) {
|
||||||
|
hash ^= static_cast<uint64_t>(cell);
|
||||||
|
hash *= 1099511628211ull;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
} };
|
||||||
|
|
||||||
|
randomize_grid();
|
||||||
|
|
||||||
|
auto *pool { wl_shm_create_pool(g_shm, fd, static_cast<int>(size)) };
|
||||||
|
auto *buffer { wl_shm_pool_create_buffer(
|
||||||
|
pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888) };
|
||||||
|
auto *surface { wl_compositor_create_surface(g_compositor) };
|
||||||
|
if (!pool || !buffer || !surface) {
|
||||||
|
std::println(std::cerr, "Failed to create shm objects.");
|
||||||
|
if (surface) {
|
||||||
|
wl_surface_destroy(surface);
|
||||||
|
}
|
||||||
|
if (buffer) {
|
||||||
|
wl_buffer_destroy(buffer);
|
||||||
|
}
|
||||||
|
if (pool) {
|
||||||
|
wl_shm_pool_destroy(pool);
|
||||||
|
}
|
||||||
|
munmap(data, size);
|
||||||
|
close(fd);
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_xdg_surface = xdg_wm_base_get_xdg_surface(g_xdg_wm_base, surface);
|
||||||
|
if (!g_xdg_surface) {
|
||||||
|
std::println(std::cerr, "Failed to create xdg_surface.");
|
||||||
|
wl_surface_destroy(surface);
|
||||||
|
wl_buffer_destroy(buffer);
|
||||||
|
wl_shm_pool_destroy(pool);
|
||||||
|
munmap(data, size);
|
||||||
|
close(fd);
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (xdg_surface_add_listener(g_xdg_surface, &xdg_surface_listener, nullptr)
|
||||||
|
< 0) {
|
||||||
|
std::println(std::cerr, "Failed to add xdg_surface listener.");
|
||||||
|
xdg_surface_destroy(g_xdg_surface);
|
||||||
|
wl_surface_destroy(surface);
|
||||||
|
wl_buffer_destroy(buffer);
|
||||||
|
wl_shm_pool_destroy(pool);
|
||||||
|
munmap(data, size);
|
||||||
|
close(fd);
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_xdg_toplevel = xdg_surface_get_toplevel(g_xdg_surface);
|
||||||
|
if (g_xdg_toplevel) {
|
||||||
|
xdg_toplevel_add_listener(
|
||||||
|
g_xdg_toplevel, &xdg_toplevel_listener, nullptr);
|
||||||
|
xdg_toplevel_set_title(g_xdg_toplevel, "life");
|
||||||
|
xdg_toplevel_set_app_id(g_xdg_toplevel, "lunar.life");
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_surface_commit(surface);
|
||||||
|
if (wl_display_roundtrip(display) < 0 || !g_configured) {
|
||||||
|
std::println(std::cerr, "Failed to receive initial configure.");
|
||||||
|
if (g_xdg_toplevel) {
|
||||||
|
xdg_toplevel_destroy(g_xdg_toplevel);
|
||||||
|
}
|
||||||
|
xdg_surface_destroy(g_xdg_surface);
|
||||||
|
wl_surface_destroy(surface);
|
||||||
|
wl_buffer_destroy(buffer);
|
||||||
|
wl_shm_pool_destroy(pool);
|
||||||
|
munmap(data, size);
|
||||||
|
close(fd);
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_surface_attach(surface, buffer, 0, 0);
|
||||||
|
wl_surface_commit(surface);
|
||||||
|
wl_display_flush(display);
|
||||||
|
|
||||||
|
auto last_tick { std::chrono::steady_clock::now() };
|
||||||
|
while (g_running) {
|
||||||
|
wl_display_dispatch_pending(display);
|
||||||
|
|
||||||
|
auto now { std::chrono::steady_clock::now() };
|
||||||
|
auto dt { now - last_tick };
|
||||||
|
if (dt < std::chrono::milliseconds(16)) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(4));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
last_tick = now;
|
||||||
|
|
||||||
|
for (int y = 0; y < grid_height; ++y) {
|
||||||
|
for (int x = 0; x < grid_width; ++x) {
|
||||||
|
int neighbors { 0 };
|
||||||
|
for (int oy = -1; oy <= 1; ++oy) {
|
||||||
|
for (int ox = -1; ox <= 1; ++ox) {
|
||||||
|
if (ox == 0 && oy == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int ny { (y + oy + grid_height) % grid_height };
|
||||||
|
int nx { (x + ox + grid_width) % grid_width };
|
||||||
|
neighbors += grid[ny * grid_width + nx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint8_t alive { grid[y * grid_width + x] };
|
||||||
|
if (alive) {
|
||||||
|
next[y * grid_width + x]
|
||||||
|
= (neighbors == 2 || neighbors == 3) ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
next[y * grid_width + x] = (neighbors == 3) ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grid.swap(next);
|
||||||
|
std::fill(next.begin(), next.end(), 0);
|
||||||
|
|
||||||
|
uint64_t current_hash { hash_grid() };
|
||||||
|
bool repeating { std::find(history.begin(), history.end(), current_hash)
|
||||||
|
!= history.end() };
|
||||||
|
if (repeating) {
|
||||||
|
randomize_grid();
|
||||||
|
current_hash = hash_grid();
|
||||||
|
}
|
||||||
|
history.push_back(current_hash);
|
||||||
|
if (history.size() > 5) {
|
||||||
|
history.erase(history.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
hue += hue_step;
|
||||||
|
if (hue >= 360.0f) {
|
||||||
|
hue -= 360.0f;
|
||||||
|
}
|
||||||
|
uint32_t alive_color { hue_to_rgb(hue) };
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
int gy { y / cell_size };
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
int gx { x / cell_size };
|
||||||
|
uint8_t alive { grid[gy * grid_width + gx] };
|
||||||
|
if (alive) {
|
||||||
|
data[y * width + x] = alive_color;
|
||||||
|
} else {
|
||||||
|
data[y * width + x] = 0x00000000u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_surface_attach(surface, buffer, 0, 0);
|
||||||
|
wl_surface_damage_buffer(surface, 0, 0, width, height);
|
||||||
|
wl_surface_commit(surface);
|
||||||
|
wl_display_flush(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_xdg_toplevel) {
|
||||||
|
xdg_toplevel_destroy(g_xdg_toplevel);
|
||||||
|
}
|
||||||
|
if (g_xdg_surface) {
|
||||||
|
xdg_surface_destroy(g_xdg_surface);
|
||||||
|
}
|
||||||
|
wl_buffer_destroy(buffer);
|
||||||
|
wl_shm_pool_destroy(pool);
|
||||||
|
wl_surface_destroy(surface);
|
||||||
|
munmap(data, size);
|
||||||
|
close(fd);
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user