1 /**************************************************************************** 2 Copyright (c) 2011-2012 cocos2d-x.org 3 Copyright (c) 2013-2014 Chukong Technologies Inc. 4 5 http://www.cocos2d-x.org 6 7 Permission is hereby granted, free of charge, to any person obtaining a copy 8 of this software and associated documentation files (the "Software"), to deal 9 in the Software without restriction, including without limitation the rights 10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 copies of the Software, and to permit persons to whom the Software is 12 furnished to do so, subject to the following conditions: 13 14 The above copyright notice and this permission notice shall be included in 15 all copies or substantial portions of the Software. 16 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 THE SOFTWARE. 24 ****************************************************************************/ 25 26 /** 27 * ignore 28 */ 29 30 /** 31 * @constant 32 * @type {number} 33 */ 34 cc.UIInterfaceOrientationLandscapeLeft = -90; 35 /** 36 * @constant 37 * @type {number} 38 */ 39 cc.UIInterfaceOrientationLandscapeRight = 90; 40 /** 41 * @constant 42 * @type {number} 43 */ 44 cc.UIInterfaceOrientationPortraitUpsideDown = 180; 45 /** 46 * @constant 47 * @type {number} 48 */ 49 cc.UIInterfaceOrientationPortrait = 0; 50 51 /** 52 * <p> 53 * This class manages all events of input. include: touch, mouse, accelerometer, keyboard <br/> 54 * </p> 55 * @class 56 * @name cc.inputManager 57 */ 58 cc.inputManager = /** @lends cc.inputManager# */{ 59 _mousePressed: false, 60 61 _isRegisterEvent: false, 62 63 _preTouchPoint: cc.p(0,0), 64 _prevMousePoint: cc.p(0,0), 65 66 _preTouchPool: [], 67 _preTouchPoolPointer: 0, 68 69 _touches: [], 70 _touchesIntegerDict:{}, 71 72 _indexBitsUsed: 0, 73 _maxTouches: 5, 74 75 _accelEnabled: false, 76 _accelInterval: 1/30, 77 _accelMinus: 1, 78 _accelCurTime: 0, 79 _acceleration: null, 80 _accelDeviceEvent: null, 81 82 _getUnUsedIndex: function () { 83 var temp = this._indexBitsUsed; 84 85 for (var i = 0; i < this._maxTouches; i++) { 86 if (!(temp & 0x00000001)) { 87 this._indexBitsUsed |= (1 << i); 88 return i; 89 } 90 temp >>= 1; 91 } 92 93 // all bits are used 94 return -1; 95 }, 96 97 _removeUsedIndexBit: function (index) { 98 if (index < 0 || index >= this._maxTouches) 99 return; 100 101 var temp = 1 << index; 102 temp = ~temp; 103 this._indexBitsUsed &= temp; 104 }, 105 106 _glView: null, 107 108 /** 109 * @function 110 * @param {Array} touches 111 */ 112 handleTouchesBegin: function (touches) { 113 var selTouch, index, curTouch, touchID, handleTouches = [], locTouchIntDict = this._touchesIntegerDict; 114 for(var i = 0, len = touches.length; i< len; i ++){ 115 selTouch = touches[i]; 116 touchID = selTouch.getID(); 117 index = locTouchIntDict[touchID]; 118 119 if(index == null){ 120 var unusedIndex = this._getUnUsedIndex(); 121 if (unusedIndex === -1) { 122 cc.log(cc._LogInfos.inputManager_handleTouchesBegin, unusedIndex); 123 continue; 124 } 125 //curTouch = this._touches[unusedIndex] = selTouch; 126 curTouch = this._touches[unusedIndex] = new cc.Touch(selTouch._point.x, selTouch._point.y, selTouch.getID()); 127 curTouch._setPrevPoint(selTouch._prevPoint); 128 locTouchIntDict[touchID] = unusedIndex; 129 handleTouches.push(curTouch); 130 } 131 } 132 if(handleTouches.length > 0){ 133 this._glView._convertTouchesWithScale(handleTouches); 134 var touchEvent = new cc.EventTouch(handleTouches); 135 touchEvent._eventCode = cc.EventTouch.EventCode.BEGAN; 136 cc.eventManager.dispatchEvent(touchEvent); 137 } 138 }, 139 140 /** 141 * @function 142 * @param {Array} touches 143 */ 144 handleTouchesMove: function(touches){ 145 var selTouch, index, touchID, handleTouches = [], locTouches = this._touches; 146 for(var i = 0, len = touches.length; i< len; i ++){ 147 selTouch = touches[i]; 148 touchID = selTouch.getID(); 149 index = this._touchesIntegerDict[touchID]; 150 151 if(index == null){ 152 //cc.log("if the index doesn't exist, it is an error"); 153 continue; 154 } 155 if(locTouches[index]){ 156 locTouches[index]._setPoint(selTouch._point); 157 locTouches[index]._setPrevPoint(selTouch._prevPoint); 158 handleTouches.push(locTouches[index]); 159 } 160 } 161 if(handleTouches.length > 0){ 162 this._glView._convertTouchesWithScale(handleTouches); 163 var touchEvent = new cc.EventTouch(handleTouches); 164 touchEvent._eventCode = cc.EventTouch.EventCode.MOVED; 165 cc.eventManager.dispatchEvent(touchEvent); 166 } 167 }, 168 169 /** 170 * @function 171 * @param {Array} touches 172 */ 173 handleTouchesEnd: function(touches){ 174 var handleTouches = this.getSetOfTouchesEndOrCancel(touches); 175 if(handleTouches.length > 0) { 176 this._glView._convertTouchesWithScale(handleTouches); 177 var touchEvent = new cc.EventTouch(handleTouches); 178 touchEvent._eventCode = cc.EventTouch.EventCode.ENDED; 179 cc.eventManager.dispatchEvent(touchEvent); 180 } 181 }, 182 183 /** 184 * @function 185 * @param {Array} touches 186 */ 187 handleTouchesCancel: function(touches){ 188 var handleTouches = this.getSetOfTouchesEndOrCancel(touches); 189 if(handleTouches.length > 0) { 190 this._glView._convertTouchesWithScale(handleTouches); 191 var touchEvent = new cc.EventTouch(handleTouches); 192 touchEvent._eventCode = cc.EventTouch.EventCode.CANCELLED; 193 cc.eventManager.dispatchEvent(touchEvent); 194 } 195 }, 196 197 /** 198 * @function 199 * @param {Array} touches 200 * @returns {Array} 201 */ 202 getSetOfTouchesEndOrCancel: function(touches) { 203 var selTouch, index, touchID, handleTouches = [], locTouches = this._touches, locTouchesIntDict = this._touchesIntegerDict; 204 for(var i = 0, len = touches.length; i< len; i ++){ 205 selTouch = touches[i]; 206 touchID = selTouch.getID(); 207 index = locTouchesIntDict[touchID]; 208 209 if(index == null){ 210 continue; //cc.log("if the index doesn't exist, it is an error"); 211 } 212 if(locTouches[index]){ 213 locTouches[index]._setPoint(selTouch._point); 214 locTouches[index]._setPrevPoint(selTouch._prevPoint); 215 handleTouches.push(locTouches[index]); 216 this._removeUsedIndexBit(index); 217 delete locTouchesIntDict[touchID]; 218 } 219 } 220 return handleTouches; 221 }, 222 223 /** 224 * @function 225 * @param {HTMLElement} element 226 * @return {Object} 227 */ 228 getHTMLElementPosition: function (element) { 229 var docElem = document.documentElement; 230 var win = window; 231 var box = null; 232 if (cc.isFunction(element.getBoundingClientRect)) { 233 box = element.getBoundingClientRect(); 234 } else { 235 box = { 236 left: 0, 237 top: 0, 238 width: parseInt(element.style.width), 239 height: parseInt(element.style.height) 240 }; 241 } 242 return { 243 left: box.left + win.pageXOffset - docElem.clientLeft, 244 top: box.top + win.pageYOffset - docElem.clientTop, 245 width: box.width, 246 height: box.height 247 }; 248 }, 249 250 /** 251 * @function 252 * @param {cc.Touch} touch 253 * @return {cc.Touch} 254 */ 255 getPreTouch: function(touch){ 256 var preTouch = null; 257 var locPreTouchPool = this._preTouchPool; 258 var id = touch.getID(); 259 for (var i = locPreTouchPool.length - 1; i >= 0; i--) { 260 if (locPreTouchPool[i].getID() === id) { 261 preTouch = locPreTouchPool[i]; 262 break; 263 } 264 } 265 if (!preTouch) 266 preTouch = touch; 267 return preTouch; 268 }, 269 270 /** 271 * @function 272 * @param {cc.Touch} touch 273 */ 274 setPreTouch: function(touch){ 275 var find = false; 276 var locPreTouchPool = this._preTouchPool; 277 var id = touch.getID(); 278 for (var i = locPreTouchPool.length - 1; i >= 0; i--) { 279 if (locPreTouchPool[i].getID() === id) { 280 locPreTouchPool[i] = touch; 281 find = true; 282 break; 283 } 284 } 285 if (!find) { 286 if (locPreTouchPool.length <= 50) { 287 locPreTouchPool.push(touch); 288 } else { 289 locPreTouchPool[this._preTouchPoolPointer] = touch; 290 this._preTouchPoolPointer = (this._preTouchPoolPointer + 1) % 50; 291 } 292 } 293 }, 294 295 /** 296 * @function 297 * @param {Number} tx 298 * @param {Number} ty 299 * @param {cc.Point} pos 300 * @return {cc.Touch} 301 */ 302 getTouchByXY: function(tx, ty, pos){ 303 var locPreTouch = this._preTouchPoint; 304 var location = this._glView.convertToLocationInView(tx, ty, pos); 305 var touch = new cc.Touch(location.x, location.y); 306 touch._setPrevPoint(locPreTouch.x, locPreTouch.y); 307 locPreTouch.x = location.x; 308 locPreTouch.y = location.y; 309 return touch; 310 }, 311 312 /** 313 * @function 314 * @param {cc.Point} location 315 * @param {cc.Point} pos 316 * @param {Number} eventType 317 * @returns {cc.EventMouse} 318 */ 319 getMouseEvent: function(location, pos, eventType){ 320 var locPreMouse = this._prevMousePoint; 321 this._glView._convertMouseToLocationInView(location, pos); 322 var mouseEvent = new cc.EventMouse(eventType); 323 mouseEvent.setLocation(location.x, location.y); 324 mouseEvent._setPrevCursor(locPreMouse.x, locPreMouse.y); 325 locPreMouse.x = location.x; 326 locPreMouse.y = location.y; 327 return mouseEvent; 328 }, 329 330 /** 331 * @function 332 * @param {Touch} event 333 * @param {cc.Point} pos 334 * @return {cc.Point} 335 */ 336 getPointByEvent: function(event, pos){ 337 if (event.pageX != null) //not available in <= IE8 338 return {x: event.pageX, y: event.pageY}; 339 340 pos.left -= document.body.scrollLeft; 341 pos.top -= document.body.scrollTop; 342 return {x: event.clientX, y: event.clientY}; 343 }, 344 345 /** 346 * @function 347 * @param {Touch} event 348 * @param {cc.Point} pos 349 * @returns {Array} 350 */ 351 getTouchesByEvent: function(event, pos){ 352 var touchArr = [], locView = this._glView; 353 var touch_event, touch, preLocation; 354 var locPreTouch = this._preTouchPoint; 355 356 var length = event.changedTouches.length; 357 for (var i = 0; i < length; i++) { 358 touch_event = event.changedTouches[i]; 359 if (touch_event) { 360 var location; 361 if (cc.sys.BROWSER_TYPE_FIREFOX === cc.sys.browserType) 362 location = locView.convertToLocationInView(touch_event.pageX, touch_event.pageY, pos); 363 else 364 location = locView.convertToLocationInView(touch_event.clientX, touch_event.clientY, pos); 365 if (touch_event.identifier != null) { 366 touch = new cc.Touch(location.x, location.y, touch_event.identifier); 367 //use Touch Pool 368 preLocation = this.getPreTouch(touch).getLocation(); 369 touch._setPrevPoint(preLocation.x, preLocation.y); 370 this.setPreTouch(touch); 371 } else { 372 touch = new cc.Touch(location.x, location.y); 373 touch._setPrevPoint(locPreTouch.x, locPreTouch.y); 374 } 375 locPreTouch.x = location.x; 376 locPreTouch.y = location.y; 377 touchArr.push(touch); 378 } 379 } 380 return touchArr; 381 }, 382 383 /** 384 * @function 385 * @param {HTMLElement} element 386 */ 387 registerSystemEvent: function(element){ 388 if(this._isRegisterEvent) return; 389 390 var locView = this._glView = cc.view; 391 var selfPointer = this; 392 var supportMouse = ('mouse' in cc.sys.capabilities), supportTouches = ('touches' in cc.sys.capabilities); 393 394 //HACK 395 // - At the same time to trigger the ontouch event and onmouse event 396 // - The function will execute 2 times 397 //The known browser: 398 // liebiao 399 // miui 400 // WECHAT 401 var prohibition = false; 402 if( cc.sys.isMobile) 403 prohibition = true; 404 405 //register touch event 406 if (supportMouse) { 407 window.addEventListener('mousedown', function () { 408 selfPointer._mousePressed = true; 409 }, false); 410 411 window.addEventListener('mouseup', function (event) { 412 if(prohibition) return; 413 var savePressed = selfPointer._mousePressed; 414 selfPointer._mousePressed = false; 415 416 if(!savePressed) 417 return; 418 419 var pos = selfPointer.getHTMLElementPosition(element); 420 var location = selfPointer.getPointByEvent(event, pos); 421 if (!cc.rectContainsPoint(new cc.Rect(pos.left, pos.top, pos.width, pos.height), location)){ 422 selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]); 423 424 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.UP); 425 mouseEvent.setButton(event.button); 426 cc.eventManager.dispatchEvent(mouseEvent); 427 } 428 }, false); 429 430 //register canvas mouse event 431 element.addEventListener("mousedown", function (event) { 432 if(prohibition) return; 433 selfPointer._mousePressed = true; 434 435 var pos = selfPointer.getHTMLElementPosition(element); 436 var location = selfPointer.getPointByEvent(event, pos); 437 438 selfPointer.handleTouchesBegin([selfPointer.getTouchByXY(location.x, location.y, pos)]); 439 440 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.DOWN); 441 mouseEvent.setButton(event.button); 442 cc.eventManager.dispatchEvent(mouseEvent); 443 444 event.stopPropagation(); 445 event.preventDefault(); 446 element.focus(); 447 }, false); 448 449 element.addEventListener("mouseup", function (event) { 450 if(prohibition) return; 451 selfPointer._mousePressed = false; 452 453 var pos = selfPointer.getHTMLElementPosition(element); 454 var location = selfPointer.getPointByEvent(event, pos); 455 456 selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]); 457 458 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.UP); 459 mouseEvent.setButton(event.button); 460 cc.eventManager.dispatchEvent(mouseEvent); 461 462 event.stopPropagation(); 463 event.preventDefault(); 464 }, false); 465 466 element.addEventListener("mousemove", function (event) { 467 if(prohibition) return; 468 469 var pos = selfPointer.getHTMLElementPosition(element); 470 var location = selfPointer.getPointByEvent(event, pos); 471 472 selfPointer.handleTouchesMove([selfPointer.getTouchByXY(location.x, location.y, pos)]); 473 474 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.MOVE); 475 if(selfPointer._mousePressed) 476 mouseEvent.setButton(event.button); 477 else 478 mouseEvent.setButton(null); 479 cc.eventManager.dispatchEvent(mouseEvent); 480 481 event.stopPropagation(); 482 event.preventDefault(); 483 }, false); 484 485 element.addEventListener("mousewheel", function (event) { 486 var pos = selfPointer.getHTMLElementPosition(element); 487 var location = selfPointer.getPointByEvent(event, pos); 488 489 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.SCROLL); 490 mouseEvent.setButton(event.button); 491 mouseEvent.setScrollData(0, event.wheelDelta); 492 cc.eventManager.dispatchEvent(mouseEvent); 493 494 event.stopPropagation(); 495 event.preventDefault(); 496 }, false); 497 498 /* firefox fix */ 499 element.addEventListener("DOMMouseScroll", function(event) { 500 var pos = selfPointer.getHTMLElementPosition(element); 501 var location = selfPointer.getPointByEvent(event, pos); 502 503 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.SCROLL); 504 mouseEvent.setButton(event.button); 505 mouseEvent.setScrollData(0, event.detail * -120); 506 cc.eventManager.dispatchEvent(mouseEvent); 507 508 event.stopPropagation(); 509 event.preventDefault(); 510 }, false); 511 } 512 513 if(window.navigator.msPointerEnabled){ 514 var _pointerEventsMap = { 515 "MSPointerDown" : selfPointer.handleTouchesBegin, 516 "MSPointerMove" : selfPointer.handleTouchesMove, 517 "MSPointerUp" : selfPointer.handleTouchesEnd, 518 "MSPointerCancel" : selfPointer.handleTouchesCancel 519 }; 520 521 for(var eventName in _pointerEventsMap){ 522 (function(_pointerEvent, _touchEvent){ 523 element.addEventListener(_pointerEvent, function (event){ 524 var pos = selfPointer.getHTMLElementPosition(element); 525 pos.left -= document.documentElement.scrollLeft; 526 pos.top -= document.documentElement.scrollTop; 527 528 _touchEvent.call(selfPointer, [selfPointer.getTouchByXY(event.clientX, event.clientY, pos)]); 529 event.stopPropagation(); 530 }, false); 531 })(eventName, _pointerEventsMap[eventName]); 532 } 533 } 534 535 if(supportTouches) { 536 //register canvas touch event 537 element.addEventListener("touchstart", function (event) { 538 if (!event.changedTouches) return; 539 540 var pos = selfPointer.getHTMLElementPosition(element); 541 pos.left -= document.body.scrollLeft; 542 pos.top -= document.body.scrollTop; 543 selfPointer.handleTouchesBegin(selfPointer.getTouchesByEvent(event, pos)); 544 event.stopPropagation(); 545 event.preventDefault(); 546 element.focus(); 547 }, false); 548 549 element.addEventListener("touchmove", function (event) { 550 if (!event.changedTouches) return; 551 552 var pos = selfPointer.getHTMLElementPosition(element); 553 pos.left -= document.body.scrollLeft; 554 pos.top -= document.body.scrollTop; 555 selfPointer.handleTouchesMove(selfPointer.getTouchesByEvent(event, pos)); 556 event.stopPropagation(); 557 event.preventDefault(); 558 }, false); 559 560 element.addEventListener("touchend", function (event) { 561 if (!event.changedTouches) return; 562 563 var pos = selfPointer.getHTMLElementPosition(element); 564 pos.left -= document.body.scrollLeft; 565 pos.top -= document.body.scrollTop; 566 selfPointer.handleTouchesEnd(selfPointer.getTouchesByEvent(event, pos)); 567 event.stopPropagation(); 568 event.preventDefault(); 569 }, false); 570 571 element.addEventListener("touchcancel", function (event) { 572 if (!event.changedTouches) return; 573 574 var pos = selfPointer.getHTMLElementPosition(element); 575 pos.left -= document.body.scrollLeft; 576 pos.top -= document.body.scrollTop; 577 selfPointer.handleTouchesCancel(selfPointer.getTouchesByEvent(event, pos)); 578 event.stopPropagation(); 579 event.preventDefault(); 580 }, false); 581 } 582 583 //register keyboard event 584 this._registerKeyboardEvent(); 585 586 //register Accelerometer event 587 this._registerAccelerometerEvent(); 588 589 this._isRegisterEvent = true; 590 }, 591 592 _registerKeyboardEvent: function(){}, 593 594 _registerAccelerometerEvent: function(){}, 595 596 /** 597 * @function 598 * @param {Number} dt 599 */ 600 update:function(dt){ 601 if(this._accelCurTime > this._accelInterval){ 602 this._accelCurTime -= this._accelInterval; 603 cc.eventManager.dispatchEvent(new cc.EventAcceleration(this._acceleration)); 604 } 605 this._accelCurTime += dt; 606 } 607 }; 608