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