diff --git a/src/ps2.js b/src/ps2.js --- a/src/ps2.js +++ b/src/ps2.js @@ -34,6 +34,11 @@ export function PS2(cpu, bus) this.mouse_send_delta(data[0], data[1]); }, this); + this.bus.register("mouse-flush", function() + { + this.flush_mouse_movement(true); + }, this); + this.bus.register("mouse-wheel", function(data) { if(!this.have_mouse || !this.use_mouse || !this.mouse_stream_active()) @@ -298,6 +303,41 @@ PS2.prototype.kbd_send_code = function(code) } }; +PS2.prototype.flush_mouse_movement = function(force) +{ + if(!this.mouse_stream_active()) + { + return false; + } + + var change_x = this.mouse_delta_x | 0, + change_y = this.mouse_delta_y | 0; + + if(!change_x && !change_y || + !force && (this.mouse_buffer.length || + this.last_mouse_packet >= 0 && + Date.now() - this.last_mouse_packet < 1000 / this.sample_rate)) + { + return false; + } + + do + { + // Keep every emitted packet representable as signed PS/2 deltas. A + // forced button boundary drains all chunks before changing buttons. + change_x = Math.max(-255, Math.min(255, change_x)); + change_y = Math.max(-255, Math.min(255, change_y)); + this.mouse_delta_x -= change_x; + this.mouse_delta_y -= change_y; + this.send_mouse_packet(change_x, change_y); + change_x = this.mouse_delta_x | 0; + change_y = this.mouse_delta_y | 0; + } + while(force && (change_x || change_y)); + + return true; +}; + PS2.prototype.mouse_send_delta = function(delta_x, delta_y) { if(!this.have_mouse || !this.use_mouse) @@ -313,26 +353,7 @@ PS2.prototype.mouse_send_delta = function(delta_x, delta_y) this.mouse_delta_x += delta_x * factor; this.mouse_delta_y += delta_y * factor; - if(this.mouse_stream_active()) - { - var change_x = this.mouse_delta_x | 0, - change_y = this.mouse_delta_y | 0; - - if(change_x || change_y) - { - //var now = Date.now(); - //if(now - this.last_mouse_packet < 1000 / this.sample_rate) - //{ - // // TODO: set timeout - // return; - //} - - this.mouse_delta_x -= change_x; - this.mouse_delta_y -= change_y; - - this.send_mouse_packet(change_x, change_y); - } - } + this.flush_mouse_movement(false); }; PS2.prototype.mouse_send_click = function(left, middle, right) @@ -342,12 +363,16 @@ PS2.prototype.mouse_send_click = function(left, middle, right) return; } - this.mouse_clicks = left | right << 1 | middle << 2; + var clicks = left | right << 1 | middle << 2; if(this.mouse_stream_active()) { + // Motion received before this boundary belongs to the old buttons. + this.flush_mouse_movement(true); + this.mouse_clicks = clicks; this.send_mouse_packet(0, 0); } + this.mouse_clicks = clicks; }; PS2.prototype.send_mouse_packet = function(dx, dy) @@ -435,8 +460,13 @@ PS2.prototype.port60_read = function() if(this.next_byte_is_aux) { this.cpu.device_lower_irq(12); this.last_port60_byte = this.mouse_buffer.shift(); + if(!this.mouse_buffer.length) + { + // Queue the next sampled movement only after the guest catches up. + this.flush_mouse_movement(false); + } dbg_log("Port 60 read (mouse): " + h(this.last_port60_byte), LOG_PS2); } else {