Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
uobikiemukot
4333815e43 add mouse support 2018-03-14 12:48:09 +09:00
4 changed files with 298 additions and 3 deletions

View file

@ -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
![screenshot1](http://uobikiemukot.github.io/img/yaft-screenshot.png)

4
conf.h
View file

@ -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
View 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
View file

@ -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: