VteTerminal in Widget Template

Hi! o/ First-time poster, long-time user, slowly ramping up to making things in GTK4.

I’m working on a very simple GTK4 terminal emulator in C. At the moment I’ve literally just taken the standard GNOME Builder C app template and replaced the content label with a VteTerminal. I don’t currently have any custom needs for the terminal widget itself, so I’ve experimented with binding directly to VteTerminal in an app window template.

I initially tried to work out the right library Id for VTE to add to the <requires> elements. Based on pkgconfig and gir values, I’ve tried several casing permutations of vte-2.91-gtk4, vte-3.91, vte, and setting the version string with either the suffix values above or the VTE build tag (e.g., 0.76.3).

Running Builder, using any of the above values, and inserting a VteTerminal directly into the app window template, I get a content-less window and the following error in the Builder terminal:

(thinko:2): Gtk-CRITICAL **: 21:59:41.976: Error building template class 'ThinkoWindow' for an instance of type 'ThinkoWindow': .:0:0 Invalid object type 'VteTerminal'

Fair enough. So it seems that VteTerminal can’t be bound-to directly in a widget template like this.

If I subclass a new type from VteTerminal with no custom properties or methods, as long as the new type is compiled in (i.e., not excluded from source files in the Meson config), I’m able to render an app window with a terminal widget, and the error message disappears. I don’t have to reference Vte at all in the terminal widget’s own template, I don’t have to reference the terminal widget header in the app window, and I can even still bind directly to VteTerminal, not ThinkoTerminal, in both the code and the window template. (Unsurprisingly, I do subsequently have to reference vte.h in the window source for the build to succeed.)

If this were a serious project, in all likelihood I would subclass VteTerminal just for the sake of future extensibility, but all of the above has got me curious as to the disconnect I’m seeing. Basically, my questions are

  • What currently prevents VteTerminal from being directly accessible to widget templates?
  • Is there an expectation that this will be enabled in the future?
    *Worth noting, this was available in GTK+3 (cf. Example of a Gtk application using a VTE terminal widget written in Python 3 · GitHub)
  • Where should I start if I specifically want to understand the mechanism that enables the template to bind to ThinkoTerminal without having to tell ThinkoWindow (the app window) about it? I would expect to have reference the specific type explicitly in ThinkoWindow for the template binding to succeed
    • Might be a simple case of me missing something very simple - I only play a C developer on TV - definitely make me RTFM, just need to know what’s the right FM is in this case :smiley:

Again, mainly asking as a matter of curiosity, but any and all knowledge is valuable, and hence any and all insight is appreciated!

Somehow I glossed over the fact that composite widget templates reference child widgets as nested objects, not as nested templates, which I take to indicate that part of the crux here is that VteTerminal doesn’t have a defined template. Hence the need for subclassing.

Reading through the GTK code, I see intialization from templates is defined in the GtkWidget itself, but I suspect people here can offer insight faster than I can connect all the dots bouncing between the gtk and vte codebases. If anything, I’d expect this to suggest that VteTerminal should be able to be referenced directly in a widget template.

I suspect this is going to be one of those cases where the code is the only documentation, which is fine, but again, still open to links if there are docs at hand that I’m not seein’.

Hi,

I don’t think the <requires> tag is necessary.
I suspect your executable doesn’t link with the vte library… Have you added the vte dependency to your meson.build?

@gwillems hi, thanks for taking a look at this! Yep, just based on the trial and error I’ve gone through with this exercise, you appear to be right about the <requires> tag. That’s not what I would expect, but tbh that’s part of why I’m here. I expected it to be necessary since VTE is now a separate library altogether.

Then again, looks like ptyxis and Console both take a similar approach - templating and passing around a subclass of VteTerminal with no reference to vte in the template requirements - so it seems there’s common knowledge here that I don’t have. :person_shrugging:

I’m linking to vte-2.91-gtk4 in my Meson deps, and in fact/predictably, the build breaks without it linked, since either the VteTerminal subclassed widget or the app window itself references vte.h depending on whether I include the former in the build. Dependencies in src/meson.build (again, using the usual Builder Meson setup/subdirs and all):

thinko_deps = [
  dependency('gtk4'),
  dependency('libadwaita-1', version: '>= 1.4'),
  dependency('vte-2.91-gtk4', version: '>= 0.76.2'),
]

Removing the third line emits this compiler error (leading garbage stripped out for readability):

fatal error: vte/vte.h: No such file or directory
   21 | #include <vte/vte.h>
      |          ^~~~~~~~~~~

Again, not urgent as the “workaround” in my case is actually going to be better practice for most scenarios anyway - just trying to get insight into my unknown unknowns since the behavior of this specific binding defies understanding (well, mine, anyway). :slight_smile:

Inside the instance initialisation function of your templated class, before calling gtk_widget_init_template(), try calling:

g_type_ensure (VTE_TYPE_TERMINAL);

Ah, basically just need to hint GTK imperatively rather than with a lib reference in the template in this case. Neat!

Thanks for calling this out @ebassi! I haven’t gone deep into GLib itself, so I hadn’t yet encountered g_type_ensure - this is the nudge in the right direction I needed! :smiley:

The type system works at run time, and the UI definition files need to have access to the types in order to parse the XML and instantiate the objects. This means that all the types referenced must be registered prior to the parsing (or you need to write some additional bit of XML to tell GTK the name of the type function, which is not something that people usually do).

The type system works at run time, and the UI definition files need to have access to the types in order to parse the XML and instantiate the objects.

Yep, this much I’ve read into (pretty much had to to get my head around GObject’s type registration boilerplate). Coming primarily from a .NET background where its native flavor of IoC is the new hotness no matter the $currentYear, I could at least make the analogy to understand the why here.

or you need to write some additional bit of XML to tell GTK the name of the type function

This looks more like the kind of solution I was seeking/hoping for. That said, if I remove the source for the subclassed terminal widget and reference vte.h directly in my app window code, removing the call to g_type_ensure in the init function and adding type-func="vte_terminal_get_type" to the template produces a regression with no terminal loaded and the following runtime message:

(thinko:2): Gtk-CRITICAL **: 07:17:54.781: Error building template class 'ThinkoWindow' for an instance of type 'ThinkoWindow': .:0:0 Invalid type function 'vte_terminal_get_type'

The accepted answer at gtk3 - Custom Gtk Widget with template UI - Stack Overflow seems to hint at why that may be the case for a small application where templates are called before types are instantiated:

the compiler probably isn't going to export the function in the dynamic symbol table, and so GtkBuilder won't be able to find it. 

Whether alanwj’s correct here is out of my depth, but if they are, I can see why this won’t work. (I also note that you commented on this answer and didn’t call out anything as sus. :slight_smile:)

Is this all (|still) accurate from your point of view? I.e., do I likely have to call g_type_ensure in the scenario where VteTerminal (or any custom widget) is instantiated for the first time, from a template?