xref: /plan9/sys/src/cmd/gs/src/dwmainc.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1996-2004 Ghostgum Software Pty Ltd.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: dwmainc.c,v 1.24 2004/09/15 19:41:01 ray Exp $ */
18 /* dwmainc.c */
19 
20 #include "windows_.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <io.h>
24 #include <fcntl.h>
25 #include <process.h>
26 #include "ierrors.h"
27 #include "iapi.h"
28 #include "vdtrace.h"
29 #include "gdevdsp.h"
30 #include "dwdll.h"
31 #include "dwimg.h"
32 #include "dwtrace.h"
33 
34 /* Patch by Rod Webster (rodw) */
35 /* Added conditional below to allow Borland Compilation (Tested on 5.5) */
36 /* It would be better to place this code in an include file but no dwmainc.h exists */
37 #ifdef __BORLANDC__
38 #define _isatty isatty
39 #define _setmode setmode
40 #endif
41 
42 GSDLL gsdll;
43 void *instance;
44 BOOL quitnow = FALSE;
45 HANDLE hthread;
46 DWORD thread_id;
47 HWND hwndtext = NULL;	/* for dwimg.c, but not used */
48 HWND hwndforeground;	/* our best guess for our console window handle */
49 
50 char start_string[] = "systemdict /start get exec\n";
51 
52 /*********************************************************************/
53 /* stdio functions */
54 
55 static int GSDLLCALL
gsdll_stdin(void * instance,char * buf,int len)56 gsdll_stdin(void *instance, char *buf, int len)
57 {
58     return _read(fileno(stdin), buf, len);
59 }
60 
61 static int GSDLLCALL
gsdll_stdout(void * instance,const char * str,int len)62 gsdll_stdout(void *instance, const char *str, int len)
63 {
64     fwrite(str, 1, len, stdout);
65     fflush(stdout);
66     return len;
67 }
68 
69 static int GSDLLCALL
gsdll_stderr(void * instance,const char * str,int len)70 gsdll_stderr(void *instance, const char *str, int len)
71 {
72     fwrite(str, 1, len, stderr);
73     fflush(stderr);
74     return len;
75 }
76 
77 /*********************************************************************/
78 /* dll device */
79 
80 /* We must run windows from another thread, since main thread */
81 /* is running Ghostscript and blocks on stdin. */
82 
83 /* We notify second thread of events using PostThreadMessage()
84  * with the following messages. Apparently Japanese Windows sends
85  * WM_USER+1 with lParam == 0 and crashes. So we use WM_USER+101.
86  * Fix from Akira Kakuto
87  */
88 #define DISPLAY_OPEN WM_USER+101
89 #define DISPLAY_CLOSE WM_USER+102
90 #define DISPLAY_SIZE WM_USER+103
91 #define DISPLAY_SYNC WM_USER+104
92 #define DISPLAY_PAGE WM_USER+105
93 #define DISPLAY_UPDATE WM_USER+106
94 
95 /*
96 #define DISPLAY_DEBUG
97 */
98 
99 /* The second thread is the message loop */
winthread(void * arg)100 static void winthread(void *arg)
101 {
102     MSG msg;
103     thread_id = GetCurrentThreadId();
104     hthread = GetCurrentThread();
105 
106     while (!quitnow && GetMessage(&msg, (HWND)NULL, 0, 0)) {
107 	switch (msg.message) {
108 	    case DISPLAY_OPEN:
109 		image_open((IMAGE *)msg.lParam);
110 		break;
111 	    case DISPLAY_CLOSE:
112 		{
113 		    IMAGE *img = (IMAGE *)msg.lParam;
114 		    HANDLE hmutex = img->hmutex;
115 		    image_close(img);
116 		    CloseHandle(hmutex);
117 		}
118 		break;
119 	    case DISPLAY_SIZE:
120 		image_updatesize((IMAGE *)msg.lParam);
121 		break;
122 	    case DISPLAY_SYNC:
123 		image_sync((IMAGE *)msg.lParam);
124 		break;
125 	    case DISPLAY_PAGE:
126 		image_page((IMAGE *)msg.lParam);
127 		break;
128 	    case DISPLAY_UPDATE:
129 		image_poll((IMAGE *)msg.lParam);
130 		break;
131 	    default:
132 		TranslateMessage(&msg);
133 		DispatchMessage(&msg);
134 	}
135     }
136 }
137 
138 
139 /* New device has been opened */
140 /* Tell user to use another device */
display_open(void * handle,void * device)141 int display_open(void *handle, void *device)
142 {
143     IMAGE *img;
144 #ifdef DISPLAY_DEBUG
145     fprintf(stdout, "display_open(0x%x, 0x%x)\n", handle, device);
146 #endif
147     img = image_new(handle, device);	/* create and add to list */
148     img->hmutex = CreateMutex(NULL, FALSE, NULL);
149     if (img)
150 	PostThreadMessage(thread_id, DISPLAY_OPEN, 0, (LPARAM)img);
151     return 0;
152 }
153 
display_preclose(void * handle,void * device)154 int display_preclose(void *handle, void *device)
155 {
156     IMAGE *img;
157 #ifdef DISPLAY_DEBUG
158     fprintf(stdout, "display_preclose(0x%x, 0x%x)\n", handle, device);
159 #endif
160     img = image_find(handle, device);
161     if (img) {
162 	/* grab mutex to stop other thread using bitmap */
163 	WaitForSingleObject(img->hmutex, 120000);
164     }
165     return 0;
166 }
167 
display_close(void * handle,void * device)168 int display_close(void *handle, void *device)
169 {
170     IMAGE *img;
171 #ifdef DISPLAY_DEBUG
172     fprintf(stdout, "display_close(0x%x, 0x%x)\n", handle, device);
173 #endif
174     img = image_find(handle, device);
175     if (img) {
176 	/* This is a hack to pass focus from image window to console */
177 	if (GetForegroundWindow() == img->hwnd)
178 	    SetForegroundWindow(hwndforeground);
179 
180 	image_delete(img);	/* remove from list, but don't free */
181 	PostThreadMessage(thread_id, DISPLAY_CLOSE, 0, (LPARAM)img);
182     }
183     return 0;
184 }
185 
display_presize(void * handle,void * device,int width,int height,int raster,unsigned int format)186 int display_presize(void *handle, void *device, int width, int height,
187 	int raster, unsigned int format)
188 {
189     IMAGE *img;
190 #ifdef DISPLAY_DEBUG
191     fprintf(stdout, "display_presize(0x%x 0x%x, %d, %d, %d, %d, %ld)\n",
192 	handle, device, width, height, raster, format);
193 #endif
194     img = image_find(handle, device);
195     if (img) {
196 	/* grab mutex to stop other thread using bitmap */
197 	WaitForSingleObject(img->hmutex, 120000);
198     }
199     return 0;
200 }
201 
display_size(void * handle,void * device,int width,int height,int raster,unsigned int format,unsigned char * pimage)202 int display_size(void *handle, void *device, int width, int height,
203 	int raster, unsigned int format, unsigned char *pimage)
204 {
205     IMAGE *img;
206 #ifdef DISPLAY_DEBUG
207     fprintf(stdout, "display_size(0x%x 0x%x, %d, %d, %d, %d, %ld, 0x%x)\n",
208 	handle, device, width, height, raster, format, pimage);
209 #endif
210     img = image_find(handle, device);
211     if (img) {
212 	image_size(img, width, height, raster, format, pimage);
213 	/* release mutex to allow other thread to use bitmap */
214 	ReleaseMutex(img->hmutex);
215 	PostThreadMessage(thread_id, DISPLAY_SIZE, 0, (LPARAM)img);
216     }
217     return 0;
218 }
219 
display_sync(void * handle,void * device)220 int display_sync(void *handle, void *device)
221 {
222     IMAGE *img;
223 #ifdef DISPLAY_DEBUG
224     fprintf(stdout, "display_sync(0x%x, 0x%x)\n", handle, device);
225 #endif
226     img = image_find(handle, device);
227     if (img && !img->pending_sync) {
228 	img->pending_sync = 1;
229 	PostThreadMessage(thread_id, DISPLAY_SYNC, 0, (LPARAM)img);
230     }
231     return 0;
232 }
233 
display_page(void * handle,void * device,int copies,int flush)234 int display_page(void *handle, void *device, int copies, int flush)
235 {
236     IMAGE *img;
237 #ifdef DISPLAY_DEBUG
238     fprintf(stdout, "display_page(0x%x, 0x%x, copies=%d, flush=%d)\n",
239 	handle, device, copies, flush);
240 #endif
241     img = image_find(handle, device);
242     if (img)
243 	PostThreadMessage(thread_id, DISPLAY_PAGE, 0, (LPARAM)img);
244     return 0;
245 }
246 
display_update(void * handle,void * device,int x,int y,int w,int h)247 int display_update(void *handle, void *device,
248     int x, int y, int w, int h)
249 {
250     IMAGE *img;
251     img = image_find(handle, device);
252     if (img && !img->pending_update && !img->pending_sync) {
253 	img->pending_update = 1;
254 	PostThreadMessage(thread_id, DISPLAY_UPDATE, 0, (LPARAM)img);
255     }
256     return 0;
257 }
258 
259 /*
260 #define DISPLAY_DEBUG_USE_ALLOC
261 */
262 #ifdef DISPLAY_DEBUG_USE_ALLOC
263 /* This code isn't used, but shows how to use this function */
display_memalloc(void * handle,void * device,unsigned long size)264 void *display_memalloc(void *handle, void *device, unsigned long size)
265 {
266     void *mem;
267 #ifdef DISPLAY_DEBUG
268     fprintf(stdout, "display_memalloc(0x%x 0x%x %d)\n",
269 	handle, device, size);
270 #endif
271     mem = malloc(size);
272 #ifdef DISPLAY_DEBUG
273     fprintf(stdout, "  returning 0x%x\n", (int)mem);
274 #endif
275     return mem;
276 }
277 
display_memfree(void * handle,void * device,void * mem)278 int display_memfree(void *handle, void *device, void *mem)
279 {
280 #ifdef DISPLAY_DEBUG
281     fprintf(stdout, "display_memfree(0x%x, 0x%x, 0x%x)\n",
282 	handle, device, mem);
283 #endif
284     free(mem);
285     return 0;
286 }
287 #endif
288 
display_separation(void * handle,void * device,int comp_num,const char * name,unsigned short c,unsigned short m,unsigned short y,unsigned short k)289 int display_separation(void *handle, void *device,
290    int comp_num, const char *name,
291    unsigned short c, unsigned short m,
292    unsigned short y, unsigned short k)
293 {
294     IMAGE *img;
295 #ifdef DISPLAY_DEBUG
296     fprintf(stdout, "display_separation(0x%x, 0x%x, %d '%s' %d,%d,%d,%d)\n",
297 	handle, device, comp_num, name, (int)c, (int)m, (int)y, (int)k);
298 #endif
299     img = image_find(handle, device);
300     if (img)
301         image_separation(img, comp_num, name, c, m, y, k);
302     return 0;
303 }
304 
305 
306 display_callback display = {
307     sizeof(display_callback),
308     DISPLAY_VERSION_MAJOR,
309     DISPLAY_VERSION_MINOR,
310     display_open,
311     display_preclose,
312     display_close,
313     display_presize,
314     display_size,
315     display_sync,
316     display_page,
317     display_update,
318 #ifdef DISPLAY_DEBUG_USE_ALLOC
319     display_memalloc,	/* memalloc */
320     display_memfree,	/* memfree */
321 #else
322     NULL,	/* memalloc */
323     NULL,	/* memfree */
324 #endif
325     display_separation
326 };
327 
328 
329 /*********************************************************************/
330 
main(int argc,char * argv[])331 int main(int argc, char *argv[])
332 {
333     int code, code1;
334     int exit_code;
335     int exit_status;
336     int nargc;
337     char **nargv;
338     char buf[256];
339     char dformat[64];
340     char ddpi[64];
341 
342     if (!_isatty(fileno(stdin)))
343         _setmode(fileno(stdin), _O_BINARY);
344     _setmode(fileno(stdout), _O_BINARY);
345     _setmode(fileno(stderr), _O_BINARY);
346 
347     hwndforeground = GetForegroundWindow();	/* assume this is ours */
348     memset(buf, 0, sizeof(buf));
349     if (load_dll(&gsdll, buf, sizeof(buf))) {
350 	fprintf(stderr, "Can't load Ghostscript DLL\n");
351 	fprintf(stderr, "%s\n", buf);
352 	return 1;
353     }
354 
355     if (gsdll.new_instance(&instance, NULL) < 0) {
356 	fprintf(stderr, "Can't create Ghostscript instance\n");
357 	return 1;
358     }
359 
360 #ifdef DEBUG
361     visual_tracer_init();
362     gsdll.set_visual_tracer(&visual_tracer);
363 #endif
364 
365     if (_beginthread(winthread, 65535, NULL) == -1) {
366 	fprintf(stderr, "GUI thread creation failed\n");
367     }
368     else {
369 	int n = 30;
370 	/* wait for thread to start */
371 	Sleep(0);
372 	while (n && (hthread == INVALID_HANDLE_VALUE)) {
373 	    n--;
374 	    Sleep(100);
375         }
376 	while (n && (PostThreadMessage(thread_id, WM_USER, 0, 0) == 0)) {
377 	    n--;
378 	    Sleep(100);
379 	}
380 	if (n == 0)
381 	    fprintf(stderr, "Can't post message to GUI thread\n");
382     }
383 
384     gsdll.set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr);
385     gsdll.set_display_callback(instance, &display);
386 
387     {   int format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
388 		DISPLAY_DEPTH_1 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
389 	HDC hdc = GetDC(NULL);	/* get hdc for desktop */
390 	int depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
391 	sprintf(ddpi, "-dDisplayResolution=%d", GetDeviceCaps(hdc, LOGPIXELSY));
392         ReleaseDC(NULL, hdc);
393 	if (depth == 32)
394  	    format = DISPLAY_COLORS_RGB | DISPLAY_UNUSED_LAST |
395 		DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
396 	else if (depth == 16)
397  	    format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
398 		DISPLAY_DEPTH_16 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST |
399 		DISPLAY_NATIVE_555;
400 	else if (depth > 8)
401  	    format = DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE |
402 		DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
403 	else if (depth >= 8)
404  	    format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
405 		DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
406 	else if (depth >= 4)
407  	    format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
408 		DISPLAY_DEPTH_4 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
409         sprintf(dformat, "-dDisplayFormat=%d", format);
410     }
411     nargc = argc + 2;
412     nargv = (char **)malloc((nargc + 1) * sizeof(char *));
413     nargv[0] = argv[0];
414     nargv[1] = dformat;
415     nargv[2] = ddpi;
416     memcpy(&nargv[3], &argv[1], argc * sizeof(char *));
417 
418 #if defined(_MSC_VER) || defined(__BORLANDC__)
419     __try {
420 #endif
421     code = gsdll.init_with_args(instance, nargc, nargv);
422     if (code == 0)
423 	code = gsdll.run_string(instance, start_string, 0, &exit_code);
424     code1 = gsdll.exit(instance);
425     if (code == 0 || (code == e_Quit && code1 != 0))
426 	code = code1;
427 #if defined(_MSC_VER) || defined(__BORLANDC__)
428     } __except(exception_code() == EXCEPTION_STACK_OVERFLOW) {
429         code = e_Fatal;
430         fprintf(stderr, "*** C stack overflow. Quiting...\n");
431     }
432 #endif
433 
434     gsdll.delete_instance(instance);
435 
436 #ifdef DEBUG
437     visual_tracer_close();
438 #endif
439 
440     unload_dll(&gsdll);
441 
442     free(nargv);
443 
444     /* close other thread */
445     quitnow = TRUE;
446     PostThreadMessage(thread_id, WM_QUIT, 0, (LPARAM)0);
447     Sleep(0);
448 
449     exit_status = 0;
450     switch (code) {
451 	case 0:
452 	case e_Info:
453 	case e_Quit:
454 	    break;
455 	case e_Fatal:
456 	    exit_status = 1;
457 	    break;
458 	default:
459 	    exit_status = 255;
460     }
461 
462 
463     return exit_status;
464 }
465 
466