Compare commits
13 Commits
bdbde25784
...
7c6966a9c4
Author | SHA1 | Date |
---|---|---|
|
7c6966a9c4 | |
|
40d1d20cd6 | |
|
c0942d78cb | |
|
cef8779bf7 | |
|
d6667844de | |
|
4cdf218145 | |
|
994713d04c | |
|
554b13ac50 | |
|
439fb57d88 | |
|
4409d0f03f | |
|
ebd00b1eb2 | |
|
3ca0ef1583 | |
|
88abbd90dc |
|
@ -89,6 +89,8 @@ set(KERNEL_SOURCES
|
||||||
kernel/Terminal/FramebufferTerminal.cpp
|
kernel/Terminal/FramebufferTerminal.cpp
|
||||||
kernel/Terminal/PseudoTerminal.cpp
|
kernel/Terminal/PseudoTerminal.cpp
|
||||||
kernel/Terminal/Serial.cpp
|
kernel/Terminal/Serial.cpp
|
||||||
|
kernel/Terminal/TerminalDriver.cpp
|
||||||
|
kernel/Terminal/TextModeTerminal.cpp
|
||||||
kernel/Terminal/TTY.cpp
|
kernel/Terminal/TTY.cpp
|
||||||
kernel/Terminal/VirtualTTY.cpp
|
kernel/Terminal/VirtualTTY.cpp
|
||||||
kernel/Thread.cpp
|
kernel/Thread.cpp
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define BANAN_BOOTLOADER_MAGIC 0xD3C60CFF
|
#define BANAN_BOOTLOADER_MAGIC 0xD3C60CFF
|
||||||
#define BANAN_BOOTLOADER_FB_RGB 1
|
#define BANAN_BOOTLOADER_FB_RGB 1
|
||||||
|
#define BANAN_BOOTLOADER_FB_TEXT 2
|
||||||
|
|
||||||
struct BananBootFramebufferInfo
|
struct BananBootFramebufferInfo
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace Kernel
|
||||||
None,
|
None,
|
||||||
Unknown,
|
Unknown,
|
||||||
RGB,
|
RGB,
|
||||||
|
Text,
|
||||||
};
|
};
|
||||||
|
|
||||||
paddr_t address;
|
paddr_t address;
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace Kernel
|
||||||
uint32_t width() const { return m_width; }
|
uint32_t width() const { return m_width; }
|
||||||
uint32_t height() const { return m_height; }
|
uint32_t height() const { return m_height; }
|
||||||
|
|
||||||
|
uint32_t get_pixel(uint32_t x, uint32_t y) const;
|
||||||
void set_pixel(uint32_t x, uint32_t y, uint32_t rgb);
|
void set_pixel(uint32_t x, uint32_t y, uint32_t rgb);
|
||||||
|
|
||||||
// positive rows -> empty pixels on bottom
|
// positive rows -> empty pixels on bottom
|
||||||
|
|
|
@ -9,25 +9,40 @@ namespace Kernel
|
||||||
class FramebufferTerminalDriver final : public TerminalDriver
|
class FramebufferTerminalDriver final : public TerminalDriver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static FramebufferTerminalDriver* create(BAN::RefPtr<FramebufferDevice>);
|
static BAN::ErrorOr<BAN::RefPtr<FramebufferTerminalDriver>> create(BAN::RefPtr<FramebufferDevice>);
|
||||||
|
|
||||||
virtual uint32_t width() const override { return m_framebuffer_device->width() / font().width(); }
|
uint32_t width() const override { return m_framebuffer_device->width() / m_font.width(); }
|
||||||
virtual uint32_t height() const override { return m_framebuffer_device->height() / font().height(); }
|
uint32_t height() const override { return m_framebuffer_device->height() / m_font.height(); }
|
||||||
|
|
||||||
virtual void putchar_at(uint16_t, uint32_t, uint32_t, Color, Color) override;
|
void putchar_at(uint16_t, uint32_t, uint32_t, Color, Color) override;
|
||||||
virtual bool scroll(Color) override;
|
bool scroll(Color) override;
|
||||||
virtual void clear(Color) override;
|
void clear(Color) override;
|
||||||
|
|
||||||
virtual void set_cursor_position(uint32_t, uint32_t) override;
|
void set_cursor_shown(bool) override;
|
||||||
|
void set_cursor_position(uint32_t, uint32_t) override;
|
||||||
|
|
||||||
|
bool has_font() const override { return true; }
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> set_font(LibFont::Font&& font) override;
|
||||||
|
const LibFont::Font& font() const override { return m_font; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FramebufferTerminalDriver(BAN::RefPtr<FramebufferDevice> framebuffer_device)
|
FramebufferTerminalDriver(BAN::RefPtr<FramebufferDevice> framebuffer_device)
|
||||||
: m_framebuffer_device(framebuffer_device)
|
: m_framebuffer_device(framebuffer_device)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
void read_cursor();
|
||||||
|
void show_cursor(bool use_data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BAN::RefPtr<FramebufferDevice> m_framebuffer_device;
|
BAN::RefPtr<FramebufferDevice> m_framebuffer_device;
|
||||||
static constexpr Color s_cursor_color = TerminalColor::BRIGHT_WHITE;
|
LibFont::Font m_font;
|
||||||
|
|
||||||
|
uint32_t m_cursor_x { 0 };
|
||||||
|
uint32_t m_cursor_y { 0 };
|
||||||
|
bool m_cursor_shown { true };
|
||||||
|
BAN::Vector<uint32_t> m_cursor_data;
|
||||||
|
static constexpr Color m_cursor_color = TerminalColor::BRIGHT_WHITE;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Kernel
|
||||||
class TTY : public CharacterDevice
|
class TTY : public CharacterDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void set_font(const LibFont::Font&) {};
|
virtual BAN::ErrorOr<void> set_font(LibFont::Font&&) { return BAN::Error::from_errno(EINVAL); }
|
||||||
|
|
||||||
void set_foreground_pgrp(pid_t pgrp) { m_foreground_pgrp = pgrp; }
|
void set_foreground_pgrp(pid_t pgrp) { m_foreground_pgrp = pgrp; }
|
||||||
pid_t foreground_pgrp() const { return m_foreground_pgrp; }
|
pid_t foreground_pgrp() const { return m_foreground_pgrp; }
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <LibFont/Font.h>
|
#include <BAN/RefPtr.h>
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <LibFont/Font.h>
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
class TerminalDriver
|
class TerminalDriver : public BAN::RefCounted<TerminalDriver>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct Color
|
struct Color
|
||||||
|
@ -25,7 +25,7 @@ namespace Kernel
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TerminalDriver() : m_font(MUST(LibFont::Font::prefs())) {}
|
static BAN::ErrorOr<void> initialize_from_boot_info();
|
||||||
virtual ~TerminalDriver() {}
|
virtual ~TerminalDriver() {}
|
||||||
virtual uint32_t width() const = 0;
|
virtual uint32_t width() const = 0;
|
||||||
virtual uint32_t height() const = 0;
|
virtual uint32_t height() const = 0;
|
||||||
|
@ -34,15 +34,16 @@ namespace Kernel
|
||||||
virtual bool scroll(Color) { return false; }
|
virtual bool scroll(Color) { return false; }
|
||||||
virtual void clear(Color) = 0;
|
virtual void clear(Color) = 0;
|
||||||
|
|
||||||
|
virtual void set_cursor_shown(bool) = 0;
|
||||||
virtual void set_cursor_position(uint32_t, uint32_t) = 0;
|
virtual void set_cursor_position(uint32_t, uint32_t) = 0;
|
||||||
|
|
||||||
void set_font(const LibFont::Font& font) { m_font = font; };
|
virtual bool has_font() const { return false; }
|
||||||
const LibFont::Font& font() const { return m_font; }
|
virtual BAN::ErrorOr<void> set_font(LibFont::Font&&) { return BAN::Error::from_errno(EINVAL); }
|
||||||
|
virtual const LibFont::Font& font() const { ASSERT_NOT_REACHED(); }
|
||||||
private:
|
|
||||||
LibFont::Font m_font;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern BAN::RefPtr<TerminalDriver> g_terminal_driver;
|
||||||
|
|
||||||
namespace TerminalColor
|
namespace TerminalColor
|
||||||
{
|
{
|
||||||
static constexpr TerminalDriver::Color BLACK = 0x000000;
|
static constexpr TerminalDriver::Color BLACK = 0x000000;
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <kernel/Terminal/TerminalDriver.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
class TextModeTerminalDriver final : public TerminalDriver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<TextModeTerminalDriver>> create_from_boot_info();
|
||||||
|
~TextModeTerminalDriver();
|
||||||
|
|
||||||
|
uint32_t width() const override { return m_width; }
|
||||||
|
uint32_t height() const override { return m_height; }
|
||||||
|
|
||||||
|
void putchar_at(uint16_t, uint32_t, uint32_t, Color, Color) override;
|
||||||
|
void clear(Color) override;
|
||||||
|
|
||||||
|
void set_cursor_shown(bool) override;
|
||||||
|
void set_cursor_position(uint32_t, uint32_t) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TextModeTerminalDriver(paddr_t paddr, uint32_t width, uint32_t height, uint32_t pitch)
|
||||||
|
: m_paddr(paddr)
|
||||||
|
, m_width(width)
|
||||||
|
, m_height(height)
|
||||||
|
, m_pitch(pitch)
|
||||||
|
{}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> initialize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const paddr_t m_paddr;
|
||||||
|
const uint32_t m_width;
|
||||||
|
const uint32_t m_height;
|
||||||
|
const uint32_t m_pitch;
|
||||||
|
vaddr_t m_vaddr { 0 };
|
||||||
|
static constexpr Color s_cursor_color = TerminalColor::BRIGHT_WHITE;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -12,9 +12,9 @@ namespace Kernel
|
||||||
class VirtualTTY : public TTY
|
class VirtualTTY : public TTY
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<BAN::RefPtr<VirtualTTY>> create(TerminalDriver*);
|
static BAN::ErrorOr<BAN::RefPtr<VirtualTTY>> create(BAN::RefPtr<TerminalDriver>);
|
||||||
|
|
||||||
virtual void set_font(const LibFont::Font&) override;
|
virtual BAN::ErrorOr<void> set_font(LibFont::Font&&) override;
|
||||||
|
|
||||||
virtual uint32_t height() const override { return m_height; }
|
virtual uint32_t height() const override { return m_height; }
|
||||||
virtual uint32_t width() const override { return m_width; }
|
virtual uint32_t width() const override { return m_width; }
|
||||||
|
@ -26,14 +26,14 @@ namespace Kernel
|
||||||
virtual void putchar_impl(uint8_t ch) override;
|
virtual void putchar_impl(uint8_t ch) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VirtualTTY(TerminalDriver*);
|
VirtualTTY(BAN::RefPtr<TerminalDriver>);
|
||||||
|
|
||||||
void reset_ansi();
|
void reset_ansi();
|
||||||
void handle_ansi_csi(uint8_t ch);
|
void handle_ansi_csi(uint8_t ch);
|
||||||
void handle_ansi_csi_color(uint8_t ch);
|
void handle_ansi_csi_color(uint8_t ch);
|
||||||
|
void putcodepoint(uint32_t codepoint);
|
||||||
void putchar_at(uint32_t codepoint, uint32_t x, uint32_t y);
|
void putchar_at(uint32_t codepoint, uint32_t x, uint32_t y);
|
||||||
void render_from_buffer(uint32_t x, uint32_t y);
|
void render_from_buffer(uint32_t x, uint32_t y);
|
||||||
void set_cursor_position(uint32_t x, uint32_t y);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class State
|
enum class State
|
||||||
|
@ -81,9 +81,8 @@ namespace Kernel
|
||||||
uint32_t m_row { 0 };
|
uint32_t m_row { 0 };
|
||||||
uint32_t m_column { 0 };
|
uint32_t m_column { 0 };
|
||||||
Cell* m_buffer { nullptr };
|
Cell* m_buffer { nullptr };
|
||||||
bool m_show_cursor { true };
|
|
||||||
|
|
||||||
TerminalDriver* m_terminal_driver { nullptr };
|
BAN::RefPtr<TerminalDriver> m_terminal_driver;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
#define MULTIBOOT2_TAG_OLD_RSDP 14
|
#define MULTIBOOT2_TAG_OLD_RSDP 14
|
||||||
#define MULTIBOOT2_TAG_NEW_RSDP 15
|
#define MULTIBOOT2_TAG_NEW_RSDP 15
|
||||||
|
|
||||||
#define MULTIBOOT2_FRAMEBUFFER_TYPE_RGB 1
|
#define MULTIBOOT2_FRAMEBUFFER_TYPE_RGB 1
|
||||||
|
#define MULTIBOOT2_FRAMEBUFFER_TYPE_TEXT 2
|
||||||
|
|
||||||
#define MULTIBOOT2_MAGIC 0x36d76289
|
#define MULTIBOOT2_MAGIC 0x36d76289
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,8 @@ namespace Kernel
|
||||||
g_boot_info.framebuffer.bpp = framebuffer_tag.framebuffer_bpp;
|
g_boot_info.framebuffer.bpp = framebuffer_tag.framebuffer_bpp;
|
||||||
if (framebuffer_tag.framebuffer_type == MULTIBOOT2_FRAMEBUFFER_TYPE_RGB)
|
if (framebuffer_tag.framebuffer_type == MULTIBOOT2_FRAMEBUFFER_TYPE_RGB)
|
||||||
g_boot_info.framebuffer.type = FramebufferInfo::Type::RGB;
|
g_boot_info.framebuffer.type = FramebufferInfo::Type::RGB;
|
||||||
|
else if (framebuffer_tag.framebuffer_type == MULTIBOOT2_FRAMEBUFFER_TYPE_TEXT)
|
||||||
|
g_boot_info.framebuffer.type = FramebufferInfo::Type::Text;
|
||||||
else
|
else
|
||||||
g_boot_info.framebuffer.type = FramebufferInfo::Type::Unknown;
|
g_boot_info.framebuffer.type = FramebufferInfo::Type::Unknown;
|
||||||
}
|
}
|
||||||
|
@ -107,6 +109,8 @@ namespace Kernel
|
||||||
g_boot_info.framebuffer.bpp = framebuffer.bpp;
|
g_boot_info.framebuffer.bpp = framebuffer.bpp;
|
||||||
if (framebuffer.type == BANAN_BOOTLOADER_FB_RGB)
|
if (framebuffer.type == BANAN_BOOTLOADER_FB_RGB)
|
||||||
g_boot_info.framebuffer.type = FramebufferInfo::Type::RGB;
|
g_boot_info.framebuffer.type = FramebufferInfo::Type::RGB;
|
||||||
|
else if (framebuffer.type == BANAN_BOOTLOADER_FB_TEXT)
|
||||||
|
g_boot_info.framebuffer.type = FramebufferInfo::Type::Text;
|
||||||
else
|
else
|
||||||
g_boot_info.framebuffer.type = FramebufferInfo::Type::Unknown;
|
g_boot_info.framebuffer.type = FramebufferInfo::Type::Unknown;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
bool g_disable_debug = false;
|
bool g_disable_debug = false;
|
||||||
extern Kernel::TerminalDriver* g_terminal_driver;
|
|
||||||
|
|
||||||
namespace Debug
|
namespace Debug
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,14 +3,12 @@
|
||||||
#include <kernel/Device/FramebufferDevice.h>
|
#include <kernel/Device/FramebufferDevice.h>
|
||||||
#include <kernel/FS/DevFS/FileSystem.h>
|
#include <kernel/FS/DevFS/FileSystem.h>
|
||||||
#include <kernel/Memory/Heap.h>
|
#include <kernel/Memory/Heap.h>
|
||||||
#include <kernel/Terminal/TerminalDriver.h>
|
#include <kernel/Terminal/FramebufferTerminal.h>
|
||||||
|
|
||||||
#include <sys/framebuffer.h>
|
#include <sys/framebuffer.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/sysmacros.h>
|
#include <sys/sysmacros.h>
|
||||||
|
|
||||||
extern Kernel::TerminalDriver* g_terminal_driver;
|
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -22,8 +20,7 @@ namespace Kernel
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::RefPtr<FramebufferDevice>> FramebufferDevice::create_from_boot_framebuffer()
|
BAN::ErrorOr<BAN::RefPtr<FramebufferDevice>> FramebufferDevice::create_from_boot_framebuffer()
|
||||||
{
|
{
|
||||||
if (g_boot_info.framebuffer.type != FramebufferInfo::Type::RGB)
|
ASSERT(g_boot_info.framebuffer.type == FramebufferInfo::Type::RGB);
|
||||||
return BAN::Error::from_errno(ENODEV);
|
|
||||||
if (g_boot_info.framebuffer.bpp != 24 && g_boot_info.framebuffer.bpp != 32)
|
if (g_boot_info.framebuffer.bpp != 24 && g_boot_info.framebuffer.bpp != 32)
|
||||||
return BAN::Error::from_errno(ENOTSUP);
|
return BAN::Error::from_errno(ENOTSUP);
|
||||||
auto* device_ptr = new FramebufferDevice(
|
auto* device_ptr = new FramebufferDevice(
|
||||||
|
@ -39,6 +36,7 @@ namespace Kernel
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
auto device = BAN::RefPtr<FramebufferDevice>::adopt(device_ptr);
|
auto device = BAN::RefPtr<FramebufferDevice>::adopt(device_ptr);
|
||||||
TRY(device->initialize());
|
TRY(device->initialize());
|
||||||
|
DevFileSystem::get().add_device(device);
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +125,15 @@ namespace Kernel
|
||||||
return bytes_to_copy;
|
return bytes_to_copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t FramebufferDevice::get_pixel(uint32_t x, uint32_t y) const
|
||||||
|
{
|
||||||
|
ASSERT(x < m_width && y < m_height);
|
||||||
|
const auto* video_buffer_u8 = reinterpret_cast<const uint8_t*>(m_video_buffer->vaddr());
|
||||||
|
return (video_buffer_u8[(y * m_width + x) * (BANAN_FB_BPP / 8) + 0] << 0)
|
||||||
|
| (video_buffer_u8[(y * m_width + x) * (BANAN_FB_BPP / 8) + 1] << 8)
|
||||||
|
| (video_buffer_u8[(y * m_width + x) * (BANAN_FB_BPP / 8) + 2] << 16);
|
||||||
|
}
|
||||||
|
|
||||||
void FramebufferDevice::set_pixel(uint32_t x, uint32_t y, uint32_t rgb)
|
void FramebufferDevice::set_pixel(uint32_t x, uint32_t y, uint32_t rgb)
|
||||||
{
|
{
|
||||||
if (x >= m_width || y >= m_height)
|
if (x >= m_width || y >= m_height)
|
||||||
|
@ -315,6 +322,8 @@ namespace Kernel
|
||||||
|
|
||||||
const uint32_t fb_width = m_framebuffer->width();
|
const uint32_t fb_width = m_framebuffer->width();
|
||||||
|
|
||||||
|
// If we are here (in FramebufferMemoryRegion), our terminal driver is FramebufferTerminalDriver
|
||||||
|
ASSERT(g_terminal_driver->has_font());
|
||||||
const auto& font = g_terminal_driver->font();
|
const auto& font = g_terminal_driver->font();
|
||||||
|
|
||||||
const uint32_t x = first_pixel % fb_width;
|
const uint32_t x = first_pixel % fb_width;
|
||||||
|
|
|
@ -210,7 +210,7 @@ namespace Kernel
|
||||||
return m_open_files[fd].status_flags();
|
return m_open_files[fd].status_flags();
|
||||||
case F_SETFL:
|
case F_SETFL:
|
||||||
extra &= O_APPEND | O_DSYNC | O_NONBLOCK | O_RSYNC | O_SYNC;
|
extra &= O_APPEND | O_DSYNC | O_NONBLOCK | O_RSYNC | O_SYNC;
|
||||||
m_open_files[fd].status_flags() &= ~O_ACCMODE;
|
m_open_files[fd].status_flags() &= O_ACCMODE;
|
||||||
m_open_files[fd].status_flags() |= extra;
|
m_open_files[fd].status_flags() |= extra;
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -445,6 +445,9 @@ namespace Kernel
|
||||||
{
|
{
|
||||||
ASSERT(m_process_lock.is_locked());
|
ASSERT(m_process_lock.is_locked());
|
||||||
|
|
||||||
|
if (path[0] == '\0')
|
||||||
|
return BAN::Error::from_errno(ENOENT);
|
||||||
|
|
||||||
auto relative_parent = TRY(find_relative_parent(fd, path));
|
auto relative_parent = TRY(find_relative_parent(fd, path));
|
||||||
|
|
||||||
VirtualFileSystem::File parent;
|
VirtualFileSystem::File parent;
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
#include <kernel/Thread.h>
|
#include <kernel/Thread.h>
|
||||||
#include <kernel/Timer/Timer.h>
|
#include <kernel/Timer/Timer.h>
|
||||||
|
|
||||||
extern Kernel::TerminalDriver* g_terminal_driver;
|
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
FramebufferTerminalDriver* FramebufferTerminalDriver::create(BAN::RefPtr<FramebufferDevice> framebuffer_device)
|
BAN::ErrorOr<BAN::RefPtr<FramebufferTerminalDriver>> FramebufferTerminalDriver::create(BAN::RefPtr<FramebufferDevice> framebuffer_device)
|
||||||
{
|
{
|
||||||
auto* driver = new FramebufferTerminalDriver(framebuffer_device);
|
auto* driver_ptr = new FramebufferTerminalDriver(framebuffer_device);
|
||||||
if (driver == nullptr)
|
if (driver_ptr == nullptr)
|
||||||
return nullptr;
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
auto driver = BAN::RefPtr<FramebufferTerminalDriver>::adopt(driver_ptr);
|
||||||
|
TRY(driver->set_font(BAN::move(TRY(LibFont::Font::prefs()))));
|
||||||
driver->set_cursor_position(0, 0);
|
driver->set_cursor_position(0, 0);
|
||||||
driver->clear(TerminalColor::BLACK);
|
driver->clear(TerminalColor::BLACK);
|
||||||
return driver;
|
return driver;
|
||||||
|
@ -15,51 +17,129 @@ namespace Kernel
|
||||||
|
|
||||||
void FramebufferTerminalDriver::putchar_at(uint16_t ch, uint32_t x, uint32_t y, Color fg, Color bg)
|
void FramebufferTerminalDriver::putchar_at(uint16_t ch, uint32_t x, uint32_t y, Color fg, Color bg)
|
||||||
{
|
{
|
||||||
const uint8_t* glyph = font().has_glyph(ch) ? font().glyph(ch) : font().glyph('?');
|
const uint8_t* glyph = m_font.has_glyph(ch) ? m_font.glyph(ch) : m_font.glyph('?');
|
||||||
|
|
||||||
x *= font().width();
|
x *= m_font.width();
|
||||||
y *= font().height();
|
y *= m_font.height();
|
||||||
|
|
||||||
for (uint32_t dy = 0; dy < font().height() && y + dy < m_framebuffer_device->height(); dy++)
|
for (uint32_t dy = 0; dy < m_font.height() && y + dy < m_framebuffer_device->height(); dy++)
|
||||||
{
|
{
|
||||||
for (uint32_t dx = 0; dx < font().width() && x + dx < m_framebuffer_device->width(); dx++)
|
for (uint32_t dx = 0; dx < m_font.width() && x + dx < m_framebuffer_device->width(); dx++)
|
||||||
{
|
{
|
||||||
const uint8_t bitmask = 1 << (font().width() - dx - 1);
|
const uint8_t bitmask = 1 << (m_font.width() - dx - 1);
|
||||||
const auto color = glyph[dy * font().pitch()] & bitmask ? fg : bg;
|
const auto color = glyph[dy * m_font.pitch()] & bitmask ? fg : bg;
|
||||||
m_framebuffer_device->set_pixel(x + dx, y + dy, color.rgb);
|
m_framebuffer_device->set_pixel(x + dx, y + dy, color.rgb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_framebuffer_device->sync_pixels_rectangle(x, y, font().width(), font().height());
|
m_framebuffer_device->sync_pixels_rectangle(x, y, m_font.width(), m_font.height());
|
||||||
|
|
||||||
|
if (x == m_cursor_x && y == m_cursor_y)
|
||||||
|
{
|
||||||
|
read_cursor();
|
||||||
|
show_cursor(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FramebufferTerminalDriver::scroll(Color color)
|
bool FramebufferTerminalDriver::scroll(Color color)
|
||||||
{
|
{
|
||||||
m_framebuffer_device->scroll(font().height(), color.rgb);
|
m_framebuffer_device->scroll(m_font.height(), color.rgb);
|
||||||
m_framebuffer_device->sync_pixels_full();
|
m_framebuffer_device->sync_pixels_full();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramebufferTerminalDriver::clear(Color color)
|
void FramebufferTerminalDriver::clear(Color color)
|
||||||
{
|
{
|
||||||
|
for (auto& pixel : m_cursor_data)
|
||||||
|
pixel = color.rgb;
|
||||||
|
|
||||||
for (uint32_t y = 0; y < m_framebuffer_device->height(); y++)
|
for (uint32_t y = 0; y < m_framebuffer_device->height(); y++)
|
||||||
for (uint32_t x = 0; x < m_framebuffer_device->width(); x++)
|
for (uint32_t x = 0; x < m_framebuffer_device->width(); x++)
|
||||||
m_framebuffer_device->set_pixel(x, y, color.rgb);
|
m_framebuffer_device->set_pixel(x, y, color.rgb);
|
||||||
m_framebuffer_device->sync_pixels_full();
|
m_framebuffer_device->sync_pixels_full();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramebufferTerminalDriver::set_cursor_position(uint32_t x, uint32_t y)
|
void FramebufferTerminalDriver::read_cursor()
|
||||||
{
|
{
|
||||||
const uint32_t cursor_h = font().height() / 8;
|
const uint32_t cursor_h = m_font.height() / 8;
|
||||||
const uint32_t cursor_top = font().height() * 13 / 16;
|
const uint32_t cursor_top = m_font.height() * 13 / 16;
|
||||||
|
|
||||||
x *= font().width();
|
const uint32_t x = m_cursor_x * m_font.width();
|
||||||
y *= font().height();
|
const uint32_t y = m_cursor_y * m_font.height();
|
||||||
|
|
||||||
for (uint32_t dy = 0; dy < cursor_h; dy++)
|
for (uint32_t dy = 0; dy < cursor_h; dy++)
|
||||||
for (uint32_t dx = 0; dx < font().width(); dx++)
|
for (uint32_t dx = 0; dx < m_font.width(); dx++)
|
||||||
m_framebuffer_device->set_pixel(x + dx, y + cursor_top + dy, s_cursor_color.rgb);
|
m_cursor_data[dy * m_font.width() + dx] = m_framebuffer_device->get_pixel(x + dx, y + cursor_top + dy);
|
||||||
m_framebuffer_device->sync_pixels_rectangle(x, y + cursor_top, font().width(), cursor_h);
|
}
|
||||||
|
|
||||||
|
void FramebufferTerminalDriver::show_cursor(bool use_data)
|
||||||
|
{
|
||||||
|
const auto get_color =
|
||||||
|
[this, use_data](uint32_t x, uint32_t y) -> uint32_t
|
||||||
|
{
|
||||||
|
if (!use_data)
|
||||||
|
return m_cursor_color.rgb;
|
||||||
|
return m_cursor_data[y * m_font.width() + x];
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32_t cursor_h = m_font.height() / 8;
|
||||||
|
const uint32_t cursor_w = m_font.width();
|
||||||
|
const uint32_t cursor_top = m_font.height() * 13 / 16;
|
||||||
|
|
||||||
|
const uint32_t x = m_cursor_x * m_font.width();
|
||||||
|
const uint32_t y = m_cursor_y * m_font.height();
|
||||||
|
|
||||||
|
for (uint32_t dy = 0; dy < cursor_h; dy++)
|
||||||
|
for (uint32_t dx = 0; dx < cursor_w; dx++)
|
||||||
|
m_framebuffer_device->set_pixel(x + dx, y + cursor_top + dy, get_color(dx, dy));
|
||||||
|
m_framebuffer_device->sync_pixels_rectangle(x, y + cursor_top, cursor_w, cursor_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramebufferTerminalDriver::set_cursor_shown(bool shown)
|
||||||
|
{
|
||||||
|
if (m_cursor_shown == shown)
|
||||||
|
return;
|
||||||
|
m_cursor_shown = shown;
|
||||||
|
|
||||||
|
if (m_cursor_shown)
|
||||||
|
{
|
||||||
|
read_cursor();
|
||||||
|
show_cursor(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
show_cursor(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramebufferTerminalDriver::set_cursor_position(uint32_t x, uint32_t y)
|
||||||
|
{
|
||||||
|
if (!m_cursor_shown)
|
||||||
|
{
|
||||||
|
m_cursor_x = x;
|
||||||
|
m_cursor_y = y;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
show_cursor(true);
|
||||||
|
m_cursor_x = x;
|
||||||
|
m_cursor_y = y;
|
||||||
|
read_cursor();
|
||||||
|
show_cursor(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> FramebufferTerminalDriver::set_font(LibFont::Font&& font)
|
||||||
|
{
|
||||||
|
const uint32_t cursor_h = font.height() / 8;
|
||||||
|
const uint32_t cursor_w = font.width();
|
||||||
|
TRY(m_cursor_data.resize(cursor_h * cursor_w));
|
||||||
|
for (auto& val : m_cursor_data)
|
||||||
|
val = TerminalColor::BLACK.rgb;
|
||||||
|
|
||||||
|
m_font = BAN::move(font);
|
||||||
|
m_cursor_x = BAN::Math::clamp<uint32_t>(m_cursor_x, 0, width() - 1);
|
||||||
|
m_cursor_y = BAN::Math::clamp<uint32_t>(m_cursor_y, 0, height() - 1);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,7 +188,7 @@ namespace Kernel
|
||||||
{
|
{
|
||||||
auto absolute_path = TRY(Process::current().absolute_path_of(BAN::StringView(reinterpret_cast<const char*>(argument))));
|
auto absolute_path = TRY(Process::current().absolute_path_of(BAN::StringView(reinterpret_cast<const char*>(argument))));
|
||||||
auto new_font = TRY(LibFont::Font::load(absolute_path));
|
auto new_font = TRY(LibFont::Font::load(absolute_path));
|
||||||
set_font(new_font);
|
TRY(set_font(BAN::move(new_font)));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case TIOCGWINSZ:
|
case TIOCGWINSZ:
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include <kernel/BootInfo.h>
|
||||||
|
#include <kernel/Terminal/FramebufferTerminal.h>
|
||||||
|
#include <kernel/Terminal/TextModeTerminal.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::RefPtr<TerminalDriver> g_terminal_driver;
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> TerminalDriver::initialize_from_boot_info()
|
||||||
|
{
|
||||||
|
switch (g_boot_info.framebuffer.type)
|
||||||
|
{
|
||||||
|
case FramebufferInfo::Type::None:
|
||||||
|
case FramebufferInfo::Type::Unknown:
|
||||||
|
return BAN::Error::from_errno(ENODEV);
|
||||||
|
case FramebufferInfo::Type::RGB:
|
||||||
|
g_terminal_driver = TRY(FramebufferTerminalDriver::create(
|
||||||
|
TRY(FramebufferDevice::create_from_boot_framebuffer())
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case FramebufferInfo::Type::Text:
|
||||||
|
g_terminal_driver = TRY(TextModeTerminalDriver::create_from_boot_info());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
#include <kernel/BootInfo.h>
|
||||||
|
#include <kernel/IO.h>
|
||||||
|
#include <kernel/Memory/PageTable.h>
|
||||||
|
#include <kernel/MMIO.h>
|
||||||
|
#include <kernel/Terminal/TextModeTerminal.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
static constexpr TerminalDriver::Color s_palette[] {
|
||||||
|
TerminalColor::BLACK,
|
||||||
|
TerminalColor::BLUE,
|
||||||
|
TerminalColor::GREEN,
|
||||||
|
TerminalColor::CYAN,
|
||||||
|
TerminalColor::RED,
|
||||||
|
TerminalColor::MAGENTA,
|
||||||
|
TerminalColor::YELLOW,
|
||||||
|
TerminalColor::WHITE,
|
||||||
|
TerminalColor::BRIGHT_BLACK,
|
||||||
|
TerminalColor::BRIGHT_BLUE,
|
||||||
|
TerminalColor::BRIGHT_GREEN,
|
||||||
|
TerminalColor::BRIGHT_CYAN,
|
||||||
|
TerminalColor::BRIGHT_RED,
|
||||||
|
TerminalColor::BRIGHT_MAGENTA,
|
||||||
|
TerminalColor::BRIGHT_YELLOW,
|
||||||
|
TerminalColor::BRIGHT_WHITE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr uint8_t color_to_text_mode_color(TerminalDriver::Color color)
|
||||||
|
{
|
||||||
|
uint32_t min_diff = BAN::numeric_limits<uint32_t>::max();
|
||||||
|
uint8_t closest = 0;
|
||||||
|
|
||||||
|
static_assert(sizeof(s_palette) / sizeof(*s_palette) == 16);
|
||||||
|
for (size_t i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
const auto rdiff = color.red() - s_palette[i].red();
|
||||||
|
const auto gdiff = color.green() - s_palette[i].green();
|
||||||
|
const auto bdiff = color.blue() - s_palette[i].blue();
|
||||||
|
const uint32_t diff = rdiff*rdiff + gdiff*gdiff + bdiff*bdiff;
|
||||||
|
if (diff >= min_diff)
|
||||||
|
continue;
|
||||||
|
min_diff = diff;
|
||||||
|
closest = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return closest;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<TextModeTerminalDriver>> TextModeTerminalDriver::create_from_boot_info()
|
||||||
|
{
|
||||||
|
ASSERT(g_boot_info.framebuffer.type == FramebufferInfo::Type::Text);
|
||||||
|
if (g_boot_info.framebuffer.bpp != 16)
|
||||||
|
return BAN::Error::from_errno(ENOTSUP);
|
||||||
|
auto* driver_ptr = new TextModeTerminalDriver(
|
||||||
|
g_boot_info.framebuffer.address,
|
||||||
|
g_boot_info.framebuffer.width,
|
||||||
|
g_boot_info.framebuffer.height,
|
||||||
|
g_boot_info.framebuffer.pitch
|
||||||
|
);
|
||||||
|
if (driver_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
auto driver = BAN::RefPtr<TextModeTerminalDriver>::adopt(driver_ptr);
|
||||||
|
TRY(driver->initialize());
|
||||||
|
return driver;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> TextModeTerminalDriver::initialize()
|
||||||
|
{
|
||||||
|
const size_t page_count = range_page_count(m_paddr, m_height * m_pitch);
|
||||||
|
const vaddr_t vaddr = PageTable::kernel().reserve_free_contiguous_pages(page_count, KERNEL_OFFSET);
|
||||||
|
if (vaddr == 0)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
|
||||||
|
PageTable::kernel().map_range_at(
|
||||||
|
m_paddr & PAGE_ADDR_MASK,
|
||||||
|
vaddr,
|
||||||
|
page_count * PAGE_SIZE,
|
||||||
|
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||||
|
PageTable::MemoryType::WriteCombining
|
||||||
|
);
|
||||||
|
|
||||||
|
m_vaddr = vaddr + (m_paddr % PAGE_SIZE);
|
||||||
|
|
||||||
|
set_cursor_position(0, 0);
|
||||||
|
clear(TerminalColor::BLACK);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
TextModeTerminalDriver::~TextModeTerminalDriver()
|
||||||
|
{
|
||||||
|
if (m_vaddr == 0)
|
||||||
|
return;
|
||||||
|
const size_t page_count = range_page_count(m_paddr, m_height * m_pitch);
|
||||||
|
PageTable::kernel().unmap_range(m_vaddr & PAGE_ADDR_MASK, page_count * PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextModeTerminalDriver::putchar_at(uint16_t ch, uint32_t x, uint32_t y, Color fg, Color bg)
|
||||||
|
{
|
||||||
|
if (x >= m_width || y >= m_height)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ch >= 0x100)
|
||||||
|
ch = '?';
|
||||||
|
|
||||||
|
const uint8_t color =
|
||||||
|
(color_to_text_mode_color(bg) << 4) |
|
||||||
|
(color_to_text_mode_color(fg) << 0);
|
||||||
|
MMIO::write16(m_vaddr + y * m_pitch + 2 * x, ch | (color << 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextModeTerminalDriver::clear(Color color)
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < m_height; y++)
|
||||||
|
for (uint32_t x = 0; x < m_width; x++)
|
||||||
|
putchar_at(' ', x, y, TerminalColor::BRIGHT_WHITE, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextModeTerminalDriver::set_cursor_shown(bool shown)
|
||||||
|
{
|
||||||
|
if (shown)
|
||||||
|
{
|
||||||
|
IO::outb(0x3D4, 0x0A);
|
||||||
|
IO::outb(0x3D5, (IO::inb(0x3D5) & 0xC0) | 14);
|
||||||
|
|
||||||
|
IO::outb(0x3D4, 0x0B);
|
||||||
|
IO::outb(0x3D5, (IO::inb(0x3D5) & 0xE0) | 15);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IO::outb(0x3D4, 0x0A);
|
||||||
|
IO::outb(0x3D5, 0x20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextModeTerminalDriver::set_cursor_position(uint32_t x, uint32_t y)
|
||||||
|
{
|
||||||
|
const uint16_t pos = y * m_width + x;
|
||||||
|
IO::outb(0x3D4, 0x0F);
|
||||||
|
IO::outb(0x3D5, pos & 0xFF);
|
||||||
|
IO::outb(0x3D4, 0x0E);
|
||||||
|
IO::outb(0x3D5, pos >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ namespace Kernel
|
||||||
|
|
||||||
static BAN::Atomic<uint32_t> s_next_tty_number = 0;
|
static BAN::Atomic<uint32_t> s_next_tty_number = 0;
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::RefPtr<VirtualTTY>> VirtualTTY::create(TerminalDriver* driver)
|
BAN::ErrorOr<BAN::RefPtr<VirtualTTY>> VirtualTTY::create(BAN::RefPtr<TerminalDriver> driver)
|
||||||
{
|
{
|
||||||
auto* tty_ptr = new VirtualTTY(driver);
|
auto* tty_ptr = new VirtualTTY(driver);
|
||||||
ASSERT(tty_ptr);
|
ASSERT(tty_ptr);
|
||||||
|
@ -35,7 +35,7 @@ namespace Kernel
|
||||||
return tty;
|
return tty;
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualTTY::VirtualTTY(TerminalDriver* driver)
|
VirtualTTY::VirtualTTY(BAN::RefPtr<TerminalDriver> driver)
|
||||||
: TTY(0600, 0, 0)
|
: TTY(0600, 0, 0)
|
||||||
, m_name(MUST(BAN::String::formatted("tty{}", s_next_tty_number++)))
|
, m_name(MUST(BAN::String::formatted("tty{}", s_next_tty_number++)))
|
||||||
, m_terminal_driver(driver)
|
, m_terminal_driver(driver)
|
||||||
|
@ -55,11 +55,14 @@ namespace Kernel
|
||||||
m_terminal_driver->clear(m_background);
|
m_terminal_driver->clear(m_background);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VirtualTTY::set_font(const LibFont::Font& font)
|
BAN::ErrorOr<void> VirtualTTY::set_font(LibFont::Font&& font)
|
||||||
{
|
{
|
||||||
|
if (!m_terminal_driver->has_font())
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
SpinLockGuard _(m_write_lock);
|
SpinLockGuard _(m_write_lock);
|
||||||
|
|
||||||
m_terminal_driver->set_font(font);
|
TRY(m_terminal_driver->set_font(BAN::move(font)));
|
||||||
|
|
||||||
uint32_t new_width = m_terminal_driver->width();
|
uint32_t new_width = m_terminal_driver->width();
|
||||||
uint32_t new_height = m_terminal_driver->height();
|
uint32_t new_height = m_terminal_driver->height();
|
||||||
|
@ -85,19 +88,8 @@ namespace Kernel
|
||||||
for (uint32_t y = 0; y < m_height; y++)
|
for (uint32_t y = 0; y < m_height; y++)
|
||||||
for (uint32_t x = 0; x < m_width; x++)
|
for (uint32_t x = 0; x < m_width; x++)
|
||||||
render_from_buffer(x, y);
|
render_from_buffer(x, y);
|
||||||
}
|
|
||||||
|
|
||||||
void VirtualTTY::set_cursor_position(uint32_t x, uint32_t y)
|
return {};
|
||||||
{
|
|
||||||
ASSERT(m_write_lock.current_processor_has_lock());
|
|
||||||
static uint32_t last_x = -1;
|
|
||||||
static uint32_t last_y = -1;
|
|
||||||
if (last_x != uint32_t(-1) && last_y != uint32_t(-1))
|
|
||||||
render_from_buffer(last_x, last_y);
|
|
||||||
if (m_show_cursor)
|
|
||||||
m_terminal_driver->set_cursor_position(x, y);
|
|
||||||
last_x = x;
|
|
||||||
last_y = y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VirtualTTY::reset_ansi()
|
void VirtualTTY::reset_ansi()
|
||||||
|
@ -329,23 +321,24 @@ namespace Kernel
|
||||||
case '@':
|
case '@':
|
||||||
if (m_ansi_state.nums[0] == -1)
|
if (m_ansi_state.nums[0] == -1)
|
||||||
m_ansi_state.nums[0] = 1;
|
m_ansi_state.nums[0] = 1;
|
||||||
reset_ansi();
|
m_ansi_state.nums[0] = BAN::Math::min<uint32_t>(m_ansi_state.nums[0], m_width - m_column);
|
||||||
|
memmove(
|
||||||
|
&m_buffer[m_row * m_width + m_column],
|
||||||
|
&m_buffer[m_row * m_width + m_column + m_ansi_state.nums[0]],
|
||||||
|
m_width - m_column - m_ansi_state.nums[0]
|
||||||
|
);
|
||||||
for (int i = 0; i < m_ansi_state.nums[0]; i++)
|
for (int i = 0; i < m_ansi_state.nums[0]; i++)
|
||||||
putchar_impl(' ');
|
putchar_at(' ', m_column + i, m_row);
|
||||||
return;
|
for (uint32_t x = m_column + m_ansi_state.nums[0]; x < m_width; x++)
|
||||||
|
render_from_buffer(x, m_row);
|
||||||
|
return reset_ansi();
|
||||||
case 'b':
|
case 'b':
|
||||||
if (m_ansi_state.nums[0] == -1)
|
if (m_ansi_state.nums[0] == -1)
|
||||||
m_ansi_state.nums[0] = 1;
|
m_ansi_state.nums[0] = 1;
|
||||||
reset_ansi();
|
|
||||||
if (m_last_graphic_char)
|
if (m_last_graphic_char)
|
||||||
{
|
|
||||||
char buffer[5] {};
|
|
||||||
BAN::UTF8::from_codepoints(&m_last_graphic_char, 1, buffer);
|
|
||||||
for (int i = 0; i < m_ansi_state.nums[0]; i++)
|
for (int i = 0; i < m_ansi_state.nums[0]; i++)
|
||||||
for (int j = 0; buffer[j]; j++)
|
putcodepoint(m_last_graphic_char);
|
||||||
putchar_impl(buffer[j]);
|
return reset_ansi();
|
||||||
}
|
|
||||||
return;
|
|
||||||
case 'd':
|
case 'd':
|
||||||
if (m_ansi_state.nums[0] == -1)
|
if (m_ansi_state.nums[0] == -1)
|
||||||
m_ansi_state.nums[0] = 1;
|
m_ansi_state.nums[0] = 1;
|
||||||
|
@ -355,7 +348,7 @@ namespace Kernel
|
||||||
if (m_ansi_state.index == 0 || m_ansi_state.nums[0] == -1)
|
if (m_ansi_state.index == 0 || m_ansi_state.nums[0] == -1)
|
||||||
{
|
{
|
||||||
m_ansi_state.question = true;
|
m_ansi_state.question = true;
|
||||||
return reset_ansi();
|
return;
|
||||||
}
|
}
|
||||||
reset_ansi();
|
reset_ansi();
|
||||||
dprintln_if(DEBUG_VTTY, "invalid ANSI CSI ?");
|
dprintln_if(DEBUG_VTTY, "invalid ANSI CSI ?");
|
||||||
|
@ -364,7 +357,7 @@ namespace Kernel
|
||||||
case 'l':
|
case 'l':
|
||||||
if (m_ansi_state.question && m_ansi_state.nums[0] == 25)
|
if (m_ansi_state.question && m_ansi_state.nums[0] == 25)
|
||||||
{
|
{
|
||||||
m_show_cursor = (ch == 'h');
|
m_terminal_driver->set_cursor_shown(ch == 'h');
|
||||||
return reset_ansi();
|
return reset_ansi();
|
||||||
}
|
}
|
||||||
reset_ansi();
|
reset_ansi();
|
||||||
|
@ -396,75 +389,10 @@ namespace Kernel
|
||||||
m_terminal_driver->putchar_at(codepoint, x, y, m_foreground, m_background);
|
m_terminal_driver->putchar_at(codepoint, x, y, m_foreground, m_background);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VirtualTTY::putchar_impl(uint8_t ch)
|
void VirtualTTY::putcodepoint(uint32_t codepoint)
|
||||||
{
|
{
|
||||||
ASSERT(m_write_lock.current_processor_has_lock());
|
ASSERT(m_write_lock.current_processor_has_lock());
|
||||||
|
|
||||||
uint32_t codepoint = ch;
|
|
||||||
|
|
||||||
switch (m_state)
|
|
||||||
{
|
|
||||||
case State::Normal:
|
|
||||||
if ((ch & 0x80) == 0)
|
|
||||||
break;
|
|
||||||
if ((ch & 0xE0) == 0xC0)
|
|
||||||
{
|
|
||||||
m_utf8_state.codepoint = ch & 0x1F;
|
|
||||||
m_utf8_state.bytes_missing = 1;
|
|
||||||
}
|
|
||||||
else if ((ch & 0xF0) == 0xE0)
|
|
||||||
{
|
|
||||||
m_utf8_state.codepoint = ch & 0x0F;
|
|
||||||
m_utf8_state.bytes_missing = 2;
|
|
||||||
}
|
|
||||||
else if ((ch & 0xF8) == 0xF0)
|
|
||||||
{
|
|
||||||
m_utf8_state.codepoint = ch & 0x07;
|
|
||||||
m_utf8_state.bytes_missing = 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
reset_ansi();
|
|
||||||
dprintln_if(DEBUG_VTTY, "invalid utf8");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_state = State::WaitingUTF8;
|
|
||||||
return;
|
|
||||||
case State::WaitingAnsiEscape:
|
|
||||||
if (ch == CSI)
|
|
||||||
m_state = State::WaitingAnsiCSI;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
reset_ansi();
|
|
||||||
dprintln_if(DEBUG_VTTY, "unsupported byte after ansi escape {2H}", (uint8_t)ch);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case State::WaitingAnsiCSI:
|
|
||||||
handle_ansi_csi(ch);
|
|
||||||
set_cursor_position(m_column, m_row);
|
|
||||||
return;
|
|
||||||
case State::WaitingUTF8:
|
|
||||||
if ((ch & 0xC0) != 0x80)
|
|
||||||
{
|
|
||||||
m_state = State::Normal;
|
|
||||||
dprintln_if(DEBUG_VTTY, "invalid utf8");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_utf8_state.codepoint = (m_utf8_state.codepoint << 6) | (ch & 0x3F);
|
|
||||||
m_utf8_state.bytes_missing--;
|
|
||||||
if (m_utf8_state.bytes_missing)
|
|
||||||
return;
|
|
||||||
m_state = State::Normal;
|
|
||||||
codepoint = m_utf8_state.codepoint;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool old_show_cursor = m_show_cursor;
|
|
||||||
m_show_cursor = false;
|
|
||||||
set_cursor_position(m_column, m_row);
|
|
||||||
|
|
||||||
switch (codepoint)
|
switch (codepoint)
|
||||||
{
|
{
|
||||||
case BEL: // TODO
|
case BEL: // TODO
|
||||||
|
@ -523,9 +451,75 @@ namespace Kernel
|
||||||
m_column = 0;
|
m_column = 0;
|
||||||
m_row--;
|
m_row--;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_show_cursor = old_show_cursor;
|
void VirtualTTY::putchar_impl(uint8_t ch)
|
||||||
set_cursor_position(m_column, m_row);
|
{
|
||||||
|
ASSERT(m_write_lock.current_processor_has_lock());
|
||||||
|
|
||||||
|
uint32_t codepoint = ch;
|
||||||
|
|
||||||
|
switch (m_state)
|
||||||
|
{
|
||||||
|
case State::Normal:
|
||||||
|
if ((ch & 0x80) == 0)
|
||||||
|
break;
|
||||||
|
if ((ch & 0xE0) == 0xC0)
|
||||||
|
{
|
||||||
|
m_utf8_state.codepoint = ch & 0x1F;
|
||||||
|
m_utf8_state.bytes_missing = 1;
|
||||||
|
}
|
||||||
|
else if ((ch & 0xF0) == 0xE0)
|
||||||
|
{
|
||||||
|
m_utf8_state.codepoint = ch & 0x0F;
|
||||||
|
m_utf8_state.bytes_missing = 2;
|
||||||
|
}
|
||||||
|
else if ((ch & 0xF8) == 0xF0)
|
||||||
|
{
|
||||||
|
m_utf8_state.codepoint = ch & 0x07;
|
||||||
|
m_utf8_state.bytes_missing = 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reset_ansi();
|
||||||
|
dprintln_if(DEBUG_VTTY, "invalid utf8");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_state = State::WaitingUTF8;
|
||||||
|
return;
|
||||||
|
case State::WaitingAnsiEscape:
|
||||||
|
if (ch == CSI)
|
||||||
|
m_state = State::WaitingAnsiCSI;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reset_ansi();
|
||||||
|
dprintln_if(DEBUG_VTTY, "unsupported byte after ansi escape {2H}", (uint8_t)ch);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case State::WaitingAnsiCSI:
|
||||||
|
handle_ansi_csi(ch);
|
||||||
|
return;
|
||||||
|
case State::WaitingUTF8:
|
||||||
|
if ((ch & 0xC0) != 0x80)
|
||||||
|
{
|
||||||
|
m_state = State::Normal;
|
||||||
|
dprintln_if(DEBUG_VTTY, "invalid utf8");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_utf8_state.codepoint = (m_utf8_state.codepoint << 6) | (ch & 0x3F);
|
||||||
|
m_utf8_state.bytes_missing--;
|
||||||
|
if (m_utf8_state.bytes_missing)
|
||||||
|
return;
|
||||||
|
m_state = State::Normal;
|
||||||
|
codepoint = m_utf8_state.codepoint;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
putcodepoint(codepoint);
|
||||||
|
|
||||||
|
m_terminal_driver->set_cursor_position(m_column, m_row);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,8 +107,6 @@ static void parse_command_line()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::TerminalDriver* g_terminal_driver = nullptr;
|
|
||||||
|
|
||||||
static void init2(void*);
|
static void init2(void*);
|
||||||
|
|
||||||
extern "C" void kernel_main(uint32_t boot_magic, uint32_t boot_info)
|
extern "C" void kernel_main(uint32_t boot_magic, uint32_t boot_info)
|
||||||
|
@ -163,14 +161,10 @@ extern "C" void kernel_main(uint32_t boot_magic, uint32_t boot_info)
|
||||||
DevFileSystem::initialize();
|
DevFileSystem::initialize();
|
||||||
dprintln("devfs initialized");
|
dprintln("devfs initialized");
|
||||||
|
|
||||||
auto framebuffer_device = FramebufferDevice::create_from_boot_framebuffer();
|
if (auto ret = TerminalDriver::initialize_from_boot_info(); ret.is_error())
|
||||||
if (!framebuffer_device.is_error())
|
dprintln("failed to initialize terminal driver: {}", ret.error());
|
||||||
{
|
else
|
||||||
DevFileSystem::get().add_device(framebuffer_device.value());
|
dprintln("terminal driver initialized");
|
||||||
g_terminal_driver = FramebufferTerminalDriver::create(framebuffer_device.value());
|
|
||||||
}
|
|
||||||
if (g_terminal_driver)
|
|
||||||
dprintln("Framebuffer terminal initialized");
|
|
||||||
|
|
||||||
if (!cmdline.disable_smp)
|
if (!cmdline.disable_smp)
|
||||||
InterruptController::get().initialize_multiprocessor();
|
InterruptController::get().initialize_multiprocessor();
|
||||||
|
|
|
@ -147,7 +147,10 @@ char* strcat(char* __restrict__ dest, const char* __restrict__ src)
|
||||||
|
|
||||||
char* strncat(char* __restrict__ dest, const char* __restrict__ src, size_t n)
|
char* strncat(char* __restrict__ dest, const char* __restrict__ src, size_t n)
|
||||||
{
|
{
|
||||||
strncpy(dest + strlen(dest), src, n);
|
dest += strlen(dest);
|
||||||
|
while (*src && n--)
|
||||||
|
*dest++ = *src++;
|
||||||
|
*dest = '\0';
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -211,6 +211,27 @@ namespace LibGUI
|
||||||
set_pixel(x, dst_y + fill_y_off + i, fill_color);
|
set_pixel(x, dst_y + fill_y_off + i, fill_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Window::copy_rect(int32_t dst_x, int32_t dst_y, int32_t src_x, int32_t src_y, uint32_t width, uint32_t height, uint32_t fill_color)
|
||||||
|
{
|
||||||
|
fill_rect(dst_x, dst_y, width, height, fill_color);
|
||||||
|
|
||||||
|
if (!clamp_to_framebuffer(dst_x, dst_y, width, height))
|
||||||
|
return;
|
||||||
|
if (!clamp_to_framebuffer(src_x, src_y, width, height))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool copy_dir = dst_y < src_y;
|
||||||
|
for (uint32_t i = 0; i < height; i++)
|
||||||
|
{
|
||||||
|
const uint32_t y_off = copy_dir ? i : height - i - 1;
|
||||||
|
memmove(
|
||||||
|
&m_framebuffer[(dst_y + y_off) * this->width()],
|
||||||
|
&m_framebuffer[(src_y + y_off) * this->width()],
|
||||||
|
width * 4
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Window::clamp_to_framebuffer(int32_t& signed_x, int32_t& signed_y, uint32_t& width, uint32_t& height) const
|
bool Window::clamp_to_framebuffer(int32_t& signed_x, int32_t& signed_y, uint32_t& width, uint32_t& height) const
|
||||||
{
|
{
|
||||||
int32_t min_x = BAN::Math::max<int32_t>(signed_x, 0);
|
int32_t min_x = BAN::Math::max<int32_t>(signed_x, 0);
|
||||||
|
|
|
@ -59,6 +59,10 @@ namespace LibGUI
|
||||||
// fill_color is used when copying data outside of window bounds
|
// fill_color is used when copying data outside of window bounds
|
||||||
void copy_horizontal_slice(int32_t dst_y, int32_t src_y, uint32_t amount, uint32_t fill_color);
|
void copy_horizontal_slice(int32_t dst_y, int32_t src_y, uint32_t amount, uint32_t fill_color);
|
||||||
|
|
||||||
|
// copy rect (src_x, src_y, width, height) to (dst_x, dst_y, width, height)
|
||||||
|
// fill_color is used when copying data outside of window bounds
|
||||||
|
void copy_rect(int32_t dst_x, int32_t dst_y, int32_t src_x, int32_t src_y, uint32_t width, uint32_t height, uint32_t fill_color);
|
||||||
|
|
||||||
void invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height);
|
void invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height);
|
||||||
void invalidate() { return invalidate(0, 0, width(), height()); }
|
void invalidate() { return invalidate(0, 0, width(), height()); }
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,9 @@ static constexpr uint32_t s_colors_bright[] {
|
||||||
0xCC'FFFFFF,
|
0xCC'FFFFFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr auto s_default_bg_color = s_colors_dark[0];
|
||||||
|
static constexpr auto s_default_fg_color = s_colors_bright[7];
|
||||||
|
|
||||||
void Terminal::start_shell()
|
void Terminal::start_shell()
|
||||||
{
|
{
|
||||||
int pts_master = posix_openpt(O_RDWR | O_NOCTTY);
|
int pts_master = posix_openpt(O_RDWR | O_NOCTTY);
|
||||||
|
@ -108,8 +111,8 @@ void Terminal::run()
|
||||||
signal(SIGCHLD, [](int) { s_shell_exited = true; });
|
signal(SIGCHLD, [](int) { s_shell_exited = true; });
|
||||||
start_shell();
|
start_shell();
|
||||||
|
|
||||||
m_bg_color = s_colors_dark[0];
|
m_bg_color = s_default_bg_color;
|
||||||
m_fg_color = s_colors_bright[7];
|
m_fg_color = s_default_fg_color;
|
||||||
|
|
||||||
auto attributes = LibGUI::Window::default_attributes;
|
auto attributes = LibGUI::Window::default_attributes;
|
||||||
attributes.alpha_channel = true;
|
attributes.alpha_channel = true;
|
||||||
|
@ -278,8 +281,8 @@ void Terminal::handle_sgr()
|
||||||
switch (m_csi_info.fields[0])
|
switch (m_csi_info.fields[0])
|
||||||
{
|
{
|
||||||
case -1: case 0:
|
case -1: case 0:
|
||||||
m_bg_color = s_colors_dark[0];
|
m_bg_color = s_default_bg_color;
|
||||||
m_fg_color = s_colors_bright[7];
|
m_fg_color = s_default_fg_color;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
// FIXME: bold
|
// FIXME: bold
|
||||||
|
@ -287,12 +290,21 @@ void Terminal::handle_sgr()
|
||||||
case 7:
|
case 7:
|
||||||
BAN::swap(m_fg_color, m_bg_color);
|
BAN::swap(m_fg_color, m_bg_color);
|
||||||
break;
|
break;
|
||||||
|
case 10:
|
||||||
|
// default font
|
||||||
|
break;
|
||||||
case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37:
|
case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37:
|
||||||
m_fg_color = s_colors_dark[m_csi_info.fields[0] - 30];
|
m_fg_color = s_colors_dark[m_csi_info.fields[0] - 30];
|
||||||
break;
|
break;
|
||||||
|
case 39:
|
||||||
|
m_fg_color = s_default_fg_color;
|
||||||
|
break;
|
||||||
case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47:
|
case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47:
|
||||||
m_bg_color = s_colors_dark[m_csi_info.fields[0] - 40];
|
m_bg_color = s_colors_dark[m_csi_info.fields[0] - 40];
|
||||||
break;
|
break;
|
||||||
|
case 49:
|
||||||
|
m_bg_color = s_default_bg_color;
|
||||||
|
break;
|
||||||
case 90: case 91: case 92: case 93: case 94: case 95: case 96: case 97:
|
case 90: case 91: case 92: case 93: case 94: case 95: case 96: case 97:
|
||||||
m_fg_color = s_colors_bright[m_csi_info.fields[0] - 90];
|
m_fg_color = s_colors_bright[m_csi_info.fields[0] - 90];
|
||||||
break;
|
break;
|
||||||
|
@ -332,15 +344,25 @@ Rectangle Terminal::handle_csi(char ch)
|
||||||
Rectangle should_invalidate;
|
Rectangle should_invalidate;
|
||||||
switch (ch)
|
switch (ch)
|
||||||
{
|
{
|
||||||
|
case 'A':
|
||||||
|
if (m_csi_info.fields[0] == -1)
|
||||||
|
m_csi_info.fields[0] = 1;
|
||||||
|
m_cursor.y = BAN::Math::max<int32_t>(m_cursor.y - m_csi_info.fields[0], 0);
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
if (m_csi_info.fields[0] == -1)
|
||||||
|
m_csi_info.fields[0] = 1;
|
||||||
|
m_cursor.y = BAN::Math::min<int32_t>(m_cursor.y + m_csi_info.fields[0], rows() - 1);
|
||||||
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
if (m_csi_info.fields[0] == -1)
|
if (m_csi_info.fields[0] == -1)
|
||||||
m_csi_info.fields[0] = 1;
|
m_csi_info.fields[0] = 1;
|
||||||
m_cursor.x = BAN::Math::clamp<int32_t>(m_cursor.x + m_csi_info.fields[0], 0, cols() - 1);
|
m_cursor.x = BAN::Math::min<int32_t>(m_cursor.x + m_csi_info.fields[0], cols() - 1);
|
||||||
break;
|
break;
|
||||||
case 'D':
|
case 'D':
|
||||||
if (m_csi_info.fields[0] == -1)
|
if (m_csi_info.fields[0] == -1)
|
||||||
m_csi_info.fields[0] = 1;
|
m_csi_info.fields[0] = 1;
|
||||||
m_cursor.x = BAN::Math::clamp<int32_t>((int32_t)m_cursor.x - m_csi_info.fields[0], 0, cols() - 1);
|
m_cursor.x = BAN::Math::max<int32_t>(m_cursor.x - m_csi_info.fields[0], 0);
|
||||||
break;
|
break;
|
||||||
case 'G':
|
case 'G':
|
||||||
m_cursor.x = BAN::Math::clamp<int32_t>(m_csi_info.fields[0], 1, cols()) - 1;
|
m_cursor.x = BAN::Math::clamp<int32_t>(m_csi_info.fields[0], 1, cols()) - 1;
|
||||||
|
@ -449,6 +471,34 @@ Rectangle Terminal::handle_csi(char ch)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case '@':
|
||||||
|
{
|
||||||
|
const uint32_t count = (m_csi_info.fields[0] == -1) ? 1 : m_csi_info.fields[0];
|
||||||
|
const uint32_t dst_x = (m_cursor.x + count) * m_font.width();
|
||||||
|
const uint32_t src_x = m_cursor.x * m_font.width();
|
||||||
|
const uint32_t y = m_cursor.y * m_font.height();
|
||||||
|
|
||||||
|
m_window->copy_rect(dst_x, y, src_x, y, m_window->width() - dst_x, m_font.height(), m_bg_color);
|
||||||
|
m_window->fill_rect(src_x, y, count * m_font.width(), m_font.height(), m_bg_color);
|
||||||
|
should_invalidate = {
|
||||||
|
src_x,
|
||||||
|
y,
|
||||||
|
m_window->width() - src_x,
|
||||||
|
m_font.height()
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'b':
|
||||||
|
if (m_csi_info.fields[0] == -1)
|
||||||
|
m_csi_info.fields[0] = 1;
|
||||||
|
if (m_last_graphic_char)
|
||||||
|
for (int32_t i = 0; i < m_csi_info.fields[0]; i++)
|
||||||
|
should_invalidate = should_invalidate.get_bounding_box(putcodepoint(m_last_graphic_char));
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
m_cursor.y = BAN::Math::clamp<int32_t>(m_csi_info.fields[0], 1, rows()) - 1;
|
||||||
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
handle_sgr();
|
handle_sgr();
|
||||||
break;
|
break;
|
||||||
|
@ -476,6 +526,59 @@ Rectangle Terminal::handle_csi(char ch)
|
||||||
return should_invalidate;
|
return should_invalidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle Terminal::putcodepoint(uint32_t codepoint)
|
||||||
|
{
|
||||||
|
Rectangle should_invalidate;
|
||||||
|
|
||||||
|
switch (codepoint)
|
||||||
|
{
|
||||||
|
case '\e':
|
||||||
|
m_state = State::ESC;
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
m_cursor.x = 0;
|
||||||
|
m_cursor.y++;
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
m_cursor.x = 0;
|
||||||
|
break;
|
||||||
|
case '\b':
|
||||||
|
if (m_cursor.x > 0)
|
||||||
|
m_cursor.x--;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
const uint32_t cell_w = m_font.width();
|
||||||
|
const uint32_t cell_h = m_font.height();
|
||||||
|
const uint32_t cell_x = m_cursor.x * cell_w;
|
||||||
|
const uint32_t cell_y = m_cursor.y * cell_h;
|
||||||
|
|
||||||
|
m_window->fill_rect(cell_x, cell_y, cell_w, cell_h, m_bg_color);
|
||||||
|
m_window->draw_character(codepoint, m_font, cell_x, cell_y, m_fg_color);
|
||||||
|
m_last_graphic_char = codepoint;
|
||||||
|
should_invalidate = { cell_x, cell_y, cell_w, cell_h };
|
||||||
|
m_cursor.x++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_cursor.x >= cols())
|
||||||
|
{
|
||||||
|
m_cursor.x = 0;
|
||||||
|
m_cursor.y++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_cursor.y >= rows())
|
||||||
|
{
|
||||||
|
const uint32_t scroll = m_cursor.y - rows() + 1;
|
||||||
|
m_cursor.y -= scroll;
|
||||||
|
m_window->shift_vertical(-scroll * (int32_t)m_font.height(), m_bg_color);
|
||||||
|
should_invalidate = { 0, 0, m_window->width(), m_window->height() };
|
||||||
|
}
|
||||||
|
|
||||||
|
return should_invalidate;
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle Terminal::putchar(uint8_t ch)
|
Rectangle Terminal::putchar(uint8_t ch)
|
||||||
{
|
{
|
||||||
if (m_state == State::ESC)
|
if (m_state == State::ESC)
|
||||||
|
@ -538,54 +641,7 @@ Rectangle Terminal::putchar(uint8_t ch)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle should_invalidate;
|
return putcodepoint(codepoint);
|
||||||
|
|
||||||
switch (codepoint)
|
|
||||||
{
|
|
||||||
case '\e':
|
|
||||||
m_state = State::ESC;
|
|
||||||
break;
|
|
||||||
case '\n':
|
|
||||||
m_cursor.x = 0;
|
|
||||||
m_cursor.y++;
|
|
||||||
break;
|
|
||||||
case '\r':
|
|
||||||
m_cursor.x = 0;
|
|
||||||
break;
|
|
||||||
case '\b':
|
|
||||||
if (m_cursor.x > 0)
|
|
||||||
m_cursor.x--;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
const uint32_t cell_w = m_font.width();
|
|
||||||
const uint32_t cell_h = m_font.height();
|
|
||||||
const uint32_t cell_x = m_cursor.x * cell_w;
|
|
||||||
const uint32_t cell_y = m_cursor.y * cell_h;
|
|
||||||
|
|
||||||
m_window->fill_rect(cell_x, cell_y, cell_w, cell_h, m_bg_color);
|
|
||||||
m_window->draw_character(codepoint, m_font, cell_x, cell_y, m_fg_color);
|
|
||||||
should_invalidate = { cell_x, cell_y, cell_w, cell_h };
|
|
||||||
m_cursor.x++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_cursor.x >= cols())
|
|
||||||
{
|
|
||||||
m_cursor.x = 0;
|
|
||||||
m_cursor.y++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_cursor.y >= rows())
|
|
||||||
{
|
|
||||||
const uint32_t scroll = m_cursor.y - rows() + 1;
|
|
||||||
m_cursor.y -= scroll;
|
|
||||||
m_window->shift_vertical(-scroll * (int32_t)m_font.height(), m_bg_color);
|
|
||||||
should_invalidate = { 0, 0, m_window->width(), m_window->height() };
|
|
||||||
}
|
|
||||||
|
|
||||||
return should_invalidate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::on_key_event(LibGUI::EventPacket::KeyEvent::event_t event)
|
void Terminal::on_key_event(LibGUI::EventPacket::KeyEvent::event_t event)
|
||||||
|
|
|
@ -37,6 +37,7 @@ public:
|
||||||
private:
|
private:
|
||||||
void handle_sgr();
|
void handle_sgr();
|
||||||
Rectangle handle_csi(char ch);
|
Rectangle handle_csi(char ch);
|
||||||
|
Rectangle putcodepoint(uint32_t codepoint);
|
||||||
Rectangle putchar(uint8_t ch);
|
Rectangle putchar(uint8_t ch);
|
||||||
bool read_shell();
|
bool read_shell();
|
||||||
|
|
||||||
|
@ -91,6 +92,8 @@ private:
|
||||||
uint8_t m_utf8_index { 0 };
|
uint8_t m_utf8_index { 0 };
|
||||||
uint8_t m_utf8_bytes[4] { };
|
uint8_t m_utf8_bytes[4] { };
|
||||||
|
|
||||||
|
uint32_t m_last_graphic_char { 0 };
|
||||||
|
|
||||||
Cursor m_saved_cursor { 0, 0 };
|
Cursor m_saved_cursor { 0, 0 };
|
||||||
uint32_t m_fg_color { 0 };
|
uint32_t m_fg_color { 0 };
|
||||||
uint32_t m_bg_color { 0 };
|
uint32_t m_bg_color { 0 };
|
||||||
|
|
Loading…
Reference in New Issue