diff --git a/.agents/skills/cpp-meson-build/SKILL.md b/.agents/skills/cpp-meson-build/SKILL.md index cc82f49..4cb2aab 100644 --- a/.agents/skills/cpp-meson-build/SKILL.md +++ b/.agents/skills/cpp-meson-build/SKILL.md @@ -83,6 +83,18 @@ meson test -C build --print-errorlogs meson configure build ``` +## Formatting + +Run `clang-format` on every C++ source or header file before considering a +change complete. The project config is `.clang-format` at the repository root. + +```sh +find include src tests -type f \( -name '*.cpp' -o -name '*.h' \) -exec clang-format -i {} + +``` + +If `clang-format` is not available, match the existing style in the file being +edited. + ## References - `meson.build` (project root) diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..d8af23f --- /dev/null +++ b/.clang-format @@ -0,0 +1,22 @@ +--- +Language: Cpp +BasedOnStyle: LLVM +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +ColumnLimit: 120 +BreakBeforeBraces: Attach +AllowShortFunctionsOnASingleLine: Empty +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +IndentCaseLabels: true +SpaceBeforeParens: ControlStatements +PointerAlignment: Left +SortIncludes: true +IncludeBlocks: Preserve +ReflowComments: true +AlignTrailingComments: true +BinPackArguments: false +BinPackParameters: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true diff --git a/AGENTS.md b/AGENTS.md index 27ac2bc..330eb43 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -253,6 +253,9 @@ reason in the plan. - Keep headers minimal and free of unnecessary includes; forward-declare where possible. - Format code with the configured formatter (`clang-format` if available). +- Run `clang-format` on every C++ source or header file before considering a + change complete; do not commit unformatted code. +- The project formatter config is `.clang-format` at the repository root. ### Error handling diff --git a/include/screencast/app/cli.h b/include/screencast/app/cli.h index d83e3d7..128a156 100644 --- a/include/screencast/app/cli.h +++ b/include/screencast/app/cli.h @@ -7,19 +7,19 @@ namespace sc { struct SendCommand { - std::string_view target = "monitor"; // monitor, window, region - std::string_view peer_address; // optional + std::string_view target = "monitor"; // monitor, window, region + std::string_view peer_address; // optional int bitrate_kbps = 4000; }; struct ReceiveCommand { - std::string_view peer_address; // optional + std::string_view peer_address; // optional int local_rtp_port = 5004; }; using Command = std::variant; // Parse command line arguments. Prints usage and returns std::nullopt on error. -std::optional parse_cli ( int argc, const char *argv[] ); +std::optional parse_cli(int argc, const char* argv[]); -} // namespace sc +} // namespace sc diff --git a/include/screencast/app/pipeline.h b/include/screencast/app/pipeline.h index 4a2c1c0..e2cc19b 100644 --- a/include/screencast/app/pipeline.h +++ b/include/screencast/app/pipeline.h @@ -3,8 +3,6 @@ #include "screencast/capture/capture.h" #include "screencast/codec/decoder.h" #include "screencast/codec/encoder.h" -#include "screencast/network/discovery.h" -#include "screencast/network/signaling.h" #include "screencast/network/transport.h" #include "screencast/render/renderer.h" @@ -30,11 +28,11 @@ struct ReceiverPipelineConfig { // Sender pipeline: capture → encode → packetize → RTP/UDP. class SenderPipeline { public: - explicit SenderPipeline ( SenderPipelineConfig config ); - ~SenderPipeline (); + explicit SenderPipeline(SenderPipelineConfig config); + ~SenderPipeline(); - bool start (); - void stop (); + bool start(); + void stop(); private: class Impl; @@ -44,15 +42,15 @@ class SenderPipeline { // Receiver pipeline: RTP/UDP → depacketize → decode → render. class ReceiverPipeline { public: - explicit ReceiverPipeline ( ReceiverPipelineConfig config ); - ~ReceiverPipeline (); + explicit ReceiverPipeline(ReceiverPipelineConfig config); + ~ReceiverPipeline(); - bool start (); - void stop (); + bool start(); + void stop(); private: class Impl; std::unique_ptr impl_; }; -} // namespace sc +} // namespace sc diff --git a/include/screencast/network/discovery.h b/include/screencast/network/discovery.h index 6d4c053..e376cb7 100644 --- a/include/screencast/network/discovery.h +++ b/include/screencast/network/discovery.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -14,22 +15,22 @@ struct DiscoveredPeer { class DiscoveryService { public: - using PeerCallback = std::function; + using PeerCallback = std::function; - virtual ~DiscoveryService () = default; + virtual ~DiscoveryService() = default; // Announce this peer on the LAN. - virtual bool announce ( const std::string &service_name, uint16_t signaling_port ) = 0; + virtual bool announce(const std::string& service_name, uint16_t signaling_port) = 0; // Browse for peers; callback is invoked for each newly found service. - virtual bool browse ( PeerCallback on_peer ) = 0; + virtual bool browse(PeerCallback on_peer) = 0; - virtual void stop () = 0; + virtual void stop() = 0; }; class DiscoveryFactory { public: - static std::unique_ptr create_avahi (); + static std::unique_ptr create_avahi(); }; -} // namespace sc +} // namespace sc diff --git a/include/screencast/network/rtp_packet.h b/include/screencast/network/rtp_packet.h index a78c9f3..f18d360 100644 --- a/include/screencast/network/rtp_packet.h +++ b/include/screencast/network/rtp_packet.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -13,21 +14,21 @@ struct RtpHeader { bool extension = false; uint8_t csrc_count = 0; bool marker = false; - uint7_t payload_type = 96; // dynamic + uint8_t payload_type = 96; // dynamic uint16_t sequence_number = 0; uint32_t timestamp = 0; uint32_t ssrc = 0; - bool serialize ( std::span out ) const noexcept; - static std::optional parse ( std::span in ) noexcept; + bool serialize(std::span out) const noexcept; + static std::optional parse(std::span in) noexcept; }; struct RtpPacket { RtpHeader header; std::vector payload; - std::vector serialize () const; - static std::optional parse ( std::span in ) noexcept; + std::vector serialize() const; + static std::optional parse(std::span in) noexcept; }; -} // namespace sc +} // namespace sc diff --git a/include/screencast/network/signaling.h b/include/screencast/network/signaling.h index ccaf2af..ac89575 100644 --- a/include/screencast/network/signaling.h +++ b/include/screencast/network/signaling.h @@ -2,9 +2,11 @@ #include "screencast/network/transport.h" +#include #include #include #include +#include namespace sc { @@ -28,20 +30,20 @@ using SignalingMessage = std::variant; class SignalingChannel { public: - using MessageCallback = std::function; + using MessageCallback = std::function; - virtual ~SignalingChannel () = default; + virtual ~SignalingChannel() = default; - virtual bool connect ( const Endpoint &server ) = 0; - virtual void send ( const SignalingMessage &message ) = 0; - virtual void on_message ( MessageCallback callback ) = 0; - virtual void disconnect () = 0; + virtual bool connect(const Endpoint& server) = 0; + virtual void send(const SignalingMessage& message) = 0; + virtual void on_message(MessageCallback callback) = 0; + virtual void disconnect() = 0; }; class SignalingFactory { public: - static std::unique_ptr create_websocket_client (); - static std::unique_ptr create_websocket_server ( uint16_t port ); + static std::unique_ptr create_websocket_client(); + static std::unique_ptr create_websocket_server(uint16_t port); }; -} // namespace sc +} // namespace sc diff --git a/include/screencast/network/transport.h b/include/screencast/network/transport.h index 415b70c..cba846a 100644 --- a/include/screencast/network/transport.h +++ b/include/screencast/network/transport.h @@ -17,27 +17,27 @@ struct Endpoint { // UDP transport for RTP packets. Owned by the sender or receiver pipeline. class RtpTransport { public: - using ReceiveCallback = std::function; + using ReceiveCallback = std::function; - virtual ~RtpTransport () = default; + virtual ~RtpTransport() = default; // Bind locally and start the receive loop. Callback is invoked on the // transport's thread. - virtual bool start ( const Endpoint &local_endpoint, ReceiveCallback on_receive ) = 0; + virtual bool start(const Endpoint& local_endpoint, ReceiveCallback on_receive) = 0; // Send a packet to the configured peer. - virtual bool send ( const RtpPacket &packet ) = 0; + virtual bool send(const RtpPacket& packet) = 0; // Set the peer endpoint dynamically (e.g. after signaling). - virtual void set_peer ( const Endpoint &peer ) = 0; + virtual void set_peer(const Endpoint& peer) = 0; // Stop the transport and close sockets. - virtual void stop () = 0; + virtual void stop() = 0; }; class RtpTransportFactory { public: - static std::unique_ptr create (); + static std::unique_ptr create(); }; -} // namespace sc +} // namespace sc diff --git a/include/screencast/utils/clock.h b/include/screencast/utils/clock.h index 8cfc629..ba629be 100644 --- a/include/screencast/utils/clock.h +++ b/include/screencast/utils/clock.h @@ -6,14 +6,14 @@ namespace sc { // RTP clock rate for video is 90 kHz. Convert a monotonic nanosecond timestamp // into a 32-bit RTP timestamp. Overflows wrap around intentionally. -constexpr uint32_t rtp_timestamp_from_ns ( uint64_t nanoseconds ) noexcept { +constexpr uint32_t rtp_timestamp_from_ns(uint64_t nanoseconds) noexcept { // (nanoseconds * 90_000) / 1_000_000_000 // Simplify: 90_000 / 1_000_000_000 = 9 / 100_000 - return static_cast ( ( nanoseconds * 9ULL ) / 100'000ULL ); + return static_cast((nanoseconds * 9ULL) / 100'000ULL); } -static_assert ( rtp_timestamp_from_ns ( 0 ) == 0 ); -static_assert ( rtp_timestamp_from_ns ( 1'000'000'000ULL ) == 90'000 ); -static_assert ( rtp_timestamp_from_ns ( 500'000'000ULL ) == 45'000 ); +static_assert(rtp_timestamp_from_ns(0) == 0); +static_assert(rtp_timestamp_from_ns(1'000'000'000ULL) == 90'000); +static_assert(rtp_timestamp_from_ns(500'000'000ULL) == 45'000); -} // namespace sc +} // namespace sc diff --git a/meson.build b/meson.build index 591f259..c80950f 100644 --- a/meson.build +++ b/meson.build @@ -7,10 +7,7 @@ project('screen_cast', 'cpp', 'buildtype=release', ]) -# Public and private include directories -inc = include_directories('include') - -# Core public headers / include dependency defined in src/meson.build. +# Public and private include directories are declared in `src/meson.build`. subdir('src') # Tests diff --git a/tests/utils/test_clock.cpp b/tests/utils/test_clock.cpp index 77e5860..7378673 100644 --- a/tests/utils/test_clock.cpp +++ b/tests/utils/test_clock.cpp @@ -3,18 +3,18 @@ #include #include -int main () { - assert ( sc::rtp_timestamp_from_ns ( 0 ) == 0u ); - assert ( sc::rtp_timestamp_from_ns ( 1'000'000'000ULL ) == 90'000u ); - assert ( sc::rtp_timestamp_from_ns ( 500'000'000ULL ) == 45'000u ); - assert ( sc::rtp_timestamp_from_ns ( 16'666'667ULL ) == 1'500u ); +int main() { + assert(sc::rtp_timestamp_from_ns(0) == 0u); + assert(sc::rtp_timestamp_from_ns(1'000'000'000ULL) == 90'000u); + assert(sc::rtp_timestamp_from_ns(500'000'000ULL) == 45'000u); + assert(sc::rtp_timestamp_from_ns(16'666'667ULL) == 1'500u); // A multiple of the wrap period should wrap back to zero. // Period in ns = ceil(2^32 * 100_000 / 9) because the helper uses // integer floor division (ns * 9 / 100_000). - constexpr uint64_t period_ns = ( ( 1ULL << 32 ) * 100'000ULL + 9ULL - 1ULL ) / 9ULL; - static_assert ( sc::rtp_timestamp_from_ns ( period_ns ) == 0u ); - static_assert ( sc::rtp_timestamp_from_ns ( period_ns + 1'000'000'000ULL ) == 90'000u ); + constexpr uint64_t period_ns = ((1ULL << 32) * 100'000ULL + 9ULL - 1ULL) / 9ULL; + static_assert(sc::rtp_timestamp_from_ns(period_ns) == 0u); + static_assert(sc::rtp_timestamp_from_ns(period_ns + 1'000'000'000ULL) == 90'000u); return 0; }