Tải bản đầy đủ (.pdf) (84 trang)

Linux programming unleash phần 8 ppsx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (6.76 MB, 84 trang )

The only thing at all complicated about this example is handling the events associated
with the user clicking on the button object. However, these events are handled with only
a few lines of code in the same way that we handled events in the AWT example pro-
gram in Listing 32.9:
countButton.addMouseListener(
new java.awt.event.MouseAdapter() {
public void mouseClicked(MouseEvent e) {
countLabel.setText(“button clicked “ +
++count + “ times”);
}
});
The addMouseListener method is used to associate an instance of the MouseListener
with a button (or any other component). Now, it is possible to define a separate class that
implements the MouseListener listener interface (which requires defining five methods:
mouseClicked, mouseEntered, mouseExited, mousePressed, and mouseReleased).
However, there is a MouseAdapter utility class that defines “do nothing” versions of
these five methods. In this code example, we are creating an anonymous inner class (for
example, it is an inner class with no name associated with it) that overrides the
mouseClicked. Our mouseClicked method uses the setText method to change the dis-
played text of a label. In Listing 32.13 we import all classes and interfaces from the
packages java.awt (for the graphic components and containers) and java.awt.event
(for the event-handling classes and interfaces), and from the package javax.swing to get
the Swing (or JFC) classes.
In Listing 32.11, we define a static public main method that is executed when we
compile and run this sample program by typing:
javac JFCSample.java
java JFCSample
The JFCSample program does not handle window closing events, so it must be terminated
by typing Ctrl+C in the terminal window where you run the program.
Writing a Chat Program Using JFC
We will develop the ChatJFC program in this section. It is almost identical to the


ChatAWT program, only it uses the JFC classes instead of the AWT classes. Both pro-
grams were shown in Figure 32.2.
The sample program in Listing 32.12 that implements a chat client using JFC is a little
complex because the user interface has several components. Each component is posi-
tioned by an X-Y location in a panel and is sized to a specific width and height using the
setBounds method. Before looking at the code in Listing 32.12, you should look at the
Programming the User Interface
P
ART IV
590
3772316072 CH32 7/26/99 2:05 PM Page 590
bottom of Figure 32.2, which shows the ChatJFC program running. The ChatJFC pro-
gram uses the ChatEngine class. The interface to ChatEngine is very simple and was
covered in a previous section. The ChatJFC class implements the ChatListener interface
and registers itself with an instance of ChatEngine. This registration allows the chat
engine object to call the receiveText method (required to implement the ChatListener
interface) in the ChatJFC class.
Listing 32.12 THE ChatJFC.java FILE
// File: ChatJFC.java
import javax.swing.*; // swing 1.1
//import com.sun.java.swing.*; // swing 1.0
import java.awt.*;
import java.awt.event.*;
public class ChatJFC extends JFrame implements ChatListener{
JPanel jPanel1 = new JPanel();
JTextField myPortField = new JTextField();
JLabel label1 = new JLabel();
JLabel label2 = new JLabel();
JTextField hostField = new JTextField();
JLabel label3 = new JLabel();

JTextField portField = new JTextField();
JLabel label4 = new JLabel();
JTextField inputField = new JTextField();
JLabel label5 = new JLabel();
JButton listenButton = new JButton();
JButton connectButton = new JButton();
JButton disconnectButton = new JButton();
JButton quitButton = new JButton();
JTextArea outputField = new JTextArea();
JScrollPane jScrollPane1 = new JScrollPane();
protected ChatEngine chatEngine;
public ChatJFC() {
super(“Chat with JFC GUI”);
chatEngine = new ChatEngine();
chatEngine.registerChatListener(this);
this.setSize(575, 348);
setVisible(true);
jPanel1.setLayout(null);
jPanel1.setBounds(5, 16, 595, 343);
outputField.setRows(500);
jScrollPane1.setBounds(66, 92, 498, 168);
this.getContentPane().setLayout(null);
quitButton.setBounds(448, 280, 121, 32);
GUI Programming Using Java
C
HAPTER 32
591
32
GUI
PROGRAMMING

U
SING JAVA
continues
3772316072 CH32 7/26/99 2:05 PM Page 591
Listing 32.12 CONTINUED
quitButton.addMouseListener(
new java.awt.event.MouseAdapter() {
public void mouseClicked(MouseEvent e) {
System.exit(0);
}
});
quitButton.setLabel(“Quit”);
this.setSize(592, 371);
label1.setFont(new Font(“Dialog”, 1, 12));
label1.setBounds(1, 5, 69, 33);
label1.setText(“My port”);
myPortField.setBounds(62, 11, 100, 24);
myPortField.setText(“8000”);
label2.setFont(new Font(“Dialog”, 1, 12));
label2.setBounds(200, 3, 93, 38);
label2.setText(“Remote host”);
hostField.setBounds(299, 7, 129, 27);
hostField.setText(“localhost”);
label3.setFont(new Font(“Dialog”, 1, 12));
label3.setBounds(433, 5, 45, 36);
label3.setText(“Port”);
portField.setBounds(469, 9, 93, 27);
portField.setText(“8192”);
label4.setFont(new Font(“Dialog”, 1, 12));
label4.setBounds(4, 43, 60, 28);

label4.setText(“Input”);
inputField.setBounds(65, 47, 507, 34);
inputField.setText(“ “);
inputField.addKeyListener(
new java.awt.event.KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == ‘\n’) {
chatEngine.send(inputField.getText());
}
}
});
label5.setFont(new Font(“Dialog”, 1, 12));
label5.setBounds(0, 90, 58, 34);
label5.setText(“Output”);
listenButton.setBounds(5, 281, 163, 33);
listenButton.setLabel(“Start listening”);
listenButton.addMouseListener(
new java.awt.event.MouseAdapter() {
public void mouseClicked(MouseEvent e) {
chatEngine.startListening(
Integer.parseInt(myPortField.getText()));
}
});
Programming the User Interface
P
ART IV
592
3772316072 CH32 7/26/99 2:05 PM Page 592
connectButton.setBounds(175, 280, 136, 34);
connectButton.addMouseListener(

new java.awt.event.MouseAdapter() {
public void mouseClicked(MouseEvent e) {
chatEngine.connect(hostField.getText(),
Integer.parseInt(portField.getText()));
}
});
connectButton.setLabel(“Connect”);
disconnectButton.setBounds(315, 280, 127, 33);
disconnectButton.addMouseListener(
new java.awt.event.MouseAdapter() {
public void mouseClicked(MouseEvent e) {
chatEngine.connect(hostField.getText(),
Integer.parseInt(portField.getText()));
}
});
disconnectButton.setLabel(“Disconnect”);
this.getContentPane().add(jPanel1, null);
jPanel1.add(label1, null);
jPanel1.add(myPortField, null);
jPanel1.add(label2, null);
jPanel1.add(hostField, null);
jPanel1.add(label3, null);
jPanel1.add(portField, null);
jPanel1.add(label4, null);
jPanel1.add(inputField, null);
jPanel1.add(label5, null);
jPanel1.add(listenButton, null);
jPanel1.add(connectButton, null);
jPanel1.add(disconnectButton, null);
jPanel1.add(quitButton, null);

jPanel1.add(jScrollPane1, null);
jScrollPane1.getViewport().add(outputField, null);
}
private String output_string = “”;
public void receiveText(String s) {
if (s == null) return;
output_string = output_string + s;
outputField.setText(output_string + “\n”);
}
static public void main(String [] args) {
new ChatJFC();
}
}
GUI Programming Using Java
C
HAPTER 32
593
32
GUI
PROGRAMMING
U
SING JAVA
3772316072 CH32 7/26/99 2:05 PM Page 593
The ChatJFC program is very similar to the ChatAWT program. I wrote the ChatJFC pro-
gram by copying the ChatAWT program and doing the following:
• Adding the import swing (JFC) package statement.
• Changing Label to JLabel, Button to JButton, and so on.
• Using the getContentPane method where required to get the internal container for
adding components and setting the layout manager to null.
Using Native Java Compilers

There are two native mode Java compilers under development, one by TowerJ
(www.towerj.com) and the other by Cygnus (www.cygnus.com). At the time this chapter
was written (March 1999), the Cygnus compiler was available only in early test releases,
but it looked promising. The TowerJ compiler is a commercial product. I have used it for
three programs, and I have seen performance improvements from a factor of four to a
factor of 11 over using a JIT (Just In Time compiler). One of the benefits of Java is the
portability of its byte code. However, for server-side programs, giving up portability in
favor of large performance improvements is often a good option.
Summary
This short chapter has hopefully given you both a quick introduction to the Java language
and the motivation to study the language further. There are many sources of information
for programming Java on the Internet. Because Web site links change, I will keep current
links to my favorite Java resources on the Internet at my Web site
(www.markwatson.com/books/linux_prog.html).
Many interesting topics were not covered in this short chapter, such as:
• Drawing graphics
• Writing applets that run inside of Web browsers
• More techniques for handling user interface events
If you are primarily a C or C++ programmer, I urge you to also learn Java. I have been
using C for 15 years and C++ for 10 years, but I prefer Java for most of my development
work. Yes, it is true that Java programs will run slower than the equivalent C or C++ pro-
grams. However, after using Java for about 3 years, I believe that Java enables me to
write a large application in about half the time it takes to write the same application in
C++. I have talked with other developers who agree that they are approximately twice as
productive when programming in Java instead of C or C++.
Programming the User Interface
P
ART IV
594
3772316072 CH32 7/26/99 2:05 PM Page 594

IN THIS CHAPTER
• OpenGL is a Software Interface to
Graphics Hardware 596
• The Orbits Sample Program 597
33
CHAPTER
OpenGL/Mesa
Graphics
Programming
by Mark Watson
3872316072 CH33 7/26/99 2:04 PM Page 595
The OpenGL API was developed at Silicon Graphics and has become an industry standard
for high-quality 3D graphics. Although there are commercial ports of OpenGL to Linux, a
high-quality public domain OpenGL-like implementation called Mesa has been written by
Brian Paul. Mesa can not be called OpenGL because it is not licensed from Silicon
Graphics, but I have found it to be an effective tool for OpenGL programming on Linux.
Mesa might be included with your Linux distribution. Although I assume that you have
installed and are using Mesa, I will refer to OpenGL in this chapter. I will keep a current
link to the Mesa distribution on my web site.
The
OpenGL API is complex. We will use one simple example in this chapter to illustrate
how to use the
OpenGL auxiliary library to draw simple shapes, placing and rotating the
shapes under program control. We will also learn how to use lighting effects and how to
change the viewpoint. For most 3D graphics applications (like games), you need a sepa-
rate modeling program to create 3D shapes, and you also need specialized code to use
these shapes in OpenGL programs. The topics of modeling 3D images and using them in
OpenGL programs is beyond the scope of this introductory chapter.
Before reading this chapter, please download the latest Mesa distribution and install it in
your home directory. The sample program for this chapter is located in the src/OpenGL

directory on the CD-ROM. You will have to edit the first line of Makefile to reflect the
path in your home directory where you have installed Mesa. The example program uses
the OpenGL Utilities Library (GLUT). OpenGL itself is operating system- and device-
independent. The GLUT library allows programmers to initialize OpenGL, create windows,
and so on in a portable way. There are three example directories in the Mesa installation
directory: book, demos, and samples. Please make sure that building Mesa also built exe-
cutables for the many sample programs in these three directories. Then edit the first line
of Makefile for the example program for this chapter and try running it to make sure
that Mesa is set up properly on your computer. A few of the example programs in the
Mesa distribution may not run with your graphics card; do not worry about this.
OpenGL is a Software Interface to
Graphics Hardware
There are OpenGL software interface implementations for most 3D graphics cards, so
OpenGL and Mesa will probably run efficiently on your computer unless you have a very
old graphics card. Microsoft supports
OpenGL on Windows 95, 98, and NT, so the pro-
grams that you develop under Linux using Mesa will probably run with few modifica-
tions under Windows. I worked at a computer animation company, Angel Studios
Programming the User Interface
P
ART IV
596
3872316072 CH33 7/26/99 2:04 PM Page 596
(www.angel.com), where we used proprietary software for rendering real-time 3D graph-
ics on a wide variety of hardware, including Nintendo Ultra-64, Windows, and SGI
workstations. This proprietary software, as an option, used OpenGL to talk with graphics
hardware.
The Orbits Sample Program
There are many features of OpenGL that we are not covering in the orbits example pro-
gram for this chapter (for example, the use of display lists and texture mapping). The

sample program in this section does illustrate several OpenGL programming techniques,
but it is also simple enough for a tutorial example. The example program orbits.c is
located in the src/OpenGL directory.
The sample program uses the GLUT function glutSolidSphere to draw both a large
“planet” and a small orbiting satellite. This example demonstrates the following:
• Creating a window for OpenGL graphics and initializing OpenGL
• Creating simple 3D objects using GLUT
• Placing objects anywhere in a three-dimensional space using X-Y-Z coordinates
• Rotating an object about any or all of the x-, y-, and z-axes.
• Enabling the use of material properties so objects can be colored
• Enabling depth tests so that rendered objects close to the viewer correctly cover
objects that are obscured
• Handling keyboard events
• Updating OpenGL graphics for animation effects
These operations are performed using both the OpenGL core API and the OpenGL utility
library. All OpenGL implementations include the OpenGL utility library, so the simple
example in this chapter should be easily portable to other operating systems and
OpenGL
implementations.
OpenGL uses a modeling coordinate system where the X coordinate moves increasingly
toward the right on the screen, the Y coordinate moves increasingly upward, and the z
coordinate increases looking into the screen. The origin (for example, at X=Y=Z=0) is
located at the center of the screen.
The sample program cycles viewing angle, plus smooth or flat shading, when you hit any
key (except for an escape, or q characters, that halt the program) while the program is
running.
OpenGL/Mesa Graphics Programming
C
HAPTER 33
597

33
OPENGL/MESA
GRAPHICS
PROGRAMMING
3872316072 CH33 7/26/99 2:04 PM Page 597
Figure 33.1 shows the orbits sample program running in the default smooth-shaded
mode. The background color was changed to white for this figure; the program on the
CD-ROM has a black background.
Programming the User Interface
P
ART IV
598
FIGURE 33.1
The orbits pro-
gram running
with smooth-
shading.
Creating a Window for OpenGL Graphics and
Initializing OpenGL
In this chapter, we will use the following OpenGL utility library functions to initialize
OpenGL, the GLUT library, and to create a window for drawing:
Function Description
glutInit Initializes GLUT and OpenGL.
glutInitDisplayMode Sets display properties (our example program will set
properties allowing for double buffering, depth queu-
ing, and use of RGB colors).
glutInitWindowSize Sets the size of the main window.
glutInitWindowPosition Positions the main window on the desktop or X
Windows display.
glutCreateWindow Actually creates the main window.

3872316072 CH33 7/26/99 2:04 PM Page 598
The initialization code in the sample program looks like the following:
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow(argv[0]);
In calling glutInitDisplay, we use the following constants:
Constant Description
GLUT_DOUBLE Enables double buffering for smooth animation.
GLUT_RGB Specifies the use of RGB color table.
GLUT_DEPTH Enables the use of a depth buffer to determine when one
object is obscuring another in a scene.
Creating Simple 3D Objects Using GLUT
There are several GLUT utility functions for creating both wire frame and solid objects. To
create a sphere, use either of the following:
void glutSolidSphere(GLdouble radius, GLint slices,
GLint stacks);
void glutWireSphere(GLdouble radius, GLint slices, GLint stacks);
Here, radius is the radius of the sphere. The slices argument is the number of flat
bands created around the z-axis. The higher the number of slices, the rounder the sphere.
The stacks argument is the number of flat bands along the z-axis. The higher the slices
value, the rounder the sphere. Lower slices and stacks values produce shapes that are
drawn quicker. A sphere is drawn (or rendered) centered at the origin in modeling coordi-
nates (for example, when the OpenGL Matrix mode is in modeling mode; more on this
later).
To create a cube, use either of the following:
void glutSolidCube(GLdouble size);
void glutWireCube(GLdouble size);
The size argument is the length of any edge of the cube.

To draw a cone, use either of the following:
void glutSolidCone(GLdouble base, GLdouble height,
GLint slices, GLint stacks);
void glutWireCone(GLdouble base, GLdouble height,
GLint slices, GLint stacks);
OpenGL/Mesa Graphics Programming
C
HAPTER 33
599
33
OPENGL/MESA
GRAPHICS
PROGRAMMING
3872316072 CH33 7/26/99 2:04 PM Page 599
The base argument is the radius of the cone’s base. The height argument is the height of
the cone. The slices argument is the number of flat bands drawn around the z-axis. The
stacks argument is the number of flat bands drawn about the z-axis. The center of the
base of the cone is placed at X=Y=Z=0 in the modeling coordinate system. The cone
“points” along the z-axis.
If you want to draw a torus, use either of the following GLUT functions:
void glutSolidTorus(GLdouble inner_radius,
GLdouble outer_radius,
GLint nsides, GLint rings);
void glutWireTorus(GLdouble inner_radius,
GLdouble outer_radius,
GLint nsides, GLint rings);
The inner_radius and outer_radius arguments are the inner and outer radii of the
torus. The nsides argument is the number of flat bands drawn for each radial section.
The rings argument is the number of radial bands.
Placing Objects in 3D Space Using X-Y-Z

Coordinates
Before placing objects in a 3D world, we need to make sure that OpenGL is in the model
mode. This is accomplished by calling:
glMatrixMode(GL_MODELVIEW);
We can translate the origin of the model world to coordinates X, Y, and Z by calling:
glTranslatef(GLfloat X, GLfloat Y, GLfloat Z);
One problem that we have is remembering where we have moved the origin. Fortunately,
there are OpenGL utility functions that push the entire state of OpenGL onto a stack, which
pulls the state of OpenGL off a stack. These functions are:
glPushMatrix();
glPopMatrix();
In practice, we usually push the “matrix” (for example, the state of the OpenGL engine),
perform drawing operations on one or more objects, then pop the “matrix” back off the
stack. The following example shows how to draw a sphere of radius equal to 1.0 at
X=10, Y=0.0, and Z=1.0:
glPushMatrix();
{
glColor4f(1.0, 0.0, 0.0, 1.0); // make the sphere red
glTranslatef(10.0, 0.0, 1.0); // translate the model
// coordinate system
glutSolidSphere(1.0, 40, 40); // Draw a very detailed sphere
} glPopMatrix();
Programming the User Interface
P
ART IV
600
3872316072 CH33 7/26/99 2:04 PM Page 600
As a matter of programming style, I enclose within curly braces any operations between
the “push” and the “pop”; this improves the readability of the code. In this example, we
also see a call to glColor4f that is used to specify the red, green, and blue drawing col-

ors (values are between 0.0 and 1.0). The fourth argument of glColor4f is the alpha
value; an alpha value of 0.0 makes a transparent object—not too useful—and a value of
1.0 makes an entirely opaque object. In the sample program, try changing the alpha
value in the calls in display to a value of 0.5 so that you can see through the rendered
objects.
Rotating an Object About Any or All of the
X-, Y-, and Z- Axes
Rotating objects created with the GLUT utilities for drawing simple shapes is a little more
complicated than changing an object’s X-Y-Z location. In the example program in dis-
play, we draw a central planet that is centered at X=Y=Z=0 and a small satellite that
orbits around the central planet. We will start by looking at the code for rotating the cen-
tral planet about the origin (for example., X=Y=Z=0) of the model coordinate system:
// push matrix, draw central planet, then pop matrix:
glPushMatrix();
{
glRotatef((GLfloat)planet_rotation_period, 0.0, 1.0, 0.0);
glColor4f(1.0, 0.0, 0.0, 1.0);
glutSolidSphere(1.0, 10, 8); // Draw the central planet
} glPopMatrix();
Here, we use a planet_rotation variable that is incremented each time the OpenGL
scene is drawn. This value ranges between 0.0 and 360.0 because the arguments to
glRotatef are in degrees, not radians. The last three arguments of glRotatef specify the
rotation axis. The arguments (0.0, 1.0, 0.0) specify that the angle of rotation is about the
positive y-axis. By slowly varying the value of planet_rotation_period between 0.0
and 360.0, the central planet slowly rotates.
The placement and rotation of the satellite is more complex because we want to both
translate its position and to rotate it. To do this, the code from the example program’s
display function is as follows:
// push matrix, draw satellite, then pop matrix:
glPushMatrix();

{
glRotatef((GLfloat)central_orbit_period, 0.0, 1.0, 0.0);
glTranslatef(1.9, 0.0, 0.0);
glRotatef((GLfloat)-satellite_rotation_period,
0.0, 1.0, 0.0);
glColor4f(0.0, 1.0, 0.0, 1.0);
glutSolidSphere(0.2, 5, 4); // Draw the orbiting satellite
} glPopMatrix();
OpenGL/Mesa Graphics Programming
C
HAPTER 33
601
33
OPENGL/MESA
GRAPHICS
PROGRAMMING
3872316072 CH33 7/26/99 2:04 PM Page 601
Initially, we call glRotatef to rotate the satellite about the origin—just as we did for the
central planet. It is important to realize that the satellite is centered at the origin until we
move it. After rotating the coordinate system, we call glTranslate to move it to model-
ing coordinates (1.9, 0.0, 0.0), and finally rotate the coordinate system once again to sim-
ulate the satellite’s orbit around the planet.
Figure 33.2 shows the orbits sample program running in the flat-shaded mode. The
background color was changed to white for this screen shot; the program on the CD-
ROM has a black background.
Programming the User Interface
P
ART IV
602
FIGURE 33.2

The orbits pro-
gram running
with flat shading.
Enabling the Use of Material Properties
The example program’s init function sets up the OpenGL environment. The following
function call configures the OpenGL engine to allow us to later use drawing colors:
glEnable(GL_COLOR_MATERIAL);
By default, objects are flat-shaded; init also configures the OpenGL engine to use
smooth-shading. When you press the Spacebar, the example program switches between
smooth- and flat-shading (and also cycles through one of three viewing positions). The
following function call requests a smooth shading drawing model:
glShadeModel(GL_SMOOTH);
We can also use glClearColor to change the default background color for the window.
The first three arguments are the red, green, and blue color values for the background;
3872316072 CH33 7/26/99 2:04 PM Page 602
the last argument specifies the alpha (or transparency) of the background. Here, to make
the background almost black, we use low values for the red, green, and blue background-
color components. Setting the background alpha to zero makes it totally transparent.
glClearColor(0.1, 0.1, 0.1, 0.0);
In Figure 33.1 and Figure 33.2, we set the background to white by passing the value 1.0
for the first three arguments of glClearColor. The example orbits.c on the CD-ROM
sets a black background, however.
Enabling Depth Tests
The example program’s init function sets up the OpenGL environment to use a depth test
to hide obscured objects from view. The following function call configures the OpenGL
engine to allow us to later use this depth-cueing:
glCullFace(GL_BACK);
glEnable(GL_DEPTH_TEST);
In addition to making these two calls, we need to add the GLUT_DEPTH option when set-
ting up the GLUT display mode in the main function:

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
Handling Keyboard Events
It is useful to be able to handle keyboard input in OpenGL graphics programs. Using the
GLUT library, this is easily done using a Callback function that is called with the key
value whenever any key on the keyboard is pressed. The sample program uses the fol-
lowing Callback function:
void key_press_callback(unsigned char key, int x, int y)
We will use the first argument key and ignore the last two arguments in the example pro-
gram. We register this Callback function in the main function by calling:
glutKeyboardFunc(key_press_callback);
Updating OpenGL Graphics for Animation
Effects
In the example program, we use a display Callback function that is called by the GLUT
library whenever the window needs to be redrawn. The function signature for display is:
void display(void)
OpenGL/Mesa Graphics Programming
C
HAPTER 33
603
33
OPENGL/MESA
GRAPHICS
PROGRAMMING
3872316072 CH33 7/26/99 2:04 PM Page 603
We register this Callback function in main by calling:
glutDisplayFunc(display);
The last thing that display does before returning is to request an immediate redraw
event by calling:
glutPostRedisplay();
This effectively causes the OpenGL engine and the GLUT event-handler to update the ani-

mation continually.
Sample Program Listing
We have seen pieces of most of the example program in the last few sections. In this sec-
tion, we will list out the entire file with a few additional comments. This example, as
seen in Listing 33.1, uses a single include file glut.h for using both OpenGL/Mesa and
the OpenGL utility functions.
Listing 33.1 THE orbits.c FILE
#include <GL/glut.h>
void init(void)
{
glClearColor(0.1, 0.1, 0.1, 0.0);
glEnable(GL_COLOR_MATERIAL);
glShadeModel(GL_SMOOTH);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glEnable(GL_DEPTH_TEST);
}
void display(void) {
static int central_orbit_period = 0;
static int planet_rotation_period = 0;
static int satellite_rotation_period = 0;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// push matrix, draw central planet, then pop matrix:
glPushMatrix();
{
glRotatef((GLfloat)planet_rotation_period, 0.0, 1.0, 0.0);
glColor4f(1.0, 0.0, 0.0, 1.0);
Programming the User Interface

P
ART IV
604
3872316072 CH33 7/26/99 2:04 PM Page 604
glutSolidSphere(1.0, 10, 8); // Draw the central planet
} glPopMatrix();
// push matrix, draw satellite, then pop matrix:
glPushMatrix();
{
glRotatef((GLfloat)central_orbit_period, 0.0, 1.0, 0.0);
glTranslatef(1.9, 0.0, 0.0);
glRotatef((GLfloat)-satellite_rotation_period,
0.0, 1.0, 0.0);
glColor4f(0.0, 1.0, 0.0, 1.0);
glutSolidSphere(0.2, 5, 4); // Draw the orbiting satellite
} glPopMatrix();
glutSwapBuffers();
central_orbit_period = (central_orbit_period + 2) % 360;
planet_rotation_period = (planet_rotation_period + 1) % 360;
satellite_rotation_period =
(satellite_rotation_period + 6) % 360;
glutPostRedisplay();
}
void reshape(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, (GLfloat)w/(GLfloat)h, 1.0, 20.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

gluLookAt(0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}
void cycle_view() {
static int count = 0;
static int shade_flag = 0;
if (++count > 2) {
count = 0;
shade_flag = 1 - shade_flag;
}
glLoadIdentity();
switch (count)
{
case 0:
gluLookAt(0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
break;
case 1:
gluLookAt(0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
break;
OpenGL/Mesa Graphics Programming
C
HAPTER 33
605
33
OPENGL/MESA
GRAPHICS
PROGRAMMING
continues
3872316072 CH33 7/26/99 2:04 PM Page 605
case 2:
gluLookAt(0.0, 0.5, -3.3, 1.0, 0.0, 0.0, -0.7, 0.2, 0.4);

break;
}
if (shade_flag == 0) glShadeModel(GL_SMOOTH);
else glShadeModel(GL_FLAT);
}
void key_press_callback(unsigned char key, int x, int y) {
switch (key)
{
case 27: /* escape character */
case ‘q’:
case ‘Q’:
exit(1);
default:
cycle_view();
break;
}
}
int main(int argc, char *argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow(argv[0]);
init();
glutKeyboardFunc(key_press_callback);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}

This simple sample program demonstrates how to animate simple geometric objects.
This program can be used as a foundation for writing simple 3D games that use only the
“canned” 3D shapes (such as spheres, cubes, cones, and so on), which are defined in the
OpenGL utility library. More information about OpenGL is available at
/>More information on Mesa can be found at:
/>Programming the User Interface
P
ART IV
606
Listing 33.1 CONTINUED
3872316072 CH33 7/26/99 2:04 PM Page 606
Special Programming
Techniques
PART
V
IN THIS PART
• Shell Programming with GNU bash 609
• Secure Programming 639
• Debugging: GNU gdb 687
3972316072 part5 7/26/99 2:30 PM Page 607
3972316072 part5 7/26/99 2:30 PM Page 608
IN THIS CHAPTER
• Why bash? 610
• bash Basics 610
• Using bash Variables 613
• Using bash Operators 615
• Flow Control 619
• Shell Functions 628
• Input and Output 629
• Command-line Processing 633

• Processes and Job Control 635
34
CHAPTER
Shell
Programming with
GNU
bash
by Kurt Wall
4072316072 CH34 7/26/99 2:28 PM Page 609
Within the first two days of becoming a Linux user in 1993, I wrote my first bash script,
a very kludgy effort to automate my backups. This anecdote illustrates how pervasive
shell scripting is in Linux. In this chapter, I will give you a firm foundation in the basics
of bash shell programming.
Why bash?
GNU’s bash (Bourne Again Shell, named in punning homage to the Bourne shell and its
author, Steven Bourne) is Linux’s default shell. Written by Brian Fox and maintained by
Chet Ramey, bash’s most popular features are its rich command-line editing facilities
and its job control abilities. From the programmer’s perspective, bash’s chief advantages
are its customizability and the complete programming environment it provides, including
function definitions, integer math, and a surprisingly complete I/O interface. As you
might expect of a Linux utility, bash contains elements of all of the popular shells,
Bourne, Korn, and C shell, as well as a few innovations of its own. As of this writing, the
current release of bash is 2.0.3. Most Linux distributions, however, use the tried and test-
ed 1.14 version. Nevertheless, I will cover bash version 2, pointing out features not
available in earlier versions as they arise, because version 2 is much closer to POSIX
compliance than version 1.
bash Basics
Throughout this chapter, I assume that you are comfortable using bash, so my discussion
focuses on bash’s features from the shell programmer’s perspective. By way of review,
however, I will refresh your memory of bash’s wildcard operators and its special charac-

ters.
Wilcards
bash’s wildcard operators are *, ?, and the set operators, [SET] and [!SET]. * matches
any string of characters and ? matches any single character. Suppose you have a directo-
ry containing the following files:
$ ls
zeisstopnm zic2xpm zipgrep zipsplit znew
zcmp zforce zip zipinfo zless
zdiff zgrep zipcloak zipnote zmore
The command ls zi* matches zic2xpm, zip, zipcloak, zipgrep,zipinfo, and
zipsplit, but ls zi? only matches zip.
Special Programming Techniques
P
ART V
610
4072316072 CH34 7/26/99 2:28 PM Page 610
The set operators allow you to use either a range or a disjoint set of characters as wild-
cards. To specify an inclusive range of characters, use a hyphen between characters. For
example, the set [a-o] includes all of the lowercase characters “a” through “o.” Use a
comma to indicate a disjoint set, such as the range of characters between “a” and “h” and
between “w” and “z”. In bash’s set notation, this range of characters could be specified
with “[a-h,w-z]”. If you want to look only for a few individual characters, such as all of
the English vowels, the notation “[aeiou]” would do. The “!” prefixing a set means to
include everything not in the set. So, an easy way to look for all of the consonants is to
write
[!aeiou].
The following examples illustrate using the set notation and also how you can use the
wildcard operators with sets. To list all of the files in the directory above whose second
letter is a vowel, you could write:
$ ls z[aeiou]*

This will match zeisstopnm, zip zipgrep, zipnote, zic2xpm,zipcloak, zipinfo, and
zipsplit. On the other hand, to list the files that have only consonants as their second
letter, use lsz[!aeiou]*, which matches zcmp, zdiff, zforce, zgrep, zless, zmore, and
znew. The command, ls *[0-9]* matches only those filenames containing at least one
numeric digit between zero and nine, which is zic2xpm in this case.
Brace Expansion
Brace expansion is a more general case of the filename globbing enabled by wildcard
characters. The basic format of an expression using brace expansion follows:
[preamble]{str1[,str2[, ]]}[postscript]
Each string inside the braces will be matched with the optional preamble and postscript.
For example, the following statement
$ echo c{ar,at,an,on}s
results in
cars cats cans cons
Because brace expressions can be nested, the preceding statement could be rewritten as
follows to obtain the same output:
$ echo c{a{r,t,n},on}s
Shell Programming with GNU bash
CHAPTER 34
611
34
SHELL
PROGRAMMING
WITH
GNU
BASH
4072316072 CH34 7/26/99 2:28 PM Page 611
Special Characters
The wildcard and set operators are four examples of bash’s special characters—charac-
ters that have a specific meaning to bash. Table 34.1 lists all of the special characters,

with a brief description of each one.
Table 34.1 bash SPECIAL CHARACTERS
Character Description
< Redirect input
> Redirect output
( Start subshell
) End subshell
| Pipe
\ Quote (escape) the next character
& Execute command in background
{ Start command block
} End command block
~ Home directory
` Command substitution
; Command separator
# Comment
‘ Strong quote
“ Weak quote
$ Variable expression
* String wildcard
? Single character wildcard
Input and output redirection should be familiar to you. Although I will discuss subshells
later in this chapter, it is important to note that all the commands between ( and ) are
executed in a subshell. Subshells inherit some of the environment variables, but not all of
them. This behavior is different from commands in a block (blocks are delimited by {
and }), which are executed in the current shell and thus retain all of the current environ-
ment.
The command separator, ;, allows you to execute multiple bash commands on a single
line. But more importantly, it is the POSIX-specified command terminator.
Special Programming Techniques

P
ART V
612
4072316072 CH34 7/26/99 2:28 PM Page 612
The comment character, #, causes bash to ignore everything from the character to the
end of the line. The difference between the strong quote and weak quote characters,
‘ and “, respectively, is that the strong quote forces bash to interpret all special charac-
ters literally; the weak quote only protects some of bash’s special characters from
interpretation as special characters.
Using bash Variables
As you might expect, bash, has variables. Usually they are character strings, but bash
also has special facilities for dealing with numeric (integer) values, too. To assign a value
to a variable, use the following syntax:
varname=value
To obtain a variable’s value, you can use one of the following two formats:
$varname
${varname}
The second form, ${varname}, is the more general format and also more difficult to type.
However, you must use it to distinguish variables from trailing letters, digits, or under-
scores. For example, suppose you have a variable named MYNAME and you want to display
its value followed by an underscore character. You might be tempted to try the following
command:
$ echo $MYNAME_
but this attempt will fail because bash will attempt to print the value of the variable
MYNAME_, not MYNAME. In this situation, you must use the second syntax, as illustrated
below:
$ echo ${MYNAME}_
Listing 34.1 illustrates this subtlety.
Listing 34.1 VARIABLE SYNTAX
1 #!/bin/bash

2 # Listing 34.1
3 # varref.sh - Variable syntax
4 #############################
5
6 MYNAME=”Kurt Wall”
7 echo ‘${MYNAME}_ yields: ‘ ${MYNAME}_
8 echo ‘$MYNAME_ yields: ‘ $MYNAME_
Shell Programming with GNU bash
CHAPTER 34
613
34
SHELL
PROGRAMMING
WITH
GNU
BASH
4072316072 CH34 7/26/99 2:28 PM Page 613
The strong quotes on lines 7 and 8 prevent bash from expanding the variables. As you
can see in the script’s output below, $MYNAME_ is empty and prints nothing, but
${MYNAME}_ produces the expected output.
$ ./varref.sh
${MYNAME}_ yields: Kurt Wall_
$MYNAME_ yields:
In addition to user-defined variables, bash comes with numerous built-in or predefined
variables. Many of these are environment variables, such as $BASH_VERSION or
$DIRSTACK. Another subset of predefined variables are the positional parameters that con-
tain the command-line arguments, if any, passed to the script (shell functions, covered
later, also accept arguments). The parameter
“$0” contains the name of the script; the
actual parameters begin at “$1” (if there are more than nine, you must use the ${} syn-

tax—for example, “${10}” to obtain their values). The positional parameters passed to
each script or function are local and read-only to those functions. However, other vari-
ables declined in a function are global by default, unless you declare them otherwise
with the local keyword.
bash also has three special positional parameters, “$#”, “$@”,and “$#”. “$#” evaluates to
the number of positional parameters passed to a script or a function (not counting “$0”).
“$*” is a list of all the positional parameters, except “$0”, formatted as a single string
with each parameter separated by the first character the $IFS, the internal field separator
(another bash environment variable). “$@”, on the other hand, is all of the positional
parameters presented as N separate double-quoted strings.
What is the difference between “$*” and “$@”? And why the distinction? The difference
allows you to treat command-line arguments in two ways. The first format, “$*”, because
it is a single string, can be displayed more flexibly without requiring a lot of shell code
to do so. “$@”, on the other hand, allows you to process each argument individually
because its value is N separate arguments. Listing 34.2 illustrates the differences between
“$#” and “$@”.
Listing 34.2 POSITIONAL PARAMETERS
1 #!/bin/bash
2 # Listing 34.2
3 # posparm.sh - Using positional parameters
4 ##########################################
5
6 function cntparm
7 {
8 echo -e “inside cntparm $# parms: $*\n”
9 }
Special Programming Techniques
P
ART V
614

4072316072 CH34 7/26/99 2:28 PM Page 614

×