Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
1c30b1bbf7 |
|||
|
1498a5bf4b |
|||
|
c8b5739e1a |
|||
|
cc04d4e121 |
|||
|
0d8287c122 |
|||
|
678f38964f |
|||
|
aed6142cce |
|||
|
b16f1e99d5 |
|||
|
6f5b192078 |
|||
|
b412640fac |
|||
|
bfc5e108e5 |
11 changed files with 1685 additions and 38 deletions
|
|
@ -74,8 +74,12 @@ void ComputeShader::Load(const std::filesystem::path& spvPath, DescriptorHeapVul
|
||||||
},
|
},
|
||||||
.layout = layout,
|
.layout = layout,
|
||||||
};
|
};
|
||||||
Device::CheckVkResult(vkCreateComputePipelines(
|
const VkResult created = vkCreateComputePipelines(
|
||||||
Device::device, Device::pipelineCache, 1, &info, nullptr, &pipeline));
|
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);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,8 +110,10 @@ void ComputeShader::Load(const std::filesystem::path& spvPath, DescriptorHeapVul
|
||||||
},
|
},
|
||||||
.layout = VK_NULL_HANDLE,
|
.layout = VK_NULL_HANDLE,
|
||||||
};
|
};
|
||||||
Device::CheckVkResult(vkCreateComputePipelines(
|
const VkResult created = vkCreateComputePipelines(
|
||||||
Device::device, Device::pipelineCache, 1, &info, nullptr, &pipeline));
|
Device::device, Device::pipelineCache, 1, &info, nullptr, &pipeline);
|
||||||
|
vkDestroyShaderModule(Device::device, shader.shader, nullptr);
|
||||||
|
Device::CheckVkResult(created);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComputeShader::Dispatch(VkCommandBuffer cmd,
|
void ComputeShader::Dispatch(VkCommandBuffer cmd,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ module;
|
||||||
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
||||||
#include "../lib/fractional-scale-v1.h"
|
#include "../lib/fractional-scale-v1.h"
|
||||||
#include "../lib/viewporter.h"
|
#include "../lib/viewporter.h"
|
||||||
|
#include "../lib/text-input-unstable-v3-client-protocol.h"
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-client-protocol.h>
|
#include <wayland-client-protocol.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -303,6 +304,10 @@ void Device::handle_global(void *data, wl_registry *registry, std::uint32_t name
|
||||||
if (dataDeviceManager != nullptr && dataDevice == nullptr) {
|
if (dataDeviceManager != nullptr && dataDevice == nullptr) {
|
||||||
dataDevice = wl_data_device_manager_get_data_device(dataDeviceManager, seat);
|
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) {
|
} 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));
|
compositor = reinterpret_cast<wl_compositor*>(wl_registry_bind(registry, name, &wl_compositor_interface, 3));
|
||||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
||||||
|
|
@ -324,9 +329,136 @@ void Device::handle_global(void *data, wl_registry *registry, std::uint32_t name
|
||||||
if (seat != nullptr && dataDevice == nullptr) {
|
if (seat != nullptr && dataDevice == nullptr) {
|
||||||
dataDevice = wl_data_device_manager_get_data_device(dataDeviceManager, seat);
|
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) {
|
void Device::handle_global_remove(void* data, wl_registry* registry, uint32_t name) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -441,11 +573,23 @@ 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) {
|
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) {
|
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) {
|
void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
||||||
|
|
@ -456,12 +600,16 @@ void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, ui
|
||||||
KeyCode code = key;
|
KeyCode code = key;
|
||||||
xkb_keycode_t xkbKeycode = key + 8;
|
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 (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||||
if (focusedWindow->heldKeys.contains(code)) {
|
if (keyboardWindow->heldKeys.contains(code)) {
|
||||||
focusedWindow->onRawKeyHold.Invoke(code);
|
keyboardWindow->onRawKeyHold.Invoke(code);
|
||||||
} else {
|
} else {
|
||||||
focusedWindow->heldKeys.insert(code);
|
keyboardWindow->heldKeys.insert(code);
|
||||||
focusedWindow->onRawKeyDown.Invoke(code);
|
keyboardWindow->onRawKeyDown.Invoke(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string buf;
|
std::string buf;
|
||||||
|
|
@ -471,7 +619,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) {
|
if (n > 0 && (unsigned char)buf[0] >= 0x20 && buf[0] != 0x7f) {
|
||||||
buf.resize(n);
|
buf.resize(n);
|
||||||
utf8 = buf;
|
utf8 = buf;
|
||||||
focusedWindow->onTextInput.Invoke(utf8);
|
keyboardWindow->onTextInput.Invoke(utf8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the active repeat with this key — most recent press wins,
|
// Replace the active repeat with this key — most recent press wins,
|
||||||
|
|
@ -482,8 +630,8 @@ void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, ui
|
||||||
keyRepeat.pressTime = std::chrono::steady_clock::now();
|
keyRepeat.pressTime = std::chrono::steady_clock::now();
|
||||||
keyRepeat.lastFireTime = keyRepeat.pressTime;
|
keyRepeat.lastFireTime = keyRepeat.pressTime;
|
||||||
} else {
|
} else {
|
||||||
focusedWindow->heldKeys.erase(code);
|
keyboardWindow->heldKeys.erase(code);
|
||||||
focusedWindow->onRawKeyUp.Invoke(code);
|
keyboardWindow->onRawKeyUp.Invoke(code);
|
||||||
|
|
||||||
// If the released key was the one repeating, stop. Otherwise leave
|
// If the released key was the one repeating, stop. Otherwise leave
|
||||||
// the existing repeat alone (user pressed/released a modifier
|
// the existing repeat alone (user pressed/released a modifier
|
||||||
|
|
@ -506,7 +654,7 @@ void Device::keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rat
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::TickKeyRepeats() {
|
void Device::TickKeyRepeats() {
|
||||||
if (!keyRepeat.active || !focusedWindow) return;
|
if (!keyRepeat.active || !keyboardWindow) return;
|
||||||
if (keyRepeat.rate <= 0) return;
|
if (keyRepeat.rate <= 0) return;
|
||||||
|
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
|
@ -521,24 +669,111 @@ void Device::TickKeyRepeats() {
|
||||||
// Catch up — emit one event per missed period so a paused frame doesn't
|
// Catch up — emit one event per missed period so a paused frame doesn't
|
||||||
// make the repeat permanently lag behind.
|
// make the repeat permanently lag behind.
|
||||||
while (now - keyRepeat.lastFireTime >= period) {
|
while (now - keyRepeat.lastFireTime >= period) {
|
||||||
focusedWindow->onRawKeyDown.Invoke(keyRepeat.key);
|
keyboardWindow->onRawKeyDown.Invoke(keyRepeat.key);
|
||||||
focusedWindow->onRawKeyHold.Invoke(keyRepeat.key);
|
keyboardWindow->onRawKeyHold.Invoke(keyRepeat.key);
|
||||||
if (!keyRepeat.utf8.empty()) {
|
if (!keyRepeat.utf8.empty()) {
|
||||||
focusedWindow->onTextInput.Invoke(keyRepeat.utf8);
|
keyboardWindow->onTextInput.Invoke(keyRepeat.utf8);
|
||||||
}
|
}
|
||||||
keyRepeat.lastFireTime += period;
|
keyRepeat.lastFireTime += period;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities) {
|
void Device::ForgetTouches() {
|
||||||
seat = seat;
|
for (TouchSlot& slot : touchSlots) slot = TouchSlot{};
|
||||||
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_KEYBOARD) {
|
if ((capabilities & WL_SEAT_CAPABILITY_TOUCH) && wlTouch == nullptr) {
|
||||||
wl_keyboard* keyboard = wl_seat_get_keyboard(seat);
|
wlTouch = wl_seat_get_touch(seat);
|
||||||
wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ module;
|
||||||
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
||||||
#include "../lib/fractional-scale-v1.h"
|
#include "../lib/fractional-scale-v1.h"
|
||||||
#include "../lib/viewporter.h"
|
#include "../lib/viewporter.h"
|
||||||
|
#include "../lib/text-input-unstable-v3-client-protocol.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
|
@ -299,7 +300,7 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height
|
||||||
|
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
zxdg_toplevel_decoration_v1* decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(Device::manager, xdgToplevel);
|
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);
|
zxdg_toplevel_decoration_v1_set_mode(decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
||||||
|
|
||||||
wpViewport = wp_viewporter_get_viewport(Device::wpViewporter, surface);
|
wpViewport = wp_viewporter_get_viewport(Device::wpViewporter, surface);
|
||||||
|
|
@ -515,6 +516,58 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height
|
||||||
currentMousePos = {0,0};
|
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) {
|
void Window::Resize(std::uint32_t newWidth, std::uint32_t newHeight) {
|
||||||
// Skip degenerate resizes. Win32 minimised windows give (0, 0); Wayland
|
// Skip degenerate resizes. Win32 minimised windows give (0, 0); Wayland
|
||||||
// sometimes echoes the current size in a configure.
|
// sometimes echoes the current size in a configure.
|
||||||
|
|
@ -568,6 +621,24 @@ void Window::SetTitle(const std::string_view title) {
|
||||||
#endif
|
#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,
|
void Window::SetCursorImage(std::uint16_t width, std::uint16_t height,
|
||||||
std::uint16_t hotspotX, std::uint16_t hotspotY,
|
std::uint16_t hotspotX, std::uint16_t hotspotY,
|
||||||
const std::uint8_t* pixels) {
|
const std::uint8_t* pixels) {
|
||||||
|
|
@ -1229,12 +1300,13 @@ void Window::wl_surface_frame_done(void* data, struct wl_callback *cb, uint32_t
|
||||||
wl_callback_destroy(cb);
|
wl_callback_destroy(cb);
|
||||||
Window* window = reinterpret_cast<Window*>(data);
|
Window* window = reinterpret_cast<Window*>(data);
|
||||||
|
|
||||||
|
// The member names the live callback, so the destructor can cancel it.
|
||||||
if(window->updating) {
|
if(window->updating) {
|
||||||
cb = wl_surface_frame(window->surface);
|
window->cb = wl_surface_frame(window->surface);
|
||||||
wl_callback_add_listener(cb, &Window::wl_callback_listener, window);
|
wl_callback_add_listener(window->cb, &Window::wl_callback_listener, window);
|
||||||
window->Update();
|
window->Update();
|
||||||
} else {
|
} else {
|
||||||
cb = nullptr;
|
window->cb = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1568,6 +1640,10 @@ void Window::SetTitle(const std::string_view title) {
|
||||||
Crafter::DomEnv::domSetTitle(title.data(), static_cast<std::int32_t>(title.size()));
|
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) {
|
void Window::Resize(std::uint32_t newWidth, std::uint32_t newHeight) {
|
||||||
if (newWidth == 0 || newHeight == 0) return;
|
if (newWidth == 0 || newHeight == 0) return;
|
||||||
if (newWidth == width && newHeight == height) return;
|
if (newWidth == width && newHeight == height) return;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ module;
|
||||||
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
||||||
#include "../lib/fractional-scale-v1.h"
|
#include "../lib/fractional-scale-v1.h"
|
||||||
#include "../lib/viewporter.h"
|
#include "../lib/viewporter.h"
|
||||||
|
#include "../lib/text-input-unstable-v3-client-protocol.h"
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
#endif
|
#endif
|
||||||
export module Crafter.Graphics:Device;
|
export module Crafter.Graphics:Device;
|
||||||
|
|
@ -49,12 +50,32 @@ export namespace Crafter {
|
||||||
inline static xkb_keymap* xkb_keymap;
|
inline static xkb_keymap* xkb_keymap;
|
||||||
inline static wl_compositor* compositor = nullptr;
|
inline static wl_compositor* compositor = nullptr;
|
||||||
inline static Window* focusedWindow = 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 wl_shm* shm = nullptr;
|
||||||
inline static wp_viewporter* wpViewporter = nullptr;
|
inline static wp_viewporter* wpViewporter = nullptr;
|
||||||
inline static xkb_context* xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
inline static xkb_context* xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
inline static xkb_state* xkb_state;
|
inline static xkb_state* xkb_state;
|
||||||
inline static std::vector<Window*> windows;
|
inline static std::vector<Window*> windows;
|
||||||
inline static wl_pointer* wlPointer;
|
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
|
// wl_data_device_manager + wl_data_device drive copy/paste. Bound
|
||||||
// lazily in handle_global; the data device is created once both
|
// lazily in handle_global; the data device is created once both
|
||||||
// the manager and the seat are present (registry binding order
|
// the manager and the seat are present (registry binding order
|
||||||
|
|
@ -68,6 +89,19 @@ export namespace Crafter {
|
||||||
// whole detent yet is carried here so smooth-scroll devices add up
|
// whole detent yet is carried here so smooth-scroll devices add up
|
||||||
// across events. Reset on pointer leave.
|
// across events. Reset on pointer leave.
|
||||||
inline static double scrollDetentRemainder = 0.0;
|
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 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);
|
static void xdg_surface_handle_preferred_scale(void* data, wp_fractional_scale_v1*, std::uint32_t scale);
|
||||||
|
|
@ -85,6 +119,17 @@ export namespace Crafter {
|
||||||
static void PointerListenerHandleAxis(void*, wl_pointer*, std::uint32_t time, std::uint32_t axis, wl_fixed_t value);
|
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 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 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 = {
|
constexpr static wl_pointer_listener pointer_listener = {
|
||||||
.enter = PointerListenerHandleEnter,
|
.enter = PointerListenerHandleEnter,
|
||||||
|
|
@ -93,6 +138,21 @@ export namespace Crafter {
|
||||||
.button = pointer_handle_button,
|
.button = pointer_handle_button,
|
||||||
.axis = PointerListenerHandleAxis,
|
.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 = {
|
constexpr static wl_keyboard_listener keyboard_listener = {
|
||||||
.keymap = keyboard_keymap,
|
.keymap = keyboard_keymap,
|
||||||
.enter = keyboard_enter,
|
.enter = keyboard_enter,
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,20 @@ export namespace Crafter {
|
||||||
bool dirty = false;
|
bool dirty = false;
|
||||||
DirtyRect dirtyRect;
|
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);
|
void Initialize(GraphicsCommandBuffer cmd);
|
||||||
|
|
||||||
// Returns the row-major byte pointer the CPU writes pixels into.
|
// Returns the row-major byte pointer the CPU writes pixels into.
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,8 @@ export namespace Crafter {
|
||||||
std::uint16_t width;
|
std::uint16_t width;
|
||||||
std::uint16_t height;
|
std::uint16_t height;
|
||||||
std::uint8_t mipLevels;
|
std::uint8_t mipLevels;
|
||||||
VkImage image;
|
VkImage image = VK_NULL_HANDLE;
|
||||||
VkDeviceMemory imageMemory;
|
VkDeviceMemory imageMemory = VK_NULL_HANDLE;
|
||||||
VulkanBuffer<PixelType, true> buffer;
|
VulkanBuffer<PixelType, true> buffer;
|
||||||
// Transient host-visible staging for the compressed Update path. Same
|
// Transient host-visible staging for the compressed Update path. Same
|
||||||
// lifetime contract as Mesh::compressedStaging: the compressed Update
|
// 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.
|
// 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.
|
// Between Updates the handle is null; the next Update's Resize re-creates it.
|
||||||
VulkanBuffer<std::byte, true> compressedStaging;
|
VulkanBuffer<std::byte, true> compressedStaging;
|
||||||
VkImageView imageView;
|
VkImageView imageView = VK_NULL_HANDLE;
|
||||||
VkDescriptorImageInfo descriptor;
|
VkDescriptorImageInfo descriptor;
|
||||||
// Pipeline stage that samples this image after upload. Set once at
|
// Pipeline stage that samples this image after upload. Set once at
|
||||||
// Create and reused by every Update so the upload barrier's dst (and
|
// Create and reused by every Update so the upload barrier's dst (and
|
||||||
|
|
@ -378,6 +378,9 @@ export namespace Crafter {
|
||||||
vkDestroyImageView(Device::device, imageView, nullptr);
|
vkDestroyImageView(Device::device, imageView, nullptr);
|
||||||
vkDestroyImage(Device::device, image, nullptr);
|
vkDestroyImage(Device::device, image, nullptr);
|
||||||
vkFreeMemory(Device::device, imageMemory, 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
|
// Free any staging this image still owns: a streamed image's
|
||||||
// persistent `buffer`, or either staging buffer on an image
|
// persistent `buffer`, or either staging buffer on an image
|
||||||
// destroyed before its first upload released them (issue #114).
|
// destroyed before its first upload released them (issue #114).
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,22 @@ export namespace Crafter {
|
||||||
Vector<float, 2> mouseDelta;
|
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 {
|
struct ScaleData2D {
|
||||||
Vector<std::int16_t, 2, 2> position;
|
Vector<std::int16_t, 2, 2> position;
|
||||||
Vector<std::int16_t, 2, 2> size;
|
Vector<std::int16_t, 2, 2> size;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ module;
|
||||||
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
||||||
#include "../lib/fractional-scale-v1.h"
|
#include "../lib/fractional-scale-v1.h"
|
||||||
#include "../lib/viewporter.h"
|
#include "../lib/viewporter.h"
|
||||||
|
#include "../lib/text-input-unstable-v3-client-protocol.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
@ -98,6 +99,19 @@ export namespace Crafter {
|
||||||
// touchpads, free-spinning wheels) is accumulated backend-side
|
// touchpads, free-spinning wheels) is accumulated backend-side
|
||||||
// until a whole detent is reached.
|
// until a whole detent is reached.
|
||||||
Event<std::uint32_t> onMouseScroll;
|
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> currentMousePos;
|
||||||
Vector<float, 2> lastMousePos;
|
Vector<float, 2> lastMousePos;
|
||||||
Vector<float, 2> mouseDelta;
|
Vector<float, 2> mouseDelta;
|
||||||
|
|
@ -110,16 +124,19 @@ export namespace Crafter {
|
||||||
Window(Window&) = delete;
|
Window(Window&) = delete;
|
||||||
Window(Window&&) = delete;
|
Window(Window&&) = delete;
|
||||||
Window& operator=(const Window&) = delete;
|
Window& operator=(const Window&) = delete;
|
||||||
#ifdef CRAFTER_GRAPHICS_WINDOW_DOM
|
|
||||||
// DOM mode keeps a process-global pointer to the live Window so
|
// DOM mode keeps a process-global pointer to the live Window so
|
||||||
// the JS bridge can deliver document/window events back. If the
|
// the JS bridge can deliver document/window events back. If the
|
||||||
// Window is destroyed (e.g. it was stack-allocated in main and
|
// Window is destroyed (e.g. it was stack-allocated in main and
|
||||||
// main returned), the global must be cleared so subsequent
|
// main returned), the global must be cleared so subsequent
|
||||||
// browser callbacks become no-ops instead of dereferencing
|
// browser callbacks become no-ops instead of dereferencing
|
||||||
// freed memory. Native builds have nothing to clean up at the
|
// freed memory. Native builds release what the constructor made:
|
||||||
// Window level (Vulkan resources teardown is the user's job).
|
// 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.
|
||||||
~Window();
|
~Window();
|
||||||
#endif
|
|
||||||
|
|
||||||
// Enter the platform event loop and never return. In DOM mode this
|
// Enter the platform event loop and never return. In DOM mode this
|
||||||
// hands the loop to requestAnimationFrame and _Exit(0)s so the wasm
|
// hands the loop to requestAnimationFrame and _Exit(0)s so the wasm
|
||||||
|
|
@ -140,6 +157,14 @@ export namespace Crafter {
|
||||||
void StartUpdate();
|
void StartUpdate();
|
||||||
void StopUpdate();
|
void StopUpdate();
|
||||||
void SetTitle(const std::string_view title);
|
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 Resize(std::uint32_t width, std::uint32_t height);
|
||||||
void Render();
|
void Render();
|
||||||
void Update();
|
void Update();
|
||||||
|
|
@ -191,6 +216,7 @@ export namespace Crafter {
|
||||||
std::int32_t pendingLogicalWidth = 0;
|
std::int32_t pendingLogicalWidth = 0;
|
||||||
std::int32_t pendingLogicalHeight = 0;
|
std::int32_t pendingLogicalHeight = 0;
|
||||||
xdg_toplevel* xdgToplevel = nullptr;
|
xdg_toplevel* xdgToplevel = nullptr;
|
||||||
|
zxdg_toplevel_decoration_v1* decoration = nullptr;
|
||||||
wp_viewport* wpViewport = nullptr;
|
wp_viewport* wpViewport = nullptr;
|
||||||
wl_surface* surface = nullptr;
|
wl_surface* surface = nullptr;
|
||||||
xdg_surface* xdgSurface = nullptr;
|
xdg_surface* xdgSurface = nullptr;
|
||||||
|
|
@ -207,6 +233,10 @@ export namespace Crafter {
|
||||||
// Needed so `SetCursorImage` can re-issue `wl_pointer_set_cursor`
|
// Needed so `SetCursorImage` can re-issue `wl_pointer_set_cursor`
|
||||||
// mid-session (the hotspot only updates when set_cursor is recalled).
|
// mid-session (the hotspot only updates when set_cursor is recalled).
|
||||||
std::uint32_t lastPointerSerial_ = 0;
|
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 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);
|
static void wl_surface_frame_done(void *data, wl_callback *cb, uint32_t time);
|
||||||
|
|
@ -227,7 +257,7 @@ export namespace Crafter {
|
||||||
constexpr static wp_fractional_scale_v1_listener wp_fractional_scale_v1_listener = {
|
constexpr static wp_fractional_scale_v1_listener wp_fractional_scale_v1_listener = {
|
||||||
.preferred_scale = xdg_surface_handle_preferred_scale,
|
.preferred_scale = xdg_surface_handle_preferred_scale,
|
||||||
};
|
};
|
||||||
inline static wp_fractional_scale_v1* wp_scale = nullptr;
|
wp_fractional_scale_v1* wp_scale = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
|
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
|
||||||
|
|
@ -269,7 +299,7 @@ export namespace Crafter {
|
||||||
// lazily in Render() (post-acquire) rather than up front.
|
// lazily in Render() (post-acquire) rather than up front.
|
||||||
std::array<bool, numFrames> imageInitialised{};
|
std::array<bool, numFrames> imageInitialised{};
|
||||||
std::thread thread;
|
std::thread thread;
|
||||||
VkCommandBuffer drawCmdBuffers[numFrames];
|
VkCommandBuffer drawCmdBuffers[numFrames] = {};
|
||||||
// Per-frame Vulkan info structs whose contents are almost entirely
|
// Per-frame Vulkan info structs whose contents are almost entirely
|
||||||
// invariant. Initialised once in the constructor; Render() patches only
|
// invariant. Initialised once in the constructor; Render() patches only
|
||||||
// the few fields that change each frame (the two barriers' image and
|
// the few fields that change each frame (the two barriers' image and
|
||||||
|
|
|
||||||
1103
lib/text-input-unstable-v3-client-protocol.h
Normal file
1103
lib/text-input-unstable-v3-client-protocol.h
Normal file
File diff suppressed because it is too large
Load diff
103
lib/text-input-unstable-v3-protocol.c
Normal file
103
lib/text-input-unstable-v3-protocol.c
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
/* 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -89,6 +89,7 @@ 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/wayland-xdg-decoration-unstable-v1-client-protocol");
|
||||||
cfg.cFiles.push_back("lib/fractional-scale-v1");
|
cfg.cFiles.push_back("lib/fractional-scale-v1");
|
||||||
cfg.cFiles.push_back("lib/viewporter");
|
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
|
// Vulkan is the only renderer on native targets. Software fallback is
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue