Open chriskmanx opened 5 months ago
Below is a basic program to illustrate the problem:
using Gtk4 win = GtkWindow("Basic Printer Operation", 400, 200) b = GtkButton("Click Me") push!(win,b) function on_begin_print(o,c) # operation, context println("begin print called ") # Use print compositor to set number of pages... end function on_draw_page(o,c,n,d) # operation, context, pagenum,data println("on draw page ") # Use compositor to draw page n using operation o and context c end function on_button_clicked(w) printer_operation = Gtk4.G_.PrintOperation_new() Gtk4.G_.set_allow_async(printer_operation,true) # both async and sync exhibit the error. signal_connect(on_draw_page,printer_operation,"draw-page") signal_connect(on_begin_print,printer_operation,"begin-print") Gtk4.G_.run(printer_operation,Gtk4.PrintOperationAction_PRINT_DIALOG,win) end signal_connect(on_button_clicked, b, "clicked") show(win)
This yields an empty Print dialog. The General Tab is empty. No Printer. No Print to File option.
On the same system, below is a basic C++ program which works.
#include#include static void begin_print (GtkPrintOperation *operation, GtkPrintContext *context) { // g_assert (editor->buffer); // /* Create a print compositor from the buffer */ // editor->print_compositor = gtk_source_print_compositor_new (editor->buffer); // /* Set some formatting options for pages */ // gtk_source_print_compositor_set_print_header (editor->print_compositor, TRUE); // gtk_source_print_compositor_set_print_footer (editor->print_compositor, TRUE); // gtk_source_print_compositor_set_header_format (editor->print_compositor, TRUE, NULL, "%N/%Q", NULL); // gtk_source_print_compositor_set_footer_format (editor->print_compositor, TRUE, NULL, PACKAGE_URL, NULL); // gtk_source_print_compositor_set_left_margin (editor->print_compositor, 15.0, GTK_UNIT_MM); // gtk_source_print_compositor_set_right_margin (editor->print_compositor, 15.0, GTK_UNIT_MM); // gtk_source_print_compositor_set_bottom_margin (editor->print_compositor, 15.0, GTK_UNIT_MM); // /* Pagination */ // while (!gtk_source_print_compositor_paginate (editor->print_compositor, context)); // gtk_print_operation_set_n_pages (operation, gtk_source_print_compositor_get_n_pages (editor->print_compositor)); } static void draw_page (GtkPrintOperation *operation, GtkPrintContext *context, gint page_nr) { // gtk_source_print_compositor_draw_page (editor->print_compositor, context, page_nr); } static void activate(GtkApplication *app, gpointer user_data) { GtkWidget *window; GtkPrintOperationResult res = GTK_PRINT_OPERATION_RESULT_ERROR; GError *error = NULL; window = gtk_application_window_new(app); gtk_window_set_title(GTK_WINDOW(window), "GTK Window : Hello World!"); gtk_window_set_default_size(GTK_WINDOW(window), 200, 200); gtk_widget_show(window); GtkPrintOperation *print_operation; print_operation = gtk_print_operation_new (); g_signal_connect (print_operation, "draw_page", G_CALLBACK (draw_page), NULL); g_signal_connect (print_operation, "begin_print", G_CALLBACK (begin_print), NULL); res = gtk_print_operation_run(print_operation, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, GTK_WINDOW(window), &error); } int main(int argc, char **argv) { GtkApplication *app; int status; app = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE); g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); status = g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); return status; }
Running this with
g++ gtkcpp.cpp -o main `pkg-config --cflags --libs gtk4` && ./main
and linking to libgtk4 from Homebrew works and results in a functional Print dialog.
Julia non working Print Dialog:
C++ Working Dialog
Thanks for posting the example code -- I had not tried the print dialog before. It also doesn't work on Linux. This is probably an issue with the JLL, but maybe there's something we can do here...
Thank you @jwahlstrand. I tried reaching out on Linked-In as well. Anything I can try, please let me know. And yes, I agree it's probably in the JLL. Would it help to post what dynamic libraries are are pulled in by both binaries? I can do a dump in Activity Monitor to show the DDLs/SOs/DYLIBs. Presumably they will be different in that the Julia version will pull in from the artifacts directories.
I am happy to work with you on putting together a sample app for printing. I'm writing an editor using gtk source view, so that is what I'm looking to print...
The print backend modules for GTK get installed in "libdir/gtk-4.0/4.0.0/printbackends". On Fedora, I see 2 backends installed in /usr/lib64/gtk-4.0/4.0.0/printbackends: one for "print to file" and one for CUPS. On Linux the GTK4_jll artifact directory contains just the one for "print to file".
As you also saw, "print to file" isn't offered as an option in the print dialog on Linux. So I think the first issue is, how can we make that appear?
Maybe one of the environmental variables here can point GTK to that directory? https://docs.gtk.org/gtk4/running.html
I have not had any success with that yet. It works! I will submit a PR.
As far as CUPS (and whatever it is on OSX, also CUPS?) is concerned, it looks like it isn't in Yggdrasil, which is a prerequisite for enabling it in GTK4_jll. There was an attempt fairly recently, but it stalled due to an issue building one of its dependencies: https://github.com/JuliaPackaging/Yggdrasil/pull/5004
I am happy to work with you on putting together a sample app for printing. I'm writing an editor using gtk source view, so that is what I'm looking to print...
I recently started trying to use GtkTextView
and realized that it was left in a half-broken state. I have been working on a breaking PR to fix it that I will submit very soon, and will then update GtkSourceView.jl once it's merged.
Yes so I started putting together my own CCALL wrappers to steer around that perhaps. I'm using GtkSourceWidget.jl, but in order to get that to fly - in particular your type constraints, I had to wrap a few functions.
# This is the original undo(instance::GtkTextBuffer...) function from gtk4_methods with the GtkTextBuffer type constraint removed in the instance paramater function undo_gtksourcebuffer(instance) ret = ccall(("gtk_text_buffer_undo", Gtk4.libgtk4), Nothing, (Ptr{Gtk4.GObject},), instance) nothing end # This is the original delete(instance::GtkTextBuffer...) function from gtk4_methods with the GtkTextBuffer type constraint removed in the instance paramater function delete_gtksourcebuffer(instance, _start::Union{GtkTextIter, Ref{_GtkTextIter}}, _end::Union{GtkTextIter, Ref{_GtkTextIter}}) @async ret = ccall(("gtk_text_buffer_delete", Gtk4.libgtk4), Nothing, (Ptr{Gtk4.GObject}, Ptr{_GtkTextIter}, Ptr{_GtkTextIter}), instance, _start, _end) nothing end # This is the original run(GtkPrintOperation...) function from gtk4_methods with the GtkWindow type constraint removed in the GtkWindow parameter function run_gtk_print_operation(instance::GtkPrintOperation, _action, _parent) @async begin _parent_maybe = Gtk4.nothing_to_null(_parent) err = Gtk4.err_buf() ret = ccall(("gtk_print_operation_run", Gtk4.libgtk4), UInt32, (Ptr{Gtk4.GObject}, UInt32, Ptr{Gtk4.GObject}, Ptr{Ptr{Gtk4.GError}}), instance, _action, _parent_maybe, err) Gtk4.check_err(err) ret2 = PrintOperationResult(ret) ret2 end end
I don't know if the above helps, but with these few, I managed to the the GtkSourceView to work, albeit only by pulling it into my project directly - which is clearly a hack.
It also meant bypassing quite a bit of the GtkSourceView functionality and reaching through to the underlying text buffers directly. If you have something I could test, I'd be happy to be the Guinea Pig.
I think the reason the type removals on the CCALLs was needed was because of namespace crossovers. Otherwise the type hierarchy should have sorted out the subtypes as valid ISA's. Just a guess, but I could not get things to work without "Python'izing" the code which as a traditional C++ programmer really galls me :)
The fix of GtkTextView is in #62 . The issue I was talking about is having to pass in _GtkTextIter
to a lot of methods..
What error are you seeing with Gtk4's undo
and delete
and GtkSourceBuffer
? I think that should work. And the parent
argument for the other method really should be a GtkWindow
or nothing
, so it makes no sense to me why removing the type helps. Is there a name collision involving Maybe
?
It is very possible that we will want to make a lot of changes to GtkSourceWidget. It is brand new and hasn't been tested much at all -- you are definitely the guinea pig. But I have been happy with how painlessly the GI-generated methods work in most cases and I expected GtkSourceWidget to work without too many ccall's.
No the undo wasn't working. It wasn't error'ing but it did nothing. Here is how I ended up getting it to work on the GtkSourceView
# This is the original undo(instance::GtkTextBuffer...) function from gtk4_methods with the GtkTextBuffer type constraint removed in the instance paramater function undo_gtksourcebuffer(instance) ret = ccall(("gtk_text_buffer_undo", Gtk4.libgtk4), Nothing, (Ptr{Gtk4.GObject},), instance) nothing end function on_edit_undo(b) @async begin println("print undo ") undo_gtksourcebuffer(input_textbuffer) end end signal_connect(on_edit_undo,edit_undo_button,"clicked")
input_textbuffer is instantiated like so
input_textbuffer = GtkSourceWidget.GtkSourceBuffer(language)
Delete wasn't my major trouble. edit-cut was.
I only managed to puzzle that together from the copy handler, then doing the delete via the iterators...
function on_input_cut_clipboard(b) println("cut button ") selbounds = Gtk4.G_.get_selection_bounds(analyticumtechnica.input_textbuffer) iter_start = Ref{Gtk4._GtkTextIter}(selbounds[2]) iter_end = Ref{Gtk4._GtkTextIter}(selbounds[3]) if iter_end > iter_start on_input_copy_clipboard(b) delete_gtksourcebuffer(analyticumtechnica.input_textbuffer,iter_start,iter_end) end end signal_connect(on_input_cut_clipboard,edit_cut_button,"clicked")
OK, it sounds like text editing is just broken. Once #62 is done and merged I will look at GtkSourceWidget again and try to produce a working example.
Regarding your original issue, "print to file" is fixable (#61 works on Linux and I'll check what it does to the other platforms today before merging it), but real printing will require a lot of effort and I will not personally have time for it in the near future.
I have a quick question there... looking at your code: why is this eval needed?
eval(include("gen/gdk4_methods"))
Now, I'm a Lisp programmer so the different evaluation times make sense to me, but I thought an include would effect this already. Seems to get compiled anyway. I guess what I'm saying is I never put the eval in front of my includes...
Also, when doing a "using Gtk4," that does not seem to pull in Gtk4_jll automatically. How is that? What confuses me further is that while src/Gtk4.jl performs a "using GTK4_jll," when I try that in my project from the REPL, it wants to install GTK4_jll.
julia> using Gtk4 julia> using GTK4_jll │ Package GTK4_jll not found, but a package named GTK4_jll is available from a registry. │ Install package? │ pkg> add GTK4_jll └ (y/n/o) [y]:
How will this have ever worked if GTK4_jll is needed by the Gtk4 module, but it isn't installed?
Thank you for your updates. As for printing not working without major re-work, might it be better then to call out to C++ land via jluna? Can the signal handlers cross from C++ to Julia? I'm struggling with the notion of A) Making the application skeleton C++ and the data science bits Julia via jluna or B) Making the application Julia only. When I found the Gtk4 wrappers I was inclined to go with B so I'm scoping out whether all the peripherals will work. I don't mind resorting to C++ for an isolated area - like printing.
I can confirm that this code will make print-to-file available.
using GTK4_jll ENV["GTK_PATH"] = joinpath(dirname(GTK4_jll.libgtk4_path::String),"gtk-4.0") using Gtk4 win = GtkWindow("Basic Printer Operation", 400, 200) b = GtkButton("Click Me") push!(win,b) function on_begin_print(o,c) # operation, context println("begin print called ") # Use print compositor to set number of pages... end function on_draw_page(o,c,n,d) # operation, context, pagenum,data println("on draw page ") # Use compositor to draw page n using operation o and context c end function on_button_clicked(w) printer_operation = Gtk4.G_.PrintOperation_new() Gtk4.G_.set_allow_async(printer_operation,true) # both async and sync exhibit the error. signal_connect(on_draw_page,printer_operation,"draw-page") signal_connect(on_begin_print,printer_operation,"begin-print") Gtk4.G_.run(printer_operation,Gtk4.PrintOperationAction_PRINT_DIALOG,win) end signal_connect(on_button_clicked, b, "clicked") show(win)
This is the original example with the following prepended:
using GTK4_jll ENV["GTK_PATH"] = joinpath(dirname(GTK4_jll.libgtk4_path::String),"gtk-4.0")
@jwahlstrand What is the complication with making regular printers available? I presume this leaves the Julia space in that both C++ and Julia simply call the C code, no? There should not be any additional development, yes?
I am noticing one difference between the gtk3 artifacts ( which by the way gedit on GTK3 installed via Homebrew works fine ) and the gtk4 artifacts:
./e6ff6a9e66b9724d435a000cd359cdef38d3876b/lib/gtk-4.0/4.0.0/printbackends ./e6ff6a9e66b9724d435a000cd359cdef38d3876b/lib/gtk-4.0/4.0.0/printbackends/libprintbackend-file.so ./5c891c10775421b72c44801565308c0140560ec0/lib/gtk-3.0/3.0.0/printbackends ./5c891c10775421b72c44801565308c0140560ec0/lib/gtk-3.0/3.0.0/printbackends/libprintbackend-file.dylib ./5c891c10775421b72c44801565308c0140560ec0/lib/gtk-3.0/3.0.0/printbackends/libprintbackend-lpr.dylib
Gtk4 features an .so file which is ordinarily Linux format. Importantly, file printing is the only compiled backend.
If we compare that to what Homebrew has installed and which is referenced by my C++ app:
./Cellar/gtk4/4.12.5/lib/gtk-4.0/4.0.0/printbackends/libprintbackend-file.so ./Cellar/gtk4/4.12.5/lib/gtk-4.0/4.0.0/printbackends/giomodule.cache ./Cellar/gtk4/4.12.5/lib/gtk-4.0/4.0.0/printbackends/libprintbackend-cups.so
That gives us the libprintbackend-cups.so file. Of course merely copying the Homebrew compiled .so over to the Julia artifact folder causes all manner of symbol conflicts...
objc[7999]: Class GNotificationCenterDelegate is implemented in both /Users/chris/.julia_x86_64/artifacts/65a74410c75b9704dc242d01ae2839af1e84c7ff/lib/libgio-2.0.0.dylib (0x157195e00) and /usr/local/Cellar/glib/2.78.4/lib/libgio-2.0.0.dylib (0x15e1436b0). One of the two will be used. Which one is undefined. objc[7999]: Class ResultReceiver is implemented in both /Users/chris/.julia_x86_64/artifacts/e6ff6a9e66b9724d435a000cd359cdef38d3876b/lib/libgtk-4.1.dylib (0x15c946c28) and /usr/local/Cellar/gtk4/4.12.5/lib/libgtk-4.1.dylib (0x1611e22b0). One of the two will be used. Which one is undefined. objc[7999]: Class GtkApplicationQuartzDelegate is implemented in both /Users/chris/.julia_x86_64/artifacts/e6ff6a9e66b9724d435a000cd359cdef38d3876b/lib/libgtk-4.1.dylib (0x15c946c50) and /usr/local/Cellar/gtk4/4.12.5/lib/libgtk-4.1.dylib (0x1611e22d8). One of the two will be used. Which one is undefined. objc[7999]: Class GNSMenuItem is implemented in both /Users/chris/.julia_x86_64/artifacts/e6ff6a9e66b9724d435a000cd359cdef38d3876b/lib/libgtk-4.1.dylib (0x15c946ca0) and /usr/local/Cellar/gtk4/4.12.5/lib/libgtk-4.1.dylib (0x1611e2328). One of the two will be used. Which one is undefined.
I have a quick question there... looking at your code: why is this eval needed?
eval(include("gen/gdk4_methods"))
Now, I'm a Lisp programmer so the different evaluation times make sense to me, but I thought an include would effect this already. Seems to get compiled anyway.
What's in the file is a bunch of Julia expressions and that is a way to have Julia evaluate them. I think I copied that pattern from Gtk.jl but maybe it's from somewhere else. I think eval
is necessary in this case because the file contains quote
. I'm sure it's possible to strip those out but I just went with eval
. I don't think it makes much difference.
Also, when doing a "using Gtk4," that does not seem to pull in Gtk4_jll automatically. How is that? What confuses me further is that while src/Gtk4.jl performs a "using GTK4_jll," when I try that in my project from the REPL, it wants to install GTK4_jll.
julia> using Gtk4
julia> using GTK4_jll │ Package GTK4_jll not found, but a package named GTK4_jll is available from a registry. │ Install package? │ pkg> add GTK4_jll └ (y/n/o) [y]:
How will this have ever worked if GTK4_jll is needed by the Gtk4 module, but it isn't installed?
The Gtk4.jl package has GTK4_jll installed in its environment but your global environment (which the REPL uses by default) doesn't unless you add it:
@jwahlstrand Ok that makes sense. There is quoted content. Eval forces the evaluation as if at runtime. It's Lisp still :) What did you think of the missing cups library in the Julia artifacts?
As for the REPL - I invoke it with --project in the project folder so I presume that will pull the correct artifacts.
So I repointed
ENV["GTK_PATH"] = "/usr/local/Cellar/gtk4/4.12.5/lib/gtk-4.0"
to the Homebrew installation. Of course that will wreck havoc with GTK4_jll. But it does confirm that it will try at least to do CUPS printing...
(process:8800): GLib-GObject-CRITICAL **: 23:25:53.349: g_type_module_use: assertion 'G_IS_TYPE_MODULE (module)' failed (process:8800): GLib-GObject-CRITICAL **: 23:25:53.349: plugin pointer (0x6000013dea60) for type 'GtkPrintBackendCups' is invalid (process:8800): GLib-GObject-CRITICAL **: 23:25:53.349: plugin pointer (0x6000013dea60) for type 'GtkPrinterCups' is invalid
Which is not surprising because we get a raft of errors from our repointing:
objc[8800]: Class GNotificationCenterDelegate is implemented in both /Users/chris/.julia_x86_64/artifacts/65a74410c75b9704dc242d01ae2839af1e84c7ff/lib/libgio-2.0.0.dylib (0x156d96e00) and /usr/local/Cellar/glib/2.78.4/lib/libgio-2.0.0.dylib (0x1698886b0). One of the two will be used. Which one is undefined. objc[8800]: Class ResultReceiver is implemented in both /Users/chris/.julia_x86_64/artifacts/e6ff6a9e66b9724d435a000cd359cdef38d3876b/lib/libgtk-4.1.dylib (0x15c547c28) and /usr/local/Cellar/gtk4/4.12.5/lib/libgtk-4.1.dylib (0x168db82b0). One of the two will be used. Which one is undefined. objc[8800]: Class GtkApplicationQuartzDelegate is implemented in both /Users/chris/.julia_x86_64/artifacts/e6ff6a9e66b9724d435a000cd359cdef38d3876b/lib/libgtk-4.1.dylib (0x15c547c50) and /usr/local/Cellar/gtk4/4.12.5/lib/libgtk-4.1.dylib (0x168db82d8). One of the two will be used. Which one is undefined. objc[8800]: Class GNSMenuItem is implemented in both /Users/chris/.julia_x86_64/artifacts/e6ff6a9e66b9724d435a000cd359cdef38d3876b/lib/libgtk-4.1.dylib (0x15c547ca0) and /usr/local/Cellar/gtk4/4.12.5/lib/libgtk-4.1.dylib (0x168db8328). One of the two will be used. Which one is undefined.
So how would we get Gtk4.jl to generate the missing cups library in addition to the file printing library?
Thank you for your updates. As for printing not working without major re-work, might it be better then to call out to C++ land via jluna? Can the signal handlers cross from C++ to Julia? I'm struggling with the notion of A) Making the application skeleton C++ and the data science bits Julia via jluna or B) Making the application Julia only. When I found the Gtk4 wrappers I was inclined to go with B so I'm scoping out whether all the peripherals will work. I don't mind resorting to C++ for an isolated area - like printing.
If you haven't already, you should definitely look at Mousetrap. I don't think it includes a text widget currently (definitely not GtkSourceView), but it does combine Julia, C++, and GTK4. You could take a look at how it does signal handlers.
I almost went with Mousetrap. Aware of it. I explicitly need Makie support though and they warned it was experimental and not to use it for anything near "production." I also need GtkSourceView for syntax support. Plotting and syntax editing and the data science that Julia is good at - that's what I'm trying to put into one pot without resorting to do the boilerplate in C++.
@jwahlstrand What is the complication with making regular printers available? I presume this leaves the Julia space in that both C++ and Julia simply call the C code, no? There should not be any additional development, yes?
Here's the issue: like most Julia packages, Gtk4.jl uses C libraries built by BinaryBuilder and packaged in Yggdrasil. The GTK4_jll package does not include the modules for real printing. Maybe under some conditions you could point GTK4 to modules that are installed on your system through Homebrew or a Linux distribution -- ahh, I see you tried that. The right (or at least, conventional) way to make this work for all users of this package is to fix the issue in Yggdrasil by adding the dependencies needed for printing and then modify the GTK4 build script. In one of my comments above I pointed at a recent attempt to add CUPS to Yggdrasil, which ran into problems.
Printing is not a priority for me personally, so since I foresee this is going to involve a ton of frustrating effort, I don't plan to work on this in the near future. Sorry! You are welcome to try.
As for the REPL - I invoke it with --project in the project folder so I presume that will pull the correct artifacts.
OK, I guess the issue is your project didn't include GTK4_jll. Gtk4.jl and GTK4_jll are separate packages, and pulling in Gtk4 does not pull in GTK4_jll automatically.
I explicitly need Makie support though and they warned it was experimental and not to use it for anything near "production."
Just to give my own warning: this package is improving with time (I hope) but it also may never be suitable for "production" depending on what that means. I'm doing this as a hobby -- it's not part of my day job and I'm not a professional software engineer. If it was part of my job the package would be licensed differently and I would spend more time on it. There have been a few other contributors, but the bus factor is effectively 1 right now. And I expect my next six months to be much busier than usual.
Gtk4Makie works pretty well, but it's disappointingly easy to produce segfaults (see https://github.com/JuliaGtk/Gtk4Makie.jl/issues/14), and if I walked away it would eventually break as Makie evolves.
"print to file" should work on master now. I'll leave this issue open until real printers are enabled on Linux and OSX.
I was shocked to find that on Windows it seems to work fine without doing anything!
Re "it works on Windows." Might that be due to a dependency on file formats? Like dll, vs .so vs dylib? I know on OSX you can call it a ".so" and as long as the internal format is right it "ought" to just work, but I did note the incongruity. Of course outright missingness is another issue, like the file isn't built ( the CUPS printing DLL ).
Thanks for your diligence in sorting this out.
I can confirm that printing to file now works on OSX
Re "it works on Windows." Might that be due to a dependency on file formats? Like dll, vs .so vs dylib? I know on OSX you can call it a ".so" and as long as the internal format is right it "ought" to just work, but I did note the incongruity. Of course outright missingness is another issue, like the file isn't built ( the CUPS printing DLL ).
There are no modules present in the GTK4_jll artifact on Windows, it must be built into the library.
But the library modules aren't in the GTK4_jll artifacts. They're in the Gtk4 artifacts. That's the confusion part. As in : ./e6ff6a9e66b9724d435a000cd359cdef38d3876b/lib/gtk-4.0/4.0.0/printbackends/libprintbackend-file.so So it's the artifact produced by precompiling the Gtk4.jl module that yields this, right?
I say, as a former C++ programmer, I am totally confused what gets generated how under Julia :) AOT compile gives us artifacts which are beautifully managed with UUIDs which in turn allows parallel instances of modules --- unlike the mess under Python which mandates throwing entire operating system instances around ( aka Docker ). But we also have modules postfixed with _jll which are binary only distributions from BinaryBuilder, i.e. non Julia sourced code. How can one see what the source input to the _jll was? That would be one way to resolve the symbol clashes with Homebrew - building both off the same source and compiler. Then I should be able to drop the libraries into place.
But the library modules aren't in the GTK4_jll artifacts. They're in the Gtk4 artifacts. That's the confusion part. As in : ./e6ff6a9e66b9724d435a000cd359cdef38d3876b/lib/gtk-4.0/4.0.0/printbackends/libprintbackend-file.so So it's the artifact produced by precompiling the Gtk4.jl module that yields this, right?
No, everything in that directory is produced by BinaryBuilder. Julia precompiles its own code; it doesn't touch C code from other libraries.
How can one see what the source input to the _jll was?
Look in this script to see how the JLL was built: https://github.com/JuliaPackaging/Yggdrasil/blob/master/G/GTK4/build_tarballs.jl
And the BinaryBuilder documentation is here: https://docs.binarybuilder.org/stable/
gtk_print_operation_run with PrintOperationAction_PRINT_DIALOG results in print dialog without any printers and without Print to File option. Corresponding event handlers for on_begin_print and on_draw_page are never invoked. The system is OSX Montery. Julia is 1.9.3. I have verified that corresponding operations in the Gtk3 space and the Gtk4 space from C++ behave as expected on the same system. As the API parameters are the same across both the C++ code and the ccall in Julia I am supposing there may be an issue in the libgtk4 build of the Gtk4 Julia package.