diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index 1576ce551..3b24a5e35 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -641,8 +641,13 @@ meta_compositor_switch_workspace (MetaCompositor *compositor, meta_compositor_get_instance_private (compositor); gint to_indx, from_indx; - to_indx = meta_workspace_index (to); - from_indx = meta_workspace_index (from); + if (direction == META_MOTION_CONTEXT_SWITCH) { + to_indx = meta_workspace_get_id (to); + from_indx = meta_workspace_get_id (from); + } else { + to_indx = meta_workspace_index (to); + from_indx = meta_workspace_index (from); + } priv->switch_workspace_in_progress++; diff --git a/src/core/meta-workspace-manager-private.h b/src/core/meta-workspace-manager-private.h index 261c4d47c..4b5d4f4e3 100644 --- a/src/core/meta-workspace-manager-private.h +++ b/src/core/meta-workspace-manager-private.h @@ -30,6 +30,36 @@ #include "meta/types.h" #include "meta/meta-workspace-manager.h" +// +// Stores list of MetaWorkspaces and pointer to an active MetaWorkspace. When this context becomes +// the current context, the workspace fields are swapped into the corresponding fields in +// MetaWorkspaceManager. +// +// +struct _MetaWorkspaceContext +{ + + GObject parent; + // Link back to MetaWorkspaceManager + MetaWorkspaceManager *manager; + + // MetaWorkspace list belonging to this context. Copied to field with same name in + // MetaWorkspaceManager when this context is active. Any code which changes this list + // must make sure this context is not currently active in which case the list in + // MetaWorkspaceManager must be changed instead. + GList *workspaces; + + // Active MetaWorkspace for this context. Also copied to workspace manager upon activation. + // The rule above about not writing if context is currently active also applies to this field. + MetaWorkspace *active_workspace; + + // PID namespace + gchar *namespace; + + // A unique ID value for this context. + guint id; +}; + struct _MetaWorkspaceManager { GObject parent; @@ -37,8 +67,21 @@ struct _MetaWorkspaceManager MetaDisplay *display; MetaWorkspace *active_workspace; + GList *all_workspaces; GList *workspaces; + // List of WorkspaceContext + GList *context_list; + + gchar *mutter_namespace; + + // Current active WorkspaceContext. MetaWorkspaceManager state (workspaces, active_workspace) + // will be saved here when a new WorkspaceContext is made active. + MetaWorkspaceContext *active_context; + + // The next id value to allocate when creating a new WorkspaceContext + guint next_context_id; + int rows_of_workspaces; int columns_of_workspaces; MetaDisplayCorner starting_corner; @@ -93,4 +136,30 @@ void meta_workspace_manager_update_num_workspaces (MetaWorkspaceManager *workspa guint32 timestamp, int new_num); +MetaWorkspaceContext *meta_workspace_context_new (MetaWorkspaceManager *manager, const char *namespace); + +void meta_workspace_context_make_active (MetaWorkspaceContext *context); + +MetaWorkspaceContext *meta_workspace_manager_lookup_context (MetaWorkspaceManager *workspace_manager, + guint context_id); + + +void meta_workspace_manager_append_context_workspace (MetaWorkspaceManager *manager, + MetaWorkspace *workspace); + +void meta_workspace_manager_remove_context_workspace (MetaWorkspaceManager *manager, + MetaWorkspace *workspace); + +int meta_workspace_manager_context_workspace_index (MetaWorkspaceManager *workspace_manager, + MetaWorkspace *workspace); + +int meta_workspace_manager_get_workspace_id (MetaWorkspaceManager *workspace_manager, + MetaWorkspace *workspace); +MetaWorkspace * +meta_workspace_manager_lookup_workspace_by_id (MetaWorkspaceManager *workspace_manager, int workspace_id); + +gboolean +meta_workspace_manager_is_window_on_foreign_context (MetaWorkspaceManager *workspace_manager, MetaWindow *window); + + #endif /* META_WORKSPACE_MANAGER_PRIVATE_H */ diff --git a/src/core/meta-workspace-manager.c b/src/core/meta-workspace-manager.c index 0279ab23d..393ddf668 100644 --- a/src/core/meta-workspace-manager.c +++ b/src/core/meta-workspace-manager.c @@ -44,6 +44,9 @@ enum WORKSPACES_REORDERED, ACTIVE_WORKSPACE_CHANGED, SHOWING_DESKTOP_CHANGED, + CONTEXT_SWITCHED, + CONTEXT_WINDOW_MOVED, + CONTEXT_REMOVED, LAST_SIGNAL }; @@ -178,6 +181,29 @@ meta_workspace_manager_class_init (MetaWorkspaceManagerClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + workspace_manager_signals[CONTEXT_SWITCHED] = + g_signal_new ("context-switched", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + + workspace_manager_signals[CONTEXT_WINDOW_MOVED] = + g_signal_new("context-window-moved", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + META_TYPE_WINDOW); + + workspace_manager_signals[CONTEXT_REMOVED] = + g_signal_new("context-removed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_INT); + g_object_class_install_property (object_class, PROP_LAYOUT_COLUMNS, g_param_spec_int ("layout-columns", @@ -201,6 +227,7 @@ meta_workspace_manager_class_init (MetaWorkspaceManagerClass *klass) "Number of workspaces", 1, G_MAXINT, 1, G_PARAM_READABLE)); + } static void @@ -213,7 +240,7 @@ meta_workspace_manager_reload_work_areas (MetaWorkspaceManager *workspace_manage { GList *l; - for (l = workspace_manager->workspaces; l; l = l->next) + for (l = workspace_manager->all_workspaces; l; l = l->next) { MetaWorkspace *workspace = l->data; @@ -230,12 +257,24 @@ meta_workspace_manager_new (MetaDisplay *display) workspace_manager->display = display; workspace_manager->active_workspace = NULL; + workspace_manager->all_workspaces = NULL; workspace_manager->workspaces = NULL; workspace_manager->rows_of_workspaces = 1; workspace_manager->columns_of_workspaces = -1; workspace_manager->vertical_workspaces = FALSE; workspace_manager->starting_corner = META_DISPLAY_TOPLEFT; + workspace_manager->context_list = NULL; + workspace_manager->active_context = NULL; + workspace_manager->next_context_id = 1; + workspace_manager->mutter_namespace = meta_read_pid_namespace (getpid()); + + MetaWorkspaceContext *context = meta_workspace_context_new (workspace_manager, NULL); + + workspace_manager->workspaces = g_steal_pointer (&context->workspaces); + workspace_manager->active_workspace = g_steal_pointer (&context->active_workspace); + workspace_manager->active_context = context; + /* This is the default layout extracted from default * variable values in update_num_workspaces () * This can be overridden using _NET_DESKTOP_LAYOUT in @@ -246,11 +285,6 @@ meta_workspace_manager_new (MetaDisplay *display) 1, -1); - /* There must be at least one workspace at all times, - * so create that required workspace. - */ - meta_workspace_new (workspace_manager); - meta_workspace_manager_init_workspaces (workspace_manager); meta_prefs_add_listener (prefs_changed_callback, workspace_manager); @@ -301,6 +335,9 @@ MetaWorkspace * meta_workspace_manager_get_workspace_by_index (MetaWorkspaceManager *workspace_manager, int idx) { + if ((idx >> 16) & 0xFFFF) { + return meta_workspace_manager_lookup_workspace_by_id (workspace_manager, idx); + } return g_list_nth_data (workspace_manager->workspaces, idx); } @@ -1009,7 +1046,7 @@ meta_workspace_manager_unshow_desktop (MetaWorkspaceManager *workspace_manager) GList * meta_workspace_manager_get_workspaces (MetaWorkspaceManager *workspace_manager) { - return workspace_manager->workspaces; + return workspace_manager->all_workspaces; } int @@ -1023,6 +1060,17 @@ meta_workspace_manager_get_active_workspace_index (MetaWorkspaceManager *workspa return meta_workspace_index (active); } +int +meta_workspace_manager_get_active_workspace_id (MetaWorkspaceManager *workspace_manager) +{ + MetaWorkspace *active = workspace_manager->active_workspace; + + if (!active) + return -1; + + return meta_workspace_get_id (active); +} + /** * meta_workspace_manager_get_active_workspace: * @workspace_manager: A #MetaWorkspaceManager @@ -1041,9 +1089,15 @@ meta_workspace_manager_workspace_switched (MetaWorkspaceManager *workspace_manag int to, MetaMotionDirection direction) { - g_signal_emit (workspace_manager, - workspace_manager_signals[WORKSPACE_SWITCHED], 0, - from, to, direction); + if (direction == META_MOTION_CONTEXT_SWITCH) { + g_signal_emit (workspace_manager, + workspace_manager_signals[CONTEXT_SWITCHED], + 0, NULL); + } else { + g_signal_emit (workspace_manager, + workspace_manager_signals[WORKSPACE_SWITCHED], 0, + from, to, direction); + } } static void @@ -1066,3 +1120,379 @@ prefs_changed_callback (MetaPreference pref, timestamp, new_num); } } + + +/** + * meta_workspace_manager_set_builtin_struts_all: + * @workspace_manager: a #MetaWorkspaceManager + * @struts: (element-type Meta.Strut) (transfer none): list of #MetaStrut + * + * Sets a list of struts on every workspace that will be used in addition to the struts + * of the windows in the workspace when computing the work area of + * the workspace. + */ +void meta_workspace_manager_set_builtin_struts_all(MetaWorkspaceManager *workspace_manager, + GSList *struts) +{ + GList *context_iter = workspace_manager->context_list; + while (context_iter) { + MetaWorkspaceContext *context = context_iter->data; + for (GList *ws_iter = context->workspaces; ws_iter; ws_iter = ws_iter->next) { + MetaWorkspace *workspace = ws_iter->data; + meta_workspace_set_builtin_struts(workspace, struts); + } + context_iter = context_iter->next; + } +} + + + +/** + * meta_workspace_manager_lookup_context: + * @workspace_manager: a #MetaWorkspaceManager + * @context_id: id value of context to look up + * + * Look up WorkspaceContext by id and return it. If no context is found with + * the specified id the return value is %NULL. + * + * Return value: (transfer none) (nullable): the workspace context with the specified id + * or %NULL if no context with matching id exists. + */ +MetaWorkspaceContext * +meta_workspace_manager_lookup_context (MetaWorkspaceManager *workspace_manager, guint context_id) +{ + for (GList *iter = workspace_manager->context_list; iter; iter = iter->next) { + MetaWorkspaceContext *context = iter->data; + if (context_id == context->id) { + return context; + } + } + return NULL; +} + +const char * +meta_workspace_manager_mutter_namespace (MetaWorkspaceManager *workspace_manager) +{ + if (!workspace_manager->mutter_namespace) { + workspace_manager->mutter_namespace = meta_read_pid_namespace (getpid()); + } + return workspace_manager->mutter_namespace; +} + +/* + * Return pointer to the live workspace list depending on whether or not the context is currently active. + * While a workspace context is active, the list must be accessed through the pointer workspace_manager->workspaces + */ +GList ** +meta_workspace_manager_workspace_list_for_context (MetaWorkspaceManager *workspace_manager, guint context_id) +{ + if (workspace_manager->active_context && workspace_manager->active_context->id == context_id) { + return &workspace_manager->workspaces; + } + + MetaWorkspaceContext *context = meta_workspace_manager_lookup_context (workspace_manager, context_id); + + if (context) { + return &context->workspaces; + } else { + g_warning ("MetaWorkspaceManager: Failed to find context workspace list (context_id = %d)", context_id); + return NULL; + } +} + +void +meta_workspace_manager_append_context_workspace (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace) +{ + GList **p_workspaces = meta_workspace_manager_workspace_list_for_context (workspace_manager, workspace->context_id); + if (p_workspaces) { + *p_workspaces = g_list_append (*p_workspaces, workspace); + } +} + +void +meta_workspace_manager_remove_context_workspace (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace) +{ + GList **p_workspaces = meta_workspace_manager_workspace_list_for_context (workspace_manager, workspace->context_id); + if (p_workspaces) { + *p_workspaces = g_list_remove (*p_workspaces, workspace); + } + workspace_manager->all_workspaces = g_list_remove (workspace_manager->all_workspaces, workspace); +} + +static int +workspace_index_in_context (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace) +{ + GList **p_workspaces = meta_workspace_manager_workspace_list_for_context (workspace_manager, workspace->context_id); + if (p_workspaces) { + return g_list_index (*p_workspaces, workspace); + } else { + g_warning ("MetaWorkspaceManager: Could not find context for id=%d in workspace_index_in_context()", workspace->context_id); + return -1; + } +} + +int +meta_workspace_manager_get_workspace_id (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace) +{ + int idx = workspace_index_in_context (workspace_manager, workspace); + + if (idx >= 0) { + idx |= (int)(workspace->context_id << 16); + } else { + g_warning ("MetaWorkspaceManager: Workspace with context_id = %d not found in meta_workspace_manager_get_workspace_id()", workspace->context_id); + } + return idx; +} + +int +meta_workspace_manager_context_workspace_index (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace) +{ + + if (!(workspace_manager->active_context && workspace_manager->active_context->id == workspace->context_id)) { + guint active = (workspace_manager->active_context) ? (workspace_manager->active_context->id) : 0; + g_warning ("MetaWorkspaceManager: context_workspace_index() called on workspace not in active context. (ws->context_id = %d, active->context_id=%d)", + workspace->context_id, active); + } + + int idx = workspace_index_in_context (workspace_manager, workspace); + if (idx < 0) { + g_warning ("MetaWorkspaceManager: Failed to find workspace with context_id=%d in context_workspace_index()", + workspace->context_id); + } + return idx; +} + +MetaWorkspace * +meta_workspace_manager_lookup_workspace_by_id (MetaWorkspaceManager *workspace_manager, int workspace_id) +{ + uint context_id = (workspace_id >> 16) & 0xFFFF; + if (context_id) { + GList **p_workspaces = meta_workspace_manager_workspace_list_for_context (workspace_manager, context_id); + int idx = workspace_id & 0xFFFF; + if (p_workspaces) { + return g_list_nth_data (*p_workspaces, idx); + } + } + return NULL; +} + +/** + * meta_workspace_manager_context_for_namespace: + * @workspace_manager: a #MetaWorkspaceManager + * @namespace: namespace of context + * + * Find an existing WorkspaceContext for the specified namespace. If no WorkspaceContext + * currently exists for namespace create a new one. + * + * Return value: (transfer none): the workspace context for the specified namespace + */ +MetaWorkspaceContext * +meta_workspace_manager_context_for_namespace (MetaWorkspaceManager *workspace_manager, const gchar *namespace) +{ + MetaWorkspaceContext *anonymous_context = NULL; + + for (GList *iter = workspace_manager->context_list; iter; iter = iter->next) { + MetaWorkspaceContext *context = iter->data; + if (context->namespace == NULL && anonymous_context == NULL) { + anonymous_context = context; + } else if (!g_strcmp0 (context->namespace, namespace )) { + return context; + } + } + + if (anonymous_context) { + anonymous_context->namespace = g_strdup (namespace); + return anonymous_context; + } + + return meta_workspace_context_new (workspace_manager, namespace); +} + +guint +meta_workspace_manager_active_context_id (MetaWorkspaceManager *workspace_manager) +{ + if (workspace_manager->active_context) { + return workspace_manager->active_context->id; + } else { + return 0; + } +} + +G_DEFINE_TYPE (MetaWorkspaceContext, meta_workspace_context, G_TYPE_OBJECT) + +guint +meta_workspace_context_id (MetaWorkspaceContext *context) +{ + return context->id; +} + +void +meta_workspace_context_remove (MetaWorkspaceContext *context) +{ + if (meta_workspace_context_is_current (context)) { + g_warning ("MetaWorkspaceManager: attempt to remove active context ignored"); + return; + } + + for (GList *iter = context->workspaces; iter; iter = iter->next) { + MetaWorkspace *workspace = iter->data; + meta_workspace_relocate_windows (workspace, context->manager->active_workspace); + } + context->active_workspace = NULL; + g_list_free_full (g_steal_pointer(&context->workspaces), (GDestroyNotify) meta_workspace_remove); + + g_signal_emit (context->manager, workspace_manager_signals[CONTEXT_REMOVED], + 0, context->id); + + g_clear_pointer (&context->namespace, g_free); + context->manager->context_list = g_list_remove (context->manager->context_list, context); + g_object_unref (context); +} + +static void +meta_workspace_context_class_init (MetaWorkspaceContextClass *klass) +{ +} + +static void +meta_workspace_context_init (MetaWorkspaceContext *context) +{ +} + +MetaWorkspaceContext * +meta_workspace_context_new (MetaWorkspaceManager *manager, const char *namespace) +{ + guint context_id = manager->next_context_id++; + + MetaWorkspaceContext *context = g_object_new (META_TYPE_WORKSPACE_CONTEXT, NULL); + + context->manager = manager; + context->workspaces = NULL; + context->active_workspace = NULL; + context->namespace = NULL; + context->id = context_id; + + if (namespace) { + context->namespace = g_strdup (namespace); + } + + manager->context_list = g_list_append (manager->context_list, context); + + context->active_workspace = meta_workspace_new_with_context_id (manager, context_id); + g_object_notify (G_OBJECT (manager), "n-workspaces"); + + return context; +} + + +/* + * Synchronize the state stored in the MetaWorkspaceManager to 'context' + * by copying the relevant fields to the WorkspaceContext structure. + * + * The state which is saved is the list of workspaces and the active + * workspace pointer: + * + * workspace_manager->active_workspace + * workspace_manager->workspaces + */ +static void +workspace_context_sync_manager_state (MetaWorkspaceContext *context) +{ + context->active_workspace = context->manager->active_workspace; + context->workspaces = context->manager->workspaces; +} + +gboolean +meta_workspace_context_is_current (MetaWorkspaceContext *context) +{ + return context && context->manager->active_context == context; +} + +/** + * meta_workspace_context_get_active_workspace: + * @context: A #MetaWorkspaceContext + * + * Returns: (transfer none): The current workspace for this context + */ +MetaWorkspace * +meta_workspace_context_get_active_workspace (MetaWorkspaceContext *context) +{ + if (meta_workspace_context_is_current (context)) { + return context->manager->active_workspace; + } else { + return context->active_workspace; + } +} + +void +meta_workspace_context_move_window_to_context (MetaWorkspaceContext *context, MetaWindow *window) +{ + if (!context->active_workspace) { + g_warning ("MetaWorkspaceContext: Cannot move window to context (%d) because no workspace is active", context->id); + return; + } + + if (window->workspace->context_id != context->id) { + meta_window_change_workspace (window, context->active_workspace); + g_signal_emit (context->manager, workspace_manager_signals[CONTEXT_WINDOW_MOVED], 0, window); + } +} + + +void +meta_workspace_context_make_active (MetaWorkspaceContext *context) { + + MetaWorkspaceManager *manager = context->manager; + + workspace_context_sync_manager_state (manager->active_context); + + manager->active_context = context; + + /* + * Do not update manager->active_workspace because meta_workspace_activate() expects + * old value + */ + + /* steal pointer to avoid writing code elsewhere that changes this list while context is active */ + manager->workspaces = g_steal_pointer (&context->workspaces); +} + +/* + * Activate a WorkspaceContext instance by copying the context state fields + * into the MetaWorkspaceManager structure. + * + * If 'context' is already the current WorkspaceContext do nothing. + * + * If some other context is active, save the context fields by calling + * + * workspace_context_sync_manager_state() + */ +void +meta_workspace_context_activate (MetaWorkspaceContext *context) +{ + if (meta_workspace_context_is_current (context)) { + return; + } + + meta_workspace_context_make_active (context); + + guint32 timestamp; + + timestamp = meta_display_get_current_time_roundtrip (context->manager->display); + + /* Will set manager->active_workspace from context->active_workspace */ + meta_workspace_activate ( g_steal_pointer (&context->active_workspace), timestamp); +} + +gboolean +meta_workspace_manager_is_window_on_foreign_context (MetaWorkspaceManager *workspace_manager, MetaWindow *window) +{ + const char *ns = meta_window_namespace (window); + + if (!ns || !g_strcmp0 (ns, workspace_manager->mutter_namespace) || !window->workspace) { + return FALSE; + } + + MetaWorkspaceContext *context = meta_workspace_manager_context_for_namespace (workspace_manager, ns); + return context->id != window->workspace->context_id; +} diff --git a/src/core/util-private.h b/src/core/util-private.h index ff9d7ce42..1c52fbc86 100644 --- a/src/core/util-private.h +++ b/src/core/util-private.h @@ -54,4 +54,6 @@ void meta_init_debug_utils (void); (ycoord) >= (rect).y && \ (ycoord) < ((rect).y + (rect).height)) +char * meta_read_pid_namespace (pid_t pid); + #endif diff --git a/src/core/util.c b/src/core/util.c index 1f1048ca3..17bb7ccca 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -730,3 +730,51 @@ meta_get_debug_paint_flags (void) { return debug_paint_flags; } + +static GFileInfo * +pid_namespace_file_info (pid_t pid) +{ + char *filename = g_strdup_printf("/proc/%u/ns/pid", pid); + GFile *file = g_file_new_for_path(filename); + g_free(filename); + + GError *error = NULL; + + GFileInfo *info = g_file_query_info(file, + G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, + &error); + + g_object_unref(file); + + if (info == NULL) { + g_warning("MetaWindow: Error attempting to read /proc/%u/ns/pid symlink: %s", pid, error->message); + g_error_free (error); + return NULL; + } + + if (!g_file_info_get_is_symlink (info)) { + g_warning("MetaWindow: /proc/%u/ns/pid exists but is not a symlink?", pid); + g_object_unref (info); + return NULL; + } + + return info; + +} + +char * +meta_read_pid_namespace (pid_t pid) +{ + char *value = NULL; + GFileInfo *info = pid_namespace_file_info (pid); + const char *target = g_file_info_get_symlink_target (info); + + if (target) { + value = g_strdup (target); + } + g_object_unref (info); + + return value; +} diff --git a/src/core/window-private.h b/src/core/window-private.h index 8d2d4a020..7e6a02505 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -561,6 +561,9 @@ struct _MetaWindow gboolean has_valid_cgroup; GFile *cgroup_path; + guint namespace_set: 1; + gchar *namespace; + unsigned int events_during_ping; }; diff --git a/src/core/window.c b/src/core/window.c index e22bd5a0e..bcfc1ea17 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -327,6 +327,7 @@ meta_window_finalize (GObject *object) g_free (window->gtk_app_menu_object_path); g_free (window->gtk_menubar_object_path); g_free (window->placement.rule); + g_free (window->namespace); G_OBJECT_CLASS (meta_window_parent_class)->finalize (object); } @@ -1554,7 +1555,7 @@ meta_window_unmanage (MetaWindow *window, g_assert (window->workspace == NULL); #ifndef G_DISABLE_CHECKS - tmp = workspace_manager->workspaces; + tmp = workspace_manager->all_workspaces; while (tmp != NULL) { MetaWorkspace *workspace = tmp->data; @@ -4606,7 +4607,7 @@ set_workspace_state (MetaWindow *window, else if (window->on_all_workspaces) { GList *l; - for (l = workspace_manager->workspaces; l != NULL; l = l->next) + for (l = workspace_manager->all_workspaces; l != NULL; l = l->next) { MetaWorkspace *ws = l->data; meta_workspace_remove_window (ws, window); @@ -4621,7 +4622,7 @@ set_workspace_state (MetaWindow *window, else if (window->on_all_workspaces) { GList *l; - for (l = workspace_manager->workspaces; l != NULL; l = l->next) + for (l = workspace_manager->all_workspaces; l != NULL; l = l->next) { MetaWorkspace *ws = l->data; meta_workspace_add_window (ws, window); @@ -4982,8 +4983,10 @@ meta_window_change_workspace_by_index (MetaWindow *window, workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, space_index); - if (!workspace && append) + if (!workspace && append) { workspace = meta_workspace_manager_append_new_workspace (workspace_manager, FALSE, META_CURRENT_TIME); + } + if (workspace) meta_window_change_workspace (window, workspace); @@ -5265,7 +5268,7 @@ meta_window_get_workspaces (MetaWindow *window) MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; if (window->on_all_workspaces) - return workspace_manager->workspaces; + return workspace_manager->all_workspaces; else if (window->workspace != NULL) return window->workspace->list_containing_self; else if (window->constructing) @@ -8603,3 +8606,31 @@ meta_window_calculate_bounds (MetaWindow *window, return FALSE; } } + +/** + * meta_window_namespace: + * @window: a #MetaWindow + * + * Returns string identifying PID namespace this window belongs to + * if known + * + * Return value: (transfer none): the pid namespace string, or %NULL + */ +const char * +meta_window_namespace (MetaWindow *window) +{ + if (!window->namespace_set) { + window->namespace_set = TRUE; + pid_t pid = meta_window_get_pid (window); + if (pid) { + window->namespace = meta_read_pid_namespace (pid); + } + } + return window->namespace; +} + +gboolean +meta_window_is_on_foreign_workspace_context (MetaWindow *window) +{ + return meta_workspace_manager_is_window_on_foreign_context (window->display->workspace_manager, window); +} diff --git a/src/core/workspace-private.h b/src/core/workspace-private.h index f0d896f6c..38c3b4c94 100644 --- a/src/core/workspace-private.h +++ b/src/core/workspace-private.h @@ -39,6 +39,7 @@ struct _MetaWorkspace GObject parent_instance; MetaDisplay *display; MetaWorkspaceManager *manager; + guint context_id; GList *windows; @@ -74,6 +75,8 @@ struct _MetaWorkspaceClass }; MetaWorkspace* meta_workspace_new (MetaWorkspaceManager *workspace_manager); +MetaWorkspace* meta_workspace_new_with_context_id (MetaWorkspaceManager *workspace_manager, + guint context_id); void meta_workspace_remove (MetaWorkspace *workspace); void meta_workspace_add_window (MetaWorkspace *workspace, MetaWindow *window); diff --git a/src/core/workspace.c b/src/core/workspace.c index 3bd517fca..3819c836e 100644 --- a/src/core/workspace.c +++ b/src/core/workspace.c @@ -237,7 +237,7 @@ meta_workspace_init (MetaWorkspace *workspace) } MetaWorkspace * -meta_workspace_new (MetaWorkspaceManager *workspace_manager) +meta_workspace_new_with_context_id (MetaWorkspaceManager *workspace_manager, guint context_id) { MetaDisplay *display = workspace_manager->display; MetaWorkspace *workspace; @@ -247,9 +247,11 @@ meta_workspace_new (MetaWorkspaceManager *workspace_manager) workspace->display = display; workspace->manager = workspace_manager; + workspace->context_id = context_id; + + workspace_manager->all_workspaces = + g_list_append (workspace_manager->all_workspaces, workspace); - workspace_manager->workspaces = - g_list_append (workspace_manager->workspaces, workspace); workspace->windows = NULL; workspace->mru_list = NULL; @@ -276,9 +278,17 @@ meta_workspace_new (MetaWorkspaceManager *workspace_manager) meta_workspace_add_window (workspace, l->data); g_slist_free (windows); + meta_workspace_manager_append_context_workspace (workspace_manager, workspace); + return workspace; } +MetaWorkspace * +meta_workspace_new (MetaWorkspaceManager *workspace_manager) +{ + return meta_workspace_new_with_context_id (workspace_manager, workspace_manager->active_context->id); +} + /** * workspace_free_all_struts: * @workspace: The workspace. @@ -333,8 +343,7 @@ meta_workspace_remove (MetaWorkspace *workspace) assert_workspace_empty (workspace); - manager->workspaces = - g_list_remove (manager->workspaces, workspace); + meta_workspace_manager_remove_context_workspace (manager, workspace); meta_workspace_clear_logical_monitor_data (workspace); @@ -403,8 +412,8 @@ meta_workspace_add_window (MetaWorkspace *workspace, if (window->struts) { meta_topic (META_DEBUG_WORKAREA, - "Invalidating work area of workspace %d since we're adding window %s to it", - meta_workspace_index (workspace), window->desc); + "Invalidating work area of workspace %08x since we're adding window %s to it", + meta_workspace_get_id (workspace), window->desc); meta_workspace_invalidate_work_area (workspace); } @@ -432,8 +441,8 @@ meta_workspace_remove_window (MetaWorkspace *workspace, if (window->struts) { meta_topic (META_DEBUG_WORKAREA, - "Invalidating work area of workspace %d since we're removing window %s from it", - meta_workspace_index (workspace), window->desc); + "Invalidating work area of workspace %08x since we're removing window %s from it", + meta_workspace_get_id (workspace), window->desc); meta_workspace_invalidate_work_area (workspace); } @@ -486,6 +495,11 @@ workspace_switch_sound(MetaWorkspace *from, int i, nw, x, y, fi, ti; const char *e; + if (from->context_id != to->context_id) { + /* XXX: There is no sound for context switches, but there should be (?) */ + return; + } + nw = meta_workspace_manager_get_n_workspaces (from->manager); fi = meta_workspace_index(from); ti = meta_workspace_index(to); @@ -566,9 +580,15 @@ meta_workspace_activate_with_focus (MetaWorkspace *workspace, MetaWorkspaceLayout layout1, layout2; gint num_workspaces, current_space, new_space; MetaMotionDirection direction; + MetaWorkspaceContext *context; - meta_verbose ("Activating workspace %d", - meta_workspace_index (workspace)); + meta_verbose ("Activating workspace %08x", + meta_workspace_get_id (workspace)); + + context = meta_workspace_manager_lookup_context (workspace->manager, workspace->context_id); + if(context && !meta_workspace_context_is_current (context)) { + meta_workspace_context_make_active (context); + } if (workspace->manager->active_workspace == workspace) { @@ -631,52 +651,59 @@ meta_workspace_activate_with_focus (MetaWorkspace *workspace, comp = meta_display_get_compositor (workspace->display); direction = 0; - current_space = meta_workspace_index (old); - new_space = meta_workspace_index (workspace); - num_workspaces = meta_workspace_manager_get_n_workspaces (workspace->manager); - meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, - current_space, &layout1); - meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, + if (workspace->context_id != old->context_id) { + direction = META_MOTION_CONTEXT_SWITCH; + current_space = meta_workspace_get_id (old); + new_space = meta_workspace_get_id (workspace); + } else { + current_space = meta_workspace_index (old); + new_space = meta_workspace_index (workspace); + num_workspaces = meta_workspace_manager_get_n_workspaces (workspace->manager); + meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, + current_space, &layout1); + + meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, new_space, &layout2); - if (meta_get_locale_direction () == META_LOCALE_DIRECTION_RTL) - { - if (layout1.current_col > layout2.current_col) - direction = META_MOTION_RIGHT; - else if (layout1.current_col < layout2.current_col) - direction = META_MOTION_LEFT; - } - else - { - if (layout1.current_col < layout2.current_col) - direction = META_MOTION_RIGHT; - else if (layout1.current_col > layout2.current_col) - direction = META_MOTION_LEFT; - } + if (meta_get_locale_direction () == META_LOCALE_DIRECTION_RTL) + { + if (layout1.current_col > layout2.current_col) + direction = META_MOTION_RIGHT; + else if (layout1.current_col < layout2.current_col) + direction = META_MOTION_LEFT; + } + else + { + if (layout1.current_col < layout2.current_col) + direction = META_MOTION_RIGHT; + else if (layout1.current_col > layout2.current_col) + direction = META_MOTION_LEFT; + } - if (layout1.current_row < layout2.current_row) - { - if (!direction) - direction = META_MOTION_DOWN; - else if (direction == META_MOTION_RIGHT) - direction = META_MOTION_DOWN_RIGHT; - else - direction = META_MOTION_DOWN_LEFT; - } + if (layout1.current_row < layout2.current_row) + { + if (!direction) + direction = META_MOTION_DOWN; + else if (direction == META_MOTION_RIGHT) + direction = META_MOTION_DOWN_RIGHT; + else + direction = META_MOTION_DOWN_LEFT; + } - if (layout1.current_row > layout2.current_row) - { - if (!direction) - direction = META_MOTION_UP; - else if (direction == META_MOTION_RIGHT) - direction = META_MOTION_UP_RIGHT; - else - direction = META_MOTION_UP_LEFT; - } + if (layout1.current_row > layout2.current_row) + { + if (!direction) + direction = META_MOTION_UP; + else if (direction == META_MOTION_RIGHT) + direction = META_MOTION_UP_RIGHT; + else + direction = META_MOTION_UP_LEFT; + } - meta_workspace_manager_free_workspace_layout (&layout1); - meta_workspace_manager_free_workspace_layout (&layout2); + meta_workspace_manager_free_workspace_layout (&layout1); + meta_workspace_manager_free_workspace_layout (&layout2); + } meta_compositor_switch_workspace (comp, old, workspace, direction); @@ -714,12 +741,19 @@ meta_workspace_activate (MetaWorkspace *workspace, int meta_workspace_index (MetaWorkspace *workspace) { - int ret; + return meta_workspace_manager_context_workspace_index (workspace->manager, workspace); +} - ret = g_list_index (workspace->manager->workspaces, workspace); - g_return_val_if_fail (ret >= 0, -1); +int +meta_workspace_get_id (MetaWorkspace *workspace) +{ + return meta_workspace_manager_get_workspace_id (workspace->manager, workspace); +} - return ret; +guint +meta_workspace_get_context_id (MetaWorkspace *workspace) +{ + return workspace->context_id; } void @@ -776,14 +810,14 @@ meta_workspace_invalidate_work_area (MetaWorkspace *workspace) if (workspace->work_areas_invalid) { meta_topic (META_DEBUG_WORKAREA, - "Work area for workspace %d is already invalid", - meta_workspace_index (workspace)); + "Work area for workspace %08x is already invalid", + meta_workspace_get_id (workspace)); return; } meta_topic (META_DEBUG_WORKAREA, - "Invalidating work area for workspace %d", - meta_workspace_index (workspace)); + "Invalidating work area for workspace %08x", + meta_workspace_get_id (workspace)); /* If we are in the middle of a resize or move operation, we * might have cached pointers to the workspace's edges */ @@ -954,8 +988,8 @@ ensure_work_areas_validated (MetaWorkspace *workspace) } workspace->work_area_screen = work_area; meta_topic (META_DEBUG_WORKAREA, - "Computed work area for workspace %d: %d,%d %d x %d", - meta_workspace_index (workspace), + "Computed work area for workspace %08x: %d,%d %d x %d", + meta_workspace_get_id (workspace), workspace->work_area_screen.x, workspace->work_area_screen.y, workspace->work_area_screen.width, @@ -985,8 +1019,8 @@ ensure_work_areas_validated (MetaWorkspace *workspace) meta_topic (META_DEBUG_WORKAREA, "Computed work area for workspace %d " - "monitor %d: %d,%d %d x %d", - meta_workspace_index (workspace), + "monitor %08x: %d,%d %d x %d", + meta_workspace_get_id (workspace), logical_monitor->number, data->logical_monitor_work_area.x, data->logical_monitor_work_area.y, @@ -1228,6 +1262,8 @@ meta_motion_direction_to_string (MetaMotionDirection direction) return "Up-Left"; case META_MOTION_DOWN_LEFT: return "Down-Left"; + case META_MOTION_CONTEXT_SWITCH: + return "Switch-Context"; } return "Unknown"; diff --git a/src/meson.build b/src/meson.build index 13a69c1a6..dc66332ef 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1039,6 +1039,7 @@ libmutter = shared_library(libmutter_name, libmutter_dep = declare_dependency( link_with: libmutter, include_directories: mutter_includes, + sources: mutter_built_sources, dependencies: [ libmutter_cogl_dep, libmutter_clutter_dep, diff --git a/src/meta/common.h b/src/meta/common.h index 2c516a70d..288e973f1 100644 --- a/src/meta/common.h +++ b/src/meta/common.h @@ -324,6 +324,7 @@ typedef enum * @META_MOTION_UP_RIGHT: Motion up and to the right * @META_MOTION_DOWN_LEFT: Motion down and to the left * @META_MOTION_DOWN_RIGHT: Motion down and to the right + * @META_MOTION_REALM_SWITCH: Workspace switch is a change of current realm */ /* Negative to avoid conflicting with real workspace @@ -339,7 +340,8 @@ typedef enum META_MOTION_UP_LEFT = -5, META_MOTION_UP_RIGHT = -6, META_MOTION_DOWN_LEFT = -7, - META_MOTION_DOWN_RIGHT = -8 + META_MOTION_DOWN_RIGHT = -8, + META_MOTION_CONTEXT_SWITCH = -9 } MetaMotionDirection; /** diff --git a/src/meta/meta-workspace-manager.h b/src/meta/meta-workspace-manager.h index 92cd68191..d00948da7 100644 --- a/src/meta/meta-workspace-manager.h +++ b/src/meta/meta-workspace-manager.h @@ -30,6 +30,14 @@ #include #include +#define META_TYPE_WORKSPACE_CONTEXT (meta_workspace_context_get_type ()) + +META_EXPORT +G_DECLARE_FINAL_TYPE (MetaWorkspaceContext, + meta_workspace_context, + META, WORKSPACE_CONTEXT, + GObject) + #define META_TYPE_WORKSPACE_MANAGER (meta_workspace_manager_get_type ()) META_EXPORT @@ -66,6 +74,9 @@ void meta_workspace_manager_reorder_workspace (MetaWorkspaceManager *workspace_m META_EXPORT int meta_workspace_manager_get_active_workspace_index (MetaWorkspaceManager *workspace_manager); +META_EXPORT +int meta_workspace_manager_get_active_workspace_id (MetaWorkspaceManager *workspace_manager); + META_EXPORT MetaWorkspace *meta_workspace_manager_get_active_workspace (MetaWorkspaceManager *workspace_manager); @@ -75,4 +86,37 @@ void meta_workspace_manager_override_workspace_layout (MetaWorkspaceManager *wor gboolean vertical_layout, int n_rows, int n_columns); +META_EXPORT +guint meta_workspace_manager_active_context_id (MetaWorkspaceManager *workspace_manager); + +META_EXPORT +void meta_workspace_manager_set_builtin_struts_all(MetaWorkspaceManager *workspace_manager, + GSList *struts); + +META_EXPORT +MetaWorkspaceContext *meta_workspace_manager_context_for_namespace (MetaWorkspaceManager *workspace_manager, + const gchar *namespace); + +META_EXPORT +const char *meta_workspace_manager_mutter_namespace (MetaWorkspaceManager *workspace_manager); + +META_EXPORT +void meta_workspace_context_activate (MetaWorkspaceContext *context); + +META_EXPORT +void meta_workspace_context_remove (MetaWorkspaceContext *context); + +META_EXPORT +gboolean meta_workspace_context_is_current (MetaWorkspaceContext *context); + +META_EXPORT +MetaWorkspace * +meta_workspace_context_get_active_workspace (MetaWorkspaceContext *context); + +META_EXPORT +guint meta_workspace_context_id (MetaWorkspaceContext *context); + +META_EXPORT +void meta_workspace_context_move_window_to_context (MetaWorkspaceContext *context, MetaWindow *window); + #endif /* META_WORKSPACE_MANAGER_H */ diff --git a/src/meta/types.h b/src/meta/types.h index ba6fa02be..ca6a44fae 100644 --- a/src/meta/types.h +++ b/src/meta/types.h @@ -44,6 +44,7 @@ typedef struct _MetaCursorTracker MetaCursorTracker; typedef struct _MetaDnd MetaDnd; typedef struct _MetaSettings MetaSettings; +typedef struct _MetaWorkspaceContext MetaWorkspaceContext; typedef struct _MetaWorkspaceManager MetaWorkspaceManager; typedef struct _MetaSelection MetaSelection; diff --git a/src/meta/window.h b/src/meta/window.h index d5d07a061..87dd1655e 100644 --- a/src/meta/window.h +++ b/src/meta/window.h @@ -453,4 +453,10 @@ uint64_t meta_window_get_id (MetaWindow *window); META_EXPORT MetaWindowClientType meta_window_get_client_type (MetaWindow *window); +META_EXPORT +const char *meta_window_namespace (MetaWindow *window); + +META_EXPORT +gboolean meta_window_is_on_foreign_workspace_context (MetaWindow *window); + #endif diff --git a/src/meta/workspace.h b/src/meta/workspace.h index 99aebee5d..4b94cff18 100644 --- a/src/meta/workspace.h +++ b/src/meta/workspace.h @@ -39,6 +39,12 @@ GType meta_workspace_get_type (void); META_EXPORT int meta_workspace_index (MetaWorkspace *workspace); +META_EXPORT +int meta_workspace_get_id (MetaWorkspace *workspace); + +META_EXPORT +guint meta_workspace_get_context_id (MetaWorkspace *workspace); + META_EXPORT MetaDisplay *meta_workspace_get_display (MetaWorkspace *workspace);