From e171e8310bbfaa3942dc904928c3da2f60515f9b Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Mon, 19 Mar 2018 11:59:32 +0000 Subject: [PATCH] #1670: * refactor mouse handling so that we can send motion when the pointer is over the root window (wid=0) and not over an actual window * re-arrange code and add docstrings before each subsystem git-svn-id: https://xpra.org/svn/Xpra/trunk@18766 3bb7dfac-3a0b-4e04-842a-767bc560f471 --- src/html5/js/Client.js | 1050 ++++++++++++++++++++++++---------------- src/html5/js/Window.js | 164 +------ 2 files changed, 654 insertions(+), 560 deletions(-) diff --git a/src/html5/js/Client.js b/src/html5/js/Client.js index 917aba8a19..eca6ab6aa4 100644 --- a/src/html5/js/Client.js +++ b/src/html5/js/Client.js @@ -106,6 +106,12 @@ XpraClient.prototype.init_state = function(container) { this.browser_language = Utilities.getFirstBrowserLanguage(); this.browser_language_change_embargo_time = 0; this.key_layout = null; + // mouse + this.mousedown_event = null; + this.last_mouse_x = null; + this.last_mouse_y = null; + this.wheel_delta_x = 0; + this.wheel_delta_y = 0; // clipboard this.clipboard_buffer = ""; this.clipboard_pending = false; @@ -145,6 +151,30 @@ XpraClient.prototype.init_state = function(container) { this.topwindow = null; this.topindex = 0; this.focus = -1; + + jQuery("#screen").mousedown(function (e) { + me.on_mousedown(e); + }); + jQuery("#screen").mouseup(function (e) { + me.on_mouseup(e); + }); + jQuery("#screen").mousemove(function (e) { + me.on_mousemove(e); + }); + var me = this; + var div = document.getElementById("screen"); + function on_mousescroll(e) { + me.on_mousescroll(e); + } + if (Utilities.isEventSupported("wheel")) { + div.addEventListener('wheel', on_mousescroll, false); + } + else if (Utilities.isEventSupported("mousewheel")) { + div.addEventListener('mousewheel', on_mousescroll, false); + } + else if (Utilities.isEventSupported("DOMMouseScroll")) { + div.addEventListener('DOMMouseScroll', on_mousescroll, false); // for Firefox + } } XpraClient.prototype.send = function() { @@ -205,109 +235,6 @@ XpraClient.prototype.init = function(ignore_blacklist) { this.init_keyboard(); } -XpraClient.prototype.init_audio = function(ignore_audio_blacklist) { - this.debug("audio", "init_audio() enabled=", this.audio_enabled, ", mediasource enabled=", this.audio_mediasource_enabled, ", aurora enabled=", this.audio_aurora_enabled, ", http-stream enabled=", this.audio_httpstream_enabled); - if(!this.audio_enabled) { - return; - } - if(this.audio_mediasource_enabled) { - this.mediasource_codecs = MediaSourceUtil.getMediaSourceAudioCodecs(ignore_audio_blacklist); - for (var codec_option in this.mediasource_codecs) { - this.audio_codecs[codec_option] = this.mediasource_codecs[codec_option]; - } - } - if(this.audio_aurora_enabled) { - this.aurora_codecs = MediaSourceUtil.getAuroraAudioCodecs(); - for (var codec_option in this.aurora_codecs) { - if(codec_option in this.audio_codecs) { - //we already have native MediaSource support! - continue; - } - this.audio_codecs[codec_option] = this.aurora_codecs[codec_option]; - } - } - if (this.audio_httpstream_enabled) { - var stream_codecs = ["mp3"]; - for (var i in stream_codecs) { - var codec_option = stream_codecs[i]; - if (codec_option in this.audio_codecs) { - continue; - } - this.audio_codecs[codec_option] = codec_option; - } - } - this.debug("audio", "audio codecs:", this.audio_codecs); - if(!this.audio_codecs) { - this.audio_codec = null; - this.audio_enabled = false; - this.warn("no valid audio codecs found"); - return; - } - if(!(this.audio_codec in this.audio_codecs)) { - if(this.audio_codec) { - this.warn("invalid audio codec: "+this.audio_codec); - } - this.audio_codec = MediaSourceUtil.getDefaultAudioCodec(this.audio_codecs); - if(this.audio_codec) { - if(this.audio_mediasource_enabled && (this.audio_codec in this.mediasource_codecs)) { - this.audio_framework = "mediasource"; - } - else if (this.audio_aurora_enabled && !Utilities.isIE()) { - this.audio_framework = "aurora"; - } - else if (this.audio_httpstream_enabled) { - this.audio_framework = "http-stream"; - } - if (this.audio_framework) { - this.log("using "+this.audio_framework+" audio codec: "+this.audio_codec); - } - else { - this.warn("no valid audio framework - cannot enable audio"); - this.audio_enabled = false; - } - } - else { - this.warn("no valid audio codec found"); - this.audio_enabled = false; - } - } - else { - this.log("using "+this.audio_framework+" audio codec: "+this.audio_codec); - } - this.log("audio codecs: ", Object.keys(this.audio_codecs)); -} - -XpraClient.prototype.init_keyboard = function() { - var me = this; - // modifier keys: - this.caps_lock = null; - this.num_lock = true; - this.num_lock_mod = null; - this.alt_modifier = null; - this.meta_modifier = null; - // assign the keypress callbacks - // if we detect jQuery, use that to assign them instead - // to allow multiple clients on the same page - document.addEventListener('keydown', function(e) { - var r = me._keyb_onkeydown(e, me); - if (!r) { - e.preventDefault(); - } - }); - document.addEventListener('keyup', function (e) { - var r = me._keyb_onkeyup(e, me); - if (!r) { - e.preventDefault(); - } - }); - document.addEventListener('keypress', function (e) { - var r = me._keyb_onkeypress(e, me); - if (!r) { - e.preventDefault(); - } - }); -} - XpraClient.prototype.init_packet_handlers = function() { // the client holds a list of packet handlers @@ -541,6 +468,39 @@ XpraClient.prototype._screen_resized = function(event, ctx) { } } +/** + * Keyboard + */ +XpraClient.prototype.init_keyboard = function() { + var me = this; + // modifier keys: + this.caps_lock = null; + this.num_lock = true; + this.num_lock_mod = null; + this.alt_modifier = null; + this.meta_modifier = null; + // assign the keypress callbacks + // if we detect jQuery, use that to assign them instead + // to allow multiple clients on the same page + document.addEventListener('keydown', function(e) { + var r = me._keyb_onkeydown(e, me); + if (!r) { + e.preventDefault(); + } + }); + document.addEventListener('keyup', function (e) { + var r = me._keyb_onkeyup(e, me); + if (!r) { + e.preventDefault(); + } + }); + document.addEventListener('keypress', function (e) { + var r = me._keyb_onkeypress(e, me); + if (!r) { + e.preventDefault(); + } + }); +} XpraClient.prototype._keyb_get_modifiers = function(event) { /** @@ -871,6 +831,9 @@ XpraClient.prototype._update_capabilities = function(appendobj) { } } +/** + * Ping + */ XpraClient.prototype._check_server_echo = function(ping_sent_time) { var last = this.server_ok; this.server_ok = this.last_ping_echoed_time >= ping_sent_time; @@ -922,7 +885,9 @@ XpraClient.prototype._send_ping = function() { }, wait); } - +/** + * Hello + */ XpraClient.prototype._send_hello = function(challenge_response, client_salt) { // make the base hello this._make_hello_base(); @@ -1151,112 +1116,171 @@ XpraClient.prototype._new_ui_event = function() { this.ui_events++; } -/* - * Window callbacks +/** + * Mouse handlers */ - -XpraClient.prototype._new_window = function(wid, x, y, w, h, metadata, override_redirect, client_properties) { - // each window needs their own DIV that contains a canvas - var mydiv = document.createElement("div"); - mydiv.id = String(wid); - var mycanvas = document.createElement("canvas"); - mydiv.appendChild(mycanvas); - var screen = document.getElementById("screen"); - screen.appendChild(mydiv); - // set initial sizes - mycanvas.width = w; - mycanvas.height = h; - // create the XpraWindow object to own the new div - var win = new XpraWindow(this, mycanvas, wid, x, y, w, h, - metadata, - override_redirect, - false, - client_properties, - this._window_geometry_changed, - this._window_mouse_move, - this._window_mouse_click, - this._window_set_focus, - this._window_closed - ); - this.id_to_window[wid] = win; - if (!override_redirect) { - var geom = win.get_internal_geometry(); - this.send(["map-window", wid, geom.x, geom.y, geom.w, geom.h, this._get_client_properties(win)]); - this._window_set_focus(win); +XpraClient.prototype.getMouse = function(e, window) { + // get mouse position take into account scroll + var mx = e.clientX + jQuery(document).scrollLeft(); + var my = e.clientY + jQuery(document).scrollTop(); + + // check last mouse position incase the event + // hasn't provided it - bug #854 + if(isNaN(mx) || isNaN(my)) { + if(!isNaN(this.last_mouse_x) && !isNaN(this.last_mouse_y)) { + mx = this.last_mouse_x; + my = this.last_mouse_y; + } else { + // should we avoid sending NaN to the server? + mx = 0; + my = 0; + } + } else { + this.last_mouse_x = mx; + this.last_mouse_y = my; } -} -XpraClient.prototype._new_window_common = function(packet, override_redirect) { - var wid, x, y, w, h, metadata; - wid = packet[1]; - x = packet[2]; - y = packet[3]; - w = packet[4]; - h = packet[5]; - metadata = packet[6]; - if (wid in this.id_to_window) - throw "we already have a window " + wid; - if (w<=0 || h<=0) { - this.error("window dimensions are wrong: "+w+"x"+h); - w, h = 1, 1; - } - var client_properties = {} - if (packet.length>=8) - client_properties = packet[7]; - if (x==0 && y==0 && !metadata["set-initial-position"]) { - //find a good position for it - var l = Object.keys(this.id_to_window).length; - if (l==0) { - //first window: center it - x = Math.round((this.desktop_width-w)/2); - if (w=40 && apx<=160) { + this.wheel_delta_x = (px>0) ? 120 : -120; + } + else { + this.wheel_delta_x += px; + } + if (apy>=40 && apy<=160) { + this.wheel_delta_y = (py>0) ? 120 : -120; + } + else { + this.wheel_delta_y += py; } - client.send(["button-action", wid, button, pressed, [x, y], modifiers, buttons]); + //send synthetic click+release as many times as needed: + var wx = Math.abs(this.wheel_delta_x); + var wy = Math.abs(this.wheel_delta_y); + var btn_x = (this.wheel_delta_x>=0) ? 6 : 7; + var btn_y = (this.wheel_delta_y>=0) ? 5 : 4; + var wid = 0; + if (window) { + wid = window.wid; + } + while (wx>=120) { + wx -= 120; + this.send(["button-action", wid, btn_x, true, [x, y], modifiers, buttons]); + this.send(["button-action", wid, btn_x, false, [x, y], modifiers, buttons]); + } + while (wy>=120) { + wy -= 120; + this.send(["button-action", wid, btn_y, true, [x, y], modifiers, buttons]); + this.send(["button-action", wid, btn_y, false, [x, y], modifiers, buttons]); + } + //store left overs: + this.wheel_delta_x = (this.wheel_delta_x>=0) ? wx : -wx; + this.wheel_delta_y = (this.wheel_delta_y>=0) ? wy : -wy; } + +/** + * Focus + */ XpraClient.prototype._window_set_focus = function(win) { // don't send focus packet for override_redirect windows! if (win.override_redirect || win.tray) { @@ -1293,197 +1317,9 @@ XpraClient.prototype._window_set_focus = function(win) { //client._set_favicon(wid); } - -XpraClient.prototype._sound_start_receiving = function() { - try { - this.audio_buffers = []; - this.audio_buffers_count = 0; - if (this.audio_framework=="http-stream") { - this._sound_start_httpstream(); - } - else if (this.audio_framework=="mediasource") { - this._sound_start_mediasource(); - } - else { - this._sound_start_aurora(); - } - } - catch(e) { - this.error('error starting audio player: '+e); - } -} - - -XpraClient.prototype._send_sound_start = function() { - this.log("audio: requesting "+this.audio_codec+" stream from the server"); - this.send(["sound-control", "start", this.audio_codec]); -} - - -XpraClient.prototype._sound_start_httpstream = function() { - this.audio = document.createElement("audio"); - this.audio.setAttribute('autoplay', true); - this.audio.setAttribute('controls', false); - this.audio.setAttribute('loop', true); - var url = "http"; - if (this.ssl) { - url = "https"; - } - url += "://"+this.host+":"+this.port; - if (this.path) { - url += "/"+this.path; - } - url += "/audio.mp3?uuid="+this.uuid; - this.log("starting http stream from", url); - this.audio.src = url; -} - -XpraClient.prototype._sound_start_aurora = function() { - this.audio_aurora_ctx = AV.Player.fromXpraSource(); - this._send_sound_start(); -} - -XpraClient.prototype._sound_start_mediasource = function() { - var me = this; - - function audio_error(event) { - if(me.audio) { - me.error(event+" error: "+me.audio.error); - if(me.audio.error) { - me.error(MediaSourceConstants.ERROR_CODE[me.audio.error.code]); - } - } - else { - me.error(event+" error"); - } - me.close_audio(); - } - - //Create a MediaSource: - this.media_source = MediaSourceUtil.getMediaSource(); - if(this.debug) { - MediaSourceUtil.addMediaSourceEventDebugListeners(this.media_source, "audio"); - } - this.media_source.addEventListener('error', function(e) {audio_error("audio source"); }); - - //Create an