summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <Robin.Watts@artifex.com>2020-06-04 19:27:15 +0100
committerRobin Watts <Robin.Watts@artifex.com>2020-06-25 00:19:12 +0100
commit4438e3e5bc4dfc9e46fc86d86d912171ec582a4e (patch)
treec2cbc61774320346337f2311876f4bc9de46d011
parent7e427246abb151cde9154783775acfaa776b502e (diff)
downloadghostpdl-4438e3e5bc4dfc9e46fc86d86d912171ec582a4e.tar.gz
Rework display device "DisplayHandle" passing.
Add gsapi_{,de}register_callout API. This allows integrators to register handlers for "callouts" from gs devices. The first example of such a callout is the display device. Previously, this has relied on the gsapi_set_display_callback API to pass a pointer to a structure into the core, from where it was hackily poked into the display device structure. Instead, we now have the display device "callout" to registered handlers to get the structure and the handle to use. The legacy API is maintained by the API level implementing a handler to return the display callback in response to the devices callout. The code to do the 'poking' of the display device has therefore been removed, and replaced by code that checks to see if an opened device needs reopening after init, if so, opens/closes it.
-rw-r--r--base/gsdevice.c6
-rw-r--r--base/gslibctx.c68
-rw-r--r--base/gslibctx.h16
-rw-r--r--base/gxdevcli.h3
-rw-r--r--base/gxdevsop.h9
-rw-r--r--devices/gdevdsp.c228
-rw-r--r--devices/gdevdsp.h23
-rw-r--r--devices/gdevdsp2.h1
-rw-r--r--doc/API.htm127
-rw-r--r--pcl/pl/plapi.c20
-rw-r--r--pcl/pl/plapi.h33
-rw-r--r--pcl/pl/plmain.c66
-rw-r--r--psi/gsdll2.def2
-rw-r--r--psi/gsdll32.def2
-rw-r--r--psi/gsdll32metro.def3
-rw-r--r--psi/gsdll64.def2
-rw-r--r--psi/gsdll64metro.def2
-rw-r--r--psi/gsdllARM32metro.def2
-rw-r--r--psi/iapi.c62
-rw-r--r--psi/iapi.h33
-rw-r--r--psi/idisp.c70
-rw-r--r--psi/idisp.h5
-rw-r--r--psi/imain.c6
-rw-r--r--psi/int.mak2
24 files changed, 614 insertions, 177 deletions
diff --git a/base/gsdevice.c b/base/gsdevice.c
index cd9460374..ec0f79e58 100644
--- a/base/gsdevice.c
+++ b/base/gsdevice.c
@@ -1346,3 +1346,9 @@ bool gx_color_info_equal(const gx_device_color_info * p1, const gx_device_color_
return false;
return true;
}
+
+int gx_callout(gx_device *dev, int id, int size, void *data)
+{
+ return gs_lib_ctx_callout(dev->memory, dev->dname,
+ id, size, data);
+}
diff --git a/base/gslibctx.c b/base/gslibctx.c
index b2e046e06..35490b504 100644
--- a/base/gslibctx.c
+++ b/base/gslibctx.c
@@ -1179,3 +1179,71 @@ int gs_lib_ctx_get_args(gs_lib_ctx_t *ctx, const char * const **argv)
*argv = (const char * const *)core->argv;
return core->argc;
}
+
+int gs_lib_ctx_register_callout(gs_memory_t *mem, gs_callout_fn fn, void *arg)
+{
+ gs_lib_ctx_core_t *core;
+ gs_callout_list_t *entry;
+
+ if (mem == NULL || mem->gs_lib_ctx == NULL ||
+ mem->gs_lib_ctx->core == NULL || fn == NULL)
+ return 0;
+
+ core = mem->gs_lib_ctx->core;
+ entry = (gs_callout_list_t *)gs_alloc_bytes(mem->non_gc_memory,
+ sizeof(*entry),
+ "gs_callout_list_t");
+ if (entry == NULL)
+ return_error(gs_error_VMerror);
+ entry->next = core->callouts;
+ entry->callout = fn;
+ entry->handle = arg;
+ core->callouts = entry;
+
+ return 0;
+}
+
+void gs_lib_ctx_deregister_callout(gs_memory_t *mem, gs_callout_fn fn, void *arg)
+{
+ gs_lib_ctx_core_t *core;
+ gs_callout_list_t **entry;
+
+ if (mem == NULL || mem->gs_lib_ctx == NULL ||
+ mem->gs_lib_ctx->core == NULL || fn == NULL)
+ return;
+
+ core = mem->gs_lib_ctx->core;
+ entry = &core->callouts;
+ while (*entry) {
+ if ((*entry)->callout == fn && (*entry)->handle == arg) {
+ gs_callout_list_t *next = (*entry)->next;
+ gs_free_object(mem->non_gc_memory, *entry, "gs_callout_list_t");
+ *entry = next;
+ } else {
+ entry = &(*entry)->next;
+ }
+ }
+}
+
+int gs_lib_ctx_callout(gs_memory_t *mem, const char *dev_name,
+ int id, int size, void *data)
+{
+ gs_lib_ctx_core_t *core;
+ gs_callout_list_t *entry;
+
+ if (mem == NULL || mem->gs_lib_ctx == NULL || mem->gs_lib_ctx->core == NULL)
+ return -1;
+
+ core = mem->gs_lib_ctx->core;
+ entry = core->callouts;
+ while (entry) {
+ int code = entry->callout(mem->gs_lib_ctx->top_of_system,
+ entry->handle, dev_name, id, size, data);
+ if (code >= 0)
+ return code;
+ if (code != gs_error_unknownerror)
+ return code;
+ entry = entry->next;
+ }
+ return -1;
+}
diff --git a/base/gslibctx.h b/base/gslibctx.h
index f6ce40a32..173d2dc2a 100644
--- a/base/gslibctx.h
+++ b/base/gslibctx.h
@@ -81,6 +81,14 @@ typedef struct gs_fs_list_s {
struct gs_fs_list_s *next;
} gs_fs_list_t;
+typedef int (*gs_callout_fn)(void *, void *, const char *, int, int, void *);
+
+typedef struct gs_callout_list_s {
+ struct gs_callout_list_s *next;
+ gs_callout_fn callout;
+ void *handle;
+} gs_callout_list_t;
+
typedef struct {
void *monitor;
int refs;
@@ -118,6 +126,8 @@ typedef struct {
* all builds. */
void *cal_ctx;
+ gs_callout_list_t *callouts;
+
/* Stashed args */
int arg_max;
int argc;
@@ -194,6 +204,12 @@ void *gs_lib_ctx_get_cms_context( const gs_memory_t *mem );
void gs_lib_ctx_set_cms_context( const gs_memory_t *mem, void *cms_context );
int gs_lib_ctx_get_act_on_uel( const gs_memory_t *mem );
+int gs_lib_ctx_register_callout(gs_memory_t *mem, gs_callout_fn, void *arg);
+void gs_lib_ctx_deregister_callout(gs_memory_t *mem, gs_callout_fn, void *arg);
+int gs_lib_ctx_callout(gs_memory_t *mem, const char *dev_name,
+ int id, int size, void *data);
+
+
#ifndef GS_THREADSAFE
/* HACK to get at non garbage collection memory pointer
*
diff --git a/base/gxdevcli.h b/base/gxdevcli.h
index e7a054321..e7c3a78a4 100644
--- a/base/gxdevcli.h
+++ b/base/gxdevcli.h
@@ -1982,4 +1982,7 @@ void gx_device_dump(gx_device *dev, const char *text);
/* Compare color information structures */
bool gx_color_info_equal(const gx_device_color_info *p1, const gx_device_color_info *p2);
+/* Perform a callout to registered handlers from the device. */
+int gx_callout(gx_device *dev, int id, int size, void *data);
+
#endif /* gxdevcli_INCLUDED */
diff --git a/base/gxdevsop.h b/base/gxdevsop.h
index 3ba09fd2b..4c597de08 100644
--- a/base/gxdevsop.h
+++ b/base/gxdevsop.h
@@ -379,6 +379,15 @@ enum {
* 0 otherwise.
*/
gxdso_supports_alpha,
+ /* gxdso_reopen_after_init:
+ * data = NULL
+ * size = 0
+ * Returns 1 if the device should be closed/reopened after gs
+ * finishes initialisation (e.g. to give it a chance to fetch
+ * configuration from registered callout handlers),
+ * 0 otherwise.
+ */
+ gxdso_reopen_after_init,
/* Add new gxdso_ keys above this. */
gxdso_pattern__LAST
};
diff --git a/devices/gdevdsp.c b/devices/gdevdsp.c
index e42ad3696..2fb542b93 100644
--- a/devices/gdevdsp.c
+++ b/devices/gdevdsp.c
@@ -210,6 +210,7 @@ const gx_device_display gs_display_device =
NULL, /* mdev */
NULL, /* callback */
NULL, /* pHandle */
+ 0, /* pHandle_set */
0, /* nFormat */
NULL, /* pBitmap */
0, /* ulBitmapSize */
@@ -242,12 +243,34 @@ display_open(gx_device * dev)
{
gx_device_display *ddev = (gx_device_display *) dev;
int ccode;
+ gs_display_get_callback_t data;
/* Erase these, in case we are opening a copied device. */
ddev->mdev = NULL;
ddev->pBitmap = NULL;
ddev->ulBitmapSize = 0;
+ /* Fetch our callback procedures. */
+ data.callback = NULL;
+ data.caller_handle = NULL;
+ ccode = gx_callout(dev, DISPLAY_CALLOUT_GET_CALLBACK, sizeof(data), &data);
+ if (ccode < 0) {
+ ccode = gx_callout(dev, DISPLAY_CALLOUT_GET_CALLBACK_LEGACY, sizeof(data), &data);
+ if (ccode < 0) {
+ ddev->callback = NULL;
+ ddev->pHandle = NULL;
+ if (ccode != gs_error_unknownerror)
+ return ccode;
+ } else {
+ ddev->callback = data.callback;
+ ddev->pHandle_set = 0;
+ }
+ } else {
+ ddev->callback = data.callback;
+ ddev->pHandle = data.caller_handle;
+ ddev->pHandle_set = 1;
+ }
+
/* Allow device to be opened "disabled" without a callback. */
/* The callback will be set later and the device re-opened. */
if (ddev->callback == NULL)
@@ -805,27 +828,31 @@ display_get_params(gx_device * dev, gs_param_list * plist)
size_t dptr;
char buf[64];
- idx = ((int)sizeof(size_t)) * 8 - 4;
- buf[i++] = '1';
- buf[i++] = '6';
- buf[i++] = '#';
- dptr = (size_t)(ddev->pHandle);
- while (idx >= 0) {
- val = (int)(dptr >> idx) & 0xf;
- if (val <= 9)
- buf[i++] = '0' + val;
- else
- buf[i++] = 'a' - 10 + val;
- idx -= 4;
- }
- buf[i] = '\0';
+ code = gx_default_get_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ if (!ddev->pHandle_set) {
+ idx = ((int)sizeof(size_t)) * 8 - 4;
+ buf[i++] = '1';
+ buf[i++] = '6';
+ buf[i++] = '#';
+ dptr = (size_t)(ddev->pHandle);
+ while (idx >= 0) {
+ val = (int)(dptr >> idx) & 0xf;
+ if (val <= 9)
+ buf[i++] = '0' + val;
+ else
+ buf[i++] = 'a' - 10 + val;
+ idx -= 4;
+ }
+ buf[i] = '\0';
- param_string_from_transient_string(dhandle, buf);
+ param_string_from_transient_string(dhandle, buf);
+ code = param_write_string(plist, "DisplayHandle", &dhandle);
+ }
- code = gx_default_get_params(dev, plist);
(void)(code < 0 ||
- (code = param_write_string(plist,
- "DisplayHandle", &dhandle)) < 0 ||
(code = param_write_int(plist,
"DisplayFormat", &ddev->nFormat)) < 0 ||
(code = param_write_float(plist,
@@ -890,96 +917,98 @@ display_put_params(gx_device * dev, gs_param_list * plist)
break;
}
- /* 64-bit systems need to use DisplayHandle as a string */
- switch (code = param_read_string(plist, "DisplayHandle", &dh)) {
- case 0:
- found_string_handle = 1;
- break;
- default:
- if ((code == gs_error_typecheck) && (sizeof(size_t) <= 4)) {
- /* 32-bit systems can use the older long type */
- switch (code = param_read_long(plist, "DisplayHandle",
- (long *)(&handle))) {
- case 0:
- if (dev->is_open) {
- if (ddev->pHandle != handle)
- ecode = gs_error_rangecheck;
- else
+ if (!ddev->pHandle_set) {
+ /* 64-bit systems need to use DisplayHandle as a string */
+ switch (code = param_read_string(plist, "DisplayHandle", &dh)) {
+ case 0:
+ found_string_handle = 1;
+ break;
+ default:
+ if ((code == gs_error_typecheck) && (sizeof(size_t) <= 4)) {
+ /* 32-bit systems can use the older long type */
+ switch (code = param_read_long(plist, "DisplayHandle",
+ (long *)(&handle))) {
+ case 0:
+ if (dev->is_open) {
+ if (ddev->pHandle != handle)
+ ecode = gs_error_rangecheck;
+ else
+ break;
+ }
+ else {
+ ddev->pHandle = handle;
break;
- }
- else {
- ddev->pHandle = handle;
+ }
+ goto hdle;
+ default:
+ ecode = code;
+ hdle:param_signal_error(plist, "DisplayHandle", ecode);
+ case 1:
break;
- }
- goto hdle;
- default:
- ecode = code;
- hdle:param_signal_error(plist, "DisplayHandle", ecode);
- case 1:
- break;
+ }
+ break;
}
+ ecode = code;
+ param_signal_error(plist, "DisplayHandle", ecode);
+ /* fall through */
+ case 1:
+ dh.data = 0;
break;
- }
- ecode = code;
- param_signal_error(plist, "DisplayHandle", ecode);
- /* fall through */
- case 1:
- dh.data = 0;
- break;
- }
- if (found_string_handle) {
- /*
- * Convert from a string to a pointer.
- * It is assumed that size_t has the same size as a pointer.
- * Allow formats (1234), (10#1234) or (16#04d2).
- */
- size_t ptr = 0;
- int i;
- int base = 10;
- int val;
- code = 0;
- for (i=0; i<dh.size; i++) {
- val = dh.data[i];
- if ((val >= '0') && (val <= '9'))
- val = val - '0';
- else if ((val >= 'A') && (val <= 'F'))
- val = val - 'A' + 10;
- else if ((val >= 'a') && (val <= 'f'))
- val = val - 'a' + 10;
- else if (val == '#') {
- base = (int)ptr;
- ptr = 0;
- if ((base != 10) && (base != 16)) {
+ }
+ if (found_string_handle) {
+ /*
+ * Convert from a string to a pointer.
+ * It is assumed that size_t has the same size as a pointer.
+ * Allow formats (1234), (10#1234) or (16#04d2).
+ */
+ size_t ptr = 0;
+ int i;
+ int base = 10;
+ int val;
+ code = 0;
+ for (i=0; i<dh.size; i++) {
+ val = dh.data[i];
+ if ((val >= '0') && (val <= '9'))
+ val = val - '0';
+ else if ((val >= 'A') && (val <= 'F'))
+ val = val - 'A' + 10;
+ else if ((val >= 'a') && (val <= 'f'))
+ val = val - 'a' + 10;
+ else if (val == '#') {
+ base = (int)ptr;
+ ptr = 0;
+ if ((base != 10) && (base != 16)) {
+ code = gs_error_rangecheck;
+ break;
+ }
+ continue;
+ }
+ else {
code = gs_error_rangecheck;
break;
}
- continue;
- }
- else {
- code = gs_error_rangecheck;
- break;
- }
- if (base == 10)
- ptr = ptr * 10 + val;
- else if (base == 16)
- ptr = ptr * 16 + val;
- else {
- code = gs_error_rangecheck;
- break;
- }
- }
- if (code == 0) {
- if (dev->is_open) {
- if (ddev->pHandle != (void *)ptr)
+ if (base == 10)
+ ptr = ptr * 10 + val;
+ else if (base == 16)
+ ptr = ptr * 16 + val;
+ else {
code = gs_error_rangecheck;
+ break;
+ }
+ }
+ if (code == 0) {
+ if (dev->is_open) {
+ if (ddev->pHandle != (void *)ptr)
+ code = gs_error_rangecheck;
+ }
+ else
+ ddev->pHandle = (void *)ptr;
+ }
+ if (code < 0) {
+ ecode = code;
+ param_signal_error(plist, "DisplayHandle", ecode);
}
- else
- ddev->pHandle = (void *)ptr;
- }
- if (code < 0) {
- ecode = code;
- param_signal_error(plist, "DisplayHandle", ecode);
}
}
@@ -1249,6 +1278,9 @@ display_spec_op(gx_device *dev, int op, void *data, int datasize)
if (op == gxdso_supports_devn) {
return (dev_proc(dev, fill_rectangle_hl_color) == display_fill_rectangle_hl_color);
}
+ if (op == gxdso_reopen_after_init) {
+ return 1;
+ }
return gx_default_dev_spec_op(dev, op, data, datasize);
}
diff --git a/devices/gdevdsp.h b/devices/gdevdsp.h
index b702a8e6b..a96bf023e 100644
--- a/devices/gdevdsp.h
+++ b/devices/gdevdsp.h
@@ -262,4 +262,27 @@ struct display_callback_v1_s {
#define DISPLAY_CALLBACK_V1_SIZEOF sizeof(struct display_callback_v1_s)
+#define DISPLAY_CALLOUT_GET_CALLBACK 0
+#define DISPLAY_CALLOUT_GET_CALLBACK_LEGACY 1
+
+typedef struct {
+ display_callback *callback;
+ void *caller_handle;
+} gs_display_get_callback_t;
+
+/* The display device calls a callout to find the callback structure
+ * and caller_handle from the environment (the DLL caller/user of the
+ * API).
+ * It passes:
+ * id = DISPLAY_CALLOUT_GET_CALLBACK.
+ * size = sizeof(gs_display_get_callback_t) (or larger);
+ * data = pointer to gs_display_get_callback_t instance for callout
+ * handler to fill in.
+ *
+ * In order to support the old gsapi_set_display_callback we have a
+ * related callout, DISPLAY_CALLOUT_GET_CALLBACK_LEGACY. Do not use
+ * this!
+ */
+
+
#endif /* gdevdsp_INCLUDED */
diff --git a/devices/gdevdsp2.h b/devices/gdevdsp2.h
index 021f46e00..6a43c0fe2 100644
--- a/devices/gdevdsp2.h
+++ b/devices/gdevdsp2.h
@@ -27,6 +27,7 @@ typedef struct gx_device_display_s gx_device_display;
gx_device_memory *mdev;\
display_callback *callback;\
void *pHandle;\
+ int pHandle_set;\
int nFormat;\
void *pBitmap;\
unsigned long ulBitmapSize;\
diff --git a/doc/API.htm b/doc/API.htm
index 92d7e0072..0a4679c4f 100644
--- a/doc/API.htm
+++ b/doc/API.htm
@@ -68,6 +68,8 @@
<li><a href="#set_poll_with_handle"><code>gsapi_set_poll_with_handle</code></a></li>
<li><a href="#set_poll"><code>gsapi_set_poll</code></a></li>
<li><a href="#set_display_callback"><code>gsapi_set_display_callback</code></a></li>
+<li><a href="#register_callout"><code>gsapi_register_callout</code></a></li>
+<li><a href="#deregister_callout"><code>gsapi_deregister_callout</code></a></li>
<li><a href="#set_arg_encoding"><code>gsapi_set_arg_encoding</code></a></li>
<li><a href="#run"><code>gsapi_run_string_begin</code></a></li>
<li><a href="#run"><code>gsapi_run_string_continue</code></a></li>
@@ -86,6 +88,8 @@
<li><a href="#add_fs"><code>gsapi_add_fs</code></a></li>
<li><a href="#remove_fs"><code>gsapi_remove_fs</code></a></li>
<li><a href="#return_codes">Return codes</a></li>
+<li><a href="#gsapi_fs_t">gsapi_fs_t</a></li>
+<li><a href="#callout">Callouts</a></li>
</ul>
<li><a href="#Example_usage">Example usage</a></li>
<li><a href="#stdio">Standard input and output</a></li>
@@ -231,6 +235,18 @@ int
<li><code>
int
+<a href="#register_callout">gsapi_register_callout</a>
+(void *instance, gs_callout callout, void *callout_handle);
+</code></li>
+
+<li><code>
+void
+<a href="#deregister_callout">gsapi_deregister_callout</a>
+(void *instance, gs_callout callout, void *callout_handle);
+</code></li>
+
+<li><code>
+int
<a href="#set_arg_encoding">gsapi_set_arg_encoding</a>
(void *instance, int encoding);
</code></li>
@@ -336,7 +352,7 @@ else {
<blockquote>
Create a new instance of Ghostscript.
This instance is passed to most other gsapi functions.
-The caller_handle will be provided to callback functions.
+The caller_handle is the default value that will be provided to callback functions.
<b>Unless Ghostscript has been compiled with the </code>GS_THREADSAFE</code>
define, only one instance at a time is supported.</b>
@@ -420,8 +436,14 @@ Otherwise the behaviour of this function matches
<h3><a name="set_display_callback"></a><code>gsapi_set_display_callback()</code></h3>
<blockquote>
+<b>This call is deprecated; please use
+<code><a href="#register_callout">gsapi_register_callout</a></code>
+to register a <a href="#callout">callout</a> handler for the
+<a href="#display">display</a> device in preference.</b>
Set the callback structure for the <a href="#display">display</a>
-device. If the <a href="#display">display</a> device is used,
+device. The handle passed in the callback functions is taken from
+the <code>DisplayHandle</code> parameter (or NULL if there is no
+such parameter). If the <a href="#display">display</a> device is used,
this must be called after
<code>gsapi_new_instance()</code>
and before <code>gsapi_init_with_args()</code>.
@@ -429,6 +451,20 @@ See <code><a href="../base/gdevdsp.h">gdevdsp.h</a></code>
for more details.
</blockquote>
+<h3><a name="register_callout"></a><code>gsapi_register_callout()</code></h3>
+<blockquote>
+<p>This call registers a <code><a href="#callout">callout</a></code>
+handler.</p>
+</blockquote>
+
+<h3><a name="deregister_callout"></a><code>gsapi_deregister_callout()</code></h3>
+<blockquote>
+<p>This call deregisters a <code><a href="#callout">callout</a></code> handler
+previously registered with <code><a href="#register_callout">gsapi_register_callout</a></code>.
+All three arguments must match exactly for the callout handler to
+be deregistered.</p>
+</blockquote>
+
<h3><a name="set_arg_encoding"></a><code>gsapi_set_arg_encoding()</code></h3>
<blockquote>
Set the encoding used for the interpretation of all subsequent args
@@ -868,6 +904,45 @@ Reopen a stream with a different mode. Behaves like
<code>freopen(fname, mode, FILE *)</code>.
</dl>
+<h3><a name="callout"></a>Callouts</h3>
+<blockquote>
+<p>Callouts are a mechanism
+for the core code (specifically devices) to communicate with the
+user of gsapi. This communication can take the form of passing
+information out vis-a-vis what devices are doing, or requesting
+configuration from the caller to affect exactly how the device
+itself works.</p>
+<p>This is deliberately an extensible system, so exact details of
+callouts should be documented with the device in question. In general
+however a callout handler will be of the form:</p>
+<pre>
+typedef int (*gs_callout)(void *callout_handle,
+ const char *device_name,
+ int id,
+ int size,
+ void *data);
+</pre>
+<p>The <code>callout_handle</code> value passed to the callout will
+be the value passed in at registration. The <code>device_name</code>
+should be a null-terminated string giving the name of the device
+(though care should be taken to cope with the case where
+<code>device_name</code> is NULL for potential future uses).
+The <code>id</code> value will have a (device-specific) meaning; see
+the documentation for the device in question for more details.
+Finally, <code>size</code> and <code>data</code> have callout
+specific meanings, but typically, <code>data</code> will be a pointer
+to data block (which may either be uninitialised or wholly/partially
+initialised on entry, and may be updated on exit), and <code>size</code>
+will be the size (in bytes) of the block pointed to by <code>data</code>.
+</p>
+<p>A return value of -1 (<code>gs_error_unknownerror</code>) means
+the callout was not recognised by the handler, and should be passed
+to more handlers. Other negative values are interpreted as standard
+Ghostscript error values, and stop the propagation of the callout.
+Non-negative return codes mean the callout was handled and should
+not be passed to any more registered callout handlers.</p>
+</blockquote>
+
<hr>
<h2><a name="Example_usage"></a>Example Usage</h2>
<p>To try out the following examples in a development environment like Microsoft's
@@ -1097,18 +1172,27 @@ This device provides you with access to the raster output of
Ghostscript. It is your responsibility to copy this raster
to a display window or printer.</p>
<p>
-To use this device, you must provide a callback structure
-with addresses of a number of callback functions.
-The address of the callback structure is provided using
-<code>gsapi_set_display_callback()</code>.
+In order for this device to operate, it needs access to a structure
+containing a set of callback functions, and a callback handle (an
+opaque <code>void *</code> that can be used by caller to locate its
+own state). There are 2 ways that the device can get this
+information, a legacy method, and a modern method.
+</p>
+<dl>
+<dt>Legacy method</dt>
+<dd>
+<p>The address of the callback structure, is provided
+using <code>gsapi_set_display_callback()</code>.
This must be called after
<code>gsapi_new_instance()</code>
and before
<code>gsapi_init_with_args()</code>.</p>
<p>
-The callbacks are for device open, close, resize, sync, page,
-memory allocation and updating.
-Each callback function contains a handle can be set using</p>
+With this call, the callback handle is passed as NULL by default, but can
+be overridden by using a parameter. We actively dislike
+this way of working, as we consider passing addresses
+via the command line distasteful. The handle can be
+set using</p>
<blockquote>
-sDisplayHandle=1234
</blockquote>
@@ -1146,6 +1230,31 @@ The previous API, using a number value:</p>
is still supported on 32 bit systems, but will cause a "typecheck"
error on 64 bit systems, and is considered deprecated. It should
not be used in new code.</p>
+</dd>
+<dt>Modern method
+<dd>
+<p>The preferred method is to register a callout handler using
+<code><a href="#register_callout">gsapi_register_callout</a></code>.
+When this handler is called for the <code>&quot;display&quot;</code>
+device, with <code>id = 0</code> (= <code>DISPLAY_CALLOUT_GET_CALLBACK</code>),
+then <code>data</code> should point to an empty <code>gs_display_get_callback_t</code>
+block, with <code>size = sizeof(gs_display_get_callback_t)</code>.
+</p>
+<pre>
+typedef struct {
+ display_callback *callback;
+ void *caller_handle;
+} gs_display_get_callback_t;
+</pre>
+<p>The handler should fill in the structure before returning,
+with a return code of 0.</p>
+</dd>
+</dl>
+<p>
+Note, that the DisplayHandle value is only consulted for
+display device callbacks registered using the (legacy, now deprecated)
+<code>gsapi_set_display_callback</code> API, not the preferred
+<code>gsapi_register_callout</code> based mechanism.
<p>
The device raster format can be configured using</p>
diff --git a/pcl/pl/plapi.c b/pcl/pl/plapi.c
index 86588d7ad..ce5559299 100644
--- a/pcl/pl/plapi.c
+++ b/pcl/pl/plapi.c
@@ -63,6 +63,7 @@ gsapi_new_instance(void **lib, void *caller_handle)
}
*lib = (void *)(chunk_mem->gs_lib_ctx);
+ chunk_mem->gs_lib_ctx->core->default_caller_handle = caller_handle;
return 0;
}
@@ -397,3 +398,22 @@ gsapi_remove_fs(void *instance, gsapi_fs_t *fs, void *secret)
return;
gs_remove_fs(ctx->memory, (gs_fs_t *)fs, secret);
}
+
+GSDLLEXPORT int GSDLLAPI gsapi_register_callout(
+ void *instance, gs_callout fn, void *handle)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance;
+ if (instance == NULL)
+ return gs_error_Fatal;
+ return gs_lib_ctx_register_callout(ctx->memory, fn, handle);
+}
+
+/* Deregister a handler for gs callouts. */
+GSDLLEXPORT void GSDLLAPI gsapi_deregister_callout(
+ void *instance, gs_callout fn, void *handle)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance;
+ if (instance == NULL)
+ return;
+ gs_lib_ctx_deregister_callout(ctx->memory, fn, handle);
+}
diff --git a/pcl/pl/plapi.h b/pcl/pl/plapi.h
index 84118fa26..d59d007ff 100644
--- a/pcl/pl/plapi.h
+++ b/pcl/pl/plapi.h
@@ -182,10 +182,43 @@ GSDLLEXPORT int GSDLLAPI gsapi_set_poll_with_handle(void *instance,
* If the display device is used, this must be called
* after gsapi_new_instance() and before gsapi_init_with_args().
* See gdevdisp.h for more details.
+ * DEPRECATED: Use gsapi_set_display_callback_with_handle instead.
*/
GSDLLEXPORT int GSDLLAPI gsapi_set_display_callback(
void *instance, display_callback *callback);
+/* The callout mechanism allows devices to query "callers" (users of the
+ * DLL) in device specific ways. The callout function pointer type will
+ * be called with:
+ * callout_handle = the value given at registration
+ * device_name = the name of the current device
+ * id = An integer, guaranteed to be unique within the
+ * callouts from a given device, identifying the
+ * purpose of this call.
+ * size = device/id specific, but typically the size of 'data'.
+ * data = device/id specific, but typically the pointer to
+ * an in/out data block.
+ * Returns an error code (gs_error_unknownerror (-1) if unclaimed,
+ * non-negative on success, standard gs error numbers recommended).
+ */
+typedef int (*gs_callout)(void *instance,
+ void *callout_handle,
+ const char *device_name,
+ int id,
+ int size,
+ void *data);
+
+/* Register a handler for gs callouts.
+ * This must be called after gsapi_new_instance() and (typically)
+ * before gsapi_init_with_args().
+ */
+GSDLLEXPORT int GSDLLAPI gsapi_register_callout(
+ void *instance, gs_callout callout, void *callout_handle);
+
+/* Deregister a handler for gs callouts. */
+GSDLLEXPORT void GSDLLAPI gsapi_deregister_callout(
+ void *instance, gs_callout callout, void *callout_handle);
+
/* Set the string containing the list of default device names
* for example "display x11alpha x11 bbox". Allows the calling
* application to influence which device(s) gs will try in order
diff --git a/pcl/pl/plmain.c b/pcl/pl/plmain.c
index c76d76137..783ac1ed9 100644
--- a/pcl/pl/plmain.c
+++ b/pcl/pl/plmain.c
@@ -128,7 +128,8 @@ struct pl_main_instance_s
pl_interp_implementation_t **implementations;
pl_interp_implementation_t *curr_implementation;
byte buf[8192]; /* languages read buffer */
- void *disp; /* display device pointer NB wrong - remove */
+ void *display; /* display device pointer - to support legacy API. Will
+ * be removed. */
};
@@ -168,10 +169,50 @@ get_device_index(const gs_memory_t * mem, const char *value)
return di;
}
+static int
+legacy_display_callout(void *instance,
+ void *handle,
+ const char *dev_name,
+ int id,
+ int size,
+ void *data)
+{
+ pl_main_instance_t *inst = (pl_main_instance_t *)handle;
+
+ if (dev_name == NULL)
+ return -1;
+ if (strcmp(dev_name, "display") != 0)
+ return -1;
+
+ if (id == DISPLAY_CALLOUT_GET_CALLBACK_LEGACY) {
+ /* get display callbacks */
+ gs_display_get_callback_t *cb = (gs_display_get_callback_t *)data;
+ cb->callback = inst->display;
+ return 0;
+ }
+ return -1;
+}
+
int
pl_main_set_display_callback(pl_main_instance_t *inst, void *callback)
{
- inst->disp = callback;
+ int code;
+
+ if (inst->display == NULL && callback != NULL) {
+ /* First registration. */
+ code = gs_lib_ctx_register_callout(inst->memory,
+ legacy_display_callout,
+ inst);
+ if (code < 0)
+ return code;
+ }
+ if (inst->display != NULL && callback == NULL) {
+ /* Deregistered. */
+ gs_lib_ctx_deregister_callout(inst->memory,
+ legacy_display_callout,
+ inst);
+ }
+ inst->display = callback;
return 0;
}
@@ -816,16 +857,19 @@ pl_top_create_device(pl_main_instance_t * pti, int index)
return code;
}
- /* If the display device is selected (default), set up the callback. NB Move me. */
- if (strcmp(gs_devicename(pti->device), "display") == 0) {
- gx_device_display *ddev;
-
- if (!pti->disp) {
- code = -1;
- } else {
- ddev = (gx_device_display *) pti->device;
- ddev->callback = (display_callback *) pti->disp;
+ if (pti->device->is_open &&
+ dev_proc(pti->device, dev_spec_op)(pti->device,
+ gxdso_reopen_after_init,
+ NULL, 0) == 1) {
+ code = gs_closedevice(pti->device);
+ if (code < 0)
+ return code;
+ code = gs_opendevice(pti->device);
+ if (code < 0) {
+ dmprintf(pti->device->memory,
+ "**** Unable to open the device, quitting.\n");
+ return code;
}
}
}
diff --git a/psi/gsdll2.def b/psi/gsdll2.def
index d69b60c6e..175e22163 100644
--- a/psi/gsdll2.def
+++ b/psi/gsdll2.def
@@ -18,6 +18,8 @@ EXPORTS
gsapi_set_poll
gsapi_set_poll_with_handle
gsapi_set_display_callback
+ gsapi_register_callout
+ gsapi_deregister_callout
gsapi_set_arg_encoding
gsapi_set_default_device_list
gsapi_get_default_device_list
diff --git a/psi/gsdll32.def b/psi/gsdll32.def
index bef2fe92b..c21b24a8d 100644
--- a/psi/gsdll32.def
+++ b/psi/gsdll32.def
@@ -30,6 +30,8 @@ EXPORTS
gsapi_set_poll
gsapi_set_poll_with_handle
gsapi_set_display_callback
+ gsapi_register_callout
+ gsapi_deregister_callout
gsapi_set_arg_encoding
gsapi_set_default_device_list
gsapi_get_default_device_list
diff --git a/psi/gsdll32metro.def b/psi/gsdll32metro.def
index 49f38c8b4..4d014e193 100644
--- a/psi/gsdll32metro.def
+++ b/psi/gsdll32metro.def
@@ -30,6 +30,9 @@ EXPORTS
gsapi_set_poll
gsapi_set_poll_with_handle
gsapi_set_display_callback
+ gsapi_set_display_callback_with_handle
+ gsapi_register_callout
+ gsapi_deregister_callout
gsapi_set_arg_encoding
gsapi_set_default_device_list
gsapi_get_default_device_list
diff --git a/psi/gsdll64.def b/psi/gsdll64.def
index 44289e923..edc6bb311 100644
--- a/psi/gsdll64.def
+++ b/psi/gsdll64.def
@@ -30,6 +30,8 @@ EXPORTS
gsapi_set_poll
gsapi_set_poll_with_handle
gsapi_set_display_callback
+ gsapi_register_callout
+ gsapi_deregister_callout
gsapi_set_arg_encoding
gsapi_set_default_device_list
gsapi_get_default_device_list
diff --git a/psi/gsdll64metro.def b/psi/gsdll64metro.def
index cc05cf0bf..9991270fe 100644
--- a/psi/gsdll64metro.def
+++ b/psi/gsdll64metro.def
@@ -30,6 +30,8 @@ EXPORTS
gsapi_set_poll
gsapi_set_poll_with_handle
gsapi_set_display_callback
+ gsapi_register_callout
+ gsapi_deregister_callout
gsapi_set_arg_encoding
gsapi_set_default_device_list
gsapi_get_default_device_list
diff --git a/psi/gsdllARM32metro.def b/psi/gsdllARM32metro.def
index 0ff322ea0..c54be9a5b 100644
--- a/psi/gsdllARM32metro.def
+++ b/psi/gsdllARM32metro.def
@@ -30,6 +30,8 @@ EXPORTS
gsapi_set_poll
gsapi_set_poll_with_handle
gsapi_set_display_callback
+ gsapi_register_callout
+ gsapi_deregister_callout
gsapi_set_arg_encoding
gsapi_set_default_device_list
gsapi_get_default_device_list
diff --git a/psi/iapi.c b/psi/iapi.c
index f47fe1531..36d3d0150 100644
--- a/psi/iapi.c
+++ b/psi/iapi.c
@@ -33,6 +33,7 @@
#include "gslibctx.h"
#include "gp.h"
#include "gsargs.h"
+#include "gdevdsp.h"
typedef struct { int a[(int)GS_ARG_ENCODING_LOCAL == (int)PS_ARG_ENCODING_LOCAL ? 1 : -1]; } compile_time_assert_0;
typedef struct { int a[(int)GS_ARG_ENCODING_UTF8 == (int)PS_ARG_ENCODING_UTF8 ? 1 : -1]; } compile_time_assert_1;
@@ -131,18 +132,77 @@ gsapi_set_poll_with_handle(void *instance,
return 0;
}
+static int
+legacy_display_callout(void *instance,
+ void *handle,
+ const char *dev_name,
+ int id,
+ int size,
+ void *data)
+{
+ gs_main_instance *inst = (gs_main_instance *)instance;
+
+ if (dev_name == NULL)
+ return -1;
+ if (strcmp(dev_name, "display") != 0)
+ return -1;
+
+ if (id == DISPLAY_CALLOUT_GET_CALLBACK_LEGACY) {
+ /* get display callbacks */
+ gs_display_get_callback_t *cb = (gs_display_get_callback_t *)data;
+ cb->callback = inst->display;
+ return 0;
+ }
+ return -1;
+}
+
/* Set the display callback structure */
GSDLLEXPORT int GSDLLAPI
gsapi_set_display_callback(void *instance, display_callback *callback)
{
gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance;
+ gs_main_instance *minst;
+ int code;
if (instance == NULL)
return gs_error_Fatal;
- get_minst_from_memory(ctx->memory)->display = callback;
+ minst = get_minst_from_memory(ctx->memory);
+ if (minst->display == NULL && callback != NULL) {
+ /* First registration. */
+ code = gs_lib_ctx_register_callout(minst->heap,
+ legacy_display_callout,
+ minst);
+ if (code < 0)
+ return code;
+ }
+ if (minst->display != NULL && callback == NULL) {
+ /* Deregistered. */
+ gs_lib_ctx_deregister_callout(minst->heap,
+ legacy_display_callout,
+ minst);
+ }
+ minst->display = callback;
/* not in a language switched build */
return 0;
}
+GSDLLEXPORT int GSDLLAPI
+gsapi_register_callout(void *instance, gs_callout fn, void *handle)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance;
+ if (instance == NULL)
+ return gs_error_Fatal;
+ return gs_lib_ctx_register_callout(ctx->memory, fn, handle);
+}
+
+GSDLLEXPORT void GSDLLAPI
+gsapi_deregister_callout(void *instance, gs_callout fn, void *handle)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance;
+ if (instance == NULL)
+ return;
+ gs_lib_ctx_deregister_callout(ctx->memory, fn, handle);
+}
+
/* Set/Get the default device list string */
GSDLLEXPORT int GSDLLAPI
gsapi_set_default_device_list(void *instance, const char *list, int listlen)
diff --git a/psi/iapi.h b/psi/iapi.h
index 2eb992a4b..605b2f50a 100644
--- a/psi/iapi.h
+++ b/psi/iapi.h
@@ -226,10 +226,43 @@ GSDLLEXPORT int GSDLLAPI gsapi_set_poll_with_handle(void *instance,
* If the display device is used, this must be called
* after gsapi_new_instance() and before gsapi_init_with_args().
* See gdevdisp.h for more details.
+ * DEPRECATED: Use the gsapi_register_callback mechanism instead.
*/
GSDLLEXPORT int GSDLLAPI gsapi_set_display_callback(
void *instance, display_callback *callback);
+/* The callout mechanism allows devices to query "callers" (users of the
+ * DLL) in device specific ways. The callout function pointer type will
+ * be called with:
+ * callout_handle = the value given at registration
+ * device_name = the name of the current device
+ * id = An integer, guaranteed to be unique within the
+ * callouts from a given device, identifying the
+ * purpose of this call.
+ * size = device/id specific, but typically the size of 'data'.
+ * data = device/id specific, but typically the pointer to
+ * an in/out data block.
+ * Returns an error code (gs_error_unknownerror (-1) if unclaimed,
+ * non-negative on success, standard gs error numbers recommended).
+ */
+typedef int (*gs_callout)(void *instance,
+ void *callout_handle,
+ const char *device_name,
+ int id,
+ int size,
+ void *data);
+
+/* Register a handler for gs callouts.
+ * This must be called after gsapi_new_instance() and (typically)
+ * before gsapi_init_with_args().
+ */
+GSDLLEXPORT int GSDLLAPI gsapi_register_callout(
+ void *instance, gs_callout callout, void *callout_handle);
+
+/* Deregister a handler for gs callouts. */
+GSDLLEXPORT void GSDLLAPI gsapi_deregister_callout(
+ void *instance, gs_callout callout, void *callout_handle);
+
/* Set the string containing the list of default device names
* for example "display x11alpha x11 bbox". Allows the calling
* application to influence which device(s) gs will try in order
diff --git a/psi/idisp.c b/psi/idisp.c
index d1efcbc16..ab8d070cd 100644
--- a/psi/idisp.c
+++ b/psi/idisp.c
@@ -47,70 +47,36 @@
#include "gsequivc.h"
#include "gdevdsp.h"
#include "gdevdsp2.h"
+#include "gxgstate.h"
+#include "gxdevsop.h"
int
-display_set_callback(gs_main_instance *minst, display_callback *callback)
+reopen_device_if_required(gs_main_instance *minst)
{
i_ctx_t *i_ctx_p;
- bool was_open;
int code;
- int exit_code = 0;
- os_ptr op;
gx_device *dev;
- gx_device_display *ddev;
-
- /* If display device exists, copy prototype if needed and return
- * device true
- * If it doesn't exist, return
- * false
- */
- const char getdisplay[] =
- "devicedict /display known dup { /display finddevice exch } if";
- code = gs_main_run_string(minst, getdisplay, 0, &exit_code,
- &minst->error_object);
- if (code < 0)
- return code;
i_ctx_p = minst->i_ctx_p; /* run_string may change i_ctx_p if GC */
- op = osp;
- check_type(*op, t_boolean);
- if (op->value.boolval) {
- /* display device was included in Ghostscript so we need
- * to set the callback structure pointer within it.
- * If the device is already open, close it before
- * setting callback, then reopen it.
- */
- check_read_type(op[-1], t_device);
- if (op[-1].value.pdevice == NULL)
- /* This can happen if we invalidated devices on the stack by calling nulldevice after they were pushed */
- return_error(gs_error_undefined);
-
- dev = op[-1].value.pdevice;
+ dev = gs_currentdevice_inline(i_ctx_p->pgs);
+ if (dev == NULL)
+ /* This can happen if we invalidated devices on the stack by calling nulldevice after they were pushed */
+ return_error(gs_error_undefined);
- was_open = dev->is_open;
- if (was_open) {
- code = gs_closedevice(dev);
- if (code < 0)
- return code;
- }
+ if (!dev->is_open)
+ return 0;
- ddev = (gx_device_display *) dev;
+ if (dev_proc(dev, dev_spec_op)(dev, gxdso_reopen_after_init, NULL, 0) != 1)
+ return 0;
- /* Deal with the case where we subclassed the device before we got here */
- while (ddev->child)
- ddev = (gx_device_display *)ddev->child;
-
- ddev->callback = callback;
+ code = gs_closedevice(dev);
+ if (code < 0)
+ return code;
- if (was_open) {
- code = gs_opendevice(dev);
- if (code < 0) {
- dmprintf(dev->memory, "**** Unable to open the display device, quitting.\n");
- return code;
- }
- }
- pop(1); /* device */
+ code = gs_opendevice(dev);
+ if (code < 0) {
+ dmprintf(dev->memory, "**** Unable to reopen the device, quitting.\n");
+ return code;
}
- pop(1); /* boolean */
return 0;
}
diff --git a/psi/idisp.h b/psi/idisp.h
index 4b9eeee94..215d915bd 100644
--- a/psi/idisp.h
+++ b/psi/idisp.h
@@ -25,7 +25,8 @@
typedef struct display_callback_s display_callback;
#endif
-/* Called from imain.c to set the display callback in the device instance. */
-int display_set_callback(gs_main_instance *minst, display_callback *callback);
+/* Called from imain.c to reopen the device after initialisation if the.
+ * device requires this. This gives it a chance to refetch any callbacks. */
+int reopen_device_if_required(gs_main_instance *minst);
#endif /* idisp_INCLUDED */
diff --git a/psi/imain.c b/psi/imain.c
index ed97d06a8..6d012e2a8 100644
--- a/psi/imain.c
+++ b/psi/imain.c
@@ -306,11 +306,11 @@ int gs_main_init2aux(gs_main_instance * minst) {
if (code < 0)
return code;
minst->init_done = 2;
+
/* NB this is to be done with device parameters
* both minst->display and display_set_callback() are going away
*/
- if (minst->display)
- if ((code = display_set_callback(minst, minst->display)) < 0)
+ if ((code = reopen_device_if_required(minst)) < 0)
return code;
if ((code = gs_main_run_string(minst,
@@ -344,7 +344,7 @@ gs_main_init2(gs_main_instance * minst)
if (code < 0)
goto fail;
- i_ctx_p = minst->i_ctx_p; /* display_set_callback or run_string may change it */
+ i_ctx_p = minst->i_ctx_p; /* reopen_device_if_display or run_string may change it */
/* Now process the initial saved-pages=... argument, if any as well as saved-pages-test */
{
diff --git a/psi/int.mak b/psi/int.mak
index ec562169d..e6cf79b1e 100644
--- a/psi/int.mak
+++ b/psi/int.mak
@@ -1897,7 +1897,7 @@ gdevdsp2_h=$(DEVSRCDIR)$(D)gdevdsp2.h
$(PSOBJ)idisp.$(OBJ) : $(PSSRC)idisp.c $(OP) $(stdio__h) $(gp_h)\
$(stdpre_h) $(gscdefs_h) $(gsdevice_h) $(gsmemory_h) $(gstypes_h)\
- $(iapi_h) $(iref_h)\
+ $(iapi_h) $(iref_h) $(gxgstate_h) $(gxdevsop_h)\
$(imain_h) $(iminst_h) $(idisp_h) $(ostack_h)\
$(gx_h) $(gxdevice_h) $(gxdevmem_h) $(gdevdsp_h) $(gdevdsp2_h)\
$(INT_MAK) $(MAKEDIRS)