fix: keep event handlers alive during dispatch
This commit is contained in:
parent
39bf4fbe84
commit
6934dbe62b
6 changed files with 101 additions and 10 deletions
|
|
@ -167,12 +167,12 @@ bool EventQueue::getEvent(Event &event, double timeout)
|
||||||
bool EventQueue::dispatchEvent(const Event &event)
|
bool EventQueue::dispatchEvent(const Event &event)
|
||||||
{
|
{
|
||||||
void *target = event.getTarget();
|
void *target = event.getTarget();
|
||||||
if (const auto *type_handler = getHandler(event.getType(), target); type_handler) {
|
if (auto typeHandler = getHandler(event.getType(), target); typeHandler.has_value()) {
|
||||||
(*type_handler)(event);
|
(*typeHandler)(event);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (const auto *any_handler = getHandler(EventTypes::Unknown, target); any_handler) {
|
if (auto anyHandler = getHandler(EventTypes::Unknown, target); anyHandler.has_value()) {
|
||||||
(*any_handler)(event);
|
(*anyHandler)(event);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -295,17 +295,17 @@ void EventQueue::removeHandlers(void *target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const EventQueue::EventHandler *EventQueue::getHandler(EventTypes type, void *target) const
|
std::optional<EventQueue::EventHandler> EventQueue::getHandler(EventTypes type, void *target) const
|
||||||
{
|
{
|
||||||
std::scoped_lock lock{m_mutex};
|
std::scoped_lock lock{m_mutex};
|
||||||
if (HandlerTable::const_iterator index = m_handlers.find(target); index != m_handlers.end()) {
|
if (HandlerTable::const_iterator index = m_handlers.find(target); index != m_handlers.end()) {
|
||||||
const TypeHandlerTable &typeHandlers = index->second;
|
const TypeHandlerTable &typeHandlers = index->second;
|
||||||
TypeHandlerTable::const_iterator index2 = typeHandlers.find(type);
|
TypeHandlerTable::const_iterator index2 = typeHandlers.find(type);
|
||||||
if (index2 != typeHandlers.end()) {
|
if (index2 != typeHandlers.end()) {
|
||||||
return &index2->second;
|
return index2->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t EventQueue::saveEvent(Event &&event)
|
uint32_t EventQueue::saveEvent(Event &&event)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
|
@ -50,7 +51,7 @@ public:
|
||||||
void waitForReady() const override;
|
void waitForReady() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const EventHandler *getHandler(EventTypes type, void *target) const;
|
std::optional<EventHandler> getHandler(EventTypes type, void *target) const;
|
||||||
uint32_t saveEvent(Event &&event);
|
uint32_t saveEvent(Event &&event);
|
||||||
Event removeEvent(uint32_t eventID);
|
Event removeEvent(uint32_t eventID);
|
||||||
bool hasTimerExpired(Event &event);
|
bool hasTimerExpired(Event &event);
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,8 @@ public:
|
||||||
Looks up the dispatcher for the event's target and invokes it.
|
Looks up the dispatcher for the event's target and invokes it.
|
||||||
Returns true iff a dispatcher exists for the target.
|
Returns true iff a dispatcher exists for the target.
|
||||||
|
|
||||||
The caller must ensure that the target of the event is not removed by removeHandler() or
|
The handler may unregister itself while running. The event target object must remain alive until
|
||||||
removeHandlers().
|
the handler starts.
|
||||||
*/
|
*/
|
||||||
virtual bool dispatchEvent(const Event &event) = 0;
|
virtual bool dispatchEvent(const Event &event) = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,3 +37,10 @@ create_test(
|
||||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/base"
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
create_test(
|
||||||
|
NAME EventQueueTests
|
||||||
|
DEPENDS base
|
||||||
|
LIBS arch mt
|
||||||
|
SOURCE EventQueueTests.cpp
|
||||||
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/base"
|
||||||
|
)
|
||||||
|
|
|
||||||
58
src/unittests/base/EventQueueTests.cpp
Normal file
58
src/unittests/base/EventQueueTests.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
|
* SPDX-FileCopyrightText: (C) 2026 Deskflow Developers
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "EventQueueTests.h"
|
||||||
|
|
||||||
|
#include "base/EventQueue.h"
|
||||||
|
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
void EventQueueTests::initTestCase()
|
||||||
|
{
|
||||||
|
m_arch.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventQueueTests::dispatchEvent_noHandler_returnsFalse()
|
||||||
|
{
|
||||||
|
EventQueue events;
|
||||||
|
|
||||||
|
QVERIFY(!events.dispatchEvent(Event(EventTypes::ClientDisconnected, this)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventQueueTests::dispatchEvent_noTypeHandler_dispatchesUnknownHandler()
|
||||||
|
{
|
||||||
|
EventQueue events;
|
||||||
|
bool fallbackCalled = false;
|
||||||
|
events.addHandler(EventTypes::Unknown, this, [&fallbackCalled](const Event &) { fallbackCalled = true; });
|
||||||
|
|
||||||
|
QVERIFY(events.dispatchEvent(Event(EventTypes::ClientDisconnected, this)));
|
||||||
|
QVERIFY(fallbackCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventQueueTests::dispatchEvent_handlerRemovesItself_keepsHandlerAliveUntilReturn()
|
||||||
|
{
|
||||||
|
EventQueue events;
|
||||||
|
auto handlerLifetime = std::make_shared<int>(1);
|
||||||
|
std::weak_ptr<int> handlerLifetimeObserver = handlerLifetime;
|
||||||
|
bool handlerAliveAfterRemoval = false;
|
||||||
|
|
||||||
|
events.addHandler(
|
||||||
|
EventTypes::ClientDisconnected, this,
|
||||||
|
[this, &events, &handlerLifetimeObserver, &handlerAliveAfterRemoval, handlerLifetime](const Event &) {
|
||||||
|
events.removeHandler(EventTypes::ClientDisconnected, this);
|
||||||
|
handlerAliveAfterRemoval = handlerLifetime != nullptr && !handlerLifetimeObserver.expired();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
handlerLifetime.reset();
|
||||||
|
|
||||||
|
QVERIFY(events.dispatchEvent(Event(EventTypes::ClientDisconnected, this)));
|
||||||
|
QVERIFY(handlerAliveAfterRemoval);
|
||||||
|
QVERIFY(handlerLifetimeObserver.expired());
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(EventQueueTests)
|
||||||
25
src/unittests/base/EventQueueTests.h
Normal file
25
src/unittests/base/EventQueueTests.h
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
|
* SPDX-FileCopyrightText: (C) 2026 Deskflow Developers
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "arch/Arch.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class EventQueueTests : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void initTestCase();
|
||||||
|
void dispatchEvent_noHandler_returnsFalse();
|
||||||
|
void dispatchEvent_noTypeHandler_dispatchesUnknownHandler();
|
||||||
|
void dispatchEvent_handlerRemovesItself_keepsHandlerAliveUntilReturn();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Arch m_arch;
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue