summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRay Strode <rstrode@redhat.com>2007-03-29 21:56:35 -0400
committerRay Strode <rstrode@redhat.com>2007-03-29 21:56:35 -0400
commit31f7f32d2484413e5ac799c957daa1a37d4cf822 (patch)
treeb620cd0e0c068b9da02fc80a05a2b6a5f335b424 /src
parentfbaecaf279e9450c126e6f21e9f03fb08fa1dd92 (diff)
move files to src/ directory
Diffstat (limited to 'src')
-rw-r--r--src/Makefile3
-rw-r--r--src/pop-demo.c335
-rw-r--r--src/pop-event-listener.c877
-rw-r--r--src/pop-event-listener.h88
-rw-r--r--src/pop-overlay-window.c368
-rw-r--r--src/pop-overlay-window.h69
-rw-r--r--src/pop-window-stack.c396
-rw-r--r--src/pop-window-stack.h77
-rw-r--r--src/pop-window-view.c934
-rw-r--r--src/pop-window-view.h94
10 files changed, 3241 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..c30c56e
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,3 @@
+all:
+ gcc pop-window-stack.c pop-window-view.c pop-event-listener.c pop-demo.c pop-overlay-window.c pop-marshal.c -o pop-demo `pkg-config --cflags --libs glib-2.0 gtk+-2.0 cairo cairo-xlib xcomposite xdamage ` -ggdb3 -O0 -Wall
+
diff --git a/src/pop-demo.c b/src/pop-demo.c
new file mode 100644
index 0000000..3aa71df
--- /dev/null
+++ b/src/pop-demo.c
@@ -0,0 +1,335 @@
+#include <unistd.h>
+
+#include <glib.h>
+
+#include <X11/extensions/shape.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xfixes.h>
+
+#include <cairo.h>
+#include <cairo-xlib.h>
+
+#include <gdk/gdk.h>
+#include <gdk/gdkcairo.h>
+#include <gdk/gdkx.h>
+
+#include "pop-overlay-window.h"
+#include "pop-window-stack.h"
+#include "pop-event-listener.h"
+#include "pop-window-view.h"
+
+static PopWindowStack *stack = NULL;
+static PopEventListener *listener = NULL;
+GtkWidget *overlay_window;
+static int damage_extension_event_base,
+ damage_extension_error_base;
+
+static int shape_extension_event_base,
+ shape_extension_error_base;
+
+static gboolean
+composite_is_available (void)
+{
+ int event_base, error_base;
+
+ event_base = 0;
+ error_base = 0;
+ if (XCompositeQueryExtension (GDK_DISPLAY (), &event_base, &error_base))
+ return TRUE;
+ return FALSE;
+}
+
+static gboolean
+initialize_damage_extension (void)
+{
+ if (XDamageQueryExtension (GDK_DISPLAY (), &damage_extension_event_base,
+ &damage_extension_error_base))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+initialize_shape_extension (void)
+{
+ if (XShapeQueryExtension (GDK_DISPLAY (), &shape_extension_event_base,
+ &shape_extension_error_base))
+ return TRUE;
+
+ return FALSE;
+}
+
+gint
+window_compare (PopWindowView *view,
+ Window *x_window_id)
+{
+ GdkWindow *window;
+ if (!POP_IS_WINDOW_VIEW (view))
+ {
+ g_warning ("passed invalid data to compare func: %p\n", view);
+ return -1;
+ }
+
+ window = pop_window_view_get_window (view);
+ return ((gulong) GDK_WINDOW_XWINDOW (window)) - *x_window_id;
+}
+
+static void
+remove_window_from_list (GdkWindow *window)
+{
+ PopWindowView *view;
+
+ view = g_object_get_data (G_OBJECT (window), "pop-window-view");
+
+ if (view == NULL)
+ return;
+
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ pop_window_view_unset_window (view);
+ g_object_unref (view);
+
+ g_object_set_data (G_OBJECT (window), "pop-window-view", NULL);
+ g_object_unref (G_OBJECT (window));
+}
+
+static void
+add_window_to_list (GdkWindow *window)
+{
+ PopWindowView *view;
+
+ if (window == GTK_WIDGET (overlay_window)->window)
+ return;
+ {
+ Window parent, root, *children;
+ guint number_of_children, i;
+
+ gdk_error_trap_push ();
+ if (!XQueryTree (GDK_DISPLAY (),
+ GDK_WINDOW_XWINDOW (window),
+ &root, &parent, &children, &number_of_children))
+ return;
+ if (gdk_error_trap_pop ())
+ return;
+
+ for (i = 0; i < number_of_children; i++)
+ {
+ if (children[i] == GDK_WINDOW_XWINDOW (GTK_WIDGET (overlay_window)->window))
+ break;
+ }
+ XFree (children);
+ if (i != number_of_children)
+ return;
+ }
+
+ view = pop_window_view_new ();
+ if (!pop_window_view_set_window (view, window))
+ {
+ g_print ("couldn't redirect 0x%lx to overlay window\n",
+ GDK_WINDOW_XWINDOW (window));
+ return;
+ }
+
+ g_object_ref (G_OBJECT (window));
+ g_object_set_data (G_OBJECT (window), "pop-window-view", g_object_ref (view));
+}
+
+static void
+draw_window_from_stack (PopWindowStack *stack,
+ GdkWindow *window,
+ cairo_t *cairo_context)
+{
+ PopWindowView *view;
+
+ view = g_object_get_data (G_OBJECT (window), "pop-window-view");
+
+ if (view == NULL)
+ return;
+
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ pop_window_view_render_to_context (view, cairo_context);
+}
+
+static void
+draw_windows (cairo_t *cairo_context)
+{
+ pop_window_stack_foreach (stack,
+ (PopWindowStackForeachFunc)
+ draw_window_from_stack, cairo_context);
+}
+
+void
+add_window_from_stack_to_list (PopWindowStack *stack,
+ GdkWindow *window)
+{
+ add_window_to_list (g_object_ref (window));
+}
+
+static void
+get_initial_view_list (void)
+{
+
+ stack = pop_window_stack_get_for_screen (NULL);
+
+ pop_window_stack_foreach (stack,
+ (PopWindowStackForeachFunc)
+ add_window_from_stack_to_list, NULL);
+}
+
+static gboolean
+on_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ cairo_t *cairo_context;
+ GdkRectangle monitor_area;
+ GdkScreen *screen;
+ double x_scale_factor, y_scale_factor;
+
+ screen = gtk_widget_get_screen (widget);
+ gdk_screen_get_monitor_geometry (screen,
+ gdk_screen_get_monitor_at_window (screen, widget->window), &monitor_area);
+
+ x_scale_factor = widget->allocation.width / ((double) monitor_area.width);
+ y_scale_factor = widget->allocation.height / ((double) monitor_area.height);
+
+ cairo_context = gdk_cairo_create (widget->window);
+ //cairo_scale (cairo_context, x_scale_factor, y_scale_factor);
+ cairo_set_source_rgb (cairo_context, 0.5, 0.5, 0.6);
+ cairo_paint (cairo_context);
+ draw_windows (cairo_context);
+ cairo_destroy (cairo_context);
+
+ return FALSE;
+}
+
+static void
+on_map (GtkWidget *widget)
+{
+ get_initial_view_list ();
+}
+
+static void
+on_damage_event (XDamageNotifyEvent *damage_event,
+ PopOverlayWindow *overlay_window)
+{
+ gtk_widget_queue_draw (GTK_WIDGET (overlay_window));
+}
+
+static void
+on_create_window_event (XCreateWindowEvent *window_event,
+ PopOverlayWindow *overlay_window)
+{
+ GdkWindow *window;
+
+ if (window_event->parent != GDK_ROOT_WINDOW ())
+ return;
+
+ window = gdk_window_foreign_new (window_event->window);
+ if (window != NULL)
+ add_window_to_list (window);
+ else
+ g_print ("recently created window 0x%lx has already been destroyed\n",
+ window_event->window);
+}
+
+static void
+on_destroy_window_event (XDestroyWindowEvent *window_event,
+ PopOverlayWindow *overlay_window)
+{
+ GdkWindow *window;
+
+ window = gdk_window_lookup (window_event->window);
+ if (window == NULL)
+ return;
+ remove_window_from_list (window);
+ gtk_widget_queue_draw (GTK_WIDGET (overlay_window));
+}
+
+static gboolean
+x_event_is_damage_event (XEvent *x_event)
+{
+ return x_event->type == damage_extension_event_base + XDamageNotify;
+}
+
+static gboolean
+x_event_is_create_window_event (XEvent *x_event)
+{
+ return /* x_event->type == CreateNotify || */ x_event->type == MapNotify;
+}
+
+static gboolean
+x_event_is_destroy_window_event (XEvent *x_event)
+{
+ return /* x_event->type == DestroyNotify || */ x_event->type == UnmapNotify;
+}
+
+static GdkFilterReturn
+on_event (XEvent *x_event,
+ GdkEvent *event,
+ PopOverlayWindow *overlay_window)
+{
+
+ if (((GdkEventAny *) event)->window == GTK_WIDGET (overlay_window)->window)
+ return GDK_FILTER_CONTINUE;
+ if (x_event_is_damage_event (x_event))
+ on_damage_event ((XDamageNotifyEvent *) x_event, overlay_window);
+ else if (x_event_is_create_window_event (x_event))
+ on_create_window_event ((XCreateWindowEvent *) x_event, overlay_window);
+ else if (x_event_is_destroy_window_event (x_event))
+ on_destroy_window_event ((XDestroyWindowEvent *) x_event, overlay_window);
+
+ gtk_widget_queue_draw (GTK_WIDGET (overlay_window));
+
+ return GDK_FILTER_CONTINUE;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GdkWindow *root_window;
+
+ gtk_init (&argc, &argv);
+
+ if (!composite_is_available ())
+ {
+ g_printerr ("composite extension not found\n");
+ return 1;
+ }
+
+ if (!initialize_damage_extension ())
+ {
+ g_printerr ("damage extension not found\n");
+ return 2;
+ }
+
+ initialize_shape_extension ();
+
+ overlay_window = pop_overlay_window_new ();
+#if 0
+ overlay_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_app_paintable (GTK_WIDGET (overlay_window), TRUE);
+ gtk_window_set_default_size (GTK_WINDOW (overlay_window), 640, 480);
+#endif
+ gtk_widget_realize (GTK_WIDGET (overlay_window));
+
+ g_signal_connect (G_OBJECT (overlay_window), "expose-event",
+ G_CALLBACK (on_expose_event), NULL);
+ g_signal_connect (G_OBJECT (overlay_window), "map",
+ G_CALLBACK (on_map), NULL);
+
+ root_window = gdk_get_default_root_window ();
+ gdk_window_set_events (root_window, GDK_SUBSTRUCTURE_MASK);
+ gdk_window_add_filter (NULL, (GdkFilterFunc) on_event, overlay_window);
+
+ gtk_widget_show (overlay_window);
+
+ listener = pop_event_listener_new (gtk_widget_get_display (overlay_window));
+ pop_event_listener_start (listener);
+
+ gtk_main();
+
+ return 0;
+}
diff --git a/src/pop-event-listener.c b/src/pop-event-listener.c
new file mode 100644
index 0000000..c934c9a
--- /dev/null
+++ b/src/pop-event-listener.c
@@ -0,0 +1,877 @@
+/* pop-event-listener.c - emits signals based on display events
+ *
+ * Copyright (C) 2007 Ray Strode <rstrode@redhat.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 2, 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include "pop-event-listener.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+#include <X11/extensions/shape.h>
+#include <X11/extensions/Xdamage.h>
+
+#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
+
+#include <gtk/gtk.h>
+
+#include "pop-marshal.h"
+
+static void pop_event_listener_do_finalize (GObject *object);
+static void pop_event_listener_destroy_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window);
+static void pop_event_listener_configure_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window);
+static void pop_event_listener_map_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window);
+static void pop_event_listener_unmap_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window);
+static void pop_event_listener_damage_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window);
+static void pop_event_listener_shape_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window);
+
+static void pop_event_listener_reparent_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window);
+
+static int pop_event_listener_damage_notify_event_type,
+ pop_event_listener_shape_notify_event_type;
+
+struct _PopEventListenerPrivate
+{
+ GHashTable *event_handlers;
+};
+
+
+/* XXX: This is a bit of a hack. gdk doesn't have
+ * events for some of X extensions we need, so we
+ * are rolling our own using an event filter.
+ *
+ * The event types are given high negative values so they
+ * less likely to conflict with gdk in the future, and all
+ * the event structures are less than sizeof (GdkEvent) and
+ * have the same first few members as GdkEventAny
+ */
+typedef enum
+{
+ POP_DAMAGE = -1000,
+ POP_SHAPE = -1001,
+ POP_REPARENT = -1002
+} PopEventType;
+
+typedef struct _PopEventDamage PopEventDamage;
+struct _PopEventDamage
+{
+ PopEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkRectangle area;
+};
+
+typedef struct _PopEventShape PopEventShape;
+struct _PopEventShape
+{
+ PopEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+};
+
+typedef struct _PopEventReparent PopEventReparent;
+struct _PopEventReparent
+{
+ PopEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkWindow *parent;
+ gint x;
+ gint y;
+};
+
+typedef void (* PopEventListenerEventHandler) (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window);
+
+static void
+pop_event_listener_class_install_signals (PopEventListenerClass *listener_class);
+
+#if 0
+static void
+pop_event_listener_class_install_properties
+(PopEventListenerClass * listener_class);
+
+static void pop_event_listener_set_property (GObject * object,
+ guint prop_id,
+ const GValue * value,
+ GParamSpec * pspec);
+static void pop_event_listener_get_property (GObject * object,
+ guint prop_id,
+ GValue * value,
+ GParamSpec * pspec);
+
+enum
+{
+ PROP_0 = 0,
+ PROP_BAR
+};
+
+#define POP_EVENT_LISTENER_DEFAULT_BAR 1
+
+#endif
+
+enum
+{
+ WINDOW_CREATED = 0,
+ WINDOW_DESTROYED,
+ WINDOW_SHOWN,
+ WINDOW_HIDDEN,
+ WINDOW_MOVED,
+ WINDOW_RESIZED,
+ WINDOW_RESHAPED,
+ WINDOW_REPARENTED,
+ WINDOW_REDRAWN,
+ NUMBER_OF_SIGNALS
+};
+
+static guint pop_event_listener_signals[NUMBER_OF_SIGNALS];
+
+G_DEFINE_TYPE (PopEventListener, pop_event_listener, G_TYPE_OBJECT);
+
+static void
+pop_event_listener_class_init (PopEventListenerClass *listener_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (listener_class);
+
+ object_class->finalize = pop_event_listener_do_finalize;
+
+#if 0
+ pop_event_listener_class_install_properties (listener_class);
+#endif
+ pop_event_listener_class_install_signals (listener_class);
+
+ g_type_class_add_private (listener_class,
+ sizeof (PopEventListenerPrivate));
+}
+
+static void
+created (PopEventListener *listener,
+ GdkWindow *window)
+{
+ g_print ("created 0x%lx\n", GDK_WINDOW_XWINDOW (window));
+}
+
+static void
+destroyed (PopEventListener *listener,
+ GdkWindow *window)
+{
+ g_print ("destroyed 0x%lx\n", GDK_WINDOW_XWINDOW (window));
+}
+
+static void
+moved (PopEventListener *listener,
+ GdkWindow *window)
+{
+ g_print ("moved 0x%lx\n", GDK_WINDOW_XWINDOW (window));
+}
+
+static void
+resized (PopEventListener *listener,
+ GdkWindow *window)
+{
+ g_print ("resized 0x%lx\n", GDK_WINDOW_XWINDOW (window));
+}
+
+static void
+redrawn (PopEventListener *listener,
+ GdkWindow *window,
+ GdkRectangle *area)
+{
+ g_print ("redrawn 0x%lx\n", GDK_WINDOW_XWINDOW (window));
+}
+
+static void
+reparented (PopEventListener *listener,
+ GdkWindow *window,
+ GdkWindow *parent)
+{
+ g_print ("reparented 0x%lx\n", GDK_WINDOW_XWINDOW (window));
+}
+
+static void
+shown (PopEventListener *listener,
+ GdkWindow *window)
+{
+ g_print ("shown 0x%lx\n", GDK_WINDOW_XWINDOW (window));
+}
+
+static void
+hidden (PopEventListener *listener,
+ GdkWindow *window)
+{
+ g_print ("hidden 0x%lx\n", GDK_WINDOW_XWINDOW (window));
+}
+
+static void
+pop_event_listener_class_install_signals (PopEventListenerClass *listener_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (listener_class);
+
+ pop_event_listener_signals[WINDOW_CREATED] =
+ g_signal_new ("window-created", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (PopEventListenerClass, window_created),
+ NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+ listener_class->window_created = created;
+
+ pop_event_listener_signals[WINDOW_DESTROYED] =
+ g_signal_new ("window-destroyed", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (PopEventListenerClass, window_destroyed),
+ NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+ listener_class->window_destroyed = destroyed;
+
+ pop_event_listener_signals[WINDOW_SHOWN] =
+ g_signal_new ("window-shown", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (PopEventListenerClass, window_shown),
+ NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+ listener_class->window_shown = shown;
+
+ pop_event_listener_signals[WINDOW_HIDDEN] =
+ g_signal_new ("window-hidden", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (PopEventListenerClass, window_hidden),
+ NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+ listener_class->window_hidden = hidden;
+
+ pop_event_listener_signals[WINDOW_MOVED] =
+ g_signal_new ("window-moved", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (PopEventListenerClass, window_moved),
+ NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+ listener_class->window_moved = moved;
+
+ pop_event_listener_signals[WINDOW_RESIZED] =
+ g_signal_new ("window-resized", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (PopEventListenerClass, window_resized),
+ NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+ listener_class->window_resized = resized;
+
+ pop_event_listener_signals[WINDOW_RESHAPED] =
+ g_signal_new ("window-reshaped", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (PopEventListenerClass, window_reshaped),
+ NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+ listener_class->window_reshaped = NULL;
+
+ pop_event_listener_signals[WINDOW_REPARENTED] =
+ g_signal_new ("window-reparented", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (PopEventListenerClass, window_reparented),
+ NULL, NULL, pop_marshal_VOID__OBJECT_OBJECT, G_TYPE_NONE, 2,
+ G_TYPE_OBJECT, G_TYPE_OBJECT);
+ listener_class->window_reparented = reparented;
+
+ pop_event_listener_signals[WINDOW_REDRAWN] =
+ g_signal_new ("window-redrawn", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (PopEventListenerClass, window_redrawn),
+ NULL, NULL, pop_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2,
+ G_TYPE_OBJECT, G_TYPE_POINTER);
+ listener_class->window_redrawn = redrawn;
+}
+
+#if 0
+static void
+pop_event_listener_class_install_properties (PopEventListenerClass *listener_class)
+{
+ GObjectClass *object_class;
+ GParamSpec *bar_spec;
+
+ object_class = G_OBJECT_CLASS (listener_class);
+ object_class->set_property = pop_event_listener_set_property;
+ object_class->get_property = pop_event_listener_get_property;
+
+ bar_spec = g_param_spec_int ("bar", _("Bar"),
+ _("The amount of bar"),
+ 0, G_MAXINT,
+ POP_EVENT_LISTENER_DEFAULT_BAR,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_BAR, bar_spec);
+}
+#endif
+
+static void
+pop_event_listener_initialize_damage_extension (PopEventListener *listener)
+{
+ int event_base, error_base;
+ XDamageQueryExtension (GDK_DISPLAY (), &event_base, &error_base);
+
+ pop_event_listener_damage_notify_event_type = event_base + XDamageNotify;
+}
+
+static void
+pop_event_listener_initialize_shape_extension (PopEventListener *listener)
+{
+ int event_base, error_base;
+ XShapeQueryExtension (GDK_DISPLAY (), &event_base, &error_base);
+
+ pop_event_listener_shape_notify_event_type = event_base + ShapeNotify;
+}
+
+static void
+pop_event_listener_register_event_handlers (PopEventListener *listener)
+{
+ listener->priv->event_handlers = g_hash_table_new (NULL, NULL);
+
+ g_hash_table_insert (listener->priv->event_handlers,
+ GINT_TO_POINTER (GDK_DESTROY),
+ pop_event_listener_destroy_handler);
+
+ g_hash_table_insert (listener->priv->event_handlers,
+ GINT_TO_POINTER (GDK_CONFIGURE),
+ pop_event_listener_configure_handler);
+
+ g_hash_table_insert (listener->priv->event_handlers,
+ GINT_TO_POINTER (GDK_MAP),
+ pop_event_listener_map_handler);
+
+ g_hash_table_insert (listener->priv->event_handlers,
+ GINT_TO_POINTER (GDK_UNMAP),
+ pop_event_listener_unmap_handler);
+
+ g_hash_table_insert (listener->priv->event_handlers,
+ GINT_TO_POINTER (POP_DAMAGE),
+ pop_event_listener_damage_handler);
+
+ g_hash_table_insert (listener->priv->event_handlers,
+ GINT_TO_POINTER (POP_SHAPE),
+ pop_event_listener_shape_handler);
+
+ g_hash_table_insert (listener->priv->event_handlers,
+ GINT_TO_POINTER (POP_REPARENT),
+ pop_event_listener_reparent_handler);
+}
+
+static void
+pop_event_listener_init (PopEventListener *listener)
+{
+ listener->priv = G_TYPE_INSTANCE_GET_PRIVATE (listener,
+ POP_TYPE_EVENT_LISTENER,
+ PopEventListenerPrivate);
+ pop_event_listener_initialize_damage_extension (listener);
+ pop_event_listener_initialize_shape_extension (listener);
+ pop_event_listener_register_event_handlers (listener);
+}
+
+static void
+pop_event_listener_do_finalize (GObject *object)
+{
+ PopEventListener *listener;
+ GObjectClass *parent_class;
+
+ listener = POP_EVENT_LISTENER (object);
+
+ g_hash_table_destroy (listener->priv->event_handlers);
+
+ parent_class = G_OBJECT_CLASS (pop_event_listener_parent_class);
+
+ if (parent_class->finalize != NULL)
+ parent_class->finalize (object);
+}
+
+#if 0
+static void
+pop_event_listener_set_property (GObject * object,
+ guint prop_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ PopEventListener *listener = POP_EVENT_LISTENER (object);
+
+ switch (prop_id)
+ {
+ case PROP_BAR:
+ pop_event_listener_set_bar (listener, g_value_get_int (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+pop_event_listener_get_property (GObject * object,
+ guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ PopEventListener *listener = POP_EVENT_LISTENER (object);
+
+ switch (prop_id)
+ {
+ case PROP_BAR:
+ g_value_set_int (value, pop_event_listener_get_bar (listener));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+#endif
+
+GQuark
+pop_event_listener_error_quark (void)
+{
+ static GQuark error_quark = 0;
+
+ if (error_quark == 0)
+ error_quark = g_quark_from_static_string ("pop-event-listener");
+
+ return error_quark;
+}
+
+PopEventListener *
+pop_event_listener_new (GdkDisplay *display)
+{
+ PopEventListener *listener;
+
+ listener = g_object_new (POP_TYPE_EVENT_LISTENER, NULL);
+
+ return listener;
+}
+
+#if 0
+void
+pop_event_listener_set_bar (PopEventListener * listener, int bar)
+{
+ if (listener->priv->bar != bar)
+ {
+ listener->priv->bar = bar;
+ g_object_notify (G_OBJECT (listener), "bar");
+ }
+}
+
+int
+pop_event_listener_get_bar (PopEventListener * listener)
+{
+ return listener->priv->bar;
+}
+#endif
+
+static GQuark
+pop_event_listener_get_signal_detail_for_window (PopEventListener *listener,
+ GdkWindow *window)
+{
+ gchar window_id[sizeof ("ffffffff")];
+ g_snprintf (window_id, sizeof (window_id),
+ "%lx", GDK_WINDOW_XWINDOW (window));
+
+ return g_quark_from_string (window_id);
+}
+
+static void
+pop_event_listener_destroy_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window)
+{
+ g_signal_emit (G_OBJECT (listener),
+ pop_event_listener_signals[WINDOW_CREATED],
+ pop_event_listener_get_signal_detail_for_window (listener,
+ window),
+ window);
+}
+
+static void
+pop_event_listener_get_cached_window_geometry (PopEventListener *listener,
+ GdkWindow *window,
+ int *x,
+ int *y,
+ int *width,
+ int *height)
+{
+ if (x)
+ *x = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window),
+ "pop-event-listener-cached-window-x"));
+
+ if (y)
+ *y = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window),
+ "pop-event-listener-cached-window-y"));
+
+ if (width)
+ *width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window),
+ "pop-event-listener-cached-window-width"));
+
+ if (height)
+ *height = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window),
+ "pop-event-listener-cached-window-height"));
+}
+
+static void
+pop_event_listener_cache_window_geometry (PopEventListener *listener,
+ GdkWindow *window,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ g_object_set_data (G_OBJECT (window),
+ "pop-event-listener-cached-window-x",
+ GINT_TO_POINTER (x));
+
+ g_object_set_data (G_OBJECT (window),
+ "pop-event-listener-cached-window-y",
+ GINT_TO_POINTER (y));
+
+ g_object_set_data (G_OBJECT (window),
+ "pop-event-listener-cached-window-width",
+ GINT_TO_POINTER (width));
+
+ g_object_set_data (G_OBJECT (window),
+ "pop-event-listener-cached-window-height",
+ GINT_TO_POINTER (height));
+}
+
+static void
+pop_event_listener_configure_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window)
+{
+ GdkEventConfigure *configure_event;
+ gint x, y, width, height;
+ gboolean changed_position, changed_size;
+
+ configure_event = (GdkEventConfigure *) event;
+
+ pop_event_listener_get_cached_window_geometry (listener, window,
+ &x, &y, &width, &height);
+
+ changed_position = (x != configure_event->x) || (y != configure_event->y);
+ changed_size = (width != configure_event->width)
+ || (height != configure_event->height);
+
+ if (changed_position)
+ {
+ g_signal_emit (G_OBJECT (listener),
+ pop_event_listener_signals[WINDOW_MOVED],
+ pop_event_listener_get_signal_detail_for_window (listener,
+ window),
+ window);
+ }
+
+ if (changed_size)
+ {
+ g_signal_emit (G_OBJECT (listener),
+ pop_event_listener_signals[WINDOW_RESIZED],
+ pop_event_listener_get_signal_detail_for_window (listener,
+ window),
+ window);
+ }
+
+ pop_event_listener_cache_window_geometry (listener, window,
+ configure_event->x,
+ configure_event->y,
+ configure_event->width,
+ configure_event->height);
+}
+
+static void
+pop_event_listener_map_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window)
+{
+ g_signal_emit (G_OBJECT (listener),
+ pop_event_listener_signals[WINDOW_SHOWN],
+ pop_event_listener_get_signal_detail_for_window (listener,
+ window),
+ window);
+}
+
+static void
+pop_event_listener_unmap_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window)
+{
+
+ g_signal_emit (G_OBJECT (listener),
+ pop_event_listener_signals[WINDOW_HIDDEN],
+ pop_event_listener_get_signal_detail_for_window (listener,
+ window),
+ window);
+}
+
+static void
+pop_event_listener_damage_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window)
+{
+ PopEventDamage *damage_event;
+
+ g_assert (POP_IS_EVENT_LISTENER (listener));
+ g_assert (event->type == POP_DAMAGE);
+
+ damage_event = (PopEventDamage *) event;
+
+ g_signal_emit (G_OBJECT (listener),
+ pop_event_listener_signals[WINDOW_REDRAWN],
+ pop_event_listener_get_signal_detail_for_window (listener,
+ window),
+ window, &damage_event->area);
+}
+
+static void
+pop_event_listener_shape_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window)
+{
+ PopEventShape *shape_event;
+
+ g_assert (POP_IS_EVENT_LISTENER (listener));
+ g_assert (event->type == POP_SHAPE);
+
+ shape_event = (PopEventShape *) event;
+ g_signal_emit (G_OBJECT (listener),
+ pop_event_listener_signals[WINDOW_RESHAPED],
+ pop_event_listener_get_signal_detail_for_window (listener,
+ window),
+ window);
+}
+
+static void
+pop_event_listener_reparent_handler (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window)
+{
+ PopEventReparent *reparent_event;
+
+ g_assert (POP_IS_EVENT_LISTENER (listener));
+ g_assert (event->type == POP_REPARENT);
+
+ reparent_event = (PopEventReparent *) event;
+
+ g_signal_emit (G_OBJECT (listener),
+ pop_event_listener_signals[WINDOW_REPARENTED],
+ pop_event_listener_get_signal_detail_for_window (listener,
+ window),
+ window, reparent_event->parent);
+}
+
+static void
+pop_event_listener_process_event_for_window (PopEventListener *listener,
+ GdkEvent *event,
+ GdkWindow *window)
+{
+ PopEventListenerEventHandler event_handler;
+
+ event_handler = (PopEventListenerEventHandler)
+ g_hash_table_lookup (listener->priv->event_handlers,
+ GINT_TO_POINTER (event->type));
+
+ if (event_handler == NULL)
+ return;
+
+ event_handler (listener, event, window);
+}
+
+static void
+pop_event_listener_event_handler (GdkEvent *event,
+ gpointer data)
+{
+ PopEventListener *listener;
+
+ g_assert (event != NULL);
+ g_assert (POP_IS_EVENT_LISTENER (data));
+
+ listener = POP_EVENT_LISTENER (data);
+
+ if (event->any.window != NULL)
+ pop_event_listener_process_event_for_window (listener, event,
+ event->any.window);
+
+ /* chain up to the default handler if it's not one of our custom
+ * events
+ */
+ if (event->type >= GDK_NOTHING)
+ gtk_main_do_event (event);
+}
+
+GdkFilterReturn
+pop_event_listener_filter (XEvent *x_event,
+ GdkEvent *event,
+ PopEventListener *listener)
+{
+ if (x_event->type == pop_event_listener_damage_notify_event_type)
+ {
+ XDamageNotifyEvent *x_damage_event;
+ PopEventDamage *damage_event;
+ GdkWindow *window;
+
+ window = gdk_window_lookup (x_event->xany.window);
+
+ if (window != NULL)
+ {
+ x_damage_event = (XDamageNotifyEvent *) x_event;
+ g_assert (sizeof (*damage_event) <= sizeof (*event));
+ damage_event = (PopEventDamage *) event;
+ damage_event->window = g_object_ref (window);
+ damage_event->type = POP_DAMAGE;
+ damage_event->send_event = FALSE;
+ damage_event->area.x = x_damage_event->area.x;
+ damage_event->area.y = x_damage_event->area.y;
+ damage_event->area.width = x_damage_event->area.width;
+ damage_event->area.height = x_damage_event->area.height;
+
+ return GDK_FILTER_TRANSLATE;
+ }
+ }
+ else if (x_event->type == pop_event_listener_shape_notify_event_type)
+ {
+ XShapeEvent *x_shape_event;
+ PopEventShape *shape_event;
+ GdkWindow *window;
+
+ window = gdk_window_lookup (x_event->xany.window);
+
+ if (window != NULL)
+ {
+ x_shape_event = (XShapeEvent *) x_event;
+ g_assert (sizeof (*shape_event) <= sizeof (*event));
+ shape_event = (PopEventShape *) event;
+ shape_event->window = g_object_ref (window);
+ shape_event->type = POP_SHAPE;
+ shape_event->send_event = FALSE;
+
+ return GDK_FILTER_TRANSLATE;
+ }
+ }
+ else if (x_event->type == ReparentNotify)
+ {
+ XReparentEvent *x_reparent_event;
+ PopEventReparent *reparent_event;
+ GdkWindow *window, *parent;
+
+ window = gdk_window_lookup (x_event->xany.window);
+
+ if (window != NULL)
+ {
+ x_reparent_event = (XReparentEvent *) x_event;
+ g_assert (sizeof (*reparent_event) <= sizeof (*event));
+ reparent_event = (PopEventReparent *) event;
+ reparent_event->window = g_object_ref (window);
+ reparent_event->type = POP_REPARENT;
+ reparent_event->send_event = FALSE;
+
+ parent = gdk_window_lookup (x_reparent_event->parent);
+
+ if (parent == NULL)
+ parent = gdk_window_foreign_new (x_reparent_event->parent);
+
+ reparent_event->parent = parent;
+
+ return GDK_FILTER_TRANSLATE;
+ }
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+void
+pop_event_listener_start (PopEventListener *listener)
+{
+ /* used for questionable hack to get damage and shape events reported
+ */
+ gdk_window_add_filter (NULL, (GdkFilterFunc) pop_event_listener_filter,
+ listener);
+
+ /* translates gdk events to signals on the listener object
+ */
+ gdk_event_handler_set (pop_event_listener_event_handler,
+ listener, NULL);
+}
+
+void
+pop_event_listener_stop (PopEventListener *listener)
+{
+ gdk_event_handler_set ((GdkEventFunc) gtk_main_do_event, NULL, NULL);
+}
+
+PopEventListener *
+pop_event_listener_get_default (void)
+{
+ static PopEventListener *listener = NULL;
+
+ if (listener == NULL)
+ {
+ listener = pop_event_listener_new (gdk_display_get_default ());
+ pop_event_listener_start (listener);
+ }
+
+ return g_object_ref (listener);
+}
+
+#ifdef POP_EVENT_LISTENER_ENABLE_TEST
+
+#include <stdio.h>
+#include <glib.h>
+
+int
+main (int argc, char **argv)
+{
+ PopEventListener *listener;
+ int exit_code;
+
+ g_log_set_always_fatal (G_LOG_LEVEL_ERROR
+ | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
+
+ g_type_init ();
+
+ g_message ("creating instance of 'event listener' object...");
+ listener = pop_event_listener_new (gdk_display_get_default ());
+ g_message ("'event listener' object created successfully");
+
+ g_message
+ ("destroying previously created 'event listener' object...");
+ g_object_unref (listener);
+ g_message ("'event listener' object destroyed successfully");
+
+ exit_code = 0;
+
+ return exit_code;
+}
+#endif /* POP_EVENT_LISTENER_ENABLE_TEST */
diff --git a/src/pop-event-listener.h b/src/pop-event-listener.h
new file mode 100644
index 0000000..d302f44
--- /dev/null
+++ b/src/pop-event-listener.h
@@ -0,0 +1,88 @@
+/* pop-event-listener.h - Listens for events on toplevel window and
+ * passes them to the appropriate window model
+ *
+ * Copyright (C) 2007 Ray Strode <rstrode@redhat.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 2, 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#ifndef POP_EVENT_LISTENER_H
+#define POP_EVENT_LISTENER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdk/gdk.h>
+
+#include "pop-window-view.h"
+
+G_BEGIN_DECLS
+#define POP_TYPE_EVENT_LISTENER (pop_event_listener_get_type ())
+#define POP_EVENT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), POP_TYPE_EVENT_LISTENER, PopEventListener))
+#define POP_EVENT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), POP_TYPE_EVENT_LISTENER, PopEventListenerClass))
+#define POP_IS_EVENT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POP_TYPE_EVENT_LISTENER))
+#define POP_IS_EVENT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), POP_TYPE_EVENT_LISTENER))
+#define POP_EVENT_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), POP_TYPE_EVENT_LISTENER, PopEventListenerClass))
+#define POP_EVENT_LISTENER_ERROR (pop_event_listener_error_quark ())
+typedef struct _PopEventListener PopEventListener;
+typedef struct _PopEventListenerClass PopEventListenerClass;
+typedef struct _PopEventListenerPrivate PopEventListenerPrivate;
+typedef enum _PopEventListenerError PopEventListenerError;
+
+struct _PopEventListener
+{
+ GObject parent;
+
+ /*< private > */
+ PopEventListenerPrivate *priv;
+};
+
+struct _PopEventListenerClass
+{
+ GObjectClass parent_class;
+
+ /* signals */
+ void (* window_created) (PopEventListener *listener, GdkWindow *window);
+ void (* window_destroyed) (PopEventListener *listener, GdkWindow *window);
+ void (* window_shown) (PopEventListener *listener, GdkWindow *window);
+ void (* window_hidden) (PopEventListener *listener, GdkWindow *window);
+ void (* window_resized) (PopEventListener *listener, GdkWindow *window);
+ void (* window_reshaped) (PopEventListener *listener, GdkWindow *window);
+ void (* window_reparented) (PopEventListener *listener, GdkWindow *window,
+ GdkWindow *new_parent);
+ void (* window_moved) (PopEventListener *listener, GdkWindow *window);
+ void (* window_redrawn) (PopEventListener *listener, GdkWindow *window,
+ GdkRectangle *redrawn_area);
+};
+
+enum _PopEventListenerError
+{
+ POP_EVENT_LISTENER_ERROR_GENERIC = 0,
+};
+
+#ifndef POP_HIDE_FUNCTION_DECLARATIONS
+GType pop_event_listener_get_type (void);
+GQuark pop_event_listener_error_quark (void);
+
+PopEventListener *pop_event_listener_new (GdkDisplay *display) G_GNUC_MALLOC;
+void pop_event_listener_start (PopEventListener *listener);
+void pop_event_listener_stop (PopEventListener *listener);
+
+PopEventListener *pop_event_listener_get_default (void);
+
+#endif
+
+G_END_DECLS
+#endif /* POP_EVENT_LISTENER_H */
diff --git a/src/pop-overlay-window.c b/src/pop-overlay-window.c
new file mode 100644
index 0000000..5e7c48a
--- /dev/null
+++ b/src/pop-overlay-window.c
@@ -0,0 +1,368 @@
+/* pop-overlay-window.c - special toplevel overlay window
+ *
+ * Copyright (C) 200y Ray Strode <rstrode@redhat.com>
+ *
+ * This program 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, 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 Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Originally written by: Ray Strode <rstrode@redhat.com>
+ */
+#include "pop-overlay-window.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xfixes.h>
+
+#include <gdk/gdkx.h>
+#include <gtk/gtkwindow.h>
+
+static void pop_overlay_window_do_finalize (GObject *object);
+static void pop_overlay_window_do_realize (GtkWidget *widget);
+static void pop_overlay_window_do_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+
+
+struct _PopOverlayWindowPrivate
+{
+ GdkWindow *parent_window;
+};
+
+G_DEFINE_TYPE (PopOverlayWindow, pop_overlay_window, GTK_TYPE_WINDOW);
+
+static void
+pop_overlay_window_class_init (PopOverlayWindowClass *window_class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = G_OBJECT_CLASS (window_class);
+ widget_class = GTK_WIDGET_CLASS (window_class);
+
+ object_class->finalize = pop_overlay_window_do_finalize;
+ widget_class->realize = pop_overlay_window_do_realize;
+ widget_class->size_allocate = pop_overlay_window_do_size_allocate;
+
+ g_type_class_add_private (object_class, sizeof (PopOverlayWindowPrivate));
+}
+
+static void
+pop_overlay_window_init (PopOverlayWindow *overlay_window)
+{
+ overlay_window->priv = G_TYPE_INSTANCE_GET_PRIVATE (overlay_window,
+ POP_TYPE_OVERLAY_WINDOW,
+ PopOverlayWindowPrivate);
+}
+
+static void
+pop_overlay_window_do_finalize (GObject *object)
+{
+ PopOverlayWindow *window;
+ GObjectClass *parent_class;
+
+ window = POP_OVERLAY_WINDOW (object);
+
+ parent_class = G_OBJECT_CLASS (pop_overlay_window_parent_class);
+
+ if (parent_class->finalize != NULL)
+ parent_class->finalize (object);
+}
+
+static void
+pop_overlay_window_setup_parent_window (PopOverlayWindow *overlay_window)
+{
+ GdkDisplay *display;
+ GdkWindow *root_window;
+ Window parent_xwindow;
+
+ g_assert (POP_IS_OVERLAY_WINDOW (overlay_window));
+ g_assert (overlay_window->priv->parent_window == NULL);
+
+ display = gtk_widget_get_display (GTK_WIDGET (overlay_window));
+ root_window = gtk_widget_get_root_window (GTK_WIDGET (overlay_window));
+
+ parent_xwindow = XCompositeGetOverlayWindow (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XWINDOW (root_window));
+ overlay_window->priv->parent_window = gdk_window_foreign_new (parent_xwindow);
+
+ gdk_window_set_events (overlay_window->priv->parent_window,
+ GDK_EXPOSURE_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_FOCUS_CHANGE_MASK
+ | GDK_STRUCTURE_MASK
+ | GDK_BUTTON_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK);
+}
+
+static void
+pop_overlay_window_get_initial_display_window_attributes (PopOverlayWindow *overlay_window,
+ GdkWindowAttr *attributes,
+ gint *attributes_mask)
+{
+ GtkWidget *widget;
+ static gint new_attributes_mask = GDK_WA_COLORMAP
+ | GDK_WA_VISUAL
+ | GDK_WA_X
+ | GDK_WA_Y;
+ static GdkEventMask new_event_mask = GDK_EXPOSURE_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_FOCUS_CHANGE_MASK
+ | GDK_STRUCTURE_MASK
+ | GDK_BUTTON_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK;
+ gint x, y, width, height;
+ GdkWindowAttr new_attributes;
+
+ g_assert (POP_IS_OVERLAY_WINDOW (overlay_window));
+ g_assert (overlay_window->priv->parent_window != NULL);
+ g_assert (attributes != NULL);
+ g_assert (attributes_mask != NULL);
+
+ widget = GTK_WIDGET (overlay_window);
+
+ gdk_window_get_geometry (overlay_window->priv->parent_window,
+ &x, &y, &width, &height, NULL);
+ new_attributes.window_type = GDK_WINDOW_TOPLEVEL;
+ new_attributes.wclass = GDK_INPUT_OUTPUT;
+ new_attributes.colormap = gtk_widget_get_colormap (widget);
+ new_attributes.visual = gtk_widget_get_visual (widget);
+ new_attributes.x = x;
+ new_attributes.y = y;
+ new_attributes.width = width;
+ new_attributes.height = height;
+ new_attributes.event_mask = new_event_mask;
+
+ *attributes = new_attributes;
+ *attributes_mask = new_attributes_mask;
+}
+
+static void
+pop_overlay_window_setup_display_window (PopOverlayWindow *overlay_window)
+{
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+ GtkWidget *widget;
+ GdkScreen *screen;
+
+ g_assert (POP_IS_OVERLAY_WINDOW (overlay_window));
+ g_assert (overlay_window->priv->parent_window != NULL);
+ g_assert (GTK_WIDGET (overlay_window)->window == NULL);
+
+ widget = GTK_WIDGET (overlay_window);
+ screen = gtk_widget_get_screen (widget);
+
+ pop_overlay_window_get_initial_display_window_attributes (POP_OVERLAY_WINDOW (widget),
+ &attributes,
+ &attributes_mask);
+ widget->window = gdk_window_new (overlay_window->priv->parent_window,
+ &attributes, attributes_mask);
+ gdk_window_set_user_data (widget->window, widget);
+
+ gdk_window_set_decorations (widget->window, (GdkWMDecoration) 0);
+}
+
+static void
+pop_overlay_window_setup_initial_geometry (PopOverlayWindow *overlay_window)
+{
+ GtkRequisition requisition;
+ GtkAllocation allocation;
+ int x, y, width, height;
+ GtkWidget *widget;
+
+ g_assert (POP_IS_OVERLAY_WINDOW (overlay_window));
+ g_assert (GTK_WIDGET (overlay_window)->window != NULL);
+
+ widget = GTK_WIDGET (overlay_window);
+
+ gdk_window_get_position (widget->window, &x, &y);
+ gdk_drawable_get_size (GDK_DRAWABLE (widget->window), &width, &height);
+ gtk_widget_size_request (widget, &requisition);
+ allocation.x = x;
+ allocation.y = y;
+ allocation.width = width;
+ allocation.height = height;
+ gtk_widget_size_allocate (widget, &allocation);
+ gtk_window_resize (GTK_WINDOW (widget), width, height);
+}
+
+static void
+pop_overlay_window_do_realize (GtkWidget *widget)
+{
+ PopOverlayWindow *overlay_window;
+
+ overlay_window = POP_OVERLAY_WINDOW (widget);
+
+ /* we make the widget's display window a child of X's
+ * composite overlay window instead of using the composite
+ * overlay window directly, so that we have some flexibility
+ * over what visual to use.
+ */
+ pop_overlay_window_setup_parent_window (overlay_window);
+ pop_overlay_window_setup_display_window (overlay_window);
+
+ /* at this point widget->window is filled in with a valid window,
+ * so we're realized, let's make it official
+ */
+ g_assert (widget->window != NULL);
+ widget->style = gtk_style_attach (widget->style, widget->window);
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+ gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
+ pop_overlay_window_setup_initial_geometry (overlay_window);
+}
+
+static void
+pop_overlay_window_compute_child_allocation (PopOverlayWindow *overlay_window,
+ GtkAllocation *child_allocation)
+{
+ GtkWidget *widget;
+ guint border_width;
+
+ g_assert (POP_IS_OVERLAY_WINDOW (overlay_window));
+ g_assert (child_allocation != NULL);
+
+ widget = GTK_WIDGET (overlay_window);
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (overlay_window));
+
+ child_allocation->x = border_width;
+ child_allocation->y = border_width;
+ child_allocation->width = widget->allocation.width - 2 * border_width;
+ child_allocation->height = widget->allocation.height - 2 * border_width;
+
+ if (child_allocation->width < 1)
+ child_allocation->width = 1;
+
+ if (child_allocation->height < 1)
+ child_allocation->height = 1;
+}
+
+static GdkRegion *
+pop_overlay_window_create_region_from_allocation (PopOverlayWindow *overlay_window,
+ GtkAllocation *allocation)
+{
+ GdkRectangle rectangle;
+ GdkRegion *region;
+
+ g_assert (POP_IS_OVERLAY_WINDOW (overlay_window));
+ g_assert (allocation != NULL);
+
+ region = gdk_region_new ();
+
+ rectangle.x = allocation->x;
+ rectangle.y = allocation->y;
+ rectangle.width = allocation->width;
+ rectangle.height = allocation->height;
+
+ gdk_region_union_with_rect (region, &rectangle);
+
+ return region;
+}
+
+static void
+pop_overlay_window_set_input_region_from_allocation (PopOverlayWindow *overlay_window,
+ GtkAllocation *allocation)
+{
+ GtkWidget *widget;
+ GdkRegion *input_region;
+
+ g_assert (POP_IS_OVERLAY_WINDOW (overlay_window));
+ g_assert (allocation != NULL);
+ g_assert (gdk_display_supports_input_shapes (gtk_widget_get_display (GTK_WIDGET (overlay_window))));
+
+ /* either the child allocation is empty or present, it shouldn't
+ * ever be negative or only filled on one dimension
+ */
+ g_assert (((allocation->width > 0) && (allocation->height > 0))
+ || ((allocation->width == 0) && (allocation->height == 0)));
+
+ widget = GTK_WIDGET (overlay_window);
+ input_region =
+ pop_overlay_window_create_region_from_allocation (overlay_window,
+ allocation);
+
+ g_assert (input_region != NULL);
+
+ gdk_window_input_shape_combine_region (overlay_window->priv->parent_window,
+ input_region, 0, 0);
+ gdk_region_destroy (input_region);
+}
+
+static void
+pop_overlay_window_do_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ PopOverlayWindow *overlay_window;
+ GtkWidget *child_widget;
+ GtkAllocation child_allocation = { 0 };
+
+ g_assert (POP_IS_OVERLAY_WINDOW (widget));
+ g_assert (allocation != NULL);
+
+ overlay_window = POP_OVERLAY_WINDOW (widget);
+
+ widget->allocation = *allocation;
+
+ child_widget = gtk_bin_get_child (GTK_BIN (widget));
+
+ if ((child_widget != NULL) && GTK_WIDGET_VISIBLE (child_widget))
+ {
+ /* first figure out how much the space our children should get.
+ * Normally the answer is "all of it", but I guess to be a good
+ * GtkContainer subclass we should respect border width and exclude
+ * it from the outside of the child allocation.
+ */
+ pop_overlay_window_compute_child_allocation (overlay_window,
+ &child_allocation);
+
+ /* now that we have the child allocation, tell our child about it, so
+ * it can divvy it up to its children
+ */
+ gtk_widget_size_allocate (child_widget, &child_allocation);
+ }
+
+ /* we want our children to be able to receive input events, but all space
+ * not allocated to a child should just pass through to windows under the
+ * overlay, so we set the input region to the area occupied by our children
+ */
+ if (GTK_WIDGET_REALIZED (overlay_window))
+ pop_overlay_window_set_input_region_from_allocation (overlay_window,
+ &child_allocation);
+}
+
+GtkWidget *
+pop_overlay_window_new (void)
+{
+ PopOverlayWindow *overlay_window;
+
+ overlay_window = g_object_new (POP_TYPE_OVERLAY_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "app-paintable", TRUE,
+ NULL);
+
+ return GTK_WIDGET (overlay_window);
+}
diff --git a/src/pop-overlay-window.h b/src/pop-overlay-window.h
new file mode 100644
index 0000000..9f3f832
--- /dev/null
+++ b/src/pop-overlay-window.h
@@ -0,0 +1,69 @@
+/* pop-overlay-window.h - special toplevel overlay window
+ *
+ * Copyright (C) 2007 Ray Strode <rstrode@redhat.com>
+ *
+ * This program 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, 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 Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Originally written by: Ray Strode <rstrode@redhat.com>
+ */
+#ifndef POP_OVERLAY_WINDOW_H
+#define POP_OVERLAY_WINDOW_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define POP_TYPE_OVERLAY_WINDOW (pop_overlay_window_get_type ())
+#define POP_OVERLAY_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), POP_TYPE_OVERLAY_WINDOW, PopOverlayWindow))
+#define POP_OVERLAY_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), POP_TYPE_OVERLAY_WINDOW, PopOverlayWindowClass))
+#define POP_IS_OVERLAY_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POP_TYPE_OVERLAY_WINDOW))
+#define POP_IS_OVERLAY_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), POP_TYPE_OVERLAY_WINDOW))
+#define POP_OVERLAY_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), POP_TYPE_OVERLAY_WINDOW, PopOverlayWindowClass))
+
+typedef struct _PopOverlayWindow PopOverlayWindow;
+typedef struct _PopOverlayWindowClass PopOverlayWindowClass;
+typedef struct _PopOverlayWindowPrivate PopOverlayWindowPrivate;
+
+struct _PopOverlayWindow
+{
+ GtkWindow parent;
+
+ /*< private >*/
+ /* reserved for priv pointer */
+ PopOverlayWindowPrivate *priv;
+};
+
+struct _PopOverlayWindowClass
+{
+ GtkWindowClass parent_class;
+
+ /* for signals later if needed */
+ gpointer reserved_1;
+ gpointer reserved_2;
+ gpointer reserved_3;
+ gpointer reserved_4;
+};
+
+#ifndef POP_HIDE_FUNCTION_DECLARATIONS
+GType pop_overlay_window_get_type (void);
+GtkWidget *pop_overlay_window_new (void);
+#endif
+
+G_END_DECLS
+#endif /* POP_OVERLAY_WINDOW_H */
diff --git a/src/pop-window-stack.c b/src/pop-window-stack.c
new file mode 100644
index 0000000..f547342
--- /dev/null
+++ b/src/pop-window-stack.c
@@ -0,0 +1,396 @@
+/* pop-window-stack.c - tracks the stacking order of toplevel windows
+ *
+ * Copyright (C) 2007 Ray Strode <rstrode@redhat.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 2, 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include "pop-window-stack.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
+
+struct _PopWindowStackPrivate
+{
+ GdkScreen *screen;
+ GQueue *windows;
+};
+
+typedef struct
+{
+ PopWindowStack *stack;
+ PopWindowStackForeachFunc func;
+ gpointer user_data;
+} PopWindowStackForeachClosure;
+
+static void pop_window_stack_finalize (GObject * object);
+#if 0
+static void pop_window_stack_class_install_signals (PopWindowStackClass *
+ stack_class);
+#endif
+static void pop_window_stack_class_install_properties (PopWindowStackClass *
+ stack_class);
+
+static void pop_window_stack_set_property (GObject * object,
+ guint prop_id,
+ const GValue * value,
+ GParamSpec * pspec);
+static void pop_window_stack_get_property (GObject * object,
+ guint prop_id,
+ GValue * value,
+ GParamSpec * pspec);
+
+static void pop_window_stack_set_screen (PopWindowStack *stack,
+ GdkScreen *screen);
+static void pop_window_stack_clear_window_list (PopWindowStack *stack);
+static void pop_window_stack_get_initial_window_list (PopWindowStack *stack);
+
+
+enum
+{
+ PROP_0 = 0,
+ PROP_SCREEN
+};
+
+#if 0
+enum
+{
+ FOO = 0,
+ NUMBER_OF_SIGNALS
+};
+
+static guint pop_window_stack_signals[NUMBER_OF_SIGNALS];
+#endif
+
+G_DEFINE_TYPE (PopWindowStack, pop_window_stack, G_TYPE_OBJECT);
+
+static void
+pop_window_stack_class_init (PopWindowStackClass * stack_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (stack_class);
+
+ object_class->finalize = pop_window_stack_finalize;
+
+ pop_window_stack_class_install_properties (stack_class);
+#if 0
+ pop_window_stack_class_install_signals (stack_class);
+#endif
+
+ g_type_class_add_private (stack_class, sizeof (PopWindowStackPrivate));
+}
+
+#if 0
+static void
+pop_window_stack_class_install_signals (PopWindowStackClass * stack_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (stack_class);
+
+ pop_window_stack_signals[FOO] =
+ g_signal_new ("foo", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PopWindowStackClass, foo),
+ NULL, NULL, g_cclosure_marshal_VOID__NONE; G_TYPE_NONE, 0);
+ stack_class->foo = NULL;
+}
+#endif
+
+static void
+pop_window_stack_class_install_properties (PopWindowStackClass * stack_class)
+{
+ GObjectClass *object_class;
+ GParamSpec *param_spec;
+
+ object_class = G_OBJECT_CLASS (stack_class);
+ object_class->set_property = pop_window_stack_set_property;
+ object_class->get_property = pop_window_stack_get_property;
+
+ param_spec = g_param_spec_pointer ("screen", _("Screen"),
+ _("Which screen to user for tracking window stack"),
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_SCREEN, param_spec);
+}
+
+static void
+pop_window_stack_init (PopWindowStack * stack)
+{
+ stack->priv = G_TYPE_INSTANCE_GET_PRIVATE (stack,
+ POP_TYPE_WINDOW_STACK,
+ PopWindowStackPrivate);
+
+ stack->priv->windows = g_queue_new ();
+}
+
+static void
+pop_window_stack_finalize (GObject * object)
+{
+ PopWindowStack *stack;
+ GObjectClass *parent_class;
+
+ stack = POP_WINDOW_STACK (object);
+
+ parent_class = G_OBJECT_CLASS (pop_window_stack_parent_class);
+
+ pop_window_stack_clear_window_list (stack);
+ g_queue_free (stack->priv->windows);
+
+ if (parent_class->finalize != NULL)
+ parent_class->finalize (object);
+}
+
+static void
+pop_window_stack_set_property (GObject * object,
+ guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ PopWindowStack *stack = POP_WINDOW_STACK (object);
+
+ switch (prop_id)
+ {
+ case PROP_SCREEN:
+ pop_window_stack_set_screen (stack, g_value_get_pointer (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+pop_window_stack_get_property (GObject * object,
+ guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ PopWindowStack *stack = POP_WINDOW_STACK (object);
+
+ switch (prop_id)
+ {
+ case PROP_SCREEN:
+ g_value_set_pointer (value, stack->priv->screen);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+GQuark
+pop_window_stack_error_quark (void)
+{
+ static GQuark error_quark = 0;
+
+ if (error_quark == 0)
+ error_quark = g_quark_from_static_string ("pop-window-stack");
+
+ return error_quark;
+}
+
+static PopWindowStack *
+pop_window_stack_new (GdkScreen *screen)
+{
+ PopWindowStack *stack;
+
+ if (screen == NULL)
+ screen = gdk_screen_get_default ();
+
+ stack = g_object_new (POP_TYPE_WINDOW_STACK,
+ "screen", screen, NULL);
+
+ return stack;
+}
+
+PopWindowStack *
+pop_window_stack_get_for_screen (GdkScreen *screen)
+{
+ static GList *stack_list = NULL, *tmp;
+ PopWindowStack *stack;
+
+ if (screen == NULL)
+ screen = gdk_screen_get_default ();
+
+ for (tmp = stack_list; tmp != NULL; tmp = tmp->next)
+ {
+ g_assert (POP_IS_WINDOW_STACK (tmp->data));
+
+ stack = POP_WINDOW_STACK (tmp->data);
+
+ if (gdk_screen_get_number (stack->priv->screen) ==
+ gdk_screen_get_number (screen))
+ return g_object_ref (screen);
+ }
+
+ stack = pop_window_stack_new (screen);
+ stack_list = g_list_prepend (stack_list, stack);
+
+ return g_object_ref (stack);
+}
+
+guint
+pop_window_stack_get_length (PopWindowStack *stack)
+{
+ g_return_val_if_fail (POP_IS_WINDOW_STACK (stack), 0);
+
+ return g_queue_get_length (stack->priv->windows);
+}
+
+static void
+pop_window_stack_queue_foreach_func (gpointer data,
+ gpointer user_data)
+{
+ GdkWindow *window;
+ PopWindowStackForeachClosure *closure;
+
+ window = (GdkWindow *) data;
+ closure = (PopWindowStackForeachClosure *) user_data;
+
+ closure->func (closure->stack, window, closure->user_data);
+}
+
+void
+pop_window_stack_foreach (PopWindowStack *stack,
+ PopWindowStackForeachFunc func,
+ gpointer user_data)
+{
+ PopWindowStackForeachClosure closure;
+
+ g_return_if_fail (POP_IS_WINDOW_STACK (stack));
+ g_return_if_fail (func != NULL);
+
+ /* need to listen for display events and keep the list up to date
+ * that way instead of constantly rebuilding it
+ */
+ pop_window_stack_get_initial_window_list (stack);
+
+ closure.stack = stack;
+ closure.func = func;
+ closure.user_data = user_data;
+
+ g_queue_foreach (stack->priv->windows, pop_window_stack_queue_foreach_func, &closure);
+}
+
+static void
+pop_window_stack_clear_window_list (PopWindowStack *stack)
+{
+ if (g_queue_is_empty (stack->priv->windows))
+ return;
+
+ g_queue_foreach (stack->priv->windows, (GFunc) g_object_unref, NULL);
+
+ g_queue_free (stack->priv->windows);
+ stack->priv->windows = g_queue_new ();
+}
+
+static void
+pop_window_stack_get_initial_window_list (PopWindowStack *stack)
+{
+ GdkScreen *screen;
+ GdkDisplay *display;
+ Window root, parent, *children;
+ guint number_of_children, i;
+ gboolean query_was_successful;
+
+ pop_window_stack_clear_window_list (stack);
+
+ screen = stack->priv->screen;
+
+ if (screen == NULL)
+ screen = gdk_screen_get_default ();
+
+ display = gdk_screen_get_display (screen);
+
+ gdk_x11_display_grab (display);
+ query_was_successful =
+ XQueryTree (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)),
+ &root, &parent, &children, &number_of_children);
+
+ if (query_was_successful)
+ {
+ for (i = 0; i < number_of_children; i++)
+ {
+ GdkWindow *window;
+
+ window = gdk_window_foreign_new_for_display (display, children[i]);
+
+ if (window == NULL)
+ continue;
+
+ /* FIXME: check that it's not INPUT_ONLY */
+ g_queue_push_tail (stack->priv->windows, window);
+ }
+ XFree (children);
+ }
+ gdk_x11_display_ungrab (display);
+}
+
+static void
+pop_window_stack_set_screen (PopWindowStack *stack,
+ GdkScreen *screen)
+{
+ if (screen != stack->priv->screen)
+ {
+ stack->priv->screen = screen;
+ pop_window_stack_get_initial_window_list (stack);
+ g_object_notify (G_OBJECT (stack), "screen");
+ }
+}
+
+#if 0
+int
+pop_window_stack_get_bar (PopWindowStack * stack)
+{
+ return stack->priv->bar;
+}
+#endif
+
+#ifdef POP_WINDOW_STACK_ENABLE_TEST
+
+#include <stdio.h>
+#include <glib.h>
+
+int
+main (int argc, char **argv)
+{
+ PopWindowStack *stack;
+ int exit_code;
+
+ g_log_set_always_fatal (G_LOG_LEVEL_ERROR
+ | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
+
+ g_type_init ();
+
+ g_message ("creating instance of 'window stack' object...");
+ stack = pop_window_stack_new ();
+ g_message ("'window stack' object created successfully");
+
+ g_message ("destroying previously created 'window stack' object...");
+ g_object_unref (stack);
+ g_message ("'window stack' object destroyed successfully");
+
+ exit_code = 0;
+
+ return exit_code;
+}
+#endif /* POP_WINDOW_STACK_ENABLE_TEST */
diff --git a/src/pop-window-stack.h b/src/pop-window-stack.h
new file mode 100644
index 0000000..b54a450
--- /dev/null
+++ b/src/pop-window-stack.h
@@ -0,0 +1,77 @@
+/* pop-window-stack.h - tracks the stacking order of toplevel windows
+ *
+ * Copyright (C) 2007 Ray Strode <rstrode@redhat.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 2, 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#ifndef POP_WINDOW_STACK_H
+#define POP_WINDOW_STACK_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+#define POP_TYPE_WINDOW_STACK (pop_window_stack_get_type ())
+#define POP_WINDOW_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), POP_TYPE_WINDOW_STACK, PopWindowStack))
+#define POP_WINDOW_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), POP_TYPE_WINDOW_STACK, PopWindowStackClass))
+#define POP_IS_WINDOW_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POP_TYPE_WINDOW_STACK))
+#define POP_IS_WINDOW_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), POP_TYPE_WINDOW_STACK))
+#define POP_WINDOW_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), POP_TYPE_WINDOW_STACK, PopWindowStackClass))
+#define POP_WINDOW_STACK_ERROR (pop_window_stack_error_quark ())
+typedef struct _PopWindowStack PopWindowStack;
+typedef struct _PopWindowStackClass PopWindowStackClass;
+typedef struct _PopWindowStackPrivate PopWindowStackPrivate;
+typedef enum _PopWindowStackError PopWindowStackError;
+typedef void (* PopWindowStackForeachFunc) (PopWindowStack *stack, GdkWindow *window, gpointer user_data);
+
+struct _PopWindowStack
+{
+ GObject parent;
+
+ /*< private > */
+ PopWindowStackPrivate *priv;
+};
+
+struct _PopWindowStackClass
+{
+ GObjectClass parent_class;
+
+ /* signals */
+#if 0
+ void (*foo) (PopWindowStack * stack);
+#endif
+};
+
+enum _PopWindowStackError
+{
+ POP_WINDOW_STACK_ERROR_GENERIC = 0,
+};
+
+#ifndef POP_HIDE_FUNCTION_DECLARATIONS
+GType pop_window_stack_get_type (void);
+GQuark pop_window_stack_error_quark (void);
+
+PopWindowStack *pop_window_stack_get_for_screen (GdkScreen *screen);
+guint pop_window_stack_get_length (PopWindowStack *stack);
+void pop_window_stack_foreach (PopWindowStack *stack,
+ PopWindowStackForeachFunc func,
+ gpointer user_data);
+#endif
+
+G_END_DECLS
+#endif /* POP_WINDOW_STACK_H */
diff --git a/src/pop-window-view.c b/src/pop-window-view.c
new file mode 100644
index 0000000..117d4bb
--- /dev/null
+++ b/src/pop-window-view.c
@@ -0,0 +1,934 @@
+/* pop-window-view.c -
+ *
+ * Copyright (C) 2007 Ray Strode <rstrode@redhat.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 2, 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include "pop-window-view.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+#include <cairo.h>
+
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/shape.h>
+
+#include <cairo-xlib.h>
+#include <gdk/gdkx.h>
+
+#include <gdk/gdk.h>
+
+#include "pop-event-listener.h"
+
+struct _PopWindowViewPrivate
+{
+ GdkWindow *window;
+ PopEventListener *event_listener;
+
+ Damage damage;
+ cairo_pattern_t *pattern;
+ cairo_path_t *clip_path;
+ GdkRectangle damaged_area;
+
+ guint is_damaged : 1;
+
+ gint x, y;
+ gint width, height;
+};
+
+#if 0
+static void pop_window_view_class_install_signals (PopWindowViewClass * window_class);
+#endif
+static void pop_window_view_class_install_properties (PopWindowViewClass *
+ window_class);
+
+static void pop_window_view_set_property (GObject * object,
+ guint prop_id,
+ const GValue * value,
+ GParamSpec * pspec);
+static void pop_window_view_get_property (GObject * object,
+ guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static void pop_window_view_do_finalize (GObject *object);
+
+enum
+{
+ PROP_0 = 0,
+ PROP_WINDOW
+};
+
+#define POP_WINDOW_VIEW_INVALID_WINDOW (Window) 0
+#define POP_WINDOW_VIEW_INVALID_PIXMAP (Pixmap) 0
+#define POP_WINDOW_VIEW_INVALID_DAMAGE (Damage) 0
+
+#if 0
+enum
+{
+ FOO = 0,
+ NUMBER_OF_SIGNALS
+};
+
+static guint pop_window_view_signals[NUMBER_OF_SIGNALS];
+#endif
+
+G_DEFINE_TYPE (PopWindowView, pop_window_view, G_TYPE_OBJECT);
+
+static void
+pop_window_view_class_init (PopWindowViewClass *window_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (window_class);
+
+ object_class->finalize = pop_window_view_do_finalize;
+
+ pop_window_view_class_install_properties (window_class);
+#if 0
+ pop_window_view_class_install_signals (window_class);
+#endif
+
+ g_type_class_add_private (window_class, sizeof (PopWindowViewPrivate));
+}
+
+#if 0
+static void
+pop_window_view_class_install_signals (PopWindowViewClass * window_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (window_class);
+
+ pop_window_view_signals[FOO] =
+ g_signal_new ("foo", G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (PopWindowViewClass, foo),
+ NULL, NULL, g_cclosure_marshal_VOID__NONE; G_TYPE_NONE, 0);
+ window_class->foo = NULL;
+}
+#endif
+
+static void
+pop_window_view_class_install_properties (PopWindowViewClass *window_class)
+{
+ GObjectClass *object_class;
+ GParamSpec *param_spec;
+
+ object_class = G_OBJECT_CLASS (window_class);
+ object_class->set_property = pop_window_view_set_property;
+ object_class->get_property = pop_window_view_get_property;
+
+ param_spec = NULL;
+#if 0
+ param_spec = g_param_spec_ulong ("x-window-id", _("X Window ID"),
+ _("A client-side identifier representing a "
+ "server-side window"), 0, G_MAXULONG,
+ (gulong) POP_WINDOW_VIEW_INVALID_WINDOW,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_WINDOW, param_spec);
+#endif
+}
+
+static void
+pop_window_view_init (PopWindowView *view)
+{
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ view->priv = G_TYPE_INSTANCE_GET_PRIVATE (view,
+ POP_TYPE_WINDOW_VIEW,
+ PopWindowViewPrivate);
+
+ view->priv->event_listener = pop_event_listener_get_default ();
+ pop_event_listener_start (view->priv->event_listener);
+}
+
+static void
+pop_window_view_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PopWindowView *view = POP_WINDOW_VIEW (object);
+
+ switch (prop_id)
+ {
+ case PROP_WINDOW:
+ view = NULL;
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+pop_window_view_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PopWindowView *view = POP_WINDOW_VIEW (object);
+
+ switch (prop_id)
+ {
+ case PROP_WINDOW:
+ view = NULL;
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+pop_window_view_do_finalize (GObject *object)
+{
+ PopWindowView *view;
+ GObjectClass *parent_class;
+
+ view = POP_WINDOW_VIEW (object);
+
+ if (view->priv->window != NULL)
+ pop_window_view_unset_window (view);
+
+ parent_class = G_OBJECT_CLASS (pop_window_view_parent_class);
+
+ if (parent_class->finalize != NULL)
+ parent_class->finalize (object);
+}
+
+static gboolean
+pop_window_get_initial_geometry (PopWindowView *view)
+{
+ g_assert (POP_IS_WINDOW_VIEW (view));
+ g_assert (view->priv->window != NULL);
+
+ gdk_window_get_geometry (view->priv->window,
+ &view->priv->x,
+ &view->priv->y,
+ &view->priv->width,
+ &view->priv->height,
+ NULL);
+ return TRUE;
+}
+
+static cairo_surface_t *
+pop_window_view_get_surface (PopWindowView *view)
+{
+ cairo_surface_t *surface;
+ cairo_status_t status;
+
+ g_assert (POP_IS_WINDOW_VIEW (view));
+ g_assert (view->priv->pattern != NULL);
+
+ surface = NULL;
+ status = cairo_pattern_get_surface (view->priv->pattern, &surface);
+ g_assert (status != CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+
+ return surface;
+}
+
+static void
+pop_window_view_free_x_pixmap (PopWindowView *view)
+{
+ cairo_surface_t *surface;
+ Pixmap pixmap;
+
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ surface = pop_window_view_get_surface (view);
+
+ g_assert (surface != NULL);
+ pixmap = (Pixmap) cairo_xlib_surface_get_drawable (surface);
+ XFreePixmap (GDK_WINDOW_XDISPLAY (view->priv->window), pixmap);
+}
+
+static void
+pop_window_view_destroy_pattern (PopWindowView *view)
+{
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ if (view->priv->pattern == NULL)
+ return;
+
+ pop_window_view_free_x_pixmap (view);
+ cairo_pattern_destroy (view->priv->pattern);
+ view->priv->pattern = NULL;
+}
+
+static void
+pop_window_view_destroy_clip_path (PopWindowView *view)
+{
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ if (view->priv->clip_path == NULL)
+ return;
+
+ cairo_path_destroy (view->priv->clip_path);
+ view->priv->clip_path = NULL;
+}
+
+GQuark
+pop_window_view_error_quark (void)
+{
+ static GQuark error_quark = 0;
+
+ if (error_quark == 0)
+ error_quark = g_quark_from_static_string ("pop-window");
+
+ return error_quark;
+}
+
+PopWindowView *
+pop_window_view_new (void)
+{
+ PopWindowView *view;
+
+ view = g_object_new (POP_TYPE_WINDOW_VIEW, NULL);
+
+ return view;
+}
+
+static gboolean
+pop_window_view_redirect_window_off_screen (PopWindowView *view)
+{
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ gdk_error_trap_push ();
+ XCompositeRedirectWindow (GDK_WINDOW_XDISPLAY (view->priv->window),
+ GDK_WINDOW_XWINDOW (view->priv->window),
+ CompositeRedirectAutomatic);
+ gdk_flush ();
+ if (gdk_error_trap_pop ())
+ return FALSE;
+
+ return TRUE;
+}
+
+static Pixmap
+pop_window_view_fetch_window_pixmap (PopWindowView *view)
+{
+ Pixmap pixmap;
+ gint status;
+
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ gdk_error_trap_push ();
+ pixmap =
+ XCompositeNameWindowPixmap (GDK_WINDOW_XDISPLAY (view->priv->window),
+ GDK_WINDOW_XWINDOW (view->priv->window));
+ gdk_flush ();
+ status = gdk_error_trap_pop ();
+
+ if (status != Success)
+ {
+ char error_message[64];
+
+ XGetErrorText (GDK_WINDOW_XDISPLAY (view->priv->window),
+ status, error_message, sizeof (error_message) - 1);
+ g_print ("could not name pixmap for window 0x%lx: %s\n",
+ GDK_WINDOW_XWINDOW (view->priv->window),
+ error_message);
+
+ return POP_WINDOW_VIEW_INVALID_PIXMAP;
+ }
+
+ return pixmap;
+}
+
+static gboolean
+pop_window_view_create_pattern_from_window (PopWindowView *view)
+{
+ GdkVisual *visual;
+ Pixmap pixmap;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+ int width, height;
+
+ g_assert (POP_IS_WINDOW_VIEW (view));
+ g_assert (view->priv->window != NULL);
+ g_assert (view->priv->pattern == NULL);
+
+ pixmap = pop_window_view_fetch_window_pixmap (view);
+
+ if (pixmap == POP_WINDOW_VIEW_INVALID_PIXMAP)
+ {
+ g_print ("couldn't fetch pixmap for window\n");
+ return FALSE;
+ }
+
+ gdk_drawable_get_size (GDK_DRAWABLE (view->priv->window),
+ &width, &height);
+
+ visual = gdk_drawable_get_visual (GDK_DRAWABLE (view->priv->window));
+ surface = cairo_xlib_surface_create (GDK_WINDOW_XDISPLAY (view->priv->window),
+ (Drawable) pixmap,
+ GDK_VISUAL_XVISUAL (visual),
+ width, height);
+ status = cairo_surface_status (surface);
+ if (status != CAIRO_STATUS_SUCCESS)
+ {
+ g_print ("could create xlib surface for window: %s\n", cairo_status_to_string (status));
+ goto failed;
+ }
+
+ view->priv->pattern = cairo_pattern_create_for_surface (surface);
+ cairo_surface_destroy (surface);
+
+ status = cairo_pattern_status (view->priv->pattern);
+ if (status != CAIRO_STATUS_SUCCESS)
+ {
+ g_print ("couldn't create cairo pattern: %s\n", cairo_status_to_string (status));
+ goto failed;
+ }
+
+ return TRUE;
+
+failed:
+ if (view->priv->pattern != NULL)
+ {
+ cairo_pattern_destroy (view->priv->pattern);
+ view->priv->pattern = NULL;
+ }
+
+ XFreePixmap (GDK_WINDOW_XDISPLAY (view->priv->window),
+ pixmap);
+
+ return FALSE;
+}
+
+static XRectangle *
+pop_window_view_get_tesselated_window_shape (PopWindowView *view,
+ gint *number_of_rectangles)
+{
+ XRectangle *rectangles;
+ gint ordering;
+ gint status;
+
+ g_assert (POP_IS_WINDOW_VIEW (view));
+ g_assert (number_of_rectangles != NULL);
+
+ *number_of_rectangles = 0;
+
+ gdk_error_trap_push ();
+ rectangles = XShapeGetRectangles (GDK_WINDOW_XDISPLAY (view->priv->window),
+ GDK_WINDOW_XWINDOW (view->priv->window),
+ ShapeBounding, number_of_rectangles,
+ &ordering);
+ gdk_flush ();
+ status = gdk_error_trap_pop ();
+
+ if (status != Success)
+ return NULL;
+
+ return rectangles;
+}
+
+
+static gboolean
+pop_window_view_set_clip_path_from_window (PopWindowView *view)
+{
+ XRectangle *rectangles;
+ gint number_of_rectangles;
+ cairo_t *cairo_context;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+ gint i;
+
+ g_assert (POP_IS_WINDOW_VIEW (view));
+ g_assert (view->priv->clip_path == NULL);
+
+ rectangles = pop_window_view_get_tesselated_window_shape (view,
+ &number_of_rectangles);
+
+ if (rectangles == NULL)
+ return FALSE;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_A1,
+ view->priv->width, view->priv->height);
+
+ status = cairo_surface_status (surface);
+
+ if (status != CAIRO_STATUS_SUCCESS)
+ {
+ XFree (rectangles);
+ return FALSE;
+ }
+
+ cairo_context = cairo_create (surface);
+
+ for (i = 0; i < number_of_rectangles; i++)
+ {
+ cairo_rectangle (cairo_context,
+ rectangles[i].x,
+ rectangles[i].y,
+ rectangles[i].width,
+ rectangles[i].height);
+ }
+
+ view->priv->clip_path = cairo_copy_path_flat (cairo_context);
+ cairo_destroy (cairo_context);
+
+ return TRUE;
+}
+
+static void
+pop_window_view_enable_window_geometry_reporting (PopWindowView *view)
+{
+ GdkEventMask event_mask;
+
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ event_mask = gdk_window_get_events (view->priv->window);
+
+ gdk_window_set_events (view->priv->window, event_mask | GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK);
+}
+
+static void
+pop_window_view_disable_window_geometry_reporting (PopWindowView *view)
+{
+ GdkEventMask event_mask;
+
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ event_mask = gdk_window_get_events (view->priv->window);
+
+ gdk_window_set_events (view->priv->window, event_mask & ~GDK_STRUCTURE_MASK);
+}
+
+static void
+pop_window_view_enable_window_shape_reporting (PopWindowView *view)
+{
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ XShapeSelectInput (GDK_WINDOW_XDISPLAY (view->priv->window),
+ GDK_WINDOW_XWINDOW (view->priv->window),
+ ShapeNotifyMask);
+}
+
+static void
+pop_window_view_disable_window_shape_reporting (PopWindowView *view)
+{
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ XShapeSelectInput (GDK_WINDOW_XDISPLAY (view->priv->window),
+ GDK_WINDOW_XWINDOW (view->priv->window),
+ 0);
+}
+
+static void
+pop_window_view_enable_window_damage_reporting (PopWindowView *view)
+{
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ view->priv->damage =
+ XDamageCreate (GDK_WINDOW_XDISPLAY (view->priv->window),
+ GDK_WINDOW_XWINDOW (view->priv->window),
+ XDamageReportNonEmpty);
+}
+
+static void
+pop_window_view_disable_window_damage_reporting (PopWindowView *view)
+{
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ gdk_error_trap_push ();
+ XDamageDestroy (GDK_WINDOW_XDISPLAY (view->priv->window),
+ view->priv->damage);
+ gdk_flush ();
+ gdk_error_trap_pop ();
+ view->priv->damage = POP_WINDOW_VIEW_INVALID_DAMAGE;
+}
+
+static void
+pop_window_view_window_redrawn (PopWindowView *view,
+ GdkWindow *window,
+ GdkRectangle *rectangle)
+{
+ g_print ("window 0x%lx redrawn (area: %dx%d+%d+%d)\n", GDK_WINDOW_XWINDOW (view->priv->window), rectangle->x, rectangle->y, rectangle->width, rectangle->height);
+ if (view->priv->pattern == NULL)
+ pop_window_view_create_pattern_from_window (view);
+
+ if (view->priv->clip_path == NULL)
+ pop_window_view_set_clip_path_from_window (view);
+
+ gdk_rectangle_union (&view->priv->damaged_area,
+ rectangle,
+ &view->priv->damaged_area);
+
+ view->priv->is_damaged = TRUE;
+}
+
+static void
+pop_window_view_window_resized (PopWindowView *view)
+{
+ gint old_width, old_height;
+
+ g_print ("window 0x%lx resized\n", GDK_WINDOW_XWINDOW (view->priv->window));
+
+ old_width = view->priv->width;
+ old_height = view->priv->height;
+
+ gdk_drawable_get_size (GDK_DRAWABLE (view->priv->window),
+ &view->priv->width,
+ &view->priv->height);
+ pop_window_view_destroy_pattern (view);
+ pop_window_view_create_pattern_from_window (view);
+
+ pop_window_view_destroy_clip_path (view);
+ pop_window_view_set_clip_path_from_window (view);
+
+ g_print ("window 0x%lx resized from (%dx%d) to (%dx%d)\n",
+ GDK_WINDOW_XWINDOW (view->priv->window),
+ old_width, old_height,
+ view->priv->width, view->priv->height);
+}
+
+static void
+pop_window_view_window_moved (PopWindowView *view)
+{
+ gint old_x, old_y;
+
+ old_x = view->priv->x;
+ old_y = view->priv->y;
+
+ gdk_window_get_position (GDK_DRAWABLE (view->priv->window),
+ &view->priv->x,
+ &view->priv->y);
+
+ g_print ("window 0x%lx moved from (%d, %d) to (%d, %d)\n",
+ GDK_WINDOW_XWINDOW (view->priv->window),
+ old_x, old_y, view->priv->x, view->priv->y);
+}
+
+static void
+pop_window_view_window_shown (PopWindowView *view)
+{
+ g_print ("window 0x%lx is now visible\n",
+ GDK_WINDOW_XWINDOW (view->priv->window));
+ if (view->priv->pattern == NULL)
+ pop_window_view_create_pattern_from_window (view);
+
+ if (view->priv->clip_path == NULL)
+ pop_window_view_set_clip_path_from_window (view);
+}
+
+static void
+pop_window_view_window_hidden (PopWindowView *view)
+{
+ g_print ("window 0x%lx hidden\n", GDK_WINDOW_XWINDOW (view->priv->window));
+ pop_window_view_destroy_pattern (view);
+}
+
+static void
+pop_window_view_window_destroyed (PopWindowView *view)
+{
+ g_print ("window 0x%lx destroyed\n", GDK_WINDOW_XWINDOW (view->priv->window));
+ pop_window_view_unset_window (view);
+}
+
+static void
+pop_window_view_window_reshaped (PopWindowView *view)
+{
+ gint old_width, old_height;
+
+ g_print ("window 0x%lx reshaped\n", GDK_WINDOW_XWINDOW (view->priv->window));
+ old_width = view->priv->width;
+ old_height = view->priv->height;
+
+ gdk_drawable_get_size (GDK_DRAWABLE (view->priv->window),
+ &view->priv->width,
+ &view->priv->height);
+ pop_window_view_destroy_pattern (view);
+ pop_window_view_create_pattern_from_window (view);
+
+ pop_window_view_destroy_clip_path (view);
+ pop_window_view_set_clip_path_from_window (view);
+}
+
+static void
+pop_window_view_window_reparented (PopWindowView *view)
+{
+ g_print ("window 0x%lx reparented\n", GDK_WINDOW_XWINDOW (view->priv->window));
+ pop_window_view_unset_window (view);
+}
+
+static void
+pop_window_view_listen_to_window (PopWindowView *view)
+{
+ gchar detailed_signal[sizeof ("window-XXXXXXXXXX::ffffffff")];
+
+ g_print ("listening to window 0x%lx\n", GDK_WINDOW_XWINDOW (view->priv->window));
+
+ snprintf (detailed_signal, sizeof (detailed_signal),
+ "window-destroyed::%lx", GDK_WINDOW_XWINDOW (view->priv->window));
+ g_signal_connect_swapped (G_OBJECT (view->priv->event_listener),
+ detailed_signal,
+ G_CALLBACK (pop_window_view_window_destroyed),
+ view);
+ snprintf (detailed_signal, sizeof (detailed_signal),
+ "window-shown::%lx", GDK_WINDOW_XWINDOW (view->priv->window));
+ g_signal_connect_swapped (G_OBJECT (view->priv->event_listener),
+ detailed_signal,
+ G_CALLBACK (pop_window_view_window_shown),
+ view);
+ snprintf (detailed_signal, sizeof (detailed_signal),
+ "window-hidden::%lx", GDK_WINDOW_XWINDOW (view->priv->window));
+ g_signal_connect_swapped (G_OBJECT (view->priv->event_listener),
+ detailed_signal,
+ G_CALLBACK (pop_window_view_window_hidden),
+ view);
+ snprintf (detailed_signal, sizeof (detailed_signal),
+ "window-resized::%lx", GDK_WINDOW_XWINDOW (view->priv->window));
+ g_signal_connect_swapped (G_OBJECT (view->priv->event_listener),
+ detailed_signal,
+ G_CALLBACK (pop_window_view_window_resized),
+ view);
+ snprintf (detailed_signal, sizeof (detailed_signal),
+ "window-reshaped::%lx", GDK_WINDOW_XWINDOW (view->priv->window));
+ g_signal_connect_swapped (G_OBJECT (view->priv->event_listener),
+ detailed_signal,
+ G_CALLBACK (pop_window_view_window_reshaped),
+ view);
+ snprintf (detailed_signal, sizeof (detailed_signal),
+ "window-reparented::%lx", GDK_WINDOW_XWINDOW (view->priv->window));
+ g_signal_connect_swapped (G_OBJECT (view->priv->event_listener),
+ detailed_signal,
+ G_CALLBACK (pop_window_view_window_reparented),
+ view);
+ snprintf (detailed_signal, sizeof (detailed_signal),
+ "window-moved::%lx", GDK_WINDOW_XWINDOW (view->priv->window));
+ g_signal_connect_swapped (G_OBJECT (view->priv->event_listener),
+ detailed_signal,
+ G_CALLBACK (pop_window_view_window_moved),
+ view);
+ snprintf (detailed_signal, sizeof (detailed_signal),
+ "window-redrawn::%lx", GDK_WINDOW_XWINDOW (view->priv->window));
+ g_signal_connect_swapped (G_OBJECT (view->priv->event_listener),
+ detailed_signal,
+ G_CALLBACK (pop_window_view_window_redrawn),
+ view);
+}
+
+static void
+pop_window_view_ignore_window (PopWindowView *view)
+{
+ g_print ("ignoring window 0x%lx\n", GDK_WINDOW_XWINDOW (view->priv->window));
+
+ g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener),
+ G_CALLBACK (pop_window_view_window_destroyed),
+ view);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener),
+ G_CALLBACK (pop_window_view_window_shown),
+ view);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener),
+ G_CALLBACK (pop_window_view_window_hidden),
+ view);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener),
+ G_CALLBACK (pop_window_view_window_resized),
+ view);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener),
+ G_CALLBACK (pop_window_view_window_reshaped),
+ view);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener),
+ G_CALLBACK (pop_window_view_window_reparented),
+ view);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener),
+ G_CALLBACK (pop_window_view_window_moved),
+ view);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (view->priv->event_listener),
+ G_CALLBACK (pop_window_view_window_redrawn),
+ view);
+}
+
+static gboolean
+pop_window_view_enable_window_status_reporting (PopWindowView *view)
+{
+ gint status;
+
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ gdk_error_trap_push ();
+ pop_window_view_enable_window_geometry_reporting (view);
+ pop_window_view_enable_window_shape_reporting (view);
+ pop_window_view_enable_window_damage_reporting (view);
+ pop_window_view_listen_to_window (view);
+ gdk_flush ();
+ status = gdk_error_trap_pop ();
+
+ return status == Success;
+}
+
+static void
+pop_window_view_disable_window_status_reporting (PopWindowView *view)
+{
+ g_assert (POP_IS_WINDOW_VIEW (view));
+
+ if ((view->priv->window == NULL) || GDK_WINDOW_DESTROYED (view->priv->window))
+ return;
+
+ pop_window_view_disable_window_damage_reporting (view);
+
+ gdk_error_trap_push ();
+ pop_window_view_disable_window_geometry_reporting (view);
+ pop_window_view_disable_window_shape_reporting (view);
+ pop_window_view_ignore_window (view);
+ gdk_flush ();
+ gdk_error_trap_pop ();
+}
+
+gboolean
+pop_window_view_set_window (PopWindowView *view,
+ GdkWindow *window)
+{
+ g_return_val_if_fail (POP_IS_WINDOW_VIEW (view), FALSE);
+ g_return_val_if_fail (window != NULL, FALSE);
+
+ g_return_val_if_fail (view->priv->window == NULL, FALSE);
+ view->priv->window = window;
+
+ if (!pop_window_get_initial_geometry (view))
+ {
+ g_print ("couldn't get initial geometry\n");
+ view->priv->window = NULL;
+ return FALSE;
+ }
+
+ if (!pop_window_view_enable_window_status_reporting (view))
+ {
+ g_print ("couldn't enable damage for window\n");
+ pop_window_view_disable_window_status_reporting (view);
+ view->priv->window = NULL;
+ return FALSE;
+ }
+
+ if (!pop_window_view_redirect_window_off_screen (view))
+ {
+ g_print ("couldn't redirect window off screen\n");
+ view->priv->window = NULL;
+ return FALSE;
+ }
+
+ if (gdk_window_is_visible (view->priv->window))
+ pop_window_view_window_shown (view);
+
+ g_object_ref (window);
+
+ return TRUE;
+}
+
+void
+pop_window_view_unset_window (PopWindowView *view)
+{
+ g_return_if_fail (POP_IS_WINDOW_VIEW (view));
+ g_return_if_fail (view->priv->window != NULL);
+
+ pop_window_view_disable_window_status_reporting (view);
+ pop_window_view_destroy_clip_path (view);
+ pop_window_view_destroy_pattern (view);
+
+ g_object_unref (view->priv->window);
+ view->priv->window = NULL;
+
+ g_object_unref (view->priv->event_listener);
+ view->priv->event_listener = NULL;
+}
+
+GdkWindow *
+pop_window_view_get_window (PopWindowView *view)
+{
+ g_return_val_if_fail (POP_IS_WINDOW_VIEW (view), NULL);
+
+ return view->priv->window;
+}
+
+void
+pop_window_view_report_fixed_damage (PopWindowView *view)
+{
+ gdk_error_trap_push ();
+ XDamageSubtract (GDK_WINDOW_XDISPLAY (view->priv->window),
+ view->priv->damage, None, None);
+ gdk_flush ();
+ gdk_error_trap_pop ();
+
+ view->priv->is_damaged = FALSE;
+ view->priv->damaged_area.x = 0;
+ view->priv->damaged_area.y = 0;
+ view->priv->damaged_area.width = 0;
+ view->priv->damaged_area.height = 0;
+}
+
+void
+pop_window_view_render_to_context (PopWindowView *view,
+ cairo_t *cairo_context)
+{
+ g_return_if_fail (POP_IS_WINDOW_VIEW (view));
+ g_return_if_fail (cairo_context != NULL);
+
+ if (view->priv->pattern == NULL)
+ return;
+
+ pop_window_view_report_fixed_damage (view);
+
+ cairo_save (cairo_context);
+ cairo_translate (cairo_context, view->priv->x, view->priv->y);
+#if 0
+ char *text;
+ text = g_strdup_printf ("0x%lx", GDK_WINDOW_XWINDOW (view->priv->window));
+ cairo_move_to (cairo_context, view->priv->width, view->priv->height);
+ cairo_show_text (cairo_context, text);
+ g_free (text);
+#endif
+
+ cairo_set_source (cairo_context, view->priv->pattern);
+ cairo_move_to (cairo_context, 0.0, 0.0);
+ cairo_append_path (cairo_context, view->priv->clip_path);
+ cairo_clip (cairo_context);
+ cairo_paint (cairo_context);
+
+ cairo_restore (cairo_context);
+}
+
+void
+pop_window_view_get_size (PopWindowView *view,
+ int *width,
+ int *height)
+{
+
+ if (width)
+ *width = view->priv->width;
+
+ if (height)
+ *height = view->priv->height;
+}
+
+void
+pop_window_view_get_position (PopWindowView *view,
+ int *x,
+ int *y)
+{
+ if (x)
+ *x = view->priv->x;
+
+ if (y)
+ *y = view->priv->y;
+}
diff --git a/src/pop-window-view.h b/src/pop-window-view.h
new file mode 100644
index 0000000..bcc3fa1
--- /dev/null
+++ b/src/pop-window-view.h
@@ -0,0 +1,94 @@
+/* pop-window-view.h -
+ *
+ * Copyright (C) 2007 Ray Strode <rstrode@redhat.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 2, 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#ifndef POP_WINDOW_VIEW_H
+#define POP_WINDOW_VIEW_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <X11/X.h>
+
+#include <cairo.h>
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+#define POP_TYPE_WINDOW_VIEW (pop_window_view_get_type ())
+#define POP_WINDOW_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), POP_TYPE_WINDOW_VIEW, PopWindowView))
+#define POP_WINDOW_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), POP_TYPE_WINDOW_VIEW, PopWindowViewClass))
+#define POP_IS_WINDOW_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POP_TYPE_WINDOW_VIEW))
+#define POP_IS_WINDOW_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), POP_TYPE_WINDOW_VIEW))
+#define POP_WINDOW_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), POP_TYPE_WINDOW_VIEW, PopWindowViewClass))
+#define POP_WINDOW_VIEW_ERROR (pop_window_view_error_quark ())
+typedef struct _PopWindowView PopWindowView;
+typedef struct _PopWindowViewClass PopWindowViewClass;
+typedef struct _PopWindowViewPrivate PopWindowViewPrivate;
+typedef enum _PopWindowViewError PopWindowViewError;
+
+struct _PopWindowView
+{
+ GObject parent;
+
+ /*< private > */
+ PopWindowViewPrivate *priv;
+};
+
+struct _PopWindowViewClass
+{
+ GObjectClass parent_class;
+
+ /* signals */
+#if 0
+ void (*foo) (PopWindowView * window);
+#endif
+};
+
+enum _PopWindowViewError
+{
+ POP_WINDOW_VIEW_ERROR_GENERIC = 0,
+};
+
+#ifndef POP_HIDE_FUNCTION_DECLARATIONS
+GType pop_window_view_get_type (void);
+GQuark pop_window_view_error_quark (void);
+
+PopWindowView *pop_window_view_new (void) G_GNUC_MALLOC;
+gboolean pop_window_view_set_window (PopWindowView *view, GdkWindow *window);
+void pop_window_view_unset_window (PopWindowView *view);
+GdkWindow *pop_window_view_get_window (PopWindowView *view);
+void pop_window_view_get_size (PopWindowView *view, gint *width, gint *height);
+void pop_window_view_get_position (PopWindowView *view, int *x, int *y);
+void pop_window_view_queue_damage (PopWindowView *view);
+void pop_window_view_render_to_context (PopWindowView *view,
+ cairo_t *cairo_context);
+
+void _pop_window_view_window_damaged (PopWindowView *view,
+ GdkRectangle *rectangle);
+void _pop_window_view_window_resized (PopWindowView *view);
+void _pop_window_view_window_moved (PopWindowView *view);
+void _pop_window_view_window_shown (PopWindowView *view);
+void _pop_window_view_window_hidden (PopWindowView *view);
+void _pop_window_view_window_destroyed (PopWindowView *view);
+void _pop_window_view_window_reshaped (PopWindowView *view);
+void _pop_window_view_window_reparented (PopWindowView *view);
+#endif
+
+G_END_DECLS
+#endif /* POP_WINDOW_VIEW_H */