From 7fe5c22e8cb6b13205b811acb3e7589071b97511 Mon Sep 17 00:00:00 2001 From: ChrisDill Date: Sun, 28 Nov 2021 09:30:52 +0000 Subject: [PATCH] Test logging fix --- Raylib-cs/LoggingUtils.cs | 166 ++++++++++++++++++++++++++++++++++++++ Raylib-cs/Raylib.cs | 15 +++- 2 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 Raylib-cs/LoggingUtils.cs diff --git a/Raylib-cs/LoggingUtils.cs b/Raylib-cs/LoggingUtils.cs new file mode 100644 index 0000000..d672079 --- /dev/null +++ b/Raylib-cs/LoggingUtils.cs @@ -0,0 +1,166 @@ +using System; +using System.Runtime.InteropServices; + +namespace Raylib_cs +{ + internal readonly struct Native + { + internal const string Msvcrt = "msvcrt"; + internal const string Libc = "libc"; + internal const string LibSystem = "libSystem"; + + [DllImport(LibSystem, EntryPoint = "vasprintf", CallingConvention = CallingConvention.Cdecl)] + public static extern int vasprintf_apple(ref IntPtr buffer, IntPtr format, IntPtr args); + + [DllImport(Libc, EntryPoint = "vsprintf", CallingConvention = CallingConvention.Cdecl)] + public static extern int vsprintf_linux(IntPtr buffer, IntPtr format, IntPtr args); + + [DllImport(Msvcrt, EntryPoint = "vsprintf", CallingConvention = CallingConvention.Cdecl)] + public static extern int vsprintf_windows(IntPtr buffer, IntPtr format, IntPtr args); + + [DllImport(Libc, EntryPoint = "vsnprintf", CallingConvention = CallingConvention.Cdecl)] + public static extern int vsnprintf_linux(IntPtr buffer, UIntPtr size, IntPtr format, IntPtr args); + + [DllImport(Msvcrt, EntryPoint = "vsnprintf", CallingConvention = CallingConvention.Cdecl)] + public static extern int vsnprintf_windows(IntPtr buffer, UIntPtr size, IntPtr format, IntPtr args); + } + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + struct VaListLinuxX64 + { + uint gp_offset; + uint fp_offset; + IntPtr overflow_arg_area; + IntPtr reg_save_area; + } + + /// + /// Logging workaround for formatting strings from native code + /// + public static class LoggingUtils + { + static LoggingUtils() + { + Raylib.SetTraceLogCallback(LogConsole); + } + + public static void LogConsole(TraceLogLevel msgType, IntPtr text, IntPtr args) + { + var message = LoggingUtils.GetLogMessage(text, args); + Console.WriteLine(message); + } + + public static string GetLogMessage(IntPtr format, IntPtr args) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return AppleLogCallback(format, args); + } + + // Special marshalling is needed on Linux desktop 64 bits. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && IntPtr.Size == 8) + { + return LinuxX64LogCallback(format, args); + } + + var byteLength = vsnprintf(IntPtr.Zero, UIntPtr.Zero, format, args) + 1; + if (byteLength <= 1) + { + return string.Empty; + } + + var buffer = Marshal.AllocHGlobal(byteLength); + vsprintf(buffer, format, args); + + string result = Marshal.PtrToStringUTF8(buffer); + Marshal.FreeHGlobal(buffer); + + return result; + } + + static string AppleLogCallback(IntPtr format, IntPtr args) + { + IntPtr buffer = IntPtr.Zero; + try + { + var count = Native.vasprintf_apple(ref buffer, format, args); + if (count == -1) + { + return string.Empty; + } + return Marshal.PtrToStringUTF8(buffer) ?? string.Empty; + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } + + static string LinuxX64LogCallback(IntPtr format, IntPtr args) + { + // The args pointer cannot be reused between two calls. We need to make a copy of the underlying structure. + var listStructure = Marshal.PtrToStructure(args); + IntPtr listPointer = IntPtr.Zero; + int byteLength = 0; + string result = ""; + + // Get length of args + listPointer = Marshal.AllocHGlobal(Marshal.SizeOf(listStructure)); + Marshal.StructureToPtr(listStructure, listPointer, false); + byteLength = Native.vsnprintf_linux(IntPtr.Zero, UIntPtr.Zero, format, listPointer) + 1; + + // Allocate buffer for result + // listPointer = Marshal.AllocHGlobal(Marshal.SizeOf(listStructure)); + Marshal.StructureToPtr(listStructure, listPointer, false); + + IntPtr utf8Buffer = IntPtr.Zero; + utf8Buffer = Marshal.AllocHGlobal(byteLength); + + // Print result into buffer + Native.vsprintf_linux(utf8Buffer, format, listPointer); + result = Marshal.PtrToStringUTF8(utf8Buffer); + + Marshal.FreeHGlobal(listPointer); + Marshal.FreeHGlobal(utf8Buffer); + + return result; + } + + // https://github.com/dotnet/runtime/issues/51052 + static int vsnprintf(IntPtr buffer, UIntPtr size, IntPtr format, IntPtr args) + { + var os = Environment.OSVersion; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Native.vsnprintf_windows(buffer, size, format, args); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return Native.vsnprintf_linux(buffer, size, format, args); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("ANDROID"))) + { + return Native.vsprintf_linux(buffer, format, args); + } + return -1; + } + + // https://github.com/dotnet/runtime/issues/51052 + static int vsprintf(IntPtr buffer, IntPtr format, IntPtr args) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Native.vsprintf_windows(buffer, format, args); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return Native.vsprintf_linux(buffer, format, args); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("ANDROID"))) + { + return Native.vsprintf_linux(buffer, format, args); + } + return -1; + } + } +} diff --git a/Raylib-cs/Raylib.cs b/Raylib-cs/Raylib.cs index 846a592..8687ec4 100644 --- a/Raylib-cs/Raylib.cs +++ b/Raylib-cs/Raylib.cs @@ -26,7 +26,7 @@ namespace Raylib_cs /// WARNING: This callback is intended for advance users /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void TraceLogCallback(TraceLogLevel logLevel, string text, IntPtr args); + public delegate void TraceLogCallback(TraceLogLevel logLevel, IntPtr text, IntPtr args); /// /// FileIO: Load binary data
@@ -58,6 +58,8 @@ namespace Raylib_cs [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate CBool SaveFileTextCallback(string fileName, string text); + private static TraceLogCallback traceLogCallback; + /// /// Returns color with alpha applied, alpha goes from 0.0f to 1.0f
/// NOTE: Added for compatability with previous versions @@ -497,8 +499,15 @@ namespace Raylib_cs // WARNING: Callbacks setup is intended for advance users /// Set custom trace log - [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] - public static extern void SetTraceLogCallback(TraceLogCallback callback); + [DllImport(nativeLibName, EntryPoint = "SetTraceLogCallback", CallingConvention = CallingConvention.Cdecl)] + private static extern void SetTraceLogCallbackInternal(TraceLogCallback callback); + + /// Set custom trace log + public static void SetTraceLogCallback(TraceLogCallback callback) + { + SetTraceLogCallbackInternal(callback); + traceLogCallback = callback; + } /// Set custom file binary data loader [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]