From 6dd0052dcf530af22a43ca02a8e0b6cebcc383e0 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Mon, 25 Aug 2025 10:02:14 -0500 Subject: [PATCH] restructor code --- Makefile | 6 ++- README.md | 25 ++++++++++- argv.go | 36 ++++++++++++++++ argvAutoshell.go | 87 ++++++++++++++++++++++++++++++++++++++ common.go | 102 +++++++++++++++++++++++++++++++++++++++++++++ exit.go | 22 ++++++++++ launch_terminal.go | 98 +------------------------------------------ main.go | 55 ++++++++++++++++++++++++ structs.go | 17 ++++++++ sync_terminals.go | 34 +-------------- 10 files changed, 350 insertions(+), 132 deletions(-) create mode 100644 argv.go create mode 100644 argvAutoshell.go create mode 100644 common.go create mode 100644 exit.go create mode 100644 main.go create mode 100644 structs.go diff --git a/Makefile b/Makefile index 3a70645..035f59c 100644 --- a/Makefile +++ b/Makefile @@ -3,13 +3,15 @@ VERSION = $(shell git describe --tags) BUILDTIME = $(shell date +%Y.%m.%d) +default: goimports verbose + build: GO111MODULE=off go build \ -ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}" - ./xstartplacement + ./startxplacement verbose: - GO111MODULE=off go build -v -x \ + GO111MODULE=off go install -v -x \ -ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}" install: diff --git a/README.md b/README.md index 735fb1b..564f8d5 100644 --- a/README.md +++ b/README.md @@ -1 +1,24 @@ -# devils pie code to try to fix window placement +# This is an attempt to redo 'startx' not as a bash script this time + +GOALS: + + * use a protobuf TEXT config file + * store global file in ~/etc/startx.text + * store user config file in ~/.config/startx.text + * parse additional settings from ~/.config/startx.d/ + * restore the geometry of the apps + * make a GUI save/edit/restore tool + +CURRENTLY WORKING: + + * works with x windows + * restores mate-terminal on debian + +TODO: + + * wayland support + * other programs like firefox & keepass + * actually execute from lightdm + * run underneath mate-session + * run underneath all the other WM + * rename binary 'startx' diff --git a/argv.go b/argv.go new file mode 100644 index 0000000..8f1971e --- /dev/null +++ b/argv.go @@ -0,0 +1,36 @@ +// Copyright 2017-2025 WIT.COM Inc. All rights reserved. +// Use of this source code is governed by the GPL 3.0 + +package main + +/* + this parses the command line arguements using alex flint's go-arg +*/ + +var argv args + +type args struct { + Restore string `arg:"--restore" help:"restore terminal windows from a config file"` + Save *EmptyCmd `arg:"subcommand:save" help:"save current window geometries to the your config file"` + Dump *EmptyCmd `arg:"subcommand:dump" help:"show your current window geometries"` + Force bool `arg:"--force" help:"try to strong arm things"` + Verbose bool `arg:"--verbose" help:"show more output"` + Bash bool `arg:"--bash" help:"generate bash completion"` + BashAuto []string `arg:"--auto-complete" help:"todo: move this to go-arg"` +} + +type EmptyCmd struct { +} + +func (args) Version() string { + return ARGNAME + " " + VERSION + " Built on " + BUILDTIME +} + +func (a args) Description() string { + return ` +startxplacment -- run this after 'startx' to restore all your apps + +will attempt to launch your terminal windows on the right Workspaces +and with the right geometries. TODO: restore the bash working paths + ` +} diff --git a/argvAutoshell.go b/argvAutoshell.go new file mode 100644 index 0000000..85d841b --- /dev/null +++ b/argvAutoshell.go @@ -0,0 +1,87 @@ +// Copyright 2017-2025 WIT.COM Inc. All rights reserved. +// Use of this source code is governed by the GPL 3.0 + +package main + +import ( + "fmt" + "os" +) + +/* + handles shell autocomplete +*/ + +// used for shell auto completion +// var ARGNAME string = "forge" // todo: get this from $0 ? + +func deleteMatch() { + // f := forgedb.InitSimple() + fmt.Println("go.wit.com/lib/gui/repostatus todo: need to do this") +} + +func (args) doBashAuto() { + argv.doBashHelp() + switch argv.BashAuto[0] { + case "dump": + fmt.Println("--terminals") + default: + if argv.BashAuto[0] == ARGNAME { + // list the subcommands here + fmt.Println("--restore save dump") + } + } + os.Exit(0) +} + +// prints help to STDERR // TODO: move everything below this to go-args +func (args) doBashHelp() { + if argv.BashAuto[1] != "''" { + // if this is not blank, then the user has typed something + return + } + if argv.BashAuto[0] != ARGNAME { + // if this is not the name of the command, the user already started doing something + return + } + if argv.BashAuto[0] == ARGNAME { + me.pp.WriteHelp(os.Stderr) + return + } + fmt.Fprintln(os.Stderr, "") + fmt.Fprintln(os.Stderr, "something went wrong with the GO args package") + fmt.Fprintln(os.Stderr, "") +} + +// complete -F forge --bash forge +func (args) doBash() { + fmt.Println("# add this in your bashrc:") + fmt.Println("") + fmt.Println("# todo: add this to go-arg as a 'hidden' go-arg option --bash") + fmt.Println("#") + fmt.Println("# Put the below in the file: ~/.local/share/bash-completion/completions/" + ARGNAME) + fmt.Println("#") + fmt.Println("# todo: make this output work/parse with:") + fmt.Println("# complete -C " + ARGNAME + " --bash go") + fmt.Println("") + fmt.Println("_" + ARGNAME + "_complete()") + fmt.Println("{") + fmt.Println(" # sets local to this func vars") + fmt.Println(" local cur prev all") + fmt.Println(" cur=${COMP_WORDS[COMP_CWORD]}") + fmt.Println(" prev=${COMP_WORDS[COMP_CWORD-1]}") + fmt.Println(" all=${COMP_WORDS[@]}") + fmt.Println("") + fmt.Println(" # this is where we generate the go-arg output") + fmt.Println(" GOARGS=$(" + ARGNAME + " --auto-complete $prev \\'$cur\\' $all)") + fmt.Println("") + fmt.Println(" # this compares the command line input from the user") + fmt.Println(" # to whatever strings we output") + fmt.Println(" COMPREPLY=( $(compgen -W \"$GOARGS\" -- $cur) ) # THIS WORKS") + fmt.Println(" return 0") + fmt.Println("}") + fmt.Println("complete -F _" + ARGNAME + "_complete " + ARGNAME) + fmt.Println("") + fmt.Println("# copy and paste the above into your bash shell should work") + os.Exit(0) +} diff --git a/common.go b/common.go new file mode 100644 index 0000000..202111c --- /dev/null +++ b/common.go @@ -0,0 +1,102 @@ +package main + +import ( + "bufio" + "bytes" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" +) + +// Helper functions +func getWindowList() (map[string]string, error) { + cmd := exec.Command("wmctrl", "-l") + var out bytes.Buffer + cmd.Stdout = &out + if err := cmd.Run(); err != nil { + return nil, err + } + windows := make(map[string]string) + scanner := bufio.NewScanner(&out) + for scanner.Scan() { + line := scanner.Text() + fields := strings.Fields(line) + if len(fields) > 0 { + windows[fields[0]] = strings.Join(fields[3:], " ") + } + } + return windows, nil +} + +// findNewWindow compares two maps of windows and returns the ID of the new window. +func findNewWindow(before, after map[string]string) string { + for id := range after { + if _, ok := before[id]; !ok { + return id + } + } + return "" +} + +// parseConfig remains the same as before. +func parseConfig(filePath string) ([]WindowConfig, error) { + file, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer file.Close() + + var configs []WindowConfig + scanner := bufio.NewScanner(file) + var currentConfig WindowConfig + + homeDir, err := os.UserHomeDir() + if err != nil { + return nil, fmt.Errorf("could not get user home directory: %w", err) + } + + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, " Title: ") { + title := strings.TrimSpace(strings.TrimPrefix(line, " Title: ")) + currentConfig.Title = title + parts := strings.SplitN(title, ": ", 2) + if len(parts) == 2 { + path := parts[1] + if strings.HasPrefix(path, "~") { + path = filepath.Join(homeDir, path[1:]) + } + currentConfig.Path = path + } + } else if strings.HasPrefix(line, " Geometry: ") { + geomStr := strings.TrimSpace(strings.TrimPrefix(line, " Geometry: ")) + var x, y, w, h string + _, err := fmt.Sscanf(geomStr, "X=%s Y=%s Width=%s Height=%s", &x, &y, &w, &h) + if err == nil { + x = strings.TrimSuffix(x, ",") + y = strings.TrimSuffix(y, ",") + w = strings.TrimSuffix(w, ",") + currentConfig.Geometry = fmt.Sprintf("%sx%s+%s+%s", w, h, x, y) + } + } else if strings.HasPrefix(line, " Workspace: ") { + currentConfig.Workspace = strings.TrimSpace(strings.TrimPrefix(line, " Workspace: ")) + } else if line == "---" { + if currentConfig.Path != "" { + configs = append(configs, currentConfig) + } + currentConfig = WindowConfig{} // Reset for the next entry + } + } + + if currentConfig.Path != "" { + configs = append(configs, currentConfig) + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return configs, nil +} diff --git a/exit.go b/exit.go new file mode 100644 index 0000000..ad6195b --- /dev/null +++ b/exit.go @@ -0,0 +1,22 @@ +// Copyright 2017-2025 WIT.COM Inc. All rights reserved. +// Use of this source code is governed by the GPL 3.0 + +package main + +import ( + "os" + + "go.wit.com/log" +) + +func okExit(thing string) { + if thing != "" { + log.Info("regex exit:", thing, "ok") + } + os.Exit(0) +} + +func badExit(err error) { + log.Info("regex failed: ", err) + os.Exit(-1) +} diff --git a/launch_terminal.go b/launch_terminal.go index be3a5d7..7ee72fe 100644 --- a/launch_terminal.go +++ b/launch_terminal.go @@ -1,12 +1,9 @@ package main import ( - "bufio" - "bytes" "fmt" "os" "os/exec" - "path/filepath" "strings" "time" ) @@ -19,7 +16,7 @@ type WindowConfig struct { Workspace string } -func main() { +func doLaunch() { // 1. Get current working directory. pwd, err := os.Getwd() if err != nil { @@ -28,7 +25,6 @@ func main() { } // 2. Read and parse the configuration file. - configFile := "/home/jcarr/go/src/gemini/xstartplacement.out" configs, err := parseConfig(configFile) if err != nil { fmt.Printf("Failed to parse config file '%s': %v\n", configFile, err) @@ -108,95 +104,3 @@ func main() { fmt.Println("Window setup complete.") } } - -// getWindowList returns a map of window IDs to their titles. -func getWindowList() (map[string]string, error) { - cmd := exec.Command("wmctrl", "-l") - var out bytes.Buffer - cmd.Stdout = &out - if err := cmd.Run(); err != nil { - return nil, err - } - - windows := make(map[string]string) - scanner := bufio.NewScanner(&out) - for scanner.Scan() { - line := scanner.Text() - fields := strings.Fields(line) - if len(fields) > 0 { - windows[fields[0]] = strings.Join(fields[3:], " ") - } - } - return windows, nil -} - -// findNewWindow compares two maps of windows and returns the ID of the new window. -func findNewWindow(before, after map[string]string) string { - for id := range after { - if _, ok := before[id]; !ok { - return id - } - } - return "" -} - -// parseConfig remains the same as before. -func parseConfig(filePath string) ([]WindowConfig, error) { - file, err := os.Open(filePath) - if err != nil { - return nil, err - } - defer file.Close() - - var configs []WindowConfig - scanner := bufio.NewScanner(file) - var currentConfig WindowConfig - - homeDir, err := os.UserHomeDir() - if err != nil { - return nil, fmt.Errorf("could not get user home directory: %w", err) - } - - for scanner.Scan() { - line := scanner.Text() - if strings.HasPrefix(line, " Title: ") { - title := strings.TrimSpace(strings.TrimPrefix(line, " Title: ")) - currentConfig.Title = title - parts := strings.SplitN(title, ": ", 2) - if len(parts) == 2 { - path := parts[1] - if strings.HasPrefix(path, "~") { - path = filepath.Join(homeDir, path[1:]) - } - currentConfig.Path = path - } - } else if strings.HasPrefix(line, " Geometry: ") { - geomStr := strings.TrimSpace(strings.TrimPrefix(line, " Geometry: ")) - var x, y, w, h string - _, err := fmt.Sscanf(geomStr, "X=%s Y=%s Width=%s Height=%s", &x, &y, &w, &h) - if err == nil { - x = strings.TrimSuffix(x, ",") - y = strings.TrimSuffix(y, ",") - w = strings.TrimSuffix(w, ",") - currentConfig.Geometry = fmt.Sprintf("%sx%s+%s+%s", w, h, x, y) - } - } else if strings.HasPrefix(line, " Workspace: ") { - currentConfig.Workspace = strings.TrimSpace(strings.TrimPrefix(line, " Workspace: ")) - } else if line == "---" { - if currentConfig.Path != "" { - configs = append(configs, currentConfig) - } - currentConfig = WindowConfig{} // Reset for the next entry - } - } - - if currentConfig.Path != "" { - configs = append(configs, currentConfig) - } - - if err := scanner.Err(); err != nil { - return nil, err - } - - return configs, nil -} diff --git a/main.go b/main.go new file mode 100644 index 0000000..26bba0b --- /dev/null +++ b/main.go @@ -0,0 +1,55 @@ +// Copyright 2017-2025 WIT.COM Inc. All rights reserved. +// Use of this source code is governed by the GPL 3.0 + +package main + +// An app to submit patches for the 30 GO GUI repos + +import ( + "os" + + "go.wit.com/dev/alexflint/arg" + "go.wit.com/gui" + "go.wit.com/log" +) + +// sent via -ldflags +var VERSION string +var BUILDTIME string + +// used for shell auto completion +var ARGNAME string = "startxplacment" + +// using this for now. triggers config save +var configSave bool + +var configFile string = "/home/jcarr/.config/startxplacement.out" + +func main() { + me = new(mainType) + gui.InitArg() + me.pp = arg.MustParse(&argv) + + if argv.Bash { + argv.doBash() + os.Exit(0) + } + if len(argv.BashAuto) != 0 { + argv.doBashAuto() + os.Exit(0) + } + + if argv.Dump != nil { + // doDump() + log.Info("dump here") + okExit("") + } + + if argv.Restore != "" { + log.Info("restore here") + okExit("") + } + + // doGui() + okExit("") +} diff --git a/structs.go b/structs.go new file mode 100644 index 0000000..178a187 --- /dev/null +++ b/structs.go @@ -0,0 +1,17 @@ +// Copyright 2017-2025 WIT.COM Inc. All rights reserved. +// Use of this source code is governed by the GPL 3.0 + +package main + +import ( + "go.wit.com/dev/alexflint/arg" + "go.wit.com/gui" +) + +var me *mainType + +// this app's variables +type mainType struct { + pp *arg.Parser // for parsing the command line args. Yay to alexf lint! + myGui *gui.Node // the gui toolkit handle +} diff --git a/sync_terminals.go b/sync_terminals.go index 2c557ba..b115d8f 100644 --- a/sync_terminals.go +++ b/sync_terminals.go @@ -26,9 +26,8 @@ type CurrentState struct { Path string } -func main() { +func doSync() { // 1. Read the desired state from the config file. - configFile := "/home/jcarr/go/src/gemini/xstartplacement.out" desiredStates, err := parseDesiredState(configFile) if err != nil { fmt.Printf("Error parsing config file: %v\n", err) @@ -105,7 +104,7 @@ func launchTerminal(state DesiredState) { fmt.Printf("Successfully launched terminal for %s\n", state.Path) } -// parseDesiredState reads the xstartplacement.out file. +// parseDesiredState reads the startxplacement.out file. func parseDesiredState(filePath string) ([]DesiredState, error) { file, err := os.Open(filePath) if err != nil { @@ -197,32 +196,3 @@ func getCurrentState() ([]CurrentState, error) { } return states, nil } - -// Helper functions -func getWindowList() (map[string]string, error) { - cmd := exec.Command("wmctrl", "-l") - var out bytes.Buffer - cmd.Stdout = &out - if err := cmd.Run(); err != nil { - return nil, err - } - windows := make(map[string]string) - scanner := bufio.NewScanner(&out) - for scanner.Scan() { - line := scanner.Text() - fields := strings.Fields(line) - if len(fields) > 0 { - windows[fields[0]] = strings.Join(fields[3:], " ") - } - } - return windows, nil -} - -func findNewWindow(before, after map[string]string) string { - for id := range after { - if _, ok := before[id]; !ok { - return id - } - } - return "" -} \ No newline at end of file