Skip to content

Commit 8bf5d0b

Browse files
Update the docs for version 7.0.0
1 parent a554bce commit 8bf5d0b

File tree

16 files changed

+484
-81
lines changed

16 files changed

+484
-81
lines changed

CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,39 @@
11
# SharpHook Changelog
22

3+
## [v7.0.0](https://github.com/TolikPylypchuk/SharpHook/releases/tag/v7.0.0) (August 10, 2025)
4+
5+
### Breaking Changes
6+
7+
- `SimpleReactiveGlobalHook` was renamed to `ReactiveGlobalHook`.
8+
9+
- The `Run`, `RunAsync`, and `Stop` methods were moved from `IGlobalHook` and `IReactiveGlobalHook` into
10+
`IBasicGlobalHook` which both `IGlobalHook` and `IReactiveGlobalHook` now extend.
11+
12+
- Because of the previous change, `RunAsync` for reactive global hooks now returns a `Task` instead of an
13+
`IObservable<Unit>`.
14+
15+
- `ReactiveLogSourceAdapter` now contains a single constructor with a default parameter instead of two constructors.
16+
17+
### New Features
18+
19+
- SharpHook.R3 – a new package for integration with R3 was added.
20+
21+
- `EventLoopGlobalHook` – a new implementation of `IGlobalHook` – was added.
22+
23+
- `IBasicGlobalHook` and `BasicGlobalHookBase` were added for easier creation of custom global hooks with different
24+
event forms.
25+
26+
- `ReactiveLogSourceAdapter` now implements `ILogSource` in addition to `IReactiveLogSource`.
27+
28+
### Bug Fixes
29+
30+
- On Windows, global hooks now correctly report the key that was pressed or released on non-QWERTY layouts.
31+
32+
### Other Changes
33+
34+
- libuiohook was updated to commit
35+
[a2cba5f](https://github.com/TolikPylypchuk/libuiohook/tree/a2cba5f125072dad8683e812027a561e598c7a37).
36+
337
## [v6.2.0](https://github.com/TolikPylypchuk/SharpHook/releases/tag/v6.2.0) (July 19, 2025)
438

539
- The ability to disable events of type `KeyTyped` was added.

README.md

Lines changed: 97 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ called.
105105

106106
> [!IMPORTANT]
107107
> You have to remember that only one global hook can exist at a time since calling `SetDispatchProc` will override the
108-
> previously set one.
108+
> previously set one. Also, running a global hook when another global hook is already running will corrupt the internal
109+
> global state of libuiohook.
109110
110111
Additionally, `UioHook` contains the `PostEvent` method for simulating input events.
111112

@@ -117,15 +118,15 @@ libuiohook also provides functions to get various system properties. The corresp
117118

118119
### Global Hooks
119120

120-
SharpHook provides the `IGlobalHook` interface along with two default implementations which you can use to control the
121+
SharpHook provides the `IGlobalHook` interface along with three default implementations which you can use to control the
121122
hook and subscribe to its events. Here's a basic usage example:
122123

123124
```c#
124125
using SharpHook;
125126

126127
// ...
127128
128-
var hook = new TaskPoolGlobalHook();
129+
var hook = new EventLoopGlobalHook();
129130

130131
hook.HookEnabled += OnHookEnabled; // EventHandler<HookEventArgs>
131132
hook.HookDisabled += OnHookDisabled; // EventHandler<HookEventArgs>
@@ -173,25 +174,37 @@ a real difference only on Windows where there are two different global hooks –
173174
macOS and Linux, there is one hook for all events, and this simply enables filtering keyboard or mouse events out on
174175
these OSes.
175176

176-
SharpHook provides two implementations of `IGlobalHook`:
177+
SharpHook provides three implementations of `IGlobalHook`:
177178

178179
- `SharpHook.SimpleGlobalHook` runs all of its event handlers on the same thread on which the hook itself runs. This
179180
means that the handlers should generally be fast since they will block the hook from handling the events that follow if
180181
they run for too long.
181182

183+
- `SharpHook.EventLoopGlobalHook` runs all of its event handlers on a separate dedicated thread. On backpressure it will
184+
queue the remaining events which means that the hook will be able to process all events. This implementation should be
185+
preferred to `SimpleGlobalHook` except for very simple use-cases. But it has a downside – suppressing event propagation
186+
will be ignored since event handlers are run on another thread.
187+
182188
- `SharpHook.TaskPoolGlobalHook` runs all of its event handlers on other threads inside the default thread pool for
183-
tasks. The parallelism level of the handlers can be configured. On backpressure it will queue the remaining handlers.
184-
This means that the hook will be able to process all events. This implementation should be preferred to
185-
`SimpleGlobalHook` except for very simple use-cases. But it has a downside – suppressing event propagation will be
186-
ignored since event handlers are run on other threads.
189+
tasks. The parallelism level of the handlers can be configured. On backpressure it will queue the remaining events which
190+
means that the hook will be able to process all events. This implementation should be preferred to `SimpleGlobalHook`
191+
except for very simple use-cases. But it has a downside – suppressing event propagation will be ignored since event
192+
handlers are run on other threads. In general, `EventLoopGlobalHook` should be preferred instead, as this class provides
193+
benefits only if events should be processed in parallel, which is rarely the case.
187194

188195
The library also provides the `SharpHook.GlobalHookBase` class which you can extend to create your own implementation
189196
of the global hook. It calls the appropriate event handlers, and you only need to implement a strategy for dispatching
190197
the events. It also keeps a reference to a running global hook so that it's not garbage-collected.
191198

199+
The library also provides the `IBasicGlobalHook` interface and the `BasicGlobalHookBase` class. This class can be
200+
extended to create a custom global hook which has a different form of events from that in `IGlobalHook`.
201+
192202
### Reactive Global Hooks
193203

194-
If you're using Rx.NET, you can use the SharpHook.Reactive package to integrate SharpHook with Rx.NET.
204+
#### Rx.NET
205+
206+
If you're using [Rx.NET](https://github.com/dotnet/reactive), you can use the SharpHook.Reactive package to integrate
207+
SharpHook with Rx.NET.
195208

196209
SharpHook.Reactive provides the `SharpHook.Reactive.IReactiveGlobalHook` interface along with a default implementation
197210
which you can use to use to control the hook and subscribe to its observables. Here's a basic example:
@@ -201,7 +214,7 @@ using SharpHook.Reactive;
201214

202215
// ...
203216
204-
var hook = new SimpleReactiveGlobalHook();
217+
var hook = new ReactiveGlobalHook();
205218

206219
hook.HookEnabled.Subscribe(OnHookEnabled);
207220
hook.HookDisabled.Subscribe(OnHookDisabled);
@@ -226,20 +239,72 @@ hook.MouseWheel.Subscribe(OnMouseWheel);
226239

227240
hook.Run();
228241
// or
229-
hook.RunAsync().Subscribe();
242+
await hook.RunAsync();
230243
```
231244

232245
Reactive global hooks are basically the same as the default global hooks and the same rules apply to them.
233246

234247
SharpHook.Reactive provides two implementations of `IReactiveGlobalHook`:
235248

236-
- `SharpHook.Reactive.SimpleReactiveGlobalHook`. Since we're dealing with observables, it's up to you to decide when
237-
and where to handle the events through schedulers. A default scheduler can be specified for all observables.
249+
- `SharpHook.Reactive.ReactiveGlobalHook`. Since we're dealing with observables, it's up to you to decide when and where
250+
to handle the events through schedulers. A default scheduler can be specified for all observables.
238251

239252
- `SharpHook.Reactive.ReactiveGlobalHookAdapter` adapts an `IGlobalHook` to `IReactiveGlobalHook`. All
240253
subscriptions and changes are propagated to the adapted hook. There is no default adapter from `IReactiveGlobalHook`
241254
to `IGlobalHook`. A default scheduler can be specified for all observables.
242255

256+
#### R3
257+
258+
If you're using [R3](https://github.com/Cysharp/R3), you can use the SharpHook.R3 package to integrate SharpHook with
259+
R3.
260+
261+
SharpHook.R3 provides the `SharpHook.R3.IR3GlobalHook` interface along with a default implementation which you can use
262+
to use to control the hook and subscribe to its observables. Here's a basic example:
263+
264+
```c#
265+
using SharpHook.R3;
266+
267+
// ...
268+
269+
var hook = new R3GlobalHook();
270+
271+
hook.HookEnabled.Subscribe(OnHookEnabled);
272+
hook.HookDisabled.Subscribe(OnHookDisabled);
273+
274+
hook.KeyTyped.Subscribe(OnKeyTyped);
275+
hook.KeyPressed.Subscribe(OnKeyPressed);
276+
hook.KeyReleased.Subscribe(OnKeyReleased);
277+
278+
hook.MouseClicked.Subscribe(OnMouseClicked);
279+
hook.MousePressed.Subscribe(OnMousePressed);
280+
hook.MouseReleased.Subscribe(OnMouseReleased);
281+
282+
hook.MouseMoved
283+
.Debouce(TimeSpan.FromSeconds(0.5))
284+
.Subscribe(OnMouseMoved);
285+
286+
hook.MouseDragged
287+
.Debouce(TimeSpan.FromSeconds(0.5))
288+
.Subscribe(OnMouseDragged);
289+
290+
hook.MouseWheel.Subscribe(OnMouseWheel);
291+
292+
hook.Run();
293+
// or
294+
await hook.RunAsync();
295+
```
296+
297+
R3 global hooks are basically the same as the default global hooks and the same rules apply to them.
298+
299+
SharpHook.R3 provides two implementations of `IR3GlobalHook`:
300+
301+
- `SharpHook.R3.R3GlobalHook`. Since we're dealing with observables, it's up to you to decide when and where to handle
302+
the events through time providers. A default time provider can be specified for all observables.
303+
304+
- `SharpHook.R3.R3GlobalHookAdapter` adapts an `IGlobalHook` to `IR3GlobalHook`. All subscriptions and changes are
305+
propagated to the adapted hook. There is no default adapter from `IR3GlobalHook` to `IGlobalHook`. A default time
306+
provider can be specified for all observables.
307+
243308
### Event Simulation
244309

245310
SharpHook provides the ability to simulate keyboard and mouse events in a cross-platform way as well. Here's a quick
@@ -286,8 +351,8 @@ simulator.SimulateMouseWheel(
286351
type: MouseWheelScrollType.UnitScroll); // UnitScroll by default
287352
```
288353

289-
SharpHook provides the `IEventSimulator` interface, and the default implementation, `EventSimulator`, which calls
290-
`UioHook.PostEvent` to simulate the events.
354+
SharpHook provides the `IEventSimulator` interface, and the default implementation, `EventSimulator`, which by default
355+
calls `UioHook.PostEvent` to simulate the events.
291356

292357
### Text Entry Simulation
293358

@@ -332,6 +397,19 @@ var reactiveLogSource = new ReactiveLogSourceAdapter(logSource);
332397
reactiveLogSource.MessageLogged.Subscribe(this.OnMessageLogged);
333398
```
334399

400+
SharpHook.R3 contains the `IR3LogSource` and `R3LogSourceAdapter` so you can use them in a more reactive way as well:
401+
402+
```C#
403+
using SharpHook.Logging;
404+
using SharpHook.R3.Logging;
405+
406+
// ...
407+
408+
var logSource = LogSource.RegisterOrGet(minLevel: LogLevel.Info);
409+
var reactiveLogSource = new R3LogSourceAdapter(logSource);
410+
reactiveLogSource.MessageLogged.Subscribe(this.OnMessageLogged);
411+
```
412+
335413
### Testing
336414

337415
SharpHook provides two classes which make testing easier. They aren't required since mocks can be used instead, but
@@ -347,6 +425,9 @@ see which events were simulated using the test instance.
347425
If an `IReactiveGlobalHook` is needed for testing, then `ReactiveGlobalHookAdapter` can be used to adapt an instance of
348426
`TestGlobalHook`.
349427

428+
If an `IR3GlobalHook` is needed for testing, then `R3GlobalHookAdapter` can be used to adapt an instance of
429+
`TestGlobalHook`.
430+
350431
If the low-level functionality of SharpHook should be mocked, or mocking should be pushed as far away as possible,
351432
then `SharpHook.Testing.TestProvider` can be used. It implements every interface in the `SharpHook.Providers` namespace
352433
and as such it can be used instead of a normal low-level functionality provider.
@@ -402,7 +483,7 @@ Place the binaries into the appropriate directories in the `SharpHook` project,
402483
</tr>
403484
</table>
404485

405-
With libuiohook in place you can build SharpHook using your usual methods, e.g. with Visual Studio or the `dotnet` CLI.
486+
With libuiohook in place, you can build SharpHook using your usual methods, e.g. with Visual Studio or the `dotnet` CLI.
406487
You need .NET 9 to build SharpHook.
407488

408489
## Icon

SharpHook.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
</Folder>
1515
<Folder Name="/Docs/Articles/">
1616
<File Path="docs/articles/about.md" />
17+
<File Path="docs/articles/custom.md" />
1718
<File Path="docs/articles/hooks.md" />
1819
<File Path="docs/articles/keycodes.md" />
1920
<File Path="docs/articles/logging.md" />

SharpHook/Providers/UioHookProvider.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public UioHookResult Run() =>
134134
/// <returns>The result of the operation.</returns>
135135
/// <remarks>
136136
/// This method makes a difference only on Windows where there are two different global hooks – a keyboard hook and
137-
/// a mouse hook. On macOS and Linux there is one hook for all events, and this method simply filters mouse events
137+
/// a mouse hook. On macOS and Linux, there is one hook for all events, and this method simply filters mouse events
138138
/// out at the libuiohook level on these OSes.
139139
/// </remarks>
140140
public UioHookResult RunKeyboard() =>
@@ -146,7 +146,7 @@ public UioHookResult RunKeyboard() =>
146146
/// <returns>The result of the operation.</returns>
147147
/// <remarks>
148148
/// This method makes a difference only on Windows where there are two different global hooks – a keyboard hook and
149-
/// a mouse hook. On macOS and Linux there is one hook for all events, and this method simply filters keyboard
149+
/// a mouse hook. On macOS and Linux, there is one hook for all events, and this method simply filters keyboard
150150
/// events out at the libuiohook level on these OSes.
151151
/// </remarks>
152152
public UioHookResult RunMouse() =>
@@ -247,10 +247,10 @@ public UioHookResult PostEvent(ref UioHookEvent e) =>
247247
/// surrogate pairs, e.g. emojis) is supported.
248248
/// </para>
249249
/// <para>
250-
/// On Windows text simulation should work correctly and consistently.
250+
/// On Windows, text simulation should work correctly and consistently.
251251
/// </para>
252252
/// <para>
253-
/// On macOS applications are not required to process text simulation, but most of them should handle it correctly.
253+
/// On macOS, applications are not required to process text simulation, but most of them should handle it correctly.
254254
/// </para>
255255
/// <para>
256256
/// X11 doesn't support text simulation directly. Instead, for each character, an unused key code is remapped to

SharpHook/README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,10 @@ will be ignored since event handlers are run on another thread.
111111

112112
- `SharpHook.TaskPoolGlobalHook` runs all of its event handlers on other threads inside the default thread pool for
113113
tasks. The parallelism level of the handlers can be configured. On backpressure it will queue the remaining events which
114-
means that the hook will be able to process all events. This implementation should be preferred to
115-
`SimpleGlobalHook` except for very simple use-cases. But it has a downside – suppressing event propagation will be
116-
ignored since event handlers are run on other threads.
114+
means that the hook will be able to process all events. This implementation should be preferred to `SimpleGlobalHook`
115+
except for very simple use-cases. But it has a downside – suppressing event propagation will be ignored since event
116+
handlers are run on other threads. In general, `EventLoopGlobalHook` should be preferred instead, as this class provides
117+
benefits only if events should be processed in parallel, which is rarely the case.
117118

118119
The library also provides the `SharpHook.GlobalHookBase` class which you can extend to create your own implementation
119120
of the global hook. It calls the appropriate event handlers, and you only need to implement a strategy for dispatching
@@ -173,8 +174,8 @@ simulator.SimulateMouseWheel(
173174
type: MouseWheelScrollType.UnitScroll); // UnitScroll by default
174175
```
175176

176-
SharpHook provides the `IEventSimulator` interface, and the default implementation, `EventSimulator`, which calls
177-
`UioHook.PostEvent` to simulate the events.
177+
SharpHook provides the `IEventSimulator` interface, and the default implementation, `EventSimulator`, which by default
178+
calls `UioHook.PostEvent` to simulate the events.
178179

179180
### Text Entry Simulation
180181

SharpHook/SharpHook.xml

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

docs/articles/about.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,45 @@ Place the binaries into the appropriate directories in the `SharpHook` project,
5050
</tr>
5151
</table>
5252

53-
With libuiohook in place you can build SharpHook using your usual methods, e.g. with Visual Studio or the `dotnet` CLI.
53+
With libuiohook in place, you can build SharpHook using your usual methods, e.g. with Visual Studio or the `dotnet` CLI.
5454
You need .NET 9 to build SharpHook.
5555

5656
## Changelog
5757

58+
### [v7.0.0](https://github.com/TolikPylypchuk/SharpHook/releases/tag/v7.0.0) (August 10, 2025)
59+
60+
#### Breaking Changes
61+
62+
- `SimpleReactiveGlobalHook` was renamed to `ReactiveGlobalHook`.
63+
64+
- The `Run`, `RunAsync`, and `Stop` methods were moved from `IGlobalHook` and `IReactiveGlobalHook` into
65+
`IBasicGlobalHook` which both `IGlobalHook` and `IReactiveGlobalHook` now extend.
66+
67+
- Because of the previous change, `RunAsync` for reactive global hooks now returns a `Task` instead of an
68+
`IObservable<Unit>`.
69+
70+
- `ReactiveLogSourceAdapter` now contains a single constructor with a default parameter instead of two constructors.
71+
72+
#### New Features
73+
74+
- SharpHook.R3 – a new package for integration with R3 was added.
75+
76+
- `EventLoopGlobalHook` – a new implementation of `IGlobalHook` – was added.
77+
78+
- `IBasicGlobalHook` and `BasicGlobalHookBase` were added for easier creation of custom global hooks with different
79+
event forms.
80+
81+
- `ReactiveLogSourceAdapter` now implements `ILogSource` in addition to `IReactiveLogSource`.
82+
83+
#### Bug Fixes
84+
85+
- On Windows, global hooks now correctly report the key that was pressed or released on non-QWERTY layouts.
86+
87+
#### Other Changes
88+
89+
- libuiohook was updated to commit
90+
[a2cba5f](https://github.com/TolikPylypchuk/libuiohook/tree/a2cba5f125072dad8683e812027a561e598c7a37).
91+
5892
### [v6.2.0](https://github.com/TolikPylypchuk/SharpHook/releases/tag/v6.2.0) (July 19, 2025)
5993

6094
- The ability to disable events of type `KeyTyped` was added.

0 commit comments

Comments
 (0)