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.

2064 lines
75KB

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