// // File "sdraw.cpp" // UDP example: synchronous drawing in 2 windows // // Two such programs must be started simultaniously. // User can draw curves in a window of any program by mouse, // a curve color depends on mouse button (left - red, right - blue, // middle - green). The program sends an information of all actions // to the pair program using UDP protocol, the pair program reproduces // all actions in its windw. In result, all drawings appear // synchronously in windows of both programs // // Command line to start a program: // ./sdraw [port] [peerIP] [peerPort] // where // port is a port to receive data, // peerIP is an IP-number of pair program, // peerPort is a port number, used by pair program. // For example, the pair of programs may be started locally // with the following command lines: // ./sdraw 1234 localhost 4321 & // ./sdraw 4321 localhost 1234 & // #include #include #include #include #include #include #include #include #include #include "gwindow.h" const int DX = 80; const int DY = 80; const int LINE_WIDTH = 4; class Stroke { public: int color; std::vector points; }; class Action { public: enum { START_CURVE, DRAW_CURVE, END_CURVE }; int type; int color; I2Point point; Action(): type(START_CURVE), color(0), point() {} Action(int t, int c, const I2Point& pnt): type(t), color(c), point(pnt) {} }; //-------------------------------------------------- // Definition of our main class "MyWindow" // class MyWindow: public GWindow { // Our main class public: bool finished; std::vector strokes; Stroke myDrawing; bool myDrawingActive; Stroke peerDrawing; bool peerDrawingActive; int sock; struct sockaddr_in myAddr; struct sockaddr_in peerAddr; MyWindow(); void sendAction(const Action& a) const; void processAction(const Action& a, bool myAction = true); void drawStroke(const Stroke& str); virtual void onExpose(XEvent& event); virtual void onKeyPress(XEvent& event); virtual void onButtonPress(XEvent& event); virtual void onButtonRelease(XEvent& event); virtual void onMotionNotify(XEvent& event); virtual bool onWindowClosing(); }; //---------------------------------------------------------- // Implementation of class "MyWindow" MyWindow::MyWindow(): finished(false), strokes(), myDrawing(), myDrawingActive(false), peerDrawing(), peerDrawingActive(false), sock(-1) { memset(&myAddr, 0, sizeof(myAddr)); memset(&peerAddr, 0, sizeof(peerAddr)); } // // Process the Expose event: draw in the window // void MyWindow::onExpose(XEvent& /* event */) { // Erase a window setForeground(getBackground()); fillRectangle(m_RWinRect); for (unsigned int i = 0; i < strokes.size(); ++i) { drawStroke(strokes[i]); } if (myDrawingActive) drawStroke(myDrawing); if (myDrawingActive) drawStroke(myDrawing); if (peerDrawingActive) drawStroke(peerDrawing); } const char* const strokeColors[3] = { "red", "SeaGreen", "blue" }; void MyWindow::drawStroke(const Stroke& str) { if (str.points.size() == 0) return; int c = str.color % 3; setForeground(strokeColors[c]); moveTo(str.points[0]); for (unsigned int i = 0; i < str.points.size(); ++i) { drawLineTo(str.points[i]); } } // // Process the KeyPress event: // if "q" is pressed, then close the window // void MyWindow::onKeyPress(XEvent& event) { KeySym key; char keyName[256]; int nameLen = XLookupString(&(event.xkey), keyName, 255, &key, 0); printf("KeyPress: keycode=0x%x, state=0x%x, KeySym=0x%x\n", event.xkey.keycode, event.xkey.state, (int) key); if (nameLen > 0) { keyName[nameLen] = 0; printf("\"%s\" button pressed.\n", keyName); if (keyName[0] == 'q') { // quit => close window if (sock >= 0) { close(sock); sock = (-1); } destroyWindow(); } } } // Process mouse click void MyWindow::onButtonPress(XEvent& event) { int x = event.xbutton.x; int y = event.xbutton.y; unsigned int mouseButton = event.xbutton.button; printf("Mouse click: x=%d, y=%d, button=%d\n", x, y, mouseButton); Action a( Action::START_CURVE, 0, I2Point(x, y) ); if (mouseButton == Button2) { a.color = 1; } else if (mouseButton == Button3) { a.color = 2; } processAction(a, true); sendAction(a); } void MyWindow::onButtonRelease(XEvent& event) { int x = event.xbutton.x; int y = event.xbutton.y; Action a( Action::END_CURVE, 0, I2Point(x, y) ); processAction(a, true); sendAction(a); } void MyWindow::onMotionNotify(XEvent& event) { if (!myDrawingActive) return; int x = event.xbutton.x; int y = event.xbutton.y; Action a( Action::DRAW_CURVE, 0, I2Point(x, y) ); processAction(a, true); sendAction(a); } bool MyWindow::onWindowClosing() { if (sock >= 0) { close(sock); sock = (-1); } return true; } void MyWindow::sendAction(const Action& a) const { sendto( sock, &a, sizeof(a), 0, // flags (const sockaddr*) &peerAddr, sizeof(peerAddr) ); } void MyWindow::processAction(const Action& a, bool myAction /* = true */) { Stroke* curve = &myDrawing; bool* drawingActive = &myDrawingActive; if (!myAction) { curve = &peerDrawing; drawingActive = &peerDrawingActive; } if (a.type == Action::START_CURVE) { if (*drawingActive && curve->points.size() > 0) { strokes.push_back(*curve); curve->points.clear(); } curve->color = a.color; curve->points.push_back(a.point); *drawingActive = true; drawStroke(*curve); } else if (a.type == Action::DRAW_CURVE) { if (!*drawingActive) return; curve->points.push_back(a.point); drawStroke(*curve); } else if (a.type == Action::END_CURVE) { if (*drawingActive && curve->points.size() > 0) { strokes.push_back(*curve); curve->points.clear(); } *drawingActive = false; } } // // End of class MyWindow implementation //---------------------------------------------------------- ///////////////////////////////////////////////////////////// // Main: initialize X, create an instance of MyWindow class, // and start the message loop int main(int argc, char *argv[]) { // Initialize X stuff if (!GWindow::initX()) { printf("Could not connect to X-server.\n"); exit(1); } MyWindow w; const char* windowTitle = "Synchro Drawing"; short myPort = 12345; if (argc > 1) myPort = (short) atoi(argv[1]); const char* peerHost = "localhost"; if (argc > 2) peerHost = argv[2]; short peerPort = 12346; if (argc > 3) peerPort = (short) atoi(argv[3]); // Create a socket w.sock = socket(AF_INET, SOCK_DGRAM, 0); if (w.sock < 0) { perror("Cannot create a socket"); exit(1); } // Fill in the address structure containing self address w.myAddr.sin_family = AF_INET; w.myAddr.sin_port = htons(myPort); w.myAddr.sin_addr.s_addr = htonl(INADDR_ANY); // Bind a socket to the address int res = bind(w.sock, (struct sockaddr*) &w.myAddr, sizeof(w.myAddr)); if (res < 0) { perror("Cannot bind a socket"); exit(1); } // Set the "LINGER" timeout to zero struct linger linger_opt = { 1, 0 }; // Linger active, timeout 0 setsockopt( w.sock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(linger_opt) ); // Fill in the peer address // Resolve the address (convert from symbolic name to IP number) struct hostent *host = gethostbyname(peerHost); if (host == NULL) { perror("Cannot define host address"); exit(1); } w.peerAddr.sin_family = AF_INET; w.peerAddr.sin_port = htons(peerPort); // Print a resolved address of server (the first IP of the host) printf( "peer addr = %d.%d.%d.%d, port %d\n", host->h_addr_list[0][0] & 0xff, host->h_addr_list[0][1] & 0xff, host->h_addr_list[0][2] & 0xff, host->h_addr_list[0][3] & 0xff, (int) peerPort ); // Write resolved IP address of a server to the address structure memmove(&(w.peerAddr.sin_addr.s_addr), host->h_addr_list[0], 4); w.createWindow( I2Rectangle( // Window frame rectangle: I2Point(10, 10), // left-top corner 640, // width 480 // height ), R2Rectangle( // Coordinate rectangle: R2Point(-12., -9.), // bottom-right corner 24., 18. // width, height ), windowTitle ); w.setBackground("white"); //... GWindow::messageLoop(); // Message loop, animation XEvent e; while (GWindow::m_NumCreatedWindows > 0) { if (GWindow::getNextEvent(e)) { GWindow::dispatchEvent(e); } else { int maxFD = w.sock + 1; fd_set readSet; FD_ZERO(&readSet); FD_SET(w.sock, &readSet); fd_set* r = &readSet; if (w.finished) { maxFD = 0; r = NULL; } // Sleep a bit (we use select for sleeping) timeval dt; dt.tv_sec = 0; dt.tv_usec = 50000; // sleeping time 0.05 sec int res = select(maxFD + 1, r, 0, 0, &dt); if (res > 0) { Action peerAct; // printf("Select: res=%d\n", res); struct sockaddr_in senderAddr; struct sockaddr* fromAddr = (struct sockaddr*) &senderAddr; socklen_t senderAddrLen = sizeof(senderAddr); res = (int) recvfrom( w.sock, &peerAct, sizeof(peerAct), 0, // flags //??? (struct socaddr*) &senderAddr, fromAddr, &senderAddrLen ); // printf("Received %d bytes.\n", res); if (res == sizeof(peerAct)) w.processAction(peerAct); } } } if (w.sock >= 0) { close(w.sock); w.sock = (-1); } GWindow::closeX(); return 0; }