Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent a Java graphical program from stealing focus when updating the window?

On an (embedded) Ubuntu 12.04 system, we have a simple Java program that displays some graphics patterns on a window, updating every second or so. We use it to monitor some processes running on the system. The problem is that while it is active and not minimized, it steals the focus whenever the window is updated. This makes working with the open terminal windows impossible.

The behaviour is the same when running the app form command line or from Eclipse IDE.

The same problem does not occur on Windows 7 when running under NetBeans IDE.

How can we prevent the Java app from stealing the focus on the Ubuntu machine?


UPDATE 1: Found this question that seem to struggle with the same problem: How do I stop/workaround Java apps stealing focus in Linux window managers . Reading it, I learned that the problem is with using JFrame as a container, which is what we use. Their solution was to use the JWindow container instead of JFrame container. However, searching for the difference, the JWindow is "naked" and does not behave like a "real" window, as there's no decorations. Is there a way to use JWindow inside a JFrame, and thus eliminate the focus stealing?


UPDATE 2: Trying to run this program on an Ubuntu Virtual Machine on the PC gives the same misbehaviour. This suggests that there is a difference of the Java runtime of Windows 7 and Linux, and that the problem is not specific to the Embedded linux.


UPDATE 3: Here's a SSCCE:

//package SSCCE;

import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;

public class MonitorSSCCE extends JFrame{

    public static void main(String[] args) 
    {
        // Set the frame
        JFrame ecoreFrame = new JFrame("SSCCE");
        ecoreFrame.setSize(120, 120);
        ecoreFrame.setVisible(true);

        // Refresh frame every 200 msec
        while (true) {
            GRFX grfx = new GRFX();
            ecoreFrame.add(grfx);
            ecoreFrame.setVisible(true);
            grfx = null;

            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {

            }
        }
    }


    static int clr = 0;

    public static class GRFX extends JPanel {

        public void paintComponent(Graphics comp) {
            Graphics2D comp2D = (Graphics2D) comp;

            // Draw a changin color rectangle
            comp2D.setColor(new Color(clr, 0, 0));
            Rectangle2D.Double rect = new Rectangle2D.Double(10, 10, 100, 100);
            comp2D.fill(rect);
            clr = clr + 10;
            if (clr > 255)
                clr = 0;
        }
    }
}

UPDATE 4: While preparing the SSCCE, I had some reading and find out about the plethora of window refresh methods of the JFrame object. It happens that the problem was the setVisible() call inside the while loop. The solution was to replace it with the repaint() method.

like image 734
ysap Avatar asked Nov 01 '22 15:11

ysap


1 Answers

On Ubuntu 12.04.2 with the Unity window manager, this example animates normally on the desktop in the background at 1 Hz while a terminal and software update are also running. A few points of reference for comparison:

  • Swing GUI objects should be constructed and manipulated only on the event dispatch thread.

  • Avoid Mixing Heavyweight and Lightweight Components.

  • Avoid overriding paint(); "Swing programs should override paintComponent() instead of overriding paint()."—Painting in AWT and Swing: The Paint Methods.

  • Do not invoke updateUI() or pack() as a substitute for validate().

Addendum: I re-factored your example below to illustrate some additional points:

  • Don't sleep on the EDT; do use javax.swing.Timer to pace animation.

  • Override getPreferredSize() to establish a graphic component's initial geometry.

  • Profile to verify expected heap usage.

image

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

/**
 * @see https://stackoverflow.com/a/18455006/230513
 */
public class TestGRFX {

    private static class GRFX extends JPanel {

        private static final int N = 256;
        private float clr = 0;
        private Rectangle2D.Double rect = new Rectangle2D.Double(0, 0, N, N);
        private Timer t = new Timer(100, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                GRFX.this.repaint();
            }
        });

        public void start() {
            t.start();
        }

        @Override
        public void paintComponent(Graphics g) {
            Graphics2D g2D = (Graphics2D) g;
            g2D.setColor(new Color(Color.HSBtoRGB(clr, 1, 1)));
            g2D.fill(rect);
            clr += .01;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(N, N);
        }
    }

    private void display() {
        JFrame f = new JFrame("TestGRFX");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        GRFX g = new GRFX();
        f.add(g);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
        g.start();
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TestGRFX().display();
            }
        });
    }
}
like image 117
trashgod Avatar answered Nov 08 '22 15:11

trashgod