SWF_Events.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../idlib/precompiled.h"
  22. /*
  23. ===================
  24. idSWF::HitTest
  25. ===================
  26. */
  27. idSWFScriptObject * idSWF::HitTest( idSWFSpriteInstance * spriteInstance, const swfRenderState_t & renderState, int x, int y, idSWFScriptObject * parentObject ) {
  28. if ( spriteInstance->parent != NULL ) {
  29. swfDisplayEntry_t * thisDisplayEntry = spriteInstance->parent->FindDisplayEntry( spriteInstance->depth );
  30. if ( thisDisplayEntry->cxf.mul.w + thisDisplayEntry->cxf.add.w < 0.001f ) {
  31. return NULL;
  32. }
  33. }
  34. if ( !spriteInstance->isVisible ) {
  35. return NULL;
  36. }
  37. if ( spriteInstance->scriptObject->HasValidProperty( "onRelease" )
  38. || spriteInstance->scriptObject->HasValidProperty( "onPress" )
  39. || spriteInstance->scriptObject->HasValidProperty( "onRollOver" )
  40. || spriteInstance->scriptObject->HasValidProperty( "onRollOut" )
  41. || spriteInstance->scriptObject->HasValidProperty( "onDrag" )
  42. ) {
  43. parentObject = spriteInstance->scriptObject;
  44. }
  45. // rather than returning the first object we find, we actually want to return the last object we find
  46. idSWFScriptObject * returnObject = NULL;
  47. float xOffset = spriteInstance->xOffset;
  48. float yOffset = spriteInstance->yOffset;
  49. for ( int i = 0; i < spriteInstance->displayList.Num(); i++ ) {
  50. const swfDisplayEntry_t & display = spriteInstance->displayList[i];
  51. idSWFDictionaryEntry * entry = FindDictionaryEntry( display.characterID );
  52. if ( entry == NULL ) {
  53. continue;
  54. }
  55. swfRenderState_t renderState2;
  56. renderState2.matrix = display.matrix.Multiply( renderState.matrix );
  57. renderState2.ratio = display.ratio;
  58. if ( entry->type == SWF_DICT_SPRITE ) {
  59. idSWFScriptObject * object = HitTest( display.spriteInstance, renderState2, x, y, parentObject );
  60. if ( object != NULL && object->Get( "_visible" ).ToBool() ) {
  61. returnObject = object;
  62. }
  63. } else if ( entry->type == SWF_DICT_SHAPE && ( parentObject != NULL ) ) {
  64. idSWFShape * shape = entry->shape;
  65. for ( int i = 0; i < shape->fillDraws.Num(); i++ ) {
  66. const idSWFShapeDrawFill & fill = shape->fillDraws[i];
  67. for ( int j = 0; j < fill.indices.Num(); j+=3 ) {
  68. idVec2 xy1 = renderState2.matrix.Transform( fill.startVerts[fill.indices[j+0]] );
  69. idVec2 xy2 = renderState2.matrix.Transform( fill.startVerts[fill.indices[j+1]] );
  70. idVec2 xy3 = renderState2.matrix.Transform( fill.startVerts[fill.indices[j+2]] );
  71. idMat3 edgeEquations;
  72. edgeEquations[0].Set( xy1.x + xOffset, xy1.y + yOffset, 1.0f );
  73. edgeEquations[1].Set( xy2.x + xOffset, xy2.y + yOffset, 1.0f );
  74. edgeEquations[2].Set( xy3.x + xOffset, xy3.y + yOffset, 1.0f );
  75. edgeEquations.InverseSelf();
  76. idVec3 p( x, y, 1.0f );
  77. idVec3 signs = p * edgeEquations;
  78. bool bx = signs.x > 0;
  79. bool by = signs.y > 0;
  80. bool bz = signs.z > 0;
  81. if ( bx == by && bx == bz ) {
  82. // point inside
  83. returnObject = parentObject;
  84. }
  85. }
  86. }
  87. } else if ( entry->type == SWF_DICT_MORPH ) {
  88. // FIXME: this should be roughly the same as SWF_DICT_SHAPE
  89. } else if ( entry->type == SWF_DICT_TEXT ) {
  90. // FIXME: this should be roughly the same as SWF_DICT_SHAPE
  91. } else if ( entry->type == SWF_DICT_EDITTEXT ) {
  92. idSWFScriptObject * editObject = NULL;
  93. if ( display.textInstance->scriptObject.HasProperty( "onRelease" ) || display.textInstance->scriptObject.HasProperty( "onPress" ) ) {
  94. // if the edit box itself can be clicked, then we want to return it when it's clicked on
  95. editObject = &display.textInstance->scriptObject;
  96. } else if ( parentObject != NULL ) {
  97. // otherwise, we want to return the parent object
  98. editObject = parentObject;
  99. }
  100. if ( editObject == NULL ) {
  101. continue;
  102. }
  103. if ( display.textInstance->text.IsEmpty() ) {
  104. continue;
  105. }
  106. const idSWFEditText * shape = entry->edittext;
  107. const idSWFEditText * text = display.textInstance->GetEditText();
  108. float textLength = display.textInstance->GetTextLength();
  109. float lengthDiff = fabs( shape->bounds.br.x - shape->bounds.tl.x ) - textLength;
  110. idVec3 tl;
  111. idVec3 tr;
  112. idVec3 br;
  113. idVec3 bl;
  114. float xOffset = spriteInstance->xOffset;
  115. float yOffset = spriteInstance->yOffset;
  116. float topOffset = 0.0f;
  117. if ( text->align == SWF_ET_ALIGN_LEFT ) {
  118. tl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
  119. tr.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x - lengthDiff + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
  120. br.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x - lengthDiff + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
  121. bl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
  122. } else if ( text->align == SWF_ET_ALIGN_RIGHT ) {
  123. tl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + lengthDiff + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
  124. tr.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
  125. br.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
  126. bl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + lengthDiff + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
  127. } else if ( text->align == SWF_ET_ALIGN_CENTER ) {
  128. float middle = ( ( shape->bounds.br.x + xOffset ) + ( shape->bounds.tl.x + xOffset ) ) / 2.0f;
  129. tl.ToVec2() = renderState2.matrix.Transform( idVec2( middle - ( textLength / 2.0f ), shape->bounds.tl.y + topOffset + yOffset ) );
  130. tr.ToVec2() = renderState2.matrix.Transform( idVec2( middle + ( textLength / 2.0f ), shape->bounds.tl.y + topOffset + yOffset ) );
  131. br.ToVec2() = renderState2.matrix.Transform( idVec2( middle + ( textLength / 2.0f ), shape->bounds.br.y + topOffset + yOffset ) );
  132. bl.ToVec2() = renderState2.matrix.Transform( idVec2( middle - ( textLength / 2.0f ), shape->bounds.br.y + topOffset + yOffset ) );
  133. } else {
  134. tl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
  135. tr.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
  136. br.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
  137. bl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
  138. }
  139. tl.z = 1.0f;
  140. tr.z = 1.0f;
  141. br.z = 1.0f;
  142. bl.z = 1.0f;
  143. idMat3 edgeEquations;
  144. edgeEquations[0] = tl;
  145. edgeEquations[1] = tr;
  146. edgeEquations[2] = br;
  147. edgeEquations.InverseSelf();
  148. idVec3 p( x, y, 1.0f );
  149. idVec3 signs = p * edgeEquations;
  150. bool bx = signs.x > 0;
  151. bool by = signs.y > 0;
  152. bool bz = signs.z > 0;
  153. if ( bx == by && bx == bz ) {
  154. // point inside top right triangle
  155. returnObject = editObject;
  156. }
  157. edgeEquations[0] = tl;
  158. edgeEquations[1] = br;
  159. edgeEquations[2] = bl;
  160. edgeEquations.InverseSelf();
  161. signs = p * edgeEquations;
  162. bx = signs.x > 0;
  163. by = signs.y > 0;
  164. bz = signs.z > 0;
  165. if ( bx == by && bx == bz ) {
  166. // point inside bottom left triangle
  167. returnObject = editObject;
  168. }
  169. }
  170. }
  171. return returnObject;
  172. }
  173. /*
  174. ===================
  175. idSWF::HandleEvent
  176. ===================
  177. */
  178. bool idSWF::HandleEvent( const sysEvent_t * event ) {
  179. if ( !IsLoaded() || !IsActive() || ( !inhibitControl && useInhibtControl ) ) {
  180. return false;
  181. }
  182. if ( event->evType == SE_KEY ) {
  183. if ( event->evValue == K_MOUSE1 ) {
  184. mouseEnabled = true;
  185. idSWFScriptVar var;
  186. if ( event->evValue2 ) {
  187. idSWFScriptVar waitInput = globals->Get( "waitInput" );
  188. if ( waitInput.IsFunction() ) {
  189. useMouse = false;
  190. idSWFParmList waitParms;
  191. waitParms.Append( event->evValue );
  192. waitInput.GetFunction()->Call( NULL, waitParms );
  193. waitParms.Clear();
  194. } else {
  195. useMouse = true;
  196. }
  197. idSWFScriptObject * hitObject = HitTest( mainspriteInstance, swfRenderState_t(), mouseX, mouseY, NULL );
  198. if ( hitObject != NULL ) {
  199. mouseObject = hitObject;
  200. mouseObject->AddRef();
  201. var = hitObject->Get( "onPress" );
  202. if ( var.IsFunction() ) {
  203. idSWFParmList parms;
  204. parms.Append( event->inputDevice );
  205. var.GetFunction()->Call( hitObject, parms );
  206. parms.Clear();
  207. return true;
  208. }
  209. idSWFScriptVar var = hitObject->Get( "onDrag" );
  210. if ( var.IsFunction() ) {
  211. idSWFParmList parms;
  212. parms.Append( mouseX );
  213. parms.Append( mouseY );
  214. parms.Append( true );
  215. var.GetFunction()->Call( hitObject, parms );
  216. parms.Clear();
  217. return true;
  218. }
  219. }
  220. idSWFParmList parms;
  221. parms.Append( hitObject );
  222. Invoke( "setHitObject", parms );
  223. } else {
  224. if ( mouseObject ) {
  225. var = mouseObject->Get( "onRelease" );
  226. if ( var.IsFunction() ) {
  227. idSWFParmList parms;
  228. parms.Append( mouseObject ); // FIXME: Remove this
  229. var.GetFunction()->Call( mouseObject, parms );
  230. }
  231. mouseObject->Release();
  232. mouseObject = NULL;
  233. }
  234. if ( hoverObject ) {
  235. hoverObject->Release();
  236. hoverObject = NULL;
  237. }
  238. if ( var.IsFunction() ) {
  239. return true;
  240. }
  241. }
  242. return false;
  243. }
  244. const char * keyName = idKeyInput::KeyNumToString( (keyNum_t)event->evValue );
  245. idSWFScriptVar var = shortcutKeys->Get( keyName );
  246. // anything more than 32 levels of indirection we can be pretty sure is an infinite loop
  247. for ( int runaway = 0; runaway < 32; runaway++ ) {
  248. idSWFParmList eventParms;
  249. eventParms.Clear();
  250. eventParms.Append( event->inputDevice );
  251. if ( var.IsString() ) {
  252. // alias to another key
  253. var = shortcutKeys->Get( var.ToString() );
  254. continue;
  255. } else if ( var.IsObject() ) {
  256. // if this object is a sprite, send fake mouse events to it
  257. idSWFScriptObject * object = var.GetObject();
  258. // make sure we don't send an onRelease event unless we have already sent that object an onPress
  259. bool wasPressed = object->Get( "_pressed" ).ToBool();
  260. object->Set( "_pressed", event->evValue2 );
  261. if ( event->evValue2 ) {
  262. var = object->Get( "onPress" );
  263. } else if ( wasPressed ) {
  264. var = object->Get( "onRelease" );
  265. }
  266. if ( var.IsFunction() ) {
  267. var.GetFunction()->Call( object, eventParms );
  268. return true;
  269. }
  270. } else if ( var.IsFunction() ) {
  271. if ( event->evValue2 ) {
  272. // anonymous functions only respond to key down events
  273. var.GetFunction()->Call( NULL, eventParms );
  274. return true;
  275. }
  276. return false;
  277. }
  278. idSWFScriptVar useFunction = globals->Get( "useFunction" );
  279. if ( useFunction.IsFunction() && event->evValue2 ) {
  280. const char * action = idKeyInput::GetBinding( event->evValue );
  281. if ( idStr::Cmp( "_use", action ) == 0 ) {
  282. useFunction.GetFunction()->Call( NULL, idSWFParmList() );
  283. }
  284. }
  285. idSWFScriptVar waitInput = globals->Get( "waitInput" );
  286. if ( waitInput.IsFunction() ) {
  287. useMouse = false;
  288. if ( event->evValue2 ) {
  289. idSWFParmList waitParms;
  290. waitParms.Append( event->evValue );
  291. waitInput.GetFunction()->Call( NULL, waitParms );
  292. }
  293. } else {
  294. useMouse = true;
  295. }
  296. idSWFScriptVar focusWindow = globals->Get( "focusWindow" );
  297. if ( focusWindow.IsObject() ) {
  298. idSWFScriptVar onKey = focusWindow.GetObject()->Get( "onKey" );
  299. if ( onKey.IsFunction() ) {
  300. // make sure we don't send an onRelease event unless we have already sent that object an onPress
  301. idSWFScriptObject * object = focusWindow.GetObject();
  302. bool wasPressed = object->Get( "_kpressed" ).ToBool();
  303. object->Set( "_kpressed", event->evValue2 );
  304. if ( event->evValue2 || wasPressed ) {
  305. idSWFParmList parms;
  306. parms.Append( event->evValue );
  307. parms.Append( event->evValue2 );
  308. onKey.GetFunction()->Call( focusWindow.GetObject(), parms ).ToBool();
  309. return true;
  310. } else if ( event->evValue == K_LSHIFT || event->evValue == K_RSHIFT ) {
  311. idSWFParmList parms;
  312. parms.Append( event->evValue );
  313. parms.Append( event->evValue2 );
  314. onKey.GetFunction()->Call( focusWindow.GetObject(), parms ).ToBool();
  315. }
  316. }
  317. }
  318. return false;
  319. }
  320. idLib::Warning( "Circular reference in %s shortcutKeys.%s", filename.c_str(), keyName );
  321. } else if ( event->evType == SE_CHAR ) {
  322. idSWFScriptVar focusWindow = globals->Get( "focusWindow" );
  323. if ( focusWindow.IsObject() ) {
  324. idSWFScriptVar onChar = focusWindow.GetObject()->Get( "onChar" );
  325. if ( onChar.IsFunction() ) {
  326. idSWFParmList parms;
  327. parms.Append( event->evValue );
  328. parms.Append( idKeyInput::KeyNumToString( (keyNum_t)event->evValue ) );
  329. onChar.GetFunction()->Call( focusWindow.GetObject(), parms ).ToBool();
  330. return true;
  331. }
  332. }
  333. } else if ( event->evType == SE_MOUSE_ABSOLUTE || event->evType == SE_MOUSE ) {
  334. mouseEnabled = true;
  335. isMouseInClientArea = true;
  336. // Mouse position in screen space needs to be converted to SWF space
  337. if ( event->evType == SE_MOUSE_ABSOLUTE ) {
  338. const float pixelAspect = renderSystem->GetPixelAspect();
  339. const float sysWidth = renderSystem->GetWidth() * ( pixelAspect > 1.0f ? pixelAspect : 1.0f );
  340. const float sysHeight = renderSystem->GetHeight() / ( pixelAspect < 1.0f ? pixelAspect : 1.0f );
  341. float scale = swfScale * sysHeight / (float)frameHeight;
  342. float invScale = 1.0f / scale;
  343. float tx = 0.5f * ( sysWidth - ( frameWidth * scale ) );
  344. float ty = 0.5f * ( sysHeight - ( frameHeight * scale ) );
  345. mouseX = idMath::Ftoi( ( static_cast<float>( event->evValue ) - tx ) * invScale );
  346. mouseY = idMath::Ftoi( ( static_cast<float>( event->evValue2 ) - ty ) * invScale );
  347. } else {
  348. mouseX += event->evValue;
  349. mouseY += event->evValue2;
  350. mouseX = Max( Min( mouseX, idMath::Ftoi( frameWidth + renderBorder ) ), idMath::Ftoi( 0.0f - renderBorder ) );
  351. mouseY = Max( Min( mouseY, idMath::Ftoi(frameHeight) ), 0 );
  352. }
  353. bool retVal = false;
  354. idSWFScriptObject * hitObject = HitTest( mainspriteInstance, swfRenderState_t(), mouseX, mouseY, NULL );
  355. if ( hitObject != NULL ) {
  356. hasHitObject = true;
  357. } else {
  358. hasHitObject = false;
  359. }
  360. if ( hitObject != hoverObject ) {
  361. // First check to see if we should call onRollOut on our previous hoverObject
  362. if ( hoverObject != NULL ) {
  363. idSWFScriptVar var = hoverObject->Get( "onRollOut" );
  364. if ( var.IsFunction() ) {
  365. var.GetFunction()->Call( hoverObject, idSWFParmList() );
  366. retVal = true;
  367. }
  368. hoverObject->Release();
  369. hoverObject = NULL;
  370. }
  371. // Then call onRollOver on our hitObject
  372. if ( hitObject != NULL ) {
  373. hoverObject = hitObject;
  374. hoverObject->AddRef();
  375. idSWFScriptVar var = hitObject->Get( "onRollOver" );
  376. if ( var.IsFunction() ) {
  377. var.GetFunction()->Call( hitObject, idSWFParmList() );
  378. retVal = true;
  379. }
  380. }
  381. }
  382. if ( mouseObject != NULL ) {
  383. idSWFScriptVar var = mouseObject->Get( "onDrag" );
  384. if ( var.IsFunction() ) {
  385. idSWFParmList parms;
  386. parms.Append( mouseX );
  387. parms.Append( mouseY );
  388. parms.Append( false );
  389. var.GetFunction()->Call( mouseObject, parms );
  390. return true;
  391. }
  392. }
  393. return retVal;
  394. } else if ( event->evType == SE_MOUSE_LEAVE ) {
  395. isMouseInClientArea = false;
  396. } else if ( event->evType == SE_JOYSTICK ) {
  397. idSWFParmList parms;
  398. parms.Append( event->evValue );
  399. parms.Append( event->evValue2 / 32.0f );
  400. Invoke( "onJoystick", parms );
  401. }
  402. return false;
  403. }