2013-08-19 08:57:16 -04:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2014-04-22 17:06:36 -04:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "meta-launcher.h"
|
|
|
|
#include "weston-launch.h"
|
2013-08-19 08:57:16 -04:00
|
|
|
|
|
|
|
#include <gio/gunixfdmessage.h>
|
|
|
|
|
|
|
|
#include <clutter/clutter.h>
|
2014-03-11 16:51:24 -04:00
|
|
|
#include <clutter/egl/clutter-egl.h>
|
2013-08-19 08:57:16 -04:00
|
|
|
#include <clutter/evdev/clutter-evdev.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
2014-04-22 17:06:36 -04:00
|
|
|
#include <string.h>
|
2013-08-19 08:57:16 -04:00
|
|
|
|
2014-03-31 22:47:45 -04:00
|
|
|
#include "wayland/meta-wayland-private.h"
|
2014-04-22 15:56:34 -04:00
|
|
|
#include "backends/meta-backend.h"
|
2014-04-22 15:15:11 -04:00
|
|
|
#include "meta-cursor-renderer-native.h"
|
2013-08-19 08:57:16 -04:00
|
|
|
|
|
|
|
struct _MetaLauncher
|
|
|
|
{
|
|
|
|
GSocket *weston_launch;
|
|
|
|
|
|
|
|
gboolean vt_switched;
|
|
|
|
|
|
|
|
GMainContext *nested_context;
|
|
|
|
GMainLoop *nested_loop;
|
|
|
|
|
|
|
|
GSource *inner_source;
|
|
|
|
GSource *outer_source;
|
|
|
|
};
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2014-04-10 12:58:58 -04:00
|
|
|
guint id;
|
|
|
|
|
2013-08-19 08:57:16 -04:00
|
|
|
/* 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:
|
2014-04-10 12:58:58 -04:00
|
|
|
id = g_idle_add (request_vt_switch_idle, self);
|
|
|
|
g_source_set_name_by_id (id, "[mutter] request_vt_switch_idle");
|
2013-08-19 08:57:16 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-03-11 16:50:50 -04:00
|
|
|
static int
|
2014-03-11 16:51:24 -04:00
|
|
|
meta_launcher_open_device (MetaLauncher *self,
|
|
|
|
const char *name,
|
|
|
|
int flags,
|
|
|
|
GError **error)
|
2013-08-19 08:57:16 -04:00
|
|
|
{
|
|
|
|
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_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 ();
|
2014-03-11 17:22:08 -04:00
|
|
|
|
|
|
|
{
|
|
|
|
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
2014-04-22 15:56:34 -04:00
|
|
|
MetaBackend *backend = meta_get_backend ();
|
|
|
|
MetaCursorRenderer *renderer = meta_backend_get_cursor_renderer (backend);
|
2014-03-11 17:22:08 -04:00
|
|
|
|
|
|
|
/* When we mode-switch back, we need to immediately queue a redraw
|
|
|
|
* in case nothing else queued one for us, and force the cursor to
|
|
|
|
* update. */
|
|
|
|
|
|
|
|
clutter_actor_queue_redraw (compositor->stage);
|
2014-04-22 15:15:11 -04:00
|
|
|
meta_cursor_renderer_native_force_update (META_CURSOR_RENDERER_NATIVE (renderer));
|
2014-03-11 17:22:08 -04:00
|
|
|
}
|
2013-08-19 08:57:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2014-03-11 16:51:24 -04:00
|
|
|
return meta_launcher_open_device (launcher, path, flags, error);
|
2013-08-19 08:57:16 -04:00
|
|
|
}
|
|
|
|
|
2014-03-11 17:18:47 -04:00
|
|
|
static void
|
|
|
|
on_evdev_device_close (int fd,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
close (fd);
|
|
|
|
}
|
|
|
|
|
2013-08-19 08:57:16 -04:00
|
|
|
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;
|
|
|
|
|
2013-12-31 18:03:31 -05:00
|
|
|
meta_launcher_leave (launcher);
|
2013-08-19 08:57:16 -04:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2013-12-31 18:03:31 -05:00
|
|
|
meta_launcher_enter (launcher);
|
2013-08-19 08:57:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-12-31 18:03:31 -05:00
|
|
|
MetaLauncher *
|
|
|
|
meta_launcher_new (void)
|
2013-08-19 08:57:16 -04:00
|
|
|
{
|
2013-12-31 18:03:31 -05:00
|
|
|
MetaLauncher *self = g_slice_new0 (MetaLauncher);
|
2014-04-22 17:15:31 -04:00
|
|
|
GError *error = NULL;
|
2013-08-19 08:57:16 -04:00
|
|
|
int launch_fd;
|
2014-04-22 17:15:31 -04:00
|
|
|
int kms_fd;
|
2013-08-19 08:57:16 -04:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2014-04-22 17:15:31 -04:00
|
|
|
kms_fd = meta_launcher_open_device (self, "/dev/dri/card0", O_RDWR, &error);
|
|
|
|
if (error)
|
|
|
|
g_error ("Failed to open /dev/dri/card0: %s", error->message);
|
|
|
|
|
|
|
|
clutter_egl_set_kms_fd (kms_fd);
|
2014-03-11 17:18:47 -04:00
|
|
|
clutter_evdev_set_device_callbacks (on_evdev_device_open,
|
|
|
|
on_evdev_device_close,
|
|
|
|
self);
|
2014-03-11 16:48:52 -04:00
|
|
|
|
2013-12-31 18:03:31 -05:00
|
|
|
return self;
|
2013-08-19 08:57:16 -04:00
|
|
|
}
|
|
|
|
|
2013-12-31 18:03:31 -05:00
|
|
|
void
|
|
|
|
meta_launcher_free (MetaLauncher *launcher)
|
2013-08-19 08:57:16 -04:00
|
|
|
{
|
2013-12-31 18:03:31 -05:00
|
|
|
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_slice_free (MetaLauncher, launcher);
|
2013-08-19 08:57:16 -04:00
|
|
|
}
|
2014-01-16 13:40:41 -05:00
|
|
|
|
2014-04-22 17:08:57 -04:00
|
|
|
gboolean
|
|
|
|
meta_launcher_activate_session (MetaLauncher *launcher,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
return meta_launcher_activate_vt (launcher, -1, error);
|
|
|
|
}
|
|
|
|
|
2014-01-16 13:40:41 -05:00
|
|
|
gboolean
|
|
|
|
meta_launcher_activate_vt (MetaLauncher *launcher,
|
2014-03-11 17:04:22 -04:00
|
|
|
signed char vt,
|
2014-01-16 13:40:41 -05:00
|
|
|
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);
|
|
|
|
}
|