import "core:mem"
import "core:net"
import "core:os"
+import "core:slice"
import "core:strings"
import "core:sync"
import "core:thread"
info_bar: ^nc.Window,
input_window: ^nc.Window,
- room_list_window: ^nc.Window,
- room_window: ^nc.Window,
+ room_list_window: Maybe(^nc.Window),
+ room_window: Maybe(^nc.Window),
info_bar_content: string,
delete(app.info_bar_content)
}
- if app.state == .Main {
- nc.delwin(app.room_window)
- nc.delwin(app.room_list_window)
+ if room_window, ok := app.room_window.?; ok {
+ nc.delwin(room_window)
+ }
+
+ if room_list_window, ok := app.room_list_window.?; ok {
+ nc.delwin(room_list_window)
}
nc.delwin(app.input_window)
}
app_update_room_list_window :: proc(app: ^App) {
- if app.state != .Main {
+ room_list_window, ok := app.room_list_window.?
+
+ if !ok {
return
}
- sync.mutex_lock(&app.mutex)
- defer sync.mutex_unlock(&app.mutex)
-
- nc.wclear(app.room_list_window)
+ nc.wclear(room_list_window)
c_room_name_buffer: [ROOM_NAME_MAX_SIZE + 1]u8
c_room_name := strings.unsafe_string_to_cstring(string(c_room_name_buffer[:]))
- nc.wattron(app.room_list_window, room_color)
- nc.mvwprintw(app.room_list_window, i32(i + 1), 1, c_room_name)
- nc.wattroff(app.room_list_window, room_color)
+ nc.wattron(room_list_window, room_color)
+ nc.mvwprintw(room_list_window, i32(i + 1), 1, c_room_name)
+ nc.wattroff(room_list_window, room_color)
}
color := nc.COLOR_PAIR(3)
- nc.wattron(app.room_list_window, color)
- nc.box(app.room_list_window, 0, 0)
- nc.wattroff(app.room_list_window, color)
+ if app.state == .Room_List {
+ color = nc.COLOR_PAIR(2)
+ }
+
+ nc.wattron(room_list_window, color)
+ nc.box(room_list_window, 0, 0)
+ nc.wattroff(room_list_window, color)
+
+ nc.wrefresh(room_list_window)
+}
+
+app_update_room_window :: proc(app: ^App) {
+ room_window, ok := app.room_window.?
+
+ if !ok {
+ return
+ }
+
+ color := nc.COLOR_PAIR(3)
+
+ if app.state == .Room {
+ color = nc.COLOR_PAIR(2)
+ }
+
+ nc.wattron(room_window, color)
+ nc.box(room_window, 0, 0)
+ nc.wattroff(room_window, color)
- nc.wrefresh(app.room_list_window)
+ nc.wrefresh(room_window)
}
app_set_state :: proc(app: ^App, state: State) {
app_update_info_bar(app)
screen_height, screen_width := nc.getmaxyx(app.screen)
-
- old_state := app.state
-
- {
- sync.mutex_lock(&app.mutex)
- defer sync.mutex_unlock(&app.mutex)
-
- app.state = state
- }
-
color := nc.COLOR_PAIR(3)
- if old_state != .Main && state == .Main {
- app.room_list_window = nc.newwin(screen_height - 2, 34, 0, 0)
- app.room_window = nc.newwin(screen_height - 2, screen_width - 34, 0, 34)
-
- app_update_room_list_window(app)
+ app.state = state
- nc.wattron(app.room_window, color)
- nc.box(app.room_window, 0, 0)
- nc.wattroff(app.room_window, color)
+ if app.state == .Room_List || app.state == .Room {
+ if app.room_list_window == nil {
+ app.room_list_window = nc.newwin(screen_height - 2, 34, 0, 0)
+ }
- nc.wrefresh(app.room_window)
- } else if old_state == .Main && state != .Main {
- nc.wclear(app.room_list_window)
- nc.wclear(app.room_window)
+ if app.room_window == nil {
+ app.room_window = nc.newwin(screen_height - 2, screen_width - 34, 0, 34)
+ }
+ } else {
+ if room_list_window, ok := app.room_list_window.?; ok {
+ nc.wclear(room_list_window)
+ nc.wrefresh(room_list_window)
+ nc.delwin(room_list_window)
+ }
- nc.wrefresh(app.room_list_window)
- nc.wrefresh(app.room_window)
+ if room_window, ok := app.room_window.?; ok {
+ nc.wclear(room_window)
+ nc.wrefresh(room_window)
+ nc.delwin(room_window)
+ }
+ }
- nc.delwin(app.room_list_window)
- nc.delwin(app.room_window)
+ if app.state == .Room_List || app.state == .Room {
+ app_update_room_list_window(app)
+ app_update_room_window(app)
}
}
}
}
-handle_getch :: proc(app: ^App, buffer_len, cursor_pos: ^int) -> (Maybe(int), bool) {
+handle_getch :: proc(app: ^App, buffer_len, cursor_pos: ^int) -> Maybe(int) {
char := int(nc.wgetch(app.input_window))
switch char {
- case '\n':
- return nil, false
-
case nc.KEY_BACKSPACE:
if cursor_pos^ != 0 {
buffer_len^ -= 1
cursor_pos^ -=1
}
- return nil, true
+ return nil
case nc.KEY_LEFT:
if cursor_pos^ != 0 {
cursor_pos^ -= 1
}
- return nil, true
+ return nil
case nc.KEY_RIGHT:
if cursor_pos^ != buffer_len^ {
cursor_pos^ += 1
}
- return nil, true
+ return nil
case 0o400..=0o777:
- return nil, true
+ return nil
case:
- return char, true
+ return char
}
}
nc.curs_set(1)
- for {
- maybe_char, keep_going := handle_getch(app, &buffer_len, &cursor_pos)
-
- if !keep_going {
- break
- }
-
- char, ok := maybe_char.?
+ input_loop: for {
+ char, ok := handle_getch(app, &buffer_len, &cursor_pos).?
- if !ok {
- continue
- }
+ if ok {
+ switch char {
+ case '\n':
+ break input_loop
- if char == 27 {
- buffer_len = 0
- cursor_pos = 0
+ case 27:
+ buffer_len = 0
+ cursor_pos = 0
- continue
- }
+ continue
+ }
- buffer_len += 1
+ 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
output := string(buffer[:buffer_len + 1])
escape,
}
-Command_Or_Event :: union {
+Input_Or_Event :: union {
string,
Event,
}
-app_get_command_or_event :: proc(app: ^App, allocator := context.allocator) -> Command_Or_Event {
+app_get_input_or_event :: proc(
+ app: ^App,
+ allowed_events: []Event,
+ only_command := false,
+ allocator := context.allocator,
+) -> Input_Or_Event {
context.allocator = allocator
for {
nc.curs_set(1)
- for {
- maybe_char, keep_going := handle_getch(app, &buffer_len, &cursor_pos)
-
- if !keep_going {
- break
- }
-
- char, ok := maybe_char.?
+ input_loop: for {
+ char, ok := handle_getch(app, &buffer_len, &cursor_pos).?
if ok {
if buffer_len == 0 {
- switch char {
- case 'k': return Event.up
- case 'j': return Event.down
- case '\n': return Event.select
- case 27: return Event.escape
+ switch {
+ case char == 'k' && slice.contains(allowed_events, Event.up):
+ return Event.up
+
+ case char == 'j' && slice.contains(allowed_events, Event.down):
+ return Event.down
+
+ case char == '\n' && slice.contains(allowed_events, Event.select):
+ return Event.select
+
+ case char == 27 && slice.contains(allowed_events, Event.escape):
+ return Event.escape
}
}
- if char == 27 {
+ switch char {
+ case '\n':
+ break input_loop
+
+ case 27:
buffer_len = 0
cursor_pos = 0
- continue
- }
-
- if (buffer_len == 0 && char != ':') && buffer[0] != ':' {
- continue
- }
+ case:
+ if only_command && (buffer_len == 0 && char != ':') && buffer[0] != ':' {
+ continue
+ }
- buffer_len += 1
+ 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 .Connect_To_Host: state_connect_to_host(app)
case .Invalid_Host: state_invalid_host(app)
- case .Main: state_main(app)
+ case .Room_List: state_room_list(app)
+ case .Room: state_room(app)
}
}
}
Connect_To_Host,
Invalid_Host,
- Main,
+ Room_List,
+ Room,
}
state_load_config :: proc(app: ^App) {
app.host = host
}
- app_set_state(app, .Main)
+ app_set_state(app, .Room_List)
}
state_invalid_host :: proc(app: ^App) {
app_set_state(app, .Connect_To_Host)
}
-state_main :: proc(app: ^App) {
- command_or_event := app_get_command_or_event(app)
-
- defer {
- #partial switch thing in command_or_event {
- case string: delete(thing)
- }
- }
+state_room_list :: proc(app: ^App) {
+ input_or_event := app_get_input_or_event(
+ app,
+ { .up, .down, .select },
+ true,
+ context.temp_allocator,
+ )
- switch thing in command_or_event{
+ switch thing in input_or_event {
case string:
- command := thing
+ input := thing
- if err, ok := handle_command(app, command).?; ok {
+ if err, ok := handle_command(app, input).?; ok {
if err != .Not_A_Command {
return
}
case Event:
event := thing
- switch event {
+ #partial switch event {
case Event.up:
if app.selected_room != 0 {
app.selected_room -= 1
}
case Event.select:
+ app_set_state(app, .Room)
+ }
+ }
+}
+
+state_room :: proc(app: ^App) {
+ input_or_event := app_get_input_or_event(
+ app,
+ { .escape },
+ false,
+ context.temp_allocator,
+ )
+
+ switch thing in input_or_event {
+ case string:
+ input := thing
+
+ if err, ok := handle_command(app, input).?; ok {
+ if err != .Not_A_Command {
+ return
+ }
+ } else {
+ return
+ }
+
+ case Event:
+ event := thing
+
+ #partial switch event {
case Event.escape:
+ app_set_state(app, .Room_List)
}
}
}