Opening and closing a window in Java

Unless our goal is to create a command-line game, we need to create a graphical window, which will contain the game. (Technically, a full-screen game also runs in a window -- a full-screen window without borders.) To open a window we need two commands: the first one to create it in memory, and the second one to show it on the screen? Why two separate commands? It allows us to modify the window before it is displayed; to change its size, set a title and an icon, etc.

But before opening the window, let's think about the consequences. We probably don't want to keep this window open forever. Well, we can close it using one command. The problem is, when should we close the window? Answers of type "immediately" or "ten seconds later" are not good for game programming. The correct answer is: "when the user decides to quit the game."

Yeah, and how are we supposed to know that?

Unlike in classic procedural programming "do this, do that, now ask for an input, now display an output", when we program in a multi-task environment, we can make the program do whatever we want and whenever we want, but the user can also use the mouse and keyboard whenever they want, regardless of whether the given moment is convenient for our program or not. Operating system supports this behavior, and even more: it allows user to run other programs, switch between the programs randomly, minimize windows, etc.

Just think about how many options MS Windows user has to close an ordinary running program. They can click on the red "x" button in the upper right corner of the window. They can double-click the icon the upper left corner of the window. They can click with the right mouse button on the task bar and choose "Close". They can run a task manager, select the running application, and choose "End Task". Or they can press Alt+F4 on keyboard. And I am not sure I have mentioned all the possibilities here. Should we manage all of this in our humble program, just to open the window and close it at the right moment?

Fortunately, things like this are already solved. However the user attempts to close the window, operating system will wrap it into a single event called "trying to close the window". If we detect this event, we can close the window and end the program.

And why does not Java do this automatically? Well, sometimes it is better not to close the window immediately. If we work with some data, we probably want to save them on disk before ending the program. Or perhaps we should ask user whether they want to a) save the data and end program; or b) just end the program and discard the data; or c) none of the above, because they only clicked the window-closing button accidentally, and they actually want to continue their work. This is why closing the window and ending the program is not an automatic reaction for the given event, but it must be handled by the program.

Well then, how will we know about this event?

A long answer would require visiting a library and reading seven thick books concerning the philosophy of event-driven programming. A short answer is that the operating system (or in our case it is Java runtime) will give us this information in real time by calling a provided function of our program; and the parameter of this function will be an object describing the circumstances of what exactly has happened. So first we have to program this function in the way the system expects us to. And then we tell the system to use this function as a part of its announcement system.

Now let's move to the technical details. First, in Java we cannot literally provide a function to the system, because in Java one cannot put a function into a variable and send it away. (Some languages provide this option.) So we have to create an object having this function; and then we register this object as an event receiver, and when the event happens, the system will call the function of this object.

Second, if we want to specify how a function must be written in Java, we usually use an interface. In this case, it is the "WindowListener" interface; whoever wants to receive window-related events, must implement this interface. This interface has seven functions (opening a window, activation of a window, deactivation of a window, minimizing a window, unminimzing a window, trying to close a window, closing a window), and our object must have them all. But if we are too lazy to write them, we can use a helpful class "WindowAdapter"; this class contains all the seven functions and they do nothing; so the object learns about all the events and ignores them. (Somehow it reminds me of a General Prosecutor's Office in my country.) Our object can be derived from this class, and override only the function we really want to handle; in this case it will be the "windowClosing" function (note: don't confuse it with the "windowClosed" function). What will our object do when this function is called? Well, it will close the window; that's what we are talking about all the time.

Third, to register our object as a receiver of events for a specific window, we do it using the "addWindowListener" method of the window. It would be good to make our object remember the window, so that it later knows which window to close. So this is the whole program:

import java.awt.*;
import java.awt.event.*;

class MyWindowListener extends WindowAdapter {
	private Frame window;

	public MyWindowListener(Frame window) {
		this.window = window;
	}

	@Override
	public void windowClosing(WindowEvent e) {
		window.dispose();
	}

}

public class Program {

	public static void main(String[] args) {
		Frame window = new Frame();
		window.setTitle("Example window");
		window.setSize(400, 300);
		window.setVisible(true);
		window.addWindowListener(new MyWindowListener(window));
	}

}

The "Frame" class represents a window, more precisely the main window of the application displayed in the task bar. (If we open smaller windows in our program, we do it using the "Dialog" class. Also there is a Swing library for super-cool windows.) The "MyWindowListener" class is an event receiver; in a constructor parameter it receives a reference to a window, it stores the window reference into a variable, and on calling "windowClosing" it will close the window. The main program, symbolically called "Program", creates a window, sets a few attributes; the most important of which is the "setVisible(true)", because only then the window really appears on the screen. In the last line, a "MyWindowListener" object is created and registered as a receiver of events from the window. This is how the "main" function ends, but the whole program does not end yet, because a Java program ends only when then main function ends and there is no window. Only after closing the window the program will really end.

If I ended the article at this moment, you would probably think than in Java game programming the "effort : result" ratio is horrible. So much theory and so much code for merely opening and closing a window! But we actually managed to do more than that, because the same principle governs handling other event too, such as keyboard and mouse events. Again, we have some interfaces, specifically "KeyListener" (pressing a key, releasing a key, typing a letter), "MouseListener" (pressing a mouse button, releasing a mouse button, clicking, moving the mouse over a window, moving the mouse out of a window), "MouseMotionListener" (moving the mouse, dragging with the mouse), and "MouseWheelListener" (turning the mouse wheel); then we have helpful classes "KeyAdapter" and "MouseAdapter"; and we add these objects using "addKeyListener", "addMouseListener", "addMouseMotionListener" and "addMouseWheelListener" methods. These functions get a parameter object, which contains information about which key was pressed or where did the mouse move.

Let's try it using a simple case. At this moment, we will write the information to the command line. (Which can be actually helpful even while developing a real game.) Here are a few functions; you can try the rest of them:

import java.awt.*;
import java.awt.event.*;

class MyWindowListener extends WindowAdapter {
	private Frame window;

	public MyWindowListener(Frame window) {
		this.window = window;
	}

	@Override
	public void windowClosing(WindowEvent e) {
		System.out.println("We are going to close the window");
		window.dispose();
	}

	@Override
	public void windowIconified(WindowEvent e) {
		System.out.println("The window was minimized");
	}

}

class MyKeyListener extends KeyAdapter {

	@Override
	public void keyPressed(KeyEvent e) {
		if (KeyEvent.VK_ESCAPE == e.getKeyCode()) {
			System.out.println("An ESC key was pressed");
		}
	}

}

class MyMouseListener extends MouseAdapter {

	@Override
	public void mouseClicked(MouseEvent e) {
		System.out.println("A button was clicked " + e.getButton());
	}

}

public class Program {

	public static void main(String[] args) {
		Frame window = new Frame();
		window.setTitle("Example window");
		window.setSize(400, 300);
		window.setVisible(true);
		window.addWindowListener(new MyWindowListener(window));
		window.addKeyListener(new MyKeyListener());
		window.addMouseListener(new MyMouseListener());
	}

}

Note: When using the Eclipse environment, you don't have to remember the names of all the interfaces and their functions. The environment will suggest you the names, for example if you write "window.add" and press Ctrl+space, all the window methods starting with "add" will be displayed. If you create a subclass of e.g. "WindowAdapter" and you want to change a method, but you don't remember the name, move the cursor inside the code of the class, click the right mouse button, choose "Source | Override/Implement Methods", and all the methods for overriding will be displayed. The Eclipse environment has a lot of similar tools, and gradually you become familiar with them and they will help you a lot.

There are two ways to process a keyboard or mouse event. Events "keyTyped" and "mouseClicked" contain information about a finished activity: a letter was typed, a mouse button was clicked. Events "keyPressed", "keyReleased", "mousePressed" and "mouseReleased" contain information about the details: what was pressed and what was released. The difference can be illustrated on an example of typing a capital "A" letter on the keyboard. This could be perceived as one event (typing a letter "A") or as four events (pressing a "Shift" key, pressing an "a" key, releasing an "a" key, releasing a "Shift" key). We usually don't need to process both kinds of event; we choose one of them depending on the type of a program we are doing. For an action game, which needs to react on the keys being pressed and released, we need "keyPressed" and "keyReleased", but for example if we tried to do our own text editor, handling "keyTyped" could be enough.

viliam@bur.sk