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.

1906 lines
68KB

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