mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-01-22 17:33:19 +01:00
Compare commits
5 Commits
31939f41ba
...
f4c69c7e8c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4c69c7e8c | ||
|
|
7cf6c477eb | ||
|
|
b9c251d403 | ||
|
|
6ef977f6ae | ||
|
|
6e83884c0a |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -20,3 +20,8 @@ desktop-extensions/gnome*/@types/gir-generated
|
||||
desktop-extensions/gnome*/node_modules
|
||||
desktop-extensions/gnome*/schemas/gschemas.compiled
|
||||
desktop-extensions/gnome*/*.zip
|
||||
|
||||
# agents and reference
|
||||
CLAUDE.md
|
||||
AGENTS.md
|
||||
/reference
|
||||
|
||||
547
asusctl/examples/anime-led-scan.rs
Normal file
547
asusctl/examples/anime-led-scan.rs
Normal file
@@ -0,0 +1,547 @@
|
||||
//! LED scanning tool for discovering AniMe Matrix buffer-to-LED mappings.
|
||||
//!
|
||||
//! This tool lights up one buffer index at a time, allowing you to observe
|
||||
//! which physical LED corresponds to each buffer position. This is essential
|
||||
//! for mapping new device types like G835L where the exact layout is unknown.
|
||||
//!
|
||||
//! You might want to use it slowly, as it sometimes doesn't work properly.
|
||||
//! Maybe there's better ways to make this reliable but for now it works for my use case.
|
||||
//!
|
||||
//! # Usage
|
||||
//! ```
|
||||
//! cargo run --example anime-led-scan -- [options]
|
||||
//! ```
|
||||
//!
|
||||
//! # Controls
|
||||
//! - `n` or `Enter`: Next index
|
||||
//! - `p` or `Backspace`: Previous index
|
||||
//! - `j` followed by number: Jump to specific index
|
||||
//! - `+` / `-`: Adjust step size (default 1)
|
||||
//! - `s`: Save current index to notes file
|
||||
//! - `r`: Mark current index as row start
|
||||
//! - `q` or `Ctrl+C`: Quit
|
||||
//!
|
||||
//! # Output
|
||||
//! Creates a `led-scan-notes.txt` file with recorded observations.
|
||||
|
||||
use std::env;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{self, BufRead, Write};
|
||||
|
||||
use rog_anime::usb::{get_anime_type, Brightness};
|
||||
use rog_anime::{AnimeDataBuffer, AnimeType};
|
||||
use rog_dbus::zbus_anime::AnimeProxyBlocking;
|
||||
use zbus::blocking::Connection;
|
||||
|
||||
/// Saved device state for restoration on exit
|
||||
struct SavedState {
|
||||
builtins_enabled: bool,
|
||||
brightness: Brightness,
|
||||
display_enabled: bool,
|
||||
}
|
||||
|
||||
fn print_help(scan_len: usize, buffer_len: usize) {
|
||||
println!("\n=== LED Scan Tool ===");
|
||||
println!(
|
||||
"Scan range: 0-{} (buffer size: {})",
|
||||
scan_len - 1,
|
||||
buffer_len
|
||||
);
|
||||
println!("Commands:");
|
||||
println!(" n, Enter - Next index");
|
||||
println!(" p, Backspace - Previous index");
|
||||
println!(" j <num> - Jump to index");
|
||||
println!(" + / - - Increase/decrease step size");
|
||||
println!(" s - Save note for current index");
|
||||
println!(" r - Mark as row start");
|
||||
println!(" a - Auto-scan (runs through all indices)");
|
||||
println!(" f - Fill all buffer bytes");
|
||||
println!(" f <start> <end> - Fill range (inclusive)");
|
||||
println!(" p1/p2/p3 - Fill pane 1/2/3 only (each is 627 bytes)");
|
||||
println!(" hold - Hold current LED (press Enter to release)");
|
||||
println!(" hold <s> <e> - Hold range (press Enter to release)");
|
||||
println!(" c - Clear display");
|
||||
println!(" row - Step through rows (G835L, provisional)");
|
||||
println!(" row <n> - Show specific row (G835L, provisional)");
|
||||
println!(" allrows - Light all rows sequentially (G835L)");
|
||||
println!(" rowmap - Print the full row mapping (G835L)");
|
||||
println!(" h - Show this help");
|
||||
println!(" q - Quit and restore state");
|
||||
println!();
|
||||
}
|
||||
|
||||
fn save_note(index: usize, note: &str) -> io::Result<()> {
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open("led-scan-notes.txt")?;
|
||||
writeln!(file, "Index {}: {}", index, note)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_single_led(
|
||||
proxy: &AnimeProxyBlocking,
|
||||
anime_type: AnimeType,
|
||||
index: usize,
|
||||
brightness: u8,
|
||||
) {
|
||||
let mut buffer = AnimeDataBuffer::new(anime_type);
|
||||
let data = buffer.data_mut();
|
||||
if index < data.len() {
|
||||
data[index] = brightness;
|
||||
}
|
||||
if let Err(e) = proxy.write(buffer) {
|
||||
eprintln!("Error writing to device: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_display(proxy: &AnimeProxyBlocking, anime_type: AnimeType) {
|
||||
let buffer = AnimeDataBuffer::new(anime_type);
|
||||
let _ = proxy.write(buffer);
|
||||
}
|
||||
|
||||
fn fill_display(proxy: &AnimeProxyBlocking, anime_type: AnimeType, brightness: u8) {
|
||||
let mut buffer = AnimeDataBuffer::new(anime_type);
|
||||
let data = buffer.data_mut();
|
||||
for byte in data.iter_mut() {
|
||||
*byte = brightness;
|
||||
}
|
||||
if let Err(e) = proxy.write(buffer) {
|
||||
eprintln!("Error writing to device: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill a range of LEDs. Both start and end are INCLUSIVE.
|
||||
fn fill_range(
|
||||
proxy: &AnimeProxyBlocking,
|
||||
anime_type: AnimeType,
|
||||
start: usize,
|
||||
end: usize,
|
||||
brightness: u8,
|
||||
) {
|
||||
let mut buffer = AnimeDataBuffer::new(anime_type);
|
||||
let data = buffer.data_mut();
|
||||
for i in start..=end.min(data.len().saturating_sub(1)) {
|
||||
data[i] = brightness;
|
||||
}
|
||||
if let Err(e) = proxy.write(buffer) {
|
||||
eprintln!("Error writing to device: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_pane(proxy: &AnimeProxyBlocking, anime_type: AnimeType, pane: usize, brightness: u8) {
|
||||
const PANE_LEN: usize = 627;
|
||||
let start = pane * PANE_LEN;
|
||||
let end = start + PANE_LEN - 1;
|
||||
fill_range(proxy, anime_type, start, end, brightness);
|
||||
}
|
||||
|
||||
/// G835L row pattern (PROVISIONAL - needs hardware verification):
|
||||
/// - Rows 0-1: 1 LED each
|
||||
/// - Rows 2-3: 2 LEDs each
|
||||
/// - ... (pairs of rows with same length)
|
||||
/// - Rows 26-27: 14 LEDs each
|
||||
/// - Rows 28+: 15 LEDs each (constant)
|
||||
///
|
||||
/// Returns (start_index, end_index_inclusive, row_length)
|
||||
fn g835l_row_bounds(row: usize) -> (usize, usize, usize) {
|
||||
let triangle_rows = 28;
|
||||
let triangle_leds = 210;
|
||||
|
||||
if row < triangle_rows {
|
||||
let length = row / 2 + 1;
|
||||
let mut start = 0usize;
|
||||
for r in 0..row {
|
||||
start += r / 2 + 1;
|
||||
}
|
||||
(start, start + length - 1, length)
|
||||
} else {
|
||||
let rows_after_triangle = row - triangle_rows;
|
||||
let start = triangle_leds + rows_after_triangle * 15;
|
||||
(start, start + 14, 15)
|
||||
}
|
||||
}
|
||||
|
||||
fn g835l_total_rows() -> usize {
|
||||
28 + 40
|
||||
}
|
||||
|
||||
fn save_state(proxy: &AnimeProxyBlocking) -> SavedState {
|
||||
SavedState {
|
||||
builtins_enabled: proxy.builtins_enabled().unwrap_or(false),
|
||||
brightness: proxy.brightness().unwrap_or(Brightness::Med),
|
||||
display_enabled: proxy.enable_display().unwrap_or(true),
|
||||
}
|
||||
}
|
||||
|
||||
fn restore_state(proxy: &AnimeProxyBlocking, state: &SavedState) {
|
||||
let _ = proxy.set_builtins_enabled(state.builtins_enabled);
|
||||
let _ = proxy.set_brightness(state.brightness);
|
||||
let _ = proxy.set_enable_display(state.display_enabled);
|
||||
let _ = proxy.run_main_loop(true);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
let mut start_index = 0usize;
|
||||
let mut brightness = 200u8;
|
||||
let mut scan_limit: Option<usize> = None;
|
||||
|
||||
let mut i = 1;
|
||||
while i < args.len() {
|
||||
match args[i].as_str() {
|
||||
"--start" | "-s" => {
|
||||
if i + 1 < args.len() {
|
||||
start_index = args[i + 1].parse().unwrap_or(0);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
"--brightness" | "-b" => {
|
||||
if i + 1 < args.len() {
|
||||
brightness = args[i + 1].parse().unwrap_or(200);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
"--limit" | "-l" => {
|
||||
if i + 1 < args.len() {
|
||||
scan_limit = args[i + 1].parse().ok();
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
"--help" | "-h" => {
|
||||
println!("LED Scan Tool for AniMe Matrix");
|
||||
println!();
|
||||
println!("Usage: anime-led-scan [options]");
|
||||
println!();
|
||||
println!("Options:");
|
||||
println!(" -s, --start <N> Start at index N (default: 0)");
|
||||
println!(" -b, --brightness <N> LED brightness 0-255 (default: 200)");
|
||||
println!(" -l, --limit <N> Cap scan range to N indices (e.g. 810 for G835L)");
|
||||
println!(" -h, --help Show this help");
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let conn = match Connection::system() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to connect to D-Bus: {}", e);
|
||||
eprintln!("Make sure asusd is running.");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let proxy = match AnimeProxyBlocking::new(&conn) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to create Anime proxy: {}", e);
|
||||
eprintln!("Make sure asusd supports your device.");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let anime_type = get_anime_type();
|
||||
let buffer_len = anime_type.data_length();
|
||||
let scan_len = scan_limit.unwrap_or(buffer_len).min(buffer_len);
|
||||
|
||||
println!("=== LED Scan Tool ===");
|
||||
println!("Device type: {:?}", anime_type);
|
||||
println!("Buffer length: {} bytes", buffer_len);
|
||||
println!("Scan range: 0-{}", scan_len - 1);
|
||||
println!("Brightness: {}", brightness);
|
||||
println!();
|
||||
|
||||
// Save current state for restoration
|
||||
let saved_state = save_state(&proxy);
|
||||
println!("Saved device state for restoration on exit.");
|
||||
|
||||
// Stop system animations
|
||||
if let Err(e) = proxy.run_main_loop(false) {
|
||||
eprintln!("Warning: Could not stop main loop: {}", e);
|
||||
}
|
||||
println!("Stopped system animations.");
|
||||
|
||||
print_help(scan_len, buffer_len);
|
||||
|
||||
let mut current_index = start_index.min(scan_len - 1);
|
||||
let mut step = 1usize;
|
||||
|
||||
write_single_led(&proxy, anime_type, current_index, brightness);
|
||||
println!(">>> Index: {} (step: {})", current_index, step);
|
||||
|
||||
let stdin = io::stdin();
|
||||
let mut input = String::new();
|
||||
|
||||
loop {
|
||||
input.clear();
|
||||
print!("> ");
|
||||
io::stdout().flush().unwrap();
|
||||
|
||||
if stdin.lock().read_line(&mut input).is_err() {
|
||||
break;
|
||||
}
|
||||
|
||||
let cmd = input.trim();
|
||||
|
||||
match cmd {
|
||||
"q" | "quit" | "exit" => {
|
||||
clear_display(&proxy, anime_type);
|
||||
restore_state(&proxy, &saved_state);
|
||||
println!("Restored device state. Goodbye!");
|
||||
break;
|
||||
}
|
||||
"n" | "" => {
|
||||
current_index = (current_index + step).min(scan_len - 1);
|
||||
write_single_led(&proxy, anime_type, current_index, brightness);
|
||||
println!(">>> Index: {} (step: {})", current_index, step);
|
||||
}
|
||||
"p" => {
|
||||
current_index = current_index.saturating_sub(step);
|
||||
write_single_led(&proxy, anime_type, current_index, brightness);
|
||||
println!(">>> Index: {} (step: {})", current_index, step);
|
||||
}
|
||||
"+" => {
|
||||
step = step.saturating_mul(2).max(1);
|
||||
println!("Step size: {}", step);
|
||||
}
|
||||
"-" => {
|
||||
step = step.saturating_div(2).max(1);
|
||||
println!("Step size: {}", step);
|
||||
}
|
||||
"r" => {
|
||||
if let Err(e) = save_note(current_index, "ROW START") {
|
||||
eprintln!("Error saving note: {}", e);
|
||||
} else {
|
||||
println!("Saved: Index {} marked as ROW START", current_index);
|
||||
}
|
||||
}
|
||||
"h" | "help" | "?" => {
|
||||
print_help(scan_len, buffer_len);
|
||||
}
|
||||
cmd if cmd.starts_with('j') => {
|
||||
let num_str = cmd.trim_start_matches('j').trim();
|
||||
if let Ok(idx) = num_str.parse::<usize>() {
|
||||
if idx < scan_len {
|
||||
current_index = idx;
|
||||
write_single_led(&proxy, anime_type, current_index, brightness);
|
||||
println!(">>> Index: {} (step: {})", current_index, step);
|
||||
} else {
|
||||
println!("Index {} out of range (max: {})", idx, scan_len - 1);
|
||||
}
|
||||
} else {
|
||||
println!("Usage: j <number>");
|
||||
}
|
||||
}
|
||||
cmd if cmd.starts_with('s') && !cmd.starts_with("show") => {
|
||||
let note = cmd.trim_start_matches('s').trim();
|
||||
let note = if note.is_empty() { "observed" } else { note };
|
||||
if let Err(e) = save_note(current_index, note) {
|
||||
eprintln!("Error saving note: {}", e);
|
||||
} else {
|
||||
println!("Saved note for index {}", current_index);
|
||||
}
|
||||
}
|
||||
"a" => {
|
||||
println!("Auto-scan mode (0 to {})...", scan_len - 1);
|
||||
let delay = std::time::Duration::from_millis(10);
|
||||
for idx in current_index..scan_len {
|
||||
write_single_led(&proxy, anime_type, idx, brightness);
|
||||
print!("\rIndex: {} / {} ", idx, scan_len - 1);
|
||||
io::stdout().flush().unwrap();
|
||||
std::thread::sleep(delay);
|
||||
current_index = idx;
|
||||
}
|
||||
println!();
|
||||
println!("Auto-scan complete. Current index: {}", current_index);
|
||||
}
|
||||
"c" => {
|
||||
clear_display(&proxy, anime_type);
|
||||
println!("Display cleared");
|
||||
}
|
||||
"f" => {
|
||||
fill_display(&proxy, anime_type, brightness);
|
||||
println!("All buffer bytes filled at brightness {}", brightness);
|
||||
}
|
||||
"p1" => {
|
||||
fill_pane(&proxy, anime_type, 0, brightness);
|
||||
println!("Pane 1 (indices 0-626) filled");
|
||||
}
|
||||
"p2" => {
|
||||
fill_pane(&proxy, anime_type, 1, brightness);
|
||||
println!("Pane 2 (indices 627-1253) filled");
|
||||
}
|
||||
"p3" => {
|
||||
fill_pane(&proxy, anime_type, 2, brightness);
|
||||
println!("Pane 3 (indices 1254-1880) filled");
|
||||
}
|
||||
cmd if cmd.starts_with("f ") => {
|
||||
let parts: Vec<&str> = cmd.split_whitespace().collect();
|
||||
if parts.len() == 3 {
|
||||
if let (Ok(start), Ok(end)) =
|
||||
(parts[1].parse::<usize>(), parts[2].parse::<usize>())
|
||||
{
|
||||
fill_range(&proxy, anime_type, start, end, brightness);
|
||||
println!("Filled indices {} to {}", start, end);
|
||||
} else {
|
||||
println!("Usage: f <start> <end>");
|
||||
}
|
||||
} else {
|
||||
println!("Usage: f <start> <end>");
|
||||
}
|
||||
}
|
||||
"show" => {
|
||||
write_single_led(&proxy, anime_type, current_index, brightness);
|
||||
println!(">>> Index: {} (step: {})", current_index, step);
|
||||
}
|
||||
"row" => {
|
||||
if anime_type != AnimeType::G835L {
|
||||
println!("Warning: Row commands use G835L mapping (provisional). You can add to this code to support other types. `examples/anime-led-scan.rs[402:425]`");
|
||||
}
|
||||
println!("Row stepping mode. Press Enter for next row, 'q' to quit.");
|
||||
let total = g835l_total_rows();
|
||||
for row_num in 0..total {
|
||||
let (start, end, len) = g835l_row_bounds(row_num);
|
||||
if end >= scan_len {
|
||||
println!("Row {} exceeds scan limit, stopping.", row_num);
|
||||
break;
|
||||
}
|
||||
println!("Row {}: indices {}-{} ({} LEDs)", row_num, start, end, len);
|
||||
fill_range(&proxy, anime_type, start, end, brightness);
|
||||
input.clear();
|
||||
print!("(Enter=next, q=quit) > ");
|
||||
io::stdout().flush().unwrap();
|
||||
if stdin.lock().read_line(&mut input).is_err() {
|
||||
break;
|
||||
}
|
||||
if input.trim() == "q" {
|
||||
break;
|
||||
}
|
||||
clear_display(&proxy, anime_type);
|
||||
}
|
||||
println!("Row stepping done.");
|
||||
}
|
||||
cmd if cmd.starts_with("row ") => {
|
||||
if anime_type != AnimeType::G835L {
|
||||
println!("Warning: Row commands use G835L mapping (provisional).");
|
||||
}
|
||||
let row_str = cmd.trim_start_matches("row ").trim();
|
||||
if let Ok(row_num) = row_str.parse::<usize>() {
|
||||
let total = g835l_total_rows();
|
||||
if row_num < total {
|
||||
let (start, end, len) = g835l_row_bounds(row_num);
|
||||
if end < scan_len {
|
||||
println!("Row {}: indices {}-{} ({} LEDs)", row_num, start, end, len);
|
||||
fill_range(&proxy, anime_type, start, end, brightness);
|
||||
} else {
|
||||
println!("Row {} exceeds scan limit", row_num);
|
||||
}
|
||||
} else {
|
||||
println!("Row {} out of range (max: {})", row_num, total - 1);
|
||||
}
|
||||
} else {
|
||||
println!("Usage: row <number>");
|
||||
}
|
||||
}
|
||||
"allrows" => {
|
||||
if anime_type != AnimeType::G835L {
|
||||
println!("Warning: Row commands use G835L mapping (provisional).");
|
||||
}
|
||||
println!("Lighting all rows sequentially (200ms each)...");
|
||||
let total = g835l_total_rows();
|
||||
let delay = std::time::Duration::from_millis(200);
|
||||
for row_num in 0..total {
|
||||
let (start, end, len) = g835l_row_bounds(row_num);
|
||||
if end >= scan_len {
|
||||
println!("\nRow {} exceeds scan limit, stopping.", row_num);
|
||||
break;
|
||||
}
|
||||
print!(
|
||||
"\rRow {}/{}: indices {}-{} ({} LEDs) ",
|
||||
row_num,
|
||||
total - 1,
|
||||
start,
|
||||
end,
|
||||
len
|
||||
);
|
||||
io::stdout().flush().unwrap();
|
||||
fill_range(&proxy, anime_type, start, end, brightness);
|
||||
std::thread::sleep(delay);
|
||||
clear_display(&proxy, anime_type);
|
||||
}
|
||||
println!("\nDone.");
|
||||
}
|
||||
"rowmap" => {
|
||||
if anime_type != AnimeType::G835L {
|
||||
println!("Warning: Row map is for G835L (provisional).");
|
||||
}
|
||||
println!("G835L Row Map:");
|
||||
let total = g835l_total_rows();
|
||||
for row_num in 0..total {
|
||||
let (start, end, len) = g835l_row_bounds(row_num);
|
||||
let marker = if end >= scan_len {
|
||||
" (exceeds limit)"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
println!(
|
||||
" Row {:2}: indices {:4}-{:4} ({:2} LEDs){}",
|
||||
row_num, start, end, len, marker
|
||||
);
|
||||
}
|
||||
}
|
||||
"hold" => {
|
||||
// Single write, wait for Enter to release
|
||||
println!("Holding index {}. Press Enter to release...", current_index);
|
||||
write_single_led(&proxy, anime_type, current_index, brightness);
|
||||
input.clear();
|
||||
let _ = stdin.lock().read_line(&mut input);
|
||||
clear_display(&proxy, anime_type);
|
||||
println!("Released.");
|
||||
}
|
||||
cmd if cmd.starts_with("hold ") => {
|
||||
let arg = cmd.trim_start_matches("hold ").trim();
|
||||
let (start, end): (usize, usize) = match arg {
|
||||
"p1" | "1" => (0, 626),
|
||||
"p2" | "2" => (627, 1253),
|
||||
_ => {
|
||||
let parts: Vec<&str> = arg.split_whitespace().collect();
|
||||
if parts.len() == 2 {
|
||||
if let (Ok(s), Ok(e)) = (parts[0].parse(), parts[1].parse()) {
|
||||
(s, e)
|
||||
} else {
|
||||
println!("Usage: hold p1, hold p2, or hold <start> <end>");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
println!("Usage: hold p1, hold p2, or hold <start> <end>");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
println!("Holding range {}-{}. Press Enter to release...", start, end);
|
||||
fill_range(&proxy, anime_type, start, end, brightness);
|
||||
input.clear();
|
||||
let _ = stdin.lock().read_line(&mut input);
|
||||
clear_display(&proxy, anime_type);
|
||||
println!("Released.");
|
||||
}
|
||||
_ => {
|
||||
if let Ok(idx) = cmd.parse::<usize>() {
|
||||
if idx < scan_len {
|
||||
current_index = idx;
|
||||
write_single_led(&proxy, anime_type, current_index, brightness);
|
||||
println!(">>> Index: {} (step: {})", current_index, step);
|
||||
} else {
|
||||
println!("Index {} out of range (max: {})", idx, scan_len - 1);
|
||||
}
|
||||
} else {
|
||||
println!("Unknown command: '{}'. Type 'h' for help.", cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ impl AnimeType {
|
||||
match self {
|
||||
AnimeType::GU604 => 70,
|
||||
// TODO: Find G635L W*H
|
||||
// AnimeType::G635L => 68
|
||||
AnimeType::G635L => 68,
|
||||
AnimeType::G835L => 68,
|
||||
_ => 74,
|
||||
}
|
||||
@@ -125,7 +125,7 @@ impl AnimeType {
|
||||
match self {
|
||||
AnimeType::GA401 => 36,
|
||||
AnimeType::GU604 => 43,
|
||||
// AnimeType::G635L => 34,
|
||||
AnimeType::G635L => 34,
|
||||
AnimeType::G835L => 34,
|
||||
_ => 39,
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ impl AnimeDiagonal {
|
||||
/// Generate the base image from inputs. The result can be displayed as is
|
||||
/// or updated via scale, position, or angle then displayed again after
|
||||
/// `update()`.
|
||||
///
|
||||
/// TODO: G835L and G635L only supports grayscale
|
||||
#[inline]
|
||||
pub fn from_png(
|
||||
path: &Path,
|
||||
@@ -385,8 +383,6 @@ impl AnimeDiagonal {
|
||||
}
|
||||
|
||||
// TODO: Implement `to_g635l_packets` and `to_g835l_packets` functions
|
||||
// IMPORTANT NOTE: G635L and G835L ONLY SUPPORT GRAYSCALE
|
||||
//
|
||||
// fn to_g835l_packets(buf: &[u8]) -> Result<AnimeDataBuffer> {
|
||||
// let mut buf = vec![0u8; AnimeType::GU604.data_length()];
|
||||
|
||||
|
||||
BIN
rog-anime/tests/data/g835l-diagonal-fullbright.png
Normal file
BIN
rog-anime/tests/data/g835l-diagonal-fullbright.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 187 B |
BIN
rog-anime/tests/data/g835l-diagonal.gif
Normal file
BIN
rog-anime/tests/data/g835l-diagonal.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 829 B |
BIN
rog-anime/tests/data/g835l-diagonal.png
Normal file
BIN
rog-anime/tests/data/g835l-diagonal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 148 B |
@@ -102,8 +102,9 @@ pub fn setup_window(config: Arc<Mutex<Config>>) -> MainWindow {
|
||||
available.contains(&"xyz.ljones.Aura".to_string()),
|
||||
available.contains(&"xyz.ljones.Anime".to_string()),
|
||||
available.contains(&"xyz.ljones.FanCurves".to_string()),
|
||||
true,
|
||||
true,
|
||||
true, // GPU Configuration
|
||||
true, // App Settings
|
||||
true, // About
|
||||
]
|
||||
.into(),
|
||||
);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { PageFans } from "pages/fans.slint";
|
||||
import { PageAnime, AnimePageData } from "pages/anime.slint";
|
||||
import { RogItem } from "widgets/common.slint";
|
||||
import { PageAura } from "pages/aura.slint";
|
||||
import { PageGPU } from "pages/gpu.slint";
|
||||
import { Node } from "widgets/graph.slint";
|
||||
export { Node }
|
||||
import { FanPageData, FanType, Profile } from "types/fan_types.slint";
|
||||
@@ -24,7 +25,15 @@ export component MainWindow inherits Window {
|
||||
default-font-size: 14px;
|
||||
default-font-weight: 400;
|
||||
icon: @image-url("../data/rog-control-center.png");
|
||||
in property <[bool]> sidebar_items_avilable: [true, true, true, true, true, true];
|
||||
in property <[bool]> sidebar_items_avilable: [
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true, // GPU Configuration
|
||||
true, // App Settings
|
||||
true, // About
|
||||
];
|
||||
private property <bool> show_notif;
|
||||
private property <bool> fade_cover;
|
||||
private property <bool> toast: false;
|
||||
@@ -58,8 +67,9 @@ export component MainWindow inherits Window {
|
||||
@tr("Menu2" => "Keyboard Aura"),
|
||||
@tr("Menu3" => "AniMe Matrix"),
|
||||
@tr("Menu4" => "Fan Curves"),
|
||||
@tr("Menu5" => "App Settings"),
|
||||
@tr("Menu6" => "About"),
|
||||
@tr("Menu5" => "GPU Configuration"),
|
||||
@tr("Menu6" => "App Settings"),
|
||||
@tr("Menu7" => "About"),
|
||||
];
|
||||
available: root.sidebar_items_avilable;
|
||||
}
|
||||
@@ -89,26 +99,34 @@ export component MainWindow inherits Window {
|
||||
height: root.height + 12px;
|
||||
}
|
||||
|
||||
aura := PageAura {
|
||||
/*if(side-bar.current-item == 1):*/ aura := PageAura {
|
||||
width: root.width - side-bar.width;
|
||||
visible: side-bar.current-item == 1;
|
||||
}
|
||||
|
||||
if(side-bar.current-item == 2): PageAnime {
|
||||
width: root.width - side-bar.width;
|
||||
visible: side-bar.current-item == 2;
|
||||
}
|
||||
|
||||
fans := PageFans {
|
||||
if(side-bar.current-item == 3): fans := PageFans {
|
||||
width: root.width - side-bar.width;
|
||||
visible: side-bar.current-item == 3;
|
||||
}
|
||||
|
||||
if(side-bar.current-item == 4): PageAppSettings {
|
||||
if(side-bar.current-item == 4): PageGPU {
|
||||
width: root.width - side-bar.width;
|
||||
visible: side-bar.current-item == 4;
|
||||
}
|
||||
|
||||
if(side-bar.current-item == 5): PageAbout {
|
||||
if(side-bar.current-item == 5): PageAppSettings {
|
||||
width: root.width - side-bar.width;
|
||||
visible: side-bar.current-item == 5;
|
||||
}
|
||||
|
||||
if(side-bar.current-item == 6): PageAbout {
|
||||
width: root.width - side-bar.width;
|
||||
visible: side-bar.current-item == 6;
|
||||
}
|
||||
|
||||
if toast: Rectangle {
|
||||
|
||||
102
rog-control-center/ui/pages/gpu.slint
Normal file
102
rog-control-center/ui/pages/gpu.slint
Normal file
@@ -0,0 +1,102 @@
|
||||
import { Palette, TabWidget, Button, CheckBox } from "std-widgets.slint";
|
||||
import { Graph, Node } from "../widgets/graph.slint";
|
||||
import { SystemToggle } from "../widgets/common.slint";
|
||||
import { Profile, FanType, FanPageData } from "../types/fan_types.slint";
|
||||
import { Palette, HorizontalBox , VerticalBox, ScrollView, Slider, Button, Switch, ComboBox, GroupBox, StandardButton} from "std-widgets.slint";
|
||||
|
||||
export global GPUPageData {
|
||||
// GPU mode and device state
|
||||
in-out property <int> gpu_mux_mode: 1; // 0 = Ultra/Discreet, 1 = Integrated/Optimus
|
||||
in-out property <int> dgpu_disabled: 0; // 1 == dGPU disabled
|
||||
in-out property <int> egpu_enabled: 0; // 1 == eGPU (XGMobile) enabled
|
||||
callback cb_gpu_mux_mode(int);
|
||||
callback cb_dgpu_disabled(int);
|
||||
callback cb_egpu_enabled(int);
|
||||
}
|
||||
|
||||
export component PageGPU inherits Rectangle {
|
||||
|
||||
clip: true;
|
||||
ScrollView {
|
||||
VerticalLayout {
|
||||
padding: 10px;
|
||||
spacing: 10px;
|
||||
alignment: LayoutAlignment.start;
|
||||
Rectangle {
|
||||
background: Palette.alternate-background;
|
||||
border-color: Palette.accent-background;
|
||||
border-width: 3px;
|
||||
border-radius: 10px;
|
||||
height: 40px;
|
||||
Text {
|
||||
font-size: 18px;
|
||||
color: Palette.control-foreground;
|
||||
horizontal-alignment: TextHorizontalAlignment.center;
|
||||
text: @tr("GPU Configuration");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
HorizontalLayout {
|
||||
spacing: 10px;
|
||||
|
||||
// Ultra (discreet) mode button - disabled if dGPU is marked disabled
|
||||
Rectangle {
|
||||
width: 120px;
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
border-color: Palette.border;
|
||||
border-width: 2px;
|
||||
background: GPUPageData.gpu_mux_mode == 0 ? Palette.accent-background : Palette.control-background;
|
||||
opacity: GPUPageData.dgpu_disabled == 1 ? 0.5 : 1.0;
|
||||
|
||||
Text {
|
||||
color: Palette.control-foreground;
|
||||
horizontal-alignment: TextHorizontalAlignment.center;
|
||||
vertical-alignment: TextVerticalAlignment.center;
|
||||
text: @tr("Ultra");
|
||||
}
|
||||
|
||||
TouchArea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
clicked => {
|
||||
if (GPUPageData.dgpu_disabled != 1 && GPUPageData.gpu_mux_mode != 0) {
|
||||
GPUPageData.cb_gpu_mux_mode(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Integrated (Optimus) mode button
|
||||
Rectangle {
|
||||
width: 120px;
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
border-color: Palette.border;
|
||||
border-width: 2px;
|
||||
background: GPUPageData.gpu_mux_mode == 1 ? Palette.accent-background : Palette.control-background;
|
||||
|
||||
Text {
|
||||
color: Palette.control-foreground;
|
||||
horizontal-alignment: TextHorizontalAlignment.center;
|
||||
vertical-alignment: TextVerticalAlignment.center;
|
||||
text: @tr("Integrated");
|
||||
}
|
||||
|
||||
TouchArea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
clicked => {
|
||||
if (GPUPageData.gpu_mux_mode != 1) {
|
||||
GPUPageData.cb_gpu_mux_mode(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -253,6 +253,66 @@ export component PageSystem inherits Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
if SystemPageData.kbd_leds_awake != -1 ||
|
||||
SystemPageData.kbd_leds_sleep != -1 ||
|
||||
SystemPageData.kbd_leds_boot != -1 ||
|
||||
SystemPageData.kbd_leds_shutdown != -1: VerticalLayout {
|
||||
padding: 0px;
|
||||
spacing: 0px;
|
||||
alignment: LayoutAlignment.start;
|
||||
Rectangle {
|
||||
background: Palette.alternate-background;
|
||||
border-color: Palette.accent-background;
|
||||
border-width: 3px;
|
||||
border-radius: 10px;
|
||||
height: 40px;
|
||||
Text {
|
||||
font-size: 18px;
|
||||
color: Palette.control-foreground;
|
||||
horizontal-alignment: TextHorizontalAlignment.center;
|
||||
text: @tr("Keyboard Power Management");
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
|
||||
HorizontalLayout {
|
||||
spacing: 10px;
|
||||
if SystemPageData.kbd_leds_awake != -1: SystemToggleInt {
|
||||
text: @tr("Keyboard Awake Effect");
|
||||
checked_int <=> SystemPageData.kbd_leds_awake;
|
||||
toggled => {
|
||||
SystemPageData.cb_kbd_leds_awake(SystemPageData.kbd_leds_awake)
|
||||
}
|
||||
}
|
||||
|
||||
if SystemPageData.kbd_leds_sleep != -1: SystemToggleInt {
|
||||
text: @tr("Keyboard Sleep Effect");
|
||||
checked_int <=> SystemPageData.kbd_leds_sleep;
|
||||
toggled => {
|
||||
SystemPageData.cb_kbd_leds_sleep(SystemPageData.kbd_leds_sleep)
|
||||
}
|
||||
}
|
||||
|
||||
if SystemPageData.kbd_leds_boot != -1: SystemToggleInt {
|
||||
text: @tr("Keyboard Boot Effect");
|
||||
checked_int <=> SystemPageData.kbd_leds_boot;
|
||||
toggled => {
|
||||
SystemPageData.cb_kbd_leds_boot(SystemPageData.kbd_leds_boot)
|
||||
}
|
||||
}
|
||||
|
||||
if SystemPageData.kbd_leds_shutdown != -1: SystemToggleInt {
|
||||
text: @tr("Keyboard Shutdown Effect");
|
||||
checked_int <=> SystemPageData.kbd_leds_shutdown;
|
||||
toggled => {
|
||||
SystemPageData.cb_kbd_leds_shutdown(SystemPageData.kbd_leds_shutdown)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
background: Palette.alternate-background;
|
||||
border-color: Palette.accent-background;
|
||||
@@ -286,44 +346,6 @@ export component PageSystem inherits Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
title: @tr("Keyboard Power Management");
|
||||
HorizontalLayout {
|
||||
spacing: 10px;
|
||||
if SystemPageData.kbd_leds_awake != -1: SystemToggleInt {
|
||||
text: @tr("Keyboard Awake Effect");
|
||||
checked_int <=> SystemPageData.kbd_leds_awake;
|
||||
toggled => {
|
||||
SystemPageData.cb_kbd_leds_awake(SystemPageData.kbd_leds_awake)
|
||||
}
|
||||
}
|
||||
|
||||
if SystemPageData.kbd_leds_sleep != -1: SystemToggleInt {
|
||||
text: @tr("Keyboard Sleep Effect");
|
||||
checked_int <=> SystemPageData.kbd_leds_sleep;
|
||||
toggled => {
|
||||
SystemPageData.cb_kbd_leds_sleep(SystemPageData.kbd_leds_sleep)
|
||||
}
|
||||
}
|
||||
|
||||
if SystemPageData.kbd_leds_boot != -1: SystemToggleInt {
|
||||
text: @tr("Keyboard Boot Effect");
|
||||
checked_int <=> SystemPageData.kbd_leds_boot;
|
||||
toggled => {
|
||||
SystemPageData.cb_kbd_leds_boot(SystemPageData.kbd_leds_boot)
|
||||
}
|
||||
}
|
||||
|
||||
if SystemPageData.kbd_leds_shutdown != -1: SystemToggleInt {
|
||||
text: @tr("Keyboard Shutdown Effect");
|
||||
checked_int <=> SystemPageData.kbd_leds_shutdown;
|
||||
toggled => {
|
||||
SystemPageData.cb_kbd_leds_shutdown(SystemPageData.kbd_leds_shutdown)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalBox {
|
||||
padding: 0px;
|
||||
spacing: 10px;
|
||||
|
||||
@@ -5,7 +5,7 @@ use zbus::proxy;
|
||||
#[proxy(
|
||||
interface = "xyz.ljones.Anime",
|
||||
default_service = "xyz.ljones.Asusd",
|
||||
default_path = "/xyz/ljones"
|
||||
default_path = "/xyz/ljones/aura/anime"
|
||||
)]
|
||||
pub trait Anime {
|
||||
/// DeviceState method
|
||||
|
||||
Reference in New Issue
Block a user