Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
davidegiacometti committed Dec 28, 2022
1 parent 1ddc745 commit 024506a
Show file tree
Hide file tree
Showing 8 changed files with 387 additions and 0 deletions.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# SetBackground

Simple command line utility that allow you to quickly change desktop background.

## Requirements

- Windows 8/8.1/10/11 x64
- [.NET 7.x Desktop Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)

## Usage

_Set a solid color as background_
```
.\SetBackground.exe color "#225e89"
```
_Set a wallpaper as background_
```
.\SetBackground.exe wallpaper "D:\Wallpapers\1.png"
```

_Set a random wallpaper from the directory as background_
```
.\SetBackground.exe wallpaper "D:\Wallpapers"
```

_Show the list of attached monitors_
```
.\SetBackground.exe list-monitors
0 - Generic PnP Monitor
1 - Dell U2414H(HDMI2)
```

_Set a wallpaper as background only for the specified monitor and set how the wallpaper is displayed_
```
.\SetBackground.exe wallpaper "D:\Wallpapers\1.png" --monitor 1 --position Stretch
```
25 changes: 25 additions & 0 deletions SetBackground.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33205.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetBackground", "SetBackground\SetBackground.csproj", "{7CBC2798-02A7-411B-A5E9-89D09D0453EA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7CBC2798-02A7-411B-A5E9-89D09D0453EA}.Debug|x64.ActiveCfg = Debug|x64
{7CBC2798-02A7-411B-A5E9-89D09D0453EA}.Debug|x64.Build.0 = Debug|x64
{7CBC2798-02A7-411B-A5E9-89D09D0453EA}.Release|x64.ActiveCfg = Release|x64
{7CBC2798-02A7-411B-A5E9-89D09D0453EA}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5232AA56-8899-4823-9272-5460F8E86A6E}
EndGlobalSection
EndGlobal
101 changes: 101 additions & 0 deletions SetBackground/Helpers/DesktopWallpaperHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) Davide Giacometti. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Drawing;
using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.UI.Shell;

namespace SetBackground
{
public static class DesktopWallpaperHelper
{
public static void SetDesktopWallpaper(string path, uint? singleMonitorId)
{
var desktopWallpaper = (IDesktopWallpaper)new DesktopWallpaper();
unsafe
{
uint count;
desktopWallpaper.GetMonitorDevicePathCount(&count);

if (singleMonitorId.HasValue && singleMonitorId >= count)
{
Console.WriteLine("The specified monitor ID isn't valid.");
return;
}

var monitorIndex = singleMonitorId.GetValueOrDefault(0);
var monitorCount = singleMonitorId.HasValue ? singleMonitorId + 1 : count;

for (uint i = monitorIndex; i < monitorCount; i++)
{
PWSTR monitorId;
desktopWallpaper.GetMonitorDevicePathAt(i, &monitorId);
fixed (char* p = path)
{
var mId = monitorId.Value;
var wallpaper = new PCWSTR(p);
desktopWallpaper.SetWallpaper(mId, wallpaper);
desktopWallpaper.Enable(true);
}
}
}
}

public static void SetBackgroundColor(string colorHex)
{
var desktopWallpaper = (IDesktopWallpaper)new DesktopWallpaper();
var color = ColorTranslator.FromHtml(colorHex);
var coloref = (COLORREF)(color.R | (uint)color.G << 8 | (uint)color.B << 16);
desktopWallpaper.SetBackgroundColor(coloref);
desktopWallpaper.Enable(false);
}

public static void SetPosition(Position position)
{
var desktopWallpaperPosition = position switch
{
Position.Center => DESKTOP_WALLPAPER_POSITION.DWPOS_CENTER,
Position.Tile => DESKTOP_WALLPAPER_POSITION.DWPOS_TILE,
Position.Stretch => DESKTOP_WALLPAPER_POSITION.DWPOS_STRETCH,
Position.Fit => DESKTOP_WALLPAPER_POSITION.DWPOS_FIT,
Position.Fill => DESKTOP_WALLPAPER_POSITION.DWPOS_FILL,
Position.Span => DESKTOP_WALLPAPER_POSITION.DWPOS_SPAN,
_ => throw new ArgumentException("The position isn't valid", nameof(position)),
};

var desktopWallpaper = (IDesktopWallpaper)new DesktopWallpaper();
desktopWallpaper.SetPosition(desktopWallpaperPosition);
}

public static List<(uint Id, string DeviceString)> ListMonitors()
{
var monitors = new List<(uint, string)>();

var desktopWallpaper = (IDesktopWallpaper)new DesktopWallpaper();
unsafe
{
uint count;
desktopWallpaper.GetMonitorDevicePathCount(&count);

for (uint i = 0; i < count; i++)
{
DISPLAY_DEVICEW displayDevice = default;
displayDevice.cb = (uint)Marshal.SizeOf(displayDevice);

if (PInvoke.EnumDisplayDevices(null, i, &displayDevice, 256))
{
if (PInvoke.EnumDisplayDevices(displayDevice.DeviceName.ToString(), 0, ref displayDevice, PInvoke.EDD_GET_DEVICE_INTERFACE_NAME))
{
monitors.Add((i, displayDevice.DeviceString.ToString()));
}
}
}
}

return monitors;
}
}
}
15 changes: 15 additions & 0 deletions SetBackground/Helpers/Position.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Davide Giacometti. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace SetBackground
{
public enum Position
{
Center = 0,
Tile = 1,
Stretch = 2,
Fit = 3,
Fill = 4,
Span = 5,
}
}
4 changes: 4 additions & 0 deletions SetBackground/NativeMethods.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
IDesktopWallpaper
DesktopWallpaper
EnumDisplayDevices
EDD_GET_DEVICE_INTERFACE_NAME
140 changes: 140 additions & 0 deletions SetBackground/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright (c) Davide Giacometti. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.CommandLine;
using System.CommandLine.Parsing;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using SetBackground;

var rootCommand = new RootCommand();
var wallpaperCommand = new Command(
name: "wallpaper",
description: "Set a wallpaper as background.");

var wallpaperArgument = new Argument<string?>(
name: "path",
parse: result =>
{
if (result.Tokens.Count > 0)
{
var path = result.Tokens[0].Value;
if (File.Exists(path))
{
return Path.GetFullPath(path);
}
else if (Directory.Exists(path))
{
var files = Directory.GetFiles(Path.GetFullPath(path));
if (files.Any())
{
var randomIndex = RandomNumberGenerator.GetInt32(0, files.Length);
return files[randomIndex];
}
else
{
result.ErrorMessage = $"{path} doesn't contain any files.";
}
}
else
{
result.ErrorMessage = $"{path} isn't a valid path.";
}
}

return null;
},
description: "Path of the image to use as a background. If the path is a directory a random file will be picked.");

var positionOption = new Option<Position?>(
name: "--position",
description: "Specify how the wallpaper is displayed.");

var monitorIdOption = new Option<uint?>(
name: "--monitor",
description: "Change the wallpaper only for the specified monitor ID.");

var colorCommand = new Command(
name: "color",
description: "Set a color as background.");

var colorArgument = new Argument<string?>(
name: "color",
parse: result =>
{
if (result.Tokens.Count > 0)
{
var color = result.Tokens[0].Value;
if (ColorRegex().IsMatch(color))
{
return color;
}
else
{
result.ErrorMessage = $"{color} isn't a valid color.";
}
}

return null;
},
description: "Color to set as background.");

var listMonitorsCommand = new Command(
name: "list-monitors",
description: "Show the list of attached monitors.");

rootCommand.AddCommand(wallpaperCommand);
wallpaperCommand.AddArgument(wallpaperArgument);
wallpaperCommand.AddOption(positionOption);
wallpaperCommand.AddOption(monitorIdOption);
wallpaperCommand.SetHandler(SetWallpaper, wallpaperArgument, positionOption, monitorIdOption);

rootCommand.AddCommand(colorCommand);
colorCommand.AddArgument(colorArgument);
colorCommand.SetHandler(SetColor, colorArgument);

rootCommand.AddCommand(listMonitorsCommand);
listMonitorsCommand.SetHandler(ListMonitors);

await rootCommand.InvokeAsync(args);

static void SetWallpaper(string? path, Position? position, uint? monitorId)
{
if (path == null)
{
return;
}

DesktopWallpaperHelper.SetDesktopWallpaper(path, monitorId);

if (position.HasValue)
{
DesktopWallpaperHelper.SetPosition(position.Value);
}
}

static void SetColor(string? color)
{
if (color == null)
{
return;
}

DesktopWallpaperHelper.SetBackgroundColor(color);
}

static void ListMonitors()
{
var monitors = DesktopWallpaperHelper.ListMonitors();

foreach (var m in monitors)
{
Console.WriteLine("{0} - {1}", m.Id, m.DeviceString);
}
}

public partial class Program
{
[GeneratedRegex("[#][0-9A-Fa-f]{6}\\b")]
private static partial Regex ColorRegex();
}
40 changes: 40 additions & 0 deletions SetBackground/SetBackground.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0-windows8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>true</PublishReadyToRun>
<Copyright>Copyright (C) 2022 Davide Giacometti</Copyright>
<Version>1.0.0.0</Version>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>

<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>1591</NoWarn>
</PropertyGroup>

<ItemGroup>
<AdditionalFiles Include="StyleCop.json" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.164-beta">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>

</Project>
25 changes: 25 additions & 0 deletions SetBackground/StyleCop.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"settings": {
"documentationRules": {
"xmlHeader": false,
"companyName": "Davide Giacometti",
"copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the {licenseName} license. See {licenseFile} file in the project root for full license information.",
"variables": {
"licenseName": "MIT",
"licenseFile": "LICENSE"
},
"documentExposedElements": false
},
"layoutRules": {
"newlineAtEndOfFile": "require"
},
"indentation": {
"indentationSize": 4
},
"orderingRules": {
"usingDirectivesPlacement": "outsideNamespace",
"systemUsingDirectivesFirst": true
}
}
}

0 comments on commit 024506a

Please sign in to comment.