Allow finetuning of analog sticks using the controller's buttons

This commit is contained in:
Mathias Malmqvist
2025-07-30 00:32:00 +02:00
committed by dualshock-tools
parent 9bb9f2f082
commit 71633d7718
2 changed files with 326 additions and 55 deletions

220
core.js
View File

@@ -17,10 +17,17 @@ var gj = 0;
var gu = 0;
// DS5 finetuning
var finetune_original_data = []
var last_written_finetune_data = []
var finetune_visible = false
var on_finetune_updating = false
let finetune_original_data = []
let last_written_finetune_data = []
let finetune_visible = false
let on_finetune_updating = false
// Active stick tracking for finetune modal
let active_stick = 'left' // 'left' or 'right'
// Continuous D-pad adjustment tracking
let dpad_adjustment_interval = null
let dpad_adjustment_timeout = null
// Global object to keep track of button states
const ds_button_states = {
@@ -1255,7 +1262,7 @@ async function ds5_finetune() {
finetune_original_data = data
finetune_visible = true
refresh_finetune()
refresh_finetune_sticks();
}
async function ds5_get_inmemory_module_data() {
@@ -1314,9 +1321,7 @@ async function write_finetune_data(data) {
await device.sendFeatureReport(0x80, alloc_req(0x80, pkg))
}
function refresh_finetune() {
if (!finetune_visible)
return;
function refresh_finetune_sticks() {
if (on_finetune_updating)
return;
@@ -1328,6 +1333,47 @@ function ds5_finetune_update_all() {
const { left, right } = ds_button_states.sticks;
ds5_finetune_update("finetuneStickCanvasL", left.x, left.y);
ds5_finetune_update("finetuneStickCanvasR", right.x, right.y);
// Highlight the active finetune input based on stick position
highlight_active_finetune_input();
}
function highlight_active_finetune_input() {
const sticks = ds_button_states.sticks;
const currentStick = sticks[active_stick];
const deadzone = 0.3;
// Clear highlights from all inputs first
const inputs = ["LL", "LT", "RL", "RT", "LR", "LB", "RR", "RB"];
inputs.forEach(suffix => {
$(`#finetune${suffix}`).removeClass("border-primary border-2");
});
// Clear label highlights
const labelIds = ["Lx-lbl", "Ly-lbl", "Rx-lbl", "Ry-lbl"];
labelIds.forEach(suffix => {
$(`#finetuneStickCanvas${suffix}`).removeClass("text-primary");
});
// Only highlight if stick is moved significantly from center
if (Math.abs(currentStick.x) >= deadzone || Math.abs(currentStick.y) >= deadzone) {
const quadrant = get_stick_quadrant(currentStick.x, currentStick.y);
const inputSuffix = get_finetune_input_suffix_for_quadrant(active_stick, quadrant);
if (inputSuffix) {
// Highllight the corresponding finetune input box
$(`#finetune${inputSuffix}`).addClass("border-primary border-2");
// Also highlight the corresponding LX/LY label to observe
let labelId = "";
if (active_stick === 'left') {
labelId = `finetuneStickCanvas${quadrant === 'left' || quadrant === 'right' ? "Lx" : "Ly"}-lbl`;
} else { // right
labelId = `finetuneStickCanvas${quadrant === 'left' || quadrant === 'right' ? "Rx" : "Ry"}-lbl`;
}
$(`#${labelId}`).addClass("text-primary");
}
}
}
function ds5_finetune_update(name, plx, ply) {
@@ -1350,10 +1396,150 @@ function ds5_finetune_update(name, plx, ply) {
function finetune_close() {
$("#finetuneModal").modal("hide");
finetune_visible = false
stop_continuous_dpad_adjustment();
finetune_original_data = []
}
function set_stick_to_finetune(stick) {
// Stop any continuous adjustments when switching sticks
stop_continuous_dpad_adjustment();
active_stick = stick;
// Remove active class from both cards
$("#left-stick-card").removeClass("stick-card-active");
$("#right-stick-card").removeClass("stick-card-active");
// Add active class to the selected card
if (stick === 'left') {
$("#left-stick-card").addClass("stick-card-active");
} else {
$("#right-stick-card").addClass("stick-card-active");
}
}
function handle_finetune_stick_switching(changes) {
if (changes.l1) {
set_stick_to_finetune('left');
} else if (changes.r1) {
set_stick_to_finetune('right');
}
}
function get_stick_quadrant(x, y) {
// Determine which quadrant the stick is in based on x,y coordinates
// x and y are normalized values between -1 and 1
if (Math.abs(x) > Math.abs(y)) {
return x > 0 ? 'right' : 'left';
} else {
return y > 0 ? 'down' : 'up';
}
}
function get_finetune_input_suffix_for_quadrant(stick, quadrant) {
if (stick === 'left') {
switch (quadrant) {
case 'left': return "LL";
case 'up': return "LT";
case 'right': return "LR";
case 'down': return "LB";
}
} else if (stick === 'right') {
switch (quadrant) {
case 'left': return "RL";
case 'up': return "RT";
case 'right': return "RR";
case 'down': return "RB";
}
}
return null; // Invalid
}
function handle_finetune_dpad_adjustment(changes) {
const sticks = ds_button_states.sticks;
const currentStick = sticks[active_stick];
// Only adjust if stick is moved significantly from center
const deadzone = 0.1;
if (Math.abs(currentStick.x) < deadzone && Math.abs(currentStick.y) < deadzone) {
stop_continuous_dpad_adjustment();
return;
}
const quadrant = get_stick_quadrant(currentStick.x, currentStick.y);
// Check for button press events (not current state)
// Use different step sizes based on quadrant - right/down values are much larger
const adjustmentStep = (quadrant === 'right' || quadrant === 'down') ? 15 : 3;
let adjustment = 0;
if (quadrant === 'left' || quadrant === 'right') {
// Horizontal quadrants: left increases, right decreases
if (changes.left || changes.square) {
adjustment = adjustmentStep;
} else if (changes.right || changes.circle) {
adjustment = -adjustmentStep;
} else if ([changes.left, changes.right, changes.square, changes.circle].includes(false)) {
// Button was released
stop_continuous_dpad_adjustment();
return;
}
} else if (quadrant === 'up' || quadrant === 'down') {
// Vertical quadrants: up increases, down decreases
if (changes.up || changes.triangle) {
adjustment = adjustmentStep;
} else if (changes.down || changes.cross) {
adjustment = -adjustmentStep;
} else if ([changes.up, changes.down, changes.triangle, changes.cross].includes(false)) {
// Button was released
stop_continuous_dpad_adjustment();
return;
}
}
// Start continuous adjustment on button press
if (adjustment !== 0) {
start_continuous_dpad_adjustment(active_stick, quadrant, adjustment);
}
}
function start_continuous_dpad_adjustment(active_stick, quadrant, adjustment) {
stop_continuous_dpad_adjustment();
const inputSuffix = get_finetune_input_suffix_for_quadrant(active_stick, quadrant);
const element = $(`#finetune${inputSuffix}`);
if (!element.length) return;
// Perform initial adjustment immediately...
perform_dpad_adjustment(element, adjustment);
// ...then prime continuous adjustment
dpad_adjustment_timeout = setTimeout(() => {
dpad_adjustment_interval = setInterval(() => {
perform_dpad_adjustment(element, adjustment);
}, 150);
}, 400); // Initial delay before continuous adjustment starts (400ms)
}
function stop_continuous_dpad_adjustment() {
clearInterval(dpad_adjustment_interval);
dpad_adjustment_interval = null;
clearTimeout(dpad_adjustment_timeout);
dpad_adjustment_timeout = null;
}
async function perform_dpad_adjustment(element, adjustment) {
const currentValue = parseInt(element.val()) || 0;
const newValue = Math.max(0, Math.min(65535, currentValue + adjustment));
element.val(newValue);
// Trigger the change event to update the finetune data
await on_finetune_change();
}
function finetune_save() {
finetune_close();
@@ -1847,9 +2033,6 @@ function update_stick_graphics(changes, {is_ds5}) {
if (!changes || !changes.sticks) return;
refresh_sticks();
if (is_ds5) {
refresh_finetune();
}
}
function update_ds_button_svg(changes, BUTTON_MAP) {
@@ -2003,13 +2186,18 @@ function process_ds_input({data}) {
// Use DS5 map: dpad byte 7, L2 analog 4, R2 analog 5
const changes = record_ds_button_states(data, DS5_BUTTON_MAP, 7, 4, 5);
if(current_active_tab === 'controller-tab') {
update_stick_graphics(changes, { is_ds5: true });
update_ds_button_svg(changes, DS5_BUTTON_MAP);
if(finetune_visible) {
refresh_finetune_sticks();
handle_finetune_stick_switching(changes);
handle_finetune_dpad_adjustment(changes);
} else {
update_stick_graphics(changes, { is_ds5: true });
update_ds_button_svg(changes, DS5_BUTTON_MAP);
const points = parse_touch_points(data, 32);
update_touchpad_circles(points);
const points = parse_touch_points(data, 32);
update_touchpad_circles(points);
}
}
if(current_active_tab === 'tests-tab') {

View File

@@ -41,6 +41,84 @@
<style>
dl.row dt { font-weight: normal; }
dl.row dd { font-family: monospace; }
/* Active stick card styling */
.stick-card-active {
border: 1px solid #0d6efd !important;
box-shadow: 0 0 10px rgba(13, 110, 253, 0.3) !important;
}
.stick-card-active .card-header {
background-color: #0d6efd !important;
color: white !important;
}
/* Styling for coordinate labels - base state to prevent layout shift */
#finetuneStickCanvasLx-lbl,
#finetuneStickCanvasLy-lbl,
#finetuneStickCanvasRx-lbl,
#finetuneStickCanvasRy-lbl {
padding: 2px 4px !important;
border-radius: 3px !important;
background-color: transparent !important;
}
/* Styling for finetune input boxes - base state to prevent layout shift */
input[id^="finetune"] {
border: 2px solid transparent !important;
width: 90px !important;
min-width: 90px !important;
}
/* Styling for highlighted finetune input boxes */
input[id^="finetune"].border-primary {
border: 2px solid #0d6efd !important;
}
/* Styling for highlighted coordinate labels */
#finetuneStickCanvasLx-lbl.text-primary,
#finetuneStickCanvasLy-lbl.text-primary,
#finetuneStickCanvasRx-lbl.text-primary,
#finetuneStickCanvasRy-lbl.text-primary {
color: #0d6efd !important;
background-color: rgba(13, 110, 253, 0.1) !important;
}
/* CSS Grid layout for finetune inputs around canvas */
.finetune-grid {
display: grid;
grid-template-columns: 1fr auto 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
". top ."
"left center right"
". bottom .";
justify-items: center;
align-items: center;
width: 100%;
margin: 0 auto;
max-width: fit-content;
}
.finetune-top {
grid-area: top;
}
.finetune-left {
grid-area: left;
}
.finetune-center {
grid-area: center;
}
.finetune-right {
grid-area: right;
}
.finetune-bottom {
grid-area: bottom;
}
</style>
</head>
@@ -443,7 +521,7 @@ dl.row dd { font-family: monospace; }
<!-- Finetune Modal -->
<div class="modal fade" id="finetuneModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="finetuneModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg modal-fullscreen-lg-down">
<div class="modal-dialog modal-dialog-centered modal-xl modal-fullscreen-lg-down">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5 ds-i18n" id="finetuneModalLabel">Finetune stick calibration</h1>
@@ -451,33 +529,40 @@ dl.row dd { font-family: monospace; }
</div>
<div class="modal-body">
<p class="ds-i18n">This screen allows to finetune raw calibration data on your controller</p>
<div class="alert alert-info" role="alert">
<i class="fas fa-info-circle"></i>&nbsp;&nbsp;<span class="ds-i18n">
Press L1 to select Left stick, R1 to select Right stick</span><br/><br/>
<span class="ds-i18n">While holding the stick to be adjusted straight up/down/left/right, observe the highlighted value at the bottom of then dialog, then use the D-pad buttons to adjust the finetune value:</span><br/>
<small class="ds-i18n">
• Move the stick position towards the center until it is just smaller than 1.00 (or larger than -1.00) then move back to 1.00 (or -1.00)<br>
• Repeat the adjustment for the left/right and up/down pairs several times until you can move away from 1.00 with only one button press.
</small>
</div>
<div style="width: 100%; display: flex; justify-content: center;">
<div class="container-fluid">
<div class="row">
<div class="col col-lg-6 col-12">
<div class="card text-bg-light" >
<div class="card text-bg-light stick-card-active" id="left-stick-card">
<div class="card-header"><span class="ds-i18n">Left stick</span></div>
<div class="card-body">
<div class="container-fluid">
<div class="row">
<table>
<tr><td></td><td style="text-align: center;">
<input id="finetuneLT" type="number" class="form-control" min="0" max="65535" value="0">
</td><td></td></tr>
<tr>
<td height="160px" style="vertical-align: middle; align: right;">
<input id="finetuneLL" type="number" class="form-control" min="0" max="65535" value="0">
</td>
<td><canvas id="finetuneStickCanvasL" width="150" height="150"></canvas></td>
<td height="160px" style="vertical-align: middle; align: left;">
<input id="finetuneLR" type="number" class="form-control" min="0" max="65535" value="0">
</td>
</tr>
<tr><td></td><td style="text-align: center;">
<input id="finetuneLB" type="number" class="form-control" min="0" max="65535" value="0">
</td><td></td></tr>
</table>
<div class="finetune-grid">
<div class="finetune-top">
<input id="finetuneLT" type="number" class="form-control" min="0" max="65535" value="0">
</div>
<div class="finetune-left">
<input id="finetuneLL" type="number" class="form-control" min="0" max="65535" value="0">
</div>
<div class="finetune-center">
<canvas id="finetuneStickCanvasL" width="150" height="150"></canvas>
</div>
<div class="finetune-right">
<input id="finetuneLR" type="number" class="form-control" min="0" max="65535" value="0">
</div>
<div class="finetune-bottom">
<input id="finetuneLB" type="number" class="form-control" min="0" max="65535" value="0">
</div>
</div>
<div class="row">
<div class="col"></div>
@@ -520,28 +605,26 @@ dl.row dd { font-family: monospace; }
</div> <!-- col -->
<div class="col col-lg-6 col-12">
<div class="card text-bg-light" >
<div class="card text-bg-light" id="right-stick-card">
<div class="card-header"><span class="ds-i18n">Right stick</span></div>
<div class="card-body">
<div class="container-fluid">
<div class="row">
<table>
<tr><td></td><td style="text-align: center;">
<input id="finetuneRT" type="number" class="form-control" min="0" max="65535" value="0">
</td><td></td></tr>
<tr>
<td height="160px" style="vertical-align: middle; align: right;">
<input id="finetuneRL" type="number" class="form-control" min="0" max="65535" value="0">
</td>
<td><canvas id="finetuneStickCanvasR" width="150" height="150"></canvas></td>
<td height="160px" style="vertical-align: middle; align: left;">
<input id="finetuneRR" type="number" class="form-control" min="0" max="65535" value="0">
</td>
</tr>
<tr><td></td><td style="text-align: center;">
<input id="finetuneRB" type="number" class="form-control" min="0" max="65535" value="0">
</td><td></td></tr>
</table>
<div class="finetune-grid">
<div class="finetune-top">
<input id="finetuneRT" type="number" class="form-control" min="0" max="65535" value="0">
</div>
<div class="finetune-left">
<input id="finetuneRL" type="number" class="form-control" min="0" max="65535" value="0">
</div>
<div class="finetune-center">
<canvas id="finetuneStickCanvasR" width="150" height="150"></canvas>
</div>
<div class="finetune-right">
<input id="finetuneRR" type="number" class="form-control" min="0" max="65535" value="0">
</div>
<div class="finetune-bottom">
<input id="finetuneRB" type="number" class="form-control" min="0" max="65535" value="0">
</div>
</div>
<div class="row">
<div class="col"></div>