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
17fn array_to_string<T: Display>(array: &[T]) -> String {
18  let mut output = String::new();
19  for item in array {
20    output += &format!("{}{}", if output == String::new() {
21      ""
22    } else {
23      "\x1F"
24    }, item);
25  }
26  output
27}
28
29fn option_to_string<T: Display>(option: &Option<T>) -> String {
30  if let Some(value) = option {
31    format!("S{}", value)
32  } else {
33    "N".to_string()
34  }
35}
36
37fn get_color(serialized: &str) -> Result<[u8; 3], ()> {
38  let rgb = serialized.split("\x1F");
39  let mut color = [0; 3];
40  let mut c_i = 0;
41  //won't return error if rgb is 0, 1, or 2 elements.
42  //I guess that's okay, since it doesn't panic either
43  for c in rgb {
44    if c_i == 3 {
45      return Err(());
46    }
47    if let Ok(c) = c.parse() {
48      color[c_i] = c;
49    } else {
50      return Err(());
51    }
52    c_i += 1;
53  }
54  Ok(color)
55}
56
57fn get_two_array(serialized: &str) -> Result<[usize; 2], ()> {
58  let mut arg = serialized.split("\x1F");
59  let mut a = [0; 2];
60  for i in 0..2 {
61    if let Some(n) = arg.next() {
62      if let Ok(n) = n.parse() {
63        a[i] = n;
64        continue
65      }
66    }
67    return Err(());
68  }
69  return Ok(a);
70}
71
72pub trait Serializable {
73  fn serialize(&self) -> String;
74  fn deserialize(serialized: &str) -> Result<Self, ()> where Self: Sized;
75}
76
77//ripe for macros when I figure them out
78
79impl Serializable for ThemeInfo {
80  fn serialize(&self) -> String {
81    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))
82  }
83  fn deserialize(serialized: &str) -> Result<Self, ()> {
84    //strip newline at the end
85    let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
86    let mut theme_info: ThemeInfo = Default::default();
87    let arrays = serialized.split(":");
88    let mut a_i = 0;
89    //won't error or panic if less than 9... rest will just be black by default I guess
90    for a in arrays {
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      a_i += 1;
129    }
130    Err(())
131  }
132}
133
134#[test]
135fn theme_info_serialize_deserialize() {
136  use crate::themes::{ Themes, get_theme_info };
137  let theme_info = get_theme_info(&Default::default()).unwrap();
138  let serialized = theme_info.serialize();
139  assert!(serialized == ThemeInfo::deserialize(&serialized).unwrap().serialize());
140}
141
142impl Serializable for WindowMessageResponse {
143  fn serialize(&self) -> String {
144    match self {
145      WindowMessageResponse::JustRedraw => "JustRedraw".to_string(),
146      WindowMessageResponse::DoNothing => "DoNothing".to_string(),
147      WindowMessageResponse::Request(req) => {
148        let req = match req {
149          WindowManagerRequest::OpenWindow(name) => format!("OpenWindow/{}", name),
150          WindowManagerRequest::ClipboardCopy(name) => format!("ClipboardCopy/{}", name),
151          WindowManagerRequest::CloseStartMenu => "CloseStartMenu".to_string(),
152          WindowManagerRequest::Unlock => "Unlock".to_string(),
153          WindowManagerRequest::Lock => "Lock".to_string(),
154          WindowManagerRequest::DoKeyChar(kc) => format!("DoKeyChar/{}", match kc {
155            KeyChar::Press(c) => format!("Press/{}", c),
156            KeyChar::Alt(c) => format!("Alt/{}", c),
157            KeyChar::Ctrl(c) => format!("Ctrl/{}", c),
158          }),
159        };
160        format!("Request/{}", req)
161      },
162    }
163  }
164  fn deserialize(serialized: &str) -> Result<Self, ()> {
165    //strip newline at the end
166    let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
167    let mut parts = serialized.split("/");
168    match parts.next().unwrap_or("Invalid") {
169      "JustRedraw" => Ok(WindowMessageResponse::JustRedraw),
170      "DoNothing" => Ok(WindowMessageResponse::DoNothing),
171      "Request" => {
172        let req = match parts.next().unwrap_or("Invalid") {
173          //do get_rest_of_split instead of .next() because it is possible for window name or copy to have "/"
174          "OpenWindow" => Some(WindowManagerRequest::OpenWindow(get_rest_of_split(&mut parts, Some("/")))),
175          "ClipboardCopy" => Some(WindowManagerRequest::ClipboardCopy(get_rest_of_split(&mut parts, Some("/")))),
176          "CloseStartMenu" => Some(WindowManagerRequest::CloseStartMenu),
177          "Unlock" => Some(WindowManagerRequest::Unlock),
178          "Lock" => Some(WindowManagerRequest::Lock),
179          "DoKeyChar" => Some(WindowManagerRequest::DoKeyChar(
180            match parts.next().unwrap_or("Invalid") {
181              "Press" => KeyChar::Press(parts.next().unwrap_or("?").chars().next().unwrap()),
182              "Alt" => KeyChar::Alt(parts.next().unwrap_or("?").chars().next().unwrap()),
183              "Ctrl" => KeyChar::Ctrl(parts.next().unwrap_or("?").chars().next().unwrap()),
184              _ => KeyChar::Press('?'), //yeah.
185            }
186          )),
187          _ => None, //yeah...
188        };
189        if let Some(req) = req {
190          Ok(WindowMessageResponse::Request(req))
191        } else {
192          Err(())
193        }
194      },
195      _ => Err(()),
196    }
197  }
198}
199
200#[test]
201fn window_message_response_serialize_deserialize() {
202  let resp = WindowMessageResponse::JustRedraw;
203  let serialized = resp.serialize();
204  assert!(resp == WindowMessageResponse::deserialize(&serialized).unwrap());
205  let resp = WindowMessageResponse::Request(WindowManagerRequest::OpenWindow("a".to_string()));
206  let serialized = resp.serialize();
207  assert!(resp == WindowMessageResponse::deserialize(&serialized).unwrap());
208  let resp = WindowMessageResponse::Request(WindowManagerRequest::Unlock);
209  let serialized = resp.serialize();
210  assert!(resp == WindowMessageResponse::deserialize(&serialized).unwrap());
211  let resp = WindowMessageResponse::Request(WindowManagerRequest::DoKeyChar(KeyChar::Alt('e')));
212  let serialized = resp.serialize();
213  assert!(resp == WindowMessageResponse::deserialize(&serialized).unwrap());
214}
215
216impl Serializable for DrawInstructions {
217  fn serialize(&self) -> String {
218    match self {
219      //use \x1E (record separator) because it won't be in strings. it better fucking not be at least
220      DrawInstructions::Rect(p, d, c) => format!("Rect/{}\x1E{}\x1E{}", array_to_string(p), array_to_string(d), array_to_string(c)),
221      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)),
222      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),
223      DrawInstructions::Bmp(p, s, b) => format!("Bmp/{}\x1E{}\x1E{}", array_to_string(p), s, b),
224      DrawInstructions::Circle(p, u, c) => format!("Circle/{}\x1E{}\x1E{}", array_to_string(p), u, 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      _ => Err(()),
407    }
408  }
409}
410
411pub type DrawInstructionsVec = Vec<DrawInstructions>;
412
413impl Serializable for DrawInstructionsVec {
414  fn serialize(&self) -> String {
415    let collected: Vec<_> = self.into_iter().map(|ins| ins.serialize()).collect();
416    collected.join("\x1D")
417  }
418  fn deserialize(serialized: &str) -> Result<Self, ()> {
419    //strip newline at the end
420    let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
421    let mut instructions = Vec::new();
422    for ser_ins in serialized.split("\x1D") {
423      if let Ok(ser_ins) = DrawInstructions::deserialize(ser_ins) {
424        instructions.push(ser_ins);
425      } else {
426        return Err(());
427      }
428    }
429    Ok(instructions)
430  }
431}
432
433#[test]
434fn draw_instructions_serialize_deserialize() {
435  use std::vec;
436  let instructions = vec![
437    DrawInstructions::Rect([15, 24], [100, 320], [255, 0, 128]),
438    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),
439    DrawInstructions::Gradient([0, 500], [750, 125], [255, 255, 255], [0, 0, 0], 12),
440    DrawInstructions::Text([123, 999], vec!["nimbus-romono".to_string()], "print!(\"{}\", variable_name);".to_string(), [12, 36, 108], [128, 128, 128], Some(44), Some(200)),
441    DrawInstructions::Bmp([55, 98], "mingde".to_string(), true),
442    DrawInstructions::Bmp([55, 98], "wooooo".to_string(), false),
443    DrawInstructions::Circle([0, 1], 19, [128, 128, 128]),
444  ];
445  let serialized = instructions.serialize();
446  assert!(serialized == DrawInstructionsVec::deserialize(&serialized).unwrap().serialize());
447  let instructions = vec![
448    DrawInstructions::Rect([0, 0], [410, 410], [0, 0, 0]),
449    DrawInstructions::Text([4, 4], vec!["nimbus-romono".to_string()], "Mingde Terminal".to_string(), [255, 255, 255], [0, 0, 0], Some(0), Some(10)),
450    DrawInstructions::Text([4, 34], vec!["nimbus-romono".to_string()], "$ a".to_string(), [255, 255, 255], [0, 0, 0], Some(0), Some(10)),
451  ];
452  let serialized = instructions.serialize() + "\n";
453  assert!(serialized[..serialized.len() - 1] == DrawInstructionsVec::deserialize(&serialized).unwrap().serialize());
454}
455
456impl Serializable for WindowLikeType {
457  fn serialize(&self) -> String {
458    match self {
459      WindowLikeType::LockScreen => "LockScreen".to_string(),
460      WindowLikeType::Window => "Window".to_string(),
461      WindowLikeType::DesktopBackground => "DesktopBackground".to_string(),
462      WindowLikeType::Taskbar => "Taskbar".to_string(),
463      WindowLikeType::StartMenu => "StartMenu".to_string(),
464      WindowLikeType::WorkspaceIndicator => "WorkspaceIndicator".to_string(),
465      WindowLikeType::OnscreenKeyboard => "OnscreenKeyboard".to_string(),
466    }
467  }
468  fn deserialize(serialized: &str) -> Result<Self, ()> {
469    let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
470    match serialized {
471      "LockScreen" => Ok(WindowLikeType::LockScreen),
472      "Window" => Ok(WindowLikeType::Window),
473      "DesktopBackground" => Ok(WindowLikeType::DesktopBackground),
474      "Taskbar" => Ok(WindowLikeType::Taskbar),
475      "StartMenu" => Ok(WindowLikeType::StartMenu),
476      "WorkspaceIndicator" => Ok(WindowLikeType::WorkspaceIndicator),
477      "OnscreenKeyboard" => Ok(WindowLikeType::OnscreenKeyboard),
478      _ => Err(()),
479    }
480  }
481}
482
483#[test]
484fn window_like_type_serialize_deserialize() {
485  let wl_type = WindowLikeType::Window;
486  let serialized = wl_type.serialize();
487  assert!(serialized == WindowLikeType::deserialize(&serialized).unwrap().serialize());
488}
489
490impl Serializable for Dimensions {
491  fn serialize(&self) -> String {
492    array_to_string(self)
493  }
494  fn deserialize(serialized: &str) -> Result<Self, ()> {
495    //strip newline at the end
496    let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
497    let d = get_two_array(serialized)?;
498    Ok(d)
499  }
500}
501
502impl Serializable for WindowMessage {
503  fn serialize(&self) -> String {
504    match self {
505      WindowMessage::Init(d) => format!("Init/{}", array_to_string(d)),
506      WindowMessage::KeyPress(kp) => format!("KeyPress/{}", kp.key),
507      WindowMessage::CtrlKeyPress(kp) => format!("CtrlKeyPress/{}", kp.key),
508      WindowMessage::Shortcut(st) => format!("Shortcut/{}", match st {
509        ShortcutType::StartMenu => "StartMenu".to_string(),
510        ShortcutType::SwitchWorkspace(u) => format!("SwitchWorkspace/{}", u),
511        ShortcutType::MoveWindowToWorkspace(u) => format!("MoveWindowToWorkspace/{}", u),
512        ShortcutType::FocusPrevWindow => "FocusPrevWindow".to_string(),
513        ShortcutType::FocusNextWindow => "FocusNextWindow".to_string(),
514        ShortcutType::QuitWindow => "QuitWindow".to_string(),
515        ShortcutType::MoveWindow(d) => format!("MoveWindow/{}", match d {
516          Direction::Left => "Left",
517          Direction::Down => "Down",
518          Direction::Up => "Up",
519          Direction::Right => "Right",
520        }),
521        ShortcutType::MoveWindowToEdge(d) => format!("MoveWindowToEdge/{}", match d {
522          Direction::Left => "Left",
523          Direction::Down => "Down",
524          Direction::Up => "Up",
525          Direction::Right => "Right",
526        }),
527        ShortcutType::ChangeWindowSize(d) => format!("ChangeWindowSize/{}", match d {
528          Direction::Left => "Left",
529          Direction::Down => "Down",
530          Direction::Up => "Up",
531          Direction::Right => "Right",
532        }),
533        ShortcutType::CenterWindow => "CenterWindow".to_string(),
534        ShortcutType::FullscreenWindow => "FullscreenWindow".to_string(),
535        ShortcutType::HalfWidthWindow => "HalfWidthWindow".to_string(),
536        ShortcutType::ClipboardCopy => "ClipboardCopy".to_string(),
537        ShortcutType::ClipboardPaste(s) => format!("ClipboardPaste/{}", s),
538      }),
539      WindowMessage::Info(i) => format!("Info/{}", match i {
540        InfoType::WindowsInWorkspace(wv, u) => {
541          let mut wv_string = String::new();
542          for w in wv {
543            wv_string += &format!("{}\x1F{}\x1F", w.0, w.1);
544          }
545          wv_string = wv_string[..wv_string.len() - 1].to_string();
546          format!("WindowsInWorkspace/{}\x1E{}", wv_string, u)
547        },
548      }),
549      WindowMessage::Focus => "Focus".to_string(),
550      WindowMessage::Unfocus => "Unfocus".to_string(),
551      WindowMessage::FocusClick => "FocusClick".to_string(),
552      WindowMessage::ChangeDimensions(d) => format!("ChangeDimensions/{}", array_to_string(d)),
553      WindowMessage::Touch(u1, u2) => format!("Touch/{}\x1E{}", u1, u2),
554    }
555  }
556  fn deserialize(serialized: &str) -> Result<Self, ()> {
557    let serialized = if serialized.ends_with("\n") { &serialized[..serialized.len() - 1] } else { serialized };
558    let mut parts = serialized.split("/");
559    match parts.next().unwrap_or("Invalid") {
560      "Init" => {
561        let arg = parts.next();
562        if arg.is_none() {
563          return Err(());
564        }
565        let d = get_two_array(arg.unwrap())?;
566        Ok(WindowMessage::Init(d))
567      },
568      "KeyPress" => {
569        let charg = get_rest_of_split(&mut parts, Some("/")).chars().next();
570        if let Some(charg) = charg {
571          Ok(WindowMessage::KeyPress(KeyPress { key: charg }))
572        } else {
573          Err(())
574        }
575      },
576      "CtrlKeyPress" => {
577        let charg = get_rest_of_split(&mut parts, Some("/")).chars().next();
578        if let Some(charg) = charg {
579          Ok(WindowMessage::CtrlKeyPress(KeyPress { key: charg }))
580        } else {
581          Err(())
582        }
583      },
584      "Shortcut" => {
585        let arg = parts.next();
586        if arg.is_none() {
587          return Err(());
588        }
589        let arg = arg.unwrap();
590        let shortcut = match arg {
591          "StartMenu" => Some(ShortcutType::StartMenu),
592          "SwitchWorkspace" | "MoveWindowToWorkspace" => {
593            let narg = parts.next();
594            if narg.is_none() {
595              None
596            } else {
597              let narg = narg.unwrap();
598              if let Ok(n) = narg.parse() {
599                if arg == "SwitchWorkspace" {
600                  Some(ShortcutType::SwitchWorkspace(n))
601                } else {
602                  Some(ShortcutType::MoveWindowToWorkspace(n))
603                }
604              } else {
605                None
606              }
607            }
608          },
609          "FocusPrevWindow" => Some(ShortcutType::FocusPrevWindow),
610          "FocusNextWindow" => Some(ShortcutType::FocusNextWindow),
611          "QuitWindow" => Some(ShortcutType::QuitWindow),
612          "MoveWindow" | "MoveWindowToEdge" | "ChangeWindowSize" => {
613            let darg = parts.next();
614            if let Some(darg) = darg {
615              let direction = match darg {
616                "Left" => Some(Direction::Left),
617                "Up" => Some(Direction::Up),
618                "Down" => Some(Direction::Down),
619                "Right" => Some(Direction::Right),
620                _ => None,
621              };
622              if let Some(direction) = direction {
623                if arg == "MoveWindow" {
624                  Some(ShortcutType::MoveWindow(direction))
625                } else if arg == "MoveWindowToEdge" {
626                  Some(ShortcutType::MoveWindowToEdge(direction))
627                } else {
628                  Some(ShortcutType::ChangeWindowSize(direction))
629                }
630              } else {
631                None
632              }
633            } else {
634              None
635            }
636          },
637          "CenterWindow" => Some(ShortcutType::CenterWindow),
638          "FullscreenWindow" => Some(ShortcutType::FullscreenWindow),
639          "HalfWidthWindow" => Some(ShortcutType::HalfWidthWindow),
640          "ClipboardCopy" => Some(ShortcutType::ClipboardCopy),
641          "ClipboardPaste" => Some(ShortcutType::ClipboardPaste(get_rest_of_split(&mut parts, Some("/")))),
642          _ => None,
643        };
644        if let Some(shortcut) = shortcut {
645          Ok(WindowMessage::Shortcut(shortcut))
646        } else {
647          Err(())
648        }
649      },
650      "Info" => {
651        //skip WindowsInWorkspace cause that's the only possible InfoType atm
652        if parts.next().is_none() {
653          return Err(());
654        }
655        let arg = parts.next();
656        if arg.is_none() {
657          return Err(());
658        }
659        let mut parts2 = arg.unwrap().split("\x1E");
660        let arg2 = parts2.next();
661        if arg2.is_none() {
662          return Err(());
663        }
664        let mut w_tuple: (usize, String) = Default::default();
665        let mut w_vec = Vec::new();
666        let mut i = 0;
667        for a in arg2.unwrap().split("\x1F") {
668          if i % 2 == 0 {
669            if let Ok(n) = a.parse() {
670              w_tuple.0 = n;
671            }
672          } else {
673            w_tuple.1 = a.to_string();
674            w_vec.push(w_tuple.clone());
675          }
676          i += 1;
677        }
678        let arg2 = parts2.next();
679        if arg2.is_none() {
680          return Err(());
681        }
682        if let Ok(n) = arg2.unwrap().parse() {
683          return Ok(WindowMessage::Info(InfoType::WindowsInWorkspace(w_vec, n)));
684        } else {
685          return Err(());
686        }
687      },
688      "Focus" => Ok(WindowMessage::Focus),
689      "Unfocus" => Ok(WindowMessage::Unfocus),
690      "FocusClick" => Ok(WindowMessage::FocusClick),
691      "ChangeDimensions" => {
692        let arg = parts.next();
693        if arg.is_none() {
694          return Err(());
695        }
696        let d = get_two_array(arg.unwrap())?;
697        Ok(WindowMessage::ChangeDimensions(d))
698      },
699      "Touch" => {
700        let arg = parts.next();
701        if arg.is_none() {
702          return Err(());
703        }
704        let mut parts2 = arg.unwrap().split("\x1E");
705        let arg2 = parts2.next();
706        if arg2.is_none() {
707          return Err(());
708        }
709        let u1 = arg2.unwrap().parse();
710        let arg2 = parts2.next();
711        if u1.is_err() || arg2.is_none() {
712          return Err(());
713        }
714        let u2 = arg2.unwrap().parse();
715        if u2.is_err() {
716          return Err(());
717        }
718        Ok(WindowMessage::Touch(u1.unwrap(), u2.unwrap()))
719      },
720      _ => Err(()),
721    }
722  }
723}
724
725#[test]
726fn window_message_serialize_deserialize() {
727  for wm in [
728    WindowMessage::Init([1000, 1001]),
729    WindowMessage::KeyPress(KeyPress { key: 'a' }),
730    WindowMessage::KeyPress(KeyPress { key: '/' }),
731    WindowMessage::KeyPress(KeyPress { key: '𐘂' }),
732    WindowMessage::CtrlKeyPress(KeyPress { key: ';' }),
733    WindowMessage::Shortcut(ShortcutType::StartMenu),
734    WindowMessage::Shortcut(ShortcutType::MoveWindowToWorkspace(7)),
735    WindowMessage::Shortcut(ShortcutType::ClipboardPaste("105/20 Azumanga".to_string())),
736    WindowMessage::Info(InfoType::WindowsInWorkspace(vec![(1, "Terminal".to_string()), (2, "Minesweeper".to_string()), (12, "Test Test".to_string())], 5)),
737    WindowMessage::Focus,
738    WindowMessage::Unfocus,
739    WindowMessage::FocusClick,
740    WindowMessage::ChangeDimensions([999, 250]),
741    WindowMessage::Touch(12, 247),
742  ] {
743    let serialized = wm.serialize();
744    assert!(serialized == WindowMessage::deserialize(&serialized).unwrap().serialize());
745  }
746}
747