Upcoming: Event-based G15 LCD library
I'm sure all of you have heard of Logitech's G15 gaming keyboard, yes the one with the built-in LCD. I've recently bought the first version (see picture) and obviously want to write applications that target the small 160x43 pixel screen. Logitech provides a native (C++) SDK with their drivers and someone was kind enough to descend into the creepy world of pointers and manual memory management for all .NET developers out there.
This managed wrapper, however, is an extremely low-level interface: In order to draw something onto the screen, you have to pass an array of bytes (more precisely: a pointer to its first element) that holds the bitmap to display. This is obviously not very practical and completely alienating for people who, up until now, lived in the comfortable world of Windows.Forms.
Not being able to find a more abstract library for the G15 LCD, I decided to write my own GUI library. The first task was to make rendering via System.Drawing possible. After having to find out that the only monochrome pixel format is indexed and can therefor not be targeted by a Graphics object, I resorted to using a 24BppRGBÂ bitmap for rendering instead. I then manually iterate over the pixels in that buffer, determining the brightness of the pixel and applying a threshold in order to get a monochrome copy into a byte array.
On top of that I am currently building a Widget/Control oriented display layer complete with event routing for the four soft buttons, Z-ordering and clipping for nested controls. Below you see the trivial implementation of the LcdLabel control:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Drawing;
- namespace Logitech.GamePanel
- {
- public class LcdLabel : LcdControl
- {
- public LcdLabel()
- {
- }
- private string _text;
- public string Text
- {
- get
- {
- return _text;
- }
- set
- {
- if (value == null)
- value = "";
- _text = value;
- Update();
- }
- }
- public StringFormat StringFormat { get; set; }
- protected override void OnPaintBackground(PaintEventArgs e)
- {
- e.Graphics.FillRectangle(back, ClientBounds);
- }
- protected override void OnPaint(PaintEventArgs e)
- {
- //Paints background
- base.OnPaint(e);
- e.Graphics.DrawString(_text, Font, fore,
- ClientBounds, StringFormat);
- }
- }
- }

One thing I am proud of, is my implementation of delayed and repeated button press events if a button is being hold down. The buttons use the same repeat delay as the Keyboard (configurable via the device driver/windows).
Repeated Pressed events are particularly useful for the sample application below, a little application that provides a counter that can be incremented, decremented and reset via soft buttons.
- using System;
- using System.Drawing;
- using System.Threading;
- using Logitech.GamePanel;
- namespace LcdSandbox
- {
- class CounterPage : LcdPage
- {
- const string counterFormat = "Count: {0:N0}";
- LcdLabel lTitle;
- LcdLabel lCounter;
- public int Count { get; set; }
- public CounterPage(LcdScreen scr)
- : base(scr)
- {
- #region Page initialization
- //Although the page itself does not draw any text,
- // the font is inherited by child controls
- //lTitle - A label that display a title
- {
- Text = "I'll count for you!"
- };
- Controls.Add(lTitle);
- //lCounter - A label that displays the current count
- {
- {
- Alignment = StringAlignment.Center,
- LineAlignment = StringAlignment.Center
- }
- };
- Controls.Add(lCounter);
- //Define soft button descriptions
- LcdControl BDesc;
- //Button 0 - Increment
- Button0.Controls.Add(BDesc);
- Button0.Pressed += (obj, e) =>
- {
- Count++;
- updateCounter();
- };
- //Button 1 - Decrement
- Button1.Controls.Add(BDesc);
- Button1.Pressed += (obj, e) =>
- {
- Count--;
- updateCounter();
- };
- //Button 2 - Reset
- Button2.Controls.Add(BDesc);
- Button2.Pressed += (obj, e) =>
- {
- Count = 0;
- updateCounter();
- };
- //Button 3 - Exit
- Button3.Controls.Add(BDesc);
- Button3.Pressed += (obj, e) =>
- {
- Screen.ExitMessageLoop();
- };
- #endregion
- Priority = ScreenPriority.Normal;
- updateCounter();
- }
- private void updateCounter()
- {
- lCounter.Text = String.Format(counterFormat, Count);
- Update();
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- {
- //You must not forget to open the screen
- // before asigning any pages.
- screen.Open("My LCD Sandbox");
- //Create a new instance of our counter page
- //Tell the screen to display our counter page
- screen.CurrentPage = page;
- //Temporarily set the priority to Alert
- page.TemporaryAlert(500);
- Console.WriteLine("Application running.");
- Console.WriteLine("See LCD for instructions");
- //The Run method is similar to Application.Run
- // from System.Windows.Forms. Until screen.Close
- // or screen.ExitMessageLoop is called, this
- // thread will handle events.
- screen.Run();
- }
- }
- }
- }
The library is in what others call 'Alpha' stage. I do plan to release it here once I'm done documenting the most commonly used methods or if someone is particularly interested and wants to start coding right now.

This sounds great! Can't wait to work with it, I've been meaning to do something similar I just haven't found the time.
Thanks, that's really motivating: