diff options
author | Liav A <liavalb@gmail.com> | 2022-04-29 17:56:24 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-05-05 20:55:57 +0200 |
commit | d9a2706079d314416353148cc9822e997c37b680 (patch) | |
tree | ab9c68b8bb9651fb27c9cf28c14d7a88ce00f8fb /Userland/Services/WindowServer | |
parent | e6ebf9e5c1e82a638ac2a0c40166da36ca1d5bd5 (diff) | |
download | serenity-d9a2706079d314416353148cc9822e997c37b680.zip |
DisplaySettings+WindowServer: Add support for display connector devices
Diffstat (limited to 'Userland/Services/WindowServer')
-rw-r--r-- | Userland/Services/WindowServer/Compositor.cpp | 7 | ||||
-rw-r--r-- | Userland/Services/WindowServer/HardwareScreenBackend.cpp | 139 | ||||
-rw-r--r-- | Userland/Services/WindowServer/HardwareScreenBackend.h | 11 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Screen.cpp | 18 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Screen.h | 2 | ||||
-rw-r--r-- | Userland/Services/WindowServer/ScreenBackend.h | 2 | ||||
-rw-r--r-- | Userland/Services/WindowServer/ScreenLayout.h | 4 | ||||
-rw-r--r-- | Userland/Services/WindowServer/ScreenLayout.ipp | 95 | ||||
-rw-r--r-- | Userland/Services/WindowServer/main.cpp | 34 |
9 files changed, 274 insertions, 38 deletions
diff --git a/Userland/Services/WindowServer/Compositor.cpp b/Userland/Services/WindowServer/Compositor.cpp index 926472dd6f..6941a5c6ca 100644 --- a/Userland/Services/WindowServer/Compositor.cpp +++ b/Userland/Services/WindowServer/Compositor.cpp @@ -721,6 +721,13 @@ void Compositor::flush(Screen& screen) // now so that they can be sent to the device. screen.flush_display(screen_data.m_buffers_are_flipped ? 1 : 0); } + + // Note: We write all contents from the internal buffer of WindowServer Screen + // to the actual framebuffer with the write() syscall, but after we flush the screen + // to ensure we are in a "clean state"... + // FIXME: This write is completely inefficient and needs to be done in chunks + // only when appropriate... + screen.write_all_display_contents(); } void Compositor::invalidate_screen() diff --git a/Userland/Services/WindowServer/HardwareScreenBackend.cpp b/Userland/Services/WindowServer/HardwareScreenBackend.cpp index 2c25dd8253..36469a5cd6 100644 --- a/Userland/Services/WindowServer/HardwareScreenBackend.cpp +++ b/Userland/Services/WindowServer/HardwareScreenBackend.cpp @@ -17,8 +17,9 @@ namespace WindowServer { -HardwareScreenBackend::HardwareScreenBackend(String device) +HardwareScreenBackend::HardwareScreenBackend(String device, bool display_connector_device_backed) : m_device(move(device)) + , display_connector_device_backed(display_connector_device_backed) { } @@ -42,7 +43,10 @@ HardwareScreenBackend::~HardwareScreenBackend() m_framebuffer_fd = -1; } if (m_framebuffer) { - MUST(Core::System::munmap(m_framebuffer, m_size_in_bytes)); + if (!display_connector_device_backed) + MUST(Core::System::munmap(m_framebuffer, m_size_in_bytes)); + else + free(m_framebuffer); m_framebuffer = nullptr; m_size_in_bytes = 0; @@ -51,41 +55,95 @@ HardwareScreenBackend::~HardwareScreenBackend() ErrorOr<void> HardwareScreenBackend::set_head_resolution(FBHeadResolution resolution) { - auto rc = fb_set_resolution(m_framebuffer_fd, &resolution); - if (rc != 0) - return Error::from_syscall("fb_set_resolution", rc); + if (!display_connector_device_backed) { + auto rc = fb_set_resolution(m_framebuffer_fd, &resolution); + if (rc != 0) + return Error::from_syscall("fb_set_resolution", rc); + } else { + FBHeadModeSetting mode_setting; + memset(&mode_setting, 0, sizeof(FBHeadModeSetting)); + mode_setting.horizontal_active = resolution.width; + mode_setting.vertical_active = resolution.height; + mode_setting.horizontal_stride = resolution.pitch; + auto rc = fb_set_head_mode_setting(m_framebuffer_fd, &mode_setting); + if (rc != 0) + return Error::from_syscall("fb_set_head_mode_setting", rc); + } + return {}; } ErrorOr<void> HardwareScreenBackend::unmap_framebuffer() { if (m_framebuffer) { - size_t previous_size_in_bytes = m_size_in_bytes; - return Core::System::munmap(m_framebuffer, previous_size_in_bytes); + if (!display_connector_device_backed) { + size_t previous_size_in_bytes = m_size_in_bytes; + return Core::System::munmap(m_framebuffer, previous_size_in_bytes); + } else { + free(m_framebuffer); + } } return {}; } -ErrorOr<void> HardwareScreenBackend::map_framebuffer() +ErrorOr<void> HardwareScreenBackend::write_all_contents(Gfx::IntRect const& virtual_rect) { - FBHeadProperties properties; - properties.head_index = 0; - int rc = fb_get_head_properties(m_framebuffer_fd, &properties); - if (rc != 0) - return Error::from_syscall("fb_get_head_properties", rc); - m_size_in_bytes = properties.buffer_length; + if (!display_connector_device_backed) + return {}; + lseek(m_framebuffer_fd, 0, SEEK_SET); + write(m_framebuffer_fd, scanline(0, 0), virtual_rect.height() * m_pitch); + if (m_can_set_head_buffer) { + if (lseek(m_framebuffer_fd, virtual_rect.height() * m_pitch, SEEK_SET) < 0) { + VERIFY_NOT_REACHED(); + } - m_framebuffer = (Gfx::ARGB32*)TRY(Core::System::mmap(nullptr, m_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, m_framebuffer_fd, 0)); + if (write(m_framebuffer_fd, scanline(0, 0), virtual_rect.height() * m_pitch) < 0) + VERIFY_NOT_REACHED(); + } + return {}; +} - if (m_can_set_head_buffer) { - // Note: fall back to assuming the second buffer starts right after the last line of the first - // Note: for now, this calculation works quite well, so need to defer it to another function - // that does ioctl to figure out the correct offset. If a Framebuffer device ever happens to - // to set the second buffer at different location than this, we might need to consider bringing - // back a function with ioctl to check this. - m_back_buffer_offset = static_cast<size_t>(properties.pitch) * properties.height; +ErrorOr<void> HardwareScreenBackend::map_framebuffer() +{ + if (!display_connector_device_backed) { + FBHeadProperties properties; + properties.head_index = 0; + int rc = fb_get_head_properties(m_framebuffer_fd, &properties); + if (rc != 0) + return Error::from_syscall("fb_get_head_properties", rc); + m_size_in_bytes = properties.buffer_length; + + m_framebuffer = (Gfx::ARGB32*)TRY(Core::System::mmap(nullptr, m_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, m_framebuffer_fd, 0)); + + if (m_can_set_head_buffer) { + // Note: fall back to assuming the second buffer starts right after the last line of the first + // Note: for now, this calculation works quite well, so need to defer it to another function + // that does ioctl to figure out the correct offset. If a Framebuffer device ever happens to + // to set the second buffer at different location than this, we might need to consider bringing + // back a function with ioctl to check this. + m_back_buffer_offset = static_cast<size_t>(properties.pitch) * properties.height; + } else { + m_back_buffer_offset = 0; + } } else { - m_back_buffer_offset = 0; + FBHeadModeSetting mode_setting {}; + memset(&mode_setting, 0, sizeof(FBHeadModeSetting)); + int rc = fb_get_head_mode_setting(m_framebuffer_fd, &mode_setting); + if (rc != 0) { + return Error::from_syscall("fb_get_head_mode_setting", rc); + } + m_size_in_bytes = mode_setting.horizontal_stride * mode_setting.vertical_active * 2; + m_framebuffer = (Gfx::ARGB32*)malloc(m_size_in_bytes); + if (m_can_set_head_buffer) { + // Note: fall back to assuming the second buffer starts right after the last line of the first + // Note: for now, this calculation works quite well, so need to defer it to another function + // that does ioctl to figure out the correct offset. If a Framebuffer device ever happens to + // to set the second buffer at different location than this, we might need to consider bringing + // back a function with ioctl to check this. + m_back_buffer_offset = static_cast<size_t>(mode_setting.horizontal_stride) * mode_setting.vertical_active; + } else { + m_back_buffer_offset = 0; + } } return {}; @@ -93,13 +151,34 @@ ErrorOr<void> HardwareScreenBackend::map_framebuffer() ErrorOr<FBHeadProperties> HardwareScreenBackend::get_head_properties() { - FBHeadProperties properties; - properties.head_index = 0; - int rc = fb_get_head_properties(m_framebuffer_fd, &properties); - if (rc != 0) - return Error::from_syscall("fb_get_head_properties", rc); - m_pitch = static_cast<int>(properties.pitch); - return properties; + if (!display_connector_device_backed) { + FBHeadProperties properties; + properties.head_index = 0; + int rc = fb_get_head_properties(m_framebuffer_fd, &properties); + if (rc != 0) + return Error::from_syscall("fb_get_head_properties", rc); + m_pitch = static_cast<int>(properties.pitch); + return properties; + } else { + FBHeadModeSetting mode_setting {}; + memset(&mode_setting, 0, sizeof(FBHeadModeSetting)); + int rc = fb_get_head_mode_setting(m_framebuffer_fd, &mode_setting); + if (rc != 0) { + return Error::from_syscall("fb_get_head_mode_setting", rc); + } + m_pitch = mode_setting.horizontal_stride; + // Note: We translate (for now, until Framebuffer devices are removed) the FBHeadModeSetting + // structure to FBHeadProperties. + FBHeadProperties properties; + properties.head_index = 0; + properties.pitch = mode_setting.horizontal_stride; + properties.width = mode_setting.horizontal_active; + properties.height = mode_setting.vertical_active; + properties.offset = 0; + properties.buffer_length = mode_setting.horizontal_stride * mode_setting.vertical_active * 2; + return properties; + } + VERIFY_NOT_REACHED(); } void HardwareScreenBackend::set_head_buffer(int head_index) diff --git a/Userland/Services/WindowServer/HardwareScreenBackend.h b/Userland/Services/WindowServer/HardwareScreenBackend.h index c491db7f8b..e1ce874ea2 100644 --- a/Userland/Services/WindowServer/HardwareScreenBackend.h +++ b/Userland/Services/WindowServer/HardwareScreenBackend.h @@ -18,7 +18,7 @@ class HardwareScreenBackend : public ScreenBackend { public: virtual ~HardwareScreenBackend(); - HardwareScreenBackend(String device); + HardwareScreenBackend(String device, bool display_connector_device_backed); virtual ErrorOr<void> open() override; @@ -32,8 +32,17 @@ public: virtual ErrorOr<void> set_head_resolution(FBHeadResolution) override; virtual ErrorOr<FBHeadProperties> get_head_properties() override; + virtual ErrorOr<void> write_all_contents(Gfx::IntRect const&) override; + String m_device {}; int m_framebuffer_fd { -1 }; + bool const display_connector_device_backed { false }; + + Gfx::ARGB32* scanline(int buffer_index, int y) const + { + size_t buffer_offset = buffer_index == 1 ? m_back_buffer_offset : 0; + return reinterpret_cast<Gfx::ARGB32*>(((u8*)m_framebuffer) + buffer_offset + (y * m_pitch)); + } }; } diff --git a/Userland/Services/WindowServer/Screen.cpp b/Userland/Services/WindowServer/Screen.cpp index 710cfab10f..ef6b24165a 100644 --- a/Userland/Services/WindowServer/Screen.cpp +++ b/Userland/Services/WindowServer/Screen.cpp @@ -231,7 +231,7 @@ bool Screen::open_device() switch (info.mode) { case ScreenLayout::Screen::Mode::Device: { - m_backend = make<HardwareScreenBackend>(info.device.value()); + m_backend = make<HardwareScreenBackend>(info.device.value(), false); auto return_value = m_backend->open(); if (return_value.is_error()) { dbgln("Screen #{}: Failed to open backend: {}", index(), return_value.error()); @@ -241,6 +241,17 @@ bool Screen::open_device() set_resolution(true); return true; } + case ScreenLayout::Screen::Mode::DisplayConnectorDevice: { + m_backend = make<HardwareScreenBackend>(info.device.value(), true); + auto return_value = m_backend->open(); + if (return_value.is_error()) { + dbgln("Screen #{}: Failed to open display connector backend: {}", index(), return_value.error()); + return false; + } + + set_resolution(true); + return true; + } case ScreenLayout::Screen::Mode::Virtual: { m_backend = make<VirtualScreenBackend>(); // Virtual device open should never fail. @@ -539,6 +550,11 @@ void Screen::flush_display(int buffer_index) flush_rects.pending_flush_rects.clear_with_capacity(); } +void Screen::write_all_display_contents() +{ + MUST(m_backend->write_all_contents(m_virtual_rect)); +} + void Screen::flush_display_front_buffer(int front_buffer_index, Gfx::IntRect& rect) { VERIFY(m_backend->m_can_device_flush_buffers); diff --git a/Userland/Services/WindowServer/Screen.h b/Userland/Services/WindowServer/Screen.h index 44f1510d8c..8737a92eae 100644 --- a/Userland/Services/WindowServer/Screen.h +++ b/Userland/Services/WindowServer/Screen.h @@ -174,6 +174,8 @@ public: CompositorScreenData& compositor_screen_data() { return *m_compositor_screen_data; } + void write_all_display_contents(); + private: Screen(size_t); bool open_device(); diff --git a/Userland/Services/WindowServer/ScreenBackend.h b/Userland/Services/WindowServer/ScreenBackend.h index 2b6de0f3f0..4e96bd7f2f 100644 --- a/Userland/Services/WindowServer/ScreenBackend.h +++ b/Userland/Services/WindowServer/ScreenBackend.h @@ -34,6 +34,8 @@ public: virtual ErrorOr<void> set_head_resolution(FBHeadResolution) = 0; virtual ErrorOr<FBHeadProperties> get_head_properties() = 0; + virtual ErrorOr<void> write_all_contents(Gfx::IntRect const&) { return {}; } + bool m_can_device_flush_buffers { true }; bool m_can_set_head_buffer { false }; diff --git a/Userland/Services/WindowServer/ScreenLayout.h b/Userland/Services/WindowServer/ScreenLayout.h index 5fb3eddee8..039a7d48c2 100644 --- a/Userland/Services/WindowServer/ScreenLayout.h +++ b/Userland/Services/WindowServer/ScreenLayout.h @@ -21,6 +21,7 @@ public: enum class Mode { Invalid, Device, + DisplayConnectorDevice, Virtual, } mode; Optional<String> device; @@ -42,6 +43,7 @@ public: switch (mode) { __ENUMERATE_MODE_ENUM(Invalid) __ENUMERATE_MODE_ENUM(Device) + __ENUMERATE_MODE_ENUM(DisplayConnectorDevice) __ENUMERATE_MODE_ENUM(Virtual) } VERIFY_NOT_REACHED(); @@ -59,7 +61,9 @@ public: bool normalize(); bool load_config(Core::ConfigFile const& config_file, String* error_msg = nullptr); bool save_config(Core::ConfigFile& config_file, bool sync = true) const; + // FIXME: Remove this once framebuffer devices are removed. bool try_auto_add_framebuffer(String const&); + bool try_auto_add_display_connector(String const&); // TODO: spaceship operator bool operator!=(ScreenLayout const& other) const; diff --git a/Userland/Services/WindowServer/ScreenLayout.ipp b/Userland/Services/WindowServer/ScreenLayout.ipp index 2c8d8b4b87..3d5f32f1cd 100644 --- a/Userland/Services/WindowServer/ScreenLayout.ipp +++ b/Userland/Services/WindowServer/ScreenLayout.ipp @@ -236,8 +236,15 @@ bool ScreenLayout::load_config(const Core::ConfigFile& config_file, String* erro if (!config_file.has_group(group_name)) break; auto str_mode = config_file.read_entry(group_name, "Mode"); - auto mode = str_mode == "Device" ? Screen::Mode::Device : str_mode == "Virtual" ? Screen::Mode::Virtual - : Screen::Mode::Invalid; + Screen::Mode mode { Screen::Mode::Invalid }; + if (str_mode == "Device") { + mode = Screen::Mode::Device; + } else if (str_mode == "DisplayConnectorDevice") { + mode = Screen::Mode::DisplayConnectorDevice; + } else if (str_mode == "Virtual") { + mode = Screen::Mode::Virtual; + } + if (mode == Screen::Mode::Invalid) { *error_msg = String::formatted("Invalid screen mode '{}'", str_mode); *this = {}; @@ -302,6 +309,90 @@ bool ScreenLayout::operator!=(const ScreenLayout& other) const return false; } +bool ScreenLayout::try_auto_add_display_connector(String const& device_path) +{ + int display_connector_fd = open(device_path.characters(), O_RDWR | O_CLOEXEC); + if (display_connector_fd < 0) { + int err = errno; + dbgln("Error ({}) opening display connector device {}", err, device_path); + return false; + } + ScopeGuard fd_guard([&] { + close(display_connector_fd); + }); + + FBHeadModeSetting mode_setting {}; + memset(&mode_setting, 0, sizeof(FBHeadModeSetting)); + if (fb_get_head_mode_setting(display_connector_fd, &mode_setting) < 0) { + int err = errno; + dbgln("Error ({}) querying resolution from display connector device {}", err, device_path); + return false; + } + if (mode_setting.horizontal_active == 0 || mode_setting.vertical_active == 0) { + // Looks like the display is not turned on. Since we don't know what the desired + // resolution should be, use the main display as reference. + if (screens.is_empty()) + return false; + auto& main_screen = screens[main_screen_index]; + mode_setting.horizontal_active = main_screen.resolution.width(); + mode_setting.vertical_active = main_screen.resolution.height(); + } + + auto append_screen = [&](Gfx::IntRect const& new_screen_rect) { + screens.append({ .mode = Screen::Mode::DisplayConnectorDevice, + .device = device_path, + .location = new_screen_rect.location(), + .resolution = new_screen_rect.size(), + .scale_factor = 1 }); + }; + + if (screens.is_empty()) { + append_screen({ 0, 0, mode_setting.horizontal_active, mode_setting.vertical_active }); + return true; + } + + auto original_screens = move(screens); + screens = original_screens; + ArmedScopeGuard screens_guard([&] { + screens = move(original_screens); + }); + + // Now that we know the current resolution, try to find a location that we can add onto + // TODO: make this a little more sophisticated in case a more complex layout is already configured + for (auto& screen : screens) { + auto screen_rect = screen.virtual_rect(); + Gfx::IntRect new_screen_rect { + screen_rect.right() + 1, + screen_rect.top(), + (int)mode_setting.horizontal_active, + (int)mode_setting.vertical_active + }; + + bool collision = false; + for (auto& other_screen : screens) { + if (&screen == &other_screen) + continue; + if (other_screen.virtual_rect().intersects(new_screen_rect)) { + collision = true; + break; + } + } + + if (!collision) { + append_screen(new_screen_rect); + if (is_valid()) { + // We got lucky! + screens_guard.disarm(); + return true; + } + } + } + + dbgln("Failed to add display connector device {} with resolution {}x{} to screen layout", device_path, mode_setting.horizontal_active, mode_setting.vertical_active); + return false; +} + +// FIXME: Remove this once framebuffer devices are removed. bool ScreenLayout::try_auto_add_framebuffer(String const& device_path) { int framebuffer_fd = open(device_path.characters(), O_RDWR | O_CLOEXEC); diff --git a/Userland/Services/WindowServer/main.cpp b/Userland/Services/WindowServer/main.cpp index 3e12ae7c7e..eb050c70a7 100644 --- a/Userland/Services/WindowServer/main.cpp +++ b/Userland/Services/WindowServer/main.cpp @@ -68,7 +68,8 @@ ErrorOr<int> serenity_main(Main::Arguments) WindowServer::ScreenLayout screen_layout; String error_msg; - auto add_unconfigured_devices = [&]() { + // FIXME: Remove this once framebuffer devices are removed. + auto add_unconfigured_framebuffer_devices = [&]() { // Enumerate the /dev/fbX devices and try to set up any ones we find that we haven't already used Core::DirIterator di("/dev", Core::DirIterator::SkipParentAndBaseDir); while (di.has_next()) { @@ -76,6 +77,7 @@ ErrorOr<int> serenity_main(Main::Arguments) if (!path.starts_with("fb")) continue; auto full_path = String::formatted("/dev/{}", path); + dbgln("{} :", full_path); if (!Core::File::is_device(full_path)) continue; if (fb_devices_configured.find(full_path) != fb_devices_configured.end()) @@ -85,10 +87,31 @@ ErrorOr<int> serenity_main(Main::Arguments) } }; + auto add_unconfigured_display_connector_devices = [&]() { + // Enumerate the /dev/fbX devices and try to set up any ones we find that we haven't already used + Core::DirIterator di("/dev/gpu", Core::DirIterator::SkipParentAndBaseDir); + while (di.has_next()) { + auto path = di.next_path(); + if (!path.starts_with("connector")) + continue; + auto full_path = String::formatted("/dev/gpu/{}", path); + if (!Core::File::is_device(full_path)) + continue; + if (fb_devices_configured.find(full_path) != fb_devices_configured.end()) + continue; + if (!screen_layout.try_auto_add_display_connector(full_path)) + dbgln("Could not auto-add framebuffer device {} to screen layout", full_path); + } + }; + auto apply_and_generate_generic_screen_layout = [&]() { screen_layout = {}; fb_devices_configured = {}; - add_unconfigured_devices(); + + // FIXME: Remove this once framebuffer devices are removed. + add_unconfigured_framebuffer_devices(); + + add_unconfigured_display_connector_devices(); if (!WindowServer::Screen::apply_layout(move(screen_layout), error_msg)) { dbgln("Failed to apply generated fallback screen layout: {}", error_msg); return false; @@ -100,10 +123,13 @@ ErrorOr<int> serenity_main(Main::Arguments) if (screen_layout.load_config(*wm_config, &error_msg)) { for (auto& screen_info : screen_layout.screens) - if (screen_info.mode == WindowServer::ScreenLayout::Screen::Mode::Device) + if (screen_info.mode == WindowServer::ScreenLayout::Screen::Mode::Device || screen_info.mode == WindowServer::ScreenLayout::Screen::Mode::DisplayConnectorDevice) fb_devices_configured.set(screen_info.device.value()); - add_unconfigured_devices(); + // FIXME: Remove this once framebuffer devices are removed. + add_unconfigured_framebuffer_devices(); + + add_unconfigured_display_connector_devices(); if (!WindowServer::Screen::apply_layout(move(screen_layout), error_msg)) { dbgln("Error applying screen layout: {}", error_msg); |