forked from AuroraMiddleware/gtk
gdk: handle implicit touch grabs
If the touch sequence happens on a window with GDK_TOUCH_MASK set, a GdkTouchGrabInfo is created to back it up. Else a device grab is only created if the sequence emulates the pointer. If both a device and a touch grab are present on a window, the later of them both is obeyed, Any grab on the device happening after a touch grab generates grab-broken on all the windows an implicit touch grab was going on.
This commit is contained in:
parent
b5cfdf2db4
commit
c72a77b04c
@ -1049,12 +1049,15 @@ _gdk_display_device_grab_update (GdkDisplay *display,
|
|||||||
next_grab = NULL; /* Actually its not yet active */
|
next_grab = NULL; /* Actually its not yet active */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (next_grab)
|
||||||
|
_gdk_display_break_touch_grabs (display, device, next_grab->window);
|
||||||
|
|
||||||
if ((next_grab == NULL && current_grab->implicit_ungrab) ||
|
if ((next_grab == NULL && current_grab->implicit_ungrab) ||
|
||||||
(next_grab != NULL && current_grab->window != next_grab->window))
|
(next_grab != NULL && current_grab->window != next_grab->window))
|
||||||
generate_grab_broken_event (GDK_WINDOW (current_grab->window),
|
generate_grab_broken_event (GDK_WINDOW (current_grab->window),
|
||||||
device,
|
device,
|
||||||
current_grab->implicit,
|
current_grab->implicit,
|
||||||
next_grab? next_grab->window : NULL);
|
next_grab? next_grab->window : NULL);
|
||||||
|
|
||||||
/* Remove old grab */
|
/* Remove old grab */
|
||||||
grabs = g_list_delete_link (grabs, grabs);
|
grabs = g_list_delete_link (grabs, grabs);
|
||||||
|
208
gdk/gdkwindow.c
208
gdk/gdkwindow.c
@ -8156,6 +8156,8 @@ is_button_type (GdkEventType type)
|
|||||||
type == GDK_2BUTTON_PRESS ||
|
type == GDK_2BUTTON_PRESS ||
|
||||||
type == GDK_3BUTTON_PRESS ||
|
type == GDK_3BUTTON_PRESS ||
|
||||||
type == GDK_BUTTON_RELEASE ||
|
type == GDK_BUTTON_RELEASE ||
|
||||||
|
type == GDK_TOUCH_BEGIN ||
|
||||||
|
type == GDK_TOUCH_END ||
|
||||||
type == GDK_SCROLL;
|
type == GDK_SCROLL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8163,6 +8165,7 @@ static gboolean
|
|||||||
is_motion_type (GdkEventType type)
|
is_motion_type (GdkEventType type)
|
||||||
{
|
{
|
||||||
return type == GDK_MOTION_NOTIFY ||
|
return type == GDK_MOTION_NOTIFY ||
|
||||||
|
type == GDK_TOUCH_UPDATE ||
|
||||||
type == GDK_ENTER_NOTIFY ||
|
type == GDK_ENTER_NOTIFY ||
|
||||||
type == GDK_LEAVE_NOTIFY;
|
type == GDK_LEAVE_NOTIFY;
|
||||||
}
|
}
|
||||||
@ -9109,18 +9112,37 @@ _gdk_synthesize_crossing_events_for_geometry_change (GdkWindow *changed_window)
|
|||||||
static GdkWindow *
|
static GdkWindow *
|
||||||
get_event_window (GdkDisplay *display,
|
get_event_window (GdkDisplay *display,
|
||||||
GdkDevice *device,
|
GdkDevice *device,
|
||||||
GdkWindow *pointer_window,
|
GdkEventSequence *sequence,
|
||||||
GdkEventType type,
|
GdkWindow *pointer_window,
|
||||||
GdkModifierType mask,
|
GdkEventType type,
|
||||||
guint *evmask_out,
|
GdkModifierType mask,
|
||||||
gulong serial)
|
guint *evmask_out,
|
||||||
|
gulong serial)
|
||||||
{
|
{
|
||||||
guint evmask;
|
guint evmask;
|
||||||
GdkWindow *grab_window;
|
GdkWindow *grab_window;
|
||||||
GdkDeviceGrabInfo *grab;
|
GdkDeviceGrabInfo *grab;
|
||||||
|
GdkTouchGrabInfo *touch_grab;
|
||||||
|
|
||||||
|
touch_grab = _gdk_display_has_touch_grab (display, device, sequence, serial);
|
||||||
grab = _gdk_display_has_device_grab (display, device, serial);
|
grab = _gdk_display_has_device_grab (display, device, serial);
|
||||||
|
|
||||||
|
if (touch_grab != NULL &&
|
||||||
|
(!grab || grab->implicit || touch_grab->serial >= grab->serial_start))
|
||||||
|
{
|
||||||
|
evmask = touch_grab->event_mask;
|
||||||
|
evmask = update_evmask_for_button_motion (evmask, mask);
|
||||||
|
|
||||||
|
if (evmask & type_masks[type])
|
||||||
|
{
|
||||||
|
if (evmask_out)
|
||||||
|
*evmask_out = evmask;
|
||||||
|
return touch_grab->window;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (grab != NULL && !grab->owner_events)
|
if (grab != NULL && !grab->owner_events)
|
||||||
{
|
{
|
||||||
evmask = grab->event_mask;
|
evmask = grab->event_mask;
|
||||||
@ -9320,14 +9342,19 @@ proxy_pointer_event (GdkDisplay *display,
|
|||||||
serial, non_linear);
|
serial, non_linear);
|
||||||
_gdk_display_set_window_under_pointer (display, device, pointer_window);
|
_gdk_display_set_window_under_pointer (display, device, pointer_window);
|
||||||
}
|
}
|
||||||
else if (source_event->type == GDK_MOTION_NOTIFY)
|
else if (source_event->type == GDK_MOTION_NOTIFY ||
|
||||||
|
source_event->type == GDK_TOUCH_UPDATE)
|
||||||
{
|
{
|
||||||
GdkWindow *event_win;
|
GdkWindow *event_win;
|
||||||
guint evmask;
|
guint evmask;
|
||||||
gboolean is_hint;
|
gboolean is_hint;
|
||||||
|
GdkEventSequence *sequence;
|
||||||
|
|
||||||
|
sequence = gdk_event_get_event_sequence (source_event);
|
||||||
|
|
||||||
event_win = get_event_window (display,
|
event_win = get_event_window (display,
|
||||||
device,
|
device,
|
||||||
|
sequence,
|
||||||
pointer_window,
|
pointer_window,
|
||||||
source_event->type,
|
source_event->type,
|
||||||
state,
|
state,
|
||||||
@ -9351,6 +9378,7 @@ proxy_pointer_event (GdkDisplay *display,
|
|||||||
toplevel_x, toplevel_y,
|
toplevel_x, toplevel_y,
|
||||||
state, time_, NULL,
|
state, time_, NULL,
|
||||||
serial, FALSE);
|
serial, FALSE);
|
||||||
|
|
||||||
is_hint = FALSE;
|
is_hint = FALSE;
|
||||||
|
|
||||||
if (event_win &&
|
if (event_win &&
|
||||||
@ -9371,21 +9399,34 @@ proxy_pointer_event (GdkDisplay *display,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event_win && !display->ignore_core_events)
|
if (!event_win)
|
||||||
{
|
return TRUE;
|
||||||
event = _gdk_make_event (event_win, GDK_MOTION_NOTIFY, source_event, FALSE);
|
|
||||||
event->motion.time = time_;
|
if (!display->ignore_core_events)
|
||||||
convert_toplevel_coords_to_window (event_win,
|
{
|
||||||
toplevel_x, toplevel_y,
|
GdkEventType event_type;
|
||||||
&event->motion.x, &event->motion.y);
|
|
||||||
event->motion.x_root = source_event->motion.x_root;
|
event_type = source_event->type;
|
||||||
event->motion.y_root = source_event->motion.y_root;
|
|
||||||
event->motion.state = state;
|
event = gdk_event_new (event_type);
|
||||||
event->motion.is_hint = is_hint;
|
event->any.window = g_object_ref (event_win);
|
||||||
event->motion.device = source_event->motion.device;
|
event->any.send_event = source_event->any.send_event;
|
||||||
|
event->motion.time = time_;
|
||||||
|
convert_toplevel_coords_to_window (event_win,
|
||||||
|
toplevel_x, toplevel_y,
|
||||||
|
&event->motion.x, &event->motion.y);
|
||||||
|
event->motion.x_root = source_event->motion.x_root;
|
||||||
|
event->motion.y_root = source_event->motion.y_root;
|
||||||
|
event->motion.state = state;
|
||||||
|
event->motion.is_hint = is_hint;
|
||||||
|
event->motion.device = source_event->motion.device;
|
||||||
event->motion.axes = g_memdup (source_event->motion.axes,
|
event->motion.axes = g_memdup (source_event->motion.axes,
|
||||||
sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device));
|
sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device));
|
||||||
gdk_event_set_source_device (event, source_device);
|
gdk_event_set_source_device (event, source_device);
|
||||||
|
|
||||||
|
/* Just insert the event */
|
||||||
|
_gdk_event_queue_insert_after (gdk_window_get_display (event_win),
|
||||||
|
source_event, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9417,6 +9458,8 @@ proxy_button_event (GdkEvent *source_event,
|
|||||||
GdkDisplay *display;
|
GdkDisplay *display;
|
||||||
GdkWindow *w;
|
GdkWindow *w;
|
||||||
GdkDevice *device, *source_device;
|
GdkDevice *device, *source_device;
|
||||||
|
GdkEventMask evmask;
|
||||||
|
GdkEventSequence *sequence;
|
||||||
|
|
||||||
type = source_event->any.type;
|
type = source_event->any.type;
|
||||||
event_window = source_event->any.window;
|
event_window = source_event->any.window;
|
||||||
@ -9429,9 +9472,13 @@ proxy_button_event (GdkEvent *source_event,
|
|||||||
toplevel_window = convert_native_coords_to_toplevel (event_window,
|
toplevel_window = convert_native_coords_to_toplevel (event_window,
|
||||||
toplevel_x, toplevel_y,
|
toplevel_x, toplevel_y,
|
||||||
&toplevel_x, &toplevel_y);
|
&toplevel_x, &toplevel_y);
|
||||||
|
|
||||||
|
sequence = gdk_event_get_event_sequence (source_event);
|
||||||
|
|
||||||
pointer_info = _gdk_display_get_pointer_info (display, device);
|
pointer_info = _gdk_display_get_pointer_info (display, device);
|
||||||
|
|
||||||
if (type == GDK_BUTTON_PRESS &&
|
if ((type == GDK_BUTTON_PRESS ||
|
||||||
|
type == GDK_TOUCH_BEGIN) &&
|
||||||
!source_event->any.send_event &&
|
!source_event->any.send_event &&
|
||||||
_gdk_display_has_device_grab (display, device, serial) == NULL)
|
_gdk_display_has_device_grab (display, device, serial) == NULL)
|
||||||
{
|
{
|
||||||
@ -9446,23 +9493,46 @@ proxy_button_event (GdkEvent *source_event,
|
|||||||
(parent = get_event_parent (w)) != NULL &&
|
(parent = get_event_parent (w)) != NULL &&
|
||||||
parent->window_type != GDK_WINDOW_ROOT)
|
parent->window_type != GDK_WINDOW_ROOT)
|
||||||
{
|
{
|
||||||
if (w->event_mask & GDK_BUTTON_PRESS_MASK)
|
if (w->event_mask & GDK_BUTTON_PRESS_MASK &&
|
||||||
|
(type == GDK_BUTTON_PRESS ||
|
||||||
|
_gdk_event_get_pointer_emulated (source_event)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (type == GDK_TOUCH_BEGIN &&
|
||||||
|
w->event_mask & GDK_TOUCH_MASK)
|
||||||
|
break;
|
||||||
|
|
||||||
w = parent;
|
w = parent;
|
||||||
}
|
}
|
||||||
pointer_window = (GdkWindow *)w;
|
pointer_window = w;
|
||||||
|
|
||||||
_gdk_display_add_device_grab (display,
|
if (pointer_window)
|
||||||
device,
|
{
|
||||||
pointer_window,
|
if (type == GDK_TOUCH_BEGIN &&
|
||||||
event_window,
|
pointer_window->event_mask & GDK_TOUCH_MASK)
|
||||||
GDK_OWNERSHIP_NONE,
|
{
|
||||||
FALSE,
|
_gdk_display_add_touch_grab (display, device, sequence,
|
||||||
gdk_window_get_events (pointer_window),
|
pointer_window, event_window,
|
||||||
serial,
|
gdk_window_get_events (pointer_window),
|
||||||
time_,
|
serial, time_);
|
||||||
TRUE);
|
}
|
||||||
_gdk_display_device_grab_update (display, device, source_device, serial);
|
else if (type == GDK_BUTTON_PRESS ||
|
||||||
|
_gdk_event_get_pointer_emulated (source_event))
|
||||||
|
{
|
||||||
|
_gdk_display_add_device_grab (display,
|
||||||
|
device,
|
||||||
|
pointer_window,
|
||||||
|
event_window,
|
||||||
|
GDK_OWNERSHIP_NONE,
|
||||||
|
FALSE,
|
||||||
|
gdk_window_get_events (pointer_window),
|
||||||
|
serial,
|
||||||
|
time_,
|
||||||
|
TRUE);
|
||||||
|
_gdk_display_device_grab_update (display, device,
|
||||||
|
source_device, serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pointer_window = get_pointer_window (display, toplevel_window, device,
|
pointer_window = get_pointer_window (display, toplevel_window, device,
|
||||||
@ -9471,9 +9541,10 @@ proxy_button_event (GdkEvent *source_event,
|
|||||||
|
|
||||||
event_win = get_event_window (display,
|
event_win = get_event_window (display,
|
||||||
device,
|
device,
|
||||||
pointer_window,
|
sequence,
|
||||||
type, state,
|
pointer_window,
|
||||||
NULL, serial);
|
type, state,
|
||||||
|
&evmask, serial);
|
||||||
|
|
||||||
if (event_win == NULL || display->ignore_core_events)
|
if (event_win == NULL || display->ignore_core_events)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@ -9523,7 +9594,6 @@ proxy_button_event (GdkEvent *source_event,
|
|||||||
event->button.device = source_event->button.device;
|
event->button.device = source_event->button.device;
|
||||||
event->button.axes = g_memdup (source_event->button.axes,
|
event->button.axes = g_memdup (source_event->button.axes,
|
||||||
sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device));
|
sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device));
|
||||||
|
|
||||||
gdk_event_set_source_device (event, source_device);
|
gdk_event_set_source_device (event, source_device);
|
||||||
|
|
||||||
if (type == GDK_BUTTON_PRESS)
|
if (type == GDK_BUTTON_PRESS)
|
||||||
@ -9547,6 +9617,40 @@ proxy_button_event (GdkEvent *source_event,
|
|||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
case GDK_TOUCH_BEGIN:
|
||||||
|
case GDK_TOUCH_END:
|
||||||
|
convert_toplevel_coords_to_window (event_win,
|
||||||
|
toplevel_x, toplevel_y,
|
||||||
|
&event->button.x, &event->button.y);
|
||||||
|
event->touch.x_root = source_event->touch.x_root;
|
||||||
|
event->touch.y_root = source_event->touch.y_root;
|
||||||
|
event->touch.state = state;
|
||||||
|
event->touch.device = source_event->touch.device;
|
||||||
|
event->touch.axes = g_memdup (source_event->touch.axes,
|
||||||
|
sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
|
||||||
|
event->touch.sequence = source_event->touch.sequence;
|
||||||
|
|
||||||
|
gdk_event_set_source_device (event, source_device);
|
||||||
|
|
||||||
|
if ((type == GDK_TOUCH_END &&
|
||||||
|
_gdk_event_get_pointer_emulated (source_event)) &&
|
||||||
|
pointer_window == pointer_info->window_under_pointer &&
|
||||||
|
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
|
||||||
|
{
|
||||||
|
/* Synthesize a leave notify event
|
||||||
|
* whenever a touch device is released
|
||||||
|
*/
|
||||||
|
pointer_info->need_touch_press_enter = TRUE;
|
||||||
|
_gdk_synthesize_crossing_events (display,
|
||||||
|
pointer_window, NULL,
|
||||||
|
device, source_device,
|
||||||
|
GDK_CROSSING_TOUCH_END,
|
||||||
|
toplevel_x, toplevel_y,
|
||||||
|
state, time_, NULL,
|
||||||
|
serial, FALSE);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
case GDK_SCROLL:
|
case GDK_SCROLL:
|
||||||
event->scroll.direction = source_event->scroll.direction;
|
event->scroll.direction = source_event->scroll.direction;
|
||||||
convert_toplevel_coords_to_window (event_win,
|
convert_toplevel_coords_to_window (event_win,
|
||||||
@ -9804,16 +9908,32 @@ _gdk_windowing_got_event (GdkDisplay *display,
|
|||||||
else if (is_button_type (event->type))
|
else if (is_button_type (event->type))
|
||||||
unlink_event = proxy_button_event (event, serial);
|
unlink_event = proxy_button_event (event, serial);
|
||||||
|
|
||||||
if (event->type == GDK_BUTTON_RELEASE && !event->any.send_event)
|
if ((event->type == GDK_BUTTON_RELEASE ||
|
||||||
|
event->type == GDK_TOUCH_END) &&
|
||||||
|
!event->any.send_event)
|
||||||
{
|
{
|
||||||
button_release_grab = _gdk_display_has_device_grab (display, device, serial);
|
GdkEventSequence *sequence;
|
||||||
if (button_release_grab &&
|
|
||||||
button_release_grab->implicit &&
|
sequence = gdk_event_get_event_sequence (event);
|
||||||
(event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
|
if (event->type == GDK_TOUCH_END && sequence)
|
||||||
{
|
{
|
||||||
button_release_grab->serial_end = serial;
|
_gdk_display_end_touch_grab (display, device, sequence);
|
||||||
button_release_grab->implicit_ungrab = FALSE;
|
}
|
||||||
_gdk_display_device_grab_update (display, device, source_device, serial);
|
|
||||||
|
if (event->type == GDK_BUTTON_RELEASE ||
|
||||||
|
_gdk_event_get_pointer_emulated (event))
|
||||||
|
{
|
||||||
|
button_release_grab =
|
||||||
|
_gdk_display_has_device_grab (display, device, serial);
|
||||||
|
|
||||||
|
if (button_release_grab &&
|
||||||
|
button_release_grab->implicit &&
|
||||||
|
(event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
|
||||||
|
{
|
||||||
|
button_release_grab->serial_end = serial;
|
||||||
|
button_release_grab->implicit_ungrab = FALSE;
|
||||||
|
_gdk_display_device_grab_update (display, device, source_device, serial);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1339,6 +1339,14 @@ rewrite_event_for_window (GdkEvent *event,
|
|||||||
new_window,
|
new_window,
|
||||||
&event->motion.x, &event->motion.y);
|
&event->motion.x, &event->motion.y);
|
||||||
break;
|
break;
|
||||||
|
case GDK_TOUCH_BEGIN:
|
||||||
|
case GDK_TOUCH_UPDATE:
|
||||||
|
case GDK_TOUCH_END:
|
||||||
|
case GDK_TOUCH_CANCEL:
|
||||||
|
rewrite_events_translate (event->any.window,
|
||||||
|
new_window,
|
||||||
|
&event->touch.x, &event->touch.y);
|
||||||
|
break;
|
||||||
case GDK_KEY_PRESS:
|
case GDK_KEY_PRESS:
|
||||||
case GDK_KEY_RELEASE:
|
case GDK_KEY_RELEASE:
|
||||||
case GDK_PROXIMITY_IN:
|
case GDK_PROXIMITY_IN:
|
||||||
@ -1384,6 +1392,10 @@ rewrite_event_for_grabs (GdkEvent *event)
|
|||||||
case GDK_PROXIMITY_OUT:
|
case GDK_PROXIMITY_OUT:
|
||||||
case GDK_KEY_PRESS:
|
case GDK_KEY_PRESS:
|
||||||
case GDK_KEY_RELEASE:
|
case GDK_KEY_RELEASE:
|
||||||
|
case GDK_TOUCH_BEGIN:
|
||||||
|
case GDK_TOUCH_UPDATE:
|
||||||
|
case GDK_TOUCH_END:
|
||||||
|
case GDK_TOUCH_CANCEL:
|
||||||
display = gdk_window_get_display (event->any.window);
|
display = gdk_window_get_display (event->any.window);
|
||||||
device = gdk_event_get_device (event);
|
device = gdk_event_get_device (event);
|
||||||
|
|
||||||
@ -1643,6 +1655,7 @@ gtk_main_do_event (GdkEvent *event)
|
|||||||
case GDK_BUTTON_PRESS:
|
case GDK_BUTTON_PRESS:
|
||||||
case GDK_2BUTTON_PRESS:
|
case GDK_2BUTTON_PRESS:
|
||||||
case GDK_3BUTTON_PRESS:
|
case GDK_3BUTTON_PRESS:
|
||||||
|
case GDK_TOUCH_BEGIN:
|
||||||
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
|
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
|
||||||
gtk_propagate_event (grab_widget, event);
|
gtk_propagate_event (grab_widget, event);
|
||||||
break;
|
break;
|
||||||
@ -1693,6 +1706,9 @@ gtk_main_do_event (GdkEvent *event)
|
|||||||
case GDK_BUTTON_RELEASE:
|
case GDK_BUTTON_RELEASE:
|
||||||
case GDK_PROXIMITY_IN:
|
case GDK_PROXIMITY_IN:
|
||||||
case GDK_PROXIMITY_OUT:
|
case GDK_PROXIMITY_OUT:
|
||||||
|
case GDK_TOUCH_UPDATE:
|
||||||
|
case GDK_TOUCH_END:
|
||||||
|
case GDK_TOUCH_CANCEL:
|
||||||
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
|
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
|
||||||
gtk_propagate_event (grab_widget, event);
|
gtk_propagate_event (grab_widget, event);
|
||||||
break;
|
break;
|
||||||
@ -1743,6 +1759,7 @@ gtk_main_do_event (GdkEvent *event)
|
|||||||
|| event->type == GDK_DRAG_ENTER
|
|| event->type == GDK_DRAG_ENTER
|
||||||
|| event->type == GDK_GRAB_BROKEN
|
|| event->type == GDK_GRAB_BROKEN
|
||||||
|| event->type == GDK_MOTION_NOTIFY
|
|| event->type == GDK_MOTION_NOTIFY
|
||||||
|
|| event->type == GDK_TOUCH_UPDATE
|
||||||
|| event->type == GDK_SCROLL)
|
|| event->type == GDK_SCROLL)
|
||||||
{
|
{
|
||||||
_gtk_tooltip_handle_event (event);
|
_gtk_tooltip_handle_event (event);
|
||||||
|
Loading…
Reference in New Issue
Block a user