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.Logitech G15 (first version)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()
    {
      StringFormat = new StringFormat()
      Size = new Size(LcdScreen.Width, DefaultFont.Height);
    }

    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)
    {
      using (var back = new SolidBrush(BackgroundColor))
        e.Graphics.FillRectangle(back, ClientBounds);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
      //Paints background
      base.OnPaint(e);

      using (var fore = new SolidBrush(ForegroundColor))
        e.Graphics.DrawString(_text, Font, fore,
            ClientBounds, StringFormat);
    }
  }
}

G15 Counter in the LCD emulatorOne 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
      Font = new Font("Calibri", 12, GraphicsUnit.Pixel);

      //lTitle - A label that display a title
      lTitle = new LcdLabel()
      {
        Location = new Point(0, 2),
        Text = "I'll count for you!"
      };
      Controls.Add(lTitle);

      //lCounter - A label that displays the current count
      lCounter = new LcdLabel()
      {
        Location = new Point(0, 20),
        StringFormat = new StringFormat()
        {
          Alignment = StringAlignment.Center,
          LineAlignment = StringAlignment.Center
        }
      };
      Controls.Add(lCounter);

      //Define soft button descriptions
      LcdControl BDesc;

      //Button 0 - Increment
      BDesc = new LcdImage(Icons.UpArrow);
      Button0.Controls.Add(BDesc);
      Button0.Pressed += (obj, e) =>
      {
        Count++;
        updateCounter();
      };

      //Button 1 - Decrement
      BDesc = new LcdImage(Icons.DownArrow);
      Button1.Controls.Add(BDesc);
      Button1.Pressed += (obj, e) =>
      {
        Count--;
        updateCounter();
      };

      //Button 2 - Reset
      BDesc = new LcdImage(Icons.First);
      Button2.Controls.Add(BDesc);
      Button2.Pressed += (obj, e) =>
      {
        Count = 0;
        updateCounter();
      };

      //Button 3 - Exit
      var exitSignal = new AutoResetEvent(false);
      BDesc = new LcdImage(Icons.Delete);
      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)
    {
      using (var screen = new LcdScreen())
      {
        //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       
        var page = new CounterPage(screen);

        //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.

2 Responses to “Upcoming: Event-based G15 LCD library”

  1. 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.

  2. Thanks, that’s really motivating: :-)

Discussion Area - Leave a Comment