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
1 change: 1 addition & 0 deletions CHANGELOG.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ English (US) release notes, mirroring CHANGELOG.md (Brazilian Portuguese). Befor
- 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)
- 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)
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Notas em português do Brasil; a versão em inglês (Estados Unidos) fica em `CH
- 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)
- 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)
Expand Down
15 changes: 15 additions & 0 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ Pré-condições: Ajustes → Interface = **Automático** (padrão).
- [ ] "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: ______

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

Pré-condições: lançador = Steam Big Picture; Ajustes → "Fechar a Steam ao voltar ao PC" **ligado** (padrão); nenhum jogo aberto.

- [ ] Voltar ao PC por cada caminho (botão "Voltar ao PC", menu da sessão, atalho do controle, `consolemode://stop`, tray → Restaurar): o **Big Picture fecha** e some da mesa, e a **Steam fecha sozinha** em alguns segundos (o ícone sai da bandeja, sem janela de erro). Resultado: ______
- [ ] Sair pelo próprio Big Picture (Sair → Sair do Big Picture): a mesa volta e a Steam também fecha. Resultado: ______
- [ ] A Steam **não é encerrada à força**: no Gerenciador de Tarefas ela some sem o aviso "o programa não está respondendo", e o log mostra `Steam: pedindo para fechar (sair normal)`. Resultado: ______
- [ ] Com um **jogo da Steam rodando**: a Steam fica aberta e o log mostra `Steam: mantida aberta (jogo em execução, id …)`; o Big Picture ainda é fechado. Resultado: ______
- [ ] Com o ajuste **desligado**: o Big Picture fecha, a Steam fica aberta (`Steam: mantida aberta (desligado nos ajustes)`). Resultado: ______
- [ ] Desligar o ajuste, fechar e reabrir o Console Mode e iniciar uma sessão Big Picture: a preferência continua desligada e a Steam fica aberta. Resultado: ______
- [ ] Com o Steam aberto e o estado do jogo indisponível no Registro (valor `RunningAppID` ausente ou ilegível): a Steam fica aberta e o log informa que não foi possível verificar se há jogo em execução. Resultado: ______
- [ ] Steam **já fechada** antes de voltar: nada é aberto nem pedido (`Steam: não estava aberta`); a Steam não é iniciada de novo por engano. Resultado: ______
- [ ] Lançador Playnite ou Modo Xbox: a Steam não é tocada. Resultado: ______
- [ ] Se o Big Picture demorar a fechar (travado), a restauração da mesa continua e termina normalmente (o log mostra `Big Picture ainda aberto`). Resultado: ______

## 4. Regressões

- [ ] Interface Desktop: mapa de telas, `Segmented`, chips, tour de 3 passos e Ajustes continuam como antes. Resultado: ______
Expand Down
7 changes: 7 additions & 0 deletions src/ConsoleMode/Models/Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ public sealed class AppConfig
/// <summary>A short press of the Guide button is enough (Game Bar's own shortcut is turned off).</summary>
public bool HomeButtonShortPress { get; set; }

/// <summary>
/// Going back to the PC also quits Steam (its own Exit, never a kill), unless a game is running.
/// Big Picture itself is always closed.
/// </summary>
public bool CloseSteamOnRestore { get; set; } = true;

/// <summary>Enter console mode when a controller connects while the app is in the tray.</summary>
public bool AutoStartOnController { get; set; }

Expand Down Expand Up @@ -248,6 +254,7 @@ public sealed class ConsoleRuntimeState
public List<string> HideMonitors { get; set; } = [];
public string HideStrategy { get; set; } = "disconnect";
public string FullscreenMode { get; set; } = "bigPicture";
public bool CloseSteamOnRestore { get; set; } = true;
public string? AudioDeviceId { get; set; }
public bool AudioAutoSwitch { get; set; }
public string? AudioDeviceHint { get; set; }
Expand Down
5 changes: 5 additions & 0 deletions src/ConsoleMode/Native/NativeWindows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ public static bool ForceForeground(nint hwnd)
[DllImport("user32.dll")]
public static extern bool IsWindow(nint hWnd);

[DllImport("user32.dll")]
public static extern bool PostMessage(nint hWnd, uint msg, nint wParam, nint lParam);

public const uint WmClose = 0x0010;

[DllImport("user32.dll")]
public static extern nint SetWinEventHook(uint eventMin, uint eventMax, nint hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);

Expand Down
2 changes: 2 additions & 0 deletions src/ConsoleMode/Resources/Strings.en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
"ChooseRoleFor": "What does the {0} screen do?",
"HomeButtonCard": "Open with the controller Home button",
"HomeButtonDescription": "With the app in the tray, hold the Xbox button for 1 second to enter console mode. During a session, hold Start + Select for 1 second to go back to the PC. Xbox (XInput) controllers only.",
"CloseSteamCard": "Close Steam when going back to the PC",
"CloseSteamDescription": "When you leave Big Picture or go back to the PC, Steam is asked to quit the normal way (its own Exit, never forced). Big Picture is always closed. Steam stays open if a game is running.",
"AutoStartControllerCard": "Enter when a controller connects",
"AutoStartControllerDescription": "With the app in the tray, turning on or plugging in a controller enters console mode. Ignores the first 15 s after opening and 30 s after restoring.",
"HomeButtonShortPressCard": "Short press of the Xbox button",
Expand Down
2 changes: 2 additions & 0 deletions src/ConsoleMode/Resources/Strings.es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
"ChooseRoleFor": "¿Qué hace la pantalla {0}?",
"HomeButtonCard": "Abrir con el botón Home del mando",
"HomeButtonDescription": "Con la aplicación en la bandeja, mantén el botón Xbox 1 segundo para entrar en modo consola. Durante la sesión, mantén Start + Select 1 segundo para volver al PC. Solo mandos Xbox (XInput).",
"CloseSteamCard": "Cerrar Steam al volver al PC",
"CloseSteamDescription": "Al salir de Big Picture o volver al PC, se le pide a Steam que se cierre de la forma normal (su propio «Salir», nunca a la fuerza). Big Picture siempre se cierra. Steam sigue abierta si hay un juego en ejecución.",
"AutoStartControllerCard": "Entrar al conectar un mando",
"AutoStartControllerDescription": "Con la aplicación en la bandeja, encender o conectar un mando entra en modo consola. Ignora los primeros 15 s tras abrir y 30 s tras restaurar.",
"HomeButtonShortPressCard": "Pulsación corta del botón Xbox",
Expand Down
2 changes: 2 additions & 0 deletions src/ConsoleMode/Resources/Strings.pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
"ChooseRoleFor": "O que a tela {0} faz?",
"HomeButtonCard": "Abrir com o botão Home do controle",
"HomeButtonDescription": "Com o app na bandeja, segure o botão Xbox por 1 segundo para entrar no modo console. Durante a sessão, segure Start + Select por 1 segundo para voltar ao PC. Só controles Xbox (XInput).",
"CloseSteamCard": "Fechar a Steam ao voltar ao PC",
"CloseSteamDescription": "Ao sair do Big Picture ou voltar ao PC, a Steam é chamada para fechar do jeito normal (o \"Sair\" dela, nunca à força). O Big Picture sempre é fechado. A Steam fica aberta se houver um jogo rodando.",
"AutoStartControllerCard": "Entrar ao conectar um controle",
"AutoStartControllerDescription": "Com o app na bandeja, ligar ou conectar um controle entra no modo console. Ignora os primeiros 15 s após abrir e 30 s após restaurar.",
"HomeButtonShortPressCard": "Toque curto no botão Xbox",
Expand Down
32 changes: 32 additions & 0 deletions src/ConsoleMode/Services/ConsoleEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public void Start(AppConfig config, MonitorInfo? focusInfo, Func<ScreenRect?, bo
State.HideMonitors = [.. config.HideMonitors];
State.HideStrategy = config.HideStrategy;
State.FullscreenMode = config.FullscreenMode;
State.CloseSteamOnRestore = config.CloseSteamOnRestore;
Comment thread
pullfrog[bot] marked this conversation as resolved.
State.AudioDeviceId = config.AudioAutoSwitch ? null : config.AudioDeviceId;
State.AudioAutoSwitch = config.AudioAutoSwitch;
State.AudioDeviceHint = config.AudioDeviceName;
Expand Down Expand Up @@ -281,6 +282,7 @@ public void Stop()
AppLog.Write("Stop-ConsoleMode: iniciando restauração");
try
{
CloseSteamForRestore();
OnUi(BlackCurtain.Close);
Video.RestoreHdr(State);
Video.RestoreVrr(State);
Expand Down Expand Up @@ -337,6 +339,29 @@ public void Stop()
}
}

/// <summary>
/// Going back to the PC: Big Picture is closed (it would otherwise be left on the desk monitor when the
/// screens come back), and Steam is asked to quit its own way, because after Big Picture it is often left
/// half-working. Never a kill, never when a game is running, and never a reason to fail the restore.
/// </summary>
private void CloseSteamForRestore()
{
if (State.FullscreenMode != "bigPicture") return;
try
{
var closed = Launch.CloseBigPicture(State);
AppLog.Write($"Steam: Big Picture {(closed ? "fechado" : "ainda aberto")}");
var appId = SteamSession.RunningAppId();
var decision = SteamShutdown.Decide(State.FullscreenMode, State.CloseSteamOnRestore, SteamSession.IsRunning(), appId);
AppLog.Write(SteamShutdown.Describe(decision, appId));
if (decision == SteamShutdown.Decision.Shutdown) SteamSession.RequestShutdown();
}
catch (Exception ex)
{
AppLog.Write($"Steam: fechar ao voltar: {ex.Message}");
}
}

public void RequestExit() => State.ShouldExit = true;

private void ShowCurtains(IReadOnlyList<string> names)
Expand Down Expand Up @@ -372,21 +397,28 @@ private AppConfig ResolveMonitorNames(AppConfig config)
return new AppConfig
{
Version = config.Version,
AppLanguage = config.AppLanguage,
FocusMonitor = focus,
HideMonitors = hide,
HideStrategy = config.HideStrategy,
FullscreenMode = config.FullscreenMode,
PlaynitePath = config.PlaynitePath,
AudioDeviceId = config.AudioDeviceId,
AudioDeviceName = config.AudioDeviceName,
AudioAutoSwitch = config.AudioAutoSwitch,
FpsLimit = config.FpsLimit,
MonitorModes = modes,
HdrEnable = config.HdrEnable,
VrrEnable = config.VrrEnable,
UiMode = config.UiMode,
TourDone = config.TourDone,
ConfirmedSetup = config.ConfirmedSetup,
CheckUpdates = config.CheckUpdates,
BetaUpdates = config.BetaUpdates,
HomeButtonLaunch = config.HomeButtonLaunch,
HomeButtonShortPress = config.HomeButtonShortPress,
CloseSteamOnRestore = config.CloseSteamOnRestore,
AutoStartOnController = config.AutoStartOnController,
SkippedUpdateVersion = config.SkippedUpdateVersion
};
}
Expand Down
28 changes: 28 additions & 0 deletions src/ConsoleMode/Services/LaunchService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,34 @@ public bool IsPlayniteActive(ConsoleRuntimeState state)
return false;
}

/// <summary>
/// Leaves Big Picture the clean way, through Steam's own URL (back to the desktop client), so it does not
/// stay open on the desk once the screens are back. Halfway through the wait, if it is still there, the
/// window is asked to close (WM_CLOSE: the X button, not a kill). Returns whether it is gone.
/// </summary>
public bool CloseBigPicture(ConsoleRuntimeState state, int timeoutMs = 6000)
{
if (!IsBigPictureActive(state)) return true;
var handles = GetBigPictureHandles();
try { ProcessRunner.StartDetached("steam://close/bigpicture"); }
catch (Exception ex) { AppLog.Write($"Fechar Big Picture: {ex.Message}"); }

var deadline = Environment.TickCount64 + timeoutMs;
var nudged = false;
while (Environment.TickCount64 < deadline)
{
Thread.Sleep(400);
if (!IsBigPictureActive(state)) return true;
if (!nudged && Environment.TickCount64 > deadline - timeoutMs / 2)
{
nudged = true;
foreach (var handle in handles.Concat(GetBigPictureHandles()).Distinct())
NativeWindows.PostMessage(handle, NativeWindows.WmClose, 0, 0);
}
}
return !IsBigPictureActive(state);
}

public bool IsBigPictureActive(ConsoleRuntimeState state)
{
if (state.CachedBigPictureHandle != 0)
Expand Down
2 changes: 2 additions & 0 deletions src/ConsoleMode/Services/LocalizationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ internal void NotifyAll()
public string HomeButtonCard => LocalizationService.Get(nameof(HomeButtonCard));
public string HomeButtonDescription => LocalizationService.Get(nameof(HomeButtonDescription));
public string HomeButtonShortPressCard => LocalizationService.Get(nameof(HomeButtonShortPressCard));
public string CloseSteamCard => LocalizationService.Get(nameof(CloseSteamCard));
public string CloseSteamDescription => LocalizationService.Get(nameof(CloseSteamDescription));
public string AutoStartControllerCard => LocalizationService.Get(nameof(AutoStartControllerCard));
public string AutoStartControllerDescription => LocalizationService.Get(nameof(AutoStartControllerDescription));
public string NotifyUpdatesCard => LocalizationService.Get(nameof(NotifyUpdatesCard));
Expand Down
78 changes: 78 additions & 0 deletions src/ConsoleMode/Services/SteamSession.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Diagnostics;
using Microsoft.Win32;

namespace ConsoleMode.Services;

/// <summary>
/// Talks to the Steam client about the end of a session: is it running, is a game running, and the
/// request to quit. Quitting is Steam's own "-shutdown" (what its Exit menu does): it is asked to close,
/// never killed, so it can save and leave cleanly.
/// </summary>
public static class SteamSession
{
private const string SteamKey = @"Software\Valve\Steam";

public static bool IsRunning()
{
var processes = Process.GetProcessesByName("steam");
try { return processes.Length > 0; }
finally { foreach (var p in processes) p.Dispose(); }
}

/// <summary>The id of the game Steam is running, 0 if none, or null if the state is unknown.</summary>
public static int? RunningAppId()
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(SteamKey);
return SteamShutdown.ParseRunningAppId(key?.GetValue("RunningAppID"));
}
catch (Exception ex)
{
AppLog.Write($"Steam: RunningAppID: {ex.Message}");
return null;
}
}

public static string? FindSteamExe()
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(SteamKey);
var exe = key?.GetValue("SteamExe") as string;
if (!string.IsNullOrWhiteSpace(exe) && File.Exists(exe)) return Path.GetFullPath(exe);
var folder = key?.GetValue("SteamPath") as string;
if (!string.IsNullOrWhiteSpace(folder))
{
var candidate = Path.Combine(folder, "steam.exe");
if (File.Exists(candidate)) return Path.GetFullPath(candidate);
}
}
catch (Exception ex)
{
AppLog.Write($"Steam: caminho: {ex.Message}");
}
return null;
}

/// <summary>Asks Steam to exit the normal way. Returns whether the request was sent; it does not wait.</summary>
public static bool RequestShutdown()
{
var exe = FindSteamExe();
if (exe is null)
{
AppLog.Write("Steam: não achei o steam.exe para fechar");
return false;
}
try
{
Process.Start(new ProcessStartInfo { FileName = exe, Arguments = "-shutdown", UseShellExecute = false, CreateNoWindow = true })?.Dispose();
return true;
}
catch (Exception ex)
{
AppLog.Write($"Steam: pedido de fechar falhou: {ex.Message}");
return false;
}
}
}
Loading
Loading