/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "common/system.h" #include "common/tokenizer.h" #include "common/formats/quicktime.h" #include "video/qt_decoder.h" #include "director/director.h" #include "director/images.h" #include "director/window.h" #include "director/lingo/lingo.h" #include "director/lingo/lingo.h" #include "director/lingo/lingo-builtins.h" #include "director/lingo/lingo-utils.h" #include "director/lingo/xtras/qtvrxtra.h" /************************************************** * * USED IN: * Safecracker * **************************************************/ /* -- xtra QTVRXtra -- v. 1.0, Copyright 1997 Apple Computer, Inc. All Rights Reserved. -- Methods listed as returning | return on error. ----------------------------------- -- Initialization & Finalization -- ----------------------------------- new object me forget object me -- for Director use only. Invoke by calling me = 0 + QTVREnter object xt --> integer (zero on success) -- Initialize QTVR + QTVRExit object xt -- Release QTVR ----------------------------------- -- Methods for any QTVR movie -- ----------------------------------- QTVROpen object me, string filename, string rectStrLTRB, string visibleStr --> string emptyOrError -- visibleStr is ("visible"|"invisible") -- Opens QTVR movie, e.g.: QTVROpen(vrInst, "qtvrtest.mov", "0,0,320,200", "visible") -- (If the movie couldn't be opened, the result is "Error: ") QTVRClose object me -- Closes QTVR movie QTVRUpdate object me -- Redraws QTVR movie with latest parameter settings QTVRGetQTVRType object me --> string ("QTVRPanorama"|"QTVRObject"|"NotAQTVRType") QTVRIdle object me -- Provides idle time to QTVR controller QTVRMouseDown object me --> string mouseDownResult| QTVRMouseOver object me --> string mouseDownResultOrZero| -- mouseDownResult is a two-token string: -- "pan ,0" if panning or zooming -- "jump," if the user moved between nodes -- "navg," if the user selected a 'navg' hot spot -- "stil," if the user selected a 'stil' hot spot -- "misc," if the user selected a 'misc' hot spot -- "," if the user selected a custom hot spot -- "undf," if the user selected an undefined hot spot -- QTVRMouseOver returns "0" if the user did not mouse down, if the cursor -- moved outside the panoramic movie, or if QTVRExitMouseOver was called. QTVRGetPanAngle object me --> string floatStr| QTVRSetPanAngle object me, string floatOrIntStr QTVRGetTiltAngle object me --> string floatStr| QTVRSetTiltAngle object me, string floatOrIntStr ----------------------------------- -- Methods for QTVR Panoramas -- ----------------------------------- QTVRGetFOV object me --> string floatStr| QTVRSetFOV object me, string floatOrIntStr QTVRGetClickLoc object me --> string pointStr| QTVRSetClickLoc object me, string pointStr -- clickLoc is the click location in window coordinates QTVRGetClickPanAngles object me --> string anglePair| -- returns "," angle pair QTVRGetClickPanLoc object me --> string pointStr| -- clickPanLoc is the click location in panoramic image coordinates QTVRGetHotSpotID object me --> integer hotSpotID| -- (0-255) QTVRSetHotSpotID object me, integer hotSpotID -- (1-255 sets, 0 resets hotSpotID) QTVRGetHotSpotName object me --> string hotSpotName| QTVRGetHotSpotType object me --> string hotSpotType| QTVRGetHotSpotViewAngles object me --> string angleTriple| -- returns ",," angle triple QTVRGetObjectViewAngles object me --> string anglePairOrEmpty| -- returns "," angle pair; empty if no hot spot of type 'navg' selected QTVRGetObjectZoomRect object me --> string rectStr| QTVRGetNodeID object me --> integer nodeID| QTVRSetNodeID object me, integer nodeID QTVRGetNodeName object me --> string nodeName| QTVRGetQuality object me --> string floatStr| QTVRSetQuality object me, string floatOrIntStr -- (0|1|2|4) QTVRGetTransitionMode object me --> string transitionMode| QTVRSetTransitionMode object me, string transitionMode -- transitionMode is ("normal"|"swing") QTVRGetTransitionSpeed object me --> string floatStr| QTVRSetTransitionSpeed object me, string floatOrIntStr -- minimum transition speed is 1.0, 4.0 is typical. QTVRGetUpdateMode object me --> string updateMode| QTVRSetUpdateMode object me, string updateMode -- updateMode is ("normal"|"updateBoth"|"offscreenOnly"|"fromOffscreen"|"directToScreen") -- NOTE: QTVRGet/SetUpdateMode are functional only on MacOS. QTVRGetVisible object me --> integer (non-zero if visible) QTVRSetVisible object me, integer booleanInt -- false (0) or true (non-zero) -- NOTE: QTVRGet/SetVisible both work for QTVR Objects as well. QTVRGetWarpMode object me --> string warpMode| QTVRSetWarpMode object me, string warpMode -- warpMode is ("2"|"1"|"0") for 2-d, 1-d or no warping; -- equivalent to full, partial and no correction in QTVRPlayer QTVRCollapseToHotSpotRgn object me -- NOTE: QTVRCollapseToHotSpotRgn is functional only on MacOS. ----------------------------------- -- Methods for QTVR Objects -- ----------------------------------- QTVRZoomOutEffect object me, string startRectStr, string skipFirstFrameBoolean, string clipRectStr -- Rect strings are ",,,". Boolean string is ("true"|"false"). -- NOTE: QTVRZoomOutEffect is functional only on MacOS. QTVRGetColumn object me --> string columnStr QTVRSetColumn object me, integer column -- (column >= 0) QTVRGetRow object me --> string rowStr| QTVRSetRow object me, integer row -- (row >= 0) QTVRNudge object me, string direction -- ("up"|"down"|"left"|"right") ----------------------------------- -- Callbacks for QTVR Panoramas -- ----------------------------------- QTVRGetMouseDownHandler object me --> string handlerName| QTVRSetMouseDownHandler object me, string handlerName QTVRGetMouseOverHandler object me --> string handlerName| QTVRSetMouseOverHandler object me, string handlerName QTVRGetMouseStillDownHandler object me --> string handlerName| QTVRSetMouseStillDownHandler object me, string handlerName QTVRGetNodeLeaveHandler object me --> string handlerName| QTVRSetNodeLeaveHandler object me, string handlerName QTVRGetPanZoomStartHandler object me --> string handlerName| QTVRSetPanZoomStartHandler object me, string handlerName QTVRGetRolloverHotSpotHandler object me --> string handlerName| QTVRSetRolloverHotSpotHandler object me, string handlerName QTVRExitMouseOver object me -- returns control to Director from a QTVRMouseOver handler QTVRPassMouseDown object me -- passes a MouseDown event to the QTVR controller ----------------------------------- -- Testing for a QTVR movie -- ----------------------------------- IsQTVRMovie object me --> integer (non-zero if the movie is a valid, open QTVR movie) */ namespace Director { const char *const QtvrxtraXtra::xlibName = "QTVRXtra"; const XlibFileDesc QtvrxtraXtra::fileNames[] = { { "qtvrxtra", nullptr }, { "Qtvrw32", nullptr }, { nullptr, nullptr }, }; static const MethodProto xlibMethods[] = { { "new", QtvrxtraXtra::m_new, 0, 0, 500 }, { "forget", QtvrxtraXtra::m_forget, 0, 0, 500 }, { "QTVREnter", QtvrxtraXtra::m_QTVREnter, 0, 0, 500 }, { "QTVRExit", QtvrxtraXtra::m_QTVRExit, 0, 0, 500 }, { "QTVROpen", QtvrxtraXtra::m_QTVROpen, 3, 3, 500 }, { "QTVRClose", QtvrxtraXtra::m_QTVRClose, 0, 0, 500 }, { "QTVRUpdate", QtvrxtraXtra::m_QTVRUpdate, 0, 0, 500 }, { "QTVRGetQTVRType", QtvrxtraXtra::m_QTVRGetQTVRType, 0, 0, 500 }, { "QTVRIdle", QtvrxtraXtra::m_QTVRIdle, 0, 0, 500 }, { "QTVRMouseDown", QtvrxtraXtra::m_QTVRMouseDown, 0, 0, 500 }, { "QTVRMouseOver", QtvrxtraXtra::m_QTVRMouseOver, 0, 0, 500 }, { "QTVRGetPanAngle", QtvrxtraXtra::m_QTVRGetPanAngle, 0, 0, 500 }, { "QTVRSetPanAngle", QtvrxtraXtra::m_QTVRSetPanAngle, 1, 1, 500 }, { "QTVRGetTiltAngle", QtvrxtraXtra::m_QTVRGetTiltAngle, 0, 0, 500 }, { "QTVRSetTiltAngle", QtvrxtraXtra::m_QTVRSetTiltAngle, 1, 1, 500 }, { "QTVRGetFOV", QtvrxtraXtra::m_QTVRGetFOV, 0, 0, 500 }, { "QTVRSetFOV", QtvrxtraXtra::m_QTVRSetFOV, 1, 1, 500 }, { "QTVRGetClickLoc", QtvrxtraXtra::m_QTVRGetClickLoc, 0, 0, 500 }, { "QTVRSetClickLoc", QtvrxtraXtra::m_QTVRSetClickLoc, 1, 0, 500 }, { "QTVRGetClickPanAngles", QtvrxtraXtra::m_QTVRGetClickPanAngles, 0, 0, 500 }, { "QTVRGetClickPanLoc", QtvrxtraXtra::m_QTVRGetClickPanLoc, 0, 0, 500 }, { "QTVRGetHotSpotID", QtvrxtraXtra::m_QTVRGetHotSpotID, 0, 0, 500 }, { "QTVRSetHotSpotID", QtvrxtraXtra::m_QTVRSetHotSpotID, 1, 0, 500 }, { "QTVRGetHotSpotName", QtvrxtraXtra::m_QTVRGetHotSpotName, 0, 0, 500 }, { "QTVRGetHotSpotType", QtvrxtraXtra::m_QTVRGetHotSpotType, 0, 0, 500 }, { "QTVRGetHotSpotViewAngles", QtvrxtraXtra::m_QTVRGetHotSpotViewAngles, 0, 0, 500 }, { "QTVRGetObjectViewAngles", QtvrxtraXtra::m_QTVRGetObjectViewAngles, 0, 0, 500 }, { "QTVRGetObjectZoomRect", QtvrxtraXtra::m_QTVRGetObjectZoomRect, 0, 0, 500 }, { "QTVRGetNodeID", QtvrxtraXtra::m_QTVRGetNodeID, 0, 0, 500 }, { "QTVRSetNodeID", QtvrxtraXtra::m_QTVRSetNodeID, 1, 1, 500 }, { "QTVRGetNodeName", QtvrxtraXtra::m_QTVRGetNodeName, 0, 0, 500 }, { "QTVRGetQuality", QtvrxtraXtra::m_QTVRGetQuality, 0, 0, 500 }, { "QTVRSetQuality", QtvrxtraXtra::m_QTVRSetQuality, 1, 1, 500 }, { "QTVRGetTransitionMode", QtvrxtraXtra::m_QTVRGetTransitionMode, 0, 0, 500 }, { "QTVRSetTransitionMode", QtvrxtraXtra::m_QTVRSetTransitionMode, 1, 1, 500 }, { "QTVRGetTransitionSpeed", QtvrxtraXtra::m_QTVRGetTransitionSpeed, 0, 0, 500 }, { "QTVRSetTransitionSpeed", QtvrxtraXtra::m_QTVRSetTransitionSpeed, 1, 1, 500 }, { "QTVRGetUpdateMode", QtvrxtraXtra::m_QTVRGetUpdateMode, 0, 0, 500 }, { "QTVRSetUpdateMode", QtvrxtraXtra::m_QTVRSetUpdateMode, 1, 1, 500 }, { "QTVRGetVisible", QtvrxtraXtra::m_QTVRGetVisible, 0, 0, 500 }, { "QTVRSetVisible", QtvrxtraXtra::m_QTVRSetVisible, 1, 1, 500 }, { "QTVRGetWarpMode", QtvrxtraXtra::m_QTVRGetWarpMode, 0, 0, 500 }, { "QTVRSetWarpMode", QtvrxtraXtra::m_QTVRSetWarpMode, 1, 0, 500 }, { "QTVRCollapseToHotSpotRgn", QtvrxtraXtra::m_QTVRCollapseToHotSpotRgn, 0, 0, 500 }, { "QTVRZoomOutEffect", QtvrxtraXtra::m_QTVRZoomOutEffect, 3, 0, 500 }, { "QTVRGetColumn", QtvrxtraXtra::m_QTVRGetColumn, 0, 0, 500 }, { "QTVRSetColumn", QtvrxtraXtra::m_QTVRSetColumn, 1, 1, 500 }, { "QTVRGetRow", QtvrxtraXtra::m_QTVRGetRow, 0, 0, 500 }, { "QTVRSetRow", QtvrxtraXtra::m_QTVRSetRow, 1, 1, 500 }, { "QTVRNudge", QtvrxtraXtra::m_QTVRNudge, 1, 1, 500 }, { "QTVRGetMouseDownHandler", QtvrxtraXtra::m_QTVRGetMouseDownHandler, 0, 0, 500 }, { "QTVRSetMouseDownHandler", QtvrxtraXtra::m_QTVRSetMouseDownHandler, 1, 0, 500 }, { "QTVRGetMouseOverHandler", QtvrxtraXtra::m_QTVRGetMouseOverHandler, 0, 0, 500 }, { "QTVRSetMouseOverHandler", QtvrxtraXtra::m_QTVRSetMouseOverHandler, 1, 0, 500 }, { "QTVRGetMouseStillDownHandler", QtvrxtraXtra::m_QTVRGetMouseStillDownHandler, 0, 0, 500 }, { "QTVRSetMouseStillDownHandler", QtvrxtraXtra::m_QTVRSetMouseStillDownHandler, 1, 0, 500 }, { "QTVRGetNodeLeaveHandler", QtvrxtraXtra::m_QTVRGetNodeLeaveHandler, 0, 0, 500 }, { "QTVRSetNodeLeaveHandler", QtvrxtraXtra::m_QTVRSetNodeLeaveHandler, 1, 0, 500 }, { "QTVRGetPanZoomStartHandler", QtvrxtraXtra::m_QTVRGetPanZoomStartHandler, 0, 0, 500 }, { "QTVRSetPanZoomStartHandler", QtvrxtraXtra::m_QTVRSetPanZoomStartHandler, 1, 0, 500 }, { "QTVRGetRolloverHotSpotHandler", QtvrxtraXtra::m_QTVRGetRolloverHotSpotHandler, 0, 0, 500 }, { "QTVRSetRolloverHotSpotHandler", QtvrxtraXtra::m_QTVRSetRolloverHotSpotHandler, 1, 0, 500 }, { "QTVRExitMouseOver", QtvrxtraXtra::m_QTVRExitMouseOver, 0, 0, 500 }, { "QTVRPassMouseDown", QtvrxtraXtra::m_QTVRPassMouseDown, 0, 0, 500 }, { "IsQTVRMovie", QtvrxtraXtra::m_IsQTVRMovie, 0, 0, 500 }, { nullptr, nullptr, 0, 0, 0 } }; static const BuiltinProto xlibBuiltins[] = { { nullptr, nullptr, 0, 0, 0, VOIDSYM } }; #define HANDLER_TICKS 500 QtvrxtraXtraObject::QtvrxtraXtraObject(ObjectType ObjectType) :Object("Qtvrxtra") { _objType = ObjectType; _video = nullptr; _visible = false; _passMouseDown = false; _widget = nullptr; } bool QtvrxtraXtraObject::hasProp(const Common::String &propName) { return (propName == "name"); } Datum QtvrxtraXtraObject::getProp(const Common::String &propName) { if (propName == "name") return Datum(QtvrxtraXtra::xlibName); warning("QtvrxtraXtra::getProp: unknown property '%s'", propName.c_str()); return Datum(); } void QtvrxtraXtra::open(ObjectType type, const Common::Path &path) { QtvrxtraXtraObject::initMethods(xlibMethods); QtvrxtraXtraObject *xobj = new QtvrxtraXtraObject(type); if (type == kXtraObj) { g_lingo->_openXtras.push_back(xlibName); g_lingo->_openXtraObjects.push_back(xobj); } g_lingo->exposeXObject(xlibName, xobj); g_lingo->initBuiltIns(xlibBuiltins); } void QtvrxtraXtra::close(ObjectType type) { QtvrxtraXtraObject::cleanupMethods(); g_lingo->_globalvars[xlibName] = Datum(); } void QtvrxtraXtra::m_new(int nargs) { g_lingo->printArgs("QtvrxtraXtra::m_new", nargs); ARGNUMCHECK(0); g_lingo->push(g_lingo->_state->me); } void QtvrxtraXtra::m_forget(int nargs) { g_lingo->printArgs("QtvrxtraXtra::m_forget", nargs); ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; if (me->_video) { me->_video->close(); delete me->_video; } } void QtvrxtraXtra::m_QTVREnter(int nargs) { g_lingo->printArgs("QtvrxtraXtra::m_QTVREnter", nargs); ARGNUMCHECK(0); g_lingo->push(0); } void QtvrxtraXtra::m_QTVRExit(int nargs) { g_lingo->printArgs("QtvrxtraXtra::m_QTVRExit", nargs); ARGNUMCHECK(0); } static Common::Rect stringToRect(const Common::String &rectStr) { Common::StringTokenizer tokenizer(rectStr, Common::String(',')); Common::StringArray tokens(tokenizer.split()); if (tokens.size() != 4) { error("stringToRect(): The string should contain exactly 4 numbers separated by commas"); return {}; } Common::Rect rect; rect.left = atoi(tokens[0].c_str()); rect.top = atoi(tokens[1].c_str()); rect.right = atoi(tokens[2].c_str()); rect.bottom = atoi(tokens[3].c_str()); return rect; } static Common::Point stringToPoint(const Common::String &pointStr) { Common::StringTokenizer tokenizer(pointStr, Common::String(',')); Common::StringArray tokens(tokenizer.split()); if (tokens.size() != 2) { error("stringToPoint(): The string should contain exactly 2 numbers separated by commas"); return {}; } Common::Point point; point.x = atoi(tokens[0].c_str()); point.y = atoi(tokens[1].c_str()); return point; } void QtvrxtraXtra::m_QTVROpen(int nargs) { g_lingo->printArgs("QtvrxtraXtra::m_QTVROpen", nargs); ARGNUMCHECK(3); Common::String visiblityStr = g_lingo->pop().asString(); Common::String rectStr = g_lingo->pop().asString(); Common::String pathStr = g_lingo->pop().asString(); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; if (visiblityStr.equalsIgnoreCase("visible")) { me->_visible = true; } else if (visiblityStr.equalsIgnoreCase("invisible")) { me->_visible = false; } else { Common::String error = Common::String::format("Error: Invalid visibility string: ('%s')!", visiblityStr.c_str()); g_lingo->push(error); return; } me->_rect = stringToRect(rectStr); Common::Path path = findMoviePath(pathStr); if (path.empty()) { Common::String error = Common::String::format("Error: Movie file ('%s') not found!", pathStr.c_str()); g_lingo->push(error); return; } me->_video = new Video::QuickTimeDecoder(); debugC(5, kDebugXObj, "QtvrxtraXtra::m_QTVROpen(): Loading QT file ('%s')", path.toString().c_str()); if (!me->_video->loadFile(path)) { Common::String error = Common::String::format("Error: Failed to load movie file ('%s')!", path.toString().c_str()); g_lingo->push(error); return; } me->_video->setTargetSize(me->_rect.width(), me->_rect.height()); me->_video->setOrigin(me->_rect.left, me->_rect.top); me->_widget = new QtvrxtraWidget(me, g_director->getCurrentWindow()->getMacWindow(), me->_rect.left, me->_rect.top, me->_rect.width(), me->_rect.height(), g_director->getMacWindowManager()); g_lingo->push(Common::String()); } void QtvrxtraXtra::m_QTVRClose(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; if (me->_video) { me->_video->close(); delete me->_video; me->_video = nullptr; delete me->_widget; } } void QtvrxtraXtra::m_QTVRUpdate(int nargs) { QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_visible = true; m_QTVRIdle(0); } void QtvrxtraXtra::m_QTVRGetQTVRType(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; switch (me->_video->getQTVRType()) { case Common::QuickTimeParser::QTVRType::PANORAMA: g_lingo->push(Common::String("QTVRPanorama")); break; case Common::QuickTimeParser::QTVRType::OBJECT: g_lingo->push(Common::String("QTVRObject")); break; case Common::QuickTimeParser::QTVRType::OTHER: default: g_lingo->push(Common::String("NotAQTVRType")); break; } } void QtvrxtraXtra::m_QTVRIdle(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; if (!me->_visible) return; Graphics::Surface const *frame = me->_video->decodeNextFrame(); if (!frame) return; Graphics::Surface *dither = frame->convertTo(g_director->_wm->_pixelformat, me->_video->getPalette(), 256, g_director->getPalette(), 256, Graphics::kDitherNaive); g_director->getCurrentWindow()->getSurface()->copyRectToSurface( dither->getPixels(), dither->pitch, me->_rect.left, me->_rect.top, dither->w, dither->h ); dither->free(); delete dither; } void QtvrxtraXtra::m_QTVRMouseDown(int nargs) { QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; const Common::QuickTimeParser::PanoHotSpot *hotspot; Common::Event event; bool cont = true; if (nargs != -1337 && g_system->getEventManager()->pollEvent(event)) { if (event.type != Common::EVENT_LBUTTONDOWN) cont = false; } if (!cont) { if (me->_video->getQTVRType() == Common::QuickTimeParser::QTVRType::PANORAMA) g_lingo->push(Common::String("pan ,0")); else g_lingo->pushVoid(); return; } uint32 nextTick = g_system->getMillis(); int node; bool nodeChanged = false; while (true) { Graphics::Surface const *frame = me->_video->decodeNextFrame(); Graphics::Surface *dither = frame->convertTo(g_director->_wm->_pixelformat, me->_video->getPalette(), 256, g_director->getPalette(), 256, Graphics::kDitherNaive); g_director->getCurrentWindow()->getSurface()->copyRectToSurface( dither->getPixels(), dither->pitch, me->_rect.left, me->_rect.top, dither->w, dither->h ); dither->free(); delete dither; g_director->getCurrentWindow()->setDirty(true); node = me->_video->getCurrentNodeID(); while (g_system->getEventManager()->pollEvent(event)) { me->_widget->processEvent(event); if (event.type == Common::EVENT_LBUTTONUP) break; } if (me->_video->getCurrentNodeID() != node) { if (!me->_nodeLeaveHandler.empty()) { g_lingo->push(me->_video->getCurrentNodeID()); g_lingo->executeHandler(me->_nodeLeaveHandler, 1); } nodeChanged = true; } if (g_system->getMillis() > nextTick) { nextTick = g_system->getMillis() + HANDLER_TICKS; if (!me->_mouseStillDownHandler.empty()) g_lingo->executeHandler(me->_mouseStillDownHandler); } LB::b_updateStage(0); if (event.type == Common::EVENT_QUIT) { g_director->processEventQUIT(); break; } if (event.type == Common::EVENT_LBUTTONUP) break; g_director->delayMillis(10); } hotspot = me->_video->getClickedHotspot(); if (!hotspot) { if (nodeChanged) g_lingo->push(Common::String::format("jump,%d", node)); else g_lingo->push(Common::String("pan ,0")); return; } g_lingo->push(Common::String::format("%s,%d", tag2str((uint32)hotspot->type), hotspot->id)); } void QtvrxtraXtra::m_QTVRMouseOver(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; Common::Point pos = g_director->getCurrentWindow()->getMousePos(); if (!me->_visible || !me->_rect.contains(pos)) { g_lingo->pushVoid(); return; } // Execute handler on first call to MouseOver const Common::QuickTimeParser::PanoHotSpot *hotspot = me->_video->getRolloverHotspot(); if (!me->_rolloverHotSpotHandler.empty()) { g_lingo->push(hotspot ? hotspot->id : 0); g_lingo->executeHandler(me->_rolloverHotSpotHandler, 1); } uint32 nextTick = g_system->getMillis(); while (true) { Graphics::Surface const *frame = me->_video->decodeNextFrame(); if (!frame) { g_lingo->pushVoid(); return; } Graphics::Surface *dither = frame->convertTo(g_director->_wm->_pixelformat, me->_video->getPalette(), 256, g_director->getPalette(), 256, Graphics::kDitherNaive); g_director->getCurrentWindow()->getSurface()->copyRectToSurface( dither->getPixels(), dither->pitch, me->_rect.left, me->_rect.top, dither->w, dither->h ); dither->free(); delete dither; g_director->getCurrentWindow()->setDirty(true); Common::Event event; while (g_system->getEventManager()->pollEvent(event)) { if (Common::isMouseEvent(event)) { pos = g_director->getCurrentWindow()->getMousePos(); if (!me->_rect.contains(pos)) break; } hotspot = me->_video->getRolloverHotspot(); if (event.type == Common::EVENT_LBUTTONDOWN) { // MouseDownHandler is processed inside me->_widget->processEvent(event); if (!me->_passMouseDown) { g_lingo->push(0); return; } m_QTVRMouseDown(-1337); return; // MouseDown will take care of the return value on the stack } me->_widget->processEvent(event); if (!me->_rolloverHotSpotHandler.empty() && hotspot != me->_video->getRolloverHotspot()) { g_lingo->push(hotspot ? hotspot->id : 0); g_lingo->executeHandler(me->_rolloverHotSpotHandler, 1); if (me->_exitMouseOver) break; } } if (g_system->getMillis() > nextTick) { nextTick = g_system->getMillis() + HANDLER_TICKS; if (!me->_mouseOverHandler.empty()) g_lingo->executeHandler(me->_mouseOverHandler); } if (me->_exitMouseOver) break; LB::b_updateStage(0); if (!me->_rect.contains(pos)) break; if (event.type == Common::EVENT_QUIT) { g_director->processEventQUIT(); break; } g_director->delayMillis(10); } if (me->_video->getQTVRType() == Common::QuickTimeParser::QTVRType::PANORAMA) g_lingo->push(0); else g_lingo->pushVoid(); } void QtvrxtraXtra::m_QTVRGetPanAngle(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(Common::String::format("%f", me->_video->getPanAngle())); } void QtvrxtraXtra::m_QTVRSetPanAngle(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_video->setPanAngle(atof(g_lingo->pop().asString().c_str())); } void QtvrxtraXtra::m_QTVRGetTiltAngle(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(Common::String::format("%f", me->_video->getTiltAngle())); } void QtvrxtraXtra::m_QTVRSetTiltAngle(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_video->setTiltAngle(atof(g_lingo->pop().asString().c_str())); } void QtvrxtraXtra::m_QTVRGetFOV(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(Common::String::format("%f", me->_video->getFOV())); } void QtvrxtraXtra::m_QTVRSetFOV(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_video->setFOV(atof(g_lingo->pop().asString().c_str())); } void QtvrxtraXtra::m_QTVRGetClickLoc(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; Common::Point pos = me->_video->getLastClick(); g_lingo->push(Common::String::format("%d,%d", pos.x, pos.y)); } void QtvrxtraXtra::m_QTVRSetClickLoc(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; Common::Point pos = stringToPoint(g_lingo->pop().asString()); me->_video->handleMouseButton(true, pos.x, pos.y); } void QtvrxtraXtra::m_QTVRGetClickPanAngles(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; Common::Point pos = me->_video->getLastClick(); Graphics::FloatPoint loc = me->_video->getPanAngles(pos.x, pos.y); g_lingo->push(Common::String::format("%.4f,%.4f", loc.x, loc.y)); } void QtvrxtraXtra::m_QTVRGetClickPanLoc(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; Common::Point pos = me->_video->getLastClick(); Common::Point loc = me->_video->getPanLoc(pos.x, pos.y); g_lingo->push(Common::String::format("%d,%d", loc.x, loc.y)); } void QtvrxtraXtra::m_QTVRGetHotSpotID(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; const Common::QuickTimeParser::PanoHotSpot *hotspot = me->_video->getClickedHotspot(); g_lingo->push(hotspot ? hotspot->id : 0); } void QtvrxtraXtra::m_QTVRSetHotSpotID(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_video->setClickedHotSpot(g_lingo->pop().asInt()); } void QtvrxtraXtra::m_QTVRGetHotSpotName(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; const Common::QuickTimeParser::PanoHotSpot *hotspot = me->_video->getClickedHotspot(); if (!hotspot) { g_lingo->push(Common::String("")); return; } g_lingo->push(me->_video->getHotSpotName(hotspot->id)); } void QtvrxtraXtra::m_QTVRGetHotSpotType(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; const Common::QuickTimeParser::PanoHotSpot *hotspot = me->_video->getClickedHotspot(); g_lingo->push(hotspot ? Common::tag2string((uint32)hotspot->type) : "undf"); } void QtvrxtraXtra::m_QTVRGetHotSpotViewAngles(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; const Common::QuickTimeParser::PanoHotSpot *hotspot = me->_video->getClickedHotspot(); if (hotspot) g_lingo->push(Common::String::format("%.4f,%.4f,%.4f", hotspot->viewHPan, hotspot->viewVPan, hotspot->viewZoom)); else g_lingo->push(Common::String("")); } void QtvrxtraXtra::m_QTVRGetObjectViewAngles(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; const Common::QuickTimeParser::PanoHotSpot *hotspot = me->_video->getClickedHotspot(); if (hotspot) { const Common::QuickTimeParser::PanoNavigation *navg = me->_video->getHotSpotNavByID(hotspot->id); if (navg) { g_lingo->push(Common::String::format("%.4f,%.4f", navg->navgHPan, navg->navgVPan)); return; } } g_lingo->pushVoid(); } void QtvrxtraXtra::m_QTVRGetObjectZoomRect(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; const Common::QuickTimeParser::PanoHotSpot *hotspot = me->_video->getClickedHotspot(); if (hotspot) { const Common::QuickTimeParser::PanoNavigation *navg = me->_video->getHotSpotNavByID(hotspot->id); if (navg) { g_lingo->push(Common::String::format("%d,%d,%d,%d", navg->zoomRect.left, navg->zoomRect.top, navg->zoomRect.right, navg->zoomRect.bottom)); return; } } g_lingo->pushVoid(); } void QtvrxtraXtra::m_QTVRGetNodeID(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push((int)me->_video->getCurrentNodeID()); } void QtvrxtraXtra::m_QTVRSetNodeID(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_video->goToNode(g_lingo->pop().asInt()); } void QtvrxtraXtra::m_QTVRGetNodeName(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(me->_video->getCurrentNodeName()); } void QtvrxtraXtra::m_QTVRGetQuality(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(me->_video->getQuality()); } void QtvrxtraXtra::m_QTVRSetQuality(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_video->setQuality(g_lingo->pop().asInt()); } void QtvrxtraXtra::m_QTVRGetTransitionMode(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(me->_video->getTransitionMode()); } void QtvrxtraXtra::m_QTVRSetTransitionMode(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_video->setTransitionMode(g_lingo->pop().asString()); } void QtvrxtraXtra::m_QTVRGetTransitionSpeed(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(me->_video->getTransitionSpeed()); } void QtvrxtraXtra::m_QTVRSetTransitionSpeed(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_video->setTransitionSpeed(g_lingo->pop().asFloat()); } void QtvrxtraXtra::m_QTVRGetUpdateMode(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(me->_video->getUpdateMode()); } void QtvrxtraXtra::m_QTVRSetUpdateMode(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_video->setUpdateMode(g_lingo->pop().asString()); } void QtvrxtraXtra::m_QTVRGetVisible(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push((int)me->_visible); } void QtvrxtraXtra::m_QTVRSetVisible(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_visible = (bool)g_lingo->pop().asInt(); if (!me->_visible) g_director->getCurrentWindow()->render(true); } void QtvrxtraXtra::m_QTVRGetWarpMode(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(me->_video->getWarpMode()); } void QtvrxtraXtra::m_QTVRSetWarpMode(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_video->setWarpMode(g_lingo->pop().asInt()); } XOBJSTUB(QtvrxtraXtra::m_QTVRCollapseToHotSpotRgn, 0) XOBJSTUB(QtvrxtraXtra::m_QTVRZoomOutEffect, 0) void QtvrxtraXtra::m_QTVRGetColumn(int nargs) { g_lingo->printArgs("QtvrxtraXtra::m_QTVRGetColumn", nargs); ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(Common::String::format("%d", me->_video->getCurrentColumn())); } void QtvrxtraXtra::m_QTVRSetColumn(int nargs) { g_lingo->printArgs("QtvrxtraXtra::m_QTVRSetColumn", nargs); ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_video->setCurrentColumn(atoi(g_lingo->pop().asString().c_str())); } void QtvrxtraXtra::m_QTVRGetRow(int nargs) { g_lingo->printArgs("QtvrxtraXtra::m_QTVRGetRow", nargs); ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(Common::String::format("%d", me->_video->getCurrentRow())); } void QtvrxtraXtra::m_QTVRSetRow(int nargs) { g_lingo->printArgs("QtvrxtraXtra::m_QTVRSetRow", nargs); ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_video->setCurrentRow(atoi(g_lingo->pop().asString().c_str())); } void QtvrxtraXtra::m_QTVRNudge(int nargs) { g_lingo->printArgs("QtvrxtraXtra::m_QTVRNudge", nargs); ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; Common::String direction = g_lingo->pop().asString(); if (!(direction.equalsIgnoreCase("left") || direction.equalsIgnoreCase("right") || direction.equalsIgnoreCase("up") || direction.equalsIgnoreCase("down"))) { error("QtvrxtraXtra::m_QTVRNudge(): Invald direction: ('%s')!", direction.c_str()); return; } me->_video->nudge(direction); } void QtvrxtraXtra::m_QTVRGetMouseDownHandler(int nargs) { g_lingo->printArgs("QtvrxtraXtra::m_QTVRGetMouseDownHandler", nargs); ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(me->_mouseDownHandler); } void QtvrxtraXtra::m_QTVRSetMouseDownHandler(int nargs) { g_lingo->printArgs("QtvrxtraXtra::m_QTVRSetMouseDownHandler", nargs); ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_mouseDownHandler = g_lingo->pop().asString(); } void QtvrxtraXtra::m_QTVRGetMouseOverHandler(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(me->_mouseOverHandler); } void QtvrxtraXtra::m_QTVRSetMouseOverHandler(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_mouseOverHandler = g_lingo->pop().asString(); } void QtvrxtraXtra::m_QTVRGetMouseStillDownHandler(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(me->_mouseStillDownHandler); } void QtvrxtraXtra::m_QTVRSetMouseStillDownHandler(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_mouseStillDownHandler = g_lingo->pop().asString(); } void QtvrxtraXtra::m_QTVRGetNodeLeaveHandler(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(me->_nodeLeaveHandler); } void QtvrxtraXtra::m_QTVRSetNodeLeaveHandler(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_nodeLeaveHandler = g_lingo->pop().asString(); } void QtvrxtraXtra::m_QTVRGetPanZoomStartHandler(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(me->_panZoomStartHandler); } void QtvrxtraXtra::m_QTVRSetPanZoomStartHandler(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_panZoomStartHandler = g_lingo->pop().asString(); } void QtvrxtraXtra::m_QTVRGetRolloverHotSpotHandler(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push(me->_rolloverHotSpotHandler); } void QtvrxtraXtra::m_QTVRSetRolloverHotSpotHandler(int nargs) { ARGNUMCHECK(1); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_rolloverHotSpotHandler = g_lingo->pop().asString(); } void QtvrxtraXtra::m_QTVRExitMouseOver(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_exitMouseOver = true; } void QtvrxtraXtra::m_QTVRPassMouseDown(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; me->_passMouseDown = true; } void QtvrxtraXtra::m_IsQTVRMovie(int nargs) { ARGNUMCHECK(0); QtvrxtraXtraObject *me = (QtvrxtraXtraObject *)g_lingo->_state->me.u.obj; g_lingo->push((int)(me->_video && me->_video->isVideoLoaded() && me->_video->isVR())); } /////////////// // Widget /////////////// QtvrxtraWidget::QtvrxtraWidget(QtvrxtraXtraObject *xtra, Graphics::MacWidget *parent, int x, int y, int w, int h, Graphics::MacWindowManager *wm) : Graphics::MacWidget(parent, x, y, w, h, wm, true), _xtra(xtra) { _priority = 10000; // We stay on top of everything } bool QtvrxtraWidget::processEvent(Common::Event &event) { if (!_xtra->_visible) return false; switch (event.type) { case Common::EVENT_LBUTTONDOWN: if (_xtra->_mouseDownHandler.empty()) { _xtra->_passMouseDown = true; _xtra->_video->handleMouseButton(true, event.mouse.x - _xtra->_rect.left, event.mouse.y - _xtra->_rect.top); } else { _xtra->_passMouseDown = false; g_lingo->executeHandler(_xtra->_mouseDownHandler); if (_xtra->_passMouseDown) _xtra->_video->handleMouseButton(true, event.mouse.x - _xtra->_rect.left, event.mouse.y - _xtra->_rect.top); } return true; case Common::EVENT_LBUTTONUP: _xtra->_video->handleMouseButton(false, event.mouse.x - _xtra->_rect.left, event.mouse.y - _xtra->_rect.top); return true; case Common::EVENT_MOUSEMOVE: _xtra->_video->handleMouseMove(event.mouse.x - _xtra->_rect.left, event.mouse.y - _xtra->_rect.top); return true; case Common::EVENT_QUIT: _xtra->_video->handleQuit(); return false; case Common::EVENT_KEYDOWN: case Common::EVENT_KEYUP: { int zoomState = _xtra->_video->getZoomState(); _xtra->_video->handleKey(event.kbd, event.type == Common::EVENT_KEYDOWN); int newZoomState = _xtra->_video->getZoomState(); if (zoomState == Video::QuickTimeDecoder::kZoomNone && (newZoomState == Video::QuickTimeDecoder::kZoomIn || newZoomState == Video::QuickTimeDecoder::kZoomOut)) { if (!_xtra->_panZoomStartHandler.empty()) g_lingo->executeHandler(_xtra->_panZoomStartHandler); } } return true; default: return false; } } }