1 /**************************************************************************** 2 Copyright (c) 2010-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 cc.UIInterfaceOrientationLandscapeLeft = -90; 30 31 cc.UIInterfaceOrientationLandscapeRight = 90; 32 33 cc.UIInterfaceOrientationPortraitUpsideDown = 180; 34 35 cc.UIInterfaceOrientationPortrait = 0; 36 37 /** 38 * <p> 39 * This class manages all events of input. include: touch, mouse, accelerometer, keyboard <br/> 40 * </p> 41 * @namespace 42 */ 43 cc.inputManager = /** @lends cc.inputManager# */{ 44 _mousePressed: false, 45 46 _isRegisterEvent: false, 47 48 _preTouchPoint: cc.p(0,0), 49 _prevMousePoint: cc.p(0,0), 50 51 _preTouchPool: [], 52 _preTouchPoolPointer: 0, 53 54 _touches: [], 55 _touchesIntegerDict:{}, 56 57 _indexBitsUsed: 0, 58 _maxTouches: 5, 59 60 _accelEnabled: false, 61 _accelInterval: 1/30, 62 _accelMinus: 1, 63 _accelCurTime: 0, 64 _acceleration: null, 65 _accelDeviceEvent: null, 66 67 /** 68 * whether enable accelerometer event 69 * @param {Boolean} isEnable 70 */ 71 setAccelerometerEnabled: function(isEnable){ 72 if(this._accelEnabled === isEnable) 73 return; 74 75 this._accelEnabled = isEnable; 76 var scheduler = cc.director.getScheduler(); 77 if(this._accelEnabled){ 78 this._accelCurTime = 0; 79 scheduler.scheduleUpdateForTarget(this); 80 } else { 81 this._accelCurTime = 0; 82 scheduler.unscheduleUpdateForTarget(this); 83 } 84 }, 85 86 /** 87 * set accelerometer interval value 88 * @param {Number} interval 89 */ 90 setAccelerometerInterval: function(interval){ 91 if (this._accelInterval !== interval) { 92 this._accelInterval = interval; 93 } 94 }, 95 96 _getUnUsedIndex: function () { 97 var temp = this._indexBitsUsed; 98 99 for (var i = 0; i < this._maxTouches; i++) { 100 if (!(temp & 0x00000001)) { 101 this._indexBitsUsed |= (1 << i); 102 return i; 103 } 104 temp >>= 1; 105 } 106 107 // all bits are used 108 return -1; 109 }, 110 111 _removeUsedIndexBit: function (index) { 112 if (index < 0 || index >= this._maxTouches) 113 return; 114 115 var temp = 1 << index; 116 temp = ~temp; 117 this._indexBitsUsed &= temp; 118 }, 119 120 _glView: null, 121 122 handleTouchesBegin: function (touches) { 123 var selTouch, index, curTouch, touchID, handleTouches = [], locTouchIntDict = this._touchesIntegerDict; 124 for(var i = 0, len = touches.length; i< len; i ++){ 125 selTouch = touches[i]; 126 touchID = selTouch.getID(); 127 index = locTouchIntDict[touchID]; 128 129 if(index == null){ 130 var unusedIndex = this._getUnUsedIndex(); 131 if (unusedIndex == -1) { 132 cc.log("The touches is more than MAX_TOUCHES, nUnusedIndex = " + unusedIndex); 133 continue; 134 } 135 curTouch = this._touches[unusedIndex] = selTouch; 136 locTouchIntDict[touchID] = unusedIndex; 137 handleTouches.push(curTouch); 138 } 139 } 140 if(handleTouches.length > 0){ 141 this._glView._convertTouchesWithScale(handleTouches); 142 var touchEvent = new cc.EventTouch(handleTouches); 143 touchEvent._eventCode = cc.EventTouch.EventCode.BEGAN; 144 cc.eventManager.dispatchEvent(touchEvent); 145 } 146 }, 147 148 handleTouchesMove: function(touches){ 149 var selTouch, index, touchID, handleTouches = [], locTouches = this._touches; 150 for(var i = 0, len = touches.length; i< len; i ++){ 151 selTouch = touches[i]; 152 touchID = selTouch.getID(); 153 index = this._touchesIntegerDict[touchID]; 154 155 if(index == null){ 156 //cc.log("if the index doesn't exist, it is an error"); 157 continue; 158 } 159 if(locTouches[index]){ 160 locTouches[index]._setPoint(selTouch._point); 161 locTouches[index]._setPrevPoint(selTouch._prevPoint); 162 handleTouches.push(locTouches[index]); 163 } 164 } 165 if(handleTouches.length > 0){ 166 this._glView._convertTouchesWithScale(handleTouches); 167 var touchEvent = new cc.EventTouch(handleTouches); 168 touchEvent._eventCode = cc.EventTouch.EventCode.MOVED; 169 cc.eventManager.dispatchEvent(touchEvent); 170 } 171 }, 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 handleTouchesCancel: function(touches){ 184 var handleTouches = this.getSetOfTouchesEndOrCancel(touches); 185 if(handleTouches.length > 0) { 186 this._glView._convertTouchesWithScale(handleTouches); 187 var touchEvent = new cc.EventTouch(handleTouches); 188 touchEvent._eventCode = cc.EventTouch.EventCode.CANCELLED; 189 cc.eventManager.dispatchEvent(touchEvent); 190 } 191 }, 192 193 getSetOfTouchesEndOrCancel: function(touches) { 194 var selTouch, index, touchID, handleTouches = [], locTouches = this._touches, locTouchesIntDict = this._touchesIntegerDict; 195 for(var i = 0, len = touches.length; i< len; i ++){ 196 selTouch = touches[i]; 197 touchID = selTouch.getID(); 198 index = locTouchesIntDict[touchID]; 199 200 if(index == null){ 201 continue; //cc.log("if the index doesn't exist, it is an error"); 202 } 203 if(locTouches[index]){ 204 locTouches[index]._setPoint(selTouch._point); 205 locTouches[index]._setPrevPoint(selTouch._prevPoint); //TODO 206 handleTouches.push(locTouches[index]); 207 this._removeUsedIndexBit(index); 208 delete locTouchesIntDict[touchID]; 209 } 210 } 211 return handleTouches; 212 }, 213 214 getHTMLElementPosition: function (element) { 215 var docElem = document.documentElement; 216 var win = window; 217 var box = null; 218 if (typeof element.getBoundingClientRect === 'function') { 219 box = element.getBoundingClientRect(); 220 } else { 221 if (element instanceof HTMLCanvasElement) { 222 box = { 223 left: 0, 224 top: 0, 225 width: element.width, 226 height: element.height 227 }; 228 } else { 229 box = { 230 left: 0, 231 top: 0, 232 width: parseInt(element.style.width), 233 height: parseInt(element.style.height) 234 }; 235 } 236 } 237 return { 238 left: box.left + win.pageXOffset - docElem.clientLeft, 239 top: box.top + win.pageYOffset - docElem.clientTop, 240 width: box.width, 241 height: box.height 242 }; 243 }, 244 245 getPreTouch: function(touch){ 246 var preTouch = null; 247 var locPreTouchPool = this._preTouchPool; 248 var id = touch.getId(); 249 for (var i = locPreTouchPool.length - 1; i >= 0; i--) { 250 if (locPreTouchPool[i].getId() == id) { 251 preTouch = locPreTouchPool[i]; 252 break; 253 } 254 } 255 if (!preTouch) 256 preTouch = touch; 257 return preTouch; 258 }, 259 260 setPreTouch: function(touch){ 261 var find = false; 262 var locPreTouchPool = this._preTouchPool; 263 var id = touch.getId(); 264 for (var i = locPreTouchPool.length - 1; i >= 0; i--) { 265 if (locPreTouchPool[i].getId() == id) { 266 locPreTouchPool[i] = touch; 267 find = true; 268 break; 269 } 270 } 271 if (!find) { 272 if (locPreTouchPool.length <= 50) { 273 locPreTouchPool.push(touch); 274 } else { 275 locPreTouchPool[this._preTouchPoolPointer] = touch; 276 this._preTouchPoolPointer = (this._preTouchPoolPointer + 1) % 50; 277 } 278 } 279 }, 280 281 getTouchByXY: function(tx, ty, pos){ 282 var locPreTouch = this._preTouchPoint; 283 var location = this._glView.convertToLocationInView(tx, ty, pos); 284 var touch = new cc.Touch(location.x, location.y); 285 touch._setPrevPoint(locPreTouch.x, locPreTouch.y); 286 locPreTouch.x = location.x; 287 locPreTouch.y = location.y; 288 return touch; 289 }, 290 291 getMouseEvent: function(location, pos, eventType){ 292 var locPreMouse = this._prevMousePoint; 293 this._glView._convertMouseToLocationInView(location, pos); 294 var mouseEvent = new cc.EventMouse(eventType); 295 mouseEvent.setLocation(location.x, location.y); 296 mouseEvent._setPrevCursor(locPreMouse.x, locPreMouse.y); 297 locPreMouse.x = location.x; 298 locPreMouse.y = location.y; 299 return mouseEvent; 300 }, 301 302 getPointByEvent: function(event, pos){ 303 if (event.pageX != null) //not avalable in <= IE8 304 return {x: event.pageX, y: event.pageY}; 305 306 pos.left -= document.body.scrollLeft; 307 pos.top -= document.body.scrollTop; 308 return {x: event.clientX, y: event.clientY}; 309 }, 310 311 getTouchesByEvent: function(event, pos){ 312 var touchArr = [], locView = this._glView; 313 var touch_event, touch, preLocation; 314 var locPreTouch = this._preTouchPoint; 315 316 var length = event.changedTouches.length; 317 for (var i = 0; i < length; i++) { 318 touch_event = event.changedTouches[i]; 319 if (touch_event) { 320 var location = locView.convertToLocationInView(touch_event.clientX, touch_event.clientY, pos); 321 if (touch_event.identifier != null) { 322 touch = new cc.Touch(location.x, location.y, touch_event.identifier); 323 //use Touch Pool 324 preLocation = this.getPreTouch(touch).getLocation(); 325 touch._setPrevPoint(preLocation.x, preLocation.y); 326 this.setPreTouch(touch); 327 } else { 328 touch = new cc.Touch(location.x, location.y); 329 touch._setPrevPoint(locPreTouch.x, locPreTouch.y); 330 } 331 locPreTouch.x = location.x; 332 locPreTouch.y = location.y; 333 touchArr.push(touch); 334 } 335 } 336 return touchArr; 337 }, 338 339 registerSystemEvent: function(element){ 340 if(this._isRegisterEvent) return; 341 342 var locView = this._glView = cc.view; 343 var selfPointer = this; 344 var supportMouse = ('mouse' in cc.sys.capabilities), supportTouches = ('touches' in cc.sys.capabilities); 345 346 //register touch event 347 if (supportMouse) { 348 window.addEventListener('mousedown', function () { 349 selfPointer._mousePressed = true; 350 }, false); 351 352 window.addEventListener('mouseup', function (event) { 353 var savePressed = selfPointer._mousePressed; 354 selfPointer._mousePressed = false; 355 356 if(!savePressed) 357 return; 358 359 var pos = selfPointer.getHTMLElementPosition(element); 360 var location = selfPointer.getPointByEvent(event, pos); 361 if (!cc.rectContainsPoint(new cc.Rect(pos.left, pos.top, pos.width, pos.height), location)){ 362 if(!supportTouches) 363 selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]); 364 365 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.UP); 366 mouseEvent.setButton(event.button); 367 cc.eventManager.dispatchEvent(mouseEvent); 368 } 369 }, false); 370 371 //register canvas mouse event 372 element.addEventListener("mousedown", function (event) { 373 selfPointer._mousePressed = true; 374 375 var pos = selfPointer.getHTMLElementPosition(element); 376 var location = selfPointer.getPointByEvent(event, pos); 377 if(!supportTouches) 378 selfPointer.handleTouchesBegin([selfPointer.getTouchByXY(location.x, location.y, pos)]); 379 380 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.DOWN); 381 mouseEvent.setButton(event.button); 382 cc.eventManager.dispatchEvent(mouseEvent); 383 384 event.stopPropagation(); 385 event.preventDefault(); 386 }, false); 387 388 element.addEventListener("mouseup", function (event) { 389 selfPointer._mousePressed = false; 390 391 var pos = selfPointer.getHTMLElementPosition(element); 392 var location = selfPointer.getPointByEvent(event, pos); 393 394 if(!supportTouches) 395 selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]); 396 397 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.UP); 398 mouseEvent.setButton(event.button); 399 cc.eventManager.dispatchEvent(mouseEvent); 400 401 event.stopPropagation(); 402 event.preventDefault(); 403 }, false); 404 405 element.addEventListener("mousemove", function (event) { 406 if(!selfPointer._mousePressed) 407 return; 408 409 var pos = selfPointer.getHTMLElementPosition(element); 410 var location = selfPointer.getPointByEvent(event, pos); 411 412 if(!supportTouches) 413 selfPointer.handleTouchesMove([selfPointer.getTouchByXY(location.x, location.y, pos)]); 414 415 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.MOVE); 416 mouseEvent.setButton(event.button); 417 cc.eventManager.dispatchEvent(mouseEvent); 418 419 event.stopPropagation(); 420 event.preventDefault(); 421 }, false); 422 423 element.addEventListener("mousewheel", function (event) { 424 var pos = selfPointer.getHTMLElementPosition(element); 425 var location = selfPointer.getPointByEvent(event, pos); 426 427 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.SCROLL); 428 mouseEvent.setButton(event.button); 429 mouseEvent.setScrollData(0, event.wheelDelta); 430 cc.eventManager.dispatchEvent(mouseEvent); 431 432 event.stopPropagation(); 433 event.preventDefault(); 434 }, false); 435 436 /* firefox fix */ 437 element.addEventListener("DOMMouseScroll", function(event) { 438 var pos = selfPointer.getHTMLElementPosition(element); 439 var location = selfPointer.getPointByEvent(event, pos); 440 441 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.SCROLL); 442 mouseEvent.setButton(event.button); 443 mouseEvent.setScrollData(0, event.detail * -120); 444 cc.eventManager.dispatchEvent(mouseEvent); 445 446 event.stopPropagation(); 447 event.preventDefault(); 448 }, false); 449 } 450 451 if(window.navigator.msPointerEnabled){ 452 var _pointerEventsMap = { 453 "MSPointerDown" : "handleTouchesBegin", 454 "MSPointerMove" : "handleTouchesMove", 455 "MSPointerUp" : "handleTouchesEnd", 456 "MSPointerCancel" : "handleTouchesCancel" 457 }; 458 459 for(var eventName in _pointerEventsMap){ 460 (function(_pointerEvent, _touchEvent){ 461 element.addEventListener(_pointerEvent, function (event){ 462 var pos = selfPointer.getHTMLElementPosition(element); 463 pos.left -= document.body.scrollLeft; 464 pos.top -= document.body.scrollTop; 465 466 selfPointer[_touchEvent]([selfPointer.getTouchByXY(event.clientX, event.clientY, pos)]); 467 event.stopPropagation(); 468 event.preventDefault(); 469 }, false); 470 })(eventName, _pointerEventsMap[eventName]); 471 } 472 } 473 474 if(supportTouches) { 475 //register canvas touch event 476 element.addEventListener("touchstart", function (event) { 477 if (!event.changedTouches) return; 478 479 var pos = selfPointer.getHTMLElementPosition(element); 480 pos.left -= document.body.scrollLeft; 481 pos.top -= document.body.scrollTop; 482 selfPointer.handleTouchesBegin(selfPointer.getTouchesByEvent(event, pos)); 483 event.stopPropagation(); 484 event.preventDefault(); 485 }, false); 486 487 element.addEventListener("touchmove", function (event) { 488 if (!event.changedTouches) return; 489 490 var pos = selfPointer.getHTMLElementPosition(element); 491 pos.left -= document.body.scrollLeft; 492 pos.top -= document.body.scrollTop; 493 selfPointer.handleTouchesMove(selfPointer.getTouchesByEvent(event, pos)); 494 event.stopPropagation(); 495 event.preventDefault(); 496 }, false); 497 498 element.addEventListener("touchend", function (event) { 499 if (!event.changedTouches) return; 500 501 var pos = selfPointer.getHTMLElementPosition(element); 502 pos.left -= document.body.scrollLeft; 503 pos.top -= document.body.scrollTop; 504 selfPointer.handleTouchesEnd(selfPointer.getTouchesByEvent(event, pos)); 505 event.stopPropagation(); 506 event.preventDefault(); 507 }, false); 508 509 element.addEventListener("touchcancel", function (event) { 510 if (!event.changedTouches) return; 511 512 var pos = selfPointer.getHTMLElementPosition(element); 513 pos.left -= document.body.scrollLeft; 514 pos.top -= document.body.scrollTop; 515 locView.handleTouchesCancel(selfPointer.getTouchesByEvent(event, pos)); 516 event.stopPropagation(); 517 event.preventDefault(); 518 }, false); 519 } 520 521 //register keyboard event 522 this._registerKeyboardEvent(); 523 524 //register Accelerometer event 525 this._registerAccelerometerEvent(); 526 527 this._isRegisterEvent = true; 528 }, 529 530 _registerKeyboardEvent: function(){ 531 document.addEventListener("keydown", function (e) { 532 cc.eventManager.dispatchEvent(new cc.EventKeyboard(e.keyCode, true)); 533 }); 534 document.addEventListener("keyup", function (e) { 535 cc.eventManager.dispatchEvent(new cc.EventKeyboard(e.keyCode, false)); 536 }); 537 }, 538 539 _registerAccelerometerEvent: function(){ 540 this._acceleration = new cc.Acceleration(); 541 var w = window; 542 this._accelDeviceEvent = w.DeviceMotionEvent || w.DeviceOrientationEvent; 543 544 //TODO fix DeviceMotionEvent bug on QQ Browser version 4.1 and below. 545 if (cc.sys.browserType == cc.sys.BROWSER_TYPE_MOBILE_QQ) 546 this._accelDeviceEvent = window.DeviceOrientationEvent; 547 548 var _deviceEventType = (this._accelDeviceEvent == w.DeviceMotionEvent) ? "devicemotion" : "deviceorientation"; 549 var ua = navigator.userAgent; 550 if (/Android/.test(ua) || (/Adr/.test(ua) && cc.sys.browserType == cc.BROWSER_TYPE_UC)) { 551 this._minus = -1; 552 } 553 554 w.addEventListener(_deviceEventType, this.didAccelerate.bind(this), false); 555 }, 556 557 didAccelerate: function (eventData) { 558 if (!this._accelEnabled) 559 return; 560 561 var mAcceleration = this._acceleration; 562 if (this._accelDeviceEvent == window.DeviceMotionEvent) { 563 var eventAcceleration = eventData["accelerationIncludingGravity"]; 564 mAcceleration.x = this._accelMinus * eventAcceleration.x * 0.1; 565 mAcceleration.y = this._accelMinus * eventAcceleration.y * 0.1; 566 mAcceleration.z = eventAcceleration.z * 0.1; 567 } else { 568 mAcceleration.x = (eventData["gamma"] / 90) * 0.981; 569 mAcceleration.y = -(eventData["beta"] / 90) * 0.981; 570 mAcceleration.z = (eventData["alpha"] / 90) * 0.981; 571 } 572 mAcceleration.timestamp = eventData.timeStamp || Date.now(); 573 574 var tmpX = mAcceleration.x; 575 switch (window.orientation) { 576 case cc.UIInterfaceOrientationLandscapeRight://-90 577 mAcceleration.x = -mAcceleration.y; 578 mAcceleration.y = tmpX; 579 break; 580 581 case cc.UIInterfaceOrientationLandscapeLeft://90 582 mAcceleration.x = mAcceleration.y; 583 mAcceleration.y = -tmpX; 584 break; 585 586 case cc.UIInterfaceOrientationPortraitUpsideDown://180 587 mAcceleration.x = -mAcceleration.x; 588 mAcceleration.y = -mAcceleration.y; 589 break; 590 591 case cc.UIInterfaceOrientationPortrait://0 592 break; 593 } 594 }, 595 596 update:function(dt){ 597 if(this._accelCurTime > this._accelInterval){ 598 this._accelCurTime -= this._accelInterval; 599 cc.eventManager.dispatchEvent(new cc.EventAcceleration(this._acceleration)); 600 } 601 this._accelCurTime += dt; 602 } 603 }; 604