226 lines
8.9 KiB
C#
226 lines
8.9 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using Newtonsoft.Json.Linq;
|
|
using OBSBlur.Window;
|
|
using OBSWebsocketDotNet;
|
|
using OBSWebsocketDotNet.Communication;
|
|
using OBSWebsocketDotNet.Types;
|
|
using OBSWebsocketDotNet.Types.Events;
|
|
|
|
namespace OBSBlur.OBS;
|
|
|
|
public class Blur
|
|
{
|
|
private readonly WindowManager _windowManager = new ();
|
|
private readonly OBSWebsocket _websocket = new ();
|
|
private string _currentObsScene = "";
|
|
private readonly string _displayCaptureName;
|
|
// ReSharper disable once MemberCanBePrivate.Global
|
|
public readonly List<string> EnabledObsScenes = new();
|
|
// ReSharper disable once MemberCanBePrivate.Global
|
|
public readonly List<string> BlurPrograms = new();
|
|
// ReSharper disable once MemberCanBePrivate.Global
|
|
public readonly List<string> BlurWindows = new();
|
|
private readonly Dictionary<IntPtr, uint> _windowHandleSceneItems = new();
|
|
private readonly ILogger? _logger;
|
|
private double _scaleWidth = 1, _scaleHeight = 1, _xOffset, _yOffset;
|
|
|
|
public Blur(string obsUrl, string obsPassword, string[] enabledObsScenes, string[] blurPrograms, string[] blurWindows, string displayCaptureName, ILogger? logger = null)
|
|
{
|
|
this._logger = logger;
|
|
this.EnabledObsScenes.AddRange(enabledObsScenes);
|
|
this.BlurPrograms.AddRange(blurPrograms);
|
|
this.BlurWindows.AddRange(blurWindows);
|
|
this._displayCaptureName = displayCaptureName;
|
|
|
|
_websocket.Connected += WebsocketOnConnected;
|
|
_websocket.Disconnected += WebsocketOnDisconnected;
|
|
_websocket.SceneItemTransformChanged += WebsocketOnSceneItemTransformChanged;
|
|
_websocket.CurrentProgramSceneChanged += WebsocketOnCurrentProgramSceneChanged;
|
|
|
|
_websocket.ConnectAsync(obsUrl, obsPassword);
|
|
|
|
_windowManager.WindowsChanged += WindowManagerOnWindowsChanged;
|
|
_windowManager.ZOrderChanged += WindowManagerOnWindowZOrderChanged;
|
|
}
|
|
|
|
private void UpdateBlurs()
|
|
{
|
|
if (!EnabledObsScenes.Contains(_currentObsScene))
|
|
return;
|
|
if (!_websocket.IsConnected)
|
|
return;
|
|
WindowInfo[] windowInfos = _windowManager.WindowInfos;
|
|
IntPtr[] zOrder = _windowManager.WindowZOrder;
|
|
|
|
bool maximisedWindowReached = false;
|
|
|
|
foreach (IntPtr window in zOrder)
|
|
{
|
|
WindowInfo windowInfo = windowInfos.FirstOrDefault(w => w.WindowHandle == window);
|
|
if(windowInfo is {WindowHandle: 0x0})//No WindowInfo found
|
|
continue;
|
|
|
|
if (ShouldWindowBeBlurred(windowInfo))
|
|
{
|
|
if(!maximisedWindowReached)
|
|
BlurWindow(windowInfo);
|
|
else if(maximisedWindowReached)
|
|
DeleteBlur(windowInfo);
|
|
}
|
|
else
|
|
DeleteBlur(windowInfo);
|
|
|
|
if (windowInfo.WindowCommands is ShowWindowCommands.Maximize or ShowWindowCommands.ShowMaximized)
|
|
maximisedWindowReached = true;
|
|
}
|
|
|
|
foreach(IntPtr blurredWindow in _windowHandleSceneItems.Keys.ToArray())
|
|
if(windowInfos.All(w => w.WindowHandle != blurredWindow) || !ShouldWindowBeBlurred(windowInfos.First(w => w.WindowHandle == blurredWindow)))
|
|
DeleteBlur(blurredWindow);
|
|
}
|
|
|
|
private bool ShouldWindowBeBlurred(WindowInfo windowInfo)
|
|
{
|
|
foreach(string program in BlurPrograms)
|
|
if (windowInfo.ProcessInfo.ProcessName.Contains(program, StringComparison.OrdinalIgnoreCase))
|
|
return true;
|
|
foreach(string window in BlurWindows)
|
|
if (windowInfo.WindowTitle.Contains(window, StringComparison.OrdinalIgnoreCase))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
private void BlurWindow(WindowInfo windowInfo)
|
|
{
|
|
if(_windowHandleSceneItems.ContainsKey(windowInfo.WindowHandle))
|
|
MoveBlur(windowInfo);
|
|
else
|
|
AddBlur(windowInfo);
|
|
}
|
|
|
|
private void AddBlur(WindowInfo windowInfo)
|
|
{
|
|
int sceneItemId = _websocket.GetSceneItemId(_currentObsScene, "Blur", 0);
|
|
Dictionary<string, object> duplicateSceneItemRequest = new()
|
|
{
|
|
{"sceneName", _currentObsScene},
|
|
{"sceneItemId", sceneItemId}
|
|
};
|
|
try
|
|
{
|
|
JObject response = _websocket.SendRequest("DuplicateSceneItem", JObject.FromObject(duplicateSceneItemRequest));
|
|
if (!response.ContainsKey("sceneItemId"))
|
|
{
|
|
_logger?.LogWarning("Request DuplicateSceneItem: Response did not include 'sceneItemId'.");
|
|
return;
|
|
}
|
|
_windowHandleSceneItems.Add(windowInfo.WindowHandle, response["sceneItemId"]!.Value<uint>());
|
|
MoveBlur(windowInfo);
|
|
}
|
|
catch (ErrorResponseException e)
|
|
{
|
|
_logger?.LogError(e, "Request 'DuplicateSceneItem'");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private void MoveBlur(WindowInfo windowInfo)
|
|
{
|
|
if (!_windowHandleSceneItems.ContainsKey(windowInfo.WindowHandle))
|
|
return;
|
|
|
|
SceneItemTransformInfo info = new()
|
|
{
|
|
X = windowInfo.WindowRectangle.X * _scaleWidth + _xOffset,
|
|
Y = windowInfo.WindowRectangle.Y * _scaleHeight + _yOffset,
|
|
BoundsWidth = windowInfo.WindowRectangle.Width * _scaleWidth,
|
|
BoundsHeight = windowInfo.WindowRectangle.Height * _scaleHeight,
|
|
BoundsType = SceneItemBoundsType.OBS_BOUNDS_STRETCH,
|
|
Alignnment = 5
|
|
};
|
|
_websocket.SetSceneItemTransform(_currentObsScene, (int)_windowHandleSceneItems[windowInfo.WindowHandle], info);
|
|
_websocket.SetSceneItemEnabled(_currentObsScene, (int)_windowHandleSceneItems[windowInfo.WindowHandle], true);
|
|
}
|
|
|
|
private void DeleteBlur(IntPtr windowHandle)
|
|
{
|
|
if (!_windowHandleSceneItems.TryGetValue(windowHandle, out uint sceneItemId))
|
|
return;
|
|
_websocket.RemoveSceneItem(_currentObsScene, (int)sceneItemId);
|
|
_windowHandleSceneItems.Remove(windowHandle);
|
|
}
|
|
|
|
private void DeleteBlur(WindowInfo windowInfo)
|
|
{
|
|
DeleteBlur(windowInfo.WindowHandle);
|
|
}
|
|
|
|
private void SetScaleFactors(SceneItemTransformInfo? sceneItemTransformInfo = null)
|
|
{
|
|
int displayCapture = _websocket.GetSceneItemId(_currentObsScene, _displayCaptureName, 0);
|
|
sceneItemTransformInfo ??= _websocket.GetSceneItemTransform(_currentObsScene, displayCapture);
|
|
|
|
Rectangle desktopRectangle = WindowManager.GetDesktopRectangle();
|
|
this._scaleWidth = sceneItemTransformInfo.Width / desktopRectangle.Width;
|
|
this._scaleHeight = sceneItemTransformInfo.Height / desktopRectangle.Height;
|
|
this._xOffset = sceneItemTransformInfo.X;
|
|
this._yOffset = sceneItemTransformInfo.Y;
|
|
}
|
|
|
|
private void WindowManagerOnWindowZOrderChanged(IntPtr[] newOrder)
|
|
{
|
|
|
|
uint i = 0;
|
|
string print = $"Z-order changed\n{"Z",3} | {"hWnd",-10} | {"PID",-8} | {"Name",-17} | {"Window Title",-17} | {"State",-13} | BBox\n";
|
|
foreach (IntPtr windowHandle in newOrder)
|
|
{
|
|
WindowInfo windowInfo = _windowManager.WindowInfos.FirstOrDefault(w => w.WindowHandle == windowHandle);
|
|
if (windowInfo.WindowHandle is 0x0)
|
|
continue;
|
|
print += $"{++i,3} | {windowInfo}\n";
|
|
}
|
|
|
|
_logger?.LogInformation(print);
|
|
UpdateBlurs();
|
|
}
|
|
|
|
private void WindowManagerOnWindowsChanged(WindowInfo[] before, WindowInfo[] after)
|
|
{
|
|
string print = $"Window changed\n{"hWnd",-10} | {"PID",-8} | {"Name",-17} | {"Window Title",-17} | {"State",-13} | BBox\n";
|
|
foreach (WindowInfo windowInfo in after)
|
|
print += $"{windowInfo}\n";
|
|
_logger?.LogInformation(print);
|
|
UpdateBlurs();
|
|
}
|
|
|
|
private void WebsocketOnDisconnected(object? sender, ObsDisconnectionInfo e)
|
|
{
|
|
_logger?.LogInformation("Obs Disconnected");
|
|
}
|
|
|
|
private void WebsocketOnConnected(object? sender, EventArgs e)
|
|
{
|
|
_currentObsScene = _websocket.GetCurrentProgramScene();
|
|
_logger?.LogInformation($"Obs Connected. Current Scene '{_currentObsScene}'");
|
|
SetScaleFactors();
|
|
_logger?.LogInformation($"Scale-Factors: Width={this._scaleWidth:F} Height={this._scaleHeight:F}");
|
|
UpdateBlurs();
|
|
}
|
|
|
|
private void WebsocketOnCurrentProgramSceneChanged(object? sender, ProgramSceneChangedEventArgs e)
|
|
{
|
|
_currentObsScene = e.SceneName;
|
|
_logger?.LogInformation($"Obs Scene Changed -> '{_currentObsScene}'");
|
|
UpdateBlurs();
|
|
}
|
|
|
|
private void WebsocketOnSceneItemTransformChanged(object? sender, SceneItemTransformEventArgs e)
|
|
{
|
|
_logger?.LogInformation("Obs SceneItemTransform Changed");
|
|
if (!EnabledObsScenes.Contains(e.SceneName))
|
|
return;
|
|
if (_websocket.GetSceneItemId(_currentObsScene, _displayCaptureName, 0) != uint.Parse(e.SceneItemId))
|
|
return;
|
|
SetScaleFactors(e.Transform);
|
|
}
|
|
} |