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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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/40] 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 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 - 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/40] 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/40] 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);