gdi32/tests: Test BitBlt() to an enhanced metafile.
[wine] / dlls / gdi32 / printdrv16.c
1 /*
2  * Implementation of some printer driver bits
3  *
4  * Copyright 1996 John Harvey
5  * Copyright 1998 Huw Davies
6  * Copyright 1998 Andreas Mohr
7  * Copyright 1999 Klaas van Gend
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <signal.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #ifdef HAVE_IO_H
35 # include <io.h>
36 #endif
37 #ifdef HAVE_UNISTD_H
38 # include <unistd.h>
39 #endif
40 #include <fcntl.h>
41 #include "windef.h"
42 #include "winbase.h"
43 #include "winuser.h"
44 #include "wine/winbase16.h"
45 #include "wine/wingdi16.h"
46 #include "winspool.h"
47 #include "winerror.h"
48 #include "winreg.h"
49 #include "wownt32.h"
50 #include "wine/debug.h"
51 #include "gdi_private.h"
52
53 WINE_DEFAULT_DEBUG_CHANNEL(print);
54
55 static const char PrinterModel[]      = "Printer Model";
56 static const char DefaultDevMode[]    = "Default DevMode";
57 static const char PrinterDriverData[] = "PrinterDriverData";
58 static const char Printers[]          = "System\\CurrentControlSet\\Control\\Print\\Printers\\";
59
60 /****************** misc. printer related functions */
61
62 /*
63  * The following function should implement a queing system
64  */
65 struct hpq
66 {
67     struct hpq  *next;
68     int          tag;
69     int          key;
70 };
71
72 static struct hpq *hpqueue;
73
74 /**********************************************************************
75  *           CreatePQ   (GDI.230)
76  *
77  */
78 HPQ16 WINAPI CreatePQ16(INT16 size)
79 {
80 #if 0
81     HGLOBAL16 hpq = 0;
82     WORD tmp_size;
83     LPWORD pPQ;
84
85     tmp_size = size << 2;
86     if (!(hpq = GlobalAlloc16(GMEM_SHARE|GMEM_MOVEABLE, tmp_size + 8)))
87        return 0xffff;
88     pPQ = GlobalLock16(hpq);
89     *pPQ++ = 0;
90     *pPQ++ = tmp_size;
91     *pPQ++ = 0;
92     *pPQ++ = 0;
93     GlobalUnlock16(hpq);
94
95     return (HPQ16)hpq;
96 #else
97     FIXME("(%d): stub\n",size);
98     return 1;
99 #endif
100 }
101
102 /**********************************************************************
103  *           DeletePQ   (GDI.235)
104  *
105  */
106 INT16 WINAPI DeletePQ16(HPQ16 hPQ)
107 {
108     return GlobalFree16(hPQ);
109 }
110
111 /**********************************************************************
112  *           ExtractPQ   (GDI.232)
113  *
114  */
115 INT16 WINAPI ExtractPQ16(HPQ16 hPQ)
116 {
117     struct hpq *queue, *prev, *current, *currentPrev;
118     int key = 0, tag = -1;
119     currentPrev = prev = NULL;
120     queue = current = hpqueue;
121     if (current)
122         key = current->key;
123
124     while (current)
125     {
126         currentPrev = current;
127         current = current->next;
128         if (current)
129         {
130             if (current->key < key)
131             {
132                 queue = current;
133                 prev = currentPrev;
134             }
135         }
136     }
137     if (queue)
138     {
139         tag = queue->tag;
140
141         if (prev)
142             prev->next = queue->next;
143         else
144             hpqueue = queue->next;
145         HeapFree(GetProcessHeap(), 0, queue);
146     }
147
148     TRACE("%x got tag %d key %d\n", hPQ, tag, key);
149
150     return tag;
151 }
152
153 /**********************************************************************
154  *           InsertPQ   (GDI.233)
155  *
156  */
157 INT16 WINAPI InsertPQ16(HPQ16 hPQ, INT16 tag, INT16 key)
158 {
159     struct hpq *queueItem = HeapAlloc(GetProcessHeap(), 0, sizeof(struct hpq));
160     if(queueItem == NULL) {
161         ERR("Memory exausted!\n");
162         return FALSE;
163     }
164     queueItem->next = hpqueue;
165     hpqueue = queueItem;
166     queueItem->key = key;
167     queueItem->tag = tag;
168
169     FIXME("(%x %d %d): stub???\n", hPQ, tag, key);
170     return TRUE;
171 }
172
173 /**********************************************************************
174  *           MinPQ   (GDI.231)
175  *
176  */
177 INT16 WINAPI MinPQ16(HPQ16 hPQ)
178 {
179     FIXME("(%x): stub\n", hPQ);
180     return 0;
181 }
182
183 /**********************************************************************
184  *           SizePQ   (GDI.234)
185  *
186  */
187 INT16 WINAPI SizePQ16(HPQ16 hPQ, INT16 sizechange)
188 {
189     FIXME("(%x %d): stub\n", hPQ, sizechange);
190     return -1;
191 }
192
193
194
195 /*
196  * The following functions implement part of the spooling process to
197  * print manager.  I would like to see wine have a version of print managers
198  * that used LPR/LPD.  For simplicity print jobs will be sent to a file for
199  * now.
200  */
201 typedef struct PRINTJOB
202 {
203     char        *pszOutput;
204     char        *pszTitle;
205     HDC16       hDC;
206     HANDLE16    hHandle;
207     int         nIndex;
208     int         fd;
209 } PRINTJOB, *PPRINTJOB;
210
211 #define MAX_PRINT_JOBS 1
212 #define SP_OK 1
213
214 static PPRINTJOB gPrintJobsTable[MAX_PRINT_JOBS];
215
216
217 static PPRINTJOB FindPrintJobFromHandle(HANDLE16 hHandle)
218 {
219     return gPrintJobsTable[0];
220 }
221
222 static int CreateSpoolFile(LPCSTR pszOutput)
223 {
224     int fd=-1;
225     char psCmd[1024];
226     const char *psCmdP = psCmd;
227     HKEY hkey;
228
229     /* TTD convert the 'output device' into a spool file name */
230
231     if (pszOutput == NULL || *pszOutput == '\0')
232       return -1;
233
234     psCmd[0] = 0;
235     /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
236     if(!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Printing\\Spooler", &hkey))
237     {
238         DWORD type, count = sizeof(psCmd);
239         RegQueryValueExA(hkey, pszOutput, 0, &type, (LPBYTE)psCmd, &count);
240         RegCloseKey(hkey);
241     }
242     if (!psCmd[0] && !strncmp("LPR:",pszOutput,4))
243         sprintf(psCmd,"|lpr -P'%s'",pszOutput+4);
244
245     TRACE("Got printerSpoolCommand '%s' for output device '%s'\n",
246           psCmd, pszOutput);
247     if (!*psCmd)
248         psCmdP = pszOutput;
249     else
250     {
251         while (*psCmdP && isspace(*psCmdP))
252         {
253             psCmdP++;
254         }
255         if (!*psCmdP)
256             return -1;
257     }
258     TRACE("command: '%s'\n", psCmdP);
259 #ifdef HAVE_FORK
260     if (*psCmdP == '|')
261     {
262         int fds[2];
263         if (pipe(fds)) {
264             ERR("pipe() failed!\n");
265             return -1;
266         }
267         if (fork() == 0)
268         {
269             psCmdP++;
270
271             TRACE("In child need to exec %s\n",psCmdP);
272             close(0);
273             dup2(fds[0],0);
274             close (fds[1]);
275
276             /* reset signals that we previously set to SIG_IGN */
277             signal( SIGPIPE, SIG_DFL );
278             signal( SIGCHLD, SIG_DFL );
279
280             execl("/bin/sh", "/bin/sh", "-c", psCmdP, NULL);
281             _exit(1);
282
283         }
284         close (fds[0]);
285         fd = fds[1];
286         TRACE("Need to execute a cmnd and pipe the output to it\n");
287     }
288     else
289 #endif
290     {
291         char *buffer;
292         WCHAR psCmdPW[MAX_PATH];
293
294         TRACE("Just assume it's a file\n");
295
296         /**
297          * The file name can be dos based, we have to find its
298          * corresponding Unix file name.
299          */
300         MultiByteToWideChar(CP_ACP, 0, psCmdP, -1, psCmdPW, MAX_PATH);
301         if ((buffer = wine_get_unix_file_name(psCmdPW)))
302         {
303             if ((fd = open(buffer, O_CREAT | O_TRUNC | O_WRONLY, 0666)) < 0)
304             {
305                 ERR("Failed to create spool file '%s' ('%s'). (error %s)\n",
306                     buffer, psCmdP, strerror(errno));
307             }
308             HeapFree(GetProcessHeap(), 0, buffer);
309         }
310     }
311     return fd;
312 }
313
314 static int FreePrintJob(HANDLE16 hJob)
315 {
316     int nRet = SP_ERROR;
317     PPRINTJOB pPrintJob;
318
319     pPrintJob = FindPrintJobFromHandle(hJob);
320     if (pPrintJob != NULL)
321     {
322         gPrintJobsTable[pPrintJob->nIndex] = NULL;
323         HeapFree(GetProcessHeap(), 0, pPrintJob->pszOutput);
324         HeapFree(GetProcessHeap(), 0, pPrintJob->pszTitle);
325         if (pPrintJob->fd >= 0) close(pPrintJob->fd);
326         HeapFree(GetProcessHeap(), 0, pPrintJob);
327         nRet = SP_OK;
328     }
329     return nRet;
330 }
331
332 /**********************************************************************
333  *           OpenJob   (GDI.240)
334  *
335  */
336 HPJOB16 WINAPI OpenJob16(LPCSTR lpOutput, LPCSTR lpTitle, HDC16 hDC)
337 {
338     HPJOB16 hHandle = (HPJOB16)SP_ERROR;
339     PPRINTJOB pPrintJob;
340
341     TRACE("'%s' '%s' %04x\n", lpOutput, lpTitle, hDC);
342
343     pPrintJob = gPrintJobsTable[0];
344     if (pPrintJob == NULL)
345     {
346         int fd;
347
348         /* Try and create a spool file */
349         fd = CreateSpoolFile(lpOutput);
350         if (fd >= 0)
351         {
352             pPrintJob = HeapAlloc(GetProcessHeap(), 0, sizeof(PRINTJOB));
353             if(pPrintJob == NULL) {
354                 WARN("Memory exausted!\n");
355                 return hHandle;
356             }
357
358             hHandle = 1;
359
360             pPrintJob->pszOutput = HeapAlloc(GetProcessHeap(), 0, strlen(lpOutput)+1);
361             strcpy( pPrintJob->pszOutput, lpOutput );
362             if(lpTitle)
363             {
364                 pPrintJob->pszTitle = HeapAlloc(GetProcessHeap(), 0, strlen(lpTitle)+1);
365                 strcpy( pPrintJob->pszTitle, lpTitle );
366             }
367             pPrintJob->hDC = hDC;
368             pPrintJob->fd = fd;
369             pPrintJob->nIndex = 0;
370             pPrintJob->hHandle = hHandle;
371             gPrintJobsTable[pPrintJob->nIndex] = pPrintJob;
372         }
373     }
374     TRACE("return %04x\n", hHandle);
375     return hHandle;
376 }
377
378 /**********************************************************************
379  *           CloseJob   (GDI.243)
380  *
381  */
382 INT16 WINAPI CloseJob16(HPJOB16 hJob)
383 {
384     int nRet = SP_ERROR;
385     PPRINTJOB pPrintJob = NULL;
386
387     TRACE("%04x\n", hJob);
388
389     pPrintJob = FindPrintJobFromHandle(hJob);
390     if (pPrintJob != NULL)
391     {
392         /* Close the spool file */
393         close(pPrintJob->fd);
394         FreePrintJob(hJob);
395         nRet  = 1;
396     }
397     return nRet;
398 }
399
400 /**********************************************************************
401  *           WriteSpool   (GDI.241)
402  *
403  */
404 INT16 WINAPI WriteSpool16(HPJOB16 hJob, LPSTR lpData, INT16 cch)
405 {
406     int nRet = SP_ERROR;
407     PPRINTJOB pPrintJob = NULL;
408
409     TRACE("%04x %p %04x\n", hJob, lpData, cch);
410
411     pPrintJob = FindPrintJobFromHandle(hJob);
412     if (pPrintJob != NULL && pPrintJob->fd >= 0 && cch)
413     {
414         if (write(pPrintJob->fd, lpData, cch) != cch)
415           nRet = SP_OUTOFDISK;
416         else
417           nRet = cch;
418 #if 0
419         /* FIXME: We just cannot call 16 bit functions from here, since we
420          * have acquired several locks (DC). And we do not really need to.
421          */
422         if (pPrintJob->hDC == 0) {
423             TRACE("hDC == 0 so no QueryAbort\n");
424         }
425         else if (!(QueryAbort16(pPrintJob->hDC, (nRet == SP_OUTOFDISK) ? nRet : 0 )))
426         {
427             CloseJob16(hJob); /* printing aborted */
428             nRet = SP_APPABORT;
429         }
430 #endif
431     }
432     return nRet;
433 }
434
435 typedef INT (WINAPI *MSGBOX_PROC)( HWND, LPCSTR, LPCSTR, UINT );
436
437 /**********************************************************************
438  *           WriteDialog   (GDI.242)
439  *
440  */
441 INT16 WINAPI WriteDialog16(HPJOB16 hJob, LPSTR lpMsg, INT16 cchMsg)
442 {
443     HMODULE mod;
444     MSGBOX_PROC pMessageBoxA;
445     INT16 ret = 0;
446
447     TRACE("%04x %04x '%s'\n", hJob,  cchMsg, lpMsg);
448
449     if ((mod = GetModuleHandleA("user32.dll")))
450     {
451         if ((pMessageBoxA = (MSGBOX_PROC)GetProcAddress( mod, "MessageBoxA" )))
452             ret = pMessageBoxA(0, lpMsg, "Printing Error", MB_OKCANCEL);
453     }
454     return ret;
455 }
456
457
458 /**********************************************************************
459  *           DeleteJob  (GDI.244)
460  *
461  */
462 INT16 WINAPI DeleteJob16(HPJOB16 hJob, INT16 nNotUsed)
463 {
464     int nRet;
465
466     TRACE("%04x\n", hJob);
467
468     nRet = FreePrintJob(hJob);
469     return nRet;
470 }
471
472 /*
473  * The following two function would allow a page to be sent to the printer
474  * when it has been processed.  For simplicity they haven't been implemented.
475  * This means a whole job has to be processed before it is sent to the printer.
476  */
477
478 /**********************************************************************
479  *           StartSpoolPage   (GDI.246)
480  *
481  */
482 INT16 WINAPI StartSpoolPage16(HPJOB16 hJob)
483 {
484     FIXME("StartSpoolPage GDI.246 unimplemented\n");
485     return 1;
486
487 }
488
489
490 /**********************************************************************
491  *           EndSpoolPage   (GDI.247)
492  *
493  */
494 INT16 WINAPI EndSpoolPage16(HPJOB16 hJob)
495 {
496     FIXME("EndSpoolPage GDI.247 unimplemented\n");
497     return 1;
498 }
499
500
501 /**********************************************************************
502  *           GetSpoolJob   (GDI.245)
503  *
504  */
505 DWORD WINAPI GetSpoolJob16(int nOption, LONG param)
506 {
507     DWORD retval = 0;
508     TRACE("In GetSpoolJob param 0x%x noption %d\n",param, nOption);
509     return retval;
510 }
511
512
513 /******************************************************************
514  *                  DrvGetPrinterDataInternal
515  *
516  * Helper for DrvGetPrinterData
517  */
518 static DWORD DrvGetPrinterDataInternal(LPSTR RegStr_Printer,
519 LPBYTE lpPrinterData, int cbData, int what)
520 {
521     DWORD res = -1;
522     HKEY hkey;
523     DWORD dwType, cbQueryData;
524
525     if (!(RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey))) {
526         if (what == INT_PD_DEFAULT_DEVMODE) { /* "Default DevMode" */
527             if (!(RegQueryValueExA(hkey, DefaultDevMode, 0, &dwType, 0, &cbQueryData))) {
528                 if (!lpPrinterData)
529                     res = cbQueryData;
530                 else if ((cbQueryData) && (cbQueryData <= cbData)) {
531                     cbQueryData = cbData;
532                     if (RegQueryValueExA(hkey, DefaultDevMode, 0,
533                                 &dwType, lpPrinterData, &cbQueryData))
534                         res = cbQueryData;
535                 }
536             }
537         } else { /* "Printer Driver" */
538             cbQueryData = 32;
539             RegQueryValueExA(hkey, "Printer Driver", 0,
540                         &dwType, lpPrinterData, &cbQueryData);
541             res = cbQueryData;
542         }
543     }
544     if (hkey) RegCloseKey(hkey);
545     return res;
546 }
547
548 /******************************************************************
549  *                DrvGetPrinterData     (GDI.282)
550  *
551  */
552 DWORD WINAPI DrvGetPrinterData16(LPSTR lpPrinter, LPSTR lpProfile,
553                                LPDWORD lpType, LPBYTE lpPrinterData,
554                                int cbData, LPDWORD lpNeeded)
555 {
556     LPSTR RegStr_Printer;
557     HKEY hkey = 0, hkey2 = 0;
558     DWORD res = 0;
559     DWORD dwType, PrinterAttr, cbPrinterAttr, SetData, size;
560
561     if (HIWORD(lpPrinter))
562             TRACE("printer %s\n",lpPrinter);
563     else
564             TRACE("printer %p\n",lpPrinter);
565     if (HIWORD(lpProfile))
566             TRACE("profile %s\n",lpProfile);
567     else
568             TRACE("profile %p\n",lpProfile);
569     TRACE("lpType %p\n",lpType);
570
571     if ((!lpPrinter) || (!lpProfile) || (!lpNeeded))
572         return ERROR_INVALID_PARAMETER;
573
574     RegStr_Printer = HeapAlloc(GetProcessHeap(), 0,
575                                strlen(Printers) + strlen(lpPrinter) + 2);
576     strcpy(RegStr_Printer, Printers);
577     strcat(RegStr_Printer, lpPrinter);
578
579     if ((PtrToUlong(lpProfile) == INT_PD_DEFAULT_DEVMODE) || (HIWORD(lpProfile) &&
580     (!strcmp(lpProfile, DefaultDevMode)))) {
581         size = DrvGetPrinterDataInternal(RegStr_Printer, lpPrinterData, cbData,
582                                          INT_PD_DEFAULT_DEVMODE);
583         if (size+1) {
584             *lpNeeded = size;
585             if ((lpPrinterData) && (*lpNeeded > cbData))
586                 res = ERROR_MORE_DATA;
587         }
588         else res = ERROR_INVALID_PRINTER_NAME;
589     }
590     else
591     if ((PtrToUlong(lpProfile) == INT_PD_DEFAULT_MODEL) || (HIWORD(lpProfile) &&
592     (!strcmp(lpProfile, PrinterModel)))) {
593         *lpNeeded = 32;
594         if (!lpPrinterData) goto failed;
595         if (cbData < 32) {
596             res = ERROR_MORE_DATA;
597             goto failed;
598         }
599         size = DrvGetPrinterDataInternal(RegStr_Printer, lpPrinterData, cbData,
600                                          INT_PD_DEFAULT_MODEL);
601         if ((size+1) && (lpType))
602             *lpType = REG_SZ;
603         else
604             res = ERROR_INVALID_PRINTER_NAME;
605     }
606     else
607     {
608         if ((res = RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey)))
609             goto failed;
610         cbPrinterAttr = 4;
611         if ((res = RegQueryValueExA(hkey, "Attributes", 0,
612                         &dwType, (LPBYTE)&PrinterAttr, &cbPrinterAttr)))
613             goto failed;
614         if ((res = RegOpenKeyA(hkey, PrinterDriverData, &hkey2)))
615             goto failed;
616         *lpNeeded = cbData;
617         res = RegQueryValueExA(hkey2, lpProfile, 0,
618                 lpType, lpPrinterData, lpNeeded);
619         if ((res != ERROR_CANTREAD) &&
620          ((PrinterAttr &
621         (PRINTER_ATTRIBUTE_ENABLE_BIDI|PRINTER_ATTRIBUTE_NETWORK))
622         == PRINTER_ATTRIBUTE_NETWORK))
623         {
624             if (!(res) && (*lpType == REG_DWORD) && (*(LPDWORD)lpPrinterData == -1))
625                 res = ERROR_INVALID_DATA;
626         }
627         else
628         {
629             SetData = -1;
630             RegSetValueExA(hkey2, lpProfile, 0, REG_DWORD, (LPBYTE)&SetData, 4); /* no result returned */
631         }
632     }
633
634 failed:
635     if (hkey2) RegCloseKey(hkey2);
636     if (hkey) RegCloseKey(hkey);
637     HeapFree(GetProcessHeap(), 0, RegStr_Printer);
638     return res;
639 }
640
641
642 /******************************************************************
643  *                 DrvSetPrinterData     (GDI.281)
644  *
645  */
646 DWORD WINAPI DrvSetPrinterData16(LPSTR lpPrinter, LPSTR lpProfile,
647                                DWORD lpType, LPBYTE lpPrinterData,
648                                DWORD dwSize)
649 {
650     LPSTR RegStr_Printer;
651     HKEY hkey = 0;
652     DWORD res = 0;
653
654     if (HIWORD(lpPrinter))
655             TRACE("printer %s\n",lpPrinter);
656     else
657             TRACE("printer %p\n",lpPrinter);
658     if (HIWORD(lpProfile))
659             TRACE("profile %s\n",lpProfile);
660     else
661             TRACE("profile %p\n",lpProfile);
662     TRACE("lpType %08x\n",lpType);
663
664     if ((!lpPrinter) || (!lpProfile) ||
665     (PtrToUlong(lpProfile) == INT_PD_DEFAULT_MODEL) || (HIWORD(lpProfile) &&
666     (!strcmp(lpProfile, PrinterModel))))
667         return ERROR_INVALID_PARAMETER;
668
669     RegStr_Printer = HeapAlloc(GetProcessHeap(), 0,
670                         strlen(Printers) + strlen(lpPrinter) + 2);
671     strcpy(RegStr_Printer, Printers);
672     strcat(RegStr_Printer, lpPrinter);
673
674     if ((PtrToUlong(lpProfile) == INT_PD_DEFAULT_DEVMODE) || (HIWORD(lpProfile) &&
675     (!strcmp(lpProfile, DefaultDevMode)))) {
676         if ( RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey)
677              != ERROR_SUCCESS ||
678              RegSetValueExA(hkey, DefaultDevMode, 0, REG_BINARY,
679                               lpPrinterData, dwSize) != ERROR_SUCCESS )
680                 res = ERROR_INVALID_PRINTER_NAME;
681     }
682     else
683     {
684         strcat(RegStr_Printer, "\\");
685
686         if( (res = RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey)) ==
687             ERROR_SUCCESS ) {
688
689             if (!lpPrinterData)
690                 res = RegDeleteValueA(hkey, lpProfile);
691             else
692                 res = RegSetValueExA(hkey, lpProfile, 0, lpType,
693                                        lpPrinterData, dwSize);
694         }
695     }
696
697     if (hkey) RegCloseKey(hkey);
698     HeapFree(GetProcessHeap(), 0, RegStr_Printer);
699     return res;
700 }