mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-24 09:13:50 +02:00
Flash taskbar icon on 2FA prompt
This commit is contained in:
153
AppApi.cs
153
AppApi.cs
@@ -4,11 +4,6 @@
|
|||||||
// This work is licensed under the terms of the MIT license.
|
// This work is licensed under the terms of the MIT license.
|
||||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
using CefSharp;
|
|
||||||
using librsync.net;
|
|
||||||
using Microsoft.Win32;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -23,8 +18,12 @@ using System.Text;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Windows.Data.Xml.Dom;
|
|
||||||
using Windows.UI.Notifications;
|
using Windows.UI.Notifications;
|
||||||
|
using CefSharp;
|
||||||
|
using librsync.net;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace VRCX
|
namespace VRCX
|
||||||
{
|
{
|
||||||
@@ -32,6 +31,8 @@ namespace VRCX
|
|||||||
{
|
{
|
||||||
public static readonly AppApi Instance;
|
public static readonly AppApi Instance;
|
||||||
|
|
||||||
|
private static readonly MD5 _hasher = MD5.Create();
|
||||||
|
|
||||||
static AppApi()
|
static AppApi()
|
||||||
{
|
{
|
||||||
Instance = new AppApi();
|
Instance = new AppApi();
|
||||||
@@ -39,24 +40,24 @@ namespace VRCX
|
|||||||
|
|
||||||
public string MD5File(string Blob)
|
public string MD5File(string Blob)
|
||||||
{
|
{
|
||||||
byte[] fileData = Convert.FromBase64CharArray(Blob.ToCharArray(), 0, Blob.Length);
|
var fileData = Convert.FromBase64CharArray(Blob.ToCharArray(), 0, Blob.Length);
|
||||||
byte[] md5 = MD5.Create().ComputeHash(fileData);
|
var md5 = MD5.Create().ComputeHash(fileData);
|
||||||
return System.Convert.ToBase64String(md5);
|
return Convert.ToBase64String(md5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SignFile(string Blob)
|
public string SignFile(string Blob)
|
||||||
{
|
{
|
||||||
byte[] fileData = Convert.FromBase64CharArray(Blob.ToCharArray(), 0, Blob.Length);
|
var fileData = Convert.FromBase64CharArray(Blob.ToCharArray(), 0, Blob.Length);
|
||||||
Stream sig = Librsync.ComputeSignature(new MemoryStream(fileData));
|
var sig = Librsync.ComputeSignature(new MemoryStream(fileData));
|
||||||
var memoryStream = new MemoryStream();
|
var memoryStream = new MemoryStream();
|
||||||
sig.CopyTo(memoryStream);
|
sig.CopyTo(memoryStream);
|
||||||
byte[] sigBytes = memoryStream.ToArray();
|
var sigBytes = memoryStream.ToArray();
|
||||||
return System.Convert.ToBase64String(sigBytes);
|
return Convert.ToBase64String(sigBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string FileLength(string Blob)
|
public string FileLength(string Blob)
|
||||||
{
|
{
|
||||||
byte[] fileData = Convert.FromBase64CharArray(Blob.ToCharArray(), 0, Blob.Length);
|
var fileData = Convert.FromBase64CharArray(Blob.ToCharArray(), 0, Blob.Length);
|
||||||
return fileData.Length.ToString();
|
return fileData.Length.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +69,7 @@ namespace VRCX
|
|||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
var json = File.ReadAllText(configFile);
|
var json = File.ReadAllText(configFile);
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
@@ -94,6 +96,7 @@ namespace VRCX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var cachePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"Low\VRChat\VRChat";
|
var cachePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"Low\VRChat\VRChat";
|
||||||
return cachePath;
|
return cachePath;
|
||||||
}
|
}
|
||||||
@@ -122,12 +125,13 @@ namespace VRCX
|
|||||||
{
|
{
|
||||||
isGameRunning = true;
|
isGameRunning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Process.GetProcessesByName("vrserver").Length > 0)
|
if (Process.GetProcessesByName("vrserver").Length > 0)
|
||||||
{
|
{
|
||||||
isSteamVRRunning = true;
|
isSteamVRRunning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new bool[]
|
return new[]
|
||||||
{
|
{
|
||||||
isGameRunning,
|
isGameRunning,
|
||||||
isSteamVRRunning
|
isSteamVRRunning
|
||||||
@@ -152,7 +156,7 @@ namespace VRCX
|
|||||||
{
|
{
|
||||||
// "C:\Program Files (x86)\Steam\steam.exe" -- "%1"
|
// "C:\Program Files (x86)\Steam\steam.exe" -- "%1"
|
||||||
var match = Regex.Match(key.GetValue(string.Empty) as string, "^\"(.+?)\\\\steam.exe\"");
|
var match = Regex.Match(key.GetValue(string.Empty) as string, "^\"(.+?)\\\\steam.exe\"");
|
||||||
if (match.Success == true)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
var path = match.Groups[1].Value;
|
var path = match.Groups[1].Value;
|
||||||
// var _arguments = Uri.EscapeDataString(arguments);
|
// var _arguments = Uri.EscapeDataString(arguments);
|
||||||
@@ -178,7 +182,7 @@ namespace VRCX
|
|||||||
{
|
{
|
||||||
// "C:\Program Files (x86)\Steam\steamapps\common\VRChat\launch.exe" "%1" %*
|
// "C:\Program Files (x86)\Steam\steamapps\common\VRChat\launch.exe" "%1" %*
|
||||||
var match = Regex.Match(key.GetValue(string.Empty) as string, "(?!\")(.+?\\\\VRChat.*)(!?\\\\launch.exe\")");
|
var match = Regex.Match(key.GetValue(string.Empty) as string, "(?!\")(.+?\\\\VRChat.*)(!?\\\\launch.exe\")");
|
||||||
if (match.Success == true)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
var path = match.Groups[1].Value;
|
var path = match.Groups[1].Value;
|
||||||
StartGameFromPath(path, arguments);
|
StartGameFromPath(path, arguments);
|
||||||
@@ -206,8 +210,8 @@ namespace VRCX
|
|||||||
|
|
||||||
public void OpenLink(string url)
|
public void OpenLink(string url)
|
||||||
{
|
{
|
||||||
if (url.StartsWith("http://") == true ||
|
if (url.StartsWith("http://") ||
|
||||||
url.StartsWith("https://") == true)
|
url.StartsWith("https://"))
|
||||||
{
|
{
|
||||||
Process.Start(url).Close();
|
Process.Start(url).Close();
|
||||||
}
|
}
|
||||||
@@ -257,45 +261,31 @@ namespace VRCX
|
|||||||
|
|
||||||
public void DesktopNotification(string BoldText, string Text = "", string Image = "")
|
public void DesktopNotification(string BoldText, string Text = "", string Image = "")
|
||||||
{
|
{
|
||||||
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText02);
|
var toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText02);
|
||||||
XmlNodeList stringElements = toastXml.GetElementsByTagName("text");
|
var stringElements = toastXml.GetElementsByTagName("text");
|
||||||
String imagePath = Path.Combine(Program.BaseDirectory, "VRCX.ico");
|
var imagePath = Path.Combine(Program.BaseDirectory, "VRCX.ico");
|
||||||
if (!String.IsNullOrEmpty(Image))
|
if (!string.IsNullOrEmpty(Image))
|
||||||
{
|
{
|
||||||
imagePath = Image;
|
imagePath = Image;
|
||||||
}
|
}
|
||||||
|
|
||||||
stringElements[0].AppendChild(toastXml.CreateTextNode(BoldText));
|
stringElements[0].AppendChild(toastXml.CreateTextNode(BoldText));
|
||||||
stringElements[1].AppendChild(toastXml.CreateTextNode(Text));
|
stringElements[1].AppendChild(toastXml.CreateTextNode(Text));
|
||||||
XmlNodeList imageElements = toastXml.GetElementsByTagName("image");
|
var imageElements = toastXml.GetElementsByTagName("image");
|
||||||
imageElements[0].Attributes.GetNamedItem("src").NodeValue = imagePath;
|
imageElements[0].Attributes.GetNamedItem("src").NodeValue = imagePath;
|
||||||
ToastNotification toast = new ToastNotification(toastXml);
|
var toast = new ToastNotification(toastXml);
|
||||||
ToastNotificationManager.CreateToastNotifier("VRCX").Show(toast);
|
ToastNotificationManager.CreateToastNotifier("VRCX").Show(toast);
|
||||||
}
|
}
|
||||||
|
|
||||||
private 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; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void XSNotification(string Title, string Content, int Timeout, string Image = "")
|
public void XSNotification(string Title, string Content, int Timeout, string Image = "")
|
||||||
{
|
{
|
||||||
bool UseBase64Icon;
|
bool UseBase64Icon;
|
||||||
string Icon;
|
string Icon;
|
||||||
if (String.IsNullOrEmpty(Image))
|
if (string.IsNullOrEmpty(Image))
|
||||||
{
|
{
|
||||||
UseBase64Icon = true;
|
UseBase64Icon = true;
|
||||||
Icon = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHaGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo1NTE2MWIyMi1hYzgxLTY3NDYtODAyYi1kODIzYWFmN2RjYjciIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ZjJjNTA2ZS02YTVhLWRhNGEtOTg5Mi02NDZiMzQ0MGQxZTgiPiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT5hZG9iZTpkb2NpZDpwaG90b3Nob3A6NmJmOGE5MTgtY2QzZS03OTRjLTk3NzktMzM0YjYwZWJiNTYyPC9yZGY6bGk+IDwvcmRmOkJhZz4gPC9waG90b3Nob3A6RG9jdW1lbnRBbmNlc3RvcnM+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6N2YyYzUwNmUtNmE1YS1kYTRhLTk4OTItNjQ2YjM0NDBkMWU4IiBzdEV2dDp3aGVuPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhM2ZjODI3LTM0ZjQtYjU0OC05ZGFiLTZhMTZlZmQzZjAxMSIgc3RFdnQ6d2hlbj0iMjAyMS0wNC0wOFQxNTowMTozMSsxMjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHN0RXZ0OndoZW49IjIwMjEtMDQtMDhUMTY6MzM6MTArMTI6MDAiIHN0RXZ0OnNv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";
|
Icon =
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHaGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo1NTE2MWIyMi1hYzgxLTY3NDYtODAyYi1kODIzYWFmN2RjYjciIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ZjJjNTA2ZS02YTVhLWRhNGEtOTg5Mi02NDZiMzQ0MGQxZTgiPiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT5hZG9iZTpkb2NpZDpwaG90b3Nob3A6NmJmOGE5MTgtY2QzZS03OTRjLTk3NzktMzM0YjYwZWJiNTYyPC9yZGY6bGk+IDwvcmRmOkJhZz4gPC9waG90b3Nob3A6RG9jdW1lbnRBbmNlc3RvcnM+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6N2YyYzUwNmUtNmE1YS1kYTRhLTk4OTItNjQ2YjM0NDBkMWU4IiBzdEV2dDp3aGVuPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhM2ZjODI3LTM0ZjQtYjU0OC05ZGFiLTZhMTZlZmQzZjAxMSIgc3RFdnQ6d2hlbj0iMjAyMS0wNC0wOFQxNTowMTozMSsxMjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHN0RXZ0OndoZW49IjIwMjEtMDQtMDhUMTY6MzM6MTArMTI6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4XAd9sAAAFM0lEQVR42u2aWUhjVxjHjVpf3Iraoh3c4ksFx7ZYahV8EHEBqdQHFdsHQRRxpcyDIDNFpdSK+iBKUcTpmy/iglVrtT4oYsEq7hP3RGXcqqY6invy9Xy3OdPEE5PY5pKb5P7hTyA5y/1+Ofc7y70OAOBgz3YQAYgARAAiABGACEAEIAIQAYgADBT6V4HErcRbxCAwy4nriN/DC+UDADb8swADv++fiN3MDeAJ8be0k9HRUbi4uACUWq22qFFvzt5AZ1enNoSvzJ4DiJ5j412dXSBUVf9QTQH08gHgF2x8b2/P0nGqNGa0ML9AAazyAeA3bPzg4MDoFV5fX8PZ2RlcXl7qGL83JjKsVeT2UpHyaqxzdXXFtUVvOVpMYx3JFfK3CZEPAL9i4/v7+0aDwDL5+fmQl5cHBQUFnHNzc6GsrAzW19cNBQ8dHR3q7OxsFamvxnrFxcWQnp4O4+PjRvtdW1ujANYtCgBVWlqqN0vn5ORw/6o+TU1Nga+vL1MnMTERtre3rQvA3d0dZGZmMsG4ublBW1sbU/7k5ATi4+OZ8uHh4bC5uWlSn4ICQC/I39+fCSo0NBRWV1d1M3h1NVPOw8MDenp6HtWfoACg8N92dnZmgisqKuISI2pkZAS8vLyYMngb3dzcWDcAvBUKCwuZ4FxdXWFwcJDLB1FRUczvcXFxcHx8/Ki+BAkAtbW1BZGRkUyQsbGx3Gzh5OSk831QUJBJWd9qAKD6+/vB29tbJ1CJRMIE7+7uDk1NTf+pD0EDwFuhoqKCC9rQZiYrKwtub29tDwBqZ2cHUlNTwdHRkQkcwURHRxtcKFk9ANTAwAB4enoyAHCmqKys/F9tCx4ATnuY9B4a/mFhYTA3N2e7AFpaWoweaKSkpHCbH5sDMDMzw01vxgC4uLhAfX29oAHo3Yoa0vn5OSQnJzPBZmRkQFpaGjMz+Pn5wdjYmGAB3D0WQG1tLRM8Bjk7OwsKhQICAwOZ3xMSEkw6e7AEANVjAAwPD3ObmvsBVlVVgUr1z8FOQ0MD8zsukMrLyx+1JhBcDtjd3YWIiAgmOLwdtP9dTHpJSUl6d4M4bVolADzdKSkpYYIKCAjgdn/3NT8/Dz4+Pkz5mJgYkw5DBAUAh3ZzczOzDcYVYE1NzYNL5bq6Or1LZVw7nJ6eWg8APMHBRQ0ehkilUggODuaSHp4QGdriHh0dcTMDlsV6ISEhXF0cGb29vRYHMGTqqTCWmZiYgKWlJVheXgaZTMatAw4PD43WVSqVMD09zdVD48kRtiWXy98mzYe0Id+gADb4ADCMjSuPlYJ9MKLYVFAAm3wAaMbGFxcXBQugu7ubAviDDwCfY+N4Ro/DVGjCmUIrcX7P1+PxfdpJ68tWGBoagr7+PrMZH3DiwglnBGPCtQOWxeSIM45W8IvEUr4AfEG8xPcj7sbGRqMAVpZX9NWdIv6Ur/cDqD4k/o64j/h34jEzeUTTHhdMX2+fQQCyVzIa9KXmwe0z4hB6kXwCQL2DLyEQ+xK/byZ7EfsRN1AICwsLDwLAKVZTDkfkZ8RO2hfINwA+9YQ+iUYf/nloDADe80/vN2LNAFCRxGsYx4vnL/QmRS0Ar4g/sjUAqC/pKGhvb7dLAKhyCmFyctIuAbxL3EEhaL+eowVARvyxrQJASYlnKAS6IbInAKg44lMKAYU7Ra1p8BNbB4D6hvgGY8MlMG6PNQBWiCPsAYAL8Y96lr+4ivzAHgDQpPiS+EwikfxFPl8Tf00s4RWA+Lq8CEAEIAIQAYgARAA26b8BaVJkoY+4rDoAAAAASUVORK5CYII=";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -303,11 +293,11 @@ namespace VRCX
|
|||||||
Icon = Image;
|
Icon = Image;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPAddress broadcastIP = IPAddress.Parse("127.0.0.1");
|
var broadcastIP = IPAddress.Parse("127.0.0.1");
|
||||||
Socket broadcastSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
var broadcastSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||||
IPEndPoint endPoint = new IPEndPoint(broadcastIP, 42069);
|
var endPoint = new IPEndPoint(broadcastIP, 42069);
|
||||||
|
|
||||||
XSOMessage msg = new XSOMessage();
|
var msg = new XSOMessage();
|
||||||
msg.messageType = 1;
|
msg.messageType = 1;
|
||||||
msg.title = Title;
|
msg.title = Title;
|
||||||
msg.content = Content;
|
msg.content = Content;
|
||||||
@@ -318,21 +308,21 @@ namespace VRCX
|
|||||||
msg.useBase64Icon = UseBase64Icon;
|
msg.useBase64Icon = UseBase64Icon;
|
||||||
msg.icon = Icon;
|
msg.icon = Icon;
|
||||||
|
|
||||||
byte[] byteBuffer = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(msg);
|
var byteBuffer = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(msg);
|
||||||
broadcastSocket.SendTo(byteBuffer, endPoint);
|
broadcastSocket.SendTo(byteBuffer, endPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DownloadVRCXUpdate(string url)
|
public void DownloadVRCXUpdate(string url)
|
||||||
{
|
{
|
||||||
var Location = Path.Combine(Program.AppDataDirectory, "update.exe");
|
var Location = Path.Combine(Program.AppDataDirectory, "update.exe");
|
||||||
WebClient client = new WebClient();
|
var client = new WebClient();
|
||||||
client.Headers.Add("user-agent", Program.Version);
|
client.Headers.Add("user-agent", Program.Version);
|
||||||
client.DownloadFile(new Uri(url), Location);
|
client.DownloadFile(new Uri(url), Location);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RestartApplication()
|
public void RestartApplication()
|
||||||
{
|
{
|
||||||
Process VRCXProcess = new Process();
|
var VRCXProcess = new Process();
|
||||||
VRCXProcess.StartInfo.FileName = Path.Combine(Program.BaseDirectory, "VRCX.exe");
|
VRCXProcess.StartInfo.FileName = Path.Combine(Program.BaseDirectory, "VRCX.exe");
|
||||||
VRCXProcess.StartInfo.UseShellExecute = false;
|
VRCXProcess.StartInfo.UseShellExecute = false;
|
||||||
VRCXProcess.StartInfo.Arguments = "/Upgrade";
|
VRCXProcess.StartInfo.Arguments = "/Upgrade";
|
||||||
@@ -353,7 +343,7 @@ namespace VRCX
|
|||||||
ipcClient.Connect();
|
ipcClient.Connect();
|
||||||
if (!ipcClient.IsConnected)
|
if (!ipcClient.IsConnected)
|
||||||
return;
|
return;
|
||||||
var buffer = Encoding.UTF8.GetBytes($"{{\"type\":\"VRCXLaunch\"}}" + (char)0x00);
|
var buffer = Encoding.UTF8.GetBytes("{\"type\":\"VRCXLaunch\"}" + (char)0x00);
|
||||||
ipcClient.BeginWrite(buffer, 0, buffer.Length, IPCClient.OnSend, ipcClient);
|
ipcClient.BeginWrite(buffer, 0, buffer.Length, IPCClient.OnSend, ipcClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,22 +367,19 @@ namespace VRCX
|
|||||||
|
|
||||||
public string GetLaunchCommand()
|
public string GetLaunchCommand()
|
||||||
{
|
{
|
||||||
string command = StartupArgs.LaunchCommand;
|
var command = StartupArgs.LaunchCommand;
|
||||||
StartupArgs.LaunchCommand = string.Empty;
|
StartupArgs.LaunchCommand = string.Empty;
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FocusWindow()
|
public void FocusWindow()
|
||||||
{
|
{
|
||||||
MainForm.Instance.Invoke(new Action(() =>
|
MainForm.Instance.Invoke(new Action(() => { MainForm.Instance.Focus_Window(); }));
|
||||||
{
|
|
||||||
MainForm.Instance.Focus_Window();
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CustomCssPath()
|
public string CustomCssPath()
|
||||||
{
|
{
|
||||||
var output = String.Empty;
|
var output = string.Empty;
|
||||||
var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "VRCX\\custom.css");
|
var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "VRCX\\custom.css");
|
||||||
if (File.Exists(filePath))
|
if (File.Exists(filePath))
|
||||||
output = filePath;
|
output = filePath;
|
||||||
@@ -401,7 +388,7 @@ namespace VRCX
|
|||||||
|
|
||||||
public string CustomScriptPath()
|
public string CustomScriptPath()
|
||||||
{
|
{
|
||||||
var output = String.Empty;
|
var output = string.Empty;
|
||||||
var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "VRCX\\custom.js");
|
var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "VRCX\\custom.js");
|
||||||
if (File.Exists(filePath))
|
if (File.Exists(filePath))
|
||||||
output = filePath;
|
output = filePath;
|
||||||
@@ -442,28 +429,27 @@ namespace VRCX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly MD5 _hasher = MD5.Create();
|
|
||||||
|
|
||||||
public int GetColourFromUserID(string userId)
|
public int GetColourFromUserID(string userId)
|
||||||
{
|
{
|
||||||
var hash = _hasher.ComputeHash(Encoding.UTF8.GetBytes(userId));
|
var hash = _hasher.ComputeHash(Encoding.UTF8.GetBytes(userId));
|
||||||
return hash[3] << 8 | hash[4];
|
return (hash[3] << 8) | hash[4];
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<string, int> GetColourBulk(List<Object> userIds)
|
public Dictionary<string, int> GetColourBulk(List<object> userIds)
|
||||||
{
|
{
|
||||||
Dictionary<string, int> output = new Dictionary<string, int>();
|
var output = new Dictionary<string, int>();
|
||||||
foreach (string userId in userIds)
|
foreach (string userId in userIds)
|
||||||
{
|
{
|
||||||
output.Add(userId, GetColourFromUserID(userId));
|
output.Add(userId, GetColourFromUserID(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetClipboard()
|
public string GetClipboard()
|
||||||
{
|
{
|
||||||
var clipboard = String.Empty;
|
var clipboard = string.Empty;
|
||||||
Thread thread = new Thread(() => clipboard = Clipboard.GetText());
|
var thread = new Thread(() => clipboard = Clipboard.GetText());
|
||||||
thread.SetApartmentState(ApartmentState.STA);
|
thread.SetApartmentState(ApartmentState.STA);
|
||||||
thread.Start();
|
thread.Start();
|
||||||
thread.Join();
|
thread.Join();
|
||||||
@@ -475,8 +461,8 @@ namespace VRCX
|
|||||||
// https://answers.unity.com/questions/177945/playerprefs-changing-the-name-of-keys.html?childToView=208076#answer-208076
|
// https://answers.unity.com/questions/177945/playerprefs-changing-the-name-of-keys.html?childToView=208076#answer-208076
|
||||||
// VRC_GROUP_ORDER_usr_032383a7-748c-4fb2-94e4-bcb928e5de6b_h2810492971
|
// VRC_GROUP_ORDER_usr_032383a7-748c-4fb2-94e4-bcb928e5de6b_h2810492971
|
||||||
uint hash = 5381;
|
uint hash = 5381;
|
||||||
foreach (char c in key)
|
foreach (var c in key)
|
||||||
hash = hash * 33 ^ c;
|
hash = (hash * 33) ^ c;
|
||||||
var keyName = key + "_h" + hash;
|
var keyName = key + "_h" + hash;
|
||||||
|
|
||||||
using (var regKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\VRChat\VRChat"))
|
using (var regKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\VRChat\VRChat"))
|
||||||
@@ -502,8 +488,8 @@ namespace VRCX
|
|||||||
public bool SetVRChatRegistryKey(string key, string value)
|
public bool SetVRChatRegistryKey(string key, string value)
|
||||||
{
|
{
|
||||||
uint hash = 5381;
|
uint hash = 5381;
|
||||||
foreach (char c in key)
|
foreach (var c in key)
|
||||||
hash = hash * 33 ^ c;
|
hash = (hash * 33) ^ c;
|
||||||
var keyName = key + "_h" + hash;
|
var keyName = key + "_h" + hash;
|
||||||
|
|
||||||
using (var regKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\VRChat\VRChat", true))
|
using (var regKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\VRChat\VRChat", true))
|
||||||
@@ -523,11 +509,13 @@ namespace VRCX
|
|||||||
setValue = value;
|
setValue = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setValue == null)
|
if (setValue == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
regKey.SetValue(keyName, setValue, type);
|
regKey.SetValue(keyName, setValue, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,6 +543,7 @@ namespace VRCX
|
|||||||
output.Add(userId, type);
|
output.Add(userId, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -579,6 +568,7 @@ namespace VRCX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,6 +592,7 @@ namespace VRCX
|
|||||||
sb.Append(type.ToString("000"));
|
sb.Append(type.ToString("000"));
|
||||||
lines.Add(sb.ToString());
|
lines.Add(sb.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.WriteAllLines(filePath, lines);
|
File.WriteAllLines(filePath, lines);
|
||||||
@@ -610,6 +601,7 @@ namespace VRCX
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,7 +611,7 @@ namespace VRCX
|
|||||||
{
|
{
|
||||||
using (var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true))
|
using (var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true))
|
||||||
{
|
{
|
||||||
if (enabled == true)
|
if (enabled)
|
||||||
{
|
{
|
||||||
var path = Application.ExecutablePath;
|
var path = Application.ExecutablePath;
|
||||||
key.SetValue("VRCX", $"\"{path}\" --startup");
|
key.SetValue("VRCX", $"\"{path}\" --startup");
|
||||||
@@ -651,5 +643,26 @@ namespace VRCX
|
|||||||
|
|
||||||
ScreenshotHelper.WritePNGDescription(path, metadataString);
|
ScreenshotHelper.WritePNGDescription(path, metadataString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void FlashWindow()
|
||||||
|
{
|
||||||
|
MainForm.Instance.BeginInvoke(new MethodInvoker(() => { WinformThemer.Flash(MainForm.Instance); }));
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
119
WinformThemer.cs
119
WinformThemer.cs
@@ -1,9 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace VRCX
|
namespace VRCX
|
||||||
@@ -14,22 +11,33 @@ namespace VRCX
|
|||||||
internal static class WinformThemer
|
internal static class WinformThemer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Private holder of current theme
|
/// Flash both the window caption and taskbar button.
|
||||||
|
/// This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static int currentTheme = 0;
|
public const uint FLASHW_ALL = 3;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the global theme of the app
|
/// Flash continuously until the window comes to the foreground.
|
||||||
/// Light = 0
|
/// </summary>
|
||||||
/// Dark = 1
|
public const uint FLASHW_TIMERNOFG = 12;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Private holder of current theme
|
||||||
|
/// </summary>
|
||||||
|
private static int currentTheme;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the global theme of the app
|
||||||
|
/// Light = 0
|
||||||
|
/// Dark = 1
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void SetGlobalTheme(int theme)
|
public static void SetGlobalTheme(int theme)
|
||||||
{
|
{
|
||||||
currentTheme = theme;
|
currentTheme = theme;
|
||||||
|
|
||||||
//Make a seperate list for all current forms (causes issues otherwise)
|
//Make a seperate list for all current forms (causes issues otherwise)
|
||||||
List<Form> forms = new List<Form>();
|
var forms = new List<Form>();
|
||||||
foreach(Form form in Application.OpenForms)
|
foreach (Form form in Application.OpenForms)
|
||||||
{
|
{
|
||||||
forms.Add(form);
|
forms.Add(form);
|
||||||
}
|
}
|
||||||
@@ -38,23 +46,26 @@ namespace VRCX
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the global theme of the app
|
/// Gets the global theme of the app
|
||||||
/// Light = 0
|
/// Light = 0
|
||||||
/// Dark = 1
|
/// Dark = 1
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static int GetGlobalTheme() => currentTheme;
|
public static int GetGlobalTheme()
|
||||||
|
{
|
||||||
|
return currentTheme;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set given form to the current global theme
|
/// Set given form to the current global theme
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="form"></param>
|
/// <param name="form"></param>
|
||||||
public static void SetThemeToGlobal(Form form)
|
public static void SetThemeToGlobal(Form form)
|
||||||
{
|
{
|
||||||
SetThemeToGlobal(new List<Form>() { form });
|
SetThemeToGlobal(new List<Form> { form });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set a list of given forms to the current global theme
|
/// Set a list of given forms to the current global theme
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="forms"></param>
|
/// <param name="forms"></param>
|
||||||
public static void SetThemeToGlobal(List<Form> forms)
|
public static void SetThemeToGlobal(List<Form> forms)
|
||||||
@@ -62,7 +73,7 @@ namespace VRCX
|
|||||||
MainForm.Instance.Invoke(new Action(() =>
|
MainForm.Instance.Invoke(new Action(() =>
|
||||||
{
|
{
|
||||||
//For each form, set the theme, then move focus onto it to force refresh
|
//For each form, set the theme, then move focus onto it to force refresh
|
||||||
foreach (Form form in forms)
|
foreach (var form in forms)
|
||||||
{
|
{
|
||||||
//Set the theme of the window
|
//Set the theme of the window
|
||||||
SetThemeToGlobal(form.Handle);
|
SetThemeToGlobal(form.Handle);
|
||||||
@@ -86,14 +97,14 @@ namespace VRCX
|
|||||||
private static int GetTheme(IntPtr handle)
|
private static int GetTheme(IntPtr handle)
|
||||||
{
|
{
|
||||||
//Allocate needed memory
|
//Allocate needed memory
|
||||||
IntPtr curThemePtr = Marshal.AllocHGlobal(4);
|
var curThemePtr = Marshal.AllocHGlobal(4);
|
||||||
|
|
||||||
//See what window state it currently is
|
//See what window state it currently is
|
||||||
if (PInvoke.DwmGetWindowAttribute(handle, 19, curThemePtr, 4) != 0)
|
if (PInvoke.DwmGetWindowAttribute(handle, 19, curThemePtr, 4) != 0)
|
||||||
PInvoke.DwmGetWindowAttribute(handle, 20, curThemePtr, 4);
|
PInvoke.DwmGetWindowAttribute(handle, 20, curThemePtr, 4);
|
||||||
|
|
||||||
//Read current theme (light = 0, dark = 1)
|
//Read current theme (light = 0, dark = 1)
|
||||||
int theme = Marshal.ReadInt32(curThemePtr);
|
var theme = Marshal.ReadInt32(curThemePtr);
|
||||||
|
|
||||||
//Free previously allocated
|
//Free previously allocated
|
||||||
Marshal.FreeHGlobal(curThemePtr);
|
Marshal.FreeHGlobal(curThemePtr);
|
||||||
@@ -101,6 +112,37 @@ namespace VRCX
|
|||||||
return theme;
|
return theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void DoFunny()
|
||||||
|
{
|
||||||
|
foreach (Form form in Application.OpenForms)
|
||||||
|
{
|
||||||
|
PInvoke.SetWindowLong(form.Handle, -20, 0x00C00000);
|
||||||
|
// PInvoke.SetWindowLong(form.Handle, -20, 0x00050100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FLASHWINFO Create_FLASHWINFO(IntPtr handle, uint flags, uint count, uint timeout)
|
||||||
|
{
|
||||||
|
var fi = new FLASHWINFO();
|
||||||
|
fi.cbSize = Convert.ToUInt32(Marshal.SizeOf(fi));
|
||||||
|
fi.hwnd = handle;
|
||||||
|
fi.dwFlags = flags;
|
||||||
|
fi.uCount = count;
|
||||||
|
fi.dwTimeout = timeout;
|
||||||
|
return fi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flash the spacified Window (Form) until it receives focus.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="form">The Form (Window) to Flash.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool Flash(Form form)
|
||||||
|
{
|
||||||
|
var fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL | FLASHW_TIMERNOFG, uint.MaxValue, 0);
|
||||||
|
return PInvoke.FlashWindowEx(ref fi);
|
||||||
|
}
|
||||||
|
|
||||||
internal static class PInvoke
|
internal static class PInvoke
|
||||||
{
|
{
|
||||||
[DllImport("DwmApi")]
|
[DllImport("DwmApi")]
|
||||||
@@ -111,15 +153,40 @@ namespace VRCX
|
|||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
internal static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
|
internal static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool FlashWindowEx(ref FLASHWINFO pwfi);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DoFunny()
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct FLASHWINFO
|
||||||
{
|
{
|
||||||
foreach (Form form in Application.OpenForms)
|
/// <summary>
|
||||||
{
|
/// The size of the structure in bytes.
|
||||||
PInvoke.SetWindowLong(form.Handle, -20, 0x00C00000);
|
/// </summary>
|
||||||
// PInvoke.SetWindowLong(form.Handle, -20, 0x00050100);
|
public uint cbSize;
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// A Handle to the Window to be Flashed. The window can be either opened or minimized.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr hwnd;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Flash Status.
|
||||||
|
/// </summary>
|
||||||
|
public uint dwFlags;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of times to Flash the window.
|
||||||
|
/// </summary>
|
||||||
|
public uint uCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rate at which the Window is to be flashed, in milliseconds. If Zero, the function uses the default cursor blink
|
||||||
|
/// rate.
|
||||||
|
/// </summary>
|
||||||
|
public uint dwTimeout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6550,6 +6550,7 @@ speechSynthesis.getVoices();
|
|||||||
if (this.twoFactorAuthDialogVisible) {
|
if (this.twoFactorAuthDialogVisible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
AppApi.FlashWindow();
|
||||||
this.twoFactorAuthDialogVisible = true;
|
this.twoFactorAuthDialogVisible = true;
|
||||||
this.$prompt($t('prompt.totp.description'), $t('prompt.totp.header'), {
|
this.$prompt($t('prompt.totp.description'), $t('prompt.totp.header'), {
|
||||||
distinguishCancelAndClose: true,
|
distinguishCancelAndClose: true,
|
||||||
@@ -6622,6 +6623,7 @@ speechSynthesis.getVoices();
|
|||||||
if (this.twoFactorAuthDialogVisible) {
|
if (this.twoFactorAuthDialogVisible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
AppApi.FlashWindow();
|
||||||
this.twoFactorAuthDialogVisible = true;
|
this.twoFactorAuthDialogVisible = true;
|
||||||
this.$prompt(
|
this.$prompt(
|
||||||
$t('prompt.email_otp.description'),
|
$t('prompt.email_otp.description'),
|
||||||
|
|||||||
Reference in New Issue
Block a user