diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index b5c283c..016242a 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -223,6 +223,11 @@ void Device::handle_global_remove(void* data, wl_registry* registry, uint32_t na } void Device::pointer_handle_button(void* data, wl_pointer* pointer, std::uint32_t serial, std::uint32_t time, std::uint32_t button, std::uint32_t state) { + // Defensive: compositors can deliver pointer events without (or after) + // a matching enter — observed on sway during input-device hot-plug. + if (focusedWindow == nullptr) { + return; + } if (button == BTN_LEFT) { if(state == WL_POINTER_BUTTON_STATE_PRESSED) { Device::focusedWindow->mouseLeftHeld = true; @@ -243,6 +248,9 @@ void Device::pointer_handle_button(void* data, wl_pointer* pointer, std::uint32_ } void Device::PointerListenerHandleMotion(void* data, wl_pointer* wl_pointer, std::uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + if (focusedWindow == nullptr) { + return; + } Vector pos(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)); //Device::focusedWindow->lastMousePos = Device::focusedWindow->currentMousePos; Device::focusedWindow->currentMousePos = pos * Device::focusedWindow->scale; @@ -267,8 +275,12 @@ void Device::PointerListenerHandleEnter(void* data, wl_pointer* wl_pointer, std: } void Device::PointerListenerHandleLeave(void* data, wl_pointer*, std::uint32_t, wl_surface*) { - Device::focusedWindow->onMouseLeave.Invoke(); - focusedWindow = nullptr; + // Sway has been observed to send a second leave after focus is already + // gone (input-device hot-plug) — don't crash on it. + if (focusedWindow != nullptr) { + focusedWindow->onMouseLeave.Invoke(); + focusedWindow = nullptr; + } // Drop any sub-detent scroll remainder so it can't leak into whatever // window the pointer enters next. scrollDetentRemainder = 0.0;