File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										271
									
								
								src/Config.cppm
									
									
									
									
									
								
							
							
						
						
									
										271
									
								
								src/Config.cppm
									
									
									
									
									
								
							| @@ -1,271 +0,0 @@ | |||||||
| module; |  | ||||||
|  |  | ||||||
| #include <boost/algorithm/string.hpp> |  | ||||||
|  |  | ||||||
| extern "C" { |  | ||||||
| #include <wlr/types/wlr_keyboard.h> |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #include <xkbcommon/xkbcommon.h> |  | ||||||
|  |  | ||||||
| #include "dhos_config.h" |  | ||||||
|  |  | ||||||
| export module LunarWM.Config; |  | ||||||
|  |  | ||||||
| import std; |  | ||||||
|  |  | ||||||
| static auto default_configuration_paths() |  | ||||||
|     -> std::vector<std::filesystem::path> const |  | ||||||
| { |  | ||||||
| 	std::vector<std::filesystem::path> paths = { |  | ||||||
| 		"lunarwm/lunarwm.dcfg", |  | ||||||
| 		"lunarwm.dcfg", |  | ||||||
| 		"/etc/lunarwm.dcfg", |  | ||||||
| 		"/etc/lunarwm/lunarwm.dcfg", |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	if (char const *xdg_dirs |  | ||||||
| 	    = std::getenv("XDG_CONFIG_DIRS")) { // NOLINT(concurrency-mt-unsafe) |  | ||||||
| 		std::string const dirs { xdg_dirs }; |  | ||||||
| 		std::istringstream ss { dirs }; |  | ||||||
| 		std::string dir; |  | ||||||
| 		while (std::getline(ss, dir, ':')) { |  | ||||||
| 			if (!dir.empty()) { |  | ||||||
| 				paths.push_back( |  | ||||||
| 				    std::filesystem::path(dir) / "lunarwm/lunarwm.dcfg"); |  | ||||||
| 				paths.push_back(std::filesystem::path(dir) / "lunarwm.dcfg"); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (char const *home |  | ||||||
| 	    = std::getenv("HOME")) { // NOLINT(concurrency-mt-unsafe) |  | ||||||
| 		std::filesystem::path const home_path(home); |  | ||||||
| 		paths.push_back(home_path / ".config/lunarwm/lunarwm.dcfg"); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return paths; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline auto string_to_modifier(std::string_view str) |  | ||||||
|     -> std::optional<wlr_keyboard_modifier> |  | ||||||
| { |  | ||||||
| 	if (boost::iequals(str, "shift")) { |  | ||||||
| 		return WLR_MODIFIER_SHIFT; |  | ||||||
| 	} else if (boost::iequals(str, "ctrl")) { |  | ||||||
| 		return WLR_MODIFIER_CTRL; |  | ||||||
| 	} else if (boost::iequals(str, "alt")) { |  | ||||||
| 		return WLR_MODIFIER_ALT; |  | ||||||
| 	} else if (boost::iequals(str, "mod2")) { |  | ||||||
| 		return WLR_MODIFIER_MOD2; |  | ||||||
| 	} else if (boost::iequals(str, "mod3")) { |  | ||||||
| 		return WLR_MODIFIER_MOD3; |  | ||||||
| 	} else if (boost::iequals(str, "logo") || boost::iequals(str, "win") |  | ||||||
| 	    || boost::iequals(str, "super") || boost::iequals(str, "mod4")) { |  | ||||||
| 		return WLR_MODIFIER_LOGO; |  | ||||||
| 	} else if (boost::iequals(str, "mod5")) { |  | ||||||
| 		return WLR_MODIFIER_MOD5; |  | ||||||
| 	} |  | ||||||
| 	return std::nullopt; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export namespace LunarWM::Config { |  | ||||||
|  |  | ||||||
| struct Configuration { |  | ||||||
| 	dhos::Value e; |  | ||||||
|  |  | ||||||
| 	struct KeyCombo { |  | ||||||
| 		uint32_t modifiers {}; |  | ||||||
| 		xkb_keysym_t sym {}; |  | ||||||
|  |  | ||||||
| 		static auto parse(std::string const &str, |  | ||||||
| 		    std::unordered_map<std::string, uint32_t> const &modifier_alias) |  | ||||||
| 		    -> std::optional<Configuration::KeyCombo>; |  | ||||||
| 	}; |  | ||||||
| 	struct Keybind { |  | ||||||
| 		KeyCombo combo; |  | ||||||
| 		dhos::Value action; |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	struct { |  | ||||||
| 		struct { |  | ||||||
| 			std::vector<std::string> xkb_options; |  | ||||||
| 		} keyboard {}; |  | ||||||
| 	} input {}; |  | ||||||
|  |  | ||||||
| 	std::unordered_map<std::string, uint32_t> modifier_aliases; |  | ||||||
| 	std::vector<Keybind> keybindings; |  | ||||||
|  |  | ||||||
| 	dhos::Object custom_lib; |  | ||||||
|  |  | ||||||
| 	void execute_keybind(Keybind const &keybind) |  | ||||||
| 	{ |  | ||||||
| 		dhos::eval(keybind.action, &this->custom_lib); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void add_to_custom_lib(std::string const &name, dhos::Builtin const &fn) |  | ||||||
| 	{ |  | ||||||
| 		this->custom_lib.insert_or_assign(name, dhos::Value(fn, e.env)); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void load() { this->load(default_configuration_paths()); } |  | ||||||
| 	void load(std::vector<std::filesystem::path> const &paths); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| auto Configuration::KeyCombo::parse(std::string const &str, |  | ||||||
|     std::unordered_map<std::string, uint32_t> const &modifier_alias) |  | ||||||
|     -> std::optional<Configuration::KeyCombo> |  | ||||||
| { |  | ||||||
| 	std::istringstream iss(str); |  | ||||||
| 	std::string component; |  | ||||||
| 	std::vector<std::string> components; |  | ||||||
| 	components.reserve(5); |  | ||||||
| 	while (std::getline(iss, component, '-')) { |  | ||||||
| 		boost::algorithm::trim(component); |  | ||||||
| 		if (component.empty()) { |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
| 		components.push_back(component); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (components.size() < 2) { |  | ||||||
| 		return std::nullopt; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	wlr_keyboard_modifier mods {}; |  | ||||||
| 	for (auto const &entry : |  | ||||||
| 	    std::span(components.begin(), components.end() - 1)) { |  | ||||||
| 		auto mod_opt = string_to_modifier(entry); |  | ||||||
| 		if (!mod_opt) { |  | ||||||
| 			if (modifier_alias.contains(entry)) { |  | ||||||
| 				mod_opt = static_cast<wlr_keyboard_modifier>( |  | ||||||
| 				    modifier_alias.at(entry)); |  | ||||||
| 			} else { |  | ||||||
| 				return std::nullopt; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		mods = static_cast<wlr_keyboard_modifier>( |  | ||||||
| 		    static_cast<unsigned>(mods) | static_cast<unsigned>(*mod_opt)); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	auto const &key_name = components.back(); |  | ||||||
| 	xkb_keysym_t const sym = xkb_keysym_from_name(key_name.c_str(), |  | ||||||
| 	    static_cast<enum xkb_keysym_flags>(XKB_KEYSYM_CASE_INSENSITIVE)); |  | ||||||
| 	if (sym == XKB_KEY_NoSymbol) { |  | ||||||
| 		return std::nullopt; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	Configuration::KeyCombo const combo { |  | ||||||
| 		.modifiers = mods, |  | ||||||
| 		.sym = sym, |  | ||||||
| 	}; |  | ||||||
| 	return combo; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void Configuration::load(std::vector<std::filesystem::path> const &paths) |  | ||||||
| { |  | ||||||
| 	for (auto const &path : paths) { |  | ||||||
| 		try { |  | ||||||
| 			e = dhos::eval(dhos::parse_config(path), &this->custom_lib); |  | ||||||
| 			if (!e.is_obj()) { |  | ||||||
| 				throw std::runtime_error("Top level is not an object!"); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			static dhos::Object base = dhos::make_default_lib(); |  | ||||||
| 			auto merged = dhos::merge_lib(base, &this->custom_lib); |  | ||||||
| 			dhos::get_env_tbl(&*e.env)["lib"] = dhos::Value(merged, e.env); |  | ||||||
|  |  | ||||||
| 			if (e["input"].is_obj()) { |  | ||||||
| 				auto const &input = e["input"]; |  | ||||||
| 				if (input["keyboard"].is_obj()) { |  | ||||||
| 					auto const &keyboard = input["keyboard"]; |  | ||||||
| 					if (keyboard["xkb_options"].is_arr()) { |  | ||||||
| 						auto const &arr = keyboard["xkb_options"].arr(); |  | ||||||
| 						this->input.keyboard.xkb_options.clear(); |  | ||||||
| 						for (auto const &value : arr) { |  | ||||||
| 							if (value.is_str()) { |  | ||||||
| 								this->input.keyboard.xkb_options.push_back( |  | ||||||
| 								    value.str()); |  | ||||||
| 							} else { |  | ||||||
| 								// FIXME: Send warning |  | ||||||
| 								std::println("Invalid xkb_options type"); |  | ||||||
| 							} |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if (e["modifier_aliases"].is_obj()) { |  | ||||||
| 				auto const &modifier_aliases = e["modifier_aliases"]; |  | ||||||
| 				this->modifier_aliases.clear(); |  | ||||||
|  |  | ||||||
| 				for (auto const &[k, v] : modifier_aliases.obj()) { |  | ||||||
| 					if (!v.is_str()) { |  | ||||||
| 						// FIXME: Send warning |  | ||||||
| 						std::println("Invalid modifier aliases value"); |  | ||||||
| 						continue; |  | ||||||
| 					} else { |  | ||||||
| 						auto const to_parse = v.str() + "-a"; |  | ||||||
| 						std::println( |  | ||||||
| 						    "Sending alias value to parse: {}", to_parse); |  | ||||||
| 						auto const kc_opt = KeyCombo::parse(to_parse, {}); |  | ||||||
| 						if (kc_opt) { |  | ||||||
| 							auto const mod_opt = kc_opt->modifiers; |  | ||||||
| 							this->modifier_aliases[k] = mod_opt; |  | ||||||
| 						} else { |  | ||||||
| 							// FIXME: Send warning |  | ||||||
| 							std::println( |  | ||||||
| 							    "Invalid modifier aliases value format"); |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if (e["keybindings"].is_arr()) { |  | ||||||
| 				auto const &keybindings = e["keybindings"].arr(); |  | ||||||
| 				this->keybindings.clear(); |  | ||||||
|  |  | ||||||
| 				for (auto const &v : keybindings) { |  | ||||||
| 					if (v.is_obj()) { |  | ||||||
| 						auto const &o = v.obj(); |  | ||||||
| 						if (!o.contains("bind") || !o.contains("action") |  | ||||||
| 						    || !o.at("bind").is_str() |  | ||||||
| 						    || !o.at("action").is_call()) { |  | ||||||
| 							// FIXME: Send warning |  | ||||||
| 							std::println("Invalid keybinding object format"); |  | ||||||
| 							continue; |  | ||||||
| 						} |  | ||||||
|  |  | ||||||
| 						auto const combo_opt = KeyCombo::parse( |  | ||||||
| 						    o.at("bind").str(), this->modifier_aliases); |  | ||||||
| 						if (!combo_opt) { |  | ||||||
| 							// FIXME: Send warning |  | ||||||
| 							std::println("Invalid keybinding combo"); |  | ||||||
| 							continue; |  | ||||||
| 						} |  | ||||||
|  |  | ||||||
| 						auto const &combo = *combo_opt; |  | ||||||
|  |  | ||||||
| 						this->keybindings.push_back({ |  | ||||||
| 						    .combo = combo, |  | ||||||
| 						    .action = o.at("action"), |  | ||||||
| 						}); |  | ||||||
| 					} else { |  | ||||||
| 						// FIXME: Send warning |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			return; |  | ||||||
| 		} catch (std::filesystem::filesystem_error const &e) { |  | ||||||
| 			(void)e; |  | ||||||
| 		} catch (std::exception &e) { |  | ||||||
| 			(void)e; |  | ||||||
| 			std::println("Exception for config file: {}", e.what()); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	throw std::runtime_error("Unable to find a valid configuration file!"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| } // LunarWM::Config |  | ||||||
							
								
								
									
										1572
									
								
								src/LunarWM.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1572
									
								
								src/LunarWM.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1818
									
								
								src/LunarWM.cppm
									
									
									
									
									
								
							
							
						
						
									
										1818
									
								
								src/LunarWM.cppm
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										173
									
								
								src/LunarWM.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/LunarWM.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | |||||||
|  | #ifndef LUNAR_WM_H | ||||||
|  | #define LUNAR_WM_H | ||||||
|  |  | ||||||
|  | #include <EGL/egl.h> | ||||||
|  | #include <EGL/eglext.h> | ||||||
|  | #include <GLES2/gl2.h> | ||||||
|  | #include <GLES2/gl2ext.h> | ||||||
|  | #include <GLES3/gl3.h> | ||||||
|  | #include <GLES3/gl31.h> | ||||||
|  | #include <openxr/openxr.h> | ||||||
|  | #include <openxr/openxr_platform.h> | ||||||
|  |  | ||||||
|  | #include <wayland-client-protocol.h> | ||||||
|  | #include <wayland-egl.h> | ||||||
|  | #include <wayland-server-core.h> | ||||||
|  |  | ||||||
|  | #include <wlr/backend/session.h> | ||||||
|  | #include <wlr/backend/wayland.h> | ||||||
|  | #include <wlr/render/allocator.h> | ||||||
|  | #include <wlr/render/egl.h> | ||||||
|  | #include <wlr/render/gles2.h> | ||||||
|  | #include <wlr/types/wlr_compositor.h> | ||||||
|  | #include <wlr/types/wlr_cursor.h> | ||||||
|  | #include <wlr/types/wlr_data_device.h> | ||||||
|  | #include <wlr/types/wlr_pointer.h> | ||||||
|  | #include <wlr/types/wlr_subcompositor.h> | ||||||
|  | #include <wlr/types/wlr_xdg_shell.h> | ||||||
|  | #include <wlr/util/log.h> | ||||||
|  |  | ||||||
|  | #include <raylib.h> | ||||||
|  | #include <raymath.h> | ||||||
|  | #include <rlgl.h> | ||||||
|  |  | ||||||
|  | #include "common.h" | ||||||
|  |  | ||||||
|  | struct LunarWM; | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  | 	struct LunarWM *server; | ||||||
|  |  | ||||||
|  | 	struct wl_list link; | ||||||
|  | 	struct wlr_keyboard *wlr_keyboard; | ||||||
|  |  | ||||||
|  | 	struct wl_listener modifiers; | ||||||
|  | 	struct wl_listener key; | ||||||
|  | 	struct wl_listener destroy; | ||||||
|  | } LunarWM_Keyboard; | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  | 	struct LunarWM *server; | ||||||
|  |  | ||||||
|  | 	struct wl_listener commit; | ||||||
|  | 	struct wl_listener destroy; | ||||||
|  |  | ||||||
|  | 	struct wlr_xdg_toplevel *xdg_toplevel; | ||||||
|  | 	struct wlr_surface *surface; | ||||||
|  | 	struct wlr_texture *texture; | ||||||
|  |  | ||||||
|  | 	struct wlr_buffer *locked_buffer; | ||||||
|  | 	struct wlr_gles2_texture_attribs attribs; | ||||||
|  | 	struct wlr_gles2_texture *gles_texture; | ||||||
|  | 	Texture2D rl_texture; | ||||||
|  | } LunarWM_Toplevel; | ||||||
|  |  | ||||||
|  | bool LunarWM_Toplevel_init( | ||||||
|  |     LunarWM_Toplevel *tl, struct LunarWM *wm, struct wlr_xdg_toplevel *xdg); | ||||||
|  | bool LunarWM_Toplevel_destroy(LunarWM_Toplevel *this); | ||||||
|  |  | ||||||
|  | bool LunarWM_Toplevel_update(LunarWM_Toplevel *this); | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  | 	XrSwapchain swapchain; | ||||||
|  | 	int64_t swapchain_format; | ||||||
|  | 	GLuint *v_image_views; | ||||||
|  | } LunarWM_SwapchainInfo; | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  | 	XrSwapchain handle; | ||||||
|  | 	XrSwapchainImageOpenGLESKHR *a_imgs; | ||||||
|  | 	uint32_t a_imgs_count; | ||||||
|  | } LunarWM_SwapchainImagesEntry; | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  | 	XrHandJointLocationEXT joint_locations[XR_HAND_JOINT_COUNT_EXT]; | ||||||
|  | 	XrHandTrackerEXT hand_tracker; | ||||||
|  | } LunarWM_Hand; | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  | 	XrTime predicted_display_time; | ||||||
|  | 	XrCompositionLayerProjection layer_projection; | ||||||
|  | 	XrCompositionLayerBaseHeader *layers[10]; | ||||||
|  | 	uint32_t layers_count; | ||||||
|  | 	XrCompositionLayerProjectionView | ||||||
|  | 	    layer_projection_views[10]; // Hopefully we dont have more than 10. | ||||||
|  | 	uint32_t layer_projection_views_count; | ||||||
|  | } LunarWM_RenderLayerInfo; | ||||||
|  |  | ||||||
|  | typedef struct LunarWM { | ||||||
|  | 	struct { | ||||||
|  | 		struct wl_display *display; | ||||||
|  | 		struct wl_event_loop *event_loop; | ||||||
|  |  | ||||||
|  | 		struct wlr_backend *backend; | ||||||
|  | 		struct wlr_renderer *renderer; | ||||||
|  | 		struct wlr_session *session; | ||||||
|  |  | ||||||
|  | 		struct wlr_egl *egl; | ||||||
|  | 		EGLDisplay egl_display; | ||||||
|  | 		EGLContext egl_context; | ||||||
|  | 		EGLConfig egl_config; | ||||||
|  |  | ||||||
|  | 		struct wlr_allocator *allocator; | ||||||
|  | 		struct wlr_compositor *compositor; | ||||||
|  | 		struct wlr_subcompositor *subcompositor; | ||||||
|  | 		struct wlr_data_device_manager *data_device_manager; | ||||||
|  |  | ||||||
|  | 		struct wlr_seat *seat; | ||||||
|  | 		struct wl_list keyboards; | ||||||
|  | 		struct wl_listener new_input_listener; | ||||||
|  |  | ||||||
|  | 		struct wlr_xdg_shell *xdg_shell; | ||||||
|  | 		struct wl_listener new_xdg_toplevel_listener; | ||||||
|  | 		struct wl_listener new_xdg_popup_listener; | ||||||
|  |  | ||||||
|  | 		struct wlr_cursor *cursor; | ||||||
|  |  | ||||||
|  | 		LunarWM_Toplevel **v_toplevels; | ||||||
|  | 	} wayland; | ||||||
|  |  | ||||||
|  | 	struct { | ||||||
|  | 		XrInstance instance; | ||||||
|  | 		XrSystemId system_id; | ||||||
|  | 		XrSession session; | ||||||
|  | 		XrSessionState session_state; | ||||||
|  | 		struct { | ||||||
|  | 			LunarWM_SwapchainInfo *v_color; | ||||||
|  | 			LunarWM_SwapchainInfo *v_depth; | ||||||
|  | 		} swapchains; | ||||||
|  | 		LunarWM_SwapchainImagesEntry | ||||||
|  | 		    swapchain_images[2]; // 0 is color, 1 is depth | ||||||
|  | 		XrViewConfigurationView *a_view_configuration_views; | ||||||
|  | 		uint32_t view_configuration_views_count; | ||||||
|  | 		XrEnvironmentBlendMode environment_blend_mode; | ||||||
|  | 		XrSpace local_space, view_space; | ||||||
|  | 		LunarWM_Hand hands[2]; | ||||||
|  | 		XrSystemHandTrackingPropertiesEXT hand_tracking_system_properties; | ||||||
|  |  | ||||||
|  | 		PFN_xrCreateHandTrackerEXT CreateHandTrackerEXT; | ||||||
|  | 		PFN_xrDestroyHandTrackerEXT DestroyHandTrackerEXT; | ||||||
|  | 		PFN_xrLocateHandJointsEXT LocateHandJointsEXT; | ||||||
|  |  | ||||||
|  | 		bool session_running; | ||||||
|  | 	} xr; | ||||||
|  |  | ||||||
|  | 	struct { | ||||||
|  | 		GLuint fbo; | ||||||
|  | 		RenderTexture2D tmp_rt; | ||||||
|  | 		Camera3D camera; | ||||||
|  | 	} renderer; | ||||||
|  |  | ||||||
|  | 	bool initialized; | ||||||
|  | 	bool running; | ||||||
|  | } LunarWM; | ||||||
|  |  | ||||||
|  | bool LunarWM_init(LunarWM *wm); | ||||||
|  | void LunarWM_destroy(LunarWM *this); | ||||||
|  |  | ||||||
|  | void LunarWM_terminate(LunarWM *this); | ||||||
|  | void LunarWM_run(LunarWM *this); | ||||||
|  |  | ||||||
|  | extern LunarWM g_wm; | ||||||
|  |  | ||||||
|  | #endif // LUNAR_WM_H | ||||||
							
								
								
									
										224
									
								
								src/Math.cppm
									
									
									
									
									
								
							
							
						
						
									
										224
									
								
								src/Math.cppm
									
									
									
									
									
								
							| @@ -1,224 +0,0 @@ | |||||||
| export module LunarWM.Math; |  | ||||||
|  |  | ||||||
| import std; |  | ||||||
|  |  | ||||||
| export namespace LunarWM::Math { |  | ||||||
|  |  | ||||||
| template<typename T = float> |  | ||||||
| requires std::is_arithmetic_v<T> struct Vec2 { |  | ||||||
| 	template<typename U = T> |  | ||||||
| 	requires std::is_arithmetic_v<U> |  | ||||||
| 	auto operator+(Vec2<U> const &other) -> Vec2<U> |  | ||||||
| 	{ |  | ||||||
| 		return { x + other.x, y + other.y }; |  | ||||||
| 	} |  | ||||||
| 	template<typename U = T> |  | ||||||
| 	requires std::is_arithmetic_v<U> |  | ||||||
| 	auto operator-(Vec2<U> const &other) -> Vec2<U> |  | ||||||
| 	{ |  | ||||||
| 		return { x - other.x, y - other.y }; |  | ||||||
| 	} |  | ||||||
| 	template<typename U = T> |  | ||||||
| 	requires std::is_arithmetic_v<U> |  | ||||||
| 	auto operator*(Vec2<U> const &other) -> Vec2<U> |  | ||||||
| 	{ |  | ||||||
| 		return { x * other.x, y * other.x }; |  | ||||||
| 	} |  | ||||||
| 	template<typename U = T> |  | ||||||
| 	requires std::is_arithmetic_v<U> auto operator*(T scalar) -> Vec2<U> |  | ||||||
| 	{ |  | ||||||
| 		return { x * scalar, y * scalar }; |  | ||||||
| 	} |  | ||||||
| 	template<typename U = T> |  | ||||||
| 	requires std::is_arithmetic_v<U> auto operator/(T scalar) -> Vec2<U> |  | ||||||
| 	{ |  | ||||||
| 		return { x / scalar, y / scalar }; |  | ||||||
| 	} |  | ||||||
| 	template<typename U = T> |  | ||||||
| 	requires std::is_arithmetic_v<U> auto operator-() -> Vec2 |  | ||||||
| 	{ |  | ||||||
| 		return { -x, -y }; |  | ||||||
| 	} |  | ||||||
| 	auto length() const -> T { return std::sqrt(x * x + y * y); } |  | ||||||
| 	auto lengthSquared() const -> T { return x * x + y * y; } |  | ||||||
| 	auto normalized() const -> Vec2<T> |  | ||||||
| 	{ |  | ||||||
| 		T len = length(); |  | ||||||
| 		if (len == T(0)) { |  | ||||||
| 			return { T(0), T(0) }; |  | ||||||
| 		} |  | ||||||
| 		return *this / len; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	T x, y; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| template<typename T> |  | ||||||
| requires std::is_arithmetic_v<T> |  | ||||||
| auto operator*(T scalar, Vec2<T> const &v) -> Vec2<T> |  | ||||||
| { |  | ||||||
| 	return { v.x * scalar, v.y * scalar }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| template<typename T = float> |  | ||||||
| requires std::is_arithmetic_v<T> struct Rect { |  | ||||||
| 	Rect(Vec2<T> pos, Vec2<T> size) |  | ||||||
| 	    : pos(pos) |  | ||||||
| 	    , size(size) |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 	Rect(T x, T y, T w, T h) |  | ||||||
| 	    : pos({ x, y }) |  | ||||||
| 	    , size({ w, h }) |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	auto x() -> T & { return pos.x; } |  | ||||||
| 	auto y() -> T & { return pos.y; } |  | ||||||
| 	auto w() -> T & { return size.x; } |  | ||||||
| 	auto h() -> T & { return size.y; } |  | ||||||
|  |  | ||||||
| 	auto x() const -> T { return pos.x; } |  | ||||||
| 	auto y() const -> T { return pos.y; } |  | ||||||
| 	auto w() const -> T { return size.x; } |  | ||||||
| 	auto h() const -> T { return size.y; } |  | ||||||
|  |  | ||||||
| 	auto left() const -> T { return x(); } |  | ||||||
| 	auto right() const -> T { return x() + w(); } |  | ||||||
| 	auto top() const -> T { return y(); } |  | ||||||
| 	auto bottom() const -> T { return y() + h(); } |  | ||||||
|  |  | ||||||
| 	auto left() -> T & { return x(); } |  | ||||||
| 	auto top() -> T & { return y(); } |  | ||||||
|  |  | ||||||
| 	Vec2<T> pos, size; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| template<typename T = float> |  | ||||||
| requires std::is_arithmetic_v<T> struct Box { |  | ||||||
| 	template<typename U = int> |  | ||||||
| 	requires std::is_arithmetic_v<U> auto operator[](U const index) -> Vec2<T> & |  | ||||||
| 	{ |  | ||||||
| 		if (index < 0 || index > 1) { |  | ||||||
| 			throw std::out_of_range("A box only has two points"); |  | ||||||
| 		} |  | ||||||
| 		return m_data[index]; |  | ||||||
| 	} |  | ||||||
| 	auto first() -> Vec2<T> & { return m_data[0]; } |  | ||||||
| 	auto second() -> Vec2<T> & { return m_data[1]; } |  | ||||||
| 	auto x0() -> T & { return m_data[0].x; } |  | ||||||
| 	auto y0() -> T & { return m_data[0].y; } |  | ||||||
| 	auto x1() -> T & { return m_data[1].x; } |  | ||||||
| 	auto y1() -> T & { return m_data[1].y; } |  | ||||||
| 	auto left() -> T & |  | ||||||
| 	{ |  | ||||||
| 		if (x0() < x1()) { |  | ||||||
| 			return x0(); |  | ||||||
| 		} |  | ||||||
| 		return x1(); |  | ||||||
| 	} |  | ||||||
| 	auto right() -> T & |  | ||||||
| 	{ |  | ||||||
| 		if (x0() > x1()) { |  | ||||||
| 			return x0(); |  | ||||||
| 		} |  | ||||||
| 		return x1(); |  | ||||||
| 	} |  | ||||||
| 	auto top() -> T & |  | ||||||
| 	{ |  | ||||||
| 		if (y0() < y1()) { |  | ||||||
| 			return y0(); |  | ||||||
| 		} |  | ||||||
| 		return y1(); |  | ||||||
| 	} |  | ||||||
| 	auto bottom() -> T & |  | ||||||
| 	{ |  | ||||||
| 		if (y0() > y1()) { |  | ||||||
| 			return y0(); |  | ||||||
| 		} |  | ||||||
| 		return y1(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	Box() = default; |  | ||||||
| 	explicit Box(Rect<T> rect) |  | ||||||
| 	{ |  | ||||||
| 		this->m_data[0] = rect.pos; |  | ||||||
| 		this->m_data[1] = rect.pos + rect.size; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| private: |  | ||||||
| 	std::array<Vec2<T>, 2> m_data = {}; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| template<typename T = int> |  | ||||||
| requires std::is_arithmetic_v<T> |  | ||||||
| auto subtract_rect(Math::Rect<T> const &src, Math::Rect<T> const &clip) |  | ||||||
|     -> std::vector<Math::Rect<T>> |  | ||||||
| { |  | ||||||
| 	std::vector<Math::Rect<T>> result; |  | ||||||
|  |  | ||||||
| 	auto sx = src.x(); |  | ||||||
| 	auto sy = src.y(); |  | ||||||
| 	auto sw = src.w(); |  | ||||||
| 	auto sh = src.h(); |  | ||||||
|  |  | ||||||
| 	auto cx = clip.x(); |  | ||||||
| 	auto cy = clip.y(); |  | ||||||
| 	auto cw = clip.w(); |  | ||||||
| 	auto ch = clip.h(); |  | ||||||
|  |  | ||||||
| 	T s_right = sx + sw; |  | ||||||
| 	T s_bottom = sy + sh; |  | ||||||
|  |  | ||||||
| 	T c_right = cx + cw; |  | ||||||
| 	T c_bottom = cy + ch; |  | ||||||
|  |  | ||||||
| 	// No overlap → keep src |  | ||||||
| 	if (c_right <= sx || cx >= s_right || c_bottom <= sy || cy >= s_bottom) { |  | ||||||
| 		result.push_back(src); |  | ||||||
| 		return result; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Top piece |  | ||||||
| 	if (cy > sy) { |  | ||||||
| 		result.emplace_back(sx, sy, sw, cy - sy); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Bottom piece |  | ||||||
| 	if (c_bottom < s_bottom) { |  | ||||||
| 		result.emplace_back(sx, c_bottom, sw, s_bottom - c_bottom); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Middle pieces left and right of clip |  | ||||||
| 	T middle_top = std::max(sy, cy); |  | ||||||
| 	T middle_bottom = std::min(s_bottom, c_bottom); |  | ||||||
| 	T middle_height = middle_bottom - middle_top; |  | ||||||
|  |  | ||||||
| 	if (middle_height > 0) { |  | ||||||
| 		// Left piece |  | ||||||
| 		if (cx > sx) { |  | ||||||
| 			result.emplace_back(sx, middle_top, cx - sx, middle_height); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// Right piece |  | ||||||
| 		if (c_right < s_right) { |  | ||||||
| 			result.emplace_back( |  | ||||||
| 			    c_right, middle_top, s_right - c_right, middle_height); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| template<typename T = float> |  | ||||||
| requires std::is_arithmetic_v<T> struct Viewport { |  | ||||||
| 	Rect<T> rect; |  | ||||||
| 	Vec2<T> depth_limits; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| template<typename T> constexpr auto deg2rad(T degrees) -> T |  | ||||||
| { |  | ||||||
| 	return degrees * (std::numbers::pi / 180.0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| } // namespace LunarWM::Math |  | ||||||
							
								
								
									
										676
									
								
								src/Util.cppm
									
									
									
									
									
								
							
							
						
						
									
										676
									
								
								src/Util.cppm
									
									
									
									
									
								
							| @@ -1,676 +0,0 @@ | |||||||
| module; |  | ||||||
|  |  | ||||||
| // NOLINTBEGIN |  | ||||||
| #include <openxr/openxr.h> |  | ||||||
| // NOLINTEND |  | ||||||
|  |  | ||||||
| export module LunarWM.Util; |  | ||||||
|  |  | ||||||
| import std; |  | ||||||
|  |  | ||||||
| namespace std { |  | ||||||
| template<> struct formatter<XrResult, char> { |  | ||||||
| 	template<class ParseContext> constexpr auto parse(ParseContext &ctx) |  | ||||||
| 	{ |  | ||||||
| 		return ctx.begin(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	static constexpr auto to_string(XrResult r) -> std::string_view |  | ||||||
| 	{ |  | ||||||
| 		switch (r) { |  | ||||||
| 		case XR_FRAME_DISCARDED: |  | ||||||
| 			return "XR_FRAME_DISCARDED"; |  | ||||||
| 		case XR_ERROR_VALIDATION_FAILURE: |  | ||||||
| 			return "XR_ERROR_VALIDATION_FAILURE: The function usage was " |  | ||||||
| 			       "invalid in " |  | ||||||
| 			       "some way."; |  | ||||||
| 		case XR_ERROR_RUNTIME_FAILURE: |  | ||||||
| 			return "XR_ERROR_RUNTIME_FAILURE: The runtime failed to handle the " |  | ||||||
| 			       "function in an unexpected way that is not covered by " |  | ||||||
| 			       "another " |  | ||||||
| 			       "error result."; |  | ||||||
| 		case XR_ERROR_OUT_OF_MEMORY: |  | ||||||
| 			return "XR_ERROR_OUT_OF_MEMORY: A memory allocation has failed."; |  | ||||||
| 		case XR_ERROR_API_VERSION_UNSUPPORTED: |  | ||||||
| 			return "XR_ERROR_API_VERSION_UNSUPPORTED: The runtime does not " |  | ||||||
| 			       "support " |  | ||||||
| 			       "the requested API version."; |  | ||||||
| 		case XR_ERROR_INITIALIZATION_FAILED: |  | ||||||
| 			return "XR_ERROR_INITIALIZATION_FAILED: Initialization of object " |  | ||||||
| 			       "could " |  | ||||||
| 			       "not be completed."; |  | ||||||
| 		case XR_ERROR_FUNCTION_UNSUPPORTED: |  | ||||||
| 			return "XR_ERROR_FUNCTION_UNSUPPORTED: The requested function was " |  | ||||||
| 			       "not " |  | ||||||
| 			       "found or is otherwise unsupported."; |  | ||||||
| 		case XR_ERROR_FEATURE_UNSUPPORTED: |  | ||||||
| 			return "XR_ERROR_FEATURE_UNSUPPORTED: The requested feature is not " |  | ||||||
| 			       "supported."; |  | ||||||
| 		case XR_ERROR_EXTENSION_NOT_PRESENT: |  | ||||||
| 			return "XR_ERROR_EXTENSION_NOT_PRESENT: A requested extension is " |  | ||||||
| 			       "not " |  | ||||||
| 			       "supported."; |  | ||||||
| 		case XR_ERROR_LIMIT_REACHED: |  | ||||||
| 			return "XR_ERROR_LIMIT_REACHED: The runtime supports no more of " |  | ||||||
| 			       "the " |  | ||||||
| 			       "requested resource."; |  | ||||||
| 		case XR_ERROR_SIZE_INSUFFICIENT: |  | ||||||
| 			return "XR_ERROR_SIZE_INSUFFICIENT: The supplied size was smaller " |  | ||||||
| 			       "than " |  | ||||||
| 			       "required."; |  | ||||||
| 		case XR_ERROR_HANDLE_INVALID: |  | ||||||
| 			return "XR_ERROR_HANDLE_INVALID: A supplied object handle was " |  | ||||||
| 			       "invalid."; |  | ||||||
| 		case XR_ERROR_INSTANCE_LOST: |  | ||||||
| 			return "XR_ERROR_INSTANCE_LOST: The XrInstance was lost or could " |  | ||||||
| 			       "not be " |  | ||||||
| 			       "found. It will need to be destroyed and optionally " |  | ||||||
| 			       "recreated."; |  | ||||||
| 		case XR_ERROR_SESSION_RUNNING: |  | ||||||
| 			return "XR_ERROR_SESSION_RUNNING: The session is already running."; |  | ||||||
| 		case XR_ERROR_SESSION_NOT_RUNNING: |  | ||||||
| 			return "XR_ERROR_SESSION_NOT_RUNNING: The session is not yet " |  | ||||||
| 			       "running."; |  | ||||||
| 		case XR_ERROR_SESSION_LOST: |  | ||||||
| 			return "XR_ERROR_SESSION_LOST: The XrSession was lost. It will " |  | ||||||
| 			       "need to " |  | ||||||
| 			       "be destroyed and optionally recreated."; |  | ||||||
| 		case XR_ERROR_SYSTEM_INVALID: |  | ||||||
| 			return "XR_ERROR_SYSTEM_INVALID: The provided XrSystemId was " |  | ||||||
| 			       "invalid."; |  | ||||||
| 		case XR_ERROR_PATH_INVALID: |  | ||||||
| 			return "XR_ERROR_PATH_INVALID: The provided XrPath was not valid."; |  | ||||||
| 		case XR_ERROR_PATH_COUNT_EXCEEDED: |  | ||||||
| 			return "XR_ERROR_PATH_COUNT_EXCEEDED: The maximum number of " |  | ||||||
| 			       "supported " |  | ||||||
| 			       "semantic paths has been reached."; |  | ||||||
| 		case XR_ERROR_PATH_FORMAT_INVALID: |  | ||||||
| 			return "XR_ERROR_PATH_FORMAT_INVALID: The semantic path character " |  | ||||||
| 			       "format " |  | ||||||
| 			       "is invalid."; |  | ||||||
| 		case XR_ERROR_PATH_UNSUPPORTED: |  | ||||||
| 			return "XR_ERROR_PATH_UNSUPPORTED: The semantic path is " |  | ||||||
| 			       "unsupported."; |  | ||||||
| 		case XR_ERROR_LAYER_INVALID: |  | ||||||
| 			return "XR_ERROR_LAYER_INVALID: The layer was NULL or otherwise " |  | ||||||
| 			       "invalid."; |  | ||||||
| 		case XR_ERROR_LAYER_LIMIT_EXCEEDED: |  | ||||||
| 			return "XR_ERROR_LAYER_LIMIT_EXCEEDED: The number of specified " |  | ||||||
| 			       "layers is " |  | ||||||
| 			       "greater than the supported number."; |  | ||||||
| 		case XR_ERROR_SWAPCHAIN_RECT_INVALID: |  | ||||||
| 			return "XR_ERROR_SWAPCHAIN_RECT_INVALID: The image rect was " |  | ||||||
| 			       "negatively " |  | ||||||
| 			       "sized or otherwise invalid."; |  | ||||||
| 		case XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED: |  | ||||||
| 			return "XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED: The image format is " |  | ||||||
| 			       "not " |  | ||||||
| 			       "supported by the runtime or platform."; |  | ||||||
| 		case XR_ERROR_ACTION_TYPE_MISMATCH: |  | ||||||
| 			return "XR_ERROR_ACTION_TYPE_MISMATCH: The API used to retrieve an " |  | ||||||
| 			       "action’s state does not match the action’s type."; |  | ||||||
| 		case XR_ERROR_SESSION_NOT_READY: |  | ||||||
| 			return "XR_ERROR_SESSION_NOT_READY: The session is not in the " |  | ||||||
| 			       "ready " |  | ||||||
| 			       "state."; |  | ||||||
| 		case XR_ERROR_SESSION_NOT_STOPPING: |  | ||||||
| 			return "XR_ERROR_SESSION_NOT_STOPPING: The session is not in the " |  | ||||||
| 			       "stopping state."; |  | ||||||
| 		case XR_ERROR_TIME_INVALID: |  | ||||||
| 			return "XR_ERROR_TIME_INVALID: The provided XrTime was zero, " |  | ||||||
| 			       "negative, " |  | ||||||
| 			       "or out of range."; |  | ||||||
| 		case XR_ERROR_REFERENCE_SPACE_UNSUPPORTED: |  | ||||||
| 			return "XR_ERROR_REFERENCE_SPACE_UNSUPPORTED: The specified " |  | ||||||
| 			       "reference " |  | ||||||
| 			       "space is not supported by the runtime or system."; |  | ||||||
| 		case XR_ERROR_FILE_ACCESS_ERROR: |  | ||||||
| 			return "XR_ERROR_FILE_ACCESS_ERROR: The file could not be " |  | ||||||
| 			       "accessed."; |  | ||||||
| 		case XR_ERROR_FILE_CONTENTS_INVALID: |  | ||||||
| 			return "XR_ERROR_FILE_CONTENTS_INVALID: The file’s contents were " |  | ||||||
| 			       "invalid."; |  | ||||||
| 		case XR_ERROR_FORM_FACTOR_UNSUPPORTED: |  | ||||||
| 			return "XR_ERROR_FORM_FACTOR_UNSUPPORTED: The specified form " |  | ||||||
| 			       "factor is " |  | ||||||
| 			       "not supported by the current runtime or platform."; |  | ||||||
| 		case XR_ERROR_FORM_FACTOR_UNAVAILABLE: |  | ||||||
| 			return "XR_ERROR_FORM_FACTOR_UNAVAILABLE: The specified form " |  | ||||||
| 			       "factor is " |  | ||||||
| 			       "supported, but the device is currently not available, e.g. " |  | ||||||
| 			       "not " |  | ||||||
| 			       "plugged in or powered off."; |  | ||||||
| 		case XR_ERROR_API_LAYER_NOT_PRESENT: |  | ||||||
| 			return "XR_ERROR_API_LAYER_NOT_PRESENT: A requested API layer is " |  | ||||||
| 			       "not " |  | ||||||
| 			       "present or could not be loaded."; |  | ||||||
| 		case XR_ERROR_CALL_ORDER_INVALID: |  | ||||||
| 			return "XR_ERROR_CALL_ORDER_INVALID: The call was made without " |  | ||||||
| 			       "having " |  | ||||||
| 			       "made a previously required call."; |  | ||||||
| 		case XR_ERROR_GRAPHICS_DEVICE_INVALID: |  | ||||||
| 			return "XR_ERROR_GRAPHICS_DEVICE_INVALID: The given graphics " |  | ||||||
| 			       "device is " |  | ||||||
| 			       "not in a valid state. The graphics device could be lost or " |  | ||||||
| 			       "initialized without meeting graphics requirements."; |  | ||||||
| 		case XR_ERROR_POSE_INVALID: |  | ||||||
| 			return "XR_ERROR_POSE_INVALID: The supplied pose was invalid with " |  | ||||||
| 			       "respect to the requirements."; |  | ||||||
| 		case XR_ERROR_INDEX_OUT_OF_RANGE: |  | ||||||
| 			return "XR_ERROR_INDEX_OUT_OF_RANGE: The supplied index was " |  | ||||||
| 			       "outside the " |  | ||||||
| 			       "range of valid indices."; |  | ||||||
| 		case XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED: |  | ||||||
| 			return "XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED: The " |  | ||||||
| 			       "specified view " |  | ||||||
| 			       "configuration type is not supported by the runtime or " |  | ||||||
| 			       "platform."; |  | ||||||
| 		case XR_ERROR_ENVIRONMENT_BLEND_MODE_UNSUPPORTED: |  | ||||||
| 			return "XR_ERROR_ENVIRONMENT_BLEND_MODE_UNSUPPORTED: The specified " |  | ||||||
| 			       "environment blend mode is not supported by the runtime or " |  | ||||||
| 			       "platform."; |  | ||||||
| 		case XR_ERROR_NAME_DUPLICATED: |  | ||||||
| 			return "XR_ERROR_NAME_DUPLICATED: The name provided was a " |  | ||||||
| 			       "duplicate of " |  | ||||||
| 			       "an already-existing resource."; |  | ||||||
| 		case XR_ERROR_NAME_INVALID: |  | ||||||
| 			return "XR_ERROR_NAME_INVALID: The name provided was invalid."; |  | ||||||
| 		case XR_ERROR_ACTIONSET_NOT_ATTACHED: |  | ||||||
| 			return "XR_ERROR_ACTIONSET_NOT_ATTACHED: A referenced action set " |  | ||||||
| 			       "is not " |  | ||||||
| 			       "attached to the session."; |  | ||||||
| 		case XR_ERROR_ACTIONSETS_ALREADY_ATTACHED: |  | ||||||
| 			return "XR_ERROR_ACTIONSETS_ALREADY_ATTACHED: The session already " |  | ||||||
| 			       "has " |  | ||||||
| 			       "attached action sets."; |  | ||||||
| 		case XR_ERROR_LOCALIZED_NAME_DUPLICATED: |  | ||||||
| 			return "XR_ERROR_LOCALIZED_NAME_DUPLICATED: The localized name " |  | ||||||
| 			       "provided " |  | ||||||
| 			       "was a duplicate of an already-existing resource."; |  | ||||||
| 		case XR_ERROR_LOCALIZED_NAME_INVALID: |  | ||||||
| 			return "XR_ERROR_LOCALIZED_NAME_INVALID: The localized name " |  | ||||||
| 			       "provided was " |  | ||||||
| 			       "invalid."; |  | ||||||
| 		case XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING: |  | ||||||
| 			return "XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING: The " |  | ||||||
| 			       "xrGetGraphicsRequirements* call was not made before " |  | ||||||
| 			       "calling " |  | ||||||
| 			       "xrCreateSession."; |  | ||||||
| 		case XR_ERROR_RUNTIME_UNAVAILABLE: |  | ||||||
| 			return "XR_ERROR_RUNTIME_UNAVAILABLE: The loader was unable to " |  | ||||||
| 			       "find or " |  | ||||||
| 			       "load a runtime."; |  | ||||||
| 		case XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED: |  | ||||||
| 			return "XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED: One or more of " |  | ||||||
| 			       "the " |  | ||||||
| 			       "extensions being enabled has dependency on extensions that " |  | ||||||
| 			       "are " |  | ||||||
| 			       "not enabled."; |  | ||||||
| 		case XR_ERROR_PERMISSION_INSUFFICIENT: |  | ||||||
| 			return "XR_ERROR_PERMISSION_INSUFFICIENT: Insufficient " |  | ||||||
| 			       "permissions. This " |  | ||||||
| 			       "error is included for use by vendor extensions. The " |  | ||||||
| 			       "precise " |  | ||||||
| 			       "definition of XR_ERROR_PERMISSION_INSUFFICIENT and actions " |  | ||||||
| 			       "possible by the developer or user to resolve it can vary " |  | ||||||
| 			       "by " |  | ||||||
| 			       "platform, extension or function. The developer should " |  | ||||||
| 			       "refer to " |  | ||||||
| 			       "the documentation of the function that returned the error " |  | ||||||
| 			       "code " |  | ||||||
| 			       "and extension it was defined."; |  | ||||||
| 		case XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR: |  | ||||||
| 			return "XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR: " |  | ||||||
| 			       "xrSetAndroidApplicationThreadKHR failed as thread id is " |  | ||||||
| 			       "invalid. " |  | ||||||
| 			       "(Added by the XR_KHR_android_thread_settings extension)"; |  | ||||||
| 		case XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR: |  | ||||||
| 			return "XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR: " |  | ||||||
| 			       "xrSetAndroidApplicationThreadKHR failed setting the thread " |  | ||||||
| 			       "attributes/priority. (Added by the " |  | ||||||
| 			       "XR_KHR_android_thread_settings extension)"; |  | ||||||
| 		case XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT: |  | ||||||
| 			return "XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT: Spatial anchor " |  | ||||||
| 			       "could " |  | ||||||
| 			       "not be created at that location. (Added by the " |  | ||||||
| 			       "XR_MSFT_spatial_anchor extension)"; |  | ||||||
| 		case XR_ERROR_SECONDARY_VIEW_CONFIGURATION_TYPE_NOT_ENABLED_MSFT: |  | ||||||
| 			return "XR_ERROR_SECONDARY_VIEW_CONFIGURATION_TYPE_NOT_ENABLED_" |  | ||||||
| 			       "MSFT: The " |  | ||||||
| 			       "secondary view configuration was not enabled when creating " |  | ||||||
| 			       "the " |  | ||||||
| 			       "session. (Added by the " |  | ||||||
| 			       "XR_MSFT_secondary_view_configuration " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_CONTROLLER_MODEL_KEY_INVALID_MSFT: |  | ||||||
| 			return "XR_ERROR_CONTROLLER_MODEL_KEY_INVALID_MSFT: The controller " |  | ||||||
| 			       "model " |  | ||||||
| 			       "key is invalid. (Added by the XR_MSFT_controller_model " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_REPROJECTION_MODE_UNSUPPORTED_MSFT: |  | ||||||
| 			return "XR_ERROR_REPROJECTION_MODE_UNSUPPORTED_MSFT: The " |  | ||||||
| 			       "reprojection " |  | ||||||
| 			       "mode is not supported. (Added by the " |  | ||||||
| 			       "XR_MSFT_composition_layer_reprojection extension)"; |  | ||||||
| 		case XR_ERROR_COMPUTE_NEW_SCENE_NOT_COMPLETED_MSFT: |  | ||||||
| 			return "XR_ERROR_COMPUTE_NEW_SCENE_NOT_COMPLETED_MSFT: Compute new " |  | ||||||
| 			       "scene " |  | ||||||
| 			       "not completed. (Added by the XR_MSFT_scene_understanding " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SCENE_COMPONENT_ID_INVALID_MSFT: |  | ||||||
| 			return "XR_ERROR_SCENE_COMPONENT_ID_INVALID_MSFT: Scene component " |  | ||||||
| 			       "id " |  | ||||||
| 			       "invalid. (Added by the XR_MSFT_scene_understanding " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SCENE_COMPONENT_TYPE_MISMATCH_MSFT: |  | ||||||
| 			return "XR_ERROR_SCENE_COMPONENT_TYPE_MISMATCH_MSFT: Scene " |  | ||||||
| 			       "component " |  | ||||||
| 			       "type mismatch. (Added by the XR_MSFT_scene_understanding " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SCENE_MESH_BUFFER_ID_INVALID_MSFT: |  | ||||||
| 			return "XR_ERROR_SCENE_MESH_BUFFER_ID_INVALID_MSFT: Scene mesh " |  | ||||||
| 			       "buffer id " |  | ||||||
| 			       "invalid. (Added by the XR_MSFT_scene_understanding " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SCENE_COMPUTE_FEATURE_INCOMPATIBLE_MSFT: |  | ||||||
| 			return "XR_ERROR_SCENE_COMPUTE_FEATURE_INCOMPATIBLE_MSFT: Scene " |  | ||||||
| 			       "compute " |  | ||||||
| 			       "feature incompatible. (Added by the " |  | ||||||
| 			       "XR_MSFT_scene_understanding " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SCENE_COMPUTE_CONSISTENCY_MISMATCH_MSFT: |  | ||||||
| 			return "XR_ERROR_SCENE_COMPUTE_CONSISTENCY_MISMATCH_MSFT: Scene " |  | ||||||
| 			       "compute " |  | ||||||
| 			       "consistency mismatch. (Added by the " |  | ||||||
| 			       "XR_MSFT_scene_understanding " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB: |  | ||||||
| 			return "XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB: The display " |  | ||||||
| 			       "refresh rate is not supported by the platform. (Added by " |  | ||||||
| 			       "the " |  | ||||||
| 			       "XR_FB_display_refresh_rate extension)"; |  | ||||||
| 		case XR_ERROR_COLOR_SPACE_UNSUPPORTED_FB: |  | ||||||
| 			return "XR_ERROR_COLOR_SPACE_UNSUPPORTED_FB: The color space is " |  | ||||||
| 			       "not " |  | ||||||
| 			       "supported by the runtime. (Added by the XR_FB_color_space " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SPACE_COMPONENT_NOT_SUPPORTED_FB: |  | ||||||
| 			return "XR_ERROR_SPACE_COMPONENT_NOT_SUPPORTED_FB: The component " |  | ||||||
| 			       "type is " |  | ||||||
| 			       "not supported for this space. (Added by the " |  | ||||||
| 			       "XR_FB_spatial_entity " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SPACE_COMPONENT_NOT_ENABLED_FB: |  | ||||||
| 			return "XR_ERROR_SPACE_COMPONENT_NOT_ENABLED_FB: The required " |  | ||||||
| 			       "component " |  | ||||||
| 			       "is not enabled for this space. (Added by the " |  | ||||||
| 			       "XR_FB_spatial_entity extension)"; |  | ||||||
| 		case XR_ERROR_SPACE_COMPONENT_STATUS_PENDING_FB: |  | ||||||
| 			return "XR_ERROR_SPACE_COMPONENT_STATUS_PENDING_FB: A request to " |  | ||||||
| 			       "set the " |  | ||||||
| 			       "component’s status is currently pending. (Added by the " |  | ||||||
| 			       "XR_FB_spatial_entity extension)"; |  | ||||||
| 		case XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB: |  | ||||||
| 			return "XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB: The " |  | ||||||
| 			       "component is " |  | ||||||
| 			       "already set to the requested value. (Added by the " |  | ||||||
| 			       "XR_FB_spatial_entity extension)"; |  | ||||||
| 		case XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB: |  | ||||||
| 			return "XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB: The object state " |  | ||||||
| 			       "is " |  | ||||||
| 			       "unexpected for the issued command. (Added by the " |  | ||||||
| 			       "XR_FB_passthrough extension)"; |  | ||||||
| 		case XR_ERROR_FEATURE_ALREADY_CREATED_PASSTHROUGH_FB: |  | ||||||
| 			return "XR_ERROR_FEATURE_ALREADY_CREATED_PASSTHROUGH_FB: Trying to " |  | ||||||
| 			       "create an MR feature when one was already created and only " |  | ||||||
| 			       "one " |  | ||||||
| 			       "instance is allowed. (Added by the XR_FB_passthrough " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_FEATURE_REQUIRED_PASSTHROUGH_FB: |  | ||||||
| 			return "XR_ERROR_FEATURE_REQUIRED_PASSTHROUGH_FB: Requested " |  | ||||||
| 			       "functionality requires a feature to be created first. " |  | ||||||
| 			       "(Added by " |  | ||||||
| 			       "the XR_FB_passthrough extension)"; |  | ||||||
| 		case XR_ERROR_NOT_PERMITTED_PASSTHROUGH_FB: |  | ||||||
| 			return "XR_ERROR_NOT_PERMITTED_PASSTHROUGH_FB: Requested " |  | ||||||
| 			       "functionality " |  | ||||||
| 			       "is not permitted - application is not allowed to perform " |  | ||||||
| 			       "the " |  | ||||||
| 			       "requested operation. (Added by the XR_FB_passthrough " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_INSUFFICIENT_RESOURCES_PASSTHROUGH_FB: |  | ||||||
| 			return "XR_ERROR_INSUFFICIENT_RESOURCES_PASSTHROUGH_FB: There were " |  | ||||||
| 			       "insufficient resources available to perform an operation. " |  | ||||||
| 			       "(Added " |  | ||||||
| 			       "by the XR_FB_passthrough extension)"; |  | ||||||
| 		case XR_ERROR_UNKNOWN_PASSTHROUGH_FB: |  | ||||||
| 			return "XR_ERROR_UNKNOWN_PASSTHROUGH_FB: Unknown Passthrough error " |  | ||||||
| 			       "(no " |  | ||||||
| 			       "further details provided). (Added by the XR_FB_passthrough " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_RENDER_MODEL_KEY_INVALID_FB: |  | ||||||
| 			return "XR_ERROR_RENDER_MODEL_KEY_INVALID_FB: The model key is " |  | ||||||
| 			       "invalid. " |  | ||||||
| 			       "(Added by the XR_FB_render_model extension)"; |  | ||||||
| 		case XR_ERROR_MARKER_NOT_TRACKED_VARJO: |  | ||||||
| 			return "XR_ERROR_MARKER_NOT_TRACKED_VARJO: Marker tracking is " |  | ||||||
| 			       "disabled " |  | ||||||
| 			       "or the specified marker is not currently tracked. (Added " |  | ||||||
| 			       "by the " |  | ||||||
| 			       "XR_VARJO_marker_tracking extension)"; |  | ||||||
| 		case XR_ERROR_MARKER_ID_INVALID_VARJO: |  | ||||||
| 			return "XR_ERROR_MARKER_ID_INVALID_VARJO: The specified marker ID " |  | ||||||
| 			       "is not " |  | ||||||
| 			       "valid. (Added by the XR_VARJO_marker_tracking extension)"; |  | ||||||
| 		case XR_ERROR_MARKER_DETECTOR_PERMISSION_DENIED_ML: |  | ||||||
| 			return "XR_ERROR_MARKER_DETECTOR_PERMISSION_DENIED_ML: The " |  | ||||||
| 			       "com.magicleap.permission.MARKER_TRACKING permission was " |  | ||||||
| 			       "denied. " |  | ||||||
| 			       "(Added by the XR_ML_marker_understanding extension)"; |  | ||||||
| 		case XR_ERROR_MARKER_DETECTOR_LOCATE_FAILED_ML: |  | ||||||
| 			return "XR_ERROR_MARKER_DETECTOR_LOCATE_FAILED_ML: The specified " |  | ||||||
| 			       "marker " |  | ||||||
| 			       "could not be located spatially. (Added by the " |  | ||||||
| 			       "XR_ML_marker_understanding extension)"; |  | ||||||
| 		case XR_ERROR_MARKER_DETECTOR_INVALID_DATA_QUERY_ML: |  | ||||||
| 			return "XR_ERROR_MARKER_DETECTOR_INVALID_DATA_QUERY_ML: The marker " |  | ||||||
| 			       "queried does not contain data of the requested type. " |  | ||||||
| 			       "(Added by " |  | ||||||
| 			       "the XR_ML_marker_understanding extension)"; |  | ||||||
| 		case XR_ERROR_MARKER_DETECTOR_INVALID_CREATE_INFO_ML: |  | ||||||
| 			return "XR_ERROR_MARKER_DETECTOR_INVALID_CREATE_INFO_ML: " |  | ||||||
| 			       "createInfo " |  | ||||||
| 			       "contains mutually exclusive parameters, such as setting " |  | ||||||
| 			       "XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_APRIL_TAG_ML with " |  | ||||||
| 			       "XR_MARKER_TYPE_ARUCO_ML. (Added by the " |  | ||||||
| 			       "XR_ML_marker_understanding extension)"; |  | ||||||
| 		case XR_ERROR_MARKER_INVALID_ML: |  | ||||||
| 			return "XR_ERROR_MARKER_INVALID_ML: The marker id passed to the " |  | ||||||
| 			       "function " |  | ||||||
| 			       "was invalid. (Added by the XR_ML_marker_understanding " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_LOCALIZATION_MAP_INCOMPATIBLE_ML: |  | ||||||
| 			return "XR_ERROR_LOCALIZATION_MAP_INCOMPATIBLE_ML: The " |  | ||||||
| 			       "localization map " |  | ||||||
| 			       "being imported is not compatible with current OS or mode. " |  | ||||||
| 			       "(Added " |  | ||||||
| 			       "by the XR_ML_localization_map extension)"; |  | ||||||
| 		case XR_ERROR_LOCALIZATION_MAP_UNAVAILABLE_ML: |  | ||||||
| 			return "XR_ERROR_LOCALIZATION_MAP_UNAVAILABLE_ML: The localization " |  | ||||||
| 			       "map " |  | ||||||
| 			       "requested is not available. (Added by the " |  | ||||||
| 			       "XR_ML_localization_map " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_LOCALIZATION_MAP_FAIL_ML: |  | ||||||
| 			return "XR_ERROR_LOCALIZATION_MAP_FAIL_ML: The map localization " |  | ||||||
| 			       "service " |  | ||||||
| 			       "failed to fulfill the request, retry later. (Added by the " |  | ||||||
| 			       "XR_ML_localization_map extension)"; |  | ||||||
| 		case XR_ERROR_LOCALIZATION_MAP_IMPORT_EXPORT_PERMISSION_DENIED_ML: |  | ||||||
| 			return "XR_ERROR_LOCALIZATION_MAP_IMPORT_EXPORT_PERMISSION_DENIED_" |  | ||||||
| 			       "ML: " |  | ||||||
| 			       "The com.magicleap.permission.SPACE_IMPORT_EXPORT " |  | ||||||
| 			       "permission was " |  | ||||||
| 			       "denied. (Added by the XR_ML_localization_map extension)"; |  | ||||||
| 		case XR_ERROR_LOCALIZATION_MAP_PERMISSION_DENIED_ML: |  | ||||||
| 			return "XR_ERROR_LOCALIZATION_MAP_PERMISSION_DENIED_ML: The " |  | ||||||
| 			       "com.magicleap.permission.SPACE_MANAGER permission was " |  | ||||||
| 			       "denied. " |  | ||||||
| 			       "(Added by the XR_ML_localization_map extension)"; |  | ||||||
| 		case XR_ERROR_LOCALIZATION_MAP_ALREADY_EXISTS_ML: |  | ||||||
| 			return "XR_ERROR_LOCALIZATION_MAP_ALREADY_EXISTS_ML: The map being " |  | ||||||
| 			       "imported already exists in the system. (Added by the " |  | ||||||
| 			       "XR_ML_localization_map extension)"; |  | ||||||
| 		case XR_ERROR_LOCALIZATION_MAP_CANNOT_EXPORT_CLOUD_MAP_ML: |  | ||||||
| 			return "XR_ERROR_LOCALIZATION_MAP_CANNOT_EXPORT_CLOUD_MAP_ML: The " |  | ||||||
| 			       "map " |  | ||||||
| 			       "localization service cannot export cloud based maps. " |  | ||||||
| 			       "(Added by " |  | ||||||
| 			       "the XR_ML_localization_map extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_ANCHORS_PERMISSION_DENIED_ML: |  | ||||||
| 			return "XR_ERROR_SPATIAL_ANCHORS_PERMISSION_DENIED_ML: The " |  | ||||||
| 			       "com.magicleap.permission.SPATIAL_ANCHOR permission was not " |  | ||||||
| 			       "granted. (Added by the XR_ML_spatial_anchors extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_ANCHORS_NOT_LOCALIZED_ML: |  | ||||||
| 			return "XR_ERROR_SPATIAL_ANCHORS_NOT_LOCALIZED_ML: Operation " |  | ||||||
| 			       "failed " |  | ||||||
| 			       "because the system is not localized into a localization " |  | ||||||
| 			       "map. " |  | ||||||
| 			       "(Added by the XR_ML_spatial_anchors extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_ANCHORS_OUT_OF_MAP_BOUNDS_ML: |  | ||||||
| 			return "XR_ERROR_SPATIAL_ANCHORS_OUT_OF_MAP_BOUNDS_ML: Operation " |  | ||||||
| 			       "failed " |  | ||||||
| 			       "because it is performed outside of the localization map. " |  | ||||||
| 			       "(Added " |  | ||||||
| 			       "by the XR_ML_spatial_anchors extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_ANCHORS_SPACE_NOT_LOCATABLE_ML: |  | ||||||
| 			return "XR_ERROR_SPATIAL_ANCHORS_SPACE_NOT_LOCATABLE_ML: Operation " |  | ||||||
| 			       "failed because the space referenced cannot be located. " |  | ||||||
| 			       "(Added by " |  | ||||||
| 			       "the XR_ML_spatial_anchors extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_ANCHORS_ANCHOR_NOT_FOUND_ML: |  | ||||||
| 			return "XR_ERROR_SPATIAL_ANCHORS_ANCHOR_NOT_FOUND_ML: The anchor " |  | ||||||
| 			       "references was not found. (Added by the " |  | ||||||
| 			       "XR_ML_spatial_anchors_storage extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT: |  | ||||||
| 			return "XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT: A spatial " |  | ||||||
| 			       "anchor " |  | ||||||
| 			       "was not found associated with the spatial anchor name " |  | ||||||
| 			       "provided " |  | ||||||
| 			       "(Added by the XR_MSFT_spatial_anchor_persistence " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT: |  | ||||||
| 			return "XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT: The spatial " |  | ||||||
| 			       "anchor " |  | ||||||
| 			       "name provided was not valid (Added by the " |  | ||||||
| 			       "XR_MSFT_spatial_anchor_persistence extension)"; |  | ||||||
| 		case XR_ERROR_SPACE_MAPPING_INSUFFICIENT_FB: |  | ||||||
| 			return "XR_ERROR_SPACE_MAPPING_INSUFFICIENT_FB: Anchor import from " |  | ||||||
| 			       "cloud " |  | ||||||
| 			       "or export from device failed. (Added by the " |  | ||||||
| 			       "XR_FB_spatial_entity_sharing extension)"; |  | ||||||
| 		case XR_ERROR_SPACE_LOCALIZATION_FAILED_FB: |  | ||||||
| 			return "XR_ERROR_SPACE_LOCALIZATION_FAILED_FB: Anchors were " |  | ||||||
| 			       "downloaded " |  | ||||||
| 			       "from the cloud but failed to be imported/aligned on the " |  | ||||||
| 			       "device. " |  | ||||||
| 			       "(Added by the XR_FB_spatial_entity_sharing extension)"; |  | ||||||
| 		case XR_ERROR_SPACE_NETWORK_TIMEOUT_FB: |  | ||||||
| 			return "XR_ERROR_SPACE_NETWORK_TIMEOUT_FB: Timeout occurred while " |  | ||||||
| 			       "waiting for network request to complete. (Added by the " |  | ||||||
| 			       "XR_FB_spatial_entity_sharing extension)"; |  | ||||||
| 		case XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB: |  | ||||||
| 			return "XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB: The network " |  | ||||||
| 			       "request " |  | ||||||
| 			       "failed. (Added by the XR_FB_spatial_entity_sharing " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB: |  | ||||||
| 			return "XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB: Cloud storage is " |  | ||||||
| 			       "required for this operation but is currently disabled. " |  | ||||||
| 			       "(Added by " |  | ||||||
| 			       "the XR_FB_spatial_entity_sharing extension)"; |  | ||||||
| 		case XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META: |  | ||||||
| 			return "XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META: " |  | ||||||
| 			       "The " |  | ||||||
| 			       "provided data buffer did not match the required size. " |  | ||||||
| 			       "(Added by " |  | ||||||
| 			       "the XR_META_passthrough_color_lut extension)"; |  | ||||||
| 		case XR_ERROR_RENDER_MODEL_ID_INVALID_EXT: |  | ||||||
| 			return "XR_ERROR_RENDER_MODEL_ID_INVALID_EXT: The render model ID " |  | ||||||
| 			       "is " |  | ||||||
| 			       "invalid. (Added by the XR_EXT_render_model extension)"; |  | ||||||
| 		case XR_ERROR_RENDER_MODEL_ASSET_UNAVAILABLE_EXT: |  | ||||||
| 			return "XR_ERROR_RENDER_MODEL_ASSET_UNAVAILABLE_EXT: The render " |  | ||||||
| 			       "model " |  | ||||||
| 			       "asset is unavailable. (Added by the XR_EXT_render_model " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_RENDER_MODEL_GLTF_EXTENSION_REQUIRED_EXT: |  | ||||||
| 			return "XR_ERROR_RENDER_MODEL_GLTF_EXTENSION_REQUIRED_EXT: A glTF " |  | ||||||
| 			       "extension is required. (Added by the XR_EXT_render_model " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_NOT_INTERACTION_RENDER_MODEL_EXT: |  | ||||||
| 			return "XR_ERROR_NOT_INTERACTION_RENDER_MODEL_EXT: The provided " |  | ||||||
| 			       "XrRenderModelEXT was not created from a XrRenderModelIdEXT " |  | ||||||
| 			       "from " |  | ||||||
| 			       "[XR_EXT_interaction_render_model] (Added by the " |  | ||||||
| 			       "XR_EXT_interaction_render_model extension)"; |  | ||||||
| 		case XR_ERROR_HINT_ALREADY_SET_QCOM: |  | ||||||
| 			return "XR_ERROR_HINT_ALREADY_SET_QCOM: Tracking optimization hint " |  | ||||||
| 			       "is " |  | ||||||
| 			       "already set for the domain. (Added by the " |  | ||||||
| 			       "XR_QCOM_tracking_optimization_settings extension)"; |  | ||||||
| 		case XR_ERROR_NOT_AN_ANCHOR_HTC: |  | ||||||
| 			return "XR_ERROR_NOT_AN_ANCHOR_HTC: The provided space is valid " |  | ||||||
| 			       "but not " |  | ||||||
| 			       "an anchor. (Added by the XR_HTC_anchor extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_ENTITY_ID_INVALID_BD: |  | ||||||
| 			return "XR_ERROR_SPATIAL_ENTITY_ID_INVALID_BD: The spatial entity " |  | ||||||
| 			       "id is " |  | ||||||
| 			       "invalid. (Added by the XR_BD_spatial_sensing extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_BD: |  | ||||||
| 			return "XR_ERROR_SPATIAL_SENSING_SERVICE_UNAVAILABLE_BD: The " |  | ||||||
| 			       "spatial " |  | ||||||
| 			       "sensing service is unavailable. (Added by the " |  | ||||||
| 			       "XR_BD_spatial_sensing extension)"; |  | ||||||
| 		case XR_ERROR_ANCHOR_NOT_SUPPORTED_FOR_ENTITY_BD: |  | ||||||
| 			return "XR_ERROR_ANCHOR_NOT_SUPPORTED_FOR_ENTITY_BD: The spatial " |  | ||||||
| 			       "entity " |  | ||||||
| 			       "does not support anchor. (Added by the " |  | ||||||
| 			       "XR_BD_spatial_sensing " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SCENE_CAPTURE_FAILURE_BD: |  | ||||||
| 			return "XR_ERROR_SCENE_CAPTURE_FAILURE_BD: The scene capture is " |  | ||||||
| 			       "failed, " |  | ||||||
| 			       "for example exiting abnormally. (Added by the " |  | ||||||
| 			       "XR_BD_spatial_scene extension)"; |  | ||||||
| 		case XR_ERROR_SPACE_NOT_LOCATABLE_EXT: |  | ||||||
| 			return "XR_ERROR_SPACE_NOT_LOCATABLE_EXT: The space passed to the " |  | ||||||
| 			       "function was not locatable. (Added by the " |  | ||||||
| 			       "XR_EXT_plane_detection " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT: |  | ||||||
| 			return "XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT: The " |  | ||||||
| 			       "permission " |  | ||||||
| 			       "for this resource was not granted. (Added by the " |  | ||||||
| 			       "XR_EXT_plane_detection extension)"; |  | ||||||
| 		case XR_ERROR_FUTURE_PENDING_EXT: |  | ||||||
| 			return "XR_ERROR_FUTURE_PENDING_EXT: Returned by completion " |  | ||||||
| 			       "function to " |  | ||||||
| 			       "indicate future is not ready. (Added by the XR_EXT_future " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_FUTURE_INVALID_EXT: |  | ||||||
| 			return "XR_ERROR_FUTURE_INVALID_EXT: Returned by completion " |  | ||||||
| 			       "function to " |  | ||||||
| 			       "indicate future is not valid. (Added by the XR_EXT_future " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SYSTEM_NOTIFICATION_PERMISSION_DENIED_ML: |  | ||||||
| 			return "XR_ERROR_SYSTEM_NOTIFICATION_PERMISSION_DENIED_ML: The " |  | ||||||
| 			       "com.magicleap.permission.SYSTEM_NOTIFICATION permission " |  | ||||||
| 			       "was not " |  | ||||||
| 			       "granted. (Added by the XR_ML_system_notifications " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SYSTEM_NOTIFICATION_INCOMPATIBLE_SKU_ML: |  | ||||||
| 			return "XR_ERROR_SYSTEM_NOTIFICATION_INCOMPATIBLE_SKU_ML: " |  | ||||||
| 			       "Incompatible " |  | ||||||
| 			       "SKU detected. (Added by the XR_ML_system_notifications " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_WORLD_MESH_DETECTOR_PERMISSION_DENIED_ML: |  | ||||||
| 			return "XR_ERROR_WORLD_MESH_DETECTOR_PERMISSION_DENIED_ML: The " |  | ||||||
| 			       "world " |  | ||||||
| 			       "mesh detector permission was not granted. (Added by the " |  | ||||||
| 			       "XR_ML_world_mesh_detection extension)"; |  | ||||||
| 		case XR_ERROR_WORLD_MESH_DETECTOR_SPACE_NOT_LOCATABLE_ML: |  | ||||||
| 			return "XR_ERROR_WORLD_MESH_DETECTOR_SPACE_NOT_LOCATABLE_ML: At " |  | ||||||
| 			       "the time " |  | ||||||
| 			       "of the call the runtime was unable to locate the space and " |  | ||||||
| 			       "cannot fulfill your request. (Added by the " |  | ||||||
| 			       "XR_ML_world_mesh_detection extension)"; |  | ||||||
| 		case XR_ERROR_COLOCATION_DISCOVERY_NETWORK_FAILED_META: |  | ||||||
| 			return "XR_ERROR_COLOCATION_DISCOVERY_NETWORK_FAILED_META: The " |  | ||||||
| 			       "network " |  | ||||||
| 			       "request failed. (Added by the XR_META_colocation_discovery " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_COLOCATION_DISCOVERY_NO_DISCOVERY_METHOD_META: |  | ||||||
| 			return "XR_ERROR_COLOCATION_DISCOVERY_NO_DISCOVERY_METHOD_META: " |  | ||||||
| 			       "The " |  | ||||||
| 			       "runtime does not have any methods available to perform " |  | ||||||
| 			       "discovery. (Added by the XR_META_colocation_discovery " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SPACE_GROUP_NOT_FOUND_META: |  | ||||||
| 			return "XR_ERROR_SPACE_GROUP_NOT_FOUND_META: The group UUID was " |  | ||||||
| 			       "not " |  | ||||||
| 			       "found within the runtime (Added by the " |  | ||||||
| 			       "XR_META_spatial_entity_group_sharing extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_CAPABILITY_UNSUPPORTED_EXT: |  | ||||||
| 			return "XR_ERROR_SPATIAL_CAPABILITY_UNSUPPORTED_EXT: The specified " |  | ||||||
| 			       "spatial capability is not supported by the runtime or the " |  | ||||||
| 			       "system. (Added by the XR_EXT_spatial_entity extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT: |  | ||||||
| 			return "XR_ERROR_SPATIAL_ENTITY_ID_INVALID_EXT: The specified " |  | ||||||
| 			       "spatial " |  | ||||||
| 			       "entity id is invalid or an entity with that id does not " |  | ||||||
| 			       "exist in " |  | ||||||
| 			       "the environment. (Added by the XR_EXT_spatial_entity " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_BUFFER_ID_INVALID_EXT: |  | ||||||
| 			return "XR_ERROR_SPATIAL_BUFFER_ID_INVALID_EXT: The specified " |  | ||||||
| 			       "spatial " |  | ||||||
| 			       "buffer id is invalid or does not exist in the spatial " |  | ||||||
| 			       "snapshot " |  | ||||||
| 			       "being used to query for the buffer data. (Added by the " |  | ||||||
| 			       "XR_EXT_spatial_entity extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_COMPONENT_UNSUPPORTED_FOR_CAPABILITY_EXT: |  | ||||||
| 			return "XR_ERROR_SPATIAL_COMPONENT_UNSUPPORTED_FOR_CAPABILITY_EXT: " |  | ||||||
| 			       "The " |  | ||||||
| 			       "specified spatial component is not supported by the " |  | ||||||
| 			       "runtime or " |  | ||||||
| 			       "the system for the given capability. (Added by the " |  | ||||||
| 			       "XR_EXT_spatial_entity extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_CAPABILITY_CONFIGURATION_INVALID_EXT: |  | ||||||
| 			return "XR_ERROR_SPATIAL_CAPABILITY_CONFIGURATION_INVALID_EXT: The " |  | ||||||
| 			       "specified spatial capability configuration is invalid. " |  | ||||||
| 			       "(Added by " |  | ||||||
| 			       "the XR_EXT_spatial_entity extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_COMPONENT_NOT_ENABLED_EXT: |  | ||||||
| 			return "XR_ERROR_SPATIAL_COMPONENT_NOT_ENABLED_EXT: The specified " |  | ||||||
| 			       "spatial component is not enabled for the spatial context. " |  | ||||||
| 			       "(Added " |  | ||||||
| 			       "by the XR_EXT_spatial_entity extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_UNSUPPORTED_EXT: |  | ||||||
| 			return "XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_UNSUPPORTED_EXT: The " |  | ||||||
| 			       "specified spatial persistence scope is not supported by " |  | ||||||
| 			       "the " |  | ||||||
| 			       "runtime or the system. (Added by the " |  | ||||||
| 			       "XR_EXT_spatial_persistence " |  | ||||||
| 			       "extension)"; |  | ||||||
| 		case XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_INCOMPATIBLE_EXT: |  | ||||||
| 			return "XR_ERROR_SPATIAL_PERSISTENCE_SCOPE_INCOMPATIBLE_EXT: THe " |  | ||||||
| 			       "scope " |  | ||||||
| 			       "configured for the persistence context is incompatible for " |  | ||||||
| 			       "the " |  | ||||||
| 			       "current spatial entity. (Added by the " |  | ||||||
| 			       "XR_EXT_spatial_persistence_operations extension)"; |  | ||||||
| 		default: |  | ||||||
| 			return ""; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	template<class FmtCtx> auto format(XrResult r, FmtCtx &ctx) const |  | ||||||
| 	{ |  | ||||||
| 		if (spec == 'd') { |  | ||||||
| 			return format_to(ctx.out(), "{}", static_cast<int32_t>(r)); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		std::string_view str = std::formatter<XrResult, char>::to_string(r); |  | ||||||
| 		if (!str.empty()) { |  | ||||||
| 			return format_to(ctx.out(), "{}", str); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return format_to(ctx.out(), "<0x{:x}>", static_cast<uint32_t>(r)); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| private: |  | ||||||
| 	char spec { 's' }; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| } // namespace std |  | ||||||
							
								
								
									
										6
									
								
								src/common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/common.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | #ifndef COMMON_H | ||||||
|  | #define COMMON_H | ||||||
|  |  | ||||||
|  | #define ARRAY_SZ(arr) (sizeof(arr) / sizeof(*arr)) | ||||||
|  |  | ||||||
|  | #endif // COMMON_H | ||||||
| @@ -1,705 +0,0 @@ | |||||||
| #include "dhos_config.h" |  | ||||||
|  |  | ||||||
| // NOLINTBEGIN |  | ||||||
|  |  | ||||||
| #include <algorithm> |  | ||||||
| #include <cassert> |  | ||||||
| #include <cctype> |  | ||||||
| #include <charconv> |  | ||||||
| #include <format> |  | ||||||
| #include <fstream> |  | ||||||
| #include <print> |  | ||||||
| #include <sstream> |  | ||||||
|  |  | ||||||
| namespace dhos { |  | ||||||
|  |  | ||||||
| // environment shared across parser and evaluation |  | ||||||
| struct Environment { |  | ||||||
| 	Environment(Environment *p = nullptr) |  | ||||||
| 	    : parent { p } |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 	std::unordered_map<std::string, Value> tbl; |  | ||||||
| 	Environment *parent; |  | ||||||
| 	bool contains(std::string const &k) const |  | ||||||
| 	{ |  | ||||||
| 		return tbl.contains(k) || (parent && parent->contains(k)); |  | ||||||
| 	} |  | ||||||
| 	Value &operator[](std::string const &k) |  | ||||||
| 	{ |  | ||||||
| 		if (tbl.contains(k)) |  | ||||||
| 			return tbl.at(k); |  | ||||||
| 		if (parent) |  | ||||||
| 			return (*parent)[k]; |  | ||||||
| 		throw std::runtime_error(std::format("Undefined variable: {}", k)); |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| namespace { |  | ||||||
| // env, lexer, parser |  | ||||||
|  |  | ||||||
| enum class Tok { |  | ||||||
| 	End, |  | ||||||
| 	Id, |  | ||||||
| 	Num, |  | ||||||
| 	Str, |  | ||||||
| 	Path, |  | ||||||
| 	LPar, |  | ||||||
| 	RPar, |  | ||||||
| 	LBr, |  | ||||||
| 	RBr, |  | ||||||
| 	LBrk, |  | ||||||
| 	RBrk, |  | ||||||
| 	Eq, |  | ||||||
| 	Dot, |  | ||||||
| 	Comma |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| std::string Tok_str(Tok t) |  | ||||||
| { |  | ||||||
| 	if (t == Tok::End) |  | ||||||
| 		return "End"; |  | ||||||
| 	else if (t == Tok::Id) |  | ||||||
| 		return "Id"; |  | ||||||
| 	else if (t == Tok::Num) |  | ||||||
| 		return "Num"; |  | ||||||
| 	else if (t == Tok::Str) |  | ||||||
| 		return "Str"; |  | ||||||
| 	else if (t == Tok::Path) |  | ||||||
| 		return "Path"; |  | ||||||
| 	else if (t == Tok::LPar) |  | ||||||
| 		return "LPar"; |  | ||||||
| 	else if (t == Tok::RPar) |  | ||||||
| 		return "RPar"; |  | ||||||
| 	else if (t == Tok::LBr) |  | ||||||
| 		return "LBr"; |  | ||||||
| 	else if (t == Tok::RBr) |  | ||||||
| 		return "RBr"; |  | ||||||
| 	else if (t == Tok::LBrk) |  | ||||||
| 		return "LBrk"; |  | ||||||
| 	else if (t == Tok::RBrk) |  | ||||||
| 		return "RBrk"; |  | ||||||
| 	else if (t == Tok::Eq) |  | ||||||
| 		return "Eq"; |  | ||||||
| 	else if (t == Tok::Dot) |  | ||||||
| 		return "Dot"; |  | ||||||
| 	else if (t == Tok::Comma) |  | ||||||
| 		return "Comma"; |  | ||||||
| 	else |  | ||||||
| 		return "?"; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct T { |  | ||||||
| 	Tok k; |  | ||||||
| 	std::string t; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| class Lex { |  | ||||||
| public: |  | ||||||
| 	explicit Lex(std::string_view s) |  | ||||||
| 	    : src { s } |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 	T next(); |  | ||||||
|  |  | ||||||
| private: |  | ||||||
| 	T id(); |  | ||||||
| 	T num(); |  | ||||||
| 	T str(); |  | ||||||
| 	T path(); |  | ||||||
| 	void skip(); |  | ||||||
| 	std::string_view src; |  | ||||||
| 	std::size_t pos {}; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| T Lex::id() |  | ||||||
| { |  | ||||||
| 	std::size_t s = pos; |  | ||||||
| 	while (pos < src.size() |  | ||||||
| 	    && (std::isalnum((unsigned char)src[pos]) || src[pos] == '_')) |  | ||||||
| 		++pos; |  | ||||||
| 	return { Tok::Id, std::string { src.substr(s, pos - s) } }; |  | ||||||
| } |  | ||||||
| T Lex::num() |  | ||||||
| { |  | ||||||
| 	std::size_t s = pos; |  | ||||||
| 	while (pos < src.size() |  | ||||||
| 	    && (std::isdigit((unsigned char)src[pos]) || src[pos] == '_')) |  | ||||||
| 		++pos; |  | ||||||
| 	if (pos < src.size() && src[pos] == '.') { |  | ||||||
| 		++pos; |  | ||||||
| 		while (pos < src.size() |  | ||||||
| 		    && (std::isdigit((unsigned char)src[pos]) || src[pos] == '_')) |  | ||||||
| 			++pos; |  | ||||||
| 	} |  | ||||||
| 	return { Tok::Num, std::string { src.substr(s, pos - s) } }; |  | ||||||
| } |  | ||||||
| T Lex::str() |  | ||||||
| { |  | ||||||
| 	char q = src[pos++]; |  | ||||||
| 	std::string o; |  | ||||||
| 	while (pos < src.size() && src[pos] != q) |  | ||||||
| 		o.push_back(src[pos++]); |  | ||||||
| 	if (pos >= src.size()) |  | ||||||
| 		throw std::runtime_error("Unterminated string literal"); |  | ||||||
| 	++pos; |  | ||||||
| 	return { Tok::Str, o }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static bool is_path_ch(char c) |  | ||||||
| { |  | ||||||
| 	return std::isalnum((unsigned char)c) || c == '_' || c == '.' || c == '/' |  | ||||||
| 	    || c == '-' || c == ':'; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| T Lex::path() |  | ||||||
| { |  | ||||||
| 	std::string out; |  | ||||||
| 	while (pos < src.size()) { |  | ||||||
| 		char c = src[pos]; |  | ||||||
| 		if (c == '\\') { |  | ||||||
| 			++pos; |  | ||||||
| 			if (pos < src.size()) { |  | ||||||
| 				out.push_back(src[pos]); |  | ||||||
| 				++pos; |  | ||||||
| 			} else { |  | ||||||
| 				out.push_back('\\'); |  | ||||||
| 			} |  | ||||||
| 		} else if (is_path_ch(c)) { |  | ||||||
| 			out.push_back(c); |  | ||||||
| 			++pos; |  | ||||||
| 		} else { |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return { Tok::Path, out }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void Lex::skip() |  | ||||||
| { |  | ||||||
| 	while (pos < src.size()) { |  | ||||||
| 		if (std::isspace((unsigned char)src[pos])) { |  | ||||||
| 			++pos; |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
| 		if (src[pos] == '#') { |  | ||||||
| 			while (pos < src.size() && src[pos] != '\n') |  | ||||||
| 				++pos; |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| T Lex::next() |  | ||||||
| { |  | ||||||
| 	skip(); |  | ||||||
| 	if (pos >= src.size()) |  | ||||||
| 		return { Tok::End, "" }; |  | ||||||
| 	char c = src[pos]; |  | ||||||
|  |  | ||||||
| 	if (c == '/'                                                     // "/foo" |  | ||||||
| 	    || (c == '.' && pos + 1 < src.size() && src[pos + 1] == '/') // "./foo" |  | ||||||
| 	    || (c == '.' && pos + 2 < src.size() && src[pos + 1] == '.' |  | ||||||
| 	        && src[pos + 2] == '/') // "../foo" |  | ||||||
| 	    || (std::isalpha((unsigned char)c) && pos + 1 < src.size() |  | ||||||
| 	        && src[pos + 1] == ':')) // "C:/foo" |  | ||||||
| 		return path(); |  | ||||||
|  |  | ||||||
| 	switch (c) { |  | ||||||
| 	case '(': |  | ||||||
| 		++pos; |  | ||||||
| 		return { Tok::LPar, "(" }; |  | ||||||
| 	case ')': |  | ||||||
| 		++pos; |  | ||||||
| 		return { Tok::RPar, ")" }; |  | ||||||
| 	case '{': |  | ||||||
| 		++pos; |  | ||||||
| 		return { Tok::LBr, "{" }; |  | ||||||
| 	case '}': |  | ||||||
| 		++pos; |  | ||||||
| 		return { Tok::RBr, "}" }; |  | ||||||
| 	case '[': |  | ||||||
| 		++pos; |  | ||||||
| 		return { Tok::LBrk, "[" }; |  | ||||||
| 	case ']': |  | ||||||
| 		++pos; |  | ||||||
| 		return { Tok::RBrk, "]" }; |  | ||||||
| 	case '=': |  | ||||||
| 		++pos; |  | ||||||
| 		return { Tok::Eq, "=" }; |  | ||||||
| 	case '.': |  | ||||||
| 		++pos; |  | ||||||
| 		return { Tok::Dot, "." }; |  | ||||||
| 	case ',': |  | ||||||
| 		++pos; |  | ||||||
| 		return { Tok::Comma, "," }; |  | ||||||
| 	case '"': |  | ||||||
| 	case '\'': |  | ||||||
| 		return str(); |  | ||||||
| 	case 0: |  | ||||||
| 		return { Tok::End, "" }; |  | ||||||
| 	default: |  | ||||||
| 		if (std::isdigit((unsigned char)c)) |  | ||||||
| 			return num(); |  | ||||||
| 		if (std::isalpha((unsigned char)c) || c == '_') |  | ||||||
| 			return id(); |  | ||||||
| 		std::println("Invalid character: {} ({:d})", c, c); |  | ||||||
| 		throw std::runtime_error(std::format("Invalid character: {}", c)); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // parser |  | ||||||
| class Parser { |  | ||||||
| public: |  | ||||||
| 	Parser(std::string_view s, std::shared_ptr<Environment> e) |  | ||||||
| 	    : lx { s } |  | ||||||
| 	    , env { std::move(e) } |  | ||||||
| 	{ |  | ||||||
| 		adv(); |  | ||||||
| 	} |  | ||||||
| 	Value parse() { return val(); } |  | ||||||
|  |  | ||||||
| private: |  | ||||||
| 	Lex lx; |  | ||||||
| 	T cur; |  | ||||||
| 	std::shared_ptr<Environment> env; |  | ||||||
| 	void adv() { cur = lx.next(); } |  | ||||||
| 	bool acc(Tok k) |  | ||||||
| 	{ |  | ||||||
| 		if (cur.k == k) { |  | ||||||
| 			adv(); |  | ||||||
| 			return true; |  | ||||||
| 		} |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| 	void exp(Tok k) |  | ||||||
| 	{ |  | ||||||
| 		if (!acc(k)) { |  | ||||||
| 			std::println("Expected: {}, Got: {}", Tok_str(k), Tok_str(cur.k)); |  | ||||||
| 			throw std::runtime_error( |  | ||||||
| 			    std::format("Syntax error: expected {}, got {}", Tok_str(k), |  | ||||||
| 			        Tok_str(cur.k))); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	bool begins(Tok k) |  | ||||||
| 	{ |  | ||||||
| 		return k == Tok::Path || k == Tok::Id || k == Tok::Num || k == Tok::Str |  | ||||||
| 		    || k == Tok::LBr || k == Tok::LBrk || k == Tok::LPar; |  | ||||||
| 	} |  | ||||||
| 	std::string id() |  | ||||||
| 	{ |  | ||||||
| 		std::string s = cur.t; |  | ||||||
| 		exp(Tok::Id); |  | ||||||
| 		return s; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	Value array() |  | ||||||
| 	{ |  | ||||||
| 		exp(Tok::LBrk); |  | ||||||
| 		Array a; |  | ||||||
| 		while (cur.k != Tok::RBrk) { |  | ||||||
| 			a.push_back(val()); |  | ||||||
| 			acc(Tok::Comma); |  | ||||||
| 		} |  | ||||||
| 		exp(Tok::RBrk); |  | ||||||
| 		return Value { std::move(a), env }; |  | ||||||
| 	} |  | ||||||
| 	Value block() |  | ||||||
| 	{ |  | ||||||
| 		exp(Tok::LBr); |  | ||||||
| 		Object o; |  | ||||||
| 		while (cur.k != Tok::RBr) { |  | ||||||
| 			if (acc(Tok::LPar)) { |  | ||||||
| 				Value inner = val(); |  | ||||||
| 				exp(Tok::RPar); |  | ||||||
| 				o.insert(inner.obj().begin(), inner.obj().end()); |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
| 			std::string k = id(); |  | ||||||
| 			exp(Tok::Eq); |  | ||||||
| 			o[k] = val(); |  | ||||||
| 		} |  | ||||||
| 		exp(Tok::RBr); |  | ||||||
| 		return Value { std::move(o), env }; |  | ||||||
| 	} |  | ||||||
| 	Value func() |  | ||||||
| 	{ |  | ||||||
| 		exp(Tok::Id); |  | ||||||
| 		std::vector<std::string> params; |  | ||||||
| 		while (cur.k == Tok::Id) |  | ||||||
| 			params.push_back(id()); |  | ||||||
| 		exp(Tok::Eq); |  | ||||||
| 		Value body = val(); |  | ||||||
| 		auto f = std::make_shared<Value::Function>(); |  | ||||||
| 		f->params = params; |  | ||||||
| 		f->body = std::make_shared<Value>(body); |  | ||||||
| 		return Value { std::move(f), env }; |  | ||||||
| 	} |  | ||||||
| 	Value call() |  | ||||||
| 	{ |  | ||||||
| 		std::string name = id(); |  | ||||||
| 		std::vector<Value> args; |  | ||||||
| 		while (begins(cur.k)) |  | ||||||
| 			args.emplace_back(val()); |  | ||||||
| 		auto c = std::make_shared<Value::Call>(); |  | ||||||
| 		c->name = name; |  | ||||||
| 		c->args = std::move(args); |  | ||||||
| 		return Value { std::move(c), env }; |  | ||||||
| 	} |  | ||||||
| 	Value val() |  | ||||||
| 	{ |  | ||||||
| 		switch (cur.k) { |  | ||||||
| 		case Tok::Id: |  | ||||||
| 			if (cur.t == "fn") { |  | ||||||
| 				return func(); |  | ||||||
| 			} |  | ||||||
| 			if (cur.t == "nil") { |  | ||||||
| 				adv(); |  | ||||||
| 				return Value { env }; |  | ||||||
| 			} |  | ||||||
| 			{ |  | ||||||
| 				std::string idn = cur.t; |  | ||||||
| 				adv(); |  | ||||||
|  |  | ||||||
| 				// make the base AST node |  | ||||||
| 				auto node = std::make_shared<Value::Call>(); |  | ||||||
| 				node->recv = nullptr; // top‐level call |  | ||||||
| 				node->name = std::move(idn); |  | ||||||
|  |  | ||||||
| 				// consume any “.field” chains |  | ||||||
| 				while (acc(Tok::Dot)) { |  | ||||||
| 					auto parent = node; |  | ||||||
| 					std::string fld = id(); |  | ||||||
| 					auto mnode = std::make_shared<Value::Call>(); |  | ||||||
| 					mnode->recv = std::make_shared<Value>( |  | ||||||
| 					    parent, env); // wrap the old AST |  | ||||||
| 					mnode->name = std::move(fld); |  | ||||||
| 					node = mnode; |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				// then any args |  | ||||||
| 				while (begins(cur.k)) { |  | ||||||
| 					node->args.push_back(val()); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				return Value { node, env }; |  | ||||||
| 			} |  | ||||||
| 		case Tok::Num: { |  | ||||||
| 			std::string s = cur.t; |  | ||||||
| 			s.erase(std::remove(s.begin(), s.end(), '_'), s.end()); |  | ||||||
| 			double n; |  | ||||||
| 			if (static_cast<int>( |  | ||||||
| 			        std::from_chars(s.data(), s.data() + s.size(), n).ec)) |  | ||||||
| 				throw; |  | ||||||
| 			adv(); |  | ||||||
| 			return Value { n, env }; |  | ||||||
| 		} |  | ||||||
| 		case Tok::Str: { |  | ||||||
| 			Value v { cur.t, env }; |  | ||||||
| 			adv(); |  | ||||||
| 			return v; |  | ||||||
| 		} |  | ||||||
| 		case Tok::Path: { |  | ||||||
| 			Value v { std::filesystem::path { cur.t }, env }; |  | ||||||
| 			adv(); |  | ||||||
| 			return v; |  | ||||||
| 		} |  | ||||||
| 		case Tok::LBrk: |  | ||||||
| 			return array(); |  | ||||||
| 		case Tok::LBr: |  | ||||||
| 			return block(); |  | ||||||
| 		case Tok::LPar: |  | ||||||
| 			adv(); |  | ||||||
| 			{ |  | ||||||
| 				Value v = val(); |  | ||||||
| 				exp(Tok::RPar); |  | ||||||
| 				return v; |  | ||||||
| 			} |  | ||||||
| 		default: |  | ||||||
| 			throw std::runtime_error( |  | ||||||
| 			    std::format("Unexpected token: {}", Tok_str(cur.k))); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| // built-ins |  | ||||||
| Value join(Array const &a) |  | ||||||
| { |  | ||||||
| 	Array r; |  | ||||||
| 	for (auto &v : a) { |  | ||||||
| 		if (!v.is_arr()) |  | ||||||
| 			throw; |  | ||||||
| 		r.insert(r.end(), v.arr().begin(), v.arr().end()); |  | ||||||
| 	} |  | ||||||
| 	return r; |  | ||||||
| } |  | ||||||
| Value list_rng(Array const &a) |  | ||||||
| { |  | ||||||
| 	if (a.size() != 3 || !a[0].is_num() || !a[1].is_num() || !a[2].is_num()) |  | ||||||
| 		throw; |  | ||||||
| 	double s = a[0].num(), e = a[1].num(), st = a[2].num(); |  | ||||||
| 	if (!st) |  | ||||||
| 		throw; |  | ||||||
| 	Array r; |  | ||||||
| 	if (st > 0) |  | ||||||
| 		for (double x = s; x <= e; x += st) |  | ||||||
| 			r.emplace_back(x); |  | ||||||
| 	else |  | ||||||
| 		for (double x = s; x >= e; x += st) |  | ||||||
| 			r.emplace_back(x); |  | ||||||
| 	return r; |  | ||||||
| } |  | ||||||
| Value imp(Array const &a) |  | ||||||
| { |  | ||||||
| 	if (a.empty()) |  | ||||||
| 		throw; |  | ||||||
| 	std::ifstream f; |  | ||||||
| 	if (a[0].is_str()) { |  | ||||||
| 		f = std::ifstream { a[0].str() }; |  | ||||||
| 	} else if (a[0].is_path()) { |  | ||||||
| 		f = std::ifstream { a[0].path() }; |  | ||||||
| 	} else { |  | ||||||
| 		throw; |  | ||||||
| 	} |  | ||||||
| 	if (!f) |  | ||||||
| 		throw; |  | ||||||
| 	std::stringstream b; |  | ||||||
| 	b << f.rdbuf(); |  | ||||||
| 	std::istringstream in { b.str() }; |  | ||||||
| 	return parse_config(in); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // serializer |  | ||||||
| void w(std::ostream &o, Value const &v) |  | ||||||
| { |  | ||||||
| 	if (v.is_nil()) { |  | ||||||
| 		o << "nil"; |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	if (v.is_num()) { |  | ||||||
| 		o << v.num(); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	if (v.is_str()) { |  | ||||||
| 		o << '"' << v.str() << '"'; |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	if (v.is_path()) { |  | ||||||
| 		o << v.path().generic_string(); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	if (v.is_arr()) { |  | ||||||
| 		o << "["; |  | ||||||
| 		bool f = true; |  | ||||||
| 		for (auto &x : v.arr()) { |  | ||||||
| 			if (!f) |  | ||||||
| 				o << ","; |  | ||||||
| 			w(o, x); |  | ||||||
| 			f = false; |  | ||||||
| 		} |  | ||||||
| 		o << "]"; |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	if (v.is_obj()) { |  | ||||||
| 		o << "{"; |  | ||||||
| 		bool f = true; |  | ||||||
| 		for (auto &[k, x] : v.obj()) { |  | ||||||
| 			if (!f) |  | ||||||
| 				o << ","; |  | ||||||
| 			o << k << "="; |  | ||||||
| 			w(o, x); |  | ||||||
| 			f = false; |  | ||||||
| 		} |  | ||||||
| 		o << "}"; |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	o << "fn"; // prints literal token for functions |  | ||||||
| } |  | ||||||
|  |  | ||||||
| } // namespace |  | ||||||
|  |  | ||||||
| // public helpers |  | ||||||
| Value eval(Value const &v, Object const *extra_lib) |  | ||||||
| { |  | ||||||
| 	static Object base = make_default_lib(); |  | ||||||
| 	Object merged = merge_lib(base, extra_lib); |  | ||||||
|  |  | ||||||
| 	if (v.is_call()) { |  | ||||||
| 		auto const &c = v.call(); |  | ||||||
| 		Value fn; |  | ||||||
|  |  | ||||||
| 		// receiver or global |  | ||||||
| 		if (c.recv && !c.recv->is_nil()) { |  | ||||||
| 			Value recv = eval(*c.recv, extra_lib); |  | ||||||
| 			fn = recv[c.name]; |  | ||||||
| 		} else { |  | ||||||
| 			if (auto it = merged.find(c.name); it != merged.end()) |  | ||||||
| 				fn = it->second; |  | ||||||
| 			else |  | ||||||
| 				fn = (*v.env)[c.name]; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// args |  | ||||||
| 		Array argv; |  | ||||||
| 		argv.reserve(c.args.size()); |  | ||||||
| 		for (auto &a : c.args) |  | ||||||
| 			argv.push_back(eval(a, extra_lib)); |  | ||||||
|  |  | ||||||
| 		// call |  | ||||||
| 		if (auto b = std::get_if<Builtin>(&fn.data)) |  | ||||||
| 			return (*b)(argv); |  | ||||||
|  |  | ||||||
| 		if (auto fp = std::get_if<std::shared_ptr<Value::Function>>(&fn.data)) { |  | ||||||
| 			auto child = std::make_shared<Environment>(fn.env.get()); |  | ||||||
| 			for (size_t i = 0; i < (*fp)->params.size(); ++i) |  | ||||||
| 				child->tbl[(*fp)->params[i]] |  | ||||||
| 				    = i < argv.size() ? argv[i] : Value(child); |  | ||||||
|  |  | ||||||
| 			Value body = *(*fp)->body; |  | ||||||
| 			body.env = child; |  | ||||||
| 			return eval(body, extra_lib); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return fn; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// not a call |  | ||||||
| 	if (!v.is_fn()) |  | ||||||
| 		return v; |  | ||||||
|  |  | ||||||
| 	if (auto fp = std::get_if<std::shared_ptr<Value::Function>>(&v.data)) |  | ||||||
| 		return *(*fp)->body; |  | ||||||
|  |  | ||||||
| 	return v; // builtin with no args |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // auto-evaluating operator[] impl |  | ||||||
| static void materialise(Value &v) |  | ||||||
| { |  | ||||||
| 	auto r = eval(v, nullptr); |  | ||||||
| 	if (!r.is_fn() && !r.is_call()) |  | ||||||
| 		v = std::move(r); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Value &Value::operator[](std::string const &k) |  | ||||||
| { |  | ||||||
| 	materialise(*this); |  | ||||||
| 	if (is_nil()) |  | ||||||
| 		return *this; |  | ||||||
| 	if (!is_obj()) |  | ||||||
| 		throw std::logic_error("[] on non-object"); |  | ||||||
|  |  | ||||||
| 	Value &child = obj()[k]; |  | ||||||
| 	materialise(child); |  | ||||||
| 	return child; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Value &Value::operator[](std::size_t i) |  | ||||||
| { |  | ||||||
| 	materialise(*this); |  | ||||||
| 	if (is_nil()) |  | ||||||
| 		return *this; |  | ||||||
| 	if (!is_arr()) |  | ||||||
| 		throw std::logic_error("[] on non-array"); |  | ||||||
| 	if (i >= arr().size()) |  | ||||||
| 		throw std::out_of_range("index"); |  | ||||||
|  |  | ||||||
| 	Value &child = arr()[i]; |  | ||||||
| 	materialise(child); |  | ||||||
| 	return child; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Value const &Value::operator[](std::string const &k) const |  | ||||||
| { |  | ||||||
| 	return const_cast<Value *>(this)->operator[](k); |  | ||||||
| } |  | ||||||
| Value const &Value::operator[](std::size_t i) const |  | ||||||
| { |  | ||||||
| 	return const_cast<Value *>(this)->operator[](i); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // parse/write |  | ||||||
| Value parse_config(std::istream &in) |  | ||||||
| { |  | ||||||
| 	std::stringstream b; |  | ||||||
| 	b << in.rdbuf(); |  | ||||||
| 	std::string src = b.str(); |  | ||||||
| 	auto env = std::make_shared<Environment>(); |  | ||||||
| 	env->tbl = make_default_lib(); |  | ||||||
| 	Parser p { src, env }; |  | ||||||
| 	return p.parse(); |  | ||||||
| } |  | ||||||
| inline void resolve_paths(Value &v, std::filesystem::path const &base) |  | ||||||
| { |  | ||||||
| 	if (v.is_path()) { |  | ||||||
| 		auto &p = v.path(); |  | ||||||
| 		if (p.is_relative()) |  | ||||||
| 			p = base / p; |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	if (v.is_arr()) { |  | ||||||
| 		for (auto &x : v.arr()) |  | ||||||
| 			resolve_paths(x, base); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	if (v.is_obj()) { |  | ||||||
| 		for (auto &[_, x] : v.obj()) |  | ||||||
| 			resolve_paths(x, base); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	if (v.is_call()) { |  | ||||||
| 		for (auto &x : v.call().args) |  | ||||||
| 			resolve_paths(x, base); |  | ||||||
| 	} |  | ||||||
| 	if (std::holds_alternative<std::shared_ptr<Value::Function>>(v.data)) { |  | ||||||
| 		resolve_paths( |  | ||||||
| 		    *std::get<std::shared_ptr<Value::Function>>(v.data)->body, base); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Value parse_config(std::filesystem::path const &f) |  | ||||||
| { |  | ||||||
| 	std::ifstream in { f }; |  | ||||||
| 	if (!in) |  | ||||||
| 		throw std::runtime_error( |  | ||||||
| 		    std::format("Failed to open configuration file: {}", f.string())); |  | ||||||
| 	Value v = parse_config(in); |  | ||||||
| 	resolve_paths(v, f.parent_path()); |  | ||||||
| 	return v; |  | ||||||
| } |  | ||||||
| void write_config(Value const &v, std::filesystem::path const &o) |  | ||||||
| { |  | ||||||
| 	std::ofstream f { o }; |  | ||||||
| 	if (!f) |  | ||||||
| 		throw std::runtime_error( |  | ||||||
| 		    std::format("Failed to write configuration file: {}", o.string())); |  | ||||||
| 	w(f, v); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // default lib |  | ||||||
| Object make_default_lib(); |  | ||||||
| Object merge_lib(Object const &base, Object const *extra) |  | ||||||
| { |  | ||||||
| 	Object out = base; |  | ||||||
| 	if (extra) |  | ||||||
| 		out.insert(extra->begin(), extra->end()); |  | ||||||
| 	return out; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| auto get_env_tbl(Environment *env) -> std::unordered_map<std::string, Value> & |  | ||||||
| { |  | ||||||
| 	return env->tbl; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Object make_default_lib() |  | ||||||
| { |  | ||||||
| 	Object l; |  | ||||||
| 	l["join"] = Builtin { join }; |  | ||||||
| 	l["list_from_range"] = Builtin { list_rng }; |  | ||||||
| 	l["import"] = Builtin { imp }; |  | ||||||
| 	return l; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| } // namespace dhos |  | ||||||
|  |  | ||||||
| // NOLINTEND |  | ||||||
| @@ -1,149 +0,0 @@ | |||||||
| #pragma once |  | ||||||
|  |  | ||||||
| // NOLINTBEGIN |  | ||||||
|  |  | ||||||
| #include <filesystem> |  | ||||||
| #include <functional> |  | ||||||
| #include <istream> |  | ||||||
| #include <memory> |  | ||||||
| #include <string> |  | ||||||
| #include <unordered_map> |  | ||||||
| #include <variant> |  | ||||||
| #include <vector> |  | ||||||
|  |  | ||||||
| namespace dhos { |  | ||||||
|  |  | ||||||
| struct Environment; |  | ||||||
| struct Value; |  | ||||||
|  |  | ||||||
| using Array = std::vector<Value>; |  | ||||||
| using Object = std::unordered_map<std::string, Value>; |  | ||||||
| using Builtin = std::function<Value(Array const &)>; |  | ||||||
|  |  | ||||||
| struct Value { |  | ||||||
| 	using EnvPtr = std::shared_ptr<Environment>; |  | ||||||
|  |  | ||||||
| 	struct Function { |  | ||||||
| 		std::vector<std::string> params; |  | ||||||
| 		std::shared_ptr<Value> body; |  | ||||||
| 	}; |  | ||||||
| 	struct Call { |  | ||||||
| 		std::string name; |  | ||||||
| 		Array args; |  | ||||||
| 		std::shared_ptr<Value> recv; |  | ||||||
| 	}; |  | ||||||
| 	using Data = std::variant<std::monostate, double, std::string, |  | ||||||
| 	    std::filesystem::path, Array, Object, std::shared_ptr<Function>, |  | ||||||
| 	    Builtin, std::shared_ptr<Call>>; |  | ||||||
| 	Data data {}; |  | ||||||
| 	EnvPtr env {}; |  | ||||||
|  |  | ||||||
| 	Value() = default; |  | ||||||
| 	explicit Value(EnvPtr e) |  | ||||||
| 	    : env { std::move(e) } |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 	Value(double n, EnvPtr e = {}) |  | ||||||
| 	    : data { n } |  | ||||||
| 	    , env { std::move(e) } |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 	Value(std::string const &s, EnvPtr e = {}) |  | ||||||
| 	    : data { s } |  | ||||||
| 	    , env { std::move(e) } |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 	Value(char const *s, EnvPtr e = {}) |  | ||||||
| 	    : data { std::string { s } } |  | ||||||
| 	    , env { std::move(e) } |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 	Value(std::filesystem::path const &p, EnvPtr e = {}) |  | ||||||
| 	    : data { p } |  | ||||||
| 	    , env { std::move(e) } |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 	Value(Array a, EnvPtr e = {}) |  | ||||||
| 	    : data { std::move(a) } |  | ||||||
| 	    , env { std::move(e) } |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 	Value(Object o, EnvPtr e = {}) |  | ||||||
| 	    : data { std::move(o) } |  | ||||||
| 	    , env { std::move(e) } |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 	Value(std::shared_ptr<Function> f, EnvPtr e = {}) |  | ||||||
| 	    : data { std::move(f) } |  | ||||||
| 	    , env { std::move(e) } |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 	Value(Builtin b, EnvPtr e = {}) |  | ||||||
| 	    : data { std::move(b) } |  | ||||||
| 	    , env { std::move(e) } |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 	Value(std::shared_ptr<Call> c, EnvPtr e = {}) |  | ||||||
| 	    : data { std::move(c) } |  | ||||||
| 	    , env { std::move(e) } |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool is_nil() const { return std::holds_alternative<std::monostate>(data); } |  | ||||||
| 	bool is_num() const { return std::holds_alternative<double>(data); } |  | ||||||
| 	bool is_str() const { return std::holds_alternative<std::string>(data); } |  | ||||||
| 	bool is_path() const |  | ||||||
| 	{ |  | ||||||
| 		return std::holds_alternative<std::filesystem::path>(data); |  | ||||||
| 	} |  | ||||||
| 	bool is_arr() const { return std::holds_alternative<Array>(data); } |  | ||||||
| 	bool is_obj() const { return std::holds_alternative<Object>(data); } |  | ||||||
| 	bool is_fn() const |  | ||||||
| 	{ |  | ||||||
| 		return std::holds_alternative<std::shared_ptr<Function>>(data) |  | ||||||
| 		    || std::holds_alternative<Builtin>(data); |  | ||||||
| 	} |  | ||||||
| 	bool is_call() const |  | ||||||
| 	{ |  | ||||||
| 		return std::holds_alternative<std::shared_ptr<Call>>(data); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	double num() const { return std::get<double>(data); } |  | ||||||
| 	std::string const &str() const { return std::get<std::string>(data); } |  | ||||||
| 	Array &arr() { return std::get<Array>(data); } |  | ||||||
| 	Array const &arr() const { return std::get<Array>(data); } |  | ||||||
| 	Object &obj() { return std::get<Object>(data); } |  | ||||||
| 	Object const &obj() const { return std::get<Object>(data); } |  | ||||||
| 	std::filesystem::path &path() |  | ||||||
| 	{ |  | ||||||
| 		return std::get<std::filesystem::path>(data); |  | ||||||
| 	} |  | ||||||
| 	std::filesystem::path const &path() const |  | ||||||
| 	{ |  | ||||||
| 		return std::get<std::filesystem::path>(data); |  | ||||||
| 	} |  | ||||||
| 	Call &call() { return *std::get<std::shared_ptr<Call>>(data); } |  | ||||||
| 	Call const &call() const { return *std::get<std::shared_ptr<Call>>(data); } |  | ||||||
|  |  | ||||||
| 	// auto-evaluating nlohmann-style access |  | ||||||
| 	Value &operator[](std::string const &k); |  | ||||||
| 	Value &operator[](std::size_t i); |  | ||||||
| 	Value const &operator[](std::string const &k) const; |  | ||||||
| 	Value const &operator[](std::size_t i) const; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| // pure evaluation helper (never mutates its argument) |  | ||||||
| Value eval(Value const &v, Object const *extra_lib = nullptr); |  | ||||||
|  |  | ||||||
| Value parse_config(std::istream &in); |  | ||||||
| Value parse_config(std::filesystem::path const &f); |  | ||||||
| void write_config(Value const &v, std::filesystem::path const &out); |  | ||||||
|  |  | ||||||
| Object make_default_lib(); |  | ||||||
| Object merge_lib(Object const &base, Object const *extra); |  | ||||||
|  |  | ||||||
| auto get_env_tbl(Environment *env) -> std::unordered_map<std::string, Value> &; |  | ||||||
|  |  | ||||||
| } // namespace dhos |  | ||||||
|  |  | ||||||
| // NOLINTEND |  | ||||||
							
								
								
									
										16
									
								
								src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | #include <assert.h> | ||||||
|  | #include <signal.h> | ||||||
|  |  | ||||||
|  | #include "LunarWM.h" | ||||||
|  |  | ||||||
|  | LunarWM g_wm = {}; | ||||||
|  |  | ||||||
|  | void sigint_handler(int) { LunarWM_terminate(&g_wm); } | ||||||
|  |  | ||||||
|  | int main(void) | ||||||
|  | { | ||||||
|  | 	assert(LunarWM_init(&g_wm)); | ||||||
|  | 	signal(SIGINT, sigint_handler); | ||||||
|  | 	LunarWM_run(&g_wm); | ||||||
|  | 	LunarWM_destroy(&g_wm); | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								src/main.cpp
									
									
									
									
									
								
							| @@ -1,30 +0,0 @@ | |||||||
| // NOLINTBEGIN |  | ||||||
| #include <cassert> |  | ||||||
| #include <csignal> |  | ||||||
|  |  | ||||||
| #include <cpptrace/cpptrace.hpp> |  | ||||||
| #include <cpptrace/from_current.hpp> |  | ||||||
| // NOLINTEND |  | ||||||
|  |  | ||||||
| import std; |  | ||||||
|  |  | ||||||
| import LunarWM.LunarWM; |  | ||||||
|  |  | ||||||
| static std::unique_ptr<LunarWM::LunarWM> g_comp; |  | ||||||
|  |  | ||||||
| auto main() noexcept -> int // NOLINT(bugprone-exception-escape) |  | ||||||
| { |  | ||||||
| 	CPPTRACE_TRY |  | ||||||
| 	{ |  | ||||||
| 		g_comp = std::make_unique<LunarWM::LunarWM>(); |  | ||||||
| 		g_comp->init(); |  | ||||||
| 		assert( |  | ||||||
| 		    std::signal(SIGINT, [](int) { g_comp->terminate(); }) != SIG_ERR); |  | ||||||
| 		g_comp->run(); |  | ||||||
| 	} |  | ||||||
| 	CPPTRACE_CATCH(std::exception const &e) |  | ||||||
| 	{ |  | ||||||
| 		std::println(std::cerr, "Uncaught exception: {}", e.what()); |  | ||||||
| 		cpptrace::from_current_exception().print(); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										148
									
								
								src/vec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								src/vec.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | |||||||
|  | /* | ||||||
|  | BSD 3-Clause License | ||||||
|  |  | ||||||
|  | Copyright (c) 2024, Mashpoe | ||||||
|  | All rights reserved. | ||||||
|  |  | ||||||
|  | Redistribution and use in source and binary forms, with or without | ||||||
|  | modification, are permitted provided that the following conditions are met: | ||||||
|  |  | ||||||
|  | 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |    list of conditions and the following disclaimer. | ||||||
|  |  | ||||||
|  | 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |    this list of conditions and the following disclaimer in the documentation | ||||||
|  |    and/or other materials provided with the distribution. | ||||||
|  |  | ||||||
|  | 3. Neither the name of the copyright holder nor the names of its | ||||||
|  |    contributors may be used to endorse or promote products derived from | ||||||
|  |    this software without specific prior written permission. | ||||||
|  |  | ||||||
|  | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #include "vec.h" | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	vec_size_t size; | ||||||
|  | 	vec_size_t capacity; | ||||||
|  | 	unsigned char data[];  | ||||||
|  | } vector_header; | ||||||
|  |  | ||||||
|  | vector_header* vector_get_header(vector vec) { return &((vector_header*)vec)[-1]; } | ||||||
|  |  | ||||||
|  | vector vector_create(void) | ||||||
|  | { | ||||||
|  | 	vector_header* h = (vector_header*)malloc(sizeof(vector_header)); | ||||||
|  | 	h->capacity = 0; | ||||||
|  | 	h->size = 0; | ||||||
|  |  | ||||||
|  | 	return &h->data; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void vector_free(vector vec) { free(vector_get_header(vec)); } | ||||||
|  |  | ||||||
|  | vec_size_t vector_size(vector vec) { return vector_get_header(vec)->size; } | ||||||
|  |  | ||||||
|  | vec_size_t vector_capacity(vector vec) { return vector_get_header(vec)->capacity; } | ||||||
|  |  | ||||||
|  | vector_header* vector_realloc(vector_header* h, vec_type_t type_size) | ||||||
|  | { | ||||||
|  | 	vec_size_t new_capacity = (h->capacity == 0) ? 1 : h->capacity * 2; | ||||||
|  | 	vector_header* new_h = (vector_header*)realloc(h, sizeof(vector_header) + new_capacity * type_size); | ||||||
|  | 	new_h->capacity = new_capacity; | ||||||
|  |  | ||||||
|  | 	return new_h; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool vector_has_space(vector_header* h) | ||||||
|  | { | ||||||
|  | 	return h->capacity - h->size > 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void* _vector_add_dst(vector* vec_addr, vec_type_t type_size) | ||||||
|  | { | ||||||
|  | 	vector_header* h = vector_get_header(*vec_addr); | ||||||
|  |  | ||||||
|  | 	if (!vector_has_space(h)) | ||||||
|  | 	{ | ||||||
|  | 		h = vector_realloc(h, type_size); | ||||||
|  | 		*vec_addr = h->data; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &h->data[type_size * h->size++]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void* _vector_insert_dst(vector* vec_addr, vec_type_t type_size, vec_size_t pos) | ||||||
|  | { | ||||||
|  | 	vector_header* h = vector_get_header(*vec_addr); | ||||||
|  |  | ||||||
|  | 	vec_size_t new_length = h->size + 1; | ||||||
|  |  | ||||||
|  | 	// make sure there is enough room for the new element | ||||||
|  | 	if (!vector_has_space(h)) | ||||||
|  | 	{ | ||||||
|  | 		h = vector_realloc(h, type_size); | ||||||
|  | 		*vec_addr = h->data; | ||||||
|  | 	} | ||||||
|  | 	// move trailing elements | ||||||
|  | 	memmove(&h->data[(pos + 1) * type_size], | ||||||
|  | 		&h->data[pos * type_size], | ||||||
|  | 		(h->size - pos) * type_size); | ||||||
|  |  | ||||||
|  | 	h->size = new_length; | ||||||
|  |  | ||||||
|  | 	return &h->data[pos * type_size]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void _vector_erase(vector vec, vec_type_t type_size, vec_size_t pos, vec_size_t len) | ||||||
|  | { | ||||||
|  | 	vector_header* h = vector_get_header(vec); | ||||||
|  | 	memmove(&h->data[pos * type_size], | ||||||
|  | 		&h->data[(pos + len) * type_size], | ||||||
|  | 		(h->size - pos - len) * type_size); | ||||||
|  |  | ||||||
|  | 	h->size -= len; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void _vector_remove(vector vec, vec_type_t type_size, vec_size_t pos) | ||||||
|  | { | ||||||
|  | 	_vector_erase(vec, type_size, pos, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void vector_pop(vector vec) { --vector_get_header(vec)->size; } | ||||||
|  |  | ||||||
|  | void _vector_reserve(vector* vec_addr, vec_type_t type_size, vec_size_t capacity) | ||||||
|  | { | ||||||
|  | 	vector_header* h = vector_get_header(*vec_addr); | ||||||
|  | 	if (h->capacity >= capacity) | ||||||
|  | 	{ | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	h = (vector_header*)realloc(h, sizeof(vector_header) + capacity * type_size); | ||||||
|  | 	h->capacity = capacity; | ||||||
|  | 	*vec_addr = &h->data; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | vector _vector_copy(vector vec, vec_type_t type_size) | ||||||
|  | { | ||||||
|  | 	vector_header* h = vector_get_header(vec); | ||||||
|  | 	size_t alloc_size = sizeof(vector_header) + h->size * type_size; | ||||||
|  | 	vector_header* copy_h = (vector_header*)malloc(alloc_size); | ||||||
|  | 	memcpy(copy_h, h, alloc_size); | ||||||
|  | 	copy_h->capacity = copy_h->size; | ||||||
|  |  | ||||||
|  | 	return ©_h->data; | ||||||
|  | } | ||||||
							
								
								
									
										127
									
								
								src/vec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/vec.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | |||||||
|  | /* | ||||||
|  | BSD 3-Clause License | ||||||
|  |  | ||||||
|  | Copyright (c) 2024, Mashpoe | ||||||
|  | All rights reserved. | ||||||
|  |  | ||||||
|  | Redistribution and use in source and binary forms, with or without | ||||||
|  | modification, are permitted provided that the following conditions are met: | ||||||
|  |  | ||||||
|  | 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |    list of conditions and the following disclaimer. | ||||||
|  |  | ||||||
|  | 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |    this list of conditions and the following disclaimer in the documentation | ||||||
|  |    and/or other materials provided with the distribution. | ||||||
|  |  | ||||||
|  | 3. Neither the name of the copyright holder nor the names of its | ||||||
|  |    contributors may be used to endorse or promote products derived from | ||||||
|  |    this software without specific prior written permission. | ||||||
|  |  | ||||||
|  | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef vec_h | ||||||
|  | #define vec_h | ||||||
|  |  | ||||||
|  | #ifdef __cpp_decltype | ||||||
|  | #include <type_traits> | ||||||
|  | #define typeof(T) std::remove_reference<std::add_lvalue_reference<decltype(T)>::type>::type | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
|  | // generic type for internal use | ||||||
|  | typedef void* vector; | ||||||
|  | // number of elements in a vector | ||||||
|  | typedef size_t vec_size_t; | ||||||
|  | // number of bytes for a type | ||||||
|  | typedef size_t vec_type_t; | ||||||
|  |  | ||||||
|  | // TODO: more rigorous check for typeof support with different compilers | ||||||
|  | #if _MSC_VER == 0 || __STDC_VERSION__ >= 202311L || defined __cpp_decltype | ||||||
|  |  | ||||||
|  | // shortcut defines | ||||||
|  |  | ||||||
|  | // vec_addr is a vector* (aka type**) | ||||||
|  | #define vector_add_dst(vec_addr)\ | ||||||
|  | 	((typeof(*vec_addr))(\ | ||||||
|  | 	    _vector_add_dst((vector*)vec_addr, sizeof(**vec_addr))\ | ||||||
|  | 	)) | ||||||
|  | #define vector_insert_dst(vec_addr, pos)\ | ||||||
|  | 	((typeof(*vec_addr))(\ | ||||||
|  | 	    _vector_insert_dst((vector*)vec_addr, sizeof(**vec_addr), pos))) | ||||||
|  |  | ||||||
|  | #define vector_add(vec_addr, value)\ | ||||||
|  | 	(*vector_add_dst(vec_addr) = value) | ||||||
|  | #define vector_insert(vec_addr, pos, value)\ | ||||||
|  | 	(*vector_insert_dst(vec_addr, pos) = value) | ||||||
|  |  | ||||||
|  | #else | ||||||
|  |  | ||||||
|  | #define vector_add_dst(vec_addr, type)\ | ||||||
|  | 	((type*)_vector_add_dst((vector*)vec_addr, sizeof(type))) | ||||||
|  | #define vector_insert_dst(vec_addr, type, pos)\ | ||||||
|  | 	((type*)_vector_insert_dst((vector*)vec_addr, sizeof(type), pos)) | ||||||
|  |  | ||||||
|  | #define vector_add(vec_addr, type, value)\ | ||||||
|  | 	(*vector_add_dst(vec_addr, type) = value) | ||||||
|  | #define vector_insert(vec_addr, type, pos, value)\ | ||||||
|  | 	(*vector_insert_dst(vec_addr, type, pos) = value) | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | // vec is a vector (aka type*) | ||||||
|  | #define vector_erase(vec, pos, len)\ | ||||||
|  | 	(_vector_erase((vector)vec, sizeof(*vec), pos, len)) | ||||||
|  | #define vector_remove(vec, pos)\ | ||||||
|  | 	(_vector_remove((vector)vec, sizeof(*vec), pos)) | ||||||
|  |  | ||||||
|  | #define vector_reserve(vec_addr, capacity)\ | ||||||
|  | 	(_vector_reserve((vector*)vec_addr, sizeof(**vec_addr), capacity)) | ||||||
|  |  | ||||||
|  | #define vector_copy(vec)\ | ||||||
|  | 	(_vector_copy((vector)vec, sizeof(*vec))) | ||||||
|  |  | ||||||
|  | vector vector_create(void); | ||||||
|  |  | ||||||
|  | void vector_free(vector vec); | ||||||
|  |  | ||||||
|  | void* _vector_add_dst(vector* vec_addr, vec_type_t type_size); | ||||||
|  |  | ||||||
|  | void* _vector_insert_dst(vector* vec_addr, vec_type_t type_size, vec_size_t pos); | ||||||
|  |  | ||||||
|  | void _vector_erase(vector vec_addr, vec_type_t type_size, vec_size_t pos, vec_size_t len); | ||||||
|  |  | ||||||
|  | void _vector_remove(vector vec_addr, vec_type_t type_size, vec_size_t pos); | ||||||
|  |  | ||||||
|  | void vector_pop(vector vec); | ||||||
|  |  | ||||||
|  | void _vector_reserve(vector* vec_addr, vec_type_t type_size, vec_size_t capacity); | ||||||
|  |  | ||||||
|  | vector _vector_copy(vector vec, vec_type_t type_size); | ||||||
|  |  | ||||||
|  | vec_size_t vector_size(vector vec); | ||||||
|  |  | ||||||
|  | vec_size_t vector_capacity(vector vec); | ||||||
|  |  | ||||||
|  | // closing bracket for extern "C" | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif /* vec_h */ | ||||||
		Reference in New Issue
	
	Block a user