root/src/system/boot/platform/generic/video_text_console.cpp
/*
 * Copyright 2021, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */

#include <boot/stage2.h>
#include <boot/platform/generic/text_console.h>
#include <boot/platform/generic/video.h>

#include <frame_buffer_console.h>


extern console_module_info gFrameBufferConsoleModule;


class VideoTextConsole : public ConsoleNode {
        public:
                VideoTextConsole();

                void Init(addr_t framebuffer);

                virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer,
                        size_t bufferSize);
                virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer,
                        size_t bufferSize);

                virtual void    ClearScreen();
                virtual int32   Width();
                virtual int32   Height();
                virtual void    SetCursor(int32 x, int32 y);
                virtual void    SetCursorVisible(bool visible);
                virtual void    SetColors(int32 foreground, int32 background);

        private:
                uint16 fColor;
                bool fShowCursor;
                int32 fCursorX, fCursorY;
                int32 fScreenWidth, fScreenHeight;
};

static VideoTextConsole sVideoTextConsole;
static bool sVideoTextConsoleInitialized;


//      #pragma mark -


VideoTextConsole::VideoTextConsole()
        : ConsoleNode()
{
}


void
VideoTextConsole::Init(addr_t framebuffer)
{
        frame_buffer_update(framebuffer, gKernelArgs.frame_buffer.width,
                gKernelArgs.frame_buffer.height, gKernelArgs.frame_buffer.depth,
                gKernelArgs.frame_buffer.bytes_per_row);
        gFrameBufferConsoleModule.get_size(&fScreenWidth, &fScreenHeight);

        SetCursorVisible(false);
        SetCursor(0, 0);
        SetColors(15, 0);
        ClearScreen();
}


ssize_t
VideoTextConsole::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
{
        return B_ERROR;
}


ssize_t
VideoTextConsole::WriteAt(void *cookie, off_t /*pos*/, const void *buffer,
        size_t bufferSize)
{
        const char *string = (const char *)buffer;
        for (size_t i = 0; i < bufferSize; i++) {
                const char c = string[i];
                if (c == '\n' || fCursorX >= fScreenWidth) {
                        fCursorX = 0;
                        if (fCursorY == (fScreenHeight - 1)) {
                                // Move the screen up and clear the bottom line.
                                gFrameBufferConsoleModule.blit(0, 1, fScreenWidth, 0,
                                        0, fScreenHeight - 1);
                                gFrameBufferConsoleModule.fill_glyph(0, fCursorY, fScreenWidth,
                                        1, ' ', fColor);
                        } else {
                                fCursorY++;
                        }
                }

                switch (c) {
                        case '\n':
                                // already handled above
                                break;

                        default:
                                gFrameBufferConsoleModule.put_glyph(fCursorX, fCursorY, c, fColor);
                                break;
                }
                fCursorX++;
        }
        return bufferSize;
}


void
VideoTextConsole::ClearScreen()
{
        gFrameBufferConsoleModule.clear(fColor);
}


int32
VideoTextConsole::Width()
{
        return fScreenWidth;
}


int32
VideoTextConsole::Height()
{
        return fScreenHeight;
}


void
VideoTextConsole::SetCursor(int32 x, int32 y)
{
        fCursorX = x;
        fCursorY = y;
        if (fShowCursor)
                SetCursorVisible(true);
}


void
VideoTextConsole::SetCursorVisible(bool visible)
{
        fShowCursor = visible;
        if (fShowCursor)
                gFrameBufferConsoleModule.move_cursor(fCursorX, fCursorY);
        else
                gFrameBufferConsoleModule.move_cursor(-1, -1);
}


void
VideoTextConsole::SetColors(int32 foreground, int32 background)
{
        fColor = (background & 0xf) << 4 | (foreground & 0xf);
}


ConsoleNode*
video_text_console_init(addr_t frameBuffer)
{
        if (!sVideoTextConsoleInitialized) {
                new(&sVideoTextConsole) VideoTextConsole;
                sVideoTextConsoleInitialized = true;
        }

        sVideoTextConsole.Init(frameBuffer);
        return &sVideoTextConsole;
}