# Annexe : Application - Gestionnaire des tâches (TaskMgr)

## Rôle et utilité

L'application `apps/taskmgr/app.py` permet à l'utilisateur de surveiller les processus en cours d'exécution sur la machine et de "tuer" (fermer de force) les applications qui ne répondent plus. C'est l'équivalent du classique "Ctrl+Alt+Suppr" sous Windows.

## Implémentation technique

- Analyse du système (Lecture de `/proc`) : Plutôt que d'utiliser des commandes externes lourdes comme `ps` ou `top`, le script lit directement les données brutes dans le répertoire `/proc/` de Linux. Chaque dossier numéroté dans `/proc/` correspond à un processus. Le script extrait le nom et la consommation mémoire de chaque processus à la vitesse de l'éclair.
- Boucle de rafraîchissement : L'interface graphique contient un tableau (Treeview) qui est vidé et repeuplé toutes les 2 secondes grâce à un appel asynchrone (`window.after(2000, refresh)`).
- Terminaison de processus : Lorsqu'un utilisateur sélectionne une ligne et clique sur "Terminer", l'application extrait le PID (Identifiant de Processus) et exécute la commande native `kill -9 [PID]` pour détruire immédiatement l'application bloquée.

## Pistes de modification

- Graphiques dynamiques : Le gestionnaire de tâches actuel affiche une simple liste textuelle. On pourrait y intégrer le composant de dessin (`Canvas`) utilisé dans le `desktop.py` pour afficher l'historique de consommation CPU/RAM de l'ordinateur sur les 60 dernières secondes, sous forme de courbes.
- Filtrage intelligent : Actuellement, le gestionnaire affiche tous les processus du système Linux, y compris les démons internes qui peuvent intimider l'utilisateur. Ajouter une case à cocher "Ne montrer que mes applications" pour masquer tout ce qui n'appartient pas au processus de l'utilisateur principal serait un vrai plus ergonomique.

## Code Source

    import tkinter as tk
    from tkinter import ttk, messagebox
    import os
    import signal

    def start(window, app_manager=None, **kwargs):
        top_frame = tk.Frame(window, bg="lightgray")
        top_frame.pack(side="top", fill="x")

        btn_kill = tk.Button(top_frame, text=" Fin de tâche", image=(app_manager.desktop.icons.get("btn_kill") if app_manager else None), compound="left", bg="red", fg="white", font=("Arial", 9, "bold"))
        btn_kill.pack(side="left", padx=5, pady=5)

        btn_refresh = tk.Button(top_frame, text=" Actualiser", image=(app_manager.desktop.icons.get("btn_refresh") if app_manager else None), compound="left", font=("Arial", 9))
        btn_refresh.pack(side="left", padx=5, pady=5)

        def show_help():
            msg = (
                "Aide du Gestionnaire des Tâches\n\n"
                "• Cette liste affiche les processus en cours sur le système.\n"
                "• Le bouton rouge 'Fin de tâche' envoie un signal d'arrêt immédiat (kill) au programme.\n\n"
                "⚠️ Attention : Tuer un processus vital (comme systemd, xorg, ou dbus) causera "
                "le plantage immédiat de l'interface ou du système."
            )
            messagebox.showinfo("Aide Gestionnaire", msg)

        btn_help = tk.Button(top_frame, text=" Aide", image=(app_manager.desktop.icons.get("btn_help") if app_manager else None), compound="left", command=show_help, relief="flat", bg="lightblue")
        btn_help.pack(side="right", padx=5, pady=2)

        tree_frame = tk.Frame(window)
        tree_frame.pack(fill="both", expand=True)

        scrollbar = tk.Scrollbar(tree_frame)
        scrollbar.pack(side="right", fill="y")

        tree = ttk.Treeview(tree_frame, columns=("PID", "Name", "State"), show="headings", yscrollcommand=scrollbar.set)
        tree.heading("PID", text="PID")
        tree.heading("Name", text="Nom du processus")
        tree.heading("State", text="État")
        tree.column("PID", width=60, anchor="center")
        tree.column("Name", width=250)
        tree.column("State", width=60, anchor="center")
        tree.pack(side="left", fill="both", expand=True)
        scrollbar.config(command=tree.yview)

        def refresh():
            for i in tree.get_children():
                tree.delete(i)

            try:
                pids = [pid for pid in os.listdir('/proc') if pid.isdigit()]
                pids.sort(key=int)
                for pid_str in pids:
                    try:
                        with open(f"/proc/{pid_str}/stat", "r") as f:
                            stat = f.read().split()
                            # stat[1] is the process name, usually in parentheses like (systemd)
                            name = stat[1].strip("()")
                            state = stat[2]
                            tree.insert("", "end", values=(pid_str, name, state))
                    except Exception:
                        continue
            except Exception as e:
                messagebox.showerror("Erreur", f"Impossible de lire /proc:\n{e}")

        def kill_proc():
            selected = tree.selection()
            if not selected:
                return
            item = tree.item(selected[0])
            pid = int(item['values'][0])
            name = item['values'][1]

            # Prevent killing vital system processes lightly
            if pid in (1, 2) or "xorg" in str(name).lower() or "python" in str(name).lower():
                if not messagebox.askyesno("Attention Système", f"Tuer '{name}' (PID: {pid}) pourrait crasher le système ! Continuer ?"):
                    return

            if messagebox.askyesno("Confirmer", f"Voulez-vous forcer l'arrêt de '{name}' (PID: {pid}) ?"):
                try:
                    os.kill(pid, signal.SIGKILL)
                    window.after(500, refresh)
                except PermissionError:
                    messagebox.showerror("Accès refusé", f"Vous n'avez pas les droits pour tuer ce processus (PID: {pid}).")
                except Exception as e:
                    messagebox.showerror("Erreur", f"Impossible de terminer le processus:\n{e}")

        btn_refresh.config(command=refresh)
        btn_kill.config(command=kill_proc)

        refresh()