diff options
-rw-r--r-- | Kernel/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Kernel/Graphics/DisplayConnector.cpp | 333 | ||||
-rw-r--r-- | Kernel/Graphics/DisplayConnector.h | 144 | ||||
-rw-r--r-- | Kernel/Graphics/GraphicsManagement.cpp | 19 | ||||
-rw-r--r-- | Kernel/Graphics/GraphicsManagement.h | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibC/sys/ioctl_numbers.h | 19 |
6 files changed, 522 insertions, 0 deletions
diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 6ef7641326..c95f5464e3 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -80,6 +80,7 @@ set(KERNEL_SOURCES Graphics/Console/ContiguousFramebufferConsole.cpp Graphics/Console/TextModeConsole.cpp Graphics/Console/VGAConsole.cpp + Graphics/DisplayConnector.cpp Graphics/FramebufferDevice.cpp Graphics/GraphicsManagement.cpp Graphics/Intel/NativeGraphicsAdapter.cpp diff --git a/Kernel/Graphics/DisplayConnector.cpp b/Kernel/Graphics/DisplayConnector.cpp new file mode 100644 index 0000000000..facf9b7ea9 --- /dev/null +++ b/Kernel/Graphics/DisplayConnector.cpp @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <Kernel/Graphics/DisplayConnector.h> +#include <Kernel/Graphics/GraphicsManagement.h> +#include <LibC/sys/ioctl_numbers.h> + +namespace Kernel { + +DisplayConnector::DisplayConnector() + : CharacterDevice(226, GraphicsManagement::the().allocate_minor_device_number()) +{ +} + +ErrorOr<Memory::Region*> DisplayConnector::mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool) +{ + return Error::from_errno(ENOTSUP); +} + +ErrorOr<size_t> DisplayConnector::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) +{ + return Error::from_errno(ENOTIMPL); +} +ErrorOr<size_t> DisplayConnector::write(OpenFileDescription&, u64 offset, UserOrKernelBuffer const& framebuffer_data, size_t length) +{ + SpinlockLocker locker(m_control_lock); + // FIXME: We silently ignore the request if we are in console mode. + // WindowServer is not ready yet to handle errors such as EBUSY currently. + if (console_mode()) { + return length; + } + return write_to_first_surface(offset, framebuffer_data, length); +} + +void DisplayConnector::will_be_destroyed() +{ + GraphicsManagement::the().detach_display_connector({}, *this); + Device::will_be_destroyed(); +} + +void DisplayConnector::after_inserting() +{ + Device::after_inserting(); + GraphicsManagement::the().attach_new_display_connector({}, *this); +} + +bool DisplayConnector::console_mode() const +{ + VERIFY(m_control_lock.is_locked()); + return m_console_mode; +} + +void DisplayConnector::set_display_mode(Badge<GraphicsManagement>, DisplayMode mode) +{ + SpinlockLocker locker(m_control_lock); + m_console_mode = mode == DisplayMode::Console ? true : false; + if (m_console_mode) + enable_console(); + else + disable_console(); +} + +ErrorOr<void> DisplayConnector::initialize_edid_for_generic_monitor() +{ + Array<u8, 128> virtual_monitor_edid = { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */ + 0x0, 0x0, /* manufacturer */ + 0x00, 0x00, /* product code */ + 0x00, 0x00, 0x00, 0x00, /* serial number goes here */ + 0x01, /* week of manufacture */ + 0x00, /* year of manufacture */ + 0x01, 0x03, /* EDID version */ + 0x80, /* capabilities - digital */ + 0x00, /* horiz. res in cm, zero for projectors */ + 0x00, /* vert. res in cm */ + 0x78, /* display gamma (120 == 2.2). */ + 0xEE, /* features (standby, suspend, off, RGB, std */ + /* colour space, preferred timing mode) */ + 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54, + /* chromaticity for standard colour space. */ + 0x00, 0x00, 0x00, /* no default timings */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, /* no standard timings */ + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02, + 0x02, 0x02, + /* descriptor block 1 goes below */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* descriptor block 2, monitor ranges */ + 0x00, 0x00, 0x00, 0xFD, 0x00, + 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, + /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */ + 0x20, + /* descriptor block 3, monitor name */ + 0x00, 0x00, 0x00, 0xFC, 0x00, + 'G', 'e', 'n', 'e', 'r', 'i', 'c', 'S', 'c', 'r', 'e', 'e', 'n', + /* descriptor block 4: dummy data */ + 0x00, 0x00, 0x00, 0x10, 0x00, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, + 0x00, /* number of extensions */ + 0x00 /* checksum goes here */ + }; + set_edid_bytes(virtual_monitor_edid); + return {}; +} + +void DisplayConnector::set_edid_bytes(Array<u8, 128> const& edid_bytes) +{ + memcpy((u8*)m_edid_bytes, edid_bytes.data(), sizeof(m_edid_bytes)); + if (auto parsed_edid = EDID::Parser::from_bytes({ m_edid_bytes, sizeof(m_edid_bytes) }); !parsed_edid.is_error()) { + m_edid_parser = parsed_edid.release_value(); + m_edid_valid = true; + } else { + dmesgln("DisplayConnector: Print offending EDID"); + for (size_t x = 0; x < 128; x = x + 16) { + dmesgln("{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", + m_edid_bytes[x], m_edid_bytes[x + 1], m_edid_bytes[x + 2], m_edid_bytes[x + 3], + m_edid_bytes[x + 4], m_edid_bytes[x + 5], m_edid_bytes[x + 6], m_edid_bytes[x + 7], + m_edid_bytes[x + 8], m_edid_bytes[x + 9], m_edid_bytes[x + 10], m_edid_bytes[x + 11], + m_edid_bytes[x + 12], m_edid_bytes[x + 13], m_edid_bytes[x + 14], m_edid_bytes[x + 15]); + } + dmesgln("DisplayConnector: Parsing EDID failed: {}", parsed_edid.error()); + } +} + +ErrorOr<void> DisplayConnector::flush_rectangle(size_t, FBRect const&) +{ + return Error::from_errno(ENOTSUP); +} + +DisplayConnector::ModeSetting DisplayConnector::current_mode_setting() const +{ + SpinlockLocker locker(m_modeset_lock); + return m_current_mode_setting; +} + +ErrorOr<ByteBuffer> DisplayConnector::get_edid() const +{ + if (!m_edid_valid) + return Error::from_errno(ENOTIMPL); + return ByteBuffer::copy(m_edid_bytes, sizeof(m_edid_bytes)); +} + +ErrorOr<void> DisplayConnector::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) +{ + if (request != FB_IOCTL_GET_HEAD_EDID) { + // Allow anyone to query the EDID. Eventually we'll publish the current EDID on /sys + // so it doesn't really make sense to require the video pledge to query it. + TRY(Process::current().require_promise(Pledge::video)); + } + + // TODO: We really should have ioctls for destroying resources as well + switch (request) { + case FB_IOCTL_GET_PROPERTIES: { + auto user_properties = static_ptr_cast<FBProperties*>(arg); + FBProperties properties {}; + properties.flushing_support = flush_support(); + properties.doublebuffer_support = double_framebuffering_capable(); + properties.partial_flushing_support = partial_flush_support(); + properties.refresh_rate_support = refresh_rate_support(); + + return copy_to_user(user_properties, &properties); + } + case FB_IOCTL_GET_HEAD_MODE_SETTING: { + auto user_head_mode_setting = static_ptr_cast<FBHeadModeSetting*>(arg); + FBHeadModeSetting head_mode_setting {}; + TRY(copy_from_user(&head_mode_setting, user_head_mode_setting)); + { + SpinlockLocker control_locker(m_control_lock); + head_mode_setting.horizontal_stride = m_current_mode_setting.horizontal_stride; + head_mode_setting.pixel_clock_in_khz = m_current_mode_setting.pixel_clock_in_khz; + head_mode_setting.horizontal_active = m_current_mode_setting.horizontal_active; + head_mode_setting.horizontal_front_porch_pixels = m_current_mode_setting.horizontal_front_porch_pixels; + head_mode_setting.horizontal_sync_time_pixels = m_current_mode_setting.horizontal_sync_time_pixels; + head_mode_setting.horizontal_blank_pixels = m_current_mode_setting.horizontal_blank_pixels; + head_mode_setting.vertical_active = m_current_mode_setting.vertical_active; + head_mode_setting.vertical_front_porch_lines = m_current_mode_setting.vertical_front_porch_lines; + head_mode_setting.vertical_sync_time_lines = m_current_mode_setting.vertical_sync_time_lines; + head_mode_setting.vertical_blank_lines = m_current_mode_setting.vertical_blank_lines; + head_mode_setting.horizontal_offset = m_current_mode_setting.horizontal_offset; + head_mode_setting.vertical_offset = m_current_mode_setting.vertical_offset; + } + return copy_to_user(user_head_mode_setting, &head_mode_setting); + } + case FB_IOCTL_GET_HEAD_EDID: { + auto user_head_edid = static_ptr_cast<FBHeadEDID*>(arg); + FBHeadEDID head_edid {}; + TRY(copy_from_user(&head_edid, user_head_edid)); + + auto edid_bytes = TRY(get_edid()); + if (head_edid.bytes != nullptr) { + // Only return the EDID if a buffer was provided. Either way, + // we'll write back the bytes_size with the actual size + if (head_edid.bytes_size < edid_bytes.size()) { + head_edid.bytes_size = edid_bytes.size(); + TRY(copy_to_user(user_head_edid, &head_edid)); + return Error::from_errno(EOVERFLOW); + } + TRY(copy_to_user(head_edid.bytes, (void const*)edid_bytes.data(), edid_bytes.size())); + } + head_edid.bytes_size = edid_bytes.size(); + return copy_to_user(user_head_edid, &head_edid); + } + case FB_IOCTL_SET_HEAD_MODE_SETTING: { + auto user_mode_setting = static_ptr_cast<FBHeadModeSetting const*>(arg); + auto head_mode_setting = TRY(copy_typed_from_user(user_mode_setting)); + + if (head_mode_setting.horizontal_stride < 0) + return Error::from_errno(EINVAL); + if (head_mode_setting.pixel_clock_in_khz < 0) + return Error::from_errno(EINVAL); + if (head_mode_setting.horizontal_active < 0) + return Error::from_errno(EINVAL); + if (head_mode_setting.horizontal_front_porch_pixels < 0) + return Error::from_errno(EINVAL); + if (head_mode_setting.horizontal_sync_time_pixels < 0) + return Error::from_errno(EINVAL); + if (head_mode_setting.horizontal_blank_pixels < 0) + return Error::from_errno(EINVAL); + if (head_mode_setting.vertical_active < 0) + return Error::from_errno(EINVAL); + if (head_mode_setting.vertical_front_porch_lines < 0) + return Error::from_errno(EINVAL); + if (head_mode_setting.vertical_sync_time_lines < 0) + return Error::from_errno(EINVAL); + if (head_mode_setting.vertical_blank_lines < 0) + return Error::from_errno(EINVAL); + if (head_mode_setting.horizontal_offset < 0) + return Error::from_errno(EINVAL); + if (head_mode_setting.vertical_offset < 0) + return Error::from_errno(EINVAL); + { + SpinlockLocker control_locker(m_control_lock); + ModeSetting requested_mode_setting; + requested_mode_setting.horizontal_stride = 0; + requested_mode_setting.pixel_clock_in_khz = head_mode_setting.pixel_clock_in_khz; + requested_mode_setting.horizontal_active = head_mode_setting.horizontal_active; + requested_mode_setting.horizontal_front_porch_pixels = head_mode_setting.horizontal_front_porch_pixels; + requested_mode_setting.horizontal_sync_time_pixels = head_mode_setting.horizontal_sync_time_pixels; + requested_mode_setting.horizontal_blank_pixels = head_mode_setting.horizontal_blank_pixels; + requested_mode_setting.vertical_active = head_mode_setting.vertical_active; + requested_mode_setting.vertical_front_porch_lines = head_mode_setting.vertical_front_porch_lines; + requested_mode_setting.vertical_sync_time_lines = head_mode_setting.vertical_sync_time_lines; + requested_mode_setting.vertical_blank_lines = head_mode_setting.vertical_blank_lines; + requested_mode_setting.horizontal_offset = head_mode_setting.horizontal_offset; + requested_mode_setting.vertical_offset = head_mode_setting.vertical_offset; + + TRY(set_mode_setting(requested_mode_setting)); + } + return {}; + } + + case FB_IOCTL_SET_SAFE_HEAD_MODE_SETTING: { + SpinlockLocker control_locker(m_control_lock); + TRY(set_safe_mode_setting()); + return {}; + } + + case FB_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER: { + // FIXME: We silently ignore the request if we are in console mode. + // WindowServer is not ready yet to handle errors such as EBUSY currently. + SpinlockLocker control_locker(m_control_lock); + if (console_mode()) { + return {}; + } + + auto user_head_vertical_buffer_offset = static_ptr_cast<FBHeadVerticalOffset const*>(arg); + auto head_vertical_buffer_offset = TRY(copy_typed_from_user(user_head_vertical_buffer_offset)); + + SpinlockLocker locker(m_modeset_lock); + + if (head_vertical_buffer_offset.offsetted < 0 || head_vertical_buffer_offset.offsetted > 1) + return Error::from_errno(EINVAL); + TRY(set_y_offset(head_vertical_buffer_offset.offsetted == 0 ? 0 : m_current_mode_setting.vertical_active)); + if (head_vertical_buffer_offset.offsetted == 0) + m_vertical_offsetted = false; + else + m_vertical_offsetted = true; + return {}; + } + case FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER: { + auto user_head_vertical_buffer_offset = static_ptr_cast<FBHeadVerticalOffset*>(arg); + FBHeadVerticalOffset head_vertical_buffer_offset {}; + TRY(copy_from_user(&head_vertical_buffer_offset, user_head_vertical_buffer_offset)); + + head_vertical_buffer_offset.offsetted = m_vertical_offsetted; + return copy_to_user(user_head_vertical_buffer_offset, &head_vertical_buffer_offset); + } + case FB_IOCTL_FLUSH_HEAD_BUFFERS: { + if (!partial_flush_support()) + return Error::from_errno(ENOTSUP); + MutexLocker locker(m_flushing_lock); + auto user_flush_rects = static_ptr_cast<FBFlushRects const*>(arg); + auto flush_rects = TRY(copy_typed_from_user(user_flush_rects)); + if (Checked<unsigned>::multiplication_would_overflow(flush_rects.count, sizeof(FBRect))) + return Error::from_errno(EFAULT); + if (flush_rects.count > 0) { + for (unsigned i = 0; i < flush_rects.count; i++) { + FBRect user_dirty_rect; + TRY(copy_from_user(&user_dirty_rect, &flush_rects.rects[i])); + { + SpinlockLocker control_locker(m_control_lock); + if (console_mode()) + return {}; + TRY(flush_rectangle(flush_rects.buffer_index, user_dirty_rect)); + } + } + } + return {}; + }; + case FB_IOCTL_FLUSH_HEAD: { + // FIXME: We silently ignore the request if we are in console mode. + // WindowServer is not ready yet to handle errors such as EBUSY currently. + MutexLocker locker(m_flushing_lock); + SpinlockLocker control_locker(m_control_lock); + if (console_mode()) + return {}; + if (!flush_support()) + return Error::from_errno(ENOTSUP); + TRY(flush_first_surface()); + return {}; + } + } + return EINVAL; +} + +} diff --git a/Kernel/Graphics/DisplayConnector.h b/Kernel/Graphics/DisplayConnector.h new file mode 100644 index 0000000000..ee01d7cdf4 --- /dev/null +++ b/Kernel/Graphics/DisplayConnector.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Types.h> +#include <Kernel/Devices/CharacterDevice.h> +#include <LibC/sys/ioctl_numbers.h> +#include <LibEDID/EDID.h> + +namespace Kernel { + +class GraphicsManagement; +class DisplayConnector : public CharacterDevice { + friend class GraphicsManagement; + friend class DeviceManagement; + +public: + struct ModeSetting { + size_t horizontal_blanking_start() const + { + return horizontal_active; + } + size_t horizontal_sync_start() const + { + return horizontal_active + horizontal_front_porch_pixels; + } + size_t horizontal_sync_end() const + { + return horizontal_active + horizontal_front_porch_pixels + horizontal_sync_time_pixels; + } + size_t horizontal_total() const + { + return horizontal_active + horizontal_blank_pixels; + } + + size_t vertical_blanking_start() const + { + return vertical_active; + } + size_t vertical_sync_start() const + { + return vertical_active + vertical_front_porch_lines; + } + size_t vertical_sync_end() const + { + return vertical_active + vertical_front_porch_lines + vertical_sync_time_lines; + } + size_t vertical_total() const + { + return vertical_active + vertical_blank_lines; + } + + size_t horizontal_stride; // Note: This is commonly known as "pitch" + size_t pixel_clock_in_khz; + + size_t horizontal_active; + size_t horizontal_front_porch_pixels; + size_t horizontal_sync_time_pixels; + size_t horizontal_blank_pixels; + + size_t vertical_active; + size_t vertical_front_porch_lines; + size_t vertical_sync_time_lines; + size_t vertical_blank_lines; + + size_t horizontal_offset; // Note: This is commonly known as "x offset" + size_t vertical_offset; // Note: This is commonly known as "y offset" + }; + +public: + enum class DisplayMode { + Graphical, + Console, + }; + +public: + virtual ~DisplayConnector() = default; + + virtual bool mutable_mode_setting_capable() const = 0; + virtual bool double_framebuffering_capable() const = 0; + virtual bool flush_support() const = 0; + virtual bool partial_flush_support() const = 0; + // Note: This can indicate to userland if the underlying hardware requires + // a defined refresh rate being supplied when modesetting the screen resolution. + // Paravirtualized hardware don't need such setting and can safely ignore this. + virtual bool refresh_rate_support() const = 0; + + bool console_mode() const; + ErrorOr<ByteBuffer> get_edid() const; + virtual ErrorOr<void> set_mode_setting(ModeSetting const&) = 0; + virtual ErrorOr<void> set_safe_mode_setting() = 0; + ModeSetting current_mode_setting() const; + virtual ErrorOr<void> set_y_offset(size_t y) = 0; + virtual ErrorOr<void> unblank() = 0; + + void set_display_mode(Badge<GraphicsManagement>, DisplayMode); + +protected: + void set_edid_bytes(Array<u8, 128> const& edid_bytes); + + // ^File + virtual bool is_seekable() const override { return true; } + virtual bool can_read(OpenFileDescription const&, u64) const final override { return true; } + virtual bool can_write(OpenFileDescription const&, u64) const final override { return true; } + virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override final; + virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override final; + virtual ErrorOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64, int, bool) override final; + virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override final; + virtual StringView class_name() const override final { return "DisplayConnector"sv; } + + DisplayConnector(); + virtual ErrorOr<size_t> write_to_first_surface(u64 offset, UserOrKernelBuffer const&, size_t length) = 0; + virtual void enable_console() = 0; + virtual void disable_console() = 0; + virtual ErrorOr<void> flush_first_surface() = 0; + virtual ErrorOr<void> flush_rectangle(size_t buffer_index, FBRect const& rect); + + ErrorOr<void> initialize_edid_for_generic_monitor(); + + mutable Spinlock m_control_lock; + mutable Mutex m_flushing_lock; + + bool m_console_mode { false }; + + bool m_vertical_offsetted { false }; + + mutable Spinlock m_modeset_lock; + ModeSetting m_current_mode_setting {}; + + Optional<EDID::Parser> m_edid_parser; + EDID::Parser::RawBytes m_edid_bytes {}; + bool m_edid_valid { false }; + +private: + virtual void will_be_destroyed() override; + virtual void after_inserting() override; + + IntrusiveListNode<DisplayConnector, RefPtr<DisplayConnector>> m_list_node; +}; +} diff --git a/Kernel/Graphics/GraphicsManagement.cpp b/Kernel/Graphics/GraphicsManagement.cpp index 48536992dc..d5fb78d185 100644 --- a/Kernel/Graphics/GraphicsManagement.cpp +++ b/Kernel/Graphics/GraphicsManagement.cpp @@ -95,15 +95,34 @@ void GraphicsManagement::set_vga_text_mode_cursor(size_t console_width, size_t x void GraphicsManagement::deactivate_graphical_mode() { + // FIXME: Remove this once we don't support Framebuffer devices anymore. for (auto& graphics_device : m_graphics_devices) { graphics_device.enable_consoles(); } + + for (auto& connector : m_display_connector_nodes) { + connector.set_display_mode({}, DisplayConnector::DisplayMode::Console); + } } void GraphicsManagement::activate_graphical_mode() { + // FIXME: Remove this once we don't support Framebuffer devices anymore. for (auto& graphics_device : m_graphics_devices) { graphics_device.disable_consoles(); } + + for (auto& connector : m_display_connector_nodes) { + connector.set_display_mode({}, DisplayConnector::DisplayMode::Graphical); + } +} + +void GraphicsManagement::attach_new_display_connector(Badge<DisplayConnector>, DisplayConnector& connector) +{ + m_display_connector_nodes.append(connector); +} +void GraphicsManagement::detach_display_connector(Badge<DisplayConnector>, DisplayConnector& connector) +{ + m_display_connector_nodes.remove(connector); } static inline bool is_vga_compatible_pci_device(PCI::DeviceIdentifier const& device_identifier) diff --git a/Kernel/Graphics/GraphicsManagement.h b/Kernel/Graphics/GraphicsManagement.h index 428ac52b0d..b730d4012a 100644 --- a/Kernel/Graphics/GraphicsManagement.h +++ b/Kernel/Graphics/GraphicsManagement.h @@ -12,6 +12,7 @@ #include <AK/Types.h> #include <Kernel/Bus/PCI/Definitions.h> #include <Kernel/Graphics/Console/Console.h> +#include <Kernel/Graphics/DisplayConnector.h> #include <Kernel/Graphics/GenericGraphicsAdapter.h> #include <Kernel/Graphics/VGACompatibleAdapter.h> #include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h> @@ -29,6 +30,9 @@ public: unsigned allocate_minor_device_number() { return m_current_minor_number++; }; GraphicsManagement(); + void attach_new_display_connector(Badge<DisplayConnector>, DisplayConnector&); + void detach_display_connector(Badge<DisplayConnector>, DisplayConnector&); + bool framebuffer_devices_console_only() const; bool framebuffer_devices_use_bootloader_framebuffer() const; bool framebuffer_devices_exist() const; @@ -55,6 +59,8 @@ private: RefPtr<VGACompatibleAdapter> m_vga_adapter; unsigned m_current_minor_number { 0 }; + IntrusiveList<&DisplayConnector::m_list_node> m_display_connector_nodes; + RecursiveSpinlock m_main_vga_lock; bool m_vga_access_is_disabled { false }; }; diff --git a/Userland/Libraries/LibC/sys/ioctl_numbers.h b/Userland/Libraries/LibC/sys/ioctl_numbers.h index a1a314e378..95e8b74742 100644 --- a/Userland/Libraries/LibC/sys/ioctl_numbers.h +++ b/Userland/Libraries/LibC/sys/ioctl_numbers.h @@ -23,6 +23,7 @@ struct FBProperties { unsigned char doublebuffer_support; unsigned char flushing_support; unsigned char partial_flushing_support; + unsigned char refresh_rate_support; }; struct FBHeadProperties { @@ -36,6 +37,21 @@ struct FBHeadProperties { unsigned buffer_length; }; +struct FBHeadModeSetting { + int horizontal_stride; + int pixel_clock_in_khz; + int horizontal_active; + int horizontal_front_porch_pixels; + int horizontal_sync_time_pixels; + int horizontal_blank_pixels; + int vertical_active; + int vertical_front_porch_lines; + int vertical_sync_time_lines; + int vertical_blank_lines; + int horizontal_offset; + int vertical_offset; +}; + struct FBHeadResolution { int head_index; int pitch; @@ -103,6 +119,9 @@ enum IOCtlNumber { FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER, FB_IOCTL_FLUSH_HEAD_BUFFERS, FB_IOCTL_FLUSH_HEAD, + FB_IOCTL_SET_HEAD_MODE_SETTING, + FB_IOCTL_GET_HEAD_MODE_SETTING, + FB_IOCTL_SET_SAFE_HEAD_MODE_SETTING, KEYBOARD_IOCTL_GET_NUM_LOCK, KEYBOARD_IOCTL_SET_NUM_LOCK, KEYBOARD_IOCTL_GET_CAPS_LOCK, |