Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
738b6ab
fix: critical Vulkan/Wayland bugs, resource leaks, and build improvem…
daedaevibin Jun 19, 2026
5e0cc65
fix: address Devin Review issues and CI build failure
daedaevibin Jun 19, 2026
6fd028b
fix: clean up build warnings
daedaevibin Jun 19, 2026
08855c6
fix: Hyprland/wlroots swapchain creation and SIGSEGV crash
daedaevibin Jun 19, 2026
fe638a5
fix: Hyprland swapchain recreation crash
daedaevibin Jun 19, 2026
8dfdaa9
fix: io_uring PTY I/O, keyboard input, and swapchain recreation
daedaevibin Jun 19, 2026
a15b07e
feat: dynamic terminal resize, TIOCSWINSZ, CSI sequences, arrow keys
daedaevibin Jun 19, 2026
2027ca6
fix: add Kaelum:: namespace qualifier to k_default_cols/rows in main.cpp
daedaevibin Jun 19, 2026
a8f18fc
fix: Hyprland windowing compliance and frame pacing
daedaevibin Jun 19, 2026
56ebab3
fix: xdg_surface_set_window_geometry, swapchain extent, and configure…
daedaevibin Jun 19, 2026
3786ddf
fix: glyph rendering, pointer focus, scrolling, and multiple correctn…
daedaevibin Jun 19, 2026
ea85983
fix: add missing wl_pointer frame/axis_source/axis_stop/axis_discrete…
daedaevibin Jun 19, 2026
526bcfc
fix: correct Vulkan NDC Y-axis, pointer listener completeness, vertex…
daedaevibin Jun 19, 2026
a55457d
fix: initial grid resize to match compositor, add render diagnostics
daedaevibin Jun 19, 2026
aca6906
fix: remove O_NONBLOCK from PTY master fd — breaks io_uring reads
daedaevibin Jun 19, 2026
99f8222
fix: escape sequence parser — handle DEC private modes, OSC, charset …
daedaevibin Jun 19, 2026
4f5eff9
fix: replace io_uring PTY reads with direct poll()+read()
daedaevibin Jun 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
- name: Install dependencies and oneAPI
run: |
sudo apt-get update
sudo apt-get install -y liburing-dev libvulkan-dev libwayland-dev libfreetype6-dev libfontconfig1-dev wget ninja-build ccache mold
sudo apt-get install -y liburing-dev libvulkan-dev libwayland-dev libfreetype6-dev libfontconfig1-dev wget ninja-build ccache mold glslang-tools
Comment thread
daedaevibin marked this conversation as resolved.

# Install Intel oneAPI Base Toolkit (Compiler)
wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor | sudo tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ Thumbs.db
*.dll
*.exe

# Compiled shaders (generated from GLSL sources)
shaders/*.spv

# Logs
*.log
26 changes: 24 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ endif()

# ---- Compiler & Feature Flags ----
# Optimized for performance and security, mirroring Voix's professional standards
set(COMMON_FLAGS "-march=${KAELUM_ARCH} -Oz -pipe -fno-plt -fstack-protector-strong \
set(COMMON_FLAGS "-march=${KAELUM_ARCH} -pipe -fno-plt -fstack-protector-strong \
Comment thread
daedaevibin marked this conversation as resolved.
-fstack-clash-protection -D_FORTIFY_SOURCE=3 -fPIE -flto=thin -fvisibility=hidden \
-fexperimental-library -std=c++26")

set(DEBUG_FLAGS "-g -fstandalone-debug -fno-omit-frame-pointer")
set(DEBUG_FLAGS "-g -O1 -fstandalone-debug -fno-omit-frame-pointer")
set(RELEASE_FLAGS "-O3 -DNDEBUG")

set(CMAKE_CXX_FLAGS "${COMMON_FLAGS} ${DEBUG_FLAGS}" CACHE STRING "" FORCE)
Expand Down Expand Up @@ -150,6 +150,28 @@ target_link_libraries(kaelum PRIVATE kaelum_lib)
target_compile_options(kaelum_lib PRIVATE -Wall -Wextra)
target_compile_options(kaelum PRIVATE -Wall -Wextra)

# ---- Shader Compilation ----
find_program(GLSLC glslc)
if(GLSLC)
set(SHADER_DIR ${CMAKE_SOURCE_DIR}/shaders)
set(SHADER_SOURCES ${SHADER_DIR}/test.vert ${SHADER_DIR}/test.frag)
foreach(SHADER_SRC ${SHADER_SOURCES})
get_filename_component(SHADER_NAME ${SHADER_SRC} NAME)
set(SHADER_SPV ${SHADER_DIR}/${SHADER_NAME}.spv)
add_custom_command(
OUTPUT ${SHADER_SPV}
COMMAND ${GLSLC} ${SHADER_SRC} -o ${SHADER_SPV}
Comment thread
daedaevibin marked this conversation as resolved.
DEPENDS ${SHADER_SRC}
COMMENT "Compiling shader ${SHADER_NAME}"
)
list(APPEND SHADER_SPV_FILES ${SHADER_SPV})
endforeach()
add_custom_target(shaders ALL DEPENDS ${SHADER_SPV_FILES})
add_dependencies(kaelum shaders)
else()
message(WARNING "glslc not found. Shaders will not be compiled automatically.")
endif()
Comment thread
daedaevibin marked this conversation as resolved.

# ---- Installation ----
include(GNUInstallDirs)
install(TARGETS kaelum RUNTIME DESTINATION bin)
14 changes: 8 additions & 6 deletions include/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include <cstdint>
#include <string_view>
#include <array>
#include <vector>

namespace Kaelum {

Expand All @@ -21,16 +21,18 @@ namespace Kaelum {
constexpr bool operator==(const Color&) const = default;
};

constexpr Color nord_bg = {46, 52, 64, 255};
constexpr Color nord_fg = {216, 222, 233, 255};
// Veridian Zenith "Atmosphere" palette — deep void with amber accents
constexpr Color vz_bg = {5, 2, 0, 255}; // --vz-bg-primary #050200
constexpr Color vz_fg = {243, 244, 246, 255}; // body text #f3f4f6
constexpr Color vz_accent = {255, 179, 71, 255}; // --vz-accent-vibrant #FFB347

/**
* @brief A single terminal cell
*/
struct Cell {
char32_t codepoint = U' ';
Color fg = nord_fg;
Color bg = nord_bg;
Color fg = vz_fg;
Color bg = vz_bg;
uint32_t attrs = 0; // Bold, Italic, Underline, etc.

constexpr bool operator==(const Cell&) const = default;
Expand All @@ -39,6 +41,6 @@ namespace Kaelum {
/**
* @brief Grid of cells representing the terminal state
*/
using Grid = std::array<Cell, k_default_cols * k_default_rows>;
using Grid = std::vector<Cell>;

} // namespace Kaelum
6 changes: 4 additions & 2 deletions include/glyph_engine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
namespace Kaelum {

struct GlyphMetric {
uint32_t bearing_x;
uint32_t bearing_y;
int32_t bearing_x;
int32_t bearing_y;
uint32_t width;
uint32_t height;
uint32_t advance;
Expand All @@ -39,11 +39,13 @@ namespace Kaelum {
std::expected<GlyphData, GlyphError> get_glyph(char32_t codepoint);

uint32_t get_line_height() const { return line_height_; }
uint32_t get_cell_width() const { return cell_width_; }

private:
FT_Library library_ = nullptr;
FT_Face face_ = nullptr;
uint32_t line_height_ = 0;
uint32_t cell_width_ = 0;
std::map<char32_t, GlyphData> glyph_cache_;
};

Expand Down
15 changes: 13 additions & 2 deletions include/loom.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <memory>
#include <span>
#include <liburing.h>
#include <sys/ioctl.h>
#include "common.hpp"


Expand Down Expand Up @@ -56,16 +57,26 @@ namespace Kaelum {
*/
std::expected<int, LoomError> register_wake_fd();

/**
* @brief Sets the PTY window size (TIOCSWINSZ).
*/
void set_pty_size(uint16_t cols, uint16_t rows, uint16_t xpixel = 0, uint16_t ypixel = 0) {
struct winsize ws = {rows, cols, xpixel, ypixel};
if (master_fd_ >= 0) ioctl(master_fd_, TIOCSWINSZ, &ws);
}

private:
void submit_read();

int master_fd_ = -1;
pid_t child_pid_ = -1;
struct io_uring ring_;
bool initialized_ = false;

// Buffer for io_uring read requests

static constexpr size_t k_ring_buffer_size = 4096;
static constexpr size_t k_write_buffer_size = 256;
uint8_t read_buffer_[k_ring_buffer_size];
uint8_t write_buffer_[k_write_buffer_size];
};

} // namespace Kaelum
12 changes: 10 additions & 2 deletions include/nexus.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <vector>
#include <span>
#include <array>
#include <variant>
#include "common.hpp"

namespace Kaelum {
Expand All @@ -18,7 +17,7 @@ namespace Kaelum {
Ground = 0,
Escape,
CSI,
SGR,
EscapeSkip, // Consume one byte after ESC ( ) * +
OSC,
Count // Sentinel for dispatch table size
};
Expand All @@ -33,7 +32,11 @@ namespace Kaelum {

std::expected<void, NexusError> process_input(std::span<const uint8_t> data);

void resize(size_t cols, size_t rows);

const Grid& get_grid() const { return grid_; }
size_t cols() const { return cols_; }
size_t rows() const { return rows_; }
std::pair<size_t, size_t> get_cursor() const { return {cursor_x_, cursor_y_}; }

private:
Expand All @@ -45,19 +48,24 @@ namespace Kaelum {
void handle_ground(uint8_t c);
void handle_escape(uint8_t c);
void handle_csi(uint8_t c);
void handle_escape_skip(uint8_t c);
void handle_osc(uint8_t c);

// Sequence helpers
void process_csi(uint8_t final_char);
void parse_sgr(std::span<const uint8_t> params);

size_t cols_ = k_default_cols;
size_t rows_ = k_default_rows;
Grid grid_;
size_t cursor_x_ = 0;
size_t cursor_y_ = 0;
State current_state_ = State::Ground;
std::vector<uint8_t> sequence_buffer_;
char csi_prefix_ = 0; // '?' for DEC private, '>' for secondary DA, 0 for standard

void move_cursor(int dx, int dy);
void scroll_up();
void set_cell(char32_t cp, Color fg, Color bg, uint32_t attrs);
void clear_screen();
};
Expand Down
56 changes: 53 additions & 3 deletions include/sigil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ struct SigilVertex {

struct GlyphRect {
float u0, v0, u1, v1;
// Pixel-space metrics for positioning within a cell
int32_t bearing_x, bearing_y;
uint32_t width, height, advance;
};

// Wayland type aliases to avoid namespace collisions
Expand Down Expand Up @@ -69,25 +72,53 @@ namespace Kaelum {
*/
void set_keyboard_callback(std::function<void(uint32_t key, bool pressed)> callback);

/**
* @brief Sets a callback for window resize events (cols, rows, width_px, height_px).
*/
void set_resize_callback(std::function<void(uint32_t cols, uint32_t rows, uint32_t wpx, uint32_t hpx)> callback) {
resize_callback_ = std::move(callback);
}

void set_cell_size(uint32_t w, uint32_t h) { cell_width_ = w; cell_height_ = h; }

bool should_close() const { return should_close_; }
void request_close() { should_close_ = true; }
void frame_done() { frame_pending_ = false; frame_callback_ = nullptr; }
uint32_t configured_width() const { return configured_width_; }
uint32_t configured_height() const { return configured_height_; }

/**
* @brief Polls Wayland events and updates surface state.
*/
void poll_events();

void prepare_read() { wl_display_prepare_read(display_); }
bool prepare_read() { return wl_display_prepare_read(display_) == 0; }
void cancel_read() { wl_display_cancel_read(display_); }
void dispatch_pending() { wl_display_dispatch_pending(display_); }
void flush() { wl_display_flush(display_); }

/**
* @brief Returns the Wayland display file descriptor.
*/
int get_display_fd() const { return wl_display_get_fd(display_); }
WaylandSurface* get_wl_surface() const { return wl_surface_; }


/**
* @brief Handles window resize events.
*/
void on_resize(uint32_t width, uint32_t height);

/**
* @brief Called from xdg_toplevel configure to store dimensions.
*/
void handle_configure(uint32_t width, uint32_t height);

/**
* @brief Process any pending resize (deferred from configure events).
*/
void process_pending_resize();


private:
friend void registry_handle_global(void* data, struct wl_registry* registry, uint32_t id, const char* interface, uint32_t version);
Expand All @@ -101,6 +132,7 @@ namespace Kaelum {
XdgToplevel* xdg_toplevel_ = nullptr;
struct wl_seat* seat_ = nullptr;
struct wl_keyboard* keyboard_ = nullptr;
struct wl_pointer* pointer_ = nullptr;

// Vulkan handles
VkInstance instance_ = VK_NULL_HANDLE;
Expand All @@ -110,12 +142,23 @@ namespace Kaelum {
VkSurfaceKHR vk_surface_ = VK_NULL_HANDLE;
VkSwapchainKHR swapchain_ = VK_NULL_HANDLE;
VkExtent2D extent_ = {0, 0};
uint32_t configured_width_ = 800;
uint32_t configured_height_ = 600;
bool initial_configure_done_ = false;
bool needs_resize_ = false;
bool should_close_ = false;
bool frame_pending_ = false;
uint32_t render_diag_count_ = 0;
struct wl_callback* frame_callback_ = nullptr;
std::vector<VkImage> swapchain_images_;
std::vector<VkImageView> swapchain_image_views_;
std::vector<VkFramebuffer> swapchain_framebuffers_;
VkRenderPass render_pass_ = VK_NULL_HANDLE;
VkPipeline graphics_pipeline_ = VK_NULL_HANDLE;
VkPipelineLayout pipeline_layout_ = VK_NULL_HANDLE;
VkDescriptorSetLayout descriptor_set_layout_ = VK_NULL_HANDLE;
VkFormat swapchain_format_ = VK_FORMAT_B8G8R8A8_SRGB;
uint32_t graphics_queue_family_ = 0;

// Command buffers
VkCommandPool command_pool_ = VK_NULL_HANDLE;
Expand All @@ -125,12 +168,12 @@ namespace Kaelum {
VkSemaphore image_available_semaphore_ = VK_NULL_HANDLE;
VkSemaphore render_finished_semaphore_ = VK_NULL_HANDLE;
VkFence in_flight_fence_ = VK_NULL_HANDLE;
uint32_t current_frame_ = 0;

// Vertex Buffer
VkBuffer vertex_buffer_ = VK_NULL_HANDLE;
VkDeviceMemory vertex_buffer_memory_ = VK_NULL_HANDLE;
void* vertex_buffer_mapped_ = nullptr;
size_t vertex_buffer_capacity_ = 0;

// Glyph Atlas
VkImage glyph_atlas_image_ = VK_NULL_HANDLE;
Expand All @@ -143,6 +186,13 @@ namespace Kaelum {
VkDescriptorPool descriptor_pool_ = VK_NULL_HANDLE;
VkDescriptorSet descriptor_set_ = VK_NULL_HANDLE;

// Cell dimensions for resize calculation
uint32_t cell_width_ = 8;
uint32_t cell_height_ = 14;

// Callbacks
std::function<void(uint32_t, uint32_t, uint32_t, uint32_t)> resize_callback_;

// Intel Level Zero handles (opaque pointers)
void* lz_driver_ = nullptr;
void* lz_device_ = nullptr;
Expand All @@ -151,7 +201,7 @@ namespace Kaelum {
std::expected<void, SigilError> init_wayland();
std::expected<void, SigilError> init_vulkan();
std::expected<void, SigilError> init_level_zero();
std::expected<void, SigilError> create_swapchain();
std::expected<void, SigilError> create_swapchain(VkSwapchainKHR old_swapchain = VK_NULL_HANDLE);
void create_framebuffers();
void create_render_pass();
void create_graphics_pipeline();
Expand Down
6 changes: 3 additions & 3 deletions src/glyph_engine.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include "glyph_engine.hpp"
#include <iostream>
#include <print>
#include <cstring>

Expand Down Expand Up @@ -51,6 +50,7 @@ std::expected<void, GlyphError> GlyphEngine::load_font(const std::string& font_f

FT_Set_Pixel_Sizes(face_, 0, 14);
line_height_ = static_cast<uint32_t>(face_->size->metrics.height >> 6);
cell_width_ = static_cast<uint32_t>(face_->size->metrics.max_advance >> 6);

std::println("GlyphEngine: Loaded font face: {}", reinterpret_cast<const char*>(face_->family_name));

Expand All @@ -73,8 +73,8 @@ std::expected<GlyphData, GlyphError> GlyphEngine::get_glyph(char32_t codepoint)
FT_GlyphSlot slot = face_->glyph;
GlyphData data;
data.metric = {
.bearing_x = static_cast<uint32_t>(slot->bitmap_left),
.bearing_y = static_cast<uint32_t>(slot->bitmap_top),
.bearing_x = static_cast<int32_t>(slot->bitmap_left),
.bearing_y = static_cast<int32_t>(slot->bitmap_top),
.width = static_cast<uint32_t>(slot->bitmap.width),
.height = static_cast<uint32_t>(slot->bitmap.rows),
.advance = static_cast<uint32_t>(slot->advance.x >> 6)
Expand Down
Loading
Loading