[mutter/wip/wayland-kms: 22/39] wayland: add TTY and DRM master management



commit ef36dfbdc2e234701dd023db33ebd7d759d345f3
Author: Giovanni Campagna <gcampagn redhat com>
Date:   Mon Aug 19 14:57:16 2013 +0200

    wayland: add TTY and DRM master management
    
    Now that we have a setuid launcher binary, we can make use of
    using the standard weston-launch protocol, through the socket
    we're passed at startup.
    We also add TTY management, to ensure the keyboard is in the
    correct mode and the VT switching is in our control.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=705861

 configure.ac                       |    2 +-
 src/Makefile.am                    |    6 +-
 src/wayland/meta-tty.c             |  399 ++++++++++++++++++++++++++++++++++++
 src/wayland/meta-tty.h             |   43 ++++
 src/wayland/meta-wayland-private.h |    5 +
 src/wayland/meta-wayland.c         |   84 ++++++++-
 src/wayland/meta-weston-launch.c   |  202 ++++++++++++++++++
 src/wayland/meta-weston-launch.h   |   56 +++++
 8 files changed, 794 insertions(+), 3 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 7d46d1f..ca1b5ec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -225,7 +225,7 @@ AS_IF([test "x$WAYLAND_SCANNER" = "xno"],
 AC_SUBST([WAYLAND_SCANNER])
 AC_SUBST(XWAYLAND_PATH)
 
-MUTTER_PC_MODULES="$MUTTER_PC_MODULES wayland-server"
+MUTTER_PC_MODULES="$MUTTER_PC_MODULES wayland-server libdrm"
 PKG_CHECK_MODULES(MUTTER, $MUTTER_PC_MODULES)
 
 PKG_CHECK_EXISTS([xi >= 1.6.99.1],
diff --git a/src/Makefile.am b/src/Makefile.am
index 0b01e5b..b084739 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -174,6 +174,8 @@ libmutter_wayland_la_SOURCES =                      \
        $(mutter_built_sources)
 
 libmutter_wayland_la_SOURCES +=                        \
+       wayland/meta-tty.c                      \
+       wayland/meta-tty.h                      \
        wayland/meta-wayland.c                  \
        wayland/meta-wayland-private.h          \
        wayland/meta-xwayland-private.h         \
@@ -187,7 +189,9 @@ libmutter_wayland_la_SOURCES +=                     \
        wayland/meta-wayland-seat.c             \
        wayland/meta-wayland-seat.h             \
        wayland/meta-wayland-stage.h            \
-       wayland/meta-wayland-stage.c
+       wayland/meta-wayland-stage.c            \
+       wayland/meta-weston-launch.c            \
+       wayland/meta-weston-launch.h
 
 libmutter_wayland_la_LDFLAGS = -no-undefined
 libmutter_wayland_la_LIBADD  = $(MUTTER_LIBS)
diff --git a/src/wayland/meta-tty.c b/src/wayland/meta-tty.c
new file mode 100644
index 0000000..8aae4c7
--- /dev/null
+++ b/src/wayland/meta-tty.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *             2013 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <linux/major.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "meta-tty.h"
+#include <gio/gio.h>
+#include <glib-unix.h>
+
+/* Introduced in 2.6.38 */
+#ifndef K_OFF
+#define K_OFF 0x04
+#endif
+
+struct _MetaTTYClass
+{
+  GObjectClass parent_class;
+};
+
+struct _MetaTTY
+{
+  GObject parent;
+
+  int fd;
+  struct termios terminal_attributes;
+
+  int input_source, vt_source;
+  int vt, starting_vt;
+  gboolean has_vt;
+  int kb_mode;
+};
+
+enum {
+  SIGNAL_ENTER,
+  SIGNAL_LEAVE,
+  SIGNAL_LAST
+};
+
+static int signals[SIGNAL_LAST];
+
+static void meta_tty_initable_iface_init (GInitableIface *);
+
+G_DEFINE_TYPE_WITH_CODE (MetaTTY, meta_tty, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                               meta_tty_initable_iface_init));
+
+static int
+vt_handler (gpointer user_data)
+{
+  MetaTTY *tty = user_data;
+
+  if (tty->has_vt)
+    {
+      tty->has_vt = FALSE;
+      g_signal_emit (tty, signals[SIGNAL_LEAVE], 0);
+
+      ioctl (tty->fd, VT_RELDISP, 1);
+    }
+  else
+    {
+      ioctl (tty->fd, VT_RELDISP, VT_ACKACQ);
+
+      tty->has_vt = 1;
+      g_signal_emit (tty, signals[SIGNAL_ENTER], 0);
+    }
+
+  return 1;
+}
+
+static int
+on_tty_input (int          fd, 
+             GIOCondition mask,
+             gpointer     user_data)
+{
+  MetaTTY *tty = user_data;
+
+  /* Ignore input to tty.  We get keyboard events from evdev */
+  tcflush(tty->fd, TCIFLUSH);
+
+  return 1;
+}
+
+static int
+try_open_vt (MetaTTY  *tty,
+            GError  **error)
+{
+  int tty0, fd;
+  char filename[16];
+
+  tty0 = open ("/dev/tty0", O_WRONLY | O_CLOEXEC);
+  if (tty0 < 0)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  "Could not open tty0: %s", strerror (errno));
+      return -1;
+    }
+
+  if (ioctl (tty0, VT_OPENQRY, &tty->vt) < 0 || tty->vt == -1) {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  "Could not open tty0: %s", strerror (errno));
+      close (tty0);
+      return -1;
+  }
+
+  close (tty0);
+  snprintf (filename, sizeof filename, "/dev/tty%d", tty->vt);
+  g_debug("compositor: using new vt %s\n", filename);
+  fd = open (filename, O_RDWR | O_NOCTTY | O_CLOEXEC);
+  return fd;
+}
+
+/* FIXME? */
+static int
+tty_activate_vt (MetaTTY *tty,
+                int      vt)
+{
+  return ioctl(tty->fd, VT_ACTIVATE, vt);
+}
+
+static int
+env_get_fd (const char *env)
+{
+  const char *value;
+
+  value = g_getenv (env);
+
+  if (value == NULL)
+    return -1;
+  else
+    return g_ascii_strtoll (env, NULL, 10);
+}
+
+static gboolean
+meta_tty_initable_init(GInitable     *initable,
+                      GCancellable  *cancellable,
+                      GError       **error)
+{
+  MetaTTY *tty = META_TTY (initable);
+  struct termios raw_attributes;
+  struct vt_mode mode = { 0 };
+  int ret;
+       
+  struct stat buf;
+  struct vt_stat vts;
+
+  tty->fd = env_get_fd ("WESTON_TTY_FD");
+  if (tty->fd < 0)
+    tty->fd = STDIN_FILENO;
+
+  if (fstat(tty->fd, &buf) == 0 &&
+      major(buf.st_rdev) == TTY_MAJOR &&
+      minor(buf.st_rdev) > 0)
+    {
+      if (tty->fd == STDIN_FILENO)
+       tty->fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 0);
+      tty->vt = minor(buf.st_rdev);
+    }
+  else
+    {
+      /* Fall back to try opening a new VT.  This typically
+       * requires root. */
+      tty->fd = try_open_vt(tty, error);
+    }
+
+  if (tty->fd <= 0 && (!error || !*error))
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  "Could not open tty0: %s", strerror (errno));
+      return FALSE;
+    }
+
+  if (ioctl(tty->fd, VT_GETSTATE, &vts) == 0)
+    tty->starting_vt = vts.v_active;
+  else
+    tty->starting_vt = tty->vt;
+  
+  if (tty->starting_vt != tty->vt)
+    {
+      if (ioctl(tty->fd, VT_ACTIVATE, tty->vt) < 0 ||
+         ioctl(tty->fd, VT_WAITACTIVE, tty->vt) < 0)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      "Failed to switch to new vt: %s", strerror (errno));
+         goto err;
+       }
+    }
+
+  if (tcgetattr(tty->fd, &tty->terminal_attributes) < 0)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  "Could not get terminal attributes: %s", strerror (errno));
+      goto err;
+    }
+
+  /* Ignore control characters and disable echo */
+  raw_attributes = tty->terminal_attributes;
+  cfmakeraw(&raw_attributes);
+
+  /* Fix up line endings to be normal (cfmakeraw hoses them) */
+  raw_attributes.c_oflag |= OPOST | OCRNL;
+  /* Don't generate ttou signals */
+  raw_attributes.c_oflag &= ~TOSTOP;
+
+  if (tcsetattr(tty->fd, TCSANOW, &raw_attributes) < 0)
+    g_warning("Could not put terminal into raw mode: %s", strerror (errno));
+
+  ioctl(tty->fd, KDGKBMODE, &tty->kb_mode);
+  ret = ioctl(tty->fd, KDSKBMODE, K_OFF);
+  if (ret)
+    {
+      ret = ioctl(tty->fd, KDSKBMODE, K_RAW);
+      if (ret)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      "Failed to set keyboard mode: %s", strerror (errno));
+         goto err_attr;
+       }
+
+      tty->input_source = g_unix_fd_add (tty->fd,
+                                        G_IO_IN,
+                                        on_tty_input, tty);
+    }
+
+  ret = ioctl(tty->fd, KDSETMODE, KD_GRAPHICS);
+  if (ret)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  "Failed to set KD_GRAPHICS mode: %s", strerror (errno));
+      goto err_kdkbmode;
+    }
+
+  tty->has_vt = 1;
+  mode.mode = VT_PROCESS;
+  mode.relsig = SIGUSR1;
+  mode.acqsig = SIGUSR1;
+  if (ioctl(tty->fd, VT_SETMODE, &mode) < 0)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  "Failed to take control of vt handling: %s", strerror (errno));
+      goto err_kdmode;
+    }
+
+  tty->vt_source = g_unix_signal_add (SIGUSR1, vt_handler, tty);
+
+  return TRUE;
+
+ err_kdmode:
+  ioctl (tty->fd, KDSETMODE, KD_TEXT);
+
+ err_kdkbmode:
+  if (tty->input_source)
+    g_source_remove (tty->input_source);
+  ioctl (tty->fd, KDSKBMODE, tty->kb_mode);
+
+ err_attr:
+  tcsetattr (tty->fd, TCSANOW, &tty->terminal_attributes);
+  
+ err:
+  close (tty->fd);
+  return FALSE;
+}
+
+static void
+tty_reset (MetaTTY *tty)
+{
+  struct vt_mode mode = { 0 };
+
+  if (ioctl (tty->fd, KDSKBMODE, tty->kb_mode))
+    g_warning ("failed to restore keyboard mode: %s", strerror (errno));
+
+  if (ioctl (tty->fd, KDSETMODE, KD_TEXT))
+    g_warning ("failed to set KD_TEXT mode on tty: %s", strerror (errno));
+
+  if (tcsetattr (tty->fd, TCSANOW, &tty->terminal_attributes) < 0)
+    g_warning ("could not restore terminal to canonical mode");
+
+  mode.mode = VT_AUTO;
+  if (ioctl (tty->fd, VT_SETMODE, &mode) < 0)
+    g_warning ("could not reset vt handling\n");
+
+  if (tty->has_vt && tty->vt != tty->starting_vt)
+    {
+      ioctl(tty->fd, VT_ACTIVATE, tty->starting_vt);
+      ioctl(tty->fd, VT_WAITACTIVE, tty->starting_vt);
+    }
+}
+
+static void
+meta_tty_finalize (GObject *object)
+{
+  MetaTTY *tty = META_TTY (object);
+
+  if (tty->input_source)
+    g_source_remove (tty->input_source);
+
+  g_source_remove (tty->vt_source);
+
+  tty_reset (tty);
+
+  close (tty->fd);
+
+  G_OBJECT_CLASS (meta_tty_parent_class)->finalize (object);
+}
+
+static void
+meta_tty_init (MetaTTY *self)
+{
+}
+
+static void
+meta_tty_class_init (MetaTTYClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = meta_tty_finalize;
+
+  signals[SIGNAL_ENTER] = g_signal_new ("enter",
+                                       G_TYPE_FROM_CLASS (klass),
+                                       G_SIGNAL_RUN_FIRST,
+                                       0, /* class offset */
+                                       NULL, NULL, /* accumulator */
+                                       g_cclosure_marshal_VOID__VOID,
+                                       G_TYPE_NONE, 0);
+
+  signals[SIGNAL_LEAVE] = g_signal_new ("leave",
+                                       G_TYPE_FROM_CLASS (klass),
+                                       G_SIGNAL_RUN_FIRST,
+                                       0, /* class offset */
+                                       NULL, NULL, /* accumulator */
+                                       g_cclosure_marshal_VOID__VOID,
+                                       G_TYPE_NONE, 0);
+}
+
+static void
+meta_tty_initable_iface_init (GInitableIface *iface)
+{
+  iface->init = meta_tty_initable_init;
+}
+
+MetaTTY *
+meta_tty_new (void)
+{
+  GError *error;
+  MetaTTY *tty;
+
+  error = NULL;
+  tty = g_initable_new (META_TYPE_TTY, NULL, &error, NULL);
+
+  if (tty == NULL)
+    {
+      g_warning ("Failed to initalize TTY handling: %s", error->message);
+      g_error_free (error);
+    }
+
+  return tty;
+}
diff --git a/src/wayland/meta-tty.h b/src/wayland/meta-tty.h
new file mode 100644
index 0000000..2c3bf2a
--- /dev/null
+++ b/src/wayland/meta-tty.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * 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 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_TTY_H
+#define META_TTY_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define META_TYPE_TTY              (meta_tty_get_type())
+#define META_TTY(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_TTY, MetaTTY))
+#define META_TTY_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_TTY, MetaTTYClass))
+#define META_IS_TTY(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_TTY))
+#define META_IS_TTY_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_TTY))
+#define META_TTY_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TTY, MetaTTYClass))
+
+typedef struct _MetaTTY      MetaTTY;
+typedef struct _MetaTTYClass MetaTTYClass;
+
+GType             meta_tty_get_type                (void) G_GNUC_CONST;
+
+MetaTTY          *meta_tty_new                     (void);
+
+G_END_DECLS
+
+#endif /* META_TTY_H */
diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h
index 89c145d..d341909 100644
--- a/src/wayland/meta-wayland-private.h
+++ b/src/wayland/meta-wayland-private.h
@@ -28,6 +28,7 @@
 #include <cairo.h>
 
 #include "window-private.h"
+#include "meta-tty.h"
 #include <meta/meta-cursor-tracker.h>
 
 typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
@@ -166,6 +167,10 @@ struct _MetaWaylandCompositor
   struct wl_client *xwayland_client;
   struct wl_resource *xserver_resource;
 
+  MetaTTY *tty;
+  int drm_fd;
+  GSocket *weston_launch;
+
   MetaWaylandSeat *seat;
 
   /* This surface is only used to keep drag of the implicit grab when
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index 0452bc6..14428cc 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -50,6 +50,7 @@
 #include <meta/main.h>
 #include "frame.h"
 #include "meta-idle-monitor-private.h"
+#include "meta-weston-launch.h"
 
 static MetaWaylandCompositor _meta_wayland_compositor;
 
@@ -1413,11 +1414,59 @@ event_emission_hook_cb (GSignalInvocationHint *ihint,
   return TRUE /* stay connected */;
 }
 
+static int
+env_get_fd (const char *env)
+{
+  const char *value;
+
+  value = g_getenv (env);
+
+  if (value == NULL)
+    return -1;
+  else
+    return g_ascii_strtoll (env, NULL, 10);
+}
+
+static void
+on_our_vt_enter (MetaTTY               *tty,
+                MetaWaylandCompositor *compositor)
+{
+  GError *error;
+
+  error = NULL;
+  if (!meta_weston_launch_set_master (compositor->weston_launch,
+                                     compositor->drm_fd, TRUE, &error))
+    {
+      g_warning ("Failed to become DRM master: %s", error->message);
+      g_error_free (error);
+    }
+}
+
+static void
+on_our_vt_leave (MetaTTY               *tty,
+                MetaWaylandCompositor *compositor)
+{
+  GError *error;
+
+  error = NULL;
+  if (!meta_weston_launch_set_master (compositor->weston_launch,
+                                     compositor->drm_fd, FALSE, &error))
+    {
+      g_warning ("Failed to release DRM master: %s", error->message);
+      g_error_free (error);
+    }
+
+  /* FIXME: we must release input devices as well! */
+}
+
 void
 meta_wayland_init (void)
 {
   MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
   guint event_signal;
+  ClutterBackend *backend;
+  CoglContext *cogl_context;
+  CoglRenderer *cogl_renderer;
 
   memset (compositor, 0, sizeof (MetaWaylandCompositor));
 
@@ -1457,6 +1506,34 @@ meta_wayland_init (void)
   if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS)
     g_error ("Failed to initialize Clutter");
 
+  backend = clutter_get_default_backend ();
+  cogl_context = clutter_backend_get_cogl_context (backend);
+  cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (cogl_context));
+
+  if (cogl_renderer_get_winsys_id (cogl_renderer) == COGL_WINSYS_ID_EGL_KMS)
+    compositor->drm_fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
+  else
+    compositor->drm_fd = -1;
+
+  if (compositor->drm_fd >= 0)
+    {
+      /* Running on bare metal, let's initalize DRM master and VT handling */
+      int weston_launch_fd;
+
+      weston_launch_fd = env_get_fd ("WESTON_LAUNCHER_SOCK");
+      if (weston_launch_fd >= 0)
+       compositor->weston_launch = g_socket_new_from_fd (weston_launch_fd, NULL);
+
+      compositor->tty = meta_tty_new ();
+      if (compositor->tty)
+       {
+         g_signal_connect (compositor->tty, "enter", G_CALLBACK (on_our_vt_enter), compositor);
+         g_signal_connect (compositor->tty, "leave", G_CALLBACK (on_our_vt_leave), compositor);
+       }
+
+      on_our_vt_enter (compositor->tty, compositor);
+    }
+
   compositor->stage = meta_wayland_stage_new ();
   clutter_stage_set_user_resizable (CLUTTER_STAGE (compositor->stage), FALSE);
   g_signal_connect_after (compositor->stage, "paint",
@@ -1514,5 +1591,10 @@ meta_wayland_init (void)
 void
 meta_wayland_finalize (void)
 {
-  meta_xwayland_stop (meta_wayland_compositor_get_default ());
+  MetaWaylandCompositor *compositor;
+
+  compositor = meta_wayland_compositor_get_default ();
+
+  meta_xwayland_stop (compositor);
+  g_clear_object (&compositor->tty);
 }
diff --git a/src/wayland/meta-weston-launch.c b/src/wayland/meta-weston-launch.c
new file mode 100644
index 0000000..68f14cf
--- /dev/null
+++ b/src/wayland/meta-weston-launch.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * 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 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gio/gio.h>
+#include <gio/gunixfdmessage.h>
+
+#include <glib.h>
+#include <sys/time.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include <drm.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "meta-weston-launch.h"
+
+static gboolean
+send_message_to_wl (GSocket                *weston_launch,
+                   void                   *message,
+                   gsize                   size,
+                   GSocketControlMessage  *out_cmsg,
+                   GSocketControlMessage **in_cmsg,
+                   GError                **error)
+{
+  int ok;
+  GInputVector in_iov = { &ok, sizeof (int) };
+  GOutputVector out_iov = { message, size };
+  GSocketControlMessage *out_all_cmsg[2];
+  GSocketControlMessage **in_all_cmsg;
+  int flags = 0;
+  int i;
+
+  out_all_cmsg[0] = out_cmsg;
+  out_all_cmsg[1] = NULL;
+  if (g_socket_send_message (weston_launch, NULL,
+                            &out_iov, 1,
+                            out_all_cmsg, -1,
+                            flags, NULL, error) != (gssize)size)
+    return FALSE;
+
+  if (g_socket_receive_message (weston_launch, NULL,
+                               &in_iov, 1,
+                               &in_all_cmsg, NULL,
+                               &flags, NULL, error) != sizeof (int))
+    return FALSE;
+
+  if (ok != 0)
+    {
+      if (ok == -1)
+       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                    "Got failure from weston-launch");
+      else
+       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ok),
+                    "Got failure from weston-launch: %s", strerror (-ok));
+
+      for (i = 0; in_all_cmsg[i]; i++)
+       g_object_unref (in_all_cmsg[i]);
+      g_free (in_all_cmsg);
+
+      return FALSE;
+    }
+
+  if (in_all_cmsg[0])
+    {
+      for (i = 1; in_all_cmsg[i]; i++)
+       g_object_unref (in_all_cmsg[i]);
+      *in_cmsg = in_all_cmsg[0];
+    }
+
+  g_free (in_all_cmsg);
+  return TRUE;
+}          
+
+gboolean
+meta_weston_launch_set_master (GSocket   *weston_launch,
+                              int        drm_fd,
+                              gboolean   master,
+                              GError   **error)
+{
+  if (weston_launch)
+    {
+      struct weston_launcher_set_master message;
+      GSocketControlMessage *cmsg;
+      gboolean ok;
+
+      message.header.opcode = WESTON_LAUNCHER_DRM_SET_MASTER;
+      message.set_master = master;
+
+      cmsg = g_unix_fd_message_new ();
+      if (g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (cmsg),
+                                      drm_fd, error) == FALSE)
+       {
+         g_object_unref (cmsg);
+         return FALSE;
+       }
+
+      ok = send_message_to_wl (weston_launch, &message, sizeof message, cmsg, NULL, error);
+
+      g_object_unref (cmsg);
+      return ok;
+    }
+  else
+    {
+      int ret;
+
+      if (master)
+       ret = drmSetMaster (drm_fd);
+      else
+       ret = drmDropMaster (drm_fd);
+
+      if (ret < 0)
+       {
+         g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
+                      "Failed to set DRM master directly: %s", strerror (-ret));
+         return FALSE;
+       }
+      else
+       return TRUE;
+    }
+}
+
+int
+meta_weston_launch_open_input_device (GSocket    *weston_launch,
+                                     const char *name,
+                                     int         flags,
+                                     GError    **error)
+{
+  if (weston_launch)
+    {
+      struct weston_launcher_open *message;
+      GSocketControlMessage *cmsg;
+      gboolean ok;
+      int *fds, n_fd;
+      int ret;
+
+      message = g_malloc (sizeof (struct weston_launcher_open) +
+                         strlen (name));
+      message->header.opcode = WESTON_LAUNCHER_OPEN;
+      message->flags = flags;
+      strcpy (message->path, name);
+
+      ok = send_message_to_wl (weston_launch, message,
+                              sizeof (struct weston_launcher_open) + strlen (name),
+                              NULL, &cmsg, error);
+
+      if (ok)
+       {
+         g_assert (G_IS_UNIX_FD_MESSAGE (cmsg));
+
+         fds = g_unix_fd_message_steal_fds (G_UNIX_FD_MESSAGE (cmsg), &n_fd);
+         g_assert (n_fd == 1);
+
+         ret = fds[0];
+         g_free (fds);
+       }
+      else
+       ret = -1;
+
+      g_free (message);
+      g_object_unref (cmsg);
+      return ret;
+    }
+  else
+    {
+      int ret;
+
+      ret = open (name, flags, 0);
+
+      if (ret < 0)
+       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
+                    "Failed to open input device directly: %s", strerror (errno));
+
+      return ret;
+    }
+}
+
diff --git a/src/wayland/meta-weston-launch.h b/src/wayland/meta-weston-launch.h
new file mode 100644
index 0000000..f642ba9
--- /dev/null
+++ b/src/wayland/meta-weston-launch.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * 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 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_WESTON_LAUNCH_H
+#define META_WESTON_LAUNCH_H
+
+#include <glib-object.h>
+
+/* Keep this in sync with weston-launch */
+
+enum weston_launcher_opcode {
+       WESTON_LAUNCHER_OPEN,
+       WESTON_LAUNCHER_DRM_SET_MASTER
+};
+
+struct weston_launcher_message {
+       int opcode;
+};
+
+struct weston_launcher_open {
+       struct weston_launcher_message header;
+       int flags;
+       char path[0];
+};
+
+struct weston_launcher_set_master {
+       struct weston_launcher_message header;
+       int set_master;
+};
+
+gboolean meta_weston_launch_set_master (GSocket   *weston_launch,
+                                       int        drm_fd,
+                                       gboolean   master,
+                                       GError   **error);
+int      meta_weston_launch_open_input_device (GSocket     *weston_launch,
+                                              const char  *name,
+                                              int          flags,
+                                              GError     **error);
+
+#endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]