Phantom OS: sous-système de fenêtre - faire des contrôles

Aujourd'hui, nous allons parler de la façon dont l'interface graphique Phantom est organisée.

(Quel est Phantom OS, vous pouvez le découvrir en lisant ces articles .)

Plus précisément - comment est née cette interface graphique. Pendant longtemps, le Phantom n'a eu qu'une conclusion graphique - il était presque impossible de transmettre quoi que ce soit au système avec la souris.

Maintenant, le moment est venu de rendre au moins simple - mais les applications, ce qui signifie - vous avez besoin d'une interface utilisateur. Quoi qu'il en soit - le système, nous serons honnêtes, il avait l'air effrayant. Et ce n'est pas à la mode maintenant.

Qu'est-ce qui était disponible au début du projet UI? En principe - beaucoup.

Il y avait, en fait, des graphiques - un pilote vidéo, un sous-système de fenêtre en mode affichage uniquement, des polices bitmap, un sous-système d'événements de fenêtre (événements), un contrôle de focus de fenêtre et des primitives associées.

Maintenant, les étapes et un peu plus.

Le sous-système de pilote vidéo peut exécuter la fonction probe () de plusieurs pilotes tour à tour, recevoir de leur part des demandes de résolution maximale et de couleur, ainsi que la possibilité de travailler en mode accélérateur 2D. Le système nécessite un minimum de couleurs 24 bits. À ce niveau, nous avons un framebuffer (un écran en mémoire), une souris et plusieurs types de primitives bitblt.

Primitives Bitblt - trois types de base ont été implémentés - copie complète des graphiques (avec des rectangles découpés), copie en tenant compte de la transparence binaire (un pixel est soit complètement transparent ou complètement opaque) et z-buffer. C'est-à-dire, la possibilité de copier à l'écran uniquement les pixels qui ont une coordonnée z supérieure à la coordonnée z d'un pixel existant - pour calculer un chevauchement partiel des fenêtres.

La couche de fonctions suivante est le sous-système de fenêtre. Il y a ici le concept d'une fenêtre, la décoration de fenêtre (cadre, fenêtre de titre avec boutons), les coordonnées x / y / z des fenêtres et un ensemble de fonctions qui sont chargées de dessiner les fenêtres à l'écran et de contrôler leur mouvement sur tous les axes.


Les événements suivants suivent - la file d'attente de microtâches, qui est traitée par le pilote de niveau inférieur pour le rendu et la gestion de l'état des fenêtres.

Il convient de noter que les meilleurs esprits de l'humanité prétendent qu'un système de fenêtre graphique qui fonctionnerait de manière stable et sans problème dans un environnement mutithread ne peut pas être écrit sans une file d'attente d'événements. Mes modestes tentatives d'ignorer cette déclaration l'ont jusqu'à présent confirmée. Il est très difficile de se passer de la file d'attente de messages et de faire tous les threads demandant des événements de fenêtre pour le programme et conduit parfois à une guerre à l'écran.

Par conséquent, la plupart des primitives du système de fenêtres concernant tout ce qui est plus grand que l'image à l'intérieur de la fenêtre sont implémentées via une file d'attente de messages. La demande envoie à la file d'attente le message «dessinez cette zone sur l'écran» ou «réorganisez la fenêtre au-dessus des autres», et un fil séparé ci-dessous les exécute de manière ordonnée et réfléchie.

Il obtient simplement le flux d'événements de la souris (pressé, glissé), du clavier (pressé, relâché) et du système de fenêtre lui-même (événements secondaires - après avoir déplacé la fenêtre vers le haut, redessinez la zone d'écran).

Une tâche distincte au niveau du flux d'événements est ce que l'on appelle la concentration. Une fenêtre focalisée reçoit un flux d'événements du clavier, et en effet, il est clairement mis en évidence sur l'écran comme un point d'adressage de l'activité de l'utilisateur. En plus de la tâche évidente de choisir une fenêtre pour diriger l'événement, ce système informe également la fenêtre de la perte de concentration, ce qui est parfois important.


Le niveau suivant est celui des primitives graphiques pour dessiner sur la fenêtre.

Il existe deux options principales pour la mise en œuvre. Ancien, économique - lorsqu'une fenêtre ne stocke pas de copie de ce qui y est dessiné. Si une telle fenêtre est effacée et que vous devez dessiner à nouveau l'effacé (par exemple, la fenêtre a été renvoyée à l'écran depuis le bord), la fenêtre appelle la fonction à partir de son programme et cette fonction doit dessiner tout ce qui est nécessaire. Il s'agit d'un modèle typique et terriblement gênant pour diverses raisons. La seconde est sélectionnée dans Phantom - chaque fenêtre a un bitmap dans lequel le contenu de la fenêtre est actuellement dessiné. Le système graphique peut toujours se référer à cette copie et la mettre à jour à l'écran sans tirer le programme utilisateur.

Notez que la fenêtre appartenant au programme utilisateur (et non au noyau) dans Phantom, bien sûr, est persistante, est stockée dans la mémoire persistante et après le redémarrage, le système d'exploitation enregistre tout ce qui y est dessiné. Ceci, soit dit en passant, est étonnamment utile et simplifie le code d'application dans certains endroits à l'indécence.

Un ensemble de primitives de dessin permet au code d'application, comme d'habitude, de dessiner un point, une ligne, un bitmap, une ligne de texte dans une police bitmap et d'autres petites choses dans la fenêtre.

Sur cette richesse du sous-système graphique au début du projet "New Phantom UI" et terminé. En principe, ce kit gentleman était suffisant pour beaucoup, mais uniquement pour l'utilisateur. Aucune entrée.

Plus précisément, il y avait un support rudimentaire pour le concept de «bouton», mais uniquement avec la souris, uniquement dans la barre d'outils et uniquement pour fermer la fenêtre. :)

La tâche de développement était la suivante:

  • TrueType Sans cela, c'est dommage.
  • Événements clavier et commandes clavier. Au moins basique.
  • Penser à la localisation des mises en page - au moins l'alphabet cyrillique, mais jeter les bases d'un changement de mise en page.
  • Commandes - boutons, boutons radio, champs de texte, étiquettes, menus, etc.
  • Le focus du contrôle est le choix d'un point de contrôle à l'intérieur de la fenêtre.
  • Une sorte de composant d'écran pour gérer les fenêtres à l'écran. Barre des tâches?
  • En fait, les images des contrôles et en général une sorte de conception d'interface utilisateur - ne devraient pas être aussi fermes collectives qu'elles l'étaient.

C'était comme ça:

image

En cours de route, il s'est avéré que le mélange alpha était également nécessaire, c'est-à-dire une transparence partielle des pixels lors de la superposition d'images. Eh bien, il est devenu clair qu'il était temps de toucher Unicode pour le pis.

L'approche de ce poids est divisée en trois grandes parties: Design, Trump, le reste.

À propos de la conception, en bref: il existe des conceptions d'interface utilisateur gratuites sur Internet sans exigences d'utilisation malveillantes. Trois jours pour rechercher et sélectionner, un temps infini pour la découpe artistique des éléments graphiques.

Type d'atout


J'avais peur de cela, mais, en fin de compte, en vain. Il y a libfreetype, il y a des exemples d'application, après deux jours le rendu des polices vectorielles fonctionnait assez bien en mode test.

Cependant, il y a des subtilités, et tout le chemin n'a pas été couvert. À savoir. Travailler avec les polices du noyau - est. Les polices sont ensuite pilotées par le code dur dans le binaire du noyau. Cela est inévitable pour une police système, mais le code utilisateur doit avoir ses propres mécanismes de chargement. Et bien que certains FS du Phantom, bien sûr, le soient et le seront, ce modèle n'est pas naturel pour lui. Vous devez pouvoir stocker des polices dans des objets persistants et les faire passer sur le réseau.

La seconde est plus simple - les rookeries de polices gratuites sont abondantes et leur organisation ne sera pas longue.

Mais le premier ...

Vous ne le savez probablement pas, mais les variables de chaîne dans le fantôme possèdent des propriétés inattendues pour les programmeurs qui ne sont pas habitués à la persistance. Ils peuvent remplacer des fichiers. Un flux d'octets est un flux d'octets. Non seulement cela, c'est aussi, par définition, mappé en mémoire - c'est une variable. C'est, en principe, ce que nous stockons dans un système d'exploitation normal dans un fichier, dans Phantom, vous pouvez simplement le mettre dans une variable de chaîne. J'agis si souvent - et le compilateur Phantom a même un design - pour aspirer un fichier dans une constante de chaîne. Ainsi, dans les pays utilisateurs, les fantômes pénètrent, par exemple, les bitmaps. Mais c'est aussi une méthode honteuse, car alors au moment de l'exécution, cette variable doit être analysée pour obtenir une représentation opérationnelle de l'objet. Cependant, en ce qui concerne les bitmaps, à l'honneur du concept Phantom, tout va bien ici. Nous compilons le fichier graphique dans une chaîne lors de la compilation, lorsque Phantom est lancé pour la première fois, il est converti en un objet binaire persistant de type bitmep, et il est déjà utilisé plus tard après un certain nombre de redémarrages du système d'exploitation et ne nécessite pas la source d'origine. Cela devrait également être fait avec des polices, mais c'est un peu moins courant. Lorsque vous travaillez, la police vectorielle est rendue dans un raster, et il serait nécessaire de stocker uniquement ces rasters rendus. Ce n'est pas une astuce ou un problème - ils peuvent à nouveau être pliés en objets Phantom tels que le bitmap, mais il existe déjà une sorte d'infrastructure nécessaire - un bitmap de glyphe de style de style de style de style de style de police (UTF).

Ce n'est pas si difficile, mais, apparemment, la tâche de la prochaine étape. Alors que les polices sont tramées en appel.


Unicode


Le rendu des polices par définition implique de travailler avec Unicode. Bien sûr, c'est bien, car il fallait commencer quelque temps. En fait, il suffisait d'équiper le moteur de rendu d'un convertisseur UTF-8 en UTF-32 (et c'est le numéro de glyphe dans la police), de télécharger des polices avec l'alphabet cyrillique et cette partie de la localisation a fonctionné. De plus, si nous voulons d'autres langues, il est nécessaire et suffisant de remplacer la police. Cependant, la police de base sélectionnée contient beaucoup - pour l'Europe, certainement assez. La Chine aura besoin d'un remplacement de police, oui.

Travailler avec le clavier


Il n'y avait aucun signe d'action militaire du tout, mais, plus que des espoirs, j'ai dû me battre. Il s'est avéré que l'ancien pilote de clavier est toujours ... en espérant voir le matériel de l'IBM PC XT. Oui, au siècle dernier. Le fait est que le contrôleur de clavier est capable (était capable!) De convertir les codes de numérisation des claviers modernes (le soi-disant deuxième ensemble de codes) en celui ancien.

Cela s'est avéré à cause de la fin du QEMU, une telle conversion, apparemment, a finalement été rejetée. Ou accidentellement cassé. Mais le fait est que le conducteur a refusé de travailler. Avec chagrin, pendant une heure, avec l'aide d'une mère, j'ai transféré le chauffeur d'un uOS amical dans Phantom. Juste pour découvrir qu'il a le même problème. Le premier set. J'ai dû réécrire le tableau des codes de numérisation et l'analyseur. Je ne suis pas retourné à l'ancien chauffeur, et c'est pourquoi. Il s'est avéré que le pilote de uOS a une interface plus élégante avec le système. À savoir, il n'y revient pas, comme c'était la coutume, une paire (code de caractère, code de balayage des boutons), mais un caractère UTF-32 32 bits. Il s'avère qu'en UTF, il existe une gamme spéciale de codes alloués pour une utilisation locale, et ils sont plus que suffisants pour toutes les touches de fonction possibles. Travailler avec un tel flux d'événements dans le code de l'interface utilisateur est beaucoup plus simple.

De plus, la localisation tombait parfaitement sur un tel modèle. Il suffit de superposer le tableau ASCII-> UTF32 pour la langue souhaitée (jeu de caractères), et bravo - nous avons le cyrillique. Eh bien - presque là. Il faudrait maintenant soit le transcoder en UTF-8, soit refaire les abats de certaines parties de l'interface utilisateur en UTF-32. Moi aussi, j'ai accordé à ce moment une faible priorité.

Contrôles



Boutons, radio, cases à cocher et autres éléments d'interface utilisateur spécifiques.

L'infrastructure commune comprend:

  • Le mécanisme de stockage des contrôles par rapport à la fenêtre
  • Éléments de visualisation de contrôle typiques - cadre, arrière-plan, texte, icône, etc.
  • Transmission au contrôle des événements et aux schémas de réaction typiques (pousser / basculer)
  • Suivi des événements de la souris et de l'état de vol stationnaire
  • Rappels et génération d'événements secondaires pour informer des changements d'état

Contrôle de la mise au point


Pour que le contrôle (bouton, par exemple) puisse être utilisé sans souris, plusieurs choses sont nécessaires.

  • La possibilité de le sélectionner depuis le clavier
  • Afficher cette sélection
  • Réponse du clavier
  • Détection de perte de mise au point.

Ce dernier est le plus difficile.

En fait, le contrôle se concentre à la fois avec le clavier et la souris, et il s'agit d'une seule et même entité - si nous sélectionnons un champ de texte avec la souris, il répondra également aux touches. Si après cela, vous appuyez sur TAB, le droit de travailler avec le clavier passera à un autre.

Une tâche distincte consiste à regrouper certains contrôles et à mettre à jour leur statut de manière connexe. Presser un radiobathorn «serre» ses voisins de groupe.

Encore une fois, revenons au fait que nous écrivons un OS persistant. Cela signifie que potentiellement le contrôle peut être stocké dans la RAM persistante et survivre au redémarrage du noyau du système.

Autrement dit, sa connexion avec le noyau serait bonne à minimiser. Chaque pointeur vers une mémoire non persistante (en fait vers le noyau) après un redémarrage sera invalide et devra être restauré. Cela signifie qu'un tel pointeur n'a pas le droit de stocker des informations sur l'état du contrôle. La position du curseur sous forme d'entier - oui. Un pointeur vers un tampon dans le noyau dont la position est déterminée par la position du curseur ne l'est pas. Eh bien ou oui, seul un entier est toujours là et c'est plus important. En pratique, cela n'est pas très lourd, mais nous devons nous en souvenir.

Enfin, la barre des tâches. C'est une telle chose en bas (côté, haut) de l'écran où l'utilisateur pique s'il a perdu la fenêtre.

En principe, cela devrait déjà faire partie de l'espace utilisateur, mais ... le noyau utilise déjà activement l'interface graphique, donc pour l'instant cette partie sera également en bas. J'espère temporairement.

Total


image

À mon avis, les tâches qui ont été définies dans ce sens ont été généralement résolues. Bien sûr, il n'y a pas de limite à la perfection, mais, comme il me semble, l'interface a fait un pas tangible du hacker à l'universel.

Source: https://habr.com/ru/post/fr472160/


All Articles