Release Candidate
This commit is contained in:
parent
9c048ec7fe
commit
c663731818
28
src/TED.sln
Normal file
28
src/TED.sln
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.2.32630.192
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TED", "TED\TED.csproj", "{BD69CE02-1B63-4949-B34B-AA6E875018DA}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Open|Any CPU = Open|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{BD69CE02-1B63-4949-B34B-AA6E875018DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{BD69CE02-1B63-4949-B34B-AA6E875018DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{BD69CE02-1B63-4949-B34B-AA6E875018DA}.Open|Any CPU.ActiveCfg = Open|Any CPU
|
||||||
|
{BD69CE02-1B63-4949-B34B-AA6E875018DA}.Open|Any CPU.Build.0 = Open|Any CPU
|
||||||
|
{BD69CE02-1B63-4949-B34B-AA6E875018DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{BD69CE02-1B63-4949-B34B-AA6E875018DA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {CEDB2C21-E30F-4966-9C5F-7A936F90E393}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
33
src/TED/TED.DrawModes/DrawModeBase.cs
Normal file
33
src/TED/TED.DrawModes/DrawModeBase.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using TED.Program;
|
||||||
|
|
||||||
|
namespace TED.DrawModes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Serves as the base class for different drawing modes.
|
||||||
|
/// </summary>
|
||||||
|
internal abstract class DrawModeBase
|
||||||
|
{
|
||||||
|
// The device context to be drawn on
|
||||||
|
protected IntPtr DeviceContext;
|
||||||
|
|
||||||
|
// The options to be used when drawing
|
||||||
|
protected readonly Options Options;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the DrawModeBase class with the specified device context and options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deviceContext">The device context to be drawn on.</param>
|
||||||
|
/// <param name="options">The options to be used when drawing.</param>
|
||||||
|
internal DrawModeBase(IntPtr deviceContext, Options options)
|
||||||
|
{
|
||||||
|
DeviceContext = deviceContext;
|
||||||
|
Options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, performs the drawing action.
|
||||||
|
/// </summary>
|
||||||
|
public abstract void Draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
100
src/TED/TED.DrawModes/OneTimeDrawMode.cs
Normal file
100
src/TED/TED.DrawModes/OneTimeDrawMode.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TED.Program;
|
||||||
|
using TED.Utils;
|
||||||
|
|
||||||
|
namespace TED.DrawModes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a drawing mode that operates once and does not update.
|
||||||
|
/// </summary>
|
||||||
|
internal class OneTimeDrawMode : DrawModeBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the OneTimeDrawMode class with the specified device context and options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deviceContext">The device context to be drawn on.</param>
|
||||||
|
/// <param name="options">The options to be used when drawing.</param>
|
||||||
|
public OneTimeDrawMode(IntPtr deviceContext, Options options) : base(deviceContext, options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws the image and text on the given device context based on the options provided.
|
||||||
|
/// </summary>
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
// Calculate the luminance of the wallpaper
|
||||||
|
var wallpaperLuminance = ImageUtilities.CalculateWallpaperLuminance();
|
||||||
|
var primaryAreaRect = SystemUtilities.GetPrimaryScreenRect();
|
||||||
|
|
||||||
|
if (DeviceContext != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
using (var graphics = Graphics.FromHdc(DeviceContext))
|
||||||
|
{
|
||||||
|
// Get the path of the image based on the luminance of the wallpaper
|
||||||
|
var imagePath = Options.GetImagePath(wallpaperLuminance);
|
||||||
|
|
||||||
|
// Set the text color based on the luminance of the wallpaper
|
||||||
|
var textColor = wallpaperLuminance > 0.5 ? Color.Black : Color.White;
|
||||||
|
|
||||||
|
using (var font = new Font(Options.FontName, Options.FontSize, FontStyle.Bold))
|
||||||
|
{
|
||||||
|
// Calculate the scaling factors
|
||||||
|
var scaleX = graphics.DpiX / 96.0f;
|
||||||
|
var scaleY = graphics.DpiY / 96.0f;
|
||||||
|
|
||||||
|
// Calculate the working area dimensions
|
||||||
|
var scaledWorkingAreaWidth = primaryAreaRect.X / scaleX;
|
||||||
|
var scaledWorkingAreaHeight = primaryAreaRect.Y / scaleY;
|
||||||
|
|
||||||
|
// Calculate the maximum width of all lines
|
||||||
|
var maxWidth = graphics.MeasureString(Options.Lines.Max(l => l), font).Width;
|
||||||
|
|
||||||
|
// Calculate the positions of the text and the image
|
||||||
|
var textX = scaledWorkingAreaWidth + Screen.PrimaryScreen.WorkingArea.Width - maxWidth - Options.PaddingHorizontal;
|
||||||
|
var textY = scaledWorkingAreaHeight + Screen.PrimaryScreen.WorkingArea.Height - Options.Lines.Sum(line => graphics.MeasureString(line, font).Height) - (Options.LineSpacing * (Options.Lines.Count - 1)) - Options.PaddingVertical;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(imagePath))
|
||||||
|
{
|
||||||
|
using (var overlayImage = Image.FromFile(imagePath))
|
||||||
|
{
|
||||||
|
// Scale the image while maintaining its aspect ratio
|
||||||
|
ImageUtilities.ScaleImageAndMaintainAspectRatio(overlayImage.Width, overlayImage.Height, maxWidth, int.MaxValue, out int newWidth, out int newHeight);
|
||||||
|
|
||||||
|
var imageX = scaledWorkingAreaWidth + Screen.PrimaryScreen.WorkingArea.Width - newWidth - Options.PaddingHorizontal;
|
||||||
|
var imageY = scaledWorkingAreaHeight + Screen.PrimaryScreen.WorkingArea.Height - newHeight - Options.Lines.Sum(line => graphics.MeasureString(line, font).Height) - (Options.LineSpacing * (Options.Lines.Count)) - Options.PaddingVertical;
|
||||||
|
|
||||||
|
textX = imageX;
|
||||||
|
textY = imageY + newHeight + Options.LineSpacing;
|
||||||
|
|
||||||
|
// Draw the image
|
||||||
|
graphics.DrawImage(overlayImage, new RectangleF(imageX, imageY, newWidth, newHeight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw each line of text
|
||||||
|
for (var i = 0; i < Options.Lines.Count; i++)
|
||||||
|
{
|
||||||
|
var line = Options.Lines[i];
|
||||||
|
|
||||||
|
// Draw the line
|
||||||
|
graphics.DrawString(line, font, new SolidBrush(textColor), new PointF(textX, textY));
|
||||||
|
|
||||||
|
// Move the text cursor down to the next line
|
||||||
|
textY += graphics.MeasureString(line, font).Height;
|
||||||
|
|
||||||
|
// If there are more lines, add additional spacing
|
||||||
|
if (i < Options.Lines.Count)
|
||||||
|
{
|
||||||
|
textY += Options.LineSpacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
91
src/TED/TED.Program/Options.cs
Normal file
91
src/TED/TED.Program/Options.cs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using TED.Utils;
|
||||||
|
|
||||||
|
namespace TED.Program
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Container for the various options utilized within TED.
|
||||||
|
/// </summary>
|
||||||
|
internal class Options
|
||||||
|
{
|
||||||
|
internal readonly int PaddingHorizontal;
|
||||||
|
internal readonly int PaddingVertical;
|
||||||
|
internal readonly int LineSpacing;
|
||||||
|
internal readonly int FontSize;
|
||||||
|
internal readonly string FontName;
|
||||||
|
internal readonly string ImagePath;
|
||||||
|
internal readonly string LightImagePath;
|
||||||
|
internal readonly string DarkImagePath;
|
||||||
|
internal readonly List<string> Lines;
|
||||||
|
internal readonly bool Debug;
|
||||||
|
internal readonly bool AdaptiveImageMode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets default options
|
||||||
|
/// </summary>
|
||||||
|
private static Options? _default;
|
||||||
|
internal static Options Default
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
_default ??= new Options(
|
||||||
|
10, 10, 8, 8, "Arial",
|
||||||
|
"", "", "",
|
||||||
|
new List<string>()
|
||||||
|
{
|
||||||
|
Tokenizer.ReplaceTokens("USERNAME: @userName"),
|
||||||
|
Tokenizer.ReplaceTokens("DEVICE NAME: @machineName"),
|
||||||
|
Tokenizer.ReplaceTokens("OS: @os"),
|
||||||
|
},
|
||||||
|
false);
|
||||||
|
|
||||||
|
return _default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the Options class with the specified settings.
|
||||||
|
/// </summary>
|
||||||
|
internal Options(int paddingHorizontal, int paddingVertical,
|
||||||
|
int lineSpacing, int fontSize, string fontName,
|
||||||
|
string imagePath, string lightImagePath,
|
||||||
|
string darkImagePath, List<string> lines,
|
||||||
|
bool debug)
|
||||||
|
{
|
||||||
|
PaddingHorizontal = paddingHorizontal;
|
||||||
|
PaddingVertical = paddingVertical;
|
||||||
|
LineSpacing = lineSpacing;
|
||||||
|
FontSize = fontSize;
|
||||||
|
FontName = fontName;
|
||||||
|
ImagePath = imagePath;
|
||||||
|
LightImagePath = lightImagePath;
|
||||||
|
DarkImagePath = darkImagePath;
|
||||||
|
Lines = lines;
|
||||||
|
Debug = debug;
|
||||||
|
AdaptiveImageMode = !string.IsNullOrEmpty(LightImagePath) && !string.IsNullOrEmpty(DarkImagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines the image path based on the wallpaper's luminance.
|
||||||
|
/// When in AdaptiveImageMode, chooses the light or dark image path based on the provided luminance.
|
||||||
|
///
|
||||||
|
/// Note: AdaptiveImageMode requires both a light image and dark image to be set. If only one is provided, the regular image
|
||||||
|
/// path will be used as a fallback.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="wallpaperLuminance">The luminance of the wallpaper.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The appropriate image path considering the luminance and the AdaptiveImageMode.
|
||||||
|
/// </returns>
|
||||||
|
internal string GetImagePath(double wallpaperLuminance)
|
||||||
|
{
|
||||||
|
if (AdaptiveImageMode && !string.IsNullOrEmpty(LightImagePath) && !string.IsNullOrEmpty(DarkImagePath))
|
||||||
|
{
|
||||||
|
return wallpaperLuminance > 0.5 ? DarkImagePath : LightImagePath;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ImagePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
159
src/TED/TED.Program/Program.cs
Normal file
159
src/TED/TED.Program/Program.cs
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using TED.Utils;
|
||||||
|
|
||||||
|
namespace TED.Program
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main class for the program.
|
||||||
|
/// </summary>
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
|
||||||
|
private static Tagger? tagger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Application entry point
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">An array of command-line arguments.</param>
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
tagger = new Tagger(ParseArgsIntoOptions(args));
|
||||||
|
tagger.Tag();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses the command-line arguments into an Options object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">An array of command-line arguments.</param>
|
||||||
|
/// <returns>An Options object that encapsulates application settings.</returns>
|
||||||
|
private static Options ParseArgsIntoOptions(string[] args)
|
||||||
|
{
|
||||||
|
var fontName = GetArgument(args, new string[] { "-font", "-f" }, Options.Default.FontName);
|
||||||
|
var imagePath = GetArgument(args, new string[] { "-image", "-i" }, Options.Default.ImagePath);
|
||||||
|
var darkImagePath = GetArgument(args, new string[] { "-darkimage", "-di" }, Options.Default.DarkImagePath);
|
||||||
|
var lightImagePath = GetArgument(args, new string[] { "-lightimage", "-li" }, Options.Default.LightImagePath);
|
||||||
|
var lines = Options.Default.Lines;
|
||||||
|
|
||||||
|
if (!bool.TryParse(GetArgument(args, new string[] { "-debug", "-d" }, Options.Default.Debug.ToString()), out bool debug))
|
||||||
|
{
|
||||||
|
debug = Options.Default.Debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!int.TryParse(GetArgument(args, new string[] { "-fontsize", "-fs" }, Options.Default.FontSize.ToString()), out int fontSize))
|
||||||
|
{
|
||||||
|
fontSize = Options.Default.FontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!int.TryParse(GetArgument(args, new string[] { "-linespacing", "-ls" }, Options.Default.LineSpacing.ToString()), out int margin))
|
||||||
|
{
|
||||||
|
margin = Options.Default.LineSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!int.TryParse(GetArgument(args, new string[] { "-hpad", "-hp" }, Options.Default.PaddingHorizontal.ToString()), out int paddingHorizontal))
|
||||||
|
{
|
||||||
|
paddingHorizontal = Options.Default.PaddingHorizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!int.TryParse(GetArgument(args, new string[] { "-vpad", "-vp" }, Options.Default.PaddingHorizontal.ToString()), out int paddingVertical))
|
||||||
|
{
|
||||||
|
paddingVertical = Options.Default.PaddingVertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Any(arg => arg.Contains("-line")))
|
||||||
|
{
|
||||||
|
lines.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
if (args[i] == "-line")
|
||||||
|
{
|
||||||
|
|
||||||
|
for (int j = i + 1; j < args.Length; j++)
|
||||||
|
{
|
||||||
|
if (args[j].StartsWith("-"))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.Add(Tokenizer.ReplaceTokens(args[j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imagePath = string.IsNullOrEmpty(imagePath) ? imagePath : EnsureImageExists(imagePath);
|
||||||
|
lightImagePath = string.IsNullOrEmpty(lightImagePath) ? lightImagePath : EnsureImageExists(lightImagePath);
|
||||||
|
darkImagePath = string.IsNullOrEmpty(darkImagePath) ? darkImagePath : EnsureImageExists(darkImagePath);
|
||||||
|
|
||||||
|
return new Options(
|
||||||
|
paddingHorizontal,
|
||||||
|
paddingVertical,
|
||||||
|
margin,
|
||||||
|
fontSize,
|
||||||
|
fontName,
|
||||||
|
imagePath,
|
||||||
|
lightImagePath,
|
||||||
|
darkImagePath,
|
||||||
|
lines,
|
||||||
|
debug
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates the existence of the image file at the specified path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="imagePath">Path of the image file.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The path of the image. If the image path points to a URL, the method attempts to download
|
||||||
|
/// the image, cache it, and then return the local path of the downloaded image.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="HttpRequestException">
|
||||||
|
/// Thrown if the image path points to a URL and the image fails to download.
|
||||||
|
/// </exception>
|
||||||
|
/// <exception cref="FileNotFoundException">
|
||||||
|
/// Thrown if the image path does not point to a valid local file or URL.
|
||||||
|
/// </exception>
|
||||||
|
private static string EnsureImageExists(string imagePath)
|
||||||
|
{
|
||||||
|
if (!FileUtilities.PathIsLocalFile(imagePath))
|
||||||
|
{
|
||||||
|
if (FileUtilities.PathIsUrl(imagePath))
|
||||||
|
{
|
||||||
|
var path = string.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
path = FileUtilities.DownloadAndCacheFileAsync(imagePath).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw new HttpRequestException("ERROR: Failed to acquire image from provided URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("ERROR: Image filepath or URL is invalid.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return imagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the value of a specific command-line argument.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">A collection of command-line arguments.</param>
|
||||||
|
/// <param name="options">An array of options to match against the command-line arguments.</param>
|
||||||
|
/// <param name="defaultValue">The default value to return if the option is not found.</param>
|
||||||
|
/// <returns>The value of the specified command-line argument, or the default value if the option is not found.</returns>
|
||||||
|
private static string GetArgument(IEnumerable<string> args, string[] options, string defaultValue) =>
|
||||||
|
args.SkipWhile(i => !options.Contains(i)).Skip(1).Take(1).DefaultIfEmpty(defaultValue).First();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
40
src/TED/TED.Program/Tagger.cs
Normal file
40
src/TED/TED.Program/Tagger.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using TED.DrawModes;
|
||||||
|
using TED.Utils;
|
||||||
|
|
||||||
|
namespace TED.Program
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality for tagging windows in the system.
|
||||||
|
/// </summary>
|
||||||
|
internal class Tagger
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality for tagging windows in the system.
|
||||||
|
/// </summary>
|
||||||
|
internal Options Options;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Tagger"/> class with the specified options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The options to be used while tagging.</param>
|
||||||
|
public Tagger(Options options)
|
||||||
|
{
|
||||||
|
Options = options;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tags the window based on the given <see cref="Options"/>.
|
||||||
|
/// </summary>
|
||||||
|
public void Tag()
|
||||||
|
{
|
||||||
|
var progman = Win32Native.GetProgmanWindow();
|
||||||
|
Win32Native.ClearWindow(progman);
|
||||||
|
var workerWindow = Win32Native.CreateWorkerW(progman);
|
||||||
|
var deviceContext = Win32Native.GetWorkerWindowDeviceContext(workerWindow);
|
||||||
|
DrawModeBase drawer = new OneTimeDrawMode(deviceContext, Options);
|
||||||
|
drawer.Draw();
|
||||||
|
Win32Native.ReleaseWorkerWindowDeviceContext(workerWindow, deviceContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/TED/TED.Utils/FileUtilities.cs
Normal file
77
src/TED/TED.Utils/FileUtilities.cs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TED.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides utility methods for working with files
|
||||||
|
/// </summary>
|
||||||
|
internal class FileUtilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Downloads a file from a specified URL and saves it to a cache.
|
||||||
|
/// If the file already exists in the cache, the function returns the path to the cached file.
|
||||||
|
/// If the file does not exist in the cache, the function downloads the file, saves it to the cache, and then returns the path to the cached file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="url">The URL of the file to download.</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation. The task result is the path to the downloaded (or cached) file.</returns>
|
||||||
|
public static async Task<string> DownloadAndCacheFileAsync(string url)
|
||||||
|
{
|
||||||
|
using (var client = new HttpClient()
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromSeconds(10)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
var response = await client.GetAsync(url);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var etag = response.Headers.ETag?.Tag.Replace("\"", string.Empty) ?? "untagged";
|
||||||
|
if (!Directory.Exists(Path.Combine(Path.GetTempPath(), "TED")))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "TED"));
|
||||||
|
}
|
||||||
|
var path = Path.Combine(Path.GetTempPath(), "TED", $"{etag}.png");
|
||||||
|
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
var filesToDelete = Directory.GetFiles(Path.Combine(Path.GetTempPath(), "TED"), "*.png")
|
||||||
|
.Where(filePath => Path.GetFileNameWithoutExtension(filePath) != etag);
|
||||||
|
foreach (var fileToDelete in filesToDelete)
|
||||||
|
{
|
||||||
|
File.Delete(fileToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var stream = await response.Content.ReadAsStreamAsync())
|
||||||
|
{
|
||||||
|
using (var fileStream = new FileStream(path, FileMode.CreateNew))
|
||||||
|
{
|
||||||
|
await stream.CopyToAsync(fileStream);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Downloads a file from a specified URL and saves it to a cache.
|
||||||
|
/// If the file already exists in the cache, the function returns the path to the cached file.
|
||||||
|
/// If the file does not exist in the cache, the function downloads the file, saves it to the cache, and then returns the path to the cached file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="url">The URL of the file to download.</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation. The task result is the path to the downloaded (or cached) file.</returns>
|
||||||
|
public static bool PathIsLocalFile(string path) => Path.IsPathFullyQualified(path) && File.Exists(path);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether a specified path represents a URL.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to check.</param>
|
||||||
|
/// <returns>True if the path represents a URL; otherwise, false.</returns>
|
||||||
|
public static bool PathIsUrl(string path) => !Path.IsPathFullyQualified(path) && Uri.TryCreate(path, UriKind.Absolute, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
88
src/TED/TED.Utils/ImageUtilities.cs
Normal file
88
src/TED/TED.Utils/ImageUtilities.cs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace TED.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides utility methods for working with images
|
||||||
|
/// </summary>
|
||||||
|
public class ImageUtilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Scales the image dimensions to fit within the specified maximum width and height, maintaining the original aspect ratio.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="srcWidth">The original image width.</param>
|
||||||
|
/// <param name="srcHeight">The original image height.</param>
|
||||||
|
/// <param name="maxWidth">The maximum allowable width for the scaled image.</param>
|
||||||
|
/// <param name="maxHeight">The maximum allowable height for the scaled image.</param>
|
||||||
|
/// <param name="destWidth">The calculated width for the scaled image.</param>
|
||||||
|
/// <param name="destHeight">The calculated height for the scaled image.</param>
|
||||||
|
public static void ScaleImageAndMaintainAspectRatio(int srcWidth, int srcHeight, float maxWidth, float maxHeight, out int destWidth, out int destHeight)
|
||||||
|
{
|
||||||
|
var ratioX = (double)maxWidth / srcWidth;
|
||||||
|
var ratioY = (double)maxHeight / srcHeight;
|
||||||
|
var ratio = Math.Min(ratioX, ratioY);
|
||||||
|
|
||||||
|
destWidth = (int)(srcWidth * ratio);
|
||||||
|
destHeight = (int)(srcHeight * ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the perceived luminance of the current desktop wallpaper.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A value representing the calculated perceived luminance of the wallpaper. Returns 0.0 if the wallpaper path is not found.</returns>
|
||||||
|
public static double CalculateWallpaperLuminance()
|
||||||
|
{
|
||||||
|
// Get the wallpaper path from the registry
|
||||||
|
string wallpaperPath = SystemUtilities.GetWallpaperPathFromRegistry();
|
||||||
|
double luminance = 0.0;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(wallpaperPath))
|
||||||
|
{
|
||||||
|
using (var bmp = new Bitmap(wallpaperPath))
|
||||||
|
{
|
||||||
|
luminance = CalculateImageLuminance01(bmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return luminance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the perceived luminance of a Bitmap image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bm">The Bitmap image to calculate the luminance for.</param>
|
||||||
|
/// <returns>A value representing the calculated luminance of the image.</returns>
|
||||||
|
public static double CalculateImageLuminance01(Bitmap bm)
|
||||||
|
{
|
||||||
|
var lum = 0.0;
|
||||||
|
var width = bm.Width;
|
||||||
|
var height = bm.Height;
|
||||||
|
var bytesPerPixel = Image.GetPixelFormatSize(bm.PixelFormat) / 8;
|
||||||
|
|
||||||
|
var srcData = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadOnly, bm.PixelFormat);
|
||||||
|
var stride = srcData.Stride;
|
||||||
|
IntPtr scan0 = srcData.Scan0;
|
||||||
|
|
||||||
|
// Luminance (standard, objective): (0.2126*R) + (0.7152*G) + (0.0722*B)
|
||||||
|
// Luminance (perceived option 1): (0.299*R + 0.587*G + 0.114*B)
|
||||||
|
// Luminance (perceived option 2, slower to calculate): sqrt( 0.299*R^2 + 0.587*G^2 + 0.114*B^2 )
|
||||||
|
|
||||||
|
for (var y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (var x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
var idx = y * stride + x * bytesPerPixel;
|
||||||
|
lum += (0.299 * Marshal.ReadByte(scan0, idx + 2) + 0.587 * Marshal.ReadByte(scan0, idx + 1) + 0.114 * Marshal.ReadByte(scan0, idx)) / 255.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bm.UnlockBits(srcData);
|
||||||
|
var normalized = lum / (width * height);
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
45
src/TED/TED.Utils/SystemUtilities.cs
Normal file
45
src/TED/TED.Utils/SystemUtilities.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using Microsoft.Win32;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace TED.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides utility functions related to system configurations.
|
||||||
|
/// </summary>
|
||||||
|
public class SystemUtilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get's the Rectangle of the Primary Screen in Virtual Screen space.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The Rectangle of the Primary Screen in Virtual Screen space.</returns>
|
||||||
|
public static Rectangle GetPrimaryScreenRect()
|
||||||
|
{
|
||||||
|
return new Rectangle(
|
||||||
|
Screen.PrimaryScreen.Bounds.X - SystemInformation.VirtualScreen.Left,
|
||||||
|
Screen.PrimaryScreen.Bounds.Y - SystemInformation.VirtualScreen.Top,
|
||||||
|
Screen.PrimaryScreen.WorkingArea.Width,
|
||||||
|
Screen.PrimaryScreen.WorkingArea.Height
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetches the current wallpaper path from the system registry.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The full path of the current desktop wallpaper.</returns>
|
||||||
|
public static string GetWallpaperPathFromRegistry()
|
||||||
|
{
|
||||||
|
string result = string.Empty;
|
||||||
|
using (var key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop"))
|
||||||
|
{
|
||||||
|
if (key != null)
|
||||||
|
{
|
||||||
|
result = key.GetValue("Wallpaper").ToString();
|
||||||
|
key.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/TED/TED.Utils/Tokenizer.cs
Normal file
41
src/TED/TED.Utils/Tokenizer.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Security.Principal;
|
||||||
|
|
||||||
|
namespace TED.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality for replacing tokens in a string.
|
||||||
|
/// </summary>
|
||||||
|
internal class Tokenizer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary that maps token names to their corresponding values.
|
||||||
|
/// </summary>
|
||||||
|
static Dictionary<string, Func<string>> TokenLookup = new Dictionary<string, Func<string>>()
|
||||||
|
{
|
||||||
|
{ "@userName", () => WindowsIdentity.GetCurrent().Name },
|
||||||
|
{ "@machineName", () => Environment.MachineName },
|
||||||
|
{ "@os", () => Environment.OSVersion.ToString() },
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces tokens in the input string with their corresponding values.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The string that may contain tokens to be replaced.</param>
|
||||||
|
/// <returns>The input string with all tokens replaced by their corresponding values.</returns>
|
||||||
|
public static string ReplaceTokens(string input)
|
||||||
|
{
|
||||||
|
foreach (var kvp in TokenLookup)
|
||||||
|
{
|
||||||
|
var token = kvp.Key;
|
||||||
|
var value = kvp.Value();
|
||||||
|
|
||||||
|
input = input.Replace(token, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
169
src/TED/TED.Utils/Win32Native.cs
Normal file
169
src/TED/TED.Utils/Win32Native.cs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace TED.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// The information and methods stored within this class are largely attributed to <see href="https://www.pinvoke.net/"/>
|
||||||
|
/// It has been a fantastic resource for tinkering with and learning how native methods work.
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
public class Win32Native
|
||||||
|
{
|
||||||
|
public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
|
||||||
|
|
||||||
|
[Flags()]
|
||||||
|
public enum DeviceContextValues : uint
|
||||||
|
{
|
||||||
|
/// <summary>DCX_WINDOW: Returns a DC that corresponds to the window rectangle rather
|
||||||
|
/// than the client rectangle.</summary>
|
||||||
|
Window = 0x00000001,
|
||||||
|
|
||||||
|
/// <summary>DCX_CACHE: Returns a DC from the cache, rather than the OWNDC or CLASSDC
|
||||||
|
/// window. Essentially overrides CS_OWNDC and CS_CLASSDC.</summary>
|
||||||
|
Cache = 0x00000002,
|
||||||
|
|
||||||
|
/// <summary>DCX_NORESETATTRS: Does not reset the attributes of this DC to the
|
||||||
|
/// default attributes when this DC is released.</summary>
|
||||||
|
NoResetAttrs = 0x00000004,
|
||||||
|
|
||||||
|
/// <summary>DCX_CLIPCHILDREN: Excludes the visible regions of all child windows
|
||||||
|
/// below the window identified by hWnd.</summary>
|
||||||
|
ClipChildren = 0x00000008,
|
||||||
|
|
||||||
|
/// <summary>DCX_CLIPSIBLINGS: Excludes the visible regions of all sibling windows
|
||||||
|
/// above the window identified by hWnd.</summary>
|
||||||
|
ClipSiblings = 0x00000010,
|
||||||
|
|
||||||
|
/// <summary>DCX_PARENTCLIP: Uses the visible region of the parent window. The
|
||||||
|
/// parent's WS_CLIPCHILDREN and CS_PARENTDC style bits are ignored. The origin is
|
||||||
|
/// set to the upper-left corner of the window identified by hWnd.</summary>
|
||||||
|
ParentClip = 0x00000020,
|
||||||
|
|
||||||
|
/// <summary>DCX_EXCLUDERGN: The clipping region identified by hrgnClip is excluded
|
||||||
|
/// from the visible region of the returned DC.</summary>
|
||||||
|
ExcludeRgn = 0x00000040,
|
||||||
|
|
||||||
|
/// <summary>DCX_INTERSECTRGN: The clipping region identified by hrgnClip is
|
||||||
|
/// intersected with the visible region of the returned DC.</summary>
|
||||||
|
IntersectRgn = 0x00000080,
|
||||||
|
|
||||||
|
/// <summary>DCX_EXCLUDEUPDATE: Unknown...Undocumented</summary>
|
||||||
|
ExcludeUpdate = 0x00000100,
|
||||||
|
|
||||||
|
/// <summary>DCX_INTERSECTUPDATE: Unknown...Undocumented</summary>
|
||||||
|
IntersectUpdate = 0x00000200,
|
||||||
|
|
||||||
|
/// <summary>DCX_LOCKWINDOWUPDATE: Allows drawing even if there is a LockWindowUpdate
|
||||||
|
/// call in effect that would otherwise exclude this window. Used for drawing during
|
||||||
|
/// tracking.</summary>
|
||||||
|
LockWindowUpdate = 0x00000400,
|
||||||
|
|
||||||
|
/// <summary>DCX_USESTYLE: Undocumented, something related to WM_NCPAINT message.</summary>
|
||||||
|
UseStyle = 0x00010000,
|
||||||
|
|
||||||
|
/// <summary>DCX_VALIDATE When specified with DCX_INTERSECTUPDATE, causes the DC to
|
||||||
|
/// be completely validated. Using this function with both DCX_INTERSECTUPDATE and
|
||||||
|
/// DCX_VALIDATE is identical to using the BeginPaint function.</summary>
|
||||||
|
Validate = 0x00200000,
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum SendMessageTimeoutFlags : uint
|
||||||
|
{
|
||||||
|
SMTO_NORMAL = 0x0,
|
||||||
|
SMTO_BLOCK = 0x1,
|
||||||
|
SMTO_ABORTIFHUNG = 0x2,
|
||||||
|
SMTO_NOTIMEOUTIFNOTHUNG = 0x8,
|
||||||
|
SMTO_ERRORONEXIT = 0x20
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||||
|
|
||||||
|
//Keep
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern IntPtr GetDCEx(IntPtr hWnd, IntPtr hrgnClip, DeviceContextValues flags);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", EntryPoint = "ReleaseDC")]
|
||||||
|
public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||||
|
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||||
|
public static extern IntPtr SendMessageTimeout(IntPtr windowHandle, uint Msg, IntPtr wParam, IntPtr lParam, SendMessageTimeoutFlags flags, uint timeout, out IntPtr result);
|
||||||
|
|
||||||
|
public static IntPtr GetProgmanWindow()
|
||||||
|
{
|
||||||
|
// We're going to render the watermark over the wallpaper, but behind desktop icons.
|
||||||
|
// We can do so with some trickery using the Progman window
|
||||||
|
// We're going to spawn a WorkerW window behind the desktop icons, and then render our watermark to that
|
||||||
|
return FindWindow("Progman", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ClearWindow(IntPtr windowHandle)
|
||||||
|
{
|
||||||
|
// The below command clears any existing watermark we've drawn
|
||||||
|
// The Thread.Sleep() is necessary because the command has a minor delay to it
|
||||||
|
// Without it, the clear will end up running after we've generated a watermark,
|
||||||
|
// removing our newly generated watermark
|
||||||
|
SendMessage(windowHandle, 0x0034, 4, IntPtr.Zero);
|
||||||
|
//Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IntPtr CreateWorkerW(IntPtr progman)
|
||||||
|
{
|
||||||
|
IntPtr result = IntPtr.Zero;
|
||||||
|
|
||||||
|
// Send 0x052C to Progman. This message directs Progman to spawn a
|
||||||
|
// WorkerW behind the desktop icons. If it is already there, nothing
|
||||||
|
// happens.
|
||||||
|
SendMessageTimeout(progman,
|
||||||
|
0x052C,
|
||||||
|
new IntPtr(0),
|
||||||
|
IntPtr.Zero,
|
||||||
|
SendMessageTimeoutFlags.SMTO_NORMAL,
|
||||||
|
1000,
|
||||||
|
out result);
|
||||||
|
|
||||||
|
IntPtr workerw = IntPtr.Zero;
|
||||||
|
|
||||||
|
// We enumerate all Windows, until we find one, that has the SHELLDLL_DefView
|
||||||
|
// as a child. These should be our desktop icons.
|
||||||
|
// If we found that window, we take its next sibling and assign it to workerw.
|
||||||
|
EnumWindows(new EnumWindowsProc((tophandle, topparamhandle) =>
|
||||||
|
{
|
||||||
|
IntPtr p = FindWindowEx(tophandle,
|
||||||
|
IntPtr.Zero,
|
||||||
|
"SHELLDLL_DefView",
|
||||||
|
IntPtr.Zero);
|
||||||
|
|
||||||
|
if (p != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
// Gets the WorkerW Window after the current one.
|
||||||
|
workerw = FindWindowEx(IntPtr.Zero,
|
||||||
|
tophandle,
|
||||||
|
"WorkerW",
|
||||||
|
IntPtr.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}), IntPtr.Zero);
|
||||||
|
|
||||||
|
return workerw;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IntPtr GetWorkerWindowDeviceContext(IntPtr workerw) => GetDCEx(workerw, IntPtr.Zero, (DeviceContextValues)0x403);
|
||||||
|
public static void ReleaseWorkerWindowDeviceContext(IntPtr workerw, IntPtr deviceContext) => ReleaseDC(workerw, deviceContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/TED/TED.csproj
Normal file
40
src/TED/TED.csproj
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net7.0-windows10.0.22621.0</TargetFramework>
|
||||||
|
<PublishSingleFile>true</PublishSingleFile>
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
<PublishReadyToRun>true</PublishReadyToRun>
|
||||||
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
|
<TrimMode>Link</TrimMode>
|
||||||
|
<RootNamespace>TED</RootNamespace>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<Configurations>Debug;Release;Open</Configurations>
|
||||||
|
<Version>1.0.4.1-RC1</Version>
|
||||||
|
<InformationalVersion>Prerelease</InformationalVersion>
|
||||||
|
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
|
||||||
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||||
|
<Company>Health IT</Company>
|
||||||
|
<Title>TED - Tag Every Desktop</Title>
|
||||||
|
<Authors>Health IT</Authors>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Open|AnyCPU'">
|
||||||
|
<Optimize>True</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<Optimize>False</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.WindowsDesktop.App.WindowsForms" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Properties\PublishProfiles\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
17
src/TED/app.manifest
Normal file
17
src/TED/app.manifest
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||||
|
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
</assembly>
|
||||||
Loading…
Reference in New Issue
Block a user