Public Access
Make meeting lifecycle stateful
This commit is contained in:
@@ -1,27 +1,28 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using MeetingAssistant.LaunchProfiles;
|
||||
using MeetingAssistant.Recording;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace MeetingAssistant.Hotkeys;
|
||||
|
||||
public sealed class GlobalHotkeyService : BackgroundService
|
||||
{
|
||||
private const int HotkeyId = 0x4D41;
|
||||
private const int HotkeyIdBase = 0x4D41;
|
||||
private const int WmHotkey = 0x0312;
|
||||
private const int WmQuit = 0x0012;
|
||||
private readonly MeetingAssistantOptions options;
|
||||
private readonly ILaunchProfileOptionsProvider launchProfiles;
|
||||
private readonly MeetingRecordingCoordinator coordinator;
|
||||
private readonly ILogger<GlobalHotkeyService> logger;
|
||||
private readonly Dictionary<int, string> hotkeyProfiles = [];
|
||||
private TaskCompletionSource? messageLoopCompletion;
|
||||
private uint messageThreadId;
|
||||
|
||||
public GlobalHotkeyService(
|
||||
IOptions<MeetingAssistantOptions> options,
|
||||
ILaunchProfileOptionsProvider launchProfiles,
|
||||
MeetingRecordingCoordinator coordinator,
|
||||
ILogger<GlobalHotkeyService> logger)
|
||||
{
|
||||
this.options = options.Value;
|
||||
this.launchProfiles = launchProfiles;
|
||||
this.coordinator = coordinator;
|
||||
this.logger = logger;
|
||||
}
|
||||
@@ -58,31 +59,49 @@ public sealed class GlobalHotkeyService : BackgroundService
|
||||
|
||||
private void RunMessageLoop(CancellationToken stoppingToken)
|
||||
{
|
||||
var hotkey = HotkeyDefinition.Parse(options.Hotkey.Toggle);
|
||||
var hotkeys = launchProfiles.GetHotkeys();
|
||||
messageThreadId = GetCurrentThreadId();
|
||||
using var stoppingRegistration = stoppingToken.Register(() => PostQuitToMessageThread());
|
||||
|
||||
if (!RegisterHotKey(IntPtr.Zero, HotkeyId, (uint)hotkey.Modifiers, hotkey.VirtualKey))
|
||||
for (var index = 0; index < hotkeys.Count; index++)
|
||||
{
|
||||
logger.LogWarning("Could not register global hotkey {Hotkey}", options.Hotkey.Toggle);
|
||||
return;
|
||||
}
|
||||
var profileHotkey = hotkeys[index];
|
||||
var hotkey = HotkeyDefinition.Parse(profileHotkey.Toggle);
|
||||
var hotkeyId = HotkeyIdBase + index;
|
||||
if (!RegisterHotKey(IntPtr.Zero, hotkeyId, (uint)hotkey.Modifiers, hotkey.VirtualKey))
|
||||
{
|
||||
logger.LogWarning(
|
||||
"Could not register global hotkey {Hotkey} for launch profile {LaunchProfile}",
|
||||
profileHotkey.Toggle,
|
||||
profileHotkey.ProfileName);
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.LogInformation("Registered global hotkey {Hotkey}", options.Hotkey.Toggle);
|
||||
hotkeyProfiles[hotkeyId] = profileHotkey.ProfileName;
|
||||
logger.LogInformation(
|
||||
"Registered global hotkey {Hotkey} for launch profile {LaunchProfile}",
|
||||
profileHotkey.Toggle,
|
||||
profileHotkey.ProfileName);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested && GetMessage(out var message, IntPtr.Zero, 0, 0) > 0)
|
||||
{
|
||||
if (message.Message == WmHotkey)
|
||||
if (message.Message == WmHotkey && hotkeyProfiles.TryGetValue((int)message.WParam, out var profileName))
|
||||
{
|
||||
_ = Task.Run(ToggleRecordingAsync, CancellationToken.None);
|
||||
_ = Task.Run(() => ToggleRecordingAsync(profileName), CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnregisterHotKey(IntPtr.Zero, HotkeyId);
|
||||
foreach (var hotkeyId in hotkeyProfiles.Keys)
|
||||
{
|
||||
UnregisterHotKey(IntPtr.Zero, hotkeyId);
|
||||
}
|
||||
|
||||
hotkeyProfiles.Clear();
|
||||
messageThreadId = 0;
|
||||
}
|
||||
}
|
||||
@@ -93,12 +112,15 @@ public sealed class GlobalHotkeyService : BackgroundService
|
||||
return base.StopAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task ToggleRecordingAsync()
|
||||
private async Task ToggleRecordingAsync(string profileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var status = await coordinator.ToggleAsync(CancellationToken.None);
|
||||
logger.LogInformation("Hotkey toggled recording. IsRecording={IsRecording}", status.IsRecording);
|
||||
var status = await coordinator.ToggleAsync(profileName, CancellationToken.None);
|
||||
logger.LogInformation(
|
||||
"Hotkey toggled recording for launch profile {LaunchProfile}. IsRecording={IsRecording}",
|
||||
profileName,
|
||||
status.IsRecording);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user