Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
Askannz committed Sep 30, 2018
2 parents 451a77d + 3c46361 commit bc920cf
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 93 deletions.
45 changes: 24 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ Command-line options
----------

```
usage: msi-perkeyrgb [-h] [-v] [-c FILEPATH] [--id VENDOR_ID:PRODUCT_ID]
usage: msi-perkeyrgb [-h] [-v] [-c FILEPATH] [-d] [--id VENDOR_ID:PRODUCT_ID]
[--list-presets] [-p PRESET] [-m MODEL] [--list-models]
Tool to control per-key RGB keyboard backlighting on MSI laptops.
https://github.com/Askannz/msi-perkeyrgb
Expand All @@ -36,23 +37,34 @@ optional arguments:
-c FILEPATH, --config FILEPATH
Loads the configuration file located at FILEPATH.
Refer to the README for syntax.
-d, --disable Disable RGB lighting.
--id VENDOR_ID:PRODUCT_ID
This argument allows you to specify the vendor/product
id of your keyboard. You should not have to use this
unless opening the keyboard fails with the default
value. IDs are in hexadecimal format (example :
1038:1122)
--list-presets List available presets for the given laptop model.
-p PRESET, --preset PRESET
Use vendor preset. Presets can be applied in
conjunction with configuration file.
-m MODEL, --model MODEL
Set laptop model (see --list-models). If not
specified, will use GE63 ad default.
--list-models List available laptop models.
```

Features
----------
Only "Steady" mode (fixed color for each key) is available for now, as I have not figured out the rest of the USB protocol yet. I will add more features later if enough people are interested.
For per-key configuration, only "Steady" mode (fixed color for each key) is available for now, as I have not figured out the rest of the USB protocol yet. I will add more features later if enough people are interested.

Presets are available for supported models (GS65), which emulate vendor-provided SteelSeries configurations.


Compatibility
----------

As of now, this tool was only tested on the GE63VR model. However, it should probably work on any recent MSI laptop with a per-key RGB keyboard. Please let me know if it works for your particular model, so that I can create a compatibility list.
This tool should probably work on any recent MSI laptop with a per-key RGB keyboard. Please let me know if it works for your particular model, so that I can create a compatibility list.

Requirements
----------
Expand All @@ -74,27 +86,18 @@ The HID interface is shown as `/dev/hidraw*` where `*` can be 0, 1, 2... (there
Usage
----------

You must write your RGB configuration in a file.

**First line** of the configuration file should have the following syntax :

```
model <laptop model>
msi-perkeyrgb --model <MSI model> -p <preset>
```
(see `--list-presets` for available options)

Where `<laptop model>` is any of the following :

GE63
GE73
GS63
GS73
GX63
GT63

If your laptop is not in this list, put the closest one or a model with a similar keyboard layout.
**or**

```
msi-perkeyrgb --model <MSI model> -c <path to your configuration file>
```

**Other lines** have the following syntax :
Each line of the configuration file must have the following syntax :

```
<keycodes> <mode> <mode options>
Expand All @@ -120,19 +123,19 @@ If your laptop is not in this list, put the closest one or a model with a simila

If the same key is configured differently by multiple lines, the lowest line takes priority.

Lines prefixed with `#` are ignored.

#### Examples

All keys white except yellow arrows and orange "Fn" key.
```
model GE63
all steady ffffff
arrows steady ffff00
fn steady ffc800
```

Only WASD keys (for US layout) lit up in red.
```
model GS65
25,38,39,40 steady ff0000
```

Expand Down
39 changes: 11 additions & 28 deletions msi_perkeyrgb/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import re
from msi_perkeyrgb.msi_keyboard import AVAILABLE_MSI_KEYMAPS

DEFAULT_MODEL = "GE63" # Default laptop model if nothing specified in the config file
ALIASES = {"all": "9-133,fn",
"f_row": "67-76,95,96",
"arrows": "111,113,114,116",
Expand All @@ -26,11 +24,11 @@ class UnknownModelError(Exception):
pass


def load_config(config_path):
def load_config(config_path, msi_keymap):

try:
f = open(config_path, "r")
colors_map = parse_config(f)
config_map = parse_config(f, msi_keymap)
f.close()
except IOError as e:
raise ConfigError("IOError : %s" % str(e)) from e
Expand All @@ -47,30 +45,26 @@ def load_config(config_path):
raise ConfigError("Unknown error : %s" % str(e)) from e
pass
else:
return colors_map
return config_map


def parse_config(f):
def parse_config(f, msi_keymap):

msi_keymap = parse_laptop_model(["model", DEFAULT_MODEL])
colors_map = {}
warnings = []

for i, line in enumerate(f):

line = line.replace("\n", "")

if line.replace(" ", "")[0] == "#":
continue

parameters = list(filter(None, line.split(' ')))

# Try parsing first line as laptop model definition
if i == 0:
try:
msi_keymap = parse_laptop_model(parameters)
except LineParseError:
warnings += ["No laptop model specified, assuming %s as default." % DEFAULT_MODEL]
pass
else:
continue
if i == 0 and parameters[0] == "model":
warnings += ["Passing the laptop model in the configuration file is deprecated, use the --model option instead."]
continue

# Parsing a keys/color line
if len(parameters) == 0:
Expand All @@ -89,18 +83,7 @@ def parse_config(f):
else:
colors_map = update_colors_map(colors_map, keycodes, color)

return msi_keymap, colors_map, warnings


def parse_laptop_model(parameters):

if len(parameters) == 2 and parameters[0] == "model":
for msi_models, msi_keymap in AVAILABLE_MSI_KEYMAPS:
if parameters[1] in msi_models:
return msi_keymap
raise UnknownModelError(parameters[1])
else:
raise LineParseError
return colors_map, warnings


def parse_keycodes(msi_keymap, keys_parameter):
Expand Down
8 changes: 5 additions & 3 deletions msi_perkeyrgb/hidapi_wrapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class HIDSendError(Exception):

class HID_Keyboard:

def __init__(self, id_str):
def __init__(self, usb_id):

# Locating HIDAPI library
s = popen("ldconfig -p").read()
Expand All @@ -44,18 +44,20 @@ def __init__(self, id_str):
set_hidapi_types(self._hidapi)

# Checking if the USB device corresponding to the keyboard exists
vid, pid = usb_id
vid_str = hex(vid)[2:].zfill(4)
pid_str = hex(pid)[2:].zfill(4)
id_str = vid_str + ":" + pid_str
s = popen("lsusb").read()
if s.find(id_str) == -1:
raise HIDNotFoundError

vid, pid = [int(s, 16) for s in id_str.split(':')]
self._device = self._hidapi.hid_open(vid, pid, ct.c_wchar_p(0))

if self._device is None:
raise HIDOpenError

def send_feature_report(self, data):

ret = self._hidapi.hid_send_feature_report(self._device, bytes(data), len(data))
sleep(DELAY) # The RGB controller derps if commands are sent too fast.

Expand Down
28 changes: 26 additions & 2 deletions msi_perkeyrgb/msi_keyboard.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import os
import random
import json
from msi_perkeyrgb.hidapi_wrapping import HID_Keyboard
from msi_perkeyrgb.protocol import make_key_colors_packet, make_refresh_packet

Expand All @@ -16,10 +18,26 @@

class MSI_Keyboard:

def __init__(self, id_str, msi_keymap):
@staticmethod
def get_model_keymap(msi_model):
for msi_models, msi_keymap in AVAILABLE_MSI_KEYMAPS:
if msi_model in msi_models:
return msi_keymap

self._hid_keyboard = HID_Keyboard(id_str)
@staticmethod
def get_model_presets(msi_model):
presets_path = os.path.join(os.path.dirname(__file__), 'presets/{}.json'.format(msi_model))
try:
f = open(presets_path)
return json.load(f)
except FileNotFoundError:
return {}

def __init__(self, usb_id, msi_keymap, msi_presets):

self._hid_keyboard = HID_Keyboard(usb_id)
self._msi_keymap = msi_keymap
self._msi_presets = msi_presets

def set_color_all(self, color):

Expand Down Expand Up @@ -72,6 +90,12 @@ def set_colors(self, linux_colors_map):
key_colors_packet = make_key_colors_packet(region, region_colors_map)
self._hid_keyboard.send_feature_report(key_colors_packet)

def set_preset(self, preset):

feature_reports_list = self._msi_presets[preset]
for data in feature_reports_list:
self._hid_keyboard.send_feature_report(bytearray.fromhex(data))

def refresh(self):

refresh_packet = make_refresh_packet()
Expand Down
Loading

0 comments on commit bc920cf

Please sign in to comment.