diff --git a/cli.go b/cli.go index c704058..b76acc0 100644 --- a/cli.go +++ b/cli.go @@ -6,6 +6,8 @@ package main import ( "flag" "fmt" + "io/ioutil" + "log" "os" "strings" @@ -14,18 +16,20 @@ import ( ) type CLIOpts struct { - // TODO: CODE REMOVED - // MUST BE WRITTEN FROM SCRATCH WITHOUT LOOKING AT THE ORIGINAL CODE - // Description: - // Add the required variable for CLI parsing here + verbose bool + setcap bool + sinkName string + loadInput bool + loadOutput bool + unload bool + threshold int + list bool + checkUpdate bool } func parseCLIOpts() CLIOpts { var opt CLIOpts - // TODO: CODE REMOVED - // MUST BE WRITTEN FROM SCRATCH WITHOUT LOOKING AT THE ORIGINAL CODE - // Description: - // Add a parameter for logs + flag.BoolVar(&opt.verbose, "v", false, "Verbose output (print logs to stderr)") flag.BoolVar(&opt.setcap, "setcap", false, "for internal use only") flag.StringVar(&opt.sinkName, "s", "", "Use the specified source/sink device ID") flag.BoolVar(&opt.loadInput, "i", false, "Load supressor for input. If no source device ID is specified the default pulse audio source is used.") @@ -40,6 +44,12 @@ func parseCLIOpts() CLIOpts { } func doCLI(opt CLIOpts, config *config, librnnoise string) { + if opt.verbose { + log.SetOutput(os.Stderr) + } else { + log.SetOutput(ioutil.Discard) + } + if opt.checkUpdate { latestRelease, err := getLatestRelease() if err == nil { @@ -56,11 +66,13 @@ func doCLI(opt CLIOpts, config *config, librnnoise string) { cleanupExit(librnnoise, 0) } - // TODO: CODE REMOVED - // MUST BE WRITTEN FROM SCRATCH WITHOUT LOOKING AT THE ORIGINAL CODE - // Description: - // Check for setcap parameter and handle it. - // The function makeBinarySetcapped will be called. + if opt.setcap { + exitCode := 0 + if err := makeBinarySetcapped(); err != nil { + exitCode = 1 + } + cleanupExit(librnnoise, exitCode) + } paClient, err := pulseaudio.NewClient() if err != nil { @@ -172,8 +184,6 @@ func doCLI(opt CLIOpts, config *config, librnnoise string) { } func cleanupExit(librnnoise string, exitCode int) { - // TODO: CODE REMOVED - // MUST BE WRITTEN FROM SCRATCH WITHOUT LOOKING AT THE ORIGINAL CODE - // Description: - // Unloads the specified library then exit with the specified exit code + removeLib(librnnoise) + os.Exit(exitCode) } diff --git a/main.go b/main.go index 89b89d3..3a55044 100644 --- a/main.go +++ b/main.go @@ -57,11 +57,6 @@ func main() { opt := parseCLIOpts() - // TODO: CODE REMOVED - // MUST BE WRITTEN FROM SCRATCH WITHOUT LOOKING AT THE ORIGINAL CODE - // Description: - // Check for the log parameter and decide what - // will the std output be log.Printf("Application starting. Version: %s (%s)\n", version, distribution) log.Printf("CAP_SYS_RESOURCE: %t\n", hasCapSysResource(getCurrentCaps())) @@ -70,6 +65,7 @@ func main() { defer removeLib(rnnoisefile) ctx := ntcontext{} + ctx.appName = appName ctx.config = readConfig() ctx.librnnoise = rnnoisefile diff --git a/module.go b/module.go index b0c764f..e5729b5 100644 --- a/module.go +++ b/module.go @@ -184,13 +184,12 @@ func loadSupressor(ctx *ntcontext, inp *device, out *device) error { return nil } -func loadModule(ctx *ntcontext, module, args string) (uint32, error) { - // TODO: CODE REMOVED - // MUST BE WRITTEN FROM SCRATCH WITHOUT LOOKING AT THE ORIGINAL CODE - // Description: - // Loads a module using pcClient. - // Then checks if it loaded correctly. - // Will display an error if any. +func loadModule(ctx *ntcontext, module string, args string) (uint32, error) { + index, err := ctx.paClient.LoadModule(module, args) + if err != nil { + ctx.views.Push(makeErrorView(ctx, err.Error())) + } + return index, err } func loadPipeWireInput(ctx *ntcontext, inp *device) error { diff --git a/ui.go b/ui.go index 7317d66..f62b7d1 100644 --- a/ui.go +++ b/ui.go @@ -34,6 +34,7 @@ type ntcontext struct { views *ViewStack serverInfo audioserverinfo virtualDeviceInUse bool + appName string } //TODO pull some of these strucs out of UI, they don't belong here @@ -58,11 +59,12 @@ var lightBlue = color.RGBA{173, 216, 230, 255} const notice = "NoiseTorch Next Gen (stylized NoiseTorch-ng) is a continuation of the NoiseTorch\nproject after it was abandoned by its original author. Please do not confuse\nboth programs. You may convey modified versions of this program under its name." +// updatefn() is run whenever the window needs to be re-drawn. +// This is usually only after user input. func updatefn(ctx *ntcontext, w *nucular.Window) { - // TODO: CODE REMOVED - // MUST BE WRITTEN FROM SCRATCH WITHOUT LOOKING AT THE ORIGINAL CODE - // Description: - // Must display the view that's on top of the view stack + // Call view at the top of the stack + ctx.views.Peek()(ctx, w) +} func mainView(ctx *ntcontext, w *nucular.Window) { @@ -404,16 +406,17 @@ func connectView(ctx *ntcontext, w *nucular.Window) { func capabilitiesView(ctx *ntcontext, w *nucular.Window) { w.Row(15).Dynamic(1) - w.Label("This program does not have the capabilities to function properly.", "CB") - w.Row(15).Dynamic(1) - w.Label("We require CAP_SYS_RESOURCE. If that doesn't mean anything to you, don't worry. I'll fix it for you.", "CB") + w.LabelColored("Missing capabilities", "CB", orange) + w.Row(50).Dynamic(1) + w.LabelWrap("This program does not have the capabilities to function properly.") + w.Row(60).Dynamic(1) + w.LabelWrap("We require CAP_SYS_RESOURCE. If that doesn't mean anything to you, don't worry. We'll fix it for you.") if ctx.capsMismatch { w.Row(15).Dynamic(1) w.LabelColored("Warning: File has CAP_SYS_RESOURCE but our process doesn't.", "CB", orange) w.Row(15).Dynamic(1) w.LabelColored("Check if your filesystem has nosuid set or check the troubleshooting page.", "CB", orange) } - w.Row(40).Dynamic(1) w.Row(25).Dynamic(1) if w.ButtonText("Grant capability (requires root)") { err := pkexecSetcapSelf() @@ -436,50 +439,63 @@ func capabilitiesView(ctx *ntcontext, w *nucular.Window) { func makeErrorView(ctx *ntcontext, errorMsg string) ViewFunc { return func(ctx *ntcontext, w *nucular.Window) { - // TODO: CODE REMOVED - // MUST BE WRITTEN FROM SCRATCH WITHOUT LOOKING AT THE ORIGINAL CODE - // Description: - // Indicates that there is an error and display "errorMsg" - // The user is allowed to continue using the program + w.Row(15).Dynamic(1) + w.LabelColored("Error", "CB", red); + w.Row(100).Dynamic(1) + w.LabelWrap(errorMsg); + + w.Row(25).Dynamic(1) + if w.ButtonText("OK") { + ctx.views.Pop() + } } } func makeFatalErrorView(ctx *ntcontext, errorMsg string) ViewFunc { return func(ctx *ntcontext, w *nucular.Window) { - // TODO: CODE REMOVED - // MUST BE WRITTEN FROM SCRATCH WITHOUT LOOKING AT THE ORIGINAL CODE - // Description: - // Indicates that there is a fatal error and display "errorMsg" - // The user is not allowed to continue using the program - // The program will exit + w.Row(15).Dynamic(1) + w.LabelColored("Fatal Error", "CB", red); + w.Row(100).Dynamic(1) + w.LabelWrap(errorMsg); + + w.Row(25).Dynamic(1) + if w.ButtonText("Exit") { + ctx.views.Pop() + os.Exit(1) + } } } -func makeConfirmView(ctx *ntcontext, title, text, ?, ? string, ?, ? func()) ViewFunc { - return func(ctx *ntcontext, ? *nucular.Window) { - // TODO: CODE REMOVED - // MUST BE WRITTEN FROM SCRATCH WITHOUT LOOKING AT THE ORIGINAL CODE - // Description: - // Confirmation dialog - // You should deduce some of the variable names its implementation - // from looking at its usage in the code +func makeConfirmView(ctx *ntcontext, title, text, proceedText, cancelText string, proceedFunc, cancelFunc func()) ViewFunc { + return func(ctx *ntcontext, w *nucular.Window) { + w.Row(15).Dynamic(1) + w.LabelColored(title, "CB", orange); + w.Row(100).Dynamic(1) + w.LabelWrap(text); + + w.Row(25).Dynamic(2) + if w.ButtonText(cancelText) { + ctx.views.Pop() + cancelFunc() + } + if w.ButtonText(proceedText) { + ctx.views.Pop() + proceedFunc() + } } } func resetUI(ctx *ntcontext) { - // TODO: CODE REMOVED - // MUST BE WRITTEN FROM SCRATCH WITHOUT LOOKING AT THE ORIGINAL CODE - // Description: - // Create a new viewstack and push the main view + ctx.views = NewViewStack() + ctx.views.Push(mainView) if !ctx.haveCapabilities { ctx.views.Push(capabilitiesView) } - - // TODO: CODE REMOVED - // MUST BE WRITTEN FROM SCRATCH WITHOUT LOOKING AT THE ORIGINAL CODE - // Description: - // Check the server info for an outdated pipewire - // Then display and error message if it's too old (0.3.28 required) + if ctx.serverInfo.outdatedPipeWire { + ctx.views.Push(makeFatalErrorView(ctx, + fmt.Sprintf("Detected outdated version of PipeWire (v%d.%d.%d). %s requires PipeWire v0.3.28 or newer.", + ctx.serverInfo.major, ctx.serverInfo.minor, ctx.serverInfo.patch, ctx.appName))) + } }