void mnb_zones_preview_add_window (MnbZonesPreview *preview, MetaWindow *window) { ClutterActor *clone; ClutterActor *group; MetaRectangle rect; MetaWorkspace *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_get_workspace (window); group = mnb_zones_preview_get_workspace_group (preview, meta_workspace_index(workspace)); clone = clutter_clone_new (CLUTTER_ACTOR(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_outer_rect (window, &rect); clutter_actor_set_position (clone, rect.x, rect.y); clutter_actor_add_child (CLUTTER_ACTOR (group), clone); }
//borrowed from gtk static gint get_monitor_at_window(MetaWindow* window) { gint num_monitors, i, area = 0, screen_num = -1; GdkRectangle win_rect; GdkScreen* screen = gdk_screen_get_default(); meta_window_get_outer_rect(window, (MetaRectangle*)&win_rect); num_monitors = gdk_screen_get_n_monitors (screen); for (i=0; i<num_monitors; i++) { GdkRectangle tmp_monitor, intersect; gdk_screen_get_monitor_geometry (screen, i, &tmp_monitor); gdk_rectangle_intersect (&win_rect, &tmp_monitor, &intersect); if (intersect.width * intersect.height > area) { area = intersect.width * intersect.height; screen_num = i; } } if (screen_num >= 0) return screen_num; else return get_nearest_monitor (screen, win_rect.x + win_rect.width / 2, win_rect.y + win_rect.height / 2); }
static MetaDeepinClonedWidget* _clone_window(DeepinWorkspaceOverview* self, MetaWindow* window) { DeepinWorkspaceOverviewPrivate* priv = self->priv; GtkWidget* widget = meta_deepin_cloned_widget_new(window, TRUE); gtk_widget_set_sensitive(widget, TRUE); ClonedPrivateInfo* info = clone_get_info(widget); info->monitor = get_monitor_at_window(window); g_assert(info->monitor < priv->monitors->len); MonitorData* md = g_ptr_array_index(priv->monitors, info->monitor); g_ptr_array_add(md->clones, widget); MetaRectangle r; meta_window_get_outer_rect(window, &r); meta_deepin_cloned_widget_set_size( META_DEEPIN_CLONED_WIDGET(widget), r.width, r.height); meta_deepin_cloned_widget_set_render_frame( META_DEEPIN_CLONED_WIDGET(widget), TRUE); //it doesn't matter where we put it, since we'll move it very soon. deepin_fixed_put(DEEPIN_FIXED(self), widget, 0, 0); g_object_connect(G_OBJECT(widget), "signal::enter-notify-event", on_deepin_cloned_widget_entered, self, "signal::leave-notify-event", on_deepin_cloned_widget_leaved, self, "signal::motion-notify-event", on_deepin_cloned_widget_motion, self, "signal::button-release-event", on_deepin_cloned_widget_released, self, NULL); return META_DEEPIN_CLONED_WIDGET(widget); }
/** * shell_global_get_focus_monitor: * @global: the #ShellGlobal * * Gets the bounding box of the monitor containing the window that * currently contains the keyboard focus. * * Return value: the bounding box of the focus monitor */ GdkRectangle * shell_global_get_focus_monitor (ShellGlobal *global) { MetaScreen *screen = shell_global_get_screen (global); MetaDisplay *display = meta_screen_get_display (screen); MetaWindow *focus = meta_display_get_focus_window (display); MetaRectangle rect, wrect; int nmonitors, i; if (focus) { meta_window_get_outer_rect (focus, &wrect); nmonitors = meta_screen_get_n_monitors (screen); /* Find the monitor that the top-left corner of @focus is on. */ for (i = 0; i < nmonitors; i++) { meta_screen_get_monitor_geometry (screen, i, &rect); if (rect.x <= wrect.x && rect.y <= wrect.y && rect.x + rect.width > wrect.x && rect.y + rect.height > wrect.y) return g_boxed_copy (GDK_TYPE_RECTANGLE, &rect); } } meta_screen_get_monitor_geometry (screen, 0, &rect); return g_boxed_copy (GDK_TYPE_RECTANGLE, &rect); }
static gboolean window_contains_point (MetaWindow *window, int root_x, int root_y) { MetaRectangle rect; meta_window_get_outer_rect (window, &rect); return POINT_IN_RECT (root_x, root_y, rect); }
static gboolean rectangle_overlaps_some_window (MetaRectangle *rect, GList *windows) { GList *tmp; MetaRectangle dest; tmp = windows; while (tmp != NULL) { MetaWindow *other = tmp->data; MetaRectangle other_rect; switch (other->type) { case META_WINDOW_DOCK: case META_WINDOW_SPLASHSCREEN: case META_WINDOW_DESKTOP: case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: /* override redirect window types: */ case META_WINDOW_DROPDOWN_MENU: case META_WINDOW_POPUP_MENU: case META_WINDOW_TOOLTIP: case META_WINDOW_NOTIFICATION: case META_WINDOW_COMBO: case META_WINDOW_DND: case META_WINDOW_OVERRIDE_OTHER: break; case META_WINDOW_NORMAL: case META_WINDOW_UTILITY: case META_WINDOW_TOOLBAR: case META_WINDOW_MENU: if (!meta_window_is_override_redirect(other)) { meta_window_get_outer_rect (other, &other_rect); if (meta_rectangle_intersect (rect, &other_rect, &dest)) return TRUE; } break; } tmp = tmp->next; } return FALSE; }
static gboolean rectangle_overlaps_some_window (MetaRectangle *rect, GList *windows) { GList *tmp; MetaRectangle dest; tmp = windows; while (tmp != NULL) { MetaWindow *other = tmp->data; MetaRectangle other_rect; switch (other->type) { case META_WINDOW_DOCK: case META_WINDOW_SPLASHSCREEN: case META_WINDOW_DESKTOP: case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: break; case META_WINDOW_NORMAL: case META_WINDOW_UTILITY: case META_WINDOW_TOOLBAR: case META_WINDOW_MENU: meta_window_get_outer_rect (other, &other_rect); if (meta_rectangle_intersect (rect, &other_rect, &dest)) return TRUE; break; default: break; } tmp = tmp->next; } return FALSE; }
/* * Shapes the cow so that the given window is exposed, * when metaWindow is NULL it clears the shape again */ static void meta_shape_cow_for_window (MetaScreen *screen, MetaWindow *metaWindow) { MetaCompScreen *info = meta_screen_get_compositor_data (screen); Display *xdisplay = meta_display_get_xdisplay (meta_screen_get_display (screen)); if (metaWindow == NULL) XFixesSetWindowShapeRegion (xdisplay, info->output, ShapeBounding, 0, 0, None); else { XserverRegion output_region; XRectangle screen_rect, window_bounds; int width, height; MetaRectangle rect; meta_window_get_outer_rect (metaWindow, &rect); window_bounds.x = rect.x; window_bounds.y = rect.y; window_bounds.width = rect.width; window_bounds.height = rect.height; meta_screen_get_size (screen, &width, &height); screen_rect.x = 0; screen_rect.y = 0; screen_rect.width = width; screen_rect.height = height; output_region = XFixesCreateRegion (xdisplay, &window_bounds, 1); XFixesInvertRegion (xdisplay, output_region, &screen_rect, output_region); XFixesSetWindowShapeRegion (xdisplay, info->output, ShapeBounding, 0, 0, output_region); XFixesDestroyRegion (xdisplay, output_region); } }
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 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); }
//TODO: response to screen-changed? void deepin_workspace_overview_populate(DeepinWorkspaceOverview* self, MetaWorkspace* ws) { DeepinWorkspaceOverviewPrivate* priv = self->priv; priv->workspace = ws; GdkScreen* screen = gdk_screen_get_default(); priv->primary = gdk_screen_get_primary_monitor(screen); gint n_monitors = gdk_screen_get_n_monitors(screen); priv->monitors = g_ptr_array_new_full(n_monitors, monitor_data_destroy); for (int i = 0; i < n_monitors; i++) { MonitorData* md = monitor_data_new(); g_ptr_array_add(priv->monitors, md); md->monitor = i; md->clones = g_ptr_array_new(); gdk_screen_get_monitor_geometry(screen, i, (GdkRectangle*)&md->mon_rect); /** * gdk_screen_get_monitor_workarea fails to honor struts, so I have to use xinerama * to get correct workarea */ const MetaXineramaScreenInfo* xinerama = meta_screen_get_xinerama_for_rect(ws->screen, &md->mon_rect); meta_workspace_get_work_area_for_xinerama(ws, xinerama->number, (GdkRectangle*)&md->mon_workarea); } GList* ls = meta_stack_list_windows(ws->screen->stack, priv->all_window_mode? NULL: ws); GList* l = ls; while (l) { MetaWindow* win = (MetaWindow*)l->data; if (win->type == META_WINDOW_NORMAL) { if (priv->present_xids && g_hash_table_size(priv->present_xids)) { if (g_hash_table_contains(priv->present_xids, GINT_TO_POINTER(win->xwindow))) { _clone_window(self, win); } } else { _clone_window(self, win); } } l = l->next; } g_list_free(ls); { priv->close_button = gtk_event_box_new(); gtk_event_box_set_above_child(GTK_EVENT_BOX(priv->close_button), FALSE); gtk_event_box_set_visible_window(GTK_EVENT_BOX(priv->close_button), FALSE); GtkWidget* image = gtk_image_new_from_file(METACITY_PKGDATADIR "/close.png"); gtk_container_add(GTK_CONTAINER(priv->close_button), image); deepin_fixed_put(DEEPIN_FIXED(self), priv->close_button, 0, 0); gtk_widget_set_opacity(self->priv->close_button, 0.0); g_object_connect(G_OBJECT(priv->close_button), "signal::leave-notify-event", on_close_button_leaved, self, "signal::button-release-event", on_close_button_clicked, self, NULL); } priv->dock_height = 0; MetaWindow *desktop_win = NULL, *dock_win = NULL; GList* windows = priv->workspace->mru_list; while (windows != NULL) { MetaWindow *w = (MetaWindow*)windows->data; if (w->type == META_WINDOW_DESKTOP) { desktop_win = w; } if (w->type == META_WINDOW_DOCK) { dock_win = w; } if (desktop_win && dock_win) break; windows = windows->next; } for (int i = 0; i < n_monitors; i++) { MonitorData* md = (MonitorData*)g_ptr_array_index(priv->monitors, i); if (i == priv->primary) { MetaRectangle r1 = {0, 0, 0, 0}, r2 = {0, 0, 0, 0}; cairo_surface_t* aux1 = NULL, *aux2 = NULL; if (desktop_win) { meta_window_get_outer_rect(desktop_win, &r1); aux1 = deepin_window_surface_manager_get_surface(desktop_win, 1.0); r1.x -= md->mon_rect.x; r1.y -= md->mon_rect.y; meta_verbose ("%s: desktop offset(%d, %d)\n", __func__, r1.x, r1.y); } if (dock_win) { meta_window_get_outer_rect(dock_win, &r2); aux2 = deepin_window_surface_manager_get_surface(dock_win, 1.0); priv->dock_height = r2.height; r2.x -= md->mon_rect.x; r2.y -= md->mon_rect.y; meta_verbose ("%s: dock offset(%d, %d)\n", __func__, r2.x, r2.y); } md->desktop_surface = deepin_window_surface_manager_get_combined3( deepin_background_cache_get_surface(md->monitor, 1.0), aux1, r1.x, r1.y, aux2, r2.x, r2.y, 1.0); } else { md->desktop_surface = deepin_background_cache_get_surface(md->monitor, 1.0); cairo_surface_reference(md->desktop_surface); } } gtk_widget_queue_resize(GTK_WIDGET(self)); }
/** * shell_screenshot_screenshot_window: * @screenshot: the #ShellScreenshot * @include_frame: Whether to include the frame or not * @include_cursor: Whether to include the cursor or not * @filename: The filename for the screenshot * @callback: (scope async): function to call returning success or failure * of the async grabbing * * Takes a screenshot of the focused window (optionally omitting the frame) * in @filename as png image. * */ void shell_screenshot_screenshot_window (ShellScreenshot *screenshot, gboolean include_frame, gboolean include_cursor, const char *filename, ShellScreenshotCallback callback) { GSimpleAsyncResult *result; GSettings *settings; _screenshot_data *screenshot_data = g_new0 (_screenshot_data, 1); MetaScreen *screen = shell_global_get_screen (screenshot->global); MetaCursorTracker *tracker; MetaDisplay *display = meta_screen_get_display (screen); MetaWindow *window = meta_display_get_focus_window (display); ClutterActor *window_actor; gfloat actor_x, actor_y; MetaShapedTexture *stex; MetaRectangle rect; cairo_rectangle_int_t clip; screenshot_data->screenshot = g_object_ref (screenshot); screenshot_data->filename = g_strdup (filename); screenshot_data->callback = callback; if (!window) { screenshot_data->filename_used = g_strdup (""); result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, shell_screenshot_screenshot_window); g_simple_async_result_set_op_res_gboolean (result, FALSE); g_simple_async_result_complete (result); g_object_unref (result); return; } window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window)); clutter_actor_get_position (window_actor, &actor_x, &actor_y); if (include_frame || !meta_window_get_frame (window)) { meta_window_get_outer_rect (window, &rect); screenshot_data->screenshot_area.x = rect.x; screenshot_data->screenshot_area.y = rect.y; clip.x = rect.x - (gint) actor_x; clip.y = rect.y - (gint) actor_y; } else { rect = *meta_window_get_rect (window); screenshot_data->screenshot_area.x = (gint) actor_x + rect.x; screenshot_data->screenshot_area.y = (gint) actor_y + rect.y; clip.x = rect.x; clip.y = rect.y; } clip.width = screenshot_data->screenshot_area.width = rect.width; clip.height = screenshot_data->screenshot_area.height = rect.height; stex = META_SHAPED_TEXTURE (meta_window_actor_get_texture (META_WINDOW_ACTOR (window_actor))); screenshot_data->image = meta_shaped_texture_get_image (stex, &clip); settings = g_settings_new (A11Y_APPS_SCHEMA); if (include_cursor && !g_settings_get_boolean (settings, MAGNIFIER_ACTIVE_KEY)) { tracker = meta_cursor_tracker_get_for_screen (screen); _draw_cursor_image (tracker, screenshot_data->image, screenshot_data->screenshot_area); } g_object_unref (settings); result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, shell_screenshot_screenshot_window); g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); g_object_unref (result); }
void meta_window_place (MetaWindow *window, MetaFrameGeometry *fgeom, int x, int y, int *new_x, int *new_y) { GList *windows; const MetaXineramaScreenInfo *xi; /* frame member variables should NEVER be used in here, only * MetaFrameGeometry. But remember fgeom == NULL * for undecorated windows. Also, this function should * NEVER have side effects other than computing the * placement coordinates. */ meta_topic (META_DEBUG_PLACEMENT, "Placing window %s\n", window->desc); windows = NULL; switch (window->type) { /* Run placement algorithm on these. */ case META_WINDOW_NORMAL: case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: case META_WINDOW_SPLASHSCREEN: break; /* Assume the app knows best how to place these, no placement * algorithm ever (other than "leave them as-is") */ case META_WINDOW_DESKTOP: case META_WINDOW_DOCK: case META_WINDOW_TOOLBAR: case META_WINDOW_MENU: case META_WINDOW_UTILITY: goto done_no_constraints; } if (meta_prefs_get_disable_workarounds ()) { switch (window->type) { /* Only accept USPosition on normal windows because the app is full * of shit claiming the user set -geometry for a dialog or dock */ case META_WINDOW_NORMAL: if (window->size_hints.flags & USPosition) { /* don't constrain with placement algorithm */ meta_topic (META_DEBUG_PLACEMENT, "Honoring USPosition for %s instead of using placement algorithm\n", window->desc); goto done; } break; /* Ignore even USPosition on dialogs, splashscreen */ case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: case META_WINDOW_SPLASHSCREEN: break; /* Assume the app knows best how to place these. */ case META_WINDOW_DESKTOP: case META_WINDOW_DOCK: case META_WINDOW_TOOLBAR: case META_WINDOW_MENU: case META_WINDOW_UTILITY: if (window->size_hints.flags & PPosition) { meta_topic (META_DEBUG_PLACEMENT, "Not placing non-normal non-dialog window with PPosition set\n"); goto done_no_constraints; } break; } } else { /* workarounds enabled */ if ((window->size_hints.flags & PPosition) || (window->size_hints.flags & USPosition)) { meta_topic (META_DEBUG_PLACEMENT, "Not placing window with PPosition or USPosition set\n"); avoid_being_obscured_as_second_modal_dialog (window, fgeom, &x, &y); goto done_no_constraints; } } if ((window->type == META_WINDOW_DIALOG || window->type == META_WINDOW_MODAL_DIALOG) && window->xtransient_for != None) { /* Center horizontally, at top of parent vertically */ MetaWindow *parent; parent = meta_display_lookup_x_window (window->display, window->xtransient_for); if (parent) { int w; meta_window_get_position (parent, &x, &y); w = parent->rect.width; /* center of parent */ x = x + w / 2; /* center of child over center of parent */ x -= window->rect.width / 2; /* "visually" center window over parent, leaving twice as * much space below as on top. */ y += (parent->rect.height - window->rect.height)/3; /* put top of child's frame, not top of child's client */ if (fgeom) y += fgeom->top_height; meta_topic (META_DEBUG_PLACEMENT, "Centered window %s over transient parent\n", window->desc); avoid_being_obscured_as_second_modal_dialog (window, fgeom, &x, &y); goto done; } } /* FIXME UTILITY with transient set should be stacked up * on the sides of the parent window or something. */ if (window->type == META_WINDOW_DIALOG || window->type == META_WINDOW_MODAL_DIALOG || window->type == META_WINDOW_SPLASHSCREEN) { /* Center on current xinerama (i.e. on current monitor) */ int w, h; /* Warning, this function is a round trip! */ xi = meta_screen_get_current_xinerama (window->screen); w = xi->rect.width; h = xi->rect.height; x = (w - window->rect.width) / 2; y = (h - window->rect.height) / 2; x += xi->rect.x; y += xi->rect.y; meta_topic (META_DEBUG_PLACEMENT, "Centered window %s on screen %d xinerama %d\n", window->desc, window->screen->number, xi->number); goto done_check_denied_focus; } /* Find windows that matter (not minimized, on same workspace * as placed window, may be shaded - if shaded we pretend it isn't * for placement purposes) */ { GSList *all_windows; GSList *tmp; all_windows = meta_display_list_windows (window->display); tmp = all_windows; while (tmp != NULL) { MetaWindow *w = tmp->data; if (meta_window_showing_on_its_workspace (w) && w != window && (window->workspace == w->workspace || window->on_all_workspaces || w->on_all_workspaces)) windows = g_list_prepend (windows, w); tmp = tmp->next; } g_slist_free (all_windows); } /* Warning, this is a round trip! */ xi = meta_screen_get_current_xinerama (window->screen); /* "Origin" placement algorithm */ x = xi->rect.x; y = xi->rect.y; if (find_first_fit (window, fgeom, windows, xi->number, x, y, &x, &y)) goto done_check_denied_focus; /* Maximize windows if they are too big for their work area (bit of * a hack here). Assume undecorated windows probably don't intend to * be maximized. */ if (window->has_maximize_func && window->decorated && !window->fullscreen) { MetaRectangle workarea; MetaRectangle outer; meta_window_get_work_area_for_xinerama (window, xi->number, &workarea); meta_window_get_outer_rect (window, &outer); /* If the window is bigger than the screen, then automaximize. Do NOT * auto-maximize the directions independently. See #419810. */ if (outer.width >= workarea.width && outer.height >= workarea.height) { window->maximize_horizontally_after_placement = TRUE; window->maximize_vertically_after_placement = TRUE; } } /* If no placement has been done, revert to cascade to avoid * fully overlapping window (e.g. starting multiple terminals) * */ if (!meta_prefs_get_center_new_windows() && (x == xi->rect.x && y == xi->rect.y)) find_next_cascade (window, fgeom, windows, x, y, &x, &y); done_check_denied_focus: /* If the window is being denied focus and isn't a transient of the * focus window, we do NOT want it to overlap with the focus window * if at all possible. This is guaranteed to only be called if the * focus_window is non-NULL, and we try to avoid that window. */ if (window->denied_focus_and_not_transient) { gboolean found_fit; MetaWindow *focus_window; MetaRectangle overlap; focus_window = window->display->focus_window; g_assert (focus_window != NULL); /* No need to do anything if the window doesn't overlap at all */ found_fit = !meta_rectangle_intersect (&window->rect, &focus_window->rect, &overlap); /* Try to do a first fit again, this time only taking into account the * focus window. */ if (!meta_prefs_get_center_new_windows() && !found_fit) { GList *focus_window_list; focus_window_list = g_list_prepend (NULL, focus_window); /* Reset x and y ("origin" placement algorithm) */ x = xi->rect.x; y = xi->rect.y; found_fit = find_first_fit (window, fgeom, focus_window_list, xi->number, x, y, &x, &y); g_list_free (focus_window_list); } /* If that still didn't work, just place it where we can see as much * as possible. */ if (!found_fit) find_most_freespace (window, fgeom, focus_window, x, y, &x, &y); } done: g_list_free (windows); done_no_constraints: *new_x = x; *new_y = y; }
/* Find the leftmost, then topmost, empty area on the workspace * that can contain the new window. * * Cool feature to have: if we can't fit the current window size, * try shrinking the window (within geometry constraints). But * beware windows such as Emacs with no sane minimum size, we * don't want to create a 1x1 Emacs. */ static gboolean find_first_fit (MetaWindow *window, MetaFrameGeometry *fgeom, /* visible windows on relevant workspaces */ GList *windows, int xinerama, int x, int y, int *new_x, int *new_y) { /* This algorithm is limited - it just brute-force tries * to fit the window in a small number of locations that are aligned * with existing windows. It tries to place the window on * the bottom of each existing window, and then to the right * of each existing window, aligned with the left/top of the * existing window in each of those cases. */ int retval; GList *below_sorted; GList *right_sorted; GList *tmp; MetaRectangle rect; MetaRectangle work_area; retval = FALSE; /* Below each window */ below_sorted = g_list_copy (windows); below_sorted = g_list_sort (below_sorted, leftmost_cmp); below_sorted = g_list_sort (below_sorted, topmost_cmp); /* To the right of each window */ right_sorted = g_list_copy (windows); right_sorted = g_list_sort (right_sorted, topmost_cmp); right_sorted = g_list_sort (right_sorted, leftmost_cmp); rect.width = window->rect.width; rect.height = window->rect.height; if (fgeom) { rect.width += fgeom->left_width + fgeom->right_width; rect.height += fgeom->top_height + fgeom->bottom_height; } #ifdef WITH_VERBOSE_MODE { char xinerama_location_string[RECT_LENGTH]; meta_rectangle_to_string (&window->screen->xinerama_infos[xinerama].rect, xinerama_location_string); meta_topic (META_DEBUG_XINERAMA, "Natural xinerama is %s\n", xinerama_location_string); } #endif meta_window_get_work_area_for_xinerama (window, xinerama, &work_area); if (meta_prefs_get_center_new_windows ()) center_rect_in_area (&rect, &work_area); else center_tile_rect_in_area (&rect, &work_area); if (meta_rectangle_contains_rect (&work_area, &rect) && (meta_prefs_get_center_new_windows () || !rectangle_overlaps_some_window (&rect, windows))) { *new_x = rect.x; *new_y = rect.y; if (fgeom) { *new_x += fgeom->left_width; *new_y += fgeom->top_height; } retval = TRUE; goto out; } /* try below each window */ tmp = below_sorted; while (tmp != NULL) { MetaWindow *w = tmp->data; MetaRectangle outer_rect; meta_window_get_outer_rect (w, &outer_rect); rect.x = outer_rect.x; rect.y = outer_rect.y + outer_rect.height; if (meta_rectangle_contains_rect (&work_area, &rect) && !rectangle_overlaps_some_window (&rect, below_sorted)) { *new_x = rect.x; *new_y = rect.y; if (fgeom) { *new_x += fgeom->left_width; *new_y += fgeom->top_height; } retval = TRUE; goto out; } tmp = tmp->next; } /* try to the right of each window */ tmp = right_sorted; while (tmp != NULL) { MetaWindow *w = tmp->data; MetaRectangle outer_rect; meta_window_get_outer_rect (w, &outer_rect); rect.x = outer_rect.x + outer_rect.width; rect.y = outer_rect.y; if (meta_rectangle_contains_rect (&work_area, &rect) && !rectangle_overlaps_some_window (&rect, right_sorted)) { *new_x = rect.x; *new_y = rect.y; if (fgeom) { *new_x += fgeom->left_width; *new_y += fgeom->top_height; } retval = TRUE; goto out; } tmp = tmp->next; } out: g_list_free (below_sorted); g_list_free (right_sorted); return retval; }
static void find_most_freespace (MetaWindow *window, MetaFrameGeometry *fgeom, /* visible windows on relevant workspaces */ MetaWindow *focus_window, int x, int y, int *new_x, int *new_y) { MetaWindowDirection side; int max_area; int max_width, max_height, left, right, top, bottom; int left_space, right_space, top_space, bottom_space; int frame_size_left, frame_size_top; MetaRectangle work_area; MetaRectangle avoid; MetaRectangle outer; frame_size_left = fgeom ? fgeom->left_width : 0; frame_size_top = fgeom ? fgeom->top_height : 0; meta_window_get_work_area_current_xinerama (focus_window, &work_area); meta_window_get_outer_rect (focus_window, &avoid); meta_window_get_outer_rect (window, &outer); /* Find the areas of choosing the various sides of the focus window */ max_width = MIN (avoid.width, outer.width); max_height = MIN (avoid.height, outer.height); left_space = avoid.x - work_area.x; right_space = work_area.width - (avoid.x + avoid.width - work_area.x); top_space = avoid.y - work_area.y; bottom_space = work_area.height - (avoid.y + avoid.height - work_area.y); left = MIN (left_space, outer.width); right = MIN (right_space, outer.width); top = MIN (top_space, outer.height); bottom = MIN (bottom_space, outer.height); /* Find out which side of the focus_window can show the most of the window */ side = META_LEFT; max_area = left*max_height; if (right*max_height > max_area) { side = META_RIGHT; max_area = right*max_height; } if (top*max_width > max_area) { side = META_TOP; max_area = top*max_width; } if (bottom*max_width > max_area) { side = META_BOTTOM; max_area = bottom*max_width; } /* Give up if there's no where to put it (i.e. focus window is maximized) */ if (max_area == 0) return; /* Place the window on the relevant side; if the whole window fits, * make it adjacent to the focus window; if not, make sure the * window doesn't go off the edge of the screen. */ switch (side) { case META_LEFT: *new_y = avoid.y + frame_size_top; if (left_space > outer.width) *new_x = avoid.x - outer.width + frame_size_left; else *new_x = work_area.x + frame_size_left; break; case META_RIGHT: *new_y = avoid.y + frame_size_top; if (right_space > outer.width) *new_x = avoid.x + avoid.width + frame_size_left; else *new_x = work_area.x + work_area.width - outer.width + frame_size_left; break; case META_TOP: *new_x = avoid.x + frame_size_left; if (top_space > outer.height) *new_y = avoid.y - outer.height + frame_size_top; else *new_y = work_area.y + frame_size_top; break; case META_BOTTOM: *new_x = avoid.x + frame_size_left; if (bottom_space > outer.height) *new_y = avoid.y + avoid.height + frame_size_top; else *new_y = work_area.y + work_area.height - outer.height + frame_size_top; break; } }