Better loop/socket management.
This commit is contained in:
parent
c3a504fcbf
commit
d2899385a8
82
main.go
82
main.go
@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -13,10 +14,16 @@ var (
|
||||
sessionSubscription I3Session
|
||||
workspaces []I3Workspace
|
||||
outputIndices map[string]int
|
||||
subscriptions []string
|
||||
)
|
||||
|
||||
func init() {
|
||||
outputIndices = make(map[string]int)
|
||||
subscriptions = []string{
|
||||
"binding",
|
||||
"shutdown",
|
||||
}
|
||||
|
||||
workspaces = []I3Workspace{
|
||||
{ Num: 0, Name: "Development" },
|
||||
{ Num: 1, Name: "Terminal" },
|
||||
@ -34,23 +41,7 @@ func init() {
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
// session is used for interactively communicating with i3,
|
||||
// like getting workspace and output data.
|
||||
if session, err = NewI3Session(); err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
// sessionSubscription is used for listening to subscribed events
|
||||
// from i3.
|
||||
if sessionSubscription, err = NewI3Session(); err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
sessionSubscription.Subscribe([]string{"binding", "output"})
|
||||
defer sessionSubscription.Close()
|
||||
go sessionSubscription.Loop()
|
||||
go SessionLoop()
|
||||
|
||||
// Socket server reading commands and acting upon them
|
||||
var listener net.Listener
|
||||
@ -60,13 +51,6 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Find outputs and assign an index to them
|
||||
err = session.UpdateOutputIndices()
|
||||
if err != nil {
|
||||
fmt.Printf("Output error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Listen for external commands
|
||||
var conn net.Conn
|
||||
var n int
|
||||
@ -82,13 +66,61 @@ func main() {
|
||||
}
|
||||
|
||||
cmd := strings.TrimSpace(string(buf[:n]))
|
||||
if cmd == "fix workspaces" {
|
||||
switch(cmd) {
|
||||
case "fix workspaces":
|
||||
err = session.FixWorkspaces()
|
||||
if err != nil {
|
||||
fmt.Printf("Fix workspaces: %s\n", err)
|
||||
}
|
||||
|
||||
case "outputs":
|
||||
res, err := session.Socket.Request(GET_OUTPUTS, "")
|
||||
if err != nil {
|
||||
fmt.Printf("test error: %s\n", err)
|
||||
}
|
||||
fmt.Printf("test: %s\n", res)
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func SessionLoop() {
|
||||
var err error
|
||||
for {
|
||||
fmt.Printf("-------- SessionLoop --------\n")
|
||||
|
||||
// session is used for interactively communicating with i3,
|
||||
// like getting workspace and output data.
|
||||
if err = session.Open(); err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// sessionSubscription is used for listening to subscribed
|
||||
// events from i3.
|
||||
if err = sessionSubscription.Open(); err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// i3-session-manager reacts to keybindings and i3 restarts.
|
||||
sessionSubscription.Subscribe(subscriptions)
|
||||
|
||||
// Find outputs and assign an index to them
|
||||
err = session.UpdateOutputIndices()
|
||||
if err != nil {
|
||||
fmt.Printf("Output error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
||||
/* Loop will return on socket errors, for example when an i3
|
||||
* restart occurs. The socket will not be immediately available
|
||||
* then. Some connection errors can thus occur until i3 has
|
||||
* initialized the socket again. */
|
||||
err = sessionSubscription.Loop()
|
||||
fmt.Printf("Loop error: %s\n", err)
|
||||
time.Sleep(time.Millisecond*100)
|
||||
}
|
||||
}
|
||||
|
205
session.go
205
session.go
@ -4,20 +4,18 @@ import (
|
||||
// Standard
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewI3Session finds the i3 socket path and connects to it.
|
||||
func NewI3Session() (session I3Session, err error) {
|
||||
func (sess *I3Session) Open() (err error) {
|
||||
// Find path to i3 socket
|
||||
i3SocketPathCmd := exec.Command("i3", "--get-socketpath")
|
||||
i3SocketPathBytes, _ := i3SocketPathCmd.CombinedOutput()
|
||||
i3SocketPath := strings.TrimSpace(string(i3SocketPathBytes))
|
||||
session.Socket, err = NewI3Socket(i3SocketPath)
|
||||
sess.Socket, err = NewI3Socket(i3SocketPath)
|
||||
return
|
||||
}
|
||||
|
||||
@ -112,98 +110,6 @@ func (sess I3Session) Config() (config string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (sess I3Session) Loop() {
|
||||
var msg []byte
|
||||
var err error
|
||||
var cmdBinding I3EventBinding
|
||||
|
||||
cmdOutput := regexp.MustCompile(`(?i)^\s*nop\s*output\s*(.*?)\s*$`)
|
||||
cmdWS := regexp.MustCompile(`(?i)^\s*nop\s*workspace\s*(\d+)\s*$`)
|
||||
cmdSession := regexp.MustCompile(`(?i)^\s*nop\s*manage\s+session`)
|
||||
cmdMarkTag := regexp.MustCompile(`(?i)^\s*nop\s*mark\s+tag`)
|
||||
cmdMarkMove := regexp.MustCompile(`(?i)^\s*nop\s*mark\s+move`)
|
||||
cmdMarkClear := regexp.MustCompile(`(?i)^\s*nop\s*mark\s+clear`)
|
||||
|
||||
for {
|
||||
msg, err = I3ReadMessage(sess.Socket)
|
||||
if err != nil {
|
||||
fmt.Printf("Loop error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cmdBinding = I3EventBinding{}
|
||||
err = json.Unmarshal(msg, &cmdBinding)
|
||||
if err != nil {
|
||||
fmt.Printf(
|
||||
"Loop JSON parse: %s\n\nMessage:\n(%s)\n",
|
||||
err,
|
||||
msg,
|
||||
)
|
||||
}
|
||||
binding := cmdBinding.Binding
|
||||
|
||||
// Switch output
|
||||
m := cmdOutput.FindAllStringSubmatch(binding.Command, 1)
|
||||
if len(m) == 1 && len(m[0]) == 2 {
|
||||
err = sess.SwitchOutput(m[0][1])
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Switch output
|
||||
m = cmdWS.FindAllStringSubmatch(binding.Command, 1)
|
||||
if len(m) == 1 && len(m[0]) == 2 {
|
||||
i, _ := strconv.Atoi(m[0][1])
|
||||
err = sess.SwitchWorkspace(i)
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Manage session
|
||||
if cmdSession.Match([]byte(binding.Command)) {
|
||||
err = sess.ManageSession()
|
||||
if err != nil {
|
||||
fmt.Printf("Manage session: %s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Mark current window with move tag
|
||||
if cmdMarkTag.Match([]byte(binding.Command)) {
|
||||
err = sess.MarkTag()
|
||||
if err != nil {
|
||||
fmt.Printf("Mark tag: %s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove all move tags
|
||||
if cmdMarkClear.Match([]byte(binding.Command)) {
|
||||
err = sess.MarkClear()
|
||||
if err != nil {
|
||||
fmt.Printf("Mark clear: %s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Move all windows with move tag
|
||||
if cmdMarkMove.Match([]byte(binding.Command)) {
|
||||
err = sess.MarkMove()
|
||||
if err != nil {
|
||||
fmt.Printf("Mark move: %s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", msg)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// SwitchWorkspace switches to the given workspace index on the same output
|
||||
// that has the workspace that is currently focused.
|
||||
func (sess I3Session) SwitchWorkspace(workspaceIndex int) error {
|
||||
@ -270,7 +176,7 @@ func (sess I3Session) SwitchOutput(output string) error {
|
||||
}
|
||||
|
||||
// ManageSession runs dmenu and asks for reload, restart or exit.
|
||||
func (sess I3Session) ManageSession() error {
|
||||
func (sess *I3Session) ManageSession() error {
|
||||
cmd := exec.Command(
|
||||
"sh", "-c",
|
||||
"echo \"reload\nrestart\nexit\" | dmenu -l 3 -i",
|
||||
@ -283,11 +189,14 @@ func (sess I3Session) ManageSession() error {
|
||||
|
||||
switch(choice) {
|
||||
case "reload", "restart", "exit":
|
||||
res, err := sess.Socket.Request(RUN_COMMAND, choice)
|
||||
_, err = sess.Socket.Request(RUN_COMMAND, choice)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Management %s: %s\n", choice, res)
|
||||
|
||||
if choice == "restart" {
|
||||
return fmt.Errorf("restarted")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Unknown management command")
|
||||
}
|
||||
@ -439,4 +348,100 @@ func (sess I3Session) FixWorkspaces() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// vim: foldmethod=marker
|
||||
// Loop reads the subscription socket and reacts to the implemented messages.
|
||||
func (sess *I3Session) Loop() error {
|
||||
var msg []byte
|
||||
var err error
|
||||
var cmdBinding I3EventBinding
|
||||
|
||||
cmdOutput := regexp.MustCompile(`(?i)^\s*nop\s*output\s*(.*?)\s*$`)
|
||||
cmdWS := regexp.MustCompile(`(?i)^\s*nop\s*workspace\s*(\d+)\s*$`)
|
||||
cmdSession := regexp.MustCompile(`(?i)^\s*nop\s*manage\s+session`)
|
||||
cmdMarkTag := regexp.MustCompile(`(?i)^\s*nop\s*mark\s+tag`)
|
||||
cmdMarkMove := regexp.MustCompile(`(?i)^\s*nop\s*mark\s+move`)
|
||||
cmdMarkClear := regexp.MustCompile(`(?i)^\s*nop\s*mark\s+clear`)
|
||||
|
||||
for {
|
||||
msg, err = I3ReadMessage(sess.Socket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmdBinding = I3EventBinding{}
|
||||
err = json.Unmarshal(msg, &cmdBinding)
|
||||
if err != nil {
|
||||
fmt.Printf(
|
||||
"\nLoop JSON parse: %s\nMessage:\n(%s)\n\n",
|
||||
err,
|
||||
msg,
|
||||
)
|
||||
}
|
||||
binding := cmdBinding.Binding
|
||||
|
||||
// Process is restarted
|
||||
if cmdBinding.Change == "restart" {
|
||||
return fmt.Errorf("restarted")
|
||||
}
|
||||
|
||||
// Switch output
|
||||
m := cmdOutput.FindAllStringSubmatch(binding.Command, 1)
|
||||
if len(m) == 1 && len(m[0]) == 2 {
|
||||
err = sess.SwitchOutput(m[0][1])
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Switch output
|
||||
m = cmdWS.FindAllStringSubmatch(binding.Command, 1)
|
||||
if len(m) == 1 && len(m[0]) == 2 {
|
||||
i, _ := strconv.Atoi(m[0][1])
|
||||
err = sess.SwitchWorkspace(i)
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Manage session
|
||||
if cmdSession.Match([]byte(binding.Command)) {
|
||||
err = sess.ManageSession()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Mark current window with move tag
|
||||
if cmdMarkTag.Match([]byte(binding.Command)) {
|
||||
err = sess.MarkTag()
|
||||
if err != nil {
|
||||
fmt.Printf("Mark tag: %s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove all move tags
|
||||
if cmdMarkClear.Match([]byte(binding.Command)) {
|
||||
err = sess.MarkClear()
|
||||
if err != nil {
|
||||
fmt.Printf("Mark clear: %s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Move all windows with move tag
|
||||
if cmdMarkMove.Match([]byte(binding.Command)) {
|
||||
err = sess.MarkMove()
|
||||
if err != nil {
|
||||
fmt.Printf("Mark move: %s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
//fmt.Printf("%s\n", msg)
|
||||
}
|
||||
}
|
||||
|
||||
// vim: foldmethod=syntax foldnestmax=1
|
||||
|
@ -26,13 +26,18 @@ func NewI3Socket(filename string) (I3Socket, error) {
|
||||
var i3Socket I3Socket
|
||||
var err error
|
||||
|
||||
i3Socket.conn, err = net.Dial("unix", filename)
|
||||
i3Socket.filename = filename
|
||||
err = i3Socket.Open()
|
||||
if err != nil {
|
||||
return i3Socket, err
|
||||
}
|
||||
|
||||
return i3Socket, nil
|
||||
}
|
||||
func (sock *I3Socket) Open() (err error) {
|
||||
sock.conn, err = net.Dial("unix", sock.filename)
|
||||
return
|
||||
}
|
||||
|
||||
func (sock I3Socket) Close() error {
|
||||
return sock.conn.Close()
|
||||
|
Loading…
Reference in New Issue
Block a user