PowerToys/doc/devdocs/modules/powerdisplay/mccsParserDesign.md
moooyo 18efa0559c
Introduce new utility PowerDisplay to control your monitor settings (#42642)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Introduce a new PowerToys' module PowerDisplay to let user can control
their monitor settings without touching monitor's button.

Support feature list:
Common:
1. Profiles support
2. Integration with LightSwitch (auto switch profile when theme change)
3. TrayIcon
4. Save and restore settings when startup
5. Shortcut
6. Rotation
7. GPO support
8. Auto re-discovery monitor when plugging and unplugging monitors.
9. Identify Monitors
10. Quick profile switch

Especially for DDC/CI monitor:
1. Brightness
2. Contrast
3. Volume
4. Color temperature (preset profile)
5. Input source
6. Power State (poweroff)


Design doc:
https://github.com/microsoft/PowerToys/blob/yuleng/display/pr/3/doc/devdocs/modules/powerdisplay/design.md

AOT compatibility:
I designed this module for AOT from the start, so I'm pretty sure at
least 95% of it is AOT compatible. But unfortunately, PowerToys still
have a AOT blocker to block this module publish with AOT.

Currently PowerToys will check the .net file version (file version not
lib version) to avoid crash. So, all modules should reference Common.UI
or add UseWPF to avoid overwrite the .net file with different version
(which may cause crash).

Todo:
- [ ] BugBash
- [ ] Icon
- [ ] IdentifyWindow UI improvement


Demo

Main UI:
<img width="546" height="671" alt="image"
src="https://github.com/user-attachments/assets/b0ad9ac5-8000-4365-a192-ab8c2d66d4f1"
/>

Input Source:
<img width="536" height="674" alt="image"
src="https://github.com/user-attachments/assets/80f9ccd7-4f8c-4201-b177-cc86c5bcc9e3"
/>


Settings UI:
<img width="1581" height="1191" alt="image"
src="https://github.com/user-attachments/assets/6a82e4bb-8f96-4f28-abf9-d7c45e1c8ef7"
/>

<img width="1525" height="1146" alt="image"
src="https://github.com/user-attachments/assets/aae81e65-08fd-453a-bf52-02a74f2fdea0"
/>



Closes: 
#42942
#42678
#41117
#38109
#35564
#34932
#28500
#1052
#18149

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #1052
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [x] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: moooyo <lengyuchn@gmail.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 13:53:25 +08:00

6.6 KiB

MCCS Capabilities String Parser - Recursive Descent Design

Overview

This document describes the recursive descent parser implementation for DDC/CI MCCS (Monitor Control Command Set) capabilities strings.

Attention!

This document and the code implement are generated by Copilot.

Grammar Definition (BNF)

capabilities      ::= ['('] segment* [')']
segment           ::= identifier '(' segment_content ')'
segment_content   ::= text | vcp_entries | hex_list
vcp_entries       ::= vcp_entry*
vcp_entry         ::= hex_byte [ '(' hex_list ')' ]
hex_list          ::= hex_byte*
hex_byte          ::= [0-9A-Fa-f]{2}
identifier        ::= [a-z_A-Z]+
text              ::= [^()]+

Example Input

(prot(monitor)type(lcd)model(PD3220U)cmds(01 02 03 07)vcp(10 12 14(04 05 06) 16 60(11 12 0F) DC DF)mccs_ver(2.2)vcpname(F0(Custom Setting)))

Parser Architecture

Component Hierarchy

MccsCapabilitiesParser (main parser)
├── ParseCapabilities()      → MccsParseResult
├── ParseSegment()           → ParsedSegment?
├── ParseBalancedContent()   → string
├── ParseIdentifier()        → ReadOnlySpan<char>
├── ApplySegment()           → void
│   ├── ParseHexList()       → List<byte>
│   ├── ParseVcpEntries()    → Dictionary<byte, VcpCodeInfo>
│   └── ParseVcpNames()      → void
│
├── VcpEntryParser (sub-parser for vcp() content)
│   └── TryParseEntry()      → VcpEntry
│
├── VcpNameParser (sub-parser for vcpname() content)
│   └── TryParseEntry()      → (byte code, string name)
│
└── WindowParser (sub-parser for windowN() content)
    ├── Parse()              → WindowCapability
    └── ParseSubSegment()    → (name, content)?

Design Principles

  1. ref struct for Zero Allocation

    • Main parser uses ref struct to avoid heap allocation
    • Works with ReadOnlySpan<char> for efficient string slicing
    • No intermediate string allocations during parsing
  2. Recursive Descent Pattern

    • Each grammar rule has a corresponding parse method
    • Methods call each other recursively for nested structures
    • Single-character lookahead via Peek()
  3. Error Recovery

    • Errors are accumulated, not thrown
    • Parser attempts to continue after errors
    • Returns partial results when possible
  4. Sub-parsers for Specialized Content

    • VcpEntryParser for VCP code entries
    • VcpNameParser for custom VCP names
    • Each sub-parser handles its own grammar subset

Parse Methods Detail

ParseCapabilities()

Entry point. Handles optional outer parentheses and iterates through segments.

private MccsParseResult ParseCapabilities()
{
    // Handle optional outer parens
    // while (!IsAtEnd()) { ParseSegment() }
    // Return result with accumulated errors
}

ParseSegment()

Parses a single identifier(content) segment.

private ParsedSegment? ParseSegment()
{
    // 1. ParseIdentifier()
    // 2. Expect '('
    // 3. ParseBalancedContent()
    // 4. Expect ')'
}

ParseBalancedContent()

Extracts content between balanced parentheses, handling nested parens.

private string ParseBalancedContent()
{
    int depth = 1;
    while (depth > 0) {
        if (char == '(') depth++;
        if (char == ')') depth--;
    }
}

ParseVcpEntries()

Delegates to VcpEntryParser for the specialized VCP entry grammar.

vcp_entry ::= hex_byte [ '(' hex_list ')' ]

Examples:
- "10"            code=0x10, values=[]
- "14(04 05 06)"  code=0x14, values=[4, 5, 6]
- "60(11 12 0F)"  code=0x60, values=[0x11, 0x12, 0x0F]

Comparison with Other Approaches

Approach Pros Cons
Recursive Descent (this) Clear structure, handles nesting, extensible More code
Regex (DDCSharp) Concise Hard to debug, limited nesting
Mixed (original) Pragmatic Inconsistent, hard to maintain

Performance Characteristics

  • Time Complexity: O(n) where n = input length
  • Space Complexity: O(1) for parsing + O(m) for output where m = number of VCP codes
  • Allocations: Minimal - only for output structures

Supported Segments

Segment Description Parser
prot(...) Protocol type Direct assignment
type(...) Display type (lcd/crt) Direct assignment
model(...) Model name Direct assignment
cmds(...) Supported commands ParseHexList
vcp(...) VCP code entries VcpEntryParser
mccs_ver(...) MCCS version Direct assignment
vcpname(...) Custom VCP names VcpNameParser
windowN(...) PIP/PBP window capabilities WindowParser

Window Segment Format

The windowN segment (where N is 1, 2, 3, etc.) describes PIP/PBP window capabilities:

window1(type(PIP) area(25 25 1895 1175) max(640 480) min(10 10) window(10))
Sub-field Format Description
type type(PIP) or type(PBP) Window type (Picture-in-Picture or Picture-by-Picture)
area area(x1 y1 x2 y2) Window area coordinates in pixels
max max(width height) Maximum window dimensions
min min(width height) Minimum window dimensions
window window(id) Window identifier

All sub-fields are optional; missing fields default to zero values.

Error Handling

public readonly struct ParseError
{
    public int Position { get; }    // Character position
    public string Message { get; }  // Human-readable error
}

public sealed class MccsParseResult
{
    public VcpCapabilities Capabilities { get; }
    public IReadOnlyList<ParseError> Errors { get; }
    public bool HasErrors => Errors.Count > 0;
    public bool IsValid => !HasErrors && Capabilities.SupportedVcpCodes.Count > 0;
}

Usage Example

// Parse capabilities string
var result = MccsCapabilitiesParser.Parse(capabilitiesString);

if (result.IsValid)
{
    var caps = result.Capabilities;
    Console.WriteLine($"Model: {caps.Model}");
    Console.WriteLine($"MCCS Version: {caps.MccsVersion}");
    Console.WriteLine($"VCP Codes: {caps.SupportedVcpCodes.Count}");
}

if (result.HasErrors)
{
    foreach (var error in result.Errors)
    {
        Console.WriteLine($"Parse error at {error.Position}: {error.Message}");
    }
}

Edge Cases Handled

  1. Missing outer parentheses (Apple Cinema Display)
  2. No spaces between hex bytes (010203 vs 01 02 03)
  3. Nested parentheses in VCP values
  4. Unknown segments (logged but not fatal)
  5. Malformed input (partial results returned)