GlkOte protocol for measuring styles

@dfabulich has submitted pull requests to AsyncGlk and RemGlk-rs to support glk_style_measure and glk_style_distinguish. Previously these haven’t been supported in Parchment because the GlkOte protocol makes it tricky to obtain the current styles. While most of the time the RemGlk library is on the same computer, it doesn’t have to be, and I didn’t want to add something that would mysteriously stop working if someone tried setting up a remote client. I also didn’t want to implement it as an RPC because the performance would be pretty bad.

Dan has proposed that the client send styles at startup, as well as in arrange events. This makes a lot of sense! The approach is solid, but I’d like to get the community’s input on the specific protocol before we implement it.

/** Runner theme for glk_style_measure */
export interface Theme {
    stylehints_enabled: boolean,
    buffer: StyleEntry[],
    grid: StyleEntry[],
}

/** Effective theme values for one Glk style */
export interface StyleEntry {
    fg: number,
    bg: number,
    weight: -1 | 0 | 1,
    oblique: 0 | 1,
    /** Glk sense: 1 = proportional, 0 = fixed-width */
    proportional: 0 | 1,
    reverse: 0 | 1,
}

So starting with the StyleEntry interface, we’re sending the basic font details of each style. They’re all numbers, which makes it simple, but it is a little less human friendly than the GlkOte protocol usually is. In particular colours are sent as numbers rather than as a CSS colour in text. Anyone have a problem with that?

I think we could measure the font size too. As the client is responsible for turning a Glk font hint into its actual font size, it should be able to convert back again.

Next is the Theme interface. I think the stylehints_enabled flag would be better as a support capability instead. Then it just has the all the styles for buffer and grid windows in an array.

I think it would be better to make them maps, so that any styles which are identical to the base (normal) style can be skipped. The keys could be style names, or just their numbers. Numbers are simpler and more condensed, but the GlkOte protocol uses style names elsewhere, so I’m not sure.

Likewise I think the individual properties within a StyleEntry should also be optional. Non base styles which only change one thing could just send that thing. But if a client didn’t support, for example, colours, then it could just leave that out entirely too.


This is not so much about the protocol but the implementation, but Dan’s RemGlk-rs PR currently implements a default theme if no specific details are sent. I don’t like this, I think glk_style_measure should just continue to return 0 if it can’t actually measure a style, like it does now. Fake information is worse than no information.

Parchment now supports my additional styles Glk extension. They will not be sent in the protocol, and will not be measurable.

2 Likes

I don’t know if I’ve ever seen those functions actually used, but supporting them can only be a good thing. I added similar capabilities to Dialog because there was a clear use case for them.

There are a number of interpreters, such as Level9 and Magnetic, which use glk_style_measure to match the background colour of the graphics window to the buffer window background, by measuring the background colour of the Normal style.

1 Like

Yep, that’s what implementing this extension will make possible in RemGlk-rs interpreters. TADS does something similar for it’s “transparent” style.

Sure!

Perhaps we should also have a “stylehints” support capability, but I think it’s a separate question as to whether the user has set a preference to override the author’s styles. The user might toggle that preference at runtime, e.g. with a Settings menu or something.

(If a user sets a preference like “Override colors and styles”, I think it would apply equally to stylehints, garglk_set_zcolors, and the new CSS Basic thing. So maybe we shouldn’t call it stylehints or stylehints_enabled… honor_game_styles?)

FWIW, almost all of the usage of this API in garglk/terps is to measure the normal styles, e.g. to find out what foreground/background color the user selected in preferences. So, we’d always have to include normal, but it sounds fine to skip stuff that’s identical to Normal.

OK, sure.

1 Like

I’ve updated my PRs to incorporate feedback here.

I manually tested it in a local build of Parchment using a test I6 game:

I also tested Spatterlight and Gargoyle using this test game.

This proposal doesn’t (yet) account for window specific styles. These could be just stylehints that you set before opening a window, or they could even be from a custom CSS stylesheet that the author added. So I think it will need to transmit a map of window specific measurements. They could be omitted if the same as the baseline measurements for the corresponding window type.

That doesn’t sound right to me.

The point of my idea is that AsyncGlk doesn’t need to send stylehinted styles (which originated in the remote terp) back to the remote terp. AsyncGlk only needs to provide enough information to the remote terp to allow the remote terp to compute the measured styles itself.

AsyncGlk should/will automatically return settings from a custom CSS stylesheet, because in measure_style_entry, we’re using getComputedStyle. So, if there’s a custom CSS stylesheet in place, that will be factored in to the “theme” values we send back.

In measure_style, on the terp side, we check the theme to see if game styles are honored. If not, we report on the measured theme styles. If game styles are honored, then we check our own stylehints to see if we’ve overridden the theme styles. If we have, then we report the overridden styles; otherwise, we report on the theme styles.

Does that make sense?