aboutsummaryrefslogtreecommitdiff
path: root/nx-X11/lib/XprintUtil/xprintutil_printtofile.c
diff options
context:
space:
mode:
Diffstat (limited to 'nx-X11/lib/XprintUtil/xprintutil_printtofile.c')
-rw-r--r--nx-X11/lib/XprintUtil/xprintutil_printtofile.c498
1 files changed, 498 insertions, 0 deletions
diff --git a/nx-X11/lib/XprintUtil/xprintutil_printtofile.c b/nx-X11/lib/XprintUtil/xprintutil_printtofile.c
new file mode 100644
index 000000000..331e7ad5b
--- /dev/null
+++ b/nx-X11/lib/XprintUtil/xprintutil_printtofile.c
@@ -0,0 +1,498 @@
+/******************************************************************************
+ ******************************************************************************
+ **
+ ** (c) Copyright 2001-2004 Roland Mainz <roland.mainz@nrubsig.org>
+ **
+ ** Permission is hereby granted, free of charge, to any person obtaining a copy
+ ** of this software and associated documentation files (the "Software"), to deal
+ ** in the Software without restriction, including without limitation the rights
+ ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ ** copies of the Software, and to permit persons to whom the Software is
+ ** furnished to do so, subject to the following conditions:
+ **
+ ** The above copyright notice and this permission notice shall be included in
+ ** all copies or substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ ** IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ ** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ **
+ ** Except as contained in this notice, the names of the copyright holders shall
+ ** not be used in advertising or otherwise to promote the sale, use or other
+ ** dealings in this Software without prior written authorization from said
+ ** copyright holders.
+ **
+ ******************************************************************************
+ *****************************************************************************/
+
+#include "xprintutil.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#ifdef XPU_USE_THREADS
+#include <time.h>
+#ifdef XPU_USE_NSPR
+#include <prthread.h>
+#else
+#include <pthread.h>
+#endif /* XPU_USE_NSPR */
+#endif /* XPU_USE_THREADS */
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/* local prototypes */
+#ifdef DEBUG
+static void PrintXPGetDocStatus( XPGetDocStatus status );
+#endif
+static Bool XNextEventTimeout( Display *display, XEvent *event_return, struct timeval *timeout );
+static void *XpuPrintToFile( Display *pdpy, XPContext pcontext, const char *filename );
+static void MyPrintToFileProc( Display *pdpy, XPContext pcontext, unsigned char *data, unsigned int data_len, XPointer client_data );
+static void MyFinishProc( Display *pdpy, XPContext pcontext, XPGetDocStatus status, XPointer client_data );
+#ifdef XPU_USE_NSPR
+static void PrintToFile_Consumer( void *handle );
+#else
+static void *PrintToFile_Consumer( void *handle );
+#endif
+
+void XpuStartJobToSpooler(Display *pdpy)
+{
+ XpStartJob(pdpy, XPSpool);
+}
+
+void *XpuStartJobToFile( Display *pdpy, XPContext pcontext, const char *filename )
+{
+ void *handle;
+
+ XpStartJob(pdpy, XPGetData);
+ handle = XpuPrintToFile(pdpy, pcontext, filename);
+
+ if (!handle)
+ {
+ /* Cancel the print job and discard all events... */
+ XpCancelJob(pdpy, True);
+ }
+
+ return(handle);
+}
+
+#ifdef DEBUG
+/* DEBUG: Print XPGetDocStatus */
+static
+void PrintXPGetDocStatus( XPGetDocStatus status )
+{
+ switch(status)
+ {
+ case XPGetDocFinished: puts("PrintXPGetDocStatus: XPGetDocFinished"); break;
+ case XPGetDocSecondConsumer: puts("PrintXPGetDocStatus: XPGetDocSecondConsumer"); break;
+ case XPGetDocError: puts("PrintXPGetDocStatus: XPGetDocError"); break;
+ default: puts("PrintXPGetDocStatus: <unknown value"); break;
+ }
+}
+#endif /* DEBUG */
+
+
+/* XNextEvent() with timeout */
+static
+Bool XNextEventTimeout( Display *display, XEvent *event_return, struct timeval *timeout )
+{
+ int res;
+ fd_set readfds;
+ int display_fd = XConnectionNumber(display);
+
+ /* small shortcut... */
+ if( timeout == NULL )
+ {
+ XNextEvent(display, event_return);
+ return(True);
+ }
+
+ FD_ZERO(&readfds);
+ FD_SET(display_fd, &readfds);
+
+ /* Note/bug: In the case of internal X events (like used to trigger callbacks
+ * registered by XpGetDocumentData()&co.) select() will return with "new info"
+ * - but XNextEvent() below processes these _internal_ events silently - and
+ * will block if there are no other non-internal events.
+ * The workaround here is to check with XEventsQueued() if there are non-internal
+ * events queued - if not select() will be called again - unfortunately we use
+ * the old timeout here instead of the "remaining" time... (this only would hurt
+ * if the timeout would be really long - but for current use with values below
+ * 1/2 secs it does not hurt... =:-)
+ */
+ while( XEventsQueued(display, QueuedAfterFlush) == 0 )
+ {
+ res = select(display_fd+1, &readfds, NULL, NULL, timeout);
+
+ switch(res)
+ {
+ case -1: /* select() error - should not happen */
+ perror("XNextEventTimeout: select() failure");
+ return(False);
+ case 0: /* timeout */
+ return(False);
+ }
+ }
+
+ XNextEvent(display, event_return);
+ return(True);
+}
+
+
+#ifdef XPU_USE_THREADS
+/**
+ ** XpuPrintToFile() - threaded version
+ ** Create consumer thread which creates it's own display connection to print server
+ ** (a 2nd display connection/thread is required to avoid deadlocks within Xlib),
+ ** registers (Xlib-internal) consumer callback (via XpGetDocumentData(3Xp)) and
+ ** processes/eats all incoming events via MyPrintToFileProc(). A final call to
+ ** MyPrintToFileProc() cleans-up all stuff and sets the "done" flag.
+ ** Note that these callbacks are called directly by Xlib while waiting for events in
+ ** XNextEvent() because XpGetDocumentData() registeres them as "internal" callbacks,
+ ** e.g. XNextEvent() does _not_ return before/after processing these events !!
+ **
+ ** Usage:
+ ** XpStartJob(pdpy, XPGetData);
+ ** handle = XpuPrintToFile(pdpy, pcontext, "myfile");
+ ** // render something
+ ** XpEndJob(); // or XpCancelJob()
+ ** status = XpuWaitForPrintFileChild(handle);
+ **
+ */
+
+typedef struct
+{
+#ifdef XPU_USE_NSPR
+ PRThread *prthread;
+#else
+ pthread_t tid;
+#endif
+ char *displayname;
+ Display *pdpy;
+ Display *parent_pdpy;
+ XPContext pcontext;
+ const char *file_name;
+ FILE *file;
+ XPGetDocStatus status;
+ Bool done;
+} MyPrintFileData;
+
+
+static
+void *XpuPrintToFile( Display *pdpy, XPContext pcontext, const char *filename )
+{
+ MyPrintFileData *mpfd; /* warning: shared between threads !! */
+
+ if( (mpfd = malloc(sizeof(MyPrintFileData))) == NULL )
+ return(NULL);
+
+ mpfd->parent_pdpy = pdpy;
+ mpfd->displayname = XDisplayString(pdpy);
+ mpfd->pdpy = NULL;
+ mpfd->pcontext = pcontext;
+ mpfd->file_name = filename;
+ mpfd->file = NULL;
+ mpfd->status = XPGetDocError;
+
+ /* make sure we can open the file for writing */
+ if( (mpfd->file = fopen(mpfd->file_name, "w")) == NULL )
+ {
+ /* fopen() error */
+ free(mpfd);
+ return(NULL);
+ }
+
+ /* its important to flush before we start the consumer thread,
+ * to make sure that the XpStartJob gets through first in the parent
+ */
+ XFlush(pdpy);
+#ifdef XPU_USE_NSPR
+ if( (mpfd->prthread = PR_CreateThread(PR_SYSTEM_THREAD, PrintToFile_Consumer, mpfd, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0)) == NULL )
+#else
+ if( pthread_create(&(mpfd->tid), NULL, PrintToFile_Consumer, mpfd) != 0 )
+#endif
+ {
+ /* pthread_create() error */
+ fclose(mpfd->file);
+ free(mpfd);
+ return(NULL);
+ }
+
+ /* we're still in the parent */
+ XPU_DEBUG_ONLY(printf("### parent started consumer thread.\n" ));
+ return(mpfd);
+}
+
+
+XPGetDocStatus XpuWaitForPrintFileChild( void *handle )
+{
+ MyPrintFileData *mpfd = (MyPrintFileData *)handle;
+ void *res;
+ XPGetDocStatus status;
+
+ /* Flush data a last time to make sure we send all data to Xprt and that
+ * the Xlib internal hooks have called a last time */
+ XFlush(mpfd->parent_pdpy);
+
+#ifdef XPU_USE_NSPR
+ if( PR_JoinThread(mpfd->prthread) != PR_SUCCESS )
+ perror("XpuWaitForPrintFileChild: PR_JoinThread() failure"); /* fixme(later): use NSPR error handling calls... */
+#else
+ if( XPU_TRACE(pthread_join(mpfd->tid, &res)) != 0 )
+ perror("XpuWaitForPrintFileChild: pthread_join() failure");
+#endif
+
+ status = mpfd->status;
+ free(handle);
+
+ XPU_DEBUG_ONLY(PrintXPGetDocStatus(status));
+ return(status);
+}
+
+#else /* XPU_USE_THREADS */
+/**
+ ** XpuPrintToFile() - fork() version
+ ** Create consumer thread which creates it's own display connection to print server
+ ** (a 2nd display connection/process is required to avoid deadlocks within Xlib),
+ ** registers (Xlib-internal) consumer callback (via XpGetDocumentData(3Xp)) and
+ ** processes/eats all incoming events via MyPrintToFileProc(). A final call to
+ ** MyPrintToFileProc() cleans-up all stuff and sets the "done" flag.
+ ** Note that these callbacks are called directly by Xlib while waiting for events in
+ ** XNextEvent() because XpGetDocumentData() registeres them as "internal" callbacks,
+ ** e.g. XNextEvent() does _not_ return before/after processing these events !!
+ **
+ ** Usage:
+ ** XpStartJob(pdpy, XPGetData);
+ ** handle = XpuPrintToFile(pdpy, pcontext, "myfile");
+ ** // render something
+ ** XpEndJob(); // or XpCancelJob()
+ ** status = XpuWaitForPrintFileChild(handle);
+ **
+ */
+typedef struct
+{
+ pid_t pid;
+ int pipe[2]; /* child-->parent communication pipe */
+ const char *displayname;
+ Display *pdpy;
+ Display *parent_pdpy;
+ XPContext pcontext;
+ const char *file_name;
+ FILE *file;
+ XPGetDocStatus status;
+ Bool done;
+} MyPrintFileData;
+
+
+static
+void *XpuPrintToFile( Display *pdpy, XPContext pcontext, const char *filename )
+{
+ MyPrintFileData *mpfd;
+
+ if( (mpfd = (MyPrintFileData *)malloc(sizeof(MyPrintFileData))) == NULL )
+ return(NULL);
+
+ /* create pipe */
+ if( pipe(mpfd->pipe) == -1 )
+ {
+ /* this should never happen, but... */
+ perror("XpuPrintToFile: cannot create pipe");
+ free(mpfd);
+ return(NULL);
+ }
+
+ mpfd->parent_pdpy = pdpy;
+ mpfd->displayname = XDisplayString(pdpy);
+ mpfd->pcontext = pcontext;
+ mpfd->file_name = filename;
+ mpfd->file = NULL;
+ mpfd->status = XPGetDocError;
+
+ /* make sure we can open the file for writing */
+ if( (mpfd->file = fopen(mpfd->file_name, "w")) == NULL )
+ {
+ /* fopen() error */
+ close(mpfd->pipe[1]);
+ close(mpfd->pipe[0]);
+ free(mpfd);
+ return(NULL);
+ }
+
+ /* its important to flush before we fork, to make sure that the
+ * XpStartJob gets through first in the parent
+ */
+ XFlush(pdpy);
+
+ mpfd->pid = fork();
+
+ if( mpfd->pid == 0 )
+ {
+ /* we're now in the fork()'ed child */
+ PrintToFile_Consumer(mpfd);
+ }
+ else if( mpfd->pid < 0 )
+ {
+ /* fork() error */
+ close(mpfd->pipe[1]);
+ close(mpfd->pipe[0]);
+ fclose(mpfd->file);
+ free(mpfd);
+ return(NULL);
+ }
+
+ /* we're still in the parent */
+ XPU_DEBUG_ONLY(printf("### parent fork()'ed consumer child.\n"));
+
+ /* child will write into file - we don't need it anymore here... :-) */
+ fclose(mpfd->file);
+ close(mpfd->pipe[1]);
+ return(mpfd);
+}
+
+
+XPGetDocStatus XpuWaitForPrintFileChild( void *handle )
+{
+ MyPrintFileData *mpfd = (MyPrintFileData *)handle;
+ int res;
+ XPGetDocStatus status = XPGetDocError; /* used when read() from pipe fails */
+
+ /* Flush data a last time to make sure we send all data to Xprt and that
+ * the Xlib internal hooks have called a last time */
+ XFlush(mpfd->parent_pdpy);
+
+ if( XPU_TRACE(waitpid(mpfd->pid, &res, 0)) == -1 )
+ perror("XpuWaitForPrintFileChild: waitpid failure");
+
+ /* read the status from the child */
+ if( read(mpfd->pipe[0], &status, sizeof(XPGetDocStatus)) != sizeof(XPGetDocStatus) )
+ {
+ perror("XpuWaitForPrintFileChild: can't read XPGetDocStatus");
+ }
+ close(mpfd->pipe[0]);
+
+ free(handle);
+
+ XPU_DEBUG_ONLY(PrintXPGetDocStatus(status));
+ return(status);
+}
+#endif /* XPU_USE_THREADS */
+
+
+static
+void MyPrintToFileProc( Display *pdpy,
+ XPContext pcontext,
+ unsigned char *data,
+ unsigned int data_len,
+ XPointer client_data )
+{
+ MyPrintFileData *mpfd = (MyPrintFileData *)client_data;
+
+ /* write to the file */
+ XPU_TRACE_CHILD((void)fwrite(data, data_len, 1, mpfd->file)); /* what about error handling ? */
+}
+
+
+static
+void MyFinishProc( Display *pdpy,
+ XPContext pcontext,
+ XPGetDocStatus status,
+ XPointer client_data )
+{
+ MyPrintFileData *mpfd = (MyPrintFileData *)client_data;
+
+ /* remove the file if unsuccessful */
+ if( status != XPGetDocFinished )
+ {
+ XPU_DEBUG_ONLY(printf("MyFinishProc: error %d\n", (int)status));
+ XPU_TRACE_CHILD(remove(mpfd->file_name));
+ }
+
+ XPU_TRACE_CHILD((void)fclose(mpfd->file)); /* what about error handling ? */
+
+ mpfd->status = status;
+ mpfd->done = True;
+}
+
+
+static
+#ifdef XPU_USE_NSPR
+void PrintToFile_Consumer( void *handle )
+#else
+void *PrintToFile_Consumer( void *handle )
+#endif
+{
+ MyPrintFileData *mpfd = (MyPrintFileData *)handle;
+ XEvent dummy;
+ struct timeval timeout;
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 100000; /* 1/10 s */
+
+ XPU_DEBUG_ONLY(printf("### child running, getting data from '%s'.\n", mpfd->displayname));
+
+ /* we cannot reuse fork()'ed display handles - our child needs his own one */
+ if( (mpfd->pdpy = XPU_TRACE_CHILD(XOpenDisplay(mpfd->displayname))) == NULL )
+ {
+ perror("child cannot open display");
+#ifdef XPU_USE_NSPR
+ return;
+#else
+ return(NULL);
+#endif
+ }
+
+ mpfd->done = False;
+
+ /* register "consumer" callbacks */
+ if( XPU_TRACE_CHILD(XpGetDocumentData(mpfd->pdpy, mpfd->pcontext,
+ MyPrintToFileProc, MyFinishProc,
+ (XPointer)mpfd)) == 0 )
+ {
+ XPU_DEBUG_ONLY(printf("XpGetDocumentData cannot register callbacks\n"));
+#ifdef XPU_USE_NSPR
+ return;
+#else
+ return(NULL);
+#endif
+ }
+
+ /* loop forever - libXp has registered hidden event callbacks for the consumer
+ * callbacks - the finishCB will call set the "done" boolean after all...
+ */
+ while( mpfd->done != True )
+ {
+ XNextEventTimeout(mpfd->pdpy, &dummy, &timeout);
+ }
+
+ XCloseDisplay(mpfd->pdpy);
+
+#ifdef XPU_USE_THREADS
+#ifdef XPU_USE_NSPR
+ return;
+#else
+ return(NULL);
+#endif
+#else
+ /* write the status to the parent */
+ if( XPU_TRACE_CHILD(write(mpfd->pipe[1], &mpfd->status, sizeof(XPGetDocStatus))) != sizeof(XPGetDocStatus) )
+ {
+ perror("PrintToFile_Consumer: can't write XPGetDocStatus");
+ }
+
+ /* we don't do any free's or close's, as we are just
+ * going to exit, in fact, get out without calling any C++
+ * destructors, etc., as we don't want anything funny to happen
+ * to the parent
+ */
+ XPU_TRACE_CHILD(_exit(EXIT_SUCCESS));
+#endif /* XPU_USE_THREADS */
+}
+
+/* EOF. */