|  |  |  | @ -271,16 +271,35 @@ func get_private_key(path string, generate bool) ed25519.PrivateKey { | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | func main_loop(client *pnyx.Client, audio_data chan []int16, window ncurses.Window, packet_chan chan pnyx.Payload, user_chan chan rune) { | 
		
	
		
			
				|  |  |  |  |   max_y := ncurses.GetMaxY(window) | 
		
	
		
			
				|  |  |  |  |   max_x := ncurses.GetMaxX(window) | 
		
	
		
			
				|  |  |  |  |   titlebar := ncurses.NewWin(1, max_x, 0, 0) | 
		
	
		
			
				|  |  |  |  |   channels := ncurses.NewWin(max_y - 1, max_x / 3, 1, 0) | 
		
	
		
			
				|  |  |  |  |   body := ncurses.NewWin(max_y - 1, max_x * 2 / 3, 1, max_x / 3) | 
		
	
		
			
				|  |  |  |  | func print_decoration(window, channels, body ncurses.Window, top_left string) {  | 
		
	
		
			
				|  |  |  |  |   max_y := ncurses.GetMaxY(*ncurses.CurScr) | 
		
	
		
			
				|  |  |  |  |   max_x := ncurses.GetMaxX(*ncurses.CurScr) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   ncurses.MvWAddStr(titlebar, 0, 0, fmt.Sprintf("pnyx client %X:%X", client.Key.Public().(ed25519.PublicKey)[:2], client.Session.ID[:2])) | 
		
	
		
			
				|  |  |  |  |   ncurses.MvWAddStr(body, 0, max_x-len(client.Remote()), client.Remote()) | 
		
	
		
			
				|  |  |  |  |   ncurses.WRefresh(titlebar) | 
		
	
		
			
				|  |  |  |  |   ncurses.WResize(channels, max_y - 2, (max_x / 4) - 1) | 
		
	
		
			
				|  |  |  |  |   ncurses.WResize(body, max_y - 2, max_x - (max_x / 4) - 1) | 
		
	
		
			
				|  |  |  |  |   ncurses.MvWin(body, 2, (max_x / 4) + 1) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   ncurses.MvWAddStr(window, 0, 0, top_left) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   for i := 0; i < max_x; i++ { | 
		
	
		
			
				|  |  |  |  |     ncurses.MvWAddStr(window, 1, i, "═") | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   ncurses.MvWAddStr(window, 1, max_x/4, "╦") | 
		
	
		
			
				|  |  |  |  |   for i := 2; i < max_y; i++ { | 
		
	
		
			
				|  |  |  |  |     ncurses.MvWAddStr(window, i, max_x/4, "║") | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   ncurses.WRefresh(window) | 
		
	
		
			
				|  |  |  |  |   ncurses.WRefresh(channels) | 
		
	
		
			
				|  |  |  |  |   ncurses.WRefresh(body) | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | func main_loop(client *pnyx.Client, audio_data chan []int16, window ncurses.Window, packet_chan chan pnyx.Payload, user_chan chan []byte) { | 
		
	
		
			
				|  |  |  |  |   channels := ncurses.NewWin(0, 0, 2, 0) | 
		
	
		
			
				|  |  |  |  |   body := ncurses.NewWin(0, 0, 2, 0) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   print_decoration(window, channels, body, fmt.Sprintf("pnyx client %X:%X", client.Key.Public().(ed25519.PublicKey)[:2], client.Session.ID[:2])) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   for client.Active() { | 
		
	
		
			
				|  |  |  |  |     select { | 
		
	
	
		
			
				
					|  |  |  | @ -325,30 +344,12 @@ func main_loop(client *pnyx.Client, audio_data chan []int16, window ncurses.Wind | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |       } | 
		
	
		
			
				|  |  |  |  |     case char := <-user_chan: | 
		
	
		
			
				|  |  |  |  |       ncurses.MvWAddStr(body, 0, 0, string(char)) | 
		
	
		
			
				|  |  |  |  |       ncurses.WAddStr(body,  string(char)) | 
		
	
		
			
				|  |  |  |  |       ncurses.WRefresh(body) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |       ncurses.MvWAddStr(channels, 0, 0, string(char)) | 
		
	
		
			
				|  |  |  |  |       ncurses.WRefresh(channels) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | func bitmatch(b byte, pattern byte, length int) bool { | 
		
	
		
			
				|  |  |  |  |   mask := ^(byte(1 << (8 - length)) - 1) | 
		
	
		
			
				|  |  |  |  |   return (b ^ pattern) & mask == 0 | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | func ch_listen(client *pnyx.Client, user_chan chan rune) { | 
		
	
		
			
				|  |  |  |  |   b := [4]byte{} | 
		
	
		
			
				|  |  |  |  |   for client.Active() { | 
		
	
		
			
				|  |  |  |  |     os.Stdin.Read(b[0:1]) | 
		
	
		
			
				|  |  |  |  |     if bitmatch(b[0], 0b00000000, 1) { | 
		
	
		
			
				|  |  |  |  |       user_chan <- rune(b[0]) | 
		
	
		
			
				|  |  |  |  |     } // TODO: further utf-8 support
 | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | func process_mic(client *pnyx.Client, mic chan []byte) { | 
		
	
		
			
				|  |  |  |  |   for true { | 
		
	
		
			
				|  |  |  |  |     data := <- mic | 
		
	
	
		
			
				
					|  |  |  | @ -380,7 +381,7 @@ func main() { | 
		
	
		
			
				|  |  |  |  |   defer inDevice.Stop() | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   user_chan := make(chan rune, 1024) | 
		
	
		
			
				|  |  |  |  |   user_chan := make(chan []byte, 1024) | 
		
	
		
			
				|  |  |  |  |   packet_chan := make(chan pnyx.Payload, 1024) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   key := get_private_key(os.ExpandEnv(*key_file_arg), *generate_key_arg) | 
		
	
	
		
			
				
					|  |  |  | @ -409,15 +410,24 @@ func main() { | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   go process_mic(client, mic) | 
		
	
		
			
				|  |  |  |  |   go ch_listen(client, user_chan) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   locale := ncurses.SetLocale(0, "") | 
		
	
		
			
				|  |  |  |  |   fmt.Printf("locale: %s\n", locale) | 
		
	
		
			
				|  |  |  |  |    | 
		
	
		
			
				|  |  |  |  |   user_chan, stdin_active := ncurses.UTF8Listener(100, os.Stdin) | 
		
	
		
			
				|  |  |  |  |   window := ncurses.InitScr() | 
		
	
		
			
				|  |  |  |  |   ret := ncurses.StartColor() | 
		
	
		
			
				|  |  |  |  |   if ret != 0 { | 
		
	
		
			
				|  |  |  |  |     panic(ret) | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   go main_loop(client, audio_data, window, packet_chan, user_chan) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   os_sigs := make(chan os.Signal, 1) | 
		
	
		
			
				|  |  |  |  |   signal.Notify(os_sigs, syscall.SIGINT, syscall.SIGABRT) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   <-os_sigs | 
		
	
		
			
				|  |  |  |  |   stdin_active.Store(false) | 
		
	
		
			
				|  |  |  |  |   client.Close() | 
		
	
		
			
				|  |  |  |  |   ncurses.EndWin() | 
		
	
		
			
				|  |  |  |  | } | 
		
	
	
		
			
				
					|  |  |  | 
 |