You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: inputLoop.go
    path of file: ./repos/oak
    
    the code of the file until where you have to start completion: package oak

import (
	"github.com/oakmound/oak/v4/alg/intgeom"
	"github.com/oakmound/oak/v4/event"
	"github.com/oakmound/oak/v4/timing"

	"github.com/oakmound/oak/v4/dlog"
	okey "github.com/oakmound/oak/v4/key"
	omouse "github.com/oakmound/oak/v4/mouse"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/mouse"
	"golang.org/x/mobile/event/size"
)

// The following block defines events generated by oak during scene execution
var (
	// ViewportUpdate is triggered when the position of of the viewport changes
	ViewportUpdate = event.RegisterEvent[intgeom.Point2]()
	// OnStop is triggered when the engine is stopped, e.g. when a window's close
	// button is clicked.
	OnStop = event.RegisterEvent[struct{}]()
	// FocusGain is triggered when a window gains focus
	FocusGain = event.RegisterEvent[struct{}]()
	// FocusLoss is triggered when a window loses focus
	FocusLoss = event.RegisterEvent[struct{}]()
	// InputChange is triggered when the most recent input device changes (e.g. keyboard to joystick or vice versa). It
	// is only sent if Config.TrackInputChanges is true when Init is called.
	InputChange = event.RegisterEvent[InputType]()
)

func (w *Window) inputLoop() {
	for {
		switch e := w.Window.NextEvent().(type) {
    
func (w *Window) inputLoop() {
	for {
		switch e := w.Window.NextEvent().(type) {
		// We only currently respond to death lifecycle events.
		case lifecycle.Event:
			switch e.To {
			case lifecycle.StageDead:
				dlog.Info(dlog.WindowClosed)
				<-event.TriggerOn(w.eventHandler, OnStop, struct{}{})
				close(w.quitCh)
				return
			case lifecycle.StageFocused:
				w.inFocus = true
				// If you are in focused state, we don't care how you got there
				w.DrawTicker.Reset(timing.FPSToFrameDelay(w.DrawFrameRate))
				event.TriggerOn(w.eventHandler, FocusGain, struct{}{})
			case lifecycle.StageVisible:
				// If the last state was focused, this means the app is out of focus
				// otherwise, we're visible for the first time
				if e.From > e.To {
					w.inFocus = false
					w.DrawTicker.Reset(timing.FPSToFrameDelay(w.IdleDrawFrameRate))
					event.TriggerOn(w.eventHandler, FocusLoss, struct{}{})
				} else {
					w.inFocus = true
					w.DrawTicker.Reset(timing.FPSToFrameDelay(w.DrawFrameRate))
					event.TriggerOn(w.eventHandler, FocusGain, struct{}{})
				}
			}
		// Send key events
		//
		// Key events have two varieties:
		// The "KeyDown" and "KeyUp" events, which trigger for all keys
		// and specific "KeyDown$key", etc events which trigger only for $key.
		// The specific key that is pressed is passed as the data interface for
		// the former events, but not for the latter.
		case key.Event:
			switch e.Direction {
			case key.DirPress:
				w.TriggerKeyDown(okey.Event(e))
			case key.DirRelease:
				w.TriggerKeyUp(okey.Event(e))
			default:
				w.TriggerKeyHeld(okey.Event(e))
			}

		// Send mouse events
		//
		// Mouse events are parsed based on their button
		// and direction into an event name and then triggered:
		// 'MousePress', 'MouseRelease', 'MouseScrollDown', 'MouseScrollUp', and 'MouseDrag'
		//
		// The basic event name is meant for entities which
		// want to respond to the mouse event happening -anywhere-.
		//
		// For events which have mouse collision enabled, they'll receive
		// $eventName+"On" when the event occurs within their collision area.
		//
		// Mouse events all receive an x, y, and button string.
		case mouse.Event:
			button := omouse.Button(e.Button)
			ev := omouse.GetEvent(e.Direction, e.Button)
			// The event triggered for mouse events has the same scaling as the
			// render and collision space. I.e. if the viewport is at 0, the mouse's
			// position is exactly the same as the position of a visible entity
			// on screen. When not at zero, the offset will be exactly the viewport.
			mevent := omouse.NewEvent(
				float64((((e.X - float32(w.windowRect.Min.X)) / float32(w.windowRect.Max.X-w.windowRect.Min.X)) * float32(w.ScreenWidth))),
				float64((((e.Y - float32(w.windowRect.Min.Y)) / float32(w.windowRect.Max.Y-w.windowRect.Min.Y)) * float32(w.ScreenHeight))),
				button,
				ev,
			)
			w.TriggerMouseEvent(mevent)

		// Size events update what we scale the screen to
		case size.Event:
			err := w.ChangeWindow(e.WidthPx, e.HeightPx)
			dlog.ErrorCheck(err)
		}
	}
}