mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 03:28:20 +02:00
Merge remote-tracking branch 'upstream/master' into Yuvan/CustomBuildProperties
This commit is contained in:
commit
524ebaa914
49 changed files with 1372 additions and 146 deletions
40
BeefLibs/Beefy2D/src/geom/Oval.bf
Normal file
40
BeefLibs/Beefy2D/src/geom/Oval.bf
Normal 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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -568,7 +568,13 @@ namespace System
|
|||
public T* CArray()
|
||||
{
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]
|
||||
public static void Mark(Object obj) {}
|
||||
[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
|
||||
|
||||
|
|
|
@ -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 = ?;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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,37 +151,40 @@ namespace LogViewer
|
|||
|
||||
let filters = mFilter.Split!('\n');
|
||||
|
||||
for (var line in mContent.Split('\n'))
|
||||
if (mContent != null)
|
||||
{
|
||||
bool hadMatch = false;
|
||||
bool hadFilter = false;
|
||||
|
||||
for (var filter in filters)
|
||||
for (var line in mContent.Split('\n'))
|
||||
{
|
||||
if (filter.Length == 0)
|
||||
continue;
|
||||
hadFilter = true;
|
||||
int lastIdx = -1;
|
||||
while (true)
|
||||
bool hadMatch = false;
|
||||
bool hadFilter = false;
|
||||
|
||||
for (var filter in filters)
|
||||
{
|
||||
int findIdx = line.IndexOf(filter, lastIdx + 1);
|
||||
if (findIdx == -1)
|
||||
break;
|
||||
if (filter.Length == 0)
|
||||
continue;
|
||||
hadFilter = true;
|
||||
int lastIdx = -1;
|
||||
while (true)
|
||||
{
|
||||
int findIdx = line.IndexOf(filter, lastIdx + 1);
|
||||
if (findIdx == -1)
|
||||
break;
|
||||
|
||||
hadMatch = true;
|
||||
lastIdx = findIdx + filter.Length - 1;
|
||||
hadMatch = true;
|
||||
lastIdx = findIdx + filter.Length - 1;
|
||||
|
||||
Match match;
|
||||
match.mFilterIdx = (.)@filter;
|
||||
match.mTextIdx = (.)(mNewContent.Length + findIdx);
|
||||
mNewMatches.Add(match);
|
||||
Match match;
|
||||
match.mFilterIdx = (.)@filter;
|
||||
match.mTextIdx = (.)(mNewContent.Length + findIdx);
|
||||
mNewMatches.Add(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((hadMatch) || (!hadFilter))
|
||||
{
|
||||
mNewContent.Append(line);
|
||||
mNewContent.Append('\n');
|
||||
if ((hadMatch) || (!hadFilter))
|
||||
{
|
||||
mNewContent.Append(line);
|
||||
mNewContent.Append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ using Beefy.utils;
|
|||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Beefy.sys;
|
||||
|
||||
namespace LogViewer
|
||||
{
|
||||
|
@ -26,7 +27,7 @@ namespace LogViewer
|
|||
{
|
||||
base.Init();
|
||||
|
||||
var dialog = scope OpenFileDialog();
|
||||
/*var dialog = scope OpenFileDialog();
|
||||
dialog.SetFilter("All files (*.*)|*.*");
|
||||
dialog.InitialDirectory = mInstallDir;
|
||||
dialog.Title = "Open Log";
|
||||
|
@ -35,7 +36,7 @@ namespace LogViewer
|
|||
{
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
|
||||
BeefPerf.Init("127.0.0.1", "LogViewer");
|
||||
|
||||
|
@ -45,7 +46,7 @@ namespace LogViewer
|
|||
|
||||
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;
|
||||
BFWindow.Flags.SysMenu | .Menu;
|
||||
|
||||
mFont = new Font();
|
||||
float fontSize = 12;
|
||||
|
@ -55,17 +56,43 @@ namespace LogViewer
|
|||
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);
|
||||
//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;
|
||||
dialog.Title = "Open Log";
|
||||
let result = dialog.ShowDialog();
|
||||
if ((result case .Err) || (dialog.FileNames.Count == 0))
|
||||
{
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
mBoard.Load(dialog.FileNames[0]);
|
||||
});
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
21
BeefTools/LogViewer/src/MemLogDialog.bf
Normal file
21
BeefTools/LogViewer/src/MemLogDialog.bf
Normal 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("");
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
@ -926,4 +927,36 @@ 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();
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ...)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
NOT_IMPL;
|
||||
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
|
||||
|
|
|
@ -553,18 +553,46 @@ struct BfpOverlappedFile : BfpOverlapped
|
|||
}
|
||||
};
|
||||
|
||||
struct BfpAsyncData
|
||||
{
|
||||
HANDLE mEvent;
|
||||
struct OverlappedReadResult : OVERLAPPED
|
||||
{
|
||||
BfpFile* mFile;
|
||||
intptr mBytesRead;
|
||||
DWORD mErrorCode;
|
||||
Array<uint8> mData;
|
||||
bool mPending;
|
||||
bool mAttemptedRead;
|
||||
|
||||
BfpAsyncData()
|
||||
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(BfpFile* file)
|
||||
{
|
||||
mFile = file;
|
||||
mEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
}
|
||||
|
||||
~BfpAsyncData()
|
||||
{
|
||||
::CloseHandle(mEvent);
|
||||
::CloseHandle(mEvent);
|
||||
}
|
||||
|
||||
void SetEvent()
|
||||
|
@ -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)
|
||||
|
@ -3217,52 +3345,55 @@ 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 (!file->mAsyncData->WaitAndResetEvent(timeoutMS))
|
||||
if (::ReadFileEx(file->mHandle, overlapped->GetPtr(), (uint32)size, overlapped, OverlappedReadComplete))
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
OUTRESULT(BfpFileResult_Ok);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (overlapped.mErrorCode == 0)
|
||||
{
|
||||
|
||||
}
|
||||
else if (overlapped.mErrorCode == ERROR_OPERATION_ABORTED)
|
||||
{
|
||||
else
|
||||
{
|
||||
OUTRESULT(BfpFileResult_Timeout);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -3277,10 +3408,7 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr
|
|||
OUTRESULT(BfpFileResult_UnknownError);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
OUTRESULT(BfpFileResult_Ok);
|
||||
return overlapped.mBytesRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
176
BeefySysLib/util/MemLogger.cpp
Normal file
176
BeefySysLib/util/MemLogger.cpp
Normal 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);
|
||||
}
|
29
BeefySysLib/util/MemLogger.h
Normal file
29
BeefySysLib/util/MemLogger.h
Normal 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
|
|
@ -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;
|
||||
};
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -7222,15 +7222,33 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
|
|||
mHasRequiredTypes = true;
|
||||
|
||||
//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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1837,6 +1837,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
|
|||
else
|
||||
{
|
||||
Fail("Sized array type expected", typeRef);
|
||||
AddErrorNode(typeRef);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2660,25 +2660,33 @@ bool WinDebugger::DoUpdate()
|
|||
}
|
||||
}
|
||||
|
||||
BF_ASSERT(foundBreakpoint != NULL);
|
||||
|
||||
DbgSubprogram* subprogram = mDebugTarget->FindSubProgram(pcAddress);
|
||||
if (CheckConditionalBreakpoint(foundBreakpoint, subprogram, pcAddress))
|
||||
if (foundBreakpoint == NULL)
|
||||
{
|
||||
if (foundBreakpoint != NULL)
|
||||
{
|
||||
mDebugManager->mOutMessages.push_back(StrFormat("memoryBreak %s", EncodeDataPtr(foundBreakpoint->mMemoryBreakpointInfo->mMemoryAddress, false).c_str()));
|
||||
mRunState = RunState_Paused;
|
||||
}
|
||||
|
||||
mActiveBreakpoint = foundBreakpoint;
|
||||
mBreakStackFrameIdx = -1;
|
||||
RemoveTempBreakpoints();
|
||||
BfLogDbg("Memory breakpoint hit: %p\n", foundBreakpoint);
|
||||
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
|
||||
ClearCallStack();
|
||||
break;
|
||||
{
|
||||
DbgSubprogram* subprogram = mDebugTarget->FindSubProgram(pcAddress);
|
||||
if (CheckConditionalBreakpoint(foundBreakpoint, subprogram, pcAddress))
|
||||
{
|
||||
if (foundBreakpoint != NULL)
|
||||
{
|
||||
mDebugManager->mOutMessages.push_back(StrFormat("memoryBreak %s", EncodeDataPtr(foundBreakpoint->mMemoryBreakpointInfo->mMemoryAddress, false).c_str()));
|
||||
mRunState = RunState_Paused;
|
||||
}
|
||||
|
||||
mActiveBreakpoint = foundBreakpoint;
|
||||
mBreakStackFrameIdx = -1;
|
||||
RemoveTempBreakpoints();
|
||||
BfLogDbg("Memory breakpoint hit: %p\n", foundBreakpoint);
|
||||
}
|
||||
else
|
||||
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);
|
||||
demangledName += StrFormat("0x%@", funcPtr);
|
||||
}
|
||||
}
|
||||
|
||||
retVal += " {";
|
||||
retVal += demangledName;
|
||||
retVal += "}";
|
||||
if (!demangledName.IsEmpty())
|
||||
{
|
||||
retVal += " {";
|
||||
retVal += demangledName;
|
||||
retVal += "}";
|
||||
}
|
||||
retVal += "\n" + origValueType->ToString(language);
|
||||
|
||||
return retVal;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue