/*
* Copyright © 2017-2018 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
*/
#include "config.h"
#include "remote-desktop.h"
#include "screen-cast.h"
#include "request.h"
#include "restore-token.h"
#include "pipewire.h"
#include "call.h"
#include "session.h"
#include "xdp-dbus.h"
#include "xdp-impl-dbus.h"
#include "xdp-utils.h"
#include
#include
#define REMOTE_DESKTOP_TABLE "remote-desktop"
typedef struct _RemoteDesktop RemoteDesktop;
typedef struct _RemoteDesktopClass RemoteDesktopClass;
struct _RemoteDesktop
{
XdpDbusRemoteDesktopSkeleton parent_instance;
};
struct _RemoteDesktopClass
{
XdpDbusRemoteDesktopSkeletonClass parent_class;
};
static XdpDbusImplRemoteDesktop *impl;
static RemoteDesktop *remote_desktop;
GType remote_desktop_get_type (void) G_GNUC_CONST;
static void remote_desktop_iface_init (XdpDbusRemoteDesktopIface *iface);
static GQuark quark_request_session;
G_DEFINE_TYPE_WITH_CODE (RemoteDesktop, remote_desktop,
XDP_DBUS_TYPE_REMOTE_DESKTOP_SKELETON,
G_IMPLEMENT_INTERFACE (XDP_DBUS_TYPE_REMOTE_DESKTOP,
remote_desktop_iface_init))
typedef enum _RemoteDesktopSessionState
{
REMOTE_DESKTOP_SESSION_STATE_INIT,
REMOTE_DESKTOP_SESSION_STATE_STARTED,
REMOTE_DESKTOP_SESSION_STATE_CLOSED
} RemoteDesktopSessionState;
typedef enum _DeviceType
{
DEVICE_TYPE_NONE = 0,
DEVICE_TYPE_KEYBOARD = 1 << 0,
DEVICE_TYPE_POINTER = 1 << 1,
DEVICE_TYPE_TOUCHSCREEN = 1 << 2,
} DeviceType;
typedef struct _RemoteDesktopSession
{
Session parent;
RemoteDesktopSessionState state;
DeviceType shared_devices;
GList *streams;
gboolean clipboard_requested;
gboolean devices_selected;
gboolean sources_selected;
gboolean clipboard_enabled;
gboolean uses_eis;
char *restore_token;
PersistMode persist_mode;
GVariant *restore_data;
} RemoteDesktopSession;
typedef struct _RemoteDesktopSessionClass
{
SessionClass parent_class;
} RemoteDesktopSessionClass;
GType remote_desktop_session_get_type (void);
G_DEFINE_TYPE (RemoteDesktopSession, remote_desktop_session, session_get_type ())
gboolean
is_remote_desktop_session (Session *session)
{
return G_TYPE_CHECK_INSTANCE_TYPE (session,
remote_desktop_session_get_type ());
}
gboolean
remote_desktop_session_can_select_sources (RemoteDesktopSession *session)
{
if (session->sources_selected)
return FALSE;
switch (session->state)
{
case REMOTE_DESKTOP_SESSION_STATE_INIT:
return TRUE;
case REMOTE_DESKTOP_SESSION_STATE_STARTED:
case REMOTE_DESKTOP_SESSION_STATE_CLOSED:
return FALSE;
}
g_assert_not_reached ();
}
gboolean
remote_desktop_session_can_select_devices (RemoteDesktopSession *session)
{
if (session->devices_selected)
return FALSE;
switch (session->state)
{
case REMOTE_DESKTOP_SESSION_STATE_INIT:
return TRUE;
case REMOTE_DESKTOP_SESSION_STATE_STARTED:
case REMOTE_DESKTOP_SESSION_STATE_CLOSED:
return FALSE;
}
g_assert_not_reached ();
}
gboolean
remote_desktop_session_can_request_clipboard (RemoteDesktopSession *session)
{
if (session->clipboard_requested)
return FALSE;
if (xdp_dbus_impl_remote_desktop_get_version (impl) < 2)
return FALSE;
switch (session->state)
{
case REMOTE_DESKTOP_SESSION_STATE_INIT:
return TRUE;
case REMOTE_DESKTOP_SESSION_STATE_STARTED:
case REMOTE_DESKTOP_SESSION_STATE_CLOSED:
return FALSE;
}
g_assert_not_reached ();
}
GList *
remote_desktop_session_get_streams (RemoteDesktopSession *session)
{
return session->streams;
}
void
remote_desktop_session_sources_selected (RemoteDesktopSession *session)
{
session->sources_selected = TRUE;
}
gboolean
remote_desktop_session_is_clipboard_enabled (RemoteDesktopSession *session)
{
return session->clipboard_enabled;
}
void
remote_desktop_session_clipboard_requested (RemoteDesktopSession *session)
{
session->clipboard_requested = TRUE;
}
static RemoteDesktopSession *
remote_desktop_session_new (GVariant *options,
Request *request,
GError **error)
{
Session *session;
GDBusInterfaceSkeleton *interface_skeleton =
G_DBUS_INTERFACE_SKELETON (request);
const char *session_token;
GDBusConnection *connection =
g_dbus_interface_skeleton_get_connection (interface_skeleton);
GDBusConnection *impl_connection =
g_dbus_proxy_get_connection (G_DBUS_PROXY (impl));
const char *impl_dbus_name = g_dbus_proxy_get_name (G_DBUS_PROXY (impl));
session_token = lookup_session_token (options);
session = g_initable_new (remote_desktop_session_get_type (), NULL, error,
"sender", request->sender,
"app-id", xdp_app_info_get_id (request->app_info),
"token", session_token,
"connection", connection,
"impl-connection", impl_connection,
"impl-dbus-name", impl_dbus_name,
NULL);
if (session)
g_debug ("remote desktop session owned by '%s' created", session->sender);
return (RemoteDesktopSession *)session;
}
static void
create_session_done (GObject *source_object,
GAsyncResult *res,
gpointer data)
{
g_autoptr(Request) request = data;
Session *session;
guint response = 2;
gboolean should_close_session;
GVariantBuilder results_builder;
g_autoptr(GError) error = NULL;
REQUEST_AUTOLOCK (request);
session = g_object_get_qdata (G_OBJECT (request), quark_request_session);
g_variant_builder_init (&results_builder, G_VARIANT_TYPE_VARDICT);
SESSION_AUTOLOCK_UNREF (g_object_ref (session));
g_object_set_qdata (G_OBJECT (request), quark_request_session, NULL);
if (!xdp_dbus_impl_remote_desktop_call_create_session_finish (impl,
&response,
NULL,
res,
&error))
{
g_dbus_error_strip_remote_error (error);
g_warning ("A backend call failed: %s", error->message);
should_close_session = TRUE;
goto out;
}
if (request->exported && response == 0)
{
if (!session_export (session, &error))
{
g_warning ("Failed to export session: %s", error->message);
response = 2;
should_close_session = TRUE;
goto out;
}
should_close_session = FALSE;
session_register (session);
}
else
{
should_close_session = TRUE;
}
g_variant_builder_add (&results_builder, "{sv}",
"session_handle", g_variant_new ("s", session->id));
out:
if (request->exported)
{
xdp_dbus_request_emit_response (XDP_DBUS_REQUEST (request),
response,
g_variant_builder_end (&results_builder));
request_unexport (request);
}
else
{
g_variant_builder_clear (&results_builder);
}
if (should_close_session)
session_close (session, FALSE);
}
static gboolean
handle_create_session (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
GVariant *arg_options)
{
Request *request = request_from_invocation (invocation);
g_autoptr(GError) error = NULL;
g_autoptr(XdpDbusImplRequest) impl_request = NULL;
Session *session;
GVariantBuilder options_builder;
GVariant *options;
REQUEST_AUTOLOCK (request);
impl_request =
xdp_dbus_impl_request_proxy_new_sync (g_dbus_proxy_get_connection (G_DBUS_PROXY (impl)),
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
g_dbus_proxy_get_name (G_DBUS_PROXY (impl)),
request->id,
NULL, &error);
if (!impl_request)
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
request_set_impl_request (request, impl_request);
request_export (request, g_dbus_method_invocation_get_connection (invocation));
session = (Session *)remote_desktop_session_new (arg_options, request, &error);
if (!session)
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
options = g_variant_builder_end (&options_builder);
g_object_set_qdata_full (G_OBJECT (request),
quark_request_session,
g_object_ref (session),
g_object_unref);
xdp_dbus_impl_remote_desktop_call_create_session (impl,
request->id,
session->id,
xdp_app_info_get_id (request->app_info),
options,
NULL,
create_session_done,
g_object_ref (request));
xdp_dbus_remote_desktop_complete_create_session (object, invocation, request->id);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static void
select_devices_done (GObject *source_object,
GAsyncResult *res,
gpointer data)
{
g_autoptr(Request) request = data;
Session *session;
guint response = 2;
gboolean should_close_session;
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) results = NULL;
REQUEST_AUTOLOCK (request);
session = g_object_get_qdata (G_OBJECT (request), quark_request_session);
SESSION_AUTOLOCK_UNREF (g_object_ref (session));
g_object_set_qdata (G_OBJECT (request), quark_request_session, NULL);
if (!xdp_dbus_impl_remote_desktop_call_select_devices_finish (impl,
&response,
&results,
res,
&error))
{
g_dbus_error_strip_remote_error (error);
g_warning ("A backend call failed: %s", error->message);
}
should_close_session = !request->exported || response != 0;
if (request->exported)
{
if (!results)
{
GVariantBuilder results_builder;
g_variant_builder_init (&results_builder, G_VARIANT_TYPE_VARDICT);
results = g_variant_ref_sink (g_variant_builder_end (&results_builder));
}
xdp_dbus_request_emit_response (XDP_DBUS_REQUEST (request),
response, results);
request_unexport (request);
}
if (should_close_session)
{
session_close (session, TRUE);
}
else if (!session->closed)
{
RemoteDesktopSession *remote_desktop_session = (RemoteDesktopSession *)session;
remote_desktop_session->devices_selected = TRUE;
}
}
static gboolean
validate_device_types (const char *key,
GVariant *value,
GVariant *options,
GError **error)
{
guint32 types = g_variant_get_uint32 (value);
if ((types & ~(1 | 2 | 4)) != 0)
{
g_set_error (error, XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT,
"Unsupported device type: %x", types & ~(1 | 2 | 4));
return FALSE;
}
return TRUE;
}
static gboolean
validate_restore_token (const char *key,
GVariant *value,
GVariant *options,
GError **error)
{
const char *restore_token = g_variant_get_string (value, NULL);
if (!g_uuid_string_is_valid (restore_token))
{
g_set_error (error,
XDG_DESKTOP_PORTAL_ERROR,
XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT,
"Restore token is not a valid UUID string");
return FALSE;
}
return TRUE;
}
static gboolean
validate_persist_mode (const char *key,
GVariant *value,
GVariant *options,
GError **error)
{
uint32_t mode = g_variant_get_uint32 (value);
if (mode > PERSIST_MODE_PERSISTENT)
{
g_set_error (error,
XDG_DESKTOP_PORTAL_ERROR,
XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT,
"Invalid persist mode %x", mode);
return FALSE;
}
return TRUE;
}
static XdpOptionKey remote_desktop_select_devices_options[] = {
{ "types", G_VARIANT_TYPE_UINT32, validate_device_types },
{ "restore_token", G_VARIANT_TYPE_STRING, validate_restore_token },
{ "persist_mode", G_VARIANT_TYPE_UINT32, validate_persist_mode },
};
static gboolean
replace_remote_desktop_restore_token_with_data (Session *session,
GVariant **in_out_options,
GError **error)
{
RemoteDesktopSession *remote_desktop_session = (RemoteDesktopSession *) session;
g_autoptr(GVariant) options = NULL;
PersistMode persist_mode;
options = *in_out_options;
if (!g_variant_lookup (options, "persist_mode", "u", &persist_mode))
persist_mode = PERSIST_MODE_NONE;
remote_desktop_session->persist_mode = persist_mode;
xdp_session_persistence_replace_restore_token_with_data (session,
REMOTE_DESKTOP_TABLE,
in_out_options,
&remote_desktop_session->restore_token);
return TRUE;
}
static gboolean
handle_select_devices (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
const char *arg_session_handle,
GVariant *arg_options)
{
Request *request = request_from_invocation (invocation);
Session *session;
RemoteDesktopSession *remote_desktop_session;
g_autoptr(GError) error = NULL;
g_autoptr(XdpDbusImplRequest) impl_request = NULL;
GVariantBuilder options_builder;
GVariant *options;
REQUEST_AUTOLOCK (request);
session = acquire_session (arg_session_handle, request);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
remote_desktop_session = (RemoteDesktopSession *)session;
if (!remote_desktop_session_can_select_devices (remote_desktop_session))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Invalid state");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
impl_request =
xdp_dbus_impl_request_proxy_new_sync (g_dbus_proxy_get_connection (G_DBUS_PROXY (impl)),
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
g_dbus_proxy_get_name (G_DBUS_PROXY (impl)),
request->id,
NULL, &error);
if (!impl_request)
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
request_set_impl_request (request, impl_request);
request_export (request, g_dbus_method_invocation_get_connection (invocation));
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
if (!xdp_filter_options (arg_options, &options_builder,
remote_desktop_select_devices_options,
G_N_ELEMENTS (remote_desktop_select_devices_options),
&error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
options = g_variant_builder_end (&options_builder);
/* If 'restore_token' is passed, lookup the corresponding data in the
* permission store and / or the GHashTable with transient permissions.
* Portal implementations do not have access to the restore token.
*/
if (!replace_remote_desktop_restore_token_with_data (session, &options, &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_object_set_qdata_full (G_OBJECT (request),
quark_request_session,
g_object_ref (session),
g_object_unref);
xdp_dbus_impl_remote_desktop_call_select_devices (impl,
request->id,
arg_session_handle,
xdp_app_info_get_id (request->app_info),
options,
NULL,
select_devices_done,
g_object_ref (request));
xdp_dbus_remote_desktop_complete_select_devices (object, invocation, request->id);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static void
replace_restore_remote_desktop_data_with_token (RemoteDesktopSession *remote_desktop_session,
GVariant **in_out_results)
{
xdp_session_persistence_replace_restore_data_with_token ((Session *) remote_desktop_session,
REMOTE_DESKTOP_TABLE,
in_out_results,
&remote_desktop_session->persist_mode,
&remote_desktop_session->restore_token,
&remote_desktop_session->restore_data);
}
static gboolean
process_results (RemoteDesktopSession *remote_desktop_session,
GVariant **in_out_results,
GError **error)
{
g_autoptr(GVariantIter) streams_iter = NULL;
GVariant *results = *in_out_results;
uint32_t devices = 0;
gboolean clipboard_enabled = FALSE;
if (g_variant_lookup (results, "streams", "a(ua{sv})", &streams_iter))
{
remote_desktop_session->streams =
collect_screen_cast_stream_data (streams_iter);
}
if (g_variant_lookup (results, "devices", "u", &devices))
remote_desktop_session->shared_devices = devices;
if (g_variant_lookup (results, "clipboard_enabled", "b", &clipboard_enabled))
remote_desktop_session->clipboard_enabled = clipboard_enabled;
replace_restore_remote_desktop_data_with_token (remote_desktop_session,
in_out_results);
return TRUE;
}
static void
start_done (GObject *source_object,
GAsyncResult *res,
gpointer data)
{
g_autoptr(Request) request = data;
Session *session;
RemoteDesktopSession *remote_desktop_session;
guint response = 2;
gboolean should_close_session;
GVariant *results = NULL;
g_autoptr(GError) error = NULL;
REQUEST_AUTOLOCK (request);
session = g_object_get_qdata (G_OBJECT (request), quark_request_session);
remote_desktop_session = (RemoteDesktopSession *)session;
SESSION_AUTOLOCK_UNREF (g_object_ref (session));
g_object_set_qdata (G_OBJECT (request), quark_request_session, NULL);
if (!xdp_dbus_impl_remote_desktop_call_start_finish (impl,
&response,
&results,
res,
&error))
{
g_dbus_error_strip_remote_error (error);
g_warning ("A backend call failed: %s", error->message);
}
should_close_session = !request->exported || response != 0;
if (request->exported)
{
if (response == 0)
{
if (!process_results (remote_desktop_session, &results, &error))
{
g_warning ("Could not start remote desktop session: %s",
error->message);
g_clear_error (&error);
g_clear_pointer (&results, g_variant_unref);
response = 2;
should_close_session = TRUE;
}
}
else
{
GVariantBuilder results_builder;
g_variant_builder_init (&results_builder, G_VARIANT_TYPE_VARDICT);
results = g_variant_builder_end (&results_builder);
}
xdp_dbus_request_emit_response (XDP_DBUS_REQUEST (request),
response, results);
request_unexport (request);
}
if (should_close_session)
{
session_close (session, TRUE);
}
else if (!session->closed)
{
g_debug ("remote desktop session owned by '%s' started", session->sender);
remote_desktop_session->state = REMOTE_DESKTOP_SESSION_STATE_STARTED;
}
}
static gboolean
handle_start (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
const char *arg_session_handle,
const char *arg_parent_window,
GVariant *arg_options)
{
Request *request = request_from_invocation (invocation);
Session *session;
RemoteDesktopSession *remote_desktop_session;
g_autoptr(GError) error = NULL;
g_autoptr(XdpDbusImplRequest) impl_request = NULL;
GVariantBuilder options_builder;
GVariant *options;
REQUEST_AUTOLOCK (request);
session = acquire_session (arg_session_handle, request);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
remote_desktop_session = (RemoteDesktopSession *)session;
switch (remote_desktop_session->state)
{
case REMOTE_DESKTOP_SESSION_STATE_INIT:
break;
case REMOTE_DESKTOP_SESSION_STATE_STARTED:
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Can only start once");
return G_DBUS_METHOD_INVOCATION_HANDLED;
case REMOTE_DESKTOP_SESSION_STATE_CLOSED:
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_object_set_data_full (G_OBJECT (request),
"window", g_strdup (arg_parent_window), g_free);
impl_request =
xdp_dbus_impl_request_proxy_new_sync (g_dbus_proxy_get_connection (G_DBUS_PROXY (impl)),
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
g_dbus_proxy_get_name (G_DBUS_PROXY (impl)),
request->id,
NULL, &error);
if (!impl_request)
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
request_set_impl_request (request, impl_request);
request_export (request, g_dbus_method_invocation_get_connection (invocation));
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
options = g_variant_builder_end (&options_builder);
g_object_set_qdata_full (G_OBJECT (request),
quark_request_session,
g_object_ref (session),
g_object_unref);
xdp_dbus_impl_remote_desktop_call_start (impl,
request->id,
arg_session_handle,
xdp_app_info_get_id (request->app_info),
arg_parent_window,
options,
NULL,
start_done,
g_object_ref (request));
xdp_dbus_remote_desktop_complete_start (object, invocation, request->id);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
check_notify (Session *session,
DeviceType device_type)
{
RemoteDesktopSession *remote_desktop_session = (RemoteDesktopSession *)session;
if (!remote_desktop_session->devices_selected || remote_desktop_session->uses_eis)
return FALSE;
switch (remote_desktop_session->state)
{
case REMOTE_DESKTOP_SESSION_STATE_STARTED:
break;
case REMOTE_DESKTOP_SESSION_STATE_INIT:
case REMOTE_DESKTOP_SESSION_STATE_CLOSED:
return FALSE;
}
if ((remote_desktop_session->shared_devices & device_type) == 0)
return FALSE;
return TRUE;
}
static gboolean
check_position (Session *session,
uint32_t stream,
double x,
double y)
{
RemoteDesktopSession *remote_desktop_session = (RemoteDesktopSession *)session;
GList *l;
for (l = remote_desktop_session->streams; l; l = l->next)
{
ScreenCastStream *stream = l->data;
int32_t width, height;
screen_cast_stream_get_size (stream, &width, &height);
if (x >= 0.0 && x < width &&
y >= 0.0 && y < height)
return TRUE;
}
return FALSE;
}
static XdpOptionKey remote_desktop_notify_options[] = {
};
static gboolean
handle_notify_pointer_motion (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
const char *arg_session_handle,
GVariant *arg_options,
double dx,
double dy)
{
Call *call = call_from_invocation (invocation);
Session *session;
GVariantBuilder options_builder;
GVariant *options;
g_autoptr(GError) error = NULL;
session = acquire_session_from_call (arg_session_handle, call);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
if (!check_notify (session, DEVICE_TYPE_POINTER))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is not allowed to call NotifyPointer methods");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
if (!xdp_filter_options (arg_options, &options_builder,
remote_desktop_notify_options,
G_N_ELEMENTS (remote_desktop_notify_options),
&error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
options = g_variant_builder_end (&options_builder);
xdp_dbus_impl_remote_desktop_call_notify_pointer_motion (impl,
session->id,
options,
dx, dy,
NULL, NULL, NULL);
xdp_dbus_remote_desktop_complete_notify_pointer_motion (object, invocation);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
handle_notify_pointer_motion_absolute (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
const char *arg_session_handle,
GVariant *arg_options,
uint32_t stream,
double x,
double y)
{
Call *call = call_from_invocation (invocation);
Session *session;
GVariantBuilder options_builder;
GVariant *options;
g_autoptr(GError) error = NULL;
session = acquire_session_from_call (arg_session_handle, call);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
if (!check_notify (session, DEVICE_TYPE_POINTER))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is not allowed to call NotifyPointer methods");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
if (!check_position (session, stream, x, y))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Invalid position");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
if (!xdp_filter_options (arg_options, &options_builder,
remote_desktop_notify_options,
G_N_ELEMENTS (remote_desktop_notify_options),
&error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
options = g_variant_builder_end (&options_builder);
xdp_dbus_impl_remote_desktop_call_notify_pointer_motion_absolute (impl,
session->id,
options,
stream,
x, y,
NULL, NULL, NULL);
xdp_dbus_remote_desktop_complete_notify_pointer_motion_absolute (object, invocation);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
handle_notify_pointer_button (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
const char *arg_session_handle,
GVariant *arg_options,
int32_t button,
uint32_t state)
{
Call *call = call_from_invocation (invocation);
Session *session;
GVariantBuilder options_builder;
GVariant *options;
g_autoptr(GError) error = NULL;
session = acquire_session_from_call (arg_session_handle, call);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
if (!check_notify (session, DEVICE_TYPE_POINTER))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is not allowed to call NotifyPointer methods");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
if (!xdp_filter_options (arg_options, &options_builder,
remote_desktop_notify_options,
G_N_ELEMENTS (remote_desktop_notify_options),
&error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
options = g_variant_builder_end (&options_builder);
xdp_dbus_impl_remote_desktop_call_notify_pointer_button (impl,
session->id,
options,
button,
state,
NULL, NULL, NULL);
xdp_dbus_remote_desktop_complete_notify_pointer_button (object, invocation);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static XdpOptionKey remote_desktop_notify_pointer_axis_options[] = {
{ "finish", G_VARIANT_TYPE_BOOLEAN, NULL },
};
static gboolean
handle_notify_pointer_axis (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
const char *arg_session_handle,
GVariant *arg_options,
double dx,
double dy)
{
Call *call = call_from_invocation (invocation);
Session *session;
GVariantBuilder options_builder;
GVariant *options;
g_autoptr(GError) error = NULL;
session = acquire_session_from_call (arg_session_handle, call);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
if (!check_notify (session, DEVICE_TYPE_POINTER))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is not allowed to call NotifyPointer methods");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
if (!xdp_filter_options (arg_options, &options_builder,
remote_desktop_notify_pointer_axis_options,
G_N_ELEMENTS (remote_desktop_notify_pointer_axis_options),
&error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
options = g_variant_builder_end (&options_builder);
xdp_dbus_impl_remote_desktop_call_notify_pointer_axis (impl,
session->id,
options,
dx, dy,
NULL, NULL, NULL);
xdp_dbus_remote_desktop_complete_notify_pointer_axis (object, invocation);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
handle_notify_pointer_axis_discrete (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
const char *arg_session_handle,
GVariant *arg_options,
uint32_t axis,
int32_t steps)
{
Call *call = call_from_invocation (invocation);
Session *session;
GVariantBuilder options_builder;
GVariant *options;
g_autoptr(GError) error = NULL;
session = acquire_session_from_call (arg_session_handle, call);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
if (!check_notify (session, DEVICE_TYPE_POINTER))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is not allowed to call NotifyPointer methods");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
if (!xdp_filter_options (arg_options, &options_builder,
remote_desktop_notify_options,
G_N_ELEMENTS (remote_desktop_notify_options),
&error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
options = g_variant_builder_end (&options_builder);
xdp_dbus_impl_remote_desktop_call_notify_pointer_axis_discrete (impl,
session->id,
options,
axis,
steps,
NULL, NULL, NULL);
xdp_dbus_remote_desktop_complete_notify_pointer_axis_discrete (object, invocation);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
handle_notify_keyboard_keycode (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
const char *arg_session_handle,
GVariant *arg_options,
int32_t keycode,
uint32_t state)
{
Call *call = call_from_invocation (invocation);
Session *session;
GVariantBuilder options_builder;
GVariant *options;
g_autoptr(GError) error = NULL;
session = acquire_session_from_call (arg_session_handle, call);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
if (!check_notify (session, DEVICE_TYPE_KEYBOARD))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is not allowed to call NotifyPointer methods");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
if (!xdp_filter_options (arg_options, &options_builder,
remote_desktop_notify_options,
G_N_ELEMENTS (remote_desktop_notify_options),
&error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
options = g_variant_builder_end (&options_builder);
xdp_dbus_impl_remote_desktop_call_notify_keyboard_keycode (impl,
session->id,
options,
keycode,
state,
NULL, NULL, NULL);
xdp_dbus_remote_desktop_complete_notify_keyboard_keycode (object, invocation);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
handle_notify_keyboard_keysym (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
const char *arg_session_handle,
GVariant *arg_options,
int32_t keysym,
uint32_t state)
{
Call *call = call_from_invocation (invocation);
Session *session;
GVariantBuilder options_builder;
GVariant *options;
g_autoptr(GError) error = NULL;
session = acquire_session_from_call (arg_session_handle, call);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
if (!check_notify (session, DEVICE_TYPE_KEYBOARD))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is not allowed to call NotifyKeyboard methods");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
if (!xdp_filter_options (arg_options, &options_builder,
remote_desktop_notify_options,
G_N_ELEMENTS (remote_desktop_notify_options),
&error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
options = g_variant_builder_end (&options_builder);
xdp_dbus_impl_remote_desktop_call_notify_keyboard_keysym (impl,
session->id,
options,
keysym,
state,
NULL, NULL, NULL);
xdp_dbus_remote_desktop_complete_notify_keyboard_keysym (object, invocation);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
handle_notify_touch_down (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
const char *arg_session_handle,
GVariant *arg_options,
uint32_t stream,
uint32_t slot,
double x,
double y)
{
Call *call = call_from_invocation (invocation);
Session *session;
GVariantBuilder options_builder;
GVariant *options;
g_autoptr(GError) error = NULL;
session = acquire_session_from_call (arg_session_handle, call);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
if (!check_notify (session, DEVICE_TYPE_TOUCHSCREEN))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is not allowed to call NotifyTouch methods");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
if (!check_position (session, stream, x, y))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Invalid position");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
if (!xdp_filter_options (arg_options, &options_builder,
remote_desktop_notify_options,
G_N_ELEMENTS (remote_desktop_notify_options),
&error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
options = g_variant_builder_end (&options_builder);
xdp_dbus_impl_remote_desktop_call_notify_touch_down (impl,
session->id,
options,
stream,
slot,
x, y,
NULL, NULL, NULL);
xdp_dbus_remote_desktop_complete_notify_touch_down (object, invocation);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
handle_notify_touch_motion (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
const char *arg_session_handle,
GVariant *arg_options,
uint32_t stream,
uint32_t slot,
double x,
double y)
{
Call *call = call_from_invocation (invocation);
Session *session;
GVariantBuilder options_builder;
GVariant *options;
g_autoptr(GError) error = NULL;
session = acquire_session_from_call (arg_session_handle, call);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
if (!check_notify (session, DEVICE_TYPE_TOUCHSCREEN))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is not allowed to call NotifyTouch methods");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
if (!check_position (session, stream, x, y))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Invalid position");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
if (!xdp_filter_options (arg_options, &options_builder,
remote_desktop_notify_options,
G_N_ELEMENTS (remote_desktop_notify_options),
&error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
options = g_variant_builder_end (&options_builder);
xdp_dbus_impl_remote_desktop_call_notify_touch_motion (impl,
session->id,
options,
stream,
slot,
x, y,
NULL, NULL, NULL);
xdp_dbus_remote_desktop_complete_notify_touch_motion (object, invocation);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
handle_notify_touch_up (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
const char *arg_session_handle,
GVariant *arg_options,
uint32_t slot)
{
Call *call = call_from_invocation (invocation);
Session *session;
GVariantBuilder options_builder;
GVariant *options;
g_autoptr(GError) error = NULL;
session = acquire_session_from_call (arg_session_handle, call);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
if (!check_notify (session, DEVICE_TYPE_TOUCHSCREEN))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is not allowed to call NotifyTouch methods");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
if (!xdp_filter_options (arg_options, &options_builder,
remote_desktop_notify_options,
G_N_ELEMENTS (remote_desktop_notify_options),
&error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
options = g_variant_builder_end (&options_builder);
xdp_dbus_impl_remote_desktop_call_notify_touch_up (impl,
session->id,
options,
slot,
NULL, NULL, NULL);
xdp_dbus_remote_desktop_complete_notify_touch_up (object, invocation);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static XdpOptionKey remote_desktop_connect_to_eis_options[] = {
};
static gboolean
handle_connect_to_eis (XdpDbusRemoteDesktop *object,
GDBusMethodInvocation *invocation,
GUnixFDList *in_fd_list,
const char *arg_session_handle,
GVariant *arg_options)
{
Call *call = call_from_invocation (invocation);
Session *session;
RemoteDesktopSession *remote_desktop_session;
g_autoptr(GUnixFDList) out_fd_list = NULL;
g_autoptr(GError) error = NULL;
GVariantBuilder options_builder;
GVariant *fd;
session = acquire_session_from_call (arg_session_handle, call);
if (!session)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
SESSION_AUTOLOCK_UNREF (session);
if (!is_remote_desktop_session (session))
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid session");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
remote_desktop_session = (RemoteDesktopSession *)session;
if (remote_desktop_session->uses_eis)
{
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is already connected");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
switch (remote_desktop_session->state)
{
case REMOTE_DESKTOP_SESSION_STATE_STARTED:
break;
case REMOTE_DESKTOP_SESSION_STATE_INIT:
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is not ready");
return G_DBUS_METHOD_INVOCATION_HANDLED;
case REMOTE_DESKTOP_SESSION_STATE_CLOSED:
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"Session is already closed");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
if (!xdp_filter_options (arg_options, &options_builder,
remote_desktop_connect_to_eis_options,
G_N_ELEMENTS (remote_desktop_connect_to_eis_options),
&error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
if (!xdp_dbus_impl_remote_desktop_call_connect_to_eis_sync (impl,
arg_session_handle,
xdp_app_info_get_id (call->app_info),
g_variant_builder_end (&options_builder),
in_fd_list,
&fd,
&out_fd_list,
NULL,
&error))
{
g_warning ("Failed to ConnectToEIS: %s", error->message);
g_dbus_method_invocation_return_gerror (invocation, error);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
remote_desktop_session->uses_eis = TRUE;
xdp_dbus_remote_desktop_complete_connect_to_eis (object, invocation, out_fd_list, fd);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static void
remote_desktop_iface_init (XdpDbusRemoteDesktopIface *iface)
{
iface->handle_create_session = handle_create_session;
iface->handle_select_devices = handle_select_devices;
iface->handle_start = handle_start;
iface->handle_notify_pointer_motion = handle_notify_pointer_motion;
iface->handle_notify_pointer_motion_absolute = handle_notify_pointer_motion_absolute;
iface->handle_notify_pointer_button = handle_notify_pointer_button;
iface->handle_notify_pointer_axis = handle_notify_pointer_axis;
iface->handle_notify_pointer_axis_discrete = handle_notify_pointer_axis_discrete;
iface->handle_notify_keyboard_keycode = handle_notify_keyboard_keycode;
iface->handle_notify_keyboard_keysym = handle_notify_keyboard_keysym;
iface->handle_notify_touch_down = handle_notify_touch_down;
iface->handle_notify_touch_motion = handle_notify_touch_motion;
iface->handle_notify_touch_up = handle_notify_touch_up;
iface->handle_connect_to_eis = handle_connect_to_eis;
}
static void
sync_supported_device_types (RemoteDesktop *remote_desktop)
{
unsigned int available_device_types;
available_device_types =
xdp_dbus_impl_remote_desktop_get_available_device_types (impl);
xdp_dbus_remote_desktop_set_available_device_types (XDP_DBUS_REMOTE_DESKTOP (remote_desktop),
available_device_types);
}
static void
on_supported_device_types_changed (GObject *gobject,
GParamSpec *pspec,
RemoteDesktop *remote_desktop)
{
sync_supported_device_types (remote_desktop);
}
static void
remote_desktop_init (RemoteDesktop *remote_desktop)
{
xdp_dbus_remote_desktop_set_version (XDP_DBUS_REMOTE_DESKTOP (remote_desktop), 2);
g_signal_connect (impl, "notify::supported-device-types",
G_CALLBACK (on_supported_device_types_changed),
remote_desktop);
sync_supported_device_types (remote_desktop);
}
static void
remote_desktop_class_init (RemoteDesktopClass *klass)
{
}
GDBusInterfaceSkeleton *
remote_desktop_create (GDBusConnection *connection,
const char *dbus_name)
{
g_autoptr(GError) error = NULL;
impl = xdp_dbus_impl_remote_desktop_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
dbus_name,
DESKTOP_PORTAL_OBJECT_PATH,
NULL,
&error);
if (impl == NULL)
{
g_warning ("Failed to create remote desktop proxy: %s", error->message);
return NULL;
}
g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (impl), G_MAXINT);
remote_desktop = g_object_new (remote_desktop_get_type (), NULL);
return G_DBUS_INTERFACE_SKELETON (remote_desktop);
}
static void
remote_desktop_session_close (Session *session)
{
RemoteDesktopSession *remote_desktop_session = (RemoteDesktopSession *)session;
remote_desktop_session->state = REMOTE_DESKTOP_SESSION_STATE_CLOSED;
g_debug ("remote desktop session owned by '%s' closed", session->sender);
}
static void
remote_desktop_session_finalize (GObject *object)
{
RemoteDesktopSession *remote_desktop_session = (RemoteDesktopSession *)object;
g_list_free_full (remote_desktop_session->streams,
(GDestroyNotify)screen_cast_stream_free);
G_OBJECT_CLASS (remote_desktop_session_parent_class)->finalize (object);
}
static void
remote_desktop_session_init (RemoteDesktopSession *remote_desktop_session)
{
}
static void
remote_desktop_session_class_init (RemoteDesktopSessionClass *klass)
{
GObjectClass *object_class;
SessionClass *session_class;
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = remote_desktop_session_finalize;
session_class = (SessionClass *)klass;
session_class->close = remote_desktop_session_close;
quark_request_session =
g_quark_from_static_string ("-xdp-request-remote-desktop-session");
}