#include "config.h" #include #include #include #include #include #include #include #include #include #include #include #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 Status query_tree_request (GdkDisplay *display, gpointer data) { GdkWindow *window; Window parent, root, *children; guint number_of_children, i; gboolean tree_queried; window = GDK_WINDOW (data); g_assert (GDK_IS_WINDOW (window)); return XQueryTree (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XWINDOW (window), &root, &parent, &children, &number_of_children); } 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 view_subtract_overlapping_region (PopWindowStack *stack, GdkWindow *window, GdkWindow *above_window, GdkRegion **visible_region) { PopWindowView *view, *above_view; GdkRegion *difference; g_assert (POP_IS_WINDOW_STACK (stack)); g_assert (GDK_IS_WINDOW (window)); g_assert (GDK_IS_WINDOW (above_window)); g_assert (visible_region != NULL); above_view = g_object_get_data (G_OBJECT (above_window), "pop-window-view"); if (!POP_IS_WINDOW_VIEW (above_view)) { return; } view = g_object_get_data (G_OBJECT (window), "pop-window-view"); g_assert (POP_IS_WINDOW_VIEW (view)); difference = pop_window_view_get_difference (view, above_view); g_assert (difference != NULL); if (*visible_region == NULL) { *visible_region = difference; } else { gdk_region_intersect (*visible_region, difference); gdk_region_destroy (difference); } } static GdkRegion * get_view_visible_region (PopWindowStack *stack, PopWindowView *view) { GdkWindow *window; GdkRegion *visible_region; g_assert (POP_IS_WINDOW_VIEW (view)); window = pop_window_view_get_window (view); g_assert (GDK_IS_WINDOW (window)); visible_region = NULL; pop_window_stack_above_window_foreach (stack, window, (PopWindowStackAboveWindowForeachFunc) view_subtract_overlapping_region, &visible_region); if (visible_region == NULL) { visible_region = gdk_region_new (); } return visible_region; } static void draw_window_from_stack (PopWindowStack *stack, GdkWindow *window, cairo_t *cairo_context) { PopWindowView *view; GdkRegion *visible_region; view = g_object_get_data (G_OBJECT (window), "pop-window-view"); if (view == NULL) { return; } g_assert (POP_IS_WINDOW_VIEW (view)); #if 0 visible_region = get_view_visible_region (stack, view); if (!gdk_region_empty (visible_region)) { #endif cairo_save (cairo_context); pop_window_view_render_to_context (view, cairo_context); cairo_restore (cairo_context); #if 0 } gdk_region_destroy (visible_region); #endif } static void draw_windows (cairo_t *cairo_context) { if (!pop_window_stack_is_ready (stack)) { return; } 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 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 add_windows_from_stack_to_list (PopWindowStack *stack) { if (pop_window_stack_is_ready (stack)) { pop_window_stack_foreach (stack, (PopWindowStackForeachFunc) add_window_from_stack_to_list, NULL); } } static void on_stack_ready_add_windows (GObject *object, GParamSpec *param_spec, gpointer user_data) { PopWindowStack *stack; stack = POP_WINDOW_STACK (object); add_windows_from_stack_to_list (stack); g_signal_handlers_disconnect_by_func (object, on_stack_ready_add_windows, user_data); } static void on_map (GtkWidget *widget) { stack = pop_window_stack_get_for_screen (NULL); if (pop_window_stack_is_ready (stack)) { add_windows_from_stack_to_list (stack); } else { g_signal_connect (G_OBJECT (stack), "notify::is-ready", on_stack_ready_add_windows, NULL); } } static void remove_window_from_stack_from_list (PopWindowStack *stack, GdkWindow *window) { remove_window_from_list (window); } static void remove_windows_from_stack_from_list (PopWindowStack *stack) { pop_window_stack_foreach (stack, (PopWindowStackForeachFunc) remove_window_from_stack_from_list, NULL); g_object_unref (stack); stack = NULL; gtk_main_quit (); } static void on_stack_ready_remove_windows (GObject *object, GParamSpec *param_spec, gpointer user_data) { PopWindowStack *stack; stack = POP_WINDOW_STACK (object); remove_windows_from_stack_from_list (stack); g_signal_handlers_disconnect_by_func (object, on_stack_ready_remove_windows, user_data); } static gboolean on_close (GtkWidget *widget) { if (pop_window_stack_is_ready (stack)) { remove_windows_from_stack_from_list (stack); } else { g_signal_connect (G_OBJECT (stack), "notify::is-ready", on_stack_ready_remove_windows, NULL); } return FALSE; } 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); gtk_widget_realize (GTK_WIDGET (overlay_window)); #endif 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); g_signal_connect (G_OBJECT (overlay_window), "delete-event", G_CALLBACK (on_close), 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); listener = pop_event_listener_get_default (); gtk_widget_show (overlay_window); g_print ("overlay window is 0x%lx\n", GDK_WINDOW_XWINDOW (overlay_window->window)); gtk_main (); return 0; }