Skip to content

Adding file/folder drop support & MacOS build fixes #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -1037,4 +1037,4 @@ _deps
modules.order
Module.symvers
Mkfile.old
dkms.conf
dkms.conf
14 changes: 13 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20 FATAL_ERROR)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17) # this can be changed to 14 if Poco is earlier than version 1.13.0
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_POSITION_INDEPENDENT_CODE YES)

Expand All @@ -13,6 +13,12 @@ project(projectMSDL

list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")

# the following should be set to the 'install' folder of projectM if you encounter issues with it finding the projectM libraries
#list(APPEND CMAKE_PREFIX_PATH "projectM 'install' folder")

# the following should be set to the 'install' folder of projectM if you encounter issues with it finding the projectM libraries
#list(APPEND CMAKE_PREFIX_PATH "projectM 'install' folder")

# Default install layouts.
option(ENABLE_FLAT_PACKAGE "Creates a \"flat\" install layout with files and preset/texture dirs directly in the main dir." OFF)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT ENABLE_FLAT_PACKAGE)
Expand Down Expand Up @@ -71,6 +77,12 @@ if(NOT SDL2_LINKAGE STREQUAL "shared" AND NOT SDL2_LINKAGE STREQUAL "static")
)
endif()

# Add utf8proc before Poco
#set(UTF8PROC_ROOT "utf8proc/<version>") # if you get build errors about utf8proc, set this to the path to your utf8proc install
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
find_package(utf8proc REQUIRED)
endif()

find_package(projectM4 REQUIRED COMPONENTS Playlist)
find_package(SDL2 REQUIRED)
find_package(Poco REQUIRED COMPONENTS JSON XML Util Foundation)
Expand Down
32 changes: 32 additions & 0 deletions cmake/Findutf8proc.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Find the utf8proc header directory
find_path(UTF8PROC_INCLUDE_DIR
NAMES utf8proc.h
PATHS
$ENV{UTF8PROC_ROOT}/include
/usr/local/include
/usr/include
)

# Find the utf8proc library
find_library(UTF8PROC_LIBRARY
NAMES utf8proc
PATHS
$ENV{UTF8PROC_ROOT}/lib
/usr/local/lib
/usr/lib
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(utf8proc
REQUIRED_VARS
UTF8PROC_LIBRARY
UTF8PROC_INCLUDE_DIR
)

if(utf8proc_FOUND AND NOT TARGET Utf8Proc::Utf8Proc)
add_library(Utf8Proc::Utf8Proc UNKNOWN IMPORTED)
set_target_properties(Utf8Proc::Utf8Proc PROPERTIES
IMPORTED_LOCATION "${UTF8PROC_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${UTF8PROC_INCLUDE_DIR}"
)
endif()
63 changes: 62 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ target_compile_definitions(projectMSDL
PROJECTMSDL_VERSION="${PROJECT_VERSION}"
)


target_link_libraries(projectMSDL
PRIVATE
ProjectMSDL-GUI
Expand All @@ -70,7 +71,67 @@ target_link_libraries(projectMSDL
SDL2::SDL2main
)

if(MSVC)
# Add the dylib copying for macOS
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
function(copy_dylibs_to_frameworks TARGET_NAME DYLIB_SOURCE_DIR)
# Create Frameworks directory
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
"$<TARGET_FILE_DIR:${TARGET_NAME}>/../Frameworks"
)

# Find all .dylib files in the source directory
file(GLOB DYLIB_FILES "${DYLIB_SOURCE_DIR}/*.dylib")

# Copy each .dylib file and fix its rpath
foreach(DYLIB_FILE ${DYLIB_FILES})
get_filename_component(DYLIB_NAME ${DYLIB_FILE} NAME)
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${DYLIB_FILE}"
"$<TARGET_FILE_DIR:${TARGET_NAME}>/../Frameworks/${DYLIB_NAME}"
)
endforeach()

# Add rpath to the main executable
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks"
"$<TARGET_FILE:${TARGET_NAME}>"
)
endfunction()

# Try to locate projectm
find_path(PROJECTM_LIBRARY_DIR
NAMES libprojectM-4.dylib # Use a known file from the library directory
PATHS
$ENV{PROJECTM_ROOT}/lib # Check an environment variable
/usr/local/lib/projectm # Common install location
/opt/homebrew/lib/projectm # Homebrew on Apple Silicon
/opt/local/lib/projectm # MacPorts location
PATH_SUFFIXES
lib # Look automatically appended subdirectories like 'lib'
NO_DEFAULT_PATH
)

if (NOT PROJECTM_LIBRARY_DIR)
find_path(PROJECTM_LIBRARY_DIR
NAMES libprojectM.dylib
PATHS ${CMAKE_PREFIX_PATH}
PATH_SUFFIXES lib
)
endif()

if (PROJECTM_LIBRARY_DIR)
message(STATUS "Found projectM libraries at: ${PROJECTM_LIBRARY_DIR}")
# Call the function to copy dylibs found in PROJECTM_LIBRARY_DIR
copy_dylibs_to_frameworks(projectMSDL "${PROJECTM_LIBRARY_DIR}")
else()
message(WARNING "Could not find projectM libraries. Please set PROJECTM_ROOT or specify the library location manually.")
endif()
endif()


if (MSVC)
set_target_properties(projectMSDL
PROPERTIES
VS_DPI_AWARE "PerMonitor"
Expand Down
8 changes: 8 additions & 0 deletions src/ProjectMSDLApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,14 @@ void ProjectMSDLApplication::defineOptions(Poco::Util::OptionSet& options)
false, "<0/1>", true)
.binding("projectM.shuffleEnabled", _commandLineOverrides));

options.addOption(Option("skipToDropped", "", "Skip to drag & dropped presets",
false, "<0/1>", true)
.binding("projectM.skipToDropped", _commandLineOverrides));

options.addOption(Option("droppedFolderOverride", "", "When dropping a folder, clear the playlist and add all presets from the folder.",
false, "<0/1>", true)
.binding("projectM.droppedFolderOverride", _commandLineOverrides));

options.addOption(Option("presetDuration", "", "Preset duration. Any number > 1, default 30.",
false, "<number>", true)
.binding("projectM.displayDuration", _commandLineOverrides));
Expand Down
78 changes: 78 additions & 0 deletions src/RenderLoop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@

#include <SDL2/SDL.h>

#include "ProjectMSDLApplication.h"

RenderLoop::RenderLoop()
: _audioCapture(Poco::Util::Application::instance().getSubsystem<AudioCapture>())
, _projectMWrapper(Poco::Util::Application::instance().getSubsystem<ProjectMWrapper>())
, _sdlRenderingWindow(Poco::Util::Application::instance().getSubsystem<SDLRenderingWindow>())
, _projectMHandle(_projectMWrapper.ProjectM())
, _playlistHandle(_projectMWrapper.Playlist())
, _projectMGui(Poco::Util::Application::instance().getSubsystem<ProjectMGUI>())
, _userConfig(ProjectMSDLApplication::instance().UserConfiguration())
{
}

Expand Down Expand Up @@ -103,6 +106,81 @@ void RenderLoop::PollEvents()

break;

case SDL_DROPFILE: {
char* droppedFilePath = event.drop.file;

// first we want to get the config settings that are relevant ehre
// namely skipToDropped and droppedFolderOverride
// we can get them from the projectMWrapper, in the _projectMConfigView available on it
bool skipToDropped = _userConfig->getBool("projectM.skipToDropped", true);
bool droppedFolderOverride = _userConfig->getBool("projectM.droppedFolderOverride", false);


bool shuffle = projectm_playlist_get_shuffle(_playlistHandle);
if (shuffle && skipToDropped) {
// if shuffle is enabled, we disable it temporarily, so the dropped preset is played next
// if skipToDropped is false, we also keep shuffle enabled, as it doesn't matter since the current preset is unaffected
projectm_playlist_set_shuffle(_playlistHandle, false);
}

int index = projectm_playlist_get_position(_playlistHandle) + 1;

do {
Poco::File droppedFile(droppedFilePath);
if (!droppedFile.isDirectory()) {
// handle dropped preset file
Poco::Path droppedFileP(droppedFilePath);
if (!droppedFile.exists() || (droppedFileP.getExtension() != "milk" && droppedFileP.getExtension() != "prjm")) {
std::string toastMessage = std::string("Invalid preset file: ") + droppedFilePath;
Poco::NotificationCenter::defaultCenter().postNotification(new DisplayToastNotification(toastMessage));
poco_information_f1(_logger, "%s", toastMessage);
break; // exit the block and go to the shuffle check
}

if (projectm_playlist_insert_preset(_playlistHandle, droppedFilePath, index, true)) {
if(skipToDropped){
projectm_playlist_play_next(_playlistHandle, true);
}
poco_information_f1(_logger, "Added preset: %s", std::string(droppedFilePath));
// no need to toast single presets, as its obvious if a preset was loaded.
}
} else {
// handle dropped directory

// if droppedFolderOverride is enabled, we clear the playlist first
// current edge case: if the dropped directory is invalid or contains no presets, then it still clears the playlist
if (droppedFolderOverride) {
projectm_playlist_clear(_playlistHandle);
index = 0;
}

uint32_t addedFilesCount = projectm_playlist_insert_path(_playlistHandle, droppedFilePath, index, true, true);
if (addedFilesCount > 0) {
std::string toastMessage = "Added " + std::to_string(addedFilesCount) + " presets from " + droppedFilePath;
poco_information_f1(_logger, "%s", toastMessage);
if(skipToDropped || droppedFolderOverride){
// if skip to dropped is true, or if a folder was dropped and it overrode the playlist, we skip to the next preset
projectm_playlist_play_next(_playlistHandle, true);
}
Poco::NotificationCenter::defaultCenter().postNotification(new DisplayToastNotification(toastMessage));

}else{
std::string toastMessage = std::string("No presets found in: ") + droppedFilePath;
Poco::NotificationCenter::defaultCenter().postNotification(new DisplayToastNotification(toastMessage));
poco_information_f1(_logger, "%s", toastMessage);
}
}
} while (false);

if (shuffle && skipToDropped) {
projectm_playlist_set_shuffle(_playlistHandle, true);
}

SDL_free(droppedFilePath);
break;
}


case SDL_QUIT:
_wantsToQuit = true;
break;
Expand Down
2 changes: 2 additions & 0 deletions src/RenderLoop.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,7 @@ class RenderLoop

ModifierKeyStates _keyStates; //!< Current "pressed" states of modifier keys

Poco::AutoPtr<Poco::Util::AbstractConfiguration> _userConfig; //!< View of the "projectM" configuration subkey in the "user" configuration.

Poco::Logger& _logger{Poco::Logger::get("RenderLoop")}; //!< The class logger.
};
2 changes: 1 addition & 1 deletion src/gui/ProjectMGUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void ProjectMGUI::initialize(Poco::Util::Application& app)
_glContext = renderingWindow.GetGlContext();

ImGui_ImplSDL2_InitForOpenGL(_renderingWindow, _glContext);
ImGui_ImplOpenGL3_Init("#version 130");
ImGui_ImplOpenGL3_Init("#version 150"); // changing this from '130' to '150' fixes the UI not displaying on MacOS 15

UpdateFontSize();

Expand Down
8 changes: 8 additions & 0 deletions src/gui/SettingsWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ void SettingsWindow::DrawProjectMSettingsTab()
LabelWithTooltip("Shuffle Presets", "Selects presets randomly from the current playlist.");
BooleanSetting("projectM.shuffleEnabled", true);

ImGui::TableNextRow();
LabelWithTooltip("Skip To Dropped Presets", "If enabled, will skip to the new presets when preset(s) are dropped onto the window and added to the playlist");
BooleanSetting("projectM.skipToDropped", true);

ImGui::TableNextRow();
LabelWithTooltip("Dropped Folder Overrides Playlist", "When dropping a folder, clear the playlist and add all presets from the folder.");
BooleanSetting("projectM.droppedFolderOverride", false);

ImGui::TableNextRow();
LabelWithTooltip("Preset Display Duration", "Time in seconds a preset will be displayed before it's switched.");
DoubleSetting("projectM.displayDuration", 30.0, 1.0, 240.0);
Expand Down