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 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 //HACK 404 // - At the same time to trigger the ontouch event and onmouse event 405 // - The function will execute 2 times 406 //The known browser: 407 // liebiao 408 // miui 409 // WECHAT 410 var prohibition = false; 411 if( cc.sys.isMobile) 412 prohibition = true; 413 414 //register touch event 415 if (supportMouse) { 416 window.addEventListener('mousedown', function () { 417 selfPointer._mousePressed = true; 418 }, false); 419 420 window.addEventListener('mouseup', function (event) { 421 if(prohibition) return; 422 var savePressed = selfPointer._mousePressed; 423 selfPointer._mousePressed = false; 424 425 if(!savePressed) 426 return; 427 428 var pos = selfPointer.getHTMLElementPosition(element); 429 var location = selfPointer.getPointByEvent(event, pos); 430 if (!cc.rectContainsPoint(new cc.Rect(pos.left, pos.top, pos.width, pos.height), location)){ 431 selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]); 432 433 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.UP); 434 mouseEvent.setButton(event.button); 435 cc.eventManager.dispatchEvent(mouseEvent); 436 } 437 }, false); 438 439 //register canvas mouse event 440 element.addEventListener("mousedown", function (event) { 441 if(prohibition) return; 442 selfPointer._mousePressed = true; 443 444 var pos = selfPointer.getHTMLElementPosition(element); 445 var location = selfPointer.getPointByEvent(event, pos); 446 447 selfPointer.handleTouchesBegin([selfPointer.getTouchByXY(location.x, location.y, pos)]); 448 449 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.DOWN); 450 mouseEvent.setButton(event.button); 451 cc.eventManager.dispatchEvent(mouseEvent); 452 453 event.stopPropagation(); 454 event.preventDefault(); 455 element.focus(); 456 }, false); 457 458 element.addEventListener("mouseup", function (event) { 459 if(prohibition) return; 460 selfPointer._mousePressed = false; 461 462 var pos = selfPointer.getHTMLElementPosition(element); 463 var location = selfPointer.getPointByEvent(event, pos); 464 465 selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]); 466 467 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.UP); 468 mouseEvent.setButton(event.button); 469 cc.eventManager.dispatchEvent(mouseEvent); 470 471 event.stopPropagation(); 472 event.preventDefault(); 473 }, false); 474 475 element.addEventListener("mousemove", function (event) { 476 if(prohibition) return; 477 478 var pos = selfPointer.getHTMLElementPosition(element); 479 var location = selfPointer.getPointByEvent(event, pos); 480 481 selfPointer.handleTouchesMove([selfPointer.getTouchByXY(location.x, location.y, pos)]); 482 483 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.MOVE); 484 if(selfPointer._mousePressed) 485 mouseEvent.setButton(event.button); 486 else 487 mouseEvent.setButton(null); 488 cc.eventManager.dispatchEvent(mouseEvent); 489 490 event.stopPropagation(); 491 event.preventDefault(); 492 }, false); 493 494 element.addEventListener("mousewheel", function (event) { 495 var pos = selfPointer.getHTMLElementPosition(element); 496 var location = selfPointer.getPointByEvent(event, pos); 497 498 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.SCROLL); 499 mouseEvent.setButton(event.button); 500 mouseEvent.setScrollData(0, event.wheelDelta); 501 cc.eventManager.dispatchEvent(mouseEvent); 502 503 event.stopPropagation(); 504 event.preventDefault(); 505 }, false); 506 507 /* firefox fix */ 508 element.addEventListener("DOMMouseScroll", function(event) { 509 var pos = selfPointer.getHTMLElementPosition(element); 510 var location = selfPointer.getPointByEvent(event, pos); 511 512 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.SCROLL); 513 mouseEvent.setButton(event.button); 514 mouseEvent.setScrollData(0, event.detail * -120); 515 cc.eventManager.dispatchEvent(mouseEvent); 516 517 event.stopPropagation(); 518 event.preventDefault(); 519 }, false); 520 } 521 522 if(window.navigator.msPointerEnabled){ 523 var _pointerEventsMap = { 524 "MSPointerDown" : selfPointer.handleTouchesBegin, 525 "MSPointerMove" : selfPointer.handleTouchesMove, 526 "MSPointerUp" : selfPointer.handleTouchesEnd, 527 "MSPointerCancel" : selfPointer.handleTouchesCancel 528 }; 529 530 for(var eventName in _pointerEventsMap){ 531 (function(_pointerEvent, _touchEvent){ 532 element.addEventListener(_pointerEvent, function (event){ 533 var pos = selfPointer.getHTMLElementPosition(element); 534 pos.left -= document.documentElement.scrollLeft; 535 pos.top -= document.documentElement.scrollTop; 536 537 _touchEvent.call(selfPointer, [selfPointer.getTouchByXY(event.clientX, event.clientY, pos)]); 538 event.stopPropagation(); 539 }, false); 540 })(eventName, _pointerEventsMap[eventName]); 541 } 542 } 543 544 if(supportTouches) { 545 //register canvas touch event 546 element.addEventListener("touchstart", function (event) { 547 if (!event.changedTouches) return; 548 549 var pos = selfPointer.getHTMLElementPosition(element); 550 pos.left -= document.body.scrollLeft; 551 pos.top -= document.body.scrollTop; 552 selfPointer.handleTouchesBegin(selfPointer.getTouchesByEvent(event, pos)); 553 event.stopPropagation(); 554 event.preventDefault(); 555 element.focus(); 556 }, false); 557 558 element.addEventListener("touchmove", function (event) { 559 if (!event.changedTouches) return; 560 561 var pos = selfPointer.getHTMLElementPosition(element); 562 pos.left -= document.body.scrollLeft; 563 pos.top -= document.body.scrollTop; 564 selfPointer.handleTouchesMove(selfPointer.getTouchesByEvent(event, pos)); 565 event.stopPropagation(); 566 event.preventDefault(); 567 }, false); 568 569 element.addEventListener("touchend", function (event) { 570 if (!event.changedTouches) return; 571 572 var pos = selfPointer.getHTMLElementPosition(element); 573 pos.left -= document.body.scrollLeft; 574 pos.top -= document.body.scrollTop; 575 selfPointer.handleTouchesEnd(selfPointer.getTouchesByEvent(event, pos)); 576 event.stopPropagation(); 577 event.preventDefault(); 578 }, false); 579 580 element.addEventListener("touchcancel", function (event) { 581 if (!event.changedTouches) return; 582 583 var pos = selfPointer.getHTMLElementPosition(element); 584 pos.left -= document.body.scrollLeft; 585 pos.top -= document.body.scrollTop; 586 selfPointer.handleTouchesCancel(selfPointer.getTouchesByEvent(event, pos)); 587 event.stopPropagation(); 588 event.preventDefault(); 589 }, false); 590 } 591 592 //register keyboard event 593 this._registerKeyboardEvent(); 594 595 //register Accelerometer event 596 this._registerAccelerometerEvent(); 597 598 this._isRegisterEvent = true; 599 }, 600 601 _registerKeyboardEvent: function(){}, 602 603 _registerAccelerometerEvent: function(){}, 604 605 /** 606 * @function 607 * @param {Number} dt 608 */ 609 update:function(dt){ 610 if(this._accelCurTime > this._accelInterval){ 611 this._accelCurTime -= this._accelInterval; 612 cc.eventManager.dispatchEvent(new cc.EventAcceleration(this._acceleration)); 613 } 614 this._accelCurTime += dt; 615 } 616 }; 617