mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 14:28:12 +00:00
Kernel + WindowServer: Re-define the interface to framebuffer devices
We create a base class called GenericFramebufferDevice, which defines all the virtual functions that must be implemented by a FramebufferDevice. Then, we make the VirtIO FramebufferDevice and other FramebufferDevice implementations inherit from it. The most important consequence of rearranging the classes is that we now have one IOCTL method, so all drivers should be committed to not override the IOCTL method or make their own IOCTLs of FramebufferDevice. All graphical IOCTLs are known to all FramebufferDevices, and it's up to the specific implementation whether to support them or discard them (so we require extensive usage of KResult and KResultOr, together with virtual characteristic functions). As a result, the interface is much cleaner and understandable to read.
This commit is contained in:
parent
78e724a899
commit
8554952690
19 changed files with 673 additions and 350 deletions
|
@ -13,39 +13,42 @@
|
||||||
|
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
ALWAYS_INLINE int fb_get_size_in_bytes(int fd, size_t* out)
|
ALWAYS_INLINE int fb_get_properties(int fd, FBProperties* info)
|
||||||
{
|
{
|
||||||
return ioctl(fd, FB_IOCTL_GET_SIZE_IN_BYTES, out);
|
return ioctl(fd, FB_IOCTL_GET_PROPERTIES, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE int fb_get_resolution(int fd, FBResolution* info)
|
ALWAYS_INLINE int fb_get_head_properties(int fd, FBHeadProperties* info)
|
||||||
{
|
{
|
||||||
return ioctl(fd, FB_IOCTL_GET_RESOLUTION, info);
|
return ioctl(fd, FB_IOCTL_GET_HEAD_PROPERTIES, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE int fb_set_resolution(int fd, FBResolution* info)
|
ALWAYS_INLINE int fb_get_resolution(int fd, FBHeadResolution* info)
|
||||||
{
|
{
|
||||||
return ioctl(fd, FB_IOCTL_SET_RESOLUTION, info);
|
FBHeadProperties head_properties;
|
||||||
|
head_properties.head_index = info->head_index;
|
||||||
|
if (auto rc = ioctl(fd, FB_IOCTL_GET_HEAD_PROPERTIES, &head_properties); rc < 0)
|
||||||
|
return rc;
|
||||||
|
info->head_index = head_properties.head_index;
|
||||||
|
info->pitch = head_properties.pitch;
|
||||||
|
info->width = head_properties.width;
|
||||||
|
info->height = head_properties.height;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE int fb_get_buffer_offset(int fd, int index, unsigned* offset)
|
ALWAYS_INLINE int fb_set_resolution(int fd, FBHeadResolution* info)
|
||||||
{
|
{
|
||||||
FBBufferOffset fb_buffer_offset;
|
return ioctl(fd, FB_IOCTL_SET_HEAD_RESOLUTION, info);
|
||||||
fb_buffer_offset.buffer_index = index;
|
|
||||||
int result = ioctl(fd, FB_IOCTL_GET_BUFFER_OFFSET, &fb_buffer_offset);
|
|
||||||
if (result == 0)
|
|
||||||
*offset = fb_buffer_offset.offset;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE int fb_get_buffer(int fd, int* index)
|
ALWAYS_INLINE int fb_get_head_vertical_offset_buffer(int fd, FBHeadVerticalOffset* vertical_offset)
|
||||||
{
|
{
|
||||||
return ioctl(fd, FB_IOCTL_GET_BUFFER, index);
|
return ioctl(fd, FB_IOCTL_GET_HEAD_VERITCAL_OFFSET_BUFFER, vertical_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE int fb_set_buffer(int fd, int index)
|
ALWAYS_INLINE int fb_set_head_vertical_offset_buffer(int fd, FBHeadVerticalOffset* vertical_offset)
|
||||||
{
|
{
|
||||||
return ioctl(fd, FB_IOCTL_SET_BUFFER, index);
|
return ioctl(fd, FB_IOCTL_SET_HEAD_VERITCAL_OFFSET_BUFFER, vertical_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE int fb_flush_buffers(int fd, int index, FBRect const* rects, unsigned count)
|
ALWAYS_INLINE int fb_flush_buffers(int fd, int index, FBRect const* rects, unsigned count)
|
||||||
|
@ -54,7 +57,7 @@ ALWAYS_INLINE int fb_flush_buffers(int fd, int index, FBRect const* rects, unsig
|
||||||
fb_flush_rects.buffer_index = index;
|
fb_flush_rects.buffer_index = index;
|
||||||
fb_flush_rects.count = count;
|
fb_flush_rects.count = count;
|
||||||
fb_flush_rects.rects = rects;
|
fb_flush_rects.rects = rects;
|
||||||
return ioctl(fd, FB_IOCTL_FLUSH_BUFFERS, &fb_flush_rects);
|
return ioctl(fd, FB_IOCTL_FLUSH_HEAD_BUFFERS, &fb_flush_rects);
|
||||||
}
|
}
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|
|
@ -80,6 +80,7 @@ set(KERNEL_SOURCES
|
||||||
Graphics/VirtIOGPU/Console.cpp
|
Graphics/VirtIOGPU/Console.cpp
|
||||||
Graphics/VirtIOGPU/GraphicsAdapter.cpp
|
Graphics/VirtIOGPU/GraphicsAdapter.cpp
|
||||||
Graphics/VGACompatibleAdapter.cpp
|
Graphics/VGACompatibleAdapter.cpp
|
||||||
|
Graphics/GenericFramebufferDevice.cpp
|
||||||
SanCov.cpp
|
SanCov.cpp
|
||||||
Storage/Partition/DiskPartition.cpp
|
Storage/Partition/DiskPartition.cpp
|
||||||
Storage/Partition/DiskPartitionMetadata.cpp
|
Storage/Partition/DiskPartitionMetadata.cpp
|
||||||
|
|
|
@ -127,9 +127,9 @@ UNMAP_AFTER_INIT BochsGraphicsAdapter::BochsGraphicsAdapter(PCI::DeviceIdentifie
|
||||||
UNMAP_AFTER_INIT void BochsGraphicsAdapter::initialize_framebuffer_devices()
|
UNMAP_AFTER_INIT void BochsGraphicsAdapter::initialize_framebuffer_devices()
|
||||||
{
|
{
|
||||||
// FIXME: Find a better way to determine default resolution...
|
// FIXME: Find a better way to determine default resolution...
|
||||||
m_framebuffer_device = FramebufferDevice::create(*this, 0, PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffff0), 1024, 768, 1024 * sizeof(u32));
|
m_framebuffer_device = FramebufferDevice::create(*this, PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffff0), 1024, 768, 1024 * sizeof(u32));
|
||||||
// FIXME: Would be nice to be able to return a KResult here.
|
// FIXME: Would be nice to be able to return a KResult here.
|
||||||
VERIFY(!m_framebuffer_device->initialize().is_error());
|
VERIFY(!m_framebuffer_device->try_to_initialize().is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BochsGraphicsAdapter::vga_compatible() const
|
bool BochsGraphicsAdapter::vga_compatible() const
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Checked.h>
|
#include <AK/Checked.h>
|
||||||
|
#include <AK/Try.h>
|
||||||
#include <Kernel/Debug.h>
|
#include <Kernel/Debug.h>
|
||||||
#include <Kernel/Devices/DeviceManagement.h>
|
#include <Kernel/Devices/DeviceManagement.h>
|
||||||
#include <Kernel/Graphics/FramebufferDevice.h>
|
#include <Kernel/Graphics/FramebufferDevice.h>
|
||||||
|
@ -16,14 +17,11 @@
|
||||||
#include <LibC/errno_numbers.h>
|
#include <LibC/errno_numbers.h>
|
||||||
#include <LibC/sys/ioctl_numbers.h>
|
#include <LibC/sys/ioctl_numbers.h>
|
||||||
|
|
||||||
#define MAX_RESOLUTION_WIDTH 4096
|
|
||||||
#define MAX_RESOLUTION_HEIGHT 2160
|
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
NonnullRefPtr<FramebufferDevice> FramebufferDevice::create(const GenericGraphicsAdapter& adapter, size_t output_port_index, PhysicalAddress paddr, size_t width, size_t height, size_t pitch)
|
NonnullRefPtr<FramebufferDevice> FramebufferDevice::create(const GenericGraphicsAdapter& adapter, PhysicalAddress paddr, size_t width, size_t height, size_t pitch)
|
||||||
{
|
{
|
||||||
auto framebuffer_device_or_error = DeviceManagement::try_create_device<FramebufferDevice>(adapter, output_port_index, paddr, width, height, pitch);
|
auto framebuffer_device_or_error = DeviceManagement::try_create_device<FramebufferDevice>(adapter, paddr, width, height, pitch);
|
||||||
// FIXME: Find a way to propagate errors
|
// FIXME: Find a way to propagate errors
|
||||||
VERIFY(!framebuffer_device_or_error.is_error());
|
VERIFY(!framebuffer_device_or_error.is_error());
|
||||||
return framebuffer_device_or_error.release_value();
|
return framebuffer_device_or_error.release_value();
|
||||||
|
@ -37,14 +35,15 @@ KResultOr<Memory::Region*> FramebufferDevice::mmap(Process& process, OpenFileDes
|
||||||
return ENODEV;
|
return ENODEV;
|
||||||
if (offset != 0)
|
if (offset != 0)
|
||||||
return ENXIO;
|
return ENXIO;
|
||||||
if (range.size() != Memory::page_round_up(framebuffer_size_in_bytes()))
|
auto framebuffer_length = TRY(buffer_length(0));
|
||||||
|
if (range.size() != Memory::page_round_up(framebuffer_length))
|
||||||
return EOVERFLOW;
|
return EOVERFLOW;
|
||||||
|
|
||||||
m_userspace_real_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(m_framebuffer_address, Memory::page_round_up(framebuffer_size_in_bytes())));
|
m_userspace_real_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(m_framebuffer_address, Memory::page_round_up(framebuffer_length)));
|
||||||
m_real_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(m_framebuffer_address, Memory::page_round_up(framebuffer_size_in_bytes())));
|
m_real_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(m_framebuffer_address, Memory::page_round_up(framebuffer_length)));
|
||||||
m_swapped_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(Memory::page_round_up(framebuffer_size_in_bytes()), AllocationStrategy::AllocateNow));
|
m_swapped_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(Memory::page_round_up(framebuffer_length), AllocationStrategy::AllocateNow));
|
||||||
m_real_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, Memory::page_round_up(framebuffer_size_in_bytes()), "Framebuffer", Memory::Region::Access::ReadWrite));
|
m_real_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, Memory::page_round_up(framebuffer_length), "Framebuffer", Memory::Region::Access::ReadWrite));
|
||||||
m_swapped_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, Memory::page_round_up(framebuffer_size_in_bytes()), "Framebuffer Swap (Blank)", Memory::Region::Access::ReadWrite));
|
m_swapped_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, Memory::page_round_up(framebuffer_length), "Framebuffer Swap (Blank)", Memory::Region::Access::ReadWrite));
|
||||||
|
|
||||||
RefPtr<Memory::VMObject> chosen_vmobject;
|
RefPtr<Memory::VMObject> chosen_vmobject;
|
||||||
if (m_graphical_writes_enabled) {
|
if (m_graphical_writes_enabled) {
|
||||||
|
@ -67,7 +66,9 @@ void FramebufferDevice::deactivate_writes()
|
||||||
SpinlockLocker lock(m_activation_lock);
|
SpinlockLocker lock(m_activation_lock);
|
||||||
if (!m_userspace_framebuffer_region)
|
if (!m_userspace_framebuffer_region)
|
||||||
return;
|
return;
|
||||||
memcpy(m_swapped_framebuffer_region->vaddr().as_ptr(), m_real_framebuffer_region->vaddr().as_ptr(), Memory::page_round_up(framebuffer_size_in_bytes()));
|
auto framebuffer_length_or_error = buffer_length(0);
|
||||||
|
VERIFY(!framebuffer_length_or_error.is_error());
|
||||||
|
memcpy(m_swapped_framebuffer_region->vaddr().as_ptr(), m_real_framebuffer_region->vaddr().as_ptr(), Memory::page_round_up(framebuffer_length_or_error.release_value()));
|
||||||
auto vmobject = m_swapped_framebuffer_vmobject;
|
auto vmobject = m_swapped_framebuffer_vmobject;
|
||||||
m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
|
m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
|
||||||
m_userspace_framebuffer_region->remap();
|
m_userspace_framebuffer_region->remap();
|
||||||
|
@ -81,39 +82,34 @@ void FramebufferDevice::activate_writes()
|
||||||
// restore the image we had in the void area
|
// restore the image we had in the void area
|
||||||
// FIXME: if we happen to have multiple Framebuffers that are writing to that location
|
// FIXME: if we happen to have multiple Framebuffers that are writing to that location
|
||||||
// we will experience glitches...
|
// we will experience glitches...
|
||||||
memcpy(m_real_framebuffer_region->vaddr().as_ptr(), m_swapped_framebuffer_region->vaddr().as_ptr(), Memory::page_round_up(framebuffer_size_in_bytes()));
|
auto framebuffer_length_or_error = buffer_length(0);
|
||||||
|
VERIFY(!framebuffer_length_or_error.is_error());
|
||||||
|
|
||||||
|
memcpy(m_real_framebuffer_region->vaddr().as_ptr(), m_swapped_framebuffer_region->vaddr().as_ptr(), Memory::page_round_up(framebuffer_length_or_error.release_value()));
|
||||||
auto vmobject = m_userspace_real_framebuffer_vmobject;
|
auto vmobject = m_userspace_real_framebuffer_vmobject;
|
||||||
m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
|
m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
|
||||||
m_userspace_framebuffer_region->remap();
|
m_userspace_framebuffer_region->remap();
|
||||||
m_graphical_writes_enabled = true;
|
m_graphical_writes_enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
UNMAP_AFTER_INIT KResult FramebufferDevice::initialize()
|
UNMAP_AFTER_INIT KResult FramebufferDevice::try_to_initialize()
|
||||||
{
|
{
|
||||||
// FIXME: Would be nice to be able to unify this with mmap above, but this
|
// FIXME: Would be nice to be able to unify this with mmap above, but this
|
||||||
// function is UNMAP_AFTER_INIT for the time being.
|
// function is UNMAP_AFTER_INIT for the time being.
|
||||||
m_real_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(m_framebuffer_address, Memory::page_round_up(framebuffer_size_in_bytes())));
|
auto framebuffer_length = TRY(buffer_length(0));
|
||||||
m_swapped_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(Memory::page_round_up(framebuffer_size_in_bytes()), AllocationStrategy::AllocateNow));
|
m_real_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(m_framebuffer_address, Memory::page_round_up(framebuffer_length)));
|
||||||
m_real_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, Memory::page_round_up(framebuffer_size_in_bytes()), "Framebuffer", Memory::Region::Access::ReadWrite));
|
m_swapped_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(Memory::page_round_up(framebuffer_length), AllocationStrategy::AllocateNow));
|
||||||
m_swapped_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, Memory::page_round_up(framebuffer_size_in_bytes()), "Framebuffer Swap (Blank)", Memory::Region::Access::ReadWrite));
|
m_real_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, Memory::page_round_up(framebuffer_length), "Framebuffer", Memory::Region::Access::ReadWrite));
|
||||||
|
m_swapped_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, Memory::page_round_up(framebuffer_length), "Framebuffer Swap (Blank)", Memory::Region::Access::ReadWrite));
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(const GenericGraphicsAdapter& adapter, size_t output_port_index)
|
UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(const GenericGraphicsAdapter& adapter, PhysicalAddress addr, size_t width, size_t height, size_t pitch)
|
||||||
: BlockDevice(29, GraphicsManagement::the().allocate_minor_device_number())
|
: GenericFramebufferDevice(adapter)
|
||||||
, m_graphics_adapter(adapter)
|
|
||||||
, m_output_port_index(output_port_index)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(const GenericGraphicsAdapter& adapter, size_t output_port_index, PhysicalAddress addr, size_t width, size_t height, size_t pitch)
|
|
||||||
: BlockDevice(29, GraphicsManagement::the().allocate_minor_device_number())
|
|
||||||
, m_graphics_adapter(adapter)
|
|
||||||
, m_framebuffer_address(addr)
|
, m_framebuffer_address(addr)
|
||||||
, m_framebuffer_pitch(pitch)
|
, m_framebuffer_pitch(pitch)
|
||||||
, m_framebuffer_width(width)
|
, m_framebuffer_width(width)
|
||||||
, m_framebuffer_height(height)
|
, m_framebuffer_height(height)
|
||||||
, m_output_port_index(output_port_index)
|
|
||||||
{
|
{
|
||||||
VERIFY(!m_framebuffer_address.is_null());
|
VERIFY(!m_framebuffer_address.is_null());
|
||||||
VERIFY(m_framebuffer_pitch);
|
VERIFY(m_framebuffer_pitch);
|
||||||
|
@ -122,99 +118,125 @@ UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(const GenericGraphicsAdapt
|
||||||
dbgln("Framebuffer {}: address={}, pitch={}, width={}, height={}", minor(), addr, pitch, width, height);
|
dbgln("Framebuffer {}: address={}, pitch={}, width={}, height={}", minor(), addr, pitch, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FramebufferDevice::framebuffer_size_in_bytes() const
|
KResultOr<size_t> FramebufferDevice::buffer_length(size_t head) const
|
||||||
{
|
{
|
||||||
if (m_graphics_adapter->double_framebuffering_capable())
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
|
VERIFY(head == 0);
|
||||||
|
MutexLocker locker(m_resolution_lock);
|
||||||
|
auto adapter = m_graphics_adapter.strong_ref();
|
||||||
|
if (!adapter)
|
||||||
|
return KResult(EIO);
|
||||||
|
if (adapter->double_framebuffering_capable())
|
||||||
return m_framebuffer_pitch * m_framebuffer_height * 2;
|
return m_framebuffer_pitch * m_framebuffer_height * 2;
|
||||||
return m_framebuffer_pitch * m_framebuffer_height;
|
return m_framebuffer_pitch * m_framebuffer_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
KResult FramebufferDevice::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
|
KResultOr<size_t> FramebufferDevice::pitch(size_t head) const
|
||||||
{
|
{
|
||||||
REQUIRE_PROMISE(video);
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
switch (request) {
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
case FB_IOCTL_GET_SIZE_IN_BYTES: {
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
auto user_size = static_ptr_cast<size_t*>(arg);
|
VERIFY(head == 0);
|
||||||
size_t value = framebuffer_size_in_bytes();
|
MutexLocker locker(m_resolution_lock);
|
||||||
return copy_to_user(user_size, &value);
|
return m_framebuffer_pitch;
|
||||||
}
|
}
|
||||||
case FB_IOCTL_GET_BUFFER: {
|
KResultOr<size_t> FramebufferDevice::height(size_t head) const
|
||||||
auto user_index = static_ptr_cast<int*>(arg);
|
{
|
||||||
int value = m_y_offset == 0 ? 0 : 1;
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
TRY(copy_to_user(user_index, &value));
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
if (!m_graphics_adapter->double_framebuffering_capable())
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
return ENOTIMPL;
|
VERIFY(head == 0);
|
||||||
return KSuccess;
|
MutexLocker locker(m_resolution_lock);
|
||||||
}
|
return m_framebuffer_height;
|
||||||
case FB_IOCTL_SET_BUFFER: {
|
}
|
||||||
auto buffer = static_cast<int>(arg.ptr());
|
KResultOr<size_t> FramebufferDevice::width(size_t head) const
|
||||||
if (buffer != 0 && buffer != 1)
|
{
|
||||||
return EINVAL;
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
if (!m_graphics_adapter->double_framebuffering_capable())
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
return ENOTIMPL;
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
m_graphics_adapter->set_y_offset(m_output_port_index, buffer == 0 ? 0 : m_framebuffer_height);
|
VERIFY(head == 0);
|
||||||
return KSuccess;
|
MutexLocker locker(m_resolution_lock);
|
||||||
}
|
return m_framebuffer_width;
|
||||||
case FB_IOCTL_GET_RESOLUTION: {
|
}
|
||||||
auto user_resolution = static_ptr_cast<FBResolution*>(arg);
|
KResultOr<size_t> FramebufferDevice::vertical_offset(size_t head) const
|
||||||
FBResolution resolution {};
|
{
|
||||||
resolution.pitch = m_framebuffer_pitch;
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
resolution.width = m_framebuffer_width;
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
resolution.height = m_framebuffer_height;
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
return copy_to_user(user_resolution, &resolution);
|
VERIFY(head == 0);
|
||||||
}
|
MutexLocker locker(m_buffer_offset_lock);
|
||||||
case FB_IOCTL_SET_RESOLUTION: {
|
return m_y_offset;
|
||||||
auto user_resolution = static_ptr_cast<FBResolution*>(arg);
|
}
|
||||||
FBResolution resolution;
|
KResultOr<bool> FramebufferDevice::vertical_offseted(size_t head) const
|
||||||
TRY(copy_from_user(&resolution, user_resolution));
|
{
|
||||||
if (resolution.width > MAX_RESOLUTION_WIDTH || resolution.height > MAX_RESOLUTION_HEIGHT)
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
return EINVAL;
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
|
VERIFY(head == 0);
|
||||||
|
MutexLocker locker(m_buffer_offset_lock);
|
||||||
|
return m_y_offset == 0 ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_graphics_adapter->modesetting_capable()) {
|
KResult FramebufferDevice::set_head_resolution(size_t head, size_t width, size_t height, size_t)
|
||||||
resolution.pitch = m_framebuffer_pitch;
|
{
|
||||||
resolution.width = m_framebuffer_width;
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
resolution.height = m_framebuffer_height;
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
TRY(copy_to_user(user_resolution, &resolution));
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
return ENOTIMPL;
|
VERIFY(head == 0);
|
||||||
|
MutexLocker buffer_offset_locker(m_buffer_offset_lock);
|
||||||
|
MutexLocker resolution_locker(m_resolution_lock);
|
||||||
|
auto adapter = m_graphics_adapter.strong_ref();
|
||||||
|
if (!adapter)
|
||||||
|
return KResult(EIO);
|
||||||
|
auto result = adapter->try_to_set_resolution(0, width, height);
|
||||||
|
// FIXME: Find a better way to return here a KResult.
|
||||||
|
if (!result)
|
||||||
|
return KResult(ENOTSUP);
|
||||||
|
m_framebuffer_width = width;
|
||||||
|
m_framebuffer_height = height;
|
||||||
|
m_framebuffer_pitch = width * sizeof(u32);
|
||||||
|
return KSuccess;
|
||||||
|
}
|
||||||
|
KResult FramebufferDevice::set_head_buffer(size_t head, bool second_buffer)
|
||||||
|
{
|
||||||
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
|
VERIFY(head == 0);
|
||||||
|
MutexLocker locker(m_buffer_offset_lock);
|
||||||
|
auto adapter = m_graphics_adapter.strong_ref();
|
||||||
|
if (!adapter)
|
||||||
|
return KResult(EIO);
|
||||||
|
if (second_buffer) {
|
||||||
|
if (!adapter->set_y_offset(0, m_framebuffer_height)) {
|
||||||
|
// FIXME: Find a better KResult here.
|
||||||
|
return KResult(ENOTSUP);
|
||||||
}
|
}
|
||||||
|
m_y_offset = m_framebuffer_height;
|
||||||
if (!m_graphics_adapter->try_to_set_resolution(m_output_port_index, resolution.width, resolution.height)) {
|
} else {
|
||||||
m_framebuffer_pitch = m_framebuffer_width * sizeof(u32);
|
if (!adapter->set_y_offset(0, 0)) {
|
||||||
dbgln_if(FRAMEBUFFER_DEVICE_DEBUG, "Reverting resolution: [{}x{}]", m_framebuffer_width, m_framebuffer_height);
|
// FIXME: Find a better KResult here.
|
||||||
// Note: We try to revert everything back, and if it doesn't work, just assert.
|
return KResult(ENOTSUP);
|
||||||
if (!m_graphics_adapter->try_to_set_resolution(m_output_port_index, m_framebuffer_width, m_framebuffer_height)) {
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
resolution.pitch = m_framebuffer_pitch;
|
|
||||||
resolution.width = m_framebuffer_width;
|
|
||||||
resolution.height = m_framebuffer_height;
|
|
||||||
TRY(copy_to_user(user_resolution, &resolution));
|
|
||||||
return EINVAL;
|
|
||||||
}
|
}
|
||||||
m_framebuffer_width = resolution.width;
|
m_y_offset = 0;
|
||||||
m_framebuffer_height = resolution.height;
|
|
||||||
m_framebuffer_pitch = m_framebuffer_width * sizeof(u32);
|
|
||||||
|
|
||||||
dbgln_if(FRAMEBUFFER_DEVICE_DEBUG, "New resolution: [{}x{}]", m_framebuffer_width, m_framebuffer_height);
|
|
||||||
resolution.pitch = m_framebuffer_pitch;
|
|
||||||
resolution.width = m_framebuffer_width;
|
|
||||||
resolution.height = m_framebuffer_height;
|
|
||||||
return copy_to_user(user_resolution, &resolution);
|
|
||||||
}
|
}
|
||||||
case FB_IOCTL_GET_BUFFER_OFFSET: {
|
return KSuccess;
|
||||||
auto user_buffer_offset = static_ptr_cast<FBBufferOffset*>(arg);
|
}
|
||||||
FBBufferOffset buffer_offset;
|
KResult FramebufferDevice::flush_head_buffer(size_t)
|
||||||
TRY(copy_from_user(&buffer_offset, user_buffer_offset));
|
{
|
||||||
if (buffer_offset.buffer_index != 0 && buffer_offset.buffer_index != 1)
|
// Note: This FramebufferDevice class doesn't support flushing.
|
||||||
return EINVAL;
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
buffer_offset.offset = (size_t)buffer_offset.buffer_index * m_framebuffer_pitch * m_framebuffer_height;
|
// so if we happen to accidentally reach this code, assert.
|
||||||
return copy_to_user(user_buffer_offset, &buffer_offset);
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
case FB_IOCTL_FLUSH_BUFFERS:
|
KResult FramebufferDevice::flush_rectangle(size_t, FBRect const&)
|
||||||
return ENOTSUP;
|
{
|
||||||
default:
|
// Note: This FramebufferDevice class doesn't support partial flushing.
|
||||||
return EINVAL;
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
};
|
// so if we happen to accidentally reach this code, assert.
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <AK/NonnullOwnPtr.h>
|
#include <AK/NonnullOwnPtr.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
#include <Kernel/Devices/BlockDevice.h>
|
#include <Kernel/Graphics/GenericFramebufferDevice.h>
|
||||||
#include <Kernel/Graphics/GenericGraphicsAdapter.h>
|
#include <Kernel/Graphics/GenericGraphicsAdapter.h>
|
||||||
#include <Kernel/Locking/Spinlock.h>
|
#include <Kernel/Locking/Spinlock.h>
|
||||||
#include <Kernel/Memory/AnonymousVMObject.h>
|
#include <Kernel/Memory/AnonymousVMObject.h>
|
||||||
|
@ -17,38 +17,38 @@
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
class FramebufferDevice : public BlockDevice {
|
class FramebufferDevice final : public GenericFramebufferDevice {
|
||||||
AK_MAKE_ETERNAL
|
AK_MAKE_ETERNAL
|
||||||
friend class DeviceManagement;
|
friend class DeviceManagement;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static NonnullRefPtr<FramebufferDevice> create(const GenericGraphicsAdapter&, size_t, PhysicalAddress, size_t, size_t, size_t);
|
static NonnullRefPtr<FramebufferDevice> create(const GenericGraphicsAdapter&, PhysicalAddress, size_t, size_t, size_t);
|
||||||
|
|
||||||
virtual KResult ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override;
|
|
||||||
virtual KResultOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override;
|
virtual KResultOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override;
|
||||||
|
|
||||||
virtual void deactivate_writes();
|
virtual void deactivate_writes() override;
|
||||||
virtual void activate_writes();
|
virtual void activate_writes() override;
|
||||||
size_t framebuffer_size_in_bytes() const;
|
|
||||||
|
|
||||||
virtual ~FramebufferDevice() {};
|
virtual KResult try_to_initialize() override;
|
||||||
KResult initialize();
|
|
||||||
|
|
||||||
protected:
|
virtual bool multihead_support() const override { return false; }
|
||||||
FramebufferDevice(const GenericGraphicsAdapter&, size_t);
|
virtual bool flushing_support() const override { return false; }
|
||||||
NonnullRefPtr<GenericGraphicsAdapter> m_graphics_adapter;
|
virtual bool partial_flushing_support() const override { return false; }
|
||||||
|
virtual size_t heads_count() const override { return 1; }
|
||||||
|
virtual KResultOr<size_t> buffer_length(size_t head) const override;
|
||||||
|
virtual KResultOr<size_t> pitch(size_t head) const override;
|
||||||
|
virtual KResultOr<size_t> height(size_t head) const override;
|
||||||
|
virtual KResultOr<size_t> width(size_t head) const override;
|
||||||
|
virtual KResultOr<size_t> vertical_offset(size_t head) const override;
|
||||||
|
virtual KResultOr<bool> vertical_offseted(size_t head) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FramebufferDevice(const GenericGraphicsAdapter&, size_t, PhysicalAddress, size_t, size_t, size_t);
|
virtual KResult set_head_resolution(size_t head, size_t width, size_t height, size_t pitch) override;
|
||||||
|
virtual KResult set_head_buffer(size_t head, bool second_buffer) override;
|
||||||
|
virtual KResult flush_head_buffer(size_t head) override;
|
||||||
|
virtual KResult flush_rectangle(size_t head, FBRect const&) override;
|
||||||
|
|
||||||
// ^File
|
FramebufferDevice(const GenericGraphicsAdapter&, PhysicalAddress, size_t, size_t, size_t);
|
||||||
virtual StringView class_name() const override { return "FramebufferDevice"sv; }
|
|
||||||
|
|
||||||
virtual bool can_read(const OpenFileDescription&, size_t) const override final { return true; }
|
|
||||||
virtual bool can_write(const OpenFileDescription&, size_t) const override final { return true; }
|
|
||||||
virtual void start_request(AsyncBlockDeviceRequest& request) override final { request.complete(AsyncDeviceRequest::Failure); }
|
|
||||||
virtual KResultOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override { return EINVAL; }
|
|
||||||
virtual KResultOr<size_t> write(OpenFileDescription&, u64, const UserOrKernelBuffer&, size_t) override { return EINVAL; }
|
|
||||||
|
|
||||||
PhysicalAddress m_framebuffer_address;
|
PhysicalAddress m_framebuffer_address;
|
||||||
size_t m_framebuffer_pitch { 0 };
|
size_t m_framebuffer_pitch { 0 };
|
||||||
|
@ -56,6 +56,7 @@ private:
|
||||||
size_t m_framebuffer_height { 0 };
|
size_t m_framebuffer_height { 0 };
|
||||||
|
|
||||||
Spinlock m_activation_lock;
|
Spinlock m_activation_lock;
|
||||||
|
mutable Mutex m_buffer_offset_lock;
|
||||||
|
|
||||||
RefPtr<Memory::AnonymousVMObject> m_real_framebuffer_vmobject;
|
RefPtr<Memory::AnonymousVMObject> m_real_framebuffer_vmobject;
|
||||||
RefPtr<Memory::AnonymousVMObject> m_swapped_framebuffer_vmobject;
|
RefPtr<Memory::AnonymousVMObject> m_swapped_framebuffer_vmobject;
|
||||||
|
@ -68,7 +69,6 @@ private:
|
||||||
Memory::Region* m_userspace_framebuffer_region { nullptr };
|
Memory::Region* m_userspace_framebuffer_region { nullptr };
|
||||||
|
|
||||||
size_t m_y_offset { 0 };
|
size_t m_y_offset { 0 };
|
||||||
size_t m_output_port_index;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
139
Kernel/Graphics/GenericFramebufferDevice.cpp
Normal file
139
Kernel/Graphics/GenericFramebufferDevice.cpp
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/Checked.h>
|
||||||
|
#include <AK/Try.h>
|
||||||
|
#include <Kernel/Debug.h>
|
||||||
|
#include <Kernel/Devices/DeviceManagement.h>
|
||||||
|
#include <Kernel/Graphics/FramebufferDevice.h>
|
||||||
|
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||||
|
#include <Kernel/Memory/AnonymousVMObject.h>
|
||||||
|
#include <Kernel/Memory/MemoryManager.h>
|
||||||
|
#include <Kernel/Process.h>
|
||||||
|
#include <Kernel/Sections.h>
|
||||||
|
#include <Kernel/StdLib.h>
|
||||||
|
#include <LibC/errno_numbers.h>
|
||||||
|
|
||||||
|
#define MAX_RESOLUTION_WIDTH 4096
|
||||||
|
#define MAX_RESOLUTION_HEIGHT 2160
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
KResult GenericFramebufferDevice::verify_head_index(int head_index) const
|
||||||
|
{
|
||||||
|
if (head_index < 0)
|
||||||
|
return KResult(EINVAL);
|
||||||
|
if (!multihead_support() && head_index > 0)
|
||||||
|
return KResult(ENOTSUP);
|
||||||
|
return KSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
KResult GenericFramebufferDevice::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
|
||||||
|
{
|
||||||
|
REQUIRE_PROMISE(video);
|
||||||
|
switch (request) {
|
||||||
|
case FB_IOCTL_GET_PROPERTIES: {
|
||||||
|
auto user_properties = static_ptr_cast<FBProperties*>(arg);
|
||||||
|
FBProperties properties;
|
||||||
|
auto adapter = m_graphics_adapter.strong_ref();
|
||||||
|
if (!adapter)
|
||||||
|
return KResult(EIO);
|
||||||
|
properties.multihead_support = multihead_support();
|
||||||
|
properties.flushing_support = flushing_support();
|
||||||
|
properties.doublebuffer_support = adapter->double_framebuffering_capable();
|
||||||
|
properties.partial_flushing_support = partial_flushing_support();
|
||||||
|
return copy_to_user(user_properties, &properties);
|
||||||
|
}
|
||||||
|
case FB_IOCTL_GET_HEAD_PROPERTIES: {
|
||||||
|
auto user_head_properties = static_ptr_cast<FBHeadProperties*>(arg);
|
||||||
|
FBHeadProperties head_properties;
|
||||||
|
TRY(copy_from_user(&head_properties, user_head_properties));
|
||||||
|
TRY(verify_head_index(head_properties.head_index));
|
||||||
|
|
||||||
|
head_properties.pitch = TRY(pitch(head_properties.head_index));
|
||||||
|
head_properties.width = TRY(width(head_properties.head_index));
|
||||||
|
head_properties.height = TRY(height(head_properties.head_index));
|
||||||
|
head_properties.buffer_length = TRY(buffer_length(head_properties.head_index));
|
||||||
|
head_properties.offset = TRY(vertical_offset(head_properties.head_index));
|
||||||
|
return copy_to_user(user_head_properties, &head_properties);
|
||||||
|
}
|
||||||
|
case FB_IOCTL_SET_HEAD_RESOLUTION: {
|
||||||
|
auto user_head_resolution = static_ptr_cast<FBHeadResolution*>(arg);
|
||||||
|
FBHeadResolution head_resolution;
|
||||||
|
TRY(copy_from_user(&head_resolution, user_head_resolution));
|
||||||
|
TRY(verify_head_index(head_resolution.head_index));
|
||||||
|
|
||||||
|
if (head_resolution.pitch < 0)
|
||||||
|
return KResult(EINVAL);
|
||||||
|
if (head_resolution.width < 0)
|
||||||
|
return KResult(EINVAL);
|
||||||
|
if (head_resolution.height < 0)
|
||||||
|
return KResult(EINVAL);
|
||||||
|
TRY(set_head_resolution(head_resolution.head_index, head_resolution.width, head_resolution.height, head_resolution.pitch));
|
||||||
|
return KSuccess;
|
||||||
|
}
|
||||||
|
case FB_IOCTL_SET_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));
|
||||||
|
TRY(verify_head_index(head_vertical_buffer_offset.head_index));
|
||||||
|
|
||||||
|
if (head_vertical_buffer_offset.offseted < 0 || head_vertical_buffer_offset.offseted > 1)
|
||||||
|
return KResult(EINVAL);
|
||||||
|
TRY(set_head_buffer(head_vertical_buffer_offset.head_index, head_vertical_buffer_offset.offseted));
|
||||||
|
return KSuccess;
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
TRY(verify_head_index(head_vertical_buffer_offset.head_index));
|
||||||
|
|
||||||
|
head_vertical_buffer_offset.offseted = TRY(vertical_offseted(head_vertical_buffer_offset.head_index));
|
||||||
|
return copy_to_user(user_head_vertical_buffer_offset, &head_vertical_buffer_offset);
|
||||||
|
}
|
||||||
|
case FB_IOCTL_FLUSH_HEAD_BUFFERS: {
|
||||||
|
if (!partial_flushing_support())
|
||||||
|
return KResult(ENOTSUP);
|
||||||
|
auto user_flush_rects = static_ptr_cast<FBFlushRects*>(arg);
|
||||||
|
FBFlushRects flush_rects;
|
||||||
|
TRY(copy_from_user(&flush_rects, user_flush_rects));
|
||||||
|
if (Checked<unsigned>::multiplication_would_overflow(flush_rects.count, sizeof(FBRect)))
|
||||||
|
return KResult(EFAULT);
|
||||||
|
MutexLocker locker(m_flushing_lock);
|
||||||
|
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]));
|
||||||
|
TRY(flush_rectangle(flush_rects.buffer_index, user_dirty_rect));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return KSuccess;
|
||||||
|
};
|
||||||
|
case FB_IOCTL_FLUSH_HEAD: {
|
||||||
|
if (!flushing_support())
|
||||||
|
return KResult(ENOTSUP);
|
||||||
|
// Note: We accept a FBRect, but we only really care about the head_index value.
|
||||||
|
auto user_rect = static_ptr_cast<FBRect*>(arg);
|
||||||
|
FBRect rect;
|
||||||
|
TRY(copy_from_user(&rect, user_rect));
|
||||||
|
TRY(verify_head_index(rect.head_index));
|
||||||
|
|
||||||
|
TRY(flush_head_buffer(rect.head_index));
|
||||||
|
return KSuccess;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return EINVAL;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericFramebufferDevice::GenericFramebufferDevice(const GenericGraphicsAdapter& adapter)
|
||||||
|
: BlockDevice(29, GraphicsManagement::the().allocate_minor_device_number())
|
||||||
|
, m_graphics_adapter(adapter)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
71
Kernel/Graphics/GenericFramebufferDevice.h
Normal file
71
Kernel/Graphics/GenericFramebufferDevice.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/String.h>
|
||||||
|
#include <AK/Types.h>
|
||||||
|
#include <Kernel/API/KResult.h>
|
||||||
|
#include <Kernel/Devices/BlockDevice.h>
|
||||||
|
#include <Kernel/Graphics/GenericGraphicsAdapter.h>
|
||||||
|
#include <Kernel/Locking/Mutex.h>
|
||||||
|
#include <LibC/sys/ioctl_numbers.h>
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
class GenericFramebufferDevice : public BlockDevice {
|
||||||
|
AK_MAKE_ETERNAL
|
||||||
|
friend class DeviceManagement;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual KResult try_to_initialize() = 0;
|
||||||
|
|
||||||
|
virtual void deactivate_writes() = 0;
|
||||||
|
virtual void activate_writes() = 0;
|
||||||
|
|
||||||
|
virtual ~GenericFramebufferDevice() = default;
|
||||||
|
|
||||||
|
// ^File
|
||||||
|
virtual KResultOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) = 0;
|
||||||
|
virtual KResult ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override final;
|
||||||
|
virtual StringView class_name() const override final { return "FramebufferDevice"sv; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// ^File
|
||||||
|
virtual bool can_read(const OpenFileDescription&, size_t) const override final { return true; }
|
||||||
|
virtual bool can_write(const OpenFileDescription&, size_t) const override final { return true; }
|
||||||
|
virtual void start_request(AsyncBlockDeviceRequest& request) override final { request.complete(AsyncDeviceRequest::Failure); }
|
||||||
|
virtual KResultOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override { return EINVAL; }
|
||||||
|
virtual KResultOr<size_t> write(OpenFileDescription&, u64, const UserOrKernelBuffer&, size_t) override { return EINVAL; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool multihead_support() const = 0;
|
||||||
|
virtual bool flushing_support() const = 0;
|
||||||
|
virtual bool partial_flushing_support() const = 0;
|
||||||
|
virtual size_t heads_count() const = 0;
|
||||||
|
virtual KResultOr<size_t> buffer_length(size_t head) const = 0;
|
||||||
|
virtual KResultOr<size_t> pitch(size_t head) const = 0;
|
||||||
|
virtual KResultOr<size_t> height(size_t head) const = 0;
|
||||||
|
virtual KResultOr<size_t> width(size_t head) const = 0;
|
||||||
|
virtual KResultOr<size_t> vertical_offset(size_t head) const = 0;
|
||||||
|
virtual KResultOr<bool> vertical_offseted(size_t head) const = 0;
|
||||||
|
|
||||||
|
virtual KResult set_head_resolution(size_t head, size_t width, size_t height, size_t pitch) = 0;
|
||||||
|
virtual KResult set_head_buffer(size_t head, bool second_buffer) = 0;
|
||||||
|
virtual KResult flush_head_buffer(size_t head) = 0;
|
||||||
|
// FIXME: This method is too much specific to the VirtIO implementation (especially the buffer_index parameter)
|
||||||
|
virtual KResult flush_rectangle(size_t buffer_index, FBRect const&) = 0;
|
||||||
|
|
||||||
|
KResult verify_head_index(int head_index) const;
|
||||||
|
|
||||||
|
GenericFramebufferDevice(const GenericGraphicsAdapter&);
|
||||||
|
mutable WeakPtr<GenericGraphicsAdapter> m_graphics_adapter;
|
||||||
|
mutable Mutex m_heads_lock;
|
||||||
|
mutable Mutex m_flushing_lock;
|
||||||
|
mutable Mutex m_resolution_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -8,12 +8,15 @@
|
||||||
|
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
#include <AK/Weakable.h>
|
||||||
#include <Kernel/Bus/PCI/Definitions.h>
|
#include <Kernel/Bus/PCI/Definitions.h>
|
||||||
#include <Kernel/Devices/BlockDevice.h>
|
#include <Kernel/Devices/BlockDevice.h>
|
||||||
#include <Kernel/PhysicalAddress.h>
|
#include <Kernel/PhysicalAddress.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class GenericGraphicsAdapter : public RefCounted<GenericGraphicsAdapter> {
|
class GenericGraphicsAdapter
|
||||||
|
: public RefCounted<GenericGraphicsAdapter>
|
||||||
|
, public Weakable<GenericGraphicsAdapter> {
|
||||||
public:
|
public:
|
||||||
virtual ~GenericGraphicsAdapter() = default;
|
virtual ~GenericGraphicsAdapter() = default;
|
||||||
virtual void initialize_framebuffer_devices() = 0;
|
virtual void initialize_framebuffer_devices() = 0;
|
||||||
|
|
|
@ -642,8 +642,9 @@ void IntelNativeGraphicsAdapter::initialize_framebuffer_devices()
|
||||||
VERIFY(m_framebuffer_pitch != 0);
|
VERIFY(m_framebuffer_pitch != 0);
|
||||||
VERIFY(m_framebuffer_height != 0);
|
VERIFY(m_framebuffer_height != 0);
|
||||||
VERIFY(m_framebuffer_width != 0);
|
VERIFY(m_framebuffer_width != 0);
|
||||||
m_framebuffer_device = FramebufferDevice::create(*this, 0, address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch);
|
m_framebuffer_device = FramebufferDevice::create(*this, address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch);
|
||||||
// FIXME: Would be nice to be able to return a KResult here.
|
// FIXME: Would be nice to be able to return a KResult here.
|
||||||
VERIFY(!m_framebuffer_device->initialize().is_error());
|
auto framebuffer_result = m_framebuffer_device->try_to_initialize();
|
||||||
|
VERIFY(!framebuffer_result.is_error());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,9 @@ UNMAP_AFTER_INIT void VGACompatibleAdapter::initialize_framebuffer_devices()
|
||||||
VERIFY(m_framebuffer_width != 0);
|
VERIFY(m_framebuffer_width != 0);
|
||||||
VERIFY(m_framebuffer_height != 0);
|
VERIFY(m_framebuffer_height != 0);
|
||||||
VERIFY(m_framebuffer_pitch != 0);
|
VERIFY(m_framebuffer_pitch != 0);
|
||||||
m_framebuffer_device = FramebufferDevice::create(*this, 0, m_framebuffer_address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch);
|
m_framebuffer_device = FramebufferDevice::create(*this, m_framebuffer_address, m_framebuffer_width, m_framebuffer_height, m_framebuffer_pitch);
|
||||||
// FIXME: Would be nice to be able to return KResult here.
|
// FIXME: Would be nice to be able to return KResult here.
|
||||||
VERIFY(!m_framebuffer_device->initialize().is_error());
|
VERIFY(!m_framebuffer_device->try_to_initialize().is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
UNMAP_AFTER_INIT VGACompatibleAdapter::VGACompatibleAdapter(PCI::Address address)
|
UNMAP_AFTER_INIT VGACompatibleAdapter::VGACompatibleAdapter(PCI::Address address)
|
||||||
|
|
|
@ -42,9 +42,9 @@ Console::Console(RefPtr<FramebufferDevice> const& framebuffer_device)
|
||||||
enqueue_refresh_timer();
|
enqueue_refresh_timer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Console::set_resolution(size_t width, size_t height, size_t)
|
void Console::set_resolution(size_t width, size_t height, size_t pitch)
|
||||||
{
|
{
|
||||||
auto did_set_resolution = m_framebuffer_device->try_to_set_resolution(width, height);
|
auto did_set_resolution = m_framebuffer_device->set_head_resolution(0, width, height, pitch);
|
||||||
VERIFY(!did_set_resolution.is_error());
|
VERIFY(!did_set_resolution.is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,18 +11,137 @@
|
||||||
|
|
||||||
namespace Kernel::Graphics::VirtIOGPU {
|
namespace Kernel::Graphics::VirtIOGPU {
|
||||||
|
|
||||||
GraphicsAdapter const& FramebufferDevice::adapter() const
|
RefPtr<GraphicsAdapter> FramebufferDevice::adapter() const
|
||||||
{
|
{
|
||||||
return static_cast<GraphicsAdapter const&>(*m_graphics_adapter);
|
auto adapter = m_graphics_adapter.strong_ref();
|
||||||
|
// FIXME: Propagate error gracefully
|
||||||
|
VERIFY(adapter);
|
||||||
|
return static_cast<GraphicsAdapter&>(*adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphicsAdapter& FramebufferDevice::adapter()
|
KResultOr<size_t> FramebufferDevice::buffer_length(size_t head) const
|
||||||
{
|
{
|
||||||
return static_cast<GraphicsAdapter&>(*m_graphics_adapter);
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
|
VERIFY(head == 0);
|
||||||
|
MutexLocker locker(m_resolution_lock);
|
||||||
|
return display_info().rect.width * display_info().rect.height * 4;
|
||||||
|
}
|
||||||
|
KResultOr<size_t> FramebufferDevice::pitch(size_t head) const
|
||||||
|
{
|
||||||
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
|
VERIFY(head == 0);
|
||||||
|
MutexLocker locker(m_resolution_lock);
|
||||||
|
return display_info().rect.width * 4;
|
||||||
|
}
|
||||||
|
KResultOr<size_t> FramebufferDevice::height(size_t head) const
|
||||||
|
{
|
||||||
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
|
VERIFY(head == 0);
|
||||||
|
MutexLocker locker(m_resolution_lock);
|
||||||
|
return display_info().rect.height;
|
||||||
|
}
|
||||||
|
KResultOr<size_t> FramebufferDevice::width(size_t head) const
|
||||||
|
{
|
||||||
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
|
VERIFY(head == 0);
|
||||||
|
MutexLocker locker(m_resolution_lock);
|
||||||
|
return display_info().rect.width;
|
||||||
|
}
|
||||||
|
KResultOr<size_t> FramebufferDevice::vertical_offset(size_t head) const
|
||||||
|
{
|
||||||
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
|
VERIFY(head == 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
KResultOr<bool> FramebufferDevice::vertical_offseted(size_t head) const
|
||||||
|
{
|
||||||
|
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||||
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
|
VERIFY(head == 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
KResult FramebufferDevice::set_head_resolution(size_t head, size_t width, size_t height, size_t)
|
||||||
|
{
|
||||||
|
// Note: This class doesn't support multihead setup (yet!).
|
||||||
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
|
// so if we happen to accidentally have a value different than 0, assert.
|
||||||
|
VERIFY(head == 0);
|
||||||
|
if (width > MAX_VIRTIOGPU_RESOLUTION_WIDTH || height > MAX_VIRTIOGPU_RESOLUTION_HEIGHT)
|
||||||
|
return KResult(ENOTSUP);
|
||||||
|
|
||||||
|
auto& info = display_info();
|
||||||
|
|
||||||
|
MutexLocker locker(adapter()->operation_lock());
|
||||||
|
|
||||||
|
info.rect = {
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.width = (u32)width,
|
||||||
|
.height = (u32)height,
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: Would be nice to be able to return KResultOr here.
|
||||||
|
TRY(create_framebuffer());
|
||||||
|
return KSuccess;
|
||||||
|
}
|
||||||
|
KResult FramebufferDevice::set_head_buffer(size_t, bool)
|
||||||
|
{
|
||||||
|
return KResult(ENOTSUP);
|
||||||
|
}
|
||||||
|
KResult FramebufferDevice::flush_head_buffer(size_t)
|
||||||
|
{
|
||||||
|
// Note: This class doesn't support flushing.
|
||||||
|
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||||
|
// so if we happen to accidentally reach this code, assert.
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
KResult FramebufferDevice::flush_rectangle(size_t buffer_index, FBRect const& rect)
|
||||||
|
{
|
||||||
|
MutexLocker locker(adapter()->operation_lock());
|
||||||
|
Protocol::Rect dirty_rect {
|
||||||
|
.x = rect.x,
|
||||||
|
.y = rect.y,
|
||||||
|
.width = rect.width,
|
||||||
|
.height = rect.height
|
||||||
|
};
|
||||||
|
// FIXME: Find a better KResult here.
|
||||||
|
if (!m_are_writes_active)
|
||||||
|
return KResult(EIO);
|
||||||
|
auto& buffer = buffer_from_index(buffer_index);
|
||||||
|
transfer_framebuffer_data_to_host(dirty_rect, buffer);
|
||||||
|
if (&buffer == m_current_buffer) {
|
||||||
|
// Flushing directly to screen
|
||||||
|
flush_displayed_image(dirty_rect, buffer);
|
||||||
|
buffer.dirty_rect = {};
|
||||||
|
} else {
|
||||||
|
if (buffer.dirty_rect.width == 0 || buffer.dirty_rect.height == 0) {
|
||||||
|
buffer.dirty_rect = dirty_rect;
|
||||||
|
} else {
|
||||||
|
auto current_dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width;
|
||||||
|
auto current_dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height;
|
||||||
|
buffer.dirty_rect.x = min(buffer.dirty_rect.x, dirty_rect.x);
|
||||||
|
buffer.dirty_rect.y = min(buffer.dirty_rect.y, dirty_rect.y);
|
||||||
|
buffer.dirty_rect.width = max(current_dirty_right, dirty_rect.x + dirty_rect.width) - buffer.dirty_rect.x;
|
||||||
|
buffer.dirty_rect.height = max(current_dirty_bottom, dirty_rect.y + dirty_rect.height) - buffer.dirty_rect.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
FramebufferDevice::FramebufferDevice(GraphicsAdapter const& adapter, ScanoutID scanout)
|
FramebufferDevice::FramebufferDevice(GraphicsAdapter const& adapter, ScanoutID scanout)
|
||||||
: Kernel::FramebufferDevice(adapter, scanout.value())
|
: GenericFramebufferDevice(adapter)
|
||||||
, m_scanout(scanout)
|
, m_scanout(scanout)
|
||||||
{
|
{
|
||||||
if (display_info().enabled) {
|
if (display_info().enabled) {
|
||||||
|
@ -55,7 +174,7 @@ KResult FramebufferDevice::create_framebuffer()
|
||||||
}
|
}
|
||||||
m_framebuffer_sink_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_physical_pages(pages.span()));
|
m_framebuffer_sink_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_physical_pages(pages.span()));
|
||||||
|
|
||||||
MutexLocker locker(adapter().operation_lock());
|
MutexLocker locker(adapter()->operation_lock());
|
||||||
m_current_buffer = &buffer_from_index(m_last_set_buffer_index.load());
|
m_current_buffer = &buffer_from_index(m_last_set_buffer_index.load());
|
||||||
create_buffer(m_main_buffer, 0, m_buffer_size);
|
create_buffer(m_main_buffer, 0, m_buffer_size);
|
||||||
create_buffer(m_back_buffer, m_buffer_size, m_buffer_size);
|
create_buffer(m_back_buffer, m_buffer_size, m_buffer_size);
|
||||||
|
@ -72,14 +191,14 @@ void FramebufferDevice::create_buffer(Buffer& buffer, size_t framebuffer_offset,
|
||||||
|
|
||||||
// 1. Create BUFFER using VIRTIO_GPU_CMD_RESOURCE_CREATE_2D
|
// 1. Create BUFFER using VIRTIO_GPU_CMD_RESOURCE_CREATE_2D
|
||||||
if (buffer.resource_id.value() != 0)
|
if (buffer.resource_id.value() != 0)
|
||||||
adapter().delete_resource(buffer.resource_id);
|
adapter()->delete_resource(buffer.resource_id);
|
||||||
buffer.resource_id = adapter().create_2d_resource(info.rect);
|
buffer.resource_id = adapter()->create_2d_resource(info.rect);
|
||||||
|
|
||||||
// 2. Attach backing storage using VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING
|
// 2. Attach backing storage using VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING
|
||||||
adapter().ensure_backing_storage(buffer.resource_id, *m_framebuffer, buffer.framebuffer_offset, framebuffer_size);
|
adapter()->ensure_backing_storage(buffer.resource_id, *m_framebuffer, buffer.framebuffer_offset, framebuffer_size);
|
||||||
// 3. Use VIRTIO_GPU_CMD_SET_SCANOUT to link the framebuffer to a display scanout.
|
// 3. Use VIRTIO_GPU_CMD_SET_SCANOUT to link the framebuffer to a display scanout.
|
||||||
if (&buffer == m_current_buffer)
|
if (&buffer == m_current_buffer)
|
||||||
adapter().set_scanout_resource(m_scanout.value(), buffer.resource_id, info.rect);
|
adapter()->set_scanout_resource(m_scanout.value(), buffer.resource_id, info.rect);
|
||||||
// 4. Render our test pattern
|
// 4. Render our test pattern
|
||||||
draw_ntsc_test_pattern(buffer);
|
draw_ntsc_test_pattern(buffer);
|
||||||
// 5. Use VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D to update the host resource from guest memory.
|
// 5. Use VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D to update the host resource from guest memory.
|
||||||
|
@ -101,148 +220,41 @@ void FramebufferDevice::create_buffer(Buffer& buffer, size_t framebuffer_offset,
|
||||||
|
|
||||||
Protocol::DisplayInfoResponse::Display const& FramebufferDevice::display_info() const
|
Protocol::DisplayInfoResponse::Display const& FramebufferDevice::display_info() const
|
||||||
{
|
{
|
||||||
return adapter().display_info(m_scanout);
|
return adapter()->display_info(m_scanout);
|
||||||
}
|
}
|
||||||
|
|
||||||
Protocol::DisplayInfoResponse::Display& FramebufferDevice::display_info()
|
Protocol::DisplayInfoResponse::Display& FramebufferDevice::display_info()
|
||||||
{
|
{
|
||||||
return adapter().display_info(m_scanout);
|
return adapter()->display_info(m_scanout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramebufferDevice::transfer_framebuffer_data_to_host(Protocol::Rect const& rect, Buffer& buffer)
|
void FramebufferDevice::transfer_framebuffer_data_to_host(Protocol::Rect const& rect, Buffer& buffer)
|
||||||
{
|
{
|
||||||
adapter().transfer_framebuffer_data_to_host(m_scanout, buffer.resource_id, rect);
|
adapter()->transfer_framebuffer_data_to_host(m_scanout, buffer.resource_id, rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramebufferDevice::flush_dirty_window(Protocol::Rect const& dirty_rect, Buffer& buffer)
|
void FramebufferDevice::flush_dirty_window(Protocol::Rect const& dirty_rect, Buffer& buffer)
|
||||||
{
|
{
|
||||||
adapter().flush_dirty_rectangle(m_scanout, buffer.resource_id, dirty_rect);
|
adapter()->flush_dirty_rectangle(m_scanout, buffer.resource_id, dirty_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramebufferDevice::flush_displayed_image(Protocol::Rect const& dirty_rect, Buffer& buffer)
|
void FramebufferDevice::flush_displayed_image(Protocol::Rect const& dirty_rect, Buffer& buffer)
|
||||||
{
|
{
|
||||||
adapter().flush_displayed_image(buffer.resource_id, dirty_rect);
|
adapter()->flush_displayed_image(buffer.resource_id, dirty_rect);
|
||||||
}
|
|
||||||
|
|
||||||
KResult FramebufferDevice::try_to_set_resolution(size_t width, size_t height)
|
|
||||||
{
|
|
||||||
if (width > MAX_VIRTIOGPU_RESOLUTION_WIDTH || height > MAX_VIRTIOGPU_RESOLUTION_HEIGHT)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
auto& info = display_info();
|
|
||||||
|
|
||||||
MutexLocker locker(adapter().operation_lock());
|
|
||||||
|
|
||||||
info.rect = {
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
.width = (u32)width,
|
|
||||||
.height = (u32)height,
|
|
||||||
};
|
|
||||||
|
|
||||||
return create_framebuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramebufferDevice::set_buffer(int buffer_index)
|
void FramebufferDevice::set_buffer(int buffer_index)
|
||||||
{
|
{
|
||||||
auto& buffer = buffer_index == 0 ? m_main_buffer : m_back_buffer;
|
auto& buffer = buffer_index == 0 ? m_main_buffer : m_back_buffer;
|
||||||
MutexLocker locker(adapter().operation_lock());
|
MutexLocker locker(adapter()->operation_lock());
|
||||||
if (&buffer == m_current_buffer)
|
if (&buffer == m_current_buffer)
|
||||||
return;
|
return;
|
||||||
m_current_buffer = &buffer;
|
m_current_buffer = &buffer;
|
||||||
adapter().set_scanout_resource(m_scanout.value(), buffer.resource_id, display_info().rect);
|
adapter()->set_scanout_resource(m_scanout.value(), buffer.resource_id, display_info().rect);
|
||||||
adapter().flush_displayed_image(buffer.resource_id, buffer.dirty_rect); // QEMU SDL backend requires this (as per spec)
|
adapter()->flush_displayed_image(buffer.resource_id, buffer.dirty_rect); // QEMU SDL backend requires this (as per spec)
|
||||||
buffer.dirty_rect = {};
|
buffer.dirty_rect = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
KResult FramebufferDevice::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
|
|
||||||
{
|
|
||||||
REQUIRE_PROMISE(video);
|
|
||||||
switch (request) {
|
|
||||||
case FB_IOCTL_GET_SIZE_IN_BYTES: {
|
|
||||||
auto out = static_ptr_cast<size_t*>(arg);
|
|
||||||
size_t value = m_buffer_size * 2;
|
|
||||||
return copy_to_user(out, &value);
|
|
||||||
}
|
|
||||||
case FB_IOCTL_SET_RESOLUTION: {
|
|
||||||
auto user_resolution = static_ptr_cast<FBResolution*>(arg);
|
|
||||||
FBResolution resolution;
|
|
||||||
TRY(copy_from_user(&resolution, user_resolution));
|
|
||||||
TRY(try_to_set_resolution(resolution.width, resolution.height));
|
|
||||||
resolution.pitch = pitch();
|
|
||||||
return copy_to_user(user_resolution, &resolution);
|
|
||||||
}
|
|
||||||
case FB_IOCTL_GET_RESOLUTION: {
|
|
||||||
auto user_resolution = static_ptr_cast<FBResolution*>(arg);
|
|
||||||
FBResolution resolution {};
|
|
||||||
resolution.pitch = pitch();
|
|
||||||
resolution.width = width();
|
|
||||||
resolution.height = height();
|
|
||||||
return copy_to_user(user_resolution, &resolution);
|
|
||||||
}
|
|
||||||
case FB_IOCTL_SET_BUFFER: {
|
|
||||||
auto buffer_index = static_cast<int>(arg.ptr());
|
|
||||||
if (!is_valid_buffer_index(buffer_index))
|
|
||||||
return EINVAL;
|
|
||||||
if (m_last_set_buffer_index.exchange(buffer_index) != buffer_index && m_are_writes_active)
|
|
||||||
set_buffer(buffer_index);
|
|
||||||
return KSuccess;
|
|
||||||
}
|
|
||||||
case FB_IOCTL_FLUSH_BUFFERS: {
|
|
||||||
auto user_flush_rects = static_ptr_cast<FBFlushRects*>(arg);
|
|
||||||
FBFlushRects flush_rects;
|
|
||||||
TRY(copy_from_user(&flush_rects, user_flush_rects));
|
|
||||||
if (!is_valid_buffer_index(flush_rects.buffer_index))
|
|
||||||
return EINVAL;
|
|
||||||
if (Checked<unsigned>::multiplication_would_overflow(flush_rects.count, sizeof(FBRect)))
|
|
||||||
return EFAULT;
|
|
||||||
if (m_are_writes_active && flush_rects.count > 0) {
|
|
||||||
auto& buffer = buffer_from_index(flush_rects.buffer_index);
|
|
||||||
MutexLocker locker(adapter().operation_lock());
|
|
||||||
for (unsigned i = 0; i < flush_rects.count; i++) {
|
|
||||||
FBRect user_dirty_rect;
|
|
||||||
TRY(copy_from_user(&user_dirty_rect, &flush_rects.rects[i]));
|
|
||||||
Protocol::Rect dirty_rect {
|
|
||||||
.x = user_dirty_rect.x,
|
|
||||||
.y = user_dirty_rect.y,
|
|
||||||
.width = user_dirty_rect.width,
|
|
||||||
.height = user_dirty_rect.height
|
|
||||||
};
|
|
||||||
transfer_framebuffer_data_to_host(dirty_rect, buffer);
|
|
||||||
if (&buffer == m_current_buffer) {
|
|
||||||
// Flushing directly to screen
|
|
||||||
flush_displayed_image(dirty_rect, buffer);
|
|
||||||
buffer.dirty_rect = {};
|
|
||||||
} else {
|
|
||||||
if (buffer.dirty_rect.width == 0 || buffer.dirty_rect.height == 0) {
|
|
||||||
buffer.dirty_rect = dirty_rect;
|
|
||||||
} else {
|
|
||||||
auto current_dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width;
|
|
||||||
auto current_dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height;
|
|
||||||
buffer.dirty_rect.x = min(buffer.dirty_rect.x, dirty_rect.x);
|
|
||||||
buffer.dirty_rect.y = min(buffer.dirty_rect.y, dirty_rect.y);
|
|
||||||
buffer.dirty_rect.width = max(current_dirty_right, dirty_rect.x + dirty_rect.width) - buffer.dirty_rect.x;
|
|
||||||
buffer.dirty_rect.height = max(current_dirty_bottom, dirty_rect.y + dirty_rect.height) - buffer.dirty_rect.y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return KSuccess;
|
|
||||||
}
|
|
||||||
case FB_IOCTL_GET_BUFFER_OFFSET: {
|
|
||||||
auto user_buffer_offset = static_ptr_cast<FBBufferOffset*>(arg);
|
|
||||||
FBBufferOffset buffer_offset;
|
|
||||||
TRY(copy_from_user(&buffer_offset, user_buffer_offset));
|
|
||||||
if (!is_valid_buffer_index(buffer_offset.buffer_index))
|
|
||||||
return EINVAL;
|
|
||||||
buffer_offset.offset = (size_t)buffer_offset.buffer_index * m_buffer_size;
|
|
||||||
return copy_to_user(user_buffer_offset, &buffer_offset);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return EINVAL;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
KResultOr<Memory::Region*> FramebufferDevice::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared)
|
KResultOr<Memory::Region*> FramebufferDevice::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared)
|
||||||
{
|
{
|
||||||
REQUIRE_PROMISE(video);
|
REQUIRE_PROMISE(video);
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
namespace Kernel::Graphics::VirtIOGPU {
|
namespace Kernel::Graphics::VirtIOGPU {
|
||||||
|
|
||||||
class GraphicsAdapter;
|
class GraphicsAdapter;
|
||||||
class FramebufferDevice final : public Kernel::FramebufferDevice {
|
class FramebufferDevice final : public GenericFramebufferDevice {
|
||||||
friend class Console;
|
friend class Console;
|
||||||
struct Buffer {
|
struct Buffer {
|
||||||
size_t framebuffer_offset { 0 };
|
size_t framebuffer_offset { 0 };
|
||||||
|
@ -28,12 +28,11 @@ public:
|
||||||
FramebufferDevice(GraphicsAdapter const&, ScanoutID);
|
FramebufferDevice(GraphicsAdapter const&, ScanoutID);
|
||||||
virtual ~FramebufferDevice() override;
|
virtual ~FramebufferDevice() override;
|
||||||
|
|
||||||
|
virtual KResult try_to_initialize() override { return KSuccess; }
|
||||||
|
|
||||||
virtual void deactivate_writes();
|
virtual void deactivate_writes();
|
||||||
virtual void activate_writes();
|
virtual void activate_writes();
|
||||||
|
|
||||||
KResult try_to_set_resolution(size_t width, size_t height);
|
|
||||||
void clear_to_black(Buffer&);
|
|
||||||
|
|
||||||
size_t width() const { return display_info().rect.width; }
|
size_t width() const { return display_info().rect.width; }
|
||||||
size_t height() const { return display_info().rect.height; }
|
size_t height() const { return display_info().rect.height; }
|
||||||
size_t pitch() const { return display_info().rect.width * 4; }
|
size_t pitch() const { return display_info().rect.width * 4; }
|
||||||
|
@ -44,25 +43,40 @@ public:
|
||||||
return Memory::page_round_up(sizeof(u32) * width * height);
|
return Memory::page_round_up(sizeof(u32) * width * height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u8* framebuffer_data();
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual bool multihead_support() const override { return false; }
|
||||||
|
virtual bool flushing_support() const override { return false; }
|
||||||
|
virtual bool partial_flushing_support() const override { return true; }
|
||||||
|
virtual size_t heads_count() const override { return 1; }
|
||||||
|
virtual KResultOr<size_t> buffer_length(size_t head) const override;
|
||||||
|
virtual KResultOr<size_t> pitch(size_t head) const override;
|
||||||
|
virtual KResultOr<size_t> height(size_t head) const override;
|
||||||
|
virtual KResultOr<size_t> width(size_t head) const override;
|
||||||
|
virtual KResultOr<size_t> vertical_offset(size_t head) const override;
|
||||||
|
virtual KResultOr<bool> vertical_offseted(size_t head) const override;
|
||||||
|
|
||||||
|
virtual KResult set_head_resolution(size_t head, size_t width, size_t height, size_t pitch) override;
|
||||||
|
virtual KResult set_head_buffer(size_t head, bool second_buffer) override;
|
||||||
|
virtual KResult flush_head_buffer(size_t head) override;
|
||||||
|
virtual KResult flush_rectangle(size_t head, FBRect const&) override;
|
||||||
|
|
||||||
void flush_dirty_window(Protocol::Rect const&, Buffer&);
|
void flush_dirty_window(Protocol::Rect const&, Buffer&);
|
||||||
void transfer_framebuffer_data_to_host(Protocol::Rect const&, Buffer&);
|
void transfer_framebuffer_data_to_host(Protocol::Rect const&, Buffer&);
|
||||||
void flush_displayed_image(Protocol::Rect const&, Buffer&);
|
void flush_displayed_image(Protocol::Rect const&, Buffer&);
|
||||||
|
|
||||||
void draw_ntsc_test_pattern(Buffer&);
|
void draw_ntsc_test_pattern(Buffer&);
|
||||||
|
|
||||||
u8* framebuffer_data();
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual StringView class_name() const override { return "VirtIOFrameBuffer"sv; }
|
|
||||||
|
|
||||||
Protocol::DisplayInfoResponse::Display const& display_info() const;
|
Protocol::DisplayInfoResponse::Display const& display_info() const;
|
||||||
Protocol::DisplayInfoResponse::Display& display_info();
|
Protocol::DisplayInfoResponse::Display& display_info();
|
||||||
|
|
||||||
|
void clear_to_black(Buffer&);
|
||||||
|
|
||||||
KResult create_framebuffer();
|
KResult create_framebuffer();
|
||||||
void create_buffer(Buffer&, size_t, size_t);
|
void create_buffer(Buffer&, size_t, size_t);
|
||||||
void set_buffer(int);
|
void set_buffer(int);
|
||||||
|
|
||||||
virtual KResult ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override;
|
|
||||||
virtual KResultOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override;
|
virtual KResultOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override;
|
||||||
|
|
||||||
static bool is_valid_buffer_index(int buffer_index)
|
static bool is_valid_buffer_index(int buffer_index)
|
||||||
|
@ -76,8 +90,7 @@ private:
|
||||||
}
|
}
|
||||||
Buffer& current_buffer() const { return *m_current_buffer; }
|
Buffer& current_buffer() const { return *m_current_buffer; }
|
||||||
|
|
||||||
GraphicsAdapter const& adapter() const;
|
RefPtr<GraphicsAdapter> adapter() const;
|
||||||
GraphicsAdapter& adapter();
|
|
||||||
|
|
||||||
const ScanoutID m_scanout;
|
const ScanoutID m_scanout;
|
||||||
Buffer* m_current_buffer { nullptr };
|
Buffer* m_current_buffer { nullptr };
|
||||||
|
|
|
@ -25,18 +25,20 @@ int main()
|
||||||
size_t pitch = width * 4;
|
size_t pitch = width * 4;
|
||||||
size_t framebuffer_size_in_bytes = pitch * height * 2;
|
size_t framebuffer_size_in_bytes = pitch * height * 2;
|
||||||
|
|
||||||
FBResolution original_resolution;
|
FBHeadProperties original_properties;
|
||||||
if (ioctl(fd, FB_IOCTL_GET_RESOLUTION, &original_resolution) < 0) {
|
original_properties.head_index = 0;
|
||||||
|
if (ioctl(fd, FB_IOCTL_GET_HEAD_PROPERTIES, &original_properties) < 0) {
|
||||||
perror("ioctl");
|
perror("ioctl");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FBResolution resolution;
|
FBHeadResolution resolution;
|
||||||
|
resolution.head_index = 0;
|
||||||
resolution.width = width;
|
resolution.width = width;
|
||||||
resolution.height = height;
|
resolution.height = height;
|
||||||
resolution.pitch = pitch;
|
resolution.pitch = pitch;
|
||||||
|
|
||||||
if (ioctl(fd, FB_IOCTL_SET_RESOLUTION, &resolution) < 0) {
|
if (ioctl(fd, FB_IOCTL_SET_HEAD_RESOLUTION, &resolution) < 0) {
|
||||||
perror("ioctl");
|
perror("ioctl");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -87,7 +89,12 @@ int main()
|
||||||
process->uid = 0;
|
process->uid = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ioctl(fd, FB_IOCTL_SET_RESOLUTION, &original_resolution) < 0) {
|
FBHeadResolution original_resolution;
|
||||||
|
original_resolution.head_index = 0;
|
||||||
|
original_resolution.width = original_properties.width;
|
||||||
|
original_resolution.height = original_properties.height;
|
||||||
|
original_resolution.pitch = original_properties.pitch;
|
||||||
|
if (ioctl(fd, FB_IOCTL_SET_HEAD_RESOLUTION, &original_resolution) < 0) {
|
||||||
perror("ioctl");
|
perror("ioctl");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1155,20 +1155,20 @@ int Emulator::virt$ioctl([[maybe_unused]] int fd, unsigned request, [[maybe_unus
|
||||||
if (request == TIOCNOTTY || request == TIOCSCTTY) {
|
if (request == TIOCNOTTY || request == TIOCSCTTY) {
|
||||||
return syscall(SC_ioctl, fd, request, 0);
|
return syscall(SC_ioctl, fd, request, 0);
|
||||||
}
|
}
|
||||||
if (request == FB_IOCTL_GET_SIZE_IN_BYTES) {
|
if (request == FB_IOCTL_GET_PROPERTIES) {
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
auto rc = syscall(SC_ioctl, fd, request, &size);
|
auto rc = syscall(SC_ioctl, fd, request, &size);
|
||||||
mmu().copy_to_vm(arg, &size, sizeof(size));
|
mmu().copy_to_vm(arg, &size, sizeof(size));
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
if (request == FB_IOCTL_SET_RESOLUTION) {
|
if (request == FB_IOCTL_SET_HEAD_RESOLUTION) {
|
||||||
FBResolution user_resolution;
|
FBHeadResolution user_resolution;
|
||||||
mmu().copy_from_vm(&user_resolution, arg, sizeof(user_resolution));
|
mmu().copy_from_vm(&user_resolution, arg, sizeof(user_resolution));
|
||||||
auto rc = syscall(SC_ioctl, fd, request, &user_resolution);
|
auto rc = syscall(SC_ioctl, fd, request, &user_resolution);
|
||||||
mmu().copy_to_vm(arg, &user_resolution, sizeof(user_resolution));
|
mmu().copy_to_vm(arg, &user_resolution, sizeof(user_resolution));
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
if (request == FB_IOCTL_SET_BUFFER) {
|
if (request == FB_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER) {
|
||||||
return syscall(SC_ioctl, fd, request, arg);
|
return syscall(SC_ioctl, fd, request, arg);
|
||||||
}
|
}
|
||||||
reportln("Unsupported ioctl: {}", request);
|
reportln("Unsupported ioctl: {}", request);
|
||||||
|
|
|
@ -18,13 +18,38 @@ struct winsize {
|
||||||
unsigned short ws_ypixel;
|
unsigned short ws_ypixel;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FBResolution {
|
struct FBProperties {
|
||||||
|
bool multihead_support;
|
||||||
|
bool doublebuffer_support;
|
||||||
|
bool flushing_support;
|
||||||
|
bool partial_flushing_support;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FBHeadProperties {
|
||||||
|
int head_index;
|
||||||
|
|
||||||
unsigned pitch;
|
unsigned pitch;
|
||||||
unsigned width;
|
unsigned width;
|
||||||
unsigned height;
|
unsigned height;
|
||||||
|
|
||||||
|
unsigned offset;
|
||||||
|
unsigned buffer_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FBHeadResolution {
|
||||||
|
int head_index;
|
||||||
|
int pitch;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FBHeadVerticalOffset {
|
||||||
|
int head_index;
|
||||||
|
int offseted;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FBRect {
|
struct FBRect {
|
||||||
|
int head_index;
|
||||||
unsigned x;
|
unsigned x;
|
||||||
unsigned y;
|
unsigned y;
|
||||||
unsigned width;
|
unsigned width;
|
||||||
|
@ -57,13 +82,13 @@ enum IOCtlNumber {
|
||||||
TIOCSTI,
|
TIOCSTI,
|
||||||
TIOCNOTTY,
|
TIOCNOTTY,
|
||||||
TIOCSWINSZ,
|
TIOCSWINSZ,
|
||||||
FB_IOCTL_GET_SIZE_IN_BYTES,
|
FB_IOCTL_GET_PROPERTIES,
|
||||||
FB_IOCTL_GET_RESOLUTION,
|
FB_IOCTL_GET_HEAD_PROPERTIES,
|
||||||
FB_IOCTL_SET_RESOLUTION,
|
FB_IOCTL_SET_HEAD_RESOLUTION,
|
||||||
FB_IOCTL_GET_BUFFER,
|
FB_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER,
|
||||||
FB_IOCTL_GET_BUFFER_OFFSET,
|
FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER,
|
||||||
FB_IOCTL_SET_BUFFER,
|
FB_IOCTL_FLUSH_HEAD_BUFFERS,
|
||||||
FB_IOCTL_FLUSH_BUFFERS,
|
FB_IOCTL_FLUSH_HEAD,
|
||||||
KEYBOARD_IOCTL_GET_NUM_LOCK,
|
KEYBOARD_IOCTL_GET_NUM_LOCK,
|
||||||
KEYBOARD_IOCTL_SET_NUM_LOCK,
|
KEYBOARD_IOCTL_SET_NUM_LOCK,
|
||||||
KEYBOARD_IOCTL_GET_CAPS_LOCK,
|
KEYBOARD_IOCTL_GET_CAPS_LOCK,
|
||||||
|
@ -105,13 +130,13 @@ enum IOCtlNumber {
|
||||||
#define TIOCSTI TIOCSTI
|
#define TIOCSTI TIOCSTI
|
||||||
#define TIOCNOTTY TIOCNOTTY
|
#define TIOCNOTTY TIOCNOTTY
|
||||||
#define TIOCSWINSZ TIOCSWINSZ
|
#define TIOCSWINSZ TIOCSWINSZ
|
||||||
#define FB_IOCTL_GET_SIZE_IN_BYTES FB_IOCTL_GET_SIZE_IN_BYTES
|
#define FB_IOCTL_GET_PROPERTIES FB_IOCTL_GET_PROPERTIES
|
||||||
#define FB_IOCTL_GET_RESOLUTION FB_IOCTL_GET_RESOLUTION
|
#define FB_IOCTL_GET_HEAD_PROPERTIES FB_IOCTL_GET_HEAD_PROPERTIES
|
||||||
#define FB_IOCTL_SET_RESOLUTION FB_IOCTL_SET_RESOLUTION
|
#define FB_IOCTL_SET_HEAD_RESOLUTION FB_IOCTL_SET_HEAD_RESOLUTION
|
||||||
#define FB_IOCTL_GET_BUFFER FB_IOCTL_GET_BUFFER
|
#define FB_IOCTL_SET_HEAD_VERITCAL_OFFSET_BUFFER FB_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER
|
||||||
#define FB_IOCTL_GET_BUFFER_OFFSET FB_IOCTL_GET_BUFFER_OFFSET
|
#define FB_IOCTL_GET_HEAD_VERITCAL_OFFSET_BUFFER FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER
|
||||||
#define FB_IOCTL_SET_BUFFER FB_IOCTL_SET_BUFFER
|
#define FB_IOCTL_FLUSH_HEAD_BUFFERS FB_IOCTL_FLUSH_HEAD_BUFFERS
|
||||||
#define FB_IOCTL_FLUSH_BUFFERS FB_IOCTL_FLUSH_BUFFERS
|
#define FB_IOCTL_FLUSH_HEAD FB_IOCTL_FLUSH_HEAD
|
||||||
#define KEYBOARD_IOCTL_GET_NUM_LOCK KEYBOARD_IOCTL_GET_NUM_LOCK
|
#define KEYBOARD_IOCTL_GET_NUM_LOCK KEYBOARD_IOCTL_GET_NUM_LOCK
|
||||||
#define KEYBOARD_IOCTL_SET_NUM_LOCK KEYBOARD_IOCTL_SET_NUM_LOCK
|
#define KEYBOARD_IOCTL_SET_NUM_LOCK KEYBOARD_IOCTL_SET_NUM_LOCK
|
||||||
#define KEYBOARD_IOCTL_GET_CAPS_LOCK KEYBOARD_IOCTL_GET_CAPS_LOCK
|
#define KEYBOARD_IOCTL_GET_CAPS_LOCK KEYBOARD_IOCTL_GET_CAPS_LOCK
|
||||||
|
|
|
@ -230,8 +230,15 @@ bool Screen::open_device()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_can_set_buffer = (fb_set_buffer(m_framebuffer_fd, 0) == 0);
|
FBProperties properties;
|
||||||
m_can_device_flush_buffers = true; // If the device can't do it we revert to false
|
if (fb_get_properties(m_framebuffer_fd, &properties) < 0) {
|
||||||
|
perror(String::formatted("failed to ioctl {}", info.device).characters());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_can_device_flush_buffers = properties.partial_flushing_support;
|
||||||
|
m_can_set_buffer = properties.doublebuffer_support;
|
||||||
|
|
||||||
set_resolution(true);
|
set_resolution(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -312,40 +319,49 @@ bool Screen::set_resolution(bool initial)
|
||||||
screen_with_cursor = &ScreenInput::the().cursor_location_screen();
|
screen_with_cursor = &ScreenInput::the().cursor_location_screen();
|
||||||
|
|
||||||
auto& info = screen_layout_info();
|
auto& info = screen_layout_info();
|
||||||
FBResolution physical_resolution { 0, (unsigned)info.resolution.width(), (unsigned)info.resolution.height() };
|
|
||||||
int rc = fb_set_resolution(m_framebuffer_fd, &physical_resolution);
|
int rc = -1;
|
||||||
|
{
|
||||||
|
// FIXME: Add multihead support for one framebuffer
|
||||||
|
FBHeadResolution physical_resolution { 0, 0, info.resolution.width(), info.resolution.height() };
|
||||||
|
rc = fb_set_resolution(m_framebuffer_fd, &physical_resolution);
|
||||||
|
}
|
||||||
|
|
||||||
dbgln_if(WSSCREEN_DEBUG, "Screen #{}: fb_set_resolution() - return code {}", index(), rc);
|
dbgln_if(WSSCREEN_DEBUG, "Screen #{}: fb_set_resolution() - return code {}", index(), rc);
|
||||||
|
|
||||||
auto on_change_resolution = [&]() {
|
auto on_change_resolution = [&]() {
|
||||||
if (initial || physical_resolution.width != (unsigned)info.resolution.width() || physical_resolution.height != (unsigned)info.resolution.height()) {
|
if (initial) {
|
||||||
if (m_framebuffer) {
|
if (m_framebuffer) {
|
||||||
size_t previous_size_in_bytes = m_size_in_bytes;
|
size_t previous_size_in_bytes = m_size_in_bytes;
|
||||||
int rc = munmap(m_framebuffer, previous_size_in_bytes);
|
int rc = munmap(m_framebuffer, previous_size_in_bytes);
|
||||||
VERIFY(rc == 0);
|
VERIFY(rc == 0);
|
||||||
}
|
}
|
||||||
|
FBHeadProperties properties;
|
||||||
int rc = fb_get_size_in_bytes(m_framebuffer_fd, &m_size_in_bytes);
|
properties.head_index = 0;
|
||||||
|
int rc = fb_get_head_properties(m_framebuffer_fd, &properties);
|
||||||
VERIFY(rc == 0);
|
VERIFY(rc == 0);
|
||||||
|
m_size_in_bytes = properties.buffer_length;
|
||||||
|
|
||||||
m_framebuffer = (Gfx::RGBA32*)mmap(nullptr, m_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, m_framebuffer_fd, 0);
|
m_framebuffer = (Gfx::RGBA32*)mmap(nullptr, m_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, m_framebuffer_fd, 0);
|
||||||
VERIFY(m_framebuffer && m_framebuffer != (void*)-1);
|
VERIFY(m_framebuffer && m_framebuffer != (void*)-1);
|
||||||
|
|
||||||
if (m_can_set_buffer) {
|
if (m_can_set_buffer) {
|
||||||
unsigned buffer_offset = 0;
|
// Note: fall back to assuming the second buffer starts right after the last line of the first
|
||||||
rc = fb_get_buffer_offset(m_framebuffer_fd, 1, &buffer_offset);
|
// Note: for now, this calculation works quite well, so need to defer it to another function
|
||||||
if (rc == 0) {
|
// that does ioctl to figure out the correct offset. If a Framebuffer device ever happens to
|
||||||
m_back_buffer_offset = buffer_offset;
|
// to set the second buffer at different location than this, we might need to consider bringing
|
||||||
} else {
|
// back a function with ioctl to check this.
|
||||||
// fall back to assuming the second buffer starts right after the last line of the first
|
m_back_buffer_offset = properties.pitch * properties.height;
|
||||||
m_back_buffer_offset = physical_resolution.pitch * physical_resolution.height;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
m_back_buffer_offset = 0;
|
m_back_buffer_offset = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
FBHeadProperties properties;
|
||||||
info.resolution = { physical_resolution.width, physical_resolution.height };
|
properties.head_index = 0;
|
||||||
m_pitch = physical_resolution.pitch;
|
int rc = fb_get_head_properties(m_framebuffer_fd, &properties);
|
||||||
|
VERIFY(rc == 0);
|
||||||
|
info.resolution = { properties.width, properties.height };
|
||||||
|
m_pitch = properties.pitch;
|
||||||
|
|
||||||
update_virtual_rect();
|
update_virtual_rect();
|
||||||
|
|
||||||
|
@ -375,7 +391,12 @@ bool Screen::set_resolution(bool initial)
|
||||||
void Screen::set_buffer(int index)
|
void Screen::set_buffer(int index)
|
||||||
{
|
{
|
||||||
VERIFY(m_can_set_buffer);
|
VERIFY(m_can_set_buffer);
|
||||||
int rc = fb_set_buffer(m_framebuffer_fd, index);
|
VERIFY(index <= 1 && index >= 0);
|
||||||
|
FBHeadVerticalOffset offset;
|
||||||
|
memset(&offset, 0, sizeof(FBHeadVerticalOffset));
|
||||||
|
if (index == 1)
|
||||||
|
offset.offseted = 1;
|
||||||
|
int rc = fb_set_head_vertical_offset_buffer(m_framebuffer_fd, &offset);
|
||||||
VERIFY(rc == 0);
|
VERIFY(rc == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,6 +493,7 @@ void Screen::constrain_pending_flush_rects()
|
||||||
fb_data.pending_flush_rects.clear_with_capacity();
|
fb_data.pending_flush_rects.clear_with_capacity();
|
||||||
for (auto const& rect : rects.rects()) {
|
for (auto const& rect : rects.rects()) {
|
||||||
fb_data.pending_flush_rects.append({
|
fb_data.pending_flush_rects.append({
|
||||||
|
.head_index = 0,
|
||||||
.x = (unsigned)rect.x(),
|
.x = (unsigned)rect.x(),
|
||||||
.y = (unsigned)rect.y(),
|
.y = (unsigned)rect.y(),
|
||||||
.width = (unsigned)rect.width(),
|
.width = (unsigned)rect.width(),
|
||||||
|
@ -511,7 +533,8 @@ void Screen::queue_flush_display_rect(Gfx::IntRect const& flush_region)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
VERIFY(fb_data.pending_flush_rects.size() < fb_data.pending_flush_rects.capacity());
|
VERIFY(fb_data.pending_flush_rects.size() < fb_data.pending_flush_rects.capacity());
|
||||||
fb_data.pending_flush_rects.append({ (unsigned)flush_region.left(),
|
fb_data.pending_flush_rects.append({ 0,
|
||||||
|
(unsigned)flush_region.left(),
|
||||||
(unsigned)flush_region.top(),
|
(unsigned)flush_region.top(),
|
||||||
(unsigned)flush_region.width(),
|
(unsigned)flush_region.width(),
|
||||||
(unsigned)flush_region.height() });
|
(unsigned)flush_region.height() });
|
||||||
|
@ -555,6 +578,7 @@ void Screen::flush_display_front_buffer(int front_buffer_index, Gfx::IntRect& re
|
||||||
VERIFY(m_can_device_flush_buffers);
|
VERIFY(m_can_device_flush_buffers);
|
||||||
auto scale_factor = this->scale_factor();
|
auto scale_factor = this->scale_factor();
|
||||||
FBRect flush_rect {
|
FBRect flush_rect {
|
||||||
|
.head_index = 0,
|
||||||
.x = (unsigned)(rect.x() * scale_factor),
|
.x = (unsigned)(rect.x() * scale_factor),
|
||||||
.y = (unsigned)(rect.y() * scale_factor),
|
.y = (unsigned)(rect.y() * scale_factor),
|
||||||
.width = (unsigned)(rect.width() * scale_factor),
|
.width = (unsigned)(rect.width() * scale_factor),
|
||||||
|
|
|
@ -301,7 +301,9 @@ bool ScreenLayout::try_auto_add_framebuffer(String const& device_path)
|
||||||
ScopeGuard fd_guard([&] {
|
ScopeGuard fd_guard([&] {
|
||||||
close(framebuffer_fd);
|
close(framebuffer_fd);
|
||||||
});
|
});
|
||||||
FBResolution resolution {};
|
// FIXME: Add multihead support for one framebuffer
|
||||||
|
FBHeadResolution resolution {};
|
||||||
|
memset(&resolution, 0, sizeof(FBHeadResolution));
|
||||||
if (fb_get_resolution(framebuffer_fd, &resolution) < 0) {
|
if (fb_get_resolution(framebuffer_fd, &resolution) < 0) {
|
||||||
int err = errno;
|
int err = errno;
|
||||||
dbgln("Error ({}) querying resolution from framebuffer device {}", err, device_path);
|
dbgln("Error ({}) querying resolution from framebuffer device {}", err, device_path);
|
||||||
|
|
|
@ -137,13 +137,13 @@ HANDLE(TIOCSCTTY)
|
||||||
HANDLE(TIOCSTI)
|
HANDLE(TIOCSTI)
|
||||||
HANDLE(TIOCNOTTY)
|
HANDLE(TIOCNOTTY)
|
||||||
HANDLE(TIOCSWINSZ)
|
HANDLE(TIOCSWINSZ)
|
||||||
HANDLE(FB_IOCTL_GET_SIZE_IN_BYTES)
|
HANDLE(FB_IOCTL_GET_PROPERTIES)
|
||||||
HANDLE(FB_IOCTL_GET_RESOLUTION)
|
HANDLE(FB_IOCTL_GET_HEAD_PROPERTIES)
|
||||||
HANDLE(FB_IOCTL_SET_RESOLUTION)
|
HANDLE(FB_IOCTL_SET_HEAD_RESOLUTION)
|
||||||
HANDLE(FB_IOCTL_GET_BUFFER)
|
HANDLE(FB_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER)
|
||||||
HANDLE(FB_IOCTL_GET_BUFFER_OFFSET)
|
HANDLE(FB_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER)
|
||||||
HANDLE(FB_IOCTL_SET_BUFFER)
|
HANDLE(FB_IOCTL_FLUSH_HEAD_BUFFERS)
|
||||||
HANDLE(FB_IOCTL_FLUSH_BUFFERS)
|
HANDLE(FB_IOCTL_FLUSH_HEAD)
|
||||||
HANDLE(KEYBOARD_IOCTL_GET_NUM_LOCK)
|
HANDLE(KEYBOARD_IOCTL_GET_NUM_LOCK)
|
||||||
HANDLE(KEYBOARD_IOCTL_SET_NUM_LOCK)
|
HANDLE(KEYBOARD_IOCTL_SET_NUM_LOCK)
|
||||||
HANDLE(KEYBOARD_IOCTL_GET_CAPS_LOCK)
|
HANDLE(KEYBOARD_IOCTL_GET_CAPS_LOCK)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue