diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml new file mode 100644 index 0000000..f0b3587 --- /dev/null +++ b/.github/workflows/build-linux.yml @@ -0,0 +1,120 @@ +name: Build Linux AppImage + +on: + workflow_dispatch: + push: + branches: + - main + - master + pull_request: + +jobs: + build-linux-appimage: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + cmake \ + libboost-all-dev \ + libgl1-mesa-dev \ + libxkbcommon-x11-0 \ + libxcb-cursor0 \ + openssl \ + qt6-base-dev \ + qt6-tools-dev-tools \ + qmake6 \ + patchelf \ + wget + + - name: Build and install liblsl + run: | + git clone --depth 1 --branch v1.16.2 https://github.com/sccn/liblsl.git liblsl + cmake -S liblsl -B liblsl/build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local + cmake --build liblsl/build -j"$(nproc)" + sudo cmake --install liblsl/build + sudo ldconfig + + - name: Decrypt Linux SDK + env: + SDK_DECRYPT_PASSPHRASE: ${{ secrets.SDK_DECRYPT_PASSPHRASE }} + run: | + if [ -z "${SDK_DECRYPT_PASSPHRASE}" ]; then + echo "Missing GitHub secret SDK_DECRYPT_PASSPHRASE." + exit 1 + fi + + if [ ! -f ./libeego-SDK.so.enc ]; then + echo "Missing encrypted file libeego-SDK.so.enc" + exit 1 + fi + + openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 \ + -in ./libeego-SDK.so.enc \ + -out ./libeego-SDK.so \ + -pass pass:"${SDK_DECRYPT_PASSPHRASE}" + + - name: Register eego SDK for linker + run: | + sudo ln -sf "$GITHUB_WORKSPACE/libeego-SDK.so" /usr/lib/libeego-SDK.so + ls -l /usr/lib/libeego-SDK.so + + - name: Configure and build (qmake + make) + run: | + rm -rf build-linux + mkdir -p build-linux + cd build-linux + qmake6 ../eegoSports.pro CONFIG+=release + make -j"$(nproc)" + + - name: Create desktop file + run: | + cat > eegoSports.desktop << 'EOF' + [Desktop Entry] + Type=Application + Name=eegoSports + Exec=eegoSports + Icon=eegoSports + Categories=Science; + EOF + + - name: Prepare AppImage icon + run: | + if [ ! -f ./resources/favicon_neuro.png ]; then + echo "Missing ./resources/favicon_neuro.png for AppImage icon." + exit 1 + fi + cp ./resources/favicon_neuro.png ./eegoSports.png + + - name: Build AppImage + run: | + export APPIMAGE_EXTRACT_AND_RUN=1 + export QMAKE=qmake6 + wget -O linuxdeploy.AppImage https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage + wget -O linuxdeploy-plugin-qt.AppImage https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage + chmod +x linuxdeploy.AppImage + chmod +x linuxdeploy-plugin-qt.AppImage + + ./linuxdeploy.AppImage \ + --appdir AppDir \ + -e build-linux/eegoSports \ + -d eegoSports.desktop \ + -i eegoSports.png \ + --library ./libeego-SDK.so \ + --library /usr/local/lib/liblsl.so \ + --plugin qt \ + --output appimage + + - name: Upload Linux artifacts + uses: actions/upload-artifact@v4 + with: + name: eegoSports-linux + path: | + ./*.AppImage + build-linux/eegoSports diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml new file mode 100644 index 0000000..ef9bea6 --- /dev/null +++ b/.github/workflows/build-windows.yml @@ -0,0 +1,128 @@ +name: Build Windows EXE + +on: + workflow_dispatch: + push: + branches: + - main + - master + pull_request: + +jobs: + build-windows: + runs-on: windows-2022 + + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup MSVC environment + uses: ilammy/msvc-dev-cmd@v1 + with: + toolset: 14.29 + + - name: Install Qt 6 + uses: jurplel/install-qt-action@v4 + with: + version: '6.6.3' + host: 'windows' + target: 'desktop' + arch: 'win64_msvc2019_64' + + - name: Resolve Qt tools (qmake/windeployqt) + shell: powershell + run: | + $candidates = @( + "$env:RUNNER_WORKSPACE\Qt\6.6.3\msvc2019_64\bin", + "$env:Qt6_DIR\bin" + ) + + $qtBin = $null + foreach ($candidate in $candidates) { + if ($candidate -and (Test-Path (Join-Path $candidate 'qmake.exe'))) { + $qtBin = $candidate + break + } + } + + if (-not $qtBin) { + throw 'Could not locate Qt MSVC qmake.exe.' + } + + "QT_QMAKE=$(Join-Path $qtBin 'qmake.exe')" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + "QT_WINDEPLOYQT=$(Join-Path $qtBin 'windeployqt.exe')" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Install Boost headers + shell: powershell + run: | + choco install boost-msvc-14.3 --yes --no-progress + $boostDir = Get-ChildItem -Path C:\local -Directory -Filter "boost_*" | Sort-Object Name -Descending | Select-Object -First 1 + if (-not $boostDir) { throw 'Boost installation directory not found under C:\local' } + "BOOST_ROOT=$($boostDir.FullName)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Install OpenSSL + shell: powershell + run: | + choco install openssl.light --yes --no-progress + + - name: Build and install liblsl + shell: powershell + run: | + git clone --depth 1 --branch v1.16.2 https://github.com/sccn/liblsl.git liblsl + cmake -S liblsl -B liblsl\build -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$PWD\liblsl-install" + cmake --build liblsl\build --config Release + cmake --install liblsl\build --config Release + "LSL_DIR=$PWD\liblsl-install" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Decrypt Windows SDK + shell: powershell + env: + SDK_DECRYPT_PASSPHRASE: ${{ secrets.SDK_DECRYPT_PASSPHRASE }} + run: | + if ([string]::IsNullOrWhiteSpace($env:SDK_DECRYPT_PASSPHRASE)) { + throw 'Missing GitHub secret SDK_DECRYPT_PASSPHRASE.' + } + + if (-not (Test-Path .\eego-SDK.lib.enc)) { throw 'Missing encrypted file .\eego-SDK.lib.enc' } + if (-not (Test-Path .\eego-SDK.dll.enc)) { throw 'Missing encrypted file .\eego-SDK.dll.enc' } + + openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 -in .\eego-SDK.lib.enc -out .\eego-SDK.lib -pass pass:$env:SDK_DECRYPT_PASSPHRASE + openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 -in .\eego-SDK.dll.enc -out .\eego-SDK.dll -pass pass:$env:SDK_DECRYPT_PASSPHRASE + + - name: Setup MSVC environment (build step) + uses: ilammy/msvc-dev-cmd@v1 + with: + toolset: 14.29 + + - name: Configure and build (qmake + nmake) + shell: powershell + run: | + if (Test-Path .\build-windows) { Remove-Item .\build-windows -Recurse -Force } + New-Item -ItemType Directory -Path build-windows -Force | Out-Null + Copy-Item .\eego-SDK.lib .\build-windows\eego-SDK.lib -Force + Push-Location build-windows + & "$env:QT_QMAKE" QMAKE_MSC_VER=1929 CONFIG+=release ..\eegoSports.pro + nmake + Pop-Location + + - name: Collect and deploy executable + shell: powershell + run: | + $exe = Get-ChildItem -Path build-windows -Recurse -File -Filter eegoSports.exe | Select-Object -First 1 + if (-not $exe) { throw 'eegoSports.exe not found after build.' } + + New-Item -ItemType Directory -Path dist -Force | Out-Null + Copy-Item $exe.FullName dist\eegoSports.exe -Force + + Copy-Item .\eego-SDK.dll dist\eego-SDK.dll -Force + + $lslDll = Get-ChildItem -Path "$PWD\liblsl-install" -Recurse -File -Filter lsl.dll | Select-Object -First 1 + if (-not $lslDll) { throw 'lsl.dll not found under liblsl-install.' } + Copy-Item $lslDll.FullName dist\lsl.dll -Force + + & "$env:QT_WINDEPLOYQT" --release --no-translations dist\eegoSports.exe + + - name: Upload Windows artifact + uses: actions/upload-artifact@v4 + with: + name: eegoSports-windows + path: dist/ diff --git a/eego-SDK.dll.enc b/eego-SDK.dll.enc new file mode 100644 index 0000000..349404e Binary files /dev/null and b/eego-SDK.dll.enc differ diff --git a/eego-SDK.lib.enc b/eego-SDK.lib.enc new file mode 100644 index 0000000..4abd4f1 Binary files /dev/null and b/eego-SDK.lib.enc differ diff --git a/eegoSports.pro b/eegoSports.pro index 83fae69..1cd0f86 100644 --- a/eegoSports.pro +++ b/eegoSports.pro @@ -15,7 +15,7 @@ TEMPLATE = app # any feature of Qt which has been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. -DEFINES += QT_DEPRECATED_WARNINGS +DEFINES += QT_DEPRECATED_WARNINGS EEGO_SDK_BIND_STATIC _UNICODE UNICODE # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. @@ -34,12 +34,12 @@ HEADERS += mainwindow.h \ FORMS += mainwindow.ui unix:!macx: { - LIBS += -leego-SDK -ldl -llsl -lboost_thread -lboost_chrono + LIBS += -leego-SDK -ldl -llsl } win32: { - INCLUDEPATH += $(BOOST_ROOT) \ - quote($$LSL_DIR/include) + INCLUDEPATH += $$quote($$(BOOST_ROOT)) \ + $$quote($$(LSL_DIR)/include) LIBS += -L$$quote($$PWD) -leego-SDK \ -L$$quote($$(LSL_DIR)/lib) -llsl } diff --git a/libeego-SDK.so.enc b/libeego-SDK.so.enc new file mode 100644 index 0000000..e8a9d77 Binary files /dev/null and b/libeego-SDK.so.enc differ diff --git a/mainwindow.cpp b/mainwindow.cpp index 2dae259..62ae014 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,18 +1,17 @@ #include "mainwindow.h" #include "ui_mainwindow.h" -#include "wrapper.cc" #include #include #include -#include #include #include -#include -#include #include #include +#include +#include +#include using namespace eemagine::sdk; @@ -111,13 +110,14 @@ MainWindow::MainWindow(QWidget *parent, const std::string &config_file, const bo ui.setupUi(this); // Init initial indexes - if(!config_file.empty()) + if(!config_file.empty()) { load_config(config_file); - else + } else { ui.Cap_ID->setCurrentIndex(1); ui.samplingRate->setCurrentIndex(1); ui.EEG_Range->setCurrentIndex(0); ui.BIP_Range->setCurrentIndex(2); + } // make GUI connections @@ -416,13 +416,13 @@ void Reader::read() { data_info.desc().append_child("acquisition") .append_child_value("manufacturer", "antneuro") - .append_child_value("serial_number", boost::lexical_cast(amp->getSerialNumber()).c_str()); + .append_child_value("serial_number", amp->getSerialNumber().c_str()); // make a data outlet lsl::stream_outlet data_outlet(data_info); // create marker streaminfo and outlet - lsl::stream_info marker_info("eegoSports-" + amp->getSerialNumber() + "_markers", "Markers", 1, 0, lsl::cf_string, "eegoSports_" + boost::lexical_cast(amp->getSerialNumber()) + "_markers"); + lsl::stream_info marker_info("eegoSports-" + amp->getSerialNumber() + "_markers", "Markers", 1, 0, lsl::cf_string, "eegoSports_" + amp->getSerialNumber() + "_markers"); lsl::stream_outlet marker_outlet(marker_info); std::vector eegChannelList = eegStream->getChannelList(); @@ -431,7 +431,7 @@ void Reader::read() { while (!stop) { //Sleep(8); - boost::this_thread::sleep_for(boost::chrono::milliseconds(8)); + std::this_thread::sleep_for(std::chrono::milliseconds(8)); buffer = eegStream->getData(); unsigned int channelCount = buffer.getChannelCount(); @@ -458,24 +458,24 @@ void Reader::read() { //if (int mrk = src_buffer[channelCount + s*(channelCount + 1)]) { double mrk = buffer.getSample(channelCount - 1, s); if (mrk != last_mrk) { - std::string mrk_string = boost::lexical_cast(mrk); + std::string mrk_string = std::to_string(mrk); marker_outlet.push_sample(&mrk_string, now + (s + 1 - sampleCount) / static_cast(samplingRate)); last_mrk = mrk; } } } } - catch (exceptions::notFound) { + catch (const exceptions::notFound &) { ampFound = false; emit ampNotFound(); } - catch (exceptions::notConnected) { + catch (const exceptions::notConnected &) { emit connectionLost(); } - catch (exceptions::unknown) { + catch (const exceptions::unknown &) { emit unknownError(); } - catch(std::exception &e) { + catch (const std::exception &e) { std::cout< // LSL API -#include +#include // eegoSports #define WIN32_LEAN_AND_MEAN -#define EEGO_SDK_BIND_STATIC #ifdef _WIN32 #include diff --git a/resources/favicon_neuro.png b/resources/favicon_neuro.png new file mode 100644 index 0000000..db1da3d Binary files /dev/null and b/resources/favicon_neuro.png differ diff --git a/scripts/encrypt-sdk.ps1 b/scripts/encrypt-sdk.ps1 new file mode 100644 index 0000000..d6cfea6 --- /dev/null +++ b/scripts/encrypt-sdk.ps1 @@ -0,0 +1,34 @@ +param( + [Parameter(Mandatory = $true)] + [string]$Passphrase +) + +$ErrorActionPreference = 'Stop' + +function Encrypt-File { + param( + [Parameter(Mandatory = $true)] + [string]$InputFile, + [Parameter(Mandatory = $true)] + [string]$OutputFile + ) + + if (-not (Test-Path $InputFile)) { + throw "Missing input file: $InputFile" + } + + openssl enc -aes-256-cbc -pbkdf2 -iter 200000 -salt -in $InputFile -out $OutputFile -pass pass:$Passphrase + + if (-not (Test-Path $OutputFile)) { + throw "Failed to create encrypted file: $OutputFile" + } +} + +Encrypt-File -InputFile '.\eego-SDK.dll' -OutputFile '.\eego-SDK.dll.enc' +Encrypt-File -InputFile '.\eego-SDK.lib' -OutputFile '.\eego-SDK.lib.enc' +Encrypt-File -InputFile '.\libeego-SDK.so' -OutputFile '.\libeego-SDK.so.enc' + +Write-Host 'Encrypted SDK files created:' +Write-Host ' - eego-SDK.dll.enc' +Write-Host ' - eego-SDK.lib.enc' +Write-Host ' - libeego-SDK.so.enc'