Relicense Sortix to the ISC license.
I hereby relicense all my work on Sortix under the ISC license as below.
All Sortix contributions by other people are already under this license,
are not substantial enough to be copyrightable, or have been removed.
All imported code from other projects is compatible with this license.
All GPL licensed code from other projects had previously been removed.
Copyright 2011-2016 Jonas 'Sortie' Termansen and contributors.
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 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.
2016-03-02 17:38:16 -05:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
|
|
|
*
|
|
|
|
* 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 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.
|
|
|
|
*
|
|
|
|
* lfbtextbuffer.cpp
|
|
|
|
* An indexable text buffer rendered to a graphical linear frame buffer.
|
|
|
|
*/
|
2012-07-30 13:02:05 -04:00
|
|
|
|
2013-10-26 20:42:10 -04:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <sortix/vga.h>
|
|
|
|
|
|
|
|
#include <sortix/kernel/kernel.h>
|
2012-07-30 13:02:05 -04:00
|
|
|
#include <sortix/kernel/kthread.h>
|
|
|
|
#include <sortix/kernel/refcount.h>
|
2013-01-09 04:47:22 -05:00
|
|
|
#include <sortix/kernel/scheduler.h>
|
2013-10-26 20:42:10 -04:00
|
|
|
#include <sortix/kernel/textbuffer.h>
|
2013-05-12 18:41:30 -04:00
|
|
|
#include <sortix/kernel/thread.h>
|
2013-01-09 04:47:22 -05:00
|
|
|
|
2012-07-30 13:02:05 -04:00
|
|
|
#include "vga.h"
|
|
|
|
#include "lfbtextbuffer.h"
|
|
|
|
|
|
|
|
namespace Sortix {
|
|
|
|
|
|
|
|
static uint32_t ColorFromRGB(uint8_t r, uint8_t g, uint8_t b)
|
|
|
|
{
|
|
|
|
union { struct { uint8_t b, g, r, a; }; uint32_t color; } ret;
|
|
|
|
ret.r = r;
|
|
|
|
ret.g = g;
|
|
|
|
ret.b = b;
|
|
|
|
ret.a = 255;
|
|
|
|
return ret.color;
|
|
|
|
}
|
|
|
|
|
2012-12-22 15:10:06 -05:00
|
|
|
static void LFBTextBuffer__RenderThread(void* user)
|
|
|
|
{
|
|
|
|
((LFBTextBuffer*) user)->RenderThread();
|
|
|
|
}
|
|
|
|
|
2012-07-30 13:02:05 -04:00
|
|
|
LFBTextBuffer* CreateLFBTextBuffer(uint8_t* lfb, uint32_t lfbformat,
|
|
|
|
size_t xres, size_t yres, size_t scansize)
|
|
|
|
{
|
2012-12-22 15:10:06 -05:00
|
|
|
const size_t QUEUE_LENGTH = 1024;
|
2012-07-30 13:02:05 -04:00
|
|
|
size_t columns = xres / (VGA_FONT_WIDTH + 1);
|
|
|
|
size_t rows = yres / VGA_FONT_HEIGHT;
|
|
|
|
size_t fontsize = VGA_FONT_CHARSIZE * VGA_FONT_NUMCHARS;
|
2012-12-22 15:10:06 -05:00
|
|
|
uint8_t* backbuf;
|
2012-07-30 13:02:05 -04:00
|
|
|
uint8_t* font;
|
2014-04-19 14:28:54 -04:00
|
|
|
TextChar* chars;
|
2012-12-22 15:10:06 -05:00
|
|
|
TextBufferCmd* queue;
|
2012-07-30 13:02:05 -04:00
|
|
|
LFBTextBuffer* ret;
|
|
|
|
|
2012-12-22 15:10:06 -05:00
|
|
|
Process* kernel_process = Scheduler::GetKernelProcess();
|
|
|
|
|
|
|
|
if ( !(backbuf = new uint8_t[yres * scansize]) )
|
2012-07-30 13:02:05 -04:00
|
|
|
goto cleanup_done;
|
2012-12-22 15:10:06 -05:00
|
|
|
if ( !(font = new uint8_t[fontsize]) )
|
|
|
|
goto cleanup_backbuf;
|
2014-04-19 14:28:54 -04:00
|
|
|
if ( !(chars = new TextChar[columns * rows]) )
|
2012-07-30 13:02:05 -04:00
|
|
|
goto cleanup_font;
|
2012-12-22 15:10:06 -05:00
|
|
|
if ( !(queue = new TextBufferCmd[QUEUE_LENGTH]) )
|
2015-10-04 12:55:06 -04:00
|
|
|
goto cleanup_chars;
|
2012-12-22 15:10:06 -05:00
|
|
|
if ( !(ret = new LFBTextBuffer) )
|
|
|
|
goto cleanup_queue;
|
2012-07-30 13:02:05 -04:00
|
|
|
|
2012-09-22 14:38:34 -04:00
|
|
|
memcpy(font, VGA::GetFont(), fontsize);
|
2013-11-11 16:21:56 -05:00
|
|
|
ret->execute_lock = KTHREAD_MUTEX_INITIALIZER;
|
2012-12-22 15:10:06 -05:00
|
|
|
ret->queue_lock = KTHREAD_MUTEX_INITIALIZER;
|
|
|
|
ret->queue_not_full = KTHREAD_COND_INITIALIZER;
|
|
|
|
ret->queue_not_empty = KTHREAD_COND_INITIALIZER;
|
|
|
|
ret->queue_exit = KTHREAD_COND_INITIALIZER;
|
|
|
|
ret->queue_sync = KTHREAD_COND_INITIALIZER;
|
|
|
|
ret->queue_paused = KTHREAD_COND_INITIALIZER;
|
|
|
|
ret->queue_resume = KTHREAD_COND_INITIALIZER;
|
|
|
|
ret->queue = queue;
|
|
|
|
ret->queue_length = QUEUE_LENGTH;
|
|
|
|
ret->queue_offset = 0;
|
|
|
|
ret->queue_used = 0;
|
|
|
|
ret->queue_is_paused = false;
|
|
|
|
ret->queue_thread = false;
|
2012-07-30 13:02:05 -04:00
|
|
|
ret->lfb = lfb;
|
2012-12-22 15:10:06 -05:00
|
|
|
ret->backbuf = backbuf;
|
2012-07-30 13:02:05 -04:00
|
|
|
ret->lfbformat = lfbformat;
|
|
|
|
ret->pixelsx = xres;
|
|
|
|
ret->pixelsy = yres;
|
|
|
|
ret->scansize = scansize;
|
|
|
|
ret->columns = columns;
|
|
|
|
ret->rows = rows;
|
|
|
|
ret->font = font;
|
2014-04-19 14:28:54 -04:00
|
|
|
memset(chars, 0, sizeof(chars[0]) * columns * rows);
|
2012-07-30 13:02:05 -04:00
|
|
|
ret->chars = chars;
|
|
|
|
for ( size_t i = 0; i < 16UL; i++ )
|
|
|
|
{
|
2014-05-01 07:04:32 -04:00
|
|
|
uint8_t r = i & 0b0100 ? (i & 0b1000 ? 255 : 191) : (i & 0b1000 ? 63 : 0);
|
|
|
|
uint8_t g = i & 0b0010 ? (i & 0b1000 ? 255 : 191) : (i & 0b1000 ? 63 : 0);
|
|
|
|
uint8_t b = i & 0b0001 ? (i & 0b1000 ? 255 : 191) : (i & 0b1000 ? 63 : 0);
|
2012-07-30 13:02:05 -04:00
|
|
|
ret->colors[i] = ColorFromRGB(r, g, b);
|
|
|
|
}
|
|
|
|
ret->cursorenabled = true;
|
|
|
|
ret->cursorpos = TextPos(0, 0);
|
|
|
|
for ( size_t y = 0; y < yres; y++ )
|
2012-09-22 14:38:34 -04:00
|
|
|
memset(lfb + scansize * y, 0, lfbformat/8UL * xres);
|
2013-11-11 16:21:56 -05:00
|
|
|
ret->emergency_state = false;
|
2015-05-10 14:28:33 -04:00
|
|
|
ret->invalidated = false;
|
2012-12-22 15:10:06 -05:00
|
|
|
|
2015-03-19 19:08:08 -04:00
|
|
|
if ( !kernel_process )
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret->queue_thread = true; // Visible to new thread.
|
2012-12-22 15:10:06 -05:00
|
|
|
if ( !RunKernelThread(kernel_process, LFBTextBuffer__RenderThread, ret) )
|
2014-11-20 11:04:45 -05:00
|
|
|
ret->queue_thread = false;
|
2012-12-22 15:10:06 -05:00
|
|
|
|
2012-07-30 13:02:05 -04:00
|
|
|
return ret;
|
|
|
|
|
2012-12-22 15:10:06 -05:00
|
|
|
cleanup_queue:
|
|
|
|
delete[] queue;
|
2012-07-30 13:02:05 -04:00
|
|
|
cleanup_chars:
|
|
|
|
delete[] chars;
|
|
|
|
cleanup_font:
|
|
|
|
delete[] font;
|
2012-12-22 15:10:06 -05:00
|
|
|
cleanup_backbuf:
|
|
|
|
delete[] backbuf;
|
2012-07-30 13:02:05 -04:00
|
|
|
cleanup_done:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-03-19 19:08:08 -04:00
|
|
|
void LFBTextBuffer::SpawnThreads()
|
|
|
|
{
|
|
|
|
if ( queue_thread )
|
|
|
|
return;
|
|
|
|
Process* kernel_process = Scheduler::GetKernelProcess();
|
|
|
|
queue_thread = true; // Visible to new thread.
|
|
|
|
if ( !RunKernelThread(kernel_process, LFBTextBuffer__RenderThread, this) )
|
|
|
|
queue_thread = false;
|
|
|
|
}
|
|
|
|
|
2012-07-30 13:02:05 -04:00
|
|
|
LFBTextBuffer::LFBTextBuffer()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
LFBTextBuffer::~LFBTextBuffer()
|
|
|
|
{
|
2012-12-22 15:10:06 -05:00
|
|
|
if ( queue_thread )
|
|
|
|
{
|
|
|
|
TextBufferCmd cmd;
|
|
|
|
cmd.type = TEXTBUFCMD_EXIT;
|
|
|
|
IssueCommand(&cmd);
|
2014-11-20 11:04:45 -05:00
|
|
|
kthread_mutex_lock(&queue_lock);
|
2012-12-22 15:10:06 -05:00
|
|
|
while ( queue_thread )
|
|
|
|
kthread_cond_wait(&queue_exit, &queue_lock);
|
2014-11-20 11:04:45 -05:00
|
|
|
kthread_mutex_unlock(&queue_lock);
|
2012-12-22 15:10:06 -05:00
|
|
|
}
|
|
|
|
delete[] backbuf;
|
2012-07-30 13:02:05 -04:00
|
|
|
delete[] font;
|
|
|
|
delete[] chars;
|
2012-12-22 15:10:06 -05:00
|
|
|
delete[] queue;
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t LFBTextBuffer::Width() const
|
|
|
|
{
|
|
|
|
return columns;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t LFBTextBuffer::Height() const
|
|
|
|
{
|
|
|
|
return rows;
|
|
|
|
}
|
|
|
|
|
2012-09-07 14:46:06 -04:00
|
|
|
bool LFBTextBuffer::UsablePosition(TextPos pos) const
|
2012-07-30 13:02:05 -04:00
|
|
|
{
|
|
|
|
return pos.x < columns && pos.y < rows;
|
|
|
|
}
|
|
|
|
|
|
|
|
TextPos LFBTextBuffer::CropPosition(TextPos pos) const
|
|
|
|
{
|
|
|
|
if ( columns <= pos.x )
|
|
|
|
pos.x = columns - 1;
|
|
|
|
if ( rows <= pos.y )
|
|
|
|
pos.y = rows -1;
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
TextPos LFBTextBuffer::AddToPosition(TextPos pos, size_t count)
|
|
|
|
{
|
|
|
|
size_t index = pos.y * columns + pos.x + count;
|
|
|
|
TextPos ret(index % columns, index / columns);
|
|
|
|
return CropPosition(ret);
|
|
|
|
}
|
|
|
|
|
2014-04-19 14:28:54 -04:00
|
|
|
void LFBTextBuffer::RenderChar(TextChar textchar, size_t posx, size_t posy)
|
2012-07-30 13:02:05 -04:00
|
|
|
{
|
|
|
|
if ( columns <= posx || rows <= posy )
|
|
|
|
return;
|
|
|
|
// TODO: Support other font sizes and resolutions.
|
|
|
|
if ( lfbformat != 32 || VGA_FONT_WIDTH != 8UL )
|
|
|
|
return;
|
|
|
|
bool drawcursor = cursorenabled && posx == cursorpos.x && posy == cursorpos.y;
|
2014-04-19 14:28:54 -04:00
|
|
|
uint8_t fgcoloridx = textchar.vgacolor >> 0 & 0x0F;
|
2015-10-04 12:55:06 -04:00
|
|
|
if ( textchar.attr & ATTR_BOLD )
|
|
|
|
fgcoloridx |= 0x08;
|
2014-04-19 14:28:54 -04:00
|
|
|
uint8_t bgcoloridx = textchar.vgacolor >> 4 & 0x0F;
|
2012-07-30 13:02:05 -04:00
|
|
|
uint32_t fgcolor = colors[fgcoloridx];
|
|
|
|
uint32_t bgcolor = colors[bgcoloridx];
|
2015-09-11 09:50:37 -04:00
|
|
|
int remap = VGA::MapWideToVGAFont(textchar.c);
|
|
|
|
const uint8_t* charfont = VGA::GetCharacterFont(font, remap);
|
2012-07-30 13:02:05 -04:00
|
|
|
for ( size_t y = 0; y < VGA_FONT_HEIGHT; y++ )
|
|
|
|
{
|
|
|
|
size_t pixely = posy * VGA_FONT_HEIGHT + y;
|
2014-04-19 14:28:54 -04:00
|
|
|
uint8_t linebitmap = charfont[y];
|
2015-09-11 09:50:37 -04:00
|
|
|
uint32_t* line = (uint32_t*) (lfb + pixely * scansize);
|
|
|
|
size_t pixelxoff = posx * (VGA_FONT_WIDTH+1);
|
|
|
|
for ( size_t x = 0; x < VGA_FONT_WIDTH; x++ )
|
|
|
|
line[pixelxoff + x] = linebitmap & 1 << (7-x) ? fgcolor : bgcolor;
|
|
|
|
uint32_t lastcolor = bgcolor;
|
|
|
|
if ( 0xB0 <= remap && remap <= 0xDF && (linebitmap & 1) )
|
|
|
|
lastcolor = fgcolor;
|
|
|
|
line[pixelxoff + VGA_FONT_WIDTH] = lastcolor;
|
2015-09-14 17:45:33 -04:00
|
|
|
if ( unlikely(posx + 1 == columns) )
|
|
|
|
{
|
|
|
|
for ( size_t x = pixelxoff + VGA_FONT_WIDTH + 1; x < pixelsx; x++ )
|
|
|
|
line[x] = bgcolor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( unlikely(posy + 1 == rows) )
|
|
|
|
{
|
|
|
|
size_t pixelyoff = rows * VGA_FONT_HEIGHT;
|
|
|
|
for ( size_t y = pixelyoff; y < pixelsy; y++ )
|
|
|
|
{
|
|
|
|
uint32_t* line = (uint32_t*) (lfb + y * scansize);
|
|
|
|
size_t pixelxoff = posx * (VGA_FONT_WIDTH+1);
|
|
|
|
for ( size_t x = 0; x < VGA_FONT_WIDTH + 1; x++ )
|
|
|
|
line[pixelxoff + x] = bgcolor;
|
|
|
|
if ( unlikely(posx + 1 == columns) )
|
|
|
|
{
|
|
|
|
for ( size_t x = pixelxoff + VGA_FONT_WIDTH + 1; x < pixelsx; x++ )
|
|
|
|
line[x] = bgcolor;
|
|
|
|
}
|
|
|
|
}
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
2015-10-04 12:55:06 -04:00
|
|
|
if ( likely(!drawcursor) && !(textchar.attr & ATTR_UNDERLINE) )
|
2012-07-30 13:02:05 -04:00
|
|
|
return;
|
2015-10-04 12:55:06 -04:00
|
|
|
size_t underlines = VGA_FONT_HEIGHT - (!drawcursor ? 1 : 0);
|
|
|
|
for ( size_t y = VGA_FONT_HEIGHT - 2; y < underlines; y++ )
|
2012-07-30 13:02:05 -04:00
|
|
|
{
|
|
|
|
size_t pixely = posy * VGA_FONT_HEIGHT + y;
|
|
|
|
for ( size_t x = 0; x < VGA_FONT_WIDTH+1; x++ )
|
|
|
|
{
|
|
|
|
uint32_t* line = (uint32_t*) (lfb + pixely * scansize);
|
|
|
|
size_t pixelx = posx * (VGA_FONT_WIDTH+1) + x;
|
2012-08-05 12:58:03 -04:00
|
|
|
line[pixelx] = fgcolor;
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::RenderCharAt(TextPos pos)
|
|
|
|
{
|
|
|
|
RenderChar(chars[pos.y * columns + pos.x], pos.x, pos.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::RenderRegion(size_t c1, size_t r1, size_t c2, size_t r2)
|
|
|
|
{
|
|
|
|
for ( size_t y = r1; y <= r2; y++ )
|
|
|
|
for ( size_t x = c1; x <= c2; x++ )
|
|
|
|
RenderChar(chars[y * columns + x], x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::RenderRange(TextPos from, TextPos to)
|
|
|
|
{
|
|
|
|
from = CropPosition(from);
|
|
|
|
to = CropPosition(to);
|
2012-12-22 15:10:06 -05:00
|
|
|
uint8_t* orig_lfb = lfb;
|
|
|
|
bool backbuffered = from.y != to.y;
|
|
|
|
if ( backbuffered )
|
|
|
|
{
|
|
|
|
lfb = backbuf;
|
|
|
|
from.x = 0;
|
|
|
|
to.x = columns - 1;
|
|
|
|
}
|
2012-07-30 13:02:05 -04:00
|
|
|
TextPos i = from;
|
|
|
|
RenderChar(chars[i.y * columns + i.x], i.x, i.y);
|
2012-12-22 15:10:06 -05:00
|
|
|
while ( !(i.x==to.x && i.y==to.y) )
|
2012-07-30 13:02:05 -04:00
|
|
|
{
|
|
|
|
i = AddToPosition(i, 1);
|
|
|
|
RenderChar(chars[i.y * columns + i.x], i.x, i.y);
|
2012-12-22 15:10:06 -05:00
|
|
|
}
|
|
|
|
if ( backbuffered )
|
|
|
|
{
|
|
|
|
lfb = orig_lfb;
|
2015-09-14 17:45:33 -04:00
|
|
|
size_t scanline_start = from.y * VGA_FONT_HEIGHT;
|
|
|
|
size_t scanline_end = ((to.y+1) * VGA_FONT_HEIGHT) - 1;
|
|
|
|
if ( to.y + 1 == rows )
|
|
|
|
scanline_end = pixelsy - 1;
|
2012-12-22 15:10:06 -05:00
|
|
|
for ( size_t sc = scanline_start; sc <= scanline_end; sc++ )
|
|
|
|
{
|
|
|
|
size_t offset = sc * scansize;
|
2015-09-14 17:45:33 -04:00
|
|
|
memcpy(lfb + offset, backbuf + offset, pixelsx * sizeof(uint32_t));
|
2012-12-22 15:10:06 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::IssueCommand(TextBufferCmd* cmd)
|
|
|
|
{
|
2015-05-10 14:28:33 -04:00
|
|
|
if ( invalidated )
|
|
|
|
{
|
|
|
|
invalidated = false;
|
|
|
|
TextBufferCmd newcmd;
|
|
|
|
newcmd.type = TEXTBUFCMD_REDRAW;
|
|
|
|
IssueCommand(&newcmd);
|
|
|
|
}
|
2015-03-19 19:08:08 -04:00
|
|
|
if ( !queue_thread || emergency_state )
|
2013-11-11 16:21:56 -05:00
|
|
|
{
|
|
|
|
bool exit_requested = false;
|
|
|
|
bool sync_requested = false;
|
|
|
|
bool pause_requested = false;
|
|
|
|
TextPos render_from(columns - 1, rows - 1);
|
|
|
|
TextPos render_to(0, 0);
|
|
|
|
ExecuteCommand(cmd, exit_requested, sync_requested, pause_requested, render_from, render_to);
|
|
|
|
if ( !IsTextPosBeforeTextPos(render_to, render_from) )
|
|
|
|
RenderRange(render_from, render_to);
|
|
|
|
return;
|
|
|
|
}
|
2012-12-22 15:10:06 -05:00
|
|
|
ScopedLock lock(&queue_lock);
|
|
|
|
while ( queue_used == queue_length )
|
|
|
|
kthread_cond_wait(&queue_not_full, &queue_lock);
|
|
|
|
if ( !queue_used )
|
|
|
|
kthread_cond_signal(&queue_not_empty);
|
|
|
|
queue[(queue_offset + queue_used++) % queue_length] = *cmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::StopRendering()
|
|
|
|
{
|
2015-03-19 19:08:08 -04:00
|
|
|
if ( !queue_thread || emergency_state )
|
2013-11-11 16:21:56 -05:00
|
|
|
return;
|
2012-12-22 15:10:06 -05:00
|
|
|
TextBufferCmd cmd;
|
|
|
|
cmd.type = TEXTBUFCMD_PAUSE;
|
|
|
|
IssueCommand(&cmd);
|
|
|
|
ScopedLock lock(&queue_lock);
|
|
|
|
while ( !queue_is_paused )
|
|
|
|
kthread_cond_wait(&queue_paused, &queue_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::ResumeRendering()
|
|
|
|
{
|
2015-03-19 19:08:08 -04:00
|
|
|
if ( !queue_thread || emergency_state )
|
2013-11-11 16:21:56 -05:00
|
|
|
return;
|
2012-12-22 15:10:06 -05:00
|
|
|
ScopedLock lock(&queue_lock);
|
|
|
|
queue_is_paused = false;
|
|
|
|
kthread_cond_signal(&queue_resume);
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
TextChar LFBTextBuffer::GetChar(TextPos pos) const
|
|
|
|
{
|
|
|
|
if ( UsablePosition(pos) )
|
2012-12-22 15:10:06 -05:00
|
|
|
{
|
|
|
|
((LFBTextBuffer*) this)->StopRendering();
|
2014-04-19 14:28:54 -04:00
|
|
|
TextChar ret = chars[pos.y * columns + pos.x];
|
2012-12-22 15:10:06 -05:00
|
|
|
((LFBTextBuffer*) this)->ResumeRendering();
|
|
|
|
return ret;
|
|
|
|
}
|
2015-10-04 12:55:06 -04:00
|
|
|
return {0, 0, 0};
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::SetChar(TextPos pos, TextChar c)
|
|
|
|
{
|
|
|
|
if ( !UsablePosition(pos) )
|
|
|
|
return;
|
2012-12-22 15:10:06 -05:00
|
|
|
TextBufferCmd cmd;
|
|
|
|
cmd.type = TEXTBUFCMD_CHAR;
|
|
|
|
cmd.x = pos.x;
|
|
|
|
cmd.y = pos.y;
|
2014-04-19 14:28:54 -04:00
|
|
|
cmd.c = c;
|
2012-12-22 15:10:06 -05:00
|
|
|
IssueCommand(&cmd);
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool LFBTextBuffer::GetCursorEnabled() const
|
|
|
|
{
|
2012-12-22 15:10:06 -05:00
|
|
|
((LFBTextBuffer*) this)->StopRendering();
|
|
|
|
bool ret = cursorenabled;
|
|
|
|
((LFBTextBuffer*) this)->ResumeRendering();
|
|
|
|
return ret;
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::SetCursorEnabled(bool enablecursor)
|
|
|
|
{
|
2012-12-22 15:10:06 -05:00
|
|
|
TextBufferCmd cmd;
|
|
|
|
cmd.type = TEXTBUFCMD_CURSOR_SET_ENABLED;
|
|
|
|
cmd.b = enablecursor;
|
|
|
|
IssueCommand(&cmd);
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
TextPos LFBTextBuffer::GetCursorPos() const
|
|
|
|
{
|
2012-12-22 15:10:06 -05:00
|
|
|
((LFBTextBuffer*) this)->StopRendering();
|
|
|
|
TextPos ret = cursorpos;
|
|
|
|
((LFBTextBuffer*) this)->ResumeRendering();
|
|
|
|
return ret;
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::SetCursorPos(TextPos newcursorpos)
|
|
|
|
{
|
2012-12-22 15:10:06 -05:00
|
|
|
TextBufferCmd cmd;
|
|
|
|
cmd.type = TEXTBUFCMD_CURSOR_MOVE;
|
|
|
|
cmd.x = newcursorpos.x;
|
|
|
|
cmd.y = newcursorpos.y;
|
|
|
|
IssueCommand(&cmd);
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
|
|
|
|
2015-05-10 14:28:33 -04:00
|
|
|
void LFBTextBuffer::Invalidate()
|
|
|
|
{
|
|
|
|
invalidated = true;
|
|
|
|
}
|
|
|
|
|
2012-07-30 13:02:05 -04:00
|
|
|
size_t LFBTextBuffer::OffsetOfPos(TextPos pos) const
|
|
|
|
{
|
|
|
|
return pos.y * columns + pos.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::Scroll(ssize_t off, TextChar fillwith)
|
|
|
|
{
|
|
|
|
if ( !off )
|
|
|
|
return;
|
2012-12-22 15:10:06 -05:00
|
|
|
TextBufferCmd cmd;
|
|
|
|
cmd.type = TEXTBUFCMD_SCROLL;
|
|
|
|
cmd.scroll_offset = off;
|
2014-04-19 14:28:54 -04:00
|
|
|
cmd.c = fillwith;
|
2012-12-22 15:10:06 -05:00
|
|
|
IssueCommand(&cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::Move(TextPos to, TextPos from, size_t numchars)
|
|
|
|
{
|
|
|
|
to = CropPosition(to);
|
|
|
|
from = CropPosition(from);
|
|
|
|
TextBufferCmd cmd;
|
|
|
|
cmd.type = TEXTBUFCMD_MOVE;
|
|
|
|
cmd.to_x = to.x;
|
|
|
|
cmd.to_y = to.y;
|
|
|
|
cmd.from_x = from.x;
|
|
|
|
cmd.from_y = from.y;
|
|
|
|
cmd.val = numchars;
|
|
|
|
IssueCommand(&cmd);
|
|
|
|
}
|
|
|
|
|
2015-10-04 12:55:06 -04:00
|
|
|
void LFBTextBuffer::Fill(TextPos from, TextPos to, TextChar fillwith)
|
2012-12-22 15:10:06 -05:00
|
|
|
{
|
|
|
|
from = CropPosition(from);
|
|
|
|
to = CropPosition(to);
|
|
|
|
TextBufferCmd cmd;
|
|
|
|
cmd.type = TEXTBUFCMD_FILL;
|
|
|
|
cmd.from_x = from.x;
|
|
|
|
cmd.from_y = from.y;
|
|
|
|
cmd.to_x = to.x;
|
|
|
|
cmd.to_y = to.y;
|
2014-04-19 14:28:54 -04:00
|
|
|
cmd.c = fillwith;
|
2012-12-22 15:10:06 -05:00
|
|
|
IssueCommand(&cmd);
|
|
|
|
}
|
|
|
|
|
2014-04-19 14:28:54 -04:00
|
|
|
void LFBTextBuffer::DoScroll(ssize_t off, TextChar entry)
|
2012-12-22 15:10:06 -05:00
|
|
|
{
|
2012-07-30 13:02:05 -04:00
|
|
|
bool neg = 0 < off;
|
|
|
|
size_t absoff = off < 0 ? -off : off;
|
|
|
|
if ( rows < absoff )
|
|
|
|
absoff = rows;
|
|
|
|
TextPos scrollfrom = neg ? TextPos{0, absoff} : TextPos{0, 0};
|
|
|
|
TextPos scrollto = neg ? TextPos{0, 0} : TextPos{0, absoff};
|
|
|
|
TextPos fillfrom = neg ? TextPos{0, rows-absoff} : TextPos{0, 0};
|
|
|
|
TextPos fillto = neg ? TextPos{columns-1, rows-1} : TextPos{columns-1, absoff-1};
|
|
|
|
size_t scrollchars = columns * (rows-absoff);
|
2012-12-22 15:10:06 -05:00
|
|
|
DoMove(scrollto, scrollfrom, scrollchars);
|
2015-10-04 12:55:06 -04:00
|
|
|
DoFill(fillfrom, fillto, entry);
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
|
|
|
|
2012-12-22 15:10:06 -05:00
|
|
|
void LFBTextBuffer::DoMove(TextPos to, TextPos from, size_t numchars)
|
2012-07-30 13:02:05 -04:00
|
|
|
{
|
|
|
|
size_t dest = OffsetOfPos(to);
|
|
|
|
size_t src = OffsetOfPos(from);
|
|
|
|
if ( dest < src )
|
|
|
|
for ( size_t i = 0; i < numchars; i++ )
|
2015-10-04 12:55:06 -04:00
|
|
|
chars[dest + i] = chars[src + i];
|
2012-07-30 13:02:05 -04:00
|
|
|
else if ( src < dest )
|
|
|
|
for ( size_t i = 0; i < numchars; i++ )
|
2015-10-04 12:55:06 -04:00
|
|
|
chars[dest + numchars-1 - i] = chars[src + numchars-1 - i];
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
|
|
|
|
2015-10-04 12:55:06 -04:00
|
|
|
void LFBTextBuffer::DoFill(TextPos from, TextPos to, TextChar fillwith)
|
2012-07-30 13:02:05 -04:00
|
|
|
{
|
|
|
|
size_t start = OffsetOfPos(from);
|
|
|
|
size_t end = OffsetOfPos(to);
|
|
|
|
for ( size_t i = start; i <= end; i++ )
|
2015-10-04 12:55:06 -04:00
|
|
|
chars[i] = fillwith;
|
2012-12-22 15:10:06 -05:00
|
|
|
}
|
|
|
|
|
2013-11-11 16:21:56 -05:00
|
|
|
bool LFBTextBuffer::IsCommandIdempotent(const TextBufferCmd* cmd) const
|
|
|
|
{
|
|
|
|
switch ( cmd->type )
|
|
|
|
{
|
|
|
|
case TEXTBUFCMD_EXIT: return true;
|
|
|
|
case TEXTBUFCMD_SYNC: return true;
|
|
|
|
case TEXTBUFCMD_PAUSE: return true;
|
|
|
|
case TEXTBUFCMD_CHAR: return true;
|
|
|
|
case TEXTBUFCMD_CURSOR_SET_ENABLED: return true;
|
|
|
|
case TEXTBUFCMD_CURSOR_MOVE: return true;
|
|
|
|
case TEXTBUFCMD_MOVE: return false;
|
|
|
|
case TEXTBUFCMD_FILL: return true;
|
|
|
|
case TEXTBUFCMD_SCROLL: return false;
|
2015-05-10 14:28:33 -04:00
|
|
|
case TEXTBUFCMD_REDRAW: return true;
|
2013-11-11 16:21:56 -05:00
|
|
|
default: return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::ExecuteCommand(TextBufferCmd* cmd,
|
|
|
|
bool& exit_requested,
|
|
|
|
bool& sync_requested,
|
|
|
|
bool& pause_requested,
|
|
|
|
TextPos& render_from,
|
|
|
|
TextPos& render_to)
|
|
|
|
{
|
|
|
|
switch ( cmd->type )
|
|
|
|
{
|
|
|
|
case TEXTBUFCMD_EXIT:
|
|
|
|
exit_requested = true;
|
|
|
|
break;
|
|
|
|
case TEXTBUFCMD_SYNC:
|
|
|
|
sync_requested = true;
|
|
|
|
break;
|
|
|
|
case TEXTBUFCMD_PAUSE:
|
|
|
|
pause_requested = true;
|
|
|
|
break;
|
|
|
|
case TEXTBUFCMD_CHAR:
|
|
|
|
{
|
|
|
|
TextPos pos(cmd->x, cmd->y);
|
|
|
|
chars[pos.y * columns + pos.x] = cmd->c;
|
|
|
|
if ( IsTextPosBeforeTextPos(pos, render_from) )
|
|
|
|
render_from = pos;
|
|
|
|
if ( IsTextPosAfterTextPos(pos, render_to) )
|
|
|
|
render_to = pos;
|
|
|
|
} break;
|
|
|
|
case TEXTBUFCMD_CURSOR_SET_ENABLED:
|
|
|
|
if ( cmd->b != cursorenabled )
|
|
|
|
{
|
|
|
|
cursorenabled = cmd->b;
|
|
|
|
if ( IsTextPosBeforeTextPos(cursorpos, render_from) )
|
|
|
|
render_from = cursorpos;
|
|
|
|
if ( IsTextPosAfterTextPos(cursorpos, render_to) )
|
|
|
|
render_to = cursorpos;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TEXTBUFCMD_CURSOR_MOVE:
|
|
|
|
{
|
|
|
|
TextPos pos(cmd->x, cmd->y);
|
|
|
|
if ( cursorpos.x != pos.x || cursorpos.y != pos.y )
|
|
|
|
{
|
|
|
|
if ( IsTextPosBeforeTextPos(cursorpos, render_from) )
|
|
|
|
render_from = cursorpos;
|
|
|
|
if ( IsTextPosAfterTextPos(cursorpos, render_to) )
|
|
|
|
render_to = cursorpos;
|
|
|
|
cursorpos = pos;
|
|
|
|
if ( IsTextPosBeforeTextPos(cursorpos, render_from) )
|
|
|
|
render_from = cursorpos;
|
|
|
|
if ( IsTextPosAfterTextPos(cursorpos, render_to) )
|
|
|
|
render_to = cursorpos;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case TEXTBUFCMD_MOVE:
|
|
|
|
{
|
|
|
|
TextPos to(cmd->to_x, cmd->to_y);
|
|
|
|
TextPos from(cmd->from_x, cmd->from_y);
|
|
|
|
size_t numchars = cmd->val;
|
|
|
|
DoMove(to, from, numchars);
|
|
|
|
TextPos toend = AddToPosition(to, numchars);
|
|
|
|
if ( IsTextPosBeforeTextPos(to, render_from) )
|
|
|
|
render_from = to;
|
|
|
|
if ( IsTextPosAfterTextPos(toend, render_to) )
|
|
|
|
render_to = toend;
|
|
|
|
} break;
|
|
|
|
case TEXTBUFCMD_FILL:
|
|
|
|
{
|
|
|
|
TextPos from(cmd->from_x, cmd->from_y);
|
|
|
|
TextPos to(cmd->to_x, cmd->to_y);
|
2015-10-04 12:55:06 -04:00
|
|
|
DoFill(from, to, cmd->c);
|
2013-11-11 16:21:56 -05:00
|
|
|
if ( IsTextPosBeforeTextPos(from, render_from) )
|
|
|
|
render_from = from;
|
|
|
|
if ( IsTextPosAfterTextPos(to, render_to) )
|
|
|
|
render_to = to;
|
|
|
|
} break;
|
|
|
|
case TEXTBUFCMD_SCROLL:
|
|
|
|
{
|
|
|
|
ssize_t off = cmd->scroll_offset;
|
|
|
|
DoScroll(off, cmd->c);
|
|
|
|
render_from = {0, 0};
|
|
|
|
render_to = {columns-1, rows-1};
|
|
|
|
} break;
|
2015-05-10 14:28:33 -04:00
|
|
|
case TEXTBUFCMD_REDRAW:
|
|
|
|
{
|
|
|
|
render_from = {0, 0};
|
|
|
|
render_to = {columns-1, rows-1};
|
|
|
|
} break;
|
2013-11-11 16:21:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-22 15:10:06 -05:00
|
|
|
void LFBTextBuffer::RenderThread()
|
|
|
|
{
|
|
|
|
queue_is_paused = false;
|
|
|
|
size_t offset = 0;
|
|
|
|
size_t amount = 0;
|
|
|
|
bool exit_requested = false;
|
|
|
|
bool sync_requested = false;
|
|
|
|
bool pause_requested = false;
|
|
|
|
|
|
|
|
while ( true )
|
|
|
|
{
|
|
|
|
kthread_mutex_lock(&queue_lock);
|
|
|
|
|
|
|
|
if ( queue_used == queue_length && amount )
|
|
|
|
kthread_cond_signal(&queue_not_full);
|
|
|
|
|
|
|
|
queue_used -= amount;
|
|
|
|
queue_offset = (queue_offset + amount) % queue_length;
|
|
|
|
|
|
|
|
if ( !queue_used )
|
|
|
|
{
|
|
|
|
if ( exit_requested )
|
|
|
|
{
|
|
|
|
queue_thread = false;
|
|
|
|
kthread_cond_signal(&queue_exit);
|
|
|
|
kthread_mutex_unlock(&queue_lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( sync_requested )
|
|
|
|
{
|
|
|
|
kthread_cond_signal(&queue_sync);
|
|
|
|
sync_requested = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( pause_requested )
|
|
|
|
{
|
|
|
|
queue_is_paused = true;
|
|
|
|
kthread_cond_signal(&queue_paused);
|
|
|
|
while ( queue_is_paused )
|
|
|
|
kthread_cond_wait(&queue_resume, &queue_lock);
|
|
|
|
pause_requested = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while ( !queue_used )
|
|
|
|
kthread_cond_wait(&queue_not_empty, &queue_lock);
|
|
|
|
|
|
|
|
amount = queue_used;
|
|
|
|
offset = queue_offset;
|
|
|
|
|
|
|
|
kthread_mutex_unlock(&queue_lock);
|
|
|
|
|
2013-11-11 16:21:56 -05:00
|
|
|
execute_amount = amount;
|
|
|
|
|
|
|
|
kthread_mutex_lock(&execute_lock);
|
|
|
|
|
2012-12-22 15:10:06 -05:00
|
|
|
TextPos render_from(columns - 1, rows - 1);
|
|
|
|
TextPos render_to(0, 0);
|
|
|
|
|
|
|
|
for ( size_t i = 0; i < amount; i++ )
|
|
|
|
{
|
|
|
|
TextBufferCmd* cmd = &queue[(offset + i) % queue_length];
|
2013-11-11 16:21:56 -05:00
|
|
|
ExecuteCommand(cmd, exit_requested, sync_requested, pause_requested, render_from, render_to);
|
2012-12-22 15:10:06 -05:00
|
|
|
}
|
|
|
|
|
2013-11-11 16:21:56 -05:00
|
|
|
kthread_mutex_unlock(&execute_lock);
|
|
|
|
|
2012-12-22 15:10:06 -05:00
|
|
|
if ( !IsTextPosBeforeTextPos(render_to, render_from) )
|
|
|
|
RenderRange(render_from, render_to);
|
|
|
|
}
|
2012-07-30 13:02:05 -04:00
|
|
|
}
|
|
|
|
|
2013-11-11 16:21:56 -05:00
|
|
|
bool LFBTextBuffer::EmergencyIsImpaired()
|
|
|
|
{
|
|
|
|
return !emergency_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LFBTextBuffer::EmergencyRecoup()
|
|
|
|
{
|
|
|
|
if ( !emergency_state )
|
|
|
|
emergency_state = true;
|
|
|
|
|
|
|
|
if ( !kthread_mutex_trylock(&queue_lock) )
|
|
|
|
return false;
|
|
|
|
kthread_mutex_unlock(&queue_lock);
|
|
|
|
|
|
|
|
if ( !kthread_mutex_trylock(&execute_lock) )
|
|
|
|
{
|
|
|
|
for ( size_t i = 0; i < execute_amount; i++ )
|
|
|
|
{
|
|
|
|
TextBufferCmd* cmd = &queue[(queue_offset + i) % queue_length];
|
|
|
|
if ( !IsCommandIdempotent(cmd) )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
kthread_mutex_unlock(&execute_lock);
|
|
|
|
|
|
|
|
TextPos render_from(0, 0);
|
|
|
|
TextPos render_to(columns - 1, rows - 1);
|
|
|
|
|
|
|
|
for ( size_t i = 0; i < queue_used; i++ )
|
|
|
|
{
|
|
|
|
bool exit_requested = false;
|
|
|
|
bool sync_requested = false;
|
|
|
|
bool pause_requested = false;
|
|
|
|
TextBufferCmd* cmd = &queue[(queue_offset + i) % queue_length];
|
|
|
|
ExecuteCommand(cmd, exit_requested, sync_requested, pause_requested,
|
|
|
|
render_from, render_to);
|
|
|
|
}
|
|
|
|
|
|
|
|
queue_used = 0;
|
|
|
|
queue_offset = 0;
|
|
|
|
|
|
|
|
RenderRange(render_from, render_to);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LFBTextBuffer::EmergencyReset()
|
|
|
|
{
|
|
|
|
// TODO: Reset everything here!
|
|
|
|
|
2015-10-04 12:55:06 -04:00
|
|
|
Fill(TextPos{0, 0}, TextPos{columns-1, rows-1}, TextChar{0, 0, 0});
|
2013-11-11 16:21:56 -05:00
|
|
|
SetCursorPos(TextPos{0, 0});
|
|
|
|
}
|
|
|
|
|
2012-07-30 13:02:05 -04:00
|
|
|
} // namespace Sortix
|