mirror of
https://gitlab.gnome.org/GNOME/libmks.git
synced 2024-12-22 21:55:20 +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 "config.h"
|
||||||
|
|
||||||
#include "mks-paintable.h"
|
#include "mks-paintable-private.h"
|
||||||
#include "mks-screen.h"
|
#include "mks-screen.h"
|
||||||
|
|
||||||
struct _MksPaintable
|
struct _MksPaintable
|
||||||
@ -224,3 +224,17 @@ paintable_iface_init (GdkPaintableInterface *iface)
|
|||||||
iface->get_intrinsic_aspect_ratio = mks_paintable_get_intrinsic_aspect_ratio;
|
iface->get_intrinsic_aspect_ratio = mks_paintable_get_intrinsic_aspect_ratio;
|
||||||
iface->snapshot = mks_paintable_snapshot;
|
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 "config.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
|
||||||
#include "mks-device-private.h"
|
#include "mks-device-private.h"
|
||||||
#include "mks-enums.h"
|
#include "mks-enums.h"
|
||||||
#include "mks-qemu.h"
|
#include "mks-qemu.h"
|
||||||
#include "mks-keyboard-private.h"
|
#include "mks-keyboard-private.h"
|
||||||
#include "mks-mouse-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-attributes-private.h"
|
||||||
#include "mks-screen-private.h"
|
#include "mks-screen-private.h"
|
||||||
|
|
||||||
@ -556,3 +563,226 @@ mks_screen_configure_sync (MksScreen *self,
|
|||||||
cancellable,
|
cancellable,
|
||||||
error);
|
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."
|
# error "Only <libmks.h> can be included directly."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <gdk/gdk.h>
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
|
|
||||||
#include "mks-types.h"
|
#include "mks-types.h"
|
||||||
@ -79,6 +80,19 @@ gboolean mks_screen_configure_sync (MksScreen *self,
|
|||||||
MksScreenAttributes *attributes,
|
MksScreenAttributes *attributes,
|
||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
GError **error);
|
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)
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MksScreen, g_object_unref)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user