From 83a08f3a6c7d79145e17333320ff02860c5d7001 Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Fri, 4 Jun 2021 15:48:20 +0200 Subject: [PATCH] Add CMake build system files (only as "autotools" replacement for now) (#487) * Initial set of CMake files with options and some targets. * Added all targets to build libprojectM shared/static libraries. * Updated currently unused (and seemingly unfinished) SDL-based JACK visualizer to use SDL2. * Converted more targets to CMake and added install directives. New targets: - Presets - Test application - Pulseaudio/Qt visualizer - JACK/Qt visualizer - JACK/SDL2 visualizer * Added libvisual plug-in target. Changed default install paths to relative. * Use debug suffix for libraries by default, but make it configurable. * Added target for projectMSDL application. * Added CMake code for the ENABLE_THREADING switch, install desktop, icon, manpage and pkgconfig files. Also added some dependency version output to the post-configuration summary and made Qt switch implicit based on Pulsaudio/JACK UI selection. * Added CMake package config files and exported targets. Some restructuring of libprojectM targets. Kept pkgconfig module name for CMake. * Add -name parameter to RCC command in existing autotools Makefile. Fixes symbol name mismatch with CMake auto-generated resources which always add the QRC file basename as resource name. * Display an author warning that CMake files aren't production ready yet. * Prefer system GLM library, with fallback to bundled version if not available. Removed the option to use the system library in favor of automated detection. * Link correct OpenGL libraries, the previous ones were Linux-specific, with GLVND not being required. * Recreate config.h as generated by autoheader, even if most defines weren't used. Will also force-include the file on all platforms. * Add option to build native preset libraries, disabled by default. * Only install "tests" presets if building the test suite. * Fix linking LLVM libraries to libprojectM. Wasn't inherited anymore due to changing MilkdropPresetFactory library type to OBJECT. * Use INTERFACE IMPORTED libraries in find scripts, set minimum CMake version to 3.19. * Added missing config.h template and renamed to config.h.cmake.in, was in the .gitignore list. * Added Windows-specific CMake configuration and some related code changes. These changes enable building for Windows with both Visual Studio and Ninja project generators. Installation and packaging for Windows is not yet supported. Source groups, filters and support for embedded debug symbols will be added later. * Link required CoreFoundation framework on MacOS. * Work around SDL2 include dir issue (upstream SDL bug #4004) by removing "/SDL2" from the path. * Fix existing Windows build by moving bundled GLEW files into "GL" subdir. * Place libprojectM-related targets in a "libprojectM" folder in IDEs supporting it, e.g. Visual Studio. * Fix displaying whether system GLM is used or not in config summary. * Disable threading support on Windows unless we don't rely solely on pthreads anymore. * Add version check for SDL2 (>=2.0.5). * Complete overhaul of build instructions, adding CMake build and use instructions. * Added check for OpenMP support and fixed a compiler error in OMPTL. Also moved compiler warning checks to features.cmake and added the DEBUG define for debug builds. * Added a quick start guide for building on Debian and Ubuntu. * Lower minimum required CMake version to 3.14. Co-authored-by: Kai Blaschke --- .gitignore | 13 +- BUILDING-cmake.md | 279 ++++ BUILDING.md | 398 +++++- CMakeLists.txt | 199 +++ cmake/CheckEnumValueExists.cmake | 26 + cmake/EnableCFlagsIfSupported.cmake | 47 + cmake/FindGLES3.cmake | 0 cmake/FindGLM.cmake | 20 + cmake/FindJACK.cmake | 33 + cmake/FindLLVM.cmake | 55 + cmake/FindPulseaudio.cmake | 44 + cmake/Findlibvisual.cmake | 42 + config.h.cmake.in | 73 ++ features.cmake | 103 ++ msvc/{ => GL}/eglew.h | 0 msvc/{ => GL}/glew.c | 4 +- msvc/{ => GL}/glew.h | 0 msvc/{ => GL}/glewinfo.c | 0 msvc/{ => GL}/glxew.h | 0 msvc/{ => GL}/wglew.h | 0 msvc/projectM.vcxproj | 4 +- msvc/projectM.vcxproj.filters | 4 +- presets/CMakeLists.txt | 25 + src/CMakeLists.txt | 8 + src/NativePresets/CMakeLists.txt | 70 + src/libprojectM/CMakeLists.txt | 333 +++++ src/libprojectM/FileScanner.cpp | 376 +++--- src/libprojectM/FileScanner.hpp | 98 +- .../MilkdropPresetFactory/CMakeLists.txt | 64 + .../NativePresetFactory/CMakeLists.txt | 23 + src/libprojectM/Renderer/CMakeLists.txt | 62 + src/libprojectM/Renderer/SOIL2/CMakeLists.txt | 36 + .../Renderer/hlslparser/CMakeLists.txt | 23 + src/libprojectM/config.inp.cmake.in | 22 + src/libprojectM/libprojectM.pc.cmake.in | 13 + src/libprojectM/libprojectMConfig.cmake.in | 14 + src/libprojectM/msvc/dirent.h | 1160 +++++++++++++++++ src/libprojectM/msvc/dlfcn.c | 489 +++++++ src/libprojectM/msvc/dlfcn.h | 59 + src/libprojectM/omptl/CMakeLists.txt | 15 + .../omptl/omptl_numeric_extentions_par.h | 4 +- src/libprojectM/projectM-opengl.h | 100 +- src/libprojectM/projectM.cpp | 12 +- src/projectM-jack/CMakeLists.txt | 11 + src/projectM-jack/projectM-jack-qt.cmake | 30 + src/projectM-jack/projectM-jack-sdl.cmake | 25 + src/projectM-jack/projectM-jack.cpp | 459 +++---- src/projectM-jack/sdltoprojectM.h | 8 +- src/projectM-jack/video_init.cpp | 302 +++-- src/projectM-libvisual/CMakeLists.txt | 30 + src/projectM-pulseaudio/CMakeLists.txt | 58 + src/projectM-qt/CMakeLists.txt | 47 + src/projectM-qt/Makefile.am | 2 +- src/projectM-qt/qprojectm.hpp | 1 + src/projectM-qt/qprojectm_mainwindow.cpp | 4 +- src/projectM-sdl/CMakeLists.txt | 30 + src/projectM-sdl/setup.cpp | 673 +++++----- src/projectM-test/CMakeLists.txt | 37 + src/projectM-test/getConfigFilename.cpp | 194 +-- src/projectM-test/projectM-unittest.cpp | 94 +- 60 files changed, 5179 insertions(+), 1176 deletions(-) create mode 100644 BUILDING-cmake.md create mode 100644 CMakeLists.txt create mode 100644 cmake/CheckEnumValueExists.cmake create mode 100644 cmake/EnableCFlagsIfSupported.cmake create mode 100644 cmake/FindGLES3.cmake create mode 100644 cmake/FindGLM.cmake create mode 100644 cmake/FindJACK.cmake create mode 100644 cmake/FindLLVM.cmake create mode 100644 cmake/FindPulseaudio.cmake create mode 100644 cmake/Findlibvisual.cmake create mode 100644 config.h.cmake.in create mode 100644 features.cmake rename msvc/{ => GL}/eglew.h (100%) rename msvc/{ => GL}/glew.c (99%) rename msvc/{ => GL}/glew.h (100%) rename msvc/{ => GL}/glewinfo.c (100%) rename msvc/{ => GL}/glxew.h (100%) rename msvc/{ => GL}/wglew.h (100%) create mode 100644 presets/CMakeLists.txt create mode 100644 src/CMakeLists.txt create mode 100644 src/NativePresets/CMakeLists.txt create mode 100644 src/libprojectM/CMakeLists.txt create mode 100644 src/libprojectM/MilkdropPresetFactory/CMakeLists.txt create mode 100644 src/libprojectM/NativePresetFactory/CMakeLists.txt create mode 100644 src/libprojectM/Renderer/CMakeLists.txt create mode 100644 src/libprojectM/Renderer/SOIL2/CMakeLists.txt create mode 100644 src/libprojectM/Renderer/hlslparser/CMakeLists.txt create mode 100644 src/libprojectM/config.inp.cmake.in create mode 100644 src/libprojectM/libprojectM.pc.cmake.in create mode 100644 src/libprojectM/libprojectMConfig.cmake.in create mode 100644 src/libprojectM/msvc/dirent.h create mode 100644 src/libprojectM/msvc/dlfcn.c create mode 100644 src/libprojectM/msvc/dlfcn.h create mode 100644 src/libprojectM/omptl/CMakeLists.txt create mode 100644 src/projectM-jack/CMakeLists.txt create mode 100644 src/projectM-jack/projectM-jack-qt.cmake create mode 100644 src/projectM-jack/projectM-jack-sdl.cmake create mode 100644 src/projectM-libvisual/CMakeLists.txt create mode 100644 src/projectM-pulseaudio/CMakeLists.txt create mode 100644 src/projectM-qt/CMakeLists.txt create mode 100644 src/projectM-sdl/CMakeLists.txt create mode 100644 src/projectM-test/CMakeLists.txt diff --git a/.gitignore b/.gitignore index a9d3de4962..7d057f4aa4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,8 @@ Makefile.in /aclocal.m4 /autom4te.cache /build-aux -/config.* +/config.h +/config.h.in /configure /libtool /stamp-h1 @@ -41,6 +42,10 @@ m4/lt~obsolete.m4 /build /dist +# Visual Studio w/CMake +/out +/.vs +/CMakeSettings.json _sync.bat _sync.sh @@ -48,4 +53,8 @@ to_sync/ .DS_Store src/projectM-sdl/build/ src/libprojectM/build/ -*.pkg \ No newline at end of file +*.pkg + +# CLion +cmake-build-* +.idea diff --git a/BUILDING-cmake.md b/BUILDING-cmake.md new file mode 100644 index 0000000000..f173ac15ad --- /dev/null +++ b/BUILDING-cmake.md @@ -0,0 +1,279 @@ +# Building with CMake + +This file contains in-depth information for building with the CMake build system. + +## Selecting a specific project file generator + +To specify a CMake generator, use the `-G` switch, followed by the generator name. Some newer generators take an +additional architecture using the `-A` switch. To list all available generators available on your current platform, +leave out the generator name: + +```shell +cmake -G +``` + +Additional information on the supported generators can be +found [in the CMake documentation](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html). + +### Popular generators + +By default, CMake will use the [`Unix Makefiles`](https://cmake.org/cmake/help/latest/generator/Unix%20Makefiles.html) +generator on Linux and macOS, which is a good choice and should work. Yet in some circumstances, you might want to +generate project files for a specific build tool or IDE: + +```shell +cmake -G "Unix Makefiles" -S /path/to/source/dir -B /path/to/build/dir +``` + +A common alternative is the [`Ninja`](https://cmake.org/cmake/help/latest/generator/Ninja.html) generator, which +requires `ninja` to be installed. It is mostly a `make` +replacement with less overhead and should work equally well. It is supported on all major platforms, including Windows: + +```shell +cmake -G Ninja -S /path/to/source/dir -B /path/to/build/dir +``` + +On macOS, CMake also supports the [`Xcode`](https://cmake.org/cmake/help/latest/generator/Xcode.html) generator. It will +create an `.xcodeproj` bundle which you can open in Xcode. It also adds support for automatic code signing, which might +be required if your application using projectM needs to be notarized for store deployment. + +```shell +cmake -G "Xcode" -S /path/to/source/dir -B /path/to/build/dir +``` + +If you develop on Windows, you will possibly use Visual Studio. While recent visual Studio versions have CMake support +built-in, you can still pre-generate the solution and project files and open the `.sln` file from the build directory. +CMake provides a separate generator for each Visual Studio release. For Visual Studio 2019 you would use +the [`Visual Studio 16 2019`](https://cmake.org/cmake/help/latest/generator/Visual%20Studio%2016%202019.html) generator +and provide an additional architecture parameter: + +```shell +cmake -G "Visual Studio 16 2019" -A "X64" -S /path/to/source/dir -B /path/to/build/dir +``` + +It is not possible to generate multi-arch solutions with CMake though. You need to create separate build directories and +use the respective `-A` switch for each. + +## Project-specific configuration options + +CMake has no built-in way of printing all available configuration options. You can either refer to the +top-level `CMakeLists.txt` which contains a block of `option` and `cmake_dependent_option` commands, or use one of the +available CMake UIs which will display the options after configuring the project once. + +### Boolean switches + +The following table also gives you an overview of the available options and their defaults. All options accept a boolean +value (`YES`/`NO`, `TRUE`/`FALSE`, `ON`/`OFF` or `1`/`0`) and can be provided on the configuration-phase command line +using the `-D` switch. + +| CMake option | Default | Required dependencies | Description | +|-------------------------|---------|-----------------------|------------------------------------------------------------------------------------------------------------| +| `ENABLE_DEBUG_PREFIX` | `ON` | | Adds `d` to the name of any binary file in debug builds. | +| `ENABLE_EMSCRIPTEN` | `OFF` | `GLES`, `Emscripten` | Build for the web using Emscripten. | +| `ENABLE_SDL` | `ON` | `SDL2` | Builds and installs the standalone SDL visualizer application. Also required for Emscripten and the tests. | +| `ENABLE_GLES` | `OFF` | `GLES` | Use OpenGL ES 3 profile for rendering instead of the Core profile. | +| `ENABLE_THREADING` | `ON` | `pthreads` | Enable multithreading support. If enabled, will cause an error if pthreads are not available. | +| `ENABLE_NATIVE_PRESETS` | `OFF` | | Builds and installs the binary (native) preset libraries. | +| `ENABLE_PRESETS` | `ON` | | Installs several thousand shipped presets. | +| `ENABLE_PULSEAUDIO` | `OFF` | `Qt5`, `Pulseaudio` | Build the Qt5-based Pulseaudio visualizer application. | +| `ENABLE_JACK` | `OFF` | `Qt5`, `JACK` | Build the Qt5-based JACK visualizer application. | +| `ENABLE_LIBVISUAL` | `OFF` | `libvisual-0.4` | Build the libvisual plug-in/actor library. | +| `ENABLE_TESTING` | `OFF` | `SDL2` | Builds the unit tests. | +| `ENABLE_LLVM` | `OFF` | `LLVM` | Enables experimental LLVM JIT support. | + +### Path options + +There are also a few textual parameters that can be used to fine-tune the installation directories. Relative paths in +the following options are appended to the value +of [`CMAKE_INSTALL_PREFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html) (which, on most UNIX +platforms, defaults to `/usr/local`): + +| CMake option | Default | Description | +|-------------------------|------------------|----------------------------------------------------------------------------------| +| `PROJECTM_BIN_DIR` | `bin` | Directory where executables (e.g. the SDL standalone application) are installed. | +| `PROJECTM_LIB_DIR` | `lib` | Directory where libprojectM is installed[[*]](#libvisual-path). | +| `PROJECTM_INCLUDE_DIR` | `include` | Directory where the libprojectM include files will be installed under. | +| `PROJECTM_DATADIR_PATH` | `share/projectM` | Directory where the default configuration and presets are installed under. | + +[*]: The libvisual projectM plug-in install location is determined automatically via +pkgconfig and not influenced by this option. + +## Always perform out-of-tree builds! + +Most classic IDEs and build systems directly make use of the source tree and create project files, temporary build +artifacts (e.g. object files) and the final binaries in the same directory structure as the source files. An advantage +of this approach is that you can find all compiled binaries side-by-side with their sources and generated headers are +already in the same directories as the source files including them. This approach has some drawbacks though: + +- Only a single build configuration is supported as files are overwritten in-place. +- A lot of noise is created in the source directory, making it hard to distinguish between generated and original source + files. +- A very large `.gitignore` file is required to cover all unwanted files. +- Mistakes in the build scripts can overwrite source files, causing errors and destroy uncommitted work. + +Some of these can be mitigated by providing additional targets (`make clean` and `make distclean`) or creating +subdirectories for Debug/Release build configurations. + +While CMake also supports in-tree builds, it is "discouraged" in the official documentation, for the above reasons. +Building out-of-tree allows it to create multiple build directories with different configurations which do not influence +each other in any way. If a build directory contains unwanted artifacts, and you want to start fresh, simply delete and +recreate the whole directory - no work is lost. + +This project follow this principle by treating the original source tree as read-only and avoiding potential conflicts: + +- Everything under [`CMAKE_SOURCE_DIR`](https://cmake.org/cmake/help/latest/variable/CMAKE_SOURCE_DIR.html) must only be + read, never changed or written to. +- Everything under [`CMAKE_BINARY_DIR`](https://cmake.org/cmake/help/latest/variable/CMAKE_BINARY_DIR.html) is temporary + and related to the current build configuration. +- When generating configuration-dependent files, + use [`CMAKE_CONFIGURATION_TYPES`](https://cmake.org/cmake/help/latest/variable/CMAKE_CONFIGURATION_TYPES.html) + and [`CMAKE_BUILD_TYPE`](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) to create non-conflicting + files in the build tree. + +While this project will not force you to build out-of-tree, there is no mechanism to clean up the generated files after +running cmake in-tree. + +## CMake build directory layout + +If you are new to CMake, the way of how CMake creates the build directory and where it creates the build targets might +be confusing. Here is a summary of what's in the build directory and how it is structured in general. + +### Using files from the build tree + +It is generally not good practice to directly take binaries and other files from the build tree for packaging, for +several reasons: + +1. The directory structure is generated by CMake and depends on the generator used. The layout might change between + CMake versions, even for the same generator. +2. On platforms with RPATH support, CMake will store absolute paths in executables and shared libraries which point to + the absolute paths of any linked dependencies, either from the build tree or external libraries as well. These + binaries are not relocatable and will most certainly not work if run on any other computer (or even on the same after + deleting the build directory). +3. For some configurations, even Release build artifacts may contain debug symbols until they are installed. + +It is fine to build and run executables from the build directory for development and debugging. For packaging, always +use the `install` target and copy files from there. + +### Generated files + +In the top-level build directory, CMake creates a few files that are present on any platform: + +- `CMakeCache.txt`: This file contains all variables and build settings CMake needs to remember from the first + configuration run. This file can be edited on demand either manually or using a CMake UI to change any values. On the + next build, CMake will regenerate the project files if this file has been modified. +- `cmake_install.cmake`: Contains generated install-related settings. +- `install_manifest.txt`: After installing the project, this file contains a list with absolute filenames of all + installed files. It can be used for packaging or deleting installed files as CMake doesn't define an `uninstall` + target. +- The top-level project file for use with the selected build toolset, e.g. `Makefile`, `build.ninja`, `projectm.sln` + or `projectm.xcodeproj`, plus additional toolset-specific files. + +The projectM build files generate additional files used in the build and install phases: + +- `config.inp`: The default configuration file, by default installed to `/share/projectM`. +- `libprojectM.pc`: Package configuration file for use with `pkgconfig`. +- `include/config.h`: A forced include file that contains macro definitions to enable or disable certain code features + based on the build configuration and availability of platform headers and features. Similar to the + autoheader-generated file. + +### Subdirectory structure + +The rest of the directory structure generally resembles the source tree. Source directories containing +a `CMakeLists.txt` file will also be created in the build tree with the same relative path. Each of these subdirectories +contains a `CMakeFiles` directory with CMake-internal data, generated project files for the select toolset, e.g. +makefiles and any temporary compile artifacts. + +### Executable and library locations + +Build targets - shared/static libraries and executables - are created in the same subdirectory in the build tree as +the `CMakeLists.txt` file that defines the target in the source tree (which, in most cases, resides in the same +directory as the source files). Depending on the generator used, the binaries are created directly in the directory for +single-configuration generators (like `Unix Makefiles` or `Ninja`) and in a subdirectory with the configuration name, +e.g. `Debug` or `Release`, for multi-configuration generators like `Xcode` or `Visual Studio 16 2019`. + +You may also find additional files and symbolic links in the same location depending on the platform, e.g. `.pdb` files +on Windows. + +## Using libprojectM in other CMake projects + +The projectM library can be used as a static library and, on non-Windows platforms, as a shared library in other +CMake-based projects to provide embedded audio visualization. There are two ways: + +- Importing the library CMake targets directly from the build tree without installation. +- Using the library from an installed location or package with the provided CMake package config files. + +### Importing libprojectM targets from the build tree + +This approach is useful for projects that either require more in-depth access to the projectM library files, especially +to headers that are not installed as part of the public API. This might cause issues if the internal headers change, but +gives a broader set of features and more control to the developer. + +To use the targets, follow these steps: + +- Configure the build directory as needed. +- Build the required library targets `projectM_static` and `projectM_shared` as needed, or simply everything. +- Include the file `src/libprojectM/projectM-exports.cmake` from the projectM build tree in your project. + +All targets and their interface properties are now defined and can be used. + +#### Example + +```cmake +# libprojectM.a/.lib is already built. +set(PROJECTM_BUILD_DIR "/some/path/to/projectM/build") +include("${PROJECTM_BUILD_DIR}/src/libprojectM/projectM-exports.cmake") + +add_executable(MyApp main.cpp) + +target_link_libraries(MyApp PRIVATE libprojectM::static) +``` + +This will also add all projectM include directories to the target's source files, pointing to the respective projectM +source directories. + +Look at the generated file in the build tree if you need more details about the available targets. + +### Importing libprojectM targets from an installed version + +This is the recommended way of importing libprojectM in your project. This project installs a set of CMake files +in `//cmake/libprojectM`, containing target definitions, version and dependency checks as well as any +additional libraries required for linking. Other projects then use CMake's `find_package` command to search for these +files in [different locations](https://cmake.org/cmake/help/latest/command/find_package.html#search-procedure). + +In the case projectM libraries and headers are not installed in any system search path, you need to add either the +install prefix path (the top-level install dir) or the directory containing the libraries (the `lib` dir by default) to +the [`CMAKE_PREFIX_PATH`](https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html) list. + +If the package was found, you can then link against one of these provided targets: + +- `libprojectM::static` - The static library (`.a` or `.lib`). +- `libprojectM::shared` - The shared library (`.so`, `.dylib` or `.dll`). + +Note that the availability of the targets depend on the platform and build configuration, so only one of those might be +available. You can check with `if(TARGET libprojectM::static)` and `if(TARGET libprojectM::shared)` to select the +available or preferred one. + +Depending on how the package was built, targets might be available for multiple configurations or only `Release`. CMake +will automatically select the most appropriate one to link. + +Include dirs, additional link dependencies and possible compiler options will be propagated to any target the library is +linked to. + +#### Example + +Links the shared library preferably, with fallback to static: + +```cmake +find_package(libprojectM REQUIRED) + +if(TARGET libprojectM::shared) + set(PROJECTM_LINK_TARGET libprojectM::shared) +else() + # If this target is also unavailable, CMake will error out on target_link_libraries(). + set(PROJECTM_LINK_TARGET libprojectM::static) +endif() + +add_executable(MyApp main.cpp) + +target_link_libraries(MyApp PRIVATE ${PROJECTM_LINK_TARGET}) +``` diff --git a/BUILDING.md b/BUILDING.md index 2e0c9d4dd5..8953b0957d 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1,56 +1,369 @@ # Building projectM from source -### Dependencies -Relevant for Linux distributions, FreeBSD, macOS: +## Quick Start (Debian / Ubuntu) -* `autotools` `autoconf` -* `libtool`, or at least `pkg-config` -* `which` +For other operating systems (Windows/macOS), see the OS-specific sections below. -Main build options & their requirements: +### Install the build tools and dependencies -| Configure flag | Required dependency | Produced binary | -|-----------------------|----------------------------------------------------------------------------------------|-----------------------| -| `--enable-sdl` | `libsdl2-dev` | `projectMSDL` | -| `--enable-pulseaudio` | `qt5-default` `qtdeclarative5-dev` `libpulse-dev` `libqt5opengl5-dev` | `projectM-pulseaudio` | -| `--enable-jack` | `libjack2-dev`OR`libjack1-dev`; `qt5-default` `qtdeclarative5-dev` `libqt5opengl5-dev` | `projectM-jack` | +Mandatory packages: -#### Additional information on dependencies -* `libglm` (headers only) for matrix math is required. lives in `vendor/glm`. -* A modified version of `hlslparser` is included in Renderer and used to transpile HLSL shaders to GLSL -* OpenGL 3+ or OpenGLES is required -* `libsdl >= 2.0.5` is required for the SDL and emscripten apps. `src/projectM-sdl` is the current reference application implementation. maybe try getting that to build and run as your testbench. +```bash +sudo apt install build-essential libgl1-mesa-dev mesa-common-dev libsdl2-dev libglm-dev +``` -If extra information needed - you can refere to `configure.ac` and the assorted `Makefile.am` files. +Optional packages for additional features: -### Building process under *nix systems -```sh +```bash +sudo apt install qtbase5-dev # For building Qt-based UIs +sudo apt install llvm-dev # for using the experimental LLVM Jit +sudo apt install libvisual-0.4-dev # To build the libvisual plug-in +sudo apt install libjack-jackd2-dev # To build the JACK visualizer application +``` + +### Download the projectM sources + +If you want to use a stable version of projectM, download the latest release from +the [Releases page on GitHub](https://github.com/projectM-visualizer/projectm/releases) and unpack it. You can then skip +to the next step. + +If you prefer a bleeding-edge version or want to modify the code, clone the Git repository: + +```bash +sudo apt install git # Probably already installed +git clone https://github.com/projectM-visualizer/projectm.git /path/to/local/repo +cd /path/to/local/repo +git fetch --all --tags +``` + +### Build and install projectM + +Older projectM releases use autoconf/automake for building. If your repository has a `CMakeLists.txt` file on the top +level, skip to the CMake part right below. + +Replace `/usr/local` with your preferred installation prefix. + +#### Configure the project using autoconf + +```bash +sudo apt install autoconf automake libtool ./autogen.sh +./configure --prefix=/usr/local +``` + +#### Configure the project using CMake + +```bash +sudo apt install cmake +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local .. +``` + +#### Build and install + +Independent of the method of configuration, this will build projectM and install it to /usr/local or the configured +installation prefix set in the step before: + +```bash +make -j && sudo make install +``` + +**Note**: You won't need to use `sudo` if the install prefix is writeable by your non-privileged user. + +#### Test projectM + +If you have a desktop environment installed, you can now run `[prefix]/bin/projectMSDL`. + +## Dependencies + +Depending on the OS/distribution and packaging system, libraries might be split into separate packages with binaries and +development files. To build projectM, both binaries and development files need to be installed. + +#### General build dependencies for all platforms: + +* A working build toolchain. +* **OpenGL**: 3D graphics library. Used to render the visualizations. +* **GLES3**: OpenGL libraries for embedded systems, version 3. Required to build projectM on mobile devices, Raspberry + Pi and Emscripten. +* [**glm**](https://github.com/g-truc/glm): OpenGL Mathematics library. Optional, will use a bundled version with + autotools or if not installed. +* [**SDL2**](https://github.com/libsdl-org/SDL): Simple Directmedia Layer. Version 2.0.5 or higher is required to build + the standalone visualizer application (projectMSDL). +* [**LLVM**](https://llvm.org/): Low-Level Virtual Machine. Optional and **experimental**, used to speed up preset + execution by leveraging the LLVM JIT compiler. + +#### Only relevant for Linux distributions, FreeBSD and macOS: + +* **pkgconfig**: Required to find some library dependencies. +* [**Qt5**](https://www.qt.io/): Qt cross-platform UI framework. Used to build the Pulseaudio and JACK visualizer + applications. Requires the `Gui` and `OpenGL` component libraries/frameworks. +* [**Pulseaudio**](https://www.freedesktop.org/wiki/Software/PulseAudio/): Sound system for POSIX platforms. Required to + build the Pulseaudio visualizer application. +* [**JACK**](https://jackaudio.org/): Real-time audio server. Required to build the JACK visualizer application. +* [**libvisual 0.4**](http://libvisual.org/): Audio visualization library with plug-in support. Required to build the + projectM libvisual plug-in. + +#### When using the classic autotools-based build system on UNIX platforms: + +* **GNU [automake](https://www.gnu.org/software/automake/) and [autoconf](https://www.gnu.org/software/autoconf/)**: + Used to create the configuration script and generate the Makefiles. +* [**libtool**](https://www.gnu.org/software/libtool/): Optional. Used by autoconf is available. +* [**which**](https://carlowood.github.io/which/): Determines full paths of shell executables. Should already be + installed by default on the majority of POSIX-compliant OSes. + +#### When using the new CMake-based build system: + +* [**CMake**](https://cmake.org/): Used to generate platform-specific build files. + +### Only relevant for Windows: + +* [**vcpkg**](https://github.com/microsoft/vcpkg): C++ Library Manager for Windows. Optional, but recommended to install + the aforementioned library dependencies and/or using CMake to configure the build. +* [**NuGet**](https://www.nuget.org/): Dependency manager for .NET. Optional, but recommended when building with the + pre-created Visual Studio solutions. +* [**GLEW**](http://glew.sourceforge.net/): The OpenGL Extension Wrangler Library. Only required if using CMake to + configure the build, the pre-created solutions use a bundled copy of GLEW. + +## Building on Linux and macOS + +### Installing dependencies + +- Linux distributions will have packages available for most (if not all) required libraries. The package names and + commands to install them vary widely between distributions (and even versions of the same distribution). Please refer + to the documentation of your build OS on how to find and install the required libraries. +- On *BSD, install the appropriate Ports with `pkg install`. +- On macOS, using [Homebrew](https://brew.sh/) is the recommended way of installing any dependencies not supplied by + Xcode. + +### Building with CMake + +--- + +:exclamation: **IMPORTANT NOTE**: Currently, CMake build support is still in active development and considered +unfinished. It is working and produces running binaries, but there are still some features, build internals and whole +targets missing. While testing the CMake build files on any platform and feedback on this is strongly encouraged, +CMake-based builds should not yet be used in any production environment until this message is gone. + +--- + +The steps documented below are a bare minimum quickstart guide on how to build and install the project. If you want to +configure the build to your needs, require more in-depth information about the build process and available tweaks, or on +how to use libprojectM in your own CMake-based projects, see [BUILDING-cmake.md](BUILDING-cmake.md). + +Using CMake is the recommended and future-proof way of building projectM. CMake is a platform-independent tool that is +able to generate files for multiple build systems and toolsets while using only a single set of build instructions. +CMake support is still new and in development, but will replace the other project files (automake/autoconf scripts, +Visual Studio solutions and Xcode projects) in this repository once mature and stable. + +Building the project with CMake requires two steps: + +- Configure the build and generate project files. +- Build and install the project using the selected build tools. + +**Note:** When building with CMake, the build directory should always be separate from the source directory. Generating +the build files directly inside the source tree is possible, but strongly discouraged. Using a subdirectory, +e.g. `cmake-build` inside the source directory is fine though. + +This documentation only covers project-specific information. CMake is way too versatile and feature-rich to cover any +possible platform- and toolset-specific configuration details here. If you are not experienced in using CMake, please +first read the [official CMake documentation](https://cmake.org/cmake/help/latest/) (at least +the [User Interaction Guide](https://cmake.org/cmake/help/latest/guide/user-interaction/index.html)) for basic usage +instructions. + +#### Configure the build + +Configuring a non-debug build with default options and install prefix (`/usr/local`) can be done with these commands, +building in a subdirectory inside the source directory: + +```shell +cd /path/to/source +mkdir cmake-build +cd cmake-build +cmake -DCMAKE_BUILD_TYPE=Release .. +``` + +CMake will check all required dependencies and display any errors. If configuration was successful, a summary of the +build configuration is printed and CMake should display a `Generating done` line. The project is now ready to build. + +#### Compile and install the project + +Depending on your generator choice, you can use your selected toolset as usual to build and install projectM: + +- With `Unix Makefiles`, run `make && sudo make install`. +- With `Ninja`, run `ninja && sudo ninja install`. +- With `Xcode`, select the appropriate target and configuration in Xcode and build it, or `INSTALL` to install the + project. + +You can also use CMake's build mode to run the selected toolset and build any specified target. CMake knows which +command to call and which parameters to pass, so the syntax works on all platforms with all generators. If you've +already set the top-level build directory as working directory, simply pass `.` as `/path/to/build/dir`: + +```shell +cmake --build /path/to/build/dir --config Release +sudo cmake --build /path/to/build/dir --config Release --target install +``` + +If you don't need root permissions to install running the second command without `sudo` is sufficient. + +If you want to provide arguments directly to the toolset command, add `--` at the end of the CMake command line followed +by any additional arguments. CMake will pass these *unchanged and unchecked* to the subcommand: + +```shell +cmake --build /path/to/build/dir --config Release -- -j 4 +``` + +### Building with automake/autoconf + +projectM ships with a set of scripts to check build dependencies and configure the build according to the user's +preferences and toolchain. + +**Note**: These scripts might be removed in the future in favor of using CMake as the sole build system tool on all +platforms, so if you're planning to base new work on the projectM libraries, consider using CMake instead. + +The source distribution only contains templates for the configure script and other files, so these need to be generated +first. `cd` into the top-level source directory and execute: + +```shell +./autogen.sh +``` + +You should now have an executable `configure` script ready. This will be used to check the platform and dependencies and +finally configure the Makefiles for the actual build. The script accepts numerous parameters to customize the build. The +most important ones and their requirements are listed in the table below. To get a full list of available parameters, +options and influential environment variables, run `./configure --help`. + +#### Important build options and their requirements: + +| Configure flag | Required dependencies | Produced binary | +|-----------------------|-----------------------|-----------------------| +| `--enable-sdl` | `SDL2` | `projectMSDL` | +| `--enable-pulseaudio` | `Qt5`, `Pulseaudio` | `projectM-pulseaudio` | +| `--enable-jack` | `Qt5`, `JACK` | `projectM-jack` | +| `--enable-threading` | `pthreads` | | +| `--enable-llvm` | `LLVM` | | +| `--enable-gles` | `GLES3` | | + +For example, to configure the project to build and install only the libprojectM libraries and the SDL-based standalone +visualizer in the default location (`/usr/local`), run the following commands: + +```shell ./configure --enable-sdl # supply additional options here, info in Dependencies make sudo make install ``` -### Debian/Ubuntu/Mint -```sh -sudo apt install clang libsdl2-dev libgl1-mesa-dev qt5-default qtdeclarative5-dev libqt5opengl5-dev libjack-dev libpulse-dev -./configure && make -j4 && sudo make install +## Building on Windows + +### Using the provided project files + +Windows build bypasses the autogen/configure pipeline and uses manually created Visual Studio/MSVC project files +in `msvc/`. See `.appveyor.yml` for command line building. + +Some dependencies are included verbatim (GLEW), while others leverage the NuGet ecosystem and are downloaded +automatically (SDL2). + +The Visual Studio solution is quite old and unmaintained. If you experience issues importing it, try using CMake to +generate the solution for your Visual Studio version instead. + +### With CMake + +To build the projectM library and the SDL-based standalone application, CMake can be used as on any other platform. +Using vcpkg to pull in the build dependencies is highly recommended, as CMake can't use NuGet (NuGet pulls in +dependencies using the project files, while CMake requires the libraries before creating the project files). + +#### Installing the dependencies with vcpkg + +As stated above, using vcpkg is the easiest way to get the required dependencies. First, +install [vcpkg from GitHub](https://github.com/microsoft/vcpkg) by following the official guide. Then install the +following packages for your desired architecture (called "triplet"): + +- `glew` +- `sdl2` + +The `glew` package will also pull in the `opengl` libraries. + +Example to install the libraries for the x64 architecture, run from a Visual Studio command prompt: + +```commandline +vcpkg install glew:x64-windows sdl2:x64-windows ``` -### OpenGL ES -projectM supports OpenGL ES 3 for embedded systems. Be sure to configure with the `--enable-gles` flag. +#### Creating the Visual Studio solution + +CMake provides separate generators for different Visual Studio versions. Newer CMake versions will support recent Visual +Studio releases, but may remove generators for older ones. To get a list of available generators from the command line, +use the `-G` switch without an argument. The CMake GUI will present you a dropdown list you can easily select from. + +To set the build architecture in Visual Studio builds, use the `-A` switch and specify either `Win32` or `X64` as the +argument. If you want to build for both architectures, create separate build directories and configure them accordingly. + +To make CMake aware of the installed vcpkg packages, simply use the provided toolchain file when configuring the +projectM build by +pointing [`CMAKE_TOOLCHAIN_FILE`](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html) to it. + +Here is a full command line example to create a Visual Studio 2019 solution for X64: + +```commandline +cmake -G "Visual Studio 16 2019" -A "X64" -DCMAKE_TOOLCHAIN_FILE="/scripts/buildsystems/vcpkg.cmake" -S "" -B "" +``` + +If you use the CMake GUI, check the "Specify toolchain file for cross-compiling" option in the first page of the +configuration assistant, then select the above `vcpkg.cmake` file on the second page. + +Another option is to open the project folder in a recent Visual Studio version as a CMake project and configure CMake +using Visual Studio's JSON-based settings file. + +#### Building the solution + +To build the project, open the generated solution in Visual Studio and build it like any other solution. Each time the +CMake files are changed, Visual Studio will automatically regenerate the CMake build files and reload the solution +before continuing the build. Be aware that in old Visual Studio versions (2015 and earlier) the reload-and-continue +might not work properly. + +You can also build the solution with msbuild via the command line, or use CMake's build wrapper to do that for you: + +```commandline +cmake --build "" --config Release +``` + +#### Using Ninja to build + +The Ninja build system is shipped with Visual Studio since version 2019 and used by default if loading a CMake project +directly from within the IDE. Ninja can also be [installed separately](https://github.com/ninja-build/ninja/releases). + +To configure the build directory for Ninja, pass `Ninja` or `Ninja Multi-Config` as the argument for the `-G` switch. +The difference between both generators is that the former uses `CMAKE_BUILD_TYPE` to specify the configuration ( +e.g. `Debug` or `Release`) while the latter supports all configurations in a single build directory, specified during +build time. + +The architecture is determined from the toolset, so make sure to run the commands in the correct Visual Studio command +prompt, e.g. "Native Tools for X64". + +Configure and build for a single-configuration Release build with vcpkg: + +```commandline +cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="/scripts/buildsystems/vcpkg.cmake" -S "" -B "" +cmake --build "" +``` + +Same, but using the multi-configuration generator: + +```commandline +cmake -G "Ninja Multi-Config" -DCMAKE_TOOLCHAIN_FILE="/scripts/buildsystems/vcpkg.cmake" -S "" -B "" +cmake --build "" --config Release +``` + +## Notes on other platforms and features ### Raspberry Pi (and other embedded systems) + * projectM is arch-independent, although there are some SSE2 enhancements for x86 * [Notes on running on raspberry pi](https://github.com/projectM-visualizer/projectm/issues/115) -### Building on Windows -Windows build bypasses the autogen/configure pipeline and uses the Visual Studio/MSVC files in `msvc/`. See `.appveyor.yml` for command line building. - -Some dependencies are included verbatim (glew), while others leverage the NuGet ecosystem and are downloaded automatically (sdl2). - ### Build using NDK for Android + Install Android Studio, launch SDK Manager and install NDK ```sh @@ -59,27 +372,38 @@ Install Android Studio, launch SDK Manager and install NDK make && make install-strip ``` -Now you should be able to copy ./src/libprojectM/.libs/libprojectM.so -and appropriate headers to projectm-android, and build it using Android Studio +Now you should be able to copy ./src/libprojectM/.libs/libprojectM.so and appropriate headers to projectm-android, and +build it using Android Studio ### LLVM JIT -There are some optmizations for parsing preset equations that leverage the LLVM JIT. You can try `./compile --enable--llvm` to enable them. They may not work with newer version of LLVM (https://github.com/projectM-visualizer/projectm/pull/360) +There are some optimizations for parsing preset equations that leverage the LLVM JIT. You can +try `./compile --enable--llvm` to enable them. They may not work with newer version of +LLVM (https://github.com/projectM-visualizer/projectm/pull/360) ## libprojectM `libprojectM` is the core library. It is made up of three sub-libraries: #### Renderer + Made up of everything in `src/libprojectM/Renderer`. These files compose the `libRenderer` sub-library. #### MilkdropPresetFactory / NativePresetFactory -From their respective folders. Native presets are visualizations that are implemented in C++ instead of `.milk` preset files. They are completely optional. Milkdrop presets are technically optional but the whole thing is basically useless without them. -If you don't want native presets, and you probably don't, don't bother with them. Ideally there should be a configure option to disable them, probably on by default (at this time this is needed for autoconf: https://github.com/projectM-visualizer/projectm/issues/99). +From their respective folders. Native presets are visualizations that are implemented in C++ instead of `.milk` preset +files. They are completely optional. Milkdrop presets are technically optional but the whole thing is basically useless +without them. +If you don't want native presets, and you probably don't, don't bother with them. Ideally there should be a configure +option to disable them, probably on by default (at this time this is needed for +autoconf: https://github.com/projectM-visualizer/projectm/issues/99). ### Assets -`libprojectM` can either have a configuration hard-coded or load from a configuration file. It's up to each application to decide how to load the config file. The config file can have paths defined specifying where to load fonts and presets from. -You will want to install the config file and presets somewhere, and then define that path for the application you're trying to build. +`libprojectM` can either have a configuration hard-coded or load from a configuration file. It's up to each application +to decide how to load the config file. The config file can have paths defined specifying where to load fonts and presets +from. + +You will want to install the config file and presets somewhere, and then define that path for the application you're +trying to build. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..3d1ebe1716 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,199 @@ +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +if(CMAKE_VERSION VERSION_LESS 3.19 AND CMAKE_GENERATOR STREQUAL "Xcode") + message(AUTHOR_WARNING "Using a CMake version before 3.19 with a recent Xcode SDK and the Xcode generator " + "will likely result in CMake failing to find the AppleClang compiler. Either upgrade CMake to at least " + "version 3.19 or use a different generator, e.g. \"Unix Makefiles\" or \"Ninja\".") +endif() + +include(CMakeDependentOption) +include(CheckSymbolExists) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_POSITION_INDEPENDENT_CODE YES) + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +option(ENABLE_DEBUG_POSTFIX "Add \"d\" after library names for debug builds" ON) +if(ENABLE_DEBUG_POSTFIX) + set(CMAKE_DEBUG_POSTFIX d) +endif() + +# The API (SO) and detailed library versions for the shared library. +set(PROJECTM_SO_VERSION "3") +set(PROJECTM_LIB_VERSION "3.1.1") + +project(projectm + LANGUAGES C CXX + VERSION 3.1.13 + ) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + +set(PROJECTM_BIN_DIR "bin" CACHE STRING "Executable installation directory, relative to the install prefix.") +set(PROJECTM_DATADIR_PATH "share/projectM" CACHE STRING "Default (absolute) path to the projectM data files (presets etc.)") +set(PROJECTM_LIB_DIR "lib" CACHE STRING "Library installation directory, relative to the install prefix.") +set(PROJECTM_INCLUDE_DIR "include" CACHE STRING "Header installation directory, relative to the install prefix.") + +# Feature options, including dependencies. +option(ENABLE_PRESETS "Build and install bundled presets" ON) +option(ENABLE_NATIVE_PRESETS "Build and install native libraries written in C/C++" OFF) +option(ENABLE_TESTING "Build and install the projectM test suite" OFF) +option(ENABLE_EMSCRIPTEN "Build for web with emscripten" OFF) +cmake_dependent_option(ENABLE_SDL "Enable SDL2 support" ON "NOT ENABLE_EMSCRIPTEN;ENABLE_TESTING" ON) +cmake_dependent_option(ENABLE_GLES "Enable OpenGL ES support" OFF "NOT ENABLE_EMSCRIPTEN" ON) +cmake_dependent_option(ENABLE_THREADING "Enable multithreading support" ON "NOT ENABLE_EMSCRIPTEN;NOT CMAKE_SYSTEM_NAME STREQUAL Windows" OFF) +cmake_dependent_option(ENABLE_PULSEAUDIO "Build Pulseaudio-based Qt UI" OFF "ENABLE_QT;NOT ENABLE_EMSCRIPTEN;CMAKE_SYSTEM_NAME STREQUAL Linux" OFF) +cmake_dependent_option(ENABLE_JACK "Build JACK-based Qt and SDL UIs" OFF "ENABLE_QT;NOT ENABLE_EMSCRIPTEN;CMAKE_SYSTEM_NAME STREQUAL Linux" OFF) +cmake_dependent_option(ENABLE_LLVM "Enable LLVM JIT support" OFF "NOT ENABLE_EMSCRIPTEN" OFF) +cmake_dependent_option(ENABLE_LIBVISUAL "Build and install the projectM libvisual plug-in" OFF "NOT ENABLE_EMSCRIPTEN;CMAKE_SYSTEM_NAME STREQUAL Linux" OFF) + +find_package(GLM) +if(NOT TARGET GLM::GLM) + add_library(GLM::GLM INTERFACE IMPORTED) + set_target_properties(GLM::GLM PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/vendor" + ) + + message(STATUS "GLM library not found, using bundled version.") + set(USE_SYSTEM_GLM OFF) +else() + set(USE_SYSTEM_GLM ON) +endif() + +if(ENABLE_EMSCRIPTEN) + message(STATUS "${CMAKE_C_COMPILER}") + check_symbol_exists(__EMSCRIPTEN__ "" HAVE_EMSCRIPTEN) + if(NOT HAVE_EMSCRIPTEN) + message(FATAL_ERROR "You are not using an emscripten compiler.") + endif() +endif() + +if(ENABLE_SDL) + find_package(SDL2 REQUIRED) + + # Temporary fix to deal with wrong include dir set by SDL2's CMake configuration. + get_target_property(_SDL2_INCLUDE_DIR SDL2::SDL2 INTERFACE_INCLUDE_DIRECTORIES) + if(_SDL2_INCLUDE_DIR MATCHES "(.+)/SDL2\$") + message(STATUS "SDL2 include dir contains \"SDL2\" subdir (SDL bug #4004) - fixing to \"${CMAKE_MATCH_1}\".") + set_target_properties(SDL2::SDL2 PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_MATCH_1}" + ) + endif() + + if(SDL2_VERSION VERSION_LESS "2.0.5") + message(FATAL_ERROR "SDL2 libraries were found, but have version ${SDL2_VERSION}. At least version 2.0.5 is required.") + endif() +endif() + +if(ENABLE_GLES) + find_package(GLES3 REQUIRED) +else() + find_package(OpenGL REQUIRED) + set(PROJECTM_OPENGL_LIBRARIES OpenGL::GL) + # GLX is required by SOIL2 on platforms with the X Window System (e.g. most Linux distributions) + if(TARGET OpenGL::GLX) + list(APPEND PROJECTM_OPENGL_LIBRARIES OpenGL::GLX) + endif() + if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + find_package(GLEW REQUIRED) + list(APPEND PROJECTM_OPENGL_LIBRARIES GLEW::glew) + endif() +endif() + +if(ENABLE_THREADING) + set(CMAKE_THREAD_PREFER_PTHREAD YES) + find_package(Threads REQUIRED) + if(NOT CMAKE_USE_PTHREADS_INIT) + message(FATAL_ERROR "Threading support requested - pthread support is required, but not available.") + endif() + set(USE_THREADS YES) +endif() + +if(ENABLE_LLVM) + find_package(LLVM REQUIRED) + set(HAVE_LLVM TRUE) +else() + unset(HAVE_LLVM) +endif() + +if(ENABLE_PULSEAUDIO) + find_package(Pulseaudio REQUIRED) +endif() + +if(ENABLE_JACK) + find_package(JACK REQUIRED) +endif() + +if(ENABLE_PULSEAUDIO OR ENABLE_JACK) + find_package(Qt5 REQUIRED COMPONENTS Gui OpenGL) +endif() + +if(ENABLE_LIBVISUAL) + find_package(libvisual REQUIRED) + set(PROJECTM_LIBVISUAL_DIR "${LIBVISUAL_PLUGINSBASEDIR}/actor" CACHE STRING "Installation directory for the libvisual plug-in library.") +endif() + +include(features.cmake) + +add_subdirectory(presets) +add_subdirectory(src) + +message(STATUS "") +message(STATUS "projectM v${PROJECT_VERSION}") +message(STATUS " =====") +message(STATUS "") +message(STATUS " prefix: ${CMAKE_INSTALL_PREFIX}") +message(STATUS " libdir: ${PROJECTM_LIB_DIR}") +message(STATUS " includedir: ${PROJECTM_INCLUDE_DIR}") +message(STATUS " bindir: ${PROJECTM_BIN_DIR}") +message(STATUS " libvisual plugin dir: ${PROJECTM_LIBVISUAL_DIR}") +message(STATUS "") +message(STATUS " compiler: ${CMAKE_CXX_COMPILER}") +message(STATUS " cflags: ${CMAKE_C_FLAGS}") +message(STATUS " cxxflags: ${CMAKE_CXX_FLAGS}") +message(STATUS " ldflags: ${CMAKE_SHARED_LINKER_FLAGS}") +message(STATUS "") +message(STATUS " DATADIR_PATH: ${PROJECTM_DATADIR_PATH}") +message(STATUS " - - -") +message(STATUS "") +message(STATUS " Applications:") +message(STATUS " =====") +message(STATUS "") +message(STATUS " libprojectM: ON") +message(STATUS " Presets: ${ENABLE_PRESETS}") +message(STATUS " Native presets: ${ENABLE_NATIVE_PRESETS}") +message(STATUS " Tests: ${ENABLE_TESTING}") +message(STATUS " Threading: ${ENABLE_THREADING}") +message(STATUS " SDL2: ${ENABLE_SDL}") +if(ENABLE_SDL) + message(STATUS " SDL2 version: ${SDL2_VERSION}") +endif() +message(STATUS " Pulseaudio UI: ${ENABLE_PULSEAUDIO}") +if(ENABLE_PULSEAUDIO) + message(STATUS " Pulseaudio version: ${PULSEAUDIO_VERSION}") +endif() +message(STATUS " JACK UI: ${ENABLE_JACK}") +if(ENABLE_JACK) + message(STATUS " JACK version: ${JACK_VERSION}") +endif() +if(ENABLE_PULSEAUDIO OR ENABLE_JACK) + message(STATUS " Qt version: ${Qt5_VERSION}") +endif() +message(STATUS " OpenGLES: ${ENABLE_GLES}") +message(STATUS " Emscripten: ${ENABLE_EMSCRIPTEN}") +message(STATUS " LLVM JIT: ${ENABLE_LLVM}") +if(ENABLE_LLVM) + message(STATUS " LLVM version: ${LLVM_VERSION}") +endif() +message(STATUS " libvisual plug-in: ${ENABLE_LIBVISUAL}") +message(STATUS " Use system GLM: ${USE_SYSTEM_GLM}") + +message(AUTHOR_WARNING + "The CMake build scripts for projectM are still in active development.\n" + "This means that build parameters, exported target names and other things can change " + "at any time until the new build system is officially documented and announced to be " + "fully supported.\n" + "DO NOT base any production work on it yet!\n" + ) diff --git a/cmake/CheckEnumValueExists.cmake b/cmake/CheckEnumValueExists.cmake new file mode 100644 index 0000000000..9930d3f1fc --- /dev/null +++ b/cmake/CheckEnumValueExists.cmake @@ -0,0 +1,26 @@ +include_guard() + +include(CheckCSourceCompiles) + +macro(check_enum_value_exists enum_value include variable) + if(NOT DEFINED ${variable}) + message(STATUS "Looking for enum value ${enum_value}") + set(_CMAKE_REQUIRED_QUIET_tmp "${CMAKE_REQUIRED_QUIET}") + set(CMAKE_REQUIRED_QUIET ON) + check_c_source_compiles(" +#include <${include}> +int main() { + int tmp = ${enum_value}; + return 0; +} +" + ${variable} + ) + if(${variable}) + message(STATUS "Looking for enum value ${enum_value} - found") + else() + message(STATUS "Looking for enum value ${enum_value} - not found") + endif() + set(CMAKE_REQUIRED_QUIET "${_CMAKE_REQUIRED_QUIET_tmp}") + endif() +endmacro() diff --git a/cmake/EnableCFlagsIfSupported.cmake b/cmake/EnableCFlagsIfSupported.cmake new file mode 100644 index 0000000000..25643fdd10 --- /dev/null +++ b/cmake/EnableCFlagsIfSupported.cmake @@ -0,0 +1,47 @@ +include(CheckCXXCompilerFlag) +include(CheckCXXSourceCompiles) + +macro(enable_cflags_if_supported) + foreach(flag ${ARGN}) + check_cxx_compiler_flag("${flag}" CXXFLAG_${flag}_SUPPORTED) + if(CXXFLAG_${flag}_SUPPORTED) + add_compile_options("${flag}") + endif() + endforeach() +endmacro() + +macro(enable_linker_flags_if_supported) + set(_old_CMAKE_REQUIRED_LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}") + foreach(flag ${ARGN}) + set(CMAKE_REQUIRED_LINK_OPTIONS "${flag}") + check_cxx_source_compiles("int main(){return 0;}" LDCFLAG_${flag}_SUPPORTED) + if(LDCFLAG_${flag}_SUPPORTED) + add_link_options(${flag}) + else() + set(CMAKE_REQUIRED_LINK_OPTIONS "LINKER:${flag}") + check_cxx_source_compiles("int main(){return 0;}" LDCFLAG_LINKER_${flag}_SUPPORTED) + if(LDFLAG_LINKER_${flag}_SUPPORTED) + add_link_options(LINKER:${flag}) + endif() + endif() + endforeach() + set(CMAKE_REQUIRED_LINK_OPTIONS "${_old_CMAKE_REQUIRED_LINK_OPTIONS}") +endmacro() + +macro(enable_target_linker_flags_if_supported target access) + set(_old_CMAKE_REQUIRED_LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}") + foreach(flag ${ARGN}) + set(CMAKE_REQUIRED_LINK_OPTIONS "${flag}") + check_cxx_source_compiles("int main(){return 0;}" LDCFLAG_${flag}_SUPPORTED) + if(LDCFLAG_${flag}_SUPPORTED) + target_link_options(${target} ${access} ${flag}) + else() + set(CMAKE_REQUIRED_LINK_OPTIONS "LINKER:${flag}") + check_cxx_source_compiles("int main(){return 0;}" LDCFLAG_LINKER_${flag}_SUPPORTED) + if(LDFLAG_LINKER_${flag}_SUPPORTED) + target_link_options(${target} ${access} LINKER:${flag}) + endif() + endif() + endforeach() + set(CMAKE_REQUIRED_LINK_OPTIONS "${_old_CMAKE_REQUIRED_LINK_OPTIONS}") +endmacro() diff --git a/cmake/FindGLES3.cmake b/cmake/FindGLES3.cmake new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmake/FindGLM.cmake b/cmake/FindGLM.cmake new file mode 100644 index 0000000000..18e5393a53 --- /dev/null +++ b/cmake/FindGLM.cmake @@ -0,0 +1,20 @@ +# The GLM library is header-only, so we only need to find the glm/glm.hpp header. +find_path(GLM_INCLUDE_DIR + glm/glm.hpp + ) + +find_file(GLM_INCLUDE_FILE + glm/glm.hpp + ) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(GLM DEFAULT_MSG GLM_INCLUDE_FILE GLM_INCLUDE_DIR) + +if(GLM_FOUND AND NOT TARGET GLM::GLM) + add_library(GLM::GLM INTERFACE IMPORTED) + + set_target_properties(GLM::GLM PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${GLM_INCLUDE_DIR}" + ) +endif() diff --git a/cmake/FindJACK.cmake b/cmake/FindJACK.cmake new file mode 100644 index 0000000000..2df4db8e9b --- /dev/null +++ b/cmake/FindJACK.cmake @@ -0,0 +1,33 @@ +# First try to use PKG_CONFIG to find JACK. +find_package(PkgConfig QUIET) +if(PKG_CONFIG_FOUND) + pkg_check_modules(JACK jack QUIET) +endif() + +if(NOT JACK_INCLUDEDIR OR NOT JACK_LIBRARIES) + find_path(JACK_INCLUDEDIR + jack/jack.h + ) + + find_library(JACK_LIBRARIES + jack + ) +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(JACK + REQUIRED_VARS JACK_LIBRARIES JACK_INCLUDEDIR + VERSION_VAR JACK_VERSION + ) + +if(JACK_FOUND AND NOT TARGET JACK::JACK) + add_library(JACK::JACK INTERFACE IMPORTED) + + set_target_properties(JACK::JACK PROPERTIES + INTERFACE_LINK_LIBRARIES "${JACK_LIBRARIES}" + INTERFACE_LINK_DIRECTORIES "${JACK_LIBRARY_DIRS}" + INTERFACE_INCLUDE_DIRECTORIES "${JACK_INCLUDEDIR}" + INTERFACE_COMPILE_OPTIONS "${JACK_CFLAGS}" + ) +endif() diff --git a/cmake/FindLLVM.cmake b/cmake/FindLLVM.cmake new file mode 100644 index 0000000000..ba5fd68756 --- /dev/null +++ b/cmake/FindLLVM.cmake @@ -0,0 +1,55 @@ +# Find and use llvm-config to determine the proper library and include locations. + +find_program(LLVM_CONFIG_COMMAND + NAMES llvm-config llvm-config.exe + ) + +if(LLVM_CONFIG_COMMAND) + execute_process(COMMAND + ${LLVM_CONFIG_COMMAND} --version + OUTPUT_VARIABLE LLVM_VERSION + ) + string(STRIP "${LLVM_VERSION}" LLVM_VERSION) + + execute_process(COMMAND + ${LLVM_CONFIG_COMMAND} --includedir + OUTPUT_VARIABLE LLVM_INCLUDE_DIR + ) + string(STRIP "${LLVM_INCLUDE_DIR}" LLVM_INCLUDE_DIR) + + execute_process(COMMAND + ${LLVM_CONFIG_COMMAND} --libdir + OUTPUT_VARIABLE LLVM_LIB_DIR + ) + string(STRIP "${LLVM_LIB_DIR}" LLVM_LIB_DIR) + + execute_process(COMMAND + ${LLVM_CONFIG_COMMAND} --libs + OUTPUT_VARIABLE LLVM_LIBRARIES + ) + string(STRIP "${LLVM_LIBRARIES}" LLVM_LIBRARIES) + + execute_process(COMMAND + ${LLVM_CONFIG_COMMAND} --ldflags + OUTPUT_VARIABLE LLVM_LDFLAGS + ) + string(STRIP "${LLVM_LDFLAGS}" LLVM_LDFLAGS) +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(LLVM + REQUIRED_VARS LLVM_CONFIG_COMMAND LLVM_LIBRARIES LLVM_LIB_DIR LLVM_INCLUDE_DIR LLVM_LDFLAGS + VERSION_VAR LLVM_VERSION + ) + +if(LLVM_FOUND AND NOT TARGET LLVM::LLVM) + add_library(LLVM::LLVM INTERFACE IMPORTED) + + set_target_properties(LLVM::LLVM PROPERTIES + INTERFACE_LINK_LIBRARIES "${LLVM_LIBRARIES}" + INTERFACE_LINK_DIRECTORIES "${LLVM_LIB_DIR}" + INTERFACE_INCLUDE_DIRECTORIES "${LLVM_INCLUDE_DIR}" + INTERFACE_LINK_OPTIONS "${LLVM_LDFLAGS}" + ) +endif() diff --git a/cmake/FindPulseaudio.cmake b/cmake/FindPulseaudio.cmake new file mode 100644 index 0000000000..9fff91e68c --- /dev/null +++ b/cmake/FindPulseaudio.cmake @@ -0,0 +1,44 @@ +# First try to use PKG_CONFIG to find Pulseaudio. +find_package(PkgConfig QUIET) +if(PKG_CONFIG_FOUND) + pkg_check_modules(PULSEAUDIO libpulse QUIET) +endif() + +if(NOT PULSEAUDIO_INCLUDEDIR OR NOT PULSEAUDIO_LIBRARIES) + find_path(PULSEAUDIO_INCLUDEDIR + pulse/pulseaudio.h + ) + + find_library(PULSEAUDIO_LIBRARIES + pulse + ) + + if(PULSEAUDIO_INCLUDEDIR AND EXISTS "${PULSEAUDIO_INCLUDEDIR}/pulse/version.h") + file(STRINGS "${PULSEAUDIO_INCLUDEDIR}/pulse/version.h" pulseaudio_version_str + REGEX "pa_get_headers_version\(\)" + ) + if(pulseaudio_version_str AND "${pulseaudio_version_str}" MATCHES "\\(\"([0-9.]+)\"\\)") + set(PULSEAUDIO_VERSION "${CMAKE_MATCH_1}") + endif() + endif() + + set(PULSEAUDIO_CFLAGS "-D_REENTRANT") +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Pulseaudio + REQUIRED_VARS PULSEAUDIO_LIBRARIES PULSEAUDIO_INCLUDEDIR + VERSION_VAR PULSEAUDIO_VERSION + ) + +if(Pulseaudio_FOUND AND NOT TARGET Pulseaudio::Pulseaudio) + add_library(Pulseaudio::Pulseaudio INTERFACE IMPORTED) + + set_target_properties(Pulseaudio::Pulseaudio PROPERTIES + INTERFACE_LINK_LIBRARIES "${PULSEAUDIO_LIBRARIES}" + INTERFACE_LINK_DIRECTORIES "${PULSEAUDIO_LIBRARY_DIRS}" + INTERFACE_INCLUDE_DIRECTORIES "${PULSEAUDIO_INCLUDEDIR}" + INTERFACE_COMPILE_OPTIONS "${PULSEAUDIO_CFLAGS}" + ) +endif() diff --git a/cmake/Findlibvisual.cmake b/cmake/Findlibvisual.cmake new file mode 100644 index 0000000000..ee04ebf914 --- /dev/null +++ b/cmake/Findlibvisual.cmake @@ -0,0 +1,42 @@ +# First try to use PKG_CONFIG to find libvisual. +find_package(PkgConfig QUIET) +if(PKG_CONFIG_FOUND) + pkg_check_modules(LIBVISUAL libvisual-0.4 QUIET) + + if(LIBVISUAL_INCLUDEDIR) + # Retrieve the plug-in install directory + pkg_get_variable(LIBVISUAL_PLUGINSBASEDIR libvisual-0.4 pluginsbasedir) + endif() +endif() + +if(NOT LIBVISUAL_INCLUDEDIR OR NOT LIBVISUAL_LIBRARIES) + find_path(LIBVISUAL_INCLUDEDIR + libvisual/libvisual.h + PATH_SUFFIXES libvisual-0.4 + ) + + find_library(LIBVISUAL_LIBRARIES + visual-0.4 + ) + + # Use the default path. + set(LIBVISUAL_PLUGINSBASEDIR "lib/libvisual-0.4") +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(libvisual + REQUIRED_VARS LIBVISUAL_INCLUDEDIR LIBVISUAL_LIBRARIES + VERSION_VAR LIBVISUAL_VERSION + ) + +if(LIBVISUAL_FOUND AND NOT TARGET libvisual::libvisual) + add_library(libvisual::libvisual INTERFACE IMPORTED) + + set_target_properties(libvisual::libvisual PROPERTIES + INTERFACE_LINK_LIBRARIES "${LIBVISUAL_LIBRARIES}" + INTERFACE_LINK_DIRECTORIES "${LIBVISUAL_LIBRARY_DIRS}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBVISUAL_INCLUDEDIR}" + INTERFACE_COMPILE_OPTIONS "${LIBVISUAL_CFLAGS}" + ) +endif() diff --git a/config.h.cmake.in b/config.h.cmake.in new file mode 100644 index 0000000000..c56b0d1c1a --- /dev/null +++ b/config.h.cmake.in @@ -0,0 +1,73 @@ +#pragma once + +/* Define to 1 if you have the `aligned_alloc' function. */ +#cmakedefine01 HAVE_ALIGNED_ALLOC + +/* Define to 1 if you have the header file. */ +#cmakedefine01 HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#cmakedefine01 HAVE_FTS_H + +/* Defined if a valid OpenGL implementation is found. */ +#cmakedefine01 HAVE_GL + +/* Define to 1 if you have the header file. */ +#cmakedefine01 HAVE_GLES_GL_H + +/* Define to 1 if you have the header file. */ +#cmakedefine01 HAVE_GL_GL_H + +/* Define to 1 if you have the header file. */ +#cmakedefine01 HAVE_INTTYPES_H + +/* Define HAVE_LLVM */ +#cmakedefine01 HAVE_LLVM + +/* Define to 1 if you have the header file. */ +#cmakedefine01 HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#cmakedefine01 HAVE_OPENGL_GL_H + +/* Define to 1 if you have the `posix_memalign' function. */ +#cmakedefine01 HAVE_POSIX_MEMALIGN + +/* Have PTHREAD_PRIO_INHERIT. */ +#cmakedefine01 HAVE_PTHREAD_PRIO_INHERIT + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_WINDOWS_H + +/* Define to 1 if you have the ANSI C header files. */ +#cmakedefine STDC_HEADERS + +/* Define USE_GLES */ +#cmakedefine01 USE_GLES + +/* Define USE_THREADS */ +#cmakedefine01 USE_THREADS + +/* Version number of package */ +#define VERSION "@PROJECT_VERSION@" diff --git a/features.cmake b/features.cmake new file mode 100644 index 0000000000..2541e4bbb8 --- /dev/null +++ b/features.cmake @@ -0,0 +1,103 @@ +# Tests for various platform features and generates a header with the appropriate defines, +# similar to the one created by autotools. + +include(CheckSymbolExists) +include(CheckCXXSymbolExists) +include(CheckFunctionExists) +include(CheckIncludeFileCXX) +include(CheckEnumValueExists) +include(CheckIncludeFiles) +include(EnableCFlagsIfSupported) + +add_compile_definitions( + $<$:DEBUG> + ) + +if(NOT MSVC) + enable_cflags_if_supported( + -Wall + -Wchar-subscripts + -Wformat-security + -Wpointer-arith + -Wshadow + -Wsign-compare + -Wtype-limits + -fopenmp + ) + + enable_linker_flags_if_supported( + -fopenmp + ) +else() + enable_cflags_if_supported( + /W4 + /openmp + ) + + enable_linker_flags_if_supported( + /openmp + ) +endif() + +check_function_exists(aligned_alloc HAVE_ALIGNED_ALLOC) +check_include_file_cxx("dlfcn.h" HAVE_DLFCN_H) +check_include_file_cxx("fts.h" HAVE_FTS_H) +check_include_file_cxx("GL/gl.h" HAVE_GL_GL_H) +check_include_file_cxx("inttypes.h" HAVE_INTTYPES_H) +check_include_file_cxx("memory.h" HAVE_MEMORY_H) +check_function_exists(posix_memalign HAVE_POSIX_MEMALIGN) +check_enum_value_exists("PTHREAD_PRIO_INHERIT" "pthread.h" HAVE_PTHREAD_PRIO_INHERIT) +check_include_file_cxx("fts.h" HAVE_FTS_H) +check_include_file_cxx("stdint.h" HAVE_STDINT_H) +check_include_file_cxx("stdlib.h" HAVE_STDLIB_H) +check_include_file_cxx("strings.h" HAVE_STRINGS_H) +check_include_file_cxx("string.h" HAVE_STRING_H) +check_include_file_cxx("sys/stat.h" HAVE_SYS_STAT_H) +check_include_file_cxx("sys/types.h" HAVE_SYS_TYPES_H) +check_include_file_cxx("unistd.h" HAVE_UNISTD_H) +check_include_file_cxx("windows.h" HAVE_WINDOWS_H) + +set(_std_c_headers + # C89/C90 + assert.h + ctype.h + errno.h + float.h + limits.h + locale.h + math.h + setjmp.h + signal.h + stdarg.h + stddef.h + stdio.h + stdlib.h + string.h + time.h + # C95/NA1 + iso646.h + wctype.h + # C99 + complex.h + fenv.h + inttypes.h + stdbool.h + stdint.h + tgmath.h + ) +check_include_files("${_std_c_headers}" STDC_HEADERS LANGUAGE C) +unset(_std_c_headers) + +# Create global configuration header +file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/include") +configure_file(config.h.cmake.in "${CMAKE_BINARY_DIR}/include/config.h") +include_directories("${CMAKE_BINARY_DIR}/include") +add_compile_definitions(HAVE_CONFIG_H) + +# Force-include the file in all targets +if(MSVC) + add_definitions(/FI"config.h") +else() + # GCC or Clang + add_definitions(-include config.h) +endif() diff --git a/msvc/eglew.h b/msvc/GL/eglew.h similarity index 100% rename from msvc/eglew.h rename to msvc/GL/eglew.h diff --git a/msvc/glew.c b/msvc/GL/glew.c similarity index 99% rename from msvc/glew.c rename to msvc/GL/glew.c index 9a095ed231..e63c2abc0d 100644 --- a/msvc/glew.c +++ b/msvc/GL/glew.c @@ -31,7 +31,7 @@ */ #ifndef GLEW_INCLUDE -#include "glew.h" +#include "GL/glew.h" #else #include GLEW_INCLUDE #endif @@ -50,7 +50,7 @@ # if defined(NOGDI) # undef NOGDI # endif -# include "wglew.h" +# include "GL/wglew.h" #elif !defined(__ANDROID__) && !defined(__native_client__) && !defined(__HAIKU__) && (!defined(__APPLE__) || defined(GLEW_APPLE_GLX)) # include #endif diff --git a/msvc/glew.h b/msvc/GL/glew.h similarity index 100% rename from msvc/glew.h rename to msvc/GL/glew.h diff --git a/msvc/glewinfo.c b/msvc/GL/glewinfo.c similarity index 100% rename from msvc/glewinfo.c rename to msvc/GL/glewinfo.c diff --git a/msvc/glxew.h b/msvc/GL/glxew.h similarity index 100% rename from msvc/glxew.h rename to msvc/GL/glxew.h diff --git a/msvc/wglew.h b/msvc/GL/wglew.h similarity index 100% rename from msvc/wglew.h rename to msvc/GL/wglew.h diff --git a/msvc/projectM.vcxproj b/msvc/projectM.vcxproj index 4066f1e17e..501d85a2a7 100644 --- a/msvc/projectM.vcxproj +++ b/msvc/projectM.vcxproj @@ -133,8 +133,8 @@ - - + + CompileAsC CompileAsC diff --git a/msvc/projectM.vcxproj.filters b/msvc/projectM.vcxproj.filters index 1f55d2772e..c8ef34e335 100644 --- a/msvc/projectM.vcxproj.filters +++ b/msvc/projectM.vcxproj.filters @@ -15,11 +15,11 @@ - + - + diff --git a/presets/CMakeLists.txt b/presets/CMakeLists.txt new file mode 100644 index 0000000000..cfd0e99748 --- /dev/null +++ b/presets/CMakeLists.txt @@ -0,0 +1,25 @@ +if(ENABLE_PRESETS) + + install(DIRECTORY + presets_bltc201 + presets_eyetune + presets_milkdrop + presets_milkdrop_104 + presets_milkdrop_200 + presets_mischa_collection + presets_projectM + presets_stock + presets_tryptonaut + presets_yin + DESTINATION "${PROJECTM_DATADIR_PATH}/presets" + COMPONENT Presets + ) + + if(ENABLE_TESTING) + install(DIRECTORY + tests + DESTINATION "${PROJECTM_DATADIR_PATH}/presets" + COMPONENT Tests + ) + endif() +endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000000..75bfb34fbe --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,8 @@ +add_subdirectory(libprojectM) +add_subdirectory(NativePresets) +add_subdirectory(projectM-qt) +add_subdirectory(projectM-jack) +add_subdirectory(projectM-libvisual) +add_subdirectory(projectM-pulseaudio) +add_subdirectory(projectM-sdl) +add_subdirectory(projectM-test) diff --git a/src/NativePresets/CMakeLists.txt b/src/NativePresets/CMakeLists.txt new file mode 100644 index 0000000000..b77c05bfb1 --- /dev/null +++ b/src/NativePresets/CMakeLists.txt @@ -0,0 +1,70 @@ +if(NOT ENABLE_PRESETS OR NOT ENABLE_NATIVE_PRESETS) + return() +endif() + +# Using a macro to reduce repetition. +macro(add_native_preset name) + if(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") + add_library(${name}_static STATIC ${name}.cpp) + + set_target_properties(${name}_static PROPERTIES + OUTPUT_NAME ${name} + ) + + target_link_libraries(${name}_static + PRIVATE + projectM_static + GLM::GLM + ) + + install(TARGETS ${name}_static + LIBRARY DESTINATION "${PROJECTM_DATADIR_PATH}/presets" + RUNTIME DESTINATION "${PROJECTM_DATADIR_PATH}/presets" + ARCHIVE DESTINATION "${PROJECTM_DATADIR_PATH}/presets" + COMPONENT Presets + ) + endif() + + add_library(${name}_shared SHARED ${name}.cpp) + + target_compile_definitions(${name}_shared + PRIVATE + ${name}_EXPORTS + ) + + set_target_properties(${name}_shared PROPERTIES + OUTPUT_NAME ${name} + ) + + # On Windows, libprojectM is only built statically. + if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + target_link_libraries(${name}_shared + PRIVATE + projectM_static + ) + else() + target_link_libraries(${name}_shared + PRIVATE + projectM_shared + ) + endif() + + target_link_libraries(${name}_shared + PRIVATE + GLM::GLM + ) + + install(TARGETS ${name}_shared + LIBRARY DESTINATION "${PROJECTM_DATADIR_PATH}/presets" + RUNTIME DESTINATION "${PROJECTM_DATADIR_PATH}/presets" + ARCHIVE DESTINATION "${PROJECTM_DATADIR_PATH}/presets" + COMPONENT Presets + ) +endmacro() + +add_native_preset(MstressJuppyDancer) +add_native_preset(RLGFractalDrop7c) +add_native_preset(RovastarDarkSecret) +add_native_preset(RovastarDriftingChaos) +add_native_preset(RovastarFractalSpiral) +add_native_preset(RovastarFractopiaFrantic) diff --git a/src/libprojectM/CMakeLists.txt b/src/libprojectM/CMakeLists.txt new file mode 100644 index 0000000000..3bb393e79d --- /dev/null +++ b/src/libprojectM/CMakeLists.txt @@ -0,0 +1,333 @@ +add_compile_definitions( + DATADIR_PATH="${PROJECTM_DATADIR_PATH}" + GL_SILENCE_DEPRECATION +) + +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + # dirent.h support + set(MSVC_EXTRA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/msvc") + include_directories("${MSVC_EXTRA_INCLUDE_DIR}") + + add_library(dlfcn OBJECT + msvc/dlfcn.c + msvc/dlfcn.h + ) + + set_target_properties(dlfcn PROPERTIES + FOLDER libprojectM + ) + + # Additional preprocessor definitions for Windows builds + add_compile_definitions( + USE_TEXT_MENU + USE_FTGL + USE_NATIVE_GLEW + STBI_NO_DDS + projectM_FONT_MENU="${CMAKE_SOURCE_DIR}/fonts/VeraMono.ttf" + projectM_FONT_TITLE="${CMAKE_SOURCE_DIR}/fonts/Vera.ttf" + ) +endif() + +add_subdirectory(MilkdropPresetFactory) +add_subdirectory(NativePresetFactory) +add_subdirectory(Renderer) + +# CMake cannot combine multiple static libraries using target_link_libraries. +# This syntax will pull in the compiled object files into the final library. +add_library(projectM_main OBJECT + Common.hpp + ConfigFile.cpp + ConfigFile.h + dlldefs.h + event.h + fatal.h + fftsg.cpp + fftsg.h + FileScanner.cpp + FileScanner.hpp + glError.h + gltext.h + HungarianMethod.hpp + IdleTextures.hpp + KeyHandler.cpp + KeyHandler.hpp + lvtoprojectM.h + PCM.cpp + PCM.hpp + PipelineMerger.cpp + PipelineMerger.hpp + Preset.cpp + Preset.hpp + PresetChooser.cpp + PresetChooser.hpp + PresetFactory.cpp + PresetFactory.hpp + PresetFactoryManager.cpp + PresetFactoryManager.hpp + PresetLoader.cpp + PresetLoader.hpp + projectM.cpp + projectM.hpp + projectM-opengl.h + RandomNumberGenerators.hpp + resource.h + sdltoprojectM.h + TestRunner.cpp + TestRunner.hpp + TimeKeeper.cpp + TimeKeeper.hpp + timer.cpp + timer.h + wipemalloc.cpp + wipemalloc.h + ) + +target_include_directories(projectM_main + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/Renderer" + "${CMAKE_CURRENT_SOURCE_DIR}/Renderer/hlslparser/src" + "${CMAKE_CURRENT_SOURCE_DIR}/MilkdropPresetFactory" + "${CMAKE_CURRENT_SOURCE_DIR}/NativePresetFactory" + "${MSVC_EXTRA_INCLUDE_DIR}" + ) + +add_library(projectM_static STATIC + $ + $ + $ + $ + $ + $ + $<$:$> + ) + +set_target_properties(projectM_static PROPERTIES + OUTPUT_NAME projectM + EXPORT_NAME static + FOLDER libprojectM + ) + +target_include_directories(projectM_static + PUBLIC + "$" + "$" + "$" + "$" + "$" + "$" + "$" + ) + +target_link_libraries(projectM_static + PRIVATE + GLM::GLM + PUBLIC + ${PROJECTM_OPENGL_LIBRARIES} + ) + +if(ENABLE_THREADING) + target_link_libraries(projectM_static + PUBLIC + Threads::Threads + ) +endif() + +if(ENABLE_LLVM) + target_link_libraries(projectM_static + PUBLIC + LLVM::LLVM + ) +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + target_link_libraries(projectM_static + PUBLIC + "-framework CoreFoundation" + ) +endif() + +install(TARGETS projectM_static + EXPORT libprojectMTargets + LIBRARY DESTINATION "${PROJECTM_LIB_DIR}" + RUNTIME DESTINATION "${PROJECTM_LIB_DIR}" + ARCHIVE DESTINATION "${PROJECTM_LIB_DIR}" + COMPONENT Runtime + ) + +# Building projectM as a Windows DLL is not (yet) supported. +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") + add_library(projectM_shared SHARED + $ + $ + $ + $ + $ + $ + $<$:$> + ) + + set_target_properties(projectM_shared PROPERTIES + OUTPUT_NAME projectM + VERSION "${PROJECTM_LIB_VERSION}" + SOVERSION "${PROJECTM_SO_VERSION}" + EXPORT_NAME shared + FOLDER libprojectM + ) + + target_include_directories(projectM_shared + PUBLIC + "$" + "$" + "$" + "$" + "$" + "$" + "$" + ) + + target_link_libraries(projectM_shared + PRIVATE + GLM::GLM + PUBLIC + ${PROJECTM_OPENGL_LIBRARIES} + ${CMAKE_DL_LIBS} + ) + + if(ENABLE_THREADING) + target_link_libraries(projectM_shared + PUBLIC + Threads::Threads + ) + endif() + + if(ENABLE_LLVM) + target_link_libraries(projectM_shared + PUBLIC + LLVM::LLVM + ) + endif() + + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + target_link_libraries(projectM_shared + PUBLIC + "-framework CoreFoundation" + ) + endif() + + enable_target_linker_flags_if_supported(projectM_shared PRIVATE --no-undefined) + + install(TARGETS projectM_shared + EXPORT libprojectMTargets + LIBRARY DESTINATION "${PROJECTM_LIB_DIR}" + RUNTIME DESTINATION "${PROJECTM_LIB_DIR}" + ARCHIVE DESTINATION "${PROJECTM_LIB_DIR}" + COMPONENT Runtime + ) + + set(EXPORT_SHARED_LIB_TARGET projectM_shared) +endif() + +install(FILES + Common.hpp + PCM.hpp + dlldefs.h + event.h + fatal.h + projectM.hpp + DESTINATION "${PROJECTM_INCLUDE_DIR}/libprojectM" + COMPONENT Devel + ) + + + +# CMake target exports + +# For use from a local projectM build tree (without installing) +export(TARGETS + MilkdropPresetFactory + NativePresetFactory + Renderer + hlslparser + SOIL2 + projectM_static + ${EXPORT_SHARED_LIB_TARGET} + NAMESPACE libprojectM:: + FILE projectM-exports.cmake + ) + +export(PACKAGE libprojectM) + + + +# For use from an installed package (system install, vcpkg, homebrew etc.) +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/libprojectM/libprojectMConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion +) + +configure_package_config_file(libprojectMConfig.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/libprojectM/libprojectMConfig.cmake" + INSTALL_DESTINATION "${PROJECTM_LIB_DIR}/cmake/libprojectM" + PATH_VARS PROJECTM_BIN_DIR PROJECTM_INCLUDE_DIR PROJECTM_DATADIR_PATH + ) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/libprojectM/libprojectMConfigVersion.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/libprojectM/libprojectMConfig.cmake" + DESTINATION "${PROJECTM_LIB_DIR}/cmake/libprojectM" + COMPONENT Devel + ) + +install(EXPORT libprojectMTargets + FILE libprojectMTargets.cmake + NAMESPACE libprojectM:: + DESTINATION "${PROJECTM_LIB_DIR}/cmake/libprojectM" + COMPONENT Devel + ) + + + +# pkgconfig export +macro(set_pkg_config_path varname path) + if(IS_ABSOLUTE "${path}") + set(${varname} "${path}") + else() + set(${varname} "\${prefix}/${path}") + endif() +endmacro() + +set(PKGCONFIG_PREFIX "${CMAKE_INSTALL_PREFIX}") +set_pkg_config_path(PKGCONFIG_LIB_DIR "${PROJECTM_LIB_DIR}") +set_pkg_config_path(PKGCONFIG_INCLUDE_DIR "${PROJECTM_INCLUDE_DIR}") +set_pkg_config_path(PKGCONFIG_DATADIR_PATH "${PROJECTM_DATADIR_PATH}") + +configure_file(libprojectM.pc.cmake.in "${CMAKE_BINARY_DIR}/libprojectM.pc" @ONLY) +install(FILES "${CMAKE_BINARY_DIR}/libprojectM.pc" + DESTINATION "${PROJECTM_LIB_DIR}/pkgconfig" + COMPONENT Devel + ) + + + +# If the path is not absolute, need to add the prefix path (and hope the result is absolute). +if(NOT IS_ABSOLUTE PROJECTM_DATADIR_PATH) + set(PROJECTM_DATADIR_PATH_ABSOLUTE "${CMAKE_INSTALL_PREFIX}/${PROJECTM_DATADIR_PATH}") +else() + set(PROJECTM_DATADIR_PATH_ABSOLUTE "${PROJECTM_DATADIR_PATH}") +endif() + +configure_file(config.inp.cmake.in "${CMAKE_BINARY_DIR}/config.inp" @ONLY) +install(FILES "${CMAKE_BINARY_DIR}/config.inp" + DESTINATION "${PROJECTM_DATADIR_PATH}/" + COMPONENT Runtime + ) + +install(DIRECTORY "${CMAKE_SOURCE_DIR}/fonts" + DESTINATION "${PROJECTM_DATADIR_PATH}" + COMPONENT Runtime + ) + diff --git a/src/libprojectM/FileScanner.cpp b/src/libprojectM/FileScanner.cpp index 860d2a3044..ef7eddcc29 100644 --- a/src/libprojectM/FileScanner.cpp +++ b/src/libprojectM/FileScanner.cpp @@ -1,188 +1,188 @@ -// -// FileScanner.cpp -// libprojectM -// -// - -#include "FileScanner.hpp" - -FileScanner::FileScanner() {} - -FileScanner::FileScanner(std::vector &rootDirs, std::vector &extensions) : _rootDirs(rootDirs), _extensions(extensions) {} - -void FileScanner::scan(ScanCallback cb) { -#ifdef HAVE_FTS_H - scanPosix(cb); -#else - for (auto dir : _rootDirs) - scanGeneric(cb, dir.c_str()); -#endif -} - -void FileScanner::handleDirectoryError(std::string dir) { -#ifndef HAVE_FTS_H - std::cerr << "[PresetLoader] warning: errno unsupported on win32, etc platforms. fix me" << std::endl; -#else - - std::cerr << dir << " scan error: "; - - switch ( errno ) - { - case ENOENT: - std::cerr << "ENOENT error. The path \"" << dir << "\" probably does not exist. \"man open\" for more info." << std::endl; - break; - case ENOMEM: - std::cerr << "out of memory!" << std::endl; - abort(); - case ENOTDIR: - std::cerr << "directory specified is not a directory! Trying to continue..." << std::endl; - break; - case ENFILE: - std::cerr << "Your system has reached its open file limit. Trying to continue..." << std::endl; - break; - case EMFILE: - std::cerr << "too many files in use by projectM! Bailing!" << std::endl; - break; - case EACCES: - std::cerr << "permissions issue reading the specified preset directory." << std::endl; - break; - default: - break; - } -#endif -} - -std::string FileScanner::extensionMatches(std::string &filename) { - // returns file name without extension - // TODO: optimize me - - std::string lowerCaseFileName(filename); - std::transform(lowerCaseFileName.begin(), lowerCaseFileName.end(), lowerCaseFileName.begin(), tolower); - - // Remove extension - for (auto ext : _extensions) - { - size_t found = lowerCaseFileName.find(ext); - if (found != std::string::npos) - { - std::string name = filename; - name.replace(int(found), ext.size(), ""); - return name; - } - } - - return {}; -} - -bool FileScanner::isValidFilename(std::string &filename) { - if (filename.find("__MACOSX") != std::string::npos) return false; - return true; -} - -// generic implementation using dirent -void FileScanner::scanGeneric(ScanCallback cb, const char *currentDir) { - DIR * m_dir; - - // Allocate a new a stream given the current directory name - if ((m_dir = opendir(currentDir)) == NULL) - { - return; // no files found in here - } - - struct dirent * dir_entry; - - while ((dir_entry = readdir(m_dir)) != NULL) - { - // Convert char * to friendly string - std::string filename(dir_entry->d_name); - - // Some sanity checks - if (! isValidFilename(filename)) continue; - if (filename.length() > 0 && filename[0] == '.') - continue; - - std::string fullPath = std::string(currentDir) + PATH_SEPARATOR + filename; - - if (dir_entry->d_type == DT_DIR) { - // recurse into dir - scanGeneric(cb, fullPath.c_str()); - continue; - } else if (dir_entry->d_type != DT_REG && dir_entry->d_type != DT_LNK) { - // not regular file/link - continue; - } - - auto nameMatched = extensionMatches(filename); - if (! nameMatched.empty()) - cb(fullPath, nameMatched); - } - - if (m_dir) - { - closedir(m_dir); - m_dir = 0; - } -} - -#ifdef HAVE_FTS_H -// more optimized posix "fts" directory traversal -int fts_compare(const FTSENT** one, const FTSENT** two) { - return (strcmp((*one)->fts_name, (*two)->fts_name)); -} -#endif - -void FileScanner::scanPosix(ScanCallback cb) { -#ifdef HAVE_FTS_H - - // efficient directory traversal - FTS* fileSystem = NULL; - FTSENT *node = NULL; - - // list of directories to scan - auto rootDirCount = _rootDirs.size(); - char **dirList = (char **)malloc(sizeof(char*) * (rootDirCount + 1)); - for (unsigned long i = 0; i < rootDirCount; i++) { - dirList[i] = (char *) _rootDirs[i].c_str(); - } - dirList[rootDirCount] = NULL; - - // initialize file hierarchy traversal - fileSystem = fts_open(dirList, FTS_LOGICAL|FTS_NOCHDIR|FTS_NOSTAT, &fts_compare); - if (fileSystem == NULL) { - std::string s; - for (std::size_t i = 0; i < _rootDirs.size(); i++) - s += _rootDirs[i] + ' '; - handleDirectoryError(s); - - free(dirList); - return; - } - - std::string path, name, nameMatched; - - // traverse dirList - while( (node = fts_read(fileSystem)) != NULL) { - switch (node->fts_info) { - case FTS_F: - case FTS_SL: - case FTS_NSOK: - // found a file - path = std::string(node->fts_path); - name = std::string(node->fts_name); - - if (!isValidFilename(path) || !isValidFilename(name)) break; - - // check extension - nameMatched = extensionMatches(name); - if (! nameMatched.empty()) - cb(path, nameMatched); - break; - default: - break; - } - } - fts_close(fileSystem); - free(dirList); - -#endif -} +// +// FileScanner.cpp +// libprojectM +// +// + +#include "FileScanner.hpp" + +FileScanner::FileScanner() {} + +FileScanner::FileScanner(std::vector &rootDirs, std::vector &extensions) : _rootDirs(rootDirs), _extensions(extensions) {} + +void FileScanner::scan(ScanCallback cb) { +#if HAVE_FTS_H + scanPosix(cb); +#else + for (auto dir : _rootDirs) + scanGeneric(cb, dir.c_str()); +#endif +} + +void FileScanner::handleDirectoryError(std::string dir) { +#ifndef HAVE_FTS_H + std::cerr << "[PresetLoader] warning: errno unsupported on win32, etc platforms. fix me" << std::endl; +#else + + std::cerr << dir << " scan error: "; + + switch ( errno ) + { + case ENOENT: + std::cerr << "ENOENT error. The path \"" << dir << "\" probably does not exist. \"man open\" for more info." << std::endl; + break; + case ENOMEM: + std::cerr << "out of memory!" << std::endl; + abort(); + case ENOTDIR: + std::cerr << "directory specified is not a directory! Trying to continue..." << std::endl; + break; + case ENFILE: + std::cerr << "Your system has reached its open file limit. Trying to continue..." << std::endl; + break; + case EMFILE: + std::cerr << "too many files in use by projectM! Bailing!" << std::endl; + break; + case EACCES: + std::cerr << "permissions issue reading the specified preset directory." << std::endl; + break; + default: + break; + } +#endif +} + +std::string FileScanner::extensionMatches(std::string &filename) { + // returns file name without extension + // TODO: optimize me + + std::string lowerCaseFileName(filename); + std::transform(lowerCaseFileName.begin(), lowerCaseFileName.end(), lowerCaseFileName.begin(), tolower); + + // Remove extension + for (auto ext : _extensions) + { + size_t found = lowerCaseFileName.find(ext); + if (found != std::string::npos) + { + std::string name = filename; + name.replace(int(found), ext.size(), ""); + return name; + } + } + + return {}; +} + +bool FileScanner::isValidFilename(std::string &filename) { + if (filename.find("__MACOSX") != std::string::npos) return false; + return true; +} + +// generic implementation using dirent +void FileScanner::scanGeneric(ScanCallback cb, const char *currentDir) { + DIR * m_dir; + + // Allocate a new a stream given the current directory name + if ((m_dir = opendir(currentDir)) == NULL) + { + return; // no files found in here + } + + struct dirent * dir_entry; + + while ((dir_entry = readdir(m_dir)) != NULL) + { + // Convert char * to friendly string + std::string filename(dir_entry->d_name); + + // Some sanity checks + if (! isValidFilename(filename)) continue; + if (filename.length() > 0 && filename[0] == '.') + continue; + + std::string fullPath = std::string(currentDir) + PATH_SEPARATOR + filename; + + if (dir_entry->d_type == DT_DIR) { + // recurse into dir + scanGeneric(cb, fullPath.c_str()); + continue; + } else if (dir_entry->d_type != DT_REG && dir_entry->d_type != DT_LNK) { + // not regular file/link + continue; + } + + auto nameMatched = extensionMatches(filename); + if (! nameMatched.empty()) + cb(fullPath, nameMatched); + } + + if (m_dir) + { + closedir(m_dir); + m_dir = 0; + } +} + +#if HAVE_FTS_H +// more optimized posix "fts" directory traversal +int fts_compare(const FTSENT** one, const FTSENT** two) { + return (strcmp((*one)->fts_name, (*two)->fts_name)); +} +#endif + +void FileScanner::scanPosix(ScanCallback cb) { +#if HAVE_FTS_H + + // efficient directory traversal + FTS* fileSystem = NULL; + FTSENT *node = NULL; + + // list of directories to scan + auto rootDirCount = _rootDirs.size(); + char **dirList = (char **)malloc(sizeof(char*) * (rootDirCount + 1)); + for (unsigned long i = 0; i < rootDirCount; i++) { + dirList[i] = (char *) _rootDirs[i].c_str(); + } + dirList[rootDirCount] = NULL; + + // initialize file hierarchy traversal + fileSystem = fts_open(dirList, FTS_LOGICAL|FTS_NOCHDIR|FTS_NOSTAT, &fts_compare); + if (fileSystem == NULL) { + std::string s; + for (std::size_t i = 0; i < _rootDirs.size(); i++) + s += _rootDirs[i] + ' '; + handleDirectoryError(s); + + free(dirList); + return; + } + + std::string path, name, nameMatched; + + // traverse dirList + while( (node = fts_read(fileSystem)) != NULL) { + switch (node->fts_info) { + case FTS_F: + case FTS_SL: + case FTS_NSOK: + // found a file + path = std::string(node->fts_path); + name = std::string(node->fts_name); + + if (!isValidFilename(path) || !isValidFilename(name)) break; + + // check extension + nameMatched = extensionMatches(name); + if (! nameMatched.empty()) + cb(path, nameMatched); + break; + default: + break; + } + } + fts_close(fileSystem); + free(dirList); + +#endif +} diff --git a/src/libprojectM/FileScanner.hpp b/src/libprojectM/FileScanner.hpp index a2aeeb0d22..20f0e1835e 100644 --- a/src/libprojectM/FileScanner.hpp +++ b/src/libprojectM/FileScanner.hpp @@ -1,49 +1,49 @@ -// -// FileScanner.hpp -// libprojectM -// -// Cross-platform directory traversal with filtering by extension - -#ifndef FileScanner_hpp -#define FileScanner_hpp - -#include -#include -#include -#include -#include "Common.hpp" -#include - -#ifdef HAVE_FTS_H -#include -extern "C" -{ -#include -#include -} -#else -#include "dirent.h" -#endif - -typedef std::function ScanCallback; - -class FileScanner -{ -public: - FileScanner(); - FileScanner(std::vector &rootDirs, std::vector &extensions); - - void scan(ScanCallback cb); - std::string extensionMatches(std::string &filename); - -private: - std::vector _rootDirs; - std::vector _extensions; - - void scanGeneric(ScanCallback cb, const char *dir); - void scanPosix(ScanCallback cb); - void handleDirectoryError(std::string dir); - bool isValidFilename(std::string &filename); -}; - -#endif /* FileScanner_hpp */ +// +// FileScanner.hpp +// libprojectM +// +// Cross-platform directory traversal with filtering by extension + +#ifndef FileScanner_hpp +#define FileScanner_hpp + +#include +#include +#include +#include +#include "Common.hpp" +#include + +#if HAVE_FTS_H +#include +extern "C" +{ +#include +#include +} +#else +#include "dirent.h" +#endif + +typedef std::function ScanCallback; + +class FileScanner +{ +public: + FileScanner(); + FileScanner(std::vector &rootDirs, std::vector &extensions); + + void scan(ScanCallback cb); + std::string extensionMatches(std::string &filename); + +private: + std::vector _rootDirs; + std::vector _extensions; + + void scanGeneric(ScanCallback cb, const char *dir); + void scanPosix(ScanCallback cb); + void handleDirectoryError(std::string dir); + bool isValidFilename(std::string &filename); +}; + +#endif /* FileScanner_hpp */ diff --git a/src/libprojectM/MilkdropPresetFactory/CMakeLists.txt b/src/libprojectM/MilkdropPresetFactory/CMakeLists.txt new file mode 100644 index 0000000000..f1031a84d0 --- /dev/null +++ b/src/libprojectM/MilkdropPresetFactory/CMakeLists.txt @@ -0,0 +1,64 @@ +add_library(MilkdropPresetFactory OBJECT + BuiltinFuncs.cpp + BuiltinFuncs.hpp + BuiltinParams.cpp + BuiltinParams.hpp + CustomShape.cpp + CustomShape.hpp + CustomWave.cpp + CustomWave.hpp + CValue.hpp + Eval.cpp + Eval.hpp + Expr.cpp + Expr.hpp + Func.cpp + Func.hpp + IdlePreset.cpp + IdlePreset.hpp + InitCond.cpp + InitCond.hpp + InitCondUtils.hpp + JitContext.hpp + MilkdropPreset.cpp + MilkdropPresetFactory.cpp + MilkdropPresetFactory.hpp + MilkdropPreset.hpp + Param.cpp + Param.hpp + ParamUtils.hpp + Parser.cpp + Parser.hpp + PerFrameEqn.cpp + PerFrameEqn.hpp + PerPixelEqn.cpp + PerPixelEqn.hpp + PerPointEqn.cpp + PerPointEqn.hpp + PresetFrameIO.cpp + PresetFrameIO.hpp + ) + +target_include_directories(MilkdropPresetFactory + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/.." + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + ) + +target_link_libraries(MilkdropPresetFactory + PUBLIC + GLM::GLM + ${PROJECTM_OPENGL_LIBRARIES} + ) + +set_target_properties(MilkdropPresetFactory PROPERTIES + FOLDER libprojectM + ) + +if(ENABLE_LLVM) + target_link_libraries(MilkdropPresetFactory + PUBLIC + LLVM::LLVM + ) +endif() diff --git a/src/libprojectM/NativePresetFactory/CMakeLists.txt b/src/libprojectM/NativePresetFactory/CMakeLists.txt new file mode 100644 index 0000000000..3e638ed140 --- /dev/null +++ b/src/libprojectM/NativePresetFactory/CMakeLists.txt @@ -0,0 +1,23 @@ +add_library(NativePresetFactory OBJECT + MilkdropCompatability.hpp + NativePreset.hpp + NativePresetFactory.cpp + NativePresetFactory.hpp + ) + +target_include_directories(NativePresetFactory + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/.." + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + ) + +target_link_libraries(NativePresetFactory + PUBLIC + GLM::GLM + ${PROJECTM_OPENGL_LIBRARIES} + ) + +set_target_properties(NativePresetFactory PROPERTIES + FOLDER libprojectM + ) diff --git a/src/libprojectM/Renderer/CMakeLists.txt b/src/libprojectM/Renderer/CMakeLists.txt new file mode 100644 index 0000000000..2ce3512094 --- /dev/null +++ b/src/libprojectM/Renderer/CMakeLists.txt @@ -0,0 +1,62 @@ +add_subdirectory(hlslparser) +add_subdirectory(SOIL2) + +add_library(Renderer OBJECT + BeatDetect.cpp + BeatDetect.hpp + Filters.cpp + Filters.hpp + MilkdropWaveform.cpp + MilkdropWaveform.hpp + PerlinNoise.cpp + PerlinNoise.hpp + PerlinNoiseWithAlpha.cpp + PerlinNoiseWithAlpha.hpp + PerPixelMesh.cpp + PerPixelMesh.hpp + PipelineContext.cpp + PipelineContext.hpp + Pipeline.cpp + Pipeline.hpp + Renderable.cpp + Renderable.hpp + Renderer.cpp + Renderer.hpp + RenderItemDistanceMetric.cpp + RenderItemDistanceMetric.hpp + RenderItemMatcher.cpp + RenderItemMatcher.hpp + RenderItemMergeFunction.hpp + Shader.cpp + ShaderEngine.cpp + ShaderEngine.hpp + Shader.hpp + StaticGlShaders.cpp + Texture.cpp + Texture.hpp + TextureManager.cpp + TextureManager.hpp + Transformation.hpp + VideoEcho.cpp + VideoEcho.hpp + Waveform.cpp + Waveform.hpp + ) + +target_include_directories(Renderer + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/.." + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + ) + +target_link_libraries(Renderer + PUBLIC + GLM::GLM + hlslparser + SOIL2 + ) + +set_target_properties(Renderer PROPERTIES + FOLDER libprojectM + ) diff --git a/src/libprojectM/Renderer/SOIL2/CMakeLists.txt b/src/libprojectM/Renderer/SOIL2/CMakeLists.txt new file mode 100644 index 0000000000..7bcb49f7ee --- /dev/null +++ b/src/libprojectM/Renderer/SOIL2/CMakeLists.txt @@ -0,0 +1,36 @@ +add_library(SOIL2 OBJECT + etc1_utils.c + etc1_utils.h + image_DXT.c + image_DXT.h + image_helper.c + image_helper.h + pkm_helper.h + pvr_helper.h + SOIL2.c + SOIL2.h + stb_image.h + stb_image_write.h + stbi_DDS.h + stbi_DDS_c.h + stbi_ext.h + stbi_ext_c.h + stbi_pkm.h + stbi_pkm_c.h + stbi_pvr.h + stbi_pvr_c.h + ) + +target_include_directories(SOIL2 + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + ) + +target_link_libraries(SOIL2 + PUBLIC + ${PROJECTM_OPENGL_LIBRARIES} + ) + +set_target_properties(SOIL2 PROPERTIES + FOLDER libprojectM + ) diff --git a/src/libprojectM/Renderer/hlslparser/CMakeLists.txt b/src/libprojectM/Renderer/hlslparser/CMakeLists.txt new file mode 100644 index 0000000000..35f4bb645d --- /dev/null +++ b/src/libprojectM/Renderer/hlslparser/CMakeLists.txt @@ -0,0 +1,23 @@ +add_library(hlslparser OBJECT + src/CodeWriter.cpp + src/CodeWriter.h + src/Engine.cpp + src/Engine.h + src/GLSLGenerator.cpp + src/GLSLGenerator.h + src/HLSLParser.cpp + src/HLSLParser.h + src/HLSLTokenizer.cpp + src/HLSLTokenizer.h + src/HLSLTree.cpp + src/HLSLTree.h + ) + +target_include_directories(hlslparser + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/src" + ) + +set_target_properties(hlslparser PROPERTIES + FOLDER libprojectM + ) diff --git a/src/libprojectM/config.inp.cmake.in b/src/libprojectM/config.inp.cmake.in new file mode 100644 index 0000000000..d4e6402a69 --- /dev/null +++ b/src/libprojectM/config.inp.cmake.in @@ -0,0 +1,22 @@ +# config.inp +# Configuration File for projectM + +#Texture Size = 1024 # Size of internal rendering texture + +Mesh X = 220 # Width of PerPixel Equation mesh +Mesh Y = 125 # Height of PerPixel Equation mesh +FPS = 35 # Frames Per Second +Fullscreen = false +Window Width = 512 # startup window width +Window Height = 512 # startup window height + +Smooth Transition Duration = 5 # in seconds +Preset Duration = 30 # in seconds +Easter Egg Parameter = 1 + +Hard Cut Sensitivity = 10 # Lower to make hard cuts more frequent +Aspect Correction = true # Custom Shape Aspect Correction + +Preset Path = @PROJECTM_DATADIR_PATH_ABSOLUTE@/presets # preset location +Title Font = Vera.ttf +Menu Font = VeraMono.ttf diff --git a/src/libprojectM/libprojectM.pc.cmake.in b/src/libprojectM/libprojectM.pc.cmake.in new file mode 100644 index 0000000000..81084991da --- /dev/null +++ b/src/libprojectM/libprojectM.pc.cmake.in @@ -0,0 +1,13 @@ +prefix=@PKGCONFIG_PREFIX@ +exec_prefix=${prefix} +libdir=@PKGCONFIG_LIB_DIR@ +includedir=@PKGCONFIG_INCLUDE_DIR@ +pkgdatadir=@PKGCONFIG_DATADIR_PATH@ +sysconfdir=@PKGCONFIG_DATADIR_PATH@ + +Name: libprojectM +Version: @PROJECT_VERSION@ +Description: projectM - OpenGL Milkdrop +Requires: +Libs: -L${libdir} -lprojectM +Cflags: -I${includedir} diff --git a/src/libprojectM/libprojectMConfig.cmake.in b/src/libprojectM/libprojectMConfig.cmake.in new file mode 100644 index 0000000000..f64c451153 --- /dev/null +++ b/src/libprojectM/libprojectMConfig.cmake.in @@ -0,0 +1,14 @@ +set(libprojectM_VERSION @PROJECT_VERSION@) + +@PACKAGE_INIT@ + +set_and_check(libprojectM_BIN_DIR "@PACKAGE_PROJECTM_BIN_DIR@") +set_and_check(libprojectM_INCLUDE_DIR "@PACKAGE_PROJECTM_INCLUDE_DIR@") +set_and_check(libprojectM_DATA_DIR "@PACKAGE_PROJECTM_DATADIR_PATH@") + +include(CMakeFindDependencyMacro) + +find_dependency(OpenGL) +if("@ENABLE_THREADING@") # ENABLE_THREADING + find_dependency(Threads) +endif() diff --git a/src/libprojectM/msvc/dirent.h b/src/libprojectM/msvc/dirent.h new file mode 100644 index 0000000000..c885b398a7 --- /dev/null +++ b/src/libprojectM/msvc/dirent.h @@ -0,0 +1,1160 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp; + DWORD n; + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (!dirp) { + return NULL; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#else + /* WinRT */ + n = wcslen (dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Release search pattern */ + free (dirp->patt); + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (!dirp) { + return NULL; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; + } + + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; + } + + } + + /* Success */ + return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ diff --git a/src/libprojectM/msvc/dlfcn.c b/src/libprojectM/msvc/dlfcn.c new file mode 100644 index 0000000000..b356558b2a --- /dev/null +++ b/src/libprojectM/msvc/dlfcn.c @@ -0,0 +1,489 @@ +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * Copyright (c) 2015 Tiancheng "Timothy" Gu + * + * dlfcn-win32 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * dlfcn-win32 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with dlfcn-win32; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef _DEBUG +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif +#define PSAPI_VERSION 1 +#include +#include +#include +#include + +#ifdef SHARED +#define DLFCN_WIN32_EXPORTS +#endif +#include "dlfcn.h" + +#if ((defined(_WIN32) || defined(WIN32)) && (defined(_MSC_VER)) ) +#define snprintf sprintf_s +#endif + +#ifdef UNICODE +#include +#define CHAR wchar_t +#define UNICODE_L(s) L##s +#else +#define CHAR char +#define UNICODE_L(s) s +#endif + +/* Note: + * MSDN says these functions are not thread-safe. We make no efforts to have + * any kind of thread safety. + */ + +typedef struct global_object { + HMODULE hModule; + struct global_object *previous; + struct global_object *next; +} global_object; + +static global_object first_object; +static global_object first_automatic_object; +static int auto_ref_count = 0; + +/* These functions implement a double linked list for the global objects. */ +static global_object *global_search( global_object *start, HMODULE hModule ) +{ + global_object *pobject; + + if( hModule == NULL ) + return NULL; + + for( pobject = start; pobject; pobject = pobject->next ) + if( pobject->hModule == hModule ) + return pobject; + + return NULL; +} + +static void global_add( global_object *start, HMODULE hModule ) +{ + global_object *pobject; + global_object *nobject; + + if( hModule == NULL ) + return; + + pobject = global_search( start, hModule ); + + /* Do not add object again if it's already on the list */ + if( pobject ) + return; + + if( start == &first_automatic_object ) + { + pobject = global_search( &first_object, hModule ); + if( pobject ) + return; + } + + for( pobject = start; pobject->next; pobject = pobject->next ); + + nobject = (global_object*) malloc( sizeof( global_object ) ); + + /* Should this be enough to fail global_add, and therefore also fail + * dlopen? + */ + if( !nobject ) + return; + + pobject->next = nobject; + nobject->next = NULL; + nobject->previous = pobject; + nobject->hModule = hModule; +} + +static void global_rem( global_object *start, HMODULE hModule ) +{ + global_object *pobject; + + if( hModule == NULL ) + return; + + pobject = global_search( start, hModule ); + + if( !pobject ) + return; + + if( pobject->next ) + pobject->next->previous = pobject->previous; + if( pobject->previous ) + pobject->previous->next = pobject->next; + + free( pobject ); +} + +/* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one + * static buffer. + * MSDN says the buffer cannot be larger than 64K bytes, so we set it to + * the limit. + */ +static CHAR error_buffer[65535]; +static CHAR *current_error; +static char dlerror_buffer[65536]; + +static int copy_string( CHAR *dest, int dest_size, const CHAR *src ) +{ + int i = 0; + + /* gcc should optimize this out */ + if( !src || !dest ) + return 0; + + for( i = 0 ; i < dest_size-1 ; i++ ) + { + if( !src[i] ) + break; + else + dest[i] = src[i]; + } + dest[i] = '\0'; + + return i; +} + +static void save_err_str( const CHAR *str ) +{ + DWORD dwMessageId; + DWORD pos; + + dwMessageId = GetLastError( ); + + if( dwMessageId == 0 ) + return; + + /* Format error message to: + * "": + */ + pos = copy_string( error_buffer, sizeof(error_buffer), UNICODE_L("\"") ); + pos += copy_string( error_buffer+pos, sizeof(error_buffer)-pos, str ); + pos += copy_string( error_buffer+pos, sizeof(error_buffer)-pos, UNICODE_L("\": ") ); + pos += FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwMessageId, + MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), + error_buffer+pos, sizeof(error_buffer)-pos, NULL ); + + if( pos > 1 ) + { + /* POSIX says the string must not have trailing */ + if( error_buffer[pos-2] == '\r' && error_buffer[pos-1] == '\n' ) + error_buffer[pos-2] = '\0'; + } + + current_error = error_buffer; +} + +static void save_err_ptr_str( const void *ptr ) +{ + CHAR ptr_buf[19]; /* 0x up to 64 bits. */ + +#ifdef UNICODE + +# if ((defined(_WIN32) || defined(WIN32)) && (defined(_MSC_VER)) ) + swprintf_s( ptr_buf, 19, UNICODE_L("0x%p"), ptr ); +# else + swprintf(ptr_buf, 19, UNICODE_L("0x%p"), ptr); +# endif + +#else + snprintf( ptr_buf, 19, "0x%p", ptr ); +#endif + + save_err_str( ptr_buf ); +} + +void *dlopen( const char *file, int mode ) +{ + HMODULE hModule; + UINT uMode; + + current_error = NULL; + + /* Do not let Windows display the critical-error-handler message box */ + uMode = SetErrorMode( SEM_FAILCRITICALERRORS ); + + if( file == 0 ) + { + HMODULE hAddtnlMods[1024]; // Already loaded modules + HANDLE hCurrentProc = GetCurrentProcess( ); + DWORD cbNeeded; + + /* POSIX says that if the value of file is 0, a handle on a global + * symbol object must be provided. That object must be able to access + * all symbols from the original program file, and any objects loaded + * with the RTLD_GLOBAL flag. + * The return value from GetModuleHandle( ) allows us to retrieve + * symbols only from the original program file. For objects loaded with + * the RTLD_GLOBAL flag, we create our own list later on. For objects + * outside of the program file but already loaded (e.g. linked DLLs) + * they are added below. + */ + hModule = GetModuleHandle( NULL ); + + if( !hModule ) + save_err_ptr_str( file ); + + + /* GetModuleHandle( NULL ) only returns the current program file. So + * if we want to get ALL loaded module including those in linked DLLs, + * we have to use EnumProcessModules( ). + */ + if( EnumProcessModules( hCurrentProc, hAddtnlMods, + sizeof( hAddtnlMods ), &cbNeeded ) != 0 ) + { + DWORD i; + for( i = 0; i < cbNeeded / sizeof( HMODULE ); i++ ) + { + global_add( &first_automatic_object, hAddtnlMods[i] ); + } + } + auto_ref_count++; + } + else + { + CHAR lpFileName[MAX_PATH]; + int i; + + /* MSDN says backslashes *must* be used instead of forward slashes. */ + for( i = 0 ; i < sizeof(lpFileName) - 1 ; i ++ ) + { + if( !file[i] ) + break; + else if( file[i] == '/' ) + lpFileName[i] = '\\'; + else + lpFileName[i] = file[i]; + } + lpFileName[i] = '\0'; + + /* POSIX says the search path is implementation-defined. + * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely + * to UNIX's search paths (start with system folders instead of current + * folder). + */ + hModule = LoadLibraryEx(lpFileName, NULL, + LOAD_WITH_ALTERED_SEARCH_PATH ); + + /* If the object was loaded with RTLD_GLOBAL, add it to list of global + * objects, so that its symbols may be retrieved even if the handle for + * the original program file is passed. POSIX says that if the same + * file is specified in multiple invocations, and any of them are + * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the + * symbols will remain global. + */ + if( !hModule ) + save_err_str( lpFileName ); + else if( (mode & RTLD_GLOBAL) ) + global_add( &first_object, hModule ); + } + + /* Return to previous state of the error-mode bit flags. */ + SetErrorMode( uMode ); + + return (void *) hModule; +} + +static void free_auto( ) +{ + global_object *pobject = first_automatic_object.next; + if( pobject ) + { + global_object *next; + for ( ; pobject; pobject = next ) + { + next = pobject->next; + free( pobject ); + } + first_automatic_object.next = NULL; + } +} + +int dlclose( void *handle ) +{ + HMODULE hModule = (HMODULE) handle; + BOOL ret; + + current_error = NULL; + + ret = FreeLibrary( hModule ); + + /* If the object was loaded with RTLD_GLOBAL, remove it from list of global + * objects. + */ + if( ret ) + { + HMODULE cur = GetModuleHandle( NULL ); + global_rem( &first_object, hModule ); + if( hModule == cur ) + { + auto_ref_count--; + if( auto_ref_count < 0 ) + auto_ref_count = 0; + if( !auto_ref_count ) + free_auto( ); + } + } + else + save_err_ptr_str( handle ); + + /* dlclose's return value in inverted in relation to FreeLibrary's. */ + ret = !ret; + + return (int) ret; +} + +void *dlsym( void *handle, const char *name ) +{ + FARPROC symbol; + HMODULE hModule; + +#ifdef UNICODE + wchar_t namew[MAX_PATH]; + wmemset(namew, 0, MAX_PATH); +#endif + + current_error = NULL; + + symbol = GetProcAddress( (HMODULE) handle, name ); + + if( symbol != NULL ) + goto end; + + /* If the handle for the original program file is passed, also search + * in all globally loaded objects. + */ + + hModule = GetModuleHandle( NULL ); + + if( hModule == handle ) + { + global_object *pobject; + + for( pobject = &first_object; pobject; pobject = pobject->next ) + { + if( pobject->hModule ) + { + symbol = GetProcAddress( pobject->hModule, name ); + if( symbol != NULL ) + goto end; + } + } + + for( pobject = &first_automatic_object; pobject; pobject = pobject->next ) + { + if( pobject->hModule ) + { + symbol = GetProcAddress( pobject->hModule, name ); + if( symbol != NULL ) + goto end; + } + } + } + +end: + if( symbol == NULL ) + { +#ifdef UNICODE + size_t converted_chars; + + size_t str_len = strlen(name) + 1; + +#if ((defined(_WIN32) || defined(WIN32)) && (defined(_MSC_VER)) ) + errno_t err = mbstowcs_s(&converted_chars, namew, str_len, name, str_len); + if (err != 0) + return NULL; +#else + mbstowcs(namew, name, str_len); +#endif + + save_err_str( namew ); +#else + save_err_str( name ); +#endif + } + + // warning C4054: 'type cast' : from function pointer 'FARPROC' to data pointer 'void *' +#ifdef _MSC_VER +#pragma warning( suppress: 4054 ) +#endif + return (void*) symbol; +} + +char *dlerror( void ) +{ + char *error_pointer = dlerror_buffer; + + /* If this is the second consecutive call to dlerror, return NULL */ + if (current_error == NULL) + { + return NULL; + } + +#ifdef UNICODE + errno_t err = 0; + size_t converted_chars = 0; + size_t str_len = wcslen(current_error) + 1; + memset(error_pointer, 0, 65535); + +# if ((defined(_WIN32) || defined(WIN32)) && (defined(_MSC_VER)) ) + err = wcstombs_s(&converted_chars, + error_pointer, str_len * sizeof(char), + current_error, str_len * sizeof(wchar_t)); + + if (err != 0) + return NULL; +# else + wcstombs(error_pointer, current_error, str_len); +# endif + +#else + memcpy(error_pointer, current_error, strlen(current_error) + 1); +#endif + + /* POSIX says that invoking dlerror( ) a second time, immediately following + * a prior invocation, shall result in NULL being returned. + */ + current_error = NULL; + + return error_pointer; +} + +#ifdef SHARED +BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) +{ + (void) hinstDLL; + /* + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx + * + * When handling DLL_PROCESS_DETACH, a DLL should free resources such as heap + * memory only if the DLL is being unloaded dynamically (the lpReserved + * parameter is NULL). + */ + if( fdwReason == DLL_PROCESS_DETACH && !lpvReserved ) + { + auto_ref_count = 0; + free_auto( ); + } + return TRUE; +} +#endif diff --git a/src/libprojectM/msvc/dlfcn.h b/src/libprojectM/msvc/dlfcn.h new file mode 100644 index 0000000000..711e431c8c --- /dev/null +++ b/src/libprojectM/msvc/dlfcn.h @@ -0,0 +1,59 @@ +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * + * dlfcn-win32 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * dlfcn-win32 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with dlfcn-win32; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DLFCN_H +#define DLFCN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(DLFCN_WIN32_EXPORTS) +# define DLFCN_EXPORT __declspec(dllexport) +#else +# define DLFCN_EXPORT +#endif + +/* POSIX says these are implementation-defined. + * To simplify use with Windows API, we treat them the same way. + */ + +#define RTLD_LAZY 0 +#define RTLD_NOW 0 + +#define RTLD_GLOBAL (1 << 1) +#define RTLD_LOCAL (1 << 2) + +/* These two were added in The Open Group Base Specifications Issue 6. + * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant. + */ + +#define RTLD_DEFAULT 0 +#define RTLD_NEXT 0 + +DLFCN_EXPORT void *dlopen ( const char *file, int mode ); +DLFCN_EXPORT int dlclose(void *handle); +DLFCN_EXPORT void *dlsym(void *handle, const char *name); +DLFCN_EXPORT char *dlerror(void); + +#ifdef __cplusplus +} +#endif + +#endif /* DLFCN_H */ diff --git a/src/libprojectM/omptl/CMakeLists.txt b/src/libprojectM/omptl/CMakeLists.txt new file mode 100644 index 0000000000..3fea46350d --- /dev/null +++ b/src/libprojectM/omptl/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(omptl INTERFACE) + +target_sources(omptl + omptl + omptl_algorithm + omptl_algorithm_par.h + omptl_algorithm_ser.h + omptl_numeric + omptl_numeric_extentions.h + omptl_numeric_extentions_par.h + omptl_numeric_extentions_ser.h + omptl_numeric_par.h + omptl_numeric_ser.h + omptl_tools.h + ) diff --git a/src/libprojectM/omptl/omptl_numeric_extentions_par.h b/src/libprojectM/omptl/omptl_numeric_extentions_par.h index 5d152470dd..986620681e 100644 --- a/src/libprojectM/omptl/omptl_numeric_extentions_par.h +++ b/src/libprojectM/omptl/omptl_numeric_extentions_par.h @@ -123,7 +123,7 @@ struct _TransformAccumulate< ::std::input_iterator_tag > template T transform_accumulate(Iterator first, Iterator last, const T init, UnaryFunction unary_op, BinaryFunction binary_op, - const unsigned P = omp_get_max_threads()) + const unsigned P) { return ::omptl::_TransformAccumulate ::iterator_category> @@ -134,7 +134,7 @@ T transform_accumulate(Iterator first, Iterator last, const T init, template T transform_accumulate(Iterator first, Iterator last, const T init, UnaryFunction unary_op, - const unsigned P=omp_get_max_threads()) + const unsigned P) { typedef typename UnaryFunction::result_type RT; return ::omptl::transform_accumulate(first, last, init, unary_op, diff --git a/src/libprojectM/projectM-opengl.h b/src/libprojectM/projectM-opengl.h index a9d4cd48b7..2e2c420d5a 100644 --- a/src/libprojectM/projectM-opengl.h +++ b/src/libprojectM/projectM-opengl.h @@ -1,50 +1,50 @@ -/** - Include appropriate OpenGL headers for this platform. -**/ - -#ifndef __PROJECTM_OPENGL_H__ -#define __PROJECTM_OPENGL_H__ - -// stuff that needs to be ported to newer GL calls -#define GL_TRANSITION - -// Enable openGL extra checks, better not be enabled in release build -#define OGL_DEBUG 0 - -// Unlock FPS for rendering benchmarks, it disables Vblank/Vsync and prints drawn frame count within a 5s test run -#define UNLOCK_FPS 0 - -// If a shader compilation failure occurs, it dumps shader source into /tmp instead of stderr -#define DUMP_SHADERS_ON_ERROR 0 - -#ifdef __APPLE__ -# include -# include -#elif defined(EYETUNE_WINRT) -#define GL_GLEXT_PROTOTYPES -#define GLM_FORCE_CXX03 -#include -#include -#include -#include -#include -#include -#elif defined(_WIN32) -#define GLM_FORCE_CXX03 -# include -#include "glew.h" -#include "wglew.h" -#else /* linux/unix/other */ -# ifdef USE_GLES -# include -# else -# if !defined(GL_GLEXT_PROTOTYPES) -# define GL_GLEXT_PROTOTYPES -# endif -# include -# include -# endif -#endif - - -#endif // __PROJECTM_OPENGL_H__ +/** + Include appropriate OpenGL headers for this platform. +**/ + +#ifndef __PROJECTM_OPENGL_H__ +#define __PROJECTM_OPENGL_H__ + +// stuff that needs to be ported to newer GL calls +#define GL_TRANSITION + +// Enable openGL extra checks, better not be enabled in release build +#define OGL_DEBUG 0 + +// Unlock FPS for rendering benchmarks, it disables Vblank/Vsync and prints drawn frame count within a 5s test run +#define UNLOCK_FPS 0 + +// If a shader compilation failure occurs, it dumps shader source into /tmp instead of stderr +#define DUMP_SHADERS_ON_ERROR 0 + +#ifdef __APPLE__ +# include +# include +#elif defined(EYETUNE_WINRT) +#define GL_GLEXT_PROTOTYPES +#define GLM_FORCE_CXX03 +#include +#include +#include +#include +#include +#include +#elif defined(_WIN32) +#define GLM_FORCE_CXX03 +# include +#include "GL/glew.h" +#include "GL/wglew.h" +#else /* linux/unix/other */ +# ifdef USE_GLES +# include +# else +# if !defined(GL_GLEXT_PROTOTYPES) +# define GL_GLEXT_PROTOTYPES +# endif +# include +# include +# endif +#endif + + +#endif // __PROJECTM_OPENGL_H__ diff --git a/src/libprojectM/projectM.cpp b/src/libprojectM/projectM.cpp index f7b86452ec..8bd68f4cc8 100644 --- a/src/libprojectM/projectM.cpp +++ b/src/libprojectM/projectM.cpp @@ -55,7 +55,7 @@ #include "TimeKeeper.hpp" #include "RenderItemMergeFunction.hpp" -#ifdef USE_THREADS +#if USE_THREADS #include "pthread.h" #include "BackgroundWorker.h" @@ -74,7 +74,7 @@ constexpr int kMaxSwitchRetries = 10; projectM::~projectM() { -#ifdef USE_THREADS +#if USE_THREADS void *status; worker_sync.finish_up(); pthread_join(thread, &status); @@ -283,7 +283,7 @@ void projectM::readSettings (const Settings & settings ) _settings.aspectCorrection = settings.aspectCorrection; } -#ifdef USE_THREADS +#if USE_THREADS static void *thread_callback(void *prjm) { projectM *p = (projectM *)prjm; @@ -396,13 +396,13 @@ Pipeline * projectM::renderFrameOnlyPass1(Pipeline *pPipeline) /*pPipeline is a // printf("start thread\n"); assert ( m_activePreset2.get() ); -#ifdef USE_THREADS +#if USE_THREADS worker_sync.wake_up_bg(); #endif m_activePreset->Render(*beatDetect, pipelineContext()); -#ifdef USE_THREADS +#if USE_THREADS worker_sync.wait_for_bg_to_finish(); #else evaluateSecondPreset(); @@ -571,7 +571,7 @@ void projectM::projectM_init ( int gx, int gy, int fps, int texsize, int width, initPresetTools(gx, gy); -#ifdef USE_THREADS +#if USE_THREADS #ifdef SYNC_PRESET_SWITCHES pthread_mutex_init(&preset_mutex, NULL); diff --git a/src/projectM-jack/CMakeLists.txt b/src/projectM-jack/CMakeLists.txt new file mode 100644 index 0000000000..a1b521aa12 --- /dev/null +++ b/src/projectM-jack/CMakeLists.txt @@ -0,0 +1,11 @@ +if(NOT ENABLE_JACK) + return() +endif() + +if(ENABLE_QT) + include(projectM-jack-qt.cmake) +endif() + +if(ENABLE_SDL) + include(projectM-jack-sdl.cmake) +endif() diff --git a/src/projectM-jack/projectM-jack-qt.cmake b/src/projectM-jack/projectM-jack-qt.cmake new file mode 100644 index 0000000000..683666b5c2 --- /dev/null +++ b/src/projectM-jack/projectM-jack-qt.cmake @@ -0,0 +1,30 @@ +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) + +add_executable(projectM-jack-qt + ConfigFile.h + ConfigFile.cpp + qprojectM-jack.cpp + ) + +target_compile_definitions(projectM-jack-qt + PRIVATE + PROJECTM_PREFIX="${CMAKE_INSTALL_PREFIX}" + RESOURCE_PREFIX="share/projectM" + ) + +target_link_libraries(projectM-jack-qt + PRIVATE + projectM-qt + JACK::JACK + ${CMAKE_DL_LIBS} + ) + +set_target_properties(projectM-jack-qt PROPERTIES + OUTPUT_NAME projectM-jack + ) + +install(TARGETS projectM-jack-qt + RUNTIME DESTINATION "${PROJECTM_BIN_DIR}" + COMPONENT Applications + ) diff --git a/src/projectM-jack/projectM-jack-sdl.cmake b/src/projectM-jack/projectM-jack-sdl.cmake new file mode 100644 index 0000000000..aee3b67ee2 --- /dev/null +++ b/src/projectM-jack/projectM-jack-sdl.cmake @@ -0,0 +1,25 @@ +add_executable(projectM-jack-sdl + ConfigFile.h + ConfigFile.cpp + projectM-jack.cpp + video_init.cpp + video_init.h + ) + +target_compile_definitions(projectM-jack-sdl + PRIVATE + PROJECTM_PREFIX="${CMAKE_INSTALL_PREFIX}" + ) + +target_link_libraries(projectM-jack-sdl + PRIVATE + projectM_static + JACK::JACK + SDL2::SDL2 + ${CMAKE_DL_LIBS} + ) + +install(TARGETS projectM-jack-sdl + RUNTIME DESTINATION "${PROJECTM_BIN_DIR}" + COMPONENT Applications + ) diff --git a/src/projectM-jack/projectM-jack.cpp b/src/projectM-jack/projectM-jack.cpp index 36645ea2f9..dd60977c4c 100644 --- a/src/projectM-jack/projectM-jack.cpp +++ b/src/projectM-jack/projectM-jack.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include "sdltoprojectM.h" @@ -41,199 +41,208 @@ std::string read_config(); jack_port_t *input_port; jack_client_t *client; -jack_default_audio_sample_t out[1024*1000]; +jack_default_audio_sample_t out[1024 * 1000]; + +SDL_Window *screen; +SDL_Renderer *renderer; #ifdef DEBUG FILE *debugFile = NULL; #endif -volatile enum client_state_t { - Init, - Run, - Exit -} client_state = Init; - -SDL_Surface *screen; +volatile enum client_state_t { Init, Run, Exit } client_state = Init; projectM *globalPM = NULL; int dumpFrame = 0; int frameNumber = 0; - -int texsize=512; -int gx=32,gy=24; -int wvw=512,wvh=512; -int fvw=1024,fvh=768; -int fps=30, fullscreen=0; +int texsize = 512; +int gx = 32, gy = 24; +int wvw = 512, wvh = 512; +int fvw = 1024, fvh = 768; +int fps = 30, fullscreen = 0; std::string read_config() { - int n; - - char num[512]; - FILE *in; - FILE *out; - - char* home; - char projectM_home[1024]; - char projectM_config[1024]; - - strcpy(projectM_config, PROJECTM_PREFIX); - strcpy(projectM_config+strlen(PROJECTM_PREFIX), CONFIG_FILE); - projectM_config[strlen(PROJECTM_PREFIX)+strlen(CONFIG_FILE)]='\0'; - printf("dir:%s \n",projectM_config); - home=getenv("HOME"); - strcpy(projectM_home, home); - strcpy(projectM_home+strlen(home), "/.projectM/config.inp"); - projectM_home[strlen(home)+strlen("/.projectM/config.inp")]='\0'; - - - if ((in = fopen(projectM_home, "r")) != 0) - { - printf("reading ~/.projectM/config.inp \n"); - fclose(in); - return std::string(projectM_home); - } - else - { - printf("trying to create ~/.projectM/config.inp \n"); - - strcpy(projectM_home, home); - strcpy(projectM_home+strlen(home), "/.projectM"); - projectM_home[strlen(home)+strlen("/.projectM")]='\0'; - mkdir(projectM_home,0755); - - strcpy(projectM_home, home); - strcpy(projectM_home+strlen(home), "/.projectM/config.inp"); - projectM_home[strlen(home)+strlen("/.projectM/config.inp")]='\0'; - - if((out = fopen(projectM_home,"w"))!=0) - { - - if ((in = fopen(projectM_config, "r")) != 0) - { - - while(fgets(num,80,in)!=NULL) - { - fputs(num,out); - } - fclose(in); - fclose(out); - - - if ((in = fopen(projectM_home, "r")) != 0) - { - printf("created ~/.projectM/config.inp successfully\n"); - fclose(in); - return std::string(projectM_home); - } - else{printf("This shouldn't happen, using implementation defaults\n");abort();} - } - else{printf("Cannot find projectM default config, using implementation defaults\n");abort();} - } - else - { - printf("Cannot create ~/.projectM/config.inp, using default config file\n"); - if ((in = fopen(projectM_config, "r")) != 0) - { printf("Successfully opened default config file\n"); - fclose(in); - return std::string(projectM_config);} - else{ printf("Using implementation defaults, your system is really messed up, I'm surprised we even got this far\n"); abort();} - - } - - } - - - abort(); + int n; + + char num[512]; + FILE *in; + FILE *out; + + char *home; + char projectM_home[1024]; + char projectM_config[1024]; + + strcpy(projectM_config, PROJECTM_PREFIX); + strcpy(projectM_config + strlen(PROJECTM_PREFIX), CONFIG_FILE); + projectM_config[strlen(PROJECTM_PREFIX) + strlen(CONFIG_FILE)] = '\0'; + printf("dir:%s \n", projectM_config); + home = getenv("HOME"); + strcpy(projectM_home, home); + strcpy(projectM_home + strlen(home), "/.projectM/config.inp"); + projectM_home[strlen(home) + strlen("/.projectM/config.inp")] = '\0'; + + if ((in = fopen(projectM_home, "r")) != 0) + { + printf("reading ~/.projectM/config.inp \n"); + fclose(in); + return std::string(projectM_home); + } + else + { + printf("trying to create ~/.projectM/config.inp \n"); + + strcpy(projectM_home, home); + strcpy(projectM_home + strlen(home), "/.projectM"); + projectM_home[strlen(home) + strlen("/.projectM")] = '\0'; + mkdir(projectM_home, 0755); + + strcpy(projectM_home, home); + strcpy(projectM_home + strlen(home), "/.projectM/config.inp"); + projectM_home[strlen(home) + strlen("/.projectM/config.inp")] = '\0'; + + if ((out = fopen(projectM_home, "w")) != 0) + { + + if ((in = fopen(projectM_config, "r")) != 0) + { + + while (fgets(num, 80, in) != NULL) + { + fputs(num, out); + } + fclose(in); + fclose(out); + + if ((in = fopen(projectM_home, "r")) != 0) + { + printf("created ~/.projectM/config.inp successfully\n"); + fclose(in); + return std::string(projectM_home); + } + else + { + printf("This shouldn't happen, using implementation defaults\n"); + abort(); + } + } + else + { + printf("Cannot find projectM default config, using implementation defaults\n"); + abort(); + } + } + else + { + printf("Cannot create ~/.projectM/config.inp, using default config file\n"); + if ((in = fopen(projectM_config, "r")) != 0) + { + printf("Successfully opened default config file\n"); + fclose(in); + return std::string(projectM_config); + } + else + { + printf( + "Using implementation defaults, your system is really messed up, I'm surprised we even " + "got this far\n"); + abort(); + } + } + } } -void renderLoop() { - - int i; - int x, y; - int index; - short pcm_data[2][512]; - - while ( 1 ) { - projectMEvent evt; - projectMKeycode key; - projectMModifier mod; - - /** Process SDL events */ - SDL_Event event; - while ( SDL_PollEvent( &event ) ) { - /** Translate into projectM codes and process */ - evt = sdl2pmEvent( event ); - key = sdl2pmKeycode( event.key.keysym.sym ); - mod = sdl2pmModifier( event.key.keysym.mod ); - - if ( evt == PROJECTM_KEYDOWN ) { - - - if(key == PROJECTM_K_f) - { - if (++fullscreen%2) - { - resize_display(fvw, fvh, fullscreen%2); - globalPM->projectM_resetGL( fvw, fvh ); - } - else - { - resize_display(wvw, wvh, fullscreen%2); - globalPM->projectM_resetGL( wvw, wvh ); - } - } - else if(key == PROJECTM_K_q || key == PROJECTM_K_ESCAPE) {delete(globalPM); exit (1);} - else {globalPM->key_handler(evt,key,mod);} - - } - else if ( evt == PROJECTM_VIDEORESIZE ) - { - wvw=event.resize.w; - wvh=event.resize.h; - resize_display(wvw, wvh, fullscreen%2); - globalPM->projectM_resetGL( wvw, wvh ); - - } - - } - - +void renderLoop() +{ - /** Render the new frame */ - globalPM->renderFrame( ); + int i; + int x, y; + int index; + short pcm_data[2][512]; + + while (1) + { + projectMEvent evt; + projectMKeycode key; + projectMModifier mod; + + /** Process SDL events */ + SDL_Event event; + while (SDL_PollEvent(&event)) + { + /** Translate into projectM codes and process */ + evt = sdl2pmEvent(event); + key = sdl2pmKeycode(event.key.keysym.sym); + mod = sdl2pmModifier(event.key.keysym.mod); + + if (evt == PROJECTM_KEYDOWN) + { + + if (key == PROJECTM_K_f) + { + if (++fullscreen % 2) + { + resize_display(fvw, fvh, fullscreen % 2); + globalPM->projectM_resetGL(fvw, fvh); + } + else + { + resize_display(wvw, wvh, fullscreen % 2); + globalPM->projectM_resetGL(wvw, wvh); + } + } + else if (key == PROJECTM_K_q || key == PROJECTM_K_ESCAPE) + { + delete (globalPM); + exit(1); + } + else + { + globalPM->key_handler(evt, key, mod); + } + } + else if (evt == PROJECTM_VIDEORESIZE) + { + wvw = event.window.data1; + wvh = event.window.data2; + resize_display(wvw, wvh, fullscreen % 2); + globalPM->projectM_resetGL(wvw, wvh); + } + } + /** Render the new frame */ + globalPM->renderFrame(); - SDL_GL_SwapBuffers(); - } + SDL_GL_SwapWindow(screen); + } - printf("Worker thread: Exiting\n"); - } + printf("Worker thread: Exiting\n"); +} -int -process (jack_nframes_t nframes, void *arg) +int process(jack_nframes_t nframes, void *arg) { jack_default_audio_sample_t *in; - in = (jack_default_audio_sample_t*)jack_port_get_buffer (input_port, nframes); + in = (jack_default_audio_sample_t *)jack_port_get_buffer(input_port, nframes); - //memcpy (out, in,sizeof (jack_default_audio_sample_t) * nframes); + // memcpy (out, in,sizeof (jack_default_audio_sample_t) * nframes); - globalPM->pcm()->addPCMfloat(in,nframes); - //printf("%x %f\n",nframes,in[128]); + globalPM->pcm()->addPCMfloat(in, nframes); + // printf("%x %f\n",nframes,in[128]); return 0; } -void jack_shutdown (void *arg) +void jack_shutdown(void *arg) { - exit (1); + exit(1); } -int main( int argc, char **argv ) { +int main(int argc, char **argv) +{ const char **ports; const char *client_name; const char *server_name = NULL; @@ -242,112 +251,120 @@ int main( int argc, char **argv ) { int i; char projectM_data[1024]; - std::string config_file; config_file = read_config(); ConfigFile config(config_file); - wvw = config.read( "Window Width", 512 ); - wvh = config.read( "Window Height", 512 ); + wvw = config.read("Window Width", 512); + wvh = config.read("Window Height", 512); int fullscreen = 0; - if (config.read("Fullscreen", true)) fullscreen = 1; - else fullscreen = 0; - + if (config.read("Fullscreen", true)) + fullscreen = 1; + else + fullscreen = 0; #ifdef DEBUG int value; int rgb_size[3]; #endif - const SDL_VideoInfo* info = NULL; int bpp = 0; /* Flags we will pass into SDL_SetVideoMode. */ int flags = 0; - //JACK INIT + // JACK INIT //---------------------------------------------- - if (argc >= 2) { /* client name specified? */ + if (argc >= 2) + { /* client name specified? */ client_name = argv[1]; - if (argc >= 3) { /* server name specified? */ + if (argc >= 3) + { /* server name specified? */ server_name = argv[2]; // options |= JackServerName; } - } else { /* use basename of argv[0] */ + } + else + { /* use basename of argv[0] */ client_name = strrchr(argv[0], '/'); - if (client_name == 0) { + if (client_name == 0) + { client_name = argv[0]; - } else { + } + else + { client_name++; } } /* open a client connection to the JACK server */ - client = jack_client_open (client_name, options, &status, server_name); - if (client == NULL) { - fprintf (stderr, "jack_client_open() failed, " - "status = 0x%2.0x\n", status); - if (status & JackServerFailed) { - fprintf (stderr, "Unable to connect to JACK server\n"); + client = jack_client_open(client_name, options, &status, server_name); + if (client == NULL) + { + fprintf(stderr, + "jack_client_open() failed, " + "status = 0x%2.0x\n", + status); + if (status & JackServerFailed) + { + fprintf(stderr, "Unable to connect to JACK server\n"); } - exit (1); + exit(1); } - if (status & JackServerStarted) { - fprintf (stderr, "JACK server started\n"); + if (status & JackServerStarted) + { + fprintf(stderr, "JACK server started\n"); } - if (status & JackNameNotUnique) { + if (status & JackNameNotUnique) + { client_name = jack_get_client_name(client); - fprintf (stderr, "unique name `%s' assigned\n", client_name); + fprintf(stderr, "unique name `%s' assigned\n", client_name); } /* tell the JACK server to call `process()' whenever - there is work to be done. + there is work to be done. */ - jack_set_process_callback (client, process, 0); + jack_set_process_callback(client, process, 0); /* tell the JACK server to call `jack_shutdown()' if - it ever shuts down, either entirely, or if it - just decides to stop calling us. + it ever shuts down, either entirely, or if it + just decides to stop calling us. */ - jack_on_shutdown (client, jack_shutdown, 0); + jack_on_shutdown(client, jack_shutdown, 0); /* display the current sample rate. */ - printf ("engine sample rate: %d\n", - jack_get_sample_rate (client)); + printf("engine sample rate: %d\n", jack_get_sample_rate(client)); /* create two ports */ - input_port = jack_port_register (client, "input", - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsInput, 0); + input_port = jack_port_register(client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); - if (input_port == NULL) { + if (input_port == NULL) + { fprintf(stderr, "no more JACK ports available\n"); - exit (1); + exit(1); } /* Tell the JACK server that we are ready to roll. Our * process() callback will start running now. */ - - // END JACK INIT ---------------------------------- - init_display(wvw,wvh,&fvw,&fvh,fullscreen); + init_display(wvw, wvh, &fvw, &fvh, fullscreen); /** Setup some window stuff */ - SDL_WM_SetCaption( PROJECTM_TITLE, NULL ); globalPM = new projectM(config_file); /** Initialise projectM */ - //JACK BEGIN----------------------------- + // JACK BEGIN----------------------------- - if (jack_activate (client)) { - fprintf (stderr, "cannot activate client"); - exit (1); + if (jack_activate(client)) + { + fprintf(stderr, "cannot activate client"); + exit(1); } /* Connect the ports. You can't do this before the client is @@ -358,30 +375,32 @@ int main( int argc, char **argv ) { * it. */ - ports = jack_get_ports (client, NULL, NULL, JackPortIsOutput); - if (ports == NULL) { + ports = jack_get_ports(client, NULL, NULL, JackPortIsOutput); + if (ports == NULL) + { fprintf(stderr, "no physical capture ports\n"); - exit (1); + exit(1); } + i = 0; + while (ports[i] != NULL) + { + printf("Connecting to Jack port %s\n", ports[i]); + if (jack_connect(client, ports[i], jack_port_name(input_port))) + { + fprintf(stderr, "cannot connect input ports\n"); + } + i++; + } - i=0; - while (ports[i]!=NULL) - { - printf("Connecting to Jack port %s\n",ports[i]); - if (jack_connect (client, ports[i], jack_port_name (input_port))) { - fprintf (stderr, "cannot connect input ports\n"); - } - i++; - } - - free (ports); + free(ports); //----------------------------------END - /** Initialise the thread */ renderLoop(); - return 1; + close_display(); + + return 0; } diff --git a/src/projectM-jack/sdltoprojectM.h b/src/projectM-jack/sdltoprojectM.h index e921569374..63c0184ba2 100644 --- a/src/projectM-jack/sdltoprojectM.h +++ b/src/projectM-jack/sdltoprojectM.h @@ -47,13 +47,13 @@ #ifdef WIN32 #include #else -#include +#include #endif projectMEvent sdl2pmEvent( SDL_Event event ) { \ switch ( event.type ) { \ - case SDL_VIDEORESIZE: + case SDL_WINDOWEVENT_RESIZED: return PROJECTM_VIDEORESIZE; \ case SDL_KEYUP: \ return PROJECTM_KEYUP; \ @@ -64,7 +64,7 @@ projectMEvent sdl2pmEvent( SDL_Event event ) { \ } \ } \ -projectMKeycode sdl2pmKeycode( SDLKey keysym ) { \ +projectMKeycode sdl2pmKeycode( SDL_Keycode keysym ) { \ switch ( keysym ) { \ case SDLK_F1: \ return PROJECTM_K_F1; \ @@ -164,7 +164,7 @@ projectMKeycode sdl2pmKeycode( SDLKey keysym ) { \ } \ } \ -projectMModifier sdl2pmModifier( SDLMod mod ) { \ +projectMModifier sdl2pmModifier( Uint16 mod ) { \ return PROJECTM_KMOD_LSHIFT; \ } \ diff --git a/src/projectM-jack/video_init.cpp b/src/projectM-jack/video_init.cpp index 6e9404982f..224e5bdc26 100644 --- a/src/projectM-jack/video_init.cpp +++ b/src/projectM-jack/video_init.cpp @@ -19,158 +19,186 @@ * */ -//video_init.c - SDL/Opengl Windowing Creation/Resizing Functions +// video_init.c - SDL/Opengl Windowing Creation/Resizing Functions // -//by Peter Sperl +// by Peter Sperl // -//Opens an SDL Window and creates an OpenGL session -//also able to handle resizing and fullscreening of windows -//just call init_display again with differant variables +// Opens an SDL Window and creates an OpenGL session +// also able to handle resizing and fullscreening of windows +// just call init_display again with differant variables -#include +#include + +#include #include #include -extern SDL_Surface *screen; +extern SDL_Window *screen; +extern SDL_Renderer *renderer; + extern int texsize; -void setup_opengl( int w, int h ); +void setup_opengl(int w, int h); -void close_display() { - SDL_Quit(); +void close_display() +{ + SDL_Quit(); } -void resize_display(int w, int h, int f) { - int flags; - if (f) flags = SDL_OPENGL|SDL_HWSURFACE|SDL_FULLSCREEN; - else flags = SDL_OPENGL|SDL_HWSURFACE|SDL_RESIZABLE; -// SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); - screen = SDL_SetVideoMode( w, h, 0, flags ) ; - if(screen == 0 ) { - fprintf( stderr, "Video mode set failed: %s\n", SDL_GetError( ) ); - return; - } - setup_opengl(w,h); - SDL_ShowCursor(f ? SDL_DISABLE : SDL_ENABLE); +void resize_display(int w, int h, int f) +{ + /* Information about the current video settings. */ + SDL_DisplayMode info; + + // SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + if (SDL_GetWindowDisplayMode(screen, &info) != 0) + { + /* This should probably never happen. */ + fprintf(stderr, "Video query failed: %s\n", SDL_GetError()); + // projectM_vtable.disable_plugin (&projectM_vtable); + return; + } + + info.w = w; + info.h = h; + + if (SDL_SetWindowDisplayMode(screen, &info)) + { + fprintf(stderr, "Video mode set failed: %s\n", SDL_GetError()); + return; + } + + setup_opengl(w, h); + SDL_ShowCursor(f ? SDL_DISABLE : SDL_ENABLE); } -//init_display +// init_display // -//Sets screen to new width and height (w,h) -//Also switches between fullscreen and windowed -//with the boolean f (fullscreen) -void init_display(int w, int h, int *fvw, int *fvh, int f) +// Sets screen to new width and height (w,h) +// Also switches between fullscreen and windowed +// with the boolean f (fullscreen) +void init_display(int w, int h, int *fvw, int *fvh, int fullscreen) { - - /* Information about the current video settings. */ - const SDL_VideoInfo* info = NULL; - int bpp = 0; - /* Flags we will pass into SDL_SetVideoMode. */ - int flags = 0; - /* First, initialize SDL's video subsystem. */ - if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER ) < 0 ) { - /* Failed, exit. */ - fprintf( stderr, "Video initialization failed: %s\n", - SDL_GetError( ) ); - //projectM_vtable.disable_plugin (&projectM_vtable); - return; - - } - /* Let's get some video information. */ - info = SDL_GetVideoInfo( ); - if( !info ) { - /* This should probably never happen. */ - fprintf( stderr, "Video query failed: %s\n", - SDL_GetError( ) ); - // projectM_vtable.disable_plugin (&projectM_vtable); - return; - } - - printf("Screen Resolution: %d x %d\n", info->current_w, info->current_h); - *fvw = info->current_w; - *fvh = info->current_h; - - bpp = info->vfmt->BitsPerPixel; - - //SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); - //SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); - //SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 ); - - // SDL_GL_SetAttribute( SDL_GL_ACCUM_RED_SIZE, 8 ); - // SDL_GL_SetAttribute( SDL_GL_ACCUM_GREEN_SIZE, 8 ); - // SDL_GL_SetAttribute( SDL_GL_ACCUM_BLUE_SIZE, 8 ); - SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 ); - SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 ); - SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); - - if (f==0) - flags = SDL_OPENGL|SDL_HWSURFACE|SDL_RESIZABLE; - else flags = SDL_OPENGL|SDL_HWSURFACE|SDL_FULLSCREEN; - - screen= SDL_SetVideoMode( w, h, bpp, flags ) ; - - if(screen == 0 ) { - /* - * This could happen for a variety of reasons, - * including DISPLAY not being set, the specified - * resolution not being available, etc. - */ - fprintf( stderr, "Video mode set failed: %s\n", - SDL_GetError( ) ); - - // projectM_vtable.disable_plugin (&projectM_vtable); - return; - - } - - - // setup_opengl(w,h); - //gluOrtho2D(0, w, 0, h); -} + int bpp = 0; + /* Flags we will pass into SDL_SetVideoMode. */ + int flags = 0; + /* First, initialize SDL's video subsystem. */ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) + { + /* Failed, exit. */ + fprintf(stderr, "Video initialization failed: %s\n", SDL_GetError()); + // projectM_vtable.disable_plugin (&projectM_vtable); + return; + } + + screen = SDL_CreateWindow(PROJECTM_TITLE, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, + fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + if (screen == nullptr) + { + fprintf(stderr, "Video mode set failed: %s\n", SDL_GetError()); + return; + } + + renderer = SDL_CreateRenderer(screen, -1, 0); + + /* Information about the current video settings. */ + SDL_DisplayMode info; + + /* Let's get some video information. */ + if (SDL_GetWindowDisplayMode(screen, &info) != 0) + { + /* This should probably never happen. */ + fprintf(stderr, "Video mode query failed: %s\n", SDL_GetError()); + // projectM_vtable.disable_plugin (&projectM_vtable); + return; + } + + printf("Screen Resolution: %d x %d\n", info.w, info.h); + *fvw = info.w; + *fvh = info.h; + + // SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); + // SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); + // SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 ); + + // SDL_GL_SetAttribute( SDL_GL_ACCUM_RED_SIZE, 8 ); + // SDL_GL_SetAttribute( SDL_GL_ACCUM_GREEN_SIZE, 8 ); + // SDL_GL_SetAttribute( SDL_GL_ACCUM_BLUE_SIZE, 8 ); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + if (SDL_SetWindowFullscreen(screen, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0) != 0) + { + /* + * This could happen for a variety of reasons, + * including DISPLAY not being set, the specified + * resolution not being available, etc. + */ + fprintf(stderr, "Fullscreen mode set failed: %s\n", SDL_GetError()); + + return; + } + + info.w = w; + info.h = h; + + if (SDL_SetWindowDisplayMode(screen, &info) != 0) + { + /* + * This could happen for a variety of reasons, + * including DISPLAY not being set, the specified + * resolution not being available, etc. + */ + fprintf(stderr, "Video mode set failed: %s\n", SDL_GetError()); + + // projectM_vtable.disable_plugin (&projectM_vtable); + return; + } + + setup_opengl(w,h); +} - void setup_opengl( int w, int h ) +void setup_opengl(int w, int h) { - - /* Our shading model--Gouraud (smooth). */ - glShadeModel( GL_SMOOTH); - /* Culling. */ - // glCullFace( GL_BACK ); - // glFrontFace( GL_CCW ); - // glEnable( GL_CULL_FACE ); - /* Set the clear color. */ - glClearColor( 0, 0, 0, 0 ); - /* Setup our viewport. */ - glViewport( 0, 0, w, h ); - /* - * Change to the projection matrix and set - * our viewing volume. - */ - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - - // gluOrtho2D(0.0, (GLfloat) width, 0.0, (GLfloat) height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - // glFrustum(0.0, height, 0.0,width,10,40); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - -glDrawBuffer(GL_BACK); - glReadBuffer(GL_BACK); - glEnable(GL_BLEND); - - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // glBlendFunc(GL_SRC_ALPHA, GL_ONE); - glEnable(GL_LINE_SMOOTH); - glEnable(GL_POINT_SMOOTH); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - - // glCopyTexImage2D(GL_TEXTURE_2D,0,GL_RGB,0,0,texsize,texsize,0); - //glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,texsize,texsize); - glLineStipple(2, 0xAAAA); - - -} + /* Our shading model--Gouraud (smooth). */ + glShadeModel(GL_SMOOTH); + /* Culling. */ + // glCullFace( GL_BACK ); + // glFrontFace( GL_CCW ); + // glEnable( GL_CULL_FACE ); + /* Set the clear color. */ + glClearColor(0, 0, 0, 0); + /* Setup our viewport. */ + glViewport(0, 0, w, h); + /* + * Change to the projection matrix and set + * our viewing volume. + */ + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + // gluOrtho2D(0.0, (GLfloat) width, 0.0, (GLfloat) height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + // glFrustum(0.0, height, 0.0,width,10,40); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glDrawBuffer(GL_BACK); + glReadBuffer(GL_BACK); + glEnable(GL_BLEND); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_POINT_SMOOTH); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // glCopyTexImage2D(GL_TEXTURE_2D,0,GL_RGB,0,0,texsize,texsize,0); + // glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,texsize,texsize); + glLineStipple(2, 0xAAAA); +} diff --git a/src/projectM-libvisual/CMakeLists.txt b/src/projectM-libvisual/CMakeLists.txt new file mode 100644 index 0000000000..3db75fd76a --- /dev/null +++ b/src/projectM-libvisual/CMakeLists.txt @@ -0,0 +1,30 @@ +if(NOT ENABLE_LIBVISUAL) + return() +endif() + + +add_library(projectM_libvisual SHARED + actor_projectM.cpp + ConfigFile.cpp + ConfigFile.h + lvtoprojectM.h + ) + +target_compile_definitions(projectM_libvisual + PRIVATE + PROJECTM_PREFIX="${CMAKE_INSTALL_PREFIX}" + ) + +target_link_libraries(projectM_libvisual + PUBLIC + projectM_shared + libvisual::libvisual + ${CMAKE_DL_LIBS} + ) + +install(TARGETS projectM_libvisual + LIBRARY DESTINATION "${PROJECTM_LIBVISUAL_DIR}" + RUNTIME DESTINATION "${PROJECTM_LIBVISUAL_DIR}" + ARCHIVE DESTINATION "${PROJECTM_LIBVISUAL_DIR}" + COMPONENT libvisual + ) diff --git a/src/projectM-pulseaudio/CMakeLists.txt b/src/projectM-pulseaudio/CMakeLists.txt new file mode 100644 index 0000000000..fb4d085989 --- /dev/null +++ b/src/projectM-pulseaudio/CMakeLists.txt @@ -0,0 +1,58 @@ +if(NOT ENABLE_QT OR NOT ENABLE_PULSEAUDIO) + return() +endif() + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) + +add_executable(projectM-pulseaudio + ConfigFile.h + ConfigFile.cpp + PulseDeviceChooserDialog.ui + qprojectM-pulseaudio.cpp + QPulseAudioDeviceChooser.cpp + QPulseAudioDeviceChooser.hpp + QPulseAudioDeviceModel.cpp + QPulseAudioDeviceModel.hpp + QPulseAudioThread.cpp + QPulseAudioDeviceModel.hpp + ) + +target_compile_definitions(projectM-pulseaudio + PRIVATE + PROJECTM_PREFIX="${CMAKE_INSTALL_PREFIX}" + ) + +# Application includes a header file from libprojectM with its full path in the source dir. +target_include_directories(projectM-pulseaudio + PRIVATE + "${CMAKE_SOURCE_DIR}" + ) + +target_link_libraries(projectM-pulseaudio + PRIVATE + projectM-qt + Pulseaudio::Pulseaudio + ${CMAKE_DL_LIBS} + ) + +install(TARGETS projectM-pulseaudio + RUNTIME DESTINATION "${PROJECTM_BIN_DIR}" + COMPONENT Applications + ) + +install(FILES "${CMAKE_SOURCE_DIR}/src/projectM-qt/images/icons/prjm16-transparent.svg" + DESTINATION "share/icons/hicolor/scalable/apps" + COMPONENT Applications + RENAME projectM.svg + ) + +install(FILES projectM-pulseaudio.desktop + DESTINATION "share/applications" + COMPONENT Applications + ) + +install(FILES projectM-pulseaudio.1 + DESTINATION "share/man/man1" + COMPONENT Applications + ) diff --git a/src/projectM-qt/CMakeLists.txt b/src/projectM-qt/CMakeLists.txt new file mode 100644 index 0000000000..92d55b5e37 --- /dev/null +++ b/src/projectM-qt/CMakeLists.txt @@ -0,0 +1,47 @@ +if(NOT ENABLE_QT) + return() +endif() + +# Automatically create .moc files and run the UIC and resource compilers +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +add_library(projectM-qt STATIC + application.qrc + configfile.cpp + configfile.hpp + nullable.hpp + qplaylistfiledialog.cpp + qplaylistfiledialog.hpp + qplaylistmodel.cpp + qplaylistmodel.hpp + qplaylisttableview.hpp + qpreseteditordialog.cpp + qpreseteditordialog.hpp + qpreseteditordialog.ui + qpresetfiledialog.hpp + qpresettextedit.cpp + qpresettextedit.hpp + qprojectm.hpp + qprojectm_mainwindow.cpp + qprojectm_mainwindow.hpp + qprojectm_mainwindow.ui + qprojectmconfigdialog.cpp + qprojectmconfigdialog.hpp + qprojectmconfigdialog.ui + qprojectmwidget.hpp + qxmlplaylisthandler.hpp + ) + +target_include_directories(projectM-qt + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + ) + +target_link_libraries(projectM-qt + PUBLIC + projectM_static + Qt5::Gui + Qt5::OpenGL + ) diff --git a/src/projectM-qt/Makefile.am b/src/projectM-qt/Makefile.am index ec01c15ede..24a5bfed3b 100644 --- a/src/projectM-qt/Makefile.am +++ b/src/projectM-qt/Makefile.am @@ -33,7 +33,7 @@ ui_qprojectmconfigdialog.h: qprojectmconfigdialog.ui $(UIC) -o $@ $< application_qrc.cpp: application.qrc - $(RCC) -o $@ $< + $(RCC) -name application -o $@ $< BUILT_SOURCES = \ diff --git a/src/projectM-qt/qprojectm.hpp b/src/projectM-qt/qprojectm.hpp index 5e70ec7cf7..490f533f15 100644 --- a/src/projectM-qt/qprojectm.hpp +++ b/src/projectM-qt/qprojectm.hpp @@ -54,4 +54,5 @@ class QProjectM : public QObject, public projectM { }; + #endif diff --git a/src/projectM-qt/qprojectm_mainwindow.cpp b/src/projectM-qt/qprojectm_mainwindow.cpp index 0df86b24a2..edf0e01568 100644 --- a/src/projectM-qt/qprojectm_mainwindow.cpp +++ b/src/projectM-qt/qprojectm_mainwindow.cpp @@ -38,8 +38,6 @@ #include "nullable.hpp" #include "qprojectmwidget.hpp" -extern int qInitResources(); - class PlaylistWriteFunctor { public: PlaylistWriteFunctor(const QVector::iterator & begin, @@ -79,7 +77,7 @@ QProjectM_MainWindow::QProjectM_MainWindow ( const std::string & config_file, QM _menuAndStatusBarsVisible(true), m_QPresetFileDialog ( new QPresetFileDialog ( this ) ), m_QPlaylistFileDialog( new QPlaylistFileDialog ( this )) { - qInitResources(); + Q_INIT_RESOURCE(application); ui = new Ui::QProjectM_MainWindow(); ui->setupUi ( this ); diff --git a/src/projectM-sdl/CMakeLists.txt b/src/projectM-sdl/CMakeLists.txt new file mode 100644 index 0000000000..c0c1fbc2d7 --- /dev/null +++ b/src/projectM-sdl/CMakeLists.txt @@ -0,0 +1,30 @@ +add_executable(projectMSDL + audioCapture.cpp + audioCapture.hpp + loopback.cpp + loopback.hpp + pmSDL.hpp + pmSDL.cpp + projectM_SDL_main.cpp + setup.cpp + setup.hpp + ) + +target_compile_definitions(projectMSDL + PRIVATE + PROJECTM_PREFIX="${CMAKE_INSTALL_PREFIX}" + ) + +target_link_libraries(projectMSDL + PRIVATE + projectM_static + GLM::GLM + SDL2::SDL2 + SDL2::SDL2main + ${CMAKE_DL_LIBS} + ) + +install(TARGETS projectMSDL + RUNTIME DESTINATION "${PROJECTM_BIN_DIR}" + COMPONENT Applications + ) diff --git a/src/projectM-sdl/setup.cpp b/src/projectM-sdl/setup.cpp index 96b237ab63..eb27f39b19 100644 --- a/src/projectM-sdl/setup.cpp +++ b/src/projectM-sdl/setup.cpp @@ -1,334 +1,339 @@ -#include "setup.hpp" - -#include -#include - -#if OGL_DEBUG -void debugGL(GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - const void* userParam) { - - /*if (type != GL_DEBUG_TYPE_OTHER)*/ - { - std::cerr << " -- \n" << "Type: " << - type << "; Source: " << - source <<"; ID: " << id << "; Severity: " << - severity << "\n" << message << "\n"; - } -} -#endif - -// return path to config file to use -std::string getConfigFilePath(std::string datadir_path) { - char* home = NULL; - std::string projectM_home; - std::string projectM_config = DATADIR_PATH; - - projectM_config = datadir_path; - -#ifdef _MSC_VER - home=getenv("USERPROFILE"); -#else - home=getenv("HOME"); -#endif - - projectM_home = std::string(home); - projectM_home += "/.projectM"; - - // Create the ~/.projectM directory. If it already exists, mkdir will do nothing -#if defined _MSC_VER - _mkdir(projectM_home.c_str()); -#else - mkdir(projectM_home.c_str(), 0755); -#endif - - projectM_home += "/config.inp"; - projectM_config += "/config.inp"; - - std::ifstream f_home(projectM_home); - std::ifstream f_config(projectM_config); - std::cout << "f_home " << f_home.good() << "\n"; - - if (f_config.good() && !f_home.good()) { - std::ifstream f_src; - std::ofstream f_dst; - - f_src.open(projectM_config, std::ios::in | std::ios::binary); - f_dst.open(projectM_home, std::ios::out | std::ios::binary); - f_dst << f_src.rdbuf(); - f_dst.close(); - f_src.close(); - return std::string(projectM_home); - } else if (f_home.good()) { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Read ~/.projectM/config.inp\n"); - return std::string(projectM_home); - } else if (f_config.good()) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot create ~/.projectM/config.inp, using %s\n", projectM_config.c_str()); - return std::string(projectM_config); - } else { - SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Using implementation defaults, your system is really messed up, I'm surprised we even got this far\n"); - return ""; - } -} - - -void seedRand() { -#ifndef WIN32 - srand((int)(time(NULL))); -#endif -} - -void initGL() { -#ifdef USE_GLES - // use GLES 2.0 (this may need adjusting) - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); -#else - // Disabling compatibility profile - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); -#endif -} - -void dumpOpenGLInfo() { - SDL_Log("- GL_VERSION: %s", glGetString(GL_VERSION)); - SDL_Log("- GL_SHADING_LANGUAGE_VERSION: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); - SDL_Log("- GL_VENDOR: %s", glGetString(GL_VENDOR)); -} - -void initStereoscopicView(SDL_Window *win) { -#if STEREOSCOPIC_SB - // enable stereo - if (SDL_GL_SetAttribute(SDL_GL_STEREO, 1) == 0) - { - SDL_Log("SDL_GL_STEREO: true"); - } - - // requires fullscreen mode - SDL_ShowCursor(false); - SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN); -#endif -} - -void enableGLDebugOutput() { -#if OGL_DEBUG && !USE_GLES - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallback(debugGL, NULL); -#endif -} - -void testAllPresets(projectMSDL *app) { - uint buildErrors = 0; - for(unsigned int i = 0; i < app->getPlaylistSize(); i++) { - std::cout << i << "\t" << app->getPresetName(i) << std::endl; - app->selectPreset(i); - if (app->getErrorLoadingCurrentPreset()) { - buildErrors++; - } - } - - if (app->getPlaylistSize()) { - fprintf(stdout, "Preset loading errors: %d/%d [%d%%]\n", buildErrors, app->getPlaylistSize(), (buildErrors*100) / app->getPlaylistSize()); - } - - delete app; -} - -// initialize SDL, openGL, config -projectMSDL *setupSDLApp() { - projectMSDL *app; - seedRand(); - - if (!initLoopback()) - { - SDL_Log("Failed to initialize audio loopback devide."); - exit(1); - } - -#if UNLOCK_FPS - setenv("vblank_mode", "0", 1); -#endif - - SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); - - if (! SDL_VERSION_ATLEAST(2, 0, 5)) { - SDL_Log("SDL version 2.0.5 or greater is required. You have %i.%i.%i", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL); - exit(1); - } - - // default window size to usable bounds (e.g. minus menubar and dock) - SDL_Rect initialWindowBounds; -#if SDL_VERSION_ATLEAST(2, 0, 5) - // new and better - SDL_GetDisplayUsableBounds(0, &initialWindowBounds); -#else - SDL_GetDisplayBounds(0, &initialWindowBounds); -#endif - int width = initialWindowBounds.w; - int height = initialWindowBounds.h; - - initGL(); - - SDL_Window *win = SDL_CreateWindow("projectM", 0, 0, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - SDL_GL_GetDrawableSize(win,&width,&height); - - initStereoscopicView(win); - - SDL_GLContext glCtx = SDL_GL_CreateContext(win); - dumpOpenGLInfo(); - - SDL_SetWindowTitle(win, "projectM"); - - SDL_GL_MakeCurrent(win, glCtx); // associate GL context with main window - int avsync = SDL_GL_SetSwapInterval(-1); // try to enable adaptive vsync - if (avsync == -1) { // adaptive vsync not supported - SDL_GL_SetSwapInterval(1); // enable updates synchronized with vertical retrace - } - - std::string base_path = DATADIR_PATH; - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Using data directory: %s\n", base_path.c_str()); - - // load configuration file - std::string configFilePath = getConfigFilePath(base_path); - - if (! configFilePath.empty()) { - // found config file, use it - app = new projectMSDL(glCtx, configFilePath, 0); - SDL_Log("Using config from %s", configFilePath.c_str()); - } else { - // use some sane defaults if config file not found - base_path = SDL_GetBasePath(); - SDL_Log("Config file not found, using built-in settings. Data directory=%s\n", base_path.c_str()); - - // Get max refresh rate from attached displays to use as built-in max FPS. - int i = 0; - int maxRefreshRate = 0; - SDL_DisplayMode current; - for (i = 0; i < SDL_GetNumVideoDisplays(); ++i) - { - if (SDL_GetCurrentDisplayMode(i, ¤t) == 0) - { - if (current.refresh_rate > maxRefreshRate) maxRefreshRate = current.refresh_rate; - } - } - if (maxRefreshRate <= 60) maxRefreshRate = 60; - - float heightWidthRatio = (float)height / (float)width; - projectM::Settings settings; - settings.windowWidth = width; - settings.windowHeight = height; - settings.meshX = 128; - settings.meshY = settings.meshX * heightWidthRatio; - settings.fps = maxRefreshRate; - settings.smoothPresetDuration = 3; // seconds - settings.presetDuration = 22; // seconds - settings.hardcutEnabled = true; - settings.hardcutDuration = 60; - settings.hardcutSensitivity = 1.0; - settings.beatSensitivity = 1.0; - settings.aspectCorrection = 1; - settings.shuffleEnabled = 1; - settings.softCutRatingsEnabled = 1; // ??? - // get path to our app, use CWD or resource dir for presets/fonts/etc - settings.presetURL = base_path + "presets"; - // settings.presetURL = base_path + "presets/presets_shader_test"; - settings.menuFontURL = base_path + "fonts/Vera.ttf"; - settings.titleFontURL = base_path + "fonts/Vera.ttf"; - // init with settings - app = new projectMSDL(glCtx, settings, 0); - } - - // center window and full desktop screen - SDL_DisplayMode dm; - if (SDL_GetDesktopDisplayMode(0, &dm) == 0) { - width = dm.w; - height = dm.h; - } else { - SDL_Log("SDL_GetDesktopDisplayMode failed: %s", SDL_GetError()); - } - SDL_SetWindowPosition(win, initialWindowBounds.x, initialWindowBounds.y); - SDL_SetWindowSize(win, width, height); - app->resize(width, height); - - // Create a help menu specific to SDL - std::string modKey = "CTRL"; - -#if __APPLE__ - modKey = "CMD"; -#endif - - std::string sdlHelpMenu = "\n" - "F1: This help menu""\n" - "F3: Show preset name""\n" - "F4: Show details and statistics""\n" - "F5: Show FPS""\n" - "L or SPACE: Lock/Unlock Preset""\n" - "R: Random preset""\n" - "N: Next preset""\n" - "P: Previous preset""\n" - "UP: Increase Beat Sensitivity""\n" - "DOWN: Decrease Beat Sensitivity""\n" -#ifdef PROJECTM_TOUCH_ENABLED - "Left Click: Drop Random Waveform on Screen""\n" - "Right Click: Remove Random Waveform""\n" + - modKey + "+Right Click: Remove All Random Waveforms""\n" -#endif - + modKey + "+I: Audio Input (listen to next device)""\n" + - modKey + "+M: Change Monitor""\n" + - modKey + "+S: Stretch Monitors""\n" + - modKey + "+F: Fullscreen""\n" + - modKey + "+Q: Quit"; - app->setHelpText(sdlHelpMenu.c_str()); - app->init(win); - -#if STEREOSCOPIC_SBS - app->toggleFullScreen(); -#endif -#if FAKE_AUDIO - app->fakeAudio = true; -#endif - - enableGLDebugOutput(); - configureLoopback(app); - -#if !FAKE_AUDIO && !WASAPI_LOOPBACK - // get an audio input device - if (app->openAudioInput()) - app->beginAudioCapture(); -#endif - -#if TEST_ALL_PRESETS - testAllPresets(app); - return 0; -#endif - - return app; -} - -int64_t startUnlockedFPSCounter() { - using namespace std::chrono; - auto currentTime = steady_clock::now(); - auto currentTimeMs = time_point_cast(currentTime); - auto elapsedMs = currentTime.time_since_epoch(); - - return elapsedMs.count(); -} - -void advanceUnlockedFPSCounterFrame(int64_t startFrame) { - static int32_t frameCount = 0; - - frameCount++; - auto currentElapsedMs = startUnlockedFPSCounter(); - if (currentElapsedMs - startFrame > 5000) - { - printf("Frames[%d]\n", frameCount); - exit(0); - } -} +#include "setup.hpp" + +#include +#include + +#if OGL_DEBUG +void debugGL(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) { + + /*if (type != GL_DEBUG_TYPE_OTHER)*/ + { + std::cerr << " -- \n" << "Type: " << + type << "; Source: " << + source <<"; ID: " << id << "; Severity: " << + severity << "\n" << message << "\n"; + } +} +#endif + +// return path to config file to use +std::string getConfigFilePath(std::string datadir_path) { + char* home = NULL; + std::string projectM_home; + std::string projectM_config = DATADIR_PATH; + + projectM_config = datadir_path; + +#ifdef _MSC_VER + home=getenv("USERPROFILE"); +#else + home=getenv("HOME"); +#endif + + projectM_home = std::string(home); + projectM_home += "/.projectM"; + + // Create the ~/.projectM directory. If it already exists, mkdir will do nothing +#if defined _MSC_VER + _mkdir(projectM_home.c_str()); +#else + mkdir(projectM_home.c_str(), 0755); +#endif + + projectM_home += "/config.inp"; + projectM_config += "/config.inp"; + + std::ifstream f_home(projectM_home); + std::ifstream f_config(projectM_config); + std::cout << "f_home " << f_home.good() << "\n"; + + if (f_config.good() && !f_home.good()) { + std::ifstream f_src; + std::ofstream f_dst; + + f_src.open(projectM_config, std::ios::in | std::ios::binary); + f_dst.open(projectM_home, std::ios::out | std::ios::binary); + f_dst << f_src.rdbuf(); + f_dst.close(); + f_src.close(); + return std::string(projectM_home); + } else if (f_home.good()) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Read ~/.projectM/config.inp\n"); + return std::string(projectM_home); + } else if (f_config.good()) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot create ~/.projectM/config.inp, using %s\n", projectM_config.c_str()); + return std::string(projectM_config); + } else { + SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Using implementation defaults, your system is really messed up, I'm surprised we even got this far\n"); + return ""; + } +} + + +void seedRand() { +#ifndef WIN32 + srand((int)(time(NULL))); +#endif +} + +void initGL() { +#if USE_GLES + // use GLES 2.0 (this may need adjusting) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); +#else + // Disabling compatibility profile + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); +#endif +} + +void dumpOpenGLInfo() { + SDL_Log("- GL_VERSION: %s", glGetString(GL_VERSION)); + SDL_Log("- GL_SHADING_LANGUAGE_VERSION: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + SDL_Log("- GL_VENDOR: %s", glGetString(GL_VENDOR)); +} + +void initStereoscopicView(SDL_Window *win) { +#if STEREOSCOPIC_SB + // enable stereo + if (SDL_GL_SetAttribute(SDL_GL_STEREO, 1) == 0) + { + SDL_Log("SDL_GL_STEREO: true"); + } + + // requires fullscreen mode + SDL_ShowCursor(false); + SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN); +#endif +} + +void enableGLDebugOutput() { +#if OGL_DEBUG && !USE_GLES + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(debugGL, NULL); +#endif +} + +void testAllPresets(projectMSDL *app) { + uint buildErrors = 0; + for(unsigned int i = 0; i < app->getPlaylistSize(); i++) { + std::cout << i << "\t" << app->getPresetName(i) << std::endl; + app->selectPreset(i); + if (app->getErrorLoadingCurrentPreset()) { + buildErrors++; + } + } + + if (app->getPlaylistSize()) { + fprintf(stdout, "Preset loading errors: %d/%d [%d%%]\n", buildErrors, app->getPlaylistSize(), (buildErrors*100) / app->getPlaylistSize()); + } + + delete app; +} + +// initialize SDL, openGL, config +projectMSDL *setupSDLApp() { + projectMSDL *app; + seedRand(); + + if (!initLoopback()) + { + SDL_Log("Failed to initialize audio loopback devide."); + exit(1); + } + +#if UNLOCK_FPS + setenv("vblank_mode", "0", 1); +#endif + + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); + + if (! SDL_VERSION_ATLEAST(2, 0, 5)) { + SDL_Log("SDL version 2.0.5 or greater is required. You have %i.%i.%i", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL); + exit(1); + } + + // default window size to usable bounds (e.g. minus menubar and dock) + SDL_Rect initialWindowBounds; +#if SDL_VERSION_ATLEAST(2, 0, 5) + // new and better + SDL_GetDisplayUsableBounds(0, &initialWindowBounds); +#else + SDL_GetDisplayBounds(0, &initialWindowBounds); +#endif + int width = initialWindowBounds.w; + int height = initialWindowBounds.h; + + initGL(); + + SDL_Window *win = SDL_CreateWindow("projectM", 0, 0, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + SDL_GL_GetDrawableSize(win,&width,&height); + + initStereoscopicView(win); + + SDL_GLContext glCtx = SDL_GL_CreateContext(win); + +#if defined(WIN32) + GLenum err = glewInit(); +#endif /** WIN32 */ + + dumpOpenGLInfo(); + + SDL_SetWindowTitle(win, "projectM"); + + SDL_GL_MakeCurrent(win, glCtx); // associate GL context with main window + int avsync = SDL_GL_SetSwapInterval(-1); // try to enable adaptive vsync + if (avsync == -1) { // adaptive vsync not supported + SDL_GL_SetSwapInterval(1); // enable updates synchronized with vertical retrace + } + + std::string base_path = DATADIR_PATH; + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Using data directory: %s\n", base_path.c_str()); + + // load configuration file + std::string configFilePath = getConfigFilePath(base_path); + + if (! configFilePath.empty()) { + // found config file, use it + app = new projectMSDL(glCtx, configFilePath, 0); + SDL_Log("Using config from %s", configFilePath.c_str()); + } else { + // use some sane defaults if config file not found + base_path = SDL_GetBasePath(); + SDL_Log("Config file not found, using built-in settings. Data directory=%s\n", base_path.c_str()); + + // Get max refresh rate from attached displays to use as built-in max FPS. + int i = 0; + int maxRefreshRate = 0; + SDL_DisplayMode current; + for (i = 0; i < SDL_GetNumVideoDisplays(); ++i) + { + if (SDL_GetCurrentDisplayMode(i, ¤t) == 0) + { + if (current.refresh_rate > maxRefreshRate) maxRefreshRate = current.refresh_rate; + } + } + if (maxRefreshRate <= 60) maxRefreshRate = 60; + + float heightWidthRatio = (float)height / (float)width; + projectM::Settings settings; + settings.windowWidth = width; + settings.windowHeight = height; + settings.meshX = 128; + settings.meshY = settings.meshX * heightWidthRatio; + settings.fps = maxRefreshRate; + settings.smoothPresetDuration = 3; // seconds + settings.presetDuration = 22; // seconds + settings.hardcutEnabled = true; + settings.hardcutDuration = 60; + settings.hardcutSensitivity = 1.0; + settings.beatSensitivity = 1.0; + settings.aspectCorrection = 1; + settings.shuffleEnabled = 1; + settings.softCutRatingsEnabled = 1; // ??? + // get path to our app, use CWD or resource dir for presets/fonts/etc + settings.presetURL = base_path + "presets"; + // settings.presetURL = base_path + "presets/presets_shader_test"; + settings.menuFontURL = base_path + "fonts/Vera.ttf"; + settings.titleFontURL = base_path + "fonts/Vera.ttf"; + // init with settings + app = new projectMSDL(glCtx, settings, 0); + } + + // center window and full desktop screen + SDL_DisplayMode dm; + if (SDL_GetDesktopDisplayMode(0, &dm) == 0) { + width = dm.w; + height = dm.h; + } else { + SDL_Log("SDL_GetDesktopDisplayMode failed: %s", SDL_GetError()); + } + SDL_SetWindowPosition(win, initialWindowBounds.x, initialWindowBounds.y); + SDL_SetWindowSize(win, width, height); + app->resize(width, height); + + // Create a help menu specific to SDL + std::string modKey = "CTRL"; + +#if __APPLE__ + modKey = "CMD"; +#endif + + std::string sdlHelpMenu = "\n" + "F1: This help menu""\n" + "F3: Show preset name""\n" + "F4: Show details and statistics""\n" + "F5: Show FPS""\n" + "L or SPACE: Lock/Unlock Preset""\n" + "R: Random preset""\n" + "N: Next preset""\n" + "P: Previous preset""\n" + "UP: Increase Beat Sensitivity""\n" + "DOWN: Decrease Beat Sensitivity""\n" +#ifdef PROJECTM_TOUCH_ENABLED + "Left Click: Drop Random Waveform on Screen""\n" + "Right Click: Remove Random Waveform""\n" + + modKey + "+Right Click: Remove All Random Waveforms""\n" +#endif + + modKey + "+I: Audio Input (listen to next device)""\n" + + modKey + "+M: Change Monitor""\n" + + modKey + "+S: Stretch Monitors""\n" + + modKey + "+F: Fullscreen""\n" + + modKey + "+Q: Quit"; + app->setHelpText(sdlHelpMenu.c_str()); + app->init(win); + +#if STEREOSCOPIC_SBS + app->toggleFullScreen(); +#endif +#if FAKE_AUDIO + app->fakeAudio = true; +#endif + + enableGLDebugOutput(); + configureLoopback(app); + +#if !FAKE_AUDIO && !WASAPI_LOOPBACK + // get an audio input device + if (app->openAudioInput()) + app->beginAudioCapture(); +#endif + +#if TEST_ALL_PRESETS + testAllPresets(app); + return 0; +#endif + + return app; +} + +int64_t startUnlockedFPSCounter() { + using namespace std::chrono; + auto currentTime = steady_clock::now(); + auto currentTimeMs = time_point_cast(currentTime); + auto elapsedMs = currentTime.time_since_epoch(); + + return elapsedMs.count(); +} + +void advanceUnlockedFPSCounterFrame(int64_t startFrame) { + static int32_t frameCount = 0; + + frameCount++; + auto currentElapsedMs = startUnlockedFPSCounter(); + if (currentElapsedMs - startFrame > 5000) + { + printf("Frames[%d]\n", frameCount); + exit(0); + } +} diff --git a/src/projectM-test/CMakeLists.txt b/src/projectM-test/CMakeLists.txt new file mode 100644 index 0000000000..d8b07a4474 --- /dev/null +++ b/src/projectM-test/CMakeLists.txt @@ -0,0 +1,37 @@ +if(NOT ENABLE_TESTING) + return() +endif() + +add_executable(projectM-unittest + ConfigFile.cpp + ConfigFile.h + getConfigFilename.cpp + getConfigFilename.h + projectM-unittest.cpp + ) + +target_compile_definitions(projectM-unittest + PRIVATE + PROJECTM_PREFIX="${CMAKE_INSTALL_PREFIX}" + ) + +# Test includes a header file from libprojectM with its full path in the source dir. +target_include_directories(projectM-unittest + PRIVATE + "${CMAKE_SOURCE_DIR}" + ) + +target_link_libraries(projectM-unittest + PRIVATE + projectM_static + SDL2::SDL2 + SDL2::SDL2main + ${CMAKE_DL_LIBS} + ) + +# Normally there's no need to install test applications, but will +# keep it for now to resemble the autotools package structure. +install(TARGETS projectM-unittest + RUNTIME DESTINATION "${PROJECTM_BIN_DIR}" + COMPONENT Tests + ) diff --git a/src/projectM-test/getConfigFilename.cpp b/src/projectM-test/getConfigFilename.cpp index fc5e24dd86..dd48a446e1 100644 --- a/src/projectM-test/getConfigFilename.cpp +++ b/src/projectM-test/getConfigFilename.cpp @@ -1,93 +1,101 @@ -// -// File: getConfigFilename.cpp -// -// Author: fatray -// -// Created on 05 December 2007, 23:39 -// -// FIXME: portability - - -// I hacked include on to silence my compiler, is it valid? -#include -#include -#include -#include "getConfigFilename.h" -#include -#include -#include - -// get the full pathname of a configfile -std::string getConfigFilename() -{ - char num[512]; - FILE *in; - FILE *out; - - char* home; - // FIXME: fixed length buffers are not ideal. - char projectM_home[1024]; - char projectM_config[1024]; - - strcpy(projectM_config, PROJECTM_PREFIX); - strcpy(projectM_config + strlen(PROJECTM_PREFIX), CONFIG_FILE); - projectM_config[strlen(PROJECTM_PREFIX) + strlen(CONFIG_FILE)] = '\0'; - printf("dir:%s \n", projectM_config); - home = getenv("HOME"); - strcpy(projectM_home, home); - strcpy(projectM_home + strlen(home), "/.projectM/config.inp"); - projectM_home[strlen(home) + strlen("/.projectM/config.inp")] = '\0'; - - if ((in = fopen(projectM_home, "r"))) - { - printf("reading ~/.projectM/config.inp \n"); - fclose(in); - return std::string(projectM_home); - } - - printf("trying to create ~/.projectM/config.inp \n"); - - projectM_home[strlen(home) + strlen("/.projectM")] = '\0'; - mkdir(projectM_home, 0755); - - strcpy(projectM_home + strlen(home), "/.projectM/config.inp"); - projectM_home[strlen(home) + strlen("/.projectM/config.inp")] = '\0'; - - if((out = fopen(projectM_home, "w"))) - { - if ((in = fopen(projectM_config, "r"))) - { - while(fgets(num, 80, in)!=NULL) - { - fputs(num, out); - } - fclose(in); - fclose(out); - - - if ((in = fopen(projectM_home, "r"))) - { - printf("created ~/.projectM/config.inp successfully\n"); - fclose(in); - return std::string(projectM_home); - } - - printf("This shouldn't happen, using implementation defaults\n"); - abort(); - } - printf("Cannot find projectM default config, using implementation defaults\n"); - abort(); - } - - printf("Cannot create ~/.projectM/config.inp, using default config file\n"); - if ((in = fopen(projectM_config, "r"))) - { - printf("Successfully opened default config file\n"); - fclose(in); - return std::string(projectM_config); - } - - printf("Using implementation defaults, your system is really messed up, I'm surprised we even got this far\n"); - abort(); -} - +// +// File: getConfigFilename.cpp +// +// Author: fatray +// +// Created on 05 December 2007, 23:39 +// +// FIXME: portability + + +// I hacked include on to silence my compiler, is it valid? +#include +#include +#include +#include "getConfigFilename.h" +#include +#include +#include +#ifdef WIN32 +#include +#include +#endif + +// get the full pathname of a configfile +std::string getConfigFilename() +{ + char num[512]; + FILE *in; + FILE *out; + + char* home; + // FIXME: fixed length buffers are not ideal. + char projectM_home[1024]; + char projectM_config[1024]; + + strcpy(projectM_config, PROJECTM_PREFIX); + strcpy(projectM_config + strlen(PROJECTM_PREFIX), CONFIG_FILE); + projectM_config[strlen(PROJECTM_PREFIX) + strlen(CONFIG_FILE)] = '\0'; + printf("dir:%s \n", projectM_config); + home = getenv("HOME"); + strcpy(projectM_home, home); + strcpy(projectM_home + strlen(home), "/.projectM/config.inp"); + projectM_home[strlen(home) + strlen("/.projectM/config.inp")] = '\0'; + + if ((in = fopen(projectM_home, "r"))) + { + printf("reading ~/.projectM/config.inp \n"); + fclose(in); + return std::string(projectM_home); + } + + printf("trying to create ~/.projectM/config.inp \n"); + + projectM_home[strlen(home) + strlen("/.projectM")] = '\0'; +#ifdef WIN32 + CreateDirectory(home, NULL); +#else + mkdir(projectM_home, 0755); +#endif + + strcpy(projectM_home + strlen(home), "/.projectM/config.inp"); + projectM_home[strlen(home) + strlen("/.projectM/config.inp")] = '\0'; + + if((out = fopen(projectM_home, "w"))) + { + if ((in = fopen(projectM_config, "r"))) + { + while(fgets(num, 80, in)!=NULL) + { + fputs(num, out); + } + fclose(in); + fclose(out); + + + if ((in = fopen(projectM_home, "r"))) + { + printf("created ~/.projectM/config.inp successfully\n"); + fclose(in); + return std::string(projectM_home); + } + + printf("This shouldn't happen, using implementation defaults\n"); + abort(); + } + printf("Cannot find projectM default config, using implementation defaults\n"); + abort(); + } + + printf("Cannot create ~/.projectM/config.inp, using default config file\n"); + if ((in = fopen(projectM_config, "r"))) + { + printf("Successfully opened default config file\n"); + fclose(in); + return std::string(projectM_config); + } + + printf("Using implementation defaults, your system is really messed up, I'm surprised we even got this far\n"); + abort(); +} + diff --git a/src/projectM-test/projectM-unittest.cpp b/src/projectM-test/projectM-unittest.cpp index d1431b6c65..67d5fcb380 100644 --- a/src/projectM-test/projectM-unittest.cpp +++ b/src/projectM-test/projectM-unittest.cpp @@ -1,44 +1,50 @@ -/** - * projectM -- Milkdrop-esque visualisation SDK - * Copyright (C)2019-2019 projectM Team - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * See 'LICENSE.txt' included within this release - * - */ - -#include -#include -#include -#include -#include -#include - -int main(int argc, char **argv) -{ - SDL_Window *win = SDL_CreateWindow("projectM", 0, 0, 100, 100, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); - SDL_GLContext glCtx = SDL_GL_CreateContext(win); - - // try unit tests - if (!TestRunner::run()) - { - printf("unit tests failed\n"); - exit(1); - } - printf("unit tests succeeded\n"); - - SDL_GL_DeleteContext(glCtx); - exit(0); -} +/** + * projectM -- Milkdrop-esque visualisation SDK + * Copyright (C)2019-2019 projectM Team + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * See 'LICENSE.txt' included within this release + * + */ + +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif /** WIN32 */ + +int main(int argc, char *argv[]) +{ + SDL_Window *win = SDL_CreateWindow("projectM", 0, 0, 100, 100, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + SDL_GLContext glCtx = SDL_GL_CreateContext(win); +#ifdef WIN32 + GLenum err = glewInit(); +#endif /** WIN32 */ + + // try unit tests + if (!TestRunner::run()) + { + printf("unit tests failed\n"); + exit(1); + } + printf("unit tests succeeded\n"); + + SDL_GL_DeleteContext(glCtx); + exit(0); +}