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_minimum_contrast")]
291 pub minimum_contrast: f32,
292
293 #[serde(default = "default_bold_is_bright")]
297 pub bold_is_bright: bool,
298
299 #[serde(default = "default_safe_colors_only")]
305 pub safe_colors_only: bool,
306
307 #[serde(default = "default_color_scheme_mode")]
312 pub color_scheme_mode: ColorSchemeMode,
313
314 #[serde(default = "default_app_icon")]
321 pub app_icon: AppIconPreference,
322
323 #[serde(default)]
325 pub program_status: UiProgramStatusConfig,
326
327 #[serde(default)]
329 pub notifications: UiNotificationsConfig,
330
331 #[serde(default)]
333 pub fullscreen: UiFullscreenConfig,
334
335 #[serde(default = "default_screen_reader_mode")]
340 pub screen_reader_mode: bool,
341
342 #[serde(default = "default_reduce_motion_mode")]
346 pub reduce_motion_mode: bool,
347
348 #[serde(default = "default_reduce_motion_keep_progress_animation")]
351 pub reduce_motion_keep_progress_animation: bool,
352
353 #[serde(default = "default_hide_header")]
355 pub hide_header: bool,
356
357 #[serde(default = "default_diff_preview_mode")]
360 pub diff_preview_mode: DiffPreviewMode,
361
362 #[serde(default)]
364 pub transcript_review: UiTranscriptReviewConfig,
365}
366
367#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
369#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Default)]
370#[serde(rename_all = "snake_case")]
371pub enum ColorSchemeMode {
372 #[default]
374 Auto,
375 Light,
377 Dark,
379 #[serde(other)]
381 Unknown,
382}
383
384fn default_minimum_contrast() -> f32 {
385 crate::constants::ui::THEME_MIN_CONTRAST_RATIO
386}
387
388fn default_bold_is_bright() -> bool {
389 false
390}
391
392fn default_safe_colors_only() -> bool {
393 false
394}
395
396fn default_color_scheme_mode() -> ColorSchemeMode {
397 ColorSchemeMode::Auto
398}
399
400fn default_app_icon() -> AppIconPreference {
401 AppIconPreference::Dynamic
402}
403
404fn default_show_sidebar() -> bool {
405 true
406}
407
408fn default_dim_completed_todos() -> bool {
409 true
410}
411
412fn default_message_block_spacing() -> bool {
413 true
414}
415
416fn default_program_status_enabled() -> bool {
417 true
418}
419
420fn default_show_turn_timer() -> bool {
421 false
422}
423
424fn default_show_diagnostics_in_transcript() -> bool {
425 false
426}
427
428fn default_vim_mode() -> bool {
429 false
430}
431
432fn default_notifications_enabled() -> bool {
433 true
434}
435
436fn default_notifications_suppress_when_focused() -> bool {
437 true
438}
439
440fn default_notifications_command_failure() -> bool {
441 false
442}
443
444fn default_notifications_tool_failure() -> bool {
445 false
446}
447
448fn default_notifications_error() -> bool {
449 true
450}
451
452fn default_notifications_completion() -> bool {
453 true
454}
455
456fn default_notifications_completion_success() -> bool {
457 false
458}
459
460fn default_notifications_completion_failure() -> bool {
461 true
462}
463
464fn default_notifications_hitl() -> bool {
465 true
466}
467
468fn default_notifications_policy_approval() -> bool {
469 true
470}
471
472fn default_notifications_request() -> bool {
473 false
474}
475
476fn default_notifications_tool_success() -> bool {
477 false
478}
479
480fn default_notifications_repeat_window_seconds() -> u64 {
481 30
482}
483
484fn default_notifications_max_identical_in_window() -> u32 {
485 1
486}
487
488fn env_bool_var(name: &str) -> Option<bool> {
489 crate::env_helpers::read_env_var(name).and_then(|v| vtcode_commons::utils::parse_bool_env_value(&v))
490}
491
492fn env_u8_var(name: &str) -> Option<u8> {
493 crate::env_helpers::read_env_var(name)
494 .and_then(|value| value.trim().parse::<u8>().ok())
495 .map(clamp_fullscreen_scroll_speed)
496}
497
498fn clamp_fullscreen_scroll_speed(value: u8) -> u8 {
499 value.clamp(1, 20)
500}
501
502fn default_fullscreen_mouse_capture() -> bool {
503 env_bool_var("VTCODE_FULLSCREEN_MOUSE_CAPTURE").unwrap_or(true)
504}
505
506fn default_fullscreen_copy_on_select() -> bool {
507 env_bool_var("VTCODE_FULLSCREEN_COPY_ON_SELECT").unwrap_or(true)
508}
509
510fn default_fullscreen_scroll_speed() -> u8 {
511 env_u8_var("VTCODE_FULLSCREEN_SCROLL_SPEED").unwrap_or(3)
512}
513
514fn default_screen_reader_mode() -> bool {
515 env_bool_var("VTCODE_SCREEN_READER").unwrap_or(false)
516}
517
518fn default_reduce_motion_mode() -> bool {
519 env_bool_var("VTCODE_REDUCE_MOTION")
520 .or_else(reduce_motion_preference)
521 .unwrap_or(false)
522}
523
524fn default_reduce_motion_keep_progress_animation() -> bool {
525 false
526}
527
528fn default_hide_header() -> bool {
529 true
530}
531
532fn default_diff_preview_mode() -> DiffPreviewMode {
533 DiffPreviewMode::Inline
534}
535
536fn default_transcript_review_control() -> bool {
537 true
538}
539
540fn default_show_task_panel() -> bool {
541 false
542}
543
544fn default_ask_questions_enabled() -> bool {
545 true
546}
547
548impl Default for UiConfig {
549 fn default() -> Self {
550 Self {
551 tool_output_mode: default_tool_output_mode(),
552 tool_display_mode: default_tool_display_mode(),
553 keybindings: HashMap::new(),
554 tool_output_max_lines: default_tool_output_max_lines(),
555 tool_output_spool_bytes: default_tool_output_spool_bytes(),
556 tool_output_spool_dir: None,
557 allow_tool_ansi: default_allow_tool_ansi(),
558 inline_viewport_rows: default_inline_viewport_rows(),
559 reasoning_display_mode: default_reasoning_display_mode(),
560 reasoning_visible_default: default_reasoning_visible_default(),
561 thinking_display: default_thinking_display(),
562 vim_mode: default_vim_mode(),
563 status_line: StatusLineConfig::default(),
564 terminal_title: TerminalTitleConfig::default(),
565 keyboard_protocol: KeyboardProtocolConfig::default(),
566 layout_mode: LayoutModeOverride::default(),
567 display_mode: UiDisplayMode::default(),
568 show_sidebar: default_show_sidebar(),
569 dim_completed_todos: default_dim_completed_todos(),
570 show_task_panel: default_show_task_panel(),
571 message_block_spacing: default_message_block_spacing(),
572 show_turn_timer: default_show_turn_timer(),
573 show_diagnostics_in_transcript: default_show_diagnostics_in_transcript(),
574 minimum_contrast: default_minimum_contrast(),
576 bold_is_bright: default_bold_is_bright(),
577 safe_colors_only: default_safe_colors_only(),
578 color_scheme_mode: default_color_scheme_mode(),
579 app_icon: default_app_icon(),
580 program_status: UiProgramStatusConfig::default(),
581 notifications: UiNotificationsConfig::default(),
582 fullscreen: UiFullscreenConfig::default(),
583 screen_reader_mode: default_screen_reader_mode(),
584 reduce_motion_mode: default_reduce_motion_mode(),
585 reduce_motion_keep_progress_animation: default_reduce_motion_keep_progress_animation(),
586 hide_header: default_hide_header(),
587 diff_preview_mode: default_diff_preview_mode(),
588 transcript_review: UiTranscriptReviewConfig::default(),
589 }
590 }
591}
592
593#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
595#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
596#[serde(default)]
597pub struct UiProgramStatusConfig {
598 #[serde(default = "default_program_status_enabled")]
600 pub enabled: bool,
601}
602
603impl Default for UiProgramStatusConfig {
604 fn default() -> Self {
605 Self { enabled: default_program_status_enabled() }
606 }
607}
608
609#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
611#[derive(Debug, Clone, Deserialize, Serialize, Default)]
612pub struct ChatConfig {
613 #[serde(default, rename = "askQuestions", alias = "ask_questions")]
615 pub ask_questions: AskQuestionsConfig,
616}
617
618#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
620#[derive(Debug, Clone, Deserialize, Serialize)]
621pub struct AskQuestionsConfig {
622 #[serde(default = "default_ask_questions_enabled")]
624 pub enabled: bool,
625}
626
627impl Default for AskQuestionsConfig {
628 fn default() -> Self {
629 Self { enabled: default_ask_questions_enabled() }
630 }
631}
632
633#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
635#[derive(Debug, Clone, Deserialize, Serialize)]
636pub struct PtyConfig {
637 #[serde(default = "default_pty_enabled")]
639 pub enabled: bool,
640
641 #[serde(default = "default_pty_rows")]
643 pub default_rows: u16,
644
645 #[serde(default = "default_pty_cols")]
647 pub default_cols: u16,
648
649 #[serde(default = "default_max_pty_sessions")]
651 pub max_sessions: usize,
652
653 #[serde(default = "default_pty_timeout")]
655 pub command_timeout_seconds: u64,
656
657 #[serde(default = "default_stdout_tail_lines")]
659 pub stdout_tail_lines: usize,
660
661 #[serde(default = "default_scrollback_lines")]
663 pub scrollback_lines: usize,
664
665 #[serde(default = "default_max_scrollback_bytes")]
667 pub max_scrollback_bytes: usize,
668
669 #[serde(default = "default_large_output_threshold_kb")]
671 pub large_output_threshold_kb: usize,
672
673 #[serde(default)]
675 pub preferred_shell: Option<String>,
676
677 #[serde(default = "default_shell_zsh_fork")]
679 pub shell_zsh_fork: bool,
680
681 #[serde(default)]
683 pub zsh_path: Option<String>,
684}
685
686impl Default for PtyConfig {
687 fn default() -> Self {
688 Self {
689 enabled: default_pty_enabled(),
690 default_rows: default_pty_rows(),
691 default_cols: default_pty_cols(),
692 max_sessions: default_max_pty_sessions(),
693 command_timeout_seconds: default_pty_timeout(),
694 stdout_tail_lines: default_stdout_tail_lines(),
695 scrollback_lines: default_scrollback_lines(),
696 max_scrollback_bytes: default_max_scrollback_bytes(),
697 large_output_threshold_kb: default_large_output_threshold_kb(),
698 preferred_shell: None,
699 shell_zsh_fork: default_shell_zsh_fork(),
700 zsh_path: None,
701 }
702 }
703}
704
705impl PtyConfig {
706 pub fn validate(&self) -> Result<()> {
707 self.zsh_fork_shell_path()?;
708 Ok(())
709 }
710
711 pub fn zsh_fork_shell_path(&self) -> Result<Option<&str>> {
712 if !self.shell_zsh_fork {
713 return Ok(None);
714 }
715
716 let zsh_path = self
717 .zsh_path
718 .as_deref()
719 .map(str::trim)
720 .filter(|path| !path.is_empty())
721 .ok_or_else(|| {
722 anyhow!(
723 "pty.shell_zsh_fork is enabled, but pty.zsh_path is not configured. \
724 Set pty.zsh_path to an absolute path to patched zsh."
725 )
726 })?;
727
728 #[cfg(not(unix))]
729 {
730 let _ = zsh_path;
731 bail!("pty.shell_zsh_fork is only supported on Unix platforms");
732 }
733
734 #[cfg(unix)]
735 {
736 let path = std::path::Path::new(zsh_path);
737 if !path.is_absolute() {
738 bail!("pty.zsh_path '{zsh_path}' must be an absolute path when pty.shell_zsh_fork is enabled");
739 }
740 if !path.exists() {
741 bail!("pty.zsh_path '{zsh_path}' does not exist (required when pty.shell_zsh_fork is enabled)");
742 }
743 if !path.is_file() {
744 bail!("pty.zsh_path '{zsh_path}' is not a file (required when pty.shell_zsh_fork is enabled)");
745 }
746
747 Ok(Some(zsh_path))
748 }
749 }
750}
751
752fn default_pty_enabled() -> bool {
753 true
754}
755
756fn default_pty_rows() -> u16 {
757 24
758}
759
760fn default_pty_cols() -> u16 {
761 80
762}
763
764fn default_max_pty_sessions() -> usize {
765 10
766}
767
768fn default_pty_timeout() -> u64 {
769 300
770}
771
772fn default_shell_zsh_fork() -> bool {
773 false
774}
775
776fn default_stdout_tail_lines() -> usize {
777 crate::constants::defaults::DEFAULT_PTY_STDOUT_TAIL_LINES
778}
779
780fn default_scrollback_lines() -> usize {
781 crate::constants::defaults::DEFAULT_PTY_SCROLLBACK_LINES
782}
783
784fn default_max_scrollback_bytes() -> usize {
785 25_000_000 }
789
790fn default_large_output_threshold_kb() -> usize {
791 5_000 }
793
794fn default_tool_output_mode() -> ToolOutputMode {
795 ToolOutputMode::Compact
796}
797
798fn default_tool_display_mode() -> ToolDisplayMode {
799 ToolDisplayMode::Compact
800}
801
802fn default_tool_output_max_lines() -> usize {
803 30
804}
805
806fn default_tool_output_spool_bytes() -> usize {
807 80_000
808}
809
810fn default_allow_tool_ansi() -> bool {
811 false
812}
813
814fn default_inline_viewport_rows() -> u16 {
815 crate::constants::ui::DEFAULT_INLINE_VIEWPORT_ROWS
816}
817
818fn default_reasoning_display_mode() -> ReasoningDisplayMode {
819 ReasoningDisplayMode::Toggle
820}
821
822fn default_reasoning_visible_default() -> bool {
823 crate::constants::ui::DEFAULT_REASONING_VISIBLE
824}
825
826fn default_thinking_display() -> vtcode_commons::ui_protocol::ThinkingBlockState {
827 vtcode_commons::ui_protocol::ThinkingBlockState::Collapsed
828}
829
830#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
834#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize, Serialize)]
835#[serde(rename_all = "snake_case")]
836pub enum KeyboardProtocolMode {
837 #[default]
839 Default,
840 Full,
842 Minimal,
844 Custom,
846}
847
848impl KeyboardProtocolMode {
849 pub fn as_str(&self) -> &'static str {
851 match self {
852 Self::Default => "default",
853 Self::Full => "full",
854 Self::Minimal => "minimal",
855 Self::Custom => "custom",
856 }
857 }
858}
859
860#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
861#[derive(Debug, Clone, Deserialize, Serialize)]
862pub struct KeyboardProtocolConfig {
863 #[serde(default = "default_keyboard_protocol_enabled")]
865 pub enabled: bool,
866
867 #[serde(default = "default_keyboard_protocol_mode")]
869 pub mode: KeyboardProtocolMode,
870
871 #[serde(default = "default_disambiguate_escape_codes")]
873 pub disambiguate_escape_codes: bool,
874
875 #[serde(default = "default_report_event_types")]
877 pub report_event_types: bool,
878
879 #[serde(default = "default_report_alternate_keys")]
881 pub report_alternate_keys: bool,
882
883 #[serde(default = "default_report_all_keys")]
885 pub report_all_keys: bool,
886}
887
888impl Default for KeyboardProtocolConfig {
889 fn default() -> Self {
890 Self {
891 enabled: default_keyboard_protocol_enabled(),
892 mode: default_keyboard_protocol_mode(),
893 disambiguate_escape_codes: default_disambiguate_escape_codes(),
894 report_event_types: default_report_event_types(),
895 report_alternate_keys: default_report_alternate_keys(),
896 report_all_keys: default_report_all_keys(),
897 }
898 }
899}
900
901impl KeyboardProtocolConfig {
902 pub fn validate(&self) -> Result<()> {
903 let _ = self.mode;
905 Ok(())
906 }
907}
908
909fn default_keyboard_protocol_enabled() -> bool {
910 std::env::var("VTCODE_KEYBOARD_PROTOCOL_ENABLED")
911 .ok()
912 .and_then(|v| v.parse().ok())
913 .unwrap_or(true)
914}
915
916fn default_keyboard_protocol_mode() -> KeyboardProtocolMode {
917 std::env::var("VTCODE_KEYBOARD_PROTOCOL_MODE")
918 .ok()
919 .and_then(|v| match v.to_ascii_lowercase().as_str() {
920 "default" => Some(KeyboardProtocolMode::Default),
921 "full" => Some(KeyboardProtocolMode::Full),
922 "minimal" => Some(KeyboardProtocolMode::Minimal),
923 "custom" => Some(KeyboardProtocolMode::Custom),
924 _ => None,
925 })
926 .unwrap_or_default()
927}
928
929fn default_disambiguate_escape_codes() -> bool {
930 true
931}
932
933fn default_report_event_types() -> bool {
934 true
935}
936
937fn default_report_alternate_keys() -> bool {
938 true
939}
940
941fn default_report_all_keys() -> bool {
942 false
943}
944#[cfg(test)]
945mod tests;