[xorg-commit-diffs] xc/lib/XprintUtil Imakefile, NONE, 1.1.4.1 xprintutil.c, NONE, 1.1.4.1 xprintutil.h, NONE, 1.1.4.1 xprintutil_printtofile.c, NONE, 1.1.4.1

Roland Mainz xorg-commit at pdx.freedesktop.org
Wed Apr 21 20:03:18 EST 2004


Committed by: gisburn

Update of /cvs/xorg/xc/lib/XprintUtil
In directory pdx:/tmp/cvs-serv5532/lib/XprintUtil

Added Files:
      Tag: XORG-CURRENT
	Imakefile xprintutil.c xprintutil.h xprintutil_printtofile.c 
Log Message:
Fix for http://pdx.freedesktop.org/cgi-bin/bugzilla/show_bug.cgi?id=530 - Land XPRINT branch on XORG-CURRENT

--- NEW FILE: Imakefile ---
#define DoNormalLib   YES
# XprintUtil is not a stable interface yet, therefore avoid shipping it
# as shared lib for now
#define DoSharedLib   NO
#define DoDebugLib    NO
#define DoProfileLib  NO
#define HasSharedData NO
#define LibName       XprintUtil
#define SoRev         SOZLIBREV
#define IncSubdir     X11
#define IncSubSubdir  XprintUtil


HEADERS = xprintutil.h

SRCS = xprintutil.c xprintutil_printtofile.c
OBJS = xprintutil.o xprintutil_printtofile.o

#include <Library.tmpl>

DependTarget()

--- NEW FILE: xprintutil.c ---
/******************************************************************************
 ******************************************************************************
 **
 ** (c) Copyright 2001-2004 Roland Mainz <roland.mainz at 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
[...1639 lines suppressed...]
  
  return(flags);
}

XpuSupportedFlags XpuGetSupportedJobAttributes(Display *pdpy, XPContext pcontext)
{
  return XpuGetSupportedAttributes(pdpy, pcontext, XPPrinterAttr, "job-attributes-supported");
}

XpuSupportedFlags XpuGetSupportedDocAttributes(Display *pdpy, XPContext pcontext)
{
  return XpuGetSupportedAttributes(pdpy, pcontext, XPPrinterAttr, "document-attributes-supported");
}

XpuSupportedFlags XpuGetSupportedPageAttributes(Display *pdpy, XPContext pcontext)
{
  return XpuGetSupportedAttributes(pdpy, pcontext, XPPrinterAttr, "xp-page-attributes-supported");
}

/* EOF. */

--- NEW FILE: xprintutil.h ---
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
#ifndef XPRINTUTIL_H
#define XPRINTUTIL_H 1
/******************************************************************************
 ******************************************************************************
 **
 ** (c) Copyright 2001-2004 Roland Mainz <roland.mainz at 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.
 **
 ******************************************************************************
 *****************************************************************************/


/* Force ANSI C prototypes from X11 headers */
#ifndef FUNCPROTO 
#define FUNCPROTO 15
#endif /* !FUNCPROTO */

#include <X11/Xlibint.h>
#include <X11/extensions/Print.h>
#include <X11/Intrinsic.h>

/* I don't know how to make this "better" yet... ;-( */
#ifdef USE_MOZILLA_TYPES
#include <prtypes.h>
#include <prmem.h>
#include <prthread.h>
#define XPU_USE_NSPR 1
/*
 * Disabled for now - Threaded codepath does not work properly always.
 * See bug 134570 ("Print-to-file not working with threaded XprintUtil")
 * #define XPU_USE_THREADS 1
 */
#endif /* USE_MOZILLA_TYPES */

#ifdef DEBUG
/* trace function calls */
#define XPU_TRACE(EX)  (puts(#EX),EX)
/* trace function calls in child */
#define XPU_TRACE_CHILD(EX)  (puts("child: " #EX),EX)
/* execute function EX only in debug mode */
#define XPU_DEBUG_ONLY(EX)  (EX)
#else
#define XPU_TRACE(EX) (EX)
#define XPU_TRACE_CHILD(EX) (EX)
#define XPU_DEBUG_ONLY(EX)
#endif /* DEBUG */

/* debug/logging: replace NULLptrs with "<NULL>" string */
#define XPU_NULLXSTR(s) (((s)!=NULL)?(s):("<NULL>"))

/*
 * Struct for XpuGetMediumSourceSizeList(), XpuFreeMediumSourceSizeList(),
 * XpuSetDocMediumSourceSize(), XpuSetPageMediumSourceSize(),
 * XpuFindMediumSourceSizeBy*()
 */
typedef struct {
  const char *tray_name;
  const char *medium_name;
  int         mbool;
  float       ma1; 
  float       ma2; 
  float       ma3; 
  float       ma4;
} XpuMediumSourceSizeRec, *XpuMediumSourceSizeList;

/*
 * Struct for XpuGetResolutionList(), XpuFreeResolutionList(),
 * XpuGetResolution(), XpuSetPageResolution(), XpuSetDocResolution(),
 * XpuFindResolution()
 */
typedef struct {
  long dpi;
  /* ToDo: Support for Xdpi != Ydpi */
} XpuResolutionRec, *XpuResolutionList;

/*
 * Struct for XpuGetOrientationList(), XpuFreeOrientationList(),
 * XpuFindOrientationBy*(), XpuSetPageResolution(), 
 * XpuSetDocOrientation()
 */
typedef struct {
  const char *orientation;
} XpuOrientationRec, *XpuOrientationList;

/*
 * Struct for XpuGetPlexList(), XpuFreePlexList(), XpuFindPlexBy*(),
 * XpuSetDocPlex(), XpuSetPagePlex()
 */
typedef struct {
  const char *plex;
} XpuPlexRec, *XpuPlexList;


/* XPUATTRIBUTESUPPORTED_*:
 * Flags which indicate whether it is allowed to set/change a specific attribute
 */
typedef long XpuSupportedFlags;
/* Job attributes */
#define XPUATTRIBUTESUPPORTED_JOB_NAME                     (1L<<0)
#define XPUATTRIBUTESUPPORTED_JOB_OWNER                    (1L<<1)
#define XPUATTRIBUTESUPPORTED_NOTIFICATION_PROFILE         (1L<<2)
/* Document/Page attributes */
#define XPUATTRIBUTESUPPORTED_COPY_COUNT                   (1L<<3)
#define XPUATTRIBUTESUPPORTED_DOCUMENT_FORMAT              (1L<<4)
#define XPUATTRIBUTESUPPORTED_CONTENT_ORIENTATION          (1L<<5)
#define XPUATTRIBUTESUPPORTED_DEFAULT_PRINTER_RESOLUTION   (1L<<6)
#define XPUATTRIBUTESUPPORTED_DEFAULT_INPUT_TRAY           (1L<<7)
#define XPUATTRIBUTESUPPORTED_DEFAULT_MEDIUM               (1L<<8)
#define XPUATTRIBUTESUPPORTED_PLEX                         (1L<<9)

/* prototypes */
_XFUNCPROTOBEGIN

int XpuCheckExtension( Display *pdpy );

/* Create/destroy connection to printer */
int XpuGetPrinter( const char *printername, Display **pdpyptr, XPContext *pcontextptr );
void XpuClosePrinterDisplay(Display *pdpy, XPContext pcontext);

/* Misc. functions */
void XpuSetOneAttribute( Display *pdpy, XPContext pcontext, 
                         XPAttributes type, const char *attribute_name, const char *value, XPAttrReplacement replacement_rule );
void XpuSetOneLongAttribute( Display *pdpy, XPContext pcontext, 
                         XPAttributes type, const char *attribute_name, long value, XPAttrReplacement replacement_rule );
int XpuCheckSupported( Display *pdpy, XPContext pcontext, XPAttributes type, const char *attribute_name, const char *query );
int XpuSetJobTitle( Display *pdpy, XPContext pcontext, const char *title );
int XpuGetOneLongAttribute( Display *pdpy, XPContext pcontext, XPAttributes type, const char *attribute_name, long *result );
#ifdef DEBUG
void dumpXpAttributes( Display *pdpy, XPContext pcontext );
#endif /* DEBUG */
void XpuWaitForPrintNotify( Display *pdpy, int xp_event_base, int detail );

/* Get list of printers */
XPPrinterList XpuGetPrinterList( const char *printer, int *res_list_count );
void XpuFreePrinterList( XPPrinterList list );

/* Set number of document copies */
int XpuSetDocumentCopies( Display *pdpy, XPContext pcontext, long num_copies );

/* Get/Set/Query supported mediums (paper sizes) */
XpuMediumSourceSizeList XpuGetMediumSourceSizeList( Display *pdpy, XPContext pcontext, int *numEntriesPtr );
void XpuFreeMediumSourceSizeList( XpuMediumSourceSizeList list );
int XpuSetDocMediumSourceSize( Display *pdpy, XPContext pcontext, XpuMediumSourceSizeRec *medium_spec );
int XpuSetPageMediumSourceSize( Display *pdpy, XPContext pcontext, XpuMediumSourceSizeRec *medium_spec );
XpuMediumSourceSizeRec *
XpuFindMediumSourceSizeBySize( XpuMediumSourceSizeList mlist, int mlist_count,
                               float page_width_mm, float page_height_mm, float tolerance );
XpuMediumSourceSizeRec *
XpuFindMediumSourceSizeByBounds( XpuMediumSourceSizeList mlist, int mlist_count, 
                                 float m1, float m2, float m3, float m4, float tolerance );
XpuMediumSourceSizeRec *
XpuFindMediumSourceSizeByName( XpuMediumSourceSizeList mlist, int mlist_count, 
                               const char *tray_name, const char *medium_name );

/* Get/Set resolution */
XpuResolutionList XpuGetResolutionList( Display *pdpy, XPContext pcontext, int *numEntriesPtr );
void XpuFreeResolutionList( XpuResolutionList list );
Bool XpuGetResolution( Display *pdpy, XPContext pcontext, long *dpi );
Bool XpuSetPageResolution( Display *pdpy, XPContext pcontext, XpuResolutionRec * );
Bool XpuSetDocResolution( Display *pdpy, XPContext pcontext, XpuResolutionRec * );
XpuResolutionRec *XpuFindResolution( XpuResolutionList list, int list_count, long min_dpi, long max_dpi );

/* Get/Set orientation */
XpuOrientationList XpuGetOrientationList( Display *pdpy, XPContext pcontext, int *numEntriesPtr );
void XpuFreeOrientationList( XpuOrientationList list );
XpuOrientationRec *
XpuFindOrientationByName( XpuOrientationList list, int list_count, const char *orientation );
int XpuSetDocOrientation( Display *pdpy, XPContext pcontext, XpuOrientationRec *rec );
int XpuSetPageOrientation( Display *pdpy, XPContext pcontext, XpuOrientationRec *rec );

/* Get/set plex modes */
XpuPlexList XpuGetPlexList( Display *pdpy, XPContext pcontext, int *numEntriesPtr );
void XpuFreePlexList( XpuPlexList list );
XpuPlexRec *XpuFindPlexByName( XpuPlexList list, int list_count, const char *plex );
int XpuSetDocPlex( Display *pdpy, XPContext pcontext, XpuPlexRec *rec );
int XpuSetPagePlex( Display *pdpy, XPContext pcontext, XpuPlexRec *rec );

/* Start job to printer (spooler) or file */
void XpuStartJobToSpooler(Display *pdpy);
void *XpuStartJobToFile( Display *pdpy, XPContext pcontext, const char *filename );
XPGetDocStatus XpuWaitForPrintFileChild( void *handle );

/* Get flags which indicate whether it is allowed to set/change a specific attribute */
XpuSupportedFlags XpuGetSupportedJobAttributes(Display *pdpy, XPContext pcontext);
XpuSupportedFlags XpuGetSupportedDocAttributes(Display *pdpy, XPContext pcontext);
XpuSupportedFlags XpuGetSupportedPageAttributes(Display *pdpy, XPContext pcontext);

_XFUNCPROTOEND

#define XpuGetJobAttributes( pdpy, pcontext )     XpGetAttributes( (pdpy), (pcontext), XPJobAttr )
#define XpuGetDocAttributes( pdpy, pcontext )     XpGetAttributes( (pdpy), (pcontext), XPDocAttr )
#define XpuGetPageAttributes( pdpy, pcontext )    XpGetAttributes( (pdpy), (pcontext), XPPageAttr )
#define XpuGetPrinterAttributes( pdpy, pcontext ) XpGetAttributes( (pdpy), (pcontext), XPPrinterAttr )
#define XpuGetServerAttributes( pdpy, pcontext )  XpGetAttributes( (pdpy), (pcontext), XPServerAttr )

#endif /* !XPRINTUTIL_H */
/* EOF. */

--- NEW FILE: xprintutil_printtofile.c ---
/******************************************************************************
 ******************************************************************************
 **
 ** (c) Copyright 2001-2004 Roland Mainz <roland.mainz at 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. */




More information about the xorg-commit-diffs mailing list