1 pontos por GN⁺ 3 일 전 | Ainda não há comentários. | Compartilhar no WhatsApp
  • Surgiu um novo protocolo para terminais para resolver o problema antigo de ser preciso instalar fontes modificadas (como Nerd Font) para renderizar ícones personalizados em aplicações de terminal
  • O Glyph Protocol permite que aplicações registrem glifos vetoriais diretamente no terminal em tempo de execução e consultem se a renderização de um codepoint específico é possível
  • Os dados de glifo usam o formato glyf do TrueType, permitindo implementação sem novas dependências ao aproveitar o rasterizador que o terminal já possui
  • Os codepoints permitidos para registro são restritos à Unicode Private Use Area (PUA), bloqueando por princípio ataques de phishing e falsificação visual
  • A Rio terminal já está recebendo a primeira implementação, e códigos de exemplo para frameworks TUI importantes como Bubble Tea, Ratatui e Ink já foram publicados

Problema existente: dependência de fontes modificadas

  • Para que editores de terminal, prompts e TUIs exibam ícones corretamente, o usuário precisa instalar manualmente fontes modificadas como Nerd Font ou Powerline
  • Sem a fonte instalada, aparece tofu (□) no lugar do ícone, e essas fontes modificadas costumam ser pesadas, com 6–12 MB cada
    • JetBrainsMono Nerd Font Regular cerca de 7,8 MB, FiraCode Nerd Font Regular cerca de 10,4 MB, e o arquivo completo de símbolos cerca de 60 MB
  • Desenvolvedores de aplicações não têm como distribuir diretamente os glifos que desejam e acabam dependendo de que o usuário tenha a fonte, a versão e o mapeamento de codepoints corretos

Recursos principais do Glyph Protocol

  • Há suporte para duas operações centrais
    • Registro de glifos personalizados: a aplicação escolhe um codepoint na Unicode PUA e envia diretamente ao terminal o contorno vetorial para registrá-lo em tempo de execução
    • Consulta de codepoint: permite verificar se um codepoint específico é coberto pela fonte do sistema, por um registro feito na sessão, por ambos ou por nenhum
  • Se o usuário já tiver Nerd Font instalada, a aplicação pode usar a consulta para pular o envio do glifo; se não tiver, pode enviar o contorno diretamente e o ícone ainda assim será exibido corretamente

Estrutura do protocolo

Modo de transporte (Transport)

  • Usa APC (Application Program Command) em vez de OSC
  • APC foi projetado para comandos definidos por aplicações, e terminais que não o implementam podem ignorar com segurança a sequência
  • OSC compartilha um namespace global usando um único inteiro decimal como identificador de comando, o que traz risco de conflito; APC não tem esse problema porque possui sua própria estrutura de identificação

Identificador (Identifier)

  • Todas as mensagens do Glyph Protocol recebem como prefixo o codepoint 25a1 (U+25A1, WHITE SQUARE)
    • Esse caractere é o símbolo padrão de tofu que o terminal desenha quando não há glifo
  • Formato de framing: ESC _ 25a1 ; <verb> [ ; key=value ]* [ ; <payload> ] ESC \\
  • Existem 4 verbos: s (support), q (query), r (register), c (clear)

Support (s): verificar suporte do terminal

  • Serve para verificar quais formatos de payload e versões do protocolo o terminal suporta
  • Também é a forma padronizada de detectar a própria existência do Glyph Protocol
  • O fmt da resposta é um bitfield, em que cada bit representa um formato de payload
    • 1 = glyf: glifo simples TrueType, obrigatório na v1
    • 2 = colrv0: glifo colorido em camadas planas (OpenType COLR v0), adicionado na v1.2
    • 4 = colrv1: grafo completo de pintura com gradientes e transformações (OpenType COLR v1), adicionado na v1.2
  • Se houver resposta, o suporte ao protocolo foi confirmado; se houver timeout, não há suporte; se fmt=0, o protocolo existe, mas não há suporte a formatos de payload (definido por completude)

Query (q): consultar se um codepoint pode ser renderizado

  • Consulta se um codepoint específico pode ser renderizado e recebe resposta no valor status
    • 0 (free): nada é renderizado, aparece tofu
    • 1 (system): coberto pela fonte do sistema
    • 2 (glossary): coberto por registro na sessão
    • 3 (both): coberto por ambos; o registro sobrescreve a renderização da fonte do sistema
  • Se a TUI detectar que o ícone já existe no sistema, pode pular o registro; caso contrário, pode registrar um codepoint personalizado e fazer fallback elegante

Register (r): registrar glifo

  • A aplicação escolhe um codepoint PUA e o registra enviando um contorno glyf codificado em base64
  • Parâmetros principais
    • cp: codepoint de destino (hex), que deve obrigatoriamente estar em uma das 3 faixas Unicode PUA (U+E000U+F8FF, U+F0000U+FFFFD, U+100000U+10FFFD); fora dessas faixas, a resposta será reason=out_of_namespace
    • fmt: formato do payload; na v1, só glyf é definido e é o padrão, então geralmente pode ser omitido
    • upm: units per em, define o espaço de coordenadas do contorno; valor padrão 1000
  • Se um segundo r for enviado para o mesmo cp, ele sobrescreve o registro anterior
  • Em caso de erro (codepoint fora da PUA, payload inválido, glifo composto etc.), a resposta vem como status=<nonzero>; reason=<code>

Por que escolher o formato glyf

Por que vetor

  • Glifos não são fotos, então não têm resolução fixa: o mesmo ícone precisa ser renderizado tanto em uma TUI densa de 12 px quanto em uma tela HiDPI de 24 px
  • Glifos raster ficam presos a uma resolução específica e podem ficar borrados em HiDPI ou ilegíveis em células pequenas

Motivos concretos para escolher glyf

  • Todo terminal que renderiza texto já traz linkado um rasterizador glyf (FreeType, swash, ttf-parser, fontdue, allsorts etc.)
  • Adotar o Glyph Protocol não adiciona nenhuma dependência nova ao lado do terminal
  • Adotar SVG exigiria trazer resvg ou escrever um novo parser de XML+path
  • O tamanho no wire também é menor: ícones comuns ficam entre 150 e 400 bytes de dados glyf, cerca de 2 a 3 vezes menores que SVG equivalente, mesmo contando o overhead de base64
    • Ao registrar 50 ícones, a diferença é de cerca de 13 KB contra 35 KB, algo perceptível em pipes do tmux ou links SSH móveis

Explicação rápida de glyf

  • Um registro glyf armazena o glifo como um conjunto de contornos fechados (contours)
  • Cada ponto tem 1 bit de metadado: on-curve ou off-curve
    • dois pontos on-curve consecutivos → linha reta
    • um ponto off-curve entre dois on-curve → curva Bézier quadrática
    • dois pontos off-curve consecutivos → existe um ponto on-curve implícito entre eles (truque de compressão)
  • As coordenadas ficam em posições inteiras dentro do quadrado EM; com upm=1000, (500, 900) significa metade da largura e 90% da altura
  • Um triângulo fechado ocupa cerca de 30 bytes; um ícone com 30 pontos, cerca de 200 bytes

Subconjunto glyf definido pelo protocolo

  • Só glifos simples são permitidos: glifos compostos, referências a outros glifos e contexto em nível de fonte não são aceitos
  • Usa a codificação padrão de flags definida na especificação OpenType
  • Sem instruções de hinting: hinting pressupõe um conjunto completo de valores de controle da fonte, o que não existe aqui
  • O espaço de coordenadas é definido por upm, com padrão 1000, podendo ser sobrescrito por registro

Cor, escalonamento e autoria

  • Contornos glyf não carregam informação de cor e são renderizados com a cor atual de primeiro plano, igual ao caso de herança da Nerd Font
  • Glifos coloridos são suportados em formatos de payload separados: fmt=colrv0 e fmt=colrv1
  • O valor upm define o espaço de coordenadas do glifo, e o terminal mapeia isso para a célula na renderização → não é preciso registrar novamente ao mudar o tamanho da fonte
  • A maioria dos desenvolvedores não vai escrever bytes glyf manualmente, e sim converter a partir de SVG em build time: é possível usar as interfaces ttx/pens do fonttools, e o helper svg2glyf deve ser distribuído junto com a implementação de referência do Rio

Vida útil e capacidade

  • Cada sessão de terminal mantém um glossary com no máximo 1024 registros simultâneos, indexados por codepoints dentro das 3 faixas PUA
  • Os registros permanecem válidos durante toda a sessão
  • Ao registrar o 1025º glifo, o registro mais antigo é expulso em ordem FIFO → não existe erro de “glossary full”
  • Aplicações que não podem tolerar expulsão silenciosa devem consultar o codepoint antes de exibi-lo

Exemplo real: registrar um ícone em uma PUA vazia

  • Há um exemplo do pipeline completo para registrar um contorno estilizado em U+100000 (o primeiro codepoint da Supplementary PUA-B)
  • Usa fontTools como conversor de SVG para glyf
  • Após desenhar o contorno com TTGlyphPen, ele é codificado em base64, enviado em uma sequência APC e depois o codepoint correspondente é exibido
  • Um payload glyf de um ícone comum de 20 pontos tem cerca de 150 bytes; com encapsulamento APC e base64, fica em torno de 250 bytes
  • Para desenvolvedores com assets em SVG, está previsto um helper svg2glyf → registro concluído em 2 linhas

Opção para registro em massa: reply=

  • Por padrão, o terminal envia uma resposta ACK para todo r, mas em hooks de inicialização que registram 100 glifos isso causa o problema de 100 ACKs enfileirados saindo pelo PTY e aparecendo como lixo no shell
  • Há 3 níveis de controle
    • reply=1 (padrão): responde em sucesso e falha; indicado para registro interativo individual
    • reply=2: responde apenas em caso de falha; sucesso é silencioso; usado quando se quer detectar só erros em registro em massa
    • reply=0: sem resposta alguma; fire-and-forget; útil quando não existe um processo para ler a resposta, como em hooks de inicialização
  • Valores desconhecidos fazem fallback automático para reply=1, preservando compatibilidade retroativa em futuras extensões

Clear (c): remover registro

  • Pode ser usado para restaurar o padrão do terminal ao fechar o editor, trocar o tema da TUI ou depurar
  • Remoção de um único slot: especificar um codepoint com o parâmetro cp
  • Limpar todo o glossary: omitir cp
  • Limpar um slot vazio não gera erro, é um no-op, com resposta status=0
  • cp precisa estar dentro da faixa PUA; fora dela, retorna reason=out_of_namespace

Recursos deliberadamente fora da v1

  • Não é possível registrar codepoints fora da PUA: a v1 é limitada às 3 faixas Unicode PUA
  • Sem ligaturas: o registro vale apenas para um único codepoint; substituição por sequência de teclas está fora do escopo da v1, e ligaturas de programação (->) já são tratadas por fontes OpenType
  • Sem persistência entre sessões: os glifos precisam ser enviados novamente a cada execução, evitando transformar o terminal em um cache de fontes
  • Sem compartilhamento entre aplicações: cada sessão de terminal possui seu próprio glossary, sem IPC nem daemon
  • Sem glifos coloridos no payload glyf da v1: a renderização é feita na cor de primeiro plano; cor fica separada em colrv0/colrv1 na v1.2
  • Esses recursos podem ser adicionados depois, se necessário, mas foram excluídos de propósito porque depois de adicionados não é fácil removê-los

Base de segurança da restrição à PUA

  • A restrição à PUA não é uma escolha estética de API, e sim uma propriedade que torna seguro ativar o protocolo por padrão
  • Se fosse permitido registrar codepoints arbitrários: seria possível registrar um glifo com forma de o em U+0061 (a) e fazer bad.com parecer bod.com
    • O buffer de células ainda conteria bad.com, então copiar e colar produziria os bytes corretos, mas o que o usuário lê seria falso
    • Isso criaria um primitivo de phishing para qualquer programa de terminal, com efeito persistente inclusive sobre programas executados depois na mesma sessão
  • Ao restringir à PUA, esse tipo de ataque se torna mecanicamente impossível: usuários não digitam codepoints PUA, e nomes de arquivo, URLs, comandos, nomes de variáveis e logs não contêm codepoints PUA
  • O protocolo reforça no nível da implementação o modelo de confiança estabelecido por convenção na Nerd Font: glifos personalizados só existem em faixas reservadas e não podem se sobrepor a texto real
  • Propriedades extras de segurança
    • O buffer de células é a autoridade: seleção, cópia, busca, detecção de hyperlink, histórico do shell etc. devem retornar os codepoints que a aplicação realmente imprimiu, sem permitir armadilhas de “o que você vê não é o que copia”
    • Isolamento por sessão: duas abas podem registrar ícones diferentes para U+E0A0 de forma independente, e o registro de uma aba não afeta a renderização na outra

Comparação com abordagens existentes

Kitty Image Protocol (KIP) + Unicode Placeholders

  • Dá para aproximar o Glyph Protocol com placeholders Unicode do KIP, mas a integração é complicada e só Kitty, Ghostty e Rio implementam esse tipo de placeholder
  • KIP é um protocolo de imagem, e glifos não são imagens
    • Custo por uso: um glifo reutilizado 200 vezes na tela (bordas de tabela, marcadores de lista etc.) exige 200 referências de imagem, com custo de layout e composição. Com o Glyph Protocol, depois de registrar o codepoint, ele é renderizado na velocidade de fonte
    • Sem resolução nativa: contornos glyf não têm tamanho em pixels, então acompanham automaticamente mudanças no tamanho da fonte. KIP envia bitmaps em um tamanho específico, então ao mudar a fonte a imagem fica borrada ou precisa ser reenviada, e o protocolo nem tem como detectar essa mudança
    • Herança da cor de primeiro plano: um contorno monocromático glyf é renderizado com a cor atual da célula, então o tema se aplica automaticamente. Imagens têm seus próprios pixels e não participam da coloração do texto

DEC DECDLD / DRCS

  • O VT220 introduziu em 1983 os Dynamically Redefinable Character Sets, que se parecem conceitualmente com o Glyph Protocol
  • Há dois problemas centrais
    • Abordagem bitmap: o terminal faz upload de uma grade de pixels ajustada ao tamanho atual da célula, então ao mudar o tamanho da fonte, usar HiDPI ou trocar para um monitor 4K, os pixels em bloco são ampliados ou reduzidos. Era uma solução da época dos CRTs fixos de 10×20 e não combina com a variedade moderna de tamanhos de célula
    • Sem limitação de namespace: o DECDLD pode sobrescrever conjuntos de caracteres mapeáveis para a faixa GL (onde ficam a, b, c etc.), então um programa não confiável poderia redefinir a renderização de a → esse é o principal motivo pelo qual terminais modernos evitam habilitar DECDLD

Estado da implementação no terminal Rio

  • O Glyph Protocol já pode ser usado na branch main do terminal Rio, com landing oficial previsto para maio → primeira implementação
  • A especificação completa será publicada junto com o release, incluindo código de exemplo para registro de glifos e consulta ao terminal
  • Exemplos funcionais podem ser vistos no repositório raphamorim/glyph-protocol-examples, incluindo integrações de exemplo para Bubble Tea, Ratatui e Ink
  • O protocolo ainda pode ser atualizado, e o formato das mensagens, as respostas a consultas e alguns edge cases podem mudar à medida que mais aplicações e terminais participarem → por enquanto, trate compilações atuais como alvo móvel e fixe a versão implementada
  • Há expectativa de adoção por outros emuladores de terminal, e o benefício para o ecossistema como um todo é grande, enquanto o escopo de implementação foi mantido intencionalmente pequeno

Perguntas em aberto para a comunidade

  • A notificação de mudança do tamanho da fonte deveria fazer parte do escopo do protocolo?: o Glyph Protocol em si evita esse problema porque os contornos são independentes de resolução, mas TUIs que combinam imagem e glifo não têm como saber de mudanças nas métricas da célula sem polling → discute-se se um aviso como resize ou metrics-changed pertence ao escopo ou está além dele
  • Existe uma forma responsável de permitir registros fora da PUA?: a regra de PUA-only garante segurança por padrão, mas bloqueia casos como IMEs CJK que queiram enviar glifos para hanzi não cobertos ou ferramentas linguísticas que desejem sobrescrever glifos → pede-se opinião sobre formas de abrir esses casos sem reintroduzir phishing, como opt-in explícito do usuário, recursos assinados ou flags de origem confiável

Ainda não há comentários.

Ainda não há comentários.