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);
+}