Add touchscreen support with onscreen keyboard #1

Merged
stjet merged 21 commits from dev into master 2025-02-10 05:03:49 +00:00
13 changed files with 536 additions and 72 deletions

View File

@@ -1,4 +1,4 @@
Audio player with playlist and folder support.
Audio player with playlist and folder support. Requires the dev version of `alsa` lib.
## Commands

View File

@@ -0,0 +1 @@
This is primarily intended for mobile users.

View File

@@ -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);

View File

@@ -7,6 +7,7 @@ use crate::window_manager::DrawInstructions;
pub mod toggle_button;
pub mod highlight_button;
pub mod paragraph;
pub mod press_button;
pub trait Component<T> {
fn handle_message(&mut self, message: WindowMessage) -> Option<T>;
@@ -17,6 +18,7 @@ pub trait Component<T> {
//focusing for components is purely to give a visual representation
fn focusable(&self) -> bool;
fn clickable(&self) -> bool;
//fn pressable(&self) -> bool; //touch
fn name(&self) -> &String; //should be unique
}

View File

@@ -0,0 +1,69 @@
use std::vec;
use std::vec::Vec;
use crate::components::Component;
use crate::framebuffer::{ Dimensions, Point };
use crate::themes::ThemeInfo;
use crate::messages::WindowMessage;
use crate::window_manager::{ DrawInstructions };
const MONO_WIDTH: u8 = 10;
pub struct PressButton<T> {
pub top_left: Point,
pub size: Dimensions,
text: String,
press_return: T,
}
impl<T: Clone> Component<T> for PressButton<T> {
//
fn handle_message(&mut self, message: WindowMessage) -> Option<T> {
match message {
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,
}
}
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
let half = self.size[0] / 2 - self.text.len() * MONO_WIDTH as usize / 2;
vec![
DrawInstructions::Rect(self.top_left, [self.size[0], 1], theme_info.border_left_top),
DrawInstructions::Rect(self.top_left, [1, self.size[1]], theme_info.border_left_top),
DrawInstructions::Rect([self.top_left[0], self.top_left[1] + self.size[1]], [self.size[0], 1], theme_info.border_right_bottom),
DrawInstructions::Rect([self.top_left[0] + self.size[0], self.top_left[1]], [1, self.size[1]], theme_info.border_right_bottom),
//assume normal background colour
DrawInstructions::Text([self.top_left[0] + half, self.top_left[1] + 8], vec!["times-new-romono".to_string()], self.text.clone(), theme_info.text, theme_info.background, Some(0), Some(MONO_WIDTH)),
]
}
//properties
fn focusable(&self) -> bool {
false
}
fn clickable(&self) -> bool {
false
}
fn name(&self) -> &String {
//sorry
&self.text
}
}
impl<T> PressButton<T> {
pub fn new(top_left: Point, size: Dimensions, text: String, press_return: T) -> Self {
Self {
top_left,
size,
text,
press_return,
}
}
}

View File

@@ -3,6 +3,7 @@ pub mod taskbar;
pub mod lock_screen;
pub mod workspace_indicator;
pub mod start_menu;
pub mod onscreen_keyboard;
pub mod about;
pub mod help;

View File

@@ -0,0 +1,200 @@
use std::vec;
use std::vec::Vec;
use std::collections::HashMap;
use crate::window_manager::{ DrawInstructions, WindowLike, WindowLikeType, KeyChar };
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;
//seems like framebuffer only updates if (real) key is pressed...
//on mobile, volume down button seems to work but is annoying
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;
#[derive(Clone, Default, Eq, PartialEq, Hash)]
enum Board {
#[default]
Regular,
Shift,
Symbols,
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),
Alt,
Ctrl,
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,
}
impl WindowLike for OnscreenKeyboard {
fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse {
match message {
WindowMessage::Init(dimensions) => {
self.dimensions = dimensions;
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) => {
let kc = if self.alt {
self.alt = false;
KeyChar::Alt(ch)
} else if self.ctrl {
self.ctrl = false;
KeyChar::Ctrl(ch)
} else {
KeyChar::Press(ch)
};
WindowMessageResponse::Request(WindowManagerRequest::DoKeyChar(kc))
},
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,
}
}
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
let mut instructions = vec![DrawInstructions::Rect([0, 0], self.dimensions, theme_info.background)];
for component in &self.components {
instructions.extend(component.draw(theme_info));
}
instructions
}
fn subtype(&self) -> WindowLikeType {
WindowLikeType::OnscreenKeyboard
}
fn ideal_dimensions(&self, dimensions: Dimensions) -> Dimensions {
[dimensions[0] - 175, 250]
}
}
impl OnscreenKeyboard {
pub fn new() -> Self {
Default::default()
}
fn set_key_components(&mut self) {
self.components = Vec::new();
let rows: [HashMap<Board, Vec<char>>; 4] = [
HashMap::from([
(Board::Regular, vec!['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']),
(Board::Shift, vec!['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P']),
(Board::Symbols, vec!['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']),
(Board::SymbolsShift, vec![]), //empty
]),
HashMap::from([
(Board::Regular, vec!['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l']),
(Board::Shift, vec!['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L']),
(Board::Symbols, vec!['|', '=', '$', '%', '&', '-', '+', '(', ')']),
(Board::SymbolsShift, vec!['`', '@', '#', '^']),
]),
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!['~', '*', '"', '\'', ':', ';', '!', '?', '𐘁']), //backspace
(Board::SymbolsShift, vec!['[', ']', '{', '}', '𐘁']), //backspace
]),
HashMap::from([
(Board::Regular, 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::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;
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;
for key in row_keys {
let press_return = if key == &'𐘧' {
KeyResponse::SwitchBoard
} else if key == &'𐘎' {
KeyResponse::Alt
} else if key == &'𐘾' {
KeyResponse::Ctrl
} else {
KeyResponse::Key(*key)
};
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;
}
}
}

View File

@@ -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[(self.info.width - x - 1) * 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,
}
}
}

View File

@@ -1,22 +0,0 @@
use termion::event::Key;
#[derive(Clone, Debug, PartialEq)]
pub enum KeyChar {
Press(char),
Alt(char),
Ctrl(char),
}
//use Linear A for escape, backspace, enter
pub fn key_to_char(key: Key) -> Option<KeyChar> {
match key {
Key::Char('\n') => Some(KeyChar::Press('𐘂')),
Key::Char(c) => Some(KeyChar::Press(c)),
Key::Alt(c) => Some(KeyChar::Alt(c)),
Key::Ctrl(c) => Some(KeyChar::Ctrl(c)),
Key::Backspace => Some(KeyChar::Press('𐘁')),
Key::Esc => Some(KeyChar::Press('𐘃')),
_ => None,
}
}

View File

@@ -8,6 +8,5 @@ pub mod utils;
pub mod logging;
pub mod ipc;
mod proxy_window_like;
mod keyboard;
mod essential;

View File

@@ -4,12 +4,12 @@ use std::vec::Vec;
use serde::{ Deserialize, Serialize };
use crate::keyboard::KeyChar;
use crate::framebuffer::Dimensions;
use crate::window_manager::WindowLike;
use crate::window_manager::{ WindowLike, KeyChar };
pub enum WindowManagerMessage {
KeyChar(KeyChar),
Touch(usize, usize),
//
}
@@ -24,22 +24,17 @@ impl PartialEq for WindowBox {
}
*/
#[derive(PartialEq, Serialize, Deserialize)]
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum WindowManagerRequest {
OpenWindow(String),
ClipboardCopy(String),
CloseStartMenu,
Unlock,
Lock,
DoKeyChar(KeyChar),
//
}
impl fmt::Debug for WindowManagerRequest{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "WindowManagerRequest lmao")
}
}
#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub enum WindowMessageResponse {
Request(WindowManagerRequest),
@@ -47,6 +42,16 @@ pub enum WindowMessageResponse {
DoNothing,
}
impl WindowMessageResponse {
pub fn is_key_char_request(&self) -> bool {
if let WindowMessageResponse::Request(WindowManagerRequest::DoKeyChar(_)) = self {
true
} else {
false
}
}
}
#[derive(Serialize, Deserialize)]
pub struct KeyPress {
pub key: char,
@@ -99,5 +104,14 @@ pub enum WindowMessage {
Unfocus,
FocusClick,
ChangeDimensions(Dimensions),
Touch(usize, usize), //for onscreen keyboard only
//
}
pub enum ThreadMessage {
KeyChar(KeyChar),
Touch(usize, usize),
Clear,
Exit,
}

View File

@@ -1,5 +1,28 @@
use std::path::PathBuf;
use termion::event::Key;
use crate::window_manager::KeyChar;
use crate::framebuffer::{ Dimensions, Point };
//use Linear A for escape, backspace, enter
//Linear A used only internally in onscreen keyboard: 𐘎 is alt, 𐘧 is switch board, 𐘾 is ctrl
pub fn key_to_char(key: Key) -> Option<KeyChar> {
match key {
Key::Char('\n') => Some(KeyChar::Press('𐘂')),
Key::Char(c) => Some(KeyChar::Press(c)),
Key::Alt(c) => Some(KeyChar::Alt(c)),
Key::Ctrl(c) => Some(KeyChar::Ctrl(c)),
Key::Backspace => Some(KeyChar::Press('𐘁')),
Key::Esc => Some(KeyChar::Press('𐘃')),
_ => None,
}
}
pub fn min(one: usize, two: usize) -> usize {
if one > two { two } else { one }
}
pub trait Substring {
fn substring(&self, start: usize, end: usize) -> &str;
fn remove(&self, index: usize, len: usize) -> String;
@@ -20,7 +43,6 @@ impl Substring for String {
break;
}
byte_end += char_length;
}
&self[byte_start..byte_end]
}
@@ -125,3 +147,13 @@ pub fn is_hex(c: char) -> bool {
HEX_CHARS.iter().position(|hc| hc == &c).is_some()
}
pub fn point_inside(point: Point, top_left: Point, size: Dimensions) -> bool {
let x = point[0];
let y = point[1];
let x2 = top_left[0];
let y2 = top_left[1];
let x3 = x2 + size[0];
let y3 = y2 + size[1];
x >= x2 && y >= y2 && x <= x3 && y <= y3
}

View File

@@ -3,19 +3,24 @@ use std::vec;
use std::collections::{ HashMap, VecDeque };
use std::fmt;
use std::boxed::Box;
use std::io::{ stdin, stdout, Write };
use std::io::{ stdin, stdout, BufReader, BufRead, Write };
use std::process::exit;
use std::cell::RefCell;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use std::env;
use std::process::{ Command, Stdio };
use linux_framebuffer::Framebuffer;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use termion::cursor;
use termion::{ clear, cursor };
use serde::{ Deserialize, Serialize };
use crate::framebuffer::{ FramebufferWriter, FramebufferInfo, Point, Dimensions, RGBColor };
use crate::themes::{ ThemeInfo, Themes, get_theme_info };
use crate::keyboard::{ KeyChar, key_to_char };
use crate::utils::{ min, key_to_char, point_inside };
use crate::messages::*;
use crate::proxy_window_like::ProxyWindowLike;
use crate::essential::desktop_background::DesktopBackground;
@@ -25,13 +30,40 @@ use crate::essential::workspace_indicator::WorkspaceIndicator;
use crate::essential::start_menu::StartMenu;
use crate::essential::about::About;
use crate::essential::help::Help;
use crate::essential::onscreen_keyboard::OnscreenKeyboard;
//use crate::logging::log;
//todo: a lot of the usize should be changed to u16
pub const TASKBAR_HEIGHT: usize = 38;
pub const INDICATOR_HEIGHT: usize = 20;
const WINDOW_TOP_HEIGHT: usize = 26;
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum KeyChar {
Press(char),
Alt(char),
Ctrl(char),
}
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);
@@ -40,38 +72,103 @@ 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);
let mut stdout = stdout().into_raw_mode().unwrap();
write!(stdout, "{}", clear::All).unwrap();
write!(stdout, "{}", cursor::Hide).unwrap();
stdout.flush().unwrap();
wm.draw(None, false);
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();
//read key presses
thread::spawn(move || {
let stdin = stdin().lock();
let mut stdout = stdout().into_raw_mode().unwrap();
write!(stdout, "{}", cursor::Hide).unwrap();
stdout.flush().unwrap();
for c in stdin.keys() {
if let Some(kc) = key_to_char(c.unwrap()) {
//do not allow exit when locked unless debugging
//if kc == KeyChar::Alt('E') {
if kc == KeyChar::Alt('E') && !wm.locked {
if kc == KeyChar::Alt('E') {
tx.send(ThreadMessage::Exit).unwrap();
} else {
tx.send(ThreadMessage::KeyChar(kc)).unwrap();
}
}
thread::sleep(Duration::from_millis(1));
}
});
let touch = args.contains(&"touch".to_string());
//read touchscreen presses (hopefully)
thread::spawn(move || {
//spawn evtest, parse it for touch coords
if touch {
let mut evtest = Command::new("evtest").arg("/dev/input/by-path/first-touchscreen").stdout(Stdio::piped()).spawn().unwrap();
let reader = BufReader::new(evtest.stdout.as_mut().unwrap());
let mut x: Option<usize> = None;
let mut y: Option<usize> = None;
for line in reader.lines() {
let line = line.unwrap();
if line.contains(&"ABS_X), value ") || line.contains(&"ABS_Y), value ") {
let value: Vec<_> = line.split("), value ").collect();
let value = value[value.len() - 1].parse::<usize>().unwrap();
if line.contains(&"ABS_X") {
x = Some(value);
} else {
y = Some(value);
}
if x.is_some() && y.is_some() {
let (x2, y2) = if rotate {
(y.unwrap(), dimensions[1] - x.unwrap())
} else {
(x.unwrap(), y.unwrap())
};
//top right, clear
//useful sometimes, I think.
if x2 > dimensions[0] - 100 && y2 < 100 {
tx1.send(ThreadMessage::Clear).unwrap();
}
tx1.send(ThreadMessage::Touch(x2, y2)).unwrap();
x = None;
y = None;
}
}
thread::sleep(Duration::from_millis(1));
}
}
});
if touch {
//opens osk
wm.handle_message(WindowManagerMessage::Touch(1, 1));
}
for message in rx {
match message {
ThreadMessage::KeyChar(kc) => wm.handle_message(WindowManagerMessage::KeyChar(kc.clone())),
ThreadMessage::Touch(x, y) => wm.handle_message(WindowManagerMessage::Touch(x, y)),
ThreadMessage::Clear => {
write!(stdout, "{}", clear::All).unwrap();
stdout.flush().unwrap();
},
ThreadMessage::Exit => {
if !wm.locked {
write!(stdout, "{}", cursor::Show).unwrap();
stdout.suspend_raw_mode().unwrap();
exit(0);
} else {
wm.handle_message(WindowManagerMessage::KeyChar(kc.clone()));
}
},
};
}
}
//
}
pub fn min(one: usize, two: usize) -> usize {
if one > two { two } else { one }
}
#[derive(Debug, Serialize, Deserialize)]
pub enum DrawInstructions {
Rect(Point, Dimensions, RGBColor),
@@ -89,6 +186,7 @@ pub enum WindowLikeType {
Taskbar,
StartMenu,
WorkspaceIndicator,
OnscreenKeyboard,
}
pub trait WindowLike {
@@ -133,8 +231,10 @@ impl fmt::Debug for WindowLikeInfo {
pub struct WindowManager {
writer: RefCell<FramebufferWriter>,
rotate: bool,
id_count: usize,
window_infos: Vec<WindowLikeInfo>,
osk: Option<WindowLikeInfo>,
dimensions: Dimensions,
theme: Themes,
focused_id: usize,
@@ -147,11 +247,13 @@ 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,
dimensions,
theme: Themes::Standard,
focused_id: 0,
@@ -169,10 +271,9 @@ impl WindowManager {
let dimensions = dimensions.unwrap_or(window_like.ideal_dimensions(self.dimensions));
self.id_count = self.id_count + 1;
let id = self.id_count;
self.focused_id = id;
window_like.handle_message(WindowMessage::Init(dimensions));
let dimensions = if window_like.subtype() == WindowLikeType::Window { [dimensions[0], dimensions[1] + WINDOW_TOP_HEIGHT] } else { dimensions };
self.window_infos.push(WindowLikeInfo {
let window_info = WindowLikeInfo {
id,
window_like,
top_left,
@@ -183,7 +284,13 @@ impl WindowManager {
Workspace::All
},
fullscreen: false,
});
};
if subtype == WindowLikeType::OnscreenKeyboard {
self.osk = Some(window_info);
} else {
self.focused_id = id;
self.window_infos.push(window_info);
}
}
fn get_focused_index(&self) -> Option<usize> {
@@ -215,6 +322,7 @@ impl WindowManager {
}
//if off_only is true, also handle request
//written confusingly but it works I promise
fn toggle_start_menu(&mut self, off_only: bool) -> WindowMessageResponse {
let start_menu_exists = self.window_infos.iter().find(|w| w.window_like.subtype() == WindowLikeType::StartMenu).is_some();
if (start_menu_exists && off_only) || !off_only {
@@ -518,22 +626,53 @@ impl WindowManager {
},
}
},
//
WindowManagerMessage::Touch(x, y) => {
if x < 100 && y < 100 {
//toggle onscreen keyboard if top left keyboard clicked
if self.osk.is_some() {
self.osk = None;
} else {
let osk = Box::new(OnscreenKeyboard::new());
let ideal_dimensions = osk.ideal_dimensions(self.dimensions);
self.add_window_like(osk, [175, self.dimensions[1] - TASKBAR_HEIGHT - 250], Some(ideal_dimensions));
}
WindowMessageResponse::JustRedraw
} else {
//see if in onscreen keyboard, if so send to it after offsetting coords
if self.osk.is_some() {
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 {
WindowMessageResponse::DoNothing
}
} else {
WindowMessageResponse::DoNothing
}
}
}
};
if response != WindowMessageResponse::DoNothing {
let is_key_char_request = response.is_key_char_request();
match response {
WindowMessageResponse::Request(request) => self.handle_request(request),
_ => {},
};
if !is_key_char_request {
self.draw(redraw_ids, use_saved_buffer);
}
}
}
pub fn handle_request(&mut self, request: WindowManagerRequest) {
let focused_index = self.get_focused_index().unwrap();
let subtype = self.window_infos[focused_index].window_like.subtype();
let subtype = if let Some(focused_index) = self.get_focused_index() {
Some(self.window_infos[focused_index].window_like.subtype())
} else {
None
};
match request {
WindowManagerRequest::OpenWindow(w) => {
let subtype = subtype.unwrap();
if subtype != WindowLikeType::Taskbar && subtype != WindowLikeType::StartMenu {
return;
}
@@ -565,6 +704,7 @@ impl WindowManager {
self.taskbar_update_windows();
},
WindowManagerRequest::CloseStartMenu => {
let subtype = subtype.unwrap();
if subtype != WindowLikeType::Taskbar && subtype != WindowLikeType::StartMenu {
return;
}
@@ -574,13 +714,13 @@ impl WindowManager {
}
},
WindowManagerRequest::Unlock => {
if subtype != WindowLikeType::LockScreen {
if subtype.unwrap() != WindowLikeType::LockScreen {
return;
}
self.unlock();
},
WindowManagerRequest::Lock => {
if subtype != WindowLikeType::StartMenu {
if subtype.unwrap() != WindowLikeType::StartMenu {
return;
}
self.lock();
@@ -588,6 +728,9 @@ impl WindowManager {
WindowManagerRequest::ClipboardCopy(content) => {
self.clipboard = Some(content);
},
WindowManagerRequest::DoKeyChar(kc) => {
self.handle_message(WindowManagerMessage::KeyChar(kc));
},
};
}
@@ -605,12 +748,15 @@ impl WindowManager {
}
//get windows to redraw
let redraw_ids = maybe_redraw_ids.unwrap_or(Vec::new());
let all_in_workspace = self.get_windows_in_workspace(true);
let mut all_in_workspace = self.get_windows_in_workspace(true);
if let Some(osk) = &self.osk {
all_in_workspace.push(osk);
}
let maybe_length = all_in_workspace.len();
let redraw_windows = all_in_workspace.iter().filter(|w| {
//basically, maybe_redraw_ids was None
if redraw_ids.len() > 0 {
redraw_ids.contains(&w.id)
redraw_ids.contains(&w.id) || w.window_like.subtype() == WindowLikeType::OnscreenKeyboard
} else {
true
}
@@ -697,6 +843,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);
}
}