mirror of
https://gitlab.gnome.org/GNOME/libmks.git
synced 2024-12-22 05:35:21 +00:00
482ec4f9f6
If multiple instances of QEMU are running, with the currently exposed API you can't interact with queued name owners and only with the main process. Also modify mks-connect to make use of this and display the unique name there as well.
875 lines
28 KiB
C
875 lines
28 KiB
C
/*
|
|
* mks-session.c
|
|
*
|
|
* Copyright 2023 Christian Hergert <chergert@redhat.com>
|
|
*
|
|
* This library 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.1 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 General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "mks-device-private.h"
|
|
#include "mks-read-only-list-model-private.h"
|
|
#include "mks-qemu.h"
|
|
#include "mks-screen.h"
|
|
#include "mks-session.h"
|
|
|
|
/**
|
|
* MksSession:
|
|
*
|
|
* Session connected to a QEMU VM
|
|
*
|
|
* The `MksSession` represents a connection to a QEMU VM instance. It contains
|
|
* devices such as the mouse, keyboard, and screen which can be used with GTK.
|
|
*
|
|
* You may monitor [property@Mks.Session:devices] using [signal@Gio.ListModel::items-changed] to be
|
|
* notified of changes to available devices in the session.
|
|
*
|
|
* # Connecting To QEMU
|
|
*
|
|
* To use `MksSession`, you should create your QEMU instance using `dbus` for
|
|
* the various devices that support it. You'll need to provide your P2P D-Bus
|
|
* address when connecting to QEMU.
|
|
*
|
|
* Using the same [class@Gio.DBusConnection], create a `MksSession` with
|
|
* [func@Mks.Session.new_for_connection]. The `MksSession` instance will negotiate
|
|
* with the peer to determine what devices are available and expose them
|
|
* via the [property@Mks.Session:devices] [iface@Gio.ListModel].
|
|
*
|
|
* # Creating Widgets
|
|
*
|
|
* You can create a new widget to embed in your application by calling
|
|
* [method@Mks.Session.ref_screen] and set the screen for the [class@Mks.Display]
|
|
* with [method@Mks.Display.set_screen].
|
|
*/
|
|
|
|
#define QEMU_BUS_NAME "org.qemu"
|
|
|
|
static gboolean mks_session_initable_init (GInitable *initable,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
static void mks_session_async_initable_init_async (GAsyncInitable *async_initable,
|
|
int io_priority,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data);
|
|
static gboolean mks_session_async_initable_init_finish (GAsyncInitable *async_initable,
|
|
GAsyncResult *result,
|
|
GError **error);
|
|
|
|
struct _MksSession
|
|
{
|
|
GObject parent_instance;
|
|
|
|
/* @connection is used to communicate with the QEMU instance. It is expected
|
|
* to be a private point-to-point connection over a Unix socket, socketpair(),
|
|
* or other channel capable of passing FDs between peers.
|
|
*/
|
|
GDBusConnection *connection;
|
|
|
|
/* As devices are discovered from the QEMU instance, MksDevice-based objects
|
|
* are created for them and stored in @devices. Applications will work with
|
|
* these objects to perform operations on the QEMU instance. When we discover
|
|
* the devices have been removed, we drop them from the listmodel.
|
|
*/
|
|
GListStore *devices;
|
|
|
|
/* @devices_read_only is a #MksReadOnlyListModel of @devices so that we may
|
|
* allow observation of the #GListModel, but without access to perturb it.
|
|
* Such opaqueness provides us a bit more assurance we don't leak
|
|
* implementation details into the API.
|
|
*/
|
|
GListModel *devices_read_only;
|
|
|
|
/* An object manager client is used to monitor for new objects exported by
|
|
* the QEMU instance. Those objects are then wrapped by MksDevice objects
|
|
* as necessary and exported to consumers via @devices.
|
|
*/
|
|
GDBusObjectManager *object_manager;
|
|
|
|
/* A GDBusObjectProxy to the `/org/qemu/Display1/VM` instance. Generally
|
|
* this used via the `org.qemu.Display1.VM` interface. We track the
|
|
* top-level object-manager instance so that we can detect easily when
|
|
* the VM has been removed from the peer. (Which could happen if it is
|
|
* running on a private D-Bus rather than a socketpair().
|
|
*/
|
|
MksQemuObject *vm_object;
|
|
MksQemuVM *vm;
|
|
|
|
char *name;
|
|
char *uuid;
|
|
char *bus_name;
|
|
};
|
|
|
|
static void
|
|
initable_iface_init (GInitableIface *iface)
|
|
{
|
|
iface->init = mks_session_initable_init;
|
|
}
|
|
|
|
static void
|
|
async_initable_iface_init (GAsyncInitableIface *iface)
|
|
{
|
|
iface->init_async = mks_session_async_initable_init_async;
|
|
iface->init_finish = mks_session_async_initable_init_finish;
|
|
}
|
|
|
|
G_DEFINE_FINAL_TYPE_WITH_CODE (MksSession, mks_session, G_TYPE_OBJECT,
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init))
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_CONNECTION,
|
|
PROP_BUS_NAME,
|
|
PROP_DEVICES,
|
|
PROP_NAME,
|
|
PROP_UUID,
|
|
N_PROPS
|
|
};
|
|
|
|
static GParamSpec *properties [N_PROPS];
|
|
|
|
static void
|
|
mks_session_set_connection (MksSession *self,
|
|
GDBusConnection *connection)
|
|
{
|
|
g_assert (MKS_IS_SESSION (self));
|
|
g_assert (!connection || G_IS_DBUS_CONNECTION (connection));
|
|
g_assert (self->connection == NULL);
|
|
|
|
if (connection == NULL)
|
|
g_critical ("%s created without a GDBusConnection, this cannot work.",
|
|
G_OBJECT_TYPE_NAME (self));
|
|
else
|
|
self->connection = g_object_ref (connection);
|
|
}
|
|
|
|
static void
|
|
mks_session_add_device (MksSession *self,
|
|
MksDevice *device)
|
|
{
|
|
g_assert (MKS_IS_SESSION (self));
|
|
g_assert (!device || MKS_IS_DEVICE (device));
|
|
|
|
if (device == NULL)
|
|
return;
|
|
|
|
g_list_store_append (self->devices, device);
|
|
}
|
|
|
|
static void
|
|
mks_session_set_name (MksSession *self,
|
|
const char *name)
|
|
{
|
|
g_assert (MKS_IS_SESSION (self));
|
|
|
|
if (g_set_str (&self->name, name))
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NAME]);
|
|
}
|
|
|
|
static void
|
|
mks_session_set_uuid (MksSession *self,
|
|
const char *uuid)
|
|
{
|
|
g_assert (MKS_IS_SESSION (self));
|
|
|
|
if (g_set_str (&self->uuid, uuid))
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_UUID]);
|
|
}
|
|
|
|
static void
|
|
mks_session_vm_notify_cb (MksSession *self,
|
|
GParamSpec *pspec,
|
|
MksQemuVM *vm)
|
|
{
|
|
g_assert (MKS_IS_SESSION (self));
|
|
g_assert (pspec != NULL);
|
|
g_assert (MKS_QEMU_IS_VM (vm));
|
|
|
|
if (strcmp (pspec->name, "name") == 0)
|
|
mks_session_set_name (self, mks_qemu_vm_get_name (vm));
|
|
else if (strcmp (pspec->name, "uuid") == 0)
|
|
mks_session_set_uuid (self, mks_qemu_vm_get_uuid (vm));
|
|
}
|
|
|
|
static void
|
|
mks_session_set_vm (MksSession *self,
|
|
MksQemuObject *vm_object,
|
|
MksQemuVM *vm)
|
|
{
|
|
g_assert (MKS_IS_SESSION (self));
|
|
g_assert (MKS_QEMU_IS_OBJECT (vm_object));
|
|
g_assert (MKS_QEMU_IS_VM (vm));
|
|
|
|
if (self->vm != NULL)
|
|
return;
|
|
|
|
g_set_object (&self->vm_object, vm_object);
|
|
g_set_object (&self->vm, vm);
|
|
|
|
g_signal_connect_object (vm,
|
|
"notify",
|
|
G_CALLBACK (mks_session_vm_notify_cb),
|
|
self,
|
|
G_CONNECT_SWAPPED);
|
|
mks_session_set_name (self, mks_qemu_vm_get_name (vm));
|
|
mks_session_set_uuid (self, mks_qemu_vm_get_uuid (vm));
|
|
}
|
|
|
|
static void
|
|
mks_session_object_manager_object_added_cb (MksSession *self,
|
|
MksQemuObject *object,
|
|
GDBusObjectManager *manager)
|
|
{
|
|
g_autolist(GDBusInterface) interfaces = NULL;
|
|
|
|
g_assert (MKS_IS_SESSION (self));
|
|
g_assert (MKS_QEMU_IS_OBJECT (object));
|
|
g_assert (G_IS_DBUS_OBJECT_MANAGER (manager));
|
|
|
|
interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (object));
|
|
|
|
for (const GList *iter = interfaces; iter; iter = iter->next)
|
|
{
|
|
GDBusInterface *iface = iter->data;
|
|
|
|
if (MKS_QEMU_IS_VM (iface))
|
|
mks_session_set_vm (self, object, MKS_QEMU_VM (iface));
|
|
else if (MKS_QEMU_IS_CONSOLE (iface))
|
|
mks_session_add_device (self, _mks_device_new (MKS_TYPE_SCREEN, self, object));
|
|
}
|
|
}
|
|
|
|
static void
|
|
mks_session_object_manager_object_removed_cb (MksSession *self,
|
|
MksQemuObject *object,
|
|
GDBusObjectManager *manager)
|
|
{
|
|
g_assert (MKS_IS_SESSION (self));
|
|
g_assert (MKS_QEMU_IS_OBJECT (object));
|
|
g_assert (G_IS_DBUS_OBJECT_MANAGER (manager));
|
|
|
|
if (object == self->vm_object)
|
|
{
|
|
g_clear_object (&self->vm);
|
|
g_clear_object (&self->vm_object);
|
|
g_list_store_remove_all (self->devices);
|
|
}
|
|
}
|
|
|
|
static void
|
|
mks_session_set_object_manager (MksSession *self,
|
|
GDBusObjectManager *object_manager)
|
|
{
|
|
g_assert (MKS_IS_SESSION (self));
|
|
g_assert (!object_manager || MKS_QEMU_IS_OBJECT_MANAGER_CLIENT (object_manager));
|
|
g_assert (self->object_manager == NULL);
|
|
|
|
if (g_set_object (&self->object_manager, object_manager))
|
|
{
|
|
g_autolist(GDBusObjectProxy) objects = NULL;
|
|
|
|
g_signal_connect_object (object_manager,
|
|
"object-added",
|
|
G_CALLBACK (mks_session_object_manager_object_added_cb),
|
|
self,
|
|
G_CONNECT_SWAPPED);
|
|
g_signal_connect_object (object_manager,
|
|
"object-removed",
|
|
G_CALLBACK (mks_session_object_manager_object_removed_cb),
|
|
self,
|
|
G_CONNECT_SWAPPED);
|
|
|
|
objects = g_dbus_object_manager_get_objects (object_manager);
|
|
for (const GList *iter = objects; iter; iter = iter->next)
|
|
mks_session_object_manager_object_added_cb (self, iter->data, object_manager);
|
|
}
|
|
}
|
|
|
|
static void
|
|
mks_session_dispose (GObject *object)
|
|
{
|
|
MksSession *self = (MksSession *)object;
|
|
|
|
if (self->devices != NULL)
|
|
g_list_store_remove_all (self->devices);
|
|
|
|
g_clear_object (&self->object_manager);
|
|
g_clear_object (&self->vm);
|
|
g_clear_object (&self->vm_object);
|
|
g_clear_pointer (&self->name, g_free);
|
|
g_clear_pointer (&self->uuid, g_free);
|
|
g_clear_pointer (&self->bus_name, g_free);
|
|
|
|
G_OBJECT_CLASS (mks_session_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
mks_session_finalize (GObject *object)
|
|
{
|
|
MksSession *self = (MksSession *)object;
|
|
|
|
g_clear_object (&self->devices);
|
|
g_clear_object (&self->devices_read_only);
|
|
g_clear_object (&self->connection);
|
|
|
|
G_OBJECT_CLASS (mks_session_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
mks_session_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MksSession *self = MKS_SESSION (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_CONNECTION:
|
|
g_value_set_object (value, mks_session_get_connection (self));
|
|
break;
|
|
|
|
case PROP_BUS_NAME:
|
|
g_value_set_string (value, self->bus_name);
|
|
break;
|
|
|
|
case PROP_DEVICES:
|
|
g_value_set_object (value, mks_session_get_devices (self));
|
|
break;
|
|
|
|
case PROP_NAME:
|
|
g_value_set_string (value, mks_session_get_name (self));
|
|
break;
|
|
|
|
case PROP_UUID:
|
|
g_value_set_string (value, mks_session_get_uuid (self));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
mks_session_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MksSession *self = MKS_SESSION (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_CONNECTION:
|
|
mks_session_set_connection (self, g_value_get_object (value));
|
|
break;
|
|
case PROP_BUS_NAME:
|
|
g_set_str (&self->bus_name, g_value_get_string (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
mks_session_class_init (MksSessionClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = mks_session_dispose;
|
|
object_class->finalize = mks_session_finalize;
|
|
object_class->get_property = mks_session_get_property;
|
|
object_class->set_property = mks_session_set_property;
|
|
|
|
/**
|
|
* MksSession:connection:
|
|
*
|
|
* The [class@Gio.DBusConnection] that is used to communicate with QEMU.
|
|
*/
|
|
properties [PROP_CONNECTION] =
|
|
g_param_spec_object ("connection", NULL, NULL,
|
|
G_TYPE_DBUS_CONNECTION,
|
|
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* MksSession:bus-name:
|
|
*
|
|
* The unique connection name to connect to or `org.qemu` as fallback.
|
|
*/
|
|
properties [PROP_BUS_NAME] =
|
|
g_param_spec_string ("bus-name", NULL, NULL,
|
|
QEMU_BUS_NAME,
|
|
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* MksSession:devices:
|
|
*
|
|
* A [iface@Gio.ListModel] of devices that have been
|
|
* discovered on the [class@Gio.DBusConnection] to QEMU.
|
|
*/
|
|
properties [PROP_DEVICES] =
|
|
g_param_spec_object ("devices", NULL, NULL,
|
|
G_TYPE_LIST_MODEL,
|
|
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* MksSession:name:
|
|
*
|
|
* The VM name as specified by the QEMU instance.
|
|
*/
|
|
properties [PROP_NAME] =
|
|
g_param_spec_string ("name", NULL, NULL,
|
|
NULL,
|
|
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* MksSession:uuid:
|
|
*
|
|
* The VM unique identifier specified by the QEMU instance.
|
|
*/
|
|
properties [PROP_UUID] =
|
|
g_param_spec_string ("uuid", NULL, NULL,
|
|
NULL,
|
|
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_properties (object_class, N_PROPS, properties);
|
|
}
|
|
|
|
static void
|
|
mks_session_init (MksSession *self)
|
|
{
|
|
self->devices = g_list_store_new (MKS_TYPE_DEVICE);
|
|
self->devices_read_only = mks_read_only_list_model_new (G_LIST_MODEL (self->devices));
|
|
}
|
|
|
|
static gboolean
|
|
mks_session_initable_init (GInitable *initable,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
MksSession *self = (MksSession *)initable;
|
|
g_autoptr(GDBusObjectManager) object_manager = NULL;
|
|
|
|
g_assert (MKS_IS_SESSION (self));
|
|
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
|
|
|
|
if (self->connection == NULL)
|
|
{
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_NOT_CONNECTED,
|
|
"Not connected");
|
|
return FALSE;
|
|
}
|
|
|
|
object_manager =
|
|
mks_qemu_object_manager_client_new_sync (self->connection,
|
|
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
|
|
self->bus_name,
|
|
"/org/qemu/Display1",
|
|
cancellable,
|
|
error);
|
|
|
|
mks_session_set_object_manager (self, object_manager);
|
|
|
|
return object_manager != NULL;
|
|
}
|
|
|
|
static void
|
|
mks_session_async_initable_vm_cb (GObject *object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
g_autoptr(GDBusObjectManager) object_manager = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GTask) task = user_data;
|
|
MksSession *self;
|
|
|
|
g_assert (G_IS_ASYNC_RESULT (result));
|
|
g_assert (G_IS_TASK (task));
|
|
|
|
self = g_task_get_source_object (task);
|
|
object_manager = mks_qemu_object_manager_client_new_finish (result, &error);
|
|
|
|
g_assert (MKS_IS_SESSION (self));
|
|
g_assert (!object_manager || MKS_QEMU_IS_OBJECT_MANAGER_CLIENT (object_manager));
|
|
|
|
mks_session_set_object_manager (self, object_manager);
|
|
|
|
if (error != NULL)
|
|
g_task_return_error (task, g_steal_pointer (&error));
|
|
else
|
|
g_task_return_boolean (task, TRUE);
|
|
}
|
|
|
|
static void
|
|
mks_session_async_initable_init_async (GAsyncInitable *async_initable,
|
|
int io_priority,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
MksSession *self = (MksSession *)async_initable;
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_assert (MKS_IS_SESSION (self));
|
|
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
|
|
|
|
task = g_task_new (self, cancellable, callback, user_data);
|
|
g_task_set_source_tag (task, mks_session_async_initable_init_async);
|
|
g_task_set_priority (task, io_priority);
|
|
|
|
if (self->connection == NULL)
|
|
g_task_return_new_error (task,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_NOT_CONNECTED,
|
|
"Not connected");
|
|
else
|
|
mks_qemu_object_manager_client_new (self->connection,
|
|
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
|
|
self->bus_name,
|
|
"/org/qemu/Display1",
|
|
cancellable,
|
|
mks_session_async_initable_vm_cb,
|
|
g_steal_pointer (&task));
|
|
}
|
|
|
|
static gboolean
|
|
mks_session_async_initable_init_finish (GAsyncInitable *async_initable,
|
|
GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
MksSession *self = (MksSession *)async_initable;
|
|
|
|
g_assert (MKS_IS_SESSION (self));
|
|
g_assert (G_IS_TASK (result));
|
|
|
|
return g_task_propagate_boolean (G_TASK (result), error);
|
|
}
|
|
|
|
/**
|
|
* mks_session_get_devices:
|
|
* @self: a #MksSession
|
|
*
|
|
* Gets a #GListModel of devices connected to the session.
|
|
*
|
|
* Returns: (transfer none): a #GListModel of #MksDevice
|
|
*/
|
|
GListModel *
|
|
mks_session_get_devices (MksSession *self)
|
|
{
|
|
g_return_val_if_fail (MKS_IS_SESSION (self), NULL);
|
|
|
|
return self->devices_read_only;
|
|
}
|
|
|
|
static void
|
|
mks_session_new_for_connection_cb (GObject *object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
MksSession *self = (MksSession *)object;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GTask) task = user_data;
|
|
|
|
g_assert (MKS_IS_SESSION (self));
|
|
g_assert (G_IS_ASYNC_RESULT (result));
|
|
g_assert (G_IS_TASK (task));
|
|
|
|
if (!g_async_initable_init_finish (G_ASYNC_INITABLE (self), result, &error))
|
|
g_task_return_error (task, g_steal_pointer (&error));
|
|
else
|
|
g_task_return_pointer (task, g_object_ref (self), g_object_unref);
|
|
}
|
|
|
|
/**
|
|
* mks_session_new_for_connection:
|
|
* @connection: a #GDBusConnection
|
|
* @io_priority: priority for IO operations
|
|
* @cancellable: (nullable): a #GCancellable or %NULL
|
|
* @callback: a callback to execute upon completion of the operation
|
|
* @user_data: closure data for @callback
|
|
*
|
|
* Creates a #MksSession which communicates using @connection.
|
|
*
|
|
* The [class@Gio.DBusConnection] should be a private D-Bus connection to a QEMU
|
|
* instance which has devices created using the "dbus" backend.
|
|
|
|
* @callback will be executed when the session has been created or
|
|
* failed to create.
|
|
*
|
|
* This function will not block the calling thread.
|
|
*
|
|
* use [ctor@Mks.Session.new_for_connection_finish] to get the result of
|
|
* this operation.
|
|
*/
|
|
void
|
|
mks_session_new_for_connection (GDBusConnection *connection,
|
|
int io_priority,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
mks_session_new_for_connection_with_name (connection,
|
|
QEMU_BUS_NAME,
|
|
io_priority,
|
|
cancellable,
|
|
callback,
|
|
user_data);
|
|
}
|
|
|
|
/**
|
|
* mks_session_new_for_connection_finish:
|
|
* @result: a #GAsyncResult provided to callback
|
|
* @error: (nullable): a location for a #GError or %NULL
|
|
*
|
|
* Completes a request to create a #MksSession for a [class@Gio.DBusConnection].
|
|
*
|
|
* Returns: (transfer full): a #MksSession if successful; otherwise %NULL
|
|
* and @error is set.
|
|
*/
|
|
MksSession *
|
|
mks_session_new_for_connection_finish (GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
g_return_val_if_fail (G_IS_TASK (result), NULL);
|
|
|
|
return g_task_propagate_pointer (G_TASK (result), error);
|
|
}
|
|
|
|
/**
|
|
* mks_session_new_for_connection_sync:
|
|
* @connection: a private #GDBusConnetion to a QEMU instance
|
|
* @cancellable: (nullable): a #GCancellable, or %NULL
|
|
* @error: (nullable): a location for a #GError, or %NULL
|
|
*
|
|
* Synchronously creates a new #MksSession instance.
|
|
*
|
|
* This may block while the QEMU instance is contacted to query for
|
|
* initial devices and VM status.
|
|
*
|
|
* Returns: (transfer full): a #MksSession if successful; otherwise %NULL
|
|
* and @error is set.
|
|
*/
|
|
MksSession *
|
|
mks_session_new_for_connection_sync (GDBusConnection *connection,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
return mks_session_new_for_connection_with_name_sync (connection,
|
|
QEMU_BUS_NAME,
|
|
cancellable,
|
|
error);
|
|
}
|
|
|
|
/**
|
|
* mks_session_new_for_connection_with_name:
|
|
* @connection: a #GDBusConnection
|
|
* @bus_name: The unique name to connect
|
|
* @io_priority: priority for IO operations
|
|
* @cancellable: (nullable): a #GCancellable or %NULL
|
|
* @callback: a callback to execute upon completion of the operation
|
|
* @user_data: closure data for @callback
|
|
*
|
|
* Creates a #MksSession which communicates using @connection.
|
|
*
|
|
* The constructor is similar to [func@Mks.Session.new_for_connection] but allows
|
|
* to set the bus name to something else than the default `org.qemu`.
|
|
*
|
|
* use [ctor@Mks.Session.new_for_connection_with_name_finish] to get the result of
|
|
* this operation.
|
|
*/
|
|
void
|
|
mks_session_new_for_connection_with_name (GDBusConnection *connection,
|
|
const char *bus_name,
|
|
int io_priority,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
g_autoptr(MksSession) self = NULL;
|
|
g_autoptr(GTask) task = NULL;
|
|
|
|
g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
|
|
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
|
|
g_return_if_fail (bus_name != NULL);
|
|
|
|
self = g_object_new (MKS_TYPE_SESSION,
|
|
"connection", connection,
|
|
"bus-name", bus_name,
|
|
NULL);
|
|
|
|
task = g_task_new (self, cancellable, callback, user_data);
|
|
g_task_set_source_tag (task, mks_session_new_for_connection);
|
|
g_task_set_priority (task, io_priority);
|
|
|
|
g_async_initable_init_async (G_ASYNC_INITABLE (self),
|
|
io_priority,
|
|
cancellable,
|
|
mks_session_new_for_connection_cb,
|
|
g_steal_pointer (&task));
|
|
}
|
|
|
|
/**
|
|
* mks_session_new_for_connection_with_name_finish:
|
|
* @result: a #GAsyncResult provided to callback
|
|
* @error: (nullable): a location for a #GError or %NULL
|
|
*
|
|
* Completes a request to create a #MksSession for a [class@Gio.DBusConnection].
|
|
*
|
|
* Returns: (transfer full): a #MksSession if successful; otherwise %NULL
|
|
* and @error is set.
|
|
*/
|
|
MksSession *
|
|
mks_session_new_for_connection_with_name_finish (GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
g_return_val_if_fail (G_IS_TASK (result), NULL);
|
|
|
|
return g_task_propagate_pointer (G_TASK (result), error);
|
|
}
|
|
|
|
/**
|
|
* mks_session_new_for_connection_with_name_sync:
|
|
* @bus_name: The unique name to connect
|
|
* @connection: a private #GDBusConnetion to a QEMU instance
|
|
* @cancellable: (nullable): a #GCancellable, or %NULL
|
|
* @error: (nullable): a location for a #GError, or %NULL
|
|
*
|
|
* Synchronously creates a new #MksSession instance.
|
|
*
|
|
* The constructor is similar to [func@Mks.Session.new_for_connection_sync] except
|
|
* it allows to set the bus name to something else than the default `org.qemu`.
|
|
*
|
|
* Returns: (transfer full): a #MksSession if successful; otherwise %NULL
|
|
* and @error is set.
|
|
*/
|
|
MksSession *
|
|
mks_session_new_for_connection_with_name_sync (GDBusConnection *connection,
|
|
const char *bus_name,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
g_autoptr(MksSession) self = NULL;
|
|
|
|
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
|
|
g_return_val_if_fail (bus_name != NULL, NULL);
|
|
|
|
self = g_object_new (MKS_TYPE_SESSION,
|
|
"connection", connection,
|
|
"bus-name", bus_name,
|
|
NULL);
|
|
|
|
if (g_initable_init (G_INITABLE (self), cancellable, error))
|
|
return g_steal_pointer (&self);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* mks_session_get_connection:
|
|
* @self: a #MksSession
|
|
*
|
|
* Gets the DBus connection used for this session.
|
|
*
|
|
* Returns: (transfer none) (nullable): a #GDBusConnection or %NULL if
|
|
* the connection has not been set, or was disposed.
|
|
*/
|
|
GDBusConnection *
|
|
mks_session_get_connection (MksSession *self)
|
|
{
|
|
g_return_val_if_fail (MKS_IS_SESSION (self), NULL);
|
|
|
|
return self->connection;
|
|
}
|
|
|
|
/**
|
|
* mks_session_get_bus_name:
|
|
* @self: a #MksSession
|
|
*
|
|
* Gets the DBus connection unique name or `org.qemu` as a fallback.
|
|
*
|
|
* Returns: A unique name the session is connected to
|
|
*/
|
|
const char *
|
|
mks_session_get_bus_name (MksSession *self)
|
|
{
|
|
g_return_val_if_fail (MKS_IS_SESSION (self), NULL);
|
|
|
|
return self->bus_name;
|
|
}
|
|
|
|
/**
|
|
* mks_session_get_uuid:
|
|
* @self: a #MksSession
|
|
*
|
|
* Gets the unique identifier of the VM.
|
|
*/
|
|
const char *
|
|
mks_session_get_uuid (MksSession *self)
|
|
{
|
|
g_return_val_if_fail (MKS_IS_SESSION (self), NULL);
|
|
|
|
return self->uuid;
|
|
}
|
|
|
|
/**
|
|
* mks_session_get_name:
|
|
* @self: a #MksSession
|
|
*
|
|
* Gets the name of the VM.
|
|
*/
|
|
const char *
|
|
mks_session_get_name (MksSession *self)
|
|
{
|
|
g_return_val_if_fail (MKS_IS_SESSION (self), NULL);
|
|
|
|
return self->name;
|
|
}
|
|
|
|
/**
|
|
* mks_session_ref_screen:
|
|
* @self: a #MksSession
|
|
*
|
|
* Gets the main screen for the session.
|
|
*
|
|
* Returns: (transfer full) (nullable): a #MksScreen or %NULL
|
|
*/
|
|
MksScreen *
|
|
mks_session_ref_screen (MksSession *self)
|
|
{
|
|
GListModel *model;
|
|
guint n_items;
|
|
|
|
g_return_val_if_fail (MKS_IS_SESSION (self), NULL);
|
|
|
|
model = G_LIST_MODEL (self->devices);
|
|
n_items = g_list_model_get_n_items (model);
|
|
|
|
for (guint i = 0; i < n_items; i++)
|
|
{
|
|
g_autoptr(MksDevice) device = g_list_model_get_item (model, i);
|
|
|
|
if (MKS_IS_SCREEN (device))
|
|
return MKS_SCREEN (g_steal_pointer (&device));
|
|
}
|
|
|
|
return NULL;
|
|
}
|