Starting programming with the X-Windows system: Part Two.

Abstract

This document continues the tutorial started in part one. In this tutorial we will cover menus, popups and some basic XLib functions.

Menu.c

The first program displays a text window with pulldown menus above it:
Menu.c:

#include <stdio.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include <Xm/MainW.h>
#include <Xm/RowColumn.h>
#include <Xm/TextF.h>

void file_pulldown_cb(w, data, event )
Widget w;
int data;
XtPointer event;
{
	fprintf ( stdout, "data = %d\n", data );

	if (data == 5 )
		exit(0);
}


main(argc, argv)
int	argc;
char	*argv[];
{
	XtAppContext	app;

	Widget	toplevel_shell, main_window, canvas_field, top_menubar, file_pulldown;
	XmString	file, 	new, new_acc, open, open_acc, save, save_acc, saveas, close, quit, quit_acc;

	toplevel_shell = XtVaAppInitialize(&app, "Menus", NULL, 0, 
		&argc, argv, NULL, NULL);

	main_window = XtVaCreateManagedWidget("main_window", xmMainWindowWidgetClass, toplevel_shell,
				NULL);

	file = XmStringCreateSimple("File");
	top_menubar = XmVaCreateSimpleMenuBar(main_window, "top_menubar", 
				XmVaCASCADEBUTTON, file, 'F',
				NULL );
	XmStringFree(file);

	new = XmStringCreateSimple("New");
	new_acc = XmStringCreateSimple("Ctrl+N");

	open = XmStringCreateSimple("Open...");
	open_acc = XmStringCreateSimple("Ctrl+O");

	save = XmStringCreateSimple("Save");
	save_acc = XmStringCreateSimple("Ctrl+S");

	saveas = XmStringCreateSimple("Save As...");
	close = XmStringCreateSimple("Close");

	quit = XmStringCreateSimple("Quit");
	quit_acc = XmStringCreateSimple("Ctrl+C");

	file_pulldown = XmVaCreateSimplePulldownMenu(top_menubar, "file_pulldown", 0, file_pulldown_cb,
				XmVaPUSHBUTTON, new, 'N', "Ctrl<Key> n", new_acc,
				XmVaPUSHBUTTON, open, 'O', "Ctrl<Key> o", open_acc,
				XmVaPUSHBUTTON, save, 'S', "Ctrl<Key> s", save_acc,
				XmVaPUSHBUTTON, saveas, 'A', NULL, NULL,
				XmVaSEPARATOR,
				XmVaPUSHBUTTON, close, 'C', NULL, NULL,
				XmVaPUSHBUTTON, quit, 'Q', "Ctrl<Key> c", quit_acc,
				NULL );
	XmStringFree(new);
	XmStringFree(new_acc);
	XmStringFree(open);
	XmStringFree(save);
	XmStringFree(saveas);
	XmStringFree(close);
	XmStringFree(quit);

	XmStringFree(new_acc);
	XmStringFree(open_acc);
	XmStringFree(save_acc);
	XmStringFree(quit_acc);

	XtManageChild(top_menubar);

	XtRealizeWidget(toplevel_shell);
	XtAppMainLoop(app);
}


See Part One of this workshop www page for an example of how to compile the source code.

Discussion Of Menu.c

In main() - a MainWindow widget containing a menu-bar is created. A MainWindow widget is used as the highest level container for applications which wish to conform to the motif style.

The menubar is created using the XmVaCreateSimpleMenuBar() function. The returned widget must be managed with XtManageChild().

Another function is used to make pulldown menus inside the menubar - XmVaCreateSimplePulldownMenu().

The variable argument functions used have a set of arguments defined which can be used to specify of what class we want the child widgets to be. (XmVaCASCADEBUTTON, XmVaPUSHBUTTON etc. ) This is instead of creating each sub-widget explicitly with a seperate function.

Experiment with the menu program - try adding more pulldown menus so that you get a menu like those seen is full blown applications.

    Project	Edit
    Open...	Cut
    Save	Copy
    Save as...	Paste
    Print...
    Quit

Popup.c

The next program demonstrates the use of popups. Popups allow more than one main window per application.
Popup.c:

#include <X11/StringDefs.h>
#include <Xm/Xm.h>
#include <Xm/PushB.h>

Widget toplevel;

void close_window(w, client_data, event_data)
{
Widget popup = (Widget)client_data;

    XtPopdown(popup);
    XtDestroyWidget(popup);
}

void pop(w, client_data, event_data)
Widget w;
XtPointer client_data;
XtPointer event_data;
{
Widget a, button, popup;

    popup = XtVaCreatePopupShell("Popup", transientShellWidgetClass, toplevel, NULL);
    button = XtVaCreateManagedWidget("Close", xmPushButtonWidgetClass, popup,
        NULL);

    XtAddCallback(button, XmNactivateCallback, close_window, (XtPointer)popup);

    XtPopup(popup, XtGrabNone);
}


main(argc, argv)
int argc;
char *argv[];
{
    Widget button;
    XtAppContext  app;
    XmString label;

    toplevel = XtVaAppInitialize(&app, "Popup", NULL, 0,
        &argc, argv, NULL, NULL);

    label = XmStringCreateSimple("Make popup"); 
    button = XtVaCreateManagedWidget("pushme", xmPushButtonWidgetClass, toplevel,
        XmNlabelString, label,
        NULL);

    XmStringFree(label);
    XtAddCallback(button, XmNactivateCallback, pop, NULL);

    XtRealizeWidget(toplevel);
    XtAppMainLoop(app);
}


Discussion Of Popup.c

This is quite a short program which displays a single button in a window. When the button is pressed, a new window will popup. The popups each contain a single "close" button which removes the window from the display.

The popups are created with the toplevel shell widget as the parent. This means that if you iconize the toplevel shell, then all associated popup windows will also iconize.

The line that actually causes the popup-window to be displayed is:

	XtPopup(popup, XtGrabNone);
If XtGrabNone is changed to "XtGrabExclusive", then that window is made to be the only one that accepts events and you won't be able to press the "make popup" button on the main shell window until you have closed the popup. This is useful for getting information from the user before the application can proceed e.g. getting a save filename before saving a file.

Draw.c

The next program demonstates event-handlers and some basic XLib functions for drawing lines and rectangles:
draw.c:

#include <X11/StringDefs.h>
#include <Xm/Xm.h>
#include <Xm/DrawingA.h>

void redraw(w, calldata, ev)
Widget w;
XtPointer calldata;
XtPointer ev;
{
GC gc = (GC)calldata;
Window win = XtWindow(w);
Display *d = XtDisplay(w);

    XDrawLine(d, win, gc, 0, 0, 150, 150);
    XDrawRectangle(d, win, gc, 150, 150, 200, 100);
    XFillRectangle(d, win, gc, 200, 200, 100, 50);
}

main(argc, argv)
int     argc;   
char    *argv[];
{
Widget toplevel, viewport;
XGCValues values;  
GC gc;
XtAppContext appcon;

    toplevel = XtVaAppInitialize(&appcon, "Draw", NULL, 0, &argc, argv, NULL, 0);

    viewport = XtVaCreateManagedWidget("viewport", xmDrawingAreaWidgetClass, toplevel,
        XmNwidth, 400,
        XmNheight, 300,
        NULL);

    XtVaGetValues(viewport, 
        XtNforeground, &values.foreground,
        XtNbackground, &values.background,
        NULL);

    gc = XtGetGC(viewport, GCForeground | GCBackground, &values);

    XtAddEventHandler(viewport, ButtonPressMask , FALSE, redraw, (XtPointer)gc);

    XtRealizeWidget(toplevel);

    XtAppMainLoop(appcon);
}       


Discussion Of Draw.c

The program simply displays a single window. If the mouse is clicked inside the window then a line, a box and a filled-rectangle are drawn.

Notice that, if another window is dragged over this one, the drawing is lost. Refresh the window by clicking in the window once again.

The button-press is detected by the event-handler "XtAddEventHandler()". This tells the application to call the redraw function whenever a button is pressed in the viewport widget.

Event-handlers work in the same way as callbacks but allow the programmer to specify the exact event to react to. The event-masks can be added together so that one event handler can wait for more than one different event type (e.g. if the programmer wanted to watch for mouse-button presses AND key-presses - "KeyPressMask | ButtonPressMask" would be used.

(for a full list of event-masks, see the Event Definitions in /usr/include/X11/X.h).

It is up to the application to refresh the picture when it needs to be redrawn. This can be done by watching for expose events. Expose events occur when parts of the window get corrupted.

Change "ButtonPressMask" to "ExposureMask" - this will tell the application to automatically draw into the window every time it needs to.

Three XLib functions are used in this program XDrawLine, XDrawRectangle, XFillRectangle. All of these functions use the same first three parameters.

The first parameter is the display - this is a handle to the X-display to use. Normally this is not referenced from Xt but for use with XLib functions, it can be retrieved by using XtDisplay on a widget.

Widgets are built from lower-level XLib Windows. The widget being used to draw into is the DrawArea widget. This is a composite widget which can also be used for graphics. The Window-id of a widget is obtained by using the XtWindow function.

The third parameter is the Graphics Context. This is a data-structure which is used to store information about the current graphics setting. It stores current colours (foreground and background), line-style (dotted etc), line-width etc.

The lines program needs to find the default foreground and background so that the lines are drawn in the correct colours. This is done by reading the XtNforeground, XtNbackground resource values from the main widget. The values are copied into the XGCValues structure and used in the creation of the graphics context. If you wish to change the line-width or some other attribute then you would do this in a similar way.

Try adding: values.line_style = LineOnOffDash;

before the XtGetGC function, and change the value mask in the XtGetGC function to "GCForeground | GCBackground | GCLineStyle".

For more GC functions, see the xman page for XCreateGC.

Back To X-Windows Workshop Part One


Sean Butler
sean@comp.lancs.ac.uk
Doc Status: Under Development...
This page is based upon notes originally written by AAI/AI-ED Group Computing Department Lancaster University