/* * File: xmanager.c * Last modified on Sun Jul 24 10:34:47 1994 by eroberts * ----------------------------------------------------- * This file implements the xmanager.h interface, which is responsible * for the X side of the communication between the graphics client and * the X manager. The xmanager module handles the process management * and intermodule communication. All work involving actual structures * is performed by the xdisplay module. */ /* * General implementation notes * ---------------------------- * As with all implementations of the graphics library, the X * implementation is complicated by the change in model. The X * programming paradigm is based on the idea of an "event loop," * in which the application program continually calls XNextEvent * to determine whether any activity is required. This strategy * runs counter to the more conventional view of a main program * as the highest level in the problem decomposition. In the * context of teaching programming, we want the main program and * its subprograms to draw the figures -- a strategy which is not * easy directly compatible with the X paradigm. * * To solve this problem, the X implementation creates two forks: * one to poll for X events and manage the screen and the other * to run the user program. These processes are established by * the first call to InitGraphics and remain in place until the * program terminates. So that program termination can be * handled correctly, the child process is the one that returns * to the InitGraphics caller where it runs the client program. * The parent process, which is called the X manager process, * never returns from InitGraphics. After initializing the * display, the X manager process runs an event loop waiting * simultaneously for graphical events and commands from the * client process. All of this mechanism is invisible to the * client. * * The two processes communicate by means of pipes running in * each direction. The client communicates with the X manager by * sending commands over its output pipe. The command format * consists of ASCII text lines in the following format: * * LLLCC XXXXXX * * where LLL is a three-digit length field giving the length of * the entire line (including the newline), CC is a two-digit * field specifying the command number and XXXXXX is an arbitrary * text string specifying arguments to the command. The length * of the text string can be determined from the line length * indicator. * * Some client operations require a response from the X manager. * These operations call GetResponse() to read a line from the * client. Because this operation is synchronous, there is no * need for the more cumbersome form used in the opposite * direction. * * This interface is used by both the client side (graphics.c) * and the X manager side (xmanager.c), but it is important to * remember that the two are running in different forks. For * each fork, the inPipe and outPipe variables correspond to * the local perspective. Thus, reading is always performed * on inPipe and writing is performed on outPipe. The ends * of the pipe are crossed between the processes so that data * that is written to outPipe by one fork appears in inPipe * on the other side. */ #include #include #include #include #include #include #include "genlib.h" #include "exception.h" #include "simpio.h" #include "xmanager.h" #include "xdisplay.h" #include "xcompat.h" #include "glibrary.h" /* Constants */ #define ClientTimeout 0.05 /* Private state variables */ static FILE *inPipe, *outPipe; static int infd; static pid_t child; static char cmdBuffer[CommandBufferSize]; /* Private function prototypes */ static void MainEventLoop(void); static bool ReadMessage(void); static void ProcessMessage(void); static void LineMessage(string args); static void ArcMessage(string args); static void TextMessage(string args); static void WidthMessage(string args); static void SetEraseMessage(string args); static void StartRegionMessage(string args); static void EndRegionMessage(string args); static void ClearMessage(string args); static void UpdateMessage(string args); static void SetTitleMessage(string args); static void SetFontMessage(string args); static void GetWidthMessage(string args); static void GetHeightMessage(string args); /* Exported entries */ void InitXManager(string title) { int p1[2], p2[2]; OpenDisplay(title); pipe(p1); pipe(p2); if ((child = fork()) == 0) { close(p1[0]); close(p2[1]); inPipe = fdopen(p2[0], "r"); if (inPipe == NULL) Error("Can't open pipe from X client"); outPipe = fdopen(p1[1], "w"); if (outPipe == NULL) Error("Can't open pipe to X client"); } else { close(p1[1]); close(p2[0]); infd = p1[0]; outPipe = fdopen(p2[1], "w"); if (outPipe == NULL) Error("Can't open output pipe in X client"); try { MainEventLoop(); except(ErrorException) fprintf(stderr, "Error: %s\n", (string) GetExceptionValue()); CloseDisplay(); kill(child, SIGKILL); exit(1); } endtry printf("Press return to exit.\n"); infd = 0; MainEventLoop(); CloseDisplay(); exit(0); } } void SendCommand(commandT cmd, string args) { int len; len = strlen(args) + 4; fprintf(outPipe, "%3d%2d %s\n", len, (int) cmd, args); fflush(outPipe); } string GetResponse(void) { return (ReadLine(inPipe)); } /* Private functions */ static void MainEventLoop(void) { struct timeval tv, *tvp; int xfd, width, sc; fd_set readmask, readset; string message; xfd = DisplayFD(); tv.tv_sec = ClientTimeout; tv.tv_usec = ((int) (ClientTimeout * 1000000)) % 1000000; FD_ZERO(&readmask); FD_SET(xfd, &readmask); FD_SET(infd, &readmask); width = Max(xfd, infd) + 1; tvp = &tv; while (TRUE) { if (ProcessXEvent()) { tvp = &tv; } else { readset = readmask; sc = select(width, &readset, NULL, NULL, tvp); tvp = (sc == 0) ? NULL : &tv; if (sc == 0) { CheckForRedraw(); } else if (FD_ISSET(infd, &readset)) { if (!ReadMessage()) { CheckForRedraw(); return; } ProcessMessage(); SetRedrawFlag(); } } } } static bool ReadMessage(void) { int len; char lbuf[4]; if (infd == 0) { FreeBlock(GetLine()); return (FALSE); } if (read(infd, lbuf, 3) == 0) return (FALSE); lbuf[3] = '\0'; len = atoi(lbuf); if (read(infd, cmdBuffer, len) != len) { Error("Unexpected end of file"); } if (cmdBuffer[len-1] != '\n') { Error("Unexpected end of command"); } cmdBuffer[len-1] = '\0'; return (TRUE); } static void ProcessMessage(void) { int id; string args; args = cmdBuffer + 3; sscanf(cmdBuffer, "%d", &id); switch ((commandT) id) { case LineCmd: LineMessage(args); break; case ArcCmd: ArcMessage(args); break; case TextCmd: TextMessage(args); break; case WidthCmd: WidthMessage(args); break; case SetEraseCmd: SetEraseMessage(args); break; case StartRegionCmd: StartRegionMessage(args); break; case EndRegionCmd: EndRegionMessage(args); break; case ClearCmd: ClearMessage(args); break; case UpdateCmd: UpdateMessage(args); break; case SetTitleCmd: SetTitleMessage(args); break; case SetFontCmd: SetFontMessage(args); break; case GetWidthCmd: GetWidthMessage(args); break; case GetHeightCmd: GetHeightMessage(args); break; default: Error("Internal error: Illegal command"); break; } } static void LineMessage(string args) { int n; double x, y, dx, dy; n = sscanf(args, "%lg %lg %lg %lg", &x, &y, &dx, &dy); if (n != 4) { Error("Internal error: Bad arguments to LineCmd"); } DisplayLine(x, y, dx, dy); } static void ArcMessage(string args) { int n; double x, y, rx, ry, start, sweep; n = sscanf(args, "%lg %lg %lg %lg %lg %lg", &x, &y, &rx, &ry, &start, &sweep); if (n != 6) { Error("Internal error: Bad arguments to ArcCmd"); } DisplayArc(x, y, rx, ry, start, sweep); } static void TextMessage(string args) { int n; char *space; double x, y; n = sscanf(args, "%lg %lg", &x, &y); if (n != 2) { Error("Internal error: Bad arguments to TextCmd"); } space = strchr(strchr(args, ' ') + 1, ' '); if (space == NULL) { Error("Internal error: Bad arguments to TextCmd"); } DisplayText(x, y, space + 1); } static void WidthMessage(string args) { double width; width = DisplayTextWidth(args); fprintf(outPipe, "%g\n", width); fflush(outPipe); } static void SetEraseMessage(string args) { int n, flag; n = sscanf(args, "%d", &flag); if (n != 1) { Error("Internal error: Bad arguments to SetEraseCmd"); } DisplaySetEraseMode((bool) flag); } static void StartRegionMessage(string args) { int n; double grayScale; n = sscanf(args, "%lg", &grayScale); if (n != 1) { Error("Internal error: Bad arguments to SetEraseCmd"); } DisplayStartRegion(grayScale); } static void EndRegionMessage(string args) { DisplayEndRegion(); } static void ClearMessage(string args) { ClearDisplay(); } static void UpdateMessage(string args) { SetRedrawFlag(); CheckForRedraw(); } static void SetTitleMessage(string args) { DisplaySetTitle(args); } static void SetFontMessage(string args) { int n, size; char *space; n = sscanf(args, "%d", &size); if (n != 1) { Error("Internal error: Bad arguments to SetFontCmd"); } space = strchr(args, ' '); if (space == NULL) { Error("Internal error: Bad arguments to TextCmd"); } fprintf(outPipe, "%s\n", DisplaySetFont(space + 1, size)); fflush(outPipe); } static void GetWidthMessage(string args) { fprintf(outPipe, "%g\n", DisplayGetWidth()); fflush(outPipe); } static void GetHeightMessage(string args) { fprintf(outPipe, "%g\n", DisplayGetHeight()); fflush(outPipe); }