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
|
||||
*.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
|
||||
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)/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])
|
||||
- Key chords with one or more modifiers terminated by a single non-modifier (e.g. `CTRL-c`)
|
||||
- Highlighting of active modifiers
|
||||
- Automatic resizing (and later reset) of active VT to prevent overlap with keyboard
|
||||
|
||||
## 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)
|
||||
- Resize VT screen size to not overlap with keyboard
|
||||
- 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])
|
||||
- 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 "libinput_multi.h"
|
||||
#include "sq2lv_layouts.h"
|
||||
#include "terminal.h"
|
||||
#include "uinput_device.h"
|
||||
|
||||
#include "lvgl/lvgl.h"
|
||||
@@ -29,6 +30,7 @@
|
||||
#include "lv_drivers/indev/libinput_drv.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
@@ -47,6 +49,7 @@ LV_FONT_DECLARE(montserrat_extended_32);
|
||||
* Static variables
|
||||
*/
|
||||
|
||||
static bool resize_terminals = false;
|
||||
static lv_obj_t *keyboard = NULL;
|
||||
static lv_style_t style_text_normal;
|
||||
|
||||
@@ -55,6 +58,20 @@ static lv_style_t style_text_normal;
|
||||
* 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.
|
||||
*
|
||||
@@ -91,6 +108,19 @@ static void pop_checked_modifier_keys(lv_obj_t *keyboard);
|
||||
* 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) {
|
||||
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
|
||||
*/
|
||||
|
||||
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 */
|
||||
if (!bb_uinput_device_init(sq2lv_unique_scancodes, sq2lv_num_unique_scancodes)) {
|
||||
return 1;
|
||||
@@ -293,6 +336,9 @@ int main(void)
|
||||
/* Apply default keyboard layout */
|
||||
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 */
|
||||
while(1) {
|
||||
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