PROGRAMAÇÃO XSTEP

Copyright (c) 2000 by Marcelo Samsoniuk Todos os programas exemplo deste artigo podem ser redistribuídos e modificados de acordo com a GNU Public Licence. Este artigo originalmente foi produzido em duas partes, a primeira abordando a programação X baseada diretamente na X library, a segunda baseado no toolkit XSTEP, no final do artigo existem links para a primeira parte do artigo, especifico sobre a X library.
  • Atenção: os exemplos com XSHM sairam anteriormente com um erro no código, onde havia XPutImage(), leia-se XShmPutImage(), essa falha mascara um acesso inválido ao descritor shminfo e provavelmente reduz a performance efetiva.

  • INTRODUÇÃO

    Quando o X foi criado, não existiam muitas opções, exceto o toolkit athenas. Grande parte das aplicações clássicas, como o xterm, xedit, xcalc, xclipboard, etc são baseados no athenas (também chamado de Xaw). Embora o athenas pareça ter caído em desuso nos últimos anos, existem variações com look-and-feel mais modernos deste toolkit, como o NeXTaw, Xaw3D e Xaw95.

    De fato, o princípio da década foi marcado por uma disputa entre o Open Look da Sun (cujas aplicações podem ser produzidas com o toolkit XView) e o CDE da OSF (cujas aplicações são produzidas com o toolkit Motif da OSF). Em um certo momento a Sun desistiu de lutar e acabou por adotar o CDE nas suas estações UNIX, tornando o CDE o look-and-feel padrão dos UNIX comerciais. Como se trata de um produto comercial, com custos relativamente altos, o CDE não emplacou na outra metade do mercado, representado pelas variações livres, como o Linux e FreeBSD, deixando o território aberto para toolkits com licenças mais flexíveis, como o QT, utilizado no projeto KDE (nitidamente um projeto tentando fornecer um CDE livre) e o GTK, toolkit utilizado no famoso GIMP e atualmente utilizado no projeto GNOME (com os mesmos objetivos do KDE). O GTK e o QT, em princípio, produzem aplicações com o look-and-feel do CDE.

    Buscando um look-and-feel diferente do tradicional CDE ou OpenLook, aparecem projetos interessantes, como o WINGs e o XSTEP. O WINGs é o toolkit utilizado pelo Window Maker e seus pequenos utilitários, permitindo, portanto, o desenvolvimento de pequenas aplicações com o look-and-feel do NeXTSTEP. Infelizmente a programação parece um pouco pesada, de modo que se programa diretamente na biblioteca X e o WINGs fornece alguma ajuda.

    O XSTEP também é um toolkit com o look-and-feel do NeXTSTEP, mas seu projeto é bem mais antigo e passou por uma série de modificações com o passar dos anos, tornando-se extremamente compacto, eficiente e fácil de programar. Muitos programadores que consideram o X muito complexo e ineficiente podem ter uma boa surpresa com o XSTEP. O fonte do toolkit também é uma valiosa referência para os programadores interessados em desenvolver seu próprio toolkit, pois o código é bem legível, compacto e eficiente. O XSTEP costuma mudar os conceitos das pessoas frente ao X, portanto vamos experimentar escrever algumas pequenas aplicações com ele.

    INSTALANDO O XSTEP

    Existem 4 versões principais do XSTEP. As versões 1.2 e 2.0 são versões antigas, baseadas em gadgets e podem ter alguma utilidade como referência para programadores interessados em escrever algum toolkit baseado em gadgets. A versão 4.0 está em desenvolvimento e restam as variações da versão 3.0, sendo a 3.5.1 a versão mais atualizada. As últimas versões do XSTEP estão disponíveis no endereço http://xstep.dhs.org ou ftp://sunsite.unc.edu/pub/Linux/libs/X/clibs (inclusive em CDs de mirrors da sunsite). Se a versão disponível no mirror da sunsite for um pouco antiga, não existem muitos problemas, salvo alguns recursos novos e algumas melhorias, os aplicativos compilados nas versões 3.x antigas tem compatibilidade binária com a biblioteca da última versão (atualmente 3.5.1). Aplicativos compilados com a versão 2.0 e 3.0 podem ser facilmente recompiladas com poucas modificações (existem exemplos de como fazer isto na versão 3.5.1).

    Com a última versão do XSTEP em mãos, podemos fazer a instalação facilmente:

    Uma vez instalado o XSTEP, é possível rodar alguma aplicação de exemplo que acompanhe o pacote para se certificar de que a biblioteca está corretamente instalada, no diretorio examples e contrib existem muitas aplicacoes de teste e exemplo.

    HELLO WORLD - PARTE 5: POWERED BY XSTEP

    Embora nem todo toolkit seja compacto como o XSTEP, é certo que algum toolkit simplifica razoávelmente o código, pelo simples motivo de que todo o tratamento de eventos e gerenciamento de objetos passa a ser feito automaticamente. Podemos comparar nossos exemplos anteriores com a simplicidade de um produzido com um toolkit:

    Muita atenção! no XSTEP os programas começam com xmain(), de modo que a função main() verdadeira está escondida dentro das entranhas do toolkit, o que facilita a programação consideravelmente.

    A compilação fica um pouco mais complicada, pois precisamos incluir os caminhos tanto da biblioteca e cabeçalho do X quanto do XSTEP, então fica mais fácil escrever um arquivo Makefile:

    Com este arquivo no mesmo diretório do programa exemplo (supondo que ele se chame helloworld.c), basta digitar make para que o programa seja compilado. Este Makefile pode servir para compilar quantos programas desejarmos, bastando incluir novos programas na variável APPLICATIONS: Quando executamos make, o programa verifica os programas neste variável e procura pelos respectivos arquivos .c, compilando eles se estiverem atualizados (uma característica boa do make é que ele só recompila os programas que forem efetivamente modificados, poupando tempo).

    Uma vez compilado, nosso programa, teremos uma janela com a frase hello world no centro, mas, diferentemente dos nossos outros exemplos, o tamanho da fonte é Helvetica 12, pois é a fonte padrão das widgets do XSTEP. Existem formas variadas de modificar a fonte, de acordo com a versão da XSTEP. As versões mais antigas tem uma sintaxe mais complicada, mas aceita por todas as versões:

    Um pouco confuso! As versões mais novas do XSTEP simplificam bastante isto, através de algumas melhorias no código de criação das widgets: Deste modo as widgets já são criadas com a nova fonte como padrão (a fonte padrão pode ser facilmente restabelecida com o código 'defaultfont=helvetica12m'). Claro que a sintaxe antiga pode ser utilizada na versão mais nova, mas a sintaxe nova não é suportada na versão mais antiga, então é altamente recomendável que se consiga uma versão nova, pois a sintaxe se torna muito mais simples. Assumindo que agora estamos realmente contentes por ter o mesmo resultado dos exemplos anteriores feitos diretamente na biblioteca X, podemos observar com mais detalhes nosso programa. Diferentemente da nossa primeira versão, esta versão permite que movimentemos a janela, maximizemos, redimensionemos e, para nossa surpresa, a frase hello world é automaticamente redesenhada na janela, comprovando que o toolkit deve estar manipulando eventos de exposição de área das janelas. Se consultarmos o fonte da biblioteca, perceberemos que ela chega a formar um cache de até 32 áreas retângulares expostas em uma mesma janela, o que permite enviar ao servidor X uma máscara complexa formada pela união das áreas expostas e redesenhar uma única vez somente as áreas expostas.

    O RETORNO DOS BOTÕES

    Em nosso último exemplo baseado diretamente na biblioteca X, criamos alguns botões para demonstrar como construir uma widget no X, então como se comportará a mesma widget no XSTEP ? vejamos:

    O código é muito mais simples e o resultado, certamente, muito mais bonito e eficiente, mas ainda não mostra todos os recursos possíveis que o toolkit pode fornecer. Nos exemplos com a biblioteca X fizemos algumas animações simples, vejamos como o toolkit se sai com isto: Este exemplo ilustra uma série de novidades do toolkit. Realmente é interessante como tão pouco código consegue funcionar tão bem: neste exemplo podemos até redimensionar a janela que as widgets são misteriosamente redimensionadas juntas! esta característica interessante é desempenhada automaticamente pelo toolkit, que intercepta eventos sobre a organização das janelas e propaga as modificações dos descritores de widgets, de modo que a aplicação sempre reflete o tamanho real da janela. No caso da widget grande e preta que roda nossa animação, o tamanho e posição dela é passada com uma combinação interessante de números: 8,8,-8,-40. É obvio que não existe tamanho negativo para uma widget, então os valores são interpretados como sendo posições relativas ao canto inferior direito da janela. No caso do botão, passamos os valores -8,-8,72,24. Fica claro que a posição negativa também é relativa ao canto inferior direito, de fato, quando redimensionamos a janela, o botão insiste em manter exatamente a mesma distância do canto inferior direito e, sem surpressas, essa distância corresponde justamente à posição especificada durante a criação do botão. Um bom exemplo deste tipo de combinação é dado pelo seguinte código exemplo: Este exemplo demonstra várias combinações de posição e tamanho com o objetivo de demonstrar o uso de coordenadas relativas aos cantos das janelas, de modo que estas janelas sejam facilmente redimensionáveis.

    Retornando ao nosso exemplo, observamos que a widget que contém nosso desenho é criada e, como um dos parâmetros dela, é passado o nome da função drawf(). Isto ocorre porque o XSTEP trabalha com call-backs, ou seja, os eventos gerados pelo servidor X ou pelo proprio XSTEP acionarão nossas funções (portanto xmain() não se trata de um loop de eventos, pelo contrário, apenas constrói as widgets e devolve o controle ao toolkit). No nosso exemplo, o XSTEP acionará continuamente a função drawf() para redesenhar aquela widget. Mas porque ? vamos experimentar uma pequena modificação no código:

    Observe que o topo do programa permanece o mesmo, apenas adicionamos duas funções novas, adicionamos dois botões que acionam estas funções e removemos a última linha de código que atribuia o valor 1 à variável animate do XSTEP.

    O programa inicia com a animação parada. Se clicarmos no botão start, a animação funciona como antes, clicando no botão stop ela volta a parar. Na verdade a variável animate permite controlar a forma com que o XSTEP recebe os eventos do sevidor X, de modo que com um valor diferente de zero, algumas widgets pensam que estão continuamente recebendo eventos do servidor X.

    Antes de passar para o próximo exemplo, alguns comentários sobre drawf(): trata-se de uma função baseada em chamadas básicas da biblioteca X, de modo a desenhar a frase hello world pela tela. Embora o código seja razoavelmente óbvio, baseado em incrementos de x e y alteráveis, conforme o objeto alcança as laterais da área de desenho, devemos observar que grande parte dos parâmetros são baseados em membros da variável t, que aponta para o descritor XSTEP do objeto em questão (uma caixa especialmente orientada para gráficos).

    CAIXAS DE DIÁLOGO

    Obviamente um toolkit oferece muito mais opções, de modo que podemos incrementar nossos programas com diversos outros tipos de widgets, como caixas de texto, menus popup, opções de checagem e caixas de diálogo, que não podem ser ignoradas pelo usuário (não podem ser fechadas nem minimizadas):

    O código que gera a caixa de diálogo da ilustração é apresentado na seqüência: Tratemos cada segmento do código de acordo com o grupo de widgets, iniciando pela janela principal, que simplesmente apresenta parte de outro exemplo já abordado, exibindo uma caixa preta com a frase animada hello world, que é continuamente desenhada pela função drawf(). Temos um botão Quit, que aciona uma função window_close() (cuja função, facilmente, deduzimos ser de fechar a janela onde este botão se encontra), e um botão Options que aciona a função optionsf(). Esta função, por sua vez, aciona uma caixa de diálogo.

    Uma caixa de diálogo, no XSTEP, não pode ser ignorada facilmente pelo usuário: ela se mantém sempre à frente da aplicação dona da caixa e, ao mesmo tempo, não pode ser minimizada nem fechada, forçando o usuário a acionar o botão OK. Nesta caixa, em particular, criamos uma série de widgets interessantes. Em primeiro lugar decoramos a caixa com alguns labels, dividindo a caixa em duas metades, com a superior exibindo um pequeno texto informativo sobre a utilidade da mesma (poderia ser uma mensagem de alerta também) e a parte inferior reservada para algumas widgets com opções.

    Seguindo a seqüência, criamos uma caixa popup, com diversas opções de cor (as opções são definidas no topo do código):

    Observe que, colorname é um buffer de texto, preenchido como default com o conteúdo Blue. Entre os parâmetros da caixa é fácil identificar que temos posição, tamanho, o buffer de texto, a lista de opções e, finalmente, um parâmetros um tanto quanto problemático: a quantidade de itens da caixa popup. Infelizmente, para manter compatibilidade binária com versões anteriores, um pequeno erro descoberto mais tarde no XSTEP exige que se multiplique o número de itens da lista de opções pela altura da caixa popup, resultando no enigmático 6*21.

    O funcionamento da caixa popup é simples: clicamos sobre ela e escolhemos uma das opções, que será copiada para o buffer de texto. Como podemos também observar, se outra widget modificar o buffer de texto, a caixa popup reflete a modificação instantâneamente, ou seja, ela tanto pode modificar o buffer de texto quando ser modificada por ele. Grande parte das widgets do XSTEP consegue detectar estas mudanças porque gera checksums das variáveis e buffers: quando um buffer é modificado, o checksum se altera e a widget atualiza a tela e recalcula o checksum, até que nova modificação no buffer seja efetuada. Para que a verificação ocorra, a cada modificação deve ser incrementado um contador de modificações denominado broadcast. Um detalhe interessante é que a variável animate habilita e desabilita a verificação continua de checksum (as nossas animações, para o XSTEP, são simplesmente uma continua solicitação de checksum de buffers).

    Outra widget nova é a caixa de texto:

    A caixa de texto permite a digitação de texto, de modo que passamos a posição da caixa, o tamanho, um buffer de digitação (observe que na medida que é digitado o texto, o buffer é modificado), o tamanho do buffer e uma função verificadora, que chamamos de updatef(). A função verificadora sempre é acionada quando saimos da caixa, ou seja, saltamos para outra ou pressionamos ENTER dentro da caixa, de modo que, no nosso exemplo, a função verificadora é chamada. No caso de updatef(), ela simplesmente verifica se o buffer de digitação não está vazio e copia para o outro buffer definitivo, do contrário ela copia um valor padrão. O buffer de texto definitivo, no exemplo, é o mesmo que o buffer de texto da caixa popup, de modo que digitar algo na nossa caixa de texto se reflete na caixa popup.

    Finalmente, a ultima widget interessante é uma caixa de verificação ou checagem:

    Seu funcionamento é simples: quando acionada, o buffer de texto contém o nome da caixa. Quando desacionada, o buffer é apagado. Tanto a variável turbo da caixa de verificação quanto a variável colorname da caixa de texto e popup são utilizadas como parâmetros da função drawf(), permitindo mudar a velocidade e a cor de exibição da animação.

    Na caixa de texto podemos digitar alguns nomes de cores em inglês e a função getnamedcolor() tentará obter a cor na lista de cores do servidor X. Opcionalmente podemos digitar a cor codificada em hexadecimal, obedecendo a notação #RGB, #RRGGBB ou #RRRRGGGGBBBB, onde cada letra corresponde a digitos hexadecimais codificando numéricamente a cor desejada.

    CAIXAS DE DIÁLOGO AVANÇADAS

    A lista popup é uma boa opção para uma quantidade reduzida de escolhar, mas para uma grande quantidade se torna inviável (experimente criar um menu popup tão grande quanto a tela e você descobrirá porque :). A caixa de texto nos dá a opção de digitar o nome da cor desejada, mas é um tanto quanto pouco prático ficar experimentando nomes de cores até encontrar uma que seja aceita pelo servidor X. Uma opção seria exibir uma lista com todas as cores (embora este exemplo funcione em qualquer display, é necessário alertar que ele irá alocar em média 800 cores, portanto um display com 8 bits de cor não conseguirá exibir muitas delas), mas disposta em um tamanho físico compacto:

    Abaixo o respectivo código: É um código extenso, mas claro e de fácil compreensão. O código XSTEP da lista, de fato, se limita a pouco segmentos: Na lista definimos apenas a posição e tamanho, passamos um ponteiro para um inteiro que indica o item que está ativo na lista, um ponteiro para um inteiro que indica o tamanho total da lista e finalmente passamos um array de ponteiros, cada um apontando para uma string, que será um item da lista. O tamanho do array é definido estaticamente como tendo até 4096 itens, mas o número exato é indicado dinâmicamente pelo inteiro colormax. A lista é preenchida pelo seguinte segmento de código: Basicamente um loop de leitura baseado em fgets(), onde as linhas do arquivo lido são processados um a um no loop. Quando o arquivo chega ao fim, a função de leitura retorna zero e o loop se encerra. Dentro do loop é feito algum processamento para buscar os dados. Basicamente evitamos comentários, removemos o caracter de nova-linha presente no fim de cada linha e varremos a linha procurando por alguns campos delimitados por espaço ou tabulação. Encontrando o campo desejado, alocamos um item na lista colors e incrementamos o indicador de tamanho colormax.

    O acesso a itens da lista é muito simples (este código é apenas ilustrativo):

    Como a lista é delimitada por colormax, podemos utilizar: O resto do código, essencialmente é o mesmo. Modificamos a seleção de velocidade, de modo que ao invés um modo acelerado, temos um conjunto de widgets que nos permite dobrar a velocidade até 32 vezes, sendo que somente uma opção pode ser selecionada ao mesmo tempo. Outro código interessante é uma demonstração de como construir uma caixa de diálogo utilizando variáveis temporárias, de modo a não aplicar as modifições no momento das seleções. Trata-se somente de um pequeno truque com botões, mas certamente está entre os truques mais importantes do nosso exemplo, pois permite a criação de caixas de diálogo mais avançadas.

    Finalmente, um pequeno segmento de código que nos chama a atenção é o uso da função downbox(). Não se trata de uma widget, de fato esta função faz parte de um conjunto de utilitários encontrados dentro da biblioteca do XSTEP (lamentávelmente estas funções ficaram esquecidas e não se encontram no manual de referência publicado na versão 3.3 do XSTEP, mas estão oficialmente disponíveis como qualquer outra função e são utilizadas em muitos exemplos) e sua função é simplesmente desenhar uma caixa colorida com um aspecto tridimensional (também existe uma respectiva upbox()). Embora simples, estas funções são extremamente importantes no XSTEP.

    E se desejássemos uma lista com mais de uma coluna ao mesmo tempo ? a modificação é relativamente simples, embora tenhamos que utilizar outra widget. No caso do XSTEP, uma lista com multiplas colunas na verdade é uma macro que cria várias listas normais ligadas, de modo que se parece uma lista com multiplas colunas:

    Embora pareca razoável, existe uma grande falha nesta abordagem e é bem possível que isto seja melhorado em futuras versões do XSTEP, de modo que as listas com uma ou mais colunas sejam tratadas de forma semelhante. Vejamos as modificações para isto: A modificação, de fato, é bem pequena, e certamente são modificações fáceis de encontrar na forma que estamos apresentando (de certa forma, faz parte do exercício efetuar as modificações corretamente :). Quando compilado, notamos que a lista passa a apresentar também o código que indexa a cor no sistema de cores do X. Uma vantagem deste tipo de lista é que ela aceita nada menos do que três funções de call-back (enquanto que a outra lista aceita somente uma): a função de modificação, acionada quando mudamos de um item para outro, uma função de duplo-click (que neste exemplo atribuímos à função applyf(), de modo que um duplo-click aplica instantâneamente a cor desejada) e uma função acionada pelo botão direito do mouse.

    A finalidade de uma função acionada pelo botão direito do mouse sobre um item da lista é justamente o acionamento de um menu popup, possivelmente para escolhermos comandos sobre determinado item da lista ou coisas do genero. Podemos, portanto, criar um menu popup para inserir ou remover itens da nossa lista:

    Estas modificações ficam todas acima da função colorsf(), de modo que o resto do programa não precisa ser modificado e a disposição de algumas funções até nos facilitou o código. A criação de um menu popup é um pouco complexa. De fato o menu popup e a lista popup são a mesma widget, mas a lista popup tem uma rotina que decodifica a opção do usuário automaticamente, enquanto que o menu popup, ao menos no momento, exige que o usuário faça esta decodificação (isto também deve mudar em futuras versões do XSTEP). Decodificada a operação, executamos os respectivo código, como inserir ou deletar uma entrada nas listas (o código é bem simples e claro, provávelmente dispensa comentários).

    Abaixo o aspecto da caixa acionada para solicitar o nome da nova cor a ser inserida:

    CAIXAS DE DIÁLOGO COM GRUPOS

    No exemplo anterior, quando inserimos uma nova cor na lista, uma caixa de diálogo adicional era aberta. Podemos abrir quantas caixas desejarmos, mas uma abordagem melhor é criar uma unica caixa de diálogo e permitir ao usuário controlar o conteúdo desta caixa. Com poucas modificações podemos melhorar nosso exemplo para ter este tipo de funcionalidade, graças aos grupos de widgets do XSTEP. Os grupos, pelo ponto de vista do servidor X, nada mais são do que sub-janelas que contém outras sub-janelas, mas estas sub-janelas contidas não podem sair dos limites da sub-janela que as contém. É possível, então, controlar a posição de diversos grupos, de modo a se sobreporem uns sobre os outros, dando então o efeito de que estamos mudando o conteúdo de uma janela:

    Na prática só estamos trocando a posição de dois grupos, de modo que um sobrepõe o outro ao nosso comando. Vejamos as modificações necessárias no programa: A manipulação de grupo é simples. No nosso caso, criamos dois grupos bom group_begin() posicionados exatamente um em cima do outro, ambos com fundo cinza para que o grupo e a janela não sejam distinguiveis pelo usuário (cada grupo deve terminar com uma diretiva group_end()). Então, pelo botão More, podemos permutar entre os grupos utilizando a função XRaiseWindow(), que coloca uma janela sobre as outras. É necessário um certo cuidado com esta operação, pois se as áreas dos grupos fossem do tamanho de toda a janela, os botões que estão no rodapé da janela seriam sobrepostos pelos grupos durante um XRaiseWindow(), visto que os botões e grupos são sub-janelas como todas as outras widgets e podem ser tratadas da mesma forma.

    Como podemos ver, durante a execução, todas as widgets continuam funcionando perfeitamente como antes, mas agora elas estão utilizando somente uma única caixa de diálogo.

    MUITO ALÉM DE HELLO WORLD

    Como vimos até agora, o XSTEP disponibiliza uma série de recursos para o desenvolvimento de aplicações X razoávelmente compactas, rápidas e fáceis de escrever, o que deve atrair muitos programadores iniciantes interessados em resultados rápidos. Por outro lado, o XSTEP não é um toolkit muito complexo, no sentido que está intimamente relacionado com a biblioteca X e isto lhe permite explorar facilmente recursos do X que não estão originalmente no seu projeto.

    Esta facilidade de interação com a biblioteca X pode ser particularmente atraente para programadores avançados, de modo a explorar recursos mais avançados do X disponíveis somente pela biblioteca X e, ao mesmo tempo, contar com algumas widgets para melhorar seu trabalho (neste artigo não mostramos todas as widgets, pois já existe um guia de referência que faz exatamente isto, o XSTEP Reference Manual).

    Dois amplos campos de pesquisa para programadores mais avançados referem-se à geração de imagens no X com alta performance, particularmente para o projeto de jogos, simuladores, interação com cameras de vídeo ou outros dispositivos e exibição de grandes quantidades de dados. O próximo exemplo, uma versão modificada de uma aplicação de demonstração inicialmente distribuída com o XSTEP 3.2, compara a técnica que estamos utilizando nos nossos exemplos (desenhar diretamente nas janelas) com a extensão double buffer do X:

    Sua compilação é um pouco diferente: precisamos da biblioteca matemática e do suporte a extensões do X, de modo que no nosso Makefile, precisamos acrescentar, após a biblioteca -lX11 do X, os parâmetros -lXext -lm. Uma vez compilado, temos o seguinte resultado: Esta extensão double buffer, como percebemos, permite que os gráficos vetoriais sejam desenhandos em um buffer interno do servidor X e então trocados com o conteúdo da janela, de modo que não conseguimos ver o redesenho da tela e a aplicação aparenta uma imagem mais nitida e estável durante a animação. Embora seja uma técnica dispendiciosa (um buffer de uma janela 1024x768 e 16 bits de cor consome 768KB de memória no servidor X) e exija uma máquina de boa performance para mover a grande quantidade de memória envolvida (em nosso exemplo, a 512x384, 16 bits de cor e 30 frames por segundo, o servidor X movimenta nada menos do que 12MB/s de dados!), é uma técnica essencial para o desenvolvimento de aplicações que manipulem gráficos vetoriais 2D e 3D avançados.

    O próximo exemplo, por outro lado, é especialmente interessante para manipular gráficos não-vetoriais, como imagens geradas por cameras, jogos com texturização rica ou exibição de grande quantidade de dados não-vetoriais. É estranho observar que muitos bons programadores, quando se deparam com a arquitetura cliente-servidor do X, se sentem restritos pelo simples fato de não terem acesso a um ponteiro capaz de desenhar diretamente no frame-buffer da máquina. Claro que isto não é totalmente verdadeiro, de modo que o X dispõe de diversos métodos para transferir grandes massas de dados entre uma aplicação cliente e um servidor X, com boa performance mesmo através de uma conexão de rede. A técnica mais rápida, porém, é a extensão XSHM, que oferece ao programador justamente o tão sonhado ponteiro para o frame-buffer (sob certas normas, é claro).

    A extensão XSHM permite que uma aplicação rodando na mesma máquina do servidor X capture um buffer na própria memória do servidor X, através de memória partilhada via IPC, de modo que podemos endereçar diretamente os pixels através de ponteiros dentro deste buffer! Vejamos o exemplo:

    Neste exemplo alocamos um buffer no servidor X e conseguimos um ponteiro para trabalhar diretamente neste buffer (de fato, criamos três ponteiros, que são utilizados de acordo com a quantidade de bits de cor no display do usuário). A manipulação de pixels individuais é tão simples como: Portanto conseguimos criar imagens com alta performance. Quando a imagem está completa, simplesmente copiamos a imagem do buffer para a respectiva janela, com a função XShmPutImage(). Como não estamos desenhando diretamente sobre as janelas na tela, temos um efeito parecido com o proporcionado pela extensão double buffer, ou seja, escondemos o desenho. Uma vantagem, porém, sobre a extensão double buffer é que a função XShmPutImage() nos permite desenhar somente uma parte da imagem, se assim desejarmos.

    Neste exemplo somente desenhamos alguns pixels aleatóriamente na tela (um indicador no canto inferior esquerdo contabiliza a quantidade de frames no tempo, informando quantos frames por segundo estamos conseguindo exibir). Com algumas modificações podemos tornar este exemplo uma aplicação mais útil:

    O código extra nos permite entender melhor a utilidade de um ponteiro direto para um buffer no servidor X: neste nosso exemplo um gráfico é continuamente movido na tela, baseado em uma fórmula matemática que desenha uma parabolóide interada vista de cima (na prática, o que veremos serão diversos circulos com degrades, representando curvas de nível na imagem). O volume de cálculos, na máquina que gerou a imagem abaixo, é de, aproximadamente, mais de 1 milhão de pixels por segundo, gerando dinaminamicamente a imagem abaixo: A seleção dinâmica de profundidade permite desenhar os gráficos de forma parecida nas três profundidades disponíveis, de modo que este exemplo deve rodar bem com 8, 16 ou 32 bits de profundidade de cor, sempre exibindo o mesmo aspecto na imagem. Como a imagem é atualizada sob controle do toolkit, todas as widgets do toolkit continuariam perfeitamente funcionais, permitindo, por exemplo, efetuar ajustes de parâmetros na geração das imagens. As mentes mais imaginativas já devem ter percebido isto a algum tempo, de modo que não se faz necessário demonstrar isto neste artigo.

    CONCLUSÃO

    Com estes dois últimos exemplos, fechamos nosso artigo com chave de ouro, demontrando algumas das características avançadas do X para a manipulação de imagens (espero que isto sirva de incentivo para bons programadores criarem boas aplicações e, principalmente, jogos para o X :). Sobre o XSTEP, em particular, existe um bom manual de referência, oXSTEP Reference Manual , com demonstrações particulares sobre cada widget. Uma boa olhada nos fontes do XSTEP pode trazer algumas surpresas, principalmente sobre funções não documentadas, como downbox(), upbox() e getnamedcolor(). A aplicações exemplo também são uteis, principalmente no caso de widgets mais complexas, como a xyscroll_create() e mkpixmap(), que esperamos possam ser abordados com detalhes em futuros artigos.

    Uma boa referência sobre o funcionamento de um toolkit pode ser encontrado no artigo Programação X, que aborda programação basica com a X library, demonstrando inclusive algumas noções da construção de um toolkit.

    APÊNDICE A

    Todos os exemplos do artigo foram devidamente testados e estão disponíveis separadamente, em caso de dúvidas. Todos estes exemplos podem ser redistribuídos e modificados de acordo com a GNU Public Licence.

    Exemplos baseados no XSTEP: