Skip to content

Commit

Permalink
Merge pull request #155 from kas-gui/work
Browse files Browse the repository at this point in the history
Config read/write support
  • Loading branch information
dhardy authored Feb 5, 2021
2 parents 2d12c91 + e7d2954 commit 6578ae4
Show file tree
Hide file tree
Showing 21 changed files with 507 additions and 74 deletions.
14 changes: 8 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,15 @@ jobs:
- name: test (kas-macros)
run: cargo test --manifest-path kas-macros/Cargo.toml --all-features
- name: test (kas)
run: cargo test --all-features
run: |
cargo test
# Note: we must test serde without winit and with winit
cargo test --features serde
cargo test --all-features
- name: test (kas-theme)
run: |
# note: stack_dst + gat is broken
cargo test --manifest-path kas-theme/Cargo.toml --features stack_dst,unsize
cargo test --manifest-path kas-theme/Cargo.toml --no-default-features --features gat
cargo test --manifest-path kas-theme/Cargo.toml --all-features
- name: test (kas-wgpu)
run: |
# note: gat is broken
cargo test --manifest-path kas-wgpu/Cargo.toml --features nightly,shaping
cargo test
cargo test --manifest-path kas-wgpu/Cargo.toml --all-features
22 changes: 19 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ categories = ["gui"]
repository = "https://github.com/kas-gui/kas"
exclude = ["/screenshots"]

[package.metadata.docs.rs]
features = ["nightly", "stack_dst", "winit"]

[features]
# Enables usage of unstable Rust features
nightly = ["min_spec"]
Expand All @@ -36,13 +39,28 @@ shaping = ["kas-text/shaping"]
# Enable Markdown parsing
markdown = ["kas-text/markdown"]

#TODO: once namespaced-features (cargo#5565) and weak-dep-features (cargo#8832)
# are stable, enable this and remove the serde feature requirement under dependencies.winit
# For now, this does work with nightly Cargo and -Z namespaced-features -Z weak-dep-features
# serde = ["dep:serde", "winit?/serde"]

# Enable support for YAML (de)serialisation
yaml = ["serde", "serde_yaml"]

# Enable support for JSON (de)serialisation
json = ["serde", "serde_json"]

[dependencies]
log = "0.4"
smallvec = "1.4"
stack_dst = { version = "0.6", optional = true }
bitflags = "1" # only used without winit
unicode-segmentation = "1.7"
linear-map = "1.2.0"
thiserror = "1.0.23"
serde = { version = "1.0.123", features = ["derive"], optional = true }
serde_json = { version = "1.0.61", optional = true }
serde_yaml = { version = "0.8.16", optional = true }

[dependencies.kas-macros]
version = "0.6.0"
Expand All @@ -57,9 +75,7 @@ rev = "7c628156f9035abef2ffaba090c61de016b239cb"
# Provides translations for several winit types
version = "0.23"
optional = true
features = ["serde"]

[workspace]
members = ["kas-macros", "kas-theme", "kas-wgpu"]

[package.metadata.docs.rs]
features = ["nightly", "stack_dst", "winit"]
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ The `kas` crate has the following feature flags:
usage but not for end users. (This only affects generated documentation.)
- `winit`: adds compatibility code for winit's event and geometry types.
This is currently the only functional windowing/event library.
- `serde`: adds (de)serialisation support to various types
- `json`: adds config (de)serialisation using JSON (implies `serde`)
- `yaml`: adds config (de)serialisation using YAML (implies `serde`)
- `stack_dst`: some compatibility impls (see `kas-theme`'s documentation)


Expand Down
15 changes: 8 additions & 7 deletions kas-wgpu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ repository = "https://github.com/kas-gui/kas"
readme = "README.md"
documentation = "https://docs.rs/kas-wgpu/"

[package.metadata.docs.rs]
# NOTE: clipboard feature is causing build failures
# https://github.com/kas-gui/kas/issues/83
no-default-features = true
features = ["stack_dst"]

[features]
default = ["clipboard", "stack_dst"]
nightly = ["unsize", "kas/nightly", "kas-theme/nightly"]
Expand Down Expand Up @@ -40,6 +46,7 @@ smallvec = "1.1"
wgpu = "0.6.0"
wgpu_glyph = "0.10.0"
winit = "0.23.0"
thiserror = "1.0.23"

[dependencies.clipboard]
# Provides clipboard support
Expand All @@ -49,13 +56,7 @@ optional = true
[dev-dependencies]
chrono = "0.4"
env_logger = "0.7"
kas = { path = "..", features = ["markdown", "winit"] }
kas = { path = "..", features = ["markdown", "winit", "json", "yaml"] }

[build-dependencies]
glob = "0.3"

[package.metadata.docs.rs]
# NOTE: clipboard feature is causing build failures
# https://github.com/kas-gui/kas/issues/83
no-default-features = true
features = ["stack_dst"]
1 change: 1 addition & 0 deletions kas-wgpu/examples/filter-list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ mod data {
#[cfg(feature = "generator")]
mod data {
use chrono::{DateTime, Duration, Local};
use kas::conv::Conv;
use kas::widget::view::{Accessor, FilterAccessor};
use std::{cell::RefCell, rc::Rc};

Expand Down
60 changes: 36 additions & 24 deletions kas-wgpu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ pub mod options;
mod shared;
mod window;

use std::{error, fmt};
use std::cell::RefCell;
use std::rc::Rc;
use thiserror::Error;

use kas::event::UpdateHandle;
use kas::WindowId;
Expand All @@ -47,16 +49,21 @@ pub use wgpu_glyph as glyph;
/// Some variants are undocumented. Users should not match these variants since
/// they are not considered part of the public API.
#[non_exhaustive]
#[derive(Debug)]
#[derive(Error, Debug)]
pub enum Error {
/// No suitable graphics adapter found
///
/// This can be a driver/configuration issue or hardware limitation. Note
/// that for now, `wgpu` only supports DX11, DX12, Vulkan and Metal.
#[error("no graphics adapter found")]
NoAdapter,
/// Config load/save error
#[error("config load/save error")]
Config(#[from] kas::event::ConfigError),
#[doc(hidden)]
/// OS error during window creation
Window(OsError),
#[error("operating system error")]
Window(#[from] OsError),
}

impl From<wgpu::RequestDeviceError> for Error {
Expand All @@ -65,23 +72,6 @@ impl From<wgpu::RequestDeviceError> for Error {
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
Error::NoAdapter => write!(f, "no suitable graphics adapter found"),
Error::Window(e) => write!(f, "window creation error: {}", e),
}
}
}

impl error::Error for Error {}

impl From<OsError> for Error {
fn from(ose: OsError) -> Self {
Error::Window(ose)
}
}

/// A toolkit over winit and WebGPU
///
/// All KAS shells are expected to provide a similar `Toolkit` type and API.
Expand All @@ -100,7 +90,7 @@ where
/// Construct a new instance with default options.
///
/// Environment variables may affect option selection; see documentation
/// of [`Options::from_env`].
/// of [`Options::from_env`]. KAS config is provided by [`Options::config`].
#[inline]
pub fn new(theme: T) -> Result<Self, Error> {
Self::new_custom((), theme, Options::from_env())
Expand All @@ -116,20 +106,42 @@ where
/// The `custom` parameter accepts a custom draw pipe (see [`CustomPipeBuilder`]).
/// Pass `()` if you don't have one.
///
/// The [`Options`] parameter allows direct specification of shell
/// options; usually, these are provided by [`Options::from_env`].
/// The [`Options`] parameter allows direct specification of shell options;
/// usually, these are provided by [`Options::from_env`].
/// KAS config is provided by [`Options::config`].
#[inline]
pub fn new_custom<CB: CustomPipeBuilder<Pipe = C>>(
custom: CB,
theme: T,
options: Options,
) -> Result<Self, Error> {
let el = EventLoop::with_user_event();
let config = Rc::new(RefCell::new(options.config()?));
let scale_factor = find_scale_factor(&el);
Ok(Toolkit {
el,
windows: vec![],
shared: SharedState::new(custom, theme, options, config, scale_factor)?,
})
}

/// Construct an instance with custom options and config
///
/// This is like [`Toolkit::new_custom`], but allows KAS config to be
/// specified directly, instead of loading via [`Options::config`].
#[inline]
pub fn new_custom_config<CB: CustomPipeBuilder<Pipe = C>>(
custom: CB,
theme: T,
options: Options,
config: Rc<RefCell<kas::event::Config>>,
) -> Result<Self, Error> {
let el = EventLoop::with_user_event();
let scale_factor = find_scale_factor(&el);
Ok(Toolkit {
el,
windows: vec![],
shared: SharedState::new(custom, theme, options, scale_factor)?,
shared: SharedState::new(custom, theme, options, config, scale_factor)?,
})
}

Expand Down
74 changes: 74 additions & 0 deletions kas-wgpu/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,30 @@

//! Options
use super::Error;
use log::warn;
use std::env::var;
use std::path::PathBuf;
pub use wgpu::{BackendBit, PowerPreference};

/// Config mode
///
/// See [`Options::from_env`] documentation.
#[derive(Clone, PartialEq, Hash)]
pub enum ConfigMode {
/// Read-only mode
Read,
/// Use default config and write out
WriteDefault,
}

/// Shell options
#[derive(Clone, PartialEq, Hash)]
pub struct Options {
/// Config file path. Default: empty. See `KAS_CONFIG` doc.
pub config_path: PathBuf,
/// Config mode. Default: Read.
pub config_mode: ConfigMode,
/// Adapter power preference. Default value: low power.
pub power_preference: PowerPreference,
/// Adapter backend. Default value: PRIMARY (Vulkan/Metal/DX12).
Expand All @@ -21,6 +38,8 @@ pub struct Options {
impl Default for Options {
fn default() -> Self {
Options {
config_path: PathBuf::new(),
config_mode: ConfigMode::Read,
power_preference: PowerPreference::LowPower,
backends: BackendBit::PRIMARY,
}
Expand All @@ -32,6 +51,26 @@ impl Options {
///
/// The following environment variables are read, in case-insensitive mode.
///
/// ### Config
///
/// The `KAS_CONFIG` variable, if given, provides a path to the KAS config
/// file, where configuration can be read and/or written.
///
/// WARNING: file formats are unstable!
///
/// If `KAS_CONFIG` is not set, platform-default configuration is used
/// without reading or writing. This may change to use a platform-specific
/// default path in future versions.
///
/// The `KAS_CONFIG_MODE` variable determines the read/write mode:
///
/// - `Read` (default): read-only
/// - `WriteDefault`: generate platform-default configuration, and write
/// it to the config path, overwriting any existing config
///
/// Note: in the future, the default will likely change to a read-write mode,
/// allowing changes to be written out.
///
/// ### Power preference
///
/// The `KAS_POWER_PREFERENCE` variable supports:
Expand All @@ -54,6 +93,22 @@ impl Options {
pub fn from_env() -> Self {
let mut options = Options::default();

if let Ok(v) = var("KAS_CONFIG") {
options.config_path = v.into();
}

if let Ok(mut v) = var("KAS_CONFIG_MODE") {
v.make_ascii_uppercase();
options.config_mode = match v.as_str() {
"READ" => ConfigMode::Read,
"WRITEDEFAULT" => ConfigMode::WriteDefault,
other => {
warn!("Unexpected environment value: KAS_CONFIG_MODE={}", other);
options.config_mode
}
};
}

if let Ok(mut v) = var("KAS_POWER_PREFERENCE") {
v.make_ascii_uppercase();
options.power_preference = match v.as_str() {
Expand Down Expand Up @@ -100,4 +155,23 @@ impl Options {
pub(crate) fn backend(&self) -> BackendBit {
self.backends
}

/// Load KAS config
pub fn config(&self) -> Result<kas::event::Config, Error> {
if !self.config_path.as_os_str().is_empty() {
match self.config_mode {
ConfigMode::Read => Ok(kas::event::Config::from_path(
&self.config_path,
Default::default(),
)?),
ConfigMode::WriteDefault => {
let config: kas::event::Config = Default::default();
config.write_path(&self.config_path, Default::default())?;
Ok(config)
}
}
} else {
Ok(Default::default())
}
}
}
8 changes: 6 additions & 2 deletions kas-wgpu/src/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
//! Shared state
use log::{info, warn};
use std::cell::RefCell;
use std::num::NonZeroU32;
use std::rc::Rc;

use crate::draw::{CustomPipe, CustomPipeBuilder, DrawPipe, DrawWindow, ShaderManager};
use crate::{Error, Options, WindowId};
use kas::event::UpdateHandle;
use kas_theme::Theme;

#[cfg(feature = "clipboard")]
Expand All @@ -26,6 +27,7 @@ pub struct SharedState<C: CustomPipe, T> {
pub shaders: ShaderManager,
pub draw: DrawPipe<C>,
pub theme: T,
pub config: Rc<RefCell<kas::event::Config>>,
pub pending: Vec<PendingAction>,
/// Newly created windows need to know the scale_factor *before* they are
/// created. This is used to estimate ideal window size.
Expand All @@ -42,6 +44,7 @@ where
custom: CB,
mut theme: T,
options: Options,
config: Rc<RefCell<kas::event::Config>>,
scale_factor: f64,
) -> Result<Self, Error> {
#[cfg(feature = "clipboard")]
Expand Down Expand Up @@ -84,6 +87,7 @@ where
shaders,
draw,
theme,
config,
pending: vec![],
scale_factor,
window_id: 0,
Expand Down Expand Up @@ -148,5 +152,5 @@ pub enum PendingAction {
CloseWindow(WindowId),
ThemeResize,
RedrawAll,
Update(UpdateHandle, u64),
Update(kas::event::UpdateHandle, u64),
}
Loading

0 comments on commit 6578ae4

Please sign in to comment.