...
 
Commits (6)
  • Marco Trevisan's avatar
    backend: Don't emit last-device updates with no device · e559e21c
    Marco Trevisan authored
    When removing a device that has been just marked as the last in use, we may
    try to notify that a NULL device is the last one.
    
    This is not supported, as both update_last_device() and the clients of the
    "::last-device-changed" signal are assuming that the last device is always
    a valid ClutterInputDevice.
    
    So let's avoid erroring, and stop the idle when clearing the current device.
    
    Related to: #1345
    !1371
    
    (cherry picked from commit 07568267)
    e559e21c
  • Marco Trevisan's avatar
    backend: Use connect-after to perform actions on device removed · 6853ceaf
    Marco Trevisan authored
    When a device is removed we perform some actions such as stopping the
    "::last-device-changed" signal emission and unsetting the current device.
    And we want to be sure that these actions happen after all the
    device-removed operations are sorted out.
    
    Related to: #1345
    !1371
    
    (cherry picked from commit 5730b998)
    6853ceaf
  • Marco Trevisan's avatar
    clutter-event: Add device added/removed events · e283b920
    Marco Trevisan authored
    Add clutter device added and removed events to allow processing of them as
    it happens in the backends, queuing them and performing actions in order.
    
    This allows not to loose any event that is performed just before removing or
    disabling a device, and still process the events in order in the event
    queue.
    
    !1371
    
    (cherry picked from commit 928b32b1)
    e283b920
  • Marco Trevisan's avatar
    clutter-seat: Handle device events and emit signals · 0444769a
    Marco Trevisan authored
    Clutter device events are special events coming from the backend when an
    input device is added or removed.
    
    When such events are processed, we should make the seat to handle them by
    calling vfunc that can be implemented by each backend and eventually
    emitting the appropriate signal.
    
    If a device is removed, we can also safely dispose it, as it can be
    considered stale at this point.
    
    !1371
    
    (cherry picked from commit cf67c54f)
    0444769a
  • Marco Trevisan's avatar
    seat-x11: Translate device enabled/disabled into clutter events · cf67dfb0
    Marco Trevisan authored
    When a device is removed from the seat the events that this device may have
    emitted just before being removed might still be in the stage events queue,
    this may lead a to a crash because:
    
    Once the device is removed, we dispose it and the staling event is
    kept in queue and sent for processing at next loop.
    During event processing we ask the backend to update the last device
    with the disposed device
    The device is disposed once the events referencing it, are free'd
    The actual last device emission happens in an idle, but at this point
    the device may have been free'd, and in any case will be still disposed
    and so not providing useful informations.
    
    To avoid this, once a device has been added/removed from the seat, we queue
    ClutterDeviceEvent events to inform the stack that the device state has
    changed, preserving the order with the other actual generated device events.
    In this way it can't happen that we emit another event before that the
    device has been added or after that it has been removed.
    
    Fixes: #1345
    
    (cherry picked from commit 9db289b4)
    cf67dfb0
  • Marco Trevisan's avatar
    seat-native: Process device added/removed events as ClutterEvents · f5e4abd4
    Marco Trevisan authored
    Delay the addition and removal of devices using ClutterDeviceEvent's so that
    they are processed following the libinput event order, and that we don't
    have to flush the events on removal.
    
    !1371
    
    (cherry picked from commit e44c42f2)
    f5e4abd4
......@@ -13995,6 +13995,9 @@ clutter_actor_event (ClutterActor *actor,
case CLUTTER_IM_PREEDIT:
detail = quark_im;
break;
case CLUTTER_DEVICE_ADDED:
case CLUTTER_DEVICE_REMOVED:
break;
case CLUTTER_EVENT_LAST: /* Just keep compiler warnings quiet */
break;
}
......
......@@ -937,6 +937,8 @@ typedef enum /*< prefix=CLUTTER >*/
CLUTTER_IM_COMMIT,
CLUTTER_IM_DELETE,
CLUTTER_IM_PREEDIT,
CLUTTER_DEVICE_ADDED,
CLUTTER_DEVICE_REMOVED,
CLUTTER_EVENT_LAST /* helper */
} ClutterEventType;
......
......@@ -418,6 +418,8 @@ clutter_event_get_position (const ClutterEvent *event,
case CLUTTER_IM_COMMIT:
case CLUTTER_IM_DELETE:
case CLUTTER_IM_PREEDIT:
case CLUTTER_DEVICE_ADDED:
case CLUTTER_DEVICE_REMOVED:
graphene_point_init (position, 0.f, 0.f);
break;
......@@ -495,6 +497,8 @@ clutter_event_set_coords (ClutterEvent *event,
case CLUTTER_IM_COMMIT:
case CLUTTER_IM_DELETE:
case CLUTTER_IM_PREEDIT:
case CLUTTER_DEVICE_ADDED:
case CLUTTER_DEVICE_REMOVED:
break;
case CLUTTER_ENTER:
......@@ -1170,6 +1174,11 @@ clutter_event_set_device (ClutterEvent *event,
case CLUTTER_PAD_RING:
event->pad_ring.device = device;
break;
case CLUTTER_DEVICE_ADDED:
case CLUTTER_DEVICE_REMOVED:
event->device.device = device;
break;
}
}
......@@ -1270,6 +1279,11 @@ clutter_event_get_device (const ClutterEvent *event)
case CLUTTER_PAD_RING:
device = event->pad_ring.device;
break;
case CLUTTER_DEVICE_ADDED:
case CLUTTER_DEVICE_REMOVED:
device = event->device.device;
break;
}
return device;
......@@ -1428,6 +1442,11 @@ clutter_event_copy (const ClutterEvent *event)
new_event->im.text = g_strdup (event->im.text);
break;
case CLUTTER_DEVICE_ADDED:
case CLUTTER_DEVICE_REMOVED:
new_event->device.device = event->device.device;
break;
default:
break;
}
......@@ -1755,6 +1774,8 @@ clutter_event_get_axes (const ClutterEvent *event,
case CLUTTER_EVENT_LAST:
case CLUTTER_PROXIMITY_IN:
case CLUTTER_PROXIMITY_OUT:
case CLUTTER_DEVICE_ADDED:
case CLUTTER_DEVICE_REMOVED:
break;
case CLUTTER_SCROLL:
......
......@@ -122,6 +122,7 @@ typedef struct _ClutterPadButtonEvent ClutterPadButtonEvent;
typedef struct _ClutterPadStripEvent ClutterPadStripEvent;
typedef struct _ClutterPadRingEvent ClutterPadRingEvent;
typedef struct _ClutterIMEvent ClutterIMEvent;
typedef struct _ClutterDeviceEvent ClutterDeviceEvent;
/**
* ClutterAnyEvent:
......@@ -558,6 +559,17 @@ struct _ClutterIMEvent
uint32_t len;
};
struct _ClutterDeviceEvent
{
ClutterEventType type;
guint32 time;
ClutterEventFlags flags;
ClutterStage *stage;
ClutterActor *source;
ClutterInputDevice *device;
};
/**
* ClutterEvent:
*
......@@ -585,6 +597,7 @@ union _ClutterEvent
ClutterPadStripEvent pad_strip;
ClutterPadRingEvent pad_ring;
ClutterIMEvent im;
ClutterDeviceEvent device;
};
/**
......
......@@ -2119,6 +2119,17 @@ _clutter_process_event_details (ClutterActor *stage,
case CLUTTER_CLIENT_MESSAGE:
break;
case CLUTTER_DEVICE_ADDED:
case CLUTTER_DEVICE_REMOVED:
if (!_clutter_event_process_filters (event))
{
ClutterSeat *seat;
seat = clutter_backend_get_default_seat (context->backend);
clutter_seat_handle_device_event (seat, event);
}
break;
case CLUTTER_EVENT_LAST:
break;
}
......
......@@ -663,6 +663,46 @@ clutter_seat_compress_motion (ClutterSeat *seat,
seat_class->compress_motion (seat, event, to_discard);
}
gboolean
clutter_seat_handle_device_event (ClutterSeat *seat,
ClutterEvent *event)
{
ClutterSeatClass *seat_class;
ClutterInputDevice *device;
g_return_val_if_fail (CLUTTER_IS_SEAT (seat), FALSE);
g_return_val_if_fail (event, FALSE);
g_assert (event->type == CLUTTER_DEVICE_ADDED ||
event->type == CLUTTER_DEVICE_REMOVED);
seat_class = CLUTTER_SEAT_GET_CLASS (seat);
if (seat_class->handle_device_event)
{
if (!seat_class->handle_device_event (seat, event))
return FALSE;
}
device = clutter_event_get_source_device (event);
g_assert_true (CLUTTER_IS_INPUT_DEVICE (device));
switch (event->type)
{
case CLUTTER_DEVICE_ADDED:
g_signal_emit (seat, signals[DEVICE_ADDED], 0, device);
break;
case CLUTTER_DEVICE_REMOVED:
g_signal_emit (seat, signals[DEVICE_REMOVED], 0, device);
g_object_run_dispose (G_OBJECT (device));
break;
default:
break;
}
return TRUE;
}
void
clutter_seat_warp_pointer (ClutterSeat *seat,
int x,
......
......@@ -106,6 +106,9 @@ struct _ClutterSeatClass
ClutterEvent *event,
const ClutterEvent *to_discard);
gboolean (* handle_device_event) (ClutterSeat *seat,
ClutterEvent *event);
void (* warp_pointer) (ClutterSeat *seat,
int x,
int y);
......@@ -180,6 +183,9 @@ void clutter_seat_compress_motion (ClutterSeat *seat,
ClutterEvent *event,
const ClutterEvent *to_discard);
gboolean clutter_seat_handle_device_event (ClutterSeat *seat,
ClutterEvent *event);
CLUTTER_EXPORT
void clutter_seat_warp_pointer (ClutterSeat *seat,
int x,
......
......@@ -445,6 +445,7 @@ on_device_removed (ClutterSeat *seat,
ClutterInputDeviceType device_type;
priv->current_device = NULL;
g_clear_handle_id (&priv->device_update_idle_id, g_source_remove);
device_type = clutter_input_device_get_device_type (device);
has_touchscreen = check_has_slave_touchscreen (seat);
......@@ -536,7 +537,8 @@ meta_backend_real_post_init (MetaBackend *backend)
g_signal_connect_object (seat, "device-added",
G_CALLBACK (on_device_added), backend, 0);
g_signal_connect_object (seat, "device-removed",
G_CALLBACK (on_device_removed), backend, 0);
G_CALLBACK (on_device_removed), backend,
G_CONNECT_AFTER);
set_initial_pointer_visibility (backend, seat);
......
......@@ -1404,14 +1404,13 @@ update_touch_mode (MetaSeatNative *seat)
}
}
static void
static ClutterInputDevice *
evdev_add_device (MetaSeatNative *seat,
struct libinput_device *libinput_device)
{
ClutterInputDeviceType type;
ClutterInputDevice *device, *master = NULL;
ClutterActor *stage;
gboolean check_touch_mode = FALSE;
device = meta_input_device_native_new (seat, libinput_device);
stage = CLUTTER_ACTOR (meta_seat_native_get_stage (seat));
......@@ -1434,25 +1433,7 @@ evdev_add_device (MetaSeatNative *seat,
_clutter_input_device_add_slave (master, device);
}
g_signal_emit_by_name (seat, "device-added", device);
if (type == CLUTTER_TOUCHSCREEN_DEVICE)
{
seat->has_touchscreen = TRUE;
check_touch_mode = TRUE;
}
if (libinput_device_has_capability (libinput_device,
LIBINPUT_DEVICE_CAP_SWITCH) &&
libinput_device_switch_has_switch (libinput_device,
LIBINPUT_SWITCH_TABLET_MODE))
{
seat->has_tablet_switch = TRUE;
check_touch_mode = TRUE;
}
if (check_touch_mode)
update_touch_mode (seat);
return device;
}
static void
......@@ -1460,74 +1441,98 @@ evdev_remove_device (MetaSeatNative *seat,
MetaInputDeviceNative *device_evdev)
{
ClutterInputDevice *device;
ClutterInputDeviceType device_type;
device = CLUTTER_INPUT_DEVICE (device_evdev);
seat->devices = g_slist_remove (seat->devices, device);
g_signal_emit_by_name (seat, "device-removed", device);
g_object_unref (device);
}
static gboolean
meta_seat_native_handle_device_event (ClutterSeat *seat,
ClutterEvent *event)
{
MetaSeatNative *seat_native = META_SEAT_NATIVE (seat);
ClutterInputDevice *device = event->device.device;
MetaInputDeviceNative *device_native = META_INPUT_DEVICE_NATIVE (device);
gboolean check_touch_mode;
device_type = clutter_input_device_get_device_type (device);
check_touch_mode =
clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE;
if (device_type == CLUTTER_TOUCHSCREEN_DEVICE)
switch (event->type)
{
seat->has_touchscreen = has_touchscreen (seat);
update_touch_mode (seat);
}
case CLUTTER_DEVICE_ADDED:
seat_native->has_touchscreen = check_touch_mode;
if (seat->repeat_timer && seat->repeat_device == device)
meta_seat_native_clear_repeat_timer (seat);
if (libinput_device_has_capability (device_native->libinput_device,
LIBINPUT_DEVICE_CAP_SWITCH) &&
libinput_device_switch_has_switch (device_native->libinput_device,
LIBINPUT_SWITCH_TABLET_MODE))
{
seat_native->has_tablet_switch = TRUE;
check_touch_mode = TRUE;
}
break;
g_object_run_dispose (G_OBJECT (device));
g_object_unref (device);
}
case CLUTTER_DEVICE_REMOVED:
if (check_touch_mode)
seat_native->has_touchscreen = has_touchscreen (seat_native);
static void
flush_event_queue (void)
{
ClutterEvent *event;
if (seat_native->repeat_timer && seat_native->repeat_device == device)
meta_seat_native_clear_repeat_timer (seat_native);
break;
while ((event = clutter_event_get ()) != NULL)
{
_clutter_process_event (event);
clutter_event_free (event);
default:
break;
}
if (check_touch_mode)
update_touch_mode (seat_native);
return TRUE;
}
static gboolean
process_base_event (MetaSeatNative *seat,
struct libinput_event *event)
{
ClutterInputDevice *device;
ClutterInputDevice *device = NULL;
ClutterEvent *device_event;
struct libinput_device *libinput_device;
gboolean handled = TRUE;
switch (libinput_event_get_type (event))
{
case LIBINPUT_EVENT_DEVICE_ADDED:
libinput_device = libinput_event_get_device (event);
evdev_add_device (seat, libinput_device);
device = evdev_add_device (seat, libinput_device);
device_event = clutter_event_new (CLUTTER_DEVICE_ADDED);
clutter_event_set_device (device_event, device);
break;
case LIBINPUT_EVENT_DEVICE_REMOVED:
/* Flush all queued events, there
* might be some from this device.
*/
flush_event_queue ();
libinput_device = libinput_event_get_device (event);
device = libinput_device_get_user_data (libinput_device);
device_event = clutter_event_new (CLUTTER_DEVICE_REMOVED);
clutter_event_set_device (device_event, device);
evdev_remove_device (seat,
META_INPUT_DEVICE_NATIVE (device));
break;
default:
handled = FALSE;
device_event = NULL;
}
return handled;
if (device_event)
{
device_event->device.stage = _clutter_input_device_get_stage (device);
queue_event (device_event);
return TRUE;
}
return FALSE;
}
static ClutterScrollSource
......@@ -2781,6 +2786,7 @@ meta_seat_native_class_init (MetaSeatNativeClass *klass)
seat_class->get_supported_virtual_device_types = meta_seat_native_get_supported_virtual_device_types;
seat_class->compress_motion = meta_seat_native_compress_motion;
seat_class->warp_pointer = meta_seat_native_warp_pointer;
seat_class->handle_device_event = meta_seat_native_handle_device_event;
props[PROP_SEAT_ID] =
g_param_spec_string ("seat-id",
......
......@@ -654,8 +654,6 @@ add_device (MetaSeatX11 *seat_x11,
info->attachment == seat_x11->keyboard_id))
{
seat_x11->devices = g_list_prepend (seat_x11->devices, device);
seat_x11->has_touchscreens |=
clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE;
}
else
{
......@@ -679,13 +677,9 @@ add_device (MetaSeatX11 *seat_x11,
GINT_TO_POINTER (info->attachment));
_clutter_input_device_set_associated_device (device, master);
_clutter_input_device_add_slave (master, device);
g_signal_emit_by_name (seat_x11, "device-added", device);
}
}
update_touch_mode (seat_x11);
return device;
}
......@@ -704,44 +698,51 @@ has_touchscreens (MetaSeatX11 *seat_x11)
}
static void
remove_device (MetaSeatX11 *seat_x11,
int device_id)
remove_device (MetaSeatX11 *seat_x11,
ClutterInputDevice *device)
{
ClutterInputDevice *device;
gboolean check_touchscreens = FALSE;
if (seat_x11->core_pointer == device)
{
seat_x11->core_pointer = NULL;
}
else if (seat_x11->core_keyboard == device)
{
seat_x11->core_keyboard = NULL;
}
else
{
seat_x11->devices = g_list_remove (seat_x11->devices, device);
}
}
device = g_hash_table_lookup (seat_x11->devices_by_id,
GINT_TO_POINTER (device_id));
static gboolean
meta_seat_x11_handle_device_event (ClutterSeat *seat,
ClutterEvent *event)
{
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
ClutterInputDevice *device = event->device.device;
gboolean is_touch;
if (clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE)
check_touchscreens = TRUE;
is_touch =
clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE;
if (device != NULL)
switch (event->type)
{
if (seat_x11->core_pointer == device)
{
seat_x11->core_pointer = NULL;
}
else if (seat_x11->core_keyboard == device)
{
seat_x11->core_keyboard = NULL;
}
else
{
seat_x11->devices = g_list_remove (seat_x11->devices, device);
g_signal_emit_by_name (seat_x11, "device-removed", device);
}
g_object_run_dispose (G_OBJECT (device));
g_hash_table_remove (seat_x11->devices_by_id,
GINT_TO_POINTER (device_id));
case CLUTTER_DEVICE_ADDED:
seat_x11->has_touchscreens |= is_touch;
break;
case CLUTTER_DEVICE_REMOVED:
if (is_touch)
seat_x11->has_touchscreens = has_touchscreens (seat_x11);
break;
default:
break;
}
if (check_touchscreens)
{
seat_x11->has_touchscreens = has_touchscreens (seat_x11);
update_touch_mode (seat_x11);
}
if (is_touch)
update_touch_mode (seat_x11);
return TRUE;
}
static void
......@@ -803,12 +804,14 @@ device_get_tool_serial (ClutterInputDevice *device)
return serial_id;
}
static void
static gboolean
translate_hierarchy_event (ClutterBackend *backend,
MetaSeatX11 *seat_x11,
XIHierarchyEvent *ev)
XIHierarchyEvent *ev,
ClutterEvent *event)
{
int i;
gboolean retval = FALSE;
for (i = 0; i < ev->num_info; i++)
{
......@@ -828,15 +831,38 @@ translate_hierarchy_event (ClutterBackend *backend,
clutter_x11_untrap_x_errors ();
if (info != NULL)
{
add_device (seat_x11, backend, &info[0], FALSE);
ClutterInputDevice *device;
device = add_device (seat_x11, backend, &info[0], FALSE);
event->any.type = CLUTTER_DEVICE_ADDED;
event->any.time = ev->time;
clutter_event_set_device (event, device);
retval = TRUE;
XIFreeDeviceInfo (info);
}
}
else if (ev->info[i].flags & XIDeviceDisabled)
{
g_autoptr (ClutterInputDevice) device = NULL;
g_debug ("Hierarchy event: device disabled");
remove_device (seat_x11, ev->info[i].deviceid);
g_hash_table_steal_extended (seat_x11->devices_by_id,
GINT_TO_POINTER (ev->info[i].deviceid),
NULL,
(gpointer) &device);
if (device != NULL)
{
remove_device (seat_x11, device);
event->any.type = CLUTTER_DEVICE_REMOVED;
event->any.time = ev->time;
clutter_event_set_device (event, device);
retval = TRUE;
}
}
else if ((ev->info[i].flags & XISlaveAttached) ||
(ev->info[i].flags & XISlaveDetached))
......@@ -883,6 +909,8 @@ translate_hierarchy_event (ClutterBackend *backend,
}
}
}
return retval;
}
static void
......@@ -1115,6 +1143,9 @@ get_event_stage (MetaSeatX11 *seat_x11,
}
break;
case XI_HierarchyChanged:
return CLUTTER_STAGE (meta_backend_get_stage (meta_get_backend ()));
default:
break;
}
......@@ -1574,6 +1605,7 @@ meta_seat_x11_class_init (MetaSeatX11Class *klass)
seat_class->create_virtual_device = meta_seat_x11_create_virtual_device;
seat_class->get_supported_virtual_device_types = meta_seat_x11_get_supported_virtual_device_types;
seat_class->warp_pointer = meta_seat_x11_warp_pointer;
seat_class->handle_device_event = meta_seat_x11_handle_device_event;
props[PROP_OPCODE] =
g_param_spec_int ("opcode",
......@@ -1676,8 +1708,7 @@ meta_seat_x11_translate_event (MetaSeatX11 *seat,
return FALSE;
}
if (!(xi_event->evtype == XI_HierarchyChanged ||
xi_event->evtype == XI_DeviceChanged ||
if (!(xi_event->evtype == XI_DeviceChanged ||
xi_event->evtype == XI_PropertyEvent))
{
stage = get_event_stage (seat, xi_event);
......@@ -1695,9 +1726,8 @@ meta_seat_x11_translate_event (MetaSeatX11 *seat,
{
XIHierarchyEvent *xev = (XIHierarchyEvent *) xi_event;
translate_hierarchy_event (backend, seat, xev);
retval = translate_hierarchy_event (backend, seat, xev, event);
}
retval = FALSE;
break;
case XI_DeviceChanged:
......