diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index dac170e9f..13276a09b 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 ea278f883..3426735b6 100644
--- a/src/core/meta-workspace-manager-private.h
+++ b/src/core/meta-workspace-manager-private.h
@@ -29,6 +29,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;
@@ -36,8 +66,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;
@@ -92,3 +135,29 @@ META_EXPORT_TEST
void meta_workspace_manager_update_num_workspaces (MetaWorkspaceManager *workspace_manager,
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);
+
diff --git a/src/core/meta-workspace-manager.c b/src/core/meta-workspace-manager.c
index e16e1cd91..9e9780fb6 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
};
@@ -163,6 +166,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", NULL, NULL,
@@ -180,6 +206,7 @@ meta_workspace_manager_class_init (MetaWorkspaceManagerClass *klass)
g_param_spec_int ("n-workspaces", NULL, NULL,
1, G_MAXINT, 1,
G_PARAM_READABLE));
+
}
static void
@@ -192,7 +219,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;
@@ -209,12 +236,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
@@ -225,11 +264,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);
@@ -280,6 +314,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);
}
@@ -988,7 +1025,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
@@ -1002,6 +1039,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
@@ -1020,9 +1068,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
@@ -1045,3 +1099,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 18ae68de7..0bde0b35f 100644
--- a/src/core/util-private.h
+++ b/src/core/util-private.h
@@ -56,3 +56,6 @@ meta_timeval_to_microseconds (const struct timeval *tv)
#define META_CONTAINER_OF(ptr, type, member) \
(type *) ((uint8_t *) (ptr) - G_STRUCT_OFFSET (type, member))
+
+char * meta_read_pid_namespace (pid_t pid);
+
diff --git a/src/core/util.c b/src/core/util.c
index 05a0dea39..47e21fdbb 100644
--- a/src/core/util.c
+++ b/src/core/util.c
@@ -598,3 +598,50 @@ meta_log (const char *format, ...)
g_logv (G_LOG_DOMAIN, mutter_log_level, format, args);
va_end (args);
}
+
+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 4df552abe..724506c01 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -549,6 +549,10 @@ struct _MetaWindow
guint is_alive : 1;
guint in_workspace_change : 1;
+
+ guint namespace_set: 1;
+ gchar *namespace;
+
};
struct _MetaWindowClass
diff --git a/src/core/window.c b/src/core/window.c
index f100a8bf3..687f82ed8 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -348,6 +348,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);
}
@@ -1543,7 +1544,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;
@@ -4753,7 +4754,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);
@@ -4768,7 +4769,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);
@@ -5140,8 +5141,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);
@@ -5396,7 +5399,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)
@@ -8010,3 +8013,32 @@ meta_get_window_suspend_timeout_s (void)
{
return SUSPEND_HIDDEN_TIMEOUT_S;
}
+
+/**
+ * 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 43767c310..ba189e75c 100644
--- a/src/core/workspace-private.h
+++ b/src/core/workspace-private.h
@@ -38,6 +38,7 @@ struct _MetaWorkspace
GObject parent_instance;
MetaDisplay *display;
MetaWorkspaceManager *manager;
+ guint context_id;
GList *windows;
@@ -73,6 +74,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 bdcc2c560..634a8eb22 100644
--- a/src/core/workspace.c
+++ b/src/core/workspace.c
@@ -210,7 +210,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;
@@ -220,9 +220,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;
@@ -249,9 +251,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.
@@ -306,8 +316,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);
@@ -376,8 +385,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);
}
@@ -405,8 +414,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);
}
@@ -462,6 +471,11 @@ workspace_switch_sound (MetaWorkspace *from,
int x, y;
const char *sound_name;
+ if (from->context_id != to->context_id) {
+ /* XXX: There is no sound for context switches, but there should be (?) */
+ return;
+ }
+
n_workspaces = meta_workspace_manager_get_n_workspaces (from->manager);
from_idx = meta_workspace_index(from);
to_idx = meta_workspace_index(to);
@@ -546,13 +560,19 @@ meta_workspace_activate_with_focus (MetaWorkspace *workspace,
MetaWorkspaceLayout layout1, layout2;
gint num_workspaces, current_space, new_space;
MetaMotionDirection direction;
+ MetaWorkspaceContext *context;
MetaWindowDrag *window_drag;
g_return_if_fail (META_IS_WORKSPACE (workspace));
g_return_if_fail (meta_workspace_index (workspace) != -1);
- 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)
{
@@ -620,52 +640,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);
@@ -703,12 +730,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
@@ -766,14 +800,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));
window_drag =
meta_compositor_get_current_window_drag (workspace->display->compositor);
@@ -949,8 +983,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,
@@ -980,8 +1014,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,
@@ -1225,6 +1259,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/meta/meta-enums.h b/src/meta/meta-enums.h
index be7d71c0a..2d4d6599a 100644
--- a/src/meta/meta-enums.h
+++ b/src/meta/meta-enums.h
@@ -258,6 +258,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_CONTEXT_SWITCH: Workspace switch is a change of current realm
*/
/* Negative to avoid conflicting with real workspace
@@ -273,7 +274,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 6257448cd..12a0f520e 100644
--- a/src/meta/meta-workspace-manager.h
+++ b/src/meta/meta-workspace-manager.h
@@ -29,6 +29,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
@@ -65,6 +73,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);
@@ -74,3 +85,35 @@ 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);
diff --git a/src/meta/types.h b/src/meta/types.h
index cbe2a9a3d..3faa7f9f1 100644
--- a/src/meta/types.h
+++ b/src/meta/types.h
@@ -36,5 +36,6 @@ 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 42fedfadd..b508d3335 100644
--- a/src/meta/window.h
+++ b/src/meta/window.h
@@ -446,3 +446,10 @@ MetaWindowClientType meta_window_get_client_type (MetaWindow *window);
META_EXPORT
gboolean meta_window_has_pointer (MetaWindow *window);
+
+META_EXPORT
+const char *meta_window_namespace (MetaWindow *window);
+
+META_EXPORT
+gboolean meta_window_is_on_foreign_workspace_context (MetaWindow *window);
+
diff --git a/src/meta/workspace.h b/src/meta/workspace.h
index d9b7365a7..d570281ca 100644
--- a/src/meta/workspace.h
+++ b/src/meta/workspace.h
@@ -38,6 +38,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);