Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e048481
Initial plan
Copilot Mar 4, 2026
6c08902
PHASE-32: Backend Integration files for KDE Plasma client
Copilot Mar 4, 2026
c82ef10
PHASE-24/29: VR latency optimizer, integration tests, mobile UI compo…
Copilot Mar 4, 2026
95f591c
feat: PHASE-33 & PHASE-34 — code standards, quality tools, production…
Copilot Mar 4, 2026
a228172
Implement PHASE-32 through PHASE-34, complete PHASE-24 and PHASE-29; …
Copilot Mar 4, 2026
b6cfcf7
Add PHASE-35 through PHASE-38: Plugin System, Audio DSP, Multi-Client…
Copilot Mar 4, 2026
2867124
Add PHASE-39 through PHASE-42: Quality Intelligence, Relay, Session P…
Copilot Mar 4, 2026
f3f360d
Add PHASE-43 through PHASE-46: Stream Scheduler, HLS Output, Analytic…
Copilot Mar 4, 2026
be8c19a
Add PHASE-47 through PHASE-50: Watermarking, ABR Controller, Metadata…
Copilot Mar 4, 2026
164a883
Add PHASE-51 through PHASE-54: PLC, Rate Limiter, FRC, Config Seriali…
Copilot Mar 4, 2026
bd0e7ba
Add PHASE-55 through PHASE-58: Session Handshake, Congestion, Keyfram…
Copilot Mar 4, 2026
006b459
Add PHASE-59 through PHASE-62: Mixer, Bandwidth Probe, Reorder Buffer…
Copilot Mar 5, 2026
564b7ed
Add PHASE-63 through PHASE-66: Health Monitor, FEC Codec, Clock Sync,…
Copilot Mar 5, 2026
944a328
Add PHASE-67 through PHASE-70: Frame Rate Controller, Output Registry…
Copilot Mar 5, 2026
169434a
Add PHASE-71 through PHASE-74: Timestamp Sync, Session Limiter, Tag S…
Copilot Mar 5, 2026
575025c
Add PHASE-75 through PHASE-78: Event Bus, Chunk Splitter, Priority Qu…
Copilot Mar 5, 2026
c848e2b
Add PHASE-79 through PHASE-82: Flow Controller, Metrics Exporter, Sig…
Copilot Mar 5, 2026
bc87731
Add PHASE-83 through PHASE-86: Integration Tests, Client Audits, Comm…
Copilot Mar 5, 2026
d258315
plan: PHASE-93–97 rootstream_core library, client session API, KDE Vi…
Copilot Mar 5, 2026
55c65a7
Changes before error encountered
Copilot Mar 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 100
BreakBeforeBraces: Attach
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
SortIncludes: true
IncludeBlocks: Regroup
26 changes: 26 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
Checks: >
clang-diagnostic-*,
clang-analyzer-*,
-clang-analyzer-alpha.*,
bugprone-*,
cert-*,
-cert-err58-cpp,
misc-*,
-misc-non-private-member-variables-in-classes,
modernize-*,
-modernize-use-trailing-return-type,
performance-*,
portability-*,
readability-*,
-readability-magic-numbers,
-readability-braces-around-statements
WarningsAsErrors: ''
HeaderFilterRegex: '(src|clients)/'
CheckOptions:
- key: readability-identifier-naming.VariableCase
value: lower_case
- key: readability-identifier-naming.FunctionCase
value: lower_case
- key: readability-identifier-naming.ClassCase
value: CamelCase
240 changes: 134 additions & 106 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -302,202 +302,228 @@ set(WINDOWS_SOURCES
src/service.c
)

# =============================================================================
# PHASE-93: rootstream_core — Linkable Static Library
#
# ALL protocol/crypto/decode/encode/network/audio logic lives here.
# Executables (rootstream, rstr-player, rootstream-client) and the KDE client
# link this single library instead of re-compiling the same sources N times.
#
# WHY A LIBRARY?
# --------------
# Before PHASE-93, each executable compiled every source file independently.
# That meant:
# 1. Duplicate compilation = longer build times.
# 2. The KDE client could not link the real backend — it had no library to
# link against, forcing it to stub or duplicate all protocol logic.
# 3. Adding src/client_session.c (PHASE-94) would have to be added to every
# executable's source list separately.
#
# With rootstream_core STATIC:
# - One authoritative compiled object for all streaming logic.
# - KDE client links it via add_subdirectory (see clients/kde-plasma-client).
# - Tests can link specific subsets without re-compiling the world.
# - PUBLIC include/ means any downstream target automatically gets the
# correct include paths with no extra configuration.
# =============================================================================

# =============================================================================
# Targets
# =============================================================================

if(UNIX AND NOT APPLE)
# Linux: Full host + client
add_executable(rootstream
src/main.c
# ── rootstream_core: the shared backend library ────────────────────────
#
# Contains everything EXCEPT:
# - src/main.c (CLI entry point — stays in rootstream exe)
# - tools/rstr-player.c (player tool entry point)
# - src/tray*.c (tray UI — only needed by the host executable)
#
# display_sdl2.c is included because service_run_client() (which is in
# service.c → rootstream_core) still calls display_init() for the SDL
# fallback path. Once PHASE-94 is complete, service_run_client() becomes
# a thin wrapper and display_sdl2.c can be moved to the executable.
add_library(rootstream_core STATIC
${COMMON_SOURCES}
${LINUX_SOURCES}
${PLATFORM_SOURCES}
src/client_session.c
)

target_include_directories(rootstream PRIVATE
${CMAKE_SOURCE_DIR}/include
${SDL2_INCLUDE_DIRS}
${DRM_INCLUDE_DIRS}
${GTK3_INCLUDE_DIRS}
# PUBLIC include dir: any target that links rootstream_core automatically
# receives the correct include path for rootstream.h and
# rootstream_client_session.h without needing a separate
# target_include_directories call.
target_include_directories(rootstream_core
PUBLIC
${CMAKE_SOURCE_DIR}/include
PRIVATE
${CMAKE_SOURCE_DIR}/src
${SDL2_INCLUDE_DIRS}
${DRM_INCLUDE_DIRS}
${GTK3_INCLUDE_DIRS}
)

target_link_libraries(rootstream PRIVATE
# Link all system libraries against rootstream_core (PUBLIC where needed
# by downstream, PRIVATE for internal use only).
target_link_libraries(rootstream_core PUBLIC
${SDL2_LIBRARIES}
${DRM_LIBRARIES}
m pthread
)

# Conditional libraries
if(unofficial-sodium_FOUND)
target_link_libraries(rootstream PRIVATE unofficial-sodium::sodium)
target_link_libraries(rootstream_core PUBLIC unofficial-sodium::sodium)
else()
target_link_libraries(rootstream PRIVATE ${SODIUM_LIBRARIES})
target_include_directories(rootstream PRIVATE ${SODIUM_INCLUDE_DIRS})
target_link_libraries(rootstream_core PUBLIC ${SODIUM_LIBRARIES})
target_include_directories(rootstream_core PUBLIC ${SODIUM_INCLUDE_DIRS})
endif()

if(Opus_FOUND)
target_link_libraries(rootstream PRIVATE Opus::opus)
target_link_libraries(rootstream_core PUBLIC Opus::opus)
else()
target_link_libraries(rootstream PRIVATE ${OPUS_LIBRARIES})
target_include_directories(rootstream PRIVATE ${OPUS_INCLUDE_DIRS})
target_link_libraries(rootstream_core PUBLIC ${OPUS_LIBRARIES})
target_include_directories(rootstream_core PUBLIC ${OPUS_INCLUDE_DIRS})
endif()

if(VAAPI_FOUND)
target_link_libraries(rootstream PRIVATE ${VAAPI_LIBRARIES})
target_include_directories(rootstream PRIVATE ${VAAPI_INCLUDE_DIRS})
target_link_libraries(rootstream_core PUBLIC ${VAAPI_LIBRARIES})
target_include_directories(rootstream_core PUBLIC ${VAAPI_INCLUDE_DIRS})
endif()

if(ALSA_FOUND)
target_link_libraries(rootstream PRIVATE ${ALSA_LIBRARIES})
target_include_directories(rootstream PRIVATE ${ALSA_INCLUDE_DIRS})
target_link_libraries(rootstream_core PRIVATE ${ALSA_LIBRARIES})
target_include_directories(rootstream_core PRIVATE ${ALSA_INCLUDE_DIRS})
endif()

if(PULSEAUDIO_FOUND)
target_link_libraries(rootstream PRIVATE ${PULSEAUDIO_LIBRARIES})
target_include_directories(rootstream PRIVATE ${PULSEAUDIO_INCLUDE_DIRS})
target_link_libraries(rootstream_core PRIVATE ${PULSEAUDIO_LIBRARIES})
target_include_directories(rootstream_core PRIVATE ${PULSEAUDIO_INCLUDE_DIRS})
endif()

if(PIPEWIRE_FOUND)
target_link_libraries(rootstream PRIVATE ${PIPEWIRE_LIBRARIES})
target_include_directories(rootstream PRIVATE ${PIPEWIRE_INCLUDE_DIRS})
target_link_libraries(rootstream_core PRIVATE ${PIPEWIRE_LIBRARIES})
target_include_directories(rootstream_core PRIVATE ${PIPEWIRE_INCLUDE_DIRS})
endif()

if(NOT HEADLESS AND GTK3_FOUND)
target_link_libraries(rootstream PRIVATE ${GTK3_LIBRARIES})
target_include_directories(rootstream PRIVATE ${GTK3_INCLUDE_DIRS})
target_link_libraries(rootstream_core PRIVATE ${GTK3_LIBRARIES})
target_include_directories(rootstream_core PRIVATE ${GTK3_INCLUDE_DIRS})
endif()

if(AVAHI_FOUND)
target_link_libraries(rootstream PRIVATE ${AVAHI_LIBRARIES})
target_include_directories(rootstream PRIVATE ${AVAHI_INCLUDE_DIRS})
target_link_libraries(rootstream_core PRIVATE ${AVAHI_LIBRARIES})
target_include_directories(rootstream_core PRIVATE ${AVAHI_INCLUDE_DIRS})
endif()

if(X11_FOUND)
target_link_libraries(rootstream PRIVATE ${X11_LIBRARIES})
target_include_directories(rootstream PRIVATE ${X11_INCLUDE_DIRS})
target_link_libraries(rootstream_core PRIVATE ${X11_LIBRARIES})
target_include_directories(rootstream_core PRIVATE ${X11_INCLUDE_DIRS})
endif()

if(QRENCODE_FOUND)
target_link_libraries(rootstream PRIVATE ${QRENCODE_LIBRARIES})
target_include_directories(rootstream PRIVATE ${QRENCODE_INCLUDE_DIRS})
target_link_libraries(rootstream_core PRIVATE ${QRENCODE_LIBRARIES})
target_include_directories(rootstream_core PRIVATE ${QRENCODE_INCLUDE_DIRS})
endif()

if(PNG_FOUND)
target_link_libraries(rootstream PRIVATE ${PNG_LIBRARIES})
target_include_directories(rootstream PRIVATE ${PNG_INCLUDE_DIRS})
target_link_libraries(rootstream_core PRIVATE ${PNG_LIBRARIES})
target_include_directories(rootstream_core PRIVATE ${PNG_INCLUDE_DIRS})
endif()

if(NCURSES_FOUND)
target_link_libraries(rootstream PRIVATE ${NCURSES_LIBRARIES})
target_include_directories(rootstream PRIVATE ${NCURSES_INCLUDE_DIRS})
target_link_libraries(rootstream_core PRIVATE ${NCURSES_LIBRARIES})
target_include_directories(rootstream_core PRIVATE ${NCURSES_INCLUDE_DIRS})
endif()

if(FFMPEG_FOUND)
target_link_libraries(rootstream PRIVATE ${FFMPEG_LIBRARIES})
target_include_directories(rootstream PRIVATE ${FFMPEG_INCLUDE_DIRS})
target_link_libraries(rootstream_core PUBLIC ${FFMPEG_LIBRARIES})
target_include_directories(rootstream_core PUBLIC ${FFMPEG_INCLUDE_DIRS})
endif()

# Recording player tool
add_executable(rstr-player
tools/rstr-player.c
src/recording.c
src/vaapi_decoder.c
src/display_sdl2.c
src/network.c
src/network_tcp.c
src/network_reconnect.c
src/packet_validate.c
src/crypto.c
src/config.c
src/input.c
src/opus_codec.c
src/audio_playback.c
src/audio_playback_pulse.c
src/audio_playback_pipewire.c
src/audio_playback_dummy.c
src/latency.c
${PLATFORM_SOURCES}
# ── rootstream: host + client CLI executable ───────────────────────────
#
# src/main.c is the ONLY source here — all real logic is in rootstream_core.
# This keeps the executable thin and makes it easy to verify that no
# protocol logic accidentally lives only in main.c.
add_executable(rootstream
src/main.c
)

target_include_directories(rstr-player PRIVATE
${CMAKE_SOURCE_DIR}/include
${SDL2_INCLUDE_DIRS}
)
target_link_libraries(rootstream PRIVATE rootstream_core)

target_link_libraries(rstr-player PRIVATE
${SDL2_LIBRARIES}
${VAAPI_LIBRARIES}
${ALSA_LIBRARIES}
${SODIUM_LIBRARIES}
${AVAHI_LIBRARIES}
m pthread
# ── rstr-player: recording playback tool ──────────────────────────────
#
# Links rootstream_core for all decode/display/audio logic.
# Only the player tool's own main (tools/rstr-player.c) is compiled here.
add_executable(rstr-player
tools/rstr-player.c
)

if(PULSEAUDIO_FOUND)
target_link_libraries(rstr-player PRIVATE ${PULSEAUDIO_LIBRARIES})
target_include_directories(rstr-player PRIVATE ${PULSEAUDIO_INCLUDE_DIRS})
endif()
target_link_libraries(rstr-player PRIVATE rootstream_core)

if(PIPEWIRE_FOUND)
target_link_libraries(rstr-player PRIVATE ${PIPEWIRE_LIBRARIES})
target_include_directories(rstr-player PRIVATE ${PIPEWIRE_INCLUDE_DIRS})
endif()

if(Opus_FOUND)
target_link_libraries(rstr-player PRIVATE Opus::opus)
else()
target_link_libraries(rstr-player PRIVATE ${OPUS_LIBRARIES})
endif()
# KDE Plasma client is built via its own CMakeLists.txt which uses
# add_subdirectory(../.. rootstream_build) to pull in rootstream_core.
# See: clients/kde-plasma-client/CMakeLists.txt (PHASE-93.2)

endif()

if(WIN32)
# Windows: Client only
add_executable(rootstream-client WIN32
src/main_client.c
# ── Windows: rootstream_core_win (Windows backend library) ────────────
#
# Same principle as the Linux library: all backend logic in one library,
# thin executable on top.
add_library(rootstream_core_win STATIC
${COMMON_SOURCES}
${WINDOWS_SOURCES}
${PLATFORM_SOURCES}
src/client_session.c
)

target_include_directories(rootstream-client PRIVATE
${CMAKE_SOURCE_DIR}/include
target_include_directories(rootstream_core_win
PUBLIC
${CMAKE_SOURCE_DIR}/include
PRIVATE
${CMAKE_SOURCE_DIR}/src
)

# Windows system libraries
target_link_libraries(rootstream-client PRIVATE
ws2_32 # Winsock
mfplat # Media Foundation
mfuuid # Media Foundation GUIDs
mf # Media Foundation
ole32 # COM
d3d11 # Direct3D 11
dxgi # DXGI
target_link_libraries(rootstream_core_win PUBLIC
ws2_32 mfplat mfuuid mf ole32 d3d11 dxgi
)

# vcpkg dependencies
if(unofficial-sodium_FOUND)
target_link_libraries(rootstream-client PRIVATE unofficial-sodium::sodium)
target_link_libraries(rootstream_core_win PUBLIC unofficial-sodium::sodium)
endif()

if(SDL2_FOUND)
target_link_libraries(rootstream-client PRIVATE SDL2::SDL2 SDL2::SDL2main)
target_link_libraries(rootstream_core_win PUBLIC SDL2::SDL2 SDL2::SDL2main)
endif()

if(Opus_FOUND)
target_link_libraries(rootstream-client PRIVATE Opus::opus)
target_link_libraries(rootstream_core_win PUBLIC Opus::opus)
endif()

# Windows client executable — thin, links the library
add_executable(rootstream-client WIN32
src/main_client.c
)

target_link_libraries(rootstream-client PRIVATE rootstream_core_win)
endif()

# =============================================================================
# Installation
# =============================================================================

if(UNIX AND NOT APPLE)
install(TARGETS rootstream_core ARCHIVE DESTINATION lib)
install(TARGETS rootstream RUNTIME DESTINATION bin)
install(TARGETS rstr-player RUNTIME DESTINATION bin)

# Public headers (needed by downstream consumers like KDE client)
install(FILES
include/rootstream.h
include/rootstream_client_session.h
DESTINATION include/rootstream)

# Desktop file
install(FILES assets/rootstream.desktop
DESTINATION share/applications)
Expand All @@ -508,6 +534,7 @@ if(UNIX AND NOT APPLE)
endif()

if(WIN32)
install(TARGETS rootstream_core_win ARCHIVE DESTINATION lib)
install(TARGETS rootstream-client RUNTIME DESTINATION bin)
endif()

Expand All @@ -517,7 +544,8 @@ endif()

enable_testing()

# Unit tests
# Unit tests — link against rootstream_core to avoid re-listing source files
# and to ensure the tests exercise the same compiled code as the executables.
add_executable(test_crypto tests/unit/test_crypto.c src/crypto.c ${PLATFORM_SOURCES})
target_include_directories(test_crypto PRIVATE ${CMAKE_SOURCE_DIR}/include)
if(unofficial-sodium_FOUND)
Expand Down
Loading
Loading