3 files changed, 181 insertions(+), 0 deletions(-)

A => go.mod
A => go.sum
A => main.go
A => go.mod +5 -0
@@ 0,0 1,5 @@ 
+module hg.sr.ht/~ser/i3quake
+
+go 1.14
+
+require github.com/xxxserxxx/i3ipc-go v0.0.0-20200401203636-25cff3fb5bf2

          
A => go.sum +2 -0
@@ 0,0 1,2 @@ 
+github.com/xxxserxxx/i3ipc-go v0.0.0-20200401203636-25cff3fb5bf2 h1:7EF029nNcxDXO0ki15KRp/PHKnFEkdrOSBGR1vgvQdI=
+github.com/xxxserxxx/i3ipc-go v0.0.0-20200401203636-25cff3fb5bf2/go.mod h1:mkE9djHoON2xsUBGcuXBzC95l6573bYcLc5X61eOfEU=

          
A => main.go +174 -0
@@ 0,0 1,174 @@ 
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"os"
+	"strings"
+	"sync"
+
+	"github.com/xxxserxxx/i3ipc-go"
+)
+
+const (
+	NAME = "i3quake"
+)
+
+const (
+	TOP = iota
+	BOTTOM
+	LEFT
+	RIGHT
+)
+
+type con struct {
+	*i3ipc.IPCSocket
+}
+
+func main() {
+	pos := flag.String("p", "top", "Position: top, bottom, left, right (top)")
+	ratio := flag.Float64("H", 0.25, "% height of window, 0-1 (0.25)")
+	cmd := flag.String("t", "/usr/bin/st", "Terminal program")
+	flag.Parse()
+	var p int
+	switch strings.ToLower(*pos) {
+	default:
+		panic("unknown position")
+	case "top":
+		p = TOP
+	case "bottom":
+		p = BOTTOM
+	case "left":
+		p = LEFT
+	case "right":
+		p = RIGHT
+	}
+	c, e := i3ipc.GetIPCSocket()
+	if e != nil {
+		panic(e)
+	}
+	cx := con{c}
+	cx.toggleQuake(p, *ratio, *cmd)
+}
+
+func (c con) toggleQuake(pos int, ratio float64, cmd string) {
+	t, e := c.GetTree()
+	if e != nil {
+		panic(e)
+	}
+	w := t.FindMarked(NAME)
+	// QT hasn't been started yet, so fire one off
+	if w == nil {
+		if cmd == "" {
+			cmd = "st"
+		}
+		i3ipc.StartEventListener()
+		var proc *os.Process
+		evts, err := i3ipc.Subscribe(i3ipc.I3WindowEvent)
+		if err != nil {
+			panic(err)
+		}
+		w := sync.WaitGroup{}
+		w.Add(1)
+		go func() {
+			w.Done()
+			for e := range evts {
+				d := e.Details
+				we, ok := d.(i3ipc.WindowEvent)
+				if ok {
+					switch we.Change {
+					case "new":
+						cmd := fmt.Sprintf("[con_id=%d], mark %s", we.Container.ID, NAME)
+						_, err := c.Command(cmd)
+						if err != nil {
+							panic(err)
+						}
+						c.position(pos, ratio)
+						proc.Release()
+						w.Done()
+						return
+					default:
+						log.Printf("change = %s\n", we.Change)
+					}
+				}
+			}
+		}()
+		w.Wait()
+		w.Add(1)
+		proc, err = os.StartProcess(cmd, nil, &os.ProcAttr{
+			Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
+		})
+		if err != nil {
+			panic(err)
+		}
+		w.Wait()
+	} else {
+		cmd := fmt.Sprintf(`[con_mark="%s"], scratchpad show`, NAME)
+		suc, _ := c.Command(cmd)
+		if !suc {
+			log.Printf("ERROR %s", cmd)
+		}
+	}
+}
+
+func (c con) position(pos int, ratio float64) {
+	ws := c.getCurrentWorkspace()
+	wheight := int64(float64(ws.Rect.Height) * ratio)
+	wwidth := int64(float64(ws.Rect.Width) * ratio)
+	var width, height, posx, posy int64
+	switch pos {
+	default:
+		panic("unsupported position")
+	case TOP:
+		posx = ws.Rect.X
+		posy = ws.Rect.Y
+		height = wheight
+		width = ws.Rect.Width
+	case BOTTOM:
+		posx = ws.Rect.X
+		posy = ws.Rect.Y + ws.Rect.Height - wheight
+		height = wheight
+		width = ws.Rect.Width
+	case LEFT:
+		posx = ws.Rect.X
+		posy = ws.Rect.Y
+		height = ws.Rect.Height
+		width = wwidth
+	case RIGHT:
+		posx = ws.Rect.X + ws.Rect.Width - wwidth
+		posy = ws.Rect.Y
+		height = ws.Rect.Height
+		width = wwidth
+	}
+	cmd := fmt.Sprintf(`[con_mark="%s"], 
+			resize set %d px %d px,
+			move absolute position %dpx %dpx,
+			move scratchpad,
+			scratchpad show`, NAME, width, height, posx, posy)
+	suc := false
+	var retries int
+	var err error
+	for suc == false {
+		suc, err = c.Command(cmd)
+		if retries > 10 {
+			if err != nil {
+				panic(err)
+			}
+		}
+		retries++
+	}
+}
+
+func (c con) getCurrentWorkspace() i3ipc.Workspace {
+	ws, err := c.GetWorkspaces()
+	if err != nil {
+		panic(err)
+	}
+	for _, w := range ws {
+		if w.Focused {
+			return w
+		}
+	}
+	return i3ipc.Workspace{}
+}