1use anyhow::{Result, anyhow, bail};
2use hashbrown::HashMap;
3use serde::{Deserialize, Serialize};
4
5use crate::accessibility::reduce_motion_preference;
6use crate::status_line::StatusLineConfig;
7use crate::terminal_title::TerminalTitleConfig;
8use vtcode_commons::ui_protocol::AppIconPreference;
9use vtcode_commons::ui_protocol::DiffPreviewMode;
10
11mod modes;
12
13pub use modes::{
14 LayoutModeOverride, NotificationBackend, NotificationDeliveryMode, ReasoningDisplayMode, ToolDisplayMode,
15 ToolOutputMode, UiDisplayMode,
16};
17
18#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
20#[derive(Debug, Clone, Deserialize, Serialize)]
21pub struct UiNotificationsConfig {
22 #[serde(default = "default_notifications_enabled")]
24 pub enabled: bool,
25
26 #[serde(default)]
28 pub delivery_mode: NotificationDeliveryMode,
29
30 #[serde(default)]
32 pub backend: NotificationBackend,
33
34 #[serde(default = "default_notifications_suppress_when_focused")]
36 pub suppress_when_focused: bool,
37
38 #[serde(default)]
41 pub command_failure: Option<bool>,
42
43 #[serde(default = "default_notifications_tool_failure")]
45 pub tool_failure: bool,
46
47 #[serde(default = "default_notifications_error")]
49 pub error: bool,
50
51 #[serde(default = "default_notifications_completion")]
54 pub completion: bool,
55
56 #[serde(default)]
59 pub completion_success: Option<bool>,
60
61 #[serde(default)]
64 pub completion_failure: Option<bool>,
65
66 #[serde(default = "default_notifications_hitl")]
68 pub hitl: bool,
69
70 #[serde(default)]
73 pub policy_approval: Option<bool>,
74
75 #[serde(default)]
78 pub request: Option<bool>,
79
80 #[serde(default = "default_notifications_tool_success")]
82 pub tool_success: bool,
83
84 #[serde(default = "default_notifications_repeat_window_seconds")]
86 pub repeat_window_seconds: u64,
87
88 #[serde(default = "default_notifications_max_identical_in_window")]
90 pub max_identical_in_window: u32,
91}
92
93impl Default for UiNotificationsConfig {
94 fn default() -> Self {
95 Self {
96 enabled: default_notifications_enabled(),
97 delivery_mode: NotificationDeliveryMode::default(),
98 backend: NotificationBackend::default(),
99 suppress_when_focused: default_notifications_suppress_when_focused(),
100 command_failure: Some(default_notifications_command_failure()),
101 tool_failure: default_notifications_tool_failure(),
102 error: default_notifications_error(),
103 completion: default_notifications_completion(),
104 completion_success: Some(default_notifications_completion_success()),
105 completion_failure: Some(default_notifications_completion_failure()),
106 hitl: default_notifications_hitl(),
107 policy_approval: Some(default_notifications_policy_approval()),
108 request: Some(default_notifications_request()),
109 tool_success: default_notifications_tool_success(),
110 repeat_window_seconds: default_notifications_repeat_window_seconds(),
111 max_identical_in_window: default_notifications_max_identical_in_window(),
112 }
113 }
114}
115
116#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
117#[derive(Debug, Clone, Deserialize, Serialize)]
118pub struct UiFullscreenConfig {
119 #[serde(default = "default_fullscreen_mouse_capture")]
122 pub mouse_capture: bool,
123
124 #[serde(default = "default_fullscreen_copy_on_select")]
130 pub copy_on_select: bool,
131
132 #[serde(default = "default_fullscreen_scroll_speed")]
136 pub scroll_speed: u8,
137}
138
139impl Default for UiFullscreenConfig {
140 fn default() -> Self {
141 Self {
142 mouse_capture: default_fullscreen_mouse_capture(),
143 copy_on_select: default_fullscreen_copy_on_select(),
144 scroll_speed: default_fullscreen_scroll_speed(),
145 }
146 }
147}
148
149#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
153#[derive(Debug, Clone, Deserialize, Serialize)]
154pub struct UiTranscriptReviewConfig {
155 #[serde(default = "default_transcript_review_control")]
157 pub show_hints: bool,
158
159 #[serde(default = "default_transcript_review_control")]
161 pub show_shortcut_guide: bool,
162
163 #[serde(default = "default_transcript_review_control")]
165 pub show_close_button: bool,
166}
167
168impl Default for UiTranscriptReviewConfig {
169 fn default() -> Self {
170 Self {
171 show_hints: default_transcript_review_control(),
172 show_shortcut_guide: default_transcript_review_control(),
173 show_close_button: default_transcript_review_control(),
174 }
175 }
176}
177
178#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
179#[derive(Debug, Clone, Deserialize, Serialize)]
180pub struct UiConfig {
181 #[serde(default = "default_tool_output_mode")]
183 pub tool_output_mode: ToolOutputMode,
184
185 #[serde(default = "default_tool_display_mode")]
188 pub tool_display_mode: ToolDisplayMode,
189
190 #[serde(default, alias = "key_bindings")]
193 #[cfg_attr(
194 feature = "schema",
195 schemars(with = "std::collections::BTreeMap<String, Vec<String>>")
196 )]
197 pub keybindings: HashMap<String, Vec<String>>,
198
199 #[serde(default = "default_tool_output_max_lines")]
201 pub tool_output_max_lines: usize,
202
203 #[serde(default = "default_tool_output_spool_bytes")]
205 pub tool_output_spool_bytes: usize,
206
207 #[serde(default)]
209 pub tool_output_spool_dir: Option<String>,
210
211 #[serde(default = "default_allow_tool_ansi")]
213 pub allow_tool_ansi: bool,
214
215 #[serde(default = "default_inline_viewport_rows")]
217 pub inline_viewport_rows: u16,
218
219 #[serde(default = "default_reasoning_display_mode")]
221 pub reasoning_display_mode: ReasoningDisplayMode,
222
223 #[serde(default = "default_reasoning_visible_default")]
225 pub reasoning_visible_default: bool,
226
227 #[serde(default = "default_thinking_display")]
229 pub thinking_display: vtcode_commons::ui_protocol::ThinkingBlockState,
230
231 #[serde(default = "default_vim_mode")]
233 pub vim_mode: bool,
234
235 #[serde(default)]
237 pub status_line: StatusLineConfig,
238
239 #[serde(default)]
241 pub terminal_title: TerminalTitleConfig,
242
243 #[serde(default)]
245 pub keyboard_protocol: KeyboardProtocolConfig,
246
247 #[serde(default)]
249 pub layout_mode: LayoutModeOverride,
250
251 #[serde(default)]
253 pub display_mode: UiDisplayMode,
254
255 #[serde(default = "default_show_sidebar")]
257 pub show_sidebar: bool,
258
259 #[serde(default = "default_dim_completed_todos")]
261 pub dim_completed_todos: bool,
262
263 #[serde(default = "default_show_task_panel")]
267 pub show_task_panel: bool,
268
269 #[serde(default = "default_message_block_spacing")]
271 pub message_block_spacing: bool,
272
273 #[serde(default = "default_show_turn_timer")]
275 pub show_turn_timer: bool,
276
277 #[serde(default = "default_show_diagnostics_in_transcript")]
281 pub show_diagnostics_in_transcript: bool,
282
283 #[serde(default = "default_show_message_timestamps")]
285 pub show_message_timestamps: bool,
286
287 #[serde(default = "default_minimum_contrast")]
295 pub minimum_contrast: f32,
296
297 #[serde(default = "default_bold_is_bright")]
301 pub bold_is_bright: bool,
302
303 #[serde(default = "default_safe_colors_only")]
309 pub safe_colors_only: bool,
310
311 #[serde(default = "default_color_scheme_mode")]
316 pub color_scheme_mode: ColorSchemeMode,
317
318 #[serde(default = "default_app_icon")]
325 pub app_icon: AppIconPreference,
326
327 #[serde(default)]
329 pub program_status: UiProgramStatusConfig,
330
331 #[serde(default)]
333 pub notifications: UiNotificationsConfig,
334
335 #[serde(default)]
337 pub fullscreen: UiFullscreenConfig,
338
339 #[serde(default = "default_screen_reader_mode")]
344 pub screen_reader_mode: bool,
345
346 #[serde(default = "default_reduce_motion_mode")]
350 pub reduce_motion_mode: bool,
351
352 #[serde(default = "default_reduce_motion_keep_progress_animation")]
355 pub reduce_motion_keep_progress_animation: bool,
356
357 #[serde(default = "default_hide_header")]
359 pub hide_header: bool,
360
361 #[serde(default = "default_diff_preview_mode")]
364 pub diff_preview_mode: DiffPreviewMode,
365
366 #[serde(default)]
368 pub transcript_review: UiTranscriptReviewConfig,
369}
370
371#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
373#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Default)]
374#[serde(rename_all = "snake_case")]
375pub enum ColorSchemeMode {
376 #[default]
378 Auto,
379 Light,
381 Dark,
383 #[serde(other)]
385 Unknown,
386}
387
388fn default_minimum_contrast() -> f32 {
389 crate::constants::ui::THEME_MIN_CONTRAST_RATIO
390}
391
392fn default_bold_is_bright() -> bool {
393 false
394}
395
396fn default_safe_colors_only() -> bool {
397 false
398}
399
400fn default_color_scheme_mode() -> ColorSchemeMode {
401 ColorSchemeMode::Auto
402}
403
404fn default_app_icon() -> AppIconPreference {
405 AppIconPreference::Dynamic
406}
407
408fn default_show_sidebar() -> bool {
409 true
410}
411
412fn default_dim_completed_todos() -> bool {
413 true
414}
415
416fn default_message_block_spacing() -> bool {
417 true
418}
419
420fn default_program_status_enabled() -> bool {
421 true
422}
423
424fn default_show_turn_timer() -> bool {
425 false
426}
427
428fn default_show_diagnostics_in_transcript() -> bool {
429 false
430}
431
432fn default_show_message_timestamps() -> bool {
433 true
434}
435
436fn default_vim_mode() -> bool {
437 false
438}
439
440fn default_notifications_enabled() -> bool {
441 true
442}
443
444fn default_notifications_suppress_when_focused() -> bool {
445 true
446}
447
448fn default_notifications_command_failure() -> bool {
449 false
450}
451
452fn default_notifications_tool_failure() -> bool {
453 false
454}
455
456fn default_notifications_error() -> bool {
457 true
458}
459
460fn default_notifications_completion() -> bool {
461 true
462}
463
464fn default_notifications_completion_success() -> bool {
465 false
466}
467
468fn default_notifications_completion_failure() -> bool {
469 true
470}
471
472fn default_notifications_hitl() -> bool {
473 true
474}
475
476fn default_notifications_policy_approval() -> bool {
477 true
478}
479
480fn default_notifications_request() -> bool {
481 false
482}
483
484fn default_notifications_tool_success() -> bool {
485 false
486}
487
488fn default_notifications_repeat_window_seconds() -> u64 {
489 30
490}
491
492fn default_notifications_max_identical_in_window() -> u32 {
493 1
494}
495
496fn env_bool_var(name: &str) -> Option<bool> {
497 crate::env_helpers::read_env_var(name).and_then(|v| vtcode_commons::utils::parse_bool_env_value(&v))
498}
499
500fn env_u8_var(name: &str) -> Option<u8> {
501 crate::env_helpers::read_env_var(name)
502 .and_then(|value| value.trim().parse::<u8>().ok())
503 .map(clamp_fullscreen_scroll_speed)
504}
505
506fn clamp_fullscreen_scroll_speed(value: u8) -> u8 {
507 value.clamp(1, 20)
508}
509
510fn default_fullscreen_mouse_capture() -> bool {
511 env_bool_var("VTCODE_FULLSCREEN_MOUSE_CAPTURE").unwrap_or(true)
512}
513
514fn default_fullscreen_copy_on_select() -> bool {
515 env_bool_var("VTCODE_FULLSCREEN_COPY_ON_SELECT").unwrap_or(true)
516}
517
518fn default_fullscreen_scroll_speed() -> u8 {
519 env_u8_var("VTCODE_FULLSCREEN_SCROLL_SPEED").unwrap_or(3)
520}
521
522fn default_screen_reader_mode() -> bool {
523 env_bool_var("VTCODE_SCREEN_READER").unwrap_or(false)
524}
525
526fn default_reduce_motion_mode() -> bool {
527 env_bool_var("VTCODE_REDUCE_MOTION")
528 .or_else(reduce_motion_preference)
529 .unwrap_or(false)
530}
531
532fn default_reduce_motion_keep_progress_animation() -> bool {
533 false
534}
535
536fn default_hide_header() -> bool {
537 true
538}
539
540fn default_diff_preview_mode() -> DiffPreviewMode {
541 DiffPreviewMode::Inline
542}
543
544fn default_transcript_review_control() -> bool {
545 true
546}
547
548fn default_show_task_panel() -> bool {
549 false
550}
551
552fn default_ask_questions_enabled() -> bool {
553 true
554}
555
556impl Default for UiConfig {
557 fn default() -> Self {
558 Self {
559 tool_output_mode: default_tool_output_mode(),
560 tool_display_mode: default_tool_display_mode(),
561 keybindings: HashMap::new(),
562 tool_output_max_lines: default_tool_output_max_lines(),
563 tool_output_spool_bytes: default_tool_output_spool_bytes(),
564 tool_output_spool_dir: None,
565 allow_tool_ansi: default_allow_tool_ansi(),
566 inline_viewport_rows: default_inline_viewport_rows(),
567 reasoning_display_mode: default_reasoning_display_mode(),
568 reasoning_visible_default: default_reasoning_visible_default(),
569 thinking_display: default_thinking_display(),
570 vim_mode: default_vim_mode(),
571 status_line: StatusLineConfig::default(),
572 terminal_title: TerminalTitleConfig::default(),
573 keyboard_protocol: KeyboardProtocolConfig::default(),
574 layout_mode: LayoutModeOverride::default(),
575 display_mode: UiDisplayMode::default(),
576 show_sidebar: default_show_sidebar(),
577 dim_completed_todos: default_dim_completed_todos(),
578 show_task_panel: default_show_task_panel(),
579 message_block_spacing: default_message_block_spacing(),
580 show_turn_timer: default_show_turn_timer(),
581 show_diagnostics_in_transcript: default_show_diagnostics_in_transcript(),
582 show_message_timestamps: default_show_message_timestamps(),
583 minimum_contrast: default_minimum_contrast(),
585 bold_is_bright: default_bold_is_bright(),
586 safe_colors_only: default_safe_colors_only(),
587 color_scheme_mode: default_color_scheme_mode(),
588 app_icon: default_app_icon(),
589 program_status: UiProgramStatusConfig::default(),
590 notifications: UiNotificationsConfig::default(),
591 fullscreen: UiFullscreenConfig::default(),
592 screen_reader_mode: default_screen_reader_mode(),
593 reduce_motion_mode: default_reduce_motion_mode(),
594 reduce_motion_keep_progress_animation: default_reduce_motion_keep_progress_animation(),
595 hide_header: default_hide_header(),
596 diff_preview_mode: default_diff_preview_mode(),
597 transcript_review: UiTranscriptReviewConfig::default(),
598 }
599 }
600}
601
602#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
604#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
605#[serde(default)]
606pub struct UiProgramStatusConfig {
607 #[serde(default = "default_program_status_enabled")]
609 pub enabled: bool,
610}
611
612impl Default for UiProgramStatusConfig {
613 fn default() -> Self {
614 Self { enabled: default_program_status_enabled() }
615 }
616}
617
618#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
620#[derive(Debug, Clone, Deserialize, Serialize, Default)]
621pub struct ChatConfig {
622 #[serde(default, rename = "askQuestions", alias = "ask_questions")]
624 pub ask_questions: AskQuestionsConfig,
625}
626
627#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
629#[derive(Debug, Clone, Deserialize, Serialize)]
630pub struct AskQuestionsConfig {
631 #[serde(default = "default_ask_questions_enabled")]
633 pub enabled: bool,
634}
635
636impl Default for AskQuestionsConfig {
637 fn default() -> Self {
638 Self { enabled: default_ask_questions_enabled() }
639 }
640}
641
642#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
644#[derive(Debug, Clone, Deserialize, Serialize)]
645pub struct PtyConfig {
646 #[serde(default = "default_pty_enabled")]
648 pub enabled: bool,
649
650 #[serde(default = "default_pty_rows")]
652 pub default_rows: u16,
653
654 #[serde(default = "default_pty_cols")]
656 pub default_cols: u16,
657
658 #[serde(default = "default_max_pty_sessions")]
660 pub max_sessions: usize,
661
662 #[serde(default = "default_pty_timeout")]
668 pub command_timeout_seconds: u64,
669
670 #[serde(default = "default_stdout_tail_lines")]
672 pub stdout_tail_lines: usize,
673
674 #[serde(default = "default_scrollback_lines")]
676 pub scrollback_lines: usize,
677
678 #[serde(default = "default_max_scrollback_bytes")]
680 pub max_scrollback_bytes: usize,
681
682 #[serde(default = "default_large_output_threshold_kb")]
684 pub large_output_threshold_kb: usize,
685
686 #[serde(default)]
688 pub preferred_shell: Option<String>,
689
690 #[serde(default = "default_shell_zsh_fork")]
692 pub shell_zsh_fork: bool,
693
694 #[serde(default)]
696 pub zsh_path: Option<String>,
697}
698
699impl Default for PtyConfig {
700 fn default() -> Self {
701 Self {
702 enabled: default_pty_enabled(),
703 default_rows: default_pty_rows(),
704 default_cols: default_pty_cols(),
705 max_sessions: default_max_pty_sessions(),
706 command_timeout_seconds: default_pty_timeout(),
707 stdout_tail_lines: default_stdout_tail_lines(),
708 scrollback_lines: default_scrollback_lines(),
709 max_scrollback_bytes: default_max_scrollback_bytes(),
710 large_output_threshold_kb: default_large_output_threshold_kb(),
711 preferred_shell: None,
712 shell_zsh_fork: default_shell_zsh_fork(),
713 zsh_path: None,
714 }
715 }
716}
717
718impl PtyConfig {
719 pub fn validate(&self) -> Result<()> {
720 self.zsh_fork_shell_path()?;
721 Ok(())
722 }
723
724 pub fn zsh_fork_shell_path(&self) -> Result<Option<&str>> {
725 if !self.shell_zsh_fork {
726 return Ok(None);
727 }
728
729 let zsh_path = self
730 .zsh_path
731 .as_deref()
732 .map(str::trim)
733 .filter(|path| !path.is_empty())
734 .ok_or_else(|| {
735 anyhow!(
736 "pty.shell_zsh_fork is enabled, but pty.zsh_path is not configured. \
737 Set pty.zsh_path to an absolute path to patched zsh."
738 )
739 })?;
740
741 #[cfg(not(unix))]
742 {
743 let _ = zsh_path;
744 bail!("pty.shell_zsh_fork is only supported on Unix platforms");
745 }
746
747 #[cfg(unix)]
748 {
749 let path = std::path::Path::new(zsh_path);
750 if !path.is_absolute() {
751 bail!("pty.zsh_path '{zsh_path}' must be an absolute path when pty.shell_zsh_fork is enabled");
752 }
753 if !path.exists() {
754 bail!("pty.zsh_path '{zsh_path}' does not exist (required when pty.shell_zsh_fork is enabled)");
755 }
756 if !path.is_file() {
757 bail!("pty.zsh_path '{zsh_path}' is not a file (required when pty.shell_zsh_fork is enabled)");
758 }
759
760 Ok(Some(zsh_path))
761 }
762 }
763}
764
765fn default_pty_enabled() -> bool {
766 true
767}
768
769fn default_pty_rows() -> u16 {
770 24
771}
772
773fn default_pty_cols() -> u16 {
774 80
775}
776
777fn default_max_pty_sessions() -> usize {
778 10
779}
780
781fn default_pty_timeout() -> u64 {
782 300
783}
784
785fn default_shell_zsh_fork() -> bool {
786 false
787}
788
789fn default_stdout_tail_lines() -> usize {
790 crate::constants::defaults::DEFAULT_PTY_STDOUT_TAIL_LINES
791}
792
793fn default_scrollback_lines() -> usize {
794 crate::constants::defaults::DEFAULT_PTY_SCROLLBACK_LINES
795}
796
797fn default_max_scrollback_bytes() -> usize {
798 25_000_000 }
802
803fn default_large_output_threshold_kb() -> usize {
804 5_000 }
806
807fn default_tool_output_mode() -> ToolOutputMode {
808 ToolOutputMode::Compact
809}
810
811fn default_tool_display_mode() -> ToolDisplayMode {
812 ToolDisplayMode::Compact
813}
814
815fn default_tool_output_max_lines() -> usize {
816 30
817}
818
819fn default_tool_output_spool_bytes() -> usize {
820 80_000
821}
822
823fn default_allow_tool_ansi() -> bool {
824 false
825}
826
827fn default_inline_viewport_rows() -> u16 {
828 crate::constants::ui::DEFAULT_INLINE_VIEWPORT_ROWS
829}
830
831fn default_reasoning_display_mode() -> ReasoningDisplayMode {
832 ReasoningDisplayMode::Toggle
833}
834
835fn default_reasoning_visible_default() -> bool {
836 crate::constants::ui::DEFAULT_REASONING_VISIBLE
837}
838
839fn default_thinking_display() -> vtcode_commons::ui_protocol::ThinkingBlockState {
840 vtcode_commons::ui_protocol::ThinkingBlockState::Collapsed
841}
842
843#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
847#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize, Serialize)]
848#[serde(rename_all = "snake_case")]
849pub enum KeyboardProtocolMode {
850 #[default]
852 Default,
853 Full,
855 Minimal,
857 Custom,
859}
860
861impl KeyboardProtocolMode {
862 pub fn as_str(&self) -> &'static str {
864 match self {
865 Self::Default => "default",
866 Self::Full => "full",
867 Self::Minimal => "minimal",
868 Self::Custom => "custom",
869 }
870 }
871}
872
873#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
874#[derive(Debug, Clone, Deserialize, Serialize)]
875pub struct KeyboardProtocolConfig {
876 #[serde(default = "default_keyboard_protocol_enabled")]
878 pub enabled: bool,
879
880 #[serde(default = "default_keyboard_protocol_mode")]
882 pub mode: KeyboardProtocolMode,
883
884 #[serde(default = "default_disambiguate_escape_codes")]
886 pub disambiguate_escape_codes: bool,
887
888 #[serde(default = "default_report_event_types")]
890 pub report_event_types: bool,
891
892 #[serde(default = "default_report_alternate_keys")]
894 pub report_alternate_keys: bool,
895
896 #[serde(default = "default_report_all_keys")]
898 pub report_all_keys: bool,
899}
900
901impl Default for KeyboardProtocolConfig {
902 fn default() -> Self {
903 Self {
904 enabled: default_keyboard_protocol_enabled(),
905 mode: default_keyboard_protocol_mode(),
906 disambiguate_escape_codes: default_disambiguate_escape_codes(),
907 report_event_types: default_report_event_types(),
908 report_alternate_keys: default_report_alternate_keys(),
909 report_all_keys: default_report_all_keys(),
910 }
911 }
912}
913
914impl KeyboardProtocolConfig {
915 pub fn validate(&self) -> Result<()> {
916 let _ = self.mode;
918 Ok(())
919 }
920}
921
922fn default_keyboard_protocol_enabled() -> bool {
923 std::env::var("VTCODE_KEYBOARD_PROTOCOL_ENABLED")
924 .ok()
925 .and_then(|v| v.parse().ok())
926 .unwrap_or(true)
927}
928
929fn default_keyboard_protocol_mode() -> KeyboardProtocolMode {
930 std::env::var("VTCODE_KEYBOARD_PROTOCOL_MODE")
931 .ok()
932 .and_then(|v| match v.to_ascii_lowercase().as_str() {
933 "default" => Some(KeyboardProtocolMode::Default),
934 "full" => Some(KeyboardProtocolMode::Full),
935 "minimal" => Some(KeyboardProtocolMode::Minimal),
936 "custom" => Some(KeyboardProtocolMode::Custom),
937 _ => None,
938 })
939 .unwrap_or_default()
940}
941
942fn default_disambiguate_escape_codes() -> bool {
943 true
944}
945
946fn default_report_event_types() -> bool {
947 true
948}
949
950fn default_report_alternate_keys() -> bool {
951 true
952}
953
954fn default_report_all_keys() -> bool {
955 false
956}
957#[cfg(test)]
958mod tests;