gtk/gdk/gdkoffscreenwindow.c

720 lines
18 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* GDK - The GIMP Drawing Kit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GTK+ Team and others 1997-2005. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "config.h"
#include "gdkwindow.h"
#include "gdkinternals.h"
#include "gdkwindowimpl.h"
#include <math.h>
/* LIMITATIONS:
*
* Offscreen windows can't be the child of a foreign window,
* nor contain foreign windows
* GDK_POINTER_MOTION_HINT_MASK isn't effective
*/
typedef struct _GdkOffscreenWindow GdkOffscreenWindow;
typedef struct _GdkOffscreenWindowClass GdkOffscreenWindowClass;
struct _GdkOffscreenWindow
{
GdkWindowImpl parent_instance;
GdkWindow *wrapper;
cairo_surface_t *surface;
GdkWindow *embedder;
};
struct _GdkOffscreenWindowClass
{
GdkWindowImplClass parent_class;
};
#define GDK_TYPE_OFFSCREEN_WINDOW (gdk_offscreen_window_get_type())
#define GDK_OFFSCREEN_WINDOW(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_OFFSCREEN_WINDOW, GdkOffscreenWindow))
#define GDK_IS_OFFSCREEN_WINDOW(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_OFFSCREEN_WINDOW))
#define GDK_OFFSCREEN_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_OFFSCREEN_WINDOW, GdkOffscreenWindowClass))
#define GDK_IS_OFFSCREEN_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_OFFSCREEN_WINDOW))
#define GDK_OFFSCREEN_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_OFFSCREEN_WINDOW, GdkOffscreenWindowClass))
static void gdk_offscreen_window_hide (GdkWindow *window);
G_DEFINE_TYPE (GdkOffscreenWindow, gdk_offscreen_window, GDK_TYPE_WINDOW_IMPL)
static void
gdk_offscreen_window_finalize (GObject *object)
{
GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (object);
if (offscreen->surface)
cairo_surface_destroy (offscreen->surface);
G_OBJECT_CLASS (gdk_offscreen_window_parent_class)->finalize (object);
}
static void
gdk_offscreen_window_init (GdkOffscreenWindow *window)
{
}
static void
gdk_offscreen_window_destroy (GdkWindow *window,
gboolean recursing,
gboolean foreign_destroy)
{
GdkOffscreenWindow *offscreen;
offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
gdk_offscreen_window_set_embedder (window, NULL);
if (!recursing)
gdk_offscreen_window_hide (window);
}
static cairo_surface_t *
get_surface (GdkOffscreenWindow *offscreen)
{
if (! offscreen->surface)
{
GdkWindow *window = offscreen->wrapper;
g_signal_emit_by_name (window, "create-surface",
window->width,
window->height,
&offscreen->surface);
}
return offscreen->surface;
}
static gboolean
is_parent_of (GdkWindow *parent,
GdkWindow *child)
{
GdkWindow *w;
w = child;
while (w != NULL)
{
if (w == parent)
return TRUE;
w = gdk_window_get_parent (w);
}
return FALSE;
}
static cairo_surface_t *
gdk_offscreen_window_ref_cairo_surface (GdkWindow *window)
{
GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
return cairo_surface_reference (get_surface (offscreen));
}
cairo_surface_t *
_gdk_offscreen_window_create_surface (GdkWindow *offscreen,
gint width,
gint height)
{
cairo_surface_t *similar;
cairo_surface_t *surface;
cairo_content_t content = CAIRO_CONTENT_COLOR;
g_return_val_if_fail (GDK_IS_OFFSCREEN_WINDOW (offscreen->impl), NULL);
similar = _gdk_window_ref_cairo_surface (offscreen->parent);
if (gdk_window_get_visual (offscreen) ==
gdk_screen_get_rgba_visual (gdk_window_get_screen (offscreen)))
{
content = CAIRO_CONTENT_COLOR_ALPHA;
}
surface = cairo_surface_create_similar (similar, content, width, height);
cairo_surface_destroy (similar);
return surface;
}
void
_gdk_offscreen_window_new (GdkWindow *window,
GdkWindowAttr *attributes,
gint attributes_mask)
{
GdkOffscreenWindow *offscreen;
g_return_if_fail (attributes != NULL);
if (attributes->wclass != GDK_INPUT_OUTPUT)
return; /* Can't support input only offscreens */
if (window->parent != NULL && GDK_WINDOW_DESTROYED (window->parent))
return;
window->impl = g_object_new (GDK_TYPE_OFFSCREEN_WINDOW, NULL);
offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
offscreen->wrapper = window;
}
static gboolean
gdk_offscreen_window_reparent (GdkWindow *window,
GdkWindow *new_parent,
gint x,
gint y)
{
GdkWindow *old_parent;
gboolean was_mapped;
if (new_parent)
{
/* No input-output children of input-only windows */
if (new_parent->input_only && !window->input_only)
return FALSE;
/* Don't create loops in hierarchy */
if (is_parent_of (window, new_parent))
return FALSE;
}
was_mapped = GDK_WINDOW_IS_MAPPED (window);
gdk_window_hide (window);
if (window->parent)
window->parent->children = g_list_remove (window->parent->children, window);
old_parent = window->parent;
window->parent = new_parent;
window->x = x;
window->y = y;
if (new_parent)
window->parent->children = g_list_prepend (window->parent->children, window);
_gdk_synthesize_crossing_events_for_geometry_change (window);
if (old_parent)
_gdk_synthesize_crossing_events_for_geometry_change (old_parent);
return was_mapped;
}
static void
from_embedder (GdkWindow *window,
double embedder_x, double embedder_y,
double *offscreen_x, double *offscreen_y)
{
g_signal_emit_by_name (window->impl_window,
"from-embedder",
embedder_x, embedder_y,
offscreen_x, offscreen_y,
NULL);
}
static void
to_embedder (GdkWindow *window,
double offscreen_x, double offscreen_y,
double *embedder_x, double *embedder_y)
{
g_signal_emit_by_name (window->impl_window,
"to-embedder",
offscreen_x, offscreen_y,
embedder_x, embedder_y,
NULL);
}
static gint
gdk_offscreen_window_get_root_coords (GdkWindow *window,
gint x,
gint y,
gint *root_x,
gint *root_y)
{
GdkOffscreenWindow *offscreen;
int tmpx, tmpy;
tmpx = x;
tmpy = y;
offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
if (offscreen->embedder)
{
double dx, dy;
to_embedder (window,
x, y,
&dx, &dy);
tmpx = floor (dx + 0.5);
tmpy = floor (dy + 0.5);
gdk_window_get_root_coords (offscreen->embedder,
tmpx, tmpy,
&tmpx, &tmpy);
}
if (root_x)
*root_x = tmpx;
if (root_y)
*root_y = tmpy;
return TRUE;
}
static gboolean
gdk_offscreen_window_get_device_state (GdkWindow *window,
GdkDevice *device,
gint *x,
gint *y,
GdkModifierType *mask)
{
GdkOffscreenWindow *offscreen;
int tmpx, tmpy;
double dtmpx, dtmpy;
GdkModifierType tmpmask;
tmpx = 0;
tmpy = 0;
tmpmask = 0;
offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
if (offscreen->embedder != NULL)
{
gdk_window_get_device_position (offscreen->embedder, device, &tmpx, &tmpy, &tmpmask);
from_embedder (window,
tmpx, tmpy,
&dtmpx, &dtmpy);
tmpx = floor (dtmpx + 0.5);
tmpy = floor (dtmpy + 0.5);
}
if (x)
*x = tmpx;
if (y)
*y = tmpy;
if (mask)
*mask = tmpmask;
return TRUE;
}
/**
* gdk_offscreen_window_get_surface:
* @window: a #GdkWindow
*
* Gets the offscreen surface that an offscreen window renders into.
* If you need to keep this around over window resizes, you need to
* add a reference to it.
*
* Returns: The offscreen surface, or %NULL if not offscreen
*/
cairo_surface_t *
gdk_offscreen_window_get_surface (GdkWindow *window)
{
GdkOffscreenWindow *offscreen;
g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
if (!GDK_IS_OFFSCREEN_WINDOW (window->impl))
return NULL;
offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
return get_surface (offscreen);
}
static void
gdk_offscreen_window_raise (GdkWindow *window)
{
/* gdk_window_raise already changed the stacking order */
_gdk_synthesize_crossing_events_for_geometry_change (window);
}
static void
gdk_offscreen_window_lower (GdkWindow *window)
{
/* gdk_window_lower already changed the stacking order */
_gdk_synthesize_crossing_events_for_geometry_change (window);
}
static void
gdk_offscreen_window_move_resize_internal (GdkWindow *window,
gint x,
gint y,
gint width,
gint height,
gboolean send_expose_events)
{
GdkOffscreenWindow *offscreen;
gint dx, dy, dw, dh;
offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
if (width < 1)
width = 1;
if (height < 1)
height = 1;
if (window->destroyed)
return;
dx = x - window->x;
dy = y - window->y;
dw = width - window->width;
dh = height - window->height;
window->x = x;
window->y = y;
if (window->width != width ||
window->height != height)
{
window->width = width;
window->height = height;
if (offscreen->surface)
{
cairo_surface_t *old_surface;
cairo_t *cr;
old_surface = offscreen->surface;
offscreen->surface = NULL;
offscreen->surface = get_surface (offscreen);
cr = cairo_create (offscreen->surface);
cairo_set_source_surface (cr, old_surface, 0, 0);
cairo_paint (cr);
cairo_destroy (cr);
cairo_surface_destroy (old_surface);
}
}
if (GDK_WINDOW_IS_MAPPED (window))
{
// TODO: Only invalidate new area, i.e. for larger windows
gdk_window_invalidate_rect (window, NULL, TRUE);
_gdk_synthesize_crossing_events_for_geometry_change (window);
}
}
static void
gdk_offscreen_window_move_resize (GdkWindow *window,
gboolean with_move,
gint x,
gint y,
gint width,
gint height)
{
GdkOffscreenWindow *offscreen;
offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
if (!with_move)
{
x = window->x;
y = window->y;
}
if (width < 0)
width = window->width;
if (height < 0)
height = window->height;
gdk_offscreen_window_move_resize_internal (window, x, y,
width, height,
TRUE);
}
static void
gdk_offscreen_window_show (GdkWindow *window,
gboolean already_mapped)
{
GdkRectangle area = { 0, 0, window->width, window->height };
gdk_window_invalidate_rect (window, &area, FALSE);
}
static void
gdk_offscreen_window_hide (GdkWindow *window)
{
GdkOffscreenWindow *offscreen;
GdkDisplay *display;
g_return_if_fail (window != NULL);
offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
/* May need to break grabs on children */
display = gdk_window_get_display (window);
/* TODO: This needs updating to the new grab world */
#if 0
if (display->pointer_grab.window != NULL)
{
if (is_parent_of (window, display->pointer_grab.window))
{
/* Call this ourselves, even though gdk_display_pointer_ungrab
does so too, since we want to pass implicit == TRUE so the
broken grab event is generated */
_gdk_display_unset_has_pointer_grab (display,
TRUE,
FALSE,
GDK_CURRENT_TIME);
gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
}
}
#endif
}
static void
gdk_offscreen_window_withdraw (GdkWindow *window)
{
}
static GdkEventMask
gdk_offscreen_window_get_events (GdkWindow *window)
{
return 0;
}
static void
gdk_offscreen_window_set_events (GdkWindow *window,
GdkEventMask event_mask)
{
}
static void
gdk_offscreen_window_set_background (GdkWindow *window,
cairo_pattern_t *pattern)
{
}
static void
gdk_offscreen_window_shape_combine_region (GdkWindow *window,
const cairo_region_t *shape_region,
gint offset_x,
gint offset_y)
{
}
static void
gdk_offscreen_window_input_shape_combine_region (GdkWindow *window,
const cairo_region_t *shape_region,
gint offset_x,
gint offset_y)
{
}
static gboolean
gdk_offscreen_window_set_static_gravities (GdkWindow *window,
gboolean use_static)
{
return TRUE;
}
static void
gdk_offscreen_window_get_geometry (GdkWindow *window,
gint *x,
gint *y,
gint *width,
gint *height)
{
if (!GDK_WINDOW_DESTROYED (window))
{
if (x)
*x = window->x;
if (y)
*y = window->y;
if (width)
*width = window->width;
if (height)
*height = window->height;
}
}
static gboolean
gdk_offscreen_window_queue_antiexpose (GdkWindow *window,
cairo_region_t *area)
{
return FALSE;
}
static void
gdk_offscreen_window_translate (GdkWindow *window,
cairo_region_t *area,
gint dx,
gint dy)
{
GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
if (offscreen->surface)
{
cairo_t *cr;
cr = cairo_create (offscreen->surface);
area = cairo_region_copy (area);
gdk_cairo_region (cr, area);
cairo_clip (cr);
/* NB: This is a self-copy and Cairo doesn't support that yet.
* So we do a litle trick.
*/
cairo_push_group (cr);
cairo_set_source_surface (cr, offscreen->surface, dx, dy);
cairo_paint (cr);
cairo_pop_group_to_source (cr);
cairo_paint (cr);
cairo_destroy (cr);
}
_gdk_window_add_damage (window, area);
}
static cairo_surface_t *
gdk_offscreen_window_resize_cairo_surface (GdkWindow *window,
cairo_surface_t *surface,
gint width,
gint height)
{
/* No-op. The surface gets resized in
* gdk_offscreen_window_move_resize_internal().
*/
return surface;
}
/**
* gdk_offscreen_window_set_embedder:
* @window: a #GdkWindow
* @embedder: the #GdkWindow that @window gets embedded in
*
* Sets @window to be embedded in @embedder.
*
* To fully embed an offscreen window, in addition to calling this
* function, it is also necessary to handle the #GdkWindow::pick-embedded-child
* signal on the @embedder and the #GdkWindow::to-embedder and
* #GdkWindow::from-embedder signals on @window.
*
* Since: 2.18
*/
void
gdk_offscreen_window_set_embedder (GdkWindow *window,
GdkWindow *embedder)
{
GdkOffscreenWindow *offscreen;
g_return_if_fail (GDK_IS_WINDOW (window));
if (!GDK_IS_OFFSCREEN_WINDOW (window->impl))
return;
offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
if (embedder)
{
g_object_ref (embedder);
embedder->num_offscreen_children++;
}
if (offscreen->embedder)
{
g_object_unref (offscreen->embedder);
offscreen->embedder->num_offscreen_children--;
}
offscreen->embedder = embedder;
}
/**
* gdk_offscreen_window_get_embedder:
* @window: a #GdkWindow
*
* Gets the window that @window is embedded in.
*
* Returns: (transfer none): the embedding #GdkWindow, or %NULL
* if @window is not an mbedded offscreen window
*
* Since: 2.18
*/
GdkWindow *
gdk_offscreen_window_get_embedder (GdkWindow *window)
{
GdkOffscreenWindow *offscreen;
g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
if (!GDK_IS_OFFSCREEN_WINDOW (window->impl))
return NULL;
offscreen = GDK_OFFSCREEN_WINDOW (window->impl);
return offscreen->embedder;
}
static void
gdk_offscreen_window_class_init (GdkOffscreenWindowClass *klass)
{
GdkWindowImplClass *impl_class = GDK_WINDOW_IMPL_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gdk_offscreen_window_finalize;
impl_class->ref_cairo_surface = gdk_offscreen_window_ref_cairo_surface;
impl_class->show = gdk_offscreen_window_show;
impl_class->hide = gdk_offscreen_window_hide;
impl_class->withdraw = gdk_offscreen_window_withdraw;
impl_class->raise = gdk_offscreen_window_raise;
impl_class->lower = gdk_offscreen_window_lower;
impl_class->move_resize = gdk_offscreen_window_move_resize;
impl_class->set_background = gdk_offscreen_window_set_background;
impl_class->get_events = gdk_offscreen_window_get_events;
impl_class->set_events = gdk_offscreen_window_set_events;
impl_class->reparent = gdk_offscreen_window_reparent;
impl_class->get_geometry = gdk_offscreen_window_get_geometry;
impl_class->shape_combine_region = gdk_offscreen_window_shape_combine_region;
impl_class->input_shape_combine_region = gdk_offscreen_window_input_shape_combine_region;
impl_class->set_static_gravities = gdk_offscreen_window_set_static_gravities;
impl_class->queue_antiexpose = gdk_offscreen_window_queue_antiexpose;
impl_class->translate = gdk_offscreen_window_translate;
impl_class->get_root_coords = gdk_offscreen_window_get_root_coords;
impl_class->get_device_state = gdk_offscreen_window_get_device_state;
impl_class->destroy = gdk_offscreen_window_destroy;
impl_class->resize_cairo_surface = gdk_offscreen_window_resize_cairo_surface;
}