Which Device is Really a Pen?!

Seriously, this makes my hand hurt like whoa.

[1]: http://abock.org/2007/05/23/surfing-the-tubes/

So, this is annoying. Ever since I figured out how to get extended data from a stylus (pressure, tilt, etc) and actually implemented it, Virtual Paper has crashed due to an IndexOutOfRangeException if I try to use the actual mouse on it. At first I thought this had something to do with a button press event happening when using the mouse and not happening when using the stylus, or something. Whatever it was, I was more excited about getting pressure data than fixing this little bug, which seemed straightforward at the time.

This morning I decided to solve the problem real quick before heading over to my part time job at the university. As I should have expected, the problem has turned out to not be "real quick" at all. Now that I've gotten around to disabling that damned javascript rich text editor thing on WordPress, I can post some appropriately formatted code. This code is what decides to do with motion notification. If the motion comes from an InputSource.Pen, it makes a black Cairo.Color using the Pressure axis for an alpha channel. If it's not from a pen but is from a Button1, then we just put down 1.0 for the alpha, which is totally opaque. And finally, if it's either an InputSource.Eraser or from Button3, we make a big ol' mark that's the color of the current paper.

if(ev.Device.Source == InputSource.Pen) {
    strokeContinue(axes[0], axes[1], new Cairo.Color(0.0,0.0,0.0,axes[2]));
} else if(mask == ModifierType.Button1Mask) {
    strokeContinue(axes[0], axes[1], new Cairo.Color(0.0,0.0,0.0,1.0));
} else if(ev.Device.Source == InputSource.Eraser ||
          mask == ModifierType.Button3Mask) {
    strokeContinue(axes[0], axes[1], paperColor);
} else {
    // Unknown Button
}

Turns out, the problem is on line two above. If you click on the Handwriting widget with the regular mouse, ev.Device.Source is, in fact, InputSource.Pen! WTF? What's causing the IndexOutOfRangeException is the fact that the regular mouse does not have an axis[2], even though it is an InputSource.Pen.

Here's my new-and-improved device configuration code that picks which devices I want to hear from, with a Console.WriteLine added to help me get to the bottom of this:

foreach(Device d in GdkWindow.Display.ListDevices()) {
    Console.WriteLine("Device: {0}tSource: {1}t" +
            "Axes: {2}", d.Name, d.Source, d.NumAxes);
    if(((d.Source == InputSource.Pen ||
         d.Source == InputSource.Eraser) &&
        d.NumAxes > 2) ||
       d.Source == InputSource.Mouse) {
        d.SetMode(InputMode.Screen);
    }
}

And here is the output:

Device: eraser          Source: Eraser  Axes: 6
Device: stylus          Source: Pen     Axes: 6
Device: cursor          Source: Cursor  Axes: 6
Device: Synaptics       Source: Pen     Axes: 2
Device: Core Pointer    Source: Mouse   Axes: 2

I added some spaces in there to make it look nicer. As you can see: Synaptics, my touchpad device, shows up as a Pen. So, the other changes in the above configuration code are there to make sure any Pen or Eraser has more than two axes, and accept a mouse regardless.

Now drawing with the mouse doesn't crash it... it just doesn't work!! Yep, the Core Pointer now refuses to send a Motion Notify event. I tried hooking up a USB mouse and found an even more puzzling bug; it doesn't show up at all. It doesn't add anything to the above list of devices (didn't figure it would), and more bizaarely, it does not send any motion notify or button events to the widget, even if all the conditionals are removed and every device is configured as screen relative. Am I looking in the wrong place here? I think this issue requires a better understanding of X, which I do not have much of. Any input would be appreciated.

For now, this code in the motion notify handler does a good enough job, but it still doesn't solve the problem of an external mouse, and it feels kind of hackish:

if(ev.Device.Source == InputSource.Pen) {
    if(ev.Device.NumAxes > 2) {
        strokeContinue(axes[0], axes[1], new Cairo.Color(0.0,0.0,0.0,axes[2]));
    } else {
        strokeContinue(axes[0], axes[1], new Cairo.Color(0.0,0.0,0.0,1.0));
    }
} else if(ev.Device.Source == InputSource.Eraser ||
          mask == ModifierType.Button3Mask) {
    strokeContinue(axes[0], axes[1], paperColor);
} else {
    // Unknown Button
}

One Response to “Which Device is Really a Pen?!”

  1. Igor Says:

    Very very good programmer... very very bad handwriter :)

Leave a Reply