summaryrefslogtreecommitdiff
path: root/console/display.c
diff options
context:
space:
mode:
Diffstat (limited to 'console/display.c')
-rw-r--r--console/display.c524
1 files changed, 377 insertions, 147 deletions
diff --git a/console/display.c b/console/display.c
index 363c9c09..d2c0b590 100644
--- a/console/display.c
+++ b/console/display.c
@@ -57,6 +57,20 @@ struct screen
};
typedef struct screen *screen_t;
+struct cursor
+{
+ /* The visibility of the cursor. */
+ int status;
+#define CURSOR_INVISIBLE 1
+#define CURSOR_STANDOUT 2
+
+ size_t x;
+ size_t y;
+ size_t saved_x;
+ size_t saved_y;
+};
+typedef struct cursor *cursor_t;
+
struct parse
{
/* The parsing state of output characters, needed to handle escape
@@ -76,47 +90,53 @@ struct parse
int params[PARSE_MAX_PARAMS];
int nparams;
};
+typedef struct parse *parse_t;
-struct cursor
+struct output
{
- /* The visibility of the cursor. */
- int status;
-#define CURSOR_INVISIBLE 1
-#define CURSOR_STANDOUT 2
+ /* The state of the conversion of output characters. */
+ iconv_t cd;
+ /* The output queue holds the characters that are to be outputted.
+ The conversion routine might refuse to handle some incomplete
+ multi-byte or composed character at the end of the buffer, so we
+ have to keep them around. */
+ int stopped;
+ struct condition resumed;
+ char *buffer;
+ size_t allocated;
+ size_t size;
+
+ /* The parsing state of output characters. */
+ struct parse parse;
+};
+typedef struct output *output_t;
- size_t x;
- size_t y;
- size_t saved_x;
- size_t saved_y;
+struct attr
+{
+ /* Current attribute. */
+ char current;
+ int fg;
+ int bg;
+ int def_fg;
+ int def_bg;
+ int reverse : 1;
+ int bold : 1;
+ int blink : 1;
+ int invisible : 1;
+ int dim : 1;
+ int underline : 1;
};
+typedef struct attr *attr_t;
struct display
{
/* The lock for the virtual console display structure. */
struct mutex lock;
- /* The state of the conversion of output characters. */
- iconv_t cd;
-
- struct parse parse;
struct screen screen;
struct cursor cursor;
-
- struct
- {
- /* Current attribute. */
- char current;
- int fg;
- int bg;
- int def_fg;
- int def_bg;
- int reverse : 1;
- int bold : 1;
- int blink : 1;
- int invisible : 1;
- int dim : 1;
- int underline : 1;
- } attr;
+ struct output output;
+ struct attr attr;
/* Indicates if OWNER_ID is initialized. */
int has_owner;
@@ -202,7 +222,7 @@ static void
screen_scroll_left (screen_t screen, size_t x, size_t y, size_t w, size_t h,
int amt, wchar_t chr, char attr)
{
- int y;
+ int i;
wchar_t *matrixp = screen->matrix + y * screen->width + x;
if (amt < 0)
@@ -210,9 +230,9 @@ screen_scroll_left (screen_t screen, size_t x, size_t y, size_t w, size_t h,
if (amt > w)
amt = w;
- for (y = 0; y < y + h; y++)
+ for (i = 0; i < y + h; i++)
{
- wmemmov (matrixp, matrixp + amt, w - amt);
+ wmemmove (matrixp, matrixp + amt, w - amt);
matrixp += screen->width;
}
screen_fill (screen, x + w - amt, y, amt, h, chr, attr);
@@ -222,7 +242,7 @@ static void
screen_scroll_right (screen_t screen, size_t x, size_t y, size_t w, size_t h,
int amt, wchar_t chr, char attr)
{
- int y;
+ int i;
wchar_t *matrixp = screen->matrix + y * screen->width + x;
if (amt < 0)
@@ -230,152 +250,131 @@ screen_scroll_right (screen_t screen, size_t x, size_t y, size_t w, size_t h,
if (amt > w)
amt = w;
- for (y = 0; y < y + h; y++)
+ for (i = 0; i < y + h; i++)
{
- wmemmov (matrixp + amt, matrixp, w - amt);
+ wmemmove (matrixp + amt, matrixp, w - amt);
matrixp += screen->width;
}
screen_fill (screen, x, y, amt, h, chr, attr);
}
-/* Create a new virtual console display, with the system encoding
- being ENCODING. */
-error_t
-display_create (display_t *r_display, const char *encoding)
+static error_t
+output_init (output_t output, const char *encoding)
{
- error_t err = 0;
- display_t display;
-
- *r_display = NULL;
- display = calloc (1, sizeof *display);
- if (!display)
- return ENOMEM;
-
- mutex_init (&display->lock);
- err = screen_init (&display->screen);
- if (err)
- {
- free (display);
- return err;
- }
+ condition_init (&output->resumed);
+ output->stopped = 0;
+ output->buffer = NULL;
+ output->allocated = 0;
+ output->size = 0;
/* WCHAR_T happens to be UCS-4 on the GNU system. */
- display->cd = iconv_open ("WCHAR_T", encoding);
- if (display->cd == (iconv_t) -1)
- {
- err = errno;
- screen_deinit (&display->screen);
- free (display);
- }
- *r_display = display;
- return err;
+ output->cd = iconv_open ("WCHAR_T", encoding);
+ if (output->cd == (iconv_t) -1)
+ return errno;
+ return 0;
}
-
-/* Destroy the display DISPLAY. */
-void
-display_destroy (display_t display)
+static void
+output_deinit (output_t output)
{
- iconv_close (display->cd);
- screen_deinit (&display->screen);
- free (display);
+ iconv_close (output->cd);
}
static void
-handle_esc_bracket_hl (display_t display, int code, int flag)
+handle_esc_bracket_hl (cursor_t cursor, int code, int flag)
{
switch (code)
{
case 34:
/* Cursor standout: <cnorm>, <cvvis>. */
if (flag)
- display->cursor.status |= CURSOR_STANDOUT;
+ cursor->status |= CURSOR_STANDOUT;
else
- display->cursor.status &= ~CURSOR_STANDOUT;
+ cursor->status &= ~CURSOR_STANDOUT;
/* XXX Flag cursor status change. */
break;
}
}
static void
-handle_esc_bracket_m (display_t display, int code)
+handle_esc_bracket_m (attr_t attr, int code)
{
switch (code)
{
case 0:
/* All attributes off: <sgr0>. */
- display->attr.fg = display->attr.def_fg;
- display->attr.bg = display->attr.def_bg;
- display->attr.reverse = display->attr.bold = display->attr.blink
- = display->attr.invisible = display->attr.dim
- = display->attr.underline = 0;
+ attr->fg = attr->def_fg;
+ attr->bg = attr->def_bg;
+ attr->reverse = attr->bold = attr->blink
+ = attr->invisible = attr->dim
+ = attr->underline = 0;
/* Cursor attributes aren't text attributes. */
break;
case 1:
/* Bold on: <bold>. */
- display->attr.bold = 1;
+ attr->bold = 1;
break;
case 2:
/* Dim on: <dim>. */
- display->attr.dim = 1;
+ attr->dim = 1;
break;
case 4:
/* Underline on: <smul>. */
- display->attr.underline = 1;
+ attr->underline = 1;
break;
case 5:
/* Blink on: <blink>. */
- display->attr.blink = 1;
+ attr->blink = 1;
break;
case 7:
/* Reverse video on: <rev>, <smso>. */
- display->attr.reverse = 1;
+ attr->reverse = 1;
break;
case 8:
/* Concealed on: <invis>. */
- display->attr.invisible = 1;
+ attr->invisible = 1;
break;
case 21:
/* Bold Off. */
- display->attr.bold = 0;
+ attr->bold = 0;
break;
case 22:
/* Dim off. */
- display->attr.dim = 0;
+ attr->dim = 0;
break;
case 24:
/* Underline off: <rmul>. */
- display->attr.underline = 0;
+ attr->underline = 0;
break;
case 25:
/* Blink off. */
- display->attr.blink = 0;
+ attr->blink = 0;
break;
case 27:
/* Reverse video off: <rmso>. */
- display->attr.reverse = 0;
+ attr->reverse = 0;
break;
case 28:
/* Concealed off. */
- display->attr.invisible = 0;
+ attr->invisible = 0;
break;
case 30 ... 37:
/* Set foreground color: <setaf>. */
- display->attr.fg = code - 30;
+ attr->fg = code - 30;
break;
case 39:
/* Default foreground color; ANSI?. */
- display->attr.fg = display->attr.def_fg;
+ attr->fg = attr->def_fg;
break;
case 40 ... 47:
/* Set background color: <setab>. */
- display->attr.bg = code - 40;
+ attr->bg = code - 40;
break;
case 49:
/* Default background color; ANSI?. */
- display->attr.bg = display->attr.def_bg;
+ attr->bg = attr->def_bg;
break;
}
/* XXX */
@@ -385,6 +384,7 @@ handle_esc_bracket_m (display_t display, int code)
static void
handle_esc_bracket (display_t display, char op)
{
+ parse_t parse = &display->output.parse;
int i;
static void limit_cursor (void)
@@ -407,13 +407,13 @@ handle_esc_bracket (display_t display, char op)
case 'H':
case 'f':
/* Cursor position: <cup>. */
- display->cursor.x = display->parse.params[1] - 1;
- display->cursor.y = display->parse.params[0] - 1;
+ display->cursor.x = parse->params[1] - 1;
+ display->cursor.y = parse->params[0] - 1;
limit_cursor ();
break;
case 'G':
/* Horizontal position: <hpa>. */
- display->cursor.x = display->parse.params[0] - 1;
+ display->cursor.x = parse->params[0] - 1;
limit_cursor ();
break;
case 'F':
@@ -422,7 +422,7 @@ handle_esc_bracket (display_t display, char op)
/* fall through */
case 'A':
/* Cursor up: <cuu>, <cuu1>. */
- display->cursor.y -= (display->parse.params[0] ?: 1);
+ display->cursor.y -= (parse->params[0] ?: 1);
limit_cursor ();
break;
case 'E':
@@ -431,17 +431,17 @@ handle_esc_bracket (display_t display, char op)
/* Fall through. */
case 'B':
/* Cursor down: <cud1>, <cud>. */
- display->cursor.y += (display->parse.params[0] ?: 1);
+ display->cursor.y += (parse->params[0] ?: 1);
limit_cursor ();
break;
case 'C':
/* Cursor right: <cuf1>, <cuf>. */
- display->cursor.x += (display->parse.params[0] ?: 1);
+ display->cursor.x += (parse->params[0] ?: 1);
limit_cursor ();
break;
case 'D':
/* Cursor left: <cub>, <cub1>. */
- display->cursor.x -= (display->parse.params[0] ?: 1);
+ display->cursor.x -= (parse->params[0] ?: 1);
limit_cursor ();
break;
case 's':
@@ -458,20 +458,20 @@ handle_esc_bracket (display_t display, char op)
break;
case 'h':
/* Reset mode. */
- for (i = 0; i < display->parse.nparams; i++)
- handle_esc_bracket_hl (display, display->parse.params[i], 0);
+ for (i = 0; i < parse->nparams; i++)
+ handle_esc_bracket_hl (&display->cursor, parse->params[i], 0);
break;
case 'l':
/* Set mode. */
- for (i = 0; i < display->parse.nparams; i++)
- handle_esc_bracket_hl (display, display->parse.params[i], 1);
+ for (i = 0; i < parse->nparams; i++)
+ handle_esc_bracket_hl (&display->cursor, parse->params[i], 1);
break;
case 'm':
- for (i = 0; i < display->parse.nparams; i++)
- handle_esc_bracket_m (display, display->parse.params[i]);
+ for (i = 0; i < parse->nparams; i++)
+ handle_esc_bracket_m (&display->attr, parse->params[i]);
break;
case 'J':
- switch (display->parse.params[0])
+ switch (parse->params[0])
{
case 0:
/* Clear to end of screen: <ed>. */
@@ -501,7 +501,7 @@ handle_esc_bracket (display_t display, char op)
}
break;
case 'K':
- switch (display->parse.params[0])
+ switch (parse->params[0])
{
case 0:
/* Clear to end of line: <el>. */
@@ -528,7 +528,7 @@ handle_esc_bracket (display_t display, char op)
screen_scroll_down (&display->screen, 0, display->cursor.y,
display->screen.width,
display->screen.height - display->cursor.y,
- display->parse.params[0] ?: 1,
+ parse->params[0] ?: 1,
L' ', display->attr.current);
break;
case 'M':
@@ -536,7 +536,7 @@ handle_esc_bracket (display_t display, char op)
screen_scroll_up (&display->screen, 0, display->cursor.y,
display->screen.width,
display->screen.height - display->cursor.y,
- display->parse.params[0] ?: 1,
+ parse->params[0] ?: 1,
L' ', display->attr.current);
break;
case '@':
@@ -544,7 +544,7 @@ handle_esc_bracket (display_t display, char op)
screen_scroll_right (&display->screen, display->cursor.x,
display->cursor.y,
display->screen.width - display->cursor.x, 1,
- display->parse.params[0] ?: 1,
+ parse->params[0] ?: 1,
L' ', display->attr.current);
break;
case 'P':
@@ -552,27 +552,27 @@ handle_esc_bracket (display_t display, char op)
screen_scroll_left (&display->screen, display->cursor.x,
display->cursor.y,
display->screen.width - display->cursor.x, 1,
- display->parse.params[0] ?: 1,
+ parse->params[0] ?: 1,
L' ', display->attr.current);
break;
case 'S':
/* Scroll up: <ind>, <indn>. */
screen_scroll_up (&display->screen, 0, 0,
display->screen.width, display->screen.height,
- display->parse.params[0] ?: 1,
+ parse->params[0] ?: 1,
L' ', display->attr.current);
break;
case 'T':
/* Scroll down: <ri>, <rin>. */
screen_scroll_down (&display->screen, 0, 0,
display->screen.width, display->screen.height,
- display->parse.params[0] ?: 1,
+ parse->params[0] ?: 1,
L' ', display->attr.current);
break;
case 'X':
/* Erase character(s): <ech>. */
screen_fill (&display->screen, display->cursor.x, display->cursor.y,
- display->parse.params[0] ?: 1, 1,
+ parse->params[0] ?: 1, 1,
L' ', display->attr.current);
break;
}
@@ -598,18 +598,20 @@ handle_esc_bracket_question_hl (display_t display, int code, int flag)
static void
handle_esc_bracket_question (display_t display, char op)
{
+ parse_t parse = &display->output.parse;
+
int i;
switch (op)
{
case 'h':
/* Reset mode. */
- for (i = 0; i < display->parse.nparams; ++i)
- handle_esc_bracket_question_hl (display, display->parse.params[i], 0);
+ for (i = 0; i < parse->nparams; ++i)
+ handle_esc_bracket_question_hl (display, parse->params[i], 0);
break;
case 'l':
/* Set mode. */
- for (i = 0; i < display->parse.nparams; ++i)
- handle_esc_bracket_question_hl (display, display->parse.params[i], 1);
+ for (i = 0; i < parse->nparams; ++i)
+ handle_esc_bracket_question_hl (display, parse->params[i], 1);
break;
}
}
@@ -618,6 +620,8 @@ handle_esc_bracket_question (display_t display, char op)
static void
display_output_one (display_t display, wchar_t chr)
{
+ parse_t parse = &display->output.parse;
+
void newline (void)
{
if (display->cursor.y < display->screen.height - 1)
@@ -643,7 +647,7 @@ display_output_one (display_t display, wchar_t chr)
}
}
- switch (display->parse.state)
+ switch (parse->state)
{
case STATE_NORMAL:
switch (chr)
@@ -687,7 +691,7 @@ display_output_one (display_t display, wchar_t chr)
/* XXX Flag cursor update. */
break;
case L'\033':
- display->parse.state = STATE_ESC;
+ parse->state = STATE_ESC;
break;
case L'\0':
/* Padding character: <pad>. */
@@ -702,7 +706,7 @@ display_output_one (display_t display, wchar_t chr)
+ display->cursor.x] = chr;
display->cursor.x++;
- if (display->cursor.x == display->screen.height)
+ if (display->cursor.x == display->screen.width)
{
display->cursor.x = 0;
newline ();
@@ -716,7 +720,7 @@ display_output_one (display_t display, wchar_t chr)
switch (chr)
{
case L'[':
- display->parse.state = STATE_ESC_BRACKET_INIT;
+ parse->state = STATE_ESC_BRACKET_INIT;
break;
case L'c':
/* Clear screen and home cursor: <clear>. */
@@ -725,44 +729,44 @@ display_output_one (display_t display, wchar_t chr)
L' ', display->attr.current);
display->cursor.x = display->cursor.y = 0;
/* XXX Flag cursor change. */
- display->parse.state = STATE_NORMAL;
+ parse->state = STATE_NORMAL;
break;
default:
/* Unsupported escape sequence. */
- display->parse.state = STATE_NORMAL;
+ parse->state = STATE_NORMAL;
break;
}
break;
case STATE_ESC_BRACKET_INIT:
- memset (&display->parse.params, 0, sizeof display->parse.params);
- display->parse.nparams = 0;
+ memset (&parse->params, 0, sizeof parse->params);
+ parse->nparams = 0;
if (chr == '?')
{
- display->parse.state = STATE_ESC_BRACKET_QUESTION;
+ parse->state = STATE_ESC_BRACKET_QUESTION;
break; /* Consume the question mark. */
}
else
- display->parse.state = STATE_ESC_BRACKET;
+ parse->state = STATE_ESC_BRACKET;
/* Fall through. */
case STATE_ESC_BRACKET:
case STATE_ESC_BRACKET_QUESTION:
if (chr >= '0' && chr <= '9')
- display->parse.params[display->parse.nparams]
- = display->parse.params[display->parse.nparams]*10 + chr - '0';
+ parse->params[parse->nparams]
+ = parse->params[parse->nparams]*10 + chr - '0';
else if (chr == ';')
{
- if (++(display->parse.nparams) >= PARSE_MAX_PARAMS)
- display->parse.state = STATE_NORMAL; /* too many */
+ if (++(parse->nparams) >= PARSE_MAX_PARAMS)
+ parse->state = STATE_NORMAL; /* too many */
}
else
{
- display->parse.nparams++;
- if (display->parse.state == STATE_ESC_BRACKET)
+ parse->nparams++;
+ if (parse->state == STATE_ESC_BRACKET)
handle_esc_bracket (display, chr);
else
handle_esc_bracket_question (display, chr);
- display->parse.state = STATE_NORMAL;
+ parse->state = STATE_NORMAL;
}
break;
default:
@@ -770,31 +774,29 @@ display_output_one (display_t display, wchar_t chr)
}
}
-
/* Output LENGTH bytes starting from BUFFER in the system encoding.
Set BUFFER and LENGTH to the new values. The exact semantics are
just as in the iconv interface. */
-error_t
-display_output (display_t display, char **buffer, size_t *length)
+static error_t
+display_output_some (display_t display, char **buffer, size_t *length)
{
#define CONV_OUTBUF_SIZE 256
error_t err = 0;
- mutex_lock (&display->lock);
while (!err && *length > 0)
{
size_t nconv;
wchar_t outbuf[CONV_OUTBUF_SIZE];
char *outptr = (char *) outbuf;
- size_t outsize = CONV_OUTBUF_SIZE;
+ size_t outsize = CONV_OUTBUF_SIZE * sizeof (wchar_t);
error_t saved_err;
int i;
- nconv = iconv (display->cd, buffer, length, &outptr, &outsize);
+ nconv = iconv (display->output.cd, buffer, length, &outptr, &outsize);
saved_err = errno;
/* First process all successfully converted characters. */
- for (i = 0; i < CONV_OUTBUF_SIZE - outsize; i++)
+ for (i = 0; i < CONV_OUTBUF_SIZE - outsize / sizeof (wchar_t); i++)
display_output_one (display, outbuf[i]);
if (nconv == (size_t) -1)
@@ -808,11 +810,52 @@ display_output (display_t display, char **buffer, size_t *length)
err = saved_err;
}
}
- mutex_unlock (&display->lock);
return err;
}
-
+/* Create a new virtual console display, with the system encoding
+ being ENCODING. */
+error_t
+display_create (display_t *r_display, const char *encoding)
+{
+ error_t err = 0;
+ display_t display;
+
+ *r_display = NULL;
+ display = calloc (1, sizeof *display);
+ if (!display)
+ return ENOMEM;
+
+ mutex_init (&display->lock);
+ err = screen_init (&display->screen);
+ if (err)
+ {
+ free (display);
+ return err;
+ }
+
+ err = output_init (&display->output, encoding);
+ if (err)
+ {
+ screen_deinit (&display->screen);
+ free (display);
+ }
+ *r_display = display;
+ return err;
+}
+
+
+/* Destroy the display DISPLAY. */
+void
+display_destroy (display_t display)
+{
+ output_deinit (&display->output);
+ screen_deinit (&display->screen);
+ free (display);
+}
+
+
+/* Return the dimensions of the display DISPLAY in *WINSIZE. */
void
display_getsize (display_t display, struct winsize *winsize)
{
@@ -823,3 +866,190 @@ display_getsize (display_t display, struct winsize *winsize)
winsize->ws_ypixel = 0;
mutex_unlock (&display->lock);
}
+
+
+/* Set the owner of the display DISPLAY to PID. The owner receives
+ the SIGWINCH signal when the terminal size changes. */
+error_t
+display_set_owner (display_t display, pid_t pid)
+{
+ mutex_lock (&display->lock);
+ display->has_owner = 1;
+ display->owner_id = pid;
+ mutex_unlock (&display->lock);
+ return 0;
+}
+
+
+/* Return the owner of the display DISPLAY in PID. If there is no
+ owner, return ENOTTY. */
+error_t
+display_get_owner (display_t display, pid_t *pid)
+{
+ error_t err = 0;
+ mutex_lock (&display->lock);
+ if (!display->has_owner)
+ err = ENOTTY;
+ else
+ *pid = display->owner_id;
+ mutex_unlock (&display->lock);
+ return err;
+}
+
+/* Output DATALEN characters from the buffer DATA on display DISPLAY.
+ The DATA must be supplied in the system encoding configured for
+ DISPLAY. The function returns the amount of bytes written (might
+ be smaller than DATALEN) or -1 and the error number in errno. If
+ NONBLOCK is not zero, return with -1 and set errno to EWOULDBLOCK
+ if operation would block for a long time. */
+ssize_t
+display_output (display_t display, int nonblock, char *data, size_t datalen)
+{
+ output_t output = &display->output;
+ error_t err;
+ char *buffer;
+ size_t buffer_size;
+ ssize_t amount;
+
+ error_t ensure_output_buffer_size (size_t new_size)
+ {
+ /* Must be a power of two. */
+#define OUTPUT_ALLOCSIZE 32
+
+ if (output->allocated < new_size)
+ {
+ char *new_buffer;
+ new_size = (new_size + OUTPUT_ALLOCSIZE - 1)
+ & ~(OUTPUT_ALLOCSIZE - 1);
+ new_buffer = realloc (output->buffer, new_size);
+ if (!new_buffer)
+ return ENOMEM;
+ output->buffer = new_buffer;
+ output->allocated = new_size;
+ }
+ return 0;
+ }
+
+ mutex_lock (&display->lock);
+ while (output->stopped)
+ {
+ if (nonblock)
+ {
+ mutex_unlock (&display->lock);
+ errno = EWOULDBLOCK;
+ return -1;
+ }
+ if (hurd_condition_wait (&output->resumed, &display->lock))
+ {
+ mutex_unlock (&display->lock);
+ errno = EINTR;
+ return -1;
+ }
+ }
+
+ if (output->size)
+ {
+ err = ensure_output_buffer_size (output->size + datalen);
+ if (err)
+ {
+ mutex_unlock (&display->lock);
+ errno = ENOMEM;
+ return -1;
+ }
+ buffer = output->buffer;
+ buffer_size = output->size;
+ memcpy (buffer + buffer_size, data, datalen);
+ buffer_size += datalen;
+ }
+ else
+ {
+ buffer = data;
+ buffer_size = datalen;
+ }
+ amount = buffer_size;
+ err = display_output_some (display, &buffer, &buffer_size);
+ amount -= buffer_size;
+
+ if (err && !amount)
+ {
+ mutex_unlock (&display->lock);
+ errno = err;
+ return err;
+ }
+ /* XXX What should be done with invalid characters etc? */
+ if (buffer_size)
+ {
+ /* If we used the caller's buffer DATA, the remaining bytes
+ might not fit in our internal output buffer. In this case we
+ can reallocate the buffer in VCONS without needing to update
+ OUTPUT (as it points into DATA). */
+ err = ensure_output_buffer_size (buffer_size);
+ if (err)
+ {
+ mutex_unlock (&display->lock);
+ return err;
+ }
+ memmove (output->buffer, buffer, buffer_size);
+ }
+ output->size = buffer_size;
+ amount += buffer_size;
+
+ mutex_unlock (&display->lock);
+ return amount;
+}
+
+ssize_t display_read (display_t display, int nonblock, off_t off,
+ char *data, size_t len)
+{
+ mutex_lock (&display->lock);
+ if (off + len > 2000 * sizeof(wchar_t))
+ len = 2000 * sizeof(wchar_t) - off;
+ memcpy (data, (char *) display->screen.matrix + off, len);
+ mutex_unlock (&display->lock);
+ return len;
+}
+
+/* Resume the output on the display DISPLAY. */
+void
+display_start_output (display_t display)
+{
+ mutex_lock (&display->lock);
+ if (display->output.stopped)
+ {
+ display->output.stopped = 0;
+ condition_broadcast (&display->output.resumed);
+ }
+ mutex_unlock (&display->lock);
+}
+
+
+/* Stop all output on the display DISPLAY. */
+void
+display_stop_output (display_t display)
+{
+ mutex_lock (&display->lock);
+ display->output.stopped = 1;
+ mutex_unlock (&display->lock);
+}
+
+
+/* Return the number of pending output bytes for DISPLAY. */
+size_t
+display_pending_output (display_t display)
+{
+ int output_size;
+ mutex_lock (&display->lock);
+ output_size = display->output.size;
+ mutex_unlock (&display->lock);
+ return output_size;
+}
+
+
+/* Flush the output buffer, discarding all pending data. */
+void
+display_discard_output (display_t display)
+{
+ mutex_lock (&display->lock);
+ display->output.size = 0;
+ mutex_unlock (&display->lock);
+}