Sometimes when you run a Racket application on a server or device with no display you get errors like
Unable to init server: Could not connect: Connection refused
Gtk initialization failed for display ":0"
The solution I have seen recommended is to use the X virtual framebuffer (XVFB):
xvfb-run racket main.rkt`
e.g. Headless with virtual display · racket/racket Wiki · GitHub
It is one of those issues that turns up on a regular basis in all the forums, so I'm wondering what the source of the issue is and what are good approaches are to make it less of an issue?
I'm also not clear on the cause? Is it one thing - or many?
PS My fear is this happens more often than reported, and people are giving up on Racket on the server because of it.
Some examples issues:
It's when a
require directly or indirectly loads
racket/gui such as
2htdp/image. IIRC Resyntax uses the indentation API from
Does requiring a library depends on racket/gui cause it to look for a display? (Or just requiring racket/gui)
to-draw in 2htdp/universe creating a display, but if I want to use the save-svg-image function to generate svg on the fly for a web app?
racket/gui and therefore any library transitively dependent on
gui-lib makes it look for X.
For code analysis use cases like Resyntax and the langserver, it would seem there are 2 major paths to removing the screen requirement (to avoid the need for
xvfb which may not always work, as well as for overall efficiency):
- The core code analysis logic used by these tools could be extracted out of DrRacket and rewritten to process data only without needing GUI widgets
- Headless widget support could be added to
racket/gui, which should allow anything that depends on it to magically support a headless mode
I haven't investigated either path too deeply yet, but my current impression is that DrRacket's logic is quite entangled with its GUI widgets, so I would think path 2 above (headless widgets) may be the better one to go with. It also has the nice benefit of adding headless abilities to a wider array of tools.
Firefox added their own headless widgets a few years back for automation needs, perhaps this is could be useful when adding headless support to
I believe that option 1 is much more plausible. In particular, the
drracket-tool-text-lib package exists precisely to support this use case, and it's what
racket-mode uses. There are probably other parts that could be extracted to this package, but that would be much easier than developing a headless mode.
I've definitely spent time extracting various aspects of the non-GUI parts of DrRacket's IDE support into libraries and packages that don't depend on more than they need. Happy to think about what others might find useful in doing more of it.
For resyntax, for example, the gui library is used for indentation, but the framework is actually deferring to the language itself for indentation. I think that there are now a few different implementations of something that computes indentation for strings in the emacs mode, the repl, and DrRacket, although there might not be something as easy to use as the
Ah right, I forgot about this existing work. Indeed, it seems like @robby and @greghendershott have made some good progress extracting DrRacket logic away from the GUI dependency where possible.
Perhaps the best path then is for downstream projects like langserver and Resyntax to figure out what else they might need from this GUI-less DrRacket library (if anything) and then switch to it.
I think both resyntax and the langserver want to use basically this code: https://github.com/greghendershott/racket-mode/blob/hash-lang/racket/hash-lang.rkt (which could maybe live in drracket-tool-text-lib depending on what Greg and Robby think).
like-text% class from expeditor here: https://github.com/racket/expeditor/blob/master/expeditor-lib/private/object.rkt is also similar, and perhaps all of these could be consolidated.
Also, the core of the Racket indenter actually lives in
syntax-color-lib, which doesn’t instantiate the GUI.
I think it would be great if DrRacket started depending on other libraries to be smart that can be reused in other contexts!
re @samth's comments about this code of @greghendershott 's, I tried to build a reusable abstraction in something called an "irl" here but it probably still needs more work and also probably needs to be moved into a pkg that doesn't depend on the GUI. But the idea of that code is to be a centralized place to get information from a
#lang that can affect the way the IDE works. It is what's behind DrRacket's "Reload #lang extensions" menu item and the error messages that show up along the bottom when you're developing your languages's IDE support (not the errors that you get when your program is buggy -- the errors that you get when, e.g., your syntax colorer raises an exception).
Sharing is good. A few caveats or nuances:
What I have in
hash-lang.rkt feels more complicated than I'd like because it's trying to do a few things at once.
In addition to the obvious work you'd expect:
There's a "distributed computing" aspect (with the emacs process talking to the back end racket process running this code, using a thread for each command), with a notion of generations and progress.
There's a "minimal update after changes" aspect (fast enough to be called potentially for every key a user presses, because although coloring can be somewhat lazy/async, indent while typing is blocking).
It's a bit of a hairball. It's probably too bespoke. Things like resyntax probably don't need all that. Things like langserver might need something close to, but not exactly that.
I know how to refactor code... but one step at a time. When I do finally merge the
racket-hash-lang-branch (hopefully any day now) -- and if real-world usage suggests it's no more than 90% a steaming pile -- then I would feel better about looping back for greater sharing.
In other words, as soon as I get the bear dancing reliably at the neighborhood bar, I'd be open to improving its style and taking it out on tour.
I'd have said:
I know how to refactor code... one step at a time.
I don't know any other way that works.