ming_wm_lib/
serialize.rs

1use std::fmt::Display;
2
3use crate::themes::ThemeInfo;
4use crate::messages::{ WindowMessageResponse, WindowManagerRequest, KeyPress, WindowMessage, Direction, ShortcutType, InfoType };
5use crate::window_manager_types::{ KeyChar, DrawInstructions, WindowLikeType };
6use crate::framebuffer_types::Dimensions;
7use crate::utils::get_rest_of_split;
8
9//serde + ron but worse! yay
10//not same as ron - simplified
11//very messy
12
13//todo: bug with extra byte when copy/pasting because of this... maybe it's the newline or something?
14
15//I can't do `impl fmt::Display for RGBColor` which is annoying
16
17//to type \x1F, do ctrl+7, for \x1E, do ctrl+6
18
19fn array_to_string<T: Display>(array: &[T]) -> String {
20  let mut output = String::new();
21  for item in array {
22    output += &format!("{}{}", if output == String::new() {
23      ""
24    } else {
25      "\x1F"
26    }, item);
27  }
28  output
29}
30
31fn option_to_string<T: Display>(option: &Option<T>) -> String {
32  if let Some(value) = option {
33    format!("S{}", value)
34  } else {
35    "N".to_string()
36  }
37}
38
39fn get_color(serialized: &str) -> Result<[u8; 3], ()> {
40  let rgb = serialized.split("\x1F");
41  let mut color = [0; 3];
42  //won't return error if rgb is 0, 1, or 2 elements.
43  //I guess that's okay, since it doesn't panic either
44  //c_i is the loop counter. enumerate(), you are awesome
45  for (c_i, c) in rgb.enumerate() {
46    if c_i == 3 {
47      return Err(());
48    }
49    if let Ok(c) = c.parse() {
50      color[c_i] = c;
51    } else {
52      return Err(());
53    }
54  }
55  Ok(color)
56}
57
58fn get_two_array(serialized: &str) -> Result<[usize; 2], ()> {
59  let mut arg = serialized.split("\x1F");
60  let mut a = [0; 2];
61  for i in 0..2 {
62    if let Some(n) = arg.next() {
63      if let Ok(n) = n.parse() {
64        a[i] = n;
65        continue
66      }
67    }
68    return Err(());
69  }
70  Ok(a)
71}
72
73pub trait Serializable {
74  fn serialize(&self) -> String;
75  fn deserialize(serialized: &str) -> Result<Self, ()> where Self: Sized;
76}
77
78//ripe for macros when I figure them out
79
80impl Serializable for ThemeInfo {
81  fn serialize(&self) -> String {
82    format!("{}:{}:{}:{}:{}:{}:{}:{}:{}", array_to_string(&self.top), array_to_string(&self.background), array_to_string(&self.border_left_top), array_to_string(&self.border_right_bottom), array_to_string(&self.text), array_to_string(&self.top_text), array_to_string(&self.alt_background), array_to_string(&self.alt_text), array_to_string(&self.alt_secondary))
83  }
84  fn deserialize(serialized: &str) -> Result<Self, ()> {
85    //strip newline at the end
86    let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
87    let mut theme_info: ThemeInfo = Default::default();
88    let arrays = serialized.split(":");
89    //won't error or panic if less than 9... rest will just be black by default I guess
90    for (a_i, a) in arrays.enumerate() {
91      if a_i == 9 {
92        return Err(());
93      }
94      let color = get_color(a)?;
95      match a_i {
96        0 => {
97          theme_info.top = color;
98        },
99        1 => {
100          theme_info.background = color;
101        },
102        2 => {
103          theme_info.border_left_top = color;
104        },
105        3 => {
106          theme_info.border_right_bottom = color;
107        },
108        4 => {
109          theme_info.text = color;
110        },
111        5 => {
112          theme_info.top_text = color;
113        },
114        6 => {
115          theme_info.alt_background = color;
116        },
117        7 => {
118          theme_info.alt_text = color;
119        },
120        8 => {
121          theme_info.alt_secondary = color;
122        },
123        _ => {},
124      };
125      if a_i == 8 {
126        return Ok(theme_info);
127      }
128    }
129    Err(())
130  }
131}
132
133#[test]
134fn theme_info_serialize_deserialize() {
135  use crate::themes::get_theme_info;
136  let theme_info = get_theme_info(&Default::default()).unwrap();
137  let serialized = theme_info.serialize();
138  assert!(serialized == ThemeInfo::deserialize(&serialized).unwrap().serialize());
139}
140
141impl Serializable for WindowMessageResponse {
142  fn serialize(&self) -> String {
143    match self {
144      WindowMessageResponse::JustRedraw => "JustRedraw".to_string(),
145      WindowMessageResponse::DoNothing => "DoNothing".to_string(),
146      WindowMessageResponse::Request(req) => {
147        let req = match req {
148          WindowManagerRequest::OpenWindow(name) => format!("OpenWindow/{}", name),
149          WindowManagerRequest::ClipboardCopy(copy_string) => format!("ClipboardCopy/{}", copy_string.replace("\n", "𐘂")), //serialised output must be 1 line
150          WindowManagerRequest::CloseStartMenu => "CloseStartMenu".to_string(),
151          WindowManagerRequest::Unlock => "Unlock".to_string(),
152          WindowManagerRequest::Lock => "Lock".to_string(),
153          WindowManagerRequest::DoKeyChar(kc) => format!("DoKeyChar/{}", match kc {
154            KeyChar::Press(c) => format!("Press/{}", c),
155            KeyChar::Alt(c) => format!("Alt/{}", c),
156            KeyChar::Ctrl(c) => format!("Ctrl/{}", c),
157          }),
158        };
159        format!("Request/{}", req)
160      },
161    }
162  }
163  fn deserialize(serialized: &str) -> Result<Self, ()> {
164    //strip newline at the end
165    let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
166    let mut parts = serialized.split("/");
167    match parts.next().unwrap_or("Invalid") {
168      "JustRedraw" => Ok(WindowMessageResponse::JustRedraw),
169      "DoNothing" => Ok(WindowMessageResponse::DoNothing),
170      "Request" => {
171        let req = match parts.next().unwrap_or("Invalid") {
172          //do get_rest_of_split instead of .next() because it is possible for window name or copy to have "/"
173          "OpenWindow" => Some(WindowManagerRequest::OpenWindow(get_rest_of_split(&mut parts, Some("/")))),
174          "ClipboardCopy" => Some(WindowManagerRequest::ClipboardCopy(get_rest_of_split(&mut parts, Some("/")))),
175          "CloseStartMenu" => Some(WindowManagerRequest::CloseStartMenu),
176          "Unlock" => Some(WindowManagerRequest::Unlock),
177          "Lock" => Some(WindowManagerRequest::Lock),
178          "DoKeyChar" => Some(WindowManagerRequest::DoKeyChar(
179            match parts.next().unwrap_or("Invalid") {
180              "Press" => KeyChar::Press(parts.next().unwrap_or("?").chars().next().unwrap()),
181              "Alt" => KeyChar::Alt(parts.next().unwrap_or("?").chars().next().unwrap()),
182              "Ctrl" => KeyChar::Ctrl(parts.next().unwrap_or("?").chars().next().unwrap()),
183              _ => KeyChar::Press('?'), //yeah.
184            }
185          )),
186          _ => None, //yeah...
187        };
188        if let Some(req) = req {
189          Ok(WindowMessageResponse::Request(req))
190        } else {
191          Err(())
192        }
193      },
194      _ => Err(()),
195    }
196  }
197}
198
199#[test]
200fn window_message_response_serialize_deserialize() {
201  let resp = WindowMessageResponse::JustRedraw;
202  let serialized = resp.serialize();
203  assert!(resp == WindowMessageResponse::deserialize(&serialized).unwrap());
204  let resp = WindowMessageResponse::Request(WindowManagerRequest::OpenWindow("a".to_string()));
205  let serialized = resp.serialize();
206  assert!(resp == WindowMessageResponse::deserialize(&serialized).unwrap());
207  let resp = WindowMessageResponse::Request(WindowManagerRequest::Unlock);
208  let serialized = resp.serialize();
209  assert!(resp == WindowMessageResponse::deserialize(&serialized).unwrap());
210  let resp = WindowMessageResponse::Request(WindowManagerRequest::DoKeyChar(KeyChar::Alt('e')));
211  let serialized = resp.serialize();
212  assert!(resp == WindowMessageResponse::deserialize(&serialized).unwrap());
213}
214
215impl Serializable for DrawInstructions {
216  fn serialize(&self) -> String {
217    match self {
218      //use \x1E (record separator) because it won't be in strings. it better fucking not be at least
219      DrawInstructions::Rect(p, d, c) => format!("Rect/{}\x1E{}\x1E{}", array_to_string(p), array_to_string(d), array_to_string(c)),
220      DrawInstructions::Text(p, vs, s, c1, c2, ou1, ou2) => format!("Text/{}\x1E{}\x1E{}\x1E{}\x1E{}\x1E{}\x1E{}", array_to_string(p), array_to_string(vs), s, array_to_string(c1), array_to_string(c2), option_to_string(ou1), option_to_string(ou2)),
221      DrawInstructions::Gradient(p, d, c1, c2, u) => format!("Gradient/{}\x1E{}\x1E{}\x1E{}\x1E{}", array_to_string(p), array_to_string(d), array_to_string(c1), array_to_string(c2), u),
222      DrawInstructions::Bmp(p, s, b) => format!("Bmp/{}\x1E{}\x1E{}", array_to_string(p), s, b),
223      DrawInstructions::Circle(p, u, c) => format!("Circle/{}\x1E{}\x1E{}", array_to_string(p), u, array_to_string(c)),
224      DrawInstructions::Line(s, e, w, c) => format!("Line/{}\x1E{}\x1E{}\x1E{}", array_to_string(s), array_to_string(e), w, array_to_string(c)),
225    }
226  }
227  fn deserialize(serialized: &str) -> Result<Self, ()> {
228    //no need to strip newlines cause the impl for Vec<DrawInstructions> does that for us
229    let mut parts = serialized.split("/");
230    match parts.next().unwrap_or("Invalid") {
231      "Rect" => {
232        let rest = get_rest_of_split(&mut parts, Some("/"));
233        let mut args = rest.split("\x1E");
234        let arg = args.next();
235        if arg.is_none() {
236          return Err(());
237        }
238        let p = get_two_array(arg.unwrap())?;
239        let arg = args.next();
240        if arg.is_none() {
241          return Err(());
242        }
243        let d = get_two_array(arg.unwrap())?;
244        let arg = args.next();
245        if arg.is_none() {
246          return Err(());
247        }
248        let c = get_color(arg.unwrap())?;
249        Ok(DrawInstructions::Rect(p, d, c))
250      },
251      "Text" => {
252        let rest = get_rest_of_split(&mut parts, Some("/"));
253        //(p, vs, s, c1, c2, ou1, ou2)
254        let mut args = rest.split("\x1E");
255        let arg = args.next();
256        if arg.is_none() {
257          return Err(());
258        }
259        let p = get_two_array(arg.unwrap())?;
260        let arg = args.next();
261        if arg.is_none() {
262          return Err(());
263        }
264        let mut vs = Vec::new();
265        for s in arg.unwrap().split("\x1F") {
266          vs.push(s.to_string());
267        }
268        let arg = args.next();
269        if arg.is_none() {
270          return Err(());
271        }
272        let s = arg.unwrap();
273        let arg = args.next();
274        if arg.is_none() {
275          return Err(());
276        }
277        let c1 = get_color(arg.unwrap())?;
278        let arg = args.next();
279        if arg.is_none() {
280          return Err(());
281        }
282        let c2 = get_color(arg.unwrap())?;
283        let arg = args.next();
284        if arg.is_none() {
285          return Err(());
286        }
287        let arg = arg.unwrap();
288        let o1 = match arg {
289          "N" => None,
290          _ => {
291            if arg.len() > 1 {
292              if let Ok(n) = arg[1..].parse() {
293                Some(n)
294              } else {
295                None
296              }
297            } else {
298              None
299            }
300          },
301        };
302        let arg = args.next();
303        if arg.is_none() {
304          return Err(());
305        }
306        let arg = arg.unwrap();
307        let o2 = match arg {
308          "N" => None,
309          _ => {
310            if arg.len() > 1 {
311              if let Ok(n) = arg[1..].parse() {
312                Some(n)
313              } else {
314                None
315              }
316            } else {
317              None
318            }
319          },
320        };
321        Ok(DrawInstructions::Text(p, vs, s.to_string(), c1, c2, o1, o2))
322      },
323      "Gradient" => {
324        let rest = get_rest_of_split(&mut parts, Some("/"));
325        //(p, d, c1, c2, u)
326        let mut args = rest.split("\x1E");
327        let arg = args.next();
328        if arg.is_none() {
329          return Err(());
330        }
331        let p = get_two_array(arg.unwrap())?;
332        let arg = args.next();
333        if arg.is_none() {
334          return Err(());
335        }
336        let d = get_two_array(arg.unwrap())?;
337        let arg = args.next();
338        if arg.is_none() {
339          return Err(());
340        }
341        let c1 = get_color(arg.unwrap())?;
342        let arg = args.next();
343        if arg.is_none() {
344          return Err(());
345        }
346        let c2 = get_color(arg.unwrap())?;
347        let arg = args.next();
348        if arg.is_none() {
349          return Err(());
350        }
351        let u = arg.unwrap().parse();
352        if u.is_err() {
353          return Err(());
354        }
355        Ok(DrawInstructions::Gradient(p, d, c1, c2, u.unwrap()))
356      },
357      "Bmp" => {
358        let rest = get_rest_of_split(&mut parts, Some("/"));
359        //(p, s, b)
360        let mut args = rest.split("\x1E");
361        let arg = args.next();
362        if arg.is_none() {
363          return Err(());
364        }
365        let p = get_two_array(arg.unwrap())?;
366        let arg = args.next();
367        if arg.is_none() {
368          return Err(());
369        }
370        let s = arg.unwrap();
371        let arg = args.next();
372        if arg.is_none() {
373          return Err(());
374        }
375        let arg = arg.unwrap();
376        if arg != "true" && arg != "false" {
377          return Err(());
378        }
379        let b = arg == "true";
380        Ok(DrawInstructions::Bmp(p, s.to_string(), b))
381      },
382      "Circle" => {
383        let rest = get_rest_of_split(&mut parts, Some("/"));
384        //(p, u, c)
385        let mut args = rest.split("\x1E");
386        let arg = args.next();
387        if arg.is_none() {
388          return Err(());
389        }
390        let p = get_two_array(arg.unwrap())?;
391        let arg = args.next();
392        if arg.is_none() {
393          return Err(());
394        }
395        let u = arg.unwrap().parse();
396        if u.is_err() {
397          return Err(());
398        }
399        let arg = args.next();
400        if arg.is_none() {
401          return Err(());
402        }
403        let c = get_color(arg.unwrap())?;
404        Ok(DrawInstructions::Circle(p, u.unwrap(), c))
405      },
406      "Line" => {
407        let rest = get_rest_of_split(&mut parts, Some("/"));
408        //(s, e, w, c)
409        let mut args = rest.split("\x1E");
410        let arg = args.next();
411        if arg.is_none() {
412          return Err(());
413        }
414        let s = get_two_array(arg.unwrap())?;
415        let arg = args.next();
416        if arg.is_none() {
417          return Err(());
418        }
419        let e = get_two_array(arg.unwrap())?;
420        let arg = args.next();
421        if arg.is_none() {
422          return Err(());
423        }
424        let w = arg.unwrap().parse();
425        if w.is_err() {
426          return Err(());
427        }
428        let arg = args.next();
429        if arg.is_none() {
430          return Err(());
431        }
432        let c = get_color(arg.unwrap())?;
433        Ok(DrawInstructions::Line(s, e, w.unwrap(), c))
434      },
435      _ => Err(()),
436    }
437  }
438}
439
440pub type DrawInstructionsVec = Vec<DrawInstructions>;
441
442impl Serializable for DrawInstructionsVec {
443  fn serialize(&self) -> String {
444    if self.len() == 0 {
445      return "empty".to_string();
446    }
447    let collected: Vec<_> = self.into_iter().map(|ins| ins.serialize()).collect();
448    collected.join("\x1D")
449  }
450  fn deserialize(serialized: &str) -> Result<Self, ()> {
451    //strip newline at the end
452    let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
453    if serialized == "empty" {
454      return Ok(Vec::new());
455    }
456    let mut instructions = Vec::new();
457    for ser_ins in serialized.split("\x1D") {
458      if let Ok(ser_ins) = DrawInstructions::deserialize(ser_ins) {
459        instructions.push(ser_ins);
460      } else {
461        return Err(());
462      }
463    }
464    Ok(instructions)
465  }
466}
467
468#[test]
469fn draw_instructions_serialize_deserialize() {
470  use std::vec;
471  let instructions = vec![
472    DrawInstructions::Rect([15, 24], [100, 320], [255, 0, 128]),
473    DrawInstructions::Text([0, 158], vec!["nimbus-roman".to_string(), "shippori-mincho".to_string()], "Test test 1234 testing\nmictest / mictest is this thing\non?".to_string(), [12, 36, 108], [128, 128, 128], Some(1), None),
474    DrawInstructions::Gradient([0, 500], [750, 125], [255, 255, 255], [0, 0, 0], 12),
475    DrawInstructions::Text([123, 999], vec!["nimbus-romono".to_string()], "print!(\"{}\", variable_name);".to_string(), [12, 36, 108], [128, 128, 128], Some(44), Some(200)),
476    DrawInstructions::Bmp([55, 98], "mingde".to_string(), true),
477    DrawInstructions::Bmp([55, 98], "wooooo".to_string(), false),
478    DrawInstructions::Circle([0, 1], 19, [128, 128, 128]),
479  ];
480  let serialized = instructions.serialize();
481  assert!(serialized == DrawInstructionsVec::deserialize(&serialized).unwrap().serialize());
482  let instructions = vec![
483    DrawInstructions::Rect([0, 0], [410, 410], [0, 0, 0]),
484    DrawInstructions::Text([4, 4], vec!["nimbus-romono".to_string()], "Mingde Terminal".to_string(), [255, 255, 255], [0, 0, 0], Some(0), Some(10)),
485    DrawInstructions::Text([4, 34], vec!["nimbus-romono".to_string()], "$ a".to_string(), [255, 255, 255], [0, 0, 0], Some(0), Some(10)),
486  ];
487  let serialized = instructions.serialize() + "\n";
488  assert!(serialized[..serialized.len() - 1] == DrawInstructionsVec::deserialize(&serialized).unwrap().serialize());
489  let instructions = Vec::new();
490  let serialized = instructions.serialize() + "\n";
491  assert!(DrawInstructionsVec::deserialize(&serialized).unwrap().len() == 0);
492}
493
494impl Serializable for WindowLikeType {
495  fn serialize(&self) -> String {
496    match self {
497      WindowLikeType::LockScreen => "LockScreen".to_string(),
498      WindowLikeType::Window => "Window".to_string(),
499      WindowLikeType::DesktopBackground => "DesktopBackground".to_string(),
500      WindowLikeType::Taskbar => "Taskbar".to_string(),
501      WindowLikeType::StartMenu => "StartMenu".to_string(),
502      WindowLikeType::WorkspaceIndicator => "WorkspaceIndicator".to_string(),
503      WindowLikeType::OnscreenKeyboard => "OnscreenKeyboard".to_string(),
504    }
505  }
506  fn deserialize(serialized: &str) -> Result<Self, ()> {
507    let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
508    match serialized {
509      "LockScreen" => Ok(WindowLikeType::LockScreen),
510      "Window" => Ok(WindowLikeType::Window),
511      "DesktopBackground" => Ok(WindowLikeType::DesktopBackground),
512      "Taskbar" => Ok(WindowLikeType::Taskbar),
513      "StartMenu" => Ok(WindowLikeType::StartMenu),
514      "WorkspaceIndicator" => Ok(WindowLikeType::WorkspaceIndicator),
515      "OnscreenKeyboard" => Ok(WindowLikeType::OnscreenKeyboard),
516      _ => Err(()),
517    }
518  }
519}
520
521#[test]
522fn window_like_type_serialize_deserialize() {
523  let wl_type = WindowLikeType::Window;
524  let serialized = wl_type.serialize();
525  assert!(serialized == WindowLikeType::deserialize(&serialized).unwrap().serialize());
526}
527
528impl Serializable for Dimensions {
529  fn serialize(&self) -> String {
530    array_to_string(self)
531  }
532  fn deserialize(serialized: &str) -> Result<Self, ()> {
533    //strip newline at the end
534    let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
535    let d = get_two_array(serialized)?;
536    Ok(d)
537  }
538}
539
540impl Serializable for WindowMessage {
541  fn serialize(&self) -> String {
542    match self {
543      WindowMessage::Init(d) => format!("Init/{}", array_to_string(d)),
544      WindowMessage::KeyPress(kp) => format!("KeyPress/{}", kp.key),
545      WindowMessage::CtrlKeyPress(kp) => format!("CtrlKeyPress/{}", kp.key),
546      WindowMessage::Shortcut(st) => format!("Shortcut/{}", match st {
547        ShortcutType::StartMenu => "StartMenu".to_string(),
548        ShortcutType::SwitchWorkspace(u) => format!("SwitchWorkspace/{}", u),
549        ShortcutType::MoveWindowToWorkspace(u) => format!("MoveWindowToWorkspace/{}", u),
550        ShortcutType::FocusPrevWindow => "FocusPrevWindow".to_string(),
551        ShortcutType::FocusNextWindow => "FocusNextWindow".to_string(),
552        ShortcutType::QuitWindow => "QuitWindow".to_string(),
553        ShortcutType::MoveWindow(d) => format!("MoveWindow/{}", match d {
554          Direction::Left => "Left",
555          Direction::Down => "Down",
556          Direction::Up => "Up",
557          Direction::Right => "Right",
558        }),
559        ShortcutType::MoveWindowToEdge(d) => format!("MoveWindowToEdge/{}", match d {
560          Direction::Left => "Left",
561          Direction::Down => "Down",
562          Direction::Up => "Up",
563          Direction::Right => "Right",
564        }),
565        ShortcutType::ChangeWindowSize(d) => format!("ChangeWindowSize/{}", match d {
566          Direction::Left => "Left",
567          Direction::Down => "Down",
568          Direction::Up => "Up",
569          Direction::Right => "Right",
570        }),
571        ShortcutType::CenterWindow => "CenterWindow".to_string(),
572        ShortcutType::FullscreenWindow => "FullscreenWindow".to_string(),
573        ShortcutType::HalfWidthWindow => "HalfWidthWindow".to_string(),
574        ShortcutType::ClipboardCopy => "ClipboardCopy".to_string(),
575        ShortcutType::ClipboardPaste(s) => format!("ClipboardPaste/{}", s),
576      }),
577      WindowMessage::Info(i) => format!("Info/{}", match i {
578        InfoType::WindowsInWorkspace(wv, u) => {
579          let mut wv_string = String::new();
580          for w in wv {
581            wv_string += &format!("{}\x1F{}\x1F", w.0, w.1);
582          }
583          wv_string = wv_string[..wv_string.len() - 1].to_string();
584          format!("WindowsInWorkspace/{}\x1E{}", wv_string, u)
585        },
586      }),
587      WindowMessage::Focus => "Focus".to_string(),
588      WindowMessage::Unfocus => "Unfocus".to_string(),
589      WindowMessage::FocusClick => "FocusClick".to_string(),
590      WindowMessage::ChangeDimensions(d) => format!("ChangeDimensions/{}", array_to_string(d)),
591      WindowMessage::Touch(u1, u2) => format!("Touch/{}\x1E{}", u1, u2),
592    }
593  }
594  fn deserialize(serialized: &str) -> Result<Self, ()> {
595    let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
596    let mut parts = serialized.split("/");
597    match parts.next().unwrap_or("Invalid") {
598      "Init" => {
599        let arg = parts.next();
600        if arg.is_none() {
601          return Err(());
602        }
603        let d = get_two_array(arg.unwrap())?;
604        Ok(WindowMessage::Init(d))
605      },
606      "KeyPress" => {
607        let charg = get_rest_of_split(&mut parts, Some("/")).chars().next();
608        if let Some(charg) = charg {
609          Ok(WindowMessage::KeyPress(KeyPress { key: charg }))
610        } else {
611          Err(())
612        }
613      },
614      "CtrlKeyPress" => {
615        let charg = get_rest_of_split(&mut parts, Some("/")).chars().next();
616        if let Some(charg) = charg {
617          Ok(WindowMessage::CtrlKeyPress(KeyPress { key: charg }))
618        } else {
619          Err(())
620        }
621      },
622      "Shortcut" => {
623        let arg = parts.next();
624        if arg.is_none() {
625          return Err(());
626        }
627        let arg = arg.unwrap();
628        let shortcut = match arg {
629          "StartMenu" => Some(ShortcutType::StartMenu),
630          "SwitchWorkspace" | "MoveWindowToWorkspace" => {
631            let narg = parts.next();
632            if narg.is_none() {
633              None
634            } else {
635              let narg = narg.unwrap();
636              if let Ok(n) = narg.parse() {
637                if arg == "SwitchWorkspace" {
638                  Some(ShortcutType::SwitchWorkspace(n))
639                } else {
640                  Some(ShortcutType::MoveWindowToWorkspace(n))
641                }
642              } else {
643                None
644              }
645            }
646          },
647          "FocusPrevWindow" => Some(ShortcutType::FocusPrevWindow),
648          "FocusNextWindow" => Some(ShortcutType::FocusNextWindow),
649          "QuitWindow" => Some(ShortcutType::QuitWindow),
650          "MoveWindow" | "MoveWindowToEdge" | "ChangeWindowSize" => {
651            let darg = parts.next();
652            if let Some(darg) = darg {
653              let direction = match darg {
654                "Left" => Some(Direction::Left),
655                "Up" => Some(Direction::Up),
656                "Down" => Some(Direction::Down),
657                "Right" => Some(Direction::Right),
658                _ => None,
659              };
660              if let Some(direction) = direction {
661                if arg == "MoveWindow" {
662                  Some(ShortcutType::MoveWindow(direction))
663                } else if arg == "MoveWindowToEdge" {
664                  Some(ShortcutType::MoveWindowToEdge(direction))
665                } else {
666                  Some(ShortcutType::ChangeWindowSize(direction))
667                }
668              } else {
669                None
670              }
671            } else {
672              None
673            }
674          },
675          "CenterWindow" => Some(ShortcutType::CenterWindow),
676          "FullscreenWindow" => Some(ShortcutType::FullscreenWindow),
677          "HalfWidthWindow" => Some(ShortcutType::HalfWidthWindow),
678          "ClipboardCopy" => Some(ShortcutType::ClipboardCopy),
679          "ClipboardPaste" => Some(ShortcutType::ClipboardPaste(get_rest_of_split(&mut parts, Some("/")).replace("𐘂", "\n"))),
680          _ => None,
681        };
682        if let Some(shortcut) = shortcut {
683          Ok(WindowMessage::Shortcut(shortcut))
684        } else {
685          Err(())
686        }
687      },
688      "Info" => {
689        //skip WindowsInWorkspace cause that's the only possible InfoType atm
690        if parts.next().is_none() {
691          return Err(());
692        }
693        let arg = parts.next();
694        if arg.is_none() {
695          return Err(());
696        }
697        let mut parts2 = arg.unwrap().split("\x1E");
698        let arg2 = parts2.next();
699        if arg2.is_none() {
700          return Err(());
701        }
702        let mut w_tuple: (usize, String) = Default::default();
703        let mut w_vec = Vec::new();
704        for (i, a) in arg2.unwrap().split("\x1F").enumerate() {
705          if i % 2 == 0 {
706            if let Ok(n) = a.parse() {
707              w_tuple.0 = n;
708            }
709          } else {
710            w_tuple.1 = a.to_string();
711            w_vec.push(w_tuple.clone());
712          }
713        }
714        let arg2 = parts2.next();
715        if arg2.is_none() {
716          return Err(());
717        }
718        if let Ok(n) = arg2.unwrap().parse() {
719          return Ok(WindowMessage::Info(InfoType::WindowsInWorkspace(w_vec, n)));
720        } else {
721          return Err(());
722        }
723      },
724      "Focus" => Ok(WindowMessage::Focus),
725      "Unfocus" => Ok(WindowMessage::Unfocus),
726      "FocusClick" => Ok(WindowMessage::FocusClick),
727      "ChangeDimensions" => {
728        let arg = parts.next();
729        if arg.is_none() {
730          return Err(());
731        }
732        let d = get_two_array(arg.unwrap())?;
733        Ok(WindowMessage::ChangeDimensions(d))
734      },
735      "Touch" => {
736        let arg = parts.next();
737        if arg.is_none() {
738          return Err(());
739        }
740        let mut parts2 = arg.unwrap().split("\x1E");
741        let arg2 = parts2.next();
742        if arg2.is_none() {
743          return Err(());
744        }
745        let u1 = arg2.unwrap().parse();
746        let arg2 = parts2.next();
747        if u1.is_err() || arg2.is_none() {
748          return Err(());
749        }
750        let u2 = arg2.unwrap().parse();
751        if u2.is_err() {
752          return Err(());
753        }
754        Ok(WindowMessage::Touch(u1.unwrap(), u2.unwrap()))
755      },
756      _ => Err(()),
757    }
758  }
759}
760
761#[test]
762fn window_message_serialize_deserialize() {
763  for wm in [
764    WindowMessage::Init([1000, 1001]),
765    WindowMessage::KeyPress(KeyPress { key: 'a' }),
766    WindowMessage::KeyPress(KeyPress { key: '/' }),
767    WindowMessage::KeyPress(KeyPress { key: '𐘂' }),
768    WindowMessage::CtrlKeyPress(KeyPress { key: ';' }),
769    WindowMessage::Shortcut(ShortcutType::StartMenu),
770    WindowMessage::Shortcut(ShortcutType::MoveWindowToWorkspace(7)),
771    WindowMessage::Shortcut(ShortcutType::ClipboardPaste("105/20 Azumanga".to_string())),
772    WindowMessage::Info(InfoType::WindowsInWorkspace(vec![(1, "Terminal".to_string()), (2, "Minesweeper".to_string()), (12, "Test Test".to_string())], 5)),
773    WindowMessage::Focus,
774    WindowMessage::Unfocus,
775    WindowMessage::FocusClick,
776    WindowMessage::ChangeDimensions([999, 250]),
777    WindowMessage::Touch(12, 247),
778  ] {
779    let serialized = wm.serialize();
780    assert!(serialized == WindowMessage::deserialize(&serialized).unwrap().serialize());
781  }
782}
783