diff lib/imlib/imxport.c @ 0:92745d501b9a

initial import from kinput2-v3.1
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Mon, 08 Mar 2010 04:44:30 +0900
parents
children 598fcbe482b5
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/imlib/imxport.c	Mon Mar 08 04:44:30 2010 +0900
@@ -0,0 +1,672 @@
+#ifndef lint
+static char *rcsid = "$Id: imxport.c,v 1.11 1999/05/04 05:44:11 ishisone Exp $";
+#endif
+/*
+ * Copyright (c) 1994  Software Research Associates, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Software Research Associates not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.  Software Research
+ * Associates makes no representations about the suitability of this software
+ * for any purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * Author:  Makoto Ishisone, Software Research Associates, Inc., Japan
+ */
+
+#include "im.h"
+
+#include "MyDispatch.h"
+
+#include <X11/Xatom.h>
+
+#if (defined(IM_UNIX_TRANSPORT) || defined(IM_TCP_TRANSPORT))
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
+#ifdef IM_UNIX_TRANSPORT
+#include <sys/un.h>
+#endif
+
+#ifdef IM_TCP_TRANSPORT
+#include <netinet/in.h> 
+#endif
+
+extern int errno;
+
+/*
+ * X transport version number
+ *   This implementation uses:
+ *	+ ClientMessage (both single and multiple)
+ *	+ Property with notification by ClientMessage
+ *   So the major version is 0, minor version is 2.
+ *
+ * X transport dividing size
+ *   This size is the threshold between ClientMessage transfer
+ *   and Property transfer.  If the data to be sent is small,
+ *   transfer via ClientMessage is faster.  But since single
+ *   ClientMessage can transfer only 20bytes, there must be a
+ *   certain size above which the transfer via Property is
+ *   faster.
+ */
+#define ServerMajorTransportVersion	0
+#define ServerMinorTransportVersion	2
+#define XTransportDividingSize		(20 * 5)
+
+
+static int dummyDispatcher _Pt_((IMConnection *conn));
+static void dumpBuf _Pt_((IMBuffer *ibp, char *title));
+static Window communicationWindow _Pt_((Widget w));
+static IMConnection *newConnection _Pt_((Widget protocol));
+
+#if defined(IM_TCP_TRANSPORT) || defined(IM_UNIX_TRANSPORT)
+static void socketinput _Pt_((XtPointer cldata, int *fdp, XtInputId *idp));
+static int socketFlush _Pt_((IMConnection *conn));
+static void socketShutdown _Pt_((IMConnection *conn));
+
+static IMTransportOps SocketTransportOps = {
+    socketFlush,
+    socketShutdown,
+};
+#endif /* IM_TCP_TRANSPORT || IM_UNIX_TRANSPORT */
+
+#ifdef IM_X_TRANSPORT
+static void xinput _Pt_((XEvent *ev, XtPointer cldata));
+static void xdestroy _Pt_((XEvent *ev, XtPointer cldata));
+static int xBrokenPipe _Pt_((Display *dpy, XErrorEvent *eev,
+			     XPointer client_data));
+static int xFlush _Pt_((IMConnection *conn));
+static void xShutdown _Pt_((IMConnection *conn));
+
+static IMTransportOps XTransportOps = {
+    xFlush,
+    xShutdown,
+};
+#endif /* IM_X_TRANSPORT */
+
+
+/*- dummyDispatcher: dummy dispatcher routine -*/
+/* ARGSUSED */
+static int
+dummyDispatcher(conn)
+IMConnection *conn;
+{
+#ifdef DEBUG
+    printf("dummyDispatcher -- this function should not be called!\n");
+#endif
+    return 0;	/* for lint */
+}
+
+/*- dumpBuf: dump input/output buffer (for debug) -*/
+static void
+dumpBuf(ibp, title)
+IMBuffer *ibp;
+char *title;
+{
+    int len = IMBUFLEN(ibp);
+    unsigned char *data = (unsigned char *)IMBUFDATA(ibp);
+    int i;
+
+    printf("%s (%d bytes)", title, len);
+    for (i = 0; i < len; i++) {
+	if (i % 16 == 0) printf("\n\t");
+	printf("%02x ", *data++);
+    }
+    printf("\n");
+}
+
+/*- communicationWindow: create a window for communication with client -*/
+static Window
+communicationWindow(w)
+Widget w;
+{
+    return XCreateSimpleWindow(XtDisplay(w), XtWindow(w), 0, 0, 1, 1, 0, 0, 0);
+}
+
+/*- newConnection: allocate IMConnection structure and initialize -*/
+static IMConnection *
+newConnection(protocol)
+Widget protocol;
+{
+    IMConnection *conn;
+    static int connection_serial_number = 0;
+
+    conn = (IMConnection *)XtMalloc(sizeof(IMConnection));
+
+    conn->serial = ++connection_serial_number;		/* start from 1 */
+    conn->dispatcher = dummyDispatcher;
+
+    IMBufInit(&conn->in_buf);
+    IMBufInit(&conn->out_buf);
+
+    conn->byte_order = ORDER_UNKNOWN;
+    conn->im_list = NULL;
+    conn->proto_widget = protocol;
+
+    conn->schedule = 0;
+    conn->queue_next = NULL;
+
+    conn->next = NULL;
+
+    return conn;
+}
+
+#if defined(IM_TCP_TRANSPORT) || defined(IM_UNIX_TRANSPORT)
+/*- socketinput: handler for input from TCP/Unix socket -*/
+/* ARGSUSED */
+static void
+socketinput(cldata, fdp, idp)
+XtPointer cldata;
+int *fdp;
+XtInputId *idp;
+{
+    IMConnection *conn = (IMConnection *)cldata;
+    int fd = conn->transport.priv.sock.fd;
+    char buf[4096];
+    int n;
+    int cond;
+
+    if ((n = read(fd, buf, sizeof(buf))) < 0) {
+	cond = TRANSPORT_ERROR;
+    } else if (n == 0) {
+	cond = TRANSPORT_EOF;
+    } else {
+	IMBufAdd(&conn->in_buf, buf, n);
+	cond = TRANSPORT_OK;
+    }
+
+    if (DDEBUG_CONDITION(100)) dumpBuf(IM_INBUF(conn), "** input buffer");
+
+    IMDispatch(conn, cond);
+}
+
+/*- socketFlush: output to socket -*/
+static int
+socketFlush(conn)
+IMConnection *conn;
+{
+    int fd = conn->transport.priv.sock.fd;
+    IMBuffer *ibp = IM_OUTBUF(conn);
+    int n;
+
+    if ((n = write(fd, ibp->buf, IMBUFLEN(ibp))) < 0) {
+	return TRANSPORT_ERROR;
+    } else {
+	IMBufDiscard(ibp, n);
+	return (IMBUFLEN(ibp) == 0) ? TRANSPORT_OK : TRANSPORT_PARTIAL;
+    }
+}
+
+/*- socketShutdown: close socket and stop input callback associate with it -*/
+static void
+socketShutdown(conn)
+IMConnection *conn;
+{
+    int fd = conn->transport.priv.sock.fd;
+
+    (void)shutdown(fd, 2);
+    (void)close(fd);
+    XtRemoveInput(conn->transport.priv.sock.id);
+}
+#endif /* IM_TCP_TRANSPORT || IM_UNIX_TRANSPORT */
+
+#ifdef IM_X_TRANSPORT
+/*- xinput: handler for input via X inter-client communication -*/
+static void
+xinput(ev, cldata)
+XEvent *ev;
+XtPointer cldata;
+{
+    XClientMessageEvent *event = (XClientMessageEvent *)ev;
+    IMConnection *conn = (IMConnection *)cldata;
+    int cond;
+    Atom msg_type;
+
+    TRACE(("imlib:xinput()\n"));
+
+    if (event->type != ClientMessage ||
+	event->window != conn->transport.priv.x.server) {
+	return;
+    }
+
+    msg_type = event->message_type;
+
+    if (event->format == 32) {
+	/*
+	 * indirect reference -- data resides in a property,
+	 * whose name is stored in the event.
+	 */
+	Atom propatom = event->data.l[1];
+	long offset = 0;
+	unsigned long remain;
+
+	if (msg_type != IMProtocolAtom(conn->proto_widget)) return;
+
+	do {
+	    Atom actualtype;
+	    int actualformat;
+	    unsigned long nitems;
+	    char *data = NULL;
+
+	    XGetWindowProperty(event->display, event->window, propatom,
+			       offset, 1000, True, AnyPropertyType,
+			       &actualtype, &actualformat, &nitems,
+			       &remain, (unsigned char **)&data);
+	    if (data == NULL) return;
+	    if (actualformat != 8) {
+		cond = TRANSPORT_ERROR;
+	    } else {
+		IMBufAdd(&conn->in_buf, data, (int)nitems);
+		offset += nitems;
+		cond = TRANSPORT_OK;
+	    }
+	    XFree(data);
+	} while (remain > 0);
+    } else if (event->format == 8) {
+	if (msg_type != IMProtocolAtom(conn->proto_widget) &&
+	    msg_type != IMMoreDataAtom(conn->proto_widget)) {
+	    return;
+	}
+	IMBufAdd(&conn->in_buf, event->data.b, 20);
+	cond = TRANSPORT_OK;
+    } else {
+	return;
+    }
+
+    if (DDEBUG_CONDITION(100)) dumpBuf(IM_INBUF(conn), "** input buffer");
+
+    IMDispatch(conn, cond);
+}
+
+/*- xdestroy: handler for client comm. window destruction -*/
+static void
+xdestroy(ev, cldata)
+XEvent *ev;
+XtPointer cldata;
+{
+    XDestroyWindowEvent *event = (XDestroyWindowEvent *)ev;
+    IMConnection *conn = (IMConnection *)cldata;
+
+    TRACE(("imlib:xdestroy()\n"));
+
+    if (event->type != DestroyNotify ||
+	event->window != conn->transport.priv.x.client) {
+	return;
+    }
+    /*
+     * Call IMDispatch with TRANSPORT_ERROR, in order to
+     * shutdown connection and process queued operations.
+     */
+    IMDispatch(conn, TRANSPORT_ERROR);
+}
+
+/*- xBrokenPipe: asyncronous BadWindow error handler -*/
+/* ARGSUSED */
+static int
+xBrokenPipe(dpy, eev, client_data)
+Display *dpy;
+XErrorEvent *eev;
+XPointer client_data;
+{
+    TRACE(("xBrokenPipe()\n"));
+
+    if (eev->error_code == BadWindow) {
+	/*
+	 * Search for the connection using window that caused the error.
+	 * Note that we cannot pass the connection via client_data,
+	 * Since the connection might be already destroyed by a previous
+	 * error.
+	 */
+	Window bad_win = (Window)eev->resourceid;
+	IMConnection *conn;
+
+	conn = IMConnectionList((Widget)client_data);
+	while (conn != NULL) {
+	    if (bad_win == conn->transport.priv.x.client) {
+		DPRINT(("BadWindow on connection #%d\n", conn->serial));
+		IMDispatch(conn, TRANSPORT_ERROR);
+		break;
+	    }
+	    conn = conn->next;
+	}
+	return 0;
+    } else {
+	return 1;
+    }
+}
+
+/*- xFlush: output via X inter-client communication mechanism -*/
+static int
+xFlush(conn)
+IMConnection *conn;
+{
+    IMBuffer *ibp = IM_OUTBUF(conn);
+    XClientMessageEvent repl;
+    Widget w = conn->proto_widget;
+    Window client_win = conn->transport.priv.x.client;
+    Display *dpy = XtDisplay(w);
+    int length;
+    XAEHandle handle;
+
+    if ((length = IMBUFLEN(ibp)) == 0) return TRANSPORT_OK;
+
+    repl.type = ClientMessage;
+    repl.window = client_win;
+
+    handle = XAESet(dpy, xBrokenPipe, (void (*)())NULL, (XPointer)w);
+
+    if (IMBUFLEN(ibp) < XTransportDividingSize) {
+	char *data = IMBUFDATA(ibp);
+
+	repl.format = 8;
+	repl.message_type = IMMoreDataAtom(w);
+	while (length > 20) {
+	    bcopy(data, repl.data.b, 20);
+	    XSendEvent(dpy, client_win, False, NoEventMask, (XEvent *)&repl);
+	    data += 20;
+	    length -= 20;
+	}
+	repl.message_type = IMProtocolAtom(w);
+	bzero(repl.data.b, 20);
+	bcopy(data, repl.data.b, length);
+	XSendEvent(dpy, client_win, False, NoEventMask, (XEvent *)&repl);
+    } else {
+	repl.format = 32;
+	repl.message_type = IMProtocolAtom(w);
+	repl.data.l[0] = length;
+	repl.data.l[1] = IMKi2CommAtom(w);
+	XChangeProperty(dpy, client_win, IMKi2CommAtom(w), XA_STRING,
+			8, PropModeAppend,
+			(unsigned char *)IMBUFDATA(ibp), IMBUFLEN(ibp));
+	XSendEvent(dpy, client_win, False, NoEventMask, (XEvent *)&repl);
+    }
+
+    XFlush(dpy);
+    XAEUnset(handle);
+
+    IMBufClear(ibp);
+    return TRANSPORT_OK;
+}
+
+/*- xShutdown: close communication channel by destroying comm. window -*/
+static void
+xShutdown(conn)
+IMConnection *conn;
+{
+    Display *dpy = XtDisplay(conn->proto_widget);
+    MyRemoveAllEventHandler(dpy, conn->transport.priv.x.client);
+    MyRemoveAllEventHandler(dpy, conn->transport.priv.x.server);
+    XDestroyWindow(dpy, conn->transport.priv.x.server);
+}
+#endif /* IM_X_TRANSPORT */
+
+
+/*
+ * Public functions
+ */
+
+#ifdef IM_TCP_TRANSPORT
+int
+IMCreateTCPService(portp)
+int *portp;
+{
+    struct sockaddr_in addr;
+    int optval = 1;
+    int sock;
+
+    TRACE(("IMCreateTCPService(port=%d)\n", *portp));
+
+    if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+	DPRINT(("socket(PF_INET) failed with %d\n", errno));
+	return -1;
+    }
+
+#ifdef SO_REUSEADDR
+    (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+		     (char *)&optval, sizeof(optval));
+#endif /* SO_REUSEADDR */
+
+    bzero((char *)&addr, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(*portp);
+
+    if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+	DPRINT(("bind() failed with %d\n", errno));
+	return -1;
+    }
+    if (*portp == 0) {
+	int addr_len = sizeof(addr);
+
+	if (getsockname(sock, (struct sockaddr *)&addr, &addr_len) < 0) {
+	    DPRINT(("getsockname() failed with %d\n", errno));
+	    return -1;
+	}
+	*portp = ntohs(addr.sin_port);
+	TRACE(("\tport=%d\n", *portp));
+    }
+    if (listen(sock, 4) < 0) {
+	DPRINT(("listen() failed with %d\n", errno));
+	return -1;
+    }
+    return sock;
+}
+
+IMConnection *
+IMTCPConnection(protocol, wellknownfd)
+Widget protocol;
+int wellknownfd;
+{
+    IMConnection *conn;
+    int fd;
+    struct sockaddr_in addr;
+    int addrlen = sizeof(addr);
+    XtInputId id;
+
+    TRACE(("IMTCPConnection()\n"));
+
+    if ((fd = accept(wellknownfd, (struct sockaddr *)&addr, &addrlen)) < 0) {
+	DPRINT(("accept() failed with %d\n", errno));
+	return NULL;
+    }
+    conn = newConnection(protocol);
+    conn->transport.ops = &SocketTransportOps;
+    conn->transport.priv.sock.fd = fd;
+    DDPRINT(2, ("new connection #%d: transport=TCP socket=%d\n",
+		conn->serial, fd));
+
+    id = XtAppAddInput(XtWidgetToApplicationContext(protocol), fd,
+		       (XtPointer)XtInputReadMask, socketinput,
+		       (XtPointer)conn);
+    conn->transport.priv.sock.id = id;
+
+    return conn;
+}
+#endif /* IM_TCP_TRANSPORT */
+
+#ifdef IM_UNIX_TRANSPORT
+int
+IMCreateUnixService(path)
+char *path;
+{
+    struct sockaddr_un addr;
+    int sock;
+
+    TRACE(("IMCreateUnixService(%s)\n", path));
+    if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+	DPRINT(("socket(PF_UNIX) failed with %d\n", errno));
+	return -1;
+    }
+
+    bzero((char *)&addr, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    (void)strcpy(addr.sun_path, path);
+
+    /*
+     * Remove socket which is created by the previous process.
+     */
+    (void)unlink(path);
+
+    if (bind(sock, (struct sockaddr *)&addr, strlen(path) + 2) < 0) {
+	DPRINT(("bind() failed with %d\n", errno));
+	return -1;
+    }
+    if (listen(sock, 4) < 0) {
+	DPRINT(("listen() failed with %d\n", errno));
+	return -1;
+    }
+    return sock;
+}
+
+IMConnection *
+IMUnixConnection(protocol, wellknownfd)
+Widget protocol;
+int wellknownfd;
+{
+    IMConnection *conn;
+    int fd;
+    struct sockaddr_un addr;
+    int addrlen = sizeof(addr);
+    XtInputId id;
+
+    TRACE(("IMUnixConnection()\n"));
+
+    if ((fd = accept(wellknownfd, (struct sockaddr *)&addr, &addrlen)) < 0) {
+	DPRINT(("accept() failed with %d\n", errno));
+	return NULL;
+    }
+    conn = newConnection(protocol);
+    conn->transport.ops = &SocketTransportOps;
+    conn->transport.priv.sock.fd = fd;
+    DDPRINT(2, ("new connection #%d: transport=UNIX socket=%d\n",
+		conn->serial, fd));
+
+    id = XtAppAddInput(XtWidgetToApplicationContext(protocol), fd,
+		       (XtPointer)XtInputReadMask, socketinput,
+		       (XtPointer)conn);
+    conn->transport.priv.sock.id = id;
+
+    return conn;
+}
+#endif /* IM_UNIX_TRANSPORT */
+
+#ifdef IM_X_TRANSPORT
+IMConnection *
+IMXConnection(protocol, xev)
+Widget protocol;
+XEvent *xev;
+{
+    XClientMessageEvent *event = (XClientMessageEvent *)xev;
+    XClientMessageEvent repl;
+    Display *dpy = XtDisplay(protocol);
+    Window client_window;
+    IMConnection *conn;
+    XAEHandle h;
+
+    TRACE(("IMXConnection()\n"));
+
+    if (event->type != ClientMessage ||
+	event->display != dpy ||
+	event->window != XtWindow(protocol) ||
+	event->message_type != IMXConnectAtom(protocol) ||
+	event->format != 32) {
+	TRACE(("\tinvalid event\n"));
+#ifdef DEBUG
+	if (event->type != ClientMessage) printf("not ClientMessage\n");
+	if (event->display != dpy) printf("wrong display\n");
+	if (event->window != XtWindow(protocol)) printf("wrong window\n");
+	if (event->message_type != IMXConnectAtom(protocol)) {
+	    printf("wrong message type (%ld should be %ld)\n",
+		   event->message_type, IMXConnectAtom(protocol));
+	}
+	if (event->format != 32) printf("wrong format\n");
+#endif
+	return NULL;
+    }
+    client_window = event->data.l[0];
+    if (!IMValidateWindow(dpy, client_window, (IMWindowProfile *)NULL)) {
+	DPRINT(("client window %08lx does not exist\n", client_window));
+	return NULL;
+    }
+    conn = newConnection(protocol);
+    conn->transport.ops = &XTransportOps;
+    conn->transport.priv.x.client = client_window;
+    conn->transport.priv.x.server = communicationWindow(protocol);
+    DDPRINT(2, ("new connection #%d: transport=X client=%08lx\n",
+		conn->serial, client_window));
+    TRACE(("\ttransport version: %ld.%ld\n",
+	   event->data.l[1], event->data.l[2]));
+
+    repl.type = ClientMessage;
+    repl.window = client_window;
+    repl.message_type = IMXConnectAtom(protocol);
+    repl.format = 32;
+    repl.data.l[0] = conn->transport.priv.x.server;
+    repl.data.l[1] = ServerMajorTransportVersion;
+    repl.data.l[2] = ServerMinorTransportVersion;
+    repl.data.l[3] = XTransportDividingSize;
+    /* make it safe... */
+    h = XAESetIgnoreErrors(dpy);
+    XSendEvent(dpy, client_window, False, NoEventMask, (XEvent *)&repl);
+    MyAddEventHandler(dpy, client_window, DestroyNotify, StructureNotifyMask,
+		      xdestroy, (XtPointer)conn);
+    XAEUnset(h);
+
+    MyAddEventHandler(dpy, conn->transport.priv.x.server,
+		      ClientMessage, NoEventMask, xinput, (XtPointer)conn);
+
+    return conn;
+}
+#endif /* IM_X_TRANSPORT */
+
+int
+IMFlush(conn)
+IMConnection *conn;
+{
+    TRACE(("IMFlush(#%d)\n", conn->serial));
+
+    if (DDEBUG_CONDITION(100)) dumpBuf(IM_OUTBUF(conn), "** output buffer");
+
+    return (*conn->transport.ops->flush)(conn);
+}
+
+void
+IMShutdown(conn)
+IMConnection *conn;
+{
+    TRACE(("IMShutdown(#%d)\n", conn->serial));
+    (*conn->transport.ops->shutdown)(conn);
+}
+
+void
+IMCloseConnection(conn)
+IMConnection *conn;
+{
+    IMIM *imp;
+
+    DDPRINT(2, ("IMCloseConnection(#%d)\n", conn->serial));
+
+    imp = conn->im_list;
+    while (imp != NULL) {
+	IMIM *next = imp->next;
+
+	IMDestroyIM(imp);
+	imp = next;
+    }
+
+    IMBufClear(&conn->in_buf);
+    IMBufClear(&conn->out_buf);
+#ifdef notdef
+    destroyAuth(conn->server_auth);
+    destroyAuth(conn->client_auth);
+#endif
+    IMShutdown(conn);
+
+    IMUnregisterConnection(conn);
+
+    XtFree((char *)conn);
+}