diff options
author | monsta <[email protected]> | 2015-09-09 12:35:05 +0300 |
---|---|---|
committer | monsta <[email protected]> | 2015-09-09 12:35:05 +0300 |
commit | aa3a853c9d06d0321e8924cbfbc42c8411f571f5 (patch) | |
tree | 7bca363d0bb67fddadbe6fd53e92c3b3db71db8d /mate-screenshot/src/screenshot-utils.c | |
parent | 0d36d61761a1d68839d61f521889dba3db7f514f (diff) | |
download | mate-utils-aa3a853c9d06d0321e8924cbfbc42c8411f571f5.tar.bz2 mate-utils-aa3a853c9d06d0321e8924cbfbc42c8411f571f5.tar.xz |
mate-screenshot: move stuff to data/ and src/ subdirs
Diffstat (limited to 'mate-screenshot/src/screenshot-utils.c')
-rw-r--r-- | mate-screenshot/src/screenshot-utils.c | 1040 |
1 files changed, 1040 insertions, 0 deletions
diff --git a/mate-screenshot/src/screenshot-utils.c b/mate-screenshot/src/screenshot-utils.c new file mode 100644 index 00000000..eaeeca28 --- /dev/null +++ b/mate-screenshot/src/screenshot-utils.c @@ -0,0 +1,1040 @@ +/* screenshot-utils.c - common functions for MATE Screenshot + * + * Copyright (C) 2001-2006 Jonathan Blandford <[email protected]> + * Copyright (C) 2008 Cosimo Cecchi <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + */ + +#include "config.h" +#include "screenshot-utils.h" + +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> +#include <glib.h> +#include <glib/gi18n.h> + +#ifdef HAVE_X11_EXTENSIONS_SHAPE_H +#include <X11/extensions/shape.h> +#endif + +#if GTK_CHECK_VERSION (3, 0, 0) +#define GdkRegion cairo_region_t +#define gdk_region_new cairo_region_create +#define gdk_region_destroy cairo_region_destroy +#define gdk_region_rectangle cairo_region_create_rectangle +#define gdk_region_offset cairo_region_translate +#define gdk_region_intersect cairo_region_intersect +#define gdk_region_subtract cairo_region_subtract +#define gdk_region_union_with_rect cairo_region_union_rectangle +#define gdk_cursor_unref g_object_unref +#endif + +static GtkWidget *selection_window; + +#define SELECTION_NAME "_MATE_PANEL_SCREENSHOT" + +static char * +get_utf8_property (GdkWindow *window, + GdkAtom atom) +{ + gboolean res; + GdkAtom utf8_string; + GdkAtom type; + int actual_format, actual_length; + guchar *data; + char *retval; + + utf8_string = gdk_x11_xatom_to_atom (gdk_x11_get_xatom_by_name ("UTF8_STRING")); + res = gdk_property_get (window, atom, utf8_string, + 0, G_MAXLONG, FALSE, + &type, + &actual_format, &actual_length, + &data); + if (!res) + return NULL; + + if (type != utf8_string || actual_format != 8 || actual_length == 0) + { + g_free (data); + return NULL; + } + + if (!g_utf8_validate ((gchar *) data, actual_length, NULL)) + { + char *atom_name = gdk_atom_name (atom); + + g_warning ("Property `%s' (format: %d, length: %d) contained " + "invalid UTF-8", + atom_name, + actual_format, + actual_length); + + g_free (atom_name); + g_free (data); + + return NULL; + } + + retval = g_strndup ((gchar *) data, actual_length); + + g_free (data); + + return retval; +} + +/* To make sure there is only one screenshot taken at a time, + * (Imagine key repeat for the print screen key) we hold a selection + * until we are done taking the screenshot + */ +gboolean +screenshot_grab_lock (void) +{ + GdkAtom selection_atom; + gboolean result = FALSE; + + selection_atom = gdk_atom_intern (SELECTION_NAME, FALSE); + gdk_x11_grab_server (); + + if (gdk_selection_owner_get (selection_atom) != NULL) + goto out; + + selection_window = gtk_invisible_new (); + gtk_widget_show (selection_window); + + if (!gtk_selection_owner_set (selection_window, + gdk_atom_intern (SELECTION_NAME, FALSE), + GDK_CURRENT_TIME)) + { + gtk_widget_destroy (selection_window); + selection_window = NULL; + goto out; + } + + result = TRUE; + + out: + gdk_x11_ungrab_server (); + gdk_flush (); + + return result; +} + +void +screenshot_release_lock (void) +{ + if (selection_window) + { + gtk_widget_destroy (selection_window); + selection_window = NULL; + } + + gdk_flush (); +} + +static GdkWindow * +screenshot_find_active_window (void) +{ + GdkWindow *window; + GdkScreen *default_screen; + + default_screen = gdk_screen_get_default (); + window = gdk_screen_get_active_window (default_screen); + + return window; +} + +static gboolean +screenshot_window_is_desktop (GdkWindow *window) +{ + GdkWindow *root_window = gdk_get_default_root_window (); + GdkWindowTypeHint window_type_hint; + + if (window == root_window) + return TRUE; + + window_type_hint = gdk_window_get_type_hint (window); + if (window_type_hint == GDK_WINDOW_TYPE_HINT_DESKTOP) + return TRUE; + + return FALSE; + +} + +#define MAXIMUM_WM_REPARENTING_DEPTH 4 + +static GdkWindow * +look_for_hint_helper (GdkWindow *window, + GdkAtom property, + int depth) +{ + gboolean res; + GdkAtom actual_type; + int actual_format, actual_length; + guchar *data; + + res = gdk_property_get (window, property, GDK_NONE, + 0, 1, FALSE, + &actual_type, + &actual_format, &actual_length, + &data); + + if (res == TRUE && + data != NULL && + actual_format == 32 && + data[0] == 1) + { + g_free (data); + + return window; + } + + if (depth < MAXIMUM_WM_REPARENTING_DEPTH) + { + GList *children, *l; + + children = gdk_window_get_children (window); + if (children != NULL) + { + for (l = children; l; l = l->next) + { + window = look_for_hint_helper (l->data, property, depth + 1); + if (window) + break; + } + + g_list_free (children); + + if (window) + return window; + } + } + + return NULL; +} + +static GdkWindow * +look_for_hint (GdkWindow *window, + GdkAtom property) +{ + GdkWindow *retval; + + retval = look_for_hint_helper (window, property, 0); + + return retval; +} + +GdkWindow * +screenshot_find_current_window () +{ + GdkWindow *current_window; + + current_window = screenshot_find_active_window (); + + /* If there's no active window, we fall back to returning the + * window that the cursor is in. + */ + if (!current_window) + current_window = gdk_window_at_pointer (NULL, NULL); + + if (current_window) + { + if (screenshot_window_is_desktop (current_window)) + /* if the current window is the desktop (e.g. caja), we + * return NULL, as getting the whole screen makes more sense. + */ + return NULL; + + /* Once we have a window, we take the toplevel ancestor. */ + current_window = gdk_window_get_toplevel (current_window); + } + + return current_window; +} + +typedef struct { + GdkRectangle rect; + gboolean button_pressed; + GtkWidget *window; +} select_area_filter_data; + +static gboolean +select_area_button_press (GtkWidget *window, + GdkEventButton *event, + select_area_filter_data *data) +{ + if (data->button_pressed) + return TRUE; + + data->button_pressed = TRUE; + data->rect.x = event->x_root; + data->rect.y = event->y_root; + + return TRUE; +} + +static gboolean +select_area_motion_notify (GtkWidget *window, + GdkEventMotion *event, + select_area_filter_data *data) +{ + GdkRectangle draw_rect; + + if (!data->button_pressed) + return TRUE; + + draw_rect.width = ABS (data->rect.x - event->x_root); + draw_rect.height = ABS (data->rect.y - event->y_root); + draw_rect.x = MIN (data->rect.x, event->x_root); + draw_rect.y = MIN (data->rect.y, event->y_root); + + if (draw_rect.width <= 0 || draw_rect.height <= 0) + { + gtk_window_move (GTK_WINDOW (window), -100, -100); + gtk_window_resize (GTK_WINDOW (window), 10, 10); + return TRUE; + } + + gtk_window_move (GTK_WINDOW (window), draw_rect.x, draw_rect.y); + gtk_window_resize (GTK_WINDOW (window), draw_rect.width, draw_rect.height); + + /* We (ab)use app-paintable to indicate if we have an RGBA window */ + if (!gtk_widget_get_app_paintable (window)) + { + GdkWindow *gdkwindow = gtk_widget_get_window (window); + + /* Shape the window to make only the outline visible */ + if (draw_rect.width > 2 && draw_rect.height > 2) + { +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_region_t *region, *region2; + cairo_rectangle_int_t region_rect = { +#else + GdkRegion *region, *region2; + GdkRectangle region_rect = { +#endif + 0, 0, + draw_rect.width - 2, draw_rect.height - 2 + }; + +#if GTK_CHECK_VERSION (3, 0, 0) + region = cairo_region_create_rectangle (®ion_rect); +#else + region = gdk_region_rectangle (®ion_rect); +#endif + region_rect.x++; + region_rect.y++; + region_rect.width -= 2; + region_rect.height -= 2; +#if GTK_CHECK_VERSION (3, 0, 0) + region2 = cairo_region_create_rectangle (®ion_rect); + cairo_region_subtract (region, region2); +#else + region2 = gdk_region_rectangle (®ion_rect); + gdk_region_subtract (region, region2); +#endif + + gdk_window_shape_combine_region (gdkwindow, region, 0, 0); + + gdk_region_destroy (region); + gdk_region_destroy (region2); + } + else + gdk_window_shape_combine_region (gdkwindow, NULL, 0, 0); + } + + return TRUE; +} + +static gboolean +select_area_button_release (GtkWidget *window, + GdkEventButton *event, + select_area_filter_data *data) +{ + if (!data->button_pressed) + return TRUE; + + data->rect.width = ABS (data->rect.x - event->x_root); + data->rect.height = ABS (data->rect.y - event->y_root); + data->rect.x = MIN (data->rect.x, event->x_root); + data->rect.y = MIN (data->rect.y, event->y_root); + + gtk_main_quit (); + + return TRUE; +} + +static gboolean +select_area_key_press (GtkWidget *window, + GdkEventKey *event, + select_area_filter_data *data) +{ + if (event->keyval == GDK_KEY_Escape) + { + data->rect.x = 0; + data->rect.y = 0; + data->rect.width = 0; + data->rect.height = 0; + gtk_main_quit (); + } + + return TRUE; +} + + +static gboolean +#if GTK_CHECK_VERSION (3, 0, 0) +draw (GtkWidget *window, cairo_t *cr, gpointer unused) +#else +expose (GtkWidget *window, GdkEventExpose *event, gpointer unused) +#endif +{ + GtkAllocation allocation; + GtkStyle *style; +#if !GTK_CHECK_VERSION (3, 0, 0) + cairo_t *cr; + + cr = gdk_cairo_create (event->window); + gdk_cairo_region (cr, event->region); + cairo_clip (cr); +#endif + + style = gtk_widget_get_style (window); + + if (gtk_widget_get_app_paintable (window)) + { + cairo_set_line_width (cr, 1.0); + + gtk_widget_get_allocation (window, &allocation); + + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba (cr, 0, 0, 0, 0); + cairo_paint (cr); + + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_SELECTED]); + cairo_paint_with_alpha (cr, 0.25); + + cairo_rectangle (cr, + allocation.x + 0.5, allocation.y + 0.5, + allocation.width - 1, allocation.height - 1); + cairo_stroke (cr); + } + else + { + gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_SELECTED]); + cairo_paint (cr); + } + +#if !GTK_CHECK_VERSION (3, 0, 0) + cairo_destroy (cr); +#endif + + return TRUE; +} + +static GtkWidget * +create_select_window (void) +{ + GtkWidget *window; + GdkScreen *screen; +#if GTK_CHECK_VERSION (3, 0, 0) + GdkVisual *visual; +#endif + + screen = gdk_screen_get_default (); +#if GTK_CHECK_VERSION (3, 0, 0) + visual = gdk_screen_get_rgba_visual (screen); +#endif + + window = gtk_window_new (GTK_WINDOW_POPUP); + if (gdk_screen_is_composited (screen) && +#if GTK_CHECK_VERSION (3, 0, 0) + visual) +#else + gdk_screen_get_rgba_colormap (screen)) +#endif + { +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_widget_set_visual (window, visual); +#else + gtk_widget_set_colormap (window, gdk_screen_get_rgba_colormap (screen)); +#endif + gtk_widget_set_app_paintable (window, TRUE); + } +#if GTK_CHECK_VERSION (3, 0, 0) + g_signal_connect (window, "draw", G_CALLBACK (draw), NULL); +#else + g_signal_connect (window, "expose-event", G_CALLBACK (expose), NULL); +#endif + + gtk_window_move (GTK_WINDOW (window), -100, -100); + gtk_window_resize (GTK_WINDOW (window), 10, 10); + gtk_widget_show (window); + return window; +} + +typedef struct { + GdkRectangle rectangle; + SelectAreaCallback callback; +} CallbackData; + +static gboolean +emit_select_callback_in_idle (gpointer user_data) +{ + CallbackData *data = user_data; + + data->callback (&data->rectangle); + + g_slice_free (CallbackData, data); + + return FALSE; +} + +void +screenshot_select_area_async (SelectAreaCallback callback) +{ + GdkCursor *cursor; + select_area_filter_data data; + GdkRectangle *rectangle; + CallbackData *cb_data; + + data.rect.x = 0; + data.rect.y = 0; + data.rect.width = 0; + data.rect.height = 0; + data.button_pressed = FALSE; + data.window = create_select_window(); + + cb_data = g_slice_new0 (CallbackData); + cb_data->callback = callback; + + g_signal_connect (data.window, "key-press-event", G_CALLBACK (select_area_key_press), &data); + g_signal_connect (data.window, "button-press-event", G_CALLBACK (select_area_button_press), &data); + g_signal_connect (data.window, "button-release-event", G_CALLBACK (select_area_button_release), &data); + g_signal_connect (data.window, "motion-notify-event", G_CALLBACK (select_area_motion_notify), &data); + + cursor = gdk_cursor_new (GDK_CROSSHAIR); + + if (gdk_pointer_grab (gtk_widget_get_window (data.window), FALSE, + GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK, + NULL, cursor, + GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) + { + gdk_cursor_unref (cursor); + goto out; + } + + if (gdk_keyboard_grab (gtk_widget_get_window (data.window), FALSE, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) + { + gdk_pointer_ungrab (GDK_CURRENT_TIME); + gdk_cursor_unref (cursor); + goto out; + } + + gtk_main (); + + gdk_keyboard_ungrab (GDK_CURRENT_TIME); + gdk_pointer_ungrab (GDK_CURRENT_TIME); + + gtk_widget_destroy (data.window); + gdk_cursor_unref (cursor); + + gdk_flush (); + + out: + cb_data->rectangle = data.rect; + + /* FIXME: we should actually be emitting the callback When + * the compositor has finished re-drawing, but there seems to be no easy + * way to know that. + */ + g_timeout_add (200, emit_select_callback_in_idle, cb_data); +} + +static Window +find_wm_window (Window xid) +{ + Window root, parent, *children; + unsigned int nchildren; + + do + { + if (XQueryTree (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xid, &root, + &parent, &children, &nchildren) == 0) + { + g_warning ("Couldn't find window manager window"); + return None; + } + + if (root == parent) + return xid; + + xid = parent; + } + while (TRUE); +} + +#if GTK_CHECK_VERSION (3, 0, 0) +static cairo_region_t * +#else +static GdkRegion * +#endif +make_region_with_monitors (GdkScreen *screen) +{ +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_region_t *region; +#else + GdkRegion *region; +#endif + int num_monitors; + int i; + + num_monitors = gdk_screen_get_n_monitors (screen); + +#if GTK_CHECK_VERSION (3, 0, 0) + region = cairo_region_create (); +#else + region = gdk_region_new (); +#endif + + for (i = 0; i < num_monitors; i++) + { + GdkRectangle rect; + + gdk_screen_get_monitor_geometry (screen, i, &rect); +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_region_union_rectangle (region, &rect); +#else + gdk_region_union_with_rect (region, &rect); +#endif + } + + return region; +} + +static void +blank_rectangle_in_pixbuf (GdkPixbuf *pixbuf, GdkRectangle *rect) +{ + int x, y; + int x2, y2; + guchar *pixels; + int rowstride; + int n_channels; + guchar *row; + gboolean has_alpha; + + g_assert (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB); + + x2 = rect->x + rect->width; + y2 = rect->y + rect->height; + + pixels = gdk_pixbuf_get_pixels (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + n_channels = gdk_pixbuf_get_n_channels (pixbuf); + + for (y = rect->y; y < y2; y++) + { + guchar *p; + + row = pixels + y * rowstride; + p = row + rect->x * n_channels; + + for (x = rect->x; x < x2; x++) + { + *p++ = 0; + *p++ = 0; + *p++ = 0; + + if (has_alpha) + *p++ = 255; /* opaque black */ + } + } +} + +static void +#if GTK_CHECK_VERSION (3, 0, 0) +blank_region_in_pixbuf (GdkPixbuf *pixbuf, cairo_region_t *region) +{ + int n_rects; + int i; + int width, height; + cairo_rectangle_int_t pixbuf_rect; + + n_rects = cairo_region_num_rectangles (region); +#else +blank_region_in_pixbuf (GdkPixbuf *pixbuf, GdkRegion *region) +{ + GdkRectangle *rects; + int n_rects; + int i; + int width, height; + GdkRectangle pixbuf_rect; + + gdk_region_get_rectangles (region, &rects, &n_rects); +#endif + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + + pixbuf_rect.x = 0; + pixbuf_rect.y = 0; + pixbuf_rect.width = width; + pixbuf_rect.height = height; + + for (i = 0; i < n_rects; i++) + { +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_rectangle_int_t rect, dest; + + cairo_region_get_rectangle (region, i, &rect); + if (gdk_rectangle_intersect (&rect, &pixbuf_rect, &dest)) +#else + GdkRectangle dest; + + if (gdk_rectangle_intersect (rects + i, &pixbuf_rect, &dest)) +#endif + blank_rectangle_in_pixbuf (pixbuf, &dest); + + } +#if !GTK_CHECK_VERSION (3, 0, 0) + g_free (rects); +#endif +} + +/* When there are multiple monitors with different resolutions, the visible area + * within the root window may not be rectangular (it may have an L-shape, for + * example). In that case, mask out the areas of the root window which would + * not be visible in the monitors, so that screenshot do not end up with content + * that the user won't ever see. + */ +static void +mask_monitors (GdkPixbuf *pixbuf, GdkWindow *root_window) +{ + GdkScreen *screen; +#if GTK_CHECK_VERSION (3, 0, 0) + cairo_region_t *region_with_monitors; + cairo_region_t *invisible_region; + cairo_rectangle_int_t rect; +#else + GdkRegion *region_with_monitors; + GdkRegion *invisible_region; + GdkRectangle rect; +#endif + + screen = gdk_window_get_screen (root_window); + + region_with_monitors = make_region_with_monitors (screen); + + rect.x = 0; + rect.y = 0; + rect.width = gdk_screen_get_width (screen); + rect.height = gdk_screen_get_height (screen); + +#if GTK_CHECK_VERSION (3, 0, 0) + invisible_region = cairo_region_create_rectangle (&rect); + cairo_region_subtract (invisible_region, region_with_monitors); + + blank_region_in_pixbuf (pixbuf, invisible_region); + + cairo_region_destroy (region_with_monitors); + cairo_region_destroy (invisible_region); +#else + invisible_region = gdk_region_rectangle (&rect); + gdk_region_subtract (invisible_region, region_with_monitors); + + blank_region_in_pixbuf (pixbuf, invisible_region); + + gdk_region_destroy (region_with_monitors); + gdk_region_destroy (invisible_region); +#endif +} + +GdkPixbuf * +screenshot_get_pixbuf (GdkWindow *window, + GdkRectangle *rectangle, + gboolean include_pointer, + gboolean include_border, + gboolean include_mask) +{ + GdkWindow *root; + GdkPixbuf *screenshot; + gint x_real_orig, y_real_orig, x_orig, y_orig; + gint width, real_width, height, real_height; + + /* If the screenshot should include the border, we look for the WM window. */ + + if (include_border) + { + Window xid, wm; + + xid = GDK_WINDOW_XID (window); + wm = find_wm_window (xid); + + if (wm != None) + window = gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), wm); + + /* fallback to no border if we can't find the WM window. */ + } + + root = gdk_get_default_root_window (); + + real_width = gdk_window_get_width(window); + real_height = gdk_window_get_height(window); + + gdk_window_get_origin (window, &x_real_orig, &y_real_orig); + + x_orig = x_real_orig; + y_orig = y_real_orig; + width = real_width; + height = real_height; + + if (x_orig < 0) + { + width = width + x_orig; + x_orig = 0; + } + + if (y_orig < 0) + { + height = height + y_orig; + y_orig = 0; + } + + if (x_orig + width > gdk_screen_width ()) + width = gdk_screen_width () - x_orig; + + if (y_orig + height > gdk_screen_height ()) + height = gdk_screen_height () - y_orig; + + if (rectangle) + { + x_orig = rectangle->x - x_orig; + y_orig = rectangle->y - y_orig; + width = rectangle->width; + height = rectangle->height; + } + +#if GTK_CHECK_VERSION (3, 0, 0) + screenshot = gdk_pixbuf_get_from_window (root, + x_orig, y_orig, + width, height); +#else + screenshot = gdk_pixbuf_get_from_drawable (NULL, root, NULL, + x_orig, y_orig, 0, 0, + width, height); +#endif + + /* + * Masking currently only works properly with full-screen shots + */ + if (include_mask) + mask_monitors (screenshot, root); + +#ifdef HAVE_X11_EXTENSIONS_SHAPE_H + if (include_border) + { + XRectangle *rectangles; + GdkPixbuf *tmp; + int rectangle_count, rectangle_order, i; + + /* we must use XShape to avoid showing what's under the rounder corners + * of the WM decoration. + */ + + rectangles = XShapeGetRectangles (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), + GDK_WINDOW_XID (window), + ShapeBounding, + &rectangle_count, + &rectangle_order); + if (rectangles && rectangle_count > 0 && window != root) + { + gboolean has_alpha = gdk_pixbuf_get_has_alpha (screenshot); + + tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height); + gdk_pixbuf_fill (tmp, 0); + + for (i = 0; i < rectangle_count; i++) + { + gint rec_x, rec_y; + gint rec_width, rec_height; + gint y; + + rec_x = rectangles[i].x; + rec_y = rectangles[i].y; + rec_width = rectangles[i].width; + rec_height = rectangles[i].height; + + if (x_real_orig < 0) + { + rec_x += x_real_orig; + rec_x = MAX(rec_x, 0); + rec_width += x_real_orig; + } + + if (y_real_orig < 0) + { + rec_y += y_real_orig; + rec_y = MAX(rec_y, 0); + rec_height += y_real_orig; + } + + if (x_orig + rec_x + rec_width > gdk_screen_width ()) + rec_width = gdk_screen_width () - x_orig - rec_x; + + if (y_orig + rec_y + rec_height > gdk_screen_height ()) + rec_height = gdk_screen_height () - y_orig - rec_y; + + for (y = rec_y; y < rec_y + rec_height; y++) + { + guchar *src_pixels, *dest_pixels; + gint x; + + src_pixels = gdk_pixbuf_get_pixels (screenshot) + + y * gdk_pixbuf_get_rowstride(screenshot) + + rec_x * (has_alpha ? 4 : 3); + dest_pixels = gdk_pixbuf_get_pixels (tmp) + + y * gdk_pixbuf_get_rowstride (tmp) + + rec_x * 4; + + for (x = 0; x < rec_width; x++) + { + *dest_pixels++ = *src_pixels++; + *dest_pixels++ = *src_pixels++; + *dest_pixels++ = *src_pixels++; + + if (has_alpha) + *dest_pixels++ = *src_pixels++; + else + *dest_pixels++ = 255; + } + } + } + + g_object_unref (screenshot); + screenshot = tmp; + } + } +#endif /* HAVE_X11_EXTENSIONS_SHAPE_H */ + + /* if we have a selected area, there were by definition no cursor in the + * screenshot */ + if (include_pointer && !rectangle) + { + GdkCursor *cursor; + GdkPixbuf *cursor_pixbuf; + + cursor = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_LEFT_PTR); + cursor_pixbuf = gdk_cursor_get_image (cursor); + + if (cursor_pixbuf != NULL) + { + GdkRectangle r1, r2; + gint cx, cy, xhot, yhot; + + gdk_window_get_pointer (window, &cx, &cy, NULL); + sscanf (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"), "%d", &xhot); + sscanf (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"), "%d", &yhot); + + /* in r1 we have the window coordinates */ + r1.x = x_real_orig; + r1.y = y_real_orig; + r1.width = real_width; + r1.height = real_height; + + /* in r2 we have the cursor window coordinates */ + r2.x = cx + x_real_orig; + r2.y = cy + y_real_orig; + r2.width = gdk_pixbuf_get_width (cursor_pixbuf); + r2.height = gdk_pixbuf_get_height (cursor_pixbuf); + + /* see if the pointer is inside the window */ + if (gdk_rectangle_intersect (&r1, &r2, &r2)) + { + gdk_pixbuf_composite (cursor_pixbuf, screenshot, + cx - xhot, cy - yhot, + r2.width, r2.height, + cx - xhot, cy - yhot, + 1.0, 1.0, + GDK_INTERP_BILINEAR, + 255); + } + + g_object_unref (cursor_pixbuf); + gdk_cursor_unref (cursor); + } + } + + return screenshot; +} + +gchar * +screenshot_get_window_title (GdkWindow *win) +{ + gchar *name; + + win = gdk_window_get_toplevel (win); + win = look_for_hint (win, gdk_x11_xatom_to_atom (gdk_x11_get_xatom_by_name ("WM_STATE"))); + + name = get_utf8_property (win, gdk_x11_xatom_to_atom (gdk_x11_get_xatom_by_name ("_NET_WM_NAME"))); + if (name) + return name; + + /* TODO: maybe we should also look at WM_NAME and WM_CLASS? */ + + return g_strdup (_("Untitled Window")); +} + +void +screenshot_show_error_dialog (GtkWindow *parent, + const gchar *message, + const gchar *detail) +{ + GtkWidget *dialog; + + g_return_if_fail ((parent == NULL) || (GTK_IS_WINDOW (parent))); + g_return_if_fail (message != NULL); + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", message); + gtk_window_set_title (GTK_WINDOW (dialog), ""); + + if (detail) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", detail); + + if (parent && gtk_window_get_group (parent)) + gtk_window_group_add_window (gtk_window_get_group (parent), GTK_WINDOW (dialog)); + + gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (dialog); +} + +void +screenshot_show_gerror_dialog (GtkWindow *parent, + const gchar *message, + GError *error) +{ + g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent)); + g_return_if_fail (message != NULL); + g_return_if_fail (error != NULL); + + screenshot_show_error_dialog (parent, message, error->message); +} |