From 3e714940f8effb3a7255fa91cf7d2b8c1c141ba4 Mon Sep 17 00:00:00 2001 From: Alexandre Trendel Date: Mon, 7 Feb 2022 23:31:06 +0000 Subject: [PATCH] some doc (#466) --- doc/.latexmkrc | 4 ++ doc/Dockerfile | 4 ++ doc/Makefile | 30 ++++++++ doc/doc.pdf | Bin 0 -> 44585 bytes doc/doc.tex | 191 +++++++++++++++++++++++++++++++++++++++++++++++++ doc/enter.sh | 3 + 6 files changed, 232 insertions(+) create mode 100644 doc/.latexmkrc create mode 100644 doc/Dockerfile create mode 100644 doc/Makefile create mode 100644 doc/doc.pdf create mode 100644 doc/doc.tex create mode 100755 doc/enter.sh diff --git a/doc/.latexmkrc b/doc/.latexmkrc new file mode 100644 index 00000000..96515912 --- /dev/null +++ b/doc/.latexmkrc @@ -0,0 +1,4 @@ +$pdf_mode = 1; +$pdf_previewer = ''; +$pdflatex = 'lualatex -interaction=nonstopmode -synctex=1 -shell-escape %O %S'; +$out_dir = 'target'; \ No newline at end of file diff --git a/doc/Dockerfile b/doc/Dockerfile new file mode 100644 index 00000000..a7e83671 --- /dev/null +++ b/doc/Dockerfile @@ -0,0 +1,4 @@ +FROM atrendel/doxerlive:15-basic +RUN apk add gettext py3-pygments +ADD Makefile /var/doxerlive/ +RUN make install \ No newline at end of file diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 00000000..9ef43293 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,30 @@ +all: install build + +build: doc.tex + latexmk -c doc.tex + +watch: doc.tex + latexmk -pvc doc.tex + +DEPENDENCIES = luatex $\ + fontspec $\ + lm $\ + xcolor $\ + xcolor-solarized $\ + fontawesome $\ + xifthen $\ + ifmtarg $\ + pgf $\ + pgf-blur $\ + ec $\ + etoolbox $\ + xkeyval $\ + minted kvoptions fancyvrb fvextra upquote float ifplatform pdftexcmds xstring lineno framed catchfile + +install: + tlmgr update --self + tlmgr update texlive-scripts + tlmgr install $(DEPENDENCIES) + +clean: + latexmk -C \ No newline at end of file diff --git a/doc/doc.pdf b/doc/doc.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5c6f72086e7491ba8292a3b8a6a89d54e0b733d9 GIT binary patch literal 44585 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, >=latex, line width=.5mm, rounded corners, shorten >=1pt] +\tikzstyle{smalllink}=[->, >=latex, line width=.2mm, rounded corners, shorten >=1pt, dashed, gray] + + +\begin{document} + +\section{Data flow within Spot} + +\subsection{Overview} + +\paragraph{Single source of truth.} There is a single place that is considered the source of truth for anything that is related to the app state, and that is, well, the \texttt{AppState}. The app state aggregates the state of the UI, as well as the player state. This makes it easier to keep things in sync -- when possible, anything state-related should be read from the app state over some local, possibly out-of-date state. + +\paragraph{Centralized.} That state is centralized and unique. This allows various parts of the application to access any part of it, and conversely makes it easy to perform state updates that affect various and sometimes unrelated parts of the application. + +\paragraph{Controlled mutations.} There is only one way to modify the app state, and that is by dispatching \emph{actions} -- plain structs that represent a mutation to the state. Updates to the state produce \emph{events}, which \texttt{EventListeners} can use to update the UI. + +\begin{figure}[!h] + + \centering + + \begin{tikzpicture} + + \node[box, fill=spotifygreen, minimum height=2cm] (model) at (0, 0) {\ttfamily AppModel}; + \node[box] (ui) at (-4, -3) {Gtk widgets}; + \node[box] (listeners) at (4, -3) {Listeners}; + + \draw[link] (ui) edge[bend left=20] node[above, sloped] {\footnotesize actions} (model); + \draw[link] (model) edge[bend left=10] node[above, sloped] {\footnotesize events} (listeners); + \draw[link] (listeners) edge[bend left=60] node[below, sloped] {\footnotesize update} (ui); + \draw[link, dashed] (listeners) edge[bend left=10] node[below, sloped] {\footnotesize read-only access} (model); + + \end{tikzpicture} + + \caption{The data flow and and its relation to the UI -- the \texttt{AppModel} enforces read-only access to the state.} + \label{fig:flow} + +\end{figure} + +This draws heavy inspiration from the Flux architecture\footnote{See https://facebook.github.io/flux/docs/in-depth-overview for instance}; the one big difference here is that there is no way to automatically find out which portion of the UI should be updated. Instead, listeners are responsible for figuring out the updates to apply based on the events. + +It should be noted that the app state is only readable from the main thread for simplicity. + +\subsection{How actions are handled} + +Here is the relevant part of the code\footnote{Variables have been renamed for clarity...} related to handling actions and notifying listeners: + +\begin{minted}{rust} +let events = self.model.update_state(action); + +for event in events.iter() { + for listener in self.listeners.iter_mut() { + listener.on_event(event); + } +} +\end{minted} + +That first line is the only time that the app state is borrowed mutably -- to apply actions. + +On the technical side: all actions being dispatched, synchronous or not, are eventually sent through a \texttt{futures::channel::mpsc} channel. The consumer on the other end of the channel is a future that will be executed by GLib. This allows Gtk to process \emph{all actions} at its own pace, as part of its main loop. + +Note: futures are used a lot in the code to perform asynchronous operations such as calls to the Spotify API. To ease the use of futures, the dispatcher allows working with asynchronous actions, that is, futures that output one or more actions. Again, these futures are eventually handled in the main Gtk loop. + +\subsection{A listener: the player subsystem} + +Any element that wishes to update the state or react to changes from the state has to follow that same pattern. For instance, the ``player'' part of Spot receives \texttt{Commands} (mapped from events by a \texttt{PlayerNotifier}) to start playing music, and dispatches actions back the app through a \texttt{SpotifyPlayerDelegate} (see figure \ref{fig:player}). + +These two extra elements add some indirection so that the player is not too strongly coupled to the rest of the app (it does not and should not care about most events, afterall!). Moreover, those commands are handled in a separate thread where the player lives. + +\begin{figure}[!ht] + \centering + + \begin{tikzpicture} + + \node[box, fill=spotifygreen, minimum height=2cm] (model) at (0, 0) {\ttfamily AppModel}; + + \draw[smalllink] (-5, -3) node[sizedbox] (listeners) {Components} + -- +(0, -1.5) node[right] {update} + -- +(0, -2) node[sizedbox] (ui) {Gtk widgets}; + + \draw[smalllink] (5, -3) node[sizedbox, fill=gray] (notifier) {\ttfamily PlayerNotifier} + -- +(0, -1.5) node[right] {command} + -- +(0, -2) node[sizedbox, fill=gray] (player) {\ttfamily SpotifyPlayer} + -- +(0, -3.5) node[right] {calls} + -- +(0, -4) node[sizedbox, fill=gray] (delegate) {\parbox{\textwidth}{\ttfamily SpotifyPlayer Delegate}}; + + \draw[link] (ui) edge[bend right=32] node[below, sloped] {\footnotesize actions} (model); + \draw[link] (model) edge[bend right=10] node[above, sloped] {\footnotesize events} (listeners); + + \draw[link] (model) edge[bend left=10] node[above, sloped] {\footnotesize events} (notifier); + \draw[link] (delegate) edge[bend left=32] node[below, sloped] {\footnotesize actions} (model); + + \draw[dashed, gray] ($(notifier.north west) + (-0.25, 0.25)$) rectangle ($(delegate.south east) + (0.25, -0.25)$); + + \draw[dashed, gray] ($(listeners.north west) + (-0.25, 0.25)$) rectangle ($(ui.south east) + (0.25, -0.25)$); + + + \end{tikzpicture} + + \caption{The player subsystem} + \label{fig:player} + +\end{figure} + + +\subsection{Another listener: the MPRIS subsystem} + +Similarly, the MPRIS subsystem follows that same pattern. It spawns a small DBUS server that translates DBUS messages to actions, and an \texttt{AppPlaybackStateListener} listens to incoming events. + +One major difference is that the MPRIS server maintains its own state here, since the app state cannot be accessed from outside the main thread. To make sure this local state stays in sync, DBUS messages should not alter the local state directly -- instead, we should wait for a roundtrip through the app and incoming events. + +\section{Components} + +\subsection{Overview} + +Components are thin wrappers around Gtk widgets, dedicated to binding them so that they produce the right actions, and updating them when specific events occur by conforming to \texttt{EventListener}. + +\subsection{Modeling interactions} + +Components should have some associated \texttt{struct} to model the interactions with the rest of the app. Let's consider the play/pause button as an example. Its behavior is defined in the \texttt{PlaybackModel}: + +\begin{minted}{rust} +impl PlaybackModel { + fn is_playing(&self) -> bool { /**/ } + fn toggle_playback(&self) { /**/ } +} +\end{minted} + +What we need to make our button work is a way to know its current state (is a song playing?) and a way to change that state (toggling on activation). Note that it would be tempting to simply query the widget's state, which \emph{should} be in sync with the actual playback state, but what we should really do instead is query the app state, which is the one source of truth for anything state-related. + +Why do this? First, toggling the playback might fail (e.g. if no song is playing), but more importantly something else could alter the playback state (e.g. a DBUS query). + +\begin{minted}{rust} +fn is_playing(&self) -> bool { + self.app_model.get_state().playback.is_playing() +} +\end{minted} + +As for toggling the playback, remember that we can only mutate the state through actions (the \mintinline{rust}|get_state| call above returns some \mintinline{rust}|Deref|). In other words, we express what kind of action we want to perform, with no guarantee that it'll succeed. + +\begin{minted}{rust} +fn toggle_playback(&self) { + self.dispatcher.dispatch(PlaybackAction::TogglePlay.into()); +} +\end{minted} + +\subsection{Binding the widget} + +All that's left is binding the widget to our model. By wrapping our model in an \texttt{Rc}, it becomes easy to clone it into the kind of \texttt{'static} closure Gtk needs. + +\begin{minted}{rust} +// model is an Rc +widget.connect_play_pause(clone!(@weak model => move || model.toggle_playback())); +\end{minted} + +Finally, we need our component to listen to relevant events, and update our widget accordingly. + +\begin{minted}{rust} +impl EventListener for PlaybackControl { + fn on_event(&mut self, event: &AppEvent) { + match event { + AppEvent::PlaybackEvent(PlaybackEvent::PlaybackPaused) + | AppEvent::PlaybackEvent(PlaybackEvent::PlaybackResumed) => { + let is_playing = self.model.is_playing(); + self.widget.set_playing(is_playing); + } + /**/ + } + } +} +\end{minted} + +\end{document} \ No newline at end of file diff --git a/doc/enter.sh b/doc/enter.sh new file mode 100755 index 00000000..59d5ed07 --- /dev/null +++ b/doc/enter.sh @@ -0,0 +1,3 @@ +#!/bin/sh +docker build --network=host -t spot-doc . +docker run --rm -it -e THEUID="$(id -u "$USER")" -v "$PWD":/var/doxerlive spot-doc ash