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