summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-29 23:06:22 +0100
committersalmonik <salmonik@tuta.io>2026-10-29 23:06:22 +0100
commit810e212b4b255914cfa73e85b52ec23db281c0e3 (patch)
tree01e8035ce2945112af5ef41ef8959890bb0ef722 /src
parentv3.0.0: cursor-driven roulette felt with splits and corners; drop schema migr... (diff)
downloadzagan-bet-810e212b4b255914cfa73e85b52ec23db281c0e3.tar.gz
zagan-bet-810e212b4b255914cfa73e85b52ec23db281c0e3.zip
v3.1.0: warn on shrink, but let a live round finish
Diffstat (limited to '')
-rw-r--r--src/bin/client/repl.rs59
-rw-r--r--src/client/core.rs22
-rw-r--r--src/client/ui.rs10
3 files changed, 89 insertions, 2 deletions
diff --git a/src/bin/client/repl.rs b/src/bin/client/repl.rs
index 2cf121d..bf02c8d 100644
--- a/src/bin/client/repl.rs
+++ b/src/bin/client/repl.rs
@@ -17,7 +17,7 @@ use zagan_bet::client::core::{ClientCore, Effect, InteractiveCommand, Key};
17use zagan_bet::client::database; 17use zagan_bet::client::database;
18use zagan_bet::client::ui::{ 18use zagan_bet::client::ui::{
19 display_width, format_pong, host_lines, input_cursor_column, lines_to_move_up, 19 display_width, format_pong, host_lines, input_cursor_column, lines_to_move_up,
20 visual_line_count, 20 terminal_size_warning, visual_line_count, MIN_COLUMNS, MIN_ROWS,
21}; 21};
22use zagan_bet::protocol::io_failure_message; 22use zagan_bet::protocol::io_failure_message;
23use zagan_bet::roulette::{WHEEL, color as roulette_color}; 23use zagan_bet::roulette::{WHEEL, color as roulette_color};
@@ -36,6 +36,10 @@ pub struct ClientState {
36 /// Text last drawn after the `> ` prompt, used to keep the command echo 36 /// Text last drawn after the `> ` prompt, used to keep the command echo
37 /// in the transcript before printing command output. 37 /// in the transcript before printing command output.
38 prompt_text: String, 38 prompt_text: String,
39 /// Last known terminal size, updated from resize events.
40 terminal_size: (u16, u16),
41 /// Size we already warned about, so a resize does not spam the transcript.
42 size_warned: Option<(u16, u16)>,
39} 43}
40 44
41impl ClientState { 45impl ClientState {
@@ -60,6 +64,8 @@ impl ClientState {
60 cleanup_done: Arc::new(Mutex::new(false)), 64 cleanup_done: Arc::new(Mutex::new(false)),
61 game_lines: 0, 65 game_lines: 0,
62 prompt_text: String::new(), 66 prompt_text: String::new(),
67 terminal_size: crossterm::terminal::size().unwrap_or((MIN_COLUMNS, MIN_ROWS)),
68 size_warned: None,
63 } 69 }
64 } 70 }
65 71
@@ -114,7 +120,15 @@ impl ClientState {
114 } 120 }
115 } 121 }
116 } 122 }
117 Event::Resize(_, _) => self.apply_prompt(), 123 Event::Resize(columns, rows) => {
124 self.terminal_size = (columns, rows);
125 if self.core.is_in_game() {
126 let effects = self.core.refresh_board();
127 self.apply_effects(effects);
128 } else {
129 self.apply_prompt();
130 }
131 }
118 _ => {} 132 _ => {}
119 } 133 }
120 } 134 }
@@ -130,6 +144,10 @@ impl ClientState {
130 } 144 }
131 } 145 }
132 146
147 if !quit {
148 quit = self.warn_about_size();
149 }
150
133 if quit { 151 if quit {
134 break; 152 break;
135 } 153 }
@@ -292,7 +310,42 @@ impl ClientState {
292 self.prompt_text.clear(); 310 self.prompt_text.clear();
293 } 311 }
294 312
313 /// Warn once per size when the terminal is too small and no round is live.
314 /// Inside a game the warning is appended to the board instead, and during a
315 /// live round it is suppressed until the round settles.
316 fn warn_about_size(&mut self) -> bool {
317 let size = self.terminal_size;
318 let Some(message) = terminal_size_warning(size.0, size.1) else {
319 self.size_warned = None;
320 return false;
321 };
322 if self.core.is_in_game() || self.size_warned == Some(size) {
323 return false;
324 }
325 self.size_warned = Some(size);
326 self.apply_effects(vec![Effect::Print(vec![message])]);
327 false
328 }
329
295 fn draw_region(&mut self, lines: &[String]) { 330 fn draw_region(&mut self, lines: &[String]) {
331 let warning = if self.core.is_live_game() {
332 None
333 } else {
334 terminal_size_warning(self.terminal_size.0, self.terminal_size.1)
335 };
336 let owned;
337 let lines: &[String] = match warning {
338 Some(warning) => {
339 owned = {
340 let mut lines = lines.to_vec();
341 lines.push(warning);
342 lines
343 };
344 &owned
345 }
346 None => lines,
347 };
348
296 if self.game_lines == 0 { 349 if self.game_lines == 0 {
297 self.commit_prompt_line(); 350 self.commit_prompt_line();
298 } else { 351 } else {
@@ -308,6 +361,8 @@ impl ClientState {
308 .map(|line| { 361 .map(|line| {
309 if line.starts_with("===") { 362 if line.starts_with("===") {
310 line.as_str().bold().cyan().to_string() 363 line.as_str().bold().cyan().to_string()
364 } else if line.starts_with("WARNING:") {
365 line.as_str().bold().red().to_string()
311 } else if roulette { 366 } else if roulette {
312 if line.starts_with("Won ") { 367 if line.starts_with("Won ") {
313 line.as_str().bold().green().to_string() 368 line.as_str().bold().green().to_string()
diff --git a/src/client/core.rs b/src/client/core.rs
index 8411d47..95fa050 100644
--- a/src/client/core.rs
+++ b/src/client/core.rs
@@ -138,6 +138,28 @@ impl ClientCore {
138 self.game.is_in_game() 138 self.game.is_in_game()
139 } 139 }
140 140
141 /// True while a round is running and leaving would forfeit a reserved bet.
142 /// The terminal may shrink during one of these without interrupting it.
143 pub fn is_live_game(&self) -> bool {
144 match self
145 .game
146 .get_current_state()
147 .map(|state| state.game_type.as_str())
148 {
149 Some("blackjack") => self
150 .game
151 .blackjack_state()
152 .is_some_and(|hand| hand.phase == "player"),
153 Some("crash") => self.crash_started.is_some(),
154 _ => false,
155 }
156 }
157
158 /// Redraw the current board, for terminal resizes.
159 pub fn refresh_board(&self) -> Vec<Effect> {
160 self.board_effects()
161 }
162
141 pub fn prompt_effect(&self) -> Option<Effect> { 163 pub fn prompt_effect(&self) -> Option<Effect> {
142 if self.is_in_game() { 164 if self.is_in_game() {
143 return None; 165 return None;
diff --git a/src/client/ui.rs b/src/client/ui.rs
index 6161166..ca001c2 100644
--- a/src/client/ui.rs
+++ b/src/client/ui.rs
@@ -261,6 +261,16 @@ pub fn terminal_too_small(columns: u16, rows: u16) -> Option<String> {
261 )) 261 ))
262} 262}
263 263
264/// One-line warning for a terminal that shrank while the client is running.
265pub fn terminal_size_warning(columns: u16, rows: u16) -> Option<String> {
266 if columns >= MIN_COLUMNS && rows >= MIN_ROWS {
267 return None;
268 }
269 Some(format!(
270 "WARNING: terminal is {columns}x{rows}; zagan.bet needs at least {MIN_COLUMNS}x{MIN_ROWS}. Resize to fit."
271 ))
272}
273
264/// Render remembered hosts. 274/// Render remembered hosts.
265/// 275///
266/// Two addresses that present the same certificate key are the same server, so 276/// Two addresses that present the same certificate key are the same server, so