LowWire: HighWire for the rest of the ST family - #7
Open
neilrackett wants to merge 23 commits into
Open
Conversation
The info bar and close gadget asked for the VDI system font by point size (11 or 12pt). The system font is not scalable - it is a fixed 8x8 bitmap in the ST low/medium resolutions and 8x16 in ST high and above - so a point size selects whichever face is nearest, landing on 8x16 in the ST resolutions and making the interface far too large. Ask by pixel height instead, using the value graf_handle() reported for the AES system font, so our text matches the desktop in every resolution. The three copies of the same heuristic are folded into one helper.
HighWire required a Speedo/FSM capable GDOS and called exit() when it found
none, so an ST or Mega ST(E) with only the ROM VDI could not run it at all.
The requirement was real rather than cosmetic: vqt_xfntinfo() is the only
path in font_base() that ever builds a FONTBASE, and on the ROM VDI it fails
for every id including the system font. font_byType()'s fallback chain ends
at font_base(1,0), so that NULL was then dereferenced and the machine
bombed. The gate was guarding a crash.
Add romvdi.c, wrapping every extended VDI call HighWire makes. With a GDOS
present each wrapper is a straight pass-through and behaviour is unchanged.
Without one:
vqt_xfntinfo synthesised, after checking vst_font() can select the id
at all - refusing unknown ids keeps the existing fallback
chain working instead of drawing with a stale font
vst_arbpt -> vst_point
vst_setsize32 ignored (no condensed text)
vst_map_mode remembered, so text conversion knows what it is holding
vqt_advance -> vqt_width
v_ftext -> v_gtext
v_ftext16/16n narrowed to 8-bit via unicode_to_8bit(), then v_gtext
vqt_f_extent16n narrowed likewise, then vqt_extent
vst_scratch, vst_kern, vst_(un)load_fonts ignored
The result is single-face, single-size text with no bold, italic or
scaling - the same output HighWire already produces when a GDOS is present
but has no fonts loaded, which is what an ST can actually manage.
Three problems visible when running in ST low/medium resolution. vq_scrninfo() arrived with NVDI and the plain ROM VDI does not answer it, so the reply array held unrelated data that decoded as out[0]=4 and 31 bits per pixel. That raised "Unrecognized screen format!" on every ST. Sanity check the reply and describe the screen ourselves when it is not usable: an ST/STE screen is always interleaved bitplanes, one plane per bit of depth, with the VDI's own colour index to pixel value mapping supplied alongside since it is not the identity. The safety net that should have caught this could never fire. It tested (!raster_cmap), but the statics are initialised to invalid_raster rather than NULL, so it was always false - which is why an unrecognised screen was followed by "cnvpal_color() undefined function called" instead of falling back to the standard bitmap format. Test against the stub instead. The status line looked stretched and the URL field kept the 8x16 face because vst_height() takes the CHARACTER height, not the cell height: the 8x8 face is only 6 pixels tall, so asking for 8 overshoots onto the next face or has the VDI scale one. Walk the request down until the cell handed back fits the AES's. The URL field hardcoded vst_point(12) and a 16 pixel caret; both now follow the resolution.
… notice Three follow-ups from testing in ST low and medium resolution. ST medium gives four colours and the VDI's defaults are white, black, red and green. remap_color() picks the nearest by RGB distance, so a page with a dark background and dark text sends both to black and the text vanishes. Recolour the two chromatic entries to greys, turning the palette into a luminance ramp - white, light grey, dark grey, black - so page colours sort by brightness and stay legible either way round. Indices 0 and 1 are left alone so G_WHITE and G_BLACK still mean what the interface expects, and the originals are put back at exit since the ST palette is global and leaving the desktop grey is not ours to do. Two planes only: mono needs no help and sixteen colours has the range to cope. Off via GREY_PALETTE = 0. The info bar and close gadget now ask for the ROM's small 6x6 face in the ST resolutions - the one the desktop labels icons with - since 8x8 leaves the bar too cramped to show much of a URL. sys_font_setup() takes the target cell height for this. The URL field stays on 8x8 deliberately: its caret steps in whole 8 pixel cells. Also drop the startup notice printed when no FSM GDOS is found. It painted raw TOS text over the screen on every launch and said nothing the user could act on.
ignore_colours was defined as (planes < 2), which is only ever true of a monochrome screen. ST medium resolution has two planes, so it took the full colour path - and that path paints page backgrounds and 3D edges with G_LWHITE and G_LBLACK, VDI pens 8 and 9, which need sixteen colours to exist. On a four pen screen they are out of range, and everything came out black on black including HighWire's own chrome. The light and dark shades are what the test is really about, so the condition is the number of pens rather than whether the screen is mono: (planes < 4). Monochrome is unaffected, ST medium now takes the same black-on-white path, and sixteen colours and above are unchanged. The grey palette from the previous commit is kept: with the interface on the mono path it no longer affects the chrome, but it still gives images four grey levels to dither into rather than white, black, red and green.
Without a CACHEDIR the disk cache never engages, and remote images are downloaded then thrown away: the image decoders read from files, so every image on an http page came out as a placeholder while local pages worked. Default to CACHE in the program's folder, created on first run. An explicit CACHEDIR still wins, and if the default cannot be written (say a write protected drive) cache_build() backs the cache out cleanly as before. The path building shares a new prog_dir() helper with open_default() instead of adding a third copy of the Dgetdrv/Dgetpath dance.
The URL edit field, the info bar and the status line all drew their text top aligned inside boxes sized for other things (toolbar icons, AES widgets), which left the small ST faces floating high with a gap below. The font setup helpers now report the cell height they actually selected and every draw site centres on it via one VCENTER macro; the edit field caret follows its text automatically. sys_font_setup()'s vst_height() walk-down also ran up to max_cell VDI traps on every info bar, status and toolbar redraw for an answer that is fixed for the session - the result is now remembered per cell size (only two ever occur), and ui_font_setup() no longer selects the system font twice.
The vq_scrninfo() compensation moves out of rasterizer() into romvdi.c with the other ROM VDI fallbacks, as hw_vq_scrninfo(). Without a GDOS the doomed call is no longer issued at all, and the synthesised reply now covers more screens than the ST modes it was written for: the 256 colour pen map (pen 1 is hardware pixel 255, not 1) so TT low renders images in the right colours, and a packed pixel description for more than 8 planes so Falcon style truecolour lands on the existing 16/24/32 bit rasterizers instead of an unusable fallback. In the narrowing layer, the map mode test is hoisted out of narrow()'s per character loop, "until NUL" becomes an explicit num < 0 convention instead of borrowing the buffer size, and hw_vqt_advance() converts its character through a new narrow_char() rather than truncating inline. has_fsm_gdos now starts FALSE so a missed romvdi_init() fails towards the safe fallback.
The 2 plane rasterizers were black and white dithers: they write the same word to both planes, so images could only produce hardware pixels 0 and 3 and the grey palette never reached them. New G2 variants of the three (colour mapped, greyscale, truecolour) do 4 level error diffusion and write the two plane words separately - with the grey ramp installed the hardware pixel values 0..3 run white, light, dark, black (VDI pens 0,2,3,1), so the chosen level is the pixel value. They are selected when color_GreyRamp is set, which color.c now exports; without the ramp the plain black and white dither remains, which is the right output for a white/black/red/green palette. The standard format last resort gets finished too: it left raster_gray and raster_true pointing at the fatal "undefined function" stubs and raster_stnd() read the low bytes of the palette entry where cnvpal_4_8() keeps the RGB value rather than the device pixel in the top byte. New gscale_stnd()/dither_stnd() writers cover grey and truecolour images there, and setup() allocates the dither buffer for that path on deep screens as well.
edit_init() fills the text buffer with cols non-breaking spaces and measures them to size the field - but it measured word->item, which for a fresh word still points at the shared empty word, not at the buffer just filled. With a Speedo GDOS vqt_f_extent16n() takes an explicit count, so it walked cols cells of adjacent static data and returned garbage that happened to look like a plausible width. The ROM VDI path narrows to a NUL terminated string, saw the empty word's terminator immediately and measured nothing: every text input collapsed to its few pixels of padding. Measure current->text, the cells that were actually written. This also gives GDOS systems a real measurement, and makes CursorH (taken from the same extent call) meaningful on the ROM path.
On ST low the menu titles overflow 320 pixels: History wrapped around onto the left of the bar, and opening its pulldown - which at 42 chars by 23 rows is wider than the whole screen - overflowed the AES's menu save area and took the machine down with two bombs. menu_fit() reworks the tree in memory after rsrc_load(), decided purely by measuring against the screen width. A first pass tightens every title to its exact text width and realigns the pulldowns (clamped to the right screen edge); if the bar still does not fit, a second pass hides the History title and its pulldown via OF_HIDETREE - every window already offers the history from its own popup. On 640 pixel screens and up the bar fits as it is and the tree is left untouched.
Twelve pages exercising what the parser and renderer actually support, grouped by category: text and inline styles, FONT/BASEFONT, lists, tables, forms (inputs at several sizes - the collapsed edit field bug was found this way), image scaling and floats, links and anchors, colours, CSS (selectors, font properties, borders, with the recognised but unimplemented properties labelled as such), character entities and a frameset. All filenames are 8.3 safe and the folder is self contained, so it works from any drive. html/sample/sample.htm gets a section linking the suite alongside the existing test pages.
ST medium's pixels are twice as tall as wide, so every image rendered stretched to double height; TT low has the same problem sideways. Use the pixel size the VDI reports for the workstation to squash the long axis of every image by the real ratio - the height on ST medium, the width on TT low - so pictures keep their real world proportions. Near square modes are untouched, and since the correction always shrinks, it also roughly halves the decode and dither work per image. Behind a new IMG_ASPECT config option, default on; set 0 for the old 1:1 rendering.
color_mapsetup() rewrites over 200 pens for the fixed colour cube, but only the two pens recoloured by the ST medium grey ramp were saved and put back at exit - quitting HighWire on a 256 colour screen with FIXED_CMAP left the desktop repainted. save_colors() now snapshots the whole palette before anything recolours it and color_restore() puts every pen back, replacing the grey pen special case with the general mechanism.
- menu_fit() finds the History title by its object index (M_HISTORY, as AEI.c already does) instead of matching the display string, and reads ob_spec through the free_string member instead of a cast. - The exit palette snapshot becomes a static 256 entry array: res_colors is bounded, so the malloc, its failure path and the exit-time free were machinery for nothing. The grey ramp macros went with it - the general restore removed their second use site. - img_scale() folds the mirrored aspect arithmetic into one squash() helper and only recomputes the rasterizer scale steps when a rasterizing caller (info != NULL) needs them - the layout paths were paying a long division per image per reflow for a discarded value. - raster_stnd() reads the device pixel with a shift instead of a byte alias; gscale_stnd()/dither_stnd() hoist the pixel_val[] lookups out of their per pixel loops, where the stores stop gcc doing it. - The toolbar caret steps by vdi_dev.wchar instead of a literal 8, the last hardcoded font width. - GREY_PALETTE is documented in example.cfg next to IMG_ASPECT.
new_image() set the placeholder box straight from the width/height attributes, so on ST medium the box appeared at twice the height the image would eventually render at (and the reverse on TT low), with the layout jumping when the data arrived. The aspect correction moves into a shared aspect_adjust() applied to the placeholder as well; since the display size doubles as img_scale()'s scaling target, img_scale now re-derives it from the absolute attributes first, which also stops repeated calls on the reload paths squashing an already corrected height a second time. The screen shape test and its division collapse into a factor decided once per session, so the per-image cost drops to a multiply and shift.
Runtime errors were printf'd/puts'd to the console, which on plain TOS is the screen: a stale cache.idx entry drew "location_rdIdx(): host entry not found" straight over the menu bar on real hardware. errprintf() now stays quiet unless LOGGING is enabled (matching logprintf), and every unguarded console diagnostic on a runtime path - some sixty sites across the loader, parser, cache index, containers, image decoders, encodings, fonts, network and window modules - goes through it. #ifdef DEBUG output is untouched. Conditions the user must actually see get proper alerts instead of silence: the Save As failure paths use hwUi_warn like the download manager already did, and a missing STiK/STinG layer is reported once, on the first attempt to go online.
hw_vqt_advance() took vqt_width()'s return value as the advance, but that return is a status - the character code, or -1 - while the cell width arrives in the first output argument, which the wrapper then zeroed. Every ROM VDI font therefore reported SpaceWidth as 33 pixels (32 being the code for space, plus one), and since the leading space of each wrapped line is skipped at draw time and compensated by exactly SpaceWidth, every wrapped line's first word landed some 27 pixels into its neighbour. That was the text mashing visible on every GDOS-less medium and high res screenshot; it also inflated list hanging indents (SpaceWidth * 5 = 165 pixels) and justification gaps.
input_disable() dereferenced input->Next before checking it, and then dereferenced the result of a ternary that had just decided it might be NULL. A plain <input type="text" disabled> always has a NULL Next at the moment its attribute is parsed, so any page using one took the machine down with two bombs. Found by the html/test suite's forms page, which is apparently the first document ever to try it.
With a Speedo GDOS, bold and italic are real fonts and extents match the drawing. On the ROM VDI they are vst_effects(): thickening widens every glyph and skewing leans it sideways, and neither shows up in vqt_extent, so every emphasised word measured narrower than it painted and later words on the line were placed into its ink. vqt_fontinfo() reports exactly the missing amounts in its effects array, which the font cache already called but discarded; keep them per font and add chars * EffWidth + EffSkew when words are measured. Zero with a GDOS present, so scalable-font rendering is unchanged.
The system font's bitmap cells use every row - ascenders touch the top, descenders the bottom - so consecutive lines of body text sat pixel to pixel on the ROM VDI. Scalable fonts never showed this because their cell metrics carry leading of their own. Each bitmap font now gets a Leading amount, applied where lines are stacked so the font metrics themselves stay honest (folding it into Descend shrank checkboxes and radios, which reconstruct the ascender by subtraction). A new LINE_SPACING option controls it: -1 (the default) picks an eighth of the cell height plus one pixel - 2 for the 8 pixel faces, 3 for the 16 pixel one - 0 restores the packed look, a positive number is used as pixels.
Member
|
Hi, even though this is not an active project, thanks for your PR. I'm wondering, is this still "backward compatible" to the original HighWire? I.e. can it do everything what HighWire could? If so, I'm happy to merge it (or even transfer ownership to you) but I don't find renaming to "LowWire" necessary then. If you cut some features out, then your rename, separate fork and maintenance makes more sense and I'm happy just to edit README.md to point to your project. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I'm working on a project that enables stock STs to get online using WiFi, and needed a fast native browser to test it with. HighWire seemed like the perfect choice, but it needed SpeedoGDOS (kept crashing), NVDI (needs key/serial) or fVDI (painfully slow), and had a UI that targeted high resolution TT/Falcon displays.
This PR, working title "LowWire", makes HighWire run, and run well, on the plain ROM VDI at every ST, TT and Falcon resolution (most of which I've screenshotted above), while leaving the GDOS path untouched.
What's new
romvdi.ccompatibility layer — every extended VDI call HighWire makes is wrapped: straight pass-through with GDOS, GEM 1.0 fallbacks without (16-bit text narrowed forv_gtext,vqt_xfntinfosynthesised,vq_scrninfoanswered ourselves — including the TT low pen map and Falcon truecolour).IMG_ASPECTconfig option.LINE_SPACING), algorithmic bold/italic widths are measured correctly, and avqt_widthmisuse that mashed wrapped lines together is fixed.CACHEfolder is created beside the program when noCACHEDIRis configured (without it, remote images are silently never displayed), and runtime diagnostics no longerprintfover the GEM screen.<input type="text" disabled>, and incomplete rasterizer fallbacks.html/test/(linked fromsample.htm) covers all supported elements and CSS — it was what enabled me to catch two of the bugs above.New options are documented in
example.cfg(GREY_PALETTE,IMG_ASPECT,LINE_SPACING), all default-on with sensible values and all switchable off. With NVDI/SpeedoGDOS present, behaviour is unchanged by design.Tested: Hatari (TOS 2.06 and EmuTOS) across ST low/medium/high, TT low and Medium, and Falcon 40/80 columns with/without interlacing, plus online with a real Mega STE via STinG.
There's more information in the commit messages and I'd be happy to split, squash, or adjust as needed to suit project conventions.