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.

1132 lines
41KB

  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. #define WEB_VIEW_USING_CHOC 0
  26. #ifndef WEB_VIEW_USING_CHOC
  27. # define WEB_VIEW_USING_CHOC 0
  28. #elif WEB_VIEW_USING_CHOC && !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS))
  29. # undef WEB_VIEW_USING_CHOC
  30. # define WEB_VIEW_USING_CHOC 0
  31. #endif
  32. #if defined(DISTRHO_OS_MAC) && !WEB_VIEW_USING_CHOC
  33. # undef WEB_VIEW_USING_MACOS_WEBKIT
  34. # define WEB_VIEW_USING_MACOS_WEBKIT 1
  35. #else
  36. # undef WEB_VIEW_USING_MACOS_WEBKIT
  37. # define WEB_VIEW_USING_MACOS_WEBKIT 0
  38. #endif
  39. #if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX)
  40. # undef WEB_VIEW_USING_X11_IPC
  41. # define WEB_VIEW_USING_X11_IPC 1
  42. #else
  43. # undef WEB_VIEW_USING_X11_IPC
  44. # define WEB_VIEW_USING_X11_IPC 0
  45. #endif
  46. #if WEB_VIEW_USING_CHOC
  47. # define WC_ERR_INVALID_CHARS 0
  48. # include "../CHOC/gui/choc_WebView.h"
  49. #elif WEB_VIEW_USING_MACOS_WEBKIT
  50. # include <Cocoa/Cocoa.h>
  51. # include <WebKit/WebKit.h>
  52. #elif WEB_VIEW_USING_X11_IPC
  53. // #define QT_NO_VERSION_TAGGING
  54. // #include <QtCore/QChar>
  55. // #include <QtCore/QPoint>
  56. // #include <QtCore/QSize>
  57. // #undef signals
  58. # include "ChildProcess.hpp"
  59. # include "String.hpp"
  60. # include <clocale>
  61. # include <cstdio>
  62. # include <dlfcn.h>
  63. # include <functional>
  64. # include <linux/limits.h>
  65. # include <X11/Xlib.h>
  66. #endif
  67. // -----------------------------------------------------------------------------------------------------------
  68. #if WEB_VIEW_USING_MACOS_WEBKIT
  69. #define MACRO_NAME2(a, b, c) a ## b ## c
  70. #define MACRO_NAME(a, b, c) MACRO_NAME2(a, b, c)
  71. #define WEB_VIEW_DELEGATE_CLASS_NAME \
  72. MACRO_NAME(WebViewDelegate_, _, DISTRHO_NAMESPACE)
  73. @interface WEB_VIEW_DELEGATE_CLASS_NAME : NSObject<WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate>
  74. @end
  75. @implementation WEB_VIEW_DELEGATE_CLASS_NAME {
  76. @public
  77. WebViewMessageCallback callback;
  78. void* callbackPtr;
  79. bool loaded;
  80. }
  81. - (void)webView:(WKWebView *)webview
  82. didFinishNavigation:(WKNavigation*)navigation
  83. {
  84. d_stdout("page loaded");
  85. loaded = true;
  86. }
  87. - (void)webView:(WKWebView*)webview
  88. runJavaScriptAlertPanelWithMessage:(NSString*)message
  89. initiatedByFrame:(WKFrameInfo*)frame
  90. completionHandler:(void (^)(void))completionHandler
  91. {
  92. NSAlert* const alert = [[NSAlert alloc] init];
  93. [alert addButtonWithTitle:@"OK"];
  94. [alert setInformativeText:message];
  95. [alert setMessageText:@"Alert"];
  96. dispatch_async(dispatch_get_main_queue(), ^
  97. {
  98. [alert beginSheetModalForWindow:[webview window]
  99. completionHandler:^(NSModalResponse)
  100. {
  101. completionHandler();
  102. [alert release];
  103. }];
  104. });
  105. }
  106. - (void)webView:(WKWebView*)webview
  107. runJavaScriptConfirmPanelWithMessage:(NSString*)message
  108. initiatedByFrame:(WKFrameInfo*)frame
  109. completionHandler:(void (^)(BOOL))completionHandler
  110. {
  111. NSAlert* const alert = [[NSAlert alloc] init];
  112. [alert addButtonWithTitle:@"OK"];
  113. [alert addButtonWithTitle:@"Cancel"];
  114. [alert setInformativeText:message];
  115. [alert setMessageText:@"Confirm"];
  116. dispatch_async(dispatch_get_main_queue(), ^
  117. {
  118. [alert beginSheetModalForWindow:[webview window]
  119. completionHandler:^(NSModalResponse result)
  120. {
  121. completionHandler(result == NSAlertFirstButtonReturn);
  122. [alert release];
  123. }];
  124. });
  125. }
  126. - (void)webView:(WKWebView*)webview
  127. runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt
  128. defaultText:(NSString*)defaultText
  129. initiatedByFrame:(WKFrameInfo*)frame
  130. completionHandler:(void (^)(NSString*))completionHandler
  131. {
  132. NSTextField* const input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 250, 30)];
  133. [input setStringValue:defaultText];
  134. NSAlert* const alert = [[NSAlert alloc] init];
  135. [alert setAccessoryView:input];
  136. [alert addButtonWithTitle:@"OK"];
  137. [alert addButtonWithTitle:@"Cancel"];
  138. [alert setInformativeText:prompt];
  139. [alert setMessageText: @"Prompt"];
  140. dispatch_async(dispatch_get_main_queue(), ^
  141. {
  142. [alert beginSheetModalForWindow:[webview window]
  143. completionHandler:^(NSModalResponse result)
  144. {
  145. [input validateEditing];
  146. completionHandler(result == NSAlertFirstButtonReturn ? [input stringValue] : nil);
  147. [alert release];
  148. }];
  149. });
  150. }
  151. - (void)webView:(WKWebView*)webview
  152. runOpenPanelWithParameters:(WKOpenPanelParameters*)params
  153. initiatedByFrame:(WKFrameInfo*)frame
  154. completionHandler:(void (^)(NSArray<NSURL*>*))completionHandler
  155. {
  156. NSOpenPanel* const panel = [[NSOpenPanel alloc] init];
  157. [panel setAllowsMultipleSelection:[params allowsMultipleSelection]];
  158. // [panel setAllowedFileTypes:(NSArray<NSString*>*)[params _allowedFileExtensions]];
  159. [panel setCanChooseDirectories:[params allowsDirectories]];
  160. [panel setCanChooseFiles:![params allowsDirectories]];
  161. dispatch_async(dispatch_get_main_queue(), ^
  162. {
  163. [panel beginSheetModalForWindow:[webview window]
  164. completionHandler:^(NSModalResponse result)
  165. {
  166. completionHandler(result == NSModalResponseOK ? [panel URLs] : nil);
  167. [panel release];
  168. }];
  169. });
  170. }
  171. - (void)userContentController:(WKUserContentController*)userContentController
  172. didReceiveScriptMessage:(WKScriptMessage*)message
  173. {
  174. NSString* const nsstring = static_cast<NSString*>([message body]);
  175. char* const string = strdup([nsstring UTF8String]);
  176. d_debug("JS call received '%s' %p", string, callback);
  177. if (callback != nullptr)
  178. callback(callbackPtr, string);
  179. std::free(string);
  180. }
  181. @end
  182. #endif // WEB_VIEW_USING_MACOS_WEBKIT
  183. // -----------------------------------------------------------------------------------------------------------
  184. #ifdef WEB_VIEW_DGL_NAMESPACE
  185. START_NAMESPACE_DGL
  186. using DISTRHO_NAMESPACE::String;
  187. #else
  188. START_NAMESPACE_DISTRHO
  189. #endif
  190. // -----------------------------------------------------------------------------------------------------------
  191. struct WebViewData {
  192. const WebViewMessageCallback callback;
  193. #if WEB_VIEW_USING_CHOC
  194. choc::ui::WebView* const webview;
  195. #elif WEB_VIEW_USING_MACOS_WEBKIT
  196. NSView* const view;
  197. WKWebView* const webview;
  198. NSURLRequest* const urlreq;
  199. WEB_VIEW_DELEGATE_CLASS_NAME* const delegate;
  200. #elif WEB_VIEW_USING_X11_IPC
  201. ChildProcess p;
  202. ::Display* display;
  203. ::Window childWindow;
  204. ::Window ourWindow;
  205. #endif
  206. };
  207. // -----------------------------------------------------------------------------------------------------------
  208. #if WEB_VIEW_USING_CHOC
  209. static std::optional<choc::ui::WebView::Options::Resource> fetch_resource(const std::string& path)
  210. {
  211. d_stdout("requested path %s", path.c_str());
  212. if (path == "/")
  213. {
  214. const std::string html = R"PREFIX(
  215. <html>
  216. <head>
  217. <style>
  218. html, body { background: black; background-image: url(img.svg); }
  219. </style>
  220. <script>
  221. function parameterChanged(index, value) {
  222. console.log("parameterChanged received", index, value);
  223. }
  224. </script>
  225. </head>
  226. <body>
  227. hello world!
  228. </body>
  229. </html>
  230. )PREFIX";
  231. const std::vector<uint8_t> data(html.begin(), html.end());
  232. return choc::ui::WebView::Options::Resource{ data, "text/html" };
  233. }
  234. if (path == "/img.svg")
  235. {
  236. const std::string html = R"PREFIX(<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  237. <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
  238. <!-- based on https://github.com/n0jo/rackwindows/blob/master/res/components/rw_knob_large_dark.svg -->
  239. <svg width="47px" height="47px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;">
  240. <g id="knobLDark">
  241. <path id="path3832" d="M23.521,45.109c-7.674,0 -3.302,3.9 -10.224,0.498c-6.922,-3.403 -1.202,-2.341 -5.997,-8.501c-4.795,-6.159 -5.059,-0.201 -6.763,-7.827c-1.704,-7.625 1.043,-2.42 2.76,-10.046c1.718,-7.626 -2.998,-4.102 1.797,-10.221c4.795,-6.12 2.51,-0.673 9.432,-4.035c6.921,-3.363 1.321,-4.977 8.995,-4.977c7.675,0 2.087,1.574 8.996,4.977c6.909,3.402 4.636,-2.045 9.432,4.035c4.795,6.078 0.079,2.689 1.796,10.26c1.717,7.572 4.465,2.422 2.761,10.048c-1.704,7.625 -1.982,1.708 -6.763,7.827c-4.782,6.119 0.924,5.057 -5.998,8.46c-6.921,3.402 -2.549,-0.498 -10.224,-0.498Z" style="fill:#ccc;fill-rule:nonzero;"/>
  242. </g>
  243. </svg>
  244. )PREFIX";
  245. const std::vector<uint8_t> data(html.begin(), html.end());
  246. return choc::ui::WebView::Options::Resource{ data, "image/svg+xml" };
  247. }
  248. return {};
  249. }
  250. #elif WEB_VIEW_USING_X11_IPC
  251. static void getFilenameFromFunctionPtr(char filename[PATH_MAX], const void* const ptr)
  252. {
  253. Dl_info info = {};
  254. dladdr(ptr, &info);
  255. if (info.dli_fname[0] == '.')
  256. {
  257. getcwd(filename, PATH_MAX - 1);
  258. std::strncat(filename, info.dli_fname + 1, PATH_MAX - 1);
  259. }
  260. else if (info.dli_fname[0] != '/')
  261. {
  262. getcwd(filename, PATH_MAX - 1);
  263. std::strncat(filename, "/", PATH_MAX - 1);
  264. std::strncat(filename, info.dli_fname, PATH_MAX - 1);
  265. }
  266. else
  267. {
  268. std::strncpy(filename, info.dli_fname, PATH_MAX - 1);
  269. }
  270. }
  271. #endif
  272. // -----------------------------------------------------------------------------------------------------------
  273. WebViewHandle webViewCreate(const uintptr_t windowId,
  274. const uint initialWidth,
  275. const uint initialHeight,
  276. const double scaleFactor,
  277. const WebViewOptions& options)
  278. {
  279. #if WEB_VIEW_USING_CHOC
  280. choc::ui::WebView::Options woptions;
  281. woptions.acceptsFirstMouseClick = true;
  282. woptions.enableDebugMode = true;
  283. woptions.fetchResource = fetch_resource;
  284. std::unique_ptr<choc::ui::WebView> webview = std::make_unique<choc::ui::WebView>(woptions);
  285. DISTRHO_SAFE_ASSERT_RETURN(webview->loadedOK(), nullptr);
  286. void* const handle = webview->getViewHandle();
  287. DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, nullptr);
  288. choc::ui::WebView* const www = webview.get();
  289. webview->bind("setParameterValue", [www](const choc::value::ValueView&) -> choc::value::Value {
  290. static int pp = 0;
  291. std::string toeval = "typeof(parameterChanged) === 'function' && parameterChanged(";
  292. toeval += std::to_string(++pp);
  293. toeval += ", 0.1)";
  294. d_stdout("param received | %s", toeval.c_str());
  295. www->evaluateJavascript(toeval);
  296. return {};
  297. });
  298. #ifdef DISTRHO_OS_MAC
  299. NSView* const view = static_cast<NSView*>(handle);
  300. [reinterpret_cast<NSView*>(windowId) addSubview:view];
  301. [view setFrame:NSMakeRect(options.offset.x,
  302. options.offset.y,
  303. DISTRHO_UI_DEFAULT_WIDTH - options.offset.x,
  304. DISTRHO_UI_DEFAULT_HEIGHT - options.offset.y)];
  305. #else
  306. const HWND hwnd = static_cast<HWND>(handle);
  307. LONG_PTR flags = GetWindowLongPtr(hwnd, -16);
  308. flags = (flags & ~WS_POPUP) | WS_CHILD;
  309. SetWindowLongPtr(hwnd, -16, flags);
  310. SetParent(hwnd, reinterpret_cast<HWND>(windowId));
  311. SetWindowPos(hwnd, nullptr,
  312. options.offset.x * scaleFactor,
  313. options.offset.y * scaleFactor,
  314. (initialWidth - options.offset.x) * scaleFactor,
  315. (initialHeight - options.offset.y) * scaleFactor,
  316. SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
  317. ShowWindow(hwnd, SW_SHOW);
  318. #endif
  319. return new WebViewData{options.callback, webview.release()};
  320. #elif WEB_VIEW_USING_MACOS_WEBKIT
  321. NSView* const view = reinterpret_cast<NSView*>(windowId);
  322. const CGRect rect = CGRectMake(options.offset.x,
  323. options.offset.y,
  324. (initialWidth - options.offset.x),
  325. (initialHeight - options.offset.y));
  326. WKPreferences* const prefs = [[WKPreferences alloc] init];
  327. [prefs setValue:@YES forKey:@"javaScriptCanAccessClipboard"];
  328. [prefs setValue:@YES forKey:@"DOMPasteAllowed"];
  329. // if (debug)
  330. {
  331. [prefs setValue:@YES forKey:@"developerExtrasEnabled"];
  332. // TODO enable_write_console_messages_to_stdout
  333. }
  334. WKWebViewConfiguration* const config = [[WKWebViewConfiguration alloc] init];
  335. config.limitsNavigationsToAppBoundDomains = false;
  336. config.preferences = prefs;
  337. WKWebView* const webview = [[WKWebView alloc] initWithFrame:rect
  338. configuration:config];
  339. [webview setHidden:YES];
  340. [view addSubview:webview];
  341. // TODO webkit_web_view_set_background_color
  342. WEB_VIEW_DELEGATE_CLASS_NAME* const delegate = [[WEB_VIEW_DELEGATE_CLASS_NAME alloc] init];
  343. delegate->callback = options.callback;
  344. delegate->callbackPtr = options.callbackPtr;
  345. delegate->loaded = false;
  346. webview.navigationDelegate = delegate;
  347. webview.UIDelegate = delegate;
  348. if (WKUserContentController* const controller = [config userContentController])
  349. {
  350. [controller retain];
  351. [controller addScriptMessageHandler:delegate name:@"external"];
  352. }
  353. const char* const url = "file:///Users/falktx/Source/DISTRHO/DPF/examples/WebMeters/index.html";
  354. NSString* const nsurl = [[NSString alloc] initWithBytes:url
  355. length:std::strlen(url)
  356. encoding:NSUTF8StringEncoding];
  357. NSURLRequest* const urlreq = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: nsurl]];
  358. // [webview loadRequest:urlreq];
  359. [webview loadFileRequest:urlreq
  360. allowingReadAccessToURL:[NSURL URLWithString:@"file:///Users/falktx/Source/DISTRHO/DPF/examples/WebMeters/"]];
  361. d_stdout("waiting for load");
  362. if (! delegate->loaded)
  363. {
  364. NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc] init];
  365. NSDate* const date = [NSDate distantFuture];
  366. NSEvent* event;
  367. while (! delegate->loaded)
  368. {
  369. event = [NSApp
  370. #ifdef __MAC_10_12
  371. nextEventMatchingMask:NSEventMaskAny
  372. #else
  373. nextEventMatchingMask:NSAnyEventMask
  374. #endif
  375. untilDate:date
  376. inMode:NSDefaultRunLoopMode
  377. dequeue:YES];
  378. if (event == nil)
  379. break;
  380. [NSApp sendEvent: event];
  381. }
  382. [pool release];
  383. }
  384. d_stdout("waiting done");
  385. [webview setHidden:NO];
  386. [nsurl release];
  387. [config release];
  388. [prefs release];
  389. return new WebViewData{options.callback, view, webview, urlreq, delegate};
  390. #elif WEB_VIEW_USING_X11_IPC
  391. char ldlinux[PATH_MAX] = {};
  392. getFilenameFromFunctionPtr(ldlinux, dlsym(nullptr, "_rtld_global"));
  393. char filename[PATH_MAX] = {};
  394. getFilenameFromFunctionPtr(filename, reinterpret_cast<const void*>(webViewCreate));
  395. d_stdout("ld-linux is '%s'", ldlinux);
  396. d_stdout("filename is '%s'", filename);
  397. ::Display* const display = XOpenDisplay(nullptr);
  398. DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, nullptr);
  399. // set up custom child environment
  400. uint envsize = 0;
  401. while (environ[envsize] != nullptr)
  402. ++envsize;
  403. char** const envp = new char*[envsize + 5];
  404. {
  405. uint e = 0;
  406. for (uint i = 0; i < envsize; ++i)
  407. {
  408. if (std::strncmp(environ[i], "LD_PRELOAD=", 11) == 0)
  409. continue;
  410. if (std::strncmp(environ[i], "LD_LIBRARY_PATH=", 16) == 0)
  411. continue;
  412. envp[e++] = strdup(environ[i]);
  413. }
  414. envp[e++] = strdup("LANG=en_US.UTF-8");
  415. envp[e++] = ("DPF_WEB_VIEW_SCALE_FACTOR=" + String(scaleFactor)).getAndReleaseBuffer();
  416. envp[e++] = ("DPF_WEB_VIEW_WIN_ID=" +String(windowId)).getAndReleaseBuffer();
  417. for (uint i = e; i < envsize + 5; ++i)
  418. envp[e++] = nullptr;
  419. }
  420. WebViewData* const handle = new WebViewData{options.callback, {}, display, 0, windowId};
  421. const char* const args[] = { ldlinux, filename, "dpf-ld-linux-webview", nullptr };
  422. handle->p.start(args, envp);
  423. for (uint i = 0; envp[i] != nullptr; ++i)
  424. std::free(envp[i]);
  425. delete[] envp;
  426. return handle;
  427. #endif
  428. // maybe unused
  429. (void)windowId;
  430. (void)initialWidth;
  431. (void)initialHeight;
  432. (void)scaleFactor;
  433. (void)options;
  434. return nullptr;
  435. }
  436. void webViewDestroy(const WebViewHandle handle)
  437. {
  438. #if WEB_VIEW_USING_CHOC
  439. delete handle->webview;
  440. #elif WEB_VIEW_USING_MACOS_WEBKIT
  441. [handle->webview setHidden:YES];
  442. [handle->webview removeFromSuperview];
  443. [handle->urlreq release];
  444. [handle->delegate release];
  445. #elif WEB_VIEW_USING_X11_IPC
  446. XCloseDisplay(handle->display);
  447. #endif
  448. delete handle;
  449. }
  450. void webViewEvaluateJS(const WebViewHandle handle, const char* const js)
  451. {
  452. #if WEB_VIEW_USING_CHOC
  453. #elif WEB_VIEW_USING_MACOS_WEBKIT
  454. NSString* const nsjs = [[NSString alloc] initWithBytes:js
  455. length:std::strlen(js)
  456. encoding:NSUTF8StringEncoding];
  457. [handle->webview evaluateJavaScript:nsjs completionHandler:nullptr];
  458. [nsjs release];
  459. #elif WEB_VIEW_USING_X11_IPC
  460. handle->p.signal(SIGUSR2);
  461. #endif
  462. // maybe unused
  463. (void)handle;
  464. (void)js;
  465. }
  466. void webViewReload(const WebViewHandle handle)
  467. {
  468. #if WEB_VIEW_USING_CHOC
  469. #elif WEB_VIEW_USING_MACOS_WEBKIT
  470. [handle->webview loadRequest:handle->urlreq];
  471. #elif WEB_VIEW_USING_X11_IPC
  472. handle->p.signal(SIGUSR1);
  473. #endif
  474. // maybe unused
  475. (void)handle;
  476. }
  477. void webViewResize(const WebViewHandle handle, const uint width, const uint height, const double scaleFactor)
  478. {
  479. #if WEB_VIEW_USING_CHOC
  480. #ifdef DISTRHO_OS_MAC
  481. NSView* const view = static_cast<NSView*>(handle->webview->getViewHandle());
  482. [view setFrameSize:NSMakeSize(width, height)];
  483. #else
  484. const HWND hwnd = static_cast<HWND>(handle->webview->getViewHandle());
  485. SetWindowPos(hwnd, nullptr, 0, 0,
  486. width * scaleFactor,
  487. height * scaleFactor,
  488. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  489. #endif
  490. #elif WEB_VIEW_USING_MACOS_WEBKIT
  491. [handle->webview setFrameSize:NSMakeSize(width, height)];
  492. #elif WEB_VIEW_USING_X11_IPC
  493. if (handle->childWindow == 0)
  494. {
  495. ::Window rootWindow, parentWindow;
  496. ::Window* childWindows = nullptr;
  497. uint numChildren = 0;
  498. XFlush(handle->display);
  499. XQueryTree(handle->display, handle->ourWindow, &rootWindow, &parentWindow, &childWindows, &numChildren);
  500. if (numChildren == 0 || childWindows == nullptr)
  501. return;
  502. handle->childWindow = childWindows[0];
  503. XFree(childWindows);
  504. }
  505. XResizeWindow(handle->display, handle->childWindow, width, height);
  506. XFlush(handle->display);
  507. #endif
  508. // maybe unused
  509. (void)handle;
  510. (void)width;
  511. (void)height;
  512. (void)scaleFactor;
  513. }
  514. #if WEB_VIEW_USING_X11_IPC
  515. // -----------------------------------------------------------------------------------------------------------
  516. static std::function<void(const char* js)> evaluateFn;
  517. static std::function<void()> reloadFn;
  518. static std::function<void()> terminateFn;
  519. // -----------------------------------------------------------------------------------------------------------
  520. struct GtkContainer;
  521. struct GtkPlug;
  522. struct GtkWidget;
  523. struct GtkWindow;
  524. struct JSCValue;
  525. struct WebKitJavascriptResult;
  526. struct WebKitSettings;
  527. struct WebKitUserContentManager;
  528. struct WebKitWebView;
  529. typedef int gboolean;
  530. #define G_CALLBACK(p) reinterpret_cast<void*>(p)
  531. #define GTK_CONTAINER(p) reinterpret_cast<GtkContainer*>(p)
  532. #define GTK_PLUG(p) reinterpret_cast<GtkPlug*>(p)
  533. #define GTK_WINDOW(p) reinterpret_cast<GtkWindow*>(p)
  534. #define WEBKIT_WEB_VIEW(p) reinterpret_cast<WebKitWebView*>(p)
  535. // struct QApplication;
  536. // struct QUrl;
  537. // struct QWebEngineView;
  538. // struct QWindow;
  539. // -----------------------------------------------------------------------------------------------------------
  540. #define JOIN(A, B) A ## B
  541. #define AUTOSYM(S) \
  542. using JOIN(gtk3_, S) = decltype(&S); \
  543. JOIN(gtk3_, S) S = reinterpret_cast<JOIN(gtk3_, S)>(dlsym(nullptr, #S)); \
  544. DISTRHO_SAFE_ASSERT_RETURN(S != nullptr, false);
  545. #define CSYM(S, NAME) \
  546. S NAME = reinterpret_cast<S>(dlsym(nullptr, #NAME)); \
  547. DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false);
  548. #define CPPSYM(S, NAME, SN) \
  549. S NAME = reinterpret_cast<S>(dlsym(nullptr, #SN)); \
  550. DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false);
  551. // -----------------------------------------------------------------------------------------------------------
  552. // gtk3 variant
  553. static int gtk_js_cb(WebKitUserContentManager*, WebKitJavascriptResult* const result, void* const arg)
  554. {
  555. void* const lib = static_cast<void*>(arg);
  556. DISTRHO_SAFE_ASSERT_RETURN(lib != nullptr, false);
  557. using g_free_t = void (*)(void*);
  558. using jsc_value_to_string_t = char* (*)(JSCValue*);
  559. using webkit_javascript_result_get_js_value_t = JSCValue* (*)(WebKitJavascriptResult*);
  560. CSYM(g_free_t, g_free)
  561. CSYM(jsc_value_to_string_t, jsc_value_to_string)
  562. CSYM(webkit_javascript_result_get_js_value_t, webkit_javascript_result_get_js_value)
  563. JSCValue* const value = webkit_javascript_result_get_js_value(result);
  564. DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, false);
  565. char* const string = jsc_value_to_string(value);
  566. DISTRHO_SAFE_ASSERT_RETURN(string != nullptr, false);
  567. d_stdout("js call received with data '%s'", string);
  568. g_free(string);
  569. return 0;
  570. }
  571. static bool gtk3(Display* const display,
  572. const Window winId,
  573. const uint x,
  574. const uint y,
  575. const uint width,
  576. const uint height,
  577. double scaleFactor,
  578. const char* const url)
  579. {
  580. void* lib;
  581. if ((lib = dlopen("libwebkit2gtk-4.0.so.37", RTLD_NOW|RTLD_GLOBAL)) == nullptr ||
  582. (lib = dlopen("libwebkit2gtk-4.0.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  583. return false;
  584. using g_signal_connect_data_t = ulong (*)(void*, const char*, void*, void*, void*, int);
  585. using gdk_set_allowed_backends_t = void (*)(const char*);
  586. using gtk_container_add_t = void (*)(GtkContainer*, GtkWidget*);
  587. using gtk_init_check_t = gboolean (*)(int*, char***);
  588. using gtk_main_t = void (*)();
  589. using gtk_main_quit_t = void (*)();
  590. using gtk_plug_get_id_t = Window (*)(GtkPlug*);
  591. using gtk_plug_new_t = GtkWidget* (*)(Window);
  592. using gtk_widget_show_all_t = void (*)(GtkWidget*);
  593. using gtk_window_move_t = void (*)(GtkWindow*, int, int);
  594. using gtk_window_set_default_size_t = void (*)(GtkWindow*, int, int);
  595. using webkit_settings_new_t = WebKitSettings* (*)();
  596. using webkit_settings_set_enable_developer_extras_t = void (*)(WebKitSettings*, gboolean);
  597. using webkit_settings_set_enable_write_console_messages_to_stdout_t = void (*)(WebKitSettings*, gboolean);
  598. using webkit_settings_set_hardware_acceleration_policy_t = void (*)(WebKitSettings*, int);
  599. using webkit_settings_set_javascript_can_access_clipboard_t = void (*)(WebKitSettings*, gboolean);
  600. using webkit_user_content_manager_register_script_message_handler_t = gboolean (*)(WebKitUserContentManager*, const char*);
  601. using webkit_web_view_evaluate_javascript_t = void* (*)(WebKitWebView*, const char*, ssize_t, const char*, const char*, void*, void*, void*);
  602. using webkit_web_view_get_user_content_manager_t = WebKitUserContentManager* (*)(WebKitWebView*);
  603. using webkit_web_view_load_uri_t = void (*)(WebKitWebView*, const char*);
  604. using webkit_web_view_new_with_settings_t = GtkWidget* (*)(WebKitSettings*);
  605. using webkit_web_view_run_javascript_t = void* (*)(WebKitWebView*, const char*, void*, void*, void*);
  606. using webkit_web_view_set_background_color_t = void (*)(WebKitWebView*, const double*);
  607. CSYM(g_signal_connect_data_t, g_signal_connect_data)
  608. CSYM(gdk_set_allowed_backends_t, gdk_set_allowed_backends)
  609. CSYM(gtk_container_add_t, gtk_container_add)
  610. CSYM(gtk_init_check_t, gtk_init_check)
  611. CSYM(gtk_main_t, gtk_main)
  612. CSYM(gtk_main_quit_t, gtk_main_quit)
  613. CSYM(gtk_plug_get_id_t, gtk_plug_get_id)
  614. CSYM(gtk_plug_new_t, gtk_plug_new)
  615. CSYM(gtk_widget_show_all_t, gtk_widget_show_all)
  616. CSYM(gtk_window_move_t, gtk_window_move)
  617. CSYM(gtk_window_set_default_size_t, gtk_window_set_default_size)
  618. CSYM(webkit_settings_new_t, webkit_settings_new)
  619. CSYM(webkit_settings_set_enable_developer_extras_t, webkit_settings_set_enable_developer_extras)
  620. CSYM(webkit_settings_set_enable_write_console_messages_to_stdout_t, webkit_settings_set_enable_write_console_messages_to_stdout)
  621. CSYM(webkit_settings_set_hardware_acceleration_policy_t, webkit_settings_set_hardware_acceleration_policy)
  622. CSYM(webkit_settings_set_javascript_can_access_clipboard_t, webkit_settings_set_javascript_can_access_clipboard)
  623. CSYM(webkit_user_content_manager_register_script_message_handler_t, webkit_user_content_manager_register_script_message_handler)
  624. CSYM(webkit_web_view_get_user_content_manager_t, webkit_web_view_get_user_content_manager)
  625. CSYM(webkit_web_view_load_uri_t, webkit_web_view_load_uri)
  626. CSYM(webkit_web_view_new_with_settings_t, webkit_web_view_new_with_settings)
  627. CSYM(webkit_web_view_set_background_color_t, webkit_web_view_set_background_color)
  628. // special case for legacy API handling
  629. 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"));
  630. 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"));
  631. DISTRHO_SAFE_ASSERT_RETURN(webkit_web_view_evaluate_javascript != nullptr || webkit_web_view_run_javascript != nullptr, false);
  632. const int gdkScale = std::fmod(scaleFactor, 1.0) >= 0.75
  633. ? static_cast<int>(scaleFactor + 0.5)
  634. : static_cast<int>(scaleFactor);
  635. if (gdkScale != 1)
  636. {
  637. char scale[8] = {};
  638. std::snprintf(scale, 7, "%d", gdkScale);
  639. setenv("GDK_SCALE", scale, 1);
  640. std::snprintf(scale, 7, "%.2f", (1.0 / scaleFactor) * 1.2);
  641. setenv("GDK_DPI_SCALE", scale, 1);
  642. }
  643. else if (scaleFactor > 1.0)
  644. {
  645. char scale[8] = {};
  646. std::snprintf(scale, 7, "%.2f", (1.0 / scaleFactor) * 1.4);
  647. setenv("GDK_DPI_SCALE", scale, 1);
  648. }
  649. scaleFactor /= gdkScale;
  650. gdk_set_allowed_backends("x11");
  651. if (! gtk_init_check (nullptr, nullptr))
  652. return false;
  653. GtkWidget* const window = gtk_plug_new(winId);
  654. DISTRHO_SAFE_ASSERT_RETURN(window != nullptr, false);
  655. gtk_window_set_default_size(GTK_WINDOW(window),
  656. (width - x) * scaleFactor,
  657. (height - y) * scaleFactor);
  658. gtk_window_move(GTK_WINDOW(window), x * scaleFactor, y * scaleFactor);
  659. WebKitSettings* const settings = webkit_settings_new();
  660. DISTRHO_SAFE_ASSERT_RETURN(settings != nullptr, false);
  661. // TODO DOMPasteAllowed
  662. webkit_settings_set_javascript_can_access_clipboard(settings, true);
  663. webkit_settings_set_hardware_acceleration_policy(settings, 2 /* WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER */);
  664. // if (debug)
  665. {
  666. webkit_settings_set_enable_developer_extras(settings, true);
  667. webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
  668. }
  669. GtkWidget* const webview = webkit_web_view_new_with_settings(settings);
  670. DISTRHO_SAFE_ASSERT_RETURN(webview != nullptr, false);
  671. const double color[] = {49.0/255, 54.0/255, 59.0/255, 1};
  672. webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(webview), color);
  673. if (WebKitUserContentManager* const manager = webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(webview)))
  674. {
  675. g_signal_connect_data(manager, "script-message-received::external", G_CALLBACK(gtk_js_cb), lib, nullptr, 0);
  676. webkit_user_content_manager_register_script_message_handler(manager, "external");
  677. }
  678. webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url);
  679. gtk_container_add(GTK_CONTAINER(window), webview);
  680. gtk_widget_show_all(window);
  681. Window wid = gtk_plug_get_id(GTK_PLUG(window));
  682. XMapWindow(display, wid);
  683. XFlush(display);
  684. evaluateFn = [=](const char* const js){
  685. if (webkit_web_view_evaluate_javascript != nullptr)
  686. webkit_web_view_evaluate_javascript(WEBKIT_WEB_VIEW(webview), js, -1,
  687. nullptr, nullptr, nullptr, nullptr, nullptr);
  688. else
  689. webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(webview), js, nullptr, nullptr, nullptr);
  690. };
  691. reloadFn = [=](){
  692. webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url);
  693. };
  694. terminateFn = [=](){
  695. d_stdout("terminateFn");
  696. static bool quit = true;
  697. if (quit)
  698. {
  699. quit = false;
  700. gtk_main_quit();
  701. }
  702. };
  703. gtk_main();
  704. d_stdout("quit");
  705. dlclose(lib);
  706. return true;
  707. }
  708. #if 0
  709. // -----------------------------------------------------------------------------------------------------------
  710. // qt5webengine variant
  711. static bool qt5webengine(const Window winId, const double scaleFactor, const char* const url)
  712. {
  713. void* lib;
  714. if ((lib = dlopen("libQt5WebEngineWidgets.so.5", RTLD_NOW|RTLD_GLOBAL)) == nullptr ||
  715. (lib = dlopen("libQt5WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  716. return false;
  717. using QApplication__init_t = void (*)(QApplication*, int&, char**, int);
  718. using QApplication_exec_t = void (*)();
  719. using QApplication_setAttribute_t = void (*)(Qt::ApplicationAttribute, bool);
  720. using QString__init_t = void (*)(void*, const QChar*, ptrdiff_t);
  721. using QUrl__init_t = void (*)(void*, const QString&, int /* QUrl::ParsingMode */);
  722. using QWebEngineView__init_t = void (*)(QWebEngineView*, void*);
  723. using QWebEngineView_move_t = void (*)(QWebEngineView*, const QPoint&);
  724. using QWebEngineView_resize_t = void (*)(QWebEngineView*, const QSize&);
  725. using QWebEngineView_setUrl_t = void (*)(QWebEngineView*, const QUrl&);
  726. using QWebEngineView_show_t = void (*)(QWebEngineView*);
  727. using QWebEngineView_winId_t = ulonglong (*)(QWebEngineView*);
  728. using QWebEngineView_windowHandle_t = QWindow* (*)(QWebEngineView*);
  729. using QWindow_fromWinId_t = QWindow* (*)(ulonglong);
  730. using QWindow_setParent_t = void (*)(QWindow*, void*);
  731. CPPSYM(QApplication__init_t, QApplication__init, _ZN12QApplicationC1ERiPPci)
  732. CPPSYM(QApplication_exec_t, QApplication_exec, _ZN15QGuiApplication4execEv)
  733. CPPSYM(QApplication_setAttribute_t, QApplication_setAttribute, _ZN16QCoreApplication12setAttributeEN2Qt20ApplicationAttributeEb)
  734. CPPSYM(QString__init_t, QString__init, _ZN7QStringC2EPK5QChari)
  735. CPPSYM(QUrl__init_t, QUrl__init, _ZN4QUrlC1ERK7QStringNS_11ParsingModeE)
  736. CPPSYM(QWebEngineView__init_t, QWebEngineView__init, _ZN14QWebEngineViewC1EP7QWidget)
  737. CPPSYM(QWebEngineView_move_t, QWebEngineView_move, _ZN7QWidget4moveERK6QPoint)
  738. CPPSYM(QWebEngineView_resize_t, QWebEngineView_resize, _ZN7QWidget6resizeERK5QSize)
  739. CPPSYM(QWebEngineView_setUrl_t, QWebEngineView_setUrl, _ZN14QWebEngineView6setUrlERK4QUrl)
  740. CPPSYM(QWebEngineView_show_t, QWebEngineView_show, _ZN7QWidget4showEv)
  741. CPPSYM(QWebEngineView_winId_t, QWebEngineView_winId, _ZNK7QWidget5winIdEv)
  742. CPPSYM(QWebEngineView_windowHandle_t, QWebEngineView_windowHandle, _ZNK7QWidget12windowHandleEv)
  743. CPPSYM(QWindow_fromWinId_t, QWindow_fromWinId, _ZN7QWindow9fromWinIdEy)
  744. CPPSYM(QWindow_setParent_t, QWindow_setParent, _ZN7QWindow9setParentEPS_)
  745. unsetenv("QT_FONT_DPI");
  746. unsetenv("QT_SCREEN_SCALE_FACTORS");
  747. unsetenv("QT_USE_PHYSICAL_DPI");
  748. setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0", 1);
  749. char scale[8] = {};
  750. std::snprintf(scale, 7, "%.2f", scaleFactor);
  751. setenv("QT_SCALE_FACTOR", scale, 1);
  752. QApplication_setAttribute(Qt::AA_X11InitThreads, true);
  753. QApplication_setAttribute(Qt::AA_EnableHighDpiScaling, true);
  754. QApplication_setAttribute(Qt::AA_UseHighDpiPixmaps, true);
  755. static int argc = 0;
  756. static char* argv[] = { nullptr };
  757. uint8_t _app[64]; // sizeof(QApplication) == 16
  758. QApplication* const app = reinterpret_cast<QApplication*>(_app);
  759. QApplication__init(app, argc, argv, 0);
  760. uint8_t _qstrurl[32]; // sizeof(QString) == 8
  761. QString* const qstrurl(reinterpret_cast<QString*>(_qstrurl));
  762. {
  763. const size_t url_len = std::strlen(url);
  764. QChar* const url_qchar = new QChar[url_len + 1];
  765. for (size_t i = 0; i < url_len; ++i)
  766. url_qchar[i] = QChar(url[i]);
  767. url_qchar[url_len] = 0;
  768. QString__init(qstrurl, url_qchar, url_len);
  769. }
  770. uint8_t _qurl[32]; // sizeof(QUrl) == 8
  771. QUrl* const qurl(reinterpret_cast<QUrl*>(_qurl));
  772. QUrl__init(qurl, *qstrurl, 1 /* QUrl::StrictMode */);
  773. uint8_t _webview[128]; // sizeof(QWebEngineView) == 56
  774. QWebEngineView* const webview = reinterpret_cast<QWebEngineView*>(_webview);
  775. QWebEngineView__init(webview, nullptr);
  776. QWebEngineView_move(webview, QPoint(0, kVerticalOffset));
  777. QWebEngineView_resize(webview, QSize(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset));
  778. QWebEngineView_winId(webview);
  779. QWindow_setParent(QWebEngineView_windowHandle(webview), QWindow_fromWinId(winId));
  780. QWebEngineView_setUrl(webview, *qurl);
  781. QWebEngineView_show(webview);
  782. reloadFn = [=](){
  783. QWebEngineView_setUrl(webview, *qurl);
  784. };
  785. terminateFn = [=](){
  786. // TODO
  787. };
  788. QApplication_exec();
  789. dlclose(lib);
  790. return true;
  791. }
  792. // -----------------------------------------------------------------------------------------------------------
  793. // qt6webengine variant (same as qt5 but `QString__init_t` has different arguments)
  794. static bool qt6webengine(const Window winId, const double scaleFactor, const char* const url)
  795. {
  796. void* lib;
  797. if ((lib = dlopen("libQt6WebEngineWidgets.so.6", RTLD_NOW|RTLD_GLOBAL)) == nullptr ||
  798. (lib = dlopen("libQt6WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  799. return false;
  800. using QApplication__init_t = void (*)(QApplication*, int&, char**, int);
  801. using QApplication_exec_t = void (*)();
  802. using QApplication_setAttribute_t = void (*)(Qt::ApplicationAttribute, bool);
  803. using QString__init_t = void (*)(void*, const QChar*, long long);
  804. using QUrl__init_t = void (*)(void*, const QString&, int /* QUrl::ParsingMode */);
  805. using QWebEngineView__init_t = void (*)(QWebEngineView*, void*);
  806. using QWebEngineView_move_t = void (*)(QWebEngineView*, const QPoint&);
  807. using QWebEngineView_resize_t = void (*)(QWebEngineView*, const QSize&);
  808. using QWebEngineView_setUrl_t = void (*)(QWebEngineView*, const QUrl&);
  809. using QWebEngineView_show_t = void (*)(QWebEngineView*);
  810. using QWebEngineView_winId_t = ulonglong (*)(QWebEngineView*);
  811. using QWebEngineView_windowHandle_t = QWindow* (*)(QWebEngineView*);
  812. using QWindow_fromWinId_t = QWindow* (*)(ulonglong);
  813. using QWindow_setParent_t = void (*)(QWindow*, void*);
  814. CPPSYM(QApplication__init_t, QApplication__init, _ZN12QApplicationC1ERiPPci)
  815. CPPSYM(QApplication_exec_t, QApplication_exec, _ZN15QGuiApplication4execEv)
  816. CPPSYM(QApplication_setAttribute_t, QApplication_setAttribute, _ZN16QCoreApplication12setAttributeEN2Qt20ApplicationAttributeEb)
  817. CPPSYM(QString__init_t, QString__init, _ZN7QStringC2EPK5QCharx)
  818. CPPSYM(QUrl__init_t, QUrl__init, _ZN4QUrlC1ERK7QStringNS_11ParsingModeE)
  819. CPPSYM(QWebEngineView__init_t, QWebEngineView__init, _ZN14QWebEngineViewC1EP7QWidget)
  820. CPPSYM(QWebEngineView_move_t, QWebEngineView_move, _ZN7QWidget4moveERK6QPoint)
  821. CPPSYM(QWebEngineView_resize_t, QWebEngineView_resize, _ZN7QWidget6resizeERK5QSize)
  822. CPPSYM(QWebEngineView_setUrl_t, QWebEngineView_setUrl, _ZN14QWebEngineView6setUrlERK4QUrl)
  823. CPPSYM(QWebEngineView_show_t, QWebEngineView_show, _ZN7QWidget4showEv)
  824. CPPSYM(QWebEngineView_winId_t, QWebEngineView_winId, _ZNK7QWidget5winIdEv)
  825. CPPSYM(QWebEngineView_windowHandle_t, QWebEngineView_windowHandle, _ZNK7QWidget12windowHandleEv)
  826. CPPSYM(QWindow_fromWinId_t, QWindow_fromWinId, _ZN7QWindow9fromWinIdEy)
  827. CPPSYM(QWindow_setParent_t, QWindow_setParent, _ZN7QWindow9setParentEPS_)
  828. unsetenv("QT_FONT_DPI");
  829. unsetenv("QT_SCREEN_SCALE_FACTORS");
  830. unsetenv("QT_USE_PHYSICAL_DPI");
  831. setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0", 1);
  832. char scale[8] = {};
  833. std::snprintf(scale, 7, "%.2f", scaleFactor);
  834. setenv("QT_SCALE_FACTOR", scale, 1);
  835. QApplication_setAttribute(Qt::AA_X11InitThreads, true);
  836. QApplication_setAttribute(Qt::AA_EnableHighDpiScaling, true);
  837. QApplication_setAttribute(Qt::AA_UseHighDpiPixmaps, true);
  838. static int argc = 0;
  839. static char* argv[] = { nullptr };
  840. uint8_t _app[64]; // sizeof(QApplication) == 16
  841. QApplication* const app = reinterpret_cast<QApplication*>(_app);
  842. QApplication__init(app, argc, argv, 0);
  843. uint8_t _qstrurl[32]; // sizeof(QString) == 8
  844. QString* const qstrurl(reinterpret_cast<QString*>(_qstrurl));
  845. {
  846. const size_t url_len = std::strlen(url);
  847. QChar* const url_qchar = new QChar[url_len + 1];
  848. for (size_t i = 0; i < url_len; ++i)
  849. url_qchar[i] = QChar(url[i]);
  850. url_qchar[url_len] = 0;
  851. QString__init(qstrurl, url_qchar, url_len);
  852. }
  853. uint8_t _qurl[32]; // sizeof(QUrl) == 8
  854. QUrl* const qurl(reinterpret_cast<QUrl*>(_qurl));
  855. QUrl__init(qurl, *qstrurl, 1 /* QUrl::StrictMode */);
  856. uint8_t _webview[128]; // sizeof(QWebEngineView) == 56
  857. QWebEngineView* const webview = reinterpret_cast<QWebEngineView*>(_webview);
  858. QWebEngineView__init(webview, nullptr);
  859. QWebEngineView_move(webview, QPoint(0, kVerticalOffset));
  860. QWebEngineView_resize(webview, QSize(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset));
  861. QWebEngineView_winId(webview);
  862. QWindow_setParent(QWebEngineView_windowHandle(webview), QWindow_fromWinId(winId));
  863. QWebEngineView_setUrl(webview, *qurl);
  864. QWebEngineView_show(webview);
  865. reloadFn = [=](){
  866. QWebEngineView_setUrl(webview, *qurl);
  867. };
  868. terminateFn = [=](){
  869. // TODO
  870. };
  871. QApplication_exec();
  872. dlclose(lib);
  873. return true;
  874. }
  875. #endif
  876. // -----------------------------------------------------------------------------------------------------------
  877. // startup via ld-linux
  878. static void signalHandler(const int sig)
  879. {
  880. switch (sig)
  881. {
  882. case SIGINT:
  883. case SIGTERM:
  884. terminateFn();
  885. break;
  886. case SIGUSR1:
  887. reloadFn();
  888. break;
  889. case SIGUSR2:
  890. evaluateFn("typeof(parameterChanged) === 'function' && parameterChanged(0, 0);");
  891. break;
  892. }
  893. }
  894. int dpf_webview_start(int /* argc */, char** /* argv[] */)
  895. {
  896. uselocale(newlocale(LC_NUMERIC_MASK, "C", nullptr));
  897. const char* const envScaleFactor = std::getenv("DPF_WEB_VIEW_SCALE_FACTOR");
  898. DISTRHO_SAFE_ASSERT_RETURN(envScaleFactor != nullptr, 1);
  899. const char* const envWinId = std::getenv("DPF_WEB_VIEW_WIN_ID");
  900. DISTRHO_SAFE_ASSERT_RETURN(envWinId != nullptr, 1);
  901. const Window winId = std::strtoul(envWinId, nullptr, 10);
  902. DISTRHO_SAFE_ASSERT_RETURN(winId != 0, 1);
  903. const double scaleFactor = std::atof(envScaleFactor);
  904. DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0, 1);
  905. Display* const display = XOpenDisplay(nullptr);
  906. DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1);
  907. const char* url = "file:///home/falktx/Source/DISTRHO/DPF/examples/WebMeters/index.html";
  908. struct sigaction sig = {};
  909. sig.sa_handler = signalHandler;
  910. sig.sa_flags = SA_RESTART;
  911. sigemptyset(&sig.sa_mask);
  912. sigaction(SIGINT, &sig, nullptr);
  913. sigaction(SIGTERM, &sig, nullptr);
  914. sigaction(SIGUSR1, &sig, nullptr);
  915. sigaction(SIGUSR2, &sig, nullptr);
  916. // qt5webengine(winId, scaleFactor, url) ||
  917. // qt6webengine(winId, scaleFactor, url) ||
  918. gtk3(display, winId, 0, 0, 600, 400, scaleFactor, url);
  919. XCloseDisplay(display);
  920. return 0;
  921. }
  922. // --------------------------------------------------------------------------------------------------------------------
  923. #endif // WEB_VIEW_USING_X11_IPC
  924. #ifdef WEB_VIEW_DGL_NAMESPACE
  925. END_NAMESPACE_DGL
  926. #else
  927. END_NAMESPACE_DISTRHO
  928. #endif
  929. #undef MACRO_NAME
  930. #undef MACRO_NAME2
  931. #undef WEB_VIEW_DISTRHO_NAMESPACE
  932. #undef WEB_VIEW_DGL_NAMESPACE