From 585e61636aaa2c69849a399b602c361745a22de8 Mon Sep 17 00:00:00 2001 From: snow flurry Date: Tue, 13 Feb 2024 21:00:46 +0000 Subject: [PATCH] Create project "Grabby". git-svn-id: svn://vcs.sdm.2ki.xyz/Grabby/trunk@9 27729192-006e-004d-b9b5-06fbd0ef7001 --- Bitmap.c | 352 ++++++++++++++++++++++++++++++++++++++++++++++++++ Bitmap.h | 18 +++ Grabby.c | 94 ++++++++++++++ Grabby.h | 5 + Grabby.ico | Bin 0 -> 23558 bytes Grabby.rc | 128 ++++++++++++++++++ Grabby.sln | 21 +++ Grabby.vcproj | 172 ++++++++++++++++++++++++ Overlay.c | 225 ++++++++++++++++++++++++++++++++ Overlay.h | 9 ++ ReadMe.txt | 53 ++++++++ Resource.h | 29 +++++ small.ico | Bin 0 -> 23558 bytes stdafx.c | 8 ++ stdafx.h | 19 +++ 15 files changed, 1133 insertions(+) create mode 100644 Bitmap.c create mode 100644 Bitmap.h create mode 100644 Grabby.c create mode 100644 Grabby.h create mode 100644 Grabby.ico create mode 100644 Grabby.rc create mode 100644 Grabby.sln create mode 100644 Grabby.vcproj create mode 100644 Overlay.c create mode 100644 Overlay.h create mode 100644 ReadMe.txt create mode 100644 Resource.h create mode 100644 small.ico create mode 100644 stdafx.c create mode 100644 stdafx.h diff --git a/Bitmap.c b/Bitmap.c new file mode 100644 index 0000000..d2c9996 --- /dev/null +++ b/Bitmap.c @@ -0,0 +1,352 @@ +#include "stdafx.h" +#include "Bitmap.h" + +void DeleteScreen(Screen *); + +Screen * +CreateScreen(void) +{ + Screen *scrn = GlobalAlloc(GPTR, sizeof(Screen)); + RECT rectScreen = { 0 }; + + if (!scrn) { + return NULL; + } + + scrn->hdcScreen = CreateDC("DISPLAY", NULL, NULL, NULL); + scrn->hdcBitmap = CreateCompatibleDC(scrn->hdcScreen); + + rectScreen.right = GetDeviceCaps(scrn->hdcScreen, HORZRES); + rectScreen.bottom = GetDeviceCaps(scrn->hdcScreen, VERTRES); + + scrn->hScreenRgn = CreateRectRgnIndirect(&rectScreen); + if (!scrn->hScreenRgn) { + // Store the error to be bubbled up + DWORD err = GetLastError(); + DeleteScreen(scrn); + SetLastError(err); + + return NULL; + } + + return scrn; +} + +void +DeleteScreen(Screen *scrn) +{ + if (scrn) { + if (scrn->hScreenRgn) DeleteObject((HGDIOBJ) scrn->hScreenRgn); + if (scrn->hdcBitmap) DeleteDC(scrn->hdcBitmap); + if (scrn->hdcScreen) DeleteDC(scrn->hdcScreen); + + GlobalFree(scrn); + } + + return; +} + +#include +typedef struct _FULLBMPHDR { + BITMAPFILEHEADER file; + BITMAPINFOHEADER info; +} FULLBMPHDR, *PFULLBMPHDR; +#include + +PFULLBMPHDR +GenerateBitmapInfo(Screen *scrn, + BITMAP *bmp) +{ + PFULLBMPHDR pBmpHdr = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(struct _FULLBMPHDR)); + RECT rcScrn = {0}; + WORD cClrBits = (WORD)(bmp->bmPlanes * bmp->bmBitsPixel); + DWORD hdrSize; + + if (!pBmpHdr) return NULL; + + if (!GetRgnBox(scrn->hScreenRgn, &rcScrn)) { + return NULL; + } + + if (cClrBits <= 16) { + cClrBits = 16; + } else { + cClrBits = 32; + } + + pBmpHdr->info.biSize = sizeof(BITMAPINFOHEADER); + pBmpHdr->info.biWidth = bmp->bmWidth; + pBmpHdr->info.biHeight = bmp->bmHeight; + pBmpHdr->info.biPlanes = bmp->bmPlanes; + pBmpHdr->info.biBitCount = cClrBits; + pBmpHdr->info.biCompression = BI_RGB; + // According to MSDN docs, this alignment to WORD boundaries is + // required for Win9x GDI. + pBmpHdr->info.biSizeImage = (((bmp->bmWidth * cClrBits + 31) & ~31) >> 3) + * bmp->bmHeight; + + pBmpHdr->file.bfType = 0x4d42; // "BM" + // actual bits offset: file header + info header + colors used + hdrSize = (DWORD) (sizeof(BITMAPFILEHEADER) + + pBmpHdr->info.biSize + + ((cClrBits >= 16) ? 0 : (pBmpHdr->info.biClrUsed * + sizeof(RGBQUAD)))); + // file size is header size (offset above) + bits size + pBmpHdr->file.bfSize = hdrSize + pBmpHdr->info.biSizeImage; + pBmpHdr->file.bfOffBits = hdrSize; + + return pBmpHdr; +} + +int +SnapScreen(Screen *scrn) +{ + RECT rcScreen = {0}; + int iScrWidth, + iScrHeight; + if (!scrn) return FALSE; + + // Don't make a new hBitmap if we have one + if (scrn->hBitmap) return TRUE; + + GetRgnBox(scrn->hScreenRgn, &rcScreen); + + iScrWidth = rcScreen.right - rcScreen.left; + iScrHeight = rcScreen.bottom - rcScreen.top; + + scrn->hBitmap = CreateCompatibleBitmap(scrn->hdcScreen, + iScrWidth, + iScrHeight); + if (!scrn->hBitmap) return FALSE; + + scrn->hOldObj = SelectObject(scrn->hdcBitmap, scrn->hBitmap); + if (!scrn->hOldObj) { + DWORD err = GetLastError(); + DeleteObject(scrn->hBitmap); + scrn->hBitmap = NULL; + SetLastError(err); + return FALSE; + } + + if (!BitBlt(scrn->hdcBitmap, + 0, 0, + iScrWidth, + iScrHeight, + scrn->hdcScreen, + rcScreen.left, rcScreen.top, + SRCCOPY)) + { + DWORD err = GetLastError(); + SelectObject(scrn->hdcBitmap, scrn->hOldObj); + DeleteObject(scrn->hBitmap); + scrn->hBitmap = NULL; + SetLastError(err); + + return FALSE; + } + + return TRUE; +} + +int +WriteRegionToFile(Screen *scrn, + RECT *rgn, + LPTSTR fname) +{ + BITMAP bmp = {0}; // hBitmap's metadata + HANDLE hOutFile = NULL; // Output .bmp + LPBYTE lpBits = NULL; // Actual bitmap bits + + HDC hdcRgn = NULL; // HDC for defined region + HBITMAP hRgnBitmap = NULL; // Bitmap of defined region + HGDIOBJ hOldObj = NULL; // Old bitmap, if any + PFULLBMPHDR pHdr = NULL; // Full bitmap header + PBITMAPINFO pbmi = NULL; // BMI header, in case bits <= 8 + + WORD cClrBits = 0; // How many bits per pixel + + BOOL hasErr = FALSE; // If something went wrong, set this + DWORD err = 0, // Used to store LastError + dwTmp = 0; // Used for written bytes storage + + if (!scrn || (!rgn || !fname)) { + SetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + + if (!RectInRegion(scrn->hScreenRgn, rgn)) { + // TODO: is there a better error we can set here? + SetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + + // Overlay already grabs the screen, so don't trample over it + if (!scrn->hBitmap && !SnapScreen(scrn)) + { + hasErr = TRUE; + goto cleanup; + } + + hdcRgn = CreateCompatibleDC(scrn->hdcScreen); + if (!hdcRgn) { + hasErr = TRUE; + goto cleanup; + } + + // Create a new bitmap for the specific region + hRgnBitmap = CreateCompatibleBitmap(scrn->hdcScreen, + rgn->right - rgn->left, + rgn->bottom - rgn->top); + if (!hRgnBitmap) { + hasErr = TRUE; + goto cleanup; + } + + + if (!GetObject(hRgnBitmap, sizeof(BITMAP), &bmp)) { + hasErr = TRUE; + goto cleanup; + } + + hOldObj = SelectObject(hdcRgn, hRgnBitmap); + if (!hOldObj) { + hasErr = TRUE; + goto cleanup; + } + + if (!BitBlt( + hdcRgn, + 0, 0, + rgn->right - rgn->left, + rgn->bottom - rgn->top, + scrn->hdcBitmap, + rgn->left, rgn->top, + SRCCOPY)) + { + hasErr = TRUE; + goto cleanup; + } + + pHdr = GenerateBitmapInfo(scrn, + &bmp); + if (!pHdr) { + hasErr = TRUE; + goto cleanup; + } + + lpBits = HeapAlloc(GetProcessHeap(), + 0, + pHdr->info.biSizeImage); + if (!lpBits) { + hasErr = TRUE; + goto cleanup; + } + + if (pHdr->info.biBitCount <= 8) { + pbmi = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(BITMAPINFOHEADER) + + (pHdr->info.biClrUsed * sizeof(RGBQUAD))); + if (!pbmi) { + hasErr = TRUE; + goto cleanup; + } + CopyMemory(&pbmi->bmiHeader, &pHdr->info, sizeof(BITMAPINFOHEADER)); + } else { + // For all other bit sizes, we don't care about the color + // table. Just use the info header directly. + // (Yes this is pretty shady. Don't worry about it) + pbmi = (PBITMAPINFO) &pHdr->info; + } + + if (!GetDIBits(scrn->hdcBitmap, + hRgnBitmap, + 0, + (WORD) pHdr->info.biHeight, + lpBits, + pbmi, + DIB_RGB_COLORS)) + { + hasErr = TRUE; + goto cleanup; + } + + // Create and write to the output file + + hOutFile = CreateFile(fname, + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hOutFile == INVALID_HANDLE_VALUE) { + hasErr = TRUE; + goto cleanup; + } + + // First, write the header + if (!WriteFile(hOutFile, pHdr, sizeof(FULLBMPHDR), &dwTmp, NULL)) { + hasErr = TRUE; + goto cleanup; + } + + // Assuming we're only using 16- or 32-bit BMPs, we should never + // need to write a color table. But since this could easily be + // modified to support smaller bit widths (i.e., 256 or less), + // let's make sure such cases don't crash. + if (pHdr->info.biBitCount <= 8) { + if (!WriteFile(hOutFile, + pbmi, + pbmi->bmiHeader.biClrUsed * sizeof(RGBQUAD), + &dwTmp, + NULL)) + { + hasErr = TRUE; + goto cleanup; + } + } + + // And last but not least, the pixels + if (!WriteFile(hOutFile, lpBits, pHdr->info.biSizeImage, &dwTmp, NULL)) { + hasErr = TRUE; + goto cleanup; + } + +cleanup: + // make sure to grab the error before we start freeing everything + if (hasErr) { + err = GetLastError(); + } + + if (hOutFile) { + CloseHandle(hOutFile); + if (hasErr) { + // Since it's probably busted, try to delete it too + DeleteFile(fname); + } + } + + if (lpBits) HeapFree(GetProcessHeap(), 0, lpBits); + + // In case our bits are <= 8, pbmi will be an actual heap-alloc'd + // pointer. Let's make sure to handle that. + if (pbmi && pbmi != (PBITMAPINFO) &pHdr->info) HeapFree(GetProcessHeap(), 0, pbmi); + + if (pHdr) HeapFree(GetProcessHeap(), 0, pHdr); + + if (scrn->hOldObj) SelectObject(scrn->hdcBitmap, scrn->hOldObj); + if (hOldObj) SelectObject(hdcRgn, hOldObj); + if (hRgnBitmap) DeleteObject(hRgnBitmap); + if (scrn->hBitmap) DeleteObject(scrn->hBitmap); + + if (hdcRgn) DeleteDC(hdcRgn); + + if (hasErr) { + SetLastError(err); + } + + return hasErr ? -1 : 0; +} \ No newline at end of file diff --git a/Bitmap.h b/Bitmap.h new file mode 100644 index 0000000..99b2ce1 --- /dev/null +++ b/Bitmap.h @@ -0,0 +1,18 @@ +#pragma once + +#include "stdafx.h" + +typedef struct _Screen { + HDC hdcScreen; + HDC hdcBitmap; + HRGN hScreenRgn; + HBITMAP hBitmap; + HGDIOBJ hOldObj; +} Screen; + +// Create a Screen struct. +Screen *CreateScreen(void); +// Write a screen region to the given filename. +int WriteRegionToFile(Screen *, RECT *, LPTSTR); +// Commit a "screenshot" of the screen to scrn->hBitmap +int SnapScreen(Screen *); \ No newline at end of file diff --git a/Grabby.c b/Grabby.c new file mode 100644 index 0000000..003e13e --- /dev/null +++ b/Grabby.c @@ -0,0 +1,94 @@ +// Grabby.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" +#include "Bitmap.h" +#include "Overlay.h" +#include "Grabby.h" + +/** + * Print an error with a given context before bailing out + */ +void Die(LPTSTR lpContextMsg) +{ + LPCTSTR lpszFmt = "%s: %s"; + int iFullLen; + LPTSTR lpszError = NULL, + lpszFullMsg = NULL; + + // The whole point of this is to print an actual message, so get the + // actual message + if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + 0, + (LPTSTR) &lpszError, + 0, + NULL)) + { + if (lpszError != NULL) LocalFree(lpszError); + lpszError = _T("unknown error"); + } + + iFullLen = _sctprintf(lpszFmt, lpContextMsg, lpszError); + lpszFullMsg = (LPTSTR) LocalAlloc(LPTR, sizeof(TCHAR) * (iFullLen + 1)); + + if (lpszFullMsg == NULL) { + // I'm honestly gonna guess if we get here, we're out of memory. + // So let's print what we have and bail + MessageBox(NULL, + lpContextMsg, + _T("Catastrophic Error"), + MB_OK | MB_ICONERROR); + } else { + _sntprintf(lpszFullMsg, iFullLen, lpszFmt, lpContextMsg, lpszError); + MessageBox(NULL, + lpszFullMsg, + _T("Fatal Error"), + MB_OK | MB_ICONERROR); + } + + // TODO: should we still be nice and free memory here? + ExitProcess(1); +} + +int APIENTRY _tWinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) +{ + RECT capRegion = {0}; + Screen *scrn = CreateScreen(); + + if (!scrn) { + Die("Couldn't get screen information"); + } + + switch (CreateOverlay(hInstance, scrn)) { + case -1: + Die("Couldn't create overlay window"); + break; + case 0: + /* Do nothing */ + break; + case 1: + // User requested we cancel out, so do that + return 0; + } + + GetChosenRect(&capRegion); + + + if (WriteRegionToFile(scrn, &capRegion, _T("test.bmp")) == -1) { + Die("Couldn't grab region of the screen"); + } + + MessageBox(NULL, + _T("Successfully grabbed a part of the screen!"), + _T("\\o/"), + MB_OK | MB_ICONINFORMATION); + + return 0; +} \ No newline at end of file diff --git a/Grabby.h b/Grabby.h new file mode 100644 index 0000000..99b2cd0 --- /dev/null +++ b/Grabby.h @@ -0,0 +1,5 @@ +#pragma once + +#include "resource.h" + +void Die(LPTSTR); \ No newline at end of file diff --git a/Grabby.ico b/Grabby.ico new file mode 100644 index 0000000000000000000000000000000000000000..d551aa3aaf80adf9b7760e2eb8de95a5c3e53df6 GIT binary patch literal 23558 zcmeI430zgx+QuJHKtxbe5gbu*030B5$VyGcDGSFOalkY&2LuvC5pp(7&2XNl96=@z zNXGH2`|DO#nx)3nwUq43A>_N=+wHsYe$U#6ePmShD&p^B>2uySylbs@uYIPy&-w#c zpc-6UYC)x+ErDgUwQ8BlZ7hIURRB*7exZ#T}AXG2* z=^weGTI5~Inq#r?3QZRh5>Vvy7AqDy*^i;1p6BY7;LQSXZ{;g>M z?fm5AM!1uJ~14CP5-;mbWJGeF0 z_iurN!(6GBI54yo4h(CB{j~e(6Em$hj*V=Fqpvo{5$e#07L+U2`wvFkn8s8S#Efo= z^|!}o{tozLT1|Z7UlaSMxZ(5FgK^Rilm(Khv|vko7i5X}36?lI))Ggklas69 zVxSe$=33+10BfA^v%)uXY;b;dHGCaV4e6oPadwt1PEE7L#SjO4G`kKy33kG#^P1yK zcx(J^Ra<Ti+?95-JJvGIWK0JnTs;vs^DcXy)=jK$w z=lme~e0CM~SM61i7E+Zy6!Vv8(?YCpX|5H%3$bS21{dbq;8I96Tne>C8jm-9o*mM| z?2r~#1K&~U^BwT@ygK+I#1UDG8sIO%&iE*}A+E1$jbGNa!S(fRas9ovxba>)TBY{5 zxxo`Rq9|oIDtY0?rjE#1t!!u9+}s5>w|2#i&D55z%y+}h?JrQ>af9~O4zA^n9=Nr$ z7jEt9gPXg&@$23JxV49(y|Q~4emOiI-)H_6dH=qKoBYhlq5e+&PW_AegZf|U-_)N} z9@RJC3MS7vp?yXL1qC4>AOQaU{+Kjr5++WZhzS!Wz}MFoW5Wxo&I+1!G$zZHn#$;`!98-<yjHIyy#~ zd!^|5sm6LSF)_!K%8;V#rWzZU(N_%@(#Q5Ewg{KRHI95 zY?=LIo2D9@#Ky*zb^O>SmHu~IE44l?Dgh-;K81z)WLJ`;4wqn z_ZrZ%LmzL?wy3kD_lL%jZ@l`n*YIJJ=8o?=KVm^dc=tK8XTNSrUK1xwofb5!|4WPJ z4;&O=5uecStt8`&$o&U)@7lX>*XEsj-g|fBj_upFZrx%^n^vq{{r0M5OP8-%`Odni z4ek1_pUw~WS3(xf3w~KkBmDdVRSL~dfr0)bOf7sI@n%@?lm1=c0pd4Z&T02Hm@RH2 z)we;5{I7(S*0d0%twR;wLsA|##n-X4buN70s`TsBg@MbpxknH6!QPjfV-K~P+VA6v z_lLE?{$Xwi?eB?&gE}IlpC>|?5A<%2&;edpIl33d4IhkA?7Qcs#@NdnYWsbf({dao zjuAS*69M!eGt37G)4CyX#*2ub-V>ij1>vuo!mzs+z)KgL@b7{zHqOE48v-$!zJ3#Y zv6uJbc6$T6dQ*KU=65px!K_Y5n$a2Cr*_9zn`Ys&O+gqt+y{pT0q+l>1_JwOKM87w zj|1D|zXCjwI@=4Ewok|DRTFSw+Z#B)bq3CDnTav%mol33yacQq;D9qB?)YqOTV(8< zhO{02IO`82u>Hs|UYpK$#ksIn_%f8&v3sW=YtK}ip9y^Z1~r3H`B~I#;2iDQ=@jeE zsP;Kl_%^%|E=9QF`(^IPTIr6TH*`S`ui5^ww+}9?dJfr}dg8{OA;>xEhiiu?LYUzwb+T)8Ci=PAZtkjWKvm68X{|HBivlm3|Y&X;^sP6+GhB5eJk92w>5I2 z+$j(Ix}hC1827D>9dK(?2jp()h@8zG@!QT$$l2N%x3+e|?QJ|JOre?J8PhnJ%Ni~CLrzWB&44|iS%zyB8@if zn`DaR3m@|O^QyPhwX#dzrgIKY+OQIBHLeiIw|EP z&VT0+jvL~&)rdRJe}-vnAIJ6*Q-ZDH1N-*w-gRv2&ZLw99b3D3xO=#{xw*T!wQ+Oz@bGBcd0?|n&$#sN_2S8-lrFX#RqEa{~iIg60Iwp0)kazxeJo zgX#N&>G3k(9Zpk`k46?8yGp_NR9<~gx%0b2>EBc6h6N*s;*a0{2Wy6O#7ZA8q(u55 zXmAg#9`ZC+QBk9x#nSQpa4CKpR!sCp#>stnXRBl-)qQFW^fsryy=(Z?FI2AS<5;lV$HB*W zpm$$$hhFu3THa~z+qYL;AE$u>2QZl)2G;Ru)3f^vUAny3rOUHDp6~jct50i}CXE|6 zZPK7&qvp+?vT*b1+^M5y`wmZgdAPT0`%H^xiXL6DvWOu*60xx;u6V#Q2{0r8adCy( zEn;IuV&g28p4jI>W#CW53OF&!CsAr~RottogHM>&s@S>DKq|7h|3SD9 zqF9XiYwfgmNUJRFhY%(1o6xLY)@?;QKJMM%9Zv1};>0~2!r#}0zp0zW`xNH9UeDj( zg}=XRQtjm}{_d~Eq+;bB6m$ICmr^L!lH$^jp`^CQQOEr>=J>f^rrg)^KRssd^D)QI zeLuo|80KTp^Sb>{=X%)v)pLRSmCW&T|B@EJinpT1Tyzb%m&zPJ_g4w`z?hFg`Rd1_ z>Wj7&9jm;{DmLy1Gsn+8Vp@!PtSTNouWWh8cdz+W{M_4Sj-PwjDs;R>k4LR3_uiS~ z=YBll{weJklr8FC(aI`*?jJPA&pn00ytW2@1pNNmFr)z)}MRaMZIsT^P*Jr zd{v~ficiI=V%Fb3xlf-prc}}2|5bcSDrP-?@&@_Qn~c8Rs-)*Df-M*%`H0H+%lZ72 zvi{EGQOr#h;dxS84CWx2AwMJBn{b$~fyU%&3N}@!=X}9qDHtRuG5tUm68j-~fkG1sqOUyGmYlwPgb z2OYaS`ssnHnDzL{f$7y1HvU2ZvOsRl96y=1qRkb)O#V)fzZuy)A>;K#iJYK%{YIx)`7mahDM1B1t%cm9kaZNYkD4X_DC9qd+$8->B5TQhB} zPLpFP(T5^y$$V8IA1dTRh5V#84>?gGBg(O=3b|S#mnh^Cg)FI%vsB;THmdl^aSGW> zA@3;U9fcgEkcSj)tKX)y|CMyJ9 zWMGAisgNZVGNwZIRLI7bES?uKuA0cIN->306SAtME58p}SdPK5N}H!(y?QQ$SPR)# zEw=cH;9p8myVEOE~ZJrY}3iIg?0rP&%LTBp=}8h@I%TXv<9-xUO`%}-uWt5a*E=2Z6^)Nip$4?6}mrb=W3r9pMm{N(?%I<=0f{ZX!iK0oKQ1d^EdG#^%`N>O4Lp#&)lc_BC`N?cbBh&ou z$Ha>#mE4>Z3XbJ2L!+Nt++W%XmzCnEDKwe#1XEVN#&9kX7z*Ba>aDt~p(O7d58 ztNMbLMIj4qo}V1Gs?t)?V|bWl{j*<9L>}8bKN)V*HyMT)&Xn7jpKpqbGz6zmVk@{(S%;moMb= zg`B=PIy$QPUCF}>xq2agFXZoq+`W*w*DN`FAuBIr%G&-D!IW`F9}` zFJ#_@jJ%MQmz-@~sV+i3UdYL7B1xFE+kg*rC_sn}}eaYVo*?J*YFZ>$;!oOJ{ z{QCgB-)1FF4i?imzkPZz{4Rvr{h7I>sgUu{%LsSK%b0JUml0-1RnN;GSP!(-+jpO%JopO`B((dnpK-(&yRaUJ6F; zchnE_k$Wv1f4{oG;*T$8Vx5|ss!Wf01@yO_$nuNBLZ4Gvb)Vu6x9f7RD3t3{RPFna z@~=**zWfUs8kYPPZCSL4e)B1xT|TXnSM+U>y|{O?8%m4vtzIr_BVKg5vCP}`*3dR} z&a!{N#n>%>kU18z!$Q_q$meQ#RW3=oZ=knFmg=8&V&`qOUg~p1N&lWwnpHmPb9YW3 zw+z)kIP(xwOMAJX5{|A*v__uZdtvV;w2rOkgeCCc1i z#a5Q%Amc3IgIa3+fBIm(x&OWTs_~Un|HxNN{coH$#m{POUDev^Dy>e{FMhe1Y5iiu zZ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Overlay.c b/Overlay.c new file mode 100644 index 0000000..9a60017 --- /dev/null +++ b/Overlay.c @@ -0,0 +1,225 @@ +#include "stdafx.h" +#include "Bitmap.h" + +LRESULT CALLBACK OverlayWndProc(HWND, UINT, WPARAM, LPARAM); + +#define OVERLAY_CLASS _T("GrabbyOverlay") +#define OVERLAY_TITLE _T("Grabby Overlay") + +// Static function to store and retrieve the overlay-chosen rect. +static void +SetGetRect(BOOL set, RECT *r) +{ + static RECT rcMain = {0}; + if (r) { + if (set) { + CopyMemory(&rcMain, r, sizeof(RECT)); + } else { + CopyMemory(r, &rcMain, sizeof(RECT)); + } + } + return; +} + +void +GetChosenRect(RECT *rcOut) +{ + SetGetRect(FALSE, rcOut); + return; +} + +int +CreateOverlay(HINSTANCE hInstance, + Screen *scrn) +{ + HWND hWnd = NULL; + RECT rcScreen = {0}; + MSG msg = {0}; + BOOL bRet; + WNDCLASSEX wcClass = { + sizeof(WNDCLASSEX), + CS_HREDRAW | CS_VREDRAW, + OverlayWndProc, + 0, + 0, + hInstance, + LoadIcon(NULL, IDI_APPLICATION), + LoadCursor(NULL, IDC_CROSS), + (HBRUSH)( COLOR_WINDOW + 1 ), + OVERLAY_TITLE, + OVERLAY_CLASS, + NULL + }; + + if (!RegisterClassEx(&wcClass)) { + return -1; + } + + if (!GetRgnBox(scrn->hScreenRgn, &rcScreen)) { + UnregisterClass(OVERLAY_CLASS, hInstance); + return -1; + } + + hWnd = CreateWindowEx( + WS_EX_TOPMOST, + OVERLAY_CLASS, + OVERLAY_TITLE, + WS_VISIBLE | WS_POPUP, + rcScreen.left, + rcScreen.top, + rcScreen.right - rcScreen.left, + rcScreen.bottom - rcScreen.top, + NULL, + NULL, + hInstance, + scrn); + + if (!hWnd) { + UnregisterClass(OVERLAY_CLASS, hInstance); + return -1; + } + + ShowWindow(hWnd, SW_SHOWNORMAL); + + while ((bRet = GetMessage(&msg, hWnd, 0, 0)) != 0) { + if (bRet == -1) { + // TODO: error handling + break; + } else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + if (hWnd) DestroyWindow(hWnd); + UnregisterClass(OVERLAY_CLASS, hInstance); + + return (int)msg.wParam; +} + +// Left, Top, Right, Bottom +#define POINT2RECT(a,b) { \ + min(a.x, b.x), \ + min(a.y, b.y), \ + max(a.x, b.x), \ + max(a.y, b.y) \ +} + +LRESULT CALLBACK +OverlayWndProc(HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + HDC hDC = NULL; + PAINTSTRUCT ps = {0}; + static POINTS ptStart = {0}, // Starting drag point + ptCur = {0}; // Current/end drag point + static BOOL isDrag = FALSE; // Are we mid-drag? + static Screen *scrn = NULL; // Screen data + static RECT rcScreen = {0}; + static HGDIOBJ hOldObj = NULL; + + switch (uMsg) { + case WM_CREATE: + { + HDC dcWnd = GetDC(hWnd); + // Get the Screen object passed in from CreateWindowEx + HBITMAP hOldBmp = NULL; + LPCREATESTRUCT csCreate = (LPCREATESTRUCT)lParam; + if (csCreate && csCreate->lpCreateParams) { + scrn = (Screen *)csCreate->lpCreateParams; + if (!scrn) { + // uhhhhhhhhh + MessageBox(hWnd, + _T("Couldn't get screen info for mysterious reasons !?"), + _T("Fatal Error"), + MB_OK | MB_ICONERROR); + PostQuitMessage(1); + return 1; + } + + GetRgnBox(scrn->hScreenRgn, &rcScreen); + + if (!SnapScreen(scrn)) { + MessageBox(hWnd, + _T("Couldn't store screen data !!"), + _T("Fatal Error"), + MB_OK | MB_ICONERROR); + PostQuitMessage(1); + return 1; + } + } + } + /* PASSTHRU */ + case WM_PAINT: + hDC = BeginPaint(hWnd, &ps); + + if (scrn->hBitmap) { + // Draw the "background" + if (!BitBlt(hDC, + ps.rcPaint.left, + ps.rcPaint.top, + ps.rcPaint.right - ps.rcPaint.left, + ps.rcPaint.bottom - ps.rcPaint.top, + scrn->hdcBitmap, + rcScreen.left + ps.rcPaint.left, + rcScreen.top + ps.rcPaint.top, + SRCCOPY)) + { + // walk it off, I guess... + OutputDebugString(_T("Failed to blit background to DC...")); + } + } + + if (isDrag && hDC) { + RECT rcOuter = POINT2RECT(ptStart, ptCur); + DrawFocusRect(hDC, &rcOuter); + } + + EndPaint(hWnd, &ps); + break; + case WM_MOUSEMOVE: + if (isDrag && (wParam & MK_LBUTTON)) { + POINTS ptPrev = ptCur; + ptCur = MAKEPOINTS(lParam); + if ((ptCur.x != ptStart.x) && + (ptCur.y != ptStart.y)) + { + RECT rcPrevDmg = POINT2RECT(ptStart, ptPrev); + // InvalidateRect(hWnd, &rcCurDmg, TRUE); + InvalidateRect(hWnd, &rcPrevDmg, TRUE); + } + + } + break; + case WM_LBUTTONDOWN: + if (!isDrag) { + // Start the drag operation and set the origin + ptStart = ptCur = MAKEPOINTS(lParam); + isDrag = TRUE; + } + break; + case WM_LBUTTONUP: + if (isDrag) { + // Done dragging, store the rect and get out of here + RECT rcFinal = POINT2RECT(ptStart, ptCur); + + SetGetRect(TRUE, &rcFinal); + isDrag = FALSE; + PostQuitMessage(0); + } + break; + case WM_KEYUP: + // TODO: Escape + break; + case WM_RBUTTONUP: + // We treat right click as Escape + PostQuitMessage(1); + break; + default: + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } + + return 0; +} \ No newline at end of file diff --git a/Overlay.h b/Overlay.h new file mode 100644 index 0000000..02ef611 --- /dev/null +++ b/Overlay.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Bitmap.h" + +// Gets a RECT representing the screen region the user wants. +void GetChosenRect(RECT *); + +// Creates the overlay window. +int CreateOverlay(HINSTANCE, Screen *); \ No newline at end of file diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000..0ea70af --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,53 @@ +======================================================================== + WIN32 APPLICATION : Grabby Project Overview +======================================================================== + +AppWizard has created this Grabby application for you. +This file contains a summary of what you will find in each of the files that +make up your Grabby application. + + +Grabby.vcproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +Grabby.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +AppWizard has created the following resources: + +Grabby.rc + This is a listing of all of the Microsoft Windows resources that the + program uses. It includes the icons, bitmaps, and cursors that are stored + in the RES subdirectory. This file can be directly edited in Microsoft + Visual C++. + +Resource.h + This is the standard header file, which defines new resource IDs. + Microsoft Visual C++ reads and updates this file. +Grabby.ico + This is an icon file, which is used as the application's icon (32x32). + This icon is included by the main resource file Grabby.rc. + +small.ico + This is an icon file, which contains a smaller version (16x16) + of the application's icon. This icon is included by the main resource + file Grabby.rc. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named Grabby.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/Resource.h b/Resource.h new file mode 100644 index 0000000..6bcd5f7 --- /dev/null +++ b/Resource.h @@ -0,0 +1,29 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Grabby.rc +// + +#define IDS_APP_TITLE 103 + +#define IDR_MAINFRAME 128 +#define IDD_GRABBY_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDI_GRABBY 107 +#define IDI_SMALL 108 +#define IDC_GRABBY 109 +#define IDC_MYICON 2 +#define IDC_STATIC -1 +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS + +#define _APS_NO_MFC 130 +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/small.ico b/small.ico new file mode 100644 index 0000000000000000000000000000000000000000..d551aa3aaf80adf9b7760e2eb8de95a5c3e53df6 GIT binary patch literal 23558 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 +// C RunTime Header Files +#include +#include +#include +#include +#include + +// TODO: reference additional headers your program requires here