Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6a9ad78
Test build: pin moonlight-common-c-mic to the per-stream depacketizer
vibesoftwarecoder Nov 4, 2026
aa7fc81
Test build: pin moonlight-common-c-mic to the second-stream connectio…
vibesoftwarecoder Nov 4, 2026
0f8bc69
Test build: pin moonlight-common-c-mic to the stream-scoped control a…
vibesoftwarecoder Nov 4, 2026
c66d430
Add a second window for a second video stream
vibesoftwarecoder Nov 4, 2026
2764c91
Second window: log the request and its outcome at warning level
vibesoftwarecoder Nov 4, 2026
9e0a346
Second window: deliver the second stream by push even when the first …
vibesoftwarecoder Nov 4, 2026
388b82e
Point moonlight-common-c at the second-stream callback fix
vibesoftwarecoder Nov 4, 2026
2e99000
Second window: wait for a keyframe before decoding, and log the first…
vibesoftwarecoder Nov 4, 2026
0d4ce52
Second window: pull its own stream instead of sharing the first windo…
vibesoftwarecoder Nov 4, 2026
aa86855
Choose the display of each stream window
vibesoftwarecoder Nov 4, 2026
1fe38d3
Second window: tell the host to stop the stream when the window is cl…
vibesoftwarecoder Nov 4, 2026
e51bfc1
Second window: choose its video size and frame rate, open it at that …
vibesoftwarecoder Nov 4, 2026
8e0e5a3
Second window: F11 toggles borderless fullscreen
vibesoftwarecoder Nov 5, 2026
aff945f
Second window: a Settings field for the host window title, saved
vibesoftwarecoder Nov 5, 2026
e8a55d7
Settings: a Second Window section with an on/off check box
vibesoftwarecoder Nov 5, 2026
e24135b
Second window: open without taking focus from the first window
vibesoftwarecoder Nov 5, 2026
1a76a47
Second window: show a whole display, not only a window
vibesoftwarecoder Nov 5, 2026
57b691c
Second window: its focus events no longer capture the mouse for the f…
vibesoftwarecoder Nov 5, 2026
73b4c0b
Second window: option to open it in full screen
vibesoftwarecoder Nov 5, 2026
d91dbda
Settings: say that a whole display needs the console host
vibesoftwarecoder Nov 5, 2026
46bdf3f
Docs: rewrite the README for the second window, USB forwarding and th…
vibesoftwarecoder Nov 5, 2026
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
120 changes: 83 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# MoonlightVibe

**MoonlightVibe** is the companion Moonlight client for [MultiSeat](https://github.com/vibesoftwarecoder/MultiSeat) — a system that runs multiple simultaneous game streaming sessions on one Windows host.
**MoonlightVibe** is a Moonlight game streaming client. It is the companion client for [MultiSeat](https://github.com/vibesoftwarecoder/MultiSeat), which runs several streaming sessions on one Windows host, and it also works with any Moonlight-compatible host.

It is a fork of [moonlight-stream/moonlight-qt](https://github.com/moonlight-stream/moonlight-qt) with two additions: MultiSeat seat auto-discovery and microphone passthrough support.
It is a fork of [moonlight-stream/moonlight-qt](https://github.com/moonlight-stream/moonlight-qt). This fork adds seat auto-discovery, microphone passthrough, USB forwarding and a second window. All four need [ApolloVibe](https://github.com/vibesoftwarecoder/ApolloVibe) on the host, except seat auto-discovery, which needs MultiSeat.

---

## Where to report a problem

**Anything about the client goes here** — including problems inherited from upstream Moonlight.
**Anything about the client goes here**, including problems inherited from upstream Moonlight.
MoonlightVibe is what you installed, so it is ours to answer for.

| what is wrong | where it goes |
|---|---|
| The client: streaming, decoding, seat discovery, gamepad, microphone passthrough | **here** |
| The client: streaming, decoding, seat discovery, gamepad, microphone, USB forwarding, the second window | **here** |
| Picture, encoder or host audio | [ApolloVibe](https://github.com/vibesoftwarecoder/ApolloVibe/issues) |
| A seat will not provision | [MultiSeat](https://github.com/vibesoftwarecoder/MultiSeat/issues) |

Expand All @@ -24,84 +24,130 @@ not maintain this fork, and cannot help with it.

| Feature | MoonlightVibe | Upstream Moonlight |
|---|---|---|
| MultiSeat auto-discovery | ✅ Seats appear automatically in the computer list | ❌ Manual host entry required |
| Mic passthrough | ✅ Client mic streamed to host via ApolloVibe | ❌ Not available |
| Seat auto-discovery | ✅ MultiSeat seats appear in the computer list by themselves | ❌ Add each host by hand |
| Microphone passthrough | ✅ Your microphone is streamed to the host | ❌ Not available |
| Second window | ✅ A second picture from the same session, in a window of its own: one host window, or a whole display | ❌ Not available |
| USB forwarding | ✅ Share a USB device with the host (Windows, experimental, off by default) | ❌ Not available |
| Branding | MoonlightVibe | Moonlight |

Everything else — hardware video decoding, H.264/HEVC/AV1, HDR, surround sound, gamepad support, touch input — is inherited from upstream Moonlight.
Everything else is inherited from upstream Moonlight: hardware video decoding, H.264, HEVC and AV1, HDR, surround sound, gamepads and touch input.

---

## Download

Get the latest release from the [Releases page](https://github.com/vibesoftwarecoder/MoonlightVibe/releases/latest).

Extract the zip and run `MoonlightVibe.exe` — no installer required, fully portable.
Extract the zip and run `MoonlightVibe.exe`. There is no installer, and it is fully portable.

---

## Code signing policy
## Second window

Free code signing provided by [SignPath.io](https://about.signpath.io), certificate by
[SignPath Foundation](https://signpath.org).
A session can show a second picture of the host in a second client window. It is the same connection and the same pairing, and nothing extra to run on the host. The second picture is one of two things:

- **What is signed:** the two files built from this repository, `MoonlightVibe.exe` and
`AntiHooking.dll`, in the Windows release. They show "SignPath Foundation" as the publisher.
- **What is not:** third-party libraries bundled from other open-source projects, such as FFmpeg,
SDL and OpenSSL, are included as those projects or their packagers provide them. Qt's libraries
carry The Qt Company's own signature.
- **How:** every Windows release is built from this repository by GitHub Actions on GitHub-hosted
runners, and each signing request is approved by hand before it is signed.
- **A window on the host.** You name the window by its title, for example a browser or a chat. Only that window is shown, and it follows the window if it moves.
- **A whole display.** The host makes an extra virtual monitor. Drag windows onto it in the main window and they show up in the second one, so it works like a real second screen.

Team roles:
**To use it:** open **Settings → Second Window**, turn on **Show a second window**, and choose what to show. Then start a stream as usual. The options are:

- Committers and reviewers: [vibesoftwarecoder](https://github.com/vibesoftwarecoder)
- Approvers: [vibesoftwarecoder](https://github.com/vibesoftwarecoder)
- **What to show:** a window, or a whole display. For a window, type its title. A title that matches exactly is used first, then one that contains your text.
- **Resolution and frame rate** of the second picture: 720p to 4K, at 30 or 60 frames per second. A bigger picture is sharper when the window is large, and uses more bandwidth. It can't add detail the host window doesn't have.
- **Show it on this display:** which of your screens the second window opens on. Automatic picks a screen the main window is not on.
- **Open the second window in full screen:** on by default. Press **F11** in the second window to switch between full screen and a window.

Privacy: see [PRIVACY.md](PRIVACY.md). MoonlightVibe sends no data to us. It does contact some
servers run by others, such as GitHub for its update check, and that page lists each one.
Closing the second window stops only the second picture. The main window keeps streaming.

**Command line.** The same options work from the command line:

```
MoonlightVibe.exe stream <host> <app> --second-window "Window title"
MoonlightVibe.exe stream <host> <app> --second-window-source display
```

Also: `--second-window-resolution WxH`, `--second-window-fps N`, `--second-window-display N`, `--second-window-fullscreen` or `--no-second-window-fullscreen`, and `--window-display N` for the main window.

**What it needs:**

- An [ApolloVibe](https://github.com/vibesoftwarecoder/ApolloVibe) host with `second_video_stream = enabled`. A host without it ignores the request, and you get one window.
- **A whole display needs the console host**, the main desktop of the machine. A MultiSeat seat can only show a window.

**Known limits.** The second window follows the mouse by absolute position only, with no relative mouse mode inside it. If the host window is not 16:9, the picture has black bars.

---

## MultiSeat seat auto-discovery

When MultiSeat is running on the local machine, MoonlightVibe automatically discovers all active seats and lists each one as a separate server in the computer list. No manual host entry or port configuration needed — seats appear within ~15 seconds of becoming ready.
When MultiSeat is running on the local machine, MoonlightVibe finds all active seats and lists each one as its own server in the computer list. You don't enter a host or a port. Seats appear within about 15 seconds of becoming ready.

This works over the LAN too: if you point MoonlightVibe at the MultiSeat host IP, it will discover and list all seats on that machine.
This works over the LAN too. Point MoonlightVibe at the MultiSeat host's address, and it lists all the seats on that machine.

---

## Microphone passthrough

MoonlightVibe streams your microphone to the host and plays it back through `Speakers (Steam Streaming Microphone)`. Games and apps on the host should select `Microphone (Steam Streaming Microphone)` as their input device.
MoonlightVibe streams your microphone to the host, which plays it into `Speakers (Steam Streaming Microphone)`. Apps and games on the host should pick `Microphone (Steam Streaming Microphone)` as their input.

It needs [ApolloVibe](https://github.com/vibesoftwarecoder/ApolloVibe) on the host with `stream_mic = enabled`. MultiSeat turns that on for you.

---

## USB forwarding (experimental)

Windows only, and off by default. Open **Settings → USB forwarding** to share a USB device from your PC so it appears on the host you are streaming to. You can share or stop sharing a device, choose to forward it automatically when streaming, and press **Ctrl+Alt+Shift+U** to pause or resume. With the setting off, nothing is created and a stream behaves exactly as before.

Requires [ApolloVibe](https://github.com/vibesoftwarecoder/ApolloVibe) on the host side with `stream_mic = enabled` in the seat config. MultiSeat enables this automatically.
It needs `usbipd-win` on your PC, and an ApolloVibe host with USB forwarding turned on, the `usbip-win2` driver, and the USB permission granted to your client. Sharing a device asks for administrator approval. Try a USB stick or a spare gamepad first, not a keyboard or mouse.

It builds on the work of qiin2333 and AlkaidLab. See the [6.3.11 release notes](docs/release-notes/6.3.11.md) for details.

---

## Sharing diagnostic logs

On Windows and macOS, open **Settings → Advanced Settings** and turn on **Enable detailed logging** before reproducing a problem. Then select **Open Log Folder** and attach the newest `Moonlight-*.log` file to your issue. Warning and error messages are logged even when detailed logging is off. The setting persists until you turn it off.
On Windows and macOS, open **Settings → Advanced Settings** and turn on **Enable detailed logging** before reproducing a problem. Then select **Open Log Folder** and attach the newest `Moonlight-*.log` file to your issue. Warning and error messages are logged even when detailed logging is off. The setting stays on until you turn it off.

Please review a log before sharing it: it may include your device or host names, network addresses, and details of your streaming session. Session encryption keys are removed automatically.
Please read a log before sharing it. It may include your device or host names, network addresses, and details of your streaming session. Session encryption keys are removed automatically.

---

## Requirements
## Code signing policy

Free code signing provided by [SignPath.io](https://about.signpath.io), certificate by
[SignPath Foundation](https://signpath.org).

- **What is signed:** the two files built from this repository, `MoonlightVibe.exe` and
`AntiHooking.dll`, in the Windows release. They show "SignPath Foundation" as the publisher.
- **What is not:** third-party libraries bundled from other open-source projects, such as FFmpeg,
SDL and OpenSSL, are included as those projects or their packagers provide them. Qt's libraries
carry The Qt Company's own signature.
- **How:** every Windows release is built from this repository by GitHub Actions on GitHub-hosted
runners, and each signing request is approved by hand before it is signed.

- Windows 10/11 x64
- [MultiSeat](https://github.com/vibesoftwarecoder/MultiSeat) + [ApolloVibe](https://github.com/vibesoftwarecoder/ApolloVibe) on the host for full feature support
- Standard Moonlight-compatible host (e.g. upstream Sunshine) works for basic streaming without mic or auto-discovery
Team roles:

- Committers and reviewers: [vibesoftwarecoder](https://github.com/vibesoftwarecoder)
- Approvers: [vibesoftwarecoder](https://github.com/vibesoftwarecoder)

Privacy: see [PRIVACY.md](PRIVACY.md). MoonlightVibe sends no data to us. It does contact some
servers run by others, such as GitHub for its update check, and that page lists each one.

---

## Usage with standard Moonlight hosts
## Requirements

- Windows 10 or 11, x64.
- For the extra features, [ApolloVibe](https://github.com/vibesoftwarecoder/ApolloVibe) on the host. Seat auto-discovery also needs [MultiSeat](https://github.com/vibesoftwarecoder/MultiSeat).
- A standard Moonlight-compatible host, such as upstream Sunshine or Apollo, works for ordinary streaming. It does not offer the microphone, the second window or USB forwarding.

## Using standard Moonlight hosts

MoonlightVibe is fully compatible with any Moonlight-compatible streaming host (Sunshine, upstream Apollo, NVIDIA GameStream). Auto-discovery only activates when a MultiSeat service is detected. Mic passthrough requires ApolloVibe with `stream_mic = enabled`.
MoonlightVibe is fully compatible with Sunshine, upstream Apollo and NVIDIA GameStream. Seat auto-discovery only turns on when a MultiSeat service is found. The microphone, the second window and USB forwarding each need an ApolloVibe host with that feature enabled, and are ignored by other hosts.

---

## Building

MoonlightVibe uses the same build system as upstream moonlight-qt — Qt 6.7+ and Visual Studio 2022 on Windows.
MoonlightVibe builds the same way as upstream moonlight-qt: Qt 6.7 or newer and Visual Studio 2022 on Windows.

See the upstream [build instructions](https://github.com/moonlight-stream/moonlight-qt#building) for the full dependency list.

Expand All @@ -111,4 +157,4 @@ See the upstream [build instructions](https://github.com/moonlight-stream/moonli

MoonlightVibe is licensed under the same terms as Moonlight — [GPL-3.0](LICENSE).

Mic passthrough patches by [logabell](https://github.com/logabell/moonlight-qt-mic).
Microphone passthrough patches by [logabell](https://github.com/logabell/moonlight-qt-mic).
2 changes: 2 additions & 0 deletions app/app.pro
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ SOURCES += \
streaming/input/mouse.cpp \
streaming/input/reltouch.cpp \
streaming/session.cpp \
streaming/secondarystream.cpp \
streaming/audio/audio.cpp \
streaming/audio/capture/microphonecapture.cpp \
streaming/audio/renderers/sdlaud.cpp \
Expand Down Expand Up @@ -239,6 +240,7 @@ HEADERS += \
settings/streamingpreferences.h \
streaming/input/input.h \
streaming/session.h \
streaming/secondarystream.h \
streaming/audio/capture/microphonecapture.h \
streaming/audio/renderers/renderer.h \
streaming/audio/renderers/sdl.h \
Expand Down
45 changes: 45 additions & 0 deletions app/cli/commandlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,14 @@ void StreamCommandLineParser::parse(const QStringList &args, StreamingPreference
parser.addChoiceOption("audio-config", "audio config", m_AudioConfigMap.keys());
parser.addToggleOption("multi-controller", "multiple controller support");
parser.addToggleOption("quit-after", "quit app after session");
parser.addToggleOption("second-window-fullscreen", "open the second window in full screen");
parser.addToggleOption("absolute-mouse", "remote desktop optimized mouse control");
parser.addValueOption("second-window", "title of a host window to show in a second client window");
parser.addValueOption("window-display", "display number (1, 2, ...) for the stream window, 0 = automatic");
parser.addValueOption("second-window-source", "what the second window shows: window (a host window, the default) or display (a display the host makes)");
parser.addValueOption("second-window-resolution", "custom <width>x<height> resolution of the second window's video");
parser.addValueOption("second-window-fps", "frame rate of the second window's video");
parser.addValueOption("second-window-display", "display number (1, 2, ...) for the second window, 0 = automatic");
parser.addToggleOption("mouse-buttons-swap", "left and right mouse buttons swap");
parser.addToggleOption("touchscreen-trackpad", "touchscreen in trackpad mode");
parser.addToggleOption("game-optimization", "game optimizations");
Expand Down Expand Up @@ -448,6 +455,44 @@ void StreamCommandLineParser::parse(const QStringList &args, StreamingPreference

// Resolve --quit-after and --no-quit-after options
preferences->quitAppAfter = parser.getToggleOptionValue("quit-after", preferences->quitAppAfter);
preferences->secondWindowFullscreen = parser.getToggleOptionValue("second-window-fullscreen", preferences->secondWindowFullscreen);

// Resolve --second-window option
if (parser.isSet("second-window")) {
preferences->secondWindowTitle = parser.value("second-window");
preferences->secondWindowEnabled = true;
}
if (parser.isSet("second-window-source")) {
QString source = parser.value("second-window-source").toLower();
if (source == "display") {
preferences->secondWindowSource = 1;
preferences->secondWindowEnabled = true;
}
else if (source == "window") {
preferences->secondWindowSource = 0;
}
else {
parser.showError(QString("Invalid second-window-source choice: %1").arg(source));
}
}

// Resolve --second-window-resolution and --second-window-fps options
if (parser.isSet("second-window-resolution")) {
auto resolution = parser.getResolutionOptionValue("second-window-resolution");
preferences->secondWindowWidth = resolution.first;
preferences->secondWindowHeight = resolution.second;
}
if (parser.isSet("second-window-fps")) {
preferences->secondWindowFps = parser.getIntOption("second-window-fps");
}

// Resolve --window-display and --second-window-display options
if (parser.isSet("window-display")) {
preferences->primaryWindowDisplay = qMax(0, parser.getIntOption("window-display"));
}
if (parser.isSet("second-window-display")) {
preferences->secondWindowDisplay = qMax(0, parser.getIntOption("second-window-display"));
}

// Resolve --absolute-mouse and --no-absolute-mouse options
preferences->absoluteMouseMode = parser.getToggleOptionValue("absolute-mouse", preferences->absoluteMouseMode);
Expand Down
Loading
Loading