summaryrefslogtreecommitdiff
path: root/Userland/Services/WindowServer
diff options
context:
space:
mode:
authorLiav A <liavalb@gmail.com>2022-04-29 17:56:24 +0300
committerAndreas Kling <kling@serenityos.org>2022-05-05 20:55:57 +0200
commitd9a2706079d314416353148cc9822e997c37b680 (patch)
treeab9c68b8bb9651fb27c9cf28c14d7a88ce00f8fb /Userland/Services/WindowServer
parente6ebf9e5c1e82a638ac2a0c40166da36ca1d5bd5 (diff)
downloadserenity-d9a2706079d314416353148cc9822e997c37b680.zip
DisplaySettings+WindowServer: Add support for display connector devices
Diffstat (limited to 'Userland/Services/WindowServer')
-rw-r--r--Userland/Services/WindowServer/Compositor.cpp7
-rw-r--r--Userland/Services/WindowServer/HardwareScreenBackend.cpp139
-rw-r--r--Userland/Services/WindowServer/HardwareScreenBackend.h11
-rw-r--r--Userland/Services/WindowServer/Screen.cpp18
-rw-r--r--Userland/Services/WindowServer/Screen.h2
-rw-r--r--Userland/Services/WindowServer/ScreenBackend.h2
-rw-r--r--Userland/Services/WindowServer/ScreenLayout.h4
-rw-r--r--Userland/Services/WindowServer/ScreenLayout.ipp95
-rw-r--r--Userland/Services/WindowServer/main.cpp34
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);