1 /* win32_print.c */
2 /* printing support for Tux Paint */
3 
4 /* John Popplewell <john@johnnypops.demon.co.uk> */
5 
6 /*
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11 
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16 
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21 
22 /* Sept. 30, 2002 - Oct. 17, 2002 */
23 /* Oct.  07, 2003 - added banding support */
24 /*                - prints using 24-bit (not 32-bit) bitmap */
25 /* $Id$ */
26 
27 #include <windows.h>
28 #include <direct.h>
29 #include "SDL_syswm.h"
30 #include "win32_print.h"
31 #include "debug.h"
32 
33 
34 #define NOREF(x)        ((x)=(x))
35 #define GETHINST(hWnd)	((HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE))
36 #define MIR(id)	        (MAKEINTRESOURCE(id))
37 
38 static HDC hDCprinter = NULL;
39 
40 /**
41  * FIXME
42  */
make24bitDIB(SDL_Surface * surf)43 static SDL_Surface *make24bitDIB(SDL_Surface * surf)
44 {
45   SDL_PixelFormat pixfmt;
46   SDL_Surface *surf24;
47   SDL_Surface *surfDIB;
48   Uint8 *src, *dst;
49   Uint32 linesize;
50   int i;
51 
52   memset(&pixfmt, 0, sizeof(pixfmt));
53   pixfmt.palette = NULL;
54   pixfmt.BitsPerPixel = 24;
55   pixfmt.BytesPerPixel = 3;
56   pixfmt.Rmask = 0x00FF0000;
57   pixfmt.Gmask = 0x0000FF00;
58   pixfmt.Bmask = 0x000000FF;
59   pixfmt.Amask = 0;
60   pixfmt.Rshift = 16;
61   pixfmt.Gshift = 8;
62   pixfmt.Bshift = 0;
63   pixfmt.Ashift = 0;
64   pixfmt.Rloss = 0;
65   pixfmt.Gloss = 0;
66   pixfmt.Bloss = 0;
67   pixfmt.Aloss = 0;
68   pixfmt.colorkey = 0;
69   pixfmt.alpha = 0;
70 
71   surf24 = SDL_ConvertSurface(surf, &pixfmt, SDL_SWSURFACE);
72   surfDIB = SDL_CreateRGBSurface(SDL_SWSURFACE, surf24->w, surf24->h, 24,
73                                  pixfmt.Rmask, pixfmt.Gmask, pixfmt.Bmask, pixfmt.Amask);
74 
75   linesize = surf24->w * 3;     // Flip top2bottom
76   dst = surfDIB->pixels;
77   src = ((Uint8 *) surf24->pixels) + ((surf24->h - 1) * surf24->pitch);
78   for (i = 0; i < surf24->h; ++i)
79     {
80       memcpy(dst, src, linesize);
81       src -= surf24->pitch;
82       dst += surfDIB->pitch;
83     }
84 
85   SDL_FreeSurface(surf24);      // Free temp surface
86 
87   return surfDIB;
88 }
89 
90 /**
91  * FIXME
92  */
93 /* returns 0 if failed */
GetDefaultPrinterStrings(char * device,char * driver,char * output)94 static int GetDefaultPrinterStrings(char *device, char *driver, char *output)
95 {
96   const char *section = "windows";
97   const char *key = "device";
98   const char *def = "NODEFAULTPRINTER";
99   char buff[MAX_PATH];
100   char *dev, *drv, *out;
101 
102   if (!GetProfileString(section, key, def, buff, sizeof(buff)))
103     return 0;
104 
105   if (strcmp(buff, def) == 0)
106     return 0;
107 
108   if (((dev = strtok(buff, ",")) != NULL) &&
109       ((drv = strtok(NULL, ", ")) != NULL) && ((out = strtok(NULL, ", ")) != NULL))
110     {
111       if (device)
112         strcpy(device, dev);
113       if (driver)
114         strcpy(driver, drv);
115       if (output)
116         strcpy(output, out);
117       return 1;
118     }
119   return 0;
120 }
121 
122 #define dmDeviceNameSize    32
123 
124 /**
125  * FIXME
126  */
LoadCustomPrinterHDEVMODE(HWND hWnd,const char * filepath)127 static HANDLE LoadCustomPrinterHDEVMODE(HWND hWnd, const char *filepath)
128 {
129   char device[MAX_PATH];
130   HANDLE hPrinter = NULL;
131   int sizeof_devmode;
132   HGLOBAL hDevMode = NULL;
133   DEVMODE *devmode = NULL;
134   int res;
135   FILE *fp = NULL;
136   int block_size;
137   int block_read;
138 
139   if ((fp = fopen(filepath, "rb")) == NULL)
140     return NULL;
141 
142   if (fread(device, 1, dmDeviceNameSize, fp) != dmDeviceNameSize)
143     goto err_exit;
144 
145   if (!OpenPrinter(device, &hPrinter, NULL))
146     goto err_exit;
147 
148   sizeof_devmode = (int)DocumentProperties(hWnd, hPrinter, device, NULL, NULL, 0);
149 
150   if (!sizeof_devmode)
151     goto err_exit;
152 
153   hDevMode = GlobalAlloc(GHND, sizeof_devmode);
154   if (!hDevMode)
155     goto err_exit;
156 
157   devmode = (DEVMODE *) GlobalLock(hDevMode);
158   if (!devmode)
159     goto err_exit;
160 
161   res = DocumentProperties(hWnd, hPrinter, device, devmode, NULL, DM_OUT_BUFFER);
162   if (res != IDOK)
163     goto err_exit;
164 
165   block_size = devmode->dmSize + devmode->dmDriverExtra;
166   block_read = fread(devmode, 1, block_size, fp);
167   if (block_size != block_read)
168     goto err_exit;
169   fclose(fp);
170 
171   res = DocumentProperties(hWnd, hPrinter, device, devmode, devmode, DM_IN_BUFFER | DM_OUT_BUFFER);
172   if (res != IDOK)
173     goto err_exit;
174 
175   GlobalUnlock(hDevMode);
176   ClosePrinter(hPrinter);
177   return hDevMode;
178 
179 err_exit:
180   if (fp)
181     fclose(fp);
182   if (devmode)
183     GlobalUnlock(hDevMode);
184   if (hDevMode)
185     GlobalFree(hDevMode);
186   if (hPrinter)
187     ClosePrinter(hPrinter);
188   return NULL;
189 }
190 
191 /**
192  * FIXME
193  */
SaveCustomPrinterHDEVMODE(HWND hWnd,const char * filepath,HANDLE hDevMode)194 static int SaveCustomPrinterHDEVMODE(HWND hWnd, const char *filepath, HANDLE hDevMode)
195 {
196   FILE *fp = NULL;
197 
198   NOREF(hWnd);
199   if ((fp = fopen(filepath, "wb")) != NULL)
200     {
201       DEVMODE *devmode = (DEVMODE *) GlobalLock(hDevMode);
202       int block_size = devmode->dmSize + devmode->dmDriverExtra;
203       int block_written;
204       char devname[dmDeviceNameSize];
205 
206       strcpy(devname, (const char *)devmode->dmDeviceName);
207       fwrite(devname, 1, sizeof(devname), fp);
208       block_written = fwrite(devmode, 1, block_size, fp);
209       GlobalUnlock(hDevMode);
210       fclose(fp);
211       return block_size == block_written;
212     }
213   return 0;
214 }
215 
216 /**
217  * Returns whether or not a given file exists.
218  *
219  * @param filepath Path to the file
220  * @return 1 if file exists, 0 otherwise
221  */
FileExists(const char * filepath)222 static int FileExists(const char *filepath)
223 {
224   FILE *fp;
225 
226   if ((fp = fopen(filepath, "rb")) != NULL)
227     {
228       fclose(fp);
229       return 1;
230     }
231   return 0;
232 }
233 
234 
235 /**
236  * FIXME
237  */
GetCustomPrinterDC(HWND hWnd,const char * printcfg,int show)238 static int GetCustomPrinterDC(HWND hWnd, const char *printcfg, int show)
239 {
240   PRINTDLG pd = {
241     sizeof(PRINTDLG),
242     hWnd, NULL, NULL, NULL,
243     PD_RETURNDC,
244     0xFFFF,
245     0xFFFF,
246     0xFFFF,
247     0xFFFF,
248     1,
249     0, 0, 0, 0, 0, 0, 0, 0,
250   };
251 
252   pd.hDevMode = LoadCustomPrinterHDEVMODE(hWnd, printcfg);
253 
254   if (show || !FileExists(printcfg))
255     {
256       if (PrintDlg(&pd))
257         {
258           hDCprinter = pd.hDC;
259           SaveCustomPrinterHDEVMODE(hWnd, printcfg, pd.hDevMode);
260           GlobalFree(pd.hDevMode);
261           return 1;
262         }
263       GlobalFree(pd.hDevMode);
264       return 0;
265     }
266 
267   {
268     DEVMODE *devmode = (DEVMODE *) GlobalLock(pd.hDevMode);
269 
270     hDCprinter = CreateDC(NULL, (const char *)devmode->dmDeviceName, NULL, devmode);
271     GlobalUnlock(pd.hDevMode);
272     GlobalFree(pd.hDevMode);
273   }
274   return 1;
275 }
276 
277 
278 /**
279  * FIXME
280  */
GetDefaultPrinterDC(void)281 static HDC GetDefaultPrinterDC(void)
282 {
283   char device[MAX_PATH], driver[MAX_PATH], output[MAX_PATH];
284 
285   if (GetDefaultPrinterStrings(device, driver, output))
286     return CreateDC(driver, device, output, NULL);
287 
288   return NULL;
289 }
290 
291 
292 /**
293  * FIXME
294  */
GetPrinterDC(HWND hWnd,const char * printcfg,int show)295 static int GetPrinterDC(HWND hWnd, const char *printcfg, int show)
296 {
297   hDCprinter = NULL;
298 
299   if (printcfg)
300     {
301       return GetCustomPrinterDC(hWnd, printcfg, show);
302     }
303   hDCprinter = GetDefaultPrinterDC();
304   return 1;
305 }
306 
307 
308 /**
309  * FIXME
310  */
IsPrinterAvailable(void)311 int IsPrinterAvailable(void)
312 {
313   return (GetDefaultPrinterStrings(NULL, NULL, NULL) != 0);
314 }
315 
316 #define STRETCH_TO_FIT  0
317 #define SCALE_TO_FIT    1
318 
319 
320 /**
321  * FIXME
322  */
SurfacePrint(SDL_Surface * surf,const char * printcfg,int showdialog)323 const char *SurfacePrint(SDL_Surface * surf, const char *printcfg, int showdialog)
324 {
325   const char *res = NULL;
326   HWND hWnd;
327   DOCINFO di;
328   int nError;
329   SDL_SysWMinfo wminfo;
330   BITMAPINFOHEADER bmih;
331   SDL_Surface *surf24 = NULL;
332   RECT rcDst;
333   float sX, sY;
334   int pageWidth, pageHeight;
335   int hDCCaps;
336   HBITMAP hbm = NULL;
337   HDC hdcMem = NULL;
338   int scaling = SCALE_TO_FIT;
339 
340   SDL_VERSION(&wminfo.version);
341   if (!SDL_GetWMInfo(&wminfo))
342     return "win32_print: SDL_GetWMInfo() failed.";
343 
344   hWnd = wminfo.window;
345   if (!GetPrinterDC(hWnd, printcfg, showdialog))
346     {
347       ShowWindow(hWnd, SW_SHOWNORMAL);
348       return NULL;
349     }
350 
351   if (!hDCprinter)
352     return "win32_print: GetPrinterDC() failed.";
353 
354   EnableWindow(hWnd, FALSE);
355 
356   di.cbSize = sizeof(DOCINFO);
357   di.lpszDocName = "Tux Paint";
358   di.lpszOutput = (LPTSTR) NULL;
359   di.lpszDatatype = (LPTSTR) NULL;
360   di.fwType = 0;
361 
362   nError = StartDoc(hDCprinter, &di);
363   if (nError == SP_ERROR)
364     {
365       res = "win32_print: StartDoc() failed.";
366       goto error;
367     }
368 
369   nError = StartPage(hDCprinter);
370   if (nError <= 0)
371     {
372       res = "win32_print: StartPage() failed.";
373       goto error;
374     }
375 
376 //////////////////////////////////////////////////////////////////////////////////////
377 
378   surf24 = make24bitDIB(surf);
379   if (!surf24)
380     {
381       res = "win32_print: make24bitDIB() failed.";
382       goto error;
383     }
384 
385   memset(&bmih, 0, sizeof(bmih));
386   bmih.biSize = sizeof(bmih);
387   bmih.biPlanes = 1;
388   bmih.biCompression = BI_RGB;
389   bmih.biBitCount = 24;
390   bmih.biWidth = surf24->w;
391   bmih.biHeight = surf24->h;
392 
393   pageWidth = GetDeviceCaps(hDCprinter, HORZRES);
394   pageHeight = GetDeviceCaps(hDCprinter, VERTRES);
395   sX = GetDeviceCaps(hDCprinter, LOGPIXELSX);
396   sY = GetDeviceCaps(hDCprinter, LOGPIXELSY);
397 
398   switch (scaling)
399     {
400     case STRETCH_TO_FIT:
401       {
402         /* stretches x and y dimensions independently to fit the page */
403         /* doesn't preserve image aspect-ratio */
404         rcDst.top = 0;
405         rcDst.left = 0;
406         rcDst.bottom = pageHeight;
407         rcDst.right = pageWidth;
408         break;
409       }
410     case SCALE_TO_FIT:
411       {
412         /* maximises image size on the page */
413         /* preserves aspect-ratio, alignment is top and center */
414         int width = bmih.biWidth;
415         int height = bmih.biHeight;
416 
417         if (width < pageWidth && height < pageHeight)
418           {
419             float dW = (float)pageWidth / width;
420             float dH = (float)pageHeight / height;
421 
422             if (dW < dH)
423               {
424                 width = pageWidth;
425                 height = (int)((height * dW * (sY / sX)) + 0.5f);
426               }
427             else
428               {
429                 width = (int)((width * dH * (sX / sY)) + 0.5f);
430                 height = pageHeight;
431               }
432           }
433         if (width > pageWidth)
434           {
435             height = height * width / pageWidth;
436             width = pageWidth;
437           }
438         if (height > pageHeight)
439           {
440             width = width * height / pageHeight;
441             height = pageHeight;
442           }
443 
444         rcDst.top = 0;
445         rcDst.left = (pageWidth - width) / 2;
446         rcDst.bottom = rcDst.top + height;
447         rcDst.right = rcDst.left + width;
448         break;
449       }
450     default:
451       res = "win32_print: invalid scaling option.";
452       goto error;
453     }
454 
455   hDCCaps = GetDeviceCaps(hDCprinter, RASTERCAPS);
456 
457   if (hDCCaps & RC_PALETTE)
458     {
459       res = "win32_print: printer context requires palette.";
460       goto error;
461     }
462 
463   if (hDCCaps & RC_STRETCHDIB)
464     {
465       SetStretchBltMode(hDCprinter, COLORONCOLOR);
466 
467       nError = StretchDIBits(hDCprinter, rcDst.left, rcDst.top,
468                              rcDst.right - rcDst.left,
469                              rcDst.bottom - rcDst.top,
470                              0, 0, bmih.biWidth, bmih.biHeight,
471                              surf24->pixels, (BITMAPINFO *) & bmih, DIB_RGB_COLORS, SRCCOPY);
472       if (nError == GDI_ERROR)
473         {
474           res = "win32_print: StretchDIBits() failed.";
475           goto error;
476         }
477     }
478   else
479     {
480       res = "win32_print: StretchDIBits() not available.";
481       goto error;
482     }
483 
484 //////////////////////////////////////////////////////////////////////////////////////
485 
486   nError = EndPage(hDCprinter);
487   if (nError <= 0)
488     {
489       res = "win32_print: EndPage() failed.";
490       goto error;
491     }
492 
493   EndDoc(hDCprinter);
494 
495 error:
496   if (hdcMem)
497     DeleteDC(hdcMem);
498   if (hbm)
499     DeleteObject(hbm);
500   if (surf24)
501     SDL_FreeSurface(surf24);
502 
503   EnableWindow(hWnd, TRUE);
504   ShowWindow(hWnd, SW_SHOWNORMAL);
505   DeleteDC(hDCprinter);
506 
507   return res;
508 }
509 
510 
511 /**
512  * FIXME
513  */
514 /*
515   Read access to Windows Registry
516 */
ReadRegistry(const char * key,const char * option,char * value,int size)517 static HRESULT ReadRegistry(const char *key, const char *option, char *value, int size)
518 {
519   LONG res;
520   HKEY hKey = NULL;
521 
522   res = RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_READ, &hKey);
523   if (res != ERROR_SUCCESS)
524     goto err_exit;
525   res = RegQueryValueEx(hKey, option, NULL, NULL, (LPBYTE) value, (LPDWORD) & size);
526   if (res != ERROR_SUCCESS)
527     goto err_exit;
528   res = ERROR_SUCCESS;
529 
530 err_exit:
531   if (hKey)
532     RegCloseKey(hKey);
533   return HRESULT_FROM_WIN32(res);
534 }
535 
536 /**
537  * Removes a single '\' or '/' from end of path
538  *
539  * @param path Directory path
540  * @return the path argument, contents of which may have been modified
541  */
remove_slash(char * path)542 static char *remove_slash(char *path)
543 {
544   int len = strlen(path);
545 
546   if (!len)
547     return path;
548 
549   if (path[len - 1] == '/' || path[len - 1] == '\\')
550     path[len - 1] = 0;
551 
552   return path;
553 }
554 
555 /*
556   Returns heap string containing default application data path.
557   Creates suffix subdirectory (only one level).
558   E.g. C:\Documents and Settings\jfp\Application Data\suffix
559 */
GetDefaultSaveDir(const char * suffix)560 char *GetDefaultSaveDir(const char *suffix)
561 {
562   char prefix[MAX_PATH];
563   char path[2 * MAX_PATH];
564   const char *key = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
565   const char *option = "AppData";
566   HRESULT hr = S_OK;
567 
568   if (SUCCEEDED(hr = ReadRegistry(key, option, prefix, sizeof(prefix))))
569     {
570       remove_slash(prefix);
571       snprintf(path, sizeof(path), "%s/%s", prefix, suffix);
572       _mkdir(path);
573       return strdup(path);
574     }
575   return strdup("userdata");
576 }
577 
578 /**
579  *
580  * Returns heap string containing system font directory.
581  * (e.g. 'C:\Windows\Fonts')
582  *
583  * @return system font dir
584  */
GetSystemFontDir(void)585 char *GetSystemFontDir(void)
586 {
587   char path[MAX_PATH];
588   const char *key = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
589   const char *option = "Fonts";
590   HRESULT hr = S_OK;
591 
592   if (SUCCEEDED(hr = ReadRegistry(key, option, path, sizeof(path))))
593     {
594       remove_slash(path);
595       return strdup(path);
596     }
597   return strdup("C:\\WINDOWS\\FONTS");
598 }
599 
600 /**
601  *
602  * Returns heap string containing user's image directory.
603  *
604  * @return user's image dir
605  */
GetUserImageDir(void)606 char *GetUserImageDir(void)
607 {
608   char path[MAX_PATH];
609   const char *key = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
610   const char *option = "My Pictures";
611   HRESULT hr = S_OK;
612 
613   if (SUCCEEDED(hr = ReadRegistry(key, option, path, sizeof(path))))
614     {
615       remove_slash(path);
616       return strdup(path);
617     }
618   return strdup("C:\\Pictures");
619 }
620 
621 /*
622   Returns heap string containing user temp directory.
623   E.g. C:\Documents and Settings\jfp\Local Settings\Temp
624 */
GetUserTempDir(void)625 static char *GetUserTempDir(void)
626 {
627   char *temp = getenv("TEMP");
628 
629   if (!temp)
630     {
631       temp = "userdata";
632     }
633   return strdup(temp);
634 }
635 
636 /**
637  * Get path of a file in a temp directory
638  *
639  * @param name Filename for temp file
640  * @return full path to the temp file
641  */
get_temp_fname(const char * const name)642 char *get_temp_fname(const char *const name)
643 {
644   char f[512];
645   char *tempdir = GetUserTempDir();
646 
647   snprintf(f, sizeof(f), "%s/%s", tempdir, name);
648   free(tempdir);
649   return strdup(f);
650 }
651 
652 /*
653  * Nasty low-level hook into the keyboard. 2K/XP/Vista only.
654  */
655 
656 static HHOOK g_hKeyboardHook = NULL;
657 static int g_bWindowActive = 0;
658 
659 /**
660  * FIXME
661  */
LowLevelKeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)662 LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
663 {
664   int bEatKeystroke = 0;
665   KBDLLHOOKSTRUCT *p = (KBDLLHOOKSTRUCT *) lParam;
666 
667   if (nCode < 0 || nCode != HC_ACTION)
668     return CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam);
669 
670   switch (wParam)
671     {
672     case WM_KEYDOWN:
673     case WM_KEYUP:
674       {
675         bEatKeystroke = g_bWindowActive && ((p->vkCode == VK_LWIN) || (p->vkCode == VK_RWIN));
676         break;
677       }
678     }
679 
680   if (bEatKeystroke)
681     return 1;
682   return CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam);
683 }
684 
685 /**
686  * FIXME
687  */
InstallKeyboardHook(void)688 int InstallKeyboardHook(void)
689 {
690   if (g_hKeyboardHook)
691     return -1;
692   g_hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0);
693   return g_hKeyboardHook ? 0 : -2;
694 }
695 
696 /**
697  * FIXME
698  */
RemoveKeyboardHook(void)699 int RemoveKeyboardHook(void)
700 {
701   if (!g_hKeyboardHook)
702     return -1;
703   UnhookWindowsHookEx(g_hKeyboardHook);
704   g_hKeyboardHook = NULL;
705   return 0;
706 }
707 
708 /**
709  * FIXME
710  */
SetActivationState(int state)711 void SetActivationState(int state)
712 {
713   g_bWindowActive = state;
714 }
715