Repository: beeware/briefcase

Issue:
CMD.exe's Terminate Batch Script Prompt is Swallowed When Using a Wait Bar While Running a `bat` Script
This originated from and is made significantly worse by:
 - #809

However, all Python versions are affected by the aspect described here.


**Describe the bug**
`CMD.exe` emits a `Terminate batch job (Y/N)?` prompt to the user when a CTRL+C is sent during execution of a batch script. AFAICT, this is to allow the execution of the rest of batch script while canceling its current action (perhaps similar to bash's default behavior of continuing on error without `set -e`). This behavior will be ubiquitous regardless of the shell the user started briefcase in; all shells in Windows (e.g. powershell, terminal, msys2) will all defer batch script interpretation to `cmd`.

Many of the Android tools are invoked from `bat` scripts provided from Google. For instance, sending a CTRL+C while building an Android APK results in:
- `cmd` emitting its terminate prompt
- the prompt is swallowed by Rich's Wait Bar
- the user can't tell anything really happened since the Wait Bar is still pulsing
- the whole process remains stagnate until the user sends another CTRL+C
- (On Python 3.7/3.8, this also leads to Python hanging entirely, see # 809)

**To Reproduce**
Steps to reproduce the behavior:
1. Run `briefcase build android gradle`
2. Once the `Building...` Wait Bar starts, send CTRL+C
3. Witness issue

**Expected behavior**
CTRL+C should allow the user to exit or provide clear instructions (such as the terminate batch prompt) on how to exit.

**Environment:**
 - Operating System: Windows 10
 - Python version: 3.7, 3.8, 3.9, 3.10
 - Software versions:
   - Briefcase: https://github.com/beeware/briefcase/commit/8df2d112eba97175c740931cf168c89c370307d1

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#877](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (3e7fe7a) into [main](https://codecov.io/gh/beeware/briefcase/commit/9ab31c82726347d5a25fcefc6483aac6eb7ee7bb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (9ab31c8) will **decrease** coverage by `0.46%`.
> The diff coverage is `100.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/console.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb25zb2xlLnB5) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/xcode.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMveGNvZGUucHk=) | `89.62% <0.00%> (-7.55%)` | :arrow_down: |
| [src/briefcase/commands/open.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9vcGVuLnB5) | `92.85% <0.00%> (-7.15%)` | :arrow_down: |
| [src/briefcase/platforms/macOS/xcode.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvbWFjT1MveGNvZGUucHk=) | `92.30% <0.00%> (-5.77%)` | :arrow_down: |
| [src/briefcase/platforms/windows/app.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvd2luZG93cy9hcHAucHk=) | `93.02% <0.00%> (-2.33%)` | :arrow_down: |
| [src/briefcase/platforms/windows/visualstudio.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvd2luZG93cy92aXN1YWxzdHVkaW8ucHk=) | `93.02% <0.00%> (-2.33%)` | :arrow_down: |
| [src/briefcase/platforms/macOS/app.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvbWFjT1MvYXBwLnB5) | `98.03% <0.00%> (-1.97%)` | :arrow_down: |
| [src/briefcase/commands/base.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9iYXNlLnB5) | `97.96% <0.00%> (-0.41%)` | :arrow_down: |
| [src/briefcase/commands/create.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9jcmVhdGUucHk=) | `99.40% <0.00%> (-0.30%)` | :arrow_down: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#877](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (3e7fe7a) into [main](https://codecov.io/gh/beeware/briefcase/commit/9ab31c82726347d5a25fcefc6483aac6eb7ee7bb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (9ab31c8) will **decrease** coverage by `0.46%`.
> The diff coverage is `100.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/console.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb25zb2xlLnB5) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/xcode.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMveGNvZGUucHk=) | `89.62% <0.00%> (-7.55%)` | :arrow_down: |
| [src/briefcase/commands/open.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9vcGVuLnB5) | `92.85% <0.00%> (-7.15%)` | :arrow_down: |
| [src/briefcase/platforms/macOS/xcode.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvbWFjT1MveGNvZGUucHk=) | `92.30% <0.00%> (-5.77%)` | :arrow_down: |
| [src/briefcase/platforms/windows/app.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvd2luZG93cy9hcHAucHk=) | `93.02% <0.00%> (-2.33%)` | :arrow_down: |
| [src/briefcase/platforms/windows/visualstudio.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvd2luZG93cy92aXN1YWxzdHVkaW8ucHk=) | `93.02% <0.00%> (-2.33%)` | :arrow_down: |
| [src/briefcase/platforms/macOS/app.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvbWFjT1MvYXBwLnB5) | `98.03% <0.00%> (-1.97%)` | :arrow_down: |
| [src/briefcase/commands/base.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9iYXNlLnB5) | `97.96% <0.00%> (-0.41%)` | :arrow_down: |
| [src/briefcase/commands/create.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9jcmVhdGUucHk=) | `99.40% <0.00%> (-0.30%)` | :arrow_down: |
| ... and [3 more](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#877](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (3e7fe7a) into [main](https://codecov.io/gh/beeware/briefcase/commit/9ab31c82726347d5a25fcefc6483aac6eb7ee7bb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (9ab31c8) will **decrease** coverage by `0.36%`.
> The diff coverage is `100.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/console.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb25zb2xlLnB5) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/xcode.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMveGNvZGUucHk=) | `89.62% <0.00%> (-7.55%)` | :arrow_down: |
| [src/briefcase/platforms/macOS/xcode.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvbWFjT1MveGNvZGUucHk=) | `92.30% <0.00%> (-5.77%)` | :arrow_down: |
| [src/briefcase/commands/open.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9vcGVuLnB5) | `96.42% <0.00%> (-3.58%)` | :arrow_down: |
| [src/briefcase/platforms/macOS/app.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvbWFjT1MvYXBwLnB5) | `98.03% <0.00%> (-1.97%)` | :arrow_down: |
| [src/briefcase/commands/base.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9iYXNlLnB5) | `97.96% <0.00%> (-0.41%)` | :arrow_down: |
| [src/briefcase/commands/new.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9uZXcucHk=) | `97.01% <0.00%> (+0.02%)` | :arrow_up: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#877](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (3e7fe7a) into [main](https://codecov.io/gh/beeware/briefcase/commit/9ab31c82726347d5a25fcefc6483aac6eb7ee7bb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (9ab31c8) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/console.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb25zb2xlLnB5) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/commands/new.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9uZXcucHk=) | `97.01% <0.00%> (+0.02%)` | :arrow_up: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#877](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (3e7fe7a) into [main](https://codecov.io/gh/beeware/briefcase/commit/9ab31c82726347d5a25fcefc6483aac6eb7ee7bb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (9ab31c8) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head 3e7fe7a differs from pull request most recent head d4bf93d. Consider uploading reports for the commit d4bf93d to get more accurate results

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/console.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb25zb2xlLnB5) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/commands/new.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9uZXcucHk=) | `97.01% <0.00%> (+0.02%)` | :arrow_up: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#877](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (d4bf93d) into [main](https://codecov.io/gh/beeware/briefcase/commit/9ab31c82726347d5a25fcefc6483aac6eb7ee7bb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (9ab31c8) will **decrease** coverage by `0.46%`.
> The diff coverage is `100.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/console.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb25zb2xlLnB5) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/xcode.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMveGNvZGUucHk=) | `89.62% <0.00%> (-7.55%)` | :arrow_down: |
| [src/briefcase/commands/open.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9vcGVuLnB5) | `92.85% <0.00%> (-7.15%)` | :arrow_down: |
| [src/briefcase/platforms/macOS/xcode.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvbWFjT1MveGNvZGUucHk=) | `92.30% <0.00%> (-5.77%)` | :arrow_down: |
| [src/briefcase/platforms/windows/app.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvd2luZG93cy9hcHAucHk=) | `93.02% <0.00%> (-2.33%)` | :arrow_down: |
| [src/briefcase/platforms/windows/visualstudio.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvd2luZG93cy92aXN1YWxzdHVkaW8ucHk=) | `93.02% <0.00%> (-2.33%)` | :arrow_down: |
| [src/briefcase/platforms/macOS/app.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvbWFjT1MvYXBwLnB5) | `98.03% <0.00%> (-1.97%)` | :arrow_down: |
| [src/briefcase/commands/base.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9iYXNlLnB5) | `97.96% <0.00%> (-0.41%)` | :arrow_down: |
| [src/briefcase/commands/create.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9jcmVhdGUucHk=) | `99.40% <0.00%> (-0.30%)` | :arrow_down: |
| ... and [3 more](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#877](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (d4bf93d) into [main](https://codecov.io/gh/beeware/briefcase/commit/9ab31c82726347d5a25fcefc6483aac6eb7ee7bb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (9ab31c8) will **decrease** coverage by `0.46%`.
> The diff coverage is `100.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/console.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb25zb2xlLnB5) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/xcode.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMveGNvZGUucHk=) | `89.62% <0.00%> (-7.55%)` | :arrow_down: |
| [src/briefcase/commands/open.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9vcGVuLnB5) | `92.85% <0.00%> (-7.15%)` | :arrow_down: |
| [src/briefcase/platforms/macOS/xcode.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvbWFjT1MveGNvZGUucHk=) | `92.30% <0.00%> (-5.77%)` | :arrow_down: |
| [src/briefcase/platforms/windows/app.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvd2luZG93cy9hcHAucHk=) | `93.02% <0.00%> (-2.33%)` | :arrow_down: |
| [src/briefcase/platforms/windows/visualstudio.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvd2luZG93cy92aXN1YWxzdHVkaW8ucHk=) | `93.02% <0.00%> (-2.33%)` | :arrow_down: |
| [src/briefcase/platforms/macOS/app.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvbWFjT1MvYXBwLnB5) | `98.03% <0.00%> (-1.97%)` | :arrow_down: |
| [src/briefcase/commands/base.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9iYXNlLnB5) | `97.96% <0.00%> (-0.41%)` | :arrow_down: |
| [src/briefcase/commands/create.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9jcmVhdGUucHk=) | `99.40% <0.00%> (-0.30%)` | :arrow_down: |
| ... and [3 more](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#877](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (d4bf93d) into [main](https://codecov.io/gh/beeware/briefcase/commit/9ab31c82726347d5a25fcefc6483aac6eb7ee7bb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (9ab31c8) will **decrease** coverage by `0.09%`.
> The diff coverage is `100.00%`.

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/console.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb25zb2xlLnB5) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/commands/open.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9vcGVuLnB5) | `96.42% <0.00%> (-3.58%)` | :arrow_down: |
| [src/briefcase/platforms/windows/app.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvd2luZG93cy9hcHAucHk=) | `93.02% <0.00%> (-2.33%)` | :arrow_down: |
| [src/briefcase/platforms/windows/visualstudio.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9wbGF0Zm9ybXMvd2luZG93cy92aXN1YWxzdHVkaW8ucHk=) | `93.02% <0.00%> (-2.33%)` | :arrow_down: |
| [src/briefcase/commands/create.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9jcmVhdGUucHk=) | `99.40% <0.00%> (-0.30%)` | :arrow_down: |
| [src/briefcase/commands/new.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9uZXcucHk=) | `97.01% <0.00%> (+0.02%)` | :arrow_up: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#877](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (d4bf93d) into [main](https://codecov.io/gh/beeware/briefcase/commit/9ab31c82726347d5a25fcefc6483aac6eb7ee7bb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (9ab31c8) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head d4bf93d differs from pull request most recent head 13dc268. Consider uploading reports for the commit 13dc268 to get more accurate results

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/console.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb25zb2xlLnB5) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/commands/new.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9uZXcucHk=) | `97.01% <0.00%> (+0.02%)` | :arrow_up: |

codecov[bot]: # [Codecov](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) Report
> Merging [#877](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (d4bf93d) into [main](https://codecov.io/gh/beeware/briefcase/commit/9ab31c82726347d5a25fcefc6483aac6eb7ee7bb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) (9ab31c8) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head d4bf93d differs from pull request most recent head 13dc268. Consider uploading reports for the commit 13dc268 to get more accurate results

| [Impacted Files](https://codecov.io/gh/beeware/briefcase/pull/877?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware) | Coverage Δ | |
|---|---|---|
| [src/briefcase/console.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb25zb2xlLnB5) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/integrations/subprocess.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9pbnRlZ3JhdGlvbnMvc3VicHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/briefcase/commands/new.py](https://codecov.io/gh/beeware/briefcase/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beeware#diff-c3JjL2JyaWVmY2FzZS9jb21tYW5kcy9uZXcucHk=) | `97.01% <0.00%> (+0.02%)` | :arrow_up: |


Patch:
diff --git a/changes/811.bugfix.rst b/changes/811.bugfix.rst
new file mode 100644
index 00000000..8556231a
--- /dev/null
+++ b/changes/811.bugfix.rst
@@ -0,0 +1,1 @@
+The Wait Bar is disabled for batch scripts on Windows to prevent hiding user prompts when CTRL+C is pressed.
diff --git a/src/briefcase/console.py b/src/briefcase/console.py
index aa2a34f6..d291a2e3 100644
--- a/src/briefcase/console.py
+++ b/src/briefcase/console.py
@@ -307,7 +307,7 @@ class Console:
         # Signal that Rich is dynamically controlling the console output.
         # Therefore, all output must be printed to the screen by Rich to
         # prevent corruption of dynamic elements like the Wait Bar.
-        self.is_output_controlled = False
+        self.is_console_controlled = False
 
     def prompt(self, *values, markup=False, **kwargs):
         """Print to the screen for soliciting user interaction.
@@ -364,7 +364,7 @@ class Console:
             # message=None is a sentinel the Wait Bar should be inactive
             self._wait_bar.add_task("", start=False, message=None)
 
-        self.is_output_controlled = True
+        self.is_console_controlled = True
         wait_bar_task = self._wait_bar.tasks[0]
         previous_message = wait_bar_task.fields["message"]
         self._wait_bar.update(wait_bar_task.id, message=message)
@@ -384,7 +384,35 @@ class Console:
             # Deactivate the Wait Bar if returning to its initial state
             if previous_message is None:
                 self._wait_bar.stop()
-                self.is_output_controlled = False
+                self.is_console_controlled = False
+
+    @contextmanager
+    def release_console_control(self):
+        """Context manager to remove console elements such as the Wait Bar.
+
+        This is useful to temporarily release control of the console
+        when, e.g., a process is interrupted or a user needs to be
+        prompted. For instance, when batch scripts are interrupted by
+        CTRL+C in cmd.exe, the user may be prompted to abort the script;
+        so, the console cannot be controlled while such scripts run or
+        the prompt may be hidden from the user.
+        """
+        # Preserve current console state
+        is_output_controlled = self.is_console_controlled
+        is_wait_bar_running = self._wait_bar and self._wait_bar.live.is_started
+
+        # Stop any active dynamic console elements
+        if is_wait_bar_running:
+            self._wait_bar.stop()
+
+        self.is_console_controlled = False
+        try:
+            yield
+        finally:
+            self.is_console_controlled = is_output_controlled
+            # Restore previous console state
+            if is_wait_bar_running:
+                self._wait_bar.start()
 
     def boolean_input(self, question, default=False):
         """Get a boolean input from user, in the form of y/n.
diff --git a/src/briefcase/integrations/subprocess.py b/src/briefcase/integrations/subprocess.py
index 3d49f9dc..b470b958 100644
--- a/src/briefcase/integrations/subprocess.py
+++ b/src/briefcase/integrations/subprocess.py
@@ -7,6 +7,7 @@ import sys
 import threading
 import time
 from contextlib import suppress
+from functools import wraps
 from pathlib import Path
 
 import psutil
@@ -88,6 +89,46 @@ def get_process_id_by_command(
     return None
 
 
+def ensure_console_is_safe(sub_method):
+    """Decorator for Subprocess methods to conditionally remove dynamic console
+    elements such as the Wait Bar prior to running the subprocess command.
+
+    :param sub_method: wrapped Subprocess method
+    """
+
+    @wraps(sub_method)
+    def inner(sub, args, **kwargs):
+        """Evaluate whether conditions are met to remove any dynamic elements
+        in the console before returning control to Subprocess.
+
+        :param sub: Subprocess object
+        :param args: list of implicit strings that will be run as subprocess command
+        :return: the return value for the Subprocess method
+        """
+        # Just run the command if no dynamic elements are active
+        if not sub.tools.input.is_console_controlled:
+            return sub_method(sub, args, **kwargs)
+
+        remove_dynamic_elements = False
+
+        # Batch (.bat) scripts on Windows.
+        # If cmd.exe is interrupted with CTRL+C while running a bat script,
+        # it may prompt the user to abort the script and dynamic elements
+        # such as the Wait Bar can hide this message from the user.
+        if sub.tools.host_os == "Windows":
+            executable = str(args[0]).strip() if args else ""
+            remove_dynamic_elements = executable.lower().endswith(".bat")
+
+        # Run subprocess command with or without console control
+        if remove_dynamic_elements:
+            with sub.tools.input.release_console_control():
+                return sub_method(sub, args, **kwargs)
+        else:
+            return sub_method(sub, args, **kwargs)
+
+    return inner
+
+
 class NativeAppContext:
     """A wrapper around subprocess for use as an app-bound tool."""
 
@@ -214,6 +255,7 @@ class Subprocess:
         tools.subprocess = Subprocess(tools)
         return tools.subprocess
 
+    @ensure_console_is_safe
     def run(self, args, stream_output=False, **kwargs):
         """A wrapper for subprocess.run().
 
@@ -231,13 +273,13 @@ class Subprocess:
          - The `text` argument is defaulted to True so all output is returned
            as strings instead of bytes.
         """
-        # If `stream_output` or dynamic screen content (e.g. a Wait Bar) is
+        # If `stream_output` or dynamic screen content (e.g. the Wait Bar) is
         # active and output is not redirected, use run with output streaming.
         is_output_redirected = kwargs.get("capture_output") or (
             kwargs.get("stdout") and kwargs.get("stderr")
         )
         if stream_output or (
-            self.tools.input.is_output_controlled and not is_output_redirected
+            self.tools.input.is_console_controlled and not is_output_redirected
         ):
             return self._run_and_stream_output(args, **kwargs)
 
@@ -309,6 +351,7 @@ class Subprocess:
 
         return subprocess.CompletedProcess(args, return_code, stderr=stderr)
 
+    @ensure_console_is_safe
     def check_output(self, args, **kwargs):
         """A wrapper for subprocess.check_output()
 


Test patch:
diff --git a/tests/console/Console/test_release_console_control.py b/tests/console/Console/test_release_console_control.py
new file mode 100644
index 00000000..d79c5ee6
--- /dev/null
+++ b/tests/console/Console/test_release_console_control.py
@@ -0,0 +1,79 @@
+from unittest.mock import Mock
+
+from briefcase.console import Console
+
+
+def test_console_is_controlled():
+    """Console control is released and then restored."""
+    console = Console()
+
+    console.is_console_controlled = True
+
+    with console.release_console_control():
+        assert console.is_console_controlled is False
+
+    assert console.is_console_controlled is True
+
+
+def test_console_is_not_controlled():
+    """Effective no-op when console is not currently controlled."""
+    console = Console()
+
+    assert console.is_console_controlled is False
+
+    with console.release_console_control():
+        assert console.is_console_controlled is False
+
+    assert console.is_console_controlled is False
+
+
+def test_wait_bar_is_active():
+    """An active Wait Bar is stopped."""
+    console = Console()
+
+    with console.wait_bar("Testing..."):
+        # Mock the Wait Bar stop and start methods
+        console._wait_bar.stop = Mock(wraps=console._wait_bar.stop)
+        console._wait_bar.start = Mock(wraps=console._wait_bar.start)
+
+        # Wait Bar is active
+        assert console._wait_bar.live.is_started is True
+        assert console.is_console_controlled is True
+
+        with console.release_console_control():
+            # Wait Bar is not active
+            console._wait_bar.stop.assert_called_once()
+            assert console._wait_bar.live.is_started is False
+            assert console.is_console_controlled is False
+
+        # Wait bar is restored and active again
+        console._wait_bar.stop.assert_called_once()
+        assert console._wait_bar.live.is_started is True
+        assert console.is_console_controlled is True
+
+
+def test_wait_bar_is_not_active():
+    """An inactive Wait Bar is not effected."""
+    console = Console()
+
+    # Instantiate the Wait Bar
+    with console.wait_bar("Testing..."):
+        pass
+
+    # Mock the Wait Bar stop and start methods
+    console._wait_bar.stop = Mock(wraps=console._wait_bar.stop)
+    console._wait_bar.start = Mock(wraps=console._wait_bar.start)
+
+    assert console._wait_bar.live.is_started is False
+    assert console.is_console_controlled is False
+
+    with console.release_console_control():
+        # A stop request was not made for the Wait Bar
+        console._wait_bar.stop.assert_not_called()
+        assert console._wait_bar.live.is_started is False
+        assert console.is_console_controlled is False
+
+    # A start request was not made for the Wait Bar
+    console._wait_bar.start.assert_not_called()
+    assert console._wait_bar.live.is_started is False
+    assert console.is_console_controlled is False
diff --git a/tests/integrations/subprocess/test_ensure_console_is_safe.py b/tests/integrations/subprocess/test_ensure_console_is_safe.py
new file mode 100644
index 00000000..1b81f24e
--- /dev/null
+++ b/tests/integrations/subprocess/test_ensure_console_is_safe.py
@@ -0,0 +1,107 @@
+from unittest.mock import ANY, MagicMock, Mock
+
+import pytest
+
+from briefcase.integrations.subprocess import Subprocess
+
+
+@pytest.fixture
+def mock_sub(mock_tools):
+    mock_tools.input.release_console_control = Mock(
+        wraps=mock_tools.input.release_console_control
+    )
+    mock_sub = Subprocess(mock_tools)
+    mock_sub._run_and_stream_output = MagicMock()
+    mock_sub._subprocess.run = MagicMock()
+    mock_sub._subprocess.check_output = MagicMock()
+    return mock_sub
+
+
+@pytest.mark.parametrize("batch_script", ["HELLO.BAT", "hello.bat", "hElLo.BaT"])
+def test_run_windows_batch_script(mock_sub, batch_script):
+    """Console control is released for a Windows batch script in run."""
+    # Console control is only released on Windows
+    mock_sub.tools.host_os = "Windows"
+
+    with mock_sub.tools.input.wait_bar("Testing..."):
+        mock_sub.run([batch_script, "World"])
+
+    mock_sub._subprocess.run.assert_called_with(
+        [batch_script, "World"],
+        text=True,
+        encoding=ANY,
+    )
+    mock_sub.tools.input.release_console_control.assert_called_once()
+
+
+@pytest.mark.parametrize("batch_script", ["HELLO.BAT", "hello.bat", "hElLo.BaT"])
+def test_check_output_windows_batch_script(mock_sub, batch_script):
+    """Console control is released for a Windows batch script in
+    check_output."""
+    # Console control is only released on Windows
+    mock_sub.tools.host_os = "Windows"
+
+    with mock_sub.tools.input.wait_bar("Testing..."):
+        mock_sub.check_output([batch_script, "World"])
+
+    mock_sub._subprocess.check_output.assert_called_with(
+        [batch_script, "World"],
+        text=True,
+        encoding=ANY,
+    )
+    mock_sub.tools.input.release_console_control.assert_called_once()
+
+
+@pytest.mark.parametrize(
+    "cmdline, kwargs",
+    [
+        ([], dict()),
+        (["Hello", "World"], dict()),
+        (["Hello", "World"], dict(val1="value1", val2="value2")),
+    ],
+)
+def test_negative_condition_not_controlled(mock_sub, cmdline, kwargs):
+    """Passthrough to Subprocess if conditions to release console control are
+    not met while the console is not controlled."""
+    mock_sub.run(cmdline, **kwargs)
+    mock_sub._subprocess.run.assert_called_with(
+        cmdline,
+        **kwargs,
+        text=True,
+        encoding=ANY,
+    )
+
+    mock_sub.check_output(cmdline, **kwargs)
+    mock_sub._subprocess.check_output.assert_called_with(
+        cmdline,
+        **kwargs,
+        text=True,
+        encoding=ANY,
+    )
+
+    mock_sub.tools.input.release_console_control.assert_not_called()
+
+
+@pytest.mark.parametrize(
+    "cmdline, kwargs",
+    [
+        ([], dict()),
+        (["Hello", "World"], dict()),
+        (["Hello", "World"], dict(val1="value1", val2="value2")),
+    ],
+)
+def test_negative_condition_controlled(mock_sub, cmdline, kwargs):
+    """Passthrough to Subprocess if conditions to release console control are
+    not met while the console is controlled."""
+    with mock_sub.tools.input.wait_bar("Testing..."):
+        mock_sub.run(cmdline, **kwargs)
+        mock_sub.check_output(cmdline, **kwargs)
+
+    mock_sub._run_and_stream_output.assert_called_with(cmdline, **kwargs)
+    mock_sub._subprocess.check_output.assert_called_with(
+        cmdline,
+        **kwargs,
+        text=True,
+        encoding=ANY,
+    )
+    mock_sub.tools.input.release_console_control.assert_not_called()