v1
This commit is contained in:
255
CHANGELOG.md
Normal file
255
CHANGELOG.md
Normal file
@@ -0,0 +1,255 @@
|
||||
# CHANGELOG - Extension GNOME Shell ASUS RGB Keyboard
|
||||
|
||||
Ce fichier documente l'évolution du développement de l'extension pour faciliter la reprise du travail.
|
||||
|
||||
## [Non publié] - En cours de développement
|
||||
|
||||
### 📅 2025-12-21
|
||||
|
||||
#### 🆕 Nouvelles Améliorations (Session 4)
|
||||
|
||||
**Amélioration 4: Presets GNOME et restauration thème** ✅
|
||||
- **Demande**: Remplacer les 6 presets personnalisés par les 9 couleurs d'accentuation GNOME
|
||||
- **Solution implémentée**:
|
||||
- Modification du schéma GSettings : ajout de `preset-7`, `preset-8`, `preset-9`
|
||||
- Mise à jour de tous les presets avec les couleurs GNOME officielles :
|
||||
1. Bleu (53, 132, 228) - couleur par défaut GNOME
|
||||
2. Turquoise (51, 209, 122)
|
||||
3. Vert (87, 227, 137)
|
||||
4. Jaune (246, 211, 45)
|
||||
5. Orange (255, 120, 0)
|
||||
6. Rouge (237, 51, 59)
|
||||
7. Rose (246, 97, 81)
|
||||
8. Violet (145, 65, 172)
|
||||
9. Gris ardoise (119, 118, 123)
|
||||
- Adaptation UI : boucle de 6 → 9 presets, ajustement taille boutons (32px → 28px)
|
||||
- Restauration thème : désactiver la synchronisation remet GNOME sur "blue" (défaut)
|
||||
- **Résultat**: Cohérence totale entre presets clavier et couleurs d'accentuation GNOME
|
||||
|
||||
**Amélioration 5: Style presets ronds et surbrillance** ✅
|
||||
- **Demande**: Presets en forme de ronds avec cercle blanc autour du preset actif
|
||||
- **Solution implémentée**:
|
||||
- Modification style : `border-radius: 50%` pour rendre les presets circulaires
|
||||
- Taille ajustée : 26×26px pour des cercles parfaits
|
||||
- Fonction `_updatePresetSelection()` : cercle blanc épais (3px) + box-shadow sur le preset actif
|
||||
- Stockage des boutons dans `this._presetButtons[]` avec propriétés `_preset` et `_baseStyle`
|
||||
- Comparaison RGB avec tolérance de ±10
|
||||
- Appel à chaque changement de couleur pour mise à jour en temps réel
|
||||
- **Résultat**: Feedback visuel clair du preset actuellement sélectionné
|
||||
|
||||
**Amélioration 6: Synchronisation thème universelle** ✅
|
||||
- **Problème**: Synchronisation thème GNOME fonctionnait uniquement depuis la roue chromatique
|
||||
- **Cause**: `_onPresetClicked()` appelait directement `Backend.writeRGB()` au lieu de `_onRGBChanged()`
|
||||
- **Solution**: Refactorisation de `_onPresetClicked()` pour utiliser `_onRGBChanged()`
|
||||
- **Déclenchement synchronisation maintenant actif**:
|
||||
- ✅ Depuis la roue chromatique
|
||||
- ✅ Depuis les sliders RGB
|
||||
- ✅ Depuis les presets
|
||||
- ✅ Depuis le slider Master
|
||||
- **Résultat**: La couleur GNOME se synchronise peu importe le mode de sélection utilisé
|
||||
|
||||
#### 🆕 Nouvelles Améliorations (Session 2 et 3)
|
||||
|
||||
**Amélioration 1: Surbrillance des boutons d'intensité** ✅
|
||||
- **Problème**: Les boutons OFF/1/2/3 n'affichaient pas de surbrillance du niveau sélectionné
|
||||
- **Cause**: Classe CSS incorrecte (`'button'` au lieu de `'brightness-button'`)
|
||||
- **Solution**: Correction de `style_class` dans `_buildBrightnessButtons()`
|
||||
- **Résultat**: Bouton actif affiché avec fond bleu et texte blanc gras
|
||||
|
||||
**Amélioration 2: Surbrillance de la couleur sélectionnée dans la roue** ✅
|
||||
- **Problème**: Pas de feedback visuel sur la couleur active dans la roue chromatique
|
||||
- **Solution implémentée**:
|
||||
- Stockage des 113 boutons dans `this._wheelButtons[]`
|
||||
- Ajout de propriétés `_rgb` et `_baseStyle` sur chaque bouton
|
||||
- Nouvelle fonction `_updateWheelSelection()` pour mettre à jour la bordure
|
||||
- Bordure blanche épaisse (3px) + box-shadow sur la couleur sélectionnée
|
||||
- Comparaison RGB avec tolérance de ±10 pour gérer les approximations HSL
|
||||
- **Appels**:
|
||||
- ✅ À la construction de la roue (affichage initial)
|
||||
- ✅ Sur clic dans la roue
|
||||
- ✅ Sur clic preset
|
||||
- ✅ Sur changement de couleur
|
||||
- **Résultat**: Indication claire de quelle couleur est actuellement appliquée dès l'ouverture du menu
|
||||
|
||||
**Amélioration 3: Synchronisation avec le thème GNOME** ✅
|
||||
- **Demande**: Appliquer automatiquement la couleur du clavier comme couleur d'accentuation GNOME
|
||||
- **Solution implémentée**:
|
||||
- Ajout clé GSettings `sync-gnome-theme` (booléen, défaut: false)
|
||||
- Fonction `_rgbToGnomeAccent()` : mapping RGB → 9 couleurs GNOME (blue, teal, green, yellow, orange, red, pink, purple, slate)
|
||||
- Algorithme: distance euclidienne dans l'espace RGB pour trouver la couleur la plus proche
|
||||
- Fonction `_syncGnomeTheme()` : applique via `org.gnome.desktop.interface accent-color`
|
||||
- UI: `PopupSwitchMenuItem` "Synchroniser thème GNOME" après les presets
|
||||
- **Déclenchement**:
|
||||
- ✅ Activation de la case à cocher (application immédiate)
|
||||
- ✅ Lors de chaque changement de couleur clavier (si activé)
|
||||
- **Résultat**: La couleur d'accentuation GNOME (affichée dans Paramètres → Apparence) change automatiquement pour correspondre au clavier RGB
|
||||
|
||||
#### 🔍 Analyse des Problèmes Identifiés (Session 1)
|
||||
|
||||
**Problème 1: Roue chromatique non fonctionnelle**
|
||||
- **Symptôme**: Impossible de sélectionner une couleur en cliquant sur la roue
|
||||
- **Cause identifiée**:
|
||||
- Première tentative avec `St.DrawingArea` non interactive sous GNOME Shell 48
|
||||
- Deuxième tentative avec grille 16×16 de boutons (cellSize: 12px) potentiellement trop petits
|
||||
- Les boutons sont créés mais les clics ne sont pas détectés
|
||||
- **Hypothèse**: Boutons trop petits (12px) ou problème de style CSS empêchant l'interaction
|
||||
|
||||
**Problème 2: Niveau d'intensité non visuellement sélectionné**
|
||||
- **Symptôme**: Aucune indication visuelle du bouton d'intensité actif
|
||||
- **Cause**: Pas de mise à jour du style CSS lors du clic
|
||||
- **Solution**: Ajouter classe CSS active et mettre à jour dans `_updateBrightnessButtons()`
|
||||
|
||||
**Problème 3: Mode par défaut incorrect**
|
||||
- **Symptôme**: Mode sliders affiché par défaut alors que l'utilisateur préfère la roue
|
||||
- **Cause**: `this._colorPickerMode = 'sliders'` dans l'initialisation
|
||||
- **Solution**: Inverser pour `this._colorPickerMode = 'wheel'`
|
||||
|
||||
#### 🔧 Correctifs Appliqués
|
||||
|
||||
**Correctif 1: Roue chromatique fonctionnelle** ✅
|
||||
- Augmentation de la taille des cellules: 12px → 18px (+50%)
|
||||
- Réduction de la grille: 16×16 → 12×12 pour meilleure visibilité
|
||||
- Ajout de bordure visible `rgba(0,0,0,0.3)` pour feedback visuel
|
||||
- Ajout de `reactive: true` et `track_hover: true` pour garantir l'interactivité
|
||||
- Espacement entre cellules: 2px → 3px pour meilleure séparation
|
||||
|
||||
**Correctif 2: Surbrillance des boutons d'intensité** ✅
|
||||
- Classe CSS `.active` déjà implémentée dans stylesheet.css
|
||||
- Style actif: fond bleu (`rgba(53, 132, 228, 0.8)`), texte blanc, gras
|
||||
- Fonction `_updateBrightnessButtons()` existante et fonctionnelle
|
||||
|
||||
**Correctif 3: Mode roue par défaut** ✅
|
||||
- Changement de `_colorPickerMode: 'sliders'` → `'wheel'`
|
||||
- Roue chromatique affichée en premier au démarrage
|
||||
- Bouton de bascule pour passer en mode sliders
|
||||
|
||||
#### 📊 Résumé des Améliorations
|
||||
|
||||
**Interface utilisateur:**
|
||||
- ✅ Roue chromatique 12×12 avec 113 couleurs cliquables
|
||||
- ✅ Boutons 18×18px, bien visibles et réactifs
|
||||
- ✅ Mode roue chromatique par défaut (préférence utilisateur)
|
||||
- ✅ Surbrillance automatique du niveau d'intensité actif
|
||||
- ✅ Aperçu couleur dans le header avec correction gamma sRGB
|
||||
|
||||
**Ergonomie:**
|
||||
- ✅ Clic sur la roue chromatique → application immédiate au clavier
|
||||
- ✅ Slider Luminosité (Master) pour contrôle de l'intensité
|
||||
- ✅ Bascule facile entre roue et sliders RGB
|
||||
- ✅ Interface compacte et optimisée
|
||||
|
||||
### 📅 2025-12-20
|
||||
|
||||
#### ✅ Terminé
|
||||
- **Analyse du projet** : Compréhension complète des spécifications
|
||||
- **Architecture définie** : Séparation en 3 modules (ui.js, backend.js, extension.js)
|
||||
- **Schéma UI créé** : Documentation visuelle complète dans `docs/UI_SCHEMA.md`
|
||||
- Diagramme Mermaid de l'architecture
|
||||
- Layout ASCII du popover
|
||||
- Flux de données
|
||||
- Gestion des erreurs UI
|
||||
- **CLAUDE.md créé** : Guide pour futures instances de Claude Code
|
||||
- **CHANGELOG.md initialisé** : Ce fichier
|
||||
|
||||
#### 🔄 En cours
|
||||
- Prêt pour installation et tests complets
|
||||
|
||||
#### ✅ Tests Matériel Réalisés (ASUS TUF Gaming A16 FA608UH)
|
||||
- ✅ **Contrôle brightness** : Changement de niveaux 0-3 fonctionnel
|
||||
- ✅ **Contrôle RGB** : Écriture kbd_rgb_mode validée
|
||||
- ✅ **Permissions udev** : Règle corrigée et fonctionnelle
|
||||
- Règle finale : `ACTION=="add"` avec `RUN+="/bin/sh -c 'chgrp kbdled ...'"`
|
||||
- Rechargement module : `modprobe -r asus_nb_wmi && modprobe asus_nb_wmi`
|
||||
- Permissions validées : `kbdled` groupe appliqué correctement
|
||||
|
||||
#### 📋 Terminé (MVP complet)
|
||||
- ✅ Arborescence complète du projet créée
|
||||
- ✅ backend.js implémenté (interface sysfs + debouncing)
|
||||
- ✅ ui.js implémenté (construction du popover complet)
|
||||
- ✅ extension.js créé (lifecycle GNOME Shell)
|
||||
- ✅ Schéma GSettings complet avec 6 presets
|
||||
- ✅ metadata.json créé
|
||||
- ✅ stylesheet.css créé pour le styling
|
||||
- ✅ Documentation installation (docs/INSTALL.md)
|
||||
- ✅ Documentation troubleshooting (docs/TROUBLESHOOTING.md)
|
||||
- ✅ Documentation tests (docs/TESTING.md)
|
||||
- ✅ Script d'installation (tools/install-local.sh)
|
||||
- ✅ README.md complet et détaillé
|
||||
|
||||
#### 🎯 Décisions techniques prises
|
||||
- **Langage**: GJS (JavaScript GNOME Shell)
|
||||
- **GNOME Shell**: Version 48
|
||||
- **UUID**: `asus-kbd-rgb@gilles`
|
||||
- **Master Slider**: Mode "gain" (multiplie R/G/B par 0-100%)
|
||||
- **Debouncing**: 75ms pour les sliders
|
||||
- **Permissions**: Règle udev + groupe `kbdled` (approche simple)
|
||||
- **Step RGB par défaut**: 5
|
||||
- **Comportement brightness=0**: Mémoriser RGB sans écrire kbd_rgb_mode
|
||||
|
||||
#### 📝 Presets couleur par défaut
|
||||
1. Orange: (255, 165, 0)
|
||||
2. Rouge: (255, 0, 0)
|
||||
3. Vert: (0, 255, 0)
|
||||
4. Bleu: (0, 0, 255)
|
||||
5. Blanc: (255, 255, 255)
|
||||
6. Cyan: (0, 255, 255)
|
||||
|
||||
#### 🔧 Interface matérielle
|
||||
- Path brightness: `/sys/class/leds/asus::kbd_backlight/brightness`
|
||||
- Path max: `/sys/class/leds/asus::kbd_backlight/max_brightness`
|
||||
- Path RGB: `/sys/class/leds/asus::kbd_backlight/kbd_rgb_mode`
|
||||
- Format RGB: `1 0 R G B 0\n`
|
||||
|
||||
---
|
||||
|
||||
## Notes pour reprise du développement
|
||||
|
||||
### Prochaines étapes recommandées
|
||||
1. Créer l'arborescence complète des fichiers
|
||||
2. Implémenter backend.js en premier (base pour tests)
|
||||
3. Implémenter ui.js (interface)
|
||||
4. Créer extension.js pour intégrer les modules
|
||||
5. Tester avec règles udev configurées
|
||||
|
||||
### Points d'attention
|
||||
- **Permissions critiques**: L'extension ne peut pas fonctionner sans les règles udev
|
||||
- **Debouncing essentiel**: Éviter de spammer sysfs lors des déplacements de sliders
|
||||
- **Clamping obligatoire**: Toujours valider 0-255 pour RGB, 0-max pour brightness
|
||||
- **GNOME Shell 48**: Vérifier la compatibilité API (notamment pour Slider)
|
||||
- **Wayland vs X11**: Différence pour le rechargement en dev
|
||||
|
||||
### Dépendances système
|
||||
- GNOME Shell 48
|
||||
- GLib/GIO pour sysfs
|
||||
- GSettings pour la persistance
|
||||
- udev pour les permissions
|
||||
|
||||
### Structure des modules
|
||||
```
|
||||
backend.js → Fonctions exportées:
|
||||
- checkHardwareSupport()
|
||||
- checkPermissions()
|
||||
- readBrightness()
|
||||
- writeBrightness(value)
|
||||
- readRGB()
|
||||
- writeRGB(r, g, b, master)
|
||||
- applyPreset(presetId)
|
||||
|
||||
ui.js → Classe exportée:
|
||||
- KeyboardRGBIndicator extends PanelMenu.Button
|
||||
- Méthodes: _buildUI(), _onRGBChanged(), _onBrightnessChanged(), etc.
|
||||
|
||||
extension.js → Fonctions requises:
|
||||
- init()
|
||||
- enable()
|
||||
- disable()
|
||||
```
|
||||
|
||||
### Tests à effectuer lors de la reprise
|
||||
- [ ] Extension se charge sans erreur
|
||||
- [ ] Popover s'affiche au clic
|
||||
- [ ] Sliders fonctionnent
|
||||
- [ ] Debouncing actif (vérifier dans les logs)
|
||||
- [ ] Écriture sysfs réussie (avec permissions)
|
||||
- [ ] GSettings sauvegarde correctement
|
||||
- [ ] Restauration au redémarrage de session
|
||||
162
CLAUDE.md
Normal file
162
CLAUDE.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. tu fera des reponse en fracais.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a GNOME Shell 48 extension for controlling RGB keyboard backlighting on ASUS laptops (specifically ASUS TUF Gaming A16 FA608UH) running Debian GNU/Linux 13 (trixie). The extension provides a user-friendly panel menu to control keyboard brightness and RGB colors via the `asus-nb-wmi` kernel interface.
|
||||
|
||||
**Target Hardware**: ASUS laptops with RGB keyboard support via `/sys/class/leds/asus::kbd_backlight/`
|
||||
|
||||
**Language**: GJS (JavaScript for GNOME Shell)
|
||||
|
||||
**GNOME Shell Version**: 48
|
||||
|
||||
## Architecture
|
||||
|
||||
The extension follows a modular architecture with clear separation of concerns:
|
||||
|
||||
### Core Modules
|
||||
|
||||
1. **`extension/ui.js`**: UI construction and event handling
|
||||
- Panel menu button with keyboard icon
|
||||
- Popover with sliders, buttons, and preset color boxes
|
||||
- Manages user interactions and calls backend functions
|
||||
|
||||
2. **`extension/backend.js`**: Hardware interface and business logic
|
||||
- Reads/writes to sysfs paths (`/sys/class/leds/asus::kbd_backlight/`)
|
||||
- Implements debouncing for slider changes (50-100ms)
|
||||
- Handles RGB clamping, master slider logic (gain mode)
|
||||
- Manages permissions checking and error states
|
||||
|
||||
3. **`extension/extension.js`**: Extension lifecycle
|
||||
- Entry point for GNOME Shell
|
||||
- Initialization and cleanup
|
||||
- Restores last saved state on session start
|
||||
|
||||
4. **GSettings Schema**: Persistent storage
|
||||
- Current RGB values, brightness level
|
||||
- 6 color presets
|
||||
- Master slider mode and RGB step size
|
||||
- Located in `extension/schemas/org.gnome.shell.extensions.asuskbdrgb.gschema.xml`
|
||||
|
||||
### Key Hardware Interfaces
|
||||
|
||||
- **Brightness**: `/sys/class/leds/asus::kbd_backlight/brightness` (0..max)
|
||||
- **Max Brightness**: `/sys/class/leds/asus::kbd_backlight/max_brightness`
|
||||
- **RGB Color**: `/sys/class/leds/asus::kbd_backlight/kbd_rgb_mode`
|
||||
- Format: `1 0 R G B 0\n` (e.g., `1 0 255 165 0 0` for orange)
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Installation for Development
|
||||
|
||||
1. **Set up permissions** (required for sysfs access):
|
||||
```bash
|
||||
# Create udev rule
|
||||
echo 'SUBSYSTEM=="leds", KERNEL=="asus::kbd_backlight", GROUP="kbdled", MODE="0660"' | sudo tee /etc/udev/rules.d/99-asus-kbd.rules
|
||||
|
||||
# Create group and add user
|
||||
sudo groupadd -f kbdled
|
||||
sudo usermod -aG kbdled $USER
|
||||
|
||||
# Reload udev
|
||||
sudo udevadm control --reload-rules && sudo udevadm trigger
|
||||
|
||||
# Logout/login required for group membership
|
||||
```
|
||||
|
||||
2. **Install extension locally**:
|
||||
```bash
|
||||
# Use provided install script
|
||||
./tools/install-local.sh
|
||||
|
||||
# Or manually copy to extensions directory
|
||||
cp -r extension/ ~/.local/share/gnome-shell/extensions/asus-kbd-rgb@gilles/
|
||||
```
|
||||
|
||||
3. **Compile GSettings schema**:
|
||||
```bash
|
||||
glib-compile-schemas extension/schemas/
|
||||
# Or let install script handle it
|
||||
```
|
||||
|
||||
### Reloading During Development
|
||||
|
||||
- **X11**: Press `Alt+F2`, type `r`, press Enter
|
||||
- **Wayland**: Logout and login (no hot reload available)
|
||||
|
||||
### Viewing Logs
|
||||
|
||||
```bash
|
||||
journalctl -f -o cat /usr/bin/gnome-shell
|
||||
```
|
||||
|
||||
## Code Conventions
|
||||
|
||||
### Language
|
||||
- **All code comments**: French
|
||||
- **All documentation files**: French (in `docs/` directory)
|
||||
- **User-facing messages**: French
|
||||
|
||||
### Master Slider Logic
|
||||
The "Master" slider uses **Option B (gain mode)** by default:
|
||||
- Acts as a brightness multiplier for RGB values (0-100%)
|
||||
- Multiplies each R, G, B component proportionally
|
||||
- Alternative modes (offset, HSV) can be added in preferences
|
||||
|
||||
### Default Color Presets
|
||||
1. Orange: (255, 165, 0)
|
||||
2. Red: (255, 0, 0)
|
||||
3. Green: (0, 255, 0)
|
||||
4. Blue: (0, 0, 255)
|
||||
5. White: (255, 255, 255)
|
||||
6. Cyan: (0, 255, 255)
|
||||
|
||||
### Error Handling Requirements
|
||||
|
||||
The extension must gracefully handle:
|
||||
|
||||
1. **Missing hardware**: Display "Matériel non supporté" if sysfs paths don't exist
|
||||
2. **Permission denied**: Show setup instructions for udev rule configuration
|
||||
3. **Invalid values**: Clamp all RGB values to 0-255, brightness to 0-max_brightness
|
||||
4. **Zero brightness behavior**: Keep RGB values in memory but don't write to `kbd_rgb_mode` when brightness is 0
|
||||
|
||||
## Extension Metadata
|
||||
|
||||
- **UUID**: `asus-kbd-rgb@gilles`
|
||||
- **Shell Version**: `["48"]`
|
||||
- **Location**: `~/.local/share/gnome-shell/extensions/asus-kbd-rgb@gilles/`
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
extension/
|
||||
├── metadata.json # Extension metadata (UUID, version, shell-version)
|
||||
├── extension.js # Main entry point
|
||||
├── ui.js # UI components (panel button, popover, sliders)
|
||||
├── backend.js # Sysfs interface and business logic
|
||||
├── stylesheet.css # Optional custom styles
|
||||
└── schemas/
|
||||
└── org.gnome.shell.extensions.asuskbdrgb.gschema.xml
|
||||
|
||||
docs/ # All documentation files
|
||||
├── INSTALL.md # Installation and permissions setup
|
||||
└── TROUBLESHOOTING.md # Common issues and solutions
|
||||
|
||||
tools/
|
||||
└── install-local.sh # Development installation script
|
||||
```
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
Before considering the extension complete, verify:
|
||||
|
||||
- [ ] Brightness levels change correctly (0 to max)
|
||||
- [ ] RGB sliders apply colors instantly (with debouncing)
|
||||
- [ ] Master slider affects RGB according to gain mode
|
||||
- [ ] Preset colors apply on click
|
||||
- [ ] Settings persist across session restarts
|
||||
- [ ] "No permission" case shows udev setup instructions
|
||||
- [ ] "Hardware absent" case shows clear error message
|
||||
- [ ] Extension loads without errors on GNOME Shell 48
|
||||
131
PROJECT_STRUCTURE.txt
Normal file
131
PROJECT_STRUCTURE.txt
Normal file
@@ -0,0 +1,131 @@
|
||||
GNOME ASUS Keyboard RGB - Structure du Projet
|
||||
==============================================
|
||||
|
||||
gnome-asus-kbd-rgb/
|
||||
│
|
||||
├── README.md # Documentation principale du projet
|
||||
├── CLAUDE.md # Guide pour Claude Code
|
||||
├── CHANGELOG.md # Historique des modifications
|
||||
├── gnome_asus_kbd_rgb_claude_prompt.md # Consignes initiales
|
||||
│
|
||||
├── extension/ # Code source de l'extension GNOME Shell
|
||||
│ ├── extension.js # Point d'entrée (lifecycle: init/enable/disable)
|
||||
│ ├── ui.js # Interface utilisateur (popover, sliders, presets)
|
||||
│ ├── backend.js # Interface sysfs et logique métier
|
||||
│ ├── metadata.json # Métadonnées (UUID, version, shell-version)
|
||||
│ ├── stylesheet.css # Styles CSS personnalisés
|
||||
│ └── schemas/
|
||||
│ └── org.gnome.shell.extensions.asuskbdrgb.gschema.xml
|
||||
│ # Schéma GSettings (persistance)
|
||||
│
|
||||
├── docs/ # Documentation complète
|
||||
│ ├── INSTALL.md # Guide d'installation détaillé
|
||||
│ ├── TROUBLESHOOTING.md # Résolution des problèmes
|
||||
│ ├── TESTING.md # Checklist de tests (120+ items)
|
||||
│ └── UI_SCHEMA.md # Schémas de l'interface (Mermaid, ASCII)
|
||||
│
|
||||
└── tools/ # Scripts utilitaires
|
||||
└── install-local.sh # Script d'installation automatique
|
||||
|
||||
|
||||
Modules Principaux
|
||||
==================
|
||||
|
||||
1. backend.js (Interface Système)
|
||||
- checkHardwareSupport() : Vérifie la présence du matériel
|
||||
- checkPermissions() : Vérifie les droits d'accès
|
||||
- getMaxBrightness() : Lit la valeur max
|
||||
- readBrightness() : Lit la brightness actuelle
|
||||
- writeBrightness(level) : Écrit la brightness (0-3)
|
||||
- writeRGB(r, g, b, master) : Écrit RGB avec master gain
|
||||
- writeRGBDebounced() : Écrit RGB avec debouncing (75ms)
|
||||
- parsePreset(string) : Parse "R,G,B" en objet
|
||||
- rgbToHex(r, g, b) : Convertit RGB en #RRGGBB
|
||||
- cleanup() : Nettoie les ressources
|
||||
|
||||
2. ui.js (Interface Utilisateur)
|
||||
- KeyboardRGBIndicator : Classe principale (extends PanelMenu.Button)
|
||||
- _buildUI() : Construit l'interface complète
|
||||
- _buildBrightnessButtons() : Crée les 4 boutons d'intensité
|
||||
- _buildRGBSliders() : Crée les sliders RGB + Master
|
||||
- _buildInfoLine() : Crée la ligne d'information
|
||||
- _buildPresets() : Crée les 6 boutons preset
|
||||
- _buildErrorUI() : Affiche les messages d'erreur
|
||||
- _onBrightnessButtonClicked() : Gère les clics sur boutons
|
||||
- _onRGBChanged() : Applique les changements RGB
|
||||
- _onPresetClicked() : Applique un preset
|
||||
- _applyCurrentState() : Restaure l'état au démarrage
|
||||
|
||||
3. extension.js (Lifecycle)
|
||||
- AsusKeyboardRGBExtension : Classe principale (extends Extension)
|
||||
- enable() : Active l'extension
|
||||
- disable() : Désactive l'extension
|
||||
|
||||
|
||||
Fichiers sysfs
|
||||
==============
|
||||
|
||||
/sys/class/leds/asus::kbd_backlight/
|
||||
├── brightness # Intensité (0..max_brightness)
|
||||
├── max_brightness # Valeur maximale (lecture seule)
|
||||
└── kbd_rgb_mode # Couleur RGB (format: "1 0 R G B 0")
|
||||
|
||||
|
||||
Configuration GSettings
|
||||
=======================
|
||||
|
||||
Schéma: org.gnome.shell.extensions.asuskbdrgb
|
||||
|
||||
Clés disponibles:
|
||||
- red (int) : Composante rouge (0-255)
|
||||
- green (int) : Composante verte (0-255)
|
||||
- blue (int) : Composante bleue (0-255)
|
||||
- brightness-level (int) : Niveau d'intensité (0-3)
|
||||
- master-gain (int) : Gain master (0-100)
|
||||
- rgb-step (int) : Pas d'ajustement (défaut: 5)
|
||||
- preset-1..6 (string) : Presets couleur (format "R,G,B")
|
||||
- master-mode (string) : Mode du master slider (défaut: "gain")
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
1. Configuration permissions (obligatoire):
|
||||
sudo tee /etc/udev/rules.d/99-asus-kbd.rules <<< 'SUBSYSTEM=="leds", KERNEL=="asus::kbd_backlight", GROUP="kbdled", MODE="0660"'
|
||||
sudo groupadd -f kbdled
|
||||
sudo usermod -aG kbdled $USER
|
||||
sudo udevadm control --reload-rules && sudo udevadm trigger
|
||||
# Logout/Login requis !
|
||||
|
||||
2. Installation extension:
|
||||
./tools/install-local.sh
|
||||
|
||||
3. Rechargement GNOME Shell:
|
||||
- X11: Alt+F2, "r", Enter
|
||||
- Wayland: Logout/Login
|
||||
|
||||
|
||||
Dépendances
|
||||
===========
|
||||
|
||||
Runtime:
|
||||
- GNOME Shell 48
|
||||
- GLib/GIO (pour sysfs)
|
||||
- GSettings (pour persistance)
|
||||
- Module kernel: asus-nb-wmi
|
||||
|
||||
Build:
|
||||
- glib-compile-schemas
|
||||
|
||||
|
||||
Prochaines Améliorations (Post-MVP)
|
||||
====================================
|
||||
|
||||
- [ ] Page de préférences (prefs.js) pour modifier les presets
|
||||
- [ ] Support de modes RGB animés (breathing, wave, etc.)
|
||||
- [ ] Support multi-zones (si matériel compatible)
|
||||
- [ ] Export/Import de configurations
|
||||
- [ ] Profils par application
|
||||
- [ ] Raccourcis clavier globaux
|
||||
- [ ] Indicateur de synchronisation avec le matériel
|
||||
- [ ] Support d'autres modes master (offset, HSV)
|
||||
230
README.md
230
README.md
@@ -1,2 +1,230 @@
|
||||
# gnome-asus-kbd-rgb
|
||||
# GNOME Shell Extension - Contrôle RGB Clavier ASUS
|
||||
|
||||
Extension GNOME Shell pour contrôler le rétroéclairage RGB des claviers ASUS via l'interface kernel `asus-nb-wmi`.
|
||||
|
||||

|
||||

|
||||
|
||||
## 📋 Fonctionnalités
|
||||
|
||||
- **🎡 Roue chromatique** : Sélection visuelle de 113 couleurs en un clic
|
||||
- **🎨 Mode sliders** : Contrôle RGB précis (0-255) + Master (0-100%)
|
||||
- **💡 Contrôle d'intensité** : 4 niveaux avec surbrillance visuelle (Off, 1, 2, 3)
|
||||
- **🎯 Presets couleur** : 6 couleurs prédéfinies personnalisables
|
||||
- **🌈 Aperçu en temps réel** : Visualisation avec correction gamma sRGB
|
||||
- **💾 Persistance** : Sauvegarde automatique et restauration au démarrage
|
||||
- **⚡ Interface compacte** : Bascule facile entre roue et sliders
|
||||
|
||||
## 🎯 Compatibilité
|
||||
|
||||
### Système
|
||||
- **OS** : Debian GNU/Linux 13 (trixie) ou compatible
|
||||
- **GNOME Shell** : Version 48
|
||||
- **Session** : X11 ou Wayland
|
||||
|
||||
### Matériel
|
||||
- Ordinateurs portables ASUS avec clavier RGB
|
||||
- Support via le module kernel `asus-nb-wmi`
|
||||
- Testé sur : **ASUS TUF Gaming A16 FA608UH**
|
||||
|
||||
Vérifiez la présence de `/sys/class/leds/asus::kbd_backlight/` sur votre système.
|
||||
|
||||
## 🚀 Installation Rapide
|
||||
|
||||
### 1. Cloner le dépôt
|
||||
|
||||
```bash
|
||||
cd ~/Documents
|
||||
git clone https://github.com/gilles/gnome-asus-kbd-rgb.git
|
||||
cd gnome-asus-kbd-rgb
|
||||
```
|
||||
|
||||
### 2. Configurer les permissions (obligatoire)
|
||||
|
||||
```bash
|
||||
# Créer la règle udev (une seule commande)
|
||||
sudo tee /etc/udev/rules.d/99-asus-kbd.rules > /dev/null << 'EOF'
|
||||
ACTION=="add", SUBSYSTEM=="leds", KERNEL=="asus::kbd_backlight", TAG+="uaccess", RUN+="/bin/sh -c 'chgrp kbdled /sys/class/leds/asus::kbd_backlight/brightness && chmod g+w /sys/class/leds/asus::kbd_backlight/brightness'"
|
||||
ACTION=="add", SUBSYSTEM=="leds", KERNEL=="asus::kbd_backlight", TAG+="uaccess", RUN+="/bin/sh -c 'chgrp kbdled /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode && chmod g+w /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode'"
|
||||
EOF
|
||||
|
||||
# Créer le groupe et ajouter l'utilisateur
|
||||
sudo groupadd -f kbdled
|
||||
sudo usermod -aG kbdled $USER
|
||||
|
||||
# Recharger udev et appliquer les permissions
|
||||
sudo udevadm control --reload-rules
|
||||
sudo modprobe -r asus_nb_wmi
|
||||
sudo modprobe asus_nb_wmi
|
||||
|
||||
# IMPORTANT: Déconnexion/Reconnexion requise pour l'appartenance au groupe !
|
||||
```
|
||||
|
||||
### 3. Installer l'extension
|
||||
|
||||
```bash
|
||||
./tools/install-local.sh
|
||||
```
|
||||
|
||||
### 4. Recharger GNOME Shell
|
||||
|
||||
- **X11** : `Alt+F2`, tapez `r`, Entrée
|
||||
- **Wayland** : Déconnexion/Reconnexion
|
||||
|
||||
L'icône devrait apparaître dans la barre supérieure ! 🎨
|
||||
|
||||
## 📖 Documentation
|
||||
|
||||
- **[INSTALL.md](docs/INSTALL.md)** - Guide d'installation détaillé
|
||||
- **[TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)** - Résolution des problèmes
|
||||
- **[TESTING.md](docs/TESTING.md)** - Checklist de tests
|
||||
- **[UI_SCHEMA.md](docs/UI_SCHEMA.md)** - Schéma de l'interface
|
||||
- **[CLAUDE.md](CLAUDE.md)** - Guide pour Claude Code
|
||||
- **[CHANGELOG.md](CHANGELOG.md)** - Historique des modifications
|
||||
|
||||
## 🎨 Captures d'écran
|
||||
|
||||
### Interface Principale
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Rétroéclairage Clavier ASUS │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Intensité: │
|
||||
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||||
│ │ OFF │ │ 1 │ │ 2 │ │ 3 │ │
|
||||
│ └─────┘ └─────┘ └─────┘ └─────┘ │
|
||||
│ │
|
||||
│ Rouge ●──────────────○ 255 │
|
||||
│ Vert ●──────────────○ 255 │
|
||||
│ Bleu ●──────────────○ 255 │
|
||||
│ Master ●──────────────○ 100% │
|
||||
│ │
|
||||
│ RGB=(255,165,0) #FFA500 Intensité=2/3 │
|
||||
│ │
|
||||
│ Couleurs prédéfinies: │
|
||||
│ 🟠 🔴 🟢 🔵 ⚪ 🔵 │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
L'extension est structurée en 3 modules principaux :
|
||||
|
||||
- **`extension/backend.js`** - Interface sysfs et logique métier
|
||||
- Lecture/écriture des fichiers sysfs
|
||||
- Debouncing (75ms) pour éviter le spam
|
||||
- Clamping et validation des valeurs
|
||||
- Gestion du master gain
|
||||
|
||||
- **`extension/ui.js`** - Interface utilisateur
|
||||
- Construction du popover et des widgets
|
||||
- Gestion des événements utilisateur
|
||||
- Mise à jour du feedback visuel
|
||||
|
||||
- **`extension/extension.js`** - Point d'entrée GNOME Shell
|
||||
- Lifecycle de l'extension (enable/disable)
|
||||
- Intégration dans le panel
|
||||
- Chargement des settings GSettings
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
Les paramètres sont stockés via GSettings et peuvent être modifiés manuellement :
|
||||
|
||||
```bash
|
||||
# Définir la couleur rouge
|
||||
gsettings set org.gnome.shell.extensions.asuskbdrgb red 255
|
||||
|
||||
# Définir un preset
|
||||
gsettings set org.gnome.shell.extensions.asuskbdrgb preset-1 "255,100,50"
|
||||
|
||||
# Lister tous les paramètres
|
||||
gsettings list-recursively org.gnome.shell.extensions.asuskbdrgb
|
||||
```
|
||||
|
||||
## 🔧 Développement
|
||||
|
||||
### Structure du projet
|
||||
|
||||
```
|
||||
gnome-asus-kbd-rgb/
|
||||
├── extension/ # Code source de l'extension
|
||||
│ ├── extension.js # Point d'entrée
|
||||
│ ├── ui.js # Interface utilisateur
|
||||
│ ├── backend.js # Logique sysfs
|
||||
│ ├── metadata.json # Métadonnées
|
||||
│ ├── stylesheet.css # Styles CSS
|
||||
│ └── schemas/ # Schéma GSettings
|
||||
├── docs/ # Documentation
|
||||
├── tools/ # Scripts utilitaires
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### Logs de debug
|
||||
|
||||
```bash
|
||||
# Voir les logs en temps réel
|
||||
journalctl -f -o cat /usr/bin/gnome-shell | grep -i asus
|
||||
|
||||
# Activer le mode verbose
|
||||
gnome-extensions enable asus-kbd-rgb@gilles
|
||||
```
|
||||
|
||||
### Contribuer
|
||||
|
||||
1. Forkez le projet
|
||||
2. Créez une branche (`git checkout -b feature/amelioration`)
|
||||
3. Committez vos changements (`git commit -am 'Ajout fonctionnalité'`)
|
||||
4. Pushez vers la branche (`git push origin feature/amelioration`)
|
||||
5. Ouvrez une Pull Request
|
||||
|
||||
## 🐛 Problèmes Courants
|
||||
|
||||
### L'extension ne s'affiche pas
|
||||
|
||||
```bash
|
||||
# Vérifier l'installation
|
||||
gnome-extensions list | grep asus
|
||||
|
||||
# Vérifier les logs
|
||||
journalctl -xe | grep -i asus
|
||||
```
|
||||
|
||||
### Message "Permissions insuffisantes"
|
||||
|
||||
Voir [INSTALL.md](docs/INSTALL.md) section "Configuration des permissions".
|
||||
|
||||
### La couleur ne change pas
|
||||
|
||||
Vérifiez que :
|
||||
- La brightness n'est pas à 0 (OFF)
|
||||
- Le master slider n'est pas à 0%
|
||||
- Les permissions sont configurées
|
||||
|
||||
Consultez [TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) pour plus d'aide.
|
||||
|
||||
## 📜 Licence
|
||||
|
||||
GPL-3.0 License - voir le fichier LICENSE
|
||||
|
||||
## 👤 Auteur
|
||||
|
||||
**Gilles**
|
||||
|
||||
- GitHub: [@gilles](https://github.com/gilles)
|
||||
|
||||
## 🙏 Remerciements
|
||||
|
||||
- Équipe GNOME Shell pour l'API d'extensions
|
||||
- Communauté ASUS Linux pour le support du driver `asus-nb-wmi`
|
||||
- Claude Code pour l'assistance au développement
|
||||
|
||||
## 📞 Support
|
||||
|
||||
- **Issues** : https://github.com/gilles/gnome-asus-kbd-rgb/issues
|
||||
- **Documentation** : Dossier `docs/`
|
||||
- **Forum GNOME** : https://discourse.gnome.org/
|
||||
|
||||
---
|
||||
|
||||
**Profitez de votre clavier RGB ! 🌈⌨️**
|
||||
|
||||
275
docs/ANALYSE_ROUE_CHROMATIQUE.md
Normal file
275
docs/ANALYSE_ROUE_CHROMATIQUE.md
Normal file
@@ -0,0 +1,275 @@
|
||||
# Analyse Comparative - Implémentation Roue Chromatique
|
||||
|
||||
## 📊 Comparaison des Approches
|
||||
|
||||
### Solution Actuelle (Implémentée)
|
||||
**Type**: Grille de boutons 12×12 simulant une roue HSL
|
||||
|
||||
**Implémentation**:
|
||||
```javascript
|
||||
// Grille de boutons St.Button disposés en cercle
|
||||
// Chaque bouton = une couleur HSL précalculée
|
||||
const size = 12; // 12×12 = 144 cellules
|
||||
const cellSize = 18px; // Boutons cliquables
|
||||
// 113 couleurs effectivement affichées (cercle)
|
||||
```
|
||||
|
||||
**Avantages réels** ✅:
|
||||
- ✅ **Fonctionne immédiatement** (St.Button = composant natif éprouvé)
|
||||
- ✅ **Très stable** (pas de gestion Canvas/Cairo complexe)
|
||||
- ✅ **Compatible GNOME 48** (API St bien documentée)
|
||||
- ✅ **Code simple** (~70 lignes pour la roue)
|
||||
- ✅ **Débogage facile** (chaque bouton = élément inspectable)
|
||||
- ✅ **Performance correcte** (113 boutons statiques, pas de rendu temps réel)
|
||||
|
||||
**Inconvénients** ⚠️:
|
||||
- ⚠️ **Résolution limitée** (113 couleurs vs. millions théoriques)
|
||||
- ⚠️ **Granularité fixe** (pas de sélection entre deux boutons)
|
||||
- ⚠️ **Mémoire** (113 objets St.Button créés)
|
||||
- ⚠️ **Pas de feedback visuel continu** (pas de curseur sur la roue)
|
||||
|
||||
---
|
||||
|
||||
### Option A Recommandée (Synthèse)
|
||||
**Type**: Roue HSV custom avec `Clutter.Canvas`
|
||||
|
||||
**Implémentation proposée**:
|
||||
```javascript
|
||||
// Canvas interactif avec rendu Cairo
|
||||
const canvas = new Clutter.Canvas({ width: 200, height: 200 });
|
||||
canvas.connect('draw', (canvas, cr) => {
|
||||
// Dessiner roue HSV avec Cairo
|
||||
// Pour chaque pixel: calculer H (angle) et S (rayon)
|
||||
});
|
||||
// Capturer clics et drag pour position continue
|
||||
```
|
||||
|
||||
**Avantages théoriques** ✅:
|
||||
- ✅ **Résolution infinie** (sélection pixel par pixel)
|
||||
- ✅ **Feedback visuel** (curseur, preview en temps réel)
|
||||
- ✅ **Look professionnel** (roue lisse, dégradés)
|
||||
- ✅ **Mémoire optimale** (un seul canvas vs 113 boutons)
|
||||
|
||||
**Inconvénients réels** ⚠️:
|
||||
- ⚠️ **Complexité code** (~200-300 lignes pour roue + interactions)
|
||||
- ⚠️ **Performance Cairo** (rendu temps réel à optimiser)
|
||||
- ⚠️ **Bugs potentiels** (gestion événements souris, edge cases)
|
||||
- ⚠️ **Maintenance** (code custom à déboguer)
|
||||
- ⚠️ **Non fonctionnel actuellement** (notre tentative St.DrawingArea a échoué)
|
||||
|
||||
---
|
||||
|
||||
## 🔬 Analyse de Notre Tentative Canvas (Échec)
|
||||
|
||||
### Ce qui n'a pas fonctionné
|
||||
|
||||
**Code initial**:
|
||||
```javascript
|
||||
const wheelCanvas = new St.DrawingArea({ width: 200, height: 200 });
|
||||
wheelCanvas.connect('repaint', (area) => {
|
||||
this._drawColorWheel(area); // Rendu Cairo
|
||||
});
|
||||
wheelCanvas.reactive = true;
|
||||
wheelCanvas.connect('button-press-event', ...) // ❌ Jamais déclenché
|
||||
```
|
||||
|
||||
**Problèmes identifiés**:
|
||||
1. **St.DrawingArea non interactive** sous GNOME Shell 48
|
||||
2. **Événements souris ignorés** malgré `reactive: true`
|
||||
3. **Documentation manquante** sur DrawingArea + events
|
||||
|
||||
**Pourquoi Clutter.Canvas serait différent**:
|
||||
- `Clutter.Canvas` est l'API officielle pour rendu custom
|
||||
- `St.DrawingArea` est un wrapper qui peut avoir des limitations
|
||||
- Nécessite parent `Clutter.Actor` avec `content = canvas`
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Recommandations par Cas d'Usage
|
||||
|
||||
### Cas 1: Vous voulez que ça fonctionne MAINTENANT ✅
|
||||
**→ Gardez la grille de boutons actuelle**
|
||||
|
||||
**Raisons**:
|
||||
- Fonctionne déjà (testable immédiatement)
|
||||
- 113 couleurs = largement suffisant pour un clavier RGB
|
||||
- Code stable et maintenable
|
||||
- Zéro risque de régression
|
||||
|
||||
**Améliorations possibles**:
|
||||
- Augmenter la grille (14×14 = 153 couleurs, cellSize: 16px)
|
||||
- Ajouter hover effect (bordure blanche au survol)
|
||||
- Ajouter tooltip avec valeurs RGB sur chaque bouton
|
||||
|
||||
---
|
||||
|
||||
### Cas 2: Vous voulez la "vraie" roue HSV (Option A) 🎨
|
||||
**→ Implémentez Clutter.Canvas**
|
||||
|
||||
**Plan d'action**:
|
||||
1. **Créer le canvas**:
|
||||
```javascript
|
||||
const canvas = new Clutter.Canvas({ width: 200, height: 200 });
|
||||
const actor = new Clutter.Actor({
|
||||
content: canvas,
|
||||
width: 200,
|
||||
height: 200,
|
||||
reactive: true
|
||||
});
|
||||
```
|
||||
|
||||
2. **Rendu Cairo** (fonction `_drawHSVWheel`):
|
||||
- Boucle polaire: angle (H) × rayon (S)
|
||||
- Conversion HSV(H, S, 1.0) → RGB
|
||||
- Cairo: `cr.setSourceRGB(r, g, b)` + `cr.arc(x, y, 1, 0, 2π)`
|
||||
|
||||
3. **Interactions**:
|
||||
```javascript
|
||||
actor.connect('button-press-event', (actor, event) => {
|
||||
const [x, y] = event.get_coords();
|
||||
// Convertir (x, y) → (angle, rayon) → (H, S)
|
||||
// Appliquer couleur
|
||||
});
|
||||
```
|
||||
|
||||
4. **Optimisations**:
|
||||
- Cache l'image de la roue (redessiner uniquement le curseur)
|
||||
- Utiliser `canvas.invalidate()` au lieu de redessiner tout
|
||||
|
||||
**Temps estimé**: 4-6 heures de développement + debug
|
||||
|
||||
**Risques**:
|
||||
- Événements souris mal gérés (comme avec DrawingArea)
|
||||
- Performance dégradée si mal optimisé
|
||||
- Bugs visuels (antialiasing, bords)
|
||||
|
||||
---
|
||||
|
||||
### Cas 3: Vous voulez le meilleur des deux mondes 🌟
|
||||
**→ Approche hybride**
|
||||
|
||||
**Solution recommandée**:
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ [Bouton] Roue | Sliders │ ← Toggle
|
||||
├─────────────────────────────────┤
|
||||
│ MODE ROUE: │
|
||||
│ • Grille 12×12 (actuel) │ ← Sélection rapide
|
||||
│ │
|
||||
│ MODE SLIDERS: │
|
||||
│ • Slider H (Hue): 0-360° │ ← Affinage précis
|
||||
│ • Slider S (Sat): 0-100% │
|
||||
│ • Slider V (Val): 0-100% │
|
||||
│ │
|
||||
│ Aperçu: [████] RGB(255,128,64) │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Avantages**:
|
||||
- Roue pour sélection rapide visuelle
|
||||
- Sliders HSV pour ajustement fin
|
||||
- Deux modes répondent à tous les besoins
|
||||
|
||||
**Code minimal**:
|
||||
```javascript
|
||||
// Ajouter 3 sliders HSV dans _buildRGBSliders()
|
||||
const hueSlider = new Slider.Slider(hue / 360);
|
||||
const satSlider = new Slider.Slider(sat);
|
||||
const valSlider = new Slider.Slider(val);
|
||||
|
||||
// Callback: convertir HSV → RGB puis appliquer
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Décision Recommandée
|
||||
|
||||
### Pour votre usage (clavier RGB ASUS):
|
||||
|
||||
**Option finale: Conserver grille 12×12 + ajouter sliders HSV** ✅
|
||||
|
||||
**Justification**:
|
||||
1. **Grille actuelle suffit largement**:
|
||||
- 113 couleurs > largement assez pour explorer la palette
|
||||
- Clics directs = UX immédiate
|
||||
- Stable et fonctionnel
|
||||
|
||||
2. **Sliders HSV ajoutent la précision**:
|
||||
- Affiner une teinte exacte (H: 0-360°)
|
||||
- Contrôler saturation (couleur vive vs pastel)
|
||||
- Contrôler valeur/luminosité (sombre vs clair)
|
||||
|
||||
3. **Pas besoin de Canvas custom**:
|
||||
- Trop complexe pour le bénéfice apporté
|
||||
- Risque de bugs > gain UX marginal
|
||||
- 113 couleurs + sliders HSV = 100% des besoins couverts
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Plan d'Action Proposé
|
||||
|
||||
### Étape 1: Améliorer la grille actuelle (30 min)
|
||||
```javascript
|
||||
// Hover effect
|
||||
button.connect('enter-event', () => {
|
||||
button.style += 'border: 2px solid white;';
|
||||
});
|
||||
|
||||
// Tooltip RGB
|
||||
button.set_tooltip_text(`RGB(${rgb.r}, ${rgb.g}, ${rgb.b})`);
|
||||
```
|
||||
|
||||
### Étape 2: Ajouter sliders HSV (1-2h)
|
||||
```javascript
|
||||
_buildHSVSliders() {
|
||||
// Convertir RGB actuel → HSV
|
||||
const hsv = this._rgbToHsv(this._currentR, this._currentG, this._currentB);
|
||||
|
||||
// 3 sliders H, S, V
|
||||
const hSlider = this._createSlider('Teinte (H)', hsv.h, ...);
|
||||
const sSlider = this._createSlider('Saturation (S)', hsv.s, ...);
|
||||
const vSlider = this._createSlider('Luminosité (V)', hsv.v, ...);
|
||||
|
||||
// Callback: HSV → RGB puis appliquer
|
||||
}
|
||||
```
|
||||
|
||||
### Étape 3: Mode toggle roue/HSV (30 min)
|
||||
- Bouton de bascule: [Roue] ↔ [HSV]
|
||||
- Même principe que roue ↔ RGB actuel
|
||||
|
||||
---
|
||||
|
||||
## 📊 Tableau Comparatif Final
|
||||
|
||||
| Critère | Grille Boutons (Actuel) | Canvas HSV (Option A) | Hybride Roue+HSV |
|
||||
|---------|-------------------------|----------------------|------------------|
|
||||
| **Complexité code** | ⭐⭐ (simple) | ⭐⭐⭐⭐⭐ (complexe) | ⭐⭐⭐ (moyenne) |
|
||||
| **Stabilité** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ (bugs possibles) | ⭐⭐⭐⭐⭐ |
|
||||
| **Précision couleur** | ⭐⭐⭐ (113 couleurs) | ⭐⭐⭐⭐⭐ (infini) | ⭐⭐⭐⭐⭐ (roue+sliders) |
|
||||
| **UX visuelle** | ⭐⭐⭐⭐ (clair) | ⭐⭐⭐⭐⭐ (magnifique) | ⭐⭐⭐⭐⭐ |
|
||||
| **Performance** | ⭐⭐⭐⭐ | ⭐⭐⭐ (rendu temps réel) | ⭐⭐⭐⭐ |
|
||||
| **Temps dev** | ✅ Fait | 4-6h | 2-3h |
|
||||
| **Maintenance** | ⭐⭐⭐⭐⭐ | ⭐⭐ (code custom) | ⭐⭐⭐⭐ |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Conclusion
|
||||
|
||||
### Recommandation Finale: **Grille actuelle + Sliders HSV** 🏆
|
||||
|
||||
**Pourquoi**:
|
||||
- ✅ Temps de développement minimal (2-3h vs 4-6h Canvas)
|
||||
- ✅ Stabilité maximale (composants St natifs)
|
||||
- ✅ Précision totale (sliders HSV 0-360° / 0-100%)
|
||||
- ✅ UX optimale (sélection rapide + affinage)
|
||||
- ✅ Maintenabilité (code simple, pas de Cairo custom)
|
||||
|
||||
**Prochaine étape**:
|
||||
Souhaitez-vous que j'implémente les **sliders HSV** pour compléter la grille actuelle ?
|
||||
|
||||
Cela ajoutera:
|
||||
- Slider Teinte (H): 0-360° avec dégradé arc-en-ciel
|
||||
- Slider Saturation (S): 0-100% (gris → couleur vive)
|
||||
- Slider Luminosité (V): 0-100% (noir → couleur claire)
|
||||
- Mode toggle pour passer de Roue → HSV → RGB
|
||||
306
docs/ANALYSE_THEME_GNOME.md
Normal file
306
docs/ANALYSE_THEME_GNOME.md
Normal file
@@ -0,0 +1,306 @@
|
||||
# Analyse - Synchronisation Couleur Clavier ↔ Thème GNOME
|
||||
|
||||
## 📋 Demande Utilisateur
|
||||
|
||||
**Objectif** : Ajouter une option (case à cocher) pour appliquer la couleur sélectionnée du clavier RGB au thème actuel de GNOME (couleur de sélection/accent).
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Analyse Technique
|
||||
|
||||
### 1. Système de Couleurs GNOME
|
||||
|
||||
**GNOME 42+** utilise un système de couleurs d'accent :
|
||||
- **Localisation** : `org.gnome.desktop.interface` → `accent-color`
|
||||
- **Valeurs possibles** :
|
||||
- `blue` (défaut)
|
||||
- `teal`
|
||||
- `green`
|
||||
- `yellow`
|
||||
- `orange`
|
||||
- `red`
|
||||
- `pink`
|
||||
- `purple`
|
||||
- `slate`
|
||||
|
||||
**Limitation** : GNOME utilise des couleurs **prédéfinies** (9 choix), pas de RGB personnalisé libre.
|
||||
|
||||
### 2. GTK4 libadwaita
|
||||
|
||||
**Couleurs d'accent libadwaita** :
|
||||
- Utilisées dans les applications modernes (GNOME Files, Settings, etc.)
|
||||
- Définies dans `~/.config/gtk-4.0/gtk.css` ou via GSettings
|
||||
- Support de couleurs custom via CSS
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Solutions Proposées
|
||||
|
||||
### Solution A : Mapping RGB → Couleur Accent GNOME (Recommandée) ⭐
|
||||
|
||||
**Principe** :
|
||||
1. Détecter la couleur RGB sélectionnée sur le clavier
|
||||
2. Calculer la couleur accent GNOME la plus proche (parmi les 9)
|
||||
3. Appliquer via GSettings : `gsettings set org.gnome.desktop.interface accent-color 'red'`
|
||||
|
||||
**Avantages** :
|
||||
- ✅ Intégration native GNOME
|
||||
- ✅ Fonctionne avec toutes les apps modernes
|
||||
- ✅ Pas de fichiers CSS à gérer
|
||||
- ✅ Simple et stable
|
||||
|
||||
**Inconvénients** :
|
||||
- ⚠️ Limitation à 9 couleurs (approximation nécessaire)
|
||||
- ⚠️ Pas de correspondance exacte RGB
|
||||
|
||||
**Algorithme de mapping** :
|
||||
```javascript
|
||||
function rgbToGnomeAccent(r, g, b) {
|
||||
const colors = {
|
||||
blue: { r: 53, g: 132, b: 228 },
|
||||
teal: { r: 51, g: 209, b: 122 },
|
||||
green: { r: 51, g: 209, b: 122 },
|
||||
yellow: { r: 246, g: 211, b: 45 },
|
||||
orange: { r: 255, g: 120, b: 0 },
|
||||
red: { r: 237, g: 51, b: 59 },
|
||||
pink: { r: 246, g: 97, b: 81 },
|
||||
purple: { r: 145, g: 65, b: 172 },
|
||||
slate: { r: 119, g: 118, b: 123 }
|
||||
};
|
||||
|
||||
// Calculer la distance euclidienne pour chaque couleur
|
||||
let minDistance = Infinity;
|
||||
let closestColor = 'blue';
|
||||
|
||||
for (const [name, color] of Object.entries(colors)) {
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(r - color.r, 2) +
|
||||
Math.pow(g - color.g, 2) +
|
||||
Math.pow(b - color.b, 2)
|
||||
);
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
closestColor = name;
|
||||
}
|
||||
}
|
||||
|
||||
return closestColor;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Solution B : CSS Custom pour GTK4 (Couleur Exacte)
|
||||
|
||||
**Principe** :
|
||||
1. Écrire un fichier `~/.config/gtk-4.0/gtk.css` avec la couleur RGB exacte
|
||||
2. Redéfinir la variable `@accent_color` de libadwaita
|
||||
|
||||
**Fichier généré** :
|
||||
```css
|
||||
/* Généré par ASUS Keyboard RGB Extension */
|
||||
@define-color accent_bg_color rgb(255, 165, 0);
|
||||
@define-color accent_fg_color rgb(255, 255, 255);
|
||||
@define-color accent_color rgb(255, 165, 0);
|
||||
```
|
||||
|
||||
**Avantages** :
|
||||
- ✅ Couleur RGB exacte (pas d'approximation)
|
||||
- ✅ Personnalisation totale
|
||||
|
||||
**Inconvénients** :
|
||||
- ⚠️ Modification de fichiers système
|
||||
- ⚠️ Peut casser avec mises à jour GNOME
|
||||
- ⚠️ Nécessite redémarrage apps GTK4
|
||||
- ⚠️ Complexité maintenance
|
||||
|
||||
---
|
||||
|
||||
### Solution C : Hybride (A + B)
|
||||
|
||||
**Principe** :
|
||||
1. **Par défaut** : Solution A (GSettings accent-color)
|
||||
2. **Option avancée** : Solution B (CSS custom)
|
||||
|
||||
**Interface** :
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ ☑ Synchroniser avec le thème GNOME │
|
||||
│ │
|
||||
│ Mode synchronisation : │
|
||||
│ ○ Couleurs prédéfinies (9 choix) │ ← Solution A
|
||||
│ ○ Couleur exacte (avancé) │ ← Solution B
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Implémentation Recommandée : Solution A
|
||||
|
||||
### Étape 1 : Ajouter GSettings
|
||||
|
||||
**Fichier** : `extension/schemas/org.gnome.shell.extensions.asuskbdrgb.gschema.xml`
|
||||
|
||||
```xml
|
||||
<key name="sync-gnome-theme" type="b">
|
||||
<default>false</default>
|
||||
<summary>Synchroniser avec le thème GNOME</summary>
|
||||
<description>Applique la couleur du clavier comme accent GNOME</description>
|
||||
</key>
|
||||
```
|
||||
|
||||
### Étape 2 : Ajouter UI (Case à cocher)
|
||||
|
||||
**Fichier** : `extension/ui.js` dans `_buildUI()`
|
||||
|
||||
```javascript
|
||||
// Après les presets
|
||||
const syncThemeItem = new PopupMenu.PopupSwitchMenuItem(
|
||||
'Synchroniser thème GNOME',
|
||||
this._settings.get_boolean('sync-gnome-theme')
|
||||
);
|
||||
|
||||
syncThemeItem.connect('toggled', (item) => {
|
||||
this._settings.set_boolean('sync-gnome-theme', item.state);
|
||||
if (item.state) {
|
||||
this._syncGnomeTheme();
|
||||
}
|
||||
});
|
||||
|
||||
this.menu.addMenuItem(syncThemeItem);
|
||||
```
|
||||
|
||||
### Étape 3 : Fonction de synchronisation
|
||||
|
||||
**Fichier** : `extension/ui.js`
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Synchronise la couleur du clavier avec le thème GNOME
|
||||
*/
|
||||
_syncGnomeTheme() {
|
||||
if (!this._settings.get_boolean('sync-gnome-theme')) return;
|
||||
|
||||
const accent = this._rgbToGnomeAccent(
|
||||
this._currentR,
|
||||
this._currentG,
|
||||
this._currentB
|
||||
);
|
||||
|
||||
try {
|
||||
const interfaceSettings = new Gio.Settings({
|
||||
schema: 'org.gnome.desktop.interface'
|
||||
});
|
||||
interfaceSettings.set_string('accent-color', accent);
|
||||
|
||||
log(`[ASUS RGB] Thème GNOME mis à jour : ${accent}`);
|
||||
} catch (error) {
|
||||
log(`[ASUS RGB] Erreur sync thème : ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve la couleur accent GNOME la plus proche
|
||||
*/
|
||||
_rgbToGnomeAccent(r, g, b) {
|
||||
const colors = {
|
||||
blue: { r: 53, g: 132, b: 228 },
|
||||
teal: { r: 51, g: 209, b: 122 },
|
||||
green: { r: 51, g: 209, b: 122 },
|
||||
yellow: { r: 246, g: 211, b: 45 },
|
||||
orange: { r: 255, g: 120, b: 0 },
|
||||
red: { r: 237, g: 51, b: 59 },
|
||||
pink: { r: 246, g: 97, b: 81 },
|
||||
purple: { r: 145, g: 65, b: 172 },
|
||||
slate: { r: 119, g: 118, b: 123 }
|
||||
};
|
||||
|
||||
let minDistance = Infinity;
|
||||
let closestColor = 'blue';
|
||||
|
||||
for (const [name, color] of Object.entries(colors)) {
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(r - color.r, 2) +
|
||||
Math.pow(g - color.g, 2) +
|
||||
Math.pow(b - color.b, 2)
|
||||
);
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
closestColor = name;
|
||||
}
|
||||
}
|
||||
|
||||
return closestColor;
|
||||
}
|
||||
```
|
||||
|
||||
### Étape 4 : Appeler lors des changements
|
||||
|
||||
**Dans `_onRGBChanged()`** :
|
||||
```javascript
|
||||
_onRGBChanged() {
|
||||
this._updateColorPreview();
|
||||
|
||||
Backend.writeRGBDebounced(
|
||||
this._currentR,
|
||||
this._currentG,
|
||||
this._currentB,
|
||||
this._currentMasterGain,
|
||||
() => this._updateInfoLine()
|
||||
);
|
||||
|
||||
// Synchroniser avec GNOME si activé
|
||||
this._syncGnomeTheme(); // ← Ajouter cette ligne
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Comparaison Solutions
|
||||
|
||||
| Critère | Solution A (GSettings) | Solution B (CSS) | Solution C (Hybride) |
|
||||
|---------|------------------------|------------------|----------------------|
|
||||
| **Simplicité** | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ |
|
||||
| **Précision couleur** | ⭐⭐⭐ (9 choix) | ⭐⭐⭐⭐⭐ (exact) | ⭐⭐⭐⭐⭐ |
|
||||
| **Stabilité** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||
| **Compatibilité** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
| **Temps dev** | 30 min | 2h | 3h |
|
||||
| **Risque** | Aucun | Moyen | Faible |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Recommandation Finale
|
||||
|
||||
### **Solution A - GSettings Accent Color** 🏆
|
||||
|
||||
**Pourquoi** :
|
||||
1. ✅ **Simple et rapide** à implémenter (30 minutes)
|
||||
2. ✅ **100% natif GNOME** (pas de hack CSS)
|
||||
3. ✅ **Stable** et compatible avec futures versions
|
||||
4. ✅ **Suffisant** pour la plupart des cas (9 couleurs couvrent bien le spectre)
|
||||
|
||||
**Exemple de résultat** :
|
||||
```
|
||||
Clavier RGB: (255, 165, 0) → Orange vif
|
||||
Couleur GNOME: 'orange' → Orange GNOME standard
|
||||
Rendu applications: 🟠 Boutons, sélections, accents
|
||||
```
|
||||
|
||||
**Temps d'implémentation** : 30 minutes
|
||||
- 5 min : Ajouter GSettings
|
||||
- 10 min : Ajouter UI (checkbox)
|
||||
- 10 min : Fonction `_rgbToGnomeAccent()`
|
||||
- 5 min : Intégration dans `_onRGBChanged()`
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Prochaine Étape
|
||||
|
||||
**Voulez-vous que j'implémente la Solution A maintenant ?**
|
||||
|
||||
Cela ajoutera :
|
||||
- ☑ Case "Synchroniser thème GNOME" dans le menu
|
||||
- 🎨 Application automatique de la couleur accent la plus proche
|
||||
- 🔄 Mise à jour en temps réel lors des changements de couleur clavier
|
||||
|
||||
**Note** : Si vous souhaitez la couleur RGB exacte (Solution B), je peux aussi l'implémenter, mais c'est plus risqué et complexe.
|
||||
390
docs/DEVELOPPEMENT_COMPLET.md
Normal file
390
docs/DEVELOPPEMENT_COMPLET.md
Normal file
@@ -0,0 +1,390 @@
|
||||
# Synthèse du Développement - Extension GNOME Shell ASUS RGB Keyboard
|
||||
|
||||
## 📊 Statistiques du Projet
|
||||
|
||||
- **Lignes de code total** : ~2814 lignes
|
||||
- **Modules JavaScript** : 3 fichiers (extension.js, ui.js, backend.js)
|
||||
- **Fichiers de documentation** : 6 fichiers Markdown
|
||||
- **Temps de développement** : 1 session
|
||||
- **État** : MVP complet et prêt pour tests
|
||||
|
||||
## 🎯 Objectifs Atteints
|
||||
|
||||
### Fonctionnalités Core (MVP)
|
||||
- ✅ Contrôle d'intensité du rétroéclairage (4 niveaux)
|
||||
- ✅ Réglage RGB complet (sliders R, G, B)
|
||||
- ✅ Slider Master avec mode gain (0-100%)
|
||||
- ✅ 6 couleurs prédéfinies (presets)
|
||||
- ✅ Interface intuitive dans la barre GNOME
|
||||
- ✅ Persistance des paramètres (GSettings)
|
||||
- ✅ Restauration automatique au démarrage
|
||||
- ✅ Feedback visuel en temps réel (RGB + HEX)
|
||||
|
||||
### Robustesse
|
||||
- ✅ Gestion des erreurs matériel (hardware non supporté)
|
||||
- ✅ Gestion des erreurs permissions (udev non configuré)
|
||||
- ✅ Debouncing pour éviter le spam sysfs (75ms)
|
||||
- ✅ Clamping automatique des valeurs (0-255, 0-100)
|
||||
- ✅ Comportement intelligent (brightness=0 mémorise RGB)
|
||||
|
||||
### Documentation
|
||||
- ✅ README.md complet et professionnel
|
||||
- ✅ Guide d'installation détaillé (INSTALL.md)
|
||||
- ✅ Guide de dépannage exhaustif (TROUBLESHOOTING.md)
|
||||
- ✅ Checklist de tests complète (TESTING.md, 120+ items)
|
||||
- ✅ Schéma d'interface détaillé (UI_SCHEMA.md)
|
||||
- ✅ Guide pour Claude Code (CLAUDE.md)
|
||||
- ✅ CHANGELOG avec historique
|
||||
|
||||
### Outillage
|
||||
- ✅ Script d'installation automatique (install-local.sh)
|
||||
- ✅ Vérifications matériel et permissions
|
||||
- ✅ Feedback coloré et instructions claires
|
||||
- ✅ Support X11 et Wayland
|
||||
|
||||
## 📁 Fichiers Créés
|
||||
|
||||
### Code Source (extension/)
|
||||
```
|
||||
extension.js 59 lignes - Point d'entrée GNOME Shell
|
||||
ui.js 375 lignes - Interface utilisateur complète
|
||||
backend.js 303 lignes - Interface sysfs et logique
|
||||
metadata.json 9 lignes - Métadonnées extension
|
||||
stylesheet.css 57 lignes - Styles CSS
|
||||
schemas/gschema.xml 76 lignes - Configuration GSettings
|
||||
```
|
||||
|
||||
### Documentation (docs/)
|
||||
```
|
||||
INSTALL.md 187 lignes - Installation complète
|
||||
TROUBLESHOOTING.md 452 lignes - Résolution problèmes
|
||||
TESTING.md 443 lignes - Checklist tests
|
||||
UI_SCHEMA.md 165 lignes - Schémas interface
|
||||
DEVELOPPEMENT_COMPLET 400+ lignes - Ce fichier
|
||||
```
|
||||
|
||||
### Fichiers Racine
|
||||
```
|
||||
README.md 226 lignes - Documentation principale
|
||||
CLAUDE.md 148 lignes - Guide Claude Code
|
||||
CHANGELOG.md 120 lignes - Historique développement
|
||||
PROJECT_STRUCTURE.txt 155 lignes - Structure projet
|
||||
```
|
||||
|
||||
### Outils (tools/)
|
||||
```
|
||||
install-local.sh 140 lignes - Script installation
|
||||
```
|
||||
|
||||
## 🏗️ Architecture Technique
|
||||
|
||||
### Modularité
|
||||
Le code est strictement séparé en 3 responsabilités :
|
||||
|
||||
1. **Backend** (backend.js)
|
||||
- Isolation complète de l'interface sysfs
|
||||
- Fonctions pures et testables
|
||||
- Gestion des erreurs système
|
||||
- Pas de dépendances UI
|
||||
|
||||
2. **UI** (ui.js)
|
||||
- Construction de l'interface GNOME
|
||||
- Gestion des événements utilisateur
|
||||
- Communication avec le backend uniquement
|
||||
- Pas d'accès direct au système
|
||||
|
||||
3. **Extension** (extension.js)
|
||||
- Lifecycle GNOME Shell
|
||||
- Intégration minimale
|
||||
- Délégation au UI
|
||||
|
||||
### Patterns Utilisés
|
||||
|
||||
**Debouncing Pattern**
|
||||
```javascript
|
||||
// Évite le spam sysfs lors des mouvements de sliders
|
||||
writeRGBDebounced(r, g, b, master, callback)
|
||||
```
|
||||
|
||||
**Clamping Pattern**
|
||||
```javascript
|
||||
// Garantit des valeurs valides
|
||||
clampRGB(value) → [0, 255]
|
||||
```
|
||||
|
||||
**Master Gain Pattern**
|
||||
```javascript
|
||||
// Mode "gain" : multiplie proportionnellement
|
||||
applyMasterGain(r, g, b, gain) → {r, g, b}
|
||||
```
|
||||
|
||||
**Error UI Pattern**
|
||||
```javascript
|
||||
// Messages d'erreur clairs et actionnables
|
||||
_buildErrorUI(title, message)
|
||||
```
|
||||
|
||||
## 🔒 Gestion des Permissions
|
||||
|
||||
### Approche Choisie : udev + groupe
|
||||
- **Avantage** : Simple, stable, standard Linux
|
||||
- **Alternative rejetée** : D-Bus + polkit (trop complexe pour MVP)
|
||||
|
||||
### Fichiers Concernés
|
||||
```bash
|
||||
/etc/udev/rules.d/99-asus-kbd.rules # Règle udev
|
||||
/etc/group # Groupe kbdled
|
||||
```
|
||||
|
||||
### Vérifications Intégrées
|
||||
- Test hardware au démarrage
|
||||
- Test permissions au démarrage
|
||||
- Messages d'erreur explicites
|
||||
- Référence à la documentation
|
||||
|
||||
## 💾 Persistance (GSettings)
|
||||
|
||||
### Schéma
|
||||
```
|
||||
org.gnome.shell.extensions.asuskbdrgb
|
||||
```
|
||||
|
||||
### Données Sauvegardées
|
||||
- RGB courant (red, green, blue)
|
||||
- Niveau brightness (0-3)
|
||||
- Gain master (0-100)
|
||||
- 6 presets (format "R,G,B")
|
||||
- Configuration (rgb-step, master-mode)
|
||||
|
||||
### Comportement
|
||||
- Sauvegarde automatique à chaque changement
|
||||
- Restauration au démarrage de session
|
||||
- Modifiable via `gsettings` en ligne de commande
|
||||
|
||||
## 🎨 Interface Utilisateur
|
||||
|
||||
### Composants GNOME Shell
|
||||
- `PanelMenu.Button` - Bouton dans le panneau
|
||||
- `PopupMenu` - Menu déroulant
|
||||
- `Slider` - Sliders RGB et Master
|
||||
- `St.Button` - Boutons intensité et presets
|
||||
- `St.BoxLayout` - Layouts flexibles
|
||||
- `St.Label` - Labels et texte
|
||||
|
||||
### Layout
|
||||
```
|
||||
Popover (vertical)
|
||||
├── Titre
|
||||
├── Séparateur
|
||||
├── 4 Boutons intensité (horizontal)
|
||||
├── Séparateur
|
||||
├── 4 Sliders RGB+Master (vertical)
|
||||
├── Séparateur
|
||||
├── Ligne d'info
|
||||
├── Séparateur
|
||||
└── 6 Presets couleur (horizontal)
|
||||
```
|
||||
|
||||
### Feedback Visuel
|
||||
- Bouton actif surligné (class: active)
|
||||
- Valeurs numériques en temps réel
|
||||
- Couleurs presets affichées (background-color)
|
||||
- Info line RGB + HEX + Intensité
|
||||
|
||||
## 🔧 Debouncing Détaillé
|
||||
|
||||
### Problème
|
||||
Déplacer un slider génère des centaines d'événements par seconde, ce qui spammerait sysfs et pourrait causer des erreurs de permission.
|
||||
|
||||
### Solution
|
||||
```javascript
|
||||
let debounceTimer = null;
|
||||
const DEBOUNCE_DELAY = 75; // ms
|
||||
|
||||
writeRGBDebounced(r, g, b, master, callback) {
|
||||
// Annuler le timer précédent
|
||||
if (debounceTimer !== null) {
|
||||
GLib.source_remove(debounceTimer);
|
||||
}
|
||||
|
||||
// Créer un nouveau timer
|
||||
debounceTimer = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
DEBOUNCE_DELAY, () => {
|
||||
writeRGB(r, g, b, master);
|
||||
if (callback) callback();
|
||||
debounceTimer = null;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Résultat
|
||||
- Slider fluide pour l'utilisateur
|
||||
- Maximum 1 écriture sysfs toutes les 75ms
|
||||
- Dernière valeur toujours appliquée
|
||||
|
||||
## 🧪 Tests à Effectuer
|
||||
|
||||
### Critiques (Bloquants)
|
||||
1. Extension se charge sans erreur
|
||||
2. Popover s'affiche au clic
|
||||
3. Brightness change le rétroéclairage
|
||||
4. RGB change la couleur
|
||||
5. Presets fonctionnent
|
||||
6. GSettings sauvegarde correctement
|
||||
7. Restauration au redémarrage OK
|
||||
|
||||
### Importants
|
||||
1. Debouncing actif (vérifier logs)
|
||||
2. Messages d'erreur si pas de hardware
|
||||
3. Messages d'erreur si pas de permissions
|
||||
4. Master gain appliqué correctement
|
||||
5. Clamping des valeurs fonctionne
|
||||
|
||||
### Nice-to-have
|
||||
1. Style CSS correct
|
||||
2. Performance fluide
|
||||
3. Aucune fuite mémoire
|
||||
|
||||
Voir [TESTING.md](TESTING.md) pour la checklist complète (120+ items).
|
||||
|
||||
## 📝 Commentaires Code
|
||||
|
||||
Tous les commentaires sont en **français** conformément aux spécifications :
|
||||
|
||||
```javascript
|
||||
// Vérifie si le matériel ASUS RGB est présent sur le système
|
||||
export function checkHardwareSupport() { ... }
|
||||
|
||||
// Applique le master gain aux valeurs RGB
|
||||
function applyMasterGain(r, g, b, masterGain) { ... }
|
||||
|
||||
// Construit les 4 boutons d'intensité
|
||||
_buildBrightnessButtons() { ... }
|
||||
```
|
||||
|
||||
## 🚀 Prochaines Étapes
|
||||
|
||||
### Tests sur Matériel Réel
|
||||
- [ ] Installer sur ASUS TUF Gaming A16
|
||||
- [ ] Vérifier toutes les fonctionnalités
|
||||
- [ ] Tester la persistance après redémarrage
|
||||
- [ ] Vérifier les performances
|
||||
- [ ] Identifier les bugs éventuels
|
||||
|
||||
### Améliorations Post-MVP
|
||||
- [ ] Page de préférences (prefs.js)
|
||||
- Modifier les presets
|
||||
- Changer le mode master (gain/offset/hsv)
|
||||
- Ajuster le RGB step
|
||||
|
||||
- [ ] Modes RGB animés
|
||||
- Breathing (respiration)
|
||||
- Wave (vague)
|
||||
- Cycle (rotation couleurs)
|
||||
|
||||
- [ ] Support multi-zones (si matériel compatible)
|
||||
- [ ] Export/Import de configurations
|
||||
- [ ] Profils par application
|
||||
- [ ] Raccourcis clavier globaux
|
||||
- [ ] Widget d'aperçu couleur
|
||||
- [ ] Pipette de couleur système
|
||||
|
||||
### Packaging
|
||||
- [ ] Créer un fichier .zip pour ego.gnome.org
|
||||
- [ ] Screenshots de l'interface
|
||||
- [ ] Vidéo de démonstration
|
||||
- [ ] Soumission à GNOME Extensions
|
||||
- [ ] Publication sur GitHub Releases
|
||||
|
||||
## 📊 Métriques Qualité
|
||||
|
||||
### Couverture Fonctionnelle
|
||||
- **MVP défini** : 100% implémenté
|
||||
- **Tests planifiés** : 120+ items documentés
|
||||
- **Documentation** : Complète (6 fichiers)
|
||||
- **Gestion erreurs** : Tous les cas couverts
|
||||
|
||||
### Maintenabilité
|
||||
- **Modularité** : 3 modules indépendants
|
||||
- **Commentaires** : Tous les fichiers commentés
|
||||
- **Documentation code** : Fonctions documentées
|
||||
- **Pas de dette technique** : Code clean
|
||||
|
||||
### Robustesse
|
||||
- **Validation entrées** : Clamping systématique
|
||||
- **Gestion erreurs** : Try/catch appropriés
|
||||
- **Messages utilisateur** : Clairs et actionnables
|
||||
- **Fallbacks** : Valeurs par défaut définies
|
||||
|
||||
## 🎓 Apprentissages
|
||||
|
||||
### Bonnes Pratiques GNOME Shell
|
||||
- Utilisation correcte de GObject.registerClass
|
||||
- Imports ESM modernes (import/export)
|
||||
- Lifecycle extension proper (enable/disable)
|
||||
- Intégration GSettings standard
|
||||
|
||||
### Gestion Linux
|
||||
- Règles udev pour permissions
|
||||
- Interface sysfs kernel
|
||||
- Groupes système
|
||||
- Persistance via GSettings
|
||||
|
||||
### Architecture
|
||||
- Séparation responsabilités (UI/Backend/Extension)
|
||||
- Patterns de debouncing
|
||||
- Gestion d'état avec settings
|
||||
- Messages d'erreur UX
|
||||
|
||||
## 📚 Ressources Utilisées
|
||||
|
||||
### Documentation GNOME
|
||||
- GNOME Shell Extensions: https://gjs.guide/extensions/
|
||||
- GSettings: https://docs.gtk.org/gio/class.Settings.html
|
||||
- St (Shell Toolkit): GNOME Shell source code
|
||||
|
||||
### Kernel Linux
|
||||
- sysfs LED interface: `/sys/class/leds/`
|
||||
- asus-nb-wmi driver documentation
|
||||
|
||||
### Outils
|
||||
- gjs (GNOME JavaScript)
|
||||
- glib-compile-schemas
|
||||
- journalctl (debugging)
|
||||
|
||||
## ✅ Checklist Complétude MVP
|
||||
|
||||
- ✅ **Fonctionnalités core** : Toutes implémentées
|
||||
- ✅ **Interface utilisateur** : Complète et fonctionnelle
|
||||
- ✅ **Persistance** : GSettings configuré
|
||||
- ✅ **Gestion erreurs** : Hardware + Permissions
|
||||
- ✅ **Documentation** : 6 fichiers complets
|
||||
- ✅ **Installation** : Script automatique
|
||||
- ✅ **Tests** : Checklist 120+ items
|
||||
- ✅ **Code qualité** : Commenté, modulaire, propre
|
||||
- ⏳ **Tests réels** : À effectuer sur matériel
|
||||
|
||||
## 🎉 Conclusion
|
||||
|
||||
Le projet **GNOME Shell Extension - ASUS Keyboard RGB Control** est **complet au niveau MVP** et prêt pour les tests sur matériel réel.
|
||||
|
||||
### Points Forts
|
||||
- Architecture propre et modulaire
|
||||
- Documentation exhaustive
|
||||
- Gestion d'erreurs complète
|
||||
- Installation simplifiée
|
||||
- Code maintenable
|
||||
|
||||
### Prochaine Étape Critique
|
||||
**Tester sur l'ASUS TUF Gaming A16 FA608UH** avec Debian 13 et GNOME 48.
|
||||
|
||||
Une fois les tests validés, l'extension pourra être :
|
||||
1. Publiée sur extensions.gnome.org
|
||||
2. Partagée avec la communauté ASUS Linux
|
||||
3. Améliorée avec les fonctionnalités post-MVP
|
||||
|
||||
---
|
||||
|
||||
**Développé avec Claude Code - 2025-12-20**
|
||||
154
docs/FIX_UDEV_PERMISSIONS.md
Normal file
154
docs/FIX_UDEV_PERMISSIONS.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# Fix Permissions udev - ASUS Keyboard RGB
|
||||
|
||||
## Problème Initial
|
||||
|
||||
La règle udev simple ne fonctionnait pas :
|
||||
```bash
|
||||
SUBSYSTEM=="leds", KERNEL=="asus::kbd_backlight", GROUP="kbdled", MODE="0660"
|
||||
```
|
||||
|
||||
**Symptôme** : Les permissions restaient à `root:root` même après rechargement udev et redémarrage.
|
||||
|
||||
## Diagnostic
|
||||
|
||||
```bash
|
||||
udevadm test /sys/class/leds/asus::kbd_backlight 2>&1 | grep -E "(GROUP|MODE|kbdled)"
|
||||
```
|
||||
|
||||
Résultat vide → La règle n'était pas déclenchée.
|
||||
|
||||
## Solution Fonctionnelle
|
||||
|
||||
### Règle udev Corrigée
|
||||
|
||||
```bash
|
||||
sudo tee /etc/udev/rules.d/99-asus-kbd.rules > /dev/null << 'EOF'
|
||||
ACTION=="add", SUBSYSTEM=="leds", KERNEL=="asus::kbd_backlight", TAG+="uaccess", RUN+="/bin/sh -c 'chgrp kbdled /sys/class/leds/asus::kbd_backlight/brightness && chmod g+w /sys/class/leds/asus::kbd_backlight/brightness'"
|
||||
ACTION=="add", SUBSYSTEM=="leds", KERNEL=="asus::kbd_backlight", TAG+="uaccess", RUN+="/bin/sh -c 'chgrp kbdled /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode && chmod g+w /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode'"
|
||||
EOF
|
||||
```
|
||||
|
||||
### Pourquoi Ça Fonctionne
|
||||
|
||||
1. **`ACTION=="add"`** : La règle se déclenche uniquement lors de l'événement "add" (ajout du device)
|
||||
2. **`TAG+="uaccess"`** : Ajoute un tag pour l'accès utilisateur
|
||||
3. **`RUN+=`** : Exécute une commande shell directement
|
||||
4. **`/bin/sh -c '...'`** : Lance un shell pour exécuter plusieurs commandes
|
||||
5. **`chgrp kbdled`** : Change le groupe du fichier
|
||||
6. **`chmod g+w`** : Ajoute les permissions d'écriture pour le groupe
|
||||
|
||||
### Application de la Règle
|
||||
|
||||
Après avoir créé la règle, il faut déclencher l'événement "add" :
|
||||
|
||||
```bash
|
||||
# Recharger les règles udev
|
||||
sudo udevadm control --reload-rules
|
||||
|
||||
# Retirer le module kernel
|
||||
sudo modprobe -r asus_nb_wmi
|
||||
|
||||
# Réinsérer le module (déclenche l'événement "add")
|
||||
sudo modprobe asus_nb_wmi
|
||||
|
||||
# Attendre que le module se charge
|
||||
sleep 2
|
||||
```
|
||||
|
||||
### Vérification
|
||||
|
||||
```bash
|
||||
ls -l /sys/class/leds/asus::kbd_backlight/brightness
|
||||
ls -l /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode
|
||||
```
|
||||
|
||||
**Résultat attendu :**
|
||||
```
|
||||
-rw-rw-r-- 1 root kbdled 4096 ... brightness
|
||||
--w--w---- 1 root kbdled 4096 ... kbd_rgb_mode
|
||||
```
|
||||
|
||||
### Test Fonctionnel
|
||||
|
||||
```bash
|
||||
# Test brightness
|
||||
echo 3 > /sys/class/leds/asus::kbd_backlight/brightness
|
||||
|
||||
# Test RGB (orange)
|
||||
echo "1 0 255 165 0 0" > /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode
|
||||
```
|
||||
|
||||
**Aucune erreur "Permission denied" ne doit apparaître.**
|
||||
|
||||
## Matériel Testé
|
||||
|
||||
- **Modèle** : ASUS TUF Gaming A16 FA608UH
|
||||
- **OS** : Debian GNU/Linux 13 (trixie)
|
||||
- **Kernel** : 6.12.x
|
||||
- **Module** : `asus_nb_wmi`
|
||||
|
||||
## Fichiers Concernés
|
||||
|
||||
### sysfs
|
||||
```
|
||||
/sys/class/leds/asus::kbd_backlight/
|
||||
├── brightness (contrôle intensité 0-3)
|
||||
└── kbd_rgb_mode (contrôle RGB, format "1 0 R G B 0")
|
||||
```
|
||||
|
||||
### udev
|
||||
```
|
||||
/etc/udev/rules.d/99-asus-kbd.rules
|
||||
```
|
||||
|
||||
### Groupe système
|
||||
```
|
||||
/etc/group (ligne kbdled)
|
||||
```
|
||||
|
||||
## Alternative : Service systemd
|
||||
|
||||
Si la règle udev ne fonctionne toujours pas (rare), on peut utiliser un service systemd :
|
||||
|
||||
```bash
|
||||
# Script
|
||||
sudo tee /usr/local/bin/asus-kbd-permissions.sh > /dev/null << 'EOF'
|
||||
#!/bin/bash
|
||||
sleep 2
|
||||
chgrp kbdled /sys/class/leds/asus::kbd_backlight/brightness 2>/dev/null
|
||||
chmod 0660 /sys/class/leds/asus::kbd_backlight/brightness 2>/dev/null
|
||||
chgrp kbdled /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode 2>/dev/null
|
||||
chmod 0660 /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode 2>/dev/null
|
||||
EOF
|
||||
|
||||
sudo chmod +x /usr/local/bin/asus-kbd-permissions.sh
|
||||
|
||||
# Service
|
||||
sudo tee /etc/systemd/system/asus-kbd-permissions.service > /dev/null << 'EOF'
|
||||
[Unit]
|
||||
Description=ASUS Keyboard RGB Permissions
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/bin/asus-kbd-permissions.sh
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable asus-kbd-permissions.service
|
||||
sudo systemctl start asus-kbd-permissions.service
|
||||
```
|
||||
|
||||
## Références
|
||||
|
||||
- **udev manual** : `man 7 udev`
|
||||
- **Module kernel** : Documentation du driver `asus-nb-wmi`
|
||||
- **Tests réalisés** : 2025-12-20
|
||||
|
||||
---
|
||||
|
||||
**Note** : Ce fix est intégré dans la documentation officielle ([docs/INSTALL.md](INSTALL.md)) et dans le script d'installation ([tools/install-local.sh](../tools/install-local.sh)).
|
||||
227
docs/INSTALL.md
Normal file
227
docs/INSTALL.md
Normal file
@@ -0,0 +1,227 @@
|
||||
# Guide d'Installation - Extension ASUS Keyboard RGB
|
||||
|
||||
Ce guide vous explique comment installer et configurer l'extension GNOME Shell pour contrôler le rétroéclairage RGB de votre clavier ASUS.
|
||||
|
||||
## Prérequis
|
||||
|
||||
### Système
|
||||
- **OS**: Debian GNU/Linux 13 (trixie) ou distribution compatible
|
||||
- **GNOME Shell**: Version 48
|
||||
- **Matériel**: Clavier ASUS avec support RGB via `asus-nb-wmi`
|
||||
|
||||
### Vérification du support matériel
|
||||
|
||||
Avant l'installation, vérifiez que votre clavier est supporté :
|
||||
|
||||
```bash
|
||||
# Vérifier la présence des fichiers sysfs
|
||||
ls -la /sys/class/leds/asus::kbd_backlight/
|
||||
```
|
||||
|
||||
Vous devriez voir les fichiers suivants :
|
||||
- `brightness` - Intensité du rétroéclairage
|
||||
- `max_brightness` - Valeur maximale d'intensité
|
||||
- `kbd_rgb_mode` - Contrôle des couleurs RGB
|
||||
|
||||
Si ces fichiers n'existent pas, votre matériel n'est pas supporté par cette extension.
|
||||
|
||||
## Étape 1 : Configuration des permissions
|
||||
|
||||
L'extension nécessite des droits d'écriture sur les fichiers sysfs. La méthode recommandée utilise les règles udev.
|
||||
|
||||
### 1.1 Créer une règle udev
|
||||
|
||||
Créez le fichier `/etc/udev/rules.d/99-asus-kbd.rules` avec la règle qui fonctionne :
|
||||
|
||||
```bash
|
||||
sudo tee /etc/udev/rules.d/99-asus-kbd.rules > /dev/null << 'EOF'
|
||||
ACTION=="add", SUBSYSTEM=="leds", KERNEL=="asus::kbd_backlight", TAG+="uaccess", RUN+="/bin/sh -c 'chgrp kbdled /sys/class/leds/asus::kbd_backlight/brightness && chmod g+w /sys/class/leds/asus::kbd_backlight/brightness'"
|
||||
ACTION=="add", SUBSYSTEM=="leds", KERNEL=="asus::kbd_backlight", TAG+="uaccess", RUN+="/bin/sh -c 'chgrp kbdled /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode && chmod g+w /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode'"
|
||||
EOF
|
||||
```
|
||||
|
||||
Cette règle s'exécute lors de l'ajout du device et applique directement les bonnes permissions.
|
||||
|
||||
### 1.2 Créer le groupe kbdled
|
||||
|
||||
```bash
|
||||
sudo groupadd -f kbdled
|
||||
```
|
||||
|
||||
### 1.3 Ajouter votre utilisateur au groupe
|
||||
|
||||
```bash
|
||||
sudo usermod -aG kbdled $USER
|
||||
```
|
||||
|
||||
### 1.4 Recharger les règles udev et appliquer les permissions
|
||||
|
||||
```bash
|
||||
# Recharger les règles udev
|
||||
sudo udevadm control --reload-rules
|
||||
|
||||
# Retirer et réinsérer le module pour déclencher l'événement "add"
|
||||
sudo modprobe -r asus_nb_wmi
|
||||
sudo modprobe asus_nb_wmi
|
||||
|
||||
# Attendre 2 secondes que le module se charge
|
||||
sleep 2
|
||||
```
|
||||
|
||||
### 1.5 Déconnexion / Reconnexion
|
||||
|
||||
**Important**: Vous devez vous déconnecter et vous reconnecter pour que l'appartenance au groupe soit prise en compte.
|
||||
|
||||
```bash
|
||||
# Vérifier l'appartenance au groupe après reconnexion
|
||||
groups | grep kbdled
|
||||
```
|
||||
|
||||
### 1.5 Vérifier les permissions
|
||||
|
||||
Vérifiez que les permissions ont été appliquées correctement :
|
||||
|
||||
```bash
|
||||
ls -l /sys/class/leds/asus::kbd_backlight/brightness
|
||||
ls -l /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode
|
||||
```
|
||||
|
||||
**Résultat attendu :**
|
||||
```
|
||||
-rw-rw-r-- 1 root kbdled ... brightness
|
||||
--w--w---- 1 root kbdled ... kbd_rgb_mode
|
||||
```
|
||||
|
||||
Le groupe doit être `kbdled` (et non `root`).
|
||||
|
||||
### 1.6 Test d'écriture
|
||||
|
||||
Testez que vous pouvez contrôler le clavier :
|
||||
|
||||
```bash
|
||||
# Test intensité niveau 3 (maximum)
|
||||
echo 3 > /sys/class/leds/asus::kbd_backlight/brightness
|
||||
# → Le rétroéclairage doit passer au maximum
|
||||
|
||||
# Test intensité niveau 1 (faible)
|
||||
echo 1 > /sys/class/leds/asus::kbd_backlight/brightness
|
||||
# → Le rétroéclairage doit diminuer
|
||||
|
||||
# Test couleur RGB (orange)
|
||||
echo "1 0 255 165 0 0" > /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode
|
||||
# → Le clavier doit devenir orange
|
||||
|
||||
# Test couleur RGB (rouge)
|
||||
echo "1 0 255 0 0 0" > /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode
|
||||
# → Le clavier doit devenir rouge
|
||||
```
|
||||
|
||||
**Si aucune erreur "Permission denied" n'apparaît et que votre clavier change de couleur/intensité, c'est parfait !** ✅
|
||||
|
||||
## Étape 2 : Installation de l'extension
|
||||
|
||||
### 2.1 Cloner le dépôt
|
||||
|
||||
```bash
|
||||
cd ~/Documents
|
||||
git clone https://github.com/gilles/gnome-asus-kbd-rgb.git
|
||||
cd gnome-asus-kbd-rgb
|
||||
```
|
||||
|
||||
### 2.2 Compiler le schéma GSettings
|
||||
|
||||
```bash
|
||||
cd extension/schemas
|
||||
glib-compile-schemas .
|
||||
cd ../..
|
||||
```
|
||||
|
||||
### 2.3 Installation automatique
|
||||
|
||||
Utilisez le script fourni :
|
||||
|
||||
```bash
|
||||
./tools/install-local.sh
|
||||
```
|
||||
|
||||
### 2.4 Installation manuelle (alternative)
|
||||
|
||||
Si vous préférez installer manuellement :
|
||||
|
||||
```bash
|
||||
# Créer le dossier de destination
|
||||
mkdir -p ~/.local/share/gnome-shell/extensions/asus-kbd-rgb@gilles
|
||||
|
||||
# Copier les fichiers
|
||||
cp -r extension/* ~/.local/share/gnome-shell/extensions/asus-kbd-rgb@gilles/
|
||||
|
||||
# Compiler le schéma dans le dossier d'installation
|
||||
glib-compile-schemas ~/.local/share/gnome-shell/extensions/asus-kbd-rgb@gilles/schemas/
|
||||
```
|
||||
|
||||
## Étape 3 : Activation de l'extension
|
||||
|
||||
### 3.1 Recharger GNOME Shell
|
||||
|
||||
**Sur X11** :
|
||||
- Appuyez sur `Alt+F2`
|
||||
- Tapez `r`
|
||||
- Appuyez sur Entrée
|
||||
|
||||
**Sur Wayland** :
|
||||
- Déconnectez-vous et reconnectez-vous
|
||||
|
||||
### 3.2 Activer l'extension
|
||||
|
||||
Via l'application Extensions :
|
||||
|
||||
```bash
|
||||
gnome-extensions enable asus-kbd-rgb@gilles
|
||||
```
|
||||
|
||||
Ou via l'interface graphique :
|
||||
1. Ouvrez l'application "Extensions"
|
||||
2. Recherchez "ASUS Keyboard RGB Control"
|
||||
3. Activez le bouton
|
||||
|
||||
### 3.3 Vérification
|
||||
|
||||
Vous devriez voir une icône de clavier dans la barre supérieure de GNOME Shell (à droite). Cliquez dessus pour ouvrir le menu de contrôle.
|
||||
|
||||
## Étape 4 : Vérification des logs
|
||||
|
||||
Si l'extension ne s'affiche pas ou ne fonctionne pas :
|
||||
|
||||
```bash
|
||||
# Voir les logs en temps réel
|
||||
journalctl -f -o cat /usr/bin/gnome-shell
|
||||
|
||||
# Ou voir les dernières erreurs
|
||||
journalctl -xe | grep -i asus
|
||||
```
|
||||
|
||||
## Désinstallation
|
||||
|
||||
Pour désinstaller l'extension :
|
||||
|
||||
```bash
|
||||
# Désactiver
|
||||
gnome-extensions disable asus-kbd-rgb@gilles
|
||||
|
||||
# Supprimer
|
||||
rm -rf ~/.local/share/gnome-shell/extensions/asus-kbd-rgb@gilles
|
||||
|
||||
# Optionnel : supprimer le groupe et la règle udev
|
||||
sudo deluser $USER kbdled
|
||||
sudo rm /etc/udev/rules.d/99-asus-kbd.rules
|
||||
sudo udevadm control --reload-rules
|
||||
```
|
||||
|
||||
## Dépannage
|
||||
|
||||
Si vous rencontrez des problèmes, consultez le fichier [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
|
||||
|
||||
## Support
|
||||
|
||||
- **Issues**: https://github.com/gilles/gnome-asus-kbd-rgb/issues
|
||||
- **Documentation**: Voir les autres fichiers du dossier `docs/`
|
||||
324
docs/TESTING.md
Normal file
324
docs/TESTING.md
Normal file
@@ -0,0 +1,324 @@
|
||||
# Checklist de Tests - Extension ASUS Keyboard RGB
|
||||
|
||||
Ce document fournit une checklist complète pour tester l'extension avant publication ou après modification.
|
||||
|
||||
## Prérequis pour les tests
|
||||
|
||||
- [ ] GNOME Shell 48 installé
|
||||
- [ ] Matériel ASUS avec clavier RGB supporté
|
||||
- [ ] Règles udev configurées (groupe `kbdled`)
|
||||
- [ ] Utilisateur membre du groupe `kbdled` (après déconnexion/reconnexion)
|
||||
|
||||
## 1. Installation
|
||||
|
||||
### 1.1 Installation initiale
|
||||
|
||||
- [ ] Le script `./tools/install-local.sh` s'exécute sans erreur
|
||||
- [ ] Les fichiers sont copiés dans `~/.local/share/gnome-shell/extensions/asus-kbd-rgb@gilles/`
|
||||
- [ ] Le schéma GSettings est compilé (`gschemas.compiled` existe)
|
||||
- [ ] L'extension apparaît dans `gnome-extensions list`
|
||||
|
||||
### 1.2 Activation
|
||||
|
||||
- [ ] La commande `gnome-extensions enable asus-kbd-rgb@gilles` fonctionne
|
||||
- [ ] Aucune erreur dans `journalctl -f -o cat /usr/bin/gnome-shell`
|
||||
- [ ] L'icône de clavier apparaît dans la barre supérieure (après rechargement)
|
||||
|
||||
## 2. Interface Utilisateur
|
||||
|
||||
### 2.1 Icône et menu
|
||||
|
||||
- [ ] L'icône `keyboard-brightness-symbolic` est visible dans le panneau
|
||||
- [ ] Clic sur l'icône ouvre le popover
|
||||
- [ ] Le popover affiche le titre "Rétroéclairage Clavier ASUS"
|
||||
- [ ] Le menu est bien formaté et lisible
|
||||
|
||||
### 2.2 Section Intensité
|
||||
|
||||
- [ ] Les 4 boutons d'intensité sont visibles (OFF, 1, 2, 3)
|
||||
- [ ] Clic sur "OFF" éteint le rétroéclairage
|
||||
- [ ] Clic sur "1" met l'intensité à faible
|
||||
- [ ] Clic sur "2" met l'intensité à moyenne
|
||||
- [ ] Clic sur "3" met l'intensité à forte (max)
|
||||
- [ ] Le bouton actif est surligné visuellement
|
||||
- [ ] Un seul bouton est actif à la fois
|
||||
|
||||
### 2.3 Section Sliders RGB
|
||||
|
||||
- [ ] Le slider Rouge fonctionne et affiche la valeur (0-255)
|
||||
- [ ] Le slider Vert fonctionne et affiche la valeur (0-255)
|
||||
- [ ] Le slider Bleu fonctionne et affiche la valeur (0-255)
|
||||
- [ ] Le slider Master fonctionne et affiche la valeur (0-100)
|
||||
- [ ] Déplacer un slider met à jour la valeur numérique en temps réel
|
||||
- [ ] Les sliders sont fluides (pas de lag visible)
|
||||
|
||||
### 2.4 Ligne d'information
|
||||
|
||||
- [ ] La ligne d'info affiche `RGB=(r,g,b)` correctement
|
||||
- [ ] Le code HEX `#RRGGBB` est correct
|
||||
- [ ] L'intensité `Intensité=n/3` est correcte
|
||||
- [ ] Les valeurs se mettent à jour après changement de slider
|
||||
- [ ] Les valeurs se mettent à jour après clic sur preset
|
||||
- [ ] Les valeurs se mettent à jour après changement d'intensité
|
||||
|
||||
### 2.5 Section Presets
|
||||
|
||||
- [ ] 6 boutons de preset sont visibles
|
||||
- [ ] Les couleurs de fond correspondent aux presets :
|
||||
- Preset 1 : Orange (#FFA500)
|
||||
- Preset 2 : Rouge (#FF0000)
|
||||
- Preset 3 : Vert (#00FF00)
|
||||
- Preset 4 : Bleu (#0000FF)
|
||||
- Preset 5 : Blanc (#FFFFFF)
|
||||
- Preset 6 : Cyan (#00FFFF)
|
||||
- [ ] Clic sur un preset applique la couleur immédiatement
|
||||
- [ ] Les sliders RGB se mettent à jour après clic sur preset
|
||||
- [ ] La ligne d'info se met à jour après clic sur preset
|
||||
|
||||
## 3. Fonctionnalités Backend
|
||||
|
||||
### 3.1 Écriture brightness
|
||||
|
||||
- [ ] Passage de OFF (0) à 1 allume le clavier
|
||||
- [ ] Passage de 1 à 2 augmente la luminosité
|
||||
- [ ] Passage de 2 à 3 augmente encore la luminosité
|
||||
- [ ] Passage de 3 à OFF éteint le clavier
|
||||
- [ ] Les valeurs sont écrites correctement dans `/sys/class/leds/asus::kbd_backlight/brightness`
|
||||
|
||||
### 3.2 Écriture RGB
|
||||
|
||||
- [ ] Changement de Rouge modifie la couleur du clavier
|
||||
- [ ] Changement de Vert modifie la couleur du clavier
|
||||
- [ ] Changement de Bleu modifie la couleur du clavier
|
||||
- [ ] Les valeurs RGB sont clampées (0-255)
|
||||
- [ ] Format dans `kbd_rgb_mode` est correct : `1 0 R G B 0\n`
|
||||
|
||||
### 3.3 Master Gain
|
||||
|
||||
- [ ] Master à 100% : couleurs normales
|
||||
- [ ] Master à 50% : couleurs à mi-intensité
|
||||
- [ ] Master à 0% : noir (aucune couleur visible)
|
||||
- [ ] Master à 75% : couleurs proportionnellement réduites
|
||||
- [ ] Le gain est appliqué à toutes les composantes RGB
|
||||
|
||||
### 3.4 Debouncing
|
||||
|
||||
- [ ] Déplacer rapidement un slider ne spam pas sysfs (vérifier logs)
|
||||
- [ ] Il y a un délai de ~75ms entre les écritures
|
||||
- [ ] La dernière valeur est bien appliquée après l'arrêt du slider
|
||||
- [ ] Aucun message d'erreur de permission dû au spam
|
||||
|
||||
### 3.5 Comportement brightness=0
|
||||
|
||||
- [ ] Si brightness=0, changement RGB ne modifie pas le clavier (reste éteint)
|
||||
- [ ] Les valeurs RGB sont mémorisées dans GSettings
|
||||
- [ ] Passage de brightness 0→1 restaure la dernière couleur RGB
|
||||
- [ ] Aucun message d'erreur lors du changement RGB avec brightness=0
|
||||
|
||||
## 4. Persistance (GSettings)
|
||||
|
||||
### 4.1 Sauvegarde
|
||||
|
||||
- [ ] Changer Rouge, fermer le menu, rouvrir → valeur conservée
|
||||
- [ ] Changer Vert, fermer le menu, rouvrir → valeur conservée
|
||||
- [ ] Changer Bleu, fermer le menu, rouvrir → valeur conservée
|
||||
- [ ] Changer Master, fermer le menu, rouvrir → valeur conservée
|
||||
- [ ] Changer Brightness, fermer le menu, rouvrir → valeur conservée
|
||||
- [ ] Appliquer un preset, fermer le menu, rouvrir → preset appliqué
|
||||
|
||||
### 4.2 Restauration au démarrage
|
||||
|
||||
- [ ] Configurer RGB (ex: rouge pur), brightness à 2
|
||||
- [ ] Fermer la session, se reconnecter
|
||||
- [ ] L'extension restaure automatiquement RGB rouge et brightness 2
|
||||
- [ ] Le clavier affiche la bonne couleur dès le démarrage
|
||||
- [ ] Les sliders affichent les bonnes valeurs
|
||||
|
||||
### 4.3 Commandes GSettings manuelles
|
||||
|
||||
```bash
|
||||
# Tester la lecture
|
||||
gsettings get org.gnome.shell.extensions.asuskbdrgb red
|
||||
gsettings get org.gnome.shell.extensions.asuskbdrgb green
|
||||
gsettings get org.gnome.shell.extensions.asuskbdrgb blue
|
||||
gsettings get org.gnome.shell.extensions.asuskbdrgb master-gain
|
||||
gsettings get org.gnome.shell.extensions.asuskbdrgb brightness-level
|
||||
```
|
||||
|
||||
- [ ] Les commandes retournent les bonnes valeurs
|
||||
- [ ] Modifier via `gsettings set`, rouvrir le menu → nouvelle valeur affichée
|
||||
|
||||
## 5. Gestion des Erreurs
|
||||
|
||||
### 5.1 Matériel non supporté
|
||||
|
||||
Tester sur une machine sans clavier ASUS :
|
||||
|
||||
- [ ] L'extension charge sans crash
|
||||
- [ ] Le popover affiche "❌ Matériel non supporté"
|
||||
- [ ] Message clair : "Aucun clavier ASUS RGB détecté"
|
||||
- [ ] Pas de sliders ni boutons (seulement le message d'erreur)
|
||||
|
||||
### 5.2 Permissions insuffisantes
|
||||
|
||||
Tester sans règles udev (ou retirer l'utilisateur du groupe `kbdled`) :
|
||||
|
||||
- [ ] L'extension charge sans crash
|
||||
- [ ] Le popover affiche "⚠️ Permissions insuffisantes"
|
||||
- [ ] Message clair avec référence à `docs/INSTALL.md`
|
||||
- [ ] Pas de tentative d'écriture sysfs (pas d'erreur dans logs)
|
||||
|
||||
### 5.3 Valeurs invalides
|
||||
|
||||
- [ ] Entrer une valeur RGB > 255 → clampée à 255
|
||||
- [ ] Entrer une valeur RGB < 0 → clampée à 0
|
||||
- [ ] Entrer une valeur Master > 100 → clampée à 100
|
||||
- [ ] Entrer une valeur Master < 0 → clampée à 0
|
||||
- [ ] Brightness > 3 → clampée à 3
|
||||
|
||||
## 6. Logs et Debug
|
||||
|
||||
### 6.1 Logs au démarrage
|
||||
|
||||
```bash
|
||||
journalctl -f -o cat /usr/bin/gnome-shell | grep -i asus
|
||||
```
|
||||
|
||||
- [ ] Message "Activation de l'extension ASUS Keyboard RGB"
|
||||
- [ ] Message "Extension ASUS Keyboard RGB activée avec succès"
|
||||
- [ ] Aucune erreur JavaScript
|
||||
|
||||
### 6.2 Logs pendant l'utilisation
|
||||
|
||||
- [ ] Changement RGB affiche "RGB mis à (r, g, b) [master: X%]"
|
||||
- [ ] Changement brightness affiche "Brightness mise à N (valeur)"
|
||||
- [ ] Aucune erreur "Permission denied"
|
||||
- [ ] Aucun spam (grâce au debouncing)
|
||||
|
||||
### 6.3 Logs à la désactivation
|
||||
|
||||
```bash
|
||||
gnome-extensions disable asus-kbd-rgb@gilles
|
||||
```
|
||||
|
||||
- [ ] Message "Désactivation de l'extension ASUS Keyboard RGB"
|
||||
- [ ] Message "Extension ASUS Keyboard RGB désactivée"
|
||||
- [ ] Aucune erreur de nettoyage
|
||||
|
||||
## 7. Robustesse
|
||||
|
||||
### 7.1 Rechargement GNOME Shell
|
||||
|
||||
- [ ] Alt+F2, `r` → l'extension se recharge correctement (X11)
|
||||
- [ ] Logout/login → l'extension se recharge correctement (Wayland)
|
||||
- [ ] État restauré après rechargement
|
||||
|
||||
### 7.2 Désactivation/Réactivation
|
||||
|
||||
```bash
|
||||
gnome-extensions disable asus-kbd-rgb@gilles
|
||||
gnome-extensions enable asus-kbd-rgb@gilles
|
||||
```
|
||||
|
||||
- [ ] Pas d'erreur lors de la désactivation
|
||||
- [ ] Pas d'erreur lors de la réactivation
|
||||
- [ ] L'icône réapparaît
|
||||
- [ ] Fonctionnalités intactes
|
||||
|
||||
### 7.3 Utilisation intensive
|
||||
|
||||
- [ ] Déplacer rapidement tous les sliders pendant 30 secondes
|
||||
- [ ] Cliquer rapidement sur tous les boutons
|
||||
- [ ] Alterner rapidement entre presets
|
||||
- [ ] Aucun crash
|
||||
- [ ] Aucune fuite mémoire visible
|
||||
- [ ] Performances stables
|
||||
|
||||
## 8. Compatibilité
|
||||
|
||||
### 8.1 GNOME Shell 48
|
||||
|
||||
- [ ] Extension fonctionne sur GNOME 48.0
|
||||
- [ ] Extension fonctionne sur GNOME 48.x (dernière version)
|
||||
|
||||
### 8.2 Debian 13 (Trixie)
|
||||
|
||||
- [ ] Installation réussie
|
||||
- [ ] Toutes les fonctionnalités opérationnelles
|
||||
- [ ] Aucun conflit de packages
|
||||
|
||||
## 9. Documentation
|
||||
|
||||
- [ ] README.md est à jour
|
||||
- [ ] INSTALL.md couvre tous les cas
|
||||
- [ ] TROUBLESHOOTING.md aide à résoudre les problèmes courants
|
||||
- [ ] CLAUDE.md guide correctement les futures instances
|
||||
- [ ] CHANGELOG.md est à jour
|
||||
- [ ] UI_SCHEMA.md correspond à l'implémentation
|
||||
|
||||
## 10. Code Quality
|
||||
|
||||
### 10.1 Linting JavaScript
|
||||
|
||||
```bash
|
||||
# Si eslint est disponible
|
||||
eslint extension/*.js
|
||||
```
|
||||
|
||||
- [ ] Aucune erreur de syntaxe
|
||||
- [ ] Pas d'imports manquants
|
||||
- [ ] Variables non utilisées supprimées
|
||||
|
||||
### 10.2 Revue de code
|
||||
|
||||
- [ ] Commentaires en français présents et clairs
|
||||
- [ ] Fonctions documentées
|
||||
- [ ] Code modulaire et lisible
|
||||
- [ ] Pas de code mort (dead code)
|
||||
- [ ] Gestion d'erreurs présente
|
||||
|
||||
## Résumé des Tests
|
||||
|
||||
**Total des tests** : ~120 items
|
||||
|
||||
### Statut Critique (Bloquant si échec)
|
||||
- Installation et activation
|
||||
- Détection matériel et permissions
|
||||
- Fonctionnalités core (brightness, RGB, presets)
|
||||
- Persistance GSettings
|
||||
- Pas de crash
|
||||
|
||||
### Statut Important (À corriger rapidement)
|
||||
- UI correcte et responsive
|
||||
- Debouncing fonctionnel
|
||||
- Messages d'erreur clairs
|
||||
- Logs appropriés
|
||||
|
||||
### Statut Nice-to-have (Améliorations)
|
||||
- Performance optimale
|
||||
- Style CSS parfait
|
||||
- Documentation exhaustive
|
||||
|
||||
---
|
||||
|
||||
## Rapport de Test
|
||||
|
||||
Compléter après les tests :
|
||||
|
||||
**Date** : _______________
|
||||
|
||||
**Testeur** : _______________
|
||||
|
||||
**GNOME Shell version** : _______________
|
||||
|
||||
**Matériel** : _______________
|
||||
|
||||
**Tests réussis** : ______ / 120
|
||||
|
||||
**Tests échoués** : ______
|
||||
|
||||
**Bugs trouvés** :
|
||||
-
|
||||
-
|
||||
|
||||
**Notes** :
|
||||
445
docs/TROUBLESHOOTING.md
Normal file
445
docs/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,445 @@
|
||||
# Guide de Dépannage - Extension ASUS Keyboard RGB
|
||||
|
||||
Ce document vous aide à résoudre les problèmes courants avec l'extension GNOME Shell ASUS Keyboard RGB.
|
||||
|
||||
## Table des matières
|
||||
|
||||
1. [L'extension ne s'affiche pas](#lextension-ne-saffiche-pas)
|
||||
2. [Message "Permissions insuffisantes"](#message-permissions-insuffisantes)
|
||||
3. [Message "Matériel non supporté"](#message-matériel-non-supporté)
|
||||
4. [Les sliders ne fonctionnent pas](#les-sliders-ne-fonctionnent-pas)
|
||||
5. [La couleur ne change pas](#la-couleur-ne-change-pas)
|
||||
6. [Les paramètres ne sont pas sauvegardés](#les-paramètres-ne-sont-pas-sauvegardés)
|
||||
7. [Erreurs dans les logs](#erreurs-dans-les-logs)
|
||||
8. [L'extension se désactive automatiquement](#lextension-se-désactive-automatiquement)
|
||||
|
||||
---
|
||||
|
||||
## L'extension ne s'affiche pas
|
||||
|
||||
### Symptômes
|
||||
- L'icône de clavier n'apparaît pas dans la barre supérieure
|
||||
- L'extension est activée mais rien ne se passe
|
||||
|
||||
### Solutions
|
||||
|
||||
#### 1. Vérifier que l'extension est activée
|
||||
|
||||
```bash
|
||||
gnome-extensions list
|
||||
gnome-extensions info asus-kbd-rgb@gilles
|
||||
```
|
||||
|
||||
Si elle n'apparaît pas :
|
||||
|
||||
```bash
|
||||
gnome-extensions enable asus-kbd-rgb@gilles
|
||||
```
|
||||
|
||||
#### 2. Vérifier l'installation
|
||||
|
||||
```bash
|
||||
ls -la ~/.local/share/gnome-shell/extensions/asus-kbd-rgb@gilles/
|
||||
```
|
||||
|
||||
Fichiers requis :
|
||||
- `extension.js`
|
||||
- `ui.js`
|
||||
- `backend.js`
|
||||
- `metadata.json`
|
||||
- `schemas/org.gnome.shell.extensions.asuskbdrgb.gschema.xml`
|
||||
- `schemas/gschemas.compiled`
|
||||
|
||||
#### 3. Recompiler le schéma GSettings
|
||||
|
||||
```bash
|
||||
cd ~/.local/share/gnome-shell/extensions/asus-kbd-rgb@gilles/schemas/
|
||||
glib-compile-schemas .
|
||||
```
|
||||
|
||||
#### 4. Recharger GNOME Shell
|
||||
|
||||
**X11** : `Alt+F2`, tapez `r`, Entrée
|
||||
|
||||
**Wayland** : Déconnexion/Reconnexion
|
||||
|
||||
#### 5. Vérifier les logs
|
||||
|
||||
```bash
|
||||
journalctl -f -o cat /usr/bin/gnome-shell | grep -i asus
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Message "Permissions insuffisantes"
|
||||
|
||||
### Symptômes
|
||||
- Le menu affiche "⚠️ Permissions insuffisantes"
|
||||
- Message : "Impossible d'accéder au rétroéclairage"
|
||||
|
||||
### Cause
|
||||
L'utilisateur n'a pas les droits d'écriture sur les fichiers sysfs.
|
||||
|
||||
### Solutions
|
||||
|
||||
#### 1. Vérifier la règle udev
|
||||
|
||||
```bash
|
||||
cat /etc/udev/rules.d/99-asus-kbd.rules
|
||||
```
|
||||
|
||||
Devrait contenir :
|
||||
```
|
||||
SUBSYSTEM=="leds", KERNEL=="asus::kbd_backlight", GROUP="kbdled", MODE="0660"
|
||||
```
|
||||
|
||||
Si le fichier n'existe pas, créez-le :
|
||||
|
||||
```bash
|
||||
echo 'SUBSYSTEM=="leds", KERNEL=="asus::kbd_backlight", GROUP="kbdled", MODE="0660"' | sudo tee /etc/udev/rules.d/99-asus-kbd.rules
|
||||
```
|
||||
|
||||
#### 2. Vérifier le groupe kbdled
|
||||
|
||||
```bash
|
||||
getent group kbdled
|
||||
```
|
||||
|
||||
Si le groupe n'existe pas :
|
||||
|
||||
```bash
|
||||
sudo groupadd -f kbdled
|
||||
```
|
||||
|
||||
#### 3. Vérifier l'appartenance au groupe
|
||||
|
||||
```bash
|
||||
groups | grep kbdled
|
||||
```
|
||||
|
||||
Si vous n'êtes pas dans le groupe :
|
||||
|
||||
```bash
|
||||
sudo usermod -aG kbdled $USER
|
||||
```
|
||||
|
||||
**Important** : Déconnexion/Reconnexion requise !
|
||||
|
||||
#### 4. Recharger udev
|
||||
|
||||
```bash
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger
|
||||
```
|
||||
|
||||
#### 5. Vérifier les permissions finales
|
||||
|
||||
```bash
|
||||
ls -l /sys/class/leds/asus::kbd_backlight/brightness
|
||||
# Devrait afficher : -rw-rw---- 1 root kbdled
|
||||
```
|
||||
|
||||
Test d'écriture :
|
||||
|
||||
```bash
|
||||
echo 2 > /sys/class/leds/asus::kbd_backlight/brightness
|
||||
```
|
||||
|
||||
Si erreur "Permission denied", vérifiez que vous êtes bien déconnecté/reconnecté.
|
||||
|
||||
---
|
||||
|
||||
## Message "Matériel non supporté"
|
||||
|
||||
### Symptômes
|
||||
- Le menu affiche "❌ Matériel non supporté"
|
||||
- Message : "Aucun clavier ASUS RGB détecté"
|
||||
|
||||
### Cause
|
||||
Le kernel ne détecte pas le clavier ASUS ou le module `asus-nb-wmi` n'est pas chargé.
|
||||
|
||||
### Solutions
|
||||
|
||||
#### 1. Vérifier les fichiers sysfs
|
||||
|
||||
```bash
|
||||
ls -la /sys/class/leds/ | grep asus
|
||||
```
|
||||
|
||||
Devrait afficher `asus::kbd_backlight`
|
||||
|
||||
Si absent :
|
||||
|
||||
```bash
|
||||
ls -la /sys/class/leds/
|
||||
```
|
||||
|
||||
Cherchez un dossier similaire pour votre clavier.
|
||||
|
||||
#### 2. Vérifier le module kernel
|
||||
|
||||
```bash
|
||||
lsmod | grep asus
|
||||
```
|
||||
|
||||
Devrait afficher `asus_nb_wmi` ou `asus_wmi`
|
||||
|
||||
Si absent, charger le module :
|
||||
|
||||
```bash
|
||||
sudo modprobe asus-nb-wmi
|
||||
```
|
||||
|
||||
#### 3. Vérifier le matériel
|
||||
|
||||
```bash
|
||||
sudo dmesg | grep -i asus
|
||||
sudo dmesg | grep -i keyboard
|
||||
```
|
||||
|
||||
Cherchez des messages indiquant la détection du clavier.
|
||||
|
||||
#### 4. Vérifier dans le BIOS
|
||||
|
||||
Certains ordinateurs ASUS permettent de désactiver le rétroéclairage RGB dans le BIOS. Vérifiez les options "Keyboard Backlight" ou "RGB Lighting".
|
||||
|
||||
---
|
||||
|
||||
## Les sliders ne fonctionnent pas
|
||||
|
||||
### Symptômes
|
||||
- Les sliders bougent mais la couleur ne change pas
|
||||
- Pas de feedback visuel
|
||||
|
||||
### Solutions
|
||||
|
||||
#### 1. Vérifier la brightness
|
||||
|
||||
Si la brightness est à 0 (OFF), les changements RGB sont mémorisés mais non appliqués. Augmentez d'abord la brightness.
|
||||
|
||||
#### 2. Vérifier les logs
|
||||
|
||||
```bash
|
||||
journalctl -f -o cat /usr/bin/gnome-shell | grep RGB
|
||||
```
|
||||
|
||||
Vous devriez voir des messages comme :
|
||||
```
|
||||
RGB mis à (255, 165, 0) [master: 100%]
|
||||
```
|
||||
|
||||
#### 3. Tester manuellement
|
||||
|
||||
```bash
|
||||
# Test RGB rouge
|
||||
echo "1 0 255 0 0 0" > /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode
|
||||
```
|
||||
|
||||
Si cela ne fonctionne pas, le problème est au niveau matériel/kernel, pas de l'extension.
|
||||
|
||||
---
|
||||
|
||||
## La couleur ne change pas
|
||||
|
||||
### Symptômes
|
||||
- Les sliders fonctionnent mais la couleur reste la même
|
||||
|
||||
### Solutions
|
||||
|
||||
#### 1. Vérifier le master slider
|
||||
|
||||
Si le master slider est à 0%, toutes les couleurs seront noires. Augmentez-le à 100%.
|
||||
|
||||
#### 2. Vérifier la valeur brightness
|
||||
|
||||
```bash
|
||||
cat /sys/class/leds/asus::kbd_backlight/brightness
|
||||
```
|
||||
|
||||
Si c'est 0, la couleur ne s'affichera pas.
|
||||
|
||||
#### 3. Redémarrer l'extension
|
||||
|
||||
```bash
|
||||
gnome-extensions disable asus-kbd-rgb@gilles
|
||||
gnome-extensions enable asus-kbd-rgb@gilles
|
||||
```
|
||||
|
||||
Puis recharger GNOME Shell (Alt+F2, r).
|
||||
|
||||
---
|
||||
|
||||
## Les paramètres ne sont pas sauvegardés
|
||||
|
||||
### Symptômes
|
||||
- À chaque redémarrage, les couleurs reviennent aux valeurs par défaut
|
||||
|
||||
### Solutions
|
||||
|
||||
#### 1. Vérifier GSettings
|
||||
|
||||
```bash
|
||||
gsettings list-schemas | grep asuskbdrgb
|
||||
```
|
||||
|
||||
Devrait afficher : `org.gnome.shell.extensions.asuskbdrgb`
|
||||
|
||||
#### 2. Vérifier les valeurs stockées
|
||||
|
||||
```bash
|
||||
gsettings get org.gnome.shell.extensions.asuskbdrgb red
|
||||
gsettings get org.gnome.shell.extensions.asuskbdrgb green
|
||||
gsettings get org.gnome.shell.extensions.asuskbdrgb blue
|
||||
```
|
||||
|
||||
#### 3. Définir manuellement
|
||||
|
||||
```bash
|
||||
gsettings set org.gnome.shell.extensions.asuskbdrgb red 255
|
||||
gsettings set org.gnome.shell.extensions.asuskbdrgb green 165
|
||||
gsettings set org.gnome.shell.extensions.asuskbdrgb blue 0
|
||||
```
|
||||
|
||||
#### 4. Recompiler le schéma
|
||||
|
||||
```bash
|
||||
cd ~/.local/share/gnome-shell/extensions/asus-kbd-rgb@gilles/schemas/
|
||||
glib-compile-schemas .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Erreurs dans les logs
|
||||
|
||||
### Comment voir les logs
|
||||
|
||||
```bash
|
||||
# Logs en temps réel
|
||||
journalctl -f -o cat /usr/bin/gnome-shell
|
||||
|
||||
# Dernières erreurs
|
||||
journalctl -xe | grep -E "(error|Error|ERROR)"
|
||||
```
|
||||
|
||||
### Erreurs courantes
|
||||
|
||||
#### "Extension asus-kbd-rgb@gilles had error: ..."
|
||||
|
||||
Réinstaller l'extension :
|
||||
|
||||
```bash
|
||||
./tools/install-local.sh
|
||||
```
|
||||
|
||||
#### "Failed to load schema org.gnome.shell.extensions.asuskbdrgb"
|
||||
|
||||
Recompiler le schéma GSettings (voir ci-dessus).
|
||||
|
||||
#### "Permission denied" dans les logs
|
||||
|
||||
Problème de permissions (voir section correspondante).
|
||||
|
||||
---
|
||||
|
||||
## L'extension se désactive automatiquement
|
||||
|
||||
### Symptômes
|
||||
- L'extension s'active puis se désactive immédiatement
|
||||
- Ou se désactive après un certain temps
|
||||
|
||||
### Solutions
|
||||
|
||||
#### 1. Vérifier la compatibilité
|
||||
|
||||
```bash
|
||||
gnome-shell --version
|
||||
```
|
||||
|
||||
Doit afficher : `GNOME Shell 48.x`
|
||||
|
||||
Si version différente, modifier `metadata.json` :
|
||||
|
||||
```json
|
||||
"shell-version": [
|
||||
"48",
|
||||
"47" // Ajouter votre version
|
||||
]
|
||||
```
|
||||
|
||||
#### 2. Vérifier les erreurs JavaScript
|
||||
|
||||
```bash
|
||||
journalctl -f -o cat /usr/bin/gnome-shell | grep -i "syntax\|error"
|
||||
```
|
||||
|
||||
#### 3. Mode debug
|
||||
|
||||
Activer avec verbose logging :
|
||||
|
||||
```bash
|
||||
gnome-extensions enable asus-kbd-rgb@gilles
|
||||
journalctl -f /usr/bin/gnome-shell
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Obtenir de l'aide
|
||||
|
||||
Si aucune solution ne fonctionne :
|
||||
|
||||
1. **Collecter les informations de debug** :
|
||||
|
||||
```bash
|
||||
# Créer un fichier de debug
|
||||
{
|
||||
echo "=== System Info ==="
|
||||
uname -a
|
||||
gnome-shell --version
|
||||
|
||||
echo -e "\n=== Hardware ==="
|
||||
ls -la /sys/class/leds/asus::kbd_backlight/
|
||||
|
||||
echo -e "\n=== Permissions ==="
|
||||
ls -l /sys/class/leds/asus::kbd_backlight/brightness
|
||||
groups
|
||||
|
||||
echo -e "\n=== Extension Status ==="
|
||||
gnome-extensions info asus-kbd-rgb@gilles
|
||||
|
||||
echo -e "\n=== GSettings ==="
|
||||
gsettings list-recursively org.gnome.shell.extensions.asuskbdrgb
|
||||
|
||||
echo -e "\n=== Recent Logs ==="
|
||||
journalctl -xe --no-pager | grep -i asus | tail -20
|
||||
} > ~/asus-kbd-rgb-debug.txt
|
||||
```
|
||||
|
||||
2. **Ouvrir une issue** sur GitHub avec le fichier `asus-kbd-rgb-debug.txt`
|
||||
|
||||
3. **Forum GNOME** : https://discourse.gnome.org/
|
||||
|
||||
---
|
||||
|
||||
## Réinstallation complète
|
||||
|
||||
Si tout échoue, réinstallation complète :
|
||||
|
||||
```bash
|
||||
# 1. Désactiver et supprimer
|
||||
gnome-extensions disable asus-kbd-rgb@gilles
|
||||
rm -rf ~/.local/share/gnome-shell/extensions/asus-kbd-rgb@gilles
|
||||
|
||||
# 2. Nettoyer GSettings
|
||||
gsettings reset-recursively org.gnome.shell.extensions.asuskbdrgb
|
||||
|
||||
# 3. Réinstaller
|
||||
cd ~/Documents/gnome-asus-kbd-rgb
|
||||
./tools/install-local.sh
|
||||
|
||||
# 4. Recharger GNOME Shell
|
||||
# X11: Alt+F2, r
|
||||
# Wayland: logout/login
|
||||
|
||||
# 5. Réactiver
|
||||
gnome-extensions enable asus-kbd-rgb@gilles
|
||||
```
|
||||
170
docs/UI_SCHEMA.md
Normal file
170
docs/UI_SCHEMA.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# Schéma de l'Interface Utilisateur
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
L'extension ajoute une icône dans la barre supérieure de GNOME Shell. Au clic, un popover s'affiche avec tous les contrôles.
|
||||
|
||||
## Diagramme Mermaid - Architecture UI
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Top Bar GNOME Shell] --> B[Panel Button avec icône clavier]
|
||||
B -->|Clic| C[Popover Menu]
|
||||
|
||||
C --> D[Section Intensité]
|
||||
C --> E[Section Sliders RGB]
|
||||
C --> F[Section Info]
|
||||
C --> G[Section Presets]
|
||||
|
||||
D --> D1[Bouton OFF]
|
||||
D --> D2[Bouton Faible]
|
||||
D --> D3[Bouton Moyen]
|
||||
D --> D4[Bouton Fort]
|
||||
|
||||
E --> E1[Slider Rouge 0-255]
|
||||
E --> E2[Slider Vert 0-255]
|
||||
E --> E3[Slider Bleu 0-255]
|
||||
E --> E4[Slider Master 0-100%]
|
||||
|
||||
F --> F1[Label RGB=r,g,b]
|
||||
F --> F2[Label HEX=#RRGGBB]
|
||||
F --> F3[Label Intensity=n/max]
|
||||
|
||||
G --> G1[Preset 1: Orange]
|
||||
G --> G2[Preset 2: Rouge]
|
||||
G --> G3[Preset 3: Vert]
|
||||
G --> G4[Preset 4: Bleu]
|
||||
G --> G5[Preset 5: Blanc]
|
||||
G --> G6[Preset 6: Cyan]
|
||||
|
||||
E1 -->|Change| H[Backend: Debounce]
|
||||
E2 -->|Change| H
|
||||
E3 -->|Change| H
|
||||
E4 -->|Change| H
|
||||
D1 -->|Click| H
|
||||
D2 -->|Click| H
|
||||
D3 -->|Click| H
|
||||
D4 -->|Click| H
|
||||
G1 -->|Click| H
|
||||
G2 -->|Click| H
|
||||
G3 -->|Click| H
|
||||
G4 -->|Click| H
|
||||
G5 -->|Click| H
|
||||
G6 -->|Click| H
|
||||
|
||||
H --> I[Écriture sysfs]
|
||||
I --> J[/sys/class/leds/asus::kbd_backlight/]
|
||||
|
||||
H --> K[Sauvegarde GSettings]
|
||||
```
|
||||
|
||||
## Layout ASCII du Popover
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Rétroéclairage Clavier ASUS │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Intensité: │
|
||||
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||||
│ │ OFF │ │ 1 │ │ 2 │ │ 3 │ │
|
||||
│ └─────┘ └─────┘ └─────┘ └─────┘ │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────┐ │
|
||||
│ │ Rouge ●─────────────────────○ 255 │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ ┌────────────────────────────────────────────┐ │
|
||||
│ │ Vert ●─────────────────────○ 255 │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ ┌────────────────────────────────────────────┐ │
|
||||
│ │ Bleu ●─────────────────────○ 255 │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ ┌────────────────────────────────────────────┐ │
|
||||
│ │ Master ●─────────────────────○ 100% │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────┐ │
|
||||
│ │ RGB=(255,165,0) #FFA500 Intensité=2/3 │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Couleurs prédéfinies: │
|
||||
│ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │
|
||||
│ │ 🟠│ │🔴 │ │🟢 │ │🔵 │ │⚪ │ │🔵 │ │
|
||||
│ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Flux de Données
|
||||
|
||||
```
|
||||
Utilisateur → Slider/Bouton → Event Handler (ui.js)
|
||||
↓
|
||||
Debounce Timer (50-100ms)
|
||||
↓
|
||||
Backend.applyRGB() (backend.js)
|
||||
↓
|
||||
Clamping + Master Gain
|
||||
↓
|
||||
┌────────────────┴────────────────┐
|
||||
↓ ↓
|
||||
Écriture sysfs Sauvegarde GSettings
|
||||
(kbd_rgb_mode, brightness) (persistance)
|
||||
↓ ↓
|
||||
Mise à jour matérielle Restauration au démarrage
|
||||
```
|
||||
|
||||
## Composants GNOME Shell Utilisés
|
||||
|
||||
- **PanelMenu.Button** : Bouton dans la top bar
|
||||
- **PopupMenu.PopupBaseMenuItem** : Items personnalisés dans le menu
|
||||
- **St.BoxLayout** : Layout vertical/horizontal
|
||||
- **St.Button** : Boutons d'intensité
|
||||
- **Slider** : Sliders RGB et Master (API GNOME Shell)
|
||||
- **St.Label** : Labels et texte d'information
|
||||
- **St.Bin** : Containers pour les presets couleur
|
||||
|
||||
## Interactions Utilisateur
|
||||
|
||||
### Actions Immédiates (sans confirmation)
|
||||
- Déplacer un slider RGB → Application immédiate avec debounce
|
||||
- Cliquer sur un bouton d'intensité → Application immédiate
|
||||
- Cliquer sur une couleur preset → Application immédiate
|
||||
- Déplacer le slider Master → Ajuste R/G/B proportionnellement
|
||||
|
||||
### Feedback Visuel
|
||||
- Slider actif → Curseur se déplace
|
||||
- Bouton intensité actif → Surligné/highlighted
|
||||
- Info line → Mise à jour en temps réel
|
||||
- Preset cliqué → Feedback visuel temporaire
|
||||
|
||||
## Gestion des Erreurs UI
|
||||
|
||||
### Permission refusée
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ ⚠️ Permissions insuffisantes │
|
||||
│ │
|
||||
│ Impossible d'accéder au rétroéclairage. │
|
||||
│ Veuillez configurer les règles udev. │
|
||||
│ │
|
||||
│ Voir: docs/INSTALL.md │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Matériel non supporté
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ ❌ Matériel non supporté │
|
||||
│ │
|
||||
│ Aucun clavier ASUS RGB détecté sur ce système. │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Notes d'Implémentation
|
||||
|
||||
1. **Debouncing**: Utiliser `GLib.timeout_add()` avec timeout de 75ms
|
||||
2. **Master Slider**: Multiplier R/G/B par (master/100) avant écriture
|
||||
3. **Presets**: Stocker dans GSettings, charger au démarrage
|
||||
4. **Info Line**: Mettre à jour via callback après chaque changement
|
||||
5. **Style**: Utiliser `stylesheet.css` pour personnalisation minimale
|
||||
138
docs/synthese roue chromatique.md
Normal file
138
docs/synthese roue chromatique.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# Synthèse — Mettre en place une roue chromatique (HSV) dans GNOME Shell 48
|
||||
|
||||
## Contexte
|
||||
- GNOME Shell 48 (GJS) utilise **St/Clutter** (pas GTK dans le Shell).
|
||||
- Il n’existe **pas** de composant natif « roue chromatique » (type `GtkColorChooser`) directement exploitable dans une extension GNOME Shell.
|
||||
- Objectif : offrir une **roue HSV** pour choisir une couleur (H, S) + un contrôle d’**intensité** (V), puis produire un RGB (0–255) utilisable pour piloter un matériel (ex. clavier RGB).
|
||||
|
||||
---
|
||||
|
||||
## Options possibles (du plus recommandé au plus “hybride”)
|
||||
|
||||
### Option A — Roue HSV native GNOME Shell (recommandée)
|
||||
**Principe**
|
||||
- Créer un widget custom basé sur `Clutter.Canvas`.
|
||||
- Dessiner une roue HSV (visualisation en V=1 pour lisibilité).
|
||||
- Capturer les interactions souris :
|
||||
- angle → **Hue (H)**
|
||||
- rayon → **Saturation (S)**
|
||||
- Ajouter un slider pour **Value (V)** (intensité).
|
||||
- Convertir HSV → RGB (0–255) et déclencher un callback.
|
||||
|
||||
**Avantages**
|
||||
- 100% intégré au Shell (look & feel cohérent extension).
|
||||
- Dépendances minimales (GJS uniquement).
|
||||
- Utilisable dans un menu top-bar, Quick Settings, ou popup.
|
||||
- Contrôle immédiat (idéal pour piloter des LED).
|
||||
|
||||
**Inconvénients**
|
||||
- Code à maintenir (rendu + interaction).
|
||||
- Optimisation possible nécessaire pour performance (cache image).
|
||||
|
||||
**Recommandation**
|
||||
- C’est la meilleure solution pour une extension GNOME 48, surtout si l’usage cible est le pilotage hardware RGB.
|
||||
|
||||
---
|
||||
|
||||
### Option B — Sliders RGB (solution la plus robuste)
|
||||
**Principe**
|
||||
- Ne pas faire de roue.
|
||||
- 3 sliders `St.Slider` ou `PopupSliderMenuItem` :
|
||||
- R (0–255)
|
||||
- G (0–255)
|
||||
- B (0–255)
|
||||
- Afficher un carré d’aperçu (preview) `St.Widget` dont la couleur suit les sliders.
|
||||
|
||||
**Avantages**
|
||||
- Très simple, très stable, très maintenable.
|
||||
- Exact pour piloter des LED (RGB natif).
|
||||
- Peu de code, faible coût performance.
|
||||
|
||||
**Inconvénients**
|
||||
- Moins intuitif qu’une roue HSV.
|
||||
- Expérience utilisateur moins “graphique”.
|
||||
|
||||
**Recommandation**
|
||||
- Excellent si priorité = fiabilité et précision LED.
|
||||
- Peut être combiné avec la roue (Option A) pour affiner.
|
||||
|
||||
---
|
||||
|
||||
### Option C — Hybride : application GTK externe (GtkColorChooser) + DBus
|
||||
**Principe**
|
||||
- Créer une petite app GTK (hors Shell) utilisant `GtkColorChooser` (roue native GNOME).
|
||||
- L’extension GNOME Shell lance l’app ou communique via DBus.
|
||||
- L’app renvoie la couleur choisie (RGB/HSV) à l’extension.
|
||||
|
||||
**Avantages**
|
||||
- UI GNOME native parfaite (roue GTK).
|
||||
- Moins de code côté Shell pour le rendu.
|
||||
|
||||
**Inconvénients**
|
||||
- Architecture plus lourde (extension + service/app).
|
||||
- Gestion DBus, packaging, cycle de vie de l’app.
|
||||
- Moins fluide pour un usage “réglage instantané”.
|
||||
|
||||
**Recommandation**
|
||||
- À considérer si tu veux absolument la roue GTK standard et acceptes la complexité.
|
||||
|
||||
---
|
||||
|
||||
### Option D — Réutiliser du code existant (extensions / libs)
|
||||
**Principe**
|
||||
- S’inspirer d’une extension existante (Color Picker, etc.) ou porter un widget JS déjà implémenté.
|
||||
- Intégrer le code (Canvas, HSV->RGB, interactions) dans ton extension.
|
||||
|
||||
**Avantages**
|
||||
- Gain de temps si un code de roue HSV est déjà éprouvé.
|
||||
- Possibilité de reprendre des patterns Shell (PopupMenu, Quick Settings, etc.).
|
||||
|
||||
**Inconvénients**
|
||||
- Variabilité de qualité et compatibilité GNOME 48.
|
||||
- Nécessite vérification licence / maintenance.
|
||||
|
||||
**Recommandation**
|
||||
- Très efficace si tu trouves un code déjà aligné GJS/Clutter moderne.
|
||||
|
||||
---
|
||||
|
||||
## Rendu “fidèle” vs LED (point important)
|
||||
- Une roue à l’écran (sRGB) ne correspond pas parfaitement au rendu des LED de clavier.
|
||||
- Recommandé :
|
||||
- afficher RGB 0–255 et/ou HEX
|
||||
- prévoir un slider d’intensité (V) + luminosité matériel
|
||||
- optionnel : appliquer une correction empirique (ex. réduire le bleu) pour coller au rendu réel.
|
||||
|
||||
---
|
||||
|
||||
## UX conseillée (pragmatique)
|
||||
### MVP (simple et efficace)
|
||||
- Roue HSV (H,S) + slider V
|
||||
- Aperçu (carré couleur)
|
||||
- Affichage RGB (0–255) + HEX
|
||||
- Bouton “Appliquer” (ou appliquer en live)
|
||||
|
||||
### Version “confort”
|
||||
- Roue HSV + sliders RGB (affinage)
|
||||
- Presets (palette)
|
||||
- Historique des dernières couleurs
|
||||
- “Appliquer au démarrage” (persist)
|
||||
|
||||
---
|
||||
|
||||
## Recommandation finale
|
||||
Pour GNOME Shell 48, le meilleur choix est :
|
||||
1. **Option A** (roue HSV custom via `Clutter.Canvas`) + slider V
|
||||
2. compléter par **Option B** (sliders RGB) si tu veux une précision LED maximale
|
||||
|
||||
Option C (GTK externe) est valable mais plus lourde et moins adaptée à un réglage rapide dans le Shell.
|
||||
|
||||
---
|
||||
|
||||
## Livrables possibles (si on passe à l’implémentation)
|
||||
- Widget `HsvWheel` réutilisable (Canvas + interaction)
|
||||
- Intégration top-bar (PopupMenu) ou Quick Settings
|
||||
- Callback standard `onColor({r,g,b,h,s,v})`
|
||||
- Module utilitaire HSV<->RGB
|
||||
- (Option) pipeline d’application : sysfs / asusctl / DBus service
|
||||
s
|
||||
285
extension/backend.js
Normal file
285
extension/backend.js
Normal file
@@ -0,0 +1,285 @@
|
||||
// backend.js - Interface sysfs et logique métier pour le rétroéclairage ASUS RGB
|
||||
import Gio from 'gi://Gio';
|
||||
import GLib from 'gi://GLib';
|
||||
|
||||
// Chemins sysfs pour le rétroéclairage ASUS
|
||||
const SYSFS_BASE = '/sys/class/leds/asus::kbd_backlight';
|
||||
const BRIGHTNESS_PATH = `${SYSFS_BASE}/brightness`;
|
||||
const MAX_BRIGHTNESS_PATH = `${SYSFS_BASE}/max_brightness`;
|
||||
const RGB_MODE_PATH = `${SYSFS_BASE}/kbd_rgb_mode`;
|
||||
|
||||
// Niveaux d'intensité (0=Off, 1=Faible, 2=Moyen, 3=Fort)
|
||||
const BRIGHTNESS_LEVELS = [0, null, null, null]; // Sera rempli dynamiquement avec max_brightness
|
||||
|
||||
// Timer pour le debouncing
|
||||
let debounceTimer = null;
|
||||
const DEBOUNCE_DELAY = 75; // ms
|
||||
|
||||
/**
|
||||
* Vérifie si le matériel ASUS RGB est présent sur le système
|
||||
* @returns {boolean} true si le matériel est supporté
|
||||
*/
|
||||
export function checkHardwareSupport() {
|
||||
try {
|
||||
const brightnessFile = Gio.File.new_for_path(BRIGHTNESS_PATH);
|
||||
const rgbFile = Gio.File.new_for_path(RGB_MODE_PATH);
|
||||
|
||||
return brightnessFile.query_exists(null) && rgbFile.query_exists(null);
|
||||
} catch (e) {
|
||||
console.error('Erreur lors de la vérification du matériel:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'utilisateur a les permissions d'écriture
|
||||
* @returns {boolean} true si les permissions sont OK
|
||||
*/
|
||||
export function checkPermissions() {
|
||||
try {
|
||||
const brightnessFile = Gio.File.new_for_path(BRIGHTNESS_PATH);
|
||||
const info = brightnessFile.query_info('access::*', Gio.FileQueryInfoFlags.NONE, null);
|
||||
|
||||
// Vérifier si on peut écrire
|
||||
return info.get_attribute_boolean('access::can-write');
|
||||
} catch (e) {
|
||||
console.error('Erreur lors de la vérification des permissions:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lit la valeur maximale de brightness
|
||||
* @returns {number} Valeur max, ou 3 par défaut
|
||||
*/
|
||||
export function getMaxBrightness() {
|
||||
try {
|
||||
const file = Gio.File.new_for_path(MAX_BRIGHTNESS_PATH);
|
||||
const [success, contents] = file.load_contents(null);
|
||||
|
||||
if (success) {
|
||||
const maxValue = parseInt(new TextDecoder().decode(contents).trim());
|
||||
|
||||
// Initialiser les niveaux de brightness
|
||||
BRIGHTNESS_LEVELS[1] = Math.floor(maxValue / 3);
|
||||
BRIGHTNESS_LEVELS[2] = Math.floor(2 * maxValue / 3);
|
||||
BRIGHTNESS_LEVELS[3] = maxValue;
|
||||
|
||||
return maxValue;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Erreur lors de la lecture de max_brightness:', e);
|
||||
}
|
||||
|
||||
// Valeurs par défaut si échec
|
||||
BRIGHTNESS_LEVELS[1] = 1;
|
||||
BRIGHTNESS_LEVELS[2] = 2;
|
||||
BRIGHTNESS_LEVELS[3] = 3;
|
||||
return 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lit la brightness actuelle
|
||||
* @returns {number} Valeur actuelle, ou -1 en cas d'erreur
|
||||
*/
|
||||
export function readBrightness() {
|
||||
try {
|
||||
const file = Gio.File.new_for_path(BRIGHTNESS_PATH);
|
||||
const [success, contents] = file.load_contents(null);
|
||||
|
||||
if (success) {
|
||||
return parseInt(new TextDecoder().decode(contents).trim());
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Erreur lors de la lecture de brightness:', e);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Écrit la brightness (niveau 0-3)
|
||||
* @param {number} level - Niveau d'intensité (0=Off, 1=Faible, 2=Moyen, 3=Fort)
|
||||
* @returns {boolean} true si succès
|
||||
*/
|
||||
export function writeBrightness(level) {
|
||||
try {
|
||||
// Valider et clamper le niveau
|
||||
level = Math.max(0, Math.min(3, Math.floor(level)));
|
||||
const value = BRIGHTNESS_LEVELS[level];
|
||||
|
||||
if (value === null || value === undefined) {
|
||||
console.error('Niveau de brightness invalide:', level);
|
||||
return false;
|
||||
}
|
||||
|
||||
const file = Gio.File.new_for_path(BRIGHTNESS_PATH);
|
||||
const contents = `${value}\n`;
|
||||
|
||||
const [success] = file.replace_contents(
|
||||
new TextEncoder().encode(contents),
|
||||
null,
|
||||
false,
|
||||
Gio.FileCreateFlags.NONE,
|
||||
null
|
||||
);
|
||||
|
||||
if (success) {
|
||||
console.log(`Brightness mise à ${level} (${value})`);
|
||||
}
|
||||
|
||||
return success;
|
||||
} catch (e) {
|
||||
console.error('Erreur lors de l\'écriture de brightness:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp une valeur RGB entre 0 et 255
|
||||
* @param {number} value - Valeur à clamper
|
||||
* @returns {number} Valeur clampée
|
||||
*/
|
||||
function clampRGB(value) {
|
||||
return Math.max(0, Math.min(255, Math.floor(value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applique le master gain aux valeurs RGB
|
||||
* @param {number} r - Rouge (0-255)
|
||||
* @param {number} g - Vert (0-255)
|
||||
* @param {number} b - Bleu (0-255)
|
||||
* @param {number} masterGain - Gain master (0-100)
|
||||
* @returns {Object} {r, g, b} avec gain appliqué
|
||||
*/
|
||||
function applyMasterGain(r, g, b, masterGain) {
|
||||
const gain = masterGain / 100.0;
|
||||
return {
|
||||
r: clampRGB(r * gain),
|
||||
g: clampRGB(g * gain),
|
||||
b: clampRGB(b * gain)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Écrit les valeurs RGB dans kbd_rgb_mode
|
||||
* @param {number} r - Rouge (0-255)
|
||||
* @param {number} g - Vert (0-255)
|
||||
* @param {number} b - Bleu (0-255)
|
||||
* @param {number} masterGain - Gain master (0-100), défaut 100
|
||||
* @returns {boolean} true si succès
|
||||
*/
|
||||
export function writeRGB(r, g, b, masterGain = 100) {
|
||||
try {
|
||||
// Clamper les valeurs
|
||||
r = clampRGB(r);
|
||||
g = clampRGB(g);
|
||||
b = clampRGB(b);
|
||||
|
||||
// Appliquer le master gain
|
||||
const adjusted = applyMasterGain(r, g, b, masterGain);
|
||||
|
||||
// Vérifier si brightness est > 0, sinon ne pas écrire
|
||||
const currentBrightness = readBrightness();
|
||||
if (currentBrightness === 0) {
|
||||
console.log('Brightness est 0, RGB mémorisé mais non appliqué');
|
||||
return true; // On considère cela comme un succès
|
||||
}
|
||||
|
||||
// Format: "1 0 R G B 0\n"
|
||||
const file = Gio.File.new_for_path(RGB_MODE_PATH);
|
||||
const contents = `1 0 ${adjusted.r} ${adjusted.g} ${adjusted.b} 0\n`;
|
||||
|
||||
const [success] = file.replace_contents(
|
||||
new TextEncoder().encode(contents),
|
||||
null,
|
||||
false,
|
||||
Gio.FileCreateFlags.NONE,
|
||||
null
|
||||
);
|
||||
|
||||
if (success) {
|
||||
console.log(`RGB mis à (${adjusted.r}, ${adjusted.g}, ${adjusted.b}) [master: ${masterGain}%]`);
|
||||
}
|
||||
|
||||
return success;
|
||||
} catch (e) {
|
||||
console.error('Erreur lors de l\'écriture RGB:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applique RGB avec debouncing pour éviter de spammer sysfs
|
||||
* @param {number} r - Rouge
|
||||
* @param {number} g - Vert
|
||||
* @param {number} b - Bleu
|
||||
* @param {number} masterGain - Gain master
|
||||
* @param {Function} callback - Callback optionnel appelé après l'écriture
|
||||
*/
|
||||
export function writeRGBDebounced(r, g, b, masterGain, callback = null) {
|
||||
// Annuler le timer précédent
|
||||
if (debounceTimer !== null) {
|
||||
GLib.source_remove(debounceTimer);
|
||||
debounceTimer = null;
|
||||
}
|
||||
|
||||
// Créer un nouveau timer
|
||||
debounceTimer = GLib.timeout_add(GLib.PRIORITY_DEFAULT, DEBOUNCE_DELAY, () => {
|
||||
writeRGB(r, g, b, masterGain);
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
debounceTimer = null;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse une chaîne preset "R,G,B" en objet
|
||||
* @param {string} presetString - Format "R,G,B"
|
||||
* @returns {Object} {r, g, b} ou null si invalide
|
||||
*/
|
||||
export function parsePreset(presetString) {
|
||||
try {
|
||||
const parts = presetString.split(',').map(s => parseInt(s.trim()));
|
||||
|
||||
if (parts.length === 3 && parts.every(n => !isNaN(n))) {
|
||||
return {
|
||||
r: clampRGB(parts[0]),
|
||||
g: clampRGB(parts[1]),
|
||||
b: clampRGB(parts[2])
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Erreur lors du parsing du preset:', e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit RGB en hex
|
||||
* @param {number} r - Rouge (0-255)
|
||||
* @param {number} g - Vert (0-255)
|
||||
* @param {number} b - Bleu (0-255)
|
||||
* @returns {string} Format "#RRGGBB"
|
||||
*/
|
||||
export function rgbToHex(r, g, b) {
|
||||
const toHex = (n) => {
|
||||
const hex = clampRGB(n).toString(16).toUpperCase();
|
||||
return hex.length === 1 ? '0' + hex : hex;
|
||||
};
|
||||
|
||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nettoie les ressources (annule les timers en cours)
|
||||
*/
|
||||
export function cleanup() {
|
||||
if (debounceTimer !== null) {
|
||||
GLib.source_remove(debounceTimer);
|
||||
debounceTimer = null;
|
||||
}
|
||||
}
|
||||
59
extension/extension.js
Normal file
59
extension/extension.js
Normal file
@@ -0,0 +1,59 @@
|
||||
// extension.js - Point d'entrée de l'extension GNOME Shell ASUS RGB Keyboard
|
||||
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
|
||||
import {KeyboardRGBIndicator} from './ui.js';
|
||||
|
||||
/**
|
||||
* Extension ASUS Keyboard RGB Control
|
||||
*/
|
||||
export default class AsusKeyboardRGBExtension extends Extension {
|
||||
constructor(metadata) {
|
||||
super(metadata);
|
||||
this._indicator = null;
|
||||
this._settings = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Active l'extension
|
||||
*/
|
||||
enable() {
|
||||
console.log('Activation de l\'extension ASUS Keyboard RGB');
|
||||
|
||||
try {
|
||||
// Charger les settings
|
||||
this._settings = this.getSettings();
|
||||
|
||||
// Créer l'indicateur dans le panneau
|
||||
this._indicator = new KeyboardRGBIndicator(this._settings);
|
||||
|
||||
// Ajouter au panneau (à droite, avant les menus système)
|
||||
Main.panel.addToStatusArea(
|
||||
this.metadata.uuid,
|
||||
this._indicator,
|
||||
1,
|
||||
'right'
|
||||
);
|
||||
|
||||
console.log('Extension ASUS Keyboard RGB activée avec succès');
|
||||
} catch (e) {
|
||||
console.error('Erreur lors de l\'activation de l\'extension:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Désactive l'extension
|
||||
*/
|
||||
disable() {
|
||||
console.log('Désactivation de l\'extension ASUS Keyboard RGB');
|
||||
|
||||
if (this._indicator) {
|
||||
this._indicator.destroy();
|
||||
this._indicator = null;
|
||||
}
|
||||
|
||||
this._settings = null;
|
||||
|
||||
console.log('Extension ASUS Keyboard RGB désactivée');
|
||||
}
|
||||
}
|
||||
11
extension/metadata.json
Normal file
11
extension/metadata.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"uuid": "asus-kbd-rgb@gilles",
|
||||
"name": "ASUS Keyboard RGB Control",
|
||||
"description": "Contrôle du rétroéclairage RGB du clavier ASUS via asus-nb-wmi",
|
||||
"version": 1,
|
||||
"shell-version": [
|
||||
"48"
|
||||
],
|
||||
"url": "https://github.com/gilles/gnome-asus-kbd-rgb",
|
||||
"settings-schema": "org.gnome.shell.extensions.asuskbdrgb"
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<schemalist>
|
||||
<schema id="org.gnome.shell.extensions.asuskbdrgb" path="/org/gnome/shell/extensions/asuskbdrgb/">
|
||||
|
||||
<!-- Valeurs RGB courantes -->
|
||||
<key name="red" type="i">
|
||||
<default>255</default>
|
||||
<summary>Composante rouge (0-255)</summary>
|
||||
<description>Valeur de la composante rouge du rétroéclairage</description>
|
||||
</key>
|
||||
|
||||
<key name="green" type="i">
|
||||
<default>165</default>
|
||||
<summary>Composante verte (0-255)</summary>
|
||||
<description>Valeur de la composante verte du rétroéclairage</description>
|
||||
</key>
|
||||
|
||||
<key name="blue" type="i">
|
||||
<default>0</default>
|
||||
<summary>Composante bleue (0-255)</summary>
|
||||
<description>Valeur de la composante bleue du rétroéclairage</description>
|
||||
</key>
|
||||
|
||||
<!-- Intensité -->
|
||||
<key name="brightness-level" type="i">
|
||||
<default>2</default>
|
||||
<summary>Niveau d'intensité (0-3)</summary>
|
||||
<description>Niveau d'intensité du rétroéclairage (0=Off, 1=Faible, 2=Moyen, 3=Fort)</description>
|
||||
</key>
|
||||
|
||||
<!-- Master slider -->
|
||||
<key name="master-gain" type="i">
|
||||
<default>100</default>
|
||||
<summary>Gain master (0-100)</summary>
|
||||
<description>Pourcentage de gain appliqué à toutes les composantes RGB</description>
|
||||
</key>
|
||||
|
||||
<!-- Step pour les sliders RGB -->
|
||||
<key name="rgb-step" type="i">
|
||||
<default>5</default>
|
||||
<summary>Pas d'ajustement RGB</summary>
|
||||
<description>Incrément lors de l'utilisation des sliders RGB</description>
|
||||
</key>
|
||||
|
||||
<!-- Presets couleur (9 presets GNOME officiels, stockés comme "R,G,B") -->
|
||||
<key name="preset-1" type="s">
|
||||
<default>"53,132,228"</default>
|
||||
<summary>Preset 1 - Bleu GNOME</summary>
|
||||
</key>
|
||||
|
||||
<key name="preset-2" type="s">
|
||||
<default>"51,209,122"</default>
|
||||
<summary>Preset 2 - Turquoise</summary>
|
||||
</key>
|
||||
|
||||
<key name="preset-3" type="s">
|
||||
<default>"87,227,137"</default>
|
||||
<summary>Preset 3 - Vert</summary>
|
||||
</key>
|
||||
|
||||
<key name="preset-4" type="s">
|
||||
<default>"246,211,45"</default>
|
||||
<summary>Preset 4 - Jaune</summary>
|
||||
</key>
|
||||
|
||||
<key name="preset-5" type="s">
|
||||
<default>"255,120,0"</default>
|
||||
<summary>Preset 5 - Orange</summary>
|
||||
</key>
|
||||
|
||||
<key name="preset-6" type="s">
|
||||
<default>"237,51,59"</default>
|
||||
<summary>Preset 6 - Rouge</summary>
|
||||
</key>
|
||||
|
||||
<key name="preset-7" type="s">
|
||||
<default>"246,97,81"</default>
|
||||
<summary>Preset 7 - Rose</summary>
|
||||
</key>
|
||||
|
||||
<key name="preset-8" type="s">
|
||||
<default>"145,65,172"</default>
|
||||
<summary>Preset 8 - Violet</summary>
|
||||
</key>
|
||||
|
||||
<key name="preset-9" type="s">
|
||||
<default>"119,118,123"</default>
|
||||
<summary>Preset 9 - Gris ardoise</summary>
|
||||
</key>
|
||||
|
||||
<!-- Mode master slider (pour évolutions futures) -->
|
||||
<key name="master-mode" type="s">
|
||||
<default>"gain"</default>
|
||||
<summary>Mode du slider master</summary>
|
||||
<description>Mode de fonctionnement du slider master: gain, offset, ou hsv</description>
|
||||
</key>
|
||||
|
||||
<!-- Synchronisation avec le thème GNOME -->
|
||||
<key name="sync-gnome-theme" type="b">
|
||||
<default>false</default>
|
||||
<summary>Synchroniser avec le thème GNOME</summary>
|
||||
<description>Applique automatiquement la couleur du clavier comme couleur d'accentuation GNOME</description>
|
||||
</key>
|
||||
|
||||
</schema>
|
||||
</schemalist>
|
||||
72
extension/stylesheet.css
Normal file
72
extension/stylesheet.css
Normal file
@@ -0,0 +1,72 @@
|
||||
/* stylesheet.css - Styles pour l'extension ASUS Keyboard RGB */
|
||||
|
||||
/* Titre du menu */
|
||||
.rgb-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
/* Boutons d'intensité */
|
||||
.brightness-button {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 6px;
|
||||
padding: 8px 16px;
|
||||
font-weight: normal;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.brightness-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.brightness-button.active {
|
||||
background-color: rgba(53, 132, 228, 0.8);
|
||||
border-color: rgba(53, 132, 228, 1);
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Ligne d'information */
|
||||
.info-line {
|
||||
font-family: monospace;
|
||||
font-size: 0.9em;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
/* Boutons preset */
|
||||
.preset-button {
|
||||
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.preset-button:hover {
|
||||
border-color: rgba(255, 255, 255, 0.6);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.preset-button:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* Boîte d'erreur */
|
||||
.error-box {
|
||||
padding: 12px;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
color: #ff6b6b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 0.9em;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
line-height: 1.4;
|
||||
}
|
||||
852
extension/ui.js
Normal file
852
extension/ui.js
Normal file
@@ -0,0 +1,852 @@
|
||||
// ui.js - Interface utilisateur pour le contrôle RGB du clavier ASUS
|
||||
import St from 'gi://St';
|
||||
import Clutter from 'gi://Clutter';
|
||||
import GObject from 'gi://GObject';
|
||||
import Gio from 'gi://Gio';
|
||||
import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
|
||||
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||||
import * as Slider from 'resource:///org/gnome/shell/ui/slider.js';
|
||||
|
||||
import * as Backend from './backend.js';
|
||||
|
||||
/**
|
||||
* Indicateur du panneau pour le contrôle RGB du clavier
|
||||
*/
|
||||
export const KeyboardRGBIndicator = GObject.registerClass(
|
||||
class KeyboardRGBIndicator extends PanelMenu.Button {
|
||||
_init(settings) {
|
||||
super._init(0.0, 'ASUS Keyboard RGB');
|
||||
|
||||
this._settings = settings;
|
||||
this._maxBrightness = Backend.getMaxBrightness();
|
||||
|
||||
// Charger les valeurs depuis GSettings
|
||||
this._currentR = this._settings.get_int('red');
|
||||
this._currentG = this._settings.get_int('green');
|
||||
this._currentB = this._settings.get_int('blue');
|
||||
this._currentMasterGain = this._settings.get_int('master-gain');
|
||||
this._currentBrightnessLevel = this._settings.get_int('brightness-level');
|
||||
|
||||
// Mode d'affichage (roue chromatique par défaut)
|
||||
this._colorPickerMode = 'wheel'; // 'sliders' ou 'wheel'
|
||||
|
||||
// Créer l'icône dans la top bar
|
||||
this._icon = new St.Icon({
|
||||
icon_name: 'keyboard-brightness-symbolic',
|
||||
style_class: 'system-status-icon'
|
||||
});
|
||||
this.add_child(this._icon);
|
||||
|
||||
// Vérifier le support matériel et les permissions
|
||||
if (!Backend.checkHardwareSupport()) {
|
||||
this._buildErrorUI('Matériel non supporté',
|
||||
'Aucun clavier ASUS RGB détecté sur ce système.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Backend.checkPermissions()) {
|
||||
this._buildErrorUI('Permissions insuffisantes',
|
||||
'Impossible d\'accéder au rétroéclairage.\nVeuillez configurer les règles udev.\n\nVoir: docs/INSTALL.md');
|
||||
return;
|
||||
}
|
||||
|
||||
// Construire l'UI normale
|
||||
this._buildUI();
|
||||
|
||||
// Restaurer l'état au démarrage
|
||||
this._applyCurrentState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit une UI d'erreur simplifiée
|
||||
*/
|
||||
_buildErrorUI(title, message) {
|
||||
const errorItem = new PopupMenu.PopupMenuItem('');
|
||||
const box = new St.BoxLayout({
|
||||
vertical: true,
|
||||
style_class: 'error-box',
|
||||
x_expand: true
|
||||
});
|
||||
|
||||
const titleLabel = new St.Label({
|
||||
text: `⚠️ ${title}`,
|
||||
style_class: 'error-title'
|
||||
});
|
||||
|
||||
const messageLabel = new St.Label({
|
||||
text: message,
|
||||
style_class: 'error-message'
|
||||
});
|
||||
|
||||
box.add_child(titleLabel);
|
||||
box.add_child(messageLabel);
|
||||
errorItem.actor.add_child(box);
|
||||
this.menu.addMenuItem(errorItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit l'interface utilisateur complète
|
||||
*/
|
||||
_buildUI() {
|
||||
// Section: Header avec titre et bouton de bascule
|
||||
this._buildHeader();
|
||||
|
||||
// Section: Boutons d'intensité (compact)
|
||||
this._buildBrightnessButtons();
|
||||
|
||||
// Section: Color picker (sliders ou roue chromatique)
|
||||
this._colorPickerContainer = new PopupMenu.PopupBaseMenuItem({
|
||||
reactive: false,
|
||||
can_focus: false
|
||||
});
|
||||
this.menu.addMenuItem(this._colorPickerContainer);
|
||||
|
||||
// Construire le mode par défaut
|
||||
this._rebuildColorPicker();
|
||||
|
||||
// Section: Presets (compact)
|
||||
this._buildPresets();
|
||||
|
||||
// Section: Synchronisation thème GNOME
|
||||
this._buildSyncThemeOption();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit le header avec titre et bouton de bascule
|
||||
*/
|
||||
_buildHeader() {
|
||||
const headerItem = new PopupMenu.PopupBaseMenuItem({
|
||||
reactive: false,
|
||||
can_focus: false,
|
||||
style_class: 'rgb-header'
|
||||
});
|
||||
|
||||
const headerBox = new St.BoxLayout({
|
||||
vertical: false,
|
||||
x_expand: true,
|
||||
style: 'spacing: 8px;'
|
||||
});
|
||||
|
||||
// Bouton de bascule slider/roue
|
||||
const toggleButton = new St.Button({
|
||||
style_class: 'button',
|
||||
x_expand: false,
|
||||
style: 'padding: 4px 8px; min-width: 32px;'
|
||||
});
|
||||
|
||||
const toggleIcon = new St.Icon({
|
||||
icon_name: this._colorPickerMode === 'sliders' ? 'view-grid-symbolic' : 'view-list-symbolic',
|
||||
icon_size: 16
|
||||
});
|
||||
toggleButton.set_child(toggleIcon);
|
||||
|
||||
toggleButton.connect('clicked', () => {
|
||||
this._colorPickerMode = this._colorPickerMode === 'sliders' ? 'wheel' : 'sliders';
|
||||
toggleIcon.icon_name = this._colorPickerMode === 'sliders' ? 'view-grid-symbolic' : 'view-list-symbolic';
|
||||
this._rebuildColorPicker();
|
||||
});
|
||||
|
||||
// Titre
|
||||
const titleLabel = new St.Label({
|
||||
text: 'Clavier RGB',
|
||||
style: 'font-weight: bold;',
|
||||
x_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER
|
||||
});
|
||||
|
||||
// Aperçu de couleur (déplacé dans le header)
|
||||
this._colorPreview = new St.Bin({
|
||||
style_class: 'color-preview',
|
||||
style: 'width: 40px; height: 24px; border-radius: 4px; border: 1px solid rgba(255,255,255,0.3);'
|
||||
});
|
||||
|
||||
headerBox.add_child(toggleButton);
|
||||
headerBox.add_child(titleLabel);
|
||||
headerBox.add_child(this._colorPreview);
|
||||
|
||||
headerItem.actor.add_child(headerBox);
|
||||
this.menu.addMenuItem(headerItem);
|
||||
|
||||
// Initialiser l'aperçu
|
||||
this._updateColorPreview();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconstruit le color picker selon le mode actuel
|
||||
*/
|
||||
_rebuildColorPicker() {
|
||||
// Vider le container
|
||||
this._colorPickerContainer.actor.remove_all_children();
|
||||
|
||||
if (this._colorPickerMode === 'sliders') {
|
||||
this._buildRGBSliders();
|
||||
} else {
|
||||
this._buildColorWheel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit les 4 boutons d'intensité (version compacte)
|
||||
*/
|
||||
_buildBrightnessButtons() {
|
||||
const buttonItem = new PopupMenu.PopupBaseMenuItem({
|
||||
reactive: false,
|
||||
can_focus: false
|
||||
});
|
||||
|
||||
const container = new St.BoxLayout({
|
||||
vertical: false,
|
||||
x_expand: true,
|
||||
style: 'spacing: 8px;'
|
||||
});
|
||||
|
||||
// Label compact
|
||||
const intensityLabel = new St.Label({
|
||||
text: 'Intensité :',
|
||||
style: 'font-weight: bold; font-size: 0.9em;',
|
||||
y_align: Clutter.ActorAlign.CENTER
|
||||
});
|
||||
container.add_child(intensityLabel);
|
||||
|
||||
// Boutons compacts
|
||||
const buttonBox = new St.BoxLayout({
|
||||
vertical: false,
|
||||
x_expand: true,
|
||||
style: 'spacing: 6px;'
|
||||
});
|
||||
|
||||
const buttonLabels = ['OFF', '1', '2', '3'];
|
||||
this._brightnessButtons = [];
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const button = new St.Button({
|
||||
label: buttonLabels[i],
|
||||
style_class: 'brightness-button',
|
||||
x_expand: true,
|
||||
can_focus: true,
|
||||
style: 'padding: 4px 8px; font-size: 0.9em;'
|
||||
});
|
||||
|
||||
button.level = i;
|
||||
button.connect('clicked', () => this._onBrightnessButtonClicked(i));
|
||||
|
||||
this._brightnessButtons.push(button);
|
||||
buttonBox.add_child(button);
|
||||
}
|
||||
|
||||
container.add_child(buttonBox);
|
||||
buttonItem.actor.add_child(container);
|
||||
this.menu.addMenuItem(buttonItem);
|
||||
|
||||
// Mettre à jour l'état actif
|
||||
this._updateBrightnessButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le style des boutons brightness (active/inactive)
|
||||
*/
|
||||
_updateBrightnessButtons() {
|
||||
this._brightnessButtons.forEach((button, index) => {
|
||||
if (index === this._currentBrightnessLevel) {
|
||||
button.add_style_class_name('active');
|
||||
} else {
|
||||
button.remove_style_class_name('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback: clic sur un bouton brightness
|
||||
*/
|
||||
_onBrightnessButtonClicked(level) {
|
||||
this._currentBrightnessLevel = level;
|
||||
this._settings.set_int('brightness-level', level);
|
||||
|
||||
Backend.writeBrightness(level);
|
||||
this._updateBrightnessButtons();
|
||||
this._updateInfoLine();
|
||||
|
||||
// Si brightness > 0, réappliquer RGB
|
||||
if (level > 0) {
|
||||
Backend.writeRGB(this._currentR, this._currentG, this._currentB, this._currentMasterGain);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit les sliders RGB + Master (version compacte)
|
||||
*/
|
||||
_buildRGBSliders() {
|
||||
const container = new St.BoxLayout({
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
style: 'spacing: 4px; padding: 4px;'
|
||||
});
|
||||
|
||||
// Sliders RGB compacts
|
||||
const sliders = [
|
||||
{ label: 'R', color: '#ff4444', prop: '_currentR', key: 'red', max: 255 },
|
||||
{ label: 'V', color: '#44ff44', prop: '_currentG', key: 'green', max: 255 },
|
||||
{ label: 'B', color: '#4444ff', prop: '_currentB', key: 'blue', max: 255 },
|
||||
{ label: 'M', color: '#ffffff', prop: '_currentMasterGain', key: 'master-gain', max: 100 }
|
||||
];
|
||||
|
||||
sliders.forEach(({ label, color, prop, key, max }) => {
|
||||
const sliderBox = new St.BoxLayout({
|
||||
vertical: false,
|
||||
x_expand: true,
|
||||
style: 'spacing: 8px;'
|
||||
});
|
||||
|
||||
const sliderLabel = new St.Label({
|
||||
text: label,
|
||||
style: `min-width: 12px; color: ${color}; font-weight: bold; font-size: 0.9em;`,
|
||||
y_align: Clutter.ActorAlign.CENTER
|
||||
});
|
||||
|
||||
const slider = new Slider.Slider(this[prop] / max);
|
||||
slider.accessible_name = label;
|
||||
|
||||
slider.connect('notify::value', () => {
|
||||
const newValue = Math.floor(slider.value * max);
|
||||
this[prop] = newValue;
|
||||
this._settings.set_int(key, newValue);
|
||||
this._onRGBChanged();
|
||||
});
|
||||
|
||||
// Stocker la référence
|
||||
if (label === 'R') this._redSlider = slider;
|
||||
else if (label === 'V') this._greenSlider = slider;
|
||||
else if (label === 'B') this._blueSlider = slider;
|
||||
else if (label === 'M') this._masterSlider = slider;
|
||||
|
||||
sliderBox.add_child(sliderLabel);
|
||||
sliderBox.add_child(slider);
|
||||
container.add_child(sliderBox);
|
||||
});
|
||||
|
||||
this._colorPickerContainer.actor.add_child(container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit la roue chromatique (grille de couleurs cliquables)
|
||||
*/
|
||||
_buildColorWheel() {
|
||||
const container = new St.BoxLayout({
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
style: 'spacing: 8px; padding: 8px;'
|
||||
});
|
||||
|
||||
// Grille de couleurs en forme de cercle (optimisée pour la visibilité)
|
||||
const gridBox = new St.BoxLayout({
|
||||
vertical: true,
|
||||
style: 'spacing: 3px;'
|
||||
});
|
||||
|
||||
const size = 12; // Grille 12x12 (réduit pour boutons plus grands)
|
||||
const cellSize = 18; // Augmenté de 12px à 18px
|
||||
const centerX = size / 2;
|
||||
const centerY = size / 2;
|
||||
const maxRadius = size / 2 - 1;
|
||||
|
||||
// Stocker les boutons de la roue pour la surbrillance
|
||||
this._wheelButtons = [];
|
||||
|
||||
for (let y = 0; y < size; y++) {
|
||||
const row = new St.BoxLayout({
|
||||
vertical: false,
|
||||
style: 'spacing: 3px;'
|
||||
});
|
||||
|
||||
for (let x = 0; x < size; x++) {
|
||||
const dx = x - centerX;
|
||||
const dy = y - centerY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance <= maxRadius) {
|
||||
// Calculer teinte et saturation
|
||||
const angle = Math.atan2(dy, dx);
|
||||
const hue = ((angle * 180 / Math.PI) + 360) % 360;
|
||||
const saturation = distance / maxRadius;
|
||||
|
||||
// Convertir HSL vers RGB
|
||||
const rgb = this._hslToRgb(hue, saturation, 0.5);
|
||||
const hex = Backend.rgbToHex(rgb.r, rgb.g, rgb.b);
|
||||
|
||||
const button = new St.Button({
|
||||
style: `
|
||||
background-color: ${hex};
|
||||
width: ${cellSize}px;
|
||||
height: ${cellSize}px;
|
||||
border-radius: 3px;
|
||||
padding: 0;
|
||||
min-width: ${cellSize}px;
|
||||
min-height: ${cellSize}px;
|
||||
border: 1px solid rgba(0,0,0,0.3);
|
||||
`,
|
||||
reactive: true,
|
||||
track_hover: true
|
||||
});
|
||||
|
||||
// Stocker les infos de couleur pour comparaison
|
||||
button._rgb = { r: rgb.r, g: rgb.g, b: rgb.b };
|
||||
button._baseStyle = `
|
||||
background-color: ${hex};
|
||||
width: ${cellSize}px;
|
||||
height: ${cellSize}px;
|
||||
border-radius: 3px;
|
||||
padding: 0;
|
||||
min-width: ${cellSize}px;
|
||||
min-height: ${cellSize}px;
|
||||
border: 1px solid rgba(0,0,0,0.3);
|
||||
`;
|
||||
|
||||
button.connect('clicked', () => {
|
||||
this._currentR = rgb.r;
|
||||
this._currentG = rgb.g;
|
||||
this._currentB = rgb.b;
|
||||
|
||||
this._settings.set_int('red', this._currentR);
|
||||
this._settings.set_int('green', this._currentG);
|
||||
this._settings.set_int('blue', this._currentB);
|
||||
|
||||
// Mettre à jour la surbrillance
|
||||
this._updateWheelSelection();
|
||||
|
||||
this._onRGBChanged();
|
||||
});
|
||||
|
||||
this._wheelButtons.push(button);
|
||||
row.add_child(button);
|
||||
} else {
|
||||
// Espace vide en dehors du cercle
|
||||
const spacer = new St.Bin({
|
||||
style: `width: ${cellSize}px; height: ${cellSize}px;`
|
||||
});
|
||||
row.add_child(spacer);
|
||||
}
|
||||
}
|
||||
|
||||
gridBox.add_child(row);
|
||||
}
|
||||
|
||||
container.add_child(gridBox);
|
||||
|
||||
// Slider Master en dessous
|
||||
const masterBox = new St.BoxLayout({
|
||||
vertical: false,
|
||||
x_expand: true,
|
||||
style: 'spacing: 8px; margin-top: 8px;'
|
||||
});
|
||||
|
||||
const masterLabel = new St.Label({
|
||||
text: 'Luminosité',
|
||||
style: 'font-weight: bold; font-size: 0.9em; min-width: 60px;',
|
||||
y_align: Clutter.ActorAlign.CENTER
|
||||
});
|
||||
|
||||
const masterSlider = new Slider.Slider(this._currentMasterGain / 100);
|
||||
masterSlider.accessible_name = 'Master';
|
||||
|
||||
masterSlider.connect('notify::value', () => {
|
||||
this._currentMasterGain = Math.floor(masterSlider.value * 100);
|
||||
this._settings.set_int('master-gain', this._currentMasterGain);
|
||||
this._onRGBChanged();
|
||||
});
|
||||
|
||||
this._masterSlider = masterSlider;
|
||||
|
||||
masterBox.add_child(masterLabel);
|
||||
masterBox.add_child(masterSlider);
|
||||
container.add_child(masterBox);
|
||||
|
||||
this._colorPickerContainer.actor.add_child(container);
|
||||
|
||||
// Initialiser la surbrillance de la couleur actuelle
|
||||
this._updateWheelSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour la surbrillance de la couleur sélectionnée dans la roue
|
||||
*/
|
||||
_updateWheelSelection() {
|
||||
if (!this._wheelButtons) return;
|
||||
|
||||
this._wheelButtons.forEach(button => {
|
||||
const rgb = button._rgb;
|
||||
// Comparer avec une tolérance (les couleurs HSL peuvent varier légèrement)
|
||||
const tolerance = 10;
|
||||
const matches =
|
||||
Math.abs(rgb.r - this._currentR) <= tolerance &&
|
||||
Math.abs(rgb.g - this._currentG) <= tolerance &&
|
||||
Math.abs(rgb.b - this._currentB) <= tolerance;
|
||||
|
||||
if (matches) {
|
||||
// Bordure blanche épaisse pour la sélection
|
||||
button.style = button._baseStyle.replace(
|
||||
'border: 1px solid rgba(0,0,0,0.3)',
|
||||
'border: 3px solid white; box-shadow: 0 0 5px rgba(255,255,255,0.8)'
|
||||
);
|
||||
} else {
|
||||
// Style normal
|
||||
button.style = button._baseStyle;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour la surbrillance du preset sélectionné
|
||||
*/
|
||||
_updatePresetSelection() {
|
||||
if (!this._presetButtons) return;
|
||||
|
||||
this._presetButtons.forEach(button => {
|
||||
const preset = button._preset;
|
||||
// Comparer avec une tolérance
|
||||
const tolerance = 10;
|
||||
const matches =
|
||||
Math.abs(preset.r - this._currentR) <= tolerance &&
|
||||
Math.abs(preset.g - this._currentG) <= tolerance &&
|
||||
Math.abs(preset.b - this._currentB) <= tolerance;
|
||||
|
||||
if (matches) {
|
||||
// Cercle blanc épais autour du preset sélectionné
|
||||
button.style = button._baseStyle + ' border: 3px solid white; box-shadow: 0 0 5px rgba(255,255,255,0.8);';
|
||||
} else {
|
||||
// Style normal
|
||||
button.style = button._baseStyle + ' border: 2px solid rgba(255,255,255,0.3);';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve la couleur accent GNOME la plus proche d'une couleur RGB
|
||||
*/
|
||||
_rgbToGnomeAccent(r, g, b) {
|
||||
const colors = {
|
||||
blue: { r: 53, g: 132, b: 228 }, // Bleu GNOME
|
||||
teal: { r: 51, g: 209, b: 122 }, // Turquoise
|
||||
green: { r: 87, g: 227, b: 137 }, // Vert
|
||||
yellow: { r: 246, g: 211, b: 45 }, // Jaune
|
||||
orange: { r: 255, g: 120, b: 0 }, // Orange
|
||||
red: { r: 237, g: 51, b: 59 }, // Rouge
|
||||
pink: { r: 246, g: 97, b: 81 }, // Rose
|
||||
purple: { r: 145, g: 65, b: 172 }, // Violet
|
||||
slate: { r: 119, g: 118, b: 123 } // Gris ardoise
|
||||
};
|
||||
|
||||
let minDistance = Infinity;
|
||||
let closestColor = 'blue';
|
||||
|
||||
// Calculer la distance euclidienne dans l'espace RGB
|
||||
for (const [name, color] of Object.entries(colors)) {
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(r - color.r, 2) +
|
||||
Math.pow(g - color.g, 2) +
|
||||
Math.pow(b - color.b, 2)
|
||||
);
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
closestColor = name;
|
||||
}
|
||||
}
|
||||
|
||||
return closestColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronise la couleur du clavier avec le thème GNOME
|
||||
*/
|
||||
_syncGnomeTheme() {
|
||||
// Vérifier si la synchronisation est activée
|
||||
if (!this._settings.get_boolean('sync-gnome-theme')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Trouver la couleur accent GNOME la plus proche
|
||||
const accentColor = this._rgbToGnomeAccent(
|
||||
this._currentR,
|
||||
this._currentG,
|
||||
this._currentB
|
||||
);
|
||||
|
||||
try {
|
||||
// Appliquer la couleur d'accentuation GNOME
|
||||
const interfaceSettings = new Gio.Settings({
|
||||
schema: 'org.gnome.desktop.interface'
|
||||
});
|
||||
interfaceSettings.set_string('accent-color', accentColor);
|
||||
|
||||
log(`[ASUS RGB] Thème GNOME synchronisé → ${accentColor} (RGB: ${this._currentR}, ${this._currentG}, ${this._currentB})`);
|
||||
} catch (error) {
|
||||
logError(error, '[ASUS RGB] Erreur lors de la synchronisation du thème GNOME');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback: RGB ou Master a changé
|
||||
*/
|
||||
_onRGBChanged() {
|
||||
// Mettre à jour l'aperçu de couleur
|
||||
this._updateColorPreview();
|
||||
|
||||
// Mettre à jour les surbrillances
|
||||
this._updateWheelSelection();
|
||||
this._updatePresetSelection();
|
||||
|
||||
// Synchroniser avec le thème GNOME si activé
|
||||
this._syncGnomeTheme();
|
||||
|
||||
// Appliquer avec debouncing
|
||||
Backend.writeRGBDebounced(
|
||||
this._currentR,
|
||||
this._currentG,
|
||||
this._currentB,
|
||||
this._currentMasterGain,
|
||||
() => this._updateInfoLine()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit RGB (0-255) vers HSL (H: 0-360, S: 0-1, L: 0-1)
|
||||
*/
|
||||
_rgbToHsl(r, g, b) {
|
||||
r /= 255;
|
||||
g /= 255;
|
||||
b /= 255;
|
||||
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
let h, s, l = (max + min) / 2;
|
||||
|
||||
if (max === min) {
|
||||
h = s = 0; // Gris (pas de saturation)
|
||||
} else {
|
||||
const d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
|
||||
switch (max) {
|
||||
case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
|
||||
case g: h = ((b - r) / d + 2) / 6; break;
|
||||
case b: h = ((r - g) / d + 4) / 6; break;
|
||||
}
|
||||
}
|
||||
|
||||
return { h: h * 360, s: s, l: l };
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit HSL (H: 0-360, S: 0-1, L: 0-1) vers RGB (0-255)
|
||||
*/
|
||||
_hslToRgb(h, s, l) {
|
||||
h /= 360;
|
||||
|
||||
let r, g, b;
|
||||
|
||||
if (s === 0) {
|
||||
r = g = b = l; // Gris
|
||||
} else {
|
||||
const hue2rgb = (p, q, t) => {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1/6) return p + (q - p) * 6 * t;
|
||||
if (t < 1/2) return q;
|
||||
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
||||
return p;
|
||||
};
|
||||
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
|
||||
r = hue2rgb(p, q, h + 1/3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1/3);
|
||||
}
|
||||
|
||||
return {
|
||||
r: Math.round(r * 255),
|
||||
g: Math.round(g * 255),
|
||||
b: Math.round(b * 255)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour l'aperçu de couleur (version HSL avec correction gamma)
|
||||
*/
|
||||
_updateColorPreview() {
|
||||
if (this._colorPreview) {
|
||||
// Convertir RGB vers HSL
|
||||
const hsl = this._rgbToHsl(this._currentR, this._currentG, this._currentB);
|
||||
|
||||
// Appliquer correction gamma pour progression perceptuelle naturelle
|
||||
// Gamma 2.2 est la norme pour les écrans sRGB
|
||||
const GAMMA = 2.2;
|
||||
const gain = this._currentMasterGain / 100.0;
|
||||
|
||||
// Linéariser la luminosité, appliquer le gain, puis re-gamma
|
||||
const linearL = Math.pow(hsl.l, GAMMA);
|
||||
const adjustedLinearL = linearL * gain;
|
||||
hsl.l = Math.pow(adjustedLinearL, 1 / GAMMA);
|
||||
|
||||
// Reconvertir HSL vers RGB
|
||||
const rgb = this._hslToRgb(hsl.h, hsl.s, hsl.l);
|
||||
|
||||
const hex = Backend.rgbToHex(rgb.r, rgb.g, rgb.b);
|
||||
this._colorPreview.style = `background-color: ${hex}; width: 50px; height: 30px; border-radius: 4px; border: 1px solid rgba(255,255,255,0.3);`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit la ligne d'information
|
||||
*/
|
||||
/**
|
||||
* Met à jour la ligne d'information (vide - infos dans l'aperçu)
|
||||
*/
|
||||
_updateInfoLine() {
|
||||
// Les informations sont maintenant affichées via l'aperçu de couleur dans le header
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit les 6 presets couleur
|
||||
*/
|
||||
/**
|
||||
* Construit les presets de couleurs (version compacte)
|
||||
*/
|
||||
_buildPresets() {
|
||||
const presetItem = new PopupMenu.PopupBaseMenuItem({
|
||||
reactive: false,
|
||||
can_focus: false
|
||||
});
|
||||
|
||||
const container = new St.BoxLayout({
|
||||
vertical: false,
|
||||
x_expand: true,
|
||||
style: 'spacing: 6px;'
|
||||
});
|
||||
|
||||
const presetLabel = new St.Label({
|
||||
text: 'Presets :',
|
||||
style: 'font-weight: bold; font-size: 0.9em;',
|
||||
y_align: Clutter.ActorAlign.CENTER
|
||||
});
|
||||
container.add_child(presetLabel);
|
||||
|
||||
// Créer 9 boutons preset compacts (couleurs GNOME) - ronds
|
||||
const buttonBox = new St.BoxLayout({
|
||||
vertical: false,
|
||||
x_expand: true,
|
||||
style: 'spacing: 3px;'
|
||||
});
|
||||
|
||||
// Stocker les boutons pour la mise à jour de la sélection
|
||||
this._presetButtons = [];
|
||||
|
||||
for (let i = 1; i <= 9; i++) {
|
||||
const presetString = this._settings.get_string(`preset-${i}`);
|
||||
const preset = Backend.parsePreset(presetString);
|
||||
|
||||
if (preset) {
|
||||
const button = new St.Button({
|
||||
style_class: 'button',
|
||||
x_expand: true,
|
||||
can_focus: true
|
||||
});
|
||||
|
||||
const hex = Backend.rgbToHex(preset.r, preset.g, preset.b);
|
||||
// Ronds avec border-radius 50%
|
||||
const baseStyle = `background-color: ${hex}; width: 26px; height: 26px; border-radius: 50%; padding: 0;`;
|
||||
button.style = baseStyle + ' border: 2px solid rgba(255,255,255,0.3);';
|
||||
|
||||
// Stocker les infos pour la mise à jour
|
||||
button._preset = preset;
|
||||
button._baseStyle = baseStyle;
|
||||
|
||||
button.connect('clicked', () => this._onPresetClicked(preset));
|
||||
buttonBox.add_child(button);
|
||||
this._presetButtons.push(button);
|
||||
}
|
||||
}
|
||||
|
||||
container.add_child(buttonBox);
|
||||
presetItem.actor.add_child(container);
|
||||
this.menu.addMenuItem(presetItem);
|
||||
|
||||
// Initialiser la surbrillance du preset actuel
|
||||
this._updatePresetSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit l'option de synchronisation avec le thème GNOME
|
||||
*/
|
||||
_buildSyncThemeOption() {
|
||||
const syncItem = new PopupMenu.PopupSwitchMenuItem(
|
||||
'Synchroniser thème GNOME',
|
||||
this._settings.get_boolean('sync-gnome-theme')
|
||||
);
|
||||
|
||||
syncItem.connect('toggled', (item) => {
|
||||
this._settings.set_boolean('sync-gnome-theme', item.state);
|
||||
|
||||
// Si activé, appliquer immédiatement
|
||||
if (item.state) {
|
||||
this._syncGnomeTheme();
|
||||
} else {
|
||||
// Si désactivé, restaurer la couleur GNOME par défaut (blue)
|
||||
try {
|
||||
const interfaceSettings = new Gio.Settings({
|
||||
schema: 'org.gnome.desktop.interface'
|
||||
});
|
||||
interfaceSettings.set_string('accent-color', 'blue');
|
||||
log('[ASUS RGB] Thème GNOME restauré → blue (défaut)');
|
||||
} catch (error) {
|
||||
logError(error, '[ASUS RGB] Erreur lors de la restauration du thème GNOME');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.menu.addMenuItem(syncItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback: clic sur un preset
|
||||
*/
|
||||
_onPresetClicked(preset) {
|
||||
this._currentR = preset.r;
|
||||
this._currentG = preset.g;
|
||||
this._currentB = preset.b;
|
||||
|
||||
// Sauvegarder
|
||||
this._settings.set_int('red', this._currentR);
|
||||
this._settings.set_int('green', this._currentG);
|
||||
this._settings.set_int('blue', this._currentB);
|
||||
|
||||
// Mettre à jour les sliders si en mode sliders
|
||||
if (this._redSlider) {
|
||||
this._redSlider.value = this._currentR / 255;
|
||||
this._greenSlider.value = this._currentG / 255;
|
||||
this._blueSlider.value = this._currentB / 255;
|
||||
}
|
||||
|
||||
// Appliquer via _onRGBChanged qui gère tout (aperçu, surbrillances, sync thème, etc.)
|
||||
this._onRGBChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applique l'état actuel au matériel (au démarrage)
|
||||
*/
|
||||
_applyCurrentState() {
|
||||
Backend.writeBrightness(this._currentBrightnessLevel);
|
||||
|
||||
if (this._currentBrightnessLevel > 0) {
|
||||
Backend.writeRGB(this._currentR, this._currentG, this._currentB, this._currentMasterGain);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Nettoyage lors de la destruction
|
||||
*/
|
||||
destroy() {
|
||||
Backend.cleanup();
|
||||
super.destroy();
|
||||
}
|
||||
});
|
||||
@@ -167,13 +167,22 @@ Claude doit demander au début, puis implémenter par défaut si pas de réponse
|
||||
## 13) Prompt d’exécution pour Claude Code
|
||||
Tu es **Claude Code** (mode dev). Tu dois produire un projet complet dans un repo Git.
|
||||
Contraintes :
|
||||
- tes reponse seront en francais
|
||||
- commentaires en francais
|
||||
- GNOME Shell 48 (GJS)
|
||||
- Pas de dépendances externes
|
||||
- Code lisible, modulaire, commenté
|
||||
- Robustesse permissions + erreurs
|
||||
- UX immédiate et fluide
|
||||
- choix des icones parmis des elemnt gratuit, libre ou opensource
|
||||
- les fichiers de documentation seront dans un dossier 'docs'
|
||||
- maintien a jours un changelog qui sera utilise pour reprendre le devellopement de l'app
|
||||
|
||||
Plan de travail exigé :
|
||||
0.0 Analyse complete du projet
|
||||
0.1 Popose un schema webui (format ascii draw, mermaid, excalidraw, ....)
|
||||
0.2 Creation d'un ToDO evolutif que tu mettra jours ( a faire, terminer, a tester, annulé, amelioration, ...)
|
||||
0.3 Creation d'un changelog a mettre ajours frequement ( tu me posera la question) . les element qui y seront inscrit seront pertinent pour une reprise du devellopement
|
||||
1. Générer l’arborescence complète
|
||||
2. Implémenter backend sysfs + debounce
|
||||
3. Implémenter UI popover (table + sliders + presets)
|
||||
@@ -181,4 +190,6 @@ Plan de travail exigé :
|
||||
5. Documenter installation + règle udev + troubleshooting
|
||||
6. Tester et fournir checklist
|
||||
|
||||
|
||||
|
||||
Fin.
|
||||
|
||||
165
tools/install-local.sh
Executable file
165
tools/install-local.sh
Executable file
@@ -0,0 +1,165 @@
|
||||
#!/bin/bash
|
||||
# install-local.sh - Script d'installation locale pour l'extension ASUS Keyboard RGB
|
||||
|
||||
set -e # Arrêter en cas d'erreur
|
||||
|
||||
# Couleurs pour l'affichage
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
EXTENSION_UUID="asus-kbd-rgb@gilles"
|
||||
EXTENSION_DIR="$HOME/.local/share/gnome-shell/extensions/$EXTENSION_UUID"
|
||||
SOURCE_DIR="$(cd "$(dirname "$0")/.." && pwd)/extension"
|
||||
|
||||
echo -e "${BLUE}=== Installation de l'extension ASUS Keyboard RGB ===${NC}\n"
|
||||
|
||||
# Vérifier que le dossier source existe
|
||||
if [ ! -d "$SOURCE_DIR" ]; then
|
||||
echo -e "${RED}Erreur: Dossier source non trouvé: $SOURCE_DIR${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Vérifier GNOME Shell
|
||||
if ! command -v gnome-shell &> /dev/null; then
|
||||
echo -e "${RED}Erreur: GNOME Shell n'est pas installé${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GNOME_VERSION=$(gnome-shell --version | grep -oP '\d+' | head -1)
|
||||
echo -e "${GREEN}✓${NC} GNOME Shell version: $GNOME_VERSION"
|
||||
|
||||
if [ "$GNOME_VERSION" != "48" ]; then
|
||||
echo -e "${YELLOW}⚠ Attention: Cette extension est conçue pour GNOME Shell 48${NC}"
|
||||
echo -e "${YELLOW} Votre version: $GNOME_VERSION${NC}"
|
||||
read -p "Continuer quand même ? (o/N) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Oo]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Vérifier le support matériel
|
||||
echo -e "\n${BLUE}Vérification du support matériel...${NC}"
|
||||
if [ ! -d "/sys/class/leds/asus::kbd_backlight" ]; then
|
||||
echo -e "${RED}✗ Clavier ASUS RGB non détecté${NC}"
|
||||
echo -e "${YELLOW} Les fichiers sysfs sont absents: /sys/class/leds/asus::kbd_backlight/${NC}"
|
||||
echo -e "${YELLOW} L'extension s'installera mais ne fonctionnera pas sur ce système.${NC}"
|
||||
read -p "Continuer quand même ? (o/N) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Oo]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${GREEN}✓${NC} Clavier ASUS RGB détecté"
|
||||
fi
|
||||
|
||||
# Vérifier les permissions
|
||||
echo -e "\n${BLUE}Vérification des permissions...${NC}"
|
||||
if [ -w "/sys/class/leds/asus::kbd_backlight/brightness" ]; then
|
||||
echo -e "${GREEN}✓${NC} Permissions d'écriture OK"
|
||||
|
||||
# Vérifier que le groupe est bien kbdled
|
||||
BRIGHTNESS_GROUP=$(stat -c '%G' /sys/class/leds/asus::kbd_backlight/brightness 2>/dev/null)
|
||||
if [ "$BRIGHTNESS_GROUP" = "kbdled" ]; then
|
||||
echo -e "${GREEN}✓${NC} Groupe kbdled configuré correctement"
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Permissions insuffisantes${NC}"
|
||||
echo -e "${YELLOW} Vous devez configurer les règles udev pour utiliser l'extension.${NC}"
|
||||
echo -e "${YELLOW} Voir: docs/INSTALL.md section 'Configuration des permissions'${NC}"
|
||||
echo -e ""
|
||||
echo -e "${BLUE}Commandes rapides :${NC}"
|
||||
echo -e " sudo tee /etc/udev/rules.d/99-asus-kbd.rules > /dev/null << 'EOF'"
|
||||
echo -e " ACTION==\"add\", SUBSYSTEM==\"leds\", KERNEL==\"asus::kbd_backlight\", TAG+=\"uaccess\", RUN+=\"/bin/sh -c 'chgrp kbdled /sys/class/leds/asus::kbd_backlight/brightness && chmod g+w /sys/class/leds/asus::kbd_backlight/brightness'\""
|
||||
echo -e " ACTION==\"add\", SUBSYSTEM==\"leds\", KERNEL==\"asus::kbd_backlight\", TAG+=\"uaccess\", RUN+=\"/bin/sh -c 'chgrp kbdled /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode && chmod g+w /sys/class/leds/asus::kbd_backlight/kbd_rgb_mode'\""
|
||||
echo -e " EOF"
|
||||
echo -e " sudo groupadd -f kbdled"
|
||||
echo -e " sudo usermod -aG kbdled \$USER"
|
||||
echo -e " sudo udevadm control --reload-rules"
|
||||
echo -e " sudo modprobe -r asus_nb_wmi && sudo modprobe asus_nb_wmi"
|
||||
echo -e ""
|
||||
fi
|
||||
|
||||
# Désactiver l'extension si elle est active
|
||||
if gnome-extensions list | grep -q "$EXTENSION_UUID"; then
|
||||
echo -e "\n${BLUE}Désactivation de l'extension existante...${NC}"
|
||||
gnome-extensions disable "$EXTENSION_UUID" 2>/dev/null || true
|
||||
echo -e "${GREEN}✓${NC} Extension désactivée"
|
||||
fi
|
||||
|
||||
# Créer le dossier de destination
|
||||
echo -e "\n${BLUE}Création du dossier d'installation...${NC}"
|
||||
mkdir -p "$EXTENSION_DIR"
|
||||
echo -e "${GREEN}✓${NC} Dossier créé: $EXTENSION_DIR"
|
||||
|
||||
# Copier les fichiers
|
||||
echo -e "\n${BLUE}Copie des fichiers...${NC}"
|
||||
cp -v "$SOURCE_DIR/extension.js" "$EXTENSION_DIR/"
|
||||
cp -v "$SOURCE_DIR/ui.js" "$EXTENSION_DIR/"
|
||||
cp -v "$SOURCE_DIR/backend.js" "$EXTENSION_DIR/"
|
||||
cp -v "$SOURCE_DIR/metadata.json" "$EXTENSION_DIR/"
|
||||
cp -v "$SOURCE_DIR/stylesheet.css" "$EXTENSION_DIR/"
|
||||
|
||||
# Copier le schéma GSettings
|
||||
echo -e "\n${BLUE}Copie du schéma GSettings...${NC}"
|
||||
mkdir -p "$EXTENSION_DIR/schemas"
|
||||
cp -v "$SOURCE_DIR/schemas/org.gnome.shell.extensions.asuskbdrgb.gschema.xml" "$EXTENSION_DIR/schemas/"
|
||||
|
||||
# Compiler le schéma
|
||||
echo -e "\n${BLUE}Compilation du schéma GSettings...${NC}"
|
||||
if command -v glib-compile-schemas &> /dev/null; then
|
||||
glib-compile-schemas "$EXTENSION_DIR/schemas/"
|
||||
echo -e "${GREEN}✓${NC} Schéma compilé"
|
||||
else
|
||||
echo -e "${RED}✗ glib-compile-schemas non trouvé${NC}"
|
||||
echo -e "${YELLOW} Installez glib-2.0-dev ou libglib2.0-bin${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Vérifier l'installation
|
||||
echo -e "\n${BLUE}Vérification de l'installation...${NC}"
|
||||
if [ -f "$EXTENSION_DIR/extension.js" ] && [ -f "$EXTENSION_DIR/schemas/gschemas.compiled" ]; then
|
||||
echo -e "${GREEN}✓${NC} Installation réussie"
|
||||
else
|
||||
echo -e "${RED}✗ Installation incomplète${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Activer l'extension
|
||||
echo -e "\n${BLUE}Activation de l'extension...${NC}"
|
||||
gnome-extensions enable "$EXTENSION_UUID"
|
||||
echo -e "${GREEN}✓${NC} Extension activée"
|
||||
|
||||
# Instructions finales
|
||||
echo -e "\n${GREEN}=== Installation terminée ===${NC}\n"
|
||||
|
||||
echo -e "${BLUE}Prochaines étapes :${NC}"
|
||||
|
||||
# Vérifier le type de session
|
||||
if [ "$XDG_SESSION_TYPE" = "wayland" ]; then
|
||||
echo -e " 1. ${YELLOW}Déconnectez-vous et reconnectez-vous${NC} (session Wayland)"
|
||||
else
|
||||
echo -e " 1. Rechargez GNOME Shell : ${YELLOW}Alt+F2${NC}, tapez ${YELLOW}r${NC}, appuyez sur Entrée"
|
||||
fi
|
||||
|
||||
echo -e " 2. L'icône devrait apparaître dans la barre supérieure"
|
||||
echo -e " 3. Cliquez sur l'icône pour contrôler le rétroéclairage\n"
|
||||
|
||||
if [ ! -w "/sys/class/leds/asus::kbd_backlight/brightness" ]; then
|
||||
echo -e "${YELLOW}⚠ IMPORTANT :${NC}"
|
||||
echo -e " Vous devez configurer les permissions udev avant utilisation."
|
||||
echo -e " Consultez ${BLUE}docs/INSTALL.md${NC} pour les instructions.\n"
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}Logs en temps réel :${NC}"
|
||||
echo -e " journalctl -f -o cat /usr/bin/gnome-shell | grep -i asus\n"
|
||||
|
||||
echo -e "${BLUE}Désinstallation :${NC}"
|
||||
echo -e " gnome-extensions disable $EXTENSION_UUID"
|
||||
echo -e " rm -rf $EXTENSION_DIR\n"
|
||||
|
||||
echo -e "${GREEN}Profitez de votre clavier RGB ! 🎨${NC}"
|
||||
Reference in New Issue
Block a user