SO Phantom: subsistema de janelas - fazer controles

Hoje falaremos sobre como a interface gráfica do usuário Phantom é organizada.

(O que é o Phantom OS você pode descobrir lendo estes artigos .)

Mais precisamente - como nasceu essa interface gráfica. Durante muito tempo, o Phantom teve apenas uma conclusão gráfica - era quase impossível transmitir qualquer coisa ao sistema com o mouse.

Agora chegou a hora de tornar pelo menos simples - mas aplicativos, o que significa - você precisa de uma interface do usuário. Enfim - o sistema, seremos honestos, parecia assustador. E isso não está na moda agora.

O que estava disponível no início do projeto de interface do usuário? Em princípio - muito.

De fato, havia gráficos - um driver de vídeo, um subsistema de janelas no modo somente exibição, fontes de bitmap, um subsistema de eventos de janelas (eventos), controle de foco de janelas e primitivas relacionadas.

Agora os passos e um pouco mais.

O subsistema do driver de vídeo pode executar a função probe () de vários drivers, por sua vez, receber solicitações deles para obter resolução máxima e indicação de cores, além da capacidade de trabalhar no modo acelerador 2D. O sistema requer um mínimo de cores de 24 bits. Nesse nível, temos um buffer de quadros (uma tela na memória), um mouse e vários tipos de primitivas bitblt.

Primitivas Bitblt - três tipos básicos foram implementados - cópia completa dos gráficos (com retângulos cortados), cópia levando em consideração a transparência binária (um pixel é completamente transparente ou completamente opaco) e o buffer z. Ou seja, a capacidade de copiar na tela apenas os pixels que possuem uma coordenada z maior que a coordenada z de um pixel existente - para determinar uma sobreposição parcial das janelas.

A próxima camada de funções é o subsistema de janelas. Aqui está o conceito de janela, decoração de janela (quadro, janela de título com botões), coordenadas x / y / z de janelas e um conjunto de funções responsáveis ​​por desenhar janelas na tela e controlar seu movimento em todos os eixos.

A documentação

A seguir, os eventos a seguir - a fila de microtask, processada pelo driver de nível inferior para renderizar e gerenciar o estado das janelas.

Deve-se notar que as melhores mentes da humanidade afirmam que um sistema gráfico de janelas que funcionaria de forma estável e sem problemas em um ambiente com múltiplos threads não pode ser gravado sem uma fila de eventos. Até agora, minhas modestas tentativas de ignorar essa afirmação apenas a confirmaram. É muito difícil dispensar a fila de mensagens e fazer com que todos os threads solicitem eventos da janela para o programa e, às vezes, levam à guerra na tela.

Portanto, a maioria das primitivas do sistema de janelas relacionadas a algo maior que a imagem dentro da janela é implementada por meio de uma fila de mensagens. A solicitação envia para a fila a mensagem "desenhe esta área na tela" ou "reorganize a janela em cima de outras", e um segmento separado abaixo as executa de maneira ordenada e ponderada.

Ele simplesmente obtém o fluxo de eventos do mouse (pressionado, arrastado), do teclado (pressionado, liberado) e do próprio sistema de janelas (eventos secundários - depois de mover a janela para cima, redesenhe a área da tela).

Uma tarefa separada no nível do fluxo de eventos é o chamado foco. Uma janela focada recebe um fluxo de eventos do teclado e, de fato, é claramente destacada na tela como um ponto de abordagem da atividade do usuário. Além da tarefa óbvia de escolher uma janela para direcionar o evento, esse sistema também informa a janela sobre a perda de foco, que às vezes é importante.

A documentação

O próximo nível são as primitivas gráficas para desenhar na janela.

Existem duas opções principais para implementação. Velho, econômico - quando uma janela não armazena uma cópia do que é desenhado nela. Se uma janela desse tipo for apagada e você precisar desenhá-la novamente (por exemplo, a janela retornou à tela a partir da borda), a janela chamará a função de seu programa e essa função deverá desenhar tudo o que for necessário. Este é um modelo típico e terrivelmente inconveniente por vários motivos. A segunda é selecionada no Phantom - cada janela possui um bitmap no qual o conteúdo da janela é desenhado atualmente. O sistema gráfico sempre pode consultar esta cópia e atualizá-la na tela sem puxar o programa do usuário.

Observe que a janela pertencente ao programa do usuário (e não ao kernel) no Phantom, é claro, é persistente, é armazenada na memória persistente e, após a reinicialização do sistema operacional, salva tudo o que está nele. Aliás, é surpreendentemente útil e simplifica o código do aplicativo em alguns lugares para ser indecente.

Um conjunto de primitivas de desenho permite que o código do aplicativo, como sempre, desenhe um ponto, uma linha, um bitmap, uma linha de texto em uma fonte de bitmap e algumas outras pequenas coisas na janela.

Sobre esta riqueza do subsistema gráfico no início do projeto "Nova interface fantasma" e terminou. Em princípio, o kit deste cavalheiro era suficiente para muito, mas apenas para o usuário. Nenhuma entrada.

Mais precisamente, havia um suporte rudimentar ao conceito de "botão", mas apenas com o mouse, apenas na barra de ferramentas e apenas para fechar a janela. :)

A tarefa de desenvolvimento foi a seguinte:

  • TrueType Sem isso, é uma pena.
  • Eventos do teclado e controles do teclado. Pelo menos básico.
  • Pensar na localização de layouts - pelo menos o alfabeto cirílico, mas estabelecer as bases para a alteração de layouts.
  • Controles - botões, botões de opção, campos de texto, etiquetas, menus e assim por diante.
  • O foco do controle é a escolha de um ponto de controle dentro da janela.
  • Algum tipo de componente de tela para gerenciar janelas na tela. Barra de tarefas?
  • Na verdade, as imagens dos controles e, em geral, algum tipo de design da interface do usuário - não devem ser tão farm quanto antes.

Foi assim:

imagem

No caminho, descobriu-se que a mistura alfa também era necessária, ou seja, transparência parcial dos pixels ao sobrepor imagens. Bem, ficou claro que era hora de tocar em Unicode para o úbere.

A abordagem para esse peso é dividida em três grandes partes: Design, Trump, o resto.

Em resumo, sobre o design: existem designs de interface do usuário gratuitos na Internet sem requisitos de uso indevido. Três dias para pesquisar e selecionar, tempo sem fim para o corte artístico de elementos gráficos.

Tipo Trump


Eu tinha medo disso, mas, como se viu, em vão. Há libfreetype, existem exemplos de aplicação, após dois dias a renderização de fontes vetoriais funcionou muito bem no modo de teste.

No entanto, existem sutilezas, e nem todo o caminho foi coberto. Ou seja. Trabalhar com fontes do kernel - é. As fontes são então direcionadas pelo código rígido para o binário do kernel. Isso é inevitável para uma fonte do sistema, mas o código do usuário deve ter seus próprios mecanismos de carregamento. E, embora alguns FS no Phantom, é claro, sejam e serão, esse modelo não é natural para ele. Você precisa armazenar fontes em objetos persistentes e obtê-las pela rede.

O segundo é mais simples - os viveiros de fontes gratuitas são abundantes e sua organização não será longa.

Mas o primeiro ...

Você provavelmente não sabe, mas as variáveis ​​de string no Phantom possuem propriedades inesperadas para programadores que não estão acostumados à persistência. Eles podem substituir arquivos. Um fluxo de bytes é um fluxo de bytes. Além disso, é também, por definição, a memória mapeada - é uma variável. Isso é, em princípio, o que armazenamos em um sistema operacional normal em um arquivo, no Phantom, você pode simplesmente colocá-lo em uma variável de string. Eu ajo com tanta frequência - e o compilador Phantom ainda tem um design - para sugar um arquivo em uma constante de string. Assim, na área do usuário, os Phantoms penetram, por exemplo, bitmaps. Mas esse também é um método vergonhoso, porque, em tempo de execução, essa variável precisa ser analisada para obter uma representação operacional do objeto. No entanto, quanto aos bitmaps, então, para a honra do conceito Phantom, está tudo bem aqui. Compilamos o arquivo gráfico em uma sequência de caracteres ao compilar, quando o Phantom é iniciado pela primeira vez, ele é convertido em um objeto binário persistente do tipo bitmep e já é usado posteriormente após várias reinicializações do SO e não requer a fonte original. Também deve ser feito com fontes, mas é um pouco menos comum. Ao trabalhar, a fonte de vetor é renderizada em uma varredura, e seria necessário armazenar apenas essas rasters renderizadas. Isso não é um truque ou um problema - eles podem ser novamente dobrados em objetos Phantom, como bitmap, mas já existe algum tipo de infraestrutura necessária - um bitmap de glifo (código UTF) para desenho de fonte e estilo e estilo e tamanho de glifo (código UTF).

Isso não é tão difícil, mas, aparentemente, a tarefa da próxima etapa. Enquanto as fontes são rasterizadas mediante recurso.

A documentação

Unicode


A renderização de fontes por definição envolve o trabalho com Unicode. Isso, é claro, é bom, porque é preciso começar um dia. De fato, bastava equipar o renderizador com um conversor de UTF-8 para UTF-32 (e esse é o número de glifo na fonte), fazer o download de fontes com o alfabeto cirílico e essa parte da localização funcionou. Além disso, se queremos outros idiomas, é necessário e suficiente substituir a fonte. No entanto, a fonte base selecionada contém muito - para a Europa, definitivamente o suficiente. A China precisará de uma substituição de fonte, sim.

Trabalhar com teclado


Não havia sinal de ação militar, mas, mais do que esperanças, tive que lutar. Aconteceu que o antigo driver do teclado ainda está ... esperando ver o hardware do IBM PC XT. Sim, século passado. O fato é que o controlador do teclado é capaz de (converter) códigos de verificação de teclados modernos (o chamado segundo conjunto de códigos) naquele antigo.

Aconteceu que, devido ao atraso no QEMU, essa conversão, aparentemente, foi finalmente descartada. Ou acidentalmente quebrou. Mas o fato é que o motorista se recusou a trabalhar. Com tristeza, por uma hora, com a ajuda de alguma mãe, levei o motorista de uma UOS amigável para o Phantom. Só para descobrir que ele tem o mesmo problema. O primeiro conjunto. Eu tive que reescrever a tabela de códigos de verificação e o analisador. Não voltei ao motorista antigo, e é por isso. Descobriu-se que o driver do uOS possui uma interface mais elegante para o sistema. Nomeadamente, ele retorna a ele não, como era habitual, um par (código de caractere, código de verificação de botão), mas um caractere UTF-32 de 32 bits. Acontece que em UTF existe um intervalo especial de códigos alocados para uso local e são mais que suficientes para todas as teclas de função possíveis. Trabalhar com esse fluxo de eventos no código da interface do usuário é muito mais simples.

Além disso, a localização caiu perfeitamente nesse modelo. É suficiente sobrepor a tabela ASCII-> UTF32 para o idioma desejado (conjunto de caracteres) e aplausos - temos cirílico. Bem - quase lá. Agora, seria necessário transcodificá-lo para UTF-8 ou refazer a parte externa de algumas partes da interface do usuário para UTF-32. Eu também coloquei esse momento como uma baixa prioridade.

Controles



Botões, opções de rádio, caixas de seleção e outros elementos específicos da interface do usuário.

A infraestrutura comum inclui:

  • O mecanismo para armazenar controles em relação à janela
  • Elementos típicos de visualização de controle - quadro, plano de fundo, texto, ícone etc.
  • Transmissão para controle de eventos e padrões típicos de reação (pressionar / alternar)
  • Rastreando eventos do mouse e estado de foco
  • Retornos de chamada e geração de eventos secundários para informar sobre alterações de estado

Controle de foco


Para que o controle (botão, por exemplo) seja usado sem o mouse, são necessárias várias coisas.

  • A capacidade de selecioná-lo no teclado
  • Exibir esta seleção
  • Resposta do teclado
  • Detecção de perda de foco.

O último é o mais difícil.

De fato, o controle se concentra tanto no teclado quanto no mouse, e esta é a mesma entidade - se selecionarmos um campo de texto com o mouse, ele também responderá às teclas. Se, depois disso, você pressionar TAB, o direito de trabalhar com o teclado irá para outro.

Uma tarefa separada é que alguns controles podem ser agrupados e seu status precisa ser atualizado de uma maneira relacionada. Pressionar um radiobathorn "aperta" seus vizinhos do grupo.

Mais uma vez, voltemos ao fato de que estamos escrevendo um sistema operacional persistente. Isso significa que o controle potencial pode ser armazenado na RAM persistente e sobreviver à reinicialização do kernel do sistema.

Ou seja, seria bom minimizar sua conexão com o núcleo. Cada ponteiro para a memória não persistente (na verdade, para o kernel) após uma reinicialização será inválido e precisará ser restaurado. Isso significa que esse ponteiro não tem o direito de armazenar informações sobre o status do controle. A posição do cursor como um número inteiro - sim. Um ponteiro para um buffer no kernel cuja posição é determinada pela posição do cursor não é. Bem ou sim, apenas um número inteiro ainda está lá e é mais importante. Na prática, isso não é muito oneroso, mas devemos lembrar.

Finalmente, a barra de tarefas. Isso ocorre na parte inferior (lateral, superior) da tela, onde o usuário cutuca se ele perdeu a janela.

Em princípio, isso já deve fazer parte do território do usuário, mas ... o kernel já está usando ativamente a GUI, portanto, por enquanto, essa parte também estará na parte inferior. Espero temporariamente.

Total


imagem

Na minha opinião, as tarefas que foram definidas nessa direção foram geralmente resolvidas. Obviamente, não há limite para a perfeição, mas, como me parece, a interface deu um passo tangível, de hacker para universal.

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


All Articles