]> rtime.felk.cvut.cz Git - sojka/lightdm.git/commitdiff
Strip out non-core changes in preperation for merging into trunk
authorRobert Ancell <robert.ancell@canonical.com>
Mon, 1 Jul 2013 00:22:11 +0000 (12:22 +1200)
committerRobert Ancell <robert.ancell@canonical.com>
Mon, 1 Jul 2013 00:22:11 +0000 (12:22 +1200)
26 files changed:
.bzrignore
data/lightdm.conf
src/Makefile.am
src/display-manager.c
src/lightdm.c
src/seat-unity.c [new file with mode: 0644]
src/seat-unity.h [new file with mode: 0644]
src/xserver-local.c
src/xserver-local.h
tests/Makefile.am
tests/scripts/unity-autologin.conf [new file with mode: 0644]
tests/scripts/unity-compositor-command.conf [new file with mode: 0644]
tests/scripts/unity-compositor-fail-ready.conf [new file with mode: 0644]
tests/scripts/unity-compositor-fail-start.conf [new file with mode: 0644]
tests/scripts/unity-login.conf [new file with mode: 0644]
tests/scripts/unity-switch.conf [new file with mode: 0644]
tests/src/Makefile.am
tests/src/X.c
tests/src/test-runner.c
tests/src/unity-system-compositor.c [new file with mode: 0644]
tests/test-unity-autologin [new file with mode: 0755]
tests/test-unity-compositor-command [new file with mode: 0755]
tests/test-unity-compositor-fail-ready [new file with mode: 0755]
tests/test-unity-compositor-fail-start [new file with mode: 0755]
tests/test-unity-login [new file with mode: 0755]
tests/test-unity-switch [new file with mode: 0755]

index 15ae951c6e142b1dea4b9df4018447fc0582d656..124458a2dca6d5ccdd1ad4a5fb3769369cd7d49a 100644 (file)
@@ -71,6 +71,7 @@ tests/src/test-qt5-greeter
 tests/src/test-runner
 tests/src/test-script-hook
 tests/src/test-session
+tests/src/unity-system-compositor
 tests/src/vnc-client
 tests/src/X
 tests/src/Xvnc
index d25fa189eab960ee987bed3e15d7b15478f9a233..3934f8cfbdcc703d41f971af7b663493588b2894 100644 (file)
@@ -43,6 +43,8 @@
 # xdmcp-manager = XDMCP manager to connect to (implies xserver-allow-tcp=true)
 # xdmcp-port = XDMCP UDP/IP port to communicate on
 # xdmcp-key = Authentication key to use for XDM-AUTHENTICATION-1 (stored in keys.conf)
+# unity-compositor-command = Unity compositor command to run (can also contain arguments e.g. unity-system-compositor -special-option)
+# unity-compositor-timeout = Number of seconds to wait for compositor to start
 # greeter-session = Session to load for greeter
 # greeter-hide-users = True to hide the user list
 # greeter-allow-guest = True if the greeter should show a guest login option
@@ -73,6 +75,8 @@
 #xdmcp-manager=
 #xdmcp-port=177
 #xdmcp-key=
+#unity-compositor-command=unity-system-compositor
+#unity-compositor-timeout=60
 #greeter-session=example-gtk-gnome
 #greeter-hide-users=false
 #greeter-allow-guest=true
index c18af5d9bfbe75fac8b3bd9c4563adba42bf34a1..1b7fe28465ed297ca09e3f92c4cda347f02bf8a4 100644 (file)
@@ -38,6 +38,8 @@ lightdm_SOURCES = \
        process.h \
        seat.c \
        seat.h \
+       seat-unity.c \
+       seat-unity.h \
        seat-xdmcp-session.c \
        seat-xdmcp-session.h \
        seat-xlocal.c \
index 14b78f2ba6ce08690e53610c96d65eb9efa0cb6a..465023e3b99bd44a9dae8887676c5ef095c9df4e 100644 (file)
@@ -20,6 +20,7 @@
 #include "display.h"
 #include "seat-xlocal.h"
 #include "seat-xremote.h"
+#include "seat-unity.h"
 #include "plymouth.h"
 
 enum {
@@ -148,6 +149,7 @@ display_manager_init (DisplayManager *manager)
     /* Load the seat modules */
     seat_register_module ("xlocal", SEAT_XLOCAL_TYPE);
     seat_register_module ("xremote", SEAT_XREMOTE_TYPE);
+    seat_register_module ("unity", SEAT_UNITY_TYPE);
 }
 
 static void
index 11a51e3c284a2da5e8cd08b5c14407bfc68891bc..9ca9874658011e9c8913572222c9cc42ee3197c8 100644 (file)
@@ -1040,6 +1040,8 @@ main (int argc, char **argv)
         config_set_string (config_get_instance (), "SeatDefaults", "type", "xlocal");
     if (!config_has_key (config_get_instance (), "SeatDefaults", "xserver-command"))
         config_set_string (config_get_instance (), "SeatDefaults", "xserver-command", "X");
+    if (!config_has_key (config_get_instance (), "SeatDefaults", "unity-compositor-command"))
+        config_set_string (config_get_instance (), "SeatDefaults", "unity-compositor-command", "unity-system-compositor");
     if (!config_has_key (config_get_instance (), "SeatDefaults", "start-session"))
         config_set_boolean (config_get_instance (), "SeatDefaults", "start-session", TRUE);
     if (!config_has_key (config_get_instance (), "SeatDefaults", "allow-guest"))
diff --git a/src/seat-unity.c b/src/seat-unity.c
new file mode 100644 (file)
index 0000000..88d4d0c
--- /dev/null
@@ -0,0 +1,643 @@
+/*
+ * Copyright (C) 2012-2013 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "seat-unity.h"
+#include "configuration.h"
+#include "xserver-local.h"
+#include "xsession.h"
+#include "vt.h"
+#include "plymouth.h"
+
+typedef enum
+{
+   USC_MESSAGE_PING = 0,
+   USC_MESSAGE_PONG = 1,
+   USC_MESSAGE_READY = 2,
+   USC_MESSAGE_SESSION_CONNECTED = 3,
+   USC_MESSAGE_SET_ACTIVE_SESSION = 4
+} USCMessageID;
+
+struct SeatUnityPrivate
+{
+    /* VT we are running on */
+    gint vt;
+
+    /* TRUE if waiting for X server to start before stopping Plymouth */
+    gboolean stopping_plymouth;
+
+    /* File to log to */
+    gchar *log_file;
+
+    /* Filename of Mir socket */
+    gchar *mir_socket_filename;
+
+    /* Pipes to communicate with compositor */
+    int to_compositor_pipe[2];
+    int from_compositor_pipe[2];
+
+    /* IO channel listening on for messages from the compositor */
+    GIOChannel *from_compositor_channel;
+
+    /* TRUE when the compositor indicates it is ready */
+    gboolean compositor_ready;
+
+    /* Buffer reading from channel */
+    guint8 *read_buffer;
+    gsize read_buffer_length;
+    gsize read_buffer_n_used;
+
+    /* Compositor process */
+    Process *compositor_process;
+
+    /* Timeout when waiting for compositor to start */
+    guint compositor_timeout;
+
+    /* Next Mir ID to use for a compositor client */
+    gint next_id;
+
+    /* TRUE if using VT switching fallback */
+    gboolean use_vt_switching;
+
+    /* The currently visible display */
+    Display *active_display;
+};
+
+G_DEFINE_TYPE (SeatUnity, seat_unity, SEAT_TYPE);
+
+static void
+seat_unity_setup (Seat *seat)
+{
+    seat_set_can_switch (seat, TRUE);
+    SEAT_CLASS (seat_unity_parent_class)->setup (seat);
+}
+
+static void
+compositor_stopped_cb (Process *process, SeatUnity *seat)
+{
+    if (seat->priv->compositor_timeout != 0)
+        g_source_remove (seat->priv->compositor_timeout);
+    seat->priv->compositor_timeout = 0;
+
+    if (seat_get_is_stopping (SEAT (seat)))
+    {
+        SEAT_CLASS (seat_unity_parent_class)->stop (SEAT (seat));
+        return;
+    }
+
+    /* If stopped before it was ready, then revert to VT mode */
+    if (!seat->priv->compositor_ready)
+    {
+        g_debug ("Compositor failed to start, switching to VT mode");
+        seat->priv->use_vt_switching = TRUE;
+        SEAT_CLASS (seat_unity_parent_class)->start (SEAT (seat));
+        return;
+    }
+
+    g_debug ("Stopping Unity seat, compositor terminated");
+
+    if (seat->priv->stopping_plymouth)
+    {
+        g_debug ("Stopping Plymouth, compositor failed to start");
+        plymouth_quit (FALSE);
+        seat->priv->stopping_plymouth = FALSE;
+    }
+
+    seat_stop (SEAT (seat));
+}
+
+static void
+compositor_run_cb (Process *process, SeatUnity *seat)
+{
+    int fd;
+
+    /* Make input non-blocking */
+    fd = open ("/dev/null", O_RDONLY);
+    dup2 (fd, STDIN_FILENO);
+    close (fd);
+
+    /* Redirect output to logfile */
+    if (seat->priv->log_file)
+    {
+         int fd;
+
+         fd = g_open (seat->priv->log_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+         if (fd < 0)
+             g_warning ("Failed to open log file %s: %s", seat->priv->log_file, g_strerror (errno));
+         else
+         {
+             dup2 (fd, STDOUT_FILENO);
+             dup2 (fd, STDERR_FILENO);
+             close (fd);
+         }
+    }
+}
+
+static void
+write_message (SeatUnity *seat, guint16 id, const guint8 *payload, guint16 payload_length)
+{
+    guint8 *data;
+    gsize data_length = 4 + payload_length;
+
+    data = g_malloc (data_length);
+    data[0] = id >> 8;
+    data[1] = id & 0xFF;
+    data[2] = payload_length >> 8;
+    data[3] = payload_length & 0xFF;
+    memcpy (data + 4, payload, payload_length);
+
+    errno = 0;
+    if (write (seat->priv->to_compositor_pipe[1], data, data_length) != data_length)
+        g_warning ("Failed to write to compositor: %s", strerror (errno));
+}
+
+static gboolean
+read_cb (GIOChannel *source, GIOCondition condition, gpointer data)
+{
+    SeatUnity *seat = data;
+    gsize n_to_read = 0;
+    guint16 id, payload_length;
+    guint8 *payload;
+  
+    if (condition == G_IO_HUP)
+    {
+        g_debug ("Compositor closed communication channel");
+        return FALSE;
+    }
+
+    /* Work out how much required for a message */
+    if (seat->priv->read_buffer_n_used < 4)
+        n_to_read = 4 - seat->priv->read_buffer_n_used;
+    else
+    {
+        payload_length = seat->priv->read_buffer[2] << 8 | seat->priv->read_buffer[3];
+        n_to_read = 4 + payload_length - seat->priv->read_buffer_n_used;
+    }
+
+    /* Read from compositor */
+    if (n_to_read > 0)
+    {
+        gsize n_total, n_read = 0;
+        GIOStatus status;
+        GError *error = NULL;
+
+        n_total = seat->priv->read_buffer_n_used + n_to_read;
+        if (seat->priv->read_buffer_length < n_total)
+            seat->priv->read_buffer = g_realloc (seat->priv->read_buffer, n_total);
+
+        status = g_io_channel_read_chars (source,
+                                          seat->priv->read_buffer + seat->priv->read_buffer_n_used,
+                                          n_to_read,
+                                          &n_read,
+                                          &error);
+        if (error)
+            g_warning ("Failed to read from compositor: %s", error->message);
+        if (status != G_IO_STATUS_NORMAL)
+            return TRUE;
+        g_clear_error (&error);
+        seat->priv->read_buffer_n_used += n_read;
+    }
+
+    /* Read header */
+    if (seat->priv->read_buffer_n_used < 4)
+         return TRUE;
+    id = seat->priv->read_buffer[0] << 8 | seat->priv->read_buffer[1];
+    payload_length = seat->priv->read_buffer[2] << 8 | seat->priv->read_buffer[3];
+
+    /* Read payload */
+    if (seat->priv->read_buffer_n_used < 4 + payload_length)
+        return TRUE;
+    payload = seat->priv->read_buffer + 4;
+
+    switch (id)
+    {
+    case USC_MESSAGE_PING:
+        g_debug ("PING!");
+        write_message (seat, USC_MESSAGE_PONG, NULL, 0);
+        break;
+    case USC_MESSAGE_PONG:
+        g_debug ("PONG!");
+        break;
+    case USC_MESSAGE_READY:
+        g_debug ("READY");
+        if (!seat->priv->compositor_ready)
+        {
+            seat->priv->compositor_ready = TRUE;
+            g_debug ("Compositor ready");
+            g_source_remove (seat->priv->compositor_timeout);
+            seat->priv->compositor_timeout = 0;
+            SEAT_CLASS (seat_unity_parent_class)->start (SEAT (seat));
+        }
+        break;
+    case USC_MESSAGE_SESSION_CONNECTED:
+        g_debug ("SESSION CONNECTED");
+        break;
+    default:
+        g_warning ("Ingoring unknown message %d with %d octets from system compositor", id, payload_length);
+        break;
+    }
+
+    /* Clear buffer */
+    seat->priv->read_buffer_n_used = 0;
+
+    return TRUE;
+}
+
+static gchar *
+get_absolute_command (const gchar *command)
+{
+    gchar **tokens;
+    gchar *absolute_binary, *absolute_command = NULL;
+
+    tokens = g_strsplit (command, " ", 2);
+
+    absolute_binary = g_find_program_in_path (tokens[0]);
+    if (absolute_binary)
+    {
+        if (tokens[1])
+            absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
+        else
+            absolute_command = g_strdup (absolute_binary);
+        g_free (absolute_binary);
+    }
+    else
+        absolute_command = g_strdup (command);
+
+    g_strfreev (tokens);
+
+    return absolute_command;
+}
+
+static gboolean
+compositor_timeout_cb (gpointer data)
+{
+    SeatUnity *seat = data;
+
+    /* Stop the compositor - it is not working */
+    process_stop (seat->priv->compositor_process);
+
+    return TRUE;
+}
+
+static gboolean
+seat_unity_start (Seat *seat)
+{
+    const gchar *compositor_command;
+    gchar *command, *absolute_command, *dir;
+    gboolean result;
+    int timeout;
+
+    /* Replace Plymouth if it is running */
+    if (plymouth_get_is_active () && plymouth_has_active_vt ())
+    {
+        gint active_vt = vt_get_active ();
+        if (active_vt >= vt_get_min ())
+        {
+            g_debug ("Compositor will replace Plymouth");
+            SEAT_UNITY (seat)->priv->vt = active_vt;
+            plymouth_deactivate ();
+        }
+        else
+            g_debug ("Plymouth is running on VT %d, but this is less than the configured minimum of %d so not replacing it", active_vt, vt_get_min ());
+    }
+    if (SEAT_UNITY (seat)->priv->vt < 0)
+        SEAT_UNITY (seat)->priv->vt = vt_get_unused ();
+    if (SEAT_UNITY (seat)->priv->vt < 0)
+    {
+        g_debug ("Failed to get a VT to run on");
+        return FALSE;
+    }
+    vt_ref (SEAT_UNITY (seat)->priv->vt);
+
+    /* Create pipes to talk to compositor */
+    if (pipe (SEAT_UNITY (seat)->priv->to_compositor_pipe) < 0 || pipe (SEAT_UNITY (seat)->priv->from_compositor_pipe) < 0)
+    {
+        g_debug ("Failed to create compositor pipes: %s", g_strerror (errno));
+        return FALSE;
+    }
+
+    /* Don't allow the daemon end of the pipes to be accessed in the compositor */
+    fcntl (SEAT_UNITY (seat)->priv->to_compositor_pipe[1], F_SETFD, FD_CLOEXEC);
+    fcntl (SEAT_UNITY (seat)->priv->from_compositor_pipe[0], F_SETFD, FD_CLOEXEC);
+
+    /* Listen for messages from the compositor */
+    SEAT_UNITY (seat)->priv->from_compositor_channel = g_io_channel_unix_new (SEAT_UNITY (seat)->priv->from_compositor_pipe[0]);
+    g_io_add_watch (SEAT_UNITY (seat)->priv->from_compositor_channel, G_IO_IN | G_IO_HUP, read_cb, seat);
+
+    /* Setup logging */
+    dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
+    SEAT_UNITY (seat)->priv->log_file = g_build_filename (dir, "unity-system-compositor.log", NULL);
+    g_debug ("Logging to %s", SEAT_UNITY (seat)->priv->log_file);
+    g_free (dir);
+
+    SEAT_UNITY (seat)->priv->mir_socket_filename = g_strdup ("/tmp/mir_socket"); // FIXME: Use this socket by default as XMir is hardcoded to this
+    timeout = seat_get_integer_property (seat, "unity-compositor-timeout");
+    compositor_command = seat_get_string_property (seat, "unity-compositor-command");
+    command = g_strdup_printf ("%s --from-dm-fd %d --to-dm-fd %d --vt %d", compositor_command, SEAT_UNITY (seat)->priv->to_compositor_pipe[0], SEAT_UNITY (seat)->priv->from_compositor_pipe[1], SEAT_UNITY (seat)->priv->vt);
+
+    absolute_command = get_absolute_command (command);
+    g_free (command);
+
+    /* Start the compositor */
+    process_set_command (SEAT_UNITY (seat)->priv->compositor_process, absolute_command);
+    g_free (absolute_command);
+    g_signal_connect (SEAT_UNITY (seat)->priv->compositor_process, "stopped", G_CALLBACK (compositor_stopped_cb), seat);
+    g_signal_connect (SEAT_UNITY (seat)->priv->compositor_process, "run", G_CALLBACK (compositor_run_cb), seat);
+    result = process_start (SEAT_UNITY (seat)->priv->compositor_process, FALSE);
+
+    /* Close compostor ends of the pipes */
+    close (SEAT_UNITY (seat)->priv->to_compositor_pipe[0]);
+    SEAT_UNITY (seat)->priv->to_compositor_pipe[0] = 0;
+    close (SEAT_UNITY (seat)->priv->from_compositor_pipe[1]);
+    SEAT_UNITY (seat)->priv->from_compositor_pipe[1] = 0;
+
+    if (!result)
+        return FALSE;
+
+    /* Connect to the compositor */
+    timeout = seat_get_integer_property (seat, "unity-compositor-timeout");
+    if (timeout <= 0)
+        timeout = 60;
+    g_debug ("Waiting for system compositor for %ds", timeout);
+    SEAT_UNITY (seat)->priv->compositor_timeout = g_timeout_add (timeout * 1000, compositor_timeout_cb, seat);
+
+    return TRUE;
+}
+
+static DisplayServer *
+seat_unity_create_display_server (Seat *seat)
+{
+    XServerLocal *xserver;
+    const gchar *command = NULL, *layout = NULL, *config_file = NULL, *xdmcp_manager = NULL, *key_name = NULL;
+    gboolean allow_tcp;
+    gint port = 0;
+    gchar *id;
+
+    g_debug ("Starting X server on Unity compositor");
+
+    xserver = xserver_local_new ();
+
+    if (!SEAT_UNITY (seat)->priv->use_vt_switching)
+    {
+        id = g_strdup_printf ("%d", SEAT_UNITY (seat)->priv->next_id);
+        SEAT_UNITY (seat)->priv->next_id++;
+        xserver_local_set_mir_id (xserver, id);
+        xserver_local_set_mir_socket (xserver, SEAT_UNITY (seat)->priv->mir_socket_filename);
+        g_free (id);
+    }
+
+    command = seat_get_string_property (seat, "xserver-command");
+    if (command)
+        xserver_local_set_command (xserver, command);
+
+    layout = seat_get_string_property (seat, "xserver-layout");
+    if (layout)
+        xserver_local_set_layout (xserver, layout);
+
+    config_file = seat_get_string_property (seat, "xserver-config");
+    if (config_file)
+        xserver_local_set_config (xserver, config_file);
+
+    allow_tcp = seat_get_boolean_property (seat, "xserver-allow-tcp");
+    xserver_local_set_allow_tcp (xserver, allow_tcp);
+
+    xdmcp_manager = seat_get_string_property (seat, "xdmcp-manager");
+    if (xdmcp_manager)
+        xserver_local_set_xdmcp_server (xserver, xdmcp_manager);
+
+    port = seat_get_integer_property (seat, "xdmcp-port");
+    if (port > 0)
+        xserver_local_set_xdmcp_port (xserver, port);
+
+    key_name = seat_get_string_property (seat, "xdmcp-key");
+    if (key_name)
+    {
+        gchar *dir, *path;
+        GKeyFile *keys;
+        gboolean result;
+        GError *error = NULL;
+
+        dir = config_get_string (config_get_instance (), "LightDM", "config-directory");
+        path = g_build_filename (dir, "keys.conf", NULL);
+        g_free (dir);
+
+        keys = g_key_file_new ();
+        result = g_key_file_load_from_file (keys, path, G_KEY_FILE_NONE, &error);
+        if (error)
+            g_debug ("Error getting key %s", error->message);
+        g_clear_error (&error);
+
+        if (result)
+        {
+            gchar *key = NULL;
+
+            if (g_key_file_has_key (keys, "keyring", key_name, NULL))
+                key = g_key_file_get_string (keys, "keyring", key_name, NULL);
+            else
+                g_debug ("Key %s not defined", key_name);
+
+            if (key)
+                xserver_local_set_xdmcp_key (xserver, key);
+            g_free (key);
+        }
+
+        g_free (path);
+        g_key_file_free (keys);
+    }
+
+    return DISPLAY_SERVER (xserver);
+}
+
+static Session *
+seat_unity_create_session (Seat *seat, Display *display)
+{
+    XServerLocal *xserver;
+    XSession *session;
+    int vt_number;
+    gchar *t;
+
+    xserver = XSERVER_LOCAL (display_get_display_server (display));
+
+    if (SEAT_UNITY (seat)->priv->use_vt_switching)
+        vt_number = xserver_local_get_vt (xserver);
+    else
+        vt_number = SEAT_UNITY (seat)->priv->vt;
+
+    session = xsession_new (XSERVER (xserver));
+    t = g_strdup_printf ("/dev/tty%d", vt_number);
+    session_set_tty (SESSION (session), t);
+    g_free (t);
+
+    /* Set variables for logind */
+    session_set_env (SESSION (session), "XDG_SEAT", "seat0");
+    t = g_strdup_printf ("%d", vt_number);
+    session_set_env (SESSION (session), "XDG_VTNR", t);
+    g_free (t);
+
+    return SESSION (session);
+}
+
+static void
+seat_unity_set_active_display (Seat *seat, Display *display)
+{
+    XServerLocal *xserver;
+    const gchar *id;
+
+    /* If no compositor, have to use VT switching */
+    if (SEAT_UNITY (seat)->priv->use_vt_switching)
+    {
+        gint vt = xserver_local_get_vt (XSERVER_LOCAL (display_get_display_server (display)));
+        if (vt >= 0)
+            vt_set_active (vt);
+
+        SEAT_CLASS (seat_unity_parent_class)->set_active_display (seat, display);
+        return;
+    }
+
+    if (display == SEAT_UNITY (seat)->priv->active_display)
+        return;
+    SEAT_UNITY (seat)->priv->active_display = display;
+
+    xserver = XSERVER_LOCAL (display_get_display_server (display));
+    id = xserver_local_get_mir_id (xserver);
+
+    g_debug ("Switching to Mir session %s", id);
+    write_message (SEAT_UNITY (seat), USC_MESSAGE_SET_ACTIVE_SESSION, id, strlen (id));
+
+    SEAT_CLASS (seat_unity_parent_class)->set_active_display (seat, display);
+}
+
+static Display *
+seat_unity_get_active_display (Seat *seat)
+{
+    if (SEAT_UNITY (seat)->priv->use_vt_switching)
+    {
+        gint vt;
+        GList *link;
+        vt = vt_get_active ();
+        if (vt < 0)
+            return NULL;
+
+        for (link = seat_get_displays (seat); link; link = link->next)
+        {
+            Display *display = link->data;
+            XServerLocal *xserver;
+
+            xserver = XSERVER_LOCAL (display_get_display_server (display));
+            if (xserver_local_get_vt (xserver) == vt)
+                return display;
+        }
+
+        return NULL;
+    }
+
+    return SEAT_UNITY (seat)->priv->active_display;
+}
+
+static void
+seat_unity_run_script (Seat *seat, Display *display, Process *script)
+{
+    const gchar *path;
+    XServerLocal *xserver;
+
+    xserver = XSERVER_LOCAL (display_get_display_server (display));
+    path = xserver_local_get_authority_file_path (xserver);
+    process_set_env (script, "DISPLAY", xserver_get_address (XSERVER (xserver)));
+    process_set_env (script, "XAUTHORITY", path);
+
+    SEAT_CLASS (seat_unity_parent_class)->run_script (seat, display, script);
+}
+
+static void
+seat_unity_stop (Seat *seat)
+{
+    /* Stop the compositor first */
+    if (process_get_is_running (SEAT_UNITY (seat)->priv->compositor_process))
+    {
+        process_stop (SEAT_UNITY (seat)->priv->compositor_process);
+        return;
+    }
+
+    SEAT_CLASS (seat_unity_parent_class)->stop (seat);
+}
+
+static void
+seat_unity_display_removed (Seat *seat, Display *display)
+{
+    if (seat_get_is_stopping (seat))
+        return;
+
+    /* If this is the only display and it failed to start then stop this seat */
+    if (g_list_length (seat_get_displays (seat)) == 0 && !display_get_is_ready (display))
+    {
+        g_debug ("Stopping Unity seat, failed to start a display");
+        seat_stop (seat);
+        return;
+    }
+
+    /* Show a new greeter */
+    if (display == seat_get_active_display (seat))
+    {
+        g_debug ("Active display stopped, switching to greeter");
+        seat_switch_to_greeter (seat);
+    }
+}
+
+static void
+seat_unity_init (SeatUnity *seat)
+{
+    seat->priv = G_TYPE_INSTANCE_GET_PRIVATE (seat, SEAT_UNITY_TYPE, SeatUnityPrivate);
+    seat->priv->vt = -1;
+    seat->priv->compositor_process = process_new ();
+}
+
+static void
+seat_unity_finalize (GObject *object)
+{
+    SeatUnity *seat = SEAT_UNITY (object);
+
+    if (seat->priv->vt >= 0)
+        vt_unref (seat->priv->vt);
+    g_free (seat->priv->log_file);
+    g_free (seat->priv->mir_socket_filename);
+    close (seat->priv->to_compositor_pipe[0]);
+    close (seat->priv->to_compositor_pipe[1]);
+    close (seat->priv->from_compositor_pipe[0]);
+    close (seat->priv->from_compositor_pipe[1]);
+    g_io_channel_unref (seat->priv->from_compositor_channel);
+    g_free (seat->priv->read_buffer);
+    g_object_unref (seat->priv->compositor_process);
+
+    G_OBJECT_CLASS (seat_unity_parent_class)->finalize (object);
+}
+
+static void
+seat_unity_class_init (SeatUnityClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    SeatClass *seat_class = SEAT_CLASS (klass);
+
+    object_class->finalize = seat_unity_finalize;
+    seat_class->setup = seat_unity_setup;
+    seat_class->start = seat_unity_start;
+    seat_class->create_display_server = seat_unity_create_display_server;
+    seat_class->create_session = seat_unity_create_session;
+    seat_class->set_active_display = seat_unity_set_active_display;
+    seat_class->get_active_display = seat_unity_get_active_display;
+    seat_class->run_script = seat_unity_run_script;
+    seat_class->stop = seat_unity_stop;
+    seat_class->display_removed = seat_unity_display_removed;
+
+    g_type_class_add_private (klass, sizeof (SeatUnityPrivate));
+}
diff --git a/src/seat-unity.h b/src/seat-unity.h
new file mode 100644 (file)
index 0000000..7a446cd
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012-2013 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef _SEAT_UNITY_H_
+#define _SEAT_UNITY_H_
+
+#include <glib-object.h>
+#include "seat.h"
+
+G_BEGIN_DECLS
+
+#define SEAT_UNITY_TYPE (seat_unity_get_type())
+#define SEAT_UNITY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAT_UNITY_TYPE, SeatUnity))
+
+typedef struct SeatUnityPrivate SeatUnityPrivate;
+
+typedef struct
+{
+    Seat              parent_instance;
+    SeatUnityPrivate *priv;
+} SeatUnity;
+
+typedef struct
+{
+    SeatClass parent_class;
+} SeatUnityClass;
+
+GType seat_unity_get_type (void);
+
+G_END_DECLS
+
+#endif /* _SEAT_UNITY_H_ */
index 2db24ae251b86f1d30374e010128742bf9e908b2..a9bd56548141149a6a9bdf7761fca99758ef484f 100644 (file)
@@ -56,6 +56,12 @@ struct XServerLocalPrivate
     /* XDMCP key to use */
     gchar *xdmcp_key;
 
+    /* ID to report to Mir */
+    gchar *mir_id;
+
+    /* Filename of socket Mir is listening on */
+    gchar *mir_socket;
+
     /* TRUE when received ready signal */
     gboolean got_signal;
 
@@ -249,6 +255,35 @@ xserver_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
     server->priv->xdmcp_key = g_strdup (key);
 }
 
+void
+xserver_local_set_mir_id (XServerLocal *server, const gchar *id)
+{
+    g_return_if_fail (server != NULL);
+    g_free (server->priv->mir_id);
+    server->priv->mir_id = g_strdup (id);
+
+    if (server->priv->have_vt_ref)
+    {
+        vt_unref (server->priv->vt);
+        server->priv->have_vt_ref = FALSE;
+    }
+    server->priv->vt = -1;
+}
+
+const gchar *xserver_local_get_mir_id (XServerLocal *server)
+{
+    g_return_val_if_fail (server != NULL, NULL);
+    return server->priv->mir_id; 
+}
+
+void
+xserver_local_set_mir_socket (XServerLocal *server, const gchar *socket)
+{
+    g_return_if_fail (server != NULL);
+    g_free (server->priv->mir_socket);
+    server->priv->mir_socket = g_strdup (socket);
+}
+
 gint
 xserver_local_get_vt (XServerLocal *server)
 {
@@ -458,6 +493,13 @@ xserver_local_start (DisplayServer *display_server)
     if (server->priv->authority_file)
         g_string_append_printf (command, " -auth %s", server->priv->authority_file);
 
+    /* Setup for running inside Mir */
+    if (server->priv->mir_id)
+        g_string_append_printf (command, " -mir %s", server->priv->mir_id);
+
+    if (server->priv->mir_socket)
+        g_string_append_printf (command, " -mirSocket %s", server->priv->mir_socket);
+
     /* Connect to a remote server using XDMCP */
     if (server->priv->xdmcp_server != NULL)
     {
@@ -546,6 +588,8 @@ xserver_local_finalize (GObject *object)
     g_free (self->priv->layout);
     g_free (self->priv->xdmcp_server);
     g_free (self->priv->xdmcp_key);
+    g_free (self->priv->mir_id);
+    g_free (self->priv->mir_socket);
     g_free (self->priv->authority_file);
     if (self->priv->have_vt_ref)
         vt_unref (self->priv->vt);
index 0a60aa3e17456019fc1a25a966eaa913e9fc63e2..43c0744a47cdc59590ba2459d2895787df1053dc 100644 (file)
@@ -61,6 +61,12 @@ guint xserver_local_get_xdmcp_port (XServerLocal *server);
 
 void xserver_local_set_xdmcp_key (XServerLocal *server, const gchar *key);
 
+void xserver_local_set_mir_id (XServerLocal *server, const gchar *id);
+
+const gchar *xserver_local_get_mir_id (XServerLocal *server);
+
+void xserver_local_set_mir_socket (XServerLocal *server, const gchar *socket);
+
 gint xserver_local_get_vt (XServerLocal *server);
 
 const gchar *xserver_local_get_authority_file_path (XServerLocal *server);
index f67ef5f862989e2766a824a23373452bbfd22dce..e7efbfe583789f068276a2a43322797668a304ad 100644 (file)
@@ -152,7 +152,13 @@ TESTS = \
        test-python-power-no-login1 \
        test-python-power-no-services \
        test-open-file-descriptors \
-       test-xdmcp-open-file-descriptors
+       test-xdmcp-open-file-descriptors \
+       test-unity-compositor-command \
+       test-unity-compositor-fail-start \
+       test-unity-compositor-fail-ready \
+       test-unity-autologin \
+       test-unity-login \
+       test-unity-switch
 
 #      test-session-exit-error
 #      test-greeter-no-exit
@@ -351,6 +357,12 @@ EXTRA_DIST = \
        scripts/switch-to-user-logout.conf \
        scripts/switch-to-user-no-password.conf \
        scripts/system-xauthority.conf \
+       scripts/unity-autologin.conf \
+       scripts/unity-compositor-command.conf \
+       scripts/unity-compositor-fail-ready.conf \
+       scripts/unity-compositor-fail-start.conf \
+       scripts/unity-login.conf \
+       scripts/unity-switch.conf \
        scripts/users.conf \
        scripts/util-path.conf \
        scripts/user-renamed.conf \
diff --git a/tests/scripts/unity-autologin.conf b/tests/scripts/unity-autologin.conf
new file mode 100644 (file)
index 0000000..481e362
--- /dev/null
@@ -0,0 +1,36 @@
+#
+# Check can automatically login with Unity seat type
+#
+
+[SeatDefaults]
+type=unity
+autologin-user=have-password1
+user-session=default
+
+#?RUNNER DAEMON-START
+
+# System compositor starts
+#?UNITY-SYSTEM-COMPOSITOR START
+#?*UNITY-SYSTEM-COMPOSITOR READY
+
+# X server starts
+#?XSERVER-0 START MIR-ID=0
+#?XSERVER-0 INDICATE-READY
+   
+# LightDM connects to X server
+#?XSERVER-0 ACCEPT-CONNECT
+
+# Session starts
+#?SESSION-X-0 START USER=have-password1
+#?XSERVER-0 ACCEPT-CONNECT
+#?SESSION-X-0 CONNECT-XSERVER
+
+# System compositor switches to session
+#?UNITY-SYSTEM-COMPOSITOR SET-ACTIVE-SESSION ID=0
+
+# Cleanup
+#?*STOP-DAEMON
+#?SESSION-X-0 TERMINATE SIGNAL=15
+#?XSERVER-0 TERMINATE SIGNAL=15
+#?UNITY-SYSTEM-COMPOSITOR TERMINATE SIGNAL=15
+#?RUNNER DAEMON-EXIT STATUS=0
diff --git a/tests/scripts/unity-compositor-command.conf b/tests/scripts/unity-compositor-command.conf
new file mode 100644 (file)
index 0000000..a768993
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# Check can set the unity compositor command
+#
+
+[SeatDefaults]
+type=unity
+user-session=default
+unity-compositor-command=unity-system-compositor --test
+
+#?RUNNER DAEMON-START
+
+# System compositor starts
+#?UNITY-SYSTEM-COMPOSITOR START TEST
+#?*UNITY-SYSTEM-COMPOSITOR READY
+
+# X server starts
+#?XSERVER-0 START MIR-ID=0
+#?XSERVER-0 INDICATE-READY
+
+# LightDM connects to X server
+#?XSERVER-0 ACCEPT-CONNECT
+
+# Greeter starts
+#?GREETER-X-0 START
+#?XSERVER-0 ACCEPT-CONNECT
+#?GREETER-X-0 CONNECT-XSERVER
+#?GREETER-X-0 CONNECT-TO-DAEMON
+#?GREETER-X-0 CONNECTED-TO-DAEMON
+
+# System compositor switches to greeter
+#?UNITY-SYSTEM-COMPOSITOR SET-ACTIVE-SESSION ID=0
+
+# Cleanup
+#?*STOP-DAEMON
+#?GREETER-X-0 TERMINATE SIGNAL=15
+#?XSERVER-0 TERMINATE SIGNAL=15
+#?UNITY-SYSTEM-COMPOSITOR TERMINATE SIGNAL=15
+#?RUNNER DAEMON-EXIT STATUS=0
diff --git a/tests/scripts/unity-compositor-fail-ready.conf b/tests/scripts/unity-compositor-fail-ready.conf
new file mode 100644 (file)
index 0000000..bad936e
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Check falls back to VT switching when the compositor fails to indicate it is ready
+#
+
+[SeatDefaults]
+type=unity
+unity-compositor-timeout=1
+
+#?RUNNER DAEMON-START
+
+# System compositor starts but doesn't indicate it is ready
+#?UNITY-SYSTEM-COMPOSITOR START
+
+# Timeout and compositor is stopped
+#?UNITY-SYSTEM-COMPOSITOR TERMINATE SIGNAL=15
+
+# X server starts in VT mode
+#?XSERVER-0 START VT=8
+#?XSERVER-0 INDICATE-READY
+
+# LightDM connects to X server
+#?XSERVER-0 ACCEPT-CONNECT
+
+# Greeter starts
+#?GREETER-X-0 START
+#?XSERVER-0 ACCEPT-CONNECT
+#?GREETER-X-0 CONNECT-XSERVER
+#?GREETER-X-0 CONNECT-TO-DAEMON
+#?GREETER-X-0 CONNECTED-TO-DAEMON
+
+# Cleanup
+#?*STOP-DAEMON
+#?GREETER-X-0 TERMINATE SIGNAL=15
+#?XSERVER-0 TERMINATE SIGNAL=15
+#?RUNNER DAEMON-EXIT STATUS=0
diff --git a/tests/scripts/unity-compositor-fail-start.conf b/tests/scripts/unity-compositor-fail-start.conf
new file mode 100644 (file)
index 0000000..258b7f4
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Check falls back to VT switching when the compositor fails to start
+#
+
+[unity-system-compositor-config]
+return-value=1
+
+[SeatDefaults]
+type=unity
+
+#?RUNNER DAEMON-START
+
+# System compositor fails to start
+#?UNITY-SYSTEM-COMPOSITOR START
+#?UNITY-SYSTEM-COMPOSITOR EXIT CODE=1
+
+# X server starts in VT mode
+#?XSERVER-0 START VT=8
+#?XSERVER-0 INDICATE-READY
+
+# LightDM connects to X server
+#?XSERVER-0 ACCEPT-CONNECT
+
+# Greeter starts
+#?GREETER-X-0 START
+#?XSERVER-0 ACCEPT-CONNECT
+#?GREETER-X-0 CONNECT-XSERVER
+#?GREETER-X-0 CONNECT-TO-DAEMON
+#?GREETER-X-0 CONNECTED-TO-DAEMON
+
+# Cleanup
+#?*STOP-DAEMON
+#?GREETER-X-0 TERMINATE SIGNAL=15
+#?XSERVER-0 TERMINATE SIGNAL=15
+#?RUNNER DAEMON-EXIT STATUS=0
diff --git a/tests/scripts/unity-login.conf b/tests/scripts/unity-login.conf
new file mode 100644 (file)
index 0000000..4750c23
--- /dev/null
@@ -0,0 +1,50 @@
+#
+# Check can login with Unity seat type
+#
+
+[SeatDefaults]
+type=unity
+user-session=default
+
+#?RUNNER DAEMON-START
+
+# System compositor starts
+#?UNITY-SYSTEM-COMPOSITOR START
+#?*UNITY-SYSTEM-COMPOSITOR READY
+
+# X server starts
+#?XSERVER-0 START MIR-ID=0
+#?XSERVER-0 INDICATE-READY
+
+# LightDM connects to X server
+#?XSERVER-0 ACCEPT-CONNECT
+
+# Greeter starts
+#?GREETER-X-0 START
+#?XSERVER-0 ACCEPT-CONNECT
+#?GREETER-X-0 CONNECT-XSERVER
+#?GREETER-X-0 CONNECT-TO-DAEMON
+#?GREETER-X-0 CONNECTED-TO-DAEMON
+
+# System compositor switches to greeter
+#?UNITY-SYSTEM-COMPOSITOR SET-ACTIVE-SESSION ID=0
+
+# Log into account with a password
+#?*GREETER-X-0 AUTHENTICATE USERNAME=have-password1
+#?GREETER-X-0 SHOW-PROMPT TEXT="Password:"
+#?*GREETER-X-0 RESPOND TEXT="password"
+#?GREETER-X-0 AUTHENTICATION-COMPLETE USERNAME=have-password1 AUTHENTICATED=TRUE
+#?*GREETER-X-0 START-SESSION
+#?GREETER-X-0 TERMINATE SIGNAL=15
+
+# Session starts
+#?SESSION-X-0 START USER=have-password1
+#?XSERVER-0 ACCEPT-CONNECT
+#?SESSION-X-0 CONNECT-XSERVER
+
+# Cleanup
+#?*STOP-DAEMON
+#?SESSION-X-0 TERMINATE SIGNAL=15
+#?XSERVER-0 TERMINATE SIGNAL=15
+#?UNITY-SYSTEM-COMPOSITOR TERMINATE SIGNAL=15
+#?RUNNER DAEMON-EXIT STATUS=0
diff --git a/tests/scripts/unity-switch.conf b/tests/scripts/unity-switch.conf
new file mode 100644 (file)
index 0000000..939738c
--- /dev/null
@@ -0,0 +1,91 @@
+#
+# Check system compositor correctly switches
+#
+
+[SeatDefaults]
+type=unity
+autologin-user=have-password1
+user-session=default
+
+#?RUNNER DAEMON-START
+
+# System compositor starts
+#?UNITY-SYSTEM-COMPOSITOR START
+#?*UNITY-SYSTEM-COMPOSITOR READY
+
+# X server starts
+#?XSERVER-0 START MIR-ID=0
+#?XSERVER-0 INDICATE-READY
+
+# LightDM connects to X server
+#?XSERVER-0 ACCEPT-CONNECT
+
+# Session starts
+#?SESSION-X-0 START USER=have-password1
+#?XSERVER-0 ACCEPT-CONNECT
+#?SESSION-X-0 CONNECT-XSERVER
+
+# System compositor switches to session
+#?UNITY-SYSTEM-COMPOSITOR SET-ACTIVE-SESSION ID=0
+
+# Show the greeter
+#?*SWITCH-TO-GREETER
+#?RUNNER SWITCH-TO-GREETER
+
+# New X server starts
+#?XSERVER-1 START MIR-ID=1
+#?XSERVER-1 INDICATE-READY
+
+# LightDM connects to X server
+#?XSERVER-1 ACCEPT-CONNECT
+
+# Greeter starts
+#?GREETER-X-1 START
+#?XSERVER-1 ACCEPT-CONNECT
+#?GREETER-X-1 CONNECT-XSERVER
+#?GREETER-X-1 CONNECT-TO-DAEMON
+#?GREETER-X-1 CONNECTED-TO-DAEMON
+
+# System compositor switches to greeter
+#?UNITY-SYSTEM-COMPOSITOR SET-ACTIVE-SESSION ID=1
+
+# Login
+#?*GREETER-X-1 AUTHENTICATE USERNAME=have-password2
+#?GREETER-X-1 SHOW-PROMPT TEXT="Password:"
+#?*GREETER-X-1 RESPOND TEXT="password"
+#?GREETER-X-1 AUTHENTICATION-COMPLETE USERNAME=have-password2 AUTHENTICATED=TRUE
+#?*GREETER-X-1 START-SESSION
+#?GREETER-X-1 TERMINATE SIGNAL=15
+
+# New session starts
+#?SESSION-X-1 START USER=have-password2
+#?XSERVER-1 ACCEPT-CONNECT
+#?SESSION-X-1 CONNECT-XSERVER
+
+# Logout of new session
+#?*SESSION-X-1 LOGOUT
+#?XSERVER-1 TERMINATE SIGNAL=15
+
+# X server starts
+#?XSERVER-1 START MIR-ID=2
+#?XSERVER-1 INDICATE-READY
+#?XSERVER-1 ACCEPT-CONNECT
+
+# Greeter starts
+#?GREETER-X-1 START
+#?XSERVER-1 ACCEPT-CONNECT
+#?GREETER-X-1 CONNECT-XSERVER
+#?GREETER-X-1 CONNECT-TO-DAEMON
+#?GREETER-X-1 CONNECTED-TO-DAEMON
+
+# System compositor switches to greeter
+#?UNITY-SYSTEM-COMPOSITOR SET-ACTIVE-SESSION ID=2
+
+# Cleanup
+#?*STOP-DAEMON
+#?SESSION-X-0 TERMINATE SIGNAL=15
+#?XSERVER-0 TERMINATE SIGNAL=15
+#?GREETER-X-1 TERMINATE SIGNAL=15
+#?XSERVER-1 TERMINATE SIGNAL=15
+#?UNITY-SYSTEM-COMPOSITOR TERMINATE SIGNAL=15
+#?RUNNER DAEMON-EXIT STATUS=0
index 30235524e68fc65821d58302a3b684f8c70604c0..4b65fa0a22918e6cc12cd3aa9ed021c388ccd1dc 100644 (file)
@@ -1,5 +1,18 @@
-noinst_PROGRAMS = dbus-env test-runner X Xvnc test-greeter-wrapper test-gobject-greeter test-session test-script-hook guest-account initctl plymouth vnc-client
-dist_noinst_SCRIPTS = lightdm-session test-python-greeter
+noinst_PROGRAMS = dbus-env \
+                  initctl \
+                  plymouth \
+                  test-gobject-greeter \
+                  test-greeter-wrapper \
+                  test-runner \
+                  test-script-hook \
+                  test-session \
+                  guest-account \
+                  unity-system-compositor \
+                  vnc-client \
+                  X \
+                  Xvnc
+dist_noinst_SCRIPTS = lightdm-session \
+                      test-python-greeter
 noinst_LTLIBRARIES = libsystem.la
 
 libsystem_la_SOURCES = libsystem.c
@@ -181,6 +194,17 @@ plymouth_LDADD = \
        $(GLIB_LIBS) \
        $(GIO_UNIX_LIBS)
 
+unity_system_compositor_SOURCES = unity-system-compositor.c status.c status.h
+unity_system_compositor_CFLAGS = \
+       $(WARN_CFLAGS) \
+       $(GLIB_CFLAGS) \
+       $(GIO_CFLAGS) \
+       $(GIO_UNIX_CFLAGS)
+unity_system_compositor_LDADD = \
+       $(GLIB_LIBS) \
+       $(GIO_LIBS) \
+       $(GIO_UNIX_LIBS)
+
 vnc_client_SOURCES = vnc-client.c status.c status.h
 vnc_client_CFLAGS = \
        $(WARN_CFLAGS) \
index 5746d7a0c9b8161a703e4d15342f1ab873408890..6657423861e28800d9cfc855d710f77bec5877d0 100644 (file)
@@ -198,6 +198,7 @@ main (int argc, char **argv)
     gboolean do_xdmcp = FALSE;
     guint xdmcp_port = 0;
     gchar *xdmcp_host = NULL;
+    gchar *mir_id = NULL;
     gchar *lock_filename;
     int lock_file;
     GString *status_text;
@@ -263,10 +264,20 @@ main (int argc, char **argv)
         {
             vt_number = atoi (arg + 2);
         }
-        else if (g_str_has_prefix (arg, "-novtswitch"))
+        else if (strcmp (arg, "-novtswitch") == 0)
         {
             /* Ignore VT args */
         }
+        else if (strcmp (arg, "-mir") == 0)
+        {
+            mir_id = argv[i+1];
+            i++;
+        }
+        else if (strcmp (arg, "-mirSocket") == 0)
+        {
+            /* FIXME */
+            i++;
+        }
         else
         {
             g_printerr ("Unrecognized option: %s\n"
@@ -278,6 +289,8 @@ main (int argc, char **argv)
                         "-query host-name       Contact named host for XDMCP\n"
                         "-broadcast             Broadcast for XDMCP\n"
                         "-port port-num         UDP port number to send messages to\n"
+                        "-mir id                Mir ID to use\n"
+                        "-mirSocket name        Mir socket to use\n"
                         "vtxx                   Use virtual terminal xx instead of the next available\n",
                         arg, argv[0]);
             return EXIT_FAILURE;
@@ -292,6 +305,8 @@ main (int argc, char **argv)
     g_string_printf (status_text, "XSERVER-%d START", display_number);
     if (vt_number >= 0)
         g_string_append_printf (status_text, " VT=%d", vt_number);
+    if (mir_id != NULL)
+        g_string_append_printf (status_text, " MIR-ID=%s", mir_id);
     status_notify (status_text->str);
     g_string_free (status_text, TRUE);
 
index 67cd921e533c29aebb2ae9e532c0b41d2bef666b..ce6afd39e9b24dd40b62248110782e1829460832 100644 (file)
@@ -533,7 +533,8 @@ handle_command (const gchar *command)
     /* Forward to external processes */
     else if (g_str_has_prefix (name, "SESSION-") ||
              g_str_has_prefix (name, "GREETER-") ||
-             g_str_has_prefix (name, "XSERVER-"))
+             g_str_has_prefix (name, "XSERVER-") ||
+             strcmp (name, "UNITY-SYSTEM-COMPOSITOR") == 0)
     {
         GList *link;
         for (link = status_clients; link; link = link->next)
diff --git a/tests/src/unity-system-compositor.c b/tests/src/unity-system-compositor.c
new file mode 100644 (file)
index 0000000..2dcf8c1
--- /dev/null
@@ -0,0 +1,185 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "status.h"
+
+static GMainLoop *loop;
+static int exit_status = EXIT_SUCCESS;
+static int from_dm_fd = -1, to_dm_fd = -1;
+
+static GKeyFile *config;
+
+static void
+quit (int status)
+{
+    exit_status = status;
+    g_main_loop_quit (loop);
+}
+
+static void
+signal_cb (int signum)
+{
+    status_notify ("UNITY-SYSTEM-COMPOSITOR TERMINATE SIGNAL=%d", signum);
+    quit (EXIT_SUCCESS);
+}
+
+typedef enum
+{
+   USC_MESSAGE_PING = 0,
+   USC_MESSAGE_PONG = 1,
+   USC_MESSAGE_READY = 2,
+   USC_MESSAGE_SESSION_CONNECTED = 3,
+   USC_MESSAGE_SET_ACTIVE_SESSION = 4
+} USCMessageID;
+
+static void
+write_message (guint16 id, const guint8 *payload, guint16 payload_length)
+{
+    guint8 *data;
+    gsize data_length = 4 + payload_length;
+
+    data = g_malloc (data_length);
+    data[0] = id >> 8;
+    data[1] = id & 0xFF;
+    data[2] = payload_length >> 8;
+    data[3] = payload_length & 0xFF;
+    memcpy (data + 4, payload, payload_length);
+
+    if (write (to_dm_fd, data, data_length) < 0)
+        fprintf (stderr, "Failed to write to daemon: %s\n", strerror (errno));
+}
+
+static gboolean
+read_message_cb (GIOChannel *channel, GIOCondition condition, gpointer data)
+{
+    gchar header[4], *payload;
+    gsize n_read;
+    guint16 id;
+    guint16 payload_length;
+    GError *error = NULL;
+
+    if (g_io_channel_read_chars (channel, header, 4, &n_read, &error) != G_IO_STATUS_NORMAL)
+    {
+        g_printerr ("Failed to read header: %s\n", error->message);
+        return FALSE;
+    }
+    if (n_read != 4)
+    {
+        g_printerr ("Short read for header, %d instead of expected 4\n", n_read);
+        return FALSE;
+    }
+    id = header[0] << 8 | header[1];
+    payload_length = header[2] << 8 | header[3];
+    payload = g_malloc0 (payload_length + 1);
+    if (g_io_channel_read_chars (channel, payload, payload_length, &n_read, &error) != G_IO_STATUS_NORMAL)
+    {
+        g_printerr ("Failed to read payload: %s\n", error->message);
+        return FALSE;
+    }
+    if (n_read != payload_length)
+    {
+        g_printerr ("Short read for payload, %d instead of expected %d\n", n_read, payload_length);
+        return FALSE;      
+    }
+
+    switch (id)
+    {
+    case USC_MESSAGE_PING:
+        status_notify ("UNITY-SYSTEM-COMPOSITOR PING");
+        break;
+    case USC_MESSAGE_SET_ACTIVE_SESSION:
+        status_notify ("UNITY-SYSTEM-COMPOSITOR SET-ACTIVE-SESSION ID=%s", (gchar *)payload);
+        break;
+    default:
+        g_printerr ("Ignoring message %d with %d octets\n", id, payload_length);
+        break;
+    }
+
+    free (payload);
+
+    return TRUE;
+}
+
+static void
+request_cb (const gchar *request)
+{
+    if (!request)
+    {
+        g_main_loop_quit (loop);
+        return;
+    }
+
+    if (strcmp (request, "UNITY-SYSTEM-COMPOSITOR PING") == 0)
+        write_message (USC_MESSAGE_PING, NULL, 0);
+    else if (strcmp (request, "UNITY-SYSTEM-COMPOSITOR PONG") == 0)
+        write_message (USC_MESSAGE_PONG, NULL, 0);
+    else if (strcmp (request, "UNITY-SYSTEM-COMPOSITOR READY") == 0)
+        write_message (USC_MESSAGE_READY, NULL, 0);
+}
+
+int
+main (int argc, char **argv)
+{
+    int i;
+    gboolean test = FALSE;
+
+    signal (SIGINT, signal_cb);
+    signal (SIGTERM, signal_cb);
+    signal (SIGHUP, signal_cb);
+
+#if !defined(GLIB_VERSION_2_36)
+    g_type_init ();
+#endif
+
+    loop = g_main_loop_new (NULL, FALSE);
+
+    status_connect (request_cb);
+
+    for (i = 1; i < argc; i++)
+    {
+        char *arg = argv[i];
+
+        if (strcmp (arg, "--from-dm-fd") == 0)
+        {
+            from_dm_fd = atoi (argv[i+1]);
+            i++;
+        }
+        else if (strcmp (arg, "--to-dm-fd") == 0)
+        {
+            to_dm_fd = atoi (argv[i+1]);
+            i++;
+        }
+        else if (strcmp (arg, "--vt") == 0)
+        {
+            //vt_number = atoi (argv[i+1]);
+            i++;
+        }
+        else if (strcmp (arg, "--test") == 0)
+            test = TRUE;
+        else
+            return EXIT_FAILURE;
+    }
+
+    g_io_add_watch (g_io_channel_unix_new (from_dm_fd), G_IO_IN, read_message_cb, NULL);
+
+    if (test)
+        status_notify ("UNITY-SYSTEM-COMPOSITOR START TEST");
+    else
+        status_notify ("UNITY-SYSTEM-COMPOSITOR START");
+
+    config = g_key_file_new ();
+    g_key_file_load_from_file (config, g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "script", NULL), G_KEY_FILE_NONE, NULL);
+
+    if (g_key_file_has_key (config, "unity-system-compositor-config", "return-value", NULL))
+    {
+        int return_value = g_key_file_get_integer (config, "unity-system-compositor-config", "return-value", NULL);
+        status_notify ("UNITY-SYSTEM-COMPOSITOR EXIT CODE=%d", return_value);
+        return return_value;
+    }
+
+    g_main_loop_run (loop);
+
+    return exit_status;
+}
diff --git a/tests/test-unity-autologin b/tests/test-unity-autologin
new file mode 100755 (executable)
index 0000000..1e15178
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+./src/dbus-env ./src/test-runner unity-autologin test-gobject-greeter
diff --git a/tests/test-unity-compositor-command b/tests/test-unity-compositor-command
new file mode 100755 (executable)
index 0000000..f953527
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+./src/dbus-env ./src/test-runner unity-compositor-command test-gobject-greeter
diff --git a/tests/test-unity-compositor-fail-ready b/tests/test-unity-compositor-fail-ready
new file mode 100755 (executable)
index 0000000..25b8495
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+./src/dbus-env ./src/test-runner unity-compositor-fail-ready test-gobject-greeter
diff --git a/tests/test-unity-compositor-fail-start b/tests/test-unity-compositor-fail-start
new file mode 100755 (executable)
index 0000000..7e49cd2
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+./src/dbus-env ./src/test-runner unity-compositor-fail-start test-gobject-greeter
diff --git a/tests/test-unity-login b/tests/test-unity-login
new file mode 100755 (executable)
index 0000000..dea45ca
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+./src/dbus-env ./src/test-runner unity-login test-gobject-greeter
diff --git a/tests/test-unity-switch b/tests/test-unity-switch
new file mode 100755 (executable)
index 0000000..6f5fb3f
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+./src/dbus-env ./src/test-runner unity-switch test-gobject-greeter