/* pop-window-stack.c - tracks the stacking order of toplevel windows * * Copyright (C) 2007 Ray Strode * * 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 "config.h" #include "pop-window-stack.h" #include #include #include #include #include #include #include #include "pop-event-listener.h" #include "pop-x-reply-watch.h" struct _PopWindowStackPrivate { GdkScreen *screen; PopEventListener *event_listener; GQueue *windows; guint32 is_ready : 1; }; typedef struct { PopWindowStack *stack; PopWindowStackForeachFunc func; gpointer data; } PopWindowStackForeachClosure; typedef struct { PopWindowStack *stack; GdkWindow *window; PopWindowStackAboveWindowForeachFunc func; gpointer data; } PopWindowStackAboveWindowForeachClosure; 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_set_ready (PopWindowStack *stack, gboolean is_ready); static void pop_window_stack_clear_window_list (PopWindowStack *stack); static void pop_window_stack_ask_for_window_list (PopWindowStack *stack); enum { PROP_0 = 0, PROP_SCREEN, PROP_IS_READY }; #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); param_spec = g_param_spec_boolean ("is-ready", _ ("Is it ready?"), _ ( "whether or not the window stack is fully " "initialized and can be used"), FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_IS_READY, 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->event_listener = pop_event_listener_get_default (); g_signal_connect_swapped (G_OBJECT (stack->priv->event_listener), "window-layered-under", G_CALLBACK (pop_window_stack_ask_for_window_list), stack); g_signal_connect_swapped (G_OBJECT (stack->priv->event_listener), "window-layered-on-top", G_CALLBACK (pop_window_stack_ask_for_window_list), stack); g_signal_connect_swapped (G_OBJECT (stack->priv->event_listener), "window-layered-on-bottom", G_CALLBACK (pop_window_stack_ask_for_window_list), stack); stack->priv->windows = g_queue_new (); stack->priv->is_ready = FALSE; } 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); g_object_unref (stack->priv->event_listener); 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; case PROP_IS_READY: pop_window_stack_set_ready (stack, g_value_get_boolean (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; case PROP_IS_READY: g_value_set_boolean (value, stack->priv->is_ready); 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); } gboolean pop_window_stack_is_ready (PopWindowStack *stack) { g_return_val_if_fail (POP_IS_WINDOW_STACK (stack), FALSE); return stack->priv->is_ready; } 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_foreach_func (gpointer data, gpointer user_data) { GdkWindow *window; PopWindowStackForeachClosure *closure; window = (GdkWindow *) data; closure = (PopWindowStackForeachClosure *) user_data; closure->func (closure->stack, window, closure->data); } void pop_window_stack_foreach (PopWindowStack *stack, PopWindowStackForeachFunc func, gpointer data) { PopWindowStackForeachClosure closure; g_return_if_fail (POP_IS_WINDOW_STACK (stack)); g_return_if_fail (func != NULL); g_return_if_fail (pop_window_stack_is_ready (stack)); closure.stack = stack; closure.func = func; closure.data = data; g_queue_foreach (stack->priv->windows, pop_window_stack_foreach_func, &closure); } static void pop_window_stack_above_window_foreach_func (gpointer data, gpointer user_data) { GdkWindow *above_window; PopWindowStackAboveWindowForeachClosure *closure; above_window = (GdkWindow *) data; closure = (PopWindowStackAboveWindowForeachClosure *) user_data; closure->func (closure->stack, closure->window, above_window, closure->data); } void pop_window_stack_above_window_foreach ( PopWindowStack *stack, GdkWindow*window, PopWindowStackAboveWindowForeachFunc func, gpointer data) { PopWindowStackAboveWindowForeachClosure closure; GList *window_link; g_return_if_fail (POP_IS_WINDOW_STACK (stack)); g_return_if_fail (func != NULL); g_return_if_fail (window != NULL); g_return_if_fail (pop_window_stack_is_ready (stack)); window_link = g_queue_find (stack->priv->windows, window); if (window_link == NULL) { g_warning ("window with id 0x%lx is not in window stack\n", GDK_WINDOW_XWINDOW (window)); return; } closure.stack = stack; closure.window = g_object_ref (window); closure.func = func; closure.data = data; g_list_foreach (window_link->next, pop_window_stack_above_window_foreach_func, &closure); g_object_unref (window); } static void pop_window_stack_clear_window_list (PopWindowStack *stack) { pop_window_stack_set_ready (stack, FALSE); 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 gboolean window_can_output (GdkWindow *window) { XWindowAttributes attrs; gboolean received_attrs; /* FIXME: this sucks because it requires a server round trip. */ gdk_error_trap_push (); received_attrs = XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XWINDOW (window), &attrs); if ((gdk_error_trap_pop ()) || (!received_attrs)) { return FALSE; } return attrs.class == InputOutput; } typedef struct { GdkWindow *window; Window *children; guint number_of_children; gpointer data; } PopQueryTreeState; static Status query_tree_request (GdkDisplay *display, gpointer data) { Window parent, root; PopQueryTreeState *state; g_assert (state != NULL); state = (PopQueryTreeState *) data; g_assert (GDK_IS_WINDOW (state->window)); return XQueryTree (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XWINDOW (state->window), &root, &parent, &state->children, &state->number_of_children); } static void queried_window_foreach_func (Window x_window_id, gpointer data) { PopWindowStack *stack; GdkDisplay *display; GdkWindow *window; g_assert (POP_IS_WINDOW_STACK (data)); stack = POP_WINDOW_STACK (data); display = gdk_screen_get_display (stack->priv->screen); window = gdk_window_foreign_new_for_display (display, x_window_id); if (window == NULL) { return; } if (!window_can_output (window)) { g_print ("excluding window 0x%lx\n", GDK_WINDOW_XWINDOW (window)); g_object_unref (window); return; } g_queue_push_tail (stack->priv->windows, window); } static void on_query_tree_reply (GdkDisplay *display, Status tree_queried, XErrorEvent *error, gpointer data) { PopQueryTreeState *state; state = (PopQueryTreeState *) data; g_assert (state != NULL); if (tree_queried) { guint i; g_assert (state->number_of_children > 0); g_assert (state->children != NULL); for (i = 0; i < state->number_of_children; i++) queried_window_foreach_func (state->children[i], state->data); XFree (state->children); state->children = NULL; state->number_of_children = 0; } g_object_unref (state->window); state->window = NULL; g_slice_free (PopQueryTreeState, state); g_assert (POP_IS_WINDOW_STACK (state->data)); pop_window_stack_set_ready (POP_WINDOW_STACK (state->data), TRUE); } static void pop_window_stack_ask_for_window_list (PopWindowStack *stack) { GdkScreen *screen; GdkDisplay *display; PopQueryTreeState *state; 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); state = g_slice_new0 (PopQueryTreeState); state->window = g_object_ref (gdk_screen_get_root_window (screen)); state->data = stack; pop_x_reply_watch_add (display, query_tree_request, state, on_query_tree_reply, state); } static void pop_window_stack_set_screen (PopWindowStack *stack, GdkScreen *screen) { if (screen != stack->priv->screen) { stack->priv->screen = screen; pop_window_stack_ask_for_window_list (stack); g_object_notify (G_OBJECT (stack), "screen"); } } static void pop_window_stack_set_ready (PopWindowStack *stack, gboolean is_ready) { is_ready = (is_ready == TRUE); if (is_ready != stack->priv->is_ready) { stack->priv->is_ready = is_ready; g_object_notify (G_OBJECT (stack), "is-ready"); } } #ifdef POP_WINDOW_STACK_ENABLE_TEST #include #include static void print_toplevel (PopWindowStack *stack, GdkWindow *window, gpointer data) { g_print ("0x%lx\n", GDK_WINDOW_XWINDOW (window)); } 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 (); gtk_init (&argc, &argv); g_message ("creating instance of 'window stack' object..."); stack = pop_window_stack_new (gdk_screen_get_default ()); g_message ("'window stack' object created successfully"); pop_window_stack_foreach (stack, print_toplevel, NULL); 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 */