package main import ( "context" "errors" "log" "os" "gioui.org/app" "gioui.org/op" "gioui.org/unit" "softbox.local/app-modern/platform/windows" softboxgio "softbox.local/app-modern/ui/gio" "softbox.local/core" "softbox.local/core/application" ) const applicationEventCapacity = 32 func main() { if handled, err := acknowledgeInternalUpdateHealth(os.Args[1:]); handled { if err != nil { log.Printf("%s internal update health failed: %v", core.ProductName, err) os.Exit(1) } } go func() { if err := run(); err != nil { log.Printf("%s stopped: %v", core.ProductName, err) } os.Exit(0) }() app.Main() } func run() error { return runWithLoaders( application.UnconfiguredCatalogLoader{}, application.UnconfiguredAuthorizationLoader{}, ) } func runWithCatalogLoader(loader application.CatalogSnapshotLoader) error { return runWithLoaders(loader, application.UnconfiguredAuthorizationLoader{}) } func runWithLoaders( loader application.CatalogSnapshotLoader, authorizationLoader application.AuthorizationSnapshotLoader, ) error { return runWithCompositions(loader, authorizationLoader, nil) } func runWithCompositions( loader application.CatalogSnapshotLoader, authorizationLoader application.AuthorizationSnapshotLoader, licenseImport *application.LicenseImport, ) error { platform := windows.New() window := new(app.Window) window.Option( app.Title(core.ProductName), app.Size(unit.Dp(1080), unit.Dp(720)), ) theme := softboxgio.NewTheme() shell := softboxgio.NewAppShell(string(platform.Edition())) runtime := application.NewRuntime(applicationEventCapacity) relay, err := application.NewEventRelay(applicationEventCapacity) if err != nil { return err } eventContext, cancelEvents := context.WithCancel(context.Background()) pumpDone := make(chan error, 1) go func() { pumpDone <- application.PumpEvents( eventContext, runtime.Events(), relay, window.Invalidate, ) }() catalogDone := startCatalogBootstrap(eventContext, runtime, loader) authorizationDone := startAuthorizationBootstrap(eventContext, runtime, authorizationLoader) defer func() { cancelEvents() runtime.Close() relay.Close() if bootstrapErr := <-catalogDone; bootstrapErr != nil && !errors.Is(bootstrapErr, context.Canceled) && !errors.Is(bootstrapErr, application.ErrCatalogSourceUnconfigured) && !errors.Is(bootstrapErr, application.ErrRuntimeClosed) && !errors.Is(bootstrapErr, application.ErrEventRelayClosed) { log.Printf("%s catalog bootstrap failed", core.ProductName) } if authorizationErr := <-authorizationDone; authorizationErr != nil && !errors.Is(authorizationErr, context.Canceled) && !errors.Is(authorizationErr, application.ErrAuthorizationSourceUnconfigured) && !errors.Is(authorizationErr, application.ErrRuntimeClosed) && !errors.Is(authorizationErr, application.ErrEventRelayClosed) { log.Printf("%s authorization bootstrap failed", core.ProductName) } if pumpErr := <-pumpDone; pumpErr != nil && !errors.Is(pumpErr, context.Canceled) && !errors.Is(pumpErr, application.ErrEventRelayClosed) { log.Printf("application event pump stopped: %v", pumpErr) } }() var operations op.Ops importing := make(chan struct{}, 1) for { switch event := window.Event().(type) { case app.DestroyEvent: return event.Err case app.FrameEvent: if err := relay.Drain(shell.ApplyEvent); err != nil { log.Printf("apply application event: %v", err) } gtx := app.NewContext(&operations, event) shell.Layout(gtx, theme) if shell.TakeLicenseImportRequest() && licenseImport != nil { select { case importing <- struct{}{}: go func() { defer func() { <-importing }() if importErr := licenseImport.Run(eventContext); importErr != nil && !errors.Is(importErr, context.Canceled) && !errors.Is(importErr, application.ErrRuntimeClosed) && !errors.Is(importErr, application.ErrEventRelayClosed) { log.Printf("%s license import failed", core.ProductName) } }() default: } } event.Frame(gtx.Ops) } } } func startCatalogBootstrap( ctx context.Context, runtime *application.Runtime, loader application.CatalogSnapshotLoader, ) <-chan error { done := make(chan error, 1) go func() { done <- application.NewCatalogBootstrap(loader, runtime).Run(ctx) }() return done } func startAuthorizationBootstrap( ctx context.Context, runtime *application.Runtime, loader application.AuthorizationSnapshotLoader, ) <-chan error { done := make(chan error, 1) go func() { done <- application.NewAuthorizationBootstrap(loader, runtime).Run(ctx) }() return done }