Jump to content

Verificar e configurar os Locks


thoga31
 Share

Recommended Posts

Boas, camaradas!

Ando a investigar nos tempos livres algo que parece absurdamente simples mas que, em Pascal, não me parece ser assim tão fácil.

Decidi aceitar o "desafio" de um livro meu de VB.NET... mas decidi implementá-lo em Pascal, claro! 🙂

A ideia é ter um programa que, de cada vez que um Lock é alterado, é informada a alteração.

Exemplo de output:

  Caps [On]
Scroll [Off]
   Num [Off]

Ora, numa Windows Application em VB.NET é astronomicamente simples... Uma RichTextBox onde é feito o output sequencial, um Timer com a propriedade Interval a 100, e um procedimento que verifica o estado dos Locks e indica se houve alteração.

Agora em Pascal a cena muda de figura...

Encontrei neste um código que exemplifica com o Caps Lock, do qual eu, confesso, percebi pouco. 🙂

A minha questão é: como posso verificar e configurar os Locks? Há outros modos sem ser pela leitura de bytes do Registry? Não há métodos que nos possam auxiliar? E em Delphi, já agora?

Espero o vosso contributo nesta questão a fim de poder ter um snippet, ou mesmo um how-to, para colocar na Wiki. 😄

Cumprimentos,

thoga31.

Knowledge is free!

Link to comment
Share on other sites

Em tURBO Pascal é por registry...

Em Delphi ou FreePascal, é tão simples como isto:

case GetKeyState(VK_CAPITAL) of
   0: // Caps Lock desligado
   1: // Caps Lock ligado
end;

Outros códigos de teclas úteis:

VK_NUMLOCK: Num Lock

VK_SCROLL: Scroll Lock

VK_LSHIFT: Left Shift

VK_RSHIFT: Right Shift

VK_LCONTROL: Left Control

VK_HCONTROL: Right Control

VK_LMENU: Left Alt

VK_RMENU: Right Alt

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Link to comment
Share on other sites

Actualmente, para encontrares funções do FreePascal, é mais fácil procurares as de Delphi. 🙂

O Delphi evoluiu do Pascal, mas o Pascal evolui também, baseado no Delphi.

É o típico caso do mestre que ensina ao pupilo tudo o que sabe. O pupilo entretanto aprende mais, e o mestre vai aprender com ele para poder evoluir também!

Para mudar o estado é mais "dificil" do que para os ler. Ainda assim, nada de muito complexo.

Precisamos simular o pressionar (e largar) da tecla, para que o processo seja exactamente o mesmo do que sermos nós a carregar.

procedure ToogleNumLock;
begin
   if (GetKeyState(VK_NUMLOCK) = 0) // Lê o estado actual
      then begin
                Keybd_Event(VK_NUMLOCK, 1, KEYEVENTF_EXTENDEDKEY or 0, 0) ;  // "Pressiona" a tecla
                Keybd_Event(VK_NUMLOCK, 1, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0) ; // "Larga" a tecla
           end
      else begin
                Keybd_Event(VK_NUMLOCK, 0, KEYEVENTF_EXTENDEDKEY or 0, 0) ;  // "Pressiona" a tecla
                Keybd_Event(VK_NUMLOCK, 0, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0) ; // "Larga" a tecla
           end;
end;

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Link to comment
Share on other sites

Eis a solução do exercício em Pascal! 🙂😄

Que acham?

{$MODE DELPHI}
PROGRAM LocksTest;
USES crt, windows;

TYPE // Permite a referência intuitiva ao Lock pretendido, ao invés das constantes reconhecidas pelo Pascal
     TKeySet = (CapsLock, NumLock, ScrollLock);

     // Guarda o antigo e o actual estado dos Locks
     TLockState = RECORD
                  Old : record  // Antigos estados
                        Caps, Num, Scroll : boolean;
                  end;
                  Now : record  // Novos estados
                        Caps, Num, Scroll : boolean;
                  end;
     END;

CONST // Converte um TKeySet num valor padrão reconhecido pelo Pascal
      TKey : array [TKeySet] of cardinal = (VK_CAPITAL, VK_NUMLOCK, VK_SCROLL);

VAR option : char;            // Verifica quando é premido ESC.
    LocksState : TLockState;  // Regista os estados dos Locks

PROCEDURE WriteAndRefreshLocksState(const OnLoad : boolean);
(* Actualiza os estados dos Locks e, caso algum tenha alterado, escreve o seu novo estado *)
// OnLoad: TRUE = início do programa: deve escrever todos os actuais estados. FALSE = no decorrer do programa.

   function GetLockState(const Lock : TKeySet) : boolean;
   (* Obtém o estado de um determinado Lock *)
   begin
        case GetKeyState(TKey[Lock]) of
             0 : result := false;
             1 : result := true;
        end;
   end;

begin
     // Caps Lock
     LocksState.Now.Caps := GetLockState(CapsLock);
     if (LocksState.Now.Caps <> LocksState.Old.Caps) or OnLoad then begin
        case LocksState.Now.Caps of
              true : writeln('Caps Lock [On]');
             false : writeln('Caps Lock [Off]');
        end;
        LocksState.Old.Caps := LocksState.Now.Caps;
     end;

     // Num Lock
     LocksState.Now.Num := GetLockState(NumLock);
     if (LocksState.Now.Num <> LocksState.Old.Num) or OnLoad then begin
        case LocksState.Now.Num of
              true : writeln('Num Lock [On]');
             false : writeln('Num Lock [Off]');
        end;
        LocksState.Old.Num := LocksState.Now.Num;
     end;

     // Scroll Lock
     LocksState.Now.Scroll := GetLockState(ScrollLock);
     if (LocksState.Now.Scroll <> LocksState.Old.Scroll) or OnLoad then begin
        case LocksState.Now.Scroll of
              true : writeln('Scroll Lock [On]');
             false : writeln('Scroll Lock [Off]');
        end;
        LocksState.Old.Scroll := LocksState.Now.Scroll;
     end;
end;


BEGIN  (* BLOCO PRINCIPAL *)
     writeln('INICIO:');
     WriteAndRefreshLocksState(true);  // Escreve estados iniciais

     writeln; writeln('ALTERACOES:');
     repeat
           // Actualiza estados num intervalo de 0,1 segundos.
           while not KeyPressed do begin
                 WriteAndRefreshLocksState(false);
                 delay(100);  // Para não sobrecarregar CPU - equivale à velocidade de reacção do ser humano.
           end;
           option := readkey;
     until (option = #27);  // Sai aquando ESC
END.

EDIT: na Wiki 🙂

Knowledge is free!

Link to comment
Share on other sites

😄

Deixa-me refazer-te aí uma função, recorrendo a typecasts:

function GetLockState(const Lock : TKeySet) : boolean;
   (* Obtém o estado de um determinado Lock *)
   begin
         Result:=Boolean(GetKeyState(TKey[Lock]));
   end;

E já agora, ainda que eu seja fã de arrays de constantes, bastava-te isto:

const
   CapsLock=VK_Capital;
   NumLock=VK_NumLock;
   ScrollLock=VK_ScrollLock;

Assim funcionam as originais, e as tuas, não enquanto valor do tipo TKeySet, mas como valores constantes.

Aliás, com o typecast e as constantes directas, nem vale a pena uma nova função... 🙂

if boolean(GetKeyState(CapsLock)) then // bla bla bla

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Link to comment
Share on other sites

Deixa-me refazer-te aí uma função, recorrendo a typecasts:

function GetLockState(const Lock : TKeySet) : boolean;
   (* Obtém o estado de um determinado Lock *)
   begin
         Result:=Boolean(GetKeyState(TKey[Lock]));
   end;

Oi? :o

E já agora, ainda que eu seja fã de arrays de constantes, bastava-te isto:

const
   CapsLock=VK_Capital;
   NumLock=VK_NumLock;
   ScrollLock=VK_ScrollLock;

Fiz daquela forma pois queria utilizar aquela capacidade, capacidade essa que só aprendi muito recentemente 😄

Mas de facto bastava dessa forma, é o meu método mais antigo. 🙂

Mas o snippet no fundo não está nada mal, pois não? :🙂

Knowledge is free!

Link to comment
Share on other sites

Mas o snippet no fundo não está nada mal, pois não? :🙂

Não, não está mal! 🙂

Fiz daquela forma pois queria utilizar aquela capacidade, capacidade essa que só aprendi muito recentemente 😄

hehehehe a sério? Onde? Onde?

Oi? :o

lol

hehehe Ao fazeres Boolean(x), sendo x um valor integer, estás a converter esse valor integer num valor boolean, segundo a fórmula:

0= False

Qualquer outro= True

Os typecasts são das coisas mai' lindas do pascal/delphi, não são? hehehe

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Link to comment
Share on other sites

hehehehe a sério? Onde? Onde?

Num qualquer post teu, relativamente recente 🙂

hehehe Ao fazeres Boolean(x), sendo x um valor integer, estás a converter esse valor integer num valor boolean, segundo a fórmula:

0= False

Qualquer outro= True

Os typecasts são das coisas mai' lindas do pascal/delphi, não são? hehehe

Olha, não sabia! São mes' lindos! 😄

Há mais tipos de dados que fazem typecasts? 🙂

Knowledge is free!

Link to comment
Share on other sites

em Delphi, os typecasts fazem parte da programação... Mal consegues "soltar gazes" sem fazer typecast. (imagem mai' linda e poetica esta, não?  🙂😄🙂 )

Quase todos os tipos dão para fazer typecast, pelo menos a partir de algum outro tipo.

Um muito usado, para tornar genérico um dado qualquer, é o TObject.

s:='Olá mundo';
obj:=TObject(s);

s:=String(Obj);
ShowMessage(s);

Com isto conseguimos inserir uma informação qualquer dentro das classes de dados, por exemplo:

Num combobox, cada linha da caixa pode possuir dois valores:

Uma String, e um TObject.

Com o typecast com TObjects podemos ter uma string (apresentada na caixa) e outro dado qualquer (escondida, mas associada à string).

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Link to comment
Share on other sites

Ocorreu-me agora...

Ora, numa Windows Application em VB.NET é astronomicamente simples... Uma RichTextBox onde é feito o output sequencial, um Timer com a propriedade Interval a 100, e um procedimento que verifica o estado dos Locks e indica se houve alteração.

É assim que se faz em VB.net?

My God!  😁

Agora em Pascal a cena muda de figura...

Ai pois muda...

DELPHI! DELPHI! DELPHI!  :smitten:

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Link to comment
Share on other sites

É assim que se faz em VB.net?

My God!  😁

Para uma Windows Application, sim. Em consola é super semelhante.

A base da verificação, para qualquer uma delas, é esta (e.g. caps lock):

If My.Computer.Keyboard.CapsLock Then
    MsgBox("Caps Lock ON")
Else
    MsgBox("Caps Lock OFF")
End If

DELPHI! DELPHI! DELPHI!  :smitten:

O Dia dos Namorados já lá vai... 😁

Knowledge is free!

Link to comment
Share on other sites

Ah, OK.

Estava a ficar preocupado com a necessidade de richedits e companhia só para ver o estado da tecla!  😁

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Link to comment
Share on other sites

Ah, OK.

Estava a ficar preocupado com a necessidade de richedits e companhia só para ver o estado da tecla!  😁

Relê bem o que tinha dito (eheheh 😁):

- RichTextBox para o output: numa Windows App o output tem de ocorrer de alguma forma, e uma MsgBox não é, com certeza, o melhor 😛

- Timer: há que verificar periodicamente o estado dos Locks, e numa Windows App, o Timer serve para isso mesmo: lança um evento Tick a cada Interval milissegundos - é a cada Tick que faz a verificação; 😉

- Um procedimento que verifica os Locks: aqui sim está a verificação... 3 conjuntos minúsculos como o que escrevi há pouco + umas adaptações para o output, e voilá! Très chique! 😄

Knowledge is free!

Link to comment
Share on other sites

hehehe

Então, deixa-me dizer-te que tens maneira melhor de fazer isso em VB.Net, tal como em Delphi (só não te sei é dizer como em VB.Net, mas que dá, eh pá, tem que dar!  😁)

O Windows funciona com mensagens, que são comunicadas entre o core e as aplicações e vice-versa.

É isto que torna possível usar os eventos do Delphi, C++, VB.Net e linguagens do mesmo estilo:

1. Algo acontece

2. O core disponibiliza uma mensagem informativa

3. A aplicação apanha a mensagem e despoleta o evento.

Não existem eventos directos para verificar os Locks, mas também, quem é que precisa da papa toda feita?

Se soubermos que vamos ter sempre activo um determinado componente, podemos usar os eventos OnKeyUp e OnKeyDown para detectar que teclas foram pressionadas, e nesse evento verificar o estado das teclas.

Caso não se saiba qual componente estará activo, podemos adicionar à aplicação um componente TApplicationEvents, e no seu evento OnMessage, e apanhar as mensagens WM_KEYDOWN ou WM_KEYUP, conforme o desejado...

Assim, não é preciso um timer que muito ou pouco, sempre consome recursos, e cá pra nós, não é tão "elegante"! 😁

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Link to comment
Share on other sites

Em VB.NET tens procedimentos que regem o comportamento a tomar consoante o evento ocorrido num determinado objecto.

Imagina o carregamento da Form1:

Private Sub Form1_Load(sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

MyBase refere-se ao Form1, a form onde estão contidos os objectos.

Existe o evento KeyDown, por exemplo, mas é necessário a form estar focada. Para a aplicação apanhar eventos do sistema já são necessárias outras cenas, as quais desconheço.

A forma mais fácil é com recurso a um Timer que obrigará a verificar os Locks de x em x milissegundos. Assim só tens de apanhar o avlor booleano do estado de cada Lock, e não tens de estar atento a nenhum evento. 😁

Private Sub RefreshLocks()
    ' Actualiza a informação dos Locks
End Sub

Private Sub Timer1_Tick( etc etc ) Handles Timer1.Tick
    RefreshLocks()
End Sub

E pronto, passada a pequena aula de VB.NET e voltando ao Pascal, já sabemos como sacar a informação dos Locks de uma forma cómoda e sem métodos demasiado avançados. 😁

Knowledge is free!

Link to comment
Share on other sites

Decidi refazer o meu programa. Está muito mais optimizado e intuitivo para o programador que o ler. 😁

Inovações que utiliza:

  • Cada Lock tem uma função que devolve directamente um Boolean;
  • Estas funções são calculadas por TypeCasting;
  • O programa não informa a alteração, simplesmente tem os 3 indicadores e diz "ON" e "OFF" conforme o estado;
  • Para indicar o estado a partir do Boolean, há uma função que faz a conversão Boolean>OnOff.

Espero que apreciem este pequenino snippet renovado. 😁

PROGRAM LocksTest;
USES crt, windows;

(* TIPOS *)
TYPE TRegLocksState = RECORD
                      Old : record
                            Caps, Num, Scroll : boolean;
                      end;
                      Now : record
                            Caps, Num, Scroll : boolean;
                      end;
     END;

(* VARIÁVEIS *)
VAR Locks : TRegLocksState;
    ExitControl : char;


FUNCTION CapsLock : boolean;
(* Verifica Caps Lock *)
begin
     CapsLock := boolean(GetKeyState(VK_CAPITAL));
end;

FUNCTION NumLock : boolean;
(* Verifica Num Lock *)
begin
     NumLock := boolean(GetKeyState(VK_NUMLOCK));
end;

FUNCTION ScrollLock : boolean;
(* Verifica Scroll Lock *)
begin
     ScrollLock := boolean(GetKeyState(VK_SCROLL));
end;


PROCEDURE RefreshLocks(const OnLoad : boolean);
(* Actualiza indicadores de Locks *)

   function Bool2OnOff(const b : boolean) : string;
   (* Converte o booleano do indicador num indicador ON/OFF *)
   begin
        case b of
             true  : Bool2Onoff := 'ON ';
             false : Bool2Onoff := 'OFF';
        end;
   end;

begin
     // Indicador Caps Lock
     Locks.Now.Caps := CapsLock;
     if (Locks.Now.Caps <> Locks.Old.Caps) or OnLoad then begin
        GotoXY(1,1);
        Write('  Caps Lock   ', Bool2OnOff(Locks.Now.Caps));
        Locks.Old.Caps := Locks.Now.Caps;
     end;

     // Indicador Num Lock
     Locks.Now.Num := NumLock;
     if (Locks.Now.Num <> Locks.Old.Num) or OnLoad then begin
        GotoXY(1,2);
        Write('   Num Lock   ', Bool2OnOff(Locks.Now.Num));
        Locks.Old.Num := Locks.Now.Num;
     end;

     // Indicador Scroll Lock
     Locks.Now.Scroll := ScrollLock;
     if (Locks.Now.Scroll <> Locks.Old.Scroll) or OnLoad then begin
        GotoXY(1,3);
        Write('Scroll Lock   ', Bool2OnOff(Locks.Now.Scroll));
        Locks.Old.Scroll := Locks.Now.Scroll;
     end;
end;


BEGIN  (* BLOCO PRINCIPAL *)
     RefreshLocks(true); // Mete indicadores: está a iniciar programa - parâmetro deve ser TRUE

     repeat
           while not KeyPressed do begin
                 RefreshLocks(false);  // Actualiza
                 Delay(100);
           end;

           ExitControl := ReadKey;
     until (ExitControl = #27);  // Sai no ESC
END.

Knowledge is free!

Link to comment
Share on other sites

Este é o momento certo para um array de constantes.  😁

Substituis isto:

function Bool2OnOff(const b : boolean) : string;
   (* Converte o booleano do indicador num indicador ON/OFF *)
   begin
        case b of
             true  : Bool2Onoff := 'ON ';
             false : Bool2Onoff := 'OFF';
        end;
   end;

por isto:

Const
   Bool2OnOff:Array[boolean] of string=('OFF','ON ');

E claro, substituir os parentesis curvos onde chamavas a função por parantesis rectos.

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.