root/src/system/boot/platform/amiga_m68k/console.cpp
/*
 * Copyright 2007, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT license.
 *
 * Author:
 *              François Revol, revol@free.fr.
 */

#include <SupportDefs.h>
#include <string.h>
#include "rom_calls.h"
#include <util/kernel_cpp.h>

#include "Handle.h"
#include "console.h"
#include "keyboard.h"

class ConsoleHandle : public CharHandle {
        public:
                ConsoleHandle();

                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);

                void    Clear();
                void    MoveTo(int16 x, int16 y);
                void    SetColor(int32 foreground, int32 background);
                int             Columns();
                int             Rows();

        private:
                static int16    fX;
                static int16    fY;
                uint16  fPen;
};

class ConsoleDevice : public ExecDevice {
        public:
                ConsoleDevice(const char *title);
                virtual ~ConsoleDevice();

                status_t        Open();

                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);

                void    Clear();
                void    MoveTo(int16 x, int16 y);
                void    SetColor(int32 foreground, int32 background);

                int             Columns();
                int             Rows();

                int     WaitForKey();

        protected:
                const char      *fTitle;
                struct Window   *fWindow;
                int     fRows, fCols;
};

class KeyboardDevice : public ExecDevice {
        public:
                KeyboardDevice();
                virtual ~KeyboardDevice();

                status_t        Open();

                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);

                int     WaitForKey();
                status_t        ReadEvent(struct InputEvent *event);

        protected:
};

class LLKeyboardDevice : public CharHandle {
        public:
                LLKeyboardDevice();
                virtual ~LLKeyboardDevice();

                status_t        Open();

                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);

                int     WaitForKey();

        protected:
};


static const uint16 kPalette[] = {
        0x000,
        0x00a,
        0x0a0,
        0x0aa,
        0xa00,
        0xa0a,
        0xa50,
        0xaaa,
        0x555,
        0x55f,
        0x5f5,
        0x5ff,
        0xf55,
        0xf5f,
        0xff5,
        0xfff
};

struct Screen *gScreen;
static int16 sFontWidth, sFontHeight;
static int sScreenTopOffset = 16;
int16 ConsoleHandle::fX = 0;
int16 ConsoleHandle::fY = 0;

FILE *stdin, *stdout, *stderr, *dbgerr;


//      #pragma mark -

ConsoleHandle::ConsoleHandle()
        : CharHandle()
{
}


ssize_t
ConsoleHandle::ReadAt(void */*cookie*/, off_t /*pos*/, void *buffer,
        size_t bufferSize)
{
        // don't seek in character devices
        // not implemented (and not yet? needed)
        return B_ERROR;
}


ssize_t
ConsoleHandle::WriteAt(void */*cookie*/, off_t /*pos*/, const void *buffer,
        size_t bufferSize)
{
        const char *string = (const char *)buffer;
        size_t i, len;

        // be nice to our audience and replace single "\n" with "\r\n"

        for (i = 0, len = 0; i < bufferSize; i++, len++) {
                if (string[i] == '\0')
                        break;
                if (string[i] == '\n') {
                        //Text(&gScreen->RastPort, &string[i - len], len);
                        fX = 0;
                        fY++;
                        if (fY >= console_height())
                                fY = 0;
                        len = 0;
                        console_set_cursor(fX, fY);
                        continue;
                }
                Text(&gScreen->RastPort, &string[i], 1);
        }

        // not exactly, but we don't care...
        return bufferSize;
}


void
ConsoleHandle::Clear()
{
        Move(&gScreen->RastPort, 0, sScreenTopOffset);
        ClearScreen(&gScreen->RastPort);
}


void
ConsoleHandle::MoveTo(int16 x, int16 y)
{
        fX = x;
        fY = y;
        Move(&gScreen->RastPort, sFontWidth * x,
                sFontHeight * y + sScreenTopOffset);
        // why do I have to add this to keep the title ?
        
}


void
ConsoleHandle::SetColor(int32 foreground, int32 background)
{
        SetAPen(&gScreen->RastPort, foreground);
        SetBPen(&gScreen->RastPort, background);
}


int
ConsoleHandle::Columns()
{
        int columnCount = gScreen->Width / sFontWidth;
        return columnCount;
}


int
ConsoleHandle::Rows()
{
        int lineCount = (gScreen->Height - sScreenTopOffset) / sFontHeight;
        return lineCount;
}


// #pragma mark -


ConsoleDevice::ConsoleDevice(const char *title)
        : ExecDevice(),
        fTitle(title),
        fWindow(NULL)
{
}


ConsoleDevice::~ConsoleDevice()
{
}


status_t
ConsoleDevice::Open()
{
        status_t err;
        
        if (fWindow)
                return B_ERROR;

        err = AllocRequest(sizeof(struct IOStdReq));
        if (err < B_OK)
                panic("AllocRequest");;
        if (err < B_OK)
                return err;

        int16 topEdge = gScreen->Height - 60;
        int height = 60;

        if (fTitle == NULL) {
                topEdge = 10;
                height = gScreen->Height - 40;
                fTitle = "Console";
        }

        struct NewWindow newWindow = {
                0, topEdge,
                gScreen->Width, height,
                BLACK, WHITE,
                IDCMP_CLOSEWINDOW,
                WFLG_SIZEGADGET | WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_SIMPLE_REFRESH
                        | WFLG_ACTIVATE,
                NULL,
                NULL,
                fTitle,
                gScreen,
                NULL,
                100, 45,
                gScreen->Width, gScreen->Height,
                CUSTOMSCREEN
        };
        
        fWindow = OpenWindow(&newWindow);
        if (fWindow == NULL)
                panic("OpenWindow");;
        if (fWindow == NULL)
                return B_ERROR;
        fIOStdReq->io_Data = fWindow;
        fIOStdReq->io_Length = sizeof(struct Window);
        err = ExecDevice::Open(CONSOLENAME, CONU_STANDARD, 0);
        if (err < B_OK)
                return err;

        MoveTo(0, 0);
        SetColor(WHITE, BLACK);
        Clear();
}


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


int
ConsoleDevice::WaitForKey()
{
        char ascii;
        
        if (Read(&ascii, 1) < 1)
                return TEXT_CONSOLE_NO_KEY;
        //dprintf("ascii %d %c\n", ascii, ascii);

        if (ascii == (char)0x9b) {
                if (Read(&ascii, 1) < 1)
                        return TEXT_CONSOLE_NO_KEY;
                //dprintf(">ascii %d %c\n", ascii, ascii);
                switch (ascii) {
                        case 'A':
                                return TEXT_CONSOLE_KEY_UP;
                        case 'B':
                                return TEXT_CONSOLE_KEY_DOWN;
                        case 'D':
                                return TEXT_CONSOLE_KEY_LEFT;
                        case 'C':
                                return TEXT_CONSOLE_KEY_RIGHT;
                        case '4':
                        {
                                if (Read(&ascii, 1) < 1)
                                        return TEXT_CONSOLE_NO_KEY;
                                if (ascii == '~')
                                        return TEXT_CONSOLE_NO_KEY;
                                switch (ascii) {
                                        case '1':
                                                Read(&ascii, 1); // ~
                                                return TEXT_CONSOLE_KEY_PAGE_UP;
                                        case '2':
                                                Read(&ascii, 1); // ~
                                                return TEXT_CONSOLE_KEY_PAGE_UP;
                                        default:
                                                return TEXT_CONSOLE_NO_KEY;
                                }
                                return TEXT_CONSOLE_NO_KEY;
                        }
                        default:
                                break;
                }
        }
        return ascii;
}


ssize_t
ConsoleDevice::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
{
        return ExecDevice::WriteAt(cookie, pos, buffer, bufferSize);
}


void
ConsoleDevice::Clear()
{
        char buff[] = "\x0c";
        WriteAt(NULL, 0LL, buff, sizeof(buff) - 1);
}


void
ConsoleDevice::MoveTo(int16 x, int16 y)
{
        char buff[32];
        x = MIN(79,MAX(0,x));
        y = MIN(24,MAX(0,y));
        sprintf(buff, "\x9b%02d;%02d\x48", y + 1, x + 1);
        //buff[4] += (char)x;
        //buff[2] += (char)y;
        WriteAt(NULL, 0LL, buff, strlen(buff));
}


void
ConsoleDevice::SetColor(int32 foreground, int32 background)
{
        //char buff[] = "\x9b37;40m";
        char buff[] = "\2330;30;40m";

        
        if (foreground >= 8) {
                foreground -= 8;
                //buff[1] = '1'; // bold
        }
        
        // else
        //      buff[1] = '2';
        //if (background >= 8)
        //      background -= 8;

        if (foreground == background) {
                foreground = 7 - background;
        }

        if (foreground < 8)
                buff[4] += foreground;

        if (background < 8)
                buff[7] += background;

        WriteAt(NULL, 0LL, buff, sizeof(buff) - 1);
}


int
ConsoleDevice::Columns()
{
        struct ConUnit *unit = (struct ConUnit *)fIOStdReq->io_Unit;
        return unit->cu_XMax;
}


int
ConsoleDevice::Rows()
{
        struct ConUnit *unit = (struct ConUnit *)fIOStdReq->io_Unit;
        return unit->cu_YMax - 1;
}


// #pragma mark -


KeyboardDevice::KeyboardDevice()
        : ExecDevice()
{
}


KeyboardDevice::~KeyboardDevice()
{
}


status_t
KeyboardDevice::Open()
{
        return ExecDevice::Open("keyboard.device");
}


status_t
KeyboardDevice::ReadEvent(struct InputEvent *event)
{
        fIOStdReq->io_Command = KBD_READEVENT;
        fIOStdReq->io_Flags = IOF_QUICK;
        fIOStdReq->io_Length = sizeof(struct InputEvent);
        fIOStdReq->io_Data = event;
        status_t err = Do();
        if (err < B_OK)
                return err;
        /*
        dprintf("key: class %d sclass %d code %d 0x%02x qual 0x%04x\n", 
                event->ie_Class, event->ie_SubClass,
                event->ie_Code, event->ie_Code, event->ie_Qualifier);
        */
        return B_OK;
}


ssize_t
KeyboardDevice::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
{
        struct InputEvent event;
        ssize_t actual;
        status_t err;
        
        do {
                err = ReadEvent(&event);
                if (err < B_OK)
                        return err;
        } while (event.ie_Code > IECODE_UP_PREFIX);

        actual = MapRawKey(&event, (char *)buffer, bufferSize, NULL);
        //dprintf("%s actual %d\n", __FUNCTION__, actual);
        if (actual > 0) {
                return actual;
        }
        return B_ERROR;
}


int
KeyboardDevice::WaitForKey()
{
        struct InputEvent event;
        char ascii;
        ssize_t actual;
        status_t err;
        
        do {
                err = ReadEvent(&event);
                if (err < B_OK)
                        return err;
        } while (event.ie_Code < IECODE_UP_PREFIX);

        event.ie_Code &= ~IECODE_UP_PREFIX;

        switch (event.ie_Code) {
                case IECODE_KEY_UP:
                        return TEXT_CONSOLE_KEY_UP;
                case IECODE_KEY_DOWN:
                        return TEXT_CONSOLE_KEY_DOWN;
                case IECODE_KEY_LEFT:
                        return TEXT_CONSOLE_KEY_LEFT;
                case IECODE_KEY_RIGHT:
                        return TEXT_CONSOLE_KEY_RIGHT;
                case IECODE_KEY_PAGE_UP:
                        return TEXT_CONSOLE_KEY_PAGE_UP;
                case IECODE_KEY_PAGE_DOWN:
                        return TEXT_CONSOLE_KEY_PAGE_DOWN;
                default:
                        break;
        }
        
        actual = MapRawKey(&event, &ascii, 1, NULL);
        //dprintf("%s actual %d\n", __FUNCTION__, actual);
        if (actual > 0)
                return ascii;
        return TEXT_CONSOLE_NO_KEY;
}


ssize_t
KeyboardDevice::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
{
        return B_ERROR;
}


// #pragma mark -


LLKeyboardDevice::LLKeyboardDevice()
        : CharHandle()
{
}


LLKeyboardDevice::~LLKeyboardDevice()
{
}


status_t
LLKeyboardDevice::Open()
{
        if (LowLevelBase == NULL)
                LowLevelBase = (Library *)OldOpenLibrary(LOWLEVELNAME);
        return (LowLevelBase == NULL) ? B_ERROR : B_OK;
}


ssize_t
LLKeyboardDevice::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
{
        struct InputEvent event;
        ssize_t actual;
        status_t err;
        uint32 key;
        
        
        key = GetKey();
        if (key & 0x0000ffff == 0x0ff)
                return B_ERROR;
        event.ie_Class = IECLASS_RAWKEY;
        event.ie_SubClass = IESUBCLASS_RAWKEY;
        event.ie_Code = (uint16)(key & 0x0000ffff);
        event.ie_Qualifier = (key & 0xffff0000) >> 16;

        actual = MapRawKey(&event, (char *)buffer, bufferSize, NULL);
        //dprintf("%s actual %d\n", __FUNCTION__, actual);
        if (actual > 0) {
                return actual;
        }
        return B_ERROR;
}


int
LLKeyboardDevice::WaitForKey()
{
        struct InputEvent event;
        uint32 key;
        char ascii;
        ssize_t actual;
        status_t err;
        
        do {
                key = GetKey();
        } while (key & 0x0000ffff == 0x0ff);

        event.ie_Class = IECLASS_RAWKEY;
        event.ie_SubClass = IESUBCLASS_RAWKEY;
        event.ie_Code = (uint16)(key & 0x0000ffff);
        event.ie_Qualifier = (key & 0xffff0000) >> 16;

        switch (event.ie_Code) {
                case IECODE_KEY_UP:
                        return TEXT_CONSOLE_KEY_UP;
                case IECODE_KEY_DOWN:
                        return TEXT_CONSOLE_KEY_DOWN;
                case IECODE_KEY_LEFT:
                        return TEXT_CONSOLE_KEY_LEFT;
                case IECODE_KEY_RIGHT:
                        return TEXT_CONSOLE_KEY_RIGHT;
                case IECODE_KEY_PAGE_UP:
                        return TEXT_CONSOLE_KEY_PAGE_UP;
                case IECODE_KEY_PAGE_DOWN:
                        return TEXT_CONSOLE_KEY_PAGE_DOWN;
                default:
                        break;
        }
        
        actual = MapRawKey(&event, &ascii, 1, NULL);
        //dprintf("%s actual %d\n", __FUNCTION__, actual);
        if (actual > 0)
                return ascii;
        return TEXT_CONSOLE_NO_KEY;
}


ssize_t
LLKeyboardDevice::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
{
        return B_ERROR;
}


//      #pragma mark -


static ConsoleDevice sOutput(NULL);
//static ConsoleHandle sOutput;
//static ConsoleHandle sErrorOutput;
//static ConsoleHandle sDebugOutput;
static ConsoleDevice sDebugOutput("Debug");

//static KeyboardDevice sInput;
static ConsoleDevice &sInput = sOutput;


status_t
console_init(void)
{
        status_t err;
        
        GRAPHICS_BASE_NAME = (GfxBase *)OldOpenLibrary(GRAPHICSNAME);
        if (GRAPHICS_BASE_NAME == NULL)
                panic("Cannot open %s", GRAPHICSNAME);
        
        static NewScreen newScreen = {
                0, 0,
                640, -1,
                4,
                BLACK, WHITE,
                0x8000,
                0x11,
                NULL,
                "Haiku Loader",
                NULL,
                NULL
        };
        
        gScreen = OpenScreen(&newScreen);
        if (gScreen == NULL)
                panic("OpenScreen()\n");
        
        LoadRGB4(&gScreen->ViewPort, kPalette, 16);
        
        SetDrMd(&gScreen->RastPort, JAM2);
        
        // seems not necessary, there is a default font already set.
        /*
        TextAttr attrs = { "Topaz", 8, 0, 0};
        TextFont *font = OpenFont(&attrs);
        */
        TextFont *font = OpenFont(gScreen->Font);
        if (font == NULL)
                panic("OpenFont()\n");
        sFontHeight = gScreen->Font->ta_YSize;
        sFontWidth = font->tf_XSize;
        
        sScreenTopOffset = gScreen->BarHeight * 2; // ???

        
        //ClearScreen(&gScreen->RastPort);

        
        err = sDebugOutput.Open();
        if (err < B_OK)
                panic("sDebugOutput.Open() 0x%08lx\n", err);
        dbgerr = stderr = (FILE *)&sDebugOutput;


        err = sOutput.Open();
        if (err < B_OK)
                panic("sOutput.Open() 0x%08lx\n", err);

        stdout = (FILE *)&sOutput;

        console_set_cursor(0, 0);
        
        /*
        dprintf("LeftEdge %d\n", gScreen->LeftEdge);
        dprintf("TopEdge %d\n", gScreen->TopEdge);
        dprintf("Width %d\n", gScreen->Width);
        dprintf("Height %d\n", gScreen->Height);
        dprintf("MouseX %d\n", gScreen->MouseX);
        dprintf("MouseY %d\n", gScreen->MouseY);
        dprintf("Flags 0x%08x\n", gScreen->Flags);
        dprintf("BarHeight %d\n", gScreen->BarHeight);
        dprintf("BarVBorder %d\n", gScreen->BarVBorder);
        dprintf("BarHBorder %d\n", gScreen->BarHBorder);
        dprintf("MenuVBorder %d\n", gScreen->MenuVBorder);
        dprintf("MenuHBorder %d\n", gScreen->MenuHBorder);
        dprintf("WBorTop %d\n", gScreen->WBorTop);
        dprintf("WBorLeft %d\n", gScreen->WBorLeft);
        dprintf("WBorRight %d\n", gScreen->WBorRight);
        dprintf("WBorBottom %d\n", gScreen->WBorBottom);
        */

        KEYMAP_BASE_NAME = (Library *)OldOpenLibrary(KEYMAPNAME);
        if (KEYMAP_BASE_NAME == NULL)
                panic("Cannot open %s", KEYMAPNAME);

        
        /*
        err = sInput.Open();
        if (err < B_OK)
                panic("sInput.Open() 0x%08lx\n", err);
        */
        stdin = (FILE *)&sInput;
        
        return B_OK;
}


// #pragma mark -


void
console_clear_screen(void)
{
        sOutput.Clear();
}


int32
console_width(void)
{
        return sOutput.Columns();
}


int32
console_height(void)
{
        return sOutput.Rows();
}


void
console_set_cursor(int32 x, int32 y)
{
        sOutput.MoveTo(x, y);
}


void
console_set_color(int32 foreground, int32 background)
{
        sOutput.SetColor(foreground, background);
}


void
console_show_cursor(void)
{
}


void
console_hide_cursor(void)
{
}


int
console_wait_for_key(void)
{
        int key = sInput.WaitForKey();
        //dprintf("k: %08x '%c'\n", key, key);
        return key;
}