wine.inf: Add LSA key under the HKLM control key.
[wine/hacks.git] / dlls / wineps.drv / escape.c
blob1062b96fafc0edf739d69e1bb1ad6a39cc36fc84
1 /*
2 * PostScript driver Escape function
4 * Copyright 1998 Huw D M Davies
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.
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.
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
21 #include "config.h"
22 #include "wine/port.h"
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
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"
44 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
46 static const char psbegindocument[] =
47 "%%BeginDocument: Wine passthrough\n";
49 /* FIXME: should use winspool functions instead */
50 static DWORD create_job(LPCSTR pszOutput)
52 int fd = -1;
53 char psCmd[1024];
54 const char *psCmdP = psCmd;
55 HKEY hkey;
57 /* TTD convert the 'output device' into a spool file name */
59 if (pszOutput == NULL || *pszOutput == '\0') return 0;
61 psCmd[0] = 0;
62 /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
63 if(!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Printing\\Spooler", &hkey))
65 DWORD type, count = sizeof(psCmd);
66 RegQueryValueExA(hkey, pszOutput, 0, &type, (LPBYTE)psCmd, &count);
67 RegCloseKey(hkey);
69 if (!psCmd[0] && !strncmp("LPR:",pszOutput,4))
70 sprintf(psCmd,"|lpr -P'%s'",pszOutput+4);
72 TRACE("Got printerSpoolCommand '%s' for output device '%s'\n",
73 psCmd, pszOutput);
74 if (!*psCmd)
75 psCmdP = pszOutput;
76 else
78 while (*psCmdP && isspace(*psCmdP))
80 psCmdP++;
82 if (!*psCmdP) return 0;
84 TRACE("command: '%s'\n", psCmdP);
85 #ifdef HAVE_FORK
86 if (*psCmdP == '|')
88 int fds[2];
89 if (pipe(fds)) {
90 ERR("pipe() failed!\n");
91 return 0;
93 if (fork() == 0)
95 psCmdP++;
97 TRACE("In child need to exec %s\n",psCmdP);
98 close(0);
99 dup2(fds[0],0);
100 close (fds[1]);
102 /* reset signals that we previously set to SIG_IGN */
103 signal( SIGPIPE, SIG_DFL );
104 signal( SIGCHLD, SIG_DFL );
106 execl("/bin/sh", "/bin/sh", "-c", psCmdP, NULL);
107 _exit(1);
110 close (fds[0]);
111 fd = fds[1];
112 TRACE("Need to execute a cmnd and pipe the output to it\n");
114 else
115 #endif
117 char *buffer;
118 WCHAR psCmdPW[MAX_PATH];
120 TRACE("Just assume it's a file\n");
123 * The file name can be dos based, we have to find its
124 * corresponding Unix file name.
126 MultiByteToWideChar(CP_ACP, 0, psCmdP, -1, psCmdPW, MAX_PATH);
127 if ((buffer = wine_get_unix_file_name(psCmdPW)))
129 if ((fd = open(buffer, O_CREAT | O_TRUNC | O_WRONLY, 0666)) < 0)
131 ERR("Failed to create spool file '%s' ('%s'). (error %s)\n",
132 buffer, psCmdP, strerror(errno));
134 HeapFree(GetProcessHeap(), 0, buffer);
137 return fd + 1;
140 static int close_job( DWORD id )
142 int fd = id - 1;
143 close( fd );
144 return TRUE;
147 DWORD write_spool( PSDRV_PDEVICE *physDev, const void *data, DWORD num )
149 int fd = physDev->job.id - 1;
150 if (write( fd, data, num) != num) return SP_OUTOFDISK;
151 return num;
154 /**********************************************************************
155 * ExtEscape (WINEPS.@)
157 INT CDECL PSDRV_ExtEscape( PSDRV_PDEVICE *physDev, INT nEscape, INT cbInput, LPCVOID in_data,
158 INT cbOutput, LPVOID out_data )
160 switch(nEscape)
162 case QUERYESCSUPPORT:
163 if(cbInput < sizeof(INT))
165 WARN("cbInput < sizeof(INT) (=%d) for QUERYESCSUPPORT\n", cbInput);
166 return 0;
167 } else {
168 UINT num = *(const UINT *)in_data;
169 TRACE("QUERYESCSUPPORT for %d\n", num);
171 switch(num) {
172 case NEXTBAND:
173 /*case BANDINFO:*/
174 case SETCOPYCOUNT:
175 case GETTECHNOLOGY:
176 case SETLINECAP:
177 case SETLINEJOIN:
178 case SETMITERLIMIT:
179 case SETCHARSET:
180 case EXT_DEVICE_CAPS:
181 case SET_BOUNDS:
182 case EPSPRINTING:
183 case POSTSCRIPT_DATA:
184 case PASSTHROUGH:
185 case POSTSCRIPT_PASSTHROUGH:
186 case POSTSCRIPT_IGNORE:
187 case BEGIN_PATH:
188 case CLIP_TO_PATH:
189 case END_PATH:
190 /*case DRAWPATTERNRECT:*/
191 return TRUE;
193 default:
194 FIXME("QUERYESCSUPPORT(%d) - not supported.\n", num);
195 return FALSE;
199 case MFCOMMENT:
201 FIXME("MFCOMMENT(%p, %d)\n", in_data, cbInput);
202 return 1;
204 case DRAWPATTERNRECT:
206 DRAWPATRECT *dpr = (DRAWPATRECT*)in_data;
208 FIXME("DRAWPATTERNRECT(pos (%d,%d), size %dx%d, style %d, pattern %x), stub!\n",
209 dpr->ptPosition.x, dpr->ptPosition.y,
210 dpr->ptSize.x, dpr->ptSize.y,
211 dpr->wStyle, dpr->wPattern
213 return 1;
215 case BANDINFO:
217 BANDINFOSTRUCT *ibi = (BANDINFOSTRUCT*)in_data;
218 BANDINFOSTRUCT *obi = (BANDINFOSTRUCT*)out_data;
220 FIXME("BANDINFO(graphics %d, text %d, rect [%dx%d-%dx%d]), stub!\n",
221 ibi->GraphicsFlag,
222 ibi->TextFlag,
223 ibi->GraphicsRect.top,
224 ibi->GraphicsRect.bottom,
225 ibi->GraphicsRect.left,
226 ibi->GraphicsRect.right
228 *obi = *ibi;
229 return 1;
231 case NEXTBAND:
233 RECT *r = out_data;
234 if(!physDev->job.banding) {
235 physDev->job.banding = TRUE;
236 r->left = 0;
237 r->top = 0;
238 r->right = physDev->horzRes;
239 r->bottom = physDev->vertRes;
240 TRACE("NEXTBAND returning %d,%d - %d,%d\n", r->left, r->top, r->right, r->bottom );
241 return 1;
243 r->left = 0;
244 r->top = 0;
245 r->right = 0;
246 r->bottom = 0;
247 TRACE("NEXTBAND rect to 0,0 - 0,0\n" );
248 physDev->job.banding = FALSE;
249 return EndPage( physDev->hdc );
252 case SETCOPYCOUNT:
254 const INT *NumCopies = in_data;
255 INT *ActualCopies = out_data;
256 if(cbInput != sizeof(INT)) {
257 WARN("cbInput != sizeof(INT) (=%d) for SETCOPYCOUNT\n", cbInput);
258 return 0;
260 TRACE("SETCOPYCOUNT %d\n", *NumCopies);
261 *ActualCopies = 1;
262 return 1;
265 case GETTECHNOLOGY:
267 LPSTR p = out_data;
268 strcpy(p, "PostScript");
269 *(p + strlen(p) + 1) = '\0'; /* 2 '\0's at end of string */
270 return 1;
273 case SETLINECAP:
275 INT newCap = *(const INT *)in_data;
276 if(cbInput != sizeof(INT)) {
277 WARN("cbInput != sizeof(INT) (=%d) for SETLINECAP\n", cbInput);
278 return 0;
280 TRACE("SETLINECAP %d\n", newCap);
281 return 0;
284 case SETLINEJOIN:
286 INT newJoin = *(const INT *)in_data;
287 if(cbInput != sizeof(INT)) {
288 WARN("cbInput != sizeof(INT) (=%d) for SETLINEJOIN\n", cbInput);
289 return 0;
291 TRACE("SETLINEJOIN %d\n", newJoin);
292 return 0;
295 case SETMITERLIMIT:
297 INT newLimit = *(const INT *)in_data;
298 if(cbInput != sizeof(INT)) {
299 WARN("cbInput != sizeof(INT) (=%d) for SETMITERLIMIT\n", cbInput);
300 return 0;
302 TRACE("SETMITERLIMIT %d\n", newLimit);
303 return 0;
306 case SETCHARSET:
307 /* Undocumented escape used by winword6.
308 Switches between ANSI and a special charset.
309 If *lpInData == 1 we require that
310 0x91 is quoteleft
311 0x92 is quoteright
312 0x93 is quotedblleft
313 0x94 is quotedblright
314 0x95 is bullet
315 0x96 is endash
316 0x97 is emdash
317 0xa0 is non break space - yeah right.
319 If *lpInData == 0 we get ANSI.
320 Since there's nothing else there, let's just make these the default
321 anyway and see what happens...
323 return 1;
325 case EXT_DEVICE_CAPS:
327 UINT cap = *(const UINT *)in_data;
328 if(cbInput != sizeof(UINT)) {
329 WARN("cbInput != sizeof(UINT) (=%d) for EXT_DEVICE_CAPS\n", cbInput);
330 return 0;
332 TRACE("EXT_DEVICE_CAPS %d\n", cap);
333 return 0;
336 case SET_BOUNDS:
338 const RECT *r = in_data;
339 if(cbInput != sizeof(RECT)) {
340 WARN("cbInput != sizeof(RECT) (=%d) for SET_BOUNDS\n", cbInput);
341 return 0;
343 TRACE("SET_BOUNDS (%d,%d) - (%d,%d)\n", r->left, r->top,
344 r->right, r->bottom);
345 return 0;
348 case EPSPRINTING:
350 UINT epsprint = *(const UINT*)in_data;
351 /* FIXME: In this mode we do not need to send page intros and page
352 * ends according to the doc. But I just ignore that detail
353 * for now.
355 TRACE("EPS Printing support %sable.\n",epsprint?"en":"dis");
356 return 1;
359 case POSTSCRIPT_DATA:
360 case PASSTHROUGH:
361 case POSTSCRIPT_PASSTHROUGH:
363 /* Write directly to spool file, bypassing normal PS driver
364 * processing that is done along with writing PostScript code
365 * to the spool.
366 * We have a WORD before the data counting the size, but
367 * cbInput is just this +2.
368 * However Photoshop 7 has a bug that sets cbInput to 2 less than the
369 * length of the string, rather than 2 more. So we'll use the WORD at
370 * in_data[0] instead.
372 if(!physDev->job.in_passthrough) {
373 write_spool(physDev, psbegindocument, sizeof(psbegindocument)-1);
374 physDev->job.in_passthrough = TRUE;
376 return write_spool(physDev,((char*)in_data)+2,*(const WORD*)in_data);
379 case POSTSCRIPT_IGNORE:
381 BOOL ret = physDev->job.quiet;
382 TRACE("POSTSCRIPT_IGNORE %d\n", *(const short*)in_data);
383 physDev->job.quiet = *(const short*)in_data;
384 return ret;
387 case GETSETPRINTORIENT:
389 /* If lpInData is present, it is a 20 byte structure, first 32
390 * bit LONG value is the orientation. if lpInData is NULL, it
391 * returns the current orientation.
393 FIXME("GETSETPRINTORIENT not implemented (data %p)!\n",in_data);
394 return 1;
396 case BEGIN_PATH:
397 TRACE("BEGIN_PATH\n");
398 if(physDev->pathdepth)
399 FIXME("Nested paths not yet handled\n");
400 return ++physDev->pathdepth;
402 case END_PATH:
404 const struct PATH_INFO *info = (const struct PATH_INFO*)in_data;
406 TRACE("END_PATH\n");
407 if(!physDev->pathdepth) {
408 ERR("END_PATH called without a BEIGN_PATH\n");
409 return -1;
411 TRACE("RenderMode = %d, FillMode = %d, BkMode = %d\n",
412 info->RenderMode, info->FillMode, info->BkMode);
413 switch(info->RenderMode) {
414 case RENDERMODE_NO_DISPLAY:
415 PSDRV_WriteClosePath(physDev); /* not sure if this is necessary, but it can't hurt */
416 break;
417 case RENDERMODE_OPEN:
418 case RENDERMODE_CLOSED:
419 default:
420 FIXME("END_PATH: RenderMode %d, not yet supported\n", info->RenderMode);
421 break;
423 return --physDev->pathdepth;
426 case CLIP_TO_PATH:
428 WORD mode = *(const WORD*)in_data;
430 switch(mode) {
431 case CLIP_SAVE:
432 TRACE("CLIP_TO_PATH: CLIP_SAVE\n");
433 PSDRV_WriteGSave(physDev);
434 return 1;
435 case CLIP_RESTORE:
436 TRACE("CLIP_TO_PATH: CLIP_RESTORE\n");
437 PSDRV_WriteGRestore(physDev);
438 return 1;
439 case CLIP_INCLUSIVE:
440 TRACE("CLIP_TO_PATH: CLIP_INCLUSIVE\n");
441 /* FIXME to clip or eoclip ? (see PATH_INFO.FillMode) */
442 PSDRV_WriteClip(physDev);
443 PSDRV_WriteNewPath(physDev);
444 return 1;
445 case CLIP_EXCLUSIVE:
446 FIXME("CLIP_EXCLUSIVE: not implemented\n");
447 return 0;
448 default:
449 FIXME("Unknown CLIP_TO_PATH mode %d\n", mode);
450 return 0;
453 default:
454 FIXME("Unimplemented code 0x%x\n", nEscape);
455 return 0;
459 /************************************************************************
460 * PSDRV_StartPage
462 INT CDECL PSDRV_StartPage( PSDRV_PDEVICE *physDev )
464 if(!physDev->job.OutOfPage) {
465 FIXME("Already started a page?\n");
466 return 1;
469 if(physDev->job.PageNo++ == 0) {
470 if(!PSDRV_WriteHeader( physDev, physDev->job.DocName ))
471 return 0;
474 if(!PSDRV_WriteNewPage( physDev ))
475 return 0;
476 physDev->job.OutOfPage = FALSE;
477 return 1;
481 /************************************************************************
482 * PSDRV_EndPage
484 INT CDECL PSDRV_EndPage( PSDRV_PDEVICE *physDev )
486 if(physDev->job.OutOfPage) {
487 FIXME("Already ended a page?\n");
488 return 1;
490 if(!PSDRV_WriteEndPage( physDev ))
491 return 0;
492 PSDRV_EmptyDownloadList(physDev, FALSE);
493 physDev->job.OutOfPage = TRUE;
494 return 1;
498 /************************************************************************
499 * PSDRV_StartDocA
501 static INT PSDRV_StartDocA( PSDRV_PDEVICE *physDev, const DOCINFOA *doc )
503 LPCSTR output = "LPT1:";
504 BYTE buf[300];
505 HANDLE hprn = INVALID_HANDLE_VALUE;
506 PRINTER_INFO_5A *pi5 = (PRINTER_INFO_5A*)buf;
507 DWORD needed;
509 if(physDev->job.id) {
510 FIXME("hJob != 0. Now what?\n");
511 return 0;
514 if(doc->lpszOutput)
515 output = doc->lpszOutput;
516 else if(physDev->job.output)
517 output = physDev->job.output;
518 else {
519 if(OpenPrinterA(physDev->pi->FriendlyName, &hprn, NULL) &&
520 GetPrinterA(hprn, 5, buf, sizeof(buf), &needed)) {
521 output = pi5->pPortName;
523 if(hprn != INVALID_HANDLE_VALUE)
524 ClosePrinter(hprn);
527 physDev->job.id = create_job( output );
528 if(!physDev->job.id) {
529 WARN("OpenJob failed\n");
530 return 0;
532 physDev->job.banding = FALSE;
533 physDev->job.OutOfPage = TRUE;
534 physDev->job.PageNo = 0;
535 physDev->job.quiet = FALSE;
536 physDev->job.in_passthrough = FALSE;
537 physDev->job.had_passthrough_rect = FALSE;
538 if(doc->lpszDocName) {
539 physDev->job.DocName = HeapAlloc(GetProcessHeap(), 0, strlen(doc->lpszDocName)+1);
540 strcpy(physDev->job.DocName, doc->lpszDocName);
541 } else
542 physDev->job.DocName = NULL;
544 return physDev->job.id;
547 /************************************************************************
548 * PSDRV_StartDoc
550 INT CDECL PSDRV_StartDoc( PSDRV_PDEVICE *physDev, const DOCINFOW *doc )
552 DOCINFOA docA;
553 INT ret, len;
554 LPSTR docname = NULL, output = NULL, datatype = NULL;
556 docA.cbSize = doc->cbSize;
557 if (doc->lpszDocName)
559 len = WideCharToMultiByte( CP_ACP, 0, doc->lpszDocName, -1, NULL, 0, NULL, NULL );
560 if ((docname = HeapAlloc( GetProcessHeap(), 0, len )))
561 WideCharToMultiByte( CP_ACP, 0, doc->lpszDocName, -1, docname, len, NULL, NULL );
563 if (doc->lpszOutput)
565 len = WideCharToMultiByte( CP_ACP, 0, doc->lpszOutput, -1, NULL, 0, NULL, NULL );
566 if ((output = HeapAlloc( GetProcessHeap(), 0, len )))
567 WideCharToMultiByte( CP_ACP, 0, doc->lpszOutput, -1, output, len, NULL, NULL );
569 if (doc->lpszDatatype)
571 len = WideCharToMultiByte( CP_ACP, 0, doc->lpszDatatype, -1, NULL, 0, NULL, NULL );
572 if ((datatype = HeapAlloc( GetProcessHeap(), 0, len )))
573 WideCharToMultiByte( CP_ACP, 0, doc->lpszDatatype, -1, datatype, len, NULL, NULL );
575 docA.lpszDocName = docname;
576 docA.lpszOutput = output;
577 docA.lpszDatatype = datatype;
578 docA.fwType = doc->fwType;
580 ret = PSDRV_StartDocA(physDev, &docA);
582 HeapFree( GetProcessHeap(), 0, docname );
583 HeapFree( GetProcessHeap(), 0, output );
584 HeapFree( GetProcessHeap(), 0, datatype );
586 return ret;
589 /************************************************************************
590 * PSDRV_EndDoc
592 INT CDECL PSDRV_EndDoc( PSDRV_PDEVICE *physDev )
594 INT ret = 1;
595 if(!physDev->job.id) {
596 FIXME("hJob == 0. Now what?\n");
597 return 0;
600 if(!physDev->job.OutOfPage) {
601 WARN("Somebody forgot an EndPage\n");
602 PSDRV_EndPage( physDev );
604 PSDRV_WriteFooter( physDev );
606 ret = close_job( physDev->job.id );
607 physDev->job.id = 0;
608 HeapFree(GetProcessHeap(), 0, physDev->job.DocName);
609 physDev->job.DocName = NULL;
611 return ret;