Commit Graph
1170 Commits
Author SHA1 Message Date
ruchirguitar bb8320f5fa ui: resize iconic flyouts from their free edges (#627)
* ui: resize iconic flyouts from their free edges (#5713)

* ui: persist independent flyout widths per group (#5713)

* ui: keep constrained flyouts resizable (#5713)

* ui: document flyout helpers for storytold/vectorcraft#5713

* ui: honor host header minimums for storytold/vectorcraft#5713

* ui: keep constrained frame axes resizable for storytold/vectorcraft#5713
2026-11-10 12:23:09 +01:00
FredApps 0e4943cb5e print: winprint 3c978dfc: pages at the job's resolution, QName ticket values, padded integers (#761)
winprint.rs#19 draws each page at the resolution the job's ticket asks for instead of ~96 dpi
(storytold/pdfcraft#4941, #2676). #12 writes QName property values as prefix:local, which
drivers such as the Microsoft IPP Class Driver otherwise reject when merging the ticket
(storytold/pdfcraft#3823). #3 accepts whitespace around xsd:integer values in
PrintCapabilities (Toshiba Universal Printer 2, storytold/pdfcraft#2430).

The public API craft-print uses is unchanged; only the rev and the lockfile move.
2026-11-10 01:55:59 +01:00
art804andFredApps b6b8a55b2c craft-print: two_sided(printer) reads one printer's two-sided support (#133)
printer_queues() (#618) lists the queues without asking any driver, so
every printer's two_sided is unknown there. A Print dialog listing from it
still needs to know whether the printer the user picks takes two-sided
settings, without asking every other driver as printers() does: Windows
takes about 48 s to give up on a printer it can't reach, such as a WSD
printer on another network (storytold/pdfcraft#2192).

two_sided(printer) finds that queue and reads its driver's capabilities
with the same check printers() makes, so the answers match. It is None
when there is no such queue, when the capabilities can't be read, and off
Windows, as printers() reports there.

The paused-printer test now checks that two_sided(name) gives printers()'s
answer for the test queue from exactly one driver query, and a new test
checks that a name no queue has is unknown. The query counting moves into
a small helper, which keeps windows_jobs_reach_the_spooler under the
cognitive-complexity limit.

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 00:03:35 +01:00
robertobulltechandrobertobulltech 92451193ba fix(print): honor DocumentDuplex-only Windows drivers (#411)
* fix(print): honor DocumentDuplex-only Windows drivers (#5130)

* fix(print): select duplex scope by two-sided options (#5130)

* docs(print): clarify duplex ticket selection and validation

* fix(print): select duplex scope for the requested option

* fix(print): require standard namespace for duplex options

* docs(print): document duplex regression

* docs(print): describe both offered duplex scopes

---------

Co-authored-by: robertobulltech <341520377+robertobulltech@users.noreply.github.com>
2026-11-09 23:48:52 +01:00
Dylan M. Taylor 1b34eba41e Merge pull request #696 from dotCooCoo/fix/update-messages
update: say whether an update is installing, ready, installed or failed
2026-11-08 22:08:03 -05:00
dotCooCoo c365d75c48 craft-update: the restart-helper test's comment matches spawn_detached
spawn_detached now starts the process on its waiting thread, and the test
starts the helper on the calling thread. The comment names only what the
two share, standard streams set to null.
2026-11-08 14:22:08 -08:00
dotCooCoo 70ac5a64bb Merge #687's head into fix/update-messages 2026-11-08 14:05:59 -08:00
dotCooCoo f89c6686df craft-update: keep link addresses out of the opener's warnings
The warnings that open_url_with_fallback and the system opener log no
longer include the address being opened, so a link carrying a token or
personal details does not reach the application's log. The reasons from
the system stay in the messages.
2026-11-08 13:52:56 -08:00
dotCooCoo 5eb0bca0b1 craft-update: start a detached process only from the thread that waits for it
On Unix, spawn_detached starts the waiting thread first, and that thread
starts the process and waits for it. When no thread can be started, the call
returns an error and the process is not started, so no exited process is
left unreaped. On Windows, where an exited process needs no wait, the
process starts on the calling thread and no thread waits for it. The list
of processes kept for a later call, and the reaping it needed, are removed.

When the opener of open_url_with_fallback is still running after its
timeout, the open-url thread waits for it. An opener whose state can't be
read is stopped and waited for.

The tests run spawn_detached's waiting-thread path and check that the
waiting thread gets the process's exit status, that the process is not
started when no thread can start, and that a program that cannot start
reports it. They no longer need ps.
2026-11-08 13:43:12 -08:00
dotCooCoo 196d2053db Merge #687's head into fix/update-messages 2026-11-08 13:02:48 -08:00
dotCooCoo 8f17421a0a craft-update: say when a kept process is reaped and that start_pending_update is for exit
A process that no thread could wait for is reaped when a later call starts
another process after it has exited. start_pending_update is called only as
the app exits: afterwards a finishing installation starts its helper itself
instead of reporting through tick, and one still running when the process
ends installs nothing. The reaping test waits for the process to exit
through its output and fails instead of looping when reaping does not
happen.
2026-11-08 12:25:32 -08:00
dotCooCoo 658136392b craft-update: start the helper of an install that finishes after start_pending_update
An app that calls start_pending_update in on_exit without dropping the
Updater now gets the same handoff as the drop: an install that finishes
after the call starts its helper itself instead of sending its outcome to
an Updater that will not read it.
2026-11-08 12:04:02 -08:00
dotCooCoo 075ad12afc craft-update: keep the update step's error when the helper cannot restart the app
When the update helper's step fails and starting the old app again fails
too, the error recorded for the next start names both, the step's first.
Before, the restart error replaced the step's.
2026-11-08 12:04:02 -08:00
dotCooCoo 324f06f639 craft-update: wait later for a started process when no thread can wait for it
When spawn_detached or the system opener cannot start a thread to wait for
the process it started, the process is kept and waited for by a later call
once it has exited, so on Unix it does not stay in the process table.
2026-11-08 12:04:02 -08:00
skylar taylor-barrick 5ee809fe9f Merge pull request #636 from songzhihui/fix/windows-print-combined
print: adopt file-backed Windows PDF loading after the merged crash fix
2026-11-08 17:38:54 +01:00
Zhihui Song 7ee35a9fbd chore(print): refresh internal winprint pin after bounded stream test repair 2026-11-08 19:07:09 +08:00
FredApps 0a9d1f48da Merge pull request #443 from alehel/craft-audio-split/05-aac-decode
craft-audio: AAC decoder (5/10)
2026-11-08 11:32:03 +01:00
alehelandClaude Opus 5.5 650d1996be craft-audio: say why the ADTS-in-MP4 seek limit is kept (#443)
The Limits entry from 79f5839 said what goes wrong but not why it is left.
It now says that such a file is outside the MP4 specification: an MP4 AAC
sample is one raw access unit with the configuration in the sample
description (ISO/IEC 14496-14, 14496-3), so ADTS headers do not belong in
it, and a sample's duration is the time it decodes to (ISO/IEC 14496-12).
It says why files within the specification are not affected, what still
works, and why seeks and edits in such files are not corrected: finding
where each sample's audio starts means counting the ADTS frames in every
sample before it, one read per sample, which would slow every ADTS-framed
MP4 at open to work around files outside the specification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 11:12:07 +01:00
alehelandClaude Opus 5.5 79f5839aa2 craft-audio: document the ADTS-in-MP4 seek and edit limits (#443)
AAC in MP4 stored as ADTS frames, with sample durations that count fewer
frames than the samples hold, decodes in full from the start, but seeks
place samples by those durations and land later than asked, and an edit
list measured in them can end the track early. The README's Limits section
now says so, and the seek example points to it.

Reported by FredApps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 09:10:16 +01:00
dotCooCoo 01e0322721 craft-update: the restart-helper test fails when the helper exits without starting the command
The test kept the events pipe open for reading and writing, so a helper
that exited before writing `started` left the read waiting forever. The
helper is now started through `relaunch_with` with a spawner the test
controls: it starts the helper the way `spawn_detached` does, and a
thread that waits for the helper to exit writes `exited`, so every read
gets a line. The test also checks that nothing is spawned before
`Restart::start`.
2026-11-07 22:43:46 -08:00
dotCooCoo 4671e213a2 craft-update: restart helpers start at the exit and outcomes reach the new app
- On macOS and Linux the restart helper starts at the exit too
  (Restart::start), instead of when the update is prepared. A close the
  user cancels no longer leaves a helper that starts a second copy after
  its one-minute wait, or a Flatpak helper that gives up before Restart
  to Update.
- The record of a finished update names the release's version instead
  of its tag, so a release whose tag is not a version is still reported.
- A standalone Windows copy updated from the portable zip becomes
  portable. The helper now writes the outcome and the record of the
  update to the folders beside it, which the new app reads.
- When the check at start finds a release for a prompt the app already
  opened with Updater::offer, that prompt shows why the last update
  failed.
- Errors and failure reasons reach 4.5:1 contrast on the card and in
  the prompt's message box in both themes, and a long one scrolls inside
  a 160 px area so the card's buttons stay on screen.
2026-11-07 21:53:25 -08:00
alehelandClaude Opus 5.5 a0e01d2552 craft-audio: review fixes, round 11 (#443)
Corrects round 10. A short ADTS-framed MP4 track, whose last sample is in
the read-ahead, lost that sample's trim when it cut more than one frame:
the trim counted as durations falling short, so the length was left to
decoding and its padding was presented. The track's last sample is now left
out of the read-ahead comparison.

Reported by CodeRabbit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 06:16:58 +01:00
alehelandClaude Opus 5.5 2506ad3005 craft-audio: review fixes, round 10 (#443)
ADTS-framed AAC in MP4: durations short of the frames in the samples read
ahead no longer end the track. With HE-AAC samples of two ADTS frames each,
timed as two core frames, the durations matched one output frame per sample,
so the undercount check passed and the track stopped at half its length,
which probe reported as known. The frames each read-ahead sample holds are
now counted, and durations short of them leave the length unknown until
decoded, as durations short of one frame per sample already did. Correctly
timed AAC-LC samples of several frames keep their end trim.

Reported by FredApps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 05:42:48 +01:00
dotCooCoo 8ecf7fa15d craft-update: self-update tests wait on handshakes instead of time
The Windows helper tests hand step_after_wait a wait that reports when
the helper reaches it and blocks until the test has reaped the stand-in
launcher, so the old-file check no longer depends on a 500 ms sleep or a
2 s observation window. The test-only wait_watch hooks are removed. The
launcher-that-keeps-running test asks the real wait with no timeout
instead of bounding the elapsed time.

The Unix restart test puts a sleep script on the helper's PATH that
reports each pause through a named pipe, and the started command reports
whether the old process was still running. A thread that waits for the
helper to exit writes to the same pipe, so a helper that dies early fails
the test instead of hanging it.
2026-11-07 20:34:29 -08:00
dotCooCoo 10d1cbb4ab craft-update: a check that answers during an installation reports the installation
A requested check still running when the user starts the installation from
the prompt answered with its own result once it finished, which replaced
Installing or Ready with UpToDate, the check error or nothing. Its answer is
now logged, and a requested check reports Installing, Ready or the recorded
helper failure, as a check started during the installation does.
2026-11-07 19:58:28 -08:00
dotCooCoo 75eacfc2a8 craft-update: an offer after a failed update clears the failure status
Updater::offer opens a prompt that carries the failed update's reason, and now sets the status to Idle, as a check that finds a release does. An app that shows CheckResultDialog while the status is not Idle no longer shows the failure card and the prompt at once.
2026-11-07 19:50:28 -08:00
dotCooCoo 53fbd5b636 craft-update: a retry after a failed helper waits for the exit and says why
A new install worker after start_pending_update gets a fresh handoff flag, so
a retry after a helper that failed while the app kept running reports through
tick and starts its helper at the exit, instead of at once. Updater::offer
carries the reason of a failed update into its prompt, as a check does.

The outcome records are read only from plain files: a link is not followed,
as is_plain_file already does for the files it moves. The tests wait for a
check's answer on its channel instead of polling with sleeps.
2026-11-07 19:44:54 -08:00
Zhihui Song e6489d9226 print: preserve the public GDI dependency identity while updating the PDF loader 2026-11-08 11:05:10 +08:00
dotCooCoo 164dcf6a6d craft-update: an install still running at start_pending_update starts its own helper
start_pending_update now marks the Updater as exiting and lets go of an install
that is still running, so the worker starts its helper itself when it finishes
(or records its failure) instead of sending its outcome into a channel nobody
reads. Before, only dropping the Updater did this, so an app that called
start_pending_update in on_exit and kept the Updater alive lost the helper of
an install that finished afterwards. Drop now calls start_pending_update.

The docs of Action::Close and start_pending_update say that an app which ends
the process in on_exit loses an install still in progress.
2026-11-07 18:41:49 -08:00
dotCooCoo a1471f2cf3 craft-update: show_if alone decides whether Restart to Update closes the app
restart_to_update only asks for the close; show_if returns Action::Close
only while an update is ready, so the test of a call with no update
ready fails when that check is removed. The test of a folder in place of
an outcome record is removed: Windows refuses to open a folder, so it
passed with the plain-file check removed.
2026-11-07 18:34:39 -08:00
Zhihui Song 45b7ededd9 Merge main after #605 and adopt the reviewed Windows stream test repairs 2026-11-08 10:29:10 +08:00
dotCooCoo 7bde3a7c1e craft-update: record an install worker that ends without an outcome at the exit
start_pending_update now treats a disconnected install worker as a
failed update, as tick does, so the next start says the update stopped
unexpectedly.
2026-11-07 18:09:51 -08:00
dotCooCoo 2411acae5e Merge main into fix/update-messages 2026-11-07 18:05:25 -08:00
dotCooCoo 4c29fc7cd5 craft-update: Check Again after a failed update still says why it failed
A failed update's reason is kept until a prompt that offers the update
again shows it, so a dismissed failure followed by Check Again still
explains the failure. A prompt that already showed the reason does not
repeat it.
2026-11-07 18:05:12 -08:00
058b047760 print: winprint 64e7afd4, whose Direct2D factory is multi-threaded (#605)
Windows.Data.Pdf draws a page on several of its own worker threads, which
share the print control's device context. With winprint 5f80b9b's
single-threaded factory Direct2D takes no locks, so those threads race in
d2d1.dll and the app closes while printing, most often on pages with Type 3
fonts (storytold/pdfcraft#5006, storytold/pdfcraft#2684).
storytold/winprint.rs#11 made the factory multi-threaded; it was merged as
64e7afd4.

craft-winprint-gdi moves to the same revision, so one checkout of
winprint.rs serves both; crates/gdi is unchanged between 417ac3c and
64e7afd4.

Co-authored-by: mertan-wq <235487900+mertan-wq@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 19:42:38 -06:00
dotCooCoo 8ccbe9b2ca craft-update: record every update failure at the exit and clear a stale ready prompt
An installation that finishes after the Updater is dropped now records a
helper that cannot start, and a failed installation, so the next start
reports it. start_pending_update records an installation failure it picks
up at the exit too. When a requested check finds that a started helper
failed, the open prompt no longer offers Restart to Update: it says why
the update could not be installed, and no close is asked for.
2026-11-07 17:37:59 -08:00
dotCooCoo 878d4e712c craft-update: tell the user what happened to an update
A requested check while an update downloads or waits for the app to close
now reports Installing or Ready instead of leaving the result window
blank, and an app the launcher manages reports Managed instead of calling
its version the newest.

On Windows a prepared update says "Update ready. Close {app} to install
it." rather than that it is installed. The helper records the version it
installed just before it starts the new app, and the first start of that
version reports Updated. A recorded helper failure is reported once at the
next start (InstallFailed, with Check Again), in the prompt when the
update is offered again, and to a requested check while the old app still
runs. A failure wins over a stale record of a finished update, and a new
update clears both records. Records are read up to 4 KiB.

Action::Close is now a request: the Windows helper starts only at the exit
(dropping the Updater or start_pending_update in on_exit). A close the
user cancels keeps the update ready, and Restart to Update in the prompt
or the result window (Updater::restart_to_update) asks for the close
again. A helper that cannot start at the exit is recorded and reported at
the next start.

New strings for app catalogs: "Installing update…", "Update ready. Close
{app} to install it.", "Restart to Update", "The update could not be
installed: {reason}", "{app} updated to {version}." and "{app} {version}
follows the version chosen in the launcher, so it does not update itself."
2026-11-07 17:31:45 -08:00
dotCooCoo cbd9abfdc3 craft-update: say that the drop starts the helper only when the app drops the Updater 2026-11-07 17:11:33 -08:00
dotCooCoo da46f3ca20 craft-update: an install that finishes after the updater is dropped starts its helper 2026-11-07 16:34:01 -08:00
dotCooCoo 0e856027c5 craft-update: log an install failure picked up at exit and clear the finished job 2026-11-07 16:27:51 -08:00
dotCooCoo f5f45b090c craft-update: start a waiting update helper when the app exits without Close
The Windows helper of a finished installation started only when show_if
returned Action::Close. An app that exited another way (the window's
close button, a quit while unsaved work held the close back, an OS
shutdown) dropped it, so the update never installed and the download
stayed in the cache.

Dropping the Updater now starts a helper that is still waiting, once,
including one from an installation that finished after the last tick.
Updater::start_pending_update does the same for an app that ends the
process in on_exit without dropping the Updater; Action::Close's doc
comment names it.
2026-11-07 16:23:44 -08:00
dotCooCoo df66c35b07 craft-update: keep the app running when a helper thread cannot start
spawn_detached, run_command and open_url_with_fallback started their
reaper, pipe-reader and opener threads with std::thread::spawn, which
panics when the thread cannot be created. The Windows update helper now
starts from the UI thread when the app closes, so such a panic would end
the app. Each thread now starts through std::thread::Builder: a process
that has already started keeps running and the failed wait is logged,
run_command stops the helper and returns an error, and a failed opener
thread is logged.
2026-11-07 16:19:03 -08:00
dotCooCoo 85de86be60 craft-update: start the Windows update helper when the app closes, and never restart a running app
On Windows the update helper was started as soon as the download was
ready, then waited one minute for the app to exit. An app that held
unsaved work, or a user who did not close it, outlasted that wait; the
helper then started the old exe again with no arguments, which opened a
second window of the old version and installed nothing.

- self_update::prepare_update does what update did but returns a
  Restart. On Windows the Restart records the release as installed and
  starts the helper; on macOS and Linux it does nothing, since the
  restart is already scheduled. update calls prepare_update and starts
  the Restart at once, so its callers see no change.
- Updater starts the Restart when it returns Action::Close. If the
  helper can't start, the app stays open and the prompt shows the error.
- The helper does not start any exe when the app is still running after
  its wait. A step that fails after the app exited still starts the old
  exe, as before.
2026-11-07 16:14:17 -08:00
alehelandClaude Opus 5.5 8291279c8b craft-audio: review fixes, round 9 (#443)
From CodeRabbit's review of round 8:
- AudioSpecificConfig::parse consumes extensionFlag3 after the config when
  extensionFlag is set, so the 0x2b7 sync extension after it is found.
- AudioSpecificConfig::to_bytes refuses a sampling rate of 0, which would not
  parse back.
- ADTS-framed HE-AAC in MP4 whose sample durations count core frames is not cut
  to the durations' half length: durations short of one frame per sample no
  longer end an ADTS-framed track, and its length is unknown until decoded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 22:41:48 +01:00
alehelandClaude Opus 5.5 ac8c5aae5d craft-audio: review fixes, round 8 (#443)
From wanikhawar's review:
- AudioSpecificConfig::parse reads backward-compatible SBR and PS signalling
  (a 0x2b7 sync extension after the config), which craft-media's Matroska
  demuxer writes for legacy HE-AAC IDs. Without lookahead those streams decoded
  as their core.
- An MP4 media edit after one empty edit is applied: its priming skip and its
  end. The empty edit's delay is not added as silence.
- A packet lost whole is concealed as long as the last one decoded, so a stream
  of multi-frame ADTS packets keeps its timing.
- ADTS-framed MP4 samples keep their sample table's timing, instead of every
  sample being read at open to look for several frames in one.
- AdtsHeader::write refuses an object type, sampling frequency index or channel
  configuration its fields cannot hold; it masked them into other values.
- AudioSpecificConfig::lc no longer panics for a sampling frequency index with no
  rate, and the decoder refuses such a config.
- AudioSpecificConfig::to_bytes refuses channel configuration 0 without a program
  config element, which would not parse back.
- Upsample2x starts each channel's history afresh when the channel count
  changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 22:26:05 +01:00
FredApps 57f3b0bb6c Pass read control through ICC archive lookup 2026-11-07 15:48:07 +01:00
FredApps aed25e8d34 Integrate image-only CCITT guards and combined rendition coverage 2026-11-07 15:44:44 +01:00
FredApps e26f3d1102 Integrate full-composite provenance with cancellable reads 2026-11-07 15:44:13 +01:00
FredApps f766bf10cd Merge main and preserve cancellable selective document reads 2026-11-07 15:43:45 +01:00
FredApps c8ea8be292 Merge main and preserve selected full-composite extraction 2026-11-07 15:40:03 +01:00