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
6 changes: 1 addition & 5 deletions .github/workflows/switch-release.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
name: Switch build and release

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

permissions:
Expand Down Expand Up @@ -82,7 +78,7 @@ jobs:

release:
name: Publish GitHub release
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main'
needs:
- version
- build
Expand Down
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,11 @@ On Linux or in CI, use the equivalent shell entry point:
bash scripts/package-release.sh 0.0.6
```

After a change reaches `main`, the Blacksmith release workflow increments the
patch version from the latest GitHub release, embeds it in the app and NRO
metadata, publishes both files, and generates notes from the changes since the
previous release. Pull requests run the same build without publishing.
Run **Actions → Switch build and release → Run workflow** to build manually.
On `main`, the Blacksmith workflow increments the patch version from the latest
GitHub release, embeds it in the app and NRO metadata, publishes both files, and
generates release notes. Selecting another branch builds downloadable artifacts
without publishing a release. Pushes and pull requests do not trigger builds.

## Repository layout

Expand Down Expand Up @@ -164,6 +165,13 @@ Tests that exercise implementation files must compile those `.cpp` files and
link their host dependencies. Host checks do not replace a devkitA64 Nintendo
Switch build.

Run the streaming, input, and network regression suite on Linux with
`bash scripts/test-streaming-host.sh`. It requires a C/C++20 compiler and the
Jansson, libcurl, libsrtp2, libavcodec, and libavutil development packages. The suite includes
packet-loss and malformed-frame cases, decoded-frame selection, audio service
mocks, keyboard shortcuts, and bitrate persistence/negotiation; it does not
measure real Switch frame times or server bitrate adaptation.

## License

OpenNOW Switch is licensed under the [MIT License](LICENSE).
Expand Down
43 changes: 14 additions & 29 deletions app/src/StreamView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ StreamView::StreamView(
media_port,
ice_servers);
session_->start();
network_monitor_.start();
RefreshNetworkInfo(stream_started_at_);

// Suggest the view to take all available space
Expand All @@ -81,6 +82,7 @@ StreamView::StreamView(
}

StreamView::~StreamView() {
network_monitor_.request_stop();
opennow::SetSensitiveInputLoggingSuppressed(false);
std::fill(nte_credentials_.password.begin(), nte_credentials_.password.end(), '\0');
std::fill(nte_text_buffer_.begin(), nte_text_buffer_.end(), '\0');
Expand All @@ -107,6 +109,7 @@ void StreamView::onFocusGained() {
}

void StreamView::onFocusLost() {
HideInlineKeyboard(false);
brls::Box::onFocusLost();
brls::Application::getPlatform()->disableScreenDimming(false);
brls::Application::getPlatform()->getInputManager()->setPointerLock(false);
Expand All @@ -116,6 +119,7 @@ void StreamView::ExitStream() {
if (exit_requested_)
return;
exit_requested_ = true;
network_monitor_.request_stop();
if (session_)
session_->request_stop();
StopCloudSessionAsync();
Expand Down Expand Up @@ -229,44 +233,23 @@ void StreamView::draw(NVGcontext* vg, float x, float y, float width, float heigh
keyboard_combo_was_down_ = keyboard_combo;

if (keyboard_visible_) {
const bool b_down = state.buttons[brls::BUTTON_B];
const bool plus_down = state.buttons[brls::BUTTON_START];
const bool escape_chord = minus_down && state.buttons[brls::BUTTON_LT];
const bool tab_chord = minus_down && state.buttons[brls::BUTTON_LB];
const bool alt_tab_chord = minus_down && state.buttons[brls::BUTTON_RB];
const bool windows_chord = minus_down && state.buttons[brls::BUTTON_RT];
if (escape_chord && !keyboard_escape_chord_was_down_)
SendKeyboardShortcut(opennow::input::KeyboardShortcut::Escape);
else if (tab_chord && !keyboard_tab_chord_was_down_)
SendKeyboardShortcut(opennow::input::KeyboardShortcut::Tab);
else if (alt_tab_chord && !keyboard_alt_tab_chord_was_down_)
SendKeyboardShortcut(opennow::input::KeyboardShortcut::AltTab);
else if (windows_chord && !keyboard_windows_chord_was_down_)
SendKeyboardShortcut(opennow::input::KeyboardShortcut::Windows);
keyboard_escape_chord_was_down_ = escape_chord;
keyboard_tab_chord_was_down_ = tab_chord;
keyboard_alt_tab_chord_was_down_ = alt_tab_chord;
keyboard_windows_chord_was_down_ = windows_chord;
if (plus_down && !keyboard_plus_was_down_)
HideInlineKeyboard(true);
else if (b_down && !keyboard_b_was_down_)
HideInlineKeyboard(false);
keyboard_b_was_down_ = b_down;
keyboard_plus_was_down_ = plus_down;
HandleInlineKeyboardInput(state, x, y, width);
keyboard_owned_input = true;
} else {
keyboard_b_was_down_ = false;
keyboard_plus_was_down_ = false;
keyboard_escape_chord_was_down_ = false;
keyboard_tab_chord_was_down_ = false;
keyboard_alt_tab_chord_was_down_ = false;
keyboard_windows_chord_was_down_ = false;
keyboard_shortcut_latched_ = false;
keyboard_touch_was_down_ = false;
}

if (!state.buttons[brls::BUTTON_B])
suppress_b_until_release_ = false;

if (keyboard_release_guard_) {
std::vector<brls::RawTouchState> touches;
brls::Application::getPlatform()->getInputManager()->updateTouchStates(&touches);
const bool touch_active = std::any_of(touches.begin(), touches.end(),
[](const brls::RawTouchState& touch) { return touch.pressed; });
const bool controller_active =
state.buttons[brls::BUTTON_A] || state.buttons[brls::BUTTON_B] ||
state.buttons[brls::BUTTON_X] || state.buttons[brls::BUTTON_Y] ||
Expand All @@ -279,7 +262,7 @@ void StreamView::draw(NVGcontext* vg, float x, float y, float width, float heigh
std::fabs(state.axes[brls::LEFT_Y]) > 0.15f ||
std::fabs(state.axes[brls::RIGHT_X]) > 0.15f ||
std::fabs(state.axes[brls::RIGHT_Y]) > 0.15f;
if (controller_active)
if (controller_active || touch_active)
keyboard_owned_input = true;
else
keyboard_release_guard_ = false;
Expand Down Expand Up @@ -405,6 +388,8 @@ void StreamView::draw(NVGcontext* vg, float x, float y, float width, float heigh
DrawNetworkWarning(vg, x, y, width, height, notice_now);
if (stream_end_reason_ == opennow::StreamEndReason::None)
DrawControllerNotice(vg, x, y, width, notice_now);
if (stream_end_reason_ == opennow::StreamEndReason::None)
DrawKeyboardShortcuts(vg, x, y, width);

brls::Box::draw(vg, x, y, width, height, style, ctx);
}
Expand Down
15 changes: 8 additions & 7 deletions app/src/StreamView.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "gfn_client.hpp"
#include "controller_delivery_policy.hpp"
#include "keyboard_input_policy.hpp"
#include "network_utils.hpp"
#include "network_monitor.hpp"
#include "nte_credentials.hpp"
#include "stream_end_policy.hpp"
#include "stream_overlay_policy.hpp"
Expand Down Expand Up @@ -87,7 +87,11 @@ class StreamView : public brls::Box {
void OpenInlineKeyboard();
void HideInlineKeyboard(bool send_enter);
void UpdateInlineKeyboard();
void HandleInlineKeyboardInput(const brls::ControllerState& state,
float x, float y, float width);
void DrawKeyboardShortcuts(NVGcontext* vg, float x, float y, float width);
void HandleKeyboardText(const char* text);
void SendKeyboardStroke(opennow::input::KeyboardStroke stroke);
void SendKeyboardCharacter(char character);
void SendKeyboardShortcut(opennow::input::KeyboardShortcut shortcut);
void StartNteAutoLogin(std::chrono::steady_clock::time_point now);
Expand Down Expand Up @@ -181,10 +185,8 @@ class StreamView : public brls::Box {
bool keyboard_combo_was_down_ = false;
bool keyboard_b_was_down_ = false;
bool keyboard_plus_was_down_ = false;
bool keyboard_escape_chord_was_down_ = false;
bool keyboard_tab_chord_was_down_ = false;
bool keyboard_alt_tab_chord_was_down_ = false;
bool keyboard_windows_chord_was_down_ = false;
bool keyboard_shortcut_latched_ = false;
bool keyboard_touch_was_down_ = false;
bool suppress_b_until_release_ = false;
bool keyboard_release_guard_ = false;
bool keyboard_visible_ = false;
Expand Down Expand Up @@ -225,8 +227,7 @@ class StreamView : public brls::Box {
opennow::StreamEndReason stream_end_reason_ = opennow::StreamEndReason::None;
std::chrono::steady_clock::time_point stream_end_started_at_ {};
std::chrono::steady_clock::time_point stream_auto_exit_at_ {};
std::chrono::steady_clock::time_point last_network_check_at_ {};
std::chrono::steady_clock::time_point last_network_info_check_at_ {};
opennow::NetworkMonitor network_monitor_;
std::chrono::steady_clock::time_point network_warning_visible_until_ {};
opennow::NetworkConnectionInfo network_info_ {};
bool internet_connected_ = true;
Expand Down
Loading