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.

930 lines
33KB

  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. #define WEB_VIEW_USING_CHOC 0
  23. #ifndef WEB_VIEW_USING_CHOC
  24. # define WEB_VIEW_USING_CHOC 0
  25. #elif WEB_VIEW_USING_CHOC && !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS))
  26. # undef WEB_VIEW_USING_CHOC
  27. # define WEB_VIEW_USING_CHOC 0
  28. #endif
  29. #if defined(DISTRHO_OS_MAC) && !WEB_VIEW_USING_CHOC
  30. # undef WEB_VIEW_USING_MACOS_WEBKIT
  31. # define WEB_VIEW_USING_MACOS_WEBKIT 1
  32. #else
  33. # undef WEB_VIEW_USING_MACOS_WEBKIT
  34. # define WEB_VIEW_USING_MACOS_WEBKIT 0
  35. #endif
  36. #if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX)
  37. # undef WEB_VIEW_USING_X11_IPC
  38. # define WEB_VIEW_USING_X11_IPC 1
  39. #else
  40. # undef WEB_VIEW_USING_X11_IPC
  41. # define WEB_VIEW_USING_X11_IPC 0
  42. #endif
  43. #if WEB_VIEW_USING_CHOC
  44. # define WC_ERR_INVALID_CHARS 0
  45. # include "../CHOC/gui/choc_WebView.h"
  46. #elif WEB_VIEW_USING_MACOS_WEBKIT
  47. # include <Cocoa/Cocoa.h>
  48. # include <WebKit/WebKit.h>
  49. #elif WEB_VIEW_USING_X11_IPC
  50. // #define QT_NO_VERSION_TAGGING
  51. // #include <QtCore/QChar>
  52. // #include <QtCore/QPoint>
  53. // #include <QtCore/QSize>
  54. // #undef signals
  55. # include "ChildProcess.hpp"
  56. # include "String.hpp"
  57. # include <clocale>
  58. # include <cstdio>
  59. # include <dlfcn.h>
  60. # include <functional>
  61. # include <linux/limits.h>
  62. # include <X11/Xlib.h>
  63. #endif
  64. // -----------------------------------------------------------------------------------------------------------
  65. #if WEB_VIEW_USING_MACOS_WEBKIT
  66. #define MACRO_NAME2(a, b, c) a ## b ## c
  67. #define MACRO_NAME(a, b, c) MACRO_NAME2(a, b, c)
  68. #define WEB_VIEW_DELEGATE_CLASS_NAME \
  69. MACRO_NAME(WebViewDelegate_, _, DISTRHO_NAMESPACE)
  70. @interface WEB_VIEW_DELEGATE_CLASS_NAME : NSObject<WKUIDelegate>
  71. @end
  72. @implementation WEB_VIEW_DELEGATE_CLASS_NAME
  73. - (void)webView:(WKWebView*)webview
  74. runJavaScriptAlertPanelWithMessage:(NSString*)message
  75. initiatedByFrame:(WKFrameInfo*)frame
  76. completionHandler:(void (^)(void))completionHandler
  77. {
  78. NSAlert* const alert = [[NSAlert alloc] init];
  79. [alert addButtonWithTitle:@"OK"];
  80. [alert setInformativeText:message];
  81. [alert setMessageText:@"Alert"];
  82. dispatch_async(dispatch_get_main_queue(), ^
  83. {
  84. [alert beginSheetModalForWindow:[webview window]
  85. completionHandler:^(NSModalResponse)
  86. {
  87. completionHandler();
  88. [alert release];
  89. }];
  90. });
  91. }
  92. - (void)webView:(WKWebView*)webview
  93. runJavaScriptConfirmPanelWithMessage:(NSString*)message
  94. initiatedByFrame:(WKFrameInfo*)frame
  95. completionHandler:(void (^)(BOOL))completionHandler
  96. {
  97. NSAlert* const alert = [[NSAlert alloc] init];
  98. [alert addButtonWithTitle:@"OK"];
  99. [alert addButtonWithTitle:@"Cancel"];
  100. [alert setInformativeText:message];
  101. [alert setMessageText:@"Confirm"];
  102. dispatch_async(dispatch_get_main_queue(), ^
  103. {
  104. [alert beginSheetModalForWindow:[webview window]
  105. completionHandler:^(NSModalResponse result)
  106. {
  107. completionHandler(result == NSAlertFirstButtonReturn);
  108. [alert release];
  109. }];
  110. });
  111. }
  112. - (void)webView:(WKWebView*)webview
  113. runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt
  114. defaultText:(NSString*)defaultText
  115. initiatedByFrame:(WKFrameInfo*)frame
  116. completionHandler:(void (^)(NSString*))completionHandler
  117. {
  118. NSTextField* const input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 250, 30)];
  119. [input setStringValue:defaultText];
  120. NSAlert* const alert = [[NSAlert alloc] init];
  121. [alert setAccessoryView:input];
  122. [alert addButtonWithTitle:@"OK"];
  123. [alert addButtonWithTitle:@"Cancel"];
  124. [alert setInformativeText:prompt];
  125. [alert setMessageText: @"Prompt"];
  126. dispatch_async(dispatch_get_main_queue(), ^
  127. {
  128. [alert beginSheetModalForWindow:[webview window]
  129. completionHandler:^(NSModalResponse result)
  130. {
  131. [input validateEditing];
  132. completionHandler(result == NSAlertFirstButtonReturn ? [input stringValue] : nil);
  133. [alert release];
  134. }];
  135. });
  136. }
  137. - (void)webView:(WKWebView*)webview
  138. runOpenPanelWithParameters:(WKOpenPanelParameters*)params
  139. initiatedByFrame:(WKFrameInfo*)frame
  140. completionHandler:(void (^)(NSArray<NSURL*>*))completionHandler
  141. {
  142. NSOpenPanel* const panel = [[NSOpenPanel alloc] init];
  143. [panel setAllowsMultipleSelection:[params allowsMultipleSelection]];
  144. // [panel setAllowedFileTypes:(NSArray<NSString*>*)[params _allowedFileExtensions]];
  145. [panel setCanChooseDirectories:[params allowsDirectories]];
  146. [panel setCanChooseFiles:![params allowsDirectories]];
  147. dispatch_async(dispatch_get_main_queue(), ^
  148. {
  149. [panel beginSheetModalForWindow:[webview window]
  150. completionHandler:^(NSModalResponse result)
  151. {
  152. completionHandler(result == NSModalResponseOK ? [panel URLs] : nil);
  153. [panel release];
  154. }];
  155. });
  156. }
  157. @end
  158. #endif // WEB_VIEW_USING_MACOS_WEBKIT
  159. // -----------------------------------------------------------------------------------------------------------
  160. #ifdef WEB_VIEW_DGL_NAMESPACE
  161. START_NAMESPACE_DGL
  162. using DISTRHO_NAMESPACE::String;
  163. #else
  164. START_NAMESPACE_DISTRHO
  165. #endif
  166. // -----------------------------------------------------------------------------------------------------------
  167. struct WebViewData {
  168. #if WEB_VIEW_USING_CHOC
  169. choc::ui::WebView* const webview;
  170. #elif WEB_VIEW_USING_MACOS_WEBKIT
  171. NSView* const view;
  172. WKWebView* const webview;
  173. NSURLRequest* const urlreq;
  174. WEB_VIEW_DELEGATE_CLASS_NAME* const delegate;
  175. #elif WEB_VIEW_USING_X11_IPC
  176. ChildProcess p;
  177. ::Display* display;
  178. ::Window childWindow;
  179. ::Window ourWindow;
  180. #endif
  181. };
  182. // -----------------------------------------------------------------------------------------------------------
  183. #if WEB_VIEW_USING_CHOC
  184. static std::optional<choc::ui::WebView::Options::Resource> fetch_resource(const std::string& path)
  185. {
  186. d_stdout("requested path %s", path.c_str());
  187. if (path == "/")
  188. {
  189. const std::string html = R"PREFIX(
  190. <html>
  191. <head>
  192. <style>
  193. html, body { background: black; background-image: url(img.svg); }
  194. </style>
  195. <script>
  196. function parameterChanged(index, value) {
  197. console.log("parameterChanged received", index, value);
  198. }
  199. </script>
  200. </head>
  201. <body>
  202. hello world!
  203. </body>
  204. </html>
  205. )PREFIX";
  206. const std::vector<uint8_t> data(html.begin(), html.end());
  207. return choc::ui::WebView::Options::Resource{ data, "text/html" };
  208. }
  209. if (path == "/img.svg")
  210. {
  211. const std::string html = R"PREFIX(<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  212. <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
  213. <!-- based on https://github.com/n0jo/rackwindows/blob/master/res/components/rw_knob_large_dark.svg -->
  214. <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;">
  215. <g id="knobLDark">
  216. <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;"/>
  217. </g>
  218. </svg>
  219. )PREFIX";
  220. const std::vector<uint8_t> data(html.begin(), html.end());
  221. return choc::ui::WebView::Options::Resource{ data, "image/svg+xml" };
  222. }
  223. return {};
  224. }
  225. #elif WEB_VIEW_USING_X11_IPC
  226. static void getFilenameFromFunctionPtr(char filename[PATH_MAX], const void* const ptr)
  227. {
  228. Dl_info info = {};
  229. dladdr(ptr, &info);
  230. if (info.dli_fname[0] == '.')
  231. {
  232. getcwd(filename, PATH_MAX - 1);
  233. std::strncat(filename, info.dli_fname + 1, PATH_MAX - 1);
  234. }
  235. else if (info.dli_fname[0] != '/')
  236. {
  237. getcwd(filename, PATH_MAX - 1);
  238. std::strncat(filename, "/", PATH_MAX - 1);
  239. std::strncat(filename, info.dli_fname, PATH_MAX - 1);
  240. }
  241. else
  242. {
  243. std::strncpy(filename, info.dli_fname, PATH_MAX - 1);
  244. }
  245. }
  246. #endif
  247. // -----------------------------------------------------------------------------------------------------------
  248. WebViewHandle webViewCreate(const uintptr_t windowId,
  249. const uint initialWidth,
  250. const uint initialHeight,
  251. const double scaleFactor,
  252. const WebViewOptions& options)
  253. {
  254. #if WEB_VIEW_USING_CHOC
  255. choc::ui::WebView::Options woptions;
  256. woptions.acceptsFirstMouseClick = true;
  257. woptions.enableDebugMode = true;
  258. woptions.fetchResource = fetch_resource;
  259. std::unique_ptr<choc::ui::WebView> webview = std::make_unique<choc::ui::WebView>(woptions);
  260. DISTRHO_SAFE_ASSERT_RETURN(webview->loadedOK(), nullptr);
  261. void* const handle = webview->getViewHandle();
  262. DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, nullptr);
  263. choc::ui::WebView* const www = webview.get();
  264. webview->bind("setParameterValue", [www](const choc::value::ValueView&) -> choc::value::Value {
  265. static int pp = 0;
  266. std::string toeval = "typeof(parameterChanged) === 'function' && parameterChanged(";
  267. toeval += std::to_string(++pp);
  268. toeval += ", 0.1)";
  269. d_stdout("param received | %s", toeval.c_str());
  270. www->evaluateJavascript(toeval);
  271. return {};
  272. });
  273. #ifdef DISTRHO_OS_MAC
  274. NSView* const view = static_cast<NSView*>(handle);
  275. [reinterpret_cast<NSView*>(windowId) addSubview:view];
  276. [view setFrame:NSMakeRect(options.offset.x,
  277. options.offset.y,
  278. DISTRHO_UI_DEFAULT_WIDTH - options.offset.x,
  279. DISTRHO_UI_DEFAULT_HEIGHT - options.offset.y)];
  280. #else
  281. const HWND hwnd = static_cast<HWND>(handle);
  282. LONG_PTR flags = GetWindowLongPtr(hwnd, -16);
  283. flags = (flags & ~WS_POPUP) | WS_CHILD;
  284. SetWindowLongPtr(hwnd, -16, flags);
  285. SetParent(hwnd, reinterpret_cast<HWND>(windowId));
  286. SetWindowPos(hwnd, nullptr,
  287. options.offset.x * scaleFactor,
  288. options.offset.y * scaleFactor,
  289. (initialWidth - options.offset.x) * scaleFactor,
  290. (initialHeight - options.offset.y) * scaleFactor,
  291. SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
  292. ShowWindow(hwnd, SW_SHOW);
  293. #endif
  294. return new WebViewData{webview.release()};
  295. #elif WEB_VIEW_USING_MACOS_WEBKIT
  296. NSView* const view = reinterpret_cast<NSView*>(windowId);
  297. const CGRect rect = CGRectMake(options.offset.x,
  298. options.offset.y,
  299. (initialWidth - options.offset.x),
  300. (initialHeight - options.offset.y));
  301. WKPreferences* const prefs = [[WKPreferences alloc] init];
  302. [prefs setValue:@YES forKey:@"developerExtrasEnabled"];
  303. WKWebViewConfiguration* const config = [[WKWebViewConfiguration alloc] init];
  304. config.preferences = prefs;
  305. WKWebView* const webview = [[WKWebView alloc] initWithFrame:rect
  306. configuration:config];
  307. [view addSubview:webview];
  308. WEB_VIEW_DELEGATE_CLASS_NAME* const delegate = [[WEB_VIEW_DELEGATE_CLASS_NAME alloc] init];
  309. webview.UIDelegate = delegate;
  310. const char* const url = "https://mastodon.falktx.com/";
  311. NSString* const nsurl = [[NSString alloc] initWithBytes:url
  312. length:std::strlen(url)
  313. encoding:NSUTF8StringEncoding];
  314. NSURLRequest* const urlreq = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: nsurl]];
  315. [webview loadRequest:urlreq];
  316. [webview setHidden:NO];
  317. [nsurl release];
  318. [config release];
  319. [prefs release];
  320. return new WebViewData{view, webview, urlreq, delegate};
  321. #elif WEB_VIEW_USING_X11_IPC
  322. char ldlinux[PATH_MAX] = {};
  323. getFilenameFromFunctionPtr(ldlinux, dlsym(nullptr, "_rtld_global"));
  324. char filename[PATH_MAX] = {};
  325. getFilenameFromFunctionPtr(filename, reinterpret_cast<const void*>(addWebView));
  326. d_stdout("ld-linux is '%s'", ldlinux);
  327. d_stdout("filename is '%s'", filename);
  328. ::Display* const display = XOpenDisplay(nullptr);
  329. DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, nullptr);
  330. // set up custom child environment
  331. uint envsize = 0;
  332. while (environ[envsize] != nullptr)
  333. ++envsize;
  334. char** const envp = new char*[envsize + 5];
  335. {
  336. uint e = 0;
  337. for (uint i = 0; i < envsize; ++i)
  338. {
  339. if (std::strncmp(environ[i], "LD_PRELOAD=", 11) == 0)
  340. continue;
  341. if (std::strncmp(environ[i], "LD_LIBRARY_PATH=", 16) == 0)
  342. continue;
  343. envp[e++] = strdup(environ[i]);
  344. }
  345. envp[e++] = strdup("LANG=en_US.UTF-8");
  346. envp[e++] = ("DPF_WEB_VIEW_SCALE_FACTOR=" + String(scaleFactor)).getAndReleaseBuffer();
  347. envp[e++] = ("DPF_WEB_VIEW_WIN_ID=" +String(windowId)).getAndReleaseBuffer();
  348. for (uint i = e; i < envsize + 5; ++i)
  349. envp[e++] = nullptr;
  350. }
  351. WebViewData* const handle = new WebViewData();
  352. handle->display = display;
  353. handle->childWindow = 0;
  354. handle->ourWindow = windowId;
  355. const char* const args[] = { ldlinux, filename, "dpf-ld-linux-webview", nullptr };
  356. handle->p.start(args, envp);
  357. for (uint i = 0; envp[i] != nullptr; ++i)
  358. std::free(envp[i]);
  359. delete[] envp;
  360. return handle;
  361. #endif
  362. // maybe unused
  363. (void)windowId;
  364. (void)initialWidth;
  365. (void)initialHeight;
  366. (void)scaleFactor;
  367. (void)options;
  368. return nullptr;
  369. }
  370. void webViewDestroy(const WebViewHandle handle)
  371. {
  372. #if WEB_VIEW_USING_CHOC
  373. delete handle->webview;
  374. delete handle;
  375. #elif WEB_VIEW_USING_MACOS_WEBKIT
  376. [handle->webview setHidden:YES];
  377. [handle->webview removeFromSuperview];
  378. [handle->urlreq release];
  379. [handle->delegate release];
  380. delete handle;
  381. #elif WEB_VIEW_USING_X11_IPC
  382. XCloseDisplay(handle->display);
  383. delete handle;
  384. #endif
  385. // maybe unused
  386. (void)handle;
  387. }
  388. void webViewReload(const WebViewHandle handle)
  389. {
  390. #if WEB_VIEW_USING_CHOC
  391. #elif WEB_VIEW_USING_MACOS_WEBKIT
  392. [handle->webview loadRequest:handle->urlreq];
  393. #elif WEB_VIEW_USING_X11_IPC
  394. handle->p.signal(SIGUSR1);
  395. #endif
  396. // maybe unused
  397. (void)handle;
  398. }
  399. void webViewResize(const WebViewHandle handle, const uint width, const uint height, const double scaleFactor)
  400. {
  401. #if WEB_VIEW_USING_CHOC
  402. #ifdef DISTRHO_OS_MAC
  403. NSView* const view = static_cast<NSView*>(handle->webview->getViewHandle());
  404. [view setFrameSize:NSMakeSize(width, height)];
  405. #else
  406. const HWND hwnd = static_cast<HWND>(handle->webview->getViewHandle());
  407. SetWindowPos(hwnd, nullptr, 0, 0,
  408. width * scaleFactor,
  409. height * scaleFactor,
  410. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  411. #endif
  412. #elif WEB_VIEW_USING_MACOS_WEBKIT
  413. [handle->webview setFrameSize:NSMakeSize(width, height)];
  414. #elif WEB_VIEW_USING_X11_IPC
  415. if (handle->childWindow == 0)
  416. {
  417. ::Window rootWindow, parentWindow;
  418. ::Window* childWindows = nullptr;
  419. uint numChildren = 0;
  420. XFlush(handle->display);
  421. XQueryTree(handle->display, handle->ourWindow, &rootWindow, &parentWindow, &childWindows, &numChildren);
  422. if (numChildren == 0 || childWindows == nullptr)
  423. return;
  424. handle->childWindow = childWindows[0];
  425. XFree(childWindows);
  426. }
  427. XMoveResizeWindow(handle->display, handle->childWindow, x, y, width, height);
  428. XFlush(handle->display);
  429. #endif
  430. // maybe unused
  431. (void)handle;
  432. (void)width;
  433. (void)height;
  434. (void)scaleFactor;
  435. }
  436. #if WEB_VIEW_USING_X11_IPC
  437. // -----------------------------------------------------------------------------------------------------------
  438. static std::function<void()> reloadFn;
  439. // -----------------------------------------------------------------------------------------------------------
  440. struct GtkContainer;
  441. struct GtkPlug;
  442. struct GtkWidget;
  443. struct GtkWindow;
  444. struct WebKitSettings;
  445. struct WebKitWebView;
  446. #define GTK_CONTAINER(p) reinterpret_cast<GtkContainer*>(p)
  447. #define GTK_PLUG(p) reinterpret_cast<GtkPlug*>(p)
  448. #define GTK_WINDOW(p) reinterpret_cast<GtkWindow*>(p)
  449. #define WEBKIT_WEB_VIEW(p) reinterpret_cast<WebKitWebView*>(p)
  450. // struct QApplication;
  451. // struct QUrl;
  452. // struct QWebEngineView;
  453. // struct QWindow;
  454. // -----------------------------------------------------------------------------------------------------------
  455. #define JOIN(A, B) A ## B
  456. #define AUTOSYM(S) \
  457. using JOIN(gtk3_, S) = decltype(&S); \
  458. JOIN(gtk3_, S) S = reinterpret_cast<JOIN(gtk3_, S)>(dlsym(nullptr, #S)); \
  459. DISTRHO_SAFE_ASSERT_RETURN(S != nullptr, false);
  460. #define CSYM(S, NAME) \
  461. S NAME = reinterpret_cast<S>(dlsym(nullptr, #NAME)); \
  462. DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false);
  463. #define CPPSYM(S, NAME, SN) \
  464. S NAME = reinterpret_cast<S>(dlsym(nullptr, #SN)); \
  465. DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false);
  466. // -----------------------------------------------------------------------------------------------------------
  467. // gtk3 variant
  468. static bool gtk3(Display* const display,
  469. const Window winId,
  470. const uint x,
  471. const uint y,
  472. const uint width,
  473. const uint height,
  474. double scaleFactor,
  475. const char* const url)
  476. {
  477. void* lib;
  478. if ((lib = dlopen("libwebkit2gtk-4.0.so.37", RTLD_NOW|RTLD_GLOBAL)) == nullptr ||
  479. (lib = dlopen("libwebkit2gtk-4.0.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  480. return false;
  481. using gdk_set_allowed_backends_t = void (*)(const char*);
  482. using gtk_container_add_t = void (*)(GtkContainer*, GtkWidget*);
  483. using gtk_init_check_t = bool (*)(int*, char***);
  484. using gtk_main_t = void (*)();
  485. using gtk_plug_get_id_t = Window (*)(GtkPlug*);
  486. using gtk_plug_new_t = GtkWidget* (*)(Window);
  487. using gtk_widget_show_all_t = void (*)(GtkWidget*);
  488. using gtk_window_move_t = void (*)(GtkWindow*, int, int);
  489. using gtk_window_set_default_size_t = void (*)(GtkWindow*, int, int);
  490. using webkit_settings_new_t = WebKitSettings* (*)();
  491. using webkit_settings_set_hardware_acceleration_policy_t = void (*)(WebKitSettings*, int);
  492. using webkit_settings_set_javascript_can_access_clipboard_t = void (*)(WebKitSettings*, bool);
  493. using webkit_web_view_load_uri_t = void (*)(WebKitWebView*, const char*);
  494. using webkit_web_view_new_with_settings_t = GtkWidget* (*)(WebKitSettings*);
  495. CSYM(gdk_set_allowed_backends_t, gdk_set_allowed_backends)
  496. CSYM(gtk_container_add_t, gtk_container_add)
  497. CSYM(gtk_init_check_t, gtk_init_check)
  498. CSYM(gtk_main_t, gtk_main)
  499. CSYM(gtk_plug_get_id_t, gtk_plug_get_id)
  500. CSYM(gtk_plug_new_t, gtk_plug_new)
  501. CSYM(gtk_widget_show_all_t, gtk_widget_show_all)
  502. CSYM(gtk_window_move_t, gtk_window_move)
  503. CSYM(gtk_window_set_default_size_t, gtk_window_set_default_size)
  504. CSYM(webkit_settings_new_t, webkit_settings_new)
  505. CSYM(webkit_settings_set_hardware_acceleration_policy_t, webkit_settings_set_hardware_acceleration_policy)
  506. CSYM(webkit_settings_set_javascript_can_access_clipboard_t, webkit_settings_set_javascript_can_access_clipboard)
  507. CSYM(webkit_web_view_load_uri_t, webkit_web_view_load_uri)
  508. CSYM(webkit_web_view_new_with_settings_t, webkit_web_view_new_with_settings)
  509. const int gdkScale = std::fmod(scaleFactor, 1.0) >= 0.75
  510. ? static_cast<int>(scaleFactor + 0.5)
  511. : static_cast<int>(scaleFactor);
  512. if (gdkScale != 1)
  513. {
  514. char scale[8] = {};
  515. std::snprintf(scale, 7, "%d", gdkScale);
  516. setenv("GDK_SCALE", scale, 1);
  517. std::snprintf(scale, 7, "%.2f", (1.0 / scaleFactor) * 1.2);
  518. setenv("GDK_DPI_SCALE", scale, 1);
  519. }
  520. else if (scaleFactor > 1.0)
  521. {
  522. char scale[8] = {};
  523. std::snprintf(scale, 7, "%.2f", (1.0 / scaleFactor) * 1.4);
  524. setenv("GDK_DPI_SCALE", scale, 1);
  525. }
  526. scaleFactor /= gdkScale;
  527. gdk_set_allowed_backends("x11");
  528. if (! gtk_init_check (nullptr, nullptr))
  529. return false;
  530. GtkWidget* const window = gtk_plug_new(winId);
  531. DISTRHO_SAFE_ASSERT_RETURN(window != nullptr, false);
  532. gtk_window_set_default_size(GTK_WINDOW(window),
  533. (width - x) * scaleFactor,
  534. (height - y) * scaleFactor);
  535. gtk_window_move(GTK_WINDOW(window), x * scaleFactor, y * scaleFactor);
  536. WebKitSettings* const settings = webkit_settings_new();
  537. DISTRHO_SAFE_ASSERT_RETURN(settings != nullptr, false);
  538. webkit_settings_set_javascript_can_access_clipboard(settings, true);
  539. webkit_settings_set_hardware_acceleration_policy(settings, 2 /* WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER */);
  540. GtkWidget* const webview = webkit_web_view_new_with_settings(settings);
  541. DISTRHO_SAFE_ASSERT_RETURN(webview != nullptr, false);
  542. webkit_web_view_load_uri(WEBKIT_WEB_VIEW (webview), url);
  543. gtk_container_add(GTK_CONTAINER(window), webview);
  544. gtk_widget_show_all(window);
  545. Window wid = gtk_plug_get_id(GTK_PLUG(window));
  546. XMapWindow(display, wid);
  547. XFlush(display);
  548. reloadFn = [=](){
  549. webkit_web_view_load_uri(WEBKIT_WEB_VIEW (webview), url);
  550. };
  551. gtk_main();
  552. dlclose(lib);
  553. return true;
  554. }
  555. #if 0
  556. // -----------------------------------------------------------------------------------------------------------
  557. // qt5webengine variant
  558. static bool qt5webengine(const Window winId, const double scaleFactor, const char* const url)
  559. {
  560. void* lib;
  561. if ((lib = dlopen("libQt5WebEngineWidgets.so.5", RTLD_NOW|RTLD_GLOBAL)) == nullptr ||
  562. (lib = dlopen("libQt5WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  563. return false;
  564. using QApplication__init_t = void (*)(QApplication*, int&, char**, int);
  565. using QApplication_exec_t = void (*)();
  566. using QApplication_setAttribute_t = void (*)(Qt::ApplicationAttribute, bool);
  567. using QString__init_t = void (*)(void*, const QChar*, ptrdiff_t);
  568. using QUrl__init_t = void (*)(void*, const QString&, int /* QUrl::ParsingMode */);
  569. using QWebEngineView__init_t = void (*)(QWebEngineView*, void*);
  570. using QWebEngineView_move_t = void (*)(QWebEngineView*, const QPoint&);
  571. using QWebEngineView_resize_t = void (*)(QWebEngineView*, const QSize&);
  572. using QWebEngineView_setUrl_t = void (*)(QWebEngineView*, const QUrl&);
  573. using QWebEngineView_show_t = void (*)(QWebEngineView*);
  574. using QWebEngineView_winId_t = ulonglong (*)(QWebEngineView*);
  575. using QWebEngineView_windowHandle_t = QWindow* (*)(QWebEngineView*);
  576. using QWindow_fromWinId_t = QWindow* (*)(ulonglong);
  577. using QWindow_setParent_t = void (*)(QWindow*, void*);
  578. CPPSYM(QApplication__init_t, QApplication__init, _ZN12QApplicationC1ERiPPci)
  579. CPPSYM(QApplication_exec_t, QApplication_exec, _ZN15QGuiApplication4execEv)
  580. CPPSYM(QApplication_setAttribute_t, QApplication_setAttribute, _ZN16QCoreApplication12setAttributeEN2Qt20ApplicationAttributeEb)
  581. CPPSYM(QString__init_t, QString__init, _ZN7QStringC2EPK5QChari)
  582. CPPSYM(QUrl__init_t, QUrl__init, _ZN4QUrlC1ERK7QStringNS_11ParsingModeE)
  583. CPPSYM(QWebEngineView__init_t, QWebEngineView__init, _ZN14QWebEngineViewC1EP7QWidget)
  584. CPPSYM(QWebEngineView_move_t, QWebEngineView_move, _ZN7QWidget4moveERK6QPoint)
  585. CPPSYM(QWebEngineView_resize_t, QWebEngineView_resize, _ZN7QWidget6resizeERK5QSize)
  586. CPPSYM(QWebEngineView_setUrl_t, QWebEngineView_setUrl, _ZN14QWebEngineView6setUrlERK4QUrl)
  587. CPPSYM(QWebEngineView_show_t, QWebEngineView_show, _ZN7QWidget4showEv)
  588. CPPSYM(QWebEngineView_winId_t, QWebEngineView_winId, _ZNK7QWidget5winIdEv)
  589. CPPSYM(QWebEngineView_windowHandle_t, QWebEngineView_windowHandle, _ZNK7QWidget12windowHandleEv)
  590. CPPSYM(QWindow_fromWinId_t, QWindow_fromWinId, _ZN7QWindow9fromWinIdEy)
  591. CPPSYM(QWindow_setParent_t, QWindow_setParent, _ZN7QWindow9setParentEPS_)
  592. unsetenv("QT_FONT_DPI");
  593. unsetenv("QT_SCREEN_SCALE_FACTORS");
  594. unsetenv("QT_USE_PHYSICAL_DPI");
  595. setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0", 1);
  596. char scale[8] = {};
  597. std::snprintf(scale, 7, "%.2f", scaleFactor);
  598. setenv("QT_SCALE_FACTOR", scale, 1);
  599. QApplication_setAttribute(Qt::AA_X11InitThreads, true);
  600. QApplication_setAttribute(Qt::AA_EnableHighDpiScaling, true);
  601. QApplication_setAttribute(Qt::AA_UseHighDpiPixmaps, true);
  602. static int argc = 0;
  603. static char* argv[] = { nullptr };
  604. uint8_t _app[64]; // sizeof(QApplication) == 16
  605. QApplication* const app = reinterpret_cast<QApplication*>(_app);
  606. QApplication__init(app, argc, argv, 0);
  607. uint8_t _qstrurl[32]; // sizeof(QString) == 8
  608. QString* const qstrurl(reinterpret_cast<QString*>(_qstrurl));
  609. {
  610. const size_t url_len = std::strlen(url);
  611. QChar* const url_qchar = new QChar[url_len + 1];
  612. for (size_t i = 0; i < url_len; ++i)
  613. url_qchar[i] = QChar(url[i]);
  614. url_qchar[url_len] = 0;
  615. QString__init(qstrurl, url_qchar, url_len);
  616. }
  617. uint8_t _qurl[32]; // sizeof(QUrl) == 8
  618. QUrl* const qurl(reinterpret_cast<QUrl*>(_qurl));
  619. QUrl__init(qurl, *qstrurl, 1 /* QUrl::StrictMode */);
  620. uint8_t _webview[128]; // sizeof(QWebEngineView) == 56
  621. QWebEngineView* const webview = reinterpret_cast<QWebEngineView*>(_webview);
  622. QWebEngineView__init(webview, nullptr);
  623. QWebEngineView_move(webview, QPoint(0, kVerticalOffset));
  624. QWebEngineView_resize(webview, QSize(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset));
  625. QWebEngineView_winId(webview);
  626. QWindow_setParent(QWebEngineView_windowHandle(webview), QWindow_fromWinId(winId));
  627. QWebEngineView_setUrl(webview, *qurl);
  628. QWebEngineView_show(webview);
  629. reloadFn = [=](){
  630. QWebEngineView_setUrl(webview, *qurl);
  631. };
  632. QApplication_exec();
  633. dlclose(lib);
  634. return true;
  635. }
  636. // -----------------------------------------------------------------------------------------------------------
  637. // qt6webengine variant (same as qt5 but `QString__init_t` has different arguments)
  638. static bool qt6webengine(const Window winId, const double scaleFactor, const char* const url)
  639. {
  640. void* lib;
  641. if ((lib = dlopen("libQt6WebEngineWidgets.so.6", RTLD_NOW|RTLD_GLOBAL)) == nullptr ||
  642. (lib = dlopen("libQt6WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
  643. return false;
  644. using QApplication__init_t = void (*)(QApplication*, int&, char**, int);
  645. using QApplication_exec_t = void (*)();
  646. using QApplication_setAttribute_t = void (*)(Qt::ApplicationAttribute, bool);
  647. using QString__init_t = void (*)(void*, const QChar*, long long);
  648. using QUrl__init_t = void (*)(void*, const QString&, int /* QUrl::ParsingMode */);
  649. using QWebEngineView__init_t = void (*)(QWebEngineView*, void*);
  650. using QWebEngineView_move_t = void (*)(QWebEngineView*, const QPoint&);
  651. using QWebEngineView_resize_t = void (*)(QWebEngineView*, const QSize&);
  652. using QWebEngineView_setUrl_t = void (*)(QWebEngineView*, const QUrl&);
  653. using QWebEngineView_show_t = void (*)(QWebEngineView*);
  654. using QWebEngineView_winId_t = ulonglong (*)(QWebEngineView*);
  655. using QWebEngineView_windowHandle_t = QWindow* (*)(QWebEngineView*);
  656. using QWindow_fromWinId_t = QWindow* (*)(ulonglong);
  657. using QWindow_setParent_t = void (*)(QWindow*, void*);
  658. CPPSYM(QApplication__init_t, QApplication__init, _ZN12QApplicationC1ERiPPci)
  659. CPPSYM(QApplication_exec_t, QApplication_exec, _ZN15QGuiApplication4execEv)
  660. CPPSYM(QApplication_setAttribute_t, QApplication_setAttribute, _ZN16QCoreApplication12setAttributeEN2Qt20ApplicationAttributeEb)
  661. CPPSYM(QString__init_t, QString__init, _ZN7QStringC2EPK5QCharx)
  662. CPPSYM(QUrl__init_t, QUrl__init, _ZN4QUrlC1ERK7QStringNS_11ParsingModeE)
  663. CPPSYM(QWebEngineView__init_t, QWebEngineView__init, _ZN14QWebEngineViewC1EP7QWidget)
  664. CPPSYM(QWebEngineView_move_t, QWebEngineView_move, _ZN7QWidget4moveERK6QPoint)
  665. CPPSYM(QWebEngineView_resize_t, QWebEngineView_resize, _ZN7QWidget6resizeERK5QSize)
  666. CPPSYM(QWebEngineView_setUrl_t, QWebEngineView_setUrl, _ZN14QWebEngineView6setUrlERK4QUrl)
  667. CPPSYM(QWebEngineView_show_t, QWebEngineView_show, _ZN7QWidget4showEv)
  668. CPPSYM(QWebEngineView_winId_t, QWebEngineView_winId, _ZNK7QWidget5winIdEv)
  669. CPPSYM(QWebEngineView_windowHandle_t, QWebEngineView_windowHandle, _ZNK7QWidget12windowHandleEv)
  670. CPPSYM(QWindow_fromWinId_t, QWindow_fromWinId, _ZN7QWindow9fromWinIdEy)
  671. CPPSYM(QWindow_setParent_t, QWindow_setParent, _ZN7QWindow9setParentEPS_)
  672. unsetenv("QT_FONT_DPI");
  673. unsetenv("QT_SCREEN_SCALE_FACTORS");
  674. unsetenv("QT_USE_PHYSICAL_DPI");
  675. setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0", 1);
  676. char scale[8] = {};
  677. std::snprintf(scale, 7, "%.2f", scaleFactor);
  678. setenv("QT_SCALE_FACTOR", scale, 1);
  679. QApplication_setAttribute(Qt::AA_X11InitThreads, true);
  680. QApplication_setAttribute(Qt::AA_EnableHighDpiScaling, true);
  681. QApplication_setAttribute(Qt::AA_UseHighDpiPixmaps, true);
  682. static int argc = 0;
  683. static char* argv[] = { nullptr };
  684. uint8_t _app[64]; // sizeof(QApplication) == 16
  685. QApplication* const app = reinterpret_cast<QApplication*>(_app);
  686. QApplication__init(app, argc, argv, 0);
  687. uint8_t _qstrurl[32]; // sizeof(QString) == 8
  688. QString* const qstrurl(reinterpret_cast<QString*>(_qstrurl));
  689. {
  690. const size_t url_len = std::strlen(url);
  691. QChar* const url_qchar = new QChar[url_len + 1];
  692. for (size_t i = 0; i < url_len; ++i)
  693. url_qchar[i] = QChar(url[i]);
  694. url_qchar[url_len] = 0;
  695. QString__init(qstrurl, url_qchar, url_len);
  696. }
  697. uint8_t _qurl[32]; // sizeof(QUrl) == 8
  698. QUrl* const qurl(reinterpret_cast<QUrl*>(_qurl));
  699. QUrl__init(qurl, *qstrurl, 1 /* QUrl::StrictMode */);
  700. uint8_t _webview[128]; // sizeof(QWebEngineView) == 56
  701. QWebEngineView* const webview = reinterpret_cast<QWebEngineView*>(_webview);
  702. QWebEngineView__init(webview, nullptr);
  703. QWebEngineView_move(webview, QPoint(0, kVerticalOffset));
  704. QWebEngineView_resize(webview, QSize(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset));
  705. QWebEngineView_winId(webview);
  706. QWindow_setParent(QWebEngineView_windowHandle(webview), QWindow_fromWinId(winId));
  707. QWebEngineView_setUrl(webview, *qurl);
  708. QWebEngineView_show(webview);
  709. reloadFn = [=](){
  710. QWebEngineView_setUrl(webview, *qurl);
  711. };
  712. QApplication_exec();
  713. dlclose(lib);
  714. return true;
  715. }
  716. #endif
  717. // -----------------------------------------------------------------------------------------------------------
  718. // startup via ld-linux
  719. static void signalHandler(const int sig)
  720. {
  721. DISTRHO_SAFE_ASSERT_RETURN(sig == SIGUSR1,);
  722. reloadFn();
  723. }
  724. int dpf_webview_start(int /* argc */, char** /* argv[] */)
  725. {
  726. uselocale(newlocale(LC_NUMERIC_MASK, "C", nullptr));
  727. const char* const envScaleFactor = std::getenv("DPF_WEB_VIEW_SCALE_FACTOR");
  728. DISTRHO_SAFE_ASSERT_RETURN(envScaleFactor != nullptr, 1);
  729. const char* const envWinId = std::getenv("DPF_WEB_VIEW_WIN_ID");
  730. DISTRHO_SAFE_ASSERT_RETURN(envWinId != nullptr, 1);
  731. const Window winId = std::strtoul(envWinId, nullptr, 10);
  732. DISTRHO_SAFE_ASSERT_RETURN(winId != 0, 1);
  733. const double scaleFactor = std::atof(envScaleFactor);
  734. DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0, 1);
  735. Display* const display = XOpenDisplay(nullptr);
  736. DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1);
  737. // const char* url = "file:///home/falktx/";
  738. const char* url = "https://mastodon.falktx.com/";
  739. struct sigaction sig = {};
  740. sig.sa_handler = signalHandler;
  741. sig.sa_flags = SA_RESTART;
  742. sigemptyset(&sig.sa_mask);
  743. sigaction(SIGUSR1, &sig, nullptr);
  744. // qt5webengine(winId, scaleFactor, url) ||
  745. // qt6webengine(winId, scaleFactor, url) ||
  746. gtk3(display, winId, 0, 0, 600, 400, scaleFactor, url);
  747. XCloseDisplay(display);
  748. return 0;
  749. }
  750. // --------------------------------------------------------------------------------------------------------------------
  751. #endif // WEB_VIEW_USING_X11_IPC
  752. #ifdef WEB_VIEW_DGL_NAMESPACE
  753. END_NAMESPACE_DGL
  754. #else
  755. END_NAMESPACE_DISTRHO
  756. #endif
  757. #undef MACRO_NAME
  758. #undef MACRO_NAME2
  759. #undef WEB_VIEW_DISTRHO_NAMESPACE
  760. #undef WEB_VIEW_DGL_NAMESPACE
  761. #undef WEB_VIEW_NAMESPACE