From 080e48b303fe9d7eebaba6d266ddef0543bba0d3 Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Mon, 17 Mar 2025 23:12:34 +0100 Subject: [PATCH 1/5] 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 e251ce2..a174997 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/5] 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 1fe8fa1..ffffca6 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 8a63949..a118a5d 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 efac35e..6df9da1 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 06c3bbe..5b02433 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/5] 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 205774f..094a93d 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/5] 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 ffffca6..6937850 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 a118a5d..5131f9d 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 6df9da1..6c0ee92 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 5b02433..b3f928f 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/5] 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 b6a1dce..8f02244 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: