HardwareScreenBackend.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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/FB.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, bool display_connector_device_backed)
  18. : m_device(move(device))
  19. , display_connector_device_backed(display_connector_device_backed)
  20. {
  21. }
  22. ErrorOr<void> HardwareScreenBackend::open()
  23. {
  24. m_framebuffer_fd = TRY(Core::System::open(m_device.characters(), O_RDWR | O_CLOEXEC));
  25. GraphicsConnectorProperties properties;
  26. if (fb_get_properties(m_framebuffer_fd, &properties) < 0)
  27. return Error::from_syscall(String::formatted("failed to ioctl {}", m_device), errno);
  28. m_can_device_flush_buffers = (properties.partial_flushing_support != 0);
  29. m_can_device_flush_entire_framebuffer = (properties.flushing_support != 0);
  30. m_can_set_head_buffer = (properties.doublebuffer_support != 0);
  31. return {};
  32. }
  33. HardwareScreenBackend::~HardwareScreenBackend()
  34. {
  35. if (m_framebuffer_fd >= 0) {
  36. close(m_framebuffer_fd);
  37. m_framebuffer_fd = -1;
  38. }
  39. if (m_framebuffer) {
  40. if (!display_connector_device_backed)
  41. MUST(Core::System::munmap(m_framebuffer, m_size_in_bytes));
  42. else
  43. free(m_framebuffer);
  44. m_framebuffer = nullptr;
  45. m_size_in_bytes = 0;
  46. }
  47. }
  48. ErrorOr<void> HardwareScreenBackend::set_head_resolution(FBHeadResolution resolution)
  49. {
  50. if (!display_connector_device_backed) {
  51. auto rc = fb_set_resolution(m_framebuffer_fd, &resolution);
  52. if (rc != 0)
  53. return Error::from_syscall("fb_set_resolution", rc);
  54. } else {
  55. GraphicsHeadModeSetting mode_setting;
  56. memset(&mode_setting, 0, sizeof(GraphicsHeadModeSetting));
  57. mode_setting.horizontal_active = resolution.width;
  58. mode_setting.vertical_active = resolution.height;
  59. mode_setting.horizontal_stride = resolution.pitch;
  60. auto rc = fb_set_head_mode_setting(m_framebuffer_fd, &mode_setting);
  61. if (rc != 0) {
  62. dbgln("Failed to set backend mode setting: falling back to safe resolution");
  63. rc = fb_set_safe_head_mode_setting(m_framebuffer_fd);
  64. if (rc != 0) {
  65. dbgln("Failed to set backend safe mode setting: aborting");
  66. return Error::from_syscall("fb_set_safe_head_mode_setting", rc);
  67. }
  68. dbgln("Failed to set backend mode setting: falling back to safe resolution - success.");
  69. }
  70. }
  71. return {};
  72. }
  73. ErrorOr<void> HardwareScreenBackend::unmap_framebuffer()
  74. {
  75. if (m_framebuffer) {
  76. if (!display_connector_device_backed) {
  77. size_t previous_size_in_bytes = m_size_in_bytes;
  78. return Core::System::munmap(m_framebuffer, previous_size_in_bytes);
  79. } else {
  80. free(m_framebuffer);
  81. }
  82. }
  83. return {};
  84. }
  85. ErrorOr<void> HardwareScreenBackend::write_all_contents(Gfx::IntRect const& virtual_rect)
  86. {
  87. if (!display_connector_device_backed)
  88. return {};
  89. lseek(m_framebuffer_fd, 0, SEEK_SET);
  90. write(m_framebuffer_fd, scanline(0, 0), virtual_rect.height() * m_pitch);
  91. if (m_can_set_head_buffer) {
  92. if (lseek(m_framebuffer_fd, virtual_rect.height() * m_pitch, SEEK_SET) < 0) {
  93. VERIFY_NOT_REACHED();
  94. }
  95. if (write(m_framebuffer_fd, scanline(0, 0), virtual_rect.height() * m_pitch) < 0)
  96. VERIFY_NOT_REACHED();
  97. }
  98. return {};
  99. }
  100. ErrorOr<void> HardwareScreenBackend::map_framebuffer()
  101. {
  102. if (!display_connector_device_backed) {
  103. FBHeadProperties properties;
  104. properties.head_index = 0;
  105. int rc = fb_get_head_properties(m_framebuffer_fd, &properties);
  106. if (rc != 0)
  107. return Error::from_syscall("fb_get_head_properties", rc);
  108. m_size_in_bytes = properties.buffer_length;
  109. m_framebuffer = (Gfx::ARGB32*)TRY(Core::System::mmap(nullptr, m_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, m_framebuffer_fd, 0));
  110. if (m_can_set_head_buffer) {
  111. // Note: fall back to assuming the second buffer starts right after the last line of the first
  112. // Note: for now, this calculation works quite well, so need to defer it to another function
  113. // that does ioctl to figure out the correct offset. If a Framebuffer device ever happens to
  114. // to set the second buffer at different location than this, we might need to consider bringing
  115. // back a function with ioctl to check this.
  116. m_back_buffer_offset = static_cast<size_t>(properties.pitch) * properties.height;
  117. } else {
  118. m_back_buffer_offset = 0;
  119. }
  120. } else {
  121. GraphicsHeadModeSetting mode_setting {};
  122. memset(&mode_setting, 0, sizeof(GraphicsHeadModeSetting));
  123. int rc = fb_get_head_mode_setting(m_framebuffer_fd, &mode_setting);
  124. if (rc != 0) {
  125. return Error::from_syscall("fb_get_head_mode_setting", rc);
  126. }
  127. m_size_in_bytes = mode_setting.horizontal_stride * mode_setting.vertical_active * 2;
  128. m_framebuffer = (Gfx::ARGB32*)malloc(m_size_in_bytes);
  129. if (m_can_set_head_buffer) {
  130. // Note: fall back to assuming the second buffer starts right after the last line of the first
  131. // Note: for now, this calculation works quite well, so need to defer it to another function
  132. // that does ioctl to figure out the correct offset. If a Framebuffer device ever happens to
  133. // to set the second buffer at different location than this, we might need to consider bringing
  134. // back a function with ioctl to check this.
  135. m_back_buffer_offset = static_cast<size_t>(mode_setting.horizontal_stride) * mode_setting.vertical_active;
  136. } else {
  137. m_back_buffer_offset = 0;
  138. }
  139. }
  140. return {};
  141. }
  142. ErrorOr<FBHeadProperties> HardwareScreenBackend::get_head_properties()
  143. {
  144. if (!display_connector_device_backed) {
  145. FBHeadProperties properties;
  146. properties.head_index = 0;
  147. int rc = fb_get_head_properties(m_framebuffer_fd, &properties);
  148. if (rc != 0)
  149. return Error::from_syscall("fb_get_head_properties", rc);
  150. m_pitch = static_cast<int>(properties.pitch);
  151. return properties;
  152. } else {
  153. GraphicsHeadModeSetting mode_setting {};
  154. memset(&mode_setting, 0, sizeof(GraphicsHeadModeSetting));
  155. int rc = fb_get_head_mode_setting(m_framebuffer_fd, &mode_setting);
  156. if (rc != 0) {
  157. return Error::from_syscall("fb_get_head_mode_setting", rc);
  158. }
  159. m_pitch = mode_setting.horizontal_stride;
  160. // Note: We translate (for now, until Framebuffer devices are removed) the GraphicsHeadModeSetting
  161. // structure to FBHeadProperties.
  162. FBHeadProperties properties;
  163. properties.head_index = 0;
  164. properties.pitch = mode_setting.horizontal_stride;
  165. properties.width = mode_setting.horizontal_active;
  166. properties.height = mode_setting.vertical_active;
  167. properties.offset = 0;
  168. properties.buffer_length = mode_setting.horizontal_stride * mode_setting.vertical_active * 2;
  169. return properties;
  170. }
  171. VERIFY_NOT_REACHED();
  172. }
  173. void HardwareScreenBackend::set_head_buffer(int head_index)
  174. {
  175. VERIFY(m_can_set_head_buffer);
  176. VERIFY(head_index <= 1 && head_index >= 0);
  177. GraphicsHeadVerticalOffset offset { 0, 0 };
  178. if (head_index == 1)
  179. offset.offsetted = 1;
  180. int rc = fb_set_head_vertical_offset_buffer(m_framebuffer_fd, &offset);
  181. VERIFY(rc == 0);
  182. }
  183. ErrorOr<void> HardwareScreenBackend::flush_framebuffer_rects(int buffer_index, Span<FBRect const> flush_rects)
  184. {
  185. int rc = fb_flush_buffers(m_framebuffer_fd, buffer_index, flush_rects.data(), (unsigned)flush_rects.size());
  186. if (rc == -ENOTSUP)
  187. m_can_device_flush_buffers = false;
  188. else
  189. return Error::from_syscall("fb_flush_buffers", rc);
  190. return {};
  191. }
  192. ErrorOr<void> HardwareScreenBackend::flush_framebuffer()
  193. {
  194. int rc = fb_flush_head(m_framebuffer_fd);
  195. if (rc == -ENOTSUP)
  196. m_can_device_flush_entire_framebuffer = false;
  197. else
  198. return Error::from_syscall("fb_flush_head", rc);
  199. return {};
  200. }
  201. }