user32/tests: Flush events before the key flags test.
[wine] / dlls / wineps.drv / escape.c
1 /*
2  *      PostScript driver Escape function
3  *
4  *      Copyright 1998  Huw D M Davies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <signal.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34
35 #include "windef.h"
36 #include "winbase.h"
37 #include "wingdi.h"
38 #include "wine/wingdi16.h"
39 #include "winreg.h"
40 #include "psdrv.h"
41 #include "wine/debug.h"
42 #include "winspool.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
45
46 static const char psbegindocument[] =
47 "%%BeginDocument: Wine passthrough\n";
48
49
50 DWORD write_spool( PHYSDEV dev, const void *data, DWORD num )
51 {
52     PSDRV_PDEVICE *physDev = get_psdrv_dev( dev );
53     DWORD written;
54
55     if (!WritePrinter(physDev->job.hprinter, (LPBYTE) data, num, &written) || (written != num))
56         return SP_OUTOFDISK;
57
58     return num;
59 }
60
61 /**********************************************************************
62  *           ExtEscape  (WINEPS.@)
63  */
64 INT PSDRV_ExtEscape( PHYSDEV dev, INT nEscape, INT cbInput, LPCVOID in_data,
65                      INT cbOutput, LPVOID out_data )
66 {
67     PSDRV_PDEVICE *physDev = get_psdrv_dev( dev );
68
69     switch(nEscape)
70     {
71     case QUERYESCSUPPORT:
72         if(cbInput < sizeof(INT))
73         {
74             WARN("cbInput < sizeof(INT) (=%d) for QUERYESCSUPPORT\n", cbInput);
75             return 0;
76         } else {
77             UINT num = *(const UINT *)in_data;
78             TRACE("QUERYESCSUPPORT for %d\n", num);
79
80             switch(num) {
81             case NEXTBAND:
82             /*case BANDINFO:*/
83             case SETCOPYCOUNT:
84             case GETTECHNOLOGY:
85             case SETLINECAP:
86             case SETLINEJOIN:
87             case SETMITERLIMIT:
88             case SETCHARSET:
89             case EXT_DEVICE_CAPS:
90             case SET_BOUNDS:
91             case EPSPRINTING:
92             case POSTSCRIPT_DATA:
93             case PASSTHROUGH:
94             case POSTSCRIPT_PASSTHROUGH:
95             case POSTSCRIPT_IGNORE:
96             case BEGIN_PATH:
97             case CLIP_TO_PATH:
98             case END_PATH:
99             /*case DRAWPATTERNRECT:*/
100                 return TRUE;
101
102             default:
103                 FIXME("QUERYESCSUPPORT(%d) - not supported.\n", num);
104                 return FALSE;
105             }
106         }
107
108     case MFCOMMENT:
109     {
110         FIXME("MFCOMMENT(%p, %d)\n", in_data, cbInput);
111         return 1;
112     }
113     case DRAWPATTERNRECT:
114     {
115         DRAWPATRECT     *dpr = (DRAWPATRECT*)in_data;
116
117         FIXME("DRAWPATTERNRECT(pos (%d,%d), size %dx%d, style %d, pattern %x), stub!\n",
118                 dpr->ptPosition.x, dpr->ptPosition.y,
119                 dpr->ptSize.x, dpr->ptSize.y,
120                 dpr->wStyle, dpr->wPattern
121         );
122         return 1;
123     }
124     case BANDINFO:
125     {
126         BANDINFOSTRUCT  *ibi = (BANDINFOSTRUCT*)in_data;
127         BANDINFOSTRUCT  *obi = (BANDINFOSTRUCT*)out_data;
128
129         FIXME("BANDINFO(graphics %d, text %d, rect [%dx%d-%dx%d]), stub!\n",
130                 ibi->GraphicsFlag,
131                 ibi->TextFlag,
132                 ibi->GraphicsRect.top,
133                 ibi->GraphicsRect.bottom,
134                 ibi->GraphicsRect.left,
135                 ibi->GraphicsRect.right
136         );
137         *obi = *ibi;
138         return 1;
139     }
140     case NEXTBAND:
141     {
142         RECT *r = out_data;
143         if(!physDev->job.banding) {
144             physDev->job.banding = TRUE;
145             r->left   = 0;
146             r->top    = 0;
147             r->right  = physDev->horzRes;
148             r->bottom = physDev->vertRes;
149             TRACE("NEXTBAND returning %d,%d - %d,%d\n", r->left, r->top, r->right, r->bottom );
150             return 1;
151         }
152         r->left   = 0;
153         r->top    = 0;
154         r->right  = 0;
155         r->bottom = 0;
156         TRACE("NEXTBAND rect to 0,0 - 0,0\n" );
157         physDev->job.banding = FALSE;
158         return EndPage( dev->hdc );
159     }
160
161     case SETCOPYCOUNT:
162         {
163             const INT *NumCopies = in_data;
164             INT *ActualCopies = out_data;
165             if(cbInput != sizeof(INT)) {
166                 WARN("cbInput != sizeof(INT) (=%d) for SETCOPYCOUNT\n", cbInput);
167                 return 0;
168             }
169             TRACE("SETCOPYCOUNT %d\n", *NumCopies);
170             *ActualCopies = 1;
171             return 1;
172         }
173
174     case GETTECHNOLOGY:
175         {
176             LPSTR p = out_data;
177             strcpy(p, "PostScript");
178             *(p + strlen(p) + 1) = '\0'; /* 2 '\0's at end of string */
179             return 1;
180         }
181
182     case SETLINECAP:
183         {
184             INT newCap = *(const INT *)in_data;
185             if(cbInput != sizeof(INT)) {
186                 WARN("cbInput != sizeof(INT) (=%d) for SETLINECAP\n", cbInput);
187                 return 0;
188             }
189             TRACE("SETLINECAP %d\n", newCap);
190             return 0;
191         }
192
193     case SETLINEJOIN:
194         {
195             INT newJoin = *(const INT *)in_data;
196             if(cbInput != sizeof(INT)) {
197                 WARN("cbInput != sizeof(INT) (=%d) for SETLINEJOIN\n", cbInput);
198                 return 0;
199             }
200             TRACE("SETLINEJOIN %d\n", newJoin);
201             return 0;
202         }
203
204     case SETMITERLIMIT:
205         {
206             INT newLimit = *(const INT *)in_data;
207             if(cbInput != sizeof(INT)) {
208                 WARN("cbInput != sizeof(INT) (=%d) for SETMITERLIMIT\n", cbInput);
209                 return 0;
210             }
211             TRACE("SETMITERLIMIT %d\n", newLimit);
212             return 0;
213         }
214
215     case SETCHARSET:
216       /* Undocumented escape used by winword6.
217          Switches between ANSI and a special charset.
218          If *lpInData == 1 we require that
219          0x91 is quoteleft
220          0x92 is quoteright
221          0x93 is quotedblleft
222          0x94 is quotedblright
223          0x95 is bullet
224          0x96 is endash
225          0x97 is emdash
226          0xa0 is non break space - yeah right.
227
228          If *lpInData == 0 we get ANSI.
229          Since there's nothing else there, let's just make these the default
230          anyway and see what happens...
231       */
232         return 1;
233
234     case EXT_DEVICE_CAPS:
235         {
236             UINT cap = *(const UINT *)in_data;
237             if(cbInput != sizeof(UINT)) {
238                 WARN("cbInput != sizeof(UINT) (=%d) for EXT_DEVICE_CAPS\n", cbInput);
239                 return 0;
240             }
241             TRACE("EXT_DEVICE_CAPS %d\n", cap);
242             return 0;
243         }
244
245     case SET_BOUNDS:
246         {
247             const RECT *r = in_data;
248             if(cbInput != sizeof(RECT)) {
249                 WARN("cbInput != sizeof(RECT) (=%d) for SET_BOUNDS\n", cbInput);
250                 return 0;
251             }
252             TRACE("SET_BOUNDS (%d,%d) - (%d,%d)\n", r->left, r->top,
253                   r->right, r->bottom);
254             return 0;
255         }
256
257     case EPSPRINTING:
258         {
259             UINT epsprint = *(const UINT*)in_data;
260             /* FIXME: In this mode we do not need to send page intros and page
261              * ends according to the doc. But I just ignore that detail
262              * for now.
263              */
264             TRACE("EPS Printing support %sable.\n",epsprint?"en":"dis");
265             return 1;
266         }
267
268     case POSTSCRIPT_DATA:
269     case PASSTHROUGH:
270     case POSTSCRIPT_PASSTHROUGH:
271         {
272             /* Write directly to spool file, bypassing normal PS driver
273              * processing that is done along with writing PostScript code
274              * to the spool.
275              * We have a WORD before the data counting the size, but
276              * cbInput is just this +2.
277              * However Photoshop 7 has a bug that sets cbInput to 2 less than the
278              * length of the string, rather than 2 more.  So we'll use the WORD at
279              * in_data[0] instead.
280              */
281             if(!physDev->job.in_passthrough) {
282                 write_spool(dev, psbegindocument, sizeof(psbegindocument)-1);
283                 physDev->job.in_passthrough = TRUE;
284             }
285             return write_spool(dev,((char*)in_data)+2,*(const WORD*)in_data);
286         }
287
288     case POSTSCRIPT_IGNORE:
289       {
290         BOOL ret = physDev->job.quiet;
291         TRACE("POSTSCRIPT_IGNORE %d\n", *(const short*)in_data);
292         physDev->job.quiet = *(const short*)in_data;
293         return ret;
294       }
295
296     case GETSETPRINTORIENT:
297         {
298             /* If lpInData is present, it is a 20 byte structure, first 32
299              * bit LONG value is the orientation. if lpInData is NULL, it
300              * returns the current orientation.
301              */
302             FIXME("GETSETPRINTORIENT not implemented (data %p)!\n",in_data);
303             return 1;
304         }
305     case BEGIN_PATH:
306         TRACE("BEGIN_PATH\n");
307         if(physDev->pathdepth)
308             FIXME("Nested paths not yet handled\n");
309         return ++physDev->pathdepth;
310
311     case END_PATH:
312       {
313         const struct PATH_INFO *info = (const struct PATH_INFO*)in_data;
314
315         TRACE("END_PATH\n");
316         if(!physDev->pathdepth) {
317             ERR("END_PATH called without a BEGIN_PATH\n");
318             return -1;
319         }
320         TRACE("RenderMode = %d, FillMode = %d, BkMode = %d\n",
321               info->RenderMode, info->FillMode, info->BkMode);
322         switch(info->RenderMode) {
323         case RENDERMODE_NO_DISPLAY:
324             PSDRV_WriteClosePath(dev); /* not sure if this is necessary, but it can't hurt */
325             break;
326         case RENDERMODE_OPEN:
327         case RENDERMODE_CLOSED:
328         default:
329             FIXME("END_PATH: RenderMode %d, not yet supported\n", info->RenderMode);
330             break;
331         }
332         return --physDev->pathdepth;
333       }
334
335     case CLIP_TO_PATH:
336       {
337         WORD mode = *(const WORD*)in_data;
338
339         switch(mode) {
340         case CLIP_SAVE:
341             TRACE("CLIP_TO_PATH: CLIP_SAVE\n");
342             PSDRV_WriteGSave(dev);
343             return 1;
344         case CLIP_RESTORE:
345             TRACE("CLIP_TO_PATH: CLIP_RESTORE\n");
346             PSDRV_WriteGRestore(dev);
347             return 1;
348         case CLIP_INCLUSIVE:
349             TRACE("CLIP_TO_PATH: CLIP_INCLUSIVE\n");
350             /* FIXME to clip or eoclip ? (see PATH_INFO.FillMode) */
351             PSDRV_WriteClip(dev);
352             PSDRV_WriteNewPath(dev);
353             return 1;
354         case CLIP_EXCLUSIVE:
355             FIXME("CLIP_EXCLUSIVE: not implemented\n");
356             return 0;
357         default:
358             FIXME("Unknown CLIP_TO_PATH mode %d\n", mode);
359             return 0;
360         }
361       }
362     default:
363         FIXME("Unimplemented code %d\n", nEscape);
364         return 0;
365     }
366 }
367
368 /************************************************************************
369  *           PSDRV_StartPage
370  */
371 INT PSDRV_StartPage( PHYSDEV dev )
372 {
373     PSDRV_PDEVICE *physDev = get_psdrv_dev( dev );
374
375     if(!physDev->job.OutOfPage) {
376         FIXME("Already started a page?\n");
377         return 1;
378     }
379
380     if(physDev->job.PageNo++ == 0) {
381         if(!PSDRV_WriteHeader( dev, physDev->job.DocName ))
382             return 0;
383     }
384
385     if(!PSDRV_WriteNewPage( dev ))
386         return 0;
387     physDev->job.OutOfPage = FALSE;
388     return 1;
389 }
390
391
392 /************************************************************************
393  *           PSDRV_EndPage
394  */
395 INT PSDRV_EndPage( PHYSDEV dev )
396 {
397     PSDRV_PDEVICE *physDev = get_psdrv_dev( dev );
398
399     if(physDev->job.OutOfPage) {
400         FIXME("Already ended a page?\n");
401         return 1;
402     }
403     if(!PSDRV_WriteEndPage( dev ))
404         return 0;
405     PSDRV_EmptyDownloadList(dev, FALSE);
406     physDev->job.OutOfPage = TRUE;
407     return 1;
408 }
409
410
411 /************************************************************************
412  *           PSDRV_StartDocA
413  */
414 static INT PSDRV_StartDocA( PHYSDEV dev, const DOCINFOA *doc )
415 {
416     PSDRV_PDEVICE *physDev = get_psdrv_dev( dev );
417     DOC_INFO_1A di;
418
419     TRACE("(%p, %p) => %s, %s, %s\n", physDev, doc, debugstr_a(doc->lpszDocName),
420         debugstr_a(doc->lpszOutput), debugstr_a(doc->lpszDatatype));
421
422     if(physDev->job.id) {
423         FIXME("hJob != 0. Now what?\n");
424         return 0;
425     }
426
427     /* FIXME: use PRINTER_DEFAULTS here */
428     if(!OpenPrinterA(physDev->pi->FriendlyName, &physDev->job.hprinter, NULL)) {
429         WARN("OpenPrinter(%s, ...) failed: %d\n",
430             debugstr_a(physDev->pi->FriendlyName), GetLastError());
431         return 0;
432     }
433
434     di.pDocName = (LPSTR) doc->lpszDocName;
435     di.pDatatype = NULL;
436
437     if(doc->lpszOutput)
438         di.pOutputFile = (LPSTR) doc->lpszOutput;
439     else if(physDev->job.output)
440         di.pOutputFile = physDev->job.output;
441     else
442         di.pOutputFile = NULL;
443
444     TRACE("using output: %s\n", debugstr_a(di.pOutputFile));
445
446     /* redirection located in HKCU\Software\Wine\Printing\Spooler
447        is done during winspool.drv,ScheduleJob */
448     physDev->job.id = StartDocPrinterA(physDev->job.hprinter, 1, (LPBYTE) &di);
449     if(!physDev->job.id) {
450         WARN("StartDocPrinter() failed: %d\n", GetLastError());
451         ClosePrinter(physDev->job.hprinter);
452         return 0;
453     }
454     physDev->job.banding = FALSE;
455     physDev->job.OutOfPage = TRUE;
456     physDev->job.PageNo = 0;
457     physDev->job.quiet = FALSE;
458     physDev->job.in_passthrough = FALSE;
459     physDev->job.had_passthrough_rect = FALSE;
460     if(doc->lpszDocName) {
461         physDev->job.DocName = HeapAlloc(GetProcessHeap(), 0, strlen(doc->lpszDocName)+1);
462         strcpy(physDev->job.DocName, doc->lpszDocName);
463     } else
464         physDev->job.DocName = NULL;
465
466     return physDev->job.id;
467 }
468
469 /************************************************************************
470  *           PSDRV_StartDoc
471  */
472 INT PSDRV_StartDoc( PHYSDEV dev, const DOCINFOW *doc )
473 {
474     DOCINFOA docA;
475     INT ret, len;
476     LPSTR docname = NULL, output = NULL, datatype = NULL;
477
478     TRACE("(%p, %p) => %d,%s,%s,%s\n", dev, doc, doc->cbSize, debugstr_w(doc->lpszDocName),
479         debugstr_w(doc->lpszOutput), debugstr_w(doc->lpszDatatype));
480
481     docA.cbSize = doc->cbSize;
482     if (doc->lpszDocName)
483     {
484         len = WideCharToMultiByte( CP_ACP, 0, doc->lpszDocName, -1, NULL, 0, NULL, NULL );
485         if ((docname = HeapAlloc( GetProcessHeap(), 0, len )))
486             WideCharToMultiByte( CP_ACP, 0, doc->lpszDocName, -1, docname, len, NULL, NULL );
487     }
488     if (doc->lpszOutput)
489     {
490         len = WideCharToMultiByte( CP_ACP, 0, doc->lpszOutput, -1, NULL, 0, NULL, NULL );
491         if ((output = HeapAlloc( GetProcessHeap(), 0, len )))
492             WideCharToMultiByte( CP_ACP, 0, doc->lpszOutput, -1, output, len, NULL, NULL );
493     }
494     if (doc->lpszDatatype)
495     {
496         len = WideCharToMultiByte( CP_ACP, 0, doc->lpszDatatype, -1, NULL, 0, NULL, NULL );
497         if ((datatype = HeapAlloc( GetProcessHeap(), 0, len )))
498             WideCharToMultiByte( CP_ACP, 0, doc->lpszDatatype, -1, datatype, len, NULL, NULL );
499     }
500     docA.lpszDocName = docname;
501     docA.lpszOutput = output;
502     docA.lpszDatatype = datatype;
503     docA.fwType = doc->fwType;
504
505     ret = PSDRV_StartDocA(dev, &docA);
506
507     HeapFree( GetProcessHeap(), 0, docname );
508     HeapFree( GetProcessHeap(), 0, output );
509     HeapFree( GetProcessHeap(), 0, datatype );
510
511     return ret;
512 }
513
514 /************************************************************************
515  *           PSDRV_EndDoc
516  */
517 INT PSDRV_EndDoc( PHYSDEV dev )
518 {
519     PSDRV_PDEVICE *physDev = get_psdrv_dev( dev );
520     INT ret = 1;
521
522     if(!physDev->job.id) {
523         FIXME("hJob == 0. Now what?\n");
524         return 0;
525     }
526
527     if(!physDev->job.OutOfPage) {
528         WARN("Somebody forgot an EndPage\n");
529         PSDRV_EndPage( dev );
530     }
531
532     if (physDev->job.PageNo)
533         PSDRV_WriteFooter( dev );
534
535     ret = EndDocPrinter(physDev->job.hprinter);
536     ClosePrinter(physDev->job.hprinter);
537     physDev->job.hprinter = NULL;
538     physDev->job.id = 0;
539     HeapFree(GetProcessHeap(), 0, physDev->job.DocName);
540     physDev->job.DocName = NULL;
541
542     return ret;
543 }