Scheduler

The scheduler provides high-level functionalities to emit events. Using the scheduler should be preferred over using a dispatcher directly. Moreover, it provides the organization of globally available event loops.

template<Dispatchable Dispatcher>
class Scheduler : public obscura::Singleton<SchedulerImpl<Dispatcher>>

Scheduler is a singleton that manages all the loops in the application. The class inherits from obscura::SchedulerImpl that implements all the main functionalities. In order to use the scheduler, retrieve an instance of the scheduler using obscura::Scheduler::getInstance().

Template Parameters:

Dispatcher – Type of the dispatcher to be used by the scheduler.

template<Dispatchable Dispatcher>
class SchedulerImpl

Public Functions

inline auto getMainLoop() -> MainLoop<Dispatcher>*
inline auto getSyncLoop(const std::string &name) -> SyncLoop<Dispatcher, MainLoop<Dispatcher>>*
inline auto getAsyncLoop(const std::string &name) -> AsyncLoop<Dispatcher>*
inline void startMainLoop()
inline void stopMainLoop()
inline auto createSyncLoop(const std::string &name, const LoopConfig &config) -> SyncLoop<Dispatcher, MainLoop<Dispatcher>>*

Creates a new synchronous loop and registers it with the scheduler.

Parameters:
  • name – Name of the loop. The name is required to schedule event_system on the loop.

  • config – Config for the loop.

Returns:

Returns a pointer to the newly created loop.

inline auto createAsyncLoop(const std::string &name, const FPS &fps) -> AsyncLoop<Dispatcher>*

Creates a new asynchronous loop and registers it with the scheduler.

Parameters:
  • name – Name of the loop. The name is required to schedule event_system on the loop.

  • fpsFPS of the loop.

Returns:

Returns a pointer to the newly created loop.

template<class Event>
inline void scheduleOnce(Event &&event, const std::string &loopName)

Schedules an event on the specified loop.

Template Parameters:

Event – Type of the event.

Parameters:
  • eventEvent to be scheduled.

  • loopName – Name of the loop on which the event is to be scheduled.

template<class Event>
inline void scheduleOnce(Event &&event)

Schedules an event on the main loop.

Template Parameters:

Event – Type of the event.

Parameters:

eventEvent to be scheduled.

template<class Event, class Duration>
inline void scheduleOnce(Event &&event, const Duration &delay, const std::string &loopName)

Schedules an event on the specified loop after a delay.

Template Parameters:
  • Event – Type of the event.

  • Duration – Type of the duration. Usually, a std::chrono::duration.

Parameters:
  • eventEvent to be scheduled.

  • delay – Delay after which the event is to be scheduled.

  • loopName – Name of the loop on which the event is to be scheduled.

template<class Event, class Duration>
inline void scheduleOnce(Event &&event, const Duration &delay)

Schedules an event on the main loop.

Template Parameters:
  • Event – Type of the event.

  • Duration – Type of the duration. Usually, a std::chrono::duration.

Parameters:
  • eventEvent to be scheduled.

  • delay – Delay after which the event is to be scheduled.

template<class Listener>
inline void registerListener(Listener &listener, const std::string &loopName)
template<class Listener>
inline void registerListener(Listener &listener)

Registers a listener with the main loop.

Template Parameters:

Listener – Type of the listener.

Parameters:

listener – The listener to be registered.

template<class Listener>
inline void unregisterListener(Listener &listener, const std::string &loopName)

Unregisters a listener from the specified loop.

Template Parameters:

Listener – Type of the listener.

Parameters:
  • listener – The listener to be unregistered.

  • loopName – Name of the loop from which the listener is to be unregistered.

template<class Listener>
inline void unregisterListener(Listener &listener)

Unregisters a listener from the main loop.

Template Parameters:

Listener – Type of the listener.

Parameters:

listener – The listener to be unregistered.

Examples

Schedule events with a delay

#include <obscura/obscura.hxx>

using namespace obscura;
using namespace std::chrono_literals;

class Listener {
  public:
    void onKeyPressed(const KeyPressedEvent& event) {  // NOLINT(readability-convert-member-functions-to-static)
        // The function cannot be static because it needs to be registered as a callback.
        logInfo("Key pressed: {}", event.getKey());
    }
};

auto main() -> int {
    try {
        auto application = obscura::Application<HeadlessWindow>();
        auto listener = Listener {};

        auto defaultApplicationListener = DefaultApplicationListener<Application<HeadlessWindow>>(application);

        auto& scheduler = Scheduler<Dispatcher>::getInstance();

        scheduler.registerListener(listener);
        scheduler.registerListener(defaultApplicationListener);


        for (std::size_t i = 0; i < 10;
             ++i) {  // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
            auto event = KeyPressedEvent { static_cast<int>(i) };
            logInfo("Scheduling event with key {} and a {}s delay", event.getKey(), i);
            scheduler.scheduleOnce(event, std::chrono::seconds { i });
        }
        scheduler.scheduleOnce(
            WindowEvent(EventCause::WindowClosed),
            10s);  // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)


        application.run();
    } catch (const std::exception& e) {
        logError("Exception caught: {}", e.what());
    }

    return EXIT_SUCCESS;
}

Use multiple loops

#include <obscura/obscura.hxx>

using namespace obscura;
using namespace std::chrono_literals;

class Listener {
  public:
    explicit Listener(std::string shoutOut)
      : shoutOut(std::move(shoutOut)) {
    }

    void onTick(const TickEvent& /*unused*/) {
        logInfo("{}", shoutOut);
    }

  private:
    std::string shoutOut;
};

// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
auto main() -> int {
    try {
        auto application = obscura::Application<HeadlessWindow>();
        auto defaultApplicationListener = DefaultApplicationListener<Application<HeadlessWindow>>(application);

        auto mainListener = Listener("cha");
        auto syncListener1 = Listener("Dooo!");
        auto syncListener2 = Listener("MAA!");

        auto& scheduler = Scheduler<Dispatcher>::getInstance();

        scheduler.registerListener(mainListener);
        scheduler.registerListener(defaultApplicationListener);

        auto loopConfig1 = LoopConfig::runAtFractionOfBaseLoop(0.5).build();
        scheduler.createSyncLoop("syncLoop1", loopConfig1);

        auto loopConfig2 = LoopConfig::runAtFractionOfBaseLoop(0.25).build();
        scheduler.createSyncLoop("syncLoop2", loopConfig2);

        scheduler.registerListener(syncListener1, "syncLoop1");
        scheduler.registerListener(syncListener2, "syncLoop2");

        scheduler.scheduleOnce(WindowEvent { EventCause::WindowClosed }, 2s);


        application.run();
    } catch (const std::exception& e) {
        logError("Exception caught: {}", e.what());
    }

    return EXIT_SUCCESS;
}

// NOLINTEND(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)

Listen to inputs in a headless window

#include <chrono>
#include <obscura/obscura.hxx>
#include <thread>

using namespace obscura;

class InputListener {
  public:
    // NOLINTBEGIN(readability-convert-member-functions-to-static)
    [[maybe_unused]] void onKeyPressed(const KeyPressedEvent& event) {
        logInfo("Key pressed: {}", event.getKey());
    }

    [[maybe_unused]] void onKeyReleased(const KeyReleasedEvent& event) {
        logInfo("Key released: {}", event.getKey());
    }

    [[maybe_unused]] void onMouseMoved(const MouseMovedEvent& event) {
        logInfo("Mouse moved to x: {}, y: {}", event.getCurrentPosition().x, event.getCurrentPosition().y);
    }

    // NOLINTEND(readability-convert-member-functions-to-static)
};

// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
auto main() -> int {
    try {
        auto application = obscura::Application<HeadlessWindow>();
        auto inputListener = InputListener {};
        auto applicationListener = DefaultApplicationListener<Application<HeadlessWindow>>(application);
        Scheduler<Dispatcher>::getInstance().registerListener(inputListener);
        Scheduler<Dispatcher>::getInstance().registerListener(applicationListener);

        std::thread thread1([&application]() {
            std::this_thread::sleep_for(std::chrono::seconds { 2 });
            application.getWindowEventEmitter().pressKey(100);
        });

        std::thread thread2([&application]() {
            std::this_thread::sleep_for(std::chrono::seconds { 2 });
            application.getWindowEventEmitter().pressKey(90);
            std::this_thread::sleep_for(std::chrono::seconds { 3 });
            Scheduler<Dispatcher>::getInstance().scheduleOnce(WindowEvent(EventCause::WindowClosed));
        });

        std::thread thread3([&application]() {
            std::this_thread::sleep_for(std::chrono::seconds { 1 });
            application.getWindowEventEmitter().moveMouseFromTo(glm::vec2 { 0, 0 }, glm::vec2 { 200, 200 }, 200, 0.2F);
        });


        application.run();

        thread1.join();
        thread2.join();
        thread3.join();
    } catch (const std::exception& e) {
        logError("Exception caught: {}", e.what());
    }

    return EXIT_SUCCESS;
}

// NOLINTEND(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)