Compare commits

..
11 changed files with 38 additions and 1685 deletions

View file

@ -74,12 +74,8 @@ void ComputeShader::Load(const std::filesystem::path& spvPath, DescriptorHeapVul
},
.layout = layout,
};
const VkResult created = vkCreateComputePipelines(
Device::device, Device::pipelineCache, 1, &info, nullptr, &pipeline);
// The module is only an input to pipeline creation; VulkanShader does
// not destroy it, so it is released here either way.
vkDestroyShaderModule(Device::device, shader.shader, nullptr);
Device::CheckVkResult(created);
Device::CheckVkResult(vkCreateComputePipelines(
Device::device, Device::pipelineCache, 1, &info, nullptr, &pipeline));
return;
}
@ -110,10 +106,8 @@ void ComputeShader::Load(const std::filesystem::path& spvPath, DescriptorHeapVul
},
.layout = VK_NULL_HANDLE,
};
const VkResult created = vkCreateComputePipelines(
Device::device, Device::pipelineCache, 1, &info, nullptr, &pipeline);
vkDestroyShaderModule(Device::device, shader.shader, nullptr);
Device::CheckVkResult(created);
Device::CheckVkResult(vkCreateComputePipelines(
Device::device, Device::pipelineCache, 1, &info, nullptr, &pipeline));
}
void ComputeShader::Dispatch(VkCommandBuffer cmd,

View file

@ -15,7 +15,6 @@ module;
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
#include "../lib/fractional-scale-v1.h"
#include "../lib/viewporter.h"
#include "../lib/text-input-unstable-v3-client-protocol.h"
#include <wayland-client.h>
#include <wayland-client-protocol.h>
#include <stdio.h>
@ -304,10 +303,6 @@ void Device::handle_global(void *data, wl_registry *registry, std::uint32_t name
if (dataDeviceManager != nullptr && dataDevice == nullptr) {
dataDevice = wl_data_device_manager_get_data_device(dataDeviceManager, seat);
}
if (textInputManager != nullptr && textInput == nullptr) {
textInput = zwp_text_input_manager_v3_get_text_input(textInputManager, seat);
zwp_text_input_v3_add_listener(textInput, &text_input_listener, nullptr);
}
} else if (compositor == nullptr && strcmp(interface, wl_compositor_interface.name) == 0) {
compositor = reinterpret_cast<wl_compositor*>(wl_registry_bind(registry, name, &wl_compositor_interface, 3));
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
@ -329,135 +324,8 @@ void Device::handle_global(void *data, wl_registry *registry, std::uint32_t name
if (seat != nullptr && dataDevice == nullptr) {
dataDevice = wl_data_device_manager_get_data_device(dataDeviceManager, seat);
}
} else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
textInputManager = reinterpret_cast<zwp_text_input_manager_v3*>(wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1));
if (seat != nullptr && textInput == nullptr) {
textInput = zwp_text_input_manager_v3_get_text_input(textInputManager, seat);
zwp_text_input_v3_add_listener(textInput, &text_input_listener, nullptr);
}
}
}
namespace {
// A finger's slot by id, or a free one for a new finger; nullptr with more
// than ten fingers down.
Crafter::Device::TouchSlot* SlotFor(std::int32_t id) {
Crafter::Device::TouchSlot* free = nullptr;
for (Crafter::Device::TouchSlot& slot : Crafter::Device::touchSlots) {
if (slot.used && slot.id == id) return &slot;
if (!slot.used && free == nullptr) free = &slot;
}
if (free != nullptr) {
free->used = true;
free->id = id;
free->window = nullptr;
}
return free;
}
bool AnyTouch() {
for (const Crafter::Device::TouchSlot& slot : Crafter::Device::touchSlots) {
if (slot.used) return true;
}
return false;
}
// The text input's pending strings live here, as function-local statics:
// an inline static std::string in the interface made clang 23.1 crash
// in ThinLTO for aarch64-alpine-linux-musl.
struct TextInputStrings {
std::string preedit;
std::string shown; // what the window was last told
std::string commit;
};
TextInputStrings& TextStrings() {
static TextInputStrings strings;
return strings;
}
}
void Device::text_input_enter(void*, zwp_text_input_v3*, wl_surface* surface) {
textInputWindow = nullptr;
for (Window* window : windows) {
if (window->surface == surface) textInputWindow = window;
}
if (textInputWindow != nullptr && textInputWindow->textInputActive_) ApplyTextInput(textInputWindow);
}
// The compositor's leave ends the enable and the composition: the window
// hears that its composing text is gone, and the next enter enables anew.
void Device::text_input_leave(void*, zwp_text_input_v3*, wl_surface*) {
Window* window = textInputWindow;
textInputWindow = nullptr;
textInputHasPreedit = textInputHasCommit = textInputHasDelete = false;
TextStrings().preedit.clear();
if (window != nullptr) {
window->textInputEnabled_ = false;
if (!TextStrings().shown.empty()) window->onPreedit.Invoke(std::string_view());
}
TextStrings().shown.clear();
}
void Device::text_input_preedit(void*, zwp_text_input_v3*, const char* text, std::int32_t, std::int32_t) {
TextStrings().preedit = text == nullptr ? "" : text;
textInputHasPreedit = true;
}
void Device::text_input_commit(void*, zwp_text_input_v3*, const char* text) {
TextStrings().commit = text == nullptr ? "" : text;
textInputHasCommit = true;
}
void Device::text_input_delete(void*, zwp_text_input_v3*, std::uint32_t before, std::uint32_t after) {
textInputDeleteBefore = before;
textInputDeleteAfter = after;
textInputHasDelete = true;
}
// The protocol's order: the deletion around the cursor, then the committed
// text at the cursor, then the composing text after it. The pending values
// reset with every done, so a round that set no preedit means an empty one;
// the window hears about it only when that differs from what it shows.
void Device::text_input_done(void*, zwp_text_input_v3*, std::uint32_t) {
Window* window = textInputWindow;
TextInputStrings& strings = TextStrings();
if (!textInputHasPreedit) strings.preedit.clear();
if (window != nullptr) {
if (textInputHasDelete && (textInputDeleteBefore > 0 || textInputDeleteAfter > 0)) window->onTextInputDelete.Invoke({ textInputDeleteBefore, textInputDeleteAfter });
if (textInputHasCommit && !strings.commit.empty()) window->onTextInput.Invoke(strings.commit);
if (strings.preedit != strings.shown || (textInputHasCommit && !strings.commit.empty())) {
strings.shown = strings.preedit;
window->onPreedit.Invoke(strings.preedit);
}
}
textInputHasPreedit = textInputHasCommit = textInputHasDelete = false;
strings.commit.clear();
}
// enable resets the input method's state, so it goes once per focus; a
// caret that moved sends its rectangle alone.
void Device::ApplyTextInput(Window* window) {
if (textInput == nullptr || window == nullptr || textInputWindow != window) return;
if (window->textInputActive_) {
if (!window->textInputEnabled_) zwp_text_input_v3_enable(textInput);
window->textInputEnabled_ = true;
const bool phone = window->textInputPurpose_ == TextInputPurpose::Phone;
zwp_text_input_v3_set_content_type(textInput,
phone ? ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE : (ZWP_TEXT_INPUT_V3_CONTENT_HINT_COMPLETION | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SPELLCHECK | ZWP_TEXT_INPUT_V3_CONTENT_HINT_AUTO_CAPITALIZATION),
phone ? ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE : ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL);
const float s = window->scale > 0.0f ? window->scale : 1.0f;
zwp_text_input_v3_set_cursor_rectangle(textInput,
static_cast<std::int32_t>(window->textInputRect_[0] / s), static_cast<std::int32_t>(window->textInputRect_[1] / s),
std::max(1, static_cast<std::int32_t>(window->textInputRect_[2] / s)), std::max(1, static_cast<std::int32_t>(window->textInputRect_[3] / s)));
} else {
if (!window->textInputEnabled_) return;
window->textInputEnabled_ = false;
zwp_text_input_v3_disable(textInput);
}
zwp_text_input_v3_commit(textInput);
wl_display_flush(display);
}
void Device::handle_global_remove(void* data, wl_registry* registry, uint32_t name) {
@ -573,23 +441,11 @@ void Device::keyboard_keymap(void *data, wl_keyboard *keyboard, uint32_t format,
}
void Device::keyboard_enter(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys) {
for (Window* window : windows) {
if (window->surface == surface) {
keyboardWindow = window;
return;
}
}
keyboardWindow = nullptr;
}
void Device::keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface) {
// Releases happen elsewhere while the focus is away: nothing we still
// hold is held, and a repeat must not keep firing into a window that
// lost the keyboard.
if (keyboardWindow != nullptr) keyboardWindow->heldKeys.clear();
keyboardWindow = nullptr;
keyRepeat.active = false;
keyRepeat.utf8.clear();
}
void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
@ -600,16 +456,12 @@ void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, ui
KeyCode code = key;
xkb_keycode_t xkbKeycode = key + 8;
// No enter for any of our surfaces (a compositor quirk, or a key for a
// window already gone): nowhere to deliver it.
if (keyboardWindow == nullptr) return;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if (keyboardWindow->heldKeys.contains(code)) {
keyboardWindow->onRawKeyHold.Invoke(code);
if (focusedWindow->heldKeys.contains(code)) {
focusedWindow->onRawKeyHold.Invoke(code);
} else {
keyboardWindow->heldKeys.insert(code);
keyboardWindow->onRawKeyDown.Invoke(code);
focusedWindow->heldKeys.insert(code);
focusedWindow->onRawKeyDown.Invoke(code);
}
std::string buf;
@ -619,7 +471,7 @@ void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, ui
if (n > 0 && (unsigned char)buf[0] >= 0x20 && buf[0] != 0x7f) {
buf.resize(n);
utf8 = buf;
keyboardWindow->onTextInput.Invoke(utf8);
focusedWindow->onTextInput.Invoke(utf8);
}
// Replace the active repeat with this key — most recent press wins,
@ -630,8 +482,8 @@ void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, ui
keyRepeat.pressTime = std::chrono::steady_clock::now();
keyRepeat.lastFireTime = keyRepeat.pressTime;
} else {
keyboardWindow->heldKeys.erase(code);
keyboardWindow->onRawKeyUp.Invoke(code);
focusedWindow->heldKeys.erase(code);
focusedWindow->onRawKeyUp.Invoke(code);
// If the released key was the one repeating, stop. Otherwise leave
// the existing repeat alone (user pressed/released a modifier
@ -654,7 +506,7 @@ void Device::keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rat
}
void Device::TickKeyRepeats() {
if (!keyRepeat.active || !keyboardWindow) return;
if (!keyRepeat.active || !focusedWindow) return;
if (keyRepeat.rate <= 0) return;
auto now = std::chrono::steady_clock::now();
@ -669,111 +521,24 @@ void Device::TickKeyRepeats() {
// Catch up — emit one event per missed period so a paused frame doesn't
// make the repeat permanently lag behind.
while (now - keyRepeat.lastFireTime >= period) {
keyboardWindow->onRawKeyDown.Invoke(keyRepeat.key);
keyboardWindow->onRawKeyHold.Invoke(keyRepeat.key);
focusedWindow->onRawKeyDown.Invoke(keyRepeat.key);
focusedWindow->onRawKeyHold.Invoke(keyRepeat.key);
if (!keyRepeat.utf8.empty()) {
keyboardWindow->onTextInput.Invoke(keyRepeat.utf8);
focusedWindow->onTextInput.Invoke(keyRepeat.utf8);
}
keyRepeat.lastFireTime += period;
}
}
void Device::ForgetTouches() {
for (TouchSlot& slot : touchSlots) slot = TouchSlot{};
void Device::seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities) {
seat = seat;
if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
wl_pointer* pointer = wl_seat_get_pointer(seat);
wl_pointer_add_listener(pointer, &pointer_listener, nullptr);
}
// The first finger stands in for the pointer: it enters the window (so the
// clipboard has a serial and Device::focusedWindow a window), presses the
// left button, moves it and releases it. Every finger is also reported as
// such, in physical pixels.
void Device::touch_down(void*, wl_touch*, std::uint32_t serial, std::uint32_t, wl_surface* surface, std::int32_t id, wl_fixed_t x, wl_fixed_t y) {
Window* window = nullptr;
for (Window* candidate : windows) {
if (candidate->surface == surface) window = candidate;
}
if (window == nullptr) return;
touchWindow = window;
window->lastPointerSerial_ = serial;
const Vector<float, 2> pos = Vector<float, 2>(wl_fixed_to_double(x), wl_fixed_to_double(y)) * window->scale;
if (TouchSlot* slot = SlotFor(id)) {
slot->x = pos.x;
slot->y = pos.y;
slot->window = window;
}
window->onTouchDown.Invoke({ id, pos });
if (window->primaryTouchId < 0) {
window->primaryTouchId = id;
focusedWindow = window;
window->currentMousePos = pos;
window->mouseLeftHeld = true;
window->onMouseLeftClick.Invoke();
}
}
void Device::touch_up(void*, wl_touch*, std::uint32_t, std::uint32_t, std::int32_t id) {
if (touchWindow == nullptr) return;
Window* window = touchWindow;
Vector<float, 2> pos = window->currentMousePos;
if (TouchSlot* slot = SlotFor(id)) {
if (slot->window != nullptr) window = slot->window;
pos = Vector<float, 2>(slot->x, slot->y);
*slot = TouchSlot{};
}
window->onTouchUp.Invoke({ id, pos });
if (id == window->primaryTouchId) {
window->primaryTouchId = -1;
window->mouseLeftHeld = false;
window->onMouseLeftRelease.Invoke();
}
if (!AnyTouch()) touchWindow = nullptr;
}
void Device::touch_motion(void*, wl_touch*, std::uint32_t, std::int32_t id, wl_fixed_t x, wl_fixed_t y) {
if (touchWindow == nullptr) return;
Window* window = touchWindow;
TouchSlot* slot = SlotFor(id);
if (slot != nullptr && slot->window != nullptr) window = slot->window;
const Vector<float, 2> pos = Vector<float, 2>(wl_fixed_to_double(x), wl_fixed_to_double(y)) * window->scale;
if (slot != nullptr) {
slot->x = pos.x;
slot->y = pos.y;
}
window->onTouchMove.Invoke({ id, pos });
if (id == window->primaryTouchId) {
window->currentMousePos = pos;
window->onMouseMove.Invoke();
}
}
void Device::touch_frame(void*, wl_touch*) {}
void Device::touch_cancel(void*, wl_touch*) {
if (touchWindow == nullptr) return;
Window* window = touchWindow;
ForgetTouches();
touchWindow = nullptr;
if (window->primaryTouchId >= 0) {
window->primaryTouchId = -1;
window->mouseLeftHeld = false;
window->onMouseLeftRelease.Invoke();
}
window->onTouchCancel.Invoke();
}
// Capabilities come again when a device appears or goes; each object is
// created once, or every event would arrive once per copy.
void Device::seat_handle_capabilities(void*, wl_seat* seat, uint32_t capabilities) {
if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && seatPointer == nullptr) {
seatPointer = wl_seat_get_pointer(seat);
wl_pointer_add_listener(seatPointer, &pointer_listener, nullptr);
}
if ((capabilities & WL_SEAT_CAPABILITY_TOUCH) && wlTouch == nullptr) {
wlTouch = wl_seat_get_touch(seat);
wl_touch_add_listener(wlTouch, &touch_listener, nullptr);
}
if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && seatKeyboard == nullptr) {
seatKeyboard = wl_seat_get_keyboard(seat);
wl_keyboard_add_listener(seatKeyboard, &keyboard_listener, nullptr);
if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
wl_keyboard* keyboard = wl_seat_get_keyboard(seat);
wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr);
}
}

View file

@ -11,7 +11,6 @@ module;
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
#include "../lib/fractional-scale-v1.h"
#include "../lib/viewporter.h"
#include "../lib/text-input-unstable-v3-client-protocol.h"
#include <string.h>
#include <cassert>
#include <linux/input.h>
@ -300,7 +299,7 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height
wl_surface_commit(surface);
decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(Device::manager, xdgToplevel);
zxdg_toplevel_decoration_v1* decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(Device::manager, xdgToplevel);
zxdg_toplevel_decoration_v1_set_mode(decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
wpViewport = wp_viewporter_get_viewport(Device::wpViewporter, surface);
@ -516,58 +515,6 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height
currentMousePos = {0,0};
}
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
Window::~Window() {
// Nothing may still run against the semaphores, fences or images below.
if (Device::device != VK_NULL_HANDLE) vkDeviceWaitIdle(Device::device);
if (Device::device != VK_NULL_HANDLE) {
if (drawCmdBuffers[0] != VK_NULL_HANDLE) vkFreeCommandBuffers(Device::device, Device::commandPool, numFrames, drawCmdBuffers);
for (std::uint8_t i = 0; i < numFrames; i++) {
if (renderFinishedSemaphores[i]) vkDestroySemaphore(Device::device, renderFinishedSemaphores[i], nullptr);
if (waitFences[i]) vkDestroyFence(Device::device, waitFences[i], nullptr);
}
for (std::uint8_t i = 0; i < numAcquireSemaphores; i++) {
if (imageAcquiredSemaphores[i]) vkDestroySemaphore(Device::device, imageAcquiredSemaphores[i], nullptr);
}
if (swapChain != VK_NULL_HANDLE) vkDestroySwapchainKHR(Device::device, swapChain, nullptr);
swapChain = VK_NULL_HANDLE;
}
if (vulkanSurface != VK_NULL_HANDLE) vkDestroySurfaceKHR(Device::instance, vulkanSurface, nullptr);
vulkanSurface = VK_NULL_HANDLE;
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
SetDefaultCursor();
// A pending frame callback would still dispatch into this window;
// destroying the proxy makes libwayland drop its event.
if (cb) wl_callback_destroy(cb);
cb = nullptr;
if (wpViewport) wp_viewport_destroy(wpViewport);
if (wp_scale) {
wp_fractional_scale_v1_destroy(wp_scale);
wp_scale = nullptr;
}
if (decoration) zxdg_toplevel_decoration_v1_destroy(decoration);
if (xdgToplevel) xdg_toplevel_destroy(xdgToplevel);
if (xdgSurface) xdg_surface_destroy(xdgSurface);
if (surface) wl_surface_destroy(surface);
wpViewport = nullptr;
decoration = nullptr;
xdgToplevel = nullptr;
xdgSurface = nullptr;
surface = nullptr;
if (Device::display) wl_display_flush(Device::display);
std::erase(Device::windows, this);
if (Device::focusedWindow == this) Device::focusedWindow = nullptr;
if (Device::keyboardWindow == this) Device::keyboardWindow = nullptr;
if (Device::touchWindow == this) {
Device::touchWindow = nullptr;
Device::ForgetTouches();
}
if (Device::textInputWindow == this) Device::textInputWindow = nullptr;
#endif
}
#endif
void Window::Resize(std::uint32_t newWidth, std::uint32_t newHeight) {
// Skip degenerate resizes. Win32 minimised windows give (0, 0); Wayland
// sometimes echoes the current size in a configure.
@ -621,24 +568,6 @@ void Window::SetTitle(const std::string_view title) {
#endif
}
void Window::SetAppId(const std::string_view id) {
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
xdg_toplevel_set_app_id(xdgToplevel, std::string(id).c_str());
#endif
}
void Window::SetTextInput(bool active, std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height, TextInputPurpose purpose) {
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
textInputActive_ = active;
textInputRect_[0] = x;
textInputRect_[1] = y;
textInputRect_[2] = width;
textInputRect_[3] = height;
textInputPurpose_ = purpose;
Device::ApplyTextInput(this);
#endif
}
void Window::SetCursorImage(std::uint16_t width, std::uint16_t height,
std::uint16_t hotspotX, std::uint16_t hotspotY,
const std::uint8_t* pixels) {
@ -1300,13 +1229,12 @@ void Window::wl_surface_frame_done(void* data, struct wl_callback *cb, uint32_t
wl_callback_destroy(cb);
Window* window = reinterpret_cast<Window*>(data);
// The member names the live callback, so the destructor can cancel it.
if(window->updating) {
window->cb = wl_surface_frame(window->surface);
wl_callback_add_listener(window->cb, &Window::wl_callback_listener, window);
cb = wl_surface_frame(window->surface);
wl_callback_add_listener(cb, &Window::wl_callback_listener, window);
window->Update();
} else {
window->cb = nullptr;
cb = nullptr;
}
}
@ -1640,10 +1568,6 @@ void Window::SetTitle(const std::string_view title) {
Crafter::DomEnv::domSetTitle(title.data(), static_cast<std::int32_t>(title.size()));
}
void Window::SetAppId(const std::string_view) {}
void Window::SetTextInput(bool, std::int32_t, std::int32_t, std::int32_t, std::int32_t, TextInputPurpose) {}
void Window::Resize(std::uint32_t newWidth, std::uint32_t newHeight) {
if (newWidth == 0 || newHeight == 0) return;
if (newWidth == width && newHeight == height) return;

View file

@ -12,7 +12,6 @@ module;
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
#include "../lib/fractional-scale-v1.h"
#include "../lib/viewporter.h"
#include "../lib/text-input-unstable-v3-client-protocol.h"
#include <xkbcommon/xkbcommon.h>
#endif
export module Crafter.Graphics:Device;
@ -50,32 +49,12 @@ export namespace Crafter {
inline static xkb_keymap* xkb_keymap;
inline static wl_compositor* compositor = nullptr;
inline static Window* focusedWindow = nullptr;
// The window with keyboard focus (wl_keyboard.enter/leave), apart from
// the pointer's: a key arrives without the pointer ever having entered
// (a touch phone, a key typed while the pointer hovers elsewhere), so the
// keyboard handlers route by this and never by focusedWindow.
inline static Window* keyboardWindow = nullptr;
inline static wl_shm* shm = nullptr;
inline static wp_viewporter* wpViewporter = nullptr;
inline static xkb_context* xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
inline static xkb_state* xkb_state;
inline static std::vector<Window*> windows;
inline static wl_pointer* wlPointer;
inline static wl_pointer* seatPointer = nullptr;
inline static wl_keyboard* seatKeyboard = nullptr;
inline static wl_touch* wlTouch = nullptr;
inline static Window* touchWindow = nullptr; // the window under the fingers
// The fingers down, by the compositor's id, in physical pixels. Plain
// data: the statics here are constant-initialised on purpose.
struct TouchSlot {
std::int32_t id;
float x;
float y;
bool used;
Window* window;
};
inline static std::array<TouchSlot, 10> touchSlots{};
static void ForgetTouches();
// wl_data_device_manager + wl_data_device drive copy/paste. Bound
// lazily in handle_global; the data device is created once both
// the manager and the seat are present (registry binding order
@ -89,19 +68,6 @@ export namespace Crafter {
// whole detent yet is carried here so smooth-scroll devices add up
// across events. Reset on pointer leave.
inline static double scrollDetentRemainder = 0.0;
// zwp_text_input_v3: one object per seat; the compositor says which
// surface it follows (enter/leave), the events are state that done()
// applies as a whole.
inline static zwp_text_input_manager_v3* textInputManager = nullptr;
inline static zwp_text_input_v3* textInput = nullptr;
inline static Window* textInputWindow = nullptr;
inline static std::uint32_t textInputDeleteBefore = 0;
inline static std::uint32_t textInputDeleteAfter = 0;
inline static bool textInputHasPreedit = false;
inline static bool textInputHasCommit = false;
inline static bool textInputHasDelete = false;
// Sends the window's wanted text-input state when it has the focus.
static void ApplyTextInput(Window* window);
static void seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities);
static void xdg_surface_handle_preferred_scale(void* data, wp_fractional_scale_v1*, std::uint32_t scale);
@ -119,17 +85,6 @@ export namespace Crafter {
static void PointerListenerHandleAxis(void*, wl_pointer*, std::uint32_t time, std::uint32_t axis, wl_fixed_t value);
static void PointerListenerHandleEnter(void* data, wl_pointer* wl_pointer, std::uint32_t serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y);
static void PointerListenerHandleLeave(void*, wl_pointer*, std::uint32_t, wl_surface*);
static void touch_down(void* data, wl_touch* touch, std::uint32_t serial, std::uint32_t time, wl_surface* surface, std::int32_t id, wl_fixed_t x, wl_fixed_t y);
static void touch_up(void* data, wl_touch* touch, std::uint32_t serial, std::uint32_t time, std::int32_t id);
static void touch_motion(void* data, wl_touch* touch, std::uint32_t time, std::int32_t id, wl_fixed_t x, wl_fixed_t y);
static void touch_frame(void* data, wl_touch* touch);
static void touch_cancel(void* data, wl_touch* touch);
static void text_input_enter(void* data, zwp_text_input_v3* textInput, wl_surface* surface);
static void text_input_leave(void* data, zwp_text_input_v3* textInput, wl_surface* surface);
static void text_input_preedit(void* data, zwp_text_input_v3* textInput, const char* text, std::int32_t cursorBegin, std::int32_t cursorEnd);
static void text_input_commit(void* data, zwp_text_input_v3* textInput, const char* text);
static void text_input_delete(void* data, zwp_text_input_v3* textInput, std::uint32_t before, std::uint32_t after);
static void text_input_done(void* data, zwp_text_input_v3* textInput, std::uint32_t serial);
constexpr static wl_pointer_listener pointer_listener = {
.enter = PointerListenerHandleEnter,
@ -138,21 +93,6 @@ export namespace Crafter {
.button = pointer_handle_button,
.axis = PointerListenerHandleAxis,
};
constexpr static wl_touch_listener touch_listener = {
.down = touch_down,
.up = touch_up,
.motion = touch_motion,
.frame = touch_frame,
.cancel = touch_cancel,
};
constexpr static zwp_text_input_v3_listener text_input_listener = {
.enter = text_input_enter,
.leave = text_input_leave,
.preedit_string = text_input_preedit,
.commit_string = text_input_commit,
.delete_surrounding_text = text_input_delete,
.done = text_input_done,
};
constexpr static wl_keyboard_listener keyboard_listener = {
.keymap = keyboard_keymap,
.enter = keyboard_enter,

View file

@ -96,20 +96,6 @@ export namespace Crafter {
bool dirty = false;
DirtyRect dirtyRect;
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
FontAtlas() = default;
FontAtlas(const FontAtlas&) = delete;
FontAtlas& operator=(const FontAtlas&) = delete;
// The image goes with the atlas, once the GPU is done with it; in
// sets mode the owner destroys the heap holding the image's view
// before the atlas.
~FontAtlas() {
if (image.image == VK_NULL_HANDLE) return;
if (Device::queue != VK_NULL_HANDLE) vkQueueWaitIdle(Device::queue);
image.Destroy();
}
#endif
void Initialize(GraphicsCommandBuffer cmd);
// Returns the row-major byte pointer the CPU writes pixels into.

View file

@ -56,8 +56,8 @@ export namespace Crafter {
std::uint16_t width;
std::uint16_t height;
std::uint8_t mipLevels;
VkImage image = VK_NULL_HANDLE;
VkDeviceMemory imageMemory = VK_NULL_HANDLE;
VkImage image;
VkDeviceMemory imageMemory;
VulkanBuffer<PixelType, true> buffer;
// Transient host-visible staging for the compressed Update path. Same
// lifetime contract as Mesh::compressedStaging: the compressed Update
@ -66,7 +66,7 @@ export namespace Crafter {
// submit's frame has cleared instead of pinning it for the image's life.
// Between Updates the handle is null; the next Update's Resize re-creates it.
VulkanBuffer<std::byte, true> compressedStaging;
VkImageView imageView = VK_NULL_HANDLE;
VkImageView imageView;
VkDescriptorImageInfo descriptor;
// Pipeline stage that samples this image after upload. Set once at
// Create and reused by every Update so the upload barrier's dst (and
@ -378,9 +378,6 @@ export namespace Crafter {
vkDestroyImageView(Device::device, imageView, nullptr);
vkDestroyImage(Device::device, image, nullptr);
vkFreeMemory(Device::device, imageMemory, nullptr);
imageView = VK_NULL_HANDLE;
image = VK_NULL_HANDLE;
imageMemory = VK_NULL_HANDLE;
// Free any staging this image still owns: a streamed image's
// persistent `buffer`, or either staging buffer on an image
// destroyed before its first upload released them (issue #114).

View file

@ -18,22 +18,6 @@ export namespace Crafter {
Vector<float, 2> mouseDelta;
};
// One finger on a touchscreen, in the window's physical pixels; the id is
// the compositor's and stays with the finger until it lifts.
struct TouchPoint {
std::int32_t id;
Vector<float, 2> position;
};
// The input method asks for bytes around the cursor to go (text-input-v3
// counts UTF-8 bytes).
struct TextDelete {
std::uint32_t beforeBytes;
std::uint32_t afterBytes;
};
enum class TextInputPurpose : std::uint8_t { Normal, Phone };
struct ScaleData2D {
Vector<std::int16_t, 2, 2> position;
Vector<std::int16_t, 2, 2> size;

View file

@ -13,7 +13,6 @@ module;
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
#include "../lib/fractional-scale-v1.h"
#include "../lib/viewporter.h"
#include "../lib/text-input-unstable-v3-client-protocol.h"
#include <string.h>
#include <linux/input.h>
#include <sys/mman.h>
@ -99,19 +98,6 @@ export namespace Crafter {
// touchpads, free-spinning wheels) is accumulated backend-side
// until a whole detent is reached.
Event<std::uint32_t> onMouseScroll;
// Touch, from the seat's wl_touch. The first finger down also drives
// currentMousePos and the left-button events until it lifts, so a
// pointer-only application works on a touchscreen as it is.
Event<TouchPoint> onTouchDown;
Event<TouchPoint> onTouchMove;
Event<TouchPoint> onTouchUp;
Event<void> onTouchCancel;
std::int32_t primaryTouchId = -1;
// Text input (zwp_text_input_v3): the input method's composing text
// (an empty string clears it) and its deletions around the cursor;
// the text it commits arrives as onTextInput, like a key's.
Event<const std::string_view> onPreedit;
Event<TextDelete> onTextInputDelete;
Vector<float, 2> currentMousePos;
Vector<float, 2> lastMousePos;
Vector<float, 2> mouseDelta;
@ -124,19 +110,16 @@ export namespace Crafter {
Window(Window&) = delete;
Window(Window&&) = delete;
Window& operator=(const Window&) = delete;
#ifdef CRAFTER_GRAPHICS_WINDOW_DOM
// DOM mode keeps a process-global pointer to the live Window so
// the JS bridge can deliver document/window events back. If the
// Window is destroyed (e.g. it was stack-allocated in main and
// main returned), the global must be cleared so subsequent
// browser callbacks become no-ops instead of dereferencing
// freed memory. Native builds release what the constructor made:
// after a device wait, the command buffers, semaphores and fences,
// the swapchain and the Vulkan surface, then (Wayland) the cursor,
// viewport, fractional scale, decoration, xdg toplevel, xdg surface
// and wl_surface, and the window leaves Device::windows and the
// focus pointers — so a window can be closed and another created,
// which a resident app does on every hide and show.
// freed memory. Native builds have nothing to clean up at the
// Window level (Vulkan resources teardown is the user's job).
~Window();
#endif
// Enter the platform event loop and never return. In DOM mode this
// hands the loop to requestAnimationFrame and _Exit(0)s so the wasm
@ -157,14 +140,6 @@ export namespace Crafter {
void StartUpdate();
void StopUpdate();
void SetTitle(const std::string_view title);
// The xdg app id, which the compositor matches to a .desktop file
// for the icon, the task switcher and activation.
void SetAppId(const std::string_view id);
// Tells the compositor's input method that a text field has focus
// (an on-screen keyboard opens) and where its caret is, in physical
// pixels, or that none has. Remembered: it is applied again whenever
// the keyboard focus comes back to this window.
void SetTextInput(bool active, std::int32_t x = 0, std::int32_t y = 0, std::int32_t width = 0, std::int32_t height = 0, TextInputPurpose purpose = TextInputPurpose::Normal);
void Resize(std::uint32_t width, std::uint32_t height);
void Render();
void Update();
@ -216,7 +191,6 @@ export namespace Crafter {
std::int32_t pendingLogicalWidth = 0;
std::int32_t pendingLogicalHeight = 0;
xdg_toplevel* xdgToplevel = nullptr;
zxdg_toplevel_decoration_v1* decoration = nullptr;
wp_viewport* wpViewport = nullptr;
wl_surface* surface = nullptr;
xdg_surface* xdgSurface = nullptr;
@ -233,10 +207,6 @@ export namespace Crafter {
// Needed so `SetCursorImage` can re-issue `wl_pointer_set_cursor`
// mid-session (the hotspot only updates when set_cursor is recalled).
std::uint32_t lastPointerSerial_ = 0;
bool textInputActive_ = false;
bool textInputEnabled_ = false; // enable sent and not taken back since
std::int32_t textInputRect_[4] = { 0, 0, 0, 0 };
TextInputPurpose textInputPurpose_ = TextInputPurpose::Normal;
static void xdg_surface_handle_preferred_scale(void* data, wp_fractional_scale_v1*, std::uint32_t scale);
static void wl_surface_frame_done(void *data, wl_callback *cb, uint32_t time);
@ -257,7 +227,7 @@ export namespace Crafter {
constexpr static wp_fractional_scale_v1_listener wp_fractional_scale_v1_listener = {
.preferred_scale = xdg_surface_handle_preferred_scale,
};
wp_fractional_scale_v1* wp_scale = nullptr;
inline static wp_fractional_scale_v1* wp_scale = nullptr;
#endif
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
@ -299,7 +269,7 @@ export namespace Crafter {
// lazily in Render() (post-acquire) rather than up front.
std::array<bool, numFrames> imageInitialised{};
std::thread thread;
VkCommandBuffer drawCmdBuffers[numFrames] = {};
VkCommandBuffer drawCmdBuffers[numFrames];
// Per-frame Vulkan info structs whose contents are almost entirely
// invariant. Initialised once in the constructor; Render() patches only
// the few fields that change each frame (the two barriers' image and

File diff suppressed because it is too large Load diff

View file

@ -1,103 +0,0 @@
/* Generated by wayland-scanner 1.26.0 */
/*
* Copyright © 2012, 2013 Intel Corporation
* Copyright © 2015, 2016 Jan Arne Petersen
* Copyright © 2017, 2018 Red Hat, Inc.
* Copyright © 2018 Purism SPC
*
* Permission to use, copy, modify, distribute, and sell this
* software and its documentation for any purpose is hereby granted
* without fee, provided that the above copyright notice appear in
* all copies and that both that copyright notice and this permission
* notice appear in supporting documentation, and that the name of
* the copyright holders not be used in advertising or publicity
* pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "wayland-util.h"
#ifndef __has_attribute
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
#else
#define WL_PRIVATE
#endif
extern const struct wl_interface wl_seat_interface;
extern const struct wl_interface wl_surface_interface;
extern const struct wl_interface zwp_text_input_v3_interface;
static const struct wl_interface *text_input_unstable_v3_types[] = {
NULL,
NULL,
NULL,
NULL,
&wl_surface_interface,
&wl_surface_interface,
&zwp_text_input_v3_interface,
&wl_seat_interface,
};
static const struct wl_message zwp_text_input_v3_requests[] = {
{ "destroy", "", text_input_unstable_v3_types + 0 },
{ "enable", "", text_input_unstable_v3_types + 0 },
{ "disable", "", text_input_unstable_v3_types + 0 },
{ "set_surrounding_text", "sii", text_input_unstable_v3_types + 0 },
{ "set_text_change_cause", "u", text_input_unstable_v3_types + 0 },
{ "set_content_type", "uu", text_input_unstable_v3_types + 0 },
{ "set_cursor_rectangle", "iiii", text_input_unstable_v3_types + 0 },
{ "commit", "", text_input_unstable_v3_types + 0 },
{ "set_available_actions", "2a", text_input_unstable_v3_types + 0 },
{ "show_input_panel", "2", text_input_unstable_v3_types + 0 },
{ "hide_input_panel", "2", text_input_unstable_v3_types + 0 },
};
static const struct wl_message zwp_text_input_v3_events[] = {
{ "enter", "o", text_input_unstable_v3_types + 4 },
{ "leave", "o", text_input_unstable_v3_types + 5 },
{ "preedit_string", "?sii", text_input_unstable_v3_types + 0 },
{ "commit_string", "?s", text_input_unstable_v3_types + 0 },
{ "delete_surrounding_text", "uu", text_input_unstable_v3_types + 0 },
{ "done", "u", text_input_unstable_v3_types + 0 },
{ "action", "2uu", text_input_unstable_v3_types + 0 },
{ "language", "2s", text_input_unstable_v3_types + 0 },
{ "preedit_hint", "2uuu", text_input_unstable_v3_types + 0 },
};
WL_PRIVATE const struct wl_interface zwp_text_input_v3_interface = {
"zwp_text_input_v3", 2,
11, zwp_text_input_v3_requests,
9, zwp_text_input_v3_events,
};
static const struct wl_message zwp_text_input_manager_v3_requests[] = {
{ "destroy", "", text_input_unstable_v3_types + 0 },
{ "get_text_input", "no", text_input_unstable_v3_types + 6 },
};
WL_PRIVATE const struct wl_interface zwp_text_input_manager_v3_interface = {
"zwp_text_input_manager_v3", 2,
2, zwp_text_input_manager_v3_requests,
0, NULL,
};

View file

@ -89,7 +89,6 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
cfg.cFiles.push_back("lib/wayland-xdg-decoration-unstable-v1-client-protocol");
cfg.cFiles.push_back("lib/fractional-scale-v1");
cfg.cFiles.push_back("lib/viewporter");
cfg.cFiles.push_back("lib/text-input-unstable-v3-protocol");
}
// Vulkan is the only renderer on native targets. Software fallback is