Compare commits
1 commit
master
...
support/li
Author | SHA1 | Date | |
---|---|---|---|
|
4333815e43 |
4 changed files with 298 additions and 3 deletions
13
README.md
13
README.md
|
@ -92,6 +92,19 @@ Or try glyph_builder.sh (bash script for creating yaft's glyph.h)
|
|||
|
||||
- supported fonts: mplus, efont, milkjf, unifont, dina, terminus, profont, tamsyn
|
||||
|
||||
## mouse support
|
||||
|
||||
Preparation
|
||||
|
||||
- check variable, mouse_path in conf.h (maybe correct path is /dev/input/by-id/*-event-mouse or something)
|
||||
- stop gpm mouse serve (# /etc/init.d/gpm stop or # systemctl stop gpm)
|
||||
- check permission of /dev/input/* (maybe need # gpasswd -a USER input)
|
||||
|
||||
How to copy/paste
|
||||
|
||||
- copy area selection: hold mouse left button
|
||||
- paste selected text: press mouse right button
|
||||
|
||||
## screenshot
|
||||
|
||||

|
||||
|
|
4
conf.h
4
conf.h
|
@ -7,6 +7,7 @@ enum {
|
|||
DEFAULT_BG = 0,
|
||||
ACTIVE_CURSOR_COLOR = 2,
|
||||
PASSIVE_CURSOR_COLOR = 1,
|
||||
MOUSE_CURSOR_COLOR = 6,
|
||||
};
|
||||
|
||||
/* misc */
|
||||
|
@ -46,3 +47,6 @@ const char *term_name = "yaft-256color";
|
|||
#elif defined(__ANDROID__)
|
||||
const char *shell_cmd = "/system/bin/sh";
|
||||
#endif
|
||||
|
||||
/* mouse device */
|
||||
const char *mouse_path = "/dev/input/by-id/id_of_youre_mouse_dev";
|
||||
|
|
250
mouse.h
Normal file
250
mouse.h
Normal file
|
@ -0,0 +1,250 @@
|
|||
#include <linux/input.h>
|
||||
|
||||
enum {
|
||||
VALUE_PRESSED = 1,
|
||||
VALUE_RELEASED = 0,
|
||||
UTF8_LEN_MAX = 3,
|
||||
};
|
||||
|
||||
struct cursor_t {
|
||||
int x, y;
|
||||
};
|
||||
|
||||
struct mouse_t {
|
||||
int fd;
|
||||
struct cursor_t current;
|
||||
struct cursor_t pressed, released;
|
||||
bool is_pressed, do_paste;
|
||||
};
|
||||
|
||||
/* mouse functions */
|
||||
bool mouse_init(struct mouse_t *mouse)
|
||||
{
|
||||
if ((mouse->fd = eopen(mouse_path, O_RDONLY)) < 0)
|
||||
return false;
|
||||
|
||||
mouse->current.x = mouse->current.y = 0;
|
||||
mouse->pressed.x = mouse->pressed.y = 0;
|
||||
mouse->released.x = mouse->released.y = 0;
|
||||
mouse->is_pressed = false;
|
||||
mouse->do_paste = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mouse_die(struct mouse_t *mouse)
|
||||
{
|
||||
close(mouse->fd);
|
||||
}
|
||||
|
||||
void cursor(struct framebuffer_t *fb, struct mouse_t *mouse, struct input_event *ie)
|
||||
{
|
||||
if (ie->code == REL_X)
|
||||
mouse->current.x += ie->value;
|
||||
|
||||
if (mouse->current.x < 0)
|
||||
mouse->current.x = 0;
|
||||
else if (mouse->current.x >= fb->info.width)
|
||||
mouse->current.x = fb->info.width - 1;
|
||||
|
||||
if (ie->code == REL_Y)
|
||||
mouse->current.y += ie->value;
|
||||
|
||||
if (mouse->current.y < 0)
|
||||
mouse->current.y = 0;
|
||||
else if (mouse->current.y >= fb->info.height)
|
||||
mouse->current.y = fb->info.height - 1;
|
||||
}
|
||||
|
||||
void button(struct framebuffer_t *fb, struct mouse_t *mouse, struct input_event *ie)
|
||||
{
|
||||
(void) fb;
|
||||
|
||||
if (ie->code == BTN_LEFT) {
|
||||
if (ie->value == VALUE_PRESSED) {
|
||||
mouse->pressed = mouse->current;
|
||||
mouse->is_pressed = true;
|
||||
}
|
||||
|
||||
if (ie->value == VALUE_RELEASED) {
|
||||
mouse->released = mouse->current;
|
||||
mouse->is_pressed = false;
|
||||
}
|
||||
|
||||
} else if (ie->code == BTN_RIGHT) {
|
||||
if (ie->value == VALUE_PRESSED)
|
||||
mouse->do_paste = true;
|
||||
}
|
||||
}
|
||||
|
||||
void (*mouse_handler[EV_CNT])(struct framebuffer_t *fb, struct mouse_t *mouse, struct input_event *ie) = {
|
||||
[EV_REL] = cursor,
|
||||
[EV_KEY] = button,
|
||||
[EV_MAX] = NULL,
|
||||
};
|
||||
|
||||
static inline void draw_mouse(struct framebuffer_t *fb, struct terminal_t *term, struct mouse_t *mouse, int line)
|
||||
{
|
||||
int pos, size, bdf_padding, glyph_width, margin_right;
|
||||
int mouse_col, mouse_line, pressed_col, pressed_line;
|
||||
int col, w, h;
|
||||
uint32_t pixel;
|
||||
struct color_pair_t color_pair;
|
||||
struct cell_t *cellp;
|
||||
|
||||
mouse_col = mouse->current.x / CELL_WIDTH;
|
||||
mouse_line = mouse->current.y / CELL_HEIGHT;
|
||||
|
||||
pressed_col = mouse->pressed.x / CELL_WIDTH;
|
||||
pressed_line = mouse->pressed.y / CELL_HEIGHT;
|
||||
|
||||
for (col = term->cols - 1; col >= 0; col--) {
|
||||
margin_right = (term->cols - 1 - col) * CELL_WIDTH;
|
||||
|
||||
/* target cell */
|
||||
cellp = &term->cells[line][col];
|
||||
|
||||
/* draw sixel pixmap */
|
||||
if (cellp->has_pixmap) {
|
||||
draw_sixel(fb, line, col, cellp->pixmap);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* copy current color_pair (maybe changed) */
|
||||
color_pair = cellp->color_pair;
|
||||
|
||||
/* check wide character or not */
|
||||
glyph_width = (cellp->width == HALF) ? CELL_WIDTH: CELL_WIDTH * 2;
|
||||
bdf_padding = my_ceil(glyph_width, BITS_PER_BYTE) * BITS_PER_BYTE - glyph_width;
|
||||
if (cellp->width == WIDE)
|
||||
bdf_padding += CELL_WIDTH;
|
||||
|
||||
/* check cursor positon */
|
||||
if ((term->mode & MODE_CURSOR && line == term->cursor.y)
|
||||
&& (col == term->cursor.x
|
||||
|| (cellp->width == WIDE && (col + 1) == term->cursor.x)
|
||||
|| (cellp->width == NEXT_TO_WIDE && (col - 1) == term->cursor.x))) {
|
||||
color_pair.fg = DEFAULT_BG;
|
||||
color_pair.bg = (!vt_active && BACKGROUND_DRAW) ? PASSIVE_CURSOR_COLOR: ACTIVE_CURSOR_COLOR;
|
||||
}
|
||||
|
||||
/* check mouse position */
|
||||
if ((mouse_col == col && mouse_line == line)
|
||||
|| (mouse->is_pressed && pressed_col == col && pressed_line == line)) {
|
||||
color_pair.fg = DEFAULT_BG;
|
||||
color_pair.bg = MOUSE_CURSOR_COLOR;
|
||||
}
|
||||
|
||||
for (h = 0; h < CELL_HEIGHT; h++) {
|
||||
/* if UNDERLINE attribute on, swap bg/fg */
|
||||
if ((h == (CELL_HEIGHT - 1)) && (cellp->attribute & attr_mask[ATTR_UNDERLINE]))
|
||||
color_pair.bg = color_pair.fg;
|
||||
|
||||
for (w = 0; w < CELL_WIDTH; w++) {
|
||||
pos = (term->width - 1 - margin_right - w) * fb->info.bytes_per_pixel
|
||||
+ (line * CELL_HEIGHT + h) * fb->info.line_length;
|
||||
|
||||
/* set color palette */
|
||||
if (cellp->glyphp->bitmap[h] & (0x01 << (bdf_padding + w)))
|
||||
pixel = fb->real_palette[color_pair.fg];
|
||||
else if (fb->wall && color_pair.bg == DEFAULT_BG) /* wallpaper */
|
||||
memcpy(&pixel, fb->wall + pos, fb->info.bytes_per_pixel);
|
||||
else
|
||||
pixel = fb->real_palette[color_pair.bg];
|
||||
|
||||
/* update copy buffer only */
|
||||
memcpy(fb->buf + pos, &pixel, fb->info.bytes_per_pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* actual display update (bit blit) */
|
||||
pos = (line * CELL_HEIGHT) * fb->info.line_length;
|
||||
size = CELL_HEIGHT * fb->info.line_length;
|
||||
memcpy(fb->fp + pos, fb->buf + pos, size);
|
||||
|
||||
/* TODO: page flip
|
||||
if fb_fix_screeninfo.ypanstep > 0, we can use hardware panning.
|
||||
set fb_fix_screeninfo.{yres_virtual,yoffset} and call ioctl(FBIOPAN_DISPLAY)
|
||||
but drivers of recent hardware (inteldrmfb, nouveaufb, radeonfb) don't support...
|
||||
(maybe we can use this by using libdrm) */
|
||||
/* TODO: vertical synchronizing */
|
||||
|
||||
term->line_dirty[line] = (mouse_line == line) ? true: false;
|
||||
|
||||
if (mouse->is_pressed)
|
||||
term->line_dirty[pressed_line] = true;
|
||||
}
|
||||
|
||||
static inline void refresh_mouse(struct framebuffer_t *fb, struct terminal_t *term, struct mouse_t *mouse)
|
||||
{
|
||||
term->line_dirty[mouse->current.y / CELL_HEIGHT] = true;
|
||||
|
||||
for (int line = 0; line < term->lines; line++) {
|
||||
if (term->line_dirty[line]) {
|
||||
draw_mouse(fb, term, mouse, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int utf8_encode(uint32_t ucs, char utf8_buf[UTF8_LEN_MAX + 1])
|
||||
{
|
||||
if ((0xD800 <= ucs && ucs <= 0xDFFF) /* used as surrogate pair in UTF-16 */
|
||||
|| (0xFDD0 <= ucs && ucs <= 0xFDEF) /* Noncharacter */
|
||||
|| ucs == 0xFFFE /* conflict byte order mark (U+FEFF) */
|
||||
|| ucs == 0xFFFF /* Noncharacter U+FFFF ("useful for internal purposes as sentinels") */
|
||||
|| ucs > 0xFFFF) { /* UCS2 (Unicode BMP): 0x0000 - 0xFFFF */
|
||||
/* invalid codepoint */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ucs <= 0x7F) {
|
||||
/* ASCII Character */
|
||||
utf8_buf[0] = (ucs & 0x7F);
|
||||
utf8_buf[1] = '\0';
|
||||
return 1;
|
||||
} else if (0x80 <= ucs && ucs <= 0x7FF) {
|
||||
/* 2 byte sequence */
|
||||
utf8_buf[0] = 0xC0 | ((ucs >> 6) & 0x1F);
|
||||
utf8_buf[1] = 0x80 | (ucs & 0x3F);
|
||||
utf8_buf[2] = '\0';
|
||||
return 2;
|
||||
} else if (0x800 <= ucs && ucs <= 0xFFFF) {
|
||||
/* 3 byte sequence */
|
||||
utf8_buf[0] = 0xE0 | ((ucs >> 12) & 0x0F);
|
||||
utf8_buf[1] = 0x80 | ((ucs >> 6) & 0x3F);
|
||||
utf8_buf[2] = 0x80 | (ucs & 0x3F);
|
||||
utf8_buf[3] = '\0';
|
||||
return 3;
|
||||
}
|
||||
/* illegal codepoint */
|
||||
return -1;
|
||||
}
|
||||
|
||||
void mouse_paste(struct terminal_t *term, struct mouse_t *mouse)
|
||||
{
|
||||
int x, y, start, end, tmp, size;
|
||||
char utf8_buf[UTF8_LEN_MAX + 1];
|
||||
struct cell_t *cellp;
|
||||
|
||||
start = (mouse->pressed.y / CELL_HEIGHT) * term->cols + (mouse->pressed.x / CELL_WIDTH);
|
||||
end = (mouse->released.y / CELL_HEIGHT) * term->cols + (mouse->released.x / CELL_WIDTH);
|
||||
|
||||
if (start > end) {
|
||||
tmp = start;
|
||||
start = end;
|
||||
end = tmp;
|
||||
}
|
||||
|
||||
for (int pos = start; pos <= end; pos++) {
|
||||
x = pos % term->cols;
|
||||
y = pos / term->cols;
|
||||
cellp = &term->cells[y][x];
|
||||
|
||||
if (cellp->width == NEXT_TO_WIDE)
|
||||
continue;
|
||||
|
||||
size = utf8_encode(cellp->glyphp->code, utf8_buf);
|
||||
ewrite(term->fd, utf8_buf, size);
|
||||
}
|
||||
}
|
34
yaft.c
34
yaft.c
|
@ -10,6 +10,7 @@
|
|||
#include "ctrlseq/osc.h"
|
||||
#include "ctrlseq/dcs.h"
|
||||
#include "parse.h"
|
||||
#include "mouse.h"
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
|
@ -146,11 +147,13 @@ bool fork_and_exec(int *master, int lines, int cols)
|
|||
return true;
|
||||
}
|
||||
|
||||
int check_fds(fd_set *fds, struct timeval *tv, int input, int master)
|
||||
int check_fds(fd_set *fds, struct timeval *tv, int input, int master, int mouse)
|
||||
{
|
||||
FD_ZERO(fds);
|
||||
FD_SET(input, fds);
|
||||
FD_SET(master, fds);
|
||||
FD_SET(mouse, fds);
|
||||
|
||||
tv->tv_sec = 0;
|
||||
tv->tv_usec = SELECT_TIMEOUT;
|
||||
return eselect(master + 1, fds, NULL, NULL, tv);
|
||||
|
@ -164,6 +167,9 @@ int main()
|
|||
struct timeval tv;
|
||||
struct framebuffer_t fb;
|
||||
struct terminal_t term;
|
||||
struct mouse_t mouse;
|
||||
struct input_event ie;
|
||||
|
||||
/* global */
|
||||
extern volatile sig_atomic_t need_redraw;
|
||||
extern volatile sig_atomic_t child_alive;
|
||||
|
@ -188,6 +194,11 @@ int main()
|
|||
goto tty_init_failed;
|
||||
}
|
||||
|
||||
if (!mouse_init(&mouse)) {
|
||||
logging(FATAL, "mouse initialize failed\n");
|
||||
goto mouse_init_failed;
|
||||
}
|
||||
|
||||
/* fork and exec shell */
|
||||
if (!fork_and_exec(&term.fd, term.lines, term.cols)) {
|
||||
logging(FATAL, "forkpty failed\n");
|
||||
|
@ -199,12 +210,13 @@ int main()
|
|||
while (child_alive) {
|
||||
if (need_redraw) {
|
||||
need_redraw = false;
|
||||
cmap_update(fb.fd, fb.cmap); /* after VT switching, need to restore cmap (in 8bpp mode) */
|
||||
if (fb.cmap)
|
||||
cmap_update(fb.fd, fb.cmap); /* after VT switching, need to restore cmap (in 8bpp mode) */
|
||||
redraw(&term);
|
||||
refresh(&fb, &term);
|
||||
}
|
||||
|
||||
if (check_fds(&fds, &tv, STDIN_FILENO, term.fd) == -1)
|
||||
if (check_fds(&fds, &tv, STDIN_FILENO, term.fd, mouse.fd) == -1)
|
||||
continue;
|
||||
|
||||
if (FD_ISSET(STDIN_FILENO, &fds)) {
|
||||
|
@ -221,15 +233,31 @@ int main()
|
|||
refresh(&fb, &term);
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(mouse.fd, &fds)) {
|
||||
if ((size = read(mouse.fd, &ie, sizeof(struct input_event))) > 0) {
|
||||
if (mouse_handler[ie.type]) {
|
||||
mouse_handler[ie.type](&fb, &mouse, &ie);
|
||||
refresh_mouse(&fb, &term, &mouse);
|
||||
}
|
||||
|
||||
if (mouse.do_paste) {
|
||||
mouse.do_paste = false;
|
||||
mouse_paste(&term, &mouse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* normal exit */
|
||||
mouse_die(&mouse);
|
||||
tty_die(&termios_orig);
|
||||
term_die(&term);
|
||||
fb_die(&fb);
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
/* error exit */
|
||||
mouse_init_failed:
|
||||
tty_die(&termios_orig);
|
||||
tty_init_failed:
|
||||
term_die(&term);
|
||||
term_init_failed:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue