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.

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