瀏覽代碼

WindowServer: Try to auto-add unconfigured framebuffer devices

This will try to auto-add framebuffer devices that haven't been
explicitly configured to the right-hand side.
Tom 4 年之前
父節點
當前提交
a9906cfcd1

+ 1 - 0
Userland/Services/WindowServer/ScreenLayout.h

@@ -38,6 +38,7 @@ public:
     void normalize();
     bool load_config(const Core::ConfigFile& config_file, String* error_msg = nullptr);
     bool save_config(Core::ConfigFile& config_file, bool sync = true) const;
+    bool try_auto_add_framebuffer(String const&);
 
     // TODO: spaceship operator
     bool operator!=(const ScreenLayout& other) const;

+ 76 - 0
Userland/Services/WindowServer/ScreenLayout.ipp

@@ -4,7 +4,10 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <Kernel/API/FB.h>
 #include <Services/WindowServer/ScreenLayout.h>
+#include <errno.h>
+#include <fcntl.h>
 
 // Must be included after LibIPC/Forward.h
 #include <LibIPC/Decoder.h>
@@ -183,6 +186,79 @@ bool ScreenLayout::operator!=(const ScreenLayout& other) const
     return false;
 }
 
+bool ScreenLayout::try_auto_add_framebuffer(String const& device_path)
+{
+    int framebuffer_fd = open(device_path.characters(), O_RDWR | O_CLOEXEC);
+    if (framebuffer_fd < 0) {
+        int err = errno;
+        dbgln("Error ({}) opening framebuffer device {}", err, device_path);
+        return false;
+    }
+    ScopeGuard fd_guard([&] {
+        close(framebuffer_fd);
+    });
+    FBResolution resolution {};
+    if (fb_get_resolution(framebuffer_fd, &resolution) < 0) {
+        int err = errno;
+        dbgln("Error ({}) querying resolution from framebuffer device {}", err, device_path);
+        return false;
+    }
+    if (resolution.width == 0 || resolution.height == 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];
+        resolution.width = main_screen.resolution.width();
+        resolution.height = main_screen.resolution.height();
+    }
+
+    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)resolution.width,
+            (int)resolution.height
+        };
+
+        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) {
+            screens.append({
+                device : device_path,
+                location : new_screen_rect.location(),
+                resolution : new_screen_rect.size(),
+                scale_factor : 1
+            });
+
+            if (is_valid()) {
+                // We got lucky!
+                screens_guard.disarm();
+                return true;
+            }
+        }
+    }
+
+    dbgln("Failed to add framebuffer device {} with resolution {}x{} to screen layout", device_path, resolution.width, resolution.height);
+    return false;
+}
+
 }
 
 namespace IPC {

+ 16 - 1
Userland/Services/WindowServer/main.cpp

@@ -10,6 +10,8 @@
 #include "Screen.h"
 #include "WindowManager.h"
 #include <LibCore/ConfigFile.h>
+#include <LibCore/DirIterator.h>
+#include <LibCore/File.h>
 #include <LibGfx/Palette.h>
 #include <LibGfx/SystemTheme.h>
 #include <signal.h>
@@ -88,7 +90,20 @@ int main(int, char**)
         for (auto& screen_info : screen_layout.screens)
             fb_devices_configured.set(screen_info.device);
 
-        // TODO: Enumerate the /dev/fbX devices and set up any ones we find that we haven't already used
+        // 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()) {
+            auto path = di.next_path();
+            if (!path.starts_with("fb"))
+                continue;
+            auto full_path = String::formatted("/dev/{}", 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_framebuffer(full_path))
+                dbgln("Could not auto-add framebuffer device {} to screen layout", full_path);
+        }
 
         if (!WindowServer::Screen::apply_layout(move(screen_layout), error_msg)) {
             dbgln("Error applying screen layout: {}", error_msg);