Editor windows
- Linux (
editor_linux.rs): the plug-in must supportkPlatformTypeX11EmbedWindowID. Each editor opens its own X11 connection and a top-level window (sized fromgetSize, fixed size unlesscanResize), then callsattached.IPlugFrame::resizeViewresizes the window and callsonSize. Closing the window sets a flag; the nextidle()closes the view. - IRunLoop: plug-ins register file descriptors and timers through the frame. They are run by
an
EventLoopinstalled once withset_event_loop; the app installsglib_loop::GlibLoop, which adds them to GTK’s main loop (unix_fd_add_local,timeout_add_local). Source ids are kept in a thread-local map, so editors must be created and destroyed on the main thread. The X connection’s own fd is registered the same way. - HiDPI (
x11_scale.rs): before asking its size,Editor::opentells a view that implementsIPlugViewContentScaleSupportthe display’s scale, and the window is sized from thegetSizethat follows (in pixels at that scale, as the SDK has it on Windows and Linux). X11, XWayland included, has one scale for the whole display, read per editor from where GTK reads Marbble’s own:GDK_SCALE(a whole number), then the XSETTINGSGdk/WindowScalingFactor; with neither,Xft.dpi / 96from the X resources (or XSETTINGSXft/DPI), rounded to a quarter, for setups such as Hyprland’s or sway’s withforce_zero_scalingthat set onlyXft.dpi. It’s held to 1–4. At 1 nothing is called, so plug-ins that read the display themselves keep doing so: JUCE reads XSETTINGS (TONE3000 does; the older JUCE in CHOW Tape doesn’t), DPF readsXft.dpiandDPF_SCALE_FACTOR. A changed setting applies to editors opened after it; there’s no notification of a change to pass on. Measured on Xvfb (scripts/editor-scale-check.sh): at 2, LSP, Dragonfly, Zam, CHOW Tape and TONE3000 all take the scale and their windows double, with no plug-in scaling twice; Zam’s bitmap panels then draw at their 1x size in the top-left of the larger window, which is also what they do when they scale themselves fromXft.dpi. When a compositor scales XWayland windows itself (Hyprland’s and GNOME’s default), X clients see a scale of 1 and the compositor enlarges the editor, slightly blurred. On macOS AppKit handles it: the view reads its window’sbackingScaleFactor, and the SDK keeps the interface for platforms without one, soeditor_macos.rsnever calls it. - GDK_BACKEND and native Wayland:
main.rssetsGDK_BACKEND=x11unless the user set it, orwayland,x11whenMARBBLE_WAYLAND=1. Editors are X11 windows either way (through XWayland on a Wayland desktop). With GTK on Wayland, OpenGL editors failed to open because of libglvnd, not XWayland: GTK leaves its EGL context current on the main thread, and glvnd refusesglXMakeCurrenton a thread where another API’s context is current, raising a client-side BadAccess (GLX request). DPF/pugl editors (Zam, Dragonfly) then fail to realize their view and stay black. (Xlib’s default handler would end the process on such an error; the logging handler is installed beforeattached, so it’s logged.) LSP’s editors opened as before, and so did JUCE’s in CHOW Tape and TONE3000, which don’t link OpenGL. So when the setup hook finds aGdkWaylandDisplay, it callsset_gl_isolation(gdk::GLContext::clear_current), and every call into an editor on the main thread goes throughgl_isolated:Editor::openandclose,onSizefrom the window’s events, eachIRunLoopfd and timer callback, andSession::idle(controller idle, parameter follow, undo’sgetState). The outermost call clears GDK’s context (through GDK, whose cache of the current context would otherwise go stale) and, after the plug-in returns, releases any GLX context it left current (glXMakeCurrent(dpy, None, NULL)in each loadedlibGLX/libGL), so GTK’s nexteglMakeCurrentsucceeds. On X11 it isn’t set and none of this runs. Plug-in calls outside those (a parameter set from a command) aren’t wrapped: an editor drawing with GL right there would get a logged BadAccess and skip that frame, and a context it left current is released at the next idle, 30 ms later. GL that plug-ins run on threads of their own is unaffected, the rule being per thread. Tested on headless Weston with XWayland on an AMD GPU (Mesa radeonsi), with a GTK window holding aGtkGLAreaand a WebKit view next to the editors of LSP, Dragonfly, ZamCompX2, CHOW Tape and TONE3000: without the isolation, every DPF open failed (20 of 20); with it, all five reopened every 0.7 s for 30 s (70 reopenings) with no X errors or failed opens, the GL area read back what it drew each frame, and screenshots showed every editor and the web view drawn. The Tauri app itself hasn’t run on native Wayland in a check. Known limits there: the compositor places windows, so detached panels don’t reopen where they were; an editor is an XWayland window with no parent relation to the main window; NVIDIA’s driver is untested; and a plug-in that runs a GLib main loop of its own inside a call (a GTK dialog) lets GTK draw in the middle of it. - macOS (
editor_macos.rs): the plug-in must supportkPlatformTypeNSView. Each editor is an NSWindow (sized fromgetSizein points, resizable only ifcanResize) whose content view the plug-in attaches to. The windows float (NSFloatingWindowLevel,FullScreenAuxiliary) and hide while another app is active (hidesOnDeactivate), like Logic’s; several cascade. An NSWindowDelegate reports the close button (windowWillClose:, read bypump) and appliescheckSizeConstraintduring live resizes, thenonSize. The window is a small NSWindow subclass (MarbblePlugInWindow) whosekeyDown:/keyUp:get only the keys the plug-in’s view didn’t use; they go to the hook set withkeys::set_key_forwarder(keys.rsmaps macOS key codes to KeyboardEventkey/code). The app emits them to the main window asplugin-key, andsrc/pluginKeys.tsre-dispatches each as a synthetic KeyboardEvent, so key commands, the user’s keymap and Musical Typing all apply. Menu ⌘-shortcuts are taken by the menu first, so nothing runs twice. Linux doesn’t forward keys: the plug-in’s child window takes KeyPress itself.IPlugFrame::resizeViewresizes the content keeping the top-left corner. There is no IRunLoop: plug-ins use the main run loop.openfails off the main thread;closeandraisecalled from another thread (an async command, a controller dropped elsewhere) are queued on the main queue, andclosekeeps the plug-in’sSharedalive until the view is removed.set_hide_on_deactivate(false)is for the editor example, which macOS may never make the active app when it’s started from a terminal. - Windows:
editor_other.rsreturns an error (“not implemented on this platform yet”). Plug-ins load and process normally. crates/vst3-host/examples/editor.rsopens one editor for debugging: on Linux with a smallpoll()loop, without GTK; on macOS with an NSApplication event loop.
Marbble is released under the MIT licence.Source on GitHub.