summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Services/WindowServer/CMakeLists.txt1
-rw-r--r--Userland/Services/WindowServer/HardwareScreenBackend.cpp126
-rw-r--r--Userland/Services/WindowServer/HardwareScreenBackend.h39
-rw-r--r--Userland/Services/WindowServer/ScreenBackend.h47
4 files changed, 213 insertions, 0 deletions
diff --git a/Userland/Services/WindowServer/CMakeLists.txt b/Userland/Services/WindowServer/CMakeLists.txt
index 96d7cde7ef..fb52873f8a 100644
--- a/Userland/Services/WindowServer/CMakeLists.txt
+++ b/Userland/Services/WindowServer/CMakeLists.txt
@@ -25,6 +25,7 @@ set(SOURCES
MultiScaleBitmaps.cpp
Overlays.cpp
Screen.cpp
+ HardwareScreenBackend.cpp
ScreenLayout.cpp
Window.cpp
WindowFrame.cpp
diff --git a/Userland/Services/WindowServer/HardwareScreenBackend.cpp b/Userland/Services/WindowServer/HardwareScreenBackend.cpp
new file mode 100644
index 0000000000..2c25dd8253
--- /dev/null
+++ b/Userland/Services/WindowServer/HardwareScreenBackend.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2018-2020, the SerenityOS developers.
+ * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "HardwareScreenBackend.h"
+#include "ScreenBackend.h"
+#include <AK/Try.h>
+#include <Kernel/API/FB.h>
+#include <LibCore/System.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+namespace WindowServer {
+
+HardwareScreenBackend::HardwareScreenBackend(String device)
+ : m_device(move(device))
+{
+}
+
+ErrorOr<void> HardwareScreenBackend::open()
+{
+ m_framebuffer_fd = TRY(Core::System::open(m_device.characters(), O_RDWR | O_CLOEXEC));
+
+ FBProperties properties;
+ if (fb_get_properties(m_framebuffer_fd, &properties) < 0)
+ return Error::from_syscall(String::formatted("failed to ioctl {}", m_device), errno);
+
+ m_can_device_flush_buffers = (properties.partial_flushing_support != 0);
+ m_can_set_head_buffer = (properties.doublebuffer_support != 0);
+ return {};
+}
+
+HardwareScreenBackend::~HardwareScreenBackend()
+{
+ if (m_framebuffer_fd >= 0) {
+ close(m_framebuffer_fd);
+ m_framebuffer_fd = -1;
+ }
+ if (m_framebuffer) {
+ MUST(Core::System::munmap(m_framebuffer, m_size_in_bytes));
+
+ m_framebuffer = nullptr;
+ m_size_in_bytes = 0;
+ }
+}
+
+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);
+ 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);
+ }
+ return {};
+}
+
+ErrorOr<void> HardwareScreenBackend::map_framebuffer()
+{
+ 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;
+ }
+
+ return {};
+}
+
+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;
+}
+
+void HardwareScreenBackend::set_head_buffer(int head_index)
+{
+ VERIFY(m_can_set_head_buffer);
+ VERIFY(head_index <= 1 && head_index >= 0);
+ FBHeadVerticalOffset offset { 0, 0 };
+ if (head_index == 1)
+ offset.offsetted = 1;
+ int rc = fb_set_head_vertical_offset_buffer(m_framebuffer_fd, &offset);
+ VERIFY(rc == 0);
+}
+
+ErrorOr<void> HardwareScreenBackend::flush_framebuffer_rects(int buffer_index, Span<FBRect const> flush_rects)
+{
+ int rc = fb_flush_buffers(m_framebuffer_fd, buffer_index, flush_rects.data(), (unsigned)flush_rects.size());
+ if (rc == -ENOTSUP)
+ m_can_device_flush_buffers = false;
+ else
+ return Error::from_syscall("fb_flush_buffers", rc);
+ return {};
+}
+
+}
diff --git a/Userland/Services/WindowServer/HardwareScreenBackend.h b/Userland/Services/WindowServer/HardwareScreenBackend.h
new file mode 100644
index 0000000000..c491db7f8b
--- /dev/null
+++ b/Userland/Services/WindowServer/HardwareScreenBackend.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include "ScreenBackend.h"
+#include "ScreenLayout.h"
+#include <AK/Error.h>
+#include <AK/Span.h>
+#include <AK/String.h>
+#include <sys/ioctl_numbers.h>
+
+namespace WindowServer {
+class HardwareScreenBackend : public ScreenBackend {
+public:
+ virtual ~HardwareScreenBackend();
+
+ HardwareScreenBackend(String device);
+
+ virtual ErrorOr<void> open() override;
+
+ virtual void set_head_buffer(int index) override;
+
+ virtual ErrorOr<void> flush_framebuffer_rects(int buffer_index, Span<FBRect const> rects) override;
+
+ virtual ErrorOr<void> unmap_framebuffer() override;
+ virtual ErrorOr<void> map_framebuffer() override;
+
+ virtual ErrorOr<void> set_head_resolution(FBHeadResolution) override;
+ virtual ErrorOr<FBHeadProperties> get_head_properties() override;
+
+ String m_device {};
+ int m_framebuffer_fd { -1 };
+};
+
+}
diff --git a/Userland/Services/WindowServer/ScreenBackend.h b/Userland/Services/WindowServer/ScreenBackend.h
new file mode 100644
index 0000000000..2b6de0f3f0
--- /dev/null
+++ b/Userland/Services/WindowServer/ScreenBackend.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include "ScreenLayout.h"
+#include <AK/Error.h>
+#include <AK/Span.h>
+#include <sys/ioctl_numbers.h>
+
+namespace WindowServer {
+
+// Handles low-level device interfacing for the screen.
+// ScreenBackend is a thin transparent wrapper around framebuffer-related data which is responsible for setting up this data,
+// tearing it down, changing its properties like size, and performing flushes.
+// The screen is intended to directly access the members to perform its function, but it only ever reads from anything
+// except the data in the framebuffer memory.
+class ScreenBackend {
+public:
+ virtual ~ScreenBackend() = default;
+
+ virtual ErrorOr<void> open() = 0;
+
+ virtual void set_head_buffer(int index) = 0;
+
+ virtual ErrorOr<void> flush_framebuffer_rects(int buffer_index, Span<FBRect const> rects) = 0;
+
+ virtual ErrorOr<void> unmap_framebuffer() = 0;
+ virtual ErrorOr<void> map_framebuffer() = 0;
+
+ virtual ErrorOr<void> set_head_resolution(FBHeadResolution) = 0;
+ virtual ErrorOr<FBHeadProperties> get_head_properties() = 0;
+
+ bool m_can_device_flush_buffers { true };
+ bool m_can_set_head_buffer { false };
+
+ Gfx::ARGB32* m_framebuffer { nullptr };
+ size_t m_size_in_bytes { 0 };
+ size_t m_back_buffer_offset { 0 };
+
+ int m_pitch { 0 };
+};
+
+}