From cf19c5b086294dda6d4350e9f7e9633c05724a0f Mon Sep 17 00:00:00 2001
From: Joseph Walton-Rivers <joseph@walton-rivers.uk>
Date: Mon, 27 Jun 2022 21:24:43 +0100
Subject: [PATCH] new module system replacing the old one

---
 demo/demo/main.cpp                            |  47 ++-
 demo/demo/rollball.cpp                        |   2 +-
 fggl/app.cpp                                  |  18 +-
 fggl/audio/openal/audio.cpp                   |   3 -
 fggl/debug/debug.cpp                          |   3 +-
 fggl/gfx/ogl/renderer.cpp                     |   8 +-
 fggl/gfx/ogl4/canvas.cpp                      |   1 -
 fggl/gfx/window.cpp                           | 323 +++++++++---------
 fggl/gui/fonts.cpp                            |   2 +-
 fggl/scenes/game.cpp                          |   2 +-
 fggl/scenes/menu.cpp                          |   2 +-
 include/fggl/app.hpp                          |  26 +-
 include/fggl/audio/audio.hpp                  |   7 +-
 include/fggl/audio/null_audio.hpp             |  19 +-
 include/fggl/audio/openal/audio.hpp           |   3 +-
 include/fggl/audio/openal/module.hpp          |  52 +++
 include/fggl/data/module.hpp                  |  51 +++
 include/fggl/data/storage.hpp                 |   5 +-
 include/fggl/debug/debug.h                    |   4 +-
 include/fggl/display/glfw/module.hpp          |  57 ++++
 include/fggl/display/glfw/services.hpp        |  52 +++
 include/fggl/{gfx => display/glfw}/window.hpp |  47 ++-
 .../{gfx => display/glfw}/window_input.hpp    |  20 +-
 .../{gfx/windowing.hpp => display/window.hpp} |  53 ++-
 include/fggl/fggl.hpp                         |  10 +
 include/fggl/gfx/compat.hpp                   |  68 ----
 include/fggl/gfx/interfaces.hpp               |  52 +++
 include/fggl/gfx/ogl/backend.hpp              |   2 +-
 include/fggl/gfx/ogl/renderer.hpp             |   3 +-
 include/fggl/gfx/ogl4/canvas.hpp              |   2 +-
 include/fggl/gfx/ogl4/module.hpp              |  57 ++++
 include/fggl/gfx/ogl4/setup.hpp               |  56 +++
 include/fggl/gfx/setup.hpp                    |  48 +++
 include/fggl/gui/fonts.hpp                    |   9 +-
 include/fggl/gui/module.hpp                   |  49 +++
 include/fggl/input/input.hpp                  |   6 +
 include/fggl/input/module.hpp                 |  47 +++
 include/fggl/modules/manager.hpp              | 206 +++++++++++
 include/fggl/modules/module.hpp               |  91 +++++
 include/fggl/subsystem/cursed/god_object.hpp  |   6 -
 40 files changed, 1165 insertions(+), 354 deletions(-)
 create mode 100644 include/fggl/audio/openal/module.hpp
 create mode 100644 include/fggl/data/module.hpp
 create mode 100644 include/fggl/display/glfw/module.hpp
 create mode 100644 include/fggl/display/glfw/services.hpp
 rename include/fggl/{gfx => display/glfw}/window.hpp (73%)
 rename include/fggl/{gfx => display/glfw}/window_input.hpp (73%)
 rename include/fggl/{gfx/windowing.hpp => display/window.hpp} (63%)
 delete mode 100644 include/fggl/gfx/compat.hpp
 create mode 100644 include/fggl/gfx/interfaces.hpp
 create mode 100644 include/fggl/gfx/ogl4/module.hpp
 create mode 100644 include/fggl/gfx/ogl4/setup.hpp
 create mode 100644 include/fggl/gfx/setup.hpp
 create mode 100644 include/fggl/gui/module.hpp
 create mode 100644 include/fggl/input/module.hpp
 create mode 100644 include/fggl/modules/manager.hpp
 create mode 100644 include/fggl/modules/module.hpp

diff --git a/demo/demo/main.cpp b/demo/demo/main.cpp
index 59a6cb0..617b83c 100644
--- a/demo/demo/main.cpp
+++ b/demo/demo/main.cpp
@@ -25,23 +25,18 @@
 	#include "fggl/phys/bullet/bullet.hpp"
 #endif
 
-#include "fggl/subsystem/subsystem.hpp"
+#include "fggl/fggl.hpp"
 
-#include "fggl/app.hpp"
 #include "fggl/audio/openal/audio.hpp"
 
 #include "fggl/gfx/atlas.hpp"
-#include "fggl/gfx/window.hpp"
-#include "fggl/gfx/compat.hpp"
+#include "fggl/display/glfw/window.hpp"
 #include "fggl/gfx/ogl/compat.hpp"
 
-#include "fggl/data/storage.hpp"
-#include "fggl/util/service.hpp"
 #include "fggl/platform/paths.hpp"
 
-#include "fggl/ecs3/types.hpp"
-
 #include "fggl/scenes/menu.hpp"
+#include "fggl/modules/manager.hpp"
 
 #include "GameScene.h"
 #include "rollball.hpp"
@@ -77,29 +72,29 @@ static void test_atlas_api() {
 }
 
 int main(int argc, const char* argv[]) {
-	auto pathConfig = fggl::platform::calc_engine_paths("fggl-demo");
 
-	fggl::subsystem::SubsystemManager manager;
-	manager.provide<fggl::data::Storage>(new fggl::data::Storage(pathConfig));
-	manager.provide<fggl::audio::AudioService>(new fggl::audio::openAL());
+	// DAG initialisation test
+	fggl::modules::Manager moduleManager;
+	moduleManager.use<fggl::input::Generic>();
+	moduleManager.use<fggl::data::LocalStorage>();
+	moduleManager.use<fggl::audio::NullAudio>();
+	moduleManager.use<fggl::gui::FreeType>();
 
-	manager.setTypeRegistry(new fggl::ecs3::TypeRegistry());
-	manager.setFontLibrary(new fggl::gui::FontLibrary());
-	manager.setInput(new fggl::input::Input());
+	moduleManager.use<fggl::gfx::OpenGL4>();
+	moduleManager.use<fggl::display::GLFW>();
 
-	manager.setup();
+	moduleManager.resolve();
 
 	// create the application
-	fggl::App app( &manager, "fggl-demo" );
+	fggl::App app( &moduleManager, "fggl-demo" );
 
-	// Would be nice to not take args like this, it messes with lifetimes
-	auto& windowing = app.use<fggl::gfx::ecsGlfwModule>( manager.getInput() );
+	auto* windowing = app.service<fggl::display::WindowService>();
 
-    // -- should not be our problem - this is a broken api
-    auto window = windowing.createWindow("Demo Game");
-	window->make_graphics<fggl::gfx::OpenGL4>(manager.getStorage(), manager.getFontLibrary());
-    window->fullscreen( true );
-    app.setWindow( std::move(window) );
+	// make a window for our application
+	auto* window = windowing->create();
+	window->setTitle( "fggl-demo" );
+	window->setFullscreen( true );
+	app.setWindow(window);
 
 	// load a bunch of modules to provide game functionality
 	//app.use<fggl::ecs3::ecsTypes>();
@@ -108,13 +103,11 @@ int main(int argc, const char* argv[]) {
 		app.use<FGGL_MODULE_BULLET>();
 	#endif
 
-	test_atlas_api();
-
 	// Add a basic main menu
     auto *menu = app.add_state<fggl::scenes::BasicMenu>("menu");
 
 	// add some menu items for the game states
-	auto* audio = manager.getAudio();
+	auto* audio = app.service<fggl::audio::AudioService>();
     menu->add("terrain", [&app, &audio]() {
 		audio->play("click.ogg", false);
 		app.change_state("game");
diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp
index 8271336..24f188c 100644
--- a/demo/demo/rollball.cpp
+++ b/demo/demo/rollball.cpp
@@ -146,7 +146,7 @@ namespace demo {
 		Game::activate();
 
 		Prefabs prefabs{};
-		setup_prefabs(m_owner.services()->getStorage(), world(), prefabs);
+		setup_prefabs(m_owner.service<fggl::data::Storage>(), world(), prefabs);
 
 		// collectable callbacks
 		auto* collectableCallbacks = world().get<fggl::phys::CollisionCallbacks>(prefabs.collectable);
diff --git a/fggl/app.cpp b/fggl/app.cpp
index db9fbd3..63f078f 100644
--- a/fggl/app.cpp
+++ b/fggl/app.cpp
@@ -24,10 +24,10 @@
 
 namespace fggl {
 
-    App::App(subsystem::ServiceProvider* services, const Identifer& name) : App::App( services, name, name ) {
+    App::App(modules::Manager* services, const Identifer& name) : App::App( services, name, name ) {
     }
 
-    App::App(subsystem::ServiceProvider* services, const Identifer& name, const Identifer& folder ) :
+    App::App(modules::Manager* services, const Identifer& name, const Identifer& folder ) :
         m_running(true),
         m_types(std::make_unique<ecs3::TypeRegistry>()),
         m_modules(std::make_unique<ecs3::ModuleManager>(*m_types)),
@@ -36,6 +36,7 @@ namespace fggl {
 		m_subsystems(services){}
 
     int App::run(int argc, const char** argv) {
+		auto* windowing = m_subsystems->get<display::WindowService>();
 
         {
             // activate the first state
@@ -53,14 +54,19 @@ namespace fggl {
 				}
 			}
 
-            auto& state = m_states.active();
+			// process window events
+			if ( windowing != nullptr) {
+				windowing->pollEvents();
+			}
             m_modules->onUpdate();
-            state.update();
+
+			auto& state = m_states.active();
+			state.update();
 
             // window rendering to frame buffer
             if ( m_window != nullptr ) {
-                m_modules->onFrameStart();
-                m_window->frameStart();
+				m_modules->onFrameStart();
+				m_window->frameStart();
 
                 // get draw instructions
 				auto& graphics = m_window->graphics();
diff --git a/fggl/audio/openal/audio.cpp b/fggl/audio/openal/audio.cpp
index d79ae61..2f7295b 100644
--- a/fggl/audio/openal/audio.cpp
+++ b/fggl/audio/openal/audio.cpp
@@ -54,7 +54,4 @@ namespace fggl::audio::openal {
 		}
 	}
 
-	void AudioServiceOAL::setup(data::Storage* storage) {
-		m_storage = storage;
-	}
 }
\ No newline at end of file
diff --git a/fggl/debug/debug.cpp b/fggl/debug/debug.cpp
index 0a33632..629e7fd 100644
--- a/fggl/debug/debug.cpp
+++ b/fggl/debug/debug.cpp
@@ -18,10 +18,9 @@
 #include "imgui/include/imgui_impl_glfw.h"
 #include "imgui/include/imgui_impl_opengl3.h"
 
-using fggl::gfx::Window;
 using fggl::debug::DebugUI;
 
-DebugUI::DebugUI(std::shared_ptr<fggl::gfx::GlfwWindow>& win) : m_visible(false) {
+DebugUI::DebugUI(std::shared_ptr<fggl::display::glfw::Window>& win) : m_visible(false) {
 	IMGUI_CHECKVERSION();
 	ImGui::CreateContext();
 
diff --git a/fggl/gfx/ogl/renderer.cpp b/fggl/gfx/ogl/renderer.cpp
index 564a754..45a42aa 100644
--- a/fggl/gfx/ogl/renderer.cpp
+++ b/fggl/gfx/ogl/renderer.cpp
@@ -17,7 +17,7 @@
 #include "fggl/debug/pragmas.hpp"
 
 #include "fggl/gfx/ogl/common.hpp"
-#include "fggl/gfx/window.hpp"
+#include "fggl/display/glfw/window.hpp"
 
 #include <fggl/gfx/camera.hpp>
 #include <fggl/gfx/ogl/renderer.hpp>
@@ -135,7 +135,7 @@ namespace fggl::gfx {
 	using data::Mesh2D;
 	using data::Vertex2D;
 
-	OpenGL4Backend::OpenGL4Backend(const Window &owner, data::Storage* storage, gui::FontLibrary* fonts) : fggl::gfx::Graphics(), m_canvasPipeline(INVALID_SHADER_ID), m_storage(storage) {
+	OpenGL4Backend::OpenGL4Backend(data::Storage* storage, gui::FontLibrary* fonts) : fggl::gfx::Graphics(), m_canvasPipeline(INVALID_SHADER_ID), m_storage(storage) {
 		// initialise GLEW, or fail
 		// FIXME this binds the graphics stack to GLFW :'(
 		int version = gladLoadGLLoader( (GLADloadproc)glfwGetProcAddress );
@@ -155,8 +155,8 @@ namespace fggl::gfx {
 		}
 
 		// setup the viewport based on the window's framebuffer
-		auto fbSize = owner.frameSize();
-		glViewport(0, 0, fbSize.x, fbSize.y);
+		//auto fbSize = owner.frameSize();
+		//glViewport(0, 0, fbSize.x, fbSize.y);
 
 		// setup the shader cache
 		m_cache = std::make_unique<ShaderCache>(m_storage);
diff --git a/fggl/gfx/ogl4/canvas.cpp b/fggl/gfx/ogl4/canvas.cpp
index 59f1bab..fc0a38f 100644
--- a/fggl/gfx/ogl4/canvas.cpp
+++ b/fggl/gfx/ogl4/canvas.cpp
@@ -23,7 +23,6 @@
 #include "fggl/util/service.hpp"
 
 #include "fggl/gui/fonts.hpp"
-#include "fggl/gfx/windowing.hpp"
 
 #define FGGL_OPENGL_CORRECTNESS
 
diff --git a/fggl/gfx/window.cpp b/fggl/gfx/window.cpp
index 1a7a117..0420c77 100644
--- a/fggl/gfx/window.cpp
+++ b/fggl/gfx/window.cpp
@@ -13,210 +13,217 @@
  */
 
 #include <utility>
-#include <fggl/gfx/window.hpp>
-#include <fggl/gfx/window_input.hpp>
 
-#include <iostream>
+#include "fggl/display/glfw/window.hpp"
+#include "fggl/display/glfw/window_input.hpp"
+#include "fggl/debug/logging.hpp"
+
 #include <string>
 #include <stdexcept>
-#include <utility>
 #include <GLFW/glfw3.h>
 #include <spdlog/spdlog.h>
 
-using namespace fggl::gfx;
-
+namespace fggl::display::glfw {
 
-static void glfw_error(int code, const char* description) {
-    spdlog::warn("[GLFW error] {}: {}", code, description);
-}
+	static void glfw_error(int code, const char *description) {
+		debug::log(debug::Level::error, "[GLFW] {}: {}", code, description);
+	}
 
-static void framebuffer_resize(GLFWwindow* window, int width, int height) {
-	auto *fgglWindow = static_cast<GlfwWindow*>(glfwGetWindowUserPointer( window ));
-	fgglWindow->framesize( width, height );
-}
+	static void framebuffer_resize(GLFWwindow *window, int width, int height) {
+		auto *fgglWindow = static_cast<Window *>(glfwGetWindowUserPointer(window));
+		fgglWindow->framesize(width, height);
+	}
 
-static void fggl_input_cursor(GLFWwindow* window, double x, double y) {
-	auto& input = GlfwInputManager::instance();
-	auto *fgglWin = static_cast<GlfwWindow*>(glfwGetWindowUserPointer(window));
+	static void fggl_input_cursor(GLFWwindow *window, double x, double y) {
+		auto &input = GlfwInputManager::instance();
+		auto *fgglWin = static_cast<Window *>(glfwGetWindowUserPointer(window));
 
-	#ifndef FGGL_INPUT_SCREEN_COORDS
+		#ifndef FGGL_INPUT_SCREEN_COORDS
 		// convert to nice ranges...
 		x = (x / fgglWin->width() * 2) - 1.0; // [-1, 1]
 		y = (y / fgglWin->height() * 2) - 1.0; // [-1, 1]
-	#endif
-
-	// inform the input system
-	input.onMouseMove(x, y);
-}
-
-static void fggl_input_scroll(GLFWwindow* window, double x, double y) {
-	auto& input = GlfwInputManager::instance();
-	input.onMouseScroll(x, y);
-}
-
-static void fggl_input_mouse_btn(GLFWwindow* window, int btn, int action, int mods) {
-	auto& input = GlfwInputManager::instance();
-	input.onMouseButton(btn, action == GLFW_PRESS);
-}
-
-static void fggl_input_keyboard(GLFWwindow* window, int key, int scancode, int action, int mods) {
-	auto& input = GlfwInputManager::instance();
-	input.onKeyEvent( scancode, action == GLFW_PRESS || action == GLFW_REPEAT);
-}
-
-static void fggl_update_joystick(fggl::input::GamepadInput& input, int jid) {
-	bool isGamepad = glfwJoystickIsGamepad(jid);
-
-	if ( isGamepad ) {
-		if( !input.present(jid) ) {
-			std::string name = glfwGetJoystickName(jid);
-			input.setActive(jid, true);
-			input.name(jid, name);
-		}
+		#endif
+
+		// inform the input system
+		input.onMouseMove(x, y);
+	}
+
+	static void fggl_input_scroll(GLFWwindow *window, double x, double y) {
+		auto &input = GlfwInputManager::instance();
+		input.onMouseScroll(x, y);
+	}
 
-		GLFWgamepadstate state;
-		glfwGetGamepadState(jid, &state);
+	static void fggl_input_mouse_btn(GLFWwindow *window, int btn, int action, int mods) {
+		auto &input = GlfwInputManager::instance();
+		input.onMouseButton(btn, action == GLFW_PRESS);
+	}
+
+	static void fggl_input_keyboard(GLFWwindow *window, int key, int scancode, int action, int mods) {
+		auto &input = GlfwInputManager::instance();
+		input.onKeyEvent(scancode, action == GLFW_PRESS || action == GLFW_REPEAT);
+	}
+
+	static void fggl_update_joystick(fggl::input::GamepadInput &input, int jid) {
+		bool isGamepad = glfwJoystickIsGamepad(jid);
+
+		if (isGamepad) {
+			if (!input.present(jid)) {
+				std::string name = glfwGetJoystickName(jid);
+				input.setActive(jid, true);
+				input.name(jid, name);
+			}
 
-		fggl::input::GamepadState gamepadState;
-		for (int i=0; i<=GLFW_GAMEPAD_BUTTON_LAST; i++) {
-			gamepadState.buttons[i] = state.buttons[i];
+			GLFWgamepadstate state;
+			glfwGetGamepadState(jid, &state);
+
+			fggl::input::GamepadState gamepadState;
+			for (int i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) {
+				gamepadState.buttons[i] = state.buttons[i];
+			}
+
+			for (int i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) {
+				gamepadState.axes[i] = state.axes[i];
+			}
+
+			input.update(jid, gamepadState);
+		} else {
+			input.setActive(jid, false);
 		}
+	}
 
-		for (int i=0; i<=GLFW_GAMEPAD_AXIS_LAST; i++) {
-			gamepadState.axes[i] = state.axes[i];
+	static void fggl_joystick_poll() {
+		auto &input = GlfwInputManager::instance();
+		if (!input.alive()) {
+			return;
 		}
 
-		input.update(jid, gamepadState);
-	} else {
-		input.setActive(jid, false);
+		auto &gamepadCtl = input.gamepads();
+		for (int jid = 0; jid < GLFW_JOYSTICK_LAST; jid++) {
+			if (glfwJoystickPresent(jid)) {
+				fggl_update_joystick(gamepadCtl, jid);
+			} else {
+				gamepadCtl.setActive(jid, false);
+			}
+		}
 	}
-}
 
-static void fggl_joystick_poll() {
-	auto& input = GlfwInputManager::instance();
-	if ( !input.alive() ) {
-		return;
-	}
+	static void fggl_joystick(int jid, int state) {
+		auto &input = GlfwInputManager::instance();
+		if (!input.alive()) {
+			return;
+		}
 
-	auto& gamepadCtl = input.gamepads();
-	for (int jid=0; jid < GLFW_JOYSTICK_LAST; jid++) {
-		if ( glfwJoystickPresent(jid) ) {
+		auto &gamepadCtl = input.gamepads();
+		if (state == GLFW_CONNECTED) {
 			fggl_update_joystick(gamepadCtl, jid);
 		} else {
 			gamepadCtl.setActive(jid, false);
 		}
 	}
-}
-
-static void fggl_joystick(int jid, int state) {
-	auto& input = GlfwInputManager::instance();
-	if ( !input.alive() ) {
-		return;
-	}
 
-	auto& gamepadCtl = input.gamepads();
-	if ( state == GLFW_CONNECTED ) {
-		fggl_update_joystick( gamepadCtl, jid );
-	} else {
-		gamepadCtl.setActive(jid, false);
-	}
-}
+	GlfwContext::GlfwContext(fggl::input::Input *input) {
+		debug::log(debug::Level::info, "[GLFW] Initializing...");
+		auto &glfwCallbacks = GlfwInputManager::instance();
+		glfwCallbacks.setup(input);
 
-GlfwContext::GlfwContext( fggl::input::Input* input ) {
-    spdlog::debug("[glfw] context creation stated");
-	auto& glfwCallbacks = GlfwInputManager::instance();
-	glfwCallbacks.setup( input );
+		glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE);
+		glfwSetErrorCallback(glfw_error);
 
-	glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE);
-    glfwSetErrorCallback(glfw_error);
+		int state = glfwInit();
+		if (state == GLFW_FALSE) {
+			const char **error = nullptr;
+			glfwGetError(error);
+			throw std::runtime_error(*error);
+		}
 
-	int state = glfwInit();
-	if ( state == GLFW_FALSE ) {
-		const char **error = nullptr;
-		glfwGetError(error);
-		throw std::runtime_error( *error );
+		// joysticks are global
+		fggl_joystick_poll();
+		glfwSetJoystickCallback(fggl_joystick);
+		debug::log(debug::Level::info, "[GLFW] Initialization complete.");
 	}
 
-	// joysticks are global
-	fggl_joystick_poll();
-	glfwSetJoystickCallback(fggl_joystick);
-    spdlog::debug("[glfw] context creation complete");
-}
-
-GlfwContext::~GlfwContext() {
-	glfwTerminate();
-    spdlog::debug("[glfw] context terminated");
-}
+	GlfwContext::~GlfwContext() {
+		glfwTerminate();
+		spdlog::debug("[glfw] context terminated");
+	}
 
-void GlfwContext::pollEvents() {
-	auto& glfwCallbacks = GlfwInputManager::instance();
-    glfwCallbacks.frame();
+	void GlfwContext::pollEvents() {
+		auto &glfwCallbacks = GlfwInputManager::instance();
+		glfwCallbacks.frame();
 
-	glfwPollEvents();
-	fggl_joystick_poll();
-}
+		glfwPollEvents();
+		fggl_joystick_poll();
+	}
 
-GlfwWindow::GlfwWindow(std::shared_ptr<GlfwContext> context) : m_context(std::move(context)), m_window(nullptr), m_framesize() {
-    spdlog::debug("[glfw] creating window");
+	Window::Window(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics* graphics)
+		: m_context(std::move(context)), m_window(nullptr), m_framesize() {
+		spdlog::debug("[glfw] creating window");
+
+		// FIXME - this ties the graphics API before window creation
+		auto graphicsConfig = graphics->config();
+		if (graphicsConfig.api == gfx::GraphicsAPI::OpenGL) {
+			glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, graphicsConfig.majorVersion);
+			glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, graphicsConfig.minorVersion);
+			glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+			glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, graphicsConfig.debug ? GLFW_TRUE : GLFW_FALSE);
+		}
 
-	// FIXME - this ties the graphics API before window creation
-	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
-	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
-	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
-	glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
+		m_window = glfwCreateWindow(1920, 1080, "main", nullptr, nullptr);
+		if (m_window == nullptr) {
+			return;
+		}
 
-	m_window = glfwCreateWindow(1920, 1080, "main", nullptr, nullptr);
-	if ( m_window == nullptr ) {
-		return;
-	}
+		glfwSetWindowUserPointer(m_window, this);
 
-	glfwSetWindowUserPointer(m_window, this);
+		// setup callback wrappers (will invoke the methods via the user ptr above)
+		glfwSetFramebufferSizeCallback(m_window, framebuffer_resize);
+		glfwSetScrollCallback(m_window, fggl_input_scroll);
+		glfwSetCursorPosCallback(m_window, fggl_input_cursor);
+		glfwSetMouseButtonCallback(m_window, fggl_input_mouse_btn);
+		glfwSetKeyCallback(m_window, fggl_input_keyboard);
 
-	// setup callback wrappers (will invoke the methods via the user ptr above)
-	glfwSetFramebufferSizeCallback( m_window, framebuffer_resize );
-	glfwSetScrollCallback(m_window, fggl_input_scroll);
-	glfwSetCursorPosCallback(m_window, fggl_input_cursor);
-	glfwSetMouseButtonCallback(m_window, fggl_input_mouse_btn);
-	glfwSetKeyCallback(m_window, fggl_input_keyboard);
+		// bind the graphics API
+		glfwMakeContextCurrent(m_window);
+		m_graphics = std::unique_ptr<gfx::Graphics>(graphics->create(*this));
 
-    spdlog::debug("[glfw] window creation complete");
-}
+		spdlog::debug("[glfw] window creation complete");
+	}
 
-GlfwWindow::~GlfwWindow() {
-	if ( m_window != nullptr ) {
-		// prevent dangling pointers
-		glfwSetWindowUserPointer(m_window, nullptr);
-		glfwDestroyWindow(m_window);
-		m_window = nullptr;
+	Window::~Window() {
+		if (m_window != nullptr) {
+			// prevent dangling pointers
+			glfwSetWindowUserPointer(m_window, nullptr);
+			glfwDestroyWindow(m_window);
+			m_window = nullptr;
+		}
 	}
-}
 
-void GlfwWindow::frameStart() {
-    assert( m_window != nullptr );
-    assert( m_graphics != nullptr );
+	void Window::frameStart() {
+		assert(m_window != nullptr);
+		assert(m_graphics != nullptr);
 
-    glfwMakeContextCurrent(m_window);
-    m_graphics->clear();
-}
+		glfwMakeContextCurrent(m_window);
+		m_graphics->clear();
+	}
 
-void GlfwWindow::frameEnd() {
-	glfwSwapBuffers(m_window);
-}
+	void Window::frameEnd() {
+		glfwSwapBuffers(m_window);
+	}
 
-void GlfwWindow::activate() const {
-	assert( m_window != nullptr );
-	glfwMakeContextCurrent(m_window);
-}
+	void Window::activate() const {
+		assert(m_window != nullptr);
+		glfwMakeContextCurrent(m_window);
+	}
 
-fggl::math::vec2i GlfwWindow::frameSize() const {
-  assert( m_window != nullptr );
-  math::vec2i size;
-  glfwGetFramebufferSize(m_window, &size.x, &size.y);
-  return size;
-}
-
-bool GlfwWindow::wantClose() const {
-	assert( m_window != nullptr );
-	return glfwWindowShouldClose(m_window) == GLFW_TRUE;
-}
+	fggl::math::vec2i Window::frameSize() const {
+		assert(m_window != nullptr);
+		math::vec2i size;
+		glfwGetFramebufferSize(m_window, &size.x, &size.y);
+		return size;
+	}
 
+	bool Window::wantClose() const {
+		assert(m_window != nullptr);
+		return glfwWindowShouldClose(m_window) == GLFW_TRUE;
+	}
+}
\ No newline at end of file
diff --git a/fggl/gui/fonts.cpp b/fggl/gui/fonts.cpp
index 4e7ba38..0efb616 100644
--- a/fggl/gui/fonts.cpp
+++ b/fggl/gui/fonts.cpp
@@ -23,7 +23,7 @@ namespace fggl::gui {
 		FT_Done_Face(m_face);
 	}
 
-	FontLibrary::FontLibrary() {
+	FontLibrary::FontLibrary(data::Storage* storage) : m_context(nullptr), m_storage(storage) {
 		FT_Init_FreeType(&m_context);
 	}
 
diff --git a/fggl/scenes/game.cpp b/fggl/scenes/game.cpp
index 8f749c9..e90819b 100644
--- a/fggl/scenes/game.cpp
+++ b/fggl/scenes/game.cpp
@@ -26,7 +26,7 @@
 namespace fggl::scenes {
 
 	Game::Game(fggl::App &app) : AppState(app) {
-		m_input = app.services()->getInput();
+		m_input = app.service<input::Input>();
 	}
 
 	void Game::activate() {
diff --git a/fggl/scenes/menu.cpp b/fggl/scenes/menu.cpp
index 36e2ec7..d5071ec 100644
--- a/fggl/scenes/menu.cpp
+++ b/fggl/scenes/menu.cpp
@@ -26,7 +26,7 @@ namespace fggl::scenes {
 
 
     BasicMenu::BasicMenu(fggl::App& app) : AppState(app), m_inputs(nullptr), m_active(), m_hover(nullptr) {
-        m_inputs = app.services()->getInput();
+        m_inputs = app.service<input::Input>();
     }
 
     void BasicMenu::update() {
diff --git a/include/fggl/app.hpp b/include/fggl/app.hpp
index 5a63c0a..62175b2 100644
--- a/include/fggl/app.hpp
+++ b/include/fggl/app.hpp
@@ -24,10 +24,10 @@
 
 #include <fggl/ecs3/types.hpp>
 #include "fggl/ecs3/module/module.hpp"
-#include <fggl/gfx/window.hpp>
+#include "fggl/display/glfw/window.hpp"
 #include <fggl/gfx/paint.hpp>
 #include <fggl/util/states.hpp>
-#include "fggl/subsystem/provider.hpp"
+#include "fggl/modules/manager.hpp"
 
 namespace fggl {
 
@@ -83,8 +83,8 @@ namespace fggl {
 
 	class App {
 		public:
-			explicit App(subsystem::ServiceProvider* serivces, const Identifer &name);
-			App(subsystem::ServiceProvider* services, const Identifer &name, const Identifer &folderName);
+			explicit App(modules::Manager* serivces, const Identifer &name);
+			App(modules::Manager* services, const Identifer &name, const Identifer &folderName);
 
 			// class is non copy-able
 			App(const App &app) = delete;
@@ -93,8 +93,8 @@ namespace fggl {
 			App &operator=(const App& other) = delete;
 			App &operator=(App&& other) = delete;
 
-			inline void setWindow(std::unique_ptr<gfx::Window>&& window) {
-				m_window = std::move(window);
+			inline void setWindow(display::Window* window) {
+				m_window = window;
 			}
 
 			/**
@@ -125,8 +125,14 @@ namespace fggl {
 				return m_states.active();
 			}
 
-			inline subsystem::ServiceProvider* services() {
-				return m_subsystems;
+			template<typename T>
+			inline T* service() {
+				try {
+					return m_subsystems->template get<T>();
+				} catch (std::out_of_range &e) {
+					debug::log(debug::Level::error, "Service not found: {}", T::service);
+					return nullptr;
+				}
 			}
 
 			inline ecs3::TypeRegistry* registry() {
@@ -145,10 +151,10 @@ namespace fggl {
 			bool m_running;
 			std::unique_ptr<ecs3::TypeRegistry> m_types;
 			std::unique_ptr<ecs3::ModuleManager> m_modules;
-			std::unique_ptr<gfx::Window> m_window;
+			display::Window* m_window;
 			AppMachine m_states;
 			Identifer m_expectedScene;
-			subsystem::ServiceProvider* m_subsystems;
+			modules::Manager* m_subsystems;
 	};
 
 }
diff --git a/include/fggl/audio/audio.hpp b/include/fggl/audio/audio.hpp
index e1d1651..ccb3029 100644
--- a/include/fggl/audio/audio.hpp
+++ b/include/fggl/audio/audio.hpp
@@ -18,6 +18,7 @@
 #include <string>
 #include "fggl/subsystem/types.hpp"
 #include "fggl/data/storage.hpp"
+#include "fggl/modules/module.hpp"
 
 namespace fggl::audio {
 
@@ -33,13 +34,15 @@ namespace fggl::audio {
 		short* data;
 	};
 
+	constexpr modules::ModuleService SERVICE_AUDIO_PLAYBACK = modules::make_service("fggl::audio::AudioService");
+
 	class AudioService {
 		public:
+			constexpr static const modules::ModuleService service = SERVICE_AUDIO_PLAYBACK;
+
 			AudioService() = default;
 			virtual ~AudioService() = default;
 
-			virtual void setup(data::Storage* provider) = 0;
-
 			virtual void play(const std::string& filename, bool looping = false) = 0;
 			virtual void play(AudioClip& clip, bool looping = false) = 0;
 	};
diff --git a/include/fggl/audio/null_audio.hpp b/include/fggl/audio/null_audio.hpp
index ccefb33..e89f99a 100644
--- a/include/fggl/audio/null_audio.hpp
+++ b/include/fggl/audio/null_audio.hpp
@@ -28,11 +28,28 @@ namespace fggl::audio {
 			NullAudioService() = default;
 			virtual ~NullAudioService() = default;
 
-			void setup(data::Storage* storage) override {}
 			void play(const std::string& filename, bool looping = false) override {}
 			void play(AudioClip& clip, bool looping = false) override {}
 	};
 
+	struct NullAudio {
+		constexpr static const char* name = "fggl::audio::NULL";
+		constexpr static const std::array<modules::ModuleService, 1> provides = {
+			SERVICE_AUDIO_PLAYBACK
+		};
+		constexpr static const std::array<modules::ModuleService, 0> depends = {};
+		static const modules::ServiceFactory factory;
+	};
+
+	bool null_factory(modules::ModuleService service, modules::Services& services) {
+		if (service == SERVICE_AUDIO_PLAYBACK) {
+			services.bind<audio::AudioService, audio::NullAudioService>();
+			return true;
+		}
+		return false;
+	}
+	const modules::ServiceFactory NullAudio::factory = null_factory;
+
 } // namespace fggl::audio
 
 #endif //FGGL_AUDIO_NULL_AUDIO_HPP
diff --git a/include/fggl/audio/openal/audio.hpp b/include/fggl/audio/openal/audio.hpp
index 88e59d0..4834c97 100644
--- a/include/fggl/audio/openal/audio.hpp
+++ b/include/fggl/audio/openal/audio.hpp
@@ -154,7 +154,7 @@ namespace fggl::audio::openal {
 
 	class AudioServiceOAL : public AudioService {
 		public:
-			AudioServiceOAL() : m_device(alcOpenDevice(nullptr)) {
+			explicit AudioServiceOAL(data::Storage* storage) : m_device(alcOpenDevice(nullptr)), m_storage(storage) {
 				if ( m_device != nullptr ) {
 					m_context = alcCreateContext(m_device, nullptr);
 					alcMakeContextCurrent(m_context);
@@ -172,7 +172,6 @@ namespace fggl::audio::openal {
 				}
 			}
 
-			void setup(data::Storage* storage) override;
 			void play(const std::string& filename, bool looping = false) override;
 			void play(AudioClip& clip, bool looping = false) override;
 
diff --git a/include/fggl/audio/openal/module.hpp b/include/fggl/audio/openal/module.hpp
new file mode 100644
index 0000000..dd37f67
--- /dev/null
+++ b/include/fggl/audio/openal/module.hpp
@@ -0,0 +1,52 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_AUDIO_OPENAL_MODULE_HPP
+#define FGGL_AUDIO_OPENAL_MODULE_HPP
+
+#include <array>
+#include <string>
+#include "fggl/audio/audio.hpp"
+#include "fggl/audio/openal/audio.hpp"
+
+namespace fggl::audio {
+
+	struct OpenAL {
+		constexpr static const char* name = "fggl::audio::OpenAL";
+		constexpr static const std::array<modules::ModuleService, 1> provides = {
+			SERVICE_AUDIO_PLAYBACK
+		};
+		constexpr static const std::array<modules::ModuleService, 1> depends = {
+			modules::make_service("fggl::data::Storage")
+		};
+		static const modules::ServiceFactory factory;
+	};
+
+	bool openal_factory(modules::ModuleService service, modules::Services& services) {
+		if (service == SERVICE_AUDIO_PLAYBACK) {
+			auto storage = services.get<data::Storage>();
+			services.bind<audio::AudioService, openal::AudioServiceOAL>(storage);
+			return true;
+		}
+		return false;
+	}
+	const modules::ServiceFactory OpenAL::factory = openal_factory;
+
+} // namespace fggl::audio
+
+#endif //FGGL_AUDIO_OPENAL_MODULE_HPP
diff --git a/include/fggl/data/module.hpp b/include/fggl/data/module.hpp
new file mode 100644
index 0000000..6628f7f
--- /dev/null
+++ b/include/fggl/data/module.hpp
@@ -0,0 +1,51 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_DATA_MODULE_HPP
+#define FGGL_DATA_MODULE_HPP
+
+#include "fggl/modules/module.hpp"
+#include "fggl/data/storage.hpp"
+#include "fggl/platform/paths.hpp"
+
+namespace fggl::data {
+
+
+	struct LocalStorage {
+		constexpr static const char* name = "fggl::data::Storage";
+		constexpr static const std::array<modules::ModuleService, 1> provides = {
+			SERVICE_STORAGE
+		};
+		constexpr static const std::array<modules::ModuleService, 0> depends = {};
+		static const modules::ServiceFactory factory;
+	};
+
+	bool storage_factory(modules::ModuleService service, modules::Services& data) {
+		if (service == SERVICE_STORAGE) {
+			// FIXME: no easy way to set the application name
+			auto pathConfig = fggl::platform::calc_engine_paths("fggl-demo");
+			data.create<Storage>(pathConfig);
+			return true;
+		}
+		return false;
+	}
+	const modules::ServiceFactory LocalStorage::factory = storage_factory;
+
+} // namespace fggl::data
+
+#endif //FGGL_DATA_MODULE_HPP
diff --git a/include/fggl/data/storage.hpp b/include/fggl/data/storage.hpp
index f52dad1..2704ec0 100644
--- a/include/fggl/data/storage.hpp
+++ b/include/fggl/data/storage.hpp
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include "fggl/debug/logging.hpp"
+#include "fggl/modules/module.hpp"
 #include "fggl/platform/paths.hpp"
 
 namespace fggl::data {
@@ -34,9 +35,11 @@ namespace fggl::data {
 
 	enum StorageType { Data, Config, Cache };
 
-	class Storage {
+	constexpr const modules::ModuleService SERVICE_STORAGE = modules::make_service("fggl::data::Storage");
 
+	class Storage {
 		public:
+			constexpr static modules::ModuleService service = SERVICE_STORAGE;
 			Storage( fggl::platform::EnginePaths paths ) : m_paths(std::move( paths )) {}
 
 			template<typename T>
diff --git a/include/fggl/debug/debug.h b/include/fggl/debug/debug.h
index a2ff447..e1d9cb6 100644
--- a/include/fggl/debug/debug.h
+++ b/include/fggl/debug/debug.h
@@ -21,7 +21,7 @@
 #include <functional>
 #include <unordered_map>
 
-#include "fggl/gfx/window.hpp"
+#include "fggl/display/glfw/window.hpp"
 
 namespace fggl::debug {
 
@@ -34,7 +34,7 @@ namespace fggl::debug {
 
 	class DebugUI {
 		public:
-			explicit DebugUI(std::shared_ptr<gfx::GlfwWindow> &window);
+			explicit DebugUI(std::shared_ptr<display::glfw::Window> &window);
 			~DebugUI();
 
 			void frameStart();
diff --git a/include/fggl/display/glfw/module.hpp b/include/fggl/display/glfw/module.hpp
new file mode 100644
index 0000000..e3df059
--- /dev/null
+++ b/include/fggl/display/glfw/module.hpp
@@ -0,0 +1,57 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_DISPLAY_GLFW_MODULE_HPP
+#define FGGL_DISPLAY_GLFW_MODULE_HPP
+
+#include "fggl/display/window.hpp"
+#include "fggl/input/module.hpp"
+
+#include "fggl/display/glfw/window.hpp"
+#include "fggl/display/glfw/services.hpp"
+
+namespace fggl::display {
+
+	struct GLFW {
+		constexpr static const char* name = "fggl::display::glfw";
+		constexpr static const std::array<modules::ModuleService, 1> provides = {
+			WindowService::service
+		};
+		constexpr static const std::array<modules::ModuleService, 2> depends = {
+			fggl::input::Input::service,
+			fggl::gfx::WindowGraphics::service
+		};
+		static const modules::ServiceFactory factory;
+	};
+
+	bool glfw_factory(modules::ModuleService service, modules::Services& services) {
+		if (service == WindowService::service) {
+			auto input = services.get<input::Input>();
+			auto graphics = services.get<gfx::WindowGraphics>();
+
+			auto context = std::make_shared<glfw::GlfwContext>(input);
+			services.bind<WindowService, glfw::WindowService>(context, graphics);
+			return true;
+		}
+		return false;
+	}
+	const modules::ServiceFactory GLFW::factory = glfw_factory;
+
+} // namespace fggl::display
+
+#endif //FGGL_DISPLAY_GLFW_MODULE_HPP
diff --git a/include/fggl/display/glfw/services.hpp b/include/fggl/display/glfw/services.hpp
new file mode 100644
index 0000000..6661f03
--- /dev/null
+++ b/include/fggl/display/glfw/services.hpp
@@ -0,0 +1,52 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_DISPLAY_GLFW_SERVICES_HPP
+#define FGGL_DISPLAY_GLFW_SERVICES_HPP
+
+#include "fggl/display/window.hpp"
+#include "fggl/gfx/setup.hpp"
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace fggl::display::glfw {
+
+	class WindowService : public display::WindowService {
+		public:
+			explicit WindowService(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics* gfx) : m_context(std::move(context)), m_gfx(gfx), m_windows() {}
+			virtual ~WindowService() = default;
+
+			display::Window* create() override {
+				m_windows.push_back(std::make_unique<Window>(m_context, m_gfx));
+				return m_windows.back().get();
+			}
+
+			void pollEvents() override {
+				m_context->pollEvents();
+			}
+
+		private:
+			std::shared_ptr<GlfwContext> m_context;
+			gfx::WindowGraphics* m_gfx;
+			std::vector<std::unique_ptr<Window>> m_windows;
+	};
+
+} // namespace fggl::display
+
+#endif //FGGL_DISPLAY_GLFW_SERVICES_HPP
diff --git a/include/fggl/gfx/window.hpp b/include/fggl/display/glfw/window.hpp
similarity index 73%
rename from include/fggl/gfx/window.hpp
rename to include/fggl/display/glfw/window.hpp
index a51a456..d570298 100644
--- a/include/fggl/gfx/window.hpp
+++ b/include/fggl/display/glfw/window.hpp
@@ -12,6 +12,20 @@
  * If not, see <https://www.gnu.org/licenses/>.
  */
 
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
 #ifndef FGGL_GFX_WINDOW_HPP
 #define FGGL_GFX_WINDOW_HPP
 
@@ -19,14 +33,15 @@
 #include <string>
 #include <memory>
 
-#include <fggl/input/input.hpp>
-#include <fggl/gfx/common.hpp>
-#include <fggl/math/types.hpp>
-#include <fggl/gfx/windowing.hpp>
+#include "fggl/input/input.hpp"
+#include "fggl/gfx/common.hpp"
+#include "fggl/math/types.hpp"
+#include "fggl/display/window.hpp"
+#include "fggl/gfx/setup.hpp"
 
-namespace fggl::gfx {
+namespace fggl::display::glfw {
 
-	class GlfwWindow;
+	class Window;
 
 	class GlfwContext {
 		public:
@@ -36,7 +51,7 @@ namespace fggl::gfx {
 			void pollEvents();
 
 		private:
-			std::vector<std::unique_ptr<GlfwWindow>> m_windows;
+			std::vector<std::unique_ptr<Window>> m_windows;
 	};
 
 	enum MutWindowHint {
@@ -60,13 +75,13 @@ namespace fggl::gfx {
 		NoError = GLFW_CONTEXT_NO_ERROR
 	};
 
-	class GlfwWindow : public Window {
+	class Window : public display::Window {
 		public:
-			GlfwWindow(std::shared_ptr<GlfwContext> context);
-			~GlfwWindow();
+			explicit Window(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics*);
+			~Window() override;
 
-			GlfwWindow(Window &) = delete;
-			GlfwWindow(Window &&) = delete;
+			Window(Window &) = delete;
+			Window(Window &&) = delete;
 
 			[[nodiscard]]
 			math::vec2i frameSize() const override;
@@ -102,18 +117,18 @@ namespace fggl::gfx {
 				return glfwWindowShouldClose(m_window);
 			}
 
-			inline void title(const std::string &title) {
+			inline void setTitle(const char* title) override {
 				assert(m_window != nullptr);
-				glfwSetWindowTitle(m_window, title.c_str());
+				glfwSetWindowTitle(m_window, title);
 			}
 
 			[[nodiscard]]
-			inline bool fullscreen() const {
+			inline bool isFullscreen() const override {
 				assert(m_window != nullptr);
 				return glfwGetWindowMonitor(m_window) != nullptr;
 			}
 
-			inline void fullscreen(bool state) {
+			inline void setFullscreen(bool state) override {
 				assert(m_window != nullptr);
 				if (state) {
 					auto monitor = glfwGetPrimaryMonitor();
diff --git a/include/fggl/gfx/window_input.hpp b/include/fggl/display/glfw/window_input.hpp
similarity index 73%
rename from include/fggl/gfx/window_input.hpp
rename to include/fggl/display/glfw/window_input.hpp
index ca74bec..6fb7efb 100644
--- a/include/fggl/gfx/window_input.hpp
+++ b/include/fggl/display/glfw/window_input.hpp
@@ -12,16 +12,30 @@
  * If not, see <https://www.gnu.org/licenses/>.
  */
 
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
 #ifndef FGGL_GFX_WINDOW_INPUT_HPP
 #define FGGL_GFX_WINDOW_INPUT_HPP
 
 #include <memory>
 #include <string>
 
-#include <fggl/gfx/window.hpp>
-#include <fggl/input/input.hpp>
+#include "fggl/display/glfw/window.hpp"
+#include "fggl/input/input.hpp"
 
-namespace fggl::gfx {
+namespace fggl::display::glfw {
 
 	class GlfwInputManager {
 		public:
diff --git a/include/fggl/gfx/windowing.hpp b/include/fggl/display/window.hpp
similarity index 63%
rename from include/fggl/gfx/windowing.hpp
rename to include/fggl/display/window.hpp
index 250edbf..f044a54 100644
--- a/include/fggl/gfx/windowing.hpp
+++ b/include/fggl/display/window.hpp
@@ -12,33 +12,18 @@
  * If not, see <https://www.gnu.org/licenses/>.
  */
 
-#ifndef FGGL_GFX_WINDOWING_HPP
-#define FGGL_GFX_WINDOWING_HPP
+//
+// Created by webpigeon on 27/06/22.
+//
 
-#include <memory>
-#include <fggl/gfx/paint.hpp>
-#include "fggl/ecs3/ecs.hpp"
+#ifndef FGGL_DISPLAY_WINDOW_HPP
+#define FGGL_DISPLAY_WINDOW_HPP
 
-namespace fggl::gfx {
+#include "fggl/modules/module.hpp"
+#include "fggl/math/types.hpp"
+#include "fggl/gfx/interfaces.hpp"
 
-	struct Bounds {
-		float top;
-		float right;
-		float bottom;
-		float left;
-	};
-
-	class Graphics {
-		public:
-			virtual ~Graphics() = default;
-			virtual void clear() = 0;
-			virtual void resize(int width, int height) = 0;
-
-			virtual Bounds canvasBounds() = 0;
-			virtual void draw2D(const Paint &paint) = 0;
-
-			virtual void drawScene(ecs3::World&) = 0;
-	};
+namespace fggl::display {
 
 	class Window {
 		public:
@@ -62,19 +47,29 @@ namespace fggl::gfx {
 			virtual void frameStart() = 0;
 			virtual void frameEnd() = 0;
 
-			Graphics &graphics() {
+			gfx::Graphics &graphics() {
 				return *m_graphics;
 			}
 
+			virtual void setTitle(const char* title) = 0;
+
+			virtual void setFullscreen(bool state) = 0;
+
+			[[nodiscard]]
+			virtual bool isFullscreen() const = 0;
+
 		protected:
-			std::unique_ptr<Graphics> m_graphics;
+			std::unique_ptr<gfx::Graphics> m_graphics;
 	};
 
 	class WindowService {
 		public:
-			Window &create();
+			constexpr static const modules::ModuleService service = modules::make_service("fggl::display::WindowService");
+
+			virtual Window* create() = 0;
+			virtual void pollEvents() = 0;
 	};
 
-} // namespace fggl::gfx
+} // namespace fggl::display
 
-#endif
+#endif //FGGL_DISPLAY_WINDOW_HPP
diff --git a/include/fggl/fggl.hpp b/include/fggl/fggl.hpp
index 4b034f7..decee35 100644
--- a/include/fggl/fggl.hpp
+++ b/include/fggl/fggl.hpp
@@ -18,6 +18,16 @@
 #include "fggl/app.hpp"
 #include "fggl/util/service.hpp"
 
+#include "fggl/data/module.hpp"
+#include "fggl/input/module.hpp"
+#include "fggl/gui/module.hpp"
+
+#include "fggl/display/glfw/module.hpp"
+#include "fggl/audio/null_audio.hpp"
+#include "fggl/audio/openal/module.hpp"
+
+#include "fggl/gfx/ogl4/module.hpp"
+
 namespace fggl {
 
 }
diff --git a/include/fggl/gfx/compat.hpp b/include/fggl/gfx/compat.hpp
deleted file mode 100644
index 0e860e1..0000000
--- a/include/fggl/gfx/compat.hpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-#ifndef FGGL_GFX_COMPAT_HPP
-#define FGGL_GFX_COMPAT_HPP
-/**
- * Window management Calls.
- *
- * This shouldn't be exposed to the demo app, but the ECS we're using isn't smart enouph to allow us to
- * abstract this yet. It's next thing on the list, but this branch is about cleaning up OpenGL not about
- * extending our ECS.
- *
- * Should be removed when the engine has suitable abstractions in place.
- */
-
-#include <memory>
-
-#include <utility>
-#include <fggl/gfx/window.hpp>
-#include <fggl/ecs3/ecs.hpp>
-
-namespace fggl::gfx {
-
-	//
-	// fake module support - allows us to still RAII
-	//
-	struct ecsGlfwModule : ecs3::Module {
-
-		inline explicit
-		ecsGlfwModule(fggl::input::Input* inputs) : context( std::make_shared<GlfwContext>(inputs)) {
-		}
-
-		inline
-		std::unique_ptr<GlfwWindow> createWindow(const std::string &title) {
-			std::unique_ptr<GlfwWindow> window = std::make_unique<GlfwWindow>(context);
-			window->title(title);
-			return std::move(window);
-		}
-
-		void onUpdate() override {
-			context->pollEvents();
-		}
-
-		[[nodiscard]]
-		std::string name() const override {
-			return "gfx::glfw";
-		}
-
-		private:
-			std::shared_ptr<GlfwContext> context;
-
-	};
-
-	using Glfw = ecsGlfwModule;
-}
-
-#endif
diff --git a/include/fggl/gfx/interfaces.hpp b/include/fggl/gfx/interfaces.hpp
new file mode 100644
index 0000000..f546ab2
--- /dev/null
+++ b/include/fggl/gfx/interfaces.hpp
@@ -0,0 +1,52 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_GFX_INTERFACES_HPP
+#define FGGL_GFX_INTERFACES_HPP
+
+#include "fggl/gfx/paint.hpp"
+#include "fggl/ecs3/ecs.hpp"
+#include "fggl/modules/module.hpp"
+
+namespace fggl::gfx {
+
+	struct Bounds {
+		float top;
+		float right;
+		float bottom;
+		float left;
+	};
+
+	class Graphics {
+		public:
+			constexpr static const modules::ModuleService service = modules::make_service("fggl::gfx::Graphics");
+
+			virtual ~Graphics() = default;
+			virtual void clear() = 0;
+			virtual void resize(int width, int height) = 0;
+
+			virtual Bounds canvasBounds() = 0;
+			virtual void draw2D(const Paint &paint) = 0;
+
+			virtual void drawScene(ecs3::World&) = 0;
+	};
+
+
+} // namespace fggl::gfx
+
+#endif //FGGL_GFX_INTERFACES_HPP
diff --git a/include/fggl/gfx/ogl/backend.hpp b/include/fggl/gfx/ogl/backend.hpp
index ae2f177..5116f0d 100644
--- a/include/fggl/gfx/ogl/backend.hpp
+++ b/include/fggl/gfx/ogl/backend.hpp
@@ -16,7 +16,7 @@
 #define FGGL_GFX_OGL_BACKEND_HPP
 
 #include "common.hpp"
-#include <fggl/gfx/window.hpp>
+#include "fggl/display/glfw/window.hpp"
 
 #include <stdexcept>
 
diff --git a/include/fggl/gfx/ogl/renderer.hpp b/include/fggl/gfx/ogl/renderer.hpp
index 4838655..acda731 100644
--- a/include/fggl/gfx/ogl/renderer.hpp
+++ b/include/fggl/gfx/ogl/renderer.hpp
@@ -67,7 +67,7 @@ namespace fggl::gfx {
 	 */
 	class OpenGL4Backend : public Graphics {
 		public:
-			explicit OpenGL4Backend(const Window &owner, data::Storage* storage, gui::FontLibrary* fonts);
+			explicit OpenGL4Backend(data::Storage* storage, gui::FontLibrary* fonts);
 			~OpenGL4Backend() override = default;
 
 			// copy bad
@@ -124,7 +124,6 @@ namespace fggl::gfx {
 			gui::FontLibrary* m_fontLibrary;
 	};
 
-	using OpenGL4 = OpenGL4Backend;
 };  // namespace fggl::gfx
 
 #endif
diff --git a/include/fggl/gfx/ogl4/canvas.hpp b/include/fggl/gfx/ogl4/canvas.hpp
index b060e68..74a9c47 100644
--- a/include/fggl/gfx/ogl4/canvas.hpp
+++ b/include/fggl/gfx/ogl4/canvas.hpp
@@ -21,7 +21,7 @@
 
 #include "fggl/gfx/paint.hpp"
 #include "fggl/gfx/ogl/types.hpp"
-#include "fggl/gfx/windowing.hpp"
+#include "fggl/gfx/interfaces.hpp"
 
 #include "fggl/gui/fonts.hpp"
 
diff --git a/include/fggl/gfx/ogl4/module.hpp b/include/fggl/gfx/ogl4/module.hpp
new file mode 100644
index 0000000..6dc9be3
--- /dev/null
+++ b/include/fggl/gfx/ogl4/module.hpp
@@ -0,0 +1,57 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_GFX_OGL4_MODULE_HPP
+#define FGGL_GFX_OGL4_MODULE_HPP
+
+#include <array>
+#include "fggl/modules/module.hpp"
+#include "fggl/gfx/interfaces.hpp"
+#include "fggl/gfx/setup.hpp"
+
+#include "fggl/gfx/ogl/renderer.hpp"
+#include "fggl/gfx/ogl4/setup.hpp"
+
+namespace fggl::gfx {
+
+	struct OpenGL4 {
+		constexpr static const char* name = "fggl::gfx::OpenGL4";
+		constexpr static const std::array<modules::ModuleService, 1> provides = {
+			WindowGraphics::service
+		};
+		constexpr static const std::array<modules::ModuleService, 2> depends = {
+			data::Storage::service,
+			gui::FontLibrary::service
+		};
+		static const modules::ServiceFactory factory;
+	};
+
+	bool ogl4_factory(modules::ModuleService service, modules::Services& services) {
+		if (service == WindowGraphics::service) {
+			auto storage = services.get<data::Storage>();
+			auto font_library = services.get<gui::FontLibrary>();
+			services.bind<WindowGraphics, ogl4::WindowGraphics>(storage, font_library);
+			return true;
+		}
+		return false;
+	}
+	const modules::ServiceFactory OpenGL4::factory = ogl4_factory;
+
+} //namespace fggl::gfx
+
+#endif //FGGL_GFX_OGL4_MODULE_HPP
diff --git a/include/fggl/gfx/ogl4/setup.hpp b/include/fggl/gfx/ogl4/setup.hpp
new file mode 100644
index 0000000..2da109c
--- /dev/null
+++ b/include/fggl/gfx/ogl4/setup.hpp
@@ -0,0 +1,56 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_GFX_OGL4_SETUP_HPP
+#define FGGL_GFX_OGL4_SETUP_HPP
+
+#include "fggl/gfx/setup.hpp"
+#include "fggl/gfx/ogl/renderer.hpp"
+
+namespace fggl::gfx::ogl4 {
+
+	constexpr GraphicsDetails openGL4Details {
+		GraphicsAPI::OpenGL,
+		4,
+		3,
+		false
+	};
+
+	class WindowGraphics : public gfx::WindowGraphics {
+		public:
+			WindowGraphics(data::Storage* storage, gui::FontLibrary* fonts) : m_storage(storage), m_fonts(fonts) {};
+			virtual ~WindowGraphics() = default;
+
+			fggl::gfx::Graphics* create(display::Window& window) override;
+
+			[[nodiscard]]
+			inline GraphicsDetails config() const override {
+				return openGL4Details;
+			}
+		private:
+			data::Storage* m_storage;
+			gui::FontLibrary* m_fonts;
+	};
+
+	fggl::gfx::Graphics *WindowGraphics::create(display::Window &window) {
+		return new OpenGL4Backend(m_storage, m_fonts);
+	}
+
+} // namespace fggl::gfx::ogl4
+
+#endif //FGGL_GFX_OGL4_SETUP_HPP
diff --git a/include/fggl/gfx/setup.hpp b/include/fggl/gfx/setup.hpp
new file mode 100644
index 0000000..6a58005
--- /dev/null
+++ b/include/fggl/gfx/setup.hpp
@@ -0,0 +1,48 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_GFX_INTERFACES2_HPP
+#define FGGL_GFX_INTERFACES2_HPP
+
+#include "fggl/gfx/interfaces.hpp"
+#include "fggl/display/window.hpp"
+
+namespace fggl::gfx {
+
+	enum class GraphicsAPI {
+		OpenGL, Vulkan
+	};
+
+	struct GraphicsDetails {
+		GraphicsAPI api;
+		int majorVersion;
+		int minorVersion;
+		bool debug;
+	};
+
+	class WindowGraphics {
+		public:
+			constexpr const static modules::ModuleService service = modules::make_service("fggl::gfx::WindowGraphics");
+
+			virtual GraphicsDetails config() const = 0;
+			virtual Graphics* create(display::Window& window) = 0;
+	};
+
+} // namespace fggl::gfx
+
+#endif //FGGL_GFX_INTERFACES2_HPP
diff --git a/include/fggl/gui/fonts.hpp b/include/fggl/gui/fonts.hpp
index 7a36a60..a944cb5 100644
--- a/include/fggl/gui/fonts.hpp
+++ b/include/fggl/gui/fonts.hpp
@@ -26,6 +26,7 @@
 #include "fggl/math/types.hpp"
 #include "fggl/data/storage.hpp"
 #include "fggl/util/service.hpp"
+#include "fggl/modules/module.hpp"
 
 #include <ft2build.h>
 #include FT_FREETYPE_H
@@ -68,7 +69,9 @@ namespace fggl::gui {
 
 	class FontLibrary {
 		public:
-			FontLibrary();
+			constexpr static const modules::ModuleService service = modules::make_service("fggl::gui::font");
+
+			FontLibrary(data::Storage* storage);
 			~FontLibrary();
 
 			// copy and moving not needed
@@ -77,10 +80,6 @@ namespace fggl::gui {
 			FontLibrary& operator=(const FontLibrary&) = delete;
 			FontLibrary& operator=(FontLibrary&&) = delete;
 
-			void setup(data::Storage* storage) {
-				m_storage = storage;
-			}
-
 			inline std::shared_ptr<FontFace> getFont(const std::string& name) {
 				auto fontItr = m_cache.find(name);
 				if ( fontItr != m_cache.end() ) {
diff --git a/include/fggl/gui/module.hpp b/include/fggl/gui/module.hpp
new file mode 100644
index 0000000..6c926f2
--- /dev/null
+++ b/include/fggl/gui/module.hpp
@@ -0,0 +1,49 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_GUI_MODULE_HPP
+#define FGGL_GUI_MODULE_HPP
+
+#include "fggl/gui/fonts.hpp"
+#include "fggl/data/module.hpp"
+
+namespace fggl::gui {
+
+	struct FreeType {
+		constexpr static const char* name = "fggl::gui::FreeType";
+		constexpr static const std::array<modules::ModuleService, 1> provides = {
+			FontLibrary::service
+		};
+		constexpr static const std::array<modules::ModuleService, 1> depends = {
+			data::Storage::service
+		};
+		static const modules::ServiceFactory factory;
+	};
+
+	bool freetype_factory(modules::ModuleService service, modules::Services& services) {
+		if ( service == FontLibrary::service ) {
+			auto storage = services.get<data::Storage>();
+			services.create< FontLibrary >(storage);
+			return true;
+		}
+		return false;
+	}
+	const modules::ServiceFactory FreeType::factory = freetype_factory;
+}
+
+#endif //FGGL_GUI_MODULE_HPP
diff --git a/include/fggl/input/input.hpp b/include/fggl/input/input.hpp
index f73a0dc..4464994 100644
--- a/include/fggl/input/input.hpp
+++ b/include/fggl/input/input.hpp
@@ -20,10 +20,16 @@
 #include <fggl/input/mouse.hpp>
 #include <fggl/input/gamepad.hpp>
 
+#include "fggl/modules/module.hpp"
+
 namespace fggl::input {
 
+	constexpr const modules::ModuleService SERVICE_INPUT = modules::make_service("fggl::input::Input");
+
 	class Input {
 		public:
+			constexpr static const modules::ModuleService service = SERVICE_INPUT;
+
 			Input() = default;
 			void frame(float dt);
 
diff --git a/include/fggl/input/module.hpp b/include/fggl/input/module.hpp
new file mode 100644
index 0000000..125259f
--- /dev/null
+++ b/include/fggl/input/module.hpp
@@ -0,0 +1,47 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_INPUT_MODULE_HPP
+#define FGGL_INPUT_MODULE_HPP
+
+#include <array>
+#include "fggl/modules/module.hpp"
+#include "fggl/input/input.hpp"
+
+namespace fggl::input {
+
+	struct Generic {
+		constexpr static const char* name = "fggl::input::Generic";
+		constexpr static const std::array<modules::ModuleService, 1> provides = {
+			SERVICE_INPUT
+		};
+		constexpr static const std::array<modules::ModuleService, 0> depends = {};
+		static const modules::ServiceFactory factory;
+	};
+
+	bool input_factory(modules::ModuleService service, modules::Services& services) {
+		if (service == SERVICE_INPUT) {
+			services.create<input::Input>();
+			return true;
+		}
+		return false;
+	}
+	const modules::ServiceFactory Generic::factory = input_factory;
+}
+
+#endif //FGGL_INPUT_MODULE_HPP
diff --git a/include/fggl/modules/manager.hpp b/include/fggl/modules/manager.hpp
new file mode 100644
index 0000000..1348fdd
--- /dev/null
+++ b/include/fggl/modules/manager.hpp
@@ -0,0 +1,206 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_MODULES_MANAGER_HPP
+#define FGGL_MODULES_MANAGER_HPP
+
+#include "fggl/modules/module.hpp"
+#include "fggl/debug/logging.hpp"
+
+#include <queue>
+#include <vector>
+#include <map>
+#include <stack>
+#include <set>
+#include <iostream>
+
+namespace fggl::modules {
+
+	template<typename T>
+	class DependencyGraph {
+		public:
+			DependencyGraph() = default;
+
+			void clear() {
+				m_dependencies.clear();
+			}
+
+			void addAll(const T& name, const std::vector<T>& dependencies) {
+				auto existing = m_dependencies.find(name);
+				if (existing == m_dependencies.end()) {
+					m_dependencies[name] = dependencies;
+				} else {
+					existing->second.insert(existing->second.end(), dependencies.begin(), dependencies.end());
+				}
+			}
+
+			void add(const T& name, const T& depends) {
+				m_dependencies[name].push_back(depends);
+			}
+
+			bool getOrder(std::stack<T>& stack) {
+				std::set<T> visited{};
+
+				for (const auto& module : m_dependencies) {
+					if (!visited.contains(module.first)) {
+						sortUtil( module.first, visited, stack);
+					}
+				}
+
+				return true;
+			}
+
+			bool getOrderRev(std::queue<T>& stack) {
+				std::set<T> visited{};
+
+				for (const auto& module : m_dependencies) {
+					if (!visited.contains(module.first)) {
+						sortUtilRev( module.first, visited, stack);
+					}
+				}
+
+				return true;
+			}
+		private:
+			std::map<T, std::vector<T>> m_dependencies;
+
+			void sortUtil(T idx, std::set<T>& visited, std::stack<T>& stack) {
+				visited.emplace(idx);
+
+				for ( auto dep : m_dependencies.at(idx)) {
+					if ( !visited.contains(dep) )
+						sortUtil(dep, visited, stack);
+				}
+
+				stack.push(idx);
+			}
+
+			void sortUtilRev(T idx, std::set<T>& visited, std::queue<T>& stack) {
+				visited.emplace(idx);
+
+				for ( auto dep : m_dependencies.at(idx)) {
+					if ( !visited.contains(dep) )
+						sortUtilRev(dep, visited, stack);
+				}
+
+				stack.push(idx);
+			}
+	};
+
+
+	class Manager {
+		public:
+			Manager() = default;
+
+			inline void addVirtual(const Config& config) {
+				assert( !m_locked );
+
+				m_modules[config.name] = config;
+				for ( const auto& service : config.provides ) {
+					m_serviceProviders[service] = config.name;
+				}
+			}
+
+			template<typename T>
+			void use() {
+				assert( !m_locked );
+
+				Config config { .name = T::name };
+				for ( auto service : T::provides ) {
+					config.provides.push_back(service);
+				}
+
+				for ( auto service : T::depends ) {
+					config.depends.push_back(service);
+				}
+				config.factory = T::factory;
+
+				addVirtual(config);
+			}
+
+			bool buildGraph() {
+				// resolve links between modules
+				for (auto& moduleItr : m_modules) {
+					if ( moduleItr.second.depends.empty() ) {
+						m_dependencies.addAll(moduleItr.first, {});
+						continue;
+					}
+
+					for (auto& service : moduleItr.second.depends) {
+
+						auto provider = m_serviceProviders.find(service);
+						if ( provider == m_serviceProviders.end() ) {
+							debug::log(debug::Level::warning, "{} depends on {}, but nothing provides it", moduleItr.first, service);
+							// nothing can provide the service requested, setup is invalid.
+							return false;
+						}
+
+						m_dependencies.add(moduleItr.first, provider->second);
+					}
+				}
+				return true;
+			}
+
+			template<typename T>
+			T* get() const {
+				assert( m_locked );
+				return m_services.template get<T>();
+			}
+
+			void resolve() {
+				if ( !buildGraph() ) {
+					return;
+				}
+
+				std::queue<ModuleIdentifier> stack;
+				m_dependencies.getOrderRev(stack);
+
+				while ( !stack.empty() ) {
+					auto nextToInit = stack.front();
+					debug::log(debug::Level::info, "Initializing {}", nextToInit);
+
+					auto& module = m_modules.at(nextToInit);
+					if ( module.factory != nullptr ) {
+						for (auto& service : module.provides) {
+							bool result = module.factory(service, m_services);
+							if ( !result ) {
+								debug::log(debug::Level::warning, "{} could not create service {}", nextToInit, service);
+							}
+						}
+					} else {
+						debug::log(debug::Level::warning, "{} has no factory - skipping", nextToInit);
+					}
+					stack.pop();
+				}
+
+				debug::log(debug::Level::info, "engine boot complete");
+				m_locked = true;
+			}
+
+		private:
+			bool m_locked = false;
+			Services m_services;
+			std::map<ModuleIdentifier, Config> m_modules;
+			DependencyGraph<ModuleIdentifier> m_dependencies;
+			std::map<ModuleService, ModuleIdentifier> m_serviceProviders;
+
+	};
+
+} // namespace fggl::modules
+
+#endif //FGGL_MODULES_MANAGER_HPP
diff --git a/include/fggl/modules/module.hpp b/include/fggl/modules/module.hpp
new file mode 100644
index 0000000..d6c2e75
--- /dev/null
+++ b/include/fggl/modules/module.hpp
@@ -0,0 +1,91 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_MODULES_MODULE_HPP
+#define FGGL_MODULES_MODULE_HPP
+
+#include <string>
+#include <vector>
+#include <functional>
+#include <map>
+#include <memory>
+
+namespace fggl::modules {
+
+	using ModuleIdentifier = std::string;
+
+	struct ModuleService {
+		private:
+			std::string_view value;
+
+		public:
+			constexpr ModuleService(std::string_view value) : value(value) {}
+			constexpr explicit operator std::string_view() const { return value; }
+
+			bool operator==(const ModuleService& other) const {
+				return value == other.value;
+			}
+
+			std::strong_ordering operator<=>(const ModuleService& other) const noexcept {
+				return value <=> other.value;
+			}
+	};
+
+	constexpr ModuleService make_service(const std::string_view name) {
+		return {name};
+	}
+
+	class Services {
+		public:
+			template<typename Svc, typename Impl, typename ...Args>
+			void bind(Args... args) {
+				static_assert( std::is_base_of_v<Svc, Impl>, "Service type must be assignable from implementation type" );
+				m_services[Svc::service] = std::make_shared<Impl>(args...);
+			}
+
+			template<typename Svc, typename ...Args>
+			void create(Args... args){
+				m_services[Svc::service] = std::make_shared<Svc>(args...);
+			}
+
+			template<typename Svc>
+			void provide(std::shared_ptr<Svc> service) {
+				m_services[Svc::service] = service;
+			}
+
+			template<typename S>
+			S* get() const {
+				auto serviceWrapper = m_services.at(S::service);
+				auto ptr = std::static_pointer_cast<S>(serviceWrapper);
+				return ptr.get();
+			}
+		private:
+			std::map<ModuleService, std::shared_ptr<void>> m_services;
+	};
+
+	using ServiceFactory = std::function<bool(ModuleService, Services&)>;
+	struct Config {
+		ModuleIdentifier name;
+		std::vector<ModuleService> provides;
+		std::vector<ModuleService> depends;
+		ServiceFactory factory = nullptr;
+	};
+
+} // namespace fggl::modules
+
+#endif //FGGL_MODULES_MODULE_HPP
diff --git a/include/fggl/subsystem/cursed/god_object.hpp b/include/fggl/subsystem/cursed/god_object.hpp
index 01e7a2a..100fa01 100644
--- a/include/fggl/subsystem/cursed/god_object.hpp
+++ b/include/fggl/subsystem/cursed/god_object.hpp
@@ -66,12 +66,6 @@ namespace fggl::subsystem {
 
 			inline void setup() {
 				m_setup = true;
-				ServiceContainer<audio::AudioService>::getPtr()->setup(
-					ServiceContainer<data::Storage>::getPtr()
-				);
-				m_fonts->setup(
-					ServiceContainer<data::Storage>::getPtr()
-				);
 			}
 			inline void teardown() {}
 
-- 
GitLab