build: add freebsd support

This commit is contained in:
ReenigneArcher 2025-07-04 20:16:38 -04:00
parent 8372c37046
commit d3a73d47c9
No known key found for this signature in database
GPG Key ID: 362B4EC0ADC98755
26 changed files with 527 additions and 29 deletions

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

@ -0,0 +1,256 @@
---
name: CI-FreeBSD
permissions:
contents: read
on:
workflow_call:
inputs:
release_commit:
required: true
type: string
release_version:
required: true
type: string
env:
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@v1.2.3
with:
arch: ${{ matrix.arch }}
cpu: ${{ steps.processor_count.outputs.PROCESSOR_COUNT }}
# 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/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
- name: Configure
env:
BRANCH: ${{ github.head_ref || github.ref_name }}
BUILD_VERSION: ${{ inputs.release_version }}
COMMIT: ${{ inputs.release_commit }}
shell: freebsd {0}
run: |
set -e
cd "${GITHUB_WORKSPACE}"
export BRANCH="${BRANCH}"
export BUILD_VERSION="${BUILD_VERSION}"
export COMMIT="${COMMIT}"
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 +MANIFEST
if: always()
shell: bash
working-directory: build/cpack_artifacts
run: cat ./_CPack_Packages/FreeBSD/FREEBSD/Sunshine/+MANIFEST
- 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@v4
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
@ -128,6 +136,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
@ -137,29 +146,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@v5 uses: actions/download-artifact@v5
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
@ -170,8 +203,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
@ -188,6 +221,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")

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

@ -34,6 +34,14 @@ 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")
@ -77,6 +85,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 +108,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, doesn't work with the current config # This should automatically figure out dependencies, doesn't work with the current config
@ -141,6 +161,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,27 @@ 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.
@ -522,6 +542,12 @@ All shortcuts start with `Ctrl+Alt+Shift`, just like Moonlight.
* The "Desktop" app works the same as any other application except it has no commands. It does not start an application, * The "Desktop" app works the same as any other application except it has no commands. It does not start an application,
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.
* 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
* DualSense5 emulation is limited due to missing uhid features
* 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, add the user running sunshine to the `input` group.

View File

@ -35,6 +35,30 @@
#include "src/platform/common.h" #include "src/platform/common.h"
#include "vaapi.h" #include "vaapi.h"
#ifdef __FreeBSD__
#include <netinet/in.h>
#include <sys/socket.h>
// Define constants that are missing in FreeBSD
#ifndef IP_PKTINFO // packet info for IPv4
#define IP_PKTINFO IP_RECVDSTADDR
#endif
#ifndef SOL_IP // socket level for IPv4
#define SOL_IP IPPROTO_IP
#endif
#ifndef SOL_IPV6 // socket level for IPv6
#define SOL_IPV6 IPPROTO_IPV6
#endif
#ifndef SO_PRIORITY // socket option for priority, disabled for FreeBSD
#define SO_PRIORITY -1
#endif
// Define in_pktinfo structure for IPv4 packet info
struct in_pktinfo {
struct in_addr ipi_addr;
struct in_addr ipi_spec_dst;
int ipi_ifindex;
};
#endif
#ifdef __GNUC__ #ifdef __GNUC__
#define SUNSHINE_GNUC_EXTENSION __extension__ #define SUNSHINE_GNUC_EXTENSION __extension__
#else #else
@ -507,8 +531,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;
@ -753,6 +777,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.
#ifdef __FreeBSD__
// FreeBSD doesn't support SO_PRIORITY, so we skip this
BOOST_LOG(debug) << "SO_PRIORITY not supported on FreeBSD, 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 +788,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

@ -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

@ -221,7 +221,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

@ -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>
@ -442,8 +442,8 @@
if (resp) { if (resp) {
fetch("./api/apps/" + id, { fetch("./api/apps/" + id, {
method: "DELETE", method: "DELETE",
headers: { headers: {
"Content-Type": "application/json" "Content-Type": "application/json"
}, },
}).then((r) => { }).then((r) => {
if (r.status === 200) document.location.reload(); if (r.status === 200) document.location.reload();

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,12 +28,18 @@ 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="ds5">{{ $t("config.gamepad_ds5") }}</option>
<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>
<option value="xone">{{ $t("config.gamepad_xone") }}</option> <option value="xone">{{ $t("config.gamepad_xone") }}</option>
</template> </template>
<template #windows> <template #windows>
<option value="ds4">{{ $t('config.gamepad_ds4') }}</option> <option value="ds4">{{ $t('config.gamepad_ds4') }}</option>
<option value="x360">{{ $t('config.gamepad_x360') }}</option> <option value="x360">{{ $t('config.gamepad_x360') }}</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__