// compressMotionNotify takes a MotionNotify event, and inspects the event // queue for any future MotionNotify events that can be received without // blocking. The most recent MotionNotify event is then returned. // Note that we need to make sure that the Event, Child, Detail, State, Root // and SameScreen fields are the same to ensure the same window/action is // generating events. That is, we are only compressing the RootX, RootY, // EventX and EventY fields. // This function is not thread safe, since Peek returns a *copy* of the // event queue---which could be out of date by the time we dequeue events. func compressMotionNotify(X *xgbutil.XUtil, ev xevent.MotionNotifyEvent) xevent.MotionNotifyEvent { // We force a round trip request so that we make sure to read all // available events. X.Sync() xevent.Read(X, false) // The most recent MotionNotify event that we'll end up returning. laste := ev // Look through each event in the queue. If it's an event and it matches // all the fields in 'ev' that are detailed above, then set it to 'laste'. // In which case, we'll also dequeue the event, otherwise it will be // processed twice! // N.B. If our only goal was to find the most recent relevant MotionNotify // event, we could traverse the event queue backwards and simply use // the first MotionNotify we see. However, this could potentially leave // other MotionNotify events in the queue, which we *don't* want to be // processed. So we stride along and just pick off MotionNotify events // until we don't see any more. for i, ee := range xevent.Peek(X) { if ee.Err != nil { // This is an error, skip it. continue } // Use type assertion to make sure this is a MotionNotify event. if mn, ok := ee.Event.(xproto.MotionNotifyEvent); ok { // Now make sure all appropriate fields are equivalent. if ev.Event == mn.Event && ev.Child == mn.Child && ev.Detail == mn.Detail && ev.State == mn.State && ev.Root == mn.Root && ev.SameScreen == mn.SameScreen { // Set the most recent/valid motion notify event. laste = xevent.MotionNotifyEvent{&mn} // We cheat and use the stack semantics of defer to dequeue // most recent motion notify events first, so that the indices // don't become invalid. (If we dequeued oldest first, we'd // have to account for all future events shifting to the left // by one.) defer func(i int) { xevent.DequeueAt(X, i) }(i) } } } // This isn't strictly necessary, but is correct. We should update // xgbutil's sense of time with the most recent event processed. // This is typically done in the main event loop, but since we are // subverting the main event loop, we should take care of it. X.TimeSet(laste.Time) return laste }
// dragStep executes the "step" function registered for the current drag. // It also compresses the MotionNotify events. func dragStep(xu *xgbutil.XUtil, ev xevent.MotionNotifyEvent) { // If for whatever reason we don't have any *piece* of a grab, // we've gotta back out. if !mouseDrag(xu) || mouseDragStep(xu) == nil || mouseDragEnd(xu) == nil { dragUngrab(xu) mouseDragStepSet(xu, nil) mouseDragEndSet(xu, nil) return } // The most recent MotionNotify event that we'll end up returning. laste := ev // We force a round trip request so that we make sure to read all // available events. xu.Sync() xevent.Read(xu, false) // Compress MotionNotify events. for i, ee := range xevent.Peek(xu) { if ee.Err != nil { // This is an error, skip it. continue } // Use type assertion to make sure this is a MotionNotify event. if mn, ok := ee.Event.(xproto.MotionNotifyEvent); ok { // Now make sure all appropriate fields are equivalent. if ev.Event == mn.Event && ev.Child == mn.Child && ev.Detail == mn.Detail && ev.State == mn.State && ev.Root == mn.Root && ev.SameScreen == mn.SameScreen { // Set the most recent/valid motion notify event. laste = xevent.MotionNotifyEvent{&mn} // We cheat and use the stack semantics of defer to dequeue // most recent motion notify events first, so that the indices // don't become invalid. (If we dequeued oldest first, we'd // have to account for all future events shifting to the left // by one.) defer func(i int) { xevent.DequeueAt(xu, i) }(i) } } } xu.TimeSet(laste.Time) // now actually run the step mouseDragStep(xu)(xu, int(laste.RootX), int(laste.RootY), int(laste.EventX), int(laste.EventY)) }
// processEventQueue processes every item in the event/error queue. func processEventQueue(xu *xgbutil.XUtil, pingBefore, pingAfter chan struct{}) { for !Empty(xu) { if Quitting(xu) { return } // We send the ping *before* the next event is dequeued. // This is so the queue doesn't present a misrepresentation of which // events haven't been processed yet. if pingBefore != nil && pingAfter != nil { pingBefore <- struct{}{} } ev, err := Dequeue(xu) // If we gobbled up an error, send it to the error event handler // and move on the next event/error. if err != nil { ErrorHandlerGet(xu)(err) if pingBefore != nil && pingAfter != nil { pingAfter <- struct{}{} } continue } // We know there isn't an error. If there isn't an event either, // then there's a bug somewhere. if ev == nil { xgbutil.Logger.Fatal("BUG: Expected an event but got nil.") } switch event := ev.(type) { case xproto.KeyPressEvent: e := KeyPressEvent{&event} // If we're redirecting key events, this is the place to do it! if wid := RedirectKeyGet(xu); wid > 0 { e.Event = wid } xu.TimeSet(e.Time) runCallbacks(xu, e, KeyPress, e.Event) case xproto.KeyReleaseEvent: e := KeyReleaseEvent{&event} // If we're redirecting key events, this is the place to do it! if wid := RedirectKeyGet(xu); wid > 0 { e.Event = wid } xu.TimeSet(e.Time) runCallbacks(xu, e, KeyRelease, e.Event) case xproto.ButtonPressEvent: e := ButtonPressEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, ButtonPress, e.Event) case xproto.ButtonReleaseEvent: e := ButtonReleaseEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, ButtonRelease, e.Event) case xproto.MotionNotifyEvent: e := MotionNotifyEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, MotionNotify, e.Event) case xproto.EnterNotifyEvent: e := EnterNotifyEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, EnterNotify, e.Event) case xproto.LeaveNotifyEvent: e := LeaveNotifyEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, LeaveNotify, e.Event) case xproto.FocusInEvent: e := FocusInEvent{&event} runCallbacks(xu, e, FocusIn, e.Event) case xproto.FocusOutEvent: e := FocusOutEvent{&event} runCallbacks(xu, e, FocusOut, e.Event) case xproto.KeymapNotifyEvent: e := KeymapNotifyEvent{&event} runCallbacks(xu, e, KeymapNotify, NoWindow) case xproto.ExposeEvent: e := ExposeEvent{&event} runCallbacks(xu, e, Expose, e.Window) case xproto.GraphicsExposureEvent: e := GraphicsExposureEvent{&event} runCallbacks(xu, e, GraphicsExposure, xproto.Window(e.Drawable)) case xproto.NoExposureEvent: e := NoExposureEvent{&event} runCallbacks(xu, e, NoExposure, xproto.Window(e.Drawable)) case xproto.VisibilityNotifyEvent: e := VisibilityNotifyEvent{&event} runCallbacks(xu, e, VisibilityNotify, e.Window) case xproto.CreateNotifyEvent: e := CreateNotifyEvent{&event} runCallbacks(xu, e, CreateNotify, e.Parent) case xproto.DestroyNotifyEvent: e := DestroyNotifyEvent{&event} runCallbacks(xu, e, DestroyNotify, e.Window) case xproto.UnmapNotifyEvent: e := UnmapNotifyEvent{&event} runCallbacks(xu, e, UnmapNotify, e.Window) case xproto.MapNotifyEvent: e := MapNotifyEvent{&event} runCallbacks(xu, e, MapNotify, e.Event) case xproto.MapRequestEvent: e := MapRequestEvent{&event} runCallbacks(xu, e, MapRequest, e.Window) runCallbacks(xu, e, MapRequest, e.Parent) case xproto.ReparentNotifyEvent: e := ReparentNotifyEvent{&event} runCallbacks(xu, e, ReparentNotify, e.Window) case xproto.ConfigureNotifyEvent: e := ConfigureNotifyEvent{&event} runCallbacks(xu, e, ConfigureNotify, e.Window) case xproto.ConfigureRequestEvent: e := ConfigureRequestEvent{&event} runCallbacks(xu, e, ConfigureRequest, e.Window) runCallbacks(xu, e, ConfigureRequest, e.Parent) case xproto.GravityNotifyEvent: e := GravityNotifyEvent{&event} runCallbacks(xu, e, GravityNotify, e.Window) case xproto.ResizeRequestEvent: e := ResizeRequestEvent{&event} runCallbacks(xu, e, ResizeRequest, e.Window) case xproto.CirculateNotifyEvent: e := CirculateNotifyEvent{&event} runCallbacks(xu, e, CirculateNotify, e.Window) case xproto.CirculateRequestEvent: e := CirculateRequestEvent{&event} runCallbacks(xu, e, CirculateRequest, e.Window) case xproto.PropertyNotifyEvent: e := PropertyNotifyEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, PropertyNotify, e.Window) case xproto.SelectionClearEvent: e := SelectionClearEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, SelectionClear, e.Owner) case xproto.SelectionRequestEvent: e := SelectionRequestEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, SelectionRequest, e.Requestor) case xproto.SelectionNotifyEvent: e := SelectionNotifyEvent{&event} xu.TimeSet(e.Time) runCallbacks(xu, e, SelectionNotify, e.Requestor) case xproto.ColormapNotifyEvent: e := ColormapNotifyEvent{&event} runCallbacks(xu, e, ColormapNotify, e.Window) case xproto.ClientMessageEvent: e := ClientMessageEvent{&event} runCallbacks(xu, e, ClientMessage, e.Window) case xproto.MappingNotifyEvent: e := MappingNotifyEvent{&event} runCallbacks(xu, e, MappingNotify, NoWindow) case shape.NotifyEvent: e := ShapeNotifyEvent{&event} runCallbacks(xu, e, ShapeNotify, e.AffectedWindow) default: if event != nil { xgbutil.Logger.Printf("ERROR: UNSUPPORTED EVENT TYPE: %T", event) } } if pingBefore != nil && pingAfter != nil { pingAfter <- struct{}{} } } }