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