Sur cette page

Qt pour Linux embarqué

Plugins de plateforme pour les appareils Linux embarqués

Sur les systèmes Linux embarqués, il existe plusieurs plugins de plateforme que vous pouvez utiliser : EGLFS, VkKhrDisplay, LinuxFB ou Wayland. La disponibilité de ces plugins dépend de la façon dont Qt est configuré. Parmi ces plugins, Wayland nécessite la présence d'un compositeur et fournit un système de fenêtrage complet supportant plusieurs fenêtres, de manière similaire à X11 ou Windows. Les autres fonctionnent sans système de fenêtrage, ce qui signifie que l'application Qt contrôle entièrement le rendu et la sortie. Ils prennent généralement en charge une "fenêtre" Qt plein écran par écran.

EGLFS est le plugin par défaut sur de nombreux tableaux. S'il ne convient pas, utilisez la variable d'environnement QT_QPA_PLATFORM pour demander un autre plugin. Alternativement, pour des tests rapides, utilisez l'argument de ligne de commande -platform avec la même syntaxe.

Note : Depuis Qt 5.0, Qt n'a plus son propre système de fenêtres (QWS). Pour les cas d'utilisation mono-processus, l'abstraction de plateforme Qt est une solution supérieure ; les cas d'utilisation multi-processus sont pris en charge par Wayland.

Voir Configurer un dispositif Linux embarqué pour une vue d'ensemble de la configuration de Qt pour la compilation croisée à l'aide d'une chaîne d'outils Linux embarquée.

EGLFS

EGL est une interface entre OpenGL et le système de fenêtrage natif. Qt peut utiliser EGL pour la gestion du contexte et de la surface, mais l'API ne contient aucune spécificité de plate-forme. La création d'une fenêtre native, qui ne sera pas nécessairement une fenêtre réelle sur l'écran, doit toujours être effectuée par des moyens spécifiques à la plateforme. C'est pourquoi nous avons besoin du code d'adaptation spécifique à la carte ou au GPU. Généralement, ces adaptations sont fournies sous la forme de :

  • crochets EGLFS - un seul fichier source compilé dans le plugin de la plateforme
  • Intégration du dispositif EGL - plugins chargés dynamiquement

EGLFS est un plugin de plateforme pour faire tourner des applications QtGL au dessus d'EGL et OpenGL ES 2.0, sans système de fenêtrage comme X11 ou Wayland. C'est le plugin recommandé pour les appareils modernes Embedded Linux qui incluent un GPU.

En plus de Qt Quick et des applications OpenGL natives, EGLFS supporte également les fenêtres à rendu logiciel, comme QWidget. Pour QWidget, le contenu des widgets est rendu par le CPU en images, qui sont ensuite téléchargées en textures et composées par le plugin.

EGLFS force la première fenêtre de niveau supérieur - soit une QWidget ou une QQuickView - à devenir plein écran. Cette fenêtre est également choisie pour être la fenêtre widget racine dans laquelle tous les autres widgets de niveau supérieur sont composés. Par exemple, les boîtes de dialogue, les menus déroulants ou les boîtes combinées. Ce comportement est nécessaire parce qu'avec EGLFS il y a toujours exactement une fenêtre native et une surface de fenêtre EGL ; elles appartiennent au widget ou à la fenêtre qui est créée en premier. Cette approche fonctionne bien lorsqu'il y a une fenêtre principale qui existe pour toute la durée de vie de l'application et que tous les autres widgets ne sont pas de premier niveau ou sont créés après, une fois que la fenêtre principale est affichée.

Il existe d'autres restrictions pour les fenêtres basées sur OpenGL. EGLFS supporte une seule fenêtre GL plein écran (à partir de Qt 5.3), comme les fenêtres basées sur OpenGL QWindow, QQuickView, ou QOpenGLWidget. Ouvrir des fenêtres OpenGL supplémentaires ou mélanger de telles fenêtres avec du contenu basé sur QWidget n'est pas supporté ; Qt termine l'application avec un message d'erreur.

En outre, les API conçues pour les plates-formes de bureau ou les environnements avec un système de fenêtrage, comme le glisser-déposer, ne sont pas prises en charge par l'EGLFS.

Variables d'environnement utilisées par l'EGLFS

Si nécessaire, eglfs peut être configuré en utilisant les variables d'environnement suivantes :

Variable d'environnementVariable d'environnement Description
QT_QPA_EGLFS_INTEGRATIONOutre les crochets compilés, il est également possible d'utiliser des plugins chargés dynamiquement pour fournir une adaptation spécifique à un appareil ou à un fournisseur. Cette variable d'environnement impose un plugin spécifique. Par exemple, si elle vaut eglfs_kms, le backend KMS/DRM est utilisé. Il s'agit d'une option uniquement lorsqu'aucun crochet statique ou compilé n'a été spécifié dans les spécifications techniques de l'appareil. En pratique, les crochets compilés traditionnels sont rarement utilisés, presque tous les backends sont maintenant migrés vers des plugins. Les spécifications techniques des périphériques contiennent toujours une entrée pertinente, bien qu'optionnelle, EGLFS_DEVICE_INTEGRATION: le nom du backend préféré pour ce périphérique particulier. Évitez de définir cette variable d'environnement s'il existe plus d'un plugin sur le système cible. Dans un environnement de bureau, les backends KMS ou X11 sont prioritaires, en fonction de la présence de la variable d'environnement DISPLAY.

Note : Sur certaines cartes, une valeur spéciale de none est utilisée à la place d'un plugin réel. Cela indique qu'aucune intégration spéciale n'est nécessaire pour utiliser EGL avec le framebuffer ; aucun plugin ne doit être chargé.

QT_QPA_EGLFS_PHYSICAL_WIDTH et QT_QPA_EGLFS_PHYSICAL_HEIGHTSpécifie la largeur et la hauteur de l'écran physique en millimètres. Notez que depuis Qt 6, la taille physique de l'écran n'est plus utilisée pour déterminer les ppp logiques.
QT_QPA_EGLFS_ROTATIONSpécifie la rotation appliquée au contenu rendu par logiciel dans les applications basées sur QWidget. Les valeurs prises en charge sont 180, 90 et -90. Cette variable ne s'applique pas aux fenêtres basées sur OpenGL, y compris Qt Quick. Les applications Qt Quick peuvent appliquer des transformations dans leur scène QML à la place. Le curseur standard de la souris eglfs prend toujours en compte la valeur, avec une image de pointeur positionnée et tournée de manière appropriée, quel que soit le type d'application. Cependant, des implémentations spéciales du curseur, telles que le curseur matériel du backend KMS/DRM, peuvent ne pas prendre en charge la rotation. Ce paramètre n'a aucun effet sur quoi que ce soit d'autre, y compris sur la saisie tactile. Les backends d'entrée tactile evdevtouch et libinput ont chacun leur propre mécanisme de configuration de la rotation. Voir Inputs on an Embedded Linux Device pour plus d'informations sur la configuration de l'entrée tactile.
QT_QPA_EGLFS_FORCEVSYNCLorsqu'elle est définie, eglfs demande FBIO_WAITFORVSYNC sur le périphérique framebuffer après chaque appel à eglSwapBuffers(). Cette variable ne concerne que les backends qui s'appuient sur l'ancien sous-système Linux fbdev. Normalement, avec un intervalle de permutation par défaut de 1, Qt XML suppose que l'appel à eglSwapBuffers() prend en charge vsync ; si ce n'est pas le cas (par exemple, en raison de bogues dans le pilote), essayez de définir QT_QPA_EGLFS_FORCEVSYNC à une valeur non nulle.
QT_QPA_EGLFS_FORCE888Lorsqu'elle est définie, la taille des canaux de couleur rouge, vert et bleu est ignorée lorsque eglfs crée un nouveau contexte, une fenêtre ou une surface hors écran. Au lieu de cela, le plugin demande une configuration avec 8 bits par canal. Cela peut être utile sur les appareils où les configurations avec moins de 32 ou 24 bits par pixel (par exemple, 5-6-5 ou 4-4-4) sont choisies par défaut tout en sachant qu'elles ne sont pas idéales, par exemple, en raison d'effets de bande. Au lieu de modifier le code de l'application, cette variable fournit un raccourci pour forcer les configurations 24 ou 32 bpp.

En outre, les variables suivantes, moins couramment utilisées, sont disponibles :

Variable d'environnementVariable d'environnement Description
QT_QPA_EGLFS_FBRemplace le périphérique framebuffer. La valeur par défaut est /dev/fb0. Sur la plupart des plates-formes embarquées, cette variable n'est pas très pertinente car le framebuffer n'est utilisé que pour interroger des paramètres tels que les dimensions de l'affichage. Cependant, sur certains appareils, cette variable permet de spécifier l'affichage à utiliser dans le cas de configurations d'affichage multiples, à l'instar du paramètre fb dans LinuxFB.
QT_QPA_EGLFS_WIDTH et QT_QPA_EGLFS_HEIGHTContient la largeur et la hauteur de l'écran en pixels. Bien que eglfs tente de déterminer les dimensions à partir du périphérique framebuffer /dev/fb0, cela ne fonctionne pas toujours. Il peut être nécessaire de spécifier manuellement les dimensions.
QT_QPA_EGLFS_DEPTHRemplace la profondeur de couleur de l'écran. Sur les plateformes où le périphérique framebuffer /dev/fb0 n'est pas disponible ou si la requête n'aboutit pas, une valeur par défaut de 32 est utilisée. Utilisez cette variable pour remplacer ces valeurs par défaut.

Note : Cette variable n'affecte que la valeur de profondeur de couleur rapportée par QScreen. Elle n'a aucun lien avec les configurations EGL et la profondeur de couleur utilisée pour le rendu OpenGL.

QT_QPA_EGLFS_SWAPINTERVALPar défaut, un intervalle de permutation de 1 est demandé. Cette variable permet de se synchroniser avec le rafraîchissement vertical de l'écran. Utilisez cette variable pour remplacer la valeur de l'intervalle de permutation. Par exemple, passer 0 désactive le blocage sur la permutation, ce qui permet de fonctionner aussi vite que possible sans aucune synchronisation.
QT_QPA_EGLFS_DEBUGLorsqu'elle est définie, certaines informations de débogage sont imprimées sur la sortie de débogage. Par exemple, l'entrée QSurfaceFormat et les propriétés de la configuration EGL choisie sont affichées lors de la création d'un nouveau contexte. Lorsqu'elle est utilisée avec la variable Qt Quick's QSG_INFO, vous pouvez obtenir des informations utiles pour résoudre les problèmes liés à la configuration EGL.

Journalisation

En plus de QT_QPA_EGLFS_DEBUG, eglfs supporte également le système moderne de journalisation par catégories de Qt. Les catégories de journalisation suivantes sont disponibles :

  • qt.qpa.egldeviceintegration - Active la journalisation pour les backends chargés dynamiquement. Utilisez cette catégorie pour vérifier quel backend est utilisé.
  • qt.qpa.input - Active la sortie de débogage des gestionnaires d'entrée evdev et libinput. Utilisez cette catégorie pour vérifier si un périphérique d'entrée donné a été reconnu et ouvert.
  • qt.qpa.eglfs.kms - Active la journalisation en mode verbeux dans le backend KMS/DRM.

Après avoir exécuté configure, assurez-vous d'inspecter sa sortie. C'est la manière la plus simple et la plus rapide d'identifier si vous avez activé le backend EGLFS nécessaire, libudev, ou libinput. En bref, s'il y a un "non" indésirable dans la sortie de votre configure, exécutez :

./configure -v

pour activer la sortie verbose, afin que vous puissiez voir les invocations du compilateur et de l'éditeur de liens pour chaque test de configure.

Note : Si vous rencontrez des erreurs à propos d'en-têtes ou de bibliothèques manquantes, ou des échecs apparemment cryptiques de l'éditeur de liens, c'est souvent le signe d'un sysroot incomplet ou cassé, et ce n'est pas lié à Qt.

Par exemple, lorsque vous ciblez le Raspberry Pi avec les pilotes graphiques propriétaires Broadcom, la sortie devrait contenir quelque chose comme ceci :

QPA backends:
EGLFS ................................ yes
EGLFS details:
  EGLFS i.Mx6 ........................ no
  EGLFS i.Mx6 Wayland ................ no
  EGLFS EGLDevice .................... no
  EGLFS GBM .......................... no
  EGLFS Mali ......................... no
  EGLFS Raspberry Pi ................. yes
  EGL on X11 ......................... no

Si ce n'est pas le cas, il n'est pas conseillé de poursuivre la compilation car les graphiques accélérés ne seront pas fonctionnels sans le backend spécifique au Raspberry Pi, même si le reste de Qt se compile correctement.

VkKhrDisplay

Alors qu'EGLFS ne supporte qu'OpenGL (ES), VkKhrDisplay est un plugin de plateforme expérimental qui supporte le rendu avec l'API Vulkan. Pour énumérer les affichages et configurer le rendu, il s'appuie sur la famille d'extensions VK_KHR_display. Notez qu'il n'est pas certain qu'une implémentation de Vulkan au sein d'une pile graphique prenne en charge cette fonctionnalité. Actuellement, ce plugin de plateforme a été vérifié et testé avec Mesa et V3DV sur un Raspberry Pi 4.

Ce plugin de plateforme ne prend pas en charge OpenGL ou tout autre rendu logiciel. Toute tentative d'affichage d'une interface utilisateur basée sur QWidget échouera donc. Le seul type de surface pris en charge par QWindow est QSurface::VulkanSurface. Pour les applications Qt Quick, cela implique que le rendu basé sur Vulkan doit être appliqué soit en définissant QSG_RHI_BACKEND=vulkan dans l'environnement, soit en appelant QQuickWindow::setGraphicsApi(QSGRendererInterface::Vulkan) ; tôt avant de créer une QQuickWindow ou une QQuickView.

Pour utiliser ce plugin de plateforme, exécutez l'application avec -platform vkkhrdisplay ou définissez QT_QPA_PLATFORM à vkkhrdisplay. Le plugin est construit uniquement lorsque Qt est configuré avec le support Vulkan.

La configuration avancée de type EGLFS (par exemple le fichier de configuration JSON) ou la sortie sur plusieurs écrans à partir de la même application ne sont pas actuellement implémentées. Les applications peuvent cependant choisir l'écran à utiliser via des variables d'environnement.

Pour déterminer les valeurs d'index, vérifiez les journaux imprimés sur la sortie de débogage par le plugin. Actuellement, ces journaux ne sont pas classés (ils sont imprimés via qDebug) car il est essentiel de les inspecter dans la plupart des cas, afin de s'assurer que le plugin choisit l'affichage et le mode appropriés.

  • QT_VK_DISPLAY_INDEX - Lorsqu'il est défini, l'affichage avec l'index donné est utilisé.
  • QT_VK_MODE_INDEX - Lorsqu'il est défini, le mode avec l'index donné est utilisé.
  • QT_VK_PHYSICAL_DEVICE_INDEX - Lorsqu'il est défini, le périphérique physique Vulkan avec l'index donné est utilisé. Cela ne sera pas pertinent dans la plupart des cas sur embedded. Notez que cette variable est également utilisée par le reste de la pile graphique de Qt.

La gestion des entrées (clavier, souris, tactile) est similaire à EGLFS, supportant evdev, libinput, et tslib. Cependant, aucun rendu du curseur de la souris n'est implémenté. En effet, il n'y a pas de concept de curseur matériel dans cet environnement, et le rendu d'un curseur avec Vulkan dans le plugin de plateforme, de manière similaire à ce que fait EGLFS avec OpenGL, est problématique pour de multiples raisons. Par conséquent, ce plugin de plateforme n'est pas bien adapté à la saisie à la souris pour le moment.

Les variables d'environnement correspondantes sont :

  • QT_QPA_DISABLE_INPUT - Désactive l'entrée clavier/souris/touche.
  • QT_QPA_NO_LIBINPUT - Préfère les gestionnaires d'entrée basés sur evdev même si libinput est disponible.
  • QT_QPA_TSLIB - Demande l'utilisation de l'ancienne bibliothèque tslib.

LinuxFB

Ce plugin écrit directement dans le framebuffer via le sous-système fbdev de Linux. Seul le contenu rendu par logiciel est pris en charge. Notez que sur certaines configurations, les performances d'affichage devraient être limitées. Pour utiliser les applications Qt Quick avec ce plugin de plateforme, le backend software scenegraph doit être utilisé, soit en définissant QT_QUICK_BACKEND=software dans l'environnement, soit en appelant setGraphicsApi() avec QSGRendererInterface::Software. Les applications QWidget, ou QWindow avec un type de surface QSurface::RasterSurface, sont prises en charge, mais cela n'inclut pas les widgets spéciaux tels que QOpenGLWidget.

Comme fbdev est en train d'être déprécié dans le noyau Linux, la prise en charge des tampons muets DRM est également disponible. Pour l'utiliser, définissez la variable d'environnement QT_QPA_FB_DRM avec une valeur non nulle. Lorsqu'elle est définie, si les tampons muets sont pris en charge par votre système, les périphériques framebuffer hérités tels que /dev/fb0 ne seront pas accédés. Au lieu de cela, le rendu est configuré via les API DRM, de manière similaire au backend eglfs_kms d'EGLFS. La sortie est doublement tamponnée et les pages sont retournées, ce qui permet une bonne synchronisation pour le contenu rendu par logiciel.

Note : Lorsque des tampons muets sont utilisés, aucune des options décrites ci-dessous n'est applicable puisque les propriétés telles que la taille physique et logique de l'écran sont toutes interrogées automatiquement.

Spécification de paramètres supplémentaires

Le plugin linuxfb vous permet de spécifier des paramètres supplémentaires via la variable d'environnement QT_QPA_PLATFORM ou l'option de ligne de commande -platform. Par exemple, QT_QPA_PLATFORM=linuxfb:fb=/dev/fb1 spécifie que le périphérique framebuffer /dev/fb1 doit être utilisé au lieu du périphérique par défaut fb0. Pour spécifier plusieurs paramètres, séparez les m par deux points ( :).

ParamètresDescription
fb=/dev/fbNSpécifie les périphériques de mémoire tampon. Sur les configurations à écrans multiples, ce paramètre vous permet d'exécuter l'application sur différents écrans. Actuellement, il n'existe aucun moyen d'utiliser plusieurs framebuffers à partir d'une seule application Qt.
size=<width>x<height>Spécifie la taille de l'écran en pixels. Le plugin essaie de demander les dimensions de l'écran, à la fois physiques et logiques, au framebuffer. Toutefois, cette requête n'aboutit pas toujours à des résultats corrects ; il peut être nécessaire de spécifier les valeurs de manière explicite.
mmsize=<largeur>x<hauteur>Spécifie la largeur et la hauteur physiques en millimètres.
offset=<largeur>x<hauteur>Spécifie le décalage du coin supérieur gauche de l'écran en pixels. La position par défaut est (0, 0).
nographicsmodeswitchSpécifie que le terminal virtuel ne doit pas passer en mode graphique (KD_GRAPHICS). En règle générale, l'activation du mode graphique désactive le curseur clignotant et la suppression de l'écran. Toutefois, lorsque ce paramètre est défini, ces deux fonctionnalités sont également ignorées.
tty=/dev/ttyNRemplace la console virtuelle. Utilisé uniquement lorsque nographicsmodeswitch n'est pas défini.

Depuis Qt 5.9, le comportement d'EGLFS et de LinuxFB a été synchronisé en ce qui concerne la politique de dimensionnement des fenêtres : la première fenêtre de niveau supérieur est forcée de couvrir tout l'écran, avec les deux plugins de plateforme. Si cela n'est pas souhaité, mettez la variable d'environnement QT_QPA_FB_FORCE_FULLSCREEN à 0 pour restaurer le comportement des versions antérieures de Qt.

Affichage de la sortie

Le niveau de support pour cibler un ou plusieurs écrans à partir d'une seule application Qt varie entre les plugins de plateforme. La prise en charge dépend souvent du périphérique et de sa pile graphique.

EGLFS avec le backend eglfs_kms

Lorsque le backend KMS/DRM est utilisé, EGLFS rapporte tous les écrans disponibles dans QGuiApplication::screens(). Les applications peuvent cibler différents écrans avec différentes fenêtres via QWindow::setScreen().

Note : La restriction d'une seule fenêtre plein écran par écran s'applique toujours. Changer d'écran après avoir rendu visible le site QWindow n'est pas supporté non plus. Il est donc essentiel que les applications intégrées fassent tous les appels nécessaires à QWindow::setScreen() avant d'appeler QWindow::show().

Lorsque vous commencez à développer sur un périphérique embarqué donné, il est souvent nécessaire de vérifier le comportement du périphérique et des pilotes, et de s'assurer que les écrans connectés fonctionnent comme ils le devraient. Un moyen facile est d'utiliser l'exemple hellowindow. En le lançant avec les arguments -platform eglfs --multiscreen --timeout, un logo Qt tournant apparaît sur chaque écran connecté pendant quelques secondes.

Configuration personnalisée

Le backend KMS/DRM prend également en charge les configurations personnalisées via un fichier JSON. Pour ce faire, définissez la variable d'environnement QT_QPA_EGLFS_KMS_CONFIG avec le nom du fichier. Vous pouvez également intégrer ce fichier dans l'application via le système de ressources Qt.

La plupart de ces options de configuration s'appliquent à tous les backends basés sur KMS/DRM, quelle que soit la technologie de gestion de la mémoire tampon (GBM ou EGLStreams).

Remarque : les fichiers de configuration sont considérés comme des contenus de confiance entièrement contrôlés et gérés par le créateur de l'appareil ou de la plate-forme. Ils ne sont pas censés être exposés à l'utilisateur final sous quelque forme que ce soit.

Voici un exemple de configuration :

{
  "device": "/dev/dri/card1",
  "hwcursor": false,
  "pbuffers": true,
  "outputs": [
    {
      "name": "VGA1",
      "mode": "off"
    },
    {
      "name": "HDMI1",
      "mode": "1024x768"
    }
  ]
}

Nous configurons ici le dispositif spécifié de manière à ce que :

  • Il n'utilise pas le curseur matériel (il revient au rendu du curseur de la souris via OpenGL ; par défaut, les curseurs matériels sont activés car ils sont plus efficaces).
  • Il soutient QOffscreenSurface avec des surfaces pbuffer EGL standard (par défaut, ceci est désactivé et une surface gbm est utilisée à la place).
  • La sortie sur le connecteur VGA est désactivée, tandis que le HDMI est actif avec une résolution de 1024x768.

De plus, une telle configuration désactive également la recherche d'un périphérique via libudev; à la place, le périphérique spécifié est utilisé.

Lorsque mode n'est pas défini, le mode préféré du système est choisi. Les valeurs acceptées pour mode sont les suivantes : off, current, preferred, skip, widthxheight, widthxheight@vrefresh, ou une chaîne de caractères modélisée.

En spécifiant current, vous choisissez un mode dont la résolution correspond à celle du mode actuel. Comme le réglage du mode n'est effectué que lorsque le mode désiré est effectivement différent du mode actif (à moins qu'il ne soit forcé par la variable d'environnement QT_QPA_EGLFS_ALWAYS_SET_MODE ), cette valeur est utile pour préserver le mode actuel et tout contenu dans les plans qui n'est pas touché par Qt.

skip fait en sorte que le connecteur de la sortie soit ignoré comme s'il était déconnecté. off est similaire, mais il change le mode et éteint l'affichage.

Comportement par défaut

Par défaut, tous les écrans signalés par la couche DRM sont traités comme un seul grand bureau virtuel. L'implémentation du curseur de la souris en tient compte et se déplace à travers les écrans comme prévu. Bien que cela ne soit pas recommandé, vous pouvez désactiver le bureau virtuel en réglant separateScreens sur false dans la configuration.

Par défaut, le bureau virtuel est formé de gauche à droite, en fonction de l'ordre des connecteurs tel qu'il est indiqué par le système. Pour changer cela, définissez virtualIndex avec une valeur commençant à 0.

Par exemple, la configuration suivante utilise la résolution préférée mais garantit que le côté gauche du bureau virtuel est l'écran connecté au port HDMI, tandis que le côté droit est l'écran connecté au port DisplayPort :

{
  "device": "drm-nvdc",
  "outputs": [
    {
      "name": "HDMI1",
      "virtualIndex": 0
    },
    {
      "name": "DP1",
      "virtualIndex": 1
    }
  ]
}

L'ordre des éléments du tableau n'a pas d'importance. Les sorties dont les indices virtuels ne sont pas spécifiés sont placées après les autres, l'ordre original dans la liste des connecteurs DRM étant préservé.

Pour créer un espace de bureau vertical (c'est-à-dire pour empiler les éléments de haut en bas plutôt que de gauche à droite), ajoutez une propriété virtualDesktopLayout après device avec la valeur vertical.

Attention : Il est recommandé que tous les écrans du bureau virtuel utilisent la même résolution, sinon des éléments tels que le curseur de la souris peuvent se comporter de manière inattendue lorsqu'ils entrent dans des zones qui n'existent que sur un écran donné.

Lorsque virtualIndex ne suffit pas, la propriété virtualPos peut être utilisée pour spécifier explicitement la position en haut à gauche de l'écran en question. En reprenant l'exemple précédent et en supposant une résolution de 1080p pour HDMI1, l'extrait de code suivant place un deuxième écran HDMI sous le premier :

{
   ...
  "outputs": [
    ...
    {
      "name": "HDMI2",
      "virtualPos": "0, 1080"
    }
  ]
}

Remarque : évitez ce type de configuration si vous souhaitez utiliser la souris. Le comportement du curseur de la souris peut être inattendu avec des dispositions non linéaires. Le tactile ne devrait pas poser de problème.

Interrogation automatique de la taille de l'écran physique

Dans certains cas, l'interrogation automatique de la taille physique de l'écran par le DRM peut échouer. Normalement, les variables d'environnement QT_QPA_EGLFS_PHYSICAL_WIDTH et QT_QPA_EGLFS_PHYSICAL_HEIGHT sont utilisées pour fournir les valeurs manquantes. Cette méthode n'est plus adaptée lorsque plusieurs écrans sont présents. A la place, utilisez les propriétés physicalWidth et physicalHeight dans la liste outputs pour spécifier les tailles en millimètres.

Remarque : il est déconseillé d'utiliser des tailles physiques différentes et donc des DPI logiques différents, car cela peut entraîner des problèmes inattendus dus au fait que certains composants de la pile graphique ne savent pas qu'il existe plusieurs écrans et se fient uniquement aux valeurs du premier écran.

Sorties actives et instances QScreen

Chaque sortie active du tableau outputs correspond à une instance QScreen signalée par QGuiApplication::screens(). Par défaut, l'écran primaire rapporté par QGuiApplication::primaryScreen() est l'écran enregistré en premier. Si vous n'utilisez pas virtualIndex, cela signifie que la décision est basée sur l'ordre des connecteurs DRM. Pour annuler cette décision, définissez la propriété primary sur true pour l'entrée souhaitée dans la liste outputs.

Par exemple, pour s'assurer que l'écran correspondant à la sortie VGA est l'écran principal même si le système signale d'abord l'écran HDMI, procédez comme suit :

{
  "device": "/dev/dri/card0",
  "outputs": [
      { "name": "HDMI1" },
      { "name": "VGA1", "mode": "1280x720", "primary": true },
      { "name": "LVDS1", "mode": "off" }
  ]
}

Pour le dépannage, il peut être utile d'activer les journaux de débogage du backend KMS/DRM. Pour ce faire, activez la règle de journalisation catégorisée qt.qpa.eglfs.kms.

Remarque : dans un environnement intégré, les bureaux virtuels sont plus limités qu'un système de fenêtrage complet. Les fenêtres qui chevauchent plusieurs écrans, les fenêtres qui ne sont pas en plein écran et le déplacement des fenêtres entre les écrans doivent être évités et peuvent ne pas fonctionner comme prévu.

Un cas d'utilisation courant

Le cas d'utilisation le plus courant et le mieux supporté pour une configuration multi-écrans est d'ouvrir un site QQuickWindow ou QQuickView dédié à chaque écran. Avec la boucle de rendu par défaut de threaded de la scène Qt Quick, chacune de ces fenêtres aura son propre thread de rendu. C'est une bonne chose car les threads peuvent être régulés indépendamment en fonction de vsync, et n'interféreront pas entre eux. Avec la boucle basic, cela peut devenir problématique et entraîner une dégradation des animations.

Par exemple, la découverte de tous les écrans connectés et la création de QQuickView pour chacun d'entre eux peuvent être effectuées de cette manière :

int main(int argc, char **argv)
{
    QGuiApplication app(argc, argv);

    QVector<QQuickView *> views;
    for (QScreen *screen : app.screens()) {
        QQuickView *view = new QQuickView;
        view->setScreen(screen);
        view->setResizeMode(QQuickView::SizeRootObjectToView);
        view->setSource(QUrl("qrc:/main.qml"));
        QObject::connect(view->engine(), &QQmlEngine::quit, qGuiApp, &QCoreApplication::quit);
        views.append(view);
        view->showFullScreen();
    }

    int result = app.exec();

    qDeleteAll(views);
    return result;
}

Fonctionnalités avancées d'eglfs_kms

Clonage (mise en miroir)

Le clonage d'écran (mirroring) est pris en charge. Cette fonction est activée via la propriété clones:

{
  "device": "/dev/dri/card0",
  "outputs": [
      { "name": "HDMI1", "mode": "1920x1080" },
      { "name": "DP1", "mode": "1920x1080", "clones": "HDMI1" }
 ]
}

Dans ce cas, le contenu de l'écran connecté via DisplayPort sera le même que celui de l'écran HDMI. Ceci est assuré par le balayage de la même mémoire tampon sur les deux.

Toutefois, cette fonction ne peut fonctionner que si les résolutions sont identiques, s'il n'y a pas d'incompatibilités en ce qui concerne les formats de tampon acceptés et si l'application n'a pas de sortie sur le site QScreen associée à une destination clone. En pratique, cela signifie qu'aucun QWindow associé au QScreen en question - DP1 dans l'exemple - ne doit jamais effectuer une opération QOpenGLContext::swapBuffers(). C'est à la configuration et à l'application de s'en assurer.

Mode sans tête avec DRM render

Le mode sans tête via les nœuds de rendu DRM est pris en charge. Il permet d'effectuer des calculs sur le GPU (OpenGL compute shaders, OpenCL) ou un rendu OpenGL hors écran sans avoir besoin des privilèges du maître DRM. Dans ce mode, les applications peuvent fonctionner même s'il y a déjà un autre processus qui produit un rendu à l'écran.

Le simple fait de faire passer device de /dev/dri/card0 à /dev/dri/renderD128 est futile en soi, car un certain nombre d'opérations ne peuvent pas être effectuées en mode sans tête. C'est pourquoi il faut combiner cela avec une propriété headless, par exemple :

{
    "device": "/dev/dri/renderD128",
    "headless": "1024x768"
}

Gardez à l'esprit que les fenêtres sont toujours dimensionnées pour correspondre à la taille de l'écran - désormais virtuel - d'où la nécessité de spécifier une taille dans la propriété headless. Il n'y a pas non plus d'étranglement basé sur vsync.

Une fois l'option activée, les applications ont deux choix typiques pour effectuer un rendu hors écran en mode headless :

Utiliser une fenêtre ordinaire, telle qu'une sous-classe de QOpenGLWindow, en ciblant le framebuffer par défaut de la fenêtre, c'est-à-dire une gbm_surface en pratique :

MyOpenGLWindow w;
w.show(); // will not actually show up on screen
w.grabFramebuffer().save("output.png");

Ou l'approche hors écran typique avec un FBO supplémentaire :

QOffscreenSurface s;
s.setFormat(ctx.format());
s.create();
ctx.makeCurrent(&s);
QOpenGLFramebufferObject fbo(1024, 768);
fbo.bind();
ctx.functions()->glClearColor(1, 0, 0, 1);
ctx.functions()->glClear(GL_COLOR_BUFFER_BIT);
fbo.toImage().save("output.png");
ctx.doneCurrent();

Sélection de l'API DRM

KMS/DRM peut être utilisé avec deux API DRM différentes : l'API patrimoniale et l'API atomique. Le principal avantage de l'API atomique DRM est de permettre plusieurs mises à jour des plans DRM au cours d'une même boucle de rendu, alors que l'API traditionnelle nécessiterait une mise à jour du plan par vsync.

L'API atomique est utile lorsque votre application doit mélanger du contenu dans des superpositions en conservant toutes les mises à jour dans la même vsync. Tous les appareils ne supportent pas encore cette API et elle pourrait être indisponible sur certains appareils plus anciens. Le backend de KMS utilise par défaut l'ancienne API, mais vous pouvez activer l'API atomique DRM en définissant la variable d'environnement QT_QPA_EGLFS_KMS_ATOMIC à 1.

L'utilisation d'un framebuffer plus petit que la résolution de l'écran peut également être utile. Cela est possible avec DRM atomic en utilisant le paramètre size dans le fichier JSON. L'exemple ci-dessous utilise un framebuffer de 1280x720 sur un vidéomode de 3840x2160 :

{
  "device": "/dev/dri/card0",
  "outputs": [
    { "name": "HDMI1", "mode": "3840x2160", "size": "1280x720", "format": "argb8888" }
  ]
}

EGLFS hot-plug et hot-reload

Si un fichier de configuration KMS est défini via QT_QPA_EGLFS_KMS_CONFIG, le fichier référencé sera surveillé par QFileSystemWatcher. Toute modification de ce fichier déclenchera sa relecture et entraînera les mises à jour appropriées, par exemple des changements de disposition, des changements de mode, l'activation/désactivation d'écrans.

De la même manière, mais strictement en opt-in, QT_QPA_EGLFS_HOTPLUG_ENABLED peut être défini et permettra à QDeviceDiscovery de surveiller l'appareil KMS pour tout changement tel que des écrans connectés/ branchés et déconnectés/débranchés.

Les deux fonctions peuvent être utilisées en parallèle et le seront souvent.

Pour faciliter correctement les changements dynamiques déclenchés par le branchement et le rechargement à chaud, il est important de réagir en créant et en détruisant des fenêtres dans les écrans qui apparaissent et disparaissent.

Exemple de code :

#include <QGuiApplication>
#include <QQuickView>
#include <QQuickItem>
#include <QScreen>
#include <QQmlContext>

QHash<QString, QQuickView*> screenNameToViewMap;

int main(int argc, char* argv[])
{
    QGuiApplication app(argc,argv);
    app.setQuitOnLastWindowClosed(false);

    auto lRemove = [&](QScreen *screen) {
        if (screen->name().compare(QStringLiteral("qt_Headless")) == 0)
            return;

        if (!screenNameToViewMap.contains(screen->name()))
            return;

        QQuickView *view = screenNameToViewMap.take(screen->name());
        delete view;
    };

    auto lAdd = [&](QScreen *screen) {
        if (screen->handle() == nullptr)
            return;

        if (screen->name().compare(QStringLiteral("qt_Headless")) == 0)
            return;

        if (screenNameToViewMap.contains(screen->name()))
            return;

        QQuickView *view = new QQuickView;

        view->setSource(QUrl(QStringLiteral("qrc:/main.qml")));
        view->setScreen(screen);                    // This is not as important as the next line, but good practice
        view->setGeometry(screen->geometry());      // This is vital! Otherwise QWindow::show (/QWindowPrivate::create) will change the screen!
        view->show();

        screenNameToViewMap.insert(screen->name(), view);
    };

    QObject::connect(&app, &QGuiApplication::screenAdded, &app, lAdd, Qt::QueuedConnection);
    QObject::connect(&app, &QGuiApplication::screenRemoved, &app, lRemove);

    for (QScreen *screen : app.screens())
        lAdd(screen);

    return app.exec();
}

Veuillez noter dans l'exemple ci-dessus la ligne concernant QWindow::setGeometry, qui est cruciale pour que les fenêtres apparaissent sur l'écran auquel elles sont destinées !

Étant donné que les mêmes signaux existent également en QML, un code équivalent peut être écrit en QML.

En particulier pour QT_QPA_EGLFS_HOTPLUG_ENABLED, il est vital d'adopter votre code, car certains écrans se comporteront comme s'ils étaient déconnectés lorsqu'ils sont éteints. Si cette fonction n'est pas activée, ni Qt ni le code de l'application ne seront informés des déconnexions. Toutes les ressources du backend resteront généralement intactes et l'écran réapparaîtra donc comme si rien ne s'était passé. Avec QT_QPA_EGLFS_HOTPLUG_ENABLED, les ressources de Qt XML sur l'écran (par exemple QScreen, QPlatformScreen, backing-store,...) seront détruites et seront recréées lors de la connexion (par exemple en rallumant l'écran). Par conséquent, les ressources au niveau de l'application (par exemple QWindow) doivent également être recréées.

L'écran de secours appelé "qt_Headless" est là pour faciliter les passages vers et depuis une configuration sans écran. Pour les fenêtres au niveau de l'application, cet écran peut et doit être ignoré dans la plupart des cas.

EGLFS avec un backend eglfs_kms_egldevice

Ce backend, typiquement utilisé sur les appareils Tegra, est similaire au backend KMS/DRM mentionné ci-dessus, sauf qu'il s'appuie sur les extensions EGLDevice et EGLStream au lieu de GBM.

Pour plus de détails techniques sur cette approche, consultez cette présentation.

Depuis Qt 5.7, ce backend partage un grand nombre de ses implémentations internes avec le backend basé sur le GBM. Cela signifie que les écrans multiples et la configuration avancée via QT_QPA_EGLFS_KMS_CONFIG sont pris en charge. Certains paramètres, tels que hwcursor et pbuffers, ne sont toutefois pas applicables.

Par défaut, le backend choisit automatiquement la couche EGL correcte pour le plan par défaut de chaque sortie. Si nécessaire, cela peut être surchargé en réglant la variable d'environnement QT_QPA_EGLFS_LAYER_INDEX sur l'index de la couche désirée. Cette approche ne supporte pas actuellement les sorties multiples, donc son utilisation devrait être limitée aux systèmes avec un seul écran. Pour savoir quelles couches sont disponibles et pour déboguer les problèmes de démarrage potentiels, activez la catégorie de journalisation qt.qpa.eglfs.kms.

Dans certains cas, il peut être nécessaire de définir un mode vidéo au démarrage de l'application, même si l'écran indique que la résolution souhaitée est déjà définie. Cette opération est normalement optimisée, mais si l'écran reste éteint, essayez de définir la variable d'environnement QT_QPA_EGLFS_ALWAYS_SET_MODE à une valeur non nulle et relancez l'application.

Pour configurer le comportement de l'objet EGLStream utilisé par le backend, utilisez la variable d'environnement QT_QPA_EGLFS_STREAM_FIFO_LENGTH. Cela suppose que KHR_stream_fifo est pris en charge par le système cible. Par défaut, le flux fonctionne en mode boîte aux lettres. Pour passer en mode FIFO, définissez une valeur égale ou supérieure à 1. La valeur spécifie le nombre maximum de trames que le flux peut contenir.

Sur certains systèmes, il peut s'avérer nécessaire de cibler un plan de recouvrement spécifique par le biais d'un connecteur prédéfini. Le simple fait de forcer un index de couche via QT_QPA_EGLFS_LAYER_INDEX ne permet pas de configurer le plan et n'est donc pas approprié en soi. Dans ces cas particuliers, il convient d'utiliser les variables d'environnement QT_QPA_EGLFS_KMS_CONNECTOR_INDEX et QT_QPA_EGLFS_KMS_PLANE_INDEX. Lorsque celles-ci sont définies, seuls le connecteur et le plan spécifiés seront utilisés, toutes les autres sorties seront ignorées. Le backend se chargera de sélectionner la couche EGL qui correspond au plan désiré, et de configurer le plan.

Saisie tactile dans les systèmes à écrans multiples sur KMS/DRM

Les écrans tactiles nécessitent des considérations supplémentaires dans les systèmes multi-écrans car les événements tactiles doivent être acheminés vers le bon écran virtuel, ce qui nécessite une correspondance correcte entre les écrans tactiles et les sorties d'affichage.

Cette correspondance est assurée par le fichier de configuration JSON spécifié à l'adresse QT_QPA_EGLFS_KMS_CONFIG et décrit dans les sections précédentes. Lorsqu'une propriété touchDevice est présente dans un élément du tableau outputs, la valeur est traitée comme un nœud de périphérique et le périphérique tactile est associé à la sortie d'affichage en question.

Par exemple, en supposant que notre écran tactile ait un nœud de périphérique /dev/input/event5 et qu'il s'agisse d'un écran tactile intégré au moniteur connecté via HDMI en tant qu'écran secondaire, la configuration suivante garantit une traduction correcte des événements tactiles (et de la souris synthétisée) :

 {
    "device": "drm-nvdc",
    "outputs": [
      {
        "name": "HDMI1",
        "touchDevice": "/dev/input/event5",
        "virtualIndex": 1
      },
      {
        "name": "DP1",
        "virtualIndex": 0
      }
    ]
}

Remarque : en cas de doute, activez la journalisation des sous-systèmes graphiques et d'entrée en définissant la variable d'environnement QT_LOGGING_RULES=qt.qpa.*=true avant de lancer l'application. Cela permettra d'identifier les nœuds de périphériques d'entrée corrects et de découvrir des problèmes de configuration de sortie qui peuvent être difficiles à déboguer autrement.

Note : A partir de Qt 5.14, ce qui précède n'est supporté que pour les backends evdevtouch et libinput. Les autres variantes continueront à acheminer les événements vers l'écran principal. Pour forcer l'utilisation d'evdevtouch sur les systèmes où plusieurs backends d'entrée sont disponibles, définissez la variable d'environnement QT_QPA_EGLFS_NO_LIBINPUT à 1.

EGLFS avec d'autres backends

Les autres backends, qui sont typiquement basés sur le ciblage du framebuffer ou d'une API de composition directement via l'implémentation EGL du fournisseur, fournissent généralement un support limité ou inexistant pour les écrans multiples. Sur les cartes i.MX6 équipées de GPU Vivante, la variable d'environnement QT_QPA_EGLFS_FB peut être utilisée pour spécifier le framebuffer à cibler, de manière similaire à linuxfb. Sur le Raspberry Pi, la variable d'environnement QT_QPA_EGLFS_DISPMANX_ID peut être utilisée pour spécifier l'écran sur lequel la sortie doit se faire. La valeur correspond à l'une des constantes DISPMANX_ID_, voir la documentation Dispmanx. Notez que ces approches, contrairement à KMS/DRM, ne permettent généralement pas d'afficher plusieurs écrans à partir de la même application. Par ailleurs, des variables d'environnement spécifiques au pilote ou des paramètres du noyau peuvent également être disponibles pour contrôler le framebuffer utilisé. Reportez-vous à la documentation de la carte embarquée.

Mémoire vidéo

Les systèmes disposant d'une quantité fixe de mémoire vidéo dédiée peuvent nécessiter une attention particulière avant d'exécuter des applications Qt XML basées sur Qt Quick ou des classes telles que QOpenGLWidget. Le paramètre par défaut peut être insuffisant pour de telles applications, en particulier lorsqu'elles sont affichées sur un écran à haute résolution (par exemple, full HD). Dans ce cas, elles peuvent commencer à échouer de manière inattendue. Il est recommandé de s'assurer qu'il y a au moins 128 Mo de mémoire GPU disponible. Pour les systèmes qui ne disposent pas d'une quantité fixe de mémoire réservée au GPU, ce problème ne se pose pas.

linuxfb

Utilisez le paramètre du plugin fb pour spécifier le périphérique framebuffer à utiliser.

Gestionnaires de signaux Unix

Les plugins de plateforme orientés console comme eglfs et linuxfb installent par défaut des gestionnaires de signaux pour capturer les interruptions (SIGINT), suspendre et continuer (SIGTSTP, SIGCONT) et terminer (SIGTERM). De cette manière, le clavier, le curseur du terminal et éventuellement d'autres états graphiques peuvent être restaurés lorsque l'application se termine ou est suspendue à cause de kill, ou Ctrl+C ou Ctrl+Z(bien que la terminaison ou la suspension via le clavier ne soit possible que lorsque QT_QPA_ENABLE_TERMINAL_KEYBOARD est défini, comme indiqué plus haut dans la section Entrée). Cependant, dans certains cas, la capture de SIGINT peut être indésirable car elle peut entrer en conflit avec le débogage à distance, par exemple. C'est pourquoi la variable d'environnement QT_QPA_NO_SIGNAL_HANDLER permet d'exclure la gestion de tous les signaux intégrés.

Polices de caractères

Qt utilise normalement fontconfig pour accéder aux polices de caractères du système. Si fontconfig n'est pas disponible, Qt utilisera QBasicFontDatabase. Dans ce cas, les applications Qt chercheront les polices dans le répertoire lib/fonts de Qt. Qt détectera automatiquement les polices pré-rendues et les polices TrueType. Ce répertoire peut être remplacé par la variable d'environnement QT_QPA_FONTDIR.

Pour plus d'informations sur les formats pris en charge, voir Qt for Embedded Linux Fonts.

Remarque : Qt ne fournit plus de polices dans le répertoire lib/fonts. Cela signifie que c'est à la plateforme (l'image système) de fournir les polices nécessaires.

Plugins de plate-forme pour les systèmes de fenêtrage sur les dispositifs Linux embarqués

XCB

Il s'agit du plugin X11 utilisé sur les plates-formes Linux de bureau ordinaires. Dans certains environnements embarqués, qui fournissent X et les fichiers de développement nécessaires pour xcb, ce plugin fonctionne comme sur un PC de bureau ordinaire.

Note : Sur certains appareils, il n'y a pas de support EGL et OpenGL disponible sous X car l'implémentation EGL n'est pas compatible avec Xlib. Dans ce cas, le plugin XCB est construit sans le support EGL, ce qui signifie que Qt Quick 2 ou d'autres applications basées sur OpenGL ne fonctionnent pas avec ce plugin de plateforme. Il peut toutefois être utilisé pour faire fonctionner des applications à rendu logiciel (basées sur QWidget par exemple).

En règle générale, il n'est pas conseillé d'utiliser XCB sur des appareils embarqués. Des plugins comme eglfs sont susceptibles de fournir de meilleures performances et une accélération matérielle.

Wayland

Wayland est un système de fenêtrage léger ; ou plus précisément, c'est un protocole permettant aux clients de communiquer avec un serveur d'affichage.

Qt Wayland fournit un plugin de plateforme wayland qui permet aux applications Qt de se connecter à un compositeur Wayland.

Pour plus de détails, voir Wayland et Qt.

Lignes directrices pour l'amélioration des performances

Utilisez le rendu matériel lorsque c'est possible

Lorsque les performances sont essentielles pour votre application, évitez d'utiliser les modules Qt qui reposent sur un rendu logiciel. Préférez les modules qui s'appuient sur le rendu matériel, dans la mesure du possible.

Suivre les meilleures pratiques pour Qt Quick

Suivez les meilleures pratiques pour QML et Qt Quick, en particulier en ce qui concerne l'inclusion de l'API CMake QML, de sorte que qmllint, le compilateur de scripts QML (qmlsc) et le compilateur de types QML (qmltc) soient disponibles. En outre, il est préférable d'écrire du QML déclaratif et de minimiser le Javascript. Pour plus d'informations sur l'impact de l'utilisation excessive de JavaScript sur les performances, voir QML Performance Considerations and Suggestions (Considérations et suggestions sur les performances de QML).

Utiliser des images/textures et des effets de shaders au lieu du type QML Canvas

Pour dessiner des éléments d'interface utilisateur personnalisés, utilisez des images/textures et des effets de shaders. N'utilisez pas le type QML Canvas. Les shaders nécessitent une accélération matérielle (GPU).

Utilisez Qt Quick au lieu de Qt Widgets

Avec Qt Quick, il est possible d'utiliser des backends à accélération matérielle ou à rendu logiciel. Pour les Uls complexes, l'utilisation de Qt Widgets sur des cibles embarquées n'est pas recommandée, car elle utilisera toujours un backend logiciel.

Il y a des compromis à faire :

  • L'utilisation du moteur QML et de Qt Quick s'accompagne d'une surcharge initiale.
  • Si votre interface utilisateur est très simple et ne se repeint que rarement, elle peut être plus rapide si elle est mise en œuvre à l'aide de Widgets plutôt que de QML.
  • Si votre interface utilisateur bénéficie d'animations, smooth scrolling,et scaling, rendering effects, ou de la 3D, vous devez disposer d'une accélération GPU, et donc Qt Quick.

Choisissez une résolution adaptée à la taille de votre interface utilisateur

Il convient d'être prudent avec les résolutions plus élevées. Les résolutions de 720p et plus peuvent réduire les performances.

Utilisez un type de fenêtre QML comme élément racine de votre application.

Utilisez un Window comme élément racine de votre application avec l'arrière-plan de l'application color.

La raison en est qu'un composant Window possède une propriété de couleur qui a l'effet d'un tampon clair. Le rendu de l'arrière-plan à l'aide d'un plein écran Rectangle en tant qu'élément racine de l'application Item entraînerait un appel de dessin supplémentaire. Pour certains backends RHI, cela peut être la même chose, mais il y a une différence entre un appel à glClear et le dessin d'un quad. Dans la plupart des cas, une image unique et opaque n'a pas d'impact important sur les performances, mais si vous utilisez une valeur alpha dans la couleur de cet élément, l'impact sur les performances peut être significatif.

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.