├── smx-config ├── .gitignore ├── installer │ ├── .gitignore │ ├── build.bat │ └── SMX.nsi ├── window icon.ico ├── Resources │ ├── DIP.png │ ├── pressed.gif │ ├── released.gif │ ├── sensor_up.png │ ├── DIP labels.png │ ├── sensor_down.png │ ├── sensor_left.png │ ├── window icon.ico │ ├── window icon.png │ ├── sensor_right.png │ ├── window icon grey.ico │ ├── window icon grey.png │ └── threshold_warning.png ├── App.xaml ├── ProgressWindow.xaml ├── Properties │ ├── Settings.settings │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Settings.Designer.cs │ └── Resources.resx ├── ProgressWindow.xaml.cs ├── App.config ├── SetCustomSensors.xaml.cs ├── SetCustomSensors.xaml ├── ConfigPresets.cs ├── SMXConfig.csproj ├── DoubleSlider.cs ├── App.xaml.cs └── CurrentSMXDevice.cs ├── .gitignore ├── docs ├── icon.png ├── logo.png ├── style.css └── index.html ├── sdk ├── Windows │ ├── .gitignore │ ├── SMXConfigPacket.h │ ├── SMXHelperThread.h │ ├── SMXDeviceSearch.h │ ├── SMXThread.h │ ├── SMXHelperThread.cpp │ ├── SMXThread.cpp │ ├── SMXDeviceSearchThreaded.h │ ├── update-build-version.bat │ ├── SMXPanelAnimationUpload.h │ ├── SMXGif.h │ ├── SMXPanelAnimation.h │ ├── SMXDeviceSearchThreaded.cpp │ ├── SMXManager.h │ ├── SMX.vcxproj.filters │ ├── Helpers.h │ ├── SMXDeviceConnection.h │ ├── SMX.cpp │ ├── SMXDevice.h │ ├── SMXDeviceSearch.cpp │ ├── SMX.vcxproj │ ├── SMXConfigPacket.cpp │ ├── Helpers.cpp │ └── SMXGif.cpp └── SMX.h ├── .editorconfig ├── README.md ├── sample ├── SMXSample.vcxproj.filters ├── SMXSample.cpp └── SMXSample.vcxproj ├── LICENSE.txt └── SMX.sln /smx-config/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | *.user 3 | build 4 | obj 5 | out 6 | -------------------------------------------------------------------------------- /smx-config/installer/.gitignore: -------------------------------------------------------------------------------- 1 | InstallSMXConfig.exe 2 | -------------------------------------------------------------------------------- /docs/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/docs/icon.png -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/docs/logo.png -------------------------------------------------------------------------------- /sdk/Windows/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore updates to the auto-generated build version. 2 | SMXBuildVersion.h 3 | 4 | -------------------------------------------------------------------------------- /smx-config/installer/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | "C:\Program Files (x86)\NSIS\makensis.exe" SMX.nsi 3 | pause 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | 3 | [*] 4 | indent_style = space 5 | 6 | [*.{cpp,cs,h}] 7 | indent_size = 4 8 | 9 | -------------------------------------------------------------------------------- /smx-config/window icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/window icon.ico -------------------------------------------------------------------------------- /smx-config/Resources/DIP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/DIP.png -------------------------------------------------------------------------------- /smx-config/Resources/pressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/pressed.gif -------------------------------------------------------------------------------- /smx-config/Resources/released.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/released.gif -------------------------------------------------------------------------------- /smx-config/Resources/sensor_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/sensor_up.png -------------------------------------------------------------------------------- /smx-config/Resources/DIP labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/DIP labels.png -------------------------------------------------------------------------------- /smx-config/Resources/sensor_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/sensor_down.png -------------------------------------------------------------------------------- /smx-config/Resources/sensor_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/sensor_left.png -------------------------------------------------------------------------------- /smx-config/Resources/window icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/window icon.ico -------------------------------------------------------------------------------- /smx-config/Resources/window icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/window icon.png -------------------------------------------------------------------------------- /smx-config/Resources/sensor_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/sensor_right.png -------------------------------------------------------------------------------- /smx-config/Resources/window icon grey.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/window icon grey.ico -------------------------------------------------------------------------------- /smx-config/Resources/window icon grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/window icon grey.png -------------------------------------------------------------------------------- /smx-config/Resources/threshold_warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steprevolution/stepmaniax-sdk/HEAD/smx-config/Resources/threshold_warning.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is the SDK for StepManiaX platform development. 2 | 3 | See [the StepManiaX website](https://stepmaniax.com) and the [documentation](https://steprevolution.github.io/stepmaniax-sdk/) 4 | for info. 5 | 6 | SDK support: [sdk@stepmaniax.com](mailto:sdk@stepmaniax.com) 7 | 8 | -------------------------------------------------------------------------------- /smx-config/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sdk/Windows/SMXConfigPacket.h: -------------------------------------------------------------------------------- 1 | #ifndef SMXConfigPacket_h 2 | #define SMXConfigPacket_h 3 | 4 | #include 5 | using namespace std; 6 | 7 | #include "../SMX.h" 8 | 9 | void ConvertToNewConfig(const vector &oldConfig, SMXConfig &newConfig); 10 | void ConvertToOldConfig(const SMXConfig &newConfig, vector &oldConfigData); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 1em; 3 | max-width: 1000px; 4 | font-family: "Segoe UI",Helvetica,Arial,sans-serif; 5 | margin-left: auto; 6 | margin-right: auto; 7 | line-height: 1.5; 8 | } 9 | 10 | h1 { 11 | font-size: 2em; 12 | } 13 | h1, h2 { 14 | border-bottom: 1px solid #eeeeee; 15 | padding-bottom: .3em; 16 | } 17 | 18 | h3.ref { 19 | display: block; 20 | font-family: monospace; 21 | background-color: #008080; 22 | color: #FFFFFF; 23 | padding: .5em; 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /sample/SMXSample.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | 10 | 11 | Source Files 12 | 13 | 14 | -------------------------------------------------------------------------------- /smx-config/ProgressWindow.xaml: -------------------------------------------------------------------------------- 1 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /sdk/Windows/SMXHelperThread.h: -------------------------------------------------------------------------------- 1 | #ifndef SMXHelperThread_h 2 | #define SMXHelperThread_h 3 | 4 | #include "Helpers.h" 5 | #include "SMXThread.h" 6 | 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | 12 | namespace SMX 13 | { 14 | class SMXHelperThread: public SMXThread 15 | { 16 | public: 17 | SMXHelperThread(const string &sThreadName); 18 | 19 | // Call func asynchronously from the helper thread. 20 | void RunInThread(function func); 21 | 22 | private: 23 | void ThreadMain(); 24 | 25 | // Helper threads use their independent lock. 26 | SMX::Mutex m_Lock; 27 | vector> m_FunctionsToCall; 28 | }; 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /sdk/Windows/SMXDeviceSearch.h: -------------------------------------------------------------------------------- 1 | #ifndef SMXDeviceSearch_h 2 | #define SMXDeviceSearch_h 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | 11 | #include "Helpers.h" 12 | 13 | namespace SMX { 14 | 15 | class SMXDeviceSearch 16 | { 17 | public: 18 | // Return a list of connected devices. If the same device stays connected and this 19 | // is called multiple times, the same handle will be returned. 20 | vector> GetDevices(wstring &error); 21 | 22 | // After a device is opened and then closed, tell this class that the device was closed. 23 | // We'll discard our record of it, so we'll notice a new device plugged in on the same 24 | // path. 25 | void DeviceWasClosed(shared_ptr pDevice); 26 | 27 | private: 28 | set m_setLastDevicePaths; 29 | map> m_Devices; 30 | }; 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /smx-config/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | 8 | 9 | 10 | 11 | 12 | False 13 | 14 | 15 | False 16 | 17 | 18 | False 19 | 20 | 21 | -------------------------------------------------------------------------------- /sdk/Windows/SMXThread.h: -------------------------------------------------------------------------------- 1 | #ifndef SMXThread_h 2 | #define SMXThread_h 3 | 4 | // A base class for a thread. 5 | #include "Helpers.h" 6 | #include 7 | 8 | namespace SMX 9 | { 10 | 11 | class SMXThread 12 | { 13 | public: 14 | SMXThread(SMX::Mutex &lock); 15 | 16 | // Raise the priority of the thread. 17 | void SetHighPriority(bool bHighPriority); 18 | 19 | // Start the thread, giving it a name for debugging. 20 | void Start(std::string name); 21 | 22 | // Shut down the thread. This function won't return until the thread 23 | // has been stopped. 24 | void Shutdown(); 25 | 26 | // Return true if this is the calling thread. 27 | bool IsCurrentThread() const; 28 | 29 | // The derived class implements this. 30 | virtual void ThreadMain() = 0; 31 | 32 | protected: 33 | static DWORD WINAPI ThreadMainStart(void *self); 34 | 35 | SMX::Mutex &m_Lock; 36 | SMX::Event m_Event; 37 | bool m_bShutdown = false; 38 | 39 | private: 40 | HANDLE m_hThread = INVALID_HANDLE_VALUE; 41 | DWORD m_iThreadId = 0; 42 | }; 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Step Revolution LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /sdk/Windows/SMXHelperThread.cpp: -------------------------------------------------------------------------------- 1 | #include "SMXHelperThread.h" 2 | using namespace SMX; 3 | 4 | SMX::SMXHelperThread::SMXHelperThread(const string &sThreadName): 5 | SMXThread(m_Lock) 6 | { 7 | Start(sThreadName); 8 | } 9 | 10 | void SMX::SMXHelperThread::ThreadMain() 11 | { 12 | m_Lock.Lock(); 13 | while(true) 14 | { 15 | vector> funcs; 16 | swap(m_FunctionsToCall, funcs); 17 | 18 | // If we're shutting down and have no more functions to call, stop. 19 | if(funcs.empty() && m_bShutdown) 20 | break; 21 | 22 | // Unlock while we call the queued functions. 23 | m_Lock.Unlock(); 24 | for(auto &func: funcs) 25 | func(); 26 | m_Lock.Lock(); 27 | 28 | m_Event.Wait(250); 29 | } 30 | m_Lock.Unlock(); 31 | } 32 | 33 | void SMX::SMXHelperThread::RunInThread(function func) 34 | { 35 | m_Lock.AssertNotLockedByCurrentThread(); 36 | 37 | // Add func to the list, and poke the event to wake up the thread if needed. 38 | m_Lock.Lock(); 39 | m_FunctionsToCall.push_back(func); 40 | m_Event.Set(); 41 | m_Lock.Unlock(); 42 | } 43 | -------------------------------------------------------------------------------- /sdk/Windows/SMXThread.cpp: -------------------------------------------------------------------------------- 1 | #include "SMXThread.h" 2 | 3 | using namespace std; 4 | using namespace SMX; 5 | 6 | SMXThread::SMXThread(Mutex &lock): 7 | m_Lock(lock), 8 | m_Event(lock) 9 | { 10 | } 11 | 12 | void SMX::SMXThread::SetHighPriority(bool bHighPriority) 13 | { 14 | if(m_hThread == INVALID_HANDLE_VALUE) 15 | throw exception("SetHighPriority called while the thread isn't running"); 16 | 17 | SetThreadPriority(m_hThread, THREAD_PRIORITY_HIGHEST); 18 | } 19 | 20 | bool SMX::SMXThread::IsCurrentThread() const 21 | { 22 | return GetCurrentThreadId() == m_iThreadId; 23 | } 24 | 25 | void SMXThread::Start(string name) 26 | { 27 | // Start the thread. 28 | m_hThread = CreateThread(NULL, 0, ThreadMainStart, this, 0, &m_iThreadId); 29 | SMX::SetThreadName(m_iThreadId, name); 30 | } 31 | 32 | void SMXThread::Shutdown() 33 | { 34 | m_Lock.AssertNotLockedByCurrentThread(); 35 | 36 | // Shut down the thread and wait for it to exit. 37 | m_bShutdown = true; 38 | m_Event.Set(); 39 | 40 | WaitForSingleObject(m_hThread, INFINITE); 41 | m_hThread = INVALID_HANDLE_VALUE; 42 | } 43 | 44 | DWORD WINAPI SMXThread::ThreadMainStart(void *self_) 45 | { 46 | SMXThread *self = (SMXThread *) self_; 47 | self->ThreadMain(); 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /sdk/Windows/SMXDeviceSearchThreaded.h: -------------------------------------------------------------------------------- 1 | #ifndef SMXDeviceSearchThreaded_h 2 | #define SMXDeviceSearchThreaded_h 3 | 4 | #include "Helpers.h" 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | namespace SMX { 11 | 12 | class SMXDeviceSearch; 13 | 14 | // This is a wrapper around SMXDeviceSearch which performs USB scanning in a thread. 15 | // It's free on Win10, but takes a while on Windows 7 (about 8ms), so running it on 16 | // a separate thread prevents random timing errors when reading HID updates. 17 | class SMXDeviceSearchThreaded 18 | { 19 | public: 20 | SMXDeviceSearchThreaded(); 21 | ~SMXDeviceSearchThreaded(); 22 | 23 | // The same interface as SMXDeviceSearch: 24 | vector> GetDevices(); 25 | void DeviceWasClosed(shared_ptr pDevice); 26 | 27 | // Synchronously shut down the thread. 28 | void Shutdown(); 29 | 30 | private: 31 | void UpdateDeviceList(); 32 | 33 | static DWORD WINAPI ThreadMainStart(void *self_); 34 | void ThreadMain(); 35 | 36 | SMX::Mutex m_Lock; 37 | shared_ptr m_pDeviceList; 38 | shared_ptr m_hEvent; 39 | vector> m_apDevices; 40 | vector> m_apClosedDevices; 41 | bool m_bShutdown = false; 42 | HANDLE m_hThread = INVALID_HANDLE_VALUE; 43 | }; 44 | } 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /sdk/Windows/update-build-version.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal ENABLEDELAYEDEXPANSION 3 | 4 | rem A good old 80s batch file, because it's guaranteed to always be available. 5 | rem This assumes git is in the path. 6 | 7 | for /F %%I in ('git describe --always --dirty') do set GITVER=%%I 8 | if "%GITVER%" == "" goto git_error 9 | 10 | rem Replace -dirty with -devel, to indicate builds with uncommitted changes. 11 | set GITVER=%GITVER:-dirty=-devel% 12 | 13 | goto continue 14 | 15 | :git_error 16 | rem If calling git fails for some reason, put a message in the version instead of 17 | rem letting it be blank. 18 | set GITVER=git failed 19 | 20 | :continue 21 | 22 | rem Output the current version to a temp file. 23 | set TEMP_FILE=%TEMP%\temp-SMXBuildVersion.h 24 | set OUTPUT_FILE=SMXBuildVersion.h 25 | 26 | echo // This file is auto-generated by update-build-version.bat. > %TEMP_FILE% 27 | echo. >> %TEMP_FILE% 28 | echo #ifndef SMXBuildVersion_h >> %TEMP_FILE% 29 | echo #define SMXBuildVersion_h >> %TEMP_FILE% 30 | echo. >> %TEMP_FILE% 31 | echo #define SMX_BUILD_VERSION "%GITVER%" >> %TEMP_FILE% 32 | echo. >> %TEMP_FILE% 33 | echo #endif >> %TEMP_FILE% 34 | 35 | rem Compare the temp file to any existing file. Only copy the new file over the old 36 | rem one if it's different, so we don't trigger dependency rebuilds every time. 37 | fc %TEMP_FILE% %OUTPUT_FILE% > nul 38 | if %errorlevel% == 0 goto end 39 | 40 | echo Updated to version %GITVER% 41 | copy %TEMP_FILE% %OUTPUT_FILE% > nul 42 | 43 | :end 44 | -------------------------------------------------------------------------------- /smx-config/ProgressWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Interop; 7 | 8 | namespace smx_config 9 | { 10 | public partial class ProgressWindow: Window 11 | { 12 | private const int GWL_STYLE = -16; 13 | private const int WS_SYSMENU = 0x80000; 14 | [DllImport("user32.dll", SetLastError = true)] 15 | private static extern int GetWindowLong(IntPtr hWnd, int nIndex); 16 | [DllImport("user32.dll")] 17 | private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); 18 | 19 | public ProgressWindow() 20 | { 21 | InitializeComponent(); 22 | 23 | // Hide the window close button, since we can't easily cancel. 24 | Loaded += delegate(object sender, RoutedEventArgs e) { 25 | var hwnd = new WindowInteropHelper(this).Handle; 26 | SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU); 27 | }; 28 | } 29 | 30 | public void SetTotal(int total) 31 | { 32 | ProgressBar.Maximum = total; 33 | } 34 | 35 | public void SetProgress(int progress) 36 | { 37 | ProgressBar.Value = progress; 38 | } 39 | 40 | public override void OnApplyTemplate() 41 | { 42 | base.OnApplyTemplate(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /smx-config/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | False 15 | 16 | 17 | 18 | 19 | 20 | False 21 | 22 | 23 | False 24 | 25 | 26 | False 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /sdk/Windows/SMXPanelAnimationUpload.h: -------------------------------------------------------------------------------- 1 | #ifndef SMXPanelAnimationUpload_h 2 | #define SMXPanelAnimationUpload_h 3 | 4 | #include "SMXPanelAnimation.h" 5 | 6 | // For SMX_API: 7 | #include "../SMX.h" 8 | 9 | // This is used to upload panel animations to the firmware. This is 10 | // only needed for offline animations. For live animations, either 11 | // use SMX_LightsAnimation_SetAuto, or to control lights directly 12 | // (recommended), use SMX_SetLights. animations[] contains the animations 13 | // to load. 14 | // 15 | // Prepare the currently loaded animations to be stored on the pad. 16 | // Return false with an error message on error. 17 | // 18 | // All LightTypes must be loaded before beginning the upload. 19 | // 20 | // If a lights upload is already in progress, returns an error. 21 | SMX_API bool SMX_LightsUpload_PrepareUpload(int pad, SMX_LightsType type, const SMXPanelAnimation animations[9], const char **error); 22 | 23 | typedef void SMX_LightsUploadCallback(int progress, void *pUser); 24 | 25 | // After a successful call to SMX_LightsUpload_PrepareUpload, begin uploading data 26 | // to the master controller for the given pad and animation type. 27 | // 28 | // The callback will be called as the upload progresses, with progress values 29 | // from 0-100. 30 | // 31 | // callback will always be called exactly once with a progress value of 100. 32 | // Once the 100% progress is called, the callback won't be accessed, so the 33 | // caller can safely clean up. This will happen even if the pad disconnects 34 | // partway through the upload. 35 | // 36 | // The callback will be called from the user callback helper thread. 37 | SMX_API void SMX_LightsUpload_BeginUpload(int pad, SMX_LightsUploadCallback callback, void *pUser); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /sdk/Windows/SMXGif.h: -------------------------------------------------------------------------------- 1 | #ifndef SMXGif_h 2 | #define SMXGif_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // This is a simple internal GIF decoder. It's only meant to be used by 9 | // SMXConfig. 10 | namespace SMXGif 11 | { 12 | struct Color 13 | { 14 | uint8_t color[4]; 15 | Color() 16 | { 17 | memset(color, 0, sizeof(color)); 18 | } 19 | 20 | Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) 21 | { 22 | color[0] = r; 23 | color[1] = g; 24 | color[2] = b; 25 | color[3] = a; 26 | } 27 | bool operator==(const Color &rhs) const 28 | { 29 | return !memcmp(color, rhs.color, sizeof(color)); 30 | } 31 | }; 32 | 33 | struct GIFImage 34 | { 35 | int width = 0, height = 0; 36 | void Init(int width, int height); 37 | 38 | Color get(int x, int y) const { return image[y*width+x]; } 39 | Color &get(int x, int y) { return image[y*width+x]; } 40 | 41 | // Clear to a solid color. 42 | void Clear(const Color &color); 43 | 44 | // Copy a rectangle from this image into dst. 45 | void CropImage(GIFImage &dst, int crop_left, int crop_top, int crop_width, int crop_height) const; 46 | 47 | // Copy src into a rectangle in this image. 48 | void Blit(GIFImage &src, int dst_left, int dst_top, int dst_width, int dst_height); 49 | 50 | bool operator==(const GIFImage &rhs) const; 51 | 52 | private: 53 | std::vector image; 54 | }; 55 | 56 | struct SMXGifFrame 57 | { 58 | int width = 0, height = 0; 59 | 60 | // GIF images have a delay in 10ms units. We use 1ms for clarity. 61 | int milliseconds = 0; 62 | 63 | GIFImage frame; 64 | }; 65 | 66 | // Decode a GIF into a list of frames. 67 | bool DecodeGIF(std::string buf, std::vector &frames); 68 | } 69 | 70 | void gif_test(); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /SMX.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27004.2009 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SMX", "sdk\Windows\SMX.vcxproj", "{C5FC0823-9896-4B7C-BFE1-B60DB671A462}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SMXSample", "sample\SMXSample.vcxproj", "{8861D665-FD49-4EFD-92C3-F4B8548AFD23}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SMXConfig", "smx-config\SMXConfig.csproj", "{B9EFCD31-7ACB-4195-81A8-CEF4EFD16D6E}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|x86 = Debug|x86 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {C5FC0823-9896-4B7C-BFE1-B60DB671A462}.Debug|x86.ActiveCfg = Debug|Win32 19 | {C5FC0823-9896-4B7C-BFE1-B60DB671A462}.Debug|x86.Build.0 = Debug|Win32 20 | {C5FC0823-9896-4B7C-BFE1-B60DB671A462}.Release|x86.ActiveCfg = Release|Win32 21 | {C5FC0823-9896-4B7C-BFE1-B60DB671A462}.Release|x86.Build.0 = Release|Win32 22 | {8861D665-FD49-4EFD-92C3-F4B8548AFD23}.Debug|x86.ActiveCfg = Debug|Win32 23 | {8861D665-FD49-4EFD-92C3-F4B8548AFD23}.Debug|x86.Build.0 = Debug|Win32 24 | {8861D665-FD49-4EFD-92C3-F4B8548AFD23}.Release|x86.ActiveCfg = Release|Win32 25 | {8861D665-FD49-4EFD-92C3-F4B8548AFD23}.Release|x86.Build.0 = Release|Win32 26 | {B9EFCD31-7ACB-4195-81A8-CEF4EFD16D6E}.Debug|x86.ActiveCfg = Debug|x86 27 | {B9EFCD31-7ACB-4195-81A8-CEF4EFD16D6E}.Debug|x86.Build.0 = Debug|x86 28 | {B9EFCD31-7ACB-4195-81A8-CEF4EFD16D6E}.Release|x86.ActiveCfg = Release|x86 29 | {B9EFCD31-7ACB-4195-81A8-CEF4EFD16D6E}.Release|x86.Build.0 = Release|x86 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {8DAB67F2-430F-43CC-853E-03B5A24F9806} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /sdk/Windows/SMXPanelAnimation.h: -------------------------------------------------------------------------------- 1 | #ifndef SMXPanelAnimation_h 2 | #define SMXPanelAnimation_h 3 | 4 | #include 5 | #include "SMXGif.h" 6 | 7 | enum SMX_LightsType 8 | { 9 | SMX_LightsType_Released, // animation while panels are released 10 | SMX_LightsType_Pressed, // animation while panel is pressed 11 | NUM_SMX_LightsType, 12 | }; 13 | 14 | // SMXPanelAnimation holds an animation, with graphics for a single panel. 15 | class SMXPanelAnimation 16 | { 17 | public: 18 | void Load(const std::vector &frames, int panel); 19 | 20 | // The high-level animated GIF frames: 21 | std::vector> m_aPanelGraphics; 22 | 23 | // The animation starts on frame 0. When it reaches the end, it loops 24 | // back to this frame. 25 | int m_iLoopFrame = 0; 26 | 27 | // The duration of each frame in seconds. 28 | std::vector m_iFrameDurations; 29 | }; 30 | 31 | namespace SMXAutoPanelAnimations 32 | { 33 | // If SMX_LightsAnimation_SetAuto is active, stop sending animations briefly. This is 34 | // called when lights are set directly, so they don't compete with the animation. 35 | void TemporaryStopAnimating(); 36 | } 37 | 38 | // For SMX_API: 39 | #include "../SMX.h" 40 | 41 | // High-level interface for C# bindings: 42 | // 43 | // Load an animated GIF as a panel animation. pad is the pad this animation is for (0 or 1), 44 | // and type is which animation this is for. Any previously loaded animation will be replaced. 45 | // On error, false is returned and error is set to a plain-text error message which is valid 46 | // until the next call. On success, the animation can be uploaded to the pad if supported using 47 | // SMX_LightsUpload_BeginUpload, or used directly with SMX_LightsAnimation_SetAuto. 48 | SMX_API bool SMX_LightsAnimation_Load(const char *gif, int size, int pad, SMX_LightsType type, const char **error); 49 | 50 | // Enable or disable automatically handling lights animations. If enabled, any animations 51 | // loaded with SMX_LightsAnimation_Load will run automatically as long as the SDK is loaded. 52 | // This only has an effect if the platform doesn't handle animations directly. On newer firmware, 53 | // this has no effect (upload the animation to the panel instead). 54 | // XXX: should we automatically disable SMX_SetLights when this is enabled? 55 | SMX_API void SMX_LightsAnimation_SetAuto(bool enable); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /smx-config/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("smx-config")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("smx-config")] 15 | [assembly: AssemblyCopyright("© 2017")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /smx-config/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace smx_config.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("smx_config.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /sdk/Windows/SMXDeviceSearchThreaded.cpp: -------------------------------------------------------------------------------- 1 | #include "SMXDeviceSearchThreaded.h" 2 | #include "SMXDeviceSearch.h" 3 | #include "SMXDeviceConnection.h" 4 | 5 | #include 6 | #include 7 | using namespace std; 8 | using namespace SMX; 9 | 10 | SMX::SMXDeviceSearchThreaded::SMXDeviceSearchThreaded() 11 | { 12 | m_hEvent = make_shared(CreateEvent(NULL, false, false, NULL)); 13 | m_pDeviceList = make_shared(); 14 | 15 | // Start the thread. 16 | DWORD id; 17 | m_hThread = CreateThread(NULL, 0, ThreadMainStart, this, 0, &id); 18 | SMX::SetThreadName(id, "SMXDeviceSearch"); 19 | } 20 | 21 | SMX::SMXDeviceSearchThreaded::~SMXDeviceSearchThreaded() 22 | { 23 | // Shut down the thread, if it's still running. 24 | Shutdown(); 25 | } 26 | 27 | void SMX::SMXDeviceSearchThreaded::Shutdown() 28 | { 29 | if(m_hThread == INVALID_HANDLE_VALUE) 30 | return; 31 | 32 | // Tell the thread to shut down, and wait for it before returning. 33 | m_bShutdown = true; 34 | SetEvent(m_hEvent->value()); 35 | 36 | WaitForSingleObject(m_hThread, INFINITE); 37 | m_hThread = INVALID_HANDLE_VALUE; 38 | } 39 | 40 | DWORD WINAPI SMX::SMXDeviceSearchThreaded::ThreadMainStart(void *self_) 41 | { 42 | SMXDeviceSearchThreaded *self = (SMXDeviceSearchThreaded *) self_; 43 | self->ThreadMain(); 44 | return 0; 45 | } 46 | 47 | void SMX::SMXDeviceSearchThreaded::UpdateDeviceList() 48 | { 49 | m_Lock.AssertNotLockedByCurrentThread(); 50 | 51 | // Tell m_pDeviceList about closed devices, so it knows that any device on the 52 | // same path is new. 53 | m_Lock.Lock(); 54 | for(auto pDevice: m_apClosedDevices) 55 | m_pDeviceList->DeviceWasClosed(pDevice); 56 | m_apClosedDevices.clear(); 57 | m_Lock.Unlock(); 58 | 59 | // Get the current device list. 60 | wstring sError; 61 | vector> apDevices = m_pDeviceList->GetDevices(sError); 62 | if(!sError.empty()) 63 | { 64 | Log(ssprintf("Error listing USB devices: %ls", sError.c_str())); 65 | return; 66 | } 67 | 68 | // Update the device list returned by GetDevices. 69 | m_Lock.Lock(); 70 | m_apDevices = apDevices; 71 | m_Lock.Unlock(); 72 | } 73 | 74 | void SMX::SMXDeviceSearchThreaded::ThreadMain() 75 | { 76 | while(!m_bShutdown) 77 | { 78 | UpdateDeviceList(); 79 | WaitForSingleObjectEx(m_hEvent->value(), 250, true); 80 | } 81 | } 82 | 83 | void SMX::SMXDeviceSearchThreaded::DeviceWasClosed(shared_ptr pDevice) 84 | { 85 | // Add pDevice to the list of closed devices. We'll call m_pDeviceList->DeviceWasClosed 86 | // on these from the scanning thread. 87 | m_apClosedDevices.push_back(pDevice); 88 | } 89 | 90 | vector> SMX::SMXDeviceSearchThreaded::GetDevices() 91 | { 92 | // Lock to make a copy of the device list. 93 | m_Lock.Lock(); 94 | vector> apResult = m_apDevices; 95 | m_Lock.Unlock(); 96 | return apResult; 97 | } 98 | -------------------------------------------------------------------------------- /sdk/Windows/SMXManager.h: -------------------------------------------------------------------------------- 1 | #ifndef SMXManager_h 2 | #define SMXManager_h 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | #include "Helpers.h" 11 | #include "../SMX.h" 12 | #include "SMXHelperThread.h" 13 | 14 | namespace SMX { 15 | class SMXDevice; 16 | class SMXDeviceSearchThreaded; 17 | 18 | struct SMXControllerState 19 | { 20 | // True 21 | bool m_bConnected[2]; 22 | 23 | // Pressed panels for player 1 and player 2: 24 | uint16_t m_Inputs[2]; 25 | }; 26 | 27 | // This implements the main thread that controller communication and device searching 28 | // happens in, finding and opening devices, and running device updates. 29 | // 30 | // Connected controllers can be accessed with GetDevice(), 31 | // This also abstracts controller numbers. GetDevice(SMX_PadNumber_1) will return the 32 | // first device that connected, 33 | class SMXManager 34 | { 35 | public: 36 | // Our singleton: 37 | static shared_ptr g_pSMX; 38 | 39 | // pCallback is a function to be called when something changes on any device. This allows 40 | // efficiently detecting when a panel is pressed or other changes happen. 41 | SMXManager(function pCallback); 42 | ~SMXManager(); 43 | 44 | void Shutdown(); 45 | shared_ptr GetDevice(int pad); 46 | void SetLights(const string sLights[2]); 47 | void SetPlatformLights(const string sLights[2]); 48 | void ReenableAutoLights(); 49 | void SetPanelTestMode(PanelTestMode mode); 50 | void SetSerialNumbers(); 51 | void SetOnlySendLightsOnChange(bool value) { m_bOnlySendLightsOnChange = value; } 52 | 53 | // Run a function in the user callback thread. 54 | void RunInHelperThread(function func); 55 | 56 | private: 57 | static DWORD WINAPI ThreadMainStart(void *self_); 58 | void ThreadMain(); 59 | void AttemptConnections(); 60 | void CorrectDeviceOrder(); 61 | void SendLightUpdates(); 62 | 63 | HANDLE m_hThread = INVALID_HANDLE_VALUE; 64 | shared_ptr m_hEvent; 65 | shared_ptr m_pSMXDeviceSearchThreaded; 66 | bool m_bShutdown = false; 67 | vector> m_pDevices; 68 | 69 | // We make user callbacks asynchronously in this thread, to avoid any locking or timing 70 | // issues that could occur by calling them in our I/O thread. 71 | SMXHelperThread m_UserCallbackThread; 72 | 73 | // A list of queued lights commands to send to the controllers. This is always sorted 74 | // by iTimeToSend. 75 | struct PendingCommand 76 | { 77 | PendingCommand(float fTime): fTimeToSend(fTime) { } 78 | double fTimeToSend = 0; 79 | string sPadCommand[2]; 80 | }; 81 | vector m_aPendingLightsCommands; 82 | int m_iLightsCommandsInProgress = 0; 83 | double m_fDelayLightCommandsUntil = 0; 84 | 85 | // Panel test mode. This is separate from the sensor test mode (pressure display), 86 | // which is handled in SMXDevice. 87 | void UpdatePanelTestMode(); 88 | uint32_t m_SentPanelTestModeAtTicks = 0; 89 | PanelTestMode m_PanelTestMode = PanelTestMode_Off; 90 | PanelTestMode m_LastSentPanelTestMode = PanelTestMode_Off; 91 | 92 | bool m_bOnlySendLightsOnChange = false; 93 | }; 94 | } 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /sdk/Windows/SMX.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | 10 | 11 | Source Files 12 | 13 | 14 | Source Files 15 | 16 | 17 | Source Files 18 | 19 | 20 | Source Files 21 | 22 | 23 | Source Files 24 | 25 | 26 | Source Files 27 | 28 | 29 | Source Files 30 | 31 | 32 | Source Files 33 | 34 | 35 | Source Files 36 | 37 | 38 | Source Files 39 | 40 | 41 | Source Files 42 | 43 | 44 | Source Files 45 | 46 | 47 | Source Files 48 | 49 | 50 | Source Files 51 | 52 | 53 | 54 | 55 | Source Files 56 | 57 | 58 | Source Files 59 | 60 | 61 | Source Files 62 | 63 | 64 | Source Files 65 | 66 | 67 | Source Files 68 | 69 | 70 | Source Files 71 | 72 | 73 | Source Files 74 | 75 | 76 | Source Files 77 | 78 | 79 | Source Files 80 | 81 | 82 | Source Files 83 | 84 | 85 | Source Files 86 | 87 | 88 | Source Files 89 | 90 | 91 | Source Files 92 | 93 | 94 | -------------------------------------------------------------------------------- /smx-config/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace smx_config.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 29 | public bool LaunchOnStartup { 30 | get { 31 | return ((bool)(this["LaunchOnStartup"])); 32 | } 33 | set { 34 | this["LaunchOnStartup"] = value; 35 | } 36 | } 37 | 38 | [global::System.Configuration.UserScopedSettingAttribute()] 39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 40 | [global::System.Configuration.DefaultSettingValueAttribute("")] 41 | public string CustomSensors { 42 | get { 43 | return ((string)(this["CustomSensors"])); 44 | } 45 | set { 46 | this["CustomSensors"] = value; 47 | } 48 | } 49 | 50 | [global::System.Configuration.UserScopedSettingAttribute()] 51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 52 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 53 | public bool UseInnerSensorThresholds { 54 | get { 55 | return ((bool)(this["UseInnerSensorThresholds"])); 56 | } 57 | set { 58 | this["UseInnerSensorThresholds"] = value; 59 | } 60 | } 61 | 62 | [global::System.Configuration.UserScopedSettingAttribute()] 63 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 64 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 65 | public bool UseOuterSensorThresholds { 66 | get { 67 | return ((bool)(this["UseOuterSensorThresholds"])); 68 | } 69 | set { 70 | this["UseOuterSensorThresholds"] = value; 71 | } 72 | } 73 | 74 | [global::System.Configuration.UserScopedSettingAttribute()] 75 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 76 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 77 | public bool AdvancedMode { 78 | get { 79 | return ((bool)(this["AdvancedMode"])); 80 | } 81 | set { 82 | this["AdvancedMode"] = value; 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /sample/SMXSample.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "SMX.h" 4 | #include 5 | #include 6 | using namespace std; 7 | 8 | class InputSample 9 | { 10 | public: 11 | InputSample() 12 | { 13 | // Set a logging callback. This can be called before SMX_Start. 14 | // SMX_SetLogCallback( SMXLogCallback ); 15 | 16 | // Start scanning. The update callback will be called when devices connect or 17 | // disconnect or panels are pressed or released. This callback will be called 18 | // from a thread. 19 | SMX_Start( SMXStateChangedCallback, this ); 20 | } 21 | 22 | static void SMXStateChangedCallback(int pad, SMXUpdateCallbackReason reason, void *pUser) 23 | { 24 | InputSample *pSelf = (InputSample *) pUser; 25 | pSelf->SMXStateChanged( pad, reason ); 26 | } 27 | 28 | static void SMXLogCallback(const char *log) 29 | { 30 | printf("-> %s\n", log); 31 | } 32 | 33 | void SMXStateChanged(int pad, SMXUpdateCallbackReason reason) 34 | { 35 | printf("Device %i state changed: %04x\n", pad, SMX_GetInputState(pad)); 36 | 37 | } 38 | 39 | int iPanelToLight = 0; 40 | void SetLights() 41 | { 42 | string sLightsData; 43 | auto addColor = [&sLightsData](uint8_t r, uint8_t g, uint8_t b) 44 | { 45 | sLightsData.append( 1, r ); 46 | sLightsData.append( 1, g ); 47 | sLightsData.append( 1, b ); 48 | }; 49 | for( int iPad = 0; iPad < 2; ++iPad ) 50 | { 51 | for( int iPanel = 0; iPanel < 9; ++iPanel ) 52 | { 53 | bool bLight = iPanel == iPanelToLight && iPad == 0; 54 | if( !bLight ) 55 | { 56 | // We're not lighting this panel, so append black for the 4x4 and 3x3 lights. 57 | for( int iLED = 0; iLED < 25; ++iLED ) 58 | addColor( 0, 0, 0 ); 59 | continue; 60 | } 61 | 62 | // Append light data for the outer 4x4 grid of lights. 63 | addColor( 0xFF, 0, 0 ); 64 | addColor( 0xFF, 0, 0 ); 65 | addColor( 0xFF, 0, 0 ); 66 | addColor( 0xFF, 0, 0 ); 67 | addColor( 0, 0xFF, 0 ); 68 | addColor( 0, 0xFF, 0 ); 69 | addColor( 0, 0xFF, 0 ); 70 | addColor( 0, 0xFF, 0 ); 71 | addColor( 0, 0, 0xFF ); 72 | addColor( 0, 0, 0xFF ); 73 | addColor( 0, 0, 0xFF ); 74 | addColor( 0, 0, 0xFF ); 75 | addColor( 0xFF, 0xFF, 0 ); 76 | addColor( 0xFF, 0xFF, 0 ); 77 | addColor( 0xFF, 0xFF, 0 ); 78 | addColor( 0xFF, 0xFF, 0 ); 79 | 80 | // Append light data for the inner 3x3 grid of lights, if present. These 81 | // are ignored if the platform doesn't have them. 82 | addColor( 0xFF, 0, 0 ); 83 | addColor( 0xFF, 0, 0 ); 84 | addColor( 0xFF, 0, 0 ); 85 | addColor( 0, 0xFF, 0 ); 86 | addColor( 0, 0xFF, 0 ); 87 | addColor( 0, 0xFF, 0 ); 88 | addColor( 0, 0, 0xFF ); 89 | addColor( 0, 0, 0xFF ); 90 | addColor( 0, 0, 0xFF ); 91 | } 92 | } 93 | 94 | SMX_SetLights2( sLightsData.data(), sLightsData.size() ); 95 | } 96 | }; 97 | 98 | int main() 99 | { 100 | InputSample demo; 101 | 102 | // Loop forever for this sample. 103 | while(1) 104 | { 105 | Sleep(500); 106 | demo.SetLights(); 107 | } 108 | 109 | return 0; 110 | } 111 | 112 | -------------------------------------------------------------------------------- /smx-config/SetCustomSensors.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | using System.Windows.Controls.Primitives; 6 | 7 | namespace smx_config 8 | { 9 | public class SensorSelectionButton: ToggleButton 10 | { 11 | } 12 | 13 | // A control with one button for each of four sensors: 14 | class SensorSelector: Control 15 | { 16 | // The panel we're editing (0-8). 17 | public static readonly DependencyProperty PanelProperty = DependencyProperty.RegisterAttached("Panel", 18 | typeof(int), typeof(SensorSelector), new FrameworkPropertyMetadata(0)); 19 | 20 | public int Panel { 21 | get { return (int) this.GetValue(PanelProperty); } 22 | set { this.SetValue(PanelProperty, value); } 23 | } 24 | 25 | ToggleButton[] SensorSelectionButtons = new ToggleButton[4]; 26 | public override void OnApplyTemplate() 27 | { 28 | base.OnApplyTemplate(); 29 | 30 | for(int sensor = 0; sensor < 4; ++sensor) 31 | { 32 | int ThisSensor = sensor; // bind 33 | SensorSelectionButtons[sensor] = GetTemplateChild("Sensor" + sensor) as ToggleButton; 34 | SensorSelectionButtons[sensor].Click += delegate(object sender, RoutedEventArgs e) 35 | { 36 | ClickedSensorButton(ThisSensor); 37 | }; 38 | } 39 | 40 | // These settings are stored in the application settings, not on the pad. However, 41 | // we treat changes to this as config changes, so we can use the same OnConfigChange 42 | // method for updating. 43 | OnConfigChange onConfigChange; 44 | onConfigChange = new OnConfigChange(this, delegate(LoadFromConfigDelegateArgs args) { 45 | LoadUIFromConfig(args); 46 | }); 47 | } 48 | 49 | private void ClickedSensorButton(int sensor) 50 | { 51 | // Toggle the clicked sensor. 52 | Console.WriteLine("Clicked sensor " + sensor); 53 | List customSensors = ThresholdSettings.GetCustomSensors(); 54 | bool enabled = !IsSensorEnabled(customSensors, sensor); 55 | 56 | if(enabled) 57 | customSensors.Add(new ThresholdSettings.PanelAndSensor(Panel, sensor)); 58 | else 59 | customSensors.Remove(new ThresholdSettings.PanelAndSensor(Panel, sensor)); 60 | ThresholdSettings.SetCustomSensors(customSensors); 61 | 62 | // Sync thresholds after changing custom sensors. 63 | ThresholdSettings.SyncSliderThresholds(); 64 | 65 | CurrentSMXDevice.singleton.FireConfigurationChanged(this); 66 | } 67 | 68 | // Return true if the given sensor is included in custom-sensors. 69 | bool IsSensorEnabled(List customSensors, int sensor) 70 | { 71 | foreach(ThresholdSettings.PanelAndSensor panelAndSensor in customSensors) 72 | { 73 | if(panelAndSensor.panel == Panel && panelAndSensor.sensor == sensor) 74 | return true; 75 | } 76 | return false; 77 | } 78 | 79 | private void LoadUIFromConfig(LoadFromConfigDelegateArgs args) 80 | { 81 | // Check the selected custom-sensors. 82 | List customSensors = ThresholdSettings.GetCustomSensors(); 83 | for(int sensor = 0; sensor < 4; ++sensor) 84 | SensorSelectionButtons[sensor].IsChecked = IsSensorEnabled(customSensors, sensor); 85 | } 86 | } 87 | 88 | // This dialog sets which sensors are controlled by custom-sensors. The actual work is done 89 | // by SensorSelector above. 90 | public partial class SetCustomSensors: Window 91 | { 92 | public SetCustomSensors() 93 | { 94 | InitializeComponent(); 95 | } 96 | 97 | private void OK_Click(object sender, RoutedEventArgs e) 98 | { 99 | Close(); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /sdk/Windows/Helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef HELPERS_H 2 | #define HELPERS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | 12 | namespace SMX 13 | { 14 | void Log(string s); 15 | void Log(wstring s); 16 | 17 | // Set a function to receive logs written by SMX::Log. By default, logs are written 18 | // to stdout. 19 | void SetLogCallback(function callback); 20 | 21 | void SetThreadName(DWORD iThreadId, const string &name); 22 | void StripCrnl(wstring &s); 23 | wstring GetErrorString(int err); 24 | string vssprintf(const char *szFormat, va_list argList); 25 | string ssprintf(const char *fmt, ...); 26 | wstring wvssprintf(const wchar_t *szFormat, va_list argList); 27 | wstring wssprintf(const wchar_t *fmt, ...); 28 | string BinaryToHex(const void *pData_, int iNumBytes); 29 | string BinaryToHex(const string &sString); 30 | bool GetRandomBytes(void *pData, int iBytes); 31 | double GetMonotonicTime(); 32 | void GenerateRandom(void *pOut, int iSize); 33 | string WideStringToUTF8(wstring s); 34 | 35 | // Create a char* string that will be valid until the next call to CreateError. 36 | // This is used to return error messages to the caller. 37 | const char *CreateError(string error); 38 | 39 | #define arraylen(a) (sizeof(a) / sizeof((a)[0])) 40 | 41 | // In order to be able to use smart pointers to fully manage an object, we need to get 42 | // a shared_ptr to pass around, but also store a weak_ptr in the object itself. This 43 | // lets the object create shared_ptrs for itself as needed, without keeping itself from 44 | // being deallocated. 45 | // 46 | // This helper allows this pattern: 47 | // 48 | // struct Class 49 | // { 50 | // Class(shared_ptr &pSelf): m_pSelf(GetPointers(pSelf, this)) { } 51 | // const weak_ptr m_pSelf; 52 | // }; 53 | // 54 | // shared_ptr obj; 55 | // new Class(obj); 56 | // 57 | // For a more convenient way to invoke this, see CreateObj() below. 58 | 59 | template 60 | weak_ptr GetPointers(shared_ptr &pSharedPtr, T *pObj) 61 | { 62 | pSharedPtr.reset(pObj); 63 | return pSharedPtr; 64 | } 65 | 66 | // Create a class that retains a weak reference to itself, returning a shared_ptr. 67 | template 68 | shared_ptr CreateObj(Args&&... args) 69 | { 70 | shared_ptr pResult; 71 | new T(pResult, std::forward(args)...); 72 | return dynamic_pointer_cast(pResult); 73 | } 74 | 75 | class AutoCloseHandle 76 | { 77 | public: 78 | AutoCloseHandle(HANDLE h); 79 | ~AutoCloseHandle(); 80 | HANDLE value() const { return handle; } 81 | 82 | private: 83 | AutoCloseHandle(const AutoCloseHandle &rhs); 84 | AutoCloseHandle &operator=(const AutoCloseHandle &rhs); 85 | HANDLE handle; 86 | }; 87 | 88 | class Mutex 89 | { 90 | public: 91 | Mutex(); 92 | ~Mutex(); 93 | void Lock(); 94 | void Unlock(); 95 | 96 | void AssertNotLockedByCurrentThread(); 97 | void AssertLockedByCurrentThread(); 98 | 99 | private: 100 | HANDLE m_hLock = INVALID_HANDLE_VALUE; 101 | DWORD m_iLockedByThread = 0; 102 | }; 103 | 104 | // A local lock helper for Mutex. 105 | class LockMutex 106 | { 107 | public: 108 | LockMutex(Mutex &mutex); 109 | ~LockMutex(); 110 | 111 | private: 112 | Mutex &m_Mutex; 113 | }; 114 | 115 | 116 | class Event 117 | { 118 | public: 119 | Event(Mutex &lock): 120 | m_Lock(lock) 121 | { 122 | m_hEvent = make_shared(CreateEvent(NULL, false, false, NULL)); 123 | } 124 | 125 | void Set() 126 | { 127 | SetEvent(m_hEvent->value()); 128 | } 129 | 130 | // Unlock m_Lock, wait up to iDelayMilliseconds for the event to be set, 131 | // then lock m_Lock. If iDelayMilliseconds is -1, wait forever. 132 | void Wait(int iDelayMilliseconds) 133 | { 134 | if(iDelayMilliseconds == -1) 135 | iDelayMilliseconds = INFINITE; 136 | 137 | m_Lock.AssertLockedByCurrentThread(); 138 | 139 | m_Lock.Unlock(); 140 | vector aHandles = { m_hEvent->value() }; 141 | WaitForSingleObjectEx(m_hEvent->value(), iDelayMilliseconds, true); 142 | m_Lock.Lock(); 143 | } 144 | 145 | private: 146 | shared_ptr m_hEvent; 147 | Mutex &m_Lock; 148 | }; 149 | 150 | } 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /sdk/Windows/SMXDeviceConnection.h: -------------------------------------------------------------------------------- 1 | #ifndef SMXDevice_H 2 | #define SMXDevice_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | 12 | #include "Helpers.h" 13 | 14 | namespace SMX 15 | { 16 | 17 | struct SMXDeviceInfo 18 | { 19 | // If true, this controller is set to player 2. 20 | bool m_bP2 = false; 21 | 22 | // This device's serial number. 23 | char m_Serial[33]; 24 | 25 | // This device's firmware version (normally 1). 26 | uint16_t m_iFirmwareVersion; 27 | }; 28 | 29 | // Low-level SMX device handling. 30 | class SMXDeviceConnection 31 | { 32 | public: 33 | static shared_ptr Create(); 34 | SMXDeviceConnection(shared_ptr &pSelf); 35 | ~SMXDeviceConnection(); 36 | 37 | bool Open(shared_ptr DeviceHandle, wstring &error); 38 | 39 | void Close(); 40 | 41 | // Get the device handle opened by Open(), or NULL if we're not open. 42 | shared_ptr GetDeviceHandle() const { return m_hDevice; } 43 | 44 | void Update(wstring &sError); 45 | 46 | // Devices are inactive by default, and will just read device info and then idle. We'll 47 | // process input state packets, but we won't send any commands to the device or process 48 | // any commands from it. It's safe to have a device open but inactive if it's being used 49 | // by another application. 50 | void SetActive(bool bActive); 51 | bool GetActive() const { return m_bActive; } 52 | 53 | bool IsConnected() const { return m_hDevice != nullptr; } 54 | bool IsConnectedWithDeviceInfo() const { return m_hDevice != nullptr && m_bGotInfo; } 55 | SMXDeviceInfo GetDeviceInfo() const { return m_DeviceInfo; } 56 | 57 | // Read from the read buffer. This only returns data that we've already read, so there aren't 58 | // any errors to report here. 59 | bool ReadPacket(string &out); 60 | 61 | // Send a command. This must be a single complete command: partial writes and multiple 62 | // commands in a call aren't allowed. 63 | void SendCommand(const string &cmd, function pComplete=nullptr); 64 | 65 | uint16_t GetInputState() const { return m_iInputState; } 66 | 67 | private: 68 | void RequestDeviceInfo(function pComplete = nullptr); 69 | 70 | void CheckReads(wstring &error); 71 | void BeginAsyncRead(wstring &error); 72 | void CheckWrites(wstring &error); 73 | void HandleUsbPacket(const string &buf); 74 | 75 | weak_ptr m_pSelf; 76 | shared_ptr m_hDevice; 77 | 78 | bool m_bActive = false; 79 | 80 | // After we open a device, we request basic info. Once we get it, this is set to true. 81 | bool m_bGotInfo = false; 82 | 83 | list m_sReadBuffers; 84 | string m_sCurrentReadBuffer; 85 | 86 | struct PendingCommandPacket { 87 | PendingCommandPacket(); 88 | 89 | string sData; 90 | }; 91 | 92 | // Commands that are waiting to be sent: 93 | struct PendingCommand { 94 | PendingCommand(); 95 | 96 | list> m_Packets; 97 | 98 | // The overlapped struct for writing this command's packets. m_bWriting is true 99 | // if we're waiting for the write to complete. 100 | OVERLAPPED m_Overlapped; 101 | bool m_bWriting = false; 102 | 103 | // This is only called if m_bWaitForResponse if true. Otherwise, we send the command 104 | // and forget about it. If the command has a response, it'll be in buf. 105 | function m_pComplete; 106 | 107 | // If true, once we send this command we won't send any other commands until we get 108 | // a response. 109 | bool m_bIsDeviceInfoCommand = false; 110 | 111 | // The SMX::GetMonotonicTime when we started sending this command. 112 | double m_fSentAt = 0; 113 | }; 114 | list> m_aPendingCommands; 115 | 116 | // If set, we've sent a command out of m_aPendingCommands and we're waiting for a response. We 117 | // can't send another command until the previous one has completed. 118 | shared_ptr m_pCurrentCommand = nullptr; 119 | 120 | // We always have a read in progress. 121 | OVERLAPPED overlapped_read; 122 | char overlapped_read_buffer[64]; 123 | 124 | uint16_t m_iInputState = 0; 125 | 126 | // The current device info. We retrieve this when we connect. 127 | SMXDeviceInfo m_DeviceInfo; 128 | }; 129 | } 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /sdk/Windows/SMX.cpp: -------------------------------------------------------------------------------- 1 | // This implements the public API. 2 | 3 | #include 4 | #include 5 | 6 | #include "../SMX.h" 7 | #include "SMXManager.h" 8 | #include "SMXDevice.h" 9 | #include "SMXBuildVersion.h" 10 | #include "SMXPanelAnimation.h" // for SMX_LightsAnimation_SetAuto 11 | using namespace std; 12 | using namespace SMX; 13 | 14 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 15 | { 16 | switch(ul_reason_for_call) 17 | { 18 | case DLL_PROCESS_ATTACH: 19 | case DLL_THREAD_ATTACH: 20 | case DLL_THREAD_DETACH: 21 | case DLL_PROCESS_DETACH: 22 | break; 23 | } 24 | return TRUE; 25 | } 26 | 27 | // DLL interface: 28 | SMX_API void SMX_Start(SMXUpdateCallback callback, void *pUser) 29 | { 30 | if(SMXManager::g_pSMX != NULL) 31 | return; 32 | 33 | // The C++ interface takes a std::function, which doesn't need a user pointer. We add 34 | // one for the C interface for convenience. 35 | auto UpdateCallback = [callback, pUser](int pad, SMXUpdateCallbackReason reason) { 36 | callback(pad, reason, pUser); 37 | }; 38 | 39 | // Log(ssprintf("Struct sizes (native): %i %i %i\n", sizeof(SMXConfig), sizeof(SMXInfo), sizeof(SMXSensorTestModeData))); 40 | SMXManager::g_pSMX = make_shared(UpdateCallback); 41 | } 42 | 43 | SMX_API void SMX_Stop() 44 | { 45 | // If lights animation is running, shut it down first. 46 | SMX_LightsAnimation_SetAuto(false); 47 | 48 | SMXManager::g_pSMX.reset(); 49 | } 50 | 51 | SMX_API void SMX_SetLogCallback(SMXLogCallback callback) 52 | { 53 | // Wrap the C callback with a C++ one. 54 | SMX::SetLogCallback([callback](const string &log) { 55 | callback(log.c_str()); 56 | }); 57 | } 58 | 59 | SMX_API bool SMX_GetConfig(int pad, SMXConfig *config) { return SMXManager::g_pSMX->GetDevice(pad)->GetConfig(*config); } 60 | SMX_API void SMX_SetConfig(int pad, const SMXConfig *config) { SMXManager::g_pSMX->GetDevice(pad)->SetConfig(*config); } 61 | SMX_API void SMX_GetInfo(int pad, SMXInfo *info) { SMXManager::g_pSMX->GetDevice(pad)->GetInfo(*info); } 62 | SMX_API uint16_t SMX_GetInputState(int pad) { return SMXManager::g_pSMX->GetDevice(pad)->GetInputState(); } 63 | SMX_API void SMX_FactoryReset(int pad) { SMXManager::g_pSMX->GetDevice(pad)->FactoryReset(); } 64 | SMX_API void SMX_ForceRecalibration(int pad) { SMXManager::g_pSMX->GetDevice(pad)->ForceRecalibration(); } 65 | SMX_API void SMX_SetTestMode(int pad, SensorTestMode mode) { SMXManager::g_pSMX->GetDevice(pad)->SetSensorTestMode(mode); } 66 | SMX_API bool SMX_GetTestData(int pad, SMXSensorTestModeData *data) { return SMXManager::g_pSMX->GetDevice(pad)->GetTestData(*data); } 67 | SMX_API void SMX_SetPanelTestMode(PanelTestMode mode) { SMXManager::g_pSMX->SetPanelTestMode(mode); } 68 | 69 | SMX_API void SMX_SetLights(const char lightData[864]) 70 | { 71 | SMX_SetLights2(lightData, 864); 72 | } 73 | SMX_API void SMX_SetLights2(const char *lightData, int lightDataSize) 74 | { 75 | // The lightData into data per pad depending on whether we've been 76 | // given 16 or 25 lights of data. 77 | string lights[2]; 78 | const int BytesPerPad16 = 9*16*3; 79 | const int BytesPerPad25 = 9*25*3; 80 | if(lightDataSize == 2*BytesPerPad16) 81 | { 82 | lights[0] = string(lightData, BytesPerPad16); 83 | lights[1] = string(lightData + BytesPerPad16, BytesPerPad16); 84 | } 85 | else if(lightDataSize == 2*BytesPerPad25) 86 | { 87 | lights[0] = string(lightData, BytesPerPad25); 88 | lights[1] = string(lightData + BytesPerPad25, BytesPerPad25); 89 | } 90 | else 91 | { 92 | Log(ssprintf("SMX_SetLights2: lightDataSize is invalid (must be %i or %i)\n", 93 | 2*BytesPerPad16, 2*BytesPerPad25)); 94 | return; 95 | } 96 | 97 | SMXManager::g_pSMX->SetLights(lights); 98 | 99 | // If we're running auto animations, stop them when we get an API call to set lights. 100 | SMXAutoPanelAnimations::TemporaryStopAnimating(); 101 | } 102 | 103 | // This is internal for SMXConfig. These lights aren't meant to be animated. 104 | SMX_API void SMX_SetPlatformLights(const char lightData[88*3], int lightDataSize) 105 | { 106 | if(lightDataSize != 88*3) 107 | { 108 | Log(ssprintf("SMX_SetPlatformLights: lightDataSize is invalid (must be %i)\n", 109 | 88*3)); 110 | return; 111 | } 112 | 113 | string lights[2]; 114 | lights[0] = string(lightData, 44*3); 115 | lights[1] = string(lightData + 44*3, 44*3); 116 | SMXManager::g_pSMX->SetPlatformLights(lights); 117 | } 118 | 119 | SMX_API void SMX_ReenableAutoLights() { SMXManager::g_pSMX->ReenableAutoLights(); } 120 | SMX_API const char *SMX_Version() { return SMX_BUILD_VERSION; } 121 | 122 | // These aren't exposed in the public API, since they're only used internally. 123 | SMX_API void SMX_SetOnlySendLightsOnChange(bool value) { SMXManager::g_pSMX->SetOnlySendLightsOnChange(value); } 124 | SMX_API void SMX_SetSerialNumbers() { SMXManager::g_pSMX->SetSerialNumbers(); } 125 | -------------------------------------------------------------------------------- /sample/SMXSample.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {8861D665-FD49-4EFD-92C3-F4B8548AFD23} 15 | Win32Proj 16 | SMXSample 17 | 10.0 18 | SMXSample 19 | 20 | 21 | 22 | Application 23 | true 24 | v142 25 | Unicode 26 | 27 | 28 | Application 29 | false 30 | v142 31 | false 32 | Unicode 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | true 48 | $(TargetDir)../out/ 49 | $(SolutionDir)/build/$(ProjectName)/$(Configuration)/ 50 | 51 | 52 | false 53 | $(TargetDir)../out/ 54 | $(SolutionDir)/build/$(ProjectName)/$(Configuration)/ 55 | 56 | 57 | 58 | 59 | 60 | Level4 61 | Disabled 62 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 63 | 4063;4100;4127;4201;4244;4275;4355;4505;4512;4702;4786;4996;4996;4005;4018;4389;4389;4800;4592;%(DisableSpecificWarnings) 64 | ..\sdk 65 | 66 | 67 | Console 68 | true 69 | $(SolutionDir)/out/$(TargetName)$(TargetExt) 70 | 71 | 72 | 73 | 74 | Level3 75 | 76 | 77 | MaxSpeed 78 | true 79 | true 80 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 81 | 4063;4100;4127;4201;4244;4275;4355;4505;4512;4702;4786;4996;4996;4005;4018;4389;4389;4800;4592;%(DisableSpecificWarnings) 82 | ..\sdk 83 | 84 | 85 | Console 86 | true 87 | true 88 | true 89 | $(SolutionDir)/out/$(TargetName)$(TargetExt) 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | {c5fc0823-9896-4b7c-bfe1-b60db671a462} 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /sdk/Windows/SMXDevice.h: -------------------------------------------------------------------------------- 1 | #ifndef SMXDevice_h 2 | #define SMXDevice_h 3 | 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | #include "Helpers.h" 10 | #include "../SMX.h" 11 | 12 | namespace SMX 13 | { 14 | class SMXDeviceConnection; 15 | 16 | // The high-level interface to a single controller. This is managed by SMXManager, and uses SMXDeviceConnection 17 | // for low-level USB communication. 18 | class SMXDevice 19 | { 20 | public: 21 | // Create an SMXDevice. 22 | // 23 | // lock is our serialization mutex. This is shared across SMXManager and all SMXDevices. 24 | // 25 | // hEvent is signalled when we have new packets to be sent, to wake the communications thread. The 26 | // device handle opened with OpenPort must also be monitored, to check when packets have been received 27 | // (or successfully sent). 28 | static shared_ptr Create(shared_ptr hEvent, SMX::Mutex &lock); 29 | SMXDevice(shared_ptr &pSelf, shared_ptr hEvent, SMX::Mutex &lock); 30 | ~SMXDevice(); 31 | 32 | bool OpenDeviceHandle(shared_ptr pHandle, wstring &sError); 33 | void CloseDevice(); 34 | shared_ptr GetDeviceHandle() const; 35 | 36 | // Set a function to be called when something changes on the device. This allows efficiently 37 | // detecting when a panel is pressed or other changes happen on the device. 38 | void SetUpdateCallback(function pCallback); 39 | 40 | // Return true if we're connected. 41 | bool IsConnected() const; 42 | 43 | // Send a raw command. 44 | void SendCommand(string sCmd, function pComplete=nullptr); 45 | void SendCommandLocked(string sCmd, function pComplete=nullptr); 46 | 47 | // Get basic info about the device. 48 | void GetInfo(SMXInfo &info); 49 | void GetInfoLocked(SMXInfo &info); // used by SMXManager 50 | 51 | // Return true if this device is configured as player 2. 52 | bool IsPlayer2Locked() const; // used by SMXManager 53 | 54 | // Get the configuration of the connected device (or the most recently read configuration if 55 | // we're not connected). 56 | bool GetConfig(SMXConfig &configOut); 57 | bool GetConfigLocked(SMXConfig &configOut); 58 | 59 | // Set the configuration of the connected device. 60 | // 61 | // This is asynchronous and returns immediately. 62 | void SetConfig(const SMXConfig &newConfig); 63 | 64 | // Return a mask of the panels currently pressed. 65 | uint16_t GetInputState() const; 66 | 67 | // Reset the configuration data to what the device used when it was first flashed. 68 | // GetConfig() will continue to return the previous configuration until this command 69 | // completes, which is signalled by a SMXUpdateCallback_FactoryResetCommandComplete callback. 70 | void FactoryReset(); 71 | 72 | // Force immediate fast recalibration. This is the same calibration that happens at 73 | // boot. This is only used for diagnostics, and the panels will normally auto-calibrate 74 | // on their own. 75 | void ForceRecalibration(); 76 | 77 | // Set the test mode of the connected device. 78 | // 79 | // This is asynchronous and returns immediately. 80 | void SetSensorTestMode(SensorTestMode mode); 81 | 82 | // Return the most recent test data we've received from the pad. Return false if we haven't 83 | // received test data since changing the test mode (or if we're not in a test mode). 84 | bool GetTestData(SMXSensorTestModeData &data); 85 | 86 | // Internal: 87 | 88 | // Update this device, processing received packets and sending any outbound packets. 89 | // m_Lock must be held. 90 | // 91 | // sError will be set on a communications error. The owner must close the device. 92 | void Update(wstring &sError); 93 | 94 | private: 95 | shared_ptr m_hEvent; 96 | SMX::Mutex &m_Lock; 97 | 98 | function m_pUpdateCallback; 99 | weak_ptr m_pSelf; 100 | 101 | shared_ptr m_pConnection; 102 | 103 | // The configuration we've read from the device. m_bHaveConfig is true if we've received 104 | // a configuration from the device since we've connected to it. 105 | SMXConfig config; 106 | vector rawConfig; 107 | bool m_bHaveConfig = false; 108 | double m_fDelayConfigUpdatesUntil = 0; 109 | 110 | // This is the configuration the user has set, if he's changed anything. We send this to 111 | // the device if m_bSendConfig is true. Once we send it once, m_bSendConfig is cleared, and 112 | // if we see a different configuration from the device again we won't re-send this. 113 | SMXConfig wanted_config; 114 | bool m_bSendConfig = false; 115 | bool m_bSendingConfig = false; 116 | bool m_bWaitingForConfigResponse = false; 117 | 118 | void CallUpdateCallback(SMXUpdateCallbackReason reason); 119 | void HandlePackets(); 120 | 121 | void SendConfig(); 122 | void CheckActive(); 123 | bool IsConnectedLocked() const; 124 | 125 | // Test/diagnostics mode handling. 126 | void UpdateSensorTestMode(); 127 | void HandleSensorTestDataResponse(const string &sReadBuffer); 128 | SensorTestMode m_WaitingForSensorTestModeResponse = SensorTestMode_Off; 129 | SensorTestMode m_SensorTestMode = SensorTestMode_Off; 130 | bool m_HaveSensorTestModeData = false; 131 | SMXSensorTestModeData m_SensorTestData; 132 | uint32_t m_SentSensorTestModeRequestAtTicks = 0; 133 | }; 134 | } 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /smx-config/SetCustomSensors.xaml: -------------------------------------------------------------------------------- 1 | 14 | 15 | 48 | 49 | 69 | 70 | 71 | 72 | 73 | Select which sensors are controlled 74 | by the custom threshold. 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |