/* Copyright (C) 1998 artofcode LLC.  All rights reserved.
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA, 02111-1307.

*/

/*$Id: gdevprna.h,v 1.2.6.1.2.1 2003/01/17 00:49:01 giles Exp $ */
/* Generic asynchronous printer driver support */

/* Initial version 2/1/1998 by John Desrosiers (soho@crl.com) */
/* 7/28/98 ghost@aladdin.com - Updated to Ghostscript coding standards. */

#ifndef gdevprna_INCLUDED
# define gdevprna_INCLUDED

# include "gdevprn.h"
# include "gxsync.h"

/* 
 * General
 * -------
 * Async drivers actually create two separate instances of the device at
 * the same time. The first (the writer instance) is only used in the
 * interpretation operation; it feeds rendering commands into the command 
 * lists. The second device instance is used only for rendering the 
 * commands placed into the command list by the writer.

 * The writer builds a command list for an entire page; the command list 
 * is only queued for rendering once a page's command list is completely 
 * built. The only exception to this rule is when the interpreter runs
 * out of memory, or when no free command list memory is available. In
 * such cases, the interpreter queues a "partial page" consisting of all
 * command list data written so far, plus a command indicating that the
 * page description is not complete. After queuing the partial page, the
 * interpereter waits until the rendering process has freed enough
 * command list memory to enable the interpreter to proceed.

 * To avoid deadlocks when the system runs out of memory, special
 * memory allocation provisions are made on both the writer and 
 * renderer sides. On the writer side, enough "reserve" bandlist
 * memory is set aside at startup time to cover the needs of queuing a
 * partial page to the renderer. The renderer operates out of a fixed
 * memory space; that way, it can always complete rendering pages with
 * the memory it has. To this end, the writer protects the renderer
 * from consuming unbounded amounts of memory by a) never putting
 * complex paths into the command list, b) pre-clipping any output
 * unless the clip path consists of a single rectangle, c) never putting
 * high-level images into the clip path unless the image in question
 * meets some very stringent requirements, such as only being rotated by
 * even multiples of 90 degrees and having source-image data rows which
 * fit into the command buffer in one piece. These restrictions are what
 * dictate the "restricted bandlist format."

 * Note that the renderer's instance of the device driver uses the
 * renderer's memory. That implies that it must also operate in a small, 
 * fixed amount of memory, and must do all memory allocation using the
 * memory allocator pointed to by the render device's ->memory member.

 * Opening the Device
 * ------------------
 * The writer instance is opened first. This occurs when the system
 * calls the "standard" open procedure via the device's procedure
 * vector. The driver must implement the open function, but must call
 * down to gdev_prn_async_write_open instead of calling down to
 * gdev_prn_open. Before calling down to gdev_prn_async_write_open, the
 * driver must:
 *  a - init several procedure vectors, to wit: start_render_thread,
 *      buffer_page, print_page_copies,
 *  b - init space_params.band.BandWidth, space_params.band.BandHeight,
 *      space_params.BufferSpace (see extended comments in gdevasyn.c
 *      for details on computing appropriate values).
 *  c - if it implements those functions, the driver must init the
 *      procedure vectors for: put_params, get_hardware_params,
 *      output_page, open_render_device.
 * Notice that there are two procedure vectors: the usual std_procs, and
 * the printer-specific printer_procs.

 * Since partial page support imposes extra requirements on drivers,
 * such support can be disabled by zeroing out (in the async writer open
 * routine, after calling down to gdev_prn_async_write_open) the 
 * free_up_bandlist_memory member of the driver structure. Doing so
 * will, of course, cause interpretation to fail if memory runs out.

 * Once the driver calls down to gdev_prn_async_write_open, the async
 * support logic will create a second instance of the driver for
 * rendering, but will not open it just yet. Instead, the async logic 
 * will attempt to synchronize the two device instances.

 * Synchrnonizing the instances
 * ----------------------------
 * While still in the gdev_prn_async_write_open routine, the async logic
 * will call printer_procs.start_render_thread (which the driver is
 * required to implement). start_render_thread must somehow either start a new
 * thread or rendez-vous with an existing thread for use in rendering,
 * then return. start_render_thread must also have caused the render thread 
 * to call gdev_prn_async_render_thread, passing it as an argument a magic
 * cookie passed to start_render_thread. start_render_thread will only
 * return once the device has been closed and all renering has been
 * completed.

 * The render device will be opened on the render device's thread, by
 * calling printer_procs.open_render_device.

 * Rendering Operation
 * -------------------
 * During rendering, the device will not see rendering operations -- the
 * first "rendering" operations the driver will see is when the renderer
 * instance's print_page_copies or buffer_page routines get called. In
 * both cases, the appropriate routine must then perform get_bits calls
 * on the async logic in order to retrieve rendered bits, then transmit
 * them to the appropriate device buffers.

 * The complication that is introduced is that which is related to partial
 * pages: A buffer_page call instructs the driver to grab the rendered bits,
 * but to keep the rendered bits available for later instead of marking on
 * media. This implies that a buffer_page call opens a context where
 * subsequent buffer_page's and print_page_copies' must first initialize the
 * rendering buffers with the previous rendering results before calling
 * get_bits. The first print_page_copies closes the context that was opened
 * by the initial buffer_page -- the driver must go back to normal rendering
 * until a new buffer_page comes along.
 */

/* -------------- Type declarations --------------- */

/* typedef is in gdevprn.h */
/* typedef struct gdev_prn_start_render_params_s gdev_prn_start_render_params;*/
struct gdev_prn_start_render_params_s {
    gx_device_printer *writer_device;/* writer dev that points to render dev */
    gx_semaphore_t *open_semaphore;	/* signal this once open_code is set */
    int open_code;		/* RETURNS status of open of reader device */
};

/* -------- Macros used to initialize render-specific structures ------ */

#define init_async_render_procs(xpdev, xstart_render_thread,\
				xbuffer_page, xprint_page_copies)\
  BEGIN\
    (xpdev)->printer_procs.start_render_thread = (xstart_render_thread);\
    (xpdev)->printer_procs.buffer_page = (xbuffer_page);\
    (xpdev)->printer_procs.print_page_copies = (xprint_page_copies);\
  END

/* -------------- Global procedure declarations --------- */

/* Open this printer device in ASYNC (overlapped) mode.
 *
 * This routine is always called by the concrete device's xx_open routine 
 * in lieu of gdev_prn_open.
 */
int gdev_prn_async_write_open(P4(gx_device_printer *pdev, int max_raster,
				 int min_band_height, int max_src_image_row));

/* Open the render portion of a printer device in ASYNC (overlapped) mode.
 *
 * This routine is always called by concrete device's xx_open_render_device
 * in lieu of gdev_prn_open.
 */
int gdev_prn_async_render_open(P1(gx_device_printer *prdev));

/*
 * Must be called by async device driver implementation (see
 * gdevprna.h under "Synchronizing the Instances"). This is the
 * rendering loop, which requires its own thread for as long as
 * the device is open. This proc only returns after the device is closed.
 */
int	/* rets 0 ok, -ve error code */
gdev_prn_async_render_thread(P1(gdev_prn_start_render_params *));

#endif				/* gdevprna_INCLUDED */
