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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ English (US) release notes, mirroring CHANGELOG.md (Brazilian Portuguese). Befor

## [Unreleased]
### What's new
- Controller shortcuts of your choice: the button that opens Console Mode, the session menu's and the one that goes back to the PC are now set in Settings (hold the buttons you want and let go on the same controller). Capture never combines buttons from different controllers. None is on by default: the first time you open this version the app shows the setup, with a suggestion for each action (Home, Select + Y and Start + Select). One button can't serve two actions. If you used the Home button or Start + Select before, choose them again. (#116)
- Settings → "Receive test versions (alpha and beta)": anyone can join the tests of upcoming versions from the app. Off by default; turning it on shows a warning that test versions can have bugs.

### Fixes
- Steam: when you go back to the PC (button, menu, shortcut, `stop` link or leaving Big Picture) Big Picture is always closed and Steam is asked to quit the normal way, its own Exit, without killing the process. Before, it stayed open and was often left glitchy. It does not close Steam if a game is running, and you can turn it off in Settings → "Close Steam when going back to the PC". (#121)
- Session menu: the notice that shows the shortcut when a session starts no longer gets stuck on screen; it goes away by itself after a few seconds. (#114)
- Controllers: a controller that sends impossible readings through the generic HID driver (many buttons down at once, a stuck D-pad) is now ignored even when Windows also exposes it through Windows.Gaming.Input. The log says so, and reopening the app tries again. (#115)
- Displays: a monitor turned to portrait (rotated) keeps its rotation in the layout backup, so restoring the desk puts it back in portrait. Before, the rotation was never saved or restored. (#108)
- Playnite: a slow start no longer sends you back to the PC after a few seconds. When Playnite swaps its loading window for the main one, Console Mode now follows the new window and only restores the desk once Playnite actually closes. (#83)
- Audio: if switching the output fails, the session goes on instead of being rolled back after the screens were already switched. (#109)
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ Notas em português do Brasil; a versão em inglês (Estados Unidos) fica em `CH

## [Unreleased]
### Novidades
- Atalhos do controle à sua escolha: o botão que abre o Console Mode, o do menu da sessão e o de voltar ao PC agora são configuráveis em Ajustes (basta segurar os botões que você quer e soltar no mesmo controle). A captura não mistura botões de controles diferentes. Nenhum vem ligado: na primeira abertura desta versão o app mostra a configuração, com uma sugestão para cada ação (Home, Select + Y e Start + Select). Um mesmo botão não pode servir para duas ações. Quem usava o botão Home ou Start + Select precisa escolher de novo. (#116)
- Ajustes → "Receber versões de teste (alpha e beta)": qualquer pessoa pode entrar nos testes das próximas versões pelo próprio app. Desligado por padrão; ao ligar, o app avisa que a versão pode ter bugs.

### Correções
- Steam: ao voltar ao PC (botão, menu, atalho, link `stop` ou saindo do Big Picture) o Big Picture é sempre fechado e a Steam é chamada para fechar do jeito normal, o "Sair" dela, sem encerrar o processo à força. Antes ela ficava aberta e costumava ficar bugada. Não fecha a Steam se houver um jogo rodando, e dá para desligar em Ajustes → "Fechar a Steam ao voltar ao PC". (#121)
- Menu da sessão: o aviso que mostra o atalho no início da sessão não fica mais preso na tela; ele some sozinho depois de alguns segundos. (#114)
- Controles: um controle que manda leituras impossíveis pelo driver HID genérico (muitos botões apertados ao mesmo tempo, direcional preso) agora é ignorado também quando o Windows o expõe por Windows.Gaming.Input. O log avisa, e reabrir o app tenta de novo. (#115)
- Telas: um monitor girado (em retrato) mantém a rotação no backup do layout, então restaurar a mesa devolve o monitor em retrato. Antes a rotação nunca era salva nem restaurada. (#108)
- Playnite: quando ele demora para abrir, o app não volta mais para o PC depois de alguns segundos. Se o Playnite troca a janela de carregamento pela principal, o Console Mode passa a acompanhar a nova e só restaura a mesa quando o Playnite fecha de verdade. (#83)
- Áudio: se a troca da saída falhar, a sessão continua em vez de ser desfeita depois de as telas já terem sido trocadas. (#109)
Expand Down
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ Windows 10 or 11, plus [Steam](https://store.steampowered.com/) (Big Picture), [
2. Press **Play now**, or use the one-click desktop shortcut. The first time on a new screen, the TV asks "Can you see this screen?" and reverts on its own if nobody answers.
3. Quit Big Picture or Playnite and everything goes back: screens, audio, resolution.

Prefer the couch? With the app in the tray, hold the controller's Home button and it starts from there.
Prefer the couch? With the app in the tray, hold your controller shortcut and it starts from there.

| Controller shortcut | Does |
|---|---|
| Hold **Home** (Xbox / PS) | Enter console mode from the tray |
| Hold **Select + Y** (Create + △) | Menu over the game: volume, resolution, audio, FPS, HDR, exit |
| Hold **Start + Select** (Options + Create) | Back to the PC |
You pick the controller shortcuts yourself: none is on at first, the app asks the first time you open it (and you can change them in Settings). No button can serve two actions.

| Controller shortcut | Does | Suggested |
|---|---|---|
| Hold your **open** shortcut | Enter console mode from the tray | **Home** (Xbox / PS) |
| Hold your **menu** shortcut | Menu over the game: volume, resolution, audio, FPS, HDR, exit | **Select + Y** (Create + △) |
| Hold your **back to the PC** shortcut | Back to the PC | **Start + Select** (Options + Create) |

## Features

Expand Down
14 changes: 8 additions & 6 deletions README.pt-BR.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ Windows 10 ou 11, com [Steam](https://store.steampowered.com/) (Big Picture), [P
2. Clique em **Jogar agora**, ou use o atalho de 1 clique na Área de Trabalho. Na primeira vez numa tela nova, a TV pergunta "Está vendo esta tela?" e desfaz tudo sozinha se ninguém responder.
3. Saia do Big Picture ou do Playnite e tudo volta: telas, áudio, resolução.

Prefere o sofá? Com o app na bandeja, segure o botão Home do controle e ele começa dali.
Prefere o sofá? Com o app na bandeja, segure o seu atalho do controle e ele começa dali.

| Atalho no controle | Faz |
|---|---|
| Segurar **Home** (Xbox / PS) | Entra no modo console a partir da bandeja |
| Segurar **Select + Y** (Create + △) | Menu sobre o jogo: volume, resolução, áudio, FPS, HDR, sair |
| Segurar **Start + Select** (Options + Create) | Volta para o PC |
Os atalhos do controle são você quem escolhe: nenhum vem ligado, o app pergunta na primeira abertura (e dá para mudar em Ajustes). Nenhum botão pode servir para duas ações.

| Atalho no controle | Faz | Sugestão |
|---|---|---|
| Segurar o seu atalho de **abrir** | Entra no modo console a partir da bandeja | **Home** (Xbox / PS) |
| Segurar o seu atalho do **menu** | Menu sobre o jogo: volume, resolução, áudio, FPS, HDR, sair | **Select + Y** (Create + △) |
| Segurar o seu atalho de **voltar ao PC** | Volta para o PC | **Start + Select** (Options + Create) |

## Recursos

Expand Down
14 changes: 12 additions & 2 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@ Tudo aqui precisa de hardware ou de olho humano e **não é coberto pelos testes

Como testar em build de desenvolvimento: `dotnet build src/ConsoleMode -c Debug -r win-x64` e rode `src/ConsoleMode/bin/Debug/net8.0-windows10.0.19041.0/win-x64/ConsoleMode.exe`. Os dados ficam em `ConsoleMode_Data` ao lado do exe (`config.json`, `consolemode.log`).

## 1. Botão Home do controle (PR #27)
## 0. Atalhos do controle configuráveis e primeira configuração

Pré-condições: controle **Xbox** (XInput) conectado; app na bandeja (minimizado ou aberto com `--tray`); sessão inativa.

- [ ] **Segurar o botão Xbox por 1 s** entra no modo console (mesmo caminho do atalho "1 Click"). Resultado: ______
- [ ] Ao configurar um atalho, deixe um controle segurando um botão e use outro para montar o atalho; a captura aceita o segundo sem esperar o primeiro soltar. Resultado: ______
- [ ] Com dois controles conectados, pressione partes diferentes do atalho em cada um, inclusive soltando um antes de pressionar o outro; a captura não combina os controles e só aceita a combinação feita em um único controle. Resultado: ______
- [ ] Durante a captura, desconecte o controle depois de pressionar parte da combinação e continue no segundo controle; o primeiro encerra sua captura e os botões do segundo não são acrescentados a ela. Resultado: ______
## 1. Atalho Home do controle (PR #27)

Pré-condições: atalho de "Abrir o Console Mode" definido (o padrão sugerido é o botão Xbox, controle **Xbox** (XInput)); app na bandeja (minimizado ou aberto com `--tray`); sessão inativa.

- [ ] **Segurar o atalho por 1 s** entra no modo console (mesmo caminho do atalho "1 Click"). Resultado: ______
- [ ] **Toque curto** no botão Xbox não faz nada no app (só a Game Bar abre, se o atalho dela estiver ligado). Resultado: ______
- [ ] Com a sessão **ativa** (Big Picture aberto), segurar o botão Xbox **não** reinicia nada. Resultado: ______
- [ ] **Segurar Start + Select por 1 s** durante a sessão restaura a mesa (igual a "Restaurar agora"). Resultado: ______
Expand All @@ -35,7 +43,7 @@ Pré-condições: Ajustes → "Entrar ao conectar um controle" **ligado**; app n
- [ ] O log mostra `Controle conectado: <nome>; auto-start sim/não` a cada conexão. Resultado: ______
- [ ] Com a opção **desligada** (padrão), nada disso acontece. Resultado: ______

## 1c. Menu da sessão (Select + Y)
## 1c. Menu da sessão (atalho sugerido: Select + Y)

Pré-condições: sessão ativa com Big Picture (ou jogo borderless) na tela; controle Xbox ou PlayStation.

Expand Down Expand Up @@ -105,6 +113,8 @@ Pré-condições: Ajustes → Interface = **Automático** (padrão).
- [ ] Com o **Steam aberto** e suporte a PlayStation ligado: se a leitura fica vazia, fechar o Steam faz voltar (é o Steam Input capturando o controle). Resultado: ______
- [ ] "Copiar diagnóstico" cola dispositivos + amostra; o `consolemode.log` tem a linha `Controles: …` de abertura com o DualSense listado. Resultado: ______
- [ ] Desconectar e reconectar durante o teste: a lista atualiza e a leitura continua. Resultado: ______
- [ ] Um controle HID que manda leitura impossível (ex.: Switch Pro pelo driver genérico, com 6 ou mais botões "apertados" sozinhos) é ignorado mesmo quando também aparece em Windows.Gaming.Input: o app não navega nem inicia a sessão sozinho, e o log mostra `Controles: … ignorado: leitura inválida`. Reabrir o app tenta de novo. Resultado: ______
- [ ] Com um controle HID defeituoso e outro controle normal conectados, o defeituoso não gera ações pelo HID nem pela projeção Windows.Gaming.Input, e o controle normal continua navegando. Resultado: ______

## 3c. Fechar a Steam ao voltar ao PC

Expand Down
59 changes: 45 additions & 14 deletions src/ConsoleMode/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,28 +153,28 @@ private async Task HandleStartRequestAsync()
}

/// <summary>
/// Idle: the Home button enters console mode (a hold, or a press once Game Bar's shortcut
/// is off). In a session Big Picture owns the Home button, so Start + Back held together
/// restores the desk instead.
/// The shortcuts the user picked (Settings, or the first-run setup); none is set until then.
/// Home, from the tray: enter console mode (a hold, or a press once Game Bar's shortcut is
/// off). Menu and Exit, in a session: the menu over the game, and back to the PC.
/// </summary>
private void WatchGuideButton()
{
if (_window is null || ViewModel is null) return;
_guide = new ControllerHoldWatcher(_window.DispatcherQueue, ControllerHoldWatcher.GuideButton, "botão Home");
_guide = new ControllerHoldWatcher(_window.DispatcherQueue, 0, "atalho Home");
_guide.Held += () =>
{
if (ViewModel is not null) ViewModel.LaunchedByController = true;
_ = HandleStartRequestAsync();
};
_exitChord = new ControllerHoldWatcher(_window.DispatcherQueue, (ushort)(ControllerHoldWatcher.StartButton | ControllerHoldWatcher.BackButton), "Start + Back");
_exitChord = new ControllerHoldWatcher(_window.DispatcherQueue, 0, "atalho Voltar ao PC");
_exitChord.Held += () => { if (ViewModel?.IsConsoleActive == true) _ = ViewModel.RestoreNowAsync(); };
// Select + Y: the in-session menu (volume, resolution, audio, FPS, HDR, leave).
_menuChord = new ControllerHoldWatcher(_window.DispatcherQueue, (ushort)(ControllerHoldWatcher.BackButton | ControllerHoldWatcher.YButton), "Select + Y")
_menuChord = new ControllerHoldWatcher(_window.DispatcherQueue, 0, "atalho Menu da sessão")
{
HoldDuration = TimeSpan.FromMilliseconds(250)
};
_menuChord.Held += () => ViewModel?.ToggleSessionMenu();
ViewModel.PropertyChanged += OnViewModelChangedForGuide;
ViewModel.ShortcutOnboardingRequested += () => _ = ShowShortcutOnboardingAsync();
RefreshGuideWatch();

// Issue #29: a pad connecting while we're in the tray means "I'm on the couch".
Expand All @@ -192,7 +192,7 @@ private void WatchGuideButton()

private void OnViewModelChangedForGuide(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName is nameof(MainViewModel.HomeButtonLaunch) or nameof(MainViewModel.HomeButtonShortPress)
if (e.PropertyName is MainViewModel.ShortcutsProperty or nameof(MainViewModel.HomeButtonShortPress)
or nameof(MainViewModel.IsConsoleActive))
RefreshGuideWatch();
if (e.PropertyName == nameof(MainViewModel.IsConsoleActive) && ViewModel?.IsConsoleActive == false)
Expand All @@ -202,12 +202,43 @@ or nameof(MainViewModel.IsConsoleActive))
private void RefreshGuideWatch()
{
if (_guide is null || _exitChord is null || _menuChord is null || ViewModel is null) return;
_guide.Mask = ViewModel.GetShortcut(ShortcutSlot.Home);
_menuChord.Mask = ViewModel.GetShortcut(ShortcutSlot.Menu);
_exitChord.Mask = ViewModel.GetShortcut(ShortcutSlot.Exit);
_guide.HoldDuration = ViewModel.HomeButtonShortPress ? TimeSpan.Zero : ControllerHoldWatcher.LongHold;
// The session chords (Select + Y, Start + Back) do not depend on the Home button option:
// turning that off used to leave them dead too.
if (ViewModel.IsConsoleActive) { _exitChord.Start(); _menuChord.Start(); }
else { _exitChord.Stop(); _menuChord.Stop(); }
if (ViewModel.HomeButtonLaunch && !ViewModel.IsConsoleActive) _guide.Start();
else _guide.Stop();

// Picking a shortcut presses buttons on purpose: nothing may fire meanwhile. A shortcut
// that isn't set never runs, and the three do not depend on each other.
var idle = !ViewModel.IsCapturingShortcut;
Apply(_guide, idle && _guide.Mask != 0 && !ViewModel.IsConsoleActive);
Apply(_menuChord, idle && _menuChord.Mask != 0 && ViewModel.IsConsoleActive);
Apply(_exitChord, idle && _exitChord.Mask != 0 && ViewModel.IsConsoleActive);
}

private bool _onboardingOpen;

/// <summary>The first-run shortcuts setup (nothing is set until the user chooses).</summary>
private async Task ShowShortcutOnboardingAsync()
{
if (_onboardingOpen || _window?.Content?.XamlRoot is not { } root || ViewModel is null) return;
_onboardingOpen = true;
try
{
await Controls.ShortcutPicker.ShowOnboardingAsync(root, ViewModel);
}
catch (Exception ex)
{
AppLog.Write($"Atalhos: {ex.Message}");
}
finally
{
_onboardingOpen = false;
}
}

private static void Apply(ControllerHoldWatcher watcher, bool run)
{
if (run) watcher.Start();
else watcher.Stop();
}
}
Loading
Loading