Debido a un accidente navideño con el teclado del ordenador, que incluyó el agravante de la sidra ecológica, tenemos desde hace poco uno nuevo, inalámbrico, que con estas ideas brillantes para abaratar costes, resulta que no tiene LEDs indicadores del estado de las teclas de bloqueo de mayúsculas, numérico y de desplazamiento. A resultas de esto, me ha picado el gusanillo de la programación rápida, y he creado una aplicacioncita en .NET que se encarga de controlar el estado de esas teclas e informa al usuario mediante un icono de notificación en la barra de sistema.
La verdad es que es una aplicación bastante simple, pero ya que hacía tiempo que no subía nada de programación al blog, me ha parecido interesante dejarla aquí por si interesa a alguien y, de paso para recabar la opinión de los incondicionales ;) . Su nombre es KeyLed.
También incluyo la parte relevante del código, que no es demasiado extenso: Se trata de un WinForm que muestra el estado de las teclas en varios checkboxes, y que al ser minimizada queda en el systray. En todo momento, muestra un bocadillo con el estado de las teclas de bloqueo, mediante el uso de un temporizador que va comprobándolo cada cierto tiempo (500 milisegundos). He probado utilizando un delegado para el evento Idle de la aplicación, pero no se refresca tan a menudo como debiera, de ahí el uso final del temporizador.
[Csharp]
{
public partial class KeyLed : Form
{
const int Interval = 500;
public KeyLed()
{
InitializeComponent();
KeyLedTimer.Interval = Interval;
KeyLedTimer.Enabled = true;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
public static extern short GetKeyState(int keyCode);
private void KeyNotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
//Hacemos visible el formulario
this.Show();
this.WindowState = FormWindowState.Normal;
//Ocultamos el icono de la bandeja de sistema
KeyNotifyIcon.Visible = false;
}
private void KeyLed_Resize(object sender, EventArgs e)
{
//Si el estado actual de la ventana es "minimizado"...
if (this.WindowState == FormWindowState.Minimized)
{
//Ocultamos el formulario
this.Hide();
//this.Visible = false;
//Hacemos visible el icono de la bandeja del sistema
KeyNotifyIcon.Visible = true;
}
}
private void KeyLedTimer_Tick(object sender, EventArgs e)
{
this.CapsLockCheckBox.Checked = (((ushort)GetKeyState(0x14)) & 0xffff) != 0;
this.NumLockCheckBox.Checked = (((ushort)GetKeyState(0x90)) & 0xffff) != 0;
this.ScrollLockCheckBox.Checked = (((ushort)GetKeyState(0x91)) & 0xffff) != 0;
KeyNotifyIcon.ShowBalloonTip(Interval, "", _numLock + _capsLock + _scrollLock, ToolTipIcon.Info);
}
}
[/CSharp]
Se me ocurren diversas mejoras, por ejemplo, que muestre la información de los leds cambiando el icono asociado al objeto NotifyIcon, de hecho funciona sin problema. Pero crear ocho iconos para esto me parece un pasote. Como con System.Drawing no he tenido que pelearme demasiado, a ver si existe una opción más eficiente que la que os presento. A partir del icono inicial, es posible usar el método ToBitmap() de la clase Icon para obtener un BMP en memoria sobre el que trabajar (por ejemplo, cambiando el color de un determinado pixel con SetPixel. Sin embargo, me parece poco adecuado tratar dinámicamente el icono, porque posiblemente sea mayor el tiempo de volver a dibujar el estado de los leds que cargar un icono distinto. ¿Alguna opinión al respecto? ¿Monto ambos sistemas y le hacemos pasar unos pocos benchmarks? ¿Merece la pena, realmente, todo esto? Jajaja.
Un saludo.
Actualización:
Acabo de darme cuenta, al encender el altavoz, del molesto efecto de "pop, pop" en el refresco del estado, al volver a pintar el bocadillo. Así las cosas, me da que no hay más opción que diseñarme el icono adecuado y actualizarlo dinámicamente. Así las cosas, vuelvo a la pregunta que planteaba al final de la entrada: ¿lo "repinto" en memoria, o cargo uno de una colección de iconos previamente almacenados?