Add touchscreen support with onscreen keyboard #1
@@ -12,6 +12,7 @@ fn main() {
|
||||
height: fb.var_screen_info.yres_virtual as usize,
|
||||
bytes_per_pixel,
|
||||
stride: fb.fix_screen_info.line_length as usize / bytes_per_pixel,
|
||||
old_stride: None,
|
||||
};
|
||||
|
||||
init(fb, fb_info);
|
||||
|
||||
@@ -10,8 +10,8 @@ use crate::window_manager::{ DrawInstructions };
|
||||
const MONO_WIDTH: u8 = 10;
|
||||
|
||||
pub struct PressButton<T> {
|
||||
top_left: Point,
|
||||
size: Dimensions,
|
||||
pub top_left: Point,
|
||||
pub size: Dimensions,
|
||||
text: String,
|
||||
press_return: T,
|
||||
}
|
||||
@@ -20,7 +20,9 @@ impl<T: Clone> Component<T> for PressButton<T> {
|
||||
//
|
||||
fn handle_message(&mut self, message: WindowMessage) -> Option<T> {
|
||||
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())
|
||||
},
|
||||
_ => None,
|
||||
|
||||
@@ -3,19 +3,20 @@ use std::vec::Vec;
|
||||
use std::collections::HashMap;
|
||||
|
||||
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::themes::ThemeInfo;
|
||||
use crate::components::Component;
|
||||
use crate::components::press_button::PressButton;
|
||||
use crate::utils::point_inside;
|
||||
|
||||
const padding_y: usize = 15;
|
||||
const padding_x: usize = 15;
|
||||
const PADDING_Y: usize = 15;
|
||||
const PADDING_X: usize = 15;
|
||||
//padding in between keys in the x direction
|
||||
const key_padding_x: usize = 5;
|
||||
const key_padding_y: usize = 5;
|
||||
const KEY_PADDING_X: usize = 5;
|
||||
const KEY_PADDING_Y: usize = 5;
|
||||
|
||||
#[derive(Default, Eq, PartialEq, Hash)]
|
||||
#[derive(Clone, Default, Eq, PartialEq, Hash)]
|
||||
enum Board {
|
||||
#[default]
|
||||
Regular,
|
||||
@@ -24,6 +25,17 @@ enum Board {
|
||||
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)]
|
||||
enum KeyResponse {
|
||||
Key(char),
|
||||
@@ -32,11 +44,15 @@ enum KeyResponse {
|
||||
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)]
|
||||
pub struct OnscreenKeyboard {
|
||||
dimensions: Dimensions,
|
||||
components: Vec<Box<PressButton<KeyResponse>>>,
|
||||
alt: bool,
|
||||
ctrl: bool,
|
||||
board: Board,
|
||||
}
|
||||
|
||||
@@ -48,7 +64,39 @@ impl WindowLike for OnscreenKeyboard {
|
||||
self.set_key_components();
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -60,7 +108,7 @@ impl WindowLike for OnscreenKeyboard {
|
||||
}
|
||||
instructions
|
||||
}
|
||||
//
|
||||
|
||||
fn subtype(&self) -> WindowLikeType {
|
||||
WindowLikeType::OnscreenKeyboard
|
||||
}
|
||||
@@ -93,7 +141,7 @@ impl OnscreenKeyboard {
|
||||
HashMap::from([
|
||||
(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::Symbols, vec!['~', '*', '"', '\'', ':', ';', '!', '?', '𐘁']), //basckspace
|
||||
(Board::Symbols, vec!['~', '*', '"', '\'', ':', ';', '!', '?', '𐘁']), //backspace
|
||||
(Board::SymbolsShift, vec!['[', ']', '{', '}', '𐘁']), //backspace
|
||||
]),
|
||||
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::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
|
||||
//ctrl = shimazu
|
||||
]),
|
||||
];
|
||||
//hardcoded for now
|
||||
let mut y = padding_y;
|
||||
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 mut y = PADDING_Y;
|
||||
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;
|
||||
for row in rows {
|
||||
let row_keys = &row[&self.board];
|
||||
//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 {
|
||||
let press_return = if key == &'𐘧' {
|
||||
KeyResponse::SwitchBoard
|
||||
@@ -121,10 +170,24 @@ impl OnscreenKeyboard {
|
||||
} else {
|
||||
KeyResponse::Key(*key)
|
||||
};
|
||||
self.components.push(Box::new(PressButton::new([x, y], [reg_key_width, key_height], key.to_string(), press_return)));
|
||||
x += reg_key_width + key_padding_x;
|
||||
let mut text = key.to_string();
|
||||
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();
|
||||
}
|
||||
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;
|
||||
y += key_height + KEY_PADDING_Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ pub struct FramebufferInfo {
|
||||
pub height: usize,
|
||||
pub bytes_per_pixel: 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
|
||||
@@ -45,6 +46,7 @@ pub struct FramebufferWriter {
|
||||
info: FramebufferInfo,
|
||||
buffer: Vec<u8>,
|
||||
saved_buffer: Option<Vec<u8>>,
|
||||
rotate_buffer: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl FramebufferWriter {
|
||||
@@ -57,10 +59,25 @@ impl FramebufferWriter {
|
||||
self.info.clone()
|
||||
}
|
||||
|
||||
pub fn get_buffer(&self) -> &[u8] {
|
||||
pub fn get_buffer(&mut self) -> &[u8] {
|
||||
&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) {
|
||||
self.saved_buffer = Some(self.buffer.clone());
|
||||
}
|
||||
@@ -237,6 +254,7 @@ impl Default for FramebufferWriter {
|
||||
info: Default::default(),
|
||||
buffer: Vec::new(),
|
||||
saved_buffer: None,
|
||||
rotate_buffer: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ pub enum WindowManagerRequest {
|
||||
CloseStartMenu,
|
||||
Unlock,
|
||||
Lock,
|
||||
DoKeyChar(KeyChar),
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ pub const TASKBAR_HEIGHT: usize = 38;
|
||||
pub const INDICATOR_HEIGHT: usize = 20;
|
||||
const WINDOW_TOP_HEIGHT: usize = 26;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub enum KeyChar {
|
||||
Press(char),
|
||||
Alt(char),
|
||||
@@ -53,6 +53,23 @@ enum ThreadMessage {
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
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());
|
||||
|
||||
let mut wm: WindowManager = WindowManager::new(writer, framebuffer, dimensions);
|
||||
let mut wm: WindowManager = WindowManager::new(writer, framebuffer, dimensions, rotate);
|
||||
|
||||
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());
|
||||
|
||||
//read touchscreen presses (hopefully)
|
||||
@@ -204,6 +220,7 @@ impl fmt::Debug for WindowLikeInfo {
|
||||
|
||||
pub struct WindowManager {
|
||||
writer: RefCell<FramebufferWriter>,
|
||||
rotate: bool,
|
||||
id_count: usize,
|
||||
window_infos: Vec<WindowLikeInfo>,
|
||||
osk: Option<WindowLikeInfo>,
|
||||
@@ -219,9 +236,10 @@ pub struct WindowManager {
|
||||
//1 is up, 2 is down
|
||||
|
||||
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 {
|
||||
writer: RefCell::new(writer),
|
||||
rotate,
|
||||
id_count: 0,
|
||||
window_infos: Vec::new(),
|
||||
osk: None,
|
||||
@@ -612,7 +630,7 @@ impl WindowManager {
|
||||
} else {
|
||||
//see if in onscreen keyboard, if so send to it after offsetting coords
|
||||
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) {
|
||||
osk.window_like.handle_message(WindowMessage::Touch(x - osk.top_left[0], y - osk.top_left[1]))
|
||||
} else {
|
||||
@@ -692,6 +710,9 @@ impl WindowManager {
|
||||
WindowManagerRequest::ClipboardCopy(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());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user