feat: update xstartplacement to launch and place terminals
This commit is contained in:
parent
71c3ff6642
commit
e37836bb61
204
showAll.go
204
showAll.go
|
@ -2,47 +2,201 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// WindowConfig holds the configuration for a single terminal window.
|
||||||
|
type WindowConfig struct {
|
||||||
|
Title string
|
||||||
|
Path string
|
||||||
|
Geometry string // In WIDTHxHEIGHT+X+Y format
|
||||||
|
Workspace string
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cmd := exec.Command("wmctrl", "-lG")
|
// 1. Get current working directory.
|
||||||
stdout, err := cmd.StdoutPipe()
|
pwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Failed to get stdout pipe:", err)
|
fmt.Println("Failed to get current directory:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
// 2. Read and parse the configuration file.
|
||||||
fmt.Println("Failed to start wmctrl:", err)
|
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)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
scanner := bufio.NewScanner(stdout)
|
// 3. Find the best matching configuration for the current directory.
|
||||||
for scanner.Scan() {
|
var bestMatch *WindowConfig
|
||||||
line := scanner.Text()
|
longestPrefix := 0
|
||||||
if strings.Contains(line, "jcarr@framebook") {
|
for i, config := range configs {
|
||||||
fields := strings.Fields(line)
|
if strings.HasPrefix(pwd, config.Path) {
|
||||||
if len(fields) >= 8 {
|
if len(config.Path) > longestPrefix {
|
||||||
workspace := fields[1]
|
longestPrefix = len(config.Path)
|
||||||
x := fields[2]
|
bestMatch = &configs[i]
|
||||||
y := fields[3]
|
|
||||||
width := fields[4]
|
|
||||||
height := fields[5]
|
|
||||||
title := strings.Join(fields[7:], " ")
|
|
||||||
|
|
||||||
fmt.Printf("Terminal Window Found:\n")
|
|
||||||
fmt.Printf(" Title: %s\n", title)
|
|
||||||
fmt.Printf(" Geometry: X=%s, Y=%s, Width=%s, Height=%s\n", x, y, width, height)
|
|
||||||
fmt.Printf(" Workspace: %s\n", workspace)
|
|
||||||
fmt.Println("---")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cmd.Wait(); err != nil {
|
if bestMatch == nil {
|
||||||
fmt.Println("wmctrl command failed:", err)
|
fmt.Printf("No configuration found for directory: %s\n", pwd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
targetConfig := bestMatch
|
||||||
|
fmt.Printf("Found matching configuration for path: %s\n", targetConfig.Path)
|
||||||
|
|
||||||
|
// 4. Get the list of windows before launching the new terminal.
|
||||||
|
windowsBefore, err := getWindowList()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to get initial window list:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Launch mate-terminal.
|
||||||
|
geomString := targetConfig.Geometry
|
||||||
|
cmd := exec.Command("mate-terminal", "--geometry", geomString)
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
fmt.Println("Failed to start mate-terminal:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("Launched mate-terminal with geometry %s\n", geomString)
|
||||||
|
|
||||||
|
// 6. Find the new window by comparing the window lists.
|
||||||
|
var newWindowID string
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
windowsAfter, err := getWindowList()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to get updated window list:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newWindowID = findNewWindow(windowsBefore, windowsAfter)
|
||||||
|
if newWindowID != "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if newWindowID == "" {
|
||||||
|
fmt.Println("Could not find the new terminal window.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("Found new window with ID: %s\n", newWindowID)
|
||||||
|
|
||||||
|
// 7. Move the window to the correct workspace.
|
||||||
|
cmd = exec.Command("wmctrl", "-i", "-r", newWindowID, "-t", targetConfig.Workspace)
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
fmt.Println("Failed to move window to workspace:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Moved window to workspace %s\n", targetConfig.Workspace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Set the final window title.
|
||||||
|
finalTitle := fmt.Sprintf("jcarr@framebook: %s", pwd)
|
||||||
|
cmd = exec.Command("wmctrl", "-i", "-r", newWindowID, "-T", finalTitle)
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
fmt.Println("Failed to set final window title:", err)
|
||||||
|
} else {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
121
stuff.go
121
stuff.go
|
@ -1,121 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/BurntSushi/xgb"
|
|
||||||
"github.com/BurntSushi/xgb/xproto"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
conn, err := xgb.NewConn()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to connect to X server:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Start the terminal (replace with your app)
|
|
||||||
go func() {
|
|
||||||
if err := exec.Command("mate-terminal", "--title", "Workspace1-Terminal").Start(); err != nil {
|
|
||||||
fmt.Println("Error starting terminal:", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Wait for the window to appear
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Get the root window
|
|
||||||
setup := xproto.Setup(conn)
|
|
||||||
root := setup.DefaultScreen(conn).Root
|
|
||||||
|
|
||||||
// List children windows
|
|
||||||
reply, err := xproto.QueryTree(conn, root).Reply()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to query windows:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the window with the specified title
|
|
||||||
var target xproto.Window
|
|
||||||
for _, child := range reply.Children {
|
|
||||||
// fmt.Printf("child: %+v\n", child)
|
|
||||||
/*
|
|
||||||
// Get the atom for _NET_WM_NAME
|
|
||||||
atomReply, err := xproto.InternAtom(conn, true, uint16(len("_NET_WM_NAME")), "_NET_WM_NAME").Reply()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to intern atom _NET_WM_NAME: %v", err)
|
|
||||||
}
|
|
||||||
netWmNameAtom := atomReply.Atom // Correct field to use
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Get the property for _NET_WM_NAME
|
|
||||||
nameReply, err := xproto.GetProperty(conn, false, child, netWmNameAtom, xproto.AtomString, 0, (1<<32)-1).Reply()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to get property _NET_WM_NAME: %v", err)
|
|
||||||
} else if len(nameReply.Value) > 0 {
|
|
||||||
fmt.Printf("Window name: %s\n", string(nameReply.Value))
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Get the atom for _NET_WM_NAME
|
|
||||||
atomReply, err := xproto.InternAtom(conn, true, uint16(len("_NET_WM_NAME")), "_NET_WM_NAME").Reply()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to intern atom _NET_WM_NAME: %v", err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("found atomic name: %s\n", string(atomReply.Value))
|
|
||||||
}
|
|
||||||
netWmNameAtom := atomReply.Atom
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Get the property for _NET_WM_NAME
|
|
||||||
nameReply, err := xproto.GetProperty(conn, false, child, netWmNameAtom, xproto.AtomString, 0, (1<<32)-1).Reply()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to get property _NET_WM_NAME: %v", err)
|
|
||||||
} else if len(nameReply.Value) > 0 {
|
|
||||||
fmt.Printf("Window name: %s\n", string(nameReply.Value))
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
geomReply, err := xproto.GetGeometry(conn, xproto.Drawable(child)).Reply()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("err: %+v\n", err)
|
|
||||||
// fmt.Printf("child geomReply: %+v\n", geomReply)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("child geomReply: %+v\n", geomReply)
|
|
||||||
}
|
|
||||||
|
|
||||||
nameReply, err := xproto.GetProperty(conn, false, child, xproto.AtomWmName, xproto.AtomString, 0, (1<<32)-1).Reply()
|
|
||||||
if err != nil {
|
|
||||||
// fmt.Printf("child err: %+v\n", err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("child %+v nameReply: %+v %s\n", reflect.TypeOf(child), nameReply, string(nameReply.Value))
|
|
||||||
}
|
|
||||||
if err != nil || len(nameReply.Value) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
name := string(nameReply.Value)
|
|
||||||
if name == "Terminal" {
|
|
||||||
target = child
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if target == 0 {
|
|
||||||
fmt.Println("Window not found.")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the window to workspace 1 and set its geometry
|
|
||||||
xproto.ConfigureWindow(conn, target, xproto.ConfigWindowX|xproto.ConfigWindowY|xproto.ConfigWindowWidth|xproto.ConfigWindowHeight,
|
|
||||||
[]uint32{100, 100, 800, 600})
|
|
||||||
fmt.Println("Window moved and resized.")
|
|
||||||
}
|
|
Loading…
Reference in New Issue