Automatically resize active terminal to not overlap with keyboard
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
*.o
|
|
||||||
buffyboard
|
buffyboard
|
||||||
|
*.o
|
||||||
|
.vscode
|
||||||
|
2
Makefile
2
Makefile
@@ -23,7 +23,7 @@ CFLAGS ?= -O3 -g0 -I$(LVGL_DIR)/ -Wall -Wshadow -Wundef -Wmissing-prototypes -Wn
|
|||||||
LDFLAGS ?= -lm -linput
|
LDFLAGS ?= -lm -linput
|
||||||
BIN = buffyboard
|
BIN = buffyboard
|
||||||
|
|
||||||
MAINSRC = ./cursor.c ./layout.c ./libinput_multi.c ./main.c ./montserrat_extended_32.c ./sq2lv_layouts.c ./uinput_device.c
|
MAINSRC = ./cursor.c ./layout.c ./libinput_multi.c ./main.c ./montserrat_extended_32.c ./sq2lv_layouts.c ./terminal.c ./uinput_device.c
|
||||||
|
|
||||||
include $(LVGL_DIR)/lvgl/lvgl.mk
|
include $(LVGL_DIR)/lvgl/lvgl.mk
|
||||||
include $(LVGL_DIR)/lv_drivers/lv_drivers.mk
|
include $(LVGL_DIR)/lv_drivers/lv_drivers.mk
|
||||||
|
@@ -15,11 +15,11 @@ While partially usable, buffyboard currently still is a work in progress.
|
|||||||
- Multi-layer keyboard layout including lowercase letters, uppercase letters, numbers and selected symbols (based on top three layers of [squeekboard's US terminal layout])
|
- Multi-layer keyboard layout including lowercase letters, uppercase letters, numbers and selected symbols (based on top three layers of [squeekboard's US terminal layout])
|
||||||
- Key chords with one or more modifiers terminated by a single non-modifier (e.g. `CTRL-c`)
|
- Key chords with one or more modifiers terminated by a single non-modifier (e.g. `CTRL-c`)
|
||||||
- Highlighting of active modifiers
|
- Highlighting of active modifiers
|
||||||
|
- Automatic resizing (and later reset) of active VT to prevent overlap with keyboard
|
||||||
|
|
||||||
## To do
|
## To do
|
||||||
|
|
||||||
- Show keyboard at the bottom of the screen instead of at the top (just need to set the offset once [lvgl/lv_drivers#164] is merged)
|
- Show keyboard at the bottom of the screen instead of at the top (just need to set the offset once [lvgl/lv_drivers#164] is merged)
|
||||||
- Resize VT screen size to not overlap with keyboard
|
|
||||||
- Support different screen rotations
|
- Support different screen rotations
|
||||||
- Eliminate `libinput_multi.[ch]` once support for multiple input devices has been upstreamed to [lv_drivers] (see [lvgl/lv_drivers#151])
|
- Eliminate `libinput_multi.[ch]` once support for multiple input devices has been upstreamed to [lv_drivers] (see [lvgl/lv_drivers#151])
|
||||||
- Add remaining layers from [squeekboard's US terminal layout] (symbols and actions)
|
- Add remaining layers from [squeekboard's US terminal layout] (symbols and actions)
|
||||||
|
50
main.c
50
main.c
@@ -22,6 +22,7 @@
|
|||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
#include "libinput_multi.h"
|
#include "libinput_multi.h"
|
||||||
#include "sq2lv_layouts.h"
|
#include "sq2lv_layouts.h"
|
||||||
|
#include "terminal.h"
|
||||||
#include "uinput_device.h"
|
#include "uinput_device.h"
|
||||||
|
|
||||||
#include "lvgl/lvgl.h"
|
#include "lvgl/lvgl.h"
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
#include "lv_drivers/indev/libinput_drv.h"
|
#include "lv_drivers/indev/libinput_drv.h"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -47,6 +49,7 @@ LV_FONT_DECLARE(montserrat_extended_32);
|
|||||||
* Static variables
|
* Static variables
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static bool resize_terminals = false;
|
||||||
static lv_obj_t *keyboard = NULL;
|
static lv_obj_t *keyboard = NULL;
|
||||||
static lv_style_t style_text_normal;
|
static lv_style_t style_text_normal;
|
||||||
|
|
||||||
@@ -55,6 +58,20 @@ static lv_style_t style_text_normal;
|
|||||||
* Static prototypes
|
* Static prototypes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle termination signals sent to the process.
|
||||||
|
*
|
||||||
|
* @param signum the signal's number
|
||||||
|
*/
|
||||||
|
static void sigaction_handler(int signum);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for the terminal resizing timer.
|
||||||
|
*
|
||||||
|
* @param timer the timer object
|
||||||
|
*/
|
||||||
|
static void terminal_resize_timer_cb(lv_timer_t *timer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the UI theme.
|
* Set the UI theme.
|
||||||
*
|
*
|
||||||
@@ -91,6 +108,19 @@ static void pop_checked_modifier_keys(lv_obj_t *keyboard);
|
|||||||
* Static functions
|
* Static functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static void sigaction_handler(int signum) {
|
||||||
|
if (resize_terminals) {
|
||||||
|
bb_terminal_reset_all();
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void terminal_resize_timer_cb(lv_timer_t *timer) {
|
||||||
|
if (resize_terminals) {
|
||||||
|
bb_terminal_shrink_current();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void set_theme(bool is_dark) {
|
static void set_theme(bool is_dark) {
|
||||||
lv_theme_default_init(NULL, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_CYAN), is_dark, &montserrat_extended_32);
|
lv_theme_default_init(NULL, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_CYAN), is_dark, &montserrat_extended_32);
|
||||||
}
|
}
|
||||||
@@ -195,8 +225,21 @@ static void keyboard_draw_part_begin_cb(lv_event_t *event) {
|
|||||||
* Main
|
* Main
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int main(void)
|
int main(void) {
|
||||||
{
|
/* Prepare for terminal resizing and reset */
|
||||||
|
resize_terminals = bb_terminal_init(2.0f / 3.0f);
|
||||||
|
if (resize_terminals) {
|
||||||
|
/* Clean up on termination */
|
||||||
|
struct sigaction action;
|
||||||
|
memset(&action, 0, sizeof(action));
|
||||||
|
action.sa_handler = sigaction_handler;
|
||||||
|
sigaction(SIGINT, &action, NULL);
|
||||||
|
sigaction(SIGTERM, &action, NULL);
|
||||||
|
|
||||||
|
/* Resize current terminal */
|
||||||
|
bb_terminal_shrink_current();
|
||||||
|
}
|
||||||
|
|
||||||
/* Set up uinput device */
|
/* Set up uinput device */
|
||||||
if (!bb_uinput_device_init(sq2lv_unique_scancodes, sq2lv_num_unique_scancodes)) {
|
if (!bb_uinput_device_init(sq2lv_unique_scancodes, sq2lv_num_unique_scancodes)) {
|
||||||
return 1;
|
return 1;
|
||||||
@@ -293,6 +336,9 @@ int main(void)
|
|||||||
/* Apply default keyboard layout */
|
/* Apply default keyboard layout */
|
||||||
bb_layout_switch_layout(keyboard, SQ2LV_LAYOUT_TERMINAL_US);
|
bb_layout_switch_layout(keyboard, SQ2LV_LAYOUT_TERMINAL_US);
|
||||||
|
|
||||||
|
/* Start timer for periodically resizing terminals */
|
||||||
|
lv_timer_create(terminal_resize_timer_cb, 1000, NULL);
|
||||||
|
|
||||||
/* Run lvgl in "tickless" mode */
|
/* Run lvgl in "tickless" mode */
|
||||||
while(1) {
|
while(1) {
|
||||||
lv_task_handler();
|
lv_task_handler();
|
||||||
|
294
terminal.c
Normal file
294
terminal.c
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2021 Johannes Marbach
|
||||||
|
*
|
||||||
|
* This file is part of buffyboard, hereafter referred to as the program.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "terminal.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <linux/vt.h>
|
||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static variables
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int current_fd = -1;
|
||||||
|
static int current_vt = -1;
|
||||||
|
static bool resized_vts[MAX_NR_CONSOLES];
|
||||||
|
static float height_factor = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static prototypes
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the current file descriptor and reopen /dev/tty0.
|
||||||
|
*
|
||||||
|
* @return true if opening was successful, false otherwise
|
||||||
|
*/
|
||||||
|
static bool reopen_current_terminal(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the current file descriptor.
|
||||||
|
*/
|
||||||
|
static void close_current_terminal(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently active virtual terminal.
|
||||||
|
*
|
||||||
|
* @return number of the active VT (e.g. 7 for /dev/tty7)
|
||||||
|
*/
|
||||||
|
static int get_active_terminal(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a terminal's size.
|
||||||
|
*
|
||||||
|
* @param fd TTY file descriptor
|
||||||
|
* @param size pointer to winsize struct for writing the size into
|
||||||
|
* @return true if the operation was successful, false otherwise. On failure, errno will be set
|
||||||
|
* to the value set by the failed system call.
|
||||||
|
*/
|
||||||
|
static bool get_terminal_size(int fd, struct winsize *size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a terminal's size.
|
||||||
|
*
|
||||||
|
* @param fd TTY file descriptor
|
||||||
|
* @param size pointer to winsize struct for reading the new size from
|
||||||
|
* @return true if the operation was successful, false otherwise. On failure, errno will be set
|
||||||
|
* to the value set by the failed system call.
|
||||||
|
*/
|
||||||
|
static bool set_terminal_size(int fd, struct winsize *size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shrink the height of a terminal by the current factor.
|
||||||
|
*
|
||||||
|
* @param fd TTY file descriptor
|
||||||
|
* @return true if the operation was successful, false otherwise
|
||||||
|
*/
|
||||||
|
static bool shrink_terminal(int fd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the height of a terminal to the maximum.
|
||||||
|
*
|
||||||
|
* @param fd TTY file descriptor
|
||||||
|
* @param size pointer to winsize struct for writing the final size into
|
||||||
|
* @return true if the operation was successful, false otherwise
|
||||||
|
*/
|
||||||
|
static bool reset_terminal(int fd, struct winsize *size);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool reopen_current_terminal(void) {
|
||||||
|
close_current_terminal();
|
||||||
|
|
||||||
|
current_fd = open("/dev/tty0", O_RDWR | O_NOCTTY);
|
||||||
|
if (current_fd < 0) {
|
||||||
|
perror("Could not open /dev/tty0");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_current_terminal(void) {
|
||||||
|
if (current_fd < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(current_fd);
|
||||||
|
current_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_active_terminal(void) {
|
||||||
|
struct vt_stat stat;
|
||||||
|
if (ioctl(current_fd, VT_GETSTATE, &stat) != 0) {
|
||||||
|
perror("Could not retrieve current termimal state");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return stat.v_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool get_terminal_size(int fd, struct winsize *size) {
|
||||||
|
if (ioctl(fd, TIOCGWINSZ, size) != 0) {
|
||||||
|
int errsv = errno;
|
||||||
|
perror("Could not retrieve current terminal size");
|
||||||
|
errno = errsv;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool set_terminal_size(int fd, struct winsize *size) {
|
||||||
|
if (ioctl(fd, TIOCSWINSZ, size) != 0) {
|
||||||
|
int errsv = errno;
|
||||||
|
perror("Could not update current terminal size");
|
||||||
|
errno = errsv;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool shrink_terminal(int fd) {
|
||||||
|
struct winsize size = { 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
if (!reset_terminal(fd, &size)) {
|
||||||
|
perror("Could not shrink terminal size");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size.ws_row = floor((float)size.ws_row * height_factor);
|
||||||
|
if (!set_terminal_size(fd, &size)) {
|
||||||
|
perror("Could not shrink terminal size");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool reset_terminal(int fd, struct winsize *size) {
|
||||||
|
if (!get_terminal_size(fd, size)) {
|
||||||
|
perror("Could not reset terminal size");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test-resize by two rows. If the terminal is already maximised, this will fail and we can exit early. */
|
||||||
|
size->ws_row += 2;
|
||||||
|
if (!set_terminal_size(fd, size)) {
|
||||||
|
bool is_max = (errno == EINVAL);
|
||||||
|
size->ws_row -= 2;
|
||||||
|
return is_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
size->ws_row = floor((float)size->ws_row / height_factor);
|
||||||
|
if (!set_terminal_size(fd, size)) {
|
||||||
|
if (errno != EINVAL) {
|
||||||
|
perror("Could not reset terminal size");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Size too large. Reduce by one row until it fits. */
|
||||||
|
do {
|
||||||
|
size->ws_row -= 1;
|
||||||
|
} while (size->ws_row > 0 && !set_terminal_size(fd, size) && errno == EINVAL);
|
||||||
|
|
||||||
|
if (errno != EINVAL || size->ws_row == 0) {
|
||||||
|
perror("Could not reset terminal size");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Size fits but may not max out available space. Increase by one row until it doesn't fit anymore. */
|
||||||
|
do {
|
||||||
|
size->ws_row += 1;
|
||||||
|
} while (set_terminal_size(fd, size));
|
||||||
|
|
||||||
|
if (errno != EINVAL) {
|
||||||
|
perror("Could not reset terminal size");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size->ws_row -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool bb_terminal_init(float factor) {
|
||||||
|
if (!reopen_current_terminal()) {
|
||||||
|
perror("Could not prepare for terminal resizing");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_vt = get_active_terminal();
|
||||||
|
if (current_vt < 0) {
|
||||||
|
perror("Could not prepare for terminal resizing");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
height_factor = factor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bb_terminal_shrink_current(void) {
|
||||||
|
int active_vt = get_active_terminal();
|
||||||
|
if (active_vt < 0) {
|
||||||
|
perror("Could not resize current terminal");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active_vt < 0 || active_vt > MAX_NR_CONSOLES - 1) {
|
||||||
|
perror("Could not resize current terminal, index is out of bounds");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resized_vts[active_vt - 1]) {
|
||||||
|
return; /* Already resized */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active_vt != current_vt) {
|
||||||
|
if (!reopen_current_terminal()) {
|
||||||
|
perror("Could not resize current terminal");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
current_vt = active_vt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shrink_terminal(current_fd)) {
|
||||||
|
perror("Could not resize current terminal");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resized_vts[current_vt - 1] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bb_terminal_reset_all(void) {
|
||||||
|
char device[16];
|
||||||
|
struct winsize size = { 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_NR_CONSOLES; ++i) {
|
||||||
|
if (!resized_vts[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(device, 16, "/dev/tty%d", i + 1);
|
||||||
|
int fd = open(device, O_RDWR | O_NOCTTY);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("Could not reset TTY, unable to open TTY");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_terminal(fd, &size);
|
||||||
|
}
|
||||||
|
}
|
45
terminal.h
Normal file
45
terminal.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2021 Johannes Marbach
|
||||||
|
*
|
||||||
|
* This file is part of buffyboard, hereafter referred to as the program.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BB_TERMINAL_H
|
||||||
|
#define BB_TERMINAL_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare for resizing terminals by opening the current one.
|
||||||
|
*
|
||||||
|
* @param factor factor (between 0 and 1) by which to adapt terminal sizes
|
||||||
|
* @return true if the operation was successful, false otherwise. No other bb_terminal_* functions
|
||||||
|
* must be called if false is returned.
|
||||||
|
*/
|
||||||
|
bool bb_terminal_init(float factor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shrink the height of the active terminal by the current factor.
|
||||||
|
*/
|
||||||
|
void bb_terminal_shrink_current(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-maximise the height of all previously resized terminals.
|
||||||
|
*/
|
||||||
|
void bb_terminal_reset_all(void);
|
||||||
|
|
||||||
|
#endif /* BB_TERMINAL_H */
|
Reference in New Issue
Block a user