/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2003, 2004, 2005, 2006 Red Hat, Inc. * Copyright (C) 2003 Keith Packard * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include "compositor.h" #include "screen.h" #include "errors.h" #include "window.h" #include "frame.h" #include "workspace.h" #include #include #ifdef HAVE_COMPOSITE_EXTENSIONS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "effects.h" #include "c-screen.h" #endif /* HAVE_COMPOSITE_EXTENSIONS */ #define FRAME_INTERVAL_MILLISECONDS ((int)(1000.0/40.0)) #ifdef HAVE_COMPOSITE_EXTENSIONS /* Screen specific information */ typedef struct MoveInfo MoveInfo; struct MetaCompositor { MetaDisplay *meta_display; WsDisplay *display; guint repair_idle; guint enabled : 1; guint have_composite : 1; guint have_damage : 1; guint have_fixes : 1; guint have_name_window_pixmap : 1; guint debug_updates : 1; GList *ignored_damage; }; #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS static WsDisplay *compositor_display; #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS static void handle_error (Display *dpy, XErrorEvent *ev, gpointer data) { WsDisplay *display = data; ws_display_process_xerror (display, ev); } #endif #ifdef HAVE_COMPOSITE_EXTENSIONS static Window get_xid (MetaWindow *window) { if (window->frame) return window->frame->xwindow; else return window->xwindow; } #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS /* This is called by Metacity's effect code when an effect needs to happen. * In compositor-less Metacity, this includes things like the wireframe * zoom when a window is minimised. We have a rather larger box of tricks. */ static void do_effect (MetaEffect *effect, gpointer data) { MetaCompScreen *screen; MetaCompWindow *window; screen = meta_comp_screen_get_by_xwindow (get_xid (effect->window)); if (!screen) { /* sanity check: if no screen is found, bail */ meta_warning ("No screen found for %s (%ld); aborting effect.\n", effect->window->desc, get_xid (effect->window)); return; } window = meta_comp_screen_lookup_window (screen, get_xid (effect->window)); switch (effect->type) { case META_EFFECT_MINIMIZE: if (!effect->window->frame) { meta_effect_end (effect); return; } meta_comp_screen_raise_window (screen, effect->window->frame->xwindow); meta_comp_window_run_minimize (window, effect); break; case META_EFFECT_UNMINIMIZE: meta_comp_window_run_unminimize (window, effect); break; case META_EFFECT_FOCUS: meta_comp_window_run_focus (window, effect); break; case META_EFFECT_CLOSE: meta_comp_window_freeze_stack (window); meta_comp_window_set_updates (window, FALSE); meta_comp_window_explode (window, effect); break; default: g_assert_not_reached(); break; } } #endif /* HAVE_COMPOSITE_EXTENSIONS */ /* Constructs a new compositor. The rest of Metacity knows whether the * compositor is turned on by whether this function returns NULL or not. */ MetaCompositor * meta_compositor_new (MetaDisplay *display) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaCompositor *compositor; compositor = g_new0 (MetaCompositor, 1); if (!compositor_display) { gboolean has_extensions; compositor_display = ws_display_new (NULL); meta_errors_register_foreign_display ( compositor_display->xdisplay, handle_error, compositor_display); has_extensions = ws_display_init_composite (compositor_display) && ws_display_init_damage (compositor_display) && ws_display_init_fixes (compositor_display) && ws_display_init_test (compositor_display); if (!has_extensions) { g_warning ("Disabling compositor since the server is missing at " "least one of the COMPOSITE, DAMAGE, FIXES or TEST " "extensions"); return NULL; } ws_display_set_ignore_grabs (compositor_display, TRUE); } compositor->display = compositor_display; ws_display_set_synchronize (compositor_display, getenv ("METACITY_SYNC") != NULL); compositor->meta_display = display; compositor->enabled = TRUE; /* Compositor without bling is the default, but leave the bling * accessible for now. */ if (getenv("METACITY_BLING")!=NULL) meta_push_effect_handler (do_effect, compositor); return compositor; #else /* HAVE_COMPOSITE_EXTENSIONS */ return NULL; #endif /* HAVE_COMPOSITE_EXTENSIONS */ } #if 0 void meta_compositor_set_debug_updates (MetaCompositor *compositor, gboolean debug_updates) { #ifdef HAVE_COMPOSITE_EXTENSIONS compositor->debug_updates = !!debug_updates; #endif /* HAVE_COMPOSITE_EXTENSIONS */ } #endif /* 0 */ #ifdef HAVE_COMPOSITE_EXTENSIONS static void remove_repair_idle (MetaCompositor *compositor) { if (compositor->repair_idle) { meta_topic (META_DEBUG_COMPOSITOR, "Damage idle removed\n"); g_source_remove (compositor->repair_idle); compositor->repair_idle = 0; } } #endif /* HAVE_COMPOSITE_EXTENSIONS */ void meta_compositor_unref (MetaCompositor *compositor) { #ifdef HAVE_COMPOSITE_EXTENSIONS /* There isn't really a refcount at the moment since * there's no ref() */ remove_repair_idle (compositor); g_free (compositor); #endif /* HAVE_COMPOSITE_EXTENSIONS */ } /**************************************************************************** * * HANDLING X EVENTS * * Here are several process_* functions, which are all called by * meta_compositor_process_event at the bottom. * ****************************************************************************/ #ifdef HAVE_COMPOSITE_EXTENSIONS /* Handles the CirculateNotify XEvent. */ static void process_circulate_notify (MetaCompositor *compositor, XCirculateEvent* event) { /* FIXME: Do something here. */ } #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS /* Handles the ConfigureNotify XEvent. */ static void process_configure_notify (MetaCompositor *compositor, XConfigureEvent *event) { MetaCompScreen *minfo = meta_comp_screen_get_by_xwindow (event->window); #if 0 g_print ("minfo: %lx => %p\n", event->window, minfo); #endif #if 0 g_print ("configure on %lx (above: %lx) %d %d %d %d\n", event->window, event->above, event->x, event->y, event->width, event->height); #endif if (!minfo) { #if 0 g_print (" --- ignoring configure (no screen info)\n"); #endif return; } meta_comp_screen_restack (minfo, event->window, event->above); meta_comp_screen_set_size (minfo, event->window, event->x, event->y, event->width, event->height); } #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS /* Handles the Expose XEvent. * XXX FIXME: This looks like it quite urgently needs looking into. --tthurman */ static void process_expose (MetaCompositor *compositor, XExposeEvent *event) { /* FIXME: queue repaint */ } #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS #if 0 /* Apparently never used. */ typedef struct { CmDrawableNode *node; GTimer *timer; } FadeInfo; #define FADE_TIME 0.3 static gboolean fade_out (gpointer data) { FadeInfo *info = data; gdouble elapsed = g_timer_elapsed (info->timer, NULL); gdouble alpha; if (elapsed > FADE_TIME) alpha = 0.0; else alpha = 1 - (elapsed / FADE_TIME); cm_drawable_node_set_alpha (info->node, alpha); #if 0 g_print ("fade out: %f\n", alpha); #endif if (elapsed >= FADE_TIME) { g_object_unref (info->node); cm_drawable_node_set_viewable (info->node, FALSE); return FALSE; } else { return TRUE; } } #endif /* 0 */ #endif #ifdef HAVE_COMPOSITE_EXTENSIONS /* Handles the MapNotify XEvent. */ static void process_map (MetaCompositor *compositor, XMapEvent *event) { MetaScreen *screen; /* FIXME: do we sometimes get mapnotifies for windows that are * not (direct) children of the root? */ /* See if window was mapped as child of root */ screen = meta_display_screen_for_root (compositor->meta_display, event->event); if (screen == NULL) { meta_topic (META_DEBUG_COMPOSITOR, "MapNotify received on non-root 0x%lx for 0x%lx\n", event->event, event->window); /* MapNotify wasn't for a child of the root */ return; } meta_comp_screen_add_window (screen->compositor_data, event->window); } #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS /* Handles the UnmapNotify XEvent. */ static void process_unmap (MetaCompositor *compositor, XUnmapEvent *event) { MetaScreen *screen; /* See if window was unmapped as child of root */ screen = meta_display_screen_for_root (compositor->meta_display, event->event); if (screen == NULL) { meta_topic (META_DEBUG_COMPOSITOR, "UnmapNotify received on non-root 0x%lx for 0x%lx\n", event->event, event->window); /* UnmapNotify wasn't for a child of the root */ return; } meta_comp_screen_unmap (screen->compositor_data, event->window); } #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS /* Handles the CreateNotify XEvent. */ static void process_create (MetaCompositor *compositor, XCreateWindowEvent *event) { MetaScreen *screen; XWindowAttributes attrs; screen = meta_display_screen_for_root (compositor->meta_display, event->parent); if (screen == NULL) { meta_topic (META_DEBUG_COMPOSITOR, "CreateNotify received on non-root 0x%lx for 0x%lx\n", event->parent, event->window); return; } meta_error_trap_push_with_return (compositor->meta_display); XGetWindowAttributes (compositor->meta_display->xdisplay, event->window, &attrs); if (meta_error_trap_pop_with_return (compositor->meta_display, TRUE) != Success) { meta_topic (META_DEBUG_COMPOSITOR, "Failed to get attributes for window 0x%lx\n", event->window); } else { #if 0 g_print (//META_DEBUG_COMPOSITOR, "Create window 0x%lx, adding\n", event->window); #endif meta_compositor_add_window (compositor, event->window, &attrs); } } #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS /* Handles the DestroyNotify XEvent. */ static void process_destroy (MetaCompositor *compositor, XDestroyWindowEvent *event) { MetaScreen *screen; screen = meta_display_screen_for_root (compositor->meta_display, event->event); #if 0 g_print ("destroywindow\n"); #endif if (screen == NULL) { #if 0 g_print ("ignoring\n"); #endif meta_topic (META_DEBUG_COMPOSITOR, "DestroyNotify received on non-root 0x%lx for 0x%lx\n", event->event, event->window); return; } meta_topic (META_DEBUG_COMPOSITOR, "Destroy window 0x%lx\n", event->window); meta_compositor_remove_window (compositor, event->window); } #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS /* Handles the ReparentNotify XEvent. */ static void process_reparent (MetaCompositor *compositor, XReparentEvent *event) { /* Reparent from one screen to another doesn't happen now, but * it's been suggested as a future extension */ MetaScreen *event_screen; MetaScreen *parent_screen; event_screen = meta_display_screen_for_root (compositor->meta_display, event->event); if (event_screen == NULL) { meta_topic (META_DEBUG_COMPOSITOR, "ReparentNotify received on non-root 0x%lx for 0x%lx\n", event->event, event->window); return; } parent_screen = meta_display_screen_for_root (compositor->meta_display, event->parent); if (parent_screen == NULL) { meta_topic (META_DEBUG_COMPOSITOR, "ReparentNotify 0x%lx to a non-screen or unmanaged screen 0x%lx\n", event->window, event->parent); meta_compositor_remove_window (compositor, event->window); return; } else { meta_comp_screen_raise_window (parent_screen->compositor_data, event->window); } } #endif /* HAVE_COMPOSITE_EXTENSIONS */ /* If the compositor is enabled, this function gets called with any XEvent * in case we want to deal with it specially. */ void meta_compositor_process_event (MetaCompositor *compositor, XEvent *event, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS if (!compositor->enabled) return; /* no extension */ if (event->type == CirculateNotify) { process_circulate_notify (compositor, (XCirculateEvent*) event); } else if (event->type == ConfigureNotify) { process_configure_notify (compositor, (XConfigureEvent*) event); } else if (event->type == Expose) { process_expose (compositor, (XExposeEvent*) event); } else if (event->type == UnmapNotify) { process_unmap (compositor, (XUnmapEvent*) event); } else if (event->type == MapNotify) { process_map (compositor, (XMapEvent*) event); } else if (event->type == ReparentNotify) { process_reparent (compositor, (XReparentEvent*) event); } else if (event->type == CreateNotify) { process_create (compositor, (XCreateWindowEvent*) event); } else if (event->type == DestroyNotify) { process_destroy (compositor, (XDestroyWindowEvent*) event); } #endif /* HAVE_COMPOSITE_EXTENSIONS */ } #ifdef HAVE_COMPOSITE_EXTENSIONS #if 0 static GTimer *timer; #endif /* 0 */ #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS #if 0 static void dump_stacking_order (GList *nodes) { GList *list; for (list = nodes; list != NULL; list = list->next) { CmDrawableNode *node = list->data; g_print ("%lx, ", WS_RESOURCE_XID (node->drawable)); } g_print ("\n"); } #endif /* 0 */ #endif /* This is called when metacity does its XQueryTree() on startup * and when a new window is mapped. */ void meta_compositor_add_window (MetaCompositor *compositor, Window xwindow, XWindowAttributes *attrs) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaScreen *screen = meta_screen_for_x_screen (attrs->screen); MetaCompScreen *minfo = screen->compositor_data; meta_comp_screen_add_window (minfo, xwindow); #endif } void meta_compositor_remove_window (MetaCompositor *compositor, Window xwindow) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaCompScreen *minfo; minfo = meta_comp_screen_get_by_xwindow (xwindow); if (minfo) meta_comp_screen_remove_window (minfo, xwindow); #endif /* HAVE_COMPOSITE_EXTENSIONS */ } void meta_compositor_manage_screen (MetaCompositor *compositor, MetaScreen *screen) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaCompScreen *info; if (screen->compositor_data) return; info = meta_comp_screen_new (compositor->display, screen); screen->compositor_data = info; meta_comp_screen_redirect (info); #endif } void meta_compositor_unmanage_screen (MetaCompositor *compositor, MetaScreen *screen) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaCompScreen *info = screen->compositor_data; meta_comp_screen_unredirect (info); screen->compositor_data = NULL; #endif } #ifdef HAVE_COMPOSITE_EXTENSIONS #endif typedef struct { double x; double y; double width; double height; } DoubleRect; #if 0 static gdouble interpolate (gdouble t, gdouble begin, gdouble end, double power) { return (begin + (end - begin) * pow (t, power)); } #endif #if 0 static gboolean stop_minimize (gpointer data) { MiniInfo *info = data; g_source_remove (info->repaint_id); cm_drawable_node_set_deformation_func (info->node, NULL, NULL); if (info->finished_func) info->finished_func (info->finished_data); g_free (info); return FALSE; } #endif #if 0 static void minimize_deformation (gdouble time, double in_x, double in_y, double *out_x, double *out_y, gpointer data) { #define MINIMIZE_TIME 0.5 MiniInfo *info = data; gdouble elapsed; gdouble pos; if (info->start_time == -1) info->start_time = time; elapsed = time - info->start_time; pos = elapsed / MINIMIZE_TIME; *out_x = interpolate (pos, in_x, info->target.x + info->target.width * ((in_x - info->start.x) / info->start.width), 10 * in_y); *out_y = interpolate (pos, in_y, info->target.y + info->target.height * ((in_y - info->start.y) / info->start.height), 1.0); if (elapsed > MINIMIZE_TIME) { g_assert (info->node); if (!info->idle_id) info->idle_id = g_idle_add (stop_minimize, info); } } #endif void meta_compositor_set_updates (MetaCompositor *compositor, MetaWindow *window, gboolean updates) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaCompScreen *info = window->screen->compositor_data; meta_comp_screen_set_updates (info, get_xid (window), updates); #endif } #ifdef HAVE_COMPOSITE_EXTENSIONS #define BALLOON_TIME 2 typedef struct { CmDrawableNode *node; MetaAnimationFinishedFunc finished; gpointer finished_data; GTimer *timer; } BalloonInfo; #endif void meta_compositor_destroy (MetaCompositor *compositor) { #ifdef HAVE_COMPOSITE_EXTENSIONS g_free (compositor); #endif } #ifdef HAVE_COMPOSITE_EXTENSIONS struct MoveInfo { GTimer *timer; gboolean finished; Model *model; MetaScreen *screen; MetaWindow *window; gdouble last_time; gboolean window_destroyed; MetaCompositor *compositor; }; #endif #ifdef HAVE_COMPOSITE_EXTENSIONS void get_patch_points (Model *model, CmPoint points[4][4]) { int i, j; for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { double obj_x, obj_y; model_get_position (model, i, j, &obj_x, &obj_y); points[j][i].x = obj_x; points[j][i].y = obj_y; } } } static GList *move_infos; static gboolean wobble (gpointer data) { MoveInfo *info = data; MetaCompScreen *minfo = info->screen->compositor_data; double t = g_timer_elapsed (info->timer, NULL); #if 0 g_print ("info->window_destroyed: %d\n", info->window_destroyed); #endif if ((info->finished && model_is_calm (info->model)) || info->window_destroyed) { if (!info->window_destroyed) meta_comp_screen_unset_patch (minfo, get_xid (info->window)); move_infos = g_list_remove (move_infos, info); g_free (info); #if 0 g_print ("stop wobb\n"); #endif return FALSE; } else { int i; int n_steps; CmPoint points[4][4]; n_steps = floor ((t - info->last_time) * 75); for (i = 0; i < n_steps; ++i) model_step (info->model); if (i > 0) info->last_time = t; get_patch_points (info->model, points); meta_comp_screen_set_patch (minfo, get_xid (info->window), points); return TRUE; } } #endif void compute_window_rect (MetaWindow *window, MetaRectangle *rect) { /* FIXME: does metacity include this function somewhere? */ if (window->frame) { *rect = window->frame->rect; } else { *rect = window->user_rect; } } void meta_compositor_begin_move (MetaCompositor *compositor, MetaWindow *window, MetaRectangle *initial, int grab_x, int grab_y) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaRectangle rect; MoveInfo *move_info; #if 0 g_print ("begin move\n"); #endif if (!g_getenv ("USE_WOBBLY")) return; move_info = g_new0 (MoveInfo, 1); move_infos = g_list_prepend (move_infos, move_info); move_info->compositor = compositor; move_info->last_time = 0.0; move_info->timer = g_timer_new (); move_info->window_destroyed = FALSE; compute_window_rect (window, &rect); #if 0 g_print ("init: %d %d\n", initial->x, initial->y); g_print ("window: %d %d\n", window->rect.x, window->rect.y); g_print ("frame: %d %d\n", rect.x, rect.y); g_print ("grab: %d %d\n", grab_x, grab_y); #endif move_info->model = model_new (&rect, TRUE); move_info->window = window; move_info->screen = window->screen; model_begin_move (move_info->model, grab_x, grab_y); g_idle_add (wobble, move_info); #endif } #ifdef HAVE_COMPOSITE_EXTENSIONS static MoveInfo * find_info (MetaWindow *window) { GList *list; for (list = move_infos; list != NULL; list = list->next) { MoveInfo *info = list->data; if (info->window == window) return info; } return NULL; } #endif void meta_compositor_update_move (MetaCompositor *compositor, MetaWindow *window, int x, int y) { #ifdef HAVE_COMPOSITE_EXTENSIONS MoveInfo *move_info = find_info (window); if (!g_getenv ("USE_WOBBLY")) return; model_update_move (move_info->model, x, y); #endif } void meta_compositor_end_move (MetaCompositor *compositor, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS MoveInfo *info = find_info (window); if (!g_getenv ("USE_WOBBLY")) return; info->finished = TRUE; #endif } void meta_compositor_free_window (MetaCompositor *compositor, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS MoveInfo *info = find_info (window); if (!g_getenv ("USE_WOBBLY")) return; if (info) info->window_destroyed = TRUE; #endif }