diff --git a/frontend/assets/logos/copydev.png b/frontend/assets/logos/copydev.png new file mode 100644 index 0000000..30a61eb Binary files /dev/null and b/frontend/assets/logos/copydev.png differ diff --git a/scripts/grub-theme/background.png b/scripts/grub-theme/background.png index 683466d..e9a4fb6 100644 Binary files a/scripts/grub-theme/background.png and b/scripts/grub-theme/background.png differ diff --git a/scripts/grub-theme/icons/autre.png b/scripts/grub-theme/icons/autre.png index ee0ceab..de44cd6 100644 Binary files a/scripts/grub-theme/icons/autre.png and b/scripts/grub-theme/icons/autre.png differ diff --git a/scripts/grub-theme/icons/photobooth.png b/scripts/grub-theme/icons/photobooth.png index 643ad36..4a03bc6 100644 Binary files a/scripts/grub-theme/icons/photobooth.png and b/scripts/grub-theme/icons/photobooth.png differ diff --git a/scripts/grub-theme/icons/photostation.png b/scripts/grub-theme/icons/photostation.png index 3054b9f..2cc0f13 100644 Binary files a/scripts/grub-theme/icons/photostation.png and b/scripts/grub-theme/icons/photostation.png differ diff --git a/scripts/grub-theme/icons/uefi-firmware.png b/scripts/grub-theme/icons/uefi-firmware.png new file mode 100644 index 0000000..680539d Binary files /dev/null and b/scripts/grub-theme/icons/uefi-firmware.png differ diff --git a/scripts/grub-theme/select_c.png b/scripts/grub-theme/select_c.png index 2f53240..dc1d174 100644 Binary files a/scripts/grub-theme/select_c.png and b/scripts/grub-theme/select_c.png differ diff --git a/scripts/grub-theme/select_e.png b/scripts/grub-theme/select_e.png index 0fda206..72518a2 100644 Binary files a/scripts/grub-theme/select_e.png and b/scripts/grub-theme/select_e.png differ diff --git a/scripts/grub-theme/select_n.png b/scripts/grub-theme/select_n.png index 843a1d9..2a66d3d 100644 Binary files a/scripts/grub-theme/select_n.png and b/scripts/grub-theme/select_n.png differ diff --git a/scripts/grub-theme/select_ne.png b/scripts/grub-theme/select_ne.png index fc60fc9..4c88269 100644 Binary files a/scripts/grub-theme/select_ne.png and b/scripts/grub-theme/select_ne.png differ diff --git a/scripts/grub-theme/select_nw.png b/scripts/grub-theme/select_nw.png index fc60fc9..4c88269 100644 Binary files a/scripts/grub-theme/select_nw.png and b/scripts/grub-theme/select_nw.png differ diff --git a/scripts/grub-theme/select_s.png b/scripts/grub-theme/select_s.png index 843a1d9..2a66d3d 100644 Binary files a/scripts/grub-theme/select_s.png and b/scripts/grub-theme/select_s.png differ diff --git a/scripts/grub-theme/select_se.png b/scripts/grub-theme/select_se.png index fc60fc9..4c88269 100644 Binary files a/scripts/grub-theme/select_se.png and b/scripts/grub-theme/select_se.png differ diff --git a/scripts/grub-theme/select_sw.png b/scripts/grub-theme/select_sw.png index fc60fc9..4c88269 100644 Binary files a/scripts/grub-theme/select_sw.png and b/scripts/grub-theme/select_sw.png differ diff --git a/scripts/grub-theme/select_w.png b/scripts/grub-theme/select_w.png index 0fda206..72518a2 100644 Binary files a/scripts/grub-theme/select_w.png and b/scripts/grub-theme/select_w.png differ diff --git a/scripts/grub-theme/theme.txt b/scripts/grub-theme/theme.txt index d3901e8..e0caa94 100644 --- a/scripts/grub-theme/theme.txt +++ b/scripts/grub-theme/theme.txt @@ -1,35 +1,25 @@ # Theme GRUB tactile - Triple Boot Terra title-text: "" -desktop-color: "#1a1a2e" +desktop-image: "background.png" +desktop-color: "#0f1428" message-color: "#ffffff" message-bg-color: "#000000" terminal-font: "DejaVu Sans Bold 24" + boot_menu { - left = 10% - top = 15% - width = 80% - height = 70% - item_font = "DejaVu Sans Bold 36" - item_color = "#cccccc" - selected_item_font = "DejaVu Sans Bold 42" + left = 15% + top = 25% + width = 70% + height = 60% + item_font = "DejaVu Sans Bold 48" + item_color = "#aabbcc" + selected_item_font = "DejaVu Sans Bold 48" selected_item_color = "#ffffff" - item_height = 120 - item_padding = 20 - item_spacing = 30 - item_icon_space = 30 - icon_width = 96 - icon_height = 96 + item_height = 160 + item_padding = 30 + item_spacing = 20 + item_icon_space = 40 + icon_width = 128 + icon_height = 128 selected_item_pixmap_style = "select_*.png" } - -+ label { - left = 0 - top = 90% - width = 100% - height = 30 - text = "Toucher pour selectionner - Appuyer sur Entree pour demarrer" - font = "DejaVu Sans 18" - color = "#888888" - align = "center" -} diff --git a/scripts/test_photobooth_pygame.py b/scripts/test_photobooth_pygame.py new file mode 100644 index 0000000..029938b --- /dev/null +++ b/scripts/test_photobooth_pygame.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 +""" +Prototype photobooth Pygame. +- Bouton -> liveview -> 3 2 1 -> photo x4 -> grille 4 photos +""" +import time +import threading +import io +import os +from pathlib import Path +from datetime import datetime + +import pygame +import gphoto2 as gp +from PIL import Image + +# Config +LARGEUR, HAUTEUR = 1920, 1280 +NB_PHOTOS = 4 +DUREE_COMPTE = 3 +DOSSIER_PHOTOS = Path("/tmp/photobooth_test") +DOSSIER_PHOTOS.mkdir(exist_ok=True) + +# Couleurs +NOIR = (0, 0, 0) +BLANC = (255, 255, 255) +ROSE = (233, 30, 99) +GRIS = (40, 40, 40) + +class Photobooth: + def __init__(self): + pygame.init() + self.screen = pygame.display.set_mode((LARGEUR, HAUTEUR), pygame.FULLSCREEN) + pygame.display.set_caption("Photobooth") + self.clock = pygame.time.Clock() + self.font_grand = pygame.font.SysFont(None, 300) + self.font_moyen = pygame.font.SysFont(None, 100) + self.font_petit = pygame.font.SysFont(None, 60) + + # Camera + self.cam = gp.Camera() + self.cam.init() + + # Etat + self.etat = "accueil" # accueil, preview, compte, capture, galerie + self.frame_courante = None + self.frame_lock = threading.Lock() + self.preview_actif = False + + # Capture via le thread preview (evite le baisse de miroir) + self.capture_demande = False + self.capture_resultat = None + self.capture_done = threading.Event() + self.photos = [] + self.num_photo = 0 + self.chiffre_compte = 0 + + # Thread preview + self.thread_preview = threading.Thread(target=self._loop_preview, daemon=True) + self.thread_preview.start() + + def _loop_preview(self): + while True: + if self.preview_actif: + try: + if self.capture_demande: + try: + chemin_cam = self.cam.capture(gp.GP_CAPTURE_IMAGE) + cf = gp.CameraFile() + self.cam.file_get(chemin_cam.folder, chemin_cam.name, gp.GP_FILE_TYPE_NORMAL, cf) + nom = f"photo_{datetime.now().strftime('%H%M%S_%f')}.jpg" + chemin = str(DOSSIER_PHOTOS / nom) + cf.save(chemin) + self.capture_resultat = chemin + print(f"Photo capturee: {chemin}") + except Exception as e: + print(f"Erreur capture: {e}") + self.capture_resultat = None + self.capture_demande = False + self.capture_done.set() + else: + f = self.cam.capture_preview() + data = bytes(f.get_data_and_size()) + surf = pygame.image.load(io.BytesIO(data)) + surf = pygame.transform.scale(surf, (LARGEUR, HAUTEUR)) + with self.frame_lock: + self.frame_courante = surf + except Exception as e: + print(f"Preview erreur: {e}") + time.sleep(0.3) # Camera peut etre occupee apres capture + else: + time.sleep(0.05) + + def capturer_photo(self): + """Demande une capture au thread preview (miroir reste leve).""" + self.capture_resultat = None + self.capture_done.clear() + self.capture_demande = True + ok = self.capture_done.wait(timeout=10) + if not ok: + print("Timeout capture") + self.capture_demande = False + return self.capture_resultat + + def dessiner_accueil(self): + self.screen.fill(NOIR) + txt = self.font_moyen.render("PHOTOBOOTH", True, ROSE) + self.screen.blit(txt, (LARGEUR//2 - txt.get_width()//2, 300)) + + # Bouton + btn = pygame.Rect(LARGEUR//2 - 300, HAUTEUR//2, 600, 150) + pygame.draw.rect(self.screen, ROSE, btn, border_radius=20) + t = self.font_moyen.render("COMMENCER", True, BLANC) + self.screen.blit(t, (btn.centerx - t.get_width()//2, btn.centery - t.get_height()//2)) + return btn + + def dessiner_preview(self): + with self.frame_lock: + if self.frame_courante: + self.screen.blit(self.frame_courante, (0, 0)) + else: + self.screen.fill(NOIR) + # Info + t = self.font_petit.render(f"Photo {self.num_photo+1}/{NB_PHOTOS} - Souriez!", True, BLANC) + self.screen.blit(t, (20, 20)) + + def dessiner_compte(self): + with self.frame_lock: + if self.frame_courante: + self.screen.blit(self.frame_courante, (0, 0)) + else: + self.screen.fill(NOIR) + # Overlay sombre + overlay = pygame.Surface((LARGEUR, HAUTEUR), pygame.SRCALPHA) + overlay.fill((0, 0, 0, 100)) + self.screen.blit(overlay, (0, 0)) + # Chiffre + txt = self.font_grand.render(str(self.chiffre_compte), True, BLANC) + self.screen.blit(txt, (LARGEUR//2 - txt.get_width()//2, HAUTEUR//2 - txt.get_height()//2)) + + def dessiner_galerie(self): + self.screen.fill(NOIR) + if not self.photos: + t = self.font_petit.render("Aucune photo", True, BLANC) + self.screen.blit(t, (LARGEUR//2 - t.get_width()//2, HAUTEUR//2)) + else: + # 4 photos en grille 2x2 + w, h = LARGEUR//2 - 20, HAUTEUR//2 - 20 + positions = [(10, 10), (LARGEUR//2+10, 10), (10, HAUTEUR//2+10), (LARGEUR//2+10, HAUTEUR//2+10)] + for i, chemin in enumerate(self.photos[:4]): + if i >= len(positions): + break + try: + # PIL pour redimensionner efficacement les grands JPEG Canon + img = Image.open(chemin).convert('RGB') + img = img.resize((w, h), Image.LANCZOS) + surf = pygame.image.fromstring(img.tobytes(), img.size, 'RGB') + self.screen.blit(surf, positions[i]) + except Exception as e: + print(f"Erreur galerie photo {i}: {e}") + + # Bouton recommencer + btn = pygame.Rect(LARGEUR//2 - 200, HAUTEUR - 120, 400, 100) + pygame.draw.rect(self.screen, ROSE, btn, border_radius=15) + t = self.font_petit.render("RECOMMENCER", True, BLANC) + self.screen.blit(t, (btn.centerx - t.get_width()//2, btn.centery - t.get_height()//2)) + return btn + + def lancer_sequence(self): + """Lance la séquence 4 photos dans un thread.""" + def sequence(): + self.photos = [] + for i in range(NB_PHOTOS): + self.num_photo = i + # Preview + self.preview_actif = True + self.etat = "preview" + time.sleep(1.5) + + # Compte a rebours + self.etat = "compte" + for c in range(DUREE_COMPTE, 0, -1): + self.chiffre_compte = c + time.sleep(1) + + # Flash + capture (miroir reste leve, liveview continue) + self.etat = "capture" + chemin = self.capturer_photo() + if chemin: + self.photos.append(chemin) + print(f"Photo {i+1} : {chemin}") + time.sleep(0.3) + + # Galerie + self.preview_actif = False + self.etat = "galerie" + + threading.Thread(target=sequence, daemon=True).start() + + def run(self): + btn_accueil = None + btn_galerie = None + running = True + + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: + running = False + if event.type == pygame.MOUSEBUTTONDOWN: + pos = pygame.mouse.get_pos() + if self.etat == "accueil" and btn_accueil and btn_accueil.collidepoint(pos): + self.lancer_sequence() + if self.etat == "galerie" and btn_galerie and btn_galerie.collidepoint(pos): + self.etat = "accueil" + self.photos = [] + + if self.etat == "accueil": + btn_accueil = self.dessiner_accueil() + btn_galerie = None + elif self.etat == "preview": + self.dessiner_preview() + elif self.etat == "compte": + self.dessiner_compte() + elif self.etat == "capture": + self.dessiner_compte() # Reste sur le liveview pendant la capture + elif self.etat == "galerie": + btn_galerie = self.dessiner_galerie() + btn_accueil = None + + pygame.display.flip() + self.clock.tick(30) + + self.preview_actif = False + self.cam.exit() + pygame.quit() + + +if __name__ == "__main__": + app = Photobooth() + app.run()