Program Listing for File trigger_manager.hpp#

Return to documentation for file (src/trigger/trigger_manager.hpp)

#pragma once
#include <QObject>
#include <memory>

#include "core/settings.hpp"
#include "trigger/trigger_types.hpp"

namespace mosaic {

/// @brief Central trigger coordinator — aggregates events from all sources
///        into a single signal and delegates to the CSV recorder.
///
/// TriggerManager is owned by Application for the full application lifetime.
/// It manages:
/// - **Keyboard triggers** — application-level event filters that fire on
///   configured key sequences.
/// - **Parallel port triggers** — InpOut32-based bit-edge detection (Windows),
///   receiving external pulses (e.g. an EEG amplifier's trigger-out cable),
///   and optionally sending a recording start/stop marker back out on the
///   same port's Control register (see ParallelPortConfig::sendRecordingMarker).
/// - **TriggerRecorder** — writes every TriggerEvent to @c trigger.csv.
///
/// Call reload() whenever TriggerSettings change (e.g. the user edits key
/// bindings in the UI) to rebuild all sources from the current settings.
///
/// @par Thread safety
/// event_received() and on_trigger_fired() are always invoked on the **main
/// thread** (keyboard filters use Qt::QueuedConnection).
///
/// @see TriggerEvent, KeyboardTrigger, ParallelPortTrigger
class TriggerManager : public QObject {
    Q_OBJECT
   public:
    /// @param settings  Trigger settings (key bindings, serial/parallel ports).
    ///                  Held by reference — must outlive this object.
    /// @param parent    Qt parent object.
    explicit TriggerManager(TriggerSettings& settings, QObject* parent = nullptr);
    ~TriggerManager() override;

    /// @brief Rebuilds all trigger sources to match the current TriggerSettings.
    ///
    /// Tears down existing sources, then re-creates keyboard triggers and
    /// parallel-port pollers from the current state of TriggerSettings. Call
    /// this after the user changes key bindings or parallel-port
    /// configuration in the settings UI.
    void reload();

    /// @brief Opens the CSV output file and starts recording trigger events.
    ///
    /// Also drives high any parallel port configured with
    /// ParallelPortConfig::sendRecordingMarker, so an EEG amplifier (or any
    /// other device listening on that port's Control-register INIT pin) sees
    /// a rising edge marking the start of this recording.
    ///
    /// @param csvPath  Absolute path for the output @c trigger.csv.
    void start_recording(const QString& csvPath);

    /// @brief Flushes and closes the CSV file.
    ///
    /// Also drives low any parallel port configured with
    /// ParallelPortConfig::sendRecordingMarker (falling edge marking the end
    /// of this recording).
    void stop_recording();

    /// @returns @c true while the CSV output file is open.
    [[nodiscard]] bool is_recording() const;

    /// @returns The number of active keyboard trigger event filters.
    [[nodiscard]] int keyboard_trigger_count() const;

    /// @param index  Zero-based index.
    /// @returns      The KeyboardTrigger at @p index as a @c QObject* so the
    ///               settings panel can connect to its @c count_changed() signal
    ///               without including the private header.  Returns @c nullptr
    ///               if @p index is out of range.
    [[nodiscard]] QObject* keyboard_trigger_at(int index) const;

   signals:
    /// Emitted on the main thread for every trigger event, regardless of source.
    /// Connect to this signal to react to events in real time.
    ///
    /// @param event  A copy of the TriggerEvent (source, label, value, timestamp, action).
    void event_received(mosaic::TriggerEvent event);

    /// Emitted when a trigger fires with action StartRecording or StopRecording.
    /// Application connects this to RecordManager::start() / stop().
    ///
    /// @param action  The action requested (Start or Stop).
    /// @param event   The trigger event that caused the action.
    void action_requested(mosaic::TriggerAction action, mosaic::TriggerEvent event);

   private slots:
    void on_trigger_fired(mosaic::TriggerEvent event);

   private:
    struct Impl;
    std::unique_ptr<Impl> d;
};

} // namespace mosaic