From 3b7118e8c1ea7fbfe6562fadc183805f2ab7dd20 Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Mon, 22 Jan 2024 03:12:35 +0200 Subject: [PATCH 1/6] Add a C API --- .vscode/settings.json | 3 ++- Cargo.toml | 1 + cbindgen.toml | 10 +++++++ src/capi.rs | 62 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ 5 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 cbindgen.toml create mode 100644 src/capi.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index c5e9a46..048df15 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "rust-analyzer.cargo.features": [ - "bin" + "bin", + "capi" ] } diff --git a/Cargo.toml b/Cargo.toml index ca9817f..a1c3af7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ exclude = ["/tools/"] [features] bin = ["dep:clap", "dep:clap_complete", "dep:ctrlc", "dep:shadow-rs", "dep:sysinfo", "dep:winresource"] +capi = [] [profile.release] strip = true diff --git a/cbindgen.toml b/cbindgen.toml new file mode 100644 index 0000000..6629260 --- /dev/null +++ b/cbindgen.toml @@ -0,0 +1,10 @@ +language = "C" +include_guard = "KEEPAWAKE_H" +cpp_compat = true + +after_includes = """ + +typedef struct KeepAwakeBuilder KeepAwakeBuilder;""" + +[export.rename] +Builder = "KeepAwakeBuilder" diff --git a/src/capi.rs b/src/capi.rs new file mode 100644 index 0000000..62e3985 --- /dev/null +++ b/src/capi.rs @@ -0,0 +1,62 @@ +use std::{ptr, ffi::{c_char, CStr}}; + +use crate::{Builder, KeepAwake}; + +#[no_mangle] +pub extern "C" fn keepawake_new() -> *mut Builder { + Box::into_raw(Box::new(Builder::default())) +} + +#[no_mangle] +pub unsafe extern "C" fn keepawake_display(builder: *mut Builder, value: bool) { + assert!(!builder.is_null()); + (*builder).display(value); +} + +#[no_mangle] +pub unsafe extern "C" fn keepawake_idle(builder: *mut Builder, value: bool) { + assert!(!builder.is_null()); + (*builder).idle(value); +} + +#[no_mangle] +pub unsafe extern "C" fn keepawake_sleep(builder: *mut Builder, value: bool) { + assert!(!builder.is_null()); + (*builder).sleep(value); +} + +#[no_mangle] +pub unsafe extern "C" fn keepawake_reason(builder: *mut Builder, value: *const c_char) { + assert!(!builder.is_null()); + (*builder).reason(CStr::from_ptr(value).to_string_lossy()); +} + +#[no_mangle] +pub unsafe extern "C" fn keepawake_app_name(builder: *mut Builder, value: *const c_char) { + assert!(!builder.is_null()); + (*builder).app_name(CStr::from_ptr(value).to_string_lossy()); +} + +#[no_mangle] +pub unsafe extern "C" fn keepawake_app_reverse_domain(builder: *mut Builder, value: *const c_char) { + assert!(!builder.is_null()); + (*builder).app_reverse_domain(CStr::from_ptr(value).to_string_lossy()); +} + +#[no_mangle] +pub unsafe extern "C" fn keepawake_create(builder: *mut Builder) -> *mut KeepAwake { + assert!(!builder.is_null()); + (*builder).create().map_or(ptr::null_mut(), |v| Box::into_raw(Box::new(v))) +} + +#[no_mangle] +pub unsafe extern "C" fn keepawake_builder_destroy(builder: *mut Builder) { + assert!(!builder.is_null()); + drop(Box::from_raw(builder)); +} + +#[no_mangle] +pub unsafe extern "C" fn keepawake_destroy(awake: *mut KeepAwake) { + assert!(!awake.is_null()); + drop(Box::from_raw(awake)); +} diff --git a/src/lib.rs b/src/lib.rs index c8b8349..dc81edc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,9 @@ use derive_builder::Builder; mod sys; +#[cfg(feature = "capi")] +pub mod capi; + #[derive(Builder, Debug)] #[builder(public, name = "Builder", build_fn(private))] #[allow(dead_code)] // Some fields are unused on some platforms From b7f49c4c0458148ee1cd6feab0e33ed2f28d527d Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Sun, 28 Jan 2024 17:43:23 +0200 Subject: [PATCH 2/6] Add an option to free the builder from keepawake_create --- src/capi.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/capi.rs b/src/capi.rs index 62e3985..bd3452b 100644 --- a/src/capi.rs +++ b/src/capi.rs @@ -1,4 +1,7 @@ -use std::{ptr, ffi::{c_char, CStr}}; +use std::{ + ffi::{c_char, CStr}, + ptr, +}; use crate::{Builder, KeepAwake}; @@ -44,9 +47,16 @@ pub unsafe extern "C" fn keepawake_app_reverse_domain(builder: *mut Builder, val } #[no_mangle] -pub unsafe extern "C" fn keepawake_create(builder: *mut Builder) -> *mut KeepAwake { +pub unsafe extern "C" fn keepawake_create( + builder: *mut Builder, + free_builder: bool, +) -> *mut KeepAwake { assert!(!builder.is_null()); - (*builder).create().map_or(ptr::null_mut(), |v| Box::into_raw(Box::new(v))) + let result = (*builder).create(); + if free_builder { + drop(Box::from_raw(builder)); + } + result.map_or(ptr::null_mut(), |v| Box::into_raw(Box::new(v))) } #[no_mangle] From 2132e11dcd528a574dd4ea63b29c1692e44c6455 Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Tue, 30 Jan 2024 01:42:45 +0200 Subject: [PATCH 3/6] Update README.md --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index d25da1a..00d763c 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,14 @@ Keep your computer awake. Like [`caffeinate`], [`systemd-inhibit`]/[`gnome-session-inhibit`], or [PowerToys Awake], but cross-platform and written in [Rust]. +Also available as a [Rust crate], and as a [C library](#c-library-experimental) (experimental). + [`caffeinate`]: https://ss64.com/osx/caffeinate.html [`systemd-inhibit`]: https://www.freedesktop.org/software/systemd/man/systemd-inhibit.html [`gnome-session-inhibit`]: https://manpages.ubuntu.com/manpages/jammy/man1/gnome-session-inhibit.1.html [PowerToys Awake]: https://learn.microsoft.com/en-us/windows/powertoys/awake [Rust]: https://www.rust-lang.org/ +[Rust crate]: https://docs.rs/keepawake ## Usage ``` @@ -46,6 +49,23 @@ Download from https://github.com/segevfiner/keepawake-rs/releases/latest. Use: `keepawake --completions ` to generate a completion script, you will have to install it as appropriate for the specific shell you are using. +## C library (experimental) +Built using [cargo-c]. + +```sh +# build the library, create the .h header, create the .pc file +$ cargo cbuild --destdir=${D} --prefix=/usr --libdir=/usr/lib64 +``` + +```sh +# build the library, create the .h header, create the .pc file and install all of it +$ cargo cinstall --destdir=${D} --prefix=/usr --libdir=/usr/lib64 +``` + +See the generated header file for details on the API. See [cargo-c] for futher details on cargo-c usage. + +[cargo-c]: https://crates.io/crates/cargo-c + ## Notes Preventing the computer from explicitly sleeping, and/or by closing the lid, is often restricted in various ways by the OS, e.g. Only on AC power, not in any PC running Windows with [Modern Standby](https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/modern-standby). Also note that Modern Standby ignores/terminates power requests on DC (Battery) power, [PowerSetRequest - Remarks](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-powersetrequest#remarks). From 46e84e928400a53bac53086fc2c295a759eb0596 Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Mon, 5 Feb 2024 02:38:03 +0200 Subject: [PATCH 4/6] Document --- src/capi.rs | 10 ++++++++++ src/lib.rs | 1 + 2 files changed, 11 insertions(+) diff --git a/src/capi.rs b/src/capi.rs index bd3452b..936658f 100644 --- a/src/capi.rs +++ b/src/capi.rs @@ -5,47 +5,55 @@ use std::{ use crate::{Builder, KeepAwake}; +/// Create a new [`KeepAwakeBuilder`]. #[no_mangle] pub extern "C" fn keepawake_new() -> *mut Builder { Box::into_raw(Box::new(Builder::default())) } +/// Prevent the display from turning off. #[no_mangle] pub unsafe extern "C" fn keepawake_display(builder: *mut Builder, value: bool) { assert!(!builder.is_null()); (*builder).display(value); } +/// Prevent the system from sleeping due to idleness. #[no_mangle] pub unsafe extern "C" fn keepawake_idle(builder: *mut Builder, value: bool) { assert!(!builder.is_null()); (*builder).idle(value); } +/// Prevent the system from sleeping. Only works under certain, OS dependant, conditions. #[no_mangle] pub unsafe extern "C" fn keepawake_sleep(builder: *mut Builder, value: bool) { assert!(!builder.is_null()); (*builder).sleep(value); } +/// Reason the consumer is keeping the system awake. Defaults to `"User requested"`. (Used on Linux & macOS) #[no_mangle] pub unsafe extern "C" fn keepawake_reason(builder: *mut Builder, value: *const c_char) { assert!(!builder.is_null()); (*builder).reason(CStr::from_ptr(value).to_string_lossy()); } +/// Name of the program keeping the system awake. Defaults to `"keepawake-rs"`. (Used on Linux) #[no_mangle] pub unsafe extern "C" fn keepawake_app_name(builder: *mut Builder, value: *const c_char) { assert!(!builder.is_null()); (*builder).app_name(CStr::from_ptr(value).to_string_lossy()); } +/// Reverse domain name of the program keeping the system awake. Defaults to `"io.github.segevfiner.keepawake-rs"`. (Used on Linux) #[no_mangle] pub unsafe extern "C" fn keepawake_app_reverse_domain(builder: *mut Builder, value: *const c_char) { assert!(!builder.is_null()); (*builder).app_reverse_domain(CStr::from_ptr(value).to_string_lossy()); } +/// Create the [`KeepAwake`]. Optionally destroying the builder. #[no_mangle] pub unsafe extern "C" fn keepawake_create( builder: *mut Builder, @@ -59,12 +67,14 @@ pub unsafe extern "C" fn keepawake_create( result.map_or(ptr::null_mut(), |v| Box::into_raw(Box::new(v))) } +/// Destroy the [`KeepAwakeBuilder`]. #[no_mangle] pub unsafe extern "C" fn keepawake_builder_destroy(builder: *mut Builder) { assert!(!builder.is_null()); drop(Box::from_raw(builder)); } +/// Destroy the [`KeepAwake`]. #[no_mangle] pub unsafe extern "C" fn keepawake_destroy(awake: *mut KeepAwake) { assert!(!awake.is_null()); diff --git a/src/lib.rs b/src/lib.rs index dc81edc..f627c72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,6 +69,7 @@ struct Options { } impl Builder { + /// Create the [`KeepAwake`]. pub fn create(&self) -> Result { Ok(KeepAwake { _imp: sys::KeepAwake::new(self.build()?)?, From 1c8494fed75f40095f8e5bad687d137498d8fc2b Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Mon, 5 Feb 2024 02:39:52 +0200 Subject: [PATCH 5/6] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc5bb44..803462d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Added +- An experimental C API buildable using [cargo-c]. See the generated header file for details on the API. See [cargo-c] for futher details on cargo-c usage. + +[cargo-c]: https://crates.io/crates/cargo-c + ### Changed - *BREAKING!* Switched to `derive_builder` for the builder, and renamed `AwakeHandle` to `KeepAwake`. From fbd0ddc7cb27a0a308262959be930640d2b75ec3 Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Mon, 5 Feb 2024 02:44:55 +0200 Subject: [PATCH 6/6] Fix lint --- src/capi.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/capi.rs b/src/capi.rs index 936658f..18e1fbe 100644 --- a/src/capi.rs +++ b/src/capi.rs @@ -1,3 +1,5 @@ +#![allow(clippy::missing_safety_doc)] + use std::{ ffi::{c_char, CStr}, ptr, @@ -8,7 +10,7 @@ use crate::{Builder, KeepAwake}; /// Create a new [`KeepAwakeBuilder`]. #[no_mangle] pub extern "C" fn keepawake_new() -> *mut Builder { - Box::into_raw(Box::new(Builder::default())) + Box::into_raw(Box::default()) } /// Prevent the display from turning off.