This repository was archived by the owner on Aug 3, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
35 changed files
with
5,968 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
using Newtonsoft.Json; | ||
using WlxOverlay.Numerics; | ||
using WlxOverlay.Overlays; | ||
using WlxOverlay.Types; | ||
|
||
namespace WlxOverlay.Core; | ||
|
||
public class ChaperoneManager | ||
{ | ||
public static readonly ChaperoneManager Instance = new(); | ||
|
||
public bool WantVisible; | ||
public float MaxAlpha = 1f; | ||
public float FadeDistance = 5f; | ||
public List<ChaperonePolygon> Polygons = new(); | ||
|
||
private readonly List<ChaperoneLine> _chaperoneLines = new(); | ||
|
||
public void Render() | ||
{ | ||
var distance = HmdDistanceToChaperoneEdge(); | ||
var alpha = Mathf.Clamp(1 - (distance - FadeDistance) / FadeDistance, 0, 1) * MaxAlpha; | ||
|
||
//foreach (var line in ChaperoneLines) | ||
// line.Alpha = alpha; | ||
} | ||
|
||
public void PolygonsChanged() | ||
{ | ||
var j = 0; | ||
foreach (var polygon in Polygons) | ||
{ | ||
var maxIdx = polygon.Points.Count - 1; | ||
for (var i = 0; i < maxIdx; i++) | ||
{ | ||
var start = polygon.Points[i]; | ||
var end = polygon.Points[i + 1]; | ||
|
||
ChaperoneLine line; | ||
if (j >= _chaperoneLines.Count) | ||
{ | ||
_chaperoneLines.Add(line = new ChaperoneLine { WantVisible = true }); | ||
OverlayManager.Instance.RegisterChild(line); | ||
j++; | ||
} | ||
else | ||
line = _chaperoneLines[j++]; | ||
|
||
line.SetPoints(start, end); | ||
line.Color = polygon.Color; | ||
} | ||
} | ||
for (; j < _chaperoneLines.Count; j++) | ||
{ | ||
var lastIdx = _chaperoneLines.Count - 1; | ||
var chap = _chaperoneLines[lastIdx]; | ||
_chaperoneLines.RemoveAt(lastIdx); | ||
chap.Dispose(); | ||
} | ||
} | ||
|
||
public float HmdDistanceToChaperoneEdge() | ||
{ | ||
return _chaperoneLines.Count < 1 | ||
? float.MaxValue | ||
: _chaperoneLines.Min(x => x.DistanceTo(InputManager.HmdTransform.origin)); | ||
} | ||
|
||
private Transform3D ReferenceTransform() | ||
{ | ||
var pLeft = InputManager.PoseState["LeftHand"]; | ||
var pRight = InputManager.PoseState["RightHand"]; | ||
|
||
var baseTransform = Transform3D.Identity; | ||
baseTransform.origin = (pLeft.origin + pRight.origin) / 2; | ||
return baseTransform.LookingAt(pRight.origin, Vector3.Up); | ||
} | ||
|
||
public void LoadFromFile() | ||
{ | ||
if (!Config.TryGetFile("chaperone.json", out var path)) | ||
return; | ||
|
||
var transform = ReferenceTransform(); | ||
|
||
try | ||
{ | ||
var conf = JsonConvert.DeserializeObject<ChaperoneConfig>(File.ReadAllText(path)); | ||
FadeDistance = conf!.FadeDistance; | ||
MaxAlpha = conf!.MaxAlpha; | ||
WantVisible = conf!.Visible; | ||
|
||
Polygons.Clear(); | ||
if (conf.Polygons != null) | ||
{ | ||
foreach (var poly in conf.Polygons) | ||
{ | ||
poly.Apply(transform); | ||
Polygons.Add(poly); | ||
} | ||
} | ||
PolygonsChanged(); | ||
} | ||
catch { /* */ } | ||
} | ||
|
||
public void SaveToFile() | ||
{ | ||
var path = Path.Combine(Config.ConfigFolders.First(), "chaperone.json"); | ||
|
||
var transform = ReferenceTransform().Inverse(); | ||
var savedPolygons = new List<ChaperonePolygon>(); | ||
foreach (var poly in Polygons) | ||
{ | ||
var newPoly = new ChaperonePolygon | ||
{ | ||
Color = poly.Color, | ||
Points = poly.Points.ToList() | ||
}; | ||
newPoly.Apply(transform); | ||
savedPolygons.Add(newPoly); | ||
} | ||
|
||
var json = JsonConvert.SerializeObject(new ChaperoneConfig | ||
{ | ||
Visible = WantVisible, | ||
Polygons = savedPolygons, | ||
MaxAlpha = MaxAlpha, | ||
FadeDistance = FadeDistance | ||
}, Formatting.Indented); | ||
File.WriteAllText(path, json); | ||
} | ||
|
||
private struct ChaperoneConfig | ||
{ | ||
public bool Visible; | ||
public float FadeDistance; | ||
public float MaxAlpha; | ||
public List<ChaperonePolygon>? Polygons; | ||
} | ||
} | ||
|
||
public class ChaperonePolygon | ||
{ | ||
public Vector3 Color; | ||
public List<Vector3> Points = new(); | ||
|
||
public void Apply(Transform3D transform) | ||
{ | ||
Points = Points.Select(x => transform * x).ToList(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
using System.Net; | ||
using System.Net.Sockets; | ||
using System.Text; | ||
using Newtonsoft.Json; | ||
using WlxOverlay.Overlays; | ||
using WlxOverlay.Types; | ||
|
||
namespace WlxOverlay.Core; | ||
|
||
public class NotificationsManager : IDisposable | ||
{ | ||
private static NotificationsManager _instance = null!; | ||
|
||
private readonly IPEndPoint _listenEndpoint; | ||
private readonly Socket _listenSocket; | ||
private readonly byte[] _listenBuffer = new byte[1024*16]; | ||
|
||
private readonly CancellationTokenSource _cancel = new(); | ||
private Task? _listener; | ||
private Task? _notifier; | ||
|
||
private readonly object _lockObject = new(); | ||
private readonly Queue<XSOMessage> _messages = new(10); | ||
|
||
private DateTime _nextToast = DateTime.MinValue; | ||
|
||
public static void Initialize() | ||
{ | ||
_instance = new NotificationsManager(); | ||
_instance.Start(); | ||
} | ||
|
||
public static void Toast(string title, string content, float timeout = 10) | ||
{ | ||
_instance._messages.Enqueue(new XSOMessage | ||
{ | ||
timeout = timeout, | ||
height = 110f, | ||
messageType = 1, | ||
content = content, | ||
title = title, | ||
opacity = 1 | ||
}); | ||
} | ||
|
||
private NotificationsManager() | ||
{ | ||
try | ||
{ | ||
_listenEndpoint = IPEndPoint.Parse(Config.Instance.NotificationsEndpoint); | ||
} | ||
catch | ||
{ | ||
Console.WriteLine("FATAL: Could not parse config.yaml entry: notifications_endpoint"); | ||
throw; | ||
} | ||
_listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); | ||
} | ||
|
||
private void Start() | ||
{ | ||
_listener = ListenerAsync(_cancel.Token); | ||
_notifier = NotifierAsync(_cancel.Token); | ||
} | ||
|
||
private async Task ListenerAsync(CancellationToken _cancellationToken) | ||
{ | ||
try | ||
{ | ||
try | ||
{ | ||
_listenSocket.Bind(_listenEndpoint); | ||
Console.WriteLine($"Listening for notifications @ {_listenEndpoint.Address}:{_listenEndpoint.Port}"); | ||
} | ||
catch | ||
{ | ||
Console.WriteLine($"Could not listen for notifications @ {_listenEndpoint.Address}:{_listenEndpoint.Port}"); | ||
throw; | ||
} | ||
var remoteEp = new IPEndPoint(IPAddress.Any, 0); | ||
while (_listenSocket.IsBound && !_cancellationToken.IsCancellationRequested) | ||
{ | ||
try | ||
{ | ||
var result = await _listenSocket.ReceiveFromAsync(_listenBuffer, SocketFlags.None, remoteEp); | ||
var receivedBytes = new ArraySegment<byte>(_listenBuffer, 0, result.ReceivedBytes); | ||
BytesReceived(receivedBytes); | ||
} | ||
catch (Exception x) | ||
{ | ||
Console.WriteLine(x.ToString()); | ||
} | ||
} | ||
} | ||
catch (Exception x) | ||
{ | ||
Console.WriteLine(x); | ||
} | ||
} | ||
|
||
private void BytesReceived(ArraySegment<byte> bytes) | ||
{ | ||
var json = Encoding.UTF8.GetString(bytes); | ||
var message = JsonConvert.DeserializeObject<XSOMessage>(json); | ||
|
||
if ( message.messageType == 1) | ||
lock (_lockObject) | ||
_messages.Enqueue(message); | ||
} | ||
|
||
private async Task NotifierAsync(CancellationToken _cancellationToken) | ||
{ | ||
while (!_cancellationToken.IsCancellationRequested) | ||
{ | ||
await Task.Delay(100, _cancellationToken); | ||
|
||
XSOMessage message; | ||
lock (_lockObject) | ||
if (!_messages.TryDequeue(out message)) | ||
continue; | ||
|
||
var toast = new Toast(message.title, message.content, message.opacity, (uint)message.height, message.timeout); | ||
OverlayManager.Instance.RegisterChild(toast); | ||
_nextToast = DateTime.UtcNow.AddSeconds(2); | ||
} | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
_cancel.Cancel(); | ||
|
||
_listener?.Dispose(); | ||
_notifier?.Dispose(); | ||
_cancel.Dispose(); | ||
_listenSocket.Dispose(); | ||
} | ||
} | ||
|
||
// ReSharper disable InconsistentNaming | ||
// ReSharper disable UnusedAutoPropertyAccessor.Global | ||
public struct XSOMessage | ||
{ | ||
public int messageType { get; set; } | ||
public int index { get; set; } | ||
public float volume { get; set; } | ||
public string audioPath { get; set; } | ||
public float timeout { get; set; } | ||
public string title { get; set; } | ||
public string? content { get; set; } | ||
public string? icon { get; set; } | ||
public float height { get; set; } | ||
public float opacity { get; set; } | ||
public bool useBase64Icon { get; set; } | ||
public string? sourceApp { get; set; } | ||
} |
Oops, something went wrong.