HardwareScreenBackend.cpp 6.2 KB

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