Skip to content

Commit 459b404

Browse files
Add logging and tests for the R3 integration
1 parent c967b65 commit 459b404

File tree

12 files changed

+1921
-7
lines changed

12 files changed

+1921
-7
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace SharpHook.R3.Logging;
2+
3+
/// <summary>
4+
/// Represents a reactive R3 source of libuiohook logs.
5+
/// </summary>
6+
/// <seealso cref="ILogSource" />
7+
/// <seealso cref="LogSource" />
8+
public interface IR3LogSource : IDisposable
9+
{
10+
/// <summary>
11+
/// An observable which is emitted when libuiohook logs a message.
12+
/// </summary>
13+
Observable<LogEntry> MessageLogged { get; }
14+
15+
/// <summary>
16+
/// Gets the value which indicates whether the log source is disposed.
17+
/// </summary>
18+
/// <value><see langword="true" /> if the log source is disposed. Otherwise, <see langword="false" />.</value>
19+
/// <remarks>The <see cref="MessageLogged" /> observable doesn't emit any values in a disposed log source.</remarks>
20+
bool IsDisposed { get; }
21+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
3+
namespace SharpHook.R3.Logging;
4+
5+
/// <summary>
6+
/// Adapts an <see cref="ILogSource" /> to the <see cref="IR3LogSource" /> interface.
7+
/// </summary>
8+
/// <seealso cref="ILogSource" />
9+
/// <seealso cref="IR3LogSource" />
10+
[ExcludeFromCodeCoverage]
11+
public sealed class R3LogSourceAdapter : IR3LogSource
12+
{
13+
private readonly ILogSource logSource;
14+
private readonly Subject<LogEntry> messageLoggedSubject = new();
15+
16+
/// <summary>
17+
/// Initializes a new instance of the <see cref="R3LogSourceAdapter" /> class.
18+
/// </summary>
19+
/// <param name="logSource">The log source to adapt.</param>
20+
/// <exception cref="ArgumentNullException"><paramref name="logSource" /> is <see langword="null" />.</exception>
21+
public R3LogSourceAdapter(ILogSource logSource)
22+
: this(logSource, ObservableSystem.DefaultTimeProvider)
23+
{ }
24+
25+
/// <summary>
26+
/// Initializes a new instance of the <see cref="R3LogSourceAdapter" /> class.
27+
/// </summary>
28+
/// <param name="logSource">The log source to adapt.</param>
29+
/// <param name="defaultTimeProvider">The default time provider for the observable.</param>
30+
/// <exception cref="ArgumentNullException">
31+
/// <paramref name="logSource" /> or <paramref name="defaultTimeProvider" /> is <see langword="null" />.
32+
/// </exception>
33+
public R3LogSourceAdapter(ILogSource logSource, TimeProvider defaultTimeProvider)
34+
{
35+
this.logSource = logSource ?? throw new ArgumentNullException(nameof(logSource));
36+
37+
if (defaultTimeProvider is null)
38+
{
39+
throw new ArgumentNullException(nameof(defaultTimeProvider));
40+
}
41+
42+
Observable.FromEventHandler<LogEventArgs>(
43+
h => this.logSource.MessageLogged += h, h => this.logSource.MessageLogged -= h)
44+
.Select(this.SelectEventArgs)
45+
.Select(e => e.LogEntry)
46+
.Subscribe(this.messageLoggedSubject.AsObserver());
47+
48+
this.MessageLogged = this.messageLoggedSubject.ObserveOn(defaultTimeProvider);
49+
}
50+
51+
/// <summary>
52+
/// Completes the <see cref="MessageLogged" /> observable if the log source hasn't been disposed.
53+
/// </summary>
54+
~R3LogSourceAdapter() =>
55+
this.Dispose(false);
56+
57+
/// <summary>
58+
/// An observable which is emitted when libuiohook logs a message.
59+
/// </summary>
60+
public Observable<LogEntry> MessageLogged { get; }
61+
62+
/// <summary>
63+
/// Gets the value which indicates whether the log source is disposed.
64+
/// </summary>
65+
/// <value><see langword="true" /> if the log source is disposed. Otherwise, <see langword="false" />.</value>
66+
/// <remarks>The <see cref="MessageLogged" /> observable doesn't emit any values in a disposed log source.</remarks>
67+
public bool IsDisposed => this.logSource.IsDisposed;
68+
69+
/// <summary>
70+
/// Disposes the adapted log source and emits the completion signal for <see cref="MessageLogged" />.
71+
/// </summary>
72+
public void Dispose()
73+
{
74+
if (!this.IsDisposed)
75+
{
76+
this.Dispose(true);
77+
GC.SuppressFinalize(this);
78+
}
79+
}
80+
81+
private void Dispose(bool disposing)
82+
{
83+
if (disposing)
84+
{
85+
this.logSource.Dispose();
86+
}
87+
88+
this.messageLoggedSubject.Dispose();
89+
}
90+
91+
private TArgs SelectEventArgs<TArgs>((object? Sender, TArgs Args) e) =>
92+
e.Args;
93+
}

SharpHook.R3/SharpHook.R3.xml

Lines changed: 65 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

SharpHook.R3/SimpleR3GlobalHook.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ public void Run()
206206
{
207207
this.IsRunning = false;
208208
runningGlobalHooks.TryRemove(this.hookIndex, out _);
209-
this.globalHookProvider.SetDispatchProc(null, IntPtr.Zero);
209+
this.globalHookProvider.SetDispatchProc(null, this.hookIndex);
210210
}
211211

212212
if (result != UioHookResult.Success)
@@ -258,7 +258,7 @@ public Task RunAsync()
258258
} finally
259259
{
260260
runningGlobalHooks.TryRemove(this.hookIndex, out _);
261-
this.globalHookProvider.SetDispatchProc(null, IntPtr.Zero);
261+
this.globalHookProvider.SetDispatchProc(null, this.hookIndex);
262262
}
263263
})
264264
{

SharpHook.Reactive/Logging/ReactiveLogSourceAdapter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ private void Dispose(bool disposing)
8282
this.logSource.Dispose();
8383
}
8484

85-
messageLoggedSubject.OnCompleted();
85+
this.messageLoggedSubject.OnCompleted();
86+
this.messageLoggedSubject.Dispose();
8687
}
8788
}

SharpHook.Reactive/SimpleReactiveGlobalHook.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ public void Run()
202202
{
203203
this.IsRunning = false;
204204
runningGlobalHooks.TryRemove(this.hookIndex, out _);
205-
this.globalHookProvider.SetDispatchProc(null, IntPtr.Zero);
205+
this.globalHookProvider.SetDispatchProc(null, this.hookIndex);
206206
}
207207

208208
if (result != UioHookResult.Success)
@@ -256,7 +256,7 @@ public IObservable<Unit> RunAsync()
256256
} finally
257257
{
258258
runningGlobalHooks.TryRemove(this.hookIndex, out _);
259-
this.globalHookProvider.SetDispatchProc(null, IntPtr.Zero);
259+
this.globalHookProvider.SetDispatchProc(null, this.hookIndex);
260260

261261
}
262262
})

SharpHook.Sample/Usings.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
global using System.Reactive.Concurrency;
22
global using System.Reactive.Linq;
33

4+
global using R3;
5+
46
global using SharpHook;
57
global using SharpHook.Data;
68
global using SharpHook.Logging;
79
global using SharpHook.Providers;
10+
global using SharpHook.R3;
11+
global using SharpHook.R3.Logging;
812
global using SharpHook.Reactive;
913
global using SharpHook.Reactive.Logging;

0 commit comments

Comments
 (0)