![]() |
![]() HP OpenVMS Systemsask the wizard |
![]() |
The Question is: Hi, We are currently porting an application to OpenVMS from UNIX. In the Motif calls we have the problem listed below. If you could give us a hand we woule be more than greatfull. I have been searching in the prior questions for an answer for the question below but without success. I asume though that we have a standard error/user_error in our code. If you could help us we would be very very greatfull. Best Regards Arve Arvidsson Problem: The VMS XtAppAddInput -problem:------------------------------ We have a X/Motif program that also has a TCP/IP socket. We need to beable to determine when input is available on the socket, whilst in the XtAppMainLoop() function processing X-events. The obvious way of doing this is to use XtAppAddInput(), with the so cket file-descriptor. This works on all variants of UNIX that we have ported to including DEC Unix (OSF/1), Ultrix, SunOS, Solaris, AIX 3/4, HP-UX 9/10 Linux Redhat It does not appear to work on DEC VMS. The same code does not spot that there is any input on the socket. We have a test program that attempts to re-create the environment in a simpler way: it becomes a simple server/client system, with the client providing a simple X/Motif GUI. This shows that XtAppAddInput() does not seem to spot that input is available on our AlternateInput socket. The test program also allows the XtAppMainLoop() to be replaced by an implementation that uses select() to try to find out which of the inputs (the socket or the X-socket) has anything on it: it can then process that input, and repeat infintely. However, the select() spots whenever the socket has input, but never spots anything on the X-socket. To get around this problem, we had to re-design our simple approach used on the UNIX platforms, to use a signal'ed state method. We now generate an I/O signal whenever there is input on the socket, which causes the XtAppMainLoop() to be interrupted and a signal callback to be invoked. However, this also has problems, on VMS and on ULTRIX: it works OK on OSF/1. This signal callback was originally just a conversion of the original XtAppAddInput() callback function, which read the socket stream and then modified the X-display. However, it is now a signal callback, having interrupted the MainLoop. This causes problems because any X/Xt/Xm code called within the signal callback does not get actioned whenever the callback completes and returns command back to the MainLoop: the MainLoop returns to its blocked-read state and does not process any of the X-functions that we invoked within the signal callback. This is presumably why X11R6 has included the functions XtAppAddSignal()/XtAppNoticeSignal() - the man page for these on OSF/1states "XtNoticeSignal is the only Intrinsics function that can safely be called from a signal handler." This was then modified so that in the signal handler we no longer modified the X-display according to the input: instead, we set an X-timer (using XtAppAddTimeOut()) with a timeout period of 1milli-second, that should expire as soon as the signal callbackreturns back to the MainLoop - this then invokes a timeout callback that updates the X-display. This works fine on OSF/1 and ULTRIX, but still fails to work on VMS, as the MainLoop does not even action the timeout when it returns back to its blocked state from the signal handler. VMS can be made to work by explicitly sending an event on the X-server socket so that the MainLoop spots input when it gets returned to. This is a hack. Here is a copy of the test program: 0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0 /********************************************** * FILE: t.c * AUTHOR: Paul Sharpe * DESCRIPTION: Test program to show that XtAddInput doesn't work onVMS, and * to try to provide our own XtAppMainLoop() that select's * on our own socket and on the X-socket. * This program, run with no arguments, becomes a simple socket * server that waits for an incoming client connection, reads * all client messages and responds with a simple text ack. * Run with a single argument, this program becomes a simple * client, with a simple X/Motif front-end: whenever the button * is pushed, a message is sent across the TCP/IP socket to the * simple server, and an ack read back. **************************************************************************** */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <X11/Intrinsic.h> #include <Xm/Xm.h> #include <Xm/PushB.h> /* Hard-code a portnumber and name of the server machine, for when wecreate * our own socket to ourselves running as a server. */ #define PORT_NUM 3141 #define SERVER_NAME "hgwells.omnia.de" /* Define this so that the MainLoop implementation uses select(). */ #define USE_SELECT void create_server(void); void create_client(int argc, char **argv); void process_client(int sock); void pushed(Widget w, XtPointer client, XtPointer call); void read_client(int sock); void admin_server_input(XtPointer client, int *fd, XtInputId *id); void MainLoop(XtAppContext app_cntxt, Widget toplevel, int sock); void process_events(XtAppContext app_cntxt); /*************************************************************************** ** * FUNCTION: main * DESCRIPTION: **************************************************************************** */ int main(int argc, char *argv[]){ if (argc == 1) create_server(); else create_client(argc, argv);} /*************************************************************************** ** * FUNCTION: create_server * DESCRIPTION: Create a TCP/IP socket, waiting for incomingconnections. **************************************************************************** */ void create_server(void){ int rc, len, sock, newsock; struct sockaddr_in addr; struct hostent *host; (void)fprintf(stderr, "Server Listener on %s...\n", SERVER_NAME); host = gethostbyname(SERVER_NAME); if (host == (struct hostent *)NULL) { perror("gethostbyname"); exit(1); } (void)fprintf(stderr, " creating socket\n"); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { perror("socket"); exit(1); } (void)memset((void *)&addr, '\0', sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(PORT_NUM); (void)memcpy((void *)&addr.sin_addr, (void *)host->h_addr, (size_t)host->h_length); (void)fprintf(stderr, " binding...\n"); rc = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); if (rc == -1) { perror("bind"); exit(1); } (void)fprintf(stderr, " listening...\n"); rc = listen(sock, 10); if (rc == -1) { perror("listen"); exit(1); } for ( ; ; ) { len = sizeof(addr); (void)fprintf(stderr, " accepting...\n"); newsock = accept(sock, (struct sockaddr *)&addr, (unsigned int *)&len); (void)fprintf(stderr, " new socket = %d...\n", newsock); process_client(newsock); }} /*************************************************************************** ** * FUNCTION: process_client * DESCRIPTION: Read messages from the socket, sending back a simpleack. **************************************************************************** */ void process_client(int sock){ int rc; char buf[BUFSIZ]; for ( ; ; ) { (void)fprintf(stderr, " recv'ing...\n"); rc = recv(sock, buf, sizeof(buf), 0); if (rc == -1) perror("recv"); buf[rc] = '\0'; (void)fprintf(stderr, " '%s'\n", buf); if (strcmp(buf, "bye") == 0) break; (void)fprintf(stderr, " send'ing '+OK'\n", buf); if (send(sock, (const void *)"+OK", 4, 0) == -1) perror("send"); }} /*************************************************************************** ** * FUNCTION: create_client * DESCRIPTION: Create a socket, connecting on a hardcoded portnumber to * a hardcoded server, and then create a simple Motif screen. **************************************************************************** */ void create_client(int argc, char **argv){ int rc, len, sock, newsock; struct sockaddr_in addr; struct hostent *host; Widget toplevel, w; XtInputId input_id; XtAppContext app_cntxt; (void)fprintf(stderr, "Client to %s...\n", SERVER_NAME); host = gethostbyname(SERVER_NAME); if (host == (struct hostent *)NULL) { perror("gethostbyname"); exit(1); } (void)fprintf(stderr, " creating socket...\n"); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { perror("socket"); exit(1); } (void)memset((void *)&addr, '\0', sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(PORT_NUM); (void)memcpy((void *)&addr.sin_addr, (void *)host->h_addr, (size_t)host->h_length); (void)fprintf(stderr, " connecting...\n"); rc = connect(sock, (struct sockaddr *)&addr, sizeof(addr)); if (rc == -1) { perror("connect"); exit(1); } toplevel = XtAppInitialize(&app_cntxt, "test", NULL, 0, &argc, argv, NULL, NULL, 0); w = XmCreatePushButton(toplevel, "XtAppAddInput", NULL, 0); XtAddCallback(w, XmNactivateCallback, pushed, (void *)sock); (void)XtManageChild(w); XtRealizeWidget(toplevel); /* This is the code that should work - it works fine on all variants of * UNIX - solaris, OSF/1, HP-UX, Linux, AIX, ULTRIX etc etc etc input_id = XtAppAddInput(app_cntxt, sock, (XtPointer)(XtInputReadMask|XtInputExceptMask), (XtInputCallbackProc)admin_server_input, NULL); XtAppMainLoop(app_cntxt);*/ MainLoop(app_cntxt, toplevel, sock);} /*************************************************************************** ** * FUNCTION: MainLoop * DESCRIPTION: Implementation of an X mainloop function. * It attempts to select on the server socket and the X socket, * processing whichever has input. **************************************************************************** */ void MainLoop(XtAppContext app_cntxt, Widget toplevel, int sock){ int xfd, rc; fd_set readfds; Display *dsply; dsply = XtDisplay(toplevel); xfd = ConnectionNumber(dsply); #ifdef USE_SELECT for (;;) { /* In desperation, force all buffered X events out. */ XFlush(dsply); fsync(xfd); XSync(dsply, False); /* Now try select'ing for any input on the client/server socket and * on the connection to the X-server. */ FD_ZERO(&readfds); FD_SET (sock, &readfds); FD_SET (xfd, &readfds); (void)fprintf(stderr, " selecting on %d and %d\n", sock, xfd); rc = select(FD_SETSIZE, &readfds, NULL, NULL, (struct timeval *)NULL); if (rc == -1) { perror("select"); exit(1); } /* If there's something on the X-socket, process it. */ if (FD_ISSET(xfd, &readfds) != 0) { process_events(app_cntxt); continue; } /* If there's something on the from our socket, process it. */ if (FD_ISSET(sock, &readfds) != 0) { read_client(sock); continue; } }#else /* Don't worry about the socket - just process X-requests. */ for (;;) process_events(app_cntxt);#endif /* USE_SELECT */} /*************************************************************************** ** * FUNCTION: process_events * DESCRIPTION: Loop, getting all events and dispatching them. * This uses AppPending, as a way of seeing if there is any * AlternateInput events coming in... **************************************************************************** */ void process_events(XtAppContext app_cntxt){ XtInputMask input_mask; XEvent event; while ((input_mask = XtAppPending(app_cntxt)) != 0) { if (input_mask == XtIMXEvent) (void)fprintf(stderr, " Event\n"); if (input_mask == XtIMAlternateInput) (void)fprintf(stderr, " AlternateInput\n"); XtAppNextEvent(app_cntxt, &event); XtDispatchEvent(&event); }} /*************************************************************************** ** * FUNCTION: admin_server_input * DESCRIPTION: XtInput callback - not used here unless XtAddInput isused. **************************************************************************** */ void admin_server_input(XtPointer client, int *fd, XtInputId *id){ (void)fprintf(stderr, "admin_server_input()\n"); read_client(*fd);} /*************************************************************************** ** * FUNCTION: pushed * DESCRIPTION: Motif-callback for when the screen button is pushed, tosend * a message to the server via the socket. **************************************************************************** */ void pushed(Widget w, XtPointer client, XtPointer call){ int sock = (int)client; if (send(sock, (const void *)"pushed", 7, 0) == -1) perror("send");} /*************************************************************************** ** * FUNCTION: read_client * DESCRIPTION: Receive a message from the server **************************************************************************** */ void read_client(int sock){ int rc; char buf[BUFSIZ]; (void)fprintf(stderr, " recv'ing...\n"); rc = recv(sock, buf, sizeof(buf), 0); if (rc == -1) perror("recv"); buf[rc] = '\0'; (void)fprintf(stderr, " '%s'\n", buf);} The Answer is : Please look at the platform-specific definition of XtAppAddInput(), the platform-specific argument expects an event flag, and not a C-specific file descriptor. (The MIT documentation clearly indicates that certain arguments to this call are platform-specific.) Please realize that OpenVMS has a construct called a signal handler, which is rather different from the C signal mechanism. (C signals on OpenVMS are in fact implemented using OpenVMS signals, but this design is not generally visible to users.) The event flag chosen must be from event flag cluster zero. When using the traditional lib$get_ef and lib$free_ef calls to allocate event flags, you must first explicitly call lib$free_ef to free up some event flags in event flag cluster zero. Please see the event flag documentation for specific details and event flags. Here is some example code that covers calling this routine on OpenVMS: m->InputID = XtAppAddInput( m->AppCtx, m->InputEF, m->InputIosb, the_callback, 1 ); if ( !((int) m->InputID )) { XtAppErrorMsg( m->AppCtx, "invalidDevice", "XtAppAddInput", "XtToolkitError", "Can't Access Device", (String *) NULL, (Cardinal *) NULL ); ... The Wizard has provided this information to the maintainer of the OpenVMS FAQ, for inclusion in a future issue.
|