From 52a5f74cd23b64066fa26b759c96b3586d5e39fc Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Tue, 14 Jul 2026 09:29:06 +0300 Subject: [PATCH] docs: merge coding conventions into CONTRIBUTING.md Consolidate the standalone `CONVENTION.md` into `CONTRIBUTING.md` so that all contributor guidelines live in a single, discoverable file. Added sections: - Language, build system, and formatting rules (Google style, clang-format) - Naming conventions (PascalCase types, snake_case_ members, lowerCamelCase methods) - Include ordering and header self-sufficiency requirements - Class/module design patterns (AModule hierarchy, SafeSignal threading) - JSON configuration and string handling guidelines - GTK/Glib and OpenGL patterns - Platform portability and RAII conventions - Thread-safety rules (GTK single-threaded, background thread marshalling) - Unsafe patterns to avoid (C-style casts, unbounded buffer copies) - Singleton lifetime guidance (store config by value, no dangling refs)docs: merge coding conventions into CONTRIBUTING.md --- CONTRIBUTING.md | 142 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc6fd972..5c57d93a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,3 +43,145 @@ add its man page and a line in [`.github/wiki/mapping.json`](.github/wiki/mappin - Build and test against the module(s) you touched. Have fun :) + +--- + +# Coding Conventions + +## 1. Language & Build +- **Standard**: C++20. +- **Build system**: Meson (`meson.build`). Project version is defined there. +- **Compiler flags**: Added via `add_project_arguments()` in Meson. Feature flags use `HAVE_*` / `WANT_*` prefixes (e.g. `-DHAVE_NIRI`, `-DHAVE_HYPRLAND`, `-DHAVE_LIBUDEV`). + +## 2. Formatting +- **Tool**: `.clang-format` is checked in. **Never bypass it.** +- **Style**: Google base style. +- **Indent**: 2 spaces. No tabs. +- **Column limit**: 100. +- **Braces**: K&R (opening brace on the same line). +- **Declaration alignment**: Disabled (`AlignConsecutiveDeclarations: false`). +- **Pointer/reference alignment**: Left (`const Json::Value& config`, `int* ptr`, not `int *ptr`). + +## 3. Naming + +### Files +- Match the primary exported class exactly: `AAppIconLabel.hpp`, `workspaces.cpp`, `backlight_backend.hpp`. +- Corresponding header and source should live in predictable paths: + - `include/.hpp` + - `src/.cpp` + +### Types +- **Classes / Structs**: `PascalCase`. + - Abstract base classes are prefixed with `A` (e.g., `AModule`, `ALabel`, `AIconLabel`, `AAppIconLabel`). +- **Enums / Enum classes**: `PascalCase` name. + - Enumerators: `UPPER_SNAKE_CASE` (e.g., `SCROLL_DIR::NONE`, `KillSignalAction::RELOAD`, `ChangeType::Increase`). +- **Concepts / Type aliases**: `PascalCase`. + +### Variables +- **Member variables**: `snake_case_` with a **trailing underscore**. + - Examples: `config_`, `bar_`, `label_`, `app_icon_size_`, `distance_scrolled_y_`, `on_updated_cb_`. +- **Function parameters & locals**: `snake_case` (no trailing underscore). + - Examples: `workspace_data`, `should_refresh`, `app_identifier`, `preferred_device`. +- **Static / constexpr constants**: `UPPER_SNAKE_CASE` or descriptive `kPascalCase`. + - Examples: `MODULE_CLASS`, `EPOLL_MAX_EVENTS`, `kExecFailureExitCode`. + +### Functions & Methods +- **Free functions**: `snake_case`. + - Examples: `sanitize_string()`, `rewrite_string()`, `get_total_memory()`, `best_device()`. +- **Class methods**: `lowerCamelCase`. + - Examples: `update()`, `tooltipEnabled()`, `handleScroll()`, `getScrollDir()`, `resolveFormat()`, `setBrightness()`. +- **Virtual overrides**: Mark with `override` (and `final` where applicable). Header signatures often use a trailing return type: + ```cpp + auto update() -> void override; + auto refresh(int should_refresh) -> void; + ``` + +### Namespaces +- All lowercase, nested by module path: + ```cpp + namespace waybar { } + namespace waybar::modules::niri { } + namespace waybar::util { } + ``` +- Close every namespace with a comment: + ```cpp + } // namespace waybar::modules::niri + ``` + +## 4. Includes & Headers +- Use `#pragma once` in all project headers. +- Include order in `.cpp` files: + 1. Corresponding header first. + 2. Blank line. + 3. External library headers (``, ``, ``, ``). + 4. Standard library headers (``, ``, ``). + 5. Blank line. + 6. Other project headers (`"util/..."`, `"modules/..."`). +- Do not use `using namespace` in headers. In `.cpp` files it is acceptable for narrow scopes (e.g., `using namespace std::literals::chrono_literals;`). +- Headers that expose standard-library types in their public interface (e.g. `std::chrono::milliseconds` as a return type or `std::vector` as a member) must `#include` the corresponding standard header directly. Do not rely on transitive includes from other headers. + +## 5. Class & Module Design + +### Base Class Patterns +- All UI modules ultimately derive from `AModule` (and often `ALabel` or `AIconLabel`). +- Accept configuration in constructors: + ```cpp + MyModule(const Json::Value& config, const std::string& name, const std::string& id, ...); + ``` + +### Signals & Threading +- Use `Glib::Dispatcher` (via `waybar::SafeSignal`) to marshal work to the GTK main thread. +- Use `sigc::signal` for normal GTK++ signals. +- If a scope must not be interrupted by `pthread_cancel`, guard it with `waybar::util::CancellationGuard`. + +### State / IPC +- Modules that talk to a compositor often implement a small `EventHandler` interface (`onEvent(...)`) and delegate to a singleton backend (e.g., `gIPC`). + +### RAII +- Prefer `std::unique_ptr` with custom deleters over raw `new/delete` for C-API resources (see `ScopedFd`, `UdevDeleter`, `UdevDeviceDeleter`, `ScopeGuard`). + +## 6. JSON Configuration +- Every module receives `const Json::Value& config` (usually as the first constructor argument). +- Always validate node type before reading: + ```cpp + if (config_["sort-by-id"].isBool()) { ... } + if (config.isMember("window-rewrite-default") && config["window-rewrite-default"].isString()) { ... } + ``` +- Use `waybar::util::JsonParser` if you need to pre-process JSON with non-standard escape sequences. + +## 7. String & UI Formatting +- Use `fmt::format` / `fmt::join` for all string composition. +- Use `fmt::dynamic_format_arg_store` when building arguments dynamically. +- Custom `fmt::formatter` specializations are allowed for domain types (e.g., `Glib::ustring`, project enums). +- Sanitize arbitrary text before inserting into Pango markup with `waybar::util::sanitize_string`. +- Use `waybar::util::rewriteString` for user-configurable regex rewrites. +- Truncate UTF-8 safely with `waybar::util::utf8_truncate` / `utf8_width`. + +## 8. Error Handling & Logging +- Use `spdlog` for all logging: + - `spdlog::error("Context: {}", e.what());` + - `spdlog::warn("Deprecated key '{}', prefer '{}'", old, replacement);` + - `spdlog::debug("State changed to {}", value);` +- Throw `std::runtime_error` (or similar) for fatal initialization failures that should bubble up to `main()`. + +## 9. GTK / Glib Patterns +- Prefer gtkmm-3.0 types (`Gtk::Button`, `Gtk::Label`, `Gdk::Pixbuf`, `Glib::RefPtr`, `Glib::ustring`) over raw C GTK APIs. +- Access the default icon theme through thread-safe wrappers if off the main thread (`DefaultGtkIconThemeWrapper`). +- Tooltips and labels should respect the module `tooltip` toggle (see `tooltipEnabled()` in `AModule`). + +## 10. Platform Portability +- Isolate platform-specific code in dedicated files (e.g., `linux.cpp`, `bsd.cpp`). +- Use preprocessor guards for platform differences (`#if defined(__FreeBSD__)`, `#if defined(HAVE_LIBNL)`). +- Keep the common interface in a shared header or base class. + +## 11. Thread Safety & Cross-Thread Communication +- GTK is strictly single-threaded. Never emit raw `sigc::signal` from background threads. +- Use `waybar::SafeSignal` to marshal events from worker threads to the GTK main loop. +- When a module manages background threads, use `std::mutex`, `std::recursive_mutex`, or atomic variables to protect shared state, and ensure the destructor joins or synchronizes with those threads before destroying resources. + +## 12. Unsafe Patterns to Avoid +- Do not use `strcpy`, `strcat`, or `sprintf` into fixed-size buffers (e.g. `char buf[PATH_MAX]`). Prefer `std::string`, `std::vector`, or `std::array` with bounds-safe operations. +- When passing a `std::vector` buffer to a C API that expects a mutable `char*` string, always ensure the buffer is null-terminated and clamp the written length to `size() - 1`. Never use `std::copy` from an unbounded source into a fixed-size buffer. + +## 13. Singleton Lifetime +- Singletons or objects with process-wide lifetime must not store references (`&`) or pointers to objects with shorter lifetime (e.g., configuration trees, GTK widgets, or bar instances) unless they are explicitly notified of destruction. Prefer storing configuration by value (`Json::Value`, `std::string`, etc.) if the singleton outlives the config loader.