diff --git a/README.md b/README.md index 9ec1a32..f5c59fa 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,15 @@ > **Warning** > OpenNOW is under active development. Bugs and performance issues are expected while features are finalized. +> **Trademark & Affiliation Notice** +> OpenNOW is an independent community project and is **not affiliated with, endorsed by, or sponsored by NVIDIA Corporation**. +> **NVIDIA** and **GeForce NOW** are trademarks of NVIDIA Corporation. You must use your own GeForce NOW account. + --- ## What is OpenNOW? -OpenNOW is a community-built desktop client for [NVIDIA GeForce NOW](https://www.nvidia.com/en-us/geforce-now/), built with Electron and TypeScript. It gives you a fully open-source, cross-platform alternative to the official app — with zero telemetry, full transparency, and features the official client doesn't have. +OpenNOW is an independent, community-built desktop client for [NVIDIA GeForce NOW](https://www.nvidia.com/en-us/geforce-now/), built with Electron and TypeScript. It provides a fully open-source, cross-platform alternative to the official app with zero telemetry, full transparency, and power-user features. - 🔓 **Fully open source** — audit every line, fork it, improve it - 🚫 **No telemetry** — OpenNOW collects nothing @@ -201,7 +205,7 @@ opennow-stable/src/ ## FAQ **Is this the official GeForce NOW client?** -No. OpenNOW is a community-built alternative. It uses the same NVIDIA streaming infrastructure but is not affiliated with or endorsed by NVIDIA. +No. OpenNOW is an independent third-party client and is not affiliated with, endorsed by, or sponsored by NVIDIA. NVIDIA and GeForce NOW are trademarks of NVIDIA Corporation. **Was this project built in Rust before?** Yes. OpenNOW originally used Rust/Tauri but switched to Electron for better cross-platform compatibility and faster development. diff --git a/opennow-stable/src/main/gfn/errorCodes.ts b/opennow-stable/src/main/gfn/errorCodes.ts index 3c08d48..e78fc79 100644 --- a/opennow-stable/src/main/gfn/errorCodes.ts +++ b/opennow-stable/src/main/gfn/errorCodes.ts @@ -1,11 +1,10 @@ /** - * GFN CloudMatch Error Codes + * CloudMatch error codes. * - * Error code mappings extracted from the official GFN web client. - * These provide user-friendly error messages for session failures. + * These mappings provide user-friendly messages for session failures. */ -/** GFN Session Error Codes from official client */ +/** Session error code constants. */ export enum GfnErrorCode { // Success codes Success = 15859712, @@ -296,7 +295,7 @@ export const ERROR_MESSAGES: Map = new Map([ 3237093656, { title: "Under Maintenance", - description: "GeForce NOW is currently under maintenance. Please try again later.", + description: "The service is currently under maintenance. Please try again later.", }, ], [ @@ -394,14 +393,14 @@ export const ERROR_MESSAGES: Map = new Map([ 3237093684, { title: "Region Not Supported", - description: "GeForce NOW is not available in your region.", + description: "The service is not available in your region.", }, ], [ 3237093685, { title: "Region Banned", - description: "GeForce NOW is not available in your region.", + description: "The service is not available in your region.", }, ], [ @@ -534,7 +533,7 @@ export const ERROR_MESSAGES: Map = new Map([ 3237093722, { title: "Storage Error", - description: "GFN storage is not available.", + description: "Service storage is not available.", }, ], @@ -646,7 +645,7 @@ export interface SessionErrorInfo { unifiedErrorCode?: number; /** Session error code from session.errorCode */ sessionErrorCode?: number; - /** Computed GFN error code */ + /** Computed service error code */ gfnErrorCode: number; /** User-friendly title */ title: string; @@ -679,7 +678,7 @@ export class SessionError extends Error { public readonly unifiedErrorCode?: number; /** Session error code from session.errorCode */ public readonly sessionErrorCode?: number; - /** Computed GFN error code */ + /** Computed service error code */ public readonly gfnErrorCode: number; /** User-friendly title */ public readonly title: string; @@ -733,7 +732,7 @@ export class SessionError extends Error { const unifiedErrorCode = json.requestStatus?.unifiedErrorCode; const sessionErrorCode = json.session?.errorCode; - // Compute GFN error code using official client logic + // Compute normalized service error code const gfnErrorCode = SessionError.computeErrorCode(statusCode, unifiedErrorCode); // Get user-friendly message @@ -756,7 +755,7 @@ export class SessionError extends Error { } /** - * Compute GFN error code from CloudMatch response (matching official client logic) + * Compute service error code from CloudMatch response */ private static computeErrorCode(statusCode: number, unifiedErrorCode?: number): number { // Base error code diff --git a/opennow-stable/src/renderer/src/App.tsx b/opennow-stable/src/renderer/src/App.tsx index f62a8bd..b16db90 100644 --- a/opennow-stable/src/renderer/src/App.tsx +++ b/opennow-stable/src/renderer/src/App.tsx @@ -692,6 +692,7 @@ export function App(): JSX.Element { audioElement: audioRef.current, microphoneMode: settings.microphoneMode, microphoneDeviceId: settings.microphoneDeviceId || undefined, + mouseSensitivity: settings.mouseSensitivity, onLog: (line: string) => console.log(`[WebRTC] ${line}`), onStats: (stats) => setDiagnostics(stats), onEscHoldProgress: (visible, progress) => { @@ -760,6 +761,14 @@ export function App(): JSX.Element { if (settingsLoaded) { await window.openNow.setSetting(key, value); } + // If a running client exists, push certain settings live + if (key === "mouseSensitivity") { + try { + (clientRef.current as any)?.setMouseSensitivity?.(value as number); + } catch { + // ignore + } + } }, [settingsLoaded]); // Login handler diff --git a/opennow-stable/src/renderer/src/components/SettingsPage.tsx b/opennow-stable/src/renderer/src/components/SettingsPage.tsx index a19c115..0589288 100644 --- a/opennow-stable/src/renderer/src/components/SettingsPage.tsx +++ b/opennow-stable/src/renderer/src/components/SettingsPage.tsx @@ -1416,6 +1416,36 @@ export function SettingsPage({ settings, regions, onSettingChange }: SettingsPag Export Logs + + {/* Mouse Sensitivity */} +
+ +
+ handleChange("mouseSensitivity", parseFloat(e.target.value))} + /> + { + const v = parseFloat(e.target.value || "0"); + if (Number.isFinite(v)) handleChange("mouseSensitivity", Math.max(0.1, Math.min(4, v))); + }} + /> + Multiplier applied to mouse movement (1.00 = default) +
+
diff --git a/opennow-stable/src/renderer/src/gfn/webrtcClient.ts b/opennow-stable/src/renderer/src/gfn/webrtcClient.ts index 3911ba3..a6001e8 100644 --- a/opennow-stable/src/renderer/src/gfn/webrtcClient.ts +++ b/opennow-stable/src/renderer/src/gfn/webrtcClient.ts @@ -183,6 +183,8 @@ interface ClientOptions { microphoneMode?: MicrophoneMode; /** Preferred microphone device ID */ microphoneDeviceId?: string; + /** Mouse sensitivity multiplier (1.0 = default) */ + mouseSensitivity?: number; onLog: (line: string) => void; onStats?: (stats: StreamDiagnostics) => void; onEscHoldProgress?: (visible: boolean, progress: number) => void; @@ -503,6 +505,7 @@ export class GfnWebRtcClient { private mouseFlushLastTickMs = 0; private pendingMouseTimestampUs: bigint | null = null; private mouseDeltaFilter = new MouseDeltaFilter(); + private mouseSensitivity = 1; private partialReliableThresholdMs = GfnWebRtcClient.DEFAULT_PARTIAL_RELIABLE_THRESHOLD_MS; private inputQueuePeakBufferedBytesWindow = 0; @@ -557,6 +560,7 @@ export class GfnWebRtcClient { options.videoElement.srcObject = this.videoStream; options.audioElement.srcObject = this.audioStream; options.audioElement.muted = true; + this.mouseSensitivity = options.mouseSensitivity ?? 1; // Configure video element for lowest latency playback this.configureVideoElementForLowLatency(options.videoElement); @@ -605,6 +609,13 @@ export class GfnWebRtcClient { this.log("Video element configured for low-latency playback"); } + /** Update mouse sensitivity multiplier at runtime. */ + public setMouseSensitivity(value: number): void { + const v = Number.isFinite(value) ? value : 1; + this.mouseSensitivity = Math.max(0.01, v); + this.log(`Mouse sensitivity set to ${this.mouseSensitivity}`); + } + /** * Configure an RTCRtpReceiver for minimum jitter buffer delay. * @@ -1840,8 +1851,9 @@ export class GfnWebRtcClient { return; } - this.pendingMouseDx += Math.round(this.mouseDeltaFilter.getX()); - this.pendingMouseDy += Math.round(this.mouseDeltaFilter.getY()); + // Apply user-configured mouse sensitivity multiplier before queuing + this.pendingMouseDx += Math.round(this.mouseDeltaFilter.getX() * this.mouseSensitivity); + this.pendingMouseDy += Math.round(this.mouseDeltaFilter.getY() * this.mouseSensitivity); this.pendingMouseTimestampUs = timestampUs(eventTimestampMs); };