mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-04 05:19:58 +03:00
Kernel/Graphics + SystemServer: Support text mode properly
As we removed the support of VBE modesetting that was done by GRUB early on boot, we need to determine if we can modeset the resolution with our drivers, and if not, we should enable text mode and ensure that SystemServer knows about it too. Also, SystemServer should first check if there's a framebuffer device node, which is an indication that text mode was not even if it was requested. Then, if it doesn't find it, it should check what boot_mode argument the user specified (in case it's self-test). This way if we try to use bochs-display device (which is not VGA compatible) and request a text mode, it will not honor the request and will continue with graphical mode. Also try to print critical messages with mininum memory allocations possible. In LibVT, We make the implementation flexible for kernel-specific methods that are implemented in ConsoleImpl class.
This commit is contained in:
parent
dac129e10b
commit
20743e8aed
Notes:
sideshowbarker
2024-07-18 18:00:39 +09:00
Author: https://github.com/supercomputer7 Commit: https://github.com/SerenityOS/serenity/commit/20743e8aede Pull-request: https://github.com/SerenityOS/serenity/pull/6277 Reviewed-by: https://github.com/ElectrodeYT Reviewed-by: https://github.com/awesomekling
@ -677,6 +677,29 @@ void vdmesgln(StringView fmtstr, TypeErasedFormatParams params)
|
||||
const auto string = builder.string_view();
|
||||
kernelputstr(string.characters_without_null_termination(), string.length());
|
||||
}
|
||||
|
||||
void v_critical_dmesgln(StringView fmtstr, TypeErasedFormatParams params)
|
||||
{
|
||||
// FIXME: Try to avoid memory allocations further to prevent faulting
|
||||
// at OOM conditions.
|
||||
|
||||
StringBuilder builder;
|
||||
# ifdef __serenity__
|
||||
if (Kernel::Processor::is_initialized() && Kernel::Thread::current()) {
|
||||
auto& thread = *Kernel::Thread::current();
|
||||
builder.appendff("[{}({}:{})]: ", thread.process().name(), thread.pid().value(), thread.tid().value());
|
||||
} else {
|
||||
builder.appendff("[Kernel]: ");
|
||||
}
|
||||
# endif
|
||||
|
||||
vformat(builder, fmtstr, params);
|
||||
builder.append('\n');
|
||||
|
||||
const auto string = builder.string_view();
|
||||
kernelcriticalputstr(string.characters_without_null_termination(), string.length());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
template struct Formatter<unsigned char, void>;
|
||||
|
11
AK/Format.h
11
AK/Format.h
@ -436,6 +436,16 @@ void dmesgln(CheckedFormatString<Parameters...>&& fmt, const Parameters&... para
|
||||
{
|
||||
vdmesgln(fmt.view(), VariadicFormatParams { parameters... });
|
||||
}
|
||||
|
||||
void v_critical_dmesgln(StringView fmtstr, TypeErasedFormatParams);
|
||||
|
||||
// be very careful to not cause any allocations here, since we could be in
|
||||
// a very unstable situation
|
||||
template<typename... Parameters>
|
||||
void critical_dmesgln(CheckedFormatString<Parameters...>&& fmt, const Parameters&... parameters)
|
||||
{
|
||||
v_critical_dmesgln(fmt.view(), VariadicFormatParams { parameters... });
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T, typename = void>
|
||||
@ -492,6 +502,7 @@ struct Formatter<FormatString> : Formatter<String> {
|
||||
} // namespace AK
|
||||
|
||||
#ifdef KERNEL
|
||||
using AK::critical_dmesgln;
|
||||
using AK::dmesgln;
|
||||
#else
|
||||
using AK::out;
|
||||
|
@ -2455,8 +2455,8 @@ void copy_ptrace_registers_into_kernel_registers(RegisterState& kernel_regs, con
|
||||
void __assertion_failed(const char* msg, const char* file, unsigned line, const char* func)
|
||||
{
|
||||
asm volatile("cli");
|
||||
dmesgln("ASSERTION FAILED: {}", msg);
|
||||
dmesgln("{}:{} in {}", file, line, func);
|
||||
critical_dmesgln("ASSERTION FAILED: {}", msg);
|
||||
critical_dmesgln("{}:{} in {}", file, line, func);
|
||||
|
||||
abort();
|
||||
}
|
||||
|
@ -50,6 +50,9 @@ set(KERNEL_SOURCES
|
||||
Devices/HID/PS2KeyboardDevice.cpp
|
||||
Devices/HID/PS2MouseDevice.cpp
|
||||
Devices/HID/VMWareMouseDevice.cpp
|
||||
Graphics/Console/FramebufferConsole.cpp
|
||||
Graphics/Console/TextModeConsole.cpp
|
||||
Graphics/Console/VGAConsole.cpp
|
||||
Graphics/BochsFramebufferDevice.cpp
|
||||
Graphics/BochsGraphicsAdapter.cpp
|
||||
Graphics/FramebufferDevice.cpp
|
||||
@ -205,6 +208,7 @@ set(KERNEL_SOURCES
|
||||
Syscalls/waitid.cpp
|
||||
Syscalls/inode_watcher.cpp
|
||||
Syscalls/write.cpp
|
||||
TTY/ConsoleManagement.cpp
|
||||
TTY/MasterPTY.cpp
|
||||
TTY/PTYMultiplexer.cpp
|
||||
TTY/SlavePTY.cpp
|
||||
|
@ -171,8 +171,8 @@ UNMAP_AFTER_INIT AHCIResetMode CommandLine::ahci_reset_mode() const
|
||||
UNMAP_AFTER_INIT BootMode CommandLine::boot_mode() const
|
||||
{
|
||||
const auto boot_mode = lookup("boot_mode").value_or("graphical");
|
||||
if (boot_mode == "text") {
|
||||
return BootMode::Text;
|
||||
if (boot_mode == "no-fbdev") {
|
||||
return BootMode::NoFramebufferDevices;
|
||||
} else if (boot_mode == "self-test") {
|
||||
return BootMode::SelfTest;
|
||||
} else if (boot_mode == "graphical") {
|
||||
@ -181,10 +181,10 @@ UNMAP_AFTER_INIT BootMode CommandLine::boot_mode() const
|
||||
PANIC("Unknown BootMode: {}", boot_mode);
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT bool CommandLine::is_text_mode() const
|
||||
UNMAP_AFTER_INIT bool CommandLine::is_no_framebuffer_devices_mode() const
|
||||
{
|
||||
const auto mode = boot_mode();
|
||||
return mode == BootMode::Text || mode == BootMode::SelfTest;
|
||||
return mode == BootMode::NoFramebufferDevices || mode == BootMode::SelfTest;
|
||||
}
|
||||
|
||||
String CommandLine::userspace_init() const
|
||||
|
@ -14,7 +14,7 @@
|
||||
namespace Kernel {
|
||||
|
||||
enum class BootMode {
|
||||
Text,
|
||||
NoFramebufferDevices,
|
||||
SelfTest,
|
||||
Graphical
|
||||
};
|
||||
@ -59,7 +59,7 @@ public:
|
||||
[[nodiscard]] bool is_vmmouse_enabled() const;
|
||||
[[nodiscard]] PCIAccessLevel pci_access_level() const;
|
||||
[[nodiscard]] bool is_legacy_time_enabled() const;
|
||||
[[nodiscard]] bool is_text_mode() const;
|
||||
[[nodiscard]] bool is_no_framebuffer_devices_mode() const;
|
||||
[[nodiscard]] bool is_force_pio() const;
|
||||
[[nodiscard]] AcpiFeatureLevel acpi_feature_level() const;
|
||||
[[nodiscard]] BootMode boot_mode() const;
|
||||
|
@ -306,6 +306,10 @@
|
||||
#cmakedefine01 VIRTIO_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef VIRTUAL_CONSOLE_DEBUG
|
||||
#cmakedefine01 VIRTUAL_CONSOLE_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef VRA_DEBUG
|
||||
#cmakedefine01 VRA_DEBUG
|
||||
#endif
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <Kernel/Devices/HID/HIDManagement.h>
|
||||
#include <Kernel/Devices/HID/PS2KeyboardDevice.h>
|
||||
#include <Kernel/IO.h>
|
||||
#include <Kernel/TTY/VirtualConsole.h>
|
||||
#include <Kernel/TTY/ConsoleManagement.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@ -69,7 +69,7 @@ void PS2KeyboardDevice::irq_handle_byte_read(u8 byte)
|
||||
if (m_modifiers & Mod_Alt) {
|
||||
switch (ch) {
|
||||
case 0x02 ... 0x07: // 1 to 6
|
||||
VirtualConsole::switch_to(ch - 0x02);
|
||||
ConsoleManagement::the().switch_to(ch - 0x02);
|
||||
break;
|
||||
default:
|
||||
key_state_changed(ch, pressed);
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <Kernel/Graphics/Bochs.h>
|
||||
#include <Kernel/Graphics/BochsFramebufferDevice.h>
|
||||
#include <Kernel/Graphics/BochsGraphicsAdapter.h>
|
||||
#include <Kernel/Graphics/Console/FramebufferConsole.h>
|
||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||
#include <Kernel/IO.h>
|
||||
#include <Kernel/PCI/Access.h>
|
||||
#include <Kernel/Process.h>
|
||||
@ -52,19 +54,24 @@ UNMAP_AFTER_INIT BochsGraphicsAdapter::BochsGraphicsAdapter(PCI::Address pci_add
|
||||
, m_mmio_registers(PCI::get_BAR2(pci_address) & 0xfffffff0)
|
||||
{
|
||||
set_safe_resolution();
|
||||
// We assume safe resolutio is 1024x768x32
|
||||
m_framebuffer_console = Graphics::FramebufferConsole::initialize(PhysicalAddress(PCI::get_BAR0(pci_address) & 0xfffffff0), 1024, 768, 1024 * sizeof(u32));
|
||||
// FIXME: This is a very wrong way to do this...
|
||||
GraphicsManagement::the().m_console = m_framebuffer_console;
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT void BochsGraphicsAdapter::initialize_framebuffer_devices()
|
||||
{
|
||||
// FIXME: Find a better way to determine default resolution...
|
||||
m_framebuffer = BochsFramebufferDevice::create(*this, PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffff0), 1024 * 4, 1024, 768);
|
||||
m_framebuffer_device = BochsFramebufferDevice::create(*this, PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffff0), 1024 * 4, 1024, 768);
|
||||
m_framebuffer_device->initialize();
|
||||
}
|
||||
|
||||
GraphicsDevice::Type BochsGraphicsAdapter::type() const
|
||||
{
|
||||
if (PCI::get_class(pci_address()) == 0x3 && PCI::get_subclass(pci_address()) == 0x0)
|
||||
return Type::VGACompatible;
|
||||
return Type::Bochs;
|
||||
return Type::Bochs;
|
||||
}
|
||||
|
||||
void BochsGraphicsAdapter::set_safe_resolution()
|
||||
@ -119,8 +126,33 @@ bool BochsGraphicsAdapter::validate_setup_resolution(size_t width, size_t height
|
||||
|
||||
void BochsGraphicsAdapter::set_y_offset(size_t y_offset)
|
||||
{
|
||||
if (m_console_enabled)
|
||||
return;
|
||||
auto registers = map_typed_writable<volatile BochsDisplayMMIORegisters>(m_mmio_registers);
|
||||
registers->bochs_regs.y_offset = y_offset;
|
||||
}
|
||||
|
||||
void BochsGraphicsAdapter::enable_consoles()
|
||||
{
|
||||
ScopedSpinLock lock(m_console_mode_switch_lock);
|
||||
VERIFY(m_framebuffer_console);
|
||||
m_console_enabled = true;
|
||||
auto registers = map_typed_writable<volatile BochsDisplayMMIORegisters>(m_mmio_registers);
|
||||
registers->bochs_regs.y_offset = 0;
|
||||
if (m_framebuffer_device)
|
||||
m_framebuffer_device->dectivate_writes();
|
||||
m_framebuffer_console->enable();
|
||||
}
|
||||
void BochsGraphicsAdapter::disable_consoles()
|
||||
{
|
||||
ScopedSpinLock lock(m_console_mode_switch_lock);
|
||||
VERIFY(m_framebuffer_console);
|
||||
VERIFY(m_framebuffer_device);
|
||||
m_console_enabled = false;
|
||||
auto registers = map_typed_writable<volatile BochsDisplayMMIORegisters>(m_mmio_registers);
|
||||
registers->bochs_regs.y_offset = 0;
|
||||
m_framebuffer_console->disable();
|
||||
m_framebuffer_device->activate_writes();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Graphics/Console/FramebufferConsole.h>
|
||||
#include <Kernel/Graphics/GraphicsDevice.h>
|
||||
#include <Kernel/PCI/DeviceController.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
@ -25,12 +26,16 @@ class BochsGraphicsAdapter final : public GraphicsDevice
|
||||
public:
|
||||
static NonnullRefPtr<BochsGraphicsAdapter> initialize(PCI::Address);
|
||||
virtual ~BochsGraphicsAdapter() = default;
|
||||
virtual bool framebuffer_devices_initialized() const override { return !m_framebuffer_device.is_null(); }
|
||||
|
||||
private:
|
||||
// ^GraphicsDevice
|
||||
virtual void initialize_framebuffer_devices() override;
|
||||
virtual Type type() const override;
|
||||
|
||||
virtual void enable_consoles() override;
|
||||
virtual void disable_consoles() override;
|
||||
|
||||
explicit BochsGraphicsAdapter(PCI::Address);
|
||||
|
||||
void set_safe_resolution();
|
||||
@ -43,7 +48,10 @@ private:
|
||||
void set_y_offset(size_t);
|
||||
|
||||
PhysicalAddress m_mmio_registers;
|
||||
RefPtr<BochsFramebufferDevice> m_framebuffer;
|
||||
RefPtr<BochsFramebufferDevice> m_framebuffer_device;
|
||||
RefPtr<Graphics::FramebufferConsole> m_framebuffer_console;
|
||||
SpinLock<u8> m_console_mode_switch_lock;
|
||||
bool m_console_enabled { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
82
Kernel/Graphics/Console/Console.h
Normal file
82
Kernel/Graphics/Console/Console.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Graphics/GraphicsDevice.h>
|
||||
|
||||
namespace Kernel::Graphics {
|
||||
|
||||
class Console : public RefCounted<Console> {
|
||||
public:
|
||||
// Stanadard VGA text mode colors
|
||||
enum Color : u8 {
|
||||
Black = 0,
|
||||
Blue,
|
||||
Green,
|
||||
Cyan,
|
||||
Red,
|
||||
Magenta,
|
||||
Brown,
|
||||
LightGray,
|
||||
DarkGray,
|
||||
BrightBlue,
|
||||
BrightGreen,
|
||||
BrightCyan,
|
||||
BrightRed,
|
||||
BrightMagenta,
|
||||
Yellow,
|
||||
White,
|
||||
};
|
||||
|
||||
public:
|
||||
size_t width() const { return m_width; }
|
||||
size_t height() const { return m_height; }
|
||||
size_t pitch() const { return bytes_per_base_glyph() * width(); }
|
||||
virtual size_t max_column() const { return m_width; }
|
||||
virtual size_t max_row() const { return m_height; }
|
||||
virtual size_t bytes_per_base_glyph() const = 0;
|
||||
virtual size_t chars_per_line() const = 0;
|
||||
|
||||
virtual void enable() = 0;
|
||||
virtual void disable() = 0;
|
||||
|
||||
virtual bool is_hardware_paged_capable() const = 0;
|
||||
virtual bool has_hardware_cursor() const = 0;
|
||||
|
||||
virtual void set_cursor(size_t x, size_t y) = 0;
|
||||
virtual void hide_cursor() = 0;
|
||||
virtual void show_cursor() = 0;
|
||||
|
||||
virtual void clear(size_t x, size_t y, size_t length) const = 0;
|
||||
virtual void write(size_t x, size_t y, char ch, Color background, Color foreground) const = 0;
|
||||
virtual void write(size_t x, size_t y, String, Color background, Color foreground) const = 0;
|
||||
virtual void write(size_t x, size_t y, char ch) const = 0;
|
||||
virtual void write(size_t x, size_t y, String) const = 0;
|
||||
virtual void write(char ch) const = 0;
|
||||
|
||||
virtual ~Console() { }
|
||||
|
||||
protected:
|
||||
Console(size_t width, size_t height)
|
||||
: m_width(width)
|
||||
, m_height(height)
|
||||
{
|
||||
m_enabled.store(true);
|
||||
}
|
||||
|
||||
Atomic<bool> m_enabled;
|
||||
Color m_default_foreground_color { Color::White };
|
||||
Color m_default_background_color { Color::Black };
|
||||
const size_t m_width;
|
||||
const size_t m_height;
|
||||
mutable size_t m_x { 0 };
|
||||
mutable size_t m_y { 0 };
|
||||
};
|
||||
}
|
348
Kernel/Graphics/Console/FramebufferConsole.cpp
Normal file
348
Kernel/Graphics/Console/FramebufferConsole.cpp
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Graphics/Console/FramebufferConsole.h>
|
||||
|
||||
namespace Kernel::Graphics {
|
||||
|
||||
constexpr unsigned char const font8x8_basic[128][8] = {
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0000 (nul)
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0001
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0002
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0003
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0004
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0005
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0006
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0007
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0008
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0009
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000A
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000B
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000C
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000D
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000E
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000F
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0010
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0011
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0012
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0013
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0014
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0015
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0016
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0017
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0018
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0019
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001A
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001B
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001C
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001D
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001E
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001F
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0020 (space)
|
||||
{ 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00 }, // U+0021 (!)
|
||||
{ 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0022 (")
|
||||
{ 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00 }, // U+0023 (#)
|
||||
{ 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00 }, // U+0024 ($)
|
||||
{ 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00 }, // U+0025 (%)
|
||||
{ 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00 }, // U+0026 (&)
|
||||
{ 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0027 (')
|
||||
{ 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00 }, // U+0028 (()
|
||||
{ 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00 }, // U+0029 ())
|
||||
{ 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00 }, // U+002A (*)
|
||||
{ 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00 }, // U+002B (+)
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06 }, // U+002C (,)
|
||||
{ 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00 }, // U+002D (-)
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00 }, // U+002E (.)
|
||||
{ 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00 }, // U+002F (/)
|
||||
{ 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00 }, // U+0030 (0)
|
||||
{ 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00 }, // U+0031 (1)
|
||||
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00 }, // U+0032 (2)
|
||||
{ 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00 }, // U+0033 (3)
|
||||
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00 }, // U+0034 (4)
|
||||
{ 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00 }, // U+0035 (5)
|
||||
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00 }, // U+0036 (6)
|
||||
{ 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00 }, // U+0037 (7)
|
||||
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00 }, // U+0038 (8)
|
||||
{ 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00 }, // U+0039 (9)
|
||||
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00 }, // U+003A (:)
|
||||
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06 }, // U+003B (;)
|
||||
{ 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00 }, // U+003C (<)
|
||||
{ 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00 }, // U+003D (=)
|
||||
{ 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00 }, // U+003E (>)
|
||||
{ 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00 }, // U+003F (?)
|
||||
{ 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00 }, // U+0040 (@)
|
||||
{ 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00 }, // U+0041 (A)
|
||||
{ 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00 }, // U+0042 (B)
|
||||
{ 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00 }, // U+0043 (C)
|
||||
{ 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00 }, // U+0044 (D)
|
||||
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00 }, // U+0045 (E)
|
||||
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00 }, // U+0046 (F)
|
||||
{ 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00 }, // U+0047 (G)
|
||||
{ 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00 }, // U+0048 (H)
|
||||
{ 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, // U+0049 (I)
|
||||
{ 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00 }, // U+004A (J)
|
||||
{ 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00 }, // U+004B (K)
|
||||
{ 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00 }, // U+004C (L)
|
||||
{ 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00 }, // U+004D (M)
|
||||
{ 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00 }, // U+004E (N)
|
||||
{ 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00 }, // U+004F (O)
|
||||
{ 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00 }, // U+0050 (P)
|
||||
{ 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00 }, // U+0051 (Q)
|
||||
{ 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00 }, // U+0052 (R)
|
||||
{ 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00 }, // U+0053 (S)
|
||||
{ 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, // U+0054 (T)
|
||||
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00 }, // U+0055 (U)
|
||||
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00 }, // U+0056 (V)
|
||||
{ 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00 }, // U+0057 (W)
|
||||
{ 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00 }, // U+0058 (X)
|
||||
{ 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00 }, // U+0059 (Y)
|
||||
{ 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00 }, // U+005A (Z)
|
||||
{ 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00 }, // U+005B ([)
|
||||
{ 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00 }, // U+005C (\)
|
||||
{ 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00 }, // U+005D (])
|
||||
{ 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00 }, // U+005E (^)
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, // U+005F (_)
|
||||
{ 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0060 (`)
|
||||
{ 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00 }, // U+0061 (a)
|
||||
{ 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00 }, // U+0062 (b)
|
||||
{ 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00 }, // U+0063 (c)
|
||||
{ 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00 }, // U+0064 (d)
|
||||
{ 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00 }, // U+0065 (e)
|
||||
{ 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00 }, // U+0066 (f)
|
||||
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F }, // U+0067 (g)
|
||||
{ 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00 }, // U+0068 (h)
|
||||
{ 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, // U+0069 (i)
|
||||
{ 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E }, // U+006A (j)
|
||||
{ 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00 }, // U+006B (k)
|
||||
{ 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, // U+006C (l)
|
||||
{ 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00 }, // U+006D (m)
|
||||
{ 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00 }, // U+006E (n)
|
||||
{ 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00 }, // U+006F (o)
|
||||
{ 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F }, // U+0070 (p)
|
||||
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78 }, // U+0071 (q)
|
||||
{ 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00 }, // U+0072 (r)
|
||||
{ 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00 }, // U+0073 (s)
|
||||
{ 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00 }, // U+0074 (t)
|
||||
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00 }, // U+0075 (u)
|
||||
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00 }, // U+0076 (v)
|
||||
{ 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00 }, // U+0077 (w)
|
||||
{ 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00 }, // U+0078 (x)
|
||||
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F }, // U+0079 (y)
|
||||
{ 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00 }, // U+007A (z)
|
||||
{ 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00 }, // U+007B ({)
|
||||
{ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00 }, // U+007C (|)
|
||||
{ 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00 }, // U+007D (})
|
||||
{ 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+007E (~)
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // U+007F
|
||||
};
|
||||
|
||||
// FIXME: This assumes 32 bit BGR (Blue-Green-Red) palette
|
||||
enum BGRColor : u32 {
|
||||
Black = 0,
|
||||
Blue = 0x0000FF,
|
||||
Green = 0x00FF00,
|
||||
Cyan = 0x0000FFFF,
|
||||
Red = 0xFF0000,
|
||||
Magenta = 0x00FF00FF,
|
||||
Brown = 0x00964B00,
|
||||
LightGray = 0x00D3D3D3,
|
||||
DarkGray = 0x00A9A9A9,
|
||||
BrightBlue = 0x0ADD8E6,
|
||||
BrightGreen = 0x0090EE90,
|
||||
BrightCyan = 0x00E0FFFF,
|
||||
BrightRed = 0x00D70A53,
|
||||
BrightMagenta = 0x00F984E5,
|
||||
Yellow = 0x00FFE135,
|
||||
White = 0x00FFFFFF,
|
||||
};
|
||||
|
||||
static inline BGRColor convert_standard_color_to_bgr_color(Console::Color color)
|
||||
{
|
||||
switch (color) {
|
||||
case Console::Color::Black:
|
||||
return BGRColor::Black;
|
||||
case Console::Color::Red:
|
||||
return BGRColor::Red;
|
||||
case Console::Color::Brown:
|
||||
return BGRColor::Brown;
|
||||
case Console::Color::Blue:
|
||||
return BGRColor::Blue;
|
||||
case Console::Color::Magenta:
|
||||
return BGRColor::Magenta;
|
||||
case Console::Color::Green:
|
||||
return BGRColor::Green;
|
||||
case Console::Color::Cyan:
|
||||
return BGRColor::Cyan;
|
||||
case Console::Color::LightGray:
|
||||
return BGRColor::LightGray;
|
||||
case Console::Color::DarkGray:
|
||||
return BGRColor::DarkGray;
|
||||
case Console::Color::BrightRed:
|
||||
return BGRColor::BrightRed;
|
||||
case Console::Color::BrightGreen:
|
||||
return BGRColor::BrightGreen;
|
||||
case Console::Color::Yellow:
|
||||
return BGRColor::Yellow;
|
||||
case Console::Color::BrightBlue:
|
||||
return BGRColor::BrightBlue;
|
||||
case Console::Color::BrightMagenta:
|
||||
return BGRColor::BrightMagenta;
|
||||
case Console::Color::BrightCyan:
|
||||
return BGRColor::BrightCyan;
|
||||
case Console::Color::White:
|
||||
return BGRColor::White;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
NonnullRefPtr<FramebufferConsole> FramebufferConsole::initialize(PhysicalAddress framebuffer_address, size_t width, size_t height, size_t pitch)
|
||||
{
|
||||
return adopt_ref(*new FramebufferConsole(framebuffer_address, width, height, pitch));
|
||||
}
|
||||
|
||||
FramebufferConsole::FramebufferConsole(PhysicalAddress framebuffer_address, size_t width, size_t height, size_t pitch)
|
||||
: Console(width, height)
|
||||
, m_framebuffer_address(framebuffer_address)
|
||||
, m_pitch(pitch)
|
||||
{
|
||||
dbgln("Framebuffer Console: taking {} bytes", page_round_up(pitch * height));
|
||||
m_framebuffer_region = MM.allocate_kernel_region(m_framebuffer_address, page_round_up(pitch * height), "Framebuffer Console", Region::Access::Read | Region::Access::Write, Region::Cacheable::Yes);
|
||||
VERIFY(m_framebuffer_region);
|
||||
|
||||
// Just to start cleanly, we clean the entire framebuffer
|
||||
memset(m_framebuffer_region->vaddr().as_ptr(), 0, pitch * height);
|
||||
}
|
||||
|
||||
size_t FramebufferConsole::bytes_per_base_glyph() const
|
||||
{
|
||||
// FIXME: We assume we have 32 bit bpp framebuffer.
|
||||
return 8 * 32;
|
||||
}
|
||||
size_t FramebufferConsole::chars_per_line() const
|
||||
{
|
||||
return width() / bytes_per_base_glyph();
|
||||
}
|
||||
|
||||
void FramebufferConsole::set_cursor(size_t, size_t)
|
||||
{
|
||||
}
|
||||
void FramebufferConsole::hide_cursor()
|
||||
{
|
||||
}
|
||||
void FramebufferConsole::show_cursor()
|
||||
{
|
||||
}
|
||||
|
||||
void FramebufferConsole::clear(size_t x, size_t y, size_t length) const
|
||||
{
|
||||
ScopedSpinLock lock(m_lock);
|
||||
if (x == 0 && length == max_column()) {
|
||||
// if we need to clear the entire row, just clean it with quick memset :)
|
||||
auto* offset_in_framebuffer = (u32*)m_framebuffer_region->vaddr().offset(x * sizeof(u32) * 8).offset(y * 8 * sizeof(u32) * width()).as_ptr();
|
||||
for (size_t current_x = 0; current_x < 8; current_x++) {
|
||||
memset(offset_in_framebuffer, 0, width() * sizeof(u32));
|
||||
offset_in_framebuffer = (u32*)((u8*)offset_in_framebuffer + width() * 4);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (size_t index = 0; index < length; index++) {
|
||||
if (x >= max_column()) {
|
||||
x = 0;
|
||||
y++;
|
||||
if (y >= max_row())
|
||||
y = 0;
|
||||
}
|
||||
clear_glyph(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferConsole::clear_glyph(size_t x, size_t y) const
|
||||
{
|
||||
VERIFY(m_lock.is_locked());
|
||||
auto* offset_in_framebuffer = (u32*)m_framebuffer_region->vaddr().offset(x * sizeof(u32) * 8).offset(y * 8 * sizeof(u32) * width()).as_ptr();
|
||||
for (size_t current_x = 0; current_x < 8; current_x++) {
|
||||
memset(offset_in_framebuffer, 0, 8 * sizeof(u32));
|
||||
offset_in_framebuffer = (u32*)((u8*)offset_in_framebuffer + width() * sizeof(u32));
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferConsole::enable()
|
||||
{
|
||||
ScopedSpinLock lock(m_lock);
|
||||
memset(m_framebuffer_region->vaddr().as_ptr(), 0, height() * width() * sizeof(u32));
|
||||
m_enabled.store(true);
|
||||
}
|
||||
void FramebufferConsole::disable()
|
||||
{
|
||||
ScopedSpinLock lock(m_lock);
|
||||
m_enabled.store(false);
|
||||
}
|
||||
|
||||
void FramebufferConsole::write(size_t x, size_t y, char ch, Color background, Color foreground) const
|
||||
{
|
||||
ScopedSpinLock lock(m_lock);
|
||||
if (!m_enabled.load())
|
||||
return;
|
||||
if (ch == '\r' || ch == '\n') {
|
||||
m_x = 0;
|
||||
m_y += 1;
|
||||
if (m_y >= max_row())
|
||||
m_y = 0;
|
||||
return;
|
||||
}
|
||||
if ((int)ch < 0x20 || (int)ch == 0x7f) {
|
||||
// FIXME: There's no point in printing empty glyphs...
|
||||
// Maybe try to add these special glyphs and print them.
|
||||
return;
|
||||
}
|
||||
clear_glyph(x, y);
|
||||
auto* offset_in_framebuffer = (u32*)m_framebuffer_region->vaddr().offset(x * sizeof(u32) * 8).offset(y * 8 * sizeof(u32) * width()).as_ptr();
|
||||
int current_bitpixels = 0;
|
||||
int current_bitpixel = 0;
|
||||
auto bitmap = font8x8_basic[(int)ch];
|
||||
bool set;
|
||||
BGRColor foreground_color = convert_standard_color_to_bgr_color(foreground);
|
||||
BGRColor background_color = convert_standard_color_to_bgr_color(background);
|
||||
for (current_bitpixels = 0; current_bitpixels < 8; current_bitpixels++) {
|
||||
for (current_bitpixel = 0; current_bitpixel < 8; current_bitpixel++) {
|
||||
set = bitmap[current_bitpixels] & (1 << current_bitpixel);
|
||||
if (set) {
|
||||
offset_in_framebuffer[current_bitpixel] = foreground_color;
|
||||
} else {
|
||||
offset_in_framebuffer[current_bitpixel] = background_color;
|
||||
}
|
||||
}
|
||||
offset_in_framebuffer = (u32*)((u8*)offset_in_framebuffer + width() * 4);
|
||||
}
|
||||
m_x = x + 1;
|
||||
if (m_x >= max_column()) {
|
||||
m_x = 0;
|
||||
m_y = y + 1;
|
||||
if (m_y >= max_row())
|
||||
m_y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferConsole::write(size_t, size_t, String, Color, Color) const
|
||||
{
|
||||
TODO();
|
||||
}
|
||||
void FramebufferConsole::write(size_t x, size_t y, char ch) const
|
||||
{
|
||||
write(x, y, ch, m_default_background_color, m_default_foreground_color);
|
||||
}
|
||||
void FramebufferConsole::write(size_t, size_t, String) const
|
||||
{
|
||||
TODO();
|
||||
}
|
||||
|
||||
void FramebufferConsole::write(char ch) const
|
||||
{
|
||||
write(m_x, m_y, ch, m_default_background_color, m_default_foreground_color);
|
||||
}
|
||||
|
||||
}
|
50
Kernel/Graphics/Console/FramebufferConsole.h
Normal file
50
Kernel/Graphics/Console/FramebufferConsole.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Graphics/Console/Console.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
|
||||
namespace Kernel::Graphics {
|
||||
class FramebufferConsole final : public Console {
|
||||
public:
|
||||
static NonnullRefPtr<FramebufferConsole> initialize(PhysicalAddress, size_t width, size_t height, size_t bpp);
|
||||
|
||||
virtual size_t bytes_per_base_glyph() const override;
|
||||
virtual size_t chars_per_line() const override;
|
||||
|
||||
virtual size_t max_column() const { return m_width / 8; }
|
||||
virtual size_t max_row() const { return m_height / 8; }
|
||||
|
||||
virtual bool is_hardware_paged_capable() const override { return false; }
|
||||
virtual bool has_hardware_cursor() const override { return false; }
|
||||
|
||||
virtual void set_cursor(size_t x, size_t y) override;
|
||||
virtual void hide_cursor() override;
|
||||
virtual void show_cursor() override;
|
||||
|
||||
virtual void clear(size_t x, size_t y, size_t length) const override;
|
||||
virtual void write(size_t x, size_t y, char ch, Color background, Color foreground) const override;
|
||||
virtual void write(size_t x, size_t y, String cstring, Color background, Color foreground) const override;
|
||||
virtual void write(size_t x, size_t y, char ch) const override;
|
||||
virtual void write(size_t x, size_t y, String) const override;
|
||||
virtual void write(char ch) const override;
|
||||
|
||||
virtual void enable() override;
|
||||
virtual void disable() override;
|
||||
|
||||
protected:
|
||||
void clear_glyph(size_t x, size_t y) const;
|
||||
FramebufferConsole(PhysicalAddress, size_t width, size_t height, size_t bpp);
|
||||
OwnPtr<Region> m_framebuffer_region;
|
||||
PhysicalAddress m_framebuffer_address;
|
||||
size_t m_pitch;
|
||||
mutable SpinLock<u8> m_lock;
|
||||
};
|
||||
}
|
203
Kernel/Graphics/Console/TextModeConsole.cpp
Normal file
203
Kernel/Graphics/Console/TextModeConsole.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Graphics/Console/TextModeConsole.h>
|
||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||
#include <Kernel/IO.h>
|
||||
|
||||
namespace Kernel::Graphics {
|
||||
|
||||
UNMAP_AFTER_INIT NonnullRefPtr<TextModeConsole> TextModeConsole::initialize(const VGACompatibleAdapter& adapter)
|
||||
{
|
||||
return adopt_ref(*new TextModeConsole(adapter));
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT TextModeConsole::TextModeConsole(const VGACompatibleAdapter& adapter)
|
||||
: VGAConsole(adapter, VGAConsole::Mode::TextMode, 80, 25)
|
||||
, m_current_vga_window(m_vga_region->vaddr().offset(0x18000).as_ptr())
|
||||
{
|
||||
for (size_t index = 0; index < height(); index++) {
|
||||
clear_vga_row(index);
|
||||
}
|
||||
dbgln("Text mode console initialized!");
|
||||
}
|
||||
|
||||
enum VGAColor : u8 {
|
||||
Black = 0,
|
||||
Blue,
|
||||
Green,
|
||||
Cyan,
|
||||
Red,
|
||||
Magenta,
|
||||
Brown,
|
||||
LightGray,
|
||||
DarkGray,
|
||||
BrightBlue,
|
||||
BrightGreen,
|
||||
BrightCyan,
|
||||
BrightRed,
|
||||
BrightMagenta,
|
||||
Yellow,
|
||||
White,
|
||||
};
|
||||
|
||||
static inline VGAColor convert_standard_color_to_vga_color(Console::Color color)
|
||||
{
|
||||
switch (color) {
|
||||
case Console::Color::Black:
|
||||
return VGAColor::Black;
|
||||
case Console::Color::Red:
|
||||
return VGAColor::Red;
|
||||
case Console::Color::Brown:
|
||||
return VGAColor::Brown;
|
||||
case Console::Color::Blue:
|
||||
return VGAColor::Blue;
|
||||
case Console::Color::Magenta:
|
||||
return VGAColor::Magenta;
|
||||
case Console::Color::Green:
|
||||
return VGAColor::Green;
|
||||
case Console::Color::Cyan:
|
||||
return VGAColor::Cyan;
|
||||
case Console::Color::LightGray:
|
||||
return VGAColor::LightGray;
|
||||
case Console::Color::DarkGray:
|
||||
return VGAColor::DarkGray;
|
||||
case Console::Color::BrightRed:
|
||||
return VGAColor::BrightRed;
|
||||
case Console::Color::BrightGreen:
|
||||
return VGAColor::BrightGreen;
|
||||
case Console::Color::Yellow:
|
||||
return VGAColor::Yellow;
|
||||
case Console::Color::BrightBlue:
|
||||
return VGAColor::BrightBlue;
|
||||
case Console::Color::BrightMagenta:
|
||||
return VGAColor::BrightMagenta;
|
||||
case Console::Color::BrightCyan:
|
||||
return VGAColor::BrightCyan;
|
||||
case Console::Color::White:
|
||||
return VGAColor::White;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
void TextModeConsole::set_cursor(size_t x, size_t y)
|
||||
{
|
||||
ScopedSpinLock main_lock(GraphicsManagement::the().main_vga_lock());
|
||||
ScopedSpinLock lock(m_vga_lock);
|
||||
m_cursor_x = x;
|
||||
m_cursor_y = y;
|
||||
u16 value = m_current_vga_start_address + (y * width() + x);
|
||||
IO::out8(0x3d4, 0x0e);
|
||||
IO::out8(0x3d5, MSB(value));
|
||||
IO::out8(0x3d4, 0x0f);
|
||||
IO::out8(0x3d5, LSB(value));
|
||||
}
|
||||
void TextModeConsole::hide_cursor()
|
||||
{
|
||||
ScopedSpinLock main_lock(GraphicsManagement::the().main_vga_lock());
|
||||
ScopedSpinLock lock(m_vga_lock);
|
||||
IO::out8(0x3D4, 0xA);
|
||||
IO::out8(0x3D5, 0x20);
|
||||
}
|
||||
void TextModeConsole::show_cursor()
|
||||
{
|
||||
ScopedSpinLock main_lock(GraphicsManagement::the().main_vga_lock());
|
||||
ScopedSpinLock lock(m_vga_lock);
|
||||
IO::out8(0x3D4, 0xA);
|
||||
IO::out8(0x3D5, 0x20);
|
||||
}
|
||||
|
||||
void TextModeConsole::clear(size_t x, size_t y, size_t length) const
|
||||
{
|
||||
ScopedSpinLock lock(m_vga_lock);
|
||||
auto* buf = (u16*)(m_current_vga_window + (x * 2) + (y * width() * 2));
|
||||
for (size_t index = 0; index < length; index++) {
|
||||
buf[index] = 0x0720;
|
||||
}
|
||||
}
|
||||
void TextModeConsole::write(size_t x, size_t y, char ch) const
|
||||
{
|
||||
ScopedSpinLock lock(m_vga_lock);
|
||||
auto* buf = (u16*)(m_current_vga_window + (x * 2) + (y * width() * 2));
|
||||
*buf = (m_default_foreground_color << 8) | (m_default_background_color << 12) | ch;
|
||||
m_x = x + 1;
|
||||
if (m_x >= max_column()) {
|
||||
m_x = 0;
|
||||
m_y = y + 1;
|
||||
if (m_y >= max_row())
|
||||
m_y = 0;
|
||||
}
|
||||
}
|
||||
void TextModeConsole::write(size_t x, size_t y, String cstring) const
|
||||
{
|
||||
ScopedSpinLock lock(m_vga_lock);
|
||||
auto* buf = (u16*)(m_current_vga_window + (x * 2) + (y * width() * 2));
|
||||
u16 color_mask = (m_default_foreground_color << 8) | (m_default_background_color << 12);
|
||||
for (size_t index = 0; index < cstring.length(); index++) {
|
||||
buf[index] = color_mask | cstring[index];
|
||||
}
|
||||
m_x = x + cstring.length();
|
||||
if (m_x >= max_column()) {
|
||||
m_x = 0;
|
||||
m_y = y + 1;
|
||||
if (m_y >= max_row())
|
||||
m_y = 0;
|
||||
}
|
||||
}
|
||||
void TextModeConsole::write(size_t x, size_t y, char ch, Color background, Color foreground) const
|
||||
{
|
||||
ScopedSpinLock lock(m_vga_lock);
|
||||
auto* buf = (u16*)(m_current_vga_window + (x * 2) + (y * width() * 2));
|
||||
*buf = foreground << 8 | background << 12 | ch;
|
||||
m_x = x + 1;
|
||||
if (m_x >= max_column()) {
|
||||
m_x = 0;
|
||||
m_y = y + 1;
|
||||
if (m_y >= max_row())
|
||||
m_y = 0;
|
||||
}
|
||||
}
|
||||
void TextModeConsole::write(size_t x, size_t y, String cstring, Color background, Color foreground) const
|
||||
{
|
||||
ScopedSpinLock lock(m_vga_lock);
|
||||
auto* buf = (u16*)(m_current_vga_window + (x * 2) + (y * width() * 2));
|
||||
u16 color_mask = foreground << 8 | background << 12;
|
||||
for (size_t index = 0; index < cstring.length(); index++) {
|
||||
buf[index] = color_mask | cstring[index];
|
||||
}
|
||||
m_x = x + cstring.length();
|
||||
if (m_x >= max_column()) {
|
||||
m_x = 0;
|
||||
m_y = y + 1;
|
||||
if (m_y >= max_row())
|
||||
m_y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TextModeConsole::clear_vga_row(u16 row)
|
||||
{
|
||||
clear(row * width(), width(), width());
|
||||
}
|
||||
|
||||
void TextModeConsole::set_vga_start_row(u16 row)
|
||||
{
|
||||
ScopedSpinLock lock(m_vga_lock);
|
||||
m_vga_start_row = row;
|
||||
m_current_vga_start_address = row * width();
|
||||
m_current_vga_window = m_current_vga_window + row * width() * bytes_per_base_glyph();
|
||||
IO::out8(0x3d4, 0x0c);
|
||||
IO::out8(0x3d5, MSB(m_current_vga_start_address));
|
||||
IO::out8(0x3d4, 0x0d);
|
||||
IO::out8(0x3d5, LSB(m_current_vga_start_address));
|
||||
}
|
||||
|
||||
void TextModeConsole::write(char ch) const
|
||||
{
|
||||
write(m_x, m_y, ch);
|
||||
}
|
||||
|
||||
}
|
50
Kernel/Graphics/Console/TextModeConsole.h
Normal file
50
Kernel/Graphics/Console/TextModeConsole.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Graphics/Console/VGAConsole.h>
|
||||
#include <Kernel/SpinLock.h>
|
||||
|
||||
namespace Kernel::Graphics {
|
||||
class TextModeConsole final : public VGAConsole {
|
||||
public:
|
||||
static NonnullRefPtr<TextModeConsole> initialize(const VGACompatibleAdapter& adapter);
|
||||
virtual size_t chars_per_line() const override { return width(); };
|
||||
|
||||
virtual bool has_hardware_cursor() const override { return true; }
|
||||
virtual bool is_hardware_paged_capable() const override { return true; }
|
||||
|
||||
virtual size_t bytes_per_base_glyph() const override { return 2; }
|
||||
virtual void set_cursor(size_t x, size_t y) override;
|
||||
virtual void hide_cursor() override;
|
||||
virtual void show_cursor() override;
|
||||
virtual void clear(size_t x, size_t y, size_t length) const override;
|
||||
virtual void write(size_t x, size_t y, char ch) const override;
|
||||
virtual void write(size_t x, size_t y, String cstring) const override;
|
||||
virtual void write(size_t x, size_t y, char ch, Color background, Color foreground) const override;
|
||||
virtual void write(size_t x, size_t y, String, Color background, Color foreground) const override;
|
||||
virtual void write(char ch) const override;
|
||||
|
||||
virtual void enable() override { }
|
||||
virtual void disable() override { VERIFY_NOT_REACHED(); }
|
||||
|
||||
private:
|
||||
void clear_vga_row(u16 row);
|
||||
void set_vga_start_row(u16 row);
|
||||
|
||||
explicit TextModeConsole(const VGACompatibleAdapter&);
|
||||
|
||||
mutable SpinLock<u8> m_vga_lock;
|
||||
u16 m_vga_start_row { 0 };
|
||||
u16 m_current_vga_start_address { 0 };
|
||||
u8* m_current_vga_window { nullptr };
|
||||
u16 m_cursor_x { 0 };
|
||||
u16 m_cursor_y { 0 };
|
||||
};
|
||||
}
|
19
Kernel/Graphics/Console/VGAConsole.cpp
Normal file
19
Kernel/Graphics/Console/VGAConsole.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Graphics/Console/VGAConsole.h>
|
||||
|
||||
namespace Kernel::Graphics {
|
||||
|
||||
UNMAP_AFTER_INIT VGAConsole::VGAConsole(const VGACompatibleAdapter& adapter, Mode mode, size_t width, size_t height)
|
||||
: Console(width, height)
|
||||
, m_vga_region(MM.allocate_kernel_region(PhysicalAddress(0xa0000), page_round_up(0xc0000 - 0xa0000), "VGA Display", Region::Access::Read | Region::Access::Write).release_nonnull())
|
||||
, m_adapter(adapter)
|
||||
, m_mode(mode)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
39
Kernel/Graphics/Console/VGAConsole.h
Normal file
39
Kernel/Graphics/Console/VGAConsole.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Graphics/Console/Console.h>
|
||||
#include <Kernel/Graphics/VGACompatibleAdapter.h>
|
||||
|
||||
namespace Kernel::Graphics {
|
||||
class VGAConsole : public Console {
|
||||
public:
|
||||
// Note: these are the modes we will support and only these
|
||||
enum class Mode {
|
||||
TextMode = 1, // Text Mode
|
||||
Colored256, // 320x200 256 color mode
|
||||
Colored16, // 640x480 16 color mode
|
||||
};
|
||||
|
||||
public:
|
||||
static NonnullRefPtr<VGAConsole> initialize(const VGACompatibleAdapter&, Mode, size_t width, size_t height);
|
||||
|
||||
virtual bool is_hardware_paged_capable() const override { return false; }
|
||||
virtual bool has_hardware_cursor() const override { return false; }
|
||||
|
||||
virtual ~VGAConsole() = default;
|
||||
|
||||
protected:
|
||||
VGAConsole(const VGACompatibleAdapter&, Mode, size_t width, size_t height);
|
||||
|
||||
NonnullOwnPtr<Region> m_vga_region;
|
||||
NonnullRefPtr<VGACompatibleAdapter> m_adapter;
|
||||
const Mode m_mode;
|
||||
};
|
||||
}
|
@ -16,6 +16,8 @@
|
||||
#include <LibC/errno_numbers.h>
|
||||
#include <LibC/sys/ioctl_numbers.h>
|
||||
|
||||
#include <Kernel/Panic.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KResultOr<Region*> FramebufferDevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared)
|
||||
@ -28,16 +30,51 @@ KResultOr<Region*> FramebufferDevice::mmap(Process& process, FileDescription&, c
|
||||
if (range.size() != page_round_up(framebuffer_size_in_bytes()))
|
||||
return EOVERFLOW;
|
||||
|
||||
auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes());
|
||||
// FIXME: We rely on the fact that only the WindowServer will mmap the framebuffer
|
||||
// and only once when starting to work with it. If other program wants to do so, we need to fix this.
|
||||
VERIFY(!m_userspace_framebuffer_region);
|
||||
VERIFY(!m_userspace_real_framebuffer_vmobject);
|
||||
|
||||
auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, page_round_up(framebuffer_size_in_bytes()));
|
||||
if (!vmobject)
|
||||
return ENOMEM;
|
||||
return process.space().allocate_region_with_vmobject(
|
||||
m_userspace_real_framebuffer_vmobject = vmobject;
|
||||
|
||||
auto result = process.space().allocate_region_with_vmobject(
|
||||
range,
|
||||
vmobject.release_nonnull(),
|
||||
0,
|
||||
"Framebuffer",
|
||||
prot,
|
||||
shared);
|
||||
if (!result.is_error()) {
|
||||
m_userspace_framebuffer_region = result.value();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void FramebufferDevice::dectivate_writes()
|
||||
{
|
||||
ScopedSpinLock lock(m_activation_lock);
|
||||
if (!m_userspace_framebuffer_region)
|
||||
return;
|
||||
memcpy(m_swapped_framebuffer_region->vaddr().as_ptr(), m_real_framebuffer_region->vaddr().as_ptr(), page_round_up(framebuffer_size_in_bytes()));
|
||||
auto vmobject = m_swapped_framebuffer_vmobject;
|
||||
m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
|
||||
m_userspace_framebuffer_region->remap();
|
||||
}
|
||||
void FramebufferDevice::activate_writes()
|
||||
{
|
||||
ScopedSpinLock lock(m_activation_lock);
|
||||
if (!m_userspace_framebuffer_region || !m_real_framebuffer_vmobject)
|
||||
return;
|
||||
// restore the image we had in the void area
|
||||
// FIXME: if we happen to have multiple Framebuffers that are writing to that location
|
||||
// we will experience glitches...
|
||||
memcpy(m_real_framebuffer_region->vaddr().as_ptr(), m_swapped_framebuffer_region->vaddr().as_ptr(), page_round_up(framebuffer_size_in_bytes()));
|
||||
auto vmobject = m_userspace_real_framebuffer_vmobject;
|
||||
m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
|
||||
m_userspace_framebuffer_region->remap();
|
||||
}
|
||||
|
||||
String FramebufferDevice::device_name() const
|
||||
@ -45,6 +82,18 @@ String FramebufferDevice::device_name() const
|
||||
return String::formatted("fb{}", minor());
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT void FramebufferDevice::initialize()
|
||||
{
|
||||
m_real_framebuffer_vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, page_round_up(framebuffer_size_in_bytes()));
|
||||
VERIFY(m_real_framebuffer_vmobject);
|
||||
m_real_framebuffer_region = MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, framebuffer_size_in_bytes(), "Framebuffer", Region::Access::Read | Region::Access::Write);
|
||||
VERIFY(m_real_framebuffer_region);
|
||||
m_swapped_framebuffer_vmobject = AnonymousVMObject::create_with_size(page_round_up(framebuffer_size_in_bytes()), AllocationStrategy::AllocateNow);
|
||||
VERIFY(m_swapped_framebuffer_vmobject);
|
||||
m_swapped_framebuffer_region = MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, page_round_up(framebuffer_size_in_bytes()), "Framebuffer Swap (Blank)", Region::Access::Read | Region::Access::Write);
|
||||
VERIFY(m_swapped_framebuffer_region);
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(PhysicalAddress addr, size_t pitch, size_t width, size_t height)
|
||||
: BlockDevice(29, GraphicsManagement::the().current_minor_number())
|
||||
, m_framebuffer_address(addr)
|
||||
@ -52,6 +101,10 @@ UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(PhysicalAddress addr, size
|
||||
, m_framebuffer_width(width)
|
||||
, m_framebuffer_height(height)
|
||||
{
|
||||
VERIFY(!m_framebuffer_address.is_null());
|
||||
VERIFY(m_framebuffer_pitch);
|
||||
VERIFY(m_framebuffer_width);
|
||||
VERIFY(m_framebuffer_height);
|
||||
dbgln("Framebuffer {}: address={}, pitch={}, width={}, height={}", minor(), addr, pitch, width, height);
|
||||
}
|
||||
|
||||
|
@ -6,11 +6,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Devices/BlockDevice.h>
|
||||
#include <Kernel/Graphics/GraphicsDevice.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
#include <Kernel/SpinLock.h>
|
||||
#include <Kernel/VM/AnonymousVMObject.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@ -24,9 +27,12 @@ public:
|
||||
virtual mode_t required_mode() const override { return 0660; }
|
||||
virtual String device_name() const override;
|
||||
|
||||
virtual void dectivate_writes();
|
||||
virtual void activate_writes();
|
||||
virtual size_t framebuffer_size_in_bytes() const { return m_framebuffer_pitch * m_framebuffer_height; }
|
||||
|
||||
virtual ~FramebufferDevice() {};
|
||||
void initialize();
|
||||
|
||||
protected:
|
||||
virtual bool set_resolution(size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch);
|
||||
@ -44,6 +50,17 @@ protected:
|
||||
size_t m_framebuffer_pitch { 0 };
|
||||
size_t m_framebuffer_width { 0 };
|
||||
size_t m_framebuffer_height { 0 };
|
||||
|
||||
private:
|
||||
SpinLock<u8> m_activation_lock;
|
||||
|
||||
RefPtr<AnonymousVMObject> m_real_framebuffer_vmobject;
|
||||
RefPtr<AnonymousVMObject> m_swapped_framebuffer_vmobject;
|
||||
OwnPtr<Region> m_real_framebuffer_region;
|
||||
OwnPtr<Region> m_swapped_framebuffer_region;
|
||||
|
||||
RefPtr<AnonymousVMObject> m_userspace_real_framebuffer_vmobject;
|
||||
Region* m_userspace_framebuffer_region { nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -25,9 +25,15 @@ public:
|
||||
virtual ~GraphicsDevice() = default;
|
||||
virtual void initialize_framebuffer_devices() = 0;
|
||||
virtual Type type() const = 0;
|
||||
virtual void enable_consoles() = 0;
|
||||
virtual void disable_consoles() = 0;
|
||||
bool consoles_enabled() const { return m_consoles_enabled; }
|
||||
virtual bool framebuffer_devices_initialized() const = 0;
|
||||
|
||||
protected:
|
||||
GraphicsDevice() = default;
|
||||
|
||||
bool m_consoles_enabled { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -9,10 +9,14 @@
|
||||
#include <Kernel/CommandLine.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Graphics/BochsGraphicsAdapter.h>
|
||||
#include <Kernel/Graphics/Console/FramebufferConsole.h>
|
||||
#include <Kernel/Graphics/Console/TextModeConsole.h>
|
||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||
#include <Kernel/Graphics/IntelNativeGraphicsAdapter.h>
|
||||
#include <Kernel/Graphics/VGACompatibleAdapter.h>
|
||||
#include <Kernel/IO.h>
|
||||
#include <Kernel/Multiboot.h>
|
||||
#include <Kernel/VM/AnonymousVMObject.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@ -29,10 +33,24 @@ bool GraphicsManagement::is_initialized()
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT GraphicsManagement::GraphicsManagement()
|
||||
: m_textmode_enabled(kernel_command_line().is_text_mode())
|
||||
: m_vga_font_region(MM.allocate_kernel_region(PAGE_SIZE, "VGA font", Region::Access::Read | Region::Access::Write, AllocationStrategy::AllocateNow).release_nonnull())
|
||||
, m_framebuffer_devices_allowed(!kernel_command_line().is_no_framebuffer_devices_mode())
|
||||
{
|
||||
}
|
||||
|
||||
void GraphicsManagement::deactivate_graphical_mode()
|
||||
{
|
||||
for (auto& graphics_device : m_graphics_devices) {
|
||||
graphics_device.enable_consoles();
|
||||
}
|
||||
}
|
||||
void GraphicsManagement::activate_graphical_mode()
|
||||
{
|
||||
for (auto& graphics_device : m_graphics_devices) {
|
||||
graphics_device.disable_consoles();
|
||||
}
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT RefPtr<GraphicsDevice> GraphicsManagement::determine_graphics_device(PCI::Address address, PCI::ID id) const
|
||||
{
|
||||
if ((id.vendor_id == 0x1234 && id.device_id == 0x1111) || (id.vendor_id == 0x80ee && id.device_id == 0xbeef)) {
|
||||
@ -44,31 +62,93 @@ UNMAP_AFTER_INIT RefPtr<GraphicsDevice> GraphicsManagement::determine_graphics_d
|
||||
if (!adapter.is_null())
|
||||
return adapter;
|
||||
}
|
||||
VERIFY(multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB || multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT);
|
||||
return VGACompatibleAdapter::initialize_with_preset_resolution(address,
|
||||
PhysicalAddress((u32)(multiboot_info_ptr->framebuffer_addr)),
|
||||
multiboot_info_ptr->framebuffer_pitch,
|
||||
multiboot_info_ptr->framebuffer_width,
|
||||
multiboot_info_ptr->framebuffer_height);
|
||||
if (multiboot_info_ptr->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB) {
|
||||
dmesgln("Graphics: Using a preset resolution from the bootloader");
|
||||
return VGACompatibleAdapter::initialize_with_preset_resolution(address,
|
||||
PhysicalAddress((u32)(multiboot_info_ptr->framebuffer_addr)),
|
||||
multiboot_info_ptr->framebuffer_pitch,
|
||||
multiboot_info_ptr->framebuffer_width,
|
||||
multiboot_info_ptr->framebuffer_height);
|
||||
}
|
||||
return VGACompatibleAdapter::initialize(address);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT bool GraphicsManagement::initialize()
|
||||
{
|
||||
if (kernel_command_line().is_text_mode()) {
|
||||
dbgln("Text mode enabled");
|
||||
return false;
|
||||
|
||||
/* Explanation on the flow when not requesting to force not creating any
|
||||
* framebuffer devices:
|
||||
* If the user wants to use a Console instead of the graphical environment,
|
||||
* they doesn't need to request text mode.
|
||||
* Graphical mode might not be accessible on bare-metal hardware because
|
||||
* the bootloader didn't set a framebuffer and we don't have a native driver
|
||||
* to set a framebuffer for it. We don't have VBE modesetting capabilities
|
||||
* in the kernel yet, so what will happen is one of the following situations:
|
||||
* 1. The bootloader didn't specify settings of a pre-set framebuffer. The
|
||||
* kernel has a native driver for a detected display adapter, therefore
|
||||
* the kernel can still set a framebuffer.
|
||||
* 2. The bootloader specified settings of a pre-set framebuffer, and the
|
||||
* kernel has a native driver for a detected display adapter, therefore
|
||||
* the kernel can still set a framebuffer and change the settings of it.
|
||||
* In that situation, the kernel will simply ignore the Multiboot pre-set
|
||||
* framebuffer.
|
||||
* 2. The bootloader specified settings of a pre-set framebuffer, and the
|
||||
* kernel does not have a native driver for a detected display adapter,
|
||||
* therefore the kernel will use the pre-set framebuffer. Modesetting is not
|
||||
* availabe in this situation.
|
||||
* 3. The bootloader didn't specify settings of a pre-set framebuffer, and
|
||||
* the kernel does not have a native driver for a detected display adapter,
|
||||
* therefore the kernel will try to initialize a VGA text mode console.
|
||||
* In that situation, the kernel will assume that VGA text mode was already
|
||||
* initialized, but will still try to modeset it. No switching to graphical
|
||||
* enviroment is allowed in this case.
|
||||
*
|
||||
* By default, the kernel assumes that no framebuffer was created until it
|
||||
* was proven that there's an existing framebuffer or we can modeset the
|
||||
* screen resolution to create a framebuffer.
|
||||
*
|
||||
* If the user requests to force no initialization of framebuffer devices
|
||||
* the same flow above will happen, except that no framebuffer device will
|
||||
* be created, so SystemServer will not try to initialize WindowServer.
|
||||
*/
|
||||
|
||||
if (kernel_command_line().is_no_framebuffer_devices_mode()) {
|
||||
dbgln("Forcing no initialization of framebuffer devices");
|
||||
}
|
||||
|
||||
PCI::enumerate([&](const PCI::Address& address, PCI::ID id) {
|
||||
// Note: Each graphics controller will try to set its native screen resolution
|
||||
// upon creation. Later on, if we don't want to have framebuffer devices, a
|
||||
// framebuffer console will take the control instead.
|
||||
auto adapter = determine_graphics_device(address, id);
|
||||
if (!adapter)
|
||||
return;
|
||||
adapter->initialize_framebuffer_devices();
|
||||
m_graphics_devices.append(adapter.release_nonnull());
|
||||
|
||||
// If IO space is enabled, this VGA adapter is operating in VGA mode.
|
||||
if (adapter->type() == GraphicsDevice::Type::VGACompatible && PCI::is_io_space_enabled(address)) {
|
||||
VERIFY(m_vga_adapter.is_null());
|
||||
dbgln("Graphics adapter @ {} is operating in VGA mode", address);
|
||||
m_vga_adapter = adapter;
|
||||
}
|
||||
auto display_adapter = adapter.release_nonnull();
|
||||
m_graphics_devices.append(display_adapter);
|
||||
if (!m_framebuffer_devices_allowed) {
|
||||
display_adapter->enable_consoles();
|
||||
return;
|
||||
}
|
||||
display_adapter->initialize_framebuffer_devices();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GraphicsManagement::framebuffer_devices_exist() const
|
||||
{
|
||||
for (auto& graphics_device : m_graphics_devices) {
|
||||
if (graphics_device.framebuffer_devices_initialized())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -6,16 +6,26 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/NonnullRefPtrVector.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Graphics/Console/Console.h>
|
||||
#include <Kernel/Graphics/GraphicsDevice.h>
|
||||
#include <Kernel/Graphics/VGACompatibleAdapter.h>
|
||||
#include <Kernel/PCI/Definitions.h>
|
||||
#include <Kernel/VM/Region.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class BochsGraphicsAdapter;
|
||||
class IntelNativeGraphicsAdapter;
|
||||
class VGACompatibleAdapter;
|
||||
class GraphicsManagement {
|
||||
AK_MAKE_ETERNAL;
|
||||
friend class BochsGraphicsAdapter;
|
||||
friend class IntelNativeGraphicsAdapter;
|
||||
friend class VGACompatibleAdapter;
|
||||
AK_MAKE_ETERNAL
|
||||
|
||||
public:
|
||||
static GraphicsManagement& the();
|
||||
@ -25,14 +35,28 @@ public:
|
||||
unsigned current_minor_number() { return m_current_minor_number++; };
|
||||
GraphicsManagement();
|
||||
|
||||
bool is_text_mode_enabled() const { return m_textmode_enabled; }
|
||||
bool framebuffer_devices_allowed() const { return m_framebuffer_devices_allowed; }
|
||||
bool framebuffer_devices_exist() const;
|
||||
|
||||
SpinLock<u8>& main_vga_lock() { return m_main_vga_lock; }
|
||||
RefPtr<Graphics::Console> console() const { return m_console; }
|
||||
|
||||
void deactivate_graphical_mode();
|
||||
void activate_graphical_mode();
|
||||
|
||||
private:
|
||||
RefPtr<GraphicsDevice> determine_graphics_device(PCI::Address address, PCI::ID id) const;
|
||||
|
||||
NonnullRefPtrVector<GraphicsDevice> m_graphics_devices;
|
||||
NonnullOwnPtr<Region> m_vga_font_region;
|
||||
RefPtr<Graphics::Console> m_console;
|
||||
|
||||
// Note: there could be multiple VGA adapters, but only one can operate in VGA mode
|
||||
RefPtr<VGACompatibleAdapter> m_vga_adapter;
|
||||
unsigned m_current_minor_number { 0 };
|
||||
bool m_textmode_enabled;
|
||||
const bool m_framebuffer_devices_allowed;
|
||||
|
||||
SpinLock<u8> m_main_vga_lock;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,9 @@
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Graphics/Console/FramebufferConsole.h>
|
||||
#include <Kernel/Graphics/Definitions.h>
|
||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||
#include <Kernel/Graphics/IntelNativeGraphicsAdapter.h>
|
||||
#include <Kernel/IO.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
@ -165,7 +167,7 @@ Optional<IntelNativeGraphicsAdapter::PLLSettings> IntelNativeGraphicsAdapter::cr
|
||||
}
|
||||
|
||||
IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address)
|
||||
: PCI::DeviceController(address)
|
||||
: VGACompatibleAdapter(address)
|
||||
, m_registers(PCI::get_BAR0(address) & 0xfffffffc)
|
||||
, m_framebuffer_addr(PCI::get_BAR2(address) & 0xfffffffc)
|
||||
{
|
||||
@ -182,10 +184,24 @@ IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address)
|
||||
set_gmbus_pin_pair(GMBusPinPair::DedicatedAnalog);
|
||||
}
|
||||
gmbus_read_edid();
|
||||
auto modesetting = calculate_modesetting_from_edid(m_crt_edid, 0);
|
||||
dmesgln("Intel Native Graphics Adapter @ {}, preferred resolution is {:d}x{:d}", address, modesetting.horizontal.active, modesetting.vertical.active);
|
||||
|
||||
auto modesetting = calculate_modesetting_from_edid(m_crt_edid, 0);
|
||||
dmesgln("Intel Native Graphics Adapter @ {}, preferred resolution is {:d}x{:d}", pci_address(), modesetting.horizontal.active, modesetting.vertical.active);
|
||||
set_crt_resolution(modesetting.horizontal.active, modesetting.vertical.active);
|
||||
auto framebuffer_address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
|
||||
VERIFY(!framebuffer_address.is_null());
|
||||
VERIFY(m_framebuffer_pitch != 0);
|
||||
VERIFY(m_framebuffer_height != 0);
|
||||
VERIFY(m_framebuffer_width != 0);
|
||||
m_framebuffer_console = Graphics::FramebufferConsole::initialize(framebuffer_address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch);
|
||||
// FIXME: This is a very wrong way to do this...
|
||||
GraphicsManagement::the().m_console = m_framebuffer_console;
|
||||
}
|
||||
|
||||
void IntelNativeGraphicsAdapter::enable_vga_plane()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
}
|
||||
|
||||
static inline const char* convert_register_index_to_string(IntelGraphics::RegisterIndex index)
|
||||
@ -269,6 +285,7 @@ bool IntelNativeGraphicsAdapter::pipe_a_enabled() const
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
return read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30);
|
||||
}
|
||||
|
||||
bool IntelNativeGraphicsAdapter::pipe_b_enabled() const
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
@ -396,7 +413,7 @@ bool IntelNativeGraphicsAdapter::set_crt_resolution(size_t width, size_t height)
|
||||
VERIFY_NOT_REACHED();
|
||||
auto settings = pll_settings.value();
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2);
|
||||
set_dpll_registers(pll_settings.value(), dac_multiplier);
|
||||
enable_dpll_without_vga(pll_settings.value(), dac_multiplier);
|
||||
set_display_timings(modesetting);
|
||||
auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
|
||||
VERIFY(!address.is_null());
|
||||
@ -404,7 +421,7 @@ bool IntelNativeGraphicsAdapter::set_crt_resolution(size_t width, size_t height)
|
||||
|
||||
m_framebuffer_width = width;
|
||||
m_framebuffer_height = height;
|
||||
m_framebuffer_stride = width * 4;
|
||||
m_framebuffer_pitch = width * 4;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -538,7 +555,7 @@ void IntelNativeGraphicsAdapter::enable_primary_plane(PhysicalAddress fb_address
|
||||
write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, (read_from_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl) & (~(0b1111 << 26))) | (0b0110 << 26) | (1 << 31));
|
||||
}
|
||||
|
||||
void IntelNativeGraphicsAdapter::set_dpll_registers(const PLLSettings& settings, size_t dac_multiplier)
|
||||
void IntelNativeGraphicsAdapter::set_dpll_registers(const PLLSettings& settings)
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
@ -546,6 +563,14 @@ void IntelNativeGraphicsAdapter::set_dpll_registers(const PLLSettings& settings,
|
||||
write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA1, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16));
|
||||
|
||||
write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & ~0x80000000);
|
||||
}
|
||||
|
||||
void IntelNativeGraphicsAdapter::enable_dpll_without_vga(const PLLSettings& settings, size_t dac_multiplier)
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
|
||||
set_dpll_registers(settings);
|
||||
|
||||
IO::delay(200);
|
||||
|
||||
@ -597,9 +622,10 @@ void IntelNativeGraphicsAdapter::initialize_framebuffer_devices()
|
||||
{
|
||||
auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
|
||||
VERIFY(!address.is_null());
|
||||
VERIFY(m_framebuffer_stride != 0);
|
||||
VERIFY(m_framebuffer_pitch != 0);
|
||||
VERIFY(m_framebuffer_height != 0);
|
||||
VERIFY(m_framebuffer_width != 0);
|
||||
m_framebuffer = m_framebuffer = RawFramebufferDevice::create(*this, address, m_framebuffer_stride, m_framebuffer_width, m_framebuffer_height);
|
||||
m_framebuffer_device = RawFramebufferDevice::create(*this, address, m_framebuffer_pitch, m_framebuffer_width, m_framebuffer_height);
|
||||
m_framebuffer_device->initialize();
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,8 @@
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Graphics/Definitions.h>
|
||||
#include <Kernel/Graphics/GraphicsDevice.h>
|
||||
#include <Kernel/Graphics/RawFramebufferDevice.h>
|
||||
#include <Kernel/Graphics/VGACompatibleAdapter.h>
|
||||
#include <Kernel/PCI/DeviceController.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
|
||||
@ -47,8 +47,7 @@ enum RegisterIndex {
|
||||
}
|
||||
|
||||
class IntelNativeGraphicsAdapter final
|
||||
: public GraphicsDevice
|
||||
, public PCI::DeviceController {
|
||||
: public VGACompatibleAdapter {
|
||||
AK_MAKE_ETERNAL
|
||||
public:
|
||||
struct PLLSettings {
|
||||
@ -129,6 +128,7 @@ private:
|
||||
void enable_output(PhysicalAddress fb_address, size_t width);
|
||||
|
||||
void disable_vga_emulation();
|
||||
void enable_vga_plane();
|
||||
|
||||
void disable_dac_output();
|
||||
void enable_dac_output();
|
||||
@ -138,7 +138,9 @@ private:
|
||||
void disable_pipe_b();
|
||||
void disable_dpll();
|
||||
|
||||
void set_dpll_registers(const PLLSettings&, size_t dac_multiplier);
|
||||
void set_dpll_registers(const PLLSettings&);
|
||||
|
||||
void enable_dpll_without_vga(const PLLSettings&, size_t dac_multiplier);
|
||||
void set_display_timings(const Graphics::Modesetting&);
|
||||
void enable_pipe_a();
|
||||
void set_framebuffer_parameters(size_t, size_t);
|
||||
@ -166,15 +168,7 @@ private:
|
||||
Graphics::VideoInfoBlock m_crt_edid;
|
||||
const PhysicalAddress m_registers;
|
||||
const PhysicalAddress m_framebuffer_addr;
|
||||
|
||||
OwnPtr<Region> m_registers_region;
|
||||
|
||||
size_t m_framebuffer_width { 0 };
|
||||
size_t m_framebuffer_height { 0 };
|
||||
size_t m_framebuffer_stride { 0 };
|
||||
|
||||
protected:
|
||||
RefPtr<RawFramebufferDevice> m_framebuffer;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,11 @@
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Graphics/Console/FramebufferConsole.h>
|
||||
#include <Kernel/Graphics/Console/TextModeConsole.h>
|
||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||
#include <Kernel/Graphics/VGACompatibleAdapter.h>
|
||||
#include <Kernel/IO.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@ -13,19 +17,55 @@ UNMAP_AFTER_INIT NonnullRefPtr<VGACompatibleAdapter> VGACompatibleAdapter::initi
|
||||
return adopt_ref(*new VGACompatibleAdapter(address, m_framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch));
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT NonnullRefPtr<VGACompatibleAdapter> VGACompatibleAdapter::initialize(PCI::Address address)
|
||||
{
|
||||
return adopt_ref(*new VGACompatibleAdapter(address));
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT void VGACompatibleAdapter::initialize_framebuffer_devices()
|
||||
{
|
||||
// We might not have any pre-set framebuffer, so if that's the case - don't try to initialize one.
|
||||
if (m_framebuffer_address.is_null())
|
||||
return;
|
||||
VERIFY(m_framebuffer_width);
|
||||
VERIFY(m_framebuffer_width != 0);
|
||||
VERIFY(m_framebuffer_height != 0);
|
||||
VERIFY(m_framebuffer_pitch != 0);
|
||||
m_framebuffer_device = RawFramebufferDevice::create(*this, m_framebuffer_address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch);
|
||||
m_framebuffer_device->initialize();
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT VGACompatibleAdapter::VGACompatibleAdapter(PCI::Address address)
|
||||
: PCI::DeviceController(address)
|
||||
{
|
||||
m_framebuffer_console = Graphics::TextModeConsole::initialize(*this);
|
||||
// FIXME: This is a very wrong way to do this...
|
||||
GraphicsManagement::the().m_console = m_framebuffer_console;
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT VGACompatibleAdapter::VGACompatibleAdapter(PCI::Address address, PhysicalAddress framebuffer_address, size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch)
|
||||
: PCI::DeviceController(address)
|
||||
, m_framebuffer_address(framebuffer_address)
|
||||
, m_framebuffer_width(framebuffer_width)
|
||||
, m_framebuffer_height(framebuffer_height)
|
||||
, m_framebuffer_pitch(framebuffer_pitch)
|
||||
{
|
||||
m_framebuffer = RawFramebufferDevice::create(*this, framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch);
|
||||
m_framebuffer_console = Graphics::FramebufferConsole::initialize(framebuffer_address, framebuffer_width, framebuffer_height, framebuffer_pitch);
|
||||
}
|
||||
|
||||
void VGACompatibleAdapter::enable_consoles()
|
||||
{
|
||||
VERIFY(m_framebuffer_console);
|
||||
if (m_framebuffer_device)
|
||||
m_framebuffer_device->dectivate_writes();
|
||||
m_framebuffer_console->enable();
|
||||
}
|
||||
void VGACompatibleAdapter::disable_consoles()
|
||||
{
|
||||
VERIFY(m_framebuffer_device);
|
||||
VERIFY(m_framebuffer_console);
|
||||
m_framebuffer_console->disable();
|
||||
m_framebuffer_device->activate_writes();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Graphics/Console/Console.h>
|
||||
#include <Kernel/Graphics/GraphicsDevice.h>
|
||||
#include <Kernel/Graphics/RawFramebufferDevice.h>
|
||||
#include <Kernel/PCI/DeviceController.h>
|
||||
@ -15,11 +16,14 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class VGACompatibleAdapter final : public GraphicsDevice
|
||||
class VGACompatibleAdapter : public GraphicsDevice
|
||||
, public PCI::DeviceController {
|
||||
AK_MAKE_ETERNAL
|
||||
public:
|
||||
static NonnullRefPtr<VGACompatibleAdapter> initialize_with_preset_resolution(PCI::Address, PhysicalAddress, size_t framebuffer_width, size_t framebuffer_height, size_t framebuffer_pitch);
|
||||
static NonnullRefPtr<VGACompatibleAdapter> initialize(PCI::Address);
|
||||
|
||||
virtual bool framebuffer_devices_initialized() const override { return !m_framebuffer_device.is_null(); }
|
||||
|
||||
protected:
|
||||
explicit VGACompatibleAdapter(PCI::Address);
|
||||
@ -31,8 +35,17 @@ private:
|
||||
virtual void initialize_framebuffer_devices() override;
|
||||
virtual Type type() const override { return Type::VGACompatible; }
|
||||
|
||||
virtual void enable_consoles() override;
|
||||
virtual void disable_consoles() override;
|
||||
|
||||
protected:
|
||||
RefPtr<RawFramebufferDevice> m_framebuffer;
|
||||
PhysicalAddress m_framebuffer_address;
|
||||
size_t m_framebuffer_width { 0 };
|
||||
size_t m_framebuffer_height { 0 };
|
||||
size_t m_framebuffer_pitch { 0 };
|
||||
|
||||
RefPtr<RawFramebufferDevice> m_framebuffer_device;
|
||||
RefPtr<Graphics::Console> m_framebuffer_console;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ namespace Kernel {
|
||||
|
||||
void __panic(const char* file, unsigned int line, const char* function)
|
||||
{
|
||||
dmesgln("at {}:{} in {}", file, line, function);
|
||||
critical_dmesgln("at {}:{} in {}", file, line, function);
|
||||
dump_backtrace();
|
||||
Processor::halt();
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ namespace Kernel {
|
||||
|
||||
#define PANIC(...) \
|
||||
do { \
|
||||
dmesgln("KERNEL PANIC! :^("); \
|
||||
dmesgln(__VA_ARGS__); \
|
||||
critical_dmesgln("KERNEL PANIC! :^("); \
|
||||
critical_dmesgln(__VA_ARGS__); \
|
||||
__panic(__FILE__, __LINE__, __PRETTY_FUNCTION__); \
|
||||
} while (0)
|
||||
|
||||
|
72
Kernel/TTY/ConsoleManagement.cpp
Normal file
72
Kernel/TTY/ConsoleManagement.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Singleton.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||
#include <Kernel/TTY/ConsoleManagement.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
static AK::Singleton<ConsoleManagement> s_the;
|
||||
|
||||
bool ConsoleManagement::is_initialized()
|
||||
{
|
||||
if (!s_the.is_initialized())
|
||||
return false;
|
||||
if (s_the->m_consoles.is_empty())
|
||||
return false;
|
||||
if (s_the->m_active_console.is_null())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ConsoleManagement& ConsoleManagement::the()
|
||||
{
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT ConsoleManagement::ConsoleManagement()
|
||||
{
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT void ConsoleManagement::initialize()
|
||||
{
|
||||
for (size_t index = 0; index < 4; index++) {
|
||||
m_consoles.append(VirtualConsole::create(index));
|
||||
}
|
||||
// Note: By default the active console is the first one.
|
||||
m_active_console = m_consoles[0];
|
||||
ScopedSpinLock lock(m_lock);
|
||||
m_active_console->set_active(true);
|
||||
}
|
||||
|
||||
void ConsoleManagement::switch_to(unsigned index)
|
||||
{
|
||||
ScopedSpinLock lock(m_lock);
|
||||
VERIFY(m_active_console);
|
||||
VERIFY(index < m_consoles.size());
|
||||
if (m_active_console->index() == index)
|
||||
return;
|
||||
|
||||
bool was_graphical = m_active_console->is_graphical();
|
||||
m_active_console->set_active(false);
|
||||
m_active_console = m_consoles[index];
|
||||
dbgln_if(VIRTUAL_CONSOLE_DEBUG, "Console: Switch to {}", index);
|
||||
|
||||
// Before setting current console to be "active", switch between graphical mode to "textual" mode
|
||||
// if needed. This will ensure we clear the screen and also that WindowServer won't print anything
|
||||
// in between.
|
||||
if (m_active_console->is_graphical() && !was_graphical) {
|
||||
GraphicsManagement::the().activate_graphical_mode();
|
||||
}
|
||||
if (!m_active_console->is_graphical() && was_graphical) {
|
||||
GraphicsManagement::the().deactivate_graphical_mode();
|
||||
}
|
||||
m_active_console->set_active(true);
|
||||
}
|
||||
|
||||
}
|
41
Kernel/TTY/ConsoleManagement.h
Normal file
41
Kernel/TTY/ConsoleManagement.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/NonnullRefPtrVector.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/TTY/VirtualConsole.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class ConsoleManagement {
|
||||
AK_MAKE_ETERNAL;
|
||||
friend class VirtualConsole;
|
||||
|
||||
public:
|
||||
ConsoleManagement();
|
||||
|
||||
static bool is_initialized();
|
||||
static ConsoleManagement& the();
|
||||
|
||||
void switch_to(unsigned);
|
||||
void initialize();
|
||||
|
||||
NonnullRefPtr<VirtualConsole> first_tty() const { return m_consoles[0]; }
|
||||
NonnullRefPtr<VirtualConsole> debug_tty() const { return m_consoles[1]; }
|
||||
|
||||
RecursiveSpinLock& tty_write_lock() { return m_tty_write_lock; }
|
||||
|
||||
private:
|
||||
NonnullRefPtrVector<VirtualConsole> m_consoles;
|
||||
RefPtr<VirtualConsole> m_active_console;
|
||||
SpinLock<u8> m_lock;
|
||||
RecursiveSpinLock m_tty_write_lock;
|
||||
};
|
||||
|
||||
};
|
@ -1,59 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "VirtualConsole.h"
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/String.h>
|
||||
#include <Kernel/Arch/x86/CPU.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Devices/HID/HIDManagement.h>
|
||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||
#include <Kernel/Heap/kmalloc.h>
|
||||
#include <Kernel/IO.h>
|
||||
#include <Kernel/StdLib.h>
|
||||
#include <Kernel/TTY/ConsoleManagement.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
static u8* s_vga_buffer;
|
||||
static VirtualConsole* s_consoles[s_max_virtual_consoles];
|
||||
static int s_active_console;
|
||||
static RecursiveSpinLock s_lock;
|
||||
|
||||
void VirtualConsole::flush_vga_cursor()
|
||||
ConsoleImpl::ConsoleImpl(VirtualConsole& client)
|
||||
: Terminal(client)
|
||||
{
|
||||
u16 value = m_current_vga_start_address + (m_terminal.cursor_row() * columns() + m_terminal.cursor_column());
|
||||
IO::out8(0x3d4, 0x0e);
|
||||
IO::out8(0x3d5, MSB(value));
|
||||
IO::out8(0x3d4, 0x0f);
|
||||
IO::out8(0x3d5, LSB(value));
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT void VirtualConsole::initialize()
|
||||
void ConsoleImpl::invalidate_cursor()
|
||||
{
|
||||
s_vga_buffer = (u8*)0xc00b8000;
|
||||
s_active_console = -1;
|
||||
}
|
||||
void ConsoleImpl::clear()
|
||||
{
|
||||
m_client.clear();
|
||||
}
|
||||
void ConsoleImpl::clear_including_history()
|
||||
{
|
||||
}
|
||||
|
||||
void ConsoleImpl::set_size(u16 determined_columns, u16 determined_rows)
|
||||
{
|
||||
VERIFY(determined_columns);
|
||||
VERIFY(determined_rows);
|
||||
|
||||
if (determined_columns == columns() && determined_rows == rows())
|
||||
return;
|
||||
|
||||
m_columns = determined_columns;
|
||||
m_rows = determined_rows;
|
||||
|
||||
m_cursor_row = min<size_t>((int)m_cursor_row, rows() - 1);
|
||||
m_cursor_column = min<size_t>((int)m_cursor_column, columns() - 1);
|
||||
m_saved_cursor_row = min<size_t>((int)m_saved_cursor_row, rows() - 1);
|
||||
m_saved_cursor_column = min<size_t>((int)m_saved_cursor_column, columns() - 1);
|
||||
|
||||
m_horizontal_tabs.resize(determined_columns);
|
||||
for (unsigned i = 0; i < determined_columns; ++i)
|
||||
m_horizontal_tabs[i] = (i % 8) == 0;
|
||||
// Rightmost column is always last tab on line.
|
||||
m_horizontal_tabs[determined_columns - 1] = 1;
|
||||
m_client.terminal_did_resize(m_columns, m_rows);
|
||||
}
|
||||
void ConsoleImpl::scroll_up()
|
||||
{
|
||||
// NOTE: We have to invalidate the cursor first.
|
||||
m_client.invalidate_cursor(m_cursor_row);
|
||||
m_client.scroll_up();
|
||||
}
|
||||
void ConsoleImpl::scroll_down()
|
||||
{
|
||||
}
|
||||
void ConsoleImpl::newline()
|
||||
{
|
||||
u16 new_row = m_cursor_row;
|
||||
u16 max_row = rows() - 1;
|
||||
if (new_row == max_row) {
|
||||
// NOTE: We have to invalidate the cursor first.
|
||||
m_client.invalidate_cursor(new_row);
|
||||
m_client.scroll_up();
|
||||
} else {
|
||||
++new_row;
|
||||
}
|
||||
set_cursor(new_row, 0);
|
||||
}
|
||||
void ConsoleImpl::put_character_at(unsigned row, unsigned column, u32 ch)
|
||||
{
|
||||
m_client.put_character_at(row, column, ch, m_current_attribute);
|
||||
m_last_code_point = ch;
|
||||
}
|
||||
void ConsoleImpl::set_window_title(const String&)
|
||||
{
|
||||
}
|
||||
void ConsoleImpl::ICH(Parameters)
|
||||
{
|
||||
// FIXME: Implement this
|
||||
}
|
||||
void ConsoleImpl::IL(Parameters)
|
||||
{
|
||||
// FIXME: Implement this
|
||||
}
|
||||
void ConsoleImpl::DCH(Parameters)
|
||||
{
|
||||
// FIXME: Implement this
|
||||
}
|
||||
void ConsoleImpl::DL(Parameters)
|
||||
{
|
||||
// FIXME: Implement this
|
||||
}
|
||||
|
||||
void VirtualConsole::set_graphical(bool graphical)
|
||||
{
|
||||
if (graphical)
|
||||
set_vga_start_row(0);
|
||||
|
||||
m_graphical = graphical;
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT NonnullRefPtr<VirtualConsole> VirtualConsole::create(size_t index)
|
||||
{
|
||||
return adopt_ref(*new VirtualConsole(index));
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT VirtualConsole::VirtualConsole(const unsigned index)
|
||||
: TTY(4, index)
|
||||
, m_index(index)
|
||||
, m_terminal(*this)
|
||||
, m_console_impl(*this)
|
||||
{
|
||||
VERIFY(index < s_max_virtual_consoles);
|
||||
|
||||
m_tty_name = String::formatted("/dev/tty{}", m_index);
|
||||
m_terminal.set_size(80, 25);
|
||||
VERIFY(GraphicsManagement::the().console());
|
||||
set_size(GraphicsManagement::the().console()->max_column(), GraphicsManagement::the().console()->max_row());
|
||||
m_console_impl.set_size(GraphicsManagement::the().console()->max_column(), GraphicsManagement::the().console()->max_row());
|
||||
|
||||
s_consoles[index] = this;
|
||||
// Allocate twice of the max row * max column * sizeof(Cell) to ensure we can some sort of history mechanism...
|
||||
auto size = GraphicsManagement::the().console()->max_column() * GraphicsManagement::the().console()->max_row() * sizeof(Cell) * 2;
|
||||
m_cells = MM.allocate_kernel_region(page_round_up(size), "Virtual Console Cells", Region::Access::Read | Region::Access::Write, AllocationStrategy::AllocateNow);
|
||||
|
||||
// Add the lines, so we also ensure they will be flushed now
|
||||
for (size_t row = 0; row < rows(); row++) {
|
||||
m_lines.append({ true });
|
||||
}
|
||||
clear();
|
||||
VERIFY(m_cells);
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT VirtualConsole::~VirtualConsole()
|
||||
@ -61,69 +143,6 @@ UNMAP_AFTER_INIT VirtualConsole::~VirtualConsole()
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
void VirtualConsole::switch_to(unsigned index)
|
||||
{
|
||||
if ((int)index == s_active_console)
|
||||
return;
|
||||
VERIFY(index < s_max_virtual_consoles);
|
||||
VERIFY(s_consoles[index]);
|
||||
|
||||
ScopedSpinLock lock(s_lock);
|
||||
if (s_active_console != -1) {
|
||||
auto* active_console = s_consoles[s_active_console];
|
||||
// We won't know how to switch away from a graphical console until we
|
||||
// can set the video mode on our own. Just stop anyone from trying for
|
||||
// now.
|
||||
if (active_console->is_graphical()) {
|
||||
dbgln("Cannot switch away from graphical console yet :(");
|
||||
return;
|
||||
}
|
||||
active_console->set_active(false);
|
||||
}
|
||||
dbgln("VC: Switch to {} ({})", index, s_consoles[index]);
|
||||
s_active_console = index;
|
||||
s_consoles[s_active_console]->set_active(true);
|
||||
}
|
||||
|
||||
void VirtualConsole::set_active(bool active)
|
||||
{
|
||||
if (active == m_active)
|
||||
return;
|
||||
|
||||
ScopedSpinLock lock(s_lock);
|
||||
|
||||
m_active = active;
|
||||
|
||||
if (active) {
|
||||
set_vga_start_row(0);
|
||||
HIDManagement::the().set_client(this);
|
||||
|
||||
m_terminal.m_need_full_flush = true;
|
||||
flush_dirty_lines();
|
||||
} else {
|
||||
HIDManagement::the().set_client(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
enum class VGAColor : u8 {
|
||||
Black = 0,
|
||||
Blue,
|
||||
Green,
|
||||
Cyan,
|
||||
Red,
|
||||
Magenta,
|
||||
Brown,
|
||||
LightGray,
|
||||
DarkGray,
|
||||
BrightBlue,
|
||||
BrightGreen,
|
||||
BrightCyan,
|
||||
BrightRed,
|
||||
BrightMagenta,
|
||||
Yellow,
|
||||
White,
|
||||
};
|
||||
|
||||
enum class ANSIColor : u8 {
|
||||
Black = 0,
|
||||
Red,
|
||||
@ -144,60 +163,53 @@ enum class ANSIColor : u8 {
|
||||
__Count,
|
||||
};
|
||||
|
||||
static inline VGAColor ansi_color_to_vga(ANSIColor color)
|
||||
static inline Graphics::Console::Color ansi_color_to_standard_vga_color(ANSIColor color)
|
||||
{
|
||||
switch (color) {
|
||||
case ANSIColor::Black:
|
||||
return VGAColor::Black;
|
||||
return Graphics::Console::Color::Black;
|
||||
case ANSIColor::Red:
|
||||
return VGAColor::Red;
|
||||
return Graphics::Console::Color::Red;
|
||||
case ANSIColor::Brown:
|
||||
return VGAColor::Brown;
|
||||
return Graphics::Console::Color::Brown;
|
||||
case ANSIColor::Blue:
|
||||
return VGAColor::Blue;
|
||||
return Graphics::Console::Color::Blue;
|
||||
case ANSIColor::Magenta:
|
||||
return VGAColor::Magenta;
|
||||
return Graphics::Console::Color::Magenta;
|
||||
case ANSIColor::Green:
|
||||
return VGAColor::Green;
|
||||
return Graphics::Console::Color::Green;
|
||||
case ANSIColor::Cyan:
|
||||
return VGAColor::Cyan;
|
||||
return Graphics::Console::Color::Cyan;
|
||||
case ANSIColor::LightGray:
|
||||
return VGAColor::LightGray;
|
||||
return Graphics::Console::Color::LightGray;
|
||||
case ANSIColor::DarkGray:
|
||||
return VGAColor::DarkGray;
|
||||
return Graphics::Console::Color::DarkGray;
|
||||
case ANSIColor::BrightRed:
|
||||
return VGAColor::BrightRed;
|
||||
return Graphics::Console::Color::BrightRed;
|
||||
case ANSIColor::BrightGreen:
|
||||
return VGAColor::BrightGreen;
|
||||
return Graphics::Console::Color::BrightGreen;
|
||||
case ANSIColor::Yellow:
|
||||
return VGAColor::Yellow;
|
||||
return Graphics::Console::Color::Yellow;
|
||||
case ANSIColor::BrightBlue:
|
||||
return VGAColor::BrightBlue;
|
||||
return Graphics::Console::Color::BrightBlue;
|
||||
case ANSIColor::BrightMagenta:
|
||||
return VGAColor::BrightMagenta;
|
||||
return Graphics::Console::Color::BrightMagenta;
|
||||
case ANSIColor::BrightCyan:
|
||||
return VGAColor::BrightCyan;
|
||||
return Graphics::Console::Color::BrightCyan;
|
||||
case ANSIColor::White:
|
||||
return VGAColor::White;
|
||||
return Graphics::Console::Color::White;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
static inline u8 xterm_color_to_vga(u32 color)
|
||||
static inline Graphics::Console::Color xterm_to_standard_color(u32 color)
|
||||
{
|
||||
for (u8 i = 0; i < (u8)ANSIColor::__Count; i++) {
|
||||
if (xterm_colors[i] == color)
|
||||
return (u8)ansi_color_to_vga((ANSIColor)i);
|
||||
return (Graphics::Console::Color)ansi_color_to_standard_vga_color((ANSIColor)i);
|
||||
}
|
||||
return (u8)VGAColor::LightGray;
|
||||
}
|
||||
|
||||
void VirtualConsole::clear_vga_row(u16 row)
|
||||
{
|
||||
u16* linemem = (u16*)&m_current_vga_window[row * 160];
|
||||
for (u16 i = 0; i < columns(); ++i)
|
||||
linemem[i] = 0x0720;
|
||||
return Graphics::Console::Color::LightGray;
|
||||
}
|
||||
|
||||
void VirtualConsole::on_key_pressed(KeyEvent event)
|
||||
@ -209,26 +221,18 @@ void VirtualConsole::on_key_pressed(KeyEvent event)
|
||||
if (!event.is_press())
|
||||
return;
|
||||
|
||||
if (event.key == KeyCode::Key_PageUp && event.flags == Mod_Shift) {
|
||||
// TODO: scroll up
|
||||
return;
|
||||
}
|
||||
if (event.key == KeyCode::Key_PageDown && event.flags == Mod_Shift) {
|
||||
// TODO: scroll down
|
||||
return;
|
||||
}
|
||||
|
||||
Processor::deferred_call_queue([this, event]() {
|
||||
m_terminal.handle_key_press(event.key, event.code_point, event.flags);
|
||||
m_console_impl.handle_key_press(event.key, event.code_point, event.flags);
|
||||
});
|
||||
}
|
||||
|
||||
ssize_t VirtualConsole::on_tty_write(const UserOrKernelBuffer& data, ssize_t size)
|
||||
{
|
||||
ScopedSpinLock lock(s_lock);
|
||||
ScopedSpinLock global_lock(ConsoleManagement::the().tty_write_lock());
|
||||
ScopedSpinLock lock(m_lock);
|
||||
auto result = data.read_buffered<512>((size_t)size, [&](u8 const* buffer, size_t buffer_bytes) {
|
||||
for (size_t i = 0; i < buffer_bytes; ++i)
|
||||
m_terminal.on_input(buffer[i]);
|
||||
m_console_impl.on_input(buffer[i]);
|
||||
return buffer_bytes;
|
||||
});
|
||||
if (m_active)
|
||||
@ -238,52 +242,51 @@ ssize_t VirtualConsole::on_tty_write(const UserOrKernelBuffer& data, ssize_t siz
|
||||
return (ssize_t)result.value();
|
||||
}
|
||||
|
||||
void VirtualConsole::set_vga_start_row(u16 row)
|
||||
void VirtualConsole::set_active(bool active)
|
||||
{
|
||||
m_vga_start_row = row;
|
||||
m_current_vga_start_address = row * columns();
|
||||
m_current_vga_window = s_vga_buffer + row * 160;
|
||||
IO::out8(0x3d4, 0x0c);
|
||||
IO::out8(0x3d5, MSB(m_current_vga_start_address));
|
||||
IO::out8(0x3d4, 0x0d);
|
||||
IO::out8(0x3d5, LSB(m_current_vga_start_address));
|
||||
VERIFY(ConsoleManagement::the().m_lock.is_locked());
|
||||
VERIFY(m_active != active);
|
||||
m_active = active;
|
||||
|
||||
if (active) {
|
||||
HIDManagement::the().set_client(this);
|
||||
|
||||
m_console_impl.m_need_full_flush = true;
|
||||
flush_dirty_lines();
|
||||
} else {
|
||||
HIDManagement::the().set_client(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline u8 attribute_to_vga(const VT::Attribute& attribute)
|
||||
void VirtualConsole::emit_char(char ch)
|
||||
{
|
||||
u8 vga_attr = 0x07;
|
||||
|
||||
if (attribute.flags & VT::Attribute::Bold)
|
||||
vga_attr |= 0x08;
|
||||
|
||||
// Background color
|
||||
vga_attr &= ~0x70;
|
||||
vga_attr |= xterm_color_to_vga(attribute.effective_background_color()) << 8;
|
||||
|
||||
// Foreground color
|
||||
vga_attr &= ~0x7;
|
||||
vga_attr |= xterm_color_to_vga(attribute.effective_foreground_color());
|
||||
|
||||
return vga_attr;
|
||||
echo(ch);
|
||||
}
|
||||
|
||||
void VirtualConsole::flush_dirty_lines()
|
||||
{
|
||||
for (u16 visual_row = 0; visual_row < m_terminal.rows(); ++visual_row) {
|
||||
auto& line = m_terminal.visible_line(visual_row);
|
||||
if (!line.is_dirty() && !m_terminal.m_need_full_flush)
|
||||
VERIFY(GraphicsManagement::is_initialized());
|
||||
VERIFY(GraphicsManagement::the().console());
|
||||
for (u16 visual_row = 0; visual_row < rows(); ++visual_row) {
|
||||
auto& line = m_lines[visual_row];
|
||||
if (!line.dirty && !m_console_impl.m_need_full_flush)
|
||||
continue;
|
||||
for (size_t column = 0; column < line.length(); ++column) {
|
||||
u32 code_point = line.code_point(column);
|
||||
auto attribute = line.attribute_at(column);
|
||||
u16 vga_index = (visual_row * 160) + (column * 2);
|
||||
m_current_vga_window[vga_index] = code_point < 128 ? code_point : '?';
|
||||
m_current_vga_window[vga_index + 1] = attribute_to_vga(attribute);
|
||||
for (size_t column = 0; column < columns(); ++column) {
|
||||
auto& cell = cell_at(column, visual_row);
|
||||
|
||||
auto foreground_color = xterm_to_standard_color(cell.attribute.effective_foreground_color());
|
||||
if (cell.attribute.flags & VT::Attribute::Flags::Bold)
|
||||
foreground_color = (Graphics::Console::Color)((u8)foreground_color | 0x08);
|
||||
GraphicsManagement::the().console()->write(column,
|
||||
visual_row,
|
||||
((u8)cell.ch < 128 ? cell.ch : '?'),
|
||||
xterm_to_standard_color(cell.attribute.effective_background_color()),
|
||||
foreground_color);
|
||||
}
|
||||
line.set_dirty(false);
|
||||
line.dirty = false;
|
||||
}
|
||||
flush_vga_cursor();
|
||||
m_terminal.m_need_full_flush = false;
|
||||
GraphicsManagement::the().console()->set_cursor(m_console_impl.cursor_column(), m_console_impl.cursor_row());
|
||||
m_console_impl.m_need_full_flush = false;
|
||||
}
|
||||
|
||||
void VirtualConsole::beep()
|
||||
@ -304,9 +307,8 @@ void VirtualConsole::set_window_progress(int, int)
|
||||
|
||||
void VirtualConsole::terminal_did_resize(u16 columns, u16 rows)
|
||||
{
|
||||
VERIFY(columns == 80);
|
||||
VERIFY(rows == 25);
|
||||
set_size(columns, rows);
|
||||
// FIXME: Allocate more Region(s) or deallocate them if needed...
|
||||
dbgln("VC {}: Resized to {} x {}", index(), columns, rows);
|
||||
}
|
||||
|
||||
void VirtualConsole::terminal_history_changed()
|
||||
@ -333,4 +335,66 @@ void VirtualConsole::echo(u8 ch)
|
||||
}
|
||||
}
|
||||
|
||||
VirtualConsole::Cell& VirtualConsole::cell_at(size_t x, size_t y)
|
||||
{
|
||||
auto* ptr = (VirtualConsole::Cell*)(m_cells->vaddr().as_ptr());
|
||||
ptr += (y * columns()) + x;
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
void VirtualConsole::clear()
|
||||
{
|
||||
auto* cell = (Cell*)m_cells->vaddr().as_ptr();
|
||||
for (size_t y = 0; y < rows(); y++) {
|
||||
m_lines[y].dirty = true;
|
||||
for (size_t x = 0; x < columns(); x++) {
|
||||
cell[x].clear();
|
||||
}
|
||||
cell += columns();
|
||||
}
|
||||
m_console_impl.set_cursor(0, 0);
|
||||
}
|
||||
|
||||
void VirtualConsole::scroll_up()
|
||||
{
|
||||
memmove(m_cells->vaddr().as_ptr(), m_cells->vaddr().offset(columns() * sizeof(Cell)).as_ptr(), ((rows() - 1) * columns() * sizeof(Cell)));
|
||||
clear_line(rows() - 1);
|
||||
m_console_impl.m_need_full_flush = true;
|
||||
}
|
||||
|
||||
void VirtualConsole::newline()
|
||||
{
|
||||
}
|
||||
|
||||
void VirtualConsole::clear_line(size_t y_index)
|
||||
{
|
||||
m_lines[y_index].dirty = true;
|
||||
for (size_t x = 0; x < columns(); x++) {
|
||||
auto& cell = cell_at(x, y_index);
|
||||
cell.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualConsole::put_character_at(unsigned row, unsigned column, u32 code_point, const VT::Attribute& attribute)
|
||||
{
|
||||
VERIFY(row < rows());
|
||||
VERIFY(column < columns());
|
||||
auto& line = m_lines[row];
|
||||
auto& cell = cell_at(column, row);
|
||||
cell.attribute.foreground_color = attribute.foreground_color;
|
||||
cell.attribute.background_color = attribute.background_color;
|
||||
cell.attribute.flags = attribute.flags;
|
||||
if (code_point > 128)
|
||||
cell.ch = ' ';
|
||||
else
|
||||
cell.ch = code_point;
|
||||
cell.attribute.flags |= VT::Attribute::Flags::Touched;
|
||||
line.dirty = true;
|
||||
}
|
||||
|
||||
void VirtualConsole::invalidate_cursor(size_t row)
|
||||
{
|
||||
m_lines[row].dirty = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,35 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <Kernel/API/KeyCode.h>
|
||||
#include <Kernel/ConsoleDevice.h>
|
||||
#include <Kernel/Devices/HID/HIDManagement.h>
|
||||
#include <Kernel/Graphics/Console/Console.h>
|
||||
#include <Kernel/TTY/TTY.h>
|
||||
#include <LibVT/Attribute.h>
|
||||
#include <LibVT/Position.h>
|
||||
#include <LibVT/Terminal.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
static constexpr unsigned s_max_virtual_consoles = 6;
|
||||
class ConsoleManagement;
|
||||
class VirtualConsole;
|
||||
// FIXME: This implementation has no knowledge about keeping terminal history...
|
||||
class ConsoleImpl final : public VT::Terminal {
|
||||
public:
|
||||
explicit ConsoleImpl(VirtualConsole&);
|
||||
|
||||
virtual void set_size(u16 columns, u16 rows) override;
|
||||
|
||||
private:
|
||||
virtual void invalidate_cursor() override;
|
||||
virtual void clear() override;
|
||||
virtual void clear_including_history() override;
|
||||
|
||||
virtual void scroll_up() override;
|
||||
virtual void scroll_down() override;
|
||||
virtual void newline() override;
|
||||
virtual void put_character_at(unsigned row, unsigned column, u32 ch) override;
|
||||
virtual void set_window_title(const String&) override;
|
||||
|
||||
virtual void ICH(Parameters) override;
|
||||
|
||||
virtual void IL(Parameters) override;
|
||||
virtual void DCH(Parameters) override;
|
||||
virtual void DL(Parameters) override;
|
||||
};
|
||||
|
||||
class VirtualConsole final : public TTY
|
||||
, public KeyboardClient
|
||||
, public VT::TerminalClient {
|
||||
AK_MAKE_ETERNAL
|
||||
friend class ConsoleManagement;
|
||||
friend class ConsoleImpl;
|
||||
friend class VT::Terminal;
|
||||
|
||||
public:
|
||||
VirtualConsole(const unsigned index);
|
||||
struct Line {
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
struct Cell {
|
||||
void clear()
|
||||
{
|
||||
ch = ' ';
|
||||
attribute.reset();
|
||||
}
|
||||
char ch;
|
||||
VT::Attribute attribute;
|
||||
};
|
||||
|
||||
public:
|
||||
static NonnullRefPtr<VirtualConsole> create(size_t index);
|
||||
|
||||
virtual ~VirtualConsole() override;
|
||||
|
||||
static void switch_to(unsigned);
|
||||
static void initialize();
|
||||
size_t index() const { return m_index; }
|
||||
|
||||
bool is_graphical() { return m_graphical; }
|
||||
void set_graphical(bool graphical);
|
||||
|
||||
void emit_char(char);
|
||||
|
||||
private:
|
||||
VirtualConsole(const unsigned index);
|
||||
// ^KeyboardClient
|
||||
virtual void on_key_pressed(KeyEvent) override;
|
||||
|
||||
@ -53,23 +109,37 @@ private:
|
||||
virtual String device_name() const override;
|
||||
|
||||
void set_active(bool);
|
||||
|
||||
void flush_vga_cursor();
|
||||
void flush_dirty_lines();
|
||||
|
||||
unsigned m_index;
|
||||
bool m_active { false };
|
||||
bool m_graphical { false };
|
||||
|
||||
void clear_vga_row(u16 row);
|
||||
void set_vga_start_row(u16 row);
|
||||
u16 m_vga_start_row { 0 };
|
||||
u16 m_current_vga_start_address { 0 };
|
||||
u8* m_current_vga_window { nullptr };
|
||||
|
||||
VT::Terminal m_terminal;
|
||||
|
||||
String m_tty_name;
|
||||
RecursiveSpinLock m_lock;
|
||||
|
||||
private:
|
||||
void invalidate_cursor(size_t row);
|
||||
|
||||
void clear();
|
||||
|
||||
void inject_string(const StringView&);
|
||||
|
||||
Cell& cell_at(size_t column, size_t row);
|
||||
|
||||
typedef Vector<unsigned, 4> ParamVector;
|
||||
|
||||
void on_code_point(u32);
|
||||
|
||||
void scroll_down();
|
||||
void scroll_up();
|
||||
void newline();
|
||||
void clear_line(size_t index);
|
||||
void put_character_at(unsigned row, unsigned column, u32 ch, const VT::Attribute&);
|
||||
|
||||
OwnPtr<Region> m_cells;
|
||||
Vector<VirtualConsole::Line> m_lines;
|
||||
ConsoleImpl m_console_impl;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include <Kernel/Random.h>
|
||||
#include <Kernel/Scheduler.h>
|
||||
#include <Kernel/Storage/StorageManagement.h>
|
||||
#include <Kernel/TTY/ConsoleManagement.h>
|
||||
#include <Kernel/TTY/PTYMultiplexer.h>
|
||||
#include <Kernel/TTY/VirtualConsole.h>
|
||||
#include <Kernel/Tasks/FinalizerTask.h>
|
||||
@ -138,12 +139,13 @@ extern "C" UNMAP_AFTER_INIT [[noreturn]] void init()
|
||||
for (ctor_func_t* ctor = &start_ctors; ctor < &end_ctors; ctor++)
|
||||
(*ctor)();
|
||||
|
||||
ConsoleDevice::initialize();
|
||||
|
||||
APIC::initialize();
|
||||
InterruptManagement::initialize();
|
||||
ACPI::initialize();
|
||||
|
||||
VFS::initialize();
|
||||
ConsoleDevice::initialize();
|
||||
|
||||
dmesgln("Starting SerenityOS...");
|
||||
|
||||
@ -160,12 +162,11 @@ extern "C" UNMAP_AFTER_INIT [[noreturn]] void init()
|
||||
|
||||
VMWareBackdoor::the(); // don't wait until first mouse packet
|
||||
HIDManagement::initialize();
|
||||
VirtualConsole::initialize();
|
||||
tty0 = new VirtualConsole(0);
|
||||
for (unsigned i = 1; i < s_max_virtual_consoles; i++) {
|
||||
new VirtualConsole(i);
|
||||
}
|
||||
VirtualConsole::switch_to(0);
|
||||
|
||||
PCI::initialize();
|
||||
GraphicsManagement::the().initialize();
|
||||
ConsoleManagement::the().initialize();
|
||||
ConsoleManagement::the().switch_to(0);
|
||||
|
||||
Thread::initialize();
|
||||
Process::initialize();
|
||||
@ -230,11 +231,8 @@ void init_stage2(void*)
|
||||
SyncTask::spawn();
|
||||
FinalizerTask::spawn();
|
||||
|
||||
PCI::initialize();
|
||||
auto boot_profiling = kernel_command_line().is_boot_profiling_enabled();
|
||||
|
||||
GraphicsManagement::the().initialize();
|
||||
|
||||
USB::UHCIController::detect();
|
||||
|
||||
DMIExpose::initialize();
|
||||
@ -274,7 +272,8 @@ void init_stage2(void*)
|
||||
int error;
|
||||
|
||||
// FIXME: It would be nicer to set the mode from userspace.
|
||||
tty0->set_graphical(!GraphicsManagement::the().is_text_mode_enabled());
|
||||
// FIXME: It would be smarter to not hardcode that the first tty is the only graphical one
|
||||
ConsoleManagement::the().first_tty()->set_graphical(GraphicsManagement::the().framebuffer_devices_exist());
|
||||
RefPtr<Thread> thread;
|
||||
auto userspace_init = kernel_command_line().userspace_init();
|
||||
auto init_args = kernel_command_line().userspace_init_args();
|
||||
|
@ -7,9 +7,12 @@
|
||||
#include <AK/PrintfImplementation.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/ConsoleDevice.h>
|
||||
#include <Kernel/Graphics/Console/Console.h>
|
||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||
#include <Kernel/IO.h>
|
||||
#include <Kernel/Process.h>
|
||||
#include <Kernel/SpinLock.h>
|
||||
#include <Kernel/TTY/ConsoleManagement.h>
|
||||
#include <Kernel/kstdio.h>
|
||||
|
||||
#include <LibC/stdarg.h>
|
||||
@ -60,6 +63,20 @@ static void serial_putch(char ch)
|
||||
was_cr = false;
|
||||
}
|
||||
|
||||
static void critical_console_out(char ch)
|
||||
{
|
||||
if (serial_debug)
|
||||
serial_putch(ch);
|
||||
// No need to output things to the real ConsoleDevice as no one is likely
|
||||
// to read it (because we are in a fatal situation, so only print things and halt)
|
||||
IO::out8(0xe9, ch);
|
||||
// We emit chars directly to the string. this is necessary in few cases,
|
||||
// especially when we want to avoid any memory allocations...
|
||||
if (GraphicsManagement::is_initialized() && GraphicsManagement::the().console()) {
|
||||
GraphicsManagement::the().console()->write(ch);
|
||||
}
|
||||
}
|
||||
|
||||
static void console_out(char ch)
|
||||
{
|
||||
if (serial_debug)
|
||||
@ -72,6 +89,9 @@ static void console_out(char ch)
|
||||
} else {
|
||||
IO::out8(0xe9, ch);
|
||||
}
|
||||
if (ConsoleManagement::is_initialized()) {
|
||||
ConsoleManagement::the().debug_tty()->emit_char(ch);
|
||||
}
|
||||
}
|
||||
|
||||
static void buffer_putch(char*& bufptr, char ch)
|
||||
@ -145,3 +165,12 @@ extern "C" void kernelputstr(const char* characters, size_t length)
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
console_out(characters[i]);
|
||||
}
|
||||
|
||||
extern "C" void kernelcriticalputstr(const char* characters, size_t length)
|
||||
{
|
||||
if (!characters)
|
||||
return;
|
||||
ScopedSpinLock lock(s_log_lock);
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
critical_console_out(characters[i]);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
extern "C" {
|
||||
void dbgputstr(const char*, size_t);
|
||||
void kernelputstr(const char*, size_t);
|
||||
void kernelcriticalputstr(const char*, size_t);
|
||||
int snprintf(char* buf, size_t, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
|
||||
void set_serial_debug(bool on_or_off);
|
||||
int get_serial_debug();
|
||||
|
@ -16,6 +16,7 @@ set(BXVGA_DEBUG ON)
|
||||
set(PS2MOUSE_DEBUG ON)
|
||||
set(MOUSE_DEBUG ON)
|
||||
set(VMWARE_BACKDOOR_DEBUG ON)
|
||||
set(VIRTUAL_CONSOLE_DEBUG ON)
|
||||
set(FILEDESCRIPTION_DEBUG ON)
|
||||
set(PROCFS_DEBUG ON)
|
||||
set(VFS_DEBUG ON)
|
||||
|
65
Userland/Libraries/LibVT/Attribute.h
Normal file
65
Userland/Libraries/LibVT/Attribute.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibVT/XtermColors.h>
|
||||
|
||||
namespace VT {
|
||||
|
||||
struct Attribute {
|
||||
Attribute() { reset(); }
|
||||
|
||||
static const u32 default_foreground_color = xterm_colors[7];
|
||||
static const u32 default_background_color = xterm_colors[0];
|
||||
|
||||
void reset()
|
||||
{
|
||||
foreground_color = default_foreground_color;
|
||||
background_color = default_background_color;
|
||||
flags = Flags::NoAttributes;
|
||||
}
|
||||
u32 foreground_color {};
|
||||
u32 background_color {};
|
||||
|
||||
u32 effective_background_color() const { return flags & Negative ? foreground_color : background_color; }
|
||||
u32 effective_foreground_color() const { return flags & Negative ? background_color : foreground_color; }
|
||||
|
||||
#ifndef KERNEL
|
||||
String href;
|
||||
String href_id;
|
||||
#endif
|
||||
|
||||
enum Flags : u8 {
|
||||
NoAttributes = 0x00,
|
||||
Bold = 0x01,
|
||||
Italic = 0x02,
|
||||
Underline = 0x04,
|
||||
Negative = 0x08,
|
||||
Blink = 0x10,
|
||||
Touched = 0x20,
|
||||
};
|
||||
|
||||
bool is_untouched() const { return !(flags & Touched); }
|
||||
|
||||
// TODO: it would be really nice if we had a helper for enums that
|
||||
// exposed bit ops for class enums...
|
||||
u8 flags = Flags::NoAttributes;
|
||||
|
||||
bool operator==(const Attribute& other) const
|
||||
{
|
||||
return foreground_color == other.foreground_color && background_color == other.background_color && flags == other.flags;
|
||||
}
|
||||
bool operator!=(const Attribute& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -9,57 +9,11 @@
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibVT/Attribute.h>
|
||||
#include <LibVT/XtermColors.h>
|
||||
|
||||
namespace VT {
|
||||
|
||||
struct Attribute {
|
||||
Attribute() { reset(); }
|
||||
|
||||
static const u32 default_foreground_color = xterm_colors[7];
|
||||
static const u32 default_background_color = xterm_colors[0];
|
||||
|
||||
void reset()
|
||||
{
|
||||
foreground_color = default_foreground_color;
|
||||
background_color = default_background_color;
|
||||
flags = Flags::NoAttributes;
|
||||
}
|
||||
u32 foreground_color {};
|
||||
u32 background_color {};
|
||||
|
||||
u32 effective_background_color() const { return flags & Negative ? foreground_color : background_color; }
|
||||
u32 effective_foreground_color() const { return flags & Negative ? background_color : foreground_color; }
|
||||
|
||||
String href;
|
||||
String href_id;
|
||||
|
||||
enum Flags : u8 {
|
||||
NoAttributes = 0x00,
|
||||
Bold = 0x01,
|
||||
Italic = 0x02,
|
||||
Underline = 0x04,
|
||||
Negative = 0x08,
|
||||
Blink = 0x10,
|
||||
Touched = 0x20,
|
||||
};
|
||||
|
||||
bool is_untouched() const { return !(flags & Touched); }
|
||||
|
||||
// TODO: it would be really nice if we had a helper for enums that
|
||||
// exposed bit ops for class enums...
|
||||
u8 flags = Flags::NoAttributes;
|
||||
|
||||
bool operator==(const Attribute& other) const
|
||||
{
|
||||
return foreground_color == other.foreground_color && background_color == other.background_color && flags == other.flags;
|
||||
}
|
||||
bool operator!=(const Attribute& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
class Line {
|
||||
AK_MAKE_NONCOPYABLE(Line);
|
||||
AK_MAKE_NONMOVABLE(Line);
|
||||
|
@ -9,19 +9,23 @@
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibVT/Terminal.h>
|
||||
#ifdef KERNEL
|
||||
# include <Kernel/TTY/VirtualConsole.h>
|
||||
#endif
|
||||
|
||||
namespace VT {
|
||||
|
||||
#ifndef KERNEL
|
||||
Terminal::Terminal(TerminalClient& client)
|
||||
#else
|
||||
Terminal::Terminal(Kernel::VirtualConsole& client)
|
||||
#endif
|
||||
: m_client(client)
|
||||
, m_parser(*this)
|
||||
{
|
||||
}
|
||||
|
||||
Terminal::~Terminal()
|
||||
{
|
||||
}
|
||||
|
||||
#ifndef KERNEL
|
||||
void Terminal::clear()
|
||||
{
|
||||
for (size_t i = 0; i < rows(); ++i)
|
||||
@ -38,6 +42,7 @@ void Terminal::clear_including_history()
|
||||
|
||||
m_client.terminal_history_changed();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Terminal::alter_mode(bool should_set, Parameters params, Intermediates intermediates)
|
||||
{
|
||||
@ -445,6 +450,7 @@ void Terminal::SD(Parameters params)
|
||||
scroll_down();
|
||||
}
|
||||
|
||||
#ifndef KERNEL
|
||||
void Terminal::IL(Parameters params)
|
||||
{
|
||||
int count = 1;
|
||||
@ -461,12 +467,14 @@ void Terminal::IL(Parameters params)
|
||||
|
||||
m_need_full_flush = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Terminal::DA(Parameters)
|
||||
{
|
||||
emit_string("\033[?1;0c");
|
||||
}
|
||||
|
||||
#ifndef KERNEL
|
||||
void Terminal::DL(Parameters params)
|
||||
{
|
||||
int count = 1;
|
||||
@ -511,6 +519,7 @@ void Terminal::DCH(Parameters params)
|
||||
|
||||
line.set_dirty(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Terminal::newline()
|
||||
{
|
||||
@ -527,6 +536,7 @@ void Terminal::carriage_return()
|
||||
set_cursor(m_cursor_row, 0);
|
||||
}
|
||||
|
||||
#ifndef KERNEL
|
||||
void Terminal::scroll_up()
|
||||
{
|
||||
// NOTE: We have to invalidate the cursor first.
|
||||
@ -550,6 +560,20 @@ void Terminal::scroll_down()
|
||||
m_need_full_flush = true;
|
||||
}
|
||||
|
||||
void Terminal::put_character_at(unsigned row, unsigned column, u32 code_point)
|
||||
{
|
||||
VERIFY(row < rows());
|
||||
VERIFY(column < columns());
|
||||
auto& line = m_lines[row];
|
||||
line.set_code_point(column, code_point);
|
||||
line.attribute_at(column) = m_current_attribute;
|
||||
line.attribute_at(column).flags |= Attribute::Touched;
|
||||
line.set_dirty(true);
|
||||
|
||||
m_last_code_point = code_point;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Terminal::set_cursor(unsigned a_row, unsigned a_column)
|
||||
{
|
||||
unsigned row = min(a_row, m_rows - 1u);
|
||||
@ -565,19 +589,6 @@ void Terminal::set_cursor(unsigned a_row, unsigned a_column)
|
||||
invalidate_cursor();
|
||||
}
|
||||
|
||||
void Terminal::put_character_at(unsigned row, unsigned column, u32 code_point)
|
||||
{
|
||||
VERIFY(row < rows());
|
||||
VERIFY(column < columns());
|
||||
auto& line = m_lines[row];
|
||||
line.set_code_point(column, code_point);
|
||||
line.attribute_at(column) = m_current_attribute;
|
||||
line.attribute_at(column).flags |= Attribute::Touched;
|
||||
line.set_dirty(true);
|
||||
|
||||
m_last_code_point = code_point;
|
||||
}
|
||||
|
||||
void Terminal::NEL()
|
||||
{
|
||||
newline();
|
||||
@ -607,6 +618,7 @@ void Terminal::DSR(Parameters params)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef KERNEL
|
||||
void Terminal::ICH(Parameters params)
|
||||
{
|
||||
int num = 0;
|
||||
@ -628,6 +640,7 @@ void Terminal::ICH(Parameters params)
|
||||
|
||||
line.set_dirty(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Terminal::on_input(u8 byte)
|
||||
{
|
||||
@ -837,6 +850,7 @@ void Terminal::execute_osc_sequence(OscParameters parameters, u8 last_byte)
|
||||
// Should we expose the raw OSC string from the parser? Or join by semicolon?
|
||||
break;
|
||||
case 8:
|
||||
#ifndef KERNEL
|
||||
if (parameters.size() < 2) {
|
||||
dbgln("Attempted to set href but gave too few parameters");
|
||||
} else if (parameters[2].is_empty()) {
|
||||
@ -847,6 +861,7 @@ void Terminal::execute_osc_sequence(OscParameters parameters, u8 last_byte)
|
||||
// FIXME: Respect the provided ID
|
||||
m_current_attribute.href_id = String::number(m_next_href_id++);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case 9:
|
||||
if (parameters.size() < 2 || parameters[1].is_empty() || parameters[2].is_empty())
|
||||
@ -1034,6 +1049,7 @@ void Terminal::unimplemented_osc_sequence(OscParameters parameters, u8 last_byte
|
||||
dbgln("{}", builder.string_view());
|
||||
}
|
||||
|
||||
#ifndef KERNEL
|
||||
void Terminal::set_size(u16 columns, u16 rows)
|
||||
{
|
||||
if (!columns)
|
||||
@ -1073,7 +1089,9 @@ void Terminal::set_size(u16 columns, u16 rows)
|
||||
|
||||
m_client.terminal_did_resize(m_columns, m_rows);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL
|
||||
void Terminal::invalidate_cursor()
|
||||
{
|
||||
m_lines[m_cursor_row].set_dirty(true);
|
||||
@ -1090,5 +1108,6 @@ Attribute Terminal::attribute_at(const Position& position) const
|
||||
return {};
|
||||
return line.attribute_at(position.column());
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -12,9 +12,18 @@
|
||||
#include <AK/Vector.h>
|
||||
#include <Kernel/API/KeyCode.h>
|
||||
#include <LibVT/EscapeSequenceParser.h>
|
||||
#include <LibVT/Line.h>
|
||||
#include <LibVT/Position.h>
|
||||
|
||||
#ifndef KERNEL
|
||||
# include <LibVT/Attribute.h>
|
||||
# include <LibVT/Line.h>
|
||||
#else
|
||||
namespace Kernel {
|
||||
class VirtualConsole;
|
||||
}
|
||||
# include <LibVT/Attribute.h>
|
||||
#endif
|
||||
|
||||
namespace VT {
|
||||
|
||||
class TerminalClient {
|
||||
@ -31,24 +40,52 @@ public:
|
||||
|
||||
class Terminal : public EscapeSequenceExecutor {
|
||||
public:
|
||||
#ifndef KERNEL
|
||||
explicit Terminal(TerminalClient&);
|
||||
~Terminal();
|
||||
#else
|
||||
explicit Terminal(Kernel::VirtualConsole&);
|
||||
#endif
|
||||
|
||||
virtual ~Terminal()
|
||||
{
|
||||
}
|
||||
|
||||
bool m_need_full_flush { false };
|
||||
|
||||
#ifndef KERNEL
|
||||
void invalidate_cursor();
|
||||
#else
|
||||
virtual void invalidate_cursor() = 0;
|
||||
#endif
|
||||
|
||||
void on_input(u8);
|
||||
|
||||
void set_cursor(unsigned row, unsigned column);
|
||||
|
||||
#ifndef KERNEL
|
||||
void clear();
|
||||
void clear_including_history();
|
||||
#else
|
||||
virtual void clear() = 0;
|
||||
virtual void clear_including_history() = 0;
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL
|
||||
void set_size(u16 columns, u16 rows);
|
||||
u16 columns() const { return m_columns; }
|
||||
#else
|
||||
virtual void set_size(u16 columns, u16 rows) = 0;
|
||||
#endif
|
||||
|
||||
u16 columns() const
|
||||
{
|
||||
return m_columns;
|
||||
}
|
||||
u16 rows() const { return m_rows; }
|
||||
|
||||
u16 cursor_column() const { return m_cursor_column; }
|
||||
u16 cursor_row() const { return m_cursor_row; }
|
||||
|
||||
#ifndef KERNEL
|
||||
size_t line_count() const
|
||||
{
|
||||
return m_history.size() + m_lines.size();
|
||||
@ -100,13 +137,16 @@ public:
|
||||
m_max_history_lines = value;
|
||||
}
|
||||
size_t history_size() const { return m_history.size(); }
|
||||
#endif
|
||||
|
||||
void inject_string(const StringView&);
|
||||
void handle_key_press(KeyCode, u32, u8 flags);
|
||||
|
||||
#ifndef KERNEL
|
||||
Attribute attribute_at(const Position&) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
protected:
|
||||
// ^EscapeSequenceExecutor
|
||||
virtual void emit_code_point(u32) override;
|
||||
virtual void execute_control_code(u8) override;
|
||||
@ -117,14 +157,20 @@ private:
|
||||
virtual void receive_dcs_char(u8 byte) override;
|
||||
virtual void execute_dcs_sequence() override;
|
||||
|
||||
void carriage_return();
|
||||
#ifndef KERNEL
|
||||
void scroll_up();
|
||||
void scroll_down();
|
||||
void newline();
|
||||
void carriage_return();
|
||||
|
||||
void set_cursor(unsigned row, unsigned column);
|
||||
void put_character_at(unsigned row, unsigned column, u32 ch);
|
||||
void set_window_title(const String&);
|
||||
#else
|
||||
virtual void scroll_up() = 0;
|
||||
virtual void scroll_down() = 0;
|
||||
virtual void newline() = 0;
|
||||
virtual void put_character_at(unsigned row, unsigned column, u32 ch) = 0;
|
||||
virtual void set_window_title(const String&) = 0;
|
||||
#endif
|
||||
|
||||
void unimplemented_control_code(u8);
|
||||
void unimplemented_escape_sequence(Intermediates, u8 last_byte);
|
||||
@ -192,8 +238,12 @@ private:
|
||||
// DSR - Device Status Reports
|
||||
void DSR(Parameters);
|
||||
|
||||
#ifndef KERNEL
|
||||
// ICH - Insert Character
|
||||
void ICH(Parameters);
|
||||
#else
|
||||
virtual void ICH(Parameters) = 0;
|
||||
#endif
|
||||
|
||||
// SU - Scroll Up (called "Pan Down" in VT510)
|
||||
void SU(Parameters);
|
||||
@ -201,14 +251,18 @@ private:
|
||||
// SD - Scroll Down (called "Pan Up" in VT510)
|
||||
void SD(Parameters);
|
||||
|
||||
#ifndef KERNEL
|
||||
// IL - Insert Line
|
||||
void IL(Parameters);
|
||||
|
||||
// DCH - Delete Character
|
||||
void DCH(Parameters);
|
||||
|
||||
// DL - Delete Line
|
||||
void DL(Parameters);
|
||||
#else
|
||||
virtual void IL(Parameters) = 0;
|
||||
virtual void DCH(Parameters) = 0;
|
||||
virtual void DL(Parameters) = 0;
|
||||
#endif
|
||||
|
||||
// CHA - Cursor Horizontal Absolute
|
||||
void CHA(Parameters);
|
||||
@ -225,10 +279,14 @@ private:
|
||||
// FIXME: Find the right names for these.
|
||||
void XTERM_WM(Parameters);
|
||||
|
||||
#ifndef KERNEL
|
||||
TerminalClient& m_client;
|
||||
#else
|
||||
Kernel::VirtualConsole& m_client;
|
||||
#endif
|
||||
|
||||
EscapeSequenceParser m_parser;
|
||||
|
||||
#ifndef KERNEL
|
||||
size_t m_history_start = 0;
|
||||
NonnullOwnPtrVector<Line> m_history;
|
||||
void add_line_to_history(NonnullOwnPtr<Line>&& line)
|
||||
@ -246,6 +304,7 @@ private:
|
||||
}
|
||||
|
||||
NonnullOwnPtrVector<Line> m_lines;
|
||||
#endif
|
||||
|
||||
size_t m_scroll_region_top { 0 };
|
||||
size_t m_scroll_region_bottom { 0 };
|
||||
@ -263,7 +322,9 @@ private:
|
||||
Attribute m_current_attribute;
|
||||
Attribute m_saved_attribute;
|
||||
|
||||
#ifndef KERNEL
|
||||
u32 m_next_href_id { 0 };
|
||||
#endif
|
||||
|
||||
Vector<bool> m_horizontal_tabs;
|
||||
u32 m_last_code_point { 0 };
|
||||
|
@ -57,10 +57,19 @@ static void parse_boot_mode()
|
||||
const String cmdline = String::copy(f->read_all(), Chomp);
|
||||
dbgln("Read command line: {}", cmdline);
|
||||
|
||||
for (auto& part : cmdline.split_view(' ')) {
|
||||
auto pair = part.split_view('=', 2);
|
||||
if (pair.size() == 2 && pair[0] == "boot_mode")
|
||||
g_boot_mode = pair[1];
|
||||
// FIXME: Support more than one framebuffer detection
|
||||
struct stat file_state;
|
||||
int rc = lstat("/dev/fb0", &file_state);
|
||||
if (rc < 0) {
|
||||
for (auto& part : cmdline.split_view(' ')) {
|
||||
auto pair = part.split_view('=', 2);
|
||||
if (pair.size() == 2 && pair[0] == "boot_mode")
|
||||
g_boot_mode = pair[1];
|
||||
}
|
||||
// We could boot into self-test which is not graphical too.
|
||||
if (g_boot_mode == "self-test")
|
||||
return;
|
||||
g_boot_mode = "text";
|
||||
}
|
||||
dbgln("Booting in {} mode", g_boot_mode);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user