// SPDX-License-Identifier: Unlicense OR MIT package app import ( "gioui.org/io/event" "golang.org/x/net/idna" "image" "net/url" "os" "path/filepath" "strings" "time" "gioui.org/io/input" "gioui.org/layout" "gioui.org/op" "gioui.org/unit" ) // extraArgs contains extra arguments to append to // os.Args. The arguments are separated with |. // Useful for running programs on mobiles where the // command line is not available. // Set with the go linker flag -X. var extraArgs string // ID is the app id exposed to the platform. // // On Android ID is the package property of AndroidManifest.xml, // on iOS ID is the CFBundleIdentifier of the app Info.plist, // on Wayland it is the toplevel app_id, // on X11 it is the X11 XClassHint. // // ID is set by the [gioui.org/cmd/gogio] tool or manually with the -X linker flag. For example, // // go build -ldflags="-X 'gioui.org/app.ID=org.gioui.example.Kitchen'" . // // Note that ID is treated as a constant, and that changing it at runtime // is not supported. The default value of ID is filepath.Base(os.Args[0]). var ID = "" // A FrameEvent requests a new frame in the form of a list of // operations that describes the window content. type FrameEvent struct { // Now is the current animation. Use Now instead of time.Now to // synchronize animation and to avoid the time.Now call overhead. Now time.Time // Metric converts device independent dp and sp to device pixels. Metric unit.Metric // Size is the dimensions of the window. Size image.Point // Insets represent the space occupied by system decorations and controls. Insets Insets // Frame completes the FrameEvent by drawing the graphical operations // from ops into the window. Frame func(frame *op.Ops) // Source is the interface between the window and widgets. Source input.Source } // URLEvent is generated for external requests to open a URL. Unlike window specific events, // it is delivered through the [Events] iterator. // // In order to receive URLEvents the program must register one or more URL schemes. A scheme can // be registered using gogio, with the `-schemes` flag. type URLEvent struct { URL *url.URL } // ViewEvent provides handles to the underlying window objects for the // current display protocol. type ViewEvent interface { implementsViewEvent() ImplementsEvent() // Valid will return true when the ViewEvent does contains valid handles. // If a window receives an invalid ViewEvent, it should deinitialize any // state referring to handles from a previous ViewEvent. Valid() bool } // Insets is the space taken up by // system decoration such as translucent // system bars and software keyboards. type Insets struct { // Values are in pixels. Top, Bottom, Left, Right unit.Dp } // NewContext is shorthand for // // layout.Context{ // Ops: ops, // Now: e.Now, // Source: e.Source, // Metric: e.Metric, // Constraints: layout.Exact(e.Size), // } // // NewContext calls ops.Reset and adjusts ops for e.Insets. func NewContext(ops *op.Ops, e FrameEvent) layout.Context { ops.Reset() size := e.Size if e.Insets != (Insets{}) { left := e.Metric.Dp(e.Insets.Left) top := e.Metric.Dp(e.Insets.Top) op.Offset(image.Point{ X: left, Y: top, }).Add(ops) size.X -= left + e.Metric.Dp(e.Insets.Right) size.Y -= top + e.Metric.Dp(e.Insets.Bottom) } return layout.Context{ Ops: ops, Now: e.Now, Source: e.Source, Metric: e.Metric, Constraints: layout.Exact(size), } } // DataDir returns a path to use for application-specific // configuration data. // On desktop systems, DataDir use os.UserConfigDir. // On iOS NSDocumentDirectory is queried. // For Android Context.getFilesDir is used. // // BUG: On Android, DataDir panics if called before main. func DataDir() (string, error) { return dataDir() } // Main must be called last from the program main function. // On most platforms Main blocks forever, for Android and // iOS it returns immediately to give control of the main // thread back to the system. // // Calling Main is necessary because some operating systems // require control of the main thread of the program for // running windows. func Main() { osMain() } // Events is an iterator that yields events that are not specific to any window, // such as [URLEvent]. It never returns. // // Events must be called by the main goroutine, and replaces the // call to [Main]. func Events(yield func(event.Event) bool) { yieldGlobalEvent = yield osMain() } var yieldGlobalEvent func(evt event.Event) bool func processGlobalEvent(evt event.Event) { if yieldGlobalEvent == nil { return } if !yieldGlobalEvent(evt) { yieldGlobalEvent = nil } } func (FrameEvent) ImplementsEvent() {} func (URLEvent) ImplementsEvent() {} func init() { if extraArgs != "" { args := strings.Split(extraArgs, "|") os.Args = append(os.Args, args...) } if ID == "" { ID = filepath.Base(os.Args[0]) } } // newURLEvent creates a URLEvent from a raw URL string, handling Punycode decoding. func newURLEvent(rawurl string) (URLEvent, error) { u, err := url.Parse(rawurl) if err != nil { return URLEvent{}, err } u.Host, err = idna.Punycode.ToUnicode(u.Hostname()) if err != nil { return URLEvent{}, err } u, err = url.Parse(u.String()) if err != nil { return URLEvent{}, err } return URLEvent{URL: u}, nil }