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