303 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			303 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
 | |
|  */
 | |
| 
 | |
| #include <glad/glad.h>
 | |
| #include <GLFW/glfw3.h>
 | |
| #include <imgui_impl_glfw.h>
 | |
| #include <imgui_impl_opengl3.h>
 | |
| 
 | |
| #include <ox/std/defines.hpp>
 | |
| 
 | |
| #include <turbine/config.hpp>
 | |
| 
 | |
| #include "context.hpp"
 | |
| 
 | |
| namespace turbine {
 | |
| 
 | |
| [[nodiscard]]
 | |
| inline GlfwContext &glctx(Context &ctx) noexcept {
 | |
| 	if constexpr(ox::defines::Debug) {
 | |
| 		return dynamic_cast<GlfwContext&>(ctx);
 | |
| 	} else {
 | |
| 		return static_cast<GlfwContext&>(ctx);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| namespace gl {
 | |
| 
 | |
| void addDrawer(Context &ctx, Drawer *cd) noexcept {
 | |
| 	auto &gctx = glctx(ctx);
 | |
| 	gctx.drawers.emplace_back(cd);
 | |
| }
 | |
| 
 | |
| void removeDrawer(Context &ctx, Drawer *cd) noexcept {
 | |
| 	auto &gctx = glctx(ctx);
 | |
| 	for (auto i = 0u; i < gctx.drawers.size(); ++i) {
 | |
| 		if (gctx.drawers[i] == cd) {
 | |
| 			oxIgnoreError(gctx.drawers.erase(i));
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| }
 | |
| 
 | |
| static void handleGlfwError(int err, const char *desc) noexcept {
 | |
| 	oxErrf("GLFW error ({}): {}\n", err, desc);
 | |
| }
 | |
| 
 | |
| static auto setKeyDownStatus(GlfwContext *gctx, Key key, bool down) noexcept {
 | |
| 	if (down) {
 | |
| 		gctx->keysDown |= 1llu << static_cast<int>(key);
 | |
| 	} else {
 | |
| 		gctx->keysDown &= ~(1llu << static_cast<int>(key));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void handleKeyPress(Context *ctx, int key, bool down) noexcept {
 | |
| 	static constexpr auto keyMap = [] {
 | |
| 		ox::Array<Key, GLFW_KEY_LAST> map = {};
 | |
| 		for (auto i = 0u; i < 26; ++i) {
 | |
| 			map[GLFW_KEY_A + i] = static_cast<Key>(static_cast<unsigned>(Key::Alpha_A) + i);
 | |
| 		}
 | |
| 		for (auto i = 0u; i < 10; ++i) {
 | |
| 			map[GLFW_KEY_0 + i] = static_cast<Key>(static_cast<unsigned>(Key::Num_0) + i);
 | |
| 		}
 | |
| 		map[GLFW_KEY_LEFT_ALT]      = Key::Mod_Alt;
 | |
| 		map[GLFW_KEY_RIGHT_ALT]     = Key::Mod_Alt;
 | |
| 		map[GLFW_KEY_LEFT_CONTROL]  = Key::Mod_Ctrl;
 | |
| 		map[GLFW_KEY_RIGHT_CONTROL] = Key::Mod_Ctrl;
 | |
| 		map[GLFW_KEY_LEFT_SUPER]    = Key::Mod_Super;
 | |
| 		map[GLFW_KEY_RIGHT_SUPER]   = Key::Mod_Super;
 | |
| 		map[GLFW_KEY_ESCAPE]        = Key::Escape;
 | |
| 		return map;
 | |
| 	}();
 | |
| 	const auto eventHandler = keyEventHandler(*ctx);
 | |
| 	auto &gctx = glctx(*ctx);
 | |
| 	const auto k = keyMap[static_cast<std::size_t>(key)];
 | |
| 	setKeyDownStatus(&gctx, k, down);
 | |
| 	if (eventHandler) {
 | |
| 		eventHandler(*ctx, k, down);
 | |
| 	}
 | |
| 	//if constexpr(ox::defines::Debug) {
 | |
| 	//	switch (key) {
 | |
| 	//		case GLFW_KEY_ESCAPE:
 | |
| 	//		case GLFW_KEY_Q:
 | |
| 	//			oxIgnoreError(requestShutdown(ctx));
 | |
| 	//			break;
 | |
| 	//		default:
 | |
| 	//			break;
 | |
| 	//	}
 | |
| 	//}
 | |
| }
 | |
| 
 | |
| static void handleGlfwCursorPosEvent(GLFWwindow*, double, double) noexcept {
 | |
| }
 | |
| 
 | |
| static void handleGlfwMouseButtonEvent(GLFWwindow *window, int, int, int) noexcept {
 | |
| 	const auto ctx = static_cast<GlfwContext*>(glfwGetWindowUserPointer(window));
 | |
| 	ctx->uninterruptedRefreshes = 25;
 | |
| }
 | |
| 
 | |
| static void handleGlfwKeyEvent(GLFWwindow *window, int key, int, int action, int) noexcept {
 | |
| 	const auto ctx = static_cast<GlfwContext*>(glfwGetWindowUserPointer(window));
 | |
| 	ctx->uninterruptedRefreshes = 25;
 | |
| 	if (action == GLFW_PRESS) {
 | |
| 		handleKeyPress(ctx, key, true);
 | |
| 	} else if (action == GLFW_RELEASE) {
 | |
| 		handleKeyPress(ctx, key, false);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void themeImgui() noexcept {
 | |
| 	// Dark Ruda style by Raikiri from ImThemes
 | |
| 	auto &style = ImGui::GetStyle();
 | |
| 	style.Alpha = 1.0;
 | |
| 	style.DisabledAlpha = 0.6000000238418579;
 | |
| 	style.WindowPadding = ImVec2(8.0, 8.0);
 | |
| 	style.WindowRounding = 0.0;
 | |
| 	style.WindowBorderSize = 1.0;
 | |
| 	style.WindowMinSize = ImVec2(32.0, 32.0);
 | |
| 	style.WindowTitleAlign = ImVec2(0.0, 0.5);
 | |
| 	style.WindowMenuButtonPosition = ImGuiDir_Left;
 | |
| 	style.ChildRounding = 0.0;
 | |
| 	style.ChildBorderSize = 1.0;
 | |
| 	style.PopupRounding = 0.0;
 | |
| 	style.PopupBorderSize = 1.0;
 | |
| 	style.FramePadding = ImVec2(4.0, 3.0);
 | |
| 	// custom value
 | |
| 	style.FrameRounding = 3.0;
 | |
| 	style.FrameBorderSize = 0.0;
 | |
| 	style.ItemSpacing = ImVec2(8.0, 4.0);
 | |
| 	style.ItemInnerSpacing = ImVec2(4.0, 4.0);
 | |
| 	style.CellPadding = ImVec2(4.0, 2.0);
 | |
| 	style.IndentSpacing = 21.0;
 | |
| 	style.ColumnsMinSpacing = 6.0;
 | |
| 	style.ScrollbarSize = 14.0;
 | |
| 	style.ScrollbarRounding = 9.0;
 | |
| 	style.GrabMinSize = 10.0;
 | |
| 	style.GrabRounding = 4.0;
 | |
| 	style.TabRounding = 4.0;
 | |
| 	style.TabBorderSize = 0.0;
 | |
| 	style.TabMinWidthForCloseButton = 0.0;
 | |
| 	style.ColorButtonPosition = ImGuiDir_Right;
 | |
| 	style.ButtonTextAlign = ImVec2(0.5, 0.5);
 | |
| 	style.SelectableTextAlign = ImVec2(0.0, 0.0);
 | |
| 	// colors
 | |
| 	constexpr auto imVec4 = [](double r, double g, double b, double a) {
 | |
| 		return ImVec4(
 | |
| 				static_cast<float>(r),
 | |
| 				static_cast<float>(g),
 | |
| 				static_cast<float>(b),
 | |
| 				static_cast<float>(a));
 | |
| 	};
 | |
| 	style.Colors[ImGuiCol_Text] = imVec4(0.9490196108818054, 0.95686274766922, 0.9764705896377563, 1.0);
 | |
| 	style.Colors[ImGuiCol_TextDisabled] = imVec4(0.3568627536296844, 0.4196078479290009, 0.4666666686534882, 1.0);
 | |
| 	style.Colors[ImGuiCol_WindowBg] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0);
 | |
| 	style.Colors[ImGuiCol_ChildBg] = imVec4(0.1490196138620377, 0.1764705926179886, 0.2196078449487686, 1.0);
 | |
| 	style.Colors[ImGuiCol_PopupBg] = imVec4(0.0784313753247261, 0.0784313753247261, 0.0784313753247261, 0.9399999976158142);
 | |
| 	style.Colors[ImGuiCol_Border] = imVec4(0.0784313753247261, 0.09803921729326248, 0.1176470592617989, 1.0);
 | |
| 	style.Colors[ImGuiCol_BorderShadow] = imVec4(0.0, 0.0, 0.0, 0.0);
 | |
| 	style.Colors[ImGuiCol_FrameBg] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0);
 | |
| 	style.Colors[ImGuiCol_FrameBgHovered] = imVec4(0.1176470592617989, 0.2000000029802322, 0.2784313857555389, 1.0);
 | |
| 	style.Colors[ImGuiCol_FrameBgActive] = imVec4(0.08627451211214066, 0.1176470592617989, 0.1372549086809158, 1.0);
 | |
| 	style.Colors[ImGuiCol_TitleBg] = imVec4(0.08627451211214066, 0.1176470592617989, 0.1372549086809158, 0.6499999761581421);
 | |
| 	style.Colors[ImGuiCol_TitleBgActive] = imVec4(0.0784313753247261, 0.09803921729326248, 0.1176470592617989, 1.0);
 | |
| 	style.Colors[ImGuiCol_TitleBgCollapsed] = imVec4(0.0, 0.0, 0.0, 0.5099999904632568);
 | |
| 	style.Colors[ImGuiCol_MenuBarBg] = imVec4(0.1490196138620377, 0.1764705926179886, 0.2196078449487686, 1.0);
 | |
| 	style.Colors[ImGuiCol_ScrollbarBg] = imVec4(0.01960784383118153, 0.01960784383118153, 0.01960784383118153, 0.3899999856948853);
 | |
| 	style.Colors[ImGuiCol_ScrollbarGrab] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0);
 | |
| 	style.Colors[ImGuiCol_ScrollbarGrabHovered] = imVec4(0.1764705926179886, 0.2196078449487686, 0.2470588237047195, 1.0);
 | |
| 	style.Colors[ImGuiCol_ScrollbarGrabActive] = imVec4(0.08627451211214066, 0.2078431397676468, 0.3098039329051971, 1.0);
 | |
| 	style.Colors[ImGuiCol_CheckMark] = imVec4(0.2784313857555389, 0.5568627715110779, 1.0, 1.0);
 | |
| 	style.Colors[ImGuiCol_SliderGrab] = imVec4(0.2784313857555389, 0.5568627715110779, 1.0, 1.0);
 | |
| 	style.Colors[ImGuiCol_SliderGrabActive] = imVec4(0.3686274588108063, 0.6078431606292725, 1.0, 1.0);
 | |
| 	style.Colors[ImGuiCol_Button] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0);
 | |
| 	style.Colors[ImGuiCol_ButtonHovered] = imVec4(0.2784313857555389, 0.5568627715110779, 1.0, 1.0);
 | |
| 	style.Colors[ImGuiCol_ButtonActive] = imVec4(0.05882352963089943, 0.529411792755127, 0.9764705896377563, 1.0);
 | |
| 	// custom value
 | |
| 	style.Colors[ImGuiCol_Header] = imVec4(0.4000000029802322, 0.4470588237047195, 0.4862745225429535, 0.550000011920929);
 | |
| 	style.Colors[ImGuiCol_HeaderHovered] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.800000011920929);
 | |
| 	style.Colors[ImGuiCol_HeaderActive] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 1.0);
 | |
| 	style.Colors[ImGuiCol_Separator] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0);
 | |
| 	style.Colors[ImGuiCol_SeparatorHovered] = imVec4(0.09803921729326248, 0.4000000059604645, 0.7490196228027344, 0.7799999713897705);
 | |
| 	style.Colors[ImGuiCol_SeparatorActive] = imVec4(0.09803921729326248, 0.4000000059604645, 0.7490196228027344, 1.0);
 | |
| 	style.Colors[ImGuiCol_ResizeGrip] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.25);
 | |
| 	style.Colors[ImGuiCol_ResizeGripHovered] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.6700000166893005);
 | |
| 	style.Colors[ImGuiCol_ResizeGripActive] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.949999988079071);
 | |
| 	style.Colors[ImGuiCol_Tab] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0);
 | |
| 	style.Colors[ImGuiCol_TabHovered] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.800000011920929);
 | |
| 	style.Colors[ImGuiCol_TabActive] = imVec4(0.2000000029802322, 0.2470588237047195, 0.2862745225429535, 1.0);
 | |
| 	style.Colors[ImGuiCol_TabUnfocused] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0);
 | |
| 	style.Colors[ImGuiCol_TabUnfocusedActive] = imVec4(0.1098039224743843, 0.1490196138620377, 0.168627455830574, 1.0);
 | |
| 	style.Colors[ImGuiCol_PlotLines] = imVec4(0.6078431606292725, 0.6078431606292725, 0.6078431606292725, 1.0);
 | |
| 	style.Colors[ImGuiCol_PlotLinesHovered] = imVec4(1.0, 0.4274509847164154, 0.3490196168422699, 1.0);
 | |
| 	style.Colors[ImGuiCol_PlotHistogram] = imVec4(0.8980392217636108, 0.6980392336845398, 0.0, 1.0);
 | |
| 	style.Colors[ImGuiCol_PlotHistogramHovered] = imVec4(1.0, 0.6000000238418579, 0.0, 1.0);
 | |
| 	style.Colors[ImGuiCol_TableHeaderBg] = imVec4(0.1882352977991104, 0.1882352977991104, 0.2000000029802322, 1.0);
 | |
| 	style.Colors[ImGuiCol_TableBorderStrong] = imVec4(0.3098039329051971, 0.3098039329051971, 0.3490196168422699, 1.0);
 | |
| 	style.Colors[ImGuiCol_TableBorderLight] = imVec4(0.2274509817361832, 0.2274509817361832, 0.2470588237047195, 1.0);
 | |
| 	style.Colors[ImGuiCol_TableRowBg] = imVec4(0.0, 0.0, 0.0, 0.0);
 | |
| 	style.Colors[ImGuiCol_TableRowBgAlt] = imVec4(1.0, 1.0, 1.0, 0.05999999865889549);
 | |
| 	style.Colors[ImGuiCol_TextSelectedBg] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 0.3499999940395355);
 | |
| 	style.Colors[ImGuiCol_DragDropTarget] = imVec4(1.0, 1.0, 0.0, 0.8999999761581421);
 | |
| 	style.Colors[ImGuiCol_NavHighlight] = imVec4(0.2588235437870026, 0.5882353186607361, 0.9764705896377563, 1.0);
 | |
| 	style.Colors[ImGuiCol_NavWindowingHighlight] = imVec4(1.0, 1.0, 1.0, 0.699999988079071);
 | |
| 	style.Colors[ImGuiCol_NavWindowingDimBg] = imVec4(0.800000011920929, 0.800000011920929, 0.800000011920929, 0.2000000029802322);
 | |
| 	style.Colors[ImGuiCol_ModalWindowDimBg] = imVec4(0.800000011920929, 0.800000011920929, 0.800000011920929, 0.3499999940395355);
 | |
| }
 | |
| 
 | |
| ox::Error initGfx(Context &ctx) noexcept {
 | |
| 	auto &gctx = glctx(ctx);
 | |
| 	glfwSetErrorCallback(handleGlfwError);
 | |
| 	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
 | |
| 	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
 | |
| 	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 | |
| 	if constexpr(ox::defines::OS == ox::OS::Darwin) {
 | |
| 		glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
 | |
| 	}
 | |
| 	glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
 | |
| 	auto cstr = ox_malloca(ctx.keelCtx.appName.bytes() + 1, char);
 | |
| 	ox_strncpy(cstr.get(), ctx.keelCtx.appName.data(), ctx.keelCtx.appName.bytes());
 | |
| 	constexpr auto Scale = 5;
 | |
| 	gctx.window = glfwCreateWindow(240 * Scale, 160 * Scale, cstr, nullptr, nullptr);
 | |
| 	if (gctx.window == nullptr) {
 | |
| 		return OxError(1, "Could not open GLFW window");
 | |
| 	}
 | |
| 	glfwSetCursorPosCallback(gctx.window, handleGlfwCursorPosEvent);
 | |
| 	glfwSetMouseButtonCallback(gctx.window, handleGlfwMouseButtonEvent);
 | |
| 	glfwSetKeyCallback(gctx.window, handleGlfwKeyEvent);
 | |
| 	glfwSetWindowUserPointer(gctx.window, &ctx);
 | |
| 	glfwMakeContextCurrent(gctx.window);
 | |
| 	if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress))) {
 | |
| 		return OxError(2, "Could not init Glad");
 | |
| 	}
 | |
| 	if constexpr(config::ImGuiEnabled) {
 | |
| 		IMGUI_CHECKVERSION();
 | |
| 		ImGui::CreateContext();
 | |
| 		auto &io = ImGui::GetIO();
 | |
| 		io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
 | |
| 		//io.MouseDrawCursor = true;
 | |
| 		ImGui_ImplGlfw_InitForOpenGL(gctx.window, true);
 | |
| 		ImGui_ImplOpenGL3_Init();
 | |
| 		themeImgui();
 | |
| 	}
 | |
| 	return {};
 | |
| }
 | |
| 
 | |
| void setWindowTitle(Context &ctx, ox::CRStringView title) noexcept {
 | |
| 	auto &gctx = glctx(ctx);
 | |
| 	auto cstr = ox_malloca(title.bytes() + 1, char);
 | |
| 	ox_strncpy(cstr.get(), title.data(), title.bytes());
 | |
| 	glfwSetWindowTitle(gctx.window, cstr);
 | |
| }
 | |
| 
 | |
| void focusWindow(Context &ctx) noexcept {
 | |
| 	auto &gctx = glctx(ctx);
 | |
| 	glfwFocusWindow(gctx.window);
 | |
| }
 | |
| 
 | |
| int getScreenWidth(Context &ctx) noexcept {
 | |
| 	auto &gctx = glctx(ctx);
 | |
| 	int w = 0, h = 0;
 | |
| 	glfwGetFramebufferSize(gctx.window, &w, &h);
 | |
| 	return w;
 | |
| }
 | |
| 
 | |
| int getScreenHeight(Context &ctx) noexcept {
 | |
| 	auto &gctx = glctx(ctx);
 | |
| 	int w = 0, h = 0;
 | |
| 	glfwGetFramebufferSize(gctx.window, &w, &h);
 | |
| 	return h;
 | |
| }
 | |
| 
 | |
| ox::Size getScreenSize(Context &ctx) noexcept {
 | |
| 	auto &gctx = glctx(ctx);
 | |
| 	int w = 0, h = 0;
 | |
| 	glfwGetFramebufferSize(gctx.window, &w, &h);
 | |
| 	return {w, h};
 | |
| }
 | |
| 
 | |
| ox::Bounds getWindowBounds(Context &ctx) noexcept {
 | |
| 	auto &gctx = glctx(ctx);
 | |
| 	ox::Bounds bnds;
 | |
| 	glfwGetWindowPos(gctx.window, &bnds.x, &bnds.y);
 | |
| 	glfwGetWindowSize(gctx.window, &bnds.width, &bnds.height);
 | |
| 	return bnds;
 | |
| }
 | |
| 
 | |
| ox::Error setWindowBounds(Context &ctx, const ox::Bounds &bnds) noexcept {
 | |
| 	auto &gctx = glctx(ctx);
 | |
| 	glfwSetWindowPos(gctx.window, bnds.x, bnds.y);
 | |
| 	glfwSetWindowSize(gctx.window, bnds.width, bnds.height);
 | |
| 	return {};
 | |
| }
 | |
| 
 | |
| void setConstantRefresh(Context &ctx, bool r) noexcept {
 | |
| 	auto &gctx = glctx(ctx);
 | |
| 	gctx.constantRefresh = r;
 | |
| }
 | |
| 
 | |
| }
 |