Skip to content

Commit b01138e

Browse files
committed
Port the Processing driver program to C++
I based this off of the original Processing program, but it's much lighter weight since it can take advantage of some native DirectX APIs for the screen sampling. See ReadMe.md for more details.
1 parent c9d6a28 commit b01138e

17 files changed

Lines changed: 1519 additions & 0 deletions

CppDriver/AdaLight.cpp

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
// AdaLight.cpp
2+
//
3+
// This is a port of the Processing (Java) driver for the AdaLight project to a C++ Windows Application.
4+
// This version runs without any UI, so you might want to use the preview UI in the original Processing
5+
// application first to figure out your settings and then copy those variables to settings.* and recompile.
6+
//
7+
// AdaLight guide: https://learn.adafruit.com/adalight-diy-ambient-tv-lighting/pieces?view=all#download-and-install
8+
// Serial port programming sample: https://code.msdn.microsoft.com/windowsdesktop/Serial-Port-Sample-e8accf30/sourcecode?fileId=67164&pathId=1394200469
9+
// DirectX 11 C++ references: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476082(v=vs.85).aspx
10+
11+
#pragma region ORIGINAL ADALIGHT.PDE COMMENTS
12+
13+
// "Adalight" is a do-it-yourself facsimile of the Philips Ambilight concept
14+
// for desktop computers and home theater PCs. This is the host PC-side code
15+
// written in Processing, intended for use with a USB-connected Arduino
16+
// microcontroller running the accompanying LED streaming code. Requires one
17+
// or more strands of Digital RGB LED Pixels (Adafruit product ID #322,
18+
// specifically the newer WS2801-based type, strand of 25) and a 5 Volt power
19+
// supply (such as Adafruit #276). You may need to adapt the code and the
20+
// hardware arrangement for your specific display configuration.
21+
// Screen capture adapted from code by Cedrik Kiefer (processing.org forum)
22+
23+
// --------------------------------------------------------------------
24+
// This file is part of Adalight.
25+
26+
// Adalight is free software: you can redistribute it and/or modify
27+
// it under the terms of the GNU Lesser General Public License as
28+
// published by the Free Software Foundation, either version 3 of
29+
// the License, or (at your option) any later version.
30+
31+
// Adalight is distributed in the hope that it will be useful,
32+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
33+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34+
// GNU Lesser General Public License for more details.
35+
36+
// You should have received a copy of the GNU Lesser General Public
37+
// License along with Adalight. If not, see
38+
// <http://www.gnu.org/licenses/>.
39+
// --------------------------------------------------------------------
40+
41+
#pragma endregion
42+
43+
#include "stdafx.h"
44+
45+
#include <cmath>
46+
#include <string>
47+
#include <sstream>
48+
49+
#include "settings.h"
50+
#include "gamma_correction.h"
51+
#include "serial_buffer.h"
52+
#include "screen_samples.h"
53+
#include "serial_port.h"
54+
55+
// TODO: Put this in some sort of persistence? Create configuration UI? Parse the settings from the command line?
56+
static const settings parameters;
57+
58+
static serial_buffer serial(parameters);
59+
static gamma_correction gamma;
60+
static screen_samples samples(parameters, gamma);
61+
static serial_port port(parameters);
62+
63+
// Reset the LED strip.
64+
static void ResetLEDs()
65+
{
66+
serial.clear();
67+
port.send(serial);
68+
}
69+
70+
// Update the LED strip.
71+
static void UpdateLEDs()
72+
{
73+
samples.take_samples(serial);
74+
port.send(serial);
75+
}
76+
77+
// Hidden window class name
78+
static PCWSTR s_windowClassName = L"AdaLightListener";
79+
80+
static bool s_timerSet = false;
81+
82+
// Start the timer if it's not running.
83+
static void StartTimer(HWND hwnd)
84+
{
85+
if (!s_timerSet)
86+
{
87+
SetTimer(hwnd, 0, parameters.delay, nullptr);
88+
s_timerSet = true;
89+
}
90+
}
91+
92+
// Stop the timer if it is running.
93+
static void StopTimer(HWND hwnd)
94+
{
95+
if (s_timerSet)
96+
{
97+
KillTimer(hwnd, 0);
98+
s_timerSet = false;
99+
100+
ResetLEDs();
101+
}
102+
}
103+
104+
static bool s_connectedToConsole = !GetSystemMetrics(SM_REMOTESESSION);
105+
106+
// Handle attaching and reattaching to the console.
107+
static void AttachToConsole(HWND hwnd)
108+
{
109+
if (s_connectedToConsole)
110+
{
111+
port.open();
112+
samples.create_resources();
113+
StartTimer(hwnd);
114+
}
115+
}
116+
117+
// Handle detaching from the console.
118+
static void DetachFromConsole(HWND hwnd)
119+
{
120+
if (s_connectedToConsole)
121+
{
122+
StopTimer(hwnd);
123+
samples.free_resources();
124+
port.close();
125+
}
126+
}
127+
128+
// Process window messages to the hidden window.
129+
static LRESULT CALLBACK HiddenWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
130+
{
131+
switch (message)
132+
{
133+
case WM_CREATE:
134+
WTSRegisterSessionNotification(hwnd, NOTIFY_FOR_THIS_SESSION);
135+
break;
136+
137+
case WM_DESTROY:
138+
StopTimer(hwnd);
139+
WTSUnRegisterSessionNotification(hwnd);
140+
PostQuitMessage(0);
141+
break;
142+
143+
case WM_WTSSESSION_CHANGE:
144+
switch (wParam)
145+
{
146+
case WTS_CONSOLE_CONNECT:
147+
s_connectedToConsole = true;
148+
AttachToConsole(hwnd);
149+
break;
150+
151+
case WTS_CONSOLE_DISCONNECT:
152+
DetachFromConsole(hwnd);
153+
s_connectedToConsole = false;
154+
break;
155+
156+
case WTS_SESSION_LOCK:
157+
DetachFromConsole(hwnd);
158+
break;
159+
160+
case WTS_SESSION_UNLOCK:
161+
AttachToConsole(hwnd);
162+
break;
163+
164+
default:
165+
break;
166+
}
167+
168+
break;
169+
170+
case WM_DISPLAYCHANGE:
171+
if (s_connectedToConsole)
172+
{
173+
samples.free_resources();
174+
samples.create_resources();
175+
}
176+
break;
177+
178+
case WM_TIMER:
179+
if (samples.empty())
180+
{
181+
samples.create_resources();
182+
}
183+
184+
UpdateLEDs();
185+
break;
186+
187+
default:
188+
return DefWindowProcW(hwnd, message, wParam, lParam);
189+
}
190+
191+
return 0;
192+
}
193+
194+
// Show a message box for any errors.
195+
static void DisplayLastError()
196+
{
197+
DWORD errorCode = GetLastError();
198+
PWSTR errorString = nullptr;
199+
200+
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, errorCode, 0, reinterpret_cast<PWSTR>(&errorString), 0, nullptr);
201+
MessageBoxW(HWND_DESKTOP, errorString, nullptr, MB_ICONERROR);
202+
LocalFree(reinterpret_cast<HLOCAL>(errorString));
203+
}
204+
205+
// Run the program.
206+
int WINAPI wWinMain(HINSTANCE hinstExe, HINSTANCE /*hinstPrev*/, PWSTR /*wzCmdLine*/, int /*nCmdShow*/)
207+
{
208+
// TODO: Run in an interactive service? Tough to deal with user switching. Need to handle WM_DISPLAYCHANGE with a hidden top-level window.
209+
// Windows Service C++ sample: https://www.codeproject.com/articles/499465/simple-windows-service-in-cplusplus
210+
211+
// Create the hidden window.
212+
WNDCLASSEXW windowClass = {
213+
sizeof(windowClass), // cbSize
214+
0, // style
215+
HiddenWindowProc, // lpfnWndProc
216+
0, // cbClsExtra
217+
0, // cbWndExtra
218+
hinstExe, // hInstance
219+
NULL, // hIcon
220+
NULL, // hCursor
221+
NULL, // hbrBackground
222+
nullptr, // lpszMenuName
223+
s_windowClassName, // lpszClassName
224+
NULL // hIconSm
225+
};
226+
227+
if (!RegisterClassExW(&windowClass))
228+
{
229+
DisplayLastError();
230+
return 1;
231+
}
232+
233+
HWND hwnd = CreateWindowExW(0, s_windowClassName, nullptr, 0, 0, 0, 0, 0, HWND_DESKTOP, NULL, hinstExe, nullptr);
234+
235+
// Start processing messages/timers.
236+
AttachToConsole(hwnd);
237+
238+
BOOL result;
239+
MSG msg;
240+
241+
while (result = GetMessageW(&msg, NULL, 0, 0))
242+
{
243+
if (-1 == result)
244+
{
245+
DisplayLastError();
246+
return 2;
247+
}
248+
249+
TranslateMessage(&msg);
250+
DispatchMessageW(&msg);
251+
}
252+
253+
return msg.wParam;
254+
}

0 commit comments

Comments
 (0)