mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-06 07:37:19 +02:00
Add microphone action for Game Bar
This commit is contained in:
parent
5c9202119b
commit
362c5386d1
@ -213,6 +213,7 @@ function main() {
|
|||||||
// Setup UI
|
// Setup UI
|
||||||
addCss();
|
addCss();
|
||||||
Toast.setup();
|
Toast.setup();
|
||||||
|
getPref(PrefKey.GAME_BAR_ENABLED) && GameBar.getInstance();
|
||||||
BX_FLAGS.PreloadUi && setupStreamUi();
|
BX_FLAGS.PreloadUi && setupStreamUi();
|
||||||
|
|
||||||
StreamBadges.setupEvents();
|
StreamBadges.setupEvents();
|
||||||
|
81
src/modules/game-bar/action-microphone.ts
Normal file
81
src/modules/game-bar/action-microphone.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { BxEvent, XcloudEvent } from "@utils/bx-event";
|
||||||
|
import { BxIcon } from "@utils/bx-icon";
|
||||||
|
import { createButton, ButtonStyle, CE } from "@utils/html";
|
||||||
|
import { t } from "@utils/translation";
|
||||||
|
import { BaseGameBarAction } from "./action-base";
|
||||||
|
|
||||||
|
enum MicrophoneState {
|
||||||
|
REQUESTED = 'Requested',
|
||||||
|
ENABLED = 'Enabled',
|
||||||
|
MUTED = 'Muted',
|
||||||
|
NOT_ALLOWED = 'NotAllowed',
|
||||||
|
NOT_FOUND = 'NotFound',
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MicrophoneAction extends BaseGameBarAction {
|
||||||
|
$content: HTMLElement;
|
||||||
|
|
||||||
|
visible: boolean = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
const onClick = (e: Event) => {
|
||||||
|
BxEvent.dispatch(window, BxEvent.GAME_BAR_ACTION_ACTIVATED);
|
||||||
|
const state = this.$content.getAttribute('data-enabled');
|
||||||
|
const enableMic = state === 'true' ? false : true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
window.BX_EXPOSED.streamSession.tryEnableChatAsync(enableMic);
|
||||||
|
this.$content.setAttribute('data-enabled', enableMic.toString());
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const $btnDefault = createButton({
|
||||||
|
style: ButtonStyle.GHOST,
|
||||||
|
icon: BxIcon.MICROPHONE,
|
||||||
|
title: t('show-touch-controller'),
|
||||||
|
onClick: onClick,
|
||||||
|
classes: ['bx-activated'],
|
||||||
|
});
|
||||||
|
|
||||||
|
const $btnMuted = createButton({
|
||||||
|
style: ButtonStyle.GHOST,
|
||||||
|
icon: BxIcon.MICROPHONE_MUTED,
|
||||||
|
title: t('hide-touch-controller'),
|
||||||
|
onClick: onClick,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$content = CE('div', {},
|
||||||
|
$btnDefault,
|
||||||
|
$btnMuted,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.reset();
|
||||||
|
|
||||||
|
window.addEventListener(BxEvent.STREAM_EVENT_TARGET_READY, e => {
|
||||||
|
const eventTarget = window.BX_EXPOSED.eventTarget as EventTarget;
|
||||||
|
eventTarget.addEventListener(XcloudEvent.MICROPHONE_STATE_CHANGED, e => {
|
||||||
|
const state = window.BX_EXPOSED.streamSession.microphoneState as MicrophoneState;
|
||||||
|
const enabled = state === MicrophoneState.ENABLED;
|
||||||
|
|
||||||
|
this.$content.setAttribute('data-enabled', enabled.toString());
|
||||||
|
|
||||||
|
// Show the button in Game Bar if the mic is enabled
|
||||||
|
this.$content.classList.remove('bx-gone');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): HTMLElement {
|
||||||
|
return this.$content;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
this.visible = false;
|
||||||
|
this.$content.classList.add('bx-gone');
|
||||||
|
this.$content.setAttribute('data-enabled', 'false');
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import { BxIcon } from "@utils/bx-icon";
|
|||||||
import type { BaseGameBarAction } from "./action-base";
|
import type { BaseGameBarAction } from "./action-base";
|
||||||
import { STATES } from "@utils/global";
|
import { STATES } from "@utils/global";
|
||||||
import { PrefKey, getPref } from "@utils/preferences";
|
import { PrefKey, getPref } from "@utils/preferences";
|
||||||
|
import { MicrophoneAction } from "./action-microphone";
|
||||||
|
|
||||||
|
|
||||||
export class GameBar {
|
export class GameBar {
|
||||||
@ -38,6 +39,7 @@ export class GameBar {
|
|||||||
this.actions = [
|
this.actions = [
|
||||||
new ScreenshotAction(),
|
new ScreenshotAction(),
|
||||||
...(STATES.hasTouchSupport && (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== 'off') ? [new TouchControlAction()] : []),
|
...(STATES.hasTouchSupport && (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== 'off') ? [new TouchControlAction()] : []),
|
||||||
|
new MicrophoneAction(),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Render actions
|
// Render actions
|
||||||
|
@ -4,6 +4,7 @@ import { getPref, PrefKey } from "@utils/preferences";
|
|||||||
import { VibrationManager } from "@modules/vibration-manager";
|
import { VibrationManager } from "@modules/vibration-manager";
|
||||||
import { BxLogger } from "@utils/bx-logger";
|
import { BxLogger } from "@utils/bx-logger";
|
||||||
import { hashCode } from "@utils/utils";
|
import { hashCode } from "@utils/utils";
|
||||||
|
import { BxEvent } from "@/utils/bx-event";
|
||||||
|
|
||||||
type PatchArray = (keyof typeof PATCHES)[];
|
type PatchArray = (keyof typeof PATCHES)[];
|
||||||
|
|
||||||
@ -496,6 +497,45 @@ BxLogger.info('patchRemotePlayMkb', ${configsVar});
|
|||||||
str = str.replace(text, newCode);
|
str = str.replace(text, newCode);
|
||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
exposeEventTarget(str: string) {
|
||||||
|
const text ='this._eventTarget=new EventTarget';
|
||||||
|
if (!str.includes(text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCode = `
|
||||||
|
window.BX_EXPOSED.eventTarget = ${text},
|
||||||
|
window.dispatchEvent(new Event('${BxEvent.STREAM_EVENT_TARGET_READY}'))
|
||||||
|
`;
|
||||||
|
|
||||||
|
str = str.replace(text, newCode);
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
|
||||||
|
exposeStreamSession(str: string) {
|
||||||
|
const text =',this._connectionType=';
|
||||||
|
if (!str.includes(text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCode = `;
|
||||||
|
|
||||||
|
window.BX_EXPOSED.streamSession = this;
|
||||||
|
|
||||||
|
const orgSetMicrophoneState = this.setMicrophoneState.bind(this);
|
||||||
|
this.setMicrophoneState = (e) => {
|
||||||
|
console.log(e);
|
||||||
|
orgSetMicrophoneState(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.dispatchEvent(new Event('${BxEvent.STREAM_SESSION_READY}'))
|
||||||
|
|
||||||
|
true` + text;
|
||||||
|
|
||||||
|
str = str.replace(text, newCode);
|
||||||
|
return str;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let PATCH_ORDERS: PatchArray = [
|
let PATCH_ORDERS: PatchArray = [
|
||||||
@ -503,6 +543,8 @@ let PATCH_ORDERS: PatchArray = [
|
|||||||
'overrideSettings',
|
'overrideSettings',
|
||||||
'broadcastPollingMode',
|
'broadcastPollingMode',
|
||||||
|
|
||||||
|
'exposeStreamSession',
|
||||||
|
|
||||||
getPref(PrefKey.UI_LAYOUT) !== 'default' && 'websiteLayout',
|
getPref(PrefKey.UI_LAYOUT) !== 'default' && 'websiteLayout',
|
||||||
getPref(PrefKey.LOCAL_CO_OP_ENABLED) && 'supportLocalCoOp',
|
getPref(PrefKey.LOCAL_CO_OP_ENABLED) && 'supportLocalCoOp',
|
||||||
getPref(PrefKey.GAME_FORTNITE_FORCE_CONSOLE) && 'forceFortniteConsole',
|
getPref(PrefKey.GAME_FORTNITE_FORCE_CONSOLE) && 'forceFortniteConsole',
|
||||||
@ -538,6 +580,8 @@ let PLAYING_PATCH_ORDERS: PatchArray = [
|
|||||||
'patchStreamHud',
|
'patchStreamHud',
|
||||||
'playVibration',
|
'playVibration',
|
||||||
|
|
||||||
|
'exposeEventTarget',
|
||||||
|
|
||||||
// Patch volume control for normal stream
|
// Patch volume control for normal stream
|
||||||
getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && !getPref(PrefKey.STREAM_COMBINE_SOURCES) && 'patchAudioMediaStream',
|
getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && !getPref(PrefKey.STREAM_COMBINE_SOURCES) && 'patchAudioMediaStream',
|
||||||
// Patch volume control for combined audio+video stream
|
// Patch volume control for combined audio+video stream
|
||||||
|
@ -120,8 +120,13 @@ export function injectStreamMenuButtons() {
|
|||||||
|
|
||||||
let $elm: HTMLElement | null = $node as HTMLElement;
|
let $elm: HTMLElement | null = $node as HTMLElement;
|
||||||
|
|
||||||
|
// Ignore SVG elements
|
||||||
|
if ($elm instanceof SVGSVGElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Error Page: .PureErrorPage.ErrorScreen
|
// Error Page: .PureErrorPage.ErrorScreen
|
||||||
if ($elm.className.includes('PureErrorPage')) {
|
if ($elm.className?.includes('PureErrorPage')) {
|
||||||
BxEvent.dispatch(window, BxEvent.STREAM_ERROR_PAGE);
|
BxEvent.dispatch(window, BxEvent.STREAM_ERROR_PAGE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -133,7 +138,7 @@ export function injectStreamMenuButtons() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render badges
|
// Render badges
|
||||||
if ($elm.className.startsWith('StreamMenu-module__container')) {
|
if ($elm.className?.startsWith('StreamMenu-module__container')) {
|
||||||
BxEvent.dispatch(window, BxEvent.STREAM_MENU_SHOWN);
|
BxEvent.dispatch(window, BxEvent.STREAM_MENU_SHOWN);
|
||||||
|
|
||||||
const $btnCloseHud = document.querySelector('button[class*=StreamMenu-module__backButton]');
|
const $btnCloseHud = document.querySelector('button[class*=StreamMenu-module__backButton]');
|
||||||
@ -175,7 +180,7 @@ export function injectStreamMenuButtons() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($elm.className.startsWith('Overlay-module_') || $elm.className.startsWith('InProgressScreen')) {
|
if ($elm.className?.startsWith('Overlay-module_') || $elm.className?.startsWith('InProgressScreen')) {
|
||||||
$elm = $elm.querySelector('#StreamHud');
|
$elm = $elm.querySelector('#StreamHud');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import { StreamStats } from "@modules/stream/stream-stats";
|
|||||||
import { TouchController } from "@modules/touch-controller";
|
import { TouchController } from "@modules/touch-controller";
|
||||||
import { t } from "@utils/translation";
|
import { t } from "@utils/translation";
|
||||||
import { VibrationManager } from "@modules/vibration-manager";
|
import { VibrationManager } from "@modules/vibration-manager";
|
||||||
import { GameBar } from "../game-bar/game-bar";
|
|
||||||
import { Screenshot } from "@/utils/screenshot";
|
import { Screenshot } from "@/utils/screenshot";
|
||||||
|
|
||||||
|
|
||||||
@ -477,7 +476,6 @@ export function setupStreamUi() {
|
|||||||
StreamStats.render();
|
StreamStats.render();
|
||||||
|
|
||||||
Screenshot.setup();
|
Screenshot.setup();
|
||||||
getPref(PrefKey.GAME_BAR_ENABLED) && GameBar.getInstance();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateVideoPlayerCss();
|
updateVideoPlayerCss();
|
||||||
|
@ -19,6 +19,9 @@ export enum BxEvent {
|
|||||||
STREAM_WEBRTC_CONNECTED = 'bx-stream-webrtc-connected',
|
STREAM_WEBRTC_CONNECTED = 'bx-stream-webrtc-connected',
|
||||||
STREAM_WEBRTC_DISCONNECTED = 'bx-stream-webrtc-disconnected',
|
STREAM_WEBRTC_DISCONNECTED = 'bx-stream-webrtc-disconnected',
|
||||||
|
|
||||||
|
STREAM_EVENT_TARGET_READY = 'bx-stream-event-target-ready',
|
||||||
|
STREAM_SESSION_READY = 'bx-stream-session-ready',
|
||||||
|
|
||||||
CUSTOM_TOUCH_LAYOUTS_LOADED = 'bx-custom-touch-layouts-loaded',
|
CUSTOM_TOUCH_LAYOUTS_LOADED = 'bx-custom-touch-layouts-loaded',
|
||||||
|
|
||||||
REMOTE_PLAY_READY = 'bx-remote-play-ready',
|
REMOTE_PLAY_READY = 'bx-remote-play-ready',
|
||||||
@ -31,6 +34,10 @@ export enum BxEvent {
|
|||||||
GAME_BAR_ACTION_ACTIVATED = 'bx-game-bar-action-activated',
|
GAME_BAR_ACTION_ACTIVATED = 'bx-game-bar-action-activated',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum XcloudEvent {
|
||||||
|
MICROPHONE_STATE_CHANGED = 'microphoneStateChanged',
|
||||||
|
}
|
||||||
|
|
||||||
export namespace BxEvent {
|
export namespace BxEvent {
|
||||||
export function dispatch(target: HTMLElement | Window, eventName: string, data?: any) {
|
export function dispatch(target: HTMLElement | Window, eventName: string, data?: any) {
|
||||||
if (!eventName) {
|
if (!eventName) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user