diff --git a/Xi/exevents.c b/Xi/exevents.c index 16acd5814..f878dd212 100644 --- a/Xi/exevents.c +++ b/Xi/exevents.c @@ -1743,6 +1743,67 @@ ProcessBarrierEvent(InternalEvent *e, DeviceIntPtr dev) free(ev); } +static BOOL +IsAnotherGestureActiveOnMaster(DeviceIntPtr dev, InternalEvent* ev) +{ + GestureClassPtr g = dev->gesture; + if (g->gesture.active && g->gesture.sourceid != ev->gesture_event.sourceid) { + return TRUE; + } + return FALSE; +} + +/** + * Processes and delivers a Gesture{Pinch,Swipe}{Begin,Update,End}. + * + * Due to having rather different delivery semantics (see the Xi 2.4 protocol + * spec for more information), this implements its own grab and event-selection + * delivery logic. + */ +void +ProcessGestureEvent(InternalEvent *ev, DeviceIntPtr dev) +{ + GestureInfoPtr gi; + DeviceIntPtr kbd; + Bool deactivateGestureGrab = FALSE; + + if (!dev->gesture) + return; + + if (IsMaster(dev) && IsAnotherGestureActiveOnMaster(dev, ev)) + return; + + if (IsGestureBeginEvent(ev)) + gi = GestureBeginGesture(dev, ev); + else + gi = GestureFindActiveByEventType(dev, ev->any.type); + + if (!gi) { + /* This may happen if gesture is no longer active or was never started. */ + return; + } + + kbd = GetMaster(dev, KEYBOARD_OR_FLOAT); + event_set_state_gesture(kbd, &ev->gesture_event); + + if (IsGestureBeginEvent(ev)) + GestureSetupListener(dev, gi, ev); + + if (IsGestureEndEvent(ev) && + dev->deviceGrab.grab && + dev->deviceGrab.fromPassiveGrab && + GrabIsGestureGrab(dev->deviceGrab.grab)) + deactivateGestureGrab = TRUE; + + DeliverGestureEventToOwner(dev, gi, ev); + + if (IsGestureEndEvent(ev)) + GestureEndGesture(gi); + + if (deactivateGestureGrab) + (*dev->deviceGrab.DeactivateGrab) (dev); +} + /** * Process DeviceEvents and DeviceChangedEvents. */ @@ -1937,6 +1998,14 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device) case ET_BarrierLeave: ProcessBarrierEvent(ev, device); break; + case ET_GesturePinchBegin: + case ET_GesturePinchUpdate: + case ET_GesturePinchEnd: + case ET_GestureSwipeBegin: + case ET_GestureSwipeUpdate: + case ET_GestureSwipeEnd: + ProcessGestureEvent(ev, device); + break; default: ProcessDeviceEvent(ev, device); break; @@ -2141,6 +2210,111 @@ DeliverTouchEvents(DeviceIntPtr dev, TouchPointInfoPtr ti, } } +/** + * Attempts to deliver a gesture event to the given client. + */ +static Bool +DeliverOneGestureEvent(ClientPtr client, DeviceIntPtr dev, GestureInfoPtr gi, + GrabPtr grab, WindowPtr win, InternalEvent *ev) +{ + int err; + xEvent *xi2; + Mask filter; + Window child = DeepestSpriteWin(&gi->sprite)->drawable.id; + + /* If we fail here, we're going to leave a client hanging. */ + err = EventToXI2(ev, &xi2); + if (err != Success) + FatalError("[Xi] %s: XI2 conversion failed in %s" + " (%d)\n", dev->name, __func__, err); + + FixUpEventFromWindow(&gi->sprite, xi2, win, child, FALSE); + filter = GetEventFilter(dev, xi2); + if (XaceHook(XACE_RECEIVE_ACCESS, client, win, xi2, 1) != Success) + return FALSE; + err = TryClientEvents(client, dev, xi2, 1, filter, filter, NullGrab); + free(xi2); + + /* Returning the value from TryClientEvents isn't useful, since all our + * resource-gone cleanups will update the delivery list anyway. */ + return TRUE; +} + +/** + * Given a gesture event and a potential listener, retrieve info needed for processing the event. + * + * @param dev The device generating the gesture event. + * @param ev The gesture event to process. + * @param listener The gesture event listener that may receive the gesture event. + * @param[out] client The client that should receive the gesture event. + * @param[out] win The window to deliver the event on. + * @param[out] grab The grab to deliver the event through, if any. + * @return TRUE if an event should be delivered to the listener, FALSE + * otherwise. + */ +static Bool +RetrieveGestureDeliveryData(DeviceIntPtr dev, InternalEvent *ev, GestureListener* listener, + ClientPtr *client, WindowPtr *win, GrabPtr *grab) +{ + int rc; + int evtype; + InputClients *iclients = NULL; + *grab = NULL; + + if (listener->type == GESTURE_LISTENER_GRAB || + listener->type == GESTURE_LISTENER_NONGESTURE_GRAB) { + *grab = listener->grab; + + BUG_RETURN_VAL(!*grab, FALSE); + + *client = rClient(*grab); + *win = (*grab)->window; + } + else { + rc = dixLookupResourceByType((void **) win, listener->listener, listener->resource_type, + serverClient, DixSendAccess); + if (rc != Success) + return FALSE; + + /* note that we only will have XI2 listeners as + listener->type == GESTURE_LISTENER_REGULAR */ + evtype = GetXI2Type(ev->any.type); + + nt_list_for_each_entry(iclients, wOtherInputMasks(*win)->inputClients, next) + if (xi2mask_isset(iclients->xi2mask, dev, evtype)) + break; + + BUG_RETURN_VAL(!iclients, FALSE); + + *client = rClient(iclients); + } + + return TRUE; +} + +/** + * Delivers a gesture to the owner, if possible and needed. Returns whether + * an event was delivered. + */ +Bool +DeliverGestureEventToOwner(DeviceIntPtr dev, GestureInfoPtr gi, InternalEvent *ev) +{ + GrabPtr grab = NULL; + ClientPtr client; + WindowPtr win; + + if (!gi->has_listener || gi->listener.type == GESTURE_LISTENER_NONGESTURE_GRAB) { + return 0; + } + + if (!RetrieveGestureDeliveryData(dev, ev, &gi->listener, &client, &win, &grab)) + return 0; + + ev->gesture_event.deviceid = dev->id; + + return DeliverOneGestureEvent(client, dev, gi, grab, win, ev); +} + int InitProximityClassDeviceStruct(DeviceIntPtr dev) { diff --git a/dix/devices.c b/dix/devices.c index 29f3051aa..5bf956ead 100644 --- a/dix/devices.c +++ b/dix/devices.c @@ -458,6 +458,7 @@ DisableDevice(DeviceIntPtr dev, BOOL sendevent) return FALSE; TouchEndPhysicallyActiveTouches(dev); + GestureEndActiveGestures(dev); ReleaseButtonsAndKeys(dev); SyncRemoveDeviceIdleTime(dev->idle_counter); dev->idle_counter = NULL; diff --git a/dix/dispatch.c b/dix/dispatch.c index ba01de6cf..083553610 100644 --- a/dix/dispatch.c +++ b/dix/dispatch.c @@ -3487,6 +3487,7 @@ CloseDownClient(ClientPtr client) CallCallbacks((&ClientStateCallback), (void *) &clientinfo); } TouchListenerGone(client->clientAsMask); + GestureListenerGone(client->clientAsMask); FreeClientResources(client); /* Disable client ID tracking. This must be done after * ClientStateCallback. */ diff --git a/dix/events.c b/dix/events.c index e2455a4bb..d29868ef0 100644 --- a/dix/events.c +++ b/dix/events.c @@ -1328,6 +1328,15 @@ ComputeFreezes(void) TouchListenerAcceptReject(replayDev, ti, 0, XIRejectTouch); } + else if (IsGestureEvent(event)) { + GestureInfoPtr gi = + GestureFindActiveByEventType(replayDev, event->any.type); + if (gi) { + GestureEmitGestureEndToOwner(replayDev, gi); + GestureEndGesture(gi); + } + ProcessGestureEvent(event, replayDev); + } else { WindowPtr w = XYToWindow(replayDev->spriteInfo->sprite, event->device_event.root_x, @@ -1509,6 +1518,46 @@ UpdateTouchesForGrab(DeviceIntPtr mouse) } } +/** + * Update gesture records when an explicit grab is activated. Any gestures owned + * by the grabbing client are updated so the listener state reflects the new + * grab. + */ +static void +UpdateGesturesForGrab(DeviceIntPtr mouse) +{ + if (!mouse->gesture || mouse->deviceGrab.fromPassiveGrab) + return; + + GestureInfoPtr gi = &mouse->gesture->gesture; + GestureListener *listener = &gi->listener; + GrabPtr grab = mouse->deviceGrab.grab; + + if (gi->active && CLIENT_BITS(listener->listener) == grab->resource) { + if (grab->grabtype == CORE || grab->grabtype == XI || + !xi2mask_isset(grab->xi2mask, mouse, GetXI2Type(gi->type))) { + + if (listener->type == GESTURE_LISTENER_REGULAR) { + /* if the listener already got any events relating to the gesture, we must send + a gesture end because the grab overrides the previous listener and won't + itself send any gesture events. + */ + GestureEmitGestureEndToOwner(mouse, gi); + } + listener->type = GESTURE_LISTENER_NONGESTURE_GRAB; + } else { + listener->type = GESTURE_LISTENER_GRAB; + } + + listener->listener = grab->resource; + listener->window = grab->window; + + if (listener->grab) + FreeGrab(listener->grab); + listener->grab = AllocGrab(grab); + } +} + /** * Activate a pointer grab on the given device. A pointer grab will cause all * core pointer events of this device to be delivered to the grabbing client only. @@ -1559,6 +1608,7 @@ ActivatePointerGrab(DeviceIntPtr mouse, GrabPtr grab, grabinfo->implicitGrab = autoGrab & ImplicitGrabMask; PostNewCursor(mouse); UpdateTouchesForGrab(mouse); + UpdateGesturesForGrab(mouse); CheckGrabForSyncs(mouse, (Bool) grab->pointerMode, (Bool) grab->keyboardMode); if (oldgrab) @@ -1614,6 +1664,16 @@ DeactivatePointerGrab(DeviceIntPtr mouse) if (dev->deviceGrab.sync.other == grab) dev->deviceGrab.sync.other = NullGrab; } + + /* in case of explicit gesture grab, send end event to the grab client */ + if (!wasPassive && mouse->gesture) { + GestureInfoPtr gi = &mouse->gesture->gesture; + if (gi->active && GestureResourceIsOwner(gi, grab_resource)) { + GestureEmitGestureEndToOwner(mouse, gi); + GestureEndGesture(gi); + } + } + DoEnterLeaveEvents(mouse, mouse->id, grab->window, mouse->spriteInfo->sprite->win, NotifyUngrab); if (grab->confineTo) diff --git a/dix/gestures.c b/dix/gestures.c index 7e4057deb..593a4a67f 100644 --- a/dix/gestures.c +++ b/dix/gestures.c @@ -39,6 +39,8 @@ #include "windowstr.h" #include "mi.h" +#define GESTURE_HISTORY_SIZE 100 + Bool GestureInitGestureInfo(GestureInfoPtr gi) { @@ -55,3 +57,306 @@ GestureInitGestureInfo(GestureInfoPtr gi) return TRUE; } + +/** + * Given an event type returns the associated gesture event info. + */ +GestureInfoPtr +GestureFindActiveByEventType(DeviceIntPtr dev, int type) +{ + GestureClassPtr g = dev->gesture; + enum EventType type_to_expect = GestureTypeToBegin(type); + + if (!g || type_to_expect == 0 || !g->gesture.active || + g->gesture.type != type_to_expect) { + return NULL; + } + + return &g->gesture; +} + +/** + * Sets up gesture info for a new gesture. Returns NULL on failure. + */ +GestureInfoPtr +GestureBeginGesture(DeviceIntPtr dev, InternalEvent *ev) +{ + GestureClassPtr g = dev->gesture; + enum EventType gesture_type = GestureTypeToBegin(ev->any.type); + + /* Note that we ignore begin events when an existing gesture is active */ + if (!g || gesture_type == 0 || g->gesture.active) + return NULL; + + g->gesture.type = gesture_type; + + if (!GestureBuildSprite(dev, &g->gesture)) + return NULL; + + g->gesture.active = TRUE; + g->gesture.num_touches = ev->gesture_event.num_touches; + g->gesture.sourceid = ev->gesture_event.sourceid; + g->gesture.has_listener = FALSE; + return &g->gesture; +} + +/** + * Releases a gesture: this must only be called after all events + * related to that gesture have been sent and finalised. + */ +void +GestureEndGesture(GestureInfoPtr gi) +{ + if (gi->has_listener) { + if (gi->listener.grab) { + FreeGrab(gi->listener.grab); + gi->listener.grab = NULL; + } + gi->listener.listener = 0; + gi->has_listener = FALSE; + } + + gi->active = FALSE; + gi->num_touches = 0; + gi->sprite.spriteTraceGood = 0; +} + +/** + * Ensure a window trace is present in gi->sprite, constructing one for + * Gesture{Pinch,Swipe}Begin events. + */ +Bool +GestureBuildSprite(DeviceIntPtr sourcedev, GestureInfoPtr gi) +{ + SpritePtr sprite = &gi->sprite; + + if (!sourcedev->spriteInfo->sprite) + return FALSE; + + if (!CopySprite(sourcedev->spriteInfo->sprite, sprite)) + return FALSE; + + if (sprite->spriteTraceGood <= 0) + return FALSE; + + return TRUE; +} + +/** + * @returns TRUE if the specified grab or selection is the current owner of + * the gesture sequence. + */ +Bool +GestureResourceIsOwner(GestureInfoPtr gi, XID resource) +{ + return (gi->listener.listener == resource); +} + +void +GestureAddListener(GestureInfoPtr gi, XID resource, int resource_type, + enum GestureListenerType type, WindowPtr window, const GrabPtr grab) +{ + GrabPtr g = NULL; + + BUG_RETURN(gi->has_listener); + + /* We need a copy of the grab, not the grab itself since that may be deleted by + * a UngrabButton request and leaves us with a dangling pointer */ + if (grab) + g = AllocGrab(grab); + + gi->listener.listener = resource; + gi->listener.resource_type = resource_type; + gi->listener.type = type; + gi->listener.window = window; + gi->listener.grab = g; + gi->has_listener = TRUE; +} + +static void +GestureAddGrabListener(DeviceIntPtr dev, GestureInfoPtr gi, GrabPtr grab) +{ + enum GestureListenerType type; + + /* FIXME: owner_events */ + + if (grab->grabtype == XI2) { + if (xi2mask_isset(grab->xi2mask, dev, XI_GesturePinchBegin) || + xi2mask_isset(grab->xi2mask, dev, XI_GestureSwipeBegin)) { + type = GESTURE_LISTENER_GRAB; + } else + type = GESTURE_LISTENER_NONGESTURE_GRAB; + } + else if (grab->grabtype == XI || grab->grabtype == CORE) { + type = GESTURE_LISTENER_NONGESTURE_GRAB; + } + else { + BUG_RETURN_MSG(1, "Unsupported grab type\n"); + } + + /* grab listeners are always RT_NONE since we keep the grab pointer */ + GestureAddListener(gi, grab->resource, RT_NONE, type, grab->window, grab); +} + +/** + * Add one listener if there is a grab on the given window. + */ +static void +GestureAddPassiveGrabListener(DeviceIntPtr dev, GestureInfoPtr gi, WindowPtr win, InternalEvent *ev) +{ + Bool activate = FALSE; + Bool check_core = FALSE; + + GrabPtr grab = CheckPassiveGrabsOnWindow(win, dev, ev, check_core, + activate); + if (!grab) + return; + + /* We'll deliver later in gesture-specific code */ + ActivateGrabNoDelivery(dev, grab, ev, ev); + GestureAddGrabListener(dev, gi, grab); +} + +static void +GestureAddRegularListener(DeviceIntPtr dev, GestureInfoPtr gi, WindowPtr win, InternalEvent *ev) +{ + InputClients *iclients = NULL; + OtherInputMasks *inputMasks = NULL; + uint16_t evtype = GetXI2Type(ev->any.type); + int mask; + + mask = EventIsDeliverable(dev, ev->any.type, win); + if (!mask) + return; + + inputMasks = wOtherInputMasks(win); + + if (mask & EVENT_XI2_MASK) { + nt_list_for_each_entry(iclients, inputMasks->inputClients, next) { + if (!xi2mask_isset(iclients->xi2mask, dev, evtype)) + continue; + + GestureAddListener(gi, iclients->resource, RT_INPUTCLIENT, + GESTURE_LISTENER_REGULAR, win, NULL); + return; + } + } +} + +void +GestureSetupListener(DeviceIntPtr dev, GestureInfoPtr gi, InternalEvent *ev) +{ + int i; + SpritePtr sprite = &gi->sprite; + WindowPtr win; + + /* Any current grab will consume all gesture events */ + if (dev->deviceGrab.grab) { + GestureAddGrabListener(dev, gi, dev->deviceGrab.grab); + return; + } + + /* Find passive grab that would be activated by this event, if any. If we're handling + * ReplayDevice then the search starts from the descendant of the grab window, otherwise + * the search starts at the root window. The search ends at deepest child window. */ + i = 0; + if (syncEvents.playingEvents) { + while (i < dev->spriteInfo->sprite->spriteTraceGood) { + if (dev->spriteInfo->sprite->spriteTrace[i++] == syncEvents.replayWin) + break; + } + } + + for (; i < sprite->spriteTraceGood; i++) { + win = sprite->spriteTrace[i]; + GestureAddPassiveGrabListener(dev, gi, win, ev); + if (gi->has_listener) + return; + } + + /* Find the first client with an applicable event selection, + * going from deepest child window back up to the root window. */ + for (i = sprite->spriteTraceGood - 1; i >= 0; i--) { + win = sprite->spriteTrace[i]; + GestureAddRegularListener(dev, gi, win, ev); + if (gi->has_listener) + return; + } +} + +/* As gesture grabs don't turn into active grabs with their own resources, we + * need to walk all the gestures and remove this grab from listener */ +void +GestureListenerGone(XID resource) +{ + GestureInfoPtr gi; + DeviceIntPtr dev; + InternalEvent *events = InitEventList(GetMaximumEventsNum()); + + if (!events) + FatalError("GestureListenerGone: couldn't allocate events\n"); + + for (dev = inputInfo.devices; dev; dev = dev->next) { + if (!dev->gesture) + continue; + + gi = &dev->gesture->gesture; + if (!gi->active) + continue; + + if (CLIENT_BITS(gi->listener.listener) == resource) + GestureEndGesture(gi); + } + + FreeEventList(events, GetMaximumEventsNum()); +} + +/** + * End physically active gestures for a device. + */ +void +GestureEndActiveGestures(DeviceIntPtr dev) +{ + GestureClassPtr g = dev->gesture; + InternalEvent *eventlist; + + if (!g) + return; + + eventlist = InitEventList(GetMaximumEventsNum()); + + input_lock(); + mieqProcessInputEvents(); + if (g->gesture.active) { + int j; + int type = GetXI2Type(GestureTypeToEnd(g->gesture.type)); + int nevents = GetGestureEvents(eventlist, dev, type, g->gesture.num_touches, + 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + + for (j = 0; j < nevents; j++) + mieqProcessDeviceEvent(dev, eventlist + j, NULL); + } + input_unlock(); + + FreeEventList(eventlist, GetMaximumEventsNum()); +} + +/** + * Generate and deliver a Gesture{Pinch,Swipe}End event to the owner. + * + * @param dev The device to deliver the event for. + * @param gi The gesture record to deliver the event for. + */ +void +GestureEmitGestureEndToOwner(DeviceIntPtr dev, GestureInfoPtr gi) +{ + InternalEvent event; + /* We're not processing a gesture end for a frozen device */ + if (dev->deviceGrab.sync.frozen) + return; + + DeliverDeviceClassesChangedEvent(gi->sourceid, GetTimeInMillis()); + InitGestureEvent(&event, dev, GetTimeInMillis(), GestureTypeToEnd(gi->type), + 0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + DeliverGestureEventToOwner(dev, gi, &event); +} diff --git a/dix/inpututils.c b/dix/inpututils.c index 6906f997b..9026f651b 100644 --- a/dix/inpututils.c +++ b/dix/inpututils.c @@ -809,6 +809,24 @@ event_set_state(DeviceIntPtr mouse, DeviceIntPtr kbd, DeviceEvent *event) } } +void +event_set_state_gesture(DeviceIntPtr kbd, GestureEvent *event) +{ + if (kbd && kbd->key) { + XkbStatePtr state= &kbd->key->xkbInfo->state; + + event->mods.base = state->base_mods; + event->mods.latched = state->latched_mods; + event->mods.locked = state->locked_mods; + event->mods.effective = state->mods; + + event->group.base = state->base_group; + event->group.latched = state->latched_group; + event->group.locked = state->locked_group; + event->group.effective = state->group; + } +} + /** * Return the event filter mask for the given device and the given core or * XI1 protocol type. diff --git a/include/dix.h b/include/dix.h index 07d3607f2..432bdcc35 100644 --- a/include/dix.h +++ b/include/dix.h @@ -421,6 +421,10 @@ DeliverTouchEvents(DeviceIntPtr /* dev */ , InternalEvent * /* ev */ , XID /* resource */ ); +extern Bool +DeliverGestureEventToOwner(DeviceIntPtr dev, GestureInfoPtr gi, + InternalEvent *ev); + extern void InitializeSprite(DeviceIntPtr /* pDev */ , WindowPtr /* pWin */ ); diff --git a/include/input.h b/include/input.h index c19e74969..b1aef3663 100644 --- a/include/input.h +++ b/include/input.h @@ -660,6 +660,21 @@ extern void TouchEmitTouchEnd(DeviceIntPtr dev, TouchPointInfoPtr ti, int flags, extern void TouchAcceptAndEnd(DeviceIntPtr dev, int touchid); extern Bool GestureInitGestureInfo(GestureInfoPtr gesture); +extern GestureInfoPtr GestureBeginGesture(DeviceIntPtr dev, InternalEvent *ev); +extern GestureInfoPtr GestureFindActiveByEventType(DeviceIntPtr dev, int type); +extern void GestureEndGesture(GestureInfoPtr gi); +extern Bool GestureResourceIsOwner(GestureInfoPtr gi, XID resource); +extern void GestureAddListener(GestureInfoPtr gi, XID resource, int resource_type, + enum GestureListenerType type, + WindowPtr window, GrabPtr grab); +extern void GestureSetupListener(DeviceIntPtr dev, GestureInfoPtr gi, + InternalEvent *ev); +extern Bool GestureBuildSprite(DeviceIntPtr sourcedev, GestureInfoPtr gi); +extern void GestureListenerGone(XID resource); +extern void GestureEndActiveGestures(DeviceIntPtr dev); +extern void GestureEmitGestureEndToOwner(DeviceIntPtr dev, GestureInfoPtr gi); +extern void ProcessGestureEvent(InternalEvent *ev, DeviceIntPtr dev); + /* misc event helpers */ extern Mask GetEventMask(DeviceIntPtr dev, xEvent *ev, InputClientsPtr clients); extern Mask GetEventFilter(DeviceIntPtr dev, xEvent *event); diff --git a/include/inpututils.h b/include/inpututils.h index 489e1d9b7..49b1c1074 100644 --- a/include/inpututils.h +++ b/include/inpututils.h @@ -50,6 +50,7 @@ extern void init_gesture_event(GestureEvent *event, DeviceIntPtr dev, Time ms); extern int event_get_corestate(DeviceIntPtr mouse, DeviceIntPtr kbd); extern void event_set_state(DeviceIntPtr mouse, DeviceIntPtr kbd, DeviceEvent *event); +extern void event_set_state_gesture(DeviceIntPtr kbd, GestureEvent *event); extern Mask event_get_filter_from_type(DeviceIntPtr dev, int evtype); extern Mask event_get_filter_from_xi2type(int evtype); diff --git a/mi/mieq.c b/mi/mieq.c index bcd62d572..c98d46862 100644 --- a/mi/mieq.c +++ b/mi/mieq.c @@ -342,6 +342,14 @@ ChangeDeviceID(DeviceIntPtr dev, InternalEvent *event) case ET_BarrierLeave: event->barrier_event.deviceid = dev->id; break; + case ET_GesturePinchBegin: + case ET_GesturePinchUpdate: + case ET_GesturePinchEnd: + case ET_GestureSwipeBegin: + case ET_GestureSwipeUpdate: + case ET_GestureSwipeEnd: + event->gesture_event.deviceid = dev->id; + break; default: ErrorF("[mi] Unknown event type (%d), cannot change id.\n", event->any.type);