fix(wayland): constant-initialised statics in Device, for clang 23.1 on aarch64-musl
With the touch positions in an inline static std::unordered_map and the text input's strings in inline static std::strings, clang 23.1 crashed compiling Device.cpp for aarch64-alpine-linux-musl against that sysroot's libc++ 22 — in LazyCallGraph at -O3, in the ThinLTO bitcode writer at -O0 (a dangling global); the native build was fine. The fingers now live in a constant-initialised array of slots and the strings in function-local statics of the implementation unit; nothing else changes.
This commit is contained in:
parent
0d8287c122
commit
cc04d4e121
3 changed files with 80 additions and 23 deletions
|
|
@ -338,6 +338,44 @@ void Device::handle_global(void *data, wl_registry *registry, std::uint32_t name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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) {
|
void Device::text_input_enter(void*, zwp_text_input_v3*, wl_surface* surface) {
|
||||||
textInputWindow = nullptr;
|
textInputWindow = nullptr;
|
||||||
for (Window* window : windows) {
|
for (Window* window : windows) {
|
||||||
|
|
@ -349,17 +387,17 @@ void Device::text_input_enter(void*, zwp_text_input_v3*, wl_surface* surface) {
|
||||||
void Device::text_input_leave(void*, zwp_text_input_v3*, wl_surface*) {
|
void Device::text_input_leave(void*, zwp_text_input_v3*, wl_surface*) {
|
||||||
textInputWindow = nullptr;
|
textInputWindow = nullptr;
|
||||||
textInputHasPreedit = textInputHasCommit = textInputHasDelete = false;
|
textInputHasPreedit = textInputHasCommit = textInputHasDelete = false;
|
||||||
textInputPreedit.clear();
|
TextStrings().preedit.clear();
|
||||||
textInputShownPreedit.clear();
|
TextStrings().shown.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::text_input_preedit(void*, zwp_text_input_v3*, const char* text, std::int32_t, std::int32_t) {
|
void Device::text_input_preedit(void*, zwp_text_input_v3*, const char* text, std::int32_t, std::int32_t) {
|
||||||
textInputPreedit = text == nullptr ? "" : text;
|
TextStrings().preedit = text == nullptr ? "" : text;
|
||||||
textInputHasPreedit = true;
|
textInputHasPreedit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::text_input_commit(void*, zwp_text_input_v3*, const char* text) {
|
void Device::text_input_commit(void*, zwp_text_input_v3*, const char* text) {
|
||||||
textInputCommit = text == nullptr ? "" : text;
|
TextStrings().commit = text == nullptr ? "" : text;
|
||||||
textInputHasCommit = true;
|
textInputHasCommit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -375,17 +413,18 @@ void Device::text_input_delete(void*, zwp_text_input_v3*, std::uint32_t before,
|
||||||
// the window hears about it only when that differs from what it shows.
|
// 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) {
|
void Device::text_input_done(void*, zwp_text_input_v3*, std::uint32_t) {
|
||||||
Window* window = textInputWindow;
|
Window* window = textInputWindow;
|
||||||
if (!textInputHasPreedit) textInputPreedit.clear();
|
TextInputStrings& strings = TextStrings();
|
||||||
|
if (!textInputHasPreedit) strings.preedit.clear();
|
||||||
if (window != nullptr) {
|
if (window != nullptr) {
|
||||||
if (textInputHasDelete && (textInputDeleteBefore > 0 || textInputDeleteAfter > 0)) window->onTextInputDelete.Invoke({ textInputDeleteBefore, textInputDeleteAfter });
|
if (textInputHasDelete && (textInputDeleteBefore > 0 || textInputDeleteAfter > 0)) window->onTextInputDelete.Invoke({ textInputDeleteBefore, textInputDeleteAfter });
|
||||||
if (textInputHasCommit && !textInputCommit.empty()) window->onTextInput.Invoke(textInputCommit);
|
if (textInputHasCommit && !strings.commit.empty()) window->onTextInput.Invoke(strings.commit);
|
||||||
if (textInputPreedit != textInputShownPreedit || (textInputHasCommit && !textInputCommit.empty())) {
|
if (strings.preedit != strings.shown || (textInputHasCommit && !strings.commit.empty())) {
|
||||||
textInputShownPreedit = textInputPreedit;
|
strings.shown = strings.preedit;
|
||||||
window->onPreedit.Invoke(textInputPreedit);
|
window->onPreedit.Invoke(strings.preedit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
textInputHasPreedit = textInputHasCommit = textInputHasDelete = false;
|
textInputHasPreedit = textInputHasCommit = textInputHasDelete = false;
|
||||||
textInputCommit.clear();
|
strings.commit.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::ApplyTextInput(Window* window) {
|
void Device::ApplyTextInput(Window* window) {
|
||||||
|
|
@ -626,6 +665,10 @@ void Device::TickKeyRepeats() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Device::ForgetTouches() {
|
||||||
|
for (TouchSlot& slot : touchSlots) slot = TouchSlot{};
|
||||||
|
}
|
||||||
|
|
||||||
// The first finger stands in for the pointer: it enters the window (so the
|
// 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
|
// clipboard has a serial and Device::focusedWindow a window), presses the
|
||||||
// left button, moves it and releases it. Every finger is also reported as
|
// left button, moves it and releases it. Every finger is also reported as
|
||||||
|
|
@ -639,7 +682,10 @@ void Device::touch_down(void*, wl_touch*, std::uint32_t serial, std::uint32_t, w
|
||||||
touchWindow = window;
|
touchWindow = window;
|
||||||
window->lastPointerSerial_ = serial;
|
window->lastPointerSerial_ = serial;
|
||||||
const Vector<float, 2> pos = Vector<float, 2>(wl_fixed_to_double(x), wl_fixed_to_double(y)) * window->scale;
|
const Vector<float, 2> pos = Vector<float, 2>(wl_fixed_to_double(x), wl_fixed_to_double(y)) * window->scale;
|
||||||
touchPositions[id] = { pos.x, pos.y };
|
if (TouchSlot* slot = SlotFor(id)) {
|
||||||
|
slot->x = pos.x;
|
||||||
|
slot->y = pos.y;
|
||||||
|
}
|
||||||
window->onTouchDown.Invoke({ id, pos });
|
window->onTouchDown.Invoke({ id, pos });
|
||||||
if (window->primaryTouchId < 0) {
|
if (window->primaryTouchId < 0) {
|
||||||
window->primaryTouchId = id;
|
window->primaryTouchId = id;
|
||||||
|
|
@ -653,23 +699,28 @@ void Device::touch_down(void*, wl_touch*, std::uint32_t serial, std::uint32_t, w
|
||||||
void Device::touch_up(void*, wl_touch*, std::uint32_t, std::uint32_t, std::int32_t id) {
|
void Device::touch_up(void*, wl_touch*, std::uint32_t, std::uint32_t, std::int32_t id) {
|
||||||
if (touchWindow == nullptr) return;
|
if (touchWindow == nullptr) return;
|
||||||
Window* window = touchWindow;
|
Window* window = touchWindow;
|
||||||
const auto at = touchPositions.find(id);
|
Vector<float, 2> pos = window->currentMousePos;
|
||||||
const Vector<float, 2> pos = at == touchPositions.end() ? window->currentMousePos : Vector<float, 2>(at->second.first, at->second.second);
|
if (TouchSlot* slot = SlotFor(id)) {
|
||||||
touchPositions.erase(id);
|
pos = Vector<float, 2>(slot->x, slot->y);
|
||||||
|
*slot = TouchSlot{};
|
||||||
|
}
|
||||||
window->onTouchUp.Invoke({ id, pos });
|
window->onTouchUp.Invoke({ id, pos });
|
||||||
if (id == window->primaryTouchId) {
|
if (id == window->primaryTouchId) {
|
||||||
window->primaryTouchId = -1;
|
window->primaryTouchId = -1;
|
||||||
window->mouseLeftHeld = false;
|
window->mouseLeftHeld = false;
|
||||||
window->onMouseLeftRelease.Invoke();
|
window->onMouseLeftRelease.Invoke();
|
||||||
}
|
}
|
||||||
if (touchPositions.empty()) touchWindow = nullptr;
|
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) {
|
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;
|
if (touchWindow == nullptr) return;
|
||||||
Window* window = touchWindow;
|
Window* window = touchWindow;
|
||||||
const Vector<float, 2> pos = Vector<float, 2>(wl_fixed_to_double(x), wl_fixed_to_double(y)) * window->scale;
|
const Vector<float, 2> pos = Vector<float, 2>(wl_fixed_to_double(x), wl_fixed_to_double(y)) * window->scale;
|
||||||
touchPositions[id] = { pos.x, pos.y };
|
if (TouchSlot* slot = SlotFor(id)) {
|
||||||
|
slot->x = pos.x;
|
||||||
|
slot->y = pos.y;
|
||||||
|
}
|
||||||
window->onTouchMove.Invoke({ id, pos });
|
window->onTouchMove.Invoke({ id, pos });
|
||||||
if (id == window->primaryTouchId) {
|
if (id == window->primaryTouchId) {
|
||||||
window->currentMousePos = pos;
|
window->currentMousePos = pos;
|
||||||
|
|
@ -682,7 +733,7 @@ void Device::touch_frame(void*, wl_touch*) {}
|
||||||
void Device::touch_cancel(void*, wl_touch*) {
|
void Device::touch_cancel(void*, wl_touch*) {
|
||||||
if (touchWindow == nullptr) return;
|
if (touchWindow == nullptr) return;
|
||||||
Window* window = touchWindow;
|
Window* window = touchWindow;
|
||||||
touchPositions.clear();
|
ForgetTouches();
|
||||||
touchWindow = nullptr;
|
touchWindow = nullptr;
|
||||||
if (window->primaryTouchId >= 0) {
|
if (window->primaryTouchId >= 0) {
|
||||||
window->primaryTouchId = -1;
|
window->primaryTouchId = -1;
|
||||||
|
|
|
||||||
|
|
@ -560,7 +560,7 @@ Window::~Window() {
|
||||||
if (Device::keyboardWindow == this) Device::keyboardWindow = nullptr;
|
if (Device::keyboardWindow == this) Device::keyboardWindow = nullptr;
|
||||||
if (Device::touchWindow == this) {
|
if (Device::touchWindow == this) {
|
||||||
Device::touchWindow = nullptr;
|
Device::touchWindow = nullptr;
|
||||||
Device::touchPositions.clear();
|
Device::ForgetTouches();
|
||||||
}
|
}
|
||||||
if (Device::textInputWindow == this) Device::textInputWindow = nullptr;
|
if (Device::textInputWindow == this) Device::textInputWindow = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,17 @@ export namespace Crafter {
|
||||||
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_touch* wlTouch = nullptr;
|
inline static wl_touch* wlTouch = nullptr;
|
||||||
inline static Window* touchWindow = nullptr; // the window under the fingers
|
inline static Window* touchWindow = nullptr; // the window under the fingers
|
||||||
inline static std::unordered_map<std::int32_t, std::pair<float, float>> touchPositions; // per finger, physical px
|
// 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;
|
||||||
|
};
|
||||||
|
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
|
||||||
|
|
@ -83,9 +92,6 @@ export namespace Crafter {
|
||||||
inline static zwp_text_input_manager_v3* textInputManager = nullptr;
|
inline static zwp_text_input_manager_v3* textInputManager = nullptr;
|
||||||
inline static zwp_text_input_v3* textInput = nullptr;
|
inline static zwp_text_input_v3* textInput = nullptr;
|
||||||
inline static Window* textInputWindow = nullptr;
|
inline static Window* textInputWindow = nullptr;
|
||||||
inline static std::string textInputPreedit;
|
|
||||||
inline static std::string textInputShownPreedit; // what the window was last told
|
|
||||||
inline static std::string textInputCommit;
|
|
||||||
inline static std::uint32_t textInputDeleteBefore = 0;
|
inline static std::uint32_t textInputDeleteBefore = 0;
|
||||||
inline static std::uint32_t textInputDeleteAfter = 0;
|
inline static std::uint32_t textInputDeleteAfter = 0;
|
||||||
inline static bool textInputHasPreedit = false;
|
inline static bool textInputHasPreedit = false;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue