[gnome-screenshot/wip/exalm/refactor: 2/12] Split ScreenshotUtils



commit e26ae9edac0e365326396cebfbfb9d5af8492e94
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Sun Apr 19 01:55:24 2020 +0500

    Split ScreenshotUtils
    
    Introduce ScreenshotBackendShell and ScreenshotBackendX11, move the
    code as appropriate.

 src/meson.build                |   2 +
 src/screenshot-backend-shell.c | 128 +++++++++
 src/screenshot-backend-shell.h |  33 +++
 src/screenshot-backend-x11.c   | 570 ++++++++++++++++++++++++++++++++++++++
 src/screenshot-backend-x11.h   |  33 +++
 src/screenshot-utils.c         | 601 +----------------------------------------
 6 files changed, 780 insertions(+), 587 deletions(-)
---
diff --git a/src/meson.build b/src/meson.build
index 6a026c3..b82ab49 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -6,6 +6,8 @@ sources = [
   'screenshot-application.c',
   'screenshot-area-selection.c',
   'screenshot-backend.c',
+  'screenshot-backend-shell.c',
+  'screenshot-backend-x11.c',
   'screenshot-config.c',
   'screenshot-dialog.c',
   'screenshot-filename-builder.c',
diff --git a/src/screenshot-backend-shell.c b/src/screenshot-backend-shell.c
new file mode 100644
index 0000000..84c5ead
--- /dev/null
+++ b/src/screenshot-backend-shell.c
@@ -0,0 +1,128 @@
+/* screenshot-backend-shell.c - GNOME Shell backend
+ *
+ * Copyright (C) 2001-2006  Jonathan Blandford <jrb alum mit edu>
+ * Copyright (C) 2008 Cosimo Cecchi <cosimoc gnome org>
+ * Copyright (C) 2020 Alexander Mikhaylenko <alexm gnome 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 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 Street, Fifth Floor,
+ */
+
+#include "config.h"
+
+#include "screenshot-backend-shell.h"
+
+#include "screenshot-config.h"
+
+#include <glib/gstdio.h>
+
+struct _ScreenshotBackendShell
+{
+  GObject parent_instance;
+};
+
+static void screenshot_backend_shell_backend_init (ScreenshotBackendInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ScreenshotBackendShell, screenshot_backend_shell, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (SCREENSHOT_TYPE_BACKEND, 
screenshot_backend_shell_backend_init))
+
+static GdkPixbuf *
+screenshot_backend_shell_get_pixbuf (ScreenshotBackend *backend,
+                                     GdkRectangle      *rectangle)
+{
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *path = NULL, *filename = NULL, *tmpname = NULL;
+  GdkPixbuf *screenshot = NULL;
+  const gchar *method_name;
+  GVariant *method_params;
+  GDBusConnection *connection;
+
+  path = g_build_filename (g_get_user_cache_dir (), "gnome-screenshot", NULL);
+  g_mkdir_with_parents (path, 0700);
+
+  tmpname = g_strdup_printf ("scr-%d.png", g_random_int ());
+  filename = g_build_filename (path, tmpname, NULL);
+
+  if (screenshot_config->take_window_shot)
+    {
+      method_name = "ScreenshotWindow";
+      method_params = g_variant_new ("(bbbs)",
+                                     TRUE,
+                                     screenshot_config->include_pointer,
+                                     TRUE, /* flash */
+                                     filename);
+    }
+  else if (rectangle != NULL)
+    {
+      method_name = "ScreenshotArea";
+      method_params = g_variant_new ("(iiiibs)",
+                                     rectangle->x, rectangle->y,
+                                     rectangle->width, rectangle->height,
+                                     TRUE, /* flash */
+                                     filename);
+    }
+  else
+    {
+      method_name = "Screenshot";
+      method_params = g_variant_new ("(bbs)",
+                                     screenshot_config->include_pointer,
+                                     TRUE, /* flash */
+                                     filename);
+    }
+
+  connection = g_application_get_dbus_connection (g_application_get_default ());
+  g_dbus_connection_call_sync (connection,
+                               "org.gnome.Shell.Screenshot",
+                               "/org/gnome/Shell/Screenshot",
+                               "org.gnome.Shell.Screenshot",
+                               method_name,
+                               method_params,
+                               NULL,
+                               G_DBUS_CALL_FLAGS_NONE,
+                               -1,
+                               NULL,
+                               &error);
+
+  if (error == NULL)
+    {
+      screenshot = gdk_pixbuf_new_from_file (filename, &error);
+
+      /* remove the temporary file created by the shell */
+      g_unlink (filename);
+    }
+
+  return screenshot;
+}
+
+static void
+screenshot_backend_shell_class_init (ScreenshotBackendShellClass *klass)
+{
+}
+
+static void
+screenshot_backend_shell_init (ScreenshotBackendShell *self)
+{
+}
+
+static void
+screenshot_backend_shell_backend_init (ScreenshotBackendInterface *iface)
+{
+  iface->get_pixbuf = screenshot_backend_shell_get_pixbuf;
+}
+
+ScreenshotBackend *
+screenshot_backend_shell_new (void)
+{
+  return g_object_new (SCREENSHOT_TYPE_BACKEND_SHELL, NULL);
+}
diff --git a/src/screenshot-backend-shell.h b/src/screenshot-backend-shell.h
new file mode 100644
index 0000000..4d86898
--- /dev/null
+++ b/src/screenshot-backend-shell.h
@@ -0,0 +1,33 @@
+/* screenshot-backend-shell.h - GNOME Shell backend
+ *
+ * Copyright (C) 2020 Alexander Mikhaylenko <alexm gnome 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 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 Street, Fifth Floor,
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include "screenshot-backend.h"
+
+G_BEGIN_DECLS
+
+#define SCREENSHOT_TYPE_BACKEND_SHELL (screenshot_backend_shell_get_type())
+
+G_DECLARE_FINAL_TYPE (ScreenshotBackendShell, screenshot_backend_shell, SCREENSHOT, BACKEND_SHELL, GObject)
+
+ScreenshotBackend *screenshot_backend_shell_new (void);
+
+G_END_DECLS
diff --git a/src/screenshot-backend-x11.c b/src/screenshot-backend-x11.c
new file mode 100644
index 0000000..b49a383
--- /dev/null
+++ b/src/screenshot-backend-x11.c
@@ -0,0 +1,570 @@
+/* screenshot-backend-x11.c - Fallback X11 backend
+ *
+ * Copyright (C) 2001-2006  Jonathan Blandford <jrb alum mit edu>
+ * Copyright (C) 2008 Cosimo Cecchi <cosimoc gnome org>
+ * Copyright (C) 2020 Alexander Mikhaylenko <alexm gnome 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 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 Street, Fifth Floor,
+ */
+
+#include "config.h"
+
+#ifdef HAVE_X11
+
+#include "screenshot-backend-x11.h"
+
+#include "screenshot-config.h"
+
+#include "cheese-flash.h"
+
+#include <gdk/gdkx.h>
+
+#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
+#include <X11/extensions/shape.h>
+#endif
+
+struct _ScreenshotBackendX11
+{
+  GObject parent_instance;
+};
+
+static void screenshot_backend_x11_backend_init (ScreenshotBackendInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ScreenshotBackendX11, screenshot_backend_x11, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (SCREENSHOT_TYPE_BACKEND, 
screenshot_backend_x11_backend_init))
+
+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;
+}
+
+static Window
+find_wm_window (GdkWindow *window)
+{
+  Window xid, root, parent, *children;
+  unsigned int nchildren;
+
+  if (window == gdk_get_default_root_window ())
+    return None;
+
+  xid = GDK_WINDOW_XID (window);
+
+  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);
+}
+
+static cairo_region_t *
+make_region_with_monitors (GdkScreen *screen)
+{
+  cairo_region_t *region;
+  int num_monitors;
+  int i;
+
+  num_monitors = gdk_screen_get_n_monitors (screen);
+
+  region = cairo_region_create ();
+
+  for (i = 0; i < num_monitors; i++)
+    {
+      GdkRectangle rect;
+
+      gdk_screen_get_monitor_geometry (screen, i, &rect);
+      cairo_region_union_rectangle (region, &rect);
+    }
+
+  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
+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);
+
+  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++)
+    {
+      cairo_rectangle_int_t rect, dest;
+
+      cairo_region_get_rectangle (region, i, &rect);
+      if (gdk_rectangle_intersect (&rect, &pixbuf_rect, &dest))
+        blank_rectangle_in_pixbuf (pixbuf, &dest);
+    }
+}
+
+/* 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;
+  cairo_region_t *region_with_monitors;
+  cairo_region_t *invisible_region;
+  cairo_rectangle_int_t rect;
+
+  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);
+
+  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);
+}
+
+static void
+screenshot_fallback_get_window_rect_coords (GdkWindow    *window,
+                                            GdkRectangle *real_coordinates_out,
+                                            GdkRectangle *screenshot_coordinates_out)
+{
+  gint x_orig, y_orig;
+  gint width, height;
+  GdkRectangle real_coordinates;
+
+  gdk_window_get_frame_extents (window, &real_coordinates);
+
+  x_orig = real_coordinates.x;
+  y_orig = real_coordinates.y;
+  width  = real_coordinates.width;
+  height = real_coordinates.height;
+
+  if (real_coordinates_out != NULL)
+    *real_coordinates_out = real_coordinates;
+
+  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 (screenshot_coordinates_out != NULL)
+    {
+      screenshot_coordinates_out->x = x_orig;
+      screenshot_coordinates_out->y = y_orig;
+      screenshot_coordinates_out->width = width;
+      screenshot_coordinates_out->height = height;
+    }
+}
+
+static void
+screenshot_fallback_fire_flash (GdkWindow *window,
+                                GdkRectangle *rectangle)
+{
+  GdkRectangle rect;
+  CheeseFlash *flash = NULL;
+
+  if (rectangle != NULL)
+    rect = *rectangle;
+  else
+    screenshot_fallback_get_window_rect_coords (window, NULL, &rect);
+
+  flash = cheese_flash_new ();
+  cheese_flash_fire (flash, &rect);
+}
+
+GdkWindow *
+do_find_current_window (void)
+{
+  GdkWindow *current_window;
+  GdkDeviceManager *manager;
+  GdkDevice *device;
+
+  current_window = screenshot_find_active_window ();
+  manager = gdk_display_get_device_manager (gdk_display_get_default ());
+  device = gdk_device_manager_get_client_pointer (manager);
+
+  /* If there's no active window, we fall back to returning the
+   * window that the cursor is in.
+   */
+  if (!current_window)
+    current_window = gdk_device_get_window_at_position (device, NULL, NULL);
+
+  if (current_window)
+    {
+      if (screenshot_window_is_desktop (current_window))
+        /* if the current window is the desktop (e.g. nautilus), 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;
+}
+
+static GdkWindow *
+screenshot_fallback_find_current_window (void)
+{
+  GdkWindow *window = NULL;
+
+  if (screenshot_config->take_window_shot)
+    {
+      window = do_find_current_window ();
+
+      if (window == NULL)
+        screenshot_config->take_window_shot = FALSE;
+    }
+
+  if (window == NULL)
+    window = gdk_get_default_root_window ();
+
+  return window;
+}
+
+static GdkPixbuf *
+screenshot_backend_x11_get_pixbuf (ScreenshotBackend *backend,
+                                   GdkRectangle      *rectangle)
+{
+  GdkWindow *root, *wm_window = NULL;
+  GdkPixbuf *screenshot = NULL;
+  GdkRectangle real_coords, screenshot_coords;
+  Window wm;
+  GtkBorder frame_offset = { 0, 0, 0, 0 };
+  GdkWindow *window;
+
+  window = screenshot_fallback_find_current_window ();
+
+  screenshot_fallback_get_window_rect_coords (window,
+                                              &real_coords,
+                                              &screenshot_coords);
+
+  wm = find_wm_window (window);
+  if (wm != None)
+    {
+      GdkRectangle wm_real_coords;
+
+      wm_window = gdk_x11_window_foreign_new_for_display
+        (gdk_window_get_display (window), wm);
+
+      screenshot_fallback_get_window_rect_coords (wm_window,
+                                                  &wm_real_coords,
+                                                  NULL);
+
+      frame_offset.left = (gdouble) (real_coords.x - wm_real_coords.x);
+      frame_offset.top = (gdouble) (real_coords.y - wm_real_coords.y);
+      frame_offset.right = (gdouble) (wm_real_coords.width - real_coords.width - frame_offset.left);
+      frame_offset.bottom = (gdouble) (wm_real_coords.height - real_coords.height - frame_offset.top);
+    }
+
+  if (rectangle)
+    {
+      screenshot_coords.x = rectangle->x - screenshot_coords.x;
+      screenshot_coords.y = rectangle->y - screenshot_coords.y;
+      screenshot_coords.width  = rectangle->width;
+      screenshot_coords.height = rectangle->height;
+    }
+
+  root = gdk_get_default_root_window ();
+  screenshot = gdk_pixbuf_get_from_window (root,
+                                           screenshot_coords.x, screenshot_coords.y,
+                                           screenshot_coords.width, screenshot_coords.height);
+
+  if (!screenshot_config->take_window_shot &&
+      !screenshot_config->take_area_shot)
+    mask_monitors (screenshot, root);
+
+#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
+  if (wm != None)
+    {
+      XRectangle *rectangles;
+      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()),
+                                        wm,
+                                        ShapeBounding,
+                                        &rectangle_count,
+                                        &rectangle_order);
+      if (rectangles && rectangle_count > 0)
+        {
+          int scale_factor = gdk_window_get_scale_factor (wm_window);
+          gboolean has_alpha = gdk_pixbuf_get_has_alpha (screenshot);
+          GdkPixbuf *tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+                                           gdk_pixbuf_get_width (screenshot),
+                                           gdk_pixbuf_get_height (screenshot));
+          gdk_pixbuf_fill (tmp, 0);
+
+          for (i = 0; i < rectangle_count; i++)
+            {
+              gint rec_x, rec_y;
+              gint rec_width, rec_height;
+              gint y;
+
+              /* If we're using invisible borders, the ShapeBounding might not
+               * have the same size as the frame extents, as it would include the
+               * areas for the invisible borders themselves.
+               * In that case, trim every rectangle we get by the offset between the
+               * WM window size and the frame extents.
+               *
+               * Note that the XShape values are in actual pixels, whereas the GDK
+               * ones are in display pixels (i.e. scaled), so we need to apply the
+               * scale factor to the former to use display pixels for all our math.
+               */
+              rec_x = rectangles[i].x / scale_factor;
+              rec_y = rectangles[i].y / scale_factor;
+              rec_width = rectangles[i].width / scale_factor - (frame_offset.left + frame_offset.right);
+              rec_height = rectangles[i].height / scale_factor - (frame_offset.top + frame_offset.bottom);
+
+              if (real_coords.x < 0)
+                {
+                  rec_x += real_coords.x;
+                  rec_x = MAX(rec_x, 0);
+                  rec_width += real_coords.x;
+                }
+
+              if (real_coords.y < 0)
+                {
+                  rec_y += real_coords.y;
+                  rec_y = MAX(rec_y, 0);
+                  rec_height += real_coords.y;
+                }
+
+              if (screenshot_coords.x + rec_x + rec_width > gdk_screen_width ())
+                rec_width = gdk_screen_width () - screenshot_coords.x - rec_x;
+
+              if (screenshot_coords.y + rec_y + rec_height > gdk_screen_height ())
+                rec_height = gdk_screen_height () - screenshot_coords.y - rec_y;
+
+              /* Undo the scale factor in order to copy the pixbuf data pixel-wise */
+              for (y = rec_y * scale_factor; y < (rec_y + rec_height) * scale_factor; y++)
+                {
+                  guchar *src_pixels, *dest_pixels;
+                  gint x;
+
+                  src_pixels = gdk_pixbuf_get_pixels (screenshot)
+                             + y * gdk_pixbuf_get_rowstride(screenshot)
+                             + rec_x * scale_factor * (has_alpha ? 4 : 3);
+                  dest_pixels = gdk_pixbuf_get_pixels (tmp)
+                              + y * gdk_pixbuf_get_rowstride (tmp)
+                              + rec_x * scale_factor * 4;
+
+                  for (x = 0; x < rec_width * scale_factor; 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_set_object (&screenshot, tmp);
+
+          XFree (rectangles);
+        }
+    }
+#endif /* HAVE_X11_EXTENSIONS_SHAPE_H */
+
+  /* if we have a selected area, there were by definition no cursor in the
+   * screenshot */
+  if (screenshot_config->include_pointer && !rectangle)
+    {
+      g_autoptr(GdkCursor) cursor = NULL;
+      g_autoptr(GdkPixbuf) cursor_pixbuf = NULL;
+
+      cursor = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_LEFT_PTR);
+      cursor_pixbuf = gdk_cursor_get_image (cursor);
+
+      if (cursor_pixbuf != NULL)
+        {
+          GdkDeviceManager *manager;
+          GdkDevice *device;
+          GdkRectangle rect;
+          gint cx, cy, xhot, yhot;
+
+          manager = gdk_display_get_device_manager (gdk_display_get_default ());
+          device = gdk_device_manager_get_client_pointer (manager);
+
+          if (wm_window != NULL)
+            gdk_window_get_device_position (wm_window, device,
+                                            &cx, &cy, NULL);
+          else
+            gdk_window_get_device_position (window, device,
+                                            &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 rect we have the cursor window coordinates */
+          rect.x = cx + real_coords.x;
+          rect.y = cy + real_coords.y;
+          rect.width = gdk_pixbuf_get_width (cursor_pixbuf);
+          rect.height = gdk_pixbuf_get_height (cursor_pixbuf);
+
+          /* see if the pointer is inside the window */
+          if (gdk_rectangle_intersect (&real_coords, &rect, &rect))
+            {
+              gint cursor_x, cursor_y;
+
+              cursor_x = cx - xhot - frame_offset.left;
+              cursor_y = cy - yhot - frame_offset.top;
+              gdk_pixbuf_composite (cursor_pixbuf, screenshot,
+                                    cursor_x, cursor_y,
+                                    rect.width, rect.height,
+                                    cursor_x, cursor_y,
+                                    1.0, 1.0,
+                                    GDK_INTERP_BILINEAR,
+                                    255);
+            }
+        }
+    }
+
+  screenshot_fallback_fire_flash (window, rectangle);
+
+  return screenshot;
+}
+
+static void
+screenshot_backend_x11_class_init (ScreenshotBackendX11Class *klass)
+{
+}
+
+static void
+screenshot_backend_x11_init (ScreenshotBackendX11 *self)
+{
+}
+
+static void
+screenshot_backend_x11_backend_init (ScreenshotBackendInterface *iface)
+{
+  iface->get_pixbuf = screenshot_backend_x11_get_pixbuf;
+}
+
+ScreenshotBackend *
+screenshot_backend_x11_new (void)
+{
+  return g_object_new (SCREENSHOT_TYPE_BACKEND_X11, NULL);
+}
+
+#endif /* HAVE_X11 */
diff --git a/src/screenshot-backend-x11.h b/src/screenshot-backend-x11.h
new file mode 100644
index 0000000..3b52b1e
--- /dev/null
+++ b/src/screenshot-backend-x11.h
@@ -0,0 +1,33 @@
+/* screenshot-backend-x11.h - Fallback X11 backend
+ *
+ * Copyright (C) 2020 Alexander Mikhaylenko <alexm gnome 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 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 Street, Fifth Floor,
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include "screenshot-backend.h"
+
+G_BEGIN_DECLS
+
+#define SCREENSHOT_TYPE_BACKEND_X11 (screenshot_backend_x11_get_type())
+
+G_DECLARE_FINAL_TYPE (ScreenshotBackendX11, screenshot_backend_x11, SCREENSHOT, BACKEND_X11, GObject)
+
+ScreenshotBackend *screenshot_backend_x11_new (void);
+
+G_END_DECLS
diff --git a/src/screenshot-utils.c b/src/screenshot-utils.c
index f8dab9b..26ca026 100644
--- a/src/screenshot-utils.c
+++ b/src/screenshot-utils.c
@@ -20,254 +20,18 @@
 
 #include "config.h"
 
-#include <gdk/gdkkeysyms.h>
-
-#if HAVE_X11
-#include <gdk/gdkx.h>
-#endif
+#include "screenshot-utils.h"
 
 #include <gtk/gtk.h>
 #include <glib.h>
 #include <glib/gi18n.h>
-#include <glib/gstdio.h>
 #include <canberra-gtk.h>
-#include <stdlib.h>
-
-#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
-#include <X11/extensions/shape.h>
-#endif
-
-#include "cheese-flash.h"
 
-#include "screenshot-application.h"
-#include "screenshot-config.h"
-#include "screenshot-utils.h"
+#include "screenshot-backend-shell.h"
 
 #ifdef HAVE_X11
-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;
-}
-
-static Window
-find_wm_window (GdkWindow *window)
-{
-  Window xid, root, parent, *children;
-  unsigned int nchildren;
-
-  if (window == gdk_get_default_root_window ())
-    return None;
-
-  xid = GDK_WINDOW_XID (window);
-
-  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);
-}
-
-static cairo_region_t *
-make_region_with_monitors (GdkScreen *screen)
-{
-  cairo_region_t *region;
-  int num_monitors;
-  int i;
-
-  num_monitors = gdk_screen_get_n_monitors (screen);
-
-  region = cairo_region_create ();
-
-  for (i = 0; i < num_monitors; i++)
-    {
-      GdkRectangle rect;
-
-      gdk_screen_get_monitor_geometry (screen, i, &rect);
-      cairo_region_union_rectangle (region, &rect);
-    }
-
-  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
-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);
-
-  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++)
-    {
-      cairo_rectangle_int_t rect, dest;
-
-      cairo_region_get_rectangle (region, i, &rect);
-      if (gdk_rectangle_intersect (&rect, &pixbuf_rect, &dest))
-        blank_rectangle_in_pixbuf (pixbuf, &dest);
-    }
-}
-
-/* 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;
-  cairo_region_t *region_with_monitors;
-  cairo_region_t *invisible_region;
-  cairo_rectangle_int_t rect;
-
-  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);
-
-  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);
-}
-
-static void
-screenshot_fallback_get_window_rect_coords (GdkWindow    *window,
-                                            GdkRectangle *real_coordinates_out,
-                                            GdkRectangle *screenshot_coordinates_out)
-{
-  gint x_orig, y_orig;
-  gint width, height;
-  GdkRectangle real_coordinates;
-
-  gdk_window_get_frame_extents (window, &real_coordinates);
-
-  x_orig = real_coordinates.x;
-  y_orig = real_coordinates.y;
-  width  = real_coordinates.width;
-  height = real_coordinates.height;
-
-  if (real_coordinates_out != NULL)
-    *real_coordinates_out = real_coordinates;
-
-  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 (screenshot_coordinates_out != NULL)
-    {
-      screenshot_coordinates_out->x = x_orig;
-      screenshot_coordinates_out->y = y_orig;
-      screenshot_coordinates_out->width = width;
-      screenshot_coordinates_out->height = height;
-    }
-}
-#endif /* HAVE_X11 */
+#include "screenshot-backend-x11.h"
+#endif
 
 void
 screenshot_play_sound_effect (const gchar *event_id,
@@ -302,362 +66,21 @@ screenshot_play_sound_effect (const gchar *event_id,
     ca_proplist_destroy (p);
 }
 
-#ifdef HAVE_X11
-static void
-screenshot_fallback_fire_flash (GdkWindow *window,
-                                GdkRectangle *rectangle)
-{
-  GdkRectangle rect;
-  CheeseFlash *flash = NULL;
-
-  if (rectangle != NULL)
-    rect = *rectangle;
-  else
-    screenshot_fallback_get_window_rect_coords (window, NULL, &rect);
-
-  flash = cheese_flash_new ();
-  cheese_flash_fire (flash, &rect);
-}
-
-GdkWindow *
-do_find_current_window (void)
-{
-  GdkWindow *current_window;
-  GdkDeviceManager *manager;
-  GdkDevice *device;
-
-  current_window = screenshot_find_active_window ();
-  manager = gdk_display_get_device_manager (gdk_display_get_default ());
-  device = gdk_device_manager_get_client_pointer (manager);
-
-  /* If there's no active window, we fall back to returning the
-   * window that the cursor is in.
-   */
-  if (!current_window)
-    current_window = gdk_device_get_window_at_position (device, NULL, NULL);
-
-  if (current_window)
-    {
-      if (screenshot_window_is_desktop (current_window))
-        /* if the current window is the desktop (e.g. nautilus), 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;
-}
-
-static GdkWindow *
-screenshot_fallback_find_current_window (void)
-{
-  GdkWindow *window = NULL;
-
-  if (screenshot_config->take_window_shot)
-    {
-      window = do_find_current_window ();
-
-      if (window == NULL)
-        screenshot_config->take_window_shot = FALSE;
-    }
-
-  if (window == NULL)
-    window = gdk_get_default_root_window ();
-
-  return window;
-}
-
-static GdkPixbuf *
-screenshot_fallback_get_pixbuf (GdkRectangle *rectangle)
-{
-  GdkWindow *root, *wm_window = NULL;
-  GdkPixbuf *screenshot = NULL;
-  GdkRectangle real_coords, screenshot_coords;
-  Window wm;
-  GtkBorder frame_offset = { 0, 0, 0, 0 };
-  GdkWindow *window;
-
-  window = screenshot_fallback_find_current_window ();
-
-  screenshot_fallback_get_window_rect_coords (window,
-                                              &real_coords,
-                                              &screenshot_coords);
-
-  wm = find_wm_window (window);
-  if (wm != None)
-    {
-      GdkRectangle wm_real_coords;
-
-      wm_window = gdk_x11_window_foreign_new_for_display
-        (gdk_window_get_display (window), wm);
-
-      screenshot_fallback_get_window_rect_coords (wm_window,
-                                                  &wm_real_coords,
-                                                  NULL);
-
-      frame_offset.left = (gdouble) (real_coords.x - wm_real_coords.x);
-      frame_offset.top = (gdouble) (real_coords.y - wm_real_coords.y);
-      frame_offset.right = (gdouble) (wm_real_coords.width - real_coords.width - frame_offset.left);
-      frame_offset.bottom = (gdouble) (wm_real_coords.height - real_coords.height - frame_offset.top);
-    }
-
-  if (rectangle)
-    {
-      screenshot_coords.x = rectangle->x - screenshot_coords.x;
-      screenshot_coords.y = rectangle->y - screenshot_coords.y;
-      screenshot_coords.width  = rectangle->width;
-      screenshot_coords.height = rectangle->height;
-    }
-
-  root = gdk_get_default_root_window ();
-  screenshot = gdk_pixbuf_get_from_window (root,
-                                           screenshot_coords.x, screenshot_coords.y,
-                                           screenshot_coords.width, screenshot_coords.height);
-
-  if (!screenshot_config->take_window_shot &&
-      !screenshot_config->take_area_shot)
-    mask_monitors (screenshot, root);
-
-#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
-  if (wm != None)
-    {
-      XRectangle *rectangles;
-      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()),
-                                        wm,
-                                        ShapeBounding,
-                                        &rectangle_count,
-                                        &rectangle_order);
-      if (rectangles && rectangle_count > 0)
-        {
-          int scale_factor = gdk_window_get_scale_factor (wm_window);
-          gboolean has_alpha = gdk_pixbuf_get_has_alpha (screenshot);
-          GdkPixbuf *tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
-                                           gdk_pixbuf_get_width (screenshot),
-                                           gdk_pixbuf_get_height (screenshot));
-          gdk_pixbuf_fill (tmp, 0);
-
-          for (i = 0; i < rectangle_count; i++)
-            {
-              gint rec_x, rec_y;
-              gint rec_width, rec_height;
-              gint y;
-
-              /* If we're using invisible borders, the ShapeBounding might not
-               * have the same size as the frame extents, as it would include the
-               * areas for the invisible borders themselves.
-               * In that case, trim every rectangle we get by the offset between the
-               * WM window size and the frame extents.
-               *
-               * Note that the XShape values are in actual pixels, whereas the GDK
-               * ones are in display pixels (i.e. scaled), so we need to apply the
-               * scale factor to the former to use display pixels for all our math.
-               */
-              rec_x = rectangles[i].x / scale_factor;
-              rec_y = rectangles[i].y / scale_factor;
-              rec_width = rectangles[i].width / scale_factor - (frame_offset.left + frame_offset.right);
-              rec_height = rectangles[i].height / scale_factor - (frame_offset.top + frame_offset.bottom);
-
-              if (real_coords.x < 0)
-                {
-                  rec_x += real_coords.x;
-                  rec_x = MAX(rec_x, 0);
-                  rec_width += real_coords.x;
-                }
-
-              if (real_coords.y < 0)
-                {
-                  rec_y += real_coords.y;
-                  rec_y = MAX(rec_y, 0);
-                  rec_height += real_coords.y;
-                }
-
-              if (screenshot_coords.x + rec_x + rec_width > gdk_screen_width ())
-                rec_width = gdk_screen_width () - screenshot_coords.x - rec_x;
-
-              if (screenshot_coords.y + rec_y + rec_height > gdk_screen_height ())
-                rec_height = gdk_screen_height () - screenshot_coords.y - rec_y;
-
-              /* Undo the scale factor in order to copy the pixbuf data pixel-wise */
-              for (y = rec_y * scale_factor; y < (rec_y + rec_height) * scale_factor; y++)
-                {
-                  guchar *src_pixels, *dest_pixels;
-                  gint x;
-
-                  src_pixels = gdk_pixbuf_get_pixels (screenshot)
-                             + y * gdk_pixbuf_get_rowstride(screenshot)
-                             + rec_x * scale_factor * (has_alpha ? 4 : 3);
-                  dest_pixels = gdk_pixbuf_get_pixels (tmp)
-                              + y * gdk_pixbuf_get_rowstride (tmp)
-                              + rec_x * scale_factor * 4;
-
-                  for (x = 0; x < rec_width * scale_factor; 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_set_object (&screenshot, tmp);
-
-          XFree (rectangles);
-        }
-    }
-#endif /* HAVE_X11_EXTENSIONS_SHAPE_H */
-
-  /* if we have a selected area, there were by definition no cursor in the
-   * screenshot */
-  if (screenshot_config->include_pointer && !rectangle)
-    {
-      g_autoptr(GdkCursor) cursor = NULL;
-      g_autoptr(GdkPixbuf) cursor_pixbuf = NULL;
-
-      cursor = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_LEFT_PTR);
-      cursor_pixbuf = gdk_cursor_get_image (cursor);
-
-      if (cursor_pixbuf != NULL)
-        {
-          GdkDeviceManager *manager;
-          GdkDevice *device;
-          GdkRectangle rect;
-          gint cx, cy, xhot, yhot;
-
-          manager = gdk_display_get_device_manager (gdk_display_get_default ());
-          device = gdk_device_manager_get_client_pointer (manager);
-
-          if (wm_window != NULL)
-            gdk_window_get_device_position (wm_window, device,
-                                            &cx, &cy, NULL);
-          else
-            gdk_window_get_device_position (window, device,
-                                            &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 rect we have the cursor window coordinates */
-          rect.x = cx + real_coords.x;
-          rect.y = cy + real_coords.y;
-          rect.width = gdk_pixbuf_get_width (cursor_pixbuf);
-          rect.height = gdk_pixbuf_get_height (cursor_pixbuf);
-
-          /* see if the pointer is inside the window */
-          if (gdk_rectangle_intersect (&real_coords, &rect, &rect))
-            {
-              gint cursor_x, cursor_y;
-
-              cursor_x = cx - xhot - frame_offset.left;
-              cursor_y = cy - yhot - frame_offset.top;
-              gdk_pixbuf_composite (cursor_pixbuf, screenshot,
-                                    cursor_x, cursor_y,
-                                    rect.width, rect.height,
-                                    cursor_x, cursor_y,
-                                    1.0, 1.0,
-                                    GDK_INTERP_BILINEAR,
-                                    255);
-            }
-        }
-    }
-
-  screenshot_fallback_fire_flash (window, rectangle);
-
-  return screenshot;
-}
-#endif /* HAVE_X11 */
-
-static GdkPixbuf *
-screenshot_shell_get_pixbuf (GdkRectangle *rectangle)
-{
-  g_autoptr(GError) error = NULL;
-  g_autofree gchar *path = NULL, *filename = NULL, *tmpname = NULL;
-  GdkPixbuf *screenshot = NULL;
-  const gchar *method_name;
-  GVariant *method_params;
-  GDBusConnection *connection;
-
-  path = g_build_filename (g_get_user_cache_dir (), "gnome-screenshot", NULL);
-  g_mkdir_with_parents (path, 0700);
-
-  tmpname = g_strdup_printf ("scr-%d.png", g_random_int ());
-  filename = g_build_filename (path, tmpname, NULL);
-
-  if (screenshot_config->take_window_shot)
-    {
-      method_name = "ScreenshotWindow";
-      method_params = g_variant_new ("(bbbs)",
-                                     TRUE,
-                                     screenshot_config->include_pointer,
-                                     TRUE, /* flash */
-                                     filename);
-    }
-  else if (rectangle != NULL)
-    {
-      method_name = "ScreenshotArea";
-      method_params = g_variant_new ("(iiiibs)",
-                                     rectangle->x, rectangle->y,
-                                     rectangle->width, rectangle->height,
-                                     TRUE, /* flash */
-                                     filename);
-    }
-  else
-    {
-      method_name = "Screenshot";
-      method_params = g_variant_new ("(bbs)",
-                                     screenshot_config->include_pointer,
-                                     TRUE, /* flash */
-                                     filename);
-    }
-
-  connection = g_application_get_dbus_connection (g_application_get_default ());
-  g_dbus_connection_call_sync (connection,
-                               "org.gnome.Shell.Screenshot",
-                               "/org/gnome/Shell/Screenshot",
-                               "org.gnome.Shell.Screenshot",
-                               method_name,
-                               method_params,
-                               NULL,
-                               G_DBUS_CALL_FLAGS_NONE,
-                               -1,
-                               NULL,
-                               &error);
-
-  if (error == NULL)
-    {
-      screenshot = gdk_pixbuf_new_from_file (filename, &error);
-
-      /* remove the temporary file created by the shell */
-      g_unlink (filename);
-    }
-
-  return screenshot;
-}
-
 GdkPixbuf *
 screenshot_get_pixbuf (GdkRectangle *rectangle)
 {
   GdkPixbuf *screenshot = NULL;
   gboolean force_fallback = FALSE;
+  g_autoptr (ScreenshotBackend) backend = NULL;
 
 #ifdef HAVE_X11
   force_fallback = g_getenv ("GNOME_SCREENSHOT_FORCE_FALLBACK") != NULL;
 #endif
+
   if (!force_fallback)
     {
-      screenshot = screenshot_shell_get_pixbuf (rectangle);
+      backend = screenshot_backend_shell_new ();
+      screenshot = screenshot_backend_get_pixbuf (backend, rectangle);
       if (!screenshot)
 #ifdef HAVE_X11
         g_message ("Unable to use GNOME Shell's builtin screenshot interface, "
@@ -671,8 +94,12 @@ screenshot_get_pixbuf (GdkRectangle *rectangle)
 
 #ifdef HAVE_X11
   if (!screenshot)
-    screenshot = screenshot_fallback_get_pixbuf (rectangle);
-#endif /* HAVE_X11 */
+    {
+      g_clear_object (&backend);
+      backend = screenshot_backend_x11_new ();
+      screenshot = screenshot_backend_get_pixbuf (backend, rectangle);
+    }
+#endif
 
   return screenshot;
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]