diff --git a/.gitignore b/.gitignore | |
index 3c8b0ce..aca0b68 100644 | |
--- a/.gitignore | |
+++ b/.gitignore | |
@@ -1,11 +1,11 @@ | |
* | |
-!fusion | |
-!fusion/datatypes.py | |
-!fusion/device.py | |
+!lib | |
+!lib/datatypes.py | |
+!lib/device.py | |
!.gitignore | |
!play.py | |
-!requirements.txt | |
+!pyproject.toml | |
!profiles.py | |
!test.py | |
diff --git a/fusion/datatypes.py b/lib/datatypes.py | |
similarity index 100% | |
rename from fusion/datatypes.py | |
rename to lib/datatypes.py | |
diff --git a/fusion/device.py b/lib/device.py | |
similarity index 100% | |
rename from fusion/device.py | |
rename to lib/device.py | |
diff --git a/play.py b/play.py | |
index 4c0500c..5aa6d6b 100644 | |
--- a/play.py | |
+++ b/play.py | |
@@ -1,130 +1,135 @@ | |
-from fusion.datatypes import * | |
-from fusion.device import get_device | |
+try: | |
+ from lib.datatypes import * #local script | |
+ from lib.device import get_device | |
+except ImportError: | |
+ from .lib.datatypes import * #as a module | |
+ from .lib.device import get_device | |
+ | |
from cv2.typing import MatLike | |
import sys, cv2, time, numpy, atexit | |
#10 is actually not transient - it is persistent as a secondary profile but for our purposes we can treat it as transient | |
#since nobody should have the need to write to a profile number this high (switching between profiles require os interaction anyways so might as well write a new image) | |
TRANSIENT_ID = 10 | |
if len(sys.argv) < 2: | |
print(f'Usage: {__file__} <video path> [profile id]') | |
exit(-1) | |
if len(sys.argv) >= 3: | |
try: | |
profile = int(sys.argv[2]) | |
if profile not in range(5): | |
raise TypeError() | |
except: | |
print('Invalid profile id given, defaulting to transience (will be reverted after play (or reboot if interrupted))...') | |
profile = TRANSIENT_ID | |
else: | |
profile = TRANSIENT_ID | |
dev = get_device() | |
orig_effect = dev.get_simple_light_effect() | |
#if transient, set it back to the old profile | |
if profile == TRANSIENT_ID: | |
def reset(): | |
if orig_effect.fusion_effect < FusionLightEffect.Custom1: | |
dev.set_simple_light_effect(orig_effect) | |
else: | |
load = PictureMatrix.from_bytes(dev.get_custom_light_effect(orig_effect.fusion_effect - FusionLightEffect.Custom1)) | |
dev.set_custom_light_effect(profile, load) | |
atexit.register(reset) | |
#change to profile for the image stream first (technically only need to change if not transient and current profile is non custom, but we revert afterwards anyway) | |
#to avoid any flashing set to black first (mainly when we are in simple modes which means last seen image will be shown right when set_simple_light_effect is called) | |
dev.set_custom_light_effect(profile, PictureMatrix([RGB(0,0,0) for i in range(105)])) | |
#need to set it to one of the "proper" profiles for it to know to update | |
dev.set_simple_light_effect(FusionLightData(FusionLightEffect.Custom1 + profile if profile != TRANSIENT_ID else FusionLightEffect.Custom1, 1, 255, FusionLightColor.White, FusionLightDirection.Left2Right)) | |
def set_frame(raw: MatLike, fps_data: List[float]) -> None: | |
pre = cv2.resize(raw, (19, 6), interpolation=cv2.INTER_AREA) | |
im = cv2.cvtColor(pre, cv2.COLOR_BGR2RGB) | |
#display what's supposed to be encoded in the keyboard at this frame | |
cv2.imshow('preview', cv2.resize(pre, (190*5, 60*5))) | |
if len(fps_data) > 1: | |
cv2.setWindowTitle('preview', f'Preview (FPS: {1/numpy.average(numpy.diff(fps_data))})') | |
cv2.waitKey(1) | |
#turns out even if i do translate this into a list of RGB instead of just directly send mats it still gives basically around the same fps | |
pixels = im | |
pixels = [] | |
h, w, _ = im.shape | |
for y in range(h): | |
for x in range(w): | |
pixels.append(RGB(*im[y, x])) | |
#apply any adjustment needed to give true colors | |
dev.set_custom_light_effect(profile, PictureMatrix.pixel_matrix_to_keys(pixels), adjust=dev.ADJUST_RGB) | |
#apparently also works with static images | |
vid = cv2.VideoCapture(sys.argv[1]) | |
#skip some frames so it's not too slow | |
#TODO make this less hardcoded - this value is from testing on my laptop with bad apple | |
AVG_FPS = 11 | |
interval = vid.get(cv2.CAP_PROP_FPS) // AVG_FPS | |
actual_fps = [] | |
#this actually doesnt help that much it just gives like 1 more fps and takes a while to buffer :( | |
PRECOMPUTE = False | |
if PRECOMPUTE: | |
buf = [] | |
print('Buffering...') | |
count = 0 | |
while vid.isOpened(): | |
ret, frame = vid.read() | |
if ret: | |
buf.append(frame) | |
count += interval | |
vid.set(cv2.CAP_PROP_POS_FRAMES, count) | |
else: | |
vid.release() | |
break | |
print('Done. Starting player...') | |
for frame in buf: | |
actual_fps.append(time.time()) | |
set_frame(frame, actual_fps) | |
#get an averagable data before popping | |
if len(actual_fps) > 10: | |
actual_fps.pop(0) | |
else: | |
count = 0 | |
while vid.isOpened(): | |
actual_fps.append(time.time()) | |
ret, frame = vid.read() | |
if ret: | |
set_frame(frame, actual_fps) | |
count += interval | |
vid.set(cv2.CAP_PROP_POS_FRAMES, count) | |
#get an averagable data before popping | |
if len(actual_fps) > 10: | |
actual_fps.pop(0) | |
else: | |
vid.release() | |
break | |
#keep last frame until we manually quit | |
cv2.waitKey(0) | |
\ No newline at end of file | |
diff --git a/profiles.py b/profiles.py | |
index 44b7130..57fcf37 100644 | |
--- a/profiles.py | |
+++ b/profiles.py | |
@@ -1,50 +1,55 @@ | |
-from fusion.device import get_device | |
-from fusion.datatypes import * | |
+try: | |
+ from lib.datatypes import * #local script | |
+ from lib.device import get_device | |
+except ImportError: | |
+ from .lib.datatypes import * #as a module | |
+ from .lib.device import get_device | |
+ | |
from cv2.typing import MatLike | |
import sys, cv2 | |
""" | |
For setting images in / switching between profiles. | |
""" | |
if len(sys.argv) < 2: | |
print(f'Usage: {__file__} <profile id> [image path]') | |
exit(-1) | |
#only allow properly supported profile ids since theres not much point in supporting it especially when it gets reset after reboot | |
#(even though profile ids way higher than this is actually writable and persistent, see set_custom_light_effect comments) | |
try: | |
profile = int(sys.argv[1]) | |
if profile not in range(5): | |
raise TypeError() | |
except: | |
print('Invalid profile given (must be from 0-4).') | |
exit(-1) | |
dev = get_device() | |
if len(sys.argv) > 2: | |
#load a new image | |
def set_image(raw: MatLike) -> None: | |
pre = cv2.resize(raw, (19, 6), interpolation=cv2.INTER_AREA) | |
im = cv2.cvtColor(pre, cv2.COLOR_BGR2RGB) | |
pixels = [] | |
h, w, _ = im.shape | |
for y in range(h): | |
for x in range(w): | |
pixels.append(RGB(*im[y, x])) | |
#adjustment needed to give true colors (maybe it's just my keyboard) | |
dev.set_custom_light_effect(profile, PictureMatrix.pixel_matrix_to_keys(pixels), adjust=dev.ADJUST_RGB) | |
set_image(cv2.imread(sys.argv[2])) | |
else: | |
#at runtime this would not reload if we dont set_custom_light_effect so we need to do it anyways | |
#(unless we are not in any of the custom profiles then the last image will show up right when we do set_simple_light_effect) | |
load = PictureMatrix.from_bytes(dev.get_custom_light_effect(profile)) | |
dev.set_custom_light_effect(profile, load) | |
dev.set_simple_light_effect(FusionLightData(FusionLightEffect.Custom1 + profile, 1, 255, FusionLightColor.White, FusionLightDirection.Left2Right)) | |
\ No newline at end of file | |
diff --git a/pyproject.toml b/pyproject.toml | |
new file mode 100644 | |
index 0000000..8e7a3bc | |
--- /dev/null | |
+++ b/pyproject.toml | |
@@ -0,0 +1,27 @@ | |
+[build-system] | |
+requires = ["setuptools", "setuptools-scm"] | |
+build-backend = "setuptools.build_meta" | |
+ | |
+[project] | |
+name = "fusion" | |
+version = "0.1.0" | |
+authors = [ | |
+ { name = "despawningbone", email = "[email protected]" }, | |
+] | |
+description = "GIGABYTE Fusion RGB reimplementation" | |
+dependencies = [ | |
+ "hidapi" | |
+] | |
+ | |
+[project.optional-dependencies] | |
+player = ["opencv-python"] | |
+ | |
+#install both the lib and the scripts onto site packages | |
+[tool.setuptools] | |
+include-package-data = false | |
+packages = ["fusion", "fusion.lib"] | |
+package-dir = { fusion = "." } #don't include fusion.lib here it's unnecessary | |
+ | |
+#comment the above section and use this instead to just install the dependencies since this repo can also be run standalone | |
+#[tool.setuptools] | |
+#packages = [] | |
\ No newline at end of file | |
diff --git a/requirements.txt b/requirements.txt | |
deleted file mode 100644 | |
index ece54bd..0000000 | |
--- a/requirements.txt | |
+++ /dev/null | |
@@ -1 +0,0 @@ | |
-hidapi | |
\ No newline at end of file | |
diff --git a/test.py b/test.py | |
index fb129e8..c7db826 100644 | |
--- a/test.py | |
+++ b/test.py | |
@@ -1,25 +1,29 @@ | |
-from fusion.device import get_device | |
-from fusion.datatypes import * | |
+try: | |
+ from lib.datatypes import * #local script | |
+ from lib.device import get_device | |
+except ImportError: | |
+ from .lib.datatypes import * #as a module | |
+ from .lib.device import get_device | |
dev = get_device() | |
print(dev.get_version_string()) | |
#set simple effect | |
#print(dev.set_simple_light_effect(FusionLightData(FusionLightEffect.Wave, 1, 50, FusionLightColor.Random, FusionLightDirection.Right2Left))) | |
#print(dev.set_simple_light_effect(FusionLightData(FusionLightEffect.Static, 1, 50, FusionLightColor.White, FusionLightDirection.Right2Left))) | |
#get and set to verify if get is correctly implemented | |
# load = PictureMatrix.from_bytes(dev.get_custom_light_effect(1)) | |
# print(load) | |
# dev.set_custom_light_effect(1, load) | |
#manually set color | |
mat = PictureMatrix([RGB(70, 255, 170) for i in range(105)]) #seems like the rgb algo in the keyboard firmware really sucks - this value seems more true white than anything (edit: it's actually known in the driver and has special cases) | |
dev.set_custom_light_effect(10, mat) | |
print(dev.set_simple_light_effect(FusionLightData(FusionLightEffect.Custom1+10, 1, 255, FusionLightColor.Random, FusionLightDirection.Left2Right))) | |
#test reset (seems like after reset if we set the light effect to custom profiles without data it would crash? not if we immediately set the profile afterwards tho) | |
#also seems like if we change profiles right(? seems to last longer than 100ms for sure) after reset some data (e.g wave direction and speed) would get reset | |
# print(dev.reset()) | |
# print(dev.set_simple_light_effect(FusionLightData(FusionLightEffect.Static, 2, 255, FusionLightColor.White, FusionLightDirection.Up2Down))) | |
\ No newline at end of file |
File Metadata
File Metadata
- Mime Type
- text/x-diff
- Expires
- Mon, Apr 14, 6:45 PM (13 h, 54 m)
- Storage Engine
- local-disk
- Storage Format
- Raw Data
- Storage Handle
- 62/b7/2368c6caa5cc725c606f7e8c50d9