tuxpaint-pencil-sharpener/src/win32_print.c
John Popplewell d9f70cfde6 Added GetDefaultSaveDir(suffix) which fetches the default "Application
Data" path for the current user from the registry, appends the suffix,
creates the subdirectory if it doesn't exist, then returns a string from
the heap.
This path is the new default "savedir" location on Windows. Tested on
98/2K/XP.
Changed code that sets the pwd to the directory containing the executable.
Originally for BEOS only, now working and enabled for Windows also.
2005-07-19 04:44:31 +00:00

535 lines
13 KiB
C

/* win32_print.c */
/* printing support for Tux Paint */
/* John Popplewell <john@johnnypops.demon.co.uk> */
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Sept. 30, 2002 - Oct. 17, 2002 */
/* Oct. 07, 2003 - added banding support */
/* - prints using 24-bit (not 32-bit) bitmap */
#include <windows.h>
#include <direct.h>
#include "SDL_syswm.h"
#include "win32_print.h"
#define NOREF(x) ((x)=(x))
#define GETHINST(hWnd) ((HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE))
#define MIR(id) (MAKEINTRESOURCE(id))
static PRINTDLG global_pd = {
sizeof(PRINTDLG),
NULL, NULL, NULL, NULL,
PD_RETURNDC,
0xFFFF,
0xFFFF,
0xFFFF,
0xFFFF,
1,
0, 0, 0, 0, 0, 0, 0, 0,
};
static SDL_Surface *make24bitDIB(SDL_Surface *surf)
{
SDL_PixelFormat pixfmt;
SDL_Surface *surf24;
SDL_Surface *surfDIB;
Uint8 *src,*dst;
Uint32 linesize;
int i;
memset(&pixfmt, 0, sizeof(pixfmt));
pixfmt.palette = NULL;
pixfmt.BitsPerPixel = 24;
pixfmt.BytesPerPixel= 3;
pixfmt.Rmask = 0x00FF0000;
pixfmt.Gmask = 0x0000FF00;
pixfmt.Bmask = 0x000000FF;
pixfmt.Amask = 0;
pixfmt.Rshift = 16;
pixfmt.Gshift = 8;
pixfmt.Bshift = 0;
pixfmt.Ashift = 0;
pixfmt.Rloss = 0;
pixfmt.Gloss = 0;
pixfmt.Bloss = 0;
pixfmt.Aloss = 0;
pixfmt.colorkey = 0;
pixfmt.alpha = 0;
surf24 = SDL_ConvertSurface(surf, &pixfmt, SDL_SWSURFACE);
surfDIB = SDL_CreateRGBSurface(SDL_SWSURFACE, surf24->w, surf24->h, 24,
pixfmt.Rmask, pixfmt.Gmask, pixfmt.Bmask, pixfmt.Amask);
linesize = surf24->w * 3; // Flip top2bottom
dst = surfDIB->pixels;
src = ((Uint8*)surf24->pixels)+((surf24->h-1)*surf24->pitch);
for (i = 0; i < surf24->h; ++i)
{
memcpy(dst, src, linesize);
src -= surf24->pitch;
dst += surfDIB->pitch;
}
SDL_FreeSurface(surf24); // Free temp surface
return surfDIB;
}
/* returns 0 if failed */
static int GetDefaultPrinterStrings(char *device, char *driver, char *output)
{
const char *section = "windows";
const char *key = "device";
const char *def = "NODEFAULTPRINTER";
char buff[MAX_PATH];
char *dev,*drv,*out;
if (!GetProfileString(section, key, def, buff, sizeof(buff)))
return 0;
if (strcmp(buff, def) == 0)
return 0;
if (((dev = strtok(buff, "," )) != NULL) &&
((drv = strtok(NULL, ", ")) != NULL) &&
((out = strtok(NULL, ", ")) != NULL))
{
if (device) strcpy(device, dev);
if (driver) strcpy(driver, drv);
if (output) strcpy(output, out);
return 1;
}
return 0;
}
#define dmDeviceNameSize 32
static HANDLE LoadCustomPrinterHDEVMODE(HWND hWnd, const char *filepath)
{
char device[MAX_PATH];
HANDLE hPrinter = NULL;
int sizeof_devmode;
HGLOBAL hDevMode = NULL;
DEVMODE *devmode = NULL;
int res;
FILE *fp = NULL;
int block_size;
int block_read;
if ((fp = fopen(filepath, "rb")) == NULL)
return NULL;
if (fread(device, 1, dmDeviceNameSize, fp) != dmDeviceNameSize)
goto err_exit;
if (!OpenPrinter(device, &hPrinter, NULL))
return NULL;
sizeof_devmode = (int)DocumentProperties(hWnd, hPrinter, device,
NULL, NULL, 0);
if (!sizeof_devmode)
goto err_exit;
hDevMode = GlobalAlloc(GHND, sizeof_devmode);
if (!hDevMode)
goto err_exit;
devmode = (DEVMODE*)GlobalLock(hDevMode);
if (!devmode)
goto err_exit;
res = DocumentProperties(hWnd, hPrinter, device, devmode, NULL,
DM_OUT_BUFFER);
if (res != IDOK)
goto err_exit;
block_size = devmode->dmSize + devmode->dmDriverExtra;
block_read = fread(devmode, 1, block_size, fp);
if (block_size != block_read)
goto err_exit;
fclose(fp);
res = DocumentProperties(hWnd, hPrinter, device, devmode, devmode,
DM_IN_BUFFER|DM_OUT_BUFFER);
if (res != IDOK)
goto err_exit;
GlobalUnlock(hDevMode);
ClosePrinter(hPrinter);
return hDevMode;
err_exit:
if (fp) fclose(fp);
if (devmode) GlobalUnlock(hDevMode);
if (hDevMode) GlobalFree(hDevMode);
if (hPrinter) ClosePrinter(hPrinter);
return NULL;
}
static int SaveCustomPrinterHDEVMODE(HWND hWnd, const char *filepath, HANDLE hDevMode)
{
FILE *fp = NULL;
NOREF(hWnd);
if ((fp = fopen(filepath, "wb")) != NULL)
{
DEVMODE *devmode = (DEVMODE*)GlobalLock(hDevMode);
int block_size = devmode->dmSize + devmode->dmDriverExtra;
int block_written;
char devname[dmDeviceNameSize];
strcpy(devname, (const char*)devmode->dmDeviceName);
fwrite(devname, 1, sizeof(devname), fp);
block_written = fwrite(devmode, 1, block_size, fp);
GlobalUnlock(hDevMode);
fclose(fp);
return block_size == block_written;
}
return 0;
}
static HDC GetCustomPrinterDC(HWND hWnd, const char *printcfg, int show)
{
global_pd.hwndOwner = hWnd;
global_pd.hDC = NULL;
global_pd.hDevNames = NULL;
if (global_pd.hDevMode == NULL)
{
global_pd.hDevMode = LoadCustomPrinterHDEVMODE(hWnd, printcfg);
}
if (show)
{
if (PrintDlg(&global_pd))
{
SaveCustomPrinterHDEVMODE(hWnd, printcfg, global_pd.hDevMode);
return global_pd.hDC;
}
return NULL;
}
{
DEVMODE *devmode = (DEVMODE*)GlobalLock(global_pd.hDevMode);
global_pd.hDC = CreateDC(NULL, (const char*)devmode->dmDeviceName, NULL, devmode);
GlobalUnlock(global_pd.hDevMode);
}
return global_pd.hDC;
}
static HDC GetDefaultPrinterDC(void)
{
char device[MAX_PATH],driver[MAX_PATH],output[MAX_PATH];
if (GetDefaultPrinterStrings(device, driver, output))
return CreateDC(driver, device, output, NULL);
return NULL;
}
static HDC GetPrinterDC(HWND hWnd, const char *printcfg, int show)
{
if (!printcfg) return GetDefaultPrinterDC();
return GetCustomPrinterDC(hWnd, printcfg, show);
}
static int IsBandingRequired(HDC hPrinter)
{
OSVERSIONINFO osvi;
int indata = NEXTBAND;
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if (GetVersionEx(&osvi) && (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT))
return Escape(hPrinter, QUERYESCSUPPORT, sizeof(int), (LPCSTR)&indata, NULL);
return 0;
}
int IsPrinterAvailable(void)
{
return (GetDefaultPrinterStrings(NULL, NULL, NULL) != 0);
}
const char *SurfacePrint(SDL_Surface *surf, const char *printcfg, int showdialog)
{
const char *res = NULL;
HWND hWnd;
DOCINFO di;
int nError;
SDL_SysWMinfo wminfo;
HDC hDCwindow;
HDC hDCprinter;
BITMAPINFOHEADER bmih;
SDL_Surface *surf24 = NULL;
RECT rc;
float fLogPelsX1, fLogPelsY1, fLogPelsX2, fLogPelsY2;
float fScaleX, fScaleY;
int cWidthPels, xLeft, yTop;
float subscaler,subscalerx,subscalery;
int hDCCaps;
HBITMAP hbm = NULL;
HDC hdcMem = NULL;
SDL_VERSION(&wminfo.version);
if (!SDL_GetWMInfo(&wminfo))
return "win32_print: SDL_GetWMInfo() failed.";
hWnd = wminfo.window;
hDCprinter = GetPrinterDC(hWnd, printcfg, showdialog);
if (!hDCprinter)
return "win32_print: GetPrinterDC() failed.";
EnableWindow(hWnd, FALSE);
di.cbSize = sizeof(DOCINFO);
di.lpszDocName = "Tux Paint";
di.lpszOutput = (LPTSTR)NULL;
di.lpszDatatype = (LPTSTR)NULL;
di.fwType = 0;
nError = StartDoc(hDCprinter, &di);
if (nError == SP_ERROR)
{
res = "win32_print: StartDoc() failed.";
goto error;
}
nError = StartPage(hDCprinter);
if (nError <= 0)
{
res = "win32_print: StartPage() failed.";
goto error;
}
//////////////////////////////////////////////////////////////////////////////////////
surf24 = make24bitDIB(surf);
if (!surf24)
{
res = "win32_print: make24bitDIB() failed.";
goto error;
}
memset(&bmih, 0, sizeof(bmih));
bmih.biSize = sizeof(bmih);
bmih.biPlanes = 1;
bmih.biCompression = BI_RGB;
bmih.biBitCount = 24;
bmih.biWidth = surf24->w;
bmih.biHeight = surf24->h;
GetClientRect(hWnd, &rc);
subscalerx = (float)rc.right/surf24->w;
subscalery = (float)rc.bottom/surf24->h;
subscaler = subscalery;
if (subscalerx < subscalery)
subscaler = subscalerx;
hDCwindow = GetDC(hWnd);
if (!hDCwindow)
{
res = "win32_print: failed to get window DC.";
goto error;
}
fLogPelsX1 = GetDeviceCaps(hDCwindow, LOGPIXELSX);
fLogPelsY1 = GetDeviceCaps(hDCwindow, LOGPIXELSY);
ReleaseDC(hWnd, hDCwindow);
fLogPelsX2 = GetDeviceCaps(hDCprinter, LOGPIXELSX);
fLogPelsY2 = GetDeviceCaps(hDCprinter, LOGPIXELSY);
if (fLogPelsX1 > fLogPelsX2)
fScaleX = (fLogPelsX1/fLogPelsX2);
else
fScaleX = (fLogPelsX2/fLogPelsX1);
if (fLogPelsY1 > fLogPelsY2)
fScaleY = (fLogPelsY1/fLogPelsY2);
else
fScaleY = (fLogPelsY2/fLogPelsY1);
fScaleX *= subscaler;
fScaleY *= subscaler;
yTop = 0;
cWidthPels = GetDeviceCaps(hDCprinter, PHYSICALWIDTH);
xLeft = ((cWidthPels - ((int)(fScaleX*bmih.biWidth)))/2)-
GetDeviceCaps(hDCprinter, PHYSICALOFFSETX);
hDCCaps = GetDeviceCaps(hDCprinter, RASTERCAPS);
if (hDCCaps & RC_PALETTE)
{
res = "win32_print: printer context requires palette.";
goto error;
}
if (IsBandingRequired(hDCprinter))
{
RECT rcBand = { 0, 0, 0, 0 };
RECT rcPrinter;
RECT rcImage;
SetRect(&rcPrinter, xLeft, yTop, (int)(fScaleX*bmih.biWidth), (int)(fScaleY*bmih.biHeight));
SetRect(&rcImage, 0, 0, bmih.biWidth, bmih.biHeight);
while (Escape(hDCprinter, NEXTBAND, 0, NULL, &rcBand))
{
if (IsRectEmpty(&rcBand)) break;
if (IntersectRect(&rcBand, &rcBand, &rcPrinter))
{
rcImage.top = (int)(0.5f+(float)rcBand.top/fScaleX);
rcImage.bottom = (int)(0.5f+(float)rcBand.bottom/fScaleX);
SetStretchBltMode(hDCprinter, COLORONCOLOR);
nError = StretchDIBits(hDCprinter, rcBand.left, rcBand.top,
rcBand.right - rcBand.left,
rcBand.bottom - rcBand.top,
rcImage.left, rcImage.top,
rcImage.right - rcImage.left,
rcImage.bottom - rcImage.top,
surf24->pixels, (BITMAPINFO*)&bmih,
DIB_RGB_COLORS, SRCCOPY);
if (nError == GDI_ERROR)
{
res = "win32_print: StretchDIBits() failed.";
goto error;
}
}
}
}
else
{
if (hDCCaps & RC_STRETCHDIB)
{
SetStretchBltMode(hDCprinter, COLORONCOLOR);
nError = StretchDIBits(hDCprinter, xLeft, yTop,
(int)(fScaleX*bmih.biWidth),
(int)(fScaleY*bmih.biHeight),
0, 0, bmih.biWidth, bmih.biHeight,
surf24->pixels, (BITMAPINFO*)&bmih,
DIB_RGB_COLORS, SRCCOPY);
if (nError == GDI_ERROR)
{
res = "win32_print: StretchDIBits() failed.";
goto error;
}
}
else
{
res = "win32_print: StretchDIBits() not available.";
goto error;
}
}
//////////////////////////////////////////////////////////////////////////////////////
nError = EndPage(hDCprinter);
if (nError <= 0)
{
res = "win32_print: EndPage() failed.";
goto error;
}
EndDoc(hDCprinter);
error:
if (hdcMem) DeleteDC(hdcMem);
if (hbm) DeleteObject(hbm);
if (surf24) SDL_FreeSurface(surf24);
EnableWindow(hWnd, TRUE);
DeleteDC(hDCprinter);
return res;
}
/*
Read access to Windows Registry
*/
static HRESULT ReadRegistry(const char *key, const char *option, char *value, int size)
{
LONG res;
HKEY hKey = NULL;
res = RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_READ, &hKey);
if (res != ERROR_SUCCESS)
goto err_exit;
res = RegQueryValueEx(hKey, option, NULL, NULL, (LPBYTE)value, (LPDWORD)&size);
if (res != ERROR_SUCCESS)
goto err_exit;
res = ERROR_SUCCESS;
err_exit:
if (hKey) RegCloseKey(hKey);
return HRESULT_FROM_WIN32(res);
}
/*
Removes a single '\' or '/' from end of path
*/
static char *remove_slash(char *path)
{
int len = strlen(path);
if (!len)
return path;
if (path[len-1] == '/' || path[len-1] == '\\')
path[len-1] = 0;
return path;
}
/*
Returns heap string containing default application data path.
Creates suffix subdirectory (only one level).
E.g. C:\Documents and Settings\jfp\Application Data\suffix
*/
char *GetDefaultSaveDir(const char *suffix)
{
char prefix[MAX_PATH];
char path[2*MAX_PATH];
const char *key = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
const char *option = "AppData";
HRESULT hr = S_OK;
if (SUCCEEDED(hr = ReadRegistry(key, option, prefix, sizeof(prefix))))
{
remove_slash(prefix);
snprintf(path, sizeof(path), "%s/%s", prefix, suffix);
_mkdir(path);
return strdup(path);
}
return strdup("userdata");
}