mirror of
https://github.com/tmux/tmux.git
synced 2024-11-05 02:18:47 +00:00
591 lines
14 KiB
C
591 lines
14 KiB
C
/* $Id: screen-write.c,v 1.13 2008-09-09 22:16:36 nicm Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "tmux.h"
|
|
|
|
#define screen_write_limit(s, v, lower, upper) do { \
|
|
if (v < lower) { \
|
|
v = lower; \
|
|
SCREEN_DEBUG3(s, v, lower, upper); \
|
|
} \
|
|
if (v > upper) { \
|
|
v = upper; \
|
|
SCREEN_DEBUG3(s, v, lower, upper); \
|
|
} \
|
|
} while (0)
|
|
|
|
/* Initialise writing with a window. */
|
|
void
|
|
screen_write_start_window(struct screen_write_ctx *ctx, struct window *w)
|
|
{
|
|
struct screen *t = w->screen;
|
|
|
|
screen_write_start(ctx, t, tty_write_window, w);
|
|
}
|
|
|
|
/* Initialise writing with a client. */
|
|
void
|
|
screen_write_start_client(struct screen_write_ctx *ctx, struct client *c)
|
|
{
|
|
struct screen *t = c->session->curw->window->screen;
|
|
|
|
screen_write_start(ctx, t, tty_write_client, c);
|
|
}
|
|
|
|
/* Initialise writing with a session. */
|
|
void
|
|
screen_write_start_session(struct screen_write_ctx *ctx, struct session *s)
|
|
{
|
|
struct screen *t = s->curw->window->screen;
|
|
|
|
screen_write_start(ctx, t, tty_write_session, s);
|
|
}
|
|
|
|
/* Initialise writing. */
|
|
void
|
|
screen_write_start(struct screen_write_ctx *ctx,
|
|
struct screen *s, void (*write)(void *, int, ...), void *data)
|
|
{
|
|
ctx->write = write;
|
|
ctx->data = data;
|
|
|
|
ctx->s = s;
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_CURSOROFF);
|
|
}
|
|
|
|
/* Finalise writing. */
|
|
void
|
|
screen_write_stop(struct screen_write_ctx *ctx)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
if (ctx->write != NULL && s->mode & MODE_CURSOR)
|
|
ctx->write(ctx->data, TTY_CURSORON);
|
|
}
|
|
|
|
/* Set screen title. */
|
|
void
|
|
screen_write_set_title(struct screen_write_ctx *ctx, char *title)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
xfree(s->title);
|
|
s->title = title;
|
|
}
|
|
|
|
/* Put a character. */
|
|
void
|
|
screen_write_put_character(struct screen_write_ctx *ctx, u_char ch)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
u_short attr;
|
|
|
|
if (s->cx == screen_size_x(s)) {
|
|
s->cx = 0;
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_CHARACTER, '\r');
|
|
screen_write_cursor_down_scroll(ctx);
|
|
} else if (!screen_in_x(s, s->cx) || !screen_in_y(s, s->cy)) {
|
|
SCREEN_DEBUG(s);
|
|
return;
|
|
}
|
|
attr = s->attr & ~(ATTR_UTF8|ATTR_PAD);
|
|
|
|
screen_display_set_cell(s, s->cx, s->cy, ch, attr, s->fg, s->bg);
|
|
s->cx++;
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_CHARACTER, ch);
|
|
}
|
|
|
|
/* Put a UTF8 character. */
|
|
void
|
|
screen_write_put_utf8(struct screen_write_ctx *ctx, struct utf8_data *udat)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
u_char ch, ch2, fg, bg;
|
|
u_short attr, attr2;
|
|
int idx, wide;
|
|
u_int n;
|
|
|
|
wide = 0;
|
|
if (udat->data[2] == 0xff)
|
|
n = ((udat->data[0] & 0x1f)<<6) + (udat->data[1] & 0x3f);
|
|
else if (udat->data[3] == 0xff) {
|
|
n = ((udat->data[0] & 0x0f)<<12) +
|
|
((udat->data[1] & 0x3f)<<6) + (udat->data[2] & 0x3f);
|
|
} else
|
|
n = 0;
|
|
if ((n >= 0x1100 && n <= 0x115f) || n == 0x2329 || n == 0x232a ||
|
|
(n >= 0x2e80 && n <= 0xa4cf && n != 0x303f) ||
|
|
(n >= 0xac00 && n <= 0xd7a3) || (n >= 0xf900 && n <= 0xfaff) ||
|
|
(n >= 0xfe10 && n <= 0xfe19) || (n >= 0xfe30 && n <= 0xfe6f) ||
|
|
(n >= 0xff00 && n <= 0xff60) || (n >= 0xffe0 && n <= 0xffe6) ||
|
|
(n >= 0x20000 && n <= 0x2fffd) || (n >= 0x30000 && n <= 0x3fffd))
|
|
wide = 1;
|
|
|
|
if (s->cx >= screen_size_x(s) - wide) {
|
|
s->cx = 0;
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_CHARACTER, '\r');
|
|
screen_write_cursor_down_scroll(ctx);
|
|
} else if (!screen_in_x(s, s->cx) || !screen_in_y(s, s->cy)) {
|
|
SCREEN_DEBUG(s);
|
|
return;
|
|
}
|
|
attr = s->attr & ~(ATTR_UTF8|ATTR_PAD);
|
|
|
|
if ((idx = utf8_add(&s->utf8_table, udat)) == -1)
|
|
ch = '_';
|
|
else {
|
|
utf8_pack(idx, &ch, &attr);
|
|
attr |= ATTR_UTF8;
|
|
}
|
|
|
|
/* Remove padding before and after, if any. */
|
|
screen_display_get_cell(s, s->cx, s->cy, &ch2, &attr2, &fg, &bg);
|
|
if (s->cx > 0 && (attr2 & ATTR_PAD))
|
|
screen_display_set_cell(s, s->cx - 1, s->cy, ' ', 0, 8, 8);
|
|
if (s->cx < screen_last_x(s) && (attr2 & ATTR_UTF8)) {
|
|
screen_display_get_cell(
|
|
s, s->cx + 1, s->cy, &ch2, &attr2, &fg, &bg);
|
|
if (s->cx > 0 && (attr2 & ATTR_PAD)) {
|
|
screen_display_set_cell(
|
|
s, s->cx + 1, s->cy, ' ', 0, 8, 8);
|
|
}
|
|
}
|
|
|
|
screen_display_set_cell(s, s->cx, s->cy, ch, attr, s->fg, s->bg);
|
|
s->cx++;
|
|
|
|
if (wide) {
|
|
screen_display_set_cell(s, s->cx, s->cy, ' ', ATTR_PAD, 8, 8);
|
|
s->cx++;
|
|
}
|
|
|
|
if (ctx->write != NULL) {
|
|
ctx->write(ctx->data, TTY_ATTRIBUTES, attr, s->fg, s->bg);
|
|
ctx->write(ctx->data, TTY_CHARACTER, ch);
|
|
ctx->write(ctx->data, TTY_ATTRIBUTES, s->attr, s->fg, s->bg);
|
|
}
|
|
}
|
|
|
|
/* Put a string right-justified. */
|
|
size_t printflike2
|
|
screen_write_put_string_rjust(
|
|
struct screen_write_ctx *ctx, const char *fmt, ...)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
va_list ap;
|
|
size_t size;
|
|
char *msg, *ptr;
|
|
|
|
va_start(ap, fmt);
|
|
size = vasprintf(&msg, fmt, ap);
|
|
va_end(ap);
|
|
|
|
ptr = msg;
|
|
if (size > screen_size_x(s)) {
|
|
ptr += size - screen_size_x(s);
|
|
size = screen_size_x(s);
|
|
}
|
|
screen_write_move_cursor(ctx, screen_size_x(s) - size, s->cy);
|
|
for (; *ptr != '\0'; ptr++) {
|
|
if (s->cx == screen_size_x(s))
|
|
break;
|
|
screen_write_put_character(ctx, *ptr);
|
|
}
|
|
|
|
xfree(msg);
|
|
|
|
return (size);
|
|
}
|
|
|
|
/* Put a string, truncating at end of line. */
|
|
void printflike2
|
|
screen_write_put_string(struct screen_write_ctx *ctx, const char *fmt, ...)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
va_list ap;
|
|
char *msg, *ptr;
|
|
|
|
va_start(ap, fmt);
|
|
vasprintf(&msg, fmt, ap);
|
|
va_end(ap);
|
|
|
|
for (ptr = msg; *ptr != '\0'; ptr++) {
|
|
if (s->cx == screen_size_x(s))
|
|
break;
|
|
screen_write_put_character(ctx, *ptr);
|
|
}
|
|
|
|
xfree(msg);
|
|
}
|
|
|
|
/* Set screen attributes. */
|
|
void
|
|
screen_write_set_attributes(
|
|
struct screen_write_ctx *ctx, u_short attr, u_char fg, u_char bg)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
if (s->attr != attr || s->fg != fg || s->bg != bg) {
|
|
s->attr = attr;
|
|
s->fg = fg;
|
|
s->bg = bg;
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_ATTRIBUTES, attr, fg, bg);
|
|
}
|
|
}
|
|
|
|
/* Set scroll region. */
|
|
void
|
|
screen_write_set_region(struct screen_write_ctx *ctx, u_int upper, u_int lower)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_write_limit(s, upper, 0, screen_last_y(s));
|
|
screen_write_limit(s, lower, 0, screen_last_y(s));
|
|
if (upper > lower) {
|
|
SCREEN_DEBUG2(s, upper, lower);
|
|
return;
|
|
}
|
|
|
|
/* Cursor moves to top-left. */
|
|
s->cx = 0;
|
|
s->cy = upper;
|
|
|
|
s->rupper = upper;
|
|
s->rlower = lower;
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_SCROLLREGION, s->rupper, s->rlower);
|
|
}
|
|
|
|
/* Move cursor up and scroll if necessary. */
|
|
void
|
|
screen_write_cursor_up_scroll(struct screen_write_ctx *ctx)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
if (s->cy == s->rupper)
|
|
screen_display_scroll_region_down(s);
|
|
else if (s->cy > 0)
|
|
s->cy--;
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_REVERSEINDEX);
|
|
}
|
|
|
|
/* Move cursor down and scroll if necessary */
|
|
void
|
|
screen_write_cursor_down_scroll(struct screen_write_ctx *ctx)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
if (s->cy == s->rlower)
|
|
screen_display_scroll_region_up(s);
|
|
else if (s->cy < screen_last_y(s))
|
|
s->cy++;
|
|
|
|
if (ctx->write != NULL) /* XXX FORWARDINDEX */
|
|
ctx->write(ctx->data, TTY_CHARACTER, '\n');
|
|
}
|
|
|
|
/* Move cursor up. */
|
|
void
|
|
screen_write_cursor_up(struct screen_write_ctx *ctx, u_int n)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_write_limit(s, n, 1, screen_above_y(s, s->cy) - 1);
|
|
|
|
s->cy -= n;
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_CURSORMOVE, s->cy, s->cx);
|
|
}
|
|
|
|
/* Move cursor down. */
|
|
void
|
|
screen_write_cursor_down(struct screen_write_ctx *ctx, u_int n)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_write_limit(s, n, 1, screen_below_y(s, s->cy) - 1);
|
|
|
|
s->cy += n;
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_CURSORMOVE, s->cy, s->cx);
|
|
}
|
|
|
|
/* Move cursor left. */
|
|
void
|
|
screen_write_cursor_left(struct screen_write_ctx *ctx, u_int n)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_write_limit(s, n, 1, screen_left_x(s, s->cx) - 1);
|
|
|
|
s->cx -= n;
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_CURSORMOVE, s->cy, s->cx);
|
|
}
|
|
|
|
/* Move cursor right. */
|
|
void
|
|
screen_write_cursor_right(struct screen_write_ctx *ctx, u_int n)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_write_limit(s, n, 1, screen_right_x(s, s->cx) - 1);
|
|
|
|
s->cx += n;
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_CURSORMOVE, s->cy, s->cx);
|
|
}
|
|
|
|
/* Delete lines. */
|
|
void
|
|
screen_write_delete_lines(struct screen_write_ctx *ctx, u_int n)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_write_limit(s, n, 1, screen_below_y(s, s->cy));
|
|
|
|
if (s->cy < s->rupper || s->cy > s->rlower)
|
|
screen_display_delete_lines(s, s->cy, n);
|
|
else
|
|
screen_display_delete_lines_region(s, s->cy, n);
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_DELETELINE, n);
|
|
}
|
|
|
|
/* Delete characters. */
|
|
void
|
|
screen_write_delete_characters(struct screen_write_ctx *ctx, u_int n)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_write_limit(s, n, 1, screen_right_x(s, s->cx));
|
|
|
|
screen_display_delete_characters(s, s->cx, s->cy, n);
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_DELETECHARACTER, n);
|
|
}
|
|
|
|
/* Insert lines. */
|
|
void
|
|
screen_write_insert_lines(struct screen_write_ctx *ctx, u_int n)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_write_limit(s, n, 1, screen_below_y(s, s->cy));
|
|
|
|
if (s->cy < s->rupper || s->cy > s->rlower)
|
|
screen_display_insert_lines(s, s->cy, n);
|
|
else
|
|
screen_display_insert_lines_region(s, s->cy, n);
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_INSERTLINE, n);
|
|
}
|
|
|
|
/* Insert characters. */
|
|
void
|
|
screen_write_insert_characters(struct screen_write_ctx *ctx, u_int n)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_write_limit(s, n, 1, screen_right_x(s, s->cx));
|
|
|
|
screen_display_insert_characters(s, s->cx, s->cy, n);
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_INSERTCHARACTER, n);
|
|
}
|
|
|
|
/* Move the cursor. */
|
|
void
|
|
screen_write_move_cursor(struct screen_write_ctx *ctx, u_int n, u_int m)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_write_limit(s, n, 0, screen_last_x(s));
|
|
screen_write_limit(s, m, 0, screen_last_y(s));
|
|
|
|
s->cx = n;
|
|
s->cy = m;
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_CURSORMOVE, s->cy, s->cx);
|
|
}
|
|
|
|
/* Full to end of screen. */
|
|
void
|
|
screen_write_fill_end_of_screen(struct screen_write_ctx *ctx)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
u_int i;
|
|
|
|
screen_display_fill_area(s, s->cx, s->cy,
|
|
screen_right_x(s, s->cx), 1, ' ', s->attr, s->fg, s->bg);
|
|
screen_display_fill_area(s, 0, s->cy + 1, screen_size_x(s),
|
|
screen_below_y(s, s->cy + 1), ' ', s->attr, s->fg, s->bg);
|
|
|
|
if (ctx->write != NULL) {
|
|
ctx->write(ctx->data, TTY_CLEARENDOFLINE);
|
|
for (i = s->cy + 1; i < screen_size_y(s); i++) {
|
|
ctx->write(ctx->data, TTY_CURSORMOVE, i, 0);
|
|
ctx->write(ctx->data, TTY_CLEARENDOFLINE);
|
|
}
|
|
ctx->write(ctx->data, TTY_CURSORMOVE, s->cy, s->cx);
|
|
}
|
|
}
|
|
|
|
/* Fill entire screen. */
|
|
void
|
|
screen_write_fill_screen(struct screen_write_ctx *ctx)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
u_int i;
|
|
|
|
screen_display_fill_area(s, 0, 0,
|
|
screen_size_x(s), screen_size_y(s), ' ', s->attr, s->fg, s->bg);
|
|
|
|
if (ctx->write != NULL) {
|
|
for (i = 0; i < screen_size_y(s); i++) {
|
|
ctx->write(ctx->data, TTY_CURSORMOVE, i, 0);
|
|
ctx->write(ctx->data, TTY_CLEARENDOFLINE);
|
|
}
|
|
ctx->write(ctx->data, TTY_CURSORMOVE, s->cy, s->cx);
|
|
}
|
|
}
|
|
|
|
/* Fill to end of line. */
|
|
void
|
|
screen_write_fill_end_of_line(struct screen_write_ctx *ctx)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_display_fill_area(s, s->cx, s->cy,
|
|
screen_right_x(s, s->cx), 1, ' ', s->attr, s->fg, s->bg);
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_CLEARENDOFLINE);
|
|
}
|
|
|
|
/* Fill to start of line. */
|
|
void
|
|
screen_write_fill_start_of_line(struct screen_write_ctx *ctx)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_display_fill_area(s, 0, s->cy,
|
|
screen_left_x(s, s->cx), 1, ' ', s->attr, s->fg, s->bg);
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_CLEARSTARTOFLINE);
|
|
}
|
|
|
|
/* Fill entire line. */
|
|
void
|
|
screen_write_fill_line(struct screen_write_ctx *ctx)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
screen_display_fill_area(
|
|
s, 0, s->cy, screen_size_x(s), s->cy, ' ', s->attr, s->fg, s->bg);
|
|
|
|
if (ctx->write != NULL)
|
|
ctx->write(ctx->data, TTY_CLEARLINE);
|
|
}
|
|
|
|
/* Set a screen mode. */
|
|
void
|
|
screen_write_set_mode(struct screen_write_ctx *ctx, int mode)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
s->mode |= mode;
|
|
|
|
if (ctx->write == NULL)
|
|
return;
|
|
|
|
if (mode & MODE_INSERT)
|
|
ctx->write(ctx->data, TTY_INSERTON);
|
|
if (mode & MODE_MOUSE)
|
|
ctx->write(ctx->data, TTY_MOUSEON);
|
|
}
|
|
|
|
/* Clear a screen mode. */
|
|
void
|
|
screen_write_clear_mode(struct screen_write_ctx *ctx, int mode)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
|
|
s->mode &= ~mode;
|
|
|
|
if (ctx->write == NULL)
|
|
return;
|
|
|
|
if (mode & MODE_INSERT)
|
|
ctx->write(ctx->data, TTY_INSERTOFF);
|
|
if (mode & MODE_MOUSE)
|
|
ctx->write(ctx->data, TTY_MOUSEOFF);
|
|
}
|
|
|
|
/* Copy cells from another screen. */
|
|
void
|
|
screen_write_copy_area(struct screen_write_ctx *ctx,
|
|
struct screen *src, u_int nx, u_int ny, u_int ox, u_int oy)
|
|
{
|
|
struct screen *s = ctx->s;
|
|
struct screen_redraw_ctx rctx;
|
|
int saved_mode;
|
|
|
|
screen_write_limit(s, nx, 1, screen_right_x(s, s->cx));
|
|
screen_write_limit(s, ny, 1, screen_below_y(s, s->cy));
|
|
|
|
screen_display_copy_area(ctx->s, src, s->cx, s->cy, nx, ny, ox, oy);
|
|
|
|
if (ctx->write != NULL) {
|
|
/* Save mode XXX hack */
|
|
saved_mode = ctx->s->mode;
|
|
ctx->s->mode &= ~MODE_CURSOR;
|
|
|
|
screen_redraw_start(&rctx, ctx->s, ctx->write, ctx->data);
|
|
screen_redraw_area(&rctx, s->cx, s->cy, nx, ny);
|
|
screen_redraw_stop(&rctx);
|
|
|
|
ctx->s->mode = saved_mode;
|
|
}
|
|
}
|