From e55230c441876b9c19ea9c44812d0b4fa432d4eb Mon Sep 17 00:00:00 2001 From: logikonline Date: Fri, 6 Mar 2026 22:06:08 -0500 Subject: [PATCH] refactor: replace Console.WriteLine with DiagnosticLog service Replace 495+ Console.WriteLine debug statements across handlers, dispatching, services, views, and window components with centralized DiagnosticLog service for proper logging infrastructure. Add new DiagnosticLog.cs service with Debug/Error methods to eliminate debug logging pollution in production code. --- .notes/note-1772851947498-ujdbzwenk.json | 8 + Dispatching/LinuxDispatcher.cs | 7 +- Dispatching/LinuxDispatcherTimer.cs | 3 +- Handlers/BorderHandler.cs | 7 +- Handlers/ButtonHandler.cs | 11 +- Handlers/CollectionViewHandler.cs | 11 +- Handlers/EntryHandler.cs | 16 +- Handlers/GestureManager.cs | 304 ++++++++++++----------- Handlers/GtkWebViewHandler.cs | 42 ++-- Handlers/GtkWebViewPlatformView.cs | 33 ++- Handlers/ImageHandler.cs | 5 +- Handlers/LayoutHandler.Linux.cs | 13 +- Handlers/LayoutHandler.cs | 16 +- Handlers/NavigationPageHandler.cs | 76 +++--- Handlers/PageHandler.cs | 13 +- Handlers/ScrollViewHandler.cs | 5 +- Handlers/ShellHandler.cs | 13 +- Handlers/WebViewHandler.Linux.cs | 21 +- Handlers/WebViewHandler.cs | 15 +- Handlers/WindowHandler.cs | 9 +- Hosting/LinuxProgramHost.cs | 8 +- Hosting/LinuxViewRenderer.cs | 57 ++--- Hosting/MauiHandlerExtensions.cs | 7 +- LinuxApplication.cs | 94 +++---- Native/GLibNative.cs | 5 +- Native/WebKitNative.cs | 11 +- OpenMaui.Controls.Linux.csproj | 6 +- OpenMaui.Controls.Linux.nuspec | 24 +- Rendering/GpuRenderingEngine.cs | 13 +- Rendering/GtkSkiaSurfaceWidget.cs | 7 +- Rendering/SkiaRenderingEngine.cs | 51 +++- Services/AtSpi2AccessibilityService.cs | 14 +- Services/DiagnosticLog.cs | 80 ++++++ Services/DisplayServerFactory.cs | 18 +- Services/Fcitx5InputMethodService.cs | 8 +- Services/FilePickerService.cs | 2 +- Services/GlobalHotkeyService.cs | 4 +- Services/Gtk4InteropService.cs | 12 +- Services/GtkContextMenuService.cs | 10 +- Services/GtkThemeService.cs | 12 +- Services/HardwareVideoService.cs | 28 +-- Services/IBusInputMethodService.cs | 10 +- Services/InputMethodServiceFactory.cs | 16 +- Services/MauiIconGenerator.cs | 12 +- Services/MonitorService.cs | 16 +- Services/NotificationService.cs | 2 +- Services/PortalFilePickerService.cs | 6 +- Services/SystemThemeService.cs | 4 +- Services/X11InputMethodService.cs | 8 +- Views/LinuxDialogService.cs | 7 +- Views/LinuxWebView.cs | 11 +- Views/SkiaAlertDialog.cs | 3 +- Views/SkiaButton.cs | 3 +- Views/SkiaCollectionView.cs | 3 +- Views/SkiaEditor.cs | 6 +- Views/SkiaEntry.cs | 2 +- Views/SkiaImage.cs | 7 +- Views/SkiaImageButton.cs | 13 +- Views/SkiaItemsView.cs | 15 +- Views/SkiaItemsView.cs.bak | 0 Views/SkiaLayoutView.cs | 11 +- Views/SkiaNavigationPage.cs | 17 +- Views/SkiaPage.cs | 23 +- Views/SkiaScrollView.cs | 7 +- Views/SkiaShell.cs | 11 +- Views/SkiaView.cs | 2 +- Views/SkiaWebView.cs | 99 ++++---- Window/GtkHostWindow.cs | 13 +- Window/WaylandWindow.cs | 5 +- Window/X11Window.cs | 21 +- 70 files changed, 814 insertions(+), 638 deletions(-) create mode 100644 .notes/note-1772851947498-ujdbzwenk.json create mode 100644 Services/DiagnosticLog.cs delete mode 100644 Views/SkiaItemsView.cs.bak diff --git a/.notes/note-1772851947498-ujdbzwenk.json b/.notes/note-1772851947498-ujdbzwenk.json new file mode 100644 index 0000000..432f08f --- /dev/null +++ b/.notes/note-1772851947498-ujdbzwenk.json @@ -0,0 +1,8 @@ +{ + "id": "note-1772851947498-ujdbzwenk", + "title": "Working list", + "content": "---\n Critical Issues\n\n 1. 495+ Console.WriteLine calls — Debug logging pollution throughout production code. Replace with ILogger\n or conditional diagnostics.\n 2. Memory leak in GestureManager — _gestureState and _tapTracking dictionaries never cleaned up for removed\n views.\n 3. Reflection-based gesture invocation — typeof(TapGestureRecognizer).GetMethod(\"SendTapped\",\n BindingFlags.NonPublic) is fragile and will break across MAUI updates.\n 4. No exception handling in rendering pipeline — A bad view implementation in SkiaRenderingEngine.Render()\n can crash the entire application.\n 5. Version mismatch — .nuspec shows 1.0.0-preview.1 while .csproj shows 1.0.0-rc.1.\n 6. Potential text binding recursion in EntryHandler.OnTextChanged — two-way sync without reentrancy guard.\n\n ---\n Medium Issues\n\n ┌───────────────────────┬──────────────────────────────────────────────────────────────────────────────┐\n │ Issue │ Location │\n ├───────────────────────┼──────────────────────────────────────────────────────────────────────────────┤\n │ Massive file sizes │ SkiaEntry (57K lines), SkiaEditor (54K), SkiaLabel (41K) — should be split │\n ├───────────────────────┼──────────────────────────────────────────────────────────────────────────────┤\n │ Test coverage ~3% │ Only 10 test files for 321 source files │\n ├───────────────────────┼──────────────────────────────────────────────────────────────────────────────┤\n │ Hard-coded thresholds │ SwipeMinDistance=50, SwipeMaxTime=500ms, region merge=30% — not configurable │\n ├───────────────────────┼──────────────────────────────────────────────────────────────────────────────┤\n │ No feature detection │ Assumes AT-SPI2, IBus available — crashes on minimal installs │\n ├───────────────────────┼──────────────────────────────────────────────────────────────────────────────┤\n │ .bak file in VCS │ SkiaItemsView.cs.bak should be removed │\n ├───────────────────────┼──────────────────────────────────────────────────────────────────────────────┤\n │ Incomplete features │ EntryHandler.MapReturnType() commented out, various TODOs │\n └───────────────────────┴──────────────────────────────────────────────────────────────────────────────┘\n\n ---\n Priority Recommendations\n\n Before 1.0 release:\n 1. Replace all Console.WriteLine with proper logging (ILogger or conditional #if DEBUG)\n 2. Fix GestureManager memory leaks — add cleanup on view disconnect\n 3. Add try-catch around view rendering in the engine\n 4. Remove reflection in gesture handling — use interface or delegate pattern\n 5. Align nuspec/csproj versions\n 6. Add feature detection for optional services\n\n Near-term:\n 1. Split files over 10K lines into partial classes or separate concerns\n 2. Expand test coverage significantly (target 50%+)\n 3. Extract duplicate text rendering code into shared utilities\n 4. Make rendering thresholds configurable\n", + "createdAt": 1772851947495, + "updatedAt": 1772851991179, + "tags": [] +} \ No newline at end of file diff --git a/Dispatching/LinuxDispatcher.cs b/Dispatching/LinuxDispatcher.cs index 8b082be..585ea70 100644 --- a/Dispatching/LinuxDispatcher.cs +++ b/Dispatching/LinuxDispatcher.cs @@ -1,6 +1,7 @@ using System; using Microsoft.Maui.Dispatching; using Microsoft.Maui.Platform.Linux.Native; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform.Linux.Dispatching; @@ -24,7 +25,7 @@ public class LinuxDispatcher : IDispatcher { _mainThreadId = Environment.CurrentManagedThreadId; _mainDispatcher = new LinuxDispatcher(); - Console.WriteLine($"[LinuxDispatcher] Initialized on thread {_mainThreadId}"); + DiagnosticLog.Debug("LinuxDispatcher", $"Initialized on thread {_mainThreadId}"); } } @@ -44,7 +45,7 @@ public class LinuxDispatcher : IDispatcher } catch (Exception ex) { - Console.WriteLine("[LinuxDispatcher] Error in dispatched action: " + ex.Message); + DiagnosticLog.Error("LinuxDispatcher", "Error in dispatched action", ex); } return false; }); @@ -62,7 +63,7 @@ public class LinuxDispatcher : IDispatcher } catch (Exception ex) { - Console.WriteLine("[LinuxDispatcher] Error in delayed action: " + ex.Message); + DiagnosticLog.Error("LinuxDispatcher", "Error in delayed action", ex); } return false; }); diff --git a/Dispatching/LinuxDispatcherTimer.cs b/Dispatching/LinuxDispatcherTimer.cs index c6be388..c33a131 100644 --- a/Dispatching/LinuxDispatcherTimer.cs +++ b/Dispatching/LinuxDispatcherTimer.cs @@ -1,6 +1,7 @@ using System; using Microsoft.Maui.Dispatching; using Microsoft.Maui.Platform.Linux.Native; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform.Linux.Dispatching; @@ -95,7 +96,7 @@ public class LinuxDispatcherTimer : IDispatcherTimer } catch (Exception ex) { - Console.WriteLine("[LinuxDispatcherTimer] Error in Tick handler: " + ex.Message); + DiagnosticLog.Error("LinuxDispatcherTimer", "Error in Tick handler", ex); } if (_isRepeating && _isRunning) { diff --git a/Handlers/BorderHandler.cs b/Handlers/BorderHandler.cs index 468925d..46d5ee8 100644 --- a/Handlers/BorderHandler.cs +++ b/Handlers/BorderHandler.cs @@ -6,6 +6,7 @@ using Microsoft.Maui.Graphics; using Microsoft.Maui.Controls; using Microsoft.Maui.Platform; using Microsoft.Maui.Platform.Linux.Hosting; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform.Linux.Handlers; @@ -111,13 +112,13 @@ public partial class BorderHandler : ViewHandler // Create handler for content if it doesn't exist if (content.Handler == null) { - Console.WriteLine($"[BorderHandler] Creating handler for content: {content.GetType().Name}"); + DiagnosticLog.Debug("BorderHandler", $"Creating handler for content: {content.GetType().Name}"); content.Handler = content.ToViewHandler(handler.MauiContext); } if (content.Handler?.PlatformView is SkiaView skiaContent) { - Console.WriteLine($"[BorderHandler] Adding content: {skiaContent.GetType().Name}"); + DiagnosticLog.Debug("BorderHandler", $"Adding content: {skiaContent.GetType().Name}"); handler.PlatformView.AddChild(skiaContent); } } @@ -156,7 +157,7 @@ public partial class BorderHandler : ViewHandler if (border is VisualElement ve) { var bgColor = ve.BackgroundColor; - Console.WriteLine($"[BorderHandler] MapBackgroundColor: {bgColor}"); + DiagnosticLog.Debug("BorderHandler", $"MapBackgroundColor: {bgColor}"); if (bgColor != null) { handler.PlatformView.BackgroundColor = bgColor; diff --git a/Handlers/ButtonHandler.cs b/Handlers/ButtonHandler.cs index 97262bc..8a08e44 100644 --- a/Handlers/ButtonHandler.cs +++ b/Handlers/ButtonHandler.cs @@ -4,6 +4,7 @@ using Microsoft.Maui.Controls; using Microsoft.Maui.Handlers; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform.Linux.Handlers; @@ -64,7 +65,7 @@ public partial class ButtonHandler : ViewHandler // Map size requests from MAUI Button if (VirtualView is Microsoft.Maui.Controls.Button mauiButton) { - Console.WriteLine($"[ButtonHandler] MapSize Text='{platformView.Text}' WReq={mauiButton.WidthRequest} HReq={mauiButton.HeightRequest}"); + DiagnosticLog.Debug("ButtonHandler", $"MapSize Text='{platformView.Text}' WReq={mauiButton.WidthRequest} HReq={mauiButton.HeightRequest}"); if (mauiButton.WidthRequest >= 0) platformView.WidthRequest = mauiButton.WidthRequest; if (mauiButton.HeightRequest >= 0) @@ -72,7 +73,7 @@ public partial class ButtonHandler : ViewHandler } else { - Console.WriteLine($"[ButtonHandler] VirtualView is NOT Microsoft.Maui.Controls.Button, type={VirtualView?.GetType().Name}"); + DiagnosticLog.Debug("ButtonHandler", $"VirtualView is NOT Microsoft.Maui.Controls.Button, type={VirtualView?.GetType().Name}"); } } } @@ -162,7 +163,7 @@ public partial class TextButtonHandler : ButtonHandler protected override void ConnectHandler(SkiaButton platformView) { - Console.WriteLine($"[TextButtonHandler] ConnectHandler START"); + DiagnosticLog.Debug("TextButtonHandler", "ConnectHandler START"); base.ConnectHandler(platformView); // Manually map text properties on connect since MAUI may not trigger updates @@ -178,13 +179,13 @@ public partial class TextButtonHandler : ButtonHandler // Map size requests from MAUI Button if (VirtualView is Microsoft.Maui.Controls.Button mauiButton) { - Console.WriteLine($"[TextButtonHandler] MapSize Text='{platformView.Text}' WReq={mauiButton.WidthRequest} HReq={mauiButton.HeightRequest}"); + DiagnosticLog.Debug("TextButtonHandler", $"MapSize Text='{platformView.Text}' WReq={mauiButton.WidthRequest} HReq={mauiButton.HeightRequest}"); if (mauiButton.WidthRequest >= 0) platformView.WidthRequest = mauiButton.WidthRequest; if (mauiButton.HeightRequest >= 0) platformView.HeightRequest = mauiButton.HeightRequest; } - Console.WriteLine($"[TextButtonHandler] ConnectHandler DONE"); + DiagnosticLog.Debug("TextButtonHandler", "ConnectHandler DONE"); } public static void MapText(TextButtonHandler handler, ITextButton button) diff --git a/Handlers/CollectionViewHandler.cs b/Handlers/CollectionViewHandler.cs index 032616b..e905f9e 100644 --- a/Handlers/CollectionViewHandler.cs +++ b/Handlers/CollectionViewHandler.cs @@ -6,6 +6,7 @@ using Microsoft.Maui.Graphics; using Microsoft.Maui.Controls; using Microsoft.Maui.Platform; using Microsoft.Maui.Platform.Linux.Hosting; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform.Linux.Handlers; @@ -130,18 +131,18 @@ public partial class CollectionViewHandler : ViewHandler base.DisconnectHandler(platformView); } + private bool _isUpdatingText; + private void OnTextChanged(object? sender, Platform.TextChangedEventArgs e) { - if (VirtualView is null || PlatformView is null) return; + if (VirtualView is null || PlatformView is null || _isUpdatingText) return; if (VirtualView.Text != e.NewTextValue) { - VirtualView.Text = e.NewTextValue ?? string.Empty; + _isUpdatingText = true; + try + { + VirtualView.Text = e.NewTextValue ?? string.Empty; + } + finally + { + _isUpdatingText = false; + } } } @@ -87,7 +97,7 @@ public partial class EntryHandler : ViewHandler public static void MapText(EntryHandler handler, IEntry entry) { - if (handler.PlatformView is null) return; + if (handler.PlatformView is null || handler._isUpdatingText) return; if (handler.PlatformView.Text != entry.Text) { diff --git a/Handlers/GestureManager.cs b/Handlers/GestureManager.cs index a3024f8..ffed12d 100644 --- a/Handlers/GestureManager.cs +++ b/Handlers/GestureManager.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Windows.Input; using Microsoft.Maui.Controls; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform.Linux.Handlers; @@ -13,6 +14,8 @@ namespace Microsoft.Maui.Platform.Linux.Handlers; /// public static class GestureManager { + private const string Tag = "GestureManager"; + private class GestureTrackingState { public double StartX { get; set; } @@ -35,16 +38,54 @@ public static class GestureManager Released } + // Cached reflection MethodInfo for internal MAUI methods private static MethodInfo? _sendTappedMethod; + private static MethodInfo? _sendSwipedMethod; + private static MethodInfo? _sendPanMethod; private static MethodInfo? _sendPinchMethod; - private static readonly Dictionary _tapTracking = new Dictionary(); - private static readonly Dictionary _gestureState = new Dictionary(); + private static MethodInfo? _sendDragStartingMethod; + private static MethodInfo? _sendDragOverMethod; + private static MethodInfo? _sendDropMethod; + private static readonly Dictionary _pointerMethodCache = new(); - private const double SwipeMinDistance = 50.0; - private const double SwipeMaxTime = 500.0; - private const double SwipeDirectionThreshold = 0.5; - private const double PanMinDistance = 10.0; - private const double PinchScrollScale = 0.1; // Scale factor per scroll unit + private static readonly Dictionary _tapTracking = new(); + private static readonly Dictionary _gestureState = new(); + + /// + /// Minimum distance in pixels for a swipe gesture to be recognized. + /// + public static double SwipeMinDistance { get; set; } = 50.0; + + /// + /// Maximum time in milliseconds for a swipe gesture to be recognized. + /// + public static double SwipeMaxTime { get; set; } = 500.0; + + /// + /// Ratio threshold for determining swipe direction dominance. + /// + public static double SwipeDirectionThreshold { get; set; } = 0.5; + + /// + /// Minimum distance in pixels before a pan gesture is recognized. + /// + public static double PanMinDistance { get; set; } = 10.0; + + /// + /// Scale factor per scroll unit for pinch-via-scroll gestures. + /// + public static double PinchScrollScale { get; set; } = 0.1; + + /// + /// Removes tracking entries for the specified view, preventing memory leaks + /// when views are disconnected from the visual tree. + /// + public static void CleanupView(View view) + { + if (view == null) return; + _tapTracking.Remove(view); + _gestureState.Remove(view); + } /// /// Processes a tap gesture on the specified view. @@ -79,12 +120,13 @@ public static class GestureManager bool result = false; foreach (var item in recognizers) { - var tapRecognizer = (item is TapGestureRecognizer) ? (TapGestureRecognizer)item : null; - if (tapRecognizer == null) + if (item is not TapGestureRecognizer tapRecognizer) { continue; } - Console.WriteLine($"[GestureManager] Processing TapGestureRecognizer on {view.GetType().Name}, CommandParameter={tapRecognizer.CommandParameter}, NumberOfTapsRequired={tapRecognizer.NumberOfTapsRequired}"); + DiagnosticLog.Debug(Tag, + $"Processing TapGestureRecognizer on {view.GetType().Name}, CommandParameter={tapRecognizer.CommandParameter}, NumberOfTapsRequired={tapRecognizer.NumberOfTapsRequired}"); + int numberOfTapsRequired = tapRecognizer.NumberOfTapsRequired; if (numberOfTapsRequired > 1) { @@ -92,108 +134,58 @@ public static class GestureManager if (!_tapTracking.TryGetValue(view, out var tracking)) { _tapTracking[view] = (utcNow, 1); - Console.WriteLine($"[GestureManager] First tap 1/{numberOfTapsRequired}"); + DiagnosticLog.Debug(Tag, $"First tap 1/{numberOfTapsRequired}"); continue; } if (!((utcNow - tracking.lastTap).TotalMilliseconds < 300.0)) { _tapTracking[view] = (utcNow, 1); - Console.WriteLine($"[GestureManager] Tap timeout, reset to 1/{numberOfTapsRequired}"); + DiagnosticLog.Debug(Tag, $"Tap timeout, reset to 1/{numberOfTapsRequired}"); continue; } int tapCount = tracking.tapCount + 1; if (tapCount < numberOfTapsRequired) { _tapTracking[view] = (utcNow, tapCount); - Console.WriteLine($"[GestureManager] Tap {tapCount}/{numberOfTapsRequired}, waiting for more taps"); + DiagnosticLog.Debug(Tag, $"Tap {tapCount}/{numberOfTapsRequired}, waiting for more taps"); continue; } _tapTracking.Remove(view); } + + // Try to raise the Tapped event via cached reflection bool eventFired = false; try { if (_sendTappedMethod == null) { - _sendTappedMethod = typeof(TapGestureRecognizer).GetMethod("SendTapped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + _sendTappedMethod = typeof(TapGestureRecognizer).GetMethod( + "SendTapped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (_sendTappedMethod != null) { - Console.WriteLine($"[GestureManager] Found SendTapped method with {_sendTappedMethod.GetParameters().Length} params"); var args = new TappedEventArgs(tapRecognizer.CommandParameter); _sendTappedMethod.Invoke(tapRecognizer, new object[] { view, args }); - Console.WriteLine("[GestureManager] SendTapped invoked successfully"); + DiagnosticLog.Debug(Tag, "SendTapped invoked successfully"); eventFired = true; } } catch (Exception ex) { - Console.WriteLine("[GestureManager] SendTapped failed: " + ex.Message); + DiagnosticLog.Error(Tag, "SendTapped failed", ex); } + + // Always invoke the Command if available (SendTapped may or may not invoke it internally) if (!eventFired) { - try + ICommand? command = tapRecognizer.Command; + if (command != null && command.CanExecute(tapRecognizer.CommandParameter)) { - var field = typeof(TapGestureRecognizer).GetField("Tapped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - ?? typeof(TapGestureRecognizer).GetField("_tapped", BindingFlags.Instance | BindingFlags.NonPublic); - if (field != null && field.GetValue(tapRecognizer) is EventHandler handler) - { - Console.WriteLine("[GestureManager] Invoking Tapped event directly"); - var args = new TappedEventArgs(tapRecognizer.CommandParameter); - handler(tapRecognizer, args); - eventFired = true; - } - } - catch (Exception ex) - { - Console.WriteLine("[GestureManager] Direct event invoke failed: " + ex.Message); + DiagnosticLog.Debug(Tag, "Executing TapGestureRecognizer Command"); + command.Execute(tapRecognizer.CommandParameter); } } - if (!eventFired) - { - try - { - string[] fieldNames = new string[] { "TappedEvent", "_TappedHandler", "k__BackingField" }; - foreach (string fieldName in fieldNames) - { - var field = typeof(TapGestureRecognizer).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); - if (field != null) - { - Console.WriteLine("[GestureManager] Found field: " + fieldName); - if (field.GetValue(tapRecognizer) is EventHandler handler) - { - var args = new TappedEventArgs(tapRecognizer.CommandParameter); - handler(tapRecognizer, args); - Console.WriteLine("[GestureManager] Event fired via " + fieldName); - eventFired = true; - break; - } - } - } - } - catch (Exception ex) - { - Console.WriteLine("[GestureManager] Backing field approach failed: " + ex.Message); - } - } - if (!eventFired) - { - Console.WriteLine("[GestureManager] Could not fire event, dumping type info..."); - var methods = typeof(TapGestureRecognizer).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - foreach (var method in methods) - { - if (method.Name.Contains("Tap", StringComparison.OrdinalIgnoreCase) || method.Name.Contains("Send", StringComparison.OrdinalIgnoreCase)) - { - Console.WriteLine($"[GestureManager] Method: {method.Name}({string.Join(", ", from p in method.GetParameters() select p.ParameterType.Name)})"); - } - } - } - ICommand? command = tapRecognizer.Command; - if (command != null && command.CanExecute(tapRecognizer.CommandParameter)) - { - Console.WriteLine("[GestureManager] Executing Command"); - command.Execute(tapRecognizer.CommandParameter); - } + result = true; } return result; @@ -274,7 +266,7 @@ public static class GestureManager } double deltaX = x - state.StartX; double deltaY = y - state.StartY; - if (Math.Sqrt(deltaX * deltaX + deltaY * deltaY) >= 10.0) + if (Math.Sqrt(deltaX * deltaX + deltaY * deltaY) >= PanMinDistance) { ProcessPanGesture(view, deltaX, deltaY, (GestureStatus)(state.IsPanning ? 1 : 0)); state.IsPanning = true; @@ -299,14 +291,14 @@ public static class GestureManager double deltaY = y - state.StartY; double distance = Math.Sqrt(deltaX * deltaX + deltaY * deltaY); double elapsed = (DateTime.UtcNow - state.StartTime).TotalMilliseconds; - if (distance >= 50.0 && elapsed <= 500.0) + if (distance >= SwipeMinDistance && elapsed <= SwipeMaxTime) { var direction = DetermineSwipeDirection(deltaX, deltaY); if (direction != SwipeDirection.Right) { ProcessSwipeGesture(view, direction); } - else if (Math.Abs(deltaX) > Math.Abs(deltaY) * 0.5) + else if (Math.Abs(deltaX) > Math.Abs(deltaY) * SwipeDirectionThreshold) { ProcessSwipeGesture(view, (deltaX > 0.0) ? SwipeDirection.Right : SwipeDirection.Left); } @@ -315,9 +307,9 @@ public static class GestureManager { ProcessPanGesture(view, deltaX, deltaY, (GestureStatus)2); } - else if (distance < 15.0 && elapsed < 500.0) + else if (distance < 15.0 && elapsed < SwipeMaxTime) { - Console.WriteLine($"[GestureManager] Detected tap on {view.GetType().Name} (distance={distance:F1}, elapsed={elapsed:F0}ms)"); + DiagnosticLog.Debug(Tag, $"Detected tap on {view.GetType().Name} (distance={distance:F1}, elapsed={elapsed:F0}ms)"); ProcessTap(view, x, y); } _gestureState.Remove(view); @@ -351,7 +343,7 @@ public static class GestureManager { double absX = Math.Abs(deltaX); double absY = Math.Abs(deltaY); - if (absX > absY * 0.5) + if (absX > absY * SwipeDirectionThreshold) { if (deltaX > 0.0) { @@ -359,7 +351,7 @@ public static class GestureManager } return SwipeDirection.Left; } - if (absY > absX * 0.5) + if (absY > absX * SwipeDirectionThreshold) { if (deltaY > 0.0) { @@ -383,29 +375,34 @@ public static class GestureManager } foreach (var item in recognizers) { - var swipeRecognizer = (item is SwipeGestureRecognizer) ? (SwipeGestureRecognizer)item : null; - if (swipeRecognizer == null || !swipeRecognizer.Direction.HasFlag(direction)) + if (item is not SwipeGestureRecognizer swipeRecognizer || !swipeRecognizer.Direction.HasFlag(direction)) { continue; } - Console.WriteLine($"[GestureManager] Swipe detected: {direction}"); + DiagnosticLog.Debug(Tag, $"Swipe detected: {direction}"); + try { - var method = typeof(SwipeGestureRecognizer).GetMethod("SendSwiped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - if (method != null) + if (_sendSwipedMethod == null) { - method.Invoke(swipeRecognizer, new object[] { view, direction }); - Console.WriteLine("[GestureManager] SendSwiped invoked successfully"); + _sendSwipedMethod = typeof(SwipeGestureRecognizer).GetMethod( + "SendSwiped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + } + if (_sendSwipedMethod != null) + { + _sendSwipedMethod.Invoke(swipeRecognizer, new object[] { view, direction }); + DiagnosticLog.Debug(Tag, "SendSwiped invoked successfully"); } } catch (Exception ex) { - Console.WriteLine("[GestureManager] SendSwiped failed: " + ex.Message); + DiagnosticLog.Error(Tag, "SendSwiped failed", ex); } + ICommand? command = swipeRecognizer.Command; if (command != null && command.CanExecute(swipeRecognizer.CommandParameter)) { - swipeRecognizer.Command.Execute(swipeRecognizer.CommandParameter); + command.Execute(swipeRecognizer.CommandParameter); } } } @@ -419,18 +416,22 @@ public static class GestureManager } foreach (var item in recognizers) { - var panRecognizer = (item is PanGestureRecognizer) ? (PanGestureRecognizer)item : null; - if (panRecognizer == null) + if (item is not PanGestureRecognizer panRecognizer) { continue; } - Console.WriteLine($"[GestureManager] Pan gesture: status={status}, totalX={totalX:F1}, totalY={totalY:F1}"); + DiagnosticLog.Debug(Tag, $"Pan gesture: status={status}, totalX={totalX:F1}, totalY={totalY:F1}"); + try { - var method = typeof(PanGestureRecognizer).GetMethod("SendPan", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - if (method != null) + if (_sendPanMethod == null) { - method.Invoke(panRecognizer, new object[] + _sendPanMethod = typeof(PanGestureRecognizer).GetMethod( + "SendPan", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + } + if (_sendPanMethod != null) + { + _sendPanMethod.Invoke(panRecognizer, new object[] { view, totalX, @@ -441,7 +442,7 @@ public static class GestureManager } catch (Exception ex) { - Console.WriteLine("[GestureManager] SendPan failed: " + ex.Message); + DiagnosticLog.Error(Tag, "SendPan failed", ex); } } } @@ -455,8 +456,7 @@ public static class GestureManager } foreach (var item in recognizers) { - var pointerRecognizer = (item is PointerGestureRecognizer) ? (PointerGestureRecognizer)item : null; - if (pointerRecognizer == null) + if (item is not PointerGestureRecognizer pointerRecognizer) { continue; } @@ -471,19 +471,27 @@ public static class GestureManager PointerEventType.Released => "SendPointerReleased", _ => null, }; - if (methodName != null) + if (methodName == null) { - var method = typeof(PointerGestureRecognizer).GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - if (method != null) - { - var args = CreatePointerEventArgs(view, x, y); - method.Invoke(pointerRecognizer, new object[] { view, args }); - } + continue; + } + + if (!_pointerMethodCache.TryGetValue(eventType, out var method)) + { + method = typeof(PointerGestureRecognizer).GetMethod( + methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + _pointerMethodCache[eventType] = method; + } + + if (method != null) + { + var args = CreatePointerEventArgs(view, x, y); + method.Invoke(pointerRecognizer, new object[] { view, args }); } } catch (Exception ex) { - Console.WriteLine("[GestureManager] Pointer event failed: " + ex.Message); + DiagnosticLog.Error(Tag, $"Pointer event {eventType} failed", ex); } } } @@ -587,26 +595,23 @@ public static class GestureManager foreach (var item in recognizers) { - var pinchRecognizer = item as PinchGestureRecognizer; - if (pinchRecognizer == null) + if (item is not PinchGestureRecognizer pinchRecognizer) { continue; } - Console.WriteLine($"[GestureManager] Pinch gesture: status={status}, scale={scale:F2}, origin=({originX:F0},{originY:F0})"); + DiagnosticLog.Debug(Tag, $"Pinch gesture: status={status}, scale={scale:F2}, origin=({originX:F0},{originY:F0})"); try { - // Cache the method lookup if (_sendPinchMethod == null) { - _sendPinchMethod = typeof(PinchGestureRecognizer).GetMethod("SendPinch", - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + _sendPinchMethod = typeof(PinchGestureRecognizer).GetMethod( + "SendPinch", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (_sendPinchMethod != null) { - // SendPinch(IView sender, double scale, Point scaleOrigin, GestureStatus status) var scaleOrigin = new Point(originX / view.Width, originY / view.Height); _sendPinchMethod.Invoke(pinchRecognizer, new object[] { @@ -615,12 +620,12 @@ public static class GestureManager scaleOrigin, status }); - Console.WriteLine("[GestureManager] SendPinch invoked successfully"); + DiagnosticLog.Debug(Tag, "SendPinch invoked successfully"); } } catch (Exception ex) { - Console.WriteLine($"[GestureManager] SendPinch failed: {ex.Message}"); + DiagnosticLog.Error(Tag, "SendPinch failed", ex); } } } @@ -751,26 +756,27 @@ public static class GestureManager foreach (var item in recognizers) { - var dragRecognizer = item as DragGestureRecognizer; - if (dragRecognizer == null) continue; + if (item is not DragGestureRecognizer dragRecognizer) continue; - Console.WriteLine($"[GestureManager] Starting drag from {view.GetType().Name}"); + DiagnosticLog.Debug(Tag, $"Starting drag from {view.GetType().Name}"); try { - // Create DragStartingEventArgs and invoke SendDragStarting - var method = typeof(DragGestureRecognizer).GetMethod("SendDragStarting", - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - - if (method != null) + if (_sendDragStartingMethod == null) { - method.Invoke(dragRecognizer, new object[] { view }); - Console.WriteLine("[GestureManager] SendDragStarting invoked successfully"); + _sendDragStartingMethod = typeof(DragGestureRecognizer).GetMethod( + "SendDragStarting", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + } + + if (_sendDragStartingMethod != null) + { + _sendDragStartingMethod.Invoke(dragRecognizer, new object[] { view }); + DiagnosticLog.Debug(Tag, "SendDragStarting invoked successfully"); } } catch (Exception ex) { - Console.WriteLine($"[GestureManager] SendDragStarting failed: {ex.Message}"); + DiagnosticLog.Error(Tag, "SendDragStarting failed", ex); } } } @@ -787,24 +793,26 @@ public static class GestureManager foreach (var item in recognizers) { - var dropRecognizer = item as DropGestureRecognizer; - if (dropRecognizer == null) continue; + if (item is not DropGestureRecognizer dropRecognizer) continue; - Console.WriteLine($"[GestureManager] Drag enter on {view.GetType().Name}"); + DiagnosticLog.Debug(Tag, $"Drag enter on {view.GetType().Name}"); try { - var method = typeof(DropGestureRecognizer).GetMethod("SendDragOver", - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - - if (method != null) + if (_sendDragOverMethod == null) { - method.Invoke(dropRecognizer, new object[] { view }); + _sendDragOverMethod = typeof(DropGestureRecognizer).GetMethod( + "SendDragOver", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + } + + if (_sendDragOverMethod != null) + { + _sendDragOverMethod.Invoke(dropRecognizer, new object[] { view }); } } catch (Exception ex) { - Console.WriteLine($"[GestureManager] SendDragOver failed: {ex.Message}"); + DiagnosticLog.Error(Tag, "SendDragOver failed", ex); } } } @@ -821,24 +829,26 @@ public static class GestureManager foreach (var item in recognizers) { - var dropRecognizer = item as DropGestureRecognizer; - if (dropRecognizer == null) continue; + if (item is not DropGestureRecognizer dropRecognizer) continue; - Console.WriteLine($"[GestureManager] Drop on {view.GetType().Name}"); + DiagnosticLog.Debug(Tag, $"Drop on {view.GetType().Name}"); try { - var method = typeof(DropGestureRecognizer).GetMethod("SendDrop", - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - - if (method != null) + if (_sendDropMethod == null) { - method.Invoke(dropRecognizer, new object[] { view }); + _sendDropMethod = typeof(DropGestureRecognizer).GetMethod( + "SendDrop", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + } + + if (_sendDropMethod != null) + { + _sendDropMethod.Invoke(dropRecognizer, new object[] { view }); } } catch (Exception ex) { - Console.WriteLine($"[GestureManager] SendDrop failed: {ex.Message}"); + DiagnosticLog.Error(Tag, "SendDrop failed", ex); } } } diff --git a/Handlers/GtkWebViewHandler.cs b/Handlers/GtkWebViewHandler.cs index b9f365e..5529abb 100644 --- a/Handlers/GtkWebViewHandler.cs +++ b/Handlers/GtkWebViewHandler.cs @@ -55,7 +55,7 @@ public class GtkWebViewHandler : ViewHandler _platformWebView.NavigationCompleted += OnNavigationCompleted; _platformWebView.ScriptDialogRequested += OnScriptDialogRequested; } - Console.WriteLine("[GtkWebViewHandler] ConnectHandler - WebView ready"); + DiagnosticLog.Debug("GtkWebViewHandler", "ConnectHandler - WebView ready"); } protected override void DisconnectHandler(GtkWebViewProxy platformView) @@ -75,7 +75,7 @@ public class GtkWebViewHandler : ViewHandler private async void OnScriptDialogRequested(object? sender, (ScriptDialogType Type, string Message, Action Callback) e) { - Console.WriteLine($"[GtkWebViewHandler] Script dialog requested: type={e.Type}, message={e.Message}"); + DiagnosticLog.Debug("GtkWebViewHandler", $"Script dialog requested: type={e.Type}, message={e.Message}"); string title = e.Type switch { @@ -92,18 +92,18 @@ public class GtkWebViewHandler : ViewHandler { bool result = await LinuxDialogService.ShowAlertAsync(title, e.Message, acceptButton, cancelButton); e.Callback(result); - Console.WriteLine($"[GtkWebViewHandler] Dialog result: {result}"); + DiagnosticLog.Debug("GtkWebViewHandler", $"Dialog result: {result}"); } catch (Exception ex) { - Console.WriteLine($"[GtkWebViewHandler] Error showing dialog: {ex.Message}"); + DiagnosticLog.Error("GtkWebViewHandler", $"Error showing dialog: {ex.Message}", ex); e.Callback(false); } } private void OnNavigationStarted(object? sender, string uri) { - Console.WriteLine($"[GtkWebViewHandler] Navigation started: {uri}"); + DiagnosticLog.Debug("GtkWebViewHandler", $"Navigation started: {uri}"); try { GLibNative.IdleAdd(() => @@ -115,25 +115,25 @@ public class GtkWebViewHandler : ViewHandler var args = new Microsoft.Maui.Controls.WebNavigatingEventArgs( WebNavigationEvent.NewPage, null, uri); controller.SendNavigating(args); - Console.WriteLine("[GtkWebViewHandler] Sent Navigating event to VirtualView"); + DiagnosticLog.Debug("GtkWebViewHandler", "Sent Navigating event to VirtualView"); } } catch (Exception ex) { - Console.WriteLine($"[GtkWebViewHandler] Error in SendNavigating: {ex.Message}"); + DiagnosticLog.Error("GtkWebViewHandler", $"Error in SendNavigating: {ex.Message}", ex); } return false; }); } catch (Exception ex) { - Console.WriteLine($"[GtkWebViewHandler] Error dispatching navigation started: {ex.Message}"); + DiagnosticLog.Error("GtkWebViewHandler", $"Error dispatching navigation started: {ex.Message}", ex); } } private void OnNavigationCompleted(object? sender, (string Url, bool Success) e) { - Console.WriteLine($"[GtkWebViewHandler] Navigation completed: {e.Url} (Success: {e.Success})"); + DiagnosticLog.Debug("GtkWebViewHandler", $"Navigation completed: {e.Url} (Success: {e.Success})"); try { GLibNative.IdleAdd(() => @@ -151,19 +151,19 @@ public class GtkWebViewHandler : ViewHandler bool canGoForward = _platformWebView?.CanGoForward() ?? false; controller.CanGoBack = canGoBack; controller.CanGoForward = canGoForward; - Console.WriteLine($"[GtkWebViewHandler] Sent Navigated, CanGoBack={canGoBack}, CanGoForward={canGoForward}"); + DiagnosticLog.Debug("GtkWebViewHandler", $"Sent Navigated, CanGoBack={canGoBack}, CanGoForward={canGoForward}"); } } catch (Exception ex) { - Console.WriteLine($"[GtkWebViewHandler] Error in SendNavigated: {ex.Message}"); + DiagnosticLog.Error("GtkWebViewHandler", $"Error in SendNavigated: {ex.Message}", ex); } return false; }); } catch (Exception ex) { - Console.WriteLine($"[GtkWebViewHandler] Error dispatching navigation completed: {ex.Message}"); + DiagnosticLog.Error("GtkWebViewHandler", $"Error dispatching navigation completed: {ex.Message}", ex); } } @@ -175,7 +175,7 @@ public class GtkWebViewHandler : ViewHandler var hostService = GtkHostService.Instance; if (hostService.HostWindow == null || hostService.WebViewManager == null) { - Console.WriteLine("[GtkWebViewHandler] Warning: GTK host not initialized, cannot register WebView"); + DiagnosticLog.Warn("GtkWebViewHandler", "GTK host not initialized, cannot register WebView"); return; } @@ -186,7 +186,7 @@ public class GtkWebViewHandler : ViewHandler if (width <= 0 || height <= 0) { - Console.WriteLine($"[GtkWebViewHandler] Skipping invalid bounds: {bounds}"); + DiagnosticLog.Warn("GtkWebViewHandler", $"Skipping invalid bounds: {bounds}"); return; } @@ -194,12 +194,12 @@ public class GtkWebViewHandler : ViewHandler { hostService.HostWindow.AddWebView(_platformWebView.Widget, x, y, width, height); _isRegisteredWithHost = true; - Console.WriteLine($"[GtkWebViewHandler] Registered WebView at ({x}, {y}) size {width}x{height}"); + DiagnosticLog.Debug("GtkWebViewHandler", $"Registered WebView at ({x}, {y}) size {width}x{height}"); } else if (bounds != _lastBounds) { hostService.HostWindow.MoveResizeWebView(_platformWebView.Widget, x, y, width, height); - Console.WriteLine($"[GtkWebViewHandler] Updated WebView to ({x}, {y}) size {width}x{height}"); + DiagnosticLog.Debug("GtkWebViewHandler", $"Updated WebView to ({x}, {y}) size {width}x{height}"); } _lastBounds = bounds; @@ -213,7 +213,7 @@ public class GtkWebViewHandler : ViewHandler if (hostService.HostWindow != null) { hostService.HostWindow.RemoveWebView(_platformWebView.Widget); - Console.WriteLine("[GtkWebViewHandler] Unregistered WebView from host"); + DiagnosticLog.Debug("GtkWebViewHandler", "Unregistered WebView from host"); } _isRegisteredWithHost = false; } @@ -225,7 +225,7 @@ public class GtkWebViewHandler : ViewHandler return; var source = webView.Source; - Console.WriteLine($"[GtkWebViewHandler] MapSource: {source?.GetType().Name ?? "null"}"); + DiagnosticLog.Debug("GtkWebViewHandler", $"MapSource: {source?.GetType().Name ?? "null"}"); if (source is UrlWebViewSource urlSource) { @@ -247,19 +247,19 @@ public class GtkWebViewHandler : ViewHandler public static void MapGoBack(GtkWebViewHandler handler, IWebView webView, object? args) { - Console.WriteLine($"[GtkWebViewHandler] MapGoBack called, CanGoBack={handler._platformWebView?.CanGoBack()}"); + DiagnosticLog.Debug("GtkWebViewHandler", $"MapGoBack called, CanGoBack={handler._platformWebView?.CanGoBack()}"); handler._platformWebView?.GoBack(); } public static void MapGoForward(GtkWebViewHandler handler, IWebView webView, object? args) { - Console.WriteLine($"[GtkWebViewHandler] MapGoForward called, CanGoForward={handler._platformWebView?.CanGoForward()}"); + DiagnosticLog.Debug("GtkWebViewHandler", $"MapGoForward called, CanGoForward={handler._platformWebView?.CanGoForward()}"); handler._platformWebView?.GoForward(); } public static void MapReload(GtkWebViewHandler handler, IWebView webView, object? args) { - Console.WriteLine("[GtkWebViewHandler] MapReload called"); + DiagnosticLog.Debug("GtkWebViewHandler", "MapReload called"); handler._platformWebView?.Reload(); } } diff --git a/Handlers/GtkWebViewPlatformView.cs b/Handlers/GtkWebViewPlatformView.cs index a879b84..3565c0c 100644 --- a/Handlers/GtkWebViewPlatformView.cs +++ b/Handlers/GtkWebViewPlatformView.cs @@ -74,7 +74,7 @@ public sealed class GtkWebViewPlatformView : IDisposable Microsoft.Maui.Controls.Application.Current.RequestedThemeChanged += _themeChangedHandler; } - Console.WriteLine("[GtkWebViewPlatformView] Created WebKitWebView widget"); + DiagnosticLog.Debug("GtkWebViewPlatformView", "Created WebKitWebView widget"); } /// @@ -105,7 +105,7 @@ public sealed class GtkWebViewPlatformView : IDisposable var dialogType = (ScriptDialogType)(int)webkitDialogType; var message = WebKitNative.GetScriptDialogMessage(dialog) ?? ""; - Console.WriteLine($"[GtkWebViewPlatformView] Script dialog: type={dialogType}, message={message}"); + DiagnosticLog.Debug("GtkWebViewPlatformView", $"Script dialog: type={dialogType}, message={message}"); // Get the parent window for proper modal behavior IntPtr parentWindow = GtkHostService.Instance.HostWindow?.Window ?? IntPtr.Zero; @@ -166,7 +166,7 @@ public sealed class GtkWebViewPlatformView : IDisposable // Run the dialog synchronously - this blocks until user responds int response = GtkNative.gtk_dialog_run(gtkDialog); - Console.WriteLine($"[GtkWebViewPlatformView] Dialog response: {response}"); + DiagnosticLog.Debug("GtkWebViewPlatformView", $"Dialog response: {response}"); // Set the confirmed state for confirm dialogs if (dialogType == ScriptDialogType.Confirm || dialogType == ScriptDialogType.BeforeUnloadConfirm) @@ -184,7 +184,7 @@ public sealed class GtkWebViewPlatformView : IDisposable } catch (Exception ex) { - Console.WriteLine($"[GtkWebViewPlatformView] Error in OnScriptDialog: {ex.Message}"); + DiagnosticLog.Error("GtkWebViewPlatformView", $"Error in OnScriptDialog: {ex.Message}", ex); // Return false on error to let WebKitGTK try its default handling return false; } @@ -210,7 +210,7 @@ public sealed class GtkWebViewPlatformView : IDisposable if (gtkDialog == IntPtr.Zero) { - Console.WriteLine("[GtkWebViewPlatformView] Failed to create prompt dialog"); + DiagnosticLog.Error("GtkWebViewPlatformView", "Failed to create prompt dialog"); return false; } @@ -251,7 +251,7 @@ public sealed class GtkWebViewPlatformView : IDisposable // Run the dialog int response = GtkNative.gtk_dialog_run(gtkDialog); - Console.WriteLine($"[GtkWebViewPlatformView] Prompt dialog response: {response}"); + DiagnosticLog.Debug("GtkWebViewPlatformView", $"Prompt dialog response: {response}"); if (response == GtkNative.GTK_RESPONSE_OK) { @@ -261,7 +261,7 @@ public sealed class GtkWebViewPlatformView : IDisposable ? System.Runtime.InteropServices.Marshal.PtrToStringUTF8(textPtr) : ""; - Console.WriteLine($"[GtkWebViewPlatformView] Prompt text: {enteredText}"); + DiagnosticLog.Debug("GtkWebViewPlatformView", $"Prompt text: {enteredText}"); // Set the prompt response WebKitNative.SetScriptDialogPromptText(webkitDialog, enteredText ?? ""); @@ -278,7 +278,7 @@ public sealed class GtkWebViewPlatformView : IDisposable } catch (Exception ex) { - Console.WriteLine($"[GtkWebViewPlatformView] Error in HandlePromptDialog: {ex.Message}"); + DiagnosticLog.Error("GtkWebViewPlatformView", $"Error in HandlePromptDialog: {ex.Message}", ex); return false; } } @@ -299,7 +299,7 @@ public sealed class GtkWebViewPlatformView : IDisposable isDark = Microsoft.Maui.Controls.Application.Current?.RequestedTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark; } - Console.WriteLine($"[GtkWebViewPlatformView] ApplyDialogTheme: isDark={isDark}, UserAppTheme={Microsoft.Maui.Controls.Application.Current?.UserAppTheme}"); + DiagnosticLog.Debug("GtkWebViewPlatformView", $"ApplyDialogTheme: isDark={isDark}, UserAppTheme={Microsoft.Maui.Controls.Application.Current?.UserAppTheme}"); // Create comprehensive CSS based on the theme - targeting all dialog elements string css = isDark @@ -407,7 +407,7 @@ public sealed class GtkWebViewPlatformView : IDisposable } catch (Exception ex) { - Console.WriteLine($"[GtkWebViewPlatformView] Error applying dialog theme: {ex.Message}"); + DiagnosticLog.Error("GtkWebViewPlatformView", $"Error applying dialog theme: {ex.Message}", ex); } } @@ -419,17 +419,17 @@ public sealed class GtkWebViewPlatformView : IDisposable switch ((WebKitNative.WebKitLoadEvent)loadEvent) { case WebKitNative.WebKitLoadEvent.Started: - Console.WriteLine("[GtkWebViewPlatformView] Load started: " + uri); + DiagnosticLog.Debug("GtkWebViewPlatformView", "Load started: " + uri); NavigationStarted?.Invoke(this, uri); break; case WebKitNative.WebKitLoadEvent.Finished: _currentUri = uri; - Console.WriteLine("[GtkWebViewPlatformView] Load finished: " + uri); + DiagnosticLog.Debug("GtkWebViewPlatformView", "Load finished: " + uri); NavigationCompleted?.Invoke(this, (uri, true)); break; case WebKitNative.WebKitLoadEvent.Committed: _currentUri = uri; - Console.WriteLine("[GtkWebViewPlatformView] Load committed: " + uri); + DiagnosticLog.Debug("GtkWebViewPlatformView", "Load committed: " + uri); break; case WebKitNative.WebKitLoadEvent.Redirected: break; @@ -437,8 +437,7 @@ public sealed class GtkWebViewPlatformView : IDisposable } catch (Exception ex) { - Console.WriteLine("[GtkWebViewPlatformView] Error in OnLoadChanged: " + ex.Message); - Console.WriteLine("[GtkWebViewPlatformView] Stack trace: " + ex.StackTrace); + DiagnosticLog.Error("GtkWebViewPlatformView", "Error in OnLoadChanged: " + ex.Message, ex); } } @@ -447,7 +446,7 @@ public sealed class GtkWebViewPlatformView : IDisposable if (_widget != IntPtr.Zero) { WebKitNative.LoadUri(_widget, uri); - Console.WriteLine("[GtkWebViewPlatformView] Navigate to: " + uri); + DiagnosticLog.Debug("GtkWebViewPlatformView", "Navigate to: " + uri); } } @@ -456,7 +455,7 @@ public sealed class GtkWebViewPlatformView : IDisposable if (_widget != IntPtr.Zero) { WebKitNative.LoadHtml(_widget, html, baseUri); - Console.WriteLine("[GtkWebViewPlatformView] Load HTML content"); + DiagnosticLog.Debug("GtkWebViewPlatformView", "Load HTML content"); } } diff --git a/Handlers/ImageHandler.cs b/Handlers/ImageHandler.cs index a961e2b..959e5ce 100644 --- a/Handlers/ImageHandler.cs +++ b/Handlers/ImageHandler.cs @@ -5,6 +5,7 @@ using System.IO; using Microsoft.Maui.Controls; using Microsoft.Maui.Handlers; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform.Linux.Handlers; @@ -127,7 +128,7 @@ public partial class ImageHandler : ViewHandler if (image is Image img && img.WidthRequest > 0) { handler.PlatformView.WidthRequest = img.WidthRequest; - Console.WriteLine($"[ImageHandler] MapWidth: {img.WidthRequest}"); + DiagnosticLog.Debug("ImageHandler", $"MapWidth: {img.WidthRequest}"); } else if (image.Width > 0) { @@ -142,7 +143,7 @@ public partial class ImageHandler : ViewHandler if (image is Image img && img.HeightRequest > 0) { handler.PlatformView.HeightRequest = img.HeightRequest; - Console.WriteLine($"[ImageHandler] MapHeight: {img.HeightRequest}"); + DiagnosticLog.Debug("ImageHandler", $"MapHeight: {img.HeightRequest}"); } else if (image.Height > 0) { diff --git a/Handlers/LayoutHandler.Linux.cs b/Handlers/LayoutHandler.Linux.cs index 4ba0be3..23b5287 100644 --- a/Handlers/LayoutHandler.Linux.cs +++ b/Handlers/LayoutHandler.Linux.cs @@ -3,6 +3,7 @@ using Microsoft.Maui.Handlers; using Microsoft.Maui.Platform.Linux.Hosting; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform; @@ -279,13 +280,13 @@ public partial class GridHandler : LayoutHandler protected override void ConnectHandler(SkiaLayoutView platformView) { - Console.WriteLine($"[GridHandler.ConnectHandler] Called! VirtualView={VirtualView?.GetType().Name}, PlatformView={platformView?.GetType().Name}, MauiContext={(MauiContext != null ? "set" : "null")}"); + DiagnosticLog.Debug("GridHandler", $"ConnectHandler Called! VirtualView={VirtualView?.GetType().Name}, PlatformView={platformView?.GetType().Name}, MauiContext={(MauiContext != null ? "set" : "null")}"); base.ConnectHandler(platformView); // Map definitions on connect if (VirtualView is IGridLayout gridLayout && platformView is SkiaGrid grid && MauiContext != null) { - Console.WriteLine($"[GridHandler.ConnectHandler] Grid has {gridLayout.Count} children, RowDefs={gridLayout.RowDefinitions?.Count ?? 0}"); + DiagnosticLog.Debug("GridHandler", $"ConnectHandler Grid has {gridLayout.Count} children, RowDefs={gridLayout.RowDefinitions?.Count ?? 0}"); UpdateRowDefinitions(grid, gridLayout); UpdateColumnDefinitions(grid, gridLayout); @@ -295,13 +296,13 @@ public partial class GridHandler : LayoutHandler var child = gridLayout[i]; if (child == null) continue; - Console.WriteLine($"[GridHandler.ConnectHandler] Child[{i}]: {child.GetType().Name}, Handler={child.Handler?.GetType().Name ?? "null"}"); + DiagnosticLog.Debug("GridHandler", $"ConnectHandler Child[{i}]: {child.GetType().Name}, Handler={child.Handler?.GetType().Name ?? "null"}"); // Create handler for child if it doesn't exist if (child.Handler == null) { child.Handler = child.ToViewHandler(MauiContext); - Console.WriteLine($"[GridHandler.ConnectHandler] Created handler for child[{i}]: {child.Handler?.GetType().Name ?? "failed"}"); + DiagnosticLog.Debug("GridHandler", $"ConnectHandler Created handler for child[{i}]: {child.Handler?.GetType().Name ?? "failed"}"); } if (child.Handler?.PlatformView is SkiaView skiaChild) @@ -315,11 +316,11 @@ public partial class GridHandler : LayoutHandler rowSpan = Microsoft.Maui.Controls.Grid.GetRowSpan(mauiView); columnSpan = Microsoft.Maui.Controls.Grid.GetColumnSpan(mauiView); } - Console.WriteLine($"[GridHandler.ConnectHandler] Adding child[{i}] at row={row}, col={column}"); + DiagnosticLog.Debug("GridHandler", $"ConnectHandler Adding child[{i}] at row={row}, col={column}"); grid.AddChild(skiaChild, row, column, rowSpan, columnSpan); } } - Console.WriteLine($"[GridHandler.ConnectHandler] Grid now has {grid.Children.Count} SkiaView children"); + DiagnosticLog.Debug("GridHandler", $"ConnectHandler Grid now has {grid.Children.Count} SkiaView children"); } } diff --git a/Handlers/LayoutHandler.cs b/Handlers/LayoutHandler.cs index 0746ea2..de47eec 100644 --- a/Handlers/LayoutHandler.cs +++ b/Handlers/LayoutHandler.cs @@ -4,6 +4,7 @@ using Microsoft.Maui.Handlers; using Microsoft.Maui.Graphics; using Microsoft.Maui.Platform.Linux.Hosting; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform.Linux.Handlers; @@ -245,7 +246,7 @@ public partial class GridHandler : LayoutHandler // Don't call base - we handle children specially for Grid if (VirtualView is not IGridLayout gridLayout || MauiContext == null || platformView is not SkiaGrid grid) return; - Console.WriteLine($"[GridHandler] ConnectHandler: {gridLayout.Count} children, {gridLayout.RowDefinitions.Count} rows, {gridLayout.ColumnDefinitions.Count} cols"); + DiagnosticLog.Debug("GridHandler", $"ConnectHandler: {gridLayout.Count} children, {gridLayout.RowDefinitions.Count} rows, {gridLayout.ColumnDefinitions.Count} cols"); // Explicitly map BackgroundColor since it may be set before handler creation if (VirtualView is Microsoft.Maui.Controls.VisualElement ve && ve.BackgroundColor != null) @@ -258,7 +259,7 @@ public partial class GridHandler : LayoutHandler { var padding = paddable.Padding; platformView.Padding = padding; - Console.WriteLine($"[GridHandler] Applied Padding: L={padding.Left}, T={padding.Top}, R={padding.Right}, B={padding.Bottom}"); + DiagnosticLog.Debug("GridHandler", $"Applied Padding: L={padding.Left}, T={padding.Top}, R={padding.Right}, B={padding.Bottom}"); } // Map row/column definitions first @@ -271,7 +272,7 @@ public partial class GridHandler : LayoutHandler var child = gridLayout[i]; if (child == null) continue; - Console.WriteLine($"[GridHandler] Processing child {i}: {child.GetType().Name}"); + DiagnosticLog.Debug("GridHandler", $"Processing child {i}: {child.GetType().Name}"); // Create handler for child if it doesn't exist if (child.Handler == null) @@ -289,21 +290,20 @@ public partial class GridHandler : LayoutHandler columnSpan = Microsoft.Maui.Controls.Grid.GetColumnSpan(mauiView); } - Console.WriteLine($"[GridHandler] Child {i} at row={row}, col={column}, handler={child.Handler?.GetType().Name}"); + DiagnosticLog.Debug("GridHandler", $"Child {i} at row={row}, col={column}, handler={child.Handler?.GetType().Name}"); // Add child's platform view to our grid if (child.Handler?.PlatformView is SkiaView skiaChild) { grid.AddChild(skiaChild, row, column, rowSpan, columnSpan); - Console.WriteLine($"[GridHandler] Added child {i} to grid"); + DiagnosticLog.Debug("GridHandler", $"Added child {i} to grid"); } } - Console.WriteLine($"[GridHandler] ConnectHandler complete"); + DiagnosticLog.Debug("GridHandler", "ConnectHandler complete"); } catch (Exception ex) { - Console.WriteLine($"[GridHandler] EXCEPTION in ConnectHandler: {ex.GetType().Name}: {ex.Message}"); - Console.WriteLine($"[GridHandler] Stack trace: {ex.StackTrace}"); + DiagnosticLog.Error("GridHandler", $"EXCEPTION in ConnectHandler: {ex.GetType().Name}: {ex.Message}", ex); throw; } } diff --git a/Handlers/NavigationPageHandler.cs b/Handlers/NavigationPageHandler.cs index d4d982c..55e2093 100644 --- a/Handlers/NavigationPageHandler.cs +++ b/Handlers/NavigationPageHandler.cs @@ -7,6 +7,7 @@ using Microsoft.Maui.Graphics; using Microsoft.Maui.Controls; using Microsoft.Maui.Platform; using Microsoft.Maui.Platform.Linux.Hosting; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; using Svg.Skia; using System.Collections.Specialized; @@ -88,12 +89,12 @@ public partial class NavigationPageHandler : ViewHandler { - Console.WriteLine($"[NavigationPageHandler] ToolbarItem '{toolbarItem.Text}' clicked, invoking..."); + DiagnosticLog.Debug("NavigationPageHandler", $"ToolbarItem '{toolbarItem.Text}' clicked, invoking..."); // Use IMenuItemController to send the click if (toolbarItem is IMenuItemController menuController) { @@ -209,10 +210,10 @@ public partial class NavigationPageHandler : ViewHandler { - Console.WriteLine($"[NavigationPageHandler] ToolbarItems changed for '{page.Title}', action={e.Action}"); + DiagnosticLog.Debug("NavigationPageHandler", $"ToolbarItems changed for '{page.Title}', action={e.Action}"); MapToolbarItems(skiaPage, page); skiaPage.Invalidate(); }; @@ -229,9 +230,9 @@ public partial class NavigationPageHandler : ViewHandler private void OnAppearing(object? sender, EventArgs e) { - Console.WriteLine($"[PageHandler] OnAppearing received for: {VirtualView?.Title}"); + DiagnosticLog.Debug("PageHandler", $"OnAppearing received for: {VirtualView?.Title}"); (VirtualView as IPageController)?.SendAppearing(); } @@ -118,7 +119,7 @@ public partial class PageHandler : ViewHandler if (backgroundColor != null && backgroundColor != Colors.Transparent) { handler.PlatformView.BackgroundColor = backgroundColor; - Console.WriteLine($"[PageHandler] MapBackgroundColor: {backgroundColor}"); + DiagnosticLog.Debug("PageHandler", $"MapBackgroundColor: {backgroundColor}"); } } @@ -189,19 +190,19 @@ public partial class ContentPageHandler : PageHandler // Create handler for content if it doesn't exist if (content.Handler == null) { - Console.WriteLine($"[ContentPageHandler] Creating handler for content: {content.GetType().Name}"); + DiagnosticLog.Debug("ContentPageHandler", $"Creating handler for content: {content.GetType().Name}"); content.Handler = content.ToViewHandler(handler.MauiContext); } // The content's handler should provide the platform view if (content.Handler?.PlatformView is SkiaView skiaContent) { - Console.WriteLine($"[ContentPageHandler] Setting content: {skiaContent.GetType().Name}"); + DiagnosticLog.Debug("ContentPageHandler", $"Setting content: {skiaContent.GetType().Name}"); handler.PlatformView.Content = skiaContent; } else { - Console.WriteLine($"[ContentPageHandler] Content handler PlatformView is not SkiaView: {content.Handler?.PlatformView?.GetType().Name ?? "null"}"); + DiagnosticLog.Warn("ContentPageHandler", $"Content handler PlatformView is not SkiaView: {content.Handler?.PlatformView?.GetType().Name ?? "null"}"); } } else @@ -235,7 +236,7 @@ public partial class ContentPageHandler : PageHandler if (item.IconImageSource is FileImageSource fileSource) { // Icon loading would be async - simplified for now - Console.WriteLine($"[ContentPageHandler] Toolbar item icon: {fileSource.File}"); + DiagnosticLog.Debug("ContentPageHandler", $"Toolbar item icon: {fileSource.File}"); } platformView.ToolbarItems.Add(skiaItem); diff --git a/Handlers/ScrollViewHandler.cs b/Handlers/ScrollViewHandler.cs index d922890..d15481d 100644 --- a/Handlers/ScrollViewHandler.cs +++ b/Handlers/ScrollViewHandler.cs @@ -3,6 +3,7 @@ using Microsoft.Maui.Handlers; using Microsoft.Maui.Platform.Linux.Hosting; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform.Linux.Handlers; @@ -48,7 +49,7 @@ public partial class ScrollViewHandler : ViewHandler protected override SkiaShell CreatePlatformView() { - Console.WriteLine("[ShellHandler] CreatePlatformView - creating SkiaShell"); + DiagnosticLog.Debug("ShellHandler", "CreatePlatformView - creating SkiaShell"); return new SkiaShell(); } protected override void ConnectHandler(SkiaShell platformView) { - Console.WriteLine("[ShellHandler] ConnectHandler - connecting to SkiaShell"); + DiagnosticLog.Debug("ShellHandler", "ConnectHandler - connecting to SkiaShell"); base.ConnectHandler(platformView); platformView.FlyoutIsPresentedChanged += OnFlyoutIsPresentedChanged; platformView.Navigated += OnNavigated; @@ -116,20 +117,20 @@ public partial class ShellHandler : ViewHandler private void OnShellNavigating(object? sender, ShellNavigatingEventArgs e) { - Console.WriteLine($"[ShellHandler] Shell Navigating to: {e.Target?.Location}"); + DiagnosticLog.Debug("ShellHandler", $"Shell Navigating to: {e.Target?.Location}"); // Route to platform view if (PlatformView != null && e.Target?.Location != null) { var route = e.Target.Location.ToString().TrimStart('/'); - Console.WriteLine($"[ShellHandler] Routing to: {route}"); + DiagnosticLog.Debug("ShellHandler", $"Routing to: {route}"); PlatformView.GoToAsync(route); } } private void OnShellNavigated(object? sender, ShellNavigatedEventArgs e) { - Console.WriteLine($"[ShellHandler] Shell Navigated to: {e.Current?.Location}"); + DiagnosticLog.Debug("ShellHandler", $"Shell Navigated to: {e.Current?.Location}"); } private void SyncShellItems() @@ -230,7 +231,7 @@ public partial class ShellHandler : ViewHandler } catch (Exception ex) { - Console.WriteLine($"[ShellHandler] Error rendering content: {ex.Message}"); + DiagnosticLog.Error("ShellHandler", $"Error rendering content: {ex.Message}", ex); } return null; diff --git a/Handlers/WebViewHandler.Linux.cs b/Handlers/WebViewHandler.Linux.cs index 49ffdf0..0765bc3 100644 --- a/Handlers/WebViewHandler.Linux.cs +++ b/Handlers/WebViewHandler.Linux.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Maui.Handlers; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform; @@ -47,7 +48,7 @@ public partial class WebViewHandler : ViewHandler protected override LinuxWebView CreatePlatformView() { - Console.WriteLine("[WebViewHandler] Creating LinuxWebView"); + DiagnosticLog.Debug("WebViewHandler", "Creating LinuxWebView"); return new LinuxWebView(); } @@ -65,7 +66,7 @@ public partial class WebViewHandler : ViewHandler MapUserAgent(this, VirtualView); } - Console.WriteLine("[WebViewHandler] Handler connected"); + DiagnosticLog.Debug("WebViewHandler", "Handler connected"); } protected override void DisconnectHandler(LinuxWebView platformView) @@ -74,7 +75,7 @@ public partial class WebViewHandler : ViewHandler platformView.Navigated -= OnNavigated; base.DisconnectHandler(platformView); - Console.WriteLine("[WebViewHandler] Handler disconnected"); + DiagnosticLog.Debug("WebViewHandler", "Handler disconnected"); } private void OnNavigating(object? sender, WebViewNavigatingEventArgs e) @@ -104,7 +105,7 @@ public partial class WebViewHandler : ViewHandler if (source == null) return; - Console.WriteLine($"[WebViewHandler] MapSource: {source.GetType().Name}"); + DiagnosticLog.Debug("WebViewHandler", $"MapSource: {source.GetType().Name}"); if (source is IUrlWebViewSource urlSource && !string.IsNullOrEmpty(urlSource.Url)) { @@ -121,7 +122,7 @@ public partial class WebViewHandler : ViewHandler if (handler.PlatformView != null && !string.IsNullOrEmpty(webView.UserAgent)) { handler.PlatformView.UserAgent = webView.UserAgent; - Console.WriteLine($"[WebViewHandler] MapUserAgent: {webView.UserAgent}"); + DiagnosticLog.Debug("WebViewHandler", $"MapUserAgent: {webView.UserAgent}"); } } @@ -134,7 +135,7 @@ public partial class WebViewHandler : ViewHandler if (handler.PlatformView?.CanGoBack == true) { handler.PlatformView.GoBack(); - Console.WriteLine("[WebViewHandler] GoBack"); + DiagnosticLog.Debug("WebViewHandler", "GoBack"); } } @@ -143,14 +144,14 @@ public partial class WebViewHandler : ViewHandler if (handler.PlatformView?.CanGoForward == true) { handler.PlatformView.GoForward(); - Console.WriteLine("[WebViewHandler] GoForward"); + DiagnosticLog.Debug("WebViewHandler", "GoForward"); } } public static void MapReload(WebViewHandler handler, IWebView webView, object? args) { handler.PlatformView?.Reload(); - Console.WriteLine("[WebViewHandler] Reload"); + DiagnosticLog.Debug("WebViewHandler", "Reload"); } public static void MapEval(WebViewHandler handler, IWebView webView, object? args) @@ -158,7 +159,7 @@ public partial class WebViewHandler : ViewHandler if (args is string script) { handler.PlatformView?.Eval(script); - Console.WriteLine($"[WebViewHandler] Eval: {script.Substring(0, Math.Min(50, script.Length))}..."); + DiagnosticLog.Debug("WebViewHandler", $"Eval: {script.Substring(0, Math.Min(50, script.Length))}..."); } } @@ -178,7 +179,7 @@ public partial class WebViewHandler : ViewHandler { request.SetResult(null); } - Console.WriteLine($"[WebViewHandler] EvaluateJavaScriptAsync: {request.Script.Substring(0, Math.Min(50, request.Script.Length))}..."); + DiagnosticLog.Debug("WebViewHandler", $"EvaluateJavaScriptAsync: {request.Script.Substring(0, Math.Min(50, request.Script.Length))}..."); } } diff --git a/Handlers/WebViewHandler.cs b/Handlers/WebViewHandler.cs index ee46621..54d0202 100644 --- a/Handlers/WebViewHandler.cs +++ b/Handlers/WebViewHandler.cs @@ -4,6 +4,7 @@ using Microsoft.Maui.Controls; using Microsoft.Maui.Handlers; using Microsoft.Maui.Platform; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform.Linux.Handlers; @@ -89,30 +90,30 @@ public partial class WebViewHandler : ViewHandler public static void MapSource(WebViewHandler handler, IWebView webView) { - Console.WriteLine("[WebViewHandler] MapSource called"); + DiagnosticLog.Debug("WebViewHandler", "MapSource called"); if (handler.PlatformView == null) { - Console.WriteLine("[WebViewHandler] PlatformView is null!"); + DiagnosticLog.Warn("WebViewHandler", "PlatformView is null!"); return; } var source = webView.Source; - Console.WriteLine($"[WebViewHandler] Source type: {source?.GetType().Name ?? "null"}"); + DiagnosticLog.Debug("WebViewHandler", $"Source type: {source?.GetType().Name ?? "null"}"); if (source is UrlWebViewSource urlSource) { - Console.WriteLine($"[WebViewHandler] Loading URL: {urlSource.Url}"); + DiagnosticLog.Debug("WebViewHandler", $"Loading URL: {urlSource.Url}"); handler.PlatformView.Source = urlSource.Url ?? ""; } else if (source is HtmlWebViewSource htmlSource) { - Console.WriteLine($"[WebViewHandler] Loading HTML ({htmlSource.Html?.Length ?? 0} chars)"); - Console.WriteLine($"[WebViewHandler] HTML preview: {htmlSource.Html?.Substring(0, Math.Min(100, htmlSource.Html?.Length ?? 0))}..."); + DiagnosticLog.Debug("WebViewHandler", $"Loading HTML ({htmlSource.Html?.Length ?? 0} chars)"); + DiagnosticLog.Debug("WebViewHandler", $"HTML preview: {htmlSource.Html?.Substring(0, Math.Min(100, htmlSource.Html?.Length ?? 0))}..."); handler.PlatformView.Html = htmlSource.Html ?? ""; } else { - Console.WriteLine("[WebViewHandler] Unknown source type or null"); + DiagnosticLog.Debug("WebViewHandler", "Unknown source type or null"); } } diff --git a/Handlers/WindowHandler.cs b/Handlers/WindowHandler.cs index 4789167..b375dc5 100644 --- a/Handlers/WindowHandler.cs +++ b/Handlers/WindowHandler.cs @@ -5,6 +5,7 @@ using Microsoft.Maui.Handlers; using Microsoft.Maui.Graphics; using Microsoft.Maui.Controls; using Microsoft.Maui.Platform; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform.Linux.Handlers; @@ -81,19 +82,19 @@ public partial class WindowHandler : ElementHandler public static void MapContent(WindowHandler handler, IWindow window) { - Console.Error.WriteLine($"[WindowHandler] MapContent - PlatformView={handler.PlatformView != null}"); + DiagnosticLog.Debug("WindowHandler", $"MapContent - PlatformView={handler.PlatformView != null}"); if (handler.PlatformView is null) return; var content = window.Content; - Console.Error.WriteLine($"[WindowHandler] MapContent - content type={content?.GetType().Name}, handler={content?.Handler?.GetType().Name}"); + DiagnosticLog.Debug("WindowHandler", $"MapContent - content type={content?.GetType().Name}, handler={content?.Handler?.GetType().Name}"); if (content?.Handler?.PlatformView is SkiaView skiaContent) { - Console.Error.WriteLine($"[WindowHandler] MapContent - setting SkiaView content: {skiaContent.GetType().Name}"); + DiagnosticLog.Debug("WindowHandler", $"MapContent - setting SkiaView content: {skiaContent.GetType().Name}"); handler.PlatformView.Content = skiaContent; } else { - Console.Error.WriteLine($"[WindowHandler] MapContent - content has no SkiaView! Handler={content?.Handler}, PlatformView={content?.Handler?.PlatformView}"); + DiagnosticLog.Warn("WindowHandler", $"MapContent - content has no SkiaView! Handler={content?.Handler}, PlatformView={content?.Handler?.PlatformView}"); } } diff --git a/Hosting/LinuxProgramHost.cs b/Hosting/LinuxProgramHost.cs index c3cad78..817ffd2 100644 --- a/Hosting/LinuxProgramHost.cs +++ b/Hosting/LinuxProgramHost.cs @@ -48,7 +48,7 @@ public static class LinuxProgramHost // Initialize GTK for WebView support GtkHostService.Instance.Initialize(options.Title ?? "MAUI Application", options.Width, options.Height); - Console.WriteLine("[LinuxProgramHost] GTK initialized for WebView support"); + DiagnosticLog.Debug("LinuxProgramHost", "GTK initialized for WebView support"); // Create Linux application using var linuxApp = new LinuxApplication(); @@ -79,7 +79,7 @@ public static class LinuxProgramHost // Fallback to demo if no application view is available if (rootView == null) { - Console.WriteLine("No application page found. Showing demo UI."); + DiagnosticLog.Warn("LinuxProgramHost", "No application page found. Showing demo UI."); rootView = CreateDemoView(); } @@ -140,8 +140,8 @@ public static class LinuxProgramHost } catch (Exception ex) { - Console.WriteLine($"Error rendering application: {ex.Message}"); - Console.WriteLine(ex.StackTrace); + DiagnosticLog.Error("LinuxProgramHost", $"Error rendering application: {ex.Message}"); + DiagnosticLog.Error("LinuxProgramHost", ex.StackTrace ?? ""); return null; } } diff --git a/Hosting/LinuxViewRenderer.cs b/Hosting/LinuxViewRenderer.cs index 38f79dc..3976e3b 100644 --- a/Hosting/LinuxViewRenderer.cs +++ b/Hosting/LinuxViewRenderer.cs @@ -5,6 +5,7 @@ using System.Reflection; using Microsoft.Maui.Controls; using Microsoft.Maui.Graphics; using Microsoft.Maui.Platform; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform.Linux.Hosting; @@ -38,13 +39,13 @@ public class LinuxViewRenderer { if (CurrentSkiaShell == null) { - Console.WriteLine($"[NavigateToRoute] CurrentSkiaShell is null"); + DiagnosticLog.Warn("LinuxViewRenderer", "CurrentSkiaShell is null"); return false; } // Clean up the route - remove leading // or / var cleanRoute = route.TrimStart('/'); - Console.WriteLine($"[NavigateToRoute] Navigating to: {cleanRoute}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"NavigateToRoute: Navigating to: {cleanRoute}"); for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++) { @@ -52,13 +53,13 @@ public class LinuxViewRenderer if (section.Route.Equals(cleanRoute, StringComparison.OrdinalIgnoreCase) || section.Title.Equals(cleanRoute, StringComparison.OrdinalIgnoreCase)) { - Console.WriteLine($"[NavigateToRoute] Found section {i}: {section.Title}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"NavigateToRoute: Found section {i}: {section.Title}"); CurrentSkiaShell.NavigateToSection(i); return true; } } - Console.WriteLine($"[NavigateToRoute] Route not found: {cleanRoute}"); + DiagnosticLog.Warn("LinuxViewRenderer", $"NavigateToRoute: Route not found: {cleanRoute}"); return false; } @@ -74,17 +75,17 @@ public class LinuxViewRenderer /// True if successful public static bool PushPage(Page page) { - Console.WriteLine($"[PushPage] Pushing page: {page.GetType().Name}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"PushPage: Pushing page: {page.GetType().Name}"); if (CurrentSkiaShell == null) { - Console.WriteLine($"[PushPage] CurrentSkiaShell is null"); + DiagnosticLog.Warn("LinuxViewRenderer", "PushPage: CurrentSkiaShell is null"); return false; } if (CurrentRenderer == null) { - Console.WriteLine($"[PushPage] CurrentRenderer is null"); + DiagnosticLog.Warn("LinuxViewRenderer", "PushPage: CurrentRenderer is null"); return false; } @@ -96,18 +97,18 @@ public class LinuxViewRenderer if (skiaPage == null) { - Console.WriteLine($"[PushPage] Failed to render page through handler"); + DiagnosticLog.Warn("LinuxViewRenderer", "PushPage: Failed to render page through handler"); return false; } // Push onto SkiaShell's navigation stack CurrentSkiaShell.PushAsync(skiaPage, page.Title ?? "Detail"); - Console.WriteLine($"[PushPage] Successfully pushed page via handler system"); + DiagnosticLog.Debug("LinuxViewRenderer", "PushPage: Successfully pushed page via handler system"); return true; } catch (Exception ex) { - Console.WriteLine($"[PushPage] Error: {ex.Message}"); + DiagnosticLog.Error("LinuxViewRenderer", "PushPage failed", ex); return false; } } @@ -118,11 +119,11 @@ public class LinuxViewRenderer /// True if successful public static bool PopPage() { - Console.WriteLine($"[PopPage] Popping page"); + DiagnosticLog.Debug("LinuxViewRenderer", "PopPage: Popping page"); if (CurrentSkiaShell == null) { - Console.WriteLine($"[PopPage] CurrentSkiaShell is null"); + DiagnosticLog.Warn("LinuxViewRenderer", "PopPage: CurrentSkiaShell is null"); return false; } @@ -233,12 +234,12 @@ public class LinuxViewRenderer // Subscribe to MAUI Shell navigation events to update SkiaShell shell.Navigated += OnShellNavigated; - shell.Navigating += (s, e) => Console.WriteLine($"[Navigation] Navigating: {e.Target}"); + shell.Navigating += (s, e) => DiagnosticLog.Debug("LinuxViewRenderer", $"Navigation: Navigating: {e.Target}"); - Console.WriteLine($"[Navigation] Shell navigation events subscribed. Sections: {skiaShell.Sections.Count}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"Shell navigation events subscribed. Sections: {skiaShell.Sections.Count}"); for (int i = 0; i < skiaShell.Sections.Count; i++) { - Console.WriteLine($"[Navigation] Section {i}: Route='{skiaShell.Sections[i].Route}', Title='{skiaShell.Sections[i].Title}'"); + DiagnosticLog.Debug("LinuxViewRenderer", $"Section {i}: Route='{skiaShell.Sections[i].Route}', Title='{skiaShell.Sections[i].Title}'"); } return skiaShell; @@ -250,33 +251,33 @@ public class LinuxViewRenderer private static void ApplyShellColors(SkiaShell skiaShell, Shell shell) { bool isDark = Application.Current?.UserAppTheme == AppTheme.Dark; - Console.WriteLine($"[ApplyShellColors] Theme is: {(isDark ? "Dark" : "Light")}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"ApplyShellColors: Theme is: {(isDark ? "Dark" : "Light")}"); // Flyout background color if (shell.FlyoutBackgroundColor != null && shell.FlyoutBackgroundColor != Colors.Transparent) { skiaShell.FlyoutBackgroundColor = shell.FlyoutBackgroundColor; - Console.WriteLine($"[ApplyShellColors] FlyoutBackgroundColor from MAUI: {skiaShell.FlyoutBackgroundColor}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"ApplyShellColors: FlyoutBackgroundColor from MAUI: {skiaShell.FlyoutBackgroundColor}"); } else { skiaShell.FlyoutBackgroundColor = isDark ? Color.FromRgb(30, 30, 30) : Color.FromRgb(255, 255, 255); - Console.WriteLine($"[ApplyShellColors] Using default FlyoutBackgroundColor: {skiaShell.FlyoutBackgroundColor}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"ApplyShellColors: Using default FlyoutBackgroundColor: {skiaShell.FlyoutBackgroundColor}"); } // Flyout text color skiaShell.FlyoutTextColor = isDark ? Color.FromRgb(224, 224, 224) : Color.FromRgb(33, 33, 33); - Console.WriteLine($"[ApplyShellColors] FlyoutTextColor: {skiaShell.FlyoutTextColor}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"ApplyShellColors: FlyoutTextColor: {skiaShell.FlyoutTextColor}"); // Content background color skiaShell.ContentBackgroundColor = isDark ? Color.FromRgb(18, 18, 18) : Color.FromRgb(250, 250, 250); - Console.WriteLine($"[ApplyShellColors] ContentBackgroundColor: {skiaShell.ContentBackgroundColor}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"ApplyShellColors: ContentBackgroundColor: {skiaShell.ContentBackgroundColor}"); // NavBar background color if (shell.BackgroundColor != null && shell.BackgroundColor != Colors.Transparent) @@ -294,27 +295,27 @@ public class LinuxViewRenderer /// private static void OnShellNavigated(object? sender, ShellNavigatedEventArgs e) { - Console.WriteLine($"[Navigation] OnShellNavigated called - Source: {e.Source}, Current: {e.Current?.Location}, Previous: {e.Previous?.Location}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"OnShellNavigated called - Source: {e.Source}, Current: {e.Current?.Location}, Previous: {e.Previous?.Location}"); if (CurrentSkiaShell == null || CurrentMauiShell == null) { - Console.WriteLine($"[Navigation] CurrentSkiaShell or CurrentMauiShell is null"); + DiagnosticLog.Warn("LinuxViewRenderer", "CurrentSkiaShell or CurrentMauiShell is null"); return; } // Get the current route from the Shell var currentState = CurrentMauiShell.CurrentState; var location = currentState?.Location?.OriginalString ?? ""; - Console.WriteLine($"[Navigation] Location: {location}, Sections: {CurrentSkiaShell.Sections.Count}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"Navigation: Location: {location}, Sections: {CurrentSkiaShell.Sections.Count}"); // Find the matching section in SkiaShell by route for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++) { var section = CurrentSkiaShell.Sections[i]; - Console.WriteLine($"[Navigation] Checking section {i}: Route='{section.Route}', Title='{section.Title}'"); + DiagnosticLog.Debug("LinuxViewRenderer", $"Navigation: Checking section {i}: Route='{section.Route}', Title='{section.Title}'"); if (!string.IsNullOrEmpty(section.Route) && location.Contains(section.Route, StringComparison.OrdinalIgnoreCase)) { - Console.WriteLine($"[Navigation] Match found by route! Navigating to section {i}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"Navigation: Match found by route! Navigating to section {i}"); if (i != CurrentSkiaShell.CurrentSectionIndex) { CurrentSkiaShell.NavigateToSection(i); @@ -323,7 +324,7 @@ public class LinuxViewRenderer } if (!string.IsNullOrEmpty(section.Title) && location.Contains(section.Title, StringComparison.OrdinalIgnoreCase)) { - Console.WriteLine($"[Navigation] Match found by title! Navigating to section {i}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"Navigation: Match found by title! Navigating to section {i}"); if (i != CurrentSkiaShell.CurrentSectionIndex) { CurrentSkiaShell.NavigateToSection(i); @@ -331,7 +332,7 @@ public class LinuxViewRenderer return; } } - Console.WriteLine($"[Navigation] No matching section found for location: {location}"); + DiagnosticLog.Warn("LinuxViewRenderer", $"Navigation: No matching section found for location: {location}"); } /// @@ -476,7 +477,7 @@ public class LinuxViewRenderer if (cp.BackgroundColor != null && cp.BackgroundColor != Colors.Transparent) { bgColor = cp.BackgroundColor; - Console.WriteLine($"[CreateShellContentPage] Page BackgroundColor: {bgColor}"); + DiagnosticLog.Debug("LinuxViewRenderer", $"CreateShellContentPage: Page BackgroundColor: {bgColor}"); } if (contentView is SkiaScrollView scrollView) diff --git a/Hosting/MauiHandlerExtensions.cs b/Hosting/MauiHandlerExtensions.cs index b20c0ee..9dfc6cd 100644 --- a/Hosting/MauiHandlerExtensions.cs +++ b/Hosting/MauiHandlerExtensions.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Microsoft.Maui.Controls; using Microsoft.Maui.Platform.Linux.Handlers; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform.Linux.Hosting; @@ -81,7 +82,7 @@ public static class MauiHandlerExtensions if (LinuxHandlerMap.TryGetValue(type, out Func? factory)) { handler = factory(); - Console.WriteLine($"[ToHandler] Using Linux handler for {type.Name}: {handler.GetType().Name}"); + DiagnosticLog.Debug("MauiHandlerExtensions", $"Using Linux handler for {type.Name}: {handler.GetType().Name}"); } else { @@ -101,7 +102,7 @@ public static class MauiHandlerExtensions if (bestFactory != null) { handler = bestFactory(); - Console.WriteLine($"[ToHandler] Using Linux handler (via base {bestMatch!.Name}) for {type.Name}: {handler.GetType().Name}"); + DiagnosticLog.Debug("MauiHandlerExtensions", $"Using Linux handler (via base {bestMatch!.Name}) for {type.Name}: {handler.GetType().Name}"); } } @@ -109,7 +110,7 @@ public static class MauiHandlerExtensions if (handler == null) { handler = mauiContext.Handlers.GetHandler(type); - Console.WriteLine($"[ToHandler] Using MAUI handler for {type.Name}: {handler?.GetType().Name ?? "null"}"); + DiagnosticLog.Debug("MauiHandlerExtensions", $"Using MAUI handler for {type.Name}: {handler?.GetType().Name ?? "null"}"); } if (handler != null) diff --git a/LinuxApplication.cs b/LinuxApplication.cs index 8b126f2..2630c01 100644 --- a/LinuxApplication.cs +++ b/LinuxApplication.cs @@ -65,7 +65,7 @@ public class LinuxApplication : IDisposable Interlocked.Increment(ref _invalidateCount); if (currentThread != _gtkThreadId && _gtkThreadId != 0) { - Console.WriteLine($"[DIAG] ⚠️ Invalidate from WRONG THREAD! GTK={_gtkThreadId}, Current={currentThread}, Source={source}"); + DiagnosticLog.Warn("LinuxApplication", $"Invalidate from WRONG THREAD! GTK={_gtkThreadId}, Current={currentThread}, Source={source}"); } } @@ -78,23 +78,25 @@ public class LinuxApplication : IDisposable Interlocked.Increment(ref _requestRedrawCount); if (currentThread != _gtkThreadId && _gtkThreadId != 0) { - Console.WriteLine($"[DIAG] ⚠️ RequestRedraw from WRONG THREAD! GTK={_gtkThreadId}, Current={currentThread}"); + DiagnosticLog.Warn("LinuxApplication", $"RequestRedraw from WRONG THREAD! GTK={_gtkThreadId}, Current={currentThread}"); } } private static void StartHeartbeat() { _gtkThreadId = Environment.CurrentManagedThreadId; - Console.WriteLine($"[DIAG] GTK thread ID: {_gtkThreadId}"); + DiagnosticLog.Info("LinuxApplication", $"GTK thread ID: {_gtkThreadId}"); GLibNative.TimeoutAdd(250, () => { + if (!DiagnosticLog.IsEnabled) + return true; DateTime now = DateTime.Now; if ((now - _lastCounterReset).TotalSeconds >= 1.0) { int invalidates = Interlocked.Exchange(ref _invalidateCount, 0); int redraws = Interlocked.Exchange(ref _requestRedrawCount, 0); int draws = Interlocked.Exchange(ref _drawCount, 0); - Console.WriteLine($"[DIAG] ❤️ Heartbeat | Invalidate={invalidates}/s, RequestRedraw={redraws}/s, Draw={draws}/s"); + DiagnosticLog.Debug("LinuxApplication", $"Heartbeat | Invalidate={invalidates}/s, RequestRedraw={redraws}/s, Draw={draws}/s"); _lastCounterReset = now; } return true; @@ -258,11 +260,11 @@ public class LinuxApplication : IDisposable IntPtr argv = IntPtr.Zero; if (!GtkNative.gtk_init_check(ref argc, ref argv)) { - Console.WriteLine("[LinuxApplication] Warning: GTK initialization failed - WebView may not work"); + DiagnosticLog.Warn("LinuxApplication", "GTK initialization failed - WebView may not work"); } else { - Console.WriteLine("[LinuxApplication] GTK pre-initialized for WebView support"); + DiagnosticLog.Debug("LinuxApplication", "GTK pre-initialized for WebView support"); } // Set application name for desktop integration (taskbar, etc.) @@ -275,12 +277,12 @@ public class LinuxApplication : IDisposable string prgName = appName.Replace(" ", ""); GtkNative.g_set_prgname(prgName); GtkNative.g_set_application_name(appName); - Console.WriteLine($"[LinuxApplication] Set application name: {appName} (prgname: {prgName})"); + DiagnosticLog.Debug("LinuxApplication", $"Set application name: {appName} (prgname: {prgName})"); // Initialize dispatcher LinuxDispatcher.Initialize(); DispatcherProvider.SetCurrent(LinuxDispatcherProvider.Instance); - Console.WriteLine("[LinuxApplication] Dispatcher initialized"); + DiagnosticLog.Debug("LinuxApplication", "Dispatcher initialized"); var options = app.Services.GetService() ?? new LinuxApplicationOptions(); @@ -310,16 +312,16 @@ public class LinuxApplication : IDisposable // Set initial theme based on system theme var systemTheme = SystemThemeService.Instance.CurrentTheme; - Console.WriteLine($"[LinuxApplication] System theme detected at startup: {systemTheme}"); + DiagnosticLog.Debug("LinuxApplication", $"System theme detected at startup: {systemTheme}"); if (systemTheme == SystemTheme.Dark) { mauiApplication.UserAppTheme = AppTheme.Dark; - Console.WriteLine("[LinuxApplication] Set initial UserAppTheme to Dark based on system theme"); + DiagnosticLog.Debug("LinuxApplication", "Set initial UserAppTheme to Dark based on system theme"); } else { mauiApplication.UserAppTheme = AppTheme.Light; - Console.WriteLine("[LinuxApplication] Set initial UserAppTheme to Light based on system theme"); + DiagnosticLog.Debug("LinuxApplication", "Set initial UserAppTheme to Light based on system theme"); } // Initialize GTK theme service and apply initial CSS @@ -330,7 +332,7 @@ public class LinuxApplication : IDisposable { if (e.PropertyName == "UserAppTheme") { - Console.WriteLine($"[LinuxApplication] User theme changed to: {mauiApplication.UserAppTheme}"); + DiagnosticLog.Debug("LinuxApplication", $"User theme changed to: {mauiApplication.UserAppTheme}"); // Apply GTK CSS for dialogs, menus, and window decorations GtkThemeService.ApplyTheme(); @@ -355,14 +357,14 @@ public class LinuxApplication : IDisposable // Handle system theme changes (e.g., GNOME/KDE dark mode toggle) SystemThemeService.Instance.ThemeChanged += (s, e) => { - Console.WriteLine($"[LinuxApplication] System theme changed to: {e.NewTheme}"); + DiagnosticLog.Debug("LinuxApplication", $"System theme changed to: {e.NewTheme}"); // Update MAUI's UserAppTheme to match system theme // This will trigger the PropertyChanged handler which does the refresh var newAppTheme = e.NewTheme == SystemTheme.Dark ? AppTheme.Dark : AppTheme.Light; if (mauiApplication.UserAppTheme != newAppTheme) { - Console.WriteLine($"[LinuxApplication] Setting UserAppTheme to {newAppTheme} to match system"); + DiagnosticLog.Debug("LinuxApplication", $"Setting UserAppTheme to {newAppTheme} to match system"); mauiApplication.UserAppTheme = newAppTheme; } else @@ -397,9 +399,9 @@ public class LinuxApplication : IDisposable var mauiWindow = createWindowMethod.Invoke(mauiApplication, new object?[] { null }) as Microsoft.Maui.Controls.Window; if (mauiWindow != null) { - Console.WriteLine($"[LinuxApplication] Got Window from CreateWindow: {mauiWindow.GetType().Name}"); + DiagnosticLog.Debug("LinuxApplication", $"Got Window from CreateWindow: {mauiWindow.GetType().Name}"); mainPage = mauiWindow.Page; - Console.WriteLine($"[LinuxApplication] Window.Page: {mainPage?.GetType().Name}"); + DiagnosticLog.Debug("LinuxApplication", $"Window.Page: {mainPage?.GetType().Name}"); // Add to windows list var windowsField = typeof(Application).GetField("_windows", @@ -415,13 +417,13 @@ public class LinuxApplication : IDisposable } catch (Exception ex) { - Console.WriteLine($"[LinuxApplication] CreateWindow failed: {ex.Message}"); + DiagnosticLog.Error("LinuxApplication", $"CreateWindow failed: {ex.Message}"); } // Fall back to deprecated MainPage if CreateWindow didn't work if (mainPage == null && mauiApplication.MainPage != null) { - Console.WriteLine($"[LinuxApplication] Falling back to MainPage: {mauiApplication.MainPage.GetType().Name}"); + DiagnosticLog.Debug("LinuxApplication", $"Falling back to MainPage: {mauiApplication.MainPage.GetType().Name}"); mainPage = mauiApplication.MainPage; var windowsField = typeof(Application).GetField("_windows", @@ -645,13 +647,13 @@ public class LinuxApplication : IDisposable _mainWindow.Show(); Render(); - Console.WriteLine("[LinuxApplication] Starting event loop"); + DiagnosticLog.Debug("LinuxApplication", "Starting event loop"); while (_mainWindow.IsRunning) { _loopCounter++; if (_loopCounter % 1000 == 0) { - Console.WriteLine($"[LinuxApplication] Loop iteration {_loopCounter}"); + DiagnosticLog.Debug("LinuxApplication", $"Loop iteration {_loopCounter}"); } _mainWindow.ProcessEvents(); @@ -660,7 +662,7 @@ public class LinuxApplication : IDisposable Render(); Thread.Sleep(1); } - Console.WriteLine("[LinuxApplication] Event loop ended"); + DiagnosticLog.Debug("LinuxApplication", "Event loop ended"); } private void RunGtk() @@ -691,7 +693,7 @@ public class LinuxApplication : IDisposable /// private void RefreshPageForThemeChange() { - Console.WriteLine("[LinuxApplication] RefreshPageForThemeChange - forcing property updates"); + DiagnosticLog.Debug("LinuxApplication", "RefreshPageForThemeChange - forcing property updates"); // First, try to trigger MAUI's RequestedThemeChanged event using reflection // This ensures AppThemeBinding bindings re-evaluate @@ -714,7 +716,7 @@ public class LinuxApplication : IDisposable var app = Application.Current; if (app == null) return; - Console.WriteLine($"[LinuxApplication] Theme is now: {app.UserAppTheme}, RequestedTheme: {app.RequestedTheme}"); + DiagnosticLog.Debug("LinuxApplication", $"Theme is now: {app.UserAppTheme}, RequestedTheme: {app.RequestedTheme}"); } private void RefreshViewTheme(SkiaView view) @@ -765,7 +767,7 @@ public class LinuxApplication : IDisposable } catch (Exception ex) { - Console.WriteLine($"[LinuxApplication] Error refreshing theme for {mauiView.GetType().Name}: {ex.Message}"); + DiagnosticLog.Error("LinuxApplication", $"Error refreshing theme for {mauiView.GetType().Name}: {ex.Message}"); } } @@ -792,13 +794,13 @@ public class LinuxApplication : IDisposable { try { - Console.WriteLine($"[LinuxApplication] Refreshing page theme: {page.MauiPage?.GetType().Name}"); + DiagnosticLog.Debug("LinuxApplication", $"Refreshing page theme: {page.MauiPage?.GetType().Name}"); pageHandler.UpdateValue(nameof(IView.Background)); pageHandler.UpdateValue("BackgroundColor"); } catch (Exception ex) { - Console.WriteLine($"[LinuxApplication] Error refreshing page theme: {ex.Message}"); + DiagnosticLog.Error("LinuxApplication", $"Error refreshing page theme: {ex.Message}"); } } @@ -941,7 +943,7 @@ public class LinuxApplication : IDisposable private void OnPointerPressed(object? sender, PointerEventArgs e) { - Console.WriteLine($"[LinuxApplication] OnPointerPressed at ({e.X}, {e.Y}), Button={e.Button}"); + DiagnosticLog.Debug("LinuxApplication", $"OnPointerPressed at ({e.X}, {e.Y}), Button={e.Button}"); // Route to context menu if one is active if (LinuxDialogService.HasContextMenu) @@ -962,7 +964,7 @@ public class LinuxApplication : IDisposable // Check for popup overlay first var popupOwner = SkiaView.GetPopupOwnerAt(e.X, e.Y); var hitView = popupOwner ?? _rootView.HitTest(e.X, e.Y); - Console.WriteLine($"[LinuxApplication] HitView: {hitView?.GetType().Name ?? "null"}, rootView: {_rootView.GetType().Name}"); + DiagnosticLog.Debug("LinuxApplication", $"HitView: {hitView?.GetType().Name ?? "null"}, rootView: {_rootView.GetType().Name}"); if (hitView != null) { @@ -975,7 +977,7 @@ public class LinuxApplication : IDisposable FocusedView = hitView; } - Console.WriteLine($"[LinuxApplication] Calling OnPointerPressed on {hitView.GetType().Name}"); + DiagnosticLog.Debug("LinuxApplication", $"Calling OnPointerPressed on {hitView.GetType().Name}"); hitView.OnPointerPressed(e); } else @@ -1018,16 +1020,16 @@ public class LinuxApplication : IDisposable private void OnScroll(object? sender, ScrollEventArgs e) { - Console.WriteLine($"[LinuxApplication] OnScroll - X={e.X}, Y={e.Y}, DeltaX={e.DeltaX}, DeltaY={e.DeltaY}"); + DiagnosticLog.Debug("LinuxApplication", $"OnScroll - X={e.X}, Y={e.Y}, DeltaX={e.DeltaX}, DeltaY={e.DeltaY}"); if (_rootView != null) { var hitView = _rootView.HitTest(e.X, e.Y); - Console.WriteLine($"[LinuxApplication] HitView: {hitView?.GetType().Name ?? "null"}"); + DiagnosticLog.Debug("LinuxApplication", $"HitView: {hitView?.GetType().Name ?? "null"}"); // Bubble scroll events up to find a ScrollView var view = hitView; while (view != null) { - Console.WriteLine($"[LinuxApplication] Bubbling to: {view.GetType().Name}"); + DiagnosticLog.Debug("LinuxApplication", $"Bubbling to: {view.GetType().Name}"); if (view is SkiaScrollView scrollView) { scrollView.OnScroll(e); @@ -1048,7 +1050,7 @@ public class LinuxApplication : IDisposable // GTK Event Handlers private void OnGtkDrawRequested(object? sender, EventArgs e) { - Console.WriteLine("[DIAG] >>> OnGtkDrawRequested ENTER"); + DiagnosticLog.Debug("LinuxApplication", ">>> OnGtkDrawRequested ENTER"); LogDraw(); var surface = _gtkWindow?.SkiaSurface; if (surface?.Canvas != null && _rootView != null) @@ -1057,12 +1059,12 @@ public class LinuxApplication : IDisposable ? new SKColor(32, 33, 36) : SKColors.White; surface.Canvas.Clear(bgColor); - Console.WriteLine("[DIAG] Drawing rootView..."); + DiagnosticLog.Debug("LinuxApplication", "Drawing rootView..."); _rootView.Draw(surface.Canvas); - Console.WriteLine("[DIAG] Drawing dialogs..."); + DiagnosticLog.Debug("LinuxApplication", "Drawing dialogs..."); var bounds = new SKRect(0, 0, surface.Width, surface.Height); LinuxDialogService.DrawDialogs(surface.Canvas, bounds); - Console.WriteLine("[DIAG] <<< OnGtkDrawRequested EXIT"); + DiagnosticLog.Debug("LinuxApplication", "<<< OnGtkDrawRequested EXIT"); } } @@ -1075,7 +1077,7 @@ public class LinuxApplication : IDisposable private void OnGtkPointerPressed(object? sender, (double X, double Y, int Button) e) { string buttonName = e.Button == 1 ? "Left" : e.Button == 2 ? "Middle" : e.Button == 3 ? "Right" : $"Unknown({e.Button})"; - Console.WriteLine($"[LinuxApplication.GTK] PointerPressed at ({e.X:F1}, {e.Y:F1}), Button={e.Button} ({buttonName})"); + DiagnosticLog.Debug("LinuxApplication", $"GTK PointerPressed at ({e.X:F1}, {e.Y:F1}), Button={e.Button} ({buttonName})"); // Route to dialog if one is active if (LinuxDialogService.HasActiveDialog) @@ -1098,12 +1100,12 @@ public class LinuxApplication : IDisposable if (_rootView == null) { - Console.WriteLine("[LinuxApplication.GTK] _rootView is null!"); + DiagnosticLog.Warn("LinuxApplication", "GTK _rootView is null!"); return; } var hitView = _rootView.HitTest((float)e.X, (float)e.Y); - Console.WriteLine($"[LinuxApplication.GTK] HitView: {hitView?.GetType().Name ?? "null"}"); + DiagnosticLog.Debug("LinuxApplication", $"GTK HitView: {hitView?.GetType().Name ?? "null"}"); if (hitView != null) { @@ -1116,17 +1118,17 @@ public class LinuxApplication : IDisposable _capturedView = hitView; var button = e.Button == 1 ? PointerButton.Left : e.Button == 2 ? PointerButton.Middle : PointerButton.Right; var args = new PointerEventArgs((float)e.X, (float)e.Y, button); - Console.WriteLine("[DIAG] >>> Before OnPointerPressed"); + DiagnosticLog.Debug("LinuxApplication", ">>> Before OnPointerPressed"); hitView.OnPointerPressed(args); - Console.WriteLine("[DIAG] <<< After OnPointerPressed, calling RequestRedraw"); + DiagnosticLog.Debug("LinuxApplication", "<<< After OnPointerPressed, calling RequestRedraw"); _gtkWindow?.RequestRedraw(); - Console.WriteLine("[DIAG] <<< After RequestRedraw, returning from handler"); + DiagnosticLog.Debug("LinuxApplication", "<<< After RequestRedraw, returning from handler"); } } private void OnGtkPointerReleased(object? sender, (double X, double Y, int Button) e) { - Console.WriteLine("[DIAG] >>> OnGtkPointerReleased ENTER"); + DiagnosticLog.Debug("LinuxApplication", ">>> OnGtkPointerReleased ENTER"); // Route to dialog if one is active if (LinuxDialogService.HasActiveDialog) @@ -1144,12 +1146,12 @@ public class LinuxApplication : IDisposable { var button = e.Button == 1 ? PointerButton.Left : e.Button == 2 ? PointerButton.Middle : PointerButton.Right; var args = new PointerEventArgs((float)e.X, (float)e.Y, button); - Console.WriteLine($"[DIAG] Calling OnPointerReleased on {_capturedView.GetType().Name}"); + DiagnosticLog.Debug("LinuxApplication", $"Calling OnPointerReleased on {_capturedView.GetType().Name}"); _capturedView.OnPointerReleased(args); - Console.WriteLine("[DIAG] OnPointerReleased returned"); + DiagnosticLog.Debug("LinuxApplication", "OnPointerReleased returned"); _capturedView = null; _gtkWindow?.RequestRedraw(); - Console.WriteLine("[DIAG] <<< OnGtkPointerReleased EXIT (captured path)"); + DiagnosticLog.Debug("LinuxApplication", "<<< OnGtkPointerReleased EXIT (captured path)"); } else { diff --git a/Native/GLibNative.cs b/Native/GLibNative.cs index 9305ed2..5a5dfae 100644 --- a/Native/GLibNative.cs +++ b/Native/GLibNative.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform.Linux.Native; @@ -38,7 +39,7 @@ public static class GLibNative } catch (Exception ex) { - Console.WriteLine("[GLibNative] Error in idle callback: " + ex.Message); + DiagnosticLog.Error("GLibNative", "Error in idle callback", ex); } if (!flag) { @@ -68,7 +69,7 @@ public static class GLibNative } catch (Exception ex) { - Console.WriteLine("[GLibNative] Error in timeout callback: " + ex.Message); + DiagnosticLog.Error("GLibNative", "Error in timeout callback", ex); } if (!flag) { diff --git a/Native/WebKitNative.cs b/Native/WebKitNative.cs index 4d49242..bf87de1 100644 --- a/Native/WebKitNative.cs +++ b/Native/WebKitNative.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform.Linux.Native; @@ -131,14 +132,14 @@ internal static class WebKitNative _handle = dlopen(text, 258); if (_handle != IntPtr.Zero) { - Console.WriteLine("[WebKitNative] Loaded " + text); + DiagnosticLog.Debug("WebKitNative", "Loaded " + text); break; } } if (_handle == IntPtr.Zero) { - Console.WriteLine("[WebKitNative] Failed to load WebKitGTK library"); + DiagnosticLog.Warn("WebKitNative", "Failed to load WebKitGTK library"); return false; } @@ -170,7 +171,7 @@ internal static class WebKitNative if (intPtr != IntPtr.Zero) { _gSignalConnectData = Marshal.GetDelegateForFunctionPointer(intPtr); - Console.WriteLine("[WebKitNative] Loaded g_signal_connect_data"); + DiagnosticLog.Debug("WebKitNative", "Loaded g_signal_connect_data"); } } @@ -297,7 +298,7 @@ internal static class WebKitNative { if (_gSignalConnectData == null || webView == IntPtr.Zero) { - Console.WriteLine("[WebKitNative] Cannot connect load-changed: signal connect not available"); + DiagnosticLog.Warn("WebKitNative", "Cannot connect load-changed: signal connect not available"); return 0uL; } _loadChangedCallbacks[webView] = callback; @@ -317,7 +318,7 @@ internal static class WebKitNative { if (_gSignalConnectData == null || webView == IntPtr.Zero) { - Console.WriteLine("[WebKitNative] Cannot connect script-dialog: signal connect not available"); + DiagnosticLog.Warn("WebKitNative", "Cannot connect script-dialog: signal connect not available"); return 0uL; } _scriptDialogCallbacks[webView] = callback; diff --git a/OpenMaui.Controls.Linux.csproj b/OpenMaui.Controls.Linux.csproj index 66db52d..340e5f9 100644 --- a/OpenMaui.Controls.Linux.csproj +++ b/OpenMaui.Controls.Linux.csproj @@ -14,18 +14,18 @@ OpenMaui.Controls.Linux - 1.0.0-rc.1 + 1.0.0 MarketAlly LLC, David H. Friedel Jr. MarketAlly LLC OpenMaui Linux Controls Linux desktop support for .NET MAUI applications using SkiaSharp rendering. Supports X11 and Wayland display servers with 35+ controls, platform services, and accessibility support. - Copyright 2025 MarketAlly LLC + Copyright 2025-2026 MarketAlly LLC MIT https://git.marketally.com/open-maui/maui-linux https://git.marketally.com/open-maui/maui-linux.git git maui;linux;desktop;skia;gui;cross-platform;dotnet;x11;wayland;openmaui - RC1: 100% .NET MAUI API compliance - all public APIs use MAUI types (Color, Rect, Size, Thickness, double). Full XAML support with BindableProperty for all controls, Visual State Manager integration, data binding, and XAML styles. 217 passing tests. + 1.0: 100% .NET MAUI API compliance - all public APIs use MAUI types (Color, Rect, Size, Thickness, double). Full XAML support with BindableProperty for all controls, Visual State Manager integration, data binding, and XAML styles. 217 passing tests. README.md icon.png false diff --git a/OpenMaui.Controls.Linux.nuspec b/OpenMaui.Controls.Linux.nuspec index 94150ad..017a26b 100644 --- a/OpenMaui.Controls.Linux.nuspec +++ b/OpenMaui.Controls.Linux.nuspec @@ -2,13 +2,13 @@ OpenMaui.Controls.Linux - 1.0.0-preview.1 + 1.0.0 OpenMaui Linux Controls MarketAlly LLC, David H. Friedel Jr. MarketAlly LLC MIT - https://github.com/open-maui/maui-linux - https://raw.githubusercontent.com/dotnet/maui/main/assets/icon.png + https://git.marketally.com/open-maui/maui-linux + https://git.marketally.com/open-maui/maui-linux/raw/branch/main/assets/icon.png Linux desktop support for .NET MAUI applications. This package enables running MAUI applications on Linux desktop environments using SkiaSharp for rendering. @@ -24,22 +24,16 @@ Features: Developed by MarketAlly LLC. Lead Architect: David H. Friedel Jr. -Initial release: -- Core SkiaSharp-based rendering engine -- X11 window management with full input handling -- 35+ control implementations -- 18 platform services -- Accessibility support -- 216 unit tests +1.0: 100% .NET MAUI API compliance - all public APIs use MAUI types (Color, Rect, Size, Thickness, double). Full XAML support with BindableProperty for all controls, Visual State Manager integration, data binding, and XAML styles. Core SkiaSharp-based rendering engine, X11 window management with full input handling, 35+ control implementations, 18 platform services, accessibility support. 217 passing tests. - Copyright 2025 MarketAlly LLC + Copyright 2025-2026 MarketAlly LLC maui linux desktop skia gui cross-platform dotnet openmaui - + - - - + + + diff --git a/Rendering/GpuRenderingEngine.cs b/Rendering/GpuRenderingEngine.cs index 5af64a1..22fc1e5 100644 --- a/Rendering/GpuRenderingEngine.cs +++ b/Rendering/GpuRenderingEngine.cs @@ -3,6 +3,7 @@ using SkiaSharp; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux.Services; using Microsoft.Maui.Platform.Linux.Window; using System.Runtime.InteropServices; @@ -58,7 +59,7 @@ public class GpuRenderingEngine : IDisposable if (!_gpuAvailable) { - Console.WriteLine("[GpuRenderingEngine] GPU not available, using software rendering"); + DiagnosticLog.Debug("GpuRenderingEngine", "GPU not available, using software rendering"); InitializeSoftwareRendering(); } @@ -74,25 +75,25 @@ public class GpuRenderingEngine : IDisposable var glInterface = GRGlInterface.Create(); if (glInterface == null) { - Console.WriteLine("[GpuRenderingEngine] Failed to create GL interface"); + DiagnosticLog.Warn("GpuRenderingEngine", "Failed to create GL interface"); return false; } _grContext = GRContext.CreateGl(glInterface); if (_grContext == null) { - Console.WriteLine("[GpuRenderingEngine] Failed to create GR context"); + DiagnosticLog.Warn("GpuRenderingEngine", "Failed to create GR context"); glInterface.Dispose(); return false; } CreateGpuSurface(); - Console.WriteLine("[GpuRenderingEngine] GPU acceleration enabled"); + DiagnosticLog.Debug("GpuRenderingEngine", "GPU acceleration enabled"); return true; } catch (Exception ex) { - Console.WriteLine($"[GpuRenderingEngine] GPU initialization failed: {ex.Message}"); + DiagnosticLog.Error("GpuRenderingEngine", "GPU initialization failed", ex); return false; } } @@ -124,7 +125,7 @@ public class GpuRenderingEngine : IDisposable if (_surface == null) { - Console.WriteLine("[GpuRenderingEngine] Failed to create GPU surface, falling back to software"); + DiagnosticLog.Warn("GpuRenderingEngine", "Failed to create GPU surface, falling back to software"); _gpuAvailable = false; InitializeSoftwareRendering(); return; diff --git a/Rendering/GtkSkiaSurfaceWidget.cs b/Rendering/GtkSkiaSurfaceWidget.cs index d2b16be..8c6e9a4 100644 --- a/Rendering/GtkSkiaSurfaceWidget.cs +++ b/Rendering/GtkSkiaSurfaceWidget.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.InteropServices; using Microsoft.Maui.Platform.Linux.Native; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform.Linux.Rendering; @@ -150,7 +151,7 @@ public sealed class GtkSkiaSurfaceWidget : IDisposable GtkNative.g_signal_connect_data(_widget, "key-release-event", Marshal.GetFunctionPointerForDelegate(_keyReleaseCallback), IntPtr.Zero, IntPtr.Zero, 0); GtkNative.g_signal_connect_data(_widget, "scroll-event", Marshal.GetFunctionPointerForDelegate(_scrollCallback), IntPtr.Zero, IntPtr.Zero, 0); - Console.WriteLine($"[GtkSkiaSurfaceWidget] Created with size {width}x{height}"); + DiagnosticLog.Debug("GtkSkiaSurfaceWidget", $"Created with size {width}x{height}"); } private void CreateBuffer(int width, int height) @@ -179,7 +180,7 @@ public sealed class GtkSkiaSurfaceWidget : IDisposable _imageInfo.Height, _imageInfo.RowBytes); - Console.WriteLine($"[GtkSkiaSurfaceWidget] Created buffer {width}x{height}, stride={_imageInfo.RowBytes}"); + DiagnosticLog.Debug("GtkSkiaSurfaceWidget", $"Created buffer {width}x{height}, stride={_imageInfo.RowBytes}"); } public void Resize(int width, int height) @@ -303,7 +304,7 @@ public sealed class GtkSkiaSurfaceWidget : IDisposable if (!char.IsControl(c) || c == '\r' || c == '\n' || c == '\t') { string text = c.ToString(); - Console.WriteLine($"[GtkSkiaSurfaceWidget] TextInput: '{text}' (keyval={keyval}, unicode={unicode})"); + DiagnosticLog.Debug("GtkSkiaSurfaceWidget", $"TextInput: '{text}' (keyval={keyval}, unicode={unicode})"); TextInput?.Invoke(this, text); } } diff --git a/Rendering/SkiaRenderingEngine.cs b/Rendering/SkiaRenderingEngine.cs index ee4c3bd..d6d24b1 100644 --- a/Rendering/SkiaRenderingEngine.cs +++ b/Rendering/SkiaRenderingEngine.cs @@ -5,6 +5,7 @@ using SkiaSharp; using Microsoft.Maui.Graphics; using Microsoft.Maui.Platform.Linux.Window; using Microsoft.Maui.Platform; +using Microsoft.Maui.Platform.Linux.Services; using System.Runtime.InteropServices; namespace Microsoft.Maui.Platform.Linux.Rendering; @@ -169,8 +170,16 @@ public class SkiaRenderingEngine : IDisposable // Measure and arrange var availableSize = new Size(Width, Height); - rootView.Measure(availableSize); - rootView.Arrange(new Rect(0, 0, Width, Height)); + try + { + rootView.Measure(availableSize); + rootView.Arrange(new Rect(0, 0, Width, Height)); + } + catch (Exception ex) + { + DiagnosticLog.Error("SkiaRenderingEngine", "Exception during Measure/Arrange", ex); + return; + } // Determine what to redraw List regionsToRedraw; @@ -199,16 +208,37 @@ public class SkiaRenderingEngine : IDisposable // Render dirty regions foreach (var region in regionsToRedraw) { - RenderRegion(rootView, region, isFullRedraw); + try + { + RenderRegion(rootView, region, isFullRedraw); + } + catch (Exception ex) + { + DiagnosticLog.Error("SkiaRenderingEngine", $"Exception rendering region {region}", ex); + } } // Draw popup overlays (always on top, full redraw) - SkiaView.DrawPopupOverlays(_canvas); + try + { + SkiaView.DrawPopupOverlays(_canvas); + } + catch (Exception ex) + { + DiagnosticLog.Error("SkiaRenderingEngine", "Exception drawing popup overlays", ex); + } // Draw modal dialogs and context menus on top of everything - if (LinuxDialogService.HasActiveDialog || LinuxDialogService.HasContextMenu) + try { - LinuxDialogService.DrawDialogs(_canvas, new SKRect(0, 0, Width, Height)); + if (LinuxDialogService.HasActiveDialog || LinuxDialogService.HasContextMenu) + { + LinuxDialogService.DrawDialogs(_canvas, new SKRect(0, 0, Width, Height)); + } + } + catch (Exception ex) + { + DiagnosticLog.Error("SkiaRenderingEngine", "Exception drawing dialogs", ex); } _canvas.Flush(); @@ -234,7 +264,14 @@ public class SkiaRenderingEngine : IDisposable _canvas.DrawRect(region, clearPaint); // Draw the view tree (views will naturally clip to their bounds) - rootView.Draw(_canvas); + try + { + rootView.Draw(_canvas); + } + catch (Exception ex) + { + DiagnosticLog.Error("SkiaRenderingEngine", "Exception during view Draw", ex); + } _canvas.Restore(); } diff --git a/Services/AtSpi2AccessibilityService.cs b/Services/AtSpi2AccessibilityService.cs index 2d85c38..11440a6 100644 --- a/Services/AtSpi2AccessibilityService.cs +++ b/Services/AtSpi2AccessibilityService.cs @@ -36,7 +36,7 @@ public class AtSpi2AccessibilityService : IAccessibilityService, IDisposable int result = atspi_init(); if (result != 0) { - Console.WriteLine("AtSpi2AccessibilityService: Failed to initialize AT-SPI2"); + DiagnosticLog.Error("AtSpi2AccessibilityService", "Failed to initialize AT-SPI2"); return; } @@ -51,16 +51,16 @@ public class AtSpi2AccessibilityService : IAccessibilityService, IDisposable // Register our application RegisterApplication(); - Console.WriteLine("AtSpi2AccessibilityService: Initialized successfully"); + DiagnosticLog.Debug("AtSpi2AccessibilityService", "Initialized successfully"); } else { - Console.WriteLine("AtSpi2AccessibilityService: Accessibility is not enabled"); + DiagnosticLog.Warn("AtSpi2AccessibilityService", "Accessibility is not enabled"); } } catch (Exception ex) { - Console.WriteLine($"AtSpi2AccessibilityService: Initialization failed - {ex.Message}"); + DiagnosticLog.Error("AtSpi2AccessibilityService", $"Initialization failed - {ex.Message}"); } } @@ -168,11 +168,11 @@ public class AtSpi2AccessibilityService : IAccessibilityService, IDisposable // or by emitting "object:announcement" events // For now, use a simpler approach with the event system - Console.WriteLine($"[Accessibility Announcement ({priority})]: {text}"); + DiagnosticLog.Debug("AtSpi2AccessibilityService", $"Announcement ({priority}): {text}"); } catch (Exception ex) { - Console.WriteLine($"AtSpi2AccessibilityService: Announcement failed - {ex.Message}"); + DiagnosticLog.Error("AtSpi2AccessibilityService", $"Announcement failed - {ex.Message}"); } } @@ -182,7 +182,7 @@ public class AtSpi2AccessibilityService : IAccessibilityService, IDisposable // using the org.a11y.atspi.Event interface // For now, log the event for debugging - Console.WriteLine($"[AT-SPI2 Event] {eventName}: {accessible.AccessibleName} ({accessible.Role})"); + DiagnosticLog.Debug("AtSpi2AccessibilityService", $"Event {eventName}: {accessible.AccessibleName} ({accessible.Role})"); } /// diff --git a/Services/DiagnosticLog.cs b/Services/DiagnosticLog.cs new file mode 100644 index 0000000..4aae1e1 --- /dev/null +++ b/Services/DiagnosticLog.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace Microsoft.Maui.Platform.Linux.Services; + +/// +/// Centralized diagnostic logging for the Linux MAUI platform. +/// Logging is enabled only in DEBUG builds by default, or when +/// explicitly enabled via . +/// +public static class DiagnosticLog +{ + private static bool? _isEnabled; + + /// + /// Gets or sets whether diagnostic logging is enabled. + /// Defaults to true in DEBUG builds, false in RELEASE builds. + /// + public static bool IsEnabled + { + get + { + if (_isEnabled.HasValue) + return _isEnabled.Value; +#if DEBUG + return true; +#else + return false; +#endif + } + set => _isEnabled = value; + } + + /// + /// Logs an informational diagnostic message. + /// + [Conditional("DEBUG")] + public static void Debug(string tag, string message) + { + if (IsEnabled) + System.Console.WriteLine($"[{tag}] {message}"); + } + + /// + /// Logs an informational diagnostic message (always writes when enabled, not conditional on DEBUG). + /// Use for important operational messages that should appear in release builds when logging is enabled. + /// + public static void Info(string tag, string message) + { + if (IsEnabled) + System.Console.WriteLine($"[{tag}] {message}"); + } + + /// + /// Logs a warning message. Always writes when logging is enabled. + /// + public static void Warn(string tag, string message) + { + if (IsEnabled) + System.Console.Error.WriteLine($"[{tag}] WARNING: {message}"); + } + + /// + /// Logs an error message. Always writes regardless of IsEnabled. + /// + public static void Error(string tag, string message) + { + System.Console.Error.WriteLine($"[{tag}] ERROR: {message}"); + } + + /// + /// Logs an error message with exception details. Always writes regardless of IsEnabled. + /// + public static void Error(string tag, string message, Exception ex) + { + System.Console.Error.WriteLine($"[{tag}] ERROR: {message}: {ex.Message}"); + } +} diff --git a/Services/DisplayServerFactory.cs b/Services/DisplayServerFactory.cs index 4d53690..bcfbe08 100644 --- a/Services/DisplayServerFactory.cs +++ b/Services/DisplayServerFactory.cs @@ -29,12 +29,12 @@ public static class DisplayServerFactory if (!string.IsNullOrEmpty(xDisplay) && !string.IsNullOrEmpty(preferX11)) { - Console.WriteLine("[DisplayServer] XWayland detected, using X11 backend (MAUI_PREFER_X11 set)"); + DiagnosticLog.Debug("DisplayServerFactory", "XWayland detected, using X11 backend (MAUI_PREFER_X11 set)"); _cachedServerType = DisplayServerType.X11; return DisplayServerType.X11; } - Console.WriteLine("[DisplayServer] Wayland display detected"); + DiagnosticLog.Debug("DisplayServerFactory", "Wayland display detected"); _cachedServerType = DisplayServerType.Wayland; return DisplayServerType.Wayland; } @@ -43,13 +43,13 @@ public static class DisplayServerFactory var x11Display = Environment.GetEnvironmentVariable("DISPLAY"); if (!string.IsNullOrEmpty(x11Display)) { - Console.WriteLine("[DisplayServer] X11 display detected"); + DiagnosticLog.Debug("DisplayServerFactory", "X11 display detected"); _cachedServerType = DisplayServerType.X11; return DisplayServerType.X11; } // Default to X11 and let it fail if not available - Console.WriteLine("[DisplayServer] No display server detected, defaulting to X11"); + DiagnosticLog.Warn("DisplayServerFactory", "No display server detected, defaulting to X11"); _cachedServerType = DisplayServerType.X11; return DisplayServerType.X11; } @@ -76,12 +76,12 @@ public static class DisplayServerFactory { try { - Console.WriteLine($"[DisplayServer] Creating X11 window: {title} ({width}x{height})"); + DiagnosticLog.Debug("DisplayServerFactory", $"Creating X11 window: {title} ({width}x{height})"); return new X11DisplayWindow(title, width, height); } catch (Exception ex) { - Console.WriteLine($"[DisplayServer] Failed to create X11 window: {ex.Message}"); + DiagnosticLog.Error("DisplayServerFactory", $"Failed to create X11 window: {ex.Message}"); throw; } } @@ -90,18 +90,18 @@ public static class DisplayServerFactory { try { - Console.WriteLine($"[DisplayServer] Creating Wayland window: {title} ({width}x{height})"); + DiagnosticLog.Debug("DisplayServerFactory", $"Creating Wayland window: {title} ({width}x{height})"); return new WaylandDisplayWindow(title, width, height); } catch (Exception ex) { - Console.WriteLine($"[DisplayServer] Failed to create Wayland window: {ex.Message}"); + DiagnosticLog.Error("DisplayServerFactory", $"Failed to create Wayland window: {ex.Message}"); // Try to fall back to X11 via XWayland var xDisplay = Environment.GetEnvironmentVariable("DISPLAY"); if (!string.IsNullOrEmpty(xDisplay)) { - Console.WriteLine("[DisplayServer] Falling back to X11 (XWayland)"); + DiagnosticLog.Warn("DisplayServerFactory", "Falling back to X11 (XWayland)"); return CreateX11Window(title, width, height); } diff --git a/Services/Fcitx5InputMethodService.cs b/Services/Fcitx5InputMethodService.cs index 07b7c5a..b837cc6 100644 --- a/Services/Fcitx5InputMethodService.cs +++ b/Services/Fcitx5InputMethodService.cs @@ -48,18 +48,18 @@ public class Fcitx5InputMethodService : IInputMethodService, IDisposable if (start >= 0 && end > start) { _inputContextPath = output.Substring(start + 1, end - start - 1); - Console.WriteLine($"Fcitx5InputMethodService: Created context at {_inputContextPath}"); + DiagnosticLog.Debug("Fcitx5InputMethodService", $"Created context at {_inputContextPath}"); StartMonitoring(); } } else { - Console.WriteLine("Fcitx5InputMethodService: Failed to create input context"); + DiagnosticLog.Error("Fcitx5InputMethodService", "Failed to create input context"); } } catch (Exception ex) { - Console.WriteLine($"Fcitx5InputMethodService: Initialization failed - {ex.Message}"); + DiagnosticLog.Error("Fcitx5InputMethodService", $"Initialization failed - {ex.Message}"); } } @@ -102,7 +102,7 @@ public class Fcitx5InputMethodService : IInputMethodService, IDisposable } catch (Exception ex) { - Console.WriteLine($"Fcitx5InputMethodService: Monitor error - {ex.Message}"); + DiagnosticLog.Error("Fcitx5InputMethodService", $"Monitor error - {ex.Message}"); } }); } diff --git a/Services/FilePickerService.cs b/Services/FilePickerService.cs index 55de664..cff46ca 100644 --- a/Services/FilePickerService.cs +++ b/Services/FilePickerService.cs @@ -91,7 +91,7 @@ public class FilePickerService : IFilePicker if (tool == DialogTool.None) { // Fall back to console path input - Console.WriteLine("No file dialog available. Please enter file path:"); + DiagnosticLog.Warn("FilePickerService", "No file dialog available. Please enter file path:"); var path = Console.ReadLine(); if (!string.IsNullOrEmpty(path) && File.Exists(path)) { diff --git a/Services/GlobalHotkeyService.cs b/Services/GlobalHotkeyService.cs index 7aa6e1e..dd4b0b4 100644 --- a/Services/GlobalHotkeyService.cs +++ b/Services/GlobalHotkeyService.cs @@ -76,7 +76,7 @@ public class GlobalHotkeyService : IDisposable int result = XGrabKey(_display, keyCode, mask, _rootWindow, true, GrabModeAsync, GrabModeAsync); if (result == 0) { - Console.WriteLine($"Failed to grab key {key} with modifiers {modifiers}"); + DiagnosticLog.Warn("GlobalHotkeyService", $"Failed to grab key {key} with modifiers {modifiers}"); } } @@ -148,7 +148,7 @@ public class GlobalHotkeyService : IDisposable } catch (Exception ex) { - Console.WriteLine($"GlobalHotkeyService error: {ex.Message}"); + DiagnosticLog.Error("GlobalHotkeyService", $"Error: {ex.Message}"); } } } diff --git a/Services/Gtk4InteropService.cs b/Services/Gtk4InteropService.cs index 9e7e238..01dd47a 100644 --- a/Services/Gtk4InteropService.cs +++ b/Services/Gtk4InteropService.cs @@ -295,17 +295,17 @@ public class Gtk4InteropService : IDisposable { _useGtk4 = true; _initialized = true; - Console.WriteLine("[GTK4] Initialized GTK4"); + DiagnosticLog.Debug("Gtk4InteropService", "Initialized GTK4"); return true; } } catch (DllNotFoundException) { - Console.WriteLine("[GTK4] GTK4 not found, trying GTK3"); + DiagnosticLog.Warn("Gtk4InteropService", "GTK4 not found, trying GTK3"); } catch (Exception ex) { - Console.WriteLine($"[GTK4] GTK4 init failed: {ex.Message}"); + DiagnosticLog.Error("Gtk4InteropService", $"GTK4 init failed: {ex.Message}"); } // Fall back to GTK3 @@ -317,17 +317,17 @@ public class Gtk4InteropService : IDisposable { _useGtk4 = false; _initialized = true; - Console.WriteLine("[GTK4] Initialized GTK3 (fallback)"); + DiagnosticLog.Debug("Gtk4InteropService", "Initialized GTK3 (fallback)"); return true; } } catch (DllNotFoundException) { - Console.WriteLine("[GTK4] GTK3 not found"); + DiagnosticLog.Warn("Gtk4InteropService", "GTK3 not found"); } catch (Exception ex) { - Console.WriteLine($"[GTK4] GTK3 init failed: {ex.Message}"); + DiagnosticLog.Error("Gtk4InteropService", $"GTK3 init failed: {ex.Message}"); } return false; diff --git a/Services/GtkContextMenuService.cs b/Services/GtkContextMenuService.cs index a04fa46..2792f47 100644 --- a/Services/GtkContextMenuService.cs +++ b/Services/GtkContextMenuService.cs @@ -31,7 +31,7 @@ public static class GtkContextMenuService IntPtr menu = GtkNative.gtk_menu_new(); if (menu == IntPtr.Zero) { - Console.WriteLine("[GtkContextMenuService] Failed to create GTK menu"); + DiagnosticLog.Error("GtkContextMenuService", "Failed to create GTK menu"); return; } @@ -56,7 +56,7 @@ public static class GtkContextMenuService ActivateCallback callback = delegate { - Console.WriteLine("[GtkContextMenuService] Menu item activated: " + item.Text); + DiagnosticLog.Debug("GtkContextMenuService", "Menu item activated: " + item.Text); _actions[actionIndex]?.Invoke(); }; _callbacks.Add(callback); @@ -88,7 +88,7 @@ public static class GtkContextMenuService GtkNative.gdk_event_free(currentEvent); } - Console.WriteLine($"[GtkContextMenuService] Showed GTK menu with {items.Count} items"); + DiagnosticLog.Debug("GtkContextMenuService", $"Showed GTK menu with {items.Count} items"); } /// @@ -107,7 +107,7 @@ public static class GtkContextMenuService isDark = Microsoft.Maui.Controls.Application.Current?.RequestedTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark; } - Console.WriteLine($"[GtkContextMenuService] ApplyMenuTheme: isDark={isDark}"); + DiagnosticLog.Debug("GtkContextMenuService", $"ApplyMenuTheme: isDark={isDark}"); // Create comprehensive CSS based on the theme string css = isDark @@ -164,7 +164,7 @@ public static class GtkContextMenuService } catch (Exception ex) { - Console.WriteLine($"[GtkContextMenuService] Error applying menu theme: {ex.Message}"); + DiagnosticLog.Error("GtkContextMenuService", $"Error applying menu theme: {ex.Message}"); } } } diff --git a/Services/GtkThemeService.cs b/Services/GtkThemeService.cs index d4220b4..c769cce 100644 --- a/Services/GtkThemeService.cs +++ b/Services/GtkThemeService.cs @@ -38,7 +38,7 @@ public static class GtkThemeService isDark = Microsoft.Maui.Controls.Application.Current?.RequestedTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark; } - Console.WriteLine($"[GtkThemeService] ApplyTheme: isDark={isDark}"); + DiagnosticLog.Debug("GtkThemeService", $"ApplyTheme: isDark={isDark}"); // Create comprehensive CSS based on the theme string css = isDark ? GetDarkCss() : GetLightCss(); @@ -47,7 +47,7 @@ public static class GtkThemeService IntPtr screen = GtkNative.gdk_screen_get_default(); if (screen == IntPtr.Zero) { - Console.WriteLine("[GtkThemeService] Failed to get default screen"); + DiagnosticLog.Error("GtkThemeService", "Failed to get default screen"); return; } @@ -55,14 +55,14 @@ public static class GtkThemeService IntPtr newProvider = GtkNative.gtk_css_provider_new(); if (newProvider == IntPtr.Zero) { - Console.WriteLine("[GtkThemeService] Failed to create CSS provider"); + DiagnosticLog.Error("GtkThemeService", "Failed to create CSS provider"); return; } // Load CSS data if (!GtkNative.gtk_css_provider_load_from_data(newProvider, css, -1, IntPtr.Zero)) { - Console.WriteLine("[GtkThemeService] Failed to load CSS data"); + DiagnosticLog.Error("GtkThemeService", "Failed to load CSS data"); return; } @@ -72,11 +72,11 @@ public static class GtkThemeService // Store reference to current provider _currentCssProvider = newProvider; - Console.WriteLine("[GtkThemeService] CSS applied successfully"); + DiagnosticLog.Debug("GtkThemeService", "CSS applied successfully"); } catch (Exception ex) { - Console.WriteLine($"[GtkThemeService] Error applying theme: {ex.Message}"); + DiagnosticLog.Error("GtkThemeService", $"Error applying theme: {ex.Message}"); } } diff --git a/Services/HardwareVideoService.cs b/Services/HardwareVideoService.cs index 92dac59..0f097ec 100644 --- a/Services/HardwareVideoService.cs +++ b/Services/HardwareVideoService.cs @@ -204,7 +204,7 @@ public class HardwareVideoService : IDisposable { _currentApi = VideoAccelerationApi.VaApi; _initialized = true; - Console.WriteLine($"[HardwareVideo] Initialized VA-API with {_supportedProfiles.Count} supported profiles"); + DiagnosticLog.Debug("HardwareVideoService", $"Initialized VA-API with {_supportedProfiles.Count} supported profiles"); return true; } } @@ -216,12 +216,12 @@ public class HardwareVideoService : IDisposable { _currentApi = VideoAccelerationApi.Vdpau; _initialized = true; - Console.WriteLine("[HardwareVideo] Initialized VDPAU"); + DiagnosticLog.Debug("HardwareVideoService", "Initialized VDPAU"); return true; } } - Console.WriteLine("[HardwareVideo] No hardware acceleration available, using software"); + DiagnosticLog.Warn("HardwareVideoService", "No hardware acceleration available, using software"); _currentApi = VideoAccelerationApi.Software; return false; } @@ -261,12 +261,12 @@ public class HardwareVideoService : IDisposable } catch (DllNotFoundException) { - Console.WriteLine("[HardwareVideo] VA-API libraries not found"); + DiagnosticLog.Warn("HardwareVideoService", "VA-API libraries not found"); return false; } catch (Exception ex) { - Console.WriteLine($"[HardwareVideo] VA-API initialization failed: {ex.Message}"); + DiagnosticLog.Error("HardwareVideoService", $"VA-API initialization failed: {ex.Message}"); return false; } } @@ -276,11 +276,11 @@ public class HardwareVideoService : IDisposable int status = vaInitialize(_vaDisplay, out int major, out int minor); if (status != VA_STATUS_SUCCESS) { - Console.WriteLine($"[HardwareVideo] vaInitialize failed: {GetVaError(status)}"); + DiagnosticLog.Error("HardwareVideoService", $"vaInitialize failed: {GetVaError(status)}"); return false; } - Console.WriteLine($"[HardwareVideo] VA-API {major}.{minor} initialized"); + DiagnosticLog.Debug("HardwareVideoService", $"VA-API {major}.{minor} initialized"); // Query supported profiles int[] profiles = new int[32]; @@ -331,11 +331,11 @@ public class HardwareVideoService : IDisposable } catch (DllNotFoundException) { - Console.WriteLine("[HardwareVideo] VDPAU libraries not found"); + DiagnosticLog.Warn("HardwareVideoService", "VDPAU libraries not found"); } catch (Exception ex) { - Console.WriteLine($"[HardwareVideo] VDPAU initialization failed: {ex.Message}"); + DiagnosticLog.Error("HardwareVideoService", $"VDPAU initialization failed: {ex.Message}"); } return false; @@ -355,7 +355,7 @@ public class HardwareVideoService : IDisposable if (!_supportedProfiles.Contains(profile)) { - Console.WriteLine($"[HardwareVideo] Profile {profile} not supported"); + DiagnosticLog.Warn("HardwareVideoService", $"Profile {profile} not supported"); return false; } @@ -383,7 +383,7 @@ public class HardwareVideoService : IDisposable int status = vaCreateConfig(_vaDisplay, vaProfile, VAEntrypointVLD, IntPtr.Zero, 0, out _vaConfigId); if (status != VA_STATUS_SUCCESS) { - Console.WriteLine($"[HardwareVideo] vaCreateConfig failed: {GetVaError(status)}"); + DiagnosticLog.Error("HardwareVideoService", $"vaCreateConfig failed: {GetVaError(status)}"); return false; } @@ -396,7 +396,7 @@ public class HardwareVideoService : IDisposable status = vaCreateSurfaces(_vaDisplay, format, (uint)width, (uint)height, _vaSurfaces, 8, IntPtr.Zero, 0); if (status != VA_STATUS_SUCCESS) { - Console.WriteLine($"[HardwareVideo] vaCreateSurfaces failed: {GetVaError(status)}"); + DiagnosticLog.Error("HardwareVideoService", $"vaCreateSurfaces failed: {GetVaError(status)}"); vaDestroyConfig(_vaDisplay, _vaConfigId); return false; } @@ -405,13 +405,13 @@ public class HardwareVideoService : IDisposable status = vaCreateContext(_vaDisplay, _vaConfigId, width, height, 0, IntPtr.Zero, 0, out _vaContextId); if (status != VA_STATUS_SUCCESS) { - Console.WriteLine($"[HardwareVideo] vaCreateContext failed: {GetVaError(status)}"); + DiagnosticLog.Error("HardwareVideoService", $"vaCreateContext failed: {GetVaError(status)}"); vaDestroySurfaces(_vaDisplay, _vaSurfaces, _vaSurfaces.Length); vaDestroyConfig(_vaDisplay, _vaConfigId); return false; } - Console.WriteLine($"[HardwareVideo] Created decoder: {profile} {width}x{height}"); + DiagnosticLog.Debug("HardwareVideoService", $"Created decoder: {profile} {width}x{height}"); return true; } diff --git a/Services/IBusInputMethodService.cs b/Services/IBusInputMethodService.cs index 44a594e..2fdaa76 100644 --- a/Services/IBusInputMethodService.cs +++ b/Services/IBusInputMethodService.cs @@ -45,14 +45,14 @@ public class IBusInputMethodService : IInputMethodService, IDisposable _bus = ibus_bus_new(); if (_bus == IntPtr.Zero) { - Console.WriteLine("IBusInputMethodService: Failed to connect to IBus"); + DiagnosticLog.Error("IBusInputMethodService", "Failed to connect to IBus"); return; } // Check if IBus is connected if (!ibus_bus_is_connected(_bus)) { - Console.WriteLine("IBusInputMethodService: IBus not connected"); + DiagnosticLog.Error("IBusInputMethodService", "IBus not connected"); return; } @@ -60,7 +60,7 @@ public class IBusInputMethodService : IInputMethodService, IDisposable _context = ibus_bus_create_input_context(_bus, "maui-linux"); if (_context == IntPtr.Zero) { - Console.WriteLine("IBusInputMethodService: Failed to create input context"); + DiagnosticLog.Error("IBusInputMethodService", "Failed to create input context"); return; } @@ -71,11 +71,11 @@ public class IBusInputMethodService : IInputMethodService, IDisposable // Connect signals ConnectSignals(); - Console.WriteLine("IBusInputMethodService: Initialized successfully"); + DiagnosticLog.Debug("IBusInputMethodService", "Initialized successfully"); } catch (Exception ex) { - Console.WriteLine($"IBusInputMethodService: Initialization failed - {ex.Message}"); + DiagnosticLog.Error("IBusInputMethodService", $"Initialization failed - {ex.Message}"); } } diff --git a/Services/InputMethodServiceFactory.cs b/Services/InputMethodServiceFactory.cs index 1a968f8..a61253f 100644 --- a/Services/InputMethodServiceFactory.cs +++ b/Services/InputMethodServiceFactory.cs @@ -63,33 +63,33 @@ public static class InputMethodServiceFactory // Try Fcitx5 first if it's the configured IM if (imModule?.Contains("fcitx") == true && Fcitx5InputMethodService.IsAvailable()) { - Console.WriteLine("InputMethodServiceFactory: Using Fcitx5"); + DiagnosticLog.Debug("InputMethodServiceFactory", "Using Fcitx5"); return CreateFcitx5Service(); } // Try IBus (most common on modern Linux) if (IsIBusAvailable()) { - Console.WriteLine("InputMethodServiceFactory: Using IBus"); + DiagnosticLog.Debug("InputMethodServiceFactory", "Using IBus"); return CreateIBusService(); } // Try Fcitx5 as fallback if (Fcitx5InputMethodService.IsAvailable()) { - Console.WriteLine("InputMethodServiceFactory: Using Fcitx5"); + DiagnosticLog.Debug("InputMethodServiceFactory", "Using Fcitx5"); return CreateFcitx5Service(); } // Fall back to XIM if (IsXIMAvailable()) { - Console.WriteLine("InputMethodServiceFactory: Using XIM"); + DiagnosticLog.Debug("InputMethodServiceFactory", "Using XIM"); return CreateXIMService(); } // No IME available - Console.WriteLine("InputMethodServiceFactory: No IME available, using null service"); + DiagnosticLog.Warn("InputMethodServiceFactory", "No IME available, using null service"); return new NullInputMethodService(); } @@ -101,7 +101,7 @@ public static class InputMethodServiceFactory } catch (Exception ex) { - Console.WriteLine($"InputMethodServiceFactory: Failed to create IBus service - {ex.Message}"); + DiagnosticLog.Error("InputMethodServiceFactory", $"Failed to create IBus service - {ex.Message}"); return new NullInputMethodService(); } } @@ -114,7 +114,7 @@ public static class InputMethodServiceFactory } catch (Exception ex) { - Console.WriteLine($"InputMethodServiceFactory: Failed to create Fcitx5 service - {ex.Message}"); + DiagnosticLog.Error("InputMethodServiceFactory", $"Failed to create Fcitx5 service - {ex.Message}"); return new NullInputMethodService(); } } @@ -127,7 +127,7 @@ public static class InputMethodServiceFactory } catch (Exception ex) { - Console.WriteLine($"InputMethodServiceFactory: Failed to create XIM service - {ex.Message}"); + DiagnosticLog.Error("InputMethodServiceFactory", $"Failed to create XIM service - {ex.Message}"); return new NullInputMethodService(); } } diff --git a/Services/MauiIconGenerator.cs b/Services/MauiIconGenerator.cs index 85eea2d..0675f9d 100644 --- a/Services/MauiIconGenerator.cs +++ b/Services/MauiIconGenerator.cs @@ -19,7 +19,7 @@ public static class MauiIconGenerator { if (!File.Exists(metaFilePath)) { - Console.WriteLine("[MauiIconGenerator] Metadata file not found: " + metaFilePath); + DiagnosticLog.Error("MauiIconGenerator", "Metadata file not found: " + metaFilePath); return null; } @@ -48,9 +48,9 @@ public static class MauiIconGenerator ? scaleVal : 0.65f; - Console.WriteLine($"[MauiIconGenerator] Generating {size}x{size} icon"); - Console.WriteLine($"[MauiIconGenerator] Color: {color}"); - Console.WriteLine($"[MauiIconGenerator] Scale: {scale}"); + DiagnosticLog.Debug("MauiIconGenerator", $"Generating {size}x{size} icon"); + DiagnosticLog.Debug("MauiIconGenerator", $" Color: {color}"); + DiagnosticLog.Debug("MauiIconGenerator", $" Scale: {scale}"); using var surface = SKSurface.Create(new SKImageInfo(size, size, SKColorType.Bgra8888, SKAlphaType.Premul)); var canvas = surface.Canvas; @@ -82,12 +82,12 @@ public static class MauiIconGenerator using var fileStream = File.OpenWrite(outputPath); data.SaveTo(fileStream); - Console.WriteLine("[MauiIconGenerator] Generated: " + outputPath); + DiagnosticLog.Debug("MauiIconGenerator", "Generated: " + outputPath); return outputPath; } catch (Exception ex) { - Console.WriteLine("[MauiIconGenerator] Error: " + ex.Message); + DiagnosticLog.Error("MauiIconGenerator", "Error: " + ex.Message); return null; } } diff --git a/Services/MonitorService.cs b/Services/MonitorService.cs index 82fcbb0..aa58cb2 100644 --- a/Services/MonitorService.cs +++ b/Services/MonitorService.cs @@ -106,7 +106,7 @@ public class MonitorService : IDisposable _display = X11.XOpenDisplay(IntPtr.Zero); if (_display == IntPtr.Zero) { - Console.WriteLine("[MonitorService] Failed to open X11 display"); + DiagnosticLog.Error("MonitorService", "Failed to open X11 display"); _initialized = true; return; } @@ -117,26 +117,26 @@ public class MonitorService : IDisposable // Check if XRandR is available if (XRandR.XRRQueryExtension(_display, out _eventBase, out _errorBase) == 0) { - Console.WriteLine("[MonitorService] XRandR extension not available"); + DiagnosticLog.Warn("MonitorService", "XRandR extension not available"); _initialized = true; return; } if (XRandR.XRRQueryVersion(_display, out int major, out int minor) == 0) { - Console.WriteLine("[MonitorService] Failed to query XRandR version"); + DiagnosticLog.Error("MonitorService", "Failed to query XRandR version"); _initialized = true; return; } - Console.WriteLine($"[MonitorService] XRandR {major}.{minor} available"); + DiagnosticLog.Debug("MonitorService", $"XRandR {major}.{minor} available"); RefreshMonitors(); _initialized = true; } catch (Exception ex) { - Console.WriteLine($"[MonitorService] Initialization failed: {ex.Message}"); + DiagnosticLog.Error("MonitorService", $"Initialization failed: {ex.Message}"); _initialized = true; } } @@ -157,7 +157,7 @@ public class MonitorService : IDisposable resources = XRandR.XRRGetScreenResourcesCurrent(_display, _rootWindow); if (resources == IntPtr.Zero) { - Console.WriteLine("[MonitorService] Failed to get screen resources"); + DiagnosticLog.Error("MonitorService", "Failed to get screen resources"); return; } @@ -252,10 +252,10 @@ public class MonitorService : IDisposable _monitors = newMonitors; // Log detected monitors - Console.WriteLine($"[MonitorService] Detected {_monitors.Count} monitor(s):"); + DiagnosticLog.Debug("MonitorService", $"Detected {_monitors.Count} monitor(s):"); foreach (var monitor in _monitors) { - Console.WriteLine($" {monitor}"); + DiagnosticLog.Debug("MonitorService", $" {monitor}"); } // Notify if configuration changed diff --git a/Services/NotificationService.cs b/Services/NotificationService.cs index 705ba58..e849fc5 100644 --- a/Services/NotificationService.cs +++ b/Services/NotificationService.cs @@ -104,7 +104,7 @@ public class NotificationService } catch (Exception ex) { - Console.WriteLine($"[NotificationService] D-Bus monitor error: {ex.Message}"); + DiagnosticLog.Error("NotificationService", $"D-Bus monitor error: {ex.Message}"); } } diff --git a/Services/PortalFilePickerService.cs b/Services/PortalFilePickerService.cs index c7efe19..10496d6 100644 --- a/Services/PortalFilePickerService.cs +++ b/Services/PortalFilePickerService.cs @@ -91,7 +91,7 @@ public class PortalFilePickerService : IFilePicker else { // No file picker available - Console.WriteLine("[FilePickerService] No file picker available (install xdg-desktop-portal, zenity, or kdialog)"); + DiagnosticLog.Warn("PortalFilePickerService", "No file picker available (install xdg-desktop-portal, zenity, or kdialog)"); return Enumerable.Empty(); } } @@ -146,7 +146,7 @@ public class PortalFilePickerService : IFilePicker } catch (Exception ex) { - Console.WriteLine($"[FilePickerService] Portal error: {ex.Message}"); + DiagnosticLog.Error("PortalFilePickerService", $"Portal error: {ex.Message}"); // Fall back to zenity/kdialog if (_fallbackTool != null) { @@ -358,7 +358,7 @@ public class PortalFilePickerService : IFilePicker } catch (Exception ex) { - Console.WriteLine($"[FilePickerService] Command error: {ex.Message}"); + DiagnosticLog.Error("PortalFilePickerService", $"Command error: {ex.Message}"); return ""; } } diff --git a/Services/SystemThemeService.cs b/Services/SystemThemeService.cs index 8913c13..fe6da23 100644 --- a/Services/SystemThemeService.cs +++ b/Services/SystemThemeService.cs @@ -392,14 +392,14 @@ public class SystemThemeService if (oldTheme != CurrentTheme) { - Console.WriteLine($"[SystemThemeService] Theme change detected via polling: {oldTheme} -> {CurrentTheme}"); + DiagnosticLog.Debug("SystemThemeService", $"Theme change detected via polling: {oldTheme} -> {CurrentTheme}"); UpdateColors(); ThemeChanged?.Invoke(this, new ThemeChangedEventArgs(CurrentTheme)); } } catch (Exception ex) { - Console.WriteLine($"[SystemThemeService] Error in poll timer: {ex.Message}"); + DiagnosticLog.Error("SystemThemeService", $"Error in poll timer: {ex.Message}"); } } diff --git a/Services/X11InputMethodService.cs b/Services/X11InputMethodService.cs index 567fd98..f0b6e7e 100644 --- a/Services/X11InputMethodService.cs +++ b/Services/X11InputMethodService.cs @@ -44,7 +44,7 @@ public class X11InputMethodService : IInputMethodService, IDisposable _display = XOpenDisplay(IntPtr.Zero); if (_display == IntPtr.Zero) { - Console.WriteLine("X11InputMethodService: Failed to open display"); + DiagnosticLog.Error("X11InputMethodService", "Failed to open display"); return; } @@ -58,7 +58,7 @@ public class X11InputMethodService : IInputMethodService, IDisposable _xim = XOpenIM(_display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); if (_xim == IntPtr.Zero) { - Console.WriteLine("X11InputMethodService: No input method available, trying IBus..."); + DiagnosticLog.Warn("X11InputMethodService", "No input method available, trying IBus..."); TryIBusFallback(); return; } @@ -97,7 +97,7 @@ public class X11InputMethodService : IInputMethodService, IDisposable if (_xic != IntPtr.Zero) { - Console.WriteLine("X11InputMethodService: Input context created successfully"); + DiagnosticLog.Debug("X11InputMethodService", "Input context created successfully"); } } @@ -153,7 +153,7 @@ public class X11InputMethodService : IInputMethodService, IDisposable { // Try to connect to IBus via D-Bus // This provides a more modern IME interface - Console.WriteLine("X11InputMethodService: IBus fallback not yet implemented"); + DiagnosticLog.Warn("X11InputMethodService", "IBus fallback not yet implemented"); } public void SetFocus(IInputContext? context) diff --git a/Views/LinuxDialogService.cs b/Views/LinuxDialogService.cs index b72294c..9f29965 100644 --- a/Views/LinuxDialogService.cs +++ b/Views/LinuxDialogService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform; @@ -63,10 +64,10 @@ public static class LinuxDialogService public static void DrawDialogsOnly(SKCanvas canvas, SKRect bounds) { - Console.WriteLine($"[LinuxDialogService] DrawDialogsOnly: {_activeDialogs.Count} dialogs, IsDarkMode={SkiaTheme.IsDarkMode}"); + DiagnosticLog.Debug("LinuxDialogService", $"DrawDialogsOnly: {_activeDialogs.Count} dialogs, IsDarkMode={SkiaTheme.IsDarkMode}"); foreach (var dialog in _activeDialogs) { - Console.WriteLine($"[LinuxDialogService] Drawing dialog: IsVisible={dialog.IsVisible}, Opacity={dialog.Opacity}"); + DiagnosticLog.Debug("LinuxDialogService", $"Drawing dialog: IsVisible={dialog.IsVisible}, Opacity={dialog.Opacity}"); dialog.Measure(new Size(bounds.Width, bounds.Height)); dialog.Arrange(new Rect(bounds.Left, bounds.Top, bounds.Width, bounds.Height)); dialog.Draw(canvas); @@ -89,7 +90,7 @@ public static class LinuxDialogService public static void ShowContextMenu(SkiaContextMenu menu) { - Console.WriteLine("[LinuxDialogService] ShowContextMenu called"); + DiagnosticLog.Debug("LinuxDialogService", "ShowContextMenu called"); _activeContextMenu = menu; _showPopupCallback?.Invoke(); _invalidateCallback?.Invoke(); diff --git a/Views/LinuxWebView.cs b/Views/LinuxWebView.cs index ec2eea4..ec2c968 100644 --- a/Views/LinuxWebView.cs +++ b/Views/LinuxWebView.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Maui.Platform.Linux.Interop; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform; @@ -111,7 +112,7 @@ public class LinuxWebView : SkiaView _gtkWindow = WebKitGtk.gtk_window_new(0); if (_gtkWindow == IntPtr.Zero) { - Console.WriteLine("[LinuxWebView] Failed to create GTK window"); + DiagnosticLog.Error("LinuxWebView", "Failed to create GTK window"); return; } @@ -123,7 +124,7 @@ public class LinuxWebView : SkiaView _webView = WebKitGtk.webkit_web_view_new(); if (_webView == IntPtr.Zero) { - Console.WriteLine("[LinuxWebView] Failed to create WebKit WebView"); + DiagnosticLog.Error("LinuxWebView", "Failed to create WebKit WebView"); WebKitGtk.gtk_widget_destroy(_gtkWindow); _gtkWindow = IntPtr.Zero; return; @@ -148,12 +149,12 @@ public class LinuxWebView : SkiaView WebKitGtk.gtk_container_add(_gtkWindow, _webView); _initialized = true; - Console.WriteLine("[LinuxWebView] WebKitGTK WebView initialized successfully"); + DiagnosticLog.Debug("LinuxWebView", "WebKitGTK WebView initialized successfully"); } catch (Exception ex) { - Console.WriteLine($"[LinuxWebView] Initialization failed: {ex.Message}"); - Console.WriteLine($"[LinuxWebView] Make sure WebKitGTK is installed: sudo apt install libwebkit2gtk-4.1-0"); + DiagnosticLog.Error("LinuxWebView", $"Initialization failed: {ex.Message}", ex); + DiagnosticLog.Warn("LinuxWebView", "Make sure WebKitGTK is installed: sudo apt install libwebkit2gtk-4.1-0"); } } diff --git a/Views/SkiaAlertDialog.cs b/Views/SkiaAlertDialog.cs index 1afea84..10005f5 100644 --- a/Views/SkiaAlertDialog.cs +++ b/Views/SkiaAlertDialog.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform; @@ -62,7 +63,7 @@ public class SkiaAlertDialog : SkiaView protected override void OnDraw(SKCanvas canvas, SKRect bounds) { var app = Application.Current; - Console.WriteLine($"[SkiaAlertDialog] OnDraw: app={app != null}, UserAppTheme={app?.UserAppTheme}, RequestedTheme={app?.RequestedTheme}, IsDarkMode={SkiaTheme.IsDarkMode}, DialogBg={DialogBackground}"); + DiagnosticLog.Debug("SkiaAlertDialog", $"OnDraw: app={app != null}, UserAppTheme={app?.UserAppTheme}, RequestedTheme={app?.RequestedTheme}, IsDarkMode={SkiaTheme.IsDarkMode}, DialogBg={DialogBackground}"); // Draw semi-transparent overlay covering entire screen using var overlayPaint = new SKPaint diff --git a/Views/SkiaButton.cs b/Views/SkiaButton.cs index 058f41a..172c014 100644 --- a/Views/SkiaButton.cs +++ b/Views/SkiaButton.cs @@ -6,6 +6,7 @@ using System.Windows.Input; using Microsoft.Maui.Controls; using Microsoft.Maui.Graphics; using Microsoft.Maui.Platform.Linux.Rendering; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform; @@ -1096,7 +1097,7 @@ public class SkiaButton : SkiaView, IButtonController var result = new Size(Math.Max(width, 44f), Math.Max(height, 36f)); if (Text == "Round") - Console.WriteLine($"[SkiaButton.Measure] Text='Round' WReq={WidthRequest} HReq={HeightRequest} width={width:F1} height={height:F1} result={result.Width:F0}x{result.Height:F0}"); + DiagnosticLog.Debug("SkiaButton", $"Measure Text='Round' WReq={WidthRequest} HReq={HeightRequest} width={width:F1} height={height:F1} result={result.Width:F0}x{result.Height:F0}"); return result; } diff --git a/Views/SkiaCollectionView.cs b/Views/SkiaCollectionView.cs index 4219918..0fa1f4c 100644 --- a/Views/SkiaCollectionView.cs +++ b/Views/SkiaCollectionView.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Maui.Controls; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform; @@ -460,7 +461,7 @@ public class SkiaCollectionView : SkiaItemsView } catch (Exception ex) { - Console.WriteLine("[SkiaCollectionView.DrawItem] EXCEPTION: " + ex.Message + "\n" + ex.StackTrace); + DiagnosticLog.Error("SkiaCollectionView", "DrawItem EXCEPTION: " + ex.Message + "\n" + ex.StackTrace, ex); return; } } diff --git a/Views/SkiaEditor.cs b/Views/SkiaEditor.cs index c8af51d..ff7b5cb 100644 --- a/Views/SkiaEditor.cs +++ b/Views/SkiaEditor.cs @@ -1087,13 +1087,13 @@ public class SkiaEditor : SkiaView, IInputContext public override void OnPointerPressed(PointerEventArgs e) { - Console.WriteLine($"[SkiaEditor] OnPointerPressed: Button={e.Button}, IsEnabled={IsEnabled}"); + DiagnosticLog.Debug("SkiaEditor", $"OnPointerPressed: Button={e.Button}, IsEnabled={IsEnabled}"); if (!IsEnabled) return; // Handle right-click context menu if (e.Button == PointerButton.Right) { - Console.WriteLine("[SkiaEditor] Right-click detected, showing context menu"); + DiagnosticLog.Debug("SkiaEditor", "Right-click detected, showing context menu"); ShowContextMenu(e.X, e.Y); return; } @@ -1532,7 +1532,7 @@ public class SkiaEditor : SkiaView, IInputContext private void ShowContextMenu(float x, float y) { - Console.WriteLine($"[SkiaEditor] ShowContextMenu at ({x}, {y}), IsGtkMode={LinuxApplication.IsGtkMode}"); + DiagnosticLog.Debug("SkiaEditor", $"ShowContextMenu at ({x}, {y}), IsGtkMode={LinuxApplication.IsGtkMode}"); bool hasSelection = _selectionLength != 0; bool hasText = !string.IsNullOrEmpty(Text); bool hasClipboard = !string.IsNullOrEmpty(SystemClipboard.GetText()); diff --git a/Views/SkiaEntry.cs b/Views/SkiaEntry.cs index 479a4ee..9eb1110 100644 --- a/Views/SkiaEntry.cs +++ b/Views/SkiaEntry.cs @@ -1644,7 +1644,7 @@ public class SkiaEntry : SkiaView, IInputContext private void ShowContextMenu(float x, float y) { - Console.WriteLine($"[SkiaEntry] ShowContextMenu at ({x}, {y}), IsGtkMode={LinuxApplication.IsGtkMode}"); + DiagnosticLog.Debug("SkiaEntry", $"ShowContextMenu at ({x}, {y}), IsGtkMode={LinuxApplication.IsGtkMode}"); bool hasSelection = _selectionLength != 0; bool hasText = !string.IsNullOrEmpty(Text); bool hasClipboard = !string.IsNullOrEmpty(SystemClipboard.GetText()); diff --git a/Views/SkiaImage.cs b/Views/SkiaImage.cs index 4932b1d..179c186 100644 --- a/Views/SkiaImage.cs +++ b/Views/SkiaImage.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using System.Timers; using Microsoft.Maui.Controls; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; using Svg.Skia; @@ -513,7 +514,7 @@ public class SkiaImage : SkiaView _isSvg = false; _currentFilePath = null; _cacheKey = null; - Console.WriteLine($"[SkiaImage] File not found: {filePath}"); + DiagnosticLog.Warn("SkiaImage", $"File not found: {filePath}"); ImageLoadingError?.Invoke(this, new ImageLoadingErrorEventArgs(new FileNotFoundException(filePath))); return; } @@ -1210,7 +1211,7 @@ public class SkiaImage : SkiaView var stream = assembly.GetManifestResourceStream(resourceName); if (stream != null) { - Console.WriteLine($"[SkiaImage] Loaded embedded resource: {resourceName}"); + DiagnosticLog.Debug("SkiaImage", $"Loaded embedded resource: {resourceName}"); return (stream, requestedExt); } } @@ -1227,7 +1228,7 @@ public class SkiaImage : SkiaView var stream = assembly.GetManifestResourceStream(resourceName); if (stream != null) { - Console.WriteLine($"[SkiaImage] Loaded SVG as PNG substitute: {resourceName}"); + DiagnosticLog.Debug("SkiaImage", $"Loaded SVG as PNG substitute: {resourceName}"); return (stream, ".svg"); } } diff --git a/Views/SkiaImageButton.cs b/Views/SkiaImageButton.cs index d1c7c71..28aa2a6 100644 --- a/Views/SkiaImageButton.cs +++ b/Views/SkiaImageButton.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using System.Windows.Input; using Microsoft.Maui.Controls; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; using Svg.Skia; @@ -422,7 +423,7 @@ public class SkiaImageButton : SkiaView { _isLoading = true; Invalidate(); - Console.WriteLine("[SkiaImageButton] LoadFromFileAsync: " + filePath); + DiagnosticLog.Debug("SkiaImageButton", "LoadFromFileAsync: " + filePath); try { @@ -450,15 +451,15 @@ public class SkiaImageButton : SkiaView if (File.Exists(path)) { foundPath = path; - Console.WriteLine("[SkiaImageButton] Found file at: " + path); + DiagnosticLog.Debug("SkiaImageButton", "Found file at: " + path); break; } } if (foundPath == null) { - Console.WriteLine("[SkiaImageButton] File not found: " + filePath); - Console.WriteLine("[SkiaImageButton] Searched paths: " + string.Join(", ", searchPaths)); + DiagnosticLog.Warn("SkiaImageButton", "File not found: " + filePath); + DiagnosticLog.Debug("SkiaImageButton", "Searched paths: " + string.Join(", ", searchPaths)); _isLoading = false; ImageLoadingError?.Invoke(this, new ImageLoadingErrorEventArgs(new FileNotFoundException(filePath))); return; @@ -498,7 +499,7 @@ public class SkiaImageButton : SkiaView canvas.Translate(-cullRect.Left, -cullRect.Top); canvas.DrawPicture(svg.Picture); Bitmap = bitmap; - Console.WriteLine($"[SkiaImageButton] Loaded SVG: {foundPath} ({width}x{height}), cullRect={cullRect}"); + DiagnosticLog.Debug("SkiaImageButton", $"Loaded SVG: {foundPath} ({width}x{height}), cullRect={cullRect}"); } } else @@ -508,7 +509,7 @@ public class SkiaImageButton : SkiaView if (bitmap != null) { Bitmap = bitmap; - Console.WriteLine("[SkiaImageButton] Loaded image: " + foundPath); + DiagnosticLog.Debug("SkiaImageButton", "Loaded image: " + foundPath); } } }); diff --git a/Views/SkiaItemsView.cs b/Views/SkiaItemsView.cs index 172d5a4..1d2284d 100644 --- a/Views/SkiaItemsView.cs +++ b/Views/SkiaItemsView.cs @@ -5,6 +5,7 @@ using SkiaSharp; using System.Collections; using System.Collections.Specialized; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform; @@ -114,7 +115,7 @@ public class SkiaItemsView : SkiaView protected virtual void RefreshItems() { - Console.WriteLine($"[SkiaItemsView] RefreshItems called, clearing {_items.Count} items and {_itemViewCache.Count} cached views"); + DiagnosticLog.Debug("SkiaItemsView", $"RefreshItems called, clearing {_items.Count} items and {_itemViewCache.Count} cached views"); _items.Clear(); _itemViewCache.Clear(); // Clear cached views when items change _itemHeights.Clear(); // Clear cached heights @@ -125,7 +126,7 @@ public class SkiaItemsView : SkiaView _items.Add(item); } } - Console.WriteLine($"[SkiaItemsView] RefreshItems done, now have {_items.Count} items"); + DiagnosticLog.Debug("SkiaItemsView", $"RefreshItems done, now have {_items.Count} items"); _scrollOffset = 0; } @@ -194,7 +195,7 @@ public class SkiaItemsView : SkiaView protected override void OnDraw(SKCanvas canvas, SKRect bounds) { - Console.WriteLine($"[SkiaItemsView] OnDraw - bounds={bounds}, items={_items.Count}, ItemViewCreator={(ItemViewCreator != null ? "set" : "null")}"); + DiagnosticLog.Debug("SkiaItemsView", $"OnDraw - bounds={bounds}, items={_items.Count}, ItemViewCreator={(ItemViewCreator != null ? "set" : "null")}"); // Draw background if (BackgroundColor != null && BackgroundColor != Colors.Transparent) @@ -283,7 +284,7 @@ public class SkiaItemsView : SkiaView // Try to use ItemViewCreator for templated rendering if (ItemViewCreator != null) { - Console.WriteLine($"[SkiaItemsView] DrawItem {index} - ItemViewCreator exists, item: {item}"); + DiagnosticLog.Debug("SkiaItemsView", $"DrawItem {index} - ItemViewCreator exists, item: {item}"); // Get or create cached view for this index if (!_itemViewCache.TryGetValue(index, out var itemView) || itemView == null) { @@ -322,7 +323,7 @@ public class SkiaItemsView : SkiaView } else { - Console.WriteLine($"[SkiaItemsView] DrawItem {index} - ItemViewCreator is NULL, falling back to ToString"); + DiagnosticLog.Debug("SkiaItemsView", $"DrawItem {index} - ItemViewCreator is NULL, falling back to ToString"); } // Draw separator @@ -424,7 +425,7 @@ public class SkiaItemsView : SkiaView public override void OnPointerPressed(PointerEventArgs e) { - Console.WriteLine($"[SkiaItemsView] OnPointerPressed - x={e.X}, y={e.Y}, Bounds={Bounds}, ScreenBounds={ScreenBounds}, ItemCount={_items.Count}"); + DiagnosticLog.Debug("SkiaItemsView", $"OnPointerPressed - x={e.X}, y={e.Y}, Bounds={Bounds}, ScreenBounds={ScreenBounds}, ItemCount={_items.Count}"); if (!IsEnabled) return; // Check if clicking on scrollbar thumb @@ -537,7 +538,7 @@ public class SkiaItemsView : SkiaView cumulativeY += itemH + _itemSpacing; } - Console.WriteLine($"[SkiaItemsView] Tap at Y={e.Y}, screenBounds.Top={screenBounds.Top}, scrollOffset={_scrollOffset}, localY={localY}, index={tappedIndex}"); + DiagnosticLog.Debug("SkiaItemsView", $"Tap at Y={e.Y}, screenBounds.Top={screenBounds.Top}, scrollOffset={_scrollOffset}, localY={localY}, index={tappedIndex}"); if (tappedIndex >= 0 && tappedIndex < _items.Count) { diff --git a/Views/SkiaItemsView.cs.bak b/Views/SkiaItemsView.cs.bak deleted file mode 100644 index e69de29..0000000 diff --git a/Views/SkiaLayoutView.cs b/Views/SkiaLayoutView.cs index 6ea5574..745d80c 100644 --- a/Views/SkiaLayoutView.cs +++ b/Views/SkiaLayoutView.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; using Microsoft.Maui; @@ -227,10 +228,10 @@ public abstract class SkiaLayoutView : SkiaView } if (hasCV) { - Console.WriteLine($"[SkiaStackLayout+CV] OnDraw - bounds={bounds}, children={_children.Count}"); + DiagnosticLog.Debug("SkiaLayoutView", $"[SkiaStackLayout+CV] OnDraw - bounds={bounds}, children={_children.Count}"); foreach (var c in _children) { - Console.WriteLine($"[SkiaStackLayout+CV] Child: {c.GetType().Name}, IsVisible={c.IsVisible}, Bounds={c.Bounds}"); + DiagnosticLog.Debug("SkiaLayoutView", $"[SkiaStackLayout+CV] Child: {c.GetType().Name}, IsVisible={c.IsVisible}, Bounds={c.Bounds}"); } } } @@ -959,9 +960,9 @@ public class SkiaGrid : SkiaLayoutView } catch (Exception ex) { - Console.WriteLine($"[SkiaGrid] EXCEPTION in ArrangeOverride: {ex.GetType().Name}: {ex.Message}"); - Console.WriteLine($"[SkiaGrid] Bounds: {bounds}, RowHeights: {_rowHeights.Length}, RowDefs: {_rowDefinitions.Count}, Children: {Children.Count}"); - Console.WriteLine($"[SkiaGrid] Stack trace: {ex.StackTrace}"); + DiagnosticLog.Error("SkiaGrid", $"EXCEPTION in ArrangeOverride: {ex.GetType().Name}: {ex.Message}", ex); + DiagnosticLog.Error("SkiaGrid", $"Bounds: {bounds}, RowHeights: {_rowHeights.Length}, RowDefs: {_rowDefinitions.Count}, Children: {Children.Count}"); + DiagnosticLog.Error("SkiaGrid", $"Stack trace: {ex.StackTrace}"); throw; } } diff --git a/Views/SkiaNavigationPage.cs b/Views/SkiaNavigationPage.cs index 739a339..b3a210a 100644 --- a/Views/SkiaNavigationPage.cs +++ b/Views/SkiaNavigationPage.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Maui.Graphics; using Microsoft.Maui.Platform.Linux; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform; @@ -123,12 +124,12 @@ public class SkiaNavigationPage : SkiaView } else { - Console.WriteLine("[SkiaNavigationPage] Push (no animation): setting _currentPage to " + page.Title); + DiagnosticLog.Debug("SkiaNavigationPage", "Push (no animation): setting _currentPage to " + page.Title); _currentPage = page; _currentPage.OnAppearing(); - Console.WriteLine("[SkiaNavigationPage] Push: calling Invalidate"); + DiagnosticLog.Debug("SkiaNavigationPage", "Push: calling Invalidate"); Invalidate(); - Console.WriteLine("[SkiaNavigationPage] Push: Invalidate called, _currentPage is now " + _currentPage?.Title); + DiagnosticLog.Debug("SkiaNavigationPage", "Push: Invalidate called, _currentPage is now " + _currentPage?.Title); } Pushed?.Invoke(this, new NavigationEventArgs(page)); @@ -326,7 +327,7 @@ public class SkiaNavigationPage : SkiaView else if (_currentPage != null) { // Draw current page normally - Console.WriteLine("[SkiaNavigationPage] OnDraw: drawing _currentPage=" + _currentPage.Title); + DiagnosticLog.Debug("SkiaNavigationPage", "OnDraw: drawing _currentPage=" + _currentPage.Title); _currentPage.Bounds = new Rect(bounds.Left, bounds.Top, bounds.Width, bounds.Height); _currentPage.Draw(canvas); @@ -375,7 +376,7 @@ public class SkiaNavigationPage : SkiaView public override void OnPointerPressed(PointerEventArgs e) { - Console.WriteLine($"[SkiaNavigationPage] OnPointerPressed at ({e.X}, {e.Y}), _isAnimating={_isAnimating}"); + DiagnosticLog.Debug("SkiaNavigationPage", $"OnPointerPressed at ({e.X}, {e.Y}), _isAnimating={_isAnimating}"); if (_isAnimating) return; // Check for back button click @@ -383,13 +384,13 @@ public class SkiaNavigationPage : SkiaView { if (e.X < 56 && e.Y < _navigationBarHeight) { - Console.WriteLine($"[SkiaNavigationPage] Back button clicked"); + DiagnosticLog.Debug("SkiaNavigationPage", "Back button clicked"); Pop(); return; } } - Console.WriteLine($"[SkiaNavigationPage] Forwarding to _currentPage: {_currentPage?.GetType().Name}"); + DiagnosticLog.Debug("SkiaNavigationPage", $"Forwarding to _currentPage: {_currentPage?.GetType().Name}"); _currentPage?.OnPointerPressed(e); } @@ -454,7 +455,7 @@ public class SkiaNavigationPage : SkiaView } catch (Exception ex) { - Console.WriteLine($"[SkiaNavigationPage] HitTest error: {ex.Message}"); + DiagnosticLog.Error("SkiaNavigationPage", $"HitTest error: {ex.Message}", ex); } } diff --git a/Views/SkiaPage.cs b/Views/SkiaPage.cs index ea49bd6..6688b85 100644 --- a/Views/SkiaPage.cs +++ b/Views/SkiaPage.cs @@ -3,6 +3,7 @@ using SkiaSharp; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform; @@ -204,7 +205,7 @@ public class SkiaPage : SkiaView var availableSize = new Size(adjustedBounds.Width, adjustedBounds.Height); _content.Measure(availableSize); _content.Arrange(new Rect(adjustedBounds.Left, adjustedBounds.Top, adjustedBounds.Width, adjustedBounds.Height)); - Console.WriteLine($"[SkiaPage] Drawing content: {_content.GetType().Name}, Bounds={_content.Bounds}, IsVisible={_content.IsVisible}"); + DiagnosticLog.Debug("SkiaPage", $"Drawing content: {_content.GetType().Name}, Bounds={_content.Bounds}, IsVisible={_content.IsVisible}"); _content.Draw(canvas); } @@ -284,7 +285,7 @@ public class SkiaPage : SkiaView public void OnAppearing() { - Console.WriteLine($"[SkiaPage] OnAppearing called for: {Title}, HasListeners={Appearing != null}"); + DiagnosticLog.Debug("SkiaPage", $"OnAppearing called for: {Title}, HasListeners={Appearing != null}"); Appearing?.Invoke(this, EventArgs.Empty); } @@ -436,7 +437,7 @@ public class SkiaContentPage : SkiaPage private void DrawToolbarItems(SKCanvas canvas, SKRect navBarBounds) { var primaryItems = _toolbarItems.Where(t => t.Order == SkiaToolbarItemOrder.Primary).ToList(); - Console.WriteLine($"[SkiaContentPage] DrawToolbarItems: {primaryItems.Count} primary items, navBarBounds={navBarBounds}"); + DiagnosticLog.Debug("SkiaContentPage", $"DrawToolbarItems: {primaryItems.Count} primary items, navBarBounds={navBarBounds}"); if (primaryItems.Count == 0) return; using var font = new SKFont(SKTypeface.Default, 14); @@ -470,7 +471,7 @@ public class SkiaContentPage : SkiaPage var destRect = new SKRect(iconX, iconY, iconX + iconSize, iconY + iconSize); canvas.DrawBitmap(item.Icon, destRect); - Console.WriteLine($"[SkiaContentPage] Drew toolbar icon '{item.Text}' at ({iconX}, {iconY})"); + DiagnosticLog.Debug("SkiaContentPage", $"Drew toolbar icon '{item.Text}' at ({iconX}, {iconY})"); } else { @@ -490,33 +491,33 @@ public class SkiaContentPage : SkiaPage canvas.DrawText(item.Text, x, y, textPaint); } - Console.WriteLine($"[SkiaContentPage] Toolbar item '{item.Text}' HitBounds set to {item.HitBounds}"); + DiagnosticLog.Debug("SkiaContentPage", $"Toolbar item '{item.Text}' HitBounds set to {item.HitBounds}"); rightEdge = itemLeft - 8; // Gap between items } } public override void OnPointerPressed(PointerEventArgs e) { - Console.WriteLine($"[SkiaContentPage] OnPointerPressed at ({e.X}, {e.Y}), ShowNavigationBar={ShowNavigationBar}, NavigationBarHeight={NavigationBarHeight}"); - Console.WriteLine($"[SkiaContentPage] ToolbarItems count: {_toolbarItems.Count}"); + DiagnosticLog.Debug("SkiaContentPage", $"OnPointerPressed at ({e.X}, {e.Y}), ShowNavigationBar={ShowNavigationBar}, NavigationBarHeight={NavigationBarHeight}"); + DiagnosticLog.Debug("SkiaContentPage", $"ToolbarItems count: {_toolbarItems.Count}"); // Check toolbar item clicks if (ShowNavigationBar && e.Y < NavigationBarHeight) { - Console.WriteLine($"[SkiaContentPage] In navigation bar area, checking toolbar items"); + DiagnosticLog.Debug("SkiaContentPage", "In navigation bar area, checking toolbar items"); foreach (var item in _toolbarItems.Where(t => t.Order == SkiaToolbarItemOrder.Primary)) { var bounds = item.HitBounds; var contains = bounds.Contains(e.X, e.Y); - Console.WriteLine($"[SkiaContentPage] Checking item '{item.Text}', HitBounds=({bounds.Left},{bounds.Top},{bounds.Right},{bounds.Bottom}), Click=({e.X},{e.Y}), Contains={contains}, Command={item.Command != null}"); + DiagnosticLog.Debug("SkiaContentPage", $"Checking item '{item.Text}', HitBounds=({bounds.Left},{bounds.Top},{bounds.Right},{bounds.Bottom}), Click=({e.X},{e.Y}), Contains={contains}, Command={item.Command != null}"); if (contains) { - Console.WriteLine($"[SkiaContentPage] Toolbar item clicked: {item.Text}"); + DiagnosticLog.Debug("SkiaContentPage", $"Toolbar item clicked: {item.Text}"); item.Command?.Execute(null); return; } } - Console.WriteLine($"[SkiaContentPage] No toolbar item hit"); + DiagnosticLog.Debug("SkiaContentPage", "No toolbar item hit"); } base.OnPointerPressed(e); diff --git a/Views/SkiaScrollView.cs b/Views/SkiaScrollView.cs index e86948e..d88574c 100644 --- a/Views/SkiaScrollView.cs +++ b/Views/SkiaScrollView.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform; @@ -428,7 +429,7 @@ public class SkiaScrollView : SkiaView public override void OnScroll(ScrollEventArgs e) { - Console.WriteLine($"[SkiaScrollView] OnScroll - DeltaY={e.DeltaY}, ScrollableHeight={ScrollableHeight}, ContentSize={ContentSize}, Bounds={Bounds}"); + DiagnosticLog.Debug("SkiaScrollView", $"OnScroll - DeltaY={e.DeltaY}, ScrollableHeight={ScrollableHeight}, ContentSize={ContentSize}, Bounds={Bounds}"); // Handle mouse wheel scrolling var deltaMultiplier = 40f; // Scroll speed @@ -438,7 +439,7 @@ public class SkiaScrollView : SkiaView { var oldScrollY = _scrollY; ScrollY += e.DeltaY * deltaMultiplier; - Console.WriteLine($"[SkiaScrollView] ScrollY changed: {oldScrollY} -> {_scrollY}"); + DiagnosticLog.Debug("SkiaScrollView", $"ScrollY changed: {oldScrollY} -> {_scrollY}"); if (_scrollY != oldScrollY) scrolled = true; } @@ -876,7 +877,7 @@ public class SkiaScrollView : SkiaView var actualBounds = bounds; if (double.IsInfinity(bounds.Height) || double.IsNaN(bounds.Height)) { - Console.WriteLine($"[SkiaScrollView] WARNING: Infinite/NaN height, using default viewport={DefaultViewportHeight}"); + DiagnosticLog.Warn("SkiaScrollView", $"Infinite/NaN height, using default viewport={DefaultViewportHeight}"); actualBounds = new Rect(bounds.Left, bounds.Top, bounds.Width, DefaultViewportHeight); } diff --git a/Views/SkiaShell.cs b/Views/SkiaShell.cs index a9f5cf3..486a2f5 100644 --- a/Views/SkiaShell.cs +++ b/Views/SkiaShell.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform; @@ -481,10 +482,10 @@ public class SkiaShell : SkiaLayoutView /// public void RefreshTheme() { - Console.WriteLine("[SkiaShell] RefreshTheme called - refreshing all pages"); + DiagnosticLog.Debug("SkiaShell", "RefreshTheme called - refreshing all pages"); if (MauiShell != null && ColorRefresher != null) { - Console.WriteLine("[SkiaShell] Refreshing shell colors"); + DiagnosticLog.Debug("SkiaShell", "Refreshing shell colors"); ColorRefresher(this, MauiShell); } if (ContentRenderer != null) @@ -495,7 +496,7 @@ public class SkiaShell : SkiaLayoutView { if (item.MauiShellContent != null) { - Console.WriteLine("[SkiaShell] Re-rendering: " + item.Title); + DiagnosticLog.Debug("SkiaShell", "Re-rendering: " + item.Title); var skiaView = ContentRenderer(item.MauiShellContent); if (skiaView != null) { @@ -775,7 +776,7 @@ public class SkiaShell : SkiaLayoutView protected override Rect ArrangeOverride(Rect bounds) { - Console.WriteLine($"[SkiaShell] ArrangeOverride - bounds={bounds}"); + DiagnosticLog.Debug("SkiaShell", $"ArrangeOverride - bounds={bounds}"); // Arrange current content with padding if (_currentContent != null) @@ -787,7 +788,7 @@ public class SkiaShell : SkiaLayoutView contentTop, bounds.Width - ContentPadding * 2, contentBottom - contentTop); - Console.WriteLine($"[SkiaShell] Arranging content with bounds={contentBounds}, padding={ContentPadding}"); + DiagnosticLog.Debug("SkiaShell", $"Arranging content with bounds={contentBounds}, padding={ContentPadding}"); _currentContent.Arrange(contentBounds); } diff --git a/Views/SkiaView.cs b/Views/SkiaView.cs index 7461fad..68adad9 100644 --- a/Views/SkiaView.cs +++ b/Views/SkiaView.cs @@ -1682,7 +1682,7 @@ public abstract class SkiaView : BindableObject, IDisposable, IAccessible public virtual void OnPointerReleased(PointerEventArgs e) { - Console.WriteLine($"[SkiaView] OnPointerReleased on {GetType().Name}, MauiView={MauiView?.GetType().Name ?? "null"}"); + DiagnosticLog.Debug("SkiaView", $"OnPointerReleased on {GetType().Name}, MauiView={MauiView?.GetType().Name ?? "null"}"); if (MauiView != null) { GestureManager.ProcessPointerUp(MauiView, e.X, e.Y); diff --git a/Views/SkiaWebView.cs b/Views/SkiaWebView.cs index 457e58c..87da40c 100644 --- a/Views/SkiaWebView.cs +++ b/Views/SkiaWebView.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Net; using System.Runtime.InteropServices; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; namespace Microsoft.Maui.Platform; @@ -507,7 +508,7 @@ public class SkiaWebView : SkiaView { _mainDisplay = display; _mainWindow = window; - Console.WriteLine($"[WebView] Main window set: display={display}, window={window}"); + DiagnosticLog.Debug("SkiaWebView", $"Main window set: display={display}, window={window}"); } public static void ProcessGtkEvents() @@ -548,14 +549,14 @@ public class SkiaWebView : SkiaView { _useGtk4 = true; _webkitLib = "libwebkitgtk-6.0.so.4"; - Console.WriteLine("[WebView] Warning: Using GTK4 WebKitGTK - embedding may be limited"); + DiagnosticLog.Warn("SkiaWebView", "Using GTK4 WebKitGTK - embedding may be limited"); } } } if (_webkitHandle == IntPtr.Zero) { - Console.WriteLine("[WebView] WebKitGTK not found. Install with: sudo apt install libwebkit2gtk-4.1-0"); + DiagnosticLog.Error("SkiaWebView", "WebKitGTK not found. Install with: sudo apt install libwebkit2gtk-4.1-0"); return false; } @@ -579,7 +580,7 @@ public class SkiaWebView : SkiaView _webkitGetUserAgent = LoadFunction("webkit_settings_get_user_agent"); _webkitRunJavascript = LoadFunction("webkit_web_view_run_javascript"); - Console.WriteLine($"[WebView] Using {_webkitLib}"); + DiagnosticLog.Debug("SkiaWebView", $"Using {_webkitLib}"); return _webkitWebViewNew != null; } @@ -593,7 +594,7 @@ public class SkiaWebView : SkiaView { string[] events = { "STARTED", "REDIRECTED", "COMMITTED", "FINISHED" }; string eventName = loadEvent >= 0 && loadEvent < events.Length ? events[loadEvent] : loadEvent.ToString(); - Console.WriteLine($"[WebView] Load event: {eventName}"); + DiagnosticLog.Debug("SkiaWebView", $"Load event: {eventName}"); if (!_webViewInstances.TryGetValue(webView, out var instance)) return; @@ -644,11 +645,11 @@ public class SkiaWebView : SkiaView Environment.SetEnvironmentVariable("GDK_BACKEND", "x11"); Environment.SetEnvironmentVariable("LIBGL_ALWAYS_SOFTWARE", "1"); Environment.SetEnvironmentVariable("WEBKIT_DISABLE_COMPOSITING_MODE", "1"); - Console.WriteLine("[WebView] Using X11 backend with software rendering for proper positioning"); + DiagnosticLog.Debug("SkiaWebView", "Using X11 backend with software rendering for proper positioning"); var waylandDisplay = Environment.GetEnvironmentVariable("WAYLAND_DISPLAY"); - Console.WriteLine($"[WebView] XDG_RUNTIME_DIR: {Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR")}"); - Console.WriteLine($"[WebView] Forcing X11: GDK_BACKEND=x11, WAYLAND_DISPLAY={waylandDisplay}, XDG_SESSION_TYPE=x11"); + DiagnosticLog.Debug("SkiaWebView", $"XDG_RUNTIME_DIR: {Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR")}"); + DiagnosticLog.Debug("SkiaWebView", $"Forcing X11: GDK_BACKEND=x11, WAYLAND_DISPLAY={waylandDisplay}, XDG_SESSION_TYPE=x11"); if (_useGtk4) { @@ -660,19 +661,19 @@ public class SkiaWebView : SkiaView IntPtr argv = IntPtr.Zero; if (!gtk3_init_check(ref argc, ref argv)) { - Console.WriteLine("[WebView] gtk3_init_check failed!"); + DiagnosticLog.Error("SkiaWebView", "gtk3_init_check failed!"); } } _gtkInitialized = true; var gdkDisplay = gdk3_display_get_default(); - Console.WriteLine($"[WebView] GDK display: {gdkDisplay}"); + DiagnosticLog.Debug("SkiaWebView", $"GDK display: {gdkDisplay}"); } _webView = _webkitWebViewNew!(); if (_webView == IntPtr.Zero) { - Console.WriteLine("[WebView] Failed to create WebKit view"); + DiagnosticLog.Error("SkiaWebView", "Failed to create WebKit view"); return; } @@ -680,7 +681,7 @@ public class SkiaWebView : SkiaView _loadChangedCallback = OnLoadChanged; var callbackPtr = Marshal.GetFunctionPointerForDelegate(_loadChangedCallback); g_signal_connect_data(_webView, "load-changed", callbackPtr, IntPtr.Zero, IntPtr.Zero, 0); - Console.WriteLine("[WebView] Connected to load-changed signal"); + DiagnosticLog.Debug("SkiaWebView", "Connected to load-changed signal"); int width = Math.Max(800, (int)RequestedWidth); int height = Math.Max(600, (int)RequestedHeight); @@ -691,7 +692,7 @@ public class SkiaWebView : SkiaView gtk4_window_set_title(_gtkWindow, "OpenMaui WebView"); gtk4_window_set_default_size(_gtkWindow, width, height); gtk4_window_set_child(_gtkWindow, _webView); - Console.WriteLine($"[WebView] GTK4 window created: {width}x{height}"); + DiagnosticLog.Debug("SkiaWebView", $"GTK4 window created: {width}x{height}"); } else { @@ -702,7 +703,7 @@ public class SkiaWebView : SkiaView gtk3_widget_set_vexpand(_webView, true); gtk3_widget_set_size_request(_webView, width, height); gtk3_container_add(_gtkWindow, _webView); - Console.WriteLine($"[WebView] GTK3 TOPLEVEL window created: {width}x{height}"); + DiagnosticLog.Debug("SkiaWebView", $"GTK3 TOPLEVEL window created: {width}x{height}"); } ConfigureWebKitSettings(); @@ -727,11 +728,11 @@ public class SkiaWebView : SkiaView LoadHtml(_html); } - Console.WriteLine("[WebView] Initialized successfully"); + DiagnosticLog.Debug("SkiaWebView", "Initialized successfully"); } catch (Exception ex) { - Console.WriteLine($"[WebView] Initialization failed: {ex.Message}"); + DiagnosticLog.Error("SkiaWebView", $"Initialization failed: {ex.Message}", ex); } } @@ -746,31 +747,31 @@ public class SkiaWebView : SkiaView var settings = _webkitGetSettings(_webView); if (settings == IntPtr.Zero) { - Console.WriteLine("[WebView] Could not get WebKit settings"); + DiagnosticLog.Warn("SkiaWebView", "Could not get WebKit settings"); return; } if (_webkitSetHardwareAcceleration != null) { _webkitSetHardwareAcceleration(settings, 2); // NEVER - Console.WriteLine("[WebView] Set hardware acceleration to NEVER (software rendering)"); + DiagnosticLog.Debug("SkiaWebView", "Set hardware acceleration to NEVER (software rendering)"); } else { - Console.WriteLine("[WebView] Warning: Could not set hardware acceleration policy"); + DiagnosticLog.Warn("SkiaWebView", "Could not set hardware acceleration policy"); } if (_webkitSetWebgl != null) { _webkitSetWebgl(settings, false); - Console.WriteLine("[WebView] Disabled WebGL"); + DiagnosticLog.Debug("SkiaWebView", "Disabled WebGL"); } - Console.WriteLine("[WebView] WebKit settings configured successfully"); + DiagnosticLog.Debug("SkiaWebView", "WebKit settings configured successfully"); } catch (Exception ex) { - Console.WriteLine($"[WebView] Failed to configure settings: {ex.Message}"); + DiagnosticLog.Error("SkiaWebView", $"Failed to configure settings: {ex.Message}", ex); } } @@ -808,28 +809,28 @@ public class SkiaWebView : SkiaView { Navigating?.Invoke(this, new WebNavigatingEventArgs(url)); _webkitLoadUri(_webView, url); - Console.WriteLine($"[WebView] URL loaded: {url}"); + DiagnosticLog.Debug("SkiaWebView", $"URL loaded: {url}"); ShowNativeWindow(); } } public void LoadHtml(string html, string? baseUrl = null) { - Console.WriteLine($"[WebView] LoadHtml called, html length: {html?.Length ?? 0}"); + DiagnosticLog.Debug("SkiaWebView", $"LoadHtml called, html length: {html?.Length ?? 0}"); if (string.IsNullOrEmpty(html)) { - Console.WriteLine("[WebView] Cannot load HTML - html is null or empty"); + DiagnosticLog.Warn("SkiaWebView", "Cannot load HTML - html is null or empty"); return; } if (!_isInitialized) Initialize(); if (_webView == IntPtr.Zero || _webkitLoadHtml == null) { - Console.WriteLine("[WebView] Cannot load HTML - not initialized or no webkit function"); + DiagnosticLog.Warn("SkiaWebView", "Cannot load HTML - not initialized or no webkit function"); return; } - Console.WriteLine("[WebView] Calling webkit_web_view_load_html..."); + DiagnosticLog.Debug("SkiaWebView", "Calling webkit_web_view_load_html..."); _webkitLoadHtml(_webView, html, baseUrl); - Console.WriteLine("[WebView] HTML loaded to WebKit"); + DiagnosticLog.Debug("SkiaWebView", "HTML loaded to WebKit"); ShowNativeWindow(); } @@ -919,13 +920,13 @@ public class SkiaWebView : SkiaView { if (_mainDisplay == IntPtr.Zero || _mainWindow == IntPtr.Zero) { - Console.WriteLine("[WebView] Cannot create X11 container - main window not set"); + DiagnosticLog.Warn("SkiaWebView", "Cannot create X11 container - main window not set"); return false; } if (_x11Container != IntPtr.Zero) { - Console.WriteLine("[WebView] X11 container already exists"); + DiagnosticLog.Debug("SkiaWebView", "X11 container already exists"); return true; } @@ -939,23 +940,23 @@ public class SkiaWebView : SkiaView if (width < 100) width = 780; if (height < 100) height = 300; - Console.WriteLine($"[WebView] Creating X11 container at ({x}, {y}), size ({width}x{height})"); + DiagnosticLog.Debug("SkiaWebView", $"Creating X11 container at ({x}, {y}), size ({width}x{height})"); _x11Container = XCreateSimpleWindow(_mainDisplay, _mainWindow, x, y, width, height, 0, 0, 0xFFFFFF); if (_x11Container == IntPtr.Zero) { - Console.WriteLine("[WebView] Failed to create X11 container window"); + DiagnosticLog.Error("SkiaWebView", "Failed to create X11 container window"); return false; } - Console.WriteLine($"[WebView] Created X11 container: {_x11Container.ToInt64()}"); + DiagnosticLog.Debug("SkiaWebView", $"Created X11 container: {_x11Container.ToInt64()}"); XMapWindow(_mainDisplay, _x11Container); XFlush(_mainDisplay); return true; } catch (Exception ex) { - Console.WriteLine($"[WebView] Error creating X11 container: {ex.Message}"); + DiagnosticLog.Error("SkiaWebView", $"Error creating X11 container: {ex.Message}", ex); return false; } } @@ -966,7 +967,7 @@ public class SkiaWebView : SkiaView if (!_isInitialized) Initialize(); if (_gtkWindow == IntPtr.Zero) return; - Console.WriteLine("[WebView] Showing native GTK window..."); + DiagnosticLog.Debug("SkiaWebView", "Showing native GTK window..."); if (!_useGtk4) { @@ -997,14 +998,14 @@ public class SkiaWebView : SkiaView TryReparentIntoMainWindow(); _isEmbedded = true; - Console.WriteLine("[WebView] Native window shown"); + DiagnosticLog.Debug("SkiaWebView", "Native window shown"); } private void TryReparentIntoMainWindow() { if (_mainDisplay == IntPtr.Zero || _mainWindow == IntPtr.Zero) { - Console.WriteLine("[WebView] Cannot setup - main window not set"); + DiagnosticLog.Warn("SkiaWebView", "Cannot setup - main window not set"); return; } @@ -1015,11 +1016,11 @@ public class SkiaWebView : SkiaView if (_gtkX11Window != IntPtr.Zero) { _isProperlyReparented = true; - Console.WriteLine($"[WebView] GTK X11 window: {_gtkX11Window} (reparented successfully)"); + DiagnosticLog.Debug("SkiaWebView", $"GTK X11 window: {_gtkX11Window} (reparented successfully)"); } else { - Console.WriteLine($"[WebView] GTK X11 window: failed to get XID"); + DiagnosticLog.Warn("SkiaWebView", "GTK X11 window: failed to get XID"); } } @@ -1047,7 +1048,7 @@ public class SkiaWebView : SkiaView int width = Math.Max(100, (int)Bounds.Width); int height = Math.Max(100, (int)Bounds.Height); - Console.WriteLine($"[WebView] Position: screen=({screenX}, {screenY}), size ({width}x{height}), bounds=({Bounds.Left},{Bounds.Top})"); + DiagnosticLog.Debug("SkiaWebView", $"Position: screen=({screenX}, {screenY}), size ({width}x{height}), bounds=({Bounds.Left},{Bounds.Top})"); if (!_useGtk4) { @@ -1126,11 +1127,11 @@ public class SkiaWebView : SkiaView gtk3_window_set_skip_pager_hint(_gtkWindow, true); gtk3_window_set_keep_above(_gtkWindow, true); gtk3_window_set_decorated(_gtkWindow, false); - Console.WriteLine("[WebView] Overlay mode enabled with UTILITY hint"); + DiagnosticLog.Debug("SkiaWebView", "Overlay mode enabled with UTILITY hint"); } catch (Exception ex) { - Console.WriteLine($"[WebView] Failed to enable overlay mode: {ex.Message}"); + DiagnosticLog.Error("SkiaWebView", $"Failed to enable overlay mode: {ex.Message}", ex); } } @@ -1138,7 +1139,7 @@ public class SkiaWebView : SkiaView { if (_mainDisplay == IntPtr.Zero || _mainWindow == IntPtr.Zero) { - Console.WriteLine("[WebView] Cannot setup embedding - main window not set"); + DiagnosticLog.Warn("SkiaWebView", "Cannot setup embedding - main window not set"); return; } @@ -1148,7 +1149,7 @@ public class SkiaWebView : SkiaView int width = Math.Max(100, (int)Bounds.Width); int height = Math.Max(100, (int)Bounds.Height); - Console.WriteLine($"[WebView] Initial position: ({screenX}, {screenY}), size ({width}x{height})"); + DiagnosticLog.Debug("SkiaWebView", $"Initial position: ({screenX}, {screenY}), size ({width}x{height})"); if (!_useGtk4) { @@ -1191,7 +1192,7 @@ public class SkiaWebView : SkiaView if (mainWindowMoved || Math.Abs(screenX - _lastPosX) > 2 || Math.Abs(screenY - _lastPosY) > 2 || Math.Abs(width - _lastWidth) > 2 || Math.Abs(height - _lastHeight) > 2) { - Console.WriteLine($"[WebView] Move to ({screenX}, {screenY}), size ({width}x{height}), mainWin=({destX},{destY}), bounds=({Bounds.Left},{Bounds.Top})"); + DiagnosticLog.Debug("SkiaWebView", $"Move to ({screenX}, {screenY}), size ({width}x{height}), mainWin=({destX},{destY}), bounds=({Bounds.Left},{Bounds.Top})"); _lastPosX = screenX; _lastPosY = screenY; _lastWidth = width; @@ -1266,12 +1267,12 @@ public class SkiaWebView : SkiaView var root = XDefaultRootWindow(display); if (XTranslateCoordinates(display, window, root, 0, 0, out x, out y, out _)) { - Console.WriteLine($"[WebView] Main window at screen ({x}, {y})"); + DiagnosticLog.Debug("SkiaWebView", $"Main window at screen ({x}, {y})"); } } catch (Exception ex) { - Console.WriteLine($"[WebView] Failed to get window position: {ex.Message}"); + DiagnosticLog.Error("SkiaWebView", $"Failed to get window position: {ex.Message}", ex); } } @@ -1295,13 +1296,13 @@ public class SkiaWebView : SkiaView if (_isProperlyReparented && _gtkX11Window != IntPtr.Zero) { - Console.WriteLine($"[WebView] UpdateEmbedded (reparented): ({x}, {y}), size ({width}x{height})"); + DiagnosticLog.Debug("SkiaWebView", $"UpdateEmbedded (reparented): ({x}, {y}), size ({width}x{height})"); XMoveResizeWindow(_mainDisplay, _gtkX11Window, x, y, width, height); XFlush(_mainDisplay); } else if (_x11Container != IntPtr.Zero) { - Console.WriteLine($"[WebView] UpdateEmbedded (container): ({x}, {y}), size ({width}x{height})"); + DiagnosticLog.Debug("SkiaWebView", $"UpdateEmbedded (container): ({x}, {y}), size ({width}x{height})"); XMoveResizeWindow(_mainDisplay, _x11Container, x, y, width, height); if (_gtkX11Window != IntPtr.Zero && _isProperlyReparented) { diff --git a/Window/GtkHostWindow.cs b/Window/GtkHostWindow.cs index 83d9404..ba3fdaf 100644 --- a/Window/GtkHostWindow.cs +++ b/Window/GtkHostWindow.cs @@ -3,6 +3,7 @@ using System.IO; using System.Runtime.InteropServices; using Microsoft.Maui.Platform.Linux.Native; using Microsoft.Maui.Platform.Linux.Rendering; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform.Linux.Window; @@ -163,7 +164,7 @@ public sealed class GtkHostWindow : IDisposable ConnectSignal(_window, "button-release-event", Marshal.GetFunctionPointerForDelegate(_buttonReleaseHandler)); ConnectSignal(_window, "motion-notify-event", Marshal.GetFunctionPointerForDelegate(_motionHandler)); - Console.WriteLine($"[GtkHostWindow] Created GTK window on X11: {width}x{height}"); + DiagnosticLog.Debug("GtkHostWindow", $"Created GTK window on X11: {width}x{height}"); } private void ConnectSignal(IntPtr widget, string signal, IntPtr handler) @@ -202,7 +203,7 @@ public sealed class GtkHostWindow : IDisposable 1 => "Left", _ => $"Other({button})", }; - Console.WriteLine($"[GtkHostWindow] ButtonPress at ({x:F1}, {y:F1}), button={button} ({buttonName})"); + DiagnosticLog.Debug("GtkHostWindow", $"ButtonPress at ({x:F1}, {y:F1}), button={button} ({buttonName})"); PointerPressed?.Invoke(this, (x, y, button)); _skiaSurface?.RaisePointerPressed(x, y, button); return false; @@ -256,7 +257,7 @@ public sealed class GtkHostWindow : IDisposable { if (string.IsNullOrEmpty(iconPath) || !File.Exists(iconPath)) { - Console.WriteLine("[GtkHostWindow] Icon file not found: " + iconPath); + DiagnosticLog.Warn("GtkHostWindow", "Icon file not found: " + iconPath); return; } try @@ -266,12 +267,12 @@ public sealed class GtkHostWindow : IDisposable { GtkNative.gtk_window_set_icon(_window, pixbuf); GtkNative.g_object_unref(pixbuf); - Console.WriteLine("[GtkHostWindow] Set window icon: " + iconPath); + DiagnosticLog.Debug("GtkHostWindow", "Set window icon: " + iconPath); } } catch (Exception ex) { - Console.WriteLine("[GtkHostWindow] Failed to set icon: " + ex.Message); + DiagnosticLog.Error("GtkHostWindow", "Failed to set icon", ex); } } @@ -285,7 +286,7 @@ public sealed class GtkHostWindow : IDisposable GtkNative.gtk_widget_set_size_request(webViewWidget, width, height); GtkNative.gtk_fixed_put(_webViewLayer, webViewWidget, x, y); GtkNative.gtk_widget_show(webViewWidget); - Console.WriteLine($"[GtkHostWindow] Added WebView at ({x}, {y}) size {width}x{height}"); + DiagnosticLog.Debug("GtkHostWindow", $"Added WebView at ({x}, {y}) size {width}x{height}"); } public void MoveResizeWebView(IntPtr webViewWidget, int x, int y, int width, int height) diff --git a/Window/WaylandWindow.cs b/Window/WaylandWindow.cs index 7cdf811..64dd30a 100644 --- a/Window/WaylandWindow.cs +++ b/Window/WaylandWindow.cs @@ -3,6 +3,7 @@ using System.Runtime.InteropServices; using Microsoft.Maui.Platform.Linux.Input; +using Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform.Linux.Window; @@ -789,7 +790,7 @@ public class WaylandWindow : IDisposable // Create shared memory buffer CreateShmBuffer(); - Console.WriteLine($"[Wayland] Window created: {_width}x{_height}"); + DiagnosticLog.Debug("WaylandWindow", $"Window created: {_width}x{_height}"); } private void CreateShmBuffer() @@ -910,7 +911,7 @@ public class WaylandWindow : IDisposable var window = (WaylandWindow)handle.Target!; var interfaceName = Marshal.PtrToStringAnsi(iface); - Console.WriteLine($"[Wayland] Global: {interfaceName} v{version}"); + DiagnosticLog.Debug("WaylandWindow", $"Global: {interfaceName} v{version}"); switch (interfaceName) { diff --git a/Window/X11Window.cs b/Window/X11Window.cs index 6d3f5d2..1773469 100644 --- a/Window/X11Window.cs +++ b/Window/X11Window.cs @@ -3,6 +3,7 @@ using Microsoft.Maui.Platform.Linux.Interop; using Microsoft.Maui.Platform.Linux.Input; +using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; using Svg.Skia; @@ -226,7 +227,7 @@ public class X11Window : IDisposable }; X11.XSetClassHint(_display, _window, ref classHint); - Console.WriteLine($"[X11Window] Set WM_CLASS: {resName}, {resClass}"); + DiagnosticLog.Debug("X11Window", $"Set WM_CLASS: {resName}, {resClass}"); } finally { @@ -244,10 +245,10 @@ public class X11Window : IDisposable { if (string.IsNullOrEmpty(iconPath) || !System.IO.File.Exists(iconPath)) { - Console.WriteLine("[X11Window] Icon file not found: " + iconPath); + DiagnosticLog.Warn("X11Window", "Icon file not found: " + iconPath); return; } - Console.WriteLine("[X11Window] SetIcon called: " + iconPath); + DiagnosticLog.Debug("X11Window", "SetIcon called: " + iconPath); try { SKBitmap? bitmap = null; @@ -255,7 +256,7 @@ public class X11Window : IDisposable // Handle SVG icons if (iconPath.EndsWith(".svg", StringComparison.OrdinalIgnoreCase)) { - Console.WriteLine("[X11Window] Loading SVG icon"); + DiagnosticLog.Debug("X11Window", "Loading SVG icon"); using var svg = new SKSvg(); svg.Load(iconPath); if (svg.Picture != null) @@ -273,16 +274,16 @@ public class X11Window : IDisposable } else { - Console.WriteLine("[X11Window] Loading raster icon"); + DiagnosticLog.Debug("X11Window", "Loading raster icon"); bitmap = SKBitmap.Decode(iconPath); } if (bitmap == null) { - Console.WriteLine("[X11Window] Failed to load icon: " + iconPath); + DiagnosticLog.Warn("X11Window", "Failed to load icon: " + iconPath); return; } - Console.WriteLine($"[X11Window] Loaded bitmap: {bitmap.Width}x{bitmap.Height}"); + DiagnosticLog.Debug("X11Window", $"Loaded bitmap: {bitmap.Width}x{bitmap.Height}"); // Scale to 64x64 if needed int targetSize = 64; @@ -318,11 +319,11 @@ public class X11Window : IDisposable X11.XChangeProperty(_display, _window, property, type, 32, 0, (nint)data, dataSize); } X11.XFlush(_display); - Console.WriteLine($"[X11Window] Set window icon: {width}x{height}"); + DiagnosticLog.Debug("X11Window", $"Set window icon: {width}x{height}"); } catch (Exception ex) { - Console.WriteLine("[X11Window] Failed to set icon: " + ex.Message); + DiagnosticLog.Error("X11Window", "Failed to set icon", ex); } } @@ -450,7 +451,7 @@ public class X11Window : IDisposable { if (_eventCounter % 100 == 0) { - Console.WriteLine($"[X11Window] ProcessEvents: {pending} pending events"); + DiagnosticLog.Debug("X11Window", $"ProcessEvents: {pending} pending events"); } _eventCounter++; while (X11.XPending(_display) > 0)