Search code examples
wpfopenglevent-handlinginteroptouchscreen

OpenTK makes touching button raise click event differently than mouse


What I am trying to do: I am currently hosting an OpenTK glControl (through a WindowsFormsHost) in my WPF application. The application has many buttons, but we will focus on the pause and play buttons. I use VBO's and this is how I animate GL.DrawArrays(PrimitiveType.LineStrip, 0, frameCount );. It is drawing the range of vertices from 0 to framecount, so when I hit play, it just starts incrementing framecount, which starts animating. When just using a mouse, everything works perfectly.

The Problem: My app needs to work with a touch screen also (FWIW, when I say touch, WPF is seeing it as a stylus, not touch). When the app is NOT animating, touch works like it should, I don't handle touch events so touching buttons just raised the Click event. If we aren't animating, I don't have any problems. So when I use my mouse Click the play button, the UI still responds to my Mouse (clicks work, hovering changes colors as it should, etc), but then touches seemingly get ignored for some period of time. I have to touch a button 3 to 5 times before it does what it is supposed to (like pause). Lets go back and now we aren't animating, this time, if I TOUCH the play button to raise the Click event, it will start animating, but my touches now have the same problem of having to touch multiple times to raise Click event, and on top of that, the UI now does not respond to the mouse (the WindowsFormsHost still reacts to mouse events properly). Clicking on buttons or hovering over buttons doesn't do anything. It is not until I can get the animation to pause again that the UI starts responding to Mouse again.

What I have tried and what (I think) I know: When the UI stops responding to mouse input and touch input is messsed up, if I use MOUSE or TOUCH to click anywhere outside of my app, it works as expected. This leads me to believe it is not a touch screen driver problem or anything. I also used Snoop to see what events were being raised (or not) to see the stylus and the mouse being captured and released and what has focus. I could not find an instance where one was not released. If anyone would like, I can post Snoop results showing differences between mouse and touch clicking. I tried putting a preview mouse down on the MainWindow, but after TOUCHING play, no Mouse clicks raise this event, and the first touch after touching the play button touches randomly will fire this event. I also tried hiding the WindowsFormsHost, and mouse and touch clicks all work like they should minus the glControl. Because I hide the WindowsFormsHost, the paint event never gets fired, leading me to believe there might a problem with the paint function. I am aware of this bug WPF Touch Bug, however I don't really think that this is my problem. Something tells me the interop is what is giving me problems but I am not sure.

My Code:

    private void playFwdFunc()
    {
        //disable undrawing and enable drawing
        undraw = false;
        draw = true;

        //unpause animation
        paused = false;
        //enable/disable appropriate buttons
        pauseBtn.IsEnabled = true;
        stepBackBtn.IsEnabled = false;
        stepFwdBtn.IsEnabled = false;
        clearStart.IsEnabled = true;
        clearCurrent.IsEnabled = true;
        //refresh control
        glControl1.Invalidate();
    }


    private void playFwdClick(object sender, RoutedEventArgs e)
    {
        playFwdFunc();

    }

In my glPaint event:

  if (frameCount < vertices.Length && !paused)
            {
                //draw more vertices
                if (draw)
                {
                    if (0 < (int)(vertices.Length / (25000 / speedTrack.Value)))
                        frameCount += (int)(vertices.Length / (25000 / speedTrack.Value));
                    else
                        frameCount++;
                }


                //draw less vertices
                else if (undraw)
                {
                    if (0 < (int)(vertices.Length / (25000 / speedTrack.Value)))
                        frameCount -= (int)(vertices.Length / (25000 / speedTrack.Value));
                    else
                        frameCount--;
                }


                //make sure we dont have a negative framecount (null pointer)
                if (frameCount < 0)
                {
                    paused = true;
                    frameCount = 0;
                 //   manageCodeBox();

                }
                //make sure we dont exceed # of vertices (null pointer)
                else if (frameCount > vertices.Length)
                {
                    frameCount = vertices.Length;
                    paused = true;
                 //   manageCodeBox();
                }

            }

My Question: Why is clicking a button with mouse making the UI react differently than touching a button, even though they raise the same event? Any input is appreciated.


Solution

  • So I found what was causing the behavior, in my paint function I was constantly raising an event that had a glControl1.Invalidate().

    My Best Guess: My only real guess is that certain events like touches which occur on the stylus thread some how gets into a live-lock with the UI thread when calling glControl1.Invalidate(). I think that that is why some touches got accepted, but the UI thread never reached mouse event listeners. As I said: best guess.

    My Workaround: I originally made a DispatcherTimer to call glControl1.Invalidate(). Mouse events worked fine, but because touches got promoted to mouse clicks, there was a delay unless I handled the touch event (which I did not want to do for each button on my UI). My next solution seems to be working so far.

    In my windows constructor: CompositionTarget.Rendering += invalidateProcessor;.

    The function:

    private void invalidateProcessor(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(new Action(() => { if (!paused) glControl1.Invalidate(); }), DispatcherPriority.Background);
        }
    

    I chose DispatcherPriority.Background because the higher priorities cause a delay in the touches again.