startup focus not recognizing keystrokes (P3 only?)

edited February 2018 in Questions about Code

Whenever I launch a sketch, either from the P3 pde or from Eclipse, the sketch always becomes the top app in my system (Windows, so the title bar is highlighted, and the task bar and task manager both show the sketch app on top). But more and more, I have to click inside the sketch window to get it to recognize keyboard input (i.e., the keypressed() handler is not getting called). And, the problem is intermittent. While working on a sketch, say I run it 20 times, about half the time it recognizes the keyboard directly, and half the time I have to click inside the window first. Clicking on the title bar has no effect, the app just stays on top, but no keystroke response. Also, not changing any significant stuff (settings(), setup(), draw(), mouse/key handlers) just random code changes and keypressed() working/not working.

I've spent a few hours wading through focus management tutorials and posts and at one point I thought I had nailed it with:

frame.requestFocus(); // had no effect on it's own, but thenn seemed to work with toFront() and repaint()
frame.toFront();
frame.repaint();

But after that started failing intermittently I came across the switch from 'frame' to 'surface' in 3.0 and switched to:

surface.setAlwaysOnTop(true); // no requestFocus(), toFront(), or repaint() for surface

Again, seemed to help at first but now back to never knowing if the keyboard will work or if I need to click first with the mouse.

I put the following code inside draw() to try and see who/what was getting the focus:

if (KeyboardFocusManager.getCurrentKeyboardFocusManager() != null)
    println(millis() + ": " + KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());

It seems like the keystrokes fail when the focus owner shows JFrame or null, and the keystrokes work when the focus starts on the JFrame but then switches to processing.awt.PSurfaceAWT:$SmoothCanvas. Sometimes it switches on it's own (highly desirable, the whole point of this post) or as the result of a mouse click.

Any suggestions for helping manage the startup focus? I don't recall having this problem over the last 5-10 years, just over the past year or so (P3 issue?), but no definitive start date due to the intermittency.

Answers

  • I did tried once to see if I could make the requestFocus() work: https://forum.processing.org/two/discussion/comment/109540/#Comment_109540

    but I experienced exactly what you explained: It does not work all the time. Some other member mentioned that could be related to Windows and how it manages an application when it is launched. It seems to give focus to the icon in the taskbar. I didn't pursue any further...

    Good luck,

    Kf

  • edited December 2017

    ****EDIT: Of course you should create a ticket in Github, or at least, that would be nice.


    Here are some ideas:

    https://forum.processing.org/two/discussions/tagged?Tag=robot.mousemove()
    https://forum.processing.org/two/discussion/14002/how-do-i-keep-the-mouse-constrained-inside-my-sketch-window-please#latest
    https://forum.processing.org/two/discussion/20346/how-do-you-implement-java-awt-robot-into-a-sketch#latest
    https://forum.processing.org/two/discussion/12013/how-to-know-the-position-of-the-frame-window-in-3-0#latest

    Documentation of Robot: https://docs.oracle.com/javase/7/docs/api/java/awt/Robot.html

    What if you could use a robot to move your mouse to inside your sketch and automatically click on it? Then your window should get the focus. Hacky hacky... yes. You will need to check the links above. The challenge is that for mouseMove to work, you need to know the position of your sketch window in the display. One way that seems to work is using the displayWidth/displayHeight variables. There is another way to get the position by retrieving the frame associated to the sketch. After you are able to get the mouse inside the sketch, then you need to issue the click event (check the robot docs)

    Unfortunately I won't be able to test it but I am interested to know if you could make it work.

    Here next are two sketches where I did a bit of testing related to your issue (second sketch is from a @GoToLoop's post)

    Kf

    Sketch1:

    import java.awt.Robot; 
    import java.awt.event.KeyEvent; 
    import java.io.IOException;
    
    Robot robot; // defines Object Robot
    boolean flagFocusOnKeyPressed=false;
    
    void setup()
    {
    
      try
      {
        robot = new Robot();
      }
      catch (Exception e) {
        e.printStackTrace();
        exit();
      }
    
    
      size (200, 200);
      robot.keyPress(KeyEvent.VK_SPACE);
    }
    
    void draw() {
    
      background(0, 0, 0);
      fill(255, 255, 255);  
      if (!flagFocusOnKeyPressed) {
        robot.keyPress(KeyEvent.VK_SPACE);
        flagFocusOnKeyPressed=true;
      }
    }
    
    void keyPressed() {
      println("Input1: " +key);
    }
    
    void keyReleased() {
      println("Input2: " +key);
    
      if(key=='m')robot.mouseMove(width/2, height/2);
    }
    

    Sketch 2:

    static final int POOLING_DELAY = 100;
    
    void setup() {
      size(400, 300, JAVA2D);
      noLoop();
      thread("focusThread");
    }
    
    void draw() {
      println(frameCount, focused);
    }
    
    void focusThread() {
      boolean previous = focused;
    
      for (;; delay(POOLING_DELAY))  if (previous != focused) {
        redraw = true;
        previous = focused;
      }
    }
    
    void keyReleased() {
      println("Input2: " +key);  
    }
    
  • edited March 2018

    And here is a tested hacky way to do this business... This is more to share but open to see comments on doing this the proper way.

    Kf

    //REFERENCE: https://forum.processing.org/two/discussion/23037/getting-the-frame-in-processing-3#latest
    
    import processing.awt.*;
    import java.awt.Frame;
    import java.lang.reflect.Field;
    //import processing.awt.PSurfaceAWT.SmoothCanvas;
    
    import java.awt.Robot; 
    import java.awt.event.KeyEvent; 
    import java.io.IOException;
    import java.awt.Point;
    import java.awt.MouseInfo;
    
    Robot robot; // defines Object Robot
    boolean flagFocusOnKeyPressed=false;
    
    
    void setup() {
    
      size (300, 200);
    
      try
      {
        robot = new Robot();
      }
      catch (Exception e) {
        e.printStackTrace();
        exit();
      }
    }
    
    void draw() {     
    
      background(0, 0, 0);
      fill(255, 255, 255);
    
      if (!flagFocusOnKeyPressed ) {
        Frame frame = get_frame();
    
        //MouseInfo.getPointerInfo();
        //Point pt = MouseInfo.getPointerInfo().getLocation();
    
        println(frame.getLocation().x+" "+frame.getLocation().y);
        //println(pt.getX()+"  ...  "+pt.getY());
    
        robot.mouseMove(frame.getLocation().x+1*width/2, frame.getLocation().y+1*height/2);       
    
        if (frameCount>10) {
          robot.mousePress(KeyEvent.BUTTON1_DOWN_MASK);     
          robot.mouseRelease(KeyEvent.BUTTON1_DOWN_MASK);
          flagFocusOnKeyPressed=true;  //Execute only once
        }
      }
    }
    
    
    void keyPressed() {
      println(millis()+" Input1: " +key);
    }
    
    void keyReleased() {
      println("Input2: " +key);
    
      if (key=='m')robot.mouseMove(width/2, height/2);
    }
    
    
    Frame get_frame() {
      Frame frame = null;
      try {
        Field f = ((PSurfaceAWT) surface).getClass().getDeclaredField("frame");
        f.setAccessible(true);
        frame  = (Frame) (f.get(((PSurfaceAWT) surface)));
      }
      catch(Exception e) {
        println(e);
      }
      return frame;
    }
    

    Keyword: kf_keyword robot window startup focus

  • Thanks a ton kfrajer! Sorry for the delay in getting back. I did open an issue on github, hoping for a way to control the focus without generating the mouse click (either manually or in code). Will post here if I find something! Thanks again!

  • Answer ✓

    Added this to draw() and the keyboard works every time:

    public void draw() {
            if (millis() < 1000)
                ((java.awt.Canvas) surface.getNative()).requestFocus();
            // ... the rest of draw()
    
Sign In or Register to comment.