HP OpenVMS Systems

ask the wizard
Content starts here

DECwindows (X Windows) XtAppAddInput?

» close window

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.
 

answer written or last revised on ( 23-SEP-1998 )

» close window