KeyPressed problem - solved but not quite understood...

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

KeyPressed problem - solved but not quite understood...

Kenneth R Sloan-2
For those interested in my KeyPressed problem - recall that I had a plugin which worked perfectly in most environments, but failed in others.  When I set up a new laptop, it turned out to always fail - so I had the opportunity to experiment with it.

I still don't understand everything - but here are the things I changed.  Some of them seem like good ideas in general, and others just seem to work and I haven't done enough testing to nail down why.  But...the code works perfectly on the new laptop.  I'll clean it up and test it on other machines...tomorrow.

a) Instead of removing IJ.instance as a KeyListener from ImageWindow and ImageCanvas, I now get the complete list of KeyListeners and remove them all.  When done, I will add them all back again.
This probably was NOT the problem (because the list seems to be of length 1 - but it's cleaner code than in the KeyListener example on the IJ website.

b) Instead of keyPressed(), I now pay attention only to keyTyped().  Someone or something convinced me that keyTyped() was more "reliable".  Since it now works with keyTyped, and that code seems simpler - I'm leaving it that way.  Again - this is mostly a clean up and may not be relevant to the fix.

c) I now do a win.requestFocus *immediately* before waiting for a keyTyped() event.  I think this may be the key - the behavior when broken was what you would expect if the ImageWindow had lost focus.  I don't really understand how the focus mechanism works, and have been coding by copying for years.

d) UNCLEAR at the moment....it's possible that this code fails:

 win.requestFocus();
 keyTyped = false; // set to true by keyTyped()
 while(!keyTyped) ; // I took out both wait/notify and even Thread.sleep(100) to simplify
        // key has been typed

    Note: I'm not really sure this is broken - I may have changed something else.  But, the current code is:

 keyTyped = false; // set to true by keyTyped()
 while(!keyTyped) win.requestFocus();
 // key has been typed

This is a bit nasty for a user who wants to interrupt what he's doing and look at another window - I will experiment and try to remove this...tomorrow.

I'm reporting at this stage because a few people were very helpful and I'm hoping this jogs someone's memory.  I think the issue is  FOCUS.  The previous version did a win.requestFocus() a bit earlier in the code, and there was enough stuff inbetween that *might* have broken things.  But, again, I don't understand FOCUS, so  I don't understand why the earlier code fails and this works.

My immediate plan is:

a) polish the working code so that all the details are taken care of, and it works perfectly.

b) experiment with changes, with the goal of breaking it again.

c) along the way, I'll probably add back a wait/notify to replace the ugly busy wait - partly to see if either breaks anything, but mostly to repair the ugliness.

If I succeed, I'll report back.

Thanks again to those who helped, and gave me things to think about.  It was VERY helpful!



--
Kenneth Sloan
[hidden email]
Vision is the art of seeing what is invisible to others.

--
ImageJ mailing list: http://imagej.nih.gov/ij/list.html
Reply | Threaded
Open this post in threaded view
|

Re: KeyPressed problem - solved but not quite understood...

Kenneth R Sloan-2
For experts only!

Java plugin
Brand new MacBookPro, current OS
Built using ‘ant’

The plugin establishes an ImageWindow, on which it periodically updates an Overlay.

It is registered as a KeyListener (both ImageWindow and ImageCanvas)
QUESTION: is this really necessary?  I copied it from the example on the IJ website, but I confess I don’t understand this.

All previously registered KeyListeners are removed (and remembered…and re-installed when we are done).

Here is the KeyListener code, keyChar and keyTyped are global:

   // handle keyboard interaction
    private char keyChar;
    private boolean keyTyped = false;
    @Override
        public void keyTyped(KeyEvent e)
    {
        keyChar = e.getKeyChar();
        keyTyped = true;
    }
    @Override
        public void keyReleased(KeyEvent e) {}
    @Override
        public void keyPressed(KeyEvent e) {}

Below is the code to wait for user input and deal with it.  I welcome all (expert) opinions on the observed behavior.  The cases that do not work puzzle me greatly.  Notethat the last option appears to work perfectly as desired - but I don’t understand why.  

I apologize for not providing a fully working, but minimal plugin.  I may try to do this (once I get the working version to the client!).  If you are interested, I can send the source code (warning, there is a second piece of source code for a helper Class - if necessary, I can make available the entire ‘ant’ project (and I’d welcome comment I on *that*, too).  Actually - I can easily comment out the requirement for the helper Class to reduce this to a single .java file.

Perhaps tomorrow, I’ll start taking things out and making this as tiny as possible (while still demonstrating the behavior).

I’m *really* puzzled by the fact that the last option works perfectly - I don’t understand why the user CAN interactively switch focus and have that switch stick.  I *like* it - but I don’t understand it!

        … code to update Overlay …
        ipl.updateAndDraw(); // I include this because it *might* matter

        while(true)
            {
                keyTyped = false; // global variable, set to true by keyTyped()

                /*
                  This code does NOT work - keyTyped() is not called
                  win.requestFocus();
                  while(!keyTyped) Thread.sleep(100);
                */

                /*
                  This code does NOT work - keyTyped() is not called
                  win.requestFocus();
                  while(!keyTyped) ;
                */

                /*
                  This code works, but can't switch focus away from window
                  The focus immediately switches back to our ImageWindow
                while(!keyTyped)
                    {
                        win.requestFocus();
                        Thread.sleep(100)
                    }
                */

                /*
                  This code works, and allows the user to switch focus interactively
                  Switching away from our ImageWindow works, is sticky, and no keyTyped() calls
                  Switching back to our ImageWindow works, and KeyTyped() calls happen
                */
                while(!keyTyped) win.requestFocus();
               
                // keyChar is current
                … code to process keyChar …
            }

--
Kenneth Sloan
[hidden email]
Vision is the art of seeing what is invisible to others.

--
ImageJ mailing list: http://imagej.nih.gov/ij/list.html
Reply | Threaded
Open this post in threaded view
|

Re: KeyPressed problem - solved but not quite understood...

Fred Damen
Greetings,

I'm not expert but...

for(Component c : new Component[]{imp,ic})
   c.addFocusListener(new FocusListener() {
      @Override focusGained(FocusEvent e) {
         IJ.log("Gained: "+e.getSource()
               +"\n"+e.getComponent()
               +"\n"+e.getOppositeComponent());
         }
      @Override focusLost(FocusEvent e) {
         IJ.log("Lost: "+e.getSource()
               +"\n"+e.getComponent()
               +"\n"+e.getOppositeComponent());
         }
      });

just a guess...

The requestFocus() is only a request and does not guarantee a change in
focus, and, probably only tries to fulfill the request the first time
after a thread gains control of cpu. Thread.sleep() upon entering gives up
control, (even if nothing else tries to take control), and retakes control
upon leaving, clearing the fact that the thread had previously request
focus.

The first two only request the focus once, and if something is stealing
focus then it is never requested back.
The third is stealing the focus every 100ms.
The forth is stealing the focus after something else is done with it, i.e,
something else steals the thread control and then when your thread regains
it automatically the request focus flag is cleared...


Enjoy,

Fred

On Fri, April 10, 2020 6:19 am, Kenneth Sloan wrote:

> For experts only!
>
> Java plugin
> Brand new MacBookPro, current OS
> Built using ‘ant’
>
> The plugin establishes an ImageWindow, on which it periodically updates an
> Overlay.
>
> It is registered as a KeyListener (both ImageWindow and ImageCanvas)
> QUESTION: is this really necessary?  I copied it from the example on the
> IJ website, but I confess I don’t understand this.
>
> All previously registered KeyListeners are removed (and remembered…and
> re-installed when we are done).
>
> Here is the KeyListener code, keyChar and keyTyped are global:
>
>    // handle keyboard interaction
>     private char keyChar;
>     private boolean keyTyped = false;
>     @Override
> public void keyTyped(KeyEvent e)
>     {
> keyChar = e.getKeyChar();
> keyTyped = true;
>     }
>     @Override
> public void keyReleased(KeyEvent e) {}
>     @Override
> public void keyPressed(KeyEvent e) {}
>
> Below is the code to wait for user input and deal with it.  I welcome all
> (expert) opinions on the observed behavior.  The cases that do not work
> puzzle me greatly.  Notethat the last option appears to work perfectly as
> desired - but I don’t understand why.
>
> I apologize for not providing a fully working, but minimal plugin.  I may
> try to do this (once I get the working version to the client!).  If you
> are interested, I can send the source code (warning, there is a second
> piece of source code for a helper Class - if necessary, I can make
> available the entire ‘ant’ project (and I’d welcome comment I on
> *that*, too).  Actually - I can easily comment out the requirement for the
> helper Class to reduce this to a single .java file.
>
> Perhaps tomorrow, I’ll start taking things out and making this as tiny
> as possible (while still demonstrating the behavior).
>
> I’m *really* puzzled by the fact that the last option works perfectly -
> I don’t understand why the user CAN interactively switch focus and have
> that switch stick.  I *like* it - but I don’t understand it!
>
> … code to update Overlay …
> ipl.updateAndDraw(); // I include this because it *might* matter
>
> while(true)
>    {
> keyTyped = false; // global variable, set to true by keyTyped()
>
> /*
>  This code does NOT work - keyTyped() is not called
>  win.requestFocus();
>  while(!keyTyped) Thread.sleep(100);
> */
>
> /*
>  This code does NOT work - keyTyped() is not called
>  win.requestFocus();
>  while(!keyTyped) ;
> */
>
> /*
>  This code works, but can't switch focus away from window
>  The focus immediately switches back to our ImageWindow
> while(!keyTyped)
>    {
> win.requestFocus();
> Thread.sleep(100)
>    }
> */
>
> /*
>  This code works, and allows the user to switch focus interactively
>  Switching away from our ImageWindow works, is sticky, and no
> keyTyped() calls
>  Switching back to our ImageWindow works, and KeyTyped() calls happen
> */
> while(!keyTyped) win.requestFocus();
>
> // keyChar is current
> … code to process keyChar …
>    }
>
> --
> Kenneth Sloan
> [hidden email]
> Vision is the art of seeing what is invisible to others.
>
> --
> ImageJ mailing list: http://imagej.nih.gov/ij/list.html
>

--
ImageJ mailing list: http://imagej.nih.gov/ij/list.html
Reply | Threaded
Open this post in threaded view
|

Re: KeyPressed problem - solved but not quite understood...

Kenneth R Sloan-2
Thanks. Sounds plausible, and doesn't encourage me to change the code from what works, empirically.

I wonder...do you think this might be an "improvement"?

        while(!keyTyped)
        {
          Thread.sleep(100);
          win.requestFocus();
        }

That is - request the focus AFTER sleeping rather than before?

but, you know, ugly as a bare busy-wait is, there's not usually much for the computer to do while waiting for human input.  Back in the stone ages, where there was ONE CPU and it was THE scarce resource, this mattered.  Today, with multi-core machines, who cares?

There's still the mystery as to why the original (very naive) code works perfectly on so many machines, fails only intermittently on 2, and fails every time on 1 (of those tested).

I'm reminded of an ancient example: some friends were demonstrating new software on a very new processor at a big computer exhibition (ca. 1968).  Their code had a bug, but they could *prove* from the listing that the bug was impossible.  The only way to explain the behavior was that a "write" instruction was not having any effect.  In desperation they changed:

        write addr, constant
to
        write addr, constant
        write addr, constant

and it worked perfectly.  5 minutes before the doors opened.

My working code looks a bit like this.  "give me focus, give me focus(no, really), give me focus(damn you!)...".

The good news is that the client (I'm sorry: "esteemed colleague") is now picking nits about how the cursors look and how to set the initial parameters, and...  all stuff that can be fixed in my sleep.

--
Kenneth Sloan
[hidden email]
Vision is the art of seeing what is invisible to others.

--
ImageJ mailing list: http://imagej.nih.gov/ij/list.html