Skip to content

Commit e48aac5

Browse files
committed
More tweaks
1 parent e9c4ccf commit e48aac5

16 files changed

+1213
-14
lines changed
Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
/*
2+
==============================================================================
3+
4+
This file is part of the YUP library.
5+
Copyright (c) 2025 - [email protected]
6+
7+
YUP is an open source library subject to open-source licensing.
8+
9+
The code included in this file is provided under the terms of the ISC license
10+
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
11+
to use, copy, modify, and/or distribute this software for any purpose with or
12+
without fee is hereby granted provided that the above copyright notice and
13+
this permission notice appear in all copies.
14+
15+
YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
16+
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
17+
DISCLAIMED.
18+
19+
==============================================================================
20+
*/
21+
22+
#include "yup_YupCore_bindings.h"
23+
#include "yup_YupEvents_bindings.h"
24+
25+
#define YUP_PYTHON_INCLUDE_PYBIND11_OPERATORS
26+
#define YUP_PYTHON_INCLUDE_PYBIND11_FUNCTIONAL
27+
#include "../utilities/yup_PyBind11Includes.h"
28+
29+
#include <atomic>
30+
#include <functional>
31+
32+
// =================================================================================================
33+
34+
namespace yup {
35+
36+
#if YUP_MAC
37+
extern void initialiseNSApplication();
38+
#endif
39+
40+
} // namespace yup
41+
42+
// =================================================================================================
43+
44+
namespace yup::Bindings {
45+
46+
using namespace yup;
47+
48+
namespace py = pybind11;
49+
using namespace py::literals;
50+
51+
void registerYupEventsBindings (py::module_& m)
52+
{
53+
// ============================================================================================ yup::NotificationType
54+
55+
py::enum_<NotificationType> (m, "NotificationType")
56+
.value ("dontSendNotification", NotificationType::dontSendNotification)
57+
.value ("sendNotification", NotificationType::sendNotification)
58+
.value ("sendNotificationSync", NotificationType::sendNotificationSync)
59+
.value ("sendNotificationAsync", NotificationType::sendNotificationAsync)
60+
.export_values();
61+
62+
// ============================================================================================ yup::ActionListener
63+
64+
py::class_<ActionListener, PyActionListener> classActionListener (m, "ActionListener");
65+
66+
classActionListener
67+
.def (py::init<>())
68+
.def ("actionListenerCallback", &ActionListener::actionListenerCallback)
69+
;
70+
71+
// ============================================================================================ yup::ActionBroadcaster
72+
73+
py::class_<ActionBroadcaster> classActionBroadcaster (m, "ActionBroadcaster");
74+
75+
classActionBroadcaster
76+
.def (py::init<>())
77+
.def ("addActionListener", &ActionBroadcaster::addActionListener)
78+
.def ("removeActionListener", &ActionBroadcaster::removeActionListener)
79+
.def ("removeAllActionListeners", &ActionBroadcaster::removeAllActionListeners)
80+
.def ("sendActionMessage", &ActionBroadcaster::sendActionMessage)
81+
;
82+
83+
// ============================================================================================ yup::AsyncUpdater
84+
85+
py::class_<AsyncUpdater, PyAsyncUpdater> classAsyncUpdater (m, "AsyncUpdater");
86+
87+
classAsyncUpdater
88+
.def (py::init<>())
89+
.def ("handleAsyncUpdate", &AsyncUpdater::handleAsyncUpdate)
90+
.def ("triggerAsyncUpdate", &AsyncUpdater::triggerAsyncUpdate)
91+
.def ("cancelPendingUpdate", &AsyncUpdater::cancelPendingUpdate)
92+
.def ("handleUpdateNowIfNeeded", &AsyncUpdater::handleUpdateNowIfNeeded)
93+
.def ("isUpdatePending", &AsyncUpdater::isUpdatePending)
94+
;
95+
96+
// ============================================================================================ yup::LockingAsyncUpdater
97+
98+
py::class_<LockingAsyncUpdater> classLockingAsyncUpdater (m, "LockingAsyncUpdater");
99+
100+
classLockingAsyncUpdater
101+
.def (py::init<std::function<void()>>())
102+
.def ("triggerAsyncUpdate", &LockingAsyncUpdater::triggerAsyncUpdate)
103+
.def ("cancelPendingUpdate", &LockingAsyncUpdater::cancelPendingUpdate)
104+
.def ("handleUpdateNowIfNeeded", &LockingAsyncUpdater::handleUpdateNowIfNeeded)
105+
.def ("isUpdatePending", &LockingAsyncUpdater::isUpdatePending)
106+
;
107+
108+
// ============================================================================================ yup::AsyncUpdater
109+
110+
py::class_<ChangeListener, PyChangeListener> classChangeListener (m, "ChangeListener");
111+
112+
classChangeListener
113+
.def (py::init<>())
114+
.def ("changeListenerCallback", &ChangeListener::changeListenerCallback)
115+
;
116+
117+
py::class_<ChangeBroadcaster> classChangeBroadcaster (m, "ChangeBroadcaster");
118+
119+
classChangeBroadcaster
120+
.def (py::init<>())
121+
.def ("addChangeListener", &ChangeBroadcaster::addChangeListener)
122+
.def ("removeChangeListener", &ChangeBroadcaster::removeChangeListener)
123+
.def ("removeAllChangeListeners", &ChangeBroadcaster::removeAllChangeListeners)
124+
.def ("sendChangeMessage", &ChangeBroadcaster::sendChangeMessage)
125+
.def ("sendSynchronousChangeMessage", &ChangeBroadcaster::sendSynchronousChangeMessage)
126+
.def ("dispatchPendingMessages", &ChangeBroadcaster::dispatchPendingMessages)
127+
;
128+
129+
// ============================================================================================ yup::MessageManager
130+
131+
py::class_<MessageManager> classMessageManager (m, "MessageManager");
132+
133+
py::class_<MessageManager::MessageBase, PyMessageBase<>> classMessageManagerMessageBase (classMessageManager, "MessageBase");
134+
135+
py::class_<MessageManager::Lock> classMessageManagerInnerLock (classMessageManager, "Lock");
136+
137+
classMessageManagerMessageBase
138+
.def (py::init<>())
139+
.def ("messageCallback", &MessageManager::MessageBase::messageCallback)
140+
.def ("post", [](py::object self) { return self.release().cast<MessageManager::MessageBase*>()->post(); })
141+
;
142+
143+
classMessageManagerInnerLock
144+
.def (py::init<>())
145+
.def ("enter", &MessageManager::Lock::enter)
146+
.def ("tryEnter", &MessageManager::Lock::tryEnter)
147+
.def ("exit", &MessageManager::Lock::exit)
148+
.def ("abort", &MessageManager::Lock::abort)
149+
;
150+
151+
classMessageManager
152+
.def_static ("getInstance", &MessageManager::getInstance, py::return_value_policy::reference)
153+
.def_static ("getInstanceWithoutCreating", &MessageManager::getInstanceWithoutCreating, py::return_value_policy::reference)
154+
.def_static ("deleteInstance", &MessageManager::deleteInstance)
155+
.def ("runDispatchLoop", &MessageManager::runDispatchLoop)
156+
.def ("stopDispatchLoop", &MessageManager::stopDispatchLoop)
157+
.def ("hasStopMessageBeenSent", &MessageManager::hasStopMessageBeenSent)
158+
#if YUP_MODAL_LOOPS_PERMITTED
159+
.def ("runDispatchLoopUntil", &MessageManager::runDispatchLoopUntil)
160+
#endif
161+
.def_static ("callAsync", &MessageManager::callAsync)
162+
.def ("callFunctionOnMessageThread", [](MessageManager& self, py::function func) -> py::object
163+
{
164+
void* result = nullptr;
165+
166+
if (func.is_none())
167+
py::pybind11_fail ("Invalid specified function to \"MessageManager::callFunctionOnMessageThread\"");
168+
169+
{
170+
py::gil_scoped_release release;
171+
172+
result = self.callFunctionOnMessageThread (+[](void* data) -> void*
173+
{
174+
py::gil_scoped_acquire acquire;
175+
176+
auto func = *reinterpret_cast<py::function*> (data);
177+
178+
return func().ptr();
179+
}, std::addressof (func));
180+
}
181+
182+
if (result == nullptr)
183+
return py::none();
184+
185+
return py::reinterpret_borrow<py::object> (reinterpret_cast<PyObject*> (result));
186+
})
187+
.def ("isThisTheMessageThread", &MessageManager::isThisTheMessageThread)
188+
.def ("setCurrentThreadAsMessageThread", &MessageManager::setCurrentThreadAsMessageThread)
189+
.def ("getCurrentMessageThread", [](const MessageManager& self) { return PyThreadID (self.getCurrentMessageThread()); })
190+
.def ("currentThreadHasLockedMessageManager", &MessageManager::currentThreadHasLockedMessageManager)
191+
.def_static ("existsAndIsLockedByCurrentThread", &MessageManager::existsAndIsLockedByCurrentThread)
192+
.def_static ("existsAndIsCurrentThread", &MessageManager::existsAndIsCurrentThread)
193+
.def_static ("broadcastMessage", &MessageManager::broadcastMessage)
194+
.def ("registerBroadcastListener", &MessageManager::registerBroadcastListener)
195+
.def ("deregisterBroadcastListener", &MessageManager::deregisterBroadcastListener)
196+
.def ("deliverBroadcastMessage", &MessageManager::deliverBroadcastMessage)
197+
;
198+
199+
// ============================================================================================ yup::Message
200+
201+
py::class_<Message, MessageManager::MessageBase, PyMessageBase<Message>> classMessage (m, "Message");
202+
203+
classMessage
204+
.def (py::init<>())
205+
;
206+
207+
py::class_<CallbackMessage, MessageManager::MessageBase, PyCallbackMessage<>> classCallbackMessage (classMessageManager, "CallbackMessage");
208+
209+
classCallbackMessage
210+
.def (py::init<>())
211+
.def ("messageCallback", &CallbackMessage::messageCallback)
212+
;
213+
214+
// ============================================================================================ yup::Message
215+
216+
py::class_<MessageListener, PyMessageListener> classMessageListener (m, "MessageListener");
217+
218+
classMessageListener
219+
.def (py::init<>())
220+
.def ("handleMessage", &MessageListener::handleMessage)
221+
.def ("postMessage", [](MessageListener& self, py::object message)
222+
{
223+
if (message.is_none() || ! py::isinstance<Message> (message))
224+
py::pybind11_fail ("Invalid specified message type in \"MessageListener::postMessage\"");
225+
226+
return self.postMessage (message.release().cast<Message*>());
227+
}, "message"_a)
228+
;
229+
230+
// ============================================================================================ yup::MessageManagerLock
231+
232+
py::class_<PyMessageManagerLock> classMessageManagerLock (m, "MessageManagerLock");
233+
234+
classMessageManagerLock
235+
.def (py::init<Thread*>(), "threadToCheckForExitSignal"_a = nullptr)
236+
.def (py::init<ThreadPoolJob*>())
237+
.def ("__enter__", [](PyMessageManagerLock& self)
238+
{
239+
if (self.thread != nullptr)
240+
self.state.emplace<MessageManagerLock> (self.thread);
241+
242+
else if (self.threadPoolJob != nullptr)
243+
self.state.emplace<MessageManagerLock> (self.threadPoolJob);
244+
245+
else
246+
py::pybind11_fail ("Invalid constructed \"MessageManagerLock\", either \"Thread\" or \"ThreadPoolJob\" must be provided");
247+
248+
return std::addressof (std::as_const (self));
249+
}, py::return_value_policy::reference)
250+
.def ("__exit__", [](PyMessageManagerLock& self, const std::optional<py::type>&, const std::optional<py::object>&, const std::optional<py::object>&)
251+
{
252+
self.state.emplace<std::monostate>();
253+
})
254+
.def ("lockWasGained", [](const PyMessageManagerLock& self)
255+
{
256+
if (auto lock = std::get_if<MessageManagerLock> (std::addressof (self.state)))
257+
return lock->lockWasGained();
258+
259+
return false;
260+
})
261+
;
262+
263+
// ============================================================================================ yup::Timer
264+
265+
py::class_<Timer, PyTimer> classTimer (m, "Timer");
266+
267+
classTimer
268+
.def (py::init<>())
269+
.def ("timerCallback", &Timer::timerCallback)
270+
.def ("startTimer", &Timer::startTimer)
271+
.def ("startTimerHz", &Timer::startTimerHz)
272+
.def ("stopTimer", &Timer::stopTimer)
273+
.def ("isTimerRunning", &Timer::isTimerRunning)
274+
.def ("getTimerInterval", &Timer::getTimerInterval)
275+
.def_static ("callAfterDelay", &Timer::callAfterDelay)
276+
.def_static ("callPendingTimersSynchronously", &Timer::callPendingTimersSynchronously)
277+
;
278+
279+
// ============================================================================================ yup::MultiTimer
280+
281+
py::class_<MultiTimer, PyMultiTimer> classMultiTimer (m, "MultiTimer");
282+
283+
classMultiTimer
284+
.def (py::init<>())
285+
.def ("timerCallback", &MultiTimer::timerCallback)
286+
.def ("startTimer", &MultiTimer::startTimer)
287+
.def ("stopTimer", &MultiTimer::stopTimer)
288+
.def ("isTimerRunning", &MultiTimer::isTimerRunning)
289+
.def ("getTimerInterval", &MultiTimer::getTimerInterval)
290+
;
291+
292+
// ============================================================================================ initialiseYup_GUI
293+
294+
#if ! YUP_PYTHON_EMBEDDED_INTERPRETER
295+
static std::atomic_int numScopedInitInstances = 0;
296+
297+
if (numScopedInitInstances.fetch_add(1) == 0)
298+
{
299+
initialiseYup_GUI();
300+
301+
YUPApplicationBase::createInstance = +[]() -> YUPApplicationBase* { return nullptr; };
302+
303+
#if YUP_MAC
304+
initialiseNSApplication();
305+
#endif
306+
}
307+
308+
#if 1
309+
py::cpp_function cleanupCallback ([](py::handle weakref)
310+
{
311+
if (numScopedInitInstances.fetch_sub(1) == 1)
312+
shutdownYup_GUI();
313+
314+
weakref.dec_ref();
315+
});
316+
317+
(void) py::weakref (m.attr ("MessageManager"), cleanupCallback).release();
318+
319+
#else
320+
auto atexit = py::module_::import ("atexit");
321+
atexit.attr ("register") (py::cpp_function([]
322+
{
323+
if (numScopedInitInstances.fetch_sub(1) == 1)
324+
shutdownYup_GUI();
325+
}));
326+
327+
#endif
328+
#endif
329+
}
330+
331+
} // namespace popsicle::Bindings

0 commit comments

Comments
 (0)