add input methdod framework (add sitelen pona input)

Also change audio player randomness source to /dev/urandom which is way better for the prng we are using
This commit is contained in:
stjet
2026-03-12 08:04:57 +00:00
parent c4876e5606
commit 9f7ef7c14d
158 changed files with 432 additions and 79 deletions

View File

@@ -51,11 +51,11 @@ impl WindowLike for LockScreen {
fn draw(&self, _theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
vec![
DrawInstructions::Rect([0, 0], self.dimensions, [0, 0, 0]),
DrawInstructions::Text([4, 4], vec!["nimbus-roman".to_string()], self.lines[0].clone(), [255, 255, 255], [0, 0, 0], None, None),
DrawInstructions::Text([4, 4], vec!["nimbus-roman".to_string(), "shippori-mincho".to_string(), "linja-lipamanka".to_string()], self.lines[0].clone(), [255, 255, 255], [0, 0, 0], None, None),
//He is my brother.
DrawInstructions::Text([4, 4 + 16], vec!["nimbus-roman".to_string()], self.lines[1].clone(), [255, 255, 255], [0, 0, 0], None, None),
DrawInstructions::Text([4, 4 + 16], vec!["nimbus-roman".to_string(), "shippori-mincho".to_string(), "linja-lipamanka".to_string()], self.lines[1].clone(), [255, 255, 255], [0, 0, 0], None, None),
//But I must kill him and keep strong to do it.
DrawInstructions::Text([4, 4 + 16 * 2], vec!["nimbus-roman".to_string()], self.lines[2].clone(), [255, 255, 255], [0, 0, 0], None, None),
DrawInstructions::Text([4, 4 + 16 * 2], vec!["nimbus-roman".to_string(), "shippori-mincho".to_string(), "linja-lipamanka".to_string()], self.lines[2].clone(), [255, 255, 255], [0, 0, 0], None, None),
DrawInstructions::Text([4, 4 + 16 * 3], vec!["nimbus-roman".to_string()], "Password: ".to_string(), [255, 255, 255], [0, 0, 0], None, None),
DrawInstructions::Text([80, 4 + 16 * 3], vec!["nimbus-roman".to_string()], "*".repeat(self.input_password.len()), [255, 255, 255], [0, 0, 0], None, None),
]
@@ -84,6 +84,16 @@ impl LockScreen {
"\"Yellow,\" he thought, and stomped off back to his bedroom to get dressed.".to_string(),
"He stared at it.".to_string()
],
[
"\"󱥉󱥶󱤧󱥔󱥩󱤴\"".to_string(),
"\"󱤟󱥍󱤻󱥛\"".to_string(),
"\"󱥁󱤧󱥊󱥶\"".to_string()
],
[
"安心".to_string(),
"アンコール".to_string(),
"ワット".to_string(),
],
];
let rand_index = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as usize % possible_lines.len();
Self {

View File

@@ -3,7 +3,7 @@ use std::vec::Vec;
use std::time::{ SystemTime, UNIX_EPOCH };
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType, INDICATOR_HEIGHT };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, ShortcutType };
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, ShortcutType, InfoType };
use ming_wm_lib::framebuffer_types::Dimensions;
use ming_wm_lib::themes::ThemeInfo;
@@ -15,6 +15,8 @@ const ONE_DAY: u64 = 24 * ONE_HOUR;
pub struct WorkspaceIndicator {
dimensions: Dimensions,
current_workspace: u8,
im_abbr: String,
im_buffer: String,
}
impl WindowLike for WorkspaceIndicator {
@@ -33,6 +35,15 @@ impl WindowLike for WorkspaceIndicator {
_ => WindowMessageResponse::DoNothing,
}
},
WindowMessage::Info(info) => {
if let InfoType::InputMethod(im_abbr, im_buffer) = info {
self.im_abbr = im_abbr;
self.im_buffer = im_buffer;
WindowMessageResponse::JustRedraw
} else {
WindowMessageResponse::DoNothing
}
},
_ => WindowMessageResponse::DoNothing,
}
}
@@ -53,6 +64,14 @@ impl WindowLike for WorkspaceIndicator {
instructions.push(DrawInstructions::Text([w * WIDTH + 5, 4], vec!["nimbus-roman".to_string()], (w + 1).to_string(), theme_info.text, theme_info.background, None, None));
}
}
//input method and buffer
let im_buffer = if self.im_buffer.len() > 6 {
self.im_buffer[0..6].to_string() + "..."
} else {
self.im_buffer.clone()
};
let im_info = format!("{}: {}", self.im_abbr, im_buffer);
instructions.push(DrawInstructions::Text([self.dimensions[0] - 200, 4], vec!["nimbus-roman".to_string()], im_info, theme_info.text, theme_info.background, None, None));
//also add the utc time in the right edge
let today_secs = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() % ONE_DAY;
let hours = (today_secs / ONE_HOUR).to_string();
@@ -73,10 +92,12 @@ impl WindowLike for WorkspaceIndicator {
}
impl WorkspaceIndicator {
pub fn new() -> Self {
pub fn new(im_abbr: String) -> Self {
Self {
dimensions: [0, 0],
current_workspace: 0,
im_abbr,
im_buffer: String::new(),
}
}
}

View File

@@ -16,6 +16,8 @@ fn color_with_alpha(color: RGBColor, bg_color: RGBColor, alpha: u8) -> RGBColor
//255 * 255 < max(u16)
if alpha == 255 {
color
} else if alpha == 0 {
bg_color
} else {
let alpha = alpha as u16;
[
@@ -128,7 +130,7 @@ impl FramebufferWriter {
}
}
pub fn draw_char(&mut self, top_left: Point, char_info: &FontCharInfo, color: RGBColor, bg_color: RGBColor) {
fn _draw_char(&mut self, top_left: Point, char_info: &FontCharInfo, color: RGBColor, bg_color: RGBColor) {
let mut start_pos;
for row in 0..char_info.height {
start_pos = ((top_left[1] + row + char_info.top_offset as usize) * self.info.stride + top_left[0]) * self.info.bytes_per_pixel;
@@ -238,7 +240,7 @@ impl FramebufferWriter {
} else {
add_after = char_width + horiz_spacing;
}
self.draw_char(top_left, &char_info, color, bg_color);
self._draw_char(top_left, &char_info, color, bg_color);
top_left[0] += add_after;
}
}

View File

@@ -0,0 +1,19 @@
use ming_wm_lib::window_manager_types::InputMethod;
use ming_wm_lib::messages::KeyPress;
pub struct Lat {}
impl InputMethod for Lat {
fn abbr(&self) -> [char; 3] {
['L', 'A', 'T']
}
fn internal_buffer(&self) -> String {
String::new()
}
fn add_key_press(&mut self, kp: KeyPress) -> Option<char> {
//everything goes
Some(kp.key)
}
}

View File

@@ -0,0 +1,2 @@
pub mod lat;
pub mod tok;

193
wm/src/input_method/tok.rs Normal file
View File

@@ -0,0 +1,193 @@
use std::collections::HashMap;
use ming_wm_lib::window_manager_types::InputMethod;
use ming_wm_lib::messages::KeyPress;
const TOK_CHARSET: [char; 14] = ['m', 'n', 'i', 'u', 'p', 't', 'k', 'e', 'o', 's', 'a', 'w', 'l', 'j'];
pub struct Tok {
mapping: HashMap<String, char>,
buffer: String,
}
impl InputMethod for Tok {
fn abbr(&self) -> [char; 3] {
['T', 'O', 'K']
}
fn internal_buffer(&self) -> String {
self.buffer.clone()
}
fn add_key_press(&mut self, kp: KeyPress) -> Option<char> {
if kp.is_enter() || kp.is_backspace() {
if self.buffer == String::new() {
//^if no buffer, enter or backspace key should be passed through
Some(kp.key)
} else {
if kp.is_enter() {
if let Some(ret_c) = self.mapping.get(&self.buffer.to_lowercase()) {
//^look up in table to see if buffer matches, if so, return
self.buffer = String::new();
return Some(*ret_c);
}
}
//else, just clear buffer
self.buffer = String::new();
None
}
} else if TOK_CHARSET.contains(&kp.key) {
self.buffer += &kp.key.to_string();
None
} else {
//pass through stuff not in tp charset
Some(kp.key)
}
}
}
impl Tok {
pub fn new() -> Self {
let mapping = HashMap::from([
("pali".to_string(), '󱥉'),
("weka".to_string(), '󱥶'),
("li".to_string(), '󱤧'),
("pona".to_string(), '󱥔'),
("tawa".to_string(), '󱥩'),
("mi".to_string(), '󱤴'),
("kulupu".to_string(), '󱤟'),
("pi".to_string(), '󱥍'),
("musi".to_string(), '󱤻'),
("sijelo".to_string(), '󱥛'),
("ni".to_string(), '󱥁'),
("palisa".to_string(), '󱥊'),
("a".to_string(), '󱤀'),
("akesi".to_string(), '󱤁'),
("ala".to_string(), '󱤂'),
("alasa".to_string(), '󱤃'),
("ali".to_string(), '󱤄'),
("ale".to_string(), '󱤄'),
("anpa".to_string(), '󱤅'),
("ante".to_string(), '󱤆'),
("anu".to_string(), '󱤇'),
("awen".to_string(), '󱤈'),
("e".to_string(), '󱤉'),
("en".to_string(), '󱤊'),
("esun".to_string(), '󱤋'),
("ijo".to_string(), '󱤌'),
("ike".to_string(), '󱤍'),
("ilo".to_string(), '󱤎'),
("insa".to_string(), '󱤏'),
("jaki".to_string(), '󱤐'),
("jan".to_string(), '󱤑'),
("jelo".to_string(), '󱤒'),
("jo".to_string(), '󱤓'),
("kala".to_string(), '󱤔'),
("kalama".to_string(), '󱤕'),
("kama".to_string(), '󱤖'),
("kasi".to_string(), '󱤗'),
("ken".to_string(), '󱤘'),
("kepeken".to_string(), '󱤙'),
("kili".to_string(), '󱤚'),
("kiwen".to_string(), '󱤛'),
("ko".to_string(), '󱤜'),
("kon".to_string(), '󱤝'),
("kule".to_string(), '󱤞'),
("kute".to_string(), '󱤠'),
("la".to_string(), '󱤡'),
("lape".to_string(), '󱤢'),
("laso".to_string(), '󱤣'),
("lawa".to_string(), '󱤤'),
("len".to_string(), '󱤥'),
("lete".to_string(), '󱤦'),
("lili".to_string(), '󱤨'),
("linja".to_string(), '󱤩'),
("lipu".to_string(), '󱤪'),
("loje".to_string(), '󱤫'),
("lon".to_string(), '󱤬'),
("luka".to_string(), '󱤭'),
("lukin".to_string(), '󱤮'),
("lupa".to_string(), '󱤯'),
("ma".to_string(), '󱤰'),
("mama".to_string(), '󱤱'),
("mani".to_string(), '󱤲'),
("meli".to_string(), '󱤳'),
("mije".to_string(), '󱤵'),
("moku".to_string(), '󱤶'),
("moli".to_string(), '󱤷'),
("monsi".to_string(), '󱤸'),
("mu".to_string(), '󱤹'),
("mun".to_string(), '󱤺'),
("mute".to_string(), '󱤼'),
("nanpa".to_string(), '󱤽'),
("nasa".to_string(), '󱤾'),
("nasin".to_string(), '󱤿'),
("nena".to_string(), '󱥀'),
("ni".to_string(), '󱥁'),
("nimi".to_string(), '󱥂'),
("noka".to_string(), '󱥃'),
("o".to_string(), '󱥄'),
("olin".to_string(), '󱥅'),
("ona".to_string(), '󱥆'),
("open".to_string(), '󱥇'),
("pakala".to_string(), '󱥈'),
("pan".to_string(), '󱥋'),
("pana".to_string(), '󱥌'),
("pilin".to_string(), '󱥎'),
("pimeja".to_string(), '󱥏'),
("pini".to_string(), '󱥐'),
("pipi".to_string(), '󱥑'),
("poka".to_string(), '󱥒'),
("poki".to_string(), '󱥓'),
("pu".to_string(), '󱥕'),
("sama".to_string(), '󱥖'),
("seli".to_string(), '󱥗'),
("selo".to_string(), '󱥘'),
("seme".to_string(), '󱥙'),
("sewi".to_string(), '󱥚'),
("sike".to_string(), '󱥜'),
("sin".to_string(), '󱥝'),
("sina".to_string(), '󱥞'),
("sinpin".to_string(), '󱥟'),
("sitelen".to_string(), '󱥠'),
("sona".to_string(), '󱥡'),
("soweli".to_string(), '󱥢'),
("suli".to_string(), '󱥣'),
("suno".to_string(), '󱥤'),
("supa".to_string(), '󱥥'),
("suwi".to_string(), '󱥦'),
("tan".to_string(), '󱥧'),
("taso".to_string(), '󱥨'),
("telo".to_string(), '󱥪'),
("tenpo".to_string(), '󱥫'),
("toki".to_string(), '󱥬'),
("tomo".to_string(), '󱥭'),
("tu".to_string(), '󱥮'),
("unpa".to_string(), '󱥯'),
("uta".to_string(), '󱥰'),
("utala".to_string(), '󱥱'),
("walo".to_string(), '󱥲'),
("wan".to_string(), '󱥳'),
("waso".to_string(), '󱥴'),
("wawa".to_string(), '󱥵'),
("wile".to_string(), '󱥷'),
("namako".to_string(), '󱥸'),
("kin".to_string(), '󱥹'),
("oko".to_string(), '󱥺'),
("kipisi".to_string(), '󱥻'),
("leko".to_string(), '󱥼'),
("monsuta".to_string(), '󱥽'),
("tonsi".to_string(), '󱥾'),
("kijetesantakalu".to_string(), '󱦀'),
("soko".to_string(), '󱦁'),
("meso".to_string(), '󱦂'),
("n".to_string(), '󱦆'),
("misikeke".to_string(), '󱦇'),
//
]);
Self {
mapping,
buffer: String::new(),
}
}
}

View File

@@ -5,4 +5,5 @@ pub mod window_manager;
pub mod fs;
mod proxy_window_like;
mod essential;
mod input_method;

View File

@@ -25,6 +25,8 @@ use crate::essential::start_menu::StartMenu;
use crate::essential::about::About;
use crate::essential::help::Help;
use crate::essential::onscreen_keyboard::OnscreenKeyboard;
use crate::input_method::lat::Lat;
use crate::input_method::tok::Tok;
//todo: a lot of the usize should be changed to u16
@@ -52,7 +54,6 @@ impl fmt::Debug for WindowLikeInfo {
}
}
pub struct WindowManager {
writer: RefCell<FramebufferWriter>,
rotate: bool,
@@ -65,6 +66,8 @@ pub struct WindowManager {
focused_id: usize,
pub locked: bool,
current_workspace: u8,
im_list: Vec<Box<dyn InputMethod>>,
current_im: usize,
framebuffer: Framebuffer,
clipboard: Option<String>,
version: String,
@@ -88,6 +91,8 @@ impl WindowManager {
focused_id: 0,
locked: false,
current_workspace: 0,
im_list: vec![Box::new(Lat {}), Box::new(Tok::new())],
current_im: 0,
framebuffer,
clipboard: None,
version,
@@ -151,7 +156,9 @@ impl WindowManager {
self.window_infos = Vec::new();
self.add_window_like(Box::new(DesktopBackground::new()), [0, INDICATOR_HEIGHT], None);
self.add_window_like(Box::new(Taskbar::new()), [0, self.dimensions[1] - TASKBAR_HEIGHT], None);
self.add_window_like(Box::new(WorkspaceIndicator::new()), [0, 0], None);
let current_im = &self.im_list[self.current_im];
self.add_window_like(Box::new(WorkspaceIndicator::new(current_im.abbr().iter().collect()
)), [0, 0], None);
}
fn change_theme(&mut self) {
@@ -194,6 +201,24 @@ impl WindowManager {
self.window_infos[taskbar_index].window_like.handle_message(message);
}
fn im_update(&mut self, old_buffer: Option<String>) -> bool {
let current_im = &self.im_list[self.current_im];
let indicator_index = self.window_infos.iter().position(|w| w.window_like.subtype() == WindowLikeType::WorkspaceIndicator);
//no window indicator on lockscreen
if let Some(indicator_index) = indicator_index {
let current_buffer = current_im.internal_buffer();
//if old_buffer is None, force sending message
if old_buffer.is_none() || current_buffer != old_buffer.unwrap_or_default() {
self.window_infos[indicator_index].window_like.handle_message(WindowMessage::Info(InfoType::InputMethod(current_im.abbr().iter().collect(), current_buffer)));
true
} else {
false
}
} else {
false
}
}
fn move_index_to_top(&mut self, index: usize) {
let removed = self.window_infos.remove(index);
self.window_infos.push(removed);
@@ -238,6 +263,8 @@ impl WindowManager {
//shrink window size
('N', ShortcutType::ChangeWindowSize(Direction::Left)),
('M', ShortcutType::ChangeWindowSize(Direction::Up)),
//
(' ', ShortcutType::NextInputMethod),
//no 10th workspace
('1', ShortcutType::SwitchWorkspace(0)),
('2', ShortcutType::SwitchWorkspace(1)),
@@ -383,6 +410,16 @@ impl WindowManager {
}
}
},
&ShortcutType::NextInputMethod => {
let im_count = self.im_list.len();
if self.current_im + 1 == im_count {
self.current_im = 0;
} else {
self.current_im += 1;
}
self.im_update(None);
press_response = WindowMessageResponse::JustRedraw;
},
&ShortcutType::SwitchWorkspace(workspace) => {
if self.current_workspace != workspace {
//close start menu if open
@@ -527,17 +564,32 @@ impl WindowManager {
let mut press_response = WindowMessageResponse::DoNothing;
//send to focused window
if let Some(focused_index) = self.get_focused_index() {
press_response = self.window_infos[focused_index].window_like.handle_message(if key_char == KeyChar::Press(c) {
WindowMessage::KeyPress(KeyPress {
if key_char == KeyChar::Ctrl(c) {
press_response = self.window_infos[focused_index].window_like.handle_message(WindowMessage::CtrlKeyPress(KeyPress {
key: c,
})
}));
redraw_ids = Some(vec![self.window_infos[focused_index].id]);
} else {
WindowMessage::CtrlKeyPress(KeyPress {
key: c,
})
});
//at most, only the focused window needs to be redrawed
redraw_ids = Some(vec![self.window_infos[focused_index].id]);
let old_buffer = self.im_list[self.current_im].internal_buffer().clone();
//need to send the key to the input method
if let Some(ret_c) = self.im_list[self.current_im].add_key_press(KeyPress { key: c }) {
press_response = self.window_infos[focused_index].window_like.handle_message(WindowMessage::KeyPress(KeyPress {
key: ret_c,
}));
}
redraw_ids = Some(vec![self.window_infos[focused_index].id]);
let wi_updated = self.im_update(Some(old_buffer));
if wi_updated {
//ideally, make redraw_ids the focused window and the workspace indicator,
//but that doesn't work for some reason
if press_response == WindowMessageResponse::DoNothing {
press_response = WindowMessageResponse::JustRedraw;
redraw_ids = None;
}
} else {
redraw_ids = Some(vec![self.window_infos[focused_index].id]);
}
}
//requests can result in window openings and closings, etc
if press_response != WindowMessageResponse::JustRedraw {
redraw_ids = None;