Add rpm installer for Linux

This commit is contained in:
P3n3tr4t0r
2025-06-24 20:30:43 +02:00
parent 7b56e64ef5
commit 505d1b7fe8
7 changed files with 131 additions and 57 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 shutdown4life
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -7,10 +7,12 @@ This is a fork of [corsair_lcd_tool](https://github.com/UDPSendToFailed/corsair_
- Low resources usage compared to iCUE.
# Planned Features
- .rpm installer
- Overlay text
- Implement temperatures
# TODO:
- fix tray icon with .rpm
# Usage
- Install [Python](https://www.python.org/downloads/ "Python") 3.6 or newer.
- Clone the repo or download as ZIP and extract it to a new folder.
@@ -28,7 +30,7 @@ It might work on Windows but to get sure use the original [corsair_lcd_tool by U
# WHY
Because Corsair decided to do nothing for Linux users.
If I have some time I might also integrate RGB Controllers in another repo!
If I have some time I might also integrate RGB Controllers in another repo with a fork of this!
# Thanks to
- [UDPSendToFailed](https://github.com/UDPSendToFailed "UDPSendToFailed") for the work. The main functionality is all in [corsair_lcd_tool](https://github.com/UDPSendToFailed/corsair_lcd_tool "corsair_lcd_tool")

8
corsair_lcd_tool.desktop Normal file
View File

@@ -0,0 +1,8 @@
[Desktop Entry]
Name=Corsair LCD Tool v2
Comment=Manage Corsair LCD screens
Exec=corsair-lcd-tool
Icon=corsair-lcd-tool
Terminal=false
Type=Application
Categories=Utility;System;

View File

@@ -1,3 +1,4 @@
#!/usr/bin/env python3
import logging, os, platform, sys, cv2, numpy as np, yaml, usb.core, errno
from dataclasses import dataclass
from PyQt6.QtCore import Qt, QTimer, QPointF, pyqtSignal, QThread, pyqtSlot, QRectF
@@ -5,10 +6,13 @@ from PyQt6.QtGui import QPixmap, QPainter, QMovie, QIcon, QTransform, QImage, QA
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QFileDialog, QSlider, QWidget, QGraphicsScene, \
QGraphicsView, QGraphicsPixmapItem, QSystemTrayIcon, QMenu, QStyleFactory, QGraphicsItem, QCheckBox
if platform.system() == 'Windows':
import winshell
elif platform.system() == 'Linux':
import subprocess
match platform.system():
case 'Windows':
import winshell
case 'Linux':
pass
case _:
raise RuntimeError("Unsupported platform. This script is designed for Windows or Linux.")
# Set up logging
logging.basicConfig(level=logging.DEBUG)
@@ -89,7 +93,11 @@ class UpdateDeviceThread(QThread):
device.set_configuration()
except usb.core.USBError as e:
if e.errno == errno.EACCES:
raise PermissionError("[Access denied] Please set appropriate udev rules (Linux) or use administrator rights (Windows).")
match platform.system():
case 'Windows':
raise PermissionError("[Access denied] Please use administrator rights.")
case 'Linux':
raise PermissionError("[Access denied] Please set appropriate udev rules.")
elif e.errno == errno.EBUSY:
raise RuntimeError("[Device busy] Device is blocked by another process or the kernel.")
else:
@@ -177,7 +185,9 @@ class MainWindow(QMainWindow):
self.pixmap_item.setPos(QPointF(0, 0))
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(QIcon('icon.ico'))
script_dir = os.path.dirname(os.path.realpath(__file__))
icon_path = os.path.join(script_dir, 'corsair-lcd-tool.png')
self.tray_icon.setIcon(QIcon(icon_path))
self.window_state_handler = WindowStateHandler(self)
@@ -193,7 +203,8 @@ class MainWindow(QMainWindow):
self.container = QWidget(self)
self.container.setGeometry(60, 20, 480, 480)
self.container.setStyleSheet("border: 0px")
# black background so the empty space is not visible on the LCD
self.container.setStyleSheet("background-color: #000000; border: 0px")
self.scene = QGraphicsScene(self.container)
self.view = NoScrollGraphicsView(self.scene, self.container)
@@ -219,16 +230,14 @@ class MainWindow(QMainWindow):
if platform.system() == "Windows":
self.startup_folder = winshell.startup()
self.shortcut_path = os.path.join(self.startup_folder, 'corsair_lcd_tool.lnk')
else:
pass
self.python_path = sys.executable
self.startup_checkbox = QCheckBox("Run at startup", self)
self.startup_checkbox.setGeometry(60, 580, 120, 30)
self.startup_checkbox.clicked.connect(self.update_startup)
if platform.system() == "Windows":
self.startup_checkbox = QCheckBox("Run at startup", self)
self.startup_checkbox.setGeometry(60, 580, 120, 30)
self.startup_checkbox.clicked.connect(self.update_startup)
self.slider = QSlider(Qt.Orientation.Horizontal, self)
self.slider.setGeometry(60, 510, 480, 20)
@@ -250,7 +259,7 @@ class MainWindow(QMainWindow):
self.tray_icon.setContextMenu(self.tray_menu)
self.setWindowIcon(QIcon('icon.ico'))
self.setWindowIcon(QIcon(icon_path))
self.movie = None
logging.debug("MainWindow initialized")
@@ -284,10 +293,11 @@ class MainWindow(QMainWindow):
self.save_state_handler = SaveStateHandler(self)
self.save_state_handler.load_image_state()
if not self.startup_checkbox.isChecked():
self.show()
else:
QTimer.singleShot(5, self.window_state_handler.minimize_window)
if platform.system() == "Windows":
if not self.startup_checkbox.isChecked():
self.show()
else:
QTimer.singleShot(5, self.window_state_handler.minimize_window)
logging.debug("UI Initialized")
def update_startup(self):
@@ -308,36 +318,6 @@ class MainWindow(QMainWindow):
if os.path.exists(self.shortcut_path):
os.remove(self.shortcut_path)
logging.debug("Shortcut removed.")
elif platform.system() == "Linux":
service_name = os.path.basename(self.script_path).replace(".py", ".service")
service_path = os.path.join(os.path.expanduser("~"), ".config/systemd/user", service_name)
service_content = f"""[Unit]
Description=Corsair LCD Tool
[Service]
ExecStart={self.python_path} {self.script_path}
Restart=on-failure
[Install]
WantedBy=default.target
"""
os.makedirs(os.path.dirname(service_path), exist_ok=True)
if state:
with open(service_path, 'w') as f:
f.write(service_content)
subprocess.run(["systemctl", "--user", "daemon-reload"], check=True)
subprocess.run(["systemctl", "--user", "enable", service_name], check=True)
logging.debug("Systemd user service created and enabled.")
else:
if os.path.exists(service_path):
os.remove(service_path)
subprocess.run(["systemctl", "--user", "daemon-reload"], check=True)
subprocess.run(["systemctl", "--user", "disable", service_name], check=True)
logging.debug("Systemd user service removed.")
def open_image(self):
logging.debug("Entering open_image function")
@@ -354,7 +334,7 @@ class MainWindow(QMainWindow):
pixmap.fill(Qt.GlobalColor.transparent)
painter = QPainter(pixmap)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.setBrush(QColor(0, 0, 0, 50))
painter.setBrush(QColor(0, 0, 0, 150))
painter.setPen(Qt.PenStyle.NoPen)
painter.drawRect(0, 0, size, size)
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Clear)
@@ -473,7 +453,8 @@ class SaveStateHandler:
self.main.slider.setValue(state.get('last_slider_value'))
self.main.view.horizontalScrollBar().setValue(state.get('last_scrollbar_pos')[0])
self.main.view.verticalScrollBar().setValue(state.get('last_scrollbar_pos')[1])
self.main.startup_checkbox.setChecked(state.get('startup_checkbox_state', False))
if platform.system() == "Windows":
self.main.startup_checkbox.setChecked(state.get('startup_checkbox_state', False))
logging.debug(f"Loaded state: {state}")
else:
logging.warning(
@@ -504,9 +485,10 @@ class SaveStateHandler:
'last_scene_rect': [self.main.scene.sceneRect().x(), self.main.scene.sceneRect().y(),
self.main.scene.sceneRect().width(), self.main.scene.sceneRect().height()],
'last_scrollbar_pos': [self.main.view.horizontalScrollBar().value(),
self.main.view.verticalScrollBar().value()],
'startup_checkbox_state': self.main.startup_checkbox.isChecked()
self.main.view.verticalScrollBar().value()]
}
if sys.platform.startswith("win"):
state['startup_checkbox_state'] = self.main.startup_checkbox.isChecked()
with open('state.yaml', 'w') as file:
yaml.dump(state, file)
logging.debug(f"Saved state to disk: {state}")

View File

@@ -10,4 +10,4 @@ sudo cp udev/99-corsair.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
echo "✅ Done! Please replug the USB device now."
echo "✅ Done!"

View File

@@ -1,7 +1,5 @@
hidapi
numpy
opencv-python
openrgb-python
PyQt6
pyyaml
winshell

View File

@@ -0,0 +1,63 @@
Name: corsair_lcd_tool_v2
Version: 1.0.0
Release: 1%{?dist}
Summary: Tool for managing Corsair LCDs via Python
License: MIT
URL: https://github.com/shutdown4life/corsair_lcd_tool_v2
Source0: %{name}-%{version}.tar.gz
BuildArch: noarch
BuildRequires: python3-devel
Requires: python3
Requires: python3-numpy
Requires: python3-opencv
Requires: python3-PyQt6
Requires: python3-PyYAML
Requires: python3-pyusb
%description
A Python-based tool to manage Corsair LCD screens. This package includes an installer script and a setup script that runs automatically upon installation.
%prep
%setup -q
%build
# No build step is required for plain Python and shell scripts
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}/usr/share/%{name}
cp -a install.sh corsair_lcd_tool.py %{buildroot}/usr/share/%{name}/
chmod +x %{buildroot}/usr/share/%{name}/install.sh
chmod +x %{buildroot}/usr/share/%{name}/corsair_lcd_tool.py
cp corsair-lcd-tool.png %{buildroot}/usr/share/%{name}/corsair-lcd-tool.png
mkdir -p %{buildroot}/usr/share/applications
cp corsair_lcd_tool.desktop %{buildroot}/usr/share/applications/
mkdir -p %{buildroot}/usr/bin
ln -sr %{buildroot}/usr/share/%{name}/install.sh %{buildroot}/usr/bin/%{name}-install
ln -sr %{buildroot}/usr/share/%{name}/corsair_lcd_tool.py %{buildroot}/usr/bin/corsair-lcd-tool
mkdir -p %{buildroot}/usr/share/icons/hicolor/48x48/apps
cp corsair-lcd-tool.png %{buildroot}/usr/share/icons/hicolor/48x48/apps/corsair-lcd-tool.png
%files
%license LICENSE
%doc README.md
/usr/share/%{name}/install.sh
/usr/share/%{name}/corsair_lcd_tool.py
/usr/share/%{name}/corsair-lcd-tool.png
/usr/bin/%{name}-install
/usr/bin/corsair-lcd-tool
/usr/share/applications/corsair_lcd_tool.desktop
/usr/share/icons/hicolor/48x48/apps/corsair-lcd-tool.png
%post
echo "Running install.sh to set up udev rules..." > /tmp/corsair_lcd_tool_post.log
/bin/bash /usr/share/%{name}/install.sh >> /tmp/corsair_lcd_tool_post.log 2>&1 || echo "install.sh failed" >> /tmp/corsair_lcd_tool_post.log
%changelog
* Thu Jun 19 2025 ShUtDoWn discord @shutdown4life - 1.0.0-1
- Initial RPM release with automatic post-install script