Skip to content

Commit

Permalink
Add pointer lock feature (game cursor)
Browse files Browse the repository at this point in the history
This makes first-person gaming over Xpra finally usable.
  • Loading branch information
CorbinWunderlich committed Dec 2, 2024
1 parent 95b3cdb commit cca3518
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 147 deletions.
269 changes: 125 additions & 144 deletions html5/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -108,149 +108,83 @@
</div>
</div>

<div id="screen" style="width: 100%; height: 100%">
<div id="float_menu">
<ul class="Menu -horizontal">
<li class="-hasSubmenu -noChevron">
<a href="#" title="Xpra" class="noDrag" data-icon="menu"></a>
<ul id="menu_list">
<li class="-hasSubmenu" id="startmenuentry" style="display: none">
<a data-icon="apps" href="#">Start</a>
<ul id="startmenu"></ul>
</li>
<li class="-hasSubmenu">
<a href="#" data-icon="kitchen">Server</a>
<ul>
<li id="clock_menu_entry">
<a
href="#"
data-icon="access_time"
onclick="return false"
id="clock_menu_text"
></a>
</li>
<li id="upload_menu_entry">
<a
href="#"
data-icon="cloud_upload"
onclick="upload_file(event); return false"
>Upload file</a
>
</li>
<li id="download_menu_entry">
<a
href="#"
data-icon="cloud_download"
onclick="download_file(event); return false"
>Download file</a
>
</li>
<li id="shutdown_menu_entry">
<a
href="#"
data-icon="exit_to_app"
onclick="confirm_shutdown_server(event); return false"
>Shutdown Server</a
>
</li>
</ul>
</li>
<li class="-hasSubmenu">
<a href="#" data-icon="info">Information</a>
<ul>
<li>
<a
href="#"
data-icon="info"
onclick="show_about(event); return false"
>About Xpra</a
>
</li>
<li>
<a
href="#"
data-icon="trending_up"
onclick="show_sessioninfo(event); return false"
>Session Info</a
>
</li>
<li>
<a
href="#"
data-icon="bug_report"
onclick="show_bugreport(event); return false"
>Bug Report</a
>
</li>
</ul>
</li>
<li>
<a href="#" data-icon="refresh" onclick="location.reload()"
>Reload</a
>
</li>
<li>
<a
href="#"
data-icon="exit_to_app"
onclick="client.disconnect_reason='User request'; client.close(); return false"
>Disconnect</a
>
</li>
</ul>
</li>
<li class="-hasSubmenu -noChevron">
<a
href="#"
title="Open Windows"
data-icon="filter"
id="open_windows"
></a>
<ul id="open_windows_list"></ul>
</li>
<li class="-hasSubmenu -noChevron">
<a
href="#"
id="fullscreen_button"
title="Fullscreen"
data-icon="fullscreen"
></a>
</li>
<li class="-hasSubmenu -noChevron">
<a
href="#"
id="keyboard_button"
title="Keyboard"
data-icon="keyboard"
></a>
</li>
<li class="-hasSubmenu -noChevron">
<a
href="#"
id="clipboard_button"
title="Clipboard Copy"
data-icon="content_copy"
></a>
</li>
<li class="-hasSubmenu -noChevron">
<a
href="#"
id="sound_button"
title="Audio"
data-icon="volume_off"
></a>
</li>
</ul>
<div id="float_menu_button">
<a
href="#"
id="float_menu_arrow"
title="Expand Menu"
data-icon="chevron_right"
></a>
</div>
<div id="float_tray" class="menu-divright"></div>
<div id="float_menu">
<ul class="Menu -horizontal">
<li class="-hasSubmenu -noChevron">
<a href="#" title="Xpra" class="noDrag" data-icon="menu"></a>
<ul id="menu_list">
<li class="-hasSubmenu" id="startmenuentry" style="display: none">
<a data-icon="apps" href="#">Start</a>
<ul id="startmenu"></ul>
</li>
<li class="-hasSubmenu">
<a href="#" data-icon="kitchen">Server</a>
<ul>
<li id="clock_menu_entry">
<a href="#" data-icon="access_time" onclick="return false" id="clock_menu_text"></a>
</li>
<li id="upload_menu_entry">
<a href="#" data-icon="cloud_upload" onclick="upload_file(event); return false">Upload
file</a>
</li>
<li id="download_menu_entry">
<a href="#" data-icon="cloud_download" onclick="download_file(event); return false">Download file</a>
</li>
<li id="shutdown_menu_entry">
<a href="#" data-icon="exit_to_app" onclick="confirm_shutdown_server(event); return false">Shutdown Server</a>
</li>
</ul>
</li>
<li class="-hasSubmenu">
<a href="#" data-icon="info">Information</a>
<ul>
<li>
<a href="#" data-icon="info" onclick="show_about(event); return false">About Xpra</a>
</li>
<li>
<a href="#" data-icon="trending_up" onclick="show_sessioninfo(event); return false">Session Info</a>
</li>
<li>
<a href="#" data-icon="bug_report" onclick="show_bugreport(event); return false">Bug
Report</a>
</li>
</ul>
</li>
<li>
<a href="#" data-icon="refresh" onclick="location.reload()">Reload</a>
</li>
<li>
<a href="#" data-icon="exit_to_app" onclick="client.disconnect_reason='User request'; client.close(); return false">Disconnect</a>
</li>
</ul>
</li>
<li class="-hasSubmenu -noChevron">
<a href="#" title="Open Windows" data-icon="filter" id="open_windows"></a>
<ul id="open_windows_list"></ul>
</li>
<li class="-hasSubmenu -noChevron">
<a href="#" id="fullscreen_button" title="Fullscreen" data-icon="fullscreen"></a>
</li>
<li class="-hasSubmenu -noChevron">
<a href="#" id="keyboard_button" title="Keyboard" data-icon="keyboard"></a>
</li>
<li class="-hasSubmenu -noChevron">
<a href="#" id="clipboard_button" title="Clipboard Copy" data-icon="content_copy"></a>
</li>
<li class="-hasSubmenu -noChevron">
<a href="#" id="sound_button" title="Audio" data-icon="volume_off"></a>
</li>
<li class="-hasSubmenu -noChevron">
<a href="#" id="cursor_lock_button" title="Lock Cursor (Gaming cursor mode)" data-icon="videogame_asset"></a>
</li>
</ul>
<div id="float_menu_button">
<a href="#" id="float_menu_arrow" title="Expand Menu" data-icon="chevron_right"></a>
</div>
<div id="float_tray" class="menu-divright"></div>
</div>

<div id="screen" style="width: 100%; height: 100%">
<img id="shadow_pointer" alt="shadow pointer" />
</div>

Expand Down Expand Up @@ -412,7 +346,7 @@ <h2>Xpra Bug Report</h2>
return v;
};

const float_menu_item_count = 6;
const float_menu_item_count = 7;
const float_menu_item_size = 30;
const float_menu_padding = 20;
let float_menu_width = float_menu_item_size * float_menu_item_count + float_menu_padding;
Expand Down Expand Up @@ -1457,7 +1391,9 @@ <h2>Xpra Bug Report</h2>
});
} else {
$("#float_menu_button").hide();
float_menu_element.css({ "max-width": "202px" });
float_menu_element.css({
"max-width": "232px"
});
}
if (!floating_menu) {
//nothing to do, it starts hidden
Expand Down Expand Up @@ -1493,12 +1429,15 @@ <h2>Xpra Bug Report</h2>
}

let client;

function init_page() {
clog("initializing page");
const touchaction = getparam("touchaction") || "scroll";
touchaction_scroll = touchaction == "scroll";
set_touchaction();

window.cursor_lock = false;

init_auth_autosubmit();

client = init_client();
Expand Down Expand Up @@ -1587,6 +1526,48 @@ <h2>Xpra Bug Report</h2>
client.read_clipboard(e);
});

$(".windowinfocus").children("canvas").on("click", function(e) {
const isPointerLocked = Boolean(document.pointerLockElement);

if (isPointerLocked) {
return;
}

if (window.cursor_lock && !isPointerLocked) {
const focusedWindow = document.getElementById(client.topwindow.toString());
focusedWindow.querySelector("canvas").requestPointerLock();

$("#cursor_lock_button").removeClass("icon-paused");
}
});

$("#cursor_lock_button").on("click", function(e) {
if (!window.cursor_lock) {
window.cursor_lock = true;

window.alert("You are now in cursor lock mode. Press ESC to temporarily exit, and click on the screen to enter it again.");

const focusedWindow = client.id_to_window[client.topwindow];
focusedWindow.canvas.requestPointerLock();

$("#cursor_lock_button").addClass("icon-toggled");

return;
}

window.cursor_lock = false;

document.exitPointerLock();

$("#cursor_lock_button").removeClass("icon-toggled icon-paused");
});

document.addEventListener("pointerlockchange", function(e) {
if (!(Boolean(document.pointerLockElement)) && window.cursor_lock) {
$("#cursor_lock_button").addClass("icon-paused");
}
});

// Configure Xpra tray window list right click behavior.
$("#open_windows_list")
.siblings("a")
Expand Down
28 changes: 26 additions & 2 deletions html5/js/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ class XpraClient {
const screen_element = jQuery("#screen");
screen_element.mousedown((e) => this.on_mousedown(e));
screen_element.mouseup((e) => this.on_mouseup(e));
screen_element.mousemove((e) => this.on_mousemove(e));
document.getElementById("screen").addEventListener("mousemove", (e) => this.on_mousemove(e));

const div = document.querySelector("#screen");
function on_mousescroll(e) {
Expand Down Expand Up @@ -1590,10 +1590,17 @@ class XpraClient {
* Mouse handlers
*/
getMouse(e) {
const windowIsLocked = Boolean(document.pointerLockElement);

// get mouse position take into account scroll
let mx = e.clientX + jQuery(document).scrollLeft();
let my = e.clientY + jQuery(document).scrollTop();

if (windowIsLocked) {
mx = e.movementX;
my = e.movementY;
}

if (this.scale !== 1) {
mx = Math.round(mx * this.scale);
my = Math.round(my * this.scale);
Expand All @@ -1611,6 +1618,10 @@ class XpraClient {
my = 0;
}
} else {
if (windowIsLocked) {
this.last_mouse_x += mx;
this.last_mouse_y += my;
}
this.last_mouse_x = mx;
this.last_mouse_y = my;
}
Expand All @@ -1623,8 +1634,15 @@ class XpraClient {
// IE, Opera (zero based)
mbutton = Math.max(0, e.button) + 1;

mx = this.last_mouse_x;
my = this.last_mouse_y;

// We return a simple javascript object (a hash) with x and y defined
return { x: mx, y: my, button: mbutton };
return {
x: mx,
y: my,
button: mbutton
};
}

on_mousedown(e, win) {
Expand Down Expand Up @@ -1719,6 +1737,12 @@ class XpraClient {
if (wid > 0 && this.focus != wid) {
this.set_focus(win);
}

if (window.cursor_lock) {
$("#cursor-lock-button").removeClass("icon-paused");
win.canvas.requestPointerLock();
}

let button = mouse.button;
const lbe = this.last_button_event;
if (lbe[0] == button && lbe[1] == pressed && lbe[2] == x && lbe[3] == y) {
Expand Down
2 changes: 1 addition & 1 deletion html5/js/Window.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ class XpraWindow {
this.pointer_last_y = event_.offsetY;
}
});
canvas.addEventListener("pointermove", (event_) => {
canvas.addEventListener("mousemove", (event_) => {
this.debug("mouse", "pointermove:", event_);
if (this.pointer_down == event_.pointerId) {
const dx = event_.offsetX - this.pointer_last_x;
Expand Down

0 comments on commit cca3518

Please sign in to comment.