Flip Basics Programming Guide

Signalling the Model

Now that we can control the model with persistent data, this chapter exposes how to control the model with non-persistent data, that is data that is not going to be saved. There multiple ways to achieve that in flip, with slightly different meaning, exposed here.

This chapter will use the model declared in the chapter Declaring the Model.

Signal and Message

Flip has two signal types : Signal and Message. They operate in a relative similar ways, but have different meanings.

Message

Message is a flip type. It does not store data, but allows to share non-persitent data between clients. For this reason, it is part of the document model.

When a Message is signalled, the content of the data is shared with every documents and clients connected to the same document, but the data itself is not saved within the document.

A Message describes a transient change of state of a flip Object. Receiving the data sent is done through the observer, as a Message is part of the model, which means that the data from a message is in a transaction where over members might be changed. This allows to receive synchronously changes of the model, that would be hard to manage with a Signal.

Let's take an example to illustrate this point. Let's consider a play head in a audio or video application. From a feature point of view, the play head has a position and a state (playing or not playing). The position expresses the change in the model, not the actual position as shown in the view.

If suddenly the user want to change both the position and the state of the play head, then using a Message ensure that those two properties are going to be changed synchronously and transactionally into the model.

This is not the case with Signal where those changes would come one after the other, possibly in a different order.

Example :

class PlayHead : public Object
{
public:
   enum State
   {
      State_STOPPED = 0,
      State_PLAYING,
   };
   Float position;
   Message <State> state;
};
// controlling the model, jumping to position 2.0
// and playing at the same time
auto & playhead = song.playhead;
playhead.position = 2.0;
playhead.state.send (PlayHead::State_PLAYING);
playhead.document ().commit ();
// observing the model, this allow to see the
// changes as a transaction
void  document_changed (PlayHead & playhead)
{
   if (playhead.position.changed ())
   {
      // ...
   }
   if (playhead.state.changed ())
   {
      playhead.state.get ([&](bool forward_flag, State state) {
         // ...
      });
   }
}

Finally, because it is part of the document model, Message has a forward_flag as part of its function definition, to allow for the observer to take the direction of execution of the transaction to be taken into account. The semantic of backward for a Message data depends on the semantic of the model. The only rule is to have the proper behavior if the transaction containing the Message is refused by the server so rollbacked locally, or the meaning of undoing a transaction when the transaction contains operations over a Message.

Signal

On the other hand, Signal is not a flip type. It also does not store data, and the data is not shared between other clients. It is not part of the document model.

When a Signal is signalled, the content of the data is shared only with every documents connected through a Hub on the local machine, and the data itself is not saved within the document.

A Signal describes a signal directed to a flip Object.

Receiving the data sent is done with the help of a signal connection which is outside the model, and is not received through the document_changed virtual method of the observer. Therefore it is not synchronous to changes of the model.

Because of the way it internally works, Signal is also preferred when the amount of data to exchange per second can be enormous.

Let's take the previous example and let's say that the realtime position of the playhead (not the one reflected in the model) is going to be signaled from the audio engine to the GUI using a Signal.

When the play head is constructed, the observer of the GUI will set up a signal connection to receive the realtime position, as illustrated below.

class PlayHead : public Object
{
public:
   enum
   {
      Signal_RT_POSITION = 0,
   };
   PlayHead () : signal_rt_position (Signal_RT_POSITION, *this) {}
   Signal <double> signal_rt_position;
};
void  document_changed (PlayHead & playhead)
{
   if (playhead.added ())
   {
      // Store the SignalConnection in an entity
      playhead.entity ().emplace <SignalConnection> (
         playhead.signal_rt_position.connect ([](double position){
            // update realtime position in the GUI
         });
      );
   }
   if (playhead.removed ())
   {
      playhead.entity ().erase <SignalConnection> ();
   }
}
// From audio engine :
double position = audio_engine.get_rt_position ();
playhead.signal_rt_position (position);

The signal also decouple the sender from the receiver, in the sense that the sender does not need to know who is going to receive the data, and even is the data is going to be received at all.

This is also a safe mechanism, if an object is already deleted in a document, while still present in another document : if the signal does not have a recipient anymore, it is silently dropped.

The next chapter, Working with a Remote Server will guide you through setting up a collaboration network.