Repository navigation
Linux port - #2516
Closed
ell-shad wants to merge 53 commits into
Closed
Linux port#2516ell-shad wants to merge 53 commits into
ell-shad wants to merge 53 commits into
Conversation
…test smoke Engine compiles unmodified with GCC 15 via CMake into libCalcManager.so. Shims for MSVC-only headers (ppltasks eager-task, intsafe, winerror) and a portable prelude replacing pch.h; upstream src/ untouched. C ABI uses UTF-8 (wchar_t is 4 bytes on Linux vs 2 on Windows/C#). Smoke test drives the real engine: 2+2=4, clear, div0 error, sin30=0.5, history. SPEC + plan + todo.
…e+run net10.0 console referencing Avalonia 11.3.6; dotnet build/run green. Replaced by the real Avalonia App in Task 4.
Replaces CalcManager.Interop (C++/CX) with LibraryImport-style DllImport using UTF-8 + GCHandle-routed callbacks (wchar_t size differs on Linux). Resolver finds in-tree libCalcManager.so (CALCMANAGER_PATH override, system paths for future .deb). 6 tests green against the real engine: 2+2=4, history, clear, div0 error/recovery, sin30=0.5, memory round-trip. UnitConverter interop deferred to Task 6 with additive ABI section.
Verbatim-port dependency-free Common enums (Bindable attrs dropped, noted). AppResources from generated en-US tables; LocalizationStringUtil %1 substitution in managed code (was kernel32 FormatMessage). LinuxLocalization from CultureInfo/ICU (separators, grouping, digits, RTL, week, calendar). 14 tests green.
Replaces WinRT DecimalFormatter/CurrencyFormatter surface used by VMs (grouping, fraction digits, decimal-always, per-currency digits). Ties round half away from zero (documented WinRT RoundHalfDown delta). 9 tests green.
Replaces ApplicationData.LocalSettings. Write-through, typed via System.Text.Json, corrupt-file tolerant. 5 tests green.
IClipboardService (in-memory + Avalonia), LinuxNetworkManager (no metered detection on Linux), IDispatcher (immediate + Avalonia UIThread), LinuxTraceLogger no-op keeping all VM call sites compiling. 5 tests green; full suite 39/39, zero using Windows.
create_ex with per-token command types; history item texts/tokens/flat replay commands per mode; display-command snapshot; memorized-numbers cache; remove/max-size. Frozen Task 1 core untouched. Smoke asserts [132,93,132] replay for 2+2. ctest green.
create_ex with typed expression tokens; history texts/tokens/replay commands per mode; display-command snapshot; memorized content; radix results; radix/precision/degree/load-mode controls. 7 tests green.
CalcCommands generated from Command.h; TokenType/RadixType verbatim; CopyPasteManager on IClipboardService; EngineStrings/CalcUtils helpers; NarratorTokens map; CalcEngine display/memory/history/paren events; native refresh-memorized + is-recording. Build green.
Display adapter over engine events; History/Memory/HistoryItem VMs; StandardCalculatorViewModel mechanical port (flat command replay, typed tokens, no snapshots yet). 12 headless tests green: 3 modes, programmer radixes, memory, history recall/clear, errors, paste.
CCalcEngine::InitialOneTimeOnlySetup initializes shared globals once per load; concurrent engine instances crash. Full suite 58/58 green serially. The Avalonia app must likewise drive the engine from the UI thread only.
Headless parity 12/12 (3 modes, programmer radixes, memory, history recall/clear, errors, paste). UI: Standard/Scientific/Programmer panels, docked History/Memory tabs, radix/bits/angle sync, bit-flip, keyboard map, Ctrl+C/V. Full suite 58/58.
WinRT Calendar backed by System.Globalization (1601 floor preserved, dual identifier spellings). DateCalculationEngine verbatim port. 10 tests green incl. upstream full-range/leap/add/subtract/OOB vectors.
Mechanical port (long-date via culture pattern, UTC today init). 7 tests green: diff/add/subtract, same-dates, OOB, reversed-dates, resource keys.
LinuxCalendar over System.Globalization; engine verbatim port; VM port with culture long-date; Date panel (diff/add-subtract) + nav entry. 17 tests green; app launches clean.
Native UnitConverter driven by C#-side data/display callbacks (UTF-8). Shared utf8_util.h extracted. 14 unit_converter_* symbols; build clean.
867-line database ported (ids, names, factors, explicit temperature conversions, region defaults). WinRT wrappers replaced by plain records; currency behind ICurrencySource seam (Task 7); IncludeCurrency flag gates the currency category. Build green.
P/Invoke over converter ABI with pooled loader-direction strings (native copies after return; prior batch freed on next call — fixes use-after-free that left ints perfect and strings garbage). 9 tests green: 12 categories, m->ft, ft->km, C->F both ways, kg->lb, supplementary, prefs, clear.
Mechanical port onto ManagedUnitConverter + Linux services (dispatcher, settings, formatters, clipboard seams). NetworkManager ported; currency behind Task-7 stub. 9 headless tests green incl. temperature offsets.
Dropdowns, dual read-only fields, switch, supplementary list, pad, currency-state bindings (dormant), 12-category nav (Currency disabled), prefs saved on leave. App launches clean; 93/93 green.
Live endpoint api.frankfurter.dev/v1 (migrated from .app); RegionInfo runtime metadata; recorded-JSON tests (4 green); XDG file cache.
Frankfurter ratios + RegionInfo metadata, XDG settings/cache, System.Text.Json, status-after-finalize (fixes LoadFinished polling race upstream masks via dispatcher). 6 loader tests green: web/cache/stale-shape/offline/ratios.
RegionInfo metadata + live ECB ratios (30 currencies verified live); full loader port (cache/stale/week-old/offline); VM wiring + nav; status-after-finalize race fix. 104/104 green.
Recursive-descent parser (implicit mult, right-assoc ^, functions, pi/e/x/params, y= prefix, degrees mode); NaN convention for bad samples. OxyPlot.Avalonia 2.1.0 referenced. 10 tests green.
Roots via verified bisection (asymptote-safe), refined extrema, parity sampling, observed range. 5 tests green (quadratic/cubic/sine/1/x/line).
OxyPlot.Avalonia 2.1.0 binds Avalonia 0.10 -> FileNotFound under Avalonia 11; LiveCharts2 (MIT, Avalonia-11-native) renders instead. Local parser, numeric key features, equation editor + style + settings + pad. Fixed Rebuild self-recursion. 126/126 green; app launches clean.
…collapse Panel-aware code-behind keyboard map (calc/converter/text separation); mode window titles; About dialog; History dock toggle + narrow-window auto-collapse. Build green.
Keyboard routing, titles, About, panel collapse; PORTING-NOTES.md (30-item swap table); .deb packaging (publish + side-by-side .so, desktop file, icon); bridge lifetime fixes; test content link. 126/126 + ctest green; zero using Windows; src/ untouched.
24 theory tests (12-category round-trips + negate gating); JPY/JOD decimals, week-old/stale/failed currency states; nullable pragmas for verbatim ports (0 warnings); xUnit analyzer fixes; obsolete API swap.
…ST, params, paste) Covers prior manual-check items headlessly. Real fix: engine separators now follow the UI locale at creation (German decimal comma verified).
Stripped engine, fixed perms, changelog + copyright + verified Depends, lintian overrides for inherent self-contained tags (clean run). C++ tree clang-format clean per repo .clang-format.
Async test waits, callback-gated completion asserts, extra parity tests, headless coverage of prior manual items, docs counts + checklist triage, packaging hardening. Truth table: 171/171 + ctest green.
…waits) SecondInstance hung ~1/6 full-suite runs: SpinUntil-polled plain status field vs 10s timeout while loader completed (JIT-tiering-dependent stale read). Status now volatile-backed with release/acquire pairing; tests gate on LoadData's exited hook. 10/10 full-suite green.
Avalonia 11.3 has no live-region notification API (verified in package metadata), so announcement strings bind as AutomationProperties.Name on displays and converter/date fields. Task-9 comments updated to present.
Visual review found the window did not resemble Windows Calculator:
* Mode routing: nav tags went through Enum.TryParse<ViewMode>, and
Standard/Scientific/Programmer are ViewMode members, so Standard opened the
unit converter. Now an explicit converter-tag set.
* Keypad collapse: the four mode panels were DockPanel siblings; only the last
declared child fills, so the visible panel lost the fill area and buttons
rendered content-sized in empty cells. Panels now stack in one content-host
Grid; keypad buttons force Stretch like upstream CalcButtonStyle (Avalonia's
Fluent Button does not).
* Unbound panels: Find<DockPanel>("ConverterPanel") went null when the element
became a Grid, leaving date/converter/graphing with no DataContext (blank).
Lookup is type-agnostic now.
* Keypads completed: scientific had no 0/./=/trig, programmer no C/CE. Layouts
transcribed from the upstream operator XAMLs.
* Nav is now a LeftMinimal hamburger flyout (upstream), not a docked sidebar.
* Styling from upstream App.xaml: number/operator/accent fills and opacities,
flat memory row, large display, light+dark variants.
New Avalonia.Headless suite (28 tests) runs the real window through Skia:
layout geometry (button vs cell fill), panel exclusivity, DataContext binding,
flyout, and per-mode pixel checks in both themes. 199 total, Debug+Release.
- RegionInfo.DisplayName followed the UI culture and the culture list contains chr-US, so USD showed 'Cherokee - US Dollar' / EUR 'España - Euro'. Use EnglishName with a deterministic neutral/English region preference. - Programmer mode drops the big result (upstream shows only the radix lines). - Snapshot coverage for more mode/size combinations. 206 tests green (Debug + Release), ctest green, lintian clean, .deb rebuilt.
…rogrammer missing 5 Independent audit vs the unmodified reference XAML (src/Calculator/Views/*.xaml, ButtonId list): Scientific missing: Sec, Csc, Cot + all 6 inverses, Sinh/Cosh/Tanh + their inverses, Sech/Csch/Coth + inverses, Ceil, Cube, DMS, Degrees, Euler, Floor, LogBaseY, Rand, TwoPowerX, YRootX, ClearEntry. Programmer missing: Lsh, Nand, Nor, RolC, RorC. The ViewModel and the native Ratpack engine already computed every one of these correctly (sec 30deg=1.1547005383792515290182975610039, asinh(1)=0.88137358701954302523260932497979, e=2.7182818284590452353602874713527) - they were simply unreachable from the UI, which a ViewModel-only suite cannot detect. Fix is XAML-only: scientific keypad extended to 15 rows x 5 cols (all 59 reference ButtonIds reachable) inside a ScrollViewer, programmer gains the 5 missing bitwise ops. Regression guard: tests/Calculator.Linux.Tests/Audit/KeypadCompletenessTests walks the REAL window's visual tree per mode and fails if any reference ButtonId is not bound to an on-screen button. Suite: 220 green.
…ort, run_all.sh Independent audit vs unmodified reference @ d125246. Fixed AUD-S2-001 (Scientific missing 34/59 functions, Programmer missing 5) and AUD-S3-002 (build-deb.sh exited 2 on success). Verified: engine is the real Ratpack rational engine (exact 0.1+0.2=0.3, 1/3*3=1, sqrt(2.25)-1.5=0, 170! beyond double range, 30-digit e); converter data byte-identical (158/158, 105/105, 154/154); both resource tables exact (1166/1166, 108/108); clean-checkout build green. Reference Graphing and Currency are 100% MOCK, so those cannot be compared. Outstanding: on-screen UI-RUN (compositor wedged), 220 keyboard bindings, locale matrix, scaling, a11y, soak, licences.
Reference declares 71 button shortcuts (extracted to AUDIT/evidence/reference_shortcuts.json). The port matched hardware Key codes only - 0 uses of KeySymbol - so operators silently failed on QWERTZ/AZERTY/non-Latin layouts, and only ~12 of 71 shortcuts existed. MainWindow.OnKeyDown now dispatches on e.KeySymbol through three tables transcribed from the reference: s_commonChars (operators, all modes), s_scientificChars (29), s_programmerChars (14). Key.* retained for the numpad and function keys. Also: 158/158 reference golden vectors now replay green. AUDIT/extract_golden.py pulls every TestDriver::Test case out of the reference's own CalculatorManagerTest.cpp and ReferenceGoldenVectorTests replays them against the port's engine - Microsoft-authored expectations covering standard/scientific/programmer/paren/error/memory/max-digits/ order-of-operations. Suite: 410 green.
LocaleMatrixTests (22): 15 cultures verified for decimal/group separators, en-IN 2,2,3 grouping, RTL detection, and invariant-globalization fallback. ar-EG correctly renders Arabic-Indic digits. No defect found. KeyboardShortcutParityTests (42): reference's 71 shortcuts transcribed to s_commonChars/s_scientificChars/s_programmerChars, driven through the real window with a non-US Key code so only KeySymbol can route them. Suite: 432 green. AUDIT/run_all.sh: ALL CHECKS GREEN.
S3-006: the flyout listed Graphing LAST, after all thirteen converters. The
reference's CategoryManifest (src/Calculator.ViewModels/Common/NavCategory.cs)
orders the calculator group Standard, Scientific, Graphing, Programmer, Date -
Graphing is THIRD. Fixed, and the pre-existing WindowLayoutTests expectation
that had recorded the wrong order was corrected to the reference sequence.
S3-007: Ctrl+Alt+1..5 (Windows' resolution of the manifest's Number1..5 access
keys) were absent, so no mode was keyboard-reachable. Added, plus plain
Alt+1..5 as the Linux mnemonic form. AccessKey was not put in XAML - Avalonia
11.3 has no such property on ListBoxItem (AVLN2000).
Also replayed three more reference test bodies verbatim, all green first time:
ReferenceDateEngineTests 9/9 (DateCalculatorTests.cs)
ReferenceCopyPasteTests 4/4 (CopyPasteManagerTests.cs)
ReferenceVmGoldenVectorTests 22/22 / 114 assertions
(StandardCalculatorViewModelTests.cs,
extracted by AUDIT/extract_vm_vectors.py)
Suite: 482 green.
ReferenceNavCategoryTests (61): replays NavCategoryTests.cs - CreateNavCategoryGroup pins Graphing at index 2 of the calculator group (independent confirmation of S3-006), plus the stable SerializationIds (Standard=0 ... Angle=15, Currency=16, Graphing=17) that are written to the persisted last-used-mode setting, friendly names, the three-way group classification and the Number1..5 VirtualKey map. Order-dependent locale tests fixed. LocaleMatrixTests display assertions depended on which engine was built first in the process, because the unmodified engine holds its resource provider behind a 'const' member installed by the first instance (CalcEngine.h:117). They passed or failed depending on test order. Tagged [Trait(Category, IsolatedProcess)] and AUDIT/run_all.sh now runs them in a separate process: 525 main + 18 isolated. Not user-visible - the app builds one engine at startup and never changes locale - and unfixable in the port without editing the engine header. IndianGrouping_IsTwoTwoThree previously asserted 12,000, which groups identically under en-IN and en-US and could not detect pollution; it now asserts 1234567 -> 12,34,567 vs 1,234,567. ui_drive.sh now locates the flyout's own X toplevel and polls for it instead of guessing coordinates from the window origin.
S3-008: Currency mode rendered both symbols on a row BELOW the display instead of beside their amounts. The leading symbol had no padding so its glyph was clipped by the window edge, and the trailing symbol inherited default left alignment inside its '*' grid column, so it appeared at the window's horizontal centre. The reference (UnitConverter.xaml) puts each symbol as a Grid.Column sibling of its amount, with Padding="16,0,0,0" on the leading one and the trailing one aligned to the right-aligned Value2. No test caught it - both symbols rendered, just in the wrong place. Found by driving the real app. ConverterSymbolLayoutTests (3) now pins it; both glyphs verified correct in AUDIT/evidence/screens/06-currency.png. Also: AUDIT/license_audit.py walks project.assets.json for every project and resolves each package's licence from its .nuspec. 44 transitive packages, all permissive (MIT / Apache-2.0). One test-only package (xunit.abstractions) declares only a licenseUrl and is reported for review rather than assumed. Wired into run_all.sh as a release gate. run_all.sh is now 9 steps: engine build, ctest, managed build, main suite, isolated locale process, packaging, reference data parity, licence audit, live on-screen drive.
S2-002 (security): Avalonia.FreeDesktop 11.3.6 pins Tmds.DBus.Protocol 0.21.2, which carries CVE-2026-39959 / GHSA-xrw6-gwf8-vvr9 (CVSS 7.1 High): a peer on the same D-Bus bus can spoof signals, exhaust file descriptors, or crash the process. No workaround exists. Pinned the patched 0.21.3 in Calculator.Linux.csproj; 'dotnet list package --vulnerable' is now clean. license_audit.py gained the vulnerability gate so this cannot regress silently. S2-003 (functionality): Scientific-mode calculations were computed correctly but never appeared in the History pane. HistoryViewModel._currentMode is only assigned by ReloadHistory, and ReloadHistory was never called from anywhere. The engine keeps separate m_pStdHistory / m_pSciHistory lists, so every scientific calculation was stored in one list and looked up in the other. SetCalculatorType now reloads history for Standard/Scientific, matching the reference's MainPage.xaml.cs:281,290. ReferenceHistoryTests (21) replays the reference's HistoryTests including all 13 TestHistoryStandardOrderOfOperations cases verbatim, the three-space history expression format, the 20-item cap with eviction, and the scientific case that exposed S2-003. Suite: 549 + 18 = 567 green.
Verdict narrowed from 'blocked on environment' to 'blocked on coverage'. Sections rewritten: counts by method now include reference-test replay, live on-screen UI-RUN, security and licence; the defect list carries all 8; section 6 splits what is now verified from what is genuinely not started (scaling, a11y, soak, and 8 reference C# suites still to replay); section 7 is now what the automated on-screen pass covers versus what still needs a human (per-button disabled-state transitions, live dark-mode repaint, Orca).
Closes two of the three previously-unstarted coverage areas. Scaling: launched at AVALONIA_GLOBAL_SCALE_FACTOR 1.0/1.25/1.5/2.0. At 200% everything scales proportionally, nothing clipped, all 24 standard buttons legible. The port's layouts are all *-sized, so nothing was pinned to absolute pixels. Captures in AUDIT/evidence/screens/scaling/. Live dark mode: flipped color-scheme to prefer-light with the app running; it repainted in place without a restart, confirming the theme follows the desktop setting at runtime rather than only at startup. AT-SPI: the session bus is reachable, which is the precondition for any screen reader to see the app at all. The tree itself was not enumerated - pyatspi is absent on this machine - so Orca announcement quality remains a human check. Two of my own bugs fixed on this pass: - ConverterSymbolLayoutTests located the currency symbol blocks by their text, which comes from the live rate feed, so it passed or failed depending on the network. It failed in the final gate run. Now located structurally. - run_all.sh step 9 skipped the on-screen drive when DISPLAY was unset and still reported ALL CHECKS GREEN. A gate that skips quietly is worse than one that fails. DISPLAY now defaults to :0 when the socket exists, and AUDIT_REQUIRE_DISPLAY=1 makes the skip fatal. Also: .gitignore now pins 'TEMP FOLDER SKILLS/' so the user's reference material cannot be committed to the fork even by accident. Gate is now 10 steps.
A reviewer landing on this fork sees upstream's README first, which says 'Windows Calculator' and gives no hint that a native Linux port exists here. Rather than edit upstream's README - which would misattribute the fork to Microsoft - this adds a root-level signpost pointing at linux/, the audit, the exactness evidence, and the honest open items. Upstream's README.md, LICENSE, SECURITY.md and code of conduct are untouched.
The on-screen drive step required an already-running Calculator window before it would run. ui_drive.sh launches the application itself, so that check was both wrong and self-defeating: it failed on a perfectly good display once AUDIT_REQUIRE_DISPLAY=1 made the skip fatal. Now it only requires a usable DISPLAY and xdotool. Full gate with AUDIT_REQUIRE_DISPLAY=1: all 10 steps green, no skips.
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.
Fixes #.
Description of the changes:
How changes were validated: