1 / 29

Using Motif with C++

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

adeola
Download Presentation

Using Motif with C++

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 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)

  2. 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

  3. Motif and Xt Widget Classes Label PushButton . . . DrawnButton Primitive . . Text RowColumn Core Constraint Manager Form BulletinBoard Composite . . . Shell . . .

  4. Approaches to Mixing Motif and C++ • Do not use OO features of C++ and just write applications in a style similar to C • misses out on the benefit of OOP • Wrap each Motif widget class in a C++ class • public domain and commercial wrapper sets are available • disadvantages: additional code, support, portability • 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

  5. Example: A Tic-Tac-Toe Program Board Game game canvas: Widget display: Display gc: GC state: CharArray count: Integer playerX: Player board game game board startd endd summaryd Main Dialog shell: Widget app: XtAppContext dialog: Widget *

  6. 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

  7. 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

  8. The Game Class Recall that Game also has a Board as an attribute as shown in the class diagram.

  9. The Board Class Recall that Board also has a Game as an attribute as shown in the class diagram.

  10. 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

  11. 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.)

  12. BoardInfo Constructor At the time we construct a new board, we will also add the callbacks for it: 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, ...); } Since the callbacks are static class methods, they must be qualified by the class name and begun with ``&''.

  13. 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); }

  14. 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

  15. Solution: Using Client Data Recall the XtAddCallback function prototype: void XtAddCallback ( Widget widget, //1 const String callbackName, //2 XtCallbackProc proc, //3 XtPointer clientData); //4 Recall the actual callback function prototype: void <funcname> (Widget w, // 1 XtPointer clientData, // 2 XtPointer callData); // 3

  16. 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.

  17. 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

  18. 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

  19. BoardInfo Class Declaration class BoardInfo { private: Game game; // the game state Widget canvas; // drawing area Display* display; // display device for app GC gc; // graphics context public: 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 debugging private: 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 }

  20. Draw Board Callback Methods void 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); } }

  21. Macros to Handle C++ Callbacks Since it's a hassle to define two methods for every callback, we can define macros to simplify both the declaration and the implementation of 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)

  22. 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)

  23. 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 context public: 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 debugging private: DECL_CALLBACK(drawBoard); // draw board when it is exposed DECL_CALLBACK(boardClick); // get board coords when clicked }

  24. Callback Implementation Macro We can also define a macro to simplify the implementation (definition) of callbacks: #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) This macro will hide the details of the communication between the static and nonstatic class methods.

  25. 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.h in the Tic-Tac-Toe directory.

  26. 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

  27. 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 holding information 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.

  28. 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

  29. 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 will translate pixel coordinates to game board coordinates in a 3 x 3 character array. For example: (150,150) -> (1,1)

More Related