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 (typeof element.getBoundingClientRect === 'function') { 233 box = element.getBoundingClientRect(); 234 } else { 235 if (element instanceof HTMLCanvasElement) { 236 box = { 237 left: 0, 238 top: 0, 239 width: element.width, 240 height: element.height 241 }; 242 } else { 243 box = { 244 left: 0, 245 top: 0, 246 width: parseInt(element.style.width), 247 height: parseInt(element.style.height) 248 }; 249 } 250 } 251 return { 252 left: box.left + win.pageXOffset - docElem.clientLeft, 253 top: box.top + win.pageYOffset - docElem.clientTop, 254 width: box.width, 255 height: box.height 256 }; 257 }, 258 259 /** 260 * @function 261 * @param {cc.Touch} touch 262 * @return {cc.Touch} 263 */ 264 getPreTouch: function(touch){ 265 var preTouch = null; 266 var locPreTouchPool = this._preTouchPool; 267 var id = touch.getID(); 268 for (var i = locPreTouchPool.length - 1; i >= 0; i--) { 269 if (locPreTouchPool[i].getID() == id) { 270 preTouch = locPreTouchPool[i]; 271 break; 272 } 273 } 274 if (!preTouch) 275 preTouch = touch; 276 return preTouch; 277 }, 278 279 /** 280 * @function 281 * @param {cc.Touch} touch 282 */ 283 setPreTouch: function(touch){ 284 var find = false; 285 var locPreTouchPool = this._preTouchPool; 286 var id = touch.getID(); 287 for (var i = locPreTouchPool.length - 1; i >= 0; i--) { 288 if (locPreTouchPool[i].getID() == id) { 289 locPreTouchPool[i] = touch; 290 find = true; 291 break; 292 } 293 } 294 if (!find) { 295 if (locPreTouchPool.length <= 50) { 296 locPreTouchPool.push(touch); 297 } else { 298 locPreTouchPool[this._preTouchPoolPointer] = touch; 299 this._preTouchPoolPointer = (this._preTouchPoolPointer + 1) % 50; 300 } 301 } 302 }, 303 304 /** 305 * @function 306 * @param {Number} tx 307 * @param {Number} ty 308 * @param {cc.Point} pos 309 * @return {cc.Touch} 310 */ 311 getTouchByXY: function(tx, ty, pos){ 312 var locPreTouch = this._preTouchPoint; 313 var location = this._glView.convertToLocationInView(tx, ty, pos); 314 var touch = new cc.Touch(location.x, location.y); 315 touch._setPrevPoint(locPreTouch.x, locPreTouch.y); 316 locPreTouch.x = location.x; 317 locPreTouch.y = location.y; 318 return touch; 319 }, 320 321 /** 322 * @function 323 * @param {cc.Point} location 324 * @param {cc.Point} pos 325 * @param {Number} eventType 326 * @returns {cc.EventMouse} 327 */ 328 getMouseEvent: function(location, pos, eventType){ 329 var locPreMouse = this._prevMousePoint; 330 this._glView._convertMouseToLocationInView(location, pos); 331 var mouseEvent = new cc.EventMouse(eventType); 332 mouseEvent.setLocation(location.x, location.y); 333 mouseEvent._setPrevCursor(locPreMouse.x, locPreMouse.y); 334 locPreMouse.x = location.x; 335 locPreMouse.y = location.y; 336 return mouseEvent; 337 }, 338 339 /** 340 * @function 341 * @param {Touch} event 342 * @param {cc.Point} pos 343 * @return {cc.Point} 344 */ 345 getPointByEvent: function(event, pos){ 346 if (event.pageX != null) //not avalable in <= IE8 347 return {x: event.pageX, y: event.pageY}; 348 349 pos.left -= document.body.scrollLeft; 350 pos.top -= document.body.scrollTop; 351 return {x: event.clientX, y: event.clientY}; 352 }, 353 354 /** 355 * @function 356 * @param {Touch} event 357 * @param {cc.Point} pos 358 * @returns {Array} 359 */ 360 getTouchesByEvent: function(event, pos){ 361 var touchArr = [], locView = this._glView; 362 var touch_event, touch, preLocation; 363 var locPreTouch = this._preTouchPoint; 364 365 var length = event.changedTouches.length; 366 for (var i = 0; i < length; i++) { 367 touch_event = event.changedTouches[i]; 368 if (touch_event) { 369 var location; 370 if (cc.sys.BROWSER_TYPE_FIREFOX === cc.sys.browserType) 371 location = locView.convertToLocationInView(touch_event.pageX, touch_event.pageY, pos); 372 else 373 location = locView.convertToLocationInView(touch_event.clientX, touch_event.clientY, pos); 374 if (touch_event.identifier != null) { 375 touch = new cc.Touch(location.x, location.y, touch_event.identifier); 376 //use Touch Pool 377 preLocation = this.getPreTouch(touch).getLocation(); 378 touch._setPrevPoint(preLocation.x, preLocation.y); 379 this.setPreTouch(touch); 380 } else { 381 touch = new cc.Touch(location.x, location.y); 382 touch._setPrevPoint(locPreTouch.x, locPreTouch.y); 383 } 384 locPreTouch.x = location.x; 385 locPreTouch.y = location.y; 386 touchArr.push(touch); 387 } 388 } 389 return touchArr; 390 }, 391 392 /** 393 * @function 394 * @param {HTMLElement} element 395 */ 396 registerSystemEvent: function(element){ 397 if(this._isRegisterEvent) return; 398 399 var locView = this._glView = cc.view; 400 var selfPointer = this; 401 var supportMouse = ('mouse' in cc.sys.capabilities), supportTouches = ('touches' in cc.sys.capabilities); 402 403 //register touch event 404 if (supportMouse) { 405 cc._addEventListener(window, 'mousedown', function () { 406 selfPointer._mousePressed = true; 407 }, false); 408 409 cc._addEventListener(window, 'mouseup', function (event) { 410 var savePressed = selfPointer._mousePressed; 411 selfPointer._mousePressed = false; 412 413 if(!savePressed) 414 return; 415 416 var pos = selfPointer.getHTMLElementPosition(element); 417 var location = selfPointer.getPointByEvent(event, pos); 418 if (!cc.rectContainsPoint(new cc.Rect(pos.left, pos.top, pos.width, pos.height), location)){ 419 if(!supportTouches) 420 selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]); 421 422 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.UP); 423 mouseEvent.setButton(event.button); 424 cc.eventManager.dispatchEvent(mouseEvent); 425 } 426 }, false); 427 428 //register canvas mouse event 429 cc._addEventListener(element,"mousedown", function (event) { 430 selfPointer._mousePressed = true; 431 432 var pos = selfPointer.getHTMLElementPosition(element); 433 var location = selfPointer.getPointByEvent(event, pos); 434 435 if(!supportTouches) 436 selfPointer.handleTouchesBegin([selfPointer.getTouchByXY(location.x, location.y, pos)]); 437 438 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.DOWN); 439 mouseEvent.setButton(event.button); 440 cc.eventManager.dispatchEvent(mouseEvent); 441 442 event.stopPropagation(); 443 event.preventDefault(); 444 element.focus(); 445 }, false); 446 447 cc._addEventListener(element, "mouseup", function (event) { 448 selfPointer._mousePressed = false; 449 450 var pos = selfPointer.getHTMLElementPosition(element); 451 var location = selfPointer.getPointByEvent(event, pos); 452 453 if(!supportTouches) 454 selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]); 455 456 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.UP); 457 mouseEvent.setButton(event.button); 458 cc.eventManager.dispatchEvent(mouseEvent); 459 460 event.stopPropagation(); 461 event.preventDefault(); 462 }, false); 463 464 cc._addEventListener(element, "mousemove", function (event) { 465 //if(!selfPointer._mousePressed) 466 // return; 467 468 var pos = selfPointer.getHTMLElementPosition(element); 469 var location = selfPointer.getPointByEvent(event, pos); 470 471 if(!supportTouches) 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 cc._addEventListener(element, "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 cc._addEventListener(element, "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 cc._addEventListener(element, _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 cc._addEventListener(element,"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 cc._addEventListener(element, "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 cc._addEventListener(element, "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 cc._addEventListener(element, "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 locView.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