├── simplegfx.hh ├── CMakeLists.txt ├── pixman.hh ├── cairo.hh └── dyz-shm.cpp /simplegfx.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * simplegfx.hh 3 | * Copyright (C) 2017 Adrian Perez 4 | * 5 | * Distributed under terms of the MIT license. 6 | */ 7 | 8 | #ifndef SIMPLEGFX_HH 9 | #define SIMPLEGFX_HH 10 | 11 | #include 12 | 13 | namespace simplegfx { 14 | constexpr static const char* name = "simplegfx"; 15 | 16 | static inline uint16_t Argb32toRgb565_v0(uint32_t argb) { 17 | return static_cast((((argb >> 19) & 0x1F) << 11) | 18 | (((argb >> 10) & 0x3F) << 5) | 19 | (((argb >> 3) & 0x1F) << 0)); 20 | } 21 | 22 | } // namespace simplegfx 23 | 24 | #endif /* !SIMPLEGFX_HH */ 25 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | set(ALL_GRAPHICS 4 | cairo 5 | pixman 6 | simple 7 | ) 8 | set(GRAPHICS "cairo" CACHE STRING "Choose the graphics backend (one of ${ALL_GRAPHICS}") 9 | 10 | list(FIND ALL_GRAPHICS ${GRAPHICS} RET) 11 | if (${RET} EQUAL -1) 12 | message(FATAL_ERROR "Please choose a valid graphics backend (one of ${ALL_GRAPHICS}") 13 | endif () 14 | 15 | string(TOUPPER ${GRAPHICS} GRAPHICS_MACRO_NAME) 16 | 17 | 18 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGRAPHICS_${GRAPHICS_MACRO_NAME}=1 -std=c++11 -fno-exceptions -fno-rtti") 19 | 20 | find_package(PkgConfig) 21 | pkg_check_modules(DYZSHM REQUIRED glib-2.0 wpe-webkit) 22 | 23 | if (${GRAPHICS} STREQUAL "cairo") 24 | pkg_check_modules(DYZSHM_EXTRA REQUIRED cairo) 25 | elseif (${GRAPHICS} STREQUAL "pixman") 26 | pkg_check_modules(DYZSHM_EXTRA REQUIRED pixman-1) 27 | endif () 28 | 29 | add_executable(dyz-shm dyz-shm.cpp) 30 | target_include_directories(dyz-shm PUBLIC 31 | ${DYZSHM_INCLUDE_DIRS} 32 | ${DYZSHM_EXTRA_INCLUDE_DIRS} 33 | ) 34 | target_link_libraries(dyz-shm 35 | ${DYZSHM_LIBRARIES} 36 | ${DYZSHM_EXTRA_LIBRARIES} 37 | -lWPEBackend-shm 38 | ) 39 | install(TARGETS dyz-shm DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 40 | -------------------------------------------------------------------------------- /pixman.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * pixman.hh 3 | * Copyright (C) 2017 Adrian Perez 4 | * 5 | * Distributed under terms of the MIT license. 6 | */ 7 | 8 | #ifndef PIXMAN_HH 9 | #define PIXMAN_HH 10 | 11 | #include 12 | #include 13 | 14 | namespace pixman { 15 | constexpr static const char* name = "pixman"; 16 | 17 | using Format = ::pixman_format_code_t; 18 | 19 | namespace format { 20 | constexpr Format ARGB32 = PIXMAN_a8r8g8b8; 21 | constexpr Format RGB16_565 = PIXMAN_r5g6b5; 22 | }; 23 | 24 | template 25 | class Ref { 26 | public: 27 | constexpr Ref(Ref&& other) = default; 28 | 29 | ~Ref() { 30 | if (m_pointer) { 31 | do_destroy(m_pointer); 32 | m_pointer = nullptr; 33 | } 34 | } 35 | 36 | const T* constPointer() const { return m_pointer; } 37 | T* pointer() { return m_pointer; } 38 | 39 | protected: 40 | explicit Ref(T* pointer) : m_pointer(pointer) { } 41 | 42 | private: 43 | Ref(const Ref&) = delete; 44 | void operator=(const Ref&) = delete; 45 | 46 | T* m_pointer; 47 | }; 48 | 49 | class Transform; 50 | 51 | class Surface : public Ref { 54 | public: 55 | Surface(Format format, 56 | void* bits, 57 | uint32_t width, 58 | uint32_t height, 59 | uint32_t stride) 60 | : Ref(::pixman_image_create_bits_no_clear(format, 61 | static_cast(width), 62 | static_cast(height), 63 | static_cast(bits), 64 | static_cast(stride))) 65 | { 66 | } 67 | 68 | inline uint32_t width() const { 69 | return ::pixman_image_get_width(const_cast<::pixman_image_t*>(constPointer())); 70 | } 71 | inline uint32_t height() const { 72 | return ::pixman_image_get_height(const_cast<::pixman_image_t*>(constPointer())); 73 | } 74 | 75 | void setTransform(const Transform&); 76 | }; 77 | 78 | 79 | class Transform { 80 | public: 81 | static Transform identity() { 82 | Transform xfrm; 83 | ::pixman_f_transform_init_identity(&xfrm.m_transform); 84 | return xfrm; 85 | } 86 | 87 | static Transform rotate(double c, double s) { 88 | Transform xfrm; 89 | ::pixman_f_transform_init_rotate(&xfrm.m_transform, c, s); 90 | return xfrm; 91 | } 92 | 93 | inline static Transform rotate(double d) { 94 | return rotate(std::cos(d), std::sin(d)); 95 | } 96 | 97 | private: 98 | Transform() = default; 99 | 100 | ::pixman_transform_t asFixed() const { 101 | ::pixman_transform_t xfrm; 102 | pixman_transform_from_pixman_f_transform(&xfrm, &m_transform); 103 | return xfrm; 104 | } 105 | 106 | ::pixman_f_transform_t m_transform; 107 | 108 | friend class Surface; 109 | }; 110 | 111 | void Surface::setTransform(const Transform& xfrm) { 112 | const auto fx = xfrm.asFixed(); 113 | ::pixman_image_set_transform(pointer(), &fx); 114 | } 115 | }; 116 | 117 | #endif /* !PIXMAN_HH */ 118 | -------------------------------------------------------------------------------- /cairo.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * cairo.hh 3 | * Copyright (C) 2017 Adrian Perez 4 | * 5 | * Distributed under terms of the MIT license. 6 | */ 7 | 8 | #ifndef CAIRO_HH 9 | #define CAIRO_HH 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | namespace cairo { 17 | constexpr static const char* name = "cairo"; 18 | 19 | using Status = ::cairo_status_t; 20 | using Format = ::cairo_format_t; 21 | 22 | namespace format { 23 | constexpr Format ARGB32 = CAIRO_FORMAT_ARGB32; 24 | constexpr Format RGB16_565 = CAIRO_FORMAT_RGB16_565; 25 | }; 26 | 27 | template 30 | class Ref { 31 | public: 32 | using Type = T; 33 | 34 | ~Ref() { 35 | if (m_pointer) { 36 | do_destroy(m_pointer); 37 | m_pointer = nullptr; 38 | } 39 | } 40 | 41 | inline Type* pointer() { return m_pointer; } 42 | inline const Type* constPointer() const { return m_pointer; } 43 | inline Status status() const { return do_get_status(m_pointer); } 44 | inline const char* statusString() const { return ::cairo_status_to_string(status()); } 45 | inline operator bool() const { return status() == CAIRO_STATUS_SUCCESS; } 46 | 47 | protected: 48 | explicit Ref(Type* pointer) : m_pointer(pointer) { 49 | g_return_if_fail(m_pointer != nullptr); 50 | g_return_if_fail(status() == CAIRO_STATUS_SUCCESS); 51 | } 52 | 53 | Ref(const Ref&) = delete; 54 | void operator=(const Ref&) = delete; 55 | 56 | private: 57 | Type* m_pointer; 58 | }; 59 | 60 | 61 | class Surface : public Ref<::cairo_surface_t, 62 | ::cairo_surface_status, 63 | ::cairo_surface_destroy> { 64 | public: 65 | Surface(Ref::Type* surface) : Ref(surface) { } 66 | Surface(Format format, 67 | void* bits, 68 | uint32_t width, 69 | uint32_t height, 70 | uint32_t stride) 71 | : Ref(::cairo_image_surface_create_for_data(static_cast(bits), 72 | format, 73 | width, 74 | height, 75 | stride)) { } 76 | 77 | inline uint32_t width() const { 78 | auto value = ::cairo_image_surface_get_width(const_cast(constPointer())); 79 | return (value < 0) ? 0 : static_cast(value); 80 | } 81 | inline uint32_t height() const { 82 | auto value = ::cairo_image_surface_get_height(const_cast(constPointer())); 83 | return (value < 0) ? 0 : static_cast(value); 84 | } 85 | }; 86 | 87 | 88 | enum Rotation { 89 | None = 0, 90 | ClockWise90, 91 | ClockWise180, 92 | ClockWise270, 93 | ClockWise360 = None, 94 | CounterClockWise90 = ClockWise270, 95 | CounterClockWise180 = ClockWise180, 96 | CounterClockWise270 = ClockWise90, 97 | CounterClockWise360 = None, 98 | }; 99 | 100 | 101 | class Context : public Ref<::cairo_t, 102 | ::cairo_status, 103 | ::cairo_destroy> { 104 | public: 105 | explicit Context(Surface& surface) : Ref(cairo_create(surface.pointer())) { } 106 | 107 | inline Context& source(Surface& surface, double x = 0.0, double y = 0.0) { 108 | ::cairo_set_source_surface(pointer(), surface.pointer(), x, y); 109 | return *this; 110 | } 111 | 112 | inline Context& rotate(const Surface& surface, Rotation angle) { 113 | switch (angle) { 114 | case Rotation::ClockWise90: 115 | ::cairo_translate(pointer(), surface.height(), 0); 116 | ::cairo_rotate(pointer(), 90.0 * M_PI / 180.0); 117 | break; 118 | case Rotation::ClockWise180: 119 | ::cairo_translate(pointer(), surface.width(), surface.height()); 120 | ::cairo_rotate(pointer(), 180.0 * M_PI / 180.0); 121 | break; 122 | case Rotation::ClockWise270: 123 | ::cairo_translate(pointer(), 0, surface.width()); 124 | ::cairo_rotate(pointer(), 270.0 * M_PI / 180.0); 125 | break; 126 | default: // No rotation. 127 | break; 128 | } 129 | return *this; 130 | } 131 | 132 | inline Context& paint() { 133 | ::cairo_paint(pointer()); 134 | return *this; 135 | } 136 | }; 137 | 138 | } // namespace cairo 139 | 140 | #endif /* !CAIRO_HH */ 141 | -------------------------------------------------------------------------------- /dyz-shm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #if GRAPHICS_CAIRO 20 | # include "cairo.hh" 21 | # define GRAPHICS_NEEDS_DEVICE_SURFACE 1 22 | namespace gfx = cairo; 23 | #elif GRAPHICS_PIXMAN 24 | # include "pixman.hh" 25 | # define GRAPHICS_NEEDS_DEVICE_SURFACE 1 26 | namespace gfx = pixman; 27 | #elif GRAPHICS_SIMPLE 28 | # include "simplegfx.hh" 29 | # define GRAPHICS_NEEDS_DEVICE_SURFACE 0 30 | namespace gfx = simplegfx; 31 | #else 32 | # error No graphics backend 33 | #endif 34 | 35 | 36 | static struct { 37 | bool debug; 38 | bool suppressOutput; 39 | uint32_t fpsInterval; 40 | const char* pngPath; 41 | } Options = { }; 42 | 43 | #define DEBUG(args) \ 44 | do { \ 45 | if (Options.debug) { g_printerr args ; } \ 46 | } while (0) 47 | 48 | 49 | struct FrameBuffer { 50 | public: 51 | FrameBuffer(const char* devicePath = nullptr) : m_devicePath(devicePath) { 52 | if (!m_devicePath) { 53 | if (auto value = g_getenv("WPE_FBDEV")) { 54 | m_devicePath = value; 55 | } else { 56 | m_devicePath = "/dev/fb0"; 57 | } 58 | } 59 | 60 | do { 61 | m_fd = open(m_devicePath, O_RDWR); 62 | } while (m_fd == -1 && errno == EINTR); 63 | if (m_fd == -1 ) { 64 | markError("open", errno); 65 | return; 66 | } 67 | DEBUG(("Framebuffer '%s' fd: %i\n", m_devicePath, m_fd)); 68 | 69 | if (!updateScreenInfo()) 70 | return; 71 | DEBUG(("Framebuffer '%s' smem_len = %" PRIu32 "\n", 72 | m_devicePath, m_fixInfo.smem_len)); 73 | 74 | int retcode; 75 | do { 76 | retcode = ioctl(m_fd, FBIOBLANK, FB_BLANK_UNBLANK); 77 | } while (retcode < 0 && errno == EINTR); 78 | if (retcode < 0) { 79 | markError("ioctl FBIOBLANK FB_BLANK_UNBLANK", errno); 80 | return; 81 | } 82 | DEBUG(("Framebuffer '%s' unblanked\n", m_devicePath)); 83 | 84 | if (size() > m_fixInfo.smem_len) { 85 | markError("mmap", "size to mmap bigger than framebuffer size"); 86 | return; 87 | } 88 | 89 | m_buffer = mmap(nullptr, size(), PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0); 90 | if (m_buffer == MAP_FAILED || !m_buffer) { 91 | markError("mmap", errno); 92 | return; 93 | } 94 | 95 | if (!createSurface()) { 96 | markError(gfx::name, "Cannot create device surface"); 97 | return; 98 | } 99 | } 100 | 101 | bool updateScreenInfo() { 102 | int retcode; 103 | do { 104 | retcode = ioctl(m_fd, FBIOGET_FSCREENINFO, &m_fixInfo); 105 | } while (retcode < 0 && errno == EINTR); 106 | if (retcode < 0) { 107 | markError("ioctl FBIOGET_FSCREENINFO", errno); 108 | return false; 109 | } 110 | 111 | do { 112 | retcode = ioctl(m_fd, FBIOGET_VSCREENINFO, &m_varInfo); 113 | } while (retcode < 0 && errno == EINTR); 114 | if (retcode < 0) { 115 | markError("ioctl FBIOGET_VSCREENINFO", errno); 116 | return false; 117 | } 118 | return true; 119 | } 120 | 121 | ~FrameBuffer() { 122 | if (m_buffer) { 123 | munmap(m_buffer, size()); 124 | m_buffer = nullptr; 125 | } 126 | 127 | if (m_fd != -1) { 128 | close(m_fd); 129 | m_fd = -1; 130 | } 131 | } 132 | 133 | inline void* data() { return m_buffer; } 134 | inline const void* constData() const { return m_buffer; } 135 | inline uint32_t stride() const { return m_fixInfo.line_length; } 136 | inline uint64_t size() const { return stride() * yres(); } 137 | inline uint32_t xres() const { return m_varInfo.xres; } 138 | inline uint32_t yres() const { return m_varInfo.yres; } 139 | inline uint32_t bpp() const { return m_varInfo.bits_per_pixel; } 140 | inline uint32_t rotation() const { return m_varInfo.rotate; } 141 | 142 | bool setRotation(uint32_t rotation) { 143 | m_varInfo.rotate = rotation; 144 | return applyVarInfo(); 145 | } 146 | 147 | inline bool errored() const { return m_errorCause || m_errorMessage; } 148 | inline const char* errorMessage() const { return m_errorMessage; } 149 | inline const char* errorCause() const { return m_errorCause; } 150 | inline const char* devicePath() const { return m_devicePath; } 151 | 152 | protected: 153 | FrameBuffer(const FrameBuffer&) = delete; // Prevent copying; 154 | void operator=(const FrameBuffer&) = delete; // Prevent assignment. 155 | 156 | void markError(const char* cause, const char* message) { 157 | DEBUG(("Framebuffer error: %s (%s)\n", message, cause)); 158 | m_errorCause = cause; 159 | m_errorMessage = message; 160 | } 161 | inline void markError(const char* cause, int err) { 162 | markError(cause, strerror(err)); 163 | } 164 | 165 | bool createSurface() { 166 | #if GRAPHICS_NEEDS_DEVICE_SURFACE 167 | // FIXME: Un-hardcode the framebuffer device surface format. 168 | m_surface.reset(new gfx::Surface(gfx::format::RGB16_565, 169 | m_buffer, 170 | xres(), 171 | yres(), 172 | stride())); 173 | return !!m_surface; 174 | #else 175 | return true; 176 | #endif 177 | } 178 | 179 | bool applyVarInfo() { 180 | int retcode; 181 | do { 182 | retcode = ioctl(m_fd, FBIOPUT_VSCREENINFO, &m_varInfo); 183 | } while (retcode < 0 && errno == EINTR); 184 | return retcode >= 0; 185 | } 186 | 187 | private: 188 | int m_fd { -1 }; 189 | void* m_buffer { nullptr }; 190 | const char* m_errorMessage { nullptr }; 191 | const char* m_errorCause { nullptr }; 192 | struct fb_var_screeninfo m_varInfo { }; 193 | struct fb_fix_screeninfo m_fixInfo { }; 194 | const char* m_devicePath; 195 | 196 | #if GRAPHICS_NEEDS_DEVICE_SURFACE 197 | std::unique_ptr m_surface { nullptr }; 198 | 199 | public: 200 | inline gfx::Surface& surface() { 201 | g_assert(m_surface != nullptr); 202 | return *m_surface; 203 | } 204 | #endif 205 | }; 206 | 207 | 208 | 209 | struct ViewData { 210 | FrameBuffer& framebuffer; 211 | struct wpe_view_backend_exportable_shm* exportable; 212 | }; 213 | 214 | 215 | static struct wpe_view_backend_exportable_shm_client s_exportableSHMClient = { 216 | // export_buffer 217 | [](void* data, struct wpe_view_backend_exportable_shm_buffer* buffer) 218 | { 219 | DEBUG(("export_buffer() %p\n", buffer)); 220 | DEBUG((" buffer_resource %p buffer %p\n", 221 | buffer->buffer_resource, 222 | buffer->buffer)); 223 | DEBUG((" data %p (%d,%d) stride %d\n", 224 | buffer->data, 225 | buffer->width, 226 | buffer->height, 227 | buffer->stride)); 228 | 229 | auto* viewData = reinterpret_cast(data); 230 | 231 | if (!Options.suppressOutput) { 232 | gfx::Surface image { 233 | gfx::format::ARGB32, 234 | buffer->data, 235 | static_cast(buffer->width), 236 | static_cast(buffer->height), 237 | static_cast(buffer->stride) 238 | }; 239 | 240 | #if GRAPHICS_CAIRO 241 | if (!image) { 242 | g_printerr("Could not create cairo surface for SHM buffer: %s\n", image.statusString()); 243 | return; 244 | } 245 | 246 | if (Options.pngPath) { 247 | char filename[PATH_MAX]; 248 | static int files = 0; 249 | snprintf(filename, PATH_MAX, "%s/dump_%d.png", Options.pngPath, files++); 250 | cairo_surface_write_to_png(image.pointer(), filename); 251 | g_printerr("dump image data to %s\n", filename); 252 | } 253 | 254 | gfx::Context context { viewData->framebuffer.surface() }; 255 | context.rotate(image, gfx::Rotation::CounterClockWise90).source(image).paint(); 256 | 257 | #elif GRAPHICS_PIXMAN 258 | image->setTransform(pixman::Transform::rotate(90)); 259 | ::pixman_image_composite(PIXMAN_OP_SRC, 260 | image.pointer(), 261 | nullptr, 262 | viewData->framebuffer.surface().pointer(), 263 | 0, 0, 264 | 0, 0, 265 | 0, 0, 266 | image.width(), 267 | image.height()); 268 | #elif GRAPHICS_SIMPLE 269 | auto fbLines = viewData->framebuffer.yres(); 270 | auto fbColumns = viewData->framebuffer.xres(); 271 | auto fbStride = viewData->framebuffer.stride(); 272 | uint8_t* fbData = reinterpret_cast(viewData->framebuffer.data()); 273 | uint8_t* bufData = reinterpret_cast(buffer->data); 274 | 275 | for (auto fbY = 0; fbY < fbLines; fbY++) { 276 | uint16_t *fbLineData = reinterpret_cast(fbData + fbStride * fbY); 277 | for (auto fbX = 0; fbX < fbColumns; fbX++) { 278 | fbLineData[fbX] = simplegfx::Argb32toRgb565_v0( 279 | reinterpret_cast(reinterpret_cast(buffer->data) + buffer->stride * fbX)[fbLines - fbY]); 280 | } 281 | } 282 | #endif 283 | } 284 | 285 | wpe_view_backend_exportable_shm_dispatch_frame_complete(viewData->exportable); 286 | wpe_view_backend_exportable_shm_dispatch_release_buffer(viewData->exportable, buffer); 287 | 288 | if (Options.fpsInterval > 0) { 289 | static uint32_t sFrameCount = 0; 290 | static gint64 sLastTime = g_get_monotonic_time(); 291 | ++sFrameCount; 292 | gint64 time = g_get_monotonic_time(); 293 | if (time - sLastTime >= Options.fpsInterval * G_USEC_PER_SEC) { 294 | double elapsedSeconds = static_cast(time - sLastTime) / G_USEC_PER_SEC; 295 | g_printerr("[fps] %4.2f (%" PRIu32 " frames in %.2fs)\n", 296 | sFrameCount / elapsedSeconds, sFrameCount, elapsedSeconds); 297 | sFrameCount = 0; 298 | sLastTime = time; 299 | } 300 | } 301 | }, 302 | }; 303 | 304 | 305 | // These are not declared in the headers, but they do exist in libWPEWebKit. 306 | extern "C" { 307 | void WKPreferencesSetUniversalAccessFromFileURLsAllowed(WKPreferencesRef, bool); 308 | } 309 | 310 | 311 | static WKPageNavigationClientV0 NavigationClient = { 312 | { 0, nullptr }, 313 | // decidePolicyForNavigationAction 314 | [](WKPageRef, WKNavigationActionRef, WKFramePolicyListenerRef listener, WKTypeRef, const void*) { 315 | WKFramePolicyListenerUse(listener); 316 | }, 317 | // decidePolicyForNavigationResponse 318 | [](WKPageRef, WKNavigationResponseRef, WKFramePolicyListenerRef listener, WKTypeRef, const void*) { 319 | WKFramePolicyListenerUse(listener); 320 | }, 321 | nullptr, // decidePolicyForPluginLoad 322 | nullptr, // didStartProvisionalNavigation 323 | nullptr, // didReceiveServerRedirectForProvisionalNavigation 324 | nullptr, // didFailProvisionalNavigation 325 | nullptr, // didCommitNavigation 326 | nullptr, // didFinishNavigation 327 | // didFailNavigation 328 | [](WKPageRef, WKNavigationRef navigation, WKErrorRef error, WKTypeRef userData, const void*) { 329 | g_warning("Navigation failed."); 330 | }, 331 | nullptr, // didFailProvisionalLoadInSubframe 332 | // didFinishDocumentLoad 333 | [](WKPageRef page, WKNavigationRef, WKTypeRef, const void*) { 334 | g_message("Document load finished."); 335 | }, 336 | nullptr, // didSameDocumentNavigation 337 | nullptr, // renderingProgressDidChange 338 | nullptr, // canAuthenticateAgainstProtectionSpace 339 | nullptr, // didReceiveAuthenticationChallenge 340 | // webProcessDidCrash 341 | [](WKPageRef page, const void*) { 342 | g_warning("WebProcess crashed!"); 343 | if (auto value = g_getenv("WPE_DYZSHM_NO_RELOAD_ON_CRASH")) { 344 | if (strcmp(value, "0") != 0) 345 | g_warning("Reloading. Set WPE_DYZSHM_NO_RELOAD_ON_CRASH=1 to disable."); 346 | WKPageReload(page); 347 | } 348 | }, 349 | nullptr, // copyWebCryptoMasterKey 350 | nullptr, // didBeginNavigationGesture 351 | nullptr, // willEndNavigationGesture 352 | nullptr, // didEndNavigationGesture 353 | nullptr, // didRemoveNavigationGestureSnapshot 354 | }; 355 | 356 | 357 | static WKURLRef 358 | getFileURLDirectory(const char* url) 359 | { 360 | char* dirPath = g_path_get_dirname(url + 7); // strlen("file://") -> 7 361 | g_debug("Path for %s -> %s\n", url, dirPath); 362 | auto directoryString = WKURLCreateWithUTF8CString(dirPath); 363 | g_free (dirPath); 364 | return directoryString; 365 | } 366 | 367 | 368 | int main(int argc, char *argv[]) 369 | { 370 | if (auto value = g_getenv("WPE_DYZSHM_DEBUG")) { 371 | Options.debug = strcmp(value, "0") != 0; 372 | } 373 | if (auto value = g_getenv("WPE_DYZSHM_NO_OUTPUT")) { 374 | Options.suppressOutput = strcmp(value, "0") != 0; 375 | } 376 | if (auto value = g_getenv("WPE_DUMP_PNG_PATH")) { 377 | Options.pngPath = value; 378 | } 379 | if (auto value = g_getenv("WPE_DYZSHM_SHOW_FPS")) { 380 | char *end = nullptr; 381 | auto valueAsUlong = std::strtoul(value, &end, 10); 382 | if (*end != '\0' || (valueAsUlong == ULONG_MAX && errno == ERANGE)) { 383 | g_printerr("Cannot convert '%s' to an unsigned integer\n", value); 384 | return EXIT_FAILURE; 385 | } else if (valueAsUlong > UINT32_MAX) { 386 | g_printerr("Value '%s' is out of range, try a smaller value\n", value); 387 | return EXIT_FAILURE; 388 | } 389 | Options.fpsInterval = valueAsUlong; 390 | } 391 | 392 | g_debug("Dyz-SHM with %s graphics (built %s)", gfx::name, __DATE__); 393 | g_debug("FPS reporting interval: %lu", Options.fpsInterval); 394 | 395 | FrameBuffer framebuffer; 396 | if (framebuffer.errored()) { 397 | g_printerr("Cannot initialize framebuffer: %s (%s)\n", 398 | framebuffer.errorMessage(), 399 | framebuffer.errorCause()); 400 | return EXIT_FAILURE; 401 | } 402 | 403 | g_debug("Framebuffer '%s' @ %" PRIu32 "x%" PRIu32 " %" PRIu32 "bpp" 404 | " (%" PRIu32 ", stride %" PRIu32 ", size %" PRIu64 ", %p)\n", 405 | framebuffer.devicePath(), 406 | framebuffer.xres(), 407 | framebuffer.yres(), 408 | framebuffer.bpp(), 409 | framebuffer.rotation(), 410 | framebuffer.stride(), 411 | framebuffer.size(), 412 | framebuffer.constData()); 413 | 414 | GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE); 415 | 416 | auto context = WKContextCreate(); 417 | auto pageConfiguration = WKPageConfigurationCreate(); 418 | 419 | { 420 | auto preferences = WKPreferencesCreate(); 421 | WKPreferencesSetPluginsEnabled(preferences, false); 422 | WKPreferencesSetJavaEnabled(preferences, false); 423 | WKPreferencesSetTextAreasAreResizable(preferences, false); 424 | WKPreferencesSetBackspaceKeyNavigationEnabled(preferences, false); 425 | WKPreferencesSetFullScreenEnabled(preferences, true); 426 | WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true); 427 | WKPreferencesSetDefaultFontSize(preferences, 9); 428 | WKPreferencesSetDefaultFixedFontSize(preferences, 9); 429 | if (auto value = g_getenv("WPE_DYZSHM_CONSOLE_LOG")) 430 | WKPreferencesSetLogsPageMessagesToSystemConsoleEnabled(preferences, strcmp(value, "0") != 0); 431 | 432 | auto pageGroupIdentifier = WKStringCreateWithUTF8CString("WPEPageGroup"); 433 | auto pageGroup = WKPageGroupCreateWithIdentifier(pageGroupIdentifier); 434 | 435 | WKPageGroupSetPreferences(pageGroup, preferences); 436 | 437 | WKPageConfigurationSetContext(pageConfiguration, context); 438 | WKPageConfigurationSetPageGroup(pageConfiguration, pageGroup); 439 | 440 | WKRelease(pageGroup); 441 | WKRelease(pageGroupIdentifier); 442 | WKRelease(preferences); 443 | } 444 | 445 | ViewData viewData { framebuffer, nullptr }; 446 | auto* backendExportable = wpe_view_backend_exportable_shm_create(&s_exportableSHMClient, &viewData); 447 | viewData.exportable = backendExportable; 448 | 449 | auto* backend = wpe_view_backend_exportable_shm_get_view_backend(backendExportable); 450 | auto view = WKViewCreateWithViewBackend(backend, pageConfiguration); 451 | auto page = WKViewGetPage(view); 452 | 453 | WKPageSetPageNavigationClient(page, &NavigationClient.base); 454 | 455 | { 456 | const auto url = (argc > 1) ? argv[1] : "http://igalia.com"; 457 | auto isFileURL = strncmp(url, "file://", 7) == 0; 458 | auto shellURL = WKURLCreateWithUTF8CString(url); 459 | if (isFileURL) { 460 | auto dirPath = getFileURLDirectory(url); 461 | g_debug("Loading file URL: %s\n", url); 462 | WKPageLoadFile(page, shellURL, dirPath); 463 | WKRelease(dirPath); 464 | } else { 465 | g_debug("Loading URL: %s\n", url); 466 | WKPageLoadURL(page, shellURL); 467 | } 468 | WKRelease(shellURL); 469 | } 470 | 471 | g_main_loop_run(loop); 472 | 473 | WKRelease(view); 474 | 475 | wpe_view_backend_exportable_shm_destroy(backendExportable); 476 | 477 | WKRelease(pageConfiguration); 478 | WKRelease(context); 479 | 480 | g_main_loop_unref(loop); 481 | return EXIT_SUCCESS; 482 | } 483 | --------------------------------------------------------------------------------