Console: Add buffer object

The buffer object manages the cells and scrollback buffer. It is optimized for
speed: fast rotations, fast resize, etc.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
David Herrmann 2011-12-04 17:56:43 +01:00
parent 8f06542909
commit 03a67f977a
3 changed files with 345 additions and 0 deletions

View File

@ -18,6 +18,7 @@ libkmscon_core_la_SOURCES = \
src/console.c src/console.h \
src/output.c src/output.h \
src/console_char.c \
src/console_cell.c \
src/log.c src/log.h \
src/eloop.c src/eloop.h \
src/vt.c src/vt.h

View File

@ -41,6 +41,7 @@
struct kmscon_char;
struct kmscon_font;
struct kmscon_buffer;
struct kmscon_console;
/* single printable characters */
@ -65,6 +66,14 @@ void kmscon_font_unref(struct kmscon_font *font);
int kmscon_font_draw(struct kmscon_font *font, const struct kmscon_char *ch,
cairo_t *cr, uint32_t x, uint32_t y);
/* console buffer with cell objects */
int kmscon_buffer_new(struct kmscon_buffer **out, uint32_t x, uint32_t y);
void kmscon_buffer_ref(struct kmscon_buffer *buf);
void kmscon_buffer_unref(struct kmscon_buffer *buf);
int kmscon_buffer_resize(struct kmscon_buffer *buf, uint32_t x, uint32_t y);
/* console objects */
int kmscon_console_new(struct kmscon_console **out);

335
src/console_cell.c Normal file
View File

@ -0,0 +1,335 @@
/*
* kmscon - Console Buffer and Cell Objects
*
* Copyright (c) 2011 David Herrmann <dh.herrmann@googlemail.com>
* Copyright (c) 2011 University of Tuebingen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* Console Buffer and Cell Objects
* A console buffer contains all the characters that are printed to the screen
* and the whole scrollback buffer. It has a fixed height and width measured in
* characters which can be changed on the fly.
* The buffer is a linked list of lines. The tail of the list is the current
* screen buffer which can be modified by the application. The rest of the list
* is the scrollback-buffer.
* The linked-list allows fast rotations but prevents fast access. Therefore,
* modifications of the scrollback-buffer is prohibited.
* For fast access to the current screen buffer, we use an array (cache) of
* pointers to the first n lines.
* The current screen position can be any line of the scrollback-buffer.
*
* Y-resize simply adjusts the cache to point to the new lines. X-resize only
* modifies the current screen buffer. The scrollback-buffer is not modified to
* improve performance.
*
* Cells
* A single cell describes a single character that is printed in that cell. The
* character itself is a kmscon_char unicode character. The cell also contains
* the color of the character and some other metadata.
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <cairo.h>
#include "console.h"
#include "log.h"
#define DEFAULT_WIDTH 80
#define DEFAULT_HEIGHT 24
struct cell {
struct kmscon_char *ch;
};
struct line {
struct line *next;
struct line *prev;
unsigned int num;
unsigned int size;
struct cell *cells;
};
struct kmscon_buffer {
unsigned long ref;
unsigned int count;
struct line *first;
struct line *last;
unsigned int size_x;
unsigned int size_y;
unsigned int max_scrollback;
struct line **cache;
struct line *current;
};
static void free_line(struct line *line)
{
unsigned int i;
if (!line)
return;
for (i = 0; i < line->size; ++i) {
kmscon_char_free(line->cells[i].ch);
}
free(line->cells);
free(line);
}
/*
* Allocates a new line of width \width and pushes it to the tail of the buffer.
* The line is filled with blanks. If the maximum number of lines is already
* reached, the first line is removed and pushed to the tail.
*/
static int push_line(struct kmscon_buffer *buf)
{
struct line *line;
struct cell *tmp;
unsigned int i, width;
int ret;
if (!buf)
return -EINVAL;
width = buf->size_x;
if (!width)
width = DEFAULT_WIDTH;
if (buf->count > (buf->size_y + buf->max_scrollback)) {
line = buf->first;
buf->first = line->next;
buf->first->prev = NULL;
if (buf->current == line)
buf->current = buf->first;
line->next = NULL;
line->prev = NULL;
--buf->count;
} else {
line = malloc(sizeof(*line));
if (!line)
return -ENOMEM;
memset(line, 0, sizeof(*line));
}
if (line->size < width) {
tmp = realloc(line->cells, width * sizeof(struct cell));
if (!tmp)
goto err_free;
memset(&tmp[line->size], 0,
(width - line->size) * sizeof(struct cell));
line->cells = tmp;
line->size = width;
}
line->num = width;
for (i = 0; i < line->num; ++i) {
if (line->cells[i].ch) {
ret = kmscon_char_set_u8(line->cells[i].ch, "?", 1);
} else {
ret = kmscon_char_new_u8(&line->cells[i].ch, "?", 1);
}
if (ret)
goto err_free;
}
if (buf->last) {
line->prev = buf->last;
buf->last->next = line;
buf->last = line;
} else {
buf->first = line;
buf->last = line;
}
++buf->count;
if (buf->cache) {
for (i = 0; i < buf->size_y; ++i)
buf->cache[i] = buf->cache[i]->next;
}
return 0;
err_free:
free_line(line);
return -ENOMEM;
}
static int resize_line(struct line *line, unsigned int width)
{
unsigned int i;
struct cell *tmp;
int ret;
if (!line)
return -EINVAL;
if (!width)
width = DEFAULT_WIDTH;
if (line->size < width) {
tmp = realloc(line->cells, sizeof(struct cell) * width);
if (!tmp)
return -ENOMEM;
memset(&tmp[line->size], 0,
(width - line->size) * sizeof(struct cell));
line->cells = tmp;
line->size = width;
}
for (i = line->num; i < width; ++i) {
if (!line->cells[i].ch) {
ret = kmscon_char_new_u8(&line->cells[i].ch, "?", 1);
if (ret)
return -ENOMEM;
}
}
line->num = width;
return 0;
}
int kmscon_buffer_new(struct kmscon_buffer **out, uint32_t x, uint32_t y)
{
struct kmscon_buffer *buf;
int ret;
if (!out)
return -EINVAL;
buf = malloc(sizeof(*buf));
if (!buf)
return -ENOMEM;
memset(buf, 0, sizeof(*buf));
buf->ref = 1;
ret = kmscon_buffer_resize(buf, x, y);
if (ret)
goto err_free;
*out = buf;
return 0;
err_free:
free(buf);
return ret;
}
void kmscon_buffer_ref(struct kmscon_buffer *buf)
{
if (!buf)
return;
++buf->ref;
}
void kmscon_buffer_unref(struct kmscon_buffer *buf)
{
struct line *iter, *tmp;
if (!buf || !buf->ref)
return;
if (--buf->ref)
return;
for (iter = buf->first; iter; ) {
tmp = iter;
iter = iter->next;
free_line(tmp);
}
free(buf->cache);
free(buf);
}
int kmscon_buffer_resize(struct kmscon_buffer *buf, uint32_t x, uint32_t y)
{
struct line **cache, *iter;
unsigned int old_x, old_y, i;
int ret, j;
if (!buf)
return -EINVAL;
if (!x)
x = DEFAULT_WIDTH;
if (!y)
y = DEFAULT_HEIGHT;
old_x = buf->size_x;
old_y = buf->size_y;
buf->size_x = x;
buf->size_y = y;
if (old_y != y) {
while (buf->count < y) {
ret = push_line(buf);
if (ret)
goto err_reset;
}
cache = realloc(buf->cache, sizeof(struct line*) * y);
if (!cache) {
ret = -ENOMEM;
goto err_reset;
}
memset(cache, 0, sizeof(struct line*) * y);
iter = buf->last;
for (j = (y - 1); j >= 0; --j) {
cache[j] = iter;
iter = iter->prev;
}
buf->cache = cache;
}
if (old_x != x) {
for (i = 0; i < buf->size_y; ++i) {
ret = resize_line(buf->cache[i], x);
if (ret)
goto err_reset;
}
}
return 0;
err_reset:
buf->size_x = old_x;
buf->size_y = old_y;
/* TODO: improve error recovery and correctly reset the buffer */
return ret;
}