From 49f33598219577252f3eb54899e88311a22b2e21 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Thu, 8 May 2025 13:18:52 +0200 Subject: [PATCH 01/47] Fix Linux IDE build --- BeefBuild/BeefProj.toml | 4 +-- BeefLibs/Beefy2D/BeefProj.toml | 12 +++++++ BeefLibs/corlib/src/GC.bf | 3 ++ BeefySysLib/CMakeLists.txt | 3 +- BeefySysLib/platform/linux/LinuxCommon.h | 1 + BeefySysLib/platform/sdl/SdlBFApp.cpp | 13 ++++++- IDE/BeefProj.toml | 20 +++++++++++ IDE/BeefSpace.toml | 44 ++++++++++++++++++++++++ IDEHelper/CMakeLists.txt | 5 +++ bin/build.sh | 15 ++++++-- 10 files changed, 114 insertions(+), 6 deletions(-) diff --git a/BeefBuild/BeefProj.toml b/BeefBuild/BeefProj.toml index 0fc316c1a..6c56885f0 100644 --- a/BeefBuild/BeefProj.toml +++ b/BeefBuild/BeefProj.toml @@ -40,7 +40,7 @@ TargetName = "$(ProjectName)_d" OtherLinkFlags = "$(LinkFlags) ./libIDEHelper_d.a ./libBeefySysLib_d.a ./libhunspell.so $(Var IDEHelperLibs) -Wl,-rpath -Wl,$ORIGIN" CLibType = "Dynamic" PreBuildCmds = ["ReadFile(\"$(WorkspaceDir)/../IDE/dist/IDEHelper_libs_d.txt\", \"IDEHelperLibs\")"] -DebugCommandArguments = "-proddir=..\\ -config=Debug -platform=Win64" +DebugCommandArguments = "-proddir=..\\ -config=Debug -platform=Linux64" DebugWorkingDirectory = "$(WorkspaceDir)/../IDE/dist" PreprocessorMacros = ["DEBUG", "CLI"] @@ -58,7 +58,7 @@ TargetDirectory = "$(WorkspaceDir)/../IDE/dist" OtherLinkFlags = "$(LinkFlags) ./libIDEHelper.a ./libBeefySysLib.a ./libhunspell.so $(Var IDEHelperLibs) -Wl,-rpath -Wl,$ORIGIN" CLibType = "Dynamic" PreBuildCmds = ["ReadFile(\"$(WorkspaceDir)/../IDE/dist/IDEHelper_libs.txt\", \"IDEHelperLibs\")"] -DebugCommandArguments = "-proddir=..\\ -config=Debug -platform=Win64" +DebugCommandArguments = "-proddir=..\\ -config=Debug -platform=Linux64" DebugWorkingDirectory = "$(WorkspaceDir)/../IDE/dist" PreprocessorMacros = ["CLI"] diff --git a/BeefLibs/Beefy2D/BeefProj.toml b/BeefLibs/Beefy2D/BeefProj.toml index f7bbe7043..f9dcf2f53 100644 --- a/BeefLibs/Beefy2D/BeefProj.toml +++ b/BeefLibs/Beefy2D/BeefProj.toml @@ -16,6 +16,12 @@ CLibType = "Static" BeefLibType = "Static" PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64_d.dll\")", "CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64_d.pdb\")"] +[Configs.Debug.Linux64] +OtherLinkFlags = "$(LinkFlags) \"$(ProjectDir)/dist/libBeefySysLib_d.a\"" +CLibType = "Static" +BeefLibType = "Static" +PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/libBeefySysLib_d.a\")"] + [Configs.Release.Win32] OtherLinkFlags = "" PreprocessorMacros = ["RELEASE", "BF32"] @@ -24,6 +30,12 @@ PreprocessorMacros = ["RELEASE", "BF32"] OtherLinkFlags = "$(LinkFlags) \"$(ProjectDir)/dist/BeefySysLib64.lib\"" PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64.dll\")", "CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64.pdb\")"] +[Configs.Release.Linux64] +OtherLinkFlags = "$(LinkFlags) \"$(ProjectDir)/dist/libBeefySysLib.a\"" +CLibType = "Static" +BeefLibType = "Static" +PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/libBeefySysLib.a\")"] + [Configs.Paranoid.Win32] CLibType = "Static" BeefLibType = "Static" diff --git a/BeefLibs/corlib/src/GC.bf b/BeefLibs/corlib/src/GC.bf index 2d2e6478e..276a0da90 100644 --- a/BeefLibs/corlib/src/GC.bf +++ b/BeefLibs/corlib/src/GC.bf @@ -139,13 +139,16 @@ namespace System public extern static void ExcludeThreadId(int thereadId); #else public static void Disable() {} + [LinkName("beefCollect")] public static void Collect(bool async = true) {} private static void MarkAllStaticMembers() {} public static void DebugDumpLeaks() {} [SkipCall] public static void Mark(Object obj) {} public static void Mark(void* ptr, int size) {} + [LinkName("beefSetAutoCollectPeriod")] public static void SetAutoCollectPeriod(int periodMS) {} + [LinkName("beefSetCollectFreeThreshold")] public static void SetCollectFreeThreshold(int freeBytes) {} public static void SetMaxPausePercentage(int maxPausePercentage) {} static void AddPendingThread(void* internalThreadInfo) {} diff --git a/BeefySysLib/CMakeLists.txt b/BeefySysLib/CMakeLists.txt index 860940495..3ae5b7c94 100644 --- a/BeefySysLib/CMakeLists.txt +++ b/BeefySysLib/CMakeLists.txt @@ -324,10 +324,11 @@ else() endif() if (DEFINED BF_ENABLE_SDL) - file(GLOB SRC_FILES_OS + file(GLOB SRC_FILES_SDL platform/sdl/SdlBFApp.cpp platform/sdl/GLRenderDevice.cpp ) + list(APPEND SRC_FILES_OS ${SRC_FILES_SDL}) endif() # Add library to build. diff --git a/BeefySysLib/platform/linux/LinuxCommon.h b/BeefySysLib/platform/linux/LinuxCommon.h index 466ce3552..680d899c4 100644 --- a/BeefySysLib/platform/linux/LinuxCommon.h +++ b/BeefySysLib/platform/linux/LinuxCommon.h @@ -25,6 +25,7 @@ #include #include #include +#include //#define offsetof(type, member) __builtin_offsetof (type, member) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index d310f6db7..ae5dbf59f 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -1,7 +1,9 @@ #include "SdlBFApp.h" +#include "Common.h" #include "GLRenderDevice.h" #include "platform/PlatformHelper.h" #include +#include USING_NS_BF; @@ -34,15 +36,20 @@ static HMODULE GetSDLModule(const StringImpl& installDir) { if (gSDLModule == NULL) { +#if defined (BF_PLATFORM_WINDOWS) String loadPath = installDir + "SDL2.dll"; gSDLModule = ::LoadLibraryA(loadPath.c_str()); +#elif defined (BF_PLATFORM_LINUX) + String loadPath = "/usr/lib/libSDL2.so"; + gSDLModule = dlopen(loadPath.c_str(), RTLD_LAZY); +#endif if (gSDLModule == NULL) { #ifdef BF_PLATFORM_WINDOWS ::MessageBoxA(NULL, "Failed to load SDL2.dll", "FATAL ERROR", MB_OK | MB_ICONERROR); ::ExitProcess(1); #endif - BF_FATAL("Failed to load SDL2.dll"); + BF_FATAL("Failed to load libSDL2.so"); } } return gSDLModule; @@ -51,7 +58,11 @@ static HMODULE GetSDLModule(const StringImpl& installDir) template static void BFGetSDLProc(T& proc, const char* name, const StringImpl& installDir) { +#if defined (BF_PLATFORM_WINDOWS) proc = (T)::GetProcAddress(GetSDLModule(installDir), name); +#elif defined (BF_PLATFORM_LINUX) + proc = (T)dlsym(GetSDLModule(installDir), name); +#endif } #define BF_GET_SDLPROC(name) BFGetSDLProc(bf_##name, #name, mInstallDir) diff --git a/IDE/BeefProj.toml b/IDE/BeefProj.toml index 3ae2269e3..f161cac00 100644 --- a/IDE/BeefProj.toml +++ b/IDE/BeefProj.toml @@ -29,6 +29,16 @@ DebugWorkingDirectory = "$(ProjectDir)\\.." EnvironmentVars = ["_NO_DEBUG_HEAP=1"] PreprocessorMacros = ["DEBUG,HASGIT"] +[Configs.Debug.Linux64] +TargetDirectory = "$(ProjectDir)/dist" +TargetName = "BeefIDE_d" +OtherLinkFlags = "$(LinkFlags) libIDEHelper_d.a libBeefySysLib_d.a libhunspell.so $(Var IDEHelperLibs)" +PreBuildCmds = ["ReadFile(\"$(WorkspaceDir)/dist/IDEHelper_libs_d.txt\", \"IDEHelperLibs\")"] +DebugCommandArguments = "-proddir=\"$(ProjectDir)\\..\\BeefBuild\"" +DebugWorkingDirectory = "$(ProjectDir)\\.." +EnvironmentVars = ["_NO_DEBUG_HEAP=1"] +PreprocessorMacros = ["DEBUG,HASGIT"] + [Configs.Release.Win32] TargetName = "" OtherLinkFlags = "" @@ -42,6 +52,16 @@ DebugWorkingDirectory = "$(ProjectDir)\\dist" EnvironmentVars = ["z_NO_DEBUG_HEAP=1"] PreprocessorMacros = ["RELEASE,HASGIT"] +[Configs.Release.Linux64] +TargetDirectory = "$(ProjectDir)/dist" +TargetName = "BeefIDE" +OtherLinkFlags = "libBeefRT.a libIDEHelper.a libBeefySysLib.a libhunspell.so $(Var IDEHelperLibs)" +PreBuildCmds = ["ReadFile(\"$(WorkspaceDir)/dist/IDEHelper_libs_d.txt\", \"IDEHelperLibs\")"] +DebugCommandArguments = "-proddir=\"$(ProjectDir)\\..\\BeefBuild\"" +DebugWorkingDirectory = "$(ProjectDir)\\dist" +EnvironmentVars = ["z_NO_DEBUG_HEAP=1"] +PreprocessorMacros = ["RELEASE,HASGIT"] + [Configs.Debug2.Win32] TargetName = "" OtherLinkFlags = "" diff --git a/IDE/BeefSpace.toml b/IDE/BeefSpace.toml index 9831a0f82..b1100309a 100644 --- a/IDE/BeefSpace.toml +++ b/IDE/BeefSpace.toml @@ -15,6 +15,9 @@ ConfigSelections = {IDEHelper = {Enabled = false}} [Configs.Debug.Win64] EnableSideStack = true +[Configs.Debug.Linux64] +EnableSideStack = true + [Configs.Release.Win32] BfOptimizationLevel = "OgPlus" EmitDebugInfo = "No" @@ -25,6 +28,10 @@ ConfigSelections = {IDEHelper = {Enabled = false}} BfOptimizationLevel = "O3" AllocStackTraceDepth = 0 +[Configs.Release.Linux64] +BfOptimizationLevel = "O3" +AllocStackTraceDepth = 0 + [Configs.Debug-IDE.Win32] EmitDebugInfo = "No" EmitDynamicCastCheck = false @@ -43,6 +50,15 @@ EmitObjectAccessCheck = false EnableRealtimeLeakCheck = false ConfigSelections = {IDE = {Config = "Debug"}, Beefy2D = {Config = "Debug"}, IDEHelper = {Config = "Debug"}, Debugger64 = {Config = "Debug"}, BeefySysLib = {Config = "Debug"}} +[Configs.Debug-IDE.Linux64] +BfOptimizationLevel = "O0" +EmitDebugInfo = "No" +EmitDynamicCastCheck = false +EnableObjectDebugFlags = false +EmitObjectAccessCheck = false +EnableRealtimeLeakCheck = false +ConfigSelections = {IDE = {Config = "Debug"}, Beefy2D = {Config = "Debug"}, IDEHelper = {Config = "Debug"}, Debugger64 = {Config = "Debug"}, BeefySysLib = {Config = "Debug"}} + [Configs.Debug2.Win32] EmitDebugInfo = "No" EmitDynamicCastCheck = false @@ -56,6 +72,10 @@ ConfigSelections = {Beefy2D = {Config = "Debug"}, IDEHelper = {Config = "Debug"} PreprocessorMacros = ["NEWFONT"] ConfigSelections = {Beefy2D = {Config = "Debug"}, IDEHelper = {Enabled = false, Config = "Debug"}, Debugger64 = {Enabled = false, Config = "Debug"}, BeefySysLib = {Enabled = false, Config = "Debug"}} +[Configs.Debug2.Linux64] +PreprocessorMacros = ["NEWFONT"] +ConfigSelections = {Beefy2D = {Config = "Debug"}, IDEHelper = {Enabled = false, Config = "Debug"}, Debugger64 = {Enabled = false, Config = "Debug"}, BeefySysLib = {Enabled = false, Config = "Debug"}} + [Configs.Paranoid.Win32] EmitDynamicCastCheck = false EnableObjectDebugFlags = false @@ -73,6 +93,15 @@ EnableSideStack = false COptimizationLevel = "O2" ConfigSelections = {IDEHelper = {Config = "Debug"}, Debugger64 = {Config = "Debug"}, BeefySysLib = {Config = "Debug"}} +[Configs.Paranoid.Linux64] +EmitDynamicCastCheck = false +EnableObjectDebugFlags = false +EmitObjectAccessCheck = false +EnableRealtimeLeakCheck = false +EnableSideStack = false +COptimizationLevel = "O2" +ConfigSelections = {IDEHelper = {Config = "Debug"}, Debugger64 = {Config = "Debug"}, BeefySysLib = {Config = "Debug"}} + [Configs.Test.Win32] EmitDynamicCastCheck = false EnableObjectDebugFlags = false @@ -87,6 +116,13 @@ EmitObjectAccessCheck = false EnableRealtimeLeakCheck = false COptimizationLevel = "O2" +[Configs.Test.Linux64] +EmitDynamicCastCheck = false +EnableObjectDebugFlags = false +EmitObjectAccessCheck = false +EnableRealtimeLeakCheck = false +COptimizationLevel = "O2" + [Configs.Debug3.Win32] EmitDebugInfo = "No" EmitDynamicCastCheck = false @@ -100,6 +136,10 @@ AllocStackTraceDepth = 0 BfOptimizationLevel = "O0" ConfigSelections = {Beefy2D = {Config = "Debug"}, IDEHelper = {Config = "Debug"}, Debugger64 = {Config = "Debug"}, BeefySysLib = {Config = "Debug"}} +[Configs.Debug3.Linux64] +BfOptimizationLevel = "O0" +ConfigSelections = {Beefy2D = {Config = "Debug"}, IDEHelper = {Config = "Debug"}, Debugger64 = {Config = "Debug"}, BeefySysLib = {Config = "Debug"}} + [Configs.Debug_NoDeps.Win32] InitLocalVariables = true EmitObjectAccessCheck = false @@ -110,3 +150,7 @@ ConfigSelections = {IDEHelper = {Enabled = false, Config = "Debug"}, IDE = {Conf [Configs.Debug_NoDeps.Win64] EnableSideStack = true ConfigSelections = {IDE = {Config = "Debug"}, Beefy2D = {Config = "Debug"}, IDEHelper = {Enabled = false, Config = "Debug"}, Debugger64 = {Enabled = false, Config = "Debug"}, BeefySysLib = {Enabled = false, Config = "Debug"}} + +[Configs.Debug_NoDeps.Linux64] +EnableSideStack = true +ConfigSelections = {IDE = {Config = "Debug"}, Beefy2D = {Config = "Debug"}, IDEHelper = {Enabled = false, Config = "Debug"}, Debugger64 = {Enabled = false, Config = "Debug"}, BeefySysLib = {Enabled = false, Config = "Debug"}} diff --git a/IDEHelper/CMakeLists.txt b/IDEHelper/CMakeLists.txt index db51f53c3..e5e41fbd8 100644 --- a/IDEHelper/CMakeLists.txt +++ b/IDEHelper/CMakeLists.txt @@ -193,6 +193,11 @@ if (HAVE_BACKTRACE_HEADERS) string(STRIP ${TARGET_LIBS_OS} TARGET_LIBS_OS) endif() +if (DEFINED BF_ENABLE_SDL) + string(APPEND TARGET_LIBS_OS " -lGL") + string(STRIP ${TARGET_LIBS_OS} TARGET_LIBS_OS) +endif() + if (CMAKE_BUILD_TYPE STREQUAL "Debug" ) FILE(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/../IDE/dist/IDEHelper_libs_d.txt" ${TARGET_LIBS_OS}) else() diff --git a/bin/build.sh b/bin/build.sh index 0d1c26a55..da9a9f47a 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -19,6 +19,7 @@ do if [[ $i == "sdl" ]]; then echo "Using SDL" USE_SDL="-DBF_ENABLE_SDL=1" + SDL_LINKOPTS="-lGL" fi if [[ $i == "no_ffi" ]]; then @@ -140,7 +141,7 @@ ln -s -f $ROOTPATH/jbuild/Release/bin/libIDEHelper.a ../../BeefLibs/Beefy2D/dist ### DEBUG ### echo Building BeefBuild_bootd -../../jbuild_d/Debug/bin/BeefBoot --out="BeefBuild_bootd" --src=../src --src=../../BeefBuild/src --src=../../BeefLibs/corlib/src --src=../../BeefLibs/Beefy2D/src --define=CLI --define=DEBUG --startup=BeefBuild.Program --linkparams="./libBeefRT_d.a ./libIDEHelper_d.a ./libBeefySysLib_d.a ./libhunspell.$LIBEXT $(< ../../IDE/dist/IDEHelper_libs_d.txt) $LINKOPTS" +../../jbuild_d/Debug/bin/BeefBoot --out="BeefBuild_bootd" --src=../src --src=../../BeefBuild/src --src=../../BeefLibs/corlib/src --src=../../BeefLibs/Beefy2D/src --define=CLI --define=DEBUG --startup=BeefBuild.Program --linkparams="./libBeefRT_d.a ./libIDEHelper_d.a ./libBeefySysLib_d.a ./libhunspell.$LIBEXT $(< ../../IDE/dist/IDEHelper_libs_d.txt) $SDL_LINKOPTS $LINKOPTS" echo Building BeefBuild_d ./BeefBuild_bootd -clean -proddir=../../BeefBuild -config=Debug echo Testing IDEHelper/Tests in BeefBuild_d @@ -149,8 +150,18 @@ echo Testing IDEHelper/Tests in BeefBuild_d ### RELEASE ### echo Building BeefBuild_boot -../../jbuild/Release/bin/BeefBoot --out="BeefBuild_boot" --src=../src --src=../../BeefBuild/src --src=../../BeefLibs/corlib/src --src=../../BeefLibs/Beefy2D/src --define=CLI --startup=BeefBuild.Program --linkparams="./libBeefRT.a ./libIDEHelper.a ./libBeefySysLib.a ./libhunspell.$LIBEXT $(< ../../IDE/dist/IDEHelper_libs.txt) $LINKOPTS" +../../jbuild/Release/bin/BeefBoot --out="BeefBuild_boot" --src=../src --src=../../BeefBuild/src --src=../../BeefLibs/corlib/src --src=../../BeefLibs/Beefy2D/src --define=CLI --startup=BeefBuild.Program --linkparams="./libBeefRT.a ./libIDEHelper.a ./libBeefySysLib.a ./libhunspell.$LIBEXT $(< ../../IDE/dist/IDEHelper_libs.txt) $SDL_LINKOPTS $LINKOPTS" echo Building BeefBuild ./BeefBuild_boot -clean -proddir=../../BeefBuild -config=Release echo Testing IDEHelper/Tests in BeefBuild ./BeefBuild -proddir=../../IDEHelper/Tests -test + +### IDE ### +if [ -n "$USE_SDL" ]; then + + echo Building BeefIDE_d + ./BeefBuild -clean -proddir=../ -config=Debug + echo Building BeefIDE + ./BeefBuild -clean -proddir=../ -config=Release + +fi \ No newline at end of file From ccf45212bb892933f642f9647747375e9eb9f45d Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Thu, 8 May 2025 15:24:40 +0200 Subject: [PATCH 02/47] Fix Linux IDE crash at launch --- IDE/src/IDEApp.bf | 3 +++ IDE/src/ui/WelcomePanel.bf | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index 1b03083de..c376e1f8a 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -3438,6 +3438,9 @@ namespace IDE delete mWorkspace.mDir; mWorkspace.mDir = newPath; } + else if (mWorkspace.mDir == null) { + mWorkspace.mDir = new String(); + } List platforms = scope List(); if (IDEApp.sPlatform32Name != null) diff --git a/IDE/src/ui/WelcomePanel.bf b/IDE/src/ui/WelcomePanel.bf index 8ec896d41..e983e6d59 100644 --- a/IDE/src/ui/WelcomePanel.bf +++ b/IDE/src/ui/WelcomePanel.bf @@ -70,17 +70,17 @@ namespace IDE.ui public this() { - mSampleImg0 = Image.LoadFromFile(scope String()..AppendF(@"{}\images\welcome_sample0.png", gApp.mInstallDir)); - mSampleImg1 = Image.LoadFromFile(scope String()..AppendF(@"{}\images\welcome_sample1.png", gApp.mInstallDir)); + mSampleImg0 = Image.LoadFromFile(scope String()..AppendF(@"{}/images/welcome_sample0.png", gApp.mInstallDir)); + mSampleImg1 = Image.LoadFromFile(scope String()..AppendF(@"{}/images/welcome_sample1.png", gApp.mInstallDir)); mSampleBtn0 = new .(); - mSampleBtn0.mPath = new String()..AppendF(@"{}\..\Samples\SpaceGame\BeefSpace.toml", gApp.mInstallDir); + mSampleBtn0.mPath = new String()..AppendF(@"{}/../Samples/SpaceGame/BeefSpace.toml", gApp.mInstallDir); mSampleBtn0.mLabel = new String("Space Game"); mSampleBtn0.mImage = mSampleImg0; AddWidget(mSampleBtn0); mSampleBtn1 = new .(); - mSampleBtn1.mPath = new String()..AppendF(@"{}\..\Samples\HelloWorld\BeefSpace.toml", gApp.mInstallDir); + mSampleBtn1.mPath = new String()..AppendF(@"{}/../Samples/HelloWorld/BeefSpace.toml", gApp.mInstallDir); mSampleBtn1.mLabel = new String("Hello World"); mSampleBtn1.mImage = mSampleImg1; AddWidget(mSampleBtn1); From b638c72d0e960c79a9e9489035cb4540862cf87f Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Fri, 9 May 2025 00:35:13 +0200 Subject: [PATCH 03/47] SDL handle window resize --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index ae5dbf59f..2b6b12aa6 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -2,7 +2,10 @@ #include "Common.h" #include "GLRenderDevice.h" #include "platform/PlatformHelper.h" +#include "platform/PlatformInterface.h" #include +#include +#include #include USING_NS_BF; @@ -363,6 +366,27 @@ void SdlBFApp::Run() sdlBFWindow->mKeyUpFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.keysym.scancode)); } break; + case SDL_WINDOWEVENT: + { + switch(sdlEvent.window.event) + { + case SDL_WINDOWEVENT_MOVED: + case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if (sdlBFWindow != NULL) + { + sdlBFWindow->mMovedFunc(sdlBFWindow); + if (sdlEvent.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + sdlBFWindow->mRenderWindow->Resized(); + } + } + } + break; + } + } + break; } } @@ -407,6 +431,9 @@ void SdlBFWindow::GetPosition(int* x, int* y, int* width, int* height, int* clie { bf_SDL_GetWindowPosition(mSDLWindow, x, y); bf_SDL_GetWindowSize(mSDLWindow, width, height); + + *clientX = *x; + *clientY = *y; *clientWidth = *width; *clientHeight = *height; } From 34d40814b665e9662998e3126400edf4e944be05 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Mon, 12 May 2025 14:25:48 +0200 Subject: [PATCH 04/47] Handle GLContext between windows --- BeefySysLib/platform/sdl/GLRenderDevice.cpp | 5 +++ BeefySysLib/platform/sdl/SdlBFApp.cpp | 34 +++++++++++++-------- BeefySysLib/platform/sdl/SdlBFApp.h | 2 ++ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/BeefySysLib/platform/sdl/GLRenderDevice.cpp b/BeefySysLib/platform/sdl/GLRenderDevice.cpp index 28d3a5583..816cb040b 100644 --- a/BeefySysLib/platform/sdl/GLRenderDevice.cpp +++ b/BeefySysLib/platform/sdl/GLRenderDevice.cpp @@ -1,4 +1,5 @@ #include "GLRenderDevice.h" +#include "BFApp.h" #include "SdlBFApp.h" #include "BFWindow.h" #include "img/ImageData.h" @@ -41,6 +42,7 @@ USING_NS_BF; extern void* (SDLCALL* bf_SDL_GL_GetProcAddress)(const char* proc); extern void (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); extern void (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); +extern int (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); typedef void (APIENTRYP GL_DEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam); @@ -427,6 +429,8 @@ GLRenderWindow::~GLRenderWindow() void GLRenderWindow::PhysSetAsTarget() { + bf_SDL_GL_MakeCurrent(mSDLWindow, ((SdlBFApp*)gBFApp)->mGLContext ); + GLfloat matrix[4][4]; CreateOrthographicOffCenter(0.0f, (float)mWidth, (float)mHeight, 0.0f, -100.0f, 100.0f, matrix); glViewport(0, 0, (GLsizei)mWidth, (GLsizei)mHeight); @@ -439,6 +443,7 @@ void GLRenderWindow::SetAsTarget() //TODO: Handle this more elegantly when we actually handle draw layers properly... //if (mRenderDevice->mCurRenderTarget != NULL) //mRenderDevice->mCurDrawLayer->Flush(); + bf_SDL_GL_MakeCurrent(mSDLWindow, ((SdlBFApp*)gBFApp)->mGLContext ); mHasBeenTargeted = true; mRenderDevice->mCurRenderTarget = this; diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 2b6b12aa6..ddc18e8fe 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -1,11 +1,14 @@ #include "SdlBFApp.h" +#include "BFApp.h" #include "Common.h" #include "GLRenderDevice.h" #include "platform/PlatformHelper.h" #include "platform/PlatformInterface.h" #include #include +#include #include +#include #include USING_NS_BF; @@ -31,6 +34,7 @@ void (SDLCALL* bf_SDL_SetWindowPosition)(SDL_Window* window, int x, int y); int (SDLCALL* bf_SDL_PollEvent)(SDL_Event* event); const char* (SDLCALL* bf_SDL_GetError)(void); SDL_GLContext (SDLCALL* bf_SDL_GL_CreateContext)(SDL_Window* window); +int (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); void (SDLCALL* bf_SDL_Quit)(void); static HMODULE gSDLModule; @@ -90,20 +94,23 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y bf_SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); #endif - if (!bf_SDL_GL_CreateContext(mSDLWindow)) + if(((SdlBFApp*)gBFApp)->mGLContext == NULL) { - String str = StrFormat( -#ifdef BF_PLATFORM_OPENGL_ES2 - "Unable to create SDL OpenGLES context: %s" -#else - "Unable to create SDL OpenGL context: %s" -#endif - , bf_SDL_GetError()); - - - BF_FATAL(str.c_str()); - bf_SDL_Quit(); - exit(2); + if (!(((SdlBFApp*)gBFApp)->mGLContext = bf_SDL_GL_CreateContext(mSDLWindow))) + { + String str = StrFormat( + #ifdef BF_PLATFORM_OPENGL_ES2 + "Unable to create SDL OpenGLES context: %s" + #else + "Unable to create SDL OpenGL context: %s" + #endif + , bf_SDL_GetError()); + + + BF_FATAL(str.c_str()); + bf_SDL_Quit(); + exit(2); + } } #ifndef BF_PLATFORM_OPENGL_ES2 @@ -270,6 +277,7 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_PollEvent); BF_GET_SDLPROC(SDL_GetError); BF_GET_SDLPROC(SDL_GL_CreateContext); + BF_GET_SDLPROC(SDL_GL_MakeCurrent); BF_GET_SDLPROC(SDL_Quit); } diff --git a/BeefySysLib/platform/sdl/SdlBFApp.h b/BeefySysLib/platform/sdl/SdlBFApp.h index f22f45fb8..2366d6b84 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.h +++ b/BeefySysLib/platform/sdl/SdlBFApp.h @@ -2,6 +2,7 @@ #include "BFApp.h" #include "BFWindow.h" +#include #include #include "util/Dictionary.h" @@ -54,6 +55,7 @@ class SdlBFApp : public BFApp public: bool mInMsgProc; SdlWindowMap mSdlWindowMap; + SDL_GLContext mGLContext; protected: virtual void Draw() override; From 3525d95275a38b020085e356ad45ff0d9a5f0ec2 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Mon, 12 May 2025 14:26:37 +0200 Subject: [PATCH 05/47] Fix unhandled nulls --- BeefLibs/corlib/src/IO/FolderBrowserDialog.bf | 9 +++++- IDE/src/ui/HoverWatch.bf | 31 ++++++++++--------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/BeefLibs/corlib/src/IO/FolderBrowserDialog.bf b/BeefLibs/corlib/src/IO/FolderBrowserDialog.bf index 7c8e6d044..101a83842 100644 --- a/BeefLibs/corlib/src/IO/FolderBrowserDialog.bf +++ b/BeefLibs/corlib/src/IO/FolderBrowserDialog.bf @@ -246,7 +246,14 @@ public class FolderBrowserDialog : CommonDialog } set { - mInitialDir.Set(value); + if(mInitialDir == null) + { + mInitialDir = new .(value); + } + else + { + mInitialDir.Set(value); + } } } diff --git a/IDE/src/ui/HoverWatch.bf b/IDE/src/ui/HoverWatch.bf index a5bf5af72..bef06ba71 100644 --- a/IDE/src/ui/HoverWatch.bf +++ b/IDE/src/ui/HoverWatch.bf @@ -456,20 +456,23 @@ namespace IDE.ui { base.Update(); - for (let item in mListView.GetRoot().mChildItems) - { - let listViewItem = (HoverListViewItem)item; - if (listViewItem.mWatchEntry?.mIsPending == true) - { - if ((gApp.mDebugger.IsPaused(true)) && (!gApp.mDebugger.HasMessages())) - { - let listView = (HoverListView)listViewItem.mListView; - DoListViewItem(listView, listViewItem, listViewItem.mWatchEntry.mName, listViewItem.mWatchEntry.mEvalStr, true); - FinishListView(listView, listView.mX, listView.mY, true); - gApp.RefreshVisibleViews(); - } - } - } + if(mListView != null) + { + for (let item in mListView.GetRoot().mChildItems) + { + let listViewItem = (HoverListViewItem)item; + if (listViewItem.mWatchEntry?.mIsPending == true) + { + if ((gApp.mDebugger.IsPaused(true)) && (!gApp.mDebugger.HasMessages())) + { + let listView = (HoverListView)listViewItem.mListView; + DoListViewItem(listView, listViewItem, listViewItem.mWatchEntry.mName, listViewItem.mWatchEntry.mEvalStr, true); + FinishListView(listView, listView.mX, listView.mY, true); + gApp.RefreshVisibleViews(); + } + } + } + } if (mCloseDelay > 1) mCloseDelay--; From ad1f573e1d4675a8518a18e086eeabc283f05888 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Mon, 12 May 2025 16:28:08 +0200 Subject: [PATCH 06/47] Fix mouse buttons with SDL --- BeefLibs/Beefy2D/src/widgets/Widget.bf | 5 +++++ BeefySysLib/platform/sdl/SdlBFApp.cpp | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/BeefLibs/Beefy2D/src/widgets/Widget.bf b/BeefLibs/Beefy2D/src/widgets/Widget.bf index 4ecee7092..600c457c6 100644 --- a/BeefLibs/Beefy2D/src/widgets/Widget.bf +++ b/BeefLibs/Beefy2D/src/widgets/Widget.bf @@ -12,8 +12,13 @@ namespace Beefy.widgets public enum MouseFlag { Left = 1, +#if BF_PLATFORM_WINDOWS Right = 2, Middle = 4, +#else + Right = 4, + Middle = 2, +#endif Kbd = 8 } diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index ddc18e8fe..bfd3d0c87 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -332,14 +332,14 @@ void SdlBFApp::Run() { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); if (sdlBFWindow != NULL) - sdlBFWindow->mMouseUpFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, sdlEvent.button.button); + sdlBFWindow->mMouseUpFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, sdlEvent.button.button - 1); } break; case SDL_MOUSEBUTTONDOWN: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); if (sdlBFWindow != NULL) - sdlBFWindow->mMouseDownFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, sdlEvent.button.button, 1); + sdlBFWindow->mMouseDownFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, sdlEvent.button.button - 1, 1); } break; case SDL_MOUSEMOTION: From 74e2afb9768d546217f30bf185524d7d3105aa15 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Mon, 12 May 2025 19:56:54 +0200 Subject: [PATCH 07/47] SDL scrolling, return, backspace and tab --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index bfd3d0c87..935039845 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -161,7 +161,7 @@ static int SDLConvertScanCode(int scanCode) { case SDL_SCANCODE_9: return '0'; case SDL_SCANCODE_CANCEL: return 0x03; - case SDL_SCANCODE_AC_BACK: return 0x08; + case SDL_SCANCODE_BACKSPACE: return 0x08; case SDL_SCANCODE_TAB: return 0x09; case SDL_SCANCODE_CLEAR: return 0x0C; case SDL_SCANCODE_RETURN: return 0x0D; @@ -349,12 +349,31 @@ void SdlBFApp::Run() sdlBFWindow->mMouseMoveFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y); } break; + case SDL_MOUSEWHEEL: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.wheel.windowID); + if(sdlBFWindow != NULL) + sdlBFWindow->mMouseWheelFunc(sdlBFWindow, sdlEvent.wheel.mouseX, sdlEvent.wheel.mouseY, sdlEvent.wheel.preciseX, sdlEvent.wheel.preciseY); + } case SDL_KEYDOWN: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); if (sdlBFWindow != NULL) { sdlBFWindow->mKeyDownFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.keysym.scancode), sdlEvent.key.repeat); + switch (sdlEvent.key.keysym.scancode) // These keys are not handled by SDL_TEXTINPUT + { + case SDL_SCANCODE_RETURN: + sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\n'); + break; + case SDL_SCANCODE_BACKSPACE: + sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\b'); + break; + case SDL_SCANCODE_TAB: + sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\t'); + break; + default:; + } } } break; From 2f4a44f7bb731af8225f7a1cbe8edde4041fcc98 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Mon, 12 May 2025 19:58:08 +0200 Subject: [PATCH 08/47] Disable debugger Evaluate on linux --- IDEHelper/DebugManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/IDEHelper/DebugManager.cpp b/IDEHelper/DebugManager.cpp index aa1709a6a..fc5ba3a2d 100644 --- a/IDEHelper/DebugManager.cpp +++ b/IDEHelper/DebugManager.cpp @@ -1304,7 +1304,9 @@ BF_EXPORT StringView BF_CALLTYPE Debugger_Evaluate(const char* expr, int callSta String& outString = *gTLStrReturn.Get(); outString.clear(); +#ifdef BF_PLATFORM_WINDOWS outString = debugger->Evaluate(expr, callStackIdx, cursorPos, language, (DwEvalExpressionFlags)expressionFlags); +#endif #ifdef BF_WANTS_LOG_DBG { int crPos = (int)outString.IndexOf('\n'); From fb87a98713182714eb243ee0cb108132887ce566 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Mon, 14 Jul 2025 20:34:55 +0200 Subject: [PATCH 09/47] Cleaner SDL mouse buttons and tooltips --- BeefLibs/Beefy2D/src/BFWindow.bf | 1 + .../Beefy2D/src/theme/dark/DarkTooltip.bf | 2 +- BeefLibs/Beefy2D/src/widgets/Widget.bf | 5 --- BeefySysLib/BFWindow.h | 3 +- BeefySysLib/platform/sdl/SdlBFApp.cpp | 44 ++++++++++++++++++- IDE/src/ui/HoverWatch.bf | 4 +- 6 files changed, 48 insertions(+), 11 deletions(-) diff --git a/BeefLibs/Beefy2D/src/BFWindow.bf b/BeefLibs/Beefy2D/src/BFWindow.bf index f5cbe2a56..5079a0465 100644 --- a/BeefLibs/Beefy2D/src/BFWindow.bf +++ b/BeefLibs/Beefy2D/src/BFWindow.bf @@ -52,6 +52,7 @@ namespace Beefy AcceptFiles = 0x1000'0000, NoShow = 0x2000'0000, NoMouse = 0x4000'0000, + Tooltip = 0x8000'0000 }; [AllowDuplicates] diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf index 3fe64668b..abf95b828 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf @@ -71,7 +71,7 @@ namespace Beefy.theme.dark screenX += relWidget.mWidgetWindow.mClientX; screenY += relWidget.mWidgetWindow.mClientY; - BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized | BFWindow.Flags.PopupPosition | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha; + BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized | BFWindow.Flags.PopupPosition | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha | BFWindow.Flags.Tooltip; WidgetWindow widgetWindow = new WidgetWindow(relWidget.mWidgetWindow, "Tooltip", (int32)(screenX), (int32)(screenY), diff --git a/BeefLibs/Beefy2D/src/widgets/Widget.bf b/BeefLibs/Beefy2D/src/widgets/Widget.bf index 600c457c6..4ecee7092 100644 --- a/BeefLibs/Beefy2D/src/widgets/Widget.bf +++ b/BeefLibs/Beefy2D/src/widgets/Widget.bf @@ -12,13 +12,8 @@ namespace Beefy.widgets public enum MouseFlag { Left = 1, -#if BF_PLATFORM_WINDOWS Right = 2, Middle = 4, -#else - Right = 4, - Middle = 2, -#endif Kbd = 8 } diff --git a/BeefySysLib/BFWindow.h b/BeefySysLib/BFWindow.h index 5196dcdfd..bd5a846c1 100644 --- a/BeefySysLib/BFWindow.h +++ b/BeefySysLib/BFWindow.h @@ -57,7 +57,8 @@ enum BFWINDOW_ALLOW_FULLSCREEN = 0x8000000, BFWINDOW_ACCEPTFILES = 0x10000000, BFWINDOW_NOSHOW = 0x20000000, - BFWINDOW_NO_MOUSE = 0x40000000 + BFWINDOW_NO_MOUSE = 0x40000000, + BFWINDOW_TOOLTIP = 0x80000000 }; diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 935039845..68a024997 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,10 @@ const char* (SDLCALL* bf_SDL_GetError)(void); SDL_GLContext (SDLCALL* bf_SDL_GL_CreateContext)(SDL_Window* window); int (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); void (SDLCALL* bf_SDL_Quit)(void); +int (SDLCALL* bf_SDL_GetNumVideoDisplays)(void); +int (SDLCALL* bf_SDL_GetDisplayBounds)(int displayIndex, SDL_Rect* rect); + +static int bfMouseBtnOf[4] = {NULL, 0, 2, 1}; // Translate SDL mouse buttons to what Beef expects. static HMODULE gSDLModule; @@ -82,10 +87,43 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y sdlWindowFlags |= SDL_WINDOW_OPENGL; if (windowFlags & BFWINDOW_FULLSCREEN) sdlWindowFlags |= SDL_WINDOW_FULLSCREEN; + if (!(windowFlags & BFWINDOW_BORDER)) + sdlWindowFlags |= SDL_WINDOW_BORDERLESS; + if (windowFlags & BFWINDOW_TOOLTIP) + sdlWindowFlags |= SDL_WINDOW_TOOLTIP; #ifdef BF_PLATFORM_FULLSCREEN sdlWindowFlags |= SDL_WINDOW_FULLSCREEN; #endif + if (windowFlags & BFWINDOW_POPUP_POSITION) + { + SDL_Rect adjustRect = { x, y, width, height }; + SDL_Rect wantRect = { x, y, x + width, y + height }; + + int monCount = bf_SDL_GetNumVideoDisplays(); + for (int i = 0; i < monCount; i++) + { + SDL_Rect bounds; + if (bf_SDL_GetDisplayBounds(i, &bounds)) + { + if (adjustRect.x < bounds.x) + adjustRect.x = bounds.x; + else if (adjustRect.x + adjustRect.w >= (bounds.x + bounds.w)) + adjustRect.x = BF_MAX((int)bounds.x, bounds.x + bounds.w - adjustRect.w); + + if (adjustRect.y < bounds.y) + adjustRect.y = bounds.y; + else if (adjustRect.y + adjustRect.h >= bounds.y + bounds.h) + adjustRect.y = BF_MAX((int)bounds.y, bounds.y + bounds.h - adjustRect.h); + } + } + + x = adjustRect.x; + y = adjustRect.y; + width = adjustRect.w; + height = adjustRect.h; + } + mSDLWindow = bf_SDL_CreateWindow(title.c_str(), x, y, width, height, sdlWindowFlags); #ifndef BF_PLATFORM_OPENGL_ES2 @@ -279,6 +317,8 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_GL_CreateContext); BF_GET_SDLPROC(SDL_GL_MakeCurrent); BF_GET_SDLPROC(SDL_Quit); + BF_GET_SDLPROC(SDL_GetNumVideoDisplays); + BF_GET_SDLPROC(SDL_GetDisplayBounds); } mDataDir = mInstallDir; @@ -332,14 +372,14 @@ void SdlBFApp::Run() { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); if (sdlBFWindow != NULL) - sdlBFWindow->mMouseUpFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, sdlEvent.button.button - 1); + sdlBFWindow->mMouseUpFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, bfMouseBtnOf[sdlEvent.button.button]); } break; case SDL_MOUSEBUTTONDOWN: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); if (sdlBFWindow != NULL) - sdlBFWindow->mMouseDownFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, sdlEvent.button.button - 1, 1); + sdlBFWindow->mMouseDownFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, bfMouseBtnOf[sdlEvent.button.button], 1); } break; case SDL_MOUSEMOTION: diff --git a/IDE/src/ui/HoverWatch.bf b/IDE/src/ui/HoverWatch.bf index 917dc18bf..182e0be6e 100644 --- a/IDE/src/ui/HoverWatch.bf +++ b/IDE/src/ui/HoverWatch.bf @@ -1085,7 +1085,7 @@ namespace IDE.ui var parentWidget = GetParentWidget(); - BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized /*| BFWindow.Flags.PopupPosition*/ | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha; + BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized /*| BFWindow.Flags.PopupPosition*/ | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha | BFWindow.Flags.Tooltip; #unwarn WidgetWindow widgetWindow = new WidgetWindow(parentWidget.mWidgetWindow, "HoverWatch", @@ -1685,7 +1685,7 @@ namespace IDE.ui /*float screenWidth = mListView.mWidth + 8; float screenHeight = mListView.mHeight + 8; - BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized /*| BFWindow.Flags.PopupPosition*/ | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha; + BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized /*| BFWindow.Flags.PopupPosition*/ | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha | BFWindow.Flags.Tooltip; #unwarn WidgetWindow widgetWindow = new WidgetWindow(parentWidget.mWidgetWindow, "HoverWatch", From 77c8f60073916d6f7f43ba5b13d64a560b79be4b Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Sat, 19 Jul 2025 08:50:31 +0200 Subject: [PATCH 10/47] MIgrate to SDL3 --- BeefySysLib/platform/sdl/GLRenderDevice.cpp | 10 +- BeefySysLib/platform/sdl/GLRenderDevice.h | 4 +- BeefySysLib/platform/sdl/SdlBFApp.cpp | 228 ++++++++++---------- BeefySysLib/platform/sdl/SdlBFApp.h | 2 +- 4 files changed, 126 insertions(+), 118 deletions(-) diff --git a/BeefySysLib/platform/sdl/GLRenderDevice.cpp b/BeefySysLib/platform/sdl/GLRenderDevice.cpp index 816cb040b..160031630 100644 --- a/BeefySysLib/platform/sdl/GLRenderDevice.cpp +++ b/BeefySysLib/platform/sdl/GLRenderDevice.cpp @@ -4,7 +4,7 @@ #include "BFWindow.h" #include "img/ImageData.h" #include "util/PerfTimer.h" -#include +#include USING_NS_BF; @@ -12,7 +12,7 @@ USING_NS_BF; #define NOT_IMPL throw "Not implemented" #endif -//#pragma comment(lib, "SDL2.lib") +//#pragma comment(lib, "SDL3.lib") #ifdef _WIN32 #ifdef BF_PLATFORM_OPENGL_ES2 @@ -40,9 +40,9 @@ USING_NS_BF; #endif extern void* (SDLCALL* bf_SDL_GL_GetProcAddress)(const char* proc); -extern void (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); -extern void (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); -extern int (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); +extern bool (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); +extern bool (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); +extern bool (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); typedef void (APIENTRYP GL_DEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam); diff --git a/BeefySysLib/platform/sdl/GLRenderDevice.h b/BeefySysLib/platform/sdl/GLRenderDevice.h index 241ff57fc..db072d50f 100644 --- a/BeefySysLib/platform/sdl/GLRenderDevice.h +++ b/BeefySysLib/platform/sdl/GLRenderDevice.h @@ -4,9 +4,9 @@ #include "util/Dictionary.h" #ifdef BF_PLATFORM_OPENGL_ES2 -#include +#include #else -#include +#include #endif #include "gfx/Shader.h" diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 68a024997..c23d81436 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -4,13 +4,19 @@ #include "GLRenderDevice.h" #include "platform/PlatformHelper.h" #include "platform/PlatformInterface.h" -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include #include +#include +#include USING_NS_BF; @@ -19,26 +25,38 @@ USING_NS_BF; #pragma comment(lib, "imm32.lib") #pragma comment(lib, "version.lib") -SDL_Window* (SDLCALL* bf_SDL_CreateWindow)(const char* title, int x, int y, int w, int h, Uint32 flags); -int (SDLCALL* bf_SDL_GL_SetAttribute)(SDL_GLattr attr, int value); -Uint32 (SDLCALL* bf_SDL_GetWindowID)(SDL_Window* window); +bool (SDLCALL* bf_SDL_Init)(SDL_InitFlags flags); +void (SDLCALL* bf_SDL_Quit)(void); +void (SDLCALL* bf_SDL_free)(void* mem); + +SDL_PropertiesID (SDLCALL* bf_SDL_CreateProperties)(void); +bool (SDLCALL* bf_SDL_SetNumberProperty)(SDL_PropertiesID props, const char* name, int64_t value); +bool (SDLCALL* bf_SDL_SetBooleanProperty)(SDL_PropertiesID props, const char* name, bool value); +bool (SDLCALL* bf_SDL_SetStringProperty)(SDL_PropertiesID props, const char* name, const char* value); +bool (SDLCALL* bf_SDL_SetPointerProperty)(SDL_PropertiesID props, const char *name, void *value); + +SDL_Window* (SDLCALL* bf_SDL_CreateWindowWithProperties)(SDL_PropertiesID props); +SDL_WindowID (SDLCALL* bf_SDL_GetWindowID)(SDL_Window* window); void (SDLCALL* bf_SDL_DestroyWindow)(SDL_Window* window); -int (SDLCALL* bf_SDL_Init)(Uint32 flags); -void (SDLCALL* bf_SDL_GetWindowPosition)(SDL_Window* window,int* x, int* y); +bool (SDLCALL* bf_SDL_GetWindowPosition)(SDL_Window* window,int* x, int* y); +bool (SDLCALL* bf_SDL_SetWindowPosition)(SDL_Window* window, int x, int y); +bool (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); + char* (SDLCALL* bf_SDL_GetClipboardText)(void); -int (SDLCALL* bf_SDL_SetClipboardText)(const char* text); -void* (SDLCALL* bf_SDL_GL_GetProcAddress)(const char* proc); -void (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); -void (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); -void (SDLCALL* bf_SDL_free)(void* mem); -void (SDLCALL* bf_SDL_SetWindowPosition)(SDL_Window* window, int x, int y); -int (SDLCALL* bf_SDL_PollEvent)(SDL_Event* event); +bool (SDLCALL* bf_SDL_SetClipboardText)(const char* text); + +bool (SDLCALL* bf_SDL_PollEvent)(SDL_Event* event); const char* (SDLCALL* bf_SDL_GetError)(void); + +SDL_DisplayID* (SDLCALL* bf_SDL_GetDisplays)(int* count); +bool (SDLCALL* bf_SDL_GetDisplayBounds)(SDL_DisplayID displayID, SDL_Rect* rect); + SDL_GLContext (SDLCALL* bf_SDL_GL_CreateContext)(SDL_Window* window); -int (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); -void (SDLCALL* bf_SDL_Quit)(void); -int (SDLCALL* bf_SDL_GetNumVideoDisplays)(void); -int (SDLCALL* bf_SDL_GetDisplayBounds)(int displayIndex, SDL_Rect* rect); +bool (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); +bool (SDLCALL* bf_SDL_GL_SetAttribute)(SDL_GLAttr attr, int value); +void* (SDLCALL* bf_SDL_GL_GetProcAddress)(const char* proc); +bool (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); + static int bfMouseBtnOf[4] = {NULL, 0, 2, 1}; // Translate SDL mouse buttons to what Beef expects. @@ -49,19 +67,19 @@ static HMODULE GetSDLModule(const StringImpl& installDir) if (gSDLModule == NULL) { #if defined (BF_PLATFORM_WINDOWS) - String loadPath = installDir + "SDL2.dll"; + String loadPath = installDir + "SDL3.dll"; gSDLModule = ::LoadLibraryA(loadPath.c_str()); #elif defined (BF_PLATFORM_LINUX) - String loadPath = "/usr/lib/libSDL2.so"; + String loadPath = "/usr/lib/libSDL3.so"; gSDLModule = dlopen(loadPath.c_str(), RTLD_LAZY); #endif if (gSDLModule == NULL) { #ifdef BF_PLATFORM_WINDOWS - ::MessageBoxA(NULL, "Failed to load SDL2.dll", "FATAL ERROR", MB_OK | MB_ICONERROR); + ::MessageBoxA(NULL, "Failed to load SDL3.dll", "FATAL ERROR", MB_OK | MB_ICONERROR); ::ExitProcess(1); #endif - BF_FATAL("Failed to load libSDL2.so"); + BF_FATAL("Failed to load libSDL3.so"); } } return gSDLModule; @@ -81,50 +99,31 @@ static void BFGetSDLProc(T& proc, const char* name, const StringImpl& installDir SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags) { - int sdlWindowFlags = 0; - if (windowFlags & BFWINDOW_RESIZABLE) - sdlWindowFlags |= SDL_WINDOW_RESIZABLE; - sdlWindowFlags |= SDL_WINDOW_OPENGL; - if (windowFlags & BFWINDOW_FULLSCREEN) - sdlWindowFlags |= SDL_WINDOW_FULLSCREEN; - if (!(windowFlags & BFWINDOW_BORDER)) - sdlWindowFlags |= SDL_WINDOW_BORDERLESS; - if (windowFlags & BFWINDOW_TOOLTIP) - sdlWindowFlags |= SDL_WINDOW_TOOLTIP; -#ifdef BF_PLATFORM_FULLSCREEN - sdlWindowFlags |= SDL_WINDOW_FULLSCREEN; -#endif + SDL_PropertiesID props = bf_SDL_CreateProperties(); - if (windowFlags & BFWINDOW_POPUP_POSITION) - { - SDL_Rect adjustRect = { x, y, width, height }; - SDL_Rect wantRect = { x, y, x + width, y + height }; + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, (windowFlags & BFWINDOW_RESIZABLE) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, (windowFlags & BFWINDOW_FULLSCREEN) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN, (windowFlags & BFWINDOW_BORDER) == 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN, (windowFlags & BFWINDOW_TOOLTIP) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN, (windowFlags & BFWINDOW_DEST_ALPHA) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN, (windowFlags & BFWINDOW_FAKEFOCUS) > 0); - int monCount = bf_SDL_GetNumVideoDisplays(); - for (int i = 0; i < monCount; i++) - { - SDL_Rect bounds; - if (bf_SDL_GetDisplayBounds(i, &bounds)) - { - if (adjustRect.x < bounds.x) - adjustRect.x = bounds.x; - else if (adjustRect.x + adjustRect.w >= (bounds.x + bounds.w)) - adjustRect.x = BF_MAX((int)bounds.x, bounds.x + bounds.w - adjustRect.w); - - if (adjustRect.y < bounds.y) - adjustRect.y = bounds.y; - else if (adjustRect.y + adjustRect.h >= bounds.y + bounds.h) - adjustRect.y = BF_MAX((int)bounds.y, bounds.y + bounds.h - adjustRect.h); - } - } + if (parent != NULL) + bf_SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, ((SdlBFWindow*)parent)->mSDLWindow); - x = adjustRect.x; - y = adjustRect.y; - width = adjustRect.w; - height = adjustRect.h; - } + if (windowFlags) +#ifdef BF_PLATFORM_FULLSCREEN + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, true); +#endif + + bf_SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, x); + bf_SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y); + bf_SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, width); + bf_SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, height); + bf_SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, title.c_str()); - mSDLWindow = bf_SDL_CreateWindow(title.c_str(), x, y, width, height, sdlWindowFlags); + mSDLWindow = bf_SDL_CreateWindowWithProperties(props); #ifndef BF_PLATFORM_OPENGL_ES2 bf_SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); @@ -297,33 +296,44 @@ SdlBFApp::SdlBFApp() mInstallDir += "/"; - if (bf_SDL_CreateWindow == NULL) + if (bf_SDL_Init == NULL) { - BF_GET_SDLPROC(SDL_CreateWindow); - BF_GET_SDLPROC(SDL_GL_SetAttribute); + BF_GET_SDLPROC(SDL_Init); + BF_GET_SDLPROC(SDL_Quit); + BF_GET_SDLPROC(SDL_free); + + BF_GET_SDLPROC(SDL_CreateProperties); + BF_GET_SDLPROC(SDL_SetNumberProperty); + BF_GET_SDLPROC(SDL_SetBooleanProperty); + BF_GET_SDLPROC(SDL_SetStringProperty); + BF_GET_SDLPROC(SDL_SetPointerProperty); + + BF_GET_SDLPROC(SDL_CreateWindowWithProperties); BF_GET_SDLPROC(SDL_GetWindowID); BF_GET_SDLPROC(SDL_DestroyWindow); - BF_GET_SDLPROC(SDL_Init); BF_GET_SDLPROC(SDL_GetWindowPosition); + BF_GET_SDLPROC(SDL_SetWindowPosition); + BF_GET_SDLPROC(SDL_GetWindowSize); + BF_GET_SDLPROC(SDL_GetClipboardText); BF_GET_SDLPROC(SDL_SetClipboardText); - BF_GET_SDLPROC(SDL_GL_GetProcAddress); - BF_GET_SDLPROC(SDL_GetWindowSize); - BF_GET_SDLPROC(SDL_GL_SwapWindow); - BF_GET_SDLPROC(SDL_free); - BF_GET_SDLPROC(SDL_SetWindowPosition); + BF_GET_SDLPROC(SDL_PollEvent); BF_GET_SDLPROC(SDL_GetError); + + BF_GET_SDLPROC(SDL_GetDisplays); + BF_GET_SDLPROC(SDL_GetDisplayBounds); + BF_GET_SDLPROC(SDL_GL_CreateContext); BF_GET_SDLPROC(SDL_GL_MakeCurrent); - BF_GET_SDLPROC(SDL_Quit); - BF_GET_SDLPROC(SDL_GetNumVideoDisplays); - BF_GET_SDLPROC(SDL_GetDisplayBounds); + BF_GET_SDLPROC(SDL_GL_SetAttribute); + BF_GET_SDLPROC(SDL_GL_GetProcAddress); + BF_GET_SDLPROC(SDL_GL_SwapWindow); } mDataDir = mInstallDir; - if (bf_SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) + if (bf_SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD) < 0) BF_FATAL(StrFormat("Unable to initialize SDL: %s", bf_SDL_GetError()).c_str()); } @@ -364,44 +374,44 @@ void SdlBFApp::Run() switch (sdlEvent.type) { - case SDL_QUIT: + case SDL_EVENT_QUIT: //gBFApp->RemoveWindow(sdlEvent.window); Shutdown(); break; - case SDL_MOUSEBUTTONUP: + case SDL_EVENT_MOUSE_BUTTON_UP: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); if (sdlBFWindow != NULL) sdlBFWindow->mMouseUpFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, bfMouseBtnOf[sdlEvent.button.button]); } break; - case SDL_MOUSEBUTTONDOWN: + case SDL_EVENT_MOUSE_BUTTON_DOWN: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); if (sdlBFWindow != NULL) sdlBFWindow->mMouseDownFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, bfMouseBtnOf[sdlEvent.button.button], 1); } break; - case SDL_MOUSEMOTION: + case SDL_EVENT_MOUSE_MOTION: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); if (sdlBFWindow != NULL) sdlBFWindow->mMouseMoveFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y); } break; - case SDL_MOUSEWHEEL: + case SDL_EVENT_MOUSE_WHEEL: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.wheel.windowID); if(sdlBFWindow != NULL) - sdlBFWindow->mMouseWheelFunc(sdlBFWindow, sdlEvent.wheel.mouseX, sdlEvent.wheel.mouseY, sdlEvent.wheel.preciseX, sdlEvent.wheel.preciseY); + sdlBFWindow->mMouseWheelFunc(sdlBFWindow, sdlEvent.wheel.mouse_x, sdlEvent.wheel.mouse_y, sdlEvent.wheel.x, sdlEvent.wheel.y); } - case SDL_KEYDOWN: + case SDL_EVENT_KEY_DOWN: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); if (sdlBFWindow != NULL) { - sdlBFWindow->mKeyDownFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.keysym.scancode), sdlEvent.key.repeat); - switch (sdlEvent.key.keysym.scancode) // These keys are not handled by SDL_TEXTINPUT + sdlBFWindow->mKeyDownFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.key), sdlEvent.key.repeat); + switch (sdlEvent.key.key) // These keys are not handled by SDL_TEXTINPUT { case SDL_SCANCODE_RETURN: sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\n'); @@ -417,7 +427,7 @@ void SdlBFApp::Run() } } break; - case SDL_TEXTINPUT: + case SDL_EVENT_TEXT_INPUT: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); if (sdlBFWindow != NULL) @@ -426,34 +436,28 @@ void SdlBFApp::Run() } } break; - case SDL_KEYUP: + case SDL_EVENT_KEY_UP: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); if (sdlBFWindow != NULL) - sdlBFWindow->mKeyUpFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.keysym.scancode)); + sdlBFWindow->mKeyUpFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.key)); } break; - case SDL_WINDOWEVENT: + case SDL_EVENT_WINDOW_MOVED: + case SDL_EVENT_WINDOW_RESIZED: + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: { - switch(sdlEvent.window.event) + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if (sdlBFWindow != NULL) { - case SDL_WINDOWEVENT_MOVED: - case SDL_WINDOWEVENT_RESIZED: - case SDL_WINDOWEVENT_SIZE_CHANGED: - { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); - if (sdlBFWindow != NULL) - { - sdlBFWindow->mMovedFunc(sdlBFWindow); - if (sdlEvent.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { - sdlBFWindow->mRenderWindow->Resized(); - } - } + sdlBFWindow->mMovedFunc(sdlBFWindow); + if (sdlEvent.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) { + sdlBFWindow->mRenderWindow->Resized(); } - break; } } break; + } } @@ -471,7 +475,7 @@ void SdlBFApp::Draw() glDisable(GL_SCISSOR_TEST); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); - glClearColor(1.0f, 0.0f, 1.0f, 1.0f); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); gPixelsDrawn = 0; @@ -499,10 +503,14 @@ void SdlBFWindow::GetPosition(int* x, int* y, int* width, int* height, int* clie bf_SDL_GetWindowPosition(mSDLWindow, x, y); bf_SDL_GetWindowSize(mSDLWindow, width, height); - *clientX = *x; - *clientY = *y; - *clientWidth = *width; - *clientHeight = *height; + if (clientX) + *clientX = *x; + if (clientY) + *clientY = *y; + if (clientWidth) + *clientWidth = *width; + if (clientHeight) + *clientHeight = *height; } void SdlBFApp::PhysSetCursor() diff --git a/BeefySysLib/platform/sdl/SdlBFApp.h b/BeefySysLib/platform/sdl/SdlBFApp.h index 2366d6b84..471a6e6b6 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.h +++ b/BeefySysLib/platform/sdl/SdlBFApp.h @@ -2,7 +2,7 @@ #include "BFApp.h" #include "BFWindow.h" -#include +#include #include #include "util/Dictionary.h" From 9303759e4727a88da50b2cbe655ed2d9d321e047 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Sun, 20 Jul 2025 11:17:52 +0200 Subject: [PATCH 11/47] Fix SDL text input --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 36 +++++++++++++++++++++------ BeefySysLib/platform/sdl/SdlBFApp.h | 2 +- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index c23d81436..4ac31727a 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,8 @@ bool (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); char* (SDLCALL* bf_SDL_GetClipboardText)(void); bool (SDLCALL* bf_SDL_SetClipboardText)(const char* text); +bool (SDLCALL* bf_SDL_StartTextInput)(SDL_Window* window); +bool (SDLCALL* bf_SDL_StopTextInput)(SDL_Window* window); bool (SDLCALL* bf_SDL_PollEvent)(SDL_Event* event); const char* (SDLCALL* bf_SDL_GetError)(void); @@ -125,6 +128,8 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y mSDLWindow = bf_SDL_CreateWindowWithProperties(props); + bf_SDL_StartTextInput(mSDLWindow); + #ifndef BF_PLATFORM_OPENGL_ES2 bf_SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); bf_SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); @@ -174,16 +179,22 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y SdlBFWindow::~SdlBFWindow() { if (mSDLWindow != NULL) - TryClose(); + Destroy(); } -bool SdlBFWindow::TryClose() +void SdlBFWindow::Destroy() { SdlBFApp* app = (SdlBFApp*)gBFApp; app->mSdlWindowMap.Remove(bf_SDL_GetWindowID(mSDLWindow)); + bf_SDL_StopTextInput(mSDLWindow); bf_SDL_DestroyWindow(mSDLWindow); mSDLWindow = NULL; +} + +bool SdlBFWindow::TryClose() +{ + Destroy(); return true; } @@ -317,6 +328,8 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_GetClipboardText); BF_GET_SDLPROC(SDL_SetClipboardText); + BF_GET_SDLPROC(SDL_StartTextInput); + BF_GET_SDLPROC(SDL_StopTextInput); BF_GET_SDLPROC(SDL_PollEvent); BF_GET_SDLPROC(SDL_GetError); @@ -375,9 +388,14 @@ void SdlBFApp::Run() switch (sdlEvent.type) { case SDL_EVENT_QUIT: - //gBFApp->RemoveWindow(sdlEvent.window); Shutdown(); break; + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + gBFApp->RemoveWindow(sdlBFWindow); + } + break; case SDL_EVENT_MOUSE_BUTTON_UP: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); @@ -410,8 +428,8 @@ void SdlBFApp::Run() SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); if (sdlBFWindow != NULL) { - sdlBFWindow->mKeyDownFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.key), sdlEvent.key.repeat); - switch (sdlEvent.key.key) // These keys are not handled by SDL_TEXTINPUT + sdlBFWindow->mKeyDownFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.scancode), sdlEvent.key.repeat); + switch (sdlEvent.key.scancode) // These keys are not handled by SDL_TEXTINPUT { case SDL_SCANCODE_RETURN: sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\n'); @@ -429,10 +447,12 @@ void SdlBFApp::Run() break; case SDL_EVENT_TEXT_INPUT: { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.text.windowID); if (sdlBFWindow != NULL) { - sdlBFWindow->mKeyCharFunc(sdlBFWindow, *(wchar_t*)sdlEvent.text.text); + wchar_t wchar; + mbstowcs(&wchar, sdlEvent.text.text, 1); + sdlBFWindow->mKeyCharFunc(sdlBFWindow, wchar); } } break; @@ -440,7 +460,7 @@ void SdlBFApp::Run() { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); if (sdlBFWindow != NULL) - sdlBFWindow->mKeyUpFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.key)); + sdlBFWindow->mKeyUpFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.scancode)); } break; case SDL_EVENT_WINDOW_MOVED: diff --git a/BeefySysLib/platform/sdl/SdlBFApp.h b/BeefySysLib/platform/sdl/SdlBFApp.h index 471a6e6b6..00112f006 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.h +++ b/BeefySysLib/platform/sdl/SdlBFApp.h @@ -24,7 +24,7 @@ class SdlBFWindow : public BFWindow ~SdlBFWindow(); virtual void* GetUnderlying() {return mSDLWindow; }; - virtual void Destroy() { } + virtual void Destroy() override; virtual void SetTitle(const char* title) override {} virtual void SetMinimumSize(int minWidth, int minHeight, bool clientSized) override {} From 97d8d8a0af2475520fb7e3e13d6210d48f32a41b Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Sun, 20 Jul 2025 14:39:47 +0200 Subject: [PATCH 12/47] Fix SDL window closing --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 4ac31727a..c3ef2b77e 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -29,6 +29,7 @@ USING_NS_BF; bool (SDLCALL* bf_SDL_Init)(SDL_InitFlags flags); void (SDLCALL* bf_SDL_Quit)(void); void (SDLCALL* bf_SDL_free)(void* mem); +void (SDLCALL* bf_SDL_memset)(void* dest, int c, size_t len); SDL_PropertiesID (SDLCALL* bf_SDL_CreateProperties)(void); bool (SDLCALL* bf_SDL_SetNumberProperty)(SDL_PropertiesID props, const char* name, int64_t value); @@ -49,6 +50,7 @@ bool (SDLCALL* bf_SDL_StartTextInput)(SDL_Window* window); bool (SDLCALL* bf_SDL_StopTextInput)(SDL_Window* window); bool (SDLCALL* bf_SDL_PollEvent)(SDL_Event* event); +bool (SDLCALL* bf_SDL_PushEvent)(SDL_Event* event); const char* (SDLCALL* bf_SDL_GetError)(void); SDL_DisplayID* (SDLCALL* bf_SDL_GetDisplays)(int* count); @@ -172,6 +174,7 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y mRenderWindow->mWindow = this; gBFApp->mRenderDevice->AddRenderWindow(mRenderWindow); + mParent = parent; if (parent != NULL) parent->mChildren.push_back(this); } @@ -194,8 +197,12 @@ void SdlBFWindow::Destroy() bool SdlBFWindow::TryClose() { - Destroy(); - return true; + SDL_Event closeEvent; + bf_SDL_memset(&closeEvent, 0, sizeof(SDL_Event)); + closeEvent.type = SDL_EVENT_WINDOW_CLOSE_REQUESTED; + closeEvent.window.windowID = bf_SDL_GetWindowID(this->mSDLWindow); + + return bf_SDL_PushEvent(&closeEvent); } static int SDLConvertScanCode(int scanCode) @@ -312,6 +319,7 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_Init); BF_GET_SDLPROC(SDL_Quit); BF_GET_SDLPROC(SDL_free); + BF_GET_SDLPROC(SDL_memset); BF_GET_SDLPROC(SDL_CreateProperties); BF_GET_SDLPROC(SDL_SetNumberProperty); @@ -332,6 +340,7 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_StopTextInput); BF_GET_SDLPROC(SDL_PollEvent); + BF_GET_SDLPROC(SDL_PushEvent); BF_GET_SDLPROC(SDL_GetError); BF_GET_SDLPROC(SDL_GetDisplays); From d10b95219e13ea800ffe72887ca715ab70865b0e Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Thu, 24 Jul 2025 21:45:09 +0200 Subject: [PATCH 13/47] Handle focus in SDLApp --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index c3ef2b77e..eab8ad95c 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -113,6 +113,8 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN, (windowFlags & BFWINDOW_TOOLTIP) > 0); bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN, (windowFlags & BFWINDOW_DEST_ALPHA) > 0); bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN, (windowFlags & BFWINDOW_FAKEFOCUS) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN, (windowFlags & BFWINDOW_MODAL) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN, (windowFlags & BFWINDOW_TOPMOST) > 0); if (parent != NULL) bf_SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, ((SdlBFWindow*)parent)->mSDLWindow); @@ -197,6 +199,8 @@ void SdlBFWindow::Destroy() bool SdlBFWindow::TryClose() { + mLostFocusFunc(this); + SDL_Event closeEvent; bf_SDL_memset(&closeEvent, 0, sizeof(SDL_Event)); closeEvent.type = SDL_EVENT_WINDOW_CLOSE_REQUESTED; @@ -405,6 +409,20 @@ void SdlBFApp::Run() gBFApp->RemoveWindow(sdlBFWindow); } break; + case SDL_EVENT_WINDOW_FOCUS_GAINED: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if(sdlBFWindow != NULL) + sdlBFWindow->mGotFocusFunc(sdlBFWindow); + } + break; + case SDL_EVENT_WINDOW_FOCUS_LOST: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if(sdlBFWindow != NULL) + sdlBFWindow->mLostFocusFunc(sdlBFWindow); + } + break; case SDL_EVENT_MOUSE_BUTTON_UP: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); From 9e9df3f1d1fc463d9bd5eda41e6cd4a945b896b0 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Fri, 25 Jul 2025 15:53:45 +0200 Subject: [PATCH 14/47] Fix SDLApp autocomplete --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 21 ++++++++++++++++++--- BeefySysLib/platform/sdl/SdlBFApp.h | 1 + IDE/src/ui/AutoComplete.bf | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index eab8ad95c..ca8df3648 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -132,6 +132,8 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y mSDLWindow = bf_SDL_CreateWindowWithProperties(props); +// printf("Created %i : %s\n", bf_SDL_GetWindowID(mSDLWindow), title.c_str()); + bf_SDL_StartTextInput(mSDLWindow); #ifndef BF_PLATFORM_OPENGL_ES2 @@ -172,6 +174,7 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y #endif mIsMouseInside = false; + mHasPositionInit = false; mRenderWindow = new GLRenderWindow((GLRenderDevice*)gBFApp->mRenderDevice, mSDLWindow); mRenderWindow->mWindow = this; gBFApp->mRenderDevice->AddRenderWindow(mRenderWindow); @@ -189,6 +192,8 @@ SdlBFWindow::~SdlBFWindow() void SdlBFWindow::Destroy() { +// printf("Destroy %i\n", bf_SDL_GetWindowID(this->mSDLWindow)); + SdlBFApp* app = (SdlBFApp*)gBFApp; app->mSdlWindowMap.Remove(bf_SDL_GetWindowID(mSDLWindow)); @@ -199,6 +204,8 @@ void SdlBFWindow::Destroy() bool SdlBFWindow::TryClose() { +// printf("TryClose %i\n", bf_SDL_GetWindowID(this->mSDLWindow)); + mLostFocusFunc(this); SDL_Event closeEvent; @@ -497,9 +504,17 @@ void SdlBFApp::Run() SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); if (sdlBFWindow != NULL) { - sdlBFWindow->mMovedFunc(sdlBFWindow); - if (sdlEvent.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) { - sdlBFWindow->mRenderWindow->Resized(); + if (sdlBFWindow->mHasPositionInit) + { + sdlBFWindow->mMovedFunc(sdlBFWindow); + if (sdlEvent.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) + { + sdlBFWindow->mRenderWindow->Resized(); + } + } + else + { + sdlBFWindow->mHasPositionInit = true; } } } diff --git a/BeefySysLib/platform/sdl/SdlBFApp.h b/BeefySysLib/platform/sdl/SdlBFApp.h index 00112f006..70b50d67a 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.h +++ b/BeefySysLib/platform/sdl/SdlBFApp.h @@ -18,6 +18,7 @@ class SdlBFWindow : public BFWindow SDL_Window* mSDLWindow; bool mIsMouseInside; int mModalCount; + bool mHasPositionInit; public: SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags); diff --git a/IDE/src/ui/AutoComplete.bf b/IDE/src/ui/AutoComplete.bf index 2dbecef17..2c672f185 100644 --- a/IDE/src/ui/AutoComplete.bf +++ b/IDE/src/ui/AutoComplete.bf @@ -1464,7 +1464,7 @@ namespace IDE.ui if (widgetWindow == null) { - BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized | BFWindow.Flags.PopupPosition | BFWindow.Flags.NoActivate | BFWindow.Flags.NoMouseActivate | BFWindow.Flags.DestAlpha; + BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized | BFWindow.Flags.PopupPosition | BFWindow.Flags.NoActivate | BFWindow.Flags.NoMouseActivate | BFWindow.Flags.DestAlpha | BFWindow.Flags.Tooltip; widgetWindow = new WidgetWindow(mTargetEditWidget.mWidgetWindow, "Autocomplete", (int32)screenX, (int32)screenY, From 627dcc4819cd0be1429a8f1fd003c487031d3ad3 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Wed, 13 Aug 2025 10:58:19 +0200 Subject: [PATCH 15/47] Move focus to parent on close --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index ca8df3648..c73528652 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -207,6 +207,10 @@ bool SdlBFWindow::TryClose() // printf("TryClose %i\n", bf_SDL_GetWindowID(this->mSDLWindow)); mLostFocusFunc(this); + if(this->mParent != NULL) + { + mGotFocusFunc(this->mParent); + } SDL_Event closeEvent; bf_SDL_memset(&closeEvent, 0, sizeof(SDL_Event)); From de4ec1bfe8bf10ce307db72cdc799361561d1dcc Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Sat, 11 Oct 2025 22:08:16 +0200 Subject: [PATCH 16/47] Fix Zip.bf build on Linux --- BeefLibs/MiniZ/src/Zip.bf | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/BeefLibs/MiniZ/src/Zip.bf b/BeefLibs/MiniZ/src/Zip.bf index 6a9ac3915..b92c5fd12 100644 --- a/BeefLibs/MiniZ/src/Zip.bf +++ b/BeefLibs/MiniZ/src/Zip.bf @@ -2908,22 +2908,36 @@ namespace MiniZ //static extern int64 ftell64(FILE* stream); static int64 ftell64(FILE* stream) { +#if BF_PLATFORM_WINDOWS return _ftelli64(stream); +#else + return ftello(stream); +#endif } [CallingConvention(.Stdcall), CLink] static extern int64 _ftelli64(FILE* stream); + [CallingConvention(.Stdcall), CLink] + static extern int64 ftello(FILE* stream); + //[CallingConvention(.Stdcall), CLink] //static extern int64 fseek64(FILE* stream, int64 offset, int32 origin); static int32 fseek64(FILE* stream, int64 offset, int32 origin) { +#if BF_PLATFORM_WINDOWS return _fseeki64(stream, offset, origin); +#else + return fseeko(stream, offset, origin); +#endif } [CallingConvention(.Stdcall), CLink] static extern int32 _fseeki64(FILE* stream, int64 offset, int32 origin); + [CallingConvention(.Stdcall), CLink] + static extern int32 fseeko(FILE* stream, int64 offset, int32 origin); + [CallingConvention(.Stdcall), CLink] static extern int fread(void* buf, int elementSize, int elementCount, FILE* stream); From 2db901e0cc697759c3645f51ab590c5a7c32e6b0 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:13:19 +0200 Subject: [PATCH 17/47] Reimplement SDL app clipboard data --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 72 +++++++++++++++++++++++++-- BeefySysLib/platform/sdl/SdlBFApp.h | 4 +- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index c73528652..4f066d4bc 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -28,8 +28,10 @@ USING_NS_BF; bool (SDLCALL* bf_SDL_Init)(SDL_InitFlags flags); void (SDLCALL* bf_SDL_Quit)(void); +void* (SDLCALL* bf_SDL_malloc)(size_t size); void (SDLCALL* bf_SDL_free)(void* mem); void (SDLCALL* bf_SDL_memset)(void* dest, int c, size_t len); +void* (SDLCALL* bf_SDL_memcpy)(void *dst, const void *src, size_t len); SDL_PropertiesID (SDLCALL* bf_SDL_CreateProperties)(void); bool (SDLCALL* bf_SDL_SetNumberProperty)(SDL_PropertiesID props, const char* name, int64_t value); @@ -46,11 +48,14 @@ bool (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); char* (SDLCALL* bf_SDL_GetClipboardText)(void); bool (SDLCALL* bf_SDL_SetClipboardText)(const char* text); +void* (SDLCALL* bf_SDL_GetClipboardData)(const char *mime_type, size_t *size); +bool (SDLCALL* bf_SDL_SetClipboardData)(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types); bool (SDLCALL* bf_SDL_StartTextInput)(SDL_Window* window); bool (SDLCALL* bf_SDL_StopTextInput)(SDL_Window* window); bool (SDLCALL* bf_SDL_PollEvent)(SDL_Event* event); bool (SDLCALL* bf_SDL_PushEvent)(SDL_Event* event); +bool (SDLCALL* bf_SDL_SetError)(const char *fmt, ...); const char* (SDLCALL* bf_SDL_GetError)(void); SDL_DisplayID* (SDLCALL* bf_SDL_GetDisplays)(int* count); @@ -65,6 +70,13 @@ bool (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); static int bfMouseBtnOf[4] = {NULL, 0, 2, 1}; // Translate SDL mouse buttons to what Beef expects. +static const char* mimeTypes[] = +{ + "text/plain;charset=utf-8", + "text/vnd.beeflang.bf-text", + "text/vnd.beeflang.file-list" +}; + static HMODULE gSDLModule; static HMODULE GetSDLModule(const StringImpl& installDir) @@ -309,6 +321,7 @@ SdlBFApp::SdlBFApp() { mRunning = false; mRenderDevice = NULL; + mSdlClipboardData = new SdlClipboardData(); Beefy::String exePath; BfpGetStrHelper(exePath, [](char* outStr, int* inOutStrSize, BfpResult* result) @@ -333,8 +346,10 @@ SdlBFApp::SdlBFApp() { BF_GET_SDLPROC(SDL_Init); BF_GET_SDLPROC(SDL_Quit); + BF_GET_SDLPROC(SDL_malloc); BF_GET_SDLPROC(SDL_free); BF_GET_SDLPROC(SDL_memset); + BF_GET_SDLPROC(SDL_memcpy); BF_GET_SDLPROC(SDL_CreateProperties); BF_GET_SDLPROC(SDL_SetNumberProperty); @@ -351,11 +366,14 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_GetClipboardText); BF_GET_SDLPROC(SDL_SetClipboardText); + BF_GET_SDLPROC(SDL_GetClipboardData); + BF_GET_SDLPROC(SDL_SetClipboardData); BF_GET_SDLPROC(SDL_StartTextInput); BF_GET_SDLPROC(SDL_StopTextInput); BF_GET_SDLPROC(SDL_PollEvent); BF_GET_SDLPROC(SDL_PushEvent); + BF_GET_SDLPROC(SDL_SetError); BF_GET_SDLPROC(SDL_GetError); BF_GET_SDLPROC(SDL_GetDisplays); @@ -376,6 +394,7 @@ SdlBFApp::SdlBFApp() SdlBFApp::~SdlBFApp() { + delete mSdlClipboardData; } SdlBFWindow* SdlBFApp::GetSdlWindowFromId(uint32 id) @@ -597,14 +616,29 @@ void SdlBFWindow::SetAlpha(float alpha, uint32 destAlphaSrcMask, bool isMouseVis // Not supported } -uint32 SdlBFApp::GetClipboardFormat(const StringImpl& format) +const char* SdlBFApp::GetClipboardFormat(const StringImpl& format) { - return /*CF_TEXT*/1; + if (format == "text" || format == "atext") + { + return "text/plain;charset=utf-8"; + } + if (format == "bf_text") + { + return "text/vnd.beeflang.bf-text"; + } + if (format == "code/file-list") + { + return "text/vnd.beeflang.file-list"; + } + return format.c_str(); } void* SdlBFApp::GetClipboardData(const StringImpl& format, int* size) { - return bf_SDL_GetClipboardText(); + size_t outSize; + void* data = bf_SDL_GetClipboardData(GetClipboardFormat(format), &outSize); + *size = outSize; + return data; } void SdlBFApp::ReleaseClipboardData(void* ptr) @@ -612,9 +646,39 @@ void SdlBFApp::ReleaseClipboardData(void* ptr) bf_SDL_free(ptr); } +const void* SDLClipboardCallback(void* userData, const char* mimeType, size_t* outSize) +{ + SdlClipboardData* clipboard = *(SdlClipboardData**)userData; + + void* data; + if (clipboard->TryGetValue(StringImpl::MakeRef(mimeType), &data)) + { + *outSize = strlen((const char*) data); + } + return data; +} + void SdlBFApp::SetClipboardData(const StringImpl& format, const void* ptr, int size, bool resetClipboard) { - bf_SDL_SetClipboardText((const char*)ptr); + void* buffer = bf_SDL_malloc(size); + if (buffer == NULL) + { + bf_SDL_SetError("Out of memory for clipboard"); + } + else + { + StringImpl mime = StringImpl::MakeRef(GetClipboardFormat(format)); + + void* previous; + if (mSdlClipboardData->TryGetValue(mime, &previous)) + { + bf_SDL_free(previous); + } + bf_SDL_memcpy(buffer, ptr, size); + (*mSdlClipboardData)[mime] = buffer; + + bf_SDL_SetClipboardData(SDLClipboardCallback, NULL, &mSdlClipboardData, mimeTypes, 3); + } } BFMenu* SdlBFWindow::AddMenuItem(BFMenu* parent, int insertIdx, const char* text, const char* hotKey, BFSysBitmap* bitmap, bool enabled, int checkState, bool radioCheck) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.h b/BeefySysLib/platform/sdl/SdlBFApp.h index 70b50d67a..185d80fda 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.h +++ b/BeefySysLib/platform/sdl/SdlBFApp.h @@ -50,19 +50,21 @@ class SdlBFWindow : public BFWindow }; typedef Dictionary SdlWindowMap; +typedef Dictionary SdlClipboardData; class SdlBFApp : public BFApp { public: bool mInMsgProc; SdlWindowMap mSdlWindowMap; + SdlClipboardData* mSdlClipboardData; SDL_GLContext mGLContext; protected: virtual void Draw() override; virtual void PhysSetCursor() override; - uint32 GetClipboardFormat(const StringImpl& format); + const char* GetClipboardFormat(const StringImpl& format); SdlBFWindow* GetSdlWindowFromId(uint32 id); public: From b51366fc7512c612229d639085e4d2d486ef4ab1 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Sun, 2 Nov 2025 12:16:16 +0100 Subject: [PATCH 18/47] Implement MenuBar widget --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 124 +++++++- BeefySysLib/platform/sdl/SdlBFApp.h | 1 + IDE/src/ui/MainFrame.bf | 17 +- IDE/src/ui/MenuBar.bf | 439 ++++++++++++++++++++++++++ 4 files changed, 565 insertions(+), 16 deletions(-) create mode 100644 IDE/src/ui/MenuBar.bf diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 4f066d4bc..17702a929 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -6,6 +6,7 @@ #include "platform/PlatformInterface.h" #include #include +#include #include #include #include @@ -16,8 +17,6 @@ #include #include #include -#include -#include USING_NS_BF; @@ -58,8 +57,11 @@ bool (SDLCALL* bf_SDL_PushEvent)(SDL_Event* event); bool (SDLCALL* bf_SDL_SetError)(const char *fmt, ...); const char* (SDLCALL* bf_SDL_GetError)(void); +SDL_DisplayID (SDLCALL* bf_SDL_GetPrimaryDisplay)(void); SDL_DisplayID* (SDLCALL* bf_SDL_GetDisplays)(int* count); bool (SDLCALL* bf_SDL_GetDisplayBounds)(SDL_DisplayID displayID, SDL_Rect* rect); +SDL_DisplayMode* (SDLCALL* bf_SDL_GetDesktopDisplayMode)(SDL_DisplayID displayID); +bool (SDLCALL* bf_SDL_HasRectIntersection)(const SDL_Rect* A, const SDL_Rect* B); SDL_GLContext (SDLCALL* bf_SDL_GL_CreateContext)(SDL_Window* window); bool (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); @@ -67,6 +69,14 @@ bool (SDLCALL* bf_SDL_GL_SetAttribute)(SDL_GLAttr attr, int value); void* (SDLCALL* bf_SDL_GL_GetProcAddress)(const char* proc); bool (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); +struct AdjustedMonRect +{ + int mMonCount; + int mX; + int mY; + int mWidth; + int mHeight; +}; static int bfMouseBtnOf[4] = {NULL, 0, 2, 1}; // Translate SDL mouse buttons to what Beef expects. @@ -125,6 +135,7 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN, (windowFlags & BFWINDOW_TOOLTIP) > 0); bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN, (windowFlags & BFWINDOW_DEST_ALPHA) > 0); bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN, (windowFlags & BFWINDOW_FAKEFOCUS) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN, (windowFlags & BFWINDOW_FAKEFOCUS) == 0); bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN, (windowFlags & BFWINDOW_MODAL) > 0); bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN, (windowFlags & BFWINDOW_TOPMOST) > 0); @@ -224,12 +235,8 @@ bool SdlBFWindow::TryClose() mGotFocusFunc(this->mParent); } - SDL_Event closeEvent; - bf_SDL_memset(&closeEvent, 0, sizeof(SDL_Event)); - closeEvent.type = SDL_EVENT_WINDOW_CLOSE_REQUESTED; - closeEvent.window.windowID = bf_SDL_GetWindowID(this->mSDLWindow); - - return bf_SDL_PushEvent(&closeEvent); + gBFApp->RemoveWindow(this); + return mSDLWindow == NULL; } static int SDLConvertScanCode(int scanCode) @@ -376,8 +383,11 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_SetError); BF_GET_SDLPROC(SDL_GetError); + BF_GET_SDLPROC(SDL_GetPrimaryDisplay); BF_GET_SDLPROC(SDL_GetDisplays); BF_GET_SDLPROC(SDL_GetDisplayBounds); + BF_GET_SDLPROC(SDL_GetDesktopDisplayMode); + BF_GET_SDLPROC(SDL_HasRectIntersection); BF_GET_SDLPROC(SDL_GL_CreateContext); BF_GET_SDLPROC(SDL_GL_MakeCurrent); @@ -436,7 +446,8 @@ void SdlBFApp::Run() case SDL_EVENT_WINDOW_CLOSE_REQUESTED: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); - gBFApp->RemoveWindow(sdlBFWindow); + if(sdlBFWindow != NULL) + gBFApp->RemoveWindow(sdlBFWindow); } break; case SDL_EVENT_WINDOW_FOCUS_GAINED: @@ -716,14 +727,97 @@ DrawLayer* SdlBFApp::CreateDrawLayer(BFWindow* window) void SdlBFApp::GetDesktopResolution(int& width, int& height) { - width = 1024; - height = 768; + SDL_DisplayID display = bf_SDL_GetPrimaryDisplay(); + if (display != 0) + { + SDL_DisplayMode* displayMode = bf_SDL_GetDesktopDisplayMode(display); + if (displayMode != NULL) { + width = displayMode->w; + height = displayMode->h; + } + } +} + +static bool InflateRectToMonitor(SDL_DisplayID monitor, AdjustedMonRect* inflatedRect) +{ + SDL_Rect bounds; + if(!bf_SDL_GetDisplayBounds(monitor, &bounds)) + return false; + + inflatedRect->mMonCount++; + if (inflatedRect->mMonCount == 1) + { + inflatedRect->mX = bounds.x; + inflatedRect->mY = bounds.y; + inflatedRect->mWidth = bounds.w; + inflatedRect->mHeight = bounds.h; + } + else + { + int minLeft = BF_MIN(inflatedRect->mX, bounds.x); + int minTop = BF_MIN(inflatedRect->mY, bounds.y); + int maxRight = BF_MAX(inflatedRect->mX + inflatedRect->mWidth, bounds.x + bounds.w); + int maxBottom = BF_MAX(inflatedRect->mY + inflatedRect->mHeight, bounds.y + bounds.h); + + inflatedRect->mX = minLeft; + inflatedRect->mY = minTop; + inflatedRect->mWidth = maxRight - minLeft; + inflatedRect->mHeight = maxBottom - minTop; + } + + return true; } void SdlBFApp::GetWorkspaceRect(int& x, int& y, int& width, int& height) { - x = 0; - y = 0; - width = 1024; - height = 768; + AdjustedMonRect inflateRect = { 0 }; + + int displayCount; + SDL_DisplayID* displays = bf_SDL_GetDisplays(&displayCount); + if (!displays) + return; + + for (int i = 0; i < displayCount; i++) + { + InflateRectToMonitor(displays[i], &inflateRect); + } + bf_SDL_free(displays); + + x = inflateRect.mX; + y = inflateRect.mY; + width = inflateRect.mWidth; + height = inflateRect.mHeight; } + +void SdlBFApp::GetWorkspaceRectFrom(int fromX, int fromY, int fromWidth, int fromHeight, int& outX, int& outY, int& outWidth, int& outHeight) +{ + SDL_Rect bounds; + SDL_Rect clip = {fromX, fromY, fromWidth == 0 ? 1 : fromWidth, fromHeight == 0 ? 1 : fromHeight}; + AdjustedMonRect inflateRect = { 0 }; + + int displayCount; + SDL_DisplayID* displays = bf_SDL_GetDisplays(&displayCount); + if (displays == NULL) + return; + + for (int i = 0; i < displayCount; i++) + { + bf_SDL_GetDisplayBounds(displays[i], &bounds); + if (bf_SDL_HasRectIntersection(&clip, &bounds)) + { + InflateRectToMonitor(displays[i], &inflateRect); + } + } + bf_SDL_free(displays); + + if (inflateRect.mMonCount == 0) + { + GetWorkspaceRect(outX, outY, outWidth, outHeight); + return; + } + + outX = inflateRect.mX; + outY = inflateRect.mY; + outWidth = inflateRect.mWidth; + outHeight = inflateRect.mHeight; +} \ No newline at end of file diff --git a/BeefySysLib/platform/sdl/SdlBFApp.h b/BeefySysLib/platform/sdl/SdlBFApp.h index 185d80fda..47fdb697f 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.h +++ b/BeefySysLib/platform/sdl/SdlBFApp.h @@ -84,6 +84,7 @@ class SdlBFApp : public BFApp virtual BFSysBitmap* LoadSysBitmap(const wchar_t* fileName) override; virtual void GetDesktopResolution(int& width, int& height) override; virtual void GetWorkspaceRect(int& x, int& y, int& width, int& height) override; + virtual void GetWorkspaceRectFrom(int fromX, int fromY, int fromWidth, int fromHeight, int& outX, int& outY, int& outWidth, int& outHeight) override; }; NS_BF_END; \ No newline at end of file diff --git a/IDE/src/ui/MainFrame.bf b/IDE/src/ui/MainFrame.bf index 966e9872a..3df50c1e5 100644 --- a/IDE/src/ui/MainFrame.bf +++ b/IDE/src/ui/MainFrame.bf @@ -15,12 +15,20 @@ namespace IDE.ui public StatusBar mStatusBar; public DarkDockingFrame mDockingFrame; +#if BF_PLATFORM_LINUX + public MenuBar mMenuBar; +#endif + public this() { mStatusBar = new StatusBar(); AddWidget(mStatusBar); mDockingFrame = (DarkDockingFrame)ThemeFactory.mDefault.CreateDockingFrame(); AddWidget(mDockingFrame); +#if BF_PLATFORM_LINUX + mMenuBar = new MenuBar(); + AddWidget(mMenuBar); +#endif } public void Reset() @@ -40,8 +48,15 @@ namespace IDE.ui public override void Resize(float x, float y, float width, float height) { base.Resize(x, y, width, height); - int32 statusHeight = GS!(20); + + int32 statusHeight = GS!(20); +#if BF_PLATFORM_LINUX + int32 menuHeight = GS!(20); + mDockingFrame.Resize(0, menuHeight, width, height - statusHeight - menuHeight); + mMenuBar.Resize(0, 0, width, menuHeight); +#else mDockingFrame.Resize(0, 0, width, height - statusHeight); +#endif mStatusBar.Resize(0, mHeight - statusHeight, width, statusHeight); } } diff --git a/IDE/src/ui/MenuBar.bf b/IDE/src/ui/MenuBar.bf new file mode 100644 index 000000000..38a5b655f --- /dev/null +++ b/IDE/src/ui/MenuBar.bf @@ -0,0 +1,439 @@ +using Beefy; +using Beefy.gfx; +using Beefy.widgets; +using Beefy.theme.dark; +using Beefy.geom; +using IDE; +using System; +using System.Diagnostics; + +namespace IDE.ui; + +public class MenuBar : Widget +{ + public class Button : ButtonWidget + { + private String mLabel ~ delete _; + + public StringView Label + { + get + { + return mLabel; + } + + set + { + String.NewOrSet!(mLabel, value); + } + } + + public override void Draw(Graphics g) + { + base.Draw(g); + + if (mMouseOver) + { + using (g.PushColor(0x40FFFFFF)) + { + g.FillRect(0, 0, mWidth, mHeight); + } + } + + g.SetFont(DarkTheme.sDarkTheme.mSmallFont); + if (mLabel != null) + { + using (g.PushColor(mDisabled ? 0x80FFFFFF : Color.White)) + { + using (g.PushColor(DarkTheme.COLOR_TEXT)) + DarkTheme.DrawUnderlined(g, mLabel, GS!(2), (mHeight - GS!(20)) / 2, .Centered, mWidth - GS!(4), .Truncate); + } + } + } + } + + public Button mFileButton; + public Button mEditButton; + public Button mViewButton; + public Button mBuildButton; + public Button mDebugButton; + public Button mTestButton; + public Button mWindowButton; + public Button mHelpButton; + + public this() + { + mFileButton = new Button(); + mFileButton.Label = "File"; + mFileButton.mOnMouseClick.Add(new (evt) => ShowFileMenu()); + AddWidget(mFileButton); + + mEditButton = new Button(); + mEditButton.Label = "Edit"; + mEditButton.mOnMouseClick.Add(new (evt) => ShowEditMenu()); + AddWidget(mEditButton); + + mViewButton = new Button(); + mViewButton.Label = "View"; + mViewButton.mOnMouseClick.Add(new (evt) => ShowViewMenu()); + AddWidget(mViewButton); + + mBuildButton = new Button(); + mBuildButton.Label = "Build"; + mBuildButton.mOnMouseClick.Add(new (evt) => ShowBuildMenu()); + AddWidget(mBuildButton); + + mDebugButton = new Button(); + mDebugButton.Label = "Debug"; + mDebugButton.mOnMouseClick.Add(new (evt) => ShowDebugMenu()); + AddWidget(mDebugButton); + + mTestButton = new Button(); + mTestButton.Label = "Test"; + mTestButton.mOnMouseClick.Add(new (evt) => ShowTestMenu()); + AddWidget(mTestButton); + + mWindowButton = new Button(); + mWindowButton.Label = "Window"; + mWindowButton.mOnMouseClick.Add(new (evt) => ShowWindowMenu()); + AddWidget(mWindowButton); + + mHelpButton = new Button(); + mHelpButton.Label = "Help"; + mHelpButton.mOnMouseClick.Add(new (evt) => ShowHelpMenu()); + AddWidget(mHelpButton); + } + + Menu AddMenuItem(Menu menu, StringView label, StringView command, bool disabled = false, bool isChecked = false) + { + let labelStr = scope String(label); + let hasCommand = gApp.mCommands.mCommandMap.TryGetAlt(command, var matchKey, var ideCommand); + + if(hasCommand) + { + labelStr.Append("|"); + ideCommand.ToString(labelStr); + } + + return AddMenuItem(menu, labelStr, hasCommand ? new (evt) => ideCommand.mAction() : null, disabled, isChecked); + } + + Menu AddMenuItem(Menu menu, StringView label, delegate void(Menu menu) action, bool disabled = false, bool isChecked = false) + { + Menu item = menu.AddItem(label); + item.SetDisabled(disabled); + + if (isChecked) + { + item.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check); + } + + if (action != null) + { + item.mOnMenuItemSelected.Add(action); + } + + return item; + } + + void ShowFileMenu() + { + let subMenu = new Menu(); + let newMenu = subMenu.AddItem("New"); + AddMenuItem(newMenu, "New Workspace", "New Workspace"); + AddMenuItem(newMenu, "New Project", "New Project"); + AddMenuItem(newMenu, "New Debug Session", "New Debug Session"); + AddMenuItem(newMenu, "New File", "New File"); + + let openMenu = subMenu.AddItem("Open"); + AddMenuItem(openMenu, "Open Workspace...", "Open Workspace"); + AddMenuItem(openMenu, "Open Project...", "Open Project"); + AddMenuItem(openMenu, "Open Debug Session...", "Open Debug Session"); + AddMenuItem(openMenu, "Open File...", "Open File"); + AddMenuItem(openMenu, "Open File in Workspace", "Open File in Workspace"); + AddMenuItem(openMenu, "Open Corresponding (cpp/h)", "Open Corresponding"); + AddMenuItem(openMenu, "Open Crash Dump...", "Open Crash Dump"); + + let recentMenu = subMenu.AddItem("Open Recent"); + recentMenu.SetDisabled(true); //TODO: recent + + AddMenuItem(subMenu, "Save File", "Save File", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Save As...", "Save As", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Save All", "Save All"); + let prefMenu = subMenu.AddItem("Preferences"); + AddMenuItem(prefMenu, "Settings", "Settings"); + AddMenuItem(prefMenu, "Reload Settings", "Reload Settings"); + AddMenuItem(prefMenu, "Reset UI", "Reset UI"); + AddMenuItem(prefMenu, "Safe Mode", "Safe Mode Toggle", false, gApp.mSafeMode); + AddMenuItem(subMenu, "Close Workspace", "Close Workspace", !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Exit", "Exit"); + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(subMenu); + menuWidget.Init(mFileButton, 0, mHeight); + } + + void ShowEditMenu() + { + let subMenu = new Menu(); + AddMenuItem(subMenu, "Quick Find...", "Find in Document", gApp.GetActivePanel() == null); + AddMenuItem(subMenu, "Quick Replace...", "Replace in Document", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Find in Files...", "Find in Files"); + AddMenuItem(subMenu, "Replace in Files...", "Replace in Files"); + AddMenuItem(subMenu, "Find Prev", "Find Prev", gApp.GetActivePanel() == null); + AddMenuItem(subMenu, "Find Next", "Find Next", gApp.GetActivePanel() == null); + AddMenuItem(subMenu, "Show Current", "Show Current"); + + AddMenuItem(subMenu, "Goto Line...", "Goto Line", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Goto Method...", "Goto Method", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Rename Symbol", "Rename Symbol", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Show Fixit", "Show Fixit", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Find All References", "Find All References", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Find Class...", "Find Class"); + subMenu.AddItem(null); + var encodingMenu = subMenu.AddItem("Encoding"); + var lineEndingMenu = encodingMenu.AddItem("Line Ending"); + void AddLineEndingKind(String name, LineEndingKind lineEndingKind) + { + let item = lineEndingMenu.AddItem(name); + let sourceViewPanel = gApp.GetActiveSourceViewPanel(true); + + if (sourceViewPanel != null) + { + if (sourceViewPanel.mEditData.mLineEndingKind == lineEndingKind) + { + item.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check); + } + item.mOnMenuItemSelected.Add(new (evt) => + { + if (sourceViewPanel.mEditData.mLineEndingKind != lineEndingKind) + { + sourceViewPanel.EditWidget.Content.mData.mCurTextVersionId++; + sourceViewPanel.mEditData.mLineEndingKind = lineEndingKind; + } + }); + } + } + AddLineEndingKind("Windows", .CrLf); + AddLineEndingKind("Unix", .Lf); + AddLineEndingKind("Mac OS 9", .Cr); + + var bookmarkMenu = subMenu.AddItem("Bookmarks"); + AddMenuItem(bookmarkMenu, "Toggle Bookmark", "Bookmark Toggle"); + AddMenuItem(bookmarkMenu, "Next Bookmark", "Bookmark Next"); + AddMenuItem(bookmarkMenu, "Previous Bookmark", "Bookmark Prev"); + AddMenuItem(bookmarkMenu, "Clear Bookmarks", "Bookmark Clear"); + + var comptimeMenu = subMenu.AddItem("Comptime"); + var emitViewCompiler = comptimeMenu.AddItem("Emit View Compiler"); + AddMenuItem(emitViewCompiler, "Resolve", new (evt) => gApp.SetEmbedCompiler(.Resolve), false, gApp.mSettings.mEditorSettings.mEmitCompiler == .Resolve); + AddMenuItem(emitViewCompiler, "Build", new (evt) => gApp.SetEmbedCompiler(.Build), false, gApp.mSettings.mEditorSettings.mEmitCompiler == .Build); + + var advancedEditMenu = subMenu.AddItem("Advanced"); + AddMenuItem(advancedEditMenu, "Duplicate Line", "Duplicate Line"); + AddMenuItem(advancedEditMenu, "Move Line Up", "Move Line Up"); + AddMenuItem(advancedEditMenu, "Move Line Down", "Move Line Down"); + AddMenuItem(advancedEditMenu, "Move Statement Up", "Move Statement Up"); + AddMenuItem(advancedEditMenu, "Move Statement Down", "Move Statement Down"); + advancedEditMenu.AddItem(null); + AddMenuItem(advancedEditMenu, "Make Uppercase", "Make Uppercase"); + AddMenuItem(advancedEditMenu, "Make Lowercase", "Make Lowercase"); + AddMenuItem(advancedEditMenu, "Comment Block", "Comment Block"); + AddMenuItem(advancedEditMenu, "Comment Lines", "Comment Lines"); + AddMenuItem(advancedEditMenu, "Comment Toggle", "Comment Toggle"); + AddMenuItem(advancedEditMenu, "Uncomment Selection", "Uncomment Selection"); + AddMenuItem(advancedEditMenu, "Reformat Document", "Reformat Document"); + AddMenuItem(advancedEditMenu, "View White Space", "View White Space", false, gApp.mViewWhiteSpace.Bool); + + if (gApp.mSettings.mEnableDevMode) + { + subMenu.AddItem(null); + var internalEditMenu = subMenu.AddItem("Internal"); + AddMenuItem(internalEditMenu, "Hilight Cursor References", new (menu) => { gApp.mSettings.mEditorSettings.mHiliteCursorReferences = !gApp.mSettings.mEditorSettings.mHiliteCursorReferences; }, false, gApp.mSettings.mEditorSettings.mHiliteCursorReferences); + AddMenuItem(internalEditMenu, "Delayed Autocomplete", new (menu) => { gApp.mDbgDelayedAutocomplete = !gApp.mDbgDelayedAutocomplete; }, false, gApp.mDbgDelayedAutocomplete); + AddMenuItem(internalEditMenu, "Time Autocomplete", new (menu) => { gApp.mDbgTimeAutocomplete = !gApp.mDbgTimeAutocomplete; }, false, gApp.mDbgTimeAutocomplete); + AddMenuItem(internalEditMenu, "Perf Autocomplete", new (menu) => { gApp.mDbgPerfAutocomplete = !gApp.mDbgPerfAutocomplete; }, false, gApp.mDbgPerfAutocomplete); + AddMenuItem(internalEditMenu, "Dump Undo Buffer", new (menu) => + { + if (var panel = gApp.GetActiveSourceViewPanel()) + { + var str = panel.mEditWidget.mEditWidgetContent.mData.mUndoManager.ToString(.. scope .()); + Debug.WriteLine(str); + } + }, false, gApp.mDbgPerfAutocomplete); + } + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(subMenu); + menuWidget.Init(mEditButton, 0, mHeight); + } + + void ShowViewMenu() + { + let subMenu = new Menu(); + AddMenuItem(subMenu, "AutoComplete", "Show Autocomplete Panel"); + AddMenuItem(subMenu, "Auto Watches", "Show Auto Watches"); + AddMenuItem(subMenu, "Bookmarks", "Show Bookmarks"); + AddMenuItem(subMenu, "Breakpoints", "Show Breakpoints"); + AddMenuItem(subMenu, "Call Stack", "Show Call Stack"); + AddMenuItem(subMenu, "Class View", "Show Class View"); + AddMenuItem(subMenu, "Diagnostics", "Show Diagnostics"); + AddMenuItem(subMenu, "Errors", "Show Errors"); + AddMenuItem(subMenu, "Find Results", "Show Find Results"); + AddMenuItem(subMenu, "Terminal", "Show Terminal"); + AddMenuItem(subMenu, "Console", "Show Console"); + AddMenuItem(subMenu, "Immediate Window", "Show Immediate"); + AddMenuItem(subMenu, "Memory", "Show Memory"); + AddMenuItem(subMenu, "Modules", "Show Modules"); + AddMenuItem(subMenu, "Output", "Show Output"); + AddMenuItem(subMenu, "Profiler", "Show Profiler"); + AddMenuItem(subMenu, "Threads", "Show Threads"); + AddMenuItem(subMenu, "Watches", "Show Watches"); + AddMenuItem(subMenu, "Workspace Explorer", "Show Workspace Explorer"); + subMenu.AddItem(null); + AddMenuItem(subMenu, "Next Document Panel", "Next Document Panel"); + AddMenuItem(subMenu, "Navigate Backwards", "Navigate Backwards"); + AddMenuItem(subMenu, "Navigate Forwards", "Navigate Forwards"); + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(subMenu); + menuWidget.Init(mViewButton, 0, mHeight); + } + + void ShowBuildMenu() + { + let subMenu = new Menu(); + AddMenuItem(subMenu, "Build Workspace", "Build Workspace", !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Debug Comptime", "Debug Comptime", gApp.mDebugger.mIsRunning || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Clean", "Clean", gApp.mDebugger.mIsRunning || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Clean Beef", "Clean Beef", gApp.mDebugger.mIsRunning || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Compile Current File", new (menu) => { gApp.[Friend]CompileCurrentFile(); }); + AddMenuItem(subMenu, "Cancel Build", "Cancel Build", !gApp.IsCompiling); + AddMenuItem(subMenu, "Verbose", new (menu) => + { + if (gApp.mVerbosity != .Diagnostic) + gApp.mVerbosity = .Diagnostic; + else + gApp.mVerbosity = .Normal; + }, false, (gApp.mVerbosity == .Diagnostic)); + + if (gApp.mSettings.mEnableDevMode) + { + var internalBuildMenu = subMenu.AddItem("Internal"); + AddMenuItem(internalBuildMenu, "Autobuild (Debug)", new (menu) => { gApp.mDebugAutoBuild = !gApp.mDebugAutoBuild; }); + AddMenuItem(internalBuildMenu, "Autorun (Debug)", new (menu) => { gApp.mDebugAutoRun = !gApp.mDebugAutoRun; }); + AddMenuItem(internalBuildMenu, "Disable Compiling", new (menu) => { gApp.mDisableBuilding = !gApp.mDisableBuilding; }, false, gApp.mDisableBuilding); + } + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(subMenu); + menuWidget.Init(mBuildButton, 0, mHeight); + } + + void ShowDebugMenu() + { + let subMenu = new Menu(); + AddMenuItem(subMenu, "Start Debugging", "Start Debugging", true); // TODO: Debugging status + AddMenuItem(subMenu, "Start Without Debugging", "Start Without Debugging", gApp.mDebugger.mIsRunning || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Start Without Compiling", "Start Without Compiling", gApp.mDebugger.mIsRunning || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Launch Process...", "Launch Process", gApp.mDebugger.mIsRunning); + AddMenuItem(subMenu, "Attach to Process...", "Attach to Process", gApp.mDebugger.mIsRunning); + AddMenuItem(subMenu, "Stop Debugging", "Stop Debugging", !gApp.mDebugger.mIsRunning && (gApp.mTestManager == null)); + AddMenuItem(subMenu, "Break All", "Break All", !gApp.mDebugger.mIsRunning || gApp.mExecutionPaused); + AddMenuItem(subMenu, "Remove All Breakpoints", "Remove All Breakpoints"); + AddMenuItem(subMenu, "Show Disassembly", "Show Disassembly"); + AddMenuItem(subMenu, "Quick Watch", "Show QuickWatch", !gApp.mDebugger.mIsRunning || !gApp.mExecutionPaused); + AddMenuItem(subMenu, "Profile", "Profile", !gApp.mWorkspace.IsInitialized); + subMenu.AddItem(null); + AddMenuItem(subMenu, "Step Into", "Step Into", (gApp.mDebugger.mIsRunning && !gApp.mExecutionPaused) || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Step Over", "Step Over", (gApp.mDebugger.mIsRunning && !gApp.mExecutionPaused) || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Step Out", "Step Out", !gApp.mDebugger.mIsRunning || !gApp.mExecutionPaused); + subMenu.AddItem(null); + AddMenuItem(subMenu, "Toggle Breakpoint", "Breakpoint Toggle", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Toggle Thread Breakpoint", "Breakpoint Toggle Thread", gApp.GetActiveDocumentPanel() == null); + var newBreakpointMenu = subMenu.AddItem("New Breakpoint"); + AddMenuItem(newBreakpointMenu, "Memory Breakpoint...", "Breakpoint Memory", !gApp.mDebugger.mIsRunning); + AddMenuItem(newBreakpointMenu, "Symbol Breakpoint...", "Breakpoint Symbol"); + //TODO: dev mode + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(subMenu); + menuWidget.Init(mDebugButton, 0, mHeight); + } + + void ShowTestMenu() + { + let testMenu = new Menu(); + var testRunMenu = testMenu.AddItem("Run"); + testRunMenu.SetDisabled(!gApp.mWorkspace.IsInitialized); + AddMenuItem(testRunMenu, "Normal Tests", "Run Normal Tests"); + AddMenuItem(testRunMenu, "All Tests", "Run All Tests"); + + var testDebugMenu = testMenu.AddItem("Debug"); + testDebugMenu.SetDisabled(!gApp.mWorkspace.IsInitialized); + AddMenuItem(testDebugMenu, "Normal Tests", "Debug Normal Tests"); + AddMenuItem(testDebugMenu, "All Tests", "Debug All Tests"); + testDebugMenu.AddItem(null); + AddMenuItem(testDebugMenu, "Break on Failure", new (evt) => { gApp.mTestBreakOnFailure = !gApp.mTestBreakOnFailure; }, false, gApp.mTestBreakOnFailure); + AddMenuItem(testMenu, "Enable Console", "Test Enable Console", false, gApp.mTestEnableConsole); + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(testMenu); + menuWidget.Init(mTestButton, 0, mHeight); + } + + void ShowWindowMenu() + { + let windowMenu = new Menu(); + AddMenuItem(windowMenu, "Close Document", "Close Document", gApp.GetLastActiveDocumentPanel() == null); + AddMenuItem(windowMenu, "Close Panel", "Close Panel", gApp.GetActivePanel() == null); + AddMenuItem(windowMenu, "Close All", "Close All Panels"); + AddMenuItem(windowMenu, "Close All Except Current", "Close All Panels Except"); + AddMenuItem(windowMenu, "New View into File", "View New", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(windowMenu, "Split View", "View Split", gApp.GetActiveDocumentPanel() == null); + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(windowMenu); + menuWidget.Init(mWindowButton, 0, mHeight); + } + + void ShowHelpMenu() + { + let subMenu = new Menu(); + AddMenuItem(subMenu, "About", "About"); + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(subMenu); + menuWidget.Init(mHelpButton, 0, mHeight); + } + + void ResizeComponents() + { + let font = DarkTheme.sDarkTheme.mSmallFont; + let padding = GS!(20); + float offset = 0; + + + void ResizeButton(Button button) + { + let width = font.GetWidth(button.Label) + padding; + button.Resize(offset, 0, width, mHeight); + offset += width; + } + + ResizeButton(mFileButton); + ResizeButton(mEditButton); + ResizeButton(mViewButton); + ResizeButton(mBuildButton); + ResizeButton(mDebugButton); + ResizeButton(mTestButton); + ResizeButton(mWindowButton); + ResizeButton(mHelpButton); + } + + public override void Resize(float x, float y, float width, float height) + { + base.Resize(x, y, width, height); + ResizeComponents(); + } + + public override void Draw(Graphics g) + { + using (g.PushColor(DarkTheme.COLOR_BKG)) + { + g.FillRect(0, 0, mWidth, mHeight); + } + } +} \ No newline at end of file From 64e833ca4f89ba175a35d7ee686a66621c229bc1 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Fri, 7 Nov 2025 17:22:01 +0100 Subject: [PATCH 19/47] Add bold font failover --- IDE/src/IDEApp.bf | 1 + 1 file changed, 1 insertion(+) diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index 2cab4f6ee..2febfd14f 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -12856,6 +12856,7 @@ namespace IDE } Font.AddFontFailEntry("Segoe UI", scope String()..AppendF("{}fonts/NotoSans-Regular.ttf", mInstallDir)); + Font.AddFontFailEntry("Segoe UI Bold", scope String()..AppendF("{}fonts/NotoSans-Bold.ttf", mInstallDir)); DarkTheme aTheme = new DarkTheme(); mSettings.mUISettings.Apply(); // Apply again to set actual theme From e09f8566356471fd4101608eee94ef5f7fb6e0fb Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Sat, 8 Nov 2025 13:32:01 +0100 Subject: [PATCH 20/47] Handle SDL multi clicks --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 17702a929..2a4f7f9b9 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -475,7 +475,7 @@ void SdlBFApp::Run() { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); if (sdlBFWindow != NULL) - sdlBFWindow->mMouseDownFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, bfMouseBtnOf[sdlEvent.button.button], 1); + sdlBFWindow->mMouseDownFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, bfMouseBtnOf[sdlEvent.button.button], sdlEvent.button.clicks); } break; case SDL_EVENT_MOUSE_MOTION: From 90f5fdda51d7a5ff61ec3fd85d9ce502603b04f9 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Sat, 8 Nov 2025 15:18:45 +0100 Subject: [PATCH 21/47] Implement recent menu in MenuBar --- IDE/src/ui/MenuBar.bf | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/IDE/src/ui/MenuBar.bf b/IDE/src/ui/MenuBar.bf index 38a5b655f..77e0205bb 100644 --- a/IDE/src/ui/MenuBar.bf +++ b/IDE/src/ui/MenuBar.bf @@ -4,6 +4,7 @@ using Beefy.widgets; using Beefy.theme.dark; using Beefy.geom; using IDE; +using IDE.util; using System; using System.Diagnostics; @@ -155,8 +156,11 @@ public class MenuBar : Widget AddMenuItem(openMenu, "Open Crash Dump...", "Open Crash Dump"); let recentMenu = subMenu.AddItem("Open Recent"); - recentMenu.SetDisabled(true); //TODO: recent - + PopulateRecentMenu(.OpenedWorkspace, recentMenu.AddItem("Open Recent Workspace")); + PopulateRecentMenu(.OpenedDebugSession, recentMenu.AddItem("Open Recent Debug Session")); + PopulateRecentMenu(.OpenedFile, recentMenu.AddItem("Open Recent File")); + PopulateRecentMenu(.OpenedCrashDump, recentMenu.AddItem("Open Recent Crash Dump")); + AddMenuItem(subMenu, "Save File", "Save File", gApp.GetActiveDocumentPanel() == null); AddMenuItem(subMenu, "Save As...", "Save As", gApp.GetActiveDocumentPanel() == null); AddMenuItem(subMenu, "Save All", "Save All"); @@ -172,6 +176,22 @@ public class MenuBar : Widget menuWidget.Init(mFileButton, 0, mHeight); } + void PopulateRecentMenu(RecentFiles.RecentKind kind, Menu menu) + { + let recents = gApp.mSettings.mRecentFiles.GetRecentList(kind); + let itemCount = Math.Min(recents.Count, 10); + + menu.SetDisabled(itemCount < 1); + + for(int i = 0; i < itemCount; i++) + { + AddMenuItem(menu, scope $"{i+1} {recents[i]}", new (evt) => + { + gApp.[Friend]ShowRecentFile(kind, i); + }); + } + } + void ShowEditMenu() { let subMenu = new Menu(); From 1ea3cbfee1fb21ccd15e6ff65dc52ee5d3b8dccc Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Thu, 13 Nov 2025 19:56:58 +0100 Subject: [PATCH 22/47] SDL Fix autocomplete closing on self resize --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 57 +++++++++++++++++++++++++-- BeefySysLib/platform/sdl/SdlBFApp.h | 4 +- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 2a4f7f9b9..80956169b 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -6,6 +6,7 @@ #include "platform/PlatformInterface.h" #include #include +#include #include #include #include @@ -44,6 +45,7 @@ void (SDLCALL* bf_SDL_DestroyWindow)(SDL_Window* window); bool (SDLCALL* bf_SDL_GetWindowPosition)(SDL_Window* window,int* x, int* y); bool (SDLCALL* bf_SDL_SetWindowPosition)(SDL_Window* window, int x, int y); bool (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); +bool (SDLCALL* bf_SDL_SetWindowSize)(SDL_Window* window, int w, int h); char* (SDLCALL* bf_SDL_GetClipboardText)(void); bool (SDLCALL* bf_SDL_SetClipboardText)(const char* text); @@ -54,9 +56,13 @@ bool (SDLCALL* bf_SDL_StopTextInput)(SDL_Window* window); bool (SDLCALL* bf_SDL_PollEvent)(SDL_Event* event); bool (SDLCALL* bf_SDL_PushEvent)(SDL_Event* event); +void (SDLCALL* bf_SDL_PumpEvents)(void); +int (SDLCALL* bf_SDL_PeepEvents)(SDL_Event *events, int numevents, SDL_EventAction action, Uint32 minType, Uint32 maxType); bool (SDLCALL* bf_SDL_SetError)(const char *fmt, ...); const char* (SDLCALL* bf_SDL_GetError)(void); +bool (SDLCALL* bf_SDL_SetHint)(const char *name, const char *value); + SDL_DisplayID (SDLCALL* bf_SDL_GetPrimaryDisplay)(void); SDL_DisplayID* (SDLCALL* bf_SDL_GetDisplays)(int* count); bool (SDLCALL* bf_SDL_GetDisplayBounds)(SDL_DisplayID displayID, SDL_Rect* rect); @@ -370,6 +376,7 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_GetWindowPosition); BF_GET_SDLPROC(SDL_SetWindowPosition); BF_GET_SDLPROC(SDL_GetWindowSize); + BF_GET_SDLPROC(SDL_SetWindowSize); BF_GET_SDLPROC(SDL_GetClipboardText); BF_GET_SDLPROC(SDL_SetClipboardText); @@ -380,9 +387,13 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_PollEvent); BF_GET_SDLPROC(SDL_PushEvent); + BF_GET_SDLPROC(SDL_PumpEvents); + BF_GET_SDLPROC(SDL_PeepEvents); BF_GET_SDLPROC(SDL_SetError); BF_GET_SDLPROC(SDL_GetError); + BF_GET_SDLPROC(SDL_SetHint); + BF_GET_SDLPROC(SDL_GetPrimaryDisplay); BF_GET_SDLPROC(SDL_GetDisplays); BF_GET_SDLPROC(SDL_GetDisplayBounds); @@ -419,6 +430,8 @@ void SdlBFApp::Init() mRunning = true; mInMsgProc = false; +// bf_SDL_SetHint(SDL_HINT_EVENT_LOGGING, "1"); + mRenderDevice = new GLRenderDevice(); mRenderDevice->Init(this); } @@ -532,7 +545,7 @@ void SdlBFApp::Run() } break; case SDL_EVENT_WINDOW_MOVED: - case SDL_EVENT_WINDOW_RESIZED: +// case SDL_EVENT_WINDOW_RESIZED: case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); @@ -553,7 +566,6 @@ void SdlBFApp::Run() } } break; - } } @@ -618,8 +630,45 @@ void SdlBFWindow::SetClientPosition(int x, int y) { bf_SDL_SetWindowPosition(mSDLWindow, x, y); - if (mMovedFunc != NULL) - mMovedFunc(this); + SDL_Event e; + bool hasHandled; + while (bf_SDL_PeepEvents(&e, 1, SDL_GETEVENT, SDL_EVENT_WINDOW_MOVED, SDL_EVENT_WINDOW_MOVED) > 0) + { + if (mMovedFunc != NULL && !hasHandled) + { + mRenderWindow->Resized(); + mMovedFunc(this); + + hasHandled = true; + mHasPositionInit = false; + } + } +} + +void SdlBFWindow::Resize(int x, int y, int width, int height, ShowKind showKind) +{ + bf_SDL_SetWindowPosition(mSDLWindow, x, y); + bf_SDL_SetWindowSize(mSDLWindow, width, height); + + SDL_Event e; + bool hasHandled; + while (bf_SDL_PeepEvents(&e, 1, SDL_GETEVENT, SDL_EVENT_WINDOW_MOVED, SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) > 0) + { + if (mMovedFunc != NULL && !hasHandled) + { + mRenderWindow->Resized(); + mMovedFunc(this); + + hasHandled = true; + mHasPositionInit = false; + } + } +} + +void SdlBFWindow::GetPlacement(int* normX, int* normY, int* normWidth, int* normHeight, int* showKind) +{ + bf_SDL_GetWindowPosition(mSDLWindow, normX, normY); + bf_SDL_GetWindowSize(mSDLWindow, normWidth, normHeight); } void SdlBFWindow::SetAlpha(float alpha, uint32 destAlphaSrcMask, bool isMouseVisible) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.h b/BeefySysLib/platform/sdl/SdlBFApp.h index 47fdb697f..ea40999f2 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.h +++ b/BeefySysLib/platform/sdl/SdlBFApp.h @@ -29,8 +29,8 @@ class SdlBFWindow : public BFWindow virtual void SetTitle(const char* title) override {} virtual void SetMinimumSize(int minWidth, int minHeight, bool clientSized) override {} - virtual void GetPlacement(int* normX, int* normY, int* normWidth, int* normHeight, int* showKind) override { } - virtual void Resize(int x, int y, int width, int height, ShowKind showKind) override {} + virtual void GetPlacement(int* normX, int* normY, int* normWidth, int* normHeight, int* showKind) override; + virtual void Resize(int x, int y, int width, int height, ShowKind showKind) override; virtual void SetMouseVisible(bool isMouseVisible) override {} virtual bool TryClose() override; From 8a57422b38e5382d618a483b481439bc664cc10c Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:43:49 +0100 Subject: [PATCH 23/47] SDL Restore save dialog on quit --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 80956169b..a43cac017 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -453,14 +453,12 @@ void SdlBFApp::Run() switch (sdlEvent.type) { - case SDL_EVENT_QUIT: - Shutdown(); - break; case SDL_EVENT_WINDOW_CLOSE_REQUESTED: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); if(sdlBFWindow != NULL) - gBFApp->RemoveWindow(sdlBFWindow); + if (sdlBFWindow->mCloseQueryFunc(sdlBFWindow) != 0) + gBFApp->RemoveWindow(sdlBFWindow); } break; case SDL_EVENT_WINDOW_FOCUS_GAINED: From 1b2878bc54155e5a262d7c37e98c7708daf2ae09 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Fri, 14 Nov 2025 22:13:23 +0100 Subject: [PATCH 24/47] Let system locate libSDL3 --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index a43cac017..543e7185a 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -103,7 +103,7 @@ static HMODULE GetSDLModule(const StringImpl& installDir) String loadPath = installDir + "SDL3.dll"; gSDLModule = ::LoadLibraryA(loadPath.c_str()); #elif defined (BF_PLATFORM_LINUX) - String loadPath = "/usr/lib/libSDL3.so"; + String loadPath = "libSDL3.so"; gSDLModule = dlopen(loadPath.c_str(), RTLD_LAZY); #endif if (gSDLModule == NULL) From 0aa0eb6994866473117db19ffa9d1b8a45e6dbf9 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Sat, 15 Nov 2025 10:02:13 +0100 Subject: [PATCH 25/47] Use layout aware SDL_Keycode --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 160 +++++++++++++------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 543e7185a..571f0a22d 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -245,83 +245,83 @@ bool SdlBFWindow::TryClose() return mSDLWindow == NULL; } -static int SDLConvertScanCode(int scanCode) +static int SDLConvertKeyCode(SDL_Keycode scanCode) { - if ((scanCode >= SDL_SCANCODE_A) && (scanCode <= SDL_SCANCODE_Z)) - return (scanCode - SDL_SCANCODE_A) + 'A'; - if ((scanCode >= SDL_SCANCODE_1) && (scanCode <= SDL_SCANCODE_9)) - return (scanCode - SDL_SCANCODE_1) + '1'; + if ((scanCode >= SDLK_A) && (scanCode <= SDLK_Z)) + return (scanCode - SDLK_A) + 'A'; + if ((scanCode >= SDLK_1) && (scanCode <= SDLK_9)) + return (scanCode - SDLK_1) + '1'; switch (scanCode) { - case SDL_SCANCODE_9: return '0'; - case SDL_SCANCODE_CANCEL: return 0x03; - case SDL_SCANCODE_BACKSPACE: return 0x08; - case SDL_SCANCODE_TAB: return 0x09; - case SDL_SCANCODE_CLEAR: return 0x0C; - case SDL_SCANCODE_RETURN: return 0x0D; - case SDL_SCANCODE_LSHIFT: return 0x10; - case SDL_SCANCODE_RSHIFT: return 0x10; - case SDL_SCANCODE_LCTRL: return 0x11; - case SDL_SCANCODE_RCTRL: return 0x11; - case SDL_SCANCODE_MENU: return 0x12; - case SDL_SCANCODE_PAUSE: return 0x13; - case SDL_SCANCODE_LANG1: return 0x15; - case SDL_SCANCODE_LANG2: return 0x15; - case SDL_SCANCODE_LANG3: return 0x17; - case SDL_SCANCODE_LANG4: return 0x18; - case SDL_SCANCODE_LANG5: return 0x19; - case SDL_SCANCODE_LANG6: return 0x19; - case SDL_SCANCODE_ESCAPE: return 0x1B; - case SDL_SCANCODE_SPACE: return 0x20; - case SDL_SCANCODE_PAGEUP: return 0x21; - case SDL_SCANCODE_PAGEDOWN: return 0x22; - case SDL_SCANCODE_END: return 0x23; - case SDL_SCANCODE_HOME: return 0x24; - case SDL_SCANCODE_LEFT: return 0x25; - case SDL_SCANCODE_UP: return 0x26; - case SDL_SCANCODE_RIGHT: return 0x27; - case SDL_SCANCODE_DOWN: return 0x28; - case SDL_SCANCODE_SELECT: return 0x29; - case SDL_SCANCODE_PRINTSCREEN: return 0x2A; - case SDL_SCANCODE_EXECUTE: return 0x2B; - case SDL_SCANCODE_INSERT: return 0x2D; - case SDL_SCANCODE_DELETE: return 0x2E; - case SDL_SCANCODE_HELP: return 0x2F; - case SDL_SCANCODE_LGUI: return 0x5B; - case SDL_SCANCODE_RGUI: return 0x5C; - case SDL_SCANCODE_KP_0: return 0x60; - case SDL_SCANCODE_KP_1: return 0x61; - case SDL_SCANCODE_KP_2: return 0x62; - case SDL_SCANCODE_KP_3: return 0x63; - case SDL_SCANCODE_KP_4: return 0x64; - case SDL_SCANCODE_KP_5: return 0x65; - case SDL_SCANCODE_KP_6: return 0x66; - case SDL_SCANCODE_KP_7: return 0x67; - case SDL_SCANCODE_KP_8: return 0x68; - case SDL_SCANCODE_KP_9: return 0x69; - case SDL_SCANCODE_KP_MULTIPLY: return 0x6A; - case SDL_SCANCODE_KP_PLUS: return 0x6B; - case SDL_SCANCODE_SEPARATOR: return 0x6C; - case SDL_SCANCODE_KP_MINUS: return 0x6D; - case SDL_SCANCODE_KP_PERIOD: return 0x6E; - case SDL_SCANCODE_KP_DIVIDE: return 0x6F; - case SDL_SCANCODE_F1: return 0x70; - case SDL_SCANCODE_F2: return 0x71; - case SDL_SCANCODE_F3: return 0x72; - case SDL_SCANCODE_F4: return 0x73; - case SDL_SCANCODE_F5: return 0x74; - case SDL_SCANCODE_F6: return 0x75; - case SDL_SCANCODE_F7: return 0x76; - case SDL_SCANCODE_F8: return 0x77; - case SDL_SCANCODE_F9: return 0x78; - case SDL_SCANCODE_F10: return 0x79; - case SDL_SCANCODE_F11: return 0x7A; - case SDL_SCANCODE_F12: return 0x7B; - case SDL_SCANCODE_NUMLOCKCLEAR: return 0x90; - case SDL_SCANCODE_SCROLLLOCK: return 0x91; - case SDL_SCANCODE_GRAVE: return 0xC0; - //case SDL_SCANCODE_COMMAND: return 0xF0; + case SDLK_9: return '0'; + case SDLK_CANCEL: return 0x03; + case SDLK_BACKSPACE: return 0x08; + case SDLK_TAB: return 0x09; + case SDLK_CLEAR: return 0x0C; + case SDLK_RETURN: return 0x0D; + case SDLK_LSHIFT: return 0x10; + case SDLK_RSHIFT: return 0x10; + case SDLK_LCTRL: return 0x11; + case SDLK_RCTRL: return 0x11; + case SDLK_MENU: return 0x12; + case SDLK_PAUSE: return 0x13; + case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG1): return 0x15; + case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG2): return 0x15; + case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG3): return 0x17; + case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG4): return 0x18; + case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG5): return 0x19; + case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG6): return 0x19; + case SDLK_ESCAPE: return 0x1B; + case SDLK_SPACE: return 0x20; + case SDLK_PAGEUP: return 0x21; + case SDLK_PAGEDOWN: return 0x22; + case SDLK_END: return 0x23; + case SDLK_HOME: return 0x24; + case SDLK_LEFT: return 0x25; + case SDLK_UP: return 0x26; + case SDLK_RIGHT: return 0x27; + case SDLK_DOWN: return 0x28; + case SDLK_SELECT: return 0x29; + case SDLK_PRINTSCREEN: return 0x2A; + case SDLK_EXECUTE: return 0x2B; + case SDLK_INSERT: return 0x2D; + case SDLK_DELETE: return 0x2E; + case SDLK_HELP: return 0x2F; + case SDLK_LGUI: return 0x5B; + case SDLK_RGUI: return 0x5C; + case SDLK_KP_0: return 0x60; + case SDLK_KP_1: return 0x61; + case SDLK_KP_2: return 0x62; + case SDLK_KP_3: return 0x63; + case SDLK_KP_4: return 0x64; + case SDLK_KP_5: return 0x65; + case SDLK_KP_6: return 0x66; + case SDLK_KP_7: return 0x67; + case SDLK_KP_8: return 0x68; + case SDLK_KP_9: return 0x69; + case SDLK_KP_MULTIPLY: return 0x6A; + case SDLK_KP_PLUS: return 0x6B; + case SDLK_SEPARATOR: return 0x6C; + case SDLK_KP_MINUS: return 0x6D; + case SDLK_KP_PERIOD: return 0x6E; + case SDLK_KP_DIVIDE: return 0x6F; + case SDLK_F1: return 0x70; + case SDLK_F2: return 0x71; + case SDLK_F3: return 0x72; + case SDLK_F4: return 0x73; + case SDLK_F5: return 0x74; + case SDLK_F6: return 0x75; + case SDLK_F7: return 0x76; + case SDLK_F8: return 0x77; + case SDLK_F9: return 0x78; + case SDLK_F10: return 0x79; + case SDLK_F11: return 0x7A; + case SDLK_F12: return 0x7B; + case SDLK_NUMLOCKCLEAR: return 0x90; + case SDLK_SCROLLLOCK: return 0x91; + case SDLK_GRAVE: return 0xC0; + //case SDLK_COMMAND: return 0xF0; } return 0; } @@ -507,16 +507,16 @@ void SdlBFApp::Run() SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); if (sdlBFWindow != NULL) { - sdlBFWindow->mKeyDownFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.scancode), sdlEvent.key.repeat); - switch (sdlEvent.key.scancode) // These keys are not handled by SDL_TEXTINPUT + sdlBFWindow->mKeyDownFunc(sdlBFWindow, SDLConvertKeyCode(sdlEvent.key.key), sdlEvent.key.repeat); + switch (sdlEvent.key.key) // These keys are not handled by SDL_TEXTINPUT { - case SDL_SCANCODE_RETURN: + case SDLK_RETURN: sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\n'); break; - case SDL_SCANCODE_BACKSPACE: + case SDLK_BACKSPACE: sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\b'); break; - case SDL_SCANCODE_TAB: + case SDLK_TAB: sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\t'); break; default:; @@ -539,7 +539,7 @@ void SdlBFApp::Run() { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); if (sdlBFWindow != NULL) - sdlBFWindow->mKeyUpFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.scancode)); + sdlBFWindow->mKeyUpFunc(sdlBFWindow, SDLConvertKeyCode(sdlEvent.key.key)); } break; case SDL_EVENT_WINDOW_MOVED: From 8b09920361a30451d726d2666d12173dc2cbee34 Mon Sep 17 00:00:00 2001 From: Fusioon Date: Sat, 15 Nov 2025 19:05:26 +0100 Subject: [PATCH 26/47] Add font cache on linux using fontconfig --- BeefLibs/Beefy2D/src/gfx/Font.bf | 283 ++++++++++++++++++++++++------- IDE/src/IDEApp.bf | 4 +- 2 files changed, 226 insertions(+), 61 deletions(-) diff --git a/BeefLibs/Beefy2D/src/gfx/Font.bf b/BeefLibs/Beefy2D/src/gfx/Font.bf index 005c00b23..9e6557113 100644 --- a/BeefLibs/Beefy2D/src/gfx/Font.bf +++ b/BeefLibs/Beefy2D/src/gfx/Font.bf @@ -1,4 +1,5 @@ using System; +using System.Interop; using System.Collections; using System.Text; using System.IO; @@ -259,6 +260,86 @@ namespace Beefy.gfx List mFontEffectStack ~ delete _; DisposeProxy mFontEffectDisposeProxy = new .(new () => { PopFontEffect(); }) ~ delete _; +#if BF_PLATFORM_LINUX + + const String FONTCONFIG_LIB = "libfontconfig.so"; + static bool IsFontconfigAvailable { get; private set; } = true; + + [StaticInitPriority(100)] + private static class FontconfigAllowFail + { + static this() + { + Runtime.AddErrorHandler(new (stage, error) => { + if (stage == .PreFail) + { + if (let err = error as Runtime.LoadSharedLibraryError && err.mPath == FONTCONFIG_LIB) + { + SelfOuter.IsFontconfigAvailable = false; + return .Ignore; + } + } + + return .ContinueFailure; + }); + } + } + + struct FcConfig; + struct FcPattern; + struct FcObjectSet; + + [CRepr] + struct FcFontSet + { + public c_int nfont; + public c_int sfont; + public FcPattern** fonts; + } + + enum FcResult : c_int + { + Match, + NoMatch, + TypeMismatch, + NoId, + OutOfMemory + } + + const String FC_FAMILY = "family"; + const String FC_STYLE = "style"; + const String FC_WEIGHT = "weight"; + const String FC_SLANT = "slant"; + const String FC_FILE = "file"; + + [CLink, Import(FONTCONFIG_LIB)] + static extern c_int FcInit(); + [CLink, Import(FONTCONFIG_LIB)] + static extern void FcFini(); + + [CLink, Import(FONTCONFIG_LIB)] + static extern FcPattern* FcPatternCreate(); + [CLink, Import(FONTCONFIG_LIB)] + static extern void FcPatternDestroy(FcPattern* p); + + [CLink, Import(FONTCONFIG_LIB)] + static extern FcObjectSet* FcObjectSetBuild(c_char* first, ...); + [CLink, Import(FONTCONFIG_LIB)] + static extern void FcObjectSetDestroy(FcObjectSet* os); + + [CLink, Import(FONTCONFIG_LIB)] + static extern FcFontSet* FcFontList(FcConfig* config, FcPattern* p, FcObjectSet* os); + [CLink, Import(FONTCONFIG_LIB)] + static extern void FcFontSetDestroy(FcFontSet* fs); + + [CLink, Import(FONTCONFIG_LIB)] + static extern FcResult FcPatternGetString(FcPattern* p, c_char* object, c_int n, char8** s); + + [CLink, Import(FONTCONFIG_LIB)] + static extern FcResult FcPatternGetInteger(FcPattern* p, c_char* object, c_int n, c_int *i); + +#endif + public this() { } @@ -288,84 +369,156 @@ namespace Beefy.gfx static void BuildFontNameCache() { -#if BF_PLATFORM_WINDOWS using (sMonitor.Enter()) { sFontNameMap = new .(); - for (int pass < 2) +#if BF_PLATFORM_WINDOWS + + for (int pass < 2) + { + Windows.HKey hkey; + + if (pass == 0) { - Windows.HKey hkey; + if (Windows.RegOpenKeyExA(Windows.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", 0, + Windows.KEY_QUERY_VALUE | Windows.KEY_WOW64_32KEY | Windows.KEY_ENUMERATE_SUB_KEYS, out hkey) != Windows.S_OK) + continue; + } + else + { + if (Windows.RegOpenKeyExA(Windows.HKEY_CURRENT_USER, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", 0, + Windows.KEY_QUERY_VALUE | Windows.KEY_WOW64_32KEY | Windows.KEY_ENUMERATE_SUB_KEYS, out hkey) != Windows.S_OK) + continue; + } - if (pass == 0) + defer Windows.RegCloseKey(hkey); + + for (int32 i = 0; true; i++) + { + char16[256] fontNameArr; + uint32 nameLen = 255; + uint32 valType = 0; + + char16[256] data; + uint32 dataLen = 256 * 2; + int32 result = Windows.RegEnumValueW(hkey, i, &fontNameArr, &nameLen, null, &valType, &data, &dataLen); + if (result == 0) { - if (Windows.RegOpenKeyExA(Windows.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", 0, - Windows.KEY_QUERY_VALUE | Windows.KEY_WOW64_32KEY | Windows.KEY_ENUMERATE_SUB_KEYS, out hkey) != Windows.S_OK) - continue; + if (valType == 1) + { + String fontName = scope String(&fontNameArr); + int parenPos = fontName.IndexOf(" ("); + if (parenPos != -1) + fontName.RemoveToEnd(parenPos); + fontName.ToUpper(); + String fontPath = scope String(&data); + if ((!fontPath.EndsWith(".TTF", .OrdinalIgnoreCase)) && (!fontPath.EndsWith(".TTC", .OrdinalIgnoreCase))) + continue; + + if (fontName.Contains('&')) + { + int collectionIdx = 0; + for (var namePart in fontName.Split('&', .RemoveEmptyEntries)) + { + namePart.Trim(); + if (sFontNameMap.TryAddAlt(namePart, var keyPtr, var valuePtr)) + { + *keyPtr = new String(namePart); + *valuePtr = new $"{fontPath}@{collectionIdx}"; + collectionIdx++; + } + } + } + else if (sFontNameMap.TryAdd(fontName, var keyPtr, var valuePtr)) + { + *keyPtr = new String(fontName); + *valuePtr = new String(fontPath); + } + } } else { - if (Windows.RegOpenKeyExA(Windows.HKEY_CURRENT_USER, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", 0, - Windows.KEY_QUERY_VALUE | Windows.KEY_WOW64_32KEY | Windows.KEY_ENUMERATE_SUB_KEYS, out hkey) != Windows.S_OK) + if (result == Windows.ERROR_MORE_DATA) continue; + + break; } + } + } +#elif BF_PLATFORM_LINUX + if (!IsFontconfigAvailable) + return; - defer Windows.RegCloseKey(hkey); + if (FcInit() == 0) + return; + + defer FcFini(); + + let pattern = FcPatternCreate(); + defer FcPatternDestroy(pattern); + let objectset = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT, FC_FILE, null); + defer FcObjectSetDestroy(objectset); - for (int32 i = 0; true; i++) + let fontSet = FcFontList(null, pattern, objectset); + if (fontSet == null) + return; + defer FcFontSetDestroy(fontSet); + + for (let i < fontSet.nfont) + { + let font = fontSet.fonts[i]; + + char8* pFile = null; + if (FcPatternGetString(font, FC_FILE, 0, &pFile) != .Match) + { + continue; + } + + char8* pFamily = null; + if (FcPatternGetString(font, FC_FAMILY, 0, &pFamily) != .Match) + { + continue; + } + + char8* pStyle = null; + FcPatternGetString(font, FC_STYLE, 0, &pStyle); + c_int weight = 0; + let isRegular = (FcPatternGetInteger(font, FC_WEIGHT, 0, &weight) == .Match) && (weight == 80); + c_int slant = 0; + let isRoman = (FcPatternGetInteger(font, FC_SLANT, 0, &slant) == .Match) && (slant == 0); + + let filepath = StringView(pFile); + let familyName = StringView(pFamily); + + String fontName = scope .(48); + fontName.Append(familyName); + if (pStyle != null) { - char16[256] fontNameArr; - uint32 nameLen = 255; - uint32 valType = 0; - - char16[256] data; - uint32 dataLen = 256 * 2; - int32 result = Windows.RegEnumValueW(hkey, i, &fontNameArr, &nameLen, null, &valType, &data, &dataLen); - if (result == 0) + if (isRegular && isRoman) { - if (valType == 1) + fontName.ToUpper(); + + if (sFontNameMap.TryAdd(fontName, let pKey, let pVal)) { - String fontName = scope String(&fontNameArr); - int parenPos = fontName.IndexOf(" ("); - if (parenPos != -1) - fontName.RemoveToEnd(parenPos); - fontName.ToUpper(); - String fontPath = scope String(&data); - if ((!fontPath.EndsWith(".TTF", .OrdinalIgnoreCase)) && (!fontPath.EndsWith(".TTC", .OrdinalIgnoreCase))) - continue; - - if (fontName.Contains('&')) - { - int collectionIdx = 0; - for (var namePart in fontName.Split('&', .RemoveEmptyEntries)) - { - namePart.Trim(); - if (sFontNameMap.TryAddAlt(namePart, var keyPtr, var valuePtr)) - { - *keyPtr = new String(namePart); - *valuePtr = new $"{fontPath}@{collectionIdx}"; - collectionIdx++; - } - } - } - else if (sFontNameMap.TryAdd(fontName, var keyPtr, var valuePtr)) - { - *keyPtr = new String(fontName); - *valuePtr = new String(fontPath); - } + (*pKey) = new String(fontName); + (*pVal) = new String(filepath); } } - else - { - if (result == Windows.ERROR_MORE_DATA) - continue; - break; - } + fontName..Append(' ').Append(pStyle); + } + + fontName.ToUpper(); + + if (sFontNameMap.TryAdd(fontName, let pKey, let pVal)) + { + (*pKey) = new String(fontName); + (*pVal) = new String(filepath); } - } - } + } #endif + } } public static void ClearFontNameCache() @@ -536,9 +689,9 @@ namespace Beefy.gfx BuildFontNameCache(); String pathStr; let lookupStr = scope String(fontName)..ToUpper(); -#if BF_PLATFORM_WINDOWS if (sFontNameMap.TryGetValue(lookupStr, out pathStr)) { +#if BF_PLATFORM_WINDOWS if (!pathStr.Contains(':')) { char8[256] windowsDir; @@ -546,14 +699,26 @@ namespace Beefy.gfx path.Append(&windowsDir); path.Append(@"\Fonts\"); } +#endif path.Append(pathStr); return; } -#endif if ((sFontFailMap != null) && (sFontFailMap.TryGetValue(lookupStr, out pathStr))) { - path.Append(pathStr); + StringView lastPath = pathStr; + for (let fontFailName in pathStr.Split('\0', .RemoveEmptyEntries)) + { + let failName = scope String(fontFailName)..ToUpper(); + if (sFontNameMap.TryGetValue(failName, out pathStr)) + { + GetFontPath(fontFailName, path); + return; + } + lastPath = fontFailName; + } + + path.Append(lastPath); return; } } diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index 2febfd14f..b963676d5 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -12855,8 +12855,8 @@ namespace IDE #endif } - Font.AddFontFailEntry("Segoe UI", scope String()..AppendF("{}fonts/NotoSans-Regular.ttf", mInstallDir)); - Font.AddFontFailEntry("Segoe UI Bold", scope String()..AppendF("{}fonts/NotoSans-Bold.ttf", mInstallDir)); + Font.AddFontFailEntry("Segoe UI", scope $"Noto Sans\0{mInstallDir}fonts/NotoSans-Regular.ttf"); + Font.AddFontFailEntry("Segoe UI Bold", scope $"Noto Sans Bold\0{mInstallDir}fonts/NotoSans-Bold.ttf"); DarkTheme aTheme = new DarkTheme(); mSettings.mUISettings.Apply(); // Apply again to set actual theme From 93ed5df9b59fe3896a20c7c1ecfca726cfee10a8 Mon Sep 17 00:00:00 2001 From: Fusioon Date: Sun, 16 Nov 2025 23:40:04 +0100 Subject: [PATCH 27/47] Linux themes support, ImgCreate cmake --- BeefTools/ImgCreate/CMakeLists.txt | 109 +++++++++++++++++++++++++++++ BeefTools/ImgCreate/ImgCreate.cpp | 8 ++- BeefTools/ImgCreate/build.sh | 26 +++++++ IDE/src/Settings.bf | 9 ++- 4 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 BeefTools/ImgCreate/CMakeLists.txt create mode 100755 BeefTools/ImgCreate/build.sh diff --git a/BeefTools/ImgCreate/CMakeLists.txt b/BeefTools/ImgCreate/CMakeLists.txt new file mode 100644 index 000000000..b806eb30e --- /dev/null +++ b/BeefTools/ImgCreate/CMakeLists.txt @@ -0,0 +1,109 @@ +cmake_minimum_required(VERSION 3.5) + +################### Variables. #################### +# Change if you want modify path or other values. # +################################################### + +set(PROJECT_NAME ImgCreate) +# Output Variables +set(OUTPUT_DEBUG Debug/bin) +set(CMAKE_DEBUG_POSTFIX "_d") +set(OUTPUT_RELEASE Release/bin) + +############## CMake Project ################ +# The main options of project # +############################################# + +project(${PROJECT_NAME} CXX C) + +# Define Debug by default. +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") + message(STATUS "Build type not specified: Use Debug by default.") +endif(NOT CMAKE_BUILD_TYPE) + +include_directories( + . + ../../ + ../../BeefySysLib/ +) + +file(GLOB SRC_FILES + ../../BeefySysLib/Common.cpp + ../../BeefySysLib/BFApp.cpp + ../../BeefySysLib/BFWindow.cpp + ../../BeefySysLib/DataStream.cpp + ../../BeefySysLib/FileStream.cpp + ../../BeefySysLib/util/Hash.cpp + ../../BeefySysLib/util/UTF8.cpp + ../../BeefySysLib/util/String.cpp + ../../BeefySysLib/util/PerfTimer.cpp + ../../BeefySysLib/util/CubicFuncSpline.cpp + ../../BeefySysLib/util/Vector.cpp + ../../BeefySysLib/img/ImageAdjustments.cpp + ../../BeefySysLib/img/ImageData.cpp + ../../BeefySysLib/img/ImageUtils.cpp + ../../BeefySysLib/img/ImgEffects.cpp + ../../BeefySysLib/img/PNGData.cpp + ../../BeefySysLib/img/PSDReader.cpp + ../../BeefySysLib/third_party/png/png.c + ../../BeefySysLib/third_party/png/pngerror.c + ../../BeefySysLib/third_party/png/pngget.c + ../../BeefySysLib/third_party/png/pngmem.c + ../../BeefySysLib/third_party/png/pngpread.c + ../../BeefySysLib/third_party/png/pngread.c + ../../BeefySysLib/third_party/png/pngrio.c + ../../BeefySysLib/third_party/png/pngrtran.c + ../../BeefySysLib/third_party/png/pngrutil.c + ../../BeefySysLib/third_party/png/pngset.c + ../../BeefySysLib/third_party/png/pngtrans.c + ../../BeefySysLib/third_party/png/pngvcrd.c + ../../BeefySysLib/third_party/png/pngwio.c + ../../BeefySysLib/third_party/png/pngwrite.c + ../../BeefySysLib/third_party/png/pngwtran.c + ../../BeefySysLib/third_party/png/pngwutil.c + ../../BeefySysLib/third_party/utf8proc/utf8proc.c + ../../BeefySysLib/third_party/zlib/adler32.c + ../../BeefySysLib/third_party/zlib/compress.c + ../../BeefySysLib/third_party/zlib/crc32.c + ../../BeefySysLib/third_party/zlib/deflate.c + ../../BeefySysLib/third_party/zlib/gzio.c + ../../BeefySysLib/third_party/zlib/infblock.c + ../../BeefySysLib/third_party/zlib/infcodes.c + ../../BeefySysLib/third_party/zlib/inffast.c + ../../BeefySysLib/third_party/zlib/inflate.c + ../../BeefySysLib/third_party/zlib/inftrees.c + ../../BeefySysLib/third_party/zlib/infutil.c + ../../BeefySysLib/third_party/zlib/trees.c + ../../BeefySysLib/third_party/zlib/uncompr.c + ../../BeefySysLib/third_party/zlib/zutil.c + ../../BeefySysLib/third_party/putty/wildcard.c +) + +if (${WIN32}) + include_directories( + ../../BeefySysLib/platform/win32 + ) + file(GLOB SRC_FILES_OS + ../../BeefySysLib/platform/win32/BFPlatform.cpp + ../../BeefySysLib/platform/win32/Platform.cpp + ) +elseif (${APPLE}) + include_directories( + ../../BeefySysLib/platform/darwin + ) + file(GLOB SRC_FILES_OS + ../../BeefySysLib/platform/darwin/BFPlatform.cpp + ../../BeefySysLib/platform/darwin/DarwinCommon.cpp + ) +else() + include_directories( + ../../BeefySysLib/platform/linux + ) + file(GLOB SRC_FILES_OS + ../../BeefySysLib/platform/linux/BFPlatform.cpp + ../../BeefySysLib/platform/linux/LinuxCommon.cpp + ) +endif() + +add_executable(${PROJECT_NAME} ImgCreate.cpp ${SRC_FILES_OS} ${SRC_FILES}) \ No newline at end of file diff --git a/BeefTools/ImgCreate/ImgCreate.cpp b/BeefTools/ImgCreate/ImgCreate.cpp index 7f4cb08b4..c5a12ca75 100644 --- a/BeefTools/ImgCreate/ImgCreate.cpp +++ b/BeefTools/ImgCreate/ImgCreate.cpp @@ -2,7 +2,6 @@ #include "BeefySysLib/util/Array.h" #include "BeefySysLib/img/PSDReader.h" #include "BeefySysLib/img/PNGData.h" -#include USING_NS_BF; @@ -290,8 +289,11 @@ int main() }; if (isThemeDir) - _mkdir("cache"); - + { + BfpFileResult result; + BfpDirectory_Create("cache", &result); + } + for (int size = 0; size < 3; size++) { int scale = 1 << size; diff --git a/BeefTools/ImgCreate/build.sh b/BeefTools/ImgCreate/build.sh new file mode 100755 index 000000000..c8da72e9a --- /dev/null +++ b/BeefTools/ImgCreate/build.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +PATH=/usr/local/bin:$PATH:$HOME/bin +SCRIPTPATH=$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P) + +echo Building from $SCRIPTPATH +cd $SCRIPTPATH + +if command -v ninja >/dev/null 2>&1 ; then + if [ ! -d build ] || [ -f build/build.ninja ]; then + USE_NINJA="-GNinja" + else + echo "Ninja couldn't be enabled for this build, consider doing a clean build to start using Ninja for faster build speeds." + fi +fi + +if [ ! -d build ]; then + mkdir build +fi + +cd build +cmake $USE_NINJA -DCMAKE_BUILD_TYPE=RelWithDebInfo ../ +cmake --build . + +cd $SCRIPTPATH/../../IDE/dist/images +ln -s -f $SCRIPTPATH/build/ImgCreate ImgCreate \ No newline at end of file diff --git a/IDE/src/Settings.bf b/IDE/src/Settings.bf index ec77c8189..3d2ebb2c4 100644 --- a/IDE/src/Settings.bf +++ b/IDE/src/Settings.bf @@ -529,7 +529,14 @@ namespace IDE mColors.Deserialize(sd); } - String imgCreatePath = scope String(gApp.mInstallDir, "images/ImgCreate.exe"); + const String EXECUTABLE_PATH = +#if BF_PLATFORM_WINDOWS + "images/ImgCreate.exe"; +#else + "images/ImgCreate"; +#endif + + String imgCreatePath = scope String(gApp.mInstallDir, EXECUTABLE_PATH); let imgCreateExeTime = File.GetLastWriteTime(imgCreatePath).GetValueOrDefault(); for (let theme in mTheme) From b1326a821de0160dfea1de5bca09e5132c26a8b4 Mon Sep 17 00:00:00 2001 From: Fusioon Date: Mon, 17 Nov 2025 12:43:37 +0100 Subject: [PATCH 28/47] SDL: Fix alt, diacritics and ctrl + backspace --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 24 ++++++++++++++++++------ BeefySysLib/platform/sdl/SdlBFApp.h | 2 ++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 571f0a22d..e6822c14a 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -254,7 +254,6 @@ static int SDLConvertKeyCode(SDL_Keycode scanCode) switch (scanCode) { - case SDLK_9: return '0'; case SDLK_CANCEL: return 0x03; case SDLK_BACKSPACE: return 0x08; case SDLK_TAB: return 0x09; @@ -264,7 +263,8 @@ static int SDLConvertKeyCode(SDL_Keycode scanCode) case SDLK_RSHIFT: return 0x10; case SDLK_LCTRL: return 0x11; case SDLK_RCTRL: return 0x11; - case SDLK_MENU: return 0x12; + case SDLK_LALT: return 0x12; + case SDLK_RALT: return 0x12; case SDLK_PAUSE: return 0x13; case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG1): return 0x15; case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG2): return 0x15; @@ -355,6 +355,8 @@ SdlBFApp::SdlBFApp() mInstallDir += "/"; + mIsControlDown = false; + if (bf_SDL_Init == NULL) { BF_GET_SDLPROC(SDL_Init); @@ -472,7 +474,10 @@ void SdlBFApp::Run() { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); if(sdlBFWindow != NULL) + { sdlBFWindow->mLostFocusFunc(sdlBFWindow); + mIsControlDown = false; + } } break; case SDL_EVENT_MOUSE_BUTTON_UP: @@ -514,11 +519,15 @@ void SdlBFApp::Run() sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\n'); break; case SDLK_BACKSPACE: - sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\b'); + sdlBFWindow->mKeyCharFunc(sdlBFWindow, mIsControlDown ? '\x7F' : '\b'); break; case SDLK_TAB: sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\t'); break; + case SDLK_LCTRL: + mIsControlDown = true; + break; + default:; } } @@ -529,9 +538,9 @@ void SdlBFApp::Run() SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.text.windowID); if (sdlBFWindow != NULL) { - wchar_t wchar; - mbstowcs(&wchar, sdlEvent.text.text, 1); - sdlBFWindow->mKeyCharFunc(sdlBFWindow, wchar); + const auto wideString = Beefy::UTF8Decode(sdlEvent.text.text); + for (int i = 0; i < wideString.length(); i++) + sdlBFWindow->mKeyCharFunc(sdlBFWindow, wideString[i]); } } break; @@ -540,6 +549,9 @@ void SdlBFApp::Run() SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); if (sdlBFWindow != NULL) sdlBFWindow->mKeyUpFunc(sdlBFWindow, SDLConvertKeyCode(sdlEvent.key.key)); + + if (sdlEvent.key.key == SDLK_LCTRL) + mIsControlDown = false; } break; case SDL_EVENT_WINDOW_MOVED: diff --git a/BeefySysLib/platform/sdl/SdlBFApp.h b/BeefySysLib/platform/sdl/SdlBFApp.h index ea40999f2..a602ca902 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.h +++ b/BeefySysLib/platform/sdl/SdlBFApp.h @@ -56,10 +56,12 @@ class SdlBFApp : public BFApp { public: bool mInMsgProc; + bool mIsControlDown; SdlWindowMap mSdlWindowMap; SdlClipboardData* mSdlClipboardData; SDL_GLContext mGLContext; + protected: virtual void Draw() override; virtual void PhysSetCursor() override; From 1bd64f4cdec1cb7e2b4af7985fdd18c52069c254 Mon Sep 17 00:00:00 2001 From: Fusioon Date: Tue, 18 Nov 2025 19:58:37 +0100 Subject: [PATCH 29/47] SDL: Implement changing cursor --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 571f0a22d..7752de05b 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -75,6 +75,10 @@ bool (SDLCALL* bf_SDL_GL_SetAttribute)(SDL_GLAttr attr, int value); void* (SDLCALL* bf_SDL_GL_GetProcAddress)(const char* proc); bool (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); +static SDL_Cursor* (SDLCALL* bf_SDL_GetDefaultCursor)(); +static SDL_Cursor* (SDLCALL* bf_SDL_CreateSystemCursor)(SDL_SystemCursor id); +static bool (SDLCALL* bf_SDL_SetCursor)(SDL_Cursor* cursor); + struct AdjustedMonRect { int mMonCount; @@ -405,6 +409,10 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_GL_SetAttribute); BF_GET_SDLPROC(SDL_GL_GetProcAddress); BF_GET_SDLPROC(SDL_GL_SwapWindow); + + BF_GET_SDLPROC(SDL_GetDefaultCursor); + BF_GET_SDLPROC(SDL_CreateSystemCursor); + BF_GET_SDLPROC(SDL_SetCursor); } mDataDir = mInstallDir; @@ -621,7 +629,24 @@ void SdlBFWindow::GetPosition(int* x, int* y, int* width, int* height, int* clie void SdlBFApp::PhysSetCursor() { - + static SDL_Cursor* cursors[] = + { + bf_SDL_GetDefaultCursor(), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT), + + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT), + NULL + }; + + bf_SDL_SetCursor(cursors[mCursor]); } void SdlBFWindow::SetClientPosition(int x, int y) From 8374e71441daf196b95c0e527f57f4dae26ba3af Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Fri, 21 Nov 2025 14:39:17 +0100 Subject: [PATCH 30/47] SDL fix menu/popup positioning --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 571f0a22d..d2c489d8e 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -145,8 +145,18 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN, (windowFlags & BFWINDOW_MODAL) > 0); bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN, (windowFlags & BFWINDOW_TOPMOST) > 0); - if (parent != NULL) - bf_SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, ((SdlBFWindow*)parent)->mSDLWindow); + if (parent != NULL) + { + SDL_Window* parentWindow = ((SdlBFWindow*)parent)->mSDLWindow; + bf_SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, parentWindow); + if ((windowFlags & BFWINDOW_TOOLTIP | BFWINDOW_FAKEFOCUS) > 0) // Tooltips and menus have relative positioning to their parent. + { + int parentX, parentY; + bf_SDL_GetWindowPosition(parentWindow, &parentX, &parentY); + x -= parentX; + y -= parentY; + } + } if (windowFlags) #ifdef BF_PLATFORM_FULLSCREEN From 9a01cd3e5b3e7ef914e9bb38a4a34dba3eb51941 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Fri, 21 Nov 2025 14:57:14 +0100 Subject: [PATCH 31/47] SDL Faster scroll --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index d2c489d8e..e754b250e 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -508,9 +508,10 @@ void SdlBFApp::Run() break; case SDL_EVENT_MOUSE_WHEEL: { + uint ucNumLines = 3; // Default SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.wheel.windowID); if(sdlBFWindow != NULL) - sdlBFWindow->mMouseWheelFunc(sdlBFWindow, sdlEvent.wheel.mouse_x, sdlEvent.wheel.mouse_y, sdlEvent.wheel.x, sdlEvent.wheel.y); + sdlBFWindow->mMouseWheelFunc(sdlBFWindow, sdlEvent.wheel.mouse_x, sdlEvent.wheel.mouse_y, sdlEvent.wheel.x, sdlEvent.wheel.y * (float)ucNumLines); } case SDL_EVENT_KEY_DOWN: { From c5fc962a0f2117bedb98fc86552156ccd0518acb Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Fri, 21 Nov 2025 16:12:09 +0100 Subject: [PATCH 32/47] SDL Implement title, min size, mouse visibility --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 32 +++++++++++++++++++++++++++ BeefySysLib/platform/sdl/SdlBFApp.h | 8 +++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index e754b250e..a0abc68f3 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -46,6 +46,11 @@ bool (SDLCALL* bf_SDL_GetWindowPosition)(SDL_Window* window,int* x, int* y); bool (SDLCALL* bf_SDL_SetWindowPosition)(SDL_Window* window, int x, int y); bool (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); bool (SDLCALL* bf_SDL_SetWindowSize)(SDL_Window* window, int w, int h); +bool (SDLCALL* bf_SDL_SetWindowMinimumSize)(SDL_Window* window, int min_w, int min_h); +bool (SDLCALL* bf_SDL_SetWindowTitle)(SDL_Window* window, const char* title); + +bool (SDLCALL* bf_SDL_ShowCursor)(void); +bool (SDLCALL* bf_SDL_HideCursor)(void); char* (SDLCALL* bf_SDL_GetClipboardText)(void); bool (SDLCALL* bf_SDL_SetClipboardText)(const char* text); @@ -387,6 +392,11 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_SetWindowPosition); BF_GET_SDLPROC(SDL_GetWindowSize); BF_GET_SDLPROC(SDL_SetWindowSize); + BF_GET_SDLPROC(SDL_SetWindowMinimumSize); + BF_GET_SDLPROC(SDL_SetWindowTitle); + + BF_GET_SDLPROC(SDL_ShowCursor); + BF_GET_SDLPROC(SDL_HideCursor); BF_GET_SDLPROC(SDL_GetClipboardText); BF_GET_SDLPROC(SDL_SetClipboardText); @@ -770,6 +780,28 @@ void SdlBFWindow::ModalsRemoved() //::SetFocus(mHWnd); } +void SdlBFWindow::SetTitle(const char* title) +{ + bf_SDL_SetWindowTitle(mSDLWindow, title); +} + +void SdlBFWindow::SetMinimumSize(int minWidth, int minHeight, bool clientSized) +{ + bf_SDL_SetWindowMinimumSize(mSDLWindow, minWidth, minHeight); +} + +void SdlBFWindow::SetMouseVisible(bool isMouseVisible) +{ + if (isMouseVisible) + { + bf_SDL_ShowCursor(); + } + else + { + bf_SDL_HideCursor(); + } +} + DrawLayer* SdlBFApp::CreateDrawLayer(BFWindow* window) { GLDrawLayer* drawLayer = new GLDrawLayer(); diff --git a/BeefySysLib/platform/sdl/SdlBFApp.h b/BeefySysLib/platform/sdl/SdlBFApp.h index ea40999f2..dda69a305 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.h +++ b/BeefySysLib/platform/sdl/SdlBFApp.h @@ -24,14 +24,14 @@ class SdlBFWindow : public BFWindow SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags); ~SdlBFWindow(); - virtual void* GetUnderlying() {return mSDLWindow; }; + virtual void* GetUnderlying() { return mSDLWindow; }; virtual void Destroy() override; - virtual void SetTitle(const char* title) override {} - virtual void SetMinimumSize(int minWidth, int minHeight, bool clientSized) override {} + virtual void SetTitle(const char* title) override; + virtual void SetMinimumSize(int minWidth, int minHeight, bool clientSized) override; virtual void GetPlacement(int* normX, int* normY, int* normWidth, int* normHeight, int* showKind) override; virtual void Resize(int x, int y, int width, int height, ShowKind showKind) override; - virtual void SetMouseVisible(bool isMouseVisible) override {} + virtual void SetMouseVisible(bool isMouseVisible) override; virtual bool TryClose() override; virtual void GetPosition(int* x, int* y, int* width, int* height, int* clientX, int* clientY, int* clientWidth, int* clientHeight) override; From 5ec1a97dc836ea03ff70922499e897250393d86c Mon Sep 17 00:00:00 2001 From: Fusioon Date: Sun, 23 Nov 2025 16:06:13 +0100 Subject: [PATCH 33/47] Linux: Implement open file explorer and docs link --- IDE/src/ui/ProjectPanel.bf | 41 +++++++++++++++++++ IDE/src/ui/StartupPanel.bf | 82 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 120 insertions(+), 3 deletions(-) diff --git a/IDE/src/ui/ProjectPanel.bf b/IDE/src/ui/ProjectPanel.bf index a2ca1ee49..2f1b1970c 100644 --- a/IDE/src/ui/ProjectPanel.bf +++ b/IDE/src/ui/ProjectPanel.bf @@ -2993,6 +2993,7 @@ namespace IDE.ui if (!path.IsWhiteSpace) { +#if BF_PLATFORM_WINDOWS ProcessStartInfo psi = scope ProcessStartInfo(); psi.SetFileName(path); psi.UseShellExecute = true; @@ -3000,6 +3001,46 @@ namespace IDE.ui var process = scope SpawnedProcess(); process.Start(psi).IgnoreError(); +#elif BF_PLATFORM_LINUX + if (!Linux.IsSystemdAvailable) + return; + + Linux.DBus* userDBus = ?; + if (Linux.SdBusOpenUser(&userDBus) < 0) + return; + defer Linux.SdBusUnref(userDBus); + + Linux.DBusMsg* msg = ?; + if (Linux.SdBusNewMethodCall(userDBus, &msg, + "org.freedesktop.FileManager1", + "/org/freedesktop/FileManager1", + "org.freedesktop.FileManager1", + "ShowFolders") < 0) + return; + defer Linux.SdBusMessageUnref(msg); + + if (Linux.SdBusMessageOpenContainer(msg, .Array, "s") < 0) + return; + + String arg = new:ScopedAlloc! .(path.Length + 10); + arg..Append("file://")..Append(path); + + if (Linux.SdBusMessageAppend(msg, "s", arg.CStr()) < 0) + return; + + if (Linux.SdBusMessageCloseContainer(msg) < 0) + return; + + if (Linux.SdBusMessageAppend(msg, "s", "".CStr()) < 0) + return; + + Linux.DBusErr error = default; + if (Linux.SdBusCall(userDBus, msg, 0, &error, null) < 0) + { + Linux.SdBusErrorFree(&error); + return; + } +#endif } }); diff --git a/IDE/src/ui/StartupPanel.bf b/IDE/src/ui/StartupPanel.bf index f11c17d61..d3bcbb704 100644 --- a/IDE/src/ui/StartupPanel.bf +++ b/IDE/src/ui/StartupPanel.bf @@ -229,7 +229,7 @@ namespace IDE.ui ShowNotFoundDialog(); return; } - +#if BF_PLATFORM_WINDOWS ProcessStartInfo psi = scope ProcessStartInfo(); psi.SetFileName(mPath); psi.UseShellExecute = true; @@ -237,6 +237,46 @@ namespace IDE.ui var process = scope SpawnedProcess(); process.Start(psi).IgnoreError(); +#elif BF_PLATFORM_LINUX + if (!Linux.IsSystemdAvailable) + return; + + Linux.DBus* userDBus = ?; + if (Linux.SdBusOpenUser(&userDBus) < 0) + return; + defer Linux.SdBusUnref(userDBus); + + Linux.DBusMsg* msg = ?; + if (Linux.SdBusNewMethodCall(userDBus, &msg, + "org.freedesktop.FileManager1", + "/org/freedesktop/FileManager1", + "org.freedesktop.FileManager1", + "ShowFolders") < 0) + return; + defer Linux.SdBusMessageUnref(msg); + + if (Linux.SdBusMessageOpenContainer(msg, .Array, "s") < 0) + return; + + String arg = new:ScopedAlloc! .(mPath.Length + 10); + arg..Append("file://")..Append(mPath); + + if (Linux.SdBusMessageAppend(msg, "s", arg.CStr()) < 0) + return; + + if (Linux.SdBusMessageCloseContainer(msg) < 0) + return; + + if (Linux.SdBusMessageAppend(msg, "s", "".CStr()) < 0) + return; + + Linux.DBusErr error = default; + if (Linux.SdBusCall(userDBus, msg, 0, &error, null) < 0) + { + Linux.SdBusErrorFree(&error); + return; + } +#endif } void OpenInTerminal() @@ -251,7 +291,6 @@ namespace IDE.ui psi.SetFileName(gApp.mSettings.mWindowsTerminal); psi.UseShellExecute = true; psi.SetWorkingDirectory(mPath); - var process = scope SpawnedProcess(); process.Start(psi).IgnoreError(); } @@ -412,13 +451,50 @@ namespace IDE.ui void OpenDocumentation() { + const String DOCS_URL = "https://www.beeflang.org/docs/"; +#if BF_PLATFORM_WINDOWS ProcessStartInfo psi = scope ProcessStartInfo(); - psi.SetFileName("https://www.beeflang.org/docs/"); + psi.SetFileName(DOCS_URL); psi.UseShellExecute = true; psi.SetVerb("Open"); var process = scope SpawnedProcess(); process.Start(psi).IgnoreError(); +#elif BF_PLATFORM_LINUX + if (!Linux.IsSystemdAvailable) + return; + + Linux.DBus* userDBus = ?; + if (Linux.SdBusOpenUser(&userDBus) < 0) + return; + defer Linux.SdBusUnref(userDBus); + + Linux.DBusMsg* msg = ?; + if (Linux.SdBusNewMethodCall(userDBus, &msg, + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.OpenURI", + "OpenURI") < 0) + return; + + defer Linux.SdBusMessageUnref(msg); + + if (Linux.SdBusMessageAppend(msg, "ss", "".CStr(), DOCS_URL.CStr()) < 0) + return; + + if (Linux.SdBusMessageOpenContainer(msg, .Array, "{sv}") < 0) + return; + + if (Linux.SdBusMessageCloseContainer(msg) < 0) + return; + + Linux.DBusErr error = default; + if (Linux.SdBusCall(userDBus, msg, 0, &error, null) < 0) + { + Linux.SdBusErrorFree(&error); + return; + } +#endif } } } \ No newline at end of file From abdba32e7dda486747c638a89150c319404fa6fb Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Wed, 26 Nov 2025 20:11:27 +0100 Subject: [PATCH 34/47] SDL per window cursor visibility --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 15 +++++++++++++++ BeefySysLib/platform/sdl/SdlBFApp.h | 1 + 2 files changed, 16 insertions(+) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index a0abc68f3..54ac4afe2 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -218,6 +218,7 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y #endif mIsMouseInside = false; + mIsMouseVisible = true; mHasPositionInit = false; mRenderWindow = new GLRenderWindow((GLRenderDevice*)gBFApp->mRenderDevice, mSDLWindow); mRenderWindow->mWindow = this; @@ -523,6 +524,19 @@ void SdlBFApp::Run() if(sdlBFWindow != NULL) sdlBFWindow->mMouseWheelFunc(sdlBFWindow, sdlEvent.wheel.mouse_x, sdlEvent.wheel.mouse_y, sdlEvent.wheel.x, sdlEvent.wheel.y * (float)ucNumLines); } + case SDL_EVENT_WINDOW_MOUSE_ENTER: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if(sdlBFWindow != NULL) + if (sdlBFWindow->mIsMouseVisible) + { + bf_SDL_ShowCursor(); + } + else + { + bf_SDL_HideCursor(); + } + } case SDL_EVENT_KEY_DOWN: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); @@ -792,6 +806,7 @@ void SdlBFWindow::SetMinimumSize(int minWidth, int minHeight, bool clientSized) void SdlBFWindow::SetMouseVisible(bool isMouseVisible) { + mIsMouseVisible = isMouseVisible; if (isMouseVisible) { bf_SDL_ShowCursor(); diff --git a/BeefySysLib/platform/sdl/SdlBFApp.h b/BeefySysLib/platform/sdl/SdlBFApp.h index dda69a305..1ffb42307 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.h +++ b/BeefySysLib/platform/sdl/SdlBFApp.h @@ -19,6 +19,7 @@ class SdlBFWindow : public BFWindow bool mIsMouseInside; int mModalCount; bool mHasPositionInit; + bool mIsMouseVisible; public: SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags); From 69aa751d6a09480e7db7203d039d6075659ad377 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Thu, 27 Nov 2025 13:00:58 +0100 Subject: [PATCH 35/47] Rework autocomplete resize handling --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 30 +-------------------------- IDE/src/ui/AutoComplete.bf | 21 +------------------ IDE/src/ui/HoverWatch.bf | 4 ---- 3 files changed, 2 insertions(+), 53 deletions(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 54ac4afe2..24854da9b 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -586,11 +586,11 @@ void SdlBFApp::Run() { if (sdlBFWindow->mHasPositionInit) { - sdlBFWindow->mMovedFunc(sdlBFWindow); if (sdlEvent.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) { sdlBFWindow->mRenderWindow->Resized(); } + sdlBFWindow->mMovedFunc(sdlBFWindow); } else { @@ -662,40 +662,12 @@ void SdlBFApp::PhysSetCursor() void SdlBFWindow::SetClientPosition(int x, int y) { bf_SDL_SetWindowPosition(mSDLWindow, x, y); - - SDL_Event e; - bool hasHandled; - while (bf_SDL_PeepEvents(&e, 1, SDL_GETEVENT, SDL_EVENT_WINDOW_MOVED, SDL_EVENT_WINDOW_MOVED) > 0) - { - if (mMovedFunc != NULL && !hasHandled) - { - mRenderWindow->Resized(); - mMovedFunc(this); - - hasHandled = true; - mHasPositionInit = false; - } - } } void SdlBFWindow::Resize(int x, int y, int width, int height, ShowKind showKind) { bf_SDL_SetWindowPosition(mSDLWindow, x, y); bf_SDL_SetWindowSize(mSDLWindow, width, height); - - SDL_Event e; - bool hasHandled; - while (bf_SDL_PeepEvents(&e, 1, SDL_GETEVENT, SDL_EVENT_WINDOW_MOVED, SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) > 0) - { - if (mMovedFunc != NULL && !hasHandled) - { - mRenderWindow->Resized(); - mMovedFunc(this); - - hasHandled = true; - mHasPositionInit = false; - } - } } void SdlBFWindow::GetPlacement(int* normX, int* normY, int* normWidth, int* normHeight, int* showKind) diff --git a/IDE/src/ui/AutoComplete.bf b/IDE/src/ui/AutoComplete.bf index 6f535bbed..10428b161 100644 --- a/IDE/src/ui/AutoComplete.bf +++ b/IDE/src/ui/AutoComplete.bf @@ -288,7 +288,7 @@ namespace IDE.ui return; } - if ((mAutoComplete.mIgnoreMove == 0) && (mWidgetWindow != null) && (!mWidgetWindow.mHasClosed)) + if ((mWidgetWindow != null) && (!mWidgetWindow.mHasClosed) && (window != mWidgetWindow)) mAutoComplete.Close(); } @@ -645,11 +645,9 @@ namespace IDE.ui int windowHeight = (int)(mWantHeight + Math.Max(0, mDocHeight - GS!(32))); - mAutoComplete.mIgnoreMove++; mWidgetWindow.Resize(mWidgetWindow.mNormX, mWidgetWindow.mNormY, windowWidth, windowHeight); mScrollContent.mWidth = mWidth; //Resize(0, 0, mWidgetWindow.mClientWidth, mWidgetWindow.mClientHeight); - mAutoComplete.mIgnoreMove--; ResizeContent(-1, -1, mVertScrollbar != null); } } @@ -997,9 +995,7 @@ namespace IDE.ui { if (mOwnsWindow) { - mAutoComplete.mIgnoreMove++; mAutoComplete.UpdateWindow(ref mWidgetWindow, this, mAutoComplete.mInvokeSrcPositions[0], (int32)extWidth, (int32)extHeight); - mAutoComplete.mIgnoreMove--; } else { @@ -1395,7 +1391,6 @@ namespace IDE.ui public bool mIsDocumentationPass; public bool mIsUserRequested; - public int mIgnoreMove; bool mClosed; bool mPopulating; float mWantX; @@ -1558,8 +1553,6 @@ namespace IDE.ui public void Update() { - Debug.Assert((mIgnoreMove >= 0) && (mIgnoreMove <= 4)); - if ((mInvokeWindow != null) && (!mInvokeWidget.mIsAboveText)) { int textIdx = mTargetEditWidget.Content.mTextCursors.Front.mCursorTextPos; @@ -1588,13 +1581,11 @@ namespace IDE.ui int insertLine = line; if ((insertLine != invokeLine) && ((insertLine - invokeLine) * gApp.mCodeFont.GetHeight() < GS!(40))) { - mIgnoreMove++; mInvokeWidget.mIsAboveText = true; mInvokeWidget.ResizeContent(false); UpdateWindow(ref mInvokeWindow, mInvokeWidget, mInvokeSrcPositions[0], (int32)mInvokeWidget.mWidth, (int32)mInvokeWidget.mHeight); if (mListWindow != null) UpdateWindow(ref mListWindow, mAutoCompleteListWidget, mInsertStartIdx.Value, mListWindow.mWindowWidth, mListWindow.mWindowHeight); - mIgnoreMove--; } } @@ -1915,11 +1906,6 @@ namespace IDE.ui return hadMatch; } - public void SetIgnoreMove(bool ignoreMove) - { - mIgnoreMove += ignoreMove ? 1 : -1; - } - // IDEHelper/third_party/FtsFuzzyMatch.h [CallingConvention(.Stdcall), CLink] static extern bool fts_fuzzy_match(char8* pattern, char8* str, ref int32 outScore, uint8* matches, int maxMatches); @@ -2157,8 +2143,6 @@ namespace IDE.ui SelectEntry(""); } - SetIgnoreMove(true); - gApp.mAutoCompletePanel.StartBind(this); int32 prevInvokeSelect = 0; @@ -2200,7 +2184,6 @@ namespace IDE.ui HandleAutoCompleteListWidget(visibleCount); } gApp.mAutoCompletePanel.FinishBind(); - SetIgnoreMove(false); if ((mAutoCompleteListWidget != null) && (!mAutoCompleteListWidget.mIsInitted)) mAutoCompleteListWidget.Init(); @@ -2407,7 +2390,6 @@ namespace IDE.ui { if (mAutoCompleteListWidget != null) { - mIgnoreMove++; if (IsInPanel()) { mAutoCompleteListWidget.RemoveSelf(); @@ -2421,7 +2403,6 @@ namespace IDE.ui else delete mAutoCompleteListWidget; mAutoCompleteListWidget = null; - mIgnoreMove--; } } if (mAutoCompleteListWidget == null) diff --git a/IDE/src/ui/HoverWatch.bf b/IDE/src/ui/HoverWatch.bf index c8c73149e..4cf91733f 100644 --- a/IDE/src/ui/HoverWatch.bf +++ b/IDE/src/ui/HoverWatch.bf @@ -1111,11 +1111,7 @@ namespace IDE.ui autocomplete = watchEditWidgetContent.mAutoComplete; } - if (autocomplete != null) - autocomplete.SetIgnoreMove(true); mWidgetWindow.Resize((int32)(mOrigScreenX - mTaskbarXOffset + minX), (int32)(mOrigScreenY - mTaskbarYOffset + minY), (int32)(maxX - minX), (int32)(maxY - minY)); - if (autocomplete != null) - autocomplete.SetIgnoreMove(false); } } From 85a56267712a3a5df91aada69e01d048c41d5ed0 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Fri, 28 Nov 2025 19:44:07 +0100 Subject: [PATCH 36/47] Close auto complete if parent window is moved --- IDE/src/ui/AutoComplete.bf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IDE/src/ui/AutoComplete.bf b/IDE/src/ui/AutoComplete.bf index 10428b161..c14774763 100644 --- a/IDE/src/ui/AutoComplete.bf +++ b/IDE/src/ui/AutoComplete.bf @@ -288,7 +288,7 @@ namespace IDE.ui return; } - if ((mWidgetWindow != null) && (!mWidgetWindow.mHasClosed) && (window != mWidgetWindow)) + if ((mWidgetWindow != null) && (!mWidgetWindow.mHasClosed) && (mWidgetWindow.HasParent(window))) mAutoComplete.Close(); } From 7673b8dc5bcb59bb15a709a6064e1c4705e228c0 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Fri, 28 Nov 2025 19:45:28 +0100 Subject: [PATCH 37/47] SDL add app icon --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 62 ++++++++++++++++++++++++++- IDE/src/IDEApp.bf | 14 ++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 24854da9b..2ef5d48ce 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -7,11 +7,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -48,6 +50,7 @@ bool (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); bool (SDLCALL* bf_SDL_SetWindowSize)(SDL_Window* window, int w, int h); bool (SDLCALL* bf_SDL_SetWindowMinimumSize)(SDL_Window* window, int min_w, int min_h); bool (SDLCALL* bf_SDL_SetWindowTitle)(SDL_Window* window, const char* title); +bool (SDLCALL* bf_SDL_SetWindowIcon)(SDL_Window* window, SDL_Surface* icon); bool (SDLCALL* bf_SDL_ShowCursor)(void); bool (SDLCALL* bf_SDL_HideCursor)(void); @@ -74,6 +77,9 @@ bool (SDLCALL* bf_SDL_GetDisplayBounds)(SDL_DisplayID displayID, SDL_Rect* rect) SDL_DisplayMode* (SDLCALL* bf_SDL_GetDesktopDisplayMode)(SDL_DisplayID displayID); bool (SDLCALL* bf_SDL_HasRectIntersection)(const SDL_Rect* A, const SDL_Rect* B); +SDL_IOStream* (SDLCALL* bf_SDL_IOFromMem)(void* mem, size_t size); +SDL_Surface* (SDLCALL* bf_IMG_Load_IO)(SDL_IOStream* src, bool closeio); + SDL_GLContext (SDLCALL* bf_SDL_GL_CreateContext)(SDL_Window* window); bool (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); bool (SDLCALL* bf_SDL_GL_SetAttribute)(SDL_GLAttr attr, int value); @@ -99,6 +105,7 @@ static const char* mimeTypes[] = }; static HMODULE gSDLModule; +static HMODULE gSDLImageModule; static HMODULE GetSDLModule(const StringImpl& installDir) { @@ -123,6 +130,29 @@ static HMODULE GetSDLModule(const StringImpl& installDir) return gSDLModule; } +static HMODULE GetSDLImageModule(const StringImpl& installDir) +{ + if (gSDLImageModule == NULL) + { +#if defined (BF_PLATFORM_WINDOWS) + String loadPath = installDir + "SDL3_image.dll"; + gSDLImageModule = ::LoadLibraryA(loadPath.c_str()); +#elif defined (BF_PLATFORM_LINUX) + String loadPath = "libSDL3_image.so"; + gSDLImageModule = dlopen(loadPath.c_str(), RTLD_LAZY); +#endif + if (gSDLImageModule == NULL) + { +#ifdef BF_PLATFORM_WINDOWS + ::MessageBoxA(NULL, "Failed to load SDL3_image.dll", "FATAL ERROR", MB_OK | MB_ICONERROR); + ::ExitProcess(1); +#endif + BF_FATAL("Failed to load libSDL3_image.so"); + } + } + return gSDLImageModule; +} + template static void BFGetSDLProc(T& proc, const char* name, const StringImpl& installDir) { @@ -135,6 +165,29 @@ static void BFGetSDLProc(T& proc, const char* name, const StringImpl& installDir #define BF_GET_SDLPROC(name) BFGetSDLProc(bf_##name, #name, mInstallDir) +template +static void BFGetSDLImageProc(T& proc, const char* name, const StringImpl& installDir) +{ +#if defined (BF_PLATFORM_WINDOWS) + proc = (T)::GetProcAddress(GetSDLImageModule(installDir), name); +#elif defined (BF_PLATFORM_LINUX) + proc = (T)dlsym(GetSDLImageModule(installDir), name); +#endif +} + +#define BF_GET_SDL_IMAGEPROC(name) BFGetSDLImageProc(bf_##name, #name, mInstallDir) + +static SDL_Surface* gAppIconSurface; + +BF_EXPORT void BF_CALLTYPE BFApp_RegisterAppIcon(void* imageData, size_t size) +{ + SDL_IOStream* stream = bf_SDL_IOFromMem(imageData, size); + if (!stream) + return; + + gAppIconSurface = bf_IMG_Load_IO(stream, true); +} + SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags) { SDL_PropertiesID props = bf_SDL_CreateProperties(); @@ -178,6 +231,8 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y // printf("Created %i : %s\n", bf_SDL_GetWindowID(mSDLWindow), title.c_str()); + if (gAppIconSurface) + bf_SDL_SetWindowIcon(mSDLWindow, gAppIconSurface); bf_SDL_StartTextInput(mSDLWindow); #ifndef BF_PLATFORM_OPENGL_ES2 @@ -395,6 +450,7 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_SetWindowSize); BF_GET_SDLPROC(SDL_SetWindowMinimumSize); BF_GET_SDLPROC(SDL_SetWindowTitle); + BF_GET_SDLPROC(SDL_SetWindowIcon); BF_GET_SDLPROC(SDL_ShowCursor); BF_GET_SDLPROC(SDL_HideCursor); @@ -421,6 +477,9 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_GetDesktopDisplayMode); BF_GET_SDLPROC(SDL_HasRectIntersection); + BF_GET_SDLPROC(SDL_IOFromMem); + BF_GET_SDL_IMAGEPROC(IMG_Load_IO); + BF_GET_SDLPROC(SDL_GL_CreateContext); BF_GET_SDLPROC(SDL_GL_MakeCurrent); BF_GET_SDLPROC(SDL_GL_SetAttribute); @@ -897,4 +956,5 @@ void SdlBFApp::GetWorkspaceRectFrom(int fromX, int fromY, int fromWidth, int fro outY = inflateRect.mY; outWidth = inflateRect.mWidth; outHeight = inflateRect.mHeight; -} \ No newline at end of file +} + diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index 2febfd14f..f099b7507 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -7,6 +7,7 @@ using System.IO; using System.Reflection; using System.Threading; using System.Threading.Tasks; +using System.Interop; using Beefy; using Beefy.widgets; using Beefy.gfx; @@ -121,6 +122,14 @@ namespace IDE public static String sRTVersionStr = "042"; public const String cVersion = "0.43.6"; +#if BF_PLATFORM_LINUX + public const uint8[?] cAppIcon = [IgnoreErrors]{ Compiler.ReadBinary("Resources/beef.ico") }; + + [CallingConvention(.Stdcall), CLink] + static extern void BFApp_RegisterAppIcon(void* imageData, c_size size); +#endif + + #if BF_PLATFORM_WINDOWS public static readonly String sPlatform64Name = "Win64"; public static readonly String sPlatform32Name = "Win32"; @@ -12816,6 +12825,11 @@ namespace IDE mGitManager.Init(); +#if BF_PLATFORM_LINUX + let icon = (Span)cAppIcon; + BFApp_RegisterAppIcon(icon.Ptr, (.)icon.Length); +#endif + //Yoop(); /*for (int i = 0; i < 100*1024*1024; i++) From fc6a291d19b15878e9182f381d69ec5e308ace2f Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Tue, 2 Dec 2025 11:16:33 +0100 Subject: [PATCH 38/47] Remove SDL_image dependency for app icon --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 54 ++++---------------------- IDE/Resources/beef.ico.png | Bin 0 -> 11113 bytes IDE/src/IDEApp.bf | 7 ++-- 3 files changed, 11 insertions(+), 50 deletions(-) create mode 100644 IDE/Resources/beef.ico.png diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 2ef5d48ce..da7e774e4 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -4,12 +4,13 @@ #include "GLRenderDevice.h" #include "platform/PlatformHelper.h" #include "platform/PlatformInterface.h" +#include "img/PNGData.h" #include #include #include -#include #include #include +#include #include #include #include @@ -77,8 +78,7 @@ bool (SDLCALL* bf_SDL_GetDisplayBounds)(SDL_DisplayID displayID, SDL_Rect* rect) SDL_DisplayMode* (SDLCALL* bf_SDL_GetDesktopDisplayMode)(SDL_DisplayID displayID); bool (SDLCALL* bf_SDL_HasRectIntersection)(const SDL_Rect* A, const SDL_Rect* B); -SDL_IOStream* (SDLCALL* bf_SDL_IOFromMem)(void* mem, size_t size); -SDL_Surface* (SDLCALL* bf_IMG_Load_IO)(SDL_IOStream* src, bool closeio); +SDL_Surface* (SDLCALL* bf_SDL_CreateSurfaceFrom)(int width, int height, SDL_PixelFormat format, void *pixels, int pitch); SDL_GLContext (SDLCALL* bf_SDL_GL_CreateContext)(SDL_Window* window); bool (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); @@ -130,29 +130,6 @@ static HMODULE GetSDLModule(const StringImpl& installDir) return gSDLModule; } -static HMODULE GetSDLImageModule(const StringImpl& installDir) -{ - if (gSDLImageModule == NULL) - { -#if defined (BF_PLATFORM_WINDOWS) - String loadPath = installDir + "SDL3_image.dll"; - gSDLImageModule = ::LoadLibraryA(loadPath.c_str()); -#elif defined (BF_PLATFORM_LINUX) - String loadPath = "libSDL3_image.so"; - gSDLImageModule = dlopen(loadPath.c_str(), RTLD_LAZY); -#endif - if (gSDLImageModule == NULL) - { -#ifdef BF_PLATFORM_WINDOWS - ::MessageBoxA(NULL, "Failed to load SDL3_image.dll", "FATAL ERROR", MB_OK | MB_ICONERROR); - ::ExitProcess(1); -#endif - BF_FATAL("Failed to load libSDL3_image.so"); - } - } - return gSDLImageModule; -} - template static void BFGetSDLProc(T& proc, const char* name, const StringImpl& installDir) { @@ -165,27 +142,13 @@ static void BFGetSDLProc(T& proc, const char* name, const StringImpl& installDir #define BF_GET_SDLPROC(name) BFGetSDLProc(bf_##name, #name, mInstallDir) -template -static void BFGetSDLImageProc(T& proc, const char* name, const StringImpl& installDir) -{ -#if defined (BF_PLATFORM_WINDOWS) - proc = (T)::GetProcAddress(GetSDLImageModule(installDir), name); -#elif defined (BF_PLATFORM_LINUX) - proc = (T)dlsym(GetSDLImageModule(installDir), name); -#endif -} - -#define BF_GET_SDL_IMAGEPROC(name) BFGetSDLImageProc(bf_##name, #name, mInstallDir) - static SDL_Surface* gAppIconSurface; -BF_EXPORT void BF_CALLTYPE BFApp_RegisterAppIcon(void* imageData, size_t size) +BF_EXPORT void BF_CALLTYPE BFApp_RegisterAppIcon(uint8* imageData, int size) { - SDL_IOStream* stream = bf_SDL_IOFromMem(imageData, size); - if (!stream) - return; - - gAppIconSurface = bf_IMG_Load_IO(stream, true); + PNGData* image = new PNGData(); + image->LoadFromMemory(imageData, size); + gAppIconSurface = bf_SDL_CreateSurfaceFrom(image->mWidth, image->mHeight, SDL_PIXELFORMAT_ABGR8888, image->mBits, image->mWidth*4); } SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags) @@ -477,8 +440,7 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_GetDesktopDisplayMode); BF_GET_SDLPROC(SDL_HasRectIntersection); - BF_GET_SDLPROC(SDL_IOFromMem); - BF_GET_SDL_IMAGEPROC(IMG_Load_IO); + BF_GET_SDLPROC(SDL_CreateSurfaceFrom); BF_GET_SDLPROC(SDL_GL_CreateContext); BF_GET_SDLPROC(SDL_GL_MakeCurrent); diff --git a/IDE/Resources/beef.ico.png b/IDE/Resources/beef.ico.png new file mode 100644 index 0000000000000000000000000000000000000000..585bd4d1602d60529fa30db14f5fea51d3e132f2 GIT binary patch literal 11113 zcmWk!1zZ$Q6y76`^p5U0kS=MF_6R{hPC&YZQyQeZTk21Pw6t_dBOxLnASD7)QXVPY z|Mh;iw|l!gvomjJ-hAKpUi33f6%s-^LI40ro~R;G;6CWz2E_w^`(!PK!5zdErJ@L# z8{!KD|KPk*&`3Y&I+FP>Bk^fe| zqg0Un-M|^V%PvIL*US>GW@V7VA6))D(prY%O>81q6nj2$(AiyFx@EKY;6-4>fILuXG^q0b-FO zNf9GJk+mN$=pj%!5gKu?zqM2y86t>9la3+Sfd%96q-aYIh)K4e*4Ab0|p-Inl#UzA; z4l?!m^ei{l07BpxI&rT$$F7?+zh`9dWu3l_x%sk4Ma4!Kj9|F_TSke2)rKO4FZb2N z>J3gJUeL!vE;_i)V&J&DzhwHbhH@`+T;d05*lu-GAcZ_Dc;-)=Y)8`pGOLhlae4lS zmQc4DP&N@+G&X~c7S7~cJ@kstOn`7fqLk({@n;uX{2l=J(XvztgRYrRH=<2(F$Vj4 zmrLY;z3+f!U9_LHX^n8L4Kt}Zc)oB1Odq$rygC`lEo!SGxBWexSxhYN`e zd<1{?Xzmk^h*u*%H2hA-%? z z1vWM|vmXKnM18gQi;1JZ;4d%o67|5b7`d%s5f-lPSNdu0XfLnxw=hd+&l>Ol}P{ zNrRVFB*+okd{lvQd1G3_uGBbrlIqUM8+quBIzL%C1Mq?wH0g&PCIfrWWu{MP2WvSu zE%auK)p*dw)`CV{S9jFW_p$Eiy?giM#zs~yFWXoO9Y=i~ZEY(^Sdrcq3h2=n|&5I@Pz}fxY=I8R4FJG8b z)QTeu!zHoJg@590GY?8D9BcX<$0tubN}!G%c>d3-lcEmJoJ-^p7Naij40Q+{_gcQT zErpXp8l%cA=RFo(-u=}K)ShhoK0ZFqOboVex$KHdmD$)wM5SG8;#(%~7vO79{O^`T^YdmIOZ(mX~u&fu8Ke0WQE0})rhbHExT9CSG~HI>AX`3t$)h^Wf5+R#?g#v=^x zjRyUkuXaG-Y`OD%^DF!LYWM_R(C7_b&<&P64KCX{3-mMQn~-RsmCu~6y9}ZTE?03_-X~;-?_MZ z)SB>*c%=PcHKzQz?&$CY}4 zJ1NR6^KIV7KKx#1aFBa zeOYp~eQP2}`BFE+{U#ub^6i0eyA&KQ6P8Dm7kB^^M%ZK6Cm?XS1 zaGG_0Rc+FKy5`Y-!vycdGv`2s`8q<{9P<^C>dg-KW-upbM;F&L@LjRbEER469Bxi&2@5;@RGylSC{xZB62a>=1lJ0jQC8F@^i4E0rT3kiA8@=Hl#yj_y^| zI1UKg{Kw7?$fOqL;8NamEUD{G2KfEw zkeb{`pd5syC>&fmqT}M?c(>gyk9Te7+TW5;n9QmWh4(s3V(Nxhdfy3w|O z)ra~@1#gNfBVe&YKUT}M+{mb?cwjQSmQ`ZYR$Vz{_5CS38Jdqu#LYy4LP^_+uoi3V z2=oaHdX>{)cO1r!u)1C66kw9b-%wUM2w0JdlZ`sUnGB%MO2{bjM$Mj} zy1xi^?bq1b+Z$VK5W@MD?f4T$CTeq!(M&z1katYX%y*XH>f+eF)jdtAHt)VztXJt? zHD@o-Vpv5o=#qs}qfHo$^OMaRLN{a@3#4lmqaIF*HbhwHXO(0sLMi1Xu2fri=v>_r zdd_Z|F>NOv?IsSQbg62^^`=#N60@_j5)Sho;V=YM?0xd+etEEQYV2g3#C^>?kItkhrWOi%gLL`f1>(qZoW)_cFX?2qUh1zQ>T}f6Y{_J9LpA%7FL+56qB9#-d>0PE*S*FADT9hskb&3n!^|<;9 z)<6mHPGrqo`G2dgxj5w#`LU>JV?kqxQjsw8bG2+T)`9o)XO2!|Tr1=Pty=#6eMeM4y)S zOu}Hbd_-y1tWc~U&Hj&tyd(YYn!>}w(-{X1I0`)>BghgtX)dY13ML8^jee@gXmmq4 z2t@DZ!BO(^KAc=hcFuq90l{ZQC?J-_BXaG@- zr<$6Gys@RDWk>2(tg;G1li5!r&L-5&4t>oIUb*;aWzUv;8#>U)WYIWTqZ|x_tq>dq zn0?CdI<enDC#nYdaJxG6NrFF2WvKjM*efHFLd4I)uQdi zV_y!&vr@$}+Ej5OUK?`8J2V;U8yoj8EHHp;Zf$dOghxB*1YkH23Z-BR8X*nxR%1ur zTv}%4$ZAYRUf$r>uY^%$yw`a(A3hjEgcvVwipvf|7X-K0NRhcPpmK{sbajhDd-Y1j zu_LbdiJMma2k=H~v_Ptw86zz%_uIHQ1??0z-s|e+;*^Zc%uhy6o#8D@0rcx{Xqz2C z5W}A*7zV%{GVdI(3%Lj_>u3nWv2y+XK6&@h6>q>2j=^B;*56!E^YZde%BXaVlnNgG|0}IXO6} zzOMg$2MZ|@lcKVIeov9UP3M+QWDKH@D!M#LuF}I42ocAH&b()V{;1~zsnE{zqN1V% z4{|9LZ460Y&*#s00k=naYalEso3+wGhIrj&J(KJ+x4xoc7_`GRD94snfT= zm{4K92?P(C<~OxoqTvx1t3e}j8MN_h%XVg0bX|vWP7HIZ-F7p9rww^4k#g6phE;lI zxPgyY02c1zxj1H08XShEgGu1M4!{?dm{sCH$m+*k902olXTZ<9JuW>g4ZJJ`o0PkG zQe``%wIU)CaNftf$|-k2kU{FT*W_07ZnyKAv3mNNQ6n9xMw0nw3;&6UNZ>7KfYKAS4LQTU`D3yB;0N#9Q2cG^|&xF4DeFLt}J>k4I~5{ zv%w~0C~tS0^KUwc`}Bvcgn3O`i2z+dy`rM|dHIw(g79+tOztRiZ24j{FA~$H;*n=o zu5%E|d<~4E$t!#sHOuX?nk!L%P54X_)r!$Nx(+ItpKV(TTxsRS^2Ww&v-Eb;x5v;8 zUMu0$@%Aw-?GktCffv-q?H(i%77RnLcBr|;G%7caF6LCXhXF&xYL}DH(Hr4R;}$+l z=gmfLd&wQf>`scwXRCm%@+PZf1swH~o_h;Plh@gvoc_+Z_69S)LsoBcGSk_pmkIs) zH%y*U6dUrS{+e|6C%IayTbn9>?zPvtxqKxXZyUf2mDYx zS<0nG*`t_l!dHkY!Zue@dzd|nrUtPYJ@WT%BorJ#e%N#NRFR2uKO0WRMn;x*Tb5*N z`CoLml>0VeA%CwQT^!BXduXdYq)&R910ruK8T(&!K^UYC6CAfPX->j?u!Sbvo<2h z$;t05Xg-sPn8k<4wGRtWNw{3n1L(|)P&X-=M-LtxTJlk4&&TcOy?fXHTua8?s;Rs3 ziMqNvSNgF3yd`7W`p-DZr~yk?upYsZ$!!DlEzo1nzIKqtq#)z4!~;|d9j${*Q0C#o zi<)TUYG}?!Z1m@bF6=?81*>j-TVM$`+u4Hw>L@7y?T{h$0yX#r75Tng) z>R$P(X-wuDeDNCm9yB_mg!{jt-!YiB-lGl(dS>taO)-if@GocJzb;BApB1N@_AV?e z_^)I6JWQDf`oCETJ85WYcD)<2LL*kplwXBp`yPEFv!+)&(_@zE6G|}_XyFOdKa&=O zN38I>+J><7h>S^xHoO{RvN_U19!THo1|8=!5Fd*JEC@NKgfWtP_qzSA&kxN8*ABhW zT@(a?{4tk(jCCPwurL{x*F>yHj$|9q5D!`Vm1!ar)o8Kv!z{SK<&;RUBWPu1#XtKl z^mw?U!qU~1T(J9E_~-7a|0Y(On>Y9UFjzq$mxnY{9ye8NcUDHmxpl#_k5oAvKKMON zTq|n}+p3vuqfg9en^@+e{^IDOnY7r+201u6XN_n5yG?60B_t$N?3xU1yg)tR_fYZx z3-mQ}IH4-{%GIr&*vQ#U*JpA?Xyd#C2no0#<`TC1GMpdB$V9+dnWoB41%g+Q4;j?* z*)0ma9px%|6q|6#-2`~ePkfzjKf%t;%KF>35CCM4bl{ke3%yNJED+M?qM!<4fsM&t z?QA9w{eE%!-#J`Sep1x9&5fX@<1Zl;N*K$Y7+a1FSe%v();VfxAJcHOK{ zpYQa3)k3i`BRkM`_G4ioY)#VW=h(>WCPO29qz6%m!1Q&0H2H-P@w89pueX|{fiR_W zBaH?RpA6)pwdVl~A^W>6Wo}@QJQ^vRq)NP>igohSu4cvQcEbsD72tRz5xoA&Thk*% zd_8S;Gd;6yejM!Ln%F3cR_-)idLy#v>sHmfv9U4pFt}4c#9sW-f@90e6Q9m`u_}g4 z-(S1j2=@es)piLKq^ualU4!wl6(pb?bU9dKzN6Xopf9ocRrsXDW_9|7*+8*+`oZy} zmJ?&`g5bQe?lg8eiVO|{q?d0ea1|Qn7M@>ZMkS7IKF{4;A71~f=;^p-dW9F|Px??z z*i|}vloUyl;jw!Kl9mS&5}t!0PpR1_CnvX_r*mhNMC8wY!o2G%QRBY}um~Y1ZrEFf|-tt#*EZ2#^x_CmHf3sal<_s%O%IzH-~mXTFLF zZt?o{>-~9gtdCa^^Zu7yDKQ6q%V}CHk8y;NH11q;yrG+pn7Z`80wQe5Lo7L(Y#@hT zJM?;+Nv@6k)FtBvf&+wRu7SLAN_2FA*MTp2Xj$FtlI-@>>5i(cda2eK8KzemzBZV! z-_e5v$QQ@4Q`jG??p@PjMd%z%9Y#L8afVk^^u&2kJ1+N~%KXU1mN-}$uEl~th zXUR5t0Mi7lSOkO@MWy!_#_1$Av0TIqMReQCT@er@KoEv=-W99Y9z8coVr*p8lUj`V z*dd${r$-V1lYgPZrPx{(F^f5=y2gdGf};am?hLO&S>$E&2K(;Gy%i!Z;EJinw0#?~ z#Q~)3sq?X6pj@B`b qm~~GYMITZJ9#Ws5wsgn&bdqXJKI5aZ$l4(3eT5K$#0bLe zyrQO!Uu!VO%(6MdTY>QAPlDLO3lo_h;m|rH9rg?$D|QQ8m}E+$&(1Jyi$4E)MK5#p+>!9(-X-u)p6Q2Fsdx_z?p{(w@)s(ceR%v0=LGyJ@(&nKvlzlVlhD2b^DSsk_H zdu+pJ&$qTz(-HTg=9B*TeTp!3{>@oI)%{v$Ptf*uK2nwXha|H_pk@o)XfpN&nh1JO3 z;&xnq%08cK-+b_b`|%60GR3*G)9EvR>C3Gmuk)42mKhZ&D{-Gtl6do2cR*&=d-WItdhOYDR(u2v^n$-byr~IFe zJ2pUc6 zgU(l7(PKH%am?3$O3z0}tjj)qXxzD(lXI%=V8+cgXE=va12<(t=+lV*p27! z5eoaId6(}h+7ulJy+o28J3sh;3KkQa%(}AyVZOMZ0)S;~Fw%fC!f^Wdyn>NM#3ZzJ zF9?V6Swq(2E-R=Q@j?xBp%IIzXRp<%A_;;7 z!i_Jx_SL!12Byrz^V?X#MhVf~PMn?i*5dtsC@JDYc6THfNFBN@&9KzY-&LIA%z$rp z`#1e?)G2^m0k+^l&02hX^DQl(2#~cWT83+HdFKiiAD^jxFvkiLv=|jN7nO_X?Pe`> zb@p+_x?AJDBcT-`5F~bdaMiWH*1DP7=h=O=_(**0tt4*Orgwno_DDOmKo1+DbkT8M z0k+e6g8#sLR03Y<&Fr+|q~sxxD|-sP-N|&4*QHQRGqQGJ#p$})b#fB>{J6Mpvj%;-B^ZvUwW4qCtSwj&QQwX;-iHII0I!?o9a%Z1|eI+wc@wr zVhD)S&Nng}KI-Hu*t{VQyso;7DJUplgqvm1?t1t(EIPI$r(%E}aPHibf38Lg!gmrh zsizUSYKlWy?lsaT(5c#7D7XLa=Gz%76LUm$*(1loff0+Mi64n5JjEz9JCW>D&1EVe z>IR?qr;PW^5srq-{X8OUpQD+ zYLYJtWa^mQO)-z7;X@K?HI`tl>T3uBIC zo^%wYThBo~p)l($^Yt|p;v&pH_1jy&2w-*eqf+`*T`7x0BgP!l?P?9mW9#S!8_+nr zrA-}eONY+L#uZ7;22?pHW)BSReST_zD05)0Am96TO%#$s{Hl|%W+seSvFrhMU9e~) zlA(X$NjKAf8^ZAGG?G_E5j`%fI=DVk(fTtXFdWrihrZ>S&?d$n4NT72$5?!j?Zn2f ziAKME*}e9A%Ab_k8PEw~c`XQjG&MEF!LY^NX{_NR;K?uV15O;jL8bM-#6K%_zdQ@D zf)O)rN3*`?jCi!r9*PHn)5z~$B5Xy5p(ak4G)1^jtif-l%pfX}Aja?Co(MNWfkzMS zhKGk^6=03Jx>ZQwWsvL3Fe+8V6xxFUs3vrSL6@C!V(atSrGb!0i>MP%Ipj;1s>f9* zoG^sEB8!`}y>YIBL$#kIq?l!DtjKPZ&*SpoHwv>G9->P~@XZpbcCz#n9xHkWvli6Q_)Q@*ujio(}MzQpH(}(Fn zk!bevVNGr(+~@X`=t&MPu|2iqmxnmW9A6=)I|LFc6F6(!(=Kyyd3o9NrnLhu7WiXp z@TtN?P0}HWnJKC;dDq>Z?sd|aeF$Eo{NIV3dqiNb3huk5SZ6rp-Oe4Iy(SSWHv zU}0qt49I|W?1aE9`GxO1U}iL3-MvB*s)gje5iX_~63|n|SD3!*Q z^2ozAK=E+{MfWc1yld&B*WqgS=-8MUwPHttX2Yf)7|iJ&!*#3bk%p6)b^3_Oh>M@@ zou8j?IzpXk%{6+4Xc)Wh;vZ`e@K>|BTH-KxeA`QgG5#(4-3JfQ;$sPueyU z{ZVxj3Pn*1iOWQpOZcuwnbXGkurkyyJE@$q2XWcr-1poLVP_Mf5dHjLqT&J>m00%Z z`m9;tO|flL8%vlMevZODkM2fheCPL-cDPjGFcDCRSg(9h(-q(QE_;0Qx&Er)Sex3s$&oe(rW48wa@; zNMP`soNy46{Od<#X?~|hE%=$l^4bvbB!599XvNI z7516Ty=$5^^{oS)H?lV`4y*fsD~F`R$fK6PJT0n(nwmOY!1$x{HTmpD6b3%w#(s^Ks}G2@ggCyzWd+Qj7J59C z_o-sN!xb$GTS{3V2hZs!%oOeLm;>X@ztqM)5LHfYq3Hmva<&wBw;oF^*rkPJ#`+kh z`Dbgz0>V570%S=m-nnl;)9GqcI9OV)5-t8x zWT!>3P9@xjAo^!_HJ_fl5D;g6BK{FB`DcwlHQe#w3Z)k4;cW z$>Jt(G90Hsu@)3bJa0iNeUqS9DaIT2k;eM!kG890mlMR99`*zI+c%fm$iSf7D6RPY zo>&K6Z!#;9Ik5*3QbKO_9t!fU&l&Qsflxp#5@ z$uKje9o)kOWLUO7V~fLEt1+L!NKs`pQV~>z3V!Li_RA+eV@u=9m;7{NO@c^AZ?2VT zykA~KS5o5y->gJ3gIKTodcCqr&rTD1IG1hBC;m(NhK3cD6+geC z8yS+v%<^+?3oknut1NCi;L4Xy(>W;Yey5&v`&K9(1pENu_6Y0jY!EUNi>O- zS5Z31$$9#cJ^HgVR&aSphSSi&H0I=@<(CiU6}5n|R}1IQyjJR7IX7Sd2TKr3fj$UC zI%GPP)Axt#K{iZ^tQv3m06|8oETdmS$#{Y^i5|k8@2#IF9^H{a#UK(6`49h-ca?<5 z6}UUkk+*x&OM!uuj;l(un?D2F+uIoAu|>*yc5E0kL-T=4O>zw=1`OZMXTWHD;{fs(eRr<67EpZ6)0`=28wVmMj-=D6qn3M{2L?Y!U; z3w}m@pg|dc3Qd0o|3j!>P~~y#S@@<3j1#qiuK8bf7xj3-nCGi=#|4{=HBLeR4s9bz zNCqN+cZn#bgu%5|6(;ZBzssMOf?{Uonj+M{ofg8#kAvi5m8Qjw*B7sL6ZC zH|X}>{{wvve1L}WDEuMBGEsO94a*9e!hMfu-cy)QDe4n)1IPcXlG4(pF29SvjSlnR zTJ7$J_fTN1h;8xAuWK(Uy0|D)f-p!jh#}|!I@fBWz)dR0_ji(trsmV9@0$7So@!Iv z?2VQ>z9neJp)$bfCHcF5P43^=w`_VgL3zIDd)0d4;~xMbiq-Yu^imK4g-4|ob=*qJ z((O|HA_@$A4{=Tw+xm>>4XmNQ5+J^oqRL0WzH!7eK04J0^D>r(6sG1o`IktBf)HGk^?l&|M)eiIYAXa~|xw%<0Hag1R zG`}kauTsI6*kJo4QP;VZ{|Xn>Jt*EujcJ-kjhWF<_UFenmX@m^O~EJ`){XXSG%{w=n6 zJlEsWw?Av!sxdS6^S{7*|79TPRq0iN@Gn)lXd=eXJ2h%%qQI_7>FUCxCH9Xt)wMrv zPwNBJQ}h>d#i^(=GKaMaK%A|hT{|m#AOF)* zI`A=6cPoUIsWqA%Ad=6@d=6K&oEUS9)cAppIcon; - BFApp_RegisterAppIcon(icon.Ptr, (.)icon.Length); + BFApp_RegisterAppIcon(icon.Ptr, icon.Length); #endif //Yoop(); From 3fb6e5ef6650601469315ae70cdc5aae8239e5e3 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Sat, 6 Dec 2025 15:17:07 +0100 Subject: [PATCH 39/47] Prevent linux crash when renaming projects --- IDE/src/IDEApp.bf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index 6d0182abc..3fbab11f1 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -13681,7 +13681,9 @@ namespace IDE DeleteAndNullify!(mBfBuildSystem); /// +#if BF_PLATFORM_WINDOWS mDebugger.FullReportMemory(); +#endif var workspaceBuildDir = scope String(); GetWorkspaceBuildDir(workspaceBuildDir); From 1e0f32589cec230e01bb82fb45ce9b3c2e56e743 Mon Sep 17 00:00:00 2001 From: zerkawei <40337544+zerkawei@users.noreply.github.com> Date: Fri, 26 Dec 2025 12:23:03 +0100 Subject: [PATCH 40/47] SDL Fix mouse staying hidden --- BeefySysLib/platform/sdl/SdlBFApp.cpp | 32 ++++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 8c2173782..90c62da26 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -52,6 +52,7 @@ bool (SDLCALL* bf_SDL_SetWindowSize)(SDL_Window* window, int w, int h); bool (SDLCALL* bf_SDL_SetWindowMinimumSize)(SDL_Window* window, int min_w, int min_h); bool (SDLCALL* bf_SDL_SetWindowTitle)(SDL_Window* window, const char* title); bool (SDLCALL* bf_SDL_SetWindowIcon)(SDL_Window* window, SDL_Surface* icon); +SDL_Window* (SDLCALL* bf_SDL_GetMouseFocus)(); bool (SDLCALL* bf_SDL_ShowCursor)(void); bool (SDLCALL* bf_SDL_HideCursor)(void); @@ -283,6 +284,18 @@ bool SdlBFWindow::TryClose() return mSDLWindow == NULL; } +static void RefreshMouseVisibility(SdlBFWindow* window) +{ + if (window->mIsMouseVisible) + { + bf_SDL_ShowCursor(); + } + else + { + bf_SDL_HideCursor(); + } +} + static int SDLConvertKeyCode(SDL_Keycode scanCode) { if ((scanCode >= SDLK_A) && (scanCode <= SDLK_Z)) @@ -420,6 +433,7 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_SetWindowMinimumSize); BF_GET_SDLPROC(SDL_SetWindowTitle); BF_GET_SDLPROC(SDL_SetWindowIcon); + BF_GET_SDLPROC(SDL_GetMouseFocus); BF_GET_SDLPROC(SDL_ShowCursor); BF_GET_SDLPROC(SDL_HideCursor); @@ -558,19 +572,21 @@ void SdlBFApp::Run() if(sdlBFWindow != NULL) sdlBFWindow->mMouseWheelFunc(sdlBFWindow, sdlEvent.wheel.mouse_x, sdlEvent.wheel.mouse_y, sdlEvent.wheel.x, sdlEvent.wheel.y * (float)ucNumLines); } + break; case SDL_EVENT_WINDOW_MOUSE_ENTER: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); if(sdlBFWindow != NULL) - if (sdlBFWindow->mIsMouseVisible) - { - bf_SDL_ShowCursor(); - } - else - { - bf_SDL_HideCursor(); - } + RefreshMouseVisibility(sdlBFWindow); } + break; + case SDL_EVENT_WINDOW_DESTROYED: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(bf_SDL_GetWindowID(bf_SDL_GetMouseFocus())); + if(sdlBFWindow != NULL) + RefreshMouseVisibility(sdlBFWindow); + } + break; case SDL_EVENT_KEY_DOWN: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); From 2ab81829fb8fa72fd32c67d616b8b6747fce801a Mon Sep 17 00:00:00 2001 From: Fusioon Date: Mon, 12 Jan 2026 01:05:51 +0100 Subject: [PATCH 41/47] =?UTF-8?q?SDL=20&=C2=A0GL=20Improvements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BeefySysLib/platform/sdl/GLRenderDevice.cpp | 541 +++++++++++++------- BeefySysLib/platform/sdl/GLRenderDevice.h | 38 +- BeefySysLib/platform/sdl/SdlBFApp.cpp | 454 +++++++++------- BeefySysLib/platform/sdl/SdlBFApp.h | 7 +- IDE/src/ui/AutoComplete.bf | 24 +- IDEHelper/CMakeLists.txt | 1 - bin/build.sh | 5 +- 7 files changed, 665 insertions(+), 405 deletions(-) diff --git a/BeefySysLib/platform/sdl/GLRenderDevice.cpp b/BeefySysLib/platform/sdl/GLRenderDevice.cpp index 160031630..e71420339 100644 --- a/BeefySysLib/platform/sdl/GLRenderDevice.cpp +++ b/BeefySysLib/platform/sdl/GLRenderDevice.cpp @@ -40,13 +40,16 @@ USING_NS_BF; #endif extern void* (SDLCALL* bf_SDL_GL_GetProcAddress)(const char* proc); -extern bool (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); +extern bool (SDLCALL* bf_SDL_GetWindowSizeInPixels)(SDL_Window* window, int* w, int* h); extern bool (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); extern bool (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); +extern SDL_GLContext (SDLCALL* bf_SDL_GL_GetCurrentContext)(); +extern const char* (SDLCALL* bf_SDL_GetError)(void); -typedef void (APIENTRYP GL_DEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam); +extern SDL_DisplayID (SDLCALL* bf_SDL_GetDisplayForWindow)(SDL_Window *window); +extern const SDL_DisplayMode* (SDLCALL* bf_SDL_GetCurrentDisplayMode)(SDL_DisplayID displayID); -static void (APIENTRYP bf_glDebugMessageCallback)(GL_DEBUGPROC callback, const void* userParam); +static GLenum (APIENTRYP bf_glGetError)(); static void (APIENTRYP bf_glActiveTexture)(GLenum texture); static void (APIENTRYP bf_glGenVertexArrays)(GLsizei n, GLuint* buffers); static void (APIENTRYP bf_glBindVertexArray)(GLenum target); @@ -109,10 +112,104 @@ static void (APIENTRYP bf_glGetObjectParameterivARB)(GLint obj, GLenum pname, GL static void (APIENTRYP bf_glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); static void (APIENTRYP bf_glClientActiveTexture)(GLenum texture); +static void (APIENTRYP bf_glGenTextures)(GLsizei n, GLuint *textures); +static void (APIENTRYP bf_glBindTexture)(GLenum target, GLuint texture); +static void (APIENTRYP bf_glPixelStorei)(GLenum pname, GLint param); +static void (APIENTRYP bf_glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +static void (APIENTRYP bf_glTexParameteri)(GLenum target, GLenum pname, GLint param); +static void (APIENTRYP bf_glDisable)(GLenum cap); +static void (APIENTRYP bf_glEnable)(GLenum cap); +static void (APIENTRYP bf_glCullFace)(GLenum mode); +static void (APIENTRYP bf_glDepthFunc)(GLenum mode); +static void (APIENTRYP bf_glDepthMask)(GLboolean flag); +static void (APIENTRYP bf_glPolygonMode)(GLenum face, GLenum mode); +static void (APIENTRYP bf_glBlendFunc)(GLenum sfactor, GLenum dfactor); +static void (APIENTRYP bf_glScissor)(GLint x, GLint y, GLsizei width, GLsizei height); +static void (APIENTRYP bf_glViewport)(GLint x, GLint y, GLsizei width, GLsizei height); +static void (APIENTRYP bf_glFramebufferTexture)(GLenum target, GLenum attachment, GLuint texture, GLint level); +static void (APIENTRYP bf_glDrawBuffers)(GLsizei n, const GLenum *bufs); +static GLenum (APIENTRYP bf_glCheckFramebufferStatus)(GLenum target); +static void (APIENTRYP bf_glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +static void (APIENTRYP bf_glBindFramebuffer)(GLenum target, GLuint framebuffer); +static void (APIENTRYP bf_glClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +static void (APIENTRYP bf_glClearDepth)(GLdouble depth); +static void (APIENTRYP bf_glClear)(GLbitfield mask); + #if !defined BF_PLATFORM_OPENGL_ES2 static void (APIENTRYP bf_glGetVertexAttribdv)(GLuint index, GLenum pname, GLdouble *params); #endif +enum class DebugSource : uint32 +{ + API = 0x8246, + WindowSystem = 0x8247, + ShaderCompiler = 0x8248, + ThirdParty = 0x8249, + Application = 0x824A, + Other = 0x824B +}; + +enum class DebugType : uint32 +{ + Error = 0x824C, + DeprecatedBehavior = 0x824D, + UndefinedBehavior = 0x824E, + Portability = 0x824F, + Performance = 0x8250, + Other = 0x8251, + Marker = 0x8268, +}; + +enum class DebugSeverity : uint32 +{ + High = 0x9146, + Medium = 0x9147, + Low = 0x9148, + Notification = 0x826B +}; + +typedef void(*DEBUGPROC)(DebugSource source, DebugType type, uint32 id, DebugSeverity severity, uint32 length, char* message, void* userParam); +static void (APIENTRYP bf_glDebugMessageCallback)(DEBUGPROC fn, void* userdata); + +static void APIENTRY DebugMessageCallback(DebugSource source, DebugType type, uint32 id, DebugSeverity severity, uint32 length, char* message, void* userParam) +{ + if (type == DebugType::Marker || severity == DebugSeverity::Notification) + return; + + switch (type) + { + case DebugType::Error: + OutputDebugStr("[Error] "); + break; + case DebugType::DeprecatedBehavior: + OutputDebugStr("[DeprecatedBehavior] "); + break; + + case DebugType::UndefinedBehavior: + OutputDebugStr("[UndefinedBehavior] "); + break; + + case DebugType::Portability: + OutputDebugStr("[Portability] "); + break; + + case DebugType::Performance: + OutputDebugStr("[Performance] "); + break; + + case DebugType::Other: + OutputDebugStr("[Other] "); + break; + + case DebugType::Marker: + OutputDebugStr("[Marker] "); + break; + } + + OutputDebugStr(message); + OutputDebugStrF(" %d\n", id); +} + /// static int GetPowerOfTwo(int input) @@ -223,11 +320,17 @@ void GLTexture::Blt(ImageData* imageData, int x, int y) } else { - glBindTexture(GL_TEXTURE_2D, mGLTexture); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, imageData->mWidth, imageData->mHeight, GL_RGBA, GL_UNSIGNED_BYTE, imageData->mBits); + bf_glBindTexture(GL_TEXTURE_2D, mGLTexture); + bf_glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, imageData->mWidth, imageData->mHeight, GL_RGBA, GL_UNSIGNED_BYTE, imageData->mBits); } } +void GLTexture::SetBits(int destX, int destY, int destWidth, int destHeight, int srcPitch, uint32 *bits) +{ + bf_glBindTexture(GL_TEXTURE_2D, mGLTexture); + bf_glTexSubImage2D(GL_TEXTURE_2D, 0, destX, destY, destWidth, destHeight, GL_RGBA, GL_UNSIGNED_BYTE, bits); +} + /// GLDrawBatch::GLDrawBatch() : DrawBatch() @@ -241,13 +344,6 @@ GLDrawBatch::~GLDrawBatch() extern int gBFDrawBatchCount; -struct GLVertex3D -{ - float x, y, z; - float u, v; - uint32 color; -}; - void GLDrawBatch::Render(RenderDevice* renderDevice, RenderWindow* renderWindow) { if (mIdxIdx == 0) @@ -260,34 +356,35 @@ void GLDrawBatch::Render(RenderDevice* renderDevice, RenderWindow* renderWindow) if (glRenderDevice->mGLVAO == 0) { + if (glRenderDevice->mGLVertexBuffer == 0) + { + bf_glGenBuffers(1, &glRenderDevice->mGLVertexBuffer); + bf_glGenBuffers(1, &glRenderDevice->mGLIndexBuffer); + } + + bf_glBindBuffer(GL_ARRAY_BUFFER, glRenderDevice->mGLVertexBuffer); bf_glGenVertexArrays(1, &glRenderDevice->mGLVAO); bf_glBindVertexArray(glRenderDevice->mGLVAO); - bf_glGenBuffers(1, &glRenderDevice->mGLVertexBuffer); - bf_glGenBuffers(1, &glRenderDevice->mGLIndexBuffer); + bf_glEnableVertexAttribArray(curShader->mAttribPosition); + bf_glVertexAttribPointer(curShader->mAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(DefaultVertex3D), (void*)offsetof(DefaultVertex3D, x)); + bf_glEnableVertexAttribArray(curShader->mAttribTexCoord0); + bf_glVertexAttribPointer(curShader->mAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(DefaultVertex3D), (void*)offsetof(DefaultVertex3D, u)); + bf_glEnableVertexAttribArray(curShader->mAttribColor); + bf_glVertexAttribPointer(curShader->mAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(DefaultVertex3D), (void*)offsetof(DefaultVertex3D, color)); } - - auto glVertices = (GLVertex3D*)mVertices; + bf_glBindVertexArray(glRenderDevice->mGLVAO); + auto glVertices = (DefaultVertex3D*)mVertices; bf_glBindBuffer(GL_ARRAY_BUFFER, glRenderDevice->mGLVertexBuffer); - bf_glBufferData(GL_ARRAY_BUFFER, mVtxIdx * sizeof(GLVertex3D), mVertices, GL_STREAM_DRAW); - - bf_glEnableVertexAttribArray(curShader->mAttribPosition); - bf_glVertexAttribPointer(curShader->mAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLVertex3D), (void*)offsetof(GLVertex3D, x)); - bf_glEnableVertexAttribArray(curShader->mAttribTexCoord0); - bf_glVertexAttribPointer(curShader->mAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex3D), (void*)offsetof(GLVertex3D, u)); - bf_glEnableVertexAttribArray(curShader->mAttribColor); - bf_glVertexAttribPointer(curShader->mAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GLVertex3D), (void*)offsetof(GLVertex3D, color)); + bf_glBufferData(GL_ARRAY_BUFFER, mVtxIdx * sizeof(DefaultVertex3D), mVertices, GL_STREAM_DRAW); + bf_glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glRenderDevice->mGLIndexBuffer); + bf_glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIdxIdx * sizeof(int16), mIndices, GL_STREAM_DRAW); if (mRenderState != renderDevice->mPhysRenderState) renderDevice->PhysSetRenderState(mRenderState); - bf_glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glRenderDevice->mGLIndexBuffer); - bf_glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIdxIdx * sizeof(int16), mIndices, GL_STREAM_DRAW); bf_glDrawElements(GL_TRIANGLES, mIdxIdx, GL_UNSIGNED_SHORT, NULL); - - bf_glBufferData(GL_ELEMENT_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW); - bf_glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW); } GLDrawLayer::GLDrawLayer() @@ -339,16 +436,11 @@ static void BFGetGLProc(T& proc, const char* name) #define BF_GET_GLPROC(name) BFGetGLProc(bf_##name, #name) -void GL_DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) -{ - NOP; -} - GLRenderWindow::GLRenderWindow(GLRenderDevice* renderDevice, SDL_Window* sdlWindow) { if (bf_glGenBuffers == NULL) { - BF_GET_GLPROC(glDebugMessageCallback); + BF_GET_GLPROC(glGetError); BF_GET_GLPROC(glActiveTexture); BF_GET_GLPROC(glGenVertexArrays); BF_GET_GLPROC(glBindVertexArray); @@ -409,11 +501,49 @@ GLRenderWindow::GLRenderWindow(GLRenderDevice* renderDevice, SDL_Window* sdlWind BF_GET_GLPROC(glGetObjectParameterivARB); BF_GET_GLPROC(glCompressedTexImage2D); BF_GET_GLPROC(glClientActiveTexture); + BF_GET_GLPROC(glGenTextures); + BF_GET_GLPROC(glBindTexture); + BF_GET_GLPROC(glPixelStorei); + BF_GET_GLPROC(glTexImage2D); + BF_GET_GLPROC(glTexParameteri); + BF_GET_GLPROC(glDisable); + BF_GET_GLPROC(glEnable); + BF_GET_GLPROC(glCullFace); + BF_GET_GLPROC(glDepthMask); + BF_GET_GLPROC(glPolygonMode); + BF_GET_GLPROC(glDepthFunc); + BF_GET_GLPROC(glBlendFunc); + BF_GET_GLPROC(glScissor); + BF_GET_GLPROC(glViewport); + BF_GET_GLPROC(glFramebufferTexture); + BF_GET_GLPROC(glDrawBuffers); + BF_GET_GLPROC(glCheckFramebufferStatus); + BF_GET_GLPROC(glTexSubImage2D); + BF_GET_GLPROC(glBindFramebuffer); + BF_GET_GLPROC(glClearColor); + BF_GET_GLPROC(glClearDepth); + BF_GET_GLPROC(glClear); #if !defined BF_PLATFORM_OPENGL_ES2 BF_GET_GLPROC(glGetVertexAttribdv); #endif - } + +#if defined(_DEBUG) && !defined(BF_PLATFORM_OPENGL_ES2) + + BF_GET_GLPROC(glDebugMessageCallback); + if (bf_glDebugMessageCallback != NULL) + { + bf_glEnable(GL_DEBUG_OUTPUT); + bf_glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + bf_glDebugMessageCallback(&DebugMessageCallback, NULL); + } +#endif + + } + +#ifndef BF_PLATFORM_OPENGL_ES2 + //glEnableClientState(GL_INDEX_ARRAY); +#endif mSDLWindow = sdlWindow; mRenderDevice = renderDevice; @@ -429,12 +559,25 @@ GLRenderWindow::~GLRenderWindow() void GLRenderWindow::PhysSetAsTarget() { - bf_SDL_GL_MakeCurrent(mSDLWindow, ((SdlBFApp*)gBFApp)->mGLContext ); + bf_SDL_GL_MakeCurrent(mSDLWindow, ((SdlBFApp*)gBFApp)->mGLContext); + + bf_glEnable(GL_BLEND); + bf_glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - GLfloat matrix[4][4]; - CreateOrthographicOffCenter(0.0f, (float)mWidth, (float)mHeight, 0.0f, -100.0f, 100.0f, matrix); - glViewport(0, 0, (GLsizei)mWidth, (GLsizei)mHeight); + if (mResizePending) + { + Resized(); + } + bf_glViewport(0, 0, (GLsizei)mWidth, (GLsizei)mHeight); + bf_glScissor(0, 0, (GLsizei)mWidth, (GLsizei)mHeight); + + if (!mHasBeenDrawnTo) + { + bf_glClearColor(0, 0, 0, 0); + bf_glClearDepth(1); + bf_glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } mHasBeenDrawnTo = true; } @@ -443,7 +586,6 @@ void GLRenderWindow::SetAsTarget() //TODO: Handle this more elegantly when we actually handle draw layers properly... //if (mRenderDevice->mCurRenderTarget != NULL) //mRenderDevice->mCurDrawLayer->Flush(); - bf_SDL_GL_MakeCurrent(mSDLWindow, ((SdlBFApp*)gBFApp)->mGLContext ); mHasBeenTargeted = true; mRenderDevice->mCurRenderTarget = this; @@ -454,70 +596,38 @@ void GLRenderWindow::Resized() mRenderDevice->mResizeCount++; mResizeNum = mRenderDevice->mResizeCount; - bf_SDL_GetWindowSize(mSDLWindow, &mWidth, &mHeight); - - //NOT_IMPL; - /*if (mGLSwapChain != NULL) - { - mGLRenderTargetView->Release(); - mGLBackBuffer->Release(); - GLCHECK(mGLSwapChain->ResizeBuffers(1, mWidth, mHeight, GLGI_FORMAT_R8G8B8A8_UNORM, 0)); - - GLCHECK(mGLSwapChain->GetBuffer(0, __uuidof(IGL10Texture2D), (LPVOID*)&mGLBackBuffer)); - GLCHECK(mRenderDevice->mGLDevice->CreateRenderTargetView(mGLBackBuffer, NULL, &mGLRenderTargetView)); - }*/ + bf_SDL_GetWindowSizeInPixels(mSDLWindow, &mWidth, &mHeight); + mResizePending = false; } void GLRenderWindow::Present() { bf_SDL_GL_SwapWindow(mSDLWindow); - //GLCHECK(mGLSwapChain->Present((mWindow->mFlags & BFWINDOW_VSYNC) ? 1 : 0, 0)); } void GLRenderWindow::CopyBitsTo(uint32* dest, int width, int height) { mCurDrawLayer->Flush(); - NOT_IMPL; - /*GL10_TEXTURE2D_DESC texDesc; - texDesc.ArraySize = 1; - texDesc.BindFlags = 0; - texDesc.CPUAccessFlags = 0; - texDesc.Format = GLGI_FORMAT_R8G8B8A8_UNORM; - texDesc.Width = width; - texDesc.Height = height; - texDesc.MipLevels = 1; - texDesc.MiscFlags = 0; - texDesc.SampleDesc.Count = 1; - texDesc.SampleDesc.Quality = 0; - texDesc.Usage = GL10_USAGE_STAGING; - texDesc.CPUAccessFlags = GL10_CPU_ACCESS_READ; - - IGL10Texture2D *texture; - GLCHECK(mRenderDevice->mGLDevice->CreateTexture2D(&texDesc, 0, &texture)); - mRenderDevice->mGLDevice->CopyResource(texture, mGLBackBuffer); - - GL10_MAPPED_TEXTURE2D mapTex; - GLCHECK(texture->Map(GL10CalcSubresource(0, 0, 1), GL10_MAP_READ, 0, &mapTex)); - uint8* srcPtr = (uint8*) mapTex.pData; - uint8* destPtr = (uint8*) dest; - for (int y = 0; y < height; y++) - { - memcpy(destPtr, srcPtr, width*sizeof(uint32)); - srcPtr += mapTex.RowPitch; - destPtr += width * 4; - } - texture->Unmap(0); - texture->Release();*/ +} + +float GLRenderWindow::GetRefreshRate() +{ + SDL_DisplayID displayID = bf_SDL_GetDisplayForWindow(mSDLWindow); + const SDL_DisplayMode *mode = bf_SDL_GetCurrentDisplayMode(displayID); + + if (mode != NULL) + return mode->refresh_rate; + + return RenderWindow::GetRefreshRate(); } /// GLRenderDevice::GLRenderDevice() { - //mGLDevice = NULL; mCurShader = NULL; - mGLVAO = 0; + mGLVAO = 0; mGLVertexBuffer = 0; mGLIndexBuffer = 0; mBlankTexture = 0; @@ -532,84 +642,22 @@ bool GLRenderDevice::Init(BFApp* app) { SdlBFApp* winApp = (SdlBFApp*) app; - //RenderState* glRenderState; - if (mDefaultRenderState == NULL) - { - auto dxRenderState = (RenderState*)CreateRenderState(NULL); - - mDefaultRenderState = dxRenderState; - mDefaultRenderState->mDepthFunc = DepthFunc_Less; - mDefaultRenderState->mWriteDepthBuffer = true; - - mPhysRenderState = mDefaultRenderState; - } - else - { - //glRenderState = (DXRenderState*)mDefaultRenderState; - //glRenderState->ReinitNative(); - } - - /// - - ////Use GL10_CREATE_DEVICE_DEBUG for PIX - //GLCHECK(GL10CreateDevice(NULL, GL10_DRIVER_TYPE_HARDWARE, NULL, GL10_CREATE_DEVICE_DEBUG, GL10_SDK_VERSION, &mGLDevice)); - ////GLCHECK(GL10CreateDevice(NULL, GL10_DRIVER_TYPE_HARDWARE, NULL, 0, GL10_SDK_VERSION, &mGLDevice)); - - //IGLGIDevice* pGLGIDevice = NULL; - //GLCHECK(mGLDevice->QueryInterface(__uuidof(IGLGIDevice), reinterpret_cast(&pGLGIDevice))); - - //IGLGIAdapter* pGLGIAdapter = NULL; - //GLCHECK(pGLGIDevice->GetParent(__uuidof(IGLGIAdapter), reinterpret_cast(&pGLGIAdapter))); - - //IGLGIFactory* pGLGIFactory = NULL; - //GLCHECK(pGLGIAdapter->GetParent(__uuidof(IGLGIFactory), reinterpret_cast(&mGLGIFactory))); - - ////set rasterizer - //GL10_RASTERIZER_DESC rasterizerState; - //rasterizerState.CullMode = GL10_CULL_NONE; - //rasterizerState.FillMode = GL10_FILL_SOLID; - //rasterizerState.FrontCounterClockwise = true; - // rasterizerState.DepthBias = false; - // rasterizerState.DepthBiasClamp = 0; - // rasterizerState.SlopeScaledDepthBias = 0; - // rasterizerState.DepthClipEnable = false; - // rasterizerState.ScissorEnable = true; - // //TODO:rasterizerState.MultisampleEnable = false; - //rasterizerState.MultisampleEnable = true; - // rasterizerState.AntialiasedLineEnable = true; - // - //mGLDevice->CreateRasterizerState( &rasterizerState, &mGLRasterizerStateClipped); - // - //rasterizerState.ScissorEnable = false; - //mGLDevice->CreateRasterizerState( &rasterizerState, &mGLRasterizerStateUnclipped); - //mGLDevice->RSSetState(mGLRasterizerStateUnclipped); - // - //IGL10BlendState* g_pBlendState = NULL; - - //GL10_BLEND_DESC BlendState; - //ZeroMemory(&BlendState, sizeof(GL10_BLEND_DESC)); - //BlendState.BlendEnable[0] = TRUE; - ////BlendState.SrcBlend = GL10_BLEND_SRC_ALPHA; - //BlendState.SrcBlend = GL10_BLEND_ONE; + BF_ASSERT(mDefaultRenderState == NULL); + auto renderState = (RenderState*)CreateRenderState(NULL); - //BlendState.DestBlend = GL10_BLEND_INV_SRC_ALPHA; - //BlendState.BlendOp = GL10_BLEND_OP_ADD; - //BlendState.SrcBlendAlpha = GL10_BLEND_ONE; - //BlendState.DestBlendAlpha = GL10_BLEND_ONE; - //BlendState.BlendOpAlpha = GL10_BLEND_OP_ADD; - //BlendState.RenderTargetWriteMask[0] = GL10_COLOR_WRITE_ENABLE_ALL; - //mGLDevice->CreateBlendState(&BlendState, &mGLNormalBlendState); - - //BlendState.DestBlend = GL10_BLEND_ONE; - //mGLDevice->CreateBlendState(&BlendState, &mGLAdditiveBlendState); - - //PhysSetAdditive(false); + mDefaultRenderState = renderState; + mDefaultRenderState->mDepthFunc = DepthFunc_Less; + mDefaultRenderState->mWriteDepthBuffer = true; + mPhysRenderState = mDefaultRenderState; return true; } void GLRenderDevice::FrameStart() { + if (mRenderWindowList.IsEmpty()) + return; + mCurRenderTarget = NULL; mPhysRenderWindow = NULL; @@ -618,6 +666,10 @@ void GLRenderDevice::FrameStart() aRenderWindow->mHasBeenDrawnTo = false; aRenderWindow->mHasBeenTargeted = false; } + + bf_glDisable(GL_SCISSOR_TEST); + bf_glDisable(GL_CULL_FACE); + bf_glDisable(GL_DEPTH_TEST); } void GLRenderDevice::FrameEnd() @@ -661,7 +713,7 @@ Shader* GLRenderDevice::LoadShader(const StringImpl& fileName, VertexDefinition* { GLShader* glShader = new GLShader(); - glShader->mVertexSize = sizeof(GLVertex3D); + glShader->mVertexSize = sizeof(DefaultVertex3D); glShader->mGLVertexShader = bf_glCreateShader(GL_VERTEX_SHADER); glShader->mGLFragmentShader = bf_glCreateShader(GL_FRAGMENT_SHADER); @@ -716,6 +768,7 @@ Shader* GLRenderDevice::LoadShader(const StringImpl& fileName, VertexDefinition* glShader->mAttribColor = bf_glGetAttribLocation(glShader->mGLProgram, "color"); glShader->mAttribTex0 = bf_glGetUniformLocation(glShader->mGLProgram, "tex"); glShader->mAttribTex1 = bf_glGetUniformLocation(glShader->mGLProgram, "tex2"); + glShader->mScreenUniformLoc = bf_glGetUniformLocation(glShader->mGLProgram, "screenMatrix"); return glShader; } @@ -726,29 +779,90 @@ void GLRenderDevice::PhysSetRenderState(RenderState* renderState) if (mCurShader != NULL) { bf_glUseProgram(mCurShader->mGLProgram); - - GLRenderDevice* aRenderDevice = (GLRenderDevice*)gBFApp->mRenderDevice; - - //TODO: Cache more - - GLfloat matrix[4][4]; - CreateOrthographicOffCenter(0.0f, (float)mPhysRenderWindow->mWidth, (float)mPhysRenderWindow->mHeight, 0.0f, -100.0f, 100.0f, matrix); - GLint matrixLoc = bf_glGetUniformLocation(mCurShader->mGLProgram, "screenMatrix"); - //BF_ASSERT(matrixLoc >= 0); - if (matrixLoc >= 0) - bf_glUniformMatrix4fv(matrixLoc, 1, false, (float*)matrix); + if (mCurShader->mScreenUniformLoc >= 0) + { + GLfloat matrix[4][4]; + CreateOrthographicOffCenter(0.0f, (float)mPhysRenderWindow->mWidth, (float)mPhysRenderWindow->mHeight, 0.0f, -100.0f, 100.0f, matrix); + bf_glUniformMatrix4fv(mCurShader->mScreenUniformLoc, 1, false, (float*)matrix); + } } if (renderState->mClipped) { - glEnable(GL_SCISSOR_TEST); - glScissor((GLsizei)renderState->mClipRect.x, - mPhysRenderWindow->mHeight - (GLsizei)renderState->mClipRect.y - (GLsizei)renderState->mClipRect.height, - (GLsizei)renderState->mClipRect.width, (GLsizei)renderState->mClipRect.height); + bf_glEnable(GL_SCISSOR_TEST); + bf_glScissor((GLsizei)renderState->mClipRect.x, + mPhysRenderWindow->mHeight - (GLsizei)renderState->mClipRect.y - (GLsizei)renderState->mClipRect.height, + (GLsizei)renderState->mClipRect.width, (GLsizei)renderState->mClipRect.height); } else { - glDisable(GL_SCISSOR_TEST); + bf_glDisable(GL_SCISSOR_TEST); + } + + if (renderState->mCullMode != mPhysRenderState->mCullMode) + { + switch (renderState->mCullMode) + { + case CullMode_None: + bf_glDisable(GL_CULL_FACE); + break; + case CullMode_Front: + bf_glEnable(GL_CULL_FACE); + bf_glCullFace(GL_FRONT); + break; + case CullMode_Back: + bf_glEnable(GL_CULL_FACE); + bf_glCullFace(GL_BACK); + break; + } + } + + if (renderState->mDepthFunc != mPhysRenderState->mDepthFunc) + { + bf_glDepthMask(renderState->mWriteDepthBuffer ? GL_TRUE : GL_FALSE); + + switch (renderState->mDepthFunc) + { + case DepthFunc_Never: + bf_glDisable(GL_DEPTH_TEST); + break; + case DepthFunc_Less: + bf_glDepthFunc(GL_LESS); + bf_glEnable(GL_DEPTH_TEST); + break; + case DepthFunc_LessEqual: + bf_glDepthFunc(GL_LEQUAL); + bf_glEnable(GL_DEPTH_TEST); + break; + case DepthFunc_Equal: + bf_glDepthFunc(GL_EQUAL); + bf_glEnable(GL_DEPTH_TEST); + break; + case DepthFunc_Greater: + bf_glDepthFunc(GL_GREATER); + bf_glEnable(GL_DEPTH_TEST); + break; + case DepthFunc_NotEqual: + bf_glDepthFunc(GL_NOTEQUAL); + bf_glEnable(GL_DEPTH_TEST); + break; + case DepthFunc_GreaterEqual: + bf_glDepthFunc(GL_GEQUAL); + bf_glEnable(GL_DEPTH_TEST); + break; + case DepthFunc_Always: + bf_glDepthFunc(GL_ALWAYS); + bf_glEnable(GL_DEPTH_TEST); + break; + } + } + + if ((renderState->mWireframe != mPhysRenderState->mWireframe)) + { + if (renderState->mWireframe) + bf_glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + else + bf_glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } mPhysRenderState = renderState; @@ -756,15 +870,56 @@ void GLRenderDevice::PhysSetRenderState(RenderState* renderState) Texture* GLRenderDevice::CreateRenderTarget(int width, int height, bool destAlpha) { + // GLTexture* texture = new GLTexture(); + // GLuint id; + // bf_glGenTextures(1, &id); + // bf_glBindTexture(GL_TEXTURE_2D, id); + // bf_glTexImage2D(GL_TEXTURE_2D, 0, (destAlpha ? GL_RGBA : GL_RGB), width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + // bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + // bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + // + // GLuint depthrenderbuffer; + // bf_glGenRenderbuffers(1, &depthrenderbuffer); + // bf_glBindRenderbuffer(GL_RENDERBUFFER, depthrenderbuffer); + // bf_glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); + // bf_glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthrenderbuffer); + // texture->mGLTexture = id; + // texture->mGLTexture2 = depthrenderbuffer; + // texture->AddRef(); + // return texture; NOT_IMPL; } +Texture* GLRenderDevice::CreateDynTexture(int width, int height) +{ + auto context = bf_SDL_GL_GetCurrentContext(); + auto err = bf_SDL_GetError(); + GLuint texture = 0; + bf_glGenTextures(1, &texture); + auto result = bf_glGetError(); + + bf_glBindTexture(GL_TEXTURE_2D, texture); + bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + bf_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + GLTexture* glTexture = new GLTexture(); + glTexture->mGLTexture = texture; + glTexture->mGLTexture2 = 0; + glTexture->mRenderDevice = this; + glTexture->mWidth = width; + glTexture->mHeight = height; + glTexture->AddRef(); + return glTexture; +} + void GLRenderDevice::SetRenderState(RenderState* renderState) { mCurRenderState = renderState; } - void GLSetTextureCmd::Render(RenderDevice* renderDevice, RenderWindow* renderWindow) { /*#ifdef BF_PLATFORM_OPENGL_ES2 @@ -790,12 +945,12 @@ void GLSetTextureCmd::Render(RenderDevice* renderDevice, RenderWindow* renderWin if (glRenderDevice->mBlankTexture == 0) { - glGenTextures(1, &glRenderDevice->mBlankTexture); - glBindTexture(GL_TEXTURE_2D, glRenderDevice->mBlankTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + bf_glGenTextures(1, &glRenderDevice->mBlankTexture); + bf_glBindTexture(GL_TEXTURE_2D, glRenderDevice->mBlankTexture); + bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); /*if (bf_glCompressedTexImage2D != NULL) { @@ -806,10 +961,10 @@ void GLSetTextureCmd::Render(RenderDevice* renderDevice, RenderWindow* renderWin else*/ { uint16 color = 0; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, + bf_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, &color); } - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + bf_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); } if (glTexture->mImageData != NULL) @@ -822,12 +977,12 @@ void GLSetTextureCmd::Render(RenderDevice* renderDevice, RenderWindow* renderWin for (int texNum = 0; texNum < 2/*texCount*/; texNum++) { GLuint glTextureID; - glGenTextures(1, &glTextureID); - glBindTexture(GL_TEXTURE_2D, glTextureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + bf_glGenTextures(1, &glTextureID); + bf_glBindTexture(GL_TEXTURE_2D, glTextureID); + bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + bf_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); //if (imageData->mHWBits != NULL) //{ @@ -842,11 +997,11 @@ void GLSetTextureCmd::Render(RenderDevice* renderDevice, RenderWindow* renderWin //} //else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glTexture->mImageData->mWidth, glTexture->mImageData->mHeight, 0, + bf_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glTexture->mImageData->mWidth, glTexture->mImageData->mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, glTexture->mImageData->mBits); } - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + bf_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); if (texNum == 0) glTexture->mGLTexture = glTextureID; @@ -860,5 +1015,5 @@ void GLSetTextureCmd::Render(RenderDevice* renderDevice, RenderWindow* renderWin bf_glActiveTexture(GL_TEXTURE0 + mTextureIdx); //glUniform1i(curShader->mAttribTex0, 0); - glBindTexture(GL_TEXTURE_2D, glTexture->mGLTexture); + bf_glBindTexture(GL_TEXTURE_2D, glTexture->mGLTexture); } diff --git a/BeefySysLib/platform/sdl/GLRenderDevice.h b/BeefySysLib/platform/sdl/GLRenderDevice.h index db072d50f..76219638b 100644 --- a/BeefySysLib/platform/sdl/GLRenderDevice.h +++ b/BeefySysLib/platform/sdl/GLRenderDevice.h @@ -28,14 +28,14 @@ class GLTexture : public Texture GLuint mGLTexture; GLuint mGLTexture2; ImageData* mImageData; - //IGL10RenderTargetView* mGLRenderTargetView; public: GLTexture(); - ~GLTexture(); + virtual ~GLTexture(); - virtual void PhysSetAsTarget(); + virtual void PhysSetAsTarget() override; virtual void Blt(ImageData* imageData, int x, int y) override; + virtual void SetBits(int destX, int destY, int destWidth, int destHeight, int srcPitch, uint32 *bits) override; }; class GLShaderParam : public ShaderParam @@ -47,7 +47,7 @@ class GLShaderParam : public ShaderParam GLShaderParam(); ~GLShaderParam(); - virtual void SetTexture(Texture* texture); + virtual void SetTexture(Texture* texture) override; virtual void SetFloat4(float x, float y, float z, float w) override; }; @@ -56,18 +56,17 @@ typedef Dictionary GLShaderParamMap; class GLShader : public Shader { public: - //IGL10Effect* mGLEffect; - - GLuint mGLVertexShader; - GLuint mGLFragmentShader; - GLuint mGLProgram; - GLint mAttribPosition; - GLint mAttribTexCoord0; - GLint mAttribColor; - GLint mAttribTex0; - GLint mAttribTex1; + GLuint mGLVertexShader; + GLuint mGLFragmentShader; + GLuint mGLProgram; + GLint mAttribPosition; + GLint mAttribTexCoord0; + GLint mAttribColor; + GLint mAttribTex0; + GLint mAttribTex1; + GLint mScreenUniformLoc; - GLShaderParamMap mParamsMap; + GLShaderParamMap mParamsMap; public: GLShader(); @@ -90,7 +89,7 @@ class GLSetTextureCmd : public RenderCmd class GLDrawBatch : public DrawBatch { public: - //IGL10Buffer* mGLBuffer; + public: GLDrawBatch(); @@ -126,13 +125,14 @@ class GLRenderWindow : public RenderWindow public: GLRenderWindow(GLRenderDevice* renderDevice, SDL_Window* sdlWindow); - ~GLRenderWindow(); + virtual ~GLRenderWindow(); void SetAsTarget() override; void Resized() override; - virtual void Present() override; + void Present() override; void CopyBitsTo(uint32* dest, int width, int height); + float GetRefreshRate() override; }; class GLRenderDevice : public RenderDevice @@ -167,7 +167,7 @@ class GLRenderDevice : public RenderDevice Shader* LoadShader(const StringImpl& fileName, VertexDefinition* vertexDefinition) override; Texture* CreateRenderTarget(int width, int height, bool destAlpha) override; - virtual Texture* CreateDynTexture(int width, int height) override { return NULL; } + virtual Texture* CreateDynTexture(int width, int height) override; virtual void SetRenderState(RenderState* renderState) override; /*void SetShader(Shader* shader) override; diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index 90c62da26..428517278 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -37,6 +37,8 @@ void (SDLCALL* bf_SDL_memset)(void* dest, int c, size_t len); void* (SDLCALL* bf_SDL_memcpy)(void *dst, const void *src, size_t len); SDL_PropertiesID (SDLCALL* bf_SDL_CreateProperties)(void); +void (SDLCALL* bf_SDL_DestroyProperties)(SDL_PropertiesID props); + bool (SDLCALL* bf_SDL_SetNumberProperty)(SDL_PropertiesID props, const char* name, int64_t value); bool (SDLCALL* bf_SDL_SetBooleanProperty)(SDL_PropertiesID props, const char* name, bool value); bool (SDLCALL* bf_SDL_SetStringProperty)(SDL_PropertiesID props, const char* name, const char* value); @@ -45,8 +47,10 @@ bool (SDLCALL* bf_SDL_SetPointerProperty)(SDL_PropertiesID props, const char *na SDL_Window* (SDLCALL* bf_SDL_CreateWindowWithProperties)(SDL_PropertiesID props); SDL_WindowID (SDLCALL* bf_SDL_GetWindowID)(SDL_Window* window); void (SDLCALL* bf_SDL_DestroyWindow)(SDL_Window* window); +bool (SDLCALL* bf_SDL_SyncWindow)(SDL_Window* window); bool (SDLCALL* bf_SDL_GetWindowPosition)(SDL_Window* window,int* x, int* y); bool (SDLCALL* bf_SDL_SetWindowPosition)(SDL_Window* window, int x, int y); +bool (SDLCALL* bf_SDL_GetWindowSizeInPixels)(SDL_Window* window, int* w, int* h); bool (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); bool (SDLCALL* bf_SDL_SetWindowSize)(SDL_Window* window, int w, int h); bool (SDLCALL* bf_SDL_SetWindowMinimumSize)(SDL_Window* window, int min_w, int min_h); @@ -78,10 +82,14 @@ SDL_DisplayID* (SDLCALL* bf_SDL_GetDisplays)(int* count); bool (SDLCALL* bf_SDL_GetDisplayBounds)(SDL_DisplayID displayID, SDL_Rect* rect); SDL_DisplayMode* (SDLCALL* bf_SDL_GetDesktopDisplayMode)(SDL_DisplayID displayID); bool (SDLCALL* bf_SDL_HasRectIntersection)(const SDL_Rect* A, const SDL_Rect* B); +SDL_DisplayID (SDLCALL* bf_SDL_GetDisplayForWindow)(SDL_Window *window); +const SDL_DisplayMode* (SDLCALL* bf_SDL_GetCurrentDisplayMode)(SDL_DisplayID displayID); SDL_Surface* (SDLCALL* bf_SDL_CreateSurfaceFrom)(int width, int height, SDL_PixelFormat format, void *pixels, int pitch); SDL_GLContext (SDLCALL* bf_SDL_GL_CreateContext)(SDL_Window* window); +SDL_GLContext (SDLCALL* bf_SDL_GL_GetCurrentContext)(); +SDL_Window* (SDLCALL* bf_SDL_GL_GetCurrentWindow)(); bool (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); bool (SDLCALL* bf_SDL_GL_SetAttribute)(SDL_GLAttr attr, int value); void* (SDLCALL* bf_SDL_GL_GetProcAddress)(const char* proc); @@ -90,6 +98,13 @@ bool (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); static SDL_Cursor* (SDLCALL* bf_SDL_GetDefaultCursor)(); static SDL_Cursor* (SDLCALL* bf_SDL_CreateSystemCursor)(SDL_SystemCursor id); static bool (SDLCALL* bf_SDL_SetCursor)(SDL_Cursor* cursor); +static SDL_WindowFlags (SDLCALL* bf_SDL_GetWindowFlags)(SDL_Window *window); +static bool (SDLCALL* bf_SDL_HideWindow)(SDL_Window *window); +static bool (SDLCALL* bf_SDL_RestoreWindow)(SDL_Window *window); +static bool (SDLCALL* bf_SDL_MinimizeWindow)(SDL_Window *window); +static bool (SDLCALL* bf_SDL_MaximizeWindow)(SDL_Window *window); +static bool (SDLCALL* bf_SDL_ShowWindow)(SDL_Window *window); +static bool (SDLCALL* bf_SDL_RaiseWindow)(SDL_Window* window); struct AdjustedMonRect { @@ -161,21 +176,21 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y SDL_PropertiesID props = bf_SDL_CreateProperties(); bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); - bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, (windowFlags & BFWINDOW_RESIZABLE) > 0); - bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, (windowFlags & BFWINDOW_FULLSCREEN) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, (windowFlags & BFWINDOW_RESIZABLE) != 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, (windowFlags & BFWINDOW_FULLSCREEN) != 0); bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN, (windowFlags & BFWINDOW_BORDER) == 0); - bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN, (windowFlags & BFWINDOW_TOOLTIP) > 0); - bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN, (windowFlags & BFWINDOW_DEST_ALPHA) > 0); - bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN, (windowFlags & BFWINDOW_FAKEFOCUS) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN, (windowFlags & BFWINDOW_TOOLTIP) != 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN, (windowFlags & BFWINDOW_DEST_ALPHA) != 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN, (windowFlags & BFWINDOW_FAKEFOCUS) != 0); bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN, (windowFlags & BFWINDOW_FAKEFOCUS) == 0); - bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN, (windowFlags & BFWINDOW_MODAL) > 0); - bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN, (windowFlags & BFWINDOW_TOPMOST) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN, (windowFlags & BFWINDOW_MODAL) != 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN, (windowFlags & BFWINDOW_TOPMOST) != 0); if (parent != NULL) { SDL_Window* parentWindow = ((SdlBFWindow*)parent)->mSDLWindow; bf_SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, parentWindow); - if ((windowFlags & BFWINDOW_TOOLTIP | BFWINDOW_FAKEFOCUS) > 0) // Tooltips and menus have relative positioning to their parent. + if ((windowFlags & BFWINDOW_TOOLTIP | BFWINDOW_FAKEFOCUS) != 0) // Tooltips and menus have relative positioning to their parent. { int parentX, parentY; bf_SDL_GetWindowPosition(parentWindow, &parentX, &parentY); @@ -197,6 +212,7 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y mSDLWindow = bf_SDL_CreateWindowWithProperties(props); + bf_SDL_DestroyProperties(props); // printf("Created %i : %s\n", bf_SDL_GetWindowID(mSDLWindow), title.c_str()); if (gAppIconSurface) @@ -204,14 +220,22 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y bf_SDL_StartTextInput(mSDLWindow); #ifndef BF_PLATFORM_OPENGL_ES2 + int contextFlags = SDL_GL_CONTEXT_PROFILE_CORE; +#ifdef _DEBUG + contextFlags |= SDL_GL_CONTEXT_DEBUG_FLAG; +#endif + bf_SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); bf_SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); - bf_SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + bf_SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, contextFlags); #endif - if(((SdlBFApp*)gBFApp)->mGLContext == NULL) + auto sdlApp = (SdlBFApp*)gBFApp; + if(sdlApp->mGLContext == NULL) { - if (!(((SdlBFApp*)gBFApp)->mGLContext = bf_SDL_GL_CreateContext(mSDLWindow))) + sdlApp->mGLContextWindow = mSDLWindow; + sdlApp->mGLContext = bf_SDL_GL_CreateContext(mSDLWindow); + if (sdlApp->mGLContext == NULL) { String str = StrFormat( #ifdef BF_PLATFORM_OPENGL_ES2 @@ -228,18 +252,6 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y } } -#ifndef BF_PLATFORM_OPENGL_ES2 - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); -#endif - - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - -#ifndef BF_PLATFORM_OPENGL_ES2 - //glEnableClientState(GL_INDEX_ARRAY); -#endif - mIsMouseInside = false; mIsMouseVisible = true; mHasPositionInit = false; @@ -265,8 +277,17 @@ void SdlBFWindow::Destroy() SdlBFApp* app = (SdlBFApp*)gBFApp; app->mSdlWindowMap.Remove(bf_SDL_GetWindowID(mSDLWindow)); + SDL_Window* currentContextWindow = bf_SDL_GL_GetCurrentWindow(); + bf_SDL_StopTextInput(mSDLWindow); bf_SDL_DestroyWindow(mSDLWindow); + + // Make sure we have valid gl context in case we call any GL functions between frames + auto sdlApp = (SdlBFApp*)gBFApp; + if ((currentContextWindow == mSDLWindow) && (mSDLWindow != sdlApp->mGLContextWindow)) + { + bf_SDL_GL_MakeCurrent(sdlApp->mGLContextWindow, sdlApp->mGLContext); + } mSDLWindow = NULL; } @@ -418,6 +439,7 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_memcpy); BF_GET_SDLPROC(SDL_CreateProperties); + BF_GET_SDLPROC(SDL_DestroyProperties); BF_GET_SDLPROC(SDL_SetNumberProperty); BF_GET_SDLPROC(SDL_SetBooleanProperty); BF_GET_SDLPROC(SDL_SetStringProperty); @@ -426,8 +448,10 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_CreateWindowWithProperties); BF_GET_SDLPROC(SDL_GetWindowID); BF_GET_SDLPROC(SDL_DestroyWindow); + BF_GET_SDLPROC(SDL_SyncWindow); BF_GET_SDLPROC(SDL_GetWindowPosition); BF_GET_SDLPROC(SDL_SetWindowPosition); + BF_GET_SDLPROC(SDL_GetWindowSizeInPixels); BF_GET_SDLPROC(SDL_GetWindowSize); BF_GET_SDLPROC(SDL_SetWindowSize); BF_GET_SDLPROC(SDL_SetWindowMinimumSize); @@ -459,10 +483,14 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_GetDisplayBounds); BF_GET_SDLPROC(SDL_GetDesktopDisplayMode); BF_GET_SDLPROC(SDL_HasRectIntersection); + BF_GET_SDLPROC(SDL_GetDisplayForWindow); + BF_GET_SDLPROC(SDL_GetCurrentDisplayMode); BF_GET_SDLPROC(SDL_CreateSurfaceFrom); BF_GET_SDLPROC(SDL_GL_CreateContext); + BF_GET_SDLPROC(SDL_GL_GetCurrentContext); + BF_GET_SDLPROC(SDL_GL_GetCurrentWindow); BF_GET_SDLPROC(SDL_GL_MakeCurrent); BF_GET_SDLPROC(SDL_GL_SetAttribute); BF_GET_SDLPROC(SDL_GL_GetProcAddress); @@ -471,12 +499,21 @@ SdlBFApp::SdlBFApp() BF_GET_SDLPROC(SDL_GetDefaultCursor); BF_GET_SDLPROC(SDL_CreateSystemCursor); BF_GET_SDLPROC(SDL_SetCursor); + BF_GET_SDLPROC(SDL_GetWindowFlags); + BF_GET_SDLPROC(SDL_HideWindow); + BF_GET_SDLPROC(SDL_RestoreWindow); + BF_GET_SDLPROC(SDL_MinimizeWindow); + BF_GET_SDLPROC(SDL_MaximizeWindow); + BF_GET_SDLPROC(SDL_ShowWindow); + BF_GET_SDLPROC(SDL_RaiseWindow); } mDataDir = mInstallDir; if (bf_SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD) < 0) BF_FATAL(StrFormat("Unable to initialize SDL: %s", bf_SDL_GetError()).c_str()); + + bf_SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); } SdlBFApp::~SdlBFApp() @@ -491,174 +528,177 @@ SdlBFWindow* SdlBFApp::GetSdlWindowFromId(uint32 id) return window; } -void SdlBFApp::Init() -{ - mRunning = true; - mInMsgProc = false; - -// bf_SDL_SetHint(SDL_HINT_EVENT_LOGGING, "1"); - - mRenderDevice = new GLRenderDevice(); - mRenderDevice->Init(this); -} - -void SdlBFApp::Run() +void SdlBFApp::ProcessSDLEvents() { - while (mRunning) + SDL_Event sdlEvent; + while (bf_SDL_PollEvent(&sdlEvent)) { - SDL_Event sdlEvent; - while (true) + switch (sdlEvent.type) { - { - //Beefy::DebugTimeGuard suspendTimeGuard(30, "BFApp::Run1"); - if (!bf_SDL_PollEvent(&sdlEvent)) - break; - } - - //Beefy::DebugTimeGuard suspendTimeGuard(30, "BFApp::Run2"); - - switch (sdlEvent.type) + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: { - case SDL_EVENT_WINDOW_CLOSE_REQUESTED: - { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); - if(sdlBFWindow != NULL) - if (sdlBFWindow->mCloseQueryFunc(sdlBFWindow) != 0) - gBFApp->RemoveWindow(sdlBFWindow); - } - break; - case SDL_EVENT_WINDOW_FOCUS_GAINED: - { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); - if(sdlBFWindow != NULL) - sdlBFWindow->mGotFocusFunc(sdlBFWindow); - } - break; - case SDL_EVENT_WINDOW_FOCUS_LOST: - { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); - if(sdlBFWindow != NULL) - { - sdlBFWindow->mLostFocusFunc(sdlBFWindow); - mIsControlDown = false; - } - } - break; - case SDL_EVENT_MOUSE_BUTTON_UP: - { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); - if (sdlBFWindow != NULL) - sdlBFWindow->mMouseUpFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, bfMouseBtnOf[sdlEvent.button.button]); - } - break; - case SDL_EVENT_MOUSE_BUTTON_DOWN: - { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); - if (sdlBFWindow != NULL) - sdlBFWindow->mMouseDownFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, bfMouseBtnOf[sdlEvent.button.button], sdlEvent.button.clicks); - } - break; - case SDL_EVENT_MOUSE_MOTION: - { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); - if (sdlBFWindow != NULL) - sdlBFWindow->mMouseMoveFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y); - } - break; - case SDL_EVENT_MOUSE_WHEEL: - { - uint ucNumLines = 3; // Default - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.wheel.windowID); - if(sdlBFWindow != NULL) - sdlBFWindow->mMouseWheelFunc(sdlBFWindow, sdlEvent.wheel.mouse_x, sdlEvent.wheel.mouse_y, sdlEvent.wheel.x, sdlEvent.wheel.y * (float)ucNumLines); - } - break; - case SDL_EVENT_WINDOW_MOUSE_ENTER: - { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); - if(sdlBFWindow != NULL) - RefreshMouseVisibility(sdlBFWindow); - } - break; - case SDL_EVENT_WINDOW_DESTROYED: + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if(sdlBFWindow != NULL) + if (sdlBFWindow->mCloseQueryFunc(sdlBFWindow) != 0) + gBFApp->RemoveWindow(sdlBFWindow); + } + break; + case SDL_EVENT_WINDOW_FOCUS_GAINED: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if(sdlBFWindow != NULL) + sdlBFWindow->mGotFocusFunc(sdlBFWindow); + } + break; + case SDL_EVENT_WINDOW_FOCUS_LOST: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if(sdlBFWindow != NULL) { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(bf_SDL_GetWindowID(bf_SDL_GetMouseFocus())); - if(sdlBFWindow != NULL) - RefreshMouseVisibility(sdlBFWindow); + sdlBFWindow->mLostFocusFunc(sdlBFWindow); + mIsControlDown = false; } - break; - case SDL_EVENT_KEY_DOWN: + } + break; + case SDL_EVENT_MOUSE_BUTTON_UP: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); + if (sdlBFWindow != NULL) + sdlBFWindow->mMouseUpFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, bfMouseBtnOf[sdlEvent.button.button]); + } + break; + case SDL_EVENT_MOUSE_BUTTON_DOWN: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); + if (sdlBFWindow != NULL) + sdlBFWindow->mMouseDownFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, bfMouseBtnOf[sdlEvent.button.button], sdlEvent.button.clicks); + } + break; + case SDL_EVENT_MOUSE_MOTION: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); + if (sdlBFWindow != NULL) + sdlBFWindow->mMouseMoveFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y); + } + break; + case SDL_EVENT_MOUSE_WHEEL: + { + uint ucNumLines = 3; // Default + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.wheel.windowID); + if(sdlBFWindow != NULL) + sdlBFWindow->mMouseWheelFunc(sdlBFWindow, sdlEvent.wheel.mouse_x, sdlEvent.wheel.mouse_y, sdlEvent.wheel.x, sdlEvent.wheel.y * (float)ucNumLines); + } + break; + case SDL_EVENT_WINDOW_MOUSE_ENTER: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if(sdlBFWindow != NULL) + RefreshMouseVisibility(sdlBFWindow); + } + break; + case SDL_EVENT_WINDOW_DESTROYED: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(bf_SDL_GetWindowID(bf_SDL_GetMouseFocus())); + if(sdlBFWindow != NULL) + RefreshMouseVisibility(sdlBFWindow); + } + break; + case SDL_EVENT_KEY_DOWN: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); + if (sdlBFWindow != NULL) { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); - if (sdlBFWindow != NULL) + sdlBFWindow->mKeyDownFunc(sdlBFWindow, SDLConvertKeyCode(sdlEvent.key.key), sdlEvent.key.repeat); + switch (sdlEvent.key.key) // These keys are not handled by SDL_TEXTINPUT { - sdlBFWindow->mKeyDownFunc(sdlBFWindow, SDLConvertKeyCode(sdlEvent.key.key), sdlEvent.key.repeat); - switch (sdlEvent.key.key) // These keys are not handled by SDL_TEXTINPUT - { - case SDLK_RETURN: - sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\n'); - break; - case SDLK_BACKSPACE: - sdlBFWindow->mKeyCharFunc(sdlBFWindow, mIsControlDown ? '\x7F' : '\b'); - break; - case SDLK_TAB: - sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\t'); - break; - case SDLK_LCTRL: - mIsControlDown = true; - break; - - default:; - } + case SDLK_RETURN: + sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\n'); + break; + case SDLK_BACKSPACE: + sdlBFWindow->mKeyCharFunc(sdlBFWindow, mIsControlDown ? '\x7F' : '\b'); + break; + case SDLK_TAB: + sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\t'); + break; + case SDLK_LCTRL: + mIsControlDown = true; + break; + + default:; } } - break; - case SDL_EVENT_TEXT_INPUT: + } + break; + case SDL_EVENT_TEXT_INPUT: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.text.windowID); + if (sdlBFWindow != NULL) { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.text.windowID); - if (sdlBFWindow != NULL) - { - const auto wideString = Beefy::UTF8Decode(sdlEvent.text.text); - for (int i = 0; i < wideString.length(); i++) - sdlBFWindow->mKeyCharFunc(sdlBFWindow, wideString[i]); - } + const auto wideString = Beefy::UTF8Decode(sdlEvent.text.text); + for (int i = 0; i < wideString.length(); i++) + sdlBFWindow->mKeyCharFunc(sdlBFWindow, wideString[i]); } - break; - case SDL_EVENT_KEY_UP: - { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); - if (sdlBFWindow != NULL) - sdlBFWindow->mKeyUpFunc(sdlBFWindow, SDLConvertKeyCode(sdlEvent.key.key)); + } + break; + case SDL_EVENT_KEY_UP: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); + if (sdlBFWindow != NULL) + sdlBFWindow->mKeyUpFunc(sdlBFWindow, SDLConvertKeyCode(sdlEvent.key.key)); - if (sdlEvent.key.key == SDLK_LCTRL) - mIsControlDown = false; - } - break; - case SDL_EVENT_WINDOW_MOVED: -// case SDL_EVENT_WINDOW_RESIZED: - case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + if (sdlEvent.key.key == SDLK_LCTRL) + mIsControlDown = false; + } + break; + case SDL_EVENT_WINDOW_MOVED: + case SDL_EVENT_WINDOW_RESIZED: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if (sdlBFWindow != NULL) { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); - if (sdlBFWindow != NULL) - { - if (sdlBFWindow->mHasPositionInit) - { - if (sdlEvent.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) - { - sdlBFWindow->mRenderWindow->Resized(); - } - sdlBFWindow->mMovedFunc(sdlBFWindow); - } - else - { - sdlBFWindow->mHasPositionInit = true; - } - } + sdlBFWindow->mMovedFunc(sdlBFWindow); + + // if (sdlBFWindow->mHasPositionInit) + // { + // if (sdlEvent.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) + // sdlBFWindow->mRenderWindow->Resized(); + // + // + // } + // else + // { + // sdlBFWindow->mHasPositionInit = true; + // } } - break; } + break; + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if (sdlBFWindow != NULL) + sdlBFWindow->mRenderWindow->Resized(); + } + break; } + } +} +void SdlBFApp::Init() +{ + mRunning = true; + mInMsgProc = false; + +// bf_SDL_SetHint(SDL_HINT_EVENT_LOGGING, "1"); + + mRenderDevice = new GLRenderDevice(); + mRenderDevice->Init(this); +} + +void SdlBFApp::Run() +{ + while (mRunning) + { + ProcessSDLEvents(); Process(); } } @@ -670,12 +710,6 @@ void SdlBFApp::Draw() { //Beefy::DebugTimeGuard suspendTimeGuard(30, "SdlBFApp::Draw"); - glDisable(GL_SCISSOR_TEST); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - gPixelsDrawn = 0; gBFDrawBatchCount = 0; @@ -705,10 +739,7 @@ void SdlBFWindow::GetPosition(int* x, int* y, int* width, int* height, int* clie *clientX = *x; if (clientY) *clientY = *y; - if (clientWidth) - *clientWidth = *width; - if (clientHeight) - *clientHeight = *height; + bf_SDL_GetWindowSizeInPixels(mSDLWindow, clientWidth, clientHeight); } void SdlBFApp::PhysSetCursor() @@ -741,13 +772,31 @@ void SdlBFWindow::SetClientPosition(int x, int y) void SdlBFWindow::Resize(int x, int y, int width, int height, ShowKind showKind) { bf_SDL_SetWindowPosition(mSDLWindow, x, y); + bf_SDL_SyncWindow(mSDLWindow); bf_SDL_SetWindowSize(mSDLWindow, width, height); + bf_SDL_SyncWindow(mSDLWindow); + Show(showKind); + bf_SDL_SyncWindow(mSDLWindow); + + if (mRenderWindow != NULL) + mRenderWindow->Resized(); + if (mMovedFunc != NULL) + mMovedFunc(this); } void SdlBFWindow::GetPlacement(int* normX, int* normY, int* normWidth, int* normHeight, int* showKind) { bf_SDL_GetWindowPosition(mSDLWindow, normX, normY); bf_SDL_GetWindowSize(mSDLWindow, normWidth, normHeight); + SDL_WindowFlags flags = bf_SDL_GetWindowFlags(mSDLWindow); + if ((flags & SDL_WINDOW_HIDDEN) == SDL_WINDOW_HIDDEN) + *showKind = ShowKind_Hide; + else if ((flags & SDL_WINDOW_MAXIMIZED) == SDL_WINDOW_MAXIMIZED) + *showKind = ShowKind_ShowMaximized; + else if ((flags & SDL_WINDOW_MINIMIZED) == SDL_WINDOW_MINIMIZED) + *showKind = ShowKind_ShowMinimized; + else + *showKind = ShowKind_ShowNormal; } void SdlBFWindow::SetAlpha(float alpha, uint32 destAlphaSrcMask, bool isMouseVisible) @@ -840,6 +889,45 @@ void SdlBFWindow::ModalsRemoved() //::SetFocus(mHWnd); } +void SdlBFWindow::Show(ShowKind showKind) +{ + switch (showKind) + { + case BFWindow::ShowKind_Hide: + bf_SDL_HideWindow(mSDLWindow); + break; + case BFWindow::ShowKind_Normal: + bf_SDL_RestoreWindow(mSDLWindow); + break; + case BFWindow::ShowKind_Minimized: + bf_SDL_MinimizeWindow(mSDLWindow); + break; + case BFWindow::ShowKind_Maximized: + bf_SDL_MaximizeWindow(mSDLWindow); + break; + case BFWindow::ShowKind_Show: + bf_SDL_ShowWindow(mSDLWindow); + break; + case BFWindow::ShowKind_ShowNormal: + bf_SDL_RestoreWindow(mSDLWindow); + bf_SDL_ShowWindow(mSDLWindow); + break; + case BFWindow::ShowKind_ShowMinimized: + bf_SDL_MinimizeWindow(mSDLWindow); + bf_SDL_ShowWindow(mSDLWindow); + break; + case BFWindow::ShowKind_ShowMaximized: + bf_SDL_MaximizeWindow(mSDLWindow); + bf_SDL_ShowWindow(mSDLWindow); + break; + } +} + +void SdlBFWindow::SetForeground() +{ + bf_SDL_RaiseWindow(mSDLWindow); +} + void SdlBFWindow::SetTitle(const char* title) { bf_SDL_SetWindowTitle(mSDLWindow, title); diff --git a/BeefySysLib/platform/sdl/SdlBFApp.h b/BeefySysLib/platform/sdl/SdlBFApp.h index 94611e889..106856d90 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.h +++ b/BeefySysLib/platform/sdl/SdlBFApp.h @@ -46,8 +46,8 @@ class SdlBFWindow : public BFWindow virtual void ModalsRemoved() override; - virtual void Show(ShowKind showKind) {} - virtual void SetForeground() override {}; + virtual void Show(ShowKind showKind) override; + virtual void SetForeground() override; }; typedef Dictionary SdlWindowMap; @@ -61,7 +61,7 @@ class SdlBFApp : public BFApp SdlWindowMap mSdlWindowMap; SdlClipboardData* mSdlClipboardData; SDL_GLContext mGLContext; - + SDL_Window* mGLContextWindow; protected: virtual void Draw() override; @@ -69,6 +69,7 @@ class SdlBFApp : public BFApp const char* GetClipboardFormat(const StringImpl& format); SdlBFWindow* GetSdlWindowFromId(uint32 id); + void ProcessSDLEvents(); public: SdlBFApp(); diff --git a/IDE/src/ui/AutoComplete.bf b/IDE/src/ui/AutoComplete.bf index c14774763..5ce070a9b 100644 --- a/IDE/src/ui/AutoComplete.bf +++ b/IDE/src/ui/AutoComplete.bf @@ -288,7 +288,7 @@ namespace IDE.ui return; } - if ((mWidgetWindow != null) && (!mWidgetWindow.mHasClosed) && (mWidgetWindow.HasParent(window))) + if ((mAutoComplete.mIgnoreMove == 0) && (mWidgetWindow != null) && (!mWidgetWindow.mHasClosed) && (mWidgetWindow.HasParent(window))) mAutoComplete.Close(); } @@ -381,7 +381,6 @@ namespace IDE.ui using (g.PushColor(0xFFFFFFFF)) g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Menu), 0, 0, boxWidth - GS!(6), drawHeight - GS!(8)); } - g.SetFont(IDEApp.sApp.mCodeFont); /*using (g.PushColor(0x80FF0000)) @@ -645,9 +644,11 @@ namespace IDE.ui int windowHeight = (int)(mWantHeight + Math.Max(0, mDocHeight - GS!(32))); + mAutoComplete.mIgnoreMove++; mWidgetWindow.Resize(mWidgetWindow.mNormX, mWidgetWindow.mNormY, windowWidth, windowHeight); mScrollContent.mWidth = mWidth; //Resize(0, 0, mWidgetWindow.mClientWidth, mWidgetWindow.mClientHeight); + mAutoComplete.mIgnoreMove--; ResizeContent(-1, -1, mVertScrollbar != null); } } @@ -995,7 +996,9 @@ namespace IDE.ui { if (mOwnsWindow) { + mAutoComplete.mIgnoreMove++; mAutoComplete.UpdateWindow(ref mWidgetWindow, this, mAutoComplete.mInvokeSrcPositions[0], (int32)extWidth, (int32)extHeight); + mAutoComplete.mIgnoreMove--; } else { @@ -1391,6 +1394,7 @@ namespace IDE.ui public bool mIsDocumentationPass; public bool mIsUserRequested; + public int mIgnoreMove; bool mClosed; bool mPopulating; float mWantX; @@ -1553,6 +1557,8 @@ namespace IDE.ui public void Update() { + Debug.Assert((mIgnoreMove >= 0) && (mIgnoreMove <= 4)); + if ((mInvokeWindow != null) && (!mInvokeWidget.mIsAboveText)) { int textIdx = mTargetEditWidget.Content.mTextCursors.Front.mCursorTextPos; @@ -1581,11 +1587,13 @@ namespace IDE.ui int insertLine = line; if ((insertLine != invokeLine) && ((insertLine - invokeLine) * gApp.mCodeFont.GetHeight() < GS!(40))) { + mIgnoreMove++; mInvokeWidget.mIsAboveText = true; mInvokeWidget.ResizeContent(false); UpdateWindow(ref mInvokeWindow, mInvokeWidget, mInvokeSrcPositions[0], (int32)mInvokeWidget.mWidth, (int32)mInvokeWidget.mHeight); if (mListWindow != null) UpdateWindow(ref mListWindow, mAutoCompleteListWidget, mInsertStartIdx.Value, mListWindow.mWindowWidth, mListWindow.mWindowHeight); + mIgnoreMove--; } } @@ -1906,6 +1914,11 @@ namespace IDE.ui return hadMatch; } + public void SetIgnoreMove(bool ignoreMove) + { + mIgnoreMove += ignoreMove ? 1 : -1; + } + // IDEHelper/third_party/FtsFuzzyMatch.h [CallingConvention(.Stdcall), CLink] static extern bool fts_fuzzy_match(char8* pattern, char8* str, ref int32 outScore, uint8* matches, int maxMatches); @@ -2038,7 +2051,7 @@ namespace IDE.ui if (changedAfterInfo) { mAutoCompleteListWidget.mSelectIdx = -1; - + if ((curString.Length == 0) && (!mIsMember) && (mInvokeSrcPositions == null)) { mPopulating = false; @@ -2143,6 +2156,8 @@ namespace IDE.ui SelectEntry(""); } + SetIgnoreMove(true); + gApp.mAutoCompletePanel.StartBind(this); int32 prevInvokeSelect = 0; @@ -2184,6 +2199,7 @@ namespace IDE.ui HandleAutoCompleteListWidget(visibleCount); } gApp.mAutoCompletePanel.FinishBind(); + SetIgnoreMove(false); if ((mAutoCompleteListWidget != null) && (!mAutoCompleteListWidget.mIsInitted)) mAutoCompleteListWidget.Init(); @@ -2390,6 +2406,7 @@ namespace IDE.ui { if (mAutoCompleteListWidget != null) { + mIgnoreMove++; if (IsInPanel()) { mAutoCompleteListWidget.RemoveSelf(); @@ -2403,6 +2420,7 @@ namespace IDE.ui else delete mAutoCompleteListWidget; mAutoCompleteListWidget = null; + mIgnoreMove--; } } if (mAutoCompleteListWidget == null) diff --git a/IDEHelper/CMakeLists.txt b/IDEHelper/CMakeLists.txt index e5e41fbd8..cb264cfd7 100644 --- a/IDEHelper/CMakeLists.txt +++ b/IDEHelper/CMakeLists.txt @@ -194,7 +194,6 @@ if (HAVE_BACKTRACE_HEADERS) endif() if (DEFINED BF_ENABLE_SDL) - string(APPEND TARGET_LIBS_OS " -lGL") string(STRIP ${TARGET_LIBS_OS} TARGET_LIBS_OS) endif() diff --git a/bin/build.sh b/bin/build.sh index 1faff2bb1..bb6191150 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -19,7 +19,6 @@ do if [[ $i == "sdl" ]]; then echo "Using SDL" USE_SDL="-DBF_ENABLE_SDL=1" - SDL_LINKOPTS="-lGL" fi if [[ $i == "no_ffi" ]]; then @@ -147,7 +146,7 @@ ln -s -f $ROOTPATH/jbuild/Release/bin/libIDEHelper.a ../../BeefLibs/Beefy2D/dist ### DEBUG ### echo Building BeefBuild_bootd -../../jbuild_d/Debug/bin/BeefBoot --out="BeefBuild_bootd" --src=../src --src=../../BeefBuild/src --src=../../BeefLibs/corlib/src --src=../../BeefLibs/Beefy2D/src --define=CLI --define=DEBUG --startup=BeefBuild.Program --linkparams="./libBeefRT_d.a ./libIDEHelper_d.a ./libBeefySysLib_d.a ./libhunspell.$LIBEXT $(< ../../IDE/dist/IDEHelper_libs_d.txt) $SDL_LINKOPTS $LINKOPTS" +../../jbuild_d/Debug/bin/BeefBoot --out="BeefBuild_bootd" --src=../src --src=../../BeefBuild/src --src=../../BeefLibs/corlib/src --src=../../BeefLibs/Beefy2D/src --define=CLI --define=DEBUG --startup=BeefBuild.Program --linkparams="./libBeefRT_d.a ./libIDEHelper_d.a ./libBeefySysLib_d.a ./libhunspell.$LIBEXT $(< ../../IDE/dist/IDEHelper_libs_d.txt) $LINKOPTS" echo Building BeefBuild_d ./BeefBuild_bootd -clean -proddir=../../BeefBuild -config=Debug echo Testing IDEHelper/Tests in BeefBuild_d @@ -156,7 +155,7 @@ echo Testing IDEHelper/Tests in BeefBuild_d ### RELEASE ### echo Building BeefBuild_boot -../../jbuild/Release/bin/BeefBoot --out="BeefBuild_boot" --src=../src --src=../../BeefBuild/src --src=../../BeefLibs/corlib/src --src=../../BeefLibs/Beefy2D/src --define=CLI --startup=BeefBuild.Program --linkparams="./libBeefRT.a ./libIDEHelper.a ./libBeefySysLib.a ./libhunspell.$LIBEXT $(< ../../IDE/dist/IDEHelper_libs.txt) $SDL_LINKOPTS $LINKOPTS" +../../jbuild/Release/bin/BeefBoot --out="BeefBuild_boot" --src=../src --src=../../BeefBuild/src --src=../../BeefLibs/corlib/src --src=../../BeefLibs/Beefy2D/src --define=CLI --startup=BeefBuild.Program --linkparams="./libBeefRT.a ./libIDEHelper.a ./libBeefySysLib.a ./libhunspell.$LIBEXT $(< ../../IDE/dist/IDEHelper_libs.txt) $LINKOPTS" echo Building BeefBuild ./BeefBuild_boot -clean -proddir=../../BeefBuild -config=Release echo Testing IDEHelper/Tests in BeefBuild From ababd49537558fe7d5a4f615ec8145f543d01642 Mon Sep 17 00:00:00 2001 From: Fusioon Date: Thu, 15 Jan 2026 12:16:55 +0100 Subject: [PATCH 42/47] Make MenuBar SysMenu compatible --- IDE/src/IDEApp.bf | 19 +- IDE/src/ui/MainFrame.bf | 6 +- IDE/src/ui/MenuBar.bf | 498 ++++++++++++---------------------------- 3 files changed, 168 insertions(+), 355 deletions(-) diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index b55b70377..23b0ce399 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -6063,7 +6063,13 @@ namespace IDE { scope AutoBeefPerf("IDEApp.CreateMenu"); - SysMenu root = mMainWindow.mSysMenu; + SysMenu root; +#if BF_PLATFORM_WINDOWS + root = mMainWindow.mSysMenu; +#else + root = mMainFrame.mMenuBar.mSysMenuRoot; + defer mMainFrame.RehupSize(); +#endif String keyStr = scope String(); @@ -6147,11 +6153,18 @@ namespace IDE void AddLineEndingKind(String name, LineEndingKind lineEndingKind) { + bool allowLast; +#if !BF_PLATFORM_WINDOWS + allowLast = true; +#else + allowLast = false; +#endif + lineEndingMenu.AddMenuItem(name, null, new (menu) => { var sysMenu = (SysMenu)menu; - var sourceViewPanel = GetActiveSourceViewPanel(); + var sourceViewPanel = GetActiveSourceViewPanel(allowLast); if (sourceViewPanel != null) { if (sourceViewPanel.mEditData.mLineEndingKind != lineEndingKind) @@ -6166,7 +6179,7 @@ namespace IDE { var sysMenu = (SysMenu)menu; - var sourceViewPanel = GetActiveSourceViewPanel(); + var sourceViewPanel = GetActiveSourceViewPanel(allowLast); if (sourceViewPanel != null) { sysMenu.Modify(null, null, null, true, (sourceViewPanel.mEditData.mLineEndingKind == lineEndingKind) ? 1 : 0, true); diff --git a/IDE/src/ui/MainFrame.bf b/IDE/src/ui/MainFrame.bf index 3df50c1e5..d76fcdd32 100644 --- a/IDE/src/ui/MainFrame.bf +++ b/IDE/src/ui/MainFrame.bf @@ -15,7 +15,7 @@ namespace IDE.ui public StatusBar mStatusBar; public DarkDockingFrame mDockingFrame; -#if BF_PLATFORM_LINUX +#if !BF_PLATFORM_WINDOWS public MenuBar mMenuBar; #endif @@ -25,7 +25,7 @@ namespace IDE.ui AddWidget(mStatusBar); mDockingFrame = (DarkDockingFrame)ThemeFactory.mDefault.CreateDockingFrame(); AddWidget(mDockingFrame); -#if BF_PLATFORM_LINUX +#if !BF_PLATFORM_WINDOWS mMenuBar = new MenuBar(); AddWidget(mMenuBar); #endif @@ -50,7 +50,7 @@ namespace IDE.ui base.Resize(x, y, width, height); int32 statusHeight = GS!(20); -#if BF_PLATFORM_LINUX +#if !BF_PLATFORM_WINDOWS int32 menuHeight = GS!(20); mDockingFrame.Resize(0, menuHeight, width, height - statusHeight - menuHeight); mMenuBar.Resize(0, 0, width, menuHeight); diff --git a/IDE/src/ui/MenuBar.bf b/IDE/src/ui/MenuBar.bf index 77e0205bb..9aa42a9ee 100644 --- a/IDE/src/ui/MenuBar.bf +++ b/IDE/src/ui/MenuBar.bf @@ -7,11 +7,107 @@ using IDE; using IDE.util; using System; using System.Diagnostics; +using System.Collections; +using Beefy.sys; namespace IDE.ui; public class MenuBar : Widget { + class UISysMenu : SysMenu + { + public override SysMenu AddMenuItem(String text, String hotKey = null, MenuItemSelectedHandler menuItemSelectedHandler = null, + MenuItemUpdateHandler menuItemUpdateHandler = null, SysBitmap bitmap = null, bool enabled = true, int32 checkState = -1, bool radioCheck = false) + { + if (mChildren == null) + mChildren = new List(); + + UISysMenu sysMenu = new UISysMenu(); + if (text != null) + { + sysMenu.mText = new String(text); + let bindIndex = sysMenu.mText.IndexOf('&'); + if (bindIndex != -1) + sysMenu.mText.Remove(bindIndex); + } + if ((hotKey != null) && (hotKey.Length != 0) && ((hotKey.Length > 1 ) || (hotKey[0] != '#'))) + { + sysMenu.mHotKey = new String(hotKey); + sysMenu.mHotKey.TrimStart('#'); + } + + sysMenu.mBitmap = bitmap; + sysMenu.mEnabled = enabled; + sysMenu.mCheckState = checkState; + sysMenu.mRadioCheck = radioCheck; + if (menuItemSelectedHandler != null) + sysMenu.mOnMenuItemSelected.Add(menuItemSelectedHandler); + if (menuItemUpdateHandler != null) + sysMenu.mOnMenuItemUpdate.Add(menuItemUpdateHandler); + sysMenu.mNativeBFMenu = null; + sysMenu.mParent = this; + sysMenu.mWindow = mWindow; + mChildren.Add(sysMenu); + + return sysMenu; + } + + public override void Modify(String text, String hotKey = null, SysBitmap bitmap = null, bool enabled = true, int32 checkState = -1, bool radioCheck = false) + { + if (((Object)mText != text) && (text != null)) + { + if (text.Length > 0) + { + String.NewOrSet!(mText, text); + let bindIndex = mText.IndexOf('&'); + if (bindIndex != -1) + mText.Remove(bindIndex); + } + else + DeleteAndNullify!(mText); + } + + if (((Object)mHotKey != hotKey) && (hotKey != null)) + { + if ((hotKey.Length > 0) && ((hotKey.Length > 1 ) || (hotKey[0] != '#'))) + { + String.NewOrSet!(mHotKey, hotKey); + mHotKey.TrimStart('#'); + } + else + DeleteAndNullify!(mHotKey); + } + mBitmap = bitmap; + mEnabled = enabled; + mCheckState = checkState; + mRadioCheck = radioCheck; + } + } + + public class UISysMenuRoot : UISysMenu + { + public MenuBar mRootMenuBar; + public this() + { + + } + + public override SysMenu AddMenuItem(String text, String hotKey = null, MenuItemSelectedHandler menuItemSelectedHandler = null, MenuItemUpdateHandler menuItemUpdateHandler = null, + SysBitmap bitmap = null, bool enabled = true, int32 checkState = -1, bool radioCheck = false) + { + let item = base.AddMenuItem(text, hotKey, menuItemSelectedHandler, menuItemUpdateHandler, bitmap, enabled, checkState, radioCheck); + let button = new Button(); + button.Label = text; + button.mSysMenu = item; + button.mOnMouseClick.Add(new (evt) => { + mOnMenuItemSelected(this); + mRootMenuBar.ShowMenu(button); + }); + mRootMenuBar.AddItemButton(button); + return item; + } + } + public class Button : ButtonWidget { private String mLabel ~ delete _; @@ -29,6 +125,8 @@ public class MenuBar : Widget } } + public SysMenu mSysMenu; + public override void Draw(Graphics g) { base.Draw(g); @@ -53,370 +151,82 @@ public class MenuBar : Widget } } - public Button mFileButton; - public Button mEditButton; - public Button mViewButton; - public Button mBuildButton; - public Button mDebugButton; - public Button mTestButton; - public Button mWindowButton; - public Button mHelpButton; + + public append List