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: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build, Test, Lint and Package
name: Build, Test, Lint & Package

on:
workflow_dispatch:
Expand Down Expand Up @@ -50,6 +50,7 @@ jobs:

- name: Restore cached cargo dependencies
id: deps-cache
if: ${{ !inputs.release }}
uses: actions/cache/restore@v5
with:
path: |
Expand Down
32 changes: 21 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
name: CI

on:
pull_request_target:
push:

jobs:
build:
name: Build
uses: ./.github/workflows/build.yml

comment:
name: Bot comment
pr:
name: PR
needs: build
runs-on: ubuntu-latest
if: github.event_name == 'pull_request_target'
if: github.head_ref
env:
MESSAGE_FILE: ./message.md
steps:
- name: Create message
- name: Check if in PR
uses: insurgent-lab/[email protected]
id: is-pr

- name: Create PR comment
id: create-comment
if: ${{ steps.is-pr.outputs.result == 'true' }}
run: |
function outcome_emoji() {
if [ "$1" == "success" ]; then
Expand All @@ -32,20 +38,24 @@ jobs:
echo "$1" >> ${{ env.MESSAGE_FILE }}
}

write "## :arrow_right: Commit ${{ github.event.pull_request.head.sha }}"
write "## :arrow_right: Commit ${{ github.sha }}"
write "## :hammer_and_wrench: Building result: $(outcome_emoji ${{ needs.build.outputs.build }})"
write "### :package: Artifacts: $(outcome_emoji ${{ needs.build.outputs.artifact-url }})"
write "1. [Download the tarball](${{ needs.build.outputs.artifact-url }})"
write "2. \`unzip linux-enable-ir-emitter*.tar.gz.zip\`"
write "3. [Execute the install instructions](https://github.com/EmixamPP/linux-enable-ir-emitter?tab=readme-ov-file#installation)"
if [ "${{ needs.build.outputs.build }}" == "success" ]; then
write "### :package: Artifacts:"
write "1. [Download the tarball](${{ needs.build.outputs.artifact-url }})"
write "2. \`unzip linux-enable-ir-emitter*.tar.gz.zip\`"
write "3. [Execute the install instructions](https://github.com/EmixamPP/linux-enable-ir-emitter?tab=readme-ov-file#installation)"
fi
write ""
write "## :test_tube: Tests results: $(outcome_emoji ${{ needs.build.outputs.tests }})"
write "## :stethoscope: Clippy results: $(outcome_emoji ${{ needs.build.outputs.clippy }})"
write "## :art: Format results: $(outcome_emoji ${{ needs.build.outputs.format }})"
write "## :books: Doc results: $(outcome_emoji ${{ needs.build.outputs.doc }})"
write "## :scissors: Shear results: $(outcome_emoji ${{ needs.build.outputs.shear }})"

- uses: thollander/actions-comment-pull-request@v3
- name: Comment on PR
uses: thollander/actions-comment-pull-request@v3
if: ${{ steps.create-comment.outcome == 'success' }}
with:
comment-tag: bot_comment
mode: recreate
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- New TUI interface for improved user experience and understanding.
- `tweak` command is now accessible via the `configure` command.
- Better balance between configuration search time and exhaustiveness.
- Full rootless capabilities.

### Added
- More debug commands to help users to report issues: `--config`, `--log`, `--grey-devices`.
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Thank you for considering contributing to our project! We appreciate your intere

## Packaging Guidelines
When building a package for distribution, please ensure the following:
1. You modified the configuration and log file paths in [.cargo/config.toml](.cargo/config.toml) to match the packaging standards of the target distribution.
1. You can modify the configuration and log file paths in [.cargo/config.toml](.cargo/config.toml) to match the packaging standards of the target distribution. Note that the defaults allow rootless utilization.
2. The only external *compile time* dependency needed are
* `gcc`
* `libclang`
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "linux-enable-ir-emitter"
version = "7.0.0-beta1"
version = "7.0.0-beta2"
edition = "2024"
authors = ["Maxime Dirksen (EmixamPP)"]
license = "MIT"
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ Provides support for infrared cameras that are not directly enabled out-of-the b
> If you plan to package this software for a Linux distribution, or to contribute code, please read [CONTRIBUTING.md](CONTRIBUTING.md).

## Installation
Download the latest [linux-enable-ir-emitter-x.x.x-release-x86-64.tar.gz](https://github.com/EmixamPP/linux-enable-ir-emitter/releases). Then execute:
Download the latest asset [linux-enable-ir-emitter-x.x.x-release-x86-64.tar.gz](https://github.com/EmixamPP/linux-enable-ir-emitter/releases). Then execute:
> [!NOTE]
> Please try the 7.0.0-beta! Furthermore, this README has been updated for this version.

```
tar -C $HOME/.local/bin --no-same-owner -m -vxzf linux-enable-ir-emitter*.tar.gz
```
If not already done, add `$HOME/.local/bin` to your PATH, e.g.:
If not already done, add `$HOME/.local/bin` to your `$PATH`, e.g.:
```
echo 'export PATH=$HOME/.local/bin:$PATH' >> $HOME/.bashrc && source $HOME/.bashrc
```
Expand All @@ -31,16 +31,20 @@ The installation consists of 3 files:
### Integration with Howdy
In all files returned by `grep -rl howdy /etc/pam.d`, add the following line before the one mentioning "howdy", replacing `<USER>` with your actual username:
```
auth optional pam_exec.so /home/<USER>/.local/bin/linux-enable-ir-emitter run
auth optional pam_exec.so /home/<USER>/.local/bin/linux-enable-ir-emitter run --config /home/<USER>/.config/linux-enable-ir-emitter.toml
```

The path to the binary may vary depending on your installation method. You can determine the correct absolute path by running `which linux-enable-ir-emitter` and use that path instead.
> [!TIP]
> The installation paths may vary depending on your installation method. You can determine the correct binary absolute paths by running `which linux-enable-ir-emitter` and use that path instead. For the configuration path, it will be written when you can execute `linux-enable-ir-emitter --config`.

### Integration with other program
You will need to execute the `linux-enable-ir-emitter run` command before the program that uses the infrared camera.

Alternatively, if you can and/or want to integrate better with the program that uses the camera, you can pass an opened file descriptor for the camera to the command: `linux-enable-ir-emitter run --device <DEVICE> --fd <FD>`.

> [!Important]
> You will need to pass the config path as argument to `linux-enable-ir-emitter run --config <CONFIG_PATH>` **when executed as root** if `linux-enable-ir-emitter configure` was executed as a normal user.

## How do I enable my infrared emitter?
0. For a better experience, use a large terminal window.
1. Execute the command: `linux-enable-ir-emitter configure`
Expand Down
38 changes: 22 additions & 16 deletions src/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::video::uvc::XuControl;

use std::collections::{BTreeMap as Map, BTreeSet as Set};
use std::path::Path;
use std::path::{Path, PathBuf};

use anyhow::{Context, Result, anyhow};
use derive_more::AsRef;
Expand All @@ -13,23 +13,23 @@ static _CONFIG: &str = env!("CONFIG");
#[cfg(test)]
pub const _CONFIG: &str = "target/tmp/linux-enable-ir-emitter.toml";

fn config_file_path() -> Result<String> {
fn config_file_path() -> Result<PathBuf> {
Ok(shellexpand::env(_CONFIG)
.context("failed to expend shell variable in log file path")?
.to_string())
.to_string()
.into())
}

pub fn print_config() -> Result<()> {
let config_file = config_file_path()?;
let content_str =
std::fs::read_to_string(&config_file).context("failed to read configuration file")?;
print!("# {}\n\n{}", config_file, content_str);
print!("# {}\n\n{}", config_file.display(), content_str);
Ok(())
}

mod path {
use super::*;
use std::path::PathBuf;

#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, AsRef)]
pub struct V4LPath(PathBuf);
Expand Down Expand Up @@ -118,18 +118,25 @@ impl Configurations {
let config_str =
toml::to_string(&self.devices).context("failed to serialize configuration")?;
let config_file = config_file_path()?;
log::debug!("Saving configuration at {}:\n{}", config_file, config_str);
log::debug!(
"Saving configuration at {}:\n{}",
config_file.display(),
config_str
);
std::fs::write(config_file, config_str).context("failed to write configuration file")
}

/// Load an existing configurations or create an empty one.
/// Load an existing configurations at the default location or create an empty one.
pub fn load() -> Result<Self> {
Ok(match std::fs::read_to_string(config_file_path()?) {
Ok(config_str) => Self {
devices: toml::from_str(&config_str)
.context("failed to parse configuration file")?,
},
Err(_) => Self::default(),
Ok(Self::load_from(&config_file_path()?).unwrap_or_default())
}

/// Load an existing configurations at a specified path.
pub fn load_from(config: &Path) -> Result<Self> {
log::info!("Loading configuration from {}.", config.display());
let config_str = std::fs::read_to_string(config)?;
Ok(Self {
devices: toml::from_str(&config_str).context("failed to parse configuration file")?,
})
}

Expand All @@ -140,9 +147,8 @@ impl Configurations {
/// Initializes an existing configuration if it does not exist.
fn initialize_config_dir() -> Result<()> {
let config_file = config_file_path()?;
log::debug!("Configuration located at: {}", config_file);
let config_dir = Path::new(&config_file).parent();
if let Some(config_dir) = config_dir {
log::debug!("Configuration located at: {}", config_file.display());
if let Some(config_dir) = config_file.parent() {
std::fs::create_dir_all(config_dir).context("failed to create configuration directory")
} else {
Ok(())
Expand Down
11 changes: 9 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ async fn main() -> Result<()> {
log::debug!("Version {}.", env!("CARGO_PKG_VERSION"));
configure::configure().await
}
Some(Commands::Run { device, fd }) => {
Some(Commands::Run { device, fd, config }) => {
logger::init_term()?;
run::run(device.as_deref(), fd)
run::run(device.as_deref(), fd, config.as_deref())
}
None => Ok(()),
}
Expand Down Expand Up @@ -76,5 +76,12 @@ enum Commands {
requires = "device"
)]
fd: Option<i32>,
#[arg(
short,
long,
help = "Specify the configuration file to use. Default: use the default configuration path.",
default_value = None,
)]
config: Option<std::path::PathBuf>,
},
}
9 changes: 7 additions & 2 deletions src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ use std::path::Path;
///
/// Does not return an error if no configuration exists for the device.
///
pub fn run(device: Option<&Path>, fd: Option<i32>) -> Result<()> {
pub fn run(device: Option<&Path>, fd: Option<i32>, config: Option<&Path>) -> Result<()> {
if fd.is_some() && device.is_none() {
bail!("if a file descriptor is provided, a device path must also be provided");
}

let config = Configurations::load()?;
let config = if let Some(config) = config {
Configurations::load_from(config)?
} else {
Configurations::load()?
};

for (path, conf) in config.devices() {
if let Some(d) = device
&& d != path.as_ref()
Expand Down