Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Split the Linux system adapter into focused single-instance, notification,
tray, autostart, secure-storage, and crash/shutdown implementation units,
with one private XDG/D-Bus support core and no public API change.
- Make AppImage the signed Linux update payload when that format is enabled,
emit its SHA-256 sidecar, and add install-kind-aware update selection so
AppImage, portable, and package-manager installations cannot cross streams.
Expand Down
8 changes: 8 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ The Swift implementation is intentionally not the framework's specification. `do

The GTK4 host follows the Windows C++ contract but uses a Unix `socketpair` for the in-process RVT1 transport. Racket CS is linked statically, while the UI remains an ordinary GTK4 application. Backend completions are returned to GLib's main loop before widgets are touched.

Linux system integration is split by operating-system responsibility under
`platform/linux/system`: single-instance activation, notifications, tray,
autostart, secure storage, and crash/shutdown each have one implementation
unit. `system_services.cpp` only assembles public capability discovery and
activation arguments; `system_services_internal.*` is a private XDG, D-Bus,
and low-level I/O support layer. Native applications continue to include only
`system_services.hpp`.

## Runtime ownership

Rivet treats a Racket runtime as process-scoped infrastructure:
Expand Down
7 changes: 7 additions & 0 deletions examples/taskboard/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,14 @@ endif()
add_executable(RivetHost
src/main.cpp
"${RIVET_ROOT}/platform/linux/runtime/backend.cpp"
"${RIVET_ROOT}/platform/linux/system/autostart.cpp"
"${RIVET_ROOT}/platform/linux/system/crash_shutdown.cpp"
"${RIVET_ROOT}/platform/linux/system/notifications.cpp"
"${RIVET_ROOT}/platform/linux/system/secure_storage.cpp"
"${RIVET_ROOT}/platform/linux/system/single_instance.cpp"
"${RIVET_ROOT}/platform/linux/system/system_services.cpp"
"${RIVET_ROOT}/platform/linux/system/system_services_internal.cpp"
"${RIVET_ROOT}/platform/linux/system/tray.cpp"
"${RIVET_ROOT}/platform/linux/theme/theme.cpp"
"${RIVET_ROOT}/runtime/src/protocol.cpp"
)
Expand Down
7 changes: 7 additions & 0 deletions platform/linux/Integration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,14 @@ pkg_check_modules(LIBSECRET IMPORTED_TARGET libsecret-1)
add_executable(RivetIntegration
main.cpp
../runtime/backend.cpp
../system/autostart.cpp
../system/crash_shutdown.cpp
../system/notifications.cpp
../system/secure_storage.cpp
../system/single_instance.cpp
../system/system_services.cpp
../system/system_services_internal.cpp
../system/tray.cpp
${RIVET_ROOT}/runtime/src/protocol.cpp
)

Expand Down
7 changes: 7 additions & 0 deletions platform/linux/host/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,14 @@ endif()
add_executable(RivetHost
src/main.cpp
"${RIVET_ROOT}/platform/linux/runtime/backend.cpp"
"${RIVET_ROOT}/platform/linux/system/autostart.cpp"
"${RIVET_ROOT}/platform/linux/system/crash_shutdown.cpp"
"${RIVET_ROOT}/platform/linux/system/notifications.cpp"
"${RIVET_ROOT}/platform/linux/system/secure_storage.cpp"
"${RIVET_ROOT}/platform/linux/system/single_instance.cpp"
"${RIVET_ROOT}/platform/linux/system/system_services.cpp"
"${RIVET_ROOT}/platform/linux/system/system_services_internal.cpp"
"${RIVET_ROOT}/platform/linux/system/tray.cpp"
"${RIVET_ROOT}/platform/linux/theme/theme.cpp"
"${RIVET_ROOT}/runtime/src/protocol.cpp"
)
Expand Down
10 changes: 8 additions & 2 deletions platform/linux/host/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@ platform/linux/
│ ├── backend.hpp # rivet::linux_runtime::Backend — same contract as rivet::windows
│ └── backend.cpp # racketcs boot + RVT1-over-socketpair transport
├── system/
│ ├── system_services.hpp # rivet::system — single-instance, notifications,
│ └── system_services.cpp # autostart, Secret Service, crash hook, capabilities
│ ├── system_services.hpp # public rivet::system surface
│ ├── system_services.cpp # capability discovery + activation arguments
│ ├── single_instance.cpp # local activation lease and forwarding
│ ├── notifications.cpp # freedesktop notifications
│ ├── tray.cpp # StatusNotifierItem + dbusmenu
│ ├── autostart.cpp # XDG autostart entries
│ ├── secure_storage.cpp # Secret Service through libsecret
│ └── crash_shutdown.cpp # fatal-signal record + orderly termination
└── host/
├── GeneratedBackend.hpp # scaffold schema; raco rivet build replaces it
├── CMakeLists.txt
Expand Down
92 changes: 92 additions & 0 deletions platform/linux/system/autostart.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#include "system_services.hpp"
#include "system_services_internal.hpp"

#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

#include <cerrno>
#include <cstring>
#include <stdexcept>

namespace rivet::system {
namespace {

std::string autostart_path(std::string const& application_id) {
return detail::xdg_config_home() + "/autostart/" +
detail::sanitize_file_name(application_id) + ".desktop";
}

} // namespace

using detail::write_all;
using detail::xdg_config_home;

bool Autostart::Enabled(std::string const& application_id) {
std::string path = autostart_path(application_id);
int fd = ::open(path.c_str(), O_RDONLY | O_CLOEXEC);
if (fd < 0) {
if (errno == ENOENT) return false;
throw std::runtime_error(std::string("autostart check failed: ") +
std::strerror(errno));
}
std::string contents;
char buffer[1024];
while (true) {
ssize_t received = ::read(fd, buffer, sizeof(buffer));
if (received < 0) {
if (errno == EINTR) continue;
int saved = errno;
::close(fd);
throw std::runtime_error(std::string("autostart read failed: ") +
std::strerror(saved));
}
if (received == 0) break;
contents.append(buffer, static_cast<std::size_t>(received));
}
::close(fd);

// Follow the Desktop Entry spec's disabling keys.
if (contents.find("Hidden=true") != std::string::npos) return false;
if (contents.find("X-GNOME-Autostart-enabled=false") != std::string::npos)
return false;
return true;
}

void Autostart::SetEnabled(std::string const& application_id,
std::string const& executable, bool enabled) {
std::string path = autostart_path(application_id);
if (!enabled) {
if (::unlink(path.c_str()) != 0 && errno != ENOENT)
throw std::runtime_error(std::string("autostart removal failed: ") +
std::strerror(errno));
return;
}

std::string directory = xdg_config_home() + "/autostart";
::mkdir(xdg_config_home().c_str(), 0755);
::mkdir(directory.c_str(), 0755);

std::string contents =
"[Desktop Entry]\n"
"Type=Application\n"
"Name=" + application_id + "\n"
"Exec=" + executable + "\n"
"Terminal=false\n"
"X-GNOME-Autostart-enabled=true\n";
int fd = ::open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (fd < 0)
throw std::runtime_error(std::string("autostart write failed: ") +
std::strerror(errno));
try {
write_all(fd, contents.data(), contents.size());
} catch (...) {
::close(fd);
throw;
}
::close(fd);
}



} // namespace rivet::system
Loading
Loading