diff --git a/Makefile b/Makefile index eadadc95..a8b84bed 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,8 @@ CC = $(shell cat "./makefiles/default/cc") RENDERER = $(shell cat "./makefiles/default/renderer") TARGET_NAME = program TARGET_EXTENSION = exe -TARGET_LIB_EXTENSION = dll +LIB_EXTENSION = dll +LIB_EXTENSION_A = .a PREFIX = $(ARCH).$(CC) include makefiles/$(PREFIX).make @@ -36,10 +37,18 @@ EXT_LIB_NAME += ext 7Z += /c/Program\ Files/7-Zip/7z.exe -VULKAN_SDK_PATH += /c/VulkanSDK/1.4.321.1/ -GLSLC += $(VULKAN_SDK_PATH)/Bin/glslc -SPV_OPTIMIZER += $(VULKAN_SDK_PATH)/Bin/spirv-opt -SPV_LINTER += $(VULKAN_SDK_PATH)/Bin/spirv-lint +ifneq (,$(findstring win64,$(ARCH))) + VULKAN_SDK_PATH = /c/VulkanSDK/1.4.321.1/ + GLSLC = $(VULKAN_SDK_PATH)/Bin/glslc + SPV_OPTIMIZER = $(VULKAN_SDK_PATH)/Bin/spirv-opt + SPV_LINTER = $(VULKAN_SDK_PATH)/Bin/spirv-lint +else + VULKAN_SDK_PATH = / + GLSLC = glslc + SPV_OPTIMIZER = spirv-opt + SPV_LINTER = spirv-lint +endif + # Base Engine's DLL INC_DIR += $(ENGINE_INC_DIR) @@ -62,6 +71,15 @@ ifneq (,$(findstring win64,$(ARCH))) DEPS += -lgdi32 -ldwmapi LINKS += #-Wl,-subsystem,windows INCS := -I./dep/master/include $(INCS) +else ifneq (,$(findstring linux,$(ARCH))) + ifneq (,$(findstring -DUF_DEV_ENV,$(FLAGS))) + REQ_DEPS += toml xatlas curl dc:texconv # meshoptimizer ffx:fsr cpptrace vall_e # ncurses openvr draco discord bullet ultralight-ux + FLAGS += -march=native -g # -flto # -g + endif + REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit reactphysics simd ctti gltf imgui fmt freetype openal ogg wav + FLAGS += -DUF_ENV_LINUX -fPIC + DEPS += -pthread -ldl -lX11 -lXrandr + INCS := -I./dep/master/include $(INCS) else ifneq (,$(findstring dreamcast,$(ARCH))) FLAGS += -DUF_ENV_DREAMCAST # -DUF_LEAN_AND_MEAN # this apparently crashes REQ_DEPS += opengl gldc json:nlohmann zlib lua reactphysics simd ctti fmt freetype openal aldc ogg wav png # imgui @@ -69,10 +87,18 @@ else ifneq (,$(findstring dreamcast,$(ARCH))) endif ifneq (,$(findstring vulkan,$(REQ_DEPS))) - FLAGS += -DVK_USE_PLATFORM_WIN32_KHR -DUF_USE_VULKAN - DEPS += -lvulkan-1 -lspirv-cross-core -lspirv-cross-cpp #-lVulkanMemoryAllocator + FLAGS += -DUF_USE_VULKAN + DEPS += -lspirv-cross-core -lspirv-cross-cpp #-lVulkanMemoryAllocator INCS += -I$(VULKAN_SDK_PATH)/include -I./dep/include/spirv_cross/ LIBS += -L$(VULKAN_SDK_PATH)/Lib + + ifneq (,$(findstring linux,$(ARCH))) + DEPS += -lvulkan + FLAGS += -DVK_USE_PLATFORM_XLIB_KHR # VK_USE_PLATFORM_XCB_KHR + else + DEPS += -lvulkan-1 + FLAGS += -DVK_USE_PLATFORM_WIN32_KHR + endif endif ifneq (,$(findstring opengl,$(REQ_DEPS))) FLAGS += -DUF_USE_OPENGL -DUF_USE_OPENGL_FIXED_FUNCTION @@ -182,7 +208,12 @@ ifneq (,$(findstring lua,$(REQ_DEPS))) ifneq (,$(findstring luajit,$(REQ_DEPS))) FLAGS += -DUF_USE_LUAJIT DEPS += -lluajit-5.1 - INCS += -I/mingw64/include/luajit-2.1 + + ifneq (,$(findstring linux,$(ARCH))) + INCS += -I/usr/include/luajit-2.1 + else + INCS += -I/mingw64/include/luajit-2.1 + endif else ifneq (,$(findstring dreamcast,$(ARCH))) DEPS += -llua @@ -253,8 +284,8 @@ endif SRCS_DLL := $(shell find $(ENGINE_SRC_DIR) -name "*.cpp") $(shell find $(DEP_SRC_DIR) -name "*.cpp") OBJS_DLL += $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_DLL)) BASE_DLL += lib$(LIB_NAME) -IM_DLL += $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_DLL).$(TARGET_LIB_EXTENSION).a -EX_DLL += $(BIN_DIR)/exe/lib/$(PREFIX_PATH)/$(BASE_DLL).$(TARGET_LIB_EXTENSION) +IM_DLL += $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_DLL).$(LIB_EXTENSION)$(LIB_EXTENSION_A) +EX_DLL += $(BIN_DIR)/exe/lib/$(PREFIX_PATH)/$(BASE_DLL).$(LIB_EXTENSION) # External Engine's DLL EXT_INC_DIR += $(INC_DIR) EXT_LB_FLAGS += $(LIB_DIR) @@ -269,8 +300,8 @@ EXT_LIBS += $(LIBS) SRCS_EXT_DLL := $(shell find $(EXT_SRC_DIR) -name "*.cpp") OBJS_EXT_DLL += $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_EXT_DLL)) BASE_EXT_DLL += lib$(EXT_LIB_NAME) -EXT_IM_DLL += $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_EXT_DLL).$(TARGET_LIB_EXTENSION).a -EXT_EX_DLL += $(BIN_DIR)/exe/lib/$(PREFIX_PATH)/$(BASE_EXT_DLL).$(TARGET_LIB_EXTENSION) +EXT_IM_DLL += $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_EXT_DLL).$(LIB_EXTENSION)$(LIB_EXTENSION_A) +EXT_EX_DLL += $(BIN_DIR)/exe/lib/$(PREFIX_PATH)/$(BASE_EXT_DLL).$(LIB_EXTENSION) # Client EXE #SRCS += $(wildcard $(CLIENT_SRC_DIR)/*.cpp) $(wildcard $(CLIENT_SRC_DIR)/*/*.cpp) SRCS := $(shell find $(CLIENT_SRC_DIR) -name "*.cpp") @@ -302,13 +333,13 @@ $(EX_DLL): FLAGS += -DUF_EXPORTS $(EX_DLL): $(OBJS_DLL) $(KOS_AR) cru $@ $^ $(KOS_RANLIB) $@ - cp $@ $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_DLL).a + cp $@ $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_DLL)$(LIB_EXTENSION_A) $(EXT_EX_DLL): FLAGS += -DEXT_EXPORTS $(EXT_EX_DLL): $(OBJS_EXT_DLL) $(KOS_AR) cru $@ $^ $(KOS_RANLIB) $@ - cp $@ $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_EXT_DLL).a + cp $@ $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_EXT_DLL)$(LIB_EXTENSION_A) ./bin/dreamcast/romdisk.img: $(KOS_GENROMFS) -f ./bin/dreamcast/romdisk.img -d ./bin/dreamcast/romdisk/ -v @@ -333,6 +364,23 @@ $(PREFIX): $(EX_DLL) $(EXT_EX_DLL) $(TARGET) $(TARGET_SHADERS) %.$(PREFIX).o: %.cpp $(CXX) $(FLAGS) $(INCS) -c $< -o $@ +ifneq (,$(findstring linux,$(ARCH))) +#$(EX_DLL): FLAGS += -DUF_EXPORTS -DEXT_EXPORTS +$(EX_DLL): FLAGS += -DUF_EXPORTS +$(EX_DLL): $(OBJS_DLL) + $(CXX) $(FLAGS) -shared -Wl,-soname,$(BASE_DLL).$(LIB_EXTENSION) $(OBJS_DLL) $(LIBS) $(INCS) $(LINKS) -o $(EX_DLL) + cp $(EX_DLL) $(IM_DLL) + @echo -n $(ARCH) > "./bin/exe/default/arch" + @echo -n $(CC) > "./bin/exe/default/cc" + @echo -n $(RENDERER) > "./bin/exe/default/renderer" + @echo "Setting defaults: $(ARCH).$(CC).$(RENDERER)" +$(EXT_EX_DLL): FLAGS += -DEXT_EXPORTS +$(EXT_EX_DLL): $(OBJS_EXT_DLL) + $(CXX) -shared -Wl,-soname,$(BASE_EXT_DLL).$(LIB_EXTENSION) $(OBJS_EXT_DLL) $(EXT_LIBS) $(EXT_INCS) $(EXT_LINKS) -o $(EXT_EX_DLL) + cp $(EXT_EX_DLL) $(EXT_IM_DLL) + +else + #$(EX_DLL): FLAGS += -DUF_EXPORTS -DEXT_EXPORTS $(EX_DLL): FLAGS += -DUF_EXPORTS $(EX_DLL): $(OBJS_DLL) @@ -348,6 +396,7 @@ $(EXT_EX_DLL): FLAGS += -DEXT_EXPORTS $(EXT_EX_DLL): $(OBJS_EXT_DLL) $(CXX) -shared -o $(EXT_EX_DLL) -Wl,--out-implib=$(EXT_IM_DLL) $(OBJS_EXT_DLL) $(EXT_LIBS) $(EXT_INCS) $(EXT_LINKS) cp $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_EXT_DLL).$(TARGET_LIB_EXTENSION).a $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_EXT_DLL).a +endif $(TARGET): $(OBJS) $(CXX) $(FLAGS) $(OBJS) $(LIBS) $(INCS) $(LINKS) -l$(LIB_NAME) -l$(EXT_LIB_NAME) -o $(TARGET) diff --git a/bin/data/config.json b/bin/data/config.json index f75a6dbf..266c6630 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -96,7 +96,7 @@ }, // "gpu": 7817, // 2060 // "gpu": 29631, // 6800XT - "gpu": 10114, // 4070Ti + // "gpu": 10114, // 4070Ti // "gpu": 5710, // iGPU "experimental": { "rebuild on tick begin": false, @@ -327,7 +327,7 @@ "discord": { "enabled": false } }, "audio": { - "mute": false, + "mute": true, "buffers": { "size": 32768, "count": 4 diff --git a/bin/exe/default/arch b/bin/exe/default/arch index 6ec5a6aa..9c52cb36 100644 --- a/bin/exe/default/arch +++ b/bin/exe/default/arch @@ -1 +1 @@ -win64 \ No newline at end of file +linux \ No newline at end of file diff --git a/client/main.cpp b/client/main.cpp index b3c74bd9..f3fcb072 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -77,7 +77,7 @@ int main(int argc, char** argv){ for ( size_t i = 0; i < argc; ++i ) { char* c_str = argv[i]; std::string string(argv[i]); - uf::arguments.emplace_back(string); + // uf::arguments.emplace_back(string); } std::atexit(::handlers::exit); diff --git a/debug.sh b/debug.sh old mode 100644 new mode 100755 index 7b6ecce1..40747c58 --- a/debug.sh +++ b/debug.sh @@ -3,5 +3,8 @@ cd bin ARCH=$(cat ./exe/default/arch) CC=$(cat ./exe/default/cc) RENDERER=$(cat ./exe/default/renderer) + export PATH="$(pwd)/exe/lib/${ARCH}/:$(pwd)/exe/lib/${ARCH}/${CC}/${RENDERER}/:${PATH}" +export LD_LIBRARY_PATH="$(pwd)/exe/lib/${ARCH}/:$(pwd)/exe/lib/${ARCH}/${CC}/:$(pwd)/exe/lib/${ARCH}/${CC}/${RENDERER}/:${LD_LIBRARY_PATH}" + gdb -ex 'break abort' ./exe/program.${ARCH}.${CC}.${RENDERER}.exe diff --git a/dep/include/sol/sol.hpp b/dep/include/sol/sol.hpp index 05a4e3f3..ca30987e 100644 --- a/dep/include/sol/sol.hpp +++ b/dep/include/sol/sol.hpp @@ -6057,7 +6057,9 @@ namespace sol { static_assert(std::is_constructible::value, "T must be constructible with Args"); *this = nullopt; - this->construct(std::forward(args)...); + //this->construct(std::forward(args)...); + new (static_cast(this)) optional(std::in_place, std::forward(args)...); + return **this; } /// Swaps this optional with the other. diff --git a/engine/inc/uf/engine/ext.h b/engine/inc/uf/engine/ext.h index 69b4c82f..7b22dad4 100644 --- a/engine/inc/uf/engine/ext.h +++ b/engine/inc/uf/engine/ext.h @@ -6,6 +6,7 @@ namespace uf { extern bool UF_API ready; extern uf::stl::vector UF_API arguments; + extern uf::Serializer UF_API config; extern void UF_API load(); diff --git a/engine/inc/uf/engine/object/object.h b/engine/inc/uf/engine/object/object.h index 76b97a6c..feacaf9e 100644 --- a/engine/inc/uf/engine/object/object.h +++ b/engine/inc/uf/engine/object/object.h @@ -14,7 +14,6 @@ namespace uf { class UF_API Object : public uf::Entity { public: - static uf::Timer timer; static bool assertionLoad; static bool deferLazyCalls; diff --git a/engine/inc/uf/ext/json/nlohmann.h b/engine/inc/uf/ext/json/nlohmann.h index 4f4650f9..0de60c1b 100644 --- a/engine/inc/uf/ext/json/nlohmann.h +++ b/engine/inc/uf/ext/json/nlohmann.h @@ -8,7 +8,9 @@ #if !UF_EXCEPTIONS #define JSON_NOEXCEPTION 1 #endif -// #define NDEBUG 1 +#if !UF_RTTI + #define JSON_HAS_STATIC_RTTI 0 +#endif #include #include diff --git a/engine/inc/uf/ext/vulkan/vk.h b/engine/inc/uf/ext/vulkan/vk.h index 5ef31176..52d4a6a1 100644 --- a/engine/inc/uf/ext/vulkan/vk.h +++ b/engine/inc/uf/ext/vulkan/vk.h @@ -4,6 +4,13 @@ #include #include +#if UF_ENV_LINUX + #ifdef Success + #undef Success + #undef None + #endif +#endif + #include #include #include diff --git a/engine/inc/uf/ext/vulkan/vulkan.h b/engine/inc/uf/ext/vulkan/vulkan.h index 1d28fec6..1cb71fe4 100644 --- a/engine/inc/uf/ext/vulkan/vulkan.h +++ b/engine/inc/uf/ext/vulkan/vulkan.h @@ -187,6 +187,7 @@ namespace ext { } extern UF_API Device device; + extern UF_API Allocator allocator; extern UF_API Swapchain swapchain; extern UF_API std::mutex mutex; diff --git a/engine/inc/uf/spec/context/linux.h b/engine/inc/uf/spec/context/linux.h new file mode 100644 index 00000000..c0069522 --- /dev/null +++ b/engine/inc/uf/spec/context/linux.h @@ -0,0 +1,38 @@ +#pragma once + +#include "universal.h" + +#if !UF_USE_OPENGL || UF_USE_OPENGL_GLDC +#include "unknown.h" +namespace spec { + typedef spec::unknown::Context Context; +} +#else +namespace spec { + namespace x11 { + class UF_API_VAR Context : public spec::uni::Context { + public: + typedef ::Display* dc_t; + typedef ::GLXContext context_t; + protected: + dc_t m_display; + context_t m_context; + ::Window m_window; + public: + UF_API_CALL Context( uni::Context* shared, const Context::Settings& settings = Settings() ); + UF_API_CALL Context( uni::Context* shared, const Context::Settings& settings, const Context::window_t& window ); + UF_API_CALL Context( uni::Context* shared, const Context::Settings& settings, unsigned int width, unsigned int height ); + ~Context(); + + virtual void UF_API_CALL terminate(); + + virtual bool UF_API_CALL makeCurrent(); + virtual void UF_API_CALL setVerticalSyncEnabled(bool enabled); + virtual void UF_API_CALL display(); + protected: + virtual void UF_API_CALL create( uni::Context* shared ); + }; + } + typedef spec::x11::Context Context; +} +#endif \ No newline at end of file diff --git a/engine/inc/uf/spec/controller/linux.h b/engine/inc/uf/spec/controller/linux.h new file mode 100644 index 00000000..a82e993d --- /dev/null +++ b/engine/inc/uf/spec/controller/linux.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include "universal.h" + +#if UF_ENV_LINUX + +namespace spec { + namespace linux { + namespace controller { + void UF_API initialize(); + void UF_API tick(); + void UF_API terminate(); + + bool UF_API connected( size_t = 0 ); + // bool UF_API pressed( const uf::stl::string&, size_t = 0 ); + // float UF_API analog( const uf::stl::string&, size_t = 0 ); + } + } + namespace controller = linux::controller; +} + +#endif \ No newline at end of file diff --git a/engine/inc/uf/spec/linux.h b/engine/inc/uf/spec/linux.h new file mode 100644 index 00000000..7a380690 --- /dev/null +++ b/engine/inc/uf/spec/linux.h @@ -0,0 +1,11 @@ +#pragma once + +#include +// In the event some fool includes this file directly +#include "universal.h" +// Windows-specific stuffs +#ifdef UF_ENV_LINUX + +// Headers + +#endif \ No newline at end of file diff --git a/engine/inc/uf/spec/time/linux.h b/engine/inc/uf/spec/time/linux.h new file mode 100644 index 00000000..862634fe --- /dev/null +++ b/engine/inc/uf/spec/time/linux.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include "universal.h" + +#if UF_ENV_LINUX + +namespace spec { + namespace linux { + class UF_API Time : public spec::uni::Time { + public: + static Time::exp_t unit; + spec::uni::Time& UF_API_CALL getUniversal(); + + spec::uni::Time::time_t UF_API_CALL getTime(); + }; + } + typedef linux::Time Time; + extern UF_API Time time; +} + +#endif \ No newline at end of file diff --git a/engine/inc/uf/spec/time/time.h b/engine/inc/uf/spec/time/time.h index f360b900..63f2a575 100644 --- a/engine/inc/uf/spec/time/time.h +++ b/engine/inc/uf/spec/time/time.h @@ -6,5 +6,5 @@ // include universal #include "universal.h" // defines which implementation to use -#include UF_ENV_HEADER +// #include UF_ENV_HEADER // diff --git a/engine/inc/uf/spec/time/universal.h b/engine/inc/uf/spec/time/universal.h index 1bb8942b..555a8d2c 100644 --- a/engine/inc/uf/spec/time/universal.h +++ b/engine/inc/uf/spec/time/universal.h @@ -4,16 +4,15 @@ namespace spec { namespace uni { - class UF_API Time { - public: + namespace time { typedef long long time_t; typedef int exp_t; - static Time::exp_t unit; - protected: - - public: - spec::uni::Time::time_t UF_API_CALL unixTime(); - spec::uni::Time::time_t UF_API_CALL getTime(); - }; - }; + static constexpr exp_t unit = -6; + + time_t UF_API_CALL unixTime(); + time_t UF_API_CALL getTime(); + } + } + + namespace time = uni::time; } \ No newline at end of file diff --git a/engine/inc/uf/spec/window/linux.h b/engine/inc/uf/spec/window/linux.h new file mode 100644 index 00000000..3d64c45b --- /dev/null +++ b/engine/inc/uf/spec/window/linux.h @@ -0,0 +1,94 @@ +#pragma once + +#include +#include "universal.h" + +#if UF_ENV_LINUX +#include +#include +#include +#include + +#if UF_USE_VULKAN + #include + #include +#elif UF_USE_OPENGL + #include +#endif + +namespace spec { + namespace x11 { + class UF_API Window : public spec::uni::Window { + public: + typedef ::Window handle_t; + typedef void* context_t; + + protected: + Display* m_display; + int m_screen; + handle_t m_handle; + GC m_gc; + XIM m_xim; + XIC m_xic; + context_t* m_context; + + vector_t m_lastSize; + bool m_keyRepeatEnabled; + bool m_resizing; + bool m_mouseGrabbed; + bool m_syncParse; + bool m_asyncParse; + + public: + UF_API_CALL Window(); + UF_API_CALL Window( const vector_t& size, const title_t& title = L"Window" ); + ~Window(); + + void UF_API_CALL create(const vector_t& size, const title_t& title = L"Window"); + void UF_API_CALL terminate(); + + handle_t UF_API_CALL getHandle() const; + vector_t UF_API_CALL getPosition() const; + vector_t UF_API_CALL getSize() const; + size_t UF_API_CALL getRefreshRate() const; + + void UF_API_CALL setPosition(const vector_t& position); + void UF_API_CALL centerWindow(); + void UF_API_CALL setMousePosition(const vector_t& position); + vector_t UF_API_CALL getMousePosition(); + void UF_API_CALL setSize(const vector_t& size); + void UF_API_CALL setTitle(const title_t& title); + void UF_API_CALL setIcon(const vector_t& size, uint8_t* pixels); + void UF_API_CALL setVisible(bool visibility); + void UF_API_CALL setCursorVisible(bool visibility); + void UF_API_CALL setKeyRepeatEnabled(bool state); + void UF_API_CALL setMouseGrabbed(bool state); + + static bool UF_API_CALL isKeyPressed( const uf::stl::string& key ); + + void UF_API_CALL requestFocus(); + bool UF_API_CALL hasFocus() const; + + void UF_API_CALL bufferInputs(); + void UF_API_CALL processEvents(); + bool UF_API_CALL pollEvents(bool block = false); + void UF_API_CALL grabMouse(bool state); + + static pod::Vector2ui UF_API_CALL getResolution(); + void UF_API_CALL toggleFullscreen(bool borderless = false); + + #if UF_USE_VULKAN + uf::stl::vector UF_API_CALL getExtensions(bool validationEnabled); + void UF_API_CALL createSurface(VkInstance instance, VkSurfaceKHR& surface); + #endif + + void display(); + }; + } + typedef spec::x11::Window Window; +} + +namespace uf { + using Window = spec::x11::Window; +} +#endif \ No newline at end of file diff --git a/engine/inc/uf/spec/window/window.h b/engine/inc/uf/spec/window/window.h index f360b900..d61f52c8 100644 --- a/engine/inc/uf/spec/window/window.h +++ b/engine/inc/uf/spec/window/window.h @@ -8,3 +8,4 @@ // defines which implementation to use #include UF_ENV_HEADER // + diff --git a/engine/inc/uf/utils/io/fmt.h b/engine/inc/uf/utils/io/fmt.h index 3f4f56aa..e4426fd7 100644 --- a/engine/inc/uf/utils/io/fmt.h +++ b/engine/inc/uf/utils/io/fmt.h @@ -1,7 +1,7 @@ #pragma once #if UF_USE_FMT - #if UF_ENV_DREAMCAST + #if UF_ENV_DREAMCAST || UF_ENV_LINUX #define FMT_HEADER_ONLY #endif #include diff --git a/engine/inc/uf/utils/math/physics.h b/engine/inc/uf/utils/math/physics.h index 83f5a474..38b7b6b7 100644 --- a/engine/inc/uf/utils/math/physics.h +++ b/engine/inc/uf/utils/math/physics.h @@ -3,6 +3,7 @@ #include #include #include +#include #if UF_USE_BULLET #include diff --git a/engine/inc/uf/utils/memory/allocator.h b/engine/inc/uf/utils/memory/allocator.h index f3f5cbb6..d3f827c8 100644 --- a/engine/inc/uf/utils/memory/allocator.h +++ b/engine/inc/uf/utils/memory/allocator.h @@ -5,7 +5,7 @@ // #include // #include -#define UF_MEMORYPOOL_USE_STL_ALLOCATOR 0 +#define UF_MEMORYPOOL_USE_STL_ALLOCATOR 1 namespace uf { namespace allocator { diff --git a/engine/inc/uf/utils/serialize/serializer.h b/engine/inc/uf/utils/serialize/serializer.h index a06c25aa..76e2a488 100644 --- a/engine/inc/uf/utils/serialize/serializer.h +++ b/engine/inc/uf/utils/serialize/serializer.h @@ -16,7 +16,7 @@ namespace uf { typedef uf::stl::string output_t; typedef uf::stl::string input_t; - Serializer(); + Serializer() = default; Serializer( const uf::stl::string& str ); Serializer( const ext::json::base_value& ); Serializer( const ext::json::Value& ); diff --git a/engine/inc/uf/utils/time/time.h b/engine/inc/uf/utils/time/time.h index a66f6cec..23d84d1e 100644 --- a/engine/inc/uf/utils/time/time.h +++ b/engine/inc/uf/utils/time/time.h @@ -5,11 +5,11 @@ #include namespace uf { - template + template class /*UF_API*/ Time { public: typedef T time_t; - typedef spec::Time::exp_t exp_t; + typedef spec::time::exp_t exp_t; protected: uf::Time::time_t m_time; uf::Time::exp_t m_exp; @@ -19,7 +19,7 @@ namespace uf { static const uf::Time::exp_t microseconds = -6; static const uf::Time::exp_t nanoseconds = -9; - Time(uf::Time::time_t t = 0, uf::Time::exp_t b = spec::Time::unit ); + Time(uf::Time::time_t t = 0, uf::Time::exp_t b = spec::time::unit ); void set(uf::Time::time_t t); void set(uf::Time::time_t t, uf::Time::exp_t b); @@ -31,7 +31,7 @@ namespace uf { uf::Time::time_t get() const; uf::Time::exp_t getBase() const; - uf::Time::time_t asBase(uf::Time::exp_t base = spec::Time::unit ); + uf::Time::time_t asBase(uf::Time::exp_t base = spec::time::unit ); inline uf::Time::time_t asSeconds(); inline uf::Time::time_t asMilliseconds(); inline uf::Time::time_t asMicroseconds(); @@ -55,17 +55,17 @@ namespace uf { uf::Time operator-( const uf::Time& t ); uf::Time operator+( const uf::Time& t ); }; - template + template class /*UF_API*/ Timer { public: typedef T time_t; - typedef spec::Time::exp_t exp_t; + typedef spec::time::exp_t exp_t; protected: bool m_running; uf::Time m_begin; uf::Time m_end; public: - Timer(bool running = true, uf::Timer::exp_t b = spec::Time::unit); + Timer(bool running = true, uf::Timer::exp_t b = spec::time::unit); void start( uf::Time = 0 ); void stop(); diff --git a/engine/inc/uf/utils/time/timer.inl b/engine/inc/uf/utils/time/timer.inl index c96dcad9..576be3c0 100644 --- a/engine/inc/uf/utils/time/timer.inl +++ b/engine/inc/uf/utils/time/timer.inl @@ -20,12 +20,12 @@ void uf::Timer::stop() { } template void uf::Timer::reset() { - this->m_end = this->m_begin = spec::time.getTime(); + this->m_end = this->m_begin = spec::time::getTime(); } template void uf::Timer::update() { if ( !this->m_running ) return; - this->m_end = spec::time.getTime(); + this->m_end = spec::time::getTime(); } template bool uf::Timer::running() const { diff --git a/engine/src/engine/ext/ext.cpp b/engine/src/engine/ext/ext.cpp index af40af2c..b094fbb4 100644 --- a/engine/src/engine/ext/ext.cpp +++ b/engine/src/engine/ext/ext.cpp @@ -48,6 +48,7 @@ bool uf::ready = false; uf::stl::vector uf::arguments; + uf::Serializer uf::config; namespace { @@ -56,7 +57,7 @@ namespace { std::ofstream output; struct { - uf::stl::string output = uf::io::root+"/logs/output.txt"; + uf::stl::string output; } filenames; } io; @@ -70,8 +71,6 @@ namespace { } total; } times; - auto& json = uf::config; - struct { struct { struct { @@ -112,23 +111,23 @@ void UF_API uf::load() { uf::config.readFromFile(uf::io::root+"config.json"); } void UF_API uf::load( ext::json::Value& json ) { - ::config.engine.gc.enabled = json["engine"]["debug"]["garbage collection"]["enabled"].as(::config.engine.gc.enabled); - ::config.engine.gc.every = json["engine"]["debug"]["garbage collection"]["every"].as(::config.engine.gc.every); - ::config.engine.gc.mode = json["engine"]["debug"]["garbage collection"]["mode"].as(::config.engine.gc.mode); - ::config.engine.gc.announce = json["engine"]["debug"]["garbage collection"]["announce"].as(::config.engine.gc.announce); + /*global*/::config.engine.gc.enabled = json["engine"]["debug"]["garbage collection"]["enabled"].as(/*global*/::config.engine.gc.enabled); + /*global*/::config.engine.gc.every = json["engine"]["debug"]["garbage collection"]["every"].as(/*global*/::config.engine.gc.every); + /*global*/::config.engine.gc.mode = json["engine"]["debug"]["garbage collection"]["mode"].as(/*global*/::config.engine.gc.mode); + /*global*/::config.engine.gc.announce = json["engine"]["debug"]["garbage collection"]["announce"].as(/*global*/::config.engine.gc.announce); - ::config.engine.ext.ultralight.enabled = json["engine"]["ext"]["ultralight"]["enabled"].as(::config.engine.ext.ultralight.enabled); - ::config.engine.ext.discord.enabled = json["engine"]["ext"]["discord"]["enabled"].as(::config.engine.ext.discord.enabled); - ::config.engine.ext.imgui.enabled = json["engine"]["ext"]["imgui"]["enabled"].as(::config.engine.ext.imgui.enabled); + /*global*/::config.engine.ext.ultralight.enabled = json["engine"]["ext"]["ultralight"]["enabled"].as(/*global*/::config.engine.ext.ultralight.enabled); + /*global*/::config.engine.ext.discord.enabled = json["engine"]["ext"]["discord"]["enabled"].as(/*global*/::config.engine.ext.discord.enabled); + /*global*/::config.engine.ext.imgui.enabled = json["engine"]["ext"]["imgui"]["enabled"].as(/*global*/::config.engine.ext.imgui.enabled); - ::config.engine.ext.vall_e.enabled = json["engine"]["ext"]["vall_e"]["enabled"].as(::config.engine.ext.vall_e.enabled); - ::config.engine.ext.vall_e.model_path = json["engine"]["ext"]["vall_e"]["model_path"].as(::config.engine.ext.vall_e.model_path); - ::config.engine.ext.vall_e.encodec_path = json["engine"]["ext"]["vall_e"]["encodec_path"].as(::config.engine.ext.vall_e.encodec_path); + /*global*/::config.engine.ext.vall_e.enabled = json["engine"]["ext"]["vall_e"]["enabled"].as(/*global*/::config.engine.ext.vall_e.enabled); + /*global*/::config.engine.ext.vall_e.model_path = json["engine"]["ext"]["vall_e"]["model_path"].as(/*global*/::config.engine.ext.vall_e.model_path); + /*global*/::config.engine.ext.vall_e.encodec_path = json["engine"]["ext"]["vall_e"]["encodec_path"].as(/*global*/::config.engine.ext.vall_e.encodec_path); - ::config.engine.limiter.print = json["engine"]["debug"]["framerate"]["print"].as(::config.engine.limiter.print); + /*global*/::config.engine.limiter.print = json["engine"]["debug"]["framerate"]["print"].as(/*global*/::config.engine.limiter.print); - ::config.engine.fps.print = json["engine"]["debug"]["framerate"]["print"].as(::config.engine.fps.print); - ::config.engine.fps.every = json["engine"]["debug"]["framerate"]["every"].as(::config.engine.fps.every); + /*global*/::config.engine.fps.print = json["engine"]["debug"]["framerate"]["print"].as(/*global*/::config.engine.fps.print); + /*global*/::config.engine.fps.every = json["engine"]["debug"]["framerate"]["every"].as(/*global*/::config.engine.fps.every); uf::Mesh::defaultInterleaved = json["engine"]["scenes"]["meshes"]["interleaved"].as( uf::Mesh::defaultInterleaved ); @@ -161,8 +160,8 @@ void UF_API uf::load( ext::json::Value& json ) { /* Frame limiter */ { size_t limit = configEngineLimitersJson["framerate"].as(); - ::times.limiter = limit != 0 ? 1.0 / limit : 0; - UF_MSG_DEBUG("Limiter set to {} ms", ::times.limiter); + /*global*/::times.limiter = limit != 0 ? 1.0 / limit : 0; + UF_MSG_DEBUG("Limiter set to {} ms", /*global*/::times.limiter); } /* Max delta time */{ size_t limit = configEngineLimitersJson["deltaTime"].as(); @@ -265,14 +264,14 @@ void UF_API uf::load( ext::json::Value& json ) { uf::renderer::settings::defaultDeferBufferDestroy = configRenderInvariantJson["default defer buffer destroy"].as( uf::renderer::settings::defaultDeferBufferDestroy ); #if 1 uf::renderer::settings::defaultCommandBufferImmediate = true; - ::requestDeferredCommandBufferSubmit = !configRenderInvariantJson["default command buffer immediate"].as( uf::renderer::settings::defaultCommandBufferImmediate ); + /*global*/::requestDeferredCommandBufferSubmit = !configRenderInvariantJson["default command buffer immediate"].as( uf::renderer::settings::defaultCommandBufferImmediate ); #else uf::renderer::settings::defaultCommandBufferImmediate = configRenderInvariantJson["default command buffer immediate"].as( uf::renderer::settings::defaultCommandBufferImmediate ); #endif #endif #if 1 uf::renderer::settings::experimental::dedicatedThread = false; - ::requestDedicatedRenderThread = configRenderExperimentalJson["dedicated thread"].as( uf::renderer::settings::experimental::dedicatedThread ); + /*global*/::requestDedicatedRenderThread = configRenderExperimentalJson["dedicated thread"].as( uf::renderer::settings::experimental::dedicatedThread ); #else uf::renderer::settings::experimental::dedicatedThread = configRenderExperimentalJson["dedicated thread"].as( uf::renderer::settings::experimental::dedicatedThread ); #endif @@ -303,7 +302,7 @@ void UF_API uf::load( ext::json::Value& json ) { UF_MSG_DEBUG("Using FFX FSR Preset: {} ({:.3f}% render scale)", mode, (100.0f / renderMode.scale)); } else #endif - renderMode.scale = ::json["engine"]["ext"]["vulkan"]["framebuffer"]["size"].as(1.0f); + renderMode.scale = uf::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"].as(1.0f); UF_MSG_DEBUG("Geometry render scale: {:.3f}", renderMode.scale); } } @@ -316,15 +315,15 @@ void UF_API uf::initialize() { uf::thread::get(uf::thread::asyncThreadName); } /* set JSON implicit preferences */ { - ext::json::PREFERRED_ENCODING = ::json["engine"]["ext"]["json"]["encoding"].as(ext::json::PREFERRED_ENCODING); - ext::json::PREFERRED_COMPRESSION = ::json["engine"]["ext"]["json"]["compression"].as(ext::json::PREFERRED_COMPRESSION); + ext::json::PREFERRED_ENCODING = uf::config["engine"]["ext"]["json"]["encoding"].as(ext::json::PREFERRED_ENCODING); + ext::json::PREFERRED_COMPRESSION = uf::config["engine"]["ext"]["json"]["compression"].as(ext::json::PREFERRED_COMPRESSION); UF_MSG_DEBUG("Setting JSON implicit preference: {}.{}", ext::json::PREFERRED_ENCODING, ext::json::PREFERRED_COMPRESSION); } /* Arguments */ { bool modified = false; - auto& arguments = ::json["arguments"]; + auto& arguments = uf::config["arguments"]; for ( auto& arg : uf::arguments ) { // store raw argument int i = arguments.size(); @@ -335,7 +334,7 @@ void UF_API uf::initialize() { uf::stl::string keyString = match[1]; uf::stl::string valueString = match[2]; uf::Serializer value; value.deserialize(valueString); - ::json.path(keyString) = value; + uf::config.path(keyString) = value; modified = true; } /* @@ -346,30 +345,31 @@ void UF_API uf::initialize() { uf::stl::string keyString = match[1].str(); uf::stl::string valueString = match[2].str(); uf::Serializer value; value.deserialize(valueString); - ::json.path(keyString) = value; + uf::config.path(keyString) = value; modified = true; } } */ } // UF_MSG_DEBUG("Arguments: {}", uf::Serializer(arguments)); - if ( modified ) UF_MSG_DEBUG("New config: {}", ::json.serialize()); + if ( modified ) UF_MSG_DEBUG("New config: {}", uf::config.serialize()); } /* Seed */ { std::srand(std::time(NULL)); } /* Open output file */ { - ::io.output.open(::io.filenames.output); + /*global*/::io.filenames.output = uf::io::root+"/logs/output.txt"; + /*global*/::io.output.open(/*global*/::io.filenames.output); } /* Initialize timers */ { - times.sys.start(); + /*global*/::times.sys.start(); } /* Read persistent data */ { // #include "./inits/persistence.inl" } /* Set memory pool sizes */ { - auto& configMemoryPoolJson = ::json["engine"]["memory pool"]; + auto& configMemoryPoolJson = uf::config["engine"]["memory pool"]; // check if we are even allowed to use memory pools bool enabled = configMemoryPoolJson["enabled"].as(true); @@ -427,19 +427,19 @@ void UF_API uf::initialize() { uf::Scene& scene = uf::instantiator::instantiate(); //new uf::Scene; uf::scene::scenes.emplace_back(&scene); auto& metadata = scene.getComponent(); - // metadata["system"]["config"] = ::json; + // metadata["system"]["config"] = uf::config; } - uf::load( ::json ); + uf::load( uf::config ); // renderer settings { #if UF_USE_VULKAN - auto& configRenderJson = ::json["engine"]["ext"]["vulkan"]; + auto& configRenderJson = uf::config["engine"]["ext"]["vulkan"]; #elif UF_USE_OPENGL - auto& configRenderJson = ::json["engine"]["ext"]["opengl"]; + auto& configRenderJson = uf::config["engine"]["ext"]["opengl"]; #else - auto& configRenderJson = ::json["engine"]["ext"]["software"]; + auto& configRenderJson = uf::config["engine"]["ext"]["software"]; #endif auto& configRenderInvariantJson = configRenderJson["invariant"]; auto& configRenderExperimentalJson = configRenderJson["experimental"]; @@ -529,7 +529,7 @@ void UF_API uf::initialize() { if ( uf::renderer::settings::pipelines::rt ) { // uf::renderer::settings::pipelines::vxgi = false; // uf::renderer::settings::pipelines::culling = false; - ::json["engine"]["scenes"]["lights"]["shadows"]["enabled"] = false; + uf::config["engine"]["scenes"]["lights"]["shadows"]["enabled"] = false; } #define JSON_TO_FORMAT( key ) if ( configRenderJson["formats"][#key].is() ) {\ uf::stl::string format = configRenderJson["formats"][#key].as();\ @@ -548,7 +548,9 @@ void UF_API uf::initialize() { #if UF_USE_LUA /* Lua */ { - auto& configLuaJson = ::json["engine"]["ext"]["lua"]; + auto& configLuaJson = uf::config["engine"]["ext"]["lua"]; + + ext::lua::main = uf::io::root + "/scripts/main.lua"; ext::lua::enabled = configLuaJson["enabled"].as(ext::lua::enabled); ext::lua::main = configLuaJson["main"].as(ext::lua::main); @@ -565,7 +567,7 @@ void UF_API uf::initialize() { #if UF_USE_OPENVR { - auto& configVrJson = ::json["engine"]["ext"]["vr"]; + auto& configVrJson = uf::config["engine"]["ext"]["vr"]; ext::openvr::enabled = configVrJson["enable"].as( ext::openvr::enabled ); ext::openvr::swapEyes = configVrJson["swap eyes"].as( ext::openvr::swapEyes ); @@ -578,7 +580,7 @@ void UF_API uf::initialize() { ext::openvr::driver.manifest = configVrJson["manifest"].as(ext::openvr::driver.manifest); - if ( ext::openvr::enabled ) ::json["engine"]["render modes"]["stereo deferred"] = true; + if ( ext::openvr::enabled ) uf::config["engine"]["render modes"]["stereo deferred"] = true; } #endif @@ -586,11 +588,11 @@ void UF_API uf::initialize() { // to-do: have this set per config value instead of relying on the scene to handle its own rendermodes if ( false ) { // setup render mode - if ( ::json["engine"]["render modes"]["deferred"].as(true) ) { + if ( uf::config["engine"]["render modes"]["deferred"].as(true) ) { auto* renderMode = new uf::renderer::DeferredRenderMode; - if ( ::json["engine"]["ext"]["vulkan"]["pipelines"]["postProcess"].is() ) - renderMode->metadata.json["postProcess"] = ::json["engine"]["ext"]["vulkan"]["pipelines"]["postProcess"]; + if ( uf::config["engine"]["ext"]["vulkan"]["pipelines"]["postProcess"].is() ) + renderMode->metadata.json["postProcess"] = uf::config["engine"]["ext"]["vulkan"]["pipelines"]["postProcess"]; renderMode->blitter.descriptor.renderMode = "Swapchain"; renderMode->blitter.descriptor.subpass = 0; @@ -609,10 +611,10 @@ void UF_API uf::initialize() { UF_MSG_DEBUG("Using FFX FSR Preset: {} ({:.3f}% render scale)", mode, (100.0f / renderMode->scale)); } else #endif - renderMode->scale = ::json["engine"]["ext"]["vulkan"]["framebuffer"]["size"].as(1.0f); + renderMode->scale = uf::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"].as(1.0f); UF_MSG_DEBUG("Geometry render scale: {:.3f}", renderMode->scale); - if ( ::json["engine"]["render modes"]["stereo deferred"].as() ) { + if ( uf::config["engine"]["render modes"]["stereo deferred"].as() ) { renderMode->metadata.eyes = 2; } #if UF_USE_VULKAN @@ -630,10 +632,10 @@ void UF_API uf::initialize() { uf::renderer::addRenderMode( renderMode, "" ); } - if ( ::json["engine"]["render modes"]["gui"].as(true) ) { + if ( uf::config["engine"]["render modes"]["gui"].as(true) ) { auto* renderMode = new uf::renderer::RenderTargetRenderMode; - if ( ::json["engine"]["ext"]["vulkan"]["pipelines"]["postProcess"].is() ) - renderMode->metadata.json["postProcess"] = ::json["engine"]["ext"]["vulkan"]["pipelines"]["postProcess"]; + if ( uf::config["engine"]["ext"]["vulkan"]["pipelines"]["postProcess"].is() ) + renderMode->metadata.json["postProcess"] = uf::config["engine"]["ext"]["vulkan"]["pipelines"]["postProcess"]; uf::stl::string name = "Gui"; renderMode->blitter.descriptor.renderMode = "Swapchain"; @@ -657,8 +659,8 @@ void UF_API uf::initialize() { UF_MSG_DEBUG("Recommended VR Resolution: {}, {}", renderMode.width, renderMode.height); - if ( ::json["engine"]["ext"]["vr"]["scale"].is() ) { - float scale = ::json["engine"]["ext"]["vr"]["scale"].as(); + if ( uf::config["engine"]["ext"]["vr"]["scale"].is() ) { + float scale = uf::config["engine"]["ext"]["vr"]["scale"].as(); renderMode.width *= scale; renderMode.height *= scale; @@ -668,7 +670,7 @@ void UF_API uf::initialize() { #endif #if UF_USE_OPENAL - /* Initialize OpenAL */ { + /* Initialize OpenAL */ if ( !uf::audio::muted ) { ext::al::initialize(); } #endif @@ -679,23 +681,23 @@ void UF_API uf::initialize() { pod::Thread& threadMain = uf::thread::get(uf::thread::mainThreadName); #if UF_USE_DISCORD - /* Discord */ if ( ::config.engine.ext.discord.enabled ) { + /* Discord */ if ( /*global*/::config.engine.ext.discord.enabled ) { ext::discord::initialize(); } #endif #if UF_USE_ULTRALIGHT - /* Ultralight-UX */ if ( ::config.engine.ext.ultralight.enabled ) { + /* Ultralight-UX */ if ( /*global*/::config.engine.ext.ultralight.enabled ) { ext::ultralight::initialize(); } #endif #if UF_USE_IMGUI - if ( ::config.engine.ext.imgui.enabled ) { + if ( /*global*/::config.engine.ext.imgui.enabled ) { // ext::imgui::initialize(); } #endif #if UF_USE_VALL_E - if ( ::config.engine.ext.vall_e.enabled ) { - ext::vall_e::initialize( ::config.engine.ext.vall_e.model_path, ::config.engine.ext.vall_e.encodec_path ); + if ( /*global*/::config.engine.ext.vall_e.enabled ) { + ext::vall_e::initialize( /*global*/::config.engine.ext.vall_e.model_path, /*global*/::config.engine.ext.vall_e.encodec_path ); // bind the hook uf::hooks.addHook( "llm:VALL-E.synthesize", [&](ext::json::Value& json){ @@ -720,8 +722,8 @@ void UF_API uf::initialize() { #endif /* Add hooks */ { uf::hooks.addHook( "game:Scene.Load", [&](ext::json::Value& json){ - ::sceneTransition.payload = json; - ::sceneTransition.phase = 0; + /*global*/::sceneTransition.payload = json; + /*global*/::sceneTransition.phase = 0; }); uf::hooks.addHook( "system:Quit", [&](ext::json::Value& json){ if ( json["message"].is() ) { @@ -733,11 +735,11 @@ void UF_API uf::initialize() { /* Initialize root scene*/ { ext::json::Value payload; - payload["scene"] = ::json["engine"]["scenes"]["start"]; - ::sceneTransition.payload = payload; - ::sceneTransition.phase = 0; + payload["scene"] = uf::config["engine"]["scenes"]["start"]; + /*global*/::sceneTransition.payload = payload; + /*global*/::sceneTransition.phase = 0; - // auto& scene = uf::scene::loadScene( ::json["engine"]["scenes"]["start"] ); + // auto& scene = uf::scene::loadScene( uf::config["engine"]["scenes"]["start"] ); } /* @@ -747,14 +749,14 @@ void UF_API uf::initialize() { */ uf::ready = true; - UF_MSG_INFO("EXT took {} seconds to initialize", times.sys.elapsed().asDouble()); + UF_MSG_INFO("EXT took {} seconds to initialize", /*global*/::times.sys.elapsed().asDouble()); } void UF_API uf::tick() { #if 1 - if ( ::sceneTransition.phase >= 0 ) { - auto target = ::sceneTransition.payload["scene"].as(); - auto& phase = ::sceneTransition.phase; + if ( /*global*/::sceneTransition.phase >= 0 ) { + auto target = /*global*/::sceneTransition.payload["scene"].as(); + auto& phase = /*global*/::sceneTransition.phase; ++phase; @@ -764,12 +766,12 @@ void UF_API uf::tick() { uf::renderer::synchronize(); if ( uf::renderer::settings::experimental::dedicatedThread ) { - ::requestDedicatedRenderThread = true; + /*global*/::requestDedicatedRenderThread = true; uf::renderer::settings::experimental::dedicatedThread = !uf::renderer::settings::experimental::dedicatedThread; } #if UF_USE_VULKAN if ( !uf::renderer::settings::defaultCommandBufferImmediate ) { - ::requestDeferredCommandBufferSubmit = true; + /*global*/::requestDeferredCommandBufferSubmit = true; uf::renderer::settings::defaultCommandBufferImmediate = !uf::renderer::settings::defaultCommandBufferImmediate; } #endif @@ -777,7 +779,7 @@ void UF_API uf::tick() { uf::scene::unloadScene(); uf::scene::loadScene( target ); - ::sceneTransition.phase = -1; + /*global*/::sceneTransition.phase = -1; #if UF_USE_VULKAN uf::renderer::flushCommandBuffers(); @@ -801,7 +803,6 @@ void UF_API uf::tick() { #endif /* Print Memory Pool Information */ { TIMER(1, uf::inputs::kbm::states::P ) { - // uf::iostream << uf::renderer::allocatorStats() << "\n"; UF_MSG_DEBUG("==== Memory Pool Information ===="); if ( uf::memoryPool::global.size() > 0 ) UF_MSG_DEBUG("Global Memory Pool: {}", uf::memoryPool::global.stats()); if ( uf::Entity::memoryPool.size() > 0 ) UF_MSG_DEBUG("Entity Memory Pool: {}", uf::Entity::memoryPool.stats()); @@ -855,7 +856,7 @@ void UF_API uf::tick() { uf::thread::process( uf::thread::get(uf::thread::mainThreadName) ); } #if UF_USE_ULTRALIGHT - /* Ultralight-UX */ if ( ::config.engine.ext.ultralight.enabled ) { + /* Ultralight-UX */ if ( /*global*/::config.engine.ext.ultralight.enabled ) { ext::ultralight::tick(); } #endif @@ -864,24 +865,24 @@ void UF_API uf::tick() { } #if UF_USE_DISCORD - /* Discord */ if ( ::config.engine.ext.discord.enabled ) { + /* Discord */ if ( /*global*/::config.engine.ext.discord.enabled ) { ext::discord::tick(); } #endif #if UF_USE_IMGUI - if ( ::config.engine.ext.imgui.enabled ) { + if ( /*global*/::config.engine.ext.imgui.enabled ) { ext::imgui::tick(); } #endif - /* FPS Print */ if ( ::config.engine.fps.print ) { - ++::times.frames; - ++::times.total.frames; - TIMER( ::config.engine.fps.every ) { - UF_MSG_DEBUG("System: {:.3f} ms/frame | Time: {:.3f} | Frames: {} | FPS: {:.3f}", (time * 1000.0/::times.frames), time, ::times.frames, ::times.frames / time); + /* FPS Print */ if ( /*global*/::config.engine.fps.print ) { + ++/*global*/::times.frames; + ++/*global*/::times.total.frames; + TIMER( /*global*/::config.engine.fps.every ) { + UF_MSG_DEBUG("System: {:.3f} ms/frame | Time: {:.3f} | Frames: {} | FPS: {:.3f}", (time * 1000.0 / /*global*/::times.frames), time, /*global*/::times.frames, /*global*/::times.frames / time); #if UF_ENV_DREAMCAST DC_STATS(); #endif - ::times.frames = 0; + /*global*/::times.frames = 0; } } @@ -889,17 +890,17 @@ void UF_API uf::tick() { // to-do: handle when the memory pool is disabled, because entities are NOT cleaned up at all // should also handle entity deletion when GC is disabled - if ( ::config.engine.gc.enabled ) { - TIMER( ::config.engine.gc.every ) { - size_t collected = uf::instantiator::collect( ::config.engine.gc.mode ); + if ( /*global*/::config.engine.gc.enabled ) { + TIMER( /*global*/::config.engine.gc.every ) { + size_t collected = uf::instantiator::collect( /*global*/::config.engine.gc.mode ); if ( collected > 0 ) { - if ( ::config.engine.gc.announce ) UF_MSG_DEBUG("GC collected {} unused entities", (int) collected); + if ( /*global*/::config.engine.gc.announce ) UF_MSG_DEBUG("GC collected {} unused entities", (int) collected); } } } #if !UF_ENV_DREAMCAST - if ( ::times.limiter > 0 ) { - double limiter = ::times.limiter * 1000.0; + if ( /*global*/::times.limiter > 0 ) { + double limiter = /*global*/::times.limiter * 1000.0; static uf::Timer timer(false); if ( !timer.running() ) timer.start(); @@ -913,14 +914,14 @@ void UF_API uf::tick() { } - if ( ::requestDedicatedRenderThread && controller.getName() == "Player" ) { - ::requestDedicatedRenderThread = false; + if ( /*global*/::requestDedicatedRenderThread && controller.getName() == "Player" ) { + /*global*/::requestDedicatedRenderThread = false; uf::renderer::settings::experimental::dedicatedThread = true; UF_MSG_DEBUG("Dedicated render requested"); } #if UF_USE_VULKAN - if ( ::requestDeferredCommandBufferSubmit && controller.getName() == "Player" ) { - ::requestDeferredCommandBufferSubmit = false; + if ( /*global*/::requestDeferredCommandBufferSubmit && controller.getName() == "Player" ) { + /*global*/::requestDeferredCommandBufferSubmit = false; uf::renderer::settings::defaultCommandBufferImmediate = false; UF_MSG_DEBUG("Defer command buffer submit requested"); } @@ -930,12 +931,12 @@ void UF_API uf::tick() { void UF_API uf::render() { if ( uf::scene::scenes.empty() ) return; - if ( ::sceneTransition.phase >= 0 ) { + if ( /*global*/::sceneTransition.phase >= 0 ) { return; } #if UF_USE_ULTRALIGHT - /* Ultralight-UX */ if ( ::config.engine.ext.ultralight.enabled ) { + /* Ultralight-UX */ if ( /*global*/::config.engine.ext.ultralight.enabled ) { ext::ultralight::render(); } #endif @@ -963,17 +964,17 @@ void UF_API uf::terminate() { spec::controller::terminate(); } #if UF_USE_VALL_E - if ( ::config.engine.ext.vall_e.enabled ) { + if ( /*global*/::config.engine.ext.vall_e.enabled ) { ext::vall_e::terminate(); } #endif #if UF_USE_IMGUI - if ( ::config.engine.ext.imgui.enabled ) { + if ( /*global*/::config.engine.ext.imgui.enabled ) { ext::imgui::terminate(); } #endif #if UF_USE_ULTRALIGHT - /* Ultralight-UX */ if ( ::config.engine.ext.ultralight.enabled ) { + /* Ultralight-UX */ if ( /*global*/::config.engine.ext.ultralight.enabled ) { ext::ultralight::terminate(); } #endif @@ -999,10 +1000,10 @@ void UF_API uf::terminate() { /* Kill physics */ { // uf::physics::terminate(); } - /* Garbage collection */ if ( ::config.engine.gc.enabled ) { - size_t collected = uf::instantiator::collect( ::config.engine.gc.mode ); + /* Garbage collection */ if ( /*global*/::config.engine.gc.enabled ) { + size_t collected = uf::instantiator::collect( /*global*/::config.engine.gc.mode ); if ( collected > 0 ) { - if ( ::config.engine.gc.announce ) UF_MSG_DEBUG("GC collected {} unused entities", (int) collected); + if ( /*global*/::config.engine.gc.announce ) UF_MSG_DEBUG("GC collected {} unused entities", (int) collected); } } @@ -1016,20 +1017,20 @@ void UF_API uf::terminate() { } #if UF_USE_OPENAL - /* Initialize OpenAL */ { + /* Initialize OpenAL */ if ( !uf::audio::muted ) { ext::al::destroy(); } #endif /* Print system stats */ { - ::times.total.time = times.sys.elapsed().asDouble(); - UF_MSG_DEBUG("System: Total Time: {} | Total Frames: {} | Average FPS: {}", ::times.total.time, ::times.total.frames, ::times.total.frames / ::times.total.time); + /*global*/::times.total.time = /*global*/::times.sys.elapsed().asDouble(); + UF_MSG_DEBUG("System: Total Time: {} | Total Frames: {} | Average FPS: {}", /*global*/::times.total.time, /*global*/::times.total.frames, /*global*/::times.total.frames / /*global*/::times.total.time); } /* Flush input buffer */ { - ::io.output << ::io.input << "\n"; - for ( const auto& str : uf::iostream.getHistory() ) ::io.output << str << "\n"; - ::io.output << "\nTerminated after " << times.sys.elapsed().asDouble() << " seconds" << "\n"; - ::io.output.close(); + /*global*/::io.output << /*global*/::io.input << "\n"; + for ( const auto& str : uf::iostream.getHistory() ) /*global*/::io.output << str << "\n"; + /*global*/::io.output << "\nTerminated after " << /*global*/::times.sys.elapsed().asDouble() << " seconds" << "\n"; + /*global*/::io.output.close(); } } \ No newline at end of file diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index 47ecb4ea..55def9bc 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -933,10 +933,9 @@ void uf::graph::process( pod::Graph& graph ) { // process nodes UF_DEBUG_TIMER_MULTITRACE("Processing nodes"); for ( auto index : graph.root.children ) { + UF_DEBUG_TIMER_MULTITRACE("Processing node: {}", graph.nodes[index].name); process( graph, index, *graph.root.entity ); - - auto& node = graph.nodes[index]; - if ( node.entity ) UF_DEBUG_TIMER_MULTITRACE("Processed node: {}", node.name); + UF_DEBUG_TIMER_MULTITRACE("Processed node: {}", graph.nodes[index].name); } // patch materials/textures @@ -1352,7 +1351,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) metadataJson["physics"]["center"] = uf::vector::encode( center ); metadataJson["physics"]["corner"] = uf::vector::encode( corner ); } - #if UF_GRAPH_EXTENDED + #if !UF_GRAPH_EXTENDED if ( type == "mesh" ) { auto& collider = entity.getComponent(); collider.stats.mass = phyziks["mass"].as(collider.stats.mass); diff --git a/engine/src/ext/lua/lua.cpp b/engine/src/ext/lua/lua.cpp index 1e84aa30..ecffd3c3 100644 --- a/engine/src/ext/lua/lua.cpp +++ b/engine/src/ext/lua/lua.cpp @@ -4,7 +4,7 @@ bool ext::lua::enabled = true; sol::state ext::lua::state; -uf::stl::string ext::lua::main = uf::io::root + "/scripts/main.lua"; +uf::stl::string ext::lua::main; uf::stl::unordered_map ext::lua::modules; #include diff --git a/engine/src/ext/opengl/opengl.cpp b/engine/src/ext/opengl/opengl.cpp index cfbe4d87..094814fb 100644 --- a/engine/src/ext/opengl/opengl.cpp +++ b/engine/src/ext/opengl/opengl.cpp @@ -77,7 +77,7 @@ GLhandle(VkColorSpaceKHR) ext::opengl::settings::formats::colorSpace; ext::opengl::enums::Format::type_t ext::opengl::settings::formats::color = ext::opengl::enums::Format::R8G8B8A8_UNORM; ext::opengl::enums::Format::type_t ext::opengl::settings::formats::depth = ext::opengl::enums::Format::D32_SFLOAT; -ext::opengl::Device ext::opengl::device; +ext::opengl::Device* ext::opengl::device = NULL; std::mutex ext::opengl::mutex; std::mutex ext::opengl::immediateModeMutex; @@ -505,6 +505,8 @@ void ext::opengl::destroy() { // swapchain.destroy(); device.destroy(); ext::opengl::mutex.unlock(); + + delete device; } void ext::opengl::synchronize( uint8_t flag ) { if ( flag & 0b01 ) { diff --git a/engine/src/ext/vulkan/device.cpp b/engine/src/ext/vulkan/device.cpp index 2c606917..037afbfc 100644 --- a/engine/src/ext/vulkan/device.cpp +++ b/engine/src/ext/vulkan/device.cpp @@ -746,6 +746,8 @@ VkQueue ext::vulkan::Device::getQueue( ext::vulkan::QueueEnum queueEnum ) { return this->getQueue( queueEnum, std::this_thread::get_id() ); } VkCommandPool ext::vulkan::Device::getCommandPool( ext::vulkan::QueueEnum queueEnum, std::thread::id id ) { + auto& device = *this; + uint32_t index{0}; uf::ThreadUnique* commandPool{NULL}; @@ -780,6 +782,8 @@ VkCommandPool ext::vulkan::Device::getCommandPool( ext::vulkan::QueueEnum queueE return pool; } VkQueue ext::vulkan::Device::getQueue( ext::vulkan::QueueEnum queueEnum, std::thread::id id ) { + auto& device = *this; + uint32_t index = 0; uf::ThreadUnique* commandPool{NULL}; switch ( queueEnum ) { @@ -810,7 +814,9 @@ VkQueue ext::vulkan::Device::getQueue( ext::vulkan::QueueEnum queueEnum, std::th return queue; } -void ext::vulkan::Device::initialize() { +void ext::vulkan::Device::initialize() { + auto& device = *this; + uf::stl::vector instanceLayers = { // "VK_LAYER_KHRONOS_synchronization2", }; diff --git a/engine/src/ext/vulkan/graphic.cpp b/engine/src/ext/vulkan/graphic.cpp index 7fb2e0f6..5ef281ad 100644 --- a/engine/src/ext/vulkan/graphic.cpp +++ b/engine/src/ext/vulkan/graphic.cpp @@ -1196,7 +1196,7 @@ bool ext::vulkan::Graphic::updateMesh( uf::Mesh& mesh ) { return rebuild; } void ext::vulkan::Graphic::generateBottomAccelerationStructures() { - auto& device = *this->device; // ext::vulkan::device; + auto& device = *this->device; bool update = !this->accelerationStructures.bottoms.empty(); VkPhysicalDeviceAccelerationStructurePropertiesKHR acclerationStructureProperties{}; diff --git a/engine/src/ext/vulkan/rendermodes/deferred.cpp b/engine/src/ext/vulkan/rendermodes/deferred.cpp index 7dd58143..0593e4bc 100644 --- a/engine/src/ext/vulkan/rendermodes/deferred.cpp +++ b/engine/src/ext/vulkan/rendermodes/deferred.cpp @@ -654,8 +654,8 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto cmdBufInfo.pNext = nullptr; VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device->queueFamilyIndices.graphics; //VK_QUEUE_FAMILY_IGNORED - imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device->queueFamilyIndices.graphics; //VK_QUEUE_FAMILY_IGNORED + imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics; //VK_QUEUE_FAMILY_IGNORED + imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics; //VK_QUEUE_FAMILY_IGNORED imageMemoryBarrier.subresourceRange.baseMipLevel = 0; imageMemoryBarrier.subresourceRange.levelCount = 1; imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; diff --git a/engine/src/ext/vulkan/texture.cpp b/engine/src/ext/vulkan/texture.cpp index 935ae1d5..b9f8506a 100644 --- a/engine/src/ext/vulkan/texture.cpp +++ b/engine/src/ext/vulkan/texture.cpp @@ -64,6 +64,7 @@ namespace { } ext::vulkan::Sampler ext::vulkan::Sampler::retrieve( const ext::vulkan::Sampler::Descriptor& info ) { + auto& device = ext::vulkan::device; ext::vulkan::Sampler sampler; for ( auto& s : samplers ) { if ( memcmp( &info, &s.descriptor, sizeof(info) - sizeof(VkDescriptorImageInfo) ) != 0 ) continue; @@ -79,7 +80,7 @@ ext::vulkan::Sampler ext::vulkan::Sampler::retrieve( const ext::vulkan::Sampler: { auto& s = samplers.emplace_back(); s.descriptor = info; - s.initialize( ext::vulkan::device ); + s.initialize( device ); sampler = s; } RETRIEVE_RETURN: diff --git a/engine/src/spec/context/linux.cpp b/engine/src/spec/context/linux.cpp new file mode 100644 index 00000000..84eaee7a --- /dev/null +++ b/engine/src/spec/context/linux.cpp @@ -0,0 +1,114 @@ +#include + +#if UF_ENV_LINUX && UF_USE_OPENGL && !UF_USE_OPENGL_GLDC +#include + +#include +#include +#include +#include + +spec::x11::Context::Context( uni::Context* shared, const Context::Settings& settings ) : + uni::Context(NULL, true, settings), + m_display(nullptr), + m_context(nullptr), + m_window(0) +{ + m_display = XOpenDisplay(NULL); + if (!m_display) { + std::cerr << "Failed to open X display\n"; + return; + } + + // Create a hidden dummy window + int scr = DefaultScreen(m_display); + + XSetWindowAttributes swa; + swa.event_mask = StructureNotifyMask; + m_window = XCreateSimpleWindow(m_display, RootWindow(m_display, scr), 0, 0, 1, 1, 0, 0, 0); + XMapWindow(m_display, m_window); + + this->create(shared); +} + +spec::x11::Context::Context( uni::Context* shared, const Context::Settings& settings, const Context::window_t& window ) : + uni::Context(NULL, false, settings), + m_display(window.getDisplay()), + m_context(nullptr), + m_window(window.getHandle()) +{ + if (m_display && m_window) + this->create(shared); +} + +spec::x11::Context::Context( uni::Context* shared, const Context::Settings& settings, unsigned int width, unsigned int height ) : + Context(shared, settings) { + // Nothing more; dummy window variant handles this +} + +spec::x11::Context::~Context() { + this->terminate(); +} + +void spec::x11::Context::create( uni::Context* shared ) { + int attribs[] = { + GLX_RGBA, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, (int)m_settings.depthBits, + GLX_STENCIL_SIZE, (int)m_settings.stencilBits, + None + }; + + int scr = DefaultScreen(m_display); + XVisualInfo* vi = glXChooseVisual(m_display, scr, attribs); + if (!vi) { + std::cerr << "Failed to choose visual\n"; + return; + } + + GLXContext sharedCtx = shared ? ((x11::Context*)shared)->m_context : nullptr; + m_context = glXCreateContext(m_display, vi, sharedCtx, True); + + if (!m_context) { + std::cerr << "glXCreateContext failed\n"; + } +} + +void spec::x11::Context::terminate() { + if (m_context) { + if (glXGetCurrentContext() == m_context) + glXMakeCurrent(m_display, None, NULL); + glXDestroyContext(m_display, m_context); + m_context = nullptr; + } + + if (m_display && m_window && m_ownsWindow) { + XDestroyWindow(m_display, m_window); + } + + if (m_display) { + XCloseDisplay(m_display); + m_display = nullptr; + } +} + +bool spec::x11::Context::makeCurrent() { + if (!m_display || !m_context || !m_window) return false; + return glXMakeCurrent(m_display, m_window, m_context); +} + +void spec::x11::Context::display() { + if (m_display && m_window) + glXSwapBuffers(m_display, m_window); +} + +void spec::x11::Context::setVerticalSyncEnabled(bool enabled) { + typedef int (*glXSwapIntervalEXTProc)(Display*, GLXDrawable, int); + static glXSwapIntervalEXTProc glXSwapIntervalEXT = + (glXSwapIntervalEXTProc) glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalEXT"); + + if (glXSwapIntervalEXT) { + glXSwapIntervalEXT(m_display, glXGetCurrentDrawable(), enabled ? 1 : 0); + } +} +#endif \ No newline at end of file diff --git a/engine/src/spec/controller/linux.cpp b/engine/src/spec/controller/linux.cpp new file mode 100644 index 00000000..2c47763e --- /dev/null +++ b/engine/src/spec/controller/linux.cpp @@ -0,0 +1,133 @@ +#include +#include + +#ifdef UF_ENV_LINUX +#if UF_USE_OPENVR + #include +#endif + +void spec::linux::controller::initialize() {} +void spec::linux::controller::tick() { +#if 0 && UF_USE_OPENVR + if ( ext::openvr::context ) { + uf::stl::string key = ""; + if ( name == "R_DPAD_UP" ) { i = vr::Controller_Hand::Hand_Right; key = "dpadUp"; } + else if ( name == "R_DPAD_DOWN" ) { i = vr::Controller_Hand::Hand_Right; key = "dpadDown"; } + else if ( name == "R_DPAD_LEFT" ) { i = vr::Controller_Hand::Hand_Right; key = "dpadLeft"; } + else if ( name == "R_DPAD_RIGHT" ) { i = vr::Controller_Hand::Hand_Right; key = "dpadRight"; } + else if ( name == "R_JOYSTICK" ) { i = vr::Controller_Hand::Hand_Right; key = "thumbclick"; } + else if ( name == "R_A" ) { i = vr::Controller_Hand::Hand_Right; key = "a"; } + else if ( name == "R_B" ) { i = vr::Controller_Hand::Hand_Right; key = "b"; } + else if ( name == "DPAD_UP" && i == vr::Controller_Hand::Hand_Right ) key = "dpadUp"; + else if ( name == "DPAD_DOWN" && i == vr::Controller_Hand::Hand_Right ) key = "dpadDown"; + else if ( name == "DPAD_LEFT" && i == vr::Controller_Hand::Hand_Right ) key = "dpadLeft"; + else if ( name == "DPAD_RIGHT" && i == vr::Controller_Hand::Hand_Right ) key = "dpadRight"; + else if ( name == "JOYSTICK" && i == vr::Controller_Hand::Hand_Right ) key = "thumbclick"; + else if ( name == "A" && i == vr::Controller_Hand::Hand_Right ) key = "a"; + else if ( name == "B" && i == vr::Controller_Hand::Hand_Right ) key = "b"; + + else if ( name == "L_DPAD_UP" ) { i = vr::Controller_Hand::Hand_Left; key = "dpadUp"; } + else if ( name == "L_DPAD_DOWN" ) { i = vr::Controller_Hand::Hand_Left; key = "dpadDown"; } + else if ( name == "L_DPAD_LEFT" ) { i = vr::Controller_Hand::Hand_Left; key = "dpadLeft"; } + else if ( name == "L_DPAD_RIGHT" ) { i = vr::Controller_Hand::Hand_Left; key = "dpadRight"; } + else if ( name == "L_JOYSTICK" ) { i = vr::Controller_Hand::Hand_Left; key = "thumbclick"; } + else if ( name == "L_A" ) { i = vr::Controller_Hand::Hand_Left; key = "a"; } + else if ( name == "L_B" ) { i = vr::Controller_Hand::Hand_Left; key = "b"; } + else if ( name == "DPAD_UP" && i == vr::Controller_Hand::Hand_Left ) key = "dpadUp"; + else if ( name == "DPAD_DOWN" && i == vr::Controller_Hand::Hand_Left ) key = "dpadDown"; + else if ( name == "DPAD_LEFT" && i == vr::Controller_Hand::Hand_Left ) key = "dpadLeft"; + else if ( name == "DPAD_RIGHT" && i == vr::Controller_Hand::Hand_Left ) key = "dpadRight"; + else if ( name == "JOYSTICK" && i == vr::Controller_Hand::Hand_Left ) key = "thumbclick"; + else if ( name == "A" && i == vr::Controller_Hand::Hand_Left ) key = "a"; + else if ( name == "B" && i == vr::Controller_Hand::Hand_Left ) key = "b"; + + if ( name != "" ) return ext::openvr::controllerState( i == 0 ? vr::Controller_Hand::Hand_Left : vr::Controller_Hand::Hand_Right, key )["state"].as(); + } + if ( ext::openvr::context ) { + size_t offset = 0; + uf::stl::string key = ""; + if ( name == "R_JOYSTICK_X" ) { i = vr::Controller_Hand::Hand_Right; key = "thumbstick"; offset = 0; } + else if ( name == "JOYSTICK_X" && i == vr::Controller_Hand::Hand_Right ) { key = "thumbstick"; offset = 0;} + else if ( name == "R_JOYSTICK_Y" ) { i = vr::Controller_Hand::Hand_Right; key = "thumbstick"; offset = 1; } + else if ( name == "JOYSTICK_Y" && i == vr::Controller_Hand::Hand_Right ) { key = "thumbstick"; offset = 1; } + + else if ( name == "L_JOYSTICK_X" ) { i = vr::Controller_Hand::Hand_Left; key = "thumbstick"; offset = 0; } + else if ( name == "JOYSTICK_X" && i == vr::Controller_Hand::Hand_Left ) { key = "thumbstick"; offset = 0;} + else if ( name == "L_JOYSTICK_Y" ) { i = vr::Controller_Hand::Hand_Left; key = "thumbstick"; offset = 1; } + else if ( name == "JOYSTICK_Y" && i == vr::Controller_Hand::Hand_Left ) { key = "thumbstick"; offset = 1; } + + if ( name != "" ) return ext::openvr::controllerState( i == 0 ? vr::Controller_Hand::Hand_Left : vr::Controller_Hand::Hand_Right, key )["analog"]["position"][offset].as(); + } +#endif +} +void spec::linux::controller::terminate() {} +bool spec::linux::controller::connected( size_t i ) { +#if UF_USE_OPENVR + if ( ext::openvr::controllerActive( i == 0 ? vr::Controller_Hand::Hand_Left : vr::Controller_Hand::Hand_Right ) ) return true; +#endif + return false; +} +/* +bool spec::linux::controller::pressed( const uf::stl::string& _name, size_t i ) { + uf::stl::string name = uf::string::uppercase(_name); +#if UF_USE_OPENVR + if ( ext::openvr::context ) { + uf::stl::string key = ""; + if ( name == "R_DPAD_UP" ) { i = vr::Controller_Hand::Hand_Right; key = "dpadUp"; } + else if ( name == "R_DPAD_DOWN" ) { i = vr::Controller_Hand::Hand_Right; key = "dpadDown"; } + else if ( name == "R_DPAD_LEFT" ) { i = vr::Controller_Hand::Hand_Right; key = "dpadLeft"; } + else if ( name == "R_DPAD_RIGHT" ) { i = vr::Controller_Hand::Hand_Right; key = "dpadRight"; } + else if ( name == "R_JOYSTICK" ) { i = vr::Controller_Hand::Hand_Right; key = "thumbclick"; } + else if ( name == "R_A" ) { i = vr::Controller_Hand::Hand_Right; key = "a"; } + else if ( name == "R_B" ) { i = vr::Controller_Hand::Hand_Right; key = "b"; } + else if ( name == "DPAD_UP" && i == vr::Controller_Hand::Hand_Right ) key = "dpadUp"; + else if ( name == "DPAD_DOWN" && i == vr::Controller_Hand::Hand_Right ) key = "dpadDown"; + else if ( name == "DPAD_LEFT" && i == vr::Controller_Hand::Hand_Right ) key = "dpadLeft"; + else if ( name == "DPAD_RIGHT" && i == vr::Controller_Hand::Hand_Right ) key = "dpadRight"; + else if ( name == "JOYSTICK" && i == vr::Controller_Hand::Hand_Right ) key = "thumbclick"; + else if ( name == "A" && i == vr::Controller_Hand::Hand_Right ) key = "a"; + else if ( name == "B" && i == vr::Controller_Hand::Hand_Right ) key = "b"; + + else if ( name == "L_DPAD_UP" ) { i = vr::Controller_Hand::Hand_Left; key = "dpadUp"; } + else if ( name == "L_DPAD_DOWN" ) { i = vr::Controller_Hand::Hand_Left; key = "dpadDown"; } + else if ( name == "L_DPAD_LEFT" ) { i = vr::Controller_Hand::Hand_Left; key = "dpadLeft"; } + else if ( name == "L_DPAD_RIGHT" ) { i = vr::Controller_Hand::Hand_Left; key = "dpadRight"; } + else if ( name == "L_JOYSTICK" ) { i = vr::Controller_Hand::Hand_Left; key = "thumbclick"; } + else if ( name == "L_A" ) { i = vr::Controller_Hand::Hand_Left; key = "a"; } + else if ( name == "L_B" ) { i = vr::Controller_Hand::Hand_Left; key = "b"; } + else if ( name == "DPAD_UP" && i == vr::Controller_Hand::Hand_Left ) key = "dpadUp"; + else if ( name == "DPAD_DOWN" && i == vr::Controller_Hand::Hand_Left ) key = "dpadDown"; + else if ( name == "DPAD_LEFT" && i == vr::Controller_Hand::Hand_Left ) key = "dpadLeft"; + else if ( name == "DPAD_RIGHT" && i == vr::Controller_Hand::Hand_Left ) key = "dpadRight"; + else if ( name == "JOYSTICK" && i == vr::Controller_Hand::Hand_Left ) key = "thumbclick"; + else if ( name == "A" && i == vr::Controller_Hand::Hand_Left ) key = "a"; + else if ( name == "B" && i == vr::Controller_Hand::Hand_Left ) key = "b"; + + if ( name != "" ) return ext::openvr::controllerState( i == 0 ? vr::Controller_Hand::Hand_Left : vr::Controller_Hand::Hand_Right, key )["state"].as(); + } +#endif + return false; +} +float spec::linux::controller::analog( const uf::stl::string& _name, size_t i ) { + uf::stl::string name = uf::string::uppercase(_name); +#if UF_USE_OPENVR + if ( ext::openvr::context ) { + size_t offset = 0; + uf::stl::string key = ""; + if ( name == "R_JOYSTICK_X" ) { i = vr::Controller_Hand::Hand_Right; key = "thumbstick"; offset = 0; } + else if ( name == "JOYSTICK_X" && i == vr::Controller_Hand::Hand_Right ) { key = "thumbstick"; offset = 0;} + else if ( name == "R_JOYSTICK_Y" ) { i = vr::Controller_Hand::Hand_Right; key = "thumbstick"; offset = 1; } + else if ( name == "JOYSTICK_Y" && i == vr::Controller_Hand::Hand_Right ) { key = "thumbstick"; offset = 1; } + + else if ( name == "L_JOYSTICK_X" ) { i = vr::Controller_Hand::Hand_Left; key = "thumbstick"; offset = 0; } + else if ( name == "JOYSTICK_X" && i == vr::Controller_Hand::Hand_Left ) { key = "thumbstick"; offset = 0;} + else if ( name == "L_JOYSTICK_Y" ) { i = vr::Controller_Hand::Hand_Left; key = "thumbstick"; offset = 1; } + else if ( name == "JOYSTICK_Y" && i == vr::Controller_Hand::Hand_Left ) { key = "thumbstick"; offset = 1; } + + if ( name != "" ) return ext::openvr::controllerState( i == 0 ? vr::Controller_Hand::Hand_Left : vr::Controller_Hand::Hand_Right, key )["analog"]["position"][offset].as(); + } +#endif + return 0; +} +*/ +#endif \ No newline at end of file diff --git a/engine/src/spec/terminal/linux.cpp b/engine/src/spec/terminal/linux.cpp new file mode 100644 index 00000000..e8244068 --- /dev/null +++ b/engine/src/spec/terminal/linux.cpp @@ -0,0 +1,47 @@ +#include + +#ifdef UF_ENV_LINUX + +#include +#include +#include +#include +#include + +void spec::Terminal::clear() { + spec::uni::Terminal::clear(); + std::cout << "\033[2J\033[H" << std::flush; +} + +void spec::Terminal::setLocale() { + spec::uni::Terminal::setLocale(); + std::setlocale(LC_ALL, "en_US.UTF-8"); +} + +void spec::Terminal::hide() { +/* + if (isatty(fileno(stdin))) { + struct termios tty; + if (tcgetattr(STDIN_FILENO, &tty) == 0) { + tty.c_lflag &= ~ECHO; + tcsetattr(STDIN_FILENO, TCSANOW, &tty); + } + } +*/ +} + +void spec::Terminal::show() { +/* + if (isatty(fileno(stdin))) { + struct termios tty; + if (tcgetattr(STDIN_FILENO, &tty) == 0) { + tty.c_lflag |= ECHO; + tcsetattr(STDIN_FILENO, TCSANOW, &tty); + } + } +*/ +} + + + +#endif \ No newline at end of file diff --git a/engine/src/spec/terminal/universal.cpp b/engine/src/spec/terminal/universal.cpp index 3ebfb310..1eba18d8 100644 --- a/engine/src/spec/terminal/universal.cpp +++ b/engine/src/spec/terminal/universal.cpp @@ -18,8 +18,7 @@ void spec::uni::Terminal::hide() { void spec::uni::Terminal::show() { } void spec::uni::Terminal::setLocale() { -#if UF_ENV_DREAMCAST -#else +#if !UF_ENV_DREAMCAST const char* locales[4] = { "", "C.utf8", "C", "POSIX" }; diff --git a/engine/src/spec/time/dreamcast.cpp b/engine/src/spec/time/dreamcast.cpp index 287a4d47..b509f37c 100644 --- a/engine/src/spec/time/dreamcast.cpp +++ b/engine/src/spec/time/dreamcast.cpp @@ -1,5 +1,5 @@ #include -#ifdef UF_ENV_DREAMCAST +#if 0 && UF_ENV_DREAMCAST #include spec::uni::Time::time_t spec::dreamcast::Time::getTime() { diff --git a/engine/src/spec/time/linux.cpp b/engine/src/spec/time/linux.cpp new file mode 100644 index 00000000..ee636567 --- /dev/null +++ b/engine/src/spec/time/linux.cpp @@ -0,0 +1,25 @@ +#include + +#if 0 && UF_ENV_LINUX +namespace { + typedef std::chrono::time_point chrono_time_t; + chrono_time_t getTimePoint() { + return std::chrono::system_clock::now(); + } + inline chrono_time_t& startPoint() { + static chrono_time_t start = getTimePoint(); // constructed on 1st use + return start; + } +} + +spec::uni::Time::time_t spec::linux::Time::getTime() { + std::chrono::duration elapsed = getTimePoint() - startPoint(); + return elapsed.count() * 1000000; +} +spec::uni::Time& spec::linux::Time::getUniversal() { + return (spec::uni::Time&) *this; +} + +spec::linux::Time spec::time; +spec::linux::Time::exp_t spec::linux::Time::unit = -6; +#endif \ No newline at end of file diff --git a/engine/src/spec/time/universal.cpp b/engine/src/spec/time/universal.cpp index 6a1d1532..d1a39174 100644 --- a/engine/src/spec/time/universal.cpp +++ b/engine/src/spec/time/universal.cpp @@ -2,20 +2,33 @@ #include -spec::uni::Time::exp_t spec::uni::Time::unit = -6; - namespace { typedef std::chrono::time_point chrono_time_t; chrono_time_t getTimePoint() { return std::chrono::system_clock::now(); } - chrono_time_t start = getTimePoint(); + inline chrono_time_t& startPoint() { + static chrono_time_t start = getTimePoint(); // constructed on 1st use + return start; + } } +spec::uni::time::time_t spec::uni::time::unixTime() { + return std::chrono::duration_cast(getTimePoint().time_since_epoch()).count(); +} +spec::uni::time::time_t spec::uni::time::getTime() { + std::chrono::duration elapsed = getTimePoint() - startPoint(); + return elapsed.count() * 1000000; +} + +/* spec::uni::Time::time_t spec::uni::Time::unixTime() { return std::chrono::duration_cast(getTimePoint().time_since_epoch()).count(); } spec::uni::Time::time_t spec::uni::Time::getTime() { - std::chrono::duration elapsed = getTimePoint() - start; + std::chrono::duration elapsed = getTimePoint() - startPoint(); return elapsed.count() * 1000000; -} \ No newline at end of file +} + +spec::uni::Time::exp_t spec::uni::Time::unit = -6; +*/ \ No newline at end of file diff --git a/engine/src/spec/time/windows.cpp b/engine/src/spec/time/windows.cpp index e0a680a4..aafc8f60 100644 --- a/engine/src/spec/time/windows.cpp +++ b/engine/src/spec/time/windows.cpp @@ -1,7 +1,6 @@ #include -#ifdef UF_ENV_WINDOWS - +#if 0 && UF_ENV_WINDOWS namespace { LARGE_INTEGER getFrequency() { LARGE_INTEGER freq; diff --git a/engine/src/spec/window/linux.cpp b/engine/src/spec/window/linux.cpp new file mode 100644 index 00000000..9f191127 --- /dev/null +++ b/engine/src/spec/window/linux.cpp @@ -0,0 +1,1287 @@ +#include + +#if UF_ENV_LINUX +#include +#include +#include +#include +#include +#include + +#define UF_OPENGL_CONTEXT_IN_WINDOW 0 +#include + +#define UF_HOOK_USE_USERDATA 1 +#define UF_HOOK_USE_JSON 0 + +#include +#include +#include +#include +#include + +#include +#include + +namespace { + Display *globalDisplay = nullptr; + int windowCount = 0; + + float lastMouseWheel; + + void initX11() { + if (!globalDisplay) { + globalDisplay = XOpenDisplay(nullptr); + if (!globalDisplay) { + UF_MSG_ERROR("Failed to open X11 display."); + exit(1); + } + } + ++windowCount; + } + void shutdownX11() { + if (--windowCount == 0 && globalDisplay) { + XCloseDisplay(globalDisplay); + globalDisplay = nullptr; + } + } + struct MonitorInfo { + int x, y; // top-left offset + int width; + int height; + bool primary; + }; + + uf::stl::vector getMonitors(Display *display, int screen) { + uf::stl::vector monitors; + + Window root = RootWindow(display, screen); + + XRRScreenResources *res = XRRGetScreenResourcesCurrent(display, root); + if (!res) return monitors; + + RROutput primary = XRRGetOutputPrimary(display, root); + + for (int i = 0; i < res->noutput; i++) { + XRROutputInfo *output_info = + XRRGetOutputInfo(display, res, res->outputs[i]); + if (!output_info || output_info->connection == RR_Disconnected) { + if (output_info) XRRFreeOutputInfo(output_info); + continue; + } + + XRRCrtcInfo *crtc_info = + XRRGetCrtcInfo(display, res, output_info->crtc); + if (!crtc_info) { + XRRFreeOutputInfo(output_info); + continue; + } + + MonitorInfo info; + info.x = crtc_info->x; + info.y = crtc_info->y; + info.width = crtc_info->width; + info.height = crtc_info->height; + info.primary = (res->outputs[i] == primary); + + monitors.push_back(info); + + XRRFreeCrtcInfo(crtc_info); + XRRFreeOutputInfo(output_info); + } + + XRRFreeScreenResources(res); + return monitors; + } + + #if 0 + float getXftDPI(Display* dpy) { + char* rms = XResourceManagerString(dpy); + if (!rms) return 0.0f; + + XrmInitialize(); + XrmDatabase db = XrmGetStringDatabase(rms); + if (!db) return 0.0f; + + XrmValue value; + char* type = nullptr; + if (XrmGetResource(db, "Xft.dpi", "Xft.dpi", &type, &value)) { + if (value.addr) { + return std::atof(value.addr); + } + } + return 0.0f; + } + + float getRandRDPI(Display* dpy, int screen) { + Window root = RootWindow(dpy, screen); + XRRScreenResources* res = XRRGetScreenResourcesCurrent(dpy, root); + if (!res) return 96.0f; + + XRROutputInfo* out_info = XRRGetOutputInfo(dpy, res, res->outputs[0]); + XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(dpy, res, out_info->crtc); + + double mmWidth = out_info->mm_width; + double mmHeight = out_info->mm_height; + double width = crtc_info->width; + double height = crtc_info->height; + + double dpiX = (width * 25.4) / mmWidth; // pixels per inch + double dpiY = (height * 25.4) / mmHeight; + + XRRFreeCrtcInfo(crtc_info); + XRRFreeOutputInfo(out_info); + XRRFreeScreenResources(res); + + return (dpiX + dpiY) * 0.5; // average + } + #endif + + uf::stl::string KeySymToString(KeySym sym) { + char *name = XKeysymToString(sym); + return name ? uf::stl::string(name) : "?"; + } + + static uf::stl::string NormalizeKeyName(const uf::stl::string &raw) { + if (raw == "Shift_L") return "LShift"; + if (raw == "Shift_R") return "RShift"; + if (raw == "Control_L") return "LControl"; + if (raw == "Control_R") return "RControl"; + if (raw == "Alt_L") return "LAlt"; + if (raw == "Alt_R") return "RAlt"; + if (raw == "Super_L") return "LSystem"; + if (raw == "Super_R") return "RSystem"; + // translate "Return" -> "Enter" + if (raw == "Return") return "Enter"; + return raw; + } +} // namespace + +spec::x11::Window::Window() + : m_display(nullptr), + m_screen(0), + m_handle(0), + m_gc(0), + m_xim(nullptr), + m_xic(nullptr), + m_context(nullptr), + m_lastSize({}), + m_keyRepeatEnabled(true), + m_resizing(false), + m_mouseGrabbed(false), + m_syncParse(true), + m_asyncParse(false) {} + +spec::x11::Window::Window(const vector_t &size, const title_t &title) + : Window() { + create(size, title); +} + +void spec::x11::Window::create(const vector_t &size, const title_t &title) { + initX11(); + + m_display = globalDisplay; + m_screen = DefaultScreen(m_display); + + int dimW = size.x > 0 ? size.x : DisplayWidth(m_display, m_screen); + int dimH = size.y > 0 ? size.y : DisplayHeight(m_display, m_screen); + + m_handle = XCreateSimpleWindow( + m_display, RootWindow(m_display, m_screen), 0, 0, dimW, dimH, 1, + BlackPixel(m_display, m_screen), WhitePixel(m_display, m_screen)); + + // Input events mask + XSelectInput(m_display, m_handle, + ExposureMask | KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | PointerMotionMask | + StructureNotifyMask | FocusChangeMask); + + // Set Title + setTitle(title); + + m_gc = XCreateGC(m_display, m_handle, 0, nullptr); + + m_xim = XOpenIM(m_display, nullptr, nullptr, nullptr); + if (m_xim == nullptr) { + XSetLocaleModifiers("@im=none"); + m_xim = XOpenIM(m_display, nullptr, nullptr, nullptr); + } + m_xic = XCreateIC(m_xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, m_handle, XNFocusWindow, m_handle, NULL); + + Atom WM_DELETE_WINDOW = XInternAtom(m_display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(m_display, m_handle, &WM_DELETE_WINDOW, 1); + + XMapWindow(m_display, m_handle); + XFlush(m_display); + +#if UF_USE_OPENGL && UF_OPENGL_CONTEXT_IN_WINDOW + m_context = (void *)spec::uni::Context::create(settings, *this); +#endif +} + +spec::x11::Window::~Window() { terminate(); } + +void spec::x11::Window::terminate() { + if (m_handle) { + XDestroyWindow(m_display, m_handle); + m_handle = 0; + } + shutdownX11(); + +#if UF_OPENGL_CONTEXT_IN_WINDOW + if (m_context) { + spec::Context *context = (spec::Context *)m_context; + context->terminate(); + delete context; + m_context = NULL; + } +#endif +} + +spec::x11::Window::handle_t spec::x11::Window::getHandle() const { + return m_handle; +} + +spec::x11::Window::vector_t spec::x11::Window::getPosition() const { + int x, y; + ::Window child; + XTranslateCoordinates(m_display, m_handle, RootWindow(m_display, m_screen), + 0, 0, &x, &y, &child); + return {(unsigned)x, (unsigned)y}; +} + +spec::x11::Window::vector_t spec::x11::Window::getSize() const { + XWindowAttributes attr; + XGetWindowAttributes(m_display, m_handle, &attr); + return {(unsigned)attr.width, (unsigned)attr.height}; +} + +/* + +float spec::x11::Window::getDPIScale() const { + if (!m_display) return 1.0f; + + float dpi = getXftDPI(m_display); + if (dpi <= 0.0f) dpi = getRandRDPI(m_display, m_screen); + if (dpi <= 0.0f) dpi = 96.0f; + + return dpi / 96.0f; // scale relative to Win32 baseline +} + +*/ + +size_t spec::x11::Window::getRefreshRate() const { + return 60; + /* + if (!m_display) return 60; // safe default + + Window root = RootWindow(m_display, m_screen); + + XRRScreenResources* res = XRRGetScreenResourcesCurrent(m_display, root); + if (!res) return 60; + + RROutput primary = XRRGetOutputPrimary(m_display, root); + if (primary == None && res->noutput > 0) + primary = res->outputs[0]; // fallback + + XRROutputInfo* output_info = XRRGetOutputInfo(m_display, res, primary); + if (!output_info) { + XRRFreeScreenResources(res); + return 60; + } + + XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(m_display, res, + output_info->crtc); + + size_t refresh = 60; // default fallback + if (crtc_info) { + for (int i = 0; i < res->nmode; i++) { + XRRModeInfo& mode = res->modes[i]; + if (mode.id == crtc_info->mode) { + if (mode.hTotal > 0 && mode.vTotal > 0) { + double rate = (double)mode.dotClock / + ((double)mode.hTotal * + (double)mode.vTotal); refresh = static_cast(std::round(rate)); + } + break; + } + } + XRRFreeCrtcInfo(crtc_info); + } + + XRRFreeOutputInfo(output_info); + XRRFreeScreenResources(res); + + return refresh; + */ +} + +void spec::x11::Window::setPosition(const vector_t &pos) { + XMoveWindow(m_display, m_handle, pos.x, pos.y); +} + +bool spec::x11::Window::isKeyPressed(const uf::stl::string &key) { + if (!uf::Window::focused) return false; + + auto m_display = ::globalDisplay; + + // Query raw keyboard state + char keys[32]; + XQueryKeymap(m_display, keys); + + // Convert friendly name (string) into KeySym + // KeySym sym = XStringToKeysym(key.c_str()); + KeySym sym = XStringToKeysym(NormalizeKeyName(key).c_str()); + if (sym == NoSymbol) return false; + + // Translate KeySym → KeyCode + KeyCode kc = XKeysymToKeycode(m_display, sym); + if (kc == 0) return false; + + // Each bit represents a key state + return (keys[kc >> 3] & (1 << (kc & 7))) != 0; +} + +void spec::x11::Window::centerWindow() { + auto monitors = getMonitors(m_display, m_screen); + if (monitors.empty()) return; + + // Current window geometry + XWindowAttributes attr; + XGetWindowAttributes(m_display, m_handle, &attr); + int winCenterX = attr.x + attr.width / 2; + int winCenterY = attr.y + attr.height / 2; + + // Find monitor containing window center + MonitorInfo target = monitors[0]; + for (auto &mon : monitors) { + if (winCenterX >= mon.x && winCenterX < mon.x + mon.width && + winCenterY >= mon.y && winCenterY < mon.y + mon.height) { + target = mon; + break; + } + } + + // Center window within this monitor + auto size = getSize(); + int newX = target.x + (target.width - size.x) / 2; + int newY = target.y + (target.height - size.y) / 2; + + setPosition({(unsigned)newX, (unsigned)newY}); +} + +void spec::x11::Window::setMousePosition(const vector_t &pos) { + XWarpPointer(m_display, None, m_handle, 0, 0, 0, 0, pos.x, pos.y); + XFlush(m_display); +} + +spec::x11::Window::vector_t spec::x11::Window::getMousePosition() { + int root_x, root_y, win_x, win_y; + unsigned mask; + ::Window child, root; + XQueryPointer(m_display, m_handle, &root, &child, &root_x, &root_y, &win_x, + &win_y, &mask); + return {(unsigned)win_x, (unsigned)win_y}; +} + +void spec::x11::Window::setSize(const vector_t &size) { + XResizeWindow(m_display, m_handle, size.x, size.y); +} + +void spec::x11::Window::setTitle(const title_t &title) { + XStoreName(m_display, m_handle, (const char *)title.c_str()); +} +/* +void spec::x11::Window::setIcon(const vector_t&, uint8_t*) { + // Convert RGBA8 -> ARGB32 (unsigned long) + uf::stl::vector icon; + icon.resize(2 + size.x * size.y); + icon[0] = size.x; + icon[1] = size.y; + + for (int i = 0; i < size.x * size.y; i++) { + unsigned char r = pixels[i*4+0]; + unsigned char g = pixels[i*4+1]; + unsigned char b = pixels[i*4+2]; + unsigned char a = pixels[i*4+3]; + unsigned long argb = ((unsigned long)a << 24) | + ((unsigned long)r << 16) | + ((unsigned long)g << 8) | + ((unsigned long)b); + icon[2+i] = argb; + } + + Atom property = XInternAtom(m_display, "_NET_WM_ICON", False); + XChangeProperty(m_display, m_handle, property, XA_CARDINAL, 32, +PropModeReplace, (unsigned char*)icon.data(), icon.size()); XFlush(m_display); +} +*/ +void spec::x11::Window::setIcon(const vector_t &size, uint8_t *pixels) { + // Convert RGBA8 (incoming) into ARGB32 (required by _NET_WM_ICON) + uf::stl::vector icon; + icon.resize(2 + size.x * size.y); + icon[0] = size.x; + icon[1] = size.y; + + for (size_t i = 0; i < size.x * size.y; i++) { + uint8_t r = pixels[i * 4 + 0]; + uint8_t g = pixels[i * 4 + 1]; + uint8_t b = pixels[i * 4 + 2]; + uint8_t a = pixels[i * 4 + 3]; + + // Pack ARGB as required by X + unsigned long argb = ((unsigned long)a << 24) | + ((unsigned long)r << 16) | + ((unsigned long)g << 8) | ((unsigned long)b); + + icon[2 + i] = argb; + } + + Atom property = XInternAtom(m_display, "_NET_WM_ICON", False); + XChangeProperty( + m_display, m_handle, property, XA_CARDINAL, 32, PropModeReplace, + reinterpret_cast(icon.data()), icon.size()); + XFlush(m_display); +} +void spec::x11::Window::setVisible(bool vis) { + if (vis) + XMapWindow(m_display, m_handle); + else + XUnmapWindow(m_display, m_handle); + XFlush(m_display); +} + +/* +void spec::x11::Window::setCursorVisible(bool vis) { + if (!vis) { + // hide cursor + Pixmap bm_no; + XColor black; memset(&black, 0, sizeof(black)); + char no_data[] = { 0 }; + bm_no = XCreateBitmapFromData(m_display, m_handle, no_data, 1, 1); + Cursor invisible = XCreatePixmapCursor(m_display, bm_no, bm_no, + &black, &black, 0, 0); + XDefineCursor(m_display, m_handle, invisible); + XFreeCursor(m_display, invisible); + } else { + XUndefineCursor(m_display, m_handle); + } +} +*/ + +void spec::x11::Window::setCursorVisible(bool visibility) { + if (visibility) { + // Restore default cursor + XUndefineCursor(m_display, m_handle); + XFlush(m_display); + } else { + // Create an invisible cursor (1x1 transparent) + Pixmap bm_no; + XColor dummy; + static char no_data[] = {0}; + bm_no = XCreateBitmapFromData(m_display, m_handle, no_data, 1, 1); + dummy.red = dummy.green = dummy.blue = 0; + + Cursor invisible = + XCreatePixmapCursor(m_display, bm_no, bm_no, &dummy, &dummy, 0, 0); + XDefineCursor(m_display, m_handle, invisible); + XFreeCursor(m_display, invisible); + XFreePixmap(m_display, bm_no); + XFlush(m_display); + } +} + +/* +#include + +void spec::x11::Window::setCustomCursor(const vector_t& size, uint8_t* pixels) { + XcursorImage* img = XcursorImageCreate(size.x, size.y); + if (!img) return; + + img->xhot = size.x / 2; // center hot spot + img->yhot = size.y / 2; + + // Fill ARGB pixels + for (size_t i = 0; i < size.x * size.y; i++) { + uint8_t r = pixels[i * 4 + 0]; + uint8_t g = pixels[i * 4 + 1]; + uint8_t b = pixels[i * 4 + 2]; + uint8_t a = pixels[i * 4 + 3]; + + img->pixels[i] = ((unsigned long)a << 24) | + ((unsigned long)r << 16) | + ((unsigned long)g << 8) | + ((unsigned long)b); + } + + Cursor cursor = XcursorImageLoadCursor(m_display, img); + XcursorImageDestroy(img); + + XDefineCursor(m_display, m_handle, cursor); + XFreeCursor(m_display, cursor); + XFlush(m_display); +} +*/ + +void spec::x11::Window::setKeyRepeatEnabled(bool state) { + m_keyRepeatEnabled = state; +} + +void spec::x11::Window::setMouseGrabbed(bool state) { + this->m_mouseGrabbed = state; + this->grabMouse(state); +} + +void spec::x11::Window::grabMouse(bool state) { + if (state) { + // Grab pointer: locks input focus to this window + int result = XGrabPointer( + m_display, m_handle, + True, // owner_events + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, + m_handle, // confine to this window + None, CurrentTime); + + if (result != GrabSuccess) { + UF_MSG_ERROR("Failed to grab mouse pointer via XGrabPointer"); + } + + // Optionally grab keyboard too (rare, but possible) + // XGrabKeyboard(m_display, m_handle, True, GrabModeAsync, + // GrabModeAsync, CurrentTime); + + } else { + // Release + XUngrabPointer(m_display, CurrentTime); + // XUngrabKeyboard(m_display, CurrentTime); + } + + XFlush(m_display); +} + +void spec::x11::Window::requestFocus() { + XSetInputFocus(m_display, m_handle, RevertToParent, CurrentTime); +} + +bool spec::x11::Window::hasFocus() const { + ::Window focused; + int revert; + XGetInputFocus(m_display, &focused, &revert); + return focused == m_handle; +} + +void spec::x11::Window::bufferInputs() { + uf::Window::focused = this->hasFocus(); + + char keys[32]; + XQueryKeymap(m_display, keys); + +#define GET_KEYSTATE(SYMBOL) \ + (uf::Window::focused && \ + (keys[XKeysymToKeycode(m_display, SYMBOL) >> 3] & \ + (1 << (XKeysymToKeycode(m_display, SYMBOL) & 7)))) + +#define STORE_KEYSTATE(K, SYM) uf::inputs::kbm::states::K = GET_KEYSTATE(SYM); + + STORE_KEYSTATE(LShift, XK_Shift_L); + STORE_KEYSTATE(RShift, XK_Shift_R); + STORE_KEYSTATE(LAlt, XK_Alt_L); + STORE_KEYSTATE(RAlt, XK_Alt_R); + STORE_KEYSTATE(LControl, XK_Control_L); + STORE_KEYSTATE(RControl, XK_Control_R); + STORE_KEYSTATE(LSystem, XK_Super_L); + STORE_KEYSTATE(RSystem, XK_Super_R); + + STORE_KEYSTATE(Menu, XK_Menu); + STORE_KEYSTATE(SemiColon, XK_semicolon); + STORE_KEYSTATE(Slash, XK_slash); + STORE_KEYSTATE(Equal, XK_equal); + STORE_KEYSTATE(Dash, XK_minus); + STORE_KEYSTATE(LBracket, XK_bracketleft); + STORE_KEYSTATE(RBracket, XK_bracketright); + STORE_KEYSTATE(Comma, XK_comma); + STORE_KEYSTATE(Period, XK_period); + STORE_KEYSTATE(Quote, XK_apostrophe); + STORE_KEYSTATE(BackSlash, XK_backslash); + STORE_KEYSTATE(Tilde, XK_grave); + + STORE_KEYSTATE(Escape, XK_Escape); + STORE_KEYSTATE(Space, XK_space); + STORE_KEYSTATE(Enter, XK_Return); + STORE_KEYSTATE(BackSpace, XK_BackSpace); + STORE_KEYSTATE(Tab, XK_Tab); + STORE_KEYSTATE(PageUp, XK_Page_Up); + STORE_KEYSTATE(PageDown, XK_Page_Down); + STORE_KEYSTATE(End, XK_End); + STORE_KEYSTATE(Home, XK_Home); + STORE_KEYSTATE(Insert, XK_Insert); + STORE_KEYSTATE(Delete, XK_Delete); + + STORE_KEYSTATE(Add, XK_KP_Add); + STORE_KEYSTATE(Subtract, XK_KP_Subtract); + STORE_KEYSTATE(Multiply, XK_KP_Multiply); + STORE_KEYSTATE(Divide, XK_KP_Divide); + STORE_KEYSTATE(Pause, XK_Pause); + + // Function keys + STORE_KEYSTATE(F1, XK_F1); + STORE_KEYSTATE(F2, XK_F2); + STORE_KEYSTATE(F3, XK_F3); + STORE_KEYSTATE(F4, XK_F4); + STORE_KEYSTATE(F5, XK_F5); + STORE_KEYSTATE(F6, XK_F6); + STORE_KEYSTATE(F7, XK_F7); + STORE_KEYSTATE(F8, XK_F8); + STORE_KEYSTATE(F9, XK_F9); + STORE_KEYSTATE(F10, XK_F10); + STORE_KEYSTATE(F11, XK_F11); + STORE_KEYSTATE(F12, XK_F12); + STORE_KEYSTATE(F13, XK_F13); + STORE_KEYSTATE(F14, XK_F14); + STORE_KEYSTATE(F15, XK_F15); + + // Arrow keys + STORE_KEYSTATE(Left, XK_Left); + STORE_KEYSTATE(Right, XK_Right); + STORE_KEYSTATE(Up, XK_Up); + STORE_KEYSTATE(Down, XK_Down); + + // Numpad + STORE_KEYSTATE(Numpad0, XK_KP_0); + STORE_KEYSTATE(Numpad1, XK_KP_1); + STORE_KEYSTATE(Numpad2, XK_KP_2); + STORE_KEYSTATE(Numpad3, XK_KP_3); + STORE_KEYSTATE(Numpad4, XK_KP_4); + STORE_KEYSTATE(Numpad5, XK_KP_5); + STORE_KEYSTATE(Numpad6, XK_KP_6); + STORE_KEYSTATE(Numpad7, XK_KP_7); + STORE_KEYSTATE(Numpad8, XK_KP_8); + STORE_KEYSTATE(Numpad9, XK_KP_9); + + // Letters + STORE_KEYSTATE(Q, XK_Q); + STORE_KEYSTATE(W, XK_W); + STORE_KEYSTATE(E, XK_E); + STORE_KEYSTATE(R, XK_R); + STORE_KEYSTATE(T, XK_T); + STORE_KEYSTATE(Y, XK_Y); + STORE_KEYSTATE(U, XK_U); + STORE_KEYSTATE(I, XK_I); + STORE_KEYSTATE(O, XK_O); + STORE_KEYSTATE(P, XK_P); + + STORE_KEYSTATE(A, XK_A); + STORE_KEYSTATE(S, XK_S); + STORE_KEYSTATE(D, XK_D); + STORE_KEYSTATE(F, XK_F); + STORE_KEYSTATE(G, XK_G); + STORE_KEYSTATE(H, XK_H); + STORE_KEYSTATE(J, XK_J); + STORE_KEYSTATE(K, XK_K); + STORE_KEYSTATE(L, XK_L); + + STORE_KEYSTATE(Z, XK_Z); + STORE_KEYSTATE(X, XK_X); + STORE_KEYSTATE(C, XK_C); + STORE_KEYSTATE(V, XK_V); + STORE_KEYSTATE(B, XK_B); + STORE_KEYSTATE(N, XK_N); + STORE_KEYSTATE(M, XK_M); + + STORE_KEYSTATE(Num1, XK_1); + STORE_KEYSTATE(Num2, XK_2); + STORE_KEYSTATE(Num3, XK_3); + STORE_KEYSTATE(Num4, XK_4); + STORE_KEYSTATE(Num5, XK_5); + STORE_KEYSTATE(Num6, XK_6); + STORE_KEYSTATE(Num7, XK_7); + STORE_KEYSTATE(Num8, XK_8); + STORE_KEYSTATE(Num9, XK_9); + STORE_KEYSTATE(Num0, XK_0); + + // Query mouse button states + ::Window root, child; + int rootX, rootY, winX, winY; + unsigned int mask; + XQueryPointer(m_display, m_handle, &root, &child, &rootX, &rootY, &winX, + &winY, &mask); + + uf::inputs::kbm::states::Mouse1 = (mask & Button1Mask); // Left + uf::inputs::kbm::states::Mouse2 = (mask & Button3Mask); // Right + uf::inputs::kbm::states::Mouse3 = (mask & Button2Mask); // Middle + + uf::inputs::kbm::states::MouseWheel = + uf::Window::focused ? ::lastMouseWheel : 0; + ::lastMouseWheel = 0; +} +/* +void spec::x11::Window::processEvents() { + uf::stl::string prettyName = NormalizeKeyName( KeySymToString( sym ) ); + while (XPending(m_display) > 0) { + XEvent ev; + XNextEvent(m_display, &ev); + + switch (ev.type) { + case ConfigureNotify: { + vector_t newSize = { (unsigned)ev.xconfigure.width, +(unsigned)ev.xconfigure.height }; if (newSize != m_lastSize) { m_lastSize = +newSize; pod::payloads::windowResized event { { "window:Resized", "os" }, { +m_lastSize } + }; + this->pushEvent(event.type, event); + } + } break; + + case DestroyNotify: { + pod::payloads::windowEvent event { "window:Closed", "os" }; + this->pushEvent(event.type, event); + this->terminate(); + } break; + + case FocusIn: { + pod::payloads::windowFocusedChanged event { + { "window:Focus.Changed", "os" }, + { 1 } // gained + }; + this->pushEvent(event.type, event); + } break; + case FocusOut: { + pod::payloads::windowFocusedChanged event { + { "window:Focus.Changed", "os" }, + { -1 } // lost + }; + this->pushEvent(event.type, event); + } break; + + case KeyPress: { + KeySym sym; + char buffer[32]; + Status status; + int len = Xutf8LookupString(m_xic, &ev.xkey, buffer, +sizeof(buffer)-1, &sym, &status); buffer[len] = '\0'; + + // Key info + uf::stl::string keycode = KeySymToString(sym); + + pod::payloads::windowKey event{ + { "window:Key", "os" }, + { + keycode, + (unsigned long)sym, + -1, // pressed + false, + { + .alt = (ev.xkey.state & Mod1Mask), + .ctrl = (ev.xkey.state & ControlMask), + .shift= (ev.xkey.state & ShiftMask), + .sys = (ev.xkey.state & Mod4Mask) + } + } + }; + this->pushEvent(event.type, event); + this->pushEvent(event.type + "." + keycode, event); + + // Text input (if UTF-8 char is present) + if (len > 0) { + uf::stl::string utf8(buffer); + pod::payloads::windowTextEntered textEvent{ + { "window:Text.Entered", "os" }, + { (uint32_t)utf8[0], utf8 } // simplistic, full UTF-32 +conversion recommended + }; + this->pushEvent(textEvent.type, textEvent); + } + } break; + + case KeyRelease: { + KeySym sym = XLookupKeysym(&ev.xkey, 0); + uf::stl::string keycode = KeySymToString(sym); + + pod::payloads::windowKey event{ + { "window:Key", "os" }, + { + keycode, + (unsigned long)sym, + 1, // released + false, + { + .alt = (ev.xkey.state & Mod1Mask), + .ctrl = (ev.xkey.state & ControlMask), + .shift= (ev.xkey.state & ShiftMask), + .sys = (ev.xkey.state & Mod4Mask) + } + } + }; + this->pushEvent(event.type, event); + this->pushEvent(event.type + "." + keycode, event); + } break; + + case ButtonPress: { + pod::Vector2i pos = { ev.xbutton.x, ev.xbutton.y }; + int state = -1; + uf::stl::string button; + + switch (ev.xbutton.button) { + case 1: button = "Left"; break; + case 2: button = "Middle"; break; + case 3: button = "Right"; break; + case 8: // aux typical + case 9: button = "Aux"; break; + case 4: { // wheel up + pod::payloads::windowMouseWheel wheelEvent { + { "window:Mouse.Wheel", "os" }, + { { (unsigned)pos.x, (unsigned)pos.y }, +120 } + }; + this->pushEvent(wheelEvent.type, wheelEvent); + continue; + } + case 5: { // wheel down + pod::payloads::windowMouseWheel wheelEvent { + { "window:Mouse.Wheel", "os" }, + { { (unsigned)pos.x, (unsigned)pos.y }, -120 } + }; + this->pushEvent(wheelEvent.type, wheelEvent); + continue; + } + } + + pod::payloads::windowMouseClick event{ + { "window:Mouse.Click", "os" }, + { pos, {0,0}, button, state } + }; + this->pushEvent(event.type, event); + } break; + + case ButtonRelease: { + pod::Vector2i pos = { ev.xbutton.x, ev.xbutton.y }; + int state = 1; + uf::stl::string button; + + switch (ev.xbutton.button) { + case 1: button = "Left"; break; + case 2: button = "Middle"; break; + case 3: button = "Right"; break; + case 8: + case 9: button = "Aux"; break; + } + + pod::payloads::windowMouseClick event{ + { "window:Mouse.Click", "os" }, + { pos, {0,0}, button, state } + }; + this->pushEvent(event.type, event); + } break; + case MotionNotify: { + static pod::Vector2i lastPosition{}; + pod::Vector2i current = { ev.xmotion.x, ev.xmotion.y }; + + // Hard clamp inside window bounds + auto winSize = getSize(); + bool clamped = false; + if (current.x < 0) { current.x = 0; clamped = true; } + if (current.y < 0) { current.y = 0; clamped = true; } + if (current.x >= (int)winSize.x) { current.x = winSize.x-1; +clamped = true; } if (current.y >= (int)winSize.y) { current.y = winSize.y-1; +clamped = true; } + + if (clamped) { + // Warp pointer back into client area + XWarpPointer(m_display, None, m_handle, 0,0,0,0, current.x, +current.y); XFlush(m_display); + } + + // Fire payload as usual + pod::payloads::windowMouseMoved event { + { { "window:Mouse.Moved", "client" }, { winSize } }, + { current, current - lastPosition, 0 } + }; + this->pushEvent(event.type, event); + + lastPosition = current; + } break; + case ClientMessage: { + if ((Atom)ev.xclient.data.l[0] == XInternAtom(m_display, +"WM_DELETE_WINDOW", False)) { pod::payloads::windowEvent event { +"window:Closed", "os" }; this->pushEvent(event.type, event); this->terminate(); + } + } break; + } + } +} +*/ +void spec::x11::Window::processEvents() { + while (XPending(m_display) > 0) { + XEvent ev; + XNextEvent(m_display, &ev); + + switch (ev.type) { + case ConfigureNotify: { + vector_t newSize = {(unsigned)ev.xconfigure.width, + (unsigned)ev.xconfigure.height}; + if (newSize != m_lastSize) { + m_lastSize = newSize; + pod::payloads::windowResized event{{"window:Resized", "os"}, + {m_lastSize}}; + this->pushEvent(event.type, event); + } else { + pod::payloads::windowEvent moved{"window:Moved", "os"}; + this->pushEvent(moved.type, moved); + } + } break; + + case ClientMessage: { + Atom wmDelete = + XInternAtom(m_display, "WM_DELETE_WINDOW", False); + if ((Atom)ev.xclient.data.l[0] == wmDelete) { + pod::payloads::windowEvent event{"window:Closed", "os"}; + this->pushEvent(event.type, event); + this->terminate(); + } + } break; + + case FocusIn: { + pod::payloads::windowFocusedChanged focusEvent{ + {"window:Focus.Changed", "os"}, {1}}; + this->pushEvent(focusEvent.type, focusEvent); + } break; + + case FocusOut: { + pod::payloads::windowFocusedChanged focusEvent{ + {"window:Focus.Changed", "os"}, {-1}}; + this->pushEvent(focusEvent.type, focusEvent); + } break; + + case KeyPress: { + KeySym sym; + char buffer[32]; + Status status; + int len = Xutf8LookupString(m_xic, &ev.xkey, buffer, + sizeof(buffer) - 1, &sym, &status); + buffer[len] = '\0'; + + uf::stl::string keyName = NormalizeKeyName(KeySymToString(sym)); + + pod::payloads::windowKey keyEvent{ + {"window:Key", "os"}, + {keyName, + (unsigned long)sym, + -1, // pressed + false, + {.alt = (ev.xkey.state & Mod1Mask), + .ctrl = (ev.xkey.state & ControlMask), + .shift = (ev.xkey.state & ShiftMask), + .sys = (ev.xkey.state & Mod4Mask)}}}; + this->pushEvent(keyEvent.type, keyEvent); + this->pushEvent(keyEvent.type + "." + keyName, keyEvent); + + if (len > 0) { + uf::stl::string utf8(buffer); + uint32_t codepoint = + (uint8_t)utf8[0]; // simple, but can be upgraded + pod::payloads::windowTextEntered textEvent{ + {"window:Text.Entered", "os"}, {codepoint, utf8}}; + this->pushEvent(textEvent.type, textEvent); + } + } break; + + case KeyRelease: { + KeySym sym = XLookupKeysym(&ev.xkey, 0); + uf::stl::string keyName = NormalizeKeyName(KeySymToString(sym)); + + pod::payloads::windowKey keyEvent{ + {"window:Key", "os"}, + {keyName, + (unsigned long)sym, + 1, // released + false, + {.alt = (ev.xkey.state & Mod1Mask), + .ctrl = (ev.xkey.state & ControlMask), + .shift = (ev.xkey.state & ShiftMask), + .sys = (ev.xkey.state & Mod4Mask)}}}; + this->pushEvent(keyEvent.type, keyEvent); + this->pushEvent(keyEvent.type + "." + keyName, keyEvent); + } break; + + case ButtonPress: { + pod::Vector2i pos = {ev.xbutton.x, ev.xbutton.y}; + + if (ev.xbutton.button == 4) { // wheel up + pod::payloads::windowMouseWheel w{ + {"window:Mouse.Wheel", "os"}, + {{(unsigned)pos.x, (unsigned)pos.y}, + ::lastMouseWheel = +120}}; + this->pushEvent(w.type, w); + break; + } + if (ev.xbutton.button == 5) { // wheel down + pod::payloads::windowMouseWheel w{ + {"window:Mouse.Wheel", "os"}, + {{(unsigned)pos.x, (unsigned)pos.y}, + ::lastMouseWheel = -120}}; + this->pushEvent(w.type, w); + break; + } + + uf::stl::string button = "?"; + if (ev.xbutton.button == 1) button = "Left"; + if (ev.xbutton.button == 2) button = "Middle"; + if (ev.xbutton.button == 3) button = "Right"; + + pod::payloads::windowMouseClick click{ + {"window:Mouse.Click", "os"}, {pos, {0, 0}, button, -1}}; + this->pushEvent(click.type, click); + } break; + + case ButtonRelease: { + pod::Vector2i pos = {ev.xbutton.x, ev.xbutton.y}; + uf::stl::string button = "?"; + if (ev.xbutton.button == 1) button = "Left"; + if (ev.xbutton.button == 2) button = "Middle"; + if (ev.xbutton.button == 3) button = "Right"; + + pod::payloads::windowMouseClick click{ + {"window:Mouse.Click", "os"}, {pos, {0, 0}, button, 1}}; + this->pushEvent(click.type, click); + } break; + + case MotionNotify: { + static pod::Vector2i last{}; + pod::Vector2i current = {ev.xmotion.x, ev.xmotion.y}; + + pod::payloads::windowMouseMoved move{ + {{"window:Mouse.Moved", "client"}, {getSize()}}, + {current, current - last, 0}}; + this->pushEvent(move.type, move); + + last = current; + } break; + } + } +} + +bool spec::x11::Window::pollEvents(bool block) { + /* + if (block) { + // Block until one event is received + XEvent ev; + XNextEvent(m_display, &ev); // this blocks + XPutBackEvent(m_display, &ev); // push back into event queue + } + */ + if (block && XPending(m_display) == 0) { + int fd = ConnectionNumber(m_display); + fd_set in_fds; + FD_ZERO(&in_fds); + FD_SET(fd, &in_fds); + select(fd + 1, &in_fds, NULL, NULL, + NULL); // block until input available + } + + // Process whatever is pending now + this->processEvents(); + + // Dispatch hook events from m_events (matching Win32 style) + while (!this->m_events.empty()) { + auto &event = this->m_events.front(); + uf::hooks.call("window:Event", event.payload); + uf::hooks.call(event.name, event.payload); + this->m_events.pop(); + } + return true; +} + +pod::Vector2ui spec::x11::Window::getResolution() { + auto m_display = ::globalDisplay; + auto m_screen = DefaultScreen(m_display); + + auto monitors = getMonitors(m_display, m_screen); + if (monitors.empty()) + return {(unsigned)DisplayWidth(m_display, m_screen), + (unsigned)DisplayHeight(m_display, m_screen)}; + + // Use primary monitor + for (auto &mon : monitors) { + if (mon.primary) return {(unsigned)mon.width, (unsigned)mon.height}; + } + + // Fallback: first monitor + return {(unsigned)monitors[0].width, (unsigned)monitors[0].height}; +} + +/* +void spec::x11::Window::toggleFullscreen(bool borderless) { + Atom wm_state = XInternAtom(m_display, "_NET_WM_STATE", False); + Atom fullscreen = XInternAtom(m_display, "_NET_WM_STATE_FULLSCREEN", False); + + XEvent xev{}; + xev.type = ClientMessage; + xev.xclient.window = m_handle; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = 2; // _NET_WM_STATE_TOGGLE + xev.xclient.data.l[1] = fullscreen; + xev.xclient.data.l[2] = 0; // No second property + xev.xclient.data.l[3] = 1; // Source indication: application + xev.xclient.data.l[4] = 0; + + XSendEvent(m_display, DefaultRootWindow(m_display), False, + SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XFlush(m_display); +} +*/ + +void spec::x11::Window::toggleFullscreen(bool borderless) { + Atom wmState = XInternAtom(m_display, "_NET_WM_STATE", False); + Atom wmFullscreen = + XInternAtom(m_display, "_NET_WM_STATE_FULLSCREEN", False); + Atom wmMonitors = + XInternAtom(m_display, "_NET_WM_FULLSCREEN_MONITORS", False); + + // Find the monitor where the window currently resides + auto monitors = getMonitors(m_display, m_screen); + if (monitors.empty()) return; + + XWindowAttributes attr; + XGetWindowAttributes(m_display, m_handle, &attr); + int winCenterX = attr.x + attr.width / 2; + int winCenterY = attr.y + attr.height / 2; + + int top = 0, bottom = 0, left = 0, right = 0; + for (size_t i = 0; i < monitors.size(); i++) { + auto &mon = monitors[i]; + if (winCenterX >= mon.x && winCenterX < mon.x + mon.width && + winCenterY >= mon.y && winCenterY < mon.y + mon.height) { + // Select this monitor index + top = bottom = left = right = i; + break; + } + } + + // Set which monitor to fullscreen into + XEvent monEvent{}; + monEvent.type = ClientMessage; + monEvent.xclient.window = m_handle; + monEvent.xclient.message_type = wmMonitors; + monEvent.xclient.format = 32; + monEvent.xclient.data.l[0] = top; // top monitor index + monEvent.xclient.data.l[1] = bottom; // bottom monitor index + monEvent.xclient.data.l[2] = left; // left monitor index + monEvent.xclient.data.l[3] = right; // right monitor index + monEvent.xclient.data.l[4] = 0; + + XSendEvent(m_display, DefaultRootWindow(m_display), False, + SubstructureRedirectMask | SubstructureNotifyMask, &monEvent); + + // Toggle fullscreen + XEvent ev{}; + ev.type = ClientMessage; + ev.xclient.window = m_handle; + ev.xclient.message_type = wmState; + ev.xclient.format = 32; + ev.xclient.data.l[0] = 2; // _NET_WM_STATE_TOGGLE + ev.xclient.data.l[1] = wmFullscreen; + ev.xclient.data.l[2] = 0; + ev.xclient.data.l[3] = 1; // application source + ev.xclient.data.l[4] = 0; + + XSendEvent(m_display, DefaultRootWindow(m_display), False, + SubstructureRedirectMask | SubstructureNotifyMask, &ev); + + if (borderless) { + Atom wmHints = XInternAtom(m_display, "_MOTIF_WM_HINTS", True); + if (wmHints) { + struct MotifHints { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long inputMode; + unsigned long status; + }; + + MotifHints hints; + hints.flags = 2; // Decorations + hints.decorations = 0; // No borders/title + XChangeProperty(m_display, m_handle, wmHints, wmHints, 32, + PropModeReplace, (unsigned char *)&hints, 5); + } + } + + XFlush(m_display); +} + +void spec::x11::Window::display() { +#if UF_USE_OPENGL +#if UF_OPENGL_CONTEXT_IN_WINDOW + if (m_context) { + spec::Context *context = (spec::Context *)this->m_context; + if (context->setActive(true)) context->display(); + } +#else + glXSwapBuffers(m_display, m_handle); +#endif +#endif +} + +/* + +#if UF_USE_OPENGL +void spec::x11::Window::createGLContext() { + static int visualAttribs[] = { + GLX_RGBA, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, 24, + GLX_STENCIL_SIZE, 8, + None + }; + + int screen = DefaultScreen(m_display); + XVisualInfo* vi = glXChooseVisual(m_display, screen, visualAttribs); + if (!vi) { + UF_EXCEPTION("Failed to choose X11 GLX visual"); + } + + GLXContext ctx = glXCreateContext(m_display, vi, NULL, GL_TRUE); + if (!ctx) { + UF_EXCEPTION("Failed to create GLX context"); + } + + glXMakeCurrent(m_display, m_handle, ctx); + this->m_context = ctx; +} + +void spec::x11::Window::display() { + if (m_context) { + glXSwapBuffers(m_display, m_handle); + } +} +#endif + +*/ + +#if UF_USE_VULKAN +uf::stl::vector spec::x11::Window::getExtensions( + bool validationEnabled) { + uf::stl::vector exts = { + VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_XLIB_SURFACE_EXTENSION_NAME}; + if (validationEnabled) exts.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + return exts; +} + +void spec::x11::Window::createSurface(VkInstance instance, + VkSurfaceKHR &surface) { + VkXlibSurfaceCreateInfoKHR info{}; + info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + info.dpy = m_display; + info.window = m_handle; + + VkResult result = + vkCreateXlibSurfaceKHR(instance, &info, nullptr, &surface); + if (result != VK_SUCCESS) { + UF_EXCEPTION("Failed to create Xlib Vulkan surface"); + } +} +#endif + +#endif \ No newline at end of file diff --git a/engine/src/spec/window/windows.cpp b/engine/src/spec/window/windows.cpp index ee7c1d90..098d275f 100644 --- a/engine/src/spec/window/windows.cpp +++ b/engine/src/spec/window/windows.cpp @@ -79,128 +79,132 @@ namespace { uf::stl::vector keys; keys.reserve(8); - if ( uf::inputs::kbm::states::LShift ) keys.emplace_back(VK_LSHIFT); - if ( uf::inputs::kbm::states::RShift ) keys.emplace_back(VK_RSHIFT); + #define IF_KEY_STATE( K, V ) if ( uf::inputs::kbm::states::K ) keys.emplace_back(V); - if ( uf::inputs::kbm::states::LAlt ) keys.emplace_back(VK_LMENU); - if ( uf::inputs::kbm::states::RAlt ) keys.emplace_back(VK_RMENU); + IF_KEY_STATE( LShift, VK_LSHIFT); + IF_KEY_STATE( RShift, VK_RSHIFT); - if ( uf::inputs::kbm::states::LControl ) keys.emplace_back(VK_LCONTROL); - if ( uf::inputs::kbm::states::RControl ) keys.emplace_back(VK_RCONTROL); + IF_KEY_STATE( LAlt, VK_LMENU); + IF_KEY_STATE( RAlt, VK_RMENU); - if ( uf::inputs::kbm::states::LSystem ) keys.emplace_back(VK_LWIN); - if ( uf::inputs::kbm::states::RSystem ) keys.emplace_back(VK_RWIN); + IF_KEY_STATE( LControl, VK_LCONTROL); + IF_KEY_STATE( RControl, VK_RCONTROL); - if ( uf::inputs::kbm::states::Menu ) keys.emplace_back(VK_APPS); - if ( uf::inputs::kbm::states::SemiColon ) keys.emplace_back(VK_OEM_1); - if ( uf::inputs::kbm::states::Slash ) keys.emplace_back(VK_OEM_2); - if ( uf::inputs::kbm::states::Equal ) keys.emplace_back(VK_OEM_PLUS); - if ( uf::inputs::kbm::states::Dash ) keys.emplace_back(VK_OEM_MINUS); - if ( uf::inputs::kbm::states::LBracket ) keys.emplace_back(VK_OEM_4); - if ( uf::inputs::kbm::states::RBracket ) keys.emplace_back(VK_OEM_6); - if ( uf::inputs::kbm::states::Comma ) keys.emplace_back(VK_OEM_COMMA); - if ( uf::inputs::kbm::states::Period ) keys.emplace_back(VK_OEM_PERIOD); - if ( uf::inputs::kbm::states::Quote ) keys.emplace_back(VK_OEM_7); - if ( uf::inputs::kbm::states::BackSlash ) keys.emplace_back(VK_OEM_5); - if ( uf::inputs::kbm::states::Tilde ) keys.emplace_back(VK_OEM_3); - - if ( uf::inputs::kbm::states::Escape ) keys.emplace_back(VK_ESCAPE); - if ( uf::inputs::kbm::states::Space ) keys.emplace_back(VK_SPACE); - if ( uf::inputs::kbm::states::Enter ) keys.emplace_back(VK_RETURN); - if ( uf::inputs::kbm::states::BackSpace ) keys.emplace_back(VK_BACK); - if ( uf::inputs::kbm::states::Tab ) keys.emplace_back(VK_TAB); - if ( uf::inputs::kbm::states::PageUp ) keys.emplace_back(VK_PRIOR); - if ( uf::inputs::kbm::states::PageDown ) keys.emplace_back(VK_NEXT); - if ( uf::inputs::kbm::states::End ) keys.emplace_back(VK_END); - if ( uf::inputs::kbm::states::Home ) keys.emplace_back(VK_HOME); - if ( uf::inputs::kbm::states::Insert ) keys.emplace_back(VK_INSERT); - if ( uf::inputs::kbm::states::Delete ) keys.emplace_back(VK_DELETE); - if ( uf::inputs::kbm::states::Add ) keys.emplace_back(VK_ADD); - if ( uf::inputs::kbm::states::Subtract ) keys.emplace_back(VK_SUBTRACT); - if ( uf::inputs::kbm::states::Multiply ) keys.emplace_back(VK_MULTIPLY); - if ( uf::inputs::kbm::states::Divide ) keys.emplace_back(VK_DIVIDE); - if ( uf::inputs::kbm::states::Pause ) keys.emplace_back(VK_PAUSE); - - if ( uf::inputs::kbm::states::F1 ) keys.emplace_back(VK_F1); - if ( uf::inputs::kbm::states::F2 ) keys.emplace_back(VK_F2); - if ( uf::inputs::kbm::states::F3 ) keys.emplace_back(VK_F3); - if ( uf::inputs::kbm::states::F4 ) keys.emplace_back(VK_F4); - if ( uf::inputs::kbm::states::F5 ) keys.emplace_back(VK_F5); - if ( uf::inputs::kbm::states::F6 ) keys.emplace_back(VK_F6); - if ( uf::inputs::kbm::states::F7 ) keys.emplace_back(VK_F7); - if ( uf::inputs::kbm::states::F8 ) keys.emplace_back(VK_F8); - if ( uf::inputs::kbm::states::F9 ) keys.emplace_back(VK_F9); - if ( uf::inputs::kbm::states::F10 ) keys.emplace_back(VK_F10); - if ( uf::inputs::kbm::states::F11 ) keys.emplace_back(VK_F11); - if ( uf::inputs::kbm::states::F12 ) keys.emplace_back(VK_F12); - if ( uf::inputs::kbm::states::F13 ) keys.emplace_back(VK_F13); - if ( uf::inputs::kbm::states::F14 ) keys.emplace_back(VK_F14); - if ( uf::inputs::kbm::states::F15 ) keys.emplace_back(VK_F15); - - if ( uf::inputs::kbm::states::Left ) keys.emplace_back(VK_LEFT); - if ( uf::inputs::kbm::states::Right ) keys.emplace_back(VK_RIGHT); - if ( uf::inputs::kbm::states::Up ) keys.emplace_back(VK_UP); - if ( uf::inputs::kbm::states::Down ) keys.emplace_back(VK_DOWN); + IF_KEY_STATE( LSystem, VK_LWIN); + IF_KEY_STATE( RSystem, VK_RWIN); - if ( uf::inputs::kbm::states::Numpad0 ) keys.emplace_back(VK_NUMPAD0); - if ( uf::inputs::kbm::states::Numpad1 ) keys.emplace_back(VK_NUMPAD1); - if ( uf::inputs::kbm::states::Numpad2 ) keys.emplace_back(VK_NUMPAD2); - if ( uf::inputs::kbm::states::Numpad3 ) keys.emplace_back(VK_NUMPAD3); - if ( uf::inputs::kbm::states::Numpad4 ) keys.emplace_back(VK_NUMPAD4); - if ( uf::inputs::kbm::states::Numpad5 ) keys.emplace_back(VK_NUMPAD5); - if ( uf::inputs::kbm::states::Numpad6 ) keys.emplace_back(VK_NUMPAD6); - if ( uf::inputs::kbm::states::Numpad7 ) keys.emplace_back(VK_NUMPAD7); - if ( uf::inputs::kbm::states::Numpad8 ) keys.emplace_back(VK_NUMPAD8); - if ( uf::inputs::kbm::states::Numpad9 ) keys.emplace_back(VK_NUMPAD9); + IF_KEY_STATE( Menu, VK_APPS); + IF_KEY_STATE( SemiColon, VK_OEM_1); + IF_KEY_STATE( Slash, VK_OEM_2); + IF_KEY_STATE( Equal, VK_OEM_PLUS); + IF_KEY_STATE( Dash, VK_OEM_MINUS); + IF_KEY_STATE( LBracket, VK_OEM_4); + IF_KEY_STATE( RBracket, VK_OEM_6); + IF_KEY_STATE( Comma, VK_OEM_COMMA); + IF_KEY_STATE( Period, VK_OEM_PERIOD); + IF_KEY_STATE( Quote, VK_OEM_7); + IF_KEY_STATE( BackSlash, VK_OEM_5); + IF_KEY_STATE( Tilde, VK_OEM_3); + + IF_KEY_STATE( Escape, VK_ESCAPE); + IF_KEY_STATE( Space, VK_SPACE); + IF_KEY_STATE( Enter, VK_RETURN); + IF_KEY_STATE( BackSpace, VK_BACK); + IF_KEY_STATE( Tab, VK_TAB); + IF_KEY_STATE( PageUp, VK_PRIOR); + IF_KEY_STATE( PageDown, VK_NEXT); + IF_KEY_STATE( End, VK_END); + IF_KEY_STATE( Home, VK_HOME); + IF_KEY_STATE( Insert, VK_INSERT); + IF_KEY_STATE( Delete, VK_DELETE); + IF_KEY_STATE( Add, VK_ADD); + IF_KEY_STATE( Subtract, VK_SUBTRACT); + IF_KEY_STATE( Multiply, VK_MULTIPLY); + IF_KEY_STATE( Divide, VK_DIVIDE); + IF_KEY_STATE( Pause, VK_PAUSE); + + IF_KEY_STATE( F1, VK_F1); + IF_KEY_STATE( F2, VK_F2); + IF_KEY_STATE( F3, VK_F3); + IF_KEY_STATE( F4, VK_F4); + IF_KEY_STATE( F5, VK_F5); + IF_KEY_STATE( F6, VK_F6); + IF_KEY_STATE( F7, VK_F7); + IF_KEY_STATE( F8, VK_F8); + IF_KEY_STATE( F9, VK_F9); + IF_KEY_STATE( F10, VK_F10); + IF_KEY_STATE( F11, VK_F11); + IF_KEY_STATE( F12, VK_F12); + IF_KEY_STATE( F13, VK_F13); + IF_KEY_STATE( F14, VK_F14); + IF_KEY_STATE( F15, VK_F15); + + IF_KEY_STATE( Left, VK_LEFT); + IF_KEY_STATE( Right, VK_RIGHT); + IF_KEY_STATE( Up, VK_UP); + IF_KEY_STATE( Down, VK_DOWN); - if ( uf::inputs::kbm::states::Q ) keys.emplace_back('Q'); - if ( uf::inputs::kbm::states::W ) keys.emplace_back('W'); - if ( uf::inputs::kbm::states::E ) keys.emplace_back('E'); - if ( uf::inputs::kbm::states::R ) keys.emplace_back('R'); - if ( uf::inputs::kbm::states::T ) keys.emplace_back('T'); - if ( uf::inputs::kbm::states::Y ) keys.emplace_back('Y'); - if ( uf::inputs::kbm::states::U ) keys.emplace_back('U'); - if ( uf::inputs::kbm::states::I ) keys.emplace_back('I'); - if ( uf::inputs::kbm::states::O ) keys.emplace_back('O'); - if ( uf::inputs::kbm::states::P ) keys.emplace_back('P'); + IF_KEY_STATE( Numpad0, VK_NUMPAD0); + IF_KEY_STATE( Numpad1, VK_NUMPAD1); + IF_KEY_STATE( Numpad2, VK_NUMPAD2); + IF_KEY_STATE( Numpad3, VK_NUMPAD3); + IF_KEY_STATE( Numpad4, VK_NUMPAD4); + IF_KEY_STATE( Numpad5, VK_NUMPAD5); + IF_KEY_STATE( Numpad6, VK_NUMPAD6); + IF_KEY_STATE( Numpad7, VK_NUMPAD7); + IF_KEY_STATE( Numpad8, VK_NUMPAD8); + IF_KEY_STATE( Numpad9, VK_NUMPAD9); + + IF_KEY_STATE( Q, 'Q'); + IF_KEY_STATE( W, 'W'); + IF_KEY_STATE( E, 'E'); + IF_KEY_STATE( R, 'R'); + IF_KEY_STATE( T, 'T'); + IF_KEY_STATE( Y, 'Y'); + IF_KEY_STATE( U, 'U'); + IF_KEY_STATE( I, 'I'); + IF_KEY_STATE( O, 'O'); + IF_KEY_STATE( P, 'P'); - if ( uf::inputs::kbm::states::A ) keys.emplace_back('A'); - if ( uf::inputs::kbm::states::S ) keys.emplace_back('S'); - if ( uf::inputs::kbm::states::D ) keys.emplace_back('D'); - if ( uf::inputs::kbm::states::F ) keys.emplace_back('F'); - if ( uf::inputs::kbm::states::G ) keys.emplace_back('G'); - if ( uf::inputs::kbm::states::H ) keys.emplace_back('H'); - if ( uf::inputs::kbm::states::J ) keys.emplace_back('J'); - if ( uf::inputs::kbm::states::K ) keys.emplace_back('K'); - if ( uf::inputs::kbm::states::L ) keys.emplace_back('L'); + IF_KEY_STATE( A, 'A'); + IF_KEY_STATE( S, 'S'); + IF_KEY_STATE( D, 'D'); + IF_KEY_STATE( F, 'F'); + IF_KEY_STATE( G, 'G'); + IF_KEY_STATE( H, 'H'); + IF_KEY_STATE( J, 'J'); + IF_KEY_STATE( K, 'K'); + IF_KEY_STATE( L, 'L'); - if ( uf::inputs::kbm::states::Z ) keys.emplace_back('Z'); - if ( uf::inputs::kbm::states::X ) keys.emplace_back('X'); - if ( uf::inputs::kbm::states::C ) keys.emplace_back('C'); - if ( uf::inputs::kbm::states::V ) keys.emplace_back('V'); - if ( uf::inputs::kbm::states::B ) keys.emplace_back('B'); - if ( uf::inputs::kbm::states::N ) keys.emplace_back('N'); - if ( uf::inputs::kbm::states::M ) keys.emplace_back('M'); + IF_KEY_STATE( Z, 'Z'); + IF_KEY_STATE( X, 'X'); + IF_KEY_STATE( C, 'C'); + IF_KEY_STATE( V, 'V'); + IF_KEY_STATE( B, 'B'); + IF_KEY_STATE( N, 'N'); + IF_KEY_STATE( M, 'M'); - if ( uf::inputs::kbm::states::Num1 ) keys.emplace_back('1'); - if ( uf::inputs::kbm::states::Num2 ) keys.emplace_back('2'); - if ( uf::inputs::kbm::states::Num3 ) keys.emplace_back('3'); - if ( uf::inputs::kbm::states::Num4 ) keys.emplace_back('4'); - if ( uf::inputs::kbm::states::Num5 ) keys.emplace_back('5'); - if ( uf::inputs::kbm::states::Num6 ) keys.emplace_back('6'); - if ( uf::inputs::kbm::states::Num7 ) keys.emplace_back('7'); - if ( uf::inputs::kbm::states::Num8 ) keys.emplace_back('8'); - if ( uf::inputs::kbm::states::Num9 ) keys.emplace_back('9'); - if ( uf::inputs::kbm::states::Num0 ) keys.emplace_back('0'); + IF_KEY_STATE( Num1, '1'); + IF_KEY_STATE( Num2, '2'); + IF_KEY_STATE( Num3, '3'); + IF_KEY_STATE( Num4, '4'); + IF_KEY_STATE( Num5, '5'); + IF_KEY_STATE( Num6, '6'); + IF_KEY_STATE( Num7, '7'); + IF_KEY_STATE( Num8, '8'); + IF_KEY_STATE( Num9, '9'); + IF_KEY_STATE( Num0, '0'); - if ( uf::inputs::kbm::states::Mouse1 ) keys.emplace_back(VK_LBUTTON); - if ( uf::inputs::kbm::states::Mouse2 ) keys.emplace_back(VK_RBUTTON); - if ( uf::inputs::kbm::states::Mouse3 ) keys.emplace_back(VK_MBUTTON); + IF_KEY_STATE( Mouse1, VK_LBUTTON); + IF_KEY_STATE( Mouse2, VK_RBUTTON); + IF_KEY_STATE( Mouse3, VK_MBUTTON); return keys; } + uf::stl::string _GetKeyName( WPARAM key, LPARAM flags = 0 ) { + #define CASE_KEY_RETURN( K, V ) case K: return V; switch ( key ) { // Check the scancode to distinguish between left and right shift case VK_SHIFT: { @@ -214,109 +218,109 @@ namespace { case VK_CONTROL : return (HIWORD(flags) & KF_EXTENDED) ? "RControl" : "LControl"; // Other keys are reported properly - case VK_LWIN: return "LSystem"; - case VK_RWIN: return "RSystem"; - case VK_APPS: return "Menu"; - case VK_OEM_1: return "SemiColon"; - case VK_OEM_2: return "Slash"; - case VK_OEM_PLUS: return "Equal"; - case VK_OEM_MINUS: return "Dash"; - case VK_OEM_4: return "LBracket"; - case VK_OEM_6: return "RBracket"; - case VK_OEM_COMMA: return "Comma"; - case VK_OEM_PERIOD: return "Period"; - case VK_OEM_7: return "Quote"; - case VK_OEM_5: return "BackSlash"; - case VK_OEM_3: return "Tilde"; + CASE_KEY_RETURN(VK_LWIN, "LSystem"); + CASE_KEY_RETURN(VK_RWIN, "RSystem"); + CASE_KEY_RETURN(VK_APPS, "Menu"); + CASE_KEY_RETURN(VK_OEM_1, "SemiColon"); + CASE_KEY_RETURN(VK_OEM_2, "Slash"); + CASE_KEY_RETURN(VK_OEM_PLUS, "Equal"); + CASE_KEY_RETURN(VK_OEM_MINUS, "Dash"); + CASE_KEY_RETURN(VK_OEM_4, "LBracket"); + CASE_KEY_RETURN(VK_OEM_6, "RBracket"); + CASE_KEY_RETURN(VK_OEM_COMMA, "Comma"); + CASE_KEY_RETURN(VK_OEM_PERIOD, "Period"); + CASE_KEY_RETURN(VK_OEM_7, "Quote"); + CASE_KEY_RETURN(VK_OEM_5, "BackSlash"); + CASE_KEY_RETURN(VK_OEM_3, "Tilde"); - case VK_ESCAPE: return "Escape"; - case VK_SPACE: return "Space"; - case VK_RETURN: return "Enter"; - case VK_BACK: return "BackSpace"; - case VK_TAB: return "Tab"; - case VK_PRIOR: return "PageUp"; - case VK_NEXT: return "PageDown"; - case VK_END: return "End"; - case VK_HOME: return "Home"; - case VK_INSERT: return "Insert"; - case VK_DELETE: return "Delete"; - case VK_ADD: return "Add"; - case VK_SUBTRACT: return "Subtract"; - case VK_MULTIPLY: return "Multiply"; - case VK_DIVIDE: return "Divide"; - case VK_PAUSE: return "Pause"; + CASE_KEY_RETURN(VK_ESCAPE, "Escape"); + CASE_KEY_RETURN(VK_SPACE, "Space"); + CASE_KEY_RETURN(VK_RETURN, "Enter"); + CASE_KEY_RETURN(VK_BACK, "BackSpace"); + CASE_KEY_RETURN(VK_TAB, "Tab"); + CASE_KEY_RETURN(VK_PRIOR, "PageUp"); + CASE_KEY_RETURN(VK_NEXT, "PageDown"); + CASE_KEY_RETURN(VK_END, "End"); + CASE_KEY_RETURN(VK_HOME, "Home"); + CASE_KEY_RETURN(VK_INSERT, "Insert"); + CASE_KEY_RETURN(VK_DELETE, "Delete"); + CASE_KEY_RETURN(VK_ADD, "Add"); + CASE_KEY_RETURN(VK_SUBTRACT, "Subtract"); + CASE_KEY_RETURN(VK_MULTIPLY, "Multiply"); + CASE_KEY_RETURN(VK_DIVIDE, "Divide"); + CASE_KEY_RETURN(VK_PAUSE, "Pause"); - case VK_F1: return "F1"; - case VK_F2: return "F2"; - case VK_F3: return "F3"; - case VK_F4: return "F4"; - case VK_F5: return "F5"; - case VK_F6: return "F6"; - case VK_F7: return "F7"; - case VK_F8: return "F8"; - case VK_F9: return "F9"; - case VK_F10: return "F10"; - case VK_F11: return "F11"; - case VK_F12: return "F12"; - case VK_F13: return "F13"; - case VK_F14: return "F14"; - case VK_F15: return "F15"; + CASE_KEY_RETURN(VK_F1, "F1"); + CASE_KEY_RETURN(VK_F2, "F2"); + CASE_KEY_RETURN(VK_F3, "F3"); + CASE_KEY_RETURN(VK_F4, "F4"); + CASE_KEY_RETURN(VK_F5, "F5"); + CASE_KEY_RETURN(VK_F6, "F6"); + CASE_KEY_RETURN(VK_F7, "F7"); + CASE_KEY_RETURN(VK_F8, "F8"); + CASE_KEY_RETURN(VK_F9, "F9"); + CASE_KEY_RETURN(VK_F10, "F10"); + CASE_KEY_RETURN(VK_F11, "F11"); + CASE_KEY_RETURN(VK_F12, "F12"); + CASE_KEY_RETURN(VK_F13, "F13"); + CASE_KEY_RETURN(VK_F14, "F14"); + CASE_KEY_RETURN(VK_F15, "F15"); - case VK_LEFT: return "Left"; - case VK_RIGHT: return "Right"; - case VK_UP: return "Up"; - case VK_DOWN: return "Down"; + CASE_KEY_RETURN(VK_LEFT, "Left"); + CASE_KEY_RETURN(VK_RIGHT, "Right"); + CASE_KEY_RETURN(VK_UP, "Up"); + CASE_KEY_RETURN(VK_DOWN, "Down"); - case VK_NUMPAD0: return "Numpad0"; - case VK_NUMPAD1: return "Numpad1"; - case VK_NUMPAD2: return "Numpad2"; - case VK_NUMPAD3: return "Numpad3"; - case VK_NUMPAD4: return "Numpad4"; - case VK_NUMPAD5: return "Numpad5"; - case VK_NUMPAD6: return "Numpad6"; - case VK_NUMPAD7: return "Numpad7"; - case VK_NUMPAD8: return "Numpad8"; - case VK_NUMPAD9: return "Numpad9"; + CASE_KEY_RETURN(VK_NUMPAD0, "Numpad0"); + CASE_KEY_RETURN(VK_NUMPAD1, "Numpad1"); + CASE_KEY_RETURN(VK_NUMPAD2, "Numpad2"); + CASE_KEY_RETURN(VK_NUMPAD3, "Numpad3"); + CASE_KEY_RETURN(VK_NUMPAD4, "Numpad4"); + CASE_KEY_RETURN(VK_NUMPAD5, "Numpad5"); + CASE_KEY_RETURN(VK_NUMPAD6, "Numpad6"); + CASE_KEY_RETURN(VK_NUMPAD7, "Numpad7"); + CASE_KEY_RETURN(VK_NUMPAD8, "Numpad8"); + CASE_KEY_RETURN(VK_NUMPAD9, "Numpad9"); - case 'Q': return "Q"; - case 'W': return "W"; - case 'E': return "E"; - case 'R': return "R"; - case 'T': return "T"; - case 'Y': return "Y"; - case 'U': return "U"; - case 'I': return "I"; - case 'O': return "O"; - case 'P': return "P"; + CASE_KEY_RETURN('Q', "Q"); + CASE_KEY_RETURN('W', "W"); + CASE_KEY_RETURN('E', "E"); + CASE_KEY_RETURN('R', "R"); + CASE_KEY_RETURN('T', "T"); + CASE_KEY_RETURN('Y', "Y"); + CASE_KEY_RETURN('U', "U"); + CASE_KEY_RETURN('I', "I"); + CASE_KEY_RETURN('O', "O"); + CASE_KEY_RETURN('P', "P"); - case 'A': return "A"; - case 'S': return "S"; - case 'D': return "D"; - case 'F': return "F"; - case 'G': return "G"; - case 'H': return "H"; - case 'J': return "J"; - case 'K': return "K"; - case 'L': return "L"; + CASE_KEY_RETURN('A', "A"); + CASE_KEY_RETURN('S', "S"); + CASE_KEY_RETURN('D', "D"); + CASE_KEY_RETURN('F', "F"); + CASE_KEY_RETURN('G', "G"); + CASE_KEY_RETURN('H', "H"); + CASE_KEY_RETURN('J', "J"); + CASE_KEY_RETURN('K', "K"); + CASE_KEY_RETURN('L', "L"); - case 'Z': return "Z"; - case 'X': return "X"; - case 'C': return "C"; - case 'V': return "V"; - case 'B': return "B"; - case 'N': return "N"; - case 'M': return "M"; + CASE_KEY_RETURN('Z', "Z"); + CASE_KEY_RETURN('X', "X"); + CASE_KEY_RETURN('C', "C"); + CASE_KEY_RETURN('V', "V"); + CASE_KEY_RETURN('B', "B"); + CASE_KEY_RETURN('N', "N"); + CASE_KEY_RETURN('M', "M"); - case '1': return "Num1"; - case '2': return "Num2"; - case '3': return "Num3"; - case '4': return "Num4"; - case '5': return "Num5"; - case '6': return "Num6"; - case '7': return "Num7"; - case '8': return "Num8"; - case '9': return "Num9"; - case '0': return "Num0"; + CASE_KEY_RETURN('1', "Num1"); + CASE_KEY_RETURN('2', "Num2"); + CASE_KEY_RETURN('3', "Num3"); + CASE_KEY_RETURN('4', "Num4"); + CASE_KEY_RETURN('5', "Num5"); + CASE_KEY_RETURN('6', "Num6"); + CASE_KEY_RETURN('7', "Num7"); + CASE_KEY_RETURN('8', "Num8"); + CASE_KEY_RETURN('9', "Num9"); + CASE_KEY_RETURN('0', "Num0"); } return std::to_string((int) key); } @@ -326,114 +330,116 @@ namespace { WPARAM GetKeyCode( const uf::stl::string& _name ) { uf::stl::string name = uf::string::uppercase(_name); - if ( name == "A" ) return 'A'; - else if ( name == "B" ) return 'B'; - else if ( name == "C" ) return 'C'; - else if ( name == "D" ) return 'D'; - else if ( name == "E" ) return 'E'; - else if ( name == "F" ) return 'F'; - else if ( name == "G" ) return 'G'; - else if ( name == "H" ) return 'H'; - else if ( name == "I" ) return 'I'; - else if ( name == "J" ) return 'J'; - else if ( name == "K" ) return 'K'; - else if ( name == "L" ) return 'L'; - else if ( name == "M" ) return 'M'; - else if ( name == "N" ) return 'N'; - else if ( name == "O" ) return 'O'; - else if ( name == "P" ) return 'P'; - else if ( name == "Q" ) return 'Q'; - else if ( name == "R" ) return 'R'; - else if ( name == "S" ) return 'S'; - else if ( name == "T" ) return 'T'; - else if ( name == "U" ) return 'U'; - else if ( name == "V" ) return 'V'; - else if ( name == "W" ) return 'W'; - else if ( name == "X" ) return 'X'; - else if ( name == "Y" ) return 'Y'; - else if ( name == "Z" ) return 'Z'; - else if ( name == "0" ) return '0'; - else if ( name == "1" ) return '1'; - else if ( name == "2" ) return '2'; - else if ( name == "3" ) return '3'; - else if ( name == "4" ) return '4'; - else if ( name == "5" ) return '5'; - else if ( name == "6" ) return '6'; - else if ( name == "7" ) return '7'; - else if ( name == "8" ) return '8'; - else if ( name == "9" ) return '9'; - else if ( name == "ESCAPE" ) return VK_ESCAPE; - else if ( name == "LCONTROL" ) return VK_LCONTROL; - else if ( name == "LSHIFT" ) return VK_LSHIFT; - else if ( name == "LALT" ) return VK_LMENU; - else if ( name == "LSYSTEM" ) return VK_LWIN; - else if ( name == "RCONTROL" ) return VK_RCONTROL; - else if ( name == "RSHIFT" ) return VK_RSHIFT; - else if ( name == "RALT" ) return VK_RMENU; - else if ( name == "RSYSTEM" ) return VK_RWIN; - else if ( name == "APPS" ) return VK_APPS; - else if ( name == "OEM4" ) return VK_OEM_4; - else if ( name == "OEM6" ) return VK_OEM_6; - else if ( name == "OEM1" ) return VK_OEM_1; - else if ( name == "OEMCOMMA" ) return VK_OEM_COMMA; - else if ( name == "OEMPERIOD" ) return VK_OEM_PERIOD; - else if ( name == "OEM7" ) return VK_OEM_7; - else if ( name == "OEM2" ) return VK_OEM_2; - else if ( name == "OEM5" ) return VK_OEM_5; - else if ( name == "OEM3" ) return VK_OEM_3; - else if ( name == "OEM+" ) return VK_OEM_PLUS; - else if ( name == "OEM-" ) return VK_OEM_MINUS; - else if ( name == " " ) return VK_SPACE; - else if ( name == "SPACE" ) return VK_SPACE; - else if ( name == "ENTER" ) return VK_RETURN; - else if ( name == "BACK" ) return VK_BACK; - else if ( name == "TAB" ) return VK_TAB; - else if ( name == "PRIOR" ) return VK_PRIOR; - else if ( name == "NEXT" ) return VK_NEXT; - else if ( name == "END" ) return VK_END; - else if ( name == "HOME" ) return VK_HOME; - else if ( name == "INSERT" ) return VK_INSERT; - else if ( name == "DELETE" ) return VK_DELETE; - else if ( name == "+" ) return VK_ADD; - else if ( name == "-" ) return VK_SUBTRACT; - else if ( name == "*" ) return VK_MULTIPLY; - else if ( name == "/" ) return VK_DIVIDE; - else if ( name == "LEFT" ) return VK_LEFT; - else if ( name == "RIGHT" ) return VK_RIGHT; - else if ( name == "UP" ) return VK_UP; - else if ( name == "DOWN" ) return VK_DOWN; - else if ( name == "NUM0" ) return VK_NUMPAD0; - else if ( name == "NUM1" ) return VK_NUMPAD1; - else if ( name == "NUM2" ) return VK_NUMPAD2; - else if ( name == "NUM3" ) return VK_NUMPAD3; - else if ( name == "NUM4" ) return VK_NUMPAD4; - else if ( name == "NUM5" ) return VK_NUMPAD5; - else if ( name == "NUM6" ) return VK_NUMPAD6; - else if ( name == "NUM7" ) return VK_NUMPAD7; - else if ( name == "NUM8" ) return VK_NUMPAD8; - else if ( name == "NUM9" ) return VK_NUMPAD9; - else if ( name == "F1" ) return VK_F1; - else if ( name == "F2" ) return VK_F2; - else if ( name == "F3" ) return VK_F3; - else if ( name == "F4" ) return VK_F4; - else if ( name == "F5" ) return VK_F5; - else if ( name == "F6" ) return VK_F6; - else if ( name == "F7" ) return VK_F7; - else if ( name == "F8" ) return VK_F8; - else if ( name == "F9" ) return VK_F9; - else if ( name == "F10" ) return VK_F10; - else if ( name == "F11" ) return VK_F11; - else if ( name == "F12" ) return VK_F12; - else if ( name == "F13" ) return VK_F13; - else if ( name == "F14" ) return VK_F14; - else if ( name == "F15" ) return VK_F15; - else if ( name == "PAUSE" ) return VK_PAUSE; + #define IF_KEY_RETURN( K, V ) if ( name == K ) return V + + IF_KEY_RETURN( "A", 'A'); + else IF_KEY_RETURN( "B", 'B'); + else IF_KEY_RETURN( "C", 'C'); + else IF_KEY_RETURN( "D", 'D'); + else IF_KEY_RETURN( "E", 'E'); + else IF_KEY_RETURN( "F", 'F'); + else IF_KEY_RETURN( "G", 'G'); + else IF_KEY_RETURN( "H", 'H'); + else IF_KEY_RETURN( "I", 'I'); + else IF_KEY_RETURN( "J", 'J'); + else IF_KEY_RETURN( "K", 'K'); + else IF_KEY_RETURN( "L", 'L'); + else IF_KEY_RETURN( "M", 'M'); + else IF_KEY_RETURN( "N", 'N'); + else IF_KEY_RETURN( "O", 'O'); + else IF_KEY_RETURN( "P", 'P'); + else IF_KEY_RETURN( "Q", 'Q'); + else IF_KEY_RETURN( "R", 'R'); + else IF_KEY_RETURN( "S", 'S'); + else IF_KEY_RETURN( "T", 'T'); + else IF_KEY_RETURN( "U", 'U'); + else IF_KEY_RETURN( "V", 'V'); + else IF_KEY_RETURN( "W", 'W'); + else IF_KEY_RETURN( "X", 'X'); + else IF_KEY_RETURN( "Y", 'Y'); + else IF_KEY_RETURN( "Z", 'Z'); + else IF_KEY_RETURN( "0", '0'); + else IF_KEY_RETURN( "1", '1'); + else IF_KEY_RETURN( "2", '2'); + else IF_KEY_RETURN( "3", '3'); + else IF_KEY_RETURN( "4", '4'); + else IF_KEY_RETURN( "5", '5'); + else IF_KEY_RETURN( "6", '6'); + else IF_KEY_RETURN( "7", '7'); + else IF_KEY_RETURN( "8", '8'); + else IF_KEY_RETURN( "9", '9'); + else IF_KEY_RETURN( "ESCAPE", VK_ESCAPE); + else IF_KEY_RETURN( "LCONTROL", VK_LCONTROL); + else IF_KEY_RETURN( "LSHIFT", VK_LSHIFT); + else IF_KEY_RETURN( "LALT", VK_LMENU); + else IF_KEY_RETURN( "LSYSTEM", VK_LWIN); + else IF_KEY_RETURN( "RCONTROL", VK_RCONTROL); + else IF_KEY_RETURN( "RSHIFT", VK_RSHIFT); + else IF_KEY_RETURN( "RALT", VK_RMENU); + else IF_KEY_RETURN( "RSYSTEM", VK_RWIN); + else IF_KEY_RETURN( "APPS", VK_APPS); + else IF_KEY_RETURN( "OEM4", VK_OEM_4); + else IF_KEY_RETURN( "OEM6", VK_OEM_6); + else IF_KEY_RETURN( "OEM1", VK_OEM_1); + else IF_KEY_RETURN( "OEMCOMMA", VK_OEM_COMMA); + else IF_KEY_RETURN( "OEMPERIOD", VK_OEM_PERIOD); + else IF_KEY_RETURN( "OEM7", VK_OEM_7); + else IF_KEY_RETURN( "OEM2", VK_OEM_2); + else IF_KEY_RETURN( "OEM5", VK_OEM_5); + else IF_KEY_RETURN( "OEM3", VK_OEM_3); + else IF_KEY_RETURN( "OEM+", VK_OEM_PLUS); + else IF_KEY_RETURN( "OEM-", VK_OEM_MINUS); + else IF_KEY_RETURN( " ", VK_SPACE); + else IF_KEY_RETURN( "SPACE", VK_SPACE); + else IF_KEY_RETURN( "ENTER", VK_RETURN); + else IF_KEY_RETURN( "BACK", VK_BACK); + else IF_KEY_RETURN( "TAB", VK_TAB); + else IF_KEY_RETURN( "PRIOR", VK_PRIOR); + else IF_KEY_RETURN( "NEXT", VK_NEXT); + else IF_KEY_RETURN( "END", VK_END); + else IF_KEY_RETURN( "HOME", VK_HOME); + else IF_KEY_RETURN( "INSERT", VK_INSERT); + else IF_KEY_RETURN( "DELETE", VK_DELETE); + else IF_KEY_RETURN( "+", VK_ADD); + else IF_KEY_RETURN( "-", VK_SUBTRACT); + else IF_KEY_RETURN( "*", VK_MULTIPLY); + else IF_KEY_RETURN( "/", VK_DIVIDE); + else IF_KEY_RETURN( "LEFT", VK_LEFT); + else IF_KEY_RETURN( "RIGHT", VK_RIGHT); + else IF_KEY_RETURN( "UP", VK_UP); + else IF_KEY_RETURN( "DOWN", VK_DOWN); + else IF_KEY_RETURN( "NUM0", VK_NUMPAD0); + else IF_KEY_RETURN( "NUM1", VK_NUMPAD1); + else IF_KEY_RETURN( "NUM2", VK_NUMPAD2); + else IF_KEY_RETURN( "NUM3", VK_NUMPAD3); + else IF_KEY_RETURN( "NUM4", VK_NUMPAD4); + else IF_KEY_RETURN( "NUM5", VK_NUMPAD5); + else IF_KEY_RETURN( "NUM6", VK_NUMPAD6); + else IF_KEY_RETURN( "NUM7", VK_NUMPAD7); + else IF_KEY_RETURN( "NUM8", VK_NUMPAD8); + else IF_KEY_RETURN( "NUM9", VK_NUMPAD9); + else IF_KEY_RETURN( "F1", VK_F1); + else IF_KEY_RETURN( "F2", VK_F2); + else IF_KEY_RETURN( "F3", VK_F3); + else IF_KEY_RETURN( "F4", VK_F4); + else IF_KEY_RETURN( "F5", VK_F5); + else IF_KEY_RETURN( "F6", VK_F6); + else IF_KEY_RETURN( "F7", VK_F7); + else IF_KEY_RETURN( "F8", VK_F8); + else IF_KEY_RETURN( "F9", VK_F9); + else IF_KEY_RETURN( "F10", VK_F10); + else IF_KEY_RETURN( "F11", VK_F11); + else IF_KEY_RETURN( "F12", VK_F12); + else IF_KEY_RETURN( "F13", VK_F13); + else IF_KEY_RETURN( "F14", VK_F14); + else IF_KEY_RETURN( "F15", VK_F15); + else IF_KEY_RETURN( "PAUSE", VK_PAUSE); - else if ( name == "MOUSE1" ) return VK_LBUTTON; - else if ( name == "MOUSE2" ) return VK_RBUTTON; - else if ( name == "MOUSE3" ) return VK_MBUTTON; - else if ( name == "XBUTTON1" ) return VK_XBUTTON1; - else if ( name == "XBUTTON2" ) return VK_XBUTTON2; + else IF_KEY_RETURN( "MOUSE1", VK_LBUTTON); + else IF_KEY_RETURN( "MOUSE2", VK_RBUTTON); + else IF_KEY_RETURN( "MOUSE3", VK_MBUTTON); + else IF_KEY_RETURN( "XBUTTON1", VK_XBUTTON1); + else IF_KEY_RETURN( "XBUTTON2", VK_XBUTTON2); return 0; } @@ -704,125 +710,126 @@ bool spec::win32::Window::hasFocus() const { void spec::win32::Window::bufferInputs() { uf::Window::focused = this->hasFocus(); -#define GET_KEYSTATE(X) uf::Window::focused ? GetAsyncKeyState(X) & 0x8000 : false; + #define GET_KEYSTATE(X) uf::Window::focused ? GetAsyncKeyState(X) & 0x8000 : false; + #define STORE_KEYSTATE(K, V) uf::inputs::kbm::states::K = GET_KEYSTATE(V); - uf::inputs::kbm::states::LShift = GET_KEYSTATE(VK_LSHIFT); - uf::inputs::kbm::states::RShift = GET_KEYSTATE(VK_RSHIFT); + STORE_KEYSTATE(LShift, VK_LSHIFT); + STORE_KEYSTATE(RShift, VK_RSHIFT); - uf::inputs::kbm::states::LAlt = GET_KEYSTATE(VK_LMENU); - uf::inputs::kbm::states::RAlt = GET_KEYSTATE(VK_RMENU); + STORE_KEYSTATE(LAlt, VK_LMENU); + STORE_KEYSTATE(RAlt, VK_RMENU); - uf::inputs::kbm::states::LControl = GET_KEYSTATE(VK_LCONTROL); - uf::inputs::kbm::states::RControl = GET_KEYSTATE(VK_RCONTROL); + STORE_KEYSTATE(LControl, VK_LCONTROL); + STORE_KEYSTATE(RControl, VK_RCONTROL); - uf::inputs::kbm::states::LSystem = GET_KEYSTATE(VK_LWIN); - uf::inputs::kbm::states::RSystem = GET_KEYSTATE(VK_RWIN); + STORE_KEYSTATE(LSystem, VK_LWIN); + STORE_KEYSTATE(RSystem, VK_RWIN); - uf::inputs::kbm::states::Menu = GET_KEYSTATE(VK_APPS); - uf::inputs::kbm::states::SemiColon = GET_KEYSTATE(VK_OEM_1); - uf::inputs::kbm::states::Slash = GET_KEYSTATE(VK_OEM_2); - uf::inputs::kbm::states::Equal = GET_KEYSTATE(VK_OEM_PLUS); - uf::inputs::kbm::states::Dash = GET_KEYSTATE(VK_OEM_MINUS); - uf::inputs::kbm::states::LBracket = GET_KEYSTATE(VK_OEM_4); - uf::inputs::kbm::states::RBracket = GET_KEYSTATE(VK_OEM_6); - uf::inputs::kbm::states::Comma = GET_KEYSTATE(VK_OEM_COMMA); - uf::inputs::kbm::states::Period = GET_KEYSTATE(VK_OEM_PERIOD); - uf::inputs::kbm::states::Quote = GET_KEYSTATE(VK_OEM_7); - uf::inputs::kbm::states::BackSlash = GET_KEYSTATE(VK_OEM_5); - uf::inputs::kbm::states::Tilde = GET_KEYSTATE(VK_OEM_3); + STORE_KEYSTATE(Menu, VK_APPS); + STORE_KEYSTATE(SemiColon, VK_OEM_1); + STORE_KEYSTATE(Slash, VK_OEM_2); + STORE_KEYSTATE(Equal, VK_OEM_PLUS); + STORE_KEYSTATE(Dash, VK_OEM_MINUS); + STORE_KEYSTATE(LBracket, VK_OEM_4); + STORE_KEYSTATE(RBracket, VK_OEM_6); + STORE_KEYSTATE(Comma, VK_OEM_COMMA); + STORE_KEYSTATE(Period, VK_OEM_PERIOD); + STORE_KEYSTATE(Quote, VK_OEM_7); + STORE_KEYSTATE(BackSlash, VK_OEM_5); + STORE_KEYSTATE(Tilde, VK_OEM_3); - uf::inputs::kbm::states::Escape = GET_KEYSTATE(VK_ESCAPE); - uf::inputs::kbm::states::Space = GET_KEYSTATE(VK_SPACE); - uf::inputs::kbm::states::Enter = GET_KEYSTATE(VK_RETURN); - uf::inputs::kbm::states::BackSpace = GET_KEYSTATE(VK_BACK); - uf::inputs::kbm::states::Tab = GET_KEYSTATE(VK_TAB); - uf::inputs::kbm::states::PageUp = GET_KEYSTATE(VK_PRIOR); - uf::inputs::kbm::states::PageDown = GET_KEYSTATE(VK_NEXT); - uf::inputs::kbm::states::End = GET_KEYSTATE(VK_END); - uf::inputs::kbm::states::Home = GET_KEYSTATE(VK_HOME); - uf::inputs::kbm::states::Insert = GET_KEYSTATE(VK_INSERT); - uf::inputs::kbm::states::Delete = GET_KEYSTATE(VK_DELETE); - uf::inputs::kbm::states::Add = GET_KEYSTATE(VK_ADD); - uf::inputs::kbm::states::Subtract = GET_KEYSTATE(VK_SUBTRACT); - uf::inputs::kbm::states::Multiply = GET_KEYSTATE(VK_MULTIPLY); - uf::inputs::kbm::states::Divide = GET_KEYSTATE(VK_DIVIDE); - uf::inputs::kbm::states::Pause = GET_KEYSTATE(VK_PAUSE); + STORE_KEYSTATE(Escape, VK_ESCAPE); + STORE_KEYSTATE(Space, VK_SPACE); + STORE_KEYSTATE(Enter, VK_RETURN); + STORE_KEYSTATE(BackSpace, VK_BACK); + STORE_KEYSTATE(Tab, VK_TAB); + STORE_KEYSTATE(PageUp, VK_PRIOR); + STORE_KEYSTATE(PageDown, VK_NEXT); + STORE_KEYSTATE(End, VK_END); + STORE_KEYSTATE(Home, VK_HOME); + STORE_KEYSTATE(Insert, VK_INSERT); + STORE_KEYSTATE(Delete, VK_DELETE); + STORE_KEYSTATE(Add, VK_ADD); + STORE_KEYSTATE(Subtract, VK_SUBTRACT); + STORE_KEYSTATE(Multiply, VK_MULTIPLY); + STORE_KEYSTATE(Divide, VK_DIVIDE); + STORE_KEYSTATE(Pause, VK_PAUSE); - uf::inputs::kbm::states::F1 = GET_KEYSTATE(VK_F1); - uf::inputs::kbm::states::F2 = GET_KEYSTATE(VK_F2); - uf::inputs::kbm::states::F3 = GET_KEYSTATE(VK_F3); - uf::inputs::kbm::states::F4 = GET_KEYSTATE(VK_F4); - uf::inputs::kbm::states::F5 = GET_KEYSTATE(VK_F5); - uf::inputs::kbm::states::F6 = GET_KEYSTATE(VK_F6); - uf::inputs::kbm::states::F7 = GET_KEYSTATE(VK_F7); - uf::inputs::kbm::states::F8 = GET_KEYSTATE(VK_F8); - uf::inputs::kbm::states::F9 = GET_KEYSTATE(VK_F9); - uf::inputs::kbm::states::F10 = GET_KEYSTATE(VK_F10); - uf::inputs::kbm::states::F11 = GET_KEYSTATE(VK_F11); - uf::inputs::kbm::states::F12 = GET_KEYSTATE(VK_F12); - uf::inputs::kbm::states::F13 = GET_KEYSTATE(VK_F13); - uf::inputs::kbm::states::F14 = GET_KEYSTATE(VK_F14); - uf::inputs::kbm::states::F15 = GET_KEYSTATE(VK_F15); + STORE_KEYSTATE(F1, VK_F1); + STORE_KEYSTATE(F2, VK_F2); + STORE_KEYSTATE(F3, VK_F3); + STORE_KEYSTATE(F4, VK_F4); + STORE_KEYSTATE(F5, VK_F5); + STORE_KEYSTATE(F6, VK_F6); + STORE_KEYSTATE(F7, VK_F7); + STORE_KEYSTATE(F8, VK_F8); + STORE_KEYSTATE(F9, VK_F9); + STORE_KEYSTATE(F10, VK_F10); + STORE_KEYSTATE(F11, VK_F11); + STORE_KEYSTATE(F12, VK_F12); + STORE_KEYSTATE(F13, VK_F13); + STORE_KEYSTATE(F14, VK_F14); + STORE_KEYSTATE(F15, VK_F15); - uf::inputs::kbm::states::Left = GET_KEYSTATE(VK_LEFT); - uf::inputs::kbm::states::Right = GET_KEYSTATE(VK_RIGHT); - uf::inputs::kbm::states::Up = GET_KEYSTATE(VK_UP); - uf::inputs::kbm::states::Down = GET_KEYSTATE(VK_DOWN); + STORE_KEYSTATE(Left, VK_LEFT); + STORE_KEYSTATE(Right, VK_RIGHT); + STORE_KEYSTATE(Up, VK_UP); + STORE_KEYSTATE(Down, VK_DOWN); - uf::inputs::kbm::states::Numpad0 = GET_KEYSTATE(VK_NUMPAD0); - uf::inputs::kbm::states::Numpad1 = GET_KEYSTATE(VK_NUMPAD1); - uf::inputs::kbm::states::Numpad2 = GET_KEYSTATE(VK_NUMPAD2); - uf::inputs::kbm::states::Numpad3 = GET_KEYSTATE(VK_NUMPAD3); - uf::inputs::kbm::states::Numpad4 = GET_KEYSTATE(VK_NUMPAD4); - uf::inputs::kbm::states::Numpad5 = GET_KEYSTATE(VK_NUMPAD5); - uf::inputs::kbm::states::Numpad6 = GET_KEYSTATE(VK_NUMPAD6); - uf::inputs::kbm::states::Numpad7 = GET_KEYSTATE(VK_NUMPAD7); - uf::inputs::kbm::states::Numpad8 = GET_KEYSTATE(VK_NUMPAD8); - uf::inputs::kbm::states::Numpad9 = GET_KEYSTATE(VK_NUMPAD9); + STORE_KEYSTATE(Numpad0, VK_NUMPAD0); + STORE_KEYSTATE(Numpad1, VK_NUMPAD1); + STORE_KEYSTATE(Numpad2, VK_NUMPAD2); + STORE_KEYSTATE(Numpad3, VK_NUMPAD3); + STORE_KEYSTATE(Numpad4, VK_NUMPAD4); + STORE_KEYSTATE(Numpad5, VK_NUMPAD5); + STORE_KEYSTATE(Numpad6, VK_NUMPAD6); + STORE_KEYSTATE(Numpad7, VK_NUMPAD7); + STORE_KEYSTATE(Numpad8, VK_NUMPAD8); + STORE_KEYSTATE(Numpad9, VK_NUMPAD9); - uf::inputs::kbm::states::Q = GET_KEYSTATE('Q'); - uf::inputs::kbm::states::W = GET_KEYSTATE('W'); - uf::inputs::kbm::states::E = GET_KEYSTATE('E'); - uf::inputs::kbm::states::R = GET_KEYSTATE('R'); - uf::inputs::kbm::states::T = GET_KEYSTATE('T'); - uf::inputs::kbm::states::Y = GET_KEYSTATE('Y'); - uf::inputs::kbm::states::U = GET_KEYSTATE('U'); - uf::inputs::kbm::states::I = GET_KEYSTATE('I'); - uf::inputs::kbm::states::O = GET_KEYSTATE('O'); - uf::inputs::kbm::states::P = GET_KEYSTATE('P'); + STORE_KEYSTATE(Q, 'Q'); + STORE_KEYSTATE(W, 'W'); + STORE_KEYSTATE(E, 'E'); + STORE_KEYSTATE(R, 'R'); + STORE_KEYSTATE(T, 'T'); + STORE_KEYSTATE(Y, 'Y'); + STORE_KEYSTATE(U, 'U'); + STORE_KEYSTATE(I, 'I'); + STORE_KEYSTATE(O, 'O'); + STORE_KEYSTATE(P, 'P'); - uf::inputs::kbm::states::A = GET_KEYSTATE('A'); - uf::inputs::kbm::states::S = GET_KEYSTATE('S'); - uf::inputs::kbm::states::D = GET_KEYSTATE('D'); - uf::inputs::kbm::states::F = GET_KEYSTATE('F'); - uf::inputs::kbm::states::G = GET_KEYSTATE('G'); - uf::inputs::kbm::states::H = GET_KEYSTATE('H'); - uf::inputs::kbm::states::J = GET_KEYSTATE('J'); - uf::inputs::kbm::states::K = GET_KEYSTATE('K'); - uf::inputs::kbm::states::L = GET_KEYSTATE('L'); + STORE_KEYSTATE(A, 'A'); + STORE_KEYSTATE(S, 'S'); + STORE_KEYSTATE(D, 'D'); + STORE_KEYSTATE(F, 'F'); + STORE_KEYSTATE(G, 'G'); + STORE_KEYSTATE(H, 'H'); + STORE_KEYSTATE(J, 'J'); + STORE_KEYSTATE(K, 'K'); + STORE_KEYSTATE(L, 'L'); - uf::inputs::kbm::states::Z = GET_KEYSTATE('Z'); - uf::inputs::kbm::states::X = GET_KEYSTATE('X'); - uf::inputs::kbm::states::C = GET_KEYSTATE('C'); - uf::inputs::kbm::states::V = GET_KEYSTATE('V'); - uf::inputs::kbm::states::B = GET_KEYSTATE('B'); - uf::inputs::kbm::states::N = GET_KEYSTATE('N'); - uf::inputs::kbm::states::M = GET_KEYSTATE('M'); + STORE_KEYSTATE(Z, 'Z'); + STORE_KEYSTATE(X, 'X'); + STORE_KEYSTATE(C, 'C'); + STORE_KEYSTATE(V, 'V'); + STORE_KEYSTATE(B, 'B'); + STORE_KEYSTATE(N, 'N'); + STORE_KEYSTATE(M, 'M'); - uf::inputs::kbm::states::Num1 = GET_KEYSTATE('1'); - uf::inputs::kbm::states::Num2 = GET_KEYSTATE('2'); - uf::inputs::kbm::states::Num3 = GET_KEYSTATE('3'); - uf::inputs::kbm::states::Num4 = GET_KEYSTATE('4'); - uf::inputs::kbm::states::Num5 = GET_KEYSTATE('5'); - uf::inputs::kbm::states::Num6 = GET_KEYSTATE('6'); - uf::inputs::kbm::states::Num7 = GET_KEYSTATE('7'); - uf::inputs::kbm::states::Num8 = GET_KEYSTATE('8'); - uf::inputs::kbm::states::Num9 = GET_KEYSTATE('9'); - uf::inputs::kbm::states::Num0 = GET_KEYSTATE('0'); + STORE_KEYSTATE(Num1, '1'); + STORE_KEYSTATE(Num2, '2'); + STORE_KEYSTATE(Num3, '3'); + STORE_KEYSTATE(Num4, '4'); + STORE_KEYSTATE(Num5, '5'); + STORE_KEYSTATE(Num6, '6'); + STORE_KEYSTATE(Num7, '7'); + STORE_KEYSTATE(Num8, '8'); + STORE_KEYSTATE(Num9, '9'); + STORE_KEYSTATE(Num0, '0'); - uf::inputs::kbm::states::Mouse1 = GET_KEYSTATE(VK_LBUTTON); - uf::inputs::kbm::states::Mouse2 = GET_KEYSTATE(VK_RBUTTON); - uf::inputs::kbm::states::Mouse3 = GET_KEYSTATE(VK_MBUTTON); + STORE_KEYSTATE(Mouse1, VK_LBUTTON); + STORE_KEYSTATE(Mouse2, VK_RBUTTON); + STORE_KEYSTATE(Mouse3, VK_MBUTTON); uf::inputs::kbm::states::MouseWheel = uf::Window::focused ? ::lastMouseWheel : 0; ::lastMouseWheel = 0; @@ -896,73 +903,11 @@ bool spec::win32::Window::pollEvents( bool block ) { while ( !this->m_events.empty() ) { auto& event = this->m_events.front(); - /* - if ( event.payload.is() ) { - ext::json::Value payload = uf::Serializer( event.payload.as() ); - uf::hooks.call( "window:Event", payload ); - uf::hooks.call( event.name, payload ); - } else if ( event.payload.is() ) { - uf::Serializer& payload = event.payload.as(); - uf::hooks.call( "window:Event", payload ); - uf::hooks.call( event.name, payload ); - } else if ( event.payload.is() ) { - ext::json::Value& payload = event.payload.as(); - uf::hooks.call( "window:Event", payload ); - uf::hooks.call( event.name, payload ); - } else */ { - uf::hooks.call( "window:Event", event.payload ); - uf::hooks.call( event.name, event.payload ); - } + uf::hooks.call( "window:Event", event.payload ); + uf::hooks.call( event.name, event.payload ); this->m_events.pop(); } return true; -#if 0 - /* Empty; look for something! */ if ( this->m_events.readable.empty() && this->m_events.optimal.empty() ) { - do { - this->processEvents(); - } while ( block && this->m_events.readable.empty() && this->m_events.optimal.empty() ); - } - - if ( uf::hooks.prefersReadable() ) // Should we handle events in a readable format... - /* Process events parsed in a readable format */ { - // process things - while ( !this->m_events.readable.empty() ) { - auto& event = this->m_events.readable.front(); - uf::hooks.call( event.name, event.argument ); - /* - try { - uf::hooks.call( "window:Event", event.argument ); - uf::hooks.call( event.name, event.argument ); - // uf::iostream << event.name << "\t" << event.argument << "\n"; - } catch ( ... ) { - // Let the hook handler handle the exceptions - } - */ - this->m_events.readable.pop(); - } - // this->m_events.optimal.clear(); // flush queue in case - } else // ... or in an optimal format? - /* Process events parsed in an optimal format */ { - // process things - while ( !this->m_events.optimal.empty() ) { - auto& event = this->m_events.optimal.front(); - uf::hooks.call( event.name, event.argument ); - /* - try { - uf::hooks.call( "window:Event", event.argument ); - uf::hooks.call( event.name, event.argument ); - // uf::iostream << event.name << "\t" << event.argument << "\n"; - } catch ( ... ) { - // Let the hook handler handle the exceptions - } - */ - uf::userdata::destroy(event.argument); - this->m_events.optimal.pop(); - } - // this->m_events.readable.clear(); // flush queue in case - } - return true; -#endif } void spec::win32::Window::registerWindowClass() { diff --git a/engine/src/utils/io/file.cpp b/engine/src/utils/io/file.cpp index c243b95a..c79227d4 100644 --- a/engine/src/utils/io/file.cpp +++ b/engine/src/utils/io/file.cpp @@ -271,7 +271,7 @@ size_t uf::io::mtime( const uf::stl::string& _filename ) { return buffer.st_mtime; } bool uf::io::mkdir( const uf::stl::string& _filename ) { -#if UF_ENV_DREAMCAST +#if UF_ENV_DREAMCAST || UF_ENV_LINUX return false; #else uf::stl::string filename = sanitize(_filename); diff --git a/engine/src/utils/io/iostream.cpp b/engine/src/utils/io/iostream.cpp index e2105d89..e92c48bb 100644 --- a/engine/src/utils/io/iostream.cpp +++ b/engine/src/utils/io/iostream.cpp @@ -56,7 +56,7 @@ bool uf::IoStream::ncurses = false; uf::IoStream uf::iostream; uf::IoStream::IoStream() { - spec::terminal.setLocale(); + //spec::terminal.setLocale(); } uf::IoStream::~IoStream() { } @@ -326,7 +326,7 @@ std::ostream& operator<< ( std::ostream& os, uf::IoStream& io ) { } uf::stl::string uf::IoStream::getColor() { - return this->m_currentColor; + return ""; // this->m_currentColor; } void uf::IoStream::setColor( const uf::stl::string& str ) { } diff --git a/engine/src/utils/serialize/serializer.cpp b/engine/src/utils/serialize/serializer.cpp index 001fb4c2..611376ab 100644 --- a/engine/src/utils/serialize/serializer.cpp +++ b/engine/src/utils/serialize/serializer.cpp @@ -17,11 +17,7 @@ #define UF_SERIALIZER_AUTO_CONVERT 1 #endif -//#define UF_MSG_DEBUG_(...) UF_MSG_DEBUG(__VA_ARGS__) -#define UF_MSG_DEBUG_(...) - -uf::Serializer::Serializer() { -} +// uf::Serializer::Serializer() {} uf::Serializer::Serializer( const uf::stl::string& str ) { this->deserialize(str); } diff --git a/engine/src/utils/thread/thread.cpp b/engine/src/utils/thread/thread.cpp index 0263049b..84a4937e 100644 --- a/engine/src/utils/thread/thread.cpp +++ b/engine/src/utils/thread/thread.cpp @@ -29,7 +29,7 @@ void uf::thread::quit( pod::Thread& thread ) { if ( !thread.running ) return; } void uf::thread::tick( pod::Thread& thread ) { -#if !UF_ENV_DREAMCAST +#if UF_ENV_WINDOWS bool res = SetThreadAffinityMask(GetCurrentThread(), (1u << thread.affinity)); // if ( !res ) UF_THREAD_ANNOUNCE("Failed to set affinity of Thread #" << thread.uid << " (" << thread.name << " on core " << pthread_self() << "/" << thread.affinity << ")"); #endif diff --git a/engine/src/utils/time/time.cpp b/engine/src/utils/time/time.cpp index 0f131428..d29220e8 100644 --- a/engine/src/utils/time/time.cpp +++ b/engine/src/utils/time/time.cpp @@ -1,7 +1,7 @@ #include // time(r) namespace { - uf::Time<> zero = spec::time.getTime(); // Program epoch, used for getting time deltas (without uf::Timer) + uf::Time<> zero = spec::time::getTime(); // Program epoch, used for getting time deltas (without uf::Timer) } // extern UF_API uf::Timer<> timer; // System timer, used for getting time deltas @@ -13,5 +13,5 @@ float uf::time::delta = 0; float uf::time::clamp = 0; size_t uf::time::time() { - return spec::time.unixTime(); + return spec::time::unixTime(); } \ No newline at end of file diff --git a/makefiles/default/arch b/makefiles/default/arch index 6ec5a6aa..a08e1f35 100644 --- a/makefiles/default/arch +++ b/makefiles/default/arch @@ -1 +1 @@ -win64 \ No newline at end of file +linux diff --git a/makefiles/linux.clang.make b/makefiles/linux.clang.make new file mode 100644 index 00000000..8c6f8540 --- /dev/null +++ b/makefiles/linux.clang.make @@ -0,0 +1,11 @@ +ARCH = linux +CDIR = +CC = clang +CXX = clang++ +OPTIMIZATIONS = -O3 -fstrict-aliasing -DUF_NO_EXCEPTIONS # -flto # -march=native +WARNINGS = -Wall -Wno-pointer-arith -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder-ctor -Wno-ignored-attributes -Wno-c++11-narrowing -Wno-unknown-pragmas -Wno-nullability-completeness -Wno-defaulted-function-deleted -Wno-mismatched-tags +SANITIZE = -fsanitize=address # -fuse-ld=lld -fno-omit-frame-pointer +FLAGS += -std=c++2b $(OPTIMIZATIONS) $(WARNINGS) $(SANITIZE) -fcolor-diagnostics -fansi-escape-codes + +LIB_EXTENSION = so +LIB_EXTENSION_A = \ No newline at end of file diff --git a/makefiles/linux.gcc.make b/makefiles/linux.gcc.make new file mode 100644 index 00000000..f1b203d5 --- /dev/null +++ b/makefiles/linux.gcc.make @@ -0,0 +1,10 @@ +ARCH = linux +CDIR = +CC = gcc +CXX = g++ +OPTIMIZATIONS = -O3 -fstrict-aliasing -DUF_NO_EXCEPTIONS +WARNINGS = -Wall -Wno-attributes -Wno-dangling-reference -Wno-unknown-pragmas -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder -Wno-sign-compare -Wno-unused-but-set-variable -Wno-ignored-attributes -Wno-narrowing -Wno-misleading-indentation +FLAGS += -std=c++2b $(OPTIMIZATIONS) $(WARNINGS) -fdiagnostics-color=always + +LIB_EXTENSION = so +LIB_EXTENSION_A = \ No newline at end of file diff --git a/makefiles/win64.clang.make b/makefiles/win64.clang.make index fd9dc50e..4a562092 100644 --- a/makefiles/win64.clang.make +++ b/makefiles/win64.clang.make @@ -2,7 +2,7 @@ ARCH = win64 CDIR = CC = clang CXX = clang++ -OPTIMIZATIONS = -O3 -fstrict-aliasing -DUF_NO_EXCEPTIONS -flto # -march=native +OPTIMIZATIONS = -O3 -fstrict-aliasing -DUF_NO_EXCEPTIONS # -flto # -march=native WARNINGS = -Wall -Wno-pointer-arith -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder-ctor -Wno-ignored-attributes -Wno-c++11-narrowing -Wno-unknown-pragmas -Wno-nullability-completeness -Wno-defaulted-function-deleted -Wno-mismatched-tags -SANITIZE = -fuse-ld=lld # -fsanitize=address -fno-omit-frame-pointer +SANITIZE = -fsanitize=address # -fuse-ld=lld -fno-omit-frame-pointer FLAGS += -std=c++2b $(OPTIMIZATIONS) $(WARNINGS) $(SANITIZE) -fcolor-diagnostics -fansi-escape-codes \ No newline at end of file diff --git a/program.sh b/program.sh old mode 100644 new mode 100755 index 741760be..cf26ef68 --- a/program.sh +++ b/program.sh @@ -1,5 +1,4 @@ #!/bin/bash -tskill program cd bin ARCH=$(cat ./exe/default/arch) @@ -7,6 +6,7 @@ CC=$(cat ./exe/default/cc) RENDERER=$(cat ./exe/default/renderer) export PATH="$(pwd)/exe/lib/${ARCH}/:$(pwd)/exe/lib/${ARCH}/${CC}/:$(pwd)/exe/lib/${ARCH}/${CC}/${RENDERER}/:${PATH}" +export LD_LIBRARY_PATH="$(pwd)/exe/lib/${ARCH}/:$(pwd)/exe/lib/${ARCH}/${CC}/:$(pwd)/exe/lib/${ARCH}/${CC}/${RENDERER}/:${LD_LIBRARY_PATH}" echo PATH: ${PATH} echo Executing ./exe/program.${ARCH}.${CC}.${RENDERER}.exe $@