Search code examples
multithreadinggosocketsgoroutine

How can I send a event to the main loop from a Goroutine?


I am building a status bar application where the bar shows the title of the currently focused window. The bar updates every second (in an infinite loop). So window focus changes are not immediately reflected in the bar as the main loop is stuck on the Sleep function.

I am polling my window manager's (sway) IPC socket for a change in window focus in a goroutine. But how can I "inform" the main loop from the goroutine that the window title has changed?

The main loop looks like this:

func main(){
  title_queue := make(chan string)
  go poll_changes(title_queue)

  var title string = get_title()
  for {
    current_time := get_time()
    update_status_bar(title, current_time)
    
    time.Sleep(time.Duration(time.Second))
    title = <-title_queue // But sleep is blocking this
    // channel may also block the main loop now
  }
}

poll_changes looks like this:

func poll_changes(title chan string) {
    var addr string = swayipc.Getaddr()
    var conn net.Conn = swayipc.Getsock(addr)
    var events []string = []string{"window"}

    swayipc.Subscribe(conn, events) // subscribe to window change events

    var result map[string]interface{}
    for {
        response := swayipc.Unpack(conn)
        json.Unmarshal(response, &result)

        if result["change"] == "focus" {
            window, _ := result["container"].(map[string]interface{})

            title <- window["name"].(string) // how to inform the main loop of this variable change?
        }
    }
}

Note 1: swayipc is a utility library made by me.
Note 2: This is my first time using Go to build any sort of software. I built this exact thing in python previously, in which I used threading.Event. But I don't have a clue how to do it Go. If you believe that there is a fundamental issue in my approach to the solution, please point it out.


Solution

  • Use a ticker:

    tick:=time.Ticker(time.Second)
    defer tick.Stop()
    
    title:="title"
    for {
          select {
            case <-tick.T:
              current_time := get_time()
              update_status_bar(title, current_time)
            case title=<-title_queue:
          }
      }