Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -433,10 +433,10 @@ exit_after_capture = false
# ═══════════════════════════════════════════════════════════════════════════════

[session]
# Enable persistence per mode (default to false for clean startup)
persist_transparent = false
persist_whiteboard = false
persist_blackboard = false
# Enable persistence per mode (default to true)
persist_transparent = true
persist_whiteboard = true
persist_blackboard = true

# Persist undo/redo history alongside shapes (set false to save only visible drawings)
persist_history = true
Expand Down
4 changes: 4 additions & 0 deletions configurator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ cd configurator
cargo run
```

On GNOME Wayland, the configurator forces the `tiny-skia` renderer by default to
avoid wgpu dma-buf/present mode crashes. Override with
`ICED_BACKEND=wgpu wayscriber-configurator` if desired.

The window loads the current config, lets you tweak values across the tabbed sections, and writes changes back via `Config::save_with_backup()`.

### Handy actions
Expand Down
28 changes: 27 additions & 1 deletion configurator/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,36 @@ pub fn run() -> iced::Result {
settings.window.size = Size::new(960.0, 640.0);
settings.window.resizable = true;
settings.window.decorations = true;
if std::env::var_os("ICED_BACKEND").is_none() && should_force_tiny_skia() {
// GNOME Wayland + wgpu can crash on dma-buf/present mode selection; tiny-skia avoids this.
// SAFETY: setting a process-local env var before initializing iced is safe here.
unsafe {
std::env::set_var("ICED_BACKEND", "tiny-skia");
}
eprintln!(
"wayscriber-configurator: GNOME Wayland detected; using tiny-skia renderer (set ICED_BACKEND=wgpu to override)."
);
}
ConfiguratorApp::run(settings)
}

fn should_force_tiny_skia() -> bool {
if std::env::var_os("WAYLAND_DISPLAY").is_none() {
return false;
}
let current = std::env::var("XDG_CURRENT_DESKTOP").unwrap_or_default();
let session = std::env::var("XDG_SESSION_DESKTOP").unwrap_or_default();
let combined = format!("{current};{session}");
let combined = combined.to_ascii_lowercase();
combined.contains("gnome") || combined.contains("ubuntu")
}

#[derive(Debug)]
pub struct ConfiguratorApp {
draft: ConfigDraft,
baseline: ConfigDraft,
defaults: ConfigDraft,
base_config: Arc<Config>,
status: StatusMessage,
active_tab: TabId,
active_ui_tab: UiTabId,
Expand Down Expand Up @@ -82,11 +104,13 @@ impl Application for ConfiguratorApp {
let baseline = defaults.clone();
let override_mode = defaults.ui_toolbar_layout_mode;
let config_path = Config::get_config_path().ok();
let base_config = Arc::new(default_config.clone());

let app = Self {
draft: baseline.clone(),
baseline,
defaults,
base_config,
status: StatusMessage::info("Loading configuration..."),
active_tab: TabId::Drawing,
active_ui_tab: UiTabId::Toolbar,
Expand Down Expand Up @@ -123,6 +147,7 @@ impl Application for ConfiguratorApp {
let draft = ConfigDraft::from_config(config.as_ref());
self.draft = draft.clone();
self.baseline = draft;
self.base_config = config.clone();
self.override_mode = self.draft.ui_toolbar_layout_mode;
self.is_dirty = false;
self.status = StatusMessage::success("Configuration loaded from disk.");
Expand Down Expand Up @@ -153,7 +178,7 @@ impl Application for ConfiguratorApp {
return Command::none();
}

match self.draft.to_config() {
match self.draft.to_config(self.base_config.as_ref()) {
Ok(mut config) => {
config.validate_and_clamp();
self.is_saving = true;
Expand All @@ -180,6 +205,7 @@ impl Application for ConfiguratorApp {
self.last_backup_path = backup.clone();
self.draft = draft.clone();
self.baseline = draft;
self.base_config = saved_config.clone();
self.is_dirty = false;
let mut msg = "Configuration saved successfully.".to_string();
if let Some(path) = backup {
Expand Down
12 changes: 8 additions & 4 deletions configurator/src/models/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,9 @@ impl ConfigDraft {
}
}

pub fn to_config(&self) -> Result<Config, Vec<FormError>> {
pub fn to_config(&self, base: &Config) -> Result<Config, Vec<FormError>> {
let mut errors = Vec::new();
let mut config = Config::default();
let mut config = base.clone();

match self.drawing_color.to_color_spec() {
Ok(color) => config.drawing.default_color = color,
Expand Down Expand Up @@ -764,7 +764,9 @@ mod tests {
selected_named: NamedColorOption::Custom,
};

let errors = draft.to_config().expect_err("expected validation errors");
let errors = draft
.to_config(&Config::default())
.expect_err("expected validation errors");
let fields: Vec<&str> = errors.iter().map(|err| err.field.as_str()).collect();

assert!(fields.contains(&"drawing.default_thickness"));
Expand All @@ -778,7 +780,9 @@ mod tests {
draft.session_storage_mode = SessionStorageModeOption::Custom;
draft.session_custom_directory = " ".to_string();

let config = draft.to_config().expect("to_config should succeed");
let config = draft
.to_config(&Config::default())
.expect("to_config should succeed");
assert!(config.session.custom_directory.is_none());
}

Expand Down
8 changes: 4 additions & 4 deletions docs/CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,13 +437,13 @@ exit_after_capture = false

### `[session]` - Session Persistence

Optional on-disk persistence for your drawings. Disabled by default so each session starts fresh.
Optional on-disk persistence for your drawings. Enabled by default so sessions resume automatically.

```toml
[session]
persist_transparent = false
persist_whiteboard = false
persist_blackboard = false
persist_transparent = true
persist_whiteboard = true
persist_blackboard = true
persist_history = true
restore_tool_state = true
storage = "auto"
Expand Down
2 changes: 1 addition & 1 deletion src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ pub(super) fn primary_config_dir() -> Result<PathBuf> {
/// exit = ["Escape", "Ctrl+Q"]
/// undo = ["Ctrl+Z"]
/// ```
#[derive(Debug, Serialize, Deserialize, Default, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct Config {
/// Drawing tool defaults (color, thickness, font size)
#[serde(default)]
Expand Down
26 changes: 13 additions & 13 deletions src/config/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub const PRESET_SLOTS_MAX: usize = 5;
///
/// Controls the default appearance of drawing tools when the overlay first opens.
/// Users can change these values at runtime using keybindings.
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct DrawingConfig {
/// Default pen color - either a named color (red, green, blue, yellow, orange, pink, white, black)
/// or an RGB array like `[255, 0, 0]` for red
Expand Down Expand Up @@ -222,7 +222,7 @@ impl Default for PresetSlotsConfig {
/// Arrow drawing settings.
///
/// Controls the appearance of arrowheads when using the arrow tool (Ctrl+Shift+Drag).
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ArrowConfig {
/// Arrowhead length in pixels (valid range: 5.0 - 50.0)
#[serde(default = "default_arrow_length")]
Expand All @@ -249,7 +249,7 @@ impl Default for ArrowConfig {
}

/// Undo/redo playback configuration.
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct HistoryConfig {
/// Delay in milliseconds between steps when running "undo all by delay"
#[serde(default = "default_undo_all_delay_ms")]
Expand Down Expand Up @@ -298,7 +298,7 @@ impl Default for HistoryConfig {
///
/// These settings control rendering performance and smoothness. Most users
/// won't need to change these from their defaults.
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct PerformanceConfig {
/// Number of buffers for buffering (valid range: 2 - 4)
/// - 2 = double buffering (lower memory, potential tearing)
Expand Down Expand Up @@ -331,7 +331,7 @@ impl Default for PerformanceConfig {
/// UI display preferences.
///
/// Controls the visibility and positioning of on-screen UI elements.
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct UiConfig {
/// Show the status bar displaying current color, thickness, and tool
#[serde(default = "default_show_status")]
Expand Down Expand Up @@ -394,7 +394,7 @@ impl Default for UiConfig {
}

/// Status bar styling configuration.
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct StatusBarStyle {
/// Font size for status bar text
#[serde(default = "default_status_font_size")]
Expand Down Expand Up @@ -430,7 +430,7 @@ impl Default for StatusBarStyle {
}

/// Click highlight configuration for mouse press indicator.
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ClickHighlightConfig {
/// Whether the highlight effect starts enabled
#[serde(default = "default_click_highlight_enabled")]
Expand Down Expand Up @@ -462,7 +462,7 @@ pub struct ClickHighlightConfig {
}

/// Context menu visibility configuration.
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ContextMenuUiConfig {
#[serde(default = "default_context_menu_enabled")]
pub enabled: bool,
Expand Down Expand Up @@ -491,7 +491,7 @@ impl Default for ClickHighlightConfig {
}

/// Help overlay styling configuration.
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct HelpOverlayStyle {
/// Font size for help overlay text
#[serde(default = "default_help_font_size")]
Expand Down Expand Up @@ -541,7 +541,7 @@ impl Default for HelpOverlayStyle {
// ─────────────────────────────────────────────────────────────────────────────

#[cfg(tablet)]
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct TabletInputConfig {
/// Enable tablet/stylus events at runtime (feature must be compiled in).
#[serde(default = "default_tablet_enabled")]
Expand Down Expand Up @@ -1015,9 +1015,9 @@ pub struct SessionConfig {
impl Default for SessionConfig {
fn default() -> Self {
Self {
persist_transparent: false,
persist_whiteboard: false,
persist_blackboard: false,
persist_transparent: true,
persist_whiteboard: true,
persist_blackboard: true,
persist_history: default_persist_history(),
restore_tool_state: default_restore_tool_state(),
storage: default_session_storage_mode(),
Expand Down