From 080e48b303fe9d7eebaba6d266ddef0543bba0d3 Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Mon, 17 Mar 2025 23:12:34 +0100 Subject: [PATCH 1/7] Use C++17 to build if we find POCO 1.14 or higher. --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e251ce2c..a1749978 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,11 @@ find_package(projectM4 REQUIRED COMPONENTS Playlist) find_package(SDL2 REQUIRED) find_package(Poco REQUIRED COMPONENTS JSON XML Util Foundation) +if(Poco_VERSION VERSION_GREATER_EQUAL 1.14.0) + # POCO 1.14 requires at least C++17 + set(CMAKE_CXX_STANDARD 17) +endif() + if(ENABLE_FREETYPE) find_package(Freetype) endif() From c64cb728927545d81638d703b37efb9478c7136a Mon Sep 17 00:00:00 2001 From: nuess0r Date: Mon, 29 Jul 2024 21:27:14 +0200 Subject: [PATCH 2/7] Adding game controller support Using the SDL2 library to use gamecontrollers/joypads as user input method. --- src/RenderLoop.cpp | 130 +++++++++++++++++++++++++++++++++++++ src/RenderLoop.h | 26 ++++++++ src/SDLRenderingWindow.cpp | 22 ++++++- src/SDLRenderingWindow.h | 6 ++ 4 files changed, 182 insertions(+), 2 deletions(-) diff --git a/src/RenderLoop.cpp b/src/RenderLoop.cpp index 1fe8fa14..ffffca61 100644 --- a/src/RenderLoop.cpp +++ b/src/RenderLoop.cpp @@ -33,6 +33,8 @@ void RenderLoop::Run() _projectMWrapper.DisplayInitialPreset(); + _controller = _sdlRenderingWindow.FindController(); + while (!_wantsToQuit) { limiter.TargetFPS(_projectMWrapper.TargetFPS()); @@ -52,6 +54,10 @@ void RenderLoop::Run() _projectMWrapper.UpdateRealFPS(limiter.FPS()); } + //Close game controller + SDL_GameControllerClose(_controller); + _controller = nullptr; + notificationCenter.removeObserver(_quitNotificationObserver); projectm_playlist_set_preset_switched_event_callback(_playlistHandle, nullptr, nullptr); @@ -106,6 +112,26 @@ void RenderLoop::PollEvents() break; + case SDL_CONTROLLERDEVICEADDED: + ControllerAdd(event.cdevice.which); + + break; + + case SDL_CONTROLLERDEVICEREMOVED: + ControllerRemove(event.cdevice.which); + + break; + + case SDL_CONTROLLERBUTTONDOWN: + ControllerDownEvent(event); + + break; + + case SDL_CONTROLLERBUTTONUP: + ControllerUpEvent(event); + + break; + case SDL_DROPFILE: { char* droppedFilePath = event.drop.file; @@ -415,6 +441,110 @@ void RenderLoop::MouseUpEvent(const SDL_MouseButtonEvent& event) } } +void RenderLoop::ControllerAdd(const int id ) +{ + if (!_controller) + { + _controller = SDL_GameControllerOpen(id); + } + poco_debug(_logger, "Controller added!"); +} + +void RenderLoop::ControllerRemove(const int id ) +{ + if (_controller && id == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(_controller))) + { + SDL_GameControllerClose(_controller); + _controller = _sdlRenderingWindow.FindController(); + } + poco_debug(_logger, "Controller removed!"); +} + +void RenderLoop::ControllerDownEvent(const SDL_Event& event) +{ + if (!_controller || event.cdevice.which != SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(_controller))) + { + poco_debug(_logger, "No controller initialized"); + return; + } + + switch (event.cbutton.button) + { + case SDL_CONTROLLER_BUTTON_A: + _sdlRenderingWindow.ToggleFullscreen(); + poco_debug(_logger, "A pressed!"); + break; + + case SDL_CONTROLLER_BUTTON_B: + Poco::NotificationCenter::defaultCenter().postNotification(new PlaybackControlNotification(PlaybackControlNotification::Action::RandomPreset, _keyStates._shiftPressed)); + poco_debug(_logger, "B pressed!"); + break; + + case SDL_CONTROLLER_BUTTON_X: + Poco::NotificationCenter::defaultCenter().postNotification(new PlaybackControlNotification(PlaybackControlNotification::Action::TogglePresetLocked)); + poco_debug(_logger, "X pressed!"); + break; + + case SDL_CONTROLLER_BUTTON_Y: + Poco::NotificationCenter::defaultCenter().postNotification(new PlaybackControlNotification(PlaybackControlNotification::Action::ToggleShuffle)); + poco_debug(_logger, "Y pressed!"); + break; + + case SDL_CONTROLLER_BUTTON_BACK: + _wantsToQuit = true; + poco_debug(_logger, "Back pressed!"); + break; + + case SDL_CONTROLLER_BUTTON_GUIDE: + Poco::NotificationCenter::defaultCenter().postNotification(new PlaybackControlNotification(PlaybackControlNotification::Action::LastPreset, _keyStates._shiftPressed)); + poco_debug(_logger, "Guide pressed!"); + break; + + case SDL_CONTROLLER_BUTTON_START: + _projectMGui.Toggle(); + _sdlRenderingWindow.ShowCursor(_projectMGui.Visible()); + poco_debug(_logger, "Start pressed!"); + break; + + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: + _audioCapture.NextAudioDevice(); + poco_debug(_logger, "Shoulder left pressed!"); + break; + + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: + _sdlRenderingWindow.NextDisplay(); + poco_debug(_logger, "Shoulder right pressed!"); + break; + + case SDL_CONTROLLER_BUTTON_DPAD_UP: + // Increase beat sensitivity + _projectMWrapper.ChangeBeatSensitivity(0.05f); + poco_debug(_logger, "DPad up pressed!"); + break; + + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + // Decrease beat sensitivity + _projectMWrapper.ChangeBeatSensitivity(-0.05f); + poco_debug(_logger, "DPad down pressed!"); + break; + + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + Poco::NotificationCenter::defaultCenter().postNotification(new PlaybackControlNotification(PlaybackControlNotification::Action::PreviousPreset, _keyStates._shiftPressed)); + poco_debug(_logger, "DPad left pressed!"); + break; + + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + Poco::NotificationCenter::defaultCenter().postNotification(new PlaybackControlNotification(PlaybackControlNotification::Action::NextPreset, _keyStates._shiftPressed)); + poco_debug(_logger, "DPad right pressed!"); + break; + } +} + +void RenderLoop::ControllerUpEvent(const SDL_Event& event) +{ + +} + void RenderLoop::QuitNotificationHandler(const Poco::AutoPtr& notification) { _wantsToQuit = true; diff --git a/src/RenderLoop.h b/src/RenderLoop.h index 8a639495..a118a5d6 100644 --- a/src/RenderLoop.h +++ b/src/RenderLoop.h @@ -61,6 +61,30 @@ class RenderLoop */ void MouseUpEvent(const SDL_MouseButtonEvent& event); + /** + * @brief Handles SDL game controller add events (plugin in a new controller) events. + * @param id The added controller id + */ + void ControllerAdd( int id ); + + /** + * @brief Handles SDL game controller remove events. + * @param id The removed controller id + */ + void ControllerRemove( int id ); + + /** + * @brief Handles SDL game controller button down events. + * @param event The controller button event + */ + void ControllerDownEvent(const SDL_Event& event); + + /** + * @brief Handles SDL game controller button up events. + * @param event The controller button event + */ + void ControllerUpEvent(const SDL_Event& event); + /** * @brief Handler for quit notifications. * @param notification The received notification. @@ -90,4 +114,6 @@ class RenderLoop Poco::AutoPtr _userConfig; //!< View of the "projectM" configuration subkey in the "user" configuration. Poco::Logger& _logger{Poco::Logger::get("RenderLoop")}; //!< The class logger. + + SDL_GameController *_controller; }; diff --git a/src/SDLRenderingWindow.cpp b/src/SDLRenderingWindow.cpp index efac35e9..6df9da1a 100644 --- a/src/SDLRenderingWindow.cpp +++ b/src/SDLRenderingWindow.cpp @@ -196,7 +196,7 @@ void SDLRenderingWindow::NextDisplay() void SDLRenderingWindow::CreateSDLWindow() { - SDL_InitSubSystem(SDL_INIT_VIDEO); + SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER); int width{_config->getInt("width", 800)}; int height{_config->getInt("height", 600)}; @@ -307,7 +307,7 @@ void SDLRenderingWindow::DestroySDLWindow() SDL_DestroyWindow(_renderingWindow); _renderingWindow = nullptr; - SDL_QuitSubSystem(SDL_INIT_VIDEO); + SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER); } void SDLRenderingWindow::DumpOpenGLInfo() @@ -461,3 +461,21 @@ void SDLRenderingWindow::OnConfigurationPropertyRemoved(const std::string& key) UpdateWindowTitle(); } } + +SDL_GameController* SDLRenderingWindow::FindController() { + //Check for joysticks + if( SDL_NumJoysticks() < 1 ) + { + poco_debug(_logger, "No joysticks connected"); + } + + //For simplicity, we’ll only be setting up and tracking a single + //controller at a time + for (int i = 0; i < SDL_NumJoysticks(); i++) { + if (SDL_IsGameController(i)) { + return SDL_GameControllerOpen(i); + } + } + + return nullptr; +} diff --git a/src/SDLRenderingWindow.h b/src/SDLRenderingWindow.h index 06c3bbee..5b024337 100644 --- a/src/SDLRenderingWindow.h +++ b/src/SDLRenderingWindow.h @@ -96,6 +96,12 @@ class SDLRenderingWindow : public Poco::Util::Subsystem SDL_GLContext GetGlContext() const; + /** + * @brief Returns the ID of the first game controller found + * @return SDL_GameController * Returns a gamecontroller identifier or NULL + */ + SDL_GameController* FindController(); + protected: /** From af29b3c15c38874ea500800f834aeaca231109a4 Mon Sep 17 00:00:00 2001 From: nuess0r Date: Mon, 29 Jul 2024 22:08:13 +0200 Subject: [PATCH 3/7] Add description of GUI controls to README.md --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 205774f5..094a93db 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,28 @@ It will listen to audio input and produce mesmerizing visuals. Some commands are This project is in a bit of a transition state and is in the process of being modernized. There are many rough edges at present. +## GUI controls + + +| Command | Keyboard | Mouse | Game Controller | +| --- | --- | --- | --- | +| Add a new waveform | | `ctrl` + Left | | +| Clear all custom waveforms | | Middle | | +| Toggle full screen | `ctrl` + `f` | Right | A | +| Show/hide GUI | `esc` | | Start | +| Random preset | `r` | | B | +| Next preset | `n` | | D-Pad right | +| Previous preset | `p` | | D-Pad left | +| Increase beat sensitivity | `Up` | Wheel up | D-Pad up | +| Decrease beat sensitivity | `Down` | Wheel down | D-Pad down | +| Toggle shuffle | `y` | | Y | +| Last preset | `Backspace` | | Guide | +| TogglePresetLocked | `Space` | | X | +| NextAudioDevice | `ctrl` + `i` | | Shoulder left | +| NextDisplay | `ctrl` + `m` | | Shoulder right | +| Toggle aspect ratio correction | `a` | | | +| Quit | `q` | | Back | + ## Building from source ### Build and install libprojectM From f168c1ce2ad3aacaada809e4621471f16c1542e8 Mon Sep 17 00:00:00 2001 From: nuess0r Date: Mon, 26 Aug 2024 20:42:05 +0200 Subject: [PATCH 4/7] Implementing review: Move _controller pointer to SDLRenderingWindow class --- src/RenderLoop.cpp | 34 ++++------------------- src/RenderLoop.h | 14 ---------- src/SDLRenderingWindow.cpp | 56 ++++++++++++++++++++++++++++++++++++++ src/SDLRenderingWindow.h | 19 +++++++++++++ 4 files changed, 80 insertions(+), 43 deletions(-) diff --git a/src/RenderLoop.cpp b/src/RenderLoop.cpp index ffffca61..69378509 100644 --- a/src/RenderLoop.cpp +++ b/src/RenderLoop.cpp @@ -33,8 +33,6 @@ void RenderLoop::Run() _projectMWrapper.DisplayInitialPreset(); - _controller = _sdlRenderingWindow.FindController(); - while (!_wantsToQuit) { limiter.TargetFPS(_projectMWrapper.TargetFPS()); @@ -54,10 +52,6 @@ void RenderLoop::Run() _projectMWrapper.UpdateRealFPS(limiter.FPS()); } - //Close game controller - SDL_GameControllerClose(_controller); - _controller = nullptr; - notificationCenter.removeObserver(_quitNotificationObserver); projectm_playlist_set_preset_switched_event_callback(_playlistHandle, nullptr, nullptr); @@ -113,12 +107,14 @@ void RenderLoop::PollEvents() break; case SDL_CONTROLLERDEVICEADDED: - ControllerAdd(event.cdevice.which); + poco_debug(_logger, "Controller added event received"); + _sdlRenderingWindow.ControllerAdd(event.cdevice.which); break; case SDL_CONTROLLERDEVICEREMOVED: - ControllerRemove(event.cdevice.which); + poco_debug(_logger, "Controller remove event received"); + _sdlRenderingWindow.ControllerRemove(event.cdevice.which); break; @@ -441,30 +437,10 @@ void RenderLoop::MouseUpEvent(const SDL_MouseButtonEvent& event) } } -void RenderLoop::ControllerAdd(const int id ) -{ - if (!_controller) - { - _controller = SDL_GameControllerOpen(id); - } - poco_debug(_logger, "Controller added!"); -} - -void RenderLoop::ControllerRemove(const int id ) -{ - if (_controller && id == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(_controller))) - { - SDL_GameControllerClose(_controller); - _controller = _sdlRenderingWindow.FindController(); - } - poco_debug(_logger, "Controller removed!"); -} - void RenderLoop::ControllerDownEvent(const SDL_Event& event) { - if (!_controller || event.cdevice.which != SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(_controller))) + if (!_sdlRenderingWindow.ControllerIsOurs(event.cdevice.which) ) { - poco_debug(_logger, "No controller initialized"); return; } diff --git a/src/RenderLoop.h b/src/RenderLoop.h index a118a5d6..5131f9d2 100644 --- a/src/RenderLoop.h +++ b/src/RenderLoop.h @@ -61,18 +61,6 @@ class RenderLoop */ void MouseUpEvent(const SDL_MouseButtonEvent& event); - /** - * @brief Handles SDL game controller add events (plugin in a new controller) events. - * @param id The added controller id - */ - void ControllerAdd( int id ); - - /** - * @brief Handles SDL game controller remove events. - * @param id The removed controller id - */ - void ControllerRemove( int id ); - /** * @brief Handles SDL game controller button down events. * @param event The controller button event @@ -114,6 +102,4 @@ class RenderLoop Poco::AutoPtr _userConfig; //!< View of the "projectM" configuration subkey in the "user" configuration. Poco::Logger& _logger{Poco::Logger::get("RenderLoop")}; //!< The class logger. - - SDL_GameController *_controller; }; diff --git a/src/SDLRenderingWindow.cpp b/src/SDLRenderingWindow.cpp index 6df9da1a..6c0ee923 100644 --- a/src/SDLRenderingWindow.cpp +++ b/src/SDLRenderingWindow.cpp @@ -35,6 +35,8 @@ void SDLRenderingWindow::initialize(Poco::Util::Application& app) // Observe user configuration changes (set via the settings window) _userConfig->propertyChanged += Poco::delegate(this, &SDLRenderingWindow::OnConfigurationPropertyChanged); _userConfig->propertyRemoved += Poco::delegate(this, &SDLRenderingWindow::OnConfigurationPropertyRemoved); + + _controller = FindController(); } void SDLRenderingWindow::uninitialize() @@ -48,6 +50,12 @@ void SDLRenderingWindow::uninitialize() DestroySDLWindow(); _renderingWindow = nullptr; } + + if (_controller) + { + SDL_GameControllerClose(_controller); + _controller = nullptr; + } } void SDLRenderingWindow::GetDrawableSize(int& width, int& height) const @@ -467,15 +475,63 @@ SDL_GameController* SDLRenderingWindow::FindController() { if( SDL_NumJoysticks() < 1 ) { poco_debug(_logger, "No joysticks connected"); + return nullptr; } //For simplicity, we’ll only be setting up and tracking a single //controller at a time for (int i = 0; i < SDL_NumJoysticks(); i++) { if (SDL_IsGameController(i)) { + poco_debug(_logger, "Adding first controller"); return SDL_GameControllerOpen(i); } + else { + poco_debug(_logger, "Connected joystick is not a SDL game controller"); + } } return nullptr; } + +void SDLRenderingWindow::ControllerAdd(const int id ) +{ + if (!_controller) + { + if (SDL_IsGameController(id)) { + _controller = SDL_GameControllerOpen(id); + poco_debug(_logger, "Controller added!"); + } + else { + poco_debug(_logger, "Connected joystick is not a SDL game controller"); + } + } +} + +void SDLRenderingWindow::ControllerRemove(const int id ) +{ + if (_controller && id == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(_controller))) + { + SDL_GameControllerClose(_controller); + poco_debug(_logger, "Controller removed!"); + _controller = FindController(); + } +} + +bool SDLRenderingWindow::ControllerIsOurs(const int id ) +{ + int instance = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(_controller)); + + if (!_controller ) + { + poco_debug(_logger, "No controller initialized"); + return false; + } + + if (id != instance) + { + poco_debug_f2(_logger, "Use controller %?d instead of %?d. Currently only one controller is supported", instance, id); + return false; + } + + return true; +} \ No newline at end of file diff --git a/src/SDLRenderingWindow.h b/src/SDLRenderingWindow.h index 5b024337..b3f928fb 100644 --- a/src/SDLRenderingWindow.h +++ b/src/SDLRenderingWindow.h @@ -102,6 +102,24 @@ class SDLRenderingWindow : public Poco::Util::Subsystem */ SDL_GameController* FindController(); + /** + * @brief Handles SDL game controller add events (plugin in a new controller) events. + * @param id The added controller id + */ + void ControllerAdd(const int id ); + + /** + * @brief Handles SDL game controller remove events. + * @param id The removed controller id + */ + void ControllerRemove(const int id ); + + /** + * @brief Returns true if the given controller is initialized and the one we currently use. + * @param id The removed controller id + */ + bool ControllerIsOurs(const int id ); + protected: /** @@ -162,6 +180,7 @@ class SDLRenderingWindow : public Poco::Util::Subsystem bool _fullscreen{ false }; + SDL_GameController *_controller; }; From 5ca406c9a72e24070d1b072243daf3681e3de048 Mon Sep 17 00:00:00 2001 From: nuess0r Date: Mon, 26 Aug 2024 20:45:14 +0200 Subject: [PATCH 5/7] Move debug log level example further down, so it actually works When you uncommented the existing debug log level example it had no effect because the default level (informative) is set further down in the config file and therefore took priority. --- src/resources/projectMSDL.properties.in | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/resources/projectMSDL.properties.in b/src/resources/projectMSDL.properties.in index b6a1dcea..8f02244f 100644 --- a/src/resources/projectMSDL.properties.in +++ b/src/resources/projectMSDL.properties.in @@ -102,11 +102,6 @@ projectM.aspectCorrectionEnabled = true # For detailed information on how to configure logging, please refer to the POCO documentation: # https://docs.pocoproject.org/current/Poco.Util.LoggingConfigurator.html -# Set log level to debug for all components -#logging.loggers.root.level = debug - - - ### Logging configuration # Verbose log format, includes process/thread ID, source etc. @@ -145,9 +140,13 @@ logging.channels.async.class = AsyncChannel logging.channels.async.channel = split # Default logging settings. -logging.loggers.root.level = information logging.loggers.root.channel = async +# Set log level to debug for all components +#logging.loggers.root.level = debug +logging.loggers.root.level = information + + # You can configure log levels, channels etc. for each message source (logger) individually. # See https://docs.pocoproject.org/current/Poco.Util.LoggingConfigurator.html for details. # Example: From 7ce4672c044449a142f5effb128dd98f86b420b7 Mon Sep 17 00:00:00 2001 From: nuess0r Date: Sat, 17 May 2025 00:16:04 +0200 Subject: [PATCH 6/7] Update src/resources/projectMSDL.properties.in As suggested by @kblaschke Co-authored-by: Kai Blaschke --- src/resources/projectMSDL.properties.in | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/resources/projectMSDL.properties.in b/src/resources/projectMSDL.properties.in index 8f02244f..cb9e8b70 100644 --- a/src/resources/projectMSDL.properties.in +++ b/src/resources/projectMSDL.properties.in @@ -142,11 +142,6 @@ logging.channels.async.channel = split # Default logging settings. logging.loggers.root.channel = async -# Set log level to debug for all components -#logging.loggers.root.level = debug -logging.loggers.root.level = information - - # You can configure log levels, channels etc. for each message source (logger) individually. # See https://docs.pocoproject.org/current/Poco.Util.LoggingConfigurator.html for details. # Example: From dbbc1ef5ccb638a972bbc12952088f6a45a396e0 Mon Sep 17 00:00:00 2001 From: nuess0r Date: Sat, 17 May 2025 00:50:54 +0200 Subject: [PATCH 7/7] As suggested by @kblaschke --- README.md | 2 -- src/RenderLoop.cpp | 5 ----- 2 files changed, 7 deletions(-) diff --git a/README.md b/README.md index 094a93db..a4936d15 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,6 @@ present. | Command | Keyboard | Mouse | Game Controller | | --- | --- | --- | --- | -| Add a new waveform | | `ctrl` + Left | | -| Clear all custom waveforms | | Middle | | | Toggle full screen | `ctrl` + `f` | Right | A | | Show/hide GUI | `esc` | | Start | | Random preset | `r` | | B | diff --git a/src/RenderLoop.cpp b/src/RenderLoop.cpp index 69378509..33cdd738 100644 --- a/src/RenderLoop.cpp +++ b/src/RenderLoop.cpp @@ -421,11 +421,6 @@ void RenderLoop::MouseDownEvent(const SDL_MouseButtonEvent& event) case SDL_BUTTON_RIGHT: _sdlRenderingWindow.ToggleFullscreen(); break; - - case SDL_BUTTON_MIDDLE: - projectm_touch_destroy_all(_projectMHandle); - poco_debug(_logger, "Cleared all custom waveforms."); - break; } }