Initial checkin of Pika from heckimp

This commit is contained in:
2023-09-25 15:35:21 -07:00
commit 891e999216
6761 changed files with 5240685 additions and 0 deletions

View File

@ -0,0 +1,49 @@
plugin_name = 'screenshot'
plugin_sources = [
'screenshot-freedesktop.c',
'screenshot-osx.c',
'screenshot-win32.c',
'screenshot-x11.c',
'screenshot.c',
]
screenshot_icons_images = [
'screenshot-icon.png',
]
resourcename = 'screenshot-icons'
xml_content = '<?xml version="1.0" encoding="UTF-8"?>\n'
xml_content += '<gresources>\n'
xml_content += ' <gresource prefix="/technology.heckin/screenshot-icons">\n'
foreach file : screenshot_icons_images
xml_content+=' <file>'+ file +'</file>\n'
endforeach
xml_content += ' </gresource>\n'
xml_content += '</gresources>\n'
xml_file = configure_file(
output: resourcename + '.gresource.xml',
command: [ 'echo', xml_content ],
capture: true,
)
plugin_sources += gnome.compile_resources(
resourcename,
xml_file,
c_name: resourcename.underscorify(),
)
if platform_windows
plugin_sources += windows.compile_resources('screenshot-win32.rc')
endif
screenshot = executable('screenshot',
plugin_sources,
dependencies: [
libpikaui_dep,
x11, xmu, xext, xfixes,
],
install: true,
install_dir: pikaplugindir / 'plug-ins' / plugin_name,
)

View File

@ -0,0 +1,246 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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 2016 Michael Natterer <mitch@gimp.org>
* Copyright 2017 Jehan <jehan@gimp.org>
*
* 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>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include "screenshot.h"
#include "screenshot-freedesktop.h"
static GDBusProxy *proxy = NULL;
gboolean
screenshot_freedesktop_available (void)
{
proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
NULL,
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Screenshot",
NULL, NULL);
if (proxy)
{
GError *error = NULL;
g_dbus_proxy_call_sync (proxy, "org.freedesktop.DBus.Peer.Ping",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, &error);
if (! error)
return TRUE;
g_clear_error (&error);
g_object_unref (proxy);
proxy = NULL;
}
return FALSE;
}
ScreenshotCapabilities
screenshot_freedesktop_get_capabilities (void)
{
/* Portal has no capabilities other than root screenshot! */
return 0;
}
static void
screenshot_freedesktop_dbus_signal (GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
PikaImage **image)
{
if (g_strcmp0 (signal_name, "Response") == 0)
{
GVariant *results;
guint32 response;
g_variant_get (parameters, "(u@a{sv})",
&response,
&results);
/* Possible values:
* 0: Success, the request is carried out
* 1: The user cancelled the interaction
* 2: The user interaction was ended in some other way
* Cf. https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.Request.xml
*/
if (response == 0)
{
gchar *uri;
if (g_variant_lookup (results, "uri", "s", &uri))
{
GFile *file = g_file_new_for_uri (uri);
*image = pika_file_load (PIKA_RUN_NONINTERACTIVE, file);
/* Delete the actual file. */
g_file_delete (file, NULL, NULL);
g_object_unref (file);
g_free (uri);
}
}
g_variant_unref (results);
/* Quit anyway. */
gtk_main_quit ();
}
}
PikaPDBStatusType
screenshot_freedesktop_shoot (ScreenshotValues *shootvals,
GdkMonitor *monitor,
PikaImage **image,
GError **error)
{
GVariant *retval;
GVariantBuilder *options;
gchar *opath = NULL;
gchar *parent_window = NULL;
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
{
GdkWindow *window;
window = pika_ui_get_progress_window ();
if (window)
{
gint id;
id = GDK_WINDOW_XID (window);
parent_window = g_strdup_printf ("x11:0x%x", id);
}
}
#endif
if (shootvals->shoot_type != SHOOT_ROOT)
{
/* This should not happen. */
return PIKA_PDB_EXECUTION_ERROR;
}
if (shootvals->screenshot_delay > 0)
screenshot_delay (shootvals->screenshot_delay);
options = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
/* "interactive" option will display the options first (otherwise, it
* makes a screenshot first, then proposes to tweak. Since version 2
* of the API. For older implementations, it should just be ignored.
*/
g_variant_builder_add (options, "{sv}", "interactive", g_variant_new_boolean (TRUE));
retval = g_dbus_proxy_call_sync (proxy, "Screenshot",
g_variant_new ("(sa{sv})",
parent_window ? parent_window : "",
options, NULL),
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, error);
g_free (parent_window);
g_object_unref (proxy);
g_variant_builder_unref (options);
proxy = NULL;
if (retval)
{
g_variant_get (retval, "(o)", &opath);
g_variant_unref (retval);
}
if (opath)
{
GDBusProxy *proxy2 = NULL;
proxy2 = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
NULL,
"org.freedesktop.portal.Desktop",
opath,
"org.freedesktop.portal.Request",
NULL, NULL);
*image = NULL;
g_signal_connect (proxy2, "g-signal",
G_CALLBACK (screenshot_freedesktop_dbus_signal),
image);
gtk_main ();
g_object_unref (proxy2);
g_free (opath);
/* Signal got a response. */
if (*image)
{
if (! pika_image_get_color_profile (*image))
{
/* The Freedesktop portal does not return a profile, so we
* don't have color characterization through the API.
* Ideally then, the returned screenshot image would have
* embedded profile, but this depends on each desktop
* implementation of the portal (and at time of writing,
* the GNOME implementation of Freedesktop portal at least
* didn't embed a profile with the returned PNG image).
*
* As a last resort, we assign the profile of current
* monitor. This will actually only work if we use the
* portal on X11 (because we don't have monitor's profile
* access on Wayland AFAIK), and only as long as this is a
* single-display setup.
*
* We need to figure out how to do better color management for
* portal screenshots. TODO!
*/
PikaColorProfile *profile;
profile = pika_monitor_get_color_profile (monitor);
if (profile)
{
pika_image_set_color_profile (*image, profile);
g_object_unref (profile);
}
}
return PIKA_PDB_SUCCESS;
}
}
return PIKA_PDB_EXECUTION_ERROR;
}

View File

@ -0,0 +1,36 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#ifndef __SCREENSHOT_FREEDESKTOP_H__
#define __SCREENSHOT_FREEDESKTOP_H__
gboolean screenshot_freedesktop_available (void);
ScreenshotCapabilities screenshot_freedesktop_get_capabilities (void);
PikaPDBStatusType screenshot_freedesktop_shoot (ScreenshotValues *shootvals,
GdkMonitor *monitor,
PikaImage **image,
GError **error);
#endif /* __SCREENSHOT_FREEDESKTOP_H__ */

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,160 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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
*
* 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"
#ifdef PLATFORM_OSX
#include <stdlib.h> /* for system() on OSX */
#include <string.h>
#include <glib.h>
#include <glib/gstdio.h> /* g_unlink() */
#include <libpika/pika.h>
#include <libpika/pikaui.h>
#include "screenshot.h"
#include "screenshot-osx.h"
/*
* Mac OS X uses a rootless X server. This won't let us use
* gdk_pixbuf_get_from_drawable() and similar function on the root
* window to get the entire screen contents. With a native OS X build
* we have to do this without X as well.
*
* Since Mac OS X 10.2 a system utility for screencapturing is
* included. We can safely use this, since it's available on every OS
* X version PIKA is running on.
*
* The main drawbacks are that it's not possible to shoot windows or
* regions in scripts in noninteractive mode, and that windows always
* include decorations, since decorations are different between X11
* windows and native OS X app windows. But we can use this switch
* to capture the shadow of a window, which is indeed very Mac-ish.
*
* This routines works well with X11 and as a native build.
*/
gboolean
screenshot_osx_available (void)
{
return TRUE;
}
ScreenshotCapabilities
screenshot_osx_get_capabilities (void)
{
return (SCREENSHOT_CAN_SHOOT_DECORATIONS |
SCREENSHOT_CAN_SHOOT_POINTER |
SCREENSHOT_CAN_SHOOT_REGION |
SCREENSHOT_CAN_SHOOT_WINDOW |
SCREENSHOT_CAN_PICK_WINDOW |
SCREENSHOT_CAN_DELAY_WINDOW_SHOT);
}
PikaPDBStatusType
screenshot_osx_shoot (ScreenshotValues *shootvals,
GdkScreen *screen,
PikaImage **image,
GError **error)
{
const gchar *mode = " ";
const gchar *cursor = " ";
gchar *delay = NULL;
GFile *tmpfile;
gchar *quoted;
gchar *command = NULL;
switch (shootvals->shoot_type)
{
case SHOOT_REGION:
if (shootvals->select_delay > 0)
screenshot_delay (shootvals->select_delay);
mode = "-is";
break;
case SHOOT_WINDOW:
if (shootvals->select_delay > 0)
screenshot_delay (shootvals->select_delay);
if (shootvals->decorate)
mode = "-iwo";
else
mode = "-iw";
if (shootvals->show_cursor)
cursor = "-C";
break;
case SHOOT_ROOT:
mode = " ";
if (shootvals->show_cursor)
cursor = "-C";
break;
default:
g_return_val_if_reached (PIKA_PDB_CALLING_ERROR);
break;
}
delay = g_strdup_printf ("-T %i", shootvals->screenshot_delay);
tmpfile = pika_temp_file ("png");
quoted = g_shell_quote (g_file_peek_path (tmpfile));
command = g_strjoin (" ",
"/usr/sbin/screencapture",
mode,
cursor,
delay,
quoted,
NULL);
g_free (quoted);
g_free (delay);
if (system ((const char *) command) == EXIT_SUCCESS)
{
/* don't attach a profile, screencapture attached one
*/
*image = pika_file_load (PIKA_RUN_NONINTERACTIVE,
tmpfile);
g_file_delete (tmpfile, NULL, NULL);
g_free (command);
return PIKA_PDB_SUCCESS;
}
g_free (command);
return PIKA_PDB_EXECUTION_ERROR;
}
#endif /* PLATFORM_OSX */

View File

@ -0,0 +1,40 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#ifndef __SCREENSHOT_OSX_H__
#define __SCREENSHOT_OSX_H__
#ifdef PLATFORM_OSX
gboolean screenshot_osx_available (void);
ScreenshotCapabilities screenshot_osx_get_capabilities (void);
PikaPDBStatusType screenshot_osx_shoot (ScreenshotValues *shootvals,
GdkScreen *screen,
PikaImage **image,
GError **error);
#endif /* PLATFORM_OSX */
#endif /* __SCREENSHOT_OSX_H__ */

View File

@ -0,0 +1,80 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* screenshot-win32-dwm-api.h
* Copyright (C) 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/>.
*/
void UnloadRequiredDwmFunctions (void);
BOOL LoadRequiredDwmFunctions (void);
typedef HRESULT (WINAPI* DWMGETWINDOWATTRIBUTE)(HWND, DWORD, _Out_ PVOID, DWORD);
DWMGETWINDOWATTRIBUTE DwmGetWindowAttribute;
typedef enum _DWMWINDOWATTRIBUTE {
DWMWA_NCRENDERING_ENABLED = 1,
DWMWA_NCRENDERING_POLICY,
DWMWA_TRANSITIONS_FORCEDISABLED,
DWMWA_ALLOW_NCPAINT,
DWMWA_CAPTION_BUTTON_BOUNDS,
DWMWA_NONCLIENT_RTL_LAYOUT,
DWMWA_FORCE_ICONIC_REPRESENTATION,
DWMWA_FLIP3D_POLICY,
DWMWA_EXTENDED_FRAME_BOUNDS,
DWMWA_HAS_ICONIC_BITMAP,
DWMWA_DISALLOW_PEEK,
DWMWA_EXCLUDED_FROM_PEEK,
DWMWA_CLOAK,
DWMWA_CLOAKED,
DWMWA_FREEZE_REPRESENTATION,
DWMWA_LAST
} DWMWINDOWATTRIBUTE;
typedef HRESULT (WINAPI* DWMISCOMPOSITIONENABLED) (BOOL *pfEnabled);
DWMISCOMPOSITIONENABLED DwmIsCompositionEnabled;
static HMODULE dwmApi = NULL;
void
UnloadRequiredDwmFunctions (void)
{
if (! dwmApi) return;
FreeLibrary(dwmApi);
dwmApi = NULL;
}
BOOL
LoadRequiredDwmFunctions (void)
{
if (dwmApi) return TRUE;
dwmApi = LoadLibraryW (L"dwmapi");
if (! dwmApi) return FALSE;
DwmGetWindowAttribute = (DWMGETWINDOWATTRIBUTE) GetProcAddress (dwmApi, "DwmGetWindowAttribute");
DwmIsCompositionEnabled = (DWMISCOMPOSITIONENABLED) GetProcAddress (dwmApi, "DwmIsCompositionEnabled");
if (! (DwmGetWindowAttribute && DwmIsCompositionEnabled))
{
UnloadRequiredDwmFunctions ();
return FALSE;
}
return TRUE;
}

View File

@ -0,0 +1,162 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Magnification-win32.h
* Copyright (C) 2018 Gil Eliyahu
*
* 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 <winapifamily.h>
#pragma region Desktop Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#ifndef __wincodec_h__
#include <wincodec.h>
#endif
#define WC_MAGNIFIERA "Magnifier"
#define WC_MAGNIFIERW L"Magnifier"
#ifdef UNICODE
#define WC_MAGNIFIER WC_MAGNIFIERW
#else
#define WC_MAGNIFIER WC_MAGNIFIERA
#endif
#else
#define WC_MAGNIFIER "Magnifier"
#endif
/* Magnifier Window Styles */
#define MS_SHOWMAGNIFIEDCURSOR 0x0001L
#define MS_CLIPAROUNDCURSOR 0x0002L
#define MS_INVERTCOLORS 0x0004L
/* Filter Modes */
#define MW_FILTERMODE_EXCLUDE 0
#define MW_FILTERMODE_INCLUDE 1
/* Structures */
typedef struct tagMAGTRANSFORM
{
float v[3][3];
} MAGTRANSFORM, *PMAGTRANSFORM;
typedef struct tagMAGIMAGEHEADER
{
UINT width;
UINT height;
WICPixelFormatGUID format;
UINT stride;
UINT offset;
SIZE_T cbSize;
} MAGIMAGEHEADER, *PMAGIMAGEHEADER;
typedef struct tagMAGCOLOREFFECT
{
float transform[5][5];
} MAGCOLOREFFECT, *PMAGCOLOREFFECT;
/* Proptypes for the public functions */
typedef BOOL (WINAPI* MAGINITIALIZE) ();
MAGINITIALIZE MagInitialize;
typedef BOOL (WINAPI* MAGUNINITIALIZE) ();
MAGUNINITIALIZE MagUninitialize;
typedef BOOL (WINAPI* MAGSETWINDOWSOURCE) (HWND, RECT);
MAGSETWINDOWSOURCE MagSetWindowSource;
typedef BOOL (WINAPI* MAGSETWINDOWFILTERLIST) (HWND, DWORD, int, HWND*);
MAGSETWINDOWFILTERLIST MagSetWindowFilterList;
typedef BOOL (CALLBACK* MagImageScalingCallback) (HWND hwnd, void * srcdata, MAGIMAGEHEADER srcheader, void * destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty);
typedef BOOL (WINAPI* MAGSETIMAGESCALINGCALLBACK) (HWND, MagImageScalingCallback);
MAGSETIMAGESCALINGCALLBACK MagSetImageScalingCallback;
/* Library DLL */
static HINSTANCE magnificationLibrary;
void UnLoadMagnificationLibrary (void);
BOOL LoadMagnificationLibrary (void);
void
UnLoadMagnificationLibrary (void)
{
if (!magnificationLibrary)
return;
FreeLibrary (magnificationLibrary);
}
BOOL
LoadMagnificationLibrary (void)
{
if (magnificationLibrary)
return TRUE;
magnificationLibrary = LoadLibraryW (L"Magnification");
if (!magnificationLibrary)
return FALSE;
MagInitialize = (MAGINITIALIZE) GetProcAddress (magnificationLibrary, "MagInitialize");
if (!MagInitialize)
{
UnLoadMagnificationLibrary ();
return FALSE;
}
MagUninitialize = (MAGUNINITIALIZE) GetProcAddress (magnificationLibrary, "MagUninitialize");
if (!MagUninitialize)
{
UnLoadMagnificationLibrary ();
return FALSE;
}
MagSetWindowSource = (MAGSETWINDOWSOURCE) GetProcAddress (magnificationLibrary, "MagSetWindowSource");
if (!MagSetWindowSource)
{
UnLoadMagnificationLibrary ();
return FALSE;
}
MagSetWindowFilterList = (MAGSETWINDOWFILTERLIST) GetProcAddress (magnificationLibrary, "MagSetWindowFilterList");
if (!MagSetWindowFilterList)
{
UnLoadMagnificationLibrary ();
return FALSE;
}
MagSetImageScalingCallback = (MAGSETIMAGESCALINGCALLBACK) GetProcAddress (magnificationLibrary, "MagSetImageScalingCallback");
if (!MagSetImageScalingCallback)
{
UnLoadMagnificationLibrary ();
return FALSE;
}
return TRUE;
}

View File

@ -0,0 +1,26 @@
/* {{NO_DEPENDENCIES}}
Microsoft Developer Studio generated include file.
Used by snappy.rc
*/
#define IDM_CAPTURE 100
#define IDM_EXIT 101
#define IDC_SELECT 102
#define IDD_SELECT 103
#define IDC_TEXT 1000
#define IDC_GROUP 1001
#define IDM_CAPTUREFULL 40003
#define IDM_HOOK 40004
#define IDM_UNHOOK 40005
/* Next default values for new objects */
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 104
#define _APS_NEXT_COMMAND_VALUE 40006
#define _APS_NEXT_CONTROL_VALUE 1002
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#ifndef __SCREENSHOT_WIN32_H__
#define __SCREENSHOT_WIN32_H__
#ifdef G_OS_WIN32
#define IDC_STATIC -1
#define IDS_APP_TITLE 500
#define IDS_DISPLAYCHANGED 501
#define IDS_VER_INFO_LANG 502
#define IDS_VERSION_ERROR 503
#define IDS_NO_HELP 504
gboolean screenshot_win32_available (void);
ScreenshotCapabilities screenshot_win32_get_capabilities (void);
PikaPDBStatusType screenshot_win32_shoot (ScreenshotValues *shootvals,
GdkMonitor *monitor,
PikaImage **image,
GError **error);
#endif /* G_OS_WIN32 */
#endif /* __SCREENSHOT_WIN32_H__ */

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,189 @@
//Microsoft Developer Studio generated resource script.
//
#include "screenshot-win32-resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#define APSTUDIO_HIDDEN_SYMBOLS
#include "windows.h"
#undef APSTUDIO_HIDDEN_SYMBOLS
#include "screenshot-win32.h"
#include "winver.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(65001)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
SNAPPY ICON DISCARDABLE "screenshot-win32.ico"
SMALL ICON DISCARDABLE "screenshot-win32-small.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
SNAPPY MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Capture", IDM_CAPTURE
MENUITEM "Capture Fullscreen", IDM_CAPTUREFULL
MENUITEM SEPARATOR
MENUITEM "Begin Hook", IDM_HOOK
MENUITEM "End Hook", IDM_UNHOOK
MENUITEM SEPARATOR
MENUITEM "E&xit", IDM_EXIT
END
END
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
"#include ""windows.h""\r\n"
"#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
"#include ""screenshot-win32.h""\r\n"
"#include ""winver.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Cursor
//
IDC_SELECT CURSOR DISCARDABLE "screenshot-win32-select.cur"
#ifndef _MAC
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "000004b0"
BEGIN
VALUE "Comments", "\0"
VALUE "CompanyName", "SeteraSoft\0"
VALUE "FileDescription", "snappy\0"
VALUE "FileVersion", "1, 0, 0, 1\0"
VALUE "InternalName", "snappy\0"
VALUE "LegalCopyright", "Copyright © 1999\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "snappy.exe\0"
VALUE "PrivateBuild", "\0"
VALUE "ProductName", "SeteraSoft snappy\0"
VALUE "ProductVersion", "1, 0, 0, 1\0"
VALUE "SpecialBuild", "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0, 1200
END
END
#endif // !_MAC
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_SELECT DIALOG DISCARDABLE 0, 0, 141, 95
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Select Window"
FONT 8, "MS Sans Serif"
BEGIN
PUSHBUTTON "Cancel",IDCANCEL,44,74,50,14
CTEXT "Drag crosshair to select window",IDC_TEXT,7,7,127,8
GROUPBOX "",IDC_GROUP,51,25,37,37,NOT WS_VISIBLE
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
IDD_SELECT, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 134
TOPMARGIN, 7
BOTTOMMARGIN, 88
END
END
#endif // APSTUDIO_INVOKED
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@ -0,0 +1,702 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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
*
* 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 <libpika/pika.h>
#include <libpika/pikaui.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
#include <X11/extensions/shape.h>
#endif
#ifdef HAVE_X11_XMU_WINUTIL_H
#include <X11/Xmu/WinUtil.h>
#endif
#ifdef HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
#endif
#include "screenshot.h"
#include "screenshot-x11.h"
#include "libpika/stdplugins-intl.h"
static guint32 select_window (ScreenshotValues *shootvals,
GdkMonitor *monitor);
static PikaImage * create_image (cairo_surface_t *surface,
cairo_region_t *shape,
const gchar *name);
/* Allow the user to select a window or a region with the mouse */
static guint32
select_window (ScreenshotValues *shootvals,
GdkMonitor *monitor)
{
Display *x_dpy = GDK_DISPLAY_XDISPLAY (gdk_monitor_get_display (monitor));
gint x_scr = 0;
Window x_root = RootWindow (x_dpy, x_scr);
Window x_win = None;
GC x_gc = NULL;
Cursor x_cursor = XCreateFontCursor (x_dpy, GDK_CROSSHAIR);
GdkKeymap *keymap;
GdkKeymapKey *keys = NULL;
gint status;
gint num_keys;
gint i;
gint buttons = 0;
gint mask = ButtonPressMask | ButtonReleaseMask;
gboolean cancel = FALSE;
if (shootvals->shoot_type == SHOOT_REGION)
mask |= PointerMotionMask;
status = XGrabPointer (x_dpy, x_root, False,
mask, GrabModeSync, GrabModeAsync,
x_root, x_cursor, CurrentTime);
if (status != GrabSuccess)
{
gint x, y;
guint xmask;
/* if we can't grab the pointer, return the window under the pointer */
XQueryPointer (x_dpy, x_root, &x_root, &x_win, &x, &y, &x, &y, &xmask);
if (x_win == None || x_win == x_root)
g_message (_("Error selecting the window"));
}
if (shootvals->shoot_type == SHOOT_REGION)
{
XGCValues gc_values;
gc_values.function = GXxor;
gc_values.plane_mask = AllPlanes;
gc_values.foreground = WhitePixel (x_dpy, x_scr);
gc_values.background = BlackPixel (x_dpy, x_scr);
gc_values.line_width = 0;
gc_values.line_style = LineSolid;
gc_values.fill_style = FillSolid;
gc_values.cap_style = CapButt;
gc_values.join_style = JoinMiter;
gc_values.graphics_exposures = FALSE;
gc_values.clip_x_origin = 0;
gc_values.clip_y_origin = 0;
gc_values.clip_mask = None;
gc_values.subwindow_mode = IncludeInferiors;
x_gc = XCreateGC (x_dpy, x_root,
GCFunction | GCPlaneMask | GCForeground | GCLineWidth |
GCLineStyle | GCCapStyle | GCJoinStyle |
GCGraphicsExposures | GCBackground | GCFillStyle |
GCClipXOrigin | GCClipYOrigin | GCClipMask |
GCSubwindowMode,
&gc_values);
}
keymap = gdk_keymap_get_for_display (gdk_monitor_get_display (monitor));
if (gdk_keymap_get_entries_for_keyval (keymap, GDK_KEY_Escape,
&keys, &num_keys))
{
GdkDisplay *display = gdk_monitor_get_display (monitor);
gdk_x11_display_error_trap_push (display);
#define X_GRAB_KEY(index, modifiers) \
XGrabKey (x_dpy, keys[index].keycode, modifiers, x_root, False, \
GrabModeAsync, GrabModeAsync)
for (i = 0; i < num_keys; i++)
{
X_GRAB_KEY (i, 0);
X_GRAB_KEY (i, LockMask); /* CapsLock */
X_GRAB_KEY (i, Mod2Mask); /* NumLock */
X_GRAB_KEY (i, Mod5Mask); /* ScrollLock */
X_GRAB_KEY (i, LockMask | Mod2Mask); /* CapsLock + NumLock */
X_GRAB_KEY (i, LockMask | Mod5Mask); /* CapsLock + ScrollLock */
X_GRAB_KEY (i, Mod2Mask | Mod5Mask); /* NumLock + ScrollLock */
X_GRAB_KEY (i, LockMask | Mod2Mask | Mod5Mask); /* all */
}
#undef X_GRAB_KEY
gdk_display_flush (display);
if (gdk_x11_display_error_trap_pop (display))
{
/* ignore errors */
}
}
while (! cancel && ((x_win == None) || (buttons != 0)))
{
XEvent x_event;
gint x, y, w, h;
XAllowEvents (x_dpy, SyncPointer, CurrentTime);
XWindowEvent (x_dpy, x_root, mask | KeyPressMask, &x_event);
switch (x_event.type)
{
case ButtonPress:
if (x_win == None)
{
x_win = x_event.xbutton.subwindow;
if (x_win == None)
x_win = x_root;
#ifdef HAVE_X11_XMU_WINUTIL_H
else if (! shootvals->decorate)
x_win = XmuClientWindow (x_dpy, x_win);
#endif
shootvals->x2 = shootvals->x1 = x_event.xbutton.x_root;
shootvals->y2 = shootvals->y1 = x_event.xbutton.y_root;
}
buttons++;
break;
case ButtonRelease:
if (buttons > 0)
buttons--;
if (! buttons && shootvals->shoot_type == SHOOT_REGION)
{
x = MIN (shootvals->x1, shootvals->x2);
y = MIN (shootvals->y1, shootvals->y2);
w = ABS (shootvals->x2 - shootvals->x1);
h = ABS (shootvals->y2 - shootvals->y1);
if (w > 0 && h > 0)
XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
shootvals->x2 = x_event.xbutton.x_root;
shootvals->y2 = x_event.xbutton.y_root;
}
break;
case MotionNotify:
if (buttons > 0)
{
x = MIN (shootvals->x1, shootvals->x2);
y = MIN (shootvals->y1, shootvals->y2);
w = ABS (shootvals->x2 - shootvals->x1);
h = ABS (shootvals->y2 - shootvals->y1);
if (w > 0 && h > 0)
XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
shootvals->x2 = x_event.xmotion.x_root;
shootvals->y2 = x_event.xmotion.y_root;
x = MIN (shootvals->x1, shootvals->x2);
y = MIN (shootvals->y1, shootvals->y2);
w = ABS (shootvals->x2 - shootvals->x1);
h = ABS (shootvals->y2 - shootvals->y1);
if (w > 0 && h > 0)
XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
}
break;
case KeyPress:
{
guint *keyvals;
gint n;
if (gdk_keymap_get_entries_for_keycode (NULL, x_event.xkey.keycode,
NULL, &keyvals, &n))
{
gint i;
for (i = 0; i < n && ! cancel; i++)
if (keyvals[i] == GDK_KEY_Escape)
cancel = TRUE;
g_free (keyvals);
}
}
break;
default:
break;
}
}
if (keys)
{
#define X_UNGRAB_KEY(index, modifiers) \
XUngrabKey (x_dpy, keys[index].keycode, modifiers, x_root)
for (i = 0; i < num_keys; i++)
{
X_UNGRAB_KEY (i, 0);
X_UNGRAB_KEY (i, LockMask); /* CapsLock */
X_UNGRAB_KEY (i, Mod2Mask); /* NumLock */
X_UNGRAB_KEY (i, Mod5Mask); /* ScrollLock */
X_UNGRAB_KEY (i, LockMask | Mod2Mask); /* CapsLock + NumLock */
X_UNGRAB_KEY (i, LockMask | Mod5Mask); /* CapsLock + ScrollLock */
X_UNGRAB_KEY (i, Mod2Mask | Mod5Mask); /* NumLock + ScrollLock */
X_UNGRAB_KEY (i, LockMask | Mod2Mask | Mod5Mask); /* all */
}
#undef X_UNGRAB_KEY
g_free (keys);
}
if (status == GrabSuccess)
XUngrabPointer (x_dpy, CurrentTime);
XFreeCursor (x_dpy, x_cursor);
if (x_gc != NULL)
XFreeGC (x_dpy, x_gc);
return x_win;
}
static gchar *
window_get_utf8_property (GdkDisplay *display,
guint32 window,
const gchar *name)
{
gchar *retval = NULL;
Atom utf8_string;
Atom type = None;
guchar *val = NULL;
gulong nitems = 0;
gulong after = 0;
gint format = 0;
utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), window,
gdk_x11_get_xatom_by_name_for_display (display, name),
0, G_MAXLONG, False, utf8_string,
&type, &format, &nitems, &after, &val);
if (type != utf8_string || format != 8 || nitems == 0)
{
if (val)
XFree (val);
return NULL;
}
if (g_utf8_validate ((const gchar *) val, nitems, NULL))
retval = g_strndup ((const gchar *) val, nitems);
XFree (val);
return retval;
}
static gchar *
window_get_title (GdkDisplay *display,
guint window)
{
#ifdef HAVE_X11_XMU_WINUTIL_H
window = XmuClientWindow (GDK_DISPLAY_XDISPLAY (display), window);
#endif
return window_get_utf8_property (display, window, "_NET_WM_NAME");
}
static cairo_region_t *
window_get_shape (GdkMonitor *monitor,
guint32 window)
{
cairo_region_t *shape = NULL;
#if defined(HAVE_X11_EXTENSIONS_SHAPE_H)
XRectangle *rects;
gint rect_count;
gint rect_order;
rects = XShapeGetRectangles (GDK_DISPLAY_XDISPLAY (gdk_monitor_get_display (monitor)),
window,
ShapeBounding,
&rect_count, &rect_order);
if (rects)
{
if (rect_count > 1)
{
gint i;
shape = cairo_region_create ();
for (i = 0; i < rect_count; i++)
{
cairo_rectangle_int_t rect = { rects[i].x,
rects[i].y,
rects[i].width,
rects[i].height };
cairo_region_union_rectangle (shape, &rect);
}
}
XFree (rects);
}
#endif
return shape;
}
static void
image_select_shape (PikaImage *image,
cairo_region_t *shape)
{
gint num_rects;
gint i;
pika_selection_none (image);
num_rects = cairo_region_num_rectangles (shape);
for (i = 0; i < num_rects; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (shape, i, &rect);
pika_image_select_rectangle (image, PIKA_CHANNEL_OP_ADD,
rect.x, rect.y,
rect.width, rect.height);
}
pika_selection_invert (image);
}
/* Create a PikaImage from a GdkPixbuf */
static PikaImage *
create_image (cairo_surface_t *surface,
cairo_region_t *shape,
const gchar *name)
{
PikaImage *image;
PikaLayer *layer;
gdouble xres, yres;
gint width, height;
pika_progress_init (_("Importing screenshot"));
width = cairo_image_surface_get_width (surface);
height = cairo_image_surface_get_height (surface);
image = pika_image_new (width, height, PIKA_RGB);
pika_image_undo_disable (image);
pika_get_monitor_resolution (&xres, &yres);
pika_image_set_resolution (image, xres, yres);
layer = pika_layer_new_from_surface (image,
name ? name : _("Screenshot"),
surface,
0.0, 1.0);
pika_image_insert_layer (image, layer, NULL, 0);
if (shape && ! cairo_region_is_empty (shape))
{
image_select_shape (image, shape);
if (! pika_selection_is_empty (image))
{
pika_layer_add_alpha (layer);
pika_drawable_edit_clear (PIKA_DRAWABLE (layer));
pika_selection_none (image);
}
}
pika_image_undo_enable (image);
return image;
}
static void
add_cursor_image (PikaImage *image,
GdkDisplay *display)
{
#ifdef HAVE_XFIXES
XFixesCursorImage *cursor;
GeglBuffer *buffer;
GeglBufferIterator *iter;
GeglRectangle *roi;
PikaLayer *layer;
GList *selected;
cursor = XFixesGetCursorImage (GDK_DISPLAY_XDISPLAY (display));
if (!cursor)
return;
selected = pika_image_list_selected_layers (image);
layer = pika_layer_new (image, _("Mouse Pointer"),
cursor->width, cursor->height,
PIKA_RGBA_IMAGE,
100.0,
pika_image_get_default_new_layer_mode (image));
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
iter = gegl_buffer_iterator_new (buffer,
GEGL_RECTANGLE (0, 0,
pika_drawable_get_width (PIKA_DRAWABLE (layer)),
pika_drawable_get_height (PIKA_DRAWABLE (layer))),
0, babl_format ("R'G'B'A u8"),
GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
roi = &iter->items[0].roi;
while (gegl_buffer_iterator_next (iter))
{
const gulong *src = cursor->pixels + roi->y * cursor->width + roi->x;
guchar *dest = iter->items[0].data;
gint x, y;
for (y = 0; y < roi->height; y++)
{
const gulong *s = src;
guchar *d = dest;
for (x = 0; x < roi->width; x++)
{
/* the cursor pixels are pre-multiplied ARGB */
guint a = (*s >> 24) & 0xff;
guint r = (*s >> 16) & 0xff;
guint g = (*s >> 8) & 0xff;
guint b = (*s >> 0) & 0xff;
d[0] = a ? (r * 255) / a : r;
d[1] = a ? (g * 255) / a : g;
d[2] = a ? (b * 255) / a : b;
d[3] = a;
s++;
d += 4;
}
src += cursor->width;
dest += 4 * roi->width;
}
}
g_object_unref (buffer);
pika_image_insert_layer (image, layer, NULL, -1);
pika_layer_set_offsets (layer,
cursor->x - cursor->xhot, cursor->y - cursor->yhot);
pika_image_take_selected_layers (image, selected);
#endif
}
/* The main Screenshot function */
gboolean
screenshot_x11_available (void)
{
return (gdk_display_get_default () &&
GDK_IS_X11_DISPLAY (gdk_display_get_default ()));
}
ScreenshotCapabilities
screenshot_x11_get_capabilities (void)
{
ScreenshotCapabilities capabilities = SCREENSHOT_CAN_PICK_NONINTERACTIVELY;
#ifdef HAVE_X11_XMU_WINUTIL_H
capabilities |= SCREENSHOT_CAN_SHOOT_DECORATIONS;
#endif
#ifdef HAVE_XFIXES
capabilities |= SCREENSHOT_CAN_SHOOT_POINTER;
#endif
capabilities |= SCREENSHOT_CAN_SHOOT_REGION |
SCREENSHOT_CAN_SHOOT_WINDOW |
SCREENSHOT_CAN_PICK_WINDOW |
SCREENSHOT_CAN_DELAY_WINDOW_SHOT;
return capabilities;
}
PikaPDBStatusType
screenshot_x11_shoot (ScreenshotValues *shootvals,
GdkMonitor *monitor,
PikaImage **image,
GError **error)
{
GdkDisplay *display;
GdkScreen *screen;
GdkWindow *window;
cairo_surface_t *screenshot;
cairo_region_t *shape = NULL;
cairo_t *cr;
PikaColorProfile *profile;
GdkRectangle rect;
GdkRectangle screen_rect;
gchar *name = NULL;
gint x, y;
/* use default screen if we are running non-interactively */
if (monitor == NULL)
monitor = gdk_display_get_monitor (gdk_display_get_default (), 0);
if (shootvals->shoot_type != SHOOT_ROOT && ! shootvals->window_id)
{
if (shootvals->select_delay > 0)
screenshot_delay (shootvals->select_delay);
shootvals->window_id = select_window (shootvals, monitor);
if (! shootvals->window_id)
return PIKA_PDB_CANCEL;
}
if (shootvals->screenshot_delay > 0)
screenshot_delay (shootvals->screenshot_delay);
display = gdk_monitor_get_display (monitor);
screen = gdk_display_get_default_screen (display);
if (shootvals->shoot_type == SHOOT_REGION)
{
rect.x = MIN (shootvals->x1, shootvals->x2);
rect.y = MIN (shootvals->y1, shootvals->y2);
rect.width = ABS (shootvals->x2 - shootvals->x1);
rect.height = ABS (shootvals->y2 - shootvals->y1);
monitor = gdk_display_get_monitor_at_point (display,
rect.x + rect.width / 2,
rect.y + rect.height / 2);
}
else
{
if (shootvals->shoot_type == SHOOT_ROOT)
{
window = gdk_screen_get_root_window (screen);
/* FIXME: figure monitor */
}
else
{
window = gdk_x11_window_foreign_new_for_display (display,
shootvals->window_id);
monitor = gdk_display_get_monitor_at_window (display, window);
}
if (! window)
{
g_set_error_literal (error, 0, 0, _("Specified window not found"));
return PIKA_PDB_EXECUTION_ERROR;
}
rect.width = gdk_window_get_width (window);
rect.height = gdk_window_get_height (window);
gdk_window_get_origin (window, &x, &y);
rect.x = x;
rect.y = y;
}
window = gdk_screen_get_root_window (screen);
gdk_window_get_origin (window, &screen_rect.x, &screen_rect.y);
screen_rect.width = gdk_window_get_width (window);
screen_rect.height = gdk_window_get_height (window);
if (! gdk_rectangle_intersect (&rect, &screen_rect, &rect))
return PIKA_PDB_EXECUTION_ERROR;
screenshot = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
rect.width, rect.height);
cr = cairo_create (screenshot);
gdk_cairo_set_source_window (cr, window,
- (rect.x - screen_rect.x),
- (rect.y - screen_rect.y));
cairo_paint (cr);
cairo_destroy (cr);
gdk_display_beep (display);
if (shootvals->shoot_type == SHOOT_WINDOW)
{
name = window_get_title (display, shootvals->window_id);
shape = window_get_shape (monitor, shootvals->window_id);
if (shape)
cairo_region_translate (shape, x - rect.x, y - rect.y);
}
*image = create_image (screenshot, shape, name);
cairo_surface_destroy (screenshot);
if (shape)
cairo_region_destroy (shape);
g_free (name);
/* FIXME: Some time might have passed until we get here.
* The cursor image should be grabbed together with the screenshot.
*/
if ((shootvals->shoot_type == SHOOT_ROOT ||
shootvals->shoot_type == SHOOT_WINDOW) && shootvals->show_cursor)
add_cursor_image (*image, display);
profile = pika_monitor_get_color_profile (monitor);
if (profile)
{
pika_image_set_color_profile (*image, profile);
g_object_unref (profile);
}
return PIKA_PDB_SUCCESS;
}
#endif /* GDK_WINDOWING_X11 */

View File

@ -0,0 +1,40 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#ifndef __SCREENSHOT_X11_H__
#define __SCREENSHOT_X11_H__
#ifdef GDK_WINDOWING_X11
gboolean screenshot_x11_available (void);
ScreenshotCapabilities screenshot_x11_get_capabilities (void);
PikaPDBStatusType screenshot_x11_shoot (ScreenshotValues *shootvals,
GdkMonitor *monitor,
PikaImage **image,
GError **error);
#endif /* GDK_WINDOWING_X11 */
#endif /* __SCREENSHOT_X11_H__ */

View File

@ -0,0 +1,891 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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 2016 Michael Natterer <mitch@gimp.org>
*
* 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 <libpika/pika.h>
#include <libpika/pikaui.h>
#include "screenshot.h"
#include "screenshot-freedesktop.h"
#include "screenshot-osx.h"
#include "screenshot-x11.h"
#include "screenshot-win32.h"
#include "libpika/stdplugins-intl.h"
/* Defines */
#define PLUG_IN_PROC "plug-in-screenshot"
#define PLUG_IN_BINARY "screenshot"
#define PLUG_IN_ROLE "pika-screenshot"
typedef struct _Screenshot Screenshot;
typedef struct _ScreenshotClass ScreenshotClass;
struct _Screenshot
{
PikaPlugIn parent_instance;
};
struct _ScreenshotClass
{
PikaPlugInClass parent_class;
};
#define SCREENSHOT_TYPE (screenshot_get_type ())
#define SCREENSHOT (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SCREENSHOT_TYPE, Screenshot))
GType screenshot_get_type (void) G_GNUC_CONST;
static GList * screenshot_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * screenshot_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * screenshot_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data);
static PikaPDBStatusType shoot (GdkMonitor *monitor,
PikaImage **image,
GError **error);
static gboolean shoot_dialog (GdkMonitor **monitor);
static gboolean shoot_quit_timeout (gpointer data);
static gboolean shoot_delay_timeout (gpointer data);
G_DEFINE_TYPE (Screenshot, screenshot, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (SCREENSHOT_TYPE)
DEFINE_STD_SET_I18N
static ScreenshotBackend backend = SCREENSHOT_BACKEND_NONE;
static ScreenshotCapabilities capabilities = 0;
static GtkWidget *select_delay_grid = NULL;
static GtkWidget *shot_delay_grid = NULL;
static ScreenshotValues shootvals =
{
SHOOT_WINDOW, /* root window */
TRUE, /* include WM decorations */
0, /* window ID */
0, /* select delay */
0, /* screenshot delay */
0, /* coords of region dragged out by pointer */
0,
0,
0,
FALSE, /* show cursor */
SCREENSHOT_PROFILE_POLICY_MONITOR
};
static void
screenshot_class_init (ScreenshotClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = screenshot_query_procedures;
plug_in_class->create_procedure = screenshot_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
screenshot_init (Screenshot *screenshot)
{
}
static GList *
screenshot_query_procedures (PikaPlugIn *plug_in)
{
return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}
static PikaProcedure *
screenshot_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, PLUG_IN_PROC))
{
procedure = pika_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
screenshot_run, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("_Screenshot..."));
pika_procedure_add_menu_path (procedure, "<Image>/File/Create");
pika_procedure_set_documentation
(procedure,
_("Create an image from an area of the screen"),
"The plug-in takes screenshots of an "
"interactively selected window or of the desktop, "
"either the whole desktop or an interactively "
"selected region. When called non-interactively, it "
"may grab the root window or use the window-id "
"passed as a parameter. The last four parameters "
"are optional and can be used to specify the corners "
"of the region to be grabbed."
"On Mac OS X, "
"when called non-interactively, the plug-in"
"only can take screenshots of the entire root window."
"Grabbing a window or a region is not supported"
"non-interactively. To grab a region or a particular"
"window, you need to use the interactive mode.",
name);
pika_procedure_set_attribution (procedure,
"Sven Neumann <sven@gimp.org>, "
"Henrik Brix Andersen <brix@gimp.org>,"
"Simone Karin Lehmann",
"1998 - 2008",
"v1.1 (2008/04)");
pika_procedure_set_icon_pixbuf (procedure,
gdk_pixbuf_new_from_resource ("/technology.heckin/screenshot-icons/screenshot-icon.png", NULL));
PIKA_PROC_ARG_ENUM (procedure, "run-mode",
"Run mode",
"The run mode",
PIKA_TYPE_RUN_MODE,
PIKA_RUN_NONINTERACTIVE,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "shoot-type",
"Shoot type",
"The shoot type { SHOOT-WINDOW (0), SHOOT-ROOT (1), "
"SHOOT-REGION (2) }",
0, 2, 0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "x1",
"X1",
"Region left x coord for SHOOT-WINDOW",
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "y1",
"Y1",
"Region top y coord for SHOOT-WINDOW",
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "x2",
"X2",
"Region right x coord for SHOOT-WINDOW",
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "y2",
"Y2",
"Region bottom y coord for SHOOT-WINDOW",
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE);
PIKA_PROC_VAL_IMAGE (procedure, "image",
"Image",
"Output image",
FALSE,
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
screenshot_run (PikaProcedure *procedure,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
PikaRunMode run_mode;
GdkMonitor *monitor = NULL;
PikaImage *image = NULL;
GError *error = NULL;
gegl_init (NULL, NULL);
run_mode = PIKA_VALUES_GET_ENUM (args, 0);
pika_ui_init (PLUG_IN_BINARY);
#ifdef PLATFORM_OSX
if (! backend && screenshot_osx_available ())
{
backend = SCREENSHOT_BACKEND_OSX;
capabilities = screenshot_osx_get_capabilities ();
/* on OS X, this just means shoot the shadow, default to nope */
shootvals.decorate = FALSE;
}
#endif
#ifdef G_OS_WIN32
if (! backend && screenshot_win32_available ())
{
backend = SCREENSHOT_BACKEND_WIN32;
capabilities = screenshot_win32_get_capabilities ();
}
#endif
#ifdef GDK_WINDOWING_X11
if (! backend && screenshot_x11_available ())
{
backend = SCREENSHOT_BACKEND_X11;
capabilities = screenshot_x11_get_capabilities ();
}
#endif
if (! backend && screenshot_freedesktop_available ())
{
backend = SCREENSHOT_BACKEND_FREEDESKTOP;
capabilities = screenshot_freedesktop_get_capabilities ();
}
/* how are we running today? */
switch (run_mode)
{
case PIKA_RUN_INTERACTIVE:
/* Possibly retrieve data from a previous run */
pika_get_data (PLUG_IN_PROC, &shootvals);
shootvals.window_id = 0;
if ((shootvals.shoot_type == SHOOT_WINDOW &&
! (capabilities & SCREENSHOT_CAN_SHOOT_WINDOW)) ||
(shootvals.shoot_type == SHOOT_REGION &&
! (capabilities & SCREENSHOT_CAN_SHOOT_REGION)))
{
/* Shoot root is the only type of shoot which is definitely
* shared by all screenshot backends (basically just snap the
* whole display setup).
*/
shootvals.shoot_type = SHOOT_ROOT;
}
/* Get information from the dialog. Freedesktop portal comes with
* its own dialog.
*/
if (backend != SCREENSHOT_BACKEND_FREEDESKTOP)
{
if (! shoot_dialog (&monitor))
status = PIKA_PDB_CANCEL;
}
else
{
/* This is ugly but in reality we have no idea on which monitor
* a screenshot was taken from with portals. It's like a
* better-than-nothing trick for easy single-display cases.
*/
monitor = gdk_display_get_monitor (gdk_display_get_default (), 0);
}
break;
case PIKA_RUN_NONINTERACTIVE:
shootvals.shoot_type = PIKA_VALUES_GET_INT (args, 1);
shootvals.window_id = PIKA_VALUES_GET_INT (args, 2);
shootvals.select_delay = 0;
shootvals.x1 = PIKA_VALUES_GET_INT (args, 3);
shootvals.y1 = PIKA_VALUES_GET_INT (args, 4);
shootvals.x2 = PIKA_VALUES_GET_INT (args, 5);
shootvals.y2 = PIKA_VALUES_GET_INT (args, 6);
if (! gdk_init_check (NULL, NULL))
status = PIKA_PDB_CALLING_ERROR;
if (! (capabilities & SCREENSHOT_CAN_PICK_NONINTERACTIVELY))
{
if (shootvals.shoot_type == SHOOT_WINDOW ||
shootvals.shoot_type == SHOOT_REGION)
{
status = PIKA_PDB_CALLING_ERROR;
}
}
break;
case PIKA_RUN_WITH_LAST_VALS:
/* Possibly retrieve data from a previous run */
pika_get_data (PLUG_IN_PROC, &shootvals);
break;
default:
break;
}
if (status == PIKA_PDB_SUCCESS)
{
status = shoot (monitor, &image, &error);
}
if (status == PIKA_PDB_SUCCESS)
{
gchar *comment = pika_get_default_comment ();
pika_image_undo_disable (image);
if (shootvals.profile_policy == SCREENSHOT_PROFILE_POLICY_SRGB)
{
PikaColorProfile *srgb_profile = pika_color_profile_new_rgb_srgb ();
pika_image_convert_color_profile (image,
srgb_profile,
PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
TRUE);
g_object_unref (srgb_profile);
}
if (comment)
{
PikaParasite *parasite;
parasite = pika_parasite_new ("pika-comment",
PIKA_PARASITE_PERSISTENT,
strlen (comment) + 1, comment);
pika_image_attach_parasite (image, parasite);
pika_parasite_free (parasite);
g_free (comment);
}
pika_image_undo_enable (image);
if (run_mode == PIKA_RUN_INTERACTIVE)
{
/* Store variable states for next run */
pika_set_data (PLUG_IN_PROC, &shootvals, sizeof (ScreenshotValues));
pika_display_new (image);
/* Give some sort of feedback that the shot is done */
if (shootvals.select_delay > 0)
{
gdk_display_beep (gdk_monitor_get_display (monitor));
/* flush so the beep makes it to the server */
gdk_display_flush (gdk_monitor_get_display (monitor));
}
}
}
return_vals = pika_procedure_new_return_values (procedure, status, error);
if (status == PIKA_PDB_SUCCESS)
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
/* The main Screenshot function */
static PikaPDBStatusType
shoot (GdkMonitor *monitor,
PikaImage **image,
GError **error)
{
#ifdef PLATFORM_OSX
if (backend == SCREENSHOT_BACKEND_OSX)
return screenshot_osx_shoot (&shootvals, monitor, image, error);
#endif
#ifdef G_OS_WIN32
if (backend == SCREENSHOT_BACKEND_WIN32)
return screenshot_win32_shoot (&shootvals, monitor, image, error);
#endif
if (backend == SCREENSHOT_BACKEND_FREEDESKTOP)
return screenshot_freedesktop_shoot (&shootvals, monitor, image, error);
#ifdef GDK_WINDOWING_X11
if (backend == SCREENSHOT_BACKEND_X11)
return screenshot_x11_shoot (&shootvals, monitor, image, error);
#endif
return PIKA_PDB_CALLING_ERROR; /* silence compiler */
}
/* Screenshot dialog */
static void
shoot_dialog_add_hint (GtkNotebook *notebook,
ShootType type,
const gchar *hint)
{
GtkWidget *label;
label = g_object_new (GTK_TYPE_LABEL,
"label", hint,
"wrap", TRUE,
"justify", GTK_JUSTIFY_LEFT,
"xalign", 0.0,
"yalign", 0.0,
NULL);
pika_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
-1);
gtk_notebook_insert_page (notebook, label, NULL, type);
gtk_widget_show (label);
}
static void
shoot_radio_button_toggled (GtkWidget *widget,
GtkWidget *notebook)
{
pika_radio_button_update (widget, &shootvals.shoot_type);
if (select_delay_grid)
{
if (shootvals.shoot_type == SHOOT_ROOT ||
(shootvals.shoot_type == SHOOT_WINDOW &&
! (capabilities & SCREENSHOT_CAN_PICK_WINDOW)))
{
gtk_widget_hide (select_delay_grid);
}
else
{
gtk_widget_show (select_delay_grid);
}
}
if (shot_delay_grid)
{
if (shootvals.shoot_type == SHOOT_WINDOW &&
(capabilities & SCREENSHOT_CAN_PICK_WINDOW) &&
! (capabilities & SCREENSHOT_CAN_DELAY_WINDOW_SHOT))
{
gtk_widget_hide (shot_delay_grid);
}
else
{
gtk_widget_show (shot_delay_grid);
}
}
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), shootvals.shoot_type);
}
static gboolean
shoot_dialog (GdkMonitor **monitor)
{
GtkWidget *dialog;
GtkWidget *main_vbox;
GtkWidget *notebook1;
GtkWidget *notebook2;
GtkWidget *frame;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *button;
GtkWidget *toggle;
GtkWidget *spinner;
GtkWidget *grid;
GSList *radio_group = NULL;
GtkAdjustment *adj;
gboolean run;
GtkWidget *cursor_toggle = NULL;
dialog = pika_dialog_new (_("Screenshot"), PLUG_IN_ROLE,
NULL, 0,
pika_standard_help_func, PLUG_IN_PROC,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("S_nap"), GTK_RESPONSE_OK,
NULL);
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
main_vbox, FALSE, FALSE, 0);
gtk_widget_show (main_vbox);
/* Create delay hints notebooks early */
notebook1 = g_object_new (GTK_TYPE_NOTEBOOK,
"show-border", FALSE,
"show-tabs", FALSE,
NULL);
notebook2 = g_object_new (GTK_TYPE_NOTEBOOK,
"show-border", FALSE,
"show-tabs", FALSE,
NULL);
/* Area */
frame = pika_frame_new (_("Area"));
gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_container_add (GTK_CONTAINER (frame), vbox);
gtk_widget_show (vbox);
/* Single window */
if (capabilities & SCREENSHOT_CAN_SHOOT_WINDOW)
{
button = gtk_radio_button_new_with_mnemonic (radio_group,
_("Take a screenshot of "
"a single _window"));
radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_object_set_data (G_OBJECT (button), "pika-item-data",
GINT_TO_POINTER (SHOOT_WINDOW));
g_signal_connect (button, "toggled",
G_CALLBACK (shoot_radio_button_toggled),
notebook1);
g_signal_connect (button, "toggled",
G_CALLBACK (shoot_radio_button_toggled),
notebook2);
/* Window decorations */
if (capabilities & SCREENSHOT_CAN_SHOOT_DECORATIONS)
{
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
toggle = gtk_check_button_new_with_mnemonic (_("Include window _decoration"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
shootvals.decorate);
gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 24);
gtk_widget_show (toggle);
g_signal_connect (toggle, "toggled",
G_CALLBACK (pika_toggle_button_update),
&shootvals.decorate);
g_object_bind_property (button, "active",
toggle, "sensitive",
G_BINDING_SYNC_CREATE);
}
/* Mouse pointer */
if (capabilities & SCREENSHOT_CAN_SHOOT_POINTER)
{
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
cursor_toggle = gtk_check_button_new_with_mnemonic (_("Include _mouse pointer"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cursor_toggle),
shootvals.show_cursor);
gtk_box_pack_start (GTK_BOX (hbox), cursor_toggle, TRUE, TRUE, 24);
gtk_widget_show (cursor_toggle);
g_signal_connect (cursor_toggle, "toggled",
G_CALLBACK (pika_toggle_button_update),
&shootvals.show_cursor);
g_object_bind_property (button, "active",
cursor_toggle, "sensitive",
G_BINDING_SYNC_CREATE);
}
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
shootvals.shoot_type == SHOOT_WINDOW);
}
/* Whole screen */
button = gtk_radio_button_new_with_mnemonic (radio_group,
_("Take a screenshot of "
"the entire _screen"));
radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_object_set_data (G_OBJECT (button), "pika-item-data",
GINT_TO_POINTER (SHOOT_ROOT));
g_signal_connect (button, "toggled",
G_CALLBACK (shoot_radio_button_toggled),
notebook1);
g_signal_connect (button, "toggled",
G_CALLBACK (shoot_radio_button_toggled),
notebook2);
/* Mouse pointer */
if (capabilities & SCREENSHOT_CAN_SHOOT_POINTER)
{
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
toggle = gtk_check_button_new_with_mnemonic (_("Include _mouse pointer"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
shootvals.show_cursor);
gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 24);
gtk_widget_show (toggle);
g_signal_connect (toggle, "toggled",
G_CALLBACK (pika_toggle_button_update),
&shootvals.show_cursor);
if (cursor_toggle)
{
g_object_bind_property (cursor_toggle, "active",
toggle, "active",
G_BINDING_BIDIRECTIONAL);
}
g_object_bind_property (button, "active",
toggle, "sensitive",
G_BINDING_SYNC_CREATE);
}
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
shootvals.shoot_type == SHOOT_ROOT);
/* Dragged region */
if (capabilities & SCREENSHOT_CAN_SHOOT_REGION)
{
button = gtk_radio_button_new_with_mnemonic (radio_group,
_("Select a _region to grab"));
radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
shootvals.shoot_type == SHOOT_REGION);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_object_set_data (G_OBJECT (button), "pika-item-data",
GINT_TO_POINTER (SHOOT_REGION));
g_signal_connect (button, "toggled",
G_CALLBACK (shoot_radio_button_toggled),
notebook1);
g_signal_connect (button, "toggled",
G_CALLBACK (shoot_radio_button_toggled),
notebook2);
}
frame = pika_frame_new (_("Delay"));
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
gtk_container_add (GTK_CONTAINER (frame), vbox);
gtk_widget_show (vbox);
/* Selection delay */
grid = gtk_grid_new ();
select_delay_grid = grid;
gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
/* Check if this delay must be hidden from start. */
if (shootvals.shoot_type == SHOOT_REGION ||
(shootvals.shoot_type == SHOOT_WINDOW &&
capabilities & SCREENSHOT_CAN_PICK_WINDOW))
{
gtk_widget_show (select_delay_grid);
}
label = gtk_label_new (_("Selection delay: "));
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
gtk_widget_show (label);
adj = gtk_adjustment_new (shootvals.select_delay,
0.0, 100.0, 1.0, 5.0, 0.0);
spinner = pika_spin_button_new (adj, 0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_grid_attach (GTK_GRID (grid), spinner, 1, 0, 1, 1);
gtk_widget_show (spinner);
g_signal_connect (adj, "value-changed",
G_CALLBACK (pika_int_adjustment_update),
&shootvals.select_delay);
/* translators: this is the unit label of a spinbutton */
label = gtk_label_new (_("seconds"));
gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_show (label);
/* Selection delay hints */
gtk_grid_attach (GTK_GRID (grid), notebook1, 0, 1, 3, 1);
gtk_widget_show (notebook1);
/* No selection delay for full-screen. */
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_ROOT, "");
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_REGION,
_("After the delay, drag your mouse to select "
"the region for the screenshot."));
#ifdef G_OS_WIN32
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_WINDOW,
_("Click in a window to snap it after delay."));
#else
if (capabilities & SCREENSHOT_CAN_PICK_WINDOW)
{
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_WINDOW,
_("At the end of the delay, click in a window "
"to snap it."));
}
else
{
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_WINDOW, "");
}
#endif
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook1), shootvals.shoot_type);
/* Screenshot delay */
grid = gtk_grid_new ();
shot_delay_grid = grid;
gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
if (shootvals.shoot_type != SHOOT_WINDOW ||
! (capabilities & SCREENSHOT_CAN_PICK_WINDOW) ||
(capabilities & SCREENSHOT_CAN_DELAY_WINDOW_SHOT))
{
gtk_widget_show (grid);
}
label = gtk_label_new_with_mnemonic (_("Screenshot dela_y: "));
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
gtk_widget_show (label);
adj = gtk_adjustment_new (shootvals.screenshot_delay,
0.0, 100.0, 1.0, 5.0, 0.0);
spinner = pika_spin_button_new (adj, 0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_grid_attach (GTK_GRID (grid), spinner, 1, 0, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (spinner));
gtk_widget_show (spinner);
g_signal_connect (adj, "value-changed",
G_CALLBACK (pika_int_adjustment_update),
&shootvals.screenshot_delay);
/* translators: this is the unit label of a spinbutton */
label = gtk_label_new (_("seconds"));
gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_show (label);
/* Screenshot delay hints */
gtk_grid_attach (GTK_GRID (grid), notebook2, 0, 1, 3, 1);
gtk_widget_show (notebook2);
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_ROOT,
_("After the delay, the screenshot is taken."));
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_REGION,
_("Once the region is selected, it will be "
"captured after this delay."));
if (capabilities & SCREENSHOT_CAN_PICK_WINDOW)
{
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_WINDOW,
_("Once the window is selected, it will be "
"captured after this delay."));
}
else
{
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_WINDOW,
_("After the delay, the active window "
"will be captured."));
}
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook2), shootvals.shoot_type);
/* Color profile */
frame = pika_int_radio_group_new (TRUE,
_("Color Profile"),
G_CALLBACK (pika_radio_button_update),
&shootvals.profile_policy, NULL,
shootvals.profile_policy,
_("Tag image with _monitor profile"),
SCREENSHOT_PROFILE_POLICY_MONITOR,
NULL,
_("Convert image to sR_GB"),
SCREENSHOT_PROFILE_POLICY_SRGB,
NULL,
NULL);
gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
gtk_widget_show (dialog);
run = (pika_dialog_run (PIKA_DIALOG (dialog)) == GTK_RESPONSE_OK);
if (run)
{
/* get the screen on which we are running */
*monitor = pika_widget_get_monitor (dialog);
}
gtk_widget_destroy (dialog);
if (run)
{
/* A short timeout to give the server a chance to
* redraw the area that was obscured by our dialog.
*/
g_timeout_add (100, shoot_quit_timeout, NULL);
gtk_main ();
}
return run;
}
static gboolean
shoot_quit_timeout (gpointer data)
{
gtk_main_quit ();
return FALSE;
}
static gboolean
shoot_delay_timeout (gpointer data)
{
gint *seconds_left = data;
(*seconds_left)--;
if (!*seconds_left)
gtk_main_quit ();
return *seconds_left;
}
/* public functions */
void
screenshot_delay (gint seconds)
{
g_timeout_add (1000, shoot_delay_timeout, &seconds);
gtk_main ();
}

View File

@ -0,0 +1,82 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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/>.
*/
#ifndef __SCREENSHOT_H__
#define __SCREENSHOT_H__
typedef enum
{
SCREENSHOT_BACKEND_NONE,
SCREENSHOT_BACKEND_OSX,
SCREENSHOT_BACKEND_WIN32,
SCREENSHOT_BACKEND_FREEDESKTOP,
SCREENSHOT_BACKEND_X11
} ScreenshotBackend;
typedef enum
{
SCREENSHOT_CAN_SHOOT_DECORATIONS = 0x1 << 0,
SCREENSHOT_CAN_SHOOT_POINTER = 0x1 << 1,
SCREENSHOT_CAN_PICK_NONINTERACTIVELY = 0x1 << 2,
SCREENSHOT_CAN_SHOOT_REGION = 0x1 << 3,
/* SHOOT_WINDOW mode only: it window selection requires active click. */
SCREENSHOT_CAN_PICK_WINDOW = 0x1 << 4,
/* SHOOT_WINDOW + SCREENSHOT_CAN_PICK_WINDOW only: if a delay can be
* inserted in-between selection click and actual snapshot. */
SCREENSHOT_CAN_DELAY_WINDOW_SHOT = 0x1 << 5,
SCREENSHOT_CAN_SHOOT_WINDOW = 0x1 << 6
} ScreenshotCapabilities;
typedef enum
{
SCREENSHOT_PROFILE_POLICY_MONITOR,
SCREENSHOT_PROFILE_POLICY_SRGB
} ScreenshotProfilePolicy;
typedef enum
{
SHOOT_WINDOW,
SHOOT_ROOT,
SHOOT_REGION
} ShootType;
typedef struct
{
ShootType shoot_type;
gboolean decorate;
guint window_id;
GdkMonitor *monitor;
guint select_delay;
guint screenshot_delay;
gint x1;
gint y1;
gint x2;
gint y2;
gboolean show_cursor;
ScreenshotProfilePolicy profile_policy;
} ScreenshotValues;
void screenshot_delay (gint seconds);
#endif /* __SCREENSHOT_H__ */