<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: OsirisFrik</title>
    <description>The latest articles on DEV Community by OsirisFrik (@osirisfrik).</description>
    <link>https://dev.to/osirisfrik</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F59708%2F1744cf57-632c-4917-84bc-874766e703ca.jpg</url>
      <title>DEV Community: OsirisFrik</title>
      <link>https://dev.to/osirisfrik</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/osirisfrik"/>
    <language>en</language>
    <item>
      <title>🖥️ Weekend Project: A CLI to Manage Multi-Monitor Layouts on Windows</title>
      <dc:creator>OsirisFrik</dc:creator>
      <pubDate>Mon, 30 Mar 2026 22:02:55 +0000</pubDate>
      <link>https://dev.to/osirisfrik/weekend-project-a-cli-to-manage-multi-monitor-layouts-on-windows-5a7d</link>
      <guid>https://dev.to/osirisfrik/weekend-project-a-cli-to-manage-multi-monitor-layouts-on-windows-5a7d</guid>
      <description>&lt;p&gt;This weekend I built a small tool to solve a very real (and annoying) problem…&lt;/p&gt;

&lt;p&gt;I’ve been working with 4 monitors for a while now. My setup includes an ultrawide monitor as primary when I work, but, when I want play videogames I preffer use a 24" monitor (when isn't a simrace game), so, here are two cases were I need change my screens layout.&lt;/p&gt;

&lt;p&gt;Every time I move between those setups, I have to manually rearrange monitors in Windows.&lt;/p&gt;

&lt;p&gt;Doing that repeatedly? Not fun 😅, some times I do that 3 times peer day 🫠.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 Looking for a solution
&lt;/h2&gt;

&lt;p&gt;I tried to find an existing tool to automate monitor layouts, but nothing quite matched what I needed.&lt;/p&gt;

&lt;p&gt;So I decided to build a simple CLI:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 wsm — Windows Screen Manager&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A lightweight tool to save and restore monitor configurations as profiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚡ What it does
&lt;/h2&gt;

&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save your current monitor layout&lt;/li&gt;
&lt;li&gt;Store it as a profile (YAML or JSON)&lt;/li&gt;
&lt;li&gt;Restore it instantly with a command
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wsm save work &lt;span class="nt"&gt;--&lt;/span&gt; save current layout
wsm load game &lt;span class="nt"&gt;--&lt;/span&gt; load saved layout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you frequently change your screen layout, this can save you a lot of time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wsm list
Saved profiles &lt;span class="k"&gt;in&lt;/span&gt; ~/.wsm-profiles:
  game — 4 monitor&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;
    Display 1: GBT340A                       3440x1440 @ 60Hz  pos &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-1515&lt;/span&gt;, &lt;span class="nt"&gt;-1440&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  primary: &lt;span class="nb"&gt;false
    &lt;/span&gt;Display 2: BNQ78E7                       1920x1080 @ 60Hz  pos &lt;span class="o"&gt;(&lt;/span&gt;    0,     0&lt;span class="o"&gt;)&lt;/span&gt;  primary: &lt;span class="nb"&gt;true
    &lt;/span&gt;Display 3: BNQ78E7                       1920x1080 @ 60Hz  pos &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-1928&lt;/span&gt;,     0&lt;span class="o"&gt;)&lt;/span&gt;  primary: &lt;span class="nb"&gt;false
    &lt;/span&gt;Display 5: ACI1643                       1080x1920 @ 60Hz  pos &lt;span class="o"&gt;(&lt;/span&gt; 1925,  &lt;span class="nt"&gt;-916&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  primary: &lt;span class="nb"&gt;false
  &lt;/span&gt;work — 4 monitor&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;
    Display 1: GBT340A                       3440x1440 @ 60Hz  pos &lt;span class="o"&gt;(&lt;/span&gt;    0,     0&lt;span class="o"&gt;)&lt;/span&gt;  primary: &lt;span class="nb"&gt;true
    &lt;/span&gt;Display 2: BNQ78E7                       1080x1920 @ 60Hz  pos &lt;span class="o"&gt;(&lt;/span&gt; 3440,  &lt;span class="nt"&gt;-562&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  primary: &lt;span class="nb"&gt;false
    &lt;/span&gt;Display 3: BNQ78E7                       1920x1080 @ 60Hz  pos &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-1920&lt;/span&gt;,   350&lt;span class="o"&gt;)&lt;/span&gt;  primary: &lt;span class="nb"&gt;false
    &lt;/span&gt;Display 5: ACI1643                       1080x1920 @ 60Hz  pos &lt;span class="o"&gt;(&lt;/span&gt; 4520,  &lt;span class="nt"&gt;-552&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  primary: &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📦 How it works
&lt;/h2&gt;

&lt;p&gt;Under the hood, the CLI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads all active monitors&lt;/li&gt;
&lt;li&gt;Captures:&lt;/li&gt;
&lt;li&gt;resolution&lt;/li&gt;
&lt;li&gt;refresh rate&lt;/li&gt;
&lt;li&gt;position in virtual desktop&lt;/li&gt;
&lt;li&gt;primary display&lt;/li&gt;
&lt;li&gt;orientation&lt;/li&gt;
&lt;li&gt;Stores everything in ~/.wsm-profiles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example YAML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;profiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;office&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;monitors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;device_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\\.\DISPLAY1'&lt;/span&gt;
        &lt;span class="na"&gt;friendly_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Display&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;GS34WQCA'&lt;/span&gt;
        &lt;span class="na"&gt;position_x&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
        &lt;span class="na"&gt;position_y&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3440&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1440&lt;/span&gt;
        &lt;span class="na"&gt;refresh_rate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;144&lt;/span&gt;
        &lt;span class="na"&gt;bits_per_pel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;32&lt;/span&gt;
        &lt;span class="na"&gt;orientation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
        &lt;span class="na"&gt;is_primary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;device_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\\.\DISPLAY2'&lt;/span&gt;
        &lt;span class="na"&gt;friendly_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Display&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;2:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;P2419H'&lt;/span&gt;
        &lt;span class="na"&gt;position_x&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3440&lt;/span&gt;
        &lt;span class="na"&gt;position_y&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;180&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1920&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1080&lt;/span&gt;
        &lt;span class="na"&gt;refresh_rate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
        &lt;span class="na"&gt;bits_per_pel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;32&lt;/span&gt;
        &lt;span class="na"&gt;orientation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
        &lt;span class="na"&gt;is_primary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 Atomic configuration changes&lt;/p&gt;

&lt;p&gt;One important detail: applying layouts is done atomically.&lt;/p&gt;

&lt;p&gt;Instead of changing monitors one by one (which can cause flickering or broken states), the tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stages all changes using Windows APIs&lt;/li&gt;
&lt;li&gt;commits them in a single operation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This avoids intermediate invalid configurations.&lt;/p&gt;

&lt;p&gt;Also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The primary display is always staged first&lt;/li&gt;
&lt;li&gt;Missing/disconnected monitors are handled gracefully&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧪 Built with AI
&lt;/h2&gt;

&lt;p&gt;I built this using Claude, and had a working version in under an hour.&lt;/p&gt;

&lt;p&gt;That said, it wasn’t just “generate and done”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There were bugs&lt;/li&gt;
&lt;li&gt;Edge cases around Windows APIs&lt;/li&gt;
&lt;li&gt;Iterations to get things stable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI helped a lot, but I still had to guide it and understand what was going on.&lt;/p&gt;

&lt;h2&gt;
  
  
  📈 Iterating on the idea
&lt;/h2&gt;

&lt;p&gt;The first version was pretty basic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON files&lt;/li&gt;
&lt;li&gt;One file per configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I improved it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;YAML as default (more readable)&lt;/li&gt;
&lt;li&gt;Profiles instead of separate files&lt;/li&gt;
&lt;li&gt;Still supports JSON if needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎛️ Stream Deck integration
&lt;/h2&gt;

&lt;p&gt;Running commands is fine… but I wanted something faster.&lt;/p&gt;

&lt;p&gt;So I built a plugin for the Elgato Stream Deck, this can run anny command on powershell or cmd. &lt;/p&gt;

&lt;p&gt;Now I can switch my entire setup with a single button press.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx21v9b12vxm7ka53fkk3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx21v9b12vxm7ka53fkk3.png" alt=" " width="800" height="723"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 A reminder about AI
&lt;/h2&gt;

&lt;p&gt;I’ve been in software development for about 10 years.&lt;/p&gt;

&lt;p&gt;Back then, the advice was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“It’s fine to copy from Stack Overflow… but understand what you’re copying.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That still applies today.&lt;/p&gt;

&lt;p&gt;AI is powerful, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It makes mistakes&lt;/li&gt;
&lt;li&gt;It hallucinates&lt;/li&gt;
&lt;li&gt;It doesn’t replace understanding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If something breaks, you still need to know how to fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Try it out
&lt;/h2&gt;

&lt;p&gt;If you’re on Windows and frequently change your monitor layout, this might be useful:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/OsirisFrik/windows-screen-manager" rel="noopener noreferrer"&gt;https://github.com/OsirisFrik/windows-screen-manager&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>cli</category>
      <category>windows</category>
      <category>scoop</category>
    </item>
  </channel>
</rss>
