feat: windows support for surface module
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is failing
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	continuous-integration/drone/push Build is failing
				
			This commit is contained in:
		
							parent
							
								
									9b0acc1cc3
								
							
						
					
					
						commit
						6f007c47ed
					
				
					 2 changed files with 326 additions and 1 deletions
				
			
		|  | @ -1,6 +1,7 @@ | ||||||
| if (NOT WIN32) | if (NOT WIN32) | ||||||
|     add_library_module(surface linux/system.cpp) |     add_library_module(surface linux/system.cpp) | ||||||
| else() | else(WIN32) | ||||||
|  |     add_library_module(surface windows/system.cpp) | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
| target_link_libraries(surface PUBLIC  | target_link_libraries(surface PUBLIC  | ||||||
|  |  | ||||||
							
								
								
									
										324
									
								
								modules/surface/private/windows/system.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										324
									
								
								modules/surface/private/windows/system.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,324 @@ | ||||||
|  | #define GLFW_EXPOSE_NATIVE_X11 | ||||||
|  | #include <GLFW/glfw3.h> | ||||||
|  | #include <GLFW/glfw3native.h> | ||||||
|  | #include <surface/system.hpp> | ||||||
|  | 
 | ||||||
|  | namespace lt::surface { | ||||||
|  | 
 | ||||||
|  | // This class is to ensure glfwInit/glfwTerminate is called only once and exactly when needed during
 | ||||||
|  | // entire application runtime
 | ||||||
|  | class GlfwSingleton | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	[[nodiscard]] static auto get() -> GlfwSingleton & | ||||||
|  | 	{ | ||||||
|  | 		static auto instance = GlfwSingleton {}; | ||||||
|  | 		return instance; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	GlfwSingleton(GlfwSingleton &&) = delete; | ||||||
|  | 
 | ||||||
|  | 	GlfwSingleton(const GlfwSingleton &) = delete; | ||||||
|  | 
 | ||||||
|  | 	auto operator=(GlfwSingleton &&) -> GlfwSingleton & = delete; | ||||||
|  | 
 | ||||||
|  | 	auto operator=(const GlfwSingleton &) -> GlfwSingleton & = delete; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	GlfwSingleton() | ||||||
|  | 	{ | ||||||
|  | 		log_inf("Initializing glfw..."); | ||||||
|  | 		ensure(glfwInit(), "Failed to initialize 'glfw'"); | ||||||
|  | 		log_inf("...Finished"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	~GlfwSingleton() | ||||||
|  | 	{ | ||||||
|  | 		log_inf("Terminating glfw..."); | ||||||
|  | 		glfwTerminate(); | ||||||
|  | 		log_inf("...Finished"); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void glfw_error_callbac(int32_t code, const char *description) | ||||||
|  | { | ||||||
|  | 	log_err("GLFW ERROR: {} -> {}", code, description); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void handle_event(GLFWwindow *window, const SurfaceComponent::Event &event) | ||||||
|  | { | ||||||
|  | 	auto &callbacks = *static_cast<std::vector<SurfaceComponent::EventCallback> *>( | ||||||
|  | 	    glfwGetWindowUserPointer(window) | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	for (auto &callback : callbacks) | ||||||
|  | 	{ | ||||||
|  | 		if (callback(event)) | ||||||
|  | 		{ | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void bind_glfw_events(GLFWwindow *handle) | ||||||
|  | { | ||||||
|  | 	glfwSetWindowPosCallback(handle, [](GLFWwindow *window, int xpos, int ypos) { | ||||||
|  | 		handle_event(window, MovedEvent { xpos, ypos }); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	glfwSetWindowSizeCallback(handle, [](GLFWwindow *window, int width, int height) { | ||||||
|  | 		handle_event( | ||||||
|  | 		    window, | ||||||
|  | 		    ResizedEvent { static_cast<uint32_t>(width), static_cast<uint32_t>(height) } | ||||||
|  | 		); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	glfwSetWindowCloseCallback(handle, [](GLFWwindow *window) { | ||||||
|  | 		handle_event(window, ClosedEvent {}); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	glfwSetWindowFocusCallback(handle, [](GLFWwindow *window, int focus) { | ||||||
|  | 		if (focus == GLFW_TRUE) | ||||||
|  | 		{ | ||||||
|  | 			handle_event(window, GainFocusEvent {}); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			handle_event(window, LostFocusEvent {}); | ||||||
|  | 		} | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	glfwSetCursorPosCallback(handle, [](GLFWwindow *window, double xpos, double ypos) { | ||||||
|  | 		handle_event( | ||||||
|  | 		    window, | ||||||
|  | 		    MouseMovedEvent { static_cast<float>(xpos), static_cast<float>(ypos) } | ||||||
|  | 		); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	glfwSetMouseButtonCallback( | ||||||
|  | 	    handle, | ||||||
|  | 	    [](GLFWwindow *window, int button, int action, int /*mods*/) { | ||||||
|  | 		    if (action == GLFW_PRESS) | ||||||
|  | 		    { | ||||||
|  | 			    handle_event(window, ButtonPressedEvent { button }); | ||||||
|  | 		    } | ||||||
|  | 		    else if (action == GLFW_RELEASE) | ||||||
|  | 		    { | ||||||
|  | 			    handle_event(window, ButtonReleasedEvent { button }); | ||||||
|  | 		    } | ||||||
|  | 	    } | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	glfwSetScrollCallback(handle, [](GLFWwindow *window, double /*xoffset*/, double yoffset) { | ||||||
|  | 		handle_event(window, WheelScrolledEvent { static_cast<float>(yoffset) }); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	glfwSetKeyCallback( | ||||||
|  | 	    handle, | ||||||
|  | 	    [](GLFWwindow *window, int key, int /*scancode*/, int action, int /*mods*/) { | ||||||
|  | 		    if (action == GLFW_PRESS) | ||||||
|  | 		    { | ||||||
|  | 			    handle_event(window, KeyPressedEvent { key }); | ||||||
|  | 		    } | ||||||
|  | 		    else if (action == GLFW_RELEASE) | ||||||
|  | 		    { | ||||||
|  | 			    handle_event(window, KeyReleasedEvent { key }); | ||||||
|  | 		    } | ||||||
|  | 	    } | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	glfwSetCharCallback(handle, [](GLFWwindow *window, unsigned int character) { | ||||||
|  | 		handle_event(window, KeySetCharEvent { character }); | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | System::System(Ref<ecs::Registry> registry): m_registry(std::move(registry)) | ||||||
|  | { | ||||||
|  | 	glfwSetErrorCallback(&glfw_error_callbac); | ||||||
|  | 
 | ||||||
|  | 	// will call `glfwInit()` only the first time
 | ||||||
|  | 	auto &glfw_instance = GlfwSingleton::get(); | ||||||
|  | 	ensure(m_registry, "Failed to initialize surface system: null registry"); | ||||||
|  | 
 | ||||||
|  | 	ensure( | ||||||
|  | 	    m_registry->view<SurfaceComponent>().size() == 0, | ||||||
|  | 	    "Failed to initialize surface system: registry has surface component(s)" | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	m_registry->get_entt_registry() | ||||||
|  | 	    .on_construct<SurfaceComponent>() | ||||||
|  | 	    .connect<&System::on_surface_construct>(this); | ||||||
|  | 
 | ||||||
|  | 	m_registry->get_entt_registry() | ||||||
|  | 	    .on_update<SurfaceComponent>() | ||||||
|  | 	    .connect<&System::on_surface_update>(this); | ||||||
|  | 
 | ||||||
|  | 	m_registry->get_entt_registry() | ||||||
|  | 	    .on_destroy<SurfaceComponent>() | ||||||
|  | 	    .connect<&System::on_surface_destroy>(this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | System::~System() | ||||||
|  | { | ||||||
|  | 	m_registry->get_entt_registry() | ||||||
|  | 	    .on_construct<SurfaceComponent>() | ||||||
|  | 	    .disconnect<&System::on_surface_construct>(this); | ||||||
|  | 
 | ||||||
|  | 	m_registry->get_entt_registry() | ||||||
|  | 	    .on_update<SurfaceComponent>() | ||||||
|  | 	    .connect<&System::on_surface_update>(this); | ||||||
|  | 
 | ||||||
|  | 	m_registry->get_entt_registry() | ||||||
|  | 	    .on_destroy<SurfaceComponent>() | ||||||
|  | 	    .disconnect<&System::on_surface_destroy>(this); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	m_registry->view<SurfaceComponent>().each([&](const entt::entity entity, SurfaceComponent &) { | ||||||
|  | 		m_registry->get_entt_registry().remove<SurfaceComponent>(entity); | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void System::on_surface_construct(entt::registry ®istry, entt::entity entity) | ||||||
|  | { | ||||||
|  | 	try | ||||||
|  | 	{ | ||||||
|  | 		auto &surface = registry.get<SurfaceComponent>(entity); | ||||||
|  | 		ensure_component_sanity(surface); | ||||||
|  | 
 | ||||||
|  | 		glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); | ||||||
|  | 		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); | ||||||
|  | 		glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | ||||||
|  | 
 | ||||||
|  | 		surface.m_glfw_handle = glfwCreateWindow( | ||||||
|  | 		    static_cast<int>(surface.get_resolution().x), | ||||||
|  | 		    static_cast<int>(surface.get_resolution().y), | ||||||
|  | 		    surface.get_title().begin(), | ||||||
|  | 		    nullptr, | ||||||
|  | 		    nullptr | ||||||
|  | 		); | ||||||
|  | 		ensure(surface.m_glfw_handle, "Failed to create 'GLFWwindow'"); | ||||||
|  | 
 | ||||||
|  | 		glfwSetWindowUserPointer(surface.m_glfw_handle, &surface.m_event_callbacks); | ||||||
|  | 		surface.m_native_handle = glfwGetX11Window(surface.m_glfw_handle); | ||||||
|  | 		bind_glfw_events(surface.m_glfw_handle); | ||||||
|  | 	} | ||||||
|  | 	catch (...) | ||||||
|  | 	{ | ||||||
|  | 		registry.remove<SurfaceComponent>(entity); | ||||||
|  | 		throw; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void System::on_surface_update(entt::registry ®istry, entt::entity entity) | ||||||
|  | { | ||||||
|  | 	auto &surface = registry.get<SurfaceComponent>(entity); | ||||||
|  | 	glfwSetWindowUserPointer(surface.m_glfw_handle, &surface.m_event_callbacks); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void System::on_surface_destroy(entt::registry ®istry, entt::entity entity) | ||||||
|  | { | ||||||
|  | 	auto &surface = registry.get<SurfaceComponent>(entity); | ||||||
|  | 
 | ||||||
|  | 	if (surface.m_glfw_handle) | ||||||
|  | 	{ | ||||||
|  | 		glfwDestroyWindow(surface.m_glfw_handle); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void System::set_title(ecs::Entity entity, std::string_view new_title) | ||||||
|  | { | ||||||
|  | 	auto &surface = entity.get_component<SurfaceComponent>(); | ||||||
|  | 
 | ||||||
|  | 	surface.m_title = new_title; | ||||||
|  | 	glfwSetWindowTitle(surface.m_glfw_handle, surface.m_title.c_str()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | auto System::tick() -> bool | ||||||
|  | { | ||||||
|  | 	m_registry->view<SurfaceComponent>().each([](SurfaceComponent &surface) { | ||||||
|  | 		glfwSwapBuffers(surface.m_glfw_handle); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	glfwPollEvents(); | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void System::set_size(ecs::Entity surface_entity, const math::uvec2 &new_size) | ||||||
|  | { | ||||||
|  | 	auto &surface = surface_entity.get_component<SurfaceComponent>(); | ||||||
|  | 	surface.m_resolution = new_size; | ||||||
|  | 
 | ||||||
|  | 	glfwSetWindowSize( | ||||||
|  | 	    surface.m_glfw_handle, | ||||||
|  | 	    static_cast<int>(new_size.x), | ||||||
|  | 	    static_cast<int>(new_size.y) | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void System::set_v_sync(ecs::Entity surface_entity, bool vsync) | ||||||
|  | { | ||||||
|  | 	auto &surface = surface_entity.get_component<SurfaceComponent>(); | ||||||
|  | 	surface.m_vsync = vsync; | ||||||
|  | 
 | ||||||
|  | 	glfwSwapInterval(vsync); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void System::set_visibility(ecs::Entity surface_entity, bool visible) | ||||||
|  | { | ||||||
|  | 	auto &surface = surface_entity.get_component<SurfaceComponent>(); | ||||||
|  | 	surface.m_visible = visible; | ||||||
|  | 
 | ||||||
|  | 	if (visible) | ||||||
|  | 	{ | ||||||
|  | 		glfwShowWindow(surface.m_glfw_handle); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		glfwHideWindow(surface.m_glfw_handle); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void System::add_event_listener( | ||||||
|  |     ecs::Entity surface_entity, | ||||||
|  |     SurfaceComponent::EventCallback callback | ||||||
|  | ) | ||||||
|  | { | ||||||
|  | 	auto &surface = surface_entity.get_component<SurfaceComponent>(); | ||||||
|  | 	surface.m_event_callbacks.emplace_back(std::move(callback)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void System::ensure_component_sanity(const SurfaceComponent &component) | ||||||
|  | { | ||||||
|  | 	auto [width, height] = component.get_resolution(); | ||||||
|  | 
 | ||||||
|  | 	ensure(width != 0u, "Received bad values for surface component: width({}) == 0", width); | ||||||
|  | 
 | ||||||
|  | 	ensure(height != 0u, "Received bad values for surface component: height({}) == 0", height); | ||||||
|  | 
 | ||||||
|  | 	ensure( | ||||||
|  | 	    width < SurfaceComponent::max_dimension, | ||||||
|  | 	    "Received bad values for surface component: width({}) > max_dimension({})", | ||||||
|  | 	    width, | ||||||
|  | 	    SurfaceComponent::max_dimension | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	ensure( | ||||||
|  | 	    height < SurfaceComponent::max_dimension, | ||||||
|  | 	    "Received bad values for surface component: height({}) > max_dimension({})", | ||||||
|  | 	    height, | ||||||
|  | 	    SurfaceComponent::max_dimension | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	ensure( | ||||||
|  | 	    component.get_title().size() < SurfaceComponent::max_title_length, | ||||||
|  | 	    "Received bad values for surface component: title.size({}) > max_title_length({})", | ||||||
|  | 	    component.get_title().size(), | ||||||
|  | 	    SurfaceComponent::max_title_length | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace lt::surface
 | ||||||
|  | 
 | ||||||
|  | namespace lt { | ||||||
|  | 
 | ||||||
|  | } // namespace lt
 | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue