static gint window_compare(gconstpointer a, gconstpointer b) { ClutterActor* aa = *(ClutterActor**)a; ClutterActor* bb = *(ClutterActor**)b; MetaWindowActor* a1 = META_WINDOW_ACTOR(clutter_clone_get_source(CLUTTER_CLONE(aa))); MetaWindowActor* b1 = META_WINDOW_ACTOR(clutter_clone_get_source(CLUTTER_CLONE(bb))); MetaWindow* w1 = meta_window_actor_get_meta_window(a1); MetaWindow* w2 = meta_window_actor_get_meta_window(b1); return meta_window_get_stable_sequence(w1) - meta_window_get_stable_sequence(w2); }
/* * Simple TV-out like effect. */ static void destroy (MetaPlugin *plugin, MetaWindowActor *window_actor) { MetaWindowType type; ClutterActor *actor = CLUTTER_ACTOR (window_actor); MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor); type = meta_window_get_window_type (meta_window); if (type == META_WINDOW_NORMAL) { ClutterAnimation *animation; EffectCompleteData *data = g_new0 (EffectCompleteData, 1); ActorPrivate *apriv = get_actor_private (window_actor); animation = clutter_actor_animate (actor, CLUTTER_EASE_OUT_QUAD, DESTROY_TIMEOUT, "opacity", 0, "scale-x", 0.8, "scale-y", 0.8, NULL); apriv->tml_destroy = clutter_animation_get_timeline (animation); data->plugin = plugin; data->actor = actor; g_signal_connect (apriv->tml_destroy, "completed", G_CALLBACK (on_destroy_effect_complete), data); } else meta_plugin_destroy_completed (plugin, window_actor); }
void mnb_zones_preview_add_window (MnbZonesPreview *preview, MetaWindowActor *window) { ClutterActor *clone; ClutterActor *group; MetaRectangle rect; gint workspace; /* TODO: Determine if we need to add a weak reference on the window * in case it gets destroyed during the animation. * I'd have thought that the clone's reference on the texture * would be enough that this wouldn't be necessary. * * We do; while the clone's reference is enough to keep the texture about, * it is not enough to make it possible to map the clone once the texture * has been unparented. */ workspace = meta_window_actor_get_workspace (window); group = mnb_zones_preview_get_workspace_group (preview, workspace); clone = clutter_clone_new (meta_window_actor_get_texture (window)); g_signal_connect (window, "destroy", G_CALLBACK (mnb_zones_preview_mcw_destroy_cb), clone); g_signal_connect (clone, "destroy", G_CALLBACK (mnb_zones_preview_clone_destroy_cb), window); meta_window_get_input_rect (meta_window_actor_get_meta_window (window), &rect); clutter_actor_set_position (clone, rect.x, rect.y); clutter_container_add_actor (CLUTTER_CONTAINER (group), clone); }
/* * The Nature of Maximize operation is such that it is difficult to do a visual * effect that would work well. Scaling, the obvious effect, does not work that * well, because at the end of the effect we end up with window content bigger * and differently laid out than in the real window; this is a proof concept. * * (Something like a sound would be more appropriate.) */ static void maximize (MetaPlugin *plugin, MetaWindowActor *window_actor, gint end_x, gint end_y, gint end_width, gint end_height) { MetaWindowType type; ClutterActor *actor = CLUTTER_ACTOR (window_actor); MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor); gdouble scale_x = 1.0; gdouble scale_y = 1.0; gfloat anchor_x = 0; gfloat anchor_y = 0; type = meta_window_get_window_type (meta_window); if (type == META_WINDOW_NORMAL) { ClutterAnimation *animation; EffectCompleteData *data = g_new0 (EffectCompleteData, 1); ActorPrivate *apriv = get_actor_private (window_actor); gfloat width, height; gfloat x, y; apriv->is_maximized = TRUE; clutter_actor_get_size (actor, &width, &height); clutter_actor_get_position (actor, &x, &y); /* * Work out the scale and anchor point so that the window is expanding * smoothly into the target size. */ scale_x = (gdouble)end_width / (gdouble) width; scale_y = (gdouble)end_height / (gdouble) height; anchor_x = (gdouble)(x - end_x)*(gdouble)width / ((gdouble)(end_width - width)); anchor_y = (gdouble)(y - end_y)*(gdouble)height / ((gdouble)(end_height - height)); clutter_actor_move_anchor_point (actor, anchor_x, anchor_y); animation = clutter_actor_animate (actor, CLUTTER_EASE_IN_SINE, MAXIMIZE_TIMEOUT, "scale-x", scale_x, "scale-y", scale_y, NULL); apriv->tml_maximize = clutter_animation_get_timeline (animation); data->plugin = plugin; data->actor = actor; g_signal_connect (apriv->tml_maximize, "completed", G_CALLBACK (on_maximize_effect_complete), data); return; } meta_plugin_maximize_completed (plugin, window_actor); }
void meta_compositor_remove_window (MetaCompositor *compositor, MetaWindow *window) { MetaWindowActor *window_actor = NULL; MetaScreen *screen; MetaCompScreen *info; DEBUG_TRACE ("meta_compositor_remove_window\n"); window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); if (!window_actor) return; screen = meta_window_get_screen (window); info = meta_screen_get_compositor_data (screen); if (window_actor == info->unredirected_window) { meta_window_actor_set_redirected (window_actor, TRUE); meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (info->unredirected_window)), NULL); info->unredirected_window = NULL; } meta_window_actor_destroy (window_actor); }
static void pre_paint_windows (MetaCompositor *compositor) { GList *l; MetaWindowActor *top_window; if (compositor->onscreen == NULL) { compositor->onscreen = COGL_ONSCREEN (cogl_get_draw_framebuffer ()); compositor->frame_closure = cogl_onscreen_add_frame_callback (compositor->onscreen, frame_callback, compositor, NULL); } if (compositor->windows == NULL) return; top_window = g_list_last (compositor->windows)->data; if (meta_window_actor_should_unredirect (top_window) && compositor->disable_unredirect_count == 0) set_unredirected_window (compositor, meta_window_actor_get_meta_window (top_window)); else set_unredirected_window (compositor, NULL); for (l = compositor->windows; l; l = l->next) meta_window_actor_pre_paint (l->data); if (compositor->frame_has_updated_xsurfaces) { /* We need to make sure that any X drawing that happens before * the XDamageSubtract() for each window above is visible to * subsequent GL rendering; the only standardized way to do this * is EXT_x11_sync_object, which isn't yet widely available. For * now, we count on details of Xorg and the open source drivers, * and hope for the best otherwise. * * Xorg and open source driver specifics: * * The X server makes sure to flush drawing to the kernel before * sending out damage events, but since we use * DamageReportBoundingBox there may be drawing between the last * damage event and the XDamageSubtract() that needs to be * flushed as well. * * Xorg always makes sure that drawing is flushed to the kernel * before writing events or responses to the client, so any * round trip request at this point is sufficient to flush the * GLX buffers. */ XSync (compositor->display->xdisplay, False); compositor->frame_has_updated_xsurfaces = FALSE; } }
static void pre_paint_windows (MetaCompScreen *info) { GList *l; MetaWindowActor *top_window; MetaWindowActor *expected_unredirected_window = NULL; if (info->windows == NULL) return; top_window = g_list_last (info->windows)->data; if (meta_window_actor_should_unredirect (top_window) && info->disable_unredirect_count == 0) expected_unredirected_window = top_window; if (info->unredirected_window != expected_unredirected_window) { if (info->unredirected_window != NULL) { meta_window_actor_set_redirected (info->unredirected_window, TRUE); meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (info->unredirected_window)), NULL); } if (expected_unredirected_window != NULL) { meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (top_window)), meta_window_actor_get_meta_window (top_window)); meta_window_actor_set_redirected (top_window, FALSE); } info->unredirected_window = expected_unredirected_window; } for (l = info->windows; l; l = l->next) meta_window_actor_pre_paint (l->data); }
/* * Simple minimize handler: it applies a scale effect (which must be reversed on * completion). */ static void minimize (MetaPlugin *plugin, MetaWindowActor *window_actor) { MetaWindowType type; MetaRectangle icon_geometry; MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor); ClutterActor *actor = CLUTTER_ACTOR (window_actor); type = meta_window_get_window_type (meta_window); if (!meta_window_get_icon_geometry(meta_window, &icon_geometry)) { icon_geometry.x = 0; icon_geometry.y = 0; } if (type == META_WINDOW_NORMAL) { ClutterAnimation *animation; EffectCompleteData *data = g_new0 (EffectCompleteData, 1); ActorPrivate *apriv = get_actor_private (window_actor); apriv->is_minimized = TRUE; clutter_actor_move_anchor_point_from_gravity (actor, CLUTTER_GRAVITY_CENTER); animation = clutter_actor_animate (actor, CLUTTER_EASE_IN_SINE, MINIMIZE_TIMEOUT, "scale-x", 0.0, "scale-y", 0.0, "x", (double)icon_geometry.x, "y", (double)icon_geometry.y, NULL); apriv->tml_minimize = clutter_animation_get_timeline (animation); data->plugin = plugin; data->actor = actor; g_signal_connect (apriv->tml_minimize, "completed", G_CALLBACK (on_minimize_effect_complete), data); } else meta_plugin_minimize_completed (plugin, window_actor); }
/* * See comments on the maximize() function. * * (Just a skeleton code.) */ static void unmaximize (MetaPlugin *plugin, MetaWindowActor *window_actor, gint end_x, gint end_y, gint end_width, gint end_height) { MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor); MetaWindowType type = meta_window_get_window_type (meta_window); if (type == META_WINDOW_NORMAL) { ActorPrivate *apriv = get_actor_private (window_actor); apriv->is_maximized = FALSE; } /* Do this conditionally, if the effect requires completion callback. */ meta_plugin_unmaximize_completed (plugin, window_actor); }
/* * Simple map handler: it applies a scale effect which must be reversed on * completion). */ static void map (MetaPlugin *plugin, MetaWindowActor *window_actor) { MetaWindowType type; ClutterActor *actor = CLUTTER_ACTOR (window_actor); MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor); type = meta_window_get_window_type (meta_window); if (type == META_WINDOW_NORMAL) { ClutterAnimation *animation; EffectCompleteData *data = g_new0 (EffectCompleteData, 1); ActorPrivate *apriv = get_actor_private (window_actor); clutter_actor_set_pivot_point (actor, 0.5, 0.5); clutter_actor_set_opacity (actor, 0); clutter_actor_set_scale (actor, 0.5, 0.5); clutter_actor_show (actor); animation = clutter_actor_animate (actor, CLUTTER_EASE_OUT_QUAD, MAP_TIMEOUT, "opacity", 255, "scale-x", 1.0, "scale-y", 1.0, NULL); apriv->tml_map = clutter_animation_get_timeline (animation); data->actor = actor; data->plugin = plugin; g_signal_connect (apriv->tml_map, "completed", G_CALLBACK (on_map_effect_complete), data); } else meta_plugin_map_completed (plugin, window_actor); }
/* * Simple TV-out like effect. */ static void destroy (MetaPlugin *plugin, MetaWindowActor *window_actor) { MetaWindowType type; ClutterActor *actor = CLUTTER_ACTOR (window_actor); MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor); type = meta_window_get_window_type (meta_window); if (type == META_WINDOW_NORMAL || type == META_WINDOW_DIALOG || type == META_WINDOW_MODAL_DIALOG) { ClutterAnimation *animation; EffectCompleteData *data = g_new0 (EffectCompleteData, 1); ActorPrivate *apriv = get_actor_private (window_actor); clutter_actor_move_anchor_point_from_gravity (actor, CLUTTER_GRAVITY_CENTER); animation = clutter_actor_animate (actor, CLUTTER_EASE_IN_SINE, DESTROY_TIMEOUT, "scale-x", DESTROY_SCALE, "scale-y", DESTROY_SCALE, "opacity", 0, NULL); apriv->tml_destroy = clutter_animation_get_timeline (animation); data->plugin = plugin; data->actor = actor; g_signal_connect (apriv->tml_destroy, "completed", G_CALLBACK (on_destroy_effect_complete), data); } else meta_plugin_destroy_completed (plugin, window_actor); }
/* * Simple map handler: it applies a scale effect which must be reversed on * completion). */ static void map (MetaPlugin *plugin, MetaWindowActor *window_actor) { MetaWindowType type; ClutterActor *actor = CLUTTER_ACTOR (window_actor); MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor); type = meta_window_get_window_type (meta_window); if (type == META_WINDOW_NORMAL || type == META_WINDOW_DIALOG || type == META_WINDOW_MODAL_DIALOG) { ClutterAnimation *animation; EffectCompleteData *data = g_new0 (EffectCompleteData, 1); ActorPrivate *apriv = get_actor_private (window_actor); clutter_actor_move_anchor_point_from_gravity (actor, CLUTTER_GRAVITY_CENTER); clutter_actor_set_scale (actor, MAP_SCALE, MAP_SCALE); clutter_actor_set_opacity (actor, 0); clutter_actor_show (actor); animation = clutter_actor_animate (actor, CLUTTER_EASE_IN_SINE, MAP_TIMEOUT, "scale-x", 1.0, "scale-y", 1.0, "opacity", 255, NULL); apriv->tml_map = clutter_animation_get_timeline (animation); data->actor = actor; data->plugin = plugin; g_signal_connect (apriv->tml_map, "completed", G_CALLBACK (on_map_effect_complete), data); apriv->is_minimized = FALSE; } else if (type == META_WINDOW_DOCK) { /* For context menus (popup/dropdown) we fade the menu in */ ClutterAnimation *animation; EffectCompleteData *data = g_new0 (EffectCompleteData, 1); ActorPrivate *apriv = get_actor_private (window_actor); clutter_actor_set_opacity (actor, 0); clutter_actor_show (actor); animation = clutter_actor_animate (actor, CLUTTER_EASE_IN_SINE, MAP_TIMEOUT, "opacity", 255, NULL); apriv->tml_map = clutter_animation_get_timeline (animation); data->actor = actor; data->plugin = plugin; g_signal_connect (apriv->tml_map, "completed", G_CALLBACK (on_map_effect_complete), data); apriv->is_minimized = FALSE; } else meta_plugin_map_completed (plugin, window_actor); }
static void moses_overview_dispose(GObject *object) { MosesOverview *overview = MOSES_OVERVIEW(object); MosesOverviewPrivate* priv = overview->priv; if (priv->disposed) return; priv->disposed = TRUE; g_clear_pointer(&priv->ov_head, g_object_unref); MetaScreen* screen = meta_plugin_get_screen(priv->plugin); ClutterActor* stage = meta_get_stage_for_screen(screen); ClutterActor* to_focus = NULL; if (priv->selected_actor) { to_focus = clutter_clone_get_source(CLUTTER_CLONE(priv->selected_actor)); } for (int i = 0; priv->clones && i < priv->clones->len; i++) { ClutterActor* clone = g_ptr_array_index(priv->clones, i); ClutterActor* orig = clutter_clone_get_source(CLUTTER_CLONE(clone)); clutter_actor_show(orig); // FIXME: maybe some actors had not been shown. clutter_actor_destroy(clone); } for (int i = 0; priv->badges && i < priv->badges->len; i++) { clutter_actor_destroy(CLUTTER_ACTOR(g_ptr_array_index(priv->badges, i))); } if (priv->background_actor) { clutter_actor_show(clutter_clone_get_source(CLUTTER_CLONE(priv->background_actor))); g_clear_pointer(&priv->background_actor, clutter_actor_destroy); } if (priv->modaled) { meta_plugin_end_modal(priv->plugin, clutter_get_current_event_time()); meta_enable_unredirect_for_screen(screen); if (priv->selected_workspace) { meta_workspace_activate(priv->selected_workspace, CLUTTER_CURRENT_TIME); MetaDisplay* display = meta_screen_get_display(screen); meta_compositor_switch_workspace(meta_display_get_compositor(display), meta_screen_get_active_workspace(screen), priv->selected_workspace, META_MOTION_DOWN); } else if (to_focus) { clutter_stage_set_key_focus(CLUTTER_STAGE(stage), to_focus); MetaWindowActor* actor = META_WINDOW_ACTOR(to_focus); MetaWindow* win = meta_window_actor_get_meta_window(actor); meta_window_raise(win); meta_window_focus(win, CLUTTER_CURRENT_TIME); } else if (priv->previous_focused) { if (!CLUTTER_IS_STAGE(priv->previous_focused)) { clutter_stage_set_key_focus(CLUTTER_STAGE(stage), priv->previous_focused); } } } G_OBJECT_CLASS(moses_overview_parent_class)->dispose(object); }
/* * This is the Metacity entry point for the effect. */ void mnb_switch_zones_effect (MetaPlugin *plugin, gint from, gint to, MetaMotionDirection direction) { GList *w; gint width, height; MetaScreen *screen; ClutterActor *window_group; if (running++) { /* * We have been called while the effect is already in progress; we need to * mutter know that we completed the previous run. */ if (--running < 0) { g_warning (G_STRLOC ": error in running effect accounting!"); running = 0; } meta_plugin_switch_workspace_completed (plugin); } if ((from == to) && !zones_preview) { if (--running < 0) { g_warning (G_STRLOC ": error in running effect accounting!"); running = 0; } meta_plugin_switch_workspace_completed (plugin); return; } screen = meta_plugin_get_screen (plugin); if (!zones_preview) { ClutterActor *stage; /* Construct the zones preview actor */ zones_preview = mnb_zones_preview_new (); g_object_set (G_OBJECT (zones_preview), "workspace", (gdouble)from, NULL); /* Add it to the stage */ stage = meta_get_stage_for_screen (screen); clutter_container_add_actor (CLUTTER_CONTAINER (stage), zones_preview); /* Attach to completed signal */ g_signal_connect (zones_preview, "switch-completed", G_CALLBACK (mnb_switch_zones_completed_cb), plugin); } meta_screen_get_size (screen, &width, &height); g_object_set (G_OBJECT (zones_preview), "workspace-width", (guint)width, "workspace-height", (guint)height, NULL); mnb_zones_preview_clear (MNB_ZONES_PREVIEW (zones_preview)); mnb_zones_preview_set_n_workspaces (MNB_ZONES_PREVIEW (zones_preview), meta_screen_get_n_workspaces (screen)); /* Add windows to zone preview actor */ for (w = meta_get_window_actors (screen); w; w = w->next) { MetaWindowActor *window_actor = w->data; gint workspace = meta_window_actor_get_workspace (window_actor); MetaWindow *window = meta_window_actor_get_meta_window (window_actor); MetaWindowType type = meta_window_get_window_type (window); /* * Only show regular windows that are not sticky (getting stacking order * right for sticky windows would be really hard, and since they appear * on each workspace, they do not help in identifying which workspace * it is). */ if ((workspace < 0) || meta_window_actor_is_override_redirect (window_actor) || (type != META_WINDOW_NORMAL)) continue; mnb_zones_preview_add_window (MNB_ZONES_PREVIEW (zones_preview), window_actor); } /* Make sure it's on top */ window_group = meta_get_window_group_for_screen (screen); clutter_actor_raise (zones_preview, window_group); /* Initiate animation */ mnb_zones_preview_change_workspace (MNB_ZONES_PREVIEW (zones_preview), to); }
static gboolean meta_pre_paint_func (gpointer data) { GList *l; MetaWindowActor *top_window; MetaCompositor *compositor = data; if (compositor->onscreen == NULL) { compositor->onscreen = COGL_ONSCREEN (cogl_get_draw_framebuffer ()); compositor->frame_closure = cogl_onscreen_add_frame_callback (compositor->onscreen, frame_callback, compositor, NULL); } if (compositor->windows == NULL) return TRUE; top_window = g_list_last (compositor->windows)->data; if (meta_window_actor_should_unredirect (top_window) && compositor->disable_unredirect_count == 0) set_unredirected_window (compositor, meta_window_actor_get_meta_window (top_window)); else set_unredirected_window (compositor, NULL); for (l = compositor->windows; l; l = l->next) meta_window_actor_pre_paint (l->data); if (compositor->frame_has_updated_xsurfaces) { /* We need to make sure that any X drawing that happens before * the XDamageSubtract() for each window above is visible to * subsequent GL rendering; the standardized way to do this is * GL_EXT_X11_sync_object. Since this isn't implemented yet in * mesa, we also have a path that relies on the implementation * of the open source drivers. * * Anything else, we just hope for the best. * * Xorg and open source driver specifics: * * The X server makes sure to flush drawing to the kernel before * sending out damage events, but since we use * DamageReportBoundingBox there may be drawing between the last * damage event and the XDamageSubtract() that needs to be * flushed as well. * * Xorg always makes sure that drawing is flushed to the kernel * before writing events or responses to the client, so any * round trip request at this point is sufficient to flush the * GLX buffers. */ if (compositor->have_x11_sync_object) compositor->have_x11_sync_object = meta_sync_ring_insert_wait (); else XSync (compositor->display->xdisplay, False); } return TRUE; }
void meta_compositor_sync_stack (MetaCompositor *compositor, GList *stack) { GList *old_stack; /* This is painful because hidden windows that we are in the process * of animating out of existence. They'll be at the bottom of the * stack of X windows, but we want to leave them in their old position * until the animation effect finishes. */ /* Sources: first window is the highest */ stack = g_list_copy (stack); /* The new stack of MetaWindow */ old_stack = g_list_reverse (compositor->windows); /* The old stack of MetaWindowActor */ compositor->windows = NULL; while (TRUE) { MetaWindowActor *old_actor = NULL, *stack_actor = NULL, *actor; MetaWindow *old_window = NULL, *stack_window = NULL, *window; /* Find the remaining top actor in our existing stack (ignoring * windows that have been hidden and are no longer animating) */ while (old_stack) { old_actor = old_stack->data; old_window = meta_window_actor_get_meta_window (old_actor); if ((old_window->hidden || old_window->unmanaging) && !meta_window_actor_effect_in_progress (old_actor)) { old_stack = g_list_delete_link (old_stack, old_stack); old_actor = NULL; } else break; } /* And the remaining top actor in the new stack */ while (stack) { stack_window = stack->data; stack_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (stack_window)); if (!stack_actor) { meta_verbose ("Failed to find corresponding MetaWindowActor " "for window %s\n", meta_window_get_description (stack_window)); stack = g_list_delete_link (stack, stack); } else break; } if (!old_actor && !stack_actor) /* Nothing more to stack */ break; /* We usually prefer the window in the new stack, but if if we * found a hidden window in the process of being animated out * of existence in the old stack we use that instead. We've * filtered out non-animating hidden windows above. */ if (old_actor && (!stack_actor || old_window->hidden || old_window->unmanaging)) { actor = old_actor; window = old_window; } else { actor = stack_actor; window = stack_window; } /* OK, we know what actor we want next. Add it to our window * list, and remove it from both source lists. (It will * be at the front of at least one, hopefully it will be * near the front of the other.) */ compositor->windows = g_list_prepend (compositor->windows, actor); stack = g_list_remove (stack, window); old_stack = g_list_remove (old_stack, actor); } sync_actor_stacking (compositor); }
static void switch_workspace (MetaPlugin *plugin, gint from, gint to, MetaMotionDirection direction) { MetaScreen *screen; MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (plugin)->priv; GList *l; ClutterActor *workspace0 = clutter_group_new (); ClutterActor *workspace1 = clutter_group_new (); ClutterActor *stage; int screen_width, screen_height; ClutterAnimation *animation; screen = meta_plugin_get_screen (plugin); stage = meta_get_stage_for_screen (screen); meta_screen_get_size (screen, &screen_width, &screen_height); clutter_actor_set_anchor_point (workspace1, screen_width, screen_height); clutter_actor_set_position (workspace1, screen_width, screen_height); clutter_actor_set_scale (workspace1, 0.0, 0.0); clutter_container_add_actor (CLUTTER_CONTAINER (stage), workspace1); clutter_container_add_actor (CLUTTER_CONTAINER (stage), workspace0); if (from == to) { meta_plugin_switch_workspace_completed (plugin); return; } l = g_list_last (meta_get_window_actors (screen)); while (l) { MetaWindowActor *window_actor = l->data; MetaWindow *window = meta_window_actor_get_meta_window (window_actor); MetaWorkspace *workspace; ActorPrivate *apriv = get_actor_private (window_actor); ClutterActor *actor = CLUTTER_ACTOR (window_actor); gint win_workspace; workspace = meta_window_get_workspace (window); win_workspace = meta_workspace_index (workspace); if (win_workspace == to || win_workspace == from) { apriv->orig_parent = clutter_actor_get_parent (actor); clutter_actor_reparent (actor, win_workspace == to ? workspace1 : workspace0); clutter_actor_show_all (actor); clutter_actor_raise_top (actor); } else if (win_workspace < 0) { /* Sticky window */ apriv->orig_parent = NULL; } else { /* Window on some other desktop */ clutter_actor_hide (actor); apriv->orig_parent = NULL; } l = l->prev; } priv->desktop1 = workspace0; priv->desktop2 = workspace1; animation = clutter_actor_animate (workspace0, CLUTTER_EASE_IN_SINE, SWITCH_TIMEOUT, "scale-x", 1.0, "scale-y", 1.0, NULL); priv->tml_switch_workspace1 = clutter_animation_get_timeline (animation); g_signal_connect (priv->tml_switch_workspace1, "completed", G_CALLBACK (on_switch_workspace_effect_complete), plugin); animation = clutter_actor_animate (workspace1, CLUTTER_EASE_IN_SINE, SWITCH_TIMEOUT, "scale-x", 0.0, "scale-y", 0.0, NULL); priv->tml_switch_workspace2 = clutter_animation_get_timeline (animation); }
static void meta_window_group_paint (ClutterActor *actor) { cairo_region_t *visible_region; ClutterActor *stage; cairo_rectangle_int_t visible_rect; GList *children, *l; int paint_x_origin, paint_y_origin; int actor_x_origin, actor_y_origin; int paint_x_offset, paint_y_offset; MetaWindowGroup *window_group = META_WINDOW_GROUP (actor); MetaCompScreen *info = meta_screen_get_compositor_data (window_group->screen); /* Normally we expect an actor to be drawn at it's position on the screen. * However, if we're inside the paint of a ClutterClone, that won't be the * case and we need to compensate. We look at the position of the window * group under the current model-view matrix and the position of the actor. * If they are both simply integer translations, then we can compensate * easily, otherwise we give up. * * Possible cleanup: work entirely in paint space - we can compute the * combination of the model-view matrix with the local matrix for each child * actor and get a total transformation for that actor for how we are * painting currently, and never worry about how actors are positioned * on the stage. */ if (!painting_untransformed (window_group, &paint_x_origin, &paint_y_origin) || !meta_actor_is_untransformed (actor, &actor_x_origin, &actor_y_origin)) { CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor); return; } paint_x_offset = paint_x_origin - actor_x_origin; paint_y_offset = paint_y_origin - actor_y_origin; /* We walk the list from top to bottom (opposite of painting order), * and subtract the opaque area of each window out of the visible * region that we pass to the windows below. */ children = clutter_actor_get_children (actor); children = g_list_reverse (children); /* Get the clipped redraw bounds from Clutter so that we can avoid * painting shadows on windows that don't need to be painted in this * frame. In the case of a multihead setup with mismatched monitor * sizes, we could intersect this with an accurate union of the * monitors to avoid painting shadows that are visible only in the * holes. */ stage = clutter_actor_get_stage (actor); clutter_stage_get_redraw_clip_bounds (CLUTTER_STAGE (stage), &visible_rect); visible_region = cairo_region_create_rectangle (&visible_rect); if (info->unredirected_window != NULL) { cairo_rectangle_int_t unredirected_rect; MetaWindow *window = meta_window_actor_get_meta_window (info->unredirected_window); meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect); cairo_region_subtract_rectangle (visible_region, &unredirected_rect); } for (l = children; l; l = l->next) { if (!CLUTTER_ACTOR_IS_VISIBLE (l->data)) continue; if (l->data == info->unredirected_window) continue; /* If an actor has effects applied, then that can change the area * it paints and the opacity, so we no longer can figure out what * portion of the actor is obscured and what portion of the screen * it obscures, so we skip the actor. * * This has a secondary beneficial effect: if a ClutterOffscreenEffect * is applied to an actor, then our clipped redraws interfere with the * caching of the FBO - even if we only need to draw a small portion * of the window right now, ClutterOffscreenEffect may use other portions * of the FBO later. So, skipping actors with effects applied also * prevents these bugs. * * Theoretically, we should check clutter_actor_get_offscreen_redirect() * as well for the same reason, but omitted for simplicity in the * hopes that no-one will do that. */ if (clutter_actor_has_effects (l->data)) continue; if (META_IS_WINDOW_ACTOR (l->data)) { MetaWindowActor *window_actor = l->data; int x, y; if (!meta_actor_is_untransformed (CLUTTER_ACTOR (window_actor), &x, &y)) continue; x += paint_x_offset; y += paint_y_offset; /* Temporarily move to the coordinate system of the actor */ cairo_region_translate (visible_region, - x, - y); meta_window_actor_set_visible_region (window_actor, visible_region); if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff) { cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor); if (obscured_region) cairo_region_subtract (visible_region, obscured_region); } meta_window_actor_set_visible_region_beneath (window_actor, visible_region); cairo_region_translate (visible_region, x, y); } else if (META_IS_BACKGROUND_ACTOR (l->data) || META_IS_BACKGROUND_GROUP (l->data)) { ClutterActor *background_actor = l->data; int x, y; if (!meta_actor_is_untransformed (CLUTTER_ACTOR (background_actor), &x, &y)) continue; x += paint_x_offset; y += paint_y_offset; cairo_region_translate (visible_region, - x, - y); if (META_IS_BACKGROUND_GROUP (background_actor)) meta_background_group_set_visible_region (META_BACKGROUND_GROUP (background_actor), visible_region); else meta_background_actor_set_visible_region (META_BACKGROUND_ACTOR (background_actor), visible_region); cairo_region_translate (visible_region, x, y); } } cairo_region_destroy (visible_region); CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor); /* Now that we are done painting, unset the visible regions (they will * mess up painting clones of our actors) */ for (l = children; l; l = l->next) { if (META_IS_WINDOW_ACTOR (l->data)) { MetaWindowActor *window_actor = l->data; meta_window_actor_reset_visible_regions (window_actor); } else if (META_IS_BACKGROUND_ACTOR (l->data)) { MetaBackgroundActor *background_actor = l->data; meta_background_actor_set_visible_region (background_actor, NULL); } } g_list_free (children); }
static void meta_window_group_paint (ClutterActor *actor) { cairo_region_t *clip_region; cairo_region_t *unobscured_region; ClutterActorIter iter; ClutterActor *child; cairo_rectangle_int_t visible_rect, clip_rect; int paint_x_offset, paint_y_offset; int paint_x_origin, paint_y_origin; int actor_x_origin, actor_y_origin; MetaWindowGroup *window_group = META_WINDOW_GROUP (actor); MetaCompositor *compositor = window_group->screen->display->compositor; ClutterActor *stage = CLUTTER_STAGE (compositor->stage); /* Start off by treating all windows as completely unobscured, so damage anywhere * in a window queues redraws, but confine it more below. */ clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { if (META_IS_WINDOW_ACTOR (child)) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (child); meta_window_actor_set_unobscured_region (window_actor, NULL); } } /* Normally we expect an actor to be drawn at it's position on the screen. * However, if we're inside the paint of a ClutterClone, that won't be the * case and we need to compensate. We look at the position of the window * group under the current model-view matrix and the position of the actor. * If they are both simply integer translations, then we can compensate * easily, otherwise we give up. * * Possible cleanup: work entirely in paint space - we can compute the * combination of the model-view matrix with the local matrix for each child * actor and get a total transformation for that actor for how we are * painting currently, and never worry about how actors are positioned * on the stage. */ if (!painting_untransformed (window_group, &paint_x_origin, &paint_y_origin) || !meta_actor_is_untransformed (actor, &actor_x_origin, &actor_y_origin)) { CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor); return; } paint_x_offset = paint_x_origin - actor_x_origin; paint_y_offset = paint_y_origin - actor_y_origin; visible_rect.x = visible_rect.y = 0; visible_rect.width = clutter_actor_get_width (stage); visible_rect.height = clutter_actor_get_height (stage); unobscured_region = cairo_region_create_rectangle (&visible_rect); /* Get the clipped redraw bounds from Clutter so that we can avoid * painting shadows on windows that don't need to be painted in this * frame. In the case of a multihead setup with mismatched monitor * sizes, we could intersect this with an accurate union of the * monitors to avoid painting shadows that are visible only in the * holes. */ clutter_stage_get_redraw_clip_bounds (stage, &clip_rect); clip_region = cairo_region_create_rectangle (&clip_rect); cairo_region_translate (clip_region, -paint_x_offset, -paint_y_offset); gboolean has_unredirected_window = compositor->unredirected_window != NULL; if (has_unredirected_window) { cairo_rectangle_int_t unredirected_rect; MetaWindow *window = meta_window_actor_get_meta_window (compositor->unredirected_window); meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect); cairo_region_subtract_rectangle (unobscured_region, &unredirected_rect); cairo_region_subtract_rectangle (clip_region, &unredirected_rect); } meta_window_group_cull_out (window_group, CLUTTER_ACTOR (compositor->unredirected_window), has_unredirected_window, unobscured_region, clip_region); cairo_region_destroy (unobscured_region); cairo_region_destroy (clip_region); CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor); meta_window_group_reset_culling (window_group); }
static void natural_placement (MosesOverview* self, MetaRectangle area) { g_debug("%s: geom: %d,%d,%d,%d", __func__, area.x, area.y, area.width, area.height); MosesOverviewPrivate* priv = self->priv; GPtrArray* clones = priv->clones; MetaRectangle bounds = {area.x, area.y, area.width, area.height}; int direction = 0; int* directions = g_malloc(sizeof(int)*clones->len); MetaRectangle* rects = g_malloc(sizeof(MetaRectangle)*clones->len); for (int i = 0; i < clones->len; i++) { // save rectangles into 4-dimensional arrays representing two corners of the rectangular: [left_x, top_y, right_x, bottom_y] MetaRectangle rect; ClutterActor* clone = g_ptr_array_index(clones, i); MetaWindowActor* actor = META_WINDOW_ACTOR(clutter_clone_get_source(CLUTTER_CLONE(clone))); MetaWindow* win = meta_window_actor_get_meta_window(actor); meta_window_get_frame_rect(win, &rect); rect = rect_adjusted(rect, -GAPS, -GAPS, GAPS, GAPS); rects[i] = rect; g_debug("%s: frame: %d,%d,%d,%d", __func__, rect.x, rect.y, rect.width, rect.height); meta_rectangle_union(&bounds, &rect, &bounds); // This is used when the window is on the edge of the screen to try to use as much screen real estate as possible. directions[i] = direction; direction++; if (direction == 4) direction = 0; } int loop_counter = 0; gboolean overlap = FALSE; do { overlap = FALSE; for (int i = 0; i < clones->len; i++) { for (int j = 0; j < clones->len; j++) { if (i == j) continue; MetaRectangle rect = rects[i]; MetaRectangle comp = rects[j]; if (!meta_rectangle_overlap(&rect, &comp)) continue; loop_counter ++; overlap = TRUE; // Determine pushing direction GdkPoint i_center = rect_center (rect); GdkPoint j_center = rect_center (comp); GdkPoint diff = {j_center.x - i_center.x, j_center.y - i_center.y}; // Prevent dividing by zero and non-movement if (diff.x == 0 && diff.y == 0) diff.x = 1; // Approximate a vector of between 10px and 20px in magnitude in the same direction float length = sqrtf (diff.x * diff.x + diff.y * diff.y); diff.x = (int)floorf (diff.x * ACCURACY / length); diff.y = (int)floorf (diff.y * ACCURACY / length); // Move both windows apart rect.x += -diff.x; rect.y += -diff.y; comp.x += diff.x; comp.y += diff.y; // Try to keep the bounding rect the same aspect as the screen so that more // screen real estate is utilised. We do this by splitting the screen into nine // equal sections, if the window center is in any of the corner sections pull the // window towards the outer corner. If it is in any of the other edge sections // alternate between each corner on that edge. We don't want to determine it // randomly as it will not produce consistant locations when using the filter. // Only move one window so we don't cause large amounts of unnecessary zooming // in some situations. We need to do this even when expanding later just in case // all windows are the same size. // (We are using an old bounding rect for this, hopefully it doesn't matter) int x_section = (int)roundf ((rect.x - bounds.x) / (bounds.width / 3.0f)); int y_section = (int)roundf ((comp.y - bounds.y) / (bounds.height / 3.0f)); i_center = rect_center (rect); diff.x = 0; diff.y = 0; if (x_section != 1 || y_section != 1) { // Remove this if you want the center to pull as well if (x_section == 1) x_section = (directions[i] / 2 == 1 ? 2 : 0); if (y_section == 1) y_section = (directions[i] % 2 == 1 ? 2 : 0); } if (x_section == 0 && y_section == 0) { diff.x = bounds.x - i_center.x; diff.y = bounds.y - i_center.y; } if (x_section == 2 && y_section == 0) { diff.x = bounds.x + bounds.width - i_center.x; diff.y = bounds.y - i_center.y; } if (x_section == 2 && y_section == 2) { diff.x = bounds.x + bounds.width - i_center.x; diff.y = bounds.y + bounds.height - i_center.y; } if (x_section == 0 && y_section == 2) { diff.x = bounds.x - i_center.x; diff.y = bounds.y + bounds.height - i_center.y; } if (diff.x != 0 || diff.y != 0) { length = sqrtf (diff.x * diff.x + diff.y * diff.y); diff.x *= (int)floorf (ACCURACY / length / 2.0f); diff.y *= (int)floorf (ACCURACY / length / 2.0f); rect.x += diff.x; rect.y += diff.y; } // Update bounding rect meta_rectangle_union(&bounds, &rect, &bounds); meta_rectangle_union(&bounds, &comp, &bounds); //we took copies from the rects from our list so we need to reassign them rects[i] = rect; rects[j] = comp; } } } while (overlap && loop_counter < MAX_TRANSLATIONS); // Work out scaling by getting the most top-left and most bottom-right window coords. float scale = fminf (fminf (area.width / (float)bounds.width, area.height / (float)bounds.height), 1.0f); // Make bounding rect fill the screen size for later steps bounds.x = (int)floorf (bounds.x - (area.width - bounds.width * scale) / 2); bounds.y = (int)floorf (bounds.y - (area.height - bounds.height * scale) / 2); bounds.width = (int)floorf (area.width / scale); bounds.height = (int)floorf (area.height / scale); // Move all windows back onto the screen and set their scale int index = 0; for (; index < clones->len; index++) { MetaRectangle rect = rects[index]; rects[index] = (MetaRectangle){ (int)floorf ((rect.x - bounds.x) * scale + area.x), (int)floorf ((rect.y - bounds.y) * scale + area.y), (int)floorf (rect.width * scale), (int)floorf (rect.height * scale) }; } // fill gaps by enlarging windows gboolean moved = FALSE; MetaRectangle border = area; do { moved = FALSE; index = 0; for (; index < clones->len; index++) { MetaRectangle rect = rects[index]; int width_diff = ACCURACY; int height_diff = (int)floorf ((((rect.width + width_diff) - rect.height) / (float)rect.width) * rect.height); int x_diff = width_diff / 2; int y_diff = height_diff / 2; //top right MetaRectangle old = rect; rect = (MetaRectangle){ rect.x + x_diff, rect.y - y_diff - height_diff, rect.width + width_diff, rect.height + width_diff }; if (rect_is_overlapping_any (rect, rects, clones->len, border)) rect = old; else moved = TRUE; //bottom right old = rect; rect = (MetaRectangle){rect.x + x_diff, rect.y + y_diff, rect.width + width_diff, rect.height + width_diff}; if (rect_is_overlapping_any (rect, rects, clones->len, border)) rect = old; else moved = TRUE; //bottom left old = rect; rect = (MetaRectangle){rect.x - x_diff, rect.y + y_diff, rect.width + width_diff, rect.height + width_diff}; if (rect_is_overlapping_any (rect, rects, clones->len, border)) rect = old; else moved = TRUE; //top left old = rect; rect = (MetaRectangle){rect.x - x_diff, rect.y - y_diff - height_diff, rect.width + width_diff, rect.height + width_diff}; if (rect_is_overlapping_any (rect, rects, clones->len, border)) rect = old; else moved = TRUE; rects[index] = rect; } } while (moved); index = 0; for (; index < clones->len; index++) { MetaRectangle rect = rects[index]; ClutterActor* clone = g_ptr_array_index(clones, index); MetaWindowActor* actor = META_WINDOW_ACTOR(clutter_clone_get_source(CLUTTER_CLONE(clone))); MetaWindow* window = meta_window_actor_get_meta_window(actor); MetaRectangle window_rect; meta_window_get_frame_rect(window, &window_rect); rect = rect_adjusted(rect, GAPS, GAPS, -GAPS, -GAPS); scale = rect.width / (float)window_rect.width; if (scale > 2.0 || (scale > 1.0 && (window_rect.width > 300 || window_rect.height > 300))) { scale = (window_rect.width > 300 || window_rect.height > 300) ? 1.0f : 2.0f; rect = (MetaRectangle){rect_center (rect).x - (int)floorf (window_rect.width * scale) / 2, rect_center (rect).y - (int)floorf (window_rect.height * scale) / 2, (int)floorf (window_rect.width * scale), (int)floorf (window_rect.height * scale)}; } place_window(self, clone, rect); } g_free(directions); g_free(rects); }