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,
|
||||
};
|
||||
Device::CheckVkResult(vkCreateComputePipelines(
|
||||
Device::device, Device::pipelineCache, 1, &info, nullptr, &pipeline));
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -106,8 +110,10 @@ void ComputeShader::Load(const std::filesystem::path& spvPath, DescriptorHeapVul
|
|||
},
|
||||
.layout = VK_NULL_HANDLE,
|
||||
};
|
||||
Device::CheckVkResult(vkCreateComputePipelines(
|
||||
Device::device, Device::pipelineCache, 1, &info, nullptr, &pipeline));
|
||||
const VkResult created = vkCreateComputePipelines(
|
||||
Device::device, Device::pipelineCache, 1, &info, nullptr, &pipeline);
|
||||
vkDestroyShaderModule(Device::device, shader.shader, nullptr);
|
||||
Device::CheckVkResult(created);
|
||||
}
|
||||
|
||||
void ComputeShader::Dispatch(VkCommandBuffer cmd,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ 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>
|
||||
|
|
@ -303,6 +304,10 @@ 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) {
|
||||
|
|
@ -324,7 +329,134 @@ 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) {
|
||||
|
|
@ -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) {
|
||||
|
||||
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) {
|
||||
|
|
@ -456,12 +600,16 @@ 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 (focusedWindow->heldKeys.contains(code)) {
|
||||
focusedWindow->onRawKeyHold.Invoke(code);
|
||||
if (keyboardWindow->heldKeys.contains(code)) {
|
||||
keyboardWindow->onRawKeyHold.Invoke(code);
|
||||
} else {
|
||||
focusedWindow->heldKeys.insert(code);
|
||||
focusedWindow->onRawKeyDown.Invoke(code);
|
||||
keyboardWindow->heldKeys.insert(code);
|
||||
keyboardWindow->onRawKeyDown.Invoke(code);
|
||||
}
|
||||
|
||||
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) {
|
||||
buf.resize(n);
|
||||
utf8 = buf;
|
||||
focusedWindow->onTextInput.Invoke(utf8);
|
||||
keyboardWindow->onTextInput.Invoke(utf8);
|
||||
}
|
||||
|
||||
// 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.lastFireTime = keyRepeat.pressTime;
|
||||
} else {
|
||||
focusedWindow->heldKeys.erase(code);
|
||||
focusedWindow->onRawKeyUp.Invoke(code);
|
||||
keyboardWindow->heldKeys.erase(code);
|
||||
keyboardWindow->onRawKeyUp.Invoke(code);
|
||||
|
||||
// If the released key was the one repeating, stop. Otherwise leave
|
||||
// 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() {
|
||||
if (!keyRepeat.active || !focusedWindow) return;
|
||||
if (!keyRepeat.active || !keyboardWindow) return;
|
||||
if (keyRepeat.rate <= 0) return;
|
||||
|
||||
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
|
||||
// make the repeat permanently lag behind.
|
||||
while (now - keyRepeat.lastFireTime >= period) {
|
||||
focusedWindow->onRawKeyDown.Invoke(keyRepeat.key);
|
||||
focusedWindow->onRawKeyHold.Invoke(keyRepeat.key);
|
||||
keyboardWindow->onRawKeyDown.Invoke(keyRepeat.key);
|
||||
keyboardWindow->onRawKeyHold.Invoke(keyRepeat.key);
|
||||
if (!keyRepeat.utf8.empty()) {
|
||||
focusedWindow->onTextInput.Invoke(keyRepeat.utf8);
|
||||
keyboardWindow->onTextInput.Invoke(keyRepeat.utf8);
|
||||
}
|
||||
keyRepeat.lastFireTime += period;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
void Device::ForgetTouches() {
|
||||
for (TouchSlot& slot : touchSlots) slot = TouchSlot{};
|
||||
}
|
||||
|
||||
// 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 (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
|
||||
wl_keyboard* keyboard = wl_seat_get_keyboard(seat);
|
||||
wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ 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>
|
||||
|
|
@ -299,7 +300,7 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height
|
|||
|
||||
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);
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
#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.
|
||||
|
|
@ -568,6 +621,24 @@ 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) {
|
||||
|
|
@ -1229,12 +1300,13 @@ 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) {
|
||||
cb = wl_surface_frame(window->surface);
|
||||
wl_callback_add_listener(cb, &Window::wl_callback_listener, window);
|
||||
window->cb = wl_surface_frame(window->surface);
|
||||
wl_callback_add_listener(window->cb, &Window::wl_callback_listener, window);
|
||||
window->Update();
|
||||
} 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()));
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ 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;
|
||||
|
|
@ -49,12 +50,32 @@ 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
|
||||
|
|
@ -68,6 +89,19 @@ 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);
|
||||
|
|
@ -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 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,
|
||||
|
|
@ -93,6 +138,21 @@ 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,
|
||||
|
|
|
|||
|
|
@ -96,6 +96,20 @@ 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.
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ export namespace Crafter {
|
|||
std::uint16_t width;
|
||||
std::uint16_t height;
|
||||
std::uint8_t mipLevels;
|
||||
VkImage image;
|
||||
VkDeviceMemory imageMemory;
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkDeviceMemory imageMemory = VK_NULL_HANDLE;
|
||||
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;
|
||||
VkImageView imageView = VK_NULL_HANDLE;
|
||||
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,6 +378,9 @@ 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).
|
||||
|
|
|
|||
|
|
@ -18,6 +18,22 @@ 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;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ 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>
|
||||
|
|
@ -98,6 +99,19 @@ 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;
|
||||
|
|
@ -110,16 +124,19 @@ 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 have nothing to clean up at the
|
||||
// Window level (Vulkan resources teardown is the user's job).
|
||||
// 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.
|
||||
~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
|
||||
|
|
@ -140,6 +157,14 @@ 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();
|
||||
|
|
@ -191,6 +216,7 @@ 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;
|
||||
|
|
@ -207,6 +233,10 @@ 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);
|
||||
|
|
@ -227,7 +257,7 @@ export namespace Crafter {
|
|||
constexpr static wp_fractional_scale_v1_listener wp_fractional_scale_v1_listener = {
|
||||
.preferred_scale = xdg_surface_handle_preferred_scale,
|
||||
};
|
||||
inline static wp_fractional_scale_v1* wp_scale = nullptr;
|
||||
wp_fractional_scale_v1* wp_scale = nullptr;
|
||||
#endif
|
||||
|
||||
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
|
|
@ -269,7 +299,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
|
||||
|
|
|
|||
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/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
|
||||
|
|
|
|||
Loading…
Reference in a new issue