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")]
664 pub command_timeout_seconds: u64,
665
666 #[serde(default = "default_stdout_tail_lines")]
668 pub stdout_tail_lines: usize,
669
670 #[serde(default = "default_scrollback_lines")]
672 pub scrollback_lines: usize,
673
674 #[serde(default = "default_max_scrollback_bytes")]
676 pub max_scrollback_bytes: usize,
677
678 #[serde(default = "default_large_output_threshold_kb")]
680 pub large_output_threshold_kb: usize,
681
682 #[serde(default)]
684 pub preferred_shell: Option<String>,
685
686 #[serde(default = "default_shell_zsh_fork")]
688 pub shell_zsh_fork: bool,
689
690 #[serde(default)]
692 pub zsh_path: Option<String>,
693}
694
695impl Default for PtyConfig {
696 fn default() -> Self {
697 Self {
698 enabled: default_pty_enabled(),
699 default_rows: default_pty_rows(),
700 default_cols: default_pty_cols(),
701 max_sessions: default_max_pty_sessions(),
702 command_timeout_seconds: default_pty_timeout(),
703 stdout_tail_lines: default_stdout_tail_lines(),
704 scrollback_lines: default_scrollback_lines(),
705 max_scrollback_bytes: default_max_scrollback_bytes(),
706 large_output_threshold_kb: default_large_output_threshold_kb(),
707 preferred_shell: None,
708 shell_zsh_fork: default_shell_zsh_fork(),
709 zsh_path: None,
710 }
711 }
712}
713
714impl PtyConfig {
715 pub fn validate(&self) -> Result<()> {
716 self.zsh_fork_shell_path()?;
717 Ok(())
718 }
719
720 pub fn zsh_fork_shell_path(&self) -> Result<Option<&str>> {
721 if !self.shell_zsh_fork {
722 return Ok(None);
723 }
724
725 let zsh_path = self
726 .zsh_path
727 .as_deref()
728 .map(str::trim)
729 .filter(|path| !path.is_empty())
730 .ok_or_else(|| {
731 anyhow!(
732 "pty.shell_zsh_fork is enabled, but pty.zsh_path is not configured. \
733 Set pty.zsh_path to an absolute path to patched zsh."
734 )
735 })?;
736
737 #[cfg(not(unix))]
738 {
739 let _ = zsh_path;
740 bail!("pty.shell_zsh_fork is only supported on Unix platforms");
741 }
742
743 #[cfg(unix)]
744 {
745 let path = std::path::Path::new(zsh_path);
746 if !path.is_absolute() {
747 bail!("pty.zsh_path '{zsh_path}' must be an absolute path when pty.shell_zsh_fork is enabled");
748 }
749 if !path.exists() {
750 bail!("pty.zsh_path '{zsh_path}' does not exist (required when pty.shell_zsh_fork is enabled)");
751 }
752 if !path.is_file() {
753 bail!("pty.zsh_path '{zsh_path}' is not a file (required when pty.shell_zsh_fork is enabled)");
754 }
755
756 Ok(Some(zsh_path))
757 }
758 }
759}
760
761fn default_pty_enabled() -> bool {
762 true
763}
764
765fn default_pty_rows() -> u16 {
766 24
767}
768
769fn default_pty_cols() -> u16 {
770 80
771}
772
773fn default_max_pty_sessions() -> usize {
774 10
775}
776
777fn default_pty_timeout() -> u64 {
778 300
779}
780
781fn default_shell_zsh_fork() -> bool {
782 false
783}
784
785fn default_stdout_tail_lines() -> usize {
786 crate::constants::defaults::DEFAULT_PTY_STDOUT_TAIL_LINES
787}
788
789fn default_scrollback_lines() -> usize {
790 crate::constants::defaults::DEFAULT_PTY_SCROLLBACK_LINES
791}
792
793fn default_max_scrollback_bytes() -> usize {
794 25_000_000 }
798
799fn default_large_output_threshold_kb() -> usize {
800 5_000 }
802
803fn default_tool_output_mode() -> ToolOutputMode {
804 ToolOutputMode::Compact
805}
806
807fn default_tool_display_mode() -> ToolDisplayMode {
808 ToolDisplayMode::Compact
809}
810
811fn default_tool_output_max_lines() -> usize {
812 30
813}
814
815fn default_tool_output_spool_bytes() -> usize {
816 80_000
817}
818
819fn default_allow_tool_ansi() -> bool {
820 false
821}
822
823fn default_inline_viewport_rows() -> u16 {
824 crate::constants::ui::DEFAULT_INLINE_VIEWPORT_ROWS
825}
826
827fn default_reasoning_display_mode() -> ReasoningDisplayMode {
828 ReasoningDisplayMode::Toggle
829}
830
831fn default_reasoning_visible_default() -> bool {
832 crate::constants::ui::DEFAULT_REASONING_VISIBLE
833}
834
835fn default_thinking_display() -> vtcode_commons::ui_protocol::ThinkingBlockState {
836 vtcode_commons::ui_protocol::ThinkingBlockState::Collapsed
837}
838
839#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
843#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize, Serialize)]
844#[serde(rename_all = "snake_case")]
845pub enum KeyboardProtocolMode {
846 #[default]
848 Default,
849 Full,
851 Minimal,
853 Custom,
855}
856
857impl KeyboardProtocolMode {
858 pub fn as_str(&self) -> &'static str {
860 match self {
861 Self::Default => "default",
862 Self::Full => "full",
863 Self::Minimal => "minimal",
864 Self::Custom => "custom",
865 }
866 }
867}
868
869#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
870#[derive(Debug, Clone, Deserialize, Serialize)]
871pub struct KeyboardProtocolConfig {
872 #[serde(default = "default_keyboard_protocol_enabled")]
874 pub enabled: bool,
875
876 #[serde(default = "default_keyboard_protocol_mode")]
878 pub mode: KeyboardProtocolMode,
879
880 #[serde(default = "default_disambiguate_escape_codes")]
882 pub disambiguate_escape_codes: bool,
883
884 #[serde(default = "default_report_event_types")]
886 pub report_event_types: bool,
887
888 #[serde(default = "default_report_alternate_keys")]
890 pub report_alternate_keys: bool,
891
892 #[serde(default = "default_report_all_keys")]
894 pub report_all_keys: bool,
895}
896
897impl Default for KeyboardProtocolConfig {
898 fn default() -> Self {
899 Self {
900 enabled: default_keyboard_protocol_enabled(),
901 mode: default_keyboard_protocol_mode(),
902 disambiguate_escape_codes: default_disambiguate_escape_codes(),
903 report_event_types: default_report_event_types(),
904 report_alternate_keys: default_report_alternate_keys(),
905 report_all_keys: default_report_all_keys(),
906 }
907 }
908}
909
910impl KeyboardProtocolConfig {
911 pub fn validate(&self) -> Result<()> {
912 let _ = self.mode;
914 Ok(())
915 }
916}
917
918fn default_keyboard_protocol_enabled() -> bool {
919 std::env::var("VTCODE_KEYBOARD_PROTOCOL_ENABLED")
920 .ok()
921 .and_then(|v| v.parse().ok())
922 .unwrap_or(true)
923}
924
925fn default_keyboard_protocol_mode() -> KeyboardProtocolMode {
926 std::env::var("VTCODE_KEYBOARD_PROTOCOL_MODE")
927 .ok()
928 .and_then(|v| match v.to_ascii_lowercase().as_str() {
929 "default" => Some(KeyboardProtocolMode::Default),
930 "full" => Some(KeyboardProtocolMode::Full),
931 "minimal" => Some(KeyboardProtocolMode::Minimal),
932 "custom" => Some(KeyboardProtocolMode::Custom),
933 _ => None,
934 })
935 .unwrap_or_default()
936}
937
938fn default_disambiguate_escape_codes() -> bool {
939 true
940}
941
942fn default_report_event_types() -> bool {
943 true
944}
945
946fn default_report_alternate_keys() -> bool {
947 true
948}
949
950fn default_report_all_keys() -> bool {
951 false
952}
953#[cfg(test)]
954mod tests;