mirror of
https://github.com/brl/mutter.git
synced 2024-11-28 02:50:41 -05:00
3471ef3441
On startup, the cursor is kept hidden if there's any touchscreen available. If the device that was last interacted is removed, we check on available pointing devices though, so we don't possibly hide the pointer if there are further mice/touchpads/etc. Devices being added don't update cursor visibility, we wait for the user interacting with those instead. https://bugzilla.gnome.org/show_bug.cgi?id=712775
617 lines
17 KiB
C
617 lines
17 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/*
|
|
* Copyright (C) 2014 Red Hat
|
|
*
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*
|
|
* Written by:
|
|
* Jasper St. Pierre <jstpierre@mecheye.net>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <meta/meta-backend.h>
|
|
#include "meta-backend-private.h"
|
|
#include "meta-input-settings-private.h"
|
|
|
|
#include "backends/x11/meta-backend-x11.h"
|
|
#include "meta-cursor-tracker-private.h"
|
|
#include "meta-stage.h"
|
|
|
|
#ifdef HAVE_NATIVE_BACKEND
|
|
#include "backends/native/meta-backend-native.h"
|
|
#endif
|
|
|
|
#include "backends/meta-idle-monitor-private.h"
|
|
|
|
#include "backends/meta-monitor-manager-dummy.h"
|
|
|
|
static MetaBackend *_backend;
|
|
|
|
/**
|
|
* meta_get_backend:
|
|
*
|
|
* Accessor for the singleton MetaBackend.
|
|
*
|
|
* Returns: (transfer none): The only #MetaBackend there is.
|
|
*/
|
|
MetaBackend *
|
|
meta_get_backend (void)
|
|
{
|
|
return _backend;
|
|
}
|
|
|
|
struct _MetaBackendPrivate
|
|
{
|
|
MetaMonitorManager *monitor_manager;
|
|
MetaCursorRenderer *cursor_renderer;
|
|
MetaInputSettings *input_settings;
|
|
|
|
ClutterActor *stage;
|
|
};
|
|
typedef struct _MetaBackendPrivate MetaBackendPrivate;
|
|
|
|
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaBackend, meta_backend, G_TYPE_OBJECT);
|
|
|
|
static void
|
|
meta_backend_finalize (GObject *object)
|
|
{
|
|
MetaBackend *backend = META_BACKEND (object);
|
|
MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
|
|
|
|
g_clear_object (&priv->monitor_manager);
|
|
g_clear_object (&priv->input_settings);
|
|
|
|
g_hash_table_destroy (backend->device_monitors);
|
|
|
|
G_OBJECT_CLASS (meta_backend_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_backend_sync_screen_size (MetaBackend *backend)
|
|
{
|
|
MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
|
|
int width, height;
|
|
|
|
meta_monitor_manager_get_screen_size (priv->monitor_manager, &width, &height);
|
|
|
|
META_BACKEND_GET_CLASS (backend)->update_screen_size (backend, width, height);
|
|
}
|
|
|
|
static void
|
|
on_monitors_changed (MetaMonitorManager *monitors,
|
|
gpointer user_data)
|
|
{
|
|
MetaBackend *backend = META_BACKEND (user_data);
|
|
meta_backend_sync_screen_size (backend);
|
|
}
|
|
|
|
static MetaIdleMonitor *
|
|
meta_backend_create_idle_monitor (MetaBackend *backend,
|
|
int device_id)
|
|
{
|
|
return META_BACKEND_GET_CLASS (backend)->create_idle_monitor (backend, device_id);
|
|
}
|
|
|
|
static void
|
|
create_device_monitor (MetaBackend *backend,
|
|
int device_id)
|
|
{
|
|
MetaIdleMonitor *idle_monitor;
|
|
|
|
g_assert (g_hash_table_lookup (backend->device_monitors, &device_id) == NULL);
|
|
|
|
idle_monitor = meta_backend_create_idle_monitor (backend, device_id);
|
|
g_hash_table_insert (backend->device_monitors, &idle_monitor->device_id, idle_monitor);
|
|
}
|
|
|
|
static void
|
|
destroy_device_monitor (MetaBackend *backend,
|
|
int device_id)
|
|
{
|
|
g_hash_table_remove (backend->device_monitors, &device_id);
|
|
}
|
|
|
|
static void
|
|
on_device_added (ClutterDeviceManager *device_manager,
|
|
ClutterInputDevice *device,
|
|
gpointer user_data)
|
|
{
|
|
MetaBackend *backend = META_BACKEND (user_data);
|
|
int device_id = clutter_input_device_get_device_id (device);
|
|
|
|
create_device_monitor (backend, device_id);
|
|
}
|
|
|
|
static inline gboolean
|
|
device_is_slave_touchscreen (ClutterInputDevice *device)
|
|
{
|
|
return (clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_MASTER &&
|
|
clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE);
|
|
}
|
|
|
|
static inline gboolean
|
|
check_has_pointing_device (ClutterDeviceManager *manager)
|
|
{
|
|
const GSList *devices;
|
|
|
|
devices = clutter_device_manager_peek_devices (manager);
|
|
|
|
for (; devices; devices = devices->next)
|
|
{
|
|
ClutterInputDevice *device = devices->data;
|
|
|
|
if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER)
|
|
continue;
|
|
if (clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE ||
|
|
clutter_input_device_get_device_type (device) == CLUTTER_KEYBOARD_DEVICE)
|
|
continue;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static inline gboolean
|
|
check_has_slave_touchscreen (ClutterDeviceManager *manager)
|
|
{
|
|
const GSList *devices;
|
|
|
|
devices = clutter_device_manager_peek_devices (manager);
|
|
|
|
for (; devices; devices = devices->next)
|
|
{
|
|
ClutterInputDevice *device = devices->data;
|
|
|
|
if (clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_MASTER &&
|
|
clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
on_device_removed (ClutterDeviceManager *device_manager,
|
|
ClutterInputDevice *device,
|
|
gpointer user_data)
|
|
{
|
|
MetaBackend *backend = META_BACKEND (user_data);
|
|
int device_id = clutter_input_device_get_device_id (device);
|
|
|
|
destroy_device_monitor (backend, device_id);
|
|
|
|
/* If the device the user last interacted goes away, check again pointer
|
|
* visibility.
|
|
*/
|
|
if (backend->current_device_id == device_id)
|
|
{
|
|
MetaCursorTracker *cursor_tracker = meta_cursor_tracker_get_for_screen (NULL);
|
|
gboolean has_touchscreen, has_pointing_device;
|
|
ClutterInputDeviceType device_type;
|
|
|
|
device_type = clutter_input_device_get_device_type (device);
|
|
has_touchscreen = check_has_slave_touchscreen (device_manager);
|
|
|
|
if (device_type == CLUTTER_TOUCHSCREEN_DEVICE && has_touchscreen)
|
|
{
|
|
/* There's more touchscreens left, keep the pointer hidden */
|
|
meta_cursor_tracker_set_pointer_visible (cursor_tracker, FALSE);
|
|
}
|
|
else if (device_type != CLUTTER_KEYBOARD_DEVICE)
|
|
{
|
|
has_pointing_device = check_has_pointing_device (device_manager);
|
|
meta_cursor_tracker_set_pointer_visible (cursor_tracker,
|
|
has_pointing_device &&
|
|
!has_touchscreen);
|
|
}
|
|
}
|
|
}
|
|
|
|
static MetaMonitorManager *
|
|
create_monitor_manager (MetaBackend *backend)
|
|
{
|
|
if (g_getenv ("META_DUMMY_MONITORS"))
|
|
return g_object_new (META_TYPE_MONITOR_MANAGER_DUMMY, NULL);
|
|
|
|
return META_BACKEND_GET_CLASS (backend)->create_monitor_manager (backend);
|
|
}
|
|
|
|
static void
|
|
meta_backend_real_post_init (MetaBackend *backend)
|
|
{
|
|
MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
|
|
|
|
priv->stage = meta_stage_new ();
|
|
clutter_actor_realize (priv->stage);
|
|
META_BACKEND_GET_CLASS (backend)->select_stage_events (backend);
|
|
|
|
priv->monitor_manager = create_monitor_manager (backend);
|
|
|
|
g_signal_connect (priv->monitor_manager, "monitors-changed",
|
|
G_CALLBACK (on_monitors_changed), backend);
|
|
meta_backend_sync_screen_size (backend);
|
|
|
|
priv->cursor_renderer = META_BACKEND_GET_CLASS (backend)->create_cursor_renderer (backend);
|
|
|
|
backend->device_monitors = g_hash_table_new_full (g_int_hash, g_int_equal,
|
|
NULL, (GDestroyNotify) g_object_unref);
|
|
|
|
{
|
|
MetaCursorTracker *cursor_tracker;
|
|
ClutterDeviceManager *manager;
|
|
gboolean has_touchscreen = FALSE;
|
|
GSList *devices, *l;
|
|
|
|
/* Create the core device monitor. */
|
|
create_device_monitor (backend, 0);
|
|
|
|
manager = clutter_device_manager_get_default ();
|
|
g_signal_connect_object (manager, "device-added",
|
|
G_CALLBACK (on_device_added), backend, 0);
|
|
g_signal_connect_object (manager, "device-removed",
|
|
G_CALLBACK (on_device_removed), backend, 0);
|
|
|
|
devices = clutter_device_manager_list_devices (manager);
|
|
|
|
for (l = devices; l != NULL; l = l->next)
|
|
{
|
|
ClutterInputDevice *device = l->data;
|
|
on_device_added (manager, device, backend);
|
|
has_touchscreen |= device_is_slave_touchscreen (device);
|
|
}
|
|
|
|
cursor_tracker = meta_cursor_tracker_get_for_screen (NULL);
|
|
meta_cursor_tracker_set_pointer_visible (cursor_tracker, !has_touchscreen);
|
|
|
|
g_slist_free (devices);
|
|
}
|
|
|
|
priv->input_settings = meta_input_settings_create ();
|
|
}
|
|
|
|
static MetaCursorRenderer *
|
|
meta_backend_real_create_cursor_renderer (MetaBackend *backend)
|
|
{
|
|
return meta_cursor_renderer_new ();
|
|
}
|
|
|
|
static gboolean
|
|
meta_backend_real_grab_device (MetaBackend *backend,
|
|
int device_id,
|
|
uint32_t timestamp)
|
|
{
|
|
/* Do nothing */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
meta_backend_real_ungrab_device (MetaBackend *backend,
|
|
int device_id,
|
|
uint32_t timestamp)
|
|
{
|
|
/* Do nothing */
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
meta_backend_real_update_screen_size (MetaBackend *backend,
|
|
int width, int height)
|
|
{
|
|
MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
|
|
|
|
clutter_actor_set_size (priv->stage, width, height);
|
|
}
|
|
|
|
static void
|
|
meta_backend_real_select_stage_events (MetaBackend *backend)
|
|
{
|
|
/* Do nothing */
|
|
}
|
|
|
|
static void
|
|
meta_backend_class_init (MetaBackendClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = meta_backend_finalize;
|
|
|
|
klass->post_init = meta_backend_real_post_init;
|
|
klass->create_cursor_renderer = meta_backend_real_create_cursor_renderer;
|
|
klass->grab_device = meta_backend_real_grab_device;
|
|
klass->ungrab_device = meta_backend_real_ungrab_device;
|
|
klass->update_screen_size = meta_backend_real_update_screen_size;
|
|
klass->select_stage_events = meta_backend_real_select_stage_events;
|
|
|
|
g_signal_new ("keymap-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
g_signal_new ("keymap-layout-group-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 1, G_TYPE_UINT);
|
|
g_signal_new ("last-device-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 1, G_TYPE_INT);
|
|
}
|
|
|
|
static void
|
|
meta_backend_init (MetaBackend *backend)
|
|
{
|
|
_backend = backend;
|
|
}
|
|
|
|
static void
|
|
meta_backend_post_init (MetaBackend *backend)
|
|
{
|
|
META_BACKEND_GET_CLASS (backend)->post_init (backend);
|
|
}
|
|
|
|
/**
|
|
* meta_backend_get_idle_monitor: (skip)
|
|
*/
|
|
MetaIdleMonitor *
|
|
meta_backend_get_idle_monitor (MetaBackend *backend,
|
|
int device_id)
|
|
{
|
|
return g_hash_table_lookup (backend->device_monitors, &device_id);
|
|
}
|
|
|
|
/**
|
|
* meta_backend_get_monitor_manager: (skip)
|
|
*/
|
|
MetaMonitorManager *
|
|
meta_backend_get_monitor_manager (MetaBackend *backend)
|
|
{
|
|
MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
|
|
|
|
return priv->monitor_manager;
|
|
}
|
|
|
|
/**
|
|
* meta_backend_get_cursor_renderer: (skip)
|
|
*/
|
|
MetaCursorRenderer *
|
|
meta_backend_get_cursor_renderer (MetaBackend *backend)
|
|
{
|
|
MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
|
|
|
|
return priv->cursor_renderer;
|
|
}
|
|
|
|
/**
|
|
* meta_backend_grab_device: (skip)
|
|
*/
|
|
gboolean
|
|
meta_backend_grab_device (MetaBackend *backend,
|
|
int device_id,
|
|
uint32_t timestamp)
|
|
{
|
|
return META_BACKEND_GET_CLASS (backend)->grab_device (backend, device_id, timestamp);
|
|
}
|
|
|
|
/**
|
|
* meta_backend_ungrab_device: (skip)
|
|
*/
|
|
gboolean
|
|
meta_backend_ungrab_device (MetaBackend *backend,
|
|
int device_id,
|
|
uint32_t timestamp)
|
|
{
|
|
return META_BACKEND_GET_CLASS (backend)->ungrab_device (backend, device_id, timestamp);
|
|
}
|
|
|
|
/**
|
|
* meta_backend_warp_pointer: (skip)
|
|
*/
|
|
void
|
|
meta_backend_warp_pointer (MetaBackend *backend,
|
|
int x,
|
|
int y)
|
|
{
|
|
META_BACKEND_GET_CLASS (backend)->warp_pointer (backend, x, y);
|
|
}
|
|
|
|
void
|
|
meta_backend_set_keymap (MetaBackend *backend,
|
|
const char *layouts,
|
|
const char *variants,
|
|
const char *options)
|
|
{
|
|
META_BACKEND_GET_CLASS (backend)->set_keymap (backend, layouts, variants, options);
|
|
}
|
|
|
|
/**
|
|
* meta_backend_get_keymap: (skip)
|
|
*/
|
|
struct xkb_keymap *
|
|
meta_backend_get_keymap (MetaBackend *backend)
|
|
|
|
{
|
|
return META_BACKEND_GET_CLASS (backend)->get_keymap (backend);
|
|
}
|
|
|
|
void
|
|
meta_backend_lock_layout_group (MetaBackend *backend,
|
|
guint idx)
|
|
{
|
|
META_BACKEND_GET_CLASS (backend)->lock_layout_group (backend, idx);
|
|
}
|
|
|
|
/**
|
|
* meta_backend_get_stage:
|
|
* @backend: A #MetaBackend
|
|
*
|
|
* Gets the global #ClutterStage that's managed by this backend.
|
|
*
|
|
* Returns: (transfer none): the #ClutterStage
|
|
*/
|
|
ClutterActor *
|
|
meta_backend_get_stage (MetaBackend *backend)
|
|
{
|
|
MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
|
|
return priv->stage;
|
|
}
|
|
|
|
void
|
|
meta_backend_update_last_device (MetaBackend *backend,
|
|
int device_id)
|
|
{
|
|
ClutterInputDeviceType device_type;
|
|
MetaCursorTracker *cursor_tracker;
|
|
ClutterDeviceManager *manager;
|
|
ClutterInputDevice *device;
|
|
|
|
if (backend->current_device_id == device_id)
|
|
return;
|
|
|
|
manager = clutter_device_manager_get_default ();
|
|
device = clutter_device_manager_get_device (manager, device_id);
|
|
|
|
if (!device ||
|
|
clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER)
|
|
return;
|
|
|
|
device_type = clutter_input_device_get_device_type (device);
|
|
|
|
cursor_tracker = meta_cursor_tracker_get_for_screen (NULL);
|
|
backend->current_device_id = device_id;
|
|
g_signal_emit_by_name (backend, "last-device-changed", device_id);
|
|
|
|
if (device_type == CLUTTER_KEYBOARD_DEVICE)
|
|
return;
|
|
|
|
switch (device_type)
|
|
{
|
|
case CLUTTER_TOUCHSCREEN_DEVICE:
|
|
meta_cursor_tracker_set_pointer_visible (cursor_tracker, FALSE);
|
|
break;
|
|
default:
|
|
meta_cursor_tracker_set_pointer_visible (cursor_tracker, TRUE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GType
|
|
get_backend_type (void)
|
|
{
|
|
#if defined(CLUTTER_WINDOWING_X11)
|
|
if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11))
|
|
return META_TYPE_BACKEND_X11;
|
|
#endif
|
|
|
|
#if defined(CLUTTER_WINDOWING_EGL) && defined(HAVE_NATIVE_BACKEND)
|
|
if (clutter_check_windowing_backend (CLUTTER_WINDOWING_EGL))
|
|
return META_TYPE_BACKEND_NATIVE;
|
|
#endif
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static void
|
|
meta_create_backend (void)
|
|
{
|
|
/* meta_backend_init() above install the backend globally so
|
|
* so meta_get_backend() works even during initialization. */
|
|
g_object_new (get_backend_type (), NULL);
|
|
}
|
|
|
|
/* Mutter is responsible for pulling events off the X queue, so Clutter
|
|
* doesn't need (and shouldn't) run its normal event source which polls
|
|
* the X fd, but we do have to deal with dispatching events that accumulate
|
|
* in the clutter queue. This happens, for example, when clutter generate
|
|
* enter/leave events on mouse motion - several events are queued in the
|
|
* clutter queue but only one dispatched. It could also happen because of
|
|
* explicit calls to clutter_event_put(). We add a very simple custom
|
|
* event loop source which is simply responsible for pulling events off
|
|
* of the queue and dispatching them before we block for new events.
|
|
*/
|
|
|
|
static gboolean
|
|
event_prepare (GSource *source,
|
|
gint *timeout_)
|
|
{
|
|
*timeout_ = -1;
|
|
|
|
return clutter_events_pending ();
|
|
}
|
|
|
|
static gboolean
|
|
event_check (GSource *source)
|
|
{
|
|
return clutter_events_pending ();
|
|
}
|
|
|
|
static gboolean
|
|
event_dispatch (GSource *source,
|
|
GSourceFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
ClutterEvent *event = clutter_event_get ();
|
|
|
|
if (event)
|
|
{
|
|
clutter_do_event (event);
|
|
clutter_event_free (event);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GSourceFuncs event_funcs = {
|
|
event_prepare,
|
|
event_check,
|
|
event_dispatch
|
|
};
|
|
|
|
/**
|
|
* meta_clutter_init: (skip)
|
|
*/
|
|
void
|
|
meta_clutter_init (void)
|
|
{
|
|
ClutterSettings *clutter_settings;
|
|
GSource *source;
|
|
|
|
meta_create_backend ();
|
|
|
|
if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS)
|
|
g_error ("Unable to initialize Clutter.\n");
|
|
|
|
/*
|
|
* XXX: We cannot handle high dpi scaling yet, so fix the scale to 1
|
|
* for now.
|
|
*/
|
|
clutter_settings = clutter_settings_get_default ();
|
|
g_object_set (clutter_settings, "window-scaling-factor", 1, NULL);
|
|
|
|
source = g_source_new (&event_funcs, sizeof (GSource));
|
|
g_source_attach (source, NULL);
|
|
g_source_unref (source);
|
|
|
|
meta_backend_post_init (_backend);
|
|
}
|