/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "meta-weston-launch.h" struct _MetaLauncherClass { GObjectClass parent_class; void (*enter) (MetaLauncher *); void (*leave) (MetaLauncher *); }; struct _MetaLauncher { GObject parent; GSocket *weston_launch; gboolean vt_switched; GMainContext *nested_context; GMainLoop *nested_loop; GSource *inner_source; GSource *outer_source; }; enum { SIGNAL_ENTER, SIGNAL_LEAVE, SIGNAL_LAST }; static int signals[SIGNAL_LAST]; G_DEFINE_TYPE (MetaLauncher, meta_launcher, G_TYPE_OBJECT); static void handle_request_vt_switch (MetaLauncher *self); static gboolean request_vt_switch_idle (gpointer user_data) { handle_request_vt_switch (user_data); return FALSE; } static gboolean send_message_to_wl (MetaLauncher *self, void *message, gsize size, GSocketControlMessage *out_cmsg, GSocketControlMessage **in_cmsg, GError **error) { struct weston_launcher_reply reply; GInputVector in_iov = { &reply, sizeof (reply) }; 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 (self->weston_launch, NULL, &out_iov, 1, out_all_cmsg, -1, flags, NULL, error) != (gssize)size) return FALSE; if (g_socket_receive_message (self->weston_launch, NULL, &in_iov, 1, &in_all_cmsg, NULL, &flags, NULL, error) != sizeof (reply)) return FALSE; while (reply.header.opcode != ((struct weston_launcher_message*)message)->opcode) { /* There were events queued */ g_assert ((reply.header.opcode & WESTON_LAUNCHER_EVENT) == WESTON_LAUNCHER_EVENT); /* This can never happen, because the only time mutter-launch can queue this event is after confirming a VT switch, and we don't make requests during that time. Note that getting this event would be really bad, because we would be in the wrong loop/context. */ g_assert (reply.header.opcode != WESTON_LAUNCHER_SERVER_VT_ENTER); switch (reply.header.opcode) { case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH: g_idle_add (request_vt_switch_idle, self); break; default: g_assert_not_reached (); } if (g_socket_receive_message (self->weston_launch, NULL, &in_iov, 1, NULL, NULL, &flags, NULL, error) != sizeof (reply)) return FALSE; } if (reply.ret != 0) { if (reply.ret == -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 (-reply.ret), "Got failure from weston-launch: %s", strerror (-reply.ret)); for (i = 0; in_all_cmsg && in_all_cmsg[i]; i++) g_object_unref (in_all_cmsg[i]); g_free (in_all_cmsg); return FALSE; } if (in_all_cmsg && 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_launcher_set_drm_fd (MetaLauncher *self, int drm_fd, GError **error) { struct weston_launcher_message message; GSocketControlMessage *cmsg; gboolean ok; message.opcode = WESTON_LAUNCHER_DRM_SET_FD; 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 (self, &message, sizeof message, cmsg, NULL, error); g_object_unref (cmsg); return ok; } int meta_launcher_open_input_device (MetaLauncher *self, const char *name, int flags, GError **error) { struct weston_launcher_open *message; GSocketControlMessage *cmsg; gboolean ok; gsize size; int *fds, n_fd; int ret; size = sizeof (struct weston_launcher_open) + strlen (name) + 1; message = g_malloc (size); message->header.opcode = WESTON_LAUNCHER_OPEN; message->flags = flags; strcpy (message->path, name); message->path[strlen(name)] = 0; ok = send_message_to_wl (self, message, size, 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); g_object_unref (cmsg); } else ret = -1; g_free (message); return ret; } static void meta_launcher_finalize (GObject *object) { MetaLauncher *launcher = META_LAUNCHER (object); g_source_destroy (launcher->outer_source); g_source_destroy (launcher->inner_source); g_main_loop_unref (launcher->nested_loop); g_main_context_unref (launcher->nested_context); g_object_unref (launcher->weston_launch); G_OBJECT_CLASS (meta_launcher_parent_class)->finalize (object); } static void meta_launcher_enter (MetaLauncher *launcher) { ClutterBackend *backend; CoglContext *cogl_context; CoglDisplay *cogl_display; backend = clutter_get_default_backend (); cogl_context = clutter_backend_get_cogl_context (backend); cogl_display = cogl_context_get_display (cogl_context); cogl_kms_display_queue_modes_reset (cogl_display); clutter_evdev_reclaim_devices (); } static void meta_launcher_leave (MetaLauncher *launcher) { clutter_evdev_release_devices (); } static int on_evdev_device_open (const char *path, int flags, gpointer user_data, GError **error) { MetaLauncher *launcher = user_data; return meta_launcher_open_input_device (launcher, path, flags, error); } static void handle_vt_enter (MetaLauncher *launcher) { g_assert (launcher->vt_switched); g_main_loop_quit (launcher->nested_loop); } static void handle_request_vt_switch (MetaLauncher *launcher) { struct weston_launcher_message message; GError *error; gboolean ok; g_signal_emit (launcher, signals[SIGNAL_LEAVE], 0); message.opcode = WESTON_LAUNCHER_CONFIRM_VT_SWITCH; error = NULL; ok = send_message_to_wl (launcher, &message, sizeof (message), NULL, NULL, &error); if (!ok) { g_warning ("Failed to acknowledge VT switch: %s", error->message); g_error_free (error); return; } g_assert (!launcher->vt_switched); launcher->vt_switched = TRUE; /* We can't do anything at this point, because we don't have input devices and we don't have the DRM master, so let's run a nested busy loop until the VT is reentered */ g_main_loop_run (launcher->nested_loop); g_assert (launcher->vt_switched); launcher->vt_switched = FALSE; g_signal_emit (launcher, signals[SIGNAL_ENTER], 0); } static gboolean on_socket_readable (GSocket *socket, GIOCondition condition, gpointer user_data) { MetaLauncher *launcher = user_data; struct weston_launcher_event event; gssize read; GError *error; if ((condition & G_IO_IN) == 0) return TRUE; error = NULL; read = g_socket_receive (socket, (char*)&event, sizeof(event), NULL, &error); if (read < (gssize)sizeof(event)) { g_warning ("Error reading from weston-launcher socket: %s", error->message); g_error_free (error); return TRUE; } switch (event.header.opcode) { case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH: handle_request_vt_switch (launcher); break; case WESTON_LAUNCHER_SERVER_VT_ENTER: handle_vt_enter (launcher); break; } return TRUE; } 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 (value, NULL, 10); } static void meta_launcher_init (MetaLauncher *self) { int launch_fd; launch_fd = env_get_fd ("WESTON_LAUNCHER_SOCK"); if (launch_fd < 0) g_error ("Invalid mutter-launch socket"); self->weston_launch = g_socket_new_from_fd (launch_fd, NULL); clutter_evdev_set_open_callback (on_evdev_device_open, self); self->nested_context = g_main_context_new (); self->nested_loop = g_main_loop_new (self->nested_context, FALSE); self->outer_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL); g_source_set_callback (self->outer_source, (GSourceFunc)on_socket_readable, self, NULL); g_source_attach (self->outer_source, NULL); g_source_unref (self->outer_source); self->inner_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL); g_source_set_callback (self->inner_source, (GSourceFunc)on_socket_readable, self, NULL); g_source_attach (self->inner_source, self->nested_context); g_source_unref (self->inner_source); } static void meta_launcher_class_init (MetaLauncherClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_launcher_finalize; klass->enter = meta_launcher_enter; klass->leave = meta_launcher_leave; signals[SIGNAL_ENTER] = g_signal_new ("enter", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MetaLauncherClass, enter), 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, G_STRUCT_OFFSET (MetaLauncherClass, leave), NULL, NULL, /* accumulator */ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } MetaLauncher * meta_launcher_new (void) { return g_object_new (META_TYPE_LAUNCHER, NULL); } gboolean meta_launcher_activate_vt (MetaLauncher *launcher, int vt, GError **error) { struct weston_launcher_activate_vt message; message.header.opcode = WESTON_LAUNCHER_ACTIVATE_VT; message.vt = vt; return send_message_to_wl (launcher, &message, sizeof (message), NULL, NULL, error); }