1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 11:38:21 +02:00

Merge remote-tracking branch 'upstream/master' into Yuvan/CustomBuildProperties

This commit is contained in:
Yuvan Wickramasinghe 2025-05-13 15:14:10 -07:00
commit 524ebaa914
No known key found for this signature in database
49 changed files with 1372 additions and 146 deletions

View file

@ -0,0 +1,40 @@
namespace Beefy.geom;
struct Oval
{
public float mX;
public float mY;
public float mRadiusX;
public float mRadiusY;
public this(float x, float y, float radiusX, float radiusY)
{
mX = x;
mY = y;
mRadiusX = radiusX;
mRadiusY = radiusY;
}
public this(Rect rect)
{
mX = rect.CenterX;
mY = rect.CenterY;
mRadiusX = rect.mWidth / 2;
mRadiusY = rect.mHeight / 2;
}
public bool Contains(float x, float y)
{
float dx = (x - mX) / mRadiusX;
float dy = (y - mY) / mRadiusY;
return dx*dx + dy*dy <= 1.0f;
}
public void Inflate(float x, float y) mut
{
mRadiusX += x;
mRadiusY += y;
}
public bool Contains(Vector2 vec) => Contains(vec.mX, vec.mY);
}

View file

@ -212,6 +212,12 @@ namespace Beefy.geom
(y >= mY) && (y < mY + mHeight));
}
public bool ContainsInclusive(T x, T y)
{
return ((x >= mX) && (x <= mX + mWidth) &&
(y >= mY) && (y <= mY + mHeight));
}
public bool Contains(Point<T> pt)
{
return Contains(pt.x, pt.y);
@ -219,7 +225,7 @@ namespace Beefy.geom
public bool Contains(Self rect)
{
return Contains(rect.mX, rect.mY) && Contains(rect.mX + rect.mWidth, rect.mY + rect.mHeight);
return Contains(rect.mX, rect.mY) && ContainsInclusive(rect.mX + rect.mWidth, rect.mY + rect.mHeight);
}
public void Offset(T x, T y) mut

View file

@ -1035,6 +1035,27 @@ namespace Beefy.gfx
{
Gfx_CopyDrawVertex((.)idx, (.)srcIdx);
}
public void OutlineOval(float x, float y, float radiusX, float radiusY)
{
int numSections = 12 + (.)((radiusX + radiusY) * 0.15f);
for (int section < numSections)
{
float ang0 = (section * Math.PI_f * 2) / numSections;
float ang1 = ((section + 1) * Math.PI_f * 2) / numSections;
float x0 = x + Math.Cos(ang0) * radiusX;
float y0 = y + Math.Sin(ang0) * radiusY;
float x1 = x + Math.Cos(ang1) * radiusX;
float y1 = y + Math.Sin(ang1) * radiusY;
DrawLine(x0, y0, x1, y1);
}
}
public void OutlineCircle(float x, float y, float radius)
{
OutlineOval(x, y, radius, radius);
}
}
#else
public class Graphics : GraphicsBase

View file

@ -103,6 +103,7 @@ namespace Beefy.gfx
scope AutoBeefPerf("Image.LoadFromFile");
var useFileName = scope String()..Append(fileName);
useFileName.Replace('\\', '/');
FilePackManager.TryMakeMemoryString(useFileName);
void* aNativeTextureSegment = Gfx_LoadTexture(useFileName, (int32)flags);
if (aNativeTextureSegment == null)

View file

@ -125,7 +125,11 @@ namespace Beefy.widgets
case .LWin,
.RWin,
.Alt,
.LAlt,
.RAlt,
.Control,
.LCtrl,
.RCtrl,
.Command,
.Shift:
return true;
@ -144,7 +148,7 @@ namespace Beefy.widgets
return (KeyCode)c;
if ((c >= '0') && (c <= '9'))
return (KeyCode)c;
if ((c >= 'a') && (c <= 'a'))
if ((c >= 'a') && (c <= 'z'))
return (KeyCode)(c.ToUpper);
if (c == '[')
return (KeyCode)LBracket;
@ -160,6 +164,14 @@ namespace Beefy.widgets
return (KeyCode)Period;
if (c == ',')
return (KeyCode)Comma;
if (c == ' ')
return (KeyCode)Space;
if (c == '-')
return (KeyCode)Minus;
if (c == '+')
return (KeyCode)Add;
if (c == '=')
return (KeyCode)Equals;
}
if (str.StartsWith("0x"))

View file

@ -570,6 +570,12 @@ namespace System
return &mFirstElement;
}
public void CopyTo(T[,] arrayTo)
{
Debug.Assert(arrayTo.mLength >= mLength);
Internal.MemCpy(&arrayTo.GetRef(0), &GetRef(0), strideof(T) * mLength, alignof(T));
}
public Span<T>.Enumerator GetEnumerator()
{
return .(.(&mFirstElement, mLength));

View file

@ -85,10 +85,19 @@ namespace System
static void SetupOutStringEx()
{
OutString = => OutString_Ex;
sOutString = => OutString_Ex;
}
static function void(StringView str) OutString = => OutString_Simple;
static function void(StringView str) sOutString;
static function void(StringView str) OutString
{
get
{
if (sOutString == null)
sOutString = => OutString_Simple;
return sOutString;
}
}
#if !BF_RUNTIME_DISABLE && !BF_PLATFORM_WASM
private static extern void PutChars(char8* c, int32 len);

View file

@ -99,8 +99,11 @@ namespace System
[CallingConvention(.Cdecl)]
public extern static void SetMaxRawDeferredObjectFreePercentage(int maxPercentage);
#else
[LinkName("__GC_Report")]
public static void Report() {}
[LinkName("__GC_Shutdown")]
public static void Shutdown() {}
[LinkName("__GC_SetMaxRawDeferredObjectFreePercentage")]
public static void SetMaxRawDeferredObjectFreePercentage(int maxPercentage) {}
#endif
@ -138,17 +141,27 @@ namespace System
[CallingConvention(.Cdecl)]
public extern static void ExcludeThreadId(int thereadId);
#else
[LinkName("__GC_Disable")]
public static void Disable() {}
[LinkName("__GC_Collect")]
public static void Collect(bool async = true) {}
[LinkName("__GC_MarkAllStaticMembers")]
private static void MarkAllStaticMembers() {}
[LinkName("__GC_DebugDumpLeaks")]
public static void DebugDumpLeaks() {}
[SkipCall]
[SkipCall, LinkName("__GC_Mark1")]
public static void Mark(Object obj) {}
[LinkName("__GC_Mark2")]
public static void Mark(void* ptr, int size) {}
[LinkName("__GC_SetAutoCollectPeriod")]
public static void SetAutoCollectPeriod(int periodMS) {}
[LinkName("__GC_SetCollectFreeThreshold")]
public static void SetCollectFreeThreshold(int freeBytes) {}
[LinkName("__GC_SetMaxPausePercentage")]
public static void SetMaxPausePercentage(int maxPausePercentage) {}
[LinkName("__GC_AddPendingThread")]
static void AddPendingThread(void* internalThreadInfo) {}
[LinkName("__GC_ExcludeThreadId")]
public static void ExcludeThreadId(int thereadId) {}
#endif

View file

@ -623,6 +623,9 @@ abstract class CommonDialog
public Result<DialogResult> ShowDialog(INativeWindow owner = null)
{
if (!Linux.IsSystemdAvailable)
return .Err;
TryC!(Linux.SdBusOpenUser(&mBus)); // Maybe keep the bus open while the program is running ?
Linux.DBusMsg* call = ?;

View file

@ -82,7 +82,7 @@ namespace System
}
}
class RefCounted<T> : IRefCounted where T : class, delete
class RefCounted<T> : IRefCounted where T : delete
{
public T mVal;
public int mRefCount = 1;
@ -178,7 +178,7 @@ namespace System
public virtual T Detach()
{
var val = mVal;
mVal = null;
mVal = default;
return val;
}

View file

@ -44,6 +44,33 @@ class Linux
public typealias DBusMsgHandler = function int32(DBusMsg *m, void *userdata, DBusErr *ret_error);
public static bool IsSystemdAvailable { get; private set; } = true;
[AlwaysInclude, StaticInitPriority(100)]
static class AllowFail
{
public static this()
{
Runtime.AddErrorHandler(new => Handle);
}
public static Runtime.ErrorHandlerResult Handle(Runtime.ErrorStage errorStage, Runtime.Error error)
{
if (errorStage == .PreFail)
{
if (var loadLibaryError = error as Runtime.LoadSharedLibraryError)
{
if (loadLibaryError.mPath == "libsystemd.so")
{
IsSystemdAvailable = false;
return .Ignore;
}
}
}
return .ContinueFailure;
}
}
[Import("libsystemd.so"), LinkName("sd_bus_open_user")]
public static extern c_int SdBusOpenUser(DBus **ret);
[Import("libsystemd.so"), LinkName("sd_bus_open_system")]

View file

@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
using System.Interop;
namespace System.Net
{
@ -134,6 +135,26 @@ namespace System.Net
}
}
[CRepr, Union]
public struct IPv6Address
{
public uint8[16] byte;
public uint16[8] word;
public this(params uint16[8] addr)
{
for(let i < 8)
{
this.word[i] = (.)htons((int16)addr[i]);
}
}
public this(params uint8[16] addr)
{
this.byte = addr;
}
}
[CRepr]
public struct SockAddr
{
@ -149,6 +170,16 @@ namespace System.Net
public char8[8] sin_zero;
}
[CRepr]
public struct SockAddr_in6 : SockAddr
{
public int16 sin6_family;
public uint16 sin6_port;
public uint32 sin6_flowinfo;
public IPv6Address sin6_addr;
public uint32 sin6_scope_id;
}
[CRepr]
public struct HostEnt
{
@ -159,13 +190,33 @@ namespace System.Net
public char8** h_addr_list; /* list of addresses */
}
[CRepr]
public struct AddrInfo
{
public int32 ai_flags;
public int32 ai_family;
public int32 ai_socktype;
public int32 ai_protocol;
public c_size ai_addrlen;
#if BF_PLATFORM_WINDOWS
public char8* ai_canonname;
public SockAddr* ai_addr;
#else
public SockAddr* ai_addr;
public char8* ai_canonname;
#endif
public SockAddr* ai_next;
}
public const HSocket INVALID_SOCKET = (HSocket)-1;
public const int32 SOCKET_ERROR = -1;
public const int AF_INET = 2;
public const int AF_INET6 = 23;
public const int SOCK_STREAM = 1;
public const int SOCK_DGRAM = 2;
public const int IPPROTO_TCP = 6;
public const int IPPROTO_UDP = 17;
public const int IPPROTO_IPV6 = 41;
public const int TCP_NODELAY = 1;
public const int TCP_MAXSEG = 2;
@ -176,12 +227,16 @@ namespace System.Net
public const int SOL_SOCKET = 0xffff;
public const int SO_REUSEADDR = 0x0004;
public const int SO_BROADCAST = 0x0020;
public const int IPV6_V6ONLY = 27;
#else
public const int SOL_SOCKET = 1;
public const int SO_REUSEADDR = 2;
public const int SO_BROADCAST = 6;
public const int IPV6_V6ONLY = 26;
#endif
public const IPv4Address INADDR_ANY = default;
public const IPv6Address IN6ADDR_ANY = default;
#if BF_PLATFORM_WINDOWS
const int FIONBIO = (int)0x8004667e;
@ -252,6 +307,9 @@ namespace System.Net
[CLink, CallingConvention(.Stdcall)]
static extern HostEnt* gethostbyname(char8* name);
[CLink, CallingConvention(.Stdcall)]
static extern int32 getaddrinfo(char8* pNodeName, char8* pServiceName, AddrInfo* pHints, AddrInfo** ppResult);
[CLink, CallingConvention(.Stdcall)]
static extern HSocket socket(int32 af, int32 type, int32 protocol);
@ -395,8 +453,49 @@ namespace System.Net
int32 size = sizeof(SockAddr_in);
if (bind(mHandle, &service, size) == SOCKET_ERROR)
{
int err = WSAGetLastError();
Close();
return .Err;
}
if (listen(mHandle, backlog) == SOCKET_ERROR)
{
#unwarn
int err = GetLastError();
Close();
return .Err;
}
return .Ok;
}
public Result<void> Listen(IPv6Address address, int32 port, int32 backlog = 5, bool v6Only = false)
{
Debug.Assert(mHandle == INVALID_SOCKET);
mHandle = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (mHandle == INVALID_SOCKET)
{
#unwarn
int32 err = GetLastError();
return .Err;
}
int32 ipv6Opt = v6Only ? 1 : 0;
setsockopt(mHandle, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6Opt, 4);
RehupSettings();
SockAddr_in6 service;
service.sin6_family = AF_INET6;
service.sin6_addr = address;
service.sin6_port = (uint16)htons((int16)port);
int32 size = sizeof(SockAddr_in6);
if (bind(mHandle, &service, size) == SOCKET_ERROR)
{
#unwarn
int err = GetLastError();
Close();
return .Err;
}
@ -443,13 +542,47 @@ namespace System.Net
return .Ok;
}
public Result<void> Connect(StringView addr, int32 port, out SockAddr* sockAddr, out int addrFamily)
{
sockAddr = null;
addrFamily = default;
AddrInfo hints = default;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
AddrInfo* addrInfo = null;
if (getaddrinfo(addr.Ptr, null, &hints, &addrInfo) < 0)
return .Err;
sockAddr = addrInfo.ai_addr;
addrFamily = addrInfo.ai_family;
mHandle = socket(addrInfo.ai_family, SOCK_STREAM, IPPROTO_TCP);
if (mHandle == INVALID_SOCKET)
return .Err;
if (connect(mHandle, sockAddr, (.)addrInfo.ai_addrlen) == SOCKET_ERROR)
return .Err;
if (mHandle == INVALID_SOCKET)
{
#unwarn
int32 err = GetLastError();
return .Err;
}
mIsConnected = true;
RehupSettings();
return .Ok;
}
public Result<void> Connect(StringView addr, int32 port) => Connect(addr, port, ?);
public Result<void> AcceptFrom(Socket listenSocket, out SockAddr_in clientAddr)
public Result<void> AcceptFrom(Socket listenSocket, SockAddr* from, int32* fromLen)
{
clientAddr = default;
int32 clientAddrLen = sizeof(SockAddr_in);
mHandle = accept(listenSocket.mHandle, &clientAddr, &clientAddrLen);
mHandle = accept(listenSocket.mHandle, from, fromLen);
if (mHandle == INVALID_SOCKET)
{
#unwarn
@ -462,7 +595,14 @@ namespace System.Net
return .Ok;
}
public Result<void> AcceptFrom(Socket listenSocket) => AcceptFrom(listenSocket, ?);
public Result<void> AcceptFrom(Socket listenSocket, out SockAddr_in clientAddr)
{
clientAddr = default;
int32 clientAddrLen = sizeof(SockAddr_in);
return AcceptFrom(listenSocket, &clientAddr, &clientAddrLen);
}
public Result<void> AcceptFrom(Socket listenSocket) => AcceptFrom(listenSocket, null, null);
public static int32 Select(FDSet* readFDS, FDSet* writeFDS, FDSet* exceptFDS, int waitTimeMS)
{
@ -597,6 +737,8 @@ namespace System.Net
#unwarn
public Result<int> SendTo(void* ptr, int size, SockAddr_in to) => SendTo(ptr, size, &to, sizeof(SockAddr_in));
#unwarn
public Result<int> SendTo(void* ptr, int size, SockAddr_in6 to) => SendTo(ptr, size, &to, sizeof(SockAddr_in6));
public void Close()
{
@ -632,5 +774,32 @@ namespace System.Net
status = bind(mHandle, &bindAddr, sizeof(SockAddr_in));
return .Ok;
}
public Result<void> OpenUDPIPv6(int32 port = -1, bool v6Only = false)
{
SockAddr_in6 bindAddr = default;
mHandle = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (mHandle == INVALID_SOCKET)
{
return .Err;
}
RehupSettings();
int32 yes = 1;
//setsockopt(mHandle, SOL_SOCKET, SO_REUSEADDR, &yes, 4);
int32 status = setsockopt(mHandle, SOL_SOCKET, SO_BROADCAST, &yes, 4);
int32 ipv6Opt = v6Only ? 1 : 0;
setsockopt(mHandle, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6Opt, 4);
bindAddr.sin6_addr = IN6ADDR_ANY;
bindAddr.sin6_port = (.)htons((int16)port);
bindAddr.sin6_family = AF_INET6;
status = bind(mHandle, &bindAddr, sizeof(SockAddr_in6));
return .Ok;
}
}
}

View file

@ -14,7 +14,6 @@ namespace System
public bool AVX, AVX2, AVX512;
}
[StaticInitPriority(201)]
static class Runtime
{
const int32 cVersion = 10;
@ -284,7 +283,7 @@ namespace System
mDebugMessageData_SetupProfilerCmd = => DebugMessageData_SetupProfilerCmd;
mDebugMessageData_Fatal = => DebugMessageData_Fatal;
mDebugMessageData_Clear = => DebugMessageData_Clear;
mCheckErrorHandler = => CheckErrorHandler;
mCheckErrorHandler = => CheckErrorHandler_Thunk;
}
};
@ -389,13 +388,23 @@ namespace System
public static bool sInsideErrorHandler;
}
[AlwaysInclude, StaticInitPriority(201)]
static struct RuntimeInit
{
public static this()
{
Runtime.Init();
}
}
static RtFlags sExtraFlags;
static bool sQueriedFeatures = false;
static RuntimeFeatures sFeatures;
static function void() sThreadInit;
public static this()
static void Init()
{
#if !BF_RUNTIME_DISABLE
BfRtCallbacks.sCallbacks.Init();
@ -422,6 +431,11 @@ namespace System
#endif
}
public static this()
{
}
[NoReturn]
public static void FatalError(String msg = "Fatal error encountered", String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum)
{
@ -497,6 +511,13 @@ namespace System
public static function int32(char8* kind, char8* arg1, char8* arg2, int arg3) CheckErrorHandler;
public static function void*(char8* filePath) LibraryLoadCallback;
public static int32 CheckErrorHandler_Thunk(char8* kind, char8* arg1, char8* arg2, int arg3)
{
if (CheckErrorHandler != null)
return CheckErrorHandler(kind, arg1, arg2, arg3);
return 0;
}
static ErrorHandlerResult CheckAssertError_Impl(AssertError.Kind kind, String error, String filePath, int lineNum)
{
return CheckErrorHandlers(scope AssertError(kind, error, filePath, lineNum));

View file

@ -972,6 +972,9 @@ namespace System
public void Append(char8* appendPtr, int length)
{
Debug.Assert(length >= 0);
if (length <= 0)
return;
int newCurrentIndex = mLength + length;
char8* ptr;
if (newCurrentIndex > AllocSize)
@ -996,6 +999,9 @@ namespace System
public void Append(char8[] arr, int idx, int length)
{
Debug.Assert(length >= 0);
if (length <= 0)
return;
int newCurrentIndex = mLength + length;
char8* ptr;
if (newCurrentIndex > AllocSize)

View file

@ -107,6 +107,7 @@ namespace System
public int32 Height => bottom - top;
}
[CRepr]
public struct Point : this(int32 x, int32 y)
{
@ -1715,6 +1716,9 @@ namespace System
[Import("user32.lib"), CLink, CallingConvention(.Stdcall)]
public static extern HWnd SetForegroundWindow(HWnd wnd);
[Import("user32.lib"), CLink, CallingConvention(.Stdcall)]
public static extern HWnd ShowWindow(HWnd wnd, int32 cmdShow);
[Import("user32.lib"), CLink, CallingConvention(.Stdcall)]
public static extern HWnd GetForegroundWindow();

View file

@ -155,8 +155,9 @@ namespace BeefPerf
LogLine(text);
mFailed = true;
if (!mShuttingDown)
Shutdown();
/*if (!mShuttingDown)
Shutdown();*/
//Stop();
return;
}
@ -647,7 +648,7 @@ namespace BeefPerf
public override void UnhandledCommandLine(String key, String value)
{
Fail(StackStringFormat!("Unhandled command line param: {0}", key));
Fail(scope:: String()..AppendF("Unhandled command line param: {0}", key));
}
void SetupNewWindow(WidgetWindow window)
@ -729,6 +730,9 @@ namespace BeefPerf
public override void Update(bool batchStart)
{
if (mFailed)
Stop();
base.Update(batchStart);
/*if (!mListenSocket.IsConnected)

View file

@ -1441,6 +1441,8 @@ namespace BeefPerf
if (cNext == 'd')
zoneName.mParamsSize += 4;
else if (cNext == 'p')
zoneName.mParamsSize += 8;
else if (cNext == 'f')
zoneName.mParamsSize += 4;
else if (cNext == 's')

View file

@ -216,6 +216,12 @@ namespace BeefPerf
Read(&val, 4);
val.ToString(outStr);
}
else if (cNext == 'p')
{
int64 val = 0;
Read(&val, 8);
val.ToString(outStr, "X", null);
}
else if (cNext == 'f')
{
float val = 0;

View file

@ -767,6 +767,12 @@ namespace BeefPerf
stateCtx.Read(&val, 4);
val.ToString(str);
}
else if (cNext == 'p')
{
int64 val = 0;
stateCtx.Read(&val, 8);
val.ToString(str, "X", null);
}
else if (cNext == 'f')
{
float val = 0;
@ -2316,6 +2322,11 @@ namespace BeefPerf
{
switch (keyCode)
{
case (KeyCode)'C':
if (DarkTooltipManager.sTooltip != null)
{
gApp.SetClipboardText(DarkTooltipManager.sTooltip.mText);
}
case (KeyCode)'Z':
mUndoManager.Undo();
case (KeyCode)'Y':

View file

@ -31,6 +31,9 @@ namespace LogViewer
public List<Match> mNewMatches ~ delete _;
public bool mRefreshing;
public String mFilePath ~ delete _;
public String mMemLogName ~ delete _;
public uint32[] mColors = new .(
0xFFFFFFFF,
0xFFFC5858,
@ -54,6 +57,7 @@ namespace LogViewer
ewc.mFont = gApp.mFont;
ewc.mWordWrap = false;
ewc.mTextColors = mColors;
ewc.mIsReadOnly = true;
mDocEdit.InitScrollbars(true, true);
AddWidget(mDocEdit);
@ -79,6 +83,12 @@ namespace LogViewer
public void Load(StringView filePath)
{
DeleteAndNullify!(mFilePath);
DeleteAndNullify!(mMemLogName);
mFilePath = new .(filePath);
mWidgetWindow.SetTitle(scope $"LogViewer - {mFilePath}");
scope AutoBeefPerf("Board.Load");
delete mContent;
@ -89,9 +99,45 @@ namespace LogViewer
{
gApp.Fail("Failed to open file '{0}'", filePath);
}
mFilterDirtyCountdown = 1;
//Refresh();
//mDocEdit.SetText(mContent);
}
[CallingConvention(.Stdcall), CLink]
static extern char8* MemLogger_Get(char8* name);
public void LoadMemLog(StringView name)
{
DeleteAndNullify!(mFilePath);
DeleteAndNullify!(mMemLogName);
mMemLogName = new .(name);
mWidgetWindow.SetTitle(scope $"LogViewer - {mMemLogName}");
var result = MemLogger_Get(name.ToScopeCStr!());
if (result == null)
{
gApp.Fail("Failed to open MemLog '{0}'", name);
return;
}
delete mContent;
mContent = new String();
mContent.Append(result);
mFilterDirtyCountdown = 1;
}
public void Reload()
{
if (mFilePath != null)
Load(mFilePath);
if (mMemLogName != null)
LoadMemLog(mMemLogName);
}
void Refresh()
{
scope AutoBeefPerf("Board.Refresh");
@ -105,6 +151,8 @@ namespace LogViewer
let filters = mFilter.Split!('\n');
if (mContent != null)
{
for (var line in mContent.Split('\n'))
{
bool hadMatch = false;
@ -138,6 +186,7 @@ namespace LogViewer
mNewContent.Append('\n');
}
}
}
mRefreshing = false;

View file

@ -8,6 +8,7 @@ using Beefy.utils;
using System.IO;
using System.Diagnostics;
using System.Threading;
using Beefy.sys;
namespace LogViewer
{
@ -26,6 +27,45 @@ namespace LogViewer
{
base.Init();
/*var dialog = scope OpenFileDialog();
dialog.SetFilter("All files (*.*)|*.*");
dialog.InitialDirectory = mInstallDir;
dialog.Title = "Open Log";
let result = dialog.ShowDialog();
if ((result case .Err) || (dialog.FileNames.Count == 0))
{
Stop();
return;
}*/
BeefPerf.Init("127.0.0.1", "LogViewer");
DarkTheme darkTheme = new DarkTheme();
darkTheme.Init();
ThemeFactory.mDefault = darkTheme;
BFWindow.Flags windowFlags = BFWindow.Flags.Border | //BFWindow.Flags.SysMenu | //| BFWindow.Flags.CaptureMediaKeys |
BFWindow.Flags.Caption | BFWindow.Flags.Minimize | BFWindow.Flags.QuitOnClose | BFWindowBase.Flags.Resizable |
BFWindow.Flags.SysMenu | .Menu;
mFont = new Font();
float fontSize = 12;
mFont.Load(scope String(BFApp.sApp.mInstallDir, "fonts/SourceCodePro-Regular.ttf"), fontSize);
mFont.AddAlternate("Segoe UI Symbol", fontSize);
mFont.AddAlternate("Segoe UI Historic", fontSize);
mFont.AddAlternate("Segoe UI Emoji", fontSize);
mBoard = new Board();
//mBoard.Load(dialog.FileNames[0]);
mMainWindow = new WidgetWindow(null, "LogViewer", 20, 20, 1600, 1200, windowFlags, mBoard);
//mMainWindow.mWindowKeyDownDelegate.Add(new => SysKeyDown);
mMainWindow.SetMinimumSize(480, 360);
mMainWindow.mIsMainWindow = true;
SysMenu root = mMainWindow.mSysMenu;
var subMenu = root.AddMenuItem("&File");
subMenu.AddMenuItem("&Open", "Ctrl+O", new (menu) =>
{
var dialog = scope OpenFileDialog();
dialog.SetFilter("All files (*.*)|*.*");
dialog.InitialDirectory = mInstallDir;
@ -36,36 +76,23 @@ namespace LogViewer
Stop();
return;
}
BeefPerf.Init("127.0.0.1", "LogViewer");
DarkTheme darkTheme = new DarkTheme();
darkTheme.Init();
ThemeFactory.mDefault = darkTheme;
BFWindow.Flags windowFlags = BFWindow.Flags.Border | //BFWindow.Flags.SysMenu | //| BFWindow.Flags.CaptureMediaKeys |
BFWindow.Flags.Caption | BFWindow.Flags.Minimize | BFWindow.Flags.QuitOnClose | BFWindowBase.Flags.Resizable |
BFWindow.Flags.SysMenu;
mFont = new Font();
float fontSize = 12;
mFont.Load(scope String(BFApp.sApp.mInstallDir, "fonts/SourceCodePro-Regular.ttf"), fontSize);
mFont.AddAlternate("Segoe UI Symbol", fontSize);
mFont.AddAlternate("Segoe UI Historic", fontSize);
mFont.AddAlternate("Segoe UI Emoji", fontSize);
mBoard = new Board();
mBoard.Load(dialog.FileNames[0]);
mMainWindow = new WidgetWindow(null, "LogViewer", 0, 0, 1600, 1200, windowFlags, mBoard);
//mMainWindow.mWindowKeyDownDelegate.Add(new => SysKeyDown);
mMainWindow.SetMinimumSize(480, 360);
mMainWindow.mIsMainWindow = true;
});
subMenu.AddMenuItem("Read &MemLog", "Ctrl+M", new (menu) =>
{
var dialog = new MemLogDialog();
dialog.PopupWindow(mMainWindow);
});
subMenu.AddMenuItem("&Reload", "Ctrl+R", new (menu) =>
{
mBoard.Reload();
});
}
public void Fail(String str, params Object[] paramVals)
{
var errStr = scope String();
errStr.AppendF(str, paramVals);
errStr.AppendF(str, params paramVals);
Fail(errStr);
}

View file

@ -0,0 +1,21 @@
using Beefy.theme.dark;
using Beefy.widgets;
namespace LogViewer;
class MemLogDialog : DarkDialog
{
EditWidget mEditWidget;
public this() : base("Open MemLog", "MemLog Name")
{
mDefaultButton = AddButton("OK", new (evt) =>
{
var name = mEditWidget.GetText(.. scope .());
gApp.mBoard.LoadMemLog(name);
});
mEscButton = AddButton("Cancel", new (evt) => Close());
mEditWidget = AddEdit("");
}
}

View file

@ -10,6 +10,7 @@
#include "util/Vector.h"
#include "util/PerfTimer.h"
#include "util/TLSingleton.h"
#include "util/MemLogger.h"
#include "img/ImgEffects.h"
#include "util/AllocDebug.h"
@ -927,3 +928,35 @@ BF_EXPORT void BF_CALLTYPE BF_Test()
for (int i : iArr)
OutputDebugStrF("Hey %d\n", i);
}
BF_EXPORT void* BF_CALLTYPE MemLogger_Create(const char* memName, int size)
{
MemLogger* memLogger = new MemLogger();
if (!memLogger->Create(memName, size))
{
delete memLogger;
return NULL;
}
return memLogger;
}
BF_EXPORT void BF_CALLTYPE MemLogger_Write(MemLogger* memLogger, void* ptr, int size)
{
memLogger->Write(ptr, size);
}
BF_EXPORT void BF_CALLTYPE MemLogger_Delete(MemLogger* memLogger)
{
delete memLogger;
}
BF_EXPORT const char* BF_CALLTYPE MemLogger_Get(const char* memName)
{
MemLogger memLogger;
String& outString = *gBeefySys_TLStrReturn.Get();
outString.Clear();
if (!memLogger.Get(memName, outString))
return NULL;
return outString.c_str();
}

View file

@ -1950,6 +1950,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"</Co
<ClCompile Include="util\MappedFile.cpp" />
<ClCompile Include="util\MathUtils.cpp" />
<ClCompile Include="util\Matrix4.cpp" />
<ClCompile Include="util\MemLogger.cpp" />
<ClCompile Include="util\MTRand.cpp" />
<ClCompile Include="util\PerfTimer.cpp" />
<ClCompile Include="util\Point.cpp" />
@ -2189,6 +2190,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"</Co
<ClInclude Include="util\MappedFile.h" />
<ClInclude Include="util\MathUtils.h" />
<ClInclude Include="util\Matrix4.h" />
<ClInclude Include="util\MemLogger.h" />
<ClInclude Include="util\MTRand.h" />
<ClInclude Include="util\MultiDictionary.h" />
<ClInclude Include="util\MultiHashSet.h" />

View file

@ -743,6 +743,9 @@
<ClCompile Include="img\BMPData.cpp">
<Filter>src\img</Filter>
</ClCompile>
<ClCompile Include="util\MemLogger.cpp">
<Filter>src\util</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Common.h">
@ -1147,6 +1150,9 @@
<ClInclude Include="img\BMPData.h">
<Filter>src\img</Filter>
</ClInclude>
<ClInclude Include="util\MemLogger.h">
<Filter>src\util</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="third_party\libffi\i686-pc-cygwin\src\x86\win32.asm">

View file

@ -880,6 +880,7 @@
<ClCompile Include="util\Json.cpp" />
<ClCompile Include="util\MappedFile.cpp" />
<ClCompile Include="util\Matrix4.cpp" />
<ClCompile Include="util\MemLogger.cpp" />
<ClCompile Include="util\PerfTimer.cpp" />
<ClCompile Include="util\Point.cpp" />
<ClCompile Include="util\PolySpline.cpp" />
@ -1047,6 +1048,7 @@
<ClInclude Include="util\Json.h" />
<ClInclude Include="util\MappedFile.h" />
<ClInclude Include="util\Matrix4.h" />
<ClInclude Include="util\MemLogger.h" />
<ClInclude Include="util\PerfTimer.h" />
<ClInclude Include="util\Point.h" />
<ClInclude Include="util\PolySpline.h" />

View file

@ -593,6 +593,9 @@
<ClCompile Include="img\BMPData.cpp">
<Filter>src\img</Filter>
</ClCompile>
<ClCompile Include="util\MemLogger.cpp">
<Filter>src\util</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Common.h">
@ -913,6 +916,9 @@
<ClInclude Include="img\BMPData.h">
<Filter>src\img</Filter>
</ClInclude>
<ClInclude Include="util\MemLogger.h">
<Filter>src\util</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="third_party\libffi\i686-pc-cygwin\src\x86\win32.asm">

View file

@ -294,6 +294,7 @@ file(GLOB SRC_FILES
util/MappedFile.cpp
util/MathUtils.cpp
util/Matrix4.cpp
util/MemLogger.cpp
util/PerfTimer.cpp
util/Point.cpp
util/PolySpline.cpp

View file

@ -660,7 +660,7 @@ void Beefy::ExactMinimalDoubleToStr(double d, char* str)
static char* StbspCallback(char *buf, void *user, int len)
{
((String*)user)->Append(buf, len);
((StringImpl*)user)->Append(buf, len);
return buf;
}
@ -821,6 +821,12 @@ String Beefy::vformat(const char* fmt, va_list argPtr)
BF_stbsp_vsprintfcb(StbspCallback, (void*)&str, buf, fmt, argPtr);
return str;
}
void Beefy::vformat(StringImpl& str, const char* fmt, va_list argPtr)
{
char buf[STB_SPRINTF_MIN];
BF_stbsp_vsprintfcb(StbspCallback, (void*)&str, buf, fmt, argPtr);
}
#endif
String Beefy::StrFormat(const char* fmt ...)

View file

@ -195,6 +195,7 @@ uint64 BFGetTickCountMicro();
uint64 BFGetTickCountMicroFast();
extern String vformat(const char* fmt, va_list argPtr);
extern void vformat(StringImpl& str, const char* fmt, va_list argPtr);
extern String StrFormat(const char* fmt ...);
void ExactMinimalFloatToStr(float f, char* str);
void ExactMinimalDoubleToStr(double d, char* str);

View file

@ -832,6 +832,17 @@ BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetComputerName(char* outStr, int* inOutS
// BfpProcess
struct BfpProcess
{
pid_t mProcessId;
String mImageName;
BfpProcess()
{
mProcessId = -1;
}
};
BFP_EXPORT intptr BFP_CALLTYPE BfpProcess_GetCurrentId()
{
return getpid();
@ -844,18 +855,87 @@ BFP_EXPORT bool BFP_CALLTYPE BfpProcess_IsRemoteMachine(const char* machineName)
BFP_EXPORT BfpProcess* BFP_CALLTYPE BfpProcess_GetById(const char* machineName, int processId, BfpProcessResult* outResult)
{
#ifdef BF_PLATFORM_LINUX
pid_t pid = static_cast<pid_t>(processId);
char proc_dir[64];
snprintf(proc_dir, sizeof(proc_dir), "/proc/%d", pid);
if (access(proc_dir, F_OK) != 0)
{
OUTRESULT(BfpProcessResult_NotFound);
return NULL;
}
BfpProcess* process = new BfpProcess();
process->mProcessId = pid;
return process;
#else
NOT_IMPL;
return NULL;
#endif
}
BFP_EXPORT void BFP_CALLTYPE BfpProcess_Enumerate(const char* machineName, BfpProcess** outProcesses, int* inOutProcessesSize, BfpProcessResult* outResult)
{
#ifdef BF_PLATFORM_LINUX
DIR* procDir = opendir("/proc");
if (!procDir)
{
*inOutProcessesSize = 0;
return;
}
Beefy::Array<BfpProcess*> processList;
struct dirent* entry;
while ((entry = readdir(procDir)) != NULL)
{
const char* name = entry->d_name;
bool is_pid = name[0] != '\0';
for (const char* p = name; *p; ++p)
{
if (*p < '0' || *p > '9')
{
is_pid = false;
break;
}
}
if (is_pid)
{
pid_t pid = static_cast<pid_t>(atoi(name));
if (pid == 0)
continue;
BfpProcess* proc = new BfpProcess();
proc->mProcessId = pid;
processList.Add(proc);
}
}
closedir(procDir);
if (static_cast<int>(processList.size()) > *inOutProcessesSize)
{
*inOutProcessesSize = static_cast<int>(processList.size());
OUTRESULT(BfpProcessResult_InsufficientBuffer);
for (BfpProcess* p_del : processList)
delete p_del;
return;
}
for (size_t i = 0; i < processList.size(); ++i)
outProcesses[i] = processList[i];
*inOutProcessesSize = static_cast<int>(processList.size());
OUTRESULT(BfpProcessResult_Ok);
#else
NOT_IMPL;
#endif
}
BFP_EXPORT void BFP_CALLTYPE BfpProcess_Release(BfpProcess* process)
{
NOT_IMPL;
delete process;
}
BFP_EXPORT bool BFP_CALLTYPE BfpProcess_WaitFor(BfpProcess* process, int waitMS, int* outExitCode, BfpProcessResult* outResult)
@ -865,18 +945,49 @@ BFP_EXPORT bool BFP_CALLTYPE BfpProcess_WaitFor(BfpProcess* process, int waitMS,
BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetMainWindowTitle(BfpProcess* process, char* outTitle, int* inOutTitleSize, BfpProcessResult* outResult)
{
NOT_IMPL;
String title;
TryStringOut(title, outTitle, inOutTitleSize, (BfpResult*)outResult);
}
BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetProcessName(BfpProcess* process, char* outName, int* inOutNameSize, BfpProcessResult* outResult)
{
if (process->mImageName.IsEmpty())
{
#ifdef BF_PLATFORM_LINUX
char path[PATH_MAX];
snprintf(path, sizeof(path), "/proc/%d/exe", process->mProcessId);
char name_buf[PATH_MAX] = {0};
ssize_t len = readlink(path, name_buf, sizeof(name_buf) - 1);
if (len != -1)
{
name_buf[len] = '\0';
process->mImageName.Append(basename(name_buf));
}
else
{
// Only 15 characters of the process name are returned by this, but it works for all processes.
snprintf(path, sizeof(path), "/proc/%d/comm", process->mProcessId);
FILE* fp = fopen(path, "r");
if (fp)
{
if (fgets(name_buf, sizeof(name_buf), fp)) {
size_t len = strcspn(name_buf, "\n");
name_buf[len] = '\0';
process->mImageName.Append(name_buf, len);
}
fclose(fp);
}
}
#else
NOT_IMPL;
#endif
}
TryStringOut(process->mImageName, outName, inOutNameSize, (BfpResult*)outResult);
}
BFP_EXPORT int BFP_CALLTYPE BfpProcess_GetProcessId(BfpProcess* process)
{
NOT_IMPL;
return 0;
return process->mProcessId;
}
// BfpSpawn
@ -1822,7 +1933,27 @@ BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Create(const char* path, BfpFileResult
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Rename(const char* oldName, const char* newName, BfpFileResult* outResult)
{
NOT_IMPL;
if (rename(oldName, newName) != 0)
{
switch (errno)
{
case EEXIST:
OUTRESULT(BfpFileResult_AlreadyExists);
break;
case ENOTEMPTY:
OUTRESULT(BfpFileResult_NotEmpty);
break;
case EISDIR:
case ENOTDIR:
OUTRESULT(BfpFileResult_InvalidParameter);
break;
default:
OUTRESULT(BfpFileResult_UnknownError);
break;
}
}
else
OUTRESULT(BfpFileResult_Ok);
}
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Delete(const char* path, BfpFileResult* outResult)
@ -2372,9 +2503,148 @@ BFP_EXPORT void BFP_CALLTYPE BfpFile_GetFullPath(const char* inPath, char* outPa
TryStringOut(str, outPath, inOutPathSize, (BfpResult*)outResult);
}
BFP_EXPORT void BFP_CALLTYPE BfpFile_GetActualPath(const char* inPath, char* outPath, int* inOutPathSize, BfpFileResult* outResult)
BFP_EXPORT void BFP_CALLTYPE BfpFile_GetActualPath(const char* inPathC, char* outPathC, int* inOutPathSize, BfpFileResult* outResult)
{
NOT_IMPL;
String inPath = inPathC;
String outPath;
// Check for '/../' backtracking - handle those first
{
int i = 0;
int32 lastComponentStart = -1;
while (i < inPath.mLength)
{
// Skip until path separator
while ((i < inPath.mLength) && (inPath[i] != DIR_SEP_CHAR) && (inPath[i] != DIR_SEP_CHAR_ALT))
++i;
if (lastComponentStart != -1)
{
if ((i - lastComponentStart == 2) && (inPath[lastComponentStart] == '.') && (inPath[lastComponentStart + 1] == '.'))
{
// Backtrack
while ((lastComponentStart > 0) &&
((inPath[lastComponentStart - 1] == DIR_SEP_CHAR) || (inPath[lastComponentStart - 1] == DIR_SEP_CHAR_ALT)))
lastComponentStart--;
while ((lastComponentStart > 0) && (inPath[lastComponentStart - 1] != DIR_SEP_CHAR) && (inPath[lastComponentStart - 1] != DIR_SEP_CHAR_ALT))
lastComponentStart--;
inPath.Remove(lastComponentStart, i - lastComponentStart + 1);
i = lastComponentStart;
continue;
}
else if ((i - lastComponentStart == 1) && (inPath[lastComponentStart] == '.'))
{
inPath.Remove(lastComponentStart, i - lastComponentStart + 1);
i = lastComponentStart;
continue;
}
}
++i;
// Ignore multiple slashes in a row
while ((i < inPath.mLength) && ((inPath[i] == DIR_SEP_CHAR) || (inPath[i] == DIR_SEP_CHAR_ALT)))
++i;
lastComponentStart = i;
}
}
int32 i = 0;
int length = (int)inPath.length();
if (length >= 1)
{
// Handle root paths starting with '/' or '\'
if ((inPath[0] == DIR_SEP_CHAR) || (inPath[0] == DIR_SEP_CHAR_ALT))
{
i++; // start after initial slash
outPath.Append(DIR_SEP_CHAR);
}
else
{
// Relative path - prepend current working directory
char cwd[PATH_MAX];
if (getcwd(cwd, PATH_MAX) != NULL)
{
outPath.Append(cwd);
if (outPath[outPath.length() - 1] != DIR_SEP_CHAR)
outPath.Append(DIR_SEP_CHAR);
}
}
}
int32 lastComponentStart = i;
bool addSeparator = false;
String subName;
while (i < length)
{
// skip until path separator
while ((i < length) && (inPath[i] != DIR_SEP_CHAR) && (inPath[i] != DIR_SEP_CHAR_ALT))
++i;
if (addSeparator)
outPath.Append(DIR_SEP_CHAR);
subName.Clear();
subName = inPath.Substring(0, i);
for (int j = 0; j < (int)subName.length(); j++)
if (subName[j] == DIR_SEP_CHAR_ALT)
subName[j] = DIR_SEP_CHAR;
String parentPath;
String componentName;
int32 lastSep = subName.LastIndexOf(DIR_SEP_CHAR);
if (lastSep != -1)
{
parentPath = subName.Substring(0, lastSep);
if (parentPath.length() == 0)
parentPath = "/";
componentName = subName.Substring(lastSep + 1);
}
else
{
parentPath = ".";
componentName = subName;
}
bool found = false;
DIR* dir = opendir(parentPath.c_str());
if (dir != NULL)
{
struct dirent* entry = NULL;
while ((entry = readdir(dir)) != NULL)
{
// Linux is case-sensitive, but we do a case-insensitive comparison
// to help find the correct case for the file
if (strcasecmp(entry->d_name, componentName.c_str()) == 0)
{
outPath.Append(entry->d_name);
found = true;
break;
}
}
closedir(dir);
}
if (!found)
{
// If not found, use the original component name
outPath.Append(inPath.Substring(lastComponentStart, i - lastComponentStart));
}
++i;
// Ignore multiple slashes in a row
while ((i < length) && ((inPath[i] == DIR_SEP_CHAR) || (inPath[i] == DIR_SEP_CHAR_ALT)))
++i;
lastComponentStart = i;
addSeparator = true;
}
TryStringOut(outPath, outPathC, inOutPathSize, (BfpResult*)outResult);
}
// BfpFindFileData

View file

@ -553,12 +553,40 @@ struct BfpOverlappedFile : BfpOverlapped
}
};
struct OverlappedReadResult : OVERLAPPED
{
BfpFile* mFile;
intptr mBytesRead;
DWORD mErrorCode;
Array<uint8> mData;
bool mPending;
bool mAttemptedRead;
OverlappedReadResult()
{
mFile = NULL;
mBytesRead = 0;
mErrorCode = 0;
mPending = false;
mAttemptedRead = false;
}
void* GetPtr()
{
return mData.mVals;
}
};
struct BfpAsyncData
{
BfpFile* mFile;
Array<uint8> mQueuedData;
HANDLE mEvent;
OverlappedReadResult mOverlappedResult;
BfpAsyncData()
BfpAsyncData(BfpFile* file)
{
mFile = file;
mEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
}
@ -587,6 +615,98 @@ struct BfpAsyncData
return false;
}
}
int ReadQueued(void* buffer, int size, int timeoutMS, bool& didWait)
{
gBfpCritSect.Lock();
if (mQueuedData.mSize == 0)
{
if (mOverlappedResult.mPending)
{
gBfpCritSect.Unlock();
WaitAndResetEvent(timeoutMS);
didWait = true;
gBfpCritSect.Lock();
}
if (mQueuedData.mSize == 0)
{
gBfpCritSect.Unlock();
return 0;
}
}
int readSize = BF_MIN(size, mQueuedData.mSize);
memcpy(buffer, mQueuedData.mVals, readSize);
mQueuedData.RemoveRange(0, readSize);
gBfpCritSect.Unlock();
return readSize;
}
void HandleResult(uint32 errorCode, uint32 bytesRead)
{
AutoCrit autoCrit(gBfpCritSect);
BF_ASSERT(mOverlappedResult.mPending);
mOverlappedResult.mPending = false;
mOverlappedResult.mErrorCode = errorCode;
mOverlappedResult.mBytesRead = bytesRead;
if (mOverlappedResult.mAttemptedRead) // Already tried to read and failed
{
mQueuedData.Insert(mQueuedData.mSize, (uint8*)mOverlappedResult.GetPtr(), bytesRead);
mOverlappedResult.mData.Clear();
}
SetEvent();
}
void AbortOverlapped()
{
AutoCrit autoCrit(gBfpCritSect);
BF_ASSERT(mOverlappedResult.mPending);
mOverlappedResult.mPending = false;
mOverlappedResult.mData.Clear();
}
int FinishRead(void* buffer, int size, DWORD& errorCode)
{
AutoCrit autoCrit(gBfpCritSect);
BF_ASSERT(!mOverlappedResult.mAttemptedRead);
mOverlappedResult.mAttemptedRead = true;
if (mOverlappedResult.mPending)
{
return -2; // Still executing
}
if ((mOverlappedResult.mErrorCode != 0) && (mOverlappedResult.mBytesRead == 0))
{
mOverlappedResult.mData.Clear();
errorCode = mOverlappedResult.mErrorCode;
return -1;
}
BF_ASSERT(size >= mOverlappedResult.mBytesRead);
memcpy(buffer, mOverlappedResult.GetPtr(), mOverlappedResult.mBytesRead);
int bytesRead = (int)mOverlappedResult.mBytesRead;
mOverlappedResult.mData.Clear();
return bytesRead;
}
OverlappedReadResult* StartOverlapped(int size)
{
AutoCrit autoCrit(gBfpCritSect);
BF_ASSERT(!mOverlappedResult.mPending);
BF_ASSERT(mOverlappedResult.mData.IsEmpty());
memset(&mOverlappedResult, 0, sizeof(OVERLAPPED));
mOverlappedResult.mFile = mFile;
mOverlappedResult.mBytesRead = 0;
mOverlappedResult.mPending = true;
mOverlappedResult.mAttemptedRead = false;
mOverlappedResult.mErrorCode = -1;
mOverlappedResult.mData.Resize(size);
return &mOverlappedResult;
}
};
struct BfpFile
@ -618,6 +738,12 @@ struct BfpFile
}
};
static void WINAPI OverlappedReadComplete(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
OverlappedReadResult* readResult = (OverlappedReadResult*)lpOverlapped;
readResult->mFile->mAsyncData->HandleResult(dwErrorCode, dwNumberOfBytesTransfered);
}
struct BfpFileWatcher : public BfpOverlapped
{
String mPath;
@ -2952,7 +3078,7 @@ BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* path, BfpFileCreateK
if (isOverlapped)
{
bfpFile->mAsyncData = new BfpAsyncData();
bfpFile->mAsyncData = new BfpAsyncData(bfpFile);
}
return bfpFile;
@ -3048,7 +3174,7 @@ BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* path, BfpFileCreateK
bfpFile->mHandle = handle;
if ((createFlags & BfpFileCreateFlag_AllowTimeouts) != 0)
bfpFile->mAsyncData = new BfpAsyncData();
bfpFile->mAsyncData = new BfpAsyncData(bfpFile);
if ((createFlags & BfpFileCreateFlag_Pipe) != 0)
bfpFile->mIsPipe = true;
@ -3142,21 +3268,6 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Write(BfpFile* file, const void* buffer,
return bytesWritten;
}
struct OverlappedReadResult : OVERLAPPED
{
BfpFile* mFile;
intptr mBytesRead;
DWORD mErrorCode;
};
static void WINAPI OverlappedReadComplete(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
OverlappedReadResult* readResult = (OverlappedReadResult*)lpOverlapped;
readResult->mErrorCode = dwErrorCode;
readResult->mBytesRead = dwNumberOfBytesTransfered;
readResult->mFile->mAsyncData->SetEvent();
}
BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult)
{
bool forceNormalRead = false;
@ -3208,6 +3319,23 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr
}
}
if (file->mAsyncData != NULL)
{
bool didWait = false;
int readSize = file->mAsyncData->ReadQueued(buffer, (int)size, timeoutMS, didWait);
if (readSize > 0)
{
OUTRESULT(BfpFileResult_Ok);
return readSize;
}
if (didWait)
{
OUTRESULT(BfpFileResult_Timeout);
return 0;
}
}
if ((timeoutMS != -1) && (!forceNormalRead))
{
if (file->mAsyncData == NULL)
@ -3218,29 +3346,34 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr
while (true)
{
OverlappedReadResult overlapped;
memset(&overlapped, 0, sizeof(OverlappedReadResult));
overlapped.mFile = file;
OverlappedReadResult* overlapped = file->mAsyncData->StartOverlapped((int)size);
//TODO: this doesn't set file stream location. It only works for streams like pipes, sockets, etc
if (::ReadFileEx(file->mHandle, buffer, (uint32)size, &overlapped, OverlappedReadComplete))
if (::ReadFileEx(file->mHandle, overlapped->GetPtr(), (uint32)size, overlapped, OverlappedReadComplete))
{
if (!file->mAsyncData->WaitAndResetEvent(timeoutMS))
file->mAsyncData->WaitAndResetEvent(timeoutMS);
DWORD errorCode = 0;
int readResult = file->mAsyncData->FinishRead(buffer, (int)size, errorCode);
if (readResult != -2) // Still executing
{
::CancelIoEx(file->mHandle, &overlapped);
// There's a chance we completed before we were cancelled -- check on that
if (!file->mAsyncData->WaitAndResetEvent(0))
if (errorCode == 0)
{
OUTRESULT(BfpFileResult_Ok);
return readResult;
}
else if (errorCode == ERROR_OPERATION_ABORTED)
{
OUTRESULT(BfpFileResult_Timeout);
return 0;
}
}
if (overlapped.mErrorCode == 0)
else
{
OUTRESULT(BfpFileResult_Ok);
return 0;
}
else if (overlapped.mErrorCode == ERROR_OPERATION_ABORTED)
}
else
{
OUTRESULT(BfpFileResult_Timeout);
return 0;
@ -3248,21 +3381,19 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr
}
else
{
file->mAsyncData->AbortOverlapped();
int lastError = ::GetLastError();
if (lastError == ERROR_PIPE_LISTENING)
{
overlapped.hEvent = file->mAsyncData->mEvent;
if (!::ConnectNamedPipe(file->mHandle, &overlapped))
overlapped->hEvent = file->mAsyncData->mEvent;
if (!::ConnectNamedPipe(file->mHandle, overlapped))
{
int lastError = ::GetLastError();
if (lastError == ERROR_IO_PENDING)
{
if (!file->mAsyncData->WaitAndResetEvent(timeoutMS))
{
::CancelIoEx(file->mHandle, &overlapped);
// Clear event set by CancelIoEx
file->mAsyncData->WaitAndResetEvent(0);
OUTRESULT(BfpFileResult_Timeout);
return 0;
}
@ -3278,9 +3409,6 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr
return 0;
}
}
OUTRESULT(BfpFileResult_Ok);
return overlapped.mBytesRead;
}
}

View file

@ -430,6 +430,11 @@ void BpCmdTarget::Enter(const char* name, va_list args)
intptr val = va_arg(args, intptr);
paramSize += 4; // int32
}
else if (nextC == 'p')
{
intptr val = va_arg(args, intptr);
paramSize += 8; // int64
}
else if (nextC == 's')
{
const char* str = ToStrPtr(va_arg(args, char*));
@ -488,6 +493,10 @@ void BpCmdTarget::Enter(const char* name, va_list args)
{
BPCMD_MEMBER(int32) = (int32)va_arg(args, intptr);
}
else if (nextC == 'p')
{
BPCMD_MEMBER(int64) = (int64)va_arg(args, intptr);
}
else if (nextC == 's')
{
const char* str = ToStrPtr(va_arg(args, char*));
@ -1120,6 +1129,10 @@ void BpManager::ThreadProc()
{
zoneName->mSize += 4;
}
else if (nextC == 'p')
{
zoneName->mSize += 8;
}
else if (nextC == 's')
{
isDyn = true;
@ -1175,6 +1188,10 @@ void BpManager::ThreadProc()
{
checkDataIn += 4;
}
else if (nextC == 'p')
{
checkDataIn += 8;
}
else if (nextC == 's')
{
int len = (int)strlen((const char*)checkDataIn);

View file

@ -0,0 +1,176 @@
#include "MemLogger.h"
USING_NS_BF;
struct MemLogger_Header
{
public:
int mHead;
int mTail;
int mSize;
};
MemLogger::MemLogger()
{
mFileMap = NULL;
mMemBuffer = NULL;
mBufferSize = 0;
mTotalWriteSize = 0;
mNoOverflow = false;
}
MemLogger::~MemLogger()
{
#ifdef BF_PLATFORM_WINDOWS
if (mMemBuffer != NULL)
::UnmapViewOfFile(mMemBuffer);
if (mFileMap != NULL)
::CloseHandle(mFileMap);
#endif
}
void MemLogger::Write(const void* ptr, int size)
{
if (mMemBuffer == NULL)
return;
int dataSize = mBufferSize - sizeof(MemLogger_Header);
void* dataPtr = (uint8*)mMemBuffer + sizeof(MemLogger_Header);
if (mNoOverflow)
size = BF_MIN(size, dataSize - mTotalWriteSize - 1);
if (size <= 0)
return;
MemLogger_Header* header = (MemLogger_Header*)mMemBuffer;
bool wasWrapped = header->mHead < header->mTail;
int writeSize = BF_MIN(size, dataSize - header->mHead);
memcpy((char*)dataPtr + header->mHead, ptr, writeSize);
size -= writeSize;
header->mHead += writeSize;
while (header->mHead >= dataSize)
header->mHead -= dataSize;
if (size > 0)
{
int writeSize2 = BF_MIN(size, dataSize - header->mHead);
memcpy((char*)dataPtr + header->mHead, (char*)ptr + writeSize, writeSize2);
header->mHead += writeSize2;
while (header->mHead >= dataSize)
header->mHead -= dataSize;
}
mTotalWriteSize += writeSize;
if (mTotalWriteSize >= dataSize)
{
header->mTail = header->mHead + 1;
if (header->mTail > dataSize)
header->mTail -= dataSize;
}
}
bool Beefy::MemLogger::Create(const StringImpl& memName, int size)
{
#ifdef BF_PLATFORM_WINDOWS
String sharedName = "MemLogger_" + memName;
HANDLE hMapFile = CreateFileMappingA(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // maximum object size (high-order DWORD)
size, // maximum object size (low-order DWORD)
sharedName.c_str()); // name of mapping object
if (hMapFile == NULL)
return false;
mMemBuffer = MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
size);
if (mMemBuffer == NULL)
return false;
mBufferSize = size;
MemLogger_Header* header = (MemLogger_Header*)mMemBuffer;
header->mHead = 0;
header->mTail = 0;
header->mSize = size;
return true;
#else
return false;
#endif
}
bool Beefy::MemLogger::Get(const StringImpl& memName, String& outStr)
{
#ifdef BF_PLATFORM_WINDOWS
String sharedName = "MemLogger_" + memName;
HANDLE hMapFile = ::OpenFileMappingA(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, sharedName.c_str());
if (hMapFile == NULL)
return false;
void* memPtr = MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
sizeof(MemLogger_Header));
MemLogger_Header* header = (MemLogger_Header*)(memPtr);
int size = header->mSize;
UnmapViewOfFile(memPtr);
memPtr = MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
size);
if (memPtr == NULL)
return false;
::CloseHandle(hMapFile);
header = (MemLogger_Header*)(memPtr);
int dataSize = header->mSize - sizeof(MemLogger_Header);
void* dataPtr = (uint8*)memPtr + sizeof(MemLogger_Header);
if (header->mHead >= header->mTail)
{
// Not wrapped around
outStr.Insert(outStr.mLength, (char*)dataPtr + header->mTail, header->mHead - header->mTail);
}
else
{
outStr.Insert(outStr.mLength, (char*)dataPtr + header->mTail, dataSize - header->mTail);
outStr.Insert(outStr.mLength, (char*)dataPtr, header->mHead);
}
return true;
#else
return false;
#endif
}
void Beefy::MemLogger::Log(const char* fmt ...)
{
if (mMemBuffer == NULL)
return;
StringT<4096> str;
va_list argList;
va_start(argList, fmt);
vformat(str, fmt, argList);
va_end(argList);
Write(str.c_str(), str.mLength);
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "../Common.h"
NS_BF_BEGIN
class MemLogger
{
public:
HANDLE mFileMap;
void* mMemBuffer;
int mBufferSize;
int mTotalWriteSize;
bool mNoOverflow;
public:
MemLogger();
~MemLogger();
bool Create(const StringImpl& memName, int size);
bool Get(const StringImpl& memName, String& outStr);
void Log(const char* fmt ...);
void Write(const void* ptr, int size);
};
NS_BF_END

View file

@ -119,13 +119,13 @@ typedef Rect<double> RectD;
typedef Rect<float> RectF;
typedef Rect<int32> RectI32;
NS_BF_END;
template <>
struct BeefHash<RectI32>
struct BeefHash<Beefy::RectI32>
{
size_t operator()(RectI32 val)
size_t operator()(Beefy::RectI32 val)
{
return (size_t)val.x * 4790557 + (size_t)val.y * 6578863 + (size_t)val.width * 6273881 + (size_t)val.height * 9501077;
}
};
NS_BF_END;

View file

@ -3461,6 +3461,8 @@ namespace IDE
delete mWorkspace.mDir;
mWorkspace.mDir = newPath;
}
else if (mWorkspace.mDir == null)
mWorkspace.mDir = new String();
List<String> platforms = scope List<String>();
if (IDEApp.sPlatform32Name != null)

View file

@ -1179,14 +1179,18 @@ namespace IDE.ui
if (!docString.IsWhiteSpace)
{
curY += font.GetLineSpacing() + GS!(4);
if (g != null)
{
let docY = curY + font.GetLineSpacing() + GS!(4);
using (g.PushColor(gApp.mSettings.mUISettings.mColors.mAutoCompleteDocText))
docHeight = g.DrawString(docString, curX, curY, .Left, maxDocWidth, .Wrap);
docHeight = g.DrawString(docString, curX, docY, .Left, maxDocWidth, .Wrap);
}
else
docHeight = font.GetWrapHeight(docString, maxDocWidth);
curY += docHeight;
}
extWidth = Math.Max(extWidth, Math.Min(font.GetWidth(docString), maxDocWidth) + GS!(48));

View file

@ -95,6 +95,8 @@ class GitManager
if (!File.Exists(gitPath))
gitPath.Clear();
}
psi.UseShellExecute = false;
#endif
if (gitPath.IsEmpty)
gitPath.Set("git");
@ -103,7 +105,6 @@ class GitManager
psi.SetArguments(mArgs);
if (mPath != null)
psi.SetWorkingDirectory(mPath);
psi.UseShellExecute = false;
psi.RedirectStandardError = true;
psi.RedirectStandardOutput = true;
psi.CreateNoWindow = true;

View file

@ -179,7 +179,12 @@ namespace IDE.util
if (!CheckInit())
return;
String beefBuildPath = scope $"{gApp.mInstallDir}BeefBuild.exe";
#if BF_PLATFORM_WINDOWS
let ext = ".exe";
#else
let ext = "";
#endif
String beefBuildPath = scope $"{gApp.mInstallDir}BeefBuild{ext}";
String args = scope $"-run";
var execInst = gApp.DoRun(beefBuildPath, args, path, .None);
execInst?.mAutoDelete = false;

View file

@ -3296,6 +3296,9 @@ void BfAutoComplete::CheckLabel(BfIdentifierNode* identifierNode, BfAstNode* pre
String filter;
if (identifierNode != NULL)
{
if (mModule->mCurMethodState == NULL)
return;
if ((mModule->mCompiler->mResolvePassData != NULL) && (scopeData != NULL))
{
auto rootMethodState = mModule->mCurMethodState->GetRootMethodState();

View file

@ -7223,14 +7223,32 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
//HashSet<BfTypeDef*> internalTypeDefs;
BfProject* corlibProject = NULL;
auto _GetRequiredType = [&](const StringImpl& typeName, int genericArgCount = 0)
{
auto typeDef = mSystem->FindTypeDef(typeName, genericArgCount);
BfTypeDef* ambigiousTypeDef = NULL;
auto typeDef = mSystem->FindTypeDef(typeName, genericArgCount, NULL, {}, &ambigiousTypeDef);
if (typeDef == NULL)
{
mPassInstance->Fail(StrFormat("Unable to find system type: %s", typeName.c_str()));
mHasRequiredTypes = false;
}
if (ambigiousTypeDef != NULL)
{
mPassInstance->Fail(StrFormat("Found multiple declarations of require type '%s'", typeName.c_str()), typeDef->GetRefNode());
mPassInstance->MoreInfo("See additional declaration", ambigiousTypeDef->GetRefNode());
if (typeDef->mProject != corlibProject)
{
auto rootTypeDef = mSystem->FindTypeDef(typeName, genericArgCount, corlibProject);
if (rootTypeDef != NULL)
typeDef = rootTypeDef;
}
}
if (corlibProject == NULL)
corlibProject = typeDef->mProject;
return typeDef;
};

View file

@ -7125,7 +7125,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance*
if (mDeferCallData != NULL)
{
if (mDeferCallData->mFuncAlloca_Orig == func)
if ((func) && (mDeferCallData->mFuncAlloca_Orig == func))
mModule->AddDeferredCall(BfModuleMethodInstance(methodInstance, mDeferCallData->mFuncAlloca), irArgs, mDeferCallData->mScopeAlloc, mDeferCallData->mRefNode, bypassVirtual, false, true);
else
mModule->AddDeferredCall(BfModuleMethodInstance(methodInstance, func), irArgs, mDeferCallData->mScopeAlloc, mDeferCallData->mRefNode, bypassVirtual);
@ -19701,7 +19701,7 @@ void BfExprEvaluator::DoInvocation(BfInvocationExpression* invocationExpr)
{
arrSize = constant->mInt32;
}
else
else if (constant->mConstType != BfConstType_Undef)
mModule->Fail("Non-negative integer expected", indexerExpr->mArguments[0]);
}
}

View file

@ -552,6 +552,31 @@ void BfIRCodeGen::FixValues(llvm::StructType* structType, llvm::SmallVector<llvm
}
}
void BfIRCodeGen::FixValues(llvm::StructType* structType, llvm::SmallVector<llvm::Constant*, 8>& values)
{
if (values.size() >= structType->getNumElements())
return;
int readIdx = (int)values.size() - 1;
values.resize(structType->getNumElements());
for (int i = (int)values.size() - 1; i >= 0; i--)
{
if (values[readIdx]->getType() == structType->getElementType(i))
{
values[i] = values[readIdx];
readIdx--;
}
else if (structType->getElementType(i)->isArrayTy())
{
values[i] = llvm::ConstantAggregateZero::get(structType->getElementType(i));
}
else
{
BF_FATAL("Malformed structure values");
}
}
}
void BfIRCodeGen::FixIndexer(llvm::Value*& val)
{
if ((int)val->getType()->getScalarSizeInBits() > mPtrSize * 8)
@ -1285,6 +1310,8 @@ void BfIRCodeGen::Read(BfIRTypedValue& typedValue, BfIRCodeGenEntry** codeGenEnt
}
else if (auto structType = llvm::dyn_cast<llvm::StructType>(type->mLLVMType))
{
FixValues(structType, values);
for (int i = 0; i < (int)values.size(); i++)
{
if (values[i]->getType() != structType->getElementType(i))

View file

@ -178,6 +178,7 @@ public:
public:
void InitTarget();
void FixValues(llvm::StructType* structType, llvm::SmallVector<llvm::Value*, 8>& values);
void FixValues(llvm::StructType* structType, llvm::SmallVector<llvm::Constant*, 8>& values);
void FixIndexer(llvm::Value*& val);
void FixTypedValue(BfIRTypedValue& typedValue);
BfTypeCode GetTypeCode(llvm::Type* type, bool isSigned);

View file

@ -13415,6 +13415,8 @@ BfIRValue BfModule::CastToFunction(BfAstNode* srcNode, const BfTypedValue& targe
}
if ((mCompiler->mOptions.mAllowHotSwapping) && (!mIsComptimeModule))
bindFuncVal = mBfIRBuilder->RemapBindFunction(bindFuncVal);
if ((bindFuncVal.IsFake()) && (!mBfIRBuilder->mIgnoreWrites))
return GetDefaultValue(GetPrimitiveType(BfTypeCode_IntPtr));
return mBfIRBuilder->CreatePtrToInt(bindFuncVal, BfTypeCode_IntPtr);
}

View file

@ -1837,6 +1837,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
else
{
Fail("Sized array type expected", typeRef);
AddErrorNode(typeRef);
return NULL;
}
}
}

View file

@ -161,6 +161,7 @@ namespace Tests
interface ITest
{
void TestFunc();
static void Func();
}
@ -178,6 +179,8 @@ namespace Tests
{
public static int sVal;
public void TestFunc() => Func();
public static void Func()
{
sVal = 123;
@ -189,6 +192,12 @@ namespace Tests
if (func != null)
defer:: func.Invoke();
}
public static void TestIFaceDefer()
{
ITest itest = scope Zoop();
defer itest.TestFunc();
}
}
public static int UseFunc0<T>(function int (T this, float f) func, T a, float b)
@ -265,6 +274,10 @@ namespace Tests
Zoop.sVal = 0;
Zoop.TestDefer();
Test.Assert(Zoop.sVal == 123);
Zoop.sVal = 0;
Zoop.TestIFaceDefer();
Test.Assert(Zoop.sVal == 123);
}
}
}

View file

@ -2660,8 +2660,15 @@ bool WinDebugger::DoUpdate()
}
}
BF_ASSERT(foundBreakpoint != NULL);
if (foundBreakpoint == NULL)
{
BfLogDbg("Unknown memory breakpoint hit %p\n", pcAddress);
mDebugManager->mOutMessages.push_back(StrFormat("memoryBreak %s", EncodeDataPtr(pcAddress, false).c_str()));
mRunState = RunState_Paused;
break;
}
else
{
DbgSubprogram* subprogram = mDebugTarget->FindSubProgram(pcAddress);
if (CheckConditionalBreakpoint(foundBreakpoint, subprogram, pcAddress))
{
@ -2680,6 +2687,7 @@ bool WinDebugger::DoUpdate()
ClearCallStack();
break;
}
}
if ((mRunState == RunState_DebugEval) && (mDebugEvalThreadInfo.mThreadId == mDebuggerWaitingThread->mThreadId))
{
@ -7507,7 +7515,7 @@ String WinDebugger::DbgTypedValueToString(const DbgTypedValue& origTypedValue, c
String symbolName;
addr_target offset;
DbgModule* dwarf;
static String demangledName;
String demangledName;
auto subProgram = mDebugTarget->FindSubProgram(funcPtr);
if (subProgram != NULL)
{
@ -7524,13 +7532,18 @@ String WinDebugger::DbgTypedValueToString(const DbgTypedValue& origTypedValue, c
{
auto dbgModule = mDebugTarget->FindDbgModuleForAddress(funcPtr);
if (dbgModule != NULL)
{
demangledName += dbgModule->GetLinkedModule()->mDisplayName + "!";
demangledName += StrFormat("0x%@", funcPtr);
}
}
if (!demangledName.IsEmpty())
{
retVal += " {";
retVal += demangledName;
retVal += "}";
}
retVal += "\n" + origValueType->ToString(language);
return retVal;