Collection of DPF-based plugins for packaging
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.

1494 lines
51KB

  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. // #include <gtk/gtk.h>
  23. // #include <gtk/gtkx.h>
  24. // #include <webkit2/webkit2.h>
  25. #ifdef DISTRHO_OS_MAC
  26. # define WEB_VIEW_USING_MACOS_WEBKIT 1
  27. #else
  28. # define WEB_VIEW_USING_MACOS_WEBKIT 0
  29. #endif
  30. #ifdef DISTRHO_OS_WINDOWS
  31. # define WEB_VIEW_USING_CHOC 1
  32. #else
  33. # define WEB_VIEW_USING_CHOC 0
  34. #endif
  35. #if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX)
  36. # define WEB_VIEW_USING_X11_IPC 1
  37. #else
  38. # define WEB_VIEW_USING_X11_IPC 0
  39. #endif
  40. #if WEB_VIEW_USING_CHOC
  41. # include "WebViewWin32.hpp"
  42. # include "String.hpp"
  43. #elif WEB_VIEW_USING_MACOS_WEBKIT
  44. # include <Cocoa/Cocoa.h>
  45. # include <WebKit/WebKit.h>
  46. #elif WEB_VIEW_USING_X11_IPC
  47. // #define QT_NO_VERSION_TAGGING
  48. // #include <QtCore/QChar>
  49. // #include <QtCore/QPoint>
  50. // #include <QtCore/QSize>
  51. // #undef signals
  52. # include "ChildProcess.hpp"
  53. # include "RingBuffer.hpp"
  54. # include "String.hpp"
  55. # include <clocale>
  56. # include <cstdio>
  57. # include <functional>
  58. # include <dlfcn.h>
  59. # include <fcntl.h>
  60. # include <pthread.h>
  61. # include <unistd.h>
  62. # include <sys/mman.h>
  63. # include <X11/Xlib.h>
  64. # ifdef __linux__
  65. # include <syscall.h>
  66. # include <linux/futex.h>
  67. # include <linux/limits.h>
  68. # else
  69. # include <semaphore.h>
  70. # endif
  71. #endif
  72. // -----------------------------------------------------------------------------------------------------------
  73. #if WEB_VIEW_USING_MACOS_WEBKIT
  74. #define MACRO_NAME2(a, b, c) a ## b ## c
  75. #define MACRO_NAME(a, b, c) MACRO_NAME2(a, b, c)
  76. #define WEB_VIEW_DELEGATE_CLASS_NAME \
  77. MACRO_NAME(WebViewDelegate_, _, DISTRHO_NAMESPACE)
  78. @interface WEB_VIEW_DELEGATE_CLASS_NAME : NSObject<WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate>
  79. @end
  80. @implementation WEB_VIEW_DELEGATE_CLASS_NAME {
  81. @public
  82. WebViewMessageCallback callback;
  83. void* callbackPtr;
  84. bool loaded;
  85. }
  86. - (void)webView:(WKWebView *)webview
  87. didFinishNavigation:(WKNavigation*)navigation
  88. {
  89. d_stdout("page loaded");
  90. loaded = true;
  91. }
  92. - (void)webView:(WKWebView*)webview
  93. runJavaScriptAlertPanelWithMessage:(NSString*)message
  94. initiatedByFrame:(WKFrameInfo*)frame
  95. completionHandler:(void (^)(void))completionHandler
  96. {
  97. NSAlert* const alert = [[NSAlert alloc] init];
  98. [alert addButtonWithTitle:@"OK"];
  99. [alert setInformativeText:message];
  100. [alert setMessageText:@"Alert"];
  101. dispatch_async(dispatch_get_main_queue(), ^
  102. {
  103. [alert beginSheetModalForWindow:[webview window]
  104. completionHandler:^(NSModalResponse)
  105. {
  106. completionHandler();
  107. [alert release];
  108. }];
  109. });
  110. }
  111. - (void)webView:(WKWebView*)webview
  112. runJavaScriptConfirmPanelWithMessage:(NSString*)message
  113. initiatedByFrame:(WKFrameInfo*)frame
  114. completionHandler:(void (^)(BOOL))completionHandler
  115. {
  116. NSAlert* const alert = [[NSAlert alloc] init];
  117. [alert addButtonWithTitle:@"OK"];
  118. [alert addButtonWithTitle:@"Cancel"];
  119. [alert setInformativeText:message];
  120. [alert setMessageText:@"Confirm"];
  121. dispatch_async(dispatch_get_main_queue(), ^
  122. {
  123. [alert beginSheetModalForWindow:[webview window]
  124. completionHandler:^(NSModalResponse result)
  125. {
  126. completionHandler(result == NSAlertFirstButtonReturn);
  127. [alert release];
  128. }];
  129. });
  130. }
  131. - (void)webView:(WKWebView*)webview
  132. runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt
  133. defaultText:(NSString*)defaultText
  134. initiatedByFrame:(WKFrameInfo*)frame
  135. completionHandler:(void (^)(NSString*))completionHandler
  136. {
  137. NSTextField* const input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 250, 30)];
  138. [input setStringValue:defaultText];
  139. NSAlert* const alert = [[NSAlert alloc] init];
  140. [alert setAccessoryView:input];
  141. [alert addButtonWithTitle:@"OK"];
  142. [alert addButtonWithTitle:@"Cancel"];
  143. [alert setInformativeText:prompt];
  144. [alert setMessageText: @"Prompt"];
  145. dispatch_async(dispatch_get_main_queue(), ^
  146. {
  147. [alert beginSheetModalForWindow:[webview window]
  148. completionHandler:^(NSModalResponse result)
  149. {
  150. [input validateEditing];
  151. completionHandler(result == NSAlertFirstButtonReturn ? [input stringValue] : nil);
  152. [alert release];
  153. }];
  154. });
  155. }
  156. - (void)webView:(WKWebView*)webview
  157. runOpenPanelWithParameters:(WKOpenPanelParameters*)params
  158. initiatedByFrame:(WKFrameInfo*)frame
  159. completionHandler:(void (^)(NSArray<NSURL*>*))completionHandler
  160. {
  161. NSOpenPanel* const panel = [[NSOpenPanel alloc] init];
  162. [panel setAllowsMultipleSelection:[params allowsMultipleSelection]];
  163. // [panel setAllowedFileTypes:(NSArray<NSString*>*)[params _allowedFileExtensions]];
  164. [panel setCanChooseDirectories:[params allowsDirectories]];
  165. [panel setCanChooseFiles:![params allowsDirectories]];
  166. dispatch_async(dispatch_get_main_queue(), ^
  167. {
  168. [panel beginSheetModalForWindow:[webview window]
  169. completionHandler:^(NSModalResponse result)
  170. {
  171. completionHandler(result == NSModalResponseOK ? [panel URLs] : nil);
  172. [panel release];
  173. }];
  174. });
  175. }
  176. - (void)userContentController:(WKUserContentController*)userContentController
  177. didReceiveScriptMessage:(WKScriptMessage*)message
  178. {
  179. NSString* const nsstring = static_cast<NSString*>([message body]);
  180. char* const string = strdup([nsstring UTF8String]);
  181. d_debug("JS call received '%s' %p", string, callback);
  182. if (callback != nullptr)
  183. callback(callbackPtr, string);
  184. std::free(string);
  185. }
  186. @end
  187. #endif // WEB_VIEW_USING_MACOS_WEBKIT
  188. // -----------------------------------------------------------------------------------------------------------
  189. #ifdef WEB_VIEW_DGL_NAMESPACE
  190. START_NAMESPACE_DGL
  191. using DISTRHO_NAMESPACE::String;
  192. #else
  193. START_NAMESPACE_DISTRHO
  194. #endif
  195. // -----------------------------------------------------------------------------------------------------------
  196. #if WEB_VIEW_USING_X11_IPC
  197. #ifdef __linux__
  198. typedef int32_t ipc_sem_t;
  199. #else
  200. typedef sem_t ipc_sem_t;
  201. #endif
  202. enum WebViewMessageType {
  203. kWebViewMessageNull,
  204. kWebViewMessageInitData,
  205. kWebViewMessageEvaluateJS,
  206. kWebViewMessageCallback,
  207. kWebViewMessageReload
  208. };
  209. struct WebViewSharedBuffer {
  210. static constexpr const uint32_t size = 0x100000;
  211. ipc_sem_t sem;
  212. uint32_t head, tail, wrtn;
  213. bool invalidateCommit;
  214. uint8_t buf[size];
  215. };
  216. struct WebViewRingBuffer {
  217. WebViewSharedBuffer server;
  218. WebViewSharedBuffer client;
  219. bool valid;
  220. };
  221. static void webview_wake(ipc_sem_t* const sem)
  222. {
  223. #ifdef __linux__
  224. if (__sync_bool_compare_and_swap(sem, 0, 1))
  225. syscall(SYS_futex, sem, FUTEX_WAKE, 1, nullptr, nullptr, 0);
  226. #else
  227. sem_post(sem);
  228. #endif
  229. }
  230. static bool webview_timedwait(ipc_sem_t* const sem)
  231. {
  232. #ifdef __linux__
  233. const struct timespec timeout = { 1, 0 };
  234. for (;;)
  235. {
  236. if (__sync_bool_compare_and_swap(sem, 1, 0))
  237. return true;
  238. if (syscall(SYS_futex, sem, FUTEX_WAIT, 0, &timeout, nullptr, 0) != 0)
  239. if (errno != EAGAIN && errno != EINTR)
  240. return false;
  241. }
  242. #else
  243. struct timespec timeout;
  244. if (clock_gettime(CLOCK_REALTIME, &timeout) != 0)
  245. return false;
  246. timeout.tv_sec += 1;
  247. for (int r;;)
  248. {
  249. r = sem_timedwait(sem, &timeout);
  250. if (r < 0)
  251. r = errno;
  252. if (r == EINTR)
  253. continue;
  254. return r == 0;
  255. }
  256. #endif
  257. }
  258. static void getFilenameFromFunctionPtr(char filename[PATH_MAX], const void* const ptr)
  259. {
  260. Dl_info info = {};
  261. dladdr(ptr, &info);
  262. if (info.dli_fname[0] == '.' && info.dli_fname[1] != '.')
  263. {
  264. if (getcwd(filename, PATH_MAX - 1) == nullptr)
  265. filename[0] = '\0';
  266. std::strncat(filename, info.dli_fname + 1, PATH_MAX - 1);
  267. }
  268. else if (info.dli_fname[0] != '/')
  269. {
  270. if (getcwd(filename, PATH_MAX - 1) == nullptr)
  271. filename[0] = '\0';
  272. else
  273. std::strncat(filename, "/", PATH_MAX - 1);
  274. std::strncat(filename, info.dli_fname, PATH_MAX - 1);
  275. }
  276. else
  277. {
  278. std::strncpy(filename, info.dli_fname, PATH_MAX - 1);
  279. }
  280. }
  281. #endif // WEB_VIEW_USING_X11_IPC
  282. // -----------------------------------------------------------------------------------------------------------
  283. struct WebViewData {
  284. #if WEB_VIEW_USING_CHOC
  285. WebView* webview;
  286. String url;
  287. #elif WEB_VIEW_USING_MACOS_WEBKIT
  288. NSView* view;
  289. WKWebView* webview;
  290. NSURLRequest* urlreq;
  291. WEB_VIEW_DELEGATE_CLASS_NAME* delegate;
  292. #elif WEB_VIEW_USING_X11_IPC
  293. int shmfd = 0;
  294. char shmname[128] = {};
  295. WebViewRingBuffer* shmptr = nullptr;
  296. WebViewMessageCallback callback = nullptr;
  297. void* callbackPtr = nullptr;
  298. ChildProcess p;
  299. RingBufferControl<WebViewSharedBuffer> rbctrl, rbctrl2;
  300. ::Display* display = nullptr;
  301. ::Window childWindow = 0;
  302. ::Window ourWindow = 0;
  303. #endif
  304. WebViewData() {}
  305. DISTRHO_DECLARE_NON_COPYABLE(WebViewData);
  306. };
  307. // -----------------------------------------------------------------------------------------------------------
  308. WebViewHandle webViewCreate(const char* const url,
  309. const uintptr_t windowId,
  310. const uint initialWidth,
  311. const uint initialHeight,
  312. const double scaleFactor,
  313. const WebViewOptions& options)
  314. {
  315. #if WEB_VIEW_USING_CHOC
  316. WebView* const webview = webview_choc_create(options);
  317. if (webview == nullptr)
  318. return nullptr;
  319. const HWND hwnd = static_cast<HWND>(webview_choc_handle(webview));
  320. LONG_PTR flags = GetWindowLongPtr(hwnd, -16);
  321. flags = (flags & ~WS_POPUP) | WS_CHILD;
  322. SetWindowLongPtr(hwnd, -16, flags);
  323. SetParent(hwnd, reinterpret_cast<HWND>(windowId));
  324. SetWindowPos(hwnd, nullptr,
  325. options.offset.x,
  326. options.offset.y,
  327. initialWidth - options.offset.x,
  328. initialHeight - options.offset.y,
  329. SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
  330. ShowWindow(hwnd, SW_SHOW);
  331. WebViewData* const whandle = new WebViewData;
  332. whandle->webview = webview;
  333. whandle->url = url;
  334. webview_choc_navigate(webview, url);
  335. return whandle;
  336. #elif WEB_VIEW_USING_MACOS_WEBKIT
  337. NSView* const view = reinterpret_cast<NSView*>(windowId);
  338. WKPreferences* const prefs = [[WKPreferences alloc] init];
  339. [prefs setValue:@YES forKey:@"javaScriptCanAccessClipboard"];
  340. [prefs setValue:@YES forKey:@"DOMPasteAllowed"];
  341. // if (debug)
  342. {
  343. [prefs setValue:@YES forKey:@"developerExtrasEnabled"];
  344. // TODO enable_write_console_messages_to_stdout
  345. }
  346. WKWebViewConfiguration* const config = [[WKWebViewConfiguration alloc] init];
  347. config.limitsNavigationsToAppBoundDomains = false;
  348. config.preferences = prefs;
  349. const CGRect rect = CGRectMake(options.offset.x / scaleFactor,
  350. options.offset.y / scaleFactor,
  351. initialWidth / scaleFactor,
  352. initialHeight / scaleFactor);
  353. WKWebView* const webview = [[WKWebView alloc] initWithFrame:rect
  354. configuration:config];
  355. [webview setHidden:YES];
  356. [view addSubview:webview];
  357. // TODO webkit_web_view_set_background_color
  358. WEB_VIEW_DELEGATE_CLASS_NAME* const delegate = [[WEB_VIEW_DELEGATE_CLASS_NAME alloc] init];
  359. delegate->callback = options.callback;
  360. delegate->callbackPtr = options.callbackPtr;
  361. delegate->loaded = false;
  362. if (WKUserContentController* const controller = [config userContentController])
  363. {
  364. [controller addScriptMessageHandler:delegate name:@"external"];
  365. WKUserScript* const mscript = [[WKUserScript alloc]
  366. initWithSource:(options.callback != nullptr
  367. ? @"function postMessage(m){window.webkit.messageHandlers.external.postMessage(m)}"
  368. : @"function postMessage(m){}")
  369. injectionTime:WKUserScriptInjectionTimeAtDocumentStart
  370. forMainFrameOnly:true
  371. ];
  372. [controller addUserScript:mscript];
  373. [mscript release];
  374. if (options.initialJS != nullptr)
  375. {
  376. NSString* const nsInitialJS = [[NSString alloc] initWithBytes:options.initialJS
  377. length:std::strlen(options.initialJS)
  378. encoding:NSUTF8StringEncoding];
  379. WKUserScript* const script = [[WKUserScript alloc] initWithSource:nsInitialJS
  380. injectionTime:WKUserScriptInjectionTimeAtDocumentStart
  381. forMainFrameOnly:true];
  382. [controller addUserScript:script];
  383. [script release];
  384. [nsInitialJS release];
  385. }
  386. }
  387. [webview setNavigationDelegate:delegate];
  388. [webview setUIDelegate:delegate];
  389. NSString* const nsurl = [[NSString alloc] initWithBytes:url
  390. length:std::strlen(url)
  391. encoding:NSUTF8StringEncoding];
  392. NSURLRequest* const urlreq = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: nsurl]];
  393. d_stdout("url is '%s'", url);
  394. if (std::strncmp(url, "file://", 7) == 0)
  395. {
  396. const char* const lastsep = std::strrchr(url + 7, '/');
  397. NSString* const urlpath = [[NSString alloc] initWithBytes:url
  398. length:(lastsep - url)
  399. encoding:NSUTF8StringEncoding];
  400. [webview loadFileRequest:urlreq
  401. allowingReadAccessToURL:[NSURL URLWithString:urlpath]];
  402. [urlpath release];
  403. }
  404. else
  405. {
  406. [webview loadRequest:urlreq];
  407. }
  408. d_stdout("waiting for load");
  409. if (! delegate->loaded)
  410. {
  411. NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc] init];
  412. NSDate* const date = [NSDate dateWithTimeIntervalSinceNow:0.05];
  413. NSEvent* event;
  414. while (! delegate->loaded)
  415. {
  416. event = [NSApp
  417. #ifdef __MAC_10_12
  418. nextEventMatchingMask:NSEventMaskAny
  419. #else
  420. nextEventMatchingMask:NSAnyEventMask
  421. #endif
  422. untilDate:date
  423. inMode:NSDefaultRunLoopMode
  424. dequeue:YES];
  425. if (event == nil)
  426. break;
  427. [NSApp sendEvent: event];
  428. }
  429. [pool release];
  430. }
  431. d_stdout("waiting done");
  432. [webview setHidden:NO];
  433. [nsurl release];
  434. [config release];
  435. [prefs release];
  436. WebViewData* const handle = new WebViewData;
  437. handle->view = view;
  438. handle->webview = webview;
  439. handle->urlreq = urlreq;
  440. handle->delegate = delegate;
  441. return handle;
  442. #elif WEB_VIEW_USING_X11_IPC
  443. // get startup paths
  444. char ldlinux[PATH_MAX] = {};
  445. getFilenameFromFunctionPtr(ldlinux, dlsym(nullptr, "_rtld_global"));
  446. char filename[PATH_MAX] = {};
  447. getFilenameFromFunctionPtr(filename, reinterpret_cast<const void*>(webViewCreate));
  448. d_stdout("ld-linux is '%s'", ldlinux);
  449. d_stdout("filename is '%s'", filename);
  450. // setup shared memory
  451. int shmfd;
  452. char shmname[128];
  453. void* shmptr;
  454. for (int i = 0; i < 9999; ++i)
  455. {
  456. snprintf(shmname, sizeof(shmname) - 1, "/dpf-webview-%d", i + 1);
  457. shmfd = shm_open(shmname, O_CREAT|O_EXCL|O_RDWR, 0666);
  458. if (shmfd < 0)
  459. continue;
  460. if (ftruncate(shmfd, sizeof(WebViewRingBuffer)) != 0)
  461. {
  462. close(shmfd);
  463. shm_unlink(shmname);
  464. continue;
  465. }
  466. break;
  467. }
  468. if (shmfd < 0)
  469. {
  470. d_stderr("shm_open failed: %s", strerror(errno));
  471. return nullptr;
  472. }
  473. shmptr = mmap(nullptr, sizeof(WebViewRingBuffer), PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0);
  474. if (shmptr == nullptr || shmptr == MAP_FAILED)
  475. {
  476. d_stderr("mmap failed: %s", strerror(errno));
  477. close(shmfd);
  478. shm_unlink(shmname);
  479. return nullptr;
  480. }
  481. #ifndef __linux__
  482. sem_init(&handle->shmptr->client.sem, 1, 0);
  483. sem_init(&handle->shmptr->server.sem, 1, 0);
  484. #endif
  485. ::Display* const display = XOpenDisplay(nullptr);
  486. DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, nullptr);
  487. // set up custom child environment
  488. uint envsize = 0;
  489. while (environ[envsize] != nullptr)
  490. ++envsize;
  491. char** const envp = new char*[envsize + 5];
  492. {
  493. uint e = 0;
  494. for (uint i = 0; i < envsize; ++i)
  495. {
  496. if (std::strncmp(environ[i], "LD_PRELOAD=", 11) == 0)
  497. continue;
  498. if (std::strncmp(environ[i], "LD_LIBRARY_PATH=", 16) == 0)
  499. continue;
  500. envp[e++] = strdup(environ[i]);
  501. }
  502. envp[e++] = strdup("LANG=en_US.UTF-8");
  503. envp[e++] = ("DPF_WEB_VIEW_SCALE_FACTOR=" + String(scaleFactor)).getAndReleaseBuffer();
  504. envp[e++] = ("DPF_WEB_VIEW_WIN_ID=" +String(windowId)).getAndReleaseBuffer();
  505. for (uint i = e; i < envsize + 5; ++i)
  506. envp[e++] = nullptr;
  507. }
  508. WebViewData* const handle = new WebViewData;
  509. handle->callback = options.callback;
  510. handle->callbackPtr = options.callbackPtr;
  511. handle->shmfd = shmfd;
  512. handle->shmptr = static_cast<WebViewRingBuffer*>(shmptr);
  513. handle->display = display;
  514. handle->ourWindow = windowId;
  515. std::memcpy(handle->shmname, shmname, sizeof(shmname));
  516. handle->shmptr->valid = true;
  517. handle->rbctrl.setRingBuffer(&handle->shmptr->client, false);
  518. handle->rbctrl.flush();
  519. handle->rbctrl2.setRingBuffer(&handle->shmptr->server, false);
  520. handle->rbctrl2.flush();
  521. const char* const args[] = { ldlinux, filename, "dpf-ld-linux-webview", shmname, nullptr };
  522. handle->p.start(args, envp);
  523. for (uint i = 0; envp[i] != nullptr; ++i)
  524. std::free(envp[i]);
  525. delete[] envp;
  526. const size_t urllen = std::strlen(url);
  527. const size_t initjslen = options.initialJS != nullptr ? std::strlen(options.initialJS) + 1 : 0;
  528. handle->rbctrl.writeUInt(kWebViewMessageInitData) &&
  529. handle->rbctrl.writeULong(windowId) &&
  530. handle->rbctrl.writeUInt(initialWidth) &&
  531. handle->rbctrl.writeUInt(initialHeight) &&
  532. handle->rbctrl.writeDouble(scaleFactor) &&
  533. handle->rbctrl.writeInt(options.offset.x) &&
  534. handle->rbctrl.writeInt(options.offset.y) &&
  535. handle->rbctrl.writeUInt(urllen) &&
  536. handle->rbctrl.writeCustomData(url, urllen) &&
  537. handle->rbctrl.writeUInt(initjslen) &&
  538. initjslen != 0 &&
  539. handle->rbctrl.writeCustomData(options.initialJS, initjslen);
  540. handle->rbctrl.commitWrite();
  541. webview_wake(&handle->shmptr->client.sem);
  542. for (int i = 0; i < 5 && handle->p.isRunning(); ++i)
  543. {
  544. if (webview_timedwait(&handle->shmptr->server.sem))
  545. return handle;
  546. }
  547. d_stderr("webview client side failed to start");
  548. webViewDestroy(handle);
  549. return nullptr;
  550. #endif
  551. // maybe unused
  552. (void)windowId;
  553. (void)initialWidth;
  554. (void)initialHeight;
  555. (void)scaleFactor;
  556. (void)options;
  557. return nullptr;
  558. }
  559. void webViewDestroy(const WebViewHandle handle)
  560. {
  561. #if WEB_VIEW_USING_CHOC
  562. webview_choc_destroy(handle->webview);
  563. #elif WEB_VIEW_USING_MACOS_WEBKIT
  564. [handle->webview setHidden:YES];
  565. [handle->webview removeFromSuperview];
  566. [handle->urlreq release];
  567. // [handle->delegate release];
  568. #elif WEB_VIEW_USING_X11_IPC
  569. #ifndef __linux__
  570. sem_destroy(&handle->shmptr->client.sem);
  571. sem_destroy(&handle->shmptr->server.sem);
  572. #endif
  573. munmap(handle->shmptr, sizeof(WebViewRingBuffer));
  574. close(handle->shmfd);
  575. shm_unlink(handle->shmname);
  576. XCloseDisplay(handle->display);
  577. #endif
  578. delete handle;
  579. }
  580. void webViewIdle(const WebViewHandle handle)
  581. {
  582. #if WEB_VIEW_USING_X11_IPC
  583. uint32_t size = 0;
  584. void* buffer = nullptr;
  585. while (handle->rbctrl2.isDataAvailableForReading())
  586. {
  587. switch (handle->rbctrl2.readUInt())
  588. {
  589. case kWebViewMessageCallback:
  590. if (const uint32_t len = handle->rbctrl2.readUInt())
  591. {
  592. if (len > size)
  593. {
  594. size = len;
  595. buffer = std::realloc(buffer, len);
  596. if (buffer == nullptr)
  597. {
  598. d_stderr("server out of memory, abort!");
  599. handle->rbctrl2.flush();
  600. return;
  601. }
  602. }
  603. if (handle->rbctrl2.readCustomData(buffer, len))
  604. {
  605. d_debug("server kWebViewMessageCallback -> '%s'", static_cast<char*>(buffer));
  606. if (handle->callback != nullptr)
  607. handle->callback(handle->callbackPtr, static_cast<char*>(buffer));
  608. continue;
  609. }
  610. }
  611. break;
  612. }
  613. d_stderr("server ringbuffer data race, abort!");
  614. handle->rbctrl2.flush();
  615. return;
  616. }
  617. #else
  618. // unused
  619. (void)handle;
  620. #endif
  621. }
  622. void webViewEvaluateJS(const WebViewHandle handle, const char* const js)
  623. {
  624. #if WEB_VIEW_USING_CHOC
  625. webview_choc_eval(handle->webview, js);
  626. #elif WEB_VIEW_USING_MACOS_WEBKIT
  627. NSString* const nsjs = [[NSString alloc] initWithBytes:js
  628. length:std::strlen(js)
  629. encoding:NSUTF8StringEncoding];
  630. [handle->webview evaluateJavaScript:nsjs completionHandler:nil];
  631. [nsjs release];
  632. #elif WEB_VIEW_USING_X11_IPC
  633. d_debug("evaluateJS '%s'", js);
  634. const size_t len = std::strlen(js) + 1;
  635. handle->rbctrl.writeUInt(kWebViewMessageEvaluateJS) &&
  636. handle->rbctrl.writeUInt(len) &&
  637. handle->rbctrl.writeCustomData(js, len);
  638. if (handle->rbctrl.commitWrite())
  639. webview_wake(&handle->shmptr->client.sem);
  640. #endif
  641. // maybe unused
  642. (void)handle;
  643. (void)js;
  644. }
  645. void webViewReload(const WebViewHandle handle)
  646. {
  647. #if WEB_VIEW_USING_CHOC
  648. webview_choc_navigate(handle->webview, handle->url);
  649. #elif WEB_VIEW_USING_MACOS_WEBKIT
  650. [handle->webview loadRequest:handle->urlreq];
  651. #elif WEB_VIEW_USING_X11_IPC
  652. d_stdout("reload");
  653. handle->rbctrl.writeUInt(kWebViewMessageReload);
  654. if (handle->rbctrl.commitWrite())
  655. webview_wake(&handle->shmptr->client.sem);
  656. #endif
  657. // maybe unused
  658. (void)handle;
  659. }
  660. void webViewResize(const WebViewHandle handle, const uint width, const uint height, const double scaleFactor)
  661. {
  662. #if WEB_VIEW_USING_CHOC
  663. const HWND hwnd = static_cast<HWND>(webview_choc_handle(handle->webview));
  664. SetWindowPos(hwnd, nullptr, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  665. #elif WEB_VIEW_USING_MACOS_WEBKIT
  666. [handle->webview setFrameSize:NSMakeSize(width / scaleFactor, height / scaleFactor)];
  667. #elif WEB_VIEW_USING_X11_IPC
  668. if (handle->childWindow == 0)
  669. {
  670. ::Window rootWindow, parentWindow;
  671. ::Window* childWindows = nullptr;
  672. uint numChildren = 0;
  673. XFlush(handle->display);
  674. XQueryTree(handle->display, handle->ourWindow, &rootWindow, &parentWindow, &childWindows, &numChildren);
  675. if (numChildren == 0 || childWindows == nullptr)
  676. return;
  677. handle->childWindow = childWindows[0];
  678. XFree(childWindows);
  679. }
  680. XResizeWindow(handle->display, handle->childWindow, width, height);
  681. XFlush(handle->display);
  682. #endif
  683. // maybe unused
  684. (void)handle;
  685. (void)width;
  686. (void)height;
  687. (void)scaleFactor;
  688. }
  689. #if WEB_VIEW_USING_X11_IPC
  690. // -----------------------------------------------------------------------------------------------------------
  691. static std::function<void(const char* js)> evaluateFn;
  692. static std::function<void()> reloadFn;
  693. static std::function<void()> terminateFn;
  694. static std::function<void(WebViewRingBuffer* rb)> wakeFn;
  695. // -----------------------------------------------------------------------------------------------------------
  696. struct GtkContainer;
  697. struct GtkPlug;
  698. struct GtkWidget;
  699. struct GtkWindow;
  700. struct JSCValue;
  701. struct WebKitJavascriptResult;
  702. struct WebKitSettings;
  703. struct WebKitUserContentManager;
  704. struct WebKitUserScript;
  705. struct WebKitWebView;
  706. typedef int gboolean;
  707. #define G_CALLBACK(p) reinterpret_cast<void*>(p)
  708. #define GTK_CONTAINER(p) reinterpret_cast<GtkContainer*>(p)
  709. #define GTK_PLUG(p) reinterpret_cast<GtkPlug*>(p)
  710. #define GTK_WINDOW(p) reinterpret_cast<GtkWindow*>(p)
  711. #define WEBKIT_WEB_VIEW(p) reinterpret_cast<WebKitWebView*>(p)
  712. // struct QApplication;
  713. // struct QUrl;
  714. // struct QWebEngineView;
  715. // struct QWindow;
  716. // -----------------------------------------------------------------------------------------------------------
  717. #define JOIN(A, B) A ## B
  718. #define AUTOSYM(S) \
  719. using JOIN(gtk3_, S) = decltype(&S); \
  720. JOIN(gtk3_, S) S = reinterpret_cast<JOIN(gtk3_, S)>(dlsym(nullptr, #S)); \
  721. DISTRHO_SAFE_ASSERT_RETURN(S != nullptr, false);
  722. #define CSYM(S, NAME) \
  723. S NAME = reinterpret_cast<S>(dlsym(nullptr, #NAME)); \
  724. DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false);
  725. #define CPPSYM(S, NAME, SN) \
  726. S NAME = reinterpret_cast<S>(dlsym(nullptr, #SN)); \
  727. DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false);
  728. // -----------------------------------------------------------------------------------------------------------
  729. // gtk3 variant
  730. static void gtk3_idle(void* const ptr)
  731. {
  732. WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(ptr);
  733. RingBufferControl<WebViewSharedBuffer> rbctrl;
  734. rbctrl.setRingBuffer(&shmptr->client, false);
  735. uint32_t size = 0;
  736. void* buffer = nullptr;
  737. while (rbctrl.isDataAvailableForReading())
  738. {
  739. switch (rbctrl.readUInt())
  740. {
  741. case kWebViewMessageEvaluateJS:
  742. if (const uint32_t len = rbctrl.readUInt())
  743. {
  744. if (len > size)
  745. {
  746. size = len;
  747. buffer = realloc(buffer, len);
  748. if (buffer == nullptr)
  749. {
  750. d_stderr("lv2ui client out of memory, abort!");
  751. abort();
  752. }
  753. }
  754. if (rbctrl.readCustomData(buffer, len))
  755. {
  756. d_debug("client kWebViewMessageEvaluateJS -> '%s'", static_cast<char*>(buffer));
  757. evaluateFn(static_cast<char*>(buffer));
  758. continue;
  759. }
  760. }
  761. break;
  762. case kWebViewMessageReload:
  763. d_debug("client kWebViewMessageReload");
  764. reloadFn();
  765. continue;
  766. }
  767. d_stderr("client ringbuffer data race, abort!");
  768. abort();
  769. }
  770. free(buffer);
  771. }
  772. static int gtk3_js_cb(WebKitUserContentManager*, WebKitJavascriptResult* const result, void* const arg)
  773. {
  774. WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(arg);
  775. using g_free_t = void (*)(void*);
  776. using jsc_value_to_string_t = char* (*)(JSCValue*);
  777. using webkit_javascript_result_get_js_value_t = JSCValue* (*)(WebKitJavascriptResult*);
  778. CSYM(g_free_t, g_free)
  779. CSYM(jsc_value_to_string_t, jsc_value_to_string)
  780. CSYM(webkit_javascript_result_get_js_value_t, webkit_javascript_result_get_js_value)
  781. JSCValue* const value = webkit_javascript_result_get_js_value(result);
  782. DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, false);
  783. char* const string = jsc_value_to_string(value);
  784. DISTRHO_SAFE_ASSERT_RETURN(string != nullptr, false);
  785. d_debug("js call received with data '%s'", string);
  786. const size_t len = std::strlen(string);
  787. RingBufferControl<WebViewSharedBuffer> rbctrl2;
  788. rbctrl2.setRingBuffer(&shmptr->server, false);
  789. rbctrl2.writeUInt(kWebViewMessageCallback) &&
  790. rbctrl2.writeUInt(len) &&
  791. rbctrl2.writeCustomData(string, len);
  792. rbctrl2.commitWrite();
  793. g_free(string);
  794. return 0;
  795. }
  796. static bool gtk3(Display* const display,
  797. const Window winId,
  798. const int x,
  799. const int y,
  800. const uint width,
  801. const uint height,
  802. double scaleFactor,
  803. const char* const url,
  804. const char* const initialJS,
  805. WebViewRingBuffer* const shmptr)
  806. {
  807. void* lib;
  808. if ((lib = dlopen("libwebkit2gtk-4.0.so.37", RTLD_NOW|RTLD_GLOBAL)) == nullptr ||
  809. (lib = dlopen("libwebkit2gtk-4.0.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  810. return false;
  811. using g_main_context_invoke_t = void (*)(void*, void*, void*);
  812. using g_signal_connect_data_t = ulong (*)(void*, const char*, void*, void*, void*, int);
  813. using gdk_set_allowed_backends_t = void (*)(const char*);
  814. using gtk_container_add_t = void (*)(GtkContainer*, GtkWidget*);
  815. using gtk_init_check_t = gboolean (*)(int*, char***);
  816. using gtk_main_t = void (*)();
  817. using gtk_main_quit_t = void (*)();
  818. using gtk_plug_get_id_t = Window (*)(GtkPlug*);
  819. using gtk_plug_new_t = GtkWidget* (*)(Window);
  820. using gtk_widget_show_all_t = void (*)(GtkWidget*);
  821. using gtk_window_move_t = void (*)(GtkWindow*, int, int);
  822. using gtk_window_set_default_size_t = void (*)(GtkWindow*, int, int);
  823. using webkit_settings_new_t = WebKitSettings* (*)();
  824. using webkit_settings_set_enable_developer_extras_t = void (*)(WebKitSettings*, gboolean);
  825. using webkit_settings_set_enable_write_console_messages_to_stdout_t = void (*)(WebKitSettings*, gboolean);
  826. using webkit_settings_set_hardware_acceleration_policy_t = void (*)(WebKitSettings*, int);
  827. using webkit_settings_set_javascript_can_access_clipboard_t = void (*)(WebKitSettings*, gboolean);
  828. using webkit_user_content_manager_add_script_t = void (*)(WebKitUserContentManager*, WebKitUserScript*);
  829. using webkit_user_content_manager_register_script_message_handler_t = gboolean (*)(WebKitUserContentManager*, const char*);
  830. using webkit_user_script_new_t = WebKitUserScript* (*)(const char*, int, int, const char* const*, const char* const*);
  831. using webkit_web_view_evaluate_javascript_t = void* (*)(WebKitWebView*, const char*, ssize_t, const char*, const char*, void*, void*, void*);
  832. using webkit_web_view_get_user_content_manager_t = WebKitUserContentManager* (*)(WebKitWebView*);
  833. using webkit_web_view_load_uri_t = void (*)(WebKitWebView*, const char*);
  834. using webkit_web_view_new_with_settings_t = GtkWidget* (*)(WebKitSettings*);
  835. using webkit_web_view_run_javascript_t = void* (*)(WebKitWebView*, const char*, void*, void*, void*);
  836. using webkit_web_view_set_background_color_t = void (*)(WebKitWebView*, const double*);
  837. CSYM(g_main_context_invoke_t, g_main_context_invoke)
  838. CSYM(g_signal_connect_data_t, g_signal_connect_data)
  839. CSYM(gdk_set_allowed_backends_t, gdk_set_allowed_backends)
  840. CSYM(gtk_container_add_t, gtk_container_add)
  841. CSYM(gtk_init_check_t, gtk_init_check)
  842. CSYM(gtk_main_t, gtk_main)
  843. CSYM(gtk_main_quit_t, gtk_main_quit)
  844. CSYM(gtk_plug_get_id_t, gtk_plug_get_id)
  845. CSYM(gtk_plug_new_t, gtk_plug_new)
  846. CSYM(gtk_widget_show_all_t, gtk_widget_show_all)
  847. CSYM(gtk_window_move_t, gtk_window_move)
  848. CSYM(gtk_window_set_default_size_t, gtk_window_set_default_size)
  849. CSYM(webkit_settings_new_t, webkit_settings_new)
  850. CSYM(webkit_settings_set_enable_developer_extras_t, webkit_settings_set_enable_developer_extras)
  851. CSYM(webkit_settings_set_enable_write_console_messages_to_stdout_t, webkit_settings_set_enable_write_console_messages_to_stdout)
  852. CSYM(webkit_settings_set_hardware_acceleration_policy_t, webkit_settings_set_hardware_acceleration_policy)
  853. CSYM(webkit_settings_set_javascript_can_access_clipboard_t, webkit_settings_set_javascript_can_access_clipboard)
  854. CSYM(webkit_user_content_manager_add_script_t, webkit_user_content_manager_add_script)
  855. CSYM(webkit_user_content_manager_register_script_message_handler_t, webkit_user_content_manager_register_script_message_handler)
  856. CSYM(webkit_user_script_new_t, webkit_user_script_new)
  857. CSYM(webkit_web_view_get_user_content_manager_t, webkit_web_view_get_user_content_manager)
  858. CSYM(webkit_web_view_load_uri_t, webkit_web_view_load_uri)
  859. CSYM(webkit_web_view_new_with_settings_t, webkit_web_view_new_with_settings)
  860. CSYM(webkit_web_view_set_background_color_t, webkit_web_view_set_background_color)
  861. // special case for legacy API handling
  862. 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"));
  863. 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"));
  864. DISTRHO_SAFE_ASSERT_RETURN(webkit_web_view_evaluate_javascript != nullptr || webkit_web_view_run_javascript != nullptr, false);
  865. const int gdkScale = std::fmod(scaleFactor, 1.0) >= 0.75
  866. ? static_cast<int>(scaleFactor + 0.5)
  867. : static_cast<int>(scaleFactor);
  868. if (gdkScale != 1)
  869. {
  870. char scale[8] = {};
  871. std::snprintf(scale, 7, "%d", gdkScale);
  872. setenv("GDK_SCALE", scale, 1);
  873. std::snprintf(scale, 7, "%.2f", (1.0 / scaleFactor) * 1.2);
  874. setenv("GDK_DPI_SCALE", scale, 1);
  875. }
  876. else if (scaleFactor > 1.0)
  877. {
  878. char scale[8] = {};
  879. std::snprintf(scale, 7, "%.2f", (1.0 / scaleFactor) * 1.4);
  880. setenv("GDK_DPI_SCALE", scale, 1);
  881. }
  882. scaleFactor /= gdkScale;
  883. gdk_set_allowed_backends("x11");
  884. if (! gtk_init_check (nullptr, nullptr))
  885. return false;
  886. GtkWidget* const window = gtk_plug_new(winId);
  887. DISTRHO_SAFE_ASSERT_RETURN(window != nullptr, false);
  888. gtk_window_set_default_size(GTK_WINDOW(window),
  889. (width - x) * scaleFactor,
  890. (height - y) * scaleFactor);
  891. gtk_window_move(GTK_WINDOW(window), x * scaleFactor, y * scaleFactor);
  892. WebKitSettings* const settings = webkit_settings_new();
  893. DISTRHO_SAFE_ASSERT_RETURN(settings != nullptr, false);
  894. // TODO DOMPasteAllowed
  895. webkit_settings_set_javascript_can_access_clipboard(settings, true);
  896. webkit_settings_set_hardware_acceleration_policy(settings, 2 /* WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER */);
  897. // if (debug)
  898. {
  899. webkit_settings_set_enable_developer_extras(settings, true);
  900. webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
  901. }
  902. GtkWidget* const webview = webkit_web_view_new_with_settings(settings);
  903. DISTRHO_SAFE_ASSERT_RETURN(webview != nullptr, false);
  904. const double color[] = {49.0/255, 54.0/255, 59.0/255, 1};
  905. webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(webview), color);
  906. if (WebKitUserContentManager* const manager = webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(webview)))
  907. {
  908. g_signal_connect_data(manager, "script-message-received::external", G_CALLBACK(gtk3_js_cb), shmptr, nullptr, 0);
  909. webkit_user_content_manager_register_script_message_handler(manager, "external");
  910. WebKitUserScript* const mscript = webkit_user_script_new(
  911. "function postMessage(m){window.webkit.messageHandlers.external.postMessage(m)}", 0, 0, nullptr, nullptr);
  912. webkit_user_content_manager_add_script(manager, mscript);
  913. if (initialJS != nullptr)
  914. {
  915. WebKitUserScript* const script = webkit_user_script_new(initialJS, 0, 0, nullptr, nullptr);
  916. webkit_user_content_manager_add_script(manager, script);
  917. }
  918. }
  919. webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url);
  920. gtk_container_add(GTK_CONTAINER(window), webview);
  921. gtk_widget_show_all(window);
  922. Window wid = gtk_plug_get_id(GTK_PLUG(window));
  923. XMapWindow(display, wid);
  924. XFlush(display);
  925. evaluateFn = [=](const char* const js){
  926. if (webkit_web_view_evaluate_javascript != nullptr)
  927. webkit_web_view_evaluate_javascript(WEBKIT_WEB_VIEW(webview), js, -1,
  928. nullptr, nullptr, nullptr, nullptr, nullptr);
  929. else
  930. webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(webview), js, nullptr, nullptr, nullptr);
  931. };
  932. reloadFn = [=](){
  933. webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url);
  934. };
  935. terminateFn = [=](){
  936. d_stdout("terminateFn");
  937. static bool quit = true;
  938. if (quit)
  939. {
  940. quit = false;
  941. gtk_main_quit();
  942. }
  943. };
  944. wakeFn = [=](WebViewRingBuffer* const rb){
  945. g_main_context_invoke(NULL, G_CALLBACK(gtk3_idle), rb);
  946. };
  947. // notify server we started ok
  948. webview_wake(&shmptr->server.sem);
  949. gtk_main();
  950. d_stdout("quit");
  951. dlclose(lib);
  952. return true;
  953. }
  954. #if 0
  955. // -----------------------------------------------------------------------------------------------------------
  956. // qt5webengine variant
  957. static bool qt5webengine(const Window winId, const double scaleFactor, const char* const url)
  958. {
  959. void* lib;
  960. if ((lib = dlopen("libQt5WebEngineWidgets.so.5", RTLD_NOW|RTLD_GLOBAL)) == nullptr ||
  961. (lib = dlopen("libQt5WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  962. return false;
  963. using QApplication__init_t = void (*)(QApplication*, int&, char**, int);
  964. using QApplication_exec_t = void (*)();
  965. using QApplication_setAttribute_t = void (*)(Qt::ApplicationAttribute, bool);
  966. using QString__init_t = void (*)(void*, const QChar*, ptrdiff_t);
  967. using QUrl__init_t = void (*)(void*, const QString&, int /* QUrl::ParsingMode */);
  968. using QWebEngineView__init_t = void (*)(QWebEngineView*, void*);
  969. using QWebEngineView_move_t = void (*)(QWebEngineView*, const QPoint&);
  970. using QWebEngineView_resize_t = void (*)(QWebEngineView*, const QSize&);
  971. using QWebEngineView_setUrl_t = void (*)(QWebEngineView*, const QUrl&);
  972. using QWebEngineView_show_t = void (*)(QWebEngineView*);
  973. using QWebEngineView_winId_t = ulonglong (*)(QWebEngineView*);
  974. using QWebEngineView_windowHandle_t = QWindow* (*)(QWebEngineView*);
  975. using QWindow_fromWinId_t = QWindow* (*)(ulonglong);
  976. using QWindow_setParent_t = void (*)(QWindow*, void*);
  977. CPPSYM(QApplication__init_t, QApplication__init, _ZN12QApplicationC1ERiPPci)
  978. CPPSYM(QApplication_exec_t, QApplication_exec, _ZN15QGuiApplication4execEv)
  979. CPPSYM(QApplication_setAttribute_t, QApplication_setAttribute, _ZN16QCoreApplication12setAttributeEN2Qt20ApplicationAttributeEb)
  980. CPPSYM(QString__init_t, QString__init, _ZN7QStringC2EPK5QChari)
  981. CPPSYM(QUrl__init_t, QUrl__init, _ZN4QUrlC1ERK7QStringNS_11ParsingModeE)
  982. CPPSYM(QWebEngineView__init_t, QWebEngineView__init, _ZN14QWebEngineViewC1EP7QWidget)
  983. CPPSYM(QWebEngineView_move_t, QWebEngineView_move, _ZN7QWidget4moveERK6QPoint)
  984. CPPSYM(QWebEngineView_resize_t, QWebEngineView_resize, _ZN7QWidget6resizeERK5QSize)
  985. CPPSYM(QWebEngineView_setUrl_t, QWebEngineView_setUrl, _ZN14QWebEngineView6setUrlERK4QUrl)
  986. CPPSYM(QWebEngineView_show_t, QWebEngineView_show, _ZN7QWidget4showEv)
  987. CPPSYM(QWebEngineView_winId_t, QWebEngineView_winId, _ZNK7QWidget5winIdEv)
  988. CPPSYM(QWebEngineView_windowHandle_t, QWebEngineView_windowHandle, _ZNK7QWidget12windowHandleEv)
  989. CPPSYM(QWindow_fromWinId_t, QWindow_fromWinId, _ZN7QWindow9fromWinIdEy)
  990. CPPSYM(QWindow_setParent_t, QWindow_setParent, _ZN7QWindow9setParentEPS_)
  991. unsetenv("QT_FONT_DPI");
  992. unsetenv("QT_SCREEN_SCALE_FACTORS");
  993. unsetenv("QT_USE_PHYSICAL_DPI");
  994. setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0", 1);
  995. char scale[8] = {};
  996. std::snprintf(scale, 7, "%.2f", scaleFactor);
  997. setenv("QT_SCALE_FACTOR", scale, 1);
  998. QApplication_setAttribute(Qt::AA_X11InitThreads, true);
  999. QApplication_setAttribute(Qt::AA_EnableHighDpiScaling, true);
  1000. QApplication_setAttribute(Qt::AA_UseHighDpiPixmaps, true);
  1001. static int argc = 0;
  1002. static char* argv[] = { nullptr };
  1003. uint8_t _app[64]; // sizeof(QApplication) == 16
  1004. QApplication* const app = reinterpret_cast<QApplication*>(_app);
  1005. QApplication__init(app, argc, argv, 0);
  1006. uint8_t _qstrurl[32]; // sizeof(QString) == 8
  1007. QString* const qstrurl(reinterpret_cast<QString*>(_qstrurl));
  1008. {
  1009. const size_t url_len = std::strlen(url);
  1010. QChar* const url_qchar = new QChar[url_len + 1];
  1011. for (size_t i = 0; i < url_len; ++i)
  1012. url_qchar[i] = QChar(url[i]);
  1013. url_qchar[url_len] = 0;
  1014. QString__init(qstrurl, url_qchar, url_len);
  1015. }
  1016. uint8_t _qurl[32]; // sizeof(QUrl) == 8
  1017. QUrl* const qurl(reinterpret_cast<QUrl*>(_qurl));
  1018. QUrl__init(qurl, *qstrurl, 1 /* QUrl::StrictMode */);
  1019. uint8_t _webview[128]; // sizeof(QWebEngineView) == 56
  1020. QWebEngineView* const webview = reinterpret_cast<QWebEngineView*>(_webview);
  1021. QWebEngineView__init(webview, nullptr);
  1022. QWebEngineView_move(webview, QPoint(0, kVerticalOffset));
  1023. QWebEngineView_resize(webview, QSize(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset));
  1024. QWebEngineView_winId(webview);
  1025. QWindow_setParent(QWebEngineView_windowHandle(webview), QWindow_fromWinId(winId));
  1026. QWebEngineView_setUrl(webview, *qurl);
  1027. QWebEngineView_show(webview);
  1028. reloadFn = [=](){
  1029. QWebEngineView_setUrl(webview, *qurl);
  1030. };
  1031. terminateFn = [=](){
  1032. // TODO
  1033. };
  1034. QApplication_exec();
  1035. dlclose(lib);
  1036. return true;
  1037. }
  1038. // -----------------------------------------------------------------------------------------------------------
  1039. // qt6webengine variant (same as qt5 but `QString__init_t` has different arguments)
  1040. static bool qt6webengine(const Window winId, const double scaleFactor, const char* const url)
  1041. {
  1042. void* lib;
  1043. if ((lib = dlopen("libQt6WebEngineWidgets.so.6", RTLD_NOW|RTLD_GLOBAL)) == nullptr ||
  1044. (lib = dlopen("libQt6WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  1045. return false;
  1046. using QApplication__init_t = void (*)(QApplication*, int&, char**, int);
  1047. using QApplication_exec_t = void (*)();
  1048. using QApplication_setAttribute_t = void (*)(Qt::ApplicationAttribute, bool);
  1049. using QString__init_t = void (*)(void*, const QChar*, long long);
  1050. using QUrl__init_t = void (*)(void*, const QString&, int /* QUrl::ParsingMode */);
  1051. using QWebEngineView__init_t = void (*)(QWebEngineView*, void*);
  1052. using QWebEngineView_move_t = void (*)(QWebEngineView*, const QPoint&);
  1053. using QWebEngineView_resize_t = void (*)(QWebEngineView*, const QSize&);
  1054. using QWebEngineView_setUrl_t = void (*)(QWebEngineView*, const QUrl&);
  1055. using QWebEngineView_show_t = void (*)(QWebEngineView*);
  1056. using QWebEngineView_winId_t = ulonglong (*)(QWebEngineView*);
  1057. using QWebEngineView_windowHandle_t = QWindow* (*)(QWebEngineView*);
  1058. using QWindow_fromWinId_t = QWindow* (*)(ulonglong);
  1059. using QWindow_setParent_t = void (*)(QWindow*, void*);
  1060. CPPSYM(QApplication__init_t, QApplication__init, _ZN12QApplicationC1ERiPPci)
  1061. CPPSYM(QApplication_exec_t, QApplication_exec, _ZN15QGuiApplication4execEv)
  1062. CPPSYM(QApplication_setAttribute_t, QApplication_setAttribute, _ZN16QCoreApplication12setAttributeEN2Qt20ApplicationAttributeEb)
  1063. CPPSYM(QString__init_t, QString__init, _ZN7QStringC2EPK5QCharx)
  1064. CPPSYM(QUrl__init_t, QUrl__init, _ZN4QUrlC1ERK7QStringNS_11ParsingModeE)
  1065. CPPSYM(QWebEngineView__init_t, QWebEngineView__init, _ZN14QWebEngineViewC1EP7QWidget)
  1066. CPPSYM(QWebEngineView_move_t, QWebEngineView_move, _ZN7QWidget4moveERK6QPoint)
  1067. CPPSYM(QWebEngineView_resize_t, QWebEngineView_resize, _ZN7QWidget6resizeERK5QSize)
  1068. CPPSYM(QWebEngineView_setUrl_t, QWebEngineView_setUrl, _ZN14QWebEngineView6setUrlERK4QUrl)
  1069. CPPSYM(QWebEngineView_show_t, QWebEngineView_show, _ZN7QWidget4showEv)
  1070. CPPSYM(QWebEngineView_winId_t, QWebEngineView_winId, _ZNK7QWidget5winIdEv)
  1071. CPPSYM(QWebEngineView_windowHandle_t, QWebEngineView_windowHandle, _ZNK7QWidget12windowHandleEv)
  1072. CPPSYM(QWindow_fromWinId_t, QWindow_fromWinId, _ZN7QWindow9fromWinIdEy)
  1073. CPPSYM(QWindow_setParent_t, QWindow_setParent, _ZN7QWindow9setParentEPS_)
  1074. unsetenv("QT_FONT_DPI");
  1075. unsetenv("QT_SCREEN_SCALE_FACTORS");
  1076. unsetenv("QT_USE_PHYSICAL_DPI");
  1077. setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0", 1);
  1078. char scale[8] = {};
  1079. std::snprintf(scale, 7, "%.2f", scaleFactor);
  1080. setenv("QT_SCALE_FACTOR", scale, 1);
  1081. QApplication_setAttribute(Qt::AA_X11InitThreads, true);
  1082. QApplication_setAttribute(Qt::AA_EnableHighDpiScaling, true);
  1083. QApplication_setAttribute(Qt::AA_UseHighDpiPixmaps, true);
  1084. static int argc = 0;
  1085. static char* argv[] = { nullptr };
  1086. uint8_t _app[64]; // sizeof(QApplication) == 16
  1087. QApplication* const app = reinterpret_cast<QApplication*>(_app);
  1088. QApplication__init(app, argc, argv, 0);
  1089. uint8_t _qstrurl[32]; // sizeof(QString) == 8
  1090. QString* const qstrurl(reinterpret_cast<QString*>(_qstrurl));
  1091. {
  1092. const size_t url_len = std::strlen(url);
  1093. QChar* const url_qchar = new QChar[url_len + 1];
  1094. for (size_t i = 0; i < url_len; ++i)
  1095. url_qchar[i] = QChar(url[i]);
  1096. url_qchar[url_len] = 0;
  1097. QString__init(qstrurl, url_qchar, url_len);
  1098. }
  1099. uint8_t _qurl[32]; // sizeof(QUrl) == 8
  1100. QUrl* const qurl(reinterpret_cast<QUrl*>(_qurl));
  1101. QUrl__init(qurl, *qstrurl, 1 /* QUrl::StrictMode */);
  1102. uint8_t _webview[128]; // sizeof(QWebEngineView) == 56
  1103. QWebEngineView* const webview = reinterpret_cast<QWebEngineView*>(_webview);
  1104. QWebEngineView__init(webview, nullptr);
  1105. QWebEngineView_move(webview, QPoint(0, kVerticalOffset));
  1106. QWebEngineView_resize(webview, QSize(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset));
  1107. QWebEngineView_winId(webview);
  1108. QWindow_setParent(QWebEngineView_windowHandle(webview), QWindow_fromWinId(winId));
  1109. QWebEngineView_setUrl(webview, *qurl);
  1110. QWebEngineView_show(webview);
  1111. reloadFn = [=](){
  1112. QWebEngineView_setUrl(webview, *qurl);
  1113. };
  1114. terminateFn = [=](){
  1115. // TODO
  1116. };
  1117. QApplication_exec();
  1118. dlclose(lib);
  1119. return true;
  1120. }
  1121. #endif
  1122. // -----------------------------------------------------------------------------------------------------------
  1123. // startup via ld-linux
  1124. static void signalHandler(const int sig)
  1125. {
  1126. switch (sig)
  1127. {
  1128. case SIGTERM:
  1129. terminateFn();
  1130. break;
  1131. }
  1132. }
  1133. static void* threadHandler(void* const ptr)
  1134. {
  1135. WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(ptr);
  1136. // TODO wait until page is loaded, or something better
  1137. d_sleep(1);
  1138. while (shmptr->valid)
  1139. {
  1140. if (webview_timedwait(&shmptr->client.sem))
  1141. wakeFn(shmptr);
  1142. }
  1143. return nullptr;
  1144. }
  1145. int dpf_webview_start(const int argc, char* argv[])
  1146. {
  1147. d_stdout("started %d %s", argc, argv[1]);
  1148. if (argc != 3)
  1149. {
  1150. d_stderr("WebView entry point, nothing to see here! ;)");
  1151. return 1;
  1152. }
  1153. uselocale(newlocale(LC_NUMERIC_MASK, "C", nullptr));
  1154. Display* const display = XOpenDisplay(nullptr);
  1155. DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1);
  1156. const char* const shmname = argv[2];
  1157. const int shmfd = shm_open(shmname, O_RDWR, 0);
  1158. if (shmfd < 0)
  1159. {
  1160. d_stderr("shm_open failed: %s", std::strerror(errno));
  1161. return 1;
  1162. }
  1163. WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(mmap(nullptr,
  1164. sizeof(WebViewRingBuffer),
  1165. PROT_READ|PROT_WRITE,
  1166. MAP_SHARED,
  1167. shmfd, 0));
  1168. if (shmptr == nullptr || shmptr == nullptr)
  1169. {
  1170. d_stderr("mmap failed: %s", std::strerror(errno));
  1171. close(shmfd);
  1172. return 1;
  1173. }
  1174. RingBufferControl<WebViewSharedBuffer> rbctrl;
  1175. rbctrl.setRingBuffer(&shmptr->client, false);
  1176. // fetch initial data
  1177. bool hasInitialData = false;
  1178. Window winId = 0;
  1179. uint width = 0, height = 0;
  1180. double scaleFactor = 0;
  1181. int x = 0, y = 0;
  1182. char* url = nullptr;
  1183. char* initJS = nullptr;
  1184. while (shmptr->valid && webview_timedwait(&shmptr->client.sem))
  1185. {
  1186. if (rbctrl.isDataAvailableForReading())
  1187. {
  1188. DISTRHO_SAFE_ASSERT_RETURN(rbctrl.readUInt() == kWebViewMessageInitData, 1);
  1189. hasInitialData = true;
  1190. winId = rbctrl.readULong();
  1191. width = rbctrl.readUInt();
  1192. height = rbctrl.readUInt();
  1193. scaleFactor = rbctrl.readDouble();
  1194. x = rbctrl.readInt();
  1195. y = rbctrl.readInt();
  1196. const uint urllen = rbctrl.readUInt();
  1197. url = static_cast<char*>(std::malloc(urllen));
  1198. rbctrl.readCustomData(url, urllen);
  1199. if (const uint initjslen = rbctrl.readUInt())
  1200. {
  1201. initJS = static_cast<char*>(std::malloc(initjslen));
  1202. rbctrl.readCustomData(initJS, initjslen);
  1203. }
  1204. }
  1205. }
  1206. pthread_t thread;
  1207. if (hasInitialData && pthread_create(&thread, nullptr, threadHandler, shmptr) == 0)
  1208. {
  1209. struct sigaction sig = {};
  1210. sig.sa_handler = signalHandler;
  1211. sig.sa_flags = SA_RESTART;
  1212. sigemptyset(&sig.sa_mask);
  1213. sigaction(SIGTERM, &sig, nullptr);
  1214. // qt5webengine(winId, scaleFactor, url) ||
  1215. // qt6webengine(winId, scaleFactor, url) ||
  1216. gtk3(display, winId, x, y, width, height, scaleFactor, url, initJS, shmptr);
  1217. shmptr->valid = false;
  1218. pthread_join(thread, nullptr);
  1219. }
  1220. munmap(shmptr, sizeof(WebViewRingBuffer));
  1221. close(shmfd);
  1222. XCloseDisplay(display);
  1223. return 0;
  1224. }
  1225. // --------------------------------------------------------------------------------------------------------------------
  1226. #endif // WEB_VIEW_USING_X11_IPC
  1227. #ifdef WEB_VIEW_DGL_NAMESPACE
  1228. END_NAMESPACE_DGL
  1229. #else
  1230. END_NAMESPACE_DISTRHO
  1231. #endif
  1232. #undef MACRO_NAME
  1233. #undef MACRO_NAME2
  1234. #undef WEB_VIEW_DISTRHO_NAMESPACE
  1235. #undef WEB_VIEW_DGL_NAMESPACE