@@ 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{}
+}