From 7cdd1876d116094a6fb926ea4b89a7da765ddda7 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Thu, 14 Mar 2019 07:58:48 -0700 Subject: [PATCH] Enable serializing various Line and Cell related structs --- Cargo.toml | 2 +- src/config.rs | 1 + src/font/mod.rs | 1 + src/frontend/mod.rs | 1 + src/main.rs | 2 -- src/server/codec.rs | 30 ++++++++++++++++++++++++++++++ term/Cargo.toml | 2 ++ term/src/lib.rs | 3 ++- termwiz/Cargo.toml | 5 ++++- termwiz/src/cell.rs | 35 +++++++++++++++++++++++++++++------ termwiz/src/color.rs | 33 +++++++++++++++++++++++++++++++-- termwiz/src/hyperlink.rs | 3 ++- termwiz/src/image.rs | 31 ++++++++++++++++++++++++++++--- termwiz/src/surface/change.rs | 4 ++-- termwiz/src/surface/line.rs | 3 ++- termwiz/src/surface/mod.rs | 4 ++-- 16 files changed, 138 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8f0b73f35..214432f22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ failure_derive = "~0.1" gl = "~0.11" libc = "~0.2" palette = "~0.4" -serde = "~1.0" +serde = {version="~1.0", features = ["rc"]} serde_derive = "~1.0" toml = "~0.4" unicode-width = "~0.1" diff --git a/src/config.rs b/src/config.rs index b65b47836..7fef2406a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,6 +5,7 @@ use crate::frontend::FrontEndSelection; use crate::{get_shell, Command}; use failure::{err_msg, Error}; use lazy_static::lazy_static; +use serde_derive::*; use std; use std::ffi::OsStr; use std::fs; diff --git a/src/font/mod.rs b/src/font/mod.rs index 85c222561..7b0429622 100644 --- a/src/font/mod.rs +++ b/src/font/mod.rs @@ -1,4 +1,5 @@ use failure::Error; +use serde_derive::*; mod ftfont; #[cfg(unix)] mod hbwrap; diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index 1d9495cf2..9f1d5a30a 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -4,6 +4,7 @@ use crate::mux::tab::Tab; use crate::mux::Mux; use failure::Error; use promise::Executor; +use serde_derive::*; use std::rc::Rc; use std::sync::Arc; diff --git a/src/main.rs b/src/main.rs index 5eb6ad45a..ea4342dc3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,8 +3,6 @@ extern crate failure; #[macro_use] extern crate failure_derive; #[macro_use] -extern crate serde_derive; -#[macro_use] pub mod log; use failure::Error; use std::ffi::OsString; diff --git a/src/server/codec.rs b/src/server/codec.rs index 07f487c41..2581806fc 100644 --- a/src/server/codec.rs +++ b/src/server/codec.rs @@ -13,7 +13,11 @@ use crate::mux::tab::TabId; use bincode; use failure::Error; +use serde_derive::*; use std::collections::HashMap; +use std::rc::Rc; +use term::{CursorPosition, Line}; +use termwiz::hyperlink::Hyperlink; use varu64; fn encode_raw( @@ -148,6 +152,32 @@ pub struct ListTabsResponse { pub tabs: HashMap, } +/// This is a transitional request to get some basic +/// remoting working. The ideal is to produce Change +/// objects instead of the coarse response data in +/// GetCoarseTabRenderableDataResponse +#[derive(Deserialize, Serialize, PartialEq, Debug)] +pub struct GetCoarseTabRenderableData { + pub tab_id: TabId, +} + +#[derive(Deserialize, Serialize, PartialEq, Debug)] +pub struct DirtyLine { + pub line_idx: usize, + pub line: Line, + pub selection_col_from: usize, + pub selection_col_to: usize, +} + +#[derive(Deserialize, Serialize, PartialEq, Debug)] +pub struct GetCoarseTabRenderableDataResponse { + pub cursor_position: CursorPosition, + pub physical_rows: usize, + pub physical_cols: usize, + pub current_highlight: Option>, + pub dirty_lines: Vec, +} + #[cfg(test)] mod test { use super::*; diff --git a/term/Cargo.toml b/term/Cargo.toml index 59e5aeb9c..d9e4eaee2 100644 --- a/term/Cargo.toml +++ b/term/Cargo.toml @@ -11,6 +11,8 @@ image = "~0.19" ordered-float = "~0.5" unicode-segmentation = "~1.2" unicode-width = "~0.1" +serde = {version="~1.0", features = ["rc"]} +serde_derive = "~1.0" [dependencies.termwiz] path = "../termwiz" diff --git a/term/src/lib.rs b/term/src/lib.rs index e957c5f54..369ababff 100644 --- a/term/src/lib.rs +++ b/term/src/lib.rs @@ -3,6 +3,7 @@ extern crate bitflags; #[macro_use] extern crate failure; +use serde_derive::*; use failure::Error; use std::ops::{Deref, DerefMut, Range}; @@ -85,7 +86,7 @@ pub enum Position { /// Describes the location of the cursor in the visible portion /// of the screen. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Deserialize, Serialize)] pub struct CursorPosition { pub x: usize, pub y: VisibleRowIndex, diff --git a/termwiz/Cargo.toml b/termwiz/Cargo.toml index edf7c5b26..2fe1039d4 100644 --- a/termwiz/Cargo.toml +++ b/termwiz/Cargo.toml @@ -20,7 +20,7 @@ ordered-float = "~0.5" palette = "~0.4" regex = "~0.2" semver = "0.9" -serde = "~1.0" +serde = {version="~1.0", features = ["rc"]} serde_derive = "~1.0" smallvec = "~0.6" terminfo = "~0.6" @@ -28,6 +28,9 @@ unicode-segmentation = "~1.2" unicode-width = "~0.1" vte = "~0.3" +[dev-dependencies] +bincode = "1.1" + [dependencies.num-derive] features = ["full-syntax"] version = "~0.2" diff --git a/termwiz/src/cell.rs b/termwiz/src/cell.rs index bfe3a45e3..a9de752ed 100644 --- a/termwiz/src/cell.rs +++ b/termwiz/src/cell.rs @@ -2,6 +2,7 @@ use crate::color::ColorAttribute; pub use crate::escape::osc::Hyperlink; use crate::image::ImageCell; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use smallvec::SmallVec; use std; use std::mem; @@ -13,7 +14,7 @@ use unicode_width::UnicodeWidthStr; /// to reduce per-cell overhead. /// The setter methods return a mutable self reference so that they can /// be chained together. -#[derive(Debug, Default, Clone, Eq, PartialEq)] +#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct CellAttributes { attributes: u16, /// The foreground color @@ -84,7 +85,7 @@ macro_rules! bitfield { /// implement `Intensity::Bold` by either using a bold font or by simply /// using an alternative color. Some terminals implement `Intensity::Half` /// as a dimmer color variant. -#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[repr(u16)] pub enum Intensity { Normal = 0, @@ -93,7 +94,7 @@ pub enum Intensity { } /// Specify just how underlined you want your `Cell` to be -#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[repr(u16)] pub enum Underline { /// The cell is not underlined @@ -114,7 +115,7 @@ impl Into for Underline { } /// Specify whether you want to slowly or rapidly annoy your users -#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[repr(u16)] pub enum Blink { None = 0, @@ -181,9 +182,31 @@ impl CellAttributes { } } +fn deserialize_smallvec<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let text = String::deserialize(deserializer)?; + Ok(SmallVec::from_slice(text.as_bytes())) +} + +fn serialize_smallvec(value: &SmallVec<[u8; 4]>, serializer: S) -> Result +where + S: Serializer, +{ + // unsafety: this is safe because the Cell constructor guarantees + // that the storage is valid utf8 + let s = unsafe { std::str::from_utf8_unchecked(value) }; + s.serialize(serializer) +} + /// Models the contents of a cell on the terminal display -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] pub struct Cell { + #[serde( + deserialize_with = "deserialize_smallvec", + serialize_with = "serialize_smallvec" + )] text: SmallVec<[u8; 4]>, attrs: CellAttributes, } @@ -275,7 +298,7 @@ impl Cell { /// Models a change in the attributes of a cell in a stream of changes. /// Each variant specifies one of the possible attributes; the corresponding /// value holds the new value to be used for that attribute. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum AttributeChange { Intensity(Intensity), Underline(Underline), diff --git a/termwiz/src/color.rs b/termwiz/src/color.rs index c0e28b08b..921d37d9f 100644 --- a/termwiz/src/color.rs +++ b/termwiz/src/color.rs @@ -4,7 +4,7 @@ use palette; use palette::{LinSrgba, Srgb, Srgba}; -use serde::{self, Deserialize, Deserializer}; +use serde::{self, Deserialize, Deserializer, Serialize, Serializer}; use std::result::Result; #[derive(Debug, Clone, Copy, FromPrimitive)] @@ -97,6 +97,11 @@ impl RgbColor { }) } + /// Returns a string of the form `#RRGGBB` + pub fn to_rgb_string(&self) -> String { + format!("#{:02x}{:02x}{:02x}", self.red, self.green, self.blue) + } + /// Construct a color from a string of the form `#RRGGBB` where /// R, G and B are all hex digits. pub fn from_rgb_str(s: &str) -> Option { @@ -123,6 +128,23 @@ impl RgbColor { } } +/// This is mildly unfortunate: in order to round trip RgbColor with serde +/// we need to provide a Serialize impl equivalent to the Deserialize impl +/// below. We use the impl below to allow more flexible specification of +/// color strings in the config file. A side effect of doing it this way +/// is that we have to serialize RgbColor as a 7-byte string when we could +/// otherwise serialize it as a 3-byte array. There's probably a way +/// to make this work more efficiently, but for now this will do. +impl Serialize for RgbColor { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = self.to_rgb_string(); + s.serialize(serializer) + } +} + impl<'de> Deserialize<'de> for RgbColor { fn deserialize(deserializer: D) -> Result where @@ -173,7 +195,7 @@ impl From for ColorSpec { /// type used in the `CellAttributes` struct and can specify an optional /// TrueColor value, allowing a fallback to a more traditional palette /// index if TrueColor is not available. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] pub enum ColorAttribute { /// Use RgbColor when supported, falling back to the specified PaletteIndex. TrueColorWithPaletteFallback(RgbColor, PaletteIndex), @@ -217,4 +239,11 @@ mod tests { assert_eq!(dark_green.green, 0x64); assert_eq!(dark_green.blue, 0); } + + #[test] + fn roundtrip_rgbcolor() { + let data = bincode::serialize(&RgbColor::from_named("DarkGreen").unwrap()).unwrap(); + eprintln!("serialized as {:?}", data); + let decoded: RgbColor = bincode::deserialize(&data).unwrap(); + } } diff --git a/termwiz/src/hyperlink.rs b/termwiz/src/hyperlink.rs index 0d148a90a..829e8e85a 100644 --- a/termwiz/src/hyperlink.rs +++ b/termwiz/src/hyperlink.rs @@ -7,12 +7,13 @@ use failure::{err_msg, Error}; use regex::{Captures, Regex}; use serde::{self, Deserialize, Deserializer}; +use serde_derive::*; use std::collections::HashMap; use std::fmt::{Display, Error as FmtError, Formatter}; use std::ops::Range; use std::rc::Rc; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Hyperlink { params: HashMap, uri: String, diff --git a/termwiz/src/image.rs b/termwiz/src/image.rs index c8bf986bd..b1729ba20 100644 --- a/termwiz/src/image.rs +++ b/termwiz/src/image.rs @@ -12,11 +12,36 @@ // z-order. use ordered_float::NotNaN; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_derive::*; use std::rc::Rc; -#[derive(Debug, Clone, PartialEq, Eq)] +fn deserialize_notnan<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let value = f32::deserialize(deserializer)?; + NotNaN::new(value).map_err(|e| serde::de::Error::custom(format!("{:?}", e))) +} + +fn serialize_notnan(value: &NotNaN, serializer: S) -> Result +where + S: Serializer, +{ + value.into_inner().serialize(serializer) +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct TextureCoordinate { + #[serde( + deserialize_with = "deserialize_notnan", + serialize_with = "serialize_notnan" + )] pub x: NotNaN, + #[serde( + deserialize_with = "deserialize_notnan", + serialize_with = "serialize_notnan" + )] pub y: NotNaN, } @@ -37,7 +62,7 @@ impl TextureCoordinate { /// carve up the image and track each slice of it. Each cell needs to know /// its "texture coordinates" within that image so that we can render the /// right slice. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ImageCell { /// Texture coordinate for the top left of this cell. /// (0,0) is the top left of the ImageData. (1, 1) is @@ -65,7 +90,7 @@ impl ImageCell { static IMAGE_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct ImageData { id: usize, /// The image data bytes. Data is the native image file format diff --git a/termwiz/src/surface/change.rs b/termwiz/src/surface/change.rs index cde90f156..315db7193 100644 --- a/termwiz/src/surface/change.rs +++ b/termwiz/src/surface/change.rs @@ -7,7 +7,7 @@ use std::rc::Rc; /// `Change` describes an update operation to be applied to a `Surface`. /// Changes to the active attributes (color, style), moving the cursor /// and outputting text are examples of some of the values. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum Change { /// Change a single attribute Attribute(AttributeChange), @@ -90,7 +90,7 @@ impl From for Change { /// A 4x3 cell image would set `width=3`, `height=3`, `top_left=(0,0)`, `bottom_right=(1,1)`. /// The top left cell from that image, if it were to be included in a diff, /// would be recorded as `width=1`, `height=1`, `top_left=(0,0)`, `bottom_right=(1/4,1/3)`. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct Image { /// measured in cells pub width: usize, diff --git a/termwiz/src/surface/line.rs b/termwiz/src/surface/line.rs index b4314bf0b..89cc19780 100644 --- a/termwiz/src/surface/line.rs +++ b/termwiz/src/surface/line.rs @@ -8,6 +8,7 @@ use std::rc::Rc; use unicode_segmentation::UnicodeSegmentation; bitflags! { + #[derive(Serialize, Deserialize)] struct LineBits : u8 { const NONE = 0; /// The contents of the Line have changed and cached or @@ -22,7 +23,7 @@ bitflags! { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Line { bits: LineBits, cells: Vec, diff --git a/termwiz/src/surface/mod.rs b/termwiz/src/surface/mod.rs index d0836c775..1c82af659 100644 --- a/termwiz/src/surface/mod.rs +++ b/termwiz/src/surface/mod.rs @@ -17,7 +17,7 @@ pub use self::line::Line; /// Relative(0) is the current position in the line or /// column and EndRelative(0) is the end position in the /// line or column. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum Position { NoChange, /// Negative values move up, positive values down @@ -28,7 +28,7 @@ pub enum Position { EndRelative(usize), } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum CursorShape { Hidden, Default,