Skip to content

Commit

Permalink
Refactored pretty much everything
Browse files Browse the repository at this point in the history
- Ported functionality from https://github.com/openrazer/openrazer
- RGB settings are now stored on device; no need for task scheduling
- Calculation of checksum
- GUI no longer supports command line arguments
- Adopted workspace inheritance
- Bumped minor version
  • Loading branch information
gpoulios committed Mar 6, 2023
1 parent 5a09cb1 commit 13ece3f
Show file tree
Hide file tree
Showing 12 changed files with 923 additions and 294 deletions.
16 changes: 13 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
[workspace]
members = ["lib", "cli", "gui"]
default-members = ["gui"]

members = [
"lib", "cli", "gui"
]
[workspace.package]
edition = "2021"
version = "0.2.0"
authors = ["George Poulios"]
description = "A utility to control Razer DeathAdder v2 on Windows"
readme = "./README.md"
repository = "https://github.com/gpoulios/deathadderv2"
license = "GPLv2"

[workspace.dependencies]
rgb = { version = "0.8.36" }
78 changes: 12 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,86 +1,32 @@
# deathadderv2-rgb
# deathadderv2

Set (a constant) color on the Razer DeathAdder v2. (while practising in rust)

A little utility for those of us that don't want to run 2-3 apps and 6 services (!!) just for keeping this mouse from changing colors like a christmas tree. Personally, I don't care about the auto-switching of profiles that Synapse provides and all the other functionality I can have without running Razer's apps in the background.
A little utility for those of us that don't want to run 2-3 apps and 6 services (!!) just for keeping this mouse from changing colors like a christmas tree. I don't need the auto-switching of profiles that Synapse provides and (most if not all) the other functionality I can have without running Razer's apps in the background.

Unfortunately, the device does not remember the color and it comes back with the rainbow on power on (either after sleep/hibernation or on boot). Not going to bother making it into a service as the task scheduler suits me just fine (read below if you're interested in maintaining the setting).
Device protocol largely ported from [openrazer](https://github.com/openrazer/openrazer). So far I've ported all of the functionality for this particular mouse except wave/breath/spectrum/whatnot effects. The plan is to write a small UI to control settings like DPI, poll rate and brightness before I integrate RGB effects, if ever.

## Requirements

This is not for supposed to be for Linux hosts. If you are on Linux, see [openrazer](https://github.com/openrazer/openrazer).
This is not supposed to be for Linux hosts. If you are on Linux, see [openrazer](https://github.com/openrazer/openrazer), it's a great project, and supports many more features, as well as almost all devices.

Windows users, only requirement is to be using the [libusb driver](https://github.com/libusb/libusb/wiki/Windows) (either WinUSB or libusb-win32).
For Windows users, the only requirement is to be using the [libusb driver](https://github.com/libusb/libusb/wiki/Windows) (either WinUSB or libusb-win32).

One way to install it is using [Zadig](https://zadig.akeo.ie/). You only need to do this once. Change the entry "Razer DeathAdder V2 (Interface 3)". Use the spinner to select either "WinUSB (vXXX)" or "libusb-win32 (vX.Y.Z)" and hit "Replace driver". In my case (Win11) it timed out while creating a restore point but it actually installed it.
One way to install it is using [Zadig](https://zadig.akeo.ie/). You only need to do this once. Change the entry "Razer DeathAdder V2 (Interface 3)" by using the spinner to select either "WinUSB (vXXX)" or "libusb-win32 (vX.Y.Z)" and hitting "Replace driver". In my case (Win11) it timed out while creating a restore point but it actually installed it.

## Usage

The tool comes in two forms, a console executable that you can use like so:

```
> deathadder-rgb-cli.exe aabbcc
```

and a GUI app that will just pop up a color picker prompt (check the mouse while selecting).

You can use the GUI version with command line arguments too (same usage as above), except a console window will not be allocated (this is intentional).

I have not found a way to retrieve the current color from the device so both apps will save the last sent color to a file under %APPDATA%/deathadder/config/default-config.toml.

### Bonus

It is actually possible to set a different color on the scroll wheel (Synapse doesn't support this at the time of this writing). But there's a catch: most combinations don't work and I don't understand why. For sure it accepts combinations when the RGB components in both colors are the same even if in different order. For instance, the following will work:
> deathadder-rgb-cli.exe [COLOR|LOGO_COLOR] [SCROLL_WHEEL_COLOR]
where *COLOR above should be in hex [0x/#]RGB[h] or [0x/#]RRGGBB[h] format. If no arguments are specified the saved color will be applied. If scroll wheel color is not specified, the specified color will be applied to both the logo and the scroll wheel.
```
> deathadder-rgb-cli.exe 1bc c1b
> deathadder-rgb-cli.exe 1155AA AA5511
> deathadder-rgb-cli.exe 10f243 f24310
```

Edit: apparently I'm missing a simple XOR kind-of checksum calculation in the USB report packet, which, for the following combinations, ends up the same (!). Thanks [openrazer](https://github.com/openrazer/openrazer).

### Task Scheduler: re-applying the setting

The GUI version also supports `--last` as the first argument in which case it sets the last applied color (either from cli or gui). This is useful if you want to schedule a task that does not pop up any windows.

A tested setup is to set a trigger at log on, and for waking up from sleep, a custom trigger on Power-Troubleshooter with event ID 1 and delay 5 seconds. In Action tab use the absolute path to `deathadder-rgb-gui.exe` and in the arguments put `--last`. I've added the (redacted) xml to the task I used in case you want to try importing it; just make sure to edit the required fields therein, it is not supposed to work as is.

## Technical

I captured the USB using UsbPcap while Synapse was sending the color-setting commands (it was a single control transfer-write, multiple times to provide that fade effect) and replaced the RGB values in it. The rest of the packet is identical. Haven't tested in any mouse other than mine; not sure if there's anything device-specific in there that would prevent others from using it.
and a GUI app that will just pop up a color picker prompt to preview and/or set both logo and scroll wheel colors (to the same value).

The USB message header was:

```
Setup Data
bmRequestType: 0x21
bRequest: SET_REPORT (0x09)
wValue: 0x0300
wIndex: 0
wLength: 90
Data Fragment: 001f[...]
```

And this is would be the payload for setting the color to bright white:

```
File: lib/src/lib.rs
[...]
// the start (no idea what they are)
0x00, 0x1f, 0x00, 0x00, 0x00, 0x0b, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01,
// wheel RGB (3B) | body RGB (3B)
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
// the trailer (no idea what they are either)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00
[...]
```
Contrary to all other settings, I have not found a way to retrieve the current color from the device so both apps will save the last applied color to a file under %APPDATA%/deathadder/config/default-config.toml.

---
The project is licensed under the GPL.
12 changes: 8 additions & 4 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
[package]
name = "deathadder-rgb-cli"
version = "0.1.0"
edition = "2021"
edition = { workspace = true }
version = { workspace = true }
authors = { workspace = true }
description = { workspace = true }
repository = { workspace = true }
license = { workspace = true }

[[bin]]
name = "deathadder-rgb-cli"
path = "src/cli.rs"

[dependencies]
libdeathadder = { path = "../lib" }
rgb = "0.8.36"
librazer = { path = "../lib" }
rgb = { workspace = true }
59 changes: 49 additions & 10 deletions cli/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
use rgb::RGB8;
use libdeathadder::core::{rgb_from_hex, Config};
use libdeathadder::v2::set_color;
use librazer::cfg::Config;
use librazer::common::rgb_from_hex;
use librazer::device::{DeathAdderV2, RazerMouse};

fn main() {
// let dav2 = DeathAdderV2::new().expect("failed to open device");
// println!("{}", dav2);
// // dav2.set_dpi(10000, 10000);
// // dav2.set_poll_rate(librazer::common::PollingRate::Hz250);
// // println!("{:?}", dav2.get_dpi());
// // println!("{:?}", dav2.get_poll_rate());
// // dav2.set_dpi(20000, 20000);
// // dav2.set_poll_rate(librazer::common::PollingRate::Hz1000);
// // println!("{:?}", dav2.get_dpi());
// // println!("{:?}", dav2.get_poll_rate());

// let rgb1 = RGB8::from([0x00, 0xaa, 0xaa]);
// let rgb2 = RGB8::from([0xaa, 0xaa, 0x00]);
// // dav2.preview_static(rgb1, rgb2);
// dav2.set_logo_color(rgb2);
// dav2.set_scroll_color(rgb1);

// // let init_brightness = dav2.get_logo_brightness().unwrap();
// // println!("logo brightness: {:?}, scroll: {:?}", init_brightness, dav2.get_scroll_brightness());
// // dav2.set_logo_brightness(30);
// // dav2.set_scroll_brightness(30);
// // println!("logo brightness: {:?}, scroll: {:?}", dav2.get_logo_brightness(), dav2.get_scroll_brightness());

// return;

let args: Vec<String> = std::env::args().collect();

let parse_arg = |input: &str| -> RGB8 {
Expand All @@ -14,21 +40,34 @@ fn main() {
}
};

let (color, wheel_color) = match args.len() {
let (logo_color, scroll_color) = match args.len() {
..=1 => {
match Config::load() {
Some(cfg) => (cfg.color, cfg.wheel_color),
Some(cfg) => (cfg.color, cfg.scroll_color.or(Some(cfg.color)).unwrap()),
None => panic!("failed to load configuration; please specify \
arguments manually")
}
},
2 => (parse_arg(args[1].as_ref()), None),
3 => (parse_arg(args[1].as_ref()), Some(parse_arg(args[2].as_ref()))),
2..=3 => {
let color = parse_arg(args[1].as_ref());
(color, if args.len() == 3 {
parse_arg(args[2].as_ref())
} else {
color
})
},
_ => panic!("usage: {} [(body) color] [wheel color]", args[0])
};

match set_color(color, wheel_color) {
Ok(msg) => println!("{}", msg),
Err(e) => panic!("Failed to set color(s): {}", e)
}
let dav2 = DeathAdderV2::new().expect("failed to open device");

_= dav2.set_logo_color(logo_color)
.map_err(|e| panic!("failed to set logo color: {}", e))
.and_then(|_| dav2.set_scroll_color(scroll_color))
.map_err(|e| panic!("failed to set scroll color: {}", e));

_ = Config {
color: logo_color,
scroll_color: Some(scroll_color),
}.save().map_err(|e| panic!("failed to save config: {}", e));
}
12 changes: 8 additions & 4 deletions gui/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
[package]
name = "deathadder-rgb-gui"
version = "0.1.0"
edition = "2021"
edition = { workspace = true }
version = { workspace = true }
authors = { workspace = true }
description = { workspace = true }
repository = { workspace = true }
license = { workspace = true }

[[bin]]
name = "deathadder-rgb-gui"
path = "src/gui.rs"

[dependencies]
libdeathadder = { path = "../lib" }
librazer = { path = "../lib" }
winapi = "0.3.9"
rgb = "0.8.36"
rgb = { workspace = true }

[dependencies.windows]
version = "0.44.0"
Expand Down
Loading

0 comments on commit 13ece3f

Please sign in to comment.