build: add freebsd support (#4049)

This commit is contained in:
David Lane 2025-11-11 23:46:11 -05:00 committed by GitHub
commit 1d6d916b7a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 1055 additions and 39 deletions

272
.github/workflows/ci-freebsd.yml vendored Normal file
View file

@ -0,0 +1,272 @@
---
name: CI-FreeBSD
permissions:
contents: read
on:
workflow_call:
inputs:
release_commit:
required: true
type: string
release_version:
required: true
type: string
env:
BRANCH: ${{ github.head_ref || github.ref_name }}
BUILD_VERSION: ${{ inputs.release_version }}
COMMIT: ${{ inputs.release_commit }}
FREEBSD_CLANG_VERSION: 19
jobs:
setup-matrix:
name: Setup Build Matrix
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.generate-matrix.outputs.matrix }}
steps:
- name: Generate Matrix
id: generate-matrix
shell: bash
run: |
# Base matrix with amd64 build
matrix='{
"include": [
{
"bsd_release": "14.3",
"arch": "x86_64",
"cmake_processor": "amd64",
"runner": "ubuntu-latest"
}
]
}'
# Add aarch64 build only if not a pull request event
if [[ "${{ github.event_name }}" != "pull_request" ]]; then
matrix=$(echo "$matrix" | jq '.include += [{
"bsd_release": "14.3",
"arch": "aarch64",
"cmake_processor": "aarch64",
"runner": "ubuntu-latest"
}]')
fi
# Use heredoc for multiline JSON output
{
echo "matrix<<EOF"
echo "$matrix"
echo "EOF"
} >> "${GITHUB_OUTPUT}"
echo "Generated matrix:"
echo "$matrix" | jq .
build_freebsd:
name: ${{ matrix.cmake_processor }}-${{ matrix.bsd_release }}
runs-on: ubuntu-latest
needs: setup-matrix
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup-matrix.outputs.matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v5
with:
submodules: recursive
- name: Get Processor Count
id: processor_count
shell: bash
run: |
PROCESSOR_COUNT=$(nproc)
echo "PROCESSOR_COUNT=${PROCESSOR_COUNT}" >> "${GITHUB_OUTPUT}"
echo "PROCESSOR_COUNT: $PROCESSOR_COUNT"
- name: Setup FreeBSD
uses: vmactions/freebsd-vm@783ae15c0393f8a2582a139f76cc55f2d887b4a6 # v1.2.6
with:
arch: ${{ matrix.arch }}
cpu: ${{ steps.processor_count.outputs.PROCESSOR_COUNT }}
envs: 'BRANCH BUILD_VERSION COMMIT'
# TODO: there is no libcap for freebsd... we need graphics/libdrm if we find a way to use libcap
# TODO: docs are off because doxygen is too old: https://www.freshports.org/devel/doxygen/ must be >= 1.10
prepare: |
set -e
pkg update
pkg upgrade -y
pkg install -y \
audio/opus \
audio/pulseaudio \
devel/boost-all \
devel/cmake-core \
devel/evdev-proto \
devel/git \
devel/libayatana-appindicator \
devel/libevdev \
devel/libnotify \
devel/llvm${{ env.FREEBSD_CLANG_VERSION }} \
devel/ninja \
devel/pkgconf \
ftp/curl \
graphics/libdrm \
graphics/wayland \
lang/python312 \
multimedia/libva \
net/miniupnpc \
ports-mgmt/pkg \
security/openssl \
shells/bash \
www/npm \
x11/libX11 \
x11/libxcb \
x11/libXfixes \
x11/libXrandr \
x11/libXtst \
x11-servers/xorg-server
# create symlink for shebang bash compatibility
ln -s /usr/local/bin/bash /bin/bash
# setup python
ln -s /usr/local/bin/python3.12 /usr/local/bin/python
python -m ensurepip
release: ${{ matrix.bsd_release }}
run: |
set -e
# install gcvor
python -m pip install gcovr
# fix git safe.directory issues
git config --global --add safe.directory "*"
sync: nfs # sshfs is used for build-deps; however it's much slower than nfs
- name: Configure
shell: freebsd {0}
run: |
set -e
cd "${GITHUB_WORKSPACE}"
cc_path="$(which clang${{ env.FREEBSD_CLANG_VERSION }})"
cxx_path="$(which clang++${{ env.FREEBSD_CLANG_VERSION }})"
export CC="${cc_path}"
export CXX="${cxx_path}"
mkdir -p build
cmake \
-B build \
-G Ninja \
-S . \
-DBUILD_DOCS=OFF \
-DBUILD_WERROR=OFF \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DSUNSHINE_ASSETS_DIR=share/assets \
-DSUNSHINE_EXECUTABLE_PATH=/usr/local/bin/sunshine \
-DSUNSHINE_ENABLE_CUDA=OFF \
-DSUNSHINE_ENABLE_DRM=OFF \
-DSUNSHINE_ENABLE_WAYLAND=ON \
-DSUNSHINE_ENABLE_X11=ON \
-DSUNSHINE_PUBLISHER_NAME='${{ github.repository_owner }}' \
-DSUNSHINE_PUBLISHER_WEBSITE='https://app.lizardbyte.dev' \
-DSUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support'
- name: Build
shell: freebsd {0}
run: |
set -e
cd "${GITHUB_WORKSPACE}"
ninja -C build
- name: Package
shell: freebsd {0}
run: |
set -e
cd "${GITHUB_WORKSPACE}"
mkdir -p artifacts
cd build
cpack -G FREEBSD
# move compiled files to artifacts
mv ./cpack_artifacts/Sunshine.pkg \
../artifacts/Sunshine-FreeBSD-${{ matrix.bsd_release }}-${{ matrix.cmake_processor }}.pkg
- name: Debug
if: always()
shell: bash
working-directory: build/cpack_artifacts/_CPack_Packages/FreeBSD/FREEBSD/Sunshine
run: |
echo "FreeBSD CPack Debug"
echo "===== Staging Directory Contents ====="
ls -la
echo ""
echo "===== +MANIFEST Content ====="
cat +MANIFEST
echo ""
# use tar to print the contents of the pkg
cd "${GITHUB_WORKSPACE}/artifacts"
echo "===== Package Contents ====="
tar -tvf Sunshine-FreeBSD-${{ matrix.bsd_release }}-${{ matrix.cmake_processor }}.pkg
echo ""
echo "===== Package Statistics ====="
echo -n "Total files in package: "
tar -tf Sunshine-FreeBSD-${{ matrix.bsd_release }}-${{ matrix.cmake_processor }}.pkg 2>&1 | wc -l
echo ""
echo "Package file size:"
ls -lh Sunshine-FreeBSD-${{ matrix.bsd_release }}-${{ matrix.cmake_processor }}.pkg
- name: Test
id: test
shell: freebsd {0}
run: |
set -e
cd "${GITHUB_WORKSPACE}/build/tests"
export DISPLAY=:1
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
XVFB_PID=$!
./test_sunshine --gtest_color=yes --gtest_output=xml:test_results.xml
kill ${XVFB_PID}
- name: Generate gcov report
id: test_report
# any except canceled or skipped
if: >-
always() &&
(steps.test.outcome == 'success' || steps.test.outcome == 'failure')
shell: freebsd {0}
run: |
cd "${GITHUB_WORKSPACE}/build"
python -m gcovr . -r ../src \
--exclude-noncode-lines \
--exclude-throw-branches \
--exclude-unreachable-branches \
--verbose \
--xml-pretty \
-o coverage.xml
- name: Upload coverage artifact
if: >-
always() &&
(steps.test_report.outcome == 'success')
uses: actions/upload-artifact@v5
with:
name: coverage-FreeBSD-${{ matrix.bsd_release }}-${{ matrix.cmake_processor }}
path: |
build/coverage.xml
build/tests/test_results.xml
if-no-files-found: error
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: build-FreeBSD-${{ matrix.bsd_release }}-${{ matrix.cmake_processor }}
path: artifacts/
if-no-files-found: error

View file

@ -69,6 +69,14 @@ jobs:
GH_BOT_TOKEN: ${{ secrets.GH_BOT_TOKEN }} GH_BOT_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build-freebsd:
name: FreeBSD
needs: release-setup
uses: ./.github/workflows/ci-freebsd.yml
with:
release_commit: ${{ needs.release-setup.outputs.release_commit }}
release_version: ${{ needs.release-setup.outputs.release_version }}
build-homebrew: build-homebrew:
name: Homebrew name: Homebrew
needs: release-setup needs: release-setup
@ -133,6 +141,7 @@ jobs:
!cancelled() && !cancelled() &&
startsWith(github.repository, 'LizardByte/') startsWith(github.repository, 'LizardByte/')
needs: needs:
- build-freebsd
- build-linux - build-linux
- build-linux-flatpak - build-linux-flatpak
- build-homebrew - build-homebrew
@ -142,29 +151,53 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- name: FreeBSD-14.3-amd64
coverage: true
pr: true
- name: FreeBSD-14.3-aarch64
coverage: true
pr: false
- name: Linux-AppImage - name: Linux-AppImage
coverage: true coverage: true
pr: true
- name: Homebrew-macos-14 - name: Homebrew-macos-14
coverage: false coverage: false
pr: true
- name: Homebrew-macos-15 - name: Homebrew-macos-15
coverage: false coverage: false
pr: true
- name: Homebrew-macos-26 - name: Homebrew-macos-26
coverage: false coverage: false
pr: true
- name: Homebrew-ubuntu-latest - name: Homebrew-ubuntu-latest
coverage: false coverage: false
pr: true
- name: Windows-AMD64 - name: Windows-AMD64
coverage: true coverage: true
pr: true
steps: steps:
- name: Should run
id: should_run
run: |
if [ ${{ github.event_name }} != 'pull_request' ] || [ ${{ matrix.pr }} == 'true' ]; then
echo "SHOULD_RUN=true" >> "${GITHUB_OUTPUT}"
else
echo "SHOULD_RUN=false" >> "${GITHUB_OUTPUT}"
fi
- name: Checkout - name: Checkout
if: steps.should_run.outputs.SHOULD_RUN == 'true'
uses: actions/checkout@v5 uses: actions/checkout@v5
- name: Download coverage artifact - name: Download coverage artifact
if: steps.should_run.outputs.SHOULD_RUN == 'true'
uses: actions/download-artifact@v6 uses: actions/download-artifact@v6
with: with:
name: coverage-${{ matrix.name }} name: coverage-${{ matrix.name }}
path: _coverage path: _coverage
- name: Upload test results - name: Upload test results
if: steps.should_run.outputs.SHOULD_RUN == 'true'
uses: codecov/test-results-action@v1 uses: codecov/test-results-action@v1
with: with:
disable_search: true disable_search: true
@ -175,8 +208,8 @@ jobs:
verbose: true verbose: true
- name: Upload coverage - name: Upload coverage
if: steps.should_run.outputs.SHOULD_RUN == 'true' && matrix.coverage != false
uses: codecov/codecov-action@v5 uses: codecov/codecov-action@v5
if: matrix.coverage != false
with: with:
disable_search: true disable_search: true
fail_ci_if_error: true fail_ci_if_error: true
@ -193,6 +226,7 @@ jobs:
needs: needs:
- release-setup - release-setup
- build-docker - build-docker
- build-freebsd
- build-linux - build-linux
- build-linux-flatpak - build-linux-flatpak
- build-homebrew - build-homebrew

View file

@ -24,6 +24,10 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
endif() endif()
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
set(FREEBSD ON)
endif()
# set the module path, used for includes # set the module path, used for includes
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")

179
README.md
View file

@ -32,6 +32,166 @@ LizardByte has the full documentation hosted on [Read the Docs](https://docs.liz
* [Stable Docs](https://docs.lizardbyte.dev/projects/sunshine/latest/) * [Stable Docs](https://docs.lizardbyte.dev/projects/sunshine/latest/)
* [Beta Docs](https://docs.lizardbyte.dev/projects/sunshine/master/) * [Beta Docs](https://docs.lizardbyte.dev/projects/sunshine/master/)
## 🎮 Feature Compatibility
<table>
<caption id="feature_compatibility">Platform Feature Support</caption>
<tr>
<th>Feature</th>
<th>FreeBSD</th>
<th>Linux</th>
<th>macOS</th>
<th>Windows</th>
</tr>
<tr>
<td colspan="5" align="center"><b>Gamepad Emulation</b><br>
What type of gamepads can be emulated on the host.<br>
Clients may support other gamepads.
</td>
</tr>
<tr>
<td>DualShock / DS4 (PlayStation 4)</td>
<td></td>
<td></td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>DualSense / DS5 (PlayStation 5)</td>
<td>❌</td>
<td>✅</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<td>Nintendo Switch Pro</td>
<td>✅</td>
<td>✅</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<td>Xbox 360</td>
<td></td>
<td></td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>Xbox One/Series</td>
<td>✅</td>
<td>✅</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<td colspan="5" align="center"><b>GPU Encoding</b></td>
</tr>
<tr>
<td>AMD/AMF</td>
<td>✅ (vaapi)</td>
<td>✅ (vaapi)</td>
<td>✅ (Video Toolbox)</td>
<td>✅</td>
</tr>
<tr>
<td>Intel QuickSync</td>
<td>✅ (vaapi)</td>
<td>✅ (vaapi)</td>
<td>✅ (Video Toolbox)</td>
<td>✅</td>
</tr>
<tr>
<td>NVIDIA NVENC</td>
<td>✅ (vaapi)</td>
<td>✅ (vaapi)</td>
<td>✅ (Video Toolbox)</td>
<td>✅</td>
</tr>
<tr>
<td colspan="5" align="center"><b>Screen Capture</b></td>
</tr>
<tr>
<td>DXGI</td>
<td></td>
<td></td>
<td></td>
<td>✅</td>
</tr>
<tr>
<td>KMS</td>
<td>❌</td>
<td>✅</td>
<td></td>
<td></td>
</tr>
<tr>
<td>NVIDIA NvFBC</td>
<td></td>
<td>🟡</td>
<td></td>
<td></td>
</tr>
<tr>
<td>&nbsp;&nbsp;↳ X11 Support</td>
<td></td>
<td>✅</td>
<td></td>
<td></td>
</tr>
<tr>
<td>&nbsp;&nbsp;↳ Wayland Support</td>
<td></td>
<td>❌</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Video Toolbox</td>
<td></td>
<td></td>
<td>✅</td>
<td></td>
</tr>
<tr>
<td>Wayland</td>
<td>✅</td>
<td>✅</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Windows.Graphics.Capture</td>
<td></td>
<td></td>
<td></td>
<td>🟡</td>
</tr>
<tr>
<td>&nbsp;&nbsp;↳ Portable</td>
<td></td>
<td></td>
<td></td>
<td>✅</td>
</tr>
<tr>
<td>&nbsp;&nbsp;↳ Service</td>
<td></td>
<td></td>
<td></td>
<td>❌</td>
</tr>
<tr>
<td>X11</td>
<td>✅</td>
<td>✅</td>
<td></td>
<td></td>
</tr>
</table>
**Legend:** ✅ Supported | 🟡 Partial Support | ❌ Not Yet Supported | Not Applicable
## 🖥️ System Requirements ## 🖥️ System Requirements
> [!WARNING] > [!WARNING]
@ -50,7 +210,7 @@ LizardByte has the full documentation hosted on [Read the Docs](https://docs.liz
<tr> <tr>
<td> <td>
Intel:<br> Intel:<br>
&nbsp;&nbsp;Linux: VAAPI-compatible, see: <a href="https://www.intel.com/content/www/us/en/developer/articles/technical/linuxmedia-vaapi.html">VAAPI hardware support</a><br> &nbsp;&nbsp;FreeBSD/Linux: VAAPI-compatible, see: <a href="https://www.intel.com/content/www/us/en/developer/articles/technical/linuxmedia-vaapi.html">VAAPI hardware support</a><br>
&nbsp;&nbsp;Windows: Skylake or newer with QuickSync encoding support &nbsp;&nbsp;Windows: Skylake or newer with QuickSync encoding support
</td> </td>
</tr> </tr>
@ -69,11 +229,8 @@ LizardByte has the full documentation hosted on [Read the Docs](https://docs.liz
<td>4GB or more</td> <td>4GB or more</td>
</tr> </tr>
<tr> <tr>
<td rowspan="5">OS</td> <td rowspan="6">OS</td>
<td>Windows: 10+ (Windows Server does not support virtual gamepads)</td> <td>FreeBSD: 14.3+</td>
</tr>
<tr>
<td>macOS: 14+</td>
</tr> </tr>
<tr> <tr>
<td>Linux/Debian: 13+ (trixie)</td> <td>Linux/Debian: 13+ (trixie)</td>
@ -84,6 +241,12 @@ LizardByte has the full documentation hosted on [Read the Docs](https://docs.liz
<tr> <tr>
<td>Linux/Ubuntu: 22.04+ (jammy)</td> <td>Linux/Ubuntu: 22.04+ (jammy)</td>
</tr> </tr>
<tr>
<td>macOS: 14+</td>
</tr>
<tr>
<td>Windows: 11+ (Windows Server does not support virtual gamepads)</td>
</tr>
<tr> <tr>
<td rowspan="2">Network</td> <td rowspan="2">Network</td>
<td>Host: 5GHz, 802.11ac</td> <td>Host: 5GHz, 802.11ac</td>
@ -106,14 +269,14 @@ LizardByte has the full documentation hosted on [Read the Docs](https://docs.liz
<tr> <tr>
<td> <td>
Intel:<br> Intel:<br>
&nbsp;&nbsp;Linux: HD Graphics 510 or higher<br> &nbsp;&nbsp;FreeBSD/Linux: HD Graphics 510 or higher<br>
&nbsp;&nbsp;Windows: Skylake or newer with QuickSync encoding support &nbsp;&nbsp;Windows: Skylake or newer with QuickSync encoding support
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
Nvidia:<br> Nvidia:<br>
&nbsp;&nbsp;Linux: GeForce RTX 2000 series or higher<br> &nbsp;&nbsp;FreeBSD/Linux: GeForce RTX 2000 series or higher<br>
&nbsp;&nbsp;Windows: Geforce GTX 1080 or higher &nbsp;&nbsp;Windows: Geforce GTX 1080 or higher
</td> </td>
</tr> </tr>

View file

@ -1,6 +1,10 @@
# linux specific compile definitions # linux specific compile definitions
add_compile_definitions(SUNSHINE_PLATFORM="linux") if(FREEBSD)
add_compile_definitions(SUNSHINE_PLATFORM="freebsd")
else()
add_compile_definitions(SUNSHINE_PLATFORM="linux")
endif()
# AppImage # AppImage
if(${SUNSHINE_BUILD_APPIMAGE}) if(${SUNSHINE_BUILD_APPIMAGE})
@ -211,6 +215,9 @@ endif()
# These need to be set before adding the inputtino subdirectory in order for them to be picked up # These need to be set before adding the inputtino subdirectory in order for them to be picked up
set(LIBEVDEV_CUSTOM_INCLUDE_DIR "${EVDEV_INCLUDE_DIR}") set(LIBEVDEV_CUSTOM_INCLUDE_DIR "${EVDEV_INCLUDE_DIR}")
set(LIBEVDEV_CUSTOM_LIBRARY "${EVDEV_LIBRARY}") set(LIBEVDEV_CUSTOM_LIBRARY "${EVDEV_LIBRARY}")
if(FREEBSD)
set(USE_UHID OFF)
endif()
add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/inputtino") add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/inputtino")
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES inputtino::libinputtino) list(APPEND SUNSHINE_EXTERNAL_LIBRARIES inputtino::libinputtino)

View file

@ -30,6 +30,9 @@ include_directories(SYSTEM ${MINIUPNP_INCLUDE_DIRS})
if(NOT DEFINED FFMPEG_PREPARED_BINARIES) if(NOT DEFINED FFMPEG_PREPARED_BINARIES)
if(WIN32) if(WIN32)
set(FFMPEG_PLATFORM_LIBRARIES mfplat ole32 strmiids mfuuid vpl) set(FFMPEG_PLATFORM_LIBRARIES mfplat ole32 strmiids mfuuid vpl)
elseif(FREEBSD)
# numa is not available on FreeBSD
set(FFMPEG_PLATFORM_LIBRARIES va va-drm va-x11 X11)
elseif(UNIX AND NOT APPLE) elseif(UNIX AND NOT APPLE)
set(FFMPEG_PLATFORM_LIBRARIES numa va va-drm va-x11 X11) set(FFMPEG_PLATFORM_LIBRARIES numa va va-drm va-x11 X11)
endif() endif()

View file

@ -0,0 +1,138 @@
# FreeBSD post-build script to fix +POST_INSTALL and +PRE_DEINSTALL scripts
# in the generated .pkg file.
#
# This script runs AFTER CPack creates the .pkg file. We need to:
# 1. Extract the .pkg file (which is a tar.xz archive)
# 2. Add our install/deinstall scripts to the root
# 3. Remove script entries from the +MANIFEST files section
# 4. Repack the .pkg file using pkg-static
if(NOT CPACK_GENERATOR STREQUAL "FREEBSD")
return()
endif()
message(STATUS "FreeBSD post-build: Processing install/deinstall scripts")
# Get script paths from the list we set
if(NOT DEFINED CPACK_FREEBSD_PACKAGE_SCRIPTS)
message(FATAL_ERROR "FreeBSD post-build: CPACK_FREEBSD_PACKAGE_SCRIPTS not defined")
endif()
list(LENGTH CPACK_FREEBSD_PACKAGE_SCRIPTS _script_count)
if(_script_count EQUAL 0)
message(FATAL_ERROR "FreeBSD post-build: CPACK_FREEBSD_PACKAGE_SCRIPTS is empty")
endif()
# Find the package file in CPACK_TOPLEVEL_DIRECTORY
file(GLOB _pkg_files "${CPACK_TOPLEVEL_DIRECTORY}/*.pkg")
if(NOT _pkg_files)
message(FATAL_ERROR "FreeBSD post-build: No .pkg file found in ${CPACK_TOPLEVEL_DIRECTORY}")
endif()
list(GET _pkg_files 0 _pkg_file)
message(STATUS "FreeBSD post-build: Found package: ${_pkg_file}")
# Create a temporary directory for extraction
get_filename_component(_pkg_dir "${_pkg_file}" DIRECTORY)
set(_tmp_dir "${_pkg_dir}/pkg_repack_tmp")
file(REMOVE_RECURSE "${_tmp_dir}")
file(MAKE_DIRECTORY "${_tmp_dir}")
# Extract the package using tar (pkg files are tar.xz archives)
message(STATUS "FreeBSD post-build: Extracting package...")
find_program(TAR_EXECUTABLE tar REQUIRED)
find_program(PKG_STATIC_EXECUTABLE pkg-static REQUIRED)
execute_process(
COMMAND ${TAR_EXECUTABLE} -xf ${_pkg_file} --no-same-owner --numeric-owner
WORKING_DIRECTORY "${_tmp_dir}"
RESULT_VARIABLE _extract_result
ERROR_VARIABLE _extract_error
)
if(NOT _extract_result EQUAL 0)
message(FATAL_ERROR "FreeBSD post-build: Failed to extract package: ${_extract_error}")
endif()
# Debug: Check what was extracted
file(GLOB_RECURSE _extracted_files RELATIVE "${_tmp_dir}" "${_tmp_dir}/*")
list(LENGTH _extracted_files _file_count)
message(STATUS "FreeBSD post-build: Extracted ${_file_count} files")
# Copy the install/deinstall scripts to the extracted package root
message(STATUS "FreeBSD post-build: Adding install/deinstall scripts...")
foreach(script_path ${CPACK_FREEBSD_PACKAGE_SCRIPTS})
if(EXISTS "${script_path}")
get_filename_component(_script_name "${script_path}" NAME)
file(COPY "${script_path}"
DESTINATION "${_tmp_dir}/"
FILE_PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)
message(STATUS " Added: ${_script_name}")
else()
message(FATAL_ERROR "FreeBSD post-build: Script not found: ${script_path}")
endif()
endforeach()
# Repack the package using pkg-static create
message(STATUS "FreeBSD post-build: Repacking package...")
# Debug: Verify files before repacking
file(GLOB_RECURSE _files_before_repack RELATIVE "${_tmp_dir}" "${_tmp_dir}/*")
list(LENGTH _files_before_repack _count_before_repack)
message(STATUS "FreeBSD post-build: About to repack ${_count_before_repack} files")
# Debug: Check directory structure
if(EXISTS "${_tmp_dir}/usr")
message(STATUS "FreeBSD post-build: Found usr directory in extracted package")
file(GLOB_RECURSE _usr_files RELATIVE "${_tmp_dir}/usr" "${_tmp_dir}/usr/*")
list(LENGTH _usr_files _usr_file_count)
message(STATUS "FreeBSD post-build: usr directory contains ${_usr_file_count} files")
endif()
# Create metadata directory separate from rootdir
set(_metadata_dir "${_tmp_dir}/metadata")
file(MAKE_DIRECTORY "${_metadata_dir}")
# Move manifest and scripts to metadata directory
file(GLOB _metadata_files "${_tmp_dir}/+*")
foreach(meta_file ${_metadata_files})
get_filename_component(_meta_name "${meta_file}" NAME)
file(RENAME "${meta_file}" "${_metadata_dir}/${_meta_name}")
message(STATUS "FreeBSD post-build: Moved ${_meta_name} to metadata directory")
endforeach()
# Use pkg-static create to rebuild the package
# pkg create -r rootdir -m manifestdir -o outdir
# The rootdir should contain the actual files (usr/local/...)
# The manifestdir should contain +MANIFEST and install scripts
execute_process(
COMMAND ${PKG_STATIC_EXECUTABLE} create -r ${_tmp_dir} -m ${_metadata_dir} -o ${_pkg_dir}
RESULT_VARIABLE _pack_result
OUTPUT_VARIABLE _pack_output
ERROR_VARIABLE _pack_error
)
if(NOT _pack_result EQUAL 0)
message(FATAL_ERROR "FreeBSD post-build: Failed to repack package: ${_pack_error}")
endif()
# Find the generated package file (pkg create generates its own name based on manifest)
file(GLOB _new_pkg_files "${_pkg_dir}/Sunshine-*.pkg")
if(NOT _new_pkg_files)
message(FATAL_ERROR "FreeBSD post-build: pkg-static create succeeded but no package file was generated")
endif()
list(GET _new_pkg_files 0 _generated_pkg)
# Replace the original package with the newly created one
file(REMOVE "${_pkg_file}")
file(RENAME "${_generated_pkg}" "${_pkg_file}")
message(STATUS "FreeBSD post-build: Successfully processed package")
# Clean up
file(REMOVE_RECURSE "${_tmp_dir}")

View file

@ -34,10 +34,34 @@ else()
endif() endif()
endif() endif()
# RPM specific
set(CPACK_RPM_PACKAGE_LICENSE "GPLv3")
# FreeBSD specific
set(CPACK_FREEBSD_PACKAGE_MAINTAINER "${CPACK_PACKAGE_VENDOR}")
set(CPACK_FREEBSD_PACKAGE_ORIGIN "misc/${CPACK_PACKAGE_NAME}")
set(CPACK_FREEBSD_PACKAGE_LICENSE "GPLv3")
# Post install # Post install
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/postinst") set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/postinst")
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/postinst") set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/postinst")
# FreeBSD post install/deinstall scripts
if(FREEBSD)
# Note: CPack's FreeBSD generator does NOT natively support install/deinstall scripts
# like CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA or CPACK_RPM_POST_INSTALL_SCRIPT_FILE.
# This is a known limitation of the CPack FREEBSD generator.
#
# Workaround: Use CPACK_POST_BUILD_SCRIPTS to extract the generated .pkg file,
# add the install/deinstall scripts, and repack the package. This ensures they are
# recognized as package control scripts rather than installed files.
set(CPACK_FREEBSD_PACKAGE_SCRIPTS
"${SUNSHINE_SOURCE_ASSETS_DIR}/bsd/misc/+POST_INSTALL"
"${SUNSHINE_SOURCE_ASSETS_DIR}/bsd/misc/+PRE_DEINSTALL"
)
list(APPEND CPACK_POST_BUILD_SCRIPTS "${CMAKE_MODULE_PATH}/packaging/freebsd_custom_cpack.cmake")
endif()
# Apply setcap for RPM # Apply setcap for RPM
# https://github.com/coreos/rpm-ostree/discussions/5036#discussioncomment-10291071 # https://github.com/coreos/rpm-ostree/discussions/5036#discussioncomment-10291071
set(CPACK_RPM_USER_FILELIST "%caps(cap_sys_admin+p) ${SUNSHINE_EXECUTABLE_PATH}") set(CPACK_RPM_USER_FILELIST "%caps(cap_sys_admin+p) ${SUNSHINE_EXECUTABLE_PATH}")
@ -77,6 +101,15 @@ set(CPACK_RPM_PACKAGE_REQUIRES "\
openssl >= 3.0.2, \ openssl >= 3.0.2, \
pulseaudio-libs >= 10.0, \ pulseaudio-libs >= 10.0, \
which >= 2.21") which >= 2.21")
list(APPEND CPACK_FREEBSD_PACKAGE_DEPS
audio/opus
ftp/curl
devel/libevdev
net/avahi
x11/libX11
net/miniupnpc
security/openssl
)
if(NOT BOOST_USE_STATIC) if(NOT BOOST_USE_STATIC)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "\ set(CPACK_DEBIAN_PACKAGE_DEPENDS "\
@ -91,6 +124,9 @@ if(NOT BOOST_USE_STATIC)
boost-locale >= ${Boost_VERSION}, \ boost-locale >= ${Boost_VERSION}, \
boost-log >= ${Boost_VERSION}, \ boost-log >= ${Boost_VERSION}, \
boost-program-options >= ${Boost_VERSION}") boost-program-options >= ${Boost_VERSION}")
list(APPEND CPACK_FREEBSD_PACKAGE_DEPS
devel/boost-libs
)
endif() endif()
# This should automatically figure out dependencies on packages # This should automatically figure out dependencies on packages
@ -142,6 +178,10 @@ if(${SUNSHINE_TRAY} STREQUAL 1)
set(CPACK_RPM_PACKAGE_REQUIRES "\ set(CPACK_RPM_PACKAGE_REQUIRES "\
${CPACK_RPM_PACKAGE_REQUIRES}, \ ${CPACK_RPM_PACKAGE_REQUIRES}, \
libappindicator-gtk3 >= 12.10.0") libappindicator-gtk3 >= 12.10.0")
list(APPEND CPACK_FREEBSD_PACKAGE_DEPS
devel/libayatana-appindicator
devel/libnotify
)
endif() endif()
# desktop file # desktop file

View file

@ -26,6 +26,14 @@ and applications to Sunshine.
> process is killed. > process is killed.
@tabs{ @tabs{
@tab{FreeBSD | <!-- -->
\| Field \| Value \|
\|------------------------------\|------------------------------------------------------\|
\| Application Name \| @code{}Steam Big Picture@endcode \|
\| Command Preporations -> Undo \| @code{}setsid steam steam://close/bigpicture@endcode \|
\| Detached Commands \| @code{}setsid steam steam://open/bigpicture@endcode \|
\| Image \| @code{}steam.png@endcode \|
}
@tab{Linux | <!-- --> @tab{Linux | <!-- -->
\| Field \| Value \| \| Field \| Value \|
\|------------------------------\|------------------------------------------------------\| \|------------------------------\|------------------------------------------------------\|
@ -97,6 +105,12 @@ and applications to Sunshine.
#### URI #### URI
@tabs{ @tabs{
@tab{FreeBSD | <!-- -->
\| Field \| Value \|
\|-------------------\|------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Detached Commands \| @code{}setsid steam steam://rungameid/464920@endcode \|
}
@tab{Linux | <!-- --> @tab{Linux | <!-- -->
\| Field \| Value \| \| Field \| Value \|
\|-------------------\|------------------------------------------------------\| \|-------------------\|------------------------------------------------------\|
@ -119,6 +133,13 @@ and applications to Sunshine.
#### Binary (w/ working directory #### Binary (w/ working directory
@tabs{ @tabs{
@tab{FreeBSD | <!-- -->
\| Field \| Value \|
\|-------------------\|--------------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Command \| @code{}MarsSteam@endcode \|
\| Working Directory \| @code{}~/.steam/steam/SteamApps/common/Survivng Mars@endcode \|
}
@tab{Linux | <!-- --> @tab{Linux | <!-- -->
\| Field \| Value \| \| Field \| Value \|
\|-------------------\|--------------------------------------------------------------\| \|-------------------\|--------------------------------------------------------------\|
@ -144,6 +165,12 @@ and applications to Sunshine.
#### Binary (w/o working directory) #### Binary (w/o working directory)
@tabs{ @tabs{
@tab{FreeBSD | <!-- -->
\| Field \| Value \|
\|-------------------\|------------------------------------------------------------------------\|
\| Application Name \| @code{}Surviving Mars@endcode \|
\| Command \| @code{}~/.steam/steam/SteamApps/common/Survivng Mars/MarsSteam@endcode \|
}
@tab{Linux | <!-- --> @tab{Linux | <!-- -->
\| Field \| Value \| \| Field \| Value \|
\|-------------------\|------------------------------------------------------------------------\| \|-------------------\|------------------------------------------------------------------------\|

View file

@ -14,6 +14,39 @@ It is recommended to use one of the following compilers:
### Dependencies ### Dependencies
#### FreeBSD
> [!CAUTION]
> Sunshine support for FreeBSD is experimental and may be incomplete or not work as expected
##### Install dependencies
```sh
pkg install -y \
audio/opus \
audio/pulseaudio \
devel/cmake \
devel/evdev-proto \
devel/git \
devel/libayatana-appindicator \
devel/libevdev \
devel/libnotify \
devel/ninja \
devel/pkgconf \
ftp/curl \
graphics/libdrm \
graphics/wayland \
multimedia/libva \
net/miniupnpc \
ports-mgmt/pkg \
security/openssl \
shells/bash \
www/npm \
x11/libX11 \
x11/libxcb \
x11/libXfixes \
x11/libXrandr \
x11/libXtst
```
#### Linux #### Linux
Dependencies vary depending on the distribution. You can reference our Dependencies vary depending on the distribution. You can reference our
[linux_build.sh](https://github.com/LizardByte/Sunshine/blob/master/scripts/linux_build.sh) script for a list of [linux_build.sh](https://github.com/LizardByte/Sunshine/blob/master/scripts/linux_build.sh) script for a list of
@ -135,6 +168,11 @@ ninja -C build
### Package ### Package
@tabs{ @tabs{
@tab{FreeBSD | @tabs{
@tab{pkg | ```bash
cpack -G FREEBSD --config ./build/CPackConfig.cmake
```}
}}
@tab{Linux | @tabs{ @tab{Linux | @tabs{
@tab{deb | ```bash @tab{deb | ```bash
cpack -G DEB --config ./build/CPackConfig.cmake cpack -G DEB --config ./build/CPackConfig.cmake

View file

@ -26,6 +26,7 @@ location by modifying the configuration file.
| OS | Location | | OS | Location |
|---------|-------------------------------------------------| |---------|-------------------------------------------------|
| Docker | @code{}/config@endcode | | Docker | @code{}/config@endcode |
| FreeBSD | @code{}~/.config/sunshine@endcode |
| Linux | @code{}~/.config/sunshine@endcode | | Linux | @code{}~/.config/sunshine@endcode |
| macOS | @code{}~/.config/sunshine@endcode | | macOS | @code{}~/.config/sunshine@endcode |
| Windows | @code{}%ProgramFiles%\\Sunshine\\config@endcode | | Windows | @code{}%ProgramFiles%\\Sunshine\\config@endcode |
@ -339,12 +340,12 @@ editing the `conf` file in a text editor. Use the examples as reference.
<tr> <tr>
<td>ds5</td> <td>ds5</td>
<td>DualShock 5 controller (PS5) <td>DualShock 5 controller (PS5)
@note{This option applies to Linux only.}</td> @note{This option applies to FreeBSD and Linux only.}</td>
</tr> </tr>
<tr> <tr>
<td>switch</td> <td>switch</td>
<td>Switch Pro controller <td>Switch Pro controller
@note{This option applies to Linux only.}</td> @note{This option applies to FreeBSD and Linux only.}</td>
</tr> </tr>
<tr> <tr>
<td>x360</td> <td>x360</td>
@ -354,7 +355,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
<tr> <tr>
<td>xone</td> <td>xone</td>
<td>Xbox One controller <td>Xbox One controller
@note{This option applies to Linux only.}</td> @note{This option applies to FreeBSD and Linux only.}</td>
</tr> </tr>
</table> </table>
@ -735,14 +736,14 @@ editing the `conf` file in a text editor. Use the examples as reference.
@tip{To find the name of the audio sink follow these instructions. @tip{To find the name of the audio sink follow these instructions.
<br> <br>
<br> <br>
**Linux + pulseaudio:** **FreeBSD/Linux + pulseaudio:**
<br> <br>
@code{} @code{}
pacmd list-sinks | grep "name:" pacmd list-sinks | grep "name:"
@endcode @endcode
<br> <br>
<br> <br>
**Linux + pipewire:** **FreeBSD/Linux + pipewire:**
<br> <br>
@code{} @code{}
pactl info | grep Source pactl info | grep Source
@ -776,7 +777,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
<td colspan="2">Sunshine will select the default audio device.</td> <td colspan="2">Sunshine will select the default audio device.</td>
</tr> </tr>
<tr> <tr>
<td>Example (Linux)</td> <td>Example (FreeBSD/Linux)</td>
<td colspan="2">@code{} <td colspan="2">@code{}
audio_sink = alsa_output.pci-0000_09_00.3.analog-stereo audio_sink = alsa_output.pci-0000_09_00.3.analog-stereo
@endcode</td> @endcode</td>
@ -883,7 +884,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
@tip{To find the appropriate values follow these instructions. @tip{To find the appropriate values follow these instructions.
<br> <br>
<br> <br>
**Linux + VA-API:** **FreeBSD/Linux + VA-API:**
<br> <br>
Unlike with *amdvce* and *nvenc*, it doesn't matter if video encoding is done on a different GPU. Unlike with *amdvce* and *nvenc*, it doesn't matter if video encoding is done on a different GPU.
@code{} @code{}
@ -913,7 +914,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
<td colspan="2">Sunshine will select the default video card.</td> <td colspan="2">Sunshine will select the default video card.</td>
</tr> </tr>
<tr> <tr>
<td>Example (Linux)</td> <td>Example (FreeBSD/Linux)</td>
<td colspan="2">@code{} <td colspan="2">@code{}
adapter_name = /dev/dri/renderD128 adapter_name = /dev/dri/renderD128
@endcode</td> @endcode</td>
@ -936,7 +937,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
@tip{To find the appropriate values follow these instructions. @tip{To find the appropriate values follow these instructions.
<br> <br>
<br> <br>
**Linux:** **FreeBSD/Linux:**
<br> <br>
During Sunshine startup, you should see the list of detected displays: During Sunshine startup, you should see the list of detected displays:
@code{} @code{}
@ -1021,7 +1022,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
<td colspan="2">Sunshine will select the default display.</td> <td colspan="2">Sunshine will select the default display.</td>
</tr> </tr>
<tr> <tr>
<td>Example (Linux)</td> <td>Example (FreeBSD/Linux)</td>
<td colspan="2">@code{} <td colspan="2">@code{}
output_name = 0 output_name = 0
@endcode</td> @endcode</td>
@ -2034,7 +2035,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
<tr> <tr>
<td>x11</td> <td>x11</td>
<td>Uses XCB. This is the slowest and most CPU intensive so should be avoided if possible. <td>Uses XCB. This is the slowest and most CPU intensive so should be avoided if possible.
@note{Applies to Linux only.}</td> @note{Applies to FreeBSD and Linux only.}</td>
</tr> </tr>
<tr> <tr>
<td>ddx</td> <td>ddx</td>
@ -2083,7 +2084,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
</tr> </tr>
<tr> <tr>
<td>vaapi</td> <td>vaapi</td>
<td>Use Linux VA-API (AMD, Intel)</td> <td>Use VA-API (AMD, Intel)</td>
</tr> </tr>
<tr> <tr>
<td>software</td> <td>software</td>

View file

@ -8,7 +8,7 @@ and release artifacts may be missing when merging changes on a faster cadence.
## Binaries ## Binaries
Binaries of Sunshine are created for each release. They are available for Linux, macOS, and Windows. Binaries of Sunshine are created for each release. They are available for FreeBSD, Linux, macOS, and Windows.
Binaries can be found in the [latest release][latest-release]. Binaries can be found in the [latest release][latest-release].
> [!NOTE] > [!NOTE]
@ -28,7 +28,28 @@ and [ghcr.io](https://github.com/orgs/LizardByte/packages?repo_name=sunshine).
See [Docker](../DOCKER_README.md) for more information. See [Docker](../DOCKER_README.md) for more information.
### FreeBSD
#### Install
1. Download the appropriate package for your architecture
| Architecture | Package |
|---------------|----------------------------------------------------------------------------------------------------------------------------------------|
| amd64/x86_64 | [Sunshine-FreeBSD-14.3-amd64.pkg](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-FreeBSD-14.3-amd64.pkg) |
| arm64/aarch64 | [Sunshine-FreeBSD-14.3-aarch64.pkg](https://github.com/LizardByte/Sunshine/releases/latest/download/Sunshine-FreeBSD-14.3-aarch64.pkg) |
2. Open terminal and run the following command.
```sh
sudo pkg install ./Sunshine-FreeBSD-14.3-{arch}.pkg
```
#### Uninstall
```sh
sudo pkg delete Sunshine
```
### Linux ### Linux
**CUDA Compatibility** **CUDA Compatibility**
CUDA is used for NVFBC capture. CUDA is used for NVFBC capture.
@ -380,6 +401,22 @@ overflow menu. Different versions of Windows may provide slightly different step
## Initial Setup ## Initial Setup
After installation, some initial setup is required. After installation, some initial setup is required.
### FreeBSD
#### Virtual Input Devices
> [!IMPORTANT]
> To use virtual input devices (keyboard, mouse, gamepads), you must add your user to the `input` group.
The installation process creates the `input` group and configures permissions for `/dev/uinput`.
To allow your user to create virtual input devices, run:
```bash
pw groupmod input -m $USER
```
After adding yourself to the group, log out and log back in for the changes to take effect.
### Linux ### Linux
#### KMS Capture #### KMS Capture
@ -542,7 +579,16 @@ All shortcuts start with `Ctrl+Alt+Shift`, just like Moonlight.
instead it simply starts a stream. If you removed it and would like to get it back, just add a new application with instead it simply starts a stream. If you removed it and would like to get it back, just add a new application with
the name "Desktop" and "desktop.png" as the image path. the name "Desktop" and "desktop.png" as the image path.
* For the Linux flatpak you must prepend commands with `flatpak-spawn --host`. * For the Linux flatpak you must prepend commands with `flatpak-spawn --host`.
* If inputs (mouse, keyboard, gamepads...) aren't working after connecting, add the user running sunshine to the `input` group. * If inputs (mouse, keyboard, gamepads...) aren't working after connecting:
* On FreeBSD/Linux, add the user running sunshine to the `input` group.
* The FreeBSD version of Sunshine is missing some features that are present on Linux.
The following are known limitations.
* Only X11 and Wayland capture are supported
* DualSense/DS5 emulation is not available due to missing uhid features
### HDR Support ### HDR Support
Streaming HDR content is officially supported on Windows hosts and experimentally supported for Linux hosts. Streaming HDR content is officially supported on Windows hosts and experimentally supported for Linux hosts.

View file

@ -10,14 +10,22 @@
// standard includes // standard includes
#include <fstream> #include <fstream>
#include <iomanip>
#include <iostream> #include <iostream>
#include <sstream>
// platform includes // platform includes
#include <arpa/inet.h> #include <arpa/inet.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <ifaddrs.h> #include <ifaddrs.h>
#include <netinet/in.h>
#include <netinet/udp.h> #include <netinet/udp.h>
#include <pwd.h> #include <pwd.h>
#include <sys/socket.h>
#ifdef __FreeBSD__
#include <net/if_dl.h> // For sockaddr_dl, LLADDR, and AF_LINK
#endif
// lib includes // lib includes
#include <boost/asio/ip/address.hpp> #include <boost/asio/ip/address.hpp>
@ -41,6 +49,16 @@
#define SUNSHINE_GNUC_EXTENSION #define SUNSHINE_GNUC_EXTENSION
#endif #endif
#ifndef SOL_IP
#define SOL_IP IPPROTO_IP
#endif
#ifndef SOL_IPV6
#define SOL_IPV6 IPPROTO_IPV6
#endif
#ifndef SOL_UDP
#define SOL_UDP IPPROTO_UDP
#endif
using namespace std::literals; using namespace std::literals;
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace bp = boost::process::v1; namespace bp = boost::process::v1;
@ -214,6 +232,40 @@ namespace platf {
std::string get_mac_address(const std::string_view &address) { std::string get_mac_address(const std::string_view &address) {
auto ifaddrs = get_ifaddrs(); auto ifaddrs = get_ifaddrs();
#ifdef __FreeBSD__
// On FreeBSD, we need to find the interface name first, then look for its AF_LINK entry
std::string interface_name;
for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) {
interface_name = pos->ifa_name;
break;
}
}
if (!interface_name.empty()) {
// Find the AF_LINK entry for this interface to get MAC address
for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
if (pos->ifa_addr && pos->ifa_addr->sa_family == AF_LINK &&
interface_name == pos->ifa_name) {
auto sdl = (struct sockaddr_dl *) pos->ifa_addr;
auto mac = (unsigned char *) LLADDR(sdl);
// Format MAC address as XX:XX:XX:XX:XX:XX
std::ostringstream mac_stream;
mac_stream << std::hex << std::setfill('0');
for (int i = 0; i < sdl->sdl_alen; i++) {
if (i > 0) {
mac_stream << ':';
}
mac_stream << std::setw(2) << (int) mac[i];
}
return mac_stream.str();
}
}
}
#else
// On Linux, read MAC address from sysfs
for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) { for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) { if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) {
std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + "/address"); std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + "/address");
@ -224,6 +276,7 @@ namespace platf {
} }
} }
} }
#endif
BOOST_LOG(warning) << "Unable to find MAC address for "sv << address; BOOST_LOG(warning) << "Unable to find MAC address for "sv << address;
return "00:00:00:00:00:00"s; return "00:00:00:00:00:00"s;
@ -377,7 +430,12 @@ namespace platf {
} }
union { union {
#ifdef IP_PKTINFO
char buf[CMSG_SPACE(sizeof(uint16_t)) + std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))]; char buf[CMSG_SPACE(sizeof(uint16_t)) + std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
#elif defined(IP_SENDSRCADDR)
// FreeBSD uses IP_SENDSRCADDR with struct in_addr instead of IP_PKTINFO with struct in_pktinfo
char buf[CMSG_SPACE(sizeof(uint16_t)) + std::max(CMSG_SPACE(sizeof(struct in_addr)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
#endif
struct cmsghdr alignment; struct cmsghdr alignment;
} cmbuf = {}; // Must be zeroed for CMSG_NXTHDR() } cmbuf = {}; // Must be zeroed for CMSG_NXTHDR()
@ -403,6 +461,7 @@ namespace platf {
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
} else { } else {
#ifdef IP_PKTINFO
struct in_pktinfo pktInfo; struct in_pktinfo pktInfo;
struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
@ -415,6 +474,18 @@ namespace platf {
pktinfo_cm->cmsg_type = IP_PKTINFO; pktinfo_cm->cmsg_type = IP_PKTINFO;
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
#elif defined(IP_SENDSRCADDR)
// FreeBSD uses IP_SENDSRCADDR with struct in_addr instead of IP_PKTINFO
struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
struct in_addr src_addr = saddr_v4.sin_addr;
cmbuflen += CMSG_SPACE(sizeof(src_addr));
pktinfo_cm->cmsg_level = IPPROTO_IP;
pktinfo_cm->cmsg_type = IP_SENDSRCADDR;
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(src_addr));
memcpy(CMSG_DATA(pktinfo_cm), &src_addr, sizeof(src_addr));
#endif
} }
auto const max_iovs_per_msg = send_info.payload_buffers.size() + (send_info.headers ? 1 : 0); auto const max_iovs_per_msg = send_info.payload_buffers.size() + (send_info.headers ? 1 : 0);
@ -507,8 +578,8 @@ namespace platf {
{ {
// If GSO is not supported, use sendmmsg() instead. // If GSO is not supported, use sendmmsg() instead.
struct mmsghdr msgs[send_info.block_count]; std::vector<struct mmsghdr> msgs(send_info.block_count);
struct iovec iovs[send_info.block_count * (send_info.headers ? 2 : 1)]; std::vector<struct iovec> iovs(send_info.block_count * (send_info.headers ? 2 : 1));
int iov_idx = 0; int iov_idx = 0;
for (size_t i = 0; i < send_info.block_count; i++) { for (size_t i = 0; i < send_info.block_count; i++) {
msgs[i].msg_len = 0; msgs[i].msg_len = 0;
@ -584,7 +655,12 @@ namespace platf {
} }
union { union {
#ifdef IP_PKTINFO
char buf[std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))]; char buf[std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
#elif defined(IP_SENDSRCADDR)
// FreeBSD uses IP_SENDSRCADDR with struct in_addr instead of IP_PKTINFO with struct in_pktinfo
char buf[std::max(CMSG_SPACE(sizeof(struct in_addr)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
#endif
struct cmsghdr alignment; struct cmsghdr alignment;
} cmbuf; } cmbuf;
@ -608,6 +684,7 @@ namespace platf {
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
} else { } else {
#ifdef IP_PKTINFO
struct in_pktinfo pktInfo; struct in_pktinfo pktInfo;
struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
@ -620,6 +697,18 @@ namespace platf {
pktinfo_cm->cmsg_type = IP_PKTINFO; pktinfo_cm->cmsg_type = IP_PKTINFO;
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
#elif defined(IP_SENDSRCADDR)
// FreeBSD uses IP_SENDSRCADDR with struct in_addr instead of IP_PKTINFO
struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
struct in_addr src_addr = saddr_v4.sin_addr;
cmbuflen += CMSG_SPACE(sizeof(src_addr));
pktinfo_cm->cmsg_level = IPPROTO_IP;
pktinfo_cm->cmsg_type = IP_SENDSRCADDR;
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(src_addr));
memcpy(CMSG_DATA(pktinfo_cm), &src_addr, sizeof(src_addr));
#endif
} }
struct iovec iovs[2]; struct iovec iovs[2];
@ -753,6 +842,10 @@ namespace platf {
// reset SO_PRIORITY back to 0. // reset SO_PRIORITY back to 0.
// //
// 6 is the highest priority that can be used without SYS_CAP_ADMIN. // 6 is the highest priority that can be used without SYS_CAP_ADMIN.
#ifndef SO_PRIORITY
// FreeBSD doesn't support SO_PRIORITY, so we skip this
BOOST_LOG(debug) << "SO_PRIORITY not supported on this platform, skipping traffic priority setting";
#else
int priority = data_type == qos_data_type_e::audio ? 6 : 5; int priority = data_type == qos_data_type_e::audio ? 6 : 5;
if (setsockopt(sockfd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) == 0) { if (setsockopt(sockfd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) == 0) {
// Reset SO_PRIORITY to 0 when QoS is disabled // Reset SO_PRIORITY to 0 when QoS is disabled
@ -760,6 +853,7 @@ namespace platf {
} else { } else {
BOOST_LOG(error) << "Failed to set SO_PRIORITY: "sv << errno; BOOST_LOG(error) << "Failed to set SO_PRIORITY: "sv << errno;
} }
#endif
return std::make_unique<qos_t>(sockfd, reset_options); return std::make_unique<qos_t>(sockfd, reset_options);
} }

View file

@ -520,7 +520,12 @@ namespace stream {
// for other communications to the client. This is necessary to ensure // for other communications to the client. This is necessary to ensure
// proper routing on multi-homed hosts. // proper routing on multi-homed hosts.
auto local_address = platf::from_sockaddr((sockaddr *) &peer->localAddress.address); auto local_address = platf::from_sockaddr((sockaddr *) &peer->localAddress.address);
session_p->localAddress = boost::asio::ip::make_address(local_address); try {
session_p->localAddress = boost::asio::ip::make_address(local_address);
} catch (const boost::system::system_error &e) {
BOOST_LOG(error) << "boost::system::system_error in address parsing: " << e.what() << " (code: " << e.code() << ")"sv;
throw;
}
BOOST_LOG(debug) << "Control local address ["sv << local_address << ']'; BOOST_LOG(debug) << "Control local address ["sv << local_address << ']';
BOOST_LOG(debug) << "Control peer address ["sv << peer_addr << ':' << peer_port << ']'; BOOST_LOG(debug) << "Control peer address ["sv << peer_addr << ':' << peer_port << ']';

View file

@ -13,7 +13,7 @@
#define TRAY_ICON_PLAYING WEB_DIR "images/sunshine-playing.ico" #define TRAY_ICON_PLAYING WEB_DIR "images/sunshine-playing.ico"
#define TRAY_ICON_PAUSING WEB_DIR "images/sunshine-pausing.ico" #define TRAY_ICON_PAUSING WEB_DIR "images/sunshine-pausing.ico"
#define TRAY_ICON_LOCKED WEB_DIR "images/sunshine-locked.ico" #define TRAY_ICON_LOCKED WEB_DIR "images/sunshine-locked.ico"
#elif defined(__linux__) || defined(linux) || defined(__linux) #elif defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
#define TRAY_ICON SUNSHINE_TRAY_PREFIX "-tray" #define TRAY_ICON SUNSHINE_TRAY_PREFIX "-tray"
#define TRAY_ICON_PLAYING SUNSHINE_TRAY_PREFIX "-playing" #define TRAY_ICON_PLAYING SUNSHINE_TRAY_PREFIX "-playing"
#define TRAY_ICON_PAUSING SUNSHINE_TRAY_PREFIX "-pausing" #define TRAY_ICON_PAUSING SUNSHINE_TRAY_PREFIX "-pausing"

View file

@ -897,7 +897,7 @@ namespace video {
H264_ONLY | PARALLEL_ENCODING | ALWAYS_REPROBE | YUV444_SUPPORT H264_ONLY | PARALLEL_ENCODING | ALWAYS_REPROBE | YUV444_SUPPORT
}; };
#ifdef __linux__ #if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
encoder_t vaapi { encoder_t vaapi {
"vaapi"sv, "vaapi"sv,
std::make_unique<encoder_platform_formats_avcodec>( std::make_unique<encoder_platform_formats_avcodec>(
@ -1032,7 +1032,7 @@ namespace video {
&quicksync, &quicksync,
&amdvce, &amdvce,
#endif #endif
#ifdef __linux__ #if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
&vaapi, &vaapi,
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__

View file

@ -222,7 +222,7 @@ namespace video {
extern encoder_t quicksync; extern encoder_t quicksync;
#endif #endif
#ifdef __linux__ #if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
extern encoder_t vaapi; extern encoder_t vaapi;
#endif #endif

View file

@ -0,0 +1,64 @@
#!/bin/sh
# FreeBSD post-install script for Sunshine
# This script sets up the necessary permissions for virtual input devices
echo "Configuring permissions for virtual input devices..."
# Create the 'input' group if it doesn't exist
if ! pw groupshow input >/dev/null 2>&1; then
echo "Creating 'input' group..."
pw groupadd input
if [ $? -eq 0 ]; then
echo "Successfully created 'input' group."
else
echo "Warning: Failed to create 'input' group. You may need to create it manually."
fi
else
echo "'input' group already exists."
fi
# Set permissions on /dev/uinput if it exists
if [ -e /dev/uinput ]; then
echo "Setting permissions on /dev/uinput..."
chown root:input /dev/uinput
chmod 660 /dev/uinput
echo "Permissions set on /dev/uinput."
else
echo "Note: /dev/uinput does not exist. It will be created when needed."
fi
# Create devfs rules for persistent permissions
echo "Creating devfs rules for persistent permissions..."
DEVFS_RULESET_FILE="/etc/devfs.rules"
RULESET_NUM=47989
# Check if our rules already exist
if ! grep -q "\[sunshine=$RULESET_NUM\]" "$DEVFS_RULESET_FILE" 2>/dev/null; then
cat >> "$DEVFS_RULESET_FILE" << EOF
[sunshine=$RULESET_NUM]
add path 'uinput' mode 0660 group input
EOF
echo "Devfs rules added to $DEVFS_RULESET_FILE"
else
echo "Devfs rules already exist in $DEVFS_RULESET_FILE"
fi
# Apply the devfs ruleset immediately (without waiting for reboot)
echo "Applying devfs ruleset to current system..."
if [ -e /dev/uinput ]; then
devfs -m /dev rule -s $RULESET_NUM apply
fi
echo ""
echo "Post-installation configuration complete!"
echo ""
echo "IMPORTANT: To use virtual input devices (keyboard, mouse, gamepads),"
echo "you must add your user to the 'input' group:"
echo ""
echo " pw groupmod input -m \$USER"
echo ""
echo "After adding yourself to the group, log out and log back in for the"
echo "changes to take effect."
echo ""

View file

@ -0,0 +1,32 @@
#!/bin/sh
# FreeBSD pre-deinstall script for Sunshine
# This script cleans up configuration added during installation
echo "Cleaning up Sunshine configuration..."
# Remove devfs rules
DEVFS_RULESET_FILE="/etc/devfs.rules"
RULESET_NUM=47989
# Remove rules from /etc/devfs.rules
if [ -f "$DEVFS_RULESET_FILE" ]; then
if grep -q "\[sunshine=$RULESET_NUM\]" "$DEVFS_RULESET_FILE"; then
echo "Removing devfs rules from $DEVFS_RULESET_FILE..."
# Remove the [sunshine=47989] section and its rules (match the section header and the next line)
sed -i.bak '/^\[sunshine='"$RULESET_NUM"'\]$/,/^add path.*uinput/d' "$DEVFS_RULESET_FILE"
echo "Devfs rules removed from file."
fi
fi
echo "Removing devfs ruleset from memory..."
devfs rule -s $RULESET_NUM delset 2>/dev/null || true
# Note: We intentionally do NOT:
# - Remove the 'input' group (other software may use it)
echo "Cleanup complete."
echo ""
echo "NOTE: The 'input' group has not been removed as other software may use it."
echo "If you wish to remove it manually, run: pw groupdel input"
echo ""

View file

@ -12,6 +12,10 @@ const props = defineProps({
<slot name="windows"></slot> <slot name="windows"></slot>
</template> </template>
<template v-if="$slots.freebsd && platform === 'freebsd'">
<slot name="freebsd"></slot>
</template>
<template v-if="$slots.linux && platform === 'linux'"> <template v-if="$slots.linux && platform === 'linux'">
<slot name="linux"></slot> <slot name="linux"></slot>
</template> </template>

View file

@ -331,7 +331,7 @@
<div class="form-text" v-if="platform === 'windows'"><b>{{ $t('apps.env_qres_example') }}</b> <div class="form-text" v-if="platform === 'windows'"><b>{{ $t('apps.env_qres_example') }}</b>
<pre>cmd /C &lt;{{ $t('apps.env_qres_path') }}&gt;\QRes.exe /X:%SUNSHINE_CLIENT_WIDTH% /Y:%SUNSHINE_CLIENT_HEIGHT% /R:%SUNSHINE_CLIENT_FPS%</pre> <pre>cmd /C &lt;{{ $t('apps.env_qres_path') }}&gt;\QRes.exe /X:%SUNSHINE_CLIENT_WIDTH% /Y:%SUNSHINE_CLIENT_HEIGHT% /R:%SUNSHINE_CLIENT_FPS%</pre>
</div> </div>
<div class="form-text" v-else-if="platform === 'linux'"><b>{{ $t('apps.env_xrandr_example') }}</b> <div class="form-text" v-else-if="platform === 'freebsd' || platform === 'linux'"><b>{{ $t('apps.env_xrandr_example') }}</b>
<pre>sh -c "xrandr --output HDMI-1 --mode \"${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}\" --rate ${SUNSHINE_CLIENT_FPS}"</pre> <pre>sh -c "xrandr --output HDMI-1 --mode \"${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}\" --rate ${SUNSHINE_CLIENT_FPS}"</pre>
</div> </div>
<div class="form-text" v-else-if="platform === 'macos'"><b>{{ $t('apps.env_displayplacer_example') }}</b> <div class="form-text" v-else-if="platform === 'macos'"><b>{{ $t('apps.env_displayplacer_example') }}</b>

View file

@ -305,7 +305,7 @@
return el.id !== "vt" && el.id !== "vaapi"; return el.id !== "vt" && el.id !== "vaapi";
}); });
} }
if (this.platform === "linux") { if (this.platform === "freebsd" || this.platform === "linux") {
this.tabs = this.tabs.filter((el) => { this.tabs = this.tabs.filter((el) => {
return el.id !== "amd" && el.id !== "qsv" && el.id !== "vt"; return el.id !== "amd" && el.id !== "qsv" && el.id !== "vt";
}); });

View file

@ -64,6 +64,10 @@ const config = ref(props.config)
<select id="capture" class="form-select" v-model="config.capture"> <select id="capture" class="form-select" v-model="config.capture">
<option value="">{{ $t('_common.autodetect') }}</option> <option value="">{{ $t('_common.autodetect') }}</option>
<PlatformLayout :platform="platform"> <PlatformLayout :platform="platform">
<template #freebsd>
<option value="wlr">wlroots</option>
<option value="x11">X11</option>
</template>
<template #linux> <template #linux>
<option value="nvfbc">NvFBC</option> <option value="nvfbc">NvFBC</option>
<option value="wlr">wlroots</option> <option value="wlr">wlroots</option>
@ -90,6 +94,9 @@ const config = ref(props.config)
<option value="quicksync">Intel QuickSync</option> <option value="quicksync">Intel QuickSync</option>
<option value="amdvce">AMD AMF/VCE</option> <option value="amdvce">AMD AMF/VCE</option>
</template> </template>
<template #freebsd>
<option value="vaapi">VA-API</option>
</template>
<template #linux> <template #linux>
<option value="nvenc">NVIDIA NVENC</option> <option value="nvenc">NVIDIA NVENC</option>
<option value="vaapi">VA-API</option> <option value="vaapi">VA-API</option>

View file

@ -30,6 +30,10 @@ const config = ref(props.config)
<template #windows> <template #windows>
<pre>tools\audio-info.exe</pre> <pre>tools\audio-info.exe</pre>
</template> </template>
<template #freebsd>
<pre>pacmd list-sinks | grep "name:"</pre>
<pre>pactl info | grep Source</pre>
</template>
<template #linux> <template #linux>
<pre>pacmd list-sinks | grep "name:"</pre> <pre>pacmd list-sinks | grep "name:"</pre>
<pre>pactl info | grep Source</pre> <pre>pactl info | grep Source</pre>

View file

@ -28,6 +28,11 @@ const config = ref(props.config)
<option value="auto">{{ $t('_common.auto') }}</option> <option value="auto">{{ $t('_common.auto') }}</option>
<PlatformLayout :platform="platform"> <PlatformLayout :platform="platform">
<template #freebsd>
<option value="switch">{{ $t("config.gamepad_switch") }}</option>
<option value="xone">{{ $t("config.gamepad_xone") }}</option>
</template>
<template #linux> <template #linux>
<option value="ds5">{{ $t("config.gamepad_ds5") }}</option> <option value="ds5">{{ $t("config.gamepad_ds5") }}</option>
<option value="switch">{{ $t("config.gamepad_switch") }}</option> <option value="switch">{{ $t("config.gamepad_switch") }}</option>

View file

@ -23,6 +23,16 @@ const config = ref(props.config)
{{ $t('config.adapter_name_desc_windows') }}<br> {{ $t('config.adapter_name_desc_windows') }}<br>
<pre>tools\dxgi-info.exe</pre> <pre>tools\dxgi-info.exe</pre>
</template> </template>
<template #freebsd>
{{ $t('config.adapter_name_desc_linux_1') }}<br>
<pre>ls /dev/dri/renderD* # {{ $t('config.adapter_name_desc_linux_2') }}</pre>
<pre>
vainfo --display drm --device /dev/dri/renderD129 | \
grep -E "((VAProfileH264High|VAProfileHEVCMain|VAProfileHEVCMain10).*VAEntrypointEncSlice)|Driver version"
</pre>
{{ $t('config.adapter_name_desc_linux_3') }}<br>
<i>VAProfileH264High : VAEntrypointEncSlice</i>
</template>
<template #linux> <template #linux>
{{ $t('config.adapter_name_desc_linux_1') }}<br> {{ $t('config.adapter_name_desc_linux_1') }}<br>
<pre>ls /dev/dri/renderD* # {{ $t('config.adapter_name_desc_linux_2') }}</pre> <pre>ls /dev/dri/renderD* # {{ $t('config.adapter_name_desc_linux_2') }}</pre>

View file

@ -241,6 +241,8 @@ function addRemappingEntry() {
</div> </div>
</div> </div>
</template> </template>
<template #freebsd>
</template>
<template #linux> <template #linux>
</template> </template>
<template #macos> <template #macos>

View file

@ -30,6 +30,16 @@ const outputNamePlaceholder = (props.platform === 'windows') ? '{de9bb7e2-186e-5
<b>&nbsp;&nbsp;}</b> <b>&nbsp;&nbsp;}</b>
</pre> </pre>
</template> </template>
<template #freebsd>
<pre style="white-space: pre-line;">
Info: Detecting displays
Info: Detected display: DVI-D-0 (id: 0) connected: false
Info: Detected display: HDMI-0 (id: 1) connected: true
Info: Detected display: DP-0 (id: 2) connected: true
Info: Detected display: DP-1 (id: 3) connected: false
Info: Detected display: DVI-D-1 (id: 4) connected: false
</pre>
</template>
<template #linux> <template #linux>
<pre style="white-space: pre-line;"> <pre style="white-space: pre-line;">
Info: Detecting displays Info: Detecting displays

View file

@ -126,6 +126,12 @@ namespace test_utils {
#define IS_MACOS false #define IS_MACOS false
#endif #endif
#ifdef __FreeBSD__
#define IS_FREEBSD true
#else
#define IS_FREEBSD false
#endif
struct PlatformTestSuite: testing::Test { struct PlatformTestSuite: testing::Test {
static void SetUpTestSuite() { static void SetUpTestSuite() {
ASSERT_FALSE(platf_deinit); ASSERT_FALSE(platf_deinit);

View file

@ -13,7 +13,7 @@ struct MouseHIDTest: PlatformTestSuite, testing::WithParamInterface<util::point_
// the alternative `platf::abs_mouse` method seem to work better during tests, // the alternative `platf::abs_mouse` method seem to work better during tests,
// but I'm not sure about real work // but I'm not sure about real work
GTEST_SKIP() << "TODO Windows"; GTEST_SKIP() << "TODO Windows";
#elif __linux__ #elif defined(__linux__) || defined(__FreeBSD__)
// TODO: Inputtino waiting https://github.com/games-on-whales/inputtino/issues/6 is resolved. // TODO: Inputtino waiting https://github.com/games-on-whales/inputtino/issues/6 is resolved.
GTEST_SKIP() << "TODO Inputtino"; GTEST_SKIP() << "TODO Inputtino";
#endif #endif
@ -88,7 +88,7 @@ TEST_P(MouseHIDTest, AbsMoveInputTest) {
65535, 65535,
65535 65535
}; };
#elif __linux__ #elif defined(__linux__) || defined(__FreeBSD__)
platf::touch_port_t abs_port { platf::touch_port_t abs_port {
0, 0,
0, 0,

View file

@ -32,7 +32,7 @@ INSTANTIATE_TEST_SUITE_P(
&video::amdvce, &video::amdvce,
&video::quicksync, &video::quicksync,
#endif #endif
#ifdef __linux__ #if defined(__linux__) || defined(__FreeBSD__)
&video::vaapi, &video::vaapi,
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__