mirror of
https://gitlab.gnome.org/GNOME/libmks.git
synced 2024-12-22 13:45:21 +00:00
lib: add mks_screen_attach()
This is ultimately going to give us back a paintable, but it's only in partial state right now. We need to figure out the right semantics for ownership between the listener/paintable/screen/etc. We may want the widget to be the owner of everything (and keep the painable/listener internal API) which is likely the most convenient from an object ownership standpoint.
This commit is contained in:
parent
5e75216318
commit
629cfd0e28
33
lib/mks-paintable-private.h
Normal file
33
lib/mks-paintable-private.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* mks-paintable-private.h
|
||||
*
|
||||
* Copyright 2023 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mks-paintable.h"
|
||||
#include "mks-paintable-listener-private.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GdkPaintable *_mks_paintable_new (GDBusConnection *connection,
|
||||
MksScreen *screen,
|
||||
MksPaintableListener *listener);
|
||||
|
||||
G_END_DECLS
|
@ -21,7 +21,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "mks-paintable.h"
|
||||
#include "mks-paintable-private.h"
|
||||
#include "mks-screen.h"
|
||||
|
||||
struct _MksPaintable
|
||||
@ -224,3 +224,17 @@ paintable_iface_init (GdkPaintableInterface *iface)
|
||||
iface->get_intrinsic_aspect_ratio = mks_paintable_get_intrinsic_aspect_ratio;
|
||||
iface->snapshot = mks_paintable_snapshot;
|
||||
}
|
||||
|
||||
GdkPaintable *
|
||||
_mks_paintable_new (GDBusConnection *connection,
|
||||
MksScreen *screen,
|
||||
MksPaintableListener *listener)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
|
||||
g_return_val_if_fail (MKS_IS_SCREEN (screen), NULL);
|
||||
g_return_val_if_fail (MKS_IS_PAINTABLE_LISTENER (listener), NULL);
|
||||
|
||||
/* TODO: */
|
||||
|
||||
return mks_paintable_new (screen);
|
||||
}
|
||||
|
230
lib/mks-screen.c
230
lib/mks-screen.c
@ -21,11 +21,18 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "mks-device-private.h"
|
||||
#include "mks-enums.h"
|
||||
#include "mks-qemu.h"
|
||||
#include "mks-keyboard-private.h"
|
||||
#include "mks-mouse-private.h"
|
||||
#include "mks-paintable-listener-private.h"
|
||||
#include "mks-paintable-private.h"
|
||||
#include "mks-screen-attributes-private.h"
|
||||
#include "mks-screen-private.h"
|
||||
|
||||
@ -556,3 +563,226 @@ mks_screen_configure_sync (MksScreen *self,
|
||||
cancellable,
|
||||
error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mks_screen_create_socketpair (int *us,
|
||||
int *them,
|
||||
GError **error)
|
||||
{
|
||||
int rv;
|
||||
int fds[2];
|
||||
|
||||
g_assert (us != NULL);
|
||||
g_assert (them != NULL);
|
||||
|
||||
rv = socketpair (AF_UNIX, SOCK_NONBLOCK | SOCK_CLOEXEC, SOCK_STREAM, fds);
|
||||
|
||||
if (rv != 0)
|
||||
{
|
||||
int errsv = errno;
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR,
|
||||
g_io_error_from_errno (errsv),
|
||||
g_strerror (errsv));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*us = fds[0];
|
||||
*them = fds[1];
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
mks_screen_attach_cb (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
MksQemuConsole *console = (MksQemuConsole *)object;
|
||||
g_autoptr(GTask) task = user_data;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
g_assert (G_IS_OBJECT (object));
|
||||
g_assert (G_IS_ASYNC_RESULT (result));
|
||||
g_assert (G_IS_TASK (task));
|
||||
|
||||
if (!mks_qemu_console_call_register_listener_finish (console, NULL, result, &error))
|
||||
g_task_return_error (task, g_steal_pointer (&error));
|
||||
else
|
||||
g_task_return_pointer (task,
|
||||
g_object_ref (g_task_get_task_data (task)),
|
||||
g_object_unref);
|
||||
}
|
||||
|
||||
/**
|
||||
* mks_screen_attach:
|
||||
* @self: an #MksScreen
|
||||
* @cancellable: (nullable): a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to execute upon completion
|
||||
* @user_data: closure data for @callback
|
||||
*
|
||||
* Asynchronously creates a #GdkPaintable that is updated with the
|
||||
* contents of the screen.
|
||||
*
|
||||
* This function registers a new `socketpair()` which is shared with
|
||||
* the Qemu instance to receive rendering updates. Those updates are
|
||||
* propagated to the resulting #GdkPainable which can be retrieved
|
||||
* using mks_screen_attach_finish() from @callback.
|
||||
*/
|
||||
void
|
||||
mks_screen_attach (MksScreen *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(MksPaintableListener) listener = NULL;
|
||||
g_autoptr(GUnixFDList) unix_fd_list = NULL;
|
||||
g_autoptr(GDBusConnection) connection = NULL;
|
||||
g_autoptr(GSocketConnection) io_stream = NULL;
|
||||
g_autoptr(GSocket) socket = NULL;
|
||||
g_autoptr(GTask) task = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_autofd int us = -1;
|
||||
g_autofd int them = -1;
|
||||
|
||||
g_return_if_fail (MKS_IS_SCREEN (self));
|
||||
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
|
||||
|
||||
task = g_task_new (self, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, mks_screen_attach);
|
||||
|
||||
if (!check_console (self, &error) ||
|
||||
!mks_screen_create_socketpair (&us, &them, &error))
|
||||
goto failure;
|
||||
|
||||
g_assert (us != -1);
|
||||
g_assert (them != -1);
|
||||
|
||||
if (!(socket = g_socket_new_from_fd (us, &error)))
|
||||
goto failure;
|
||||
|
||||
us = -1;
|
||||
io_stream = g_socket_connection_factory_create_connection (socket);
|
||||
connection = g_dbus_connection_new_sync (G_IO_STREAM (io_stream),
|
||||
NULL,
|
||||
G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING,
|
||||
NULL,
|
||||
cancellable,
|
||||
&error);
|
||||
|
||||
if (connection == NULL)
|
||||
goto failure;
|
||||
|
||||
unix_fd_list = g_unix_fd_list_new_from_array (&them, 1);
|
||||
them = -1;
|
||||
|
||||
listener = mks_paintable_listener_new ();
|
||||
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (listener),
|
||||
connection,
|
||||
MKS_PAINTABLE_LISTENER_OBJECT_PATH,
|
||||
&error))
|
||||
goto failure;
|
||||
|
||||
g_task_set_task_data (task,
|
||||
_mks_paintable_new (connection, self, listener),
|
||||
g_object_unref);
|
||||
|
||||
mks_qemu_console_call_register_listener (self->console,
|
||||
g_variant_new_handle (0),
|
||||
unix_fd_list,
|
||||
cancellable,
|
||||
mks_screen_attach_cb,
|
||||
g_steal_pointer (&task));
|
||||
|
||||
g_dbus_connection_start_message_processing (connection);
|
||||
|
||||
return;
|
||||
|
||||
failure:
|
||||
g_task_return_error (task, g_steal_pointer (&error));
|
||||
}
|
||||
|
||||
/**
|
||||
* mks_screen_attach_finish:
|
||||
* @self: an #MksScreen
|
||||
* @result: a #GAsyncResult provided to callback
|
||||
* @error: a location for a #GError, or %NULL
|
||||
*
|
||||
* Completes an asynchronous request to create a #GdkPaintable containing
|
||||
* the contents of #MksScreen in the Qemu instance.
|
||||
*
|
||||
* The resulting #GdkPaintable will be updated as changes are delivered
|
||||
* from Qemu over a private `socketpair()`. In the typical case, those
|
||||
* changes are propagated using a DMA-BUF and damage notifications.
|
||||
*
|
||||
* Returns: (transfer full): a #GdkPainable if successful; otherwise %NULL
|
||||
* and @error is set.
|
||||
*/
|
||||
GdkPaintable *
|
||||
mks_screen_attach_finish (MksScreen *self,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (MKS_IS_SCREEN (self), FALSE);
|
||||
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
|
||||
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
GdkPaintable *
|
||||
mks_screen_attach_sync (MksScreen *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GUnixFDList) unix_fd_list = NULL;
|
||||
g_autoptr(GDBusConnection) connection = NULL;
|
||||
g_autoptr(GSocketConnection) io_stream = NULL;
|
||||
g_autoptr(MksPaintableListener) listener = NULL;
|
||||
g_autoptr(GSocket) socket = NULL;
|
||||
g_autofd int us = -1;
|
||||
g_autofd int them = -1;
|
||||
|
||||
g_return_val_if_fail (MKS_IS_SCREEN (self), NULL);
|
||||
g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
|
||||
|
||||
if (!check_console (self, error) ||
|
||||
!mks_screen_create_socketpair (&us, &them, error))
|
||||
return NULL;
|
||||
|
||||
g_assert (us != -1);
|
||||
g_assert (them != -1);
|
||||
|
||||
if (!(socket = g_socket_new_from_fd (us, error)))
|
||||
return NULL;
|
||||
|
||||
us = -1;
|
||||
io_stream = g_socket_connection_factory_create_connection (socket);
|
||||
connection = g_dbus_connection_new_sync (G_IO_STREAM (io_stream),
|
||||
NULL,
|
||||
G_DBUS_CONNECTION_FLAGS_NONE,
|
||||
NULL,
|
||||
cancellable,
|
||||
error);
|
||||
if (connection == NULL)
|
||||
return FALSE;
|
||||
|
||||
unix_fd_list = g_unix_fd_list_new_from_array (&them, 1);
|
||||
them = -1;
|
||||
|
||||
listener = mks_paintable_listener_new ();
|
||||
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (listener),
|
||||
connection,
|
||||
MKS_PAINTABLE_LISTENER_OBJECT_PATH,
|
||||
error))
|
||||
return NULL;
|
||||
|
||||
if (!mks_qemu_console_call_register_listener_sync (self->console,
|
||||
g_variant_new_handle (0),
|
||||
unix_fd_list,
|
||||
NULL,
|
||||
cancellable,
|
||||
error))
|
||||
return NULL;
|
||||
|
||||
return _mks_paintable_new (connection, self, listener);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
# error "Only <libmks.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "mks-types.h"
|
||||
@ -79,6 +80,19 @@ gboolean mks_screen_configure_sync (MksScreen *self,
|
||||
MksScreenAttributes *attributes,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
MKS_AVAILABLE_IN_ALL
|
||||
void mks_screen_attach (MksScreen *screen,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
MKS_AVAILABLE_IN_ALL
|
||||
GdkPaintable *mks_screen_attach_finish (MksScreen *self,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
MKS_AVAILABLE_IN_ALL
|
||||
GdkPaintable *mks_screen_attach_sync (MksScreen *screen,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MksScreen, g_object_unref)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user