Browse Source

Add signal and connection-based event handling

master
C. J. Howard 1 year ago
parent
commit
849043dd5c
10 changed files with 448 additions and 9 deletions
  1. +26
    -5
      src/application.cpp
  2. +47
    -0
      src/application.hpp
  3. +47
    -0
      src/event/signal.cpp
  4. +214
    -0
      src/event/signal.hpp
  5. +51
    -0
      src/game/state/main-menu.cpp
  6. +6
    -0
      src/game/state/main-menu.hpp
  7. +28
    -0
      src/render/stage.cpp
  8. +27
    -2
      src/render/stage.hpp
  9. +1
    -1
      src/render/stage/culling-stage.cpp
  10. +1
    -1
      src/render/stage/culling-stage.hpp

+ 26
- 5
src/application.cpp View File

@ -258,8 +258,6 @@ void application::resize_window(int width, int height)
// Resize and center window
SDL_SetWindowPosition(sdl_window, x, y);
SDL_SetWindowSize(sdl_window, width, height);
window_resized();
}
void application::set_fullscreen(bool fullscreen)
@ -539,11 +537,31 @@ void application::process_events()
case SDL_WINDOWEVENT:
{
if (sdl_event.window.event == SDL_WINDOWEVENT_RESIZED)
switch (sdl_event.window.event)
{
window_resized();
case SDL_WINDOWEVENT_SIZE_CHANGED:
window_resized();
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
window_focus_signal.emit(true);
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
window_focus_signal.emit(false);
break;
case SDL_WINDOWEVENT_MOVED:
window_motion_signal.emit(sdl_event.window.data1, sdl_event.window.data2);
break;
case SDL_WINDOWEVENT_CLOSE:
window_close_signal.emit();
break;
default:
break;
}
break;
}
@ -575,4 +593,7 @@ void application::window_resized()
event.h = window_dimensions[1];
event_dispatcher->queue(event);
window_size_signal.emit(window_dimensions[0], window_dimensions[1]);
viewport_size_signal.emit(viewport_dimensions[0], viewport_dimensions[1]);
}

+ 47
- 0
src/application.hpp View File

@ -29,6 +29,7 @@
#include "input/gamepad.hpp"
#include "utility/fundamental-types.hpp"
#include "debug/logger.hpp"
#include "event/signal.hpp"
// Forward declarations
typedef struct SDL_Window SDL_Window;
@ -149,6 +150,21 @@ public:
void process_events();
bool was_closed() const;
/// Returns a connector for the signal emitted when the window is requested to close.
connector<void()>& get_window_close_signal() noexcept;
/// Returns a connector for the signal emitted each time the window gains or loses focus.
connector<void(bool)>& get_window_focus_signal() noexcept;
/// Returns a connector for the signal emitted each time the window is moved.
connector<void(int, int)>& get_window_motion_signal() noexcept;
/// Returns a connector for the signal emitted each time the window is resized.
connector<void(int, int)>& get_window_size_signal() noexcept;
/// Returns a connector for the signal emitted each time the window viewport is resized.
connector<void(int, int)>& get_viewport_size_signal() noexcept;
private:
void window_resized();
@ -177,6 +193,12 @@ private:
input::mouse* mouse;
std::list<input::gamepad*> gamepads;
std::unordered_map<int, input::gamepad*> gamepad_map;
signal<void()> window_close_signal;
signal<void(bool)> window_focus_signal;
signal<void(int, int)> window_motion_signal;
signal<void(int, int)> window_size_signal;
signal<void(int, int)> viewport_size_signal;
};
inline debug::logger* application::get_logger()
@ -244,4 +266,29 @@ inline bool application::was_closed() const
return closed;
}
inline connector<void()>& application::get_window_close_signal() noexcept
{
return window_close_signal.connector();
}
inline connector<void(bool)>& application::get_window_focus_signal() noexcept
{
return window_focus_signal.connector();
}
inline connector<void(int, int)>& application::get_window_motion_signal() noexcept
{
return window_motion_signal.connector();
}
inline connector<void(int, int)>& application::get_window_size_signal() noexcept
{
return window_size_signal.connector();
}
inline connector<void(int, int)>& application::get_viewport_size_signal() noexcept
{
return viewport_size_signal.connector();
}
#endif // ANTKEEPER_APPLICATION_HPP

+ 47
- 0
src/event/signal.cpp View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code 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 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code 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 Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "event/signal.hpp"
//namespace event {
connection::connection(std::weak_ptr<void> handler, disconnector_type disconnector):
handler(handler),
disconnector(disconnector)
{}
connection::~connection()
{
disconnect();
}
bool connection::connected() const noexcept
{
return !handler.expired();
}
void connection::disconnect()
{
if (connected())
{
disconnector(handler);
}
}
//} // namespace event

+ 214
- 0
src/event/signal.hpp View File

@ -0,0 +1,214 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code 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 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code 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 Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_EVENT_SIGNAL_HPP
#define ANTKEEPER_EVENT_SIGNAL_HPP
#include <algorithm>
#include <execution>
#include <functional>
#include <list>
#include <memory>
//namespace event {
/**
* Manages a connection between a signal and handler. A signal will be disconnected from a handler when the connection is destructed or is disconnected manually via connection::disconnect().
*/
class connection
{
public:
/// Signal handler disconnect function type.
typedef std::function<void(std::weak_ptr<void>)> disconnector_type;
/**
* Constructs a connection between a signal and a handler.
*
* @param handler Weak pointer to a signal handler.
* @param disconnector Signal handler disconnect function.
*/
connection(std::weak_ptr<void> handler, disconnector_type disconnector);
/**
* Destructs a connection between a signal and a handler.
*/
~connection();
/**
* Returns `true` if the signal and handler are connected, `false` otherwise.
*/
bool connected() const noexcept;
/**
* Disconnects the signal from the handler.
*/
void disconnect();
private:
std::weak_ptr<void> handler;
disconnector_type disconnector;
};
template<class T>
class signal;
template<class T>
class connector;
/**
* Creates connections between a signal and signal handlers.
*
* @tparam T Signal handler return type.
* @tparam Args Signal handler argument types.
*/
template <class T, class... Args>
class connector<T(Args...)>
{
public:
/// Signal type.
typedef signal<T(Args...)> signal_type;
/**
* Constructs a signal connector.
*
* @param signal Signal to which handlers may be connected.
*/
connector(signal_type& signal):
signal(&signal)
{}
/// @copydoc signal::connect(handler_type)
std::shared_ptr<connection> connect(typename signal_type::handler_type handler)
{
return signal->connect(handler);
}
private:
signal_type* signal;
};
/**
* Emits signals to connected handlers.
*
* @tparam T Signal handler return type.
* @tparam Args Signal handler argument types.
*/
template <class T, class... Args>
class signal<T(Args...)>
{
public:
/// Signal handler type.
typedef std::function<T(Args...)> handler_type;
/// Signal connector type.
typedef connector<T(Args...)> connector_type;
/**
* Constructs a signal.
*/
signal():
signal_connector(*this)
{}
/**
* Returns the connector for this signal.
*/
connector_type& connector() noexcept
{
return signal_connector;
}
/**
* Connects the signal to a handler.
*
* @param handler Signal handler to connect.
*
* @return Connection between the signal and handler.
*/
std::shared_ptr<connection> connect(handler_type handler)
{
// Allocate shared pointer to handler
std::shared_ptr<handler_type> shared_handler = std::make_shared<handler_type>(handler);
// Add handler to list of connected handlers
handlers.push_back(shared_handler);
// Return a shared pointer to the connection between the signal and handler
return std::make_shared<connection>
(
std::static_pointer_cast<void>(shared_handler),
[this](std::weak_ptr<void> handler)
{
this->handlers.remove(std::static_pointer_cast<handler_type>(handler.lock()));
}
);
}
/**
* Disconnects the signal from all connected handlers.
*/
void disconnect()
{
handlers.clear();
}
/**
* Emits a signal to all connected handlers.
*
* @tparam ExecutionPolicy Execution policy type.
*
* @param policy Execution policy to use.
* @param args Signal arguments.
*/
template <class ExecutionPolicy>
void emit(ExecutionPolicy&& policy, Args... args) const
{
std::for_each
(
policy,
std::begin(handlers),
std::end(handlers),
[&](const auto& handler)
{
(*handler)(args...);
}
);
}
/**
* Emits a signal to all connected handlers.
*
* @param args Signal arguments.
*/
void emit(Args... args) const
{
emit(std::execution::seq, args...);
}
private:
/// Signal connector.
connector_type signal_connector;
/// List of connected signal handlers.
std::list<std::shared_ptr<handler_type>> handlers;
};
//} // namespace event
#endif // ANTKEEPER_EVENT_SIGNAL_HPP

+ 51
- 0
src/game/state/main-menu.cpp View File

@ -43,6 +43,9 @@
#include "game/component/transform.hpp"
#include "math/projection.hpp"
#include <limits>
#include <iostream>
#include "event/signal.hpp"
namespace game {
namespace state {
@ -52,6 +55,54 @@ main_menu::main_menu(game::context& ctx, bool fade_in):
{
ctx.logger->push_task("Entering main menu state");
auto listener1 = [](int x, int y)
{
std::cout << "listener1 received " << x << std::endl;
};
auto listener2 = [](int x, int y)
{
std::cout << "listener2 received " << x << std::endl;
};
auto listener3 = [](int x, int y)
{
std::cout << "listener3 received " << x << std::endl;
};
viewport_size_connection = ctx.app->get_viewport_size_signal().connect
(
[](int w, int h)
{
std::cout << "viewport resized " << w << "x" << h << std::endl;
}
);
window_motion_connection = ctx.app->get_window_motion_signal().connect
(
[](int x, int y)
{
std::cout << "window moved to " << x << ", " << y << std::endl;
}
);
window_focus_connection = ctx.app->get_window_focus_signal().connect
(
[](bool focus)
{
if (focus)
std::cout << "focus gained" << std::endl;
else
std::cout << "focus lost" << std::endl;
}
);
window_close_connection = ctx.app->get_window_close_signal().connect
(
[]()
{
std::cout << "window closed" << std::endl;
}
);
ctx.ui_clear_pass->set_cleared_buffers(true, true, false);
// Construct title text

+ 6
- 0
src/game/state/main-menu.hpp View File

@ -24,6 +24,7 @@
#include "scene/text.hpp"
#include "animation/animation.hpp"
#include "entity/id.hpp"
#include "event/signal.hpp"
namespace game {
namespace state {
@ -42,6 +43,11 @@ private:
animation<float> title_fade_animation;
entity::id swarm_eid;
std::shared_ptr<connection> window_close_connection;
std::shared_ptr<connection> window_motion_connection;
std::shared_ptr<connection> window_focus_connection;
std::shared_ptr<connection> viewport_size_connection;
};
} // namespace state

+ 28
- 0
src/render/stage.cpp View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2023 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code 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 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code 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 Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "render/stage.hpp"
namespace render {
stage::stage():
priority(0)
{}
} // namespace render

+ 27
- 2
src/render/stage.hpp View File

@ -30,8 +30,15 @@ namespace render {
class stage
{
public:
/**
* Constructs a render stage and sets it priority.
*
* @param priority Stage execution order priority.
*/
stage(int priority);
/// Constructs a render stage.
stage() = default;
stage();
/// Destructs a render stage.
virtual ~stage() = default;
@ -41,9 +48,27 @@ public:
*
* @param ctx Render context.
*/
virtual void execute(render::context& ctx) = 0;
virtual void execute(render::context& ctx) const = 0;
/**
* Sets the priority of the stage's execution order in the render pipeline.
*
* @param priority Stage execution order priority. Stages with lower priorities are executed first.
*/
void set_priority(int priority);
/// Returns the priority of the stage's execution order in the render pipeline.
int get_priority() const noexcept;
private:
int priority;
};
inline int stage::get_priority() const noexcept
{
return priority;
}
} // namespace render
#endif // ANTKEEPER_RENDER_STAGE_HPP

+ 1
- 1
src/render/stage/culling-stage.cpp View File

@ -26,7 +26,7 @@
namespace render {
void culling_stage::execute(render::context& ctx)
void culling_stage::execute(render::context& ctx) const
{
// Get list of all objects in the collection
const std::list<scene::object_base*>& objects = *(ctx.collection->get_objects());

+ 1
- 1
src/render/stage/culling-stage.hpp View File

@ -37,7 +37,7 @@ public:
virtual ~culling_stage() = default;
/// @copydoc render::stage::execute(render::context&)
virtual void execute(render::context& ctx) final;
virtual void execute(render::context& ctx) const final;
};
} // namespace render

Loading…
Cancel
Save