SwyxTray und Firefox-Plugin in das Repository aufnehmen

Windows-Tray-Anwendung (CLMgr-Anbindung an SwyxIt!) samt WebSocket-Zugang,
Firefox-Erweiterung und Browser-Beispielclient.
This commit is contained in:
2026-08-21 11:33:52 +02:00
commit 78b17a1350
28 changed files with 4396 additions and 0 deletions
+90
View File
@@ -0,0 +1,90 @@
using System.Drawing.Drawing2D;
using SwyxTray.Swyx;
namespace SwyxTray;
/// <summary>
/// Erzeugt die Symbole zur Laufzeit (Kreis mit Hoererbogen), damit keine
/// .ico-Dateien mitgeliefert werden muessen. Wer eigene Symbole verwenden
/// will, ersetzt einfach <see cref="For"/> durch das Laden aus Ressourcen.
///
/// Die Symbole werden einmal je Zustand erzeugt und zwischengespeichert —
/// GDI-Handles sind eine begrenzte Ressource.
/// </summary>
internal static class TrayIcons
{
private static readonly Dictionary<SwyxOverallState, Icon> Cache = new();
private static readonly Dictionary<SwyxOverallState, Color> Colors = new()
{
[SwyxOverallState.Offline] = Color.FromArgb(0x9E, 0x9E, 0x9E), // grau
[SwyxOverallState.ServerDown] = Color.FromArgb(0xE5, 0x39, 0x35), // rot
[SwyxOverallState.Idle] = Color.FromArgb(0x43, 0xA0, 0x47), // gruen
[SwyxOverallState.Ringing] = Color.FromArgb(0x1E, 0x88, 0xE5), // blau
[SwyxOverallState.Dialing] = Color.FromArgb(0x00, 0xAC, 0xC1), // tuerkis
[SwyxOverallState.InCall] = Color.FromArgb(0xFB, 0x8C, 0x00), // orange
[SwyxOverallState.OnHold] = Color.FromArgb(0x8E, 0x24, 0xAA) // violett
};
public static Icon For(SwyxOverallState state)
{
if (Cache.TryGetValue(state, out var cached))
{
return cached;
}
var icon = Render(Colors.TryGetValue(state, out var c) ? c : Color.Gray);
Cache[state] = icon;
return icon;
}
private static Icon Render(Color color)
{
// 32x32 zeichnen und Windows herunterskalieren lassen — sieht auf
// hochaufloesenden Anzeigen besser aus als 16x16.
const int size = 32;
using var bitmap = new Bitmap(size, size);
using (var g = Graphics.FromImage(bitmap))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.Clear(Color.Transparent);
using (var brush = new SolidBrush(color))
{
g.FillEllipse(brush, 1, 1, size - 3, size - 3);
}
// Hoerer: ein dicker Bogen mit runden Enden liest sich bei kleiner
// Darstellung zuverlaessiger als eine ausmodellierte Silhouette.
using var pen = new Pen(Color.White, 5f)
{
StartCap = LineCap.Round,
EndCap = LineCap.Round
};
g.DrawArc(pen, 9f, 9f, 14f, 14f, 130f, 200f);
}
// Icon.FromHandle borgt sich das Handle nur; ueber Clone entsteht eine
// eigenstaendige Kopie, damit das HICON sofort freigegeben werden kann.
var hIcon = bitmap.GetHicon();
try
{
using var borrowed = Icon.FromHandle(hIcon);
return (Icon)borrowed.Clone();
}
finally
{
NativeMethods.DestroyIcon(hIcon);
}
}
public static void DisposeAll()
{
foreach (var icon in Cache.Values)
{
icon.Dispose();
}
Cache.Clear();
}
}