Add touchscreen support with onscreen keyboard #1

Merged
stjet merged 21 commits from dev into master 2025-02-10 05:03:49 +00:00
6 changed files with 135 additions and 26 deletions
Showing only changes of commit 76daaff023 - Show all commits

View File

@@ -12,6 +12,7 @@ fn main() {
height: fb.var_screen_info.yres_virtual as usize, height: fb.var_screen_info.yres_virtual as usize,
bytes_per_pixel, bytes_per_pixel,
stride: fb.fix_screen_info.line_length as usize / bytes_per_pixel, stride: fb.fix_screen_info.line_length as usize / bytes_per_pixel,
old_stride: None,
}; };
init(fb, fb_info); init(fb, fb_info);

View File

@@ -10,8 +10,8 @@ use crate::window_manager::{ DrawInstructions };
const MONO_WIDTH: u8 = 10; const MONO_WIDTH: u8 = 10;
pub struct PressButton<T> { pub struct PressButton<T> {
top_left: Point, pub top_left: Point,
size: Dimensions, pub size: Dimensions,
text: String, text: String,
press_return: T, press_return: T,
} }
@@ -20,7 +20,9 @@ impl<T: Clone> Component<T> for PressButton<T> {
// //
fn handle_message(&mut self, message: WindowMessage) -> Option<T> { fn handle_message(&mut self, message: WindowMessage) -> Option<T> {
match message { match message {
WindowMessage::Touch(x, y) => { WindowMessage::Touch(_, _) => {
//assume that the parent window-like passed it to us intentionally and checked already
//we can check again, but why?
Some(self.press_return.clone()) Some(self.press_return.clone())
}, },
_ => None, _ => None,

View File

@@ -3,19 +3,20 @@ use std::vec::Vec;
use std::collections::HashMap; use std::collections::HashMap;
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType, KeyChar }; use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType, KeyChar };
use crate::messages::{ WindowMessage, WindowMessageResponse }; use crate::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest };
use crate::framebuffer::Dimensions; use crate::framebuffer::Dimensions;
use crate::themes::ThemeInfo; use crate::themes::ThemeInfo;
use crate::components::Component; use crate::components::Component;
use crate::components::press_button::PressButton; use crate::components::press_button::PressButton;
use crate::utils::point_inside;
const padding_y: usize = 15; const PADDING_Y: usize = 15;
const padding_x: usize = 15; const PADDING_X: usize = 15;
//padding in between keys in the x direction //padding in between keys in the x direction
const key_padding_x: usize = 5; const KEY_PADDING_X: usize = 5;
const key_padding_y: usize = 5; const KEY_PADDING_Y: usize = 5;
#[derive(Default, Eq, PartialEq, Hash)] #[derive(Clone, Default, Eq, PartialEq, Hash)]
enum Board { enum Board {
#[default] #[default]
Regular, Regular,
@@ -24,6 +25,17 @@ enum Board {
SymbolsShift, SymbolsShift,
} }
impl Board {
fn inc(&mut self) -> Self {
match self {
Board::Regular => Board::Shift,
Board::Shift => Board::Symbols,
Board::Symbols => Board::SymbolsShift,
Board::SymbolsShift => Board::Regular,
}
}
}
#[derive(Clone)] #[derive(Clone)]
enum KeyResponse { enum KeyResponse {
Key(char), Key(char),
@@ -32,11 +44,15 @@ enum KeyResponse {
SwitchBoard, SwitchBoard,
} }
//if alt is true and ctrl is true, only alt will be sent.
//because I don't care about ctrl+alt stuff, and won't use it.
//(and probably not supported by this with a real keyboard anyways)
#[derive(Default)] #[derive(Default)]
pub struct OnscreenKeyboard { pub struct OnscreenKeyboard {
dimensions: Dimensions, dimensions: Dimensions,
components: Vec<Box<PressButton<KeyResponse>>>, components: Vec<Box<PressButton<KeyResponse>>>,
alt: bool, alt: bool,
ctrl: bool,
board: Board, board: Board,
} }
@@ -48,7 +64,39 @@ impl WindowLike for OnscreenKeyboard {
self.set_key_components(); self.set_key_components();
WindowMessageResponse::JustRedraw WindowMessageResponse::JustRedraw
}, },
// WindowMessage::Touch(x, y) => {
for c in &mut self.components {
if point_inside([x, y], c.top_left, c.size) {
let returned = c.handle_message(WindowMessage::Touch(x, y));
if let Some(returned) = returned {
return match returned {
KeyResponse::Key(ch) => {
WindowMessageResponse::Request(WindowManagerRequest::DoKeyChar(if self.alt {
KeyChar::Alt(ch)
} else if self.ctrl {
KeyChar::Ctrl(ch)
} else {
KeyChar::Press(ch)
}))
},
KeyResponse::Alt => {
self.alt = !self.alt;
WindowMessageResponse::DoNothing
},
KeyResponse::Ctrl => {
self.ctrl = !self.ctrl;
WindowMessageResponse::DoNothing
},
KeyResponse::SwitchBoard => {
self.board = self.board.inc();
WindowMessageResponse::DoNothing
},
};
}
}
}
WindowMessageResponse::DoNothing
},
_ => WindowMessageResponse::DoNothing, _ => WindowMessageResponse::DoNothing,
} }
} }
@@ -60,7 +108,7 @@ impl WindowLike for OnscreenKeyboard {
} }
instructions instructions
} }
//
fn subtype(&self) -> WindowLikeType { fn subtype(&self) -> WindowLikeType {
WindowLikeType::OnscreenKeyboard WindowLikeType::OnscreenKeyboard
} }
@@ -93,7 +141,7 @@ impl OnscreenKeyboard {
HashMap::from([ HashMap::from([
(Board::Regular, vec!['𐘃', 'z', 'x', 'c', 'v', 'b', 'n', 'm', '𐘁']), //escape and backspace (Board::Regular, vec!['𐘃', 'z', 'x', 'c', 'v', 'b', 'n', 'm', '𐘁']), //escape and backspace
(Board::Shift, vec!['𐘃', 'z', 'x', 'c', 'v', 'b', 'n', 'm', '𐘁']), //escape and backspace (Board::Shift, vec!['𐘃', 'z', 'x', 'c', 'v', 'b', 'n', 'm', '𐘁']), //escape and backspace
(Board::Symbols, vec!['~', '*', '"', '\'', ':', ';', '!', '?', '𐘁']), //basckspace (Board::Symbols, vec!['~', '*', '"', '\'', ':', ';', '!', '?', '𐘁']), //backspace
(Board::SymbolsShift, vec!['[', ']', '{', '}', '𐘁']), //backspace (Board::SymbolsShift, vec!['[', ']', '{', '}', '𐘁']), //backspace
]), ]),
HashMap::from([ HashMap::from([
@@ -101,16 +149,17 @@ impl OnscreenKeyboard {
(Board::Shift, vec!['𐘧', '𐘎', ' ', '𐘂']), //switch board (special case, not a real key), alt (special case, not a real key), enter (Board::Shift, vec!['𐘧', '𐘎', ' ', '𐘂']), //switch board (special case, not a real key), alt (special case, not a real key), enter
(Board::Symbols, vec!['𐘧', '𐘎', ',', '_', ' ', '/', '.', '𐘂']), //switch board (special case, not a real key), alt (special case, not a real key), enter (Board::Symbols, vec!['𐘧', '𐘎', ',', '_', ' ', '/', '.', '𐘂']), //switch board (special case, not a real key), alt (special case, not a real key), enter
(Board::SymbolsShift, vec!['𐘧', '𐘎', '\\', '<', ' ', '>', '𐘾', '𐘂']), //switch board (special case, not a real key), alt (special case, not a real key), ctrl (special case, not a real key), enter (Board::SymbolsShift, vec!['𐘧', '𐘎', '\\', '<', ' ', '>', '𐘾', '𐘂']), //switch board (special case, not a real key), alt (special case, not a real key), ctrl (special case, not a real key), enter
//ctrl = shimazu
]), ]),
]; ];
//hardcoded for now //hardcoded for now
let mut y = padding_y; let mut y = PADDING_Y;
let key_height = (self.dimensions[1] - padding_y * 2 - key_padding_y * (rows.len() - 1)) / rows.len(); let key_height = (self.dimensions[1] - PADDING_Y * 2 - KEY_PADDING_Y * (rows.len() - 1)) / rows.len();
let reg_key_width = (self.dimensions[0] - padding_x * 2 - key_padding_x * (10 - 1)) / 10; let reg_key_width = (self.dimensions[0] - PADDING_X * 2 - KEY_PADDING_X * (10 - 1)) / 10;
for row in rows { for row in rows {
let row_keys = &row[&self.board]; let row_keys = &row[&self.board];
//centre //centre
let mut x = padding_x + (10 - row_keys.len()) * (reg_key_width + key_padding_x) / 2; let mut x = PADDING_X + (10 - row_keys.len()) * (reg_key_width + KEY_PADDING_X) / 2;
for key in row_keys { for key in row_keys {
let press_return = if key == &'𐘧' { let press_return = if key == &'𐘧' {
KeyResponse::SwitchBoard KeyResponse::SwitchBoard
@@ -121,10 +170,24 @@ impl OnscreenKeyboard {
} else { } else {
KeyResponse::Key(*key) KeyResponse::Key(*key)
}; };
self.components.push(Box::new(PressButton::new([x, y], [reg_key_width, key_height], key.to_string(), press_return))); let mut text = key.to_string();
x += reg_key_width + key_padding_x; if text == "𐘧" {
text = "Switch".to_string();
} else if text == "𐘎" {
text = "Alt".to_string();
} else if text == "𐘂" {
text = "Enter".to_string();
} else if text == "𐘁" {
text = "Back".to_string();
} else if text == "𐘃" {
text = "Esc".to_string();
} else if text == "𐘾" {
text = "Ctrl".to_string();
} }
y += key_height + key_padding_y; self.components.push(Box::new(PressButton::new([x, y], [reg_key_width, key_height], text, press_return)));
x += reg_key_width + KEY_PADDING_X;
}
y += key_height + KEY_PADDING_Y;
} }
} }
} }

View File

@@ -38,6 +38,7 @@ pub struct FramebufferInfo {
pub height: usize, pub height: usize,
pub bytes_per_pixel: usize, pub bytes_per_pixel: usize,
pub stride: usize, pub stride: usize,
pub old_stride: Option<usize>, //used/set only when rotate is true
} }
//currently doesn't check if writing onto next line accidentally //currently doesn't check if writing onto next line accidentally
@@ -45,6 +46,7 @@ pub struct FramebufferWriter {
info: FramebufferInfo, info: FramebufferInfo,
buffer: Vec<u8>, buffer: Vec<u8>,
saved_buffer: Option<Vec<u8>>, saved_buffer: Option<Vec<u8>>,
rotate_buffer: Option<Vec<u8>>,
} }
impl FramebufferWriter { impl FramebufferWriter {
@@ -57,10 +59,25 @@ impl FramebufferWriter {
self.info.clone() self.info.clone()
} }
pub fn get_buffer(&self) -> &[u8] { pub fn get_buffer(&mut self) -> &[u8] {
&self.buffer &self.buffer
} }
pub fn get_transposed_buffer(&mut self) -> &[u8] {
let mut output_array = vec![255; self.info.byte_len];
let row_bytes_len = self.info.stride * self.info.bytes_per_pixel;
let row_bytes_len_transposed = self.info.old_stride.unwrap_or(self.info.height) * self.info.bytes_per_pixel;
for y in 0..self.info.height {
for x in 0..self.info.width {
for i in 0..self.info.bytes_per_pixel {
output_array[x * row_bytes_len_transposed + y * self.info.bytes_per_pixel + i] = self.buffer[y * row_bytes_len + x * self.info.bytes_per_pixel + i];
}
}
}
self.rotate_buffer = Some(output_array);
&self.rotate_buffer.as_ref().unwrap()
}
pub fn save_buffer(&mut self) { pub fn save_buffer(&mut self) {
self.saved_buffer = Some(self.buffer.clone()); self.saved_buffer = Some(self.buffer.clone());
} }
@@ -237,6 +254,7 @@ impl Default for FramebufferWriter {
info: Default::default(), info: Default::default(),
buffer: Vec::new(), buffer: Vec::new(),
saved_buffer: None, saved_buffer: None,
rotate_buffer: None,
} }
} }
} }

View File

@@ -31,6 +31,7 @@ pub enum WindowManagerRequest {
CloseStartMenu, CloseStartMenu,
Unlock, Unlock,
Lock, Lock,
DoKeyChar(KeyChar),
// //
} }

View File

@@ -39,7 +39,7 @@ pub const TASKBAR_HEIGHT: usize = 38;
pub const INDICATOR_HEIGHT: usize = 20; pub const INDICATOR_HEIGHT: usize = 20;
const WINDOW_TOP_HEIGHT: usize = 26; const WINDOW_TOP_HEIGHT: usize = 26;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum KeyChar { pub enum KeyChar {
Press(char), Press(char),
Alt(char), Alt(char),
@@ -53,6 +53,23 @@ enum ThreadMessage {
} }
pub fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) { pub fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
let args: Vec<_> = env::args().collect();
let rotate = args.contains(&"rotate".to_string());
let framebuffer_info = if rotate {
FramebufferInfo {
byte_len: framebuffer_info.byte_len,
width: framebuffer_info.height,
height: framebuffer_info.width,
bytes_per_pixel: framebuffer_info.bytes_per_pixel,
stride: framebuffer_info.height,
old_stride: Some(framebuffer_info.stride),
}
} else {
framebuffer_info
};
let dimensions = [framebuffer_info.width, framebuffer_info.height]; let dimensions = [framebuffer_info.width, framebuffer_info.height];
println!("bg: {}x{}", dimensions[0], dimensions[1] - TASKBAR_HEIGHT - INDICATOR_HEIGHT); println!("bg: {}x{}", dimensions[0], dimensions[1] - TASKBAR_HEIGHT - INDICATOR_HEIGHT);
@@ -61,7 +78,7 @@ pub fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
writer.init(framebuffer_info.clone()); writer.init(framebuffer_info.clone());
let mut wm: WindowManager = WindowManager::new(writer, framebuffer, dimensions); let mut wm: WindowManager = WindowManager::new(writer, framebuffer, dimensions, rotate);
wm.draw(None, false); wm.draw(None, false);
@@ -91,7 +108,6 @@ pub fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
} }
}); });
let args: Vec<_> = env::args().collect();
let touch = args.contains(&"touch".to_string()); let touch = args.contains(&"touch".to_string());
//read touchscreen presses (hopefully) //read touchscreen presses (hopefully)
@@ -204,6 +220,7 @@ impl fmt::Debug for WindowLikeInfo {
pub struct WindowManager { pub struct WindowManager {
writer: RefCell<FramebufferWriter>, writer: RefCell<FramebufferWriter>,
rotate: bool,
id_count: usize, id_count: usize,
window_infos: Vec<WindowLikeInfo>, window_infos: Vec<WindowLikeInfo>,
osk: Option<WindowLikeInfo>, osk: Option<WindowLikeInfo>,
@@ -219,9 +236,10 @@ pub struct WindowManager {
//1 is up, 2 is down //1 is up, 2 is down
impl WindowManager { impl WindowManager {
pub fn new(writer: FramebufferWriter, framebuffer: Framebuffer, dimensions: Dimensions) -> Self { pub fn new(writer: FramebufferWriter, framebuffer: Framebuffer, dimensions: Dimensions, rotate: bool) -> Self {
let mut wm = WindowManager { let mut wm = WindowManager {
writer: RefCell::new(writer), writer: RefCell::new(writer),
rotate,
id_count: 0, id_count: 0,
window_infos: Vec::new(), window_infos: Vec::new(),
osk: None, osk: None,
@@ -612,7 +630,7 @@ impl WindowManager {
} else { } else {
//see if in onscreen keyboard, if so send to it after offsetting coords //see if in onscreen keyboard, if so send to it after offsetting coords
if self.osk.is_some() { if self.osk.is_some() {
let mut osk = self.osk.as_mut().unwrap(); let osk = self.osk.as_mut().unwrap();
if point_inside([x, y], osk.top_left, osk.dimensions) { if point_inside([x, y], osk.top_left, osk.dimensions) {
osk.window_like.handle_message(WindowMessage::Touch(x - osk.top_left[0], y - osk.top_left[1])) osk.window_like.handle_message(WindowMessage::Touch(x - osk.top_left[0], y - osk.top_left[1]))
} else { } else {
@@ -692,6 +710,9 @@ impl WindowManager {
WindowManagerRequest::ClipboardCopy(content) => { WindowManagerRequest::ClipboardCopy(content) => {
self.clipboard = Some(content); self.clipboard = Some(content);
}, },
WindowManagerRequest::DoKeyChar(kc) => {
self.handle_message(WindowManagerMessage::KeyChar(kc));
},
}; };
} }
@@ -804,6 +825,9 @@ impl WindowManager {
self.writer.borrow_mut().draw_buffer(window_info.top_left, window_dimensions[1], window_dimensions[0] * bytes_per_pixel, &window_writer.get_buffer()); self.writer.borrow_mut().draw_buffer(window_info.top_left, window_dimensions[1], window_dimensions[0] * bytes_per_pixel, &window_writer.get_buffer());
w_index += 1; w_index += 1;
} }
self.framebuffer.write_frame(self.writer.borrow().get_buffer()); //could probably figure out a way to do borrow() when self.rotate is false but does it matter?
let mut writer_borrow = self.writer.borrow_mut();
let frame = if self.rotate { writer_borrow.get_transposed_buffer() } else { writer_borrow.get_buffer() };
self.framebuffer.write_frame(frame);
} }
} }