From ec3eb0a18a85de636fc92091b236b7cffd0c2465 Mon Sep 17 00:00:00 2001 From: Ged Murphy Date: Tue, 30 May 2006 21:11:41 +0000 Subject: [PATCH] add a temp screenshot app in case I don't get time to stick it in win32k before 0.3.0 FF. svn path=/trunk/; revision=22121 --- reactos/base/applications/applications.rbuild | 6 + .../base/applications/screenshot/screenshot.c | 396 ++++++++++++++++++ .../base/applications/screenshot/screenshot.h | 15 + .../applications/screenshot/screenshot.rbuild | 14 + .../applications/screenshot/screenshot.rc | 10 + reactos/base/applications/screenshot/snap.ico | Bin 0 -> 29926 bytes 6 files changed, 441 insertions(+) create mode 100644 reactos/base/applications/screenshot/screenshot.c create mode 100644 reactos/base/applications/screenshot/screenshot.h create mode 100644 reactos/base/applications/screenshot/screenshot.rbuild create mode 100644 reactos/base/applications/screenshot/screenshot.rc create mode 100644 reactos/base/applications/screenshot/snap.ico diff --git a/reactos/base/applications/applications.rbuild b/reactos/base/applications/applications.rbuild index 95cd330026c..273ee1bba92 100644 --- a/reactos/base/applications/applications.rbuild +++ b/reactos/base/applications/applications.rbuild @@ -46,12 +46,18 @@ + + + + + + diff --git a/reactos/base/applications/screenshot/screenshot.c b/reactos/base/applications/screenshot/screenshot.c new file mode 100644 index 00000000000..9700fa121bf --- /dev/null +++ b/reactos/base/applications/screenshot/screenshot.c @@ -0,0 +1,396 @@ +#include "screenshot.h" + +/* + * Save a screenshot to file until the clipboard + * is ready to accept images. + */ + + +static VOID +GetError(VOID) +{ + LPVOID lpMsgBuf; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, + NULL ); + + MessageBox(NULL, + lpMsgBuf, + _T("Error!"), + MB_OK | MB_ICONERROR); + + LocalFree(lpMsgBuf); +} + + +static BOOL +DoWriteFile(PSCREENSHOT pScrSht, + LPTSTR pstrFileName) +{ + BITMAPFILEHEADER bmfh; + BOOL bSuccess; + DWORD dwBytesWritten; + HANDLE hFile; + //INT PalEntries; + + hFile = CreateFile(pstrFileName, + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + /* write the BITMAPFILEHEADER to file */ + bmfh.bfType = *(WORD *)"BM"; // 0x4D 0x42 + bmfh.bfReserved1 = 0; + bmfh.bfReserved2 = 0; + bSuccess = WriteFile(hFile, + &bmfh, + sizeof(bmfh), + &dwBytesWritten, + NULL); + if ((!bSuccess) || (dwBytesWritten < sizeof(bmfh))) + goto fail; + + /* write the BITMAPINFOHEADER to file */ + bSuccess = WriteFile(hFile, + &pScrSht->lpbi->bmiHeader, + sizeof(BITMAPINFOHEADER), + &dwBytesWritten, + NULL); + if ((!bSuccess) || (dwBytesWritten < sizeof(BITMAPINFOHEADER))) + goto fail; + + /* calculate the size of the pallete * / + if (pScrSht->lpbi->bmiHeader.biCompression == BI_BITFIELDS) + PalEntries = 3; + else + { + if (pScrSht->lpbi->bmiHeader.biBitCount <= 8) + PalEntries = (INT)(1 << pScrSht->lpbi->bmiHeader.biBitCount); + else + PalEntries = 0; + } + if (pScrSht->lpbi->bmiHeader.biClrUsed) + PalEntries = pScrSht->lpbi->bmiHeader.biClrUsed; + + / * write pallete to file * / + if (PalEntries != 0) + { + bSuccess = WriteFile(hFile, + &pScrSht->lpbi->bmiColors, + PalEntries * sizeof(RGBQUAD), + &dwBytesWritten, + NULL); + if ((!bSuccess) || (dwBytesWritten < PalEntries * sizeof(RGBQUAD))) + goto fail; + } +*/ + /* save the current file position at the bginning of the bitmap bits */ + bmfh.bfOffBits = SetFilePointer(hFile, 0, 0, FILE_CURRENT); + + /* write the bitmap bits to file */ + bSuccess = WriteFile(hFile, + pScrSht->lpvBits, + pScrSht->lpbi->bmiHeader.biSizeImage, + &dwBytesWritten, + NULL); + if ((!bSuccess) || (dwBytesWritten < pScrSht->lpbi->bmiHeader.biSizeImage)) + goto fail; + + /* save the current file position at the final file size */ + bmfh.bfSize = SetFilePointer(hFile, 0, 0, FILE_CURRENT); + + /* rewrite the updated file headers */ + SetFilePointer(hFile, 0, 0, FILE_BEGIN); + bSuccess = WriteFile(hFile, + &bmfh, + sizeof(bmfh), + &dwBytesWritten, + NULL); + if ((!bSuccess) || (dwBytesWritten < sizeof(bmfh))) + goto fail; + + return TRUE; + +fail: + GetError(); + if (hFile) CloseHandle(hFile); + DeleteFile(pstrFileName); + return FALSE; + +} + + +static BOOL +DoSaveFile(HWND hwnd, LPTSTR szFileName) +{ + OPENFILENAME ofn; + + static TCHAR Filter[] = _T("24 bit Bitmap (*.bmp,*.dib)\0*.bmp\0"); + + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hwnd; + ofn.nMaxFile = MAX_PATH; + ofn.nMaxFileTitle = MAX_PATH; + ofn.lpstrDefExt = _T("bmp"); + ofn.lpstrFilter = Filter; + ofn.lpstrFile = szFileName; + ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; + + if (GetSaveFileName(&ofn)) + return TRUE; + + if (CommDlgExtendedError() != CDERR_GENERALCODES) + MessageBox(NULL, _T("Save to file failed"), NULL, 0); + + return FALSE; +} + + +static BOOL +CaptureScreen(PSCREENSHOT pScrSht) +{ + HDC ScreenDC; + RECT rect; + + /* get window resolution */ + //pScrSht->Width = GetSystemMetrics(SM_CXSCREEN); + //pScrSht->Height = GetSystemMetrics(SM_CYSCREEN); + + GetWindowRect(pScrSht->hSelf, &rect); + pScrSht->Width = rect.right - rect.left; + pScrSht->Height = rect.bottom - rect.top; + + /* get a DC for the screen */ + if (!(ScreenDC = GetDC(pScrSht->hSelf))) + return FALSE; + + /* get a bitmap handle for the screen + * needed to convert to a DIB */ + pScrSht->hBitmap = CreateCompatibleBitmap(ScreenDC, + pScrSht->Width, + pScrSht->Height); + if (pScrSht->hBitmap == NULL) + { + GetError(); + ReleaseDC(pScrSht->hSelf, ScreenDC); + return FALSE; + } + + /* get a DC compatable with the screen DC */ + if (!(pScrSht->hDC = CreateCompatibleDC(ScreenDC))) + { + GetError(); + ReleaseDC(pScrSht->hSelf, ScreenDC); + return FALSE; + } + + /* select the bitmap into the DC */ + SelectObject(pScrSht->hDC, + pScrSht->hBitmap); + + /* copy the screen DC to the bitmap */ + BitBlt(pScrSht->hDC, + 0, + 0, + pScrSht->Width, + pScrSht->Height, + ScreenDC, + 0, + 0, + SRCCOPY); + + /* we're finished with the screen DC */ + ReleaseDC(pScrSht->hSelf, ScreenDC); + + return TRUE; +} + + +static BOOL +ConvertDDBtoDIB(PSCREENSHOT pScrSht) +{ + INT Ret; + BITMAP bitmap; + WORD cClrBits; + + +/* + / * can't call GetDIBits with hBitmap selected * / + //SelectObject(hDC, hOldBitmap); + + / * let GetDIBits fill the lpbi structure by passing NULL pointer * / + Ret = GetDIBits(hDC, + hBitmap, + 0, + Height, + NULL, + lpbi, + DIB_RGB_COLORS); + if (Ret == 0) + { + GetError(); + ReleaseDC(hwnd, hDC); + HeapFree(GetProcessHeap(), 0, lpbi); + return -1; + } +*/ + +//////////////////////////////////////////////////// + + if (!GetObjectW(pScrSht->hBitmap, + sizeof(BITMAP), + (LPTSTR)&bitmap)) + { + GetError(); + return FALSE; + } + + cClrBits = (WORD)(bitmap.bmPlanes * bitmap.bmBitsPixel); + if (cClrBits == 1) + cClrBits = 1; + else if (cClrBits <= 4) + cClrBits = 4; + else if (cClrBits <= 8) + cClrBits = 8; + else if (cClrBits <= 16) + cClrBits = 16; + else if (cClrBits <= 24) + cClrBits = 24; + else cClrBits = 32; + + if (cClrBits != 24) + pScrSht->lpbi = (PBITMAPINFO) HeapAlloc(GetProcessHeap(), + 0, + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << cClrBits)); + else + pScrSht->lpbi = (PBITMAPINFO) HeapAlloc(GetProcessHeap(), + 0, + sizeof(BITMAPINFOHEADER)); + + if (!pScrSht->lpbi) + { + GetError(); + return FALSE; + } + + pScrSht->lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pScrSht->lpbi->bmiHeader.biWidth = bitmap.bmWidth; + pScrSht->lpbi->bmiHeader.biHeight = bitmap.bmHeight; + pScrSht->lpbi->bmiHeader.biPlanes = bitmap.bmPlanes; + pScrSht->lpbi->bmiHeader.biBitCount = bitmap.bmBitsPixel; + + if (cClrBits < 24) + pScrSht->lpbi->bmiHeader.biClrUsed = (1 << cClrBits); + + pScrSht->lpbi->bmiHeader.biCompression = BI_RGB; + pScrSht->lpbi->bmiHeader.biSizeImage = ((pScrSht->lpbi->bmiHeader.biWidth * cClrBits +31) & ~31) /8 + * pScrSht->lpbi->bmiHeader.biHeight; + + pScrSht->lpbi->bmiHeader.biClrImportant = 0; + +////////////////////////////////////////////////////// + + /* reserve memory to hold the screen bitmap */ + pScrSht->lpvBits = HeapAlloc(GetProcessHeap(), + 0, + pScrSht->lpbi->bmiHeader.biSizeImage); + if (pScrSht->lpvBits == NULL) + { + GetError(); + return FALSE; + } + + /* convert the DDB to a DIB */ + Ret = GetDIBits(pScrSht->hDC, + pScrSht->hBitmap, + 0, + pScrSht->Height, + pScrSht->lpvBits, + pScrSht->lpbi, + DIB_RGB_COLORS); + if (Ret == 0) + { + GetError(); + return FALSE; + } + + return TRUE; + +} + + +// INT WINAPI GetScreenshot(BOOL bFullScreen) +int WINAPI WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + PSTR szCmdLine, + int iCmdShow) +{ + PSCREENSHOT pScrSht; + TCHAR szFileName[MAX_PATH] = _T(""); + + BOOL bFullScreen = TRUE; + + pScrSht = HeapAlloc(GetProcessHeap(), + 0, + sizeof(SCREENSHOT)); + + if (bFullScreen) + { + pScrSht->hSelf = GetDesktopWindow(); + } + else + { + pScrSht->hSelf = GetForegroundWindow(); + } + + if (pScrSht->hSelf == NULL) + return -1; + + if (CaptureScreen(pScrSht)) + { + /* convert the DDB image to DIB */ + if(ConvertDDBtoDIB(pScrSht)) + { + /* Get filename from user */ + if(DoSaveFile(pScrSht->hSelf, szFileName)) + { + /* build the headers and write to file */ + DoWriteFile(pScrSht, szFileName); + } + } + } + + /* cleanup */ + if (pScrSht->hSelf != NULL) + ReleaseDC(pScrSht->hSelf, pScrSht->hDC); + if (pScrSht->hBitmap != NULL) + DeleteObject(pScrSht->hBitmap); + if (pScrSht->lpbi != NULL) + HeapFree(GetProcessHeap(), + 0, + pScrSht->lpbi); + if (pScrSht->lpvBits != NULL) + HeapFree(GetProcessHeap(), + 0, + pScrSht->lpvBits); + if (pScrSht != NULL) + HeapFree(GetProcessHeap(), + 0, + pScrSht); + + return 0; +} diff --git a/reactos/base/applications/screenshot/screenshot.h b/reactos/base/applications/screenshot/screenshot.h new file mode 100644 index 00000000000..d44c9528afa --- /dev/null +++ b/reactos/base/applications/screenshot/screenshot.h @@ -0,0 +1,15 @@ +#include +#include + +typedef struct _SCREENSHOT +{ + HWND hSelf; + HDC hDC; + HBITMAP hBitmap; + LPBITMAPINFO lpbi; + LPVOID lpvBits; + INT Width; + INT Height; +} SCREENSHOT, *PSCREENSHOT; + +//INT WINAPI GetScreenshot(VOID); diff --git a/reactos/base/applications/screenshot/screenshot.rbuild b/reactos/base/applications/screenshot/screenshot.rbuild new file mode 100644 index 00000000000..53719c7e276 --- /dev/null +++ b/reactos/base/applications/screenshot/screenshot.rbuild @@ -0,0 +1,14 @@ + + . + + + + 0x501 + 0x0501 + kernel32 + user32 + gdi32 + comdlg32 + screenshot.c + screenshot.rc + diff --git a/reactos/base/applications/screenshot/screenshot.rc b/reactos/base/applications/screenshot/screenshot.rc new file mode 100644 index 00000000000..474a3daf6cb --- /dev/null +++ b/reactos/base/applications/screenshot/screenshot.rc @@ -0,0 +1,10 @@ +#include + +#define REACTOS_STR_FILE_DESCRIPTION "desktop screenshot app\0" +#define REACTOS_STR_INTERNAL_NAME "screenshot\0" +#define REACTOS_STR_ORIGINAL_FILENAME "screenshot.exe\0" +#include + +#define IDI_ICON 1 + +IDI_ICON ICON "snap.ico" diff --git a/reactos/base/applications/screenshot/snap.ico b/reactos/base/applications/screenshot/snap.ico new file mode 100644 index 0000000000000000000000000000000000000000..2d0a1a67b3bd9a8115d4830f0b4934fe45fe8ec7 GIT binary patch literal 29926 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 literal 0 HcmV?d00001