portfolio_leptos/data_src/posts/kde_contribution.md
2024-02-18 18:09:21 +01:00

13 KiB

image_path slug title date description project_link draft tags
https://community.kde.org/images.community/thumb/a/af/Mascot_konqi-base-plasma.png/250px-Mascot_konqi-base-plasma.png kde_first_contrib Mes premières contributions à KDE 2024-01-12T00:00:00Z Ma première configuration et ma première contribution à l'environnement mobile (Plasma-Mobile). none true
kde
linux

Rapide rappel à propos de KDE

KDE est un projet qui a pour but de développer des logiciels open-source. Le projet comporte plusieurs éléments les plus connus.

  • KF5 ou KF6 est un ensemble de librairies utilisées pour le développement de l'ensemble des logiciels de KDE. Il est basé sur la librairie QT.

Exemple : Kirigami que l'on peut voir comme un "flutter".

Note:

KF5 => Basé sur QT5

KF6 => Basé sur QT6

  • Plasma Shell est l'environnement de bureau développé pour Linux.

Ma configuration

Pour développer sur KDE, KDE fournis un utilitaire assez puissant kdesrc-build. Il permet de configurer l'IDE, compiler, exécuter et gérer les dépendances avec l'aide d'une seule ligne de commande.

Pour le configurer, il suffit de suivre la procédure sur ce lien.

Mes premières difficultés

Il faut savoir que quand j'ai commencé à vouloir contribuer au projet KDE. Le projet était en train de migrer de QT5 à QT6. Donc, les configurations nécessaires pour compiler correctement la version 6 était nettement plus compliqué que maintenant. Il n'y avait aucune documentation pour migrer la configuration fournie de base, il fallait fouiller sur Github ou sur les blogs pour réussir à avoir la configuration. Aujourd'hui, il suffit juste de suivre le lien ci-dessus et on peut directement travailler sur KDE 6.

Pour le développement sur KDE Plasma 6, j'ai dû faire quelques réajustements sur le fichier ~/.config/kdesrc-buildrc.

J'ai changé la branche par défaut avec l'option branch-group kf6-qt6. Elle permet de cloner les repositories correctement et d'ajouter l'option cmake -DBUILD_WITH_QT6=on. J'ai aussi changé les fichiers de configuration utilisées pour obtenir le résultat ci-dessous.

global
    branch-group kf6-qt6
    [...]
end global

include ~/kde/src/kdesrc-build/data/build-include/kf6-common-options.ksb
include ~/kde/src/kdesrc-build/data/build-include/kf6-frameworks.ksb
include ~/kde/src/kdesrc-build/data/build-include/kf6-workspace.ksb
include ~/kde/src/kdesrc-build/data/build-include/kf6-applications.ksb
include ~/kde/src/kdesrc-build/data/build-include/kf6-kdepim.ksb

Configurations utiles

Désactiver l'arrêt de la compilation lors d'une erreur de compilation.

En général, kdesrc-build s'adapte et retire tous les projets qui dépendent de la dépendance qui n'a pas réussi à compiler. Le projet était encore en cours de développement donc les erreurs de compilations étaient très régulières. J'ai tout simplement désactivé avec l'option stop-on-failure false.

Activer le support des LSP

C'est très pratique si on utilise VSCode ou même n'importe quel outil qui supporte les LSP.

Il suffit d'activer les options:

  • compile-commands-linking => true
  • compile-commands-export => true

Générer les projets VSCode

Il va générer automatiquement les workspaces VSCode avec les extensions recommander et le paramètrage par défaut. Ça permet de travailler dans de très bonne condition avec VSCode.

Il suffit de l'activer avec generate-vscode-project-config true

Options utiles pour la commande kdesrc-build

  • -D : Pour ne pas inclure les dépendances dans la compilation

Exemple: kdesrc-build -D kate

  • -S : Pour ne pas mettre à jour les sources

Exemple: kdesrc-build -S kate

Ma contribution

J'ai contribué sur le support de la lampe torche pour l'environnement Plasma Mobile. La lampe torche fonctionnait uniquement sur les pinephones en utilisant le fichier en dure /sys/devices/platform/led-controller/leds/white:flash/brightness (Source). J'ai réécris le module pour que ça fonctionne de manière générale sur tous les téléphones.

Rappel sur le fonctionnement du noyau Linux autour des leds

Pour comprendre comment la fonctionnalité, un petit rappel s'impose autour du fonctionnement des leds avec le noyau Linux. Dès qu'une led est détecté par le noyau, il va mettre à disposition plusieurs fichiers. On les trouvent dans le dossier correspondant au driver et aussi à l'emplacement physique sur la carte mère ou sur le SOC. Mais on peut aussi les trouver plus facilement dans le répertoire /sys/class/leds/.

sys => Fichiers système class => Trier par class. leds => La class leds. On peut trouver aussi par appareil bluetooth, puce tpm, ...

On disposent de plusieurs fichiers importants :

  • brightness : Permet de changer ou de récupérer l'intensité de la led actuelle.
  • max_brightness : Permet de connaitre l'intensité max de la led.
  • color : Ce fichier n'existe pas tout le temps mais permet d'avoir la couleur de la led.
  • Le reste des fichiers ne sont pas important pour nous.

Explication du fonctionnement de mon code petit à petit

Tout d'abord, il faut savoir que j'utilise la libraire Udev. Il est prévu que je migre vers la librairie Solid une fois le support des leds ajoutées. Udev pour faire simple, c'est une librairie qui permet d'interagir avec les appareils assez directement sans intermédiaire. La librairie permet également de lister, filtrer, détecter si un appareil est connecté/déconnecté, ....

Pour commencer, je dois d'abord instancier Udev avec pour l'utiliser par la suite.

struct udev *udev = udev_new();

Ensuite, je liste tous les périphériques qui m'intéressent donc ceux appartenant à la class leds. Pour ça, il me suffit de créer un énumerateur sur lequel on appliquera tous les filtres.

On peut voir une class comme une catégorie d'appareils.

struct udev_enumerate *enumerate = udev_enumerate_new(udev);

J'applique le filtre pour lister uniquement ceux qui appartiennent à la class leds.

#define TORCH_SUBSYSTEM "leds"
...
udev_enumerate_add_match_subsystem(enumerate, TORCH_SUBSYSTEM);

J'applique le filtre pour lister uniquement ceux qui ont dans le nom flash ou torch.

udev_enumerate_add_match_sysname(enumerate, "*:torch");
udev_enumerate_add_match_sysname(enumerate, "*:flash");

Puis je lance le scan avec mes filtres.

udev_enumerate_scan_devices(enumerate);

Au début, il faut savoir que je prenais le premier appareil avec le code ci-dessous mais certain téléphone ont une torche de couleur jaune et blanche. Certain utilisateur malheureusement ne pouvait pas utiliser la fonctionnalité.

struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
struct udev_list_entry *entry = udev_list_entry_get_next(devices);

Source

Donc, j'ai changé le code avec le code ci-dessous qui permet d'itérer sur tous les appareils.

struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
struct udev_list_entry *entry = nullptr;

struct udev_device *device = nullptr;

udev_list_entry_foreach(entry, devices)
{
    [...]
}

Pour chaque appareil, j'ai récuperé le répertoire ou se situe l'appareil avec le code ci-dessous qui me renvoie par exemple: /sys/devices/platform/led-controller/leds/white:flash.

const char *path = udev_list_entry_get_name(entry);

J'instancie une instance udev_device pour interagir avec lui depuis le répertoire.

struct udev_device *device = udev_device_new_from_syspath(udev, path);

Et à partir de là, il me suffit de lire les fichiers dont j'ai besoin.

const char *maxBrightness = udev_device_get_sysattr_value(device, "[NOM DU FICHIER]");

Je lis d'abord le fichier 'color' et je regarde si la couleur est blanche sinon je continue. Si je ne trouve pas de led de couleur blanche, je prends la dernière valeur récupérée.

Une fois pour chaque appareil trouvé, il me reste plus qu'à récupérer les infos dont j'ai besoin. Donc, la luminosité maximale max_brightness et la luminosité actuelle brightness pour correctement initialiser l'interface de Plasma Mobile.

Quand je souhaite changer la valeur, je dois d'abord convertir la valeur entière en chaîne de charactère.

const_cast<char *>(m_torchEnabled ? "0" : m_maxBrightness)

Ensuite, je change la luminosité

udev_device_set_sysattr_value(m_device, "brightness", nouvelle_valeur_en_char_*);

Fichier final

Source

/*
 * SPDX-FileCopyrightText: 2020 Han Young <hanyoung@protonmail.com>
 * SPDX-FileCopyrightText: 2022 by Devin Lin <devin@kde.org>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "flashlightutil.h"

#include <cstring>
#include <fcntl.h>
#include <libudev.h>
#include <unistd.h>

#include <QDebug>
#include <QFileInfo>

#define TORCH_SUBSYSTEM "leds"

FlashlightUtil::FlashlightUtil(QObject *parent)
    : QObject{parent}
    , m_device{nullptr}
    , m_isAvailable{false}
{
    findTorchDevice();
}

FlashlightUtil::~FlashlightUtil()
{
    if (m_device != nullptr) {
        udev_device_unref(m_device);
    }
}

void FlashlightUtil::toggleTorch()
{
    if (!isAvailable()) {
        qWarning() << "Flashlight not available";
        return;
    }

    int ret = udev_device_set_sysattr_value(m_device, "brightness", const_cast<char *>(m_torchEnabled ? "0" : m_maxBrightness));
    if (ret < 0) {
        qWarning() << "Flashlight can't be toggled";
        return;
    }

    m_torchEnabled = !m_torchEnabled;
    Q_EMIT torchChanged(m_torchEnabled);
}

bool FlashlightUtil::torchEnabled() const
{
    return m_torchEnabled;
}

bool FlashlightUtil::isAvailable() const
{
    return m_isAvailable;
}

void FlashlightUtil::findTorchDevice()
{
    if (m_device != nullptr) {
        udev_device_unref(m_device);
    }
    m_device = nullptr;
    m_isAvailable = false;

    struct udev *udev = udev_new();
    struct udev_enumerate *enumerate = udev_enumerate_new(udev);

    udev_enumerate_add_match_subsystem(enumerate, TORCH_SUBSYSTEM);
    udev_enumerate_add_match_sysname(enumerate, "*:torch");
    udev_enumerate_add_match_sysname(enumerate, "*:flash");
    udev_enumerate_scan_devices(enumerate);

    struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
    struct udev_list_entry *entry = nullptr;

    struct udev_device *device = nullptr;

    udev_list_entry_foreach(entry, devices)
    {
        const char *path = udev_list_entry_get_name(entry);

        if (path == nullptr) {
            continue;
        }

        if (device != nullptr) {
            udev_device_unref(device); // Use to free memory from previous loop iteration
        }

        device = udev_device_new_from_syspath(udev, path);

        if (device == nullptr) {
            continue;
        }

        qInfo() << "Found flashlight device : " << path;

        const char *color = udev_device_get_sysattr_value(device, "color");

        if (color == nullptr) {
            continue;
        }

        qInfo() << "Flash color : " << color;

        if (std::strcmp(color, "white") == 0) {
            break;
        }
    }

    if (device == nullptr) {
        qWarning() << "No flashlight device found";
        return;
    }

    const char *maxBrightness = udev_device_get_sysattr_value(device, "max_brightness");

    if (maxBrightness == nullptr) {
        qWarning() << "Failed to read max_brightness from udev device";
        return;
    }

    qInfo() << "Flash maxBrightness : " << maxBrightness;

    const char *brightness = udev_device_get_sysattr_value(device, "brightness");

    if (brightness == nullptr) {
        qWarning() << "Failed to read brightness from udev device";
        return;
    }

    qInfo() << "Flash brightness : " << brightness;

    m_maxBrightness = maxBrightness;
    m_device = device;
    m_isAvailable = true;
    m_torchEnabled = std::strcmp(brightness, "0") != 0;

    udev_enumerate_unref(enumerate);
    udev_unref(udev);
}

Info utile pour débugger KDE Plama Mobile depuis Postmarket OS

Le fichier de log se situe dans ~/.local/state/tinydm.log