DISTRHO Plugin Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2067 lines
75KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #if !defined(DISTRHO_WEB_VIEW_HPP_INCLUDED) && !defined(DGL_WEB_VIEW_HPP_INCLUDED)
  17. # error bad include
  18. #endif
  19. #if !defined(WEB_VIEW_DISTRHO_NAMESPACE) && !defined(WEB_VIEW_DGL_NAMESPACE)
  20. # error bad usage
  21. #endif
  22. // #undef Bool
  23. // #undef CursorShape
  24. // #undef Expose
  25. // #undef FocusIn
  26. // #undef FocusOut
  27. // #undef FontChange
  28. // #undef KeyPress
  29. // #undef KeyRelease
  30. // #undef None
  31. // #undef Status
  32. // #define QT_NO_VERSION_TAGGING
  33. // #include <QGuiApplication>
  34. // #include <QEvent>
  35. // #include <QtCore/QChar>
  36. // #include <QtCore/QPoint>
  37. // #include <QtCore/QSize>
  38. // #undef signals
  39. // #include <gtk/gtk.h>
  40. // #include <gtk/gtkx.h>
  41. // #include <webkit2/webkit2.h>
  42. #ifdef DISTRHO_OS_MAC
  43. # define WEB_VIEW_USING_MACOS_WEBKIT 1
  44. #else
  45. # define WEB_VIEW_USING_MACOS_WEBKIT 0
  46. #endif
  47. #ifdef DISTRHO_OS_WINDOWS
  48. # define WEB_VIEW_USING_CHOC 1
  49. #else
  50. # define WEB_VIEW_USING_CHOC 0
  51. #endif
  52. #if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX)
  53. # define WEB_VIEW_USING_X11_IPC 1
  54. #else
  55. # define WEB_VIEW_USING_X11_IPC 0
  56. #endif
  57. #if WEB_VIEW_USING_CHOC
  58. # include "WebViewWin32.hpp"
  59. # include "String.hpp"
  60. #elif WEB_VIEW_USING_MACOS_WEBKIT
  61. # include <Cocoa/Cocoa.h>
  62. # include <WebKit/WebKit.h>
  63. #elif WEB_VIEW_USING_X11_IPC
  64. # include "ChildProcess.hpp"
  65. # include "RingBuffer.hpp"
  66. # include "String.hpp"
  67. # include <clocale>
  68. # include <cstdio>
  69. # include <dlfcn.h>
  70. # include <fcntl.h>
  71. # include <pthread.h>
  72. # include <unistd.h>
  73. # include <sys/mman.h>
  74. # include <X11/Xlib.h>
  75. # ifdef DISTRHO_PROPER_CPP11_SUPPORT
  76. # include <functional>
  77. # endif
  78. # ifdef __linux__
  79. # include <syscall.h>
  80. # include <linux/futex.h>
  81. # include <linux/limits.h>
  82. # else
  83. # include <semaphore.h>
  84. # endif
  85. #endif
  86. // -----------------------------------------------------------------------------------------------------------
  87. #if WEB_VIEW_USING_MACOS_WEBKIT
  88. #define MACRO_NAME2(a, b, c) a ## b ## c
  89. #define MACRO_NAME(a, b, c) MACRO_NAME2(a, b, c)
  90. #define WEB_VIEW_DELEGATE_CLASS_NAME \
  91. MACRO_NAME(WebViewDelegate_, _, WEB_VIEW_NAMESPACE)
  92. @interface WEB_VIEW_DELEGATE_CLASS_NAME : NSObject<WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate>
  93. @end
  94. @implementation WEB_VIEW_DELEGATE_CLASS_NAME {
  95. @public
  96. WEB_VIEW_NAMESPACE::WebViewMessageCallback callback;
  97. void* callbackPtr;
  98. bool loaded;
  99. }
  100. - (void)webView:(WKWebView *)webview
  101. didFinishNavigation:(WKNavigation*)navigation
  102. {
  103. d_stdout("page loaded");
  104. loaded = true;
  105. }
  106. - (void)webView:(WKWebView*)webview
  107. runJavaScriptAlertPanelWithMessage:(NSString*)message
  108. initiatedByFrame:(WKFrameInfo*)frame
  109. completionHandler:(void (^)(void))completionHandler
  110. {
  111. NSAlert* const alert = [[NSAlert alloc] init];
  112. [alert addButtonWithTitle:@"OK"];
  113. [alert setInformativeText:message];
  114. [alert setMessageText:@"Alert"];
  115. dispatch_async(dispatch_get_main_queue(), ^
  116. {
  117. [alert beginSheetModalForWindow:[webview window]
  118. completionHandler:^(NSModalResponse)
  119. {
  120. completionHandler();
  121. [alert release];
  122. }];
  123. });
  124. }
  125. - (void)webView:(WKWebView*)webview
  126. runJavaScriptConfirmPanelWithMessage:(NSString*)message
  127. initiatedByFrame:(WKFrameInfo*)frame
  128. completionHandler:(void (^)(BOOL))completionHandler
  129. {
  130. NSAlert* const alert = [[NSAlert alloc] init];
  131. [alert addButtonWithTitle:@"OK"];
  132. [alert addButtonWithTitle:@"Cancel"];
  133. [alert setInformativeText:message];
  134. [alert setMessageText:@"Confirm"];
  135. dispatch_async(dispatch_get_main_queue(), ^
  136. {
  137. [alert beginSheetModalForWindow:[webview window]
  138. completionHandler:^(NSModalResponse result)
  139. {
  140. completionHandler(result == NSAlertFirstButtonReturn);
  141. [alert release];
  142. }];
  143. });
  144. }
  145. - (void)webView:(WKWebView*)webview
  146. runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt
  147. defaultText:(NSString*)defaultText
  148. initiatedByFrame:(WKFrameInfo*)frame
  149. completionHandler:(void (^)(NSString*))completionHandler
  150. {
  151. NSTextField* const input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 250, 30)];
  152. [input setStringValue:defaultText];
  153. NSAlert* const alert = [[NSAlert alloc] init];
  154. [alert setAccessoryView:input];
  155. [alert addButtonWithTitle:@"OK"];
  156. [alert addButtonWithTitle:@"Cancel"];
  157. [alert setInformativeText:prompt];
  158. [alert setMessageText: @"Prompt"];
  159. dispatch_async(dispatch_get_main_queue(), ^
  160. {
  161. [alert beginSheetModalForWindow:[webview window]
  162. completionHandler:^(NSModalResponse result)
  163. {
  164. [input validateEditing];
  165. completionHandler(result == NSAlertFirstButtonReturn ? [input stringValue] : nil);
  166. [alert release];
  167. }];
  168. });
  169. }
  170. - (void)webView:(WKWebView*)webview
  171. runOpenPanelWithParameters:(WKOpenPanelParameters*)params
  172. initiatedByFrame:(WKFrameInfo*)frame
  173. completionHandler:(void (^)(NSArray<NSURL*>*))completionHandler
  174. {
  175. NSOpenPanel* const panel = [[NSOpenPanel alloc] init];
  176. [panel setAllowsMultipleSelection:[params allowsMultipleSelection]];
  177. // [panel setAllowedFileTypes:(NSArray<NSString*>*)[params _allowedFileExtensions]];
  178. [panel setCanChooseDirectories:[params allowsDirectories]];
  179. [panel setCanChooseFiles:![params allowsDirectories]];
  180. dispatch_async(dispatch_get_main_queue(), ^
  181. {
  182. [panel beginSheetModalForWindow:[webview window]
  183. completionHandler:^(NSModalResponse result)
  184. {
  185. completionHandler(result == NSModalResponseOK ? [panel URLs] : nil);
  186. [panel release];
  187. }];
  188. });
  189. }
  190. - (void)userContentController:(WKUserContentController*)userContentController
  191. didReceiveScriptMessage:(WKScriptMessage*)message
  192. {
  193. NSString* const nsstring = static_cast<NSString*>([message body]);
  194. char* const string = strdup([nsstring UTF8String]);
  195. d_debug("JS call received '%s' %p", string, callback);
  196. if (callback != nullptr)
  197. callback(callbackPtr, string);
  198. std::free(string);
  199. }
  200. @end
  201. #endif // WEB_VIEW_USING_MACOS_WEBKIT
  202. // -----------------------------------------------------------------------------------------------------------
  203. #ifdef WEB_VIEW_DGL_NAMESPACE
  204. START_NAMESPACE_DGL
  205. using DISTRHO_NAMESPACE::String;
  206. #else
  207. START_NAMESPACE_DISTRHO
  208. #endif
  209. // -----------------------------------------------------------------------------------------------------------
  210. #if WEB_VIEW_USING_X11_IPC
  211. #ifdef __linux__
  212. typedef int32_t ipc_sem_t;
  213. #else
  214. typedef sem_t ipc_sem_t;
  215. #endif
  216. enum WebViewMessageType {
  217. kWebViewMessageNull,
  218. kWebViewMessageInitData,
  219. kWebViewMessageEvaluateJS,
  220. kWebViewMessageCallback,
  221. kWebViewMessageReload
  222. };
  223. struct WebViewSharedBuffer {
  224. static constexpr const uint32_t size = 0x100000;
  225. ipc_sem_t sem;
  226. uint32_t head, tail, wrtn;
  227. bool invalidateCommit;
  228. uint8_t buf[size];
  229. };
  230. struct WebViewRingBuffer {
  231. WebViewSharedBuffer server;
  232. WebViewSharedBuffer client;
  233. bool valid;
  234. };
  235. static void webview_wake(ipc_sem_t* const sem)
  236. {
  237. #ifdef __linux__
  238. if (__sync_bool_compare_and_swap(sem, 0, 1))
  239. syscall(SYS_futex, sem, FUTEX_WAKE, 1, nullptr, nullptr, 0);
  240. #else
  241. sem_post(sem);
  242. #endif
  243. }
  244. static bool webview_timedwait(ipc_sem_t* const sem)
  245. {
  246. #ifdef __linux__
  247. const struct timespec timeout = { 1, 0 };
  248. for (;;)
  249. {
  250. if (__sync_bool_compare_and_swap(sem, 1, 0))
  251. return true;
  252. if (syscall(SYS_futex, sem, FUTEX_WAIT, 0, &timeout, nullptr, 0) != 0)
  253. if (errno != EAGAIN && errno != EINTR)
  254. return false;
  255. }
  256. #else
  257. struct timespec timeout;
  258. if (clock_gettime(CLOCK_REALTIME, &timeout) != 0)
  259. return false;
  260. timeout.tv_sec += 1;
  261. for (int r;;)
  262. {
  263. r = sem_timedwait(sem, &timeout);
  264. if (r < 0)
  265. r = errno;
  266. if (r == EINTR)
  267. continue;
  268. return r == 0;
  269. }
  270. #endif
  271. }
  272. static void getFilenameFromFunctionPtr(char filename[PATH_MAX], const void* const ptr)
  273. {
  274. Dl_info info = {};
  275. dladdr(ptr, &info);
  276. if (info.dli_fname[0] == '.' && info.dli_fname[1] != '.')
  277. {
  278. if (getcwd(filename, PATH_MAX - 1) == nullptr)
  279. filename[0] = '\0';
  280. std::strncat(filename, info.dli_fname + 1, PATH_MAX - 1);
  281. }
  282. else if (info.dli_fname[0] != '/')
  283. {
  284. if (getcwd(filename, PATH_MAX - 1) == nullptr)
  285. filename[0] = '\0';
  286. else
  287. std::strncat(filename, "/", PATH_MAX - 1);
  288. std::strncat(filename, info.dli_fname, PATH_MAX - 1);
  289. }
  290. else
  291. {
  292. std::strncpy(filename, info.dli_fname, PATH_MAX - 1);
  293. }
  294. }
  295. #endif // WEB_VIEW_USING_X11_IPC
  296. // -----------------------------------------------------------------------------------------------------------
  297. struct WebViewData {
  298. #if WEB_VIEW_USING_CHOC
  299. WebView* webview;
  300. String url;
  301. #elif WEB_VIEW_USING_MACOS_WEBKIT
  302. NSView* view;
  303. WKWebView* webview;
  304. NSURLRequest* urlreq;
  305. WEB_VIEW_DELEGATE_CLASS_NAME* delegate;
  306. #elif WEB_VIEW_USING_X11_IPC
  307. int shmfd;
  308. char shmname[128];
  309. WebViewRingBuffer* shmptr;
  310. WebViewMessageCallback callback;
  311. void* callbackPtr;
  312. ChildProcess p;
  313. RingBufferControl<WebViewSharedBuffer> rbctrl, rbctrl2;
  314. ::Display* display;
  315. ::Window childWindow;
  316. ::Window ourWindow;
  317. #endif
  318. WebViewData();
  319. DISTRHO_DECLARE_NON_COPYABLE(WebViewData);
  320. };
  321. WebViewData::WebViewData()
  322. #if WEB_VIEW_USING_CHOC
  323. : webview(nullptr),
  324. url()
  325. #elif WEB_VIEW_USING_MACOS_WEBKIT
  326. : view(nullptr),
  327. webview(nullptr),
  328. urlreq(nullptr),
  329. delegate(nullptr)
  330. #elif WEB_VIEW_USING_X11_IPC
  331. : shmfd(0),
  332. shmname(),
  333. shmptr(nullptr),
  334. callback(nullptr),
  335. callbackPtr(nullptr),
  336. p(),
  337. rbctrl(),
  338. rbctrl2(),
  339. display(nullptr),
  340. childWindow(0),
  341. ourWindow(0)
  342. #endif
  343. {
  344. #if WEB_VIEW_USING_X11_IPC
  345. std::memset(shmname, 0, sizeof(shmname));
  346. #endif
  347. }
  348. // -----------------------------------------------------------------------------------------------------------
  349. WebViewHandle webViewCreate(const char* const url,
  350. const uintptr_t windowId,
  351. const uint initialWidth,
  352. const uint initialHeight,
  353. const double scaleFactor,
  354. const WebViewOptions& options)
  355. {
  356. #if WEB_VIEW_USING_CHOC
  357. WebView* const webview = webview_choc_create(options);
  358. if (webview == nullptr)
  359. return nullptr;
  360. const HWND hwnd = static_cast<HWND>(webview_choc_handle(webview));
  361. LONG_PTR flags = GetWindowLongPtr(hwnd, -16);
  362. flags = (flags & ~WS_POPUP) | WS_CHILD;
  363. SetWindowLongPtr(hwnd, -16, flags);
  364. SetParent(hwnd, reinterpret_cast<HWND>(windowId));
  365. SetWindowPos(hwnd, nullptr,
  366. options.offset.x,
  367. options.offset.y,
  368. initialWidth - options.offset.x,
  369. initialHeight - options.offset.y,
  370. SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
  371. ShowWindow(hwnd, SW_SHOW);
  372. WebViewData* const whandle = new WebViewData;
  373. whandle->webview = webview;
  374. whandle->url = url;
  375. webview_choc_navigate(webview, url);
  376. return whandle;
  377. #elif WEB_VIEW_USING_MACOS_WEBKIT
  378. NSView* const view = reinterpret_cast<NSView*>(windowId);
  379. WKPreferences* const prefs = [[WKPreferences alloc] init];
  380. [prefs setValue:@YES forKey:@"javaScriptCanAccessClipboard"];
  381. [prefs setValue:@YES forKey:@"DOMPasteAllowed"];
  382. // if (debug)
  383. {
  384. [prefs setValue:@YES forKey:@"developerExtrasEnabled"];
  385. // TODO enable_write_console_messages_to_stdout
  386. }
  387. WKWebViewConfiguration* const config = [[WKWebViewConfiguration alloc] init];
  388. config.limitsNavigationsToAppBoundDomains = false;
  389. config.preferences = prefs;
  390. const CGRect rect = CGRectMake(options.offset.x / scaleFactor,
  391. options.offset.y / scaleFactor,
  392. initialWidth / scaleFactor,
  393. initialHeight / scaleFactor);
  394. WKWebView* const webview = [[WKWebView alloc] initWithFrame:rect
  395. configuration:config];
  396. [webview setHidden:YES];
  397. [view addSubview:webview];
  398. // TODO webkit_web_view_set_background_color
  399. WEB_VIEW_DELEGATE_CLASS_NAME* const delegate = [[WEB_VIEW_DELEGATE_CLASS_NAME alloc] init];
  400. delegate->callback = options.callback;
  401. delegate->callbackPtr = options.callbackPtr;
  402. delegate->loaded = false;
  403. if (WKUserContentController* const controller = [config userContentController])
  404. {
  405. [controller addScriptMessageHandler:delegate name:@"external"];
  406. WKUserScript* const mscript = [[WKUserScript alloc]
  407. initWithSource:(options.callback != nullptr
  408. ? @"function postMessage(m){window.webkit.messageHandlers.external.postMessage(m)}"
  409. : @"function postMessage(m){}")
  410. injectionTime:WKUserScriptInjectionTimeAtDocumentStart
  411. forMainFrameOnly:true
  412. ];
  413. [controller addUserScript:mscript];
  414. [mscript release];
  415. if (options.initialJS != nullptr)
  416. {
  417. NSString* const nsInitialJS = [[NSString alloc] initWithBytes:options.initialJS
  418. length:std::strlen(options.initialJS)
  419. encoding:NSUTF8StringEncoding];
  420. WKUserScript* const script = [[WKUserScript alloc] initWithSource:nsInitialJS
  421. injectionTime:WKUserScriptInjectionTimeAtDocumentStart
  422. forMainFrameOnly:true];
  423. [controller addUserScript:script];
  424. [script release];
  425. [nsInitialJS release];
  426. }
  427. }
  428. [webview setNavigationDelegate:delegate];
  429. [webview setUIDelegate:delegate];
  430. NSString* const nsurl = [[NSString alloc] initWithBytes:url
  431. length:std::strlen(url)
  432. encoding:NSUTF8StringEncoding];
  433. NSURLRequest* const urlreq = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: nsurl]];
  434. d_stdout("url is '%s'", url);
  435. if (std::strncmp(url, "file://", 7) == 0)
  436. {
  437. const char* const lastsep = std::strrchr(url + 7, '/');
  438. NSString* const urlpath = [[NSString alloc] initWithBytes:url
  439. length:(lastsep - url)
  440. encoding:NSUTF8StringEncoding];
  441. [webview loadFileRequest:urlreq
  442. allowingReadAccessToURL:[NSURL URLWithString:urlpath]];
  443. [urlpath release];
  444. }
  445. else
  446. {
  447. [webview loadRequest:urlreq];
  448. }
  449. d_stdout("waiting for load");
  450. if (! delegate->loaded)
  451. {
  452. NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc] init];
  453. NSDate* const date = [NSDate dateWithTimeIntervalSinceNow:0.05];
  454. NSEvent* event;
  455. while (! delegate->loaded)
  456. {
  457. event = [NSApp
  458. #ifdef __MAC_10_12
  459. nextEventMatchingMask:NSEventMaskAny
  460. #else
  461. nextEventMatchingMask:NSAnyEventMask
  462. #endif
  463. untilDate:date
  464. inMode:NSDefaultRunLoopMode
  465. dequeue:YES];
  466. if (event == nil)
  467. break;
  468. [NSApp sendEvent: event];
  469. }
  470. [pool release];
  471. }
  472. d_stdout("waiting done");
  473. [webview setHidden:NO];
  474. [nsurl release];
  475. [config release];
  476. [prefs release];
  477. WebViewData* const handle = new WebViewData;
  478. handle->view = view;
  479. handle->webview = webview;
  480. handle->urlreq = urlreq;
  481. handle->delegate = delegate;
  482. return handle;
  483. #elif WEB_VIEW_USING_X11_IPC
  484. // get startup paths
  485. char ldlinux[PATH_MAX] = {};
  486. getFilenameFromFunctionPtr(ldlinux, dlsym(nullptr, "_rtld_global"));
  487. char filename[PATH_MAX] = {};
  488. getFilenameFromFunctionPtr(filename, reinterpret_cast<const void*>(webViewCreate));
  489. d_stdout("ld-linux is '%s'", ldlinux);
  490. d_stdout("filename is '%s'", filename);
  491. // setup shared memory
  492. int shmfd;
  493. char shmname[128];
  494. void* shmptr;
  495. for (int i = 0; i < 9999; ++i)
  496. {
  497. snprintf(shmname, sizeof(shmname) - 1, "/dpf-webview-%d", i + 1);
  498. shmfd = shm_open(shmname, O_CREAT|O_EXCL|O_RDWR, 0666);
  499. if (shmfd < 0)
  500. continue;
  501. if (ftruncate(shmfd, sizeof(WebViewRingBuffer)) != 0)
  502. {
  503. close(shmfd);
  504. shm_unlink(shmname);
  505. continue;
  506. }
  507. break;
  508. }
  509. if (shmfd < 0)
  510. {
  511. d_stderr("shm_open failed: %s", strerror(errno));
  512. return nullptr;
  513. }
  514. shmptr = mmap(nullptr, sizeof(WebViewRingBuffer), PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0);
  515. if (shmptr == nullptr || shmptr == MAP_FAILED)
  516. {
  517. d_stderr("mmap failed: %s", strerror(errno));
  518. close(shmfd);
  519. shm_unlink(shmname);
  520. return nullptr;
  521. }
  522. #ifndef __linux__
  523. sem_init(&handle->shmptr->client.sem, 1, 0);
  524. sem_init(&handle->shmptr->server.sem, 1, 0);
  525. #endif
  526. ::Display* const display = XOpenDisplay(nullptr);
  527. DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, nullptr);
  528. // set up custom child environment
  529. uint envsize = 0;
  530. while (environ[envsize] != nullptr)
  531. ++envsize;
  532. char** const envp = new char*[envsize + 5];
  533. {
  534. uint e = 0;
  535. for (uint i = 0; i < envsize; ++i)
  536. {
  537. if (std::strncmp(environ[i], "LD_PRELOAD=", 11) == 0)
  538. continue;
  539. if (std::strncmp(environ[i], "LD_LIBRARY_PATH=", 16) == 0)
  540. continue;
  541. envp[e++] = strdup(environ[i]);
  542. }
  543. envp[e++] = strdup("LANG=en_US.UTF-8");
  544. envp[e++] = ("DPF_WEB_VIEW_SCALE_FACTOR=" + String(scaleFactor)).getAndReleaseBuffer();
  545. envp[e++] = ("DPF_WEB_VIEW_WIN_ID=" +String(windowId)).getAndReleaseBuffer();
  546. for (uint i = e; i < envsize + 5; ++i)
  547. envp[e++] = nullptr;
  548. }
  549. WebViewData* const handle = new WebViewData;
  550. handle->callback = options.callback;
  551. handle->callbackPtr = options.callbackPtr;
  552. handle->shmfd = shmfd;
  553. handle->shmptr = static_cast<WebViewRingBuffer*>(shmptr);
  554. handle->display = display;
  555. handle->ourWindow = windowId;
  556. std::memcpy(handle->shmname, shmname, sizeof(shmname));
  557. handle->shmptr->valid = true;
  558. handle->rbctrl.setRingBuffer(&handle->shmptr->client, false);
  559. handle->rbctrl.flush();
  560. handle->rbctrl2.setRingBuffer(&handle->shmptr->server, false);
  561. handle->rbctrl2.flush();
  562. const char* const args[] = { ldlinux, filename, "dpf-ld-linux-webview", shmname, nullptr };
  563. handle->p.start(args, envp);
  564. for (uint i = 0; envp[i] != nullptr; ++i)
  565. std::free(envp[i]);
  566. delete[] envp;
  567. const size_t urllen = std::strlen(url) + 1;
  568. const size_t initjslen = options.initialJS != nullptr ? std::strlen(options.initialJS) + 1 : 0;
  569. handle->rbctrl.writeUInt(kWebViewMessageInitData) &&
  570. handle->rbctrl.writeULong(windowId) &&
  571. handle->rbctrl.writeUInt(initialWidth) &&
  572. handle->rbctrl.writeUInt(initialHeight) &&
  573. handle->rbctrl.writeDouble(scaleFactor) &&
  574. handle->rbctrl.writeInt(options.offset.x) &&
  575. handle->rbctrl.writeInt(options.offset.y) &&
  576. handle->rbctrl.writeUInt(urllen) &&
  577. handle->rbctrl.writeCustomData(url, urllen) &&
  578. handle->rbctrl.writeUInt(initjslen) &&
  579. initjslen != 0 &&
  580. handle->rbctrl.writeCustomData(options.initialJS, initjslen);
  581. handle->rbctrl.commitWrite();
  582. webview_wake(&handle->shmptr->client.sem);
  583. for (int i = 0; i < 5 && handle->p.isRunning(); ++i)
  584. {
  585. if (webview_timedwait(&handle->shmptr->server.sem))
  586. return handle;
  587. }
  588. d_stderr("webview client side failed to start");
  589. webViewDestroy(handle);
  590. return nullptr;
  591. #endif
  592. // maybe unused
  593. (void)windowId;
  594. (void)initialWidth;
  595. (void)initialHeight;
  596. (void)scaleFactor;
  597. (void)options;
  598. return nullptr;
  599. }
  600. void webViewDestroy(const WebViewHandle handle)
  601. {
  602. #if WEB_VIEW_USING_CHOC
  603. webview_choc_destroy(handle->webview);
  604. #elif WEB_VIEW_USING_MACOS_WEBKIT
  605. [handle->webview setHidden:YES];
  606. [handle->webview removeFromSuperview];
  607. [handle->urlreq release];
  608. // [handle->delegate release];
  609. #elif WEB_VIEW_USING_X11_IPC
  610. #ifndef __linux__
  611. sem_destroy(&handle->shmptr->client.sem);
  612. sem_destroy(&handle->shmptr->server.sem);
  613. #endif
  614. munmap(handle->shmptr, sizeof(WebViewRingBuffer));
  615. close(handle->shmfd);
  616. shm_unlink(handle->shmname);
  617. XCloseDisplay(handle->display);
  618. #endif
  619. delete handle;
  620. }
  621. void webViewIdle(const WebViewHandle handle)
  622. {
  623. #if WEB_VIEW_USING_X11_IPC
  624. uint32_t size = 0;
  625. void* buffer = nullptr;
  626. while (handle->rbctrl2.isDataAvailableForReading())
  627. {
  628. switch (handle->rbctrl2.readUInt())
  629. {
  630. case kWebViewMessageCallback:
  631. if (const uint32_t len = handle->rbctrl2.readUInt())
  632. {
  633. if (len > size)
  634. {
  635. size = len;
  636. buffer = std::realloc(buffer, len);
  637. if (buffer == nullptr)
  638. {
  639. d_stderr("server out of memory, abort!");
  640. handle->rbctrl2.flush();
  641. return;
  642. }
  643. }
  644. if (handle->rbctrl2.readCustomData(buffer, len))
  645. {
  646. d_debug("server kWebViewMessageCallback -> '%s'", static_cast<char*>(buffer));
  647. if (handle->callback != nullptr)
  648. handle->callback(handle->callbackPtr, static_cast<char*>(buffer));
  649. continue;
  650. }
  651. }
  652. break;
  653. }
  654. d_stderr("server ringbuffer data race, abort!");
  655. handle->rbctrl2.flush();
  656. break;
  657. }
  658. std::free(buffer);
  659. #else
  660. // unused
  661. (void)handle;
  662. #endif
  663. }
  664. void webViewEvaluateJS(const WebViewHandle handle, const char* const js)
  665. {
  666. #if WEB_VIEW_USING_CHOC
  667. webview_choc_eval(handle->webview, js);
  668. #elif WEB_VIEW_USING_MACOS_WEBKIT
  669. NSString* const nsjs = [[NSString alloc] initWithBytes:js
  670. length:std::strlen(js)
  671. encoding:NSUTF8StringEncoding];
  672. [handle->webview evaluateJavaScript:nsjs completionHandler:nil];
  673. [nsjs release];
  674. #elif WEB_VIEW_USING_X11_IPC
  675. d_debug("evaluateJS '%s'", js);
  676. const size_t len = std::strlen(js) + 1;
  677. handle->rbctrl.writeUInt(kWebViewMessageEvaluateJS) &&
  678. handle->rbctrl.writeUInt(len) &&
  679. handle->rbctrl.writeCustomData(js, len);
  680. if (handle->rbctrl.commitWrite())
  681. webview_wake(&handle->shmptr->client.sem);
  682. #endif
  683. // maybe unused
  684. (void)handle;
  685. (void)js;
  686. }
  687. void webViewReload(const WebViewHandle handle)
  688. {
  689. #if WEB_VIEW_USING_CHOC
  690. webview_choc_navigate(handle->webview, handle->url);
  691. #elif WEB_VIEW_USING_MACOS_WEBKIT
  692. [handle->webview loadRequest:handle->urlreq];
  693. #elif WEB_VIEW_USING_X11_IPC
  694. d_stdout("reload");
  695. handle->rbctrl.writeUInt(kWebViewMessageReload);
  696. if (handle->rbctrl.commitWrite())
  697. webview_wake(&handle->shmptr->client.sem);
  698. #endif
  699. // maybe unused
  700. (void)handle;
  701. }
  702. void webViewResize(const WebViewHandle handle, const uint width, const uint height, const double scaleFactor)
  703. {
  704. #if WEB_VIEW_USING_CHOC
  705. const HWND hwnd = static_cast<HWND>(webview_choc_handle(handle->webview));
  706. SetWindowPos(hwnd, nullptr, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  707. #elif WEB_VIEW_USING_MACOS_WEBKIT
  708. [handle->webview setFrameSize:NSMakeSize(width / scaleFactor, height / scaleFactor)];
  709. #elif WEB_VIEW_USING_X11_IPC
  710. if (handle->childWindow == 0)
  711. {
  712. ::Window rootWindow, parentWindow;
  713. ::Window* childWindows = nullptr;
  714. uint numChildren = 0;
  715. XFlush(handle->display);
  716. XQueryTree(handle->display, handle->ourWindow, &rootWindow, &parentWindow, &childWindows, &numChildren);
  717. if (numChildren == 0 || childWindows == nullptr)
  718. return;
  719. handle->childWindow = childWindows[0];
  720. XFree(childWindows);
  721. }
  722. XResizeWindow(handle->display, handle->childWindow, width, height);
  723. XFlush(handle->display);
  724. #endif
  725. // maybe unused
  726. (void)handle;
  727. (void)width;
  728. (void)height;
  729. (void)scaleFactor;
  730. }
  731. #if WEB_VIEW_USING_X11_IPC
  732. // -----------------------------------------------------------------------------------------------------------
  733. static bool running = false;
  734. static struct WebFramework {
  735. virtual ~WebFramework() {}
  736. virtual void evaluate(const char* js) = 0;
  737. virtual void reload() = 0;
  738. virtual void terminate() = 0;
  739. virtual void wake(WebViewRingBuffer* rb) = 0;
  740. }* webFramework = nullptr;
  741. // -----------------------------------------------------------------------------------------------------------
  742. struct GtkContainer;
  743. struct GtkPlug;
  744. struct GtkWidget;
  745. struct GtkWindow;
  746. struct JSCValue;
  747. struct WebKitJavascriptResult;
  748. struct WebKitSettings;
  749. struct WebKitUserContentManager;
  750. struct WebKitUserScript;
  751. struct WebKitWebView;
  752. typedef int gboolean;
  753. #define G_CALLBACK(p) reinterpret_cast<void*>(p)
  754. #define GTK_CONTAINER(p) reinterpret_cast<GtkContainer*>(p)
  755. #define GTK_PLUG(p) reinterpret_cast<GtkPlug*>(p)
  756. #define GTK_WINDOW(p) reinterpret_cast<GtkWindow*>(p)
  757. #define WEBKIT_WEB_VIEW(p) reinterpret_cast<WebKitWebView*>(p)
  758. // -----------------------------------------------------------------------------------------------------------
  759. class QApplication { uint8_t _[16 * 2]; };
  760. class QByteArray { public: uint8_t _[24 * 2]; };
  761. class QChar;
  762. class QChildEvent;
  763. class QColor;
  764. class QEvent { uint8_t _[16 * 2]; };
  765. class QIODevice;
  766. class QJsonObject;
  767. class QJsonValue { uint8_t _[128 /* TODO */ * 2]; };
  768. class QMetaMethod;
  769. class QMetaObject { uint8_t _[56 * 2]; };
  770. class QString { uint8_t _[8 * 4]; };
  771. class QTimerEvent;
  772. class QUrl { uint8_t _[8 * 4]; };
  773. class QWebChannel { uint8_t _[128 /* TODO */ * 2]; };
  774. class QWebEnginePage { uint8_t _[128 /* TODO */ * 2]; };
  775. class QWebEngineProfile { uint8_t _[128 /* TODO */ * 2]; };
  776. class QWebEngineScript { uint8_t _[128 /* TODO */ * 2]; };
  777. class QWebEngineScriptCollection;
  778. class QWebEngineSettings;
  779. class QWebEngineUrlRequestJob;
  780. class QWebEngineUrlScheme { uint8_t _[128 /* TODO */ * 2]; };
  781. class QWebEngineUrlSchemeHandler;
  782. class QWebEngineView { uint8_t _[56 * 2]; };
  783. class QWindow;
  784. struct QPoint {
  785. int _x, _y;
  786. QPoint(int x, int y) : _x(x), _y(y) {}
  787. };
  788. struct QSize {
  789. int _w, _h;
  790. QSize(int w, int h) : _w(w), _h(h) {}
  791. };
  792. // -----------------------------------------------------------------------------------------------------------
  793. #define JOIN(A, B) A ## B
  794. #define CSYM(S, NAME) \
  795. S NAME = reinterpret_cast<S>(dlsym(nullptr, #NAME)); \
  796. DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false);
  797. #define CPPSYM(S, NAME, SN) \
  798. S NAME = reinterpret_cast<S>(dlsym(nullptr, #SN)); \
  799. DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false);
  800. static void web_wake_idle(void* const ptr)
  801. {
  802. WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(ptr);
  803. RingBufferControl<WebViewSharedBuffer> rbctrl;
  804. rbctrl.setRingBuffer(&shmptr->client, false);
  805. uint32_t size = 0;
  806. void* buffer = nullptr;
  807. while (rbctrl.isDataAvailableForReading())
  808. {
  809. switch (rbctrl.readUInt())
  810. {
  811. case kWebViewMessageEvaluateJS:
  812. if (const uint32_t len = rbctrl.readUInt())
  813. {
  814. if (len > size)
  815. {
  816. size = len;
  817. buffer = realloc(buffer, len);
  818. if (buffer == nullptr)
  819. {
  820. d_stderr("client kWebViewMessageEvaluateJS out of memory, abort!");
  821. abort();
  822. }
  823. }
  824. if (rbctrl.readCustomData(buffer, len))
  825. {
  826. d_debug("client kWebViewMessageEvaluateJS -> '%s'", static_cast<char*>(buffer));
  827. webFramework->evaluate(static_cast<char*>(buffer));
  828. continue;
  829. }
  830. }
  831. break;
  832. case kWebViewMessageReload:
  833. d_debug("client kWebViewMessageReload");
  834. webFramework->reload();
  835. continue;
  836. }
  837. d_stderr("client ringbuffer data race, abort!");
  838. abort();
  839. }
  840. free(buffer);
  841. }
  842. // -----------------------------------------------------------------------------------------------------------
  843. // gtk3 variant
  844. static int gtk3_js_cb(WebKitUserContentManager*, WebKitJavascriptResult* const result, void* const arg)
  845. {
  846. WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(arg);
  847. typedef void (*g_free_t)(void*);
  848. typedef char* (*jsc_value_to_string_t)(JSCValue*);
  849. typedef JSCValue* (*webkit_javascript_result_get_js_value_t)(WebKitJavascriptResult*);
  850. CSYM(g_free_t, g_free)
  851. CSYM(jsc_value_to_string_t, jsc_value_to_string)
  852. CSYM(webkit_javascript_result_get_js_value_t, webkit_javascript_result_get_js_value)
  853. JSCValue* const value = webkit_javascript_result_get_js_value(result);
  854. DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, false);
  855. char* const string = jsc_value_to_string(value);
  856. DISTRHO_SAFE_ASSERT_RETURN(string != nullptr, false);
  857. d_debug("js call received with data '%s'", string);
  858. const size_t len = std::strlen(string) + 1;
  859. RingBufferControl<WebViewSharedBuffer> rbctrl2;
  860. rbctrl2.setRingBuffer(&shmptr->server, false);
  861. rbctrl2.writeUInt(kWebViewMessageCallback) &&
  862. rbctrl2.writeUInt(len) &&
  863. rbctrl2.writeCustomData(string, len);
  864. rbctrl2.commitWrite();
  865. g_free(string);
  866. return 0;
  867. }
  868. static bool gtk3(Display* const display,
  869. const Window winId,
  870. const int x,
  871. const int y,
  872. const uint width,
  873. const uint height,
  874. double scaleFactor,
  875. const char* const url,
  876. const char* const initialJS,
  877. WebViewRingBuffer* const shmptr)
  878. {
  879. void* lib;
  880. if ((lib = dlopen("libwebkit2gtk-4.0.so.37", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
  881. (lib = dlopen("libwebkit2gtk-4.1.so.0", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
  882. (lib = dlopen("libwebkit2gtk-4.0.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
  883. (lib = dlopen("libwebkit2gtk-4.1.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  884. {
  885. d_stdout("WebView gtk3 platform not available: %s", dlerror());
  886. return false;
  887. }
  888. typedef void (*g_main_context_invoke_t)(void*, void*, void*);
  889. typedef ulong (*g_signal_connect_data_t)(void*, const char*, void*, void*, void*, int);
  890. typedef void (*gdk_set_allowed_backends_t)(const char*);
  891. typedef void (*gtk_container_add_t)(GtkContainer*, GtkWidget*);
  892. typedef gboolean (*gtk_init_check_t)(int*, char***);
  893. typedef void (*gtk_main_t)();
  894. typedef void (*gtk_main_quit_t)();
  895. typedef Window (*gtk_plug_get_id_t)(GtkPlug*);
  896. typedef GtkWidget* (*gtk_plug_new_t)(Window);
  897. typedef void (*gtk_widget_show_all_t)(GtkWidget*);
  898. typedef void (*gtk_window_move_t)(GtkWindow*, int, int);
  899. typedef void (*gtk_window_set_default_size_t)(GtkWindow*, int, int);
  900. typedef WebKitSettings* (*webkit_settings_new_t)();
  901. typedef void (*webkit_settings_set_enable_developer_extras_t)(WebKitSettings*, gboolean);
  902. typedef void (*webkit_settings_set_enable_write_console_messages_to_stdout_t)(WebKitSettings*, gboolean);
  903. typedef void (*webkit_settings_set_hardware_acceleration_policy_t)(WebKitSettings*, int);
  904. typedef void (*webkit_settings_set_javascript_can_access_clipboard_t)(WebKitSettings*, gboolean);
  905. typedef void (*webkit_user_content_manager_add_script_t)(WebKitUserContentManager*, WebKitUserScript*);
  906. typedef gboolean (*webkit_user_content_manager_register_script_message_handler_t)(WebKitUserContentManager*, const char*);
  907. typedef WebKitUserScript* (*webkit_user_script_new_t)(const char*, int, int, const char* const*, const char* const*);
  908. typedef void* (*webkit_web_view_evaluate_javascript_t)(WebKitWebView*, const char*, ssize_t, const char*, const char*, void*, void*, void*);
  909. typedef WebKitUserContentManager* (*webkit_web_view_get_user_content_manager_t)(WebKitWebView*);
  910. typedef void (*webkit_web_view_load_uri_t)(WebKitWebView*, const char*);
  911. typedef GtkWidget* (*webkit_web_view_new_with_settings_t)(WebKitSettings*);
  912. typedef void* (*webkit_web_view_run_javascript_t)(WebKitWebView*, const char*, void*, void*, void*);
  913. typedef void (*webkit_web_view_set_background_color_t)(WebKitWebView*, const double*);
  914. CSYM(g_main_context_invoke_t, g_main_context_invoke)
  915. CSYM(g_signal_connect_data_t, g_signal_connect_data)
  916. CSYM(gdk_set_allowed_backends_t, gdk_set_allowed_backends)
  917. CSYM(gtk_container_add_t, gtk_container_add)
  918. CSYM(gtk_init_check_t, gtk_init_check)
  919. CSYM(gtk_main_t, gtk_main)
  920. CSYM(gtk_main_quit_t, gtk_main_quit)
  921. CSYM(gtk_plug_get_id_t, gtk_plug_get_id)
  922. CSYM(gtk_plug_new_t, gtk_plug_new)
  923. CSYM(gtk_widget_show_all_t, gtk_widget_show_all)
  924. CSYM(gtk_window_move_t, gtk_window_move)
  925. CSYM(gtk_window_set_default_size_t, gtk_window_set_default_size)
  926. CSYM(webkit_settings_new_t, webkit_settings_new)
  927. CSYM(webkit_settings_set_enable_developer_extras_t, webkit_settings_set_enable_developer_extras)
  928. CSYM(webkit_settings_set_enable_write_console_messages_to_stdout_t, webkit_settings_set_enable_write_console_messages_to_stdout)
  929. CSYM(webkit_settings_set_hardware_acceleration_policy_t, webkit_settings_set_hardware_acceleration_policy)
  930. CSYM(webkit_settings_set_javascript_can_access_clipboard_t, webkit_settings_set_javascript_can_access_clipboard)
  931. CSYM(webkit_user_content_manager_add_script_t, webkit_user_content_manager_add_script)
  932. CSYM(webkit_user_content_manager_register_script_message_handler_t, webkit_user_content_manager_register_script_message_handler)
  933. CSYM(webkit_user_script_new_t, webkit_user_script_new)
  934. CSYM(webkit_web_view_get_user_content_manager_t, webkit_web_view_get_user_content_manager)
  935. CSYM(webkit_web_view_load_uri_t, webkit_web_view_load_uri)
  936. CSYM(webkit_web_view_new_with_settings_t, webkit_web_view_new_with_settings)
  937. CSYM(webkit_web_view_set_background_color_t, webkit_web_view_set_background_color)
  938. // special case for legacy API handling
  939. webkit_web_view_evaluate_javascript_t webkit_web_view_evaluate_javascript = reinterpret_cast<webkit_web_view_evaluate_javascript_t>(dlsym(nullptr, "webkit_web_view_evaluate_javascript"));
  940. webkit_web_view_run_javascript_t webkit_web_view_run_javascript = reinterpret_cast<webkit_web_view_run_javascript_t>(dlsym(nullptr, "webkit_web_view_run_javascript"));
  941. DISTRHO_SAFE_ASSERT_RETURN(webkit_web_view_evaluate_javascript != nullptr || webkit_web_view_run_javascript != nullptr, false);
  942. const int gdkScale = std::fmod(scaleFactor, 1.0) >= 0.75
  943. ? static_cast<int>(scaleFactor + 0.5)
  944. : static_cast<int>(scaleFactor);
  945. if (gdkScale != 1)
  946. {
  947. char scale[8] = {};
  948. std::snprintf(scale, 7, "%d", gdkScale);
  949. setenv("GDK_SCALE", scale, 1);
  950. std::snprintf(scale, 7, "%.2f", (1.0 / scaleFactor) * 1.2);
  951. setenv("GDK_DPI_SCALE", scale, 1);
  952. }
  953. else if (scaleFactor > 1.0)
  954. {
  955. char scale[8] = {};
  956. std::snprintf(scale, 7, "%.2f", (1.0 / scaleFactor) * 1.4);
  957. setenv("GDK_DPI_SCALE", scale, 1);
  958. }
  959. scaleFactor /= gdkScale;
  960. gdk_set_allowed_backends("x11");
  961. if (! gtk_init_check(nullptr, nullptr))
  962. {
  963. d_stderr("WebView gtk_init_check failed");
  964. return false;
  965. }
  966. GtkWidget* const window = gtk_plug_new(winId);
  967. DISTRHO_SAFE_ASSERT_RETURN(window != nullptr, false);
  968. gtk_window_set_default_size(GTK_WINDOW(window), width, height);
  969. gtk_window_move(GTK_WINDOW(window), x, y);
  970. WebKitSettings* const settings = webkit_settings_new();
  971. DISTRHO_SAFE_ASSERT_RETURN(settings != nullptr, false);
  972. // TODO DOMPasteAllowed
  973. webkit_settings_set_javascript_can_access_clipboard(settings, true);
  974. webkit_settings_set_hardware_acceleration_policy(settings, 2 /* WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER */);
  975. // if (debug)
  976. {
  977. webkit_settings_set_enable_developer_extras(settings, true);
  978. webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
  979. }
  980. GtkWidget* const webview = webkit_web_view_new_with_settings(settings);
  981. DISTRHO_SAFE_ASSERT_RETURN(webview != nullptr, false);
  982. const double color[] = {49.0/255, 54.0/255, 59.0/255, 1};
  983. webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(webview), color);
  984. if (WebKitUserContentManager* const manager = webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(webview)))
  985. {
  986. g_signal_connect_data(manager, "script-message-received::external", G_CALLBACK(gtk3_js_cb), shmptr, nullptr, 0);
  987. webkit_user_content_manager_register_script_message_handler(manager, "external");
  988. WebKitUserScript* const mscript = webkit_user_script_new(
  989. "function postMessage(m){window.webkit.messageHandlers.external.postMessage(m)}", 0, 0, nullptr, nullptr);
  990. webkit_user_content_manager_add_script(manager, mscript);
  991. if (initialJS != nullptr)
  992. {
  993. WebKitUserScript* const script = webkit_user_script_new(initialJS, 0, 0, nullptr, nullptr);
  994. webkit_user_content_manager_add_script(manager, script);
  995. }
  996. }
  997. webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url);
  998. gtk_container_add(GTK_CONTAINER(window), webview);
  999. gtk_widget_show_all(window);
  1000. Window wid = gtk_plug_get_id(GTK_PLUG(window));
  1001. XMapWindow(display, wid);
  1002. XFlush(display);
  1003. struct Gtk3WebFramework : WebFramework {
  1004. const char* const _url;
  1005. WebViewRingBuffer* const _shmptr;
  1006. GtkWidget* const _webview;
  1007. const webkit_web_view_evaluate_javascript_t _webkit_web_view_evaluate_javascript;
  1008. const webkit_web_view_run_javascript_t _webkit_web_view_run_javascript;
  1009. const webkit_web_view_load_uri_t _webkit_web_view_load_uri;
  1010. const gtk_main_quit_t _gtk_main_quit;
  1011. const g_main_context_invoke_t _g_main_context_invoke;
  1012. Gtk3WebFramework(const char* const url,
  1013. WebViewRingBuffer* const shmptr,
  1014. GtkWidget* const webview,
  1015. const webkit_web_view_evaluate_javascript_t webkit_web_view_evaluate_javascript,
  1016. const webkit_web_view_run_javascript_t webkit_web_view_run_javascript,
  1017. const webkit_web_view_load_uri_t webkit_web_view_load_uri,
  1018. const gtk_main_quit_t gtk_main_quit,
  1019. const g_main_context_invoke_t g_main_context_invoke)
  1020. : _url(url),
  1021. _shmptr(shmptr),
  1022. _webview(webview),
  1023. _webkit_web_view_evaluate_javascript(webkit_web_view_evaluate_javascript),
  1024. _webkit_web_view_run_javascript(webkit_web_view_run_javascript),
  1025. _webkit_web_view_load_uri(webkit_web_view_load_uri),
  1026. _gtk_main_quit(gtk_main_quit),
  1027. _g_main_context_invoke(g_main_context_invoke) {}
  1028. void evaluate(const char* const js) override
  1029. {
  1030. if (_webkit_web_view_evaluate_javascript != nullptr)
  1031. _webkit_web_view_evaluate_javascript(WEBKIT_WEB_VIEW(_webview), js, -1,
  1032. nullptr, nullptr, nullptr, nullptr, nullptr);
  1033. else
  1034. _webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(_webview), js, nullptr, nullptr, nullptr);
  1035. }
  1036. void reload() override
  1037. {
  1038. _webkit_web_view_load_uri(WEBKIT_WEB_VIEW(_webview), _url);
  1039. }
  1040. void terminate() override
  1041. {
  1042. if (running)
  1043. {
  1044. running = false;
  1045. webview_wake(&_shmptr->client.sem);
  1046. _gtk_main_quit();
  1047. }
  1048. }
  1049. void wake(WebViewRingBuffer* const rb) override
  1050. {
  1051. _g_main_context_invoke(NULL, G_CALLBACK(web_wake_idle), rb);
  1052. }
  1053. };
  1054. Gtk3WebFramework webFrameworkObj(url,
  1055. shmptr,
  1056. webview,
  1057. webkit_web_view_evaluate_javascript,
  1058. webkit_web_view_run_javascript,
  1059. webkit_web_view_load_uri,
  1060. gtk_main_quit,
  1061. g_main_context_invoke);
  1062. webFramework = &webFrameworkObj;
  1063. // notify server we started ok
  1064. webview_wake(&shmptr->server.sem);
  1065. d_stdout("WebView gtk3 main loop started");
  1066. gtk_main();
  1067. d_stdout("WebView gtk3 main loop quit");
  1068. dlclose(lib);
  1069. return true;
  1070. }
  1071. // -----------------------------------------------------------------------------------------------------------
  1072. // qt common code
  1073. #define TRACE d_stdout("%04d: %s", __LINE__, __PRETTY_FUNCTION__);
  1074. class QObject
  1075. {
  1076. public:
  1077. QObject(QObject* parent = nullptr)
  1078. {
  1079. static void (*m)(QObject*, QObject*) = reinterpret_cast<typeof(m)>(dlsym(
  1080. nullptr, "_ZN7QObjectC1EPS_"));
  1081. m(this, parent);
  1082. }
  1083. virtual const QMetaObject* metaObject() const
  1084. {
  1085. static const QMetaObject* (*m)(const QObject*) = reinterpret_cast<typeof(m)>(dlsym(
  1086. nullptr, "_ZNK7QObject10metaObjectEv"));
  1087. return m(this);
  1088. }
  1089. virtual void* qt_metacast(const char*) { return 0; }
  1090. virtual int qt_metacall(void* /* QMetaObject::Call */, int, void**) { return 0; }
  1091. virtual ~QObject() {}
  1092. virtual bool event(QEvent* e)
  1093. {
  1094. static bool (*m)(QObject*, QEvent*) = reinterpret_cast<typeof(m)>(dlsym(
  1095. nullptr, "_ZN7QObject5eventEP6QEvent"));
  1096. return m(this, e);
  1097. }
  1098. virtual bool eventFilter(QObject* watched, QEvent* event)
  1099. {
  1100. static bool (*m)(QObject*, QObject*, QEvent*) = reinterpret_cast<typeof(m)>(dlsym(
  1101. nullptr, "_ZN7QObject11eventFilterEPS_P6QEvent"));
  1102. return m(this, watched, event);
  1103. }
  1104. virtual void timerEvent(QTimerEvent* event)
  1105. {
  1106. static void (*m)(QObject*, QTimerEvent*) = reinterpret_cast<typeof(m)>(dlsym(
  1107. nullptr, "_ZN7QObject10timerEventEP11QTimerEvent"));
  1108. m(this, event);
  1109. }
  1110. virtual void childEvent(QChildEvent* event)
  1111. {
  1112. static void (*m)(QObject*, QChildEvent*) = reinterpret_cast<typeof(m)>(dlsym(
  1113. nullptr, "_ZN7QObject10childEventEP11QChildEvent"));
  1114. m(this, event);
  1115. }
  1116. virtual void customEvent(QEvent* event)
  1117. {
  1118. static void (*m)(QObject*, QEvent*) = reinterpret_cast<typeof(m)>(dlsym(
  1119. nullptr, "_ZN7QObject11customEventEP6QEvent"));
  1120. m(this, event);
  1121. }
  1122. virtual void connectNotify(const QMetaMethod& signal)
  1123. {
  1124. static void (*m)(QObject*, const QMetaMethod&) = reinterpret_cast<typeof(m)>(dlsym(
  1125. nullptr, "_ZN7QObject13connectNotifyERK11QMetaMethod"));
  1126. m(this, signal);
  1127. }
  1128. virtual void disconnectNotify(const QMetaMethod& signal)
  1129. {
  1130. static void (*m)(QObject*, const QMetaMethod&) = reinterpret_cast<typeof(m)>(dlsym(
  1131. nullptr, "_ZN7QObject16disconnectNotifyERK11QMetaMethod"));
  1132. m(this, signal);
  1133. }
  1134. private:
  1135. uint8_t _[8 * 2];
  1136. };
  1137. class QWebChannelAbstractTransport : public QObject
  1138. {
  1139. protected:
  1140. const QMetaObject* metaObject() const override
  1141. {
  1142. static const QMetaObject* (*m)(const QObject*) = reinterpret_cast<typeof(m)>(dlsym(
  1143. nullptr, "_ZNK28QWebChannelAbstractTransport10metaObjectEv"));
  1144. return m(this);
  1145. }
  1146. void* qt_metacast(const char*) override { return 0; }
  1147. int qt_metacall(void* /* QMetaObject::Call */, int, void**) override { return 0; }
  1148. ~QWebChannelAbstractTransport() override {}
  1149. public:
  1150. QWebChannelAbstractTransport(QObject* parent = nullptr)
  1151. : QObject(parent)
  1152. {
  1153. static void (*m)(QObject*, QObject*) = reinterpret_cast<typeof(m)>(dlsym(
  1154. nullptr, "_ZN28QWebChannelAbstractTransportC1EP7QObject"));
  1155. m(this, parent);
  1156. }
  1157. virtual void sendMessage(const QJsonObject&) = 0;
  1158. };
  1159. // -----------------------------------------------------------------------------------------------------------
  1160. // QObject subclass for receiving events on main thread
  1161. class EventFilterQObject : public QWebChannelAbstractTransport
  1162. {
  1163. QString qstrkey;
  1164. WebViewRingBuffer* const _rb;
  1165. bool isQt5;
  1166. public:
  1167. EventFilterQObject(WebViewRingBuffer* const rb)
  1168. : QWebChannelAbstractTransport(),
  1169. _rb(rb),
  1170. isQt5(false)
  1171. {
  1172. void (*QString__init)(QString*, const QChar*, int) =
  1173. reinterpret_cast<typeof(QString__init)>(dlsym(nullptr, "_ZN7QStringC2EPK5QCharx"));
  1174. if (QString__init == nullptr)
  1175. {
  1176. isQt5 = true;
  1177. QString__init = reinterpret_cast<typeof(QString__init)>(dlsym(nullptr, "_ZN7QStringC2EPK5QChari"));
  1178. }
  1179. const ushort key_qchar[] = { 'm', 0 };
  1180. QString__init(&qstrkey, reinterpret_cast<const QChar*>(key_qchar), 1);
  1181. }
  1182. void customEvent(QEvent*) override
  1183. {
  1184. web_wake_idle(_rb);
  1185. }
  1186. void sendMessage(const QJsonObject& obj) override
  1187. {
  1188. static void (*QByteArray_clear)(QByteArray*) =
  1189. reinterpret_cast<typeof(QByteArray_clear)>(dlsym(nullptr, "_ZN10QByteArray5clearEv"));
  1190. static QJsonValue (*QJsonObject_value)(const QJsonObject*, const QString&) =
  1191. reinterpret_cast<typeof(QJsonObject_value)>(dlsym(nullptr, "_ZNK11QJsonObject5valueERK7QString"));
  1192. static void (*QJsonValue__deinit)(const QJsonValue*) =
  1193. reinterpret_cast<typeof(QJsonValue__deinit)>(dlsym(nullptr, "_ZN10QJsonValueD1Ev"));
  1194. static QString (*QJsonValue_toString)(const QJsonValue*) =
  1195. reinterpret_cast<typeof(QJsonValue_toString)>(dlsym(nullptr, "_ZNK10QJsonValue8toStringEv"));
  1196. static QString& (*QString_setRawData)(QString*, const QChar*, int) =
  1197. reinterpret_cast<typeof(QString_setRawData)>(dlsym(nullptr, "_ZN7QString10setRawDataEPK5QCharx")) ?:
  1198. reinterpret_cast<typeof(QString_setRawData)>(dlsym(nullptr, "_ZN7QString10setRawDataEPK5QChari"));
  1199. static QByteArray (*QString_toUtf8)(const QString*) =
  1200. reinterpret_cast<typeof(QString_toUtf8)>(dlsym(nullptr, "_ZNK7QString6toUtf8Ev")) ?:
  1201. reinterpret_cast<typeof(QString_toUtf8)>(dlsym(nullptr, "_ZN7QString13toUtf8_helperERKS_"));
  1202. const QJsonValue json = QJsonObject_value(&obj, qstrkey);
  1203. QString qstrvalue = QJsonValue_toString(&json);
  1204. QByteArray data = QString_toUtf8(&qstrvalue);
  1205. const uint8_t* const dptr = static_cast<const uint8_t*>(*reinterpret_cast<const void**>(data._));
  1206. const intptr_t offset = isQt5 ? *reinterpret_cast<const intptr_t*>(dptr + 16) : 16;
  1207. const char* const value = reinterpret_cast<const char*>(dptr + offset);
  1208. d_debug("js call received with data '%s'", value);
  1209. const size_t len = std::strlen(value) + 1;
  1210. RingBufferControl<WebViewSharedBuffer> rbctrl2;
  1211. rbctrl2.setRingBuffer(&_rb->server, false);
  1212. rbctrl2.writeUInt(kWebViewMessageCallback) &&
  1213. rbctrl2.writeUInt(len) &&
  1214. rbctrl2.writeCustomData(value, len);
  1215. rbctrl2.commitWrite();
  1216. // QByteArray and QString destructors are inlined and can't be called from here, call their next closest thing
  1217. QByteArray_clear(&data);
  1218. QString_setRawData(&qstrvalue, nullptr, 0);
  1219. QJsonValue__deinit(&json);
  1220. }
  1221. };
  1222. // --------------------------------------------------------------------------------------------------------------------
  1223. // qt5webengine variant
  1224. static bool qtwebengine(const int qtVersion,
  1225. Display* const display,
  1226. const Window winId,
  1227. const int x,
  1228. const int y,
  1229. const uint width,
  1230. const uint height,
  1231. double scaleFactor,
  1232. const char* const url,
  1233. const char* const initialJS,
  1234. WebViewRingBuffer* const shmptr)
  1235. {
  1236. void* lib;
  1237. switch (qtVersion)
  1238. {
  1239. case 5:
  1240. if ((lib = dlopen("libQt5WebEngineWidgets.so.5", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
  1241. (lib = dlopen("libQt5WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  1242. {
  1243. d_stdout("WebView Qt5 platform not available: %s", dlerror());
  1244. return false;
  1245. }
  1246. break;
  1247. case 6:
  1248. if ((lib = dlopen("libQt6WebEngineWidgets.so.6", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
  1249. (lib = dlopen("libQt6WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  1250. {
  1251. d_stdout("WebView Qt6 platform not available: %s", dlerror());
  1252. return false;
  1253. }
  1254. break;
  1255. default:
  1256. return false;
  1257. }
  1258. // Qt >= 6 uses int
  1259. void (*QByteArray__init)(QByteArray*, const char*, int) =
  1260. reinterpret_cast<typeof(QByteArray__init)>(dlsym(nullptr, "_ZN10QByteArrayC1EPKcx")) ?:
  1261. reinterpret_cast<typeof(QByteArray__init)>(dlsym(nullptr, "_ZN10QByteArrayC1EPKci"));
  1262. DISTRHO_SAFE_ASSERT_RETURN(QByteArray__init != nullptr, false);
  1263. typedef void (*QString__init_t)(QString*, const QChar*, int);
  1264. const QString__init_t QString__init =
  1265. reinterpret_cast<QString__init_t>(dlsym(nullptr, "_ZN7QStringC2EPK5QCharx")) ?:
  1266. reinterpret_cast<QString__init_t>(dlsym(nullptr, "_ZN7QStringC2EPK5QChari"));
  1267. DISTRHO_SAFE_ASSERT_RETURN(QString__init != nullptr, false);
  1268. void (*QWebEnginePage_setWebChannel)(QWebEnginePage*, QWebChannel*, uint) =
  1269. reinterpret_cast<typeof(QWebEnginePage_setWebChannel)>(dlsym(
  1270. nullptr, "_ZN14QWebEnginePage13setWebChannelEP11QWebChannelj")) ?:
  1271. reinterpret_cast<typeof(QWebEnginePage_setWebChannel)>(dlsym(
  1272. nullptr, "_ZN14QWebEnginePage13setWebChannelEP11QWebChannel"));
  1273. DISTRHO_SAFE_ASSERT_RETURN(QWebEnginePage_setWebChannel != nullptr, false);
  1274. // Qt >= 6 has new function signature with lambdas
  1275. #ifdef DISTRHO_PROPER_CPP11_SUPPORT
  1276. typedef void (*QWebEnginePage_runJavaScript_t)(QWebEnginePage*, const QString&, uint, const std::function<void()>&);
  1277. #else
  1278. typedef void (*QWebEnginePage_runJavaScript_t)(QWebEnginePage*, const QString&, uint, const uintptr_t&);
  1279. #endif
  1280. typedef void (*QWebEnginePage_runJavaScript_compat_t)(QWebEnginePage*, const QString&);
  1281. QWebEnginePage_runJavaScript_t QWebEnginePage_runJavaScript;
  1282. QWebEnginePage_runJavaScript_compat_t QWebEnginePage_runJavaScript_compat;
  1283. if (qtVersion == 5) {
  1284. QWebEnginePage_runJavaScript = nullptr;
  1285. QWebEnginePage_runJavaScript_compat = reinterpret_cast<QWebEnginePage_runJavaScript_compat_t>(dlsym(
  1286. nullptr, "_ZN14QWebEnginePage13runJavaScriptERK7QString"));
  1287. DISTRHO_SAFE_ASSERT_RETURN(QWebEnginePage_runJavaScript_compat != nullptr, false);
  1288. } else {
  1289. QWebEnginePage_runJavaScript_compat = nullptr;
  1290. QWebEnginePage_runJavaScript = reinterpret_cast<QWebEnginePage_runJavaScript_t>(dlsym(
  1291. nullptr, "_ZN14QWebEnginePage13runJavaScriptERK7QStringjRKSt8functionIFvRK8QVariantEE"));
  1292. DISTRHO_SAFE_ASSERT_RETURN(QWebEnginePage_runJavaScript != nullptr, false);
  1293. }
  1294. typedef void (*QApplication__init_t)(QApplication*, int&, char**, int);
  1295. typedef void (*QApplication_exec_t)();
  1296. typedef void (*QApplication_postEvent_t)(QObject*, QEvent*, int);
  1297. typedef void (*QApplication_quit_t)();
  1298. typedef void (*QApplication_setAttribute_t)(int, bool);
  1299. typedef void (*QEvent__init_t)(QEvent*, int /* QEvent::Type */);
  1300. typedef QJsonValue (*QJsonObject_value_t)(const QJsonObject*, const QString &);
  1301. typedef QString (*QJsonValue_toString_t)(const QJsonValue*);
  1302. typedef void (*QUrl__init_t)(QUrl*, const QString&, int /* QUrl::ParsingMode */);
  1303. typedef void (*QWebChannel__init_t)(QWebChannel*, QObject*);
  1304. typedef void (*QWebChannel_registerObject_t)(QWebChannel*, const QString&, QObject*);
  1305. typedef void (*QWebEnginePage__init_t)(QWebEnginePage*, QWebEngineProfile*, QObject*);
  1306. typedef void (*QWebEnginePage_setBackgroundColor_t)(QWebEnginePage*, const QColor&);
  1307. typedef QWebChannel* (*QWebEnginePage_webChannel_t)(QWebEnginePage*);
  1308. typedef QWebEngineProfile* (*QWebEngineProfile_defaultProfile_t)();
  1309. typedef void (*QWebEngineProfile_installUrlSchemeHandler_t)(QWebEngineProfile*, const QByteArray&, QWebEngineUrlSchemeHandler*);
  1310. typedef QWebEngineSettings* (*QWebEngineProfile_settings_t)(QWebEngineProfile*);
  1311. typedef QWebEngineScriptCollection* (*QWebEngineProfile_scripts_t)(QWebEngineProfile*);
  1312. typedef void (*QWebEngineScript__init_t)(QWebEngineScript*);
  1313. typedef void (*QWebEngineScript_setInjectionPoint_t)(QWebEngineScript*, int /* QWebEngineScript::InjectionPoint */);
  1314. typedef void (*QWebEngineScript_setRunsOnSubFrames_t)(QWebEngineScript*, bool);
  1315. typedef void (*QWebEngineScript_setSourceCode_t)(QWebEngineScript*, const QString &);
  1316. typedef void (*QWebEngineScript_setWorldId_t)(QWebEngineScript*, uint32_t);
  1317. typedef void (*QWebEngineScriptCollection_insert_t)(QWebEngineScriptCollection*, QWebEngineScript&);
  1318. typedef void (*QWebEngineSettings_setAttribute_t)(QWebEngineSettings*, int /* QWebEngineSettings::WebAttribute */, bool);
  1319. // typedef void (*QWebEngineUrlRequestJob_reply_t)(QWebEngineUrlRequestJob*, const QByteArray&, QIODevice*);
  1320. typedef void (*QWebEngineUrlScheme__init_t)(QWebEngineUrlScheme*, const QByteArray&);
  1321. typedef void (*QWebEngineUrlScheme_registerScheme_t)(QWebEngineUrlScheme&);
  1322. typedef void (*QWebEngineUrlScheme_setFlags_t)(QWebEngineUrlScheme*, int /* QWebEngineUrlScheme::Flags */);
  1323. typedef void (*QWebEngineUrlScheme_setSyntax_t)(QWebEngineUrlScheme*, int /* QWebEngineUrlScheme::Syntax */);
  1324. typedef void (*QWebEngineUrlSchemeHandler__init_t)(QObject*, QObject*);
  1325. typedef void (*QWebEngineView__init_t)(QWebEngineView*, QObject*);
  1326. typedef void (*QWebEngineView_move_t)(QWebEngineView*, const QPoint&);
  1327. typedef void (*QWebEngineView_resize_t)(QWebEngineView*, const QSize&);
  1328. typedef void (*QWebEngineView_setPage_t)(QWebEngineView*, QWebEnginePage*);
  1329. typedef void (*QWebEngineView_setUrl_t)(QWebEngineView*, const QUrl&);
  1330. typedef void (*QWebEngineView_show_t)(QWebEngineView*);
  1331. typedef ulonglong (*QWebEngineView_winId_t)(QWebEngineView*);
  1332. typedef QWindow* (*QWebEngineView_windowHandle_t)(QWebEngineView*);
  1333. typedef QWindow* (*QWindow_fromWinId_t)(ulonglong);
  1334. typedef void (*QWindow_setParent_t)(QWindow*, void*);
  1335. CPPSYM(QApplication__init_t, QApplication__init, _ZN12QApplicationC1ERiPPci)
  1336. CPPSYM(QApplication_exec_t, QApplication_exec, _ZN15QGuiApplication4execEv)
  1337. CPPSYM(QApplication_postEvent_t, QApplication_postEvent, _ZN16QCoreApplication9postEventEP7QObjectP6QEventi)
  1338. CPPSYM(QApplication_quit_t, QApplication_quit, _ZN16QCoreApplication4quitEv)
  1339. CPPSYM(QApplication_setAttribute_t, QApplication_setAttribute, _ZN16QCoreApplication12setAttributeEN2Qt20ApplicationAttributeEb)
  1340. CPPSYM(QEvent__init_t, QEvent__init, _ZN6QEventC1ENS_4TypeE)
  1341. CPPSYM(QJsonObject_value_t, QJsonObject_value, _ZNK11QJsonObject5valueERK7QString)
  1342. CPPSYM(QJsonValue_toString_t, QJsonValue_toString, _ZNK10QJsonValue8toStringEv)
  1343. CPPSYM(QUrl__init_t, QUrl__init, _ZN4QUrlC1ERK7QStringNS_11ParsingModeE)
  1344. CPPSYM(QWebChannel__init_t, QWebChannel__init, _ZN11QWebChannelC1EP7QObject)
  1345. CPPSYM(QWebChannel_registerObject_t, QWebChannel_registerObject, _ZN11QWebChannel14registerObjectERK7QStringP7QObject)
  1346. CPPSYM(QWebEnginePage__init_t, QWebEnginePage__init, _ZN14QWebEnginePageC1EP17QWebEngineProfileP7QObject)
  1347. CPPSYM(QWebEnginePage_setBackgroundColor_t, QWebEnginePage_setBackgroundColor, _ZN14QWebEnginePage18setBackgroundColorERK6QColor)
  1348. CPPSYM(QWebEnginePage_webChannel_t, QWebEnginePage_webChannel, _ZNK14QWebEnginePage10webChannelEv)
  1349. CPPSYM(QWebEngineProfile_defaultProfile_t, QWebEngineProfile_defaultProfile, _ZN17QWebEngineProfile14defaultProfileEv)
  1350. CPPSYM(QWebEngineProfile_installUrlSchemeHandler_t, QWebEngineProfile_installUrlSchemeHandler, _ZN17QWebEngineProfile23installUrlSchemeHandlerERK10QByteArrayP26QWebEngineUrlSchemeHandler)
  1351. CPPSYM(QWebEngineProfile_settings_t, QWebEngineProfile_settings, _ZNK17QWebEngineProfile8settingsEv)
  1352. CPPSYM(QWebEngineProfile_scripts_t, QWebEngineProfile_scripts, _ZNK17QWebEngineProfile7scriptsEv)
  1353. CPPSYM(QWebEngineScript__init_t, QWebEngineScript__init, _ZN16QWebEngineScriptC1Ev)
  1354. CPPSYM(QWebEngineScript_setInjectionPoint_t, QWebEngineScript_setInjectionPoint, _ZN16QWebEngineScript17setInjectionPointENS_14InjectionPointE)
  1355. CPPSYM(QWebEngineScript_setRunsOnSubFrames_t, QWebEngineScript_setRunsOnSubFrames, _ZN16QWebEngineScript18setRunsOnSubFramesEb)
  1356. CPPSYM(QWebEngineScript_setSourceCode_t, QWebEngineScript_setSourceCode, _ZN16QWebEngineScript13setSourceCodeERK7QString)
  1357. CPPSYM(QWebEngineScript_setWorldId_t, QWebEngineScript_setWorldId, _ZN16QWebEngineScript10setWorldIdEj)
  1358. CPPSYM(QWebEngineScriptCollection_insert_t, QWebEngineScriptCollection_insert, _ZN26QWebEngineScriptCollection6insertERK16QWebEngineScript)
  1359. CPPSYM(QWebEngineSettings_setAttribute_t, QWebEngineSettings_setAttribute, _ZN18QWebEngineSettings12setAttributeENS_12WebAttributeEb)
  1360. // CPPSYM(QWebEngineUrlRequestJob_reply_t, QWebEngineUrlRequestJob_reply, _ZN23QWebEngineUrlRequestJob5replyERK10QByteArrayP9QIODevice)
  1361. CPPSYM(QWebEngineUrlScheme__init_t, QWebEngineUrlScheme__init, _ZN19QWebEngineUrlSchemeC1ERK10QByteArray)
  1362. CPPSYM(QWebEngineUrlScheme_registerScheme_t, QWebEngineUrlScheme_registerScheme, _ZN19QWebEngineUrlScheme14registerSchemeERKS_)
  1363. CPPSYM(QWebEngineUrlScheme_setFlags_t, QWebEngineUrlScheme_setFlags, _ZN19QWebEngineUrlScheme8setFlagsE6QFlagsINS_4FlagEE)
  1364. CPPSYM(QWebEngineUrlScheme_setSyntax_t, QWebEngineUrlScheme_setSyntax, _ZN19QWebEngineUrlScheme9setSyntaxENS_6SyntaxE)
  1365. CPPSYM(QWebEngineUrlSchemeHandler__init_t, QWebEngineUrlSchemeHandler__init, _ZN26QWebEngineUrlSchemeHandlerC1EP7QObject)
  1366. CPPSYM(QWebEngineView__init_t, QWebEngineView__init, _ZN14QWebEngineViewC1EP7QWidget)
  1367. CPPSYM(QWebEngineView_move_t, QWebEngineView_move, _ZN7QWidget4moveERK6QPoint)
  1368. CPPSYM(QWebEngineView_resize_t, QWebEngineView_resize, _ZN7QWidget6resizeERK5QSize)
  1369. CPPSYM(QWebEngineView_setPage_t, QWebEngineView_setPage, _ZN14QWebEngineView7setPageEP14QWebEnginePage)
  1370. CPPSYM(QWebEngineView_setUrl_t, QWebEngineView_setUrl, _ZN14QWebEngineView6setUrlERK4QUrl)
  1371. CPPSYM(QWebEngineView_show_t, QWebEngineView_show, _ZN7QWidget4showEv)
  1372. CPPSYM(QWebEngineView_winId_t, QWebEngineView_winId, _ZNK7QWidget5winIdEv)
  1373. CPPSYM(QWebEngineView_windowHandle_t, QWebEngineView_windowHandle, _ZNK7QWidget12windowHandleEv)
  1374. CPPSYM(QWindow_fromWinId_t, QWindow_fromWinId, _ZN7QWindow9fromWinIdEy)
  1375. CPPSYM(QWindow_setParent_t, QWindow_setParent, _ZN7QWindow9setParentEPS_)
  1376. unsetenv("QT_FONT_DPI");
  1377. unsetenv("QT_SCREEN_SCALE_FACTORS");
  1378. unsetenv("QT_USE_PHYSICAL_DPI");
  1379. setenv("QT_QPA_PLATFORM", "xcb", 1);
  1380. if (qtVersion == 5)
  1381. {
  1382. setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0", 1);
  1383. }
  1384. else
  1385. {
  1386. setenv("QT_ENABLE_HIGHDPI_SCALING", "0", 1);
  1387. }
  1388. char scale[8] = {};
  1389. std::snprintf(scale, 7, "%.2f", scaleFactor);
  1390. setenv("QT_SCALE_FACTOR", scale, 1);
  1391. QByteArray urlSchemeName;
  1392. QByteArray__init(&urlSchemeName, "dpf", 3);
  1393. constexpr const int urlSchemeFlags = 0
  1394. | 0x1 /* QWebEngineUrlScheme::SecureScheme */
  1395. | 0x2 /* QWebEngineUrlScheme::LocalScheme */
  1396. | 0x4 /* QWebEngineUrlScheme::LocalAccessAllowed */
  1397. | 0x8 /* QWebEngineUrlScheme::ServiceWorkersAllowed */
  1398. | 0x40 /* QWebEngineUrlScheme::ContentSecurityPolicyIgnored */
  1399. ;
  1400. QWebEngineUrlScheme urlScheme;
  1401. QWebEngineUrlScheme__init(&urlScheme, urlSchemeName);
  1402. QWebEngineUrlScheme_setSyntax(&urlScheme, 3 /* QWebEngineUrlScheme::Syntax::Path */);
  1403. QWebEngineUrlScheme_setFlags(&urlScheme, urlSchemeFlags);
  1404. QWebEngineUrlScheme_registerScheme(urlScheme);
  1405. if (qtVersion == 5)
  1406. {
  1407. QApplication_setAttribute(10 /* Qt::AA_X11InitThreads */, true);
  1408. QApplication_setAttribute(13 /* Qt::AA_UseHighDpiPixmaps */, true);
  1409. QApplication_setAttribute(20 /* Qt::AA_EnableHighDpiScaling */, true);
  1410. }
  1411. static int argc = 1;
  1412. static char argv0[] = "dpf-webview";
  1413. static char* argv[] = { argv0, nullptr };
  1414. QApplication app;
  1415. QApplication__init(&app, argc, argv, 0);
  1416. EventFilterQObject eventFilter(shmptr);
  1417. QString qstrchannel, qstrmcode, qstrurl;
  1418. {
  1419. static constexpr const char* channel_src = "external";
  1420. const size_t channel_len = std::strlen(channel_src);
  1421. ushort* const channel_qchar = new ushort[channel_len + 1];
  1422. for (size_t i = 0; i < channel_len; ++i)
  1423. channel_qchar[i] = channel_src[i];
  1424. channel_qchar[channel_len] = 0;
  1425. QString__init(&qstrchannel, reinterpret_cast<QChar*>(channel_qchar), channel_len);
  1426. delete[] channel_qchar;
  1427. }
  1428. {
  1429. static constexpr const char* mcode_src = "\
  1430. function postMessage(m){qt.webChannelTransport.send(JSON.stringify({\
  1431. \"type\":6, \
  1432. \"id\": \"WebSender\",\
  1433. \"__QObject*__\": true,\
  1434. \"object\": \"external\", \
  1435. \"method\": \"sendMessage\",\
  1436. \"args\":[{\"m\":m}], \
  1437. }));}";
  1438. const size_t mcode_len = std::strlen(mcode_src);
  1439. ushort* const mcode_qchar = new ushort[mcode_len + 1];
  1440. for (size_t i = 0; i < mcode_len; ++i)
  1441. mcode_qchar[i] = mcode_src[i];
  1442. mcode_qchar[mcode_len] = 0;
  1443. QString__init(&qstrmcode, reinterpret_cast<QChar*>(mcode_qchar), mcode_len);
  1444. delete[] mcode_qchar;
  1445. }
  1446. {
  1447. const size_t url_len = std::strlen(url);
  1448. ushort* const url_qchar = new ushort[url_len + 1];
  1449. for (size_t i = 0; i < url_len; ++i)
  1450. url_qchar[i] = url[i];
  1451. url_qchar[url_len] = 0;
  1452. QString__init(&qstrurl, reinterpret_cast<QChar*>(url_qchar), url_len);
  1453. delete[] url_qchar;
  1454. }
  1455. QUrl qurl;
  1456. QUrl__init(&qurl, qstrurl, 1 /* QUrl::StrictMode */);
  1457. QWebEngineProfile* const profile = QWebEngineProfile_defaultProfile();
  1458. QWebEngineScriptCollection* const scripts = QWebEngineProfile_scripts(profile);
  1459. QWebEngineSettings* const settings = QWebEngineProfile_settings(profile);
  1460. {
  1461. QWebEngineScript mscript;
  1462. QWebEngineScript__init(&mscript);
  1463. QWebEngineScript_setInjectionPoint(&mscript, 2 /* QWebEngineScript::DocumentCreation */);
  1464. QWebEngineScript_setRunsOnSubFrames(&mscript, true);
  1465. QWebEngineScript_setSourceCode(&mscript, qstrmcode);
  1466. QWebEngineScript_setWorldId(&mscript, 0 /* QWebEngineScript::MainWorld */);
  1467. QWebEngineScriptCollection_insert(scripts, mscript);
  1468. }
  1469. if (initialJS != nullptr)
  1470. {
  1471. QString qstrcode;
  1472. {
  1473. const size_t code_len = std::strlen(initialJS);
  1474. ushort* const code_qchar = new ushort[code_len + 1];
  1475. for (size_t i = 0; i < code_len; ++i)
  1476. code_qchar[i] = initialJS[i];
  1477. code_qchar[code_len] = 0;
  1478. QString__init(&qstrcode, reinterpret_cast<QChar*>(code_qchar), code_len);
  1479. }
  1480. QWebEngineScript script;
  1481. QWebEngineScript__init(&script);
  1482. QWebEngineScript_setInjectionPoint(&script, 2 /* QWebEngineScript::DocumentCreation */);
  1483. QWebEngineScript_setRunsOnSubFrames(&script, true);
  1484. QWebEngineScript_setSourceCode(&script, qstrcode);
  1485. QWebEngineScript_setWorldId(&script, 0 /* QWebEngineScript::MainWorld */);
  1486. QWebEngineScriptCollection_insert(scripts, script);
  1487. }
  1488. QWebEngineSettings_setAttribute(settings, 3 /* QWebEngineSettings::JavascriptCanAccessClipboard */, true);
  1489. QWebEngineSettings_setAttribute(settings, 6 /* QWebEngineSettings::LocalContentCanAccessRemoteUrls */, true);
  1490. QWebEngineSettings_setAttribute(settings, 9 /* QWebEngineSettings::LocalContentCanAccessFileUrls */, true);
  1491. QWebEngineSettings_setAttribute(settings, 28 /* QWebEngineSettings::JavascriptCanPaste */, true);
  1492. QWebEngineView webview;
  1493. QWebEngineView__init(&webview, nullptr);
  1494. QWebEnginePage page;
  1495. QWebEnginePage__init(&page, profile, reinterpret_cast<QObject*>(&webview));
  1496. // QWebEnginePage_setBackgroundColor(&page, QColor{0,0,0,0});
  1497. QWebChannel channel;
  1498. QWebChannel__init(&channel, reinterpret_cast<QObject*>(&webview));
  1499. QWebChannel_registerObject(&channel, qstrchannel, &eventFilter);
  1500. QWebEnginePage_setWebChannel(&page, &channel, 0);
  1501. QWebEngineView_move(&webview, QPoint(x, y));
  1502. QWebEngineView_resize(&webview, QSize(static_cast<int>(width / scaleFactor), static_cast<int>(height / scaleFactor)));
  1503. QWebEngineView_winId(&webview);
  1504. QWindow_setParent(QWebEngineView_windowHandle(&webview), QWindow_fromWinId(winId));
  1505. QWebEngineView_setPage(&webview, &page);
  1506. QWebEngineView_setUrl(&webview, qurl);
  1507. // FIXME Qt6 seems to need some forcing..
  1508. if (qtVersion >= 6)
  1509. {
  1510. XReparentWindow(display, QWebEngineView_winId(&webview), winId, x, y);
  1511. XFlush(display);
  1512. }
  1513. QWebEngineView_show(&webview);
  1514. struct QtWebFramework : WebFramework {
  1515. const int _qtVersion;
  1516. WebViewRingBuffer* const _shmptr;
  1517. const QUrl& _qurl;
  1518. QWebEnginePage& _page;
  1519. QWebEngineView& _webview;
  1520. EventFilterQObject& _eventFilter;
  1521. const QString__init_t _QString__init;
  1522. const QWebEnginePage_runJavaScript_compat_t _QWebEnginePage_runJavaScript_compat;
  1523. const QWebEnginePage_runJavaScript_t _QWebEnginePage_runJavaScript;
  1524. const QWebEngineView_setUrl_t _QWebEngineView_setUrl;
  1525. const QApplication_quit_t _QApplication_quit;
  1526. const QEvent__init_t _QEvent__init;
  1527. const QApplication_postEvent_t _QApplication_postEvent;
  1528. QtWebFramework(const int qtVersion,
  1529. WebViewRingBuffer* const shmptr,
  1530. const QUrl& qurl,
  1531. QWebEnginePage& page,
  1532. QWebEngineView& webview,
  1533. EventFilterQObject& eventFilter,
  1534. const QString__init_t QString__init,
  1535. const QWebEnginePage_runJavaScript_compat_t QWebEnginePage_runJavaScript_compat,
  1536. const QWebEnginePage_runJavaScript_t QWebEnginePage_runJavaScript,
  1537. const QWebEngineView_setUrl_t QWebEngineView_setUrl,
  1538. const QApplication_quit_t QApplication_quit,
  1539. const QEvent__init_t QEvent__init,
  1540. const QApplication_postEvent_t QApplication_postEvent)
  1541. : _qtVersion(qtVersion),
  1542. _shmptr(shmptr),
  1543. _qurl(qurl),
  1544. _page(page),
  1545. _webview(webview),
  1546. _eventFilter(eventFilter),
  1547. _QString__init(QString__init),
  1548. _QWebEnginePage_runJavaScript_compat(QWebEnginePage_runJavaScript_compat),
  1549. _QWebEnginePage_runJavaScript(QWebEnginePage_runJavaScript),
  1550. _QWebEngineView_setUrl(QWebEngineView_setUrl),
  1551. _QApplication_quit(QApplication_quit),
  1552. _QEvent__init(QEvent__init),
  1553. _QApplication_postEvent(QApplication_postEvent) {}
  1554. void evaluate(const char* const js) override
  1555. {
  1556. QString qstrjs;
  1557. {
  1558. const size_t js_len = std::strlen(js);
  1559. ushort* const js_qchar = new ushort[js_len + 1];
  1560. for (size_t i = 0; i < js_len; ++i)
  1561. js_qchar[i] = js[i];
  1562. js_qchar[js_len] = 0;
  1563. _QString__init(&qstrjs, reinterpret_cast<const QChar*>(js_qchar), js_len);
  1564. }
  1565. if (_qtVersion == 5)
  1566. _QWebEnginePage_runJavaScript_compat(&_page, qstrjs);
  1567. else
  1568. _QWebEnginePage_runJavaScript(&_page, qstrjs, 0,
  1569. #ifdef DISTRHO_PROPER_CPP11_SUPPORT
  1570. {}
  1571. #else
  1572. 0
  1573. #endif
  1574. );
  1575. }
  1576. void reload() override
  1577. {
  1578. _QWebEngineView_setUrl(&_webview, _qurl);
  1579. }
  1580. void terminate() override
  1581. {
  1582. if (running)
  1583. {
  1584. running = false;
  1585. webview_wake(&_shmptr->client.sem);
  1586. _QApplication_quit();
  1587. }
  1588. }
  1589. void wake(WebViewRingBuffer*) override
  1590. {
  1591. // NOTE event pointer is deleted by Qt
  1592. QEvent* const qevent = new QEvent;
  1593. _QEvent__init(qevent, 1000 /* QEvent::User */);
  1594. _QApplication_postEvent(&_eventFilter, qevent, 1 /* Qt::HighEventPriority */);
  1595. }
  1596. };
  1597. QtWebFramework webFrameworkObj(qtVersion,
  1598. shmptr,
  1599. qurl,
  1600. page,
  1601. webview,
  1602. eventFilter,
  1603. QString__init,
  1604. QWebEnginePage_runJavaScript_compat,
  1605. QWebEnginePage_runJavaScript,
  1606. QWebEngineView_setUrl,
  1607. QApplication_quit,
  1608. QEvent__init,
  1609. QApplication_postEvent);
  1610. webFramework = &webFrameworkObj;
  1611. // notify server we started ok
  1612. webview_wake(&shmptr->server.sem);
  1613. d_stdout("WebView Qt%d main loop started", qtVersion);
  1614. QApplication_exec();
  1615. d_stdout("WebView Qt%d main loop quit", qtVersion);
  1616. dlclose(lib);
  1617. return true;
  1618. }
  1619. // -----------------------------------------------------------------------------------------------------------
  1620. // startup via ld-linux
  1621. static void signalHandler(const int sig)
  1622. {
  1623. switch (sig)
  1624. {
  1625. case SIGTERM:
  1626. webFramework->terminate();
  1627. break;
  1628. }
  1629. }
  1630. static void* threadHandler(void* const ptr)
  1631. {
  1632. WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(ptr);
  1633. while (running && shmptr->valid)
  1634. {
  1635. if (webview_timedwait(&shmptr->client.sem) && running)
  1636. webFramework->wake(shmptr);
  1637. }
  1638. return nullptr;
  1639. }
  1640. int dpf_webview_start(const int argc, char* argv[])
  1641. {
  1642. if (argc != 3)
  1643. {
  1644. d_stderr("WebView entry point, nothing to see here! ;)");
  1645. return 1;
  1646. }
  1647. d_stdout("starting... %d '%s' '%s'", argc, argv[1], argv[2]);
  1648. uselocale(newlocale(LC_NUMERIC_MASK, "C", nullptr));
  1649. Display* const display = XOpenDisplay(nullptr);
  1650. DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1);
  1651. const char* const shmname = argv[2];
  1652. const int shmfd = shm_open(shmname, O_RDWR, 0);
  1653. if (shmfd < 0)
  1654. {
  1655. d_stderr("shm_open failed: %s", std::strerror(errno));
  1656. return 1;
  1657. }
  1658. WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(mmap(nullptr,
  1659. sizeof(WebViewRingBuffer),
  1660. PROT_READ|PROT_WRITE,
  1661. MAP_SHARED,
  1662. shmfd, 0));
  1663. if (shmptr == nullptr || shmptr == nullptr)
  1664. {
  1665. d_stderr("mmap failed: %s", std::strerror(errno));
  1666. close(shmfd);
  1667. return 1;
  1668. }
  1669. RingBufferControl<WebViewSharedBuffer> rbctrl;
  1670. rbctrl.setRingBuffer(&shmptr->client, false);
  1671. // fetch initial data
  1672. bool hasInitialData = false;
  1673. Window winId = 0;
  1674. uint width = 0, height = 0;
  1675. double scaleFactor = 0;
  1676. int x = 0, y = 0;
  1677. char* url = nullptr;
  1678. char* initJS = nullptr;
  1679. while (shmptr->valid && webview_timedwait(&shmptr->client.sem))
  1680. {
  1681. if (rbctrl.isDataAvailableForReading())
  1682. {
  1683. DISTRHO_SAFE_ASSERT_RETURN(rbctrl.readUInt() == kWebViewMessageInitData, 1);
  1684. hasInitialData = running = true;
  1685. winId = rbctrl.readULong();
  1686. width = rbctrl.readUInt();
  1687. height = rbctrl.readUInt();
  1688. scaleFactor = rbctrl.readDouble();
  1689. x = rbctrl.readInt();
  1690. y = rbctrl.readInt();
  1691. const uint urllen = rbctrl.readUInt();
  1692. url = static_cast<char*>(std::malloc(urllen));
  1693. rbctrl.readCustomData(url, urllen);
  1694. if (const uint initjslen = rbctrl.readUInt())
  1695. {
  1696. initJS = static_cast<char*>(std::malloc(initjslen));
  1697. rbctrl.readCustomData(initJS, initjslen);
  1698. }
  1699. }
  1700. }
  1701. pthread_t thread;
  1702. if (hasInitialData && pthread_create(&thread, nullptr, threadHandler, shmptr) == 0)
  1703. {
  1704. d_stdout("WebView IPC in place, starting engine...");
  1705. struct sigaction sig = {};
  1706. sig.sa_handler = signalHandler;
  1707. sig.sa_flags = SA_RESTART;
  1708. sigemptyset(&sig.sa_mask);
  1709. sigaction(SIGTERM, &sig, nullptr);
  1710. if (! qtwebengine(5, display, winId, x, y, width, height, scaleFactor, url, initJS, shmptr) &&
  1711. ! qtwebengine(6, display, winId, x, y, width, height, scaleFactor, url, initJS, shmptr) &&
  1712. ! gtk3(display, winId, x, y, width, height, scaleFactor, url, initJS, shmptr))
  1713. {
  1714. d_stderr("Failed to find usable WebView platform");
  1715. }
  1716. shmptr->valid = running = false;
  1717. pthread_join(thread, nullptr);
  1718. }
  1719. else
  1720. {
  1721. d_stderr("Failed to setup WebView IPC");
  1722. }
  1723. std::free(initJS);
  1724. munmap(shmptr, sizeof(WebViewRingBuffer));
  1725. close(shmfd);
  1726. XCloseDisplay(display);
  1727. return 0;
  1728. }
  1729. // --------------------------------------------------------------------------------------------------------------------
  1730. #endif // WEB_VIEW_USING_X11_IPC
  1731. #ifdef WEB_VIEW_DGL_NAMESPACE
  1732. END_NAMESPACE_DGL
  1733. #else
  1734. END_NAMESPACE_DISTRHO
  1735. #endif
  1736. #undef MACRO_NAME
  1737. #undef MACRO_NAME2
  1738. #undef WEB_VIEW_DISTRHO_NAMESPACE
  1739. #undef WEB_VIEW_DGL_NAMESPACE
  1740. #undef WEB_VIEW_NAMESPACE