From 97f7c8728950e4bde3aad17b0d0033d14427e171 Mon Sep 17 00:00:00 2001 From: Abendlied Date: Sat, 5 Sep 2026 00:12:20 +0200 Subject: [PATCH 01/11] Fix X11 cursor visibility on window enter --- Engine/system/api/x11api.cpp | 6 +++- Engine/system/eventdispatcher.cpp | 48 +++++++++++++++++++++++++++++-- Engine/system/eventdispatcher.h | 7 ++++- Engine/system/systemapi.cpp | 8 ++++++ Engine/system/systemapi.h | 3 ++ Engine/ui/widget.cpp | 4 +++ 6 files changed, 71 insertions(+), 5 deletions(-) diff --git a/Engine/system/api/x11api.cpp b/Engine/system/api/x11api.cpp index a7abcc78..74f3556f 100644 --- a/Engine/system/api/x11api.cpp +++ b/Engine/system/api/x11api.cpp @@ -279,7 +279,7 @@ SystemApi::Window *X11Api::implCreateWindow(Tempest::Window *owner, uint32_t w, XSetWindowAttributes swa={}; swa.colormap = cmap; - swa.event_mask = PointerMotionMask | ExposureMask | + swa.event_mask = PointerMotionMask | EnterWindowMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | FocusChangeMask | StructureNotifyMask | @@ -598,6 +598,10 @@ void X11Api::implProcessEvents(SystemApi::AppCallBack &cb) { } break; } + case EnterNotify: { + SystemApi::dispatchMouseReevaluate(cb,Point(xev.xcrossing.x,xev.xcrossing.y)); + break; + } case MotionNotify: { if(activeCursorChange == 1) { // FIXME: mouse behave crazy in OpenGothic diff --git a/Engine/system/eventdispatcher.cpp b/Engine/system/eventdispatcher.cpp index f48c270d..735d9b1b 100644 --- a/Engine/system/eventdispatcher.cpp +++ b/Engine/system/eventdispatcher.cpp @@ -81,6 +81,8 @@ void EventDispatcher::dispatchMouseUp(Widget& /*wnd*/, MouseEvent &e) { } void EventDispatcher::dispatchMouseMove(Widget& wnd, MouseEvent &e) { + mouseWindow = &wnd; + mousePosition = e.pos(); auto btn = Event::ButtonNone; for(uint8_t i=0; ibind(wnd)) + continue; + auto wptr = implDispatch(*i,e); + if(wptr!=nullptr) { + implSetMouseOver(wptr,e,true); + return; + } + } + + auto wptr = implDispatch(wnd,e); + implSetMouseOver(wptr,e,true); + } + + void EventDispatcher::dispatchMouseWheel(Widget& wnd, MouseEvent &e) { if(e.delta==0) return; @@ -216,13 +252,16 @@ void EventDispatcher::dispatchClose(Widget& wnd, CloseEvent& e) { void EventDispatcher::dispatchFocus(Widget& wnd, FocusEvent& e) { if(e.in) { + focusWindow = &wnd; + if(auto f = focusLast.lock()) { f->widget->setFocus(true); } focusLast.reset(); + dispatchMouseReevaluate(wnd); return; } - + focusWindow = nullptr; if(!focusLast.expired()) return; @@ -414,14 +453,17 @@ std::shared_ptr EventDispatcher::implDispatch(Widget &root, KeyEven return nullptr; } -void EventDispatcher::implSetMouseOver(const std::shared_ptr &wptr,MouseEvent& orig) { +void EventDispatcher::implSetMouseOver(const std::shared_ptr &wptr,MouseEvent& orig,bool force) { auto widget = wptr==nullptr ? nullptr : wptr->widget; Widget* oldW = nullptr; if(auto old = mouseOver.lock()) oldW = old->widget; - if(widget==oldW) + if(widget==oldW) { + if(force) + implExcMouseOver(widget,oldW); return; + } implExcMouseOver(widget,oldW); diff --git a/Engine/system/eventdispatcher.h b/Engine/system/eventdispatcher.h index fe9d0f8e..3f59f6bb 100644 --- a/Engine/system/eventdispatcher.h +++ b/Engine/system/eventdispatcher.h @@ -15,6 +15,8 @@ class EventDispatcher final { void dispatchMouseDown (Widget& wnd, Tempest::MouseEvent& event); void dispatchMouseUp (Widget& wnd, Tempest::MouseEvent& event); void dispatchMouseMove (Widget& wnd, Tempest::MouseEvent& event); + void dispatchMouseReevaluate(Widget& wnd); + void dispatchMouseReevaluate(Widget& wnd, Point pos); void dispatchMouseWheel(Widget& wnd, Tempest::MouseEvent& event); void dispatchKeyDown (Widget& wnd, Tempest::KeyEvent& event, uint32_t scancode); @@ -39,7 +41,7 @@ class EventDispatcher final { bool implShortcut(Tempest::Widget &w, Tempest::KeyEvent& event); std::shared_ptr implDispatch(Tempest::Widget &w, Tempest::KeyEvent& event); - void implSetMouseOver(const std::shared_ptr& s, MouseEvent& orig); + void implSetMouseOver(const std::shared_ptr &wptr,MouseEvent& orig,bool force=false); void implExcMouseOver(Widget *w, Widget *old); void handleModKey(const KeyEvent& e); @@ -49,6 +51,9 @@ class EventDispatcher final { std::weak_ptr mouseUp[Event::MouseButton::ButtonLast]; std::weak_ptr mouseLast; std::weak_ptr mouseOver; + Widget* mouseWindow = nullptr; + Widget* focusWindow = nullptr; + Point mousePosition; std::weak_ptr focusLast; diff --git a/Engine/system/systemapi.cpp b/Engine/system/systemapi.cpp index f942ba89..23ad5cf1 100644 --- a/Engine/system/systemapi.cpp +++ b/Engine/system/systemapi.cpp @@ -128,6 +128,14 @@ void SystemApi::dispatchMouseMove(Tempest::Window &cb, MouseEvent &e) { dispatcher.dispatchMouseMove(cb,e); } +void SystemApi::dispatchMouseReevaluate(Tempest::Widget &cb) { + dispatcher.dispatchMouseReevaluate(cb); + } + +void SystemApi::dispatchMouseReevaluate(Tempest::Widget &cb, Point pos) { + dispatcher.dispatchMouseReevaluate(cb,pos); + } + void SystemApi::dispatchMouseWheel(Tempest::Window &cb, MouseEvent &e) { dispatcher.dispatchMouseWheel(cb,e); } diff --git a/Engine/system/systemapi.h b/Engine/system/systemapi.h index 3e5704e1..5780e1f1 100644 --- a/Engine/system/systemapi.h +++ b/Engine/system/systemapi.h @@ -95,6 +95,8 @@ class SystemApi { static void dispatchMouseDown (Tempest::Window& cb, MouseEvent& e); static void dispatchMouseUp (Tempest::Window& cb, MouseEvent& e); static void dispatchMouseMove (Tempest::Window& cb, MouseEvent& e); + static void dispatchMouseReevaluate(Tempest::Widget& cb); + static void dispatchMouseReevaluate(Tempest::Widget& cb, Point pos); static void dispatchMouseWheel(Tempest::Window& cb, MouseEvent& e); static void dispatchKeyDown (Tempest::Window& cb, KeyEvent& e, uint32_t scancode); @@ -117,6 +119,7 @@ class SystemApi { friend class Tempest::Window; friend class Tempest::Application; + friend class Tempest::Widget; }; } diff --git a/Engine/ui/widget.cpp b/Engine/ui/widget.cpp index 8b119e6f..758415d7 100644 --- a/Engine/ui/widget.cpp +++ b/Engine/ui/widget.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -227,6 +228,7 @@ Widget& Widget::implAddWidget(Widget *w,size_t at) { if(astate.disable>0) implDisableSum(w,astate.disable); lay->applyLayout(); + SystemApi::dispatchMouseReevaluate(*implTrieRoot(this)); update(); return *w; } @@ -296,6 +298,7 @@ void Widget::setGeometry(const Rect &rect) { SizeEvent e(uint32_t(rect.w),uint32_t(rect.h)); resizeEvent( e ); } + SystemApi::dispatchMouseReevaluate(*implTrieRoot(this)); } void Widget::setGeometry(int x, int y, int w, int h) { @@ -451,6 +454,7 @@ void Widget::setVisible(bool v) { w->update(); w->applyLayout(); } + SystemApi::dispatchMouseReevaluate(*implTrieRoot(this)); } bool Widget::isVisible() const { From 270e8846e8d82bed2413b2d318db2246bd564bde Mon Sep 17 00:00:00 2001 From: Abendlied Date: Fri, 11 Sep 2026 15:34:56 +0200 Subject: [PATCH 02/11] Fix X11 cursor visibility on window enter --- Engine/system/api/x11api.cpp | 2 +- Engine/system/eventdispatcher.cpp | 31 +++++++++++-------------------- Engine/system/eventdispatcher.h | 6 +----- Engine/system/systemapi.cpp | 8 ++------ Engine/system/systemapi.h | 4 +--- Engine/ui/widget.cpp | 4 ---- 6 files changed, 16 insertions(+), 39 deletions(-) diff --git a/Engine/system/api/x11api.cpp b/Engine/system/api/x11api.cpp index 74f3556f..a870c002 100644 --- a/Engine/system/api/x11api.cpp +++ b/Engine/system/api/x11api.cpp @@ -599,7 +599,7 @@ void X11Api::implProcessEvents(SystemApi::AppCallBack &cb) { break; } case EnterNotify: { - SystemApi::dispatchMouseReevaluate(cb,Point(xev.xcrossing.x,xev.xcrossing.y)); + SystemApi::dispatchMouseEnter(cb,Point(xev.xcrossing.x,xev.xcrossing.y)); break; } case MotionNotify: { diff --git a/Engine/system/eventdispatcher.cpp b/Engine/system/eventdispatcher.cpp index 735d9b1b..60a09465 100644 --- a/Engine/system/eventdispatcher.cpp +++ b/Engine/system/eventdispatcher.cpp @@ -81,8 +81,6 @@ void EventDispatcher::dispatchMouseUp(Widget& /*wnd*/, MouseEvent &e) { } void EventDispatcher::dispatchMouseMove(Widget& wnd, MouseEvent &e) { - mouseWindow = &wnd; - mousePosition = e.pos(); auto btn = Event::ButtonNone; for(uint8_t i=0; iwidget->setFocus(true); } focusLast.reset(); - dispatchMouseReevaluate(wnd); return; } - focusWindow = nullptr; + if(!focusLast.expired()) return; diff --git a/Engine/system/eventdispatcher.h b/Engine/system/eventdispatcher.h index 3f59f6bb..2672b4a6 100644 --- a/Engine/system/eventdispatcher.h +++ b/Engine/system/eventdispatcher.h @@ -15,8 +15,7 @@ class EventDispatcher final { void dispatchMouseDown (Widget& wnd, Tempest::MouseEvent& event); void dispatchMouseUp (Widget& wnd, Tempest::MouseEvent& event); void dispatchMouseMove (Widget& wnd, Tempest::MouseEvent& event); - void dispatchMouseReevaluate(Widget& wnd); - void dispatchMouseReevaluate(Widget& wnd, Point pos); + void dispatchMouseEnter(Widget& wnd, Point pos); void dispatchMouseWheel(Widget& wnd, Tempest::MouseEvent& event); void dispatchKeyDown (Widget& wnd, Tempest::KeyEvent& event, uint32_t scancode); @@ -51,9 +50,6 @@ class EventDispatcher final { std::weak_ptr mouseUp[Event::MouseButton::ButtonLast]; std::weak_ptr mouseLast; std::weak_ptr mouseOver; - Widget* mouseWindow = nullptr; - Widget* focusWindow = nullptr; - Point mousePosition; std::weak_ptr focusLast; diff --git a/Engine/system/systemapi.cpp b/Engine/system/systemapi.cpp index 23ad5cf1..d4711e00 100644 --- a/Engine/system/systemapi.cpp +++ b/Engine/system/systemapi.cpp @@ -128,12 +128,8 @@ void SystemApi::dispatchMouseMove(Tempest::Window &cb, MouseEvent &e) { dispatcher.dispatchMouseMove(cb,e); } -void SystemApi::dispatchMouseReevaluate(Tempest::Widget &cb) { - dispatcher.dispatchMouseReevaluate(cb); - } - -void SystemApi::dispatchMouseReevaluate(Tempest::Widget &cb, Point pos) { - dispatcher.dispatchMouseReevaluate(cb,pos); +void SystemApi::dispatchMouseEnter(Tempest::Widget &cb, Point pos) { + dispatcher.dispatchMouseEnter(cb,pos); } void SystemApi::dispatchMouseWheel(Tempest::Window &cb, MouseEvent &e) { diff --git a/Engine/system/systemapi.h b/Engine/system/systemapi.h index 5780e1f1..354d935b 100644 --- a/Engine/system/systemapi.h +++ b/Engine/system/systemapi.h @@ -95,8 +95,7 @@ class SystemApi { static void dispatchMouseDown (Tempest::Window& cb, MouseEvent& e); static void dispatchMouseUp (Tempest::Window& cb, MouseEvent& e); static void dispatchMouseMove (Tempest::Window& cb, MouseEvent& e); - static void dispatchMouseReevaluate(Tempest::Widget& cb); - static void dispatchMouseReevaluate(Tempest::Widget& cb, Point pos); + static void dispatchMouseEnter(Tempest::Widget& cb, Point pos); static void dispatchMouseWheel(Tempest::Window& cb, MouseEvent& e); static void dispatchKeyDown (Tempest::Window& cb, KeyEvent& e, uint32_t scancode); @@ -119,7 +118,6 @@ class SystemApi { friend class Tempest::Window; friend class Tempest::Application; - friend class Tempest::Widget; }; } diff --git a/Engine/ui/widget.cpp b/Engine/ui/widget.cpp index 758415d7..8b119e6f 100644 --- a/Engine/ui/widget.cpp +++ b/Engine/ui/widget.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -228,7 +227,6 @@ Widget& Widget::implAddWidget(Widget *w,size_t at) { if(astate.disable>0) implDisableSum(w,astate.disable); lay->applyLayout(); - SystemApi::dispatchMouseReevaluate(*implTrieRoot(this)); update(); return *w; } @@ -298,7 +296,6 @@ void Widget::setGeometry(const Rect &rect) { SizeEvent e(uint32_t(rect.w),uint32_t(rect.h)); resizeEvent( e ); } - SystemApi::dispatchMouseReevaluate(*implTrieRoot(this)); } void Widget::setGeometry(int x, int y, int w, int h) { @@ -454,7 +451,6 @@ void Widget::setVisible(bool v) { w->update(); w->applyLayout(); } - SystemApi::dispatchMouseReevaluate(*implTrieRoot(this)); } bool Widget::isVisible() const { From d8118027afefdef6bf128042559a4cf85bce3ab2 Mon Sep 17 00:00:00 2001 From: Abendlied Date: Wed, 16 Sep 2026 10:47:03 +0200 Subject: [PATCH 03/11] Fix X11 cursor state after window focus --- Engine/system/eventdispatcher.cpp | 22 ++++++++-------------- Engine/system/eventdispatcher.h | 2 +- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Engine/system/eventdispatcher.cpp b/Engine/system/eventdispatcher.cpp index 60a09465..8df274d0 100644 --- a/Engine/system/eventdispatcher.cpp +++ b/Engine/system/eventdispatcher.cpp @@ -138,10 +138,8 @@ void EventDispatcher::dispatchMouseMove(Widget& wnd, MouseEvent &e) { implSetMouseOver(wptr,e1); } -// Triggered on X11 EnterNotify. Unlike dispatchMouseMove, no MotionNotify is guaranteed -// to follow (e.g. window shown/refocused under an already idle pointer), so the -// hovered widget and its cursor shape would stay stale until next actual mouse -// move. Synthesize one MouseMove at reported enter position forcing a widget test. +// X11 EnterNotify provides the pointer position, but no MotionNotify is guaranteed to +// follow. Synthesize a MouseMove so the hovered widget is evaluated immediately. void EventDispatcher::dispatchMouseEnter(Widget& wnd, Point pos) { MouseEvent e(pos.x, pos.y, @@ -156,16 +154,13 @@ void EventDispatcher::dispatchMouseEnter(Widget& wnd, Point pos) { continue; auto wptr = implDispatch(*i,e); if(wptr!=nullptr) { - // force=true: The native cursor may have gone stale while pointer was - // outside of the window, even if the hit tested widget is the same as - // before. Identity of widget is insufficient to skip reapplication of it. - implSetMouseOver(wptr,e,true); + implSetMouseOver(wptr,e); return; } } auto wptr = implDispatch(wnd,e); - implSetMouseOver(wptr,e,true); + implSetMouseOver(wptr,e); } void EventDispatcher::dispatchMouseWheel(Widget& wnd, MouseEvent &e) { @@ -250,6 +245,8 @@ void EventDispatcher::dispatchFocus(Widget& wnd, FocusEvent& e) { f->widget->setFocus(true); } focusLast.reset(); + if(auto w = mouseOver.lock()) + implExcMouseOver(w->widget,w->widget); return; } @@ -444,17 +441,14 @@ std::shared_ptr EventDispatcher::implDispatch(Widget &root, KeyEven return nullptr; } -void EventDispatcher::implSetMouseOver(const std::shared_ptr &wptr,MouseEvent& orig,bool force) { +void EventDispatcher::implSetMouseOver(const std::shared_ptr &wptr,MouseEvent& orig) { auto widget = wptr==nullptr ? nullptr : wptr->widget; Widget* oldW = nullptr; if(auto old = mouseOver.lock()) oldW = old->widget; - if(widget==oldW) { - if(force) - implExcMouseOver(widget,oldW); + if(widget==oldW) return; - } implExcMouseOver(widget,oldW); diff --git a/Engine/system/eventdispatcher.h b/Engine/system/eventdispatcher.h index 2672b4a6..c9b8d9e3 100644 --- a/Engine/system/eventdispatcher.h +++ b/Engine/system/eventdispatcher.h @@ -40,7 +40,7 @@ class EventDispatcher final { bool implShortcut(Tempest::Widget &w, Tempest::KeyEvent& event); std::shared_ptr implDispatch(Tempest::Widget &w, Tempest::KeyEvent& event); - void implSetMouseOver(const std::shared_ptr &wptr,MouseEvent& orig,bool force=false); + void implSetMouseOver(const std::shared_ptr& s, MouseEvent& orig); void implExcMouseOver(Widget *w, Widget *old); void handleModKey(const KeyEvent& e); From 6f1a67c301482f2cafc28e305aa4db179f393d07 Mon Sep 17 00:00:00 2001 From: Abendlied Date: Wed, 16 Sep 2026 10:54:08 +0200 Subject: [PATCH 04/11] Fix cursor helper parameter name --- Engine/system/eventdispatcher.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/system/eventdispatcher.h b/Engine/system/eventdispatcher.h index c9b8d9e3..b2dcac18 100644 --- a/Engine/system/eventdispatcher.h +++ b/Engine/system/eventdispatcher.h @@ -40,7 +40,7 @@ class EventDispatcher final { bool implShortcut(Tempest::Widget &w, Tempest::KeyEvent& event); std::shared_ptr implDispatch(Tempest::Widget &w, Tempest::KeyEvent& event); - void implSetMouseOver(const std::shared_ptr& s, MouseEvent& orig); + void implSetMouseOver(const std::shared_ptr& wptr, MouseEvent& orig); void implExcMouseOver(Widget *w, Widget *old); void handleModKey(const KeyEvent& e); From 989fa748e433c050b5fb401db2b977af7e37f7fd Mon Sep 17 00:00:00 2001 From: Abendlied Date: Wed, 16 Sep 2026 23:45:18 +0200 Subject: [PATCH 05/11] Fix cursor restoration after window activation --- Engine/system/api/windowsapi.cpp | 2 +- Engine/system/api/x11api.cpp | 9 +++++++- Engine/system/eventdispatcher.cpp | 38 +++++++++---------------------- Engine/system/eventdispatcher.h | 1 - Engine/system/systemapi.cpp | 4 ---- Engine/system/systemapi.h | 1 - Engine/ui/uioverlay.cpp | 5 ++++ Engine/ui/uioverlay.h | 1 + Engine/ui/window.cpp | 1 + Engine/ui/window.h | 3 ++- 10 files changed, 29 insertions(+), 36 deletions(-) diff --git a/Engine/system/api/windowsapi.cpp b/Engine/system/api/windowsapi.cpp index 6c889424..6ea446b5 100644 --- a/Engine/system/api/windowsapi.cpp +++ b/Engine/system/api/windowsapi.cpp @@ -394,7 +394,7 @@ long long WindowsApi::windowProc(void *_hWnd, uint32_t msg, const unsigned long } case WM_ACTIVATE:{ - SetCursor(cb->cursorShape()); + SetCursor(cb->implResolvedCursor()); if(wParam==WA_INACTIVE) { FocusEvent e(false, Event::UnknownReason); SystemApi::dispatchFocus(*cb, e); diff --git a/Engine/system/api/x11api.cpp b/Engine/system/api/x11api.cpp index a870c002..b7376976 100644 --- a/Engine/system/api/x11api.cpp +++ b/Engine/system/api/x11api.cpp @@ -599,7 +599,14 @@ void X11Api::implProcessEvents(SystemApi::AppCallBack &cb) { break; } case EnterNotify: { - SystemApi::dispatchMouseEnter(cb,Point(xev.xcrossing.x,xev.xcrossing.y)); + MouseEvent e( xev.xcrossing.x, + xev.xcrossing.y, + Event::ButtonNone, + Event::M_NoModifier, + 0, + 0, + Event::MouseMove ); + SystemApi::dispatchMouseMove(cb,e); break; } case MotionNotify: { diff --git a/Engine/system/eventdispatcher.cpp b/Engine/system/eventdispatcher.cpp index 8df274d0..bd598d82 100644 --- a/Engine/system/eventdispatcher.cpp +++ b/Engine/system/eventdispatcher.cpp @@ -138,31 +138,6 @@ void EventDispatcher::dispatchMouseMove(Widget& wnd, MouseEvent &e) { implSetMouseOver(wptr,e1); } -// X11 EnterNotify provides the pointer position, but no MotionNotify is guaranteed to -// follow. Synthesize a MouseMove so the hovered widget is evaluated immediately. -void EventDispatcher::dispatchMouseEnter(Widget& wnd, Point pos) { - MouseEvent e(pos.x, - pos.y, - Event::ButtonNone, - Event::M_NoModifier, - 0, - 0, - Event::MouseMove); - - for(auto i:overlays) { - if(!i->bind(wnd)) - continue; - auto wptr = implDispatch(*i,e); - if(wptr!=nullptr) { - implSetMouseOver(wptr,e); - return; - } - } - - auto wptr = implDispatch(wnd,e); - implSetMouseOver(wptr,e); - } - void EventDispatcher::dispatchMouseWheel(Widget& wnd, MouseEvent &e) { if(e.delta==0) return; @@ -245,8 +220,17 @@ void EventDispatcher::dispatchFocus(Widget& wnd, FocusEvent& e) { f->widget->setFocus(true); } focusLast.reset(); - if(auto w = mouseOver.lock()) - implExcMouseOver(w->widget,w->widget); + + if(auto w = mouseOver.lock()) { + auto root = w->widget; + while(root->owner()!=nullptr) + root = root->owner(); + + if(auto r = dynamic_cast(root)) + r->implShowCursor(r->implResolvedCursor()); + if(auto r = dynamic_cast(root)) + r->implShowCursor(r->implResolvedCursor()); + } return; } diff --git a/Engine/system/eventdispatcher.h b/Engine/system/eventdispatcher.h index b2dcac18..150e21d3 100644 --- a/Engine/system/eventdispatcher.h +++ b/Engine/system/eventdispatcher.h @@ -15,7 +15,6 @@ class EventDispatcher final { void dispatchMouseDown (Widget& wnd, Tempest::MouseEvent& event); void dispatchMouseUp (Widget& wnd, Tempest::MouseEvent& event); void dispatchMouseMove (Widget& wnd, Tempest::MouseEvent& event); - void dispatchMouseEnter(Widget& wnd, Point pos); void dispatchMouseWheel(Widget& wnd, Tempest::MouseEvent& event); void dispatchKeyDown (Widget& wnd, Tempest::KeyEvent& event, uint32_t scancode); diff --git a/Engine/system/systemapi.cpp b/Engine/system/systemapi.cpp index d4711e00..f942ba89 100644 --- a/Engine/system/systemapi.cpp +++ b/Engine/system/systemapi.cpp @@ -128,10 +128,6 @@ void SystemApi::dispatchMouseMove(Tempest::Window &cb, MouseEvent &e) { dispatcher.dispatchMouseMove(cb,e); } -void SystemApi::dispatchMouseEnter(Tempest::Widget &cb, Point pos) { - dispatcher.dispatchMouseEnter(cb,pos); - } - void SystemApi::dispatchMouseWheel(Tempest::Window &cb, MouseEvent &e) { dispatcher.dispatchMouseWheel(cb,e); } diff --git a/Engine/system/systemapi.h b/Engine/system/systemapi.h index 354d935b..3e5704e1 100644 --- a/Engine/system/systemapi.h +++ b/Engine/system/systemapi.h @@ -95,7 +95,6 @@ class SystemApi { static void dispatchMouseDown (Tempest::Window& cb, MouseEvent& e); static void dispatchMouseUp (Tempest::Window& cb, MouseEvent& e); static void dispatchMouseMove (Tempest::Window& cb, MouseEvent& e); - static void dispatchMouseEnter(Tempest::Widget& cb, Point pos); static void dispatchMouseWheel(Tempest::Window& cb, MouseEvent& e); static void dispatchKeyDown (Tempest::Window& cb, KeyEvent& e, uint32_t scancode); diff --git a/Engine/ui/uioverlay.cpp b/Engine/ui/uioverlay.cpp index bce40eec..c5b85f2f 100644 --- a/Engine/ui/uioverlay.cpp +++ b/Engine/ui/uioverlay.cpp @@ -47,3 +47,8 @@ void UiOverlay::implShowCursor(CursorShape s) { owner->implShowCursor(s); } +CursorShape UiOverlay::implResolvedCursor() const { + if(owner!=nullptr) + return owner->implResolvedCursor(); + return CursorShape::Arrow; + } diff --git a/Engine/ui/uioverlay.h b/Engine/ui/uioverlay.h index d447c867..9020a868 100644 --- a/Engine/ui/uioverlay.h +++ b/Engine/ui/uioverlay.h @@ -17,6 +17,7 @@ class UiOverlay : public Tempest::Widget { bool bind(Window& w); void dispatchDestroyWindow(SystemApi::Window* w); void implShowCursor(CursorShape s); + CursorShape implResolvedCursor() const; Window* owner=nullptr; diff --git a/Engine/ui/window.cpp b/Engine/ui/window.cpp index 69f8977e..de00eb2c 100644 --- a/Engine/ui/window.cpp +++ b/Engine/ui/window.cpp @@ -57,5 +57,6 @@ void Window::setCursorPosition(const Point& p) { } void Window::implShowCursor(CursorShape s) { + resolvedCursor = s; SystemApi::showCursor(hwnd(),s); } diff --git a/Engine/ui/window.h b/Engine/ui/window.h index 846822a6..cc2b5fb0 100644 --- a/Engine/ui/window.h +++ b/Engine/ui/window.h @@ -22,6 +22,7 @@ class Window : public Widget { ~Window() override; void setWindowTitle(const char* utf8); + CursorShape implResolvedCursor() const { return resolvedCursor; } protected: virtual void render(); @@ -38,7 +39,7 @@ class Window : public Widget { void implShowCursor(CursorShape s); SystemApi::Window* id=nullptr; - + CursorShape resolvedCursor=CursorShape::Arrow; friend class Widget; friend class UiOverlay; friend class EventDispatcher; From a28b24c612dd7243bd7ab902e7c49a5c5b23cef8 Mon Sep 17 00:00:00 2001 From: Abendlied Date: Fri, 18 Sep 2026 15:06:26 +0200 Subject: [PATCH 06/11] Address review: unify cursor restore across backends Drop the cursor block from EventDispatcher::dispatchFocus - it was a second writer of cursor state next to implExcMouseOver, and it handled pointer state inside a keyboard-focus path. Both backends now do the same two steps at their activation event: dispatch a mouse move to recompute mouseOver, then re-apply the resolved shape. Window::implResolvedCursor is private; backends reach it through a protected SystemApi::cursorShape accessor, since friendship is not inherited by WindowsApi/X11Api. --- Engine/system/api/windowsapi.cpp | 2 +- Engine/system/api/x11api.cpp | 1 + Engine/system/eventdispatcher.cpp | 11 ----------- Engine/system/eventdispatcher.h | 2 +- Engine/system/systemapi.cpp | 4 ++++ Engine/system/systemapi.h | 1 + Engine/ui/uioverlay.cpp | 5 ----- Engine/ui/uioverlay.h | 1 - Engine/ui/window.h | 3 ++- 9 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Engine/system/api/windowsapi.cpp b/Engine/system/api/windowsapi.cpp index 6ea446b5..b2f4c8bd 100644 --- a/Engine/system/api/windowsapi.cpp +++ b/Engine/system/api/windowsapi.cpp @@ -394,7 +394,7 @@ long long WindowsApi::windowProc(void *_hWnd, uint32_t msg, const unsigned long } case WM_ACTIVATE:{ - SetCursor(cb->implResolvedCursor()); + SetCursor(SystemApi::cursorShape(*cb)); if(wParam==WA_INACTIVE) { FocusEvent e(false, Event::UnknownReason); SystemApi::dispatchFocus(*cb, e); diff --git a/Engine/system/api/x11api.cpp b/Engine/system/api/x11api.cpp index b7376976..e96b0350 100644 --- a/Engine/system/api/x11api.cpp +++ b/Engine/system/api/x11api.cpp @@ -607,6 +607,7 @@ void X11Api::implProcessEvents(SystemApi::AppCallBack &cb) { 0, Event::MouseMove ); SystemApi::dispatchMouseMove(cb,e); + implShowCursor(hWnd.ptr(),SystemApi::cursorShape(cb)); break; } case MotionNotify: { diff --git a/Engine/system/eventdispatcher.cpp b/Engine/system/eventdispatcher.cpp index bd598d82..f48c270d 100644 --- a/Engine/system/eventdispatcher.cpp +++ b/Engine/system/eventdispatcher.cpp @@ -220,17 +220,6 @@ void EventDispatcher::dispatchFocus(Widget& wnd, FocusEvent& e) { f->widget->setFocus(true); } focusLast.reset(); - - if(auto w = mouseOver.lock()) { - auto root = w->widget; - while(root->owner()!=nullptr) - root = root->owner(); - - if(auto r = dynamic_cast(root)) - r->implShowCursor(r->implResolvedCursor()); - if(auto r = dynamic_cast(root)) - r->implShowCursor(r->implResolvedCursor()); - } return; } diff --git a/Engine/system/eventdispatcher.h b/Engine/system/eventdispatcher.h index 150e21d3..fe9d0f8e 100644 --- a/Engine/system/eventdispatcher.h +++ b/Engine/system/eventdispatcher.h @@ -39,7 +39,7 @@ class EventDispatcher final { bool implShortcut(Tempest::Widget &w, Tempest::KeyEvent& event); std::shared_ptr implDispatch(Tempest::Widget &w, Tempest::KeyEvent& event); - void implSetMouseOver(const std::shared_ptr& wptr, MouseEvent& orig); + void implSetMouseOver(const std::shared_ptr& s, MouseEvent& orig); void implExcMouseOver(Widget *w, Widget *old); void handleModKey(const KeyEvent& e); diff --git a/Engine/system/systemapi.cpp b/Engine/system/systemapi.cpp index f942ba89..a2920939 100644 --- a/Engine/system/systemapi.cpp +++ b/Engine/system/systemapi.cpp @@ -205,6 +205,10 @@ void SystemApi::showCursor(SystemApi::Window *w, CursorShape show) { return inst().implShowCursor(w,show); } +CursorShape SystemApi::cursorShape(Tempest::Window& cb) { + return cb.implResolvedCursor(); + } + float SystemApi::uiScale(Window* w) { return inst().implUiScale(w); } diff --git a/Engine/system/systemapi.h b/Engine/system/systemapi.h index 3e5704e1..31482d0f 100644 --- a/Engine/system/systemapi.h +++ b/Engine/system/systemapi.h @@ -89,6 +89,7 @@ class SystemApi { static void setCursorPosition(SystemApi::Window *w, int x, int y); static void showCursor(SystemApi::Window *w, CursorShape c); + static CursorShape cursorShape(Tempest::Window& cb); static void dispatchOverlayRender(Tempest::Window &w, Tempest::PaintEvent& e); static void dispatchRender (Tempest::Window& cb); diff --git a/Engine/ui/uioverlay.cpp b/Engine/ui/uioverlay.cpp index c5b85f2f..bce40eec 100644 --- a/Engine/ui/uioverlay.cpp +++ b/Engine/ui/uioverlay.cpp @@ -47,8 +47,3 @@ void UiOverlay::implShowCursor(CursorShape s) { owner->implShowCursor(s); } -CursorShape UiOverlay::implResolvedCursor() const { - if(owner!=nullptr) - return owner->implResolvedCursor(); - return CursorShape::Arrow; - } diff --git a/Engine/ui/uioverlay.h b/Engine/ui/uioverlay.h index 9020a868..d447c867 100644 --- a/Engine/ui/uioverlay.h +++ b/Engine/ui/uioverlay.h @@ -17,7 +17,6 @@ class UiOverlay : public Tempest::Widget { bool bind(Window& w); void dispatchDestroyWindow(SystemApi::Window* w); void implShowCursor(CursorShape s); - CursorShape implResolvedCursor() const; Window* owner=nullptr; diff --git a/Engine/ui/window.h b/Engine/ui/window.h index cc2b5fb0..54568a6c 100644 --- a/Engine/ui/window.h +++ b/Engine/ui/window.h @@ -22,7 +22,6 @@ class Window : public Widget { ~Window() override; void setWindowTitle(const char* utf8); - CursorShape implResolvedCursor() const { return resolvedCursor; } protected: virtual void render(); @@ -37,12 +36,14 @@ class Window : public Widget { private: void implShowCursor(CursorShape s); + CursorShape implResolvedCursor() const { return resolvedCursor; } SystemApi::Window* id=nullptr; CursorShape resolvedCursor=CursorShape::Arrow; friend class Widget; friend class UiOverlay; friend class EventDispatcher; + friend class SystemApi; }; } From 5cfbdd5ce05067084b9f334d2b4367a51db03842 Mon Sep 17 00:00:00 2001 From: Try Date: Sun, 20 Sep 2026 00:05:42 +0200 Subject: [PATCH 07/11] final touches, making X11 and WinAPI consistent --- Engine/system/api/windowsapi.cpp | 12 ++++++++++++ Engine/system/api/x11api.cpp | 20 ++++++++++---------- Engine/system/systemapi.cpp | 2 +- Engine/ui/window.h | 6 +++--- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Engine/system/api/windowsapi.cpp b/Engine/system/api/windowsapi.cpp index b2f4c8bd..181111c0 100644 --- a/Engine/system/api/windowsapi.cpp +++ b/Engine/system/api/windowsapi.cpp @@ -394,6 +394,18 @@ long long WindowsApi::windowProc(void *_hWnd, uint32_t msg, const unsigned long } case WM_ACTIVATE:{ + POINT mpos = {}; + if(wParam==WA_ACTIVE && GetCursorPos(&mpos)) { + MouseEvent e( mpos.x, + mpos.y, + Event::ButtonNone, + Event::M_NoModifier, + 0, + 0, + Event::MouseMove ); + SystemApi::dispatchMouseMove(*cb, e); + } + SetCursor(SystemApi::cursorShape(*cb)); if(wParam==WA_INACTIVE) { FocusEvent e(false, Event::UnknownReason); diff --git a/Engine/system/api/x11api.cpp b/Engine/system/api/x11api.cpp index e96b0350..c1bbdcd0 100644 --- a/Engine/system/api/x11api.cpp +++ b/Engine/system/api/x11api.cpp @@ -610,6 +610,16 @@ void X11Api::implProcessEvents(SystemApi::AppCallBack &cb) { implShowCursor(hWnd.ptr(),SystemApi::cursorShape(cb)); break; } + case FocusIn: { + FocusEvent e(true, Event::UnknownReason); + SystemApi::dispatchFocus(cb, e); + break; + } + case FocusOut: { + FocusEvent e(false, Event::UnknownReason); + SystemApi::dispatchFocus(cb, e); + break; + } case MotionNotify: { if(activeCursorChange == 1) { // FIXME: mouse behave crazy in OpenGothic @@ -655,16 +665,6 @@ void X11Api::implProcessEvents(SystemApi::AppCallBack &cb) { SystemApi::dispatchKeyUp (cb,e,scan); break; } - case FocusIn: { - FocusEvent e(true, Event::UnknownReason); - SystemApi::dispatchFocus(cb, e); - break; - } - case FocusOut: { - FocusEvent e(false, Event::UnknownReason); - SystemApi::dispatchFocus(cb, e); - break; - } } std::this_thread::yield(); diff --git a/Engine/system/systemapi.cpp b/Engine/system/systemapi.cpp index a2920939..9e61bd75 100644 --- a/Engine/system/systemapi.cpp +++ b/Engine/system/systemapi.cpp @@ -206,7 +206,7 @@ void SystemApi::showCursor(SystemApi::Window *w, CursorShape show) { } CursorShape SystemApi::cursorShape(Tempest::Window& cb) { - return cb.implResolvedCursor(); + return cb.resolvedCursor; } float SystemApi::uiScale(Window* w) { diff --git a/Engine/ui/window.h b/Engine/ui/window.h index 54568a6c..9217073e 100644 --- a/Engine/ui/window.h +++ b/Engine/ui/window.h @@ -36,10 +36,10 @@ class Window : public Widget { private: void implShowCursor(CursorShape s); - CursorShape implResolvedCursor() const { return resolvedCursor; } - SystemApi::Window* id=nullptr; - CursorShape resolvedCursor=CursorShape::Arrow; + SystemApi::Window* id = nullptr; + CursorShape resolvedCursor = CursorShape::Arrow; + friend class Widget; friend class UiOverlay; friend class EventDispatcher; From 2a7d5c16e13a9f80074f6a160200b22744fb2d93 Mon Sep 17 00:00:00 2001 From: Try Date: Sun, 20 Sep 2026 00:12:00 +0200 Subject: [PATCH 08/11] final touches: MacOS --- Engine/system/api/macosapi.mm | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Engine/system/api/macosapi.mm b/Engine/system/api/macosapi.mm index 62daeb1b..ddd43842 100644 --- a/Engine/system/api/macosapi.mm +++ b/Engine/system/api/macosapi.mm @@ -601,7 +601,7 @@ MouseEvent e(p.x,p.y, break; } default: break; - } + } auto isDown = evt.modifierFlags & flag; auto eType = (isDown ? Event::KeyDown : Event::KeyUp); @@ -614,7 +614,22 @@ MouseEvent e(p.x,p.y, } case NSEventTypeAppKitDefined: break; - case NSEventTypeMouseEntered: + case NSEventTypeMouseEntered: { + bool inWindow = false; + auto mpos = mousePos(evt,inWindow); + if(inWindow) { + MouseEvent e( mpos.x, + mpos.y, + Event::ButtonNone, + Event::M_NoModifier, + 0, + 0, + Event::MouseMove ); + SystemApi::dispatchMouseMove(*cb, e); + } + implShowCursor(cb, SystemApi::cursorShape(*cb)); + break; + } case NSEventTypeMouseExited: break; From 51748262373e6b4932a624ae43a3a20063edc805 Mon Sep 17 00:00:00 2001 From: Try Date: Sun, 20 Sep 2026 00:24:07 +0200 Subject: [PATCH 09/11] build --- Engine/system/api/macosapi.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/system/api/macosapi.mm b/Engine/system/api/macosapi.mm index ddd43842..9810bbdc 100644 --- a/Engine/system/api/macosapi.mm +++ b/Engine/system/api/macosapi.mm @@ -625,9 +625,9 @@ MouseEvent e( mpos.x, 0, 0, Event::MouseMove ); - SystemApi::dispatchMouseMove(*cb, e); + SystemApi::dispatchMouseMove(cb, e); } - implShowCursor(cb, SystemApi::cursorShape(*cb)); + implShowCursor(reinterpret_cast(dx), SystemApi::cursorShape(cb)); break; } case NSEventTypeMouseExited: From 56d6be9fd6931ec6789b544fd427ec8db3797ed3 Mon Sep 17 00:00:00 2001 From: Try Date: Sun, 20 Sep 2026 00:43:41 +0200 Subject: [PATCH 10/11] improve cursor handling on Mac --- Engine/system/api/macosapi.mm | 59 ++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/Engine/system/api/macosapi.mm b/Engine/system/api/macosapi.mm index 9810bbdc..28985b66 100644 --- a/Engine/system/api/macosapi.mm +++ b/Engine/system/api/macosapi.mm @@ -106,6 +106,28 @@ static NSPoint mousePos(NSPoint px, NSWindow* wnd, bool& inWindow) { return mousePos(e,dummy); } +static Tempest::Point mousePos(NSWindow* wnd, bool& inWindow) { + NSPoint p = [wnd mouseLocationOutsideOfEventStream]; + NSPoint px = mousePos(p, wnd, inWindow); + return Tempest::Point{int(px.x), int(px.y)}; + } + +static void implShowCursor(SystemApi::Window *w, CursorShape show) { + static CursorShape current = CursorShape::Arrow; + if(current==show) { + // show/hie mechanism is ref couter based on Mac + // https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/QuartzDisplayServicesConceptual/Articles/MouseCursor.html + return; + } + + current = show; + if(show==CursorShape::Hidden) { + CGDisplayHideCursor(kCGNullDirectDisplay); + return; + } + CGDisplayShowCursor(kCGNullDirectDisplay); + } + void Detail::ImplMacOSApi::onDisplayLink(void* hwnd) { @autoreleasepool { auto cb = reinterpret_cast(hwnd); @@ -128,6 +150,20 @@ static NSPoint mousePos(NSPoint px, NSWindow* wnd, bool& inWindow) { auto cb = reinterpret_cast(hwnd); NSWindow* wnd = reinterpret_cast(w); + bool inWindow = true; + auto mpos = mousePos(wnd, inWindow); + if(inWindow) { + MouseEvent e( mpos.x, + mpos.y, + Event::ButtonNone, + Event::M_NoModifier, + 0, + 0, + Event::MouseMove ); + MacOSApi::dispatchMouseMove(*cb, e); + } + implShowCursor(reinterpret_cast(w), MacOSApi::cursorShape(*cb)); + FocusEvent e(true, Event::UnknownReason); MacOSApi::dispatchFocus(*cb, e); } @@ -367,11 +403,7 @@ - (void)dispatchRenderer{ } void MacOSApi::implShowCursor(SystemApi::Window *w, CursorShape show) { - if(show==CursorShape::Hidden) { - CGDisplayHideCursor(kCGNullDirectDisplay); - return; - } - CGDisplayShowCursor(kCGNullDirectDisplay); + ::implShowCursor(w, show); } void MacOSApi::implSetWindowTitle(Window* w, const char* utf8) { @@ -613,23 +645,8 @@ MouseEvent e(p.x,p.y, return; } case NSEventTypeAppKitDefined: + case NSEventTypeMouseEntered: break; - case NSEventTypeMouseEntered: { - bool inWindow = false; - auto mpos = mousePos(evt,inWindow); - if(inWindow) { - MouseEvent e( mpos.x, - mpos.y, - Event::ButtonNone, - Event::M_NoModifier, - 0, - 0, - Event::MouseMove ); - SystemApi::dispatchMouseMove(cb, e); - } - implShowCursor(reinterpret_cast(dx), SystemApi::cursorShape(cb)); - break; - } case NSEventTypeMouseExited: break; From 06c912b88b8cfdb3b1d898d3c5f9f2e6732ca8ad Mon Sep 17 00:00:00 2001 From: Try Date: Sun, 20 Sep 2026 23:21:27 +0200 Subject: [PATCH 11/11] winapi: use window-local coordinates --- Engine/system/api/windowsapi.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Engine/system/api/windowsapi.cpp b/Engine/system/api/windowsapi.cpp index 181111c0..642e4a5f 100644 --- a/Engine/system/api/windowsapi.cpp +++ b/Engine/system/api/windowsapi.cpp @@ -396,6 +396,8 @@ long long WindowsApi::windowProc(void *_hWnd, uint32_t msg, const unsigned long case WM_ACTIVATE:{ POINT mpos = {}; if(wParam==WA_ACTIVE && GetCursorPos(&mpos)) { + mpos.x -= cb->x(); + mpos.y -= cb->y(); MouseEvent e( mpos.x, mpos.y, Event::ButtonNone,