Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
23 changes: 16 additions & 7 deletions .github/workflows/PR-builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ jobs:
# ===========================================================================
linux-x64:
name: Linux x86_64
# Not pinned: libsdl1.2-dev on 24.04 is the SDL2-backed sdl12-compat shim,
# which the back-end is happy with now that the ascii key table is read from
# the X keymap rather than from SDL's private X11_KeyToUnicode.
# Not pinned. SDL 2 is vendored in engine/lib/sdl and compiled by the
# configure step, so there is no SDL package to install -- only SDL's X11
# build dependencies, every one of which configure requires
# (engine/lib/CMakeLists.txt): SDL itself insists only on libxext-dev, and
# would quietly leave out the feature each of the others turns on.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -63,7 +65,8 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y build-essential \
libsdl1.2-dev libx11-dev libxft-dev libfreetype6-dev libopenal-dev libgl1-mesa-dev
libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev \
libxft-dev libfreetype6-dev libopenal-dev libgl1-mesa-dev
- name: Build Debug
run: |
cmake -S . -B build/linux-debug -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug
Expand Down Expand Up @@ -103,8 +106,9 @@ jobs:
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib nasm \
libsdl1.2-dev:i386 libx11-dev:i386 libxft-dev:i386 libfreetype6-dev:i386 \
libopenal-dev:i386 libgl1-mesa-dev:i386
libx11-dev:i386 libxext-dev:i386 libxrandr-dev:i386 libxcursor-dev:i386 \
libxfixes-dev:i386 libxi-dev:i386 libxss-dev:i386 \
libxft-dev:i386 libfreetype6-dev:i386 libopenal-dev:i386 libgl1-mesa-dev:i386
- name: Build Release (-m32)
env:
# Point pkg-config / find_package at the 32-bit libraries.
Expand Down Expand Up @@ -181,7 +185,12 @@ jobs:
with:
distribution: temurin
java-version: '17'
- uses: android-actions/setup-android@v3
# v4, not v3: v3 installs the SDK's "tools" package, which Google has
# removed, so the action now fails before the build starts ("Failed to
# find package 'tools'"). v4 installs platform-tools alone (its v4.0.2,
# "Fix for removed tools package"), and moves to node24 and a current
# cmdline-tools. sdkmanager is still on the path for the step below.
- uses: android-actions/setup-android@v4
- name: Install NDK + CMake (match app/build.gradle ndkVersion)
run: sdkmanager "ndk;25.2.9519653" "cmake;3.22.1"
- name: Build APK (assembleDebug)
Expand Down
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,38 @@ what your scripts have to say, and what ends up in your files.

## [Unreleased]

### Added

- On Linux, the game window works with tiling window managers such as Hyprland, i3 and sway. `$pref::Video::windowedRes` now says whether the game has a size in mind. Leave it empty and the window opens resizable at `$pref::Video::defaultResolution`, and a tiling window manager is free to tile it. Give it a size and the window opens at exactly that size, which a tiling window manager takes as its cue to float the window rather than tile it; it can still be resized afterwards. A stacking desktop simply opens the window at the size either way. The editor's own preferences now leave `windowedRes` empty, so the editor tiles; everywhere else -- Windows, macOS, a stacking Linux desktop -- it still opens at 1024x768. New projects keep AppCore's `1024 768`, so a game opens at the size it was designed for and floats on a tiling window manager until the player tiles it.
- On Linux, the engine follows the window manager into and out of fullscreen. Make the window fullscreen from the window manager -- SUPER+F in Hyprland -- and the canvas grows to fill the screen, and shrinks back when you leave. `isFullScreen()` and `$pref::Video::fullScreen` report it, although SDL itself never notices, so `toggleFullScreen()` takes the window out again from there. `$pref::Video::fullScreen` is also honoured at start-up, as it already was on Windows and macOS.

### Changed

- On Linux, the engine runs on SDL 2, built from source with the engine, instead of the SDL 1.2 installed on the system; there is no SDL package to install any more. SDL 1.2 came in two implementations that disagreed about windows, events and fullscreen -- the original 1.2.15 and the `sdl12-compat` shim that Arch and Ubuntu 24.04+ ship under its name -- and the engine now builds one SDL, 2.32.10, the same as Torque3D and as the Emscripten port the Web build uses. What a game sees: the window follows a drag, a tiling layout or fullscreen at once, where it used to wait for a drag to settle and then reload every texture; fullscreen always uses the desktop's own resolution; typed text arrives as character events of its own, so accented letters, dead keys, compose and input methods type what they should on any keyboard layout; a key binding written with a character still finds the key that types that character; alerts are native dialogs; the clipboard carries UTF-8 both ways instead of Latin-1, and no longer blocks for up to five seconds when another program is slow to answer; and `$pref::Video::disableVerticalSync` is honoured, where it was ignored. Building on Linux needs the X11 development packages listed in `build-linux.sh` in place of SDL's -- `libxrandr-dev`, `libxcursor-dev`, `libxfixes-dev`, `libxi-dev` and `libxss-dev` among them -- and configure stops and names any that are missing, rather than building an SDL without display modes, cursors or the XInput2 mouse.
- On Linux, a notch of the mouse wheel is worth `$pref::Input::MouseWheelSpeed` on the wheel axis -- 120 by default, as Windows reports it -- instead of 10. GUI scrolling looks only at the direction and is unchanged, but a binding that scales by the value moves twelve times as far per notch. The two side buttons reach the engine as `button3` and `button4`.
- The Web build runs on SDL 2 -- Emscripten's port of 2.32.10, the same SDL the Linux build vendors -- instead of Emscripten's JavaScript SDL 1.2, and shares the Linux build's keyboard and text handling. Typed text arrives as characters of its own, so accented letters, dead keys and input methods type what they should: `café naïve` used to arrive as `caf nave`. The mouse wheel works, worth `$pref::Input::MouseWheelSpeed` a notch; it did nothing before. Copy and paste work within the game, though not yet with other programs. The browser's own pointer is hidden over the canvas only while the canvas draws a cursor of its own, instead of both showing. `getDesktopResolution()` reports the player's screen instead of a fixed 1024x768, and changing the resolution no longer reloads every texture.
- On the Web, a page that sizes the canvas with CSS now decides its size, and the game follows the browser window as it is resized; otherwise the canvas opens at `$pref::Video::windowedRes`, or `defaultResolution` when that is empty, and takes any size it is asked for. `toggleFullScreen()` takes effect on the click or key press that asked for it -- browsers only allow fullscreen then -- so `$pref::Video::fullScreen` is not applied when the page loads.
- A key bound in the `GlobalActionMap` works while a text box has the keyboard, and types nothing, as in Torque3D -- unless it is one of the text box's own editing keys, such as the arrows, Backspace or Return. `sinkAllKeyEvents` no longer holds such a key back.

### Fixed

- On Linux and the Web, a key pressed in a text box no longer reaches the game's key bindings as well. Since the move to SDL 2 a key press arrives without its character -- the character follows as an event of its own -- so the text box passed every key on, and typing `q` into a chat box typed a `q` and fired whatever was bound to `q`. A key that types now belongs to the text box that has the keyboard; keys that type nothing, and Ctrl and Alt shortcuts, go on to the bindings as before.
- On Linux and the Web, text input is on only while a text box has the keyboard. It used to stay on while any text box anywhere was awake -- an open Toy Box tools panel was enough -- so every key pressed during play produced text that nothing was reading.
- Pressing Tab in a text box no longer runs the old box's `AltCommand` twice, and on Linux and the Web no longer leaves the box it moved to unable to type.
- A text box that can't be seen can't be typed into. Hiding or deactivating it, or any control it sits in, takes the keyboard from it, and its `onLoseFirstResponder` and `onBlur` callbacks run; and closing the last dialog lets go of the keyboard, where the control that had it used to go on receiving keys after it was gone from the screen.
- On Linux, `toggleFullScreen()` works after the window has been resized or tiled. It asks for fullscreen at the window's current size, and a check that a fullscreen size must be one the display lists refused any size a drag or a tiling layout had produced.
- On Linux, message boxes -- `messageBox()` and the engine's own alerts -- appear again, and can no longer end a debug build. The engine ignored `SIGCHLD` so that no exited child process would linger as a zombie, which also has the kernel reap a child before anything can wait for it; SDL shows its message box from a child and waits for it, so the wait failed, and a debug build stopped on SDL's assertion. Even then SDL's box can only be drawn in the X server's core fonts, which a Wayland-first desktop may not install at all, so where it can't be shown the engine now asks `zenity` -- the way SDL's own Wayland driver shows a box -- and only prints the alert to the console when neither is available.
- On Linux, `gotoWebPage()` opens the page in the desktop's browser through `xdg-open`, as Torque3D does, and a browser named in `$Pref::Unix::WebBrowser` is still used when it is set. Either way the browser is now given the address: every browser the engine used to try was started with an empty argument list, so it opened, if at all, on its home page.
- On Linux, the window no longer appears at 800x600 and resizes itself a moment later. It is created at the size the game is about to ask for; the old placeholder came from `$pref::Video::resolution`, which nothing sets before the canvas exists. The first size mattered more than it looked: a tiling window manager remembers it, and it was the size Hyprland floated the window at.
- On Linux, `getDesktopResolution()` could report the desktop's width and height the wrong way round.
- A `GuiScrollCtrl` applies `hScrollBar`, `vScrollBar` and `scrollBarThickness` the moment they are set. Which bars are showing was worked out only when the scroller was resized or a child changed size, so a scroller built in script with `Extent` written before its bar modes worked it out while both were still the default `alwaysOn`, and kept that answer. A child added afterwards was given the room left by two bars that were not there. A `scale` child keeps the proportion it first measures, so when the bars turned out not to exist it grew past the scroller -- a 1280x720 child in a 1280x720 scroller with 10px bars became 1290x730 -- and both bars then stayed on at every window size. It happened whatever the bar mode, `alwaysOff` included. Changing a bar mode from script on a scroller that already has children now takes effect at once too, rather than at its next resize.
- A control with `textWrap` and `textExtend` takes its wrapped height before it is drawn, not the first time it is. It was only ever sized while it was being rendered, and a control outside its parent's visible area is never rendered, so a column of wrapped labels in a `GuiScrollCtrl` kept the heights they were authored with below the fold: the scroll area grew as you scrolled down to them, the thumb shrank while you scrolled, and `scrollToBottom()` stopped short of rows that grew after it had measured. The control is now measured as soon as its text or its width changes and when it wakes -- so a script can `setText()` or add a row and scroll to it straight away -- and before the next frame when anything else it depends on changes, such as its profile, `fontSizeAdjust` or `textWrap`. `textExtend` without `textWrap`, which sizes the width to the text, is measured the same way.
- `fileDelete()` and `directoryDelete()` now tell the resource manager what they removed, so `isFile()` stops reporting a file that has been deleted. `isFile()` and `getFileCRC()` answer out of the manager's dictionary rather than off the filesystem -- deliberately, because a file inside a mounted zip has no standalone path to check -- and nothing maintained that dictionary on delete, so for the rest of the session a deleted file went on existing as far as script could tell. A fresh process reported correctly, which is what made it easy to miss. A file whose resource is still loaded is left in place, the same rule `removePath()` follows. `fileSize()` was never affected and remains a straight filesystem check.
- `getFileCRC()` no longer crashes when the file behind it has gone. It opened whatever the resource manager handed it and read from the result without checking, so any entry that outlived its file -- a delete that went around the engine, a removable volume, a file taken away by another process -- ended the process with an access violation instead of returning -1.
- `populateFontCacheRange()` and `populateAllFontCacheRange()` stop one code point short of `rangeEnd`, and their documentation said the opposite -- it called `rangeEnd` "the final Unicode point in range". A bake asking for printable Latin-1 as `32, 255` therefore never generated 0xFF; it wants `32, 256`. The documentation now states that the range is half-open, and the Gui Profile Editor's own font bake, which had the bug, asks for 256.
- Editor theme fonts are baked at the sizes that are actually drawn. A control's `fontSizeAdjust` multiplies its profile's `fontSize`, so the editor asks for sizes no field anywhere declares -- a profile set to 16 worn by a control adjusting 1.2 asks for 19 -- and `ThemeManager.populateFonts()` only ever filled five hardcoded ones. A size it missed was not a loud failure: the engine built that cache on demand from the installed face and filled it with only the characters that happened to be on screen, which looks correct on a machine that has the theme faces and renders as nothing on one that does not. The bake now covers every size the theme's font folder already holds and every size the engine had to rasterize during the session, and it writes each face and size on its own instead of rescanning the project for every cache sharing the face name. The twenty-one caches the four stock themes were shipping in this state -- nine of them holding nothing but a header -- have been rebaked, so editor text at those sizes now draws without the theme faces installed.
- The editor's theme font caches shrank from 124MB to 27MB. They had been baked over the whole of BMP-0, which writes a fixed 30-byte record for each of 65,536 code points whether or not the face has that glyph: about 92% of every 2MB file was that table, and most of the remainder was the missing-glyph box cached tens of thousands of times over. `ThemeManager.populateFonts()` now bakes space through the end of the Miscellaneous Symbols block (U+0020 to U+26FF) -- Latin-1, Latin Extended, Greek, Cyrillic, the punctuation nobody notices until it is missing (curly quotes, en and em dashes, the ellipsis), currency, fractions, arrows, math operators and box drawing. Every glyph metric in the rebaked caches is identical to the one it replaced. What is no longer cached is 57 code points across all four themes: the C0 control codes (which a font draws as its missing-glyph box, so caching them only made a stray control character visible), five dingbat arrows, Fira Code's private-use terminal glyphs, and the fi/fl ligatures.
- Typing a character beyond ASCII into a text box inserted the wrong thing. A text box keeps its text as UTF-8, but a typed character went in as a single byte cut from it: `é` became the lone byte 0xE9, which is not UTF-8 at all and drew as a missing glyph, and anything past U+00FF kept only its low byte, which is either not UTF-8 or a different character entirely -- a Polish `Ł` came out as `A`. A typed character now goes in as the whole of its UTF-8, and the caret, Backspace, Delete, the arrow keys, Shift-selection, overwrite mode and a click into the text all treat it as one character rather than stopping in the middle of it, where the next keystroke would split it. This holds wherever the platform reports the character a key types -- Windows, macOS and Linux -- and for text that arrives on its own rather than on a key press, which is how Windows already delivers IME input and how the SDL 2 Linux and Web back-ends will deliver all of it. Two Windows faults go with it: AltGr characters (`@` on a German keyboard, `ć` on a Polish one) were swallowed as Ctrl shortcuts, because Windows reports AltGr as Ctrl plus Alt, and Ctrl+Shift with a letter could type a control character into the text. A character outside the Basic Multilingual Plane, such as an emoji, goes in as U+FFFD, the replacement character, as it does everywhere else the engine converts text. `maxLength` counts bytes, so a field fills sooner with accented text than with ASCII.

## [4.0-ea4] - 2026-08-18

Expand Down
Loading
Loading