DisplayMagician/HeliosDisplayManagement.Shared/TaskBarStuckRectangle.cs
temacdonald a9bb295d1f Renamed app to HeliosPlus namespace and more
Renamed app to HeliosPlus namespace so that the updated
changes don't interfere with HeliosDisplayManagement if
that is also installed. The fact that I've changed so much of
the app means that my changes would be unlikely to be
accepted by Soroush, so I'm best to release this work in a
similar way to other projects like Notepad++, by keeping
the same root name, and adding a plus.
    I've also changed the Shortcut form to put all the games
in a single list to reduce the number of clicks a user has to
do in order for them to create a shortcut. I have begun to
prepare the form so that it will support multiple game
libraries, but now I am at a point that I need to fix the Steam
and Uplay game detection mechanisms so that they report
the correct information for the lv_games list view.
2020-04-23 20:16:16 +12:00

358 lines
9.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using WindowsDisplayAPI.DisplayConfig;
using Microsoft.Win32;
using Newtonsoft.Json;
namespace HeliosPlus.Shared
{
public class TaskBarStuckRectangle
{
public enum TaskBarEdge : uint
{
Left = 0,
Top = 1,
Right = 2,
Bottom = 3
}
[Flags]
public enum TaskBarOptions : uint
{
None = 0,
AutoHide = 1 << 0,
KeepOnTop = 1 << 1,
UseSmallIcons = 1 << 2,
HideClock = 1 << 3,
HideVolume = 1 << 4,
HideNetwork = 1 << 5,
HidePower = 1 << 6,
WindowPreview = 1 << 7,
Unknown1 = 1 << 8,
Unknown2 = 1 << 9,
HideActionCenter = 1 << 10,
Unknown3 = 1 << 11,
HideLocation = 1 << 12,
HideLanguageBar = 1 << 13
}
private const string MainDisplayAddress =
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects{0:D}";
private const string MultiDisplayAddress =
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MMStuckRects{0:D}";
private static readonly Dictionary<int, byte[]> Headers = new Dictionary<int, byte[]>
{
{2, new byte[] {0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}},
{3, new byte[] {0x30, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF}}
};
public TaskBarStuckRectangle(int version, string devicePath) : this(version)
{
DevicePath = devicePath;
}
public TaskBarStuckRectangle(int version)
{
if (!Headers.ContainsKey(version))
{
throw new ArgumentException(@"Invalid version number specified.", nameof(version));
}
Version = version;
DevicePath = null;
Binary = new byte[Headers[Version][0]];
Array.Copy(Headers[Version], 0, Binary, 0, Headers[Version].Length);
DPI = 96;
Rows = 1;
Location = Rectangle.Empty;
MinSize = Size.Empty;
Edge = TaskBarEdge.Bottom;
Options = TaskBarOptions.KeepOnTop;
}
public TaskBarStuckRectangle()
{
}
public byte[] Binary { get; set; }
public string DevicePath { get; set; }
[JsonIgnore]
public uint DPI
{
get
{
if (Binary.Length < 44)
{
return 0;
}
return BitConverter.ToUInt32(Binary, 40);
}
set
{
if (Binary.Length < 44)
{
return;
}
var bytes = BitConverter.GetBytes(value);
Array.Copy(bytes, 0, Binary, 40, 4);
}
}
[JsonIgnore]
public TaskBarEdge Edge
{
get
{
if (Binary.Length < 16)
{
return TaskBarEdge.Bottom;
}
return (TaskBarEdge) BitConverter.ToUInt32(Binary, 12);
}
set
{
if (Binary.Length < 16)
{
return;
}
var bytes = BitConverter.GetBytes((uint) value);
Array.Copy(bytes, 0, Binary, 12, 4);
}
}
[JsonIgnore]
public Rectangle Location
{
get
{
if (Binary.Length < 40)
{
return Rectangle.Empty;
}
var left = BitConverter.ToInt32(Binary, 24);
var top = BitConverter.ToInt32(Binary, 28);
var right = BitConverter.ToInt32(Binary, 32);
var bottom = BitConverter.ToInt32(Binary, 36);
return Rectangle.FromLTRB(left, top, right, bottom);
}
set
{
if (Binary.Length < 40)
{
return;
}
var bytes = BitConverter.GetBytes(value.Left);
Array.Copy(bytes, 0, Binary, 24, 4);
bytes = BitConverter.GetBytes(value.Top);
Array.Copy(bytes, 0, Binary, 28, 4);
bytes = BitConverter.GetBytes(value.Right);
Array.Copy(bytes, 0, Binary, 32, 4);
bytes = BitConverter.GetBytes(value.Bottom);
Array.Copy(bytes, 0, Binary, 36, 4);
}
}
[JsonIgnore]
public Size MinSize
{
get
{
if (Binary.Length < 24)
{
return Size.Empty;
}
var width = BitConverter.ToInt32(Binary, 16);
var height = BitConverter.ToInt32(Binary, 20);
return new Size(width, height);
}
set
{
if (Binary.Length < 24)
{
return;
}
var bytes = BitConverter.GetBytes(value.Width);
Array.Copy(bytes, 0, Binary, 16, 4);
bytes = BitConverter.GetBytes(value.Height);
Array.Copy(bytes, 0, Binary, 20, 4);
}
}
[JsonIgnore]
public TaskBarOptions Options
{
get
{
if (Binary.Length < 12)
{
return 0;
}
return (TaskBarOptions) BitConverter.ToUInt32(Binary, 8);
}
set
{
if (Binary.Length < 12)
{
return;
}
var bytes = BitConverter.GetBytes((uint) value);
Array.Copy(bytes, 0, Binary, 8, 4);
}
}
[JsonIgnore]
public uint Rows
{
get
{
if (Binary.Length < 48)
{
return 1;
}
return BitConverter.ToUInt32(Binary, 44);
}
set
{
if (Binary.Length < 48)
{
return;
}
var bytes = BitConverter.GetBytes(value);
Array.Copy(bytes, 0, Binary, 44, 4);
}
}
public int Version { get; set; }
public static TaskBarStuckRectangle GetCurrent()
{
return GetCurrent((string) null);
}
public static TaskBarStuckRectangle GetCurrent(PathDisplayTarget pathTargetInfo)
{
var devicePath = pathTargetInfo?.DevicePath;
var index = devicePath?.IndexOf("{", StringComparison.InvariantCultureIgnoreCase);
if (index > 0)
{
devicePath = devicePath.Substring(0, index.Value).TrimEnd('#');
}
index = devicePath?.IndexOf("#", StringComparison.InvariantCultureIgnoreCase);
if (index > 0)
{
devicePath = devicePath.Substring(index.Value).TrimStart('#');
}
return GetCurrent(devicePath);
}
public static TaskBarStuckRectangle GetCurrent(string devicePath)
{
var stuckRectanglesVersion = 0;
byte[] stuckRectanglesBinary = null;
// Try to extract the latest version of StuckRectangles available on the User Registry
foreach (var version in Headers.Keys)
{
try
{
var address = devicePath != null
? string.Format(MultiDisplayAddress, version)
: string.Format(MainDisplayAddress, version);
using (var key = Registry.CurrentUser.OpenSubKey(
address,
RegistryKeyPermissionCheck.ReadSubTree))
{
var settings = key?.GetValue(devicePath ?? "Settings") as byte[];
if (settings?.Length > 0)
{
stuckRectanglesBinary = settings;
stuckRectanglesVersion = version;
}
}
}
catch (Exception)
{
// ignored
}
}
if (stuckRectanglesVersion == 0 || stuckRectanglesBinary == null)
{
return null;
}
return new TaskBarStuckRectangle
{
DevicePath = devicePath,
Binary = stuckRectanglesBinary,
Version = stuckRectanglesVersion
};
}
public bool Apply()
{
if (Binary == null ||
Binary.Length == 0 ||
Version <= 0)
{
throw new InvalidOperationException();
}
var address = DevicePath != null
? string.Format(MultiDisplayAddress, Version)
: string.Format(MainDisplayAddress, Version);
using (var stuckRectanglesKey = Registry.CurrentUser.OpenSubKey(
address,
RegistryKeyPermissionCheck.ReadWriteSubTree))
{
if (stuckRectanglesKey == null)
{
return false;
}
try
{
stuckRectanglesKey.SetValue(DevicePath ?? "Settings", Binary);
}
catch (Exception)
{
// ignored
}
}
return true;
}
}
}