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.

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