UI-Dispatch: how to integrate into the NotificationService
WIP setup of an empty framework
This commit is contained in:
parent
46fc900980
commit
07c9ed15e8
6 changed files with 110 additions and 18 deletions
80
src/gui/ctrl/ui-dispatcher.hpp
Normal file
80
src/gui/ctrl/ui-dispatcher.hpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
UI-DISPATCHER.hpp - dispatch invocations into the UI event thread
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2017, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file ui-dispatcher.hpp
|
||||
** Allow dispatch of self-contained code blocks (lambdas) into the main UI event thread.
|
||||
** GTK is _not threadsafe by design_ -- thus it is mandatory to dispatch any asynchronous invocations
|
||||
** from external facilities in a controlled way into the main event loop. Unfortunately, this becomes
|
||||
** a tricky undertaking when these external invocations need to pass argument data. This helper serves
|
||||
** to accommodate such problems, relying on the automatic (heap based) argument storage of std::function.
|
||||
** Client code provides the actual invocation in the form of _completely closed lambdas._
|
||||
**
|
||||
** @warning these lambdas will be stored in a synchronised queue and invoked out of the original
|
||||
** call stack. It is the client's responsibility to ensure that all bindings in the closure
|
||||
** are either *by value*, or *by smart-ptr*, or alternatively to ensure the lifecycle of any
|
||||
** referred entity exceeds the lifespan of the UI-Loop. Since the shutdown-order of Lumiera's
|
||||
** subsystems is not deterministic, this rules out passing references to anything tied to some
|
||||
** subsystem lifecycle. Referring to a static singleton is acceptable though.
|
||||
**
|
||||
** @see NotificationService
|
||||
** @see CallQueue_test
|
||||
*/
|
||||
|
||||
|
||||
#ifndef GUI_CTRL_UI_DISPATCHER_H
|
||||
#define GUI_CTRL_UI_DISPATCHER_H
|
||||
|
||||
#include "gui/gtk-base.hpp"
|
||||
#include "lib/call-queue.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
//#include <string>
|
||||
|
||||
|
||||
namespace gui {
|
||||
namespace ctrl {
|
||||
|
||||
// using std::string;
|
||||
// class GlobalCtx;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Helper to dispatch code blocks into the UI event thread for execution.
|
||||
* Implementation of the actual dispatch is based on `Glib::Dispatcher`
|
||||
*/
|
||||
class UiDispatcher
|
||||
: boost::noncopyable
|
||||
{
|
||||
|
||||
public:
|
||||
UiDispatcher()
|
||||
{ }
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
|
||||
}}// namespace gui::ctrl
|
||||
#endif /*GUI_CTRL_UI_DISPATCHER_H*/
|
||||
|
|
@ -141,5 +141,5 @@ namespace ctrl {
|
|||
|
||||
|
||||
|
||||
}}// namespace gui::workspace
|
||||
}}// namespace gui::ctrl
|
||||
#endif /*GUI_CTRL_UI_MANAGER_H*/
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
|
||||
#include "gui/ctrl/ui-manager.hpp"
|
||||
#include "gui/ctrl/ui-dispatcher.hpp"
|
||||
#include "gui/notification-service.hpp"
|
||||
#include "include/logging.h"
|
||||
#include "lib/util.hpp"
|
||||
|
|
@ -44,6 +45,7 @@ extern "C" {
|
|||
|
||||
|
||||
using lib::diff::TreeMutator;
|
||||
using gui::ctrl::UiDispatcher;
|
||||
using gui::ctrl::BusTerm;
|
||||
using std::string;
|
||||
using util::cStr;
|
||||
|
|
@ -240,6 +242,7 @@ namespace gui {
|
|||
*/
|
||||
NotificationService::NotificationService (ctrl::BusTerm& upLink, ctrl::UiManager& uiManager)
|
||||
: BusTerm{lib::idi::EntryID<NotificationService>{}, upLink}
|
||||
, dispatch_{new UiDispatcher{}}
|
||||
, uiManager_{uiManager}
|
||||
, implInstance_(this,_instance)
|
||||
, serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_GuiNotification, 0,lumieraorg_GuiNotificationService))
|
||||
|
|
@ -248,4 +251,7 @@ namespace gui {
|
|||
}
|
||||
|
||||
|
||||
NotificationService::~NotificationService() { } // emit dtors of embedded objects here...
|
||||
|
||||
|
||||
} // namespace gui
|
||||
|
|
|
|||
|
|
@ -44,12 +44,15 @@
|
|||
#include "gui/ctrl/bus-term.hpp"
|
||||
#include "lib/singleton-ref.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
|
||||
namespace gui {
|
||||
|
||||
namespace ctrl {
|
||||
class UiManager;
|
||||
class UiDispatcher;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -80,20 +83,23 @@ namespace gui {
|
|||
void mutate (ID uiElement, DiffMessage&) override; ////////////////////////////////////////TICKET #1066 : how to pass a diff message
|
||||
void triggerGuiShutdown (string const& cause) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<ctrl::UiDispatcher> dispatch_;
|
||||
ctrl::UiManager& uiManager_;
|
||||
|
||||
|
||||
/* === Interface Lifecycle === */
|
||||
|
||||
using ServiceInstanceHandle = lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_GuiNotification, 0)
|
||||
, GuiNotification
|
||||
> ;
|
||||
ctrl::UiManager& uiManager_;
|
||||
|
||||
lib::SingletonRef<GuiNotification> implInstance_;
|
||||
ServiceInstanceHandle serviceInstance_;
|
||||
|
||||
|
||||
|
||||
public:
|
||||
~NotificationService();
|
||||
NotificationService (ctrl::BusTerm& upLink, ctrl::UiManager& uiManager);
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2985,19 +2985,19 @@ This treatment ensures each nested diff is consumed within a properly typed cont
|
|||
&rarr; this is the purpose of the {{{DataCap}}} within our [[generic node element|GenNode]]
|
||||
</pre>
|
||||
</div>
|
||||
<div title="GuiNotificationFacade" modifier="Ichthyostega" created="200902080659" modified="201708041509" tags="spec" changecount="2">
|
||||
<div title="GuiNotificationFacade" modifier="Ichthyostega" created="200902080659" modified="201708101341" tags="spec" changecount="9">
|
||||
<pre>LayerSeparationInterface provided by the GUI.
|
||||
Access point for the lower layers to push information and state changes (asynchronously) to the GUI. Most operations within Lumiera are in fact initiated by the user through the GUI. In the course of such actions, the GUI uses the services of the lower layer and typically receives an immediate synchronous response to indicate the change was noted. Yet often, these operations may cause additional changes to happen asynchronously from the GUI's perspective. For example, an edit operation might trigger a re-build of the low-level model, which then detects an error. Any such consequences and notifications can be "cast" up into the UI, using this service here.
|
||||
|
||||
Beyond that, a call to trigger shutdown of the UI layer is also exposed here.
|
||||
Beyond that, a call to trigger shutdown of the UI layer is also exposed here -- which becomes relevant when other [[sub-systems|Subsystem]] initiate general shutdown.
|
||||
|
||||
!Lifecycle and Threading concerns
|
||||
The GuiNotificationFacade is installed as part of building the UI-Bus, which happens while establishing the GuiTopLevel. Yet there is a specific twist, insofar GTK is ''not threadsafe by design''. Any event handling and presentation changes will happen from within a dedicated UI event loop, which need to be explicitly started, after all of the primary windows and widgets are created and wired. Only after this point the UI becomes //life.//
|
||||
The GuiNotificationFacade is installed as part of and managed by the ''UI Manager'', and connected to the UI-Bus, which happens while establishing the GuiTopLevel. Yet there is a specific twist, insofar GTK is ''not threadsafe by design''. Any event handling and presentation changes will happen from within a dedicated UI event loop, which needs to be started explicitly, after all of the primary windows and widgets are created and wired. Only after this point the UI becomes //life.//
|
||||
|
||||
A dedicated ''activation state'' is necessary for this reason -- which within the implementation translate into a queuing and dispatching facility to reschedule any calls into the UI event thread ''asynchronously''.
|
||||
|
||||
!Addressing of UI elements
|
||||
Most calls in this interface require to specify a receiver or target, in the form of an element ID. It is assumed the caller effectively just knows these IDs, typically because the same IDs are also used as element IDs for the corresponding session entities. And, to start with, the whole UI representation of the session was at some point generated by //population diff messages,// which used the same IDs to indicate the creation of the corresponding UI representation elements.
|
||||
Most calls in this interface require to specify a receiver or target, in the form of an element ID. It is assumed the caller effectively just knows these ~IDs, typically because the same ~IDs are also used as element ~IDs for the corresponding session entities. And, to start with, the whole UI representation of the session was at some point generated by //population diff messages,// which used the same ~IDs to indicate the creation of the corresponding UI representation elements.
|
||||
</pre>
|
||||
</div>
|
||||
<div title="GuiPattern" creator="Ichthyostega" modifier="Ichthyostega" created="201410160054" modified="201612021723" tags="overview" changecount="4">
|
||||
|
|
@ -6753,7 +6753,7 @@ And last but not least: the difficult part of this whole concept is encapsulated
|
|||
|
||||
{{red{WIP ... draft}}}</pre>
|
||||
</div>
|
||||
<div title="SessionSubsystem" creator="Ichthyostega" modifier="Ichthyostega" created="201612150347" modified="201701140727" tags="def impl SessionLogic img" changecount="11">
|
||||
<div title="SessionSubsystem" creator="Ichthyostega" modifier="Ichthyostega" created="201612150347" modified="201708101334" tags="def impl SessionLogic img" changecount="13">
|
||||
<pre>//A subsystem within Proc-Layer, responsible for lifecycle and access to the editing [[Session]].//
|
||||
[img[Structure of the Session Subsystem|uml/Session-subsystem.png]]
|
||||
|
||||
|
|
@ -6764,7 +6764,7 @@ The ProcDispatcher is at the heart of the //Session Subsystem.// Because the off
|
|||
As far as lifecycle is concerned, the »session subsystem« has to be distinguished from the //session proper,// which is just a data structure with its own, separate lifecycle considerations. Accessing the session data only makes sense when this data structure is fully loaded, while the //session subsystem,// deals with performing commands on the session and with triggering the builder runs.
|
||||
|
||||
!!!start-up
|
||||
The session subsystem lifecycle translates into method invocations on the {{{ProcDispatcher}}}, which in turn manages the parts actually implementing the session command processing and builder operations. This relation is expressed by holding onto the implementation as a PImpl. As long as the {{{DispatcherLoop}}} object exists, the session subsystem can be considered in //running state.// This is equivalent to the following
|
||||
The session subsystem lifecycle translates into method invocations on the {{{ProcDispatcher}}}, which in turn manages the parts actually implementing the session command processing and builder operations. This relation is expressed by holding onto the implementation as a //~PImpl.// As long as the {{{DispatcherLoop}}} object exists, the session subsystem can be considered in //running state.// This is equivalent to the following
|
||||
* the ''session loop thread'' is spawned. This thread performs all of the session and builder operations (single-threaded).
|
||||
* the {{{SessionCommandService}}} is started and connected as implementation of the {{{SessionCommand}}} façade.
|
||||
|
||||
|
|
@ -7245,7 +7245,7 @@ h1,h2,h3,h4,h5,h6 {
|
|||
/*}}}*/
|
||||
</pre>
|
||||
</div>
|
||||
<div title="Subsystem" creator="Ichthyostega" modifier="Ichthyostega" created="201402162047" modified="201402162058" tags="def" changecount="6">
|
||||
<div title="Subsystem" creator="Ichthyostega" modifier="Ichthyostega" created="201402162047" modified="201708101333" tags="def" changecount="8">
|
||||
<pre>The term ''Subsystem'' denotes a coherent and tightly coupled and integrated part of the application's functionality.
|
||||
A Subsystem exists as an entity at runtime -- it has state and a lifecycle and might depend on other subsystems.
|
||||
The arrangement of subsystems is part of the architecture; separation into subsystems is a more fine grained structuring and somewhat orthogonal to the arrangement of functionality into ''Layers'', which are more of conceptual nature.
|
||||
|
|
@ -7255,8 +7255,7 @@ The arrangement of subsystems is part of the architecture; separation into subsy
|
|||
:the actual user interface is loaded and started as a plug-in. It is typically monolithic and thus counts as //one// subsystem (but there might be several alternative interfaces)
|
||||
;~Proc-Layer
|
||||
:this is the metadata and organisational layer and serves to accomodate the user oriented view to the technical necessities of rendering and playback
|
||||
:* [[Session]]
|
||||
:* [[Builder]]
|
||||
:* SessionSubsystem, comprised of the [[Session(datastructure)|Session]] and the [[Builder]]
|
||||
:* [[Player]]
|
||||
:* RenderEngine
|
||||
;Backend
|
||||
|
|
|
|||
|
|
@ -928,9 +928,9 @@
|
|||
<linktarget COLOR="#872666" DESTINATION="ID_286857196" ENDARROW="Default" ENDINCLINATION="-584;-56;" ID="Arrow_ID_1606936924" SOURCE="ID_405470138" STARTARROW="Default" STARTINCLINATION="1461;0;"/>
|
||||
<icon BUILTIN="flag-pink"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1493753249399" ID="ID_1023343635" MODIFIED="1501854181648" TEXT="#1098 hand-over in UI-Thread">
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1493753249399" ID="ID_1023343635" MODIFIED="1502371638606" TEXT="#1098 hand-over in UI-Thread">
|
||||
<linktarget COLOR="#a75677" DESTINATION="ID_1023343635" ENDARROW="Default" ENDINCLINATION="-117;347;" ID="Arrow_ID_274836249" SOURCE="ID_1146157818" STARTARROW="None" STARTINCLINATION="-501;-573;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node CREATED="1501804992301" ID="ID_1647263044" MODIFIED="1501804997702" TEXT="schedule event"/>
|
||||
<node CREATED="1501804985286" ID="ID_320977714" MODIFIED="1501804991521" TEXT="GTK-Mechanismus">
|
||||
<node CREATED="1501850591393" ID="ID_1831316800" MODIFIED="1501850596037" TEXT="Glib::Dispatcher"/>
|
||||
|
|
@ -1034,17 +1034,18 @@
|
|||
<node CREATED="1501938690626" ID="ID_353231581" MODIFIED="1501938729247" TEXT="thread safe queue"/>
|
||||
<node CREATED="1501938705321" ID="ID_1924064403" MODIFIED="1501938711076" TEXT="lambdas für Argument-binding"/>
|
||||
</node>
|
||||
<node CREATED="1501938776839" ID="ID_1031878628" MODIFIED="1501938783474" TEXT="Anwendung explizit">
|
||||
<node CREATED="1501938776839" ID="ID_1031878628" MODIFIED="1502371627144" TEXT="Anwendung explizit">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node CREATED="1501939110138" ID="ID_203135379" MODIFIED="1501939129491" TEXT="Lambda konstruieren"/>
|
||||
<node CREATED="1501939132431" ID="ID_1400757157" MODIFIED="1501939149321" TEXT="Funktion zum Triggern"/>
|
||||
<node CREATED="1501939149837" ID="ID_1733976265" MODIFIED="1501939157472" TEXT="diese an Glib::Dispatcher binden"/>
|
||||
</node>
|
||||
<node CREATED="1501939160907" ID="ID_188350210" MODIFIED="1501939167821" TEXT="Convenience-Wrapper">
|
||||
<icon BUILTIN="help"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1501939174506" ID="ID_504999068" MODIFIED="1501939191040" TEXT="in NotificationService integrieren">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node CREATED="1501939160907" ID="ID_188350210" MODIFIED="1502371616753" TEXT="Convenience-Wrapper">
|
||||
<icon BUILTIN="help"/>
|
||||
</node>
|
||||
<node CREATED="1501939193031" HGAP="3" ID="ID_1045913810" MODIFIED="1501939200829" TEXT="Test" VSHIFT="20">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1501939204166" ID="ID_383444966" MODIFIED="1502075950777" TEXT="CallQueue_test">
|
||||
|
|
|
|||
Loading…
Reference in a new issue