Exemplo n.º 1
0
// 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
}
Exemplo n.º 2
0
// currentTime forcefully causes a PropertyNotify event to fire on the root
// window, then scans the event queue and picks up the time.
//
// It is NOT SAFE to call this function in a place other than Wingo's
// initialization. Namely, this function subverts xevent's queue and reads
// events directly from X.
func currentTime(X *xgbutil.XUtil) (xproto.Timestamp, error) {
	wmClassAtom, err := xprop.Atm(X, "WM_CLASS")
	if err != nil {
		return 0, err
	}

	stringAtom, err := xprop.Atm(X, "STRING")
	if err != nil {
		return 0, err
	}

	// Make sure we're listening to PropertyChange events on the root window.
	err = xwindow.New(X, X.RootWin()).Listen(xproto.EventMaskPropertyChange)
	if err != nil {
		return 0, fmt.Errorf(
			"Could not listen to Root window events (PropertyChange): %s", err)
	}

	// Do a zero-length append on a property as suggested by ICCCM 2.1.
	err = xproto.ChangePropertyChecked(
		X.Conn(), xproto.PropModeAppend, X.RootWin(),
		wmClassAtom, stringAtom, 8, 0, nil).Check()
	if err != nil {
		return 0, err
	}

	// Now look for the PropertyNotify generated by that zero-length append
	// and return the timestamp attached to that event.
	// Note that we do this outside of xgbutil/xevent, since ownership
	// is literally the first thing we do after connecting to X.
	// (i.e., we don't have our event handling system initialized yet.)
	timeout := time.After(3 * time.Second)
	for {
		select {
		case <-timeout:
			return 0, fmt.Errorf(
				"Expected a PropertyNotify event to get a valid timestamp, " +
					"but never received one.")
		default:
			ev, err := X.Conn().PollForEvent()
			if err != nil {
				continue
			}
			if propNotify, ok := ev.(xproto.PropertyNotifyEvent); ok {
				X.TimeSet(propNotify.Time) // why not?
				return propNotify.Time, nil
			}
			time.Sleep(100 * time.Millisecond)
		}
	}
	panic("unreachable")
}
Exemplo n.º 3
0
// 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))
}
Exemplo n.º 4
0
// 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.")
		}

		hooks := getHooks(xu)
		for _, hook := range hooks {
			if !hook.Run(xu, ev) {
				goto END
			}
		}

		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)
			}
		}

	END:

		if pingBefore != nil && pingAfter != nil {
			pingAfter <- struct{}{}
		}
	}
}
Exemplo n.º 5
0
func New(X *xgbutil.XUtil) (*SystemTray, error) {
	tray := &SystemTray{
		X: X,
	}

	var err error

	if sysTrayAtom == 0 {
		sysTrayAtom, err = xprop.Atom(X, "_NET_SYSTEM_TRAY_S0", false)

		if err != nil {
			return nil, err
		}
	}

	if sysTrayMsgAtom == 0 {
		sysTrayMsgAtom, err = xprop.Atom(X, "_NET_SYSTEM_TRAY_OPCODE", false)

		if err != nil {
			return nil, err
		}
	}

	if managerEventAtom == 0 {
		managerEventAtom, err = xprop.Atom(X, "MANAGER", false)

		if err != nil {
			return nil, err
		}
	}

	tray.wid, err = xwindow.Create(X, X.RootWin())

	if err != nil {
		return nil, err
	}

	ts, err := currentTime(X)

	if err != nil {
		return nil, err
	}

	X.TimeSet(ts)

	// tray.wid.Listen(xproto.EventMaskNoEvent | xproto.EventMaskPropertyChange)

	err = xproto.SetSelectionOwnerChecked(tray.X.Conn(), tray.wid.Id, sysTrayAtom, tray.X.TimeGet()).Check()

	if err != nil {
		return nil, err
	}

	reply, err := xproto.GetSelectionOwner(X.Conn(), sysTrayAtom).Reply()
	if err != nil {
		return nil, err
	}

	if reply.Owner != tray.wid.Id {
		return nil, fmt.Errorf("Could not get ownership of the thingy-thing.")
	}

	evt, err := xevent.NewClientMessage(32, X.RootWin(), managerEventAtom,
		int(X.TimeGet()), int(sysTrayAtom), int(tray.wid.Id))

	if err != nil {
		return nil, err
	}

	if err = xevent.SendRootEvent(X, evt, xproto.EventMaskStructureNotify); err != nil {
		return nil, err
	}

	xevent.ClientMessageFun(func(_ *xgbutil.XUtil, ev xevent.ClientMessageEvent) {
		tray.event(ev)
	}).Connect(tray.X, tray.wid.Id)

	return tray, nil
}