using motif with c++ ● x and motif are written in c ● c++ is backward compatible with c,...

29
Using Motif with C++ X and Motif are written in C C++ is backward compatible with C, provided that the function prototypes are correctly given So it is straightforward to call X and Motif from C++ programs But all of our examples so far just look like C programs with strong typing (no class-oriented design)

Upload: baldwin-lamb

Post on 16-Dec-2015

213 views

Category:

Documents


0 download

TRANSCRIPT

Using Motif with C++

● X and Motif are written in C● C++ is backward compatible with C, provided

that the function prototypes are correctly given● So it is straightforward to call X and Motif from

C++ programs● But all of our examples so far just look like C

programs with strong typing (no class-oriented design)

Classes in C++ and Motif

● Motif is designed with a class hierarchy and inheritance in mind (see next slide)

● But the approaches to classes used by Motif and C++ are incompatible:– C++ programmers often work with object-oriented

libraries by creating new specialized subclasses of the library

– However, although Motif widgets are described as classes, there is no way to create a C++ class that is a subclass of a Motif widget

Motif and Xt Widget Classes

Core

Composite

Primitive

Constraint

Shell

Text

Label

.

.

.

PushButton

DrawnButton

.

.

Manager

. . .

. . .

RowColumn

Form

BulletinBoard

Approaches to Mixing Motif and C++1 Do not use OO features of C++ and just write

applications in a style similar to C➢ misses out on the benefit of OOP

2 Wrap each Motif widget class in a C++ class➢ public domain and commercial wrapper sets are

available➢ disadvantages: additional code, support, portability

3 Create higher-level user interface components in C++ which use Motif widgets as data attributes

➢ use C++ classes to describe software architecture➢ do not force Motif widgets to be C++ classes➢ we will emphasize this approach

Example: A Tic-Tac-Toe Program

Boardcanvas: Widgetdisplay: Displaygc: GC

Gamestate: CharArraycount: IntegerplayerX: Player

Dialogdialog: Widget

Mainshell: Widgetapp: XtAppContext

board game

board

game

game

startdenddsummaryd

*

Notes on TTT Class Diagram● Only the class attributes are shown● All classes have singleton members except Dialog

● The role of Motif widgets is to be attributes of C++ classes:– Main::shell, a shell widget– Board::canvas, an XmDrawingArea widget– Dialog::dialog, an XmDialogShell widget

Notes on TTT Class Diagram (cont'd)

● The Board class manages the game display:– Detects clicks, displays marks– Needs the game object to access internal state

● The Game class runs the game and keeps an internal representation– Needs the board object to update the display

● The Dialog class manages the start, end, and summary dialogs– Needs the game object to return results of dialog

● The Main class creates all of the above and manages the Xt connection

The Game Class

Recall that Game also has a Board as an attributeas shown in the class diagram.

The Board Class

Recall that Board also has a Game as an attributeas shown in the class diagram.

Board Class Methods

● drawGrid, drawX, and drawO are the subject of a lab exercise

● manage and unManage allow for exposing and hiding the drawing area

● asciiDisplay is for debugging purposes● drawBoardCallback responds to any expose

event generated for the drawing area● boardClickCallback responds to any

mouse input event generated for the drawing area

Callbacks Available for Drawing Areas● XmNexposeCallback

– Triggered when part of the widget is exposed.– We must provide a callback that will ``repaint'' the

tic-tac-toe board when this is triggered● XmNinputCallback

– Triggered when the widget receives a keyboard or mouse event.

– We must provide a callback that will determine the coordinates of the clicked square and ask the board to draw an appropriate mark there

● XmNresizeCallback– Triggered when the widget is resized. (We will not act

on resizings.)

BoardInfo Constructor

BoardInfo::BoardInfo(Widget parent, Game g){ ... canvas = XtVaCreateManagedWidget ( "canvas", xmDrawingAreaWidgetClass, parent, XmNheight, 300, XmNwidth, 300, NULL ); ... XtAddCallback(canvas, XmNexposeCallback,

&BoardInfo::drawBoardCallback, ...); XtAddCallback(canvas, XmNinputCallback,

&BoardInfo::boardClickCallback, ...);}

At the time we construct a new board, we will also addthe callbacks for it:

Since the callbacks are static class methods, they must bequalified by the class name and begun with ``&''.

Writing the Draw Board Callback

When an expose event occurs for the drawing area,the action called for is straightforward:

drawGrid();

for (Integer x = 0; x < 3; x++) // Draw the Xs & Os for (Integer y = 0; y < 3; y++) { if (game->getState(x,y) == 'X')

drawX(x, y); else if (game->getState(x,y) == 'O')

drawO(x, y); }

Writing the Draw Board Callback (cont'd)

Q: So, will this work?

void BoardInfo::drawBoardCallback(Widget, XtPointer, XtPointer) { drawGrid(); for (Integer x = 0; x < 3; x++) // Draw the Xs & Os for (Integer y = 0; y < 3; y++) { if (game->getState(x,y) == 'X')

drawX(x, y); else if (game->getState(x,y) == 'O')

drawO(x, y); }}

A: No, because: - callbacks are straight C functions and thus are static - static methods cannot access objects like game

Solution: Using Client Data

Recall the XtAddCallback function prototype:

void XtAddCallback (Widget widget, //1const String callbackName, //2XtCallbackProc proc, //3XtPointer clientData); //4

Recall the actual callback function prototype:

void <funcname> (Widget w, // 1 XtPointer clientData, // 2 XtPointer callData); // 3

Using Client Data

● The clientdata argument is specified by the call to XtAddCallback. The system remembers it and passes it along to the callback when appropriate.

● XtPointer is a generic pointer type defined by Xt. It can be type cast to any pointer type.

● So use it to obtain a pointer to an object that knows about the game object, i.e., the board object.

Strategy● When adding the callback to draw the grid,

include the this pointer (a pointer to this board) as clientData

● Write the (static) callback to use the clientData to get the board object for which the callback was added

● The (static) callback does nothing but call a nonstatic method on the board object that does the work of drawing the grid

BoardInfo Constructor Again

BoardInfo::BoardInfo(Widget parent, Game g){ ... canvas = XtVaCreateManagedWidget ( "canvas", xmDrawingAreaWidgetClass, parent, XmNheight, 300, XmNwidth, 300, NULL ); ... XtAddCallback(canvas, XmNexposeCallback,

&BoardInfo::drawBoardCallback, (XtPointer) this); XtAddCallback(canvas, XmNinputCallback,

&BoardInfo::boardClickCallback, (XtPointer) this);}

– Since the clientData argument is of type XtPointer, the this pointer, which is of type BoardInfo*, must be type cast

BoardInfo Class Declarationclass BoardInfo {private: Game game; // the game state Widget canvas; // drawing area Display* display; // display device for app GC gc; // graphics contextpublic: BoardInfo(Widget parent, Game g); void drawGrid(); // draw game grid void drawX ( Integer x, Integer y); // Draw an X at (x,y) void drawO ( Integer x, Integer y); // Draw an O at (x,y) void manage(); // expose drawing area void unManage(); // hide the drawing area void asciiDisplay(); // display for debuggingprivate: static void drawBoardCallback(Widget, XtPointer clientData, XtPointer); void drawBoard(); // draw board when it is exposed

static void boardClickCallback(Widget, XtPointer clientData, XtPointer); void boardClick(); // get board coords when clicked}

Draw Board Callback Methodsvoid BoardInfo::drawBoardCallback(Widget, XtPointer clientData, XtPointer) { Board * b = (Board *)clientData; b->drawBoard();}

void BoardInfo::drawBoard() { drawGrid(); for (Integer x = 0; x < 3; x++) // Draw the Xs & Os for (Integer y = 0; y < 3; y++) { if (game->getState(x,y) == 'X')

drawX(x, y); else if (game->getState(x,y) == 'O')

drawO(x, y); }}

Macros to Handle C++ Callbacks

#define name2(a,b) a ## b

#define DECL_CALLBACK(func) \ private: \ static void name2(func,Callback) (Widget, \ XtPointer, \ XtPointer); \ protected: \ virtual void func ( Widget, XtPointer)

Since it's a hassle to define two methods for everycallback, we can define macros to simplify both thedeclaration and the implementation of callbacks.

Callback Declaration Macro● Whenever the preprocessor sees name2(a,b),

it plugs in the concatenation of a and b:– For example, name2(drawBoard,Callback)

becomes drawBoardCallback● Whenever the preprocessor sees DECL_CALLBACK(func) it plugs in two method declarations.– For example, DECL_CALLBACK(drawBoard)

becomes: private: \ static void drawBoardCallback (Widget, \ XtPointer, \ XtPointer); \ protected: \ virtual void drawBoard ( Widget, XtPointer)

BoardInfo Class Declaration Again

class BoardInfo {private: Game game; // the game state Widget canvas; // drawing area Display* display; // display device for app GC gc; // graphics contextpublic: BoardInfo(Widget parent, Game g); void drawGrid(); // draw game grid void drawX ( Integer x, Integer y); // Draw an X at (x,y) void drawO ( Integer x, Integer y); // Draw an O at (x,y) void manage(); // expose drawing area void unManage(); // hide the drawing area void asciiDisplay(); // display for debuggingprivate: DECL_CALLBACK(drawBoard); // draw board when it is exposed DECL_CALLBACK(boardClick); // get board coords when clicked}

Callback Implementation Macro

#define IMPL_CALLBACK(cls, func) \ void cls::name2(func,Callback) (Widget w, \ XtPointer clientData, \ XtPointer callData) \ { \ ((cls *)clientData)->func(w, callData); \ } \ \ void cls::func(Widget w, XtPointer callData)

We can also define a macro to simplify the implementation (definition) of callbacks:

This macro will hide the details of the communicationbetween the static and nonstatic class methods.

Simplified Implementation of Draw Board Callback

IMPL_CALLBACK(BoardInfo, drawBoard) { drawGrid();

for (Integer x = 0; x < 3; x++) for (Integer y = 0; y < 3; y++) { if (game->getState(x,y) == 'X')

drawX(x, y); else if (game->getState(x,y) == 'O')

drawO(x, y); }}

The callback macros are in the file CallbackMacros.hin the Tic-Tac-Toe directory.

Board Click Callback

● When a mouse click is detected in the board's drawing area, what must be done?– The (x,y) pixel coordinates of the click location must

be determined– From these coordinates the indices of the 3x3

character array (internal representation) must be calculated

– The game object must be told to process a move given these indices

● To retrieve the (x,y) pixel coordinates, we must make use of the drawing area widget's call data

Using Call Data

Recall the callback function prototype:

void <funcname> (Widget w, // 1 XtPointer clientData, // 2 XtPointer callData); // 3

The third argument is a pointer to a data structure holdinginformation from the widget. At a minimum:

typedef struct { int reason; // coded reason for callback XEvent *event; // event causing callback} XmAnyCallbackStruct

This structure can be extended in widget-specific ways.

Using Call Data (cont'd)

● When call data is needed in a callback, first do:– XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *) callData;

● Now we can get the general event causing the callback with:– cbs->event

● From the general event we can get the specific button event:– cbs->event->xbutton

● From the button event we can get the x and y pixel coordinates:– cbs->event->xbutton.x– cbs->event->xbutton.y

Board Click Callback Implementation// get board coordinates when clicked and process move

IMPL_CALLBACK(BoardInfo, boardClick) { XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *) callData; Integer x = (Integer) floor(cbs->event->xbutton.x/100); Integer y = (Integer) floor(cbs->event->xbutton.y/100);

game->processMove(x,y);}

Since a tic-tac-toe square is 100 x 100 pixels, this willtranslate pixel coordinates to game board coordinates ina 3 x 3 character array. For example:

(150,150) -> (1,1)