embed_manifest/manifest/
mod.rs

1//! A builder for Windows application manifest XML files.
2//!
3//! This module allows the construction of application manifests from code with the
4//! [`ManifestBuilder`], for use from a Cargo build script. Once configured, the builder
5//! should be passed to [`embed_manifest()`][crate::embed_manifest] to generate the
6//! correct instructions for Cargo. For any other use, the builder will output the XML
7//! when formatted for [`Display`], or with [`to_string()`][ToString]. For more
8//! information about the different elements of an application manifest, see
9//! [Application Manifests][1] in the Microsoft Windows App Development documentation.
10//!
11//! [1]: https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests
12//!
13//! To generate the manifest XML separately, the XML can be output with `write!` or
14//! copied to a string with [`to_string()`][ToString]. To produce the manifest XML with
15//! extra whitespace for formatting, format it with the ‘alternate’ flag:
16//!
17//! ```
18//! # use embed_manifest::new_manifest;
19//! let builder = new_manifest("Company.OrgUnit.Program");
20//! assert_eq!(format!("{:#}", builder), r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
21//! <assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
22//!     <assemblyIdentity name="Company.OrgUnit.Program" type="win32" version="1.4.0.0"/>
23//!     <dependency>
24//!         <dependentAssembly>
25//!             <assemblyIdentity language="*" name="Microsoft.Windows.Common-Controls" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" type="win32" version="6.0.0.0"/>
26//!         </dependentAssembly>
27//!     </dependency>
28//!     <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
29//!         <application>
30//!             <maxversiontested Id="10.0.18362.1"/>
31//!             <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
32//!             <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
33//!             <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
34//!             <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
35//!         </application>
36//!     </compatibility>
37//!     <asmv3:application>
38//!         <asmv3:windowsSettings>
39//!             <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
40//!             <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2</dpiAwareness>
41//!             <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
42//!             <printerDriverIsolation xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">true</printerDriverIsolation>
43//!         </asmv3:windowsSettings>
44//!     </asmv3:application>
45//!     <asmv3:trustInfo>
46//!         <asmv3:security>
47//!             <asmv3:requestedPrivileges>
48//!                 <asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false"/>
49//!             </asmv3:requestedPrivileges>
50//!         </asmv3:security>
51//!     </asmv3:trustInfo>
52//! </assembly>"#.replace("\n", "\r\n"))
53//! ```
54
55use std::fmt::{Display, Formatter};
56use std::ops::RangeBounds;
57use std::{env, fmt};
58
59use crate::manifest::xml::XmlFormatter;
60
61mod xml;
62
63#[cfg(test)]
64mod test;
65
66/// An opaque container to describe the Windows application manifest for the
67/// executable. A new instance with reasonable defaults is created with
68/// [`new_manifest()`][crate::new_manifest].
69#[derive(Debug)]
70pub struct ManifestBuilder {
71    identity: Option<AssemblyIdentity>,
72    dependent_assemblies: Vec<AssemblyIdentity>,
73    compatibility: ApplicationCompatibility,
74    windows_settings: WindowsSettings,
75    requested_execution_level: Option<RequestedExecutionLevel>,
76}
77
78impl ManifestBuilder {
79    pub(crate) fn new(name: &str) -> Self {
80        ManifestBuilder {
81            identity: Some(AssemblyIdentity::application(name)),
82            dependent_assemblies: vec![AssemblyIdentity::new(
83                "Microsoft.Windows.Common-Controls",
84                [6, 0, 0, 0],
85                0x6595b64144ccf1df,
86            )],
87            compatibility: ApplicationCompatibility {
88                max_version_tested: Some(MaxVersionTested::Windows10Version1903),
89                supported_os: vec![
90                    SupportedOS::Windows7,
91                    SupportedOS::Windows8,
92                    SupportedOS::Windows81,
93                    SupportedOS::Windows10,
94                ],
95            },
96            windows_settings: WindowsSettings::new(),
97            requested_execution_level: Some(RequestedExecutionLevel {
98                level: ExecutionLevel::AsInvoker,
99                ui_access: false,
100            }),
101        }
102    }
103
104    pub(crate) fn empty() -> Self {
105        ManifestBuilder {
106            identity: None,
107            dependent_assemblies: Vec::new(),
108            compatibility: ApplicationCompatibility {
109                max_version_tested: None,
110                supported_os: Vec::new(),
111            },
112            windows_settings: WindowsSettings::empty(),
113            requested_execution_level: None,
114        }
115    }
116
117    // Set the dot-separated [application name][identity] in the manifest.
118    //
119    // [identity]: https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests#assemblyIdentity
120    pub fn name(mut self, name: &str) -> Self {
121        match self.identity {
122            Some(ref mut identity) => identity.name = name.to_string(),
123            None => self.identity = Some(AssemblyIdentity::application_version(name, 0, 0, 0, 0)),
124        }
125        self
126    }
127
128    /// Set the four-part application version number in the manifest.
129    pub fn version(mut self, major: u16, minor: u16, build: u16, revision: u16) -> Self {
130        match self.identity {
131            Some(ref mut identity) => identity.version = Version(major, minor, build, revision),
132            None => {
133                self.identity = Some(AssemblyIdentity::application_version("", major, minor, build, revision));
134            }
135        }
136        self
137    }
138
139    /// Add a dependency on a specific version of a side-by-side assembly
140    /// to the application manifest. For more information on side-by-side
141    /// assemblies, see [Using Side-by-side Assemblies][sxs].
142    ///
143    /// [sxs]: https://docs.microsoft.com/en-us/windows/win32/sbscs/using-side-by-side-assemblies
144    pub fn dependency(mut self, identity: AssemblyIdentity) -> Self {
145        self.dependent_assemblies.push(identity);
146        self
147    }
148
149    /// Remove a dependency on a side-by-side assembly. This can be used to
150    /// remove the default dependency on Common Controls version 6:
151    ///
152    /// ```
153    /// # use embed_manifest::new_manifest;
154    /// new_manifest("Company.OrgUnit.Program")
155    ///     .remove_dependency("Microsoft.Windows.Common-Controls")
156    /// # ;
157    /// ```
158    pub fn remove_dependency(mut self, name: &str) -> Self {
159        self.dependent_assemblies.retain(|d| d.name != name);
160        self
161    }
162
163    /// Set the “maximum version tested” based on a Windows SDK version.
164    /// This compatibility setting enables the use of XAML Islands, as described in
165    /// [Host a standard WinRT XAML control in a C++ desktop (Win32) app][xaml].
166    ///
167    /// [xaml]: https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/host-standard-control-with-xaml-islands-cpp
168    pub fn max_version_tested(mut self, version: MaxVersionTested) -> Self {
169        self.compatibility.max_version_tested = Some(version);
170        self
171    }
172
173    /// Remove the “maximum version tested” from the application compatibility.
174    pub fn remove_max_version_tested(mut self) -> Self {
175        self.compatibility.max_version_tested = None;
176        self
177    }
178
179    /// Set the range of supported versions of Windows for application compatibility.
180    /// The default value declares compatibility with every version from
181    /// [Windows 7][SupportedOS::Windows7] to [Windows 10 and 11][SupportedOS::Windows10].
182    pub fn supported_os<R: RangeBounds<SupportedOS>>(mut self, os_range: R) -> Self {
183        use SupportedOS::*;
184
185        self.compatibility.supported_os.clear();
186        for os in [WindowsVista, Windows7, Windows8, Windows81, Windows10] {
187            if os_range.contains(&os) {
188                self.compatibility.supported_os.push(os);
189            }
190        }
191        self
192    }
193
194    /// Set the code page used for single-byte Windows API, starting from Windows 10
195    /// version 1903. The default setting of [UTF-8][`ActiveCodePage::Utf8`] makes Rust
196    /// strings work directly with APIs like `MessageBoxA`.
197    pub fn active_code_page(mut self, code_page: ActiveCodePage) -> Self {
198        self.windows_settings.active_code_page = code_page;
199        self
200    }
201
202    /// Configures how Windows should display this program on monitors where the
203    /// graphics need scaling, whether by the application drawing its user
204    /// interface at different sizes or by the Windows system rendering the graphics
205    /// to a bitmap then resizing that for display.
206    pub fn dpi_awareness(mut self, setting: DpiAwareness) -> Self {
207        self.windows_settings.dpi_awareness = setting;
208        self
209    }
210
211    /// Attempts to scale GDI primitives by the per-monitor scaling values,
212    /// from Windows 10 version 1703. It seems to be best to leave this disabled.
213    pub fn gdi_scaling(mut self, setting: Setting) -> Self {
214        self.windows_settings.gdi_scaling = setting.enabled();
215        self
216    }
217
218    /// Select the memory allocator use by the standard heap allocation APIs,
219    /// including the default Rust allocator. Selecting a different algorithm
220    /// may make performance and memory use better or worse, so any changes
221    /// should be carefully tested.
222    pub fn heap_type(mut self, setting: HeapType) -> Self {
223        self.windows_settings.heap_type = setting;
224        self
225    }
226
227    /// Enable paths longer than 260 characters with some wide-character Win32 APIs,
228    /// when also enabled in the Windows registry. For more details, see
229    /// [Maximum Path Length Limitation][1] in the Windows App Development
230    /// documentation.
231    ///
232    /// As of Rust 1.58, the [Rust standard library bypasses this limitation][2] itself
233    /// by using Unicode paths beginning with `\\?\`.
234    ///
235    /// [1]: https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
236    /// [2]: https://github.com/rust-lang/rust/pull/89174
237    pub fn long_path_aware(mut self, setting: Setting) -> Self {
238        self.windows_settings.long_path_aware = setting.enabled();
239        self
240    }
241
242    /// Enable printer driver isolation for the application, improving security and
243    /// stability when printing by loading the printer driver in a separate
244    /// application. This is poorly documented, but is described in a blog post,
245    /// “[Application-level Printer Driver Isolation][post]”.
246    ///
247    /// [post]: https://peteronprogramming.wordpress.com/2018/01/22/application-level-printer-driver-isolation/
248    pub fn printer_driver_isolation(mut self, setting: Setting) -> Self {
249        self.windows_settings.printer_driver_isolation = setting.enabled();
250        self
251    }
252
253    /// Configure whether the application should receive mouse wheel scroll events
254    /// with a minimum delta of 1, 40 or 120, as described in
255    /// [Windows precision touchpad devices][touchpad].
256    ///
257    /// [touchpad]: https://docs.microsoft.com/en-us/windows/win32/w8cookbook/windows-precision-touchpad-devices
258    pub fn scrolling_awareness(mut self, setting: ScrollingAwareness) -> Self {
259        self.windows_settings.scrolling_awareness = setting;
260        self
261    }
262
263    /// Allows the application to disable the filtering that normally
264    /// removes UWP windows from the results of the `EnumWindows` API.
265    pub fn window_filtering(mut self, setting: Setting) -> Self {
266        self.windows_settings.disable_window_filtering = setting.disabled();
267        self
268    }
269
270    /// Selects the authorities to execute the program with, rather than
271    /// [guessing based on a filename][installer] like `setup.exe`,
272    /// `update.exe` or `patch.exe`.
273    ///
274    /// [installer]: https://docs.microsoft.com/en-us/windows/security/identity-protection/user-account-control/how-user-account-control-works#installer-detection-technology
275    pub fn requested_execution_level(mut self, level: ExecutionLevel) -> Self {
276        match self.requested_execution_level {
277            Some(ref mut requested_execution_level) => requested_execution_level.level = level,
278            None => self.requested_execution_level = Some(RequestedExecutionLevel { level, ui_access: false }),
279        }
280        self
281    }
282
283    /// Allows the application to access the user interface of applications
284    /// running with elevated permissions when this program does not, typically
285    /// for accessibility. The program must additionally be correctly signed
286    /// and installed in a trusted location like the Program Files directory.
287    pub fn ui_access(mut self, access: bool) -> Self {
288        match self.requested_execution_level {
289            Some(ref mut requested_execution_level) => requested_execution_level.ui_access = access,
290            None => {
291                self.requested_execution_level = Some(RequestedExecutionLevel {
292                    level: ExecutionLevel::AsInvoker,
293                    ui_access: access,
294                })
295            }
296        }
297        self
298    }
299}
300
301impl Display for ManifestBuilder {
302    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
303        let mut w = XmlFormatter::new(f);
304        w.start_document()?;
305        let mut attrs = vec![("xmlns", "urn:schemas-microsoft-com:asm.v1")];
306        if !self.windows_settings.is_empty() || self.requested_execution_level.is_some() {
307            attrs.push(("xmlns:asmv3", "urn:schemas-microsoft-com:asm.v3"));
308        }
309        attrs.push(("manifestVersion", "1.0"));
310        w.start_element("assembly", &attrs)?;
311        if let Some(ref identity) = self.identity {
312            identity.xml_to(&mut w)?;
313        }
314        if !self.dependent_assemblies.is_empty() {
315            w.element("dependency", &[], |w| {
316                for d in self.dependent_assemblies.as_slice() {
317                    w.element("dependentAssembly", &[], |w| d.xml_to(w))?;
318                }
319                Ok(())
320            })?;
321        }
322        if !self.compatibility.is_empty() {
323            self.compatibility.xml_to(&mut w)?;
324        }
325        if !self.windows_settings.is_empty() {
326            self.windows_settings.xml_to(&mut w)?;
327        }
328        if let Some(ref requested_execution_level) = self.requested_execution_level {
329            requested_execution_level.xml_to(&mut w)?;
330        }
331        w.end_element("assembly")
332    }
333}
334
335/// Identity of a side-by-side assembly dependency for the application.
336#[derive(Debug)]
337pub struct AssemblyIdentity {
338    r#type: AssemblyType,
339    name: String,
340    language: Option<String>,
341    processor_architecture: Option<AssemblyProcessorArchitecture>,
342    version: Version,
343    public_key_token: Option<PublicKeyToken>,
344}
345
346impl AssemblyIdentity {
347    fn application(name: &str) -> AssemblyIdentity {
348        let major = env::var("CARGO_PKG_VERSION_MAJOR").map_or(0, |s| s.parse().unwrap_or(0));
349        let minor = env::var("CARGO_PKG_VERSION_MINOR").map_or(0, |s| s.parse().unwrap_or(0));
350        let patch = env::var("CARGO_PKG_VERSION_PATCH").map_or(0, |s| s.parse().unwrap_or(0));
351        AssemblyIdentity {
352            r#type: AssemblyType::Win32,
353            name: name.to_string(),
354            language: None,
355            processor_architecture: None,
356            version: Version(major, minor, patch, 0),
357            public_key_token: None,
358        }
359    }
360
361    fn application_version(name: &str, major: u16, minor: u16, build: u16, revision: u16) -> AssemblyIdentity {
362        AssemblyIdentity {
363            r#type: AssemblyType::Win32,
364            name: name.to_string(),
365            language: None,
366            processor_architecture: None,
367            version: Version(major, minor, build, revision),
368            public_key_token: None,
369        }
370    }
371
372    /// Creates a new value for a [manifest dependency][ManifestBuilder::dependency],
373    /// with the `version` as an array of numbers like `[1, 0, 0, 0]` for 1.0.0.0,
374    /// and the public key token as a 64-bit number like `0x6595b64144ccf1df`.
375    pub fn new(name: &str, version: [u16; 4], public_key_token: u64) -> AssemblyIdentity {
376        AssemblyIdentity {
377            r#type: AssemblyType::Win32,
378            name: name.to_string(),
379            language: Some("*".to_string()),
380            processor_architecture: Some(AssemblyProcessorArchitecture::All),
381            version: Version(version[0], version[1], version[2], version[3]),
382            public_key_token: Some(PublicKeyToken(public_key_token)),
383        }
384    }
385
386    /// Change the language from `"*"` to the language code in `language`.
387    pub fn language(mut self, language: &str) -> Self {
388        self.language = Some(language.to_string());
389        self
390    }
391
392    /// Change the processor architecture from `"*"` to the architecture in `arch`.
393    pub fn processor_architecture(mut self, arch: AssemblyProcessorArchitecture) -> Self {
394        self.processor_architecture = Some(arch);
395        self
396    }
397
398    fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
399        let version = self.version.to_string();
400        let public_key_token = self.public_key_token.as_ref().map(|token| token.to_string());
401
402        let mut attrs: Vec<(&str, &str)> = Vec::with_capacity(6);
403        if let Some(ref language) = self.language {
404            attrs.push(("language", language));
405        }
406        attrs.push(("name", &self.name));
407        if let Some(ref arch) = self.processor_architecture {
408            attrs.push(("processorArchitecture", arch.as_str()))
409        }
410        if let Some(ref token) = public_key_token {
411            attrs.push(("publicKeyToken", token));
412        }
413        attrs.push(("type", self.r#type.as_str()));
414        attrs.push(("version", &version));
415        w.empty_element("assemblyIdentity", &attrs)
416    }
417}
418
419#[derive(Debug)]
420struct Version(u16, u16, u16, u16);
421
422impl fmt::Display for Version {
423    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
424        write!(f, "{}.{}.{}.{}", self.0, self.1, self.2, self.3)
425    }
426}
427
428#[derive(Debug)]
429struct PublicKeyToken(u64);
430
431impl fmt::Display for PublicKeyToken {
432    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
433        write!(f, "{:016x}", self.0)
434    }
435}
436
437/// Processor architecture for an assembly identity.
438#[derive(Debug)]
439#[non_exhaustive]
440pub enum AssemblyProcessorArchitecture {
441    /// Any processor architecture, as `"*"`.
442    All,
443    /// 32-bit x86 processors, as `"x86"`.
444    X86,
445    /// 64-bit x64 processors, as `"x64"`.
446    Amd64,
447    /// 32-bit ARM processors, as `"arm"`.
448    Arm,
449    /// 64-bit ARM processors, as `"arm64"`.
450    Arm64,
451}
452
453impl AssemblyProcessorArchitecture {
454    pub fn as_str(&self) -> &'static str {
455        match self {
456            Self::All => "*",
457            Self::X86 => "x86",
458            Self::Amd64 => "amd64",
459            Self::Arm => "arm",
460            Self::Arm64 => "arm64",
461        }
462    }
463}
464
465impl fmt::Display for AssemblyProcessorArchitecture {
466    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
467        f.pad(self.as_str())
468    }
469}
470
471#[derive(Debug)]
472#[non_exhaustive]
473enum AssemblyType {
474    Win32,
475}
476
477impl AssemblyType {
478    fn as_str(&self) -> &'static str {
479        "win32"
480    }
481}
482
483#[derive(Debug)]
484struct ApplicationCompatibility {
485    max_version_tested: Option<MaxVersionTested>,
486    supported_os: Vec<SupportedOS>,
487}
488
489impl ApplicationCompatibility {
490    fn is_empty(&self) -> bool {
491        self.supported_os.is_empty()
492    }
493
494    fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
495        w.element(
496            "compatibility",
497            &[("xmlns", "urn:schemas-microsoft-com:compatibility.v1")],
498            |w| {
499                w.element("application", &[], |w| {
500                    if self.supported_os.contains(&SupportedOS::Windows10) {
501                        if let Some(ref version) = self.max_version_tested {
502                            w.empty_element("maxversiontested", &[("Id", version.as_str())])?;
503                        }
504                    }
505                    for os in self.supported_os.iter() {
506                        w.empty_element("supportedOS", &[("Id", os.as_str())])?
507                    }
508                    Ok(())
509                })
510            },
511        )
512    }
513}
514
515/// Windows build versions for [`max_version_tested()`][ManifestBuilder::max_version_tested]
516/// from the [Windows SDK archive](https://developer.microsoft.com/en-us/windows/downloads/sdk-archive/).
517#[derive(Debug)]
518#[non_exhaustive]
519pub enum MaxVersionTested {
520    /// Windows 10 version 1903, with build version 10.0.18362.0.
521    Windows10Version1903,
522    /// Windows 10 version 2004, with build version 10.0.19041.0.
523    Windows10Version2004,
524    /// Windows 10 version 2104, with build version 10.0.20348.0.
525    Windows10Version2104,
526    /// Windows 11, with build version 10.0.22000.194.
527    Windows11,
528    /// Windows 11 version 22H2, with build version 10.0.22621.1.
529    Windows11Version22H2,
530}
531
532impl MaxVersionTested {
533    /// Return the Windows version as a string.
534    pub fn as_str(&self) -> &'static str {
535        match self {
536            Self::Windows10Version1903 => "10.0.18362.1",
537            Self::Windows10Version2004 => "10.0.19041.0",
538            Self::Windows10Version2104 => "10.0.20348.0",
539            Self::Windows11 => "10.0.22000.194",
540            Self::Windows11Version22H2 => "10.0.22621.1",
541        }
542    }
543}
544
545impl Display for MaxVersionTested {
546    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
547        f.pad(self.as_str())
548    }
549}
550
551/// Operating system versions for Windows compatibility.
552#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
553#[non_exhaustive]
554pub enum SupportedOS {
555    /// Windows Vista and Windows Server 2008.
556    WindowsVista,
557    /// Windows 7 and Windows Server 2008 R2.
558    Windows7,
559    /// Windows 8 and Windows Server 2012.
560    Windows8,
561    /// Windows 8.1 and Windows Server 2012 R2.
562    Windows81,
563    /// Windows 10 and 11, and Windows Server 2016, 2019 and 2022.
564    Windows10,
565}
566
567impl SupportedOS {
568    /// Returns the GUID string for the Windows version.
569    pub fn as_str(&self) -> &'static str {
570        match self {
571            Self::WindowsVista => "{e2011457-1546-43c5-a5fe-008deee3d3f0}",
572            Self::Windows7 => "{35138b9a-5d96-4fbd-8e2d-a2440225f93a}",
573            Self::Windows8 => "{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}",
574            Self::Windows81 => "{1f676c76-80e1-4239-95bb-83d0f6d0da78}",
575            Self::Windows10 => "{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}",
576        }
577    }
578}
579
580impl Display for SupportedOS {
581    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
582        f.pad(self.as_str())
583    }
584}
585
586static WS2005: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2005/WindowsSettings");
587static WS2011: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2011/WindowsSettings");
588static WS2013: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2013/WindowsSettings");
589static WS2016: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2016/WindowsSettings");
590static WS2017: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2017/WindowsSettings");
591static WS2019: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2019/WindowsSettings");
592static WS2020: (&str, &str) = ("xmlns", "http://schemas.microsoft.com/SMI/2020/WindowsSettings");
593
594#[derive(Debug)]
595struct WindowsSettings {
596    active_code_page: ActiveCodePage,
597    disable_window_filtering: bool,
598    dpi_awareness: DpiAwareness,
599    gdi_scaling: bool,
600    heap_type: HeapType,
601    long_path_aware: bool,
602    printer_driver_isolation: bool,
603    scrolling_awareness: ScrollingAwareness,
604}
605
606impl WindowsSettings {
607    fn new() -> Self {
608        Self {
609            active_code_page: ActiveCodePage::Utf8,
610            disable_window_filtering: false,
611            dpi_awareness: DpiAwareness::PerMonitorV2Only,
612            gdi_scaling: false,
613            heap_type: HeapType::LowFragmentationHeap,
614            long_path_aware: true,
615            printer_driver_isolation: true,
616            scrolling_awareness: ScrollingAwareness::UltraHighResolution,
617        }
618    }
619
620    fn empty() -> Self {
621        Self {
622            active_code_page: ActiveCodePage::System,
623            disable_window_filtering: false,
624            dpi_awareness: DpiAwareness::UnawareByDefault,
625            gdi_scaling: false,
626            heap_type: HeapType::LowFragmentationHeap,
627            long_path_aware: false,
628            printer_driver_isolation: false,
629            scrolling_awareness: ScrollingAwareness::UltraHighResolution,
630        }
631    }
632
633    fn is_empty(&self) -> bool {
634        matches!(
635            self,
636            Self {
637                active_code_page: ActiveCodePage::System,
638                disable_window_filtering: false,
639                dpi_awareness: DpiAwareness::UnawareByDefault,
640                gdi_scaling: false,
641                heap_type: HeapType::LowFragmentationHeap,
642                long_path_aware: false,
643                printer_driver_isolation: false,
644                scrolling_awareness: ScrollingAwareness::UltraHighResolution,
645            }
646        )
647    }
648
649    fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
650        w.element("asmv3:application", &[], |w| {
651            w.element("asmv3:windowsSettings", &[], |w| {
652                self.active_code_page.xml_to(w)?;
653                if self.disable_window_filtering {
654                    w.element("disableWindowFiltering", &[WS2011], |w| w.text("true"))?;
655                }
656                self.dpi_awareness.xml_to(w)?;
657                if self.gdi_scaling {
658                    w.element("gdiScaling", &[WS2017], |w| w.text("true"))?;
659                }
660                if matches!(self.heap_type, HeapType::SegmentHeap) {
661                    w.element("heapType", &[WS2020], |w| w.text("SegmentHeap"))?;
662                }
663                if self.long_path_aware {
664                    w.element("longPathAware", &[WS2016], |w| w.text("true"))?;
665                }
666                if self.printer_driver_isolation {
667                    w.element("printerDriverIsolation", &[WS2011], |w| w.text("true"))?;
668                }
669                self.scrolling_awareness.xml_to(w)
670            })
671        })
672    }
673}
674
675/// Configure whether a Windows setting is enabled or disabled, avoiding confusion
676/// over which of these options `true` and `false` represent.
677#[derive(Debug)]
678pub enum Setting {
679    Disabled = 0,
680    Enabled = 1,
681}
682
683impl Setting {
684    /// Returns `true` if the setting should be disabled.
685    fn disabled(&self) -> bool {
686        matches!(self, Setting::Disabled)
687    }
688
689    /// Returns `true` if the setting should be enabled.
690    fn enabled(&self) -> bool {
691        matches!(self, Setting::Enabled)
692    }
693}
694
695/// The code page used by single-byte APIs in the program.
696#[derive(Debug)]
697#[non_exhaustive]
698pub enum ActiveCodePage {
699    /// Use the code page from the configured system locale, or UTF-8 if “Use Unicode UTF-8
700    /// for worldwide language support” is configured.
701    System,
702    /// Use UTF-8 from Windows 10 version 1903 and on Windows 11.
703    Utf8,
704    /// Use the code page from the configured system locale, even when “Use Unicode UTF-8
705    /// for worldwide language support” is configured.
706    Legacy,
707    /// Use the code page from the configured system locale on Windows 10, or from this
708    /// locale on Windows 11.
709    Locale(String),
710}
711
712impl ActiveCodePage {
713    pub fn as_str(&self) -> &str {
714        match self {
715            Self::System => "",
716            Self::Utf8 => "UTF-8",
717            Self::Legacy => "Legacy",
718            Self::Locale(s) => s,
719        }
720    }
721
722    fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
723        match self {
724            Self::System => Ok(()),
725            _ => w.element("activeCodePage", &[WS2019], |w| w.text(self.as_str())),
726        }
727    }
728}
729
730impl Display for ActiveCodePage {
731    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
732        f.pad(self.as_str())
733    }
734}
735
736/// Options for how Windows will handle drawing on monitors when the graphics
737/// need scaling to display at the correct size.
738///
739/// See [High DPI Desktop Application Development on Windows][dpi] for more details
740/// about the impact of these options.
741///
742/// [dpi]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
743#[derive(Debug)]
744#[non_exhaustive]
745pub enum DpiAwareness {
746    /// DPI awareness is not configured, so Windows will scale the application unless
747    /// changed via the `SetProcessDpiAware` or `SetProcessDpiAwareness` API.
748    UnawareByDefault,
749    /// DPI awareness is not configured, with Windows 8.1, 10 and 11 not able
750    /// to change the setting via API.
751    Unaware,
752    /// The program uses the system DPI, or the DPI of the monitor they start on if
753    /// “Fix scaling for apps” is enabled. If the DPI does not match the current
754    /// monitor then Windows will scale the application.
755    System,
756    /// The program uses the system DPI on Windows Vista, 7 and 8, and version 1 of
757    /// per-monitor DPI awareness on Windows 8.1, 10 and 11. Using version 1 of the
758    /// API is not recommended.
759    PerMonitor,
760    /// The program uses the system DPI on Windows Vista, 7 and 8, version 1 of
761    /// per-monitor DPI awareness on Windows 8.1 and Windows 10 version 1507 and 1511,
762    /// and version 2 of per-monitor DPI awareness from Windows 10 version 1607.
763    PerMonitorV2,
764    /// The program uses the system DPI on Windows Vista, 7, 8, 8.1 and Windows 10
765    /// version 1507 and 1511, and version 2 of per-monitor DPI awareness from
766    /// Windows 10 version 1607.
767    PerMonitorV2Only,
768}
769
770impl DpiAwareness {
771    fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
772        let settings = match self {
773            Self::UnawareByDefault => (None, None),
774            Self::Unaware => (Some("false"), None),
775            Self::System => (Some("true"), None),
776            Self::PerMonitor => (Some("true/pm"), None),
777            Self::PerMonitorV2 => (Some("true/pm"), Some("permonitorv2,permonitor")),
778            Self::PerMonitorV2Only => (None, Some("permonitorv2")),
779        };
780        if let Some(dpi_aware) = settings.0 {
781            w.element("dpiAware", &[WS2005], |w| w.text(dpi_aware))?;
782        }
783        if let Some(dpi_awareness) = settings.1 {
784            w.element("dpiAwareness", &[WS2016], |w| w.text(dpi_awareness))?;
785        }
786        Ok(())
787    }
788}
789
790/// The heap type for the default memory allocator.
791#[derive(Debug)]
792#[non_exhaustive]
793pub enum HeapType {
794    /// The default heap type since Windows Vista.
795    LowFragmentationHeap,
796    /// The modern segment heap, which may reduce total memory allocation in some programs.
797    /// This is supported since Windows 10 version 2004. See
798    /// [Improving Memory Usage in Microsoft Edge][edge].
799    ///
800    /// [edge]: https://blogs.windows.com/msedgedev/2020/06/17/improving-memory-usage/
801    SegmentHeap,
802}
803
804/// Whether the application supports scroll wheel events with a minimum delta
805/// of 1, 40 or 120.
806#[derive(Debug)]
807#[non_exhaustive]
808pub enum ScrollingAwareness {
809    /// The application can only handle scroll wheel events with the original delta of 120.
810    LowResolution,
811    /// The application can handle high precision scroll wheel events with a delta of 40.
812    HighResolution,
813    /// The application can handle ultra high precision scroll wheel events with a delta as low as 1.
814    UltraHighResolution,
815}
816
817impl ScrollingAwareness {
818    fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
819        match self {
820            Self::LowResolution => w.element("ultraHighResolutionScrollingAware", &[WS2013], |w| w.text("false")),
821            Self::HighResolution => w.element("highResolutionScrollingAware", &[WS2013], |w| w.text("true")),
822            Self::UltraHighResolution => Ok(()),
823        }
824    }
825}
826
827#[derive(Debug)]
828struct RequestedExecutionLevel {
829    level: ExecutionLevel,
830    ui_access: bool,
831}
832
833impl RequestedExecutionLevel {
834    fn xml_to(&self, w: &mut XmlFormatter) -> fmt::Result {
835        w.element("asmv3:trustInfo", &[], |w| {
836            w.element("asmv3:security", &[], |w| {
837                w.element("asmv3:requestedPrivileges", &[], |w| {
838                    w.empty_element(
839                        "asmv3:requestedExecutionLevel",
840                        &[
841                            ("level", self.level.as_str()),
842                            ("uiAccess", if self.ui_access { "true" } else { "false" }),
843                        ],
844                    )
845                })
846            })
847        })
848    }
849}
850
851/// The requested execution level for the application when started.
852///
853/// The behaviour of each option is described in
854/// [Designing UAC Applications for Windows Vista Step 6: Create and Embed an Application Manifest][step6].
855///
856/// [step6]: https://msdn.microsoft.com/en-us/library/bb756929.aspx
857#[derive(Debug)]
858pub enum ExecutionLevel {
859    /// The application will always run with the same authorities as the program invoking it.
860    AsInvoker,
861    /// The program will run without special authorities for a standard user, but will try to
862    /// run with administrator authority if the user is an administrator. This is rarely used.
863    HighestAvailable,
864    /// The application will run as an administrator, prompting for elevation if necessary.
865    RequireAdministrator,
866}
867
868impl ExecutionLevel {
869    pub fn as_str(&self) -> &'static str {
870        match self {
871            Self::AsInvoker => "asInvoker",
872            Self::HighestAvailable => "highestAvailable",
873            Self::RequireAdministrator => "requireAdministrator",
874        }
875    }
876}
877
878impl Display for ExecutionLevel {
879    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
880        f.pad(self.as_str())
881    }
882}