Serenity Operating System
at master 174 lines 6.3 kB view raw
1/* 2 * Copyright (c) 2018-2020, the SerenityOS developers. 3 * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include "HardwareScreenBackend.h" 9#include "ScreenBackend.h" 10#include <AK/Try.h> 11#include <Kernel/API/Graphics.h> 12#include <LibCore/System.h> 13#include <fcntl.h> 14#include <stdio.h> 15#include <sys/mman.h> 16#include <unistd.h> 17 18namespace WindowServer { 19 20HardwareScreenBackend::HardwareScreenBackend(DeprecatedString device) 21 : m_device(move(device)) 22{ 23} 24 25ErrorOr<void> HardwareScreenBackend::open() 26{ 27 m_display_connector_fd = TRY(Core::System::open(m_device, O_RDWR | O_CLOEXEC)); 28 29 GraphicsConnectorProperties properties; 30 if (graphics_connector_get_properties(m_display_connector_fd, &properties) < 0) 31 return Error::from_syscall(DeprecatedString::formatted("failed to ioctl {}", m_device), errno); 32 33 m_can_device_flush_buffers = (properties.partial_flushing_support != 0); 34 m_can_device_flush_entire_framebuffer = (properties.flushing_support != 0); 35 m_can_set_head_buffer = (properties.doublebuffer_support != 0); 36 m_max_size_in_bytes = properties.max_buffer_bytes; 37 return {}; 38} 39 40HardwareScreenBackend::~HardwareScreenBackend() 41{ 42 if (m_display_connector_fd >= 0) { 43 close(m_display_connector_fd); 44 m_display_connector_fd = -1; 45 } 46 if (m_framebuffer) { 47 MUST(Core::System::munmap(m_framebuffer, m_size_in_bytes)); 48 49 m_framebuffer = nullptr; 50 m_size_in_bytes = 0; 51 } 52} 53 54ErrorOr<void> HardwareScreenBackend::set_safe_head_mode_setting() 55{ 56 auto rc = graphics_connector_set_safe_head_mode_setting(m_display_connector_fd); 57 if (rc != 0) { 58 dbgln("Failed to set backend safe mode setting: aborting"); 59 return Error::from_syscall("graphics_connector_set_safe_head_mode_setting"sv, rc); 60 } 61 return {}; 62} 63 64ErrorOr<void> HardwareScreenBackend::set_head_mode_setting(GraphicsHeadModeSetting mode_setting) 65{ 66 size_t size_in_bytes = 0; 67 if (m_can_set_head_buffer) { 68 size_in_bytes = mode_setting.horizontal_stride * mode_setting.vertical_active * 2; 69 } else { 70 size_in_bytes = mode_setting.horizontal_stride * mode_setting.vertical_active; 71 } 72 VERIFY(size_in_bytes != 0); 73 if (m_max_size_in_bytes < size_in_bytes) 74 return Error::from_errno(EOVERFLOW); 75 76 GraphicsHeadModeSetting requested_mode_setting = mode_setting; 77 auto rc = graphics_connector_set_head_mode_setting(m_display_connector_fd, &requested_mode_setting); 78 if (rc != 0) { 79 dbgln("Failed to set backend mode setting: falling back to safe resolution"); 80 rc = graphics_connector_set_safe_head_mode_setting(m_display_connector_fd); 81 if (rc != 0) { 82 dbgln("Failed to set backend safe mode setting: aborting"); 83 return Error::from_syscall("graphics_connector_set_safe_head_mode_setting"sv, rc); 84 } 85 dbgln("Failed to set backend mode setting: falling back to safe resolution - success."); 86 } 87 88 return {}; 89} 90 91ErrorOr<void> HardwareScreenBackend::unmap_framebuffer() 92{ 93 if (m_framebuffer) { 94 size_t previous_size_in_bytes = m_size_in_bytes; 95 return Core::System::munmap(m_framebuffer, previous_size_in_bytes); 96 } 97 return {}; 98} 99 100ErrorOr<void> HardwareScreenBackend::map_framebuffer() 101{ 102 GraphicsHeadModeSetting mode_setting {}; 103 memset(&mode_setting, 0, sizeof(GraphicsHeadModeSetting)); 104 int rc = graphics_connector_get_head_mode_setting(m_display_connector_fd, &mode_setting); 105 if (rc != 0) { 106 return Error::from_syscall("graphics_connector_get_head_mode_setting"sv, rc); 107 } 108 if (m_can_set_head_buffer) { 109 m_size_in_bytes = mode_setting.horizontal_stride * mode_setting.vertical_active * 2; 110 } else { 111 m_size_in_bytes = mode_setting.horizontal_stride * mode_setting.vertical_active; 112 } 113 114 if (m_max_size_in_bytes < m_size_in_bytes) 115 return Error::from_errno(EOVERFLOW); 116 m_framebuffer = (Gfx::ARGB32*)TRY(Core::System::mmap(nullptr, m_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, m_display_connector_fd, 0)); 117 118 if (m_can_set_head_buffer) { 119 // Note: fall back to assuming the second buffer starts right after the last line of the first 120 // Note: for now, this calculation works quite well, so need to defer it to another function 121 // that does ioctl to figure out the correct offset. If a Framebuffer device ever happens to 122 // to set the second buffer at different location than this, we might need to consider bringing 123 // back a function with ioctl to check this. 124 m_back_buffer_offset = static_cast<size_t>(mode_setting.horizontal_stride) * mode_setting.vertical_active; 125 } else { 126 m_back_buffer_offset = 0; 127 } 128 129 return {}; 130} 131 132ErrorOr<GraphicsHeadModeSetting> HardwareScreenBackend::get_head_mode_setting() 133{ 134 GraphicsHeadModeSetting mode_setting {}; 135 memset(&mode_setting, 0, sizeof(GraphicsHeadModeSetting)); 136 int rc = graphics_connector_get_head_mode_setting(m_display_connector_fd, &mode_setting); 137 if (rc != 0) { 138 return Error::from_syscall("graphics_connector_get_head_mode_setting"sv, rc); 139 } 140 m_pitch = mode_setting.horizontal_stride; 141 return mode_setting; 142} 143 144void HardwareScreenBackend::set_head_buffer(int head_index) 145{ 146 VERIFY(m_can_set_head_buffer); 147 VERIFY(head_index <= 1 && head_index >= 0); 148 GraphicsHeadVerticalOffset offset { 0, 0 }; 149 if (head_index == 1) 150 offset.offsetted = 1; 151 int rc = fb_set_head_vertical_offset_buffer(m_display_connector_fd, &offset); 152 VERIFY(rc == 0); 153} 154 155ErrorOr<void> HardwareScreenBackend::flush_framebuffer_rects(int buffer_index, ReadonlySpan<FBRect> flush_rects) 156{ 157 int rc = fb_flush_buffers(m_display_connector_fd, buffer_index, flush_rects.data(), (unsigned)flush_rects.size()); 158 if (rc == -ENOTSUP) 159 m_can_device_flush_buffers = false; 160 else if (rc != 0) 161 return Error::from_syscall("fb_flush_buffers"sv, rc); 162 return {}; 163} 164 165ErrorOr<void> HardwareScreenBackend::flush_framebuffer() 166{ 167 int rc = fb_flush_head(m_display_connector_fd); 168 if (rc == -ENOTSUP) 169 m_can_device_flush_entire_framebuffer = false; 170 else if (rc != 0) 171 return Error::from_syscall("fb_flush_head"sv, rc); 172 return {}; 173} 174}