xref: /plan9-contrib/sys/src/cmd/gs/src/gdevos2p.c (revision 6a9fc400c33447ef5e1cda7185cb4de2c8e8010e)
1 /* Copyright (C) 1995, 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: gdevos2p.c,v 1.4 2001/03/13 06:51:39 ghostgum Exp $ */
20 /*
21  * OS/2 printer device
22  *
23  * By Russell Lang, derived from mswinpr2 device by Russell Lang and
24  * L. Peter Deutsch, Aladdin Enterprises.
25  *
26  * Bug fixed by Pierre Arnaud 2000-03-20 (os2prn_set_bpp did not set anti_alias)
27  */
28 
29 /* This device works when GS is a DLL loaded by a PM program */
30 /* It does not work when GS is a text mode EXE */
31 
32 /* This driver uses the printer default size and resolution and
33  * ignores page size and resolution set using -gWIDTHxHEIGHT and
34  * -rXxY.  You must still set the correct PageSize to get the
35  * correct clipping path.  If you don't specify a value for
36  * -dBitsPerPixel, the depth will be obtained from the printer
37  * device context.
38  */
39 
40 #define INCL_DOS
41 #define INCL_DOSERRORS
42 #define INCL_DEV
43 #define INCL_GPIBITMAPS
44 #define INCL_SPL
45 #define INCL_SPLDOSPRINT
46 #define INCL_SPLERRORS
47 
48 #include <os2.h>
49 
50 #include "gdevprn.h"
51 #include "gdevpccm.h"
52 #include "gp.h"
53 #include "gscdefs.h"		/* for gs_product */
54 
55 extern HWND hwndtext;		/* in gp_os2.h */
56 
57 typedef struct tagOS2QL {
58     PRQINFO3 *prq;		/* queue list */
59     ULONG len;			/* bytes in queue list (for gs_free) */
60     int defqueue;		/* default queue */
61     int nqueues;		/* number of queues */
62 } OS2QL;
63 
64 #ifndef NERR_BufTooSmall
65 #define NERR_BufTooSmall 2123	/* For SplEnumQueue */
66 #endif
67 
68 /* Make sure we cast to the correct structure type. */
69 typedef struct gx_device_os2prn_s gx_device_os2prn;
70 
71 #undef opdev
72 #define opdev ((gx_device_os2prn *)dev)
73 
74 /* Device procedures */
75 
76 /* See gxdevice.h for the definitions of the procedures. */
77 private dev_proc_open_device(os2prn_open);
78 private dev_proc_close_device(os2prn_close);
79 private dev_proc_print_page(os2prn_print_page);
80 private dev_proc_map_rgb_color(os2prn_map_rgb_color);
81 private dev_proc_map_color_rgb(os2prn_map_color_rgb);
82 private dev_proc_put_params(os2prn_put_params);
83 private dev_proc_get_params(os2prn_get_params);
84 
85 private void os2prn_set_bpp(gx_device * dev, int depth);
86 private int os2prn_get_queue_list(OS2QL * ql);
87 private void os2prn_free_queue_list(OS2QL * ql);
88 int os2prn_get_printer(OS2QL * ql);
89 
90 private gx_device_procs os2prn_procs =
91 prn_color_params_procs(os2prn_open, gdev_prn_output_page, os2prn_close,
92 		       os2prn_map_rgb_color, os2prn_map_color_rgb,
93 		       os2prn_get_params, os2prn_put_params);
94 
95 
96 /* The device descriptor */
97 struct gx_device_os2prn_s {
98     gx_device_common;
99     gx_prn_device_common;
100     HAB hab;
101     HDC hdc;
102     HPS hps;
103     char queue_name[256];	/* OS/2 printer queue name */
104     int newframe;		/* false before first page */
105     OS2QL ql;
106     int clipbox[4];		/* llx, lly, urx, ury in pixels */
107     HDC hdcMem;
108     HPS hpsMem;
109 };
110 
111 gx_device_os2prn far_data gs_os2prn_device =
112 {
113     prn_device_std_body(gx_device_os2prn, os2prn_procs, "os2prn",
114 			DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, 72, 72,
115 			0, 0, 0, 0,
116 			0, os2prn_print_page),	/* depth = 0 */
117     0,				/* hab */
118     0,				/* hdc */
119     0,				/* hps */
120     ""				/* queue_name */
121 };
122 
123 /* Open the os2prn driver */
124 private int
125 os2prn_open(gx_device * dev)
126 {
127     int code;
128     PTIB pptib;
129     PPIB pppib;
130     DEVOPENSTRUC dop;
131     ULONG cbBuf;
132     ULONG cbNeeded;
133     APIRET rc;
134     PBYTE pbuf;
135     char *p;
136     SIZEL sizlPage;
137     LONG caps[2];
138     HCINFO hcinfo;
139     LONG nforms;
140     float m[4];
141     int depth;
142     FILE *pfile;
143     int i;
144     char *prefix = "\\\\spool\\";	/* 8 characters long */
145 
146     PRQINFO3 *pprq;
147     gx_device_os2prn *oprn;
148 
149     oprn = opdev;
150 
151     if (DosGetInfoBlocks(&pptib, &pppib)) {
152 	errprintf("\nos2prn_open: Couldn't get pid\n");
153 	return gs_error_limitcheck;
154     }
155     if (pppib->pib_ultype != 3) {
156 	/* if caller is not PM app */
157 	errprintf("os2prn device can only be used from a PM application\n");
158 	return gs_error_limitcheck;
159     }
160     opdev->hab = WinQueryAnchorBlock(hwndtext);
161     opdev->newframe = 0;
162 
163     if (os2prn_get_queue_list(&opdev->ql))
164 	return gs_error_limitcheck;
165 
166     if (opdev->queue_name[0] == '\0') {
167 	/* obtain printer name from filename */
168 	p = opdev->fname;
169 	for (i = 0; i < 8; i++) {
170 	    if (prefix[i] == '\\') {
171 		if ((*p != '\\') && (*p != '/'))
172 		    break;
173 	    } else if (tolower(*p) != prefix[i])
174 		break;
175 	    p++;
176 	}
177 	if (i == 8 && (strlen(p) != 0))
178 	    strcpy(opdev->queue_name, p);
179     }
180     pprq = NULL;
181     if (opdev->queue_name[0] != '\0') {
182 	for (i = 0; i < opdev->ql.nqueues; i++) {
183 	    if (strcmp(opdev->ql.prq[i].pszName, opdev->queue_name) == 0) {
184 		pprq = &(opdev->ql.prq[i]);
185 		break;
186 	    }
187 	}
188     } else {
189 	/* use default queue */
190 	pprq = &(opdev->ql.prq[opdev->ql.defqueue]);
191     }
192     if (pprq == (PRQINFO3 *) NULL) {
193 	errprintf("Invalid os2prn queue  name -sOS2QUEUE=\042%s\042\n", opdev->queue_name);
194 	errprintf("Valid device names are:\n");
195 	for (i = 0; i < opdev->ql.nqueues; i++) {
196 	    errprintf("  -sOS2QUEUE=\042%s\042\n", opdev->ql.prq[i].pszName);
197 	}
198 	return gs_error_rangecheck;
199     }
200     /* open printer device */
201     memset(&dop, 0, sizeof(dop));
202     dop.pszLogAddress = pprq->pszName;	/* queue name */
203     p = strchr(pprq->pszDriverName, '.');
204     if (p != (char *)NULL)
205 	*p = '\0';
206     dop.pszDriverName = pprq->pszDriverName;
207     dop.pszDataType = "PM_Q_STD";
208     dop.pdriv = pprq->pDriverData;
209     opdev->hdc = DevOpenDC(opdev->hab, OD_QUEUED, "*", 9L, (PDEVOPENDATA) & dop, (HDC) NULL);
210     if (opdev->hdc == DEV_ERROR) {
211 	ERRORID eid = WinGetLastError(opdev->hab);
212 
213 	errprintf("DevOpenDC for printer error 0x%x\n", eid);
214 	return gs_error_limitcheck;
215     }
216     os2prn_free_queue_list(&opdev->ql);
217 
218     /* find out resolution of printer */
219     /* this is returned in pixels/metre */
220     DevQueryCaps(opdev->hdc, CAPS_HORIZONTAL_RESOLUTION, 2, caps);
221     dev->x_pixels_per_inch = (int)(caps[0] * 0.0254 + 0.5);
222     dev->y_pixels_per_inch = (int)(caps[1] * 0.0254 + 0.5);
223 
224     /* find out page size and margins */
225     /* these are returned in millimetres */
226     nforms = DevQueryHardcopyCaps(opdev->hdc, 0, 0, &hcinfo);
227     for (i = 0; i < nforms; i++) {
228 	DevQueryHardcopyCaps(opdev->hdc, i, 1, &hcinfo);
229 	if (hcinfo.flAttributes & HCAPS_CURRENT)
230 	    break;		/* this is the default page size */
231     }
232     /* GS size is in pixels */
233     dev->width = hcinfo.cx * caps[0] / 1000;
234     dev->height = hcinfo.cy * caps[1] / 1000;
235     /* GS margins are in inches */
236     m[0] /*left */  = hcinfo.xLeftClip / 25.4;
237     m[1] /*bottom */  = hcinfo.yBottomClip / 25.4;
238     m[2] /*right */  = (hcinfo.cx - hcinfo.xRightClip) / 25.4;
239     m[3] /*top */  = (hcinfo.cy - hcinfo.yTopClip) / 25.4;
240     gx_device_set_margins(dev, m, true);
241     /* set bounding box in pixels for later drawing */
242     opdev->clipbox[0] = (int)(hcinfo.xLeftClip / 25.4 * dev->x_pixels_per_inch + 1);	/* round inwards */
243     opdev->clipbox[1] = (int)(hcinfo.yBottomClip / 25.4 * dev->y_pixels_per_inch + 1);
244     opdev->clipbox[2] = (int)(hcinfo.xRightClip / 25.4 * dev->x_pixels_per_inch);
245     opdev->clipbox[3] = (int)(hcinfo.yTopClip / 25.4 * dev->y_pixels_per_inch);
246 
247     /* get presentation space */
248     sizlPage.cx = dev->width;
249     sizlPage.cy = dev->height;
250     opdev->hps = GpiCreatePS(opdev->hab, opdev->hdc, &sizlPage,
251 			 PU_PELS | GPIF_DEFAULT | GPIT_NORMAL | GPIA_ASSOC);
252 
253     depth = dev->color_info.depth;
254     if (depth == 0) {
255 	/* Set parameters that were unknown before opening device */
256 	/* Find out if the device supports color */
257 	/* We recognize 1, 3, 8 and 24 bit color devices */
258 	DevQueryCaps(opdev->hdc, CAPS_COLOR_PLANES, 2, caps);
259 	/* caps[0] is #color planes, caps[1] is #bits per plane */
260 	depth = caps[0] * caps[1];
261     }
262     os2prn_set_bpp(dev, depth);
263 
264     /* create a memory DC compatible with printer */
265     opdev->hdcMem = DevOpenDC(opdev->hab, OD_MEMORY, "*", 0L, NULL, opdev->hdc);
266     if (opdev->hdcMem == DEV_ERROR) {
267 	ERRORID eid = WinGetLastError(opdev->hab);
268 
269 	errprintf("DevOpenDC for memory error 0x%x\n", eid);
270 	return gs_error_limitcheck;
271     }
272     sizlPage.cx = dev->width;
273     sizlPage.cy = dev->height;
274     opdev->hpsMem = GpiCreatePS(opdev->hab, opdev->hdcMem, &sizlPage,
275 			 PU_PELS | GPIF_DEFAULT | GPIT_NORMAL | GPIA_ASSOC);
276     if (opdev->hpsMem == GPI_ERROR) {
277 	ERRORID eid = WinGetLastError(opdev->hab);
278 
279 	errprintf("GpiCreatePS for memory error 0x%x\n", eid);
280 	return gs_error_limitcheck;
281     }
282     if (DevEscape(opdev->hdc, DEVESC_STARTDOC, (LONG) strlen(gs_product),
283 		  (char *)gs_product, NULL, NULL) == DEVESC_ERROR) {
284 	ERRORID eid = WinGetLastError(opdev->hab);
285 
286 	errprintf("DEVESC_STARTDOC error 0x%x\n", eid);
287 	return gs_error_limitcheck;
288     }
289     /* gdev_prn_open opens a temporary file which we don't want */
290     /* so we specify the name now so we can delete it later */
291     pfile = gp_open_scratch_file(gp_scratch_file_name_prefix,
292 				 opdev->fname, "wb");
293     fclose(pfile);
294     code = gdev_prn_open(dev);
295 
296     return code;
297 }
298 
299 /* Close the os2prn driver */
300 private int
301 os2prn_close(gx_device * dev)
302 {
303     int code;
304     LONG lOut;
305     USHORT usJobID;
306 
307     /* tell printer that all is finished */
308     DevEscape(opdev->hdc, DEVESC_ENDDOC, 0L, NULL, &lOut, (PBYTE) & usJobID);
309     /* Free resources */
310     GpiAssociate(opdev->hps, (HDC) NULL);
311     GpiDestroyPS(opdev->hps);
312     DevCloseDC(opdev->hdc);
313 
314     if (opdev->hpsMem != GPI_ERROR)
315 	GpiDestroyPS(opdev->hpsMem);
316     if (opdev->hdcMem != DEV_ERROR)
317 	DevCloseDC(opdev->hdcMem);
318 
319     code = gdev_prn_close(dev);
320     /* delete unwanted temporary file */
321     unlink(opdev->fname);
322     return code;
323 }
324 
325 /* Get os2pm parameters */
326 int
327 os2prn_get_params(gx_device * dev, gs_param_list * plist)
328 {
329     int code = gdev_prn_get_params(dev, plist);
330     gs_param_string qs;
331 
332     qs.data = opdev->queue_name, qs.size = strlen(qs.data),
333 	qs.persistent = false;
334     code < 0 ||
335 	(code = param_write_string(plist, "OS2QUEUE", &qs)) < 0;
336     return code;
337 }
338 
339 
340 
341 /* We implement this ourselves so that we can change BitsPerPixel */
342 /* before the device is opened */
343 int
344 os2prn_put_params(gx_device * dev, gs_param_list * plist)
345 {
346     int ecode = 0, code;
347     int old_bpp = dev->color_info.depth;
348     int bpp = old_bpp;
349     gs_param_string qs;
350 
351     /* Handle extra parameters */
352     switch (code = param_read_string(plist, "OS2QUEUE", &qs)) {
353 	case 0:
354 	    if (qs.size == strlen(opdev->queue_name) &&
355 		!memcmp(opdev->queue_name, qs.data, qs.size)
356 		) {
357 		qs.data = 0;
358 		break;
359 	    }
360 	    if (dev->is_open)
361 		ecode = gs_error_rangecheck;
362 	    else if (qs.size >= sizeof(opdev->queue_name))
363 		ecode = gs_error_limitcheck;
364 	    else
365 		break;
366 	    goto qe;
367 	default:
368 	    ecode = code;
369 	  qe:param_signal_error(plist, "OS2QUEUE", ecode);
370 	case 1:
371 	    qs.data = 0;
372 	    break;
373     }
374 
375     switch (code = param_read_int(plist, "BitsPerPixel", &bpp)) {
376 	case 0:
377 	    if (dev->is_open)
378 		ecode = gs_error_rangecheck;
379 	    else {		/* change dev->color_info is valid before device is opened */
380 		os2prn_set_bpp(dev, bpp);
381 		break;
382 	    }
383 	    goto bppe;
384 	default:
385 	    ecode = code;
386 	  bppe:param_signal_error(plist, "BitsPerPixel", ecode);
387 	case 1:
388 	    break;
389     }
390 
391     if (ecode >= 0)
392 	ecode = gdev_prn_put_params(dev, plist);
393 
394     if ((ecode >= 0) && (qs.data != 0)) {
395 	memcpy(opdev->queue_name, qs.data, qs.size);
396 	opdev->queue_name[qs.size] = 0;
397     }
398     return ecode;
399 }
400 
401 
402 
403 /* ------ Internal routines ------ */
404 
405 #undef opdev
406 #define opdev ((gx_device_os2prn *)pdev)
407 
408 /************************************************/
409 
410 
411 /* ------ Private definitions ------ */
412 
413 
414 /* new os2prn_print_page routine */
415 
416 /* Write BMP header to memory, then send bitmap to printer */
417 /* one scan line at a time */
418 private int
419 os2prn_print_page(gx_device_printer * pdev, FILE * file)
420 {
421     int raster = gdev_prn_raster(pdev);
422 
423     /* BMP scan lines are padded to 32 bits. */
424     ulong bmp_raster = (raster + 3) & (~3);
425     ulong bmp_raster_multi;
426     int height = pdev->height;
427     int depth = pdev->color_info.depth;
428     byte *row;
429     int y;
430     int code = 0;		/* return code */
431     POINTL apts[4];
432     APIRET rc;
433     POINTL aptsb[4];
434     HBITMAP hbmp, hbmr;
435     int i, lines;
436     int ystart, yend;
437     int yslice;
438 
439     struct bmi_s {
440 	BITMAPINFOHEADER2 h;
441 	RGB2 pal[256];
442     } bmi;
443 
444     yslice = 65535 / bmp_raster;
445     bmp_raster_multi = bmp_raster * yslice;
446     row = (byte *) gs_malloc(bmp_raster_multi, 1, "bmp file buffer");
447     if (row == 0)		/* can't allocate row buffer */
448 	return_error(gs_error_VMerror);
449 
450     if (opdev->newframe)
451 	DevEscape(opdev->hdc, DEVESC_NEWFRAME, 0L, NULL, NULL, NULL);
452     opdev->newframe = 1;
453 
454     /* Write the info header. */
455 
456     memset(&bmi.h, 0, sizeof(bmi.h));
457     bmi.h.cbFix = sizeof(bmi.h);
458     bmi.h.cx = pdev->width;	/* opdev->mdev.width; */
459     /* bmi.h.cy = height; */
460     bmi.h.cy = yslice;		/* size for memory PS */
461     bmi.h.cPlanes = 1;
462     bmi.h.cBitCount = pdev->color_info.depth;
463 
464     /* Write the palette. */
465 
466     if (depth <= 8) {
467 	int i;
468 	gx_color_value rgb[3];
469 	PRGB2 pq;
470 
471 	bmi.h.cclrUsed = 1 << depth;
472 	bmi.h.cclrImportant = 1 << depth;
473 	for (i = 0; i != 1 << depth; i++) {
474 	    (*dev_proc(pdev, map_color_rgb)) ((gx_device *) pdev,
475 					      (gx_color_index) i, rgb);
476 	    pq = &bmi.pal[i];
477 	    pq->bRed = gx_color_value_to_byte(rgb[0]);
478 	    pq->bGreen = gx_color_value_to_byte(rgb[1]);
479 	    pq->bBlue = gx_color_value_to_byte(rgb[2]);
480 	    pq->fcOptions = 0;
481 	}
482     } else {
483 	bmi.h.cclrUsed = 0;
484 	bmi.h.cclrImportant = 0;
485     }
486 
487     /* for GpiDrawBits */
488     /* target is inclusive */
489     apts[0].x = 0;
490     apts[0].y = 0;		/* filled in later */
491     apts[1].x = pdev->width - 1;
492     apts[1].y = 0;		/* filled in later */
493     /* source is not inclusive of top & right borders */
494     apts[2].x = 0;
495     apts[2].y = 0;
496     apts[3].x = pdev->width;
497     apts[3].y = 0;		/* filled in later */
498 
499     /* for GpiBitBlt */
500     /* target is not inclusive */
501     aptsb[0].x = opdev->clipbox[0];
502     aptsb[0].y = 0;		/* filled in later */
503     aptsb[1].x = opdev->clipbox[2];
504     aptsb[1].y = 0;		/* filled in later */
505     /* source is not inclusive */
506     aptsb[2].x = opdev->clipbox[0];
507     aptsb[2].y = 0;
508     aptsb[3].x = opdev->clipbox[2];
509     aptsb[3].y = 0;		/* filled in later */
510 
511     /* write the bits */
512     ystart = opdev->clipbox[3];
513     yend = opdev->clipbox[1];
514     y = ystart;
515     while (y > yend) {
516 	/* create a bitmap for the memory DC */
517 	hbmp = GpiCreateBitmap(opdev->hpsMem, &bmi.h, 0L, NULL, NULL);
518 	if (hbmp == GPI_ERROR)
519 	    goto bmp_done;
520 	hbmr = GpiSetBitmap(opdev->hpsMem, hbmp);
521 
522 	/* copy slice to memory bitmap */
523 	if (y > yend + yslice)
524 	    lines = yslice;
525 	else
526 	    lines = y - yend;
527 	y -= lines;
528 	for (i = lines - 1; i >= 0; i--)
529 	    gdev_prn_copy_scan_lines(pdev, ystart - 1 - (y + i), row + (bmp_raster * i), raster);
530 	apts[0].y = 0;		/* target */
531 	apts[1].y = lines;
532 	apts[3].y = lines - 1;	/* source */
533 	/* copy DIB bitmap to memory bitmap */
534 	rc = GpiDrawBits(opdev->hpsMem, row, (BITMAPINFO2 *) & bmi, 4, apts,
535 			 (depth != 1) ? ROP_SRCCOPY : ROP_NOTSRCCOPY, 0);
536 
537 	/* copy slice to printer */
538 	aptsb[0].y = y;
539 	aptsb[1].y = y + lines;
540 	aptsb[3].y = lines;
541 	rc = GpiBitBlt(opdev->hps, opdev->hpsMem, 4, aptsb, ROP_SRCCOPY, BBO_IGNORE);
542 
543 	/* delete bitmap */
544 	if (hbmr != HBM_ERROR)
545 	    GpiSetBitmap(opdev->hpsMem, (ULONG) 0);
546 	hbmr = HBM_ERROR;
547 	if (hbmp != GPI_ERROR)
548 	    GpiDeleteBitmap(hbmp);
549 	hbmp = GPI_ERROR;
550     }
551 
552   bmp_done:
553     if (row)
554 	gs_free((char *)row, bmp_raster_multi, 1, "bmp file buffer");
555 
556     return code;
557 }
558 
559 /* combined color mappers */
560 
561 /* 24-bit color mappers (taken from gdevmem2.c). */
562 /* Note that OS/2 expects RGB values in the order B,G,R. */
563 
564 /* Map a r-g-b color to a color index. */
565 private gx_color_index
566 os2prn_map_rgb_color(gx_device * dev, gx_color_value r, gx_color_value g,
567 		     gx_color_value b)
568 {
569     switch (dev->color_info.depth) {
570 	case 1:
571 	    return gdev_prn_map_rgb_color(dev, r, g, b);
572 	case 4:
573 	    /* use only 8 colors */
574 	    return (r > (gx_max_color_value / 2 + 1) ? 4 : 0) +
575 		(g > (gx_max_color_value / 2 + 1) ? 2 : 0) +
576 		(b > (gx_max_color_value / 2 + 1) ? 1 : 0);
577 	case 8:
578 	    return pc_8bit_map_rgb_color(dev, r, g, b);
579 	case 24:
580 	    return gx_color_value_to_byte(r) +
581 		((uint) gx_color_value_to_byte(g) << 8) +
582 		((ulong) gx_color_value_to_byte(b) << 16);
583     }
584     return 0;			/* error */
585 }
586 
587 /* Map a color index to a r-g-b color. */
588 private int
589 os2prn_map_color_rgb(gx_device * dev, gx_color_index color,
590 		     gx_color_value prgb[3])
591 {
592     switch (dev->color_info.depth) {
593 	case 1:
594 	    gdev_prn_map_color_rgb(dev, color, prgb);
595 	    break;
596 	case 4:
597 	    /* use only 8 colors */
598 	    prgb[0] = (color & 4) ? gx_max_color_value : 0;
599 	    prgb[1] = (color & 2) ? gx_max_color_value : 0;
600 	    prgb[2] = (color & 1) ? gx_max_color_value : 0;
601 	    break;
602 	case 8:
603 	    pc_8bit_map_color_rgb(dev, color, prgb);
604 	    break;
605 	case 24:
606 	    prgb[2] = gx_color_value_from_byte(color >> 16);
607 	    prgb[1] = gx_color_value_from_byte((color >> 8) & 0xff);
608 	    prgb[0] = gx_color_value_from_byte(color & 0xff);
609 	    break;
610     }
611     return 0;
612 }
613 
614 void
615 os2prn_set_bpp(gx_device * dev, int depth)
616 {
617     if (depth > 8) {
618 	static const gx_device_color_info os2prn_24color = dci_std_color(24);
619 
620 	dev->color_info = os2prn_24color;
621     } else if (depth >= 8) {
622 	/* 8-bit (SuperVGA-style) color. */
623 	/* (Uses a fixed palette of 3,3,2 bits.) */
624 	static const gx_device_color_info os2prn_8color = dci_pc_8bit;
625 
626 	dev->color_info = os2prn_8color;
627     } else if (depth >= 3) {
628 	/* 3 plane printer */
629 	/* suitable for impact dot matrix CMYK printers */
630 	/* create 4-bit bitmap, but only use 8 colors */
631 	static const gx_device_color_info os2prn_4color = dci_values(3, 4, 1, 1, 2, 2);
632 
633 	dev->color_info = os2prn_4color;
634     } else {			/* default is black_and_white */
635 	static const gx_device_color_info os2prn_1color = dci_std_color(1);
636 
637 	dev->color_info = os2prn_1color;
638     }
639 }
640 
641 /* Get list of queues from SplEnumQueue */
642 /* returns 0 if OK, non-zero for error */
643 private int
644 os2prn_get_queue_list(OS2QL * ql)
645 {
646     SPLERR splerr;
647     USHORT jobCount;
648     ULONG cbBuf;
649     ULONG cTotal;
650     ULONG cReturned;
651     ULONG cbNeeded;
652     ULONG ulLevel;
653     ULONG i;
654     PSZ pszComputerName;
655     PBYTE pBuf;
656     PPRQINFO3 prq;
657 
658     ulLevel = 3L;
659     pszComputerName = (PSZ) NULL;
660     splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, 0L,	/* cbBuf */
661 			  &cReturned, &cTotal,
662 			  &cbNeeded, NULL);
663     if (splerr == ERROR_MORE_DATA || splerr == NERR_BufTooSmall) {
664 	pBuf = gs_malloc(cbNeeded, 1, "OS/2 printer device info buffer");
665 	ql->prq = (PRQINFO3 *) pBuf;
666 	if (ql->prq != (PRQINFO3 *) NULL) {
667 	    ql->len = cbNeeded;
668 	    cbBuf = cbNeeded;
669 	    splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, cbBuf,
670 				  &cReturned, &cTotal,
671 				  &cbNeeded, NULL);
672 	    if (splerr == NO_ERROR) {
673 		/* Set pointer to point to the beginning of the buffer.           */
674 		prq = (PPRQINFO3) pBuf;
675 		/* cReturned has the count of the number of PRQINFO3 structures.  */
676 		ql->nqueues = cReturned;
677 		ql->defqueue = 0;
678 		for (i = 0; i < cReturned; i++) {
679 		    if (prq->fsType & PRQ3_TYPE_APPDEFAULT)
680 			ql->defqueue = i;
681 		    prq++;
682 		}		/*endfor cReturned */
683 	    }
684 	}
685     } else {
686 	/* If we are here we had a bad error code. Print it and some other info. */
687 	eprintf4("SplEnumQueue Error=%ld, Total=%ld, Returned=%ld, Needed=%ld\n",
688 		splerr, cTotal, cReturned, cbNeeded);
689     }
690     if (splerr)
691 	return splerr;
692     return 0;
693 }
694 
695 
696 private void
697 os2prn_free_queue_list(OS2QL * ql)
698 {
699     gs_free((char *)ql->prq, ql->len, 1, "os2prn queue list");
700     ql->prq = NULL;
701     ql->len = 0;
702     ql->defqueue = 0;
703     ql->nqueues = 0;
704 }
705