nc.noecho()
nc.cbreak()
+ nc.timeout(0)
nc.curs_set(0)
nc.use_default_colors()
}
handle_getch :: proc(app: ^App, buffer_len, cursor_pos: ^int) -> Maybe(int) {
- char := int(nc.wgetch(app.input_window))
+ for {
+ char := nc.wgetch(app.input_window)
- switch char {
- case nc.KEY_BACKSPACE:
- if cursor_pos^ != 0 {
- buffer_len^ -= 1
- cursor_pos^ -=1
+ if char == nc.ERR {
+ continue
}
- return nil
+ switch char {
+ case nc.KEY_BACKSPACE:
+ if cursor_pos^ != 0 {
+ buffer_len^ -= 1
+ cursor_pos^ -=1
+ }
- case nc.KEY_LEFT:
- if cursor_pos^ != 0 {
- cursor_pos^ -= 1
- }
+ return nil
- return nil
+ case nc.KEY_LEFT:
+ if cursor_pos^ != 0 {
+ cursor_pos^ -= 1
+ }
- case nc.KEY_RIGHT:
- if cursor_pos^ != buffer_len^ {
- cursor_pos^ += 1
- }
+ return nil
- return nil
+ case nc.KEY_RIGHT:
+ if cursor_pos^ != buffer_len^ {
+ cursor_pos^ += 1
+ }
+
+ return nil
- case 0o400..=0o777:
- return nil
+ case 0o400..=0o777:
+ return nil
- case:
- return char
+ case:
+ return int(char)
+ }
}
}
buffer_len = 0
cursor_pos = 0
- continue
- }
-
- buffer_len += 1
+ case:
+ buffer_len += 1
- mem.copy(
- raw_data(buffer[cursor_pos + 1 : buffer_len]),
- raw_data(buffer[cursor_pos : buffer_len - 1]),
- int(buffer_len - cursor_pos - 1)
- )
+ mem.copy(
+ raw_data(buffer[cursor_pos + 1 : buffer_len]),
+ raw_data(buffer[cursor_pos : buffer_len - 1]),
+ int(buffer_len - cursor_pos - 1)
+ )
- buffer[cursor_pos] = u8(char)
+ buffer[cursor_pos] = u8(char)
- cursor_pos += 1
+ cursor_pos += 1
+ }
}
buffer[buffer_len] = 0
case .Invalid_Profile: state_invalid_profile(app)
case .Ask_Profile: state_ask_profile(app)
case .Ask_Profile_Seed_Phrase: state_ask_profile_seed_phrase(app)
- case .Ask_Profile_Host: state_ask_profile_host(app)
+ case .Ask_Profile_Hosts: state_ask_profile_hosts(app)
case .Ask_Profile_Set_Password: state_ask_profile_set_password(app)
case .Ask_Profile_Confirm_Password: state_ask_profile_confirm_password(app)
case .Ask_Profile_Password: state_ask_profile_password(app)
case .Connect_To_Host: state_connect_to_host(app)
- case .Invalid_Host: state_invalid_host(app)
+ case .Invalid_Hosts: state_invalid_hosts(app)
case .Room_List: state_room_list(app)
case .Room: state_room(app)
Profile :: struct {
private_key: ed25519.Private_Key,
- host: string,
+ hosts: string,
rooms: [dynamic]Room,
}
Profile_Parsed :: struct {
private_key: [ed25519.PRIVATE_KEY_SIZE]u8,
- host: string,
+ hosts: string,
rooms: [dynamic]Room,
}
return {
private_key = private_key,
- host = parsed_profile.host,
+ hosts = parsed_profile.hosts,
rooms = parsed_profile.rooms,
}
}
return {
private_key = private_key,
- host = profile.host,
+ hosts = profile.hosts,
rooms = profile.rooms,
}
}
profile_deinit :: proc(profile: Profile) {
delete(profile.rooms)
- delete(profile.host)
+ delete(profile.hosts)
}
profile_load_from_name :: proc(name: string, app: ^App) -> (Profile, Maybe(Profile_Load_Error)) {
Invalid_Profile,
Ask_Profile,
Ask_Profile_Seed_Phrase,
- Ask_Profile_Host,
+ Ask_Profile_Hosts,
Ask_Profile_Set_Password,
Ask_Profile_Confirm_Password,
Ask_Profile_Password,
Connect_To_Host,
- Invalid_Host,
+ Invalid_Hosts,
Room_List,
Room,
return
}
- app_set_state(app, .Ask_Profile_Host)
+ app_set_state(app, .Ask_Profile_Hosts)
}
-state_ask_profile_host :: proc(app: ^App) {
+state_ask_profile_hosts :: proc(app: ^App) {
app_set_box_message(
app,
{
- "Enter your host.",
+ "Enter your hosts,",
+ "seperated with spaces.",
},
)
sync.mutex_lock(&app.mutex)
defer sync.mutex_unlock(&app.mutex)
- app.profile.host = input
+ app.profile.hosts = input
}
app_set_state(app, .Ask_Profile_Set_Password)
state_connect_to_host :: proc(app: ^App) {
app_set_info_bar(app, "Connecting to host..")
- host_endpoint: net.Endpoint
+ maybe_host: Maybe(net.TCP_Socket)
+ hosts := strings.clone(app.profile.hosts, context.temp_allocator)
- blk: { // Resolve host endpoint.
- ok: bool
- host_endpoint, ok = net.parse_endpoint(app.profile.host)
+ for host_address in strings.split_iterator(&hosts, " ") {
+ host_endpoint: net.Endpoint
- if ok {
- break blk
+ resolve_endpoint: {
+ ok: bool
+ host_endpoint, ok = net.parse_endpoint(host_address)
+
+ if ok {
+ break resolve_endpoint
+ }
+
+ err: net.Network_Error
+ host_endpoint, err = net.resolve_ip4(host_address)
+
+ if err == nil {
+ break resolve_endpoint
+ }
+
+ continue
}
-
- err: net.Network_Error
- host_endpoint, err = net.resolve_ip4(app.profile.host)
- if err != nil {
- log.errorf("Failed to resolve host with error: %v", err)
- app_set_info_bar(app, "Connection failed.")
- app_set_state(app, .Invalid_Host)
+ if host_endpoint.port == 0 {
+ host_endpoint.port = DEFAULT_PORT
+ }
- return
+ host, err := net.dial_tcp_from_endpoint(host_endpoint)
+
+ if err != nil {
+ continue
}
+
+ maybe_host = host
}
- if host_endpoint.port == 0 {
- host_endpoint.port = DEFAULT_PORT
- }
-
- host, err := net.dial_tcp_from_endpoint(host_endpoint)
+ host, ok := maybe_host.?
- if err != nil {
- log.errorf("Failed to connect to host with error: %v", err)
- app_set_info_bar(app, "Connection failed.")
- app_set_state(app, .Invalid_Host)
+ if !ok {
+ app_set_info_bar(app, "Connection to host failed.")
+ app_set_state(app, .Invalid_Hosts)
return
}
app_set_info_bar(app, "Connected to host.")
- {
- sync.mutex_lock(&app.mutex)
- defer sync.mutex_unlock(&app.mutex)
-
- app.host = host
- }
+ app.host = host
app_set_state(app, .Room_List)
}
-state_invalid_host :: proc(app: ^App) {
+state_invalid_hosts :: proc(app: ^App) {
app_set_box_message(
app,
{
- "Failed to connect to host.",
- "Either enter a new host,",
+ "Failed to connect to hosts.",
+ "Either enter a new hosts,",
"or :retry."
}
)
return
}
- {
- sync.mutex_lock(&app.mutex)
- defer sync.mutex_unlock(&app.mutex)
-
- delete(app.profile.host)
- app.profile.host = input
- }
+ delete(app.profile.hosts)
+ app.profile.hosts = input
profile_update(app.profile, app)