Tonight I decided to combine two things I greatly enjoy: Sam Adams Boston Lager and building tiny applications, in a personal experiment to see if my Ballmer’s Peak was different in C# than in C++.
I have this projector in my living room hooked up to my PC in my closet through a wicked long HDMI cable. Watching shows off the PC like this rocks, but all the clicking I have to do to set it up doesn’t. Set the display mode to 1280×720, disable the screensaver, and make sure my power management settings don’t turn off my monitor. Then when I’m done watching I have to put everything back. Blah.
Tonight I spent a couple hours writing a C# app that does this for me. It runs in the systray. When I press Windows+~, it toggles between “projector mode” (1280×720, no screensaver, no monitor powerdown), and “monitor mode” (1600×1200, screensaver and monitor powerdown enabled). I can also right click it and force it into one mode or the other.
There’s all sorts of wackiness at play in this little app. For starters, there’s the method by which I toggle between 1600×1200 and 1280×720.
I have an nVidia card with 2 DVI outs; one goes to my monitor, one to the projector. To windows they appear as two monitors, which I’ve set up in “Clone” mode. The nVidia drivers are smart; they remember the last resolution used for a given primary display. So, I made my LCD panel the primary display and set its resolution to 1600×1200. I hit Apply, then went back and made the projector the primary, and set its resolution to 1280×720. Now simply swapping the primary display also switches the resolution. Cool. As it turns out this is the only viable way to do it, because you can’t switch your primary to a resolution it doesn’t support, and my LCD doesn’t support my projector’s native 720p.
Next step: automating this swap. At first I thought I’d have to fake a right click on the little green nVidia icon in the systray, which is, at best, a real pain in the ass to do in code, and at worst, impossible. Glory be! Some internet searching revealed a command line interface, where you list off the connections in order, primary first.
So to make the first DVI connection (aka (D)igital connection “A”) the primary device:
Rundll32 nvcpl.dll,dtcfg setview 0 clone DA DB
To make the second DVI connection, aka (D)igital connection B, the primary:
Rundll32 nvcpl.dll,dtcfg setview 0 clone DB DA
There are other options outlined in this post. AA is analog connection A, etc.
OK cool. Beer one and problem one down, onto the second element of wackiness: disabling the screensaver and monitor poweroff. I thought in C# this would be a no-brainer: some properties somewhere that you make false, but it’s not. The only way I found to do it was through crap P/Invoke and overriding your WinProc. Oh well, maybe it’s easier in .NET 3.0. For now it’s just a lot of googling for function signatures and WM_xxx ID numbers.
Registering a global hotkey is easy; for this particular app the one snag was that Windows associates a hotkey with a window handle, so if you are doing things that will invalidate the Handle on the form you registered the hotkey from – things like hiding/showing the form (d’oh!) – then you have to re-register the hotkey.
The rest of the code is decipherable even after two beers. Doing sysTray apps in C# is a joy compared to C++. As is launching processes and waiting for them to finish. After that it was all sizzle (form fading out, and the most important part, the about box!)
I hope it’s useful!
No install program, no warranties, a license to do anything with it – just drop it wherever, and make a shortcut to it inside your Startup folder. Icon by Copland.
[...] For full details see forum post. (Tip was found via Cuttlefish Industries). [...]