terça-feira, novembro 27, 2007

DQTimer - Um timer e cronômetro para Windows - Parte 2


Neste segundo post vou dar algumas explicações sobre o código do utilitário que eu descrevi na primeira parte.

C e WinAPI

Este tipo de utilitário é bem apropriado para ser desenvolvido em C usando a API do Windows, pois basicamente envolve o desenho simples na tela. Como ferramenta usei o velho e confiável Visual C++ 6.

Uma série (incompleta) sobre este tipo de programação pode ser vista aqui e aqui.

Os Estados do Programa

A variável modo indica o estado atual do programa:
  • ANDANDO: o relógio está andando.
  • PARADO: o relógio está parado e ainda não foi decidido se ele funcionará como timer ou cronômetro.
  • PAUSADO: o relógio está parado no meio de uma operação como timer ou cronômetro.
  • ALARME: o relógio está apresentando a indicação de final da contagem regressiva do timer.
Uma janela de Classe

No Windows, uma classe de janela é algo semelhante a uma classe em uma linguagem orientada a objeto: uma espécie de "forma" utilizada para criar as instâncias ou objetos propriamente ditos (no caso as janelas).

A definição de classe utilizada não tem nada de muito especial. O comportamento das janelas desta classe será governado pela Rotina de Janela que veremos adiante.

Na criação da janela são usados dois flags mais incomuns: WS_EX_TOPMOST deixa a janela por cima das demais e WS_EX_TOOLWINDOW cria uma janela sem moldura, sem barra de título e que não aparece na barra de tarefas.

A Rotina de Janela

A interface com operador no Windows funciona através de mensagens. Ocorrências como a movimentação do mouse e o pressionamento dos seus botões ou das teclas do teclado geram mensagens que são passadas para a Rotina de Janela.

No nosso caso, a Rotina de Janela trata as seguintes mensagens (as demais são passadas para DefWindowProc que faz o tratamento padrão do Windows):
  • WM_CREATE: gerada quando a janela está sendo criada, é usada para posicionar a janela e criar um timer de 0,5 segundo.
  • WM_TIMER: na medida do possível, o Windows irá gerar esta mensagem na frequência que solicitamos. Ela é usada para solicitar a atualização da tela.
  • WM_ERASEBKGND: gerada pelo Windows quando toda a janela tem que ser redesenhada, desenha a parte fixa da janela.
  • WM_PAINT: gerada pelo Windows para solicitar a atualização da janela.
  • WM_NCHITTEST: uma mensagem que normalmente é tratada por DefWindowProc, é gerada pelo Windows para identificar a região da janela em que o cursor do mouse está posicionado. No nosso caso, usamos esta mensagem para definir a nossa barra de título e permitir arrastar a janela.
  • WM_LBUTTONUP: gerada quando o botão esquerdo mouse é solto. Usada para detectar os apertões nos botões do programa.
  • WM_DESTROY: gerada quando a janela é destruída, é usada para desligar o timer do Windows e gerar a mensagem WM_QUIT que encerra o programa principal.
A rotina DesenhaT

O desenho do fundo é feito escrevendo o bitmap do fundo por sobre a janela. No Windows isto é feito pela rotina BitBlt, com o auxilio de um "memory device context".

A rotina DesenhaM

Esta rotina é o coração da atualização da contagem. Uma vez que a geração de WM_TIMER não é garantida, a função timer() é usada para obter a data e hora atual em segundos.

Quando a contagem zero é atingida no timer, passa para o modo ALARME e dispara o sinal sonoro através da função PlaySound().

A atualização da tela em si é feita através de BitBlts entre um bitmap com os dígitos e indicações e a janela.

As demais Rotinas
  • Encerra trata o pressionamento do botão Close, destruindo a janela.
  • BotaoHora, BotaoMinuto e BotaoSegundo tratam o pressionamento dos respectivos botões, mudando o valor no mostrador.
  • BotaoClear limpa o mostrador e volta o programa para o estado PARADO.
  • BotaoSS: atualiza o estado conforme o estado atual.
Conclusão

Embora bastantes simples, o DQTimer já é um programa bastante útil. Os mais aventureiros, com experiência em programação Windows, podem incluir funções mais avançadas, como permitir escolher um arquivo WAV para o sinal de alarme.

Nenhum comentário: