summaryrefslogtreecommitdiffstats
path: root/plug-ins/screenshot/screenshot-win32.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/screenshot/screenshot-win32.c')
-rw-r--r--plug-ins/screenshot/screenshot-win32.c1278
1 files changed, 1278 insertions, 0 deletions
diff --git a/plug-ins/screenshot/screenshot-win32.c b/plug-ins/screenshot/screenshot-win32.c
new file mode 100644
index 0000000..a9dd831
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-win32.c
@@ -0,0 +1,1278 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Screenshot plug-in
+ * Copyright 1998-2007 Sven Neumann <sven@gimp.org>
+ * Copyright 2003 Henrik Brix Andersen <brix@gimp.org>
+ * Copyright 2012 Simone Karin Lehmann - OS X patches
+ * Copyright 2018 Gil Eliyahu <gileli121@gmail.com>
+ *
+ * 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 3 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#ifdef G_OS_WIN32
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Necessary in order to have SetProcessDPIAware() defined.
+ * This value of _WIN32_WINNT corresponds to Windows 7, which is our
+ * minimum supported platform.
+ */
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0601
+#include <windows.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "screenshot.h"
+#include "screenshot-win32.h"
+#include "screenshot-win32-resource.h"
+
+#include "libgimp/stdplugins-intl.h"
+#include "screenshot-win32-magnification-api.h"
+#include "screenshot-win32-dwm-api.h"
+
+/*
+ * Application definitions
+ */
+#define SELECT_FRAME 0
+#define SELECT_CLIENT 1
+#define SELECT_WINDOW 2
+
+#define SHOW_WINDOW FALSE
+#define APP_NAME "plug_in_screenshot_win"
+#define WM_DOCAPTURE (WM_USER + 100)
+
+/* Prototypes */
+void setCaptureType (int capType);
+BOOL InitApplication (HINSTANCE hInstance);
+BOOL InitInstance (HINSTANCE hInstance,
+ int nCmdShow);
+int winsnapWinMain (void);
+
+/* File variables */
+static int captureType;
+static char buffer[512];
+static guchar *capBytes = NULL;
+static HWND mainHwnd = NULL;
+static HINSTANCE hInst = NULL;
+static HCURSOR selectCursor = 0;
+static ICONINFO iconInfo;
+static MAGIMAGEHEADER returnedSrcheader;
+static int rectScreensCount = 0;
+
+static gint32 *image_id;
+
+static void sendBMPToGimp (HBITMAP hBMP,
+ HDC hDC,
+ RECT rect);
+static int doWindowCapture (void);
+static gboolean doCapture (HWND selectedHwnd);
+static BOOL isWindowIsAboveCaptureRegion (HWND hwndWindow,
+ RECT rectCapture);
+static gboolean doCaptureMagnificationAPI (HWND selectedHwnd,
+ RECT rect);
+static void doCaptureMagnificationAPI_callback (HWND hwnd,
+ void *srcdata,
+ MAGIMAGEHEADER srcheader,
+ void *destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty);
+static gboolean doCaptureBitBlt (HWND selectedHwnd,
+ RECT rect);
+
+BOOL CALLBACK dialogProc (HWND, UINT, WPARAM, LPARAM);
+LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
+
+/* Data structure holding data between runs */
+typedef struct {
+ gint root;
+ guint delay;
+ gint decor;
+} WinSnapValues;
+
+/* Default WinSnap values */
+static WinSnapValues winsnapvals =
+{
+ FALSE,
+ 0,
+ TRUE,
+};
+
+/* The dialog information */
+typedef struct {
+#ifdef CAN_SET_DECOR
+ GtkWidget *decor_button;
+#endif
+ GtkWidget *single_button;
+ GtkWidget *root_button;
+ GtkWidget *delay_spinner;
+} WinSnapInterface;
+
+/* We create a DIB section to hold the grabbed area. The scanlines in
+ * DIB sections are aligned on a LONG (four byte) boundary. Its pixel
+ * data is in RGB (BGR actually) format, three bytes per pixel.
+ *
+ * GIMP uses no alignment for its pixel regions. The GIMP image we
+ * create is of type RGB, i.e. three bytes per pixel, too. Thus in
+ * order to be able to quickly transfer all of the image at a time, we
+ * must use a DIB section and pixel region the scanline width in
+ * bytes of which is evenly divisible with both 3 and 4. I.e. it must
+ * be a multiple of 12 bytes, or in pixels, a multiple of four pixels.
+ */
+
+#define ROUND4(width) (((width-1)/4+1)*4)
+
+
+gboolean
+screenshot_win32_available (void)
+{
+ return TRUE;
+}
+
+ScreenshotCapabilities
+screenshot_win32_get_capabilities (void)
+{
+ return (SCREENSHOT_CAN_SHOOT_DECORATIONS |
+ SCREENSHOT_CAN_SHOOT_WINDOW);
+}
+
+GimpPDBStatusType
+screenshot_win32_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error)
+{
+ GimpPDBStatusType status = GIMP_PDB_EXECUTION_ERROR;
+
+ /* leave "shootvals->monitor" alone until somebody patches the code
+ * to be able to get a monitor's color profile
+ */
+
+ image_id = image_ID;
+
+ winsnapvals.delay = shootvals->screenshot_delay;
+
+ if (shootvals->shoot_type == SHOOT_ROOT)
+ {
+ doCapture (0);
+
+ status = GIMP_PDB_SUCCESS;
+ }
+ else if (shootvals->shoot_type == SHOOT_WINDOW)
+ {
+ status = 0 == doWindowCapture () ? GIMP_PDB_CANCEL : GIMP_PDB_SUCCESS;
+ }
+ else if (shootvals->shoot_type == SHOOT_REGION)
+ {
+ /* FIXME */
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GimpColorProfile *profile;
+
+ /* XXX No idea if the "monitor" value is right at all, especially
+ * considering above comment. Just make so that it at least
+ * compiles!
+ */
+ profile = gimp_screen_get_color_profile (screen, shootvals->monitor);
+
+ if (profile)
+ {
+ gimp_image_set_color_profile (*image_ID, profile);
+ g_object_unref (profile);
+ }
+ }
+
+ return status;
+}
+
+
+
+
+
+/******************************************************************
+ * GIMP format and transfer functions
+ ******************************************************************/
+
+/*
+ * flipRedAndBlueBytes
+ *
+ * Microsoft has chosen to provide us a very nice (not!)
+ * interface for retrieving bitmap bits. DIBSections have
+ * RGB information as BGR instead. So, we have to swap
+ * the silly red and blue bytes before sending to the
+ * GIMP.
+ */
+static void
+flipRedAndBlueBytes (int width,
+ int height)
+{
+ int i, j;
+ guchar *bufp;
+ guchar temp;
+
+ j = 0;
+ while (j < height) {
+ i = width;
+ bufp = capBytes + j*ROUND4(width)*3;
+ while (i--) {
+ temp = bufp[2];
+ bufp[2] = bufp[0];
+ bufp[0] = temp;
+ bufp += 3;
+ }
+ j++;
+ }
+}
+
+/*
+ * rgbaToRgbBytes
+ *
+ * Convert rgba array to rgb array
+ */
+static void
+rgbaToRgbBytes (guchar *rgbBufp,
+ guchar *rgbaBufp,
+ int rgbaBufSize)
+{
+ int rgbPoint = 0, rgbaPoint;
+
+ for (rgbaPoint = 0; rgbaPoint < rgbaBufSize; rgbaPoint += 4)
+ {
+ rgbBufp[rgbPoint++] = rgbaBufp[rgbaPoint];
+ rgbBufp[rgbPoint++] = rgbaBufp[rgbaPoint + 1];
+ rgbBufp[rgbPoint++] = rgbaBufp[rgbaPoint + 2];
+ }
+}
+
+/*
+ * sendBMPToGIMP
+ *
+ * Take the captured data and send it across
+ * to GIMP.
+ */
+static void
+sendBMPToGimp (HBITMAP hBMP,
+ HDC hDC,
+ RECT rect)
+{
+ int width, height;
+ int imageType, layerType;
+ gint32 new_image_id;
+ gint32 layer_id;
+ GeglBuffer *buffer;
+ GeglRectangle *rectangle;
+
+ /* Our width and height */
+ width = (rect.right - rect.left);
+ height = (rect.bottom - rect.top);
+
+ /* Check that we got the memory */
+ if (!capBytes)
+ {
+ g_message (_("No data captured"));
+ return;
+ }
+
+ /* Flip the red and blue bytes */
+ flipRedAndBlueBytes (width, height);
+
+ /* Set up the image and layer types */
+ imageType = GIMP_RGB;
+ layerType = GIMP_RGB_IMAGE;
+
+ /* Create the GIMP image and layers */
+ new_image_id = gimp_image_new (width, height, imageType);
+ layer_id = gimp_layer_new (new_image_id, _("Background"),
+ ROUND4 (width), height,
+ layerType,
+ 100,
+ gimp_image_get_default_new_layer_mode (new_image_id));
+ gimp_image_insert_layer (new_image_id, layer_id, -1, 0);
+
+ /* make rectangle */
+ rectangle = g_new (GeglRectangle, 1);
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = ROUND4(width);
+ rectangle->height = height;
+
+ /* get the buffer */
+ buffer = gimp_drawable_get_buffer (layer_id);
+
+ /* fill the buffer */
+ gegl_buffer_set (buffer, rectangle, 0, NULL, (guchar *) capBytes,
+ GEGL_AUTO_ROWSTRIDE);
+
+ /* flushing data */
+ gegl_buffer_flush (buffer);
+
+ /* Now resize the layer down to the correct size if necessary. */
+ if (width != ROUND4 (width))
+ {
+ gimp_layer_resize (layer_id, width, height, 0, 0);
+ gimp_image_resize (new_image_id, width, height, 0, 0);
+ }
+
+ *image_id = new_image_id;
+
+ return;
+}
+
+/*
+ * doNonRootWindowCapture
+ *
+ * Capture a selected window.
+ * ENTRY POINT FOR WINSNAP NONROOT
+ *
+ */
+static int
+doWindowCapture (void)
+{
+ /* Start up a standard Win32
+ * message handling loop for
+ * selection of the window
+ * to be captured
+ */
+ return winsnapWinMain ();
+}
+
+/******************************************************************
+ * Debug stuff
+ ******************************************************************/
+
+/*
+ * formatWindowsError
+ *
+ * Format the latest Windows error message into
+ * a readable string. Store in the provided
+ * buffer.
+ */
+static void
+formatWindowsError (char *buffer,
+ int buf_size)
+{
+ LPVOID lpMsgBuf;
+
+ /* Format the message */
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf, 0, NULL );
+
+ /* Copy to the buffer */
+ strncpy(buffer, lpMsgBuf, buf_size - 1);
+
+ LocalFree(lpMsgBuf);
+}
+
+/******************************************************************
+ * Bitmap capture and handling
+ ******************************************************************/
+
+/*
+ * primDoWindowCapture
+ *
+ * The primitive window capture functionality. Accepts
+ * the two device contexts and the rectangle to be
+ * captured.
+ */
+static HBITMAP
+primDoWindowCapture (HDC hdcWindow,
+ HDC hdcCompat,
+ RECT rect)
+{
+ HBITMAP hbmCopy;
+ HGDIOBJ oldObject;
+ BITMAPINFO bmi;
+
+ int width = (rect.right - rect.left);
+ int height = (rect.bottom - rect.top);
+
+ /* Create the bitmap info header */
+ bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = ROUND4(width);
+ bmi.bmiHeader.biHeight = -height;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 24;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = 0;
+ bmi.bmiHeader.biXPelsPerMeter =
+ bmi.bmiHeader.biYPelsPerMeter = 0;
+ bmi.bmiHeader.biClrUsed = 0;
+ bmi.bmiHeader.biClrImportant = 0;
+
+ /* Create the bitmap storage space */
+ hbmCopy = CreateDIBSection (hdcCompat,
+ (BITMAPINFO *) &bmi,
+ DIB_RGB_COLORS,
+ (void **)&capBytes, NULL, 0);
+ if (!hbmCopy)
+ {
+ formatWindowsError(buffer, sizeof buffer);
+ g_error("Error creating DIB section: %s", buffer);
+ return NULL;
+ }
+
+ /* Select the bitmap into the compatible DC. */
+ oldObject = SelectObject (hdcCompat, hbmCopy);
+ if (!oldObject)
+ {
+ formatWindowsError (buffer, sizeof buffer);
+ g_error ("Error selecting object: %s", buffer);
+ return NULL;
+ }
+
+ /* Copy the data from the application to the bitmap. Even if we did
+ * round up the width, BitBlt only the actual data.
+ */
+ if (!BitBlt(hdcCompat, 0,0,
+ width, height,
+ hdcWindow, rect.left, rect.top,
+ SRCCOPY))
+ {
+ formatWindowsError (buffer, sizeof buffer);
+ g_error ("Error copying bitmap: %s", buffer);
+ return NULL;
+ }
+
+ /* Restore the original object */
+ SelectObject (hdcCompat, oldObject);
+
+ return hbmCopy;
+}
+
+static BOOL CALLBACK
+GetAccurateWindowRect_MonitorEnumProc (HMONITOR hMonitor,
+ HDC hdcMonitor,
+ LPRECT lprcMonitor,
+ LPARAM dwData)
+{
+ RECT **rectScreens = (RECT**) dwData;
+
+ if (!lprcMonitor) return FALSE;
+
+ if (! (*rectScreens))
+ *rectScreens = (RECT*) g_malloc (sizeof (RECT)*(rectScreensCount+1));
+ else
+ *rectScreens = (RECT*) g_realloc (rectScreens,
+ sizeof (RECT)*(rectScreensCount+1));
+
+ if (*rectScreens == NULL)
+ {
+ rectScreensCount = 0;
+ return FALSE;
+ }
+
+ (*rectScreens)[rectScreensCount] = *lprcMonitor;
+
+ rectScreensCount++;
+
+ return TRUE;
+}
+
+static BOOL
+GetAccurateWindowRect (HWND hwndTarget,
+ RECT *outRect)
+{
+ HRESULT dwmResult;
+ WINDOWPLACEMENT windowplacment;
+
+ ZeroMemory (outRect, sizeof (RECT));
+
+ /* First we try to get the rect using the dwm api. it will also avoid us from doing more ugly fixes when the window is maximized */
+ if (LoadRequiredDwmFunctions ())
+ {
+ dwmResult = DwmGetWindowAttribute (hwndTarget, DWMWA_EXTENDED_FRAME_BOUNDS, (PVOID) outRect, sizeof (RECT));
+ UnloadRequiredDwmFunctions ();
+ if (dwmResult == S_OK) return TRUE;
+ }
+
+ /* In this case, we did not got the rect from the dwm api so we try to get the rect with the normal function */
+ if (GetWindowRect (hwndTarget, outRect))
+ {
+ /* If the window is maximized then we need and can fix the rect variable
+ * (we need to do this if the rect isn't coming from dwm api)
+ */
+ ZeroMemory (&windowplacment, sizeof (WINDOWPLACEMENT));
+ if (GetWindowPlacement (hwndTarget, &windowplacment) && windowplacment.showCmd == SW_SHOWMAXIMIZED)
+ {
+ RECT *rectScreens = NULL;
+ int xCenter;
+ int yCenter;
+ int i;
+
+ /* If this is not the first time we call this function for some
+ * reason then we reset the rectScreens count
+ */
+ if (rectScreensCount)
+ rectScreensCount = 0;
+
+ /* Get the screens rects */
+ EnumDisplayMonitors (NULL, NULL, GetAccurateWindowRect_MonitorEnumProc,
+ (LPARAM) &rectScreens);
+
+ /* If for some reason the array size is 0 then we fill it with the desktop rect */
+ if (! rectScreensCount)
+ {
+ rectScreens = (RECT*) g_malloc (sizeof (RECT));
+ if (! GetWindowRect (GetDesktopWindow (), rectScreens))
+ {
+ /* error: could not get rect screens */
+ g_free (rectScreens);
+ return FALSE;
+ }
+
+ rectScreensCount = 1;
+ }
+
+ xCenter = outRect->left + (outRect->right - outRect->left) / 2;
+ yCenter = outRect->top + (outRect->bottom - outRect->top) / 2;
+
+ /* find on which screen the window exist */
+ for (i = 0; i < rectScreensCount; i++)
+ if (xCenter > rectScreens[i].left && xCenter < rectScreens[i].right &&
+ yCenter > rectScreens[i].top && yCenter < rectScreens[i].bottom)
+ break;
+
+ if (i == rectScreensCount)
+ /* Error: did not found on which screen the window exist */
+ return FALSE;
+
+ if (rectScreens[i].left > outRect->left) outRect->left = rectScreens[i].left;
+ if (rectScreens[i].right < outRect->right) outRect->right = rectScreens[i].right;
+ if (rectScreens[i].top > outRect->top) outRect->top = rectScreens[i].top;
+ if (rectScreens[i].bottom < outRect->bottom) outRect->bottom = rectScreens[i].bottom;
+
+ g_free (rectScreens);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * doCapture
+ *
+ * Do the capture. Accepts the window
+ * handle to be captured or the NULL value
+ * to specify the root window.
+ */
+static gboolean
+doCapture (HWND selectedHwnd)
+{
+ RECT rect;
+
+ /* Try and get everything out of the way before the
+ * capture.
+ */
+ Sleep (500 + winsnapvals.delay * 1000);
+
+ /* Are we capturing a window or the whole screen */
+ if (selectedHwnd)
+ {
+ if (! GetAccurateWindowRect (selectedHwnd, &rect))
+ /* For some reason it works only if we return here TRUE. strange... */
+ return TRUE;
+
+ /* First try to capture the window with the magnification api.
+ * This will solve the bug https://bugzilla.gnome.org/show_bug.cgi?id=793722/
+ */
+
+ if (! doCaptureMagnificationAPI (selectedHwnd, rect))
+ {
+ /* If for some reason this capture method failed then
+ * capture the window with the normal method:
+ */
+ HWND previousHwnd = GetForegroundWindow ();
+ gboolean success;
+
+ SetForegroundWindow (selectedHwnd);
+ BringWindowToTop (selectedHwnd);
+
+ success = doCaptureBitBlt (selectedHwnd, rect);
+
+ if (previousHwnd)
+ SetForegroundWindow (previousHwnd);
+
+ return success;
+ }
+
+ return TRUE;
+
+ }
+ else
+ {
+ /* Get the screen's rectangle */
+ rect.top = GetSystemMetrics (SM_YVIRTUALSCREEN);
+ rect.bottom = GetSystemMetrics (SM_YVIRTUALSCREEN) + GetSystemMetrics (SM_CYVIRTUALSCREEN);
+ rect.left = GetSystemMetrics (SM_XVIRTUALSCREEN);
+ rect.right = GetSystemMetrics (SM_XVIRTUALSCREEN) + GetSystemMetrics (SM_CXVIRTUALSCREEN);
+
+ return doCaptureBitBlt (selectedHwnd, rect);
+
+ }
+
+ return FALSE; /* we should never get here... */
+}
+
+static gboolean
+doCaptureBitBlt (HWND selectedHwnd,
+ RECT rect)
+{
+
+ HDC hdcSrc;
+ HDC hdcCompat;
+ HBITMAP hbm;
+
+ /* Get the device context for the whole screen
+ * even if we just want to capture a window.
+ * this will allow to capture applications that
+ * don't render to their main window's device
+ * context (e.g. browsers).
+ */
+ hdcSrc = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL);
+
+ if (!hdcSrc)
+ {
+ formatWindowsError(buffer, sizeof buffer);
+ g_error ("Error getting device context: %s", buffer);
+ return FALSE;
+ }
+ hdcCompat = CreateCompatibleDC (hdcSrc);
+ if (!hdcCompat)
+ {
+ formatWindowsError (buffer, sizeof buffer);
+ g_error ("Error getting compat device context: %s", buffer);
+ return FALSE;
+ }
+
+ /* Do the window capture */
+ hbm = primDoWindowCapture (hdcSrc, hdcCompat, rect);
+ if (!hbm)
+ return FALSE;
+
+ /* Release the device context */
+ ReleaseDC(selectedHwnd, hdcSrc);
+
+ if (hbm == NULL) return FALSE;
+
+ sendBMPToGimp (hbm, hdcCompat, rect);
+
+ return TRUE;
+
+}
+
+static void
+doCaptureMagnificationAPI_callback (HWND hwnd,
+ void *srcdata,
+ MAGIMAGEHEADER srcheader,
+ void *destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty)
+{
+ if (!srcdata) return;
+ capBytes = (guchar*) malloc (sizeof (guchar)*srcheader.cbSize);
+ rgbaToRgbBytes (capBytes, srcdata, srcheader.cbSize);
+ returnedSrcheader = srcheader;
+}
+
+static BOOL
+isWindowIsAboveCaptureRegion (HWND hwndWindow,
+ RECT rectCapture)
+{
+ RECT rectWindow;
+ ZeroMemory (&rectWindow, sizeof (RECT));
+ if (!GetWindowRect (hwndWindow, &rectWindow)) return FALSE;
+ if (
+ (
+ (rectWindow.left >= rectCapture.left && rectWindow.left < rectCapture.right)
+ ||
+ (rectWindow.right <= rectCapture.right && rectWindow.right > rectCapture.left)
+ ||
+ (rectWindow.left <= rectCapture.left && rectWindow.right >= rectCapture.right)
+ )
+ &&
+ (
+ (rectWindow.top >= rectCapture.top && rectWindow.top < rectCapture.bottom)
+ ||
+ (rectWindow.bottom <= rectCapture.bottom && rectWindow.bottom > rectCapture.top)
+ ||
+ (rectWindow.top <= rectCapture.top && rectWindow.bottom >= rectCapture.bottom)
+ )
+ )
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static gboolean
+doCaptureMagnificationAPI (HWND selectedHwnd,
+ RECT rect)
+{
+ HWND hwndMag;
+ HWND hwndHost;
+ HWND nextWindow;
+ HWND excludeWins[24];
+ RECT round4Rect;
+ int excludeWinsCount = 0;
+
+ if (!LoadMagnificationLibrary ()) return FALSE;
+
+ if (!MagInitialize ()) return FALSE;
+
+ round4Rect = rect;
+ round4Rect.right = round4Rect.left + ROUND4(round4Rect.right - round4Rect.left);
+
+ /* Create the host window that will store the mag child window */
+ hwndHost = CreateWindowEx (0x08000000 | 0x080000 | 0x80 | 0x20, APP_NAME, NULL, 0x80000000,
+ 0, 0, 0, 0, NULL, NULL, GetModuleHandle (NULL), NULL);
+
+ if (!hwndHost)
+ {
+ MagUninitialize ();
+ return FALSE;
+ }
+
+ SetLayeredWindowAttributes (hwndHost, (COLORREF)0, (BYTE)255, (DWORD)0x02);
+
+ /* Create the mag child window inside the host window */
+ hwndMag = CreateWindow (WC_MAGNIFIER, TEXT ("MagnifierWindow"),
+ WS_CHILD /*| MS_SHOWMAGNIFIEDCURSOR*/ /*| WS_VISIBLE*/,
+ 0, 0, round4Rect.right - round4Rect.left, round4Rect.bottom - round4Rect.top,
+ hwndHost, NULL, GetModuleHandle (NULL), NULL);
+
+ /* Set the callback function that will be called by the api to get the pixels */
+ if (!MagSetImageScalingCallback (hwndMag, (MagImageScalingCallback)doCaptureMagnificationAPI_callback))
+ {
+ DestroyWindow (hwndHost);
+ MagUninitialize ();
+ return FALSE;
+ }
+
+ /* Add only windows that above the target window */
+ for (nextWindow = GetNextWindow (selectedHwnd, GW_HWNDPREV); nextWindow != NULL; nextWindow = GetNextWindow (nextWindow, GW_HWNDPREV))
+ if (isWindowIsAboveCaptureRegion (nextWindow, rect))
+ {
+ excludeWins[excludeWinsCount++] = nextWindow;
+ /* This api can't work with more than 24 windows. we stop on the 24 window */
+ if (excludeWinsCount >= 24) break;
+ }
+
+ if (excludeWinsCount)
+ MagSetWindowFilterList (hwndMag, MW_FILTERMODE_EXCLUDE, excludeWinsCount, excludeWins);
+
+ /* Call the api to capture the window */
+ capBytes = NULL;
+
+ if (!MagSetWindowSource (hwndMag, round4Rect) || !capBytes)
+ {
+ DestroyWindow (hwndHost);
+ MagUninitialize ();
+ return FALSE;
+ }
+
+ /* Send it to Gimp */
+ sendBMPToGimp (NULL, NULL, rect);
+
+ DestroyWindow (hwndHost);
+ MagUninitialize ();
+
+ return TRUE;
+}
+
+/******************************************************************
+ * Win32 entry point and setup...
+ ******************************************************************/
+
+#define DINV 3
+
+/*
+ * highlightWindowFrame
+ *
+ * Highlight (or unhighlight) the specified
+ * window handle's frame.
+ */
+static void
+highlightWindowFrame (HWND hWnd)
+{
+ HDC hdc;
+ RECT rc;
+
+ if (!IsWindow (hWnd))
+ return;
+
+ hdc = GetWindowDC (hWnd);
+ GetWindowRect (hWnd, &rc);
+ OffsetRect (&rc, -rc.left, -rc.top);
+
+ if (!IsRectEmpty (&rc))
+ {
+ PatBlt (hdc, rc.left, rc.top, rc.right-rc.left, DINV, DSTINVERT);
+ PatBlt (hdc, rc.left, rc.bottom-DINV, DINV, -(rc.bottom-rc.top-2*DINV),
+ DSTINVERT);
+ PatBlt (hdc, rc.right-DINV, rc.top+DINV, DINV, rc.bottom-rc.top-2*DINV,
+ DSTINVERT);
+ PatBlt (hdc, rc.right, rc.bottom-DINV, -(rc.right-rc.left), DINV,
+ DSTINVERT);
+ }
+
+ ReleaseDC (hWnd, hdc);
+ UpdateWindow (hWnd);
+}
+
+/*
+ * setCaptureType
+ *
+ * Set the capture type. Should be one of:
+ * SELECT_FRAME
+ * SELECT_CLIENT
+ * SELECT_WINDOW
+ */
+void
+setCaptureType (int capType)
+{
+ captureType = capType;
+}
+
+/*
+ * myWindowFromPoint
+ *
+ * Map to the appropriate window from the
+ * specified point. The chosen window is
+ * based on the current capture type.
+ */
+static HWND
+myWindowFromPoint (POINT pt)
+{
+ HWND myHwnd;
+ HWND nextHwnd;
+
+ switch (captureType)
+ {
+ case SELECT_FRAME:
+ case SELECT_CLIENT:
+ nextHwnd = WindowFromPoint (pt);
+
+ do {
+ myHwnd = nextHwnd;
+ nextHwnd = GetParent (myHwnd);
+ } while (nextHwnd);
+
+ return myHwnd;
+ break;
+
+ case SELECT_WINDOW:
+ return WindowFromPoint (pt);
+ break;
+ }
+
+ return WindowFromPoint (pt);
+}
+
+/*
+ * dialogProc
+ *
+ * The window procedure for the window
+ * selection dialog box.
+ */
+BOOL CALLBACK
+dialogProc (HWND hwndDlg,
+ UINT msg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ static int mouseCaptured;
+ static int buttonDown;
+ static HCURSOR oldCursor;
+ static RECT bitmapRect;
+ static HWND highlightedHwnd = NULL;
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ int nonclientHeight;
+ HWND hwndGroup;
+ RECT dlgRect;
+ RECT clientRect;
+ RECT groupRect;
+ BITMAP bm;
+
+ /* Set the mouse capture flag */
+ buttonDown = 0;
+ mouseCaptured = 0;
+
+ /* Calculate the bitmap dimensions */
+ GetObject (iconInfo.hbmMask, sizeof(BITMAP), (VOID *)&bm);
+
+ /* Calculate the dialog window dimensions */
+ GetWindowRect (hwndDlg, &dlgRect);
+
+ /* Calculate the group box dimensions */
+ hwndGroup = GetDlgItem(hwndDlg, IDC_GROUP);
+ GetWindowRect (hwndGroup, &groupRect);
+ OffsetRect (&groupRect, -dlgRect.left, -dlgRect.top);
+
+ /* The client's rectangle */
+ GetClientRect (hwndDlg, &clientRect);
+
+ /* The non-client height */
+ nonclientHeight = (dlgRect.bottom - dlgRect.top) -
+ (clientRect.bottom - clientRect.top);
+
+ /* Calculate the bitmap rectangle */
+ bitmapRect.top = ((groupRect.top + groupRect.bottom) / 2) -
+ (bm.bmHeight / 2);
+ bitmapRect.top -= nonclientHeight;
+ bitmapRect.bottom = bitmapRect.top + bm.bmHeight;
+ bitmapRect.left = ((groupRect.left + groupRect.right) / 2) - (bm.bmWidth / 2);
+ bitmapRect.right = bitmapRect.left + bm.bmWidth;
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ /* Track the button down state */
+ buttonDown = 1;
+ break;
+
+ case WM_LBUTTONUP:
+ buttonDown = 0;
+
+ /* If we have mouse captured
+ * we do this stuff.
+ */
+ if (mouseCaptured)
+ {
+ HWND selectedHwnd;
+ POINT cursorPos;
+
+ /* Release the capture */
+ mouseCaptured = 0;
+ SetCursor (oldCursor);
+ ReleaseCapture ();
+
+ /* Remove the highlight */
+ if (highlightedHwnd)
+ highlightWindowFrame (highlightedHwnd);
+ RedrawWindow (hwndDlg, NULL, NULL, RDW_INVALIDATE);
+
+ /* Return the selected window */
+ GetCursorPos (&cursorPos);
+ selectedHwnd = myWindowFromPoint (cursorPos);
+ EndDialog (hwndDlg, (INT_PTR) selectedHwnd);
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ /* If the mouse is captured, show
+ * the window which is tracking
+ * under the mouse position.
+ */
+ if (mouseCaptured)
+ {
+ HWND currentHwnd;
+ POINT cursorPos;
+
+ /* Get the window */
+ GetCursorPos (&cursorPos);
+ currentHwnd = myWindowFromPoint (cursorPos);
+
+ /* Do the highlighting */
+ if (highlightedHwnd != currentHwnd)
+ {
+ if (highlightedHwnd)
+ highlightWindowFrame (highlightedHwnd);
+ if (currentHwnd)
+ highlightWindowFrame (currentHwnd);
+ highlightedHwnd = currentHwnd;
+ }
+ /* If the mouse has not been captured,
+ * try to figure out if we should capture
+ * the mouse.
+ */
+ }
+ else if (buttonDown)
+ {
+ POINT cursorPos;
+
+ /* Get the current client position */
+ GetCursorPos (&cursorPos);
+ ScreenToClient (hwndDlg, &cursorPos);
+
+ /* Check if within the rectangle formed
+ * by the bitmap
+ */
+ if (PtInRect (&bitmapRect, cursorPos)) {
+ mouseCaptured = 1;
+ oldCursor = SetCursor (selectCursor);
+ SetCapture (hwndDlg);
+ RedrawWindow (hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
+ }
+ }
+
+ break;
+
+ case WM_PAINT:
+ {
+ HDC hDC;
+ PAINTSTRUCT ps;
+
+ /* If the mouse is not captured draw
+ * the cursor image
+ */
+ if (!mouseCaptured)
+ {
+ hDC = BeginPaint (hwndDlg, &ps);
+ DrawIconEx (hDC, bitmapRect.left, bitmapRect.top, selectCursor,
+ 0, 0, 0, NULL, DI_NORMAL);
+ EndPaint (hwndDlg, &ps);
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ /* Handle the cancel button */
+ switch (LOWORD (wParam))
+ {
+ case IDCANCEL:
+ EndDialog (hwndDlg, 0);
+ return TRUE;
+ break;
+ }
+
+ }
+
+ return FALSE;
+}
+
+///* Don't use the normal WinMain from gimp.h */
+//#define WinMain WinMain_no_thanks
+//MAIN()
+//#undef WinMain
+
+/*
+ * WinMain
+ *
+ * The standard gimp plug-in WinMain won't quite cut it for
+ * this plug-in.
+ */
+//int APIENTRY
+//WinMain(HINSTANCE hInstance,
+// HINSTANCE hPrevInstance,
+// LPSTR lpCmdLine,
+// int nCmdShow)
+//{
+// /*
+// * Normally, we would do all of the Windows-ish set up of
+// * the window classes and stuff here in WinMain. But,
+// * the only time we really need the window and message
+// * queues is during the plug-in run. So, all of that will
+// * be done during run(). This avoids all of the Windows
+// * setup stuff for the query(). Stash the instance handle now
+// * so it is available from the run() procedure.
+// */
+// hInst = hInstance;
+//
+// /*
+// * Now, call gimp_main... This is what the normal WinMain()
+// * would do.
+// */
+//// return gimp_main(&PLUG_IN_INFO, __argc, __argv);
+//}
+
+/*
+ * InitApplication
+ *
+ * Initialize window data and register the window class
+ */
+BOOL
+InitApplication (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+ BOOL retValue;
+
+ /* Get some resources */
+#ifdef _MSC_VER
+ /* For some reason this works only with MSVC */
+ selectCursor = LoadCursor (hInstance, MAKEINTRESOURCE(IDC_SELECT));
+#else
+ selectCursor = LoadCursor (NULL, IDC_CROSS);
+#endif
+ GetIconInfo (selectCursor, &iconInfo);
+
+ /*
+ * Fill in window class structure with parameters to describe
+ * the main window.
+ */
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = (WNDPROC) WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
+ wc.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
+ wc.lpszClassName = APP_NAME;
+ wc.lpszMenuName = NULL;
+
+ /* Register the window class and stash success/failure code. */
+ retValue = RegisterClass (&wc);
+
+ /* Log error */
+ if (!retValue)
+ {
+ formatWindowsError (buffer, sizeof buffer);
+ g_error ("Error registering class: %s", buffer);
+ return retValue;
+ }
+
+ return retValue;
+}
+
+/*
+ * InitInstance
+ *
+ * Create the main window for the application.
+ */
+BOOL
+InitInstance (HINSTANCE hInstance,
+ int nCmdShow)
+{
+ HINSTANCE User32Library = LoadLibrary ("user32.dll");
+
+ if (User32Library)
+ {
+ typedef BOOL (WINAPI* SET_PROC_DPI_AWARE)();
+ SET_PROC_DPI_AWARE SetProcessDPIAware;
+
+ /* This line fix bug: https://bugzilla.gnome.org/show_bug.cgi?id=796121#c4 */
+ SetProcessDPIAware = (SET_PROC_DPI_AWARE) GetProcAddress (User32Library,
+ "SetProcessDPIAware");
+ if (SetProcessDPIAware)
+ SetProcessDPIAware();
+
+ FreeLibrary (User32Library);
+ }
+
+ /* Create our window */
+ mainHwnd = CreateWindow (APP_NAME, APP_NAME, WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
+ NULL, NULL, hInstance, NULL);
+
+ if (!mainHwnd)
+ {
+ return (FALSE);
+ }
+
+ ShowWindow (mainHwnd, nCmdShow);
+ UpdateWindow (mainHwnd);
+
+ return TRUE;
+}
+
+/*
+ * winsnapWinMain
+ *
+ * This is the function that represents the code that
+ * would normally reside in WinMain (see above). This
+ * function will get called during run() in order to set
+ * up the windowing environment necessary for WinSnap to
+ * operate.
+ */
+int
+winsnapWinMain (void)
+{
+ MSG msg;
+
+ /* Perform instance initialization */
+ if (!InitApplication (hInst))
+ return (FALSE);
+
+ /* Perform application initialization */
+ if (!InitInstance (hInst, SHOW_WINDOW))
+ return (FALSE);
+
+ /* Main message loop */
+ while (GetMessage (&msg, NULL, 0, 0))
+ {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+
+ return (msg.wParam);
+}
+
+/*
+ * WndProc
+ *
+ * Process window message for the main window.
+ */
+LRESULT CALLBACK
+WndProc (HWND hwnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ HWND selectedHwnd;
+
+ switch (message)
+ {
+
+ case WM_CREATE:
+ /* The window is created... Send the capture message */
+ PostMessage (hwnd, WM_DOCAPTURE, 0, 0);
+ break;
+
+ case WM_DOCAPTURE:
+ /* Get the selected window handle */
+ selectedHwnd = (HWND) DialogBox (hInst, MAKEINTRESOURCE(IDD_SELECT),
+ hwnd, (DLGPROC) dialogProc);
+ if (selectedHwnd)
+ doCapture (selectedHwnd);
+
+ PostQuitMessage (selectedHwnd != NULL);
+
+ break;
+
+ case WM_DESTROY:
+ PostQuitMessage (0);
+ break;
+
+ default:
+ return (DefWindowProc (hwnd, message, wParam, lParam));
+ }
+
+ return 0;
+}
+
+#endif /* G_OS_WIN32 */