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.

2066 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_, _, DISTRHO_NAMESPACE)
  92. @interface WEB_VIEW_DELEGATE_CLASS_NAME : NSObject<WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate>
  93. @end
  94. @implementation WEB_VIEW_DELEGATE_CLASS_NAME {
  95. @public
  96. DISTRHO_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. return;
  657. }
  658. #else
  659. // unused
  660. (void)handle;
  661. #endif
  662. }
  663. void webViewEvaluateJS(const WebViewHandle handle, const char* const js)
  664. {
  665. #if WEB_VIEW_USING_CHOC
  666. webview_choc_eval(handle->webview, js);
  667. #elif WEB_VIEW_USING_MACOS_WEBKIT
  668. NSString* const nsjs = [[NSString alloc] initWithBytes:js
  669. length:std::strlen(js)
  670. encoding:NSUTF8StringEncoding];
  671. [handle->webview evaluateJavaScript:nsjs completionHandler:nil];
  672. [nsjs release];
  673. #elif WEB_VIEW_USING_X11_IPC
  674. d_debug("evaluateJS '%s'", js);
  675. const size_t len = std::strlen(js) + 1;
  676. handle->rbctrl.writeUInt(kWebViewMessageEvaluateJS) &&
  677. handle->rbctrl.writeUInt(len) &&
  678. handle->rbctrl.writeCustomData(js, len);
  679. if (handle->rbctrl.commitWrite())
  680. webview_wake(&handle->shmptr->client.sem);
  681. #endif
  682. // maybe unused
  683. (void)handle;
  684. (void)js;
  685. }
  686. void webViewReload(const WebViewHandle handle)
  687. {
  688. #if WEB_VIEW_USING_CHOC
  689. webview_choc_navigate(handle->webview, handle->url);
  690. #elif WEB_VIEW_USING_MACOS_WEBKIT
  691. [handle->webview loadRequest:handle->urlreq];
  692. #elif WEB_VIEW_USING_X11_IPC
  693. d_stdout("reload");
  694. handle->rbctrl.writeUInt(kWebViewMessageReload);
  695. if (handle->rbctrl.commitWrite())
  696. webview_wake(&handle->shmptr->client.sem);
  697. #endif
  698. // maybe unused
  699. (void)handle;
  700. }
  701. void webViewResize(const WebViewHandle handle, const uint width, const uint height, const double scaleFactor)
  702. {
  703. #if WEB_VIEW_USING_CHOC
  704. const HWND hwnd = static_cast<HWND>(webview_choc_handle(handle->webview));
  705. SetWindowPos(hwnd, nullptr, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  706. #elif WEB_VIEW_USING_MACOS_WEBKIT
  707. [handle->webview setFrameSize:NSMakeSize(width / scaleFactor, height / scaleFactor)];
  708. #elif WEB_VIEW_USING_X11_IPC
  709. if (handle->childWindow == 0)
  710. {
  711. ::Window rootWindow, parentWindow;
  712. ::Window* childWindows = nullptr;
  713. uint numChildren = 0;
  714. XFlush(handle->display);
  715. XQueryTree(handle->display, handle->ourWindow, &rootWindow, &parentWindow, &childWindows, &numChildren);
  716. if (numChildren == 0 || childWindows == nullptr)
  717. return;
  718. handle->childWindow = childWindows[0];
  719. XFree(childWindows);
  720. }
  721. XResizeWindow(handle->display, handle->childWindow, width, height);
  722. XFlush(handle->display);
  723. #endif
  724. // maybe unused
  725. (void)handle;
  726. (void)width;
  727. (void)height;
  728. (void)scaleFactor;
  729. }
  730. #if WEB_VIEW_USING_X11_IPC
  731. // -----------------------------------------------------------------------------------------------------------
  732. static bool running = false;
  733. static struct WebFramework {
  734. virtual ~WebFramework() {}
  735. virtual void evaluate(const char* js) = 0;
  736. virtual void reload() = 0;
  737. virtual void terminate() = 0;
  738. virtual void wake(WebViewRingBuffer* rb) = 0;
  739. }* webFramework = nullptr;
  740. // -----------------------------------------------------------------------------------------------------------
  741. struct GtkContainer;
  742. struct GtkPlug;
  743. struct GtkWidget;
  744. struct GtkWindow;
  745. struct JSCValue;
  746. struct WebKitJavascriptResult;
  747. struct WebKitSettings;
  748. struct WebKitUserContentManager;
  749. struct WebKitUserScript;
  750. struct WebKitWebView;
  751. typedef int gboolean;
  752. #define G_CALLBACK(p) reinterpret_cast<void*>(p)
  753. #define GTK_CONTAINER(p) reinterpret_cast<GtkContainer*>(p)
  754. #define GTK_PLUG(p) reinterpret_cast<GtkPlug*>(p)
  755. #define GTK_WINDOW(p) reinterpret_cast<GtkWindow*>(p)
  756. #define WEBKIT_WEB_VIEW(p) reinterpret_cast<WebKitWebView*>(p)
  757. // -----------------------------------------------------------------------------------------------------------
  758. class QApplication { uint8_t _[16 * 2]; };
  759. class QByteArray { public: uint8_t _[24 * 2]; };
  760. class QChar;
  761. class QChildEvent;
  762. class QColor;
  763. class QEvent { uint8_t _[16 * 2]; };
  764. class QIODevice;
  765. class QJsonObject;
  766. class QJsonValue { uint8_t _[128 /* TODO */ * 2]; };
  767. class QMetaMethod;
  768. class QMetaObject { uint8_t _[56 * 2]; };
  769. class QString { uint8_t _[8 * 4]; };
  770. class QTimerEvent;
  771. class QUrl { uint8_t _[8 * 4]; };
  772. class QWebChannel { uint8_t _[128 /* TODO */ * 2]; };
  773. class QWebEnginePage { uint8_t _[128 /* TODO */ * 2]; };
  774. class QWebEngineProfile { uint8_t _[128 /* TODO */ * 2]; };
  775. class QWebEngineScript { uint8_t _[128 /* TODO */ * 2]; };
  776. class QWebEngineScriptCollection;
  777. class QWebEngineSettings;
  778. class QWebEngineUrlRequestJob;
  779. class QWebEngineUrlScheme { uint8_t _[128 /* TODO */ * 2]; };
  780. class QWebEngineUrlSchemeHandler;
  781. class QWebEngineView { uint8_t _[56 * 2]; };
  782. class QWindow;
  783. struct QPoint {
  784. int _x, _y;
  785. QPoint(int x, int y) : _x(x), _y(y) {}
  786. };
  787. struct QSize {
  788. int _w, _h;
  789. QSize(int w, int h) : _w(w), _h(h) {}
  790. };
  791. // -----------------------------------------------------------------------------------------------------------
  792. #define JOIN(A, B) A ## B
  793. #define CSYM(S, NAME) \
  794. S NAME = reinterpret_cast<S>(dlsym(nullptr, #NAME)); \
  795. DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false);
  796. #define CPPSYM(S, NAME, SN) \
  797. S NAME = reinterpret_cast<S>(dlsym(nullptr, #SN)); \
  798. DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false);
  799. static void web_wake_idle(void* const ptr)
  800. {
  801. WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(ptr);
  802. RingBufferControl<WebViewSharedBuffer> rbctrl;
  803. rbctrl.setRingBuffer(&shmptr->client, false);
  804. uint32_t size = 0;
  805. void* buffer = nullptr;
  806. while (rbctrl.isDataAvailableForReading())
  807. {
  808. switch (rbctrl.readUInt())
  809. {
  810. case kWebViewMessageEvaluateJS:
  811. if (const uint32_t len = rbctrl.readUInt())
  812. {
  813. if (len > size)
  814. {
  815. size = len;
  816. buffer = realloc(buffer, len);
  817. if (buffer == nullptr)
  818. {
  819. d_stderr("client kWebViewMessageEvaluateJS out of memory, abort!");
  820. abort();
  821. }
  822. }
  823. if (rbctrl.readCustomData(buffer, len))
  824. {
  825. d_debug("client kWebViewMessageEvaluateJS -> '%s'", static_cast<char*>(buffer));
  826. webFramework->evaluate(static_cast<char*>(buffer));
  827. continue;
  828. }
  829. }
  830. break;
  831. case kWebViewMessageReload:
  832. d_debug("client kWebViewMessageReload");
  833. webFramework->reload();
  834. continue;
  835. }
  836. d_stderr("client ringbuffer data race, abort!");
  837. abort();
  838. }
  839. free(buffer);
  840. }
  841. // -----------------------------------------------------------------------------------------------------------
  842. // gtk3 variant
  843. static int gtk3_js_cb(WebKitUserContentManager*, WebKitJavascriptResult* const result, void* const arg)
  844. {
  845. WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(arg);
  846. typedef void (*g_free_t)(void*);
  847. typedef char* (*jsc_value_to_string_t)(JSCValue*);
  848. typedef JSCValue* (*webkit_javascript_result_get_js_value_t)(WebKitJavascriptResult*);
  849. CSYM(g_free_t, g_free)
  850. CSYM(jsc_value_to_string_t, jsc_value_to_string)
  851. CSYM(webkit_javascript_result_get_js_value_t, webkit_javascript_result_get_js_value)
  852. JSCValue* const value = webkit_javascript_result_get_js_value(result);
  853. DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, false);
  854. char* const string = jsc_value_to_string(value);
  855. DISTRHO_SAFE_ASSERT_RETURN(string != nullptr, false);
  856. d_debug("js call received with data '%s'", string);
  857. const size_t len = std::strlen(string) + 1;
  858. RingBufferControl<WebViewSharedBuffer> rbctrl2;
  859. rbctrl2.setRingBuffer(&shmptr->server, false);
  860. rbctrl2.writeUInt(kWebViewMessageCallback) &&
  861. rbctrl2.writeUInt(len) &&
  862. rbctrl2.writeCustomData(string, len);
  863. rbctrl2.commitWrite();
  864. g_free(string);
  865. return 0;
  866. }
  867. static bool gtk3(Display* const display,
  868. const Window winId,
  869. const int x,
  870. const int y,
  871. const uint width,
  872. const uint height,
  873. double scaleFactor,
  874. const char* const url,
  875. const char* const initialJS,
  876. WebViewRingBuffer* const shmptr)
  877. {
  878. void* lib;
  879. if ((lib = dlopen("libwebkit2gtk-4.0.so.37", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
  880. (lib = dlopen("libwebkit2gtk-4.1.so.0", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
  881. (lib = dlopen("libwebkit2gtk-4.0.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
  882. (lib = dlopen("libwebkit2gtk-4.1.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  883. {
  884. d_stdout("WebView gtk3 platform not available: %s", dlerror());
  885. return false;
  886. }
  887. typedef void (*g_main_context_invoke_t)(void*, void*, void*);
  888. typedef ulong (*g_signal_connect_data_t)(void*, const char*, void*, void*, void*, int);
  889. typedef void (*gdk_set_allowed_backends_t)(const char*);
  890. typedef void (*gtk_container_add_t)(GtkContainer*, GtkWidget*);
  891. typedef gboolean (*gtk_init_check_t)(int*, char***);
  892. typedef void (*gtk_main_t)();
  893. typedef void (*gtk_main_quit_t)();
  894. typedef Window (*gtk_plug_get_id_t)(GtkPlug*);
  895. typedef GtkWidget* (*gtk_plug_new_t)(Window);
  896. typedef void (*gtk_widget_show_all_t)(GtkWidget*);
  897. typedef void (*gtk_window_move_t)(GtkWindow*, int, int);
  898. typedef void (*gtk_window_set_default_size_t)(GtkWindow*, int, int);
  899. typedef WebKitSettings* (*webkit_settings_new_t)();
  900. typedef void (*webkit_settings_set_enable_developer_extras_t)(WebKitSettings*, gboolean);
  901. typedef void (*webkit_settings_set_enable_write_console_messages_to_stdout_t)(WebKitSettings*, gboolean);
  902. typedef void (*webkit_settings_set_hardware_acceleration_policy_t)(WebKitSettings*, int);
  903. typedef void (*webkit_settings_set_javascript_can_access_clipboard_t)(WebKitSettings*, gboolean);
  904. typedef void (*webkit_user_content_manager_add_script_t)(WebKitUserContentManager*, WebKitUserScript*);
  905. typedef gboolean (*webkit_user_content_manager_register_script_message_handler_t)(WebKitUserContentManager*, const char*);
  906. typedef WebKitUserScript* (*webkit_user_script_new_t)(const char*, int, int, const char* const*, const char* const*);
  907. typedef void* (*webkit_web_view_evaluate_javascript_t)(WebKitWebView*, const char*, ssize_t, const char*, const char*, void*, void*, void*);
  908. typedef WebKitUserContentManager* (*webkit_web_view_get_user_content_manager_t)(WebKitWebView*);
  909. typedef void (*webkit_web_view_load_uri_t)(WebKitWebView*, const char*);
  910. typedef GtkWidget* (*webkit_web_view_new_with_settings_t)(WebKitSettings*);
  911. typedef void* (*webkit_web_view_run_javascript_t)(WebKitWebView*, const char*, void*, void*, void*);
  912. typedef void (*webkit_web_view_set_background_color_t)(WebKitWebView*, const double*);
  913. CSYM(g_main_context_invoke_t, g_main_context_invoke)
  914. CSYM(g_signal_connect_data_t, g_signal_connect_data)
  915. CSYM(gdk_set_allowed_backends_t, gdk_set_allowed_backends)
  916. CSYM(gtk_container_add_t, gtk_container_add)
  917. CSYM(gtk_init_check_t, gtk_init_check)
  918. CSYM(gtk_main_t, gtk_main)
  919. CSYM(gtk_main_quit_t, gtk_main_quit)
  920. CSYM(gtk_plug_get_id_t, gtk_plug_get_id)
  921. CSYM(gtk_plug_new_t, gtk_plug_new)
  922. CSYM(gtk_widget_show_all_t, gtk_widget_show_all)
  923. CSYM(gtk_window_move_t, gtk_window_move)
  924. CSYM(gtk_window_set_default_size_t, gtk_window_set_default_size)
  925. CSYM(webkit_settings_new_t, webkit_settings_new)
  926. CSYM(webkit_settings_set_enable_developer_extras_t, webkit_settings_set_enable_developer_extras)
  927. CSYM(webkit_settings_set_enable_write_console_messages_to_stdout_t, webkit_settings_set_enable_write_console_messages_to_stdout)
  928. CSYM(webkit_settings_set_hardware_acceleration_policy_t, webkit_settings_set_hardware_acceleration_policy)
  929. CSYM(webkit_settings_set_javascript_can_access_clipboard_t, webkit_settings_set_javascript_can_access_clipboard)
  930. CSYM(webkit_user_content_manager_add_script_t, webkit_user_content_manager_add_script)
  931. CSYM(webkit_user_content_manager_register_script_message_handler_t, webkit_user_content_manager_register_script_message_handler)
  932. CSYM(webkit_user_script_new_t, webkit_user_script_new)
  933. CSYM(webkit_web_view_get_user_content_manager_t, webkit_web_view_get_user_content_manager)
  934. CSYM(webkit_web_view_load_uri_t, webkit_web_view_load_uri)
  935. CSYM(webkit_web_view_new_with_settings_t, webkit_web_view_new_with_settings)
  936. CSYM(webkit_web_view_set_background_color_t, webkit_web_view_set_background_color)
  937. // special case for legacy API handling
  938. 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"));
  939. 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"));
  940. DISTRHO_SAFE_ASSERT_RETURN(webkit_web_view_evaluate_javascript != nullptr || webkit_web_view_run_javascript != nullptr, false);
  941. const int gdkScale = std::fmod(scaleFactor, 1.0) >= 0.75
  942. ? static_cast<int>(scaleFactor + 0.5)
  943. : static_cast<int>(scaleFactor);
  944. if (gdkScale != 1)
  945. {
  946. char scale[8] = {};
  947. std::snprintf(scale, 7, "%d", gdkScale);
  948. setenv("GDK_SCALE", scale, 1);
  949. std::snprintf(scale, 7, "%.2f", (1.0 / scaleFactor) * 1.2);
  950. setenv("GDK_DPI_SCALE", scale, 1);
  951. }
  952. else if (scaleFactor > 1.0)
  953. {
  954. char scale[8] = {};
  955. std::snprintf(scale, 7, "%.2f", (1.0 / scaleFactor) * 1.4);
  956. setenv("GDK_DPI_SCALE", scale, 1);
  957. }
  958. scaleFactor /= gdkScale;
  959. gdk_set_allowed_backends("x11");
  960. if (! gtk_init_check(nullptr, nullptr))
  961. {
  962. d_stderr("WebView gtk_init_check failed");
  963. return false;
  964. }
  965. GtkWidget* const window = gtk_plug_new(winId);
  966. DISTRHO_SAFE_ASSERT_RETURN(window != nullptr, false);
  967. gtk_window_set_default_size(GTK_WINDOW(window),
  968. (width - x) * scaleFactor,
  969. (height - y) * scaleFactor);
  970. gtk_window_move(GTK_WINDOW(window), x * scaleFactor, y * scaleFactor);
  971. WebKitSettings* const settings = webkit_settings_new();
  972. DISTRHO_SAFE_ASSERT_RETURN(settings != nullptr, false);
  973. // TODO DOMPasteAllowed
  974. webkit_settings_set_javascript_can_access_clipboard(settings, true);
  975. webkit_settings_set_hardware_acceleration_policy(settings, 2 /* WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER */);
  976. // if (debug)
  977. {
  978. webkit_settings_set_enable_developer_extras(settings, true);
  979. webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
  980. }
  981. GtkWidget* const webview = webkit_web_view_new_with_settings(settings);
  982. DISTRHO_SAFE_ASSERT_RETURN(webview != nullptr, false);
  983. const double color[] = {49.0/255, 54.0/255, 59.0/255, 1};
  984. webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(webview), color);
  985. if (WebKitUserContentManager* const manager = webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(webview)))
  986. {
  987. g_signal_connect_data(manager, "script-message-received::external", G_CALLBACK(gtk3_js_cb), shmptr, nullptr, 0);
  988. webkit_user_content_manager_register_script_message_handler(manager, "external");
  989. WebKitUserScript* const mscript = webkit_user_script_new(
  990. "function postMessage(m){window.webkit.messageHandlers.external.postMessage(m)}", 0, 0, nullptr, nullptr);
  991. webkit_user_content_manager_add_script(manager, mscript);
  992. if (initialJS != nullptr)
  993. {
  994. WebKitUserScript* const script = webkit_user_script_new(initialJS, 0, 0, nullptr, nullptr);
  995. webkit_user_content_manager_add_script(manager, script);
  996. }
  997. }
  998. webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url);
  999. gtk_container_add(GTK_CONTAINER(window), webview);
  1000. gtk_widget_show_all(window);
  1001. Window wid = gtk_plug_get_id(GTK_PLUG(window));
  1002. XMapWindow(display, wid);
  1003. XFlush(display);
  1004. struct Gtk3WebFramework : WebFramework {
  1005. const char* const _url;
  1006. WebViewRingBuffer* const _shmptr;
  1007. GtkWidget* const _webview;
  1008. const webkit_web_view_evaluate_javascript_t _webkit_web_view_evaluate_javascript;
  1009. const webkit_web_view_run_javascript_t _webkit_web_view_run_javascript;
  1010. const webkit_web_view_load_uri_t _webkit_web_view_load_uri;
  1011. const gtk_main_quit_t _gtk_main_quit;
  1012. const g_main_context_invoke_t _g_main_context_invoke;
  1013. Gtk3WebFramework(const char* const url,
  1014. WebViewRingBuffer* const shmptr,
  1015. GtkWidget* const webview,
  1016. const webkit_web_view_evaluate_javascript_t webkit_web_view_evaluate_javascript,
  1017. const webkit_web_view_run_javascript_t webkit_web_view_run_javascript,
  1018. const webkit_web_view_load_uri_t webkit_web_view_load_uri,
  1019. const gtk_main_quit_t gtk_main_quit,
  1020. const g_main_context_invoke_t g_main_context_invoke)
  1021. : _url(url),
  1022. _shmptr(shmptr),
  1023. _webview(webview),
  1024. _webkit_web_view_evaluate_javascript(webkit_web_view_evaluate_javascript),
  1025. _webkit_web_view_run_javascript(webkit_web_view_run_javascript),
  1026. _webkit_web_view_load_uri(webkit_web_view_load_uri),
  1027. _gtk_main_quit(gtk_main_quit),
  1028. _g_main_context_invoke(g_main_context_invoke) {}
  1029. void evaluate(const char* const js) override
  1030. {
  1031. if (_webkit_web_view_evaluate_javascript != nullptr)
  1032. _webkit_web_view_evaluate_javascript(WEBKIT_WEB_VIEW(_webview), js, -1,
  1033. nullptr, nullptr, nullptr, nullptr, nullptr);
  1034. else
  1035. _webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(_webview), js, nullptr, nullptr, nullptr);
  1036. }
  1037. void reload() override
  1038. {
  1039. _webkit_web_view_load_uri(WEBKIT_WEB_VIEW(_webview), _url);
  1040. }
  1041. void terminate() override
  1042. {
  1043. if (running)
  1044. {
  1045. running = false;
  1046. webview_wake(&_shmptr->client.sem);
  1047. _gtk_main_quit();
  1048. }
  1049. }
  1050. void wake(WebViewRingBuffer* const rb) override
  1051. {
  1052. _g_main_context_invoke(NULL, G_CALLBACK(web_wake_idle), rb);
  1053. }
  1054. };
  1055. Gtk3WebFramework webFrameworkObj(url,
  1056. shmptr,
  1057. webview,
  1058. webkit_web_view_evaluate_javascript,
  1059. webkit_web_view_run_javascript,
  1060. webkit_web_view_load_uri,
  1061. gtk_main_quit,
  1062. g_main_context_invoke);
  1063. webFramework = &webFrameworkObj;
  1064. // notify server we started ok
  1065. webview_wake(&shmptr->server.sem);
  1066. d_stdout("WebView gtk3 main loop started");
  1067. gtk_main();
  1068. d_stdout("WebView gtk3 main loop quit");
  1069. dlclose(lib);
  1070. return true;
  1071. }
  1072. // -----------------------------------------------------------------------------------------------------------
  1073. // qt common code
  1074. #define TRACE d_stdout("%04d: %s", __LINE__, __PRETTY_FUNCTION__);
  1075. class QObject
  1076. {
  1077. public:
  1078. QObject(QObject* parent = nullptr)
  1079. {
  1080. static void (*m)(QObject*, QObject*) = reinterpret_cast<typeof(m)>(dlsym(
  1081. nullptr, "_ZN7QObjectC1EPS_"));
  1082. m(this, parent);
  1083. }
  1084. virtual const QMetaObject* metaObject() const
  1085. {
  1086. static const QMetaObject* (*m)(const QObject*) = reinterpret_cast<typeof(m)>(dlsym(
  1087. nullptr, "_ZNK7QObject10metaObjectEv"));
  1088. return m(this);
  1089. }
  1090. virtual void* qt_metacast(const char*) { return 0; }
  1091. virtual int qt_metacall(void* /* QMetaObject::Call */, int, void**) { return 0; }
  1092. virtual ~QObject() {}
  1093. virtual bool event(QEvent* e)
  1094. {
  1095. static bool (*m)(QObject*, QEvent*) = reinterpret_cast<typeof(m)>(dlsym(
  1096. nullptr, "_ZN7QObject5eventEP6QEvent"));
  1097. return m(this, e);
  1098. }
  1099. virtual bool eventFilter(QObject* watched, QEvent* event)
  1100. {
  1101. static bool (*m)(QObject*, QObject*, QEvent*) = reinterpret_cast<typeof(m)>(dlsym(
  1102. nullptr, "_ZN7QObject11eventFilterEPS_P6QEvent"));
  1103. return m(this, watched, event);
  1104. }
  1105. virtual void timerEvent(QTimerEvent* event)
  1106. {
  1107. static void (*m)(QObject*, QTimerEvent*) = reinterpret_cast<typeof(m)>(dlsym(
  1108. nullptr, "_ZN7QObject10timerEventEP11QTimerEvent"));
  1109. m(this, event);
  1110. }
  1111. virtual void childEvent(QChildEvent* event)
  1112. {
  1113. static void (*m)(QObject*, QChildEvent*) = reinterpret_cast<typeof(m)>(dlsym(
  1114. nullptr, "_ZN7QObject10childEventEP11QChildEvent"));
  1115. m(this, event);
  1116. }
  1117. virtual void customEvent(QEvent* event)
  1118. {
  1119. static void (*m)(QObject*, QEvent*) = reinterpret_cast<typeof(m)>(dlsym(
  1120. nullptr, "_ZN7QObject11customEventEP6QEvent"));
  1121. m(this, event);
  1122. }
  1123. virtual void connectNotify(const QMetaMethod& signal)
  1124. {
  1125. static void (*m)(QObject*, const QMetaMethod&) = reinterpret_cast<typeof(m)>(dlsym(
  1126. nullptr, "_ZN7QObject13connectNotifyERK11QMetaMethod"));
  1127. m(this, signal);
  1128. }
  1129. virtual void disconnectNotify(const QMetaMethod& signal)
  1130. {
  1131. static void (*m)(QObject*, const QMetaMethod&) = reinterpret_cast<typeof(m)>(dlsym(
  1132. nullptr, "_ZN7QObject16disconnectNotifyERK11QMetaMethod"));
  1133. m(this, signal);
  1134. }
  1135. private:
  1136. uint8_t _[8 * 2];
  1137. };
  1138. class QWebChannelAbstractTransport : public QObject
  1139. {
  1140. protected:
  1141. const QMetaObject* metaObject() const override
  1142. {
  1143. static const QMetaObject* (*m)(const QObject*) = reinterpret_cast<typeof(m)>(dlsym(
  1144. nullptr, "_ZNK28QWebChannelAbstractTransport10metaObjectEv"));
  1145. return m(this);
  1146. }
  1147. void* qt_metacast(const char*) override { return 0; }
  1148. int qt_metacall(void* /* QMetaObject::Call */, int, void**) override { return 0; }
  1149. ~QWebChannelAbstractTransport() override {}
  1150. public:
  1151. QWebChannelAbstractTransport(QObject* parent = nullptr)
  1152. : QObject(parent)
  1153. {
  1154. static void (*m)(QObject*, QObject*) = reinterpret_cast<typeof(m)>(dlsym(
  1155. nullptr, "_ZN28QWebChannelAbstractTransportC1EP7QObject"));
  1156. m(this, parent);
  1157. }
  1158. virtual void sendMessage(const QJsonObject&) = 0;
  1159. };
  1160. // -----------------------------------------------------------------------------------------------------------
  1161. // QObject subclass for receiving events on main thread
  1162. class EventFilterQObject : public QWebChannelAbstractTransport
  1163. {
  1164. QString qstrkey;
  1165. WebViewRingBuffer* const _rb;
  1166. bool isQt5;
  1167. public:
  1168. EventFilterQObject(WebViewRingBuffer* const rb)
  1169. : QWebChannelAbstractTransport(),
  1170. _rb(rb),
  1171. isQt5(false)
  1172. {
  1173. void (*QString__init)(QString*, const QChar*, int) =
  1174. reinterpret_cast<typeof(QString__init)>(dlsym(nullptr, "_ZN7QStringC2EPK5QCharx"));
  1175. if (QString__init == nullptr)
  1176. {
  1177. isQt5 = true;
  1178. QString__init = reinterpret_cast<typeof(QString__init)>(dlsym(nullptr, "_ZN7QStringC2EPK5QChari"));
  1179. }
  1180. const ushort key_qchar[] = { 'm', 0 };
  1181. QString__init(&qstrkey, reinterpret_cast<const QChar*>(key_qchar), 1);
  1182. }
  1183. void customEvent(QEvent*) override
  1184. {
  1185. web_wake_idle(_rb);
  1186. }
  1187. void sendMessage(const QJsonObject& obj) override
  1188. {
  1189. static void (*QByteArray_clear)(QByteArray*) =
  1190. reinterpret_cast<typeof(QByteArray_clear)>(dlsym(nullptr, "_ZN10QByteArray5clearEv"));
  1191. static QJsonValue (*QJsonObject_value)(const QJsonObject*, const QString&) =
  1192. reinterpret_cast<typeof(QJsonObject_value)>(dlsym(nullptr, "_ZNK11QJsonObject5valueERK7QString"));
  1193. static void (*QJsonValue__deinit)(const QJsonValue*) =
  1194. reinterpret_cast<typeof(QJsonValue__deinit)>(dlsym(nullptr, "_ZN10QJsonValueD1Ev"));
  1195. static QString (*QJsonValue_toString)(const QJsonValue*) =
  1196. reinterpret_cast<typeof(QJsonValue_toString)>(dlsym(nullptr, "_ZNK10QJsonValue8toStringEv"));
  1197. static QString& (*QString_setRawData)(QString*, const QChar*, int) =
  1198. reinterpret_cast<typeof(QString_setRawData)>(dlsym(nullptr, "_ZN7QString10setRawDataEPK5QCharx")) ?:
  1199. reinterpret_cast<typeof(QString_setRawData)>(dlsym(nullptr, "_ZN7QString10setRawDataEPK5QChari"));
  1200. static QByteArray (*QString_toUtf8)(const QString*) =
  1201. reinterpret_cast<typeof(QString_toUtf8)>(dlsym(nullptr, "_ZNK7QString6toUtf8Ev")) ?:
  1202. reinterpret_cast<typeof(QString_toUtf8)>(dlsym(nullptr, "_ZN7QString13toUtf8_helperERKS_"));
  1203. const QJsonValue json = QJsonObject_value(&obj, qstrkey);
  1204. QString qstrvalue = QJsonValue_toString(&json);
  1205. QByteArray data = QString_toUtf8(&qstrvalue);
  1206. const uint8_t* const dptr = static_cast<const uint8_t*>(*reinterpret_cast<const void**>(data._));
  1207. const intptr_t offset = isQt5 ? *reinterpret_cast<const intptr_t*>(dptr + 16) : 16;
  1208. const char* const value = reinterpret_cast<const char*>(dptr + offset);
  1209. d_debug("js call received with data '%s'", value);
  1210. const size_t len = std::strlen(value) + 1;
  1211. RingBufferControl<WebViewSharedBuffer> rbctrl2;
  1212. rbctrl2.setRingBuffer(&_rb->server, false);
  1213. rbctrl2.writeUInt(kWebViewMessageCallback) &&
  1214. rbctrl2.writeUInt(len) &&
  1215. rbctrl2.writeCustomData(value, len);
  1216. rbctrl2.commitWrite();
  1217. // QByteArray and QString destructors are inlined and can't be called from here, call their next closest thing
  1218. QByteArray_clear(&data);
  1219. QString_setRawData(&qstrvalue, nullptr, 0);
  1220. QJsonValue__deinit(&json);
  1221. }
  1222. };
  1223. // --------------------------------------------------------------------------------------------------------------------
  1224. // qt5webengine variant
  1225. static bool qtwebengine(const int qtVersion,
  1226. Display* const display,
  1227. const Window winId,
  1228. const int x,
  1229. const int y,
  1230. const uint width,
  1231. const uint height,
  1232. double scaleFactor,
  1233. const char* const url,
  1234. const char* const initialJS,
  1235. WebViewRingBuffer* const shmptr)
  1236. {
  1237. void* lib;
  1238. switch (qtVersion)
  1239. {
  1240. case 5:
  1241. if ((lib = dlopen("libQt5WebEngineWidgets.so.5", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
  1242. (lib = dlopen("libQt5WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  1243. {
  1244. d_stdout("WebView Qt5 platform not available: %s", dlerror());
  1245. return false;
  1246. }
  1247. break;
  1248. case 6:
  1249. if ((lib = dlopen("libQt6WebEngineWidgets.so.6", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
  1250. (lib = dlopen("libQt6WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  1251. {
  1252. d_stdout("WebView Qt6 platform not available: %s", dlerror());
  1253. return false;
  1254. }
  1255. break;
  1256. default:
  1257. return false;
  1258. }
  1259. // Qt >= 6 uses int
  1260. void (*QByteArray__init)(QByteArray*, const char*, int) =
  1261. reinterpret_cast<typeof(QByteArray__init)>(dlsym(nullptr, "_ZN10QByteArrayC1EPKcx")) ?:
  1262. reinterpret_cast<typeof(QByteArray__init)>(dlsym(nullptr, "_ZN10QByteArrayC1EPKci"));
  1263. DISTRHO_SAFE_ASSERT_RETURN(QByteArray__init != nullptr, false);
  1264. typedef void (*QString__init_t)(QString*, const QChar*, int);
  1265. const QString__init_t QString__init =
  1266. reinterpret_cast<QString__init_t>(dlsym(nullptr, "_ZN7QStringC2EPK5QCharx")) ?:
  1267. reinterpret_cast<QString__init_t>(dlsym(nullptr, "_ZN7QStringC2EPK5QChari"));
  1268. DISTRHO_SAFE_ASSERT_RETURN(QString__init != nullptr, false);
  1269. void (*QWebEnginePage_setWebChannel)(QWebEnginePage*, QWebChannel*, uint) =
  1270. reinterpret_cast<typeof(QWebEnginePage_setWebChannel)>(dlsym(
  1271. nullptr, "_ZN14QWebEnginePage13setWebChannelEP11QWebChannelj")) ?:
  1272. reinterpret_cast<typeof(QWebEnginePage_setWebChannel)>(dlsym(
  1273. nullptr, "_ZN14QWebEnginePage13setWebChannelEP11QWebChannel"));
  1274. DISTRHO_SAFE_ASSERT_RETURN(QWebEnginePage_setWebChannel != nullptr, false);
  1275. // Qt >= 6 has new function signature with lambdas
  1276. #ifdef DISTRHO_PROPER_CPP11_SUPPORT
  1277. typedef void (*QWebEnginePage_runJavaScript_t)(QWebEnginePage*, const QString&, uint, const std::function<void()>&);
  1278. #else
  1279. typedef void (*QWebEnginePage_runJavaScript_t)(QWebEnginePage*, const QString&, uint, const uintptr_t&);
  1280. #endif
  1281. typedef void (*QWebEnginePage_runJavaScript_compat_t)(QWebEnginePage*, const QString&);
  1282. QWebEnginePage_runJavaScript_t QWebEnginePage_runJavaScript;
  1283. QWebEnginePage_runJavaScript_compat_t QWebEnginePage_runJavaScript_compat;
  1284. if (qtVersion == 5) {
  1285. QWebEnginePage_runJavaScript = nullptr;
  1286. QWebEnginePage_runJavaScript_compat = reinterpret_cast<QWebEnginePage_runJavaScript_compat_t>(dlsym(
  1287. nullptr, "_ZN14QWebEnginePage13runJavaScriptERK7QString"));
  1288. DISTRHO_SAFE_ASSERT_RETURN(QWebEnginePage_runJavaScript_compat != nullptr, false);
  1289. } else {
  1290. QWebEnginePage_runJavaScript_compat = nullptr;
  1291. QWebEnginePage_runJavaScript = reinterpret_cast<QWebEnginePage_runJavaScript_t>(dlsym(
  1292. nullptr, "_ZN14QWebEnginePage13runJavaScriptERK7QStringjRKSt8functionIFvRK8QVariantEE"));
  1293. DISTRHO_SAFE_ASSERT_RETURN(QWebEnginePage_runJavaScript != nullptr, false);
  1294. }
  1295. typedef void (*QApplication__init_t)(QApplication*, int&, char**, int);
  1296. typedef void (*QApplication_exec_t)();
  1297. typedef void (*QApplication_postEvent_t)(QObject*, QEvent*, int);
  1298. typedef void (*QApplication_quit_t)();
  1299. typedef void (*QApplication_setAttribute_t)(int, bool);
  1300. typedef void (*QEvent__init_t)(QEvent*, int /* QEvent::Type */);
  1301. typedef QJsonValue (*QJsonObject_value_t)(const QJsonObject*, const QString &);
  1302. typedef QString (*QJsonValue_toString_t)(const QJsonValue*);
  1303. typedef void (*QUrl__init_t)(QUrl*, const QString&, int /* QUrl::ParsingMode */);
  1304. typedef void (*QWebChannel__init_t)(QWebChannel*, QObject*);
  1305. typedef void (*QWebChannel_registerObject_t)(QWebChannel*, const QString&, QObject*);
  1306. typedef void (*QWebEnginePage__init_t)(QWebEnginePage*, QWebEngineProfile*, QObject*);
  1307. typedef void (*QWebEnginePage_setBackgroundColor_t)(QWebEnginePage*, const QColor&);
  1308. typedef QWebChannel* (*QWebEnginePage_webChannel_t)(QWebEnginePage*);
  1309. typedef QWebEngineProfile* (*QWebEngineProfile_defaultProfile_t)();
  1310. typedef void (*QWebEngineProfile_installUrlSchemeHandler_t)(QWebEngineProfile*, const QByteArray&, QWebEngineUrlSchemeHandler*);
  1311. typedef QWebEngineSettings* (*QWebEngineProfile_settings_t)(QWebEngineProfile*);
  1312. typedef QWebEngineScriptCollection* (*QWebEngineProfile_scripts_t)(QWebEngineProfile*);
  1313. typedef void (*QWebEngineScript__init_t)(QWebEngineScript*);
  1314. typedef void (*QWebEngineScript_setInjectionPoint_t)(QWebEngineScript*, int /* QWebEngineScript::InjectionPoint */);
  1315. typedef void (*QWebEngineScript_setRunsOnSubFrames_t)(QWebEngineScript*, bool);
  1316. typedef void (*QWebEngineScript_setSourceCode_t)(QWebEngineScript*, const QString &);
  1317. typedef void (*QWebEngineScript_setWorldId_t)(QWebEngineScript*, uint32_t);
  1318. typedef void (*QWebEngineScriptCollection_insert_t)(QWebEngineScriptCollection*, QWebEngineScript&);
  1319. typedef void (*QWebEngineSettings_setAttribute_t)(QWebEngineSettings*, int /* QWebEngineSettings::WebAttribute */, bool);
  1320. // typedef void (*QWebEngineUrlRequestJob_reply_t)(QWebEngineUrlRequestJob*, const QByteArray&, QIODevice*);
  1321. typedef void (*QWebEngineUrlScheme__init_t)(QWebEngineUrlScheme*, const QByteArray&);
  1322. typedef void (*QWebEngineUrlScheme_registerScheme_t)(QWebEngineUrlScheme&);
  1323. typedef void (*QWebEngineUrlScheme_setFlags_t)(QWebEngineUrlScheme*, int /* QWebEngineUrlScheme::Flags */);
  1324. typedef void (*QWebEngineUrlScheme_setSyntax_t)(QWebEngineUrlScheme*, int /* QWebEngineUrlScheme::Syntax */);
  1325. typedef void (*QWebEngineUrlSchemeHandler__init_t)(QObject*, QObject*);
  1326. typedef void (*QWebEngineView__init_t)(QWebEngineView*, QObject*);
  1327. typedef void (*QWebEngineView_move_t)(QWebEngineView*, const QPoint&);
  1328. typedef void (*QWebEngineView_resize_t)(QWebEngineView*, const QSize&);
  1329. typedef void (*QWebEngineView_setPage_t)(QWebEngineView*, QWebEnginePage*);
  1330. typedef void (*QWebEngineView_setUrl_t)(QWebEngineView*, const QUrl&);
  1331. typedef void (*QWebEngineView_show_t)(QWebEngineView*);
  1332. typedef ulonglong (*QWebEngineView_winId_t)(QWebEngineView*);
  1333. typedef QWindow* (*QWebEngineView_windowHandle_t)(QWebEngineView*);
  1334. typedef QWindow* (*QWindow_fromWinId_t)(ulonglong);
  1335. typedef void (*QWindow_setParent_t)(QWindow*, void*);
  1336. CPPSYM(QApplication__init_t, QApplication__init, _ZN12QApplicationC1ERiPPci)
  1337. CPPSYM(QApplication_exec_t, QApplication_exec, _ZN15QGuiApplication4execEv)
  1338. CPPSYM(QApplication_postEvent_t, QApplication_postEvent, _ZN16QCoreApplication9postEventEP7QObjectP6QEventi)
  1339. CPPSYM(QApplication_quit_t, QApplication_quit, _ZN16QCoreApplication4quitEv)
  1340. CPPSYM(QApplication_setAttribute_t, QApplication_setAttribute, _ZN16QCoreApplication12setAttributeEN2Qt20ApplicationAttributeEb)
  1341. CPPSYM(QEvent__init_t, QEvent__init, _ZN6QEventC1ENS_4TypeE)
  1342. CPPSYM(QJsonObject_value_t, QJsonObject_value, _ZNK11QJsonObject5valueERK7QString)
  1343. CPPSYM(QJsonValue_toString_t, QJsonValue_toString, _ZNK10QJsonValue8toStringEv)
  1344. CPPSYM(QUrl__init_t, QUrl__init, _ZN4QUrlC1ERK7QStringNS_11ParsingModeE)
  1345. CPPSYM(QWebChannel__init_t, QWebChannel__init, _ZN11QWebChannelC1EP7QObject)
  1346. CPPSYM(QWebChannel_registerObject_t, QWebChannel_registerObject, _ZN11QWebChannel14registerObjectERK7QStringP7QObject)
  1347. CPPSYM(QWebEnginePage__init_t, QWebEnginePage__init, _ZN14QWebEnginePageC1EP17QWebEngineProfileP7QObject)
  1348. CPPSYM(QWebEnginePage_setBackgroundColor_t, QWebEnginePage_setBackgroundColor, _ZN14QWebEnginePage18setBackgroundColorERK6QColor)
  1349. CPPSYM(QWebEnginePage_webChannel_t, QWebEnginePage_webChannel, _ZNK14QWebEnginePage10webChannelEv)
  1350. CPPSYM(QWebEngineProfile_defaultProfile_t, QWebEngineProfile_defaultProfile, _ZN17QWebEngineProfile14defaultProfileEv)
  1351. CPPSYM(QWebEngineProfile_installUrlSchemeHandler_t, QWebEngineProfile_installUrlSchemeHandler, _ZN17QWebEngineProfile23installUrlSchemeHandlerERK10QByteArrayP26QWebEngineUrlSchemeHandler)
  1352. CPPSYM(QWebEngineProfile_settings_t, QWebEngineProfile_settings, _ZNK17QWebEngineProfile8settingsEv)
  1353. CPPSYM(QWebEngineProfile_scripts_t, QWebEngineProfile_scripts, _ZNK17QWebEngineProfile7scriptsEv)
  1354. CPPSYM(QWebEngineScript__init_t, QWebEngineScript__init, _ZN16QWebEngineScriptC1Ev)
  1355. CPPSYM(QWebEngineScript_setInjectionPoint_t, QWebEngineScript_setInjectionPoint, _ZN16QWebEngineScript17setInjectionPointENS_14InjectionPointE)
  1356. CPPSYM(QWebEngineScript_setRunsOnSubFrames_t, QWebEngineScript_setRunsOnSubFrames, _ZN16QWebEngineScript18setRunsOnSubFramesEb)
  1357. CPPSYM(QWebEngineScript_setSourceCode_t, QWebEngineScript_setSourceCode, _ZN16QWebEngineScript13setSourceCodeERK7QString)
  1358. CPPSYM(QWebEngineScript_setWorldId_t, QWebEngineScript_setWorldId, _ZN16QWebEngineScript10setWorldIdEj)
  1359. CPPSYM(QWebEngineScriptCollection_insert_t, QWebEngineScriptCollection_insert, _ZN26QWebEngineScriptCollection6insertERK16QWebEngineScript)
  1360. CPPSYM(QWebEngineSettings_setAttribute_t, QWebEngineSettings_setAttribute, _ZN18QWebEngineSettings12setAttributeENS_12WebAttributeEb)
  1361. // CPPSYM(QWebEngineUrlRequestJob_reply_t, QWebEngineUrlRequestJob_reply, _ZN23QWebEngineUrlRequestJob5replyERK10QByteArrayP9QIODevice)
  1362. CPPSYM(QWebEngineUrlScheme__init_t, QWebEngineUrlScheme__init, _ZN19QWebEngineUrlSchemeC1ERK10QByteArray)
  1363. CPPSYM(QWebEngineUrlScheme_registerScheme_t, QWebEngineUrlScheme_registerScheme, _ZN19QWebEngineUrlScheme14registerSchemeERKS_)
  1364. CPPSYM(QWebEngineUrlScheme_setFlags_t, QWebEngineUrlScheme_setFlags, _ZN19QWebEngineUrlScheme8setFlagsE6QFlagsINS_4FlagEE)
  1365. CPPSYM(QWebEngineUrlScheme_setSyntax_t, QWebEngineUrlScheme_setSyntax, _ZN19QWebEngineUrlScheme9setSyntaxENS_6SyntaxE)
  1366. CPPSYM(QWebEngineUrlSchemeHandler__init_t, QWebEngineUrlSchemeHandler__init, _ZN26QWebEngineUrlSchemeHandlerC1EP7QObject)
  1367. CPPSYM(QWebEngineView__init_t, QWebEngineView__init, _ZN14QWebEngineViewC1EP7QWidget)
  1368. CPPSYM(QWebEngineView_move_t, QWebEngineView_move, _ZN7QWidget4moveERK6QPoint)
  1369. CPPSYM(QWebEngineView_resize_t, QWebEngineView_resize, _ZN7QWidget6resizeERK5QSize)
  1370. CPPSYM(QWebEngineView_setPage_t, QWebEngineView_setPage, _ZN14QWebEngineView7setPageEP14QWebEnginePage)
  1371. CPPSYM(QWebEngineView_setUrl_t, QWebEngineView_setUrl, _ZN14QWebEngineView6setUrlERK4QUrl)
  1372. CPPSYM(QWebEngineView_show_t, QWebEngineView_show, _ZN7QWidget4showEv)
  1373. CPPSYM(QWebEngineView_winId_t, QWebEngineView_winId, _ZNK7QWidget5winIdEv)
  1374. CPPSYM(QWebEngineView_windowHandle_t, QWebEngineView_windowHandle, _ZNK7QWidget12windowHandleEv)
  1375. CPPSYM(QWindow_fromWinId_t, QWindow_fromWinId, _ZN7QWindow9fromWinIdEy)
  1376. CPPSYM(QWindow_setParent_t, QWindow_setParent, _ZN7QWindow9setParentEPS_)
  1377. unsetenv("QT_FONT_DPI");
  1378. unsetenv("QT_SCREEN_SCALE_FACTORS");
  1379. unsetenv("QT_USE_PHYSICAL_DPI");
  1380. setenv("QT_QPA_PLATFORM", "xcb", 1);
  1381. if (qtVersion == 5)
  1382. {
  1383. setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0", 1);
  1384. }
  1385. else
  1386. {
  1387. setenv("QT_ENABLE_HIGHDPI_SCALING", "0", 1);
  1388. }
  1389. char scale[8] = {};
  1390. std::snprintf(scale, 7, "%.2f", scaleFactor);
  1391. setenv("QT_SCALE_FACTOR", scale, 1);
  1392. QByteArray urlSchemeName;
  1393. QByteArray__init(&urlSchemeName, "dpf", 3);
  1394. constexpr const int urlSchemeFlags = 0
  1395. | 0x1 /* QWebEngineUrlScheme::SecureScheme */
  1396. | 0x2 /* QWebEngineUrlScheme::LocalScheme */
  1397. | 0x4 /* QWebEngineUrlScheme::LocalAccessAllowed */
  1398. | 0x8 /* QWebEngineUrlScheme::ServiceWorkersAllowed */
  1399. | 0x40 /* QWebEngineUrlScheme::ContentSecurityPolicyIgnored */
  1400. ;
  1401. QWebEngineUrlScheme urlScheme;
  1402. QWebEngineUrlScheme__init(&urlScheme, urlSchemeName);
  1403. QWebEngineUrlScheme_setSyntax(&urlScheme, 3 /* QWebEngineUrlScheme::Syntax::Path */);
  1404. QWebEngineUrlScheme_setFlags(&urlScheme, urlSchemeFlags);
  1405. QWebEngineUrlScheme_registerScheme(urlScheme);
  1406. if (qtVersion == 5)
  1407. {
  1408. QApplication_setAttribute(10 /* Qt::AA_X11InitThreads */, true);
  1409. QApplication_setAttribute(13 /* Qt::AA_UseHighDpiPixmaps */, true);
  1410. QApplication_setAttribute(20 /* Qt::AA_EnableHighDpiScaling */, true);
  1411. }
  1412. static int argc = 1;
  1413. static char argv0[] = "dpf-webview";
  1414. static char* argv[] = { argv0, nullptr };
  1415. QApplication app;
  1416. QApplication__init(&app, argc, argv, 0);
  1417. EventFilterQObject eventFilter(shmptr);
  1418. QString qstrchannel, qstrmcode, qstrurl;
  1419. {
  1420. static constexpr const char* channel_src = "external";
  1421. const size_t channel_len = std::strlen(channel_src);
  1422. ushort* const channel_qchar = new ushort[channel_len + 1];
  1423. for (size_t i = 0; i < channel_len; ++i)
  1424. channel_qchar[i] = channel_src[i];
  1425. channel_qchar[channel_len] = 0;
  1426. QString__init(&qstrchannel, reinterpret_cast<QChar*>(channel_qchar), channel_len);
  1427. delete[] channel_qchar;
  1428. }
  1429. {
  1430. static constexpr const char* mcode_src = "\
  1431. function postMessage(m){qt.webChannelTransport.send(JSON.stringify({\
  1432. \"type\":6, \
  1433. \"id\": \"WebSender\",\
  1434. \"__QObject*__\": true,\
  1435. \"object\": \"external\", \
  1436. \"method\": \"sendMessage\",\
  1437. \"args\":[{\"m\":m}], \
  1438. }));}";
  1439. const size_t mcode_len = std::strlen(mcode_src);
  1440. ushort* const mcode_qchar = new ushort[mcode_len + 1];
  1441. for (size_t i = 0; i < mcode_len; ++i)
  1442. mcode_qchar[i] = mcode_src[i];
  1443. mcode_qchar[mcode_len] = 0;
  1444. QString__init(&qstrmcode, reinterpret_cast<QChar*>(mcode_qchar), mcode_len);
  1445. delete[] mcode_qchar;
  1446. }
  1447. {
  1448. const size_t url_len = std::strlen(url);
  1449. ushort* const url_qchar = new ushort[url_len + 1];
  1450. for (size_t i = 0; i < url_len; ++i)
  1451. url_qchar[i] = url[i];
  1452. url_qchar[url_len] = 0;
  1453. QString__init(&qstrurl, reinterpret_cast<QChar*>(url_qchar), url_len);
  1454. delete[] url_qchar;
  1455. }
  1456. QUrl qurl;
  1457. QUrl__init(&qurl, qstrurl, 1 /* QUrl::StrictMode */);
  1458. QWebEngineProfile* const profile = QWebEngineProfile_defaultProfile();
  1459. QWebEngineScriptCollection* const scripts = QWebEngineProfile_scripts(profile);
  1460. QWebEngineSettings* const settings = QWebEngineProfile_settings(profile);
  1461. {
  1462. QWebEngineScript mscript;
  1463. QWebEngineScript__init(&mscript);
  1464. QWebEngineScript_setInjectionPoint(&mscript, 2 /* QWebEngineScript::DocumentCreation */);
  1465. QWebEngineScript_setRunsOnSubFrames(&mscript, true);
  1466. QWebEngineScript_setSourceCode(&mscript, qstrmcode);
  1467. QWebEngineScript_setWorldId(&mscript, 0 /* QWebEngineScript::MainWorld */);
  1468. QWebEngineScriptCollection_insert(scripts, mscript);
  1469. }
  1470. if (initialJS != nullptr)
  1471. {
  1472. QString qstrcode;
  1473. {
  1474. const size_t code_len = std::strlen(initialJS);
  1475. ushort* const code_qchar = new ushort[code_len + 1];
  1476. for (size_t i = 0; i < code_len; ++i)
  1477. code_qchar[i] = initialJS[i];
  1478. code_qchar[code_len] = 0;
  1479. QString__init(&qstrcode, reinterpret_cast<QChar*>(code_qchar), code_len);
  1480. }
  1481. QWebEngineScript script;
  1482. QWebEngineScript__init(&script);
  1483. QWebEngineScript_setInjectionPoint(&script, 2 /* QWebEngineScript::DocumentCreation */);
  1484. QWebEngineScript_setRunsOnSubFrames(&script, true);
  1485. QWebEngineScript_setSourceCode(&script, qstrcode);
  1486. QWebEngineScript_setWorldId(&script, 0 /* QWebEngineScript::MainWorld */);
  1487. QWebEngineScriptCollection_insert(scripts, script);
  1488. }
  1489. QWebEngineSettings_setAttribute(settings, 3 /* QWebEngineSettings::JavascriptCanAccessClipboard */, true);
  1490. QWebEngineSettings_setAttribute(settings, 6 /* QWebEngineSettings::LocalContentCanAccessRemoteUrls */, true);
  1491. QWebEngineSettings_setAttribute(settings, 9 /* QWebEngineSettings::LocalContentCanAccessFileUrls */, true);
  1492. QWebEngineSettings_setAttribute(settings, 28 /* QWebEngineSettings::JavascriptCanPaste */, true);
  1493. QWebEngineView webview;
  1494. QWebEngineView__init(&webview, nullptr);
  1495. QWebEnginePage page;
  1496. QWebEnginePage__init(&page, profile, reinterpret_cast<QObject*>(&webview));
  1497. // QWebEnginePage_setBackgroundColor(&page, QColor{0,0,0,0});
  1498. QWebChannel channel;
  1499. QWebChannel__init(&channel, reinterpret_cast<QObject*>(&webview));
  1500. QWebChannel_registerObject(&channel, qstrchannel, &eventFilter);
  1501. QWebEnginePage_setWebChannel(&page, &channel, 0);
  1502. QWebEngineView_move(&webview, QPoint(x, y));
  1503. QWebEngineView_resize(&webview, QSize(static_cast<int>(width), static_cast<int>(height)));
  1504. QWebEngineView_winId(&webview);
  1505. QWindow_setParent(QWebEngineView_windowHandle(&webview), QWindow_fromWinId(winId));
  1506. QWebEngineView_setPage(&webview, &page);
  1507. QWebEngineView_setUrl(&webview, qurl);
  1508. // FIXME Qt6 seems to need some forcing..
  1509. if (qtVersion >= 6)
  1510. {
  1511. XReparentWindow(display, QWebEngineView_winId(&webview), winId, x, y);
  1512. XFlush(display);
  1513. }
  1514. QWebEngineView_show(&webview);
  1515. struct QtWebFramework : WebFramework {
  1516. const int _qtVersion;
  1517. WebViewRingBuffer* const _shmptr;
  1518. const QUrl& _qurl;
  1519. QWebEnginePage& _page;
  1520. QWebEngineView& _webview;
  1521. EventFilterQObject& _eventFilter;
  1522. const QString__init_t _QString__init;
  1523. const QWebEnginePage_runJavaScript_compat_t _QWebEnginePage_runJavaScript_compat;
  1524. const QWebEnginePage_runJavaScript_t _QWebEnginePage_runJavaScript;
  1525. const QWebEngineView_setUrl_t _QWebEngineView_setUrl;
  1526. const QApplication_quit_t _QApplication_quit;
  1527. const QEvent__init_t _QEvent__init;
  1528. const QApplication_postEvent_t _QApplication_postEvent;
  1529. QtWebFramework(const int qtVersion,
  1530. WebViewRingBuffer* const shmptr,
  1531. const QUrl& qurl,
  1532. QWebEnginePage& page,
  1533. QWebEngineView& webview,
  1534. EventFilterQObject& eventFilter,
  1535. const QString__init_t QString__init,
  1536. const QWebEnginePage_runJavaScript_compat_t QWebEnginePage_runJavaScript_compat,
  1537. const QWebEnginePage_runJavaScript_t QWebEnginePage_runJavaScript,
  1538. const QWebEngineView_setUrl_t QWebEngineView_setUrl,
  1539. const QApplication_quit_t QApplication_quit,
  1540. const QEvent__init_t QEvent__init,
  1541. const QApplication_postEvent_t QApplication_postEvent)
  1542. : _qtVersion(qtVersion),
  1543. _shmptr(shmptr),
  1544. _qurl(qurl),
  1545. _page(page),
  1546. _webview(webview),
  1547. _eventFilter(eventFilter),
  1548. _QString__init(QString__init),
  1549. _QWebEnginePage_runJavaScript_compat(QWebEnginePage_runJavaScript_compat),
  1550. _QWebEnginePage_runJavaScript(QWebEnginePage_runJavaScript),
  1551. _QWebEngineView_setUrl(QWebEngineView_setUrl),
  1552. _QApplication_quit(QApplication_quit),
  1553. _QEvent__init(QEvent__init),
  1554. _QApplication_postEvent(QApplication_postEvent) {}
  1555. void evaluate(const char* const js) override
  1556. {
  1557. QString qstrjs;
  1558. {
  1559. const size_t js_len = std::strlen(js);
  1560. ushort* const js_qchar = new ushort[js_len + 1];
  1561. for (size_t i = 0; i < js_len; ++i)
  1562. js_qchar[i] = js[i];
  1563. js_qchar[js_len] = 0;
  1564. _QString__init(&qstrjs, reinterpret_cast<const QChar*>(js_qchar), js_len);
  1565. }
  1566. if (_qtVersion == 5)
  1567. _QWebEnginePage_runJavaScript_compat(&_page, qstrjs);
  1568. else
  1569. _QWebEnginePage_runJavaScript(&_page, qstrjs, 0,
  1570. #ifdef DISTRHO_PROPER_CPP11_SUPPORT
  1571. {}
  1572. #else
  1573. 0
  1574. #endif
  1575. );
  1576. }
  1577. void reload() override
  1578. {
  1579. _QWebEngineView_setUrl(&_webview, _qurl);
  1580. }
  1581. void terminate() override
  1582. {
  1583. if (running)
  1584. {
  1585. running = false;
  1586. webview_wake(&_shmptr->client.sem);
  1587. _QApplication_quit();
  1588. }
  1589. }
  1590. void wake(WebViewRingBuffer*) override
  1591. {
  1592. // NOTE event pointer is deleted by Qt
  1593. QEvent* const qevent = new QEvent;
  1594. _QEvent__init(qevent, 1000 /* QEvent::User */);
  1595. _QApplication_postEvent(&_eventFilter, qevent, 1 /* Qt::HighEventPriority */);
  1596. }
  1597. };
  1598. QtWebFramework webFrameworkObj(qtVersion,
  1599. shmptr,
  1600. qurl,
  1601. page,
  1602. webview,
  1603. eventFilter,
  1604. QString__init,
  1605. QWebEnginePage_runJavaScript_compat,
  1606. QWebEnginePage_runJavaScript,
  1607. QWebEngineView_setUrl,
  1608. QApplication_quit,
  1609. QEvent__init,
  1610. QApplication_postEvent);
  1611. webFramework = &webFrameworkObj;
  1612. // notify server we started ok
  1613. webview_wake(&shmptr->server.sem);
  1614. d_stdout("WebView Qt%d main loop started", qtVersion);
  1615. QApplication_exec();
  1616. d_stdout("WebView Qt%d main loop quit", qtVersion);
  1617. dlclose(lib);
  1618. return true;
  1619. }
  1620. // -----------------------------------------------------------------------------------------------------------
  1621. // startup via ld-linux
  1622. static void signalHandler(const int sig)
  1623. {
  1624. switch (sig)
  1625. {
  1626. case SIGTERM:
  1627. webFramework->terminate();
  1628. break;
  1629. }
  1630. }
  1631. static void* threadHandler(void* const ptr)
  1632. {
  1633. WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(ptr);
  1634. while (running && shmptr->valid)
  1635. {
  1636. if (webview_timedwait(&shmptr->client.sem) && running)
  1637. webFramework->wake(shmptr);
  1638. }
  1639. return nullptr;
  1640. }
  1641. int dpf_webview_start(const int argc, char* argv[])
  1642. {
  1643. if (argc != 3)
  1644. {
  1645. d_stderr("WebView entry point, nothing to see here! ;)");
  1646. return 1;
  1647. }
  1648. d_stdout("starting... %d '%s' '%s'", argc, argv[1], argv[2]);
  1649. uselocale(newlocale(LC_NUMERIC_MASK, "C", nullptr));
  1650. Display* const display = XOpenDisplay(nullptr);
  1651. DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1);
  1652. const char* const shmname = argv[2];
  1653. const int shmfd = shm_open(shmname, O_RDWR, 0);
  1654. if (shmfd < 0)
  1655. {
  1656. d_stderr("shm_open failed: %s", std::strerror(errno));
  1657. return 1;
  1658. }
  1659. WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(mmap(nullptr,
  1660. sizeof(WebViewRingBuffer),
  1661. PROT_READ|PROT_WRITE,
  1662. MAP_SHARED,
  1663. shmfd, 0));
  1664. if (shmptr == nullptr || shmptr == nullptr)
  1665. {
  1666. d_stderr("mmap failed: %s", std::strerror(errno));
  1667. close(shmfd);
  1668. return 1;
  1669. }
  1670. RingBufferControl<WebViewSharedBuffer> rbctrl;
  1671. rbctrl.setRingBuffer(&shmptr->client, false);
  1672. // fetch initial data
  1673. bool hasInitialData = false;
  1674. Window winId = 0;
  1675. uint width = 0, height = 0;
  1676. double scaleFactor = 0;
  1677. int x = 0, y = 0;
  1678. char* url = nullptr;
  1679. char* initJS = nullptr;
  1680. while (shmptr->valid && webview_timedwait(&shmptr->client.sem))
  1681. {
  1682. if (rbctrl.isDataAvailableForReading())
  1683. {
  1684. DISTRHO_SAFE_ASSERT_RETURN(rbctrl.readUInt() == kWebViewMessageInitData, 1);
  1685. hasInitialData = running = true;
  1686. winId = rbctrl.readULong();
  1687. width = rbctrl.readUInt();
  1688. height = rbctrl.readUInt();
  1689. scaleFactor = rbctrl.readDouble();
  1690. x = rbctrl.readInt();
  1691. y = rbctrl.readInt();
  1692. const uint urllen = rbctrl.readUInt();
  1693. url = static_cast<char*>(std::malloc(urllen));
  1694. rbctrl.readCustomData(url, urllen);
  1695. if (const uint initjslen = rbctrl.readUInt())
  1696. {
  1697. initJS = static_cast<char*>(std::malloc(initjslen));
  1698. rbctrl.readCustomData(initJS, initjslen);
  1699. }
  1700. }
  1701. }
  1702. pthread_t thread;
  1703. if (hasInitialData && pthread_create(&thread, nullptr, threadHandler, shmptr) == 0)
  1704. {
  1705. d_stdout("WebView IPC in place, starting engine...");
  1706. struct sigaction sig = {};
  1707. sig.sa_handler = signalHandler;
  1708. sig.sa_flags = SA_RESTART;
  1709. sigemptyset(&sig.sa_mask);
  1710. sigaction(SIGTERM, &sig, nullptr);
  1711. if (! qtwebengine(5, display, winId, x, y, width, height, scaleFactor, url, initJS, shmptr) &&
  1712. ! qtwebengine(6, display, winId, x, y, width, height, scaleFactor, url, initJS, shmptr) &&
  1713. ! gtk3(display, winId, x, y, width, height, scaleFactor, url, initJS, shmptr))
  1714. {
  1715. d_stderr("Failed to find usable WebView platform");
  1716. }
  1717. shmptr->valid = running = false;
  1718. pthread_join(thread, nullptr);
  1719. }
  1720. else
  1721. {
  1722. d_stderr("Failed to setup WebView IPC");
  1723. }
  1724. std::free(initJS);
  1725. munmap(shmptr, sizeof(WebViewRingBuffer));
  1726. close(shmfd);
  1727. XCloseDisplay(display);
  1728. return 0;
  1729. }
  1730. // --------------------------------------------------------------------------------------------------------------------
  1731. #endif // WEB_VIEW_USING_X11_IPC
  1732. #ifdef WEB_VIEW_DGL_NAMESPACE
  1733. END_NAMESPACE_DGL
  1734. #else
  1735. END_NAMESPACE_DISTRHO
  1736. #endif
  1737. #undef MACRO_NAME
  1738. #undef MACRO_NAME2
  1739. #undef WEB_VIEW_DISTRHO_NAMESPACE
  1740. #undef WEB_VIEW_DGL_NAMESPACE