mirror of
https://github.com/brl/mutter.git
synced 2024-12-04 22:00:42 -05:00
3d693e8309
There are still various helpers that might be worth to move to mtk as well Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3128>
1544 lines
47 KiB
C
1544 lines
47 KiB
C
/*
|
|
* Copyright (C) 2022 Red Hat Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "backends/meta-input-capture-session.h"
|
|
|
|
#include <gio/gunixfdlist.h>
|
|
#include <libeis.h>
|
|
#include <stdint.h>
|
|
|
|
#include "backends/meta-dbus-session-watcher.h"
|
|
#include "backends/meta-dbus-session-manager.h"
|
|
#include "backends/meta-fd-source.h"
|
|
#include "backends/meta-input-capture-private.h"
|
|
#include "backends/meta-monitor-manager-private.h"
|
|
#include "backends/meta-logical-monitor.h"
|
|
#include "backends/meta-remote-access-controller-private.h"
|
|
#include "core/meta-anonymous-file.h"
|
|
#include "meta/barrier.h"
|
|
#include "meta/boxes.h"
|
|
#include "meta/meta-backend.h"
|
|
|
|
#include "meta-dbus-input-capture.h"
|
|
|
|
#define META_INPUT_CAPTURE_SESSION_DBUS_PATH "/org/gnome/Mutter/InputCapture/Session"
|
|
|
|
static GQuark quark_barrier_id;
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
N_PROPS
|
|
};
|
|
|
|
typedef enum _InputCaptureState
|
|
{
|
|
INPUT_CAPTURE_STATE_INIT,
|
|
INPUT_CAPTURE_STATE_ENABLED,
|
|
INPUT_CAPTURE_STATE_ACTIVATED,
|
|
INPUT_CAPTURE_STATE_CLOSED,
|
|
} InputCaptureState;
|
|
|
|
typedef struct _InputCaptureBarrier
|
|
{
|
|
int x1;
|
|
int y1;
|
|
int x2;
|
|
int y2;
|
|
|
|
unsigned int id;
|
|
MetaBarrier *barrier;
|
|
} InputCaptureBarrier;
|
|
|
|
struct _MetaInputCaptureSession
|
|
{
|
|
MetaDBusInputCaptureSessionSkeleton parent;
|
|
|
|
MetaDbusSessionManager *session_manager;
|
|
|
|
GDBusConnection *connection;
|
|
char *peer_name;
|
|
|
|
char *session_id;
|
|
char *object_path;
|
|
|
|
InputCaptureState state;
|
|
GHashTable *barriers;
|
|
|
|
uint32_t zones_serial;
|
|
uint32_t activation_id;
|
|
|
|
MetaInputCaptureSessionHandle *handle;
|
|
|
|
struct eis *eis;
|
|
struct eis_client *eis_client;
|
|
struct eis_seat *eis_seat;
|
|
struct eis_device *eis_pointer;
|
|
struct eis_device *eis_keyboard;
|
|
GSource *eis_source;
|
|
|
|
MetaAnonymousFile *keymap_file;
|
|
|
|
MetaViewportInfo *viewports;
|
|
|
|
gboolean cancel_requested;
|
|
unsigned int buttons_pressed;
|
|
unsigned int keys_pressed;
|
|
};
|
|
|
|
static void initable_init_iface (GInitableIface *iface);
|
|
|
|
static void meta_input_capture_session_init_iface (MetaDBusInputCaptureSessionIface *iface);
|
|
|
|
static void meta_dbus_session_init_iface (MetaDbusSessionInterface *iface);
|
|
|
|
static MetaInputCaptureSessionHandle * meta_input_capture_session_handle_new (MetaInputCaptureSession *session);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (MetaInputCaptureSession,
|
|
meta_input_capture_session,
|
|
META_DBUS_TYPE_INPUT_CAPTURE_SESSION_SKELETON,
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
|
|
initable_init_iface)
|
|
G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_INPUT_CAPTURE_SESSION,
|
|
meta_input_capture_session_init_iface)
|
|
G_IMPLEMENT_INTERFACE (META_TYPE_DBUS_SESSION,
|
|
meta_dbus_session_init_iface))
|
|
|
|
struct _MetaInputCaptureSessionHandle
|
|
{
|
|
MetaRemoteAccessHandle parent;
|
|
|
|
MetaInputCaptureSession *session;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaInputCaptureSessionHandle,
|
|
meta_input_capture_session_handle,
|
|
META_TYPE_REMOTE_ACCESS_HANDLE)
|
|
|
|
static void
|
|
init_remote_access_handle (MetaInputCaptureSession *session)
|
|
{
|
|
MetaBackend *backend =
|
|
meta_dbus_session_manager_get_backend (session->session_manager);
|
|
MetaRemoteAccessController *remote_access_controller;
|
|
MetaRemoteAccessHandle *remote_access_handle;
|
|
|
|
session->handle = meta_input_capture_session_handle_new (session);
|
|
|
|
remote_access_controller = meta_backend_get_remote_access_controller (backend);
|
|
remote_access_handle = META_REMOTE_ACCESS_HANDLE (session->handle);
|
|
meta_remote_access_controller_notify_new_handle (remote_access_controller,
|
|
remote_access_handle);
|
|
}
|
|
|
|
static void
|
|
release_remote_access_handle (MetaInputCaptureSession *session)
|
|
{
|
|
MetaRemoteAccessHandle *remote_access_handle =
|
|
META_REMOTE_ACCESS_HANDLE (session->handle);
|
|
|
|
meta_remote_access_handle_notify_stopped (remote_access_handle);
|
|
g_clear_object (&session->handle);
|
|
}
|
|
|
|
static void
|
|
setup_client (MetaInputCaptureSession *session,
|
|
struct eis_client *eis_client)
|
|
{
|
|
MetaBackend *backend =
|
|
meta_dbus_session_manager_get_backend (session->session_manager);
|
|
ClutterSeat *seat = meta_backend_get_default_seat (backend);
|
|
struct eis_seat *eis_seat;
|
|
|
|
session->eis_client = eis_client_ref (eis_client);
|
|
|
|
eis_client_connect (eis_client);
|
|
|
|
eis_seat = eis_client_new_seat (eis_client, clutter_seat_get_name (seat));
|
|
eis_seat_configure_capability (eis_seat, EIS_DEVICE_CAP_POINTER);
|
|
eis_seat_configure_capability (eis_seat, EIS_DEVICE_CAP_BUTTON);
|
|
eis_seat_configure_capability (eis_seat, EIS_DEVICE_CAP_SCROLL);
|
|
eis_seat_configure_capability (eis_seat, EIS_DEVICE_CAP_KEYBOARD);
|
|
eis_seat_add (eis_seat);
|
|
|
|
session->eis_seat = eis_seat;
|
|
}
|
|
|
|
static void
|
|
ensure_eis_pointer_regions (MetaInputCaptureSession *session,
|
|
struct eis_device *eis_pointer)
|
|
{
|
|
int idx = 0;
|
|
MtkRectangle rect;
|
|
float scale;
|
|
|
|
if (!session->viewports)
|
|
return;
|
|
|
|
while (meta_viewport_info_get_view_info (session->viewports, idx++, &rect, &scale))
|
|
{
|
|
struct eis_region *r = eis_device_new_region (eis_pointer);
|
|
|
|
eis_region_set_offset (r, rect.x, rect.y);
|
|
eis_region_set_size (r, rect.width, rect.height);
|
|
eis_region_set_physical_scale (r, scale);
|
|
eis_region_add (r);
|
|
eis_region_unref (r);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ensure_eis_pointer (MetaInputCaptureSession *session)
|
|
{
|
|
struct eis_device *eis_pointer;
|
|
|
|
if (session->eis_pointer)
|
|
return;
|
|
|
|
eis_pointer = eis_seat_new_device (session->eis_seat);
|
|
eis_device_configure_name (eis_pointer, "captured relative pointer");
|
|
eis_device_configure_capability (eis_pointer, EIS_DEVICE_CAP_POINTER);
|
|
eis_device_configure_capability (eis_pointer, EIS_DEVICE_CAP_BUTTON);
|
|
eis_device_configure_capability (eis_pointer, EIS_DEVICE_CAP_SCROLL);
|
|
ensure_eis_pointer_regions (session, eis_pointer);
|
|
eis_device_add (eis_pointer);
|
|
eis_device_resume (eis_pointer);
|
|
|
|
session->eis_pointer = eis_pointer;
|
|
}
|
|
|
|
static MetaAnonymousFile *
|
|
ensure_xkb_keymap_file (MetaInputCaptureSession *session,
|
|
GError **error)
|
|
{
|
|
MetaBackend *backend =
|
|
meta_dbus_session_manager_get_backend (session->session_manager);
|
|
struct xkb_keymap *keymap;
|
|
g_autofree char *keymap_string = NULL;
|
|
size_t keymap_size;
|
|
|
|
if (session->keymap_file)
|
|
return session->keymap_file;
|
|
|
|
keymap = meta_backend_get_keymap (backend);
|
|
if (!keymap)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Backend has no keymap");
|
|
return NULL;
|
|
}
|
|
|
|
keymap_string = xkb_keymap_get_as_string (keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
|
|
keymap_size = strlen (keymap_string) + 1;
|
|
|
|
session->keymap_file =
|
|
meta_anonymous_file_new (keymap_size, (const uint8_t *) keymap_string);
|
|
|
|
return session->keymap_file;
|
|
}
|
|
|
|
static void
|
|
ensure_eis_keyboard (MetaInputCaptureSession *session)
|
|
{
|
|
struct eis_device *eis_keyboard;
|
|
g_autoptr (GError) error = NULL;
|
|
struct eis_keymap *eis_keymap;
|
|
MetaAnonymousFile *keymap_file;
|
|
int keymap_fd;
|
|
size_t keymap_size;
|
|
|
|
if (session->eis_keyboard)
|
|
return;
|
|
|
|
keymap_file = ensure_xkb_keymap_file (session, &error);
|
|
if (!keymap_file)
|
|
{
|
|
g_warning ("Failed to create input capture keymap file: %s",
|
|
error->message);
|
|
return;
|
|
}
|
|
|
|
eis_keyboard = eis_seat_new_device (session->eis_seat);
|
|
eis_device_configure_name (eis_keyboard, "captured keyboard");
|
|
eis_device_configure_capability (eis_keyboard, EIS_DEVICE_CAP_KEYBOARD);
|
|
|
|
keymap_fd = meta_anonymous_file_open_fd (keymap_file,
|
|
META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
|
|
keymap_size = meta_anonymous_file_size (keymap_file);
|
|
eis_keymap = eis_device_new_keymap (eis_keyboard,
|
|
EIS_KEYMAP_TYPE_XKB,
|
|
keymap_fd, keymap_size);
|
|
eis_keymap_add (eis_keymap);
|
|
eis_keymap_unref (eis_keymap);
|
|
meta_anonymous_file_close_fd (keymap_fd);
|
|
|
|
eis_device_add (eis_keyboard);
|
|
eis_device_resume (eis_keyboard);
|
|
|
|
session->eis_keyboard = eis_keyboard;
|
|
}
|
|
|
|
static void
|
|
clear_eis_pointer (MetaInputCaptureSession *session)
|
|
{
|
|
if (!session->eis_pointer)
|
|
return;
|
|
|
|
eis_device_remove (session->eis_pointer);
|
|
g_clear_pointer (&session->eis_pointer, eis_device_unref);
|
|
}
|
|
|
|
static void
|
|
remove_eis_pointer (MetaInputCaptureSession *session)
|
|
{
|
|
clear_eis_pointer (session);
|
|
|
|
/* The pointer is removed, all its buttons are cleared */
|
|
session->buttons_pressed = 0;
|
|
}
|
|
|
|
static void
|
|
clear_eis_keyboard (MetaInputCaptureSession *session)
|
|
{
|
|
if (!session->eis_keyboard)
|
|
return;
|
|
|
|
eis_device_remove (session->eis_keyboard);
|
|
g_clear_pointer (&session->eis_keyboard, eis_device_unref);
|
|
}
|
|
|
|
static void
|
|
remove_eis_keyboard (MetaInputCaptureSession *session)
|
|
{
|
|
clear_eis_keyboard (session);
|
|
|
|
/* The pointer is removed, all its buttons are cleared */
|
|
session->keys_pressed = 0;
|
|
}
|
|
|
|
static void
|
|
on_keymap_changed (MetaBackend *backend,
|
|
gpointer user_data)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (user_data);
|
|
|
|
g_clear_pointer (&session->keymap_file, meta_anonymous_file_free);
|
|
|
|
if (session->eis_keyboard)
|
|
{
|
|
clear_eis_keyboard (session);
|
|
ensure_eis_keyboard (session);
|
|
}
|
|
}
|
|
|
|
static void
|
|
process_eis_event (MetaInputCaptureSession *session,
|
|
struct eis_event *eis_event)
|
|
{
|
|
struct eis_client *eis_client;
|
|
struct eis_device *eis_device;
|
|
|
|
switch (eis_event_get_type (eis_event))
|
|
{
|
|
case EIS_EVENT_CLIENT_CONNECT:
|
|
eis_client = eis_event_get_client (eis_event);
|
|
if (eis_client_is_sender (eis_client))
|
|
{
|
|
g_warning ("Unexpected sender libei client '%s' connected to "
|
|
"input capture session",
|
|
eis_client_get_name (eis_client));
|
|
eis_client_disconnect (eis_client);
|
|
return;
|
|
}
|
|
|
|
if (session->eis_client)
|
|
{
|
|
g_warning ("Unexpected additional libei client '%s' connected to "
|
|
"input capture session",
|
|
eis_client_get_name (eis_client));
|
|
eis_client_disconnect (eis_client);
|
|
return;
|
|
}
|
|
|
|
setup_client (session, eis_client);
|
|
break;
|
|
|
|
case EIS_EVENT_CLIENT_DISCONNECT:
|
|
g_clear_pointer (&session->eis_seat, eis_seat_unref);
|
|
g_clear_pointer (&session->eis_client, eis_client_unref);
|
|
break;
|
|
case EIS_EVENT_SEAT_BIND:
|
|
if (eis_event_seat_has_capability (eis_event, EIS_DEVICE_CAP_POINTER) &&
|
|
eis_event_seat_has_capability (eis_event, EIS_DEVICE_CAP_BUTTON) &&
|
|
eis_event_seat_has_capability (eis_event, EIS_DEVICE_CAP_SCROLL))
|
|
ensure_eis_pointer (session);
|
|
else if (session->eis_pointer)
|
|
clear_eis_pointer (session);
|
|
|
|
if (eis_event_seat_has_capability (eis_event, EIS_DEVICE_CAP_KEYBOARD))
|
|
ensure_eis_keyboard (session);
|
|
else if (session->eis_keyboard)
|
|
clear_eis_keyboard (session);
|
|
break;
|
|
case EIS_EVENT_DEVICE_CLOSED:
|
|
eis_device = eis_event_get_device (eis_event);
|
|
|
|
if (eis_device == session->eis_pointer)
|
|
remove_eis_pointer (session);
|
|
else if (eis_device == session->eis_keyboard)
|
|
remove_eis_keyboard (session);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
on_barrier_hit (MetaBarrier *barrier,
|
|
const MetaBarrierEvent *event,
|
|
MetaInputCaptureSession *session)
|
|
{
|
|
MetaDBusInputCaptureSession *skeleton =
|
|
META_DBUS_INPUT_CAPTURE_SESSION (session);
|
|
MetaInputCapture *input_capture =
|
|
META_INPUT_CAPTURE (session->session_manager);
|
|
GVariant *cursor_position;
|
|
unsigned int barrier_id;
|
|
|
|
switch (session->state)
|
|
{
|
|
case INPUT_CAPTURE_STATE_ACTIVATED:
|
|
return;
|
|
case INPUT_CAPTURE_STATE_ENABLED:
|
|
break;
|
|
case INPUT_CAPTURE_STATE_INIT:
|
|
case INPUT_CAPTURE_STATE_CLOSED:
|
|
g_warn_if_reached ();
|
|
return;
|
|
}
|
|
|
|
session->state = INPUT_CAPTURE_STATE_ACTIVATED;
|
|
|
|
barrier_id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (barrier),
|
|
quark_barrier_id));
|
|
cursor_position = g_variant_new ("(dd)", event->x, event->y);
|
|
|
|
meta_input_capture_activate (input_capture, session);
|
|
|
|
meta_dbus_input_capture_session_emit_activated (skeleton,
|
|
barrier_id,
|
|
++session->activation_id,
|
|
cursor_position);
|
|
if (session->eis_pointer)
|
|
eis_device_start_emulating (session->eis_pointer, session->activation_id);
|
|
if (session->eis_keyboard)
|
|
eis_device_start_emulating (session->eis_keyboard, session->activation_id);
|
|
|
|
init_remote_access_handle (session);
|
|
}
|
|
|
|
static void
|
|
clear_all_barriers (MetaInputCaptureSession *session)
|
|
{
|
|
GHashTableIter iter;
|
|
InputCaptureBarrier *input_capture_barrier;
|
|
|
|
g_hash_table_iter_init (&iter, session->barriers);
|
|
while (g_hash_table_iter_next (&iter, NULL,
|
|
(gpointer *) &input_capture_barrier))
|
|
g_clear_pointer (&input_capture_barrier->barrier, meta_barrier_destroy);
|
|
}
|
|
|
|
static void
|
|
release_all_barriers (MetaInputCaptureSession *session)
|
|
{
|
|
GHashTableIter iter;
|
|
InputCaptureBarrier *input_capture_barrier;
|
|
|
|
g_hash_table_iter_init (&iter, session->barriers);
|
|
while (g_hash_table_iter_next (&iter, NULL,
|
|
(gpointer *) &input_capture_barrier))
|
|
{
|
|
MetaBarrier *barrier;
|
|
|
|
barrier = input_capture_barrier->barrier;
|
|
if (!barrier)
|
|
continue;
|
|
|
|
meta_barrier_release (barrier, NULL);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
meta_input_capture_session_enable (MetaInputCaptureSession *session,
|
|
GError **error)
|
|
{
|
|
MetaBackend *backend =
|
|
meta_dbus_session_manager_get_backend (session->session_manager);
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
|
|
g_warn_if_fail (session->state == INPUT_CAPTURE_STATE_INIT);
|
|
|
|
g_hash_table_iter_init (&iter, session->barriers);
|
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
|
{
|
|
unsigned int barrier_id = GPOINTER_TO_UINT (key);
|
|
InputCaptureBarrier *input_capture_barrier = value;
|
|
g_autoptr (MetaBarrier) barrier = NULL;
|
|
|
|
barrier = meta_barrier_new (backend,
|
|
input_capture_barrier->x1,
|
|
input_capture_barrier->y1,
|
|
input_capture_barrier->x2,
|
|
input_capture_barrier->y2,
|
|
0,
|
|
META_BARRIER_FLAG_STICKY,
|
|
error);
|
|
if (!barrier)
|
|
goto err;
|
|
|
|
g_object_set_qdata (G_OBJECT (barrier), quark_barrier_id,
|
|
GUINT_TO_POINTER (barrier_id));
|
|
g_signal_connect (barrier, "hit", G_CALLBACK (on_barrier_hit), session);
|
|
input_capture_barrier->barrier = barrier;
|
|
}
|
|
|
|
session->state = INPUT_CAPTURE_STATE_ENABLED;
|
|
session->cancel_requested = FALSE;
|
|
|
|
return TRUE;
|
|
|
|
err:
|
|
clear_all_barriers (session);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_deactivate (MetaInputCaptureSession *session)
|
|
{
|
|
MetaDBusInputCaptureSession *skeleton =
|
|
META_DBUS_INPUT_CAPTURE_SESSION (session);
|
|
MetaInputCapture *input_capture =
|
|
META_INPUT_CAPTURE (session->session_manager);
|
|
|
|
meta_input_capture_deactivate (input_capture, session);
|
|
|
|
if (session->eis_pointer)
|
|
eis_device_stop_emulating (session->eis_pointer);
|
|
if (session->eis_keyboard)
|
|
eis_device_stop_emulating (session->eis_keyboard);
|
|
meta_dbus_input_capture_session_emit_deactivated (skeleton,
|
|
session->activation_id);
|
|
|
|
session->state = INPUT_CAPTURE_STATE_ENABLED;
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_disable (MetaInputCaptureSession *session)
|
|
{
|
|
switch (session->state)
|
|
{
|
|
case INPUT_CAPTURE_STATE_INIT:
|
|
return;
|
|
case INPUT_CAPTURE_STATE_ACTIVATED:
|
|
meta_input_capture_session_deactivate (session);
|
|
G_GNUC_FALLTHROUGH;
|
|
case INPUT_CAPTURE_STATE_ENABLED:
|
|
break;
|
|
case INPUT_CAPTURE_STATE_CLOSED:
|
|
g_warn_if_reached ();
|
|
return;
|
|
}
|
|
|
|
clear_all_barriers (session);
|
|
|
|
g_clear_pointer (&session->eis_pointer, eis_device_unref);
|
|
g_clear_pointer (&session->eis_keyboard, eis_device_unref);
|
|
g_clear_pointer (&session->eis_seat, eis_seat_unref);
|
|
|
|
session->state = INPUT_CAPTURE_STATE_INIT;
|
|
|
|
if (session->handle)
|
|
release_remote_access_handle (session);
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_close (MetaDbusSession *dbus_session)
|
|
{
|
|
MetaInputCaptureSession *session =
|
|
META_INPUT_CAPTURE_SESSION (dbus_session);
|
|
MetaDBusInputCaptureSession *skeleton =
|
|
META_DBUS_INPUT_CAPTURE_SESSION (session);
|
|
|
|
meta_input_capture_session_disable (session);
|
|
session->state = INPUT_CAPTURE_STATE_CLOSED;
|
|
|
|
meta_dbus_session_notify_closed (META_DBUS_SESSION (session));
|
|
meta_dbus_input_capture_session_emit_closed (skeleton);
|
|
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (session));
|
|
|
|
g_object_unref (session);
|
|
}
|
|
|
|
static gboolean
|
|
check_permission (MetaInputCaptureSession *session,
|
|
GDBusMethodInvocation *invocation)
|
|
{
|
|
return g_strcmp0 (session->peer_name,
|
|
g_dbus_method_invocation_get_sender (invocation)) == 0;
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
LINE_ADJACENCY_ERROR,
|
|
LINE_ADJACENCY_NONE,
|
|
LINE_ADJACENCY_OVERLAP,
|
|
LINE_ADJACENCY_CONTAINED,
|
|
LINE_ADJACENCY_PARTIAL,
|
|
} LineAdjacency;
|
|
|
|
static LineAdjacency
|
|
get_barrier_adjacency (MtkRectangle *rect,
|
|
int x1,
|
|
int y1,
|
|
int x2,
|
|
int y2,
|
|
GError **error)
|
|
{
|
|
int x_min, x_max;
|
|
int y_min, y_max;
|
|
|
|
x_min = MIN (x1, x2);
|
|
x_max = MAX (x1, x2);
|
|
y_min = MIN (y1, y2);
|
|
y_max = MAX (y1, y2);
|
|
|
|
if (x1 == x2)
|
|
{
|
|
int x = x1;
|
|
|
|
if (x < rect->x || x > rect->x + rect->width)
|
|
return LINE_ADJACENCY_NONE;
|
|
|
|
if (y_max < rect->y ||
|
|
y_min > rect->y + rect->height)
|
|
return LINE_ADJACENCY_NONE;
|
|
|
|
if (rect->x + rect->width == x || rect->x == x)
|
|
{
|
|
if (y_max > rect->y + rect->height ||
|
|
y_min < rect->y)
|
|
return LINE_ADJACENCY_PARTIAL;
|
|
else
|
|
return LINE_ADJACENCY_CONTAINED;
|
|
}
|
|
else
|
|
{
|
|
return LINE_ADJACENCY_OVERLAP;
|
|
}
|
|
}
|
|
else if (y1 == y2)
|
|
{
|
|
int y = y1;
|
|
|
|
if (y < rect->y || y > rect->y + rect->height)
|
|
return LINE_ADJACENCY_NONE;
|
|
|
|
if (x_max < rect->x ||
|
|
x_min > rect->x + rect->width)
|
|
return LINE_ADJACENCY_NONE;
|
|
|
|
if (rect->y + rect->height == y || rect->y == y)
|
|
{
|
|
if (x_max > rect->x + rect->width ||
|
|
x_min < rect->x)
|
|
return LINE_ADJACENCY_PARTIAL;
|
|
else
|
|
return LINE_ADJACENCY_CONTAINED;
|
|
}
|
|
else
|
|
{
|
|
return LINE_ADJACENCY_OVERLAP;
|
|
}
|
|
}
|
|
|
|
return LINE_ADJACENCY_NONE;
|
|
}
|
|
|
|
static gboolean
|
|
check_barrier (MetaInputCaptureSession *session,
|
|
int x1,
|
|
int y1,
|
|
int x2,
|
|
int y2,
|
|
GError **error)
|
|
{
|
|
MetaBackend *backend =
|
|
meta_dbus_session_manager_get_backend (session->session_manager);
|
|
MetaMonitorManager *monitor_manager =
|
|
meta_backend_get_monitor_manager (backend);
|
|
gboolean has_adjacent_monitor = FALSE;
|
|
GList *logical_monitors;
|
|
GList *l;
|
|
|
|
if (x1 != x2 && y1 != y2)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
|
|
"Barrier coordinates not axis aligned");
|
|
return FALSE;
|
|
}
|
|
|
|
if (x1 == x2 && y1 == y2)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
|
|
"Barrier cannot be a singularity");
|
|
return FALSE;
|
|
}
|
|
|
|
logical_monitors =
|
|
meta_monitor_manager_get_logical_monitors (monitor_manager);
|
|
for (l = logical_monitors; l; l = l->next)
|
|
{
|
|
MetaLogicalMonitor *logical_monitor = l->data;
|
|
MtkRectangle layout;
|
|
|
|
layout = meta_logical_monitor_get_layout (logical_monitor);
|
|
switch (get_barrier_adjacency (&layout, x1, y1, x2, y2, error))
|
|
{
|
|
case LINE_ADJACENCY_ERROR:
|
|
return FALSE;
|
|
case LINE_ADJACENCY_NONE:
|
|
break;
|
|
case LINE_ADJACENCY_CONTAINED:
|
|
if (has_adjacent_monitor)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
|
|
"Adjacent to multiple monitor edges");
|
|
return FALSE;
|
|
}
|
|
has_adjacent_monitor = TRUE;
|
|
break;
|
|
case LINE_ADJACENCY_OVERLAP:
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
|
|
"Line overlaps with monitor region");
|
|
return FALSE;
|
|
case LINE_ADJACENCY_PARTIAL:
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
|
|
"Line partially with monitor region");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return has_adjacent_monitor;
|
|
}
|
|
|
|
static unsigned int
|
|
find_available_barrier_id (MetaInputCaptureSession *session)
|
|
{
|
|
unsigned int id;
|
|
|
|
for (id = 1;; id++)
|
|
{
|
|
if (!g_hash_table_contains (session->barriers, GUINT_TO_POINTER (id)))
|
|
return id;
|
|
}
|
|
}
|
|
|
|
static void
|
|
input_capture_barrier_free (gpointer user_data)
|
|
{
|
|
InputCaptureBarrier *input_capture_barrier = user_data;
|
|
|
|
g_clear_pointer (&input_capture_barrier->barrier, meta_barrier_destroy);
|
|
g_free (input_capture_barrier);
|
|
}
|
|
|
|
static gboolean
|
|
handle_add_barrier (MetaDBusInputCaptureSession *object,
|
|
GDBusMethodInvocation *invocation,
|
|
unsigned int serial,
|
|
GVariant *position)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object);
|
|
int x1, y1, x2, y2;
|
|
g_autoptr (GError) error = NULL;
|
|
InputCaptureBarrier *input_capture_barrier;
|
|
unsigned int barrier_id;
|
|
|
|
if (!check_permission (session, invocation))
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_ACCESS_DENIED,
|
|
"Permission denied");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
if (session->zones_serial != serial)
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_BAD_ADDRESS,
|
|
"State out of date");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
if (session->state != INPUT_CAPTURE_STATE_INIT)
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_ACCESS_DENIED,
|
|
"Session already enabled");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
g_variant_get (position, "(iiii)", &x1, &y1, &x2, &y2);
|
|
if (!check_barrier (session, x1, y1, x2, y2, &error))
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_ACCESS_DENIED,
|
|
error->message);
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
barrier_id = find_available_barrier_id (session);
|
|
|
|
input_capture_barrier = g_new0 (InputCaptureBarrier, 1);
|
|
*input_capture_barrier = (InputCaptureBarrier) {
|
|
.id = barrier_id,
|
|
.x1 = x1,
|
|
.y1 = y1,
|
|
.x2 = x2,
|
|
.y2 = y2,
|
|
};
|
|
g_hash_table_insert (session->barriers,
|
|
GUINT_TO_POINTER (barrier_id),
|
|
input_capture_barrier);
|
|
|
|
meta_dbus_input_capture_session_complete_add_barrier (object, invocation,
|
|
barrier_id);
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
static gboolean
|
|
handle_clear_barriers (MetaDBusInputCaptureSession *object,
|
|
GDBusMethodInvocation *invocation)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object);
|
|
|
|
if (!check_permission (session, invocation))
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_ACCESS_DENIED,
|
|
"Permission denied");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
g_hash_table_remove_all (session->barriers);
|
|
|
|
meta_dbus_input_capture_session_complete_clear_barriers (object, invocation);
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
static gboolean
|
|
handle_get_zones (MetaDBusInputCaptureSession *object,
|
|
GDBusMethodInvocation *invocation)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object);
|
|
MetaBackend *backend =
|
|
meta_dbus_session_manager_get_backend (session->session_manager);
|
|
MetaMonitorManager *monitor_manager =
|
|
meta_backend_get_monitor_manager (backend);
|
|
GVariant *zones_variant;
|
|
GVariantBuilder zones_builder;
|
|
GList *logical_monitors;
|
|
GList *l;
|
|
|
|
g_variant_builder_init (&zones_builder, G_VARIANT_TYPE ("a(uuii)"));
|
|
logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager);
|
|
for (l = logical_monitors; l; l = l->next)
|
|
{
|
|
MetaLogicalMonitor *logical_monitor = l->data;
|
|
MtkRectangle layout;
|
|
|
|
layout = meta_logical_monitor_get_layout (logical_monitor);
|
|
g_variant_builder_add (&zones_builder, "(uuii)",
|
|
layout.width,
|
|
layout.height,
|
|
layout.x,
|
|
layout.y);
|
|
}
|
|
|
|
zones_variant = g_variant_builder_end (&zones_builder);
|
|
|
|
meta_dbus_input_capture_session_complete_get_zones (object, invocation,
|
|
session->zones_serial,
|
|
zones_variant);
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
static gboolean
|
|
handle_enable (MetaDBusInputCaptureSession *skeleton,
|
|
GDBusMethodInvocation *invocation)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (skeleton);
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
if (!check_permission (session, invocation))
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_ACCESS_DENIED,
|
|
"Permission denied");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
if (session->state != INPUT_CAPTURE_STATE_INIT)
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_FAILED,
|
|
"Already enabled");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
if (!meta_input_capture_session_enable (session, &error))
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_FAILED,
|
|
"Failed to enable input capture: %s",
|
|
error->message);
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
meta_dbus_input_capture_session_complete_enable (skeleton, invocation);
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
static gboolean
|
|
handle_disable (MetaDBusInputCaptureSession *skeleton,
|
|
GDBusMethodInvocation *invocation)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (skeleton);
|
|
|
|
if (!check_permission (session, invocation))
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_ACCESS_DENIED,
|
|
"Permission denied");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
if (session->state != INPUT_CAPTURE_STATE_ENABLED &&
|
|
session->state != INPUT_CAPTURE_STATE_ACTIVATED)
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_FAILED,
|
|
"Session not enabled");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
meta_input_capture_session_disable (session);
|
|
|
|
meta_dbus_input_capture_session_complete_disable (skeleton, invocation);
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
static gboolean
|
|
handle_release (MetaDBusInputCaptureSession *object,
|
|
GDBusMethodInvocation *invocation,
|
|
GVariant *arg_options)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object);
|
|
MetaBackend *backend =
|
|
meta_dbus_session_manager_get_backend (session->session_manager);
|
|
ClutterSeat *seat = meta_backend_get_default_seat (backend);
|
|
double x, y;
|
|
|
|
if (!check_permission (session, invocation))
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_ACCESS_DENIED,
|
|
"Permission denied");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
if (session->state != INPUT_CAPTURE_STATE_ACTIVATED)
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_ACCESS_DENIED,
|
|
"Capture not active");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
release_all_barriers (session);
|
|
meta_input_capture_session_deactivate (session);
|
|
|
|
if (g_variant_lookup (arg_options, "cursor_position", "(dd)", &x, &y))
|
|
clutter_seat_warp_pointer (seat, x, y);
|
|
|
|
if (session->handle)
|
|
release_remote_access_handle (session);
|
|
|
|
meta_dbus_input_capture_session_complete_release (object, invocation);
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
static gboolean
|
|
handle_connect_to_eis (MetaDBusInputCaptureSession *object,
|
|
GDBusMethodInvocation *invocation,
|
|
GUnixFDList *fd_list_in)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object);
|
|
int fd;
|
|
g_autoptr (GUnixFDList) fd_list = NULL;
|
|
int fd_idx;
|
|
GVariant *fd_variant;
|
|
|
|
if (!check_permission (session, invocation))
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_ACCESS_DENIED,
|
|
"Permission denied");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
fd = eis_backend_fd_add_client (session->eis);
|
|
if (fd < 0)
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_FAILED,
|
|
"Failed to create socket: %s",
|
|
g_strerror (-fd));
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
fd_list = g_unix_fd_list_new ();
|
|
fd_idx = g_unix_fd_list_append (fd_list, fd, NULL);
|
|
close (fd);
|
|
fd_variant = g_variant_new_handle (fd_idx);
|
|
|
|
meta_dbus_input_capture_session_complete_connect_to_eis (object,
|
|
invocation,
|
|
fd_list,
|
|
fd_variant);
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
static gboolean
|
|
handle_close (MetaDBusInputCaptureSession *object,
|
|
GDBusMethodInvocation *invocation)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object);
|
|
|
|
if (!check_permission (session, invocation))
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_ACCESS_DENIED,
|
|
"Permission denied");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
meta_dbus_session_close (META_DBUS_SESSION (session));
|
|
|
|
meta_dbus_input_capture_session_complete_close (object, invocation);
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_set_viewports (MetaInputCaptureSession *session,
|
|
MetaViewportInfo *viewports)
|
|
{
|
|
g_clear_object (&session->viewports);
|
|
session->viewports = g_object_ref (viewports);
|
|
|
|
if (!session->eis_pointer)
|
|
return;
|
|
|
|
clear_eis_pointer (session);
|
|
ensure_eis_pointer (session);
|
|
}
|
|
|
|
static void
|
|
on_monitors_changed (MetaMonitorManager *monitor_manager,
|
|
MetaInputCaptureSession *session)
|
|
{
|
|
MetaDBusInputCaptureSession *skeleton =
|
|
META_DBUS_INPUT_CAPTURE_SESSION (session);
|
|
MetaViewportInfo *viewports;
|
|
|
|
viewports = meta_monitor_manager_get_viewports (monitor_manager);
|
|
meta_input_capture_session_set_viewports (session, viewports);
|
|
|
|
session->zones_serial++;
|
|
meta_input_capture_session_disable (session);
|
|
meta_dbus_input_capture_session_emit_zones_changed (skeleton);
|
|
}
|
|
|
|
static gboolean
|
|
meta_input_capture_session_initable_init (GInitable *initable,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (initable);
|
|
GDBusInterfaceSkeleton *interface_skeleton = G_DBUS_INTERFACE_SKELETON (session);
|
|
MetaBackend *backend =
|
|
meta_dbus_session_manager_get_backend (session->session_manager);
|
|
MetaMonitorManager *monitor_manager =
|
|
meta_backend_get_monitor_manager (backend);
|
|
MetaViewportInfo *viewports =
|
|
meta_monitor_manager_get_viewports (monitor_manager);
|
|
|
|
session->connection =
|
|
meta_dbus_session_manager_get_connection (session->session_manager);
|
|
if (!g_dbus_interface_skeleton_export (interface_skeleton,
|
|
session->connection,
|
|
session->object_path,
|
|
error))
|
|
return FALSE;
|
|
|
|
meta_input_capture_session_set_viewports (session, viewports);
|
|
g_signal_connect_object (monitor_manager, "monitors-changed",
|
|
G_CALLBACK (on_monitors_changed),
|
|
session, 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
initable_init_iface (GInitableIface *iface)
|
|
{
|
|
iface->init = meta_input_capture_session_initable_init;
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_init_iface (MetaDBusInputCaptureSessionIface *iface)
|
|
{
|
|
iface->handle_add_barrier = handle_add_barrier;
|
|
iface->handle_clear_barriers = handle_clear_barriers;
|
|
iface->handle_enable = handle_enable;
|
|
iface->handle_disable = handle_disable;
|
|
iface->handle_release = handle_release;
|
|
iface->handle_connect_to_eis = handle_connect_to_eis;
|
|
iface->handle_close = handle_close;
|
|
iface->handle_get_zones = handle_get_zones;
|
|
}
|
|
|
|
static void
|
|
meta_dbus_session_init_iface (MetaDbusSessionInterface *iface)
|
|
{
|
|
iface->close = meta_input_capture_session_close;
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_finalize (GObject *object)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object);
|
|
|
|
g_clear_pointer (&session->barriers, g_hash_table_unref);
|
|
|
|
g_clear_object (&session->handle);
|
|
g_free (session->peer_name);
|
|
g_free (session->session_id);
|
|
g_free (session->object_path);
|
|
g_clear_object (&session->viewports);
|
|
g_clear_pointer (&session->keymap_file, meta_anonymous_file_free);
|
|
g_clear_pointer (&session->eis_source, g_source_destroy);
|
|
g_clear_pointer (&session->eis, eis_unref);
|
|
|
|
G_OBJECT_CLASS (meta_input_capture_session_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_eis_log_handler (struct eis *eis,
|
|
enum eis_log_priority priority,
|
|
const char *message,
|
|
struct eis_log_context *ctx)
|
|
{
|
|
int message_length = strlen (message);
|
|
|
|
if (priority >= EIS_LOG_PRIORITY_ERROR)
|
|
g_critical ("EIS: %.*s", message_length, message);
|
|
else if (priority >= EIS_LOG_PRIORITY_WARNING)
|
|
g_warning ("EIS: %.*s", message_length, message);
|
|
else if (priority >= EIS_LOG_PRIORITY_INFO)
|
|
g_info ("EIS: %.*s", message_length, message);
|
|
else
|
|
meta_topic (META_DEBUG_INPUT, "EIS: %.*s", message_length, message);
|
|
}
|
|
|
|
static gboolean
|
|
meta_eis_source_prepare (gpointer user_data)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (user_data);
|
|
struct eis_event *eis_event;
|
|
gboolean retval;
|
|
|
|
eis_event = eis_peek_event (session->eis);
|
|
retval = !!eis_event;
|
|
eis_event_unref (eis_event);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static gboolean
|
|
meta_eis_source_dispatch (gpointer user_data)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (user_data);
|
|
|
|
eis_dispatch (session->eis);
|
|
|
|
while (TRUE)
|
|
{
|
|
struct eis_event *eis_event;
|
|
|
|
eis_event = eis_get_event (session->eis);
|
|
if (!eis_event)
|
|
break;
|
|
|
|
process_eis_event (session, eis_event);
|
|
eis_event_unref (eis_event);
|
|
}
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_constructed (GObject *object)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object);
|
|
MetaBackend *backend =
|
|
meta_dbus_session_manager_get_backend (session->session_manager);
|
|
static unsigned int global_session_number = 0;
|
|
int fd;
|
|
GSource *source;
|
|
|
|
session->object_path =
|
|
g_strdup_printf (META_INPUT_CAPTURE_SESSION_DBUS_PATH "/u%u",
|
|
++global_session_number);
|
|
|
|
session->barriers = g_hash_table_new_full (NULL, NULL, NULL,
|
|
input_capture_barrier_free);
|
|
|
|
session->eis = eis_new (session);
|
|
eis_log_set_handler (session->eis, meta_eis_log_handler);
|
|
eis_log_set_priority (session->eis, EIS_LOG_PRIORITY_DEBUG);
|
|
eis_setup_backend_fd (session->eis);
|
|
|
|
fd = eis_get_fd (session->eis);
|
|
source = meta_create_fd_source (fd,
|
|
"[mutter] eis",
|
|
meta_eis_source_prepare,
|
|
meta_eis_source_dispatch,
|
|
session,
|
|
NULL);
|
|
session->eis_source = source;
|
|
g_source_attach (source, NULL);
|
|
g_source_unref (source);
|
|
|
|
g_signal_connect (backend, "keymap-changed",
|
|
G_CALLBACK (on_keymap_changed), session);
|
|
|
|
G_OBJECT_CLASS (meta_input_capture_session_parent_class)->constructed (object);
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case N_PROPS + META_DBUS_SESSION_PROP_SESSION_MANAGER:
|
|
session->session_manager = g_value_get_object (value);
|
|
break;
|
|
case N_PROPS + META_DBUS_SESSION_PROP_PEER_NAME:
|
|
session->peer_name = g_value_dup_string (value);
|
|
break;
|
|
case N_PROPS + META_DBUS_SESSION_PROP_ID:
|
|
session->session_id = g_value_dup_string (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case N_PROPS + META_DBUS_SESSION_PROP_SESSION_MANAGER:
|
|
g_value_set_object (value, session->session_manager);
|
|
break;
|
|
case N_PROPS + META_DBUS_SESSION_PROP_PEER_NAME:
|
|
g_value_set_string (value, session->peer_name);
|
|
break;
|
|
case N_PROPS + META_DBUS_SESSION_PROP_ID:
|
|
g_value_set_string (value, session->session_id);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_class_init (MetaInputCaptureSessionClass *klass)
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = meta_input_capture_session_finalize;
|
|
object_class->constructed = meta_input_capture_session_constructed;
|
|
object_class->set_property = meta_input_capture_session_set_property;
|
|
object_class->get_property = meta_input_capture_session_get_property;
|
|
|
|
meta_dbus_session_install_properties (object_class, N_PROPS);
|
|
|
|
quark_barrier_id =
|
|
g_quark_from_static_string ("meta-input-capture-barrier-id-quark");
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_init (MetaInputCaptureSession *session)
|
|
{
|
|
}
|
|
|
|
char *
|
|
meta_input_capture_session_get_object_path (MetaInputCaptureSession *session)
|
|
{
|
|
return session->object_path;
|
|
}
|
|
|
|
static void
|
|
maybe_disable_cancelled_session (MetaInputCaptureSession *session)
|
|
{
|
|
if (!session->cancel_requested)
|
|
return;
|
|
|
|
if (session->keys_pressed == 0 && session->buttons_pressed == 0)
|
|
meta_input_capture_session_disable (session);
|
|
}
|
|
|
|
static void
|
|
update_keys_pressed (MetaInputCaptureSession *session,
|
|
gboolean is_pressed)
|
|
{
|
|
if (is_pressed)
|
|
session->keys_pressed++;
|
|
else if (session->keys_pressed > 0)
|
|
session->keys_pressed--;
|
|
else
|
|
g_warning ("Unbalanced key release");
|
|
|
|
maybe_disable_cancelled_session (session);
|
|
}
|
|
|
|
static void
|
|
update_buttons_pressed (MetaInputCaptureSession *session,
|
|
gboolean is_pressed)
|
|
{
|
|
if (is_pressed)
|
|
session->buttons_pressed++;
|
|
else if (session->buttons_pressed > 0)
|
|
session->buttons_pressed--;
|
|
else
|
|
g_warning ("Unbalanced button release");
|
|
|
|
maybe_disable_cancelled_session (session);
|
|
}
|
|
|
|
gboolean
|
|
meta_input_capture_session_process_event (MetaInputCaptureSession *session,
|
|
const ClutterEvent *event)
|
|
{
|
|
double dx, dy, dx_constrained, dy_constrained;
|
|
|
|
switch (clutter_event_type (event))
|
|
{
|
|
case CLUTTER_MOTION:
|
|
if (!session->eis_pointer)
|
|
return TRUE;
|
|
|
|
clutter_event_get_relative_motion (event, &dx, &dy, NULL, NULL,
|
|
&dx_constrained, &dy_constrained);
|
|
|
|
eis_device_pointer_motion (session->eis_pointer,
|
|
dx - dx_constrained,
|
|
dy - dy_constrained);
|
|
eis_device_frame (session->eis_pointer, eis_now (session->eis));
|
|
break;
|
|
case CLUTTER_BUTTON_PRESS:
|
|
update_buttons_pressed (session, TRUE);
|
|
|
|
if (!session->eis_pointer)
|
|
return TRUE;
|
|
|
|
eis_device_button_button (session->eis_pointer,
|
|
clutter_event_get_event_code (event),
|
|
true);
|
|
eis_device_frame (session->eis_pointer, eis_now (session->eis));
|
|
break;
|
|
case CLUTTER_BUTTON_RELEASE:
|
|
update_buttons_pressed (session, FALSE);
|
|
|
|
if (!session->eis_pointer)
|
|
return TRUE;
|
|
|
|
eis_device_button_button (session->eis_pointer,
|
|
clutter_event_get_event_code (event),
|
|
false);
|
|
eis_device_frame (session->eis_pointer, eis_now (session->eis));
|
|
break;
|
|
case CLUTTER_SCROLL:
|
|
{
|
|
ClutterScrollFinishFlags finish_flags;
|
|
const double factor = 10.0;
|
|
bool stop_x = false, stop_y = false;
|
|
double dx, dy;
|
|
|
|
if (!session->eis_pointer)
|
|
return TRUE;
|
|
|
|
finish_flags = clutter_event_get_scroll_finish_flags (event);
|
|
|
|
if ((finish_flags & CLUTTER_SCROLL_FINISHED_HORIZONTAL))
|
|
stop_x = true;
|
|
if ((finish_flags & CLUTTER_SCROLL_FINISHED_HORIZONTAL))
|
|
stop_y = true;
|
|
|
|
if (stop_x || stop_y)
|
|
eis_device_scroll_stop (session->eis_pointer, stop_x, stop_y);
|
|
|
|
switch (clutter_event_get_scroll_direction (event))
|
|
{
|
|
case CLUTTER_SCROLL_UP:
|
|
eis_device_scroll_discrete (session->eis_pointer, 0, -120);
|
|
break;
|
|
case CLUTTER_SCROLL_DOWN:
|
|
eis_device_scroll_discrete (session->eis_pointer, 0, 120);
|
|
break;
|
|
case CLUTTER_SCROLL_LEFT:
|
|
eis_device_scroll_discrete (session->eis_pointer, -120, 0);
|
|
break;
|
|
case CLUTTER_SCROLL_RIGHT:
|
|
eis_device_scroll_discrete (session->eis_pointer, 120, 0);
|
|
break;
|
|
case CLUTTER_SCROLL_SMOOTH:
|
|
clutter_event_get_scroll_delta (event, &dx, &dy);
|
|
eis_device_scroll_delta (session->eis_pointer,
|
|
dx * factor,
|
|
dy * factor);
|
|
break;
|
|
}
|
|
eis_device_frame (session->eis_pointer, eis_now (session->eis));
|
|
break;
|
|
}
|
|
case CLUTTER_KEY_PRESS:
|
|
update_keys_pressed (session, TRUE);
|
|
|
|
if (!session->eis_keyboard)
|
|
return TRUE;
|
|
|
|
eis_device_keyboard_key (session->eis_keyboard,
|
|
clutter_event_get_event_code (event),
|
|
true);
|
|
eis_device_frame (session->eis_keyboard, eis_now (session->eis));
|
|
break;
|
|
case CLUTTER_KEY_RELEASE:
|
|
update_keys_pressed (session, FALSE);
|
|
|
|
if (!session->eis_keyboard)
|
|
return TRUE;
|
|
|
|
eis_device_keyboard_key (session->eis_keyboard,
|
|
clutter_event_get_event_code (event),
|
|
false);
|
|
eis_device_frame (session->eis_keyboard, eis_now (session->eis));
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
meta_input_capture_session_notify_cancelled (MetaInputCaptureSession *session)
|
|
{
|
|
if (session->cancel_requested)
|
|
return;
|
|
|
|
session->cancel_requested = TRUE;
|
|
|
|
maybe_disable_cancelled_session (session);
|
|
}
|
|
|
|
static MetaInputCaptureSessionHandle *
|
|
meta_input_capture_session_handle_new (MetaInputCaptureSession *session)
|
|
{
|
|
MetaInputCaptureSessionHandle *handle;
|
|
|
|
handle = g_object_new (META_TYPE_INPUT_CAPTURE_SESSION_HANDLE, NULL);
|
|
handle->session = session;
|
|
|
|
return handle;
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_handle_stop (MetaRemoteAccessHandle *handle)
|
|
{
|
|
MetaInputCaptureSession *session;
|
|
|
|
session = META_INPUT_CAPTURE_SESSION_HANDLE (handle)->session;
|
|
if (!session)
|
|
return;
|
|
|
|
meta_dbus_session_close (META_DBUS_SESSION (session));
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_handle_class_init (MetaInputCaptureSessionHandleClass *klass)
|
|
{
|
|
MetaRemoteAccessHandleClass *remote_access_handle_class =
|
|
META_REMOTE_ACCESS_HANDLE_CLASS (klass);
|
|
|
|
remote_access_handle_class->stop = meta_input_capture_session_handle_stop;
|
|
}
|
|
|
|
static void
|
|
meta_input_capture_session_handle_init (MetaInputCaptureSessionHandle *handle)
|
|
{
|
|
}
|