Skip to content
Snippets Groups Projects
Window.cpp 6.75 KiB

#include <GLFW/glfw3.h>
#include <thread>
#include <vector>

#include "vkcv/Window.hpp"

namespace vkcv {

	void Window_onMouseButtonEvent(GLFWwindow* callbackWindow, int button, int action, int mods) {
		auto window = static_cast<Window*>(glfwGetWindowUserPointer(callbackWindow));

		if (window != nullptr) {
			window->e_mouseButton(button, action, mods);
		}
	}

	void Window_onMouseMoveEvent(GLFWwindow* callbackWindow, double x, double y) {
		auto window = static_cast<Window*>(glfwGetWindowUserPointer(callbackWindow));

		if (window != nullptr) {
			window->e_mouseMove(x, y);
		}
	}

	void Window_onMouseScrollEvent(GLFWwindow* callbackWindow, double xoffset, double yoffset) {
		auto window = static_cast<Window*>(glfwGetWindowUserPointer(callbackWindow));

		if (window != nullptr) {
			window->e_mouseScroll(xoffset, yoffset);
		}
	}

	void Window_onResize(GLFWwindow* callbackWindow, int width, int height) {
		auto window = static_cast<Window*>(glfwGetWindowUserPointer(callbackWindow));

		if (window != nullptr) {
			window->e_resize(width, height);
		}
	}

	void Window_onKeyEvent(GLFWwindow* callbackWindow, int key, int scancode, int action,
						   int mods) {
		auto window = static_cast<Window*>(glfwGetWindowUserPointer(callbackWindow));

		if (window != nullptr) {
			window->e_key(key, scancode, action, mods);
		}
	}

	void Window_onCharEvent(GLFWwindow* callbackWindow, unsigned int c) {
		auto window = static_cast<Window*>(glfwGetWindowUserPointer(callbackWindow));

		if (window != nullptr) {
			window->e_char(c);
		}
	}

	static std::vector<GLFWwindow*> s_Windows;

	void Window_onGamepadEvent(int gamepadIndex) {
		Window::getFocusedWindow().e_gamepad(gamepadIndex);
	}

	static GLFWwindow* createGLFWWindow(const char* windowTitle, int width, int height,
										bool resizable) {
		if (s_Windows.empty()) {
			glfwInit();
		}
		width = std::max(width, 1);
		height = std::max(height, 1);

		glfwDefaultWindowHints();
		glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
		glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE);

		GLFWwindow* window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr);

		if (window) {
			s_Windows.push_back(window);
		}

		return window;
	}

	static void bindGLFWWindow(GLFWwindow* windowHandle, Window* window) {
		if (!windowHandle) {
			return;
		}

		glfwSetWindowUserPointer(windowHandle, window);

		// combine Callbacks with Events
		glfwSetMouseButtonCallback(windowHandle, Window_onMouseButtonEvent);
		glfwSetCursorPosCallback(windowHandle, Window_onMouseMoveEvent);
		glfwSetWindowSizeCallback(windowHandle, Window_onResize);
		glfwSetKeyCallback(windowHandle, Window_onKeyEvent);
		glfwSetScrollCallback(windowHandle, Window_onMouseScrollEvent);
		glfwSetCharCallback(windowHandle, Window_onCharEvent);
	}

	Window::Window() :
		m_title(), m_resizable(false), m_shouldClose(false), m_window(nullptr), e_mouseButton(true),
		e_mouseMove(true), e_mouseScroll(true), e_resize(true), e_key(true), e_char(true),
		e_gamepad(true) {}

	Window::Window(const std::string &title, int width, int height, bool resizable) :
		m_title(title), m_resizable(resizable), m_shouldClose(false),
		m_window(createGLFWWindow(title.c_str(), width, height, resizable)), e_mouseButton(true),
		e_mouseMove(true), e_mouseScroll(true), e_resize(true), e_key(true), e_char(true),
		e_gamepad(true) {
		bindGLFWWindow(m_window, this);
	}

	Window::~Window() {
		Window::e_mouseButton.unlock();
		Window::e_mouseMove.unlock();
		Window::e_mouseScroll.unlock();
		Window::e_resize.unlock();
		Window::e_key.unlock();
		Window::e_char.unlock();
		Window::e_gamepad.unlock();
		Window::e_resize.remove(m_resizeHandle);
		if (m_window) {
			s_Windows.erase(std::find(s_Windows.begin(), s_Windows.end(), m_window));
			glfwDestroyWindow(m_window);
		}

		if (s_Windows.empty()) {
			glfwTerminate();
		}
	}

	bool Window::hasOpenWindow() {
		for (auto glfwWindow : s_Windows) {
			auto window = static_cast<Window*>(glfwGetWindowUserPointer(glfwWindow));

			if (window->isOpen()) {
				return true;
			}
		}

		return false;
	}

	void Window::pollEvents() {
		for (auto glfwWindow : s_Windows) {
			auto window = static_cast<Window*>(glfwGetWindowUserPointer(glfwWindow));

			window->e_mouseButton.unlock();
			window->e_mouseMove.unlock();
			window->e_mouseScroll.unlock();
			window->e_resize.unlock();
			window->e_key.unlock();
			window->e_char.unlock();
			window->e_gamepad.unlock();
		}

		glfwPollEvents();

		for (int gamepadIndex = GLFW_JOYSTICK_1; gamepadIndex <= GLFW_JOYSTICK_LAST;
			 gamepadIndex++) {
			if (glfwJoystickPresent(gamepadIndex)) {
				Window_onGamepadEvent(gamepadIndex);
			}
		}

		for (auto glfwWindow : s_Windows) {
			auto window = static_cast<Window*>(glfwGetWindowUserPointer(glfwWindow));

			window->e_mouseButton.lock();
			window->e_mouseMove.lock();
			window->e_mouseScroll.lock();
			window->e_resize.lock();
			window->e_key.lock();
			window->e_char.lock();
			window->e_gamepad.lock();

			window->m_shouldClose |= glfwWindowShouldClose(glfwWindow);
		}
	}

	const std::vector<std::string> &Window::getExtensions() {
		static std::vector<std::string> extensions;

		if (extensions.empty()) {
			if (s_Windows.empty()) {
				glfwInit();
			}

			uint32_t glfwExtensionCount = 0;
			const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

			for (uint32_t i = 0; i < glfwExtensionCount; i++) {
				extensions.emplace_back(glfwExtensions [i]);
			}

			if (s_Windows.empty()) {
				glfwTerminate();
			}
		}

		return extensions;
	}

	bool Window::isOpen() const {
		if (!m_window) {
			return false;
		}

		return !m_shouldClose;
	}

	const std::string &Window::getTitle() const {
		return m_title;
	}

	int Window::getWidth() const {
		int width = 0;

		if (m_window) {
			glfwGetWindowSize(m_window, &width, nullptr);
		}

		return std::max(width, 1);
	}

	int Window::getHeight() const {
		int height = 0;

		if (m_window) {
			glfwGetWindowSize(m_window, nullptr, &height);
		}

		return std::max(height, 1);
	}

	bool Window::isResizable() const {
		return m_resizable;
	}

	GLFWwindow* Window::getWindow() const {
		return m_window;
	}

	void Window::getFramebufferSize(int &width, int &height) const {
		if (m_window) {
			glfwGetFramebufferSize(m_window, &width, &height);
		} else {
			width = 0;
			height = 0;
		}
	}

	Window &Window::getFocusedWindow() {
		static Window defaultWindow;

		auto activeWindowIterator =
			std::find_if(s_Windows.begin(), s_Windows.end(), [](GLFWwindow* window) {
				return glfwGetWindowAttrib(window, GLFW_FOCUSED);
			});

		if (activeWindowIterator == s_Windows.end()) {
			return defaultWindow;
		}
		Window &window = *static_cast<Window*>(glfwGetWindowUserPointer(*activeWindowIterator));
		return window;
	}

	SwapchainHandle Window::getSwapchain() const {
		return m_swapchainHandle;
	}
} // namespace vkcv